live-bootstrap

This page is produced by the version of the program keam_parser.cpp listed at the bottom of this page. The program parsers the contents of GitHub repositories oriansj/stage0 and fosslinux/live-bootstrap (the commit 493ddfa) in order to find all the dependencies and list relevant input sources. The parsing stops at the execution of run.sh. (This is still work in progress.)

The code displayed on this page is not copyrighted by me but by the owners of respective repositories as also mentioned in the headers of the various files.

Binary seeds files

../git/live-bootstrap/sysa/stage0-posix/src/bootstrap-seeds/POSIX/x86/kaem-optional-seed

Source: https://github.com/oriansj/bootstrap-seeds/blob/main/POSIX/x86/kaem-optional-seed

Executable seed!

../git/live-bootstrap/sysa/stage0-posix/src/bootstrap-seeds/POSIX/x86/hex0-seed

Source: https://github.com/oriansj/bootstrap-seeds/blob/main/POSIX/x86/hex0-seed

Executable seed!

Input source files

../git/live-bootstrap/sysa/stage0-posix/src/x86/hex0_x86.hex0

Source: https://github.com/oriansj/stage0-posix-x86/blob/main/hex0_x86.hex0

../git/live-bootstrap/sysa/stage0-posix/src/x86/kaem-minimal.hex0

Source: https://github.com/oriansj/stage0-posix-x86/blob/main/kaem-minimal.hex0

../git/live-bootstrap/sysa/stage0-posix/src/x86/hex1_x86.hex0

Source: https://github.com/oriansj/stage0-posix-x86/blob/main/hex1_x86.hex0

../git/live-bootstrap/sysa/stage0-posix/src/x86/hex2_x86.hex1

Source: https://github.com/oriansj/stage0-posix-x86/blob/main/hex2_x86.hex1

../git/live-bootstrap/sysa/stage0-posix/src/x86/catm_x86.hex2

Source: https://github.com/oriansj/stage0-posix-x86/blob/main/catm_x86.hex2

../git/live-bootstrap/sysa/stage0-posix/src/x86/ELF-i386.hex2

Source: https://github.com/oriansj/stage0-posix-x86/blob/main/ELF-i386.hex2

../git/live-bootstrap/sysa/stage0-posix/src/x86/M0_x86.hex2

Source: https://github.com/oriansj/stage0-posix-x86/blob/main/M0_x86.hex2

../git/live-bootstrap/sysa/stage0-posix/src/x86/cc_x86.M1

Source: https://github.com/oriansj/stage0-posix-x86/blob/main/cc_x86.M1

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/linux/bootstrap.c

Source: https://github.com/oriansj/M2libc/blob/main/x86/linux/bootstrap.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc.h

Source: https://github.com/oriansj/M2-Planet/blob/main/cc.h

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/bootstrappable.c

Source: https://github.com/oriansj/M2libc/blob/main/bootstrappable.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc_globals.c

Source: https://github.com/oriansj/M2-Planet/blob/main/cc_globals.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc_reader.c

Source: https://github.com/oriansj/M2-Planet/blob/main/cc_reader.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc_strings.c

Source: https://github.com/oriansj/M2-Planet/blob/main/cc_strings.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc_types.c

Source: https://github.com/oriansj/M2-Planet/blob/main/cc_types.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc_core.c

Source: https://github.com/oriansj/M2-Planet/blob/main/cc_core.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc_macro.c

Source: https://github.com/oriansj/M2-Planet/blob/main/cc_macro.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc.c

Source: https://github.com/oriansj/M2-Planet/blob/main/cc.c

../git/live-bootstrap/sysa/stage0-posix/src/x86/x86_defs.M1

Source: https://github.com/oriansj/stage0-posix-x86/blob/main/x86_defs.M1

../git/live-bootstrap/sysa/stage0-posix/src/x86/libc-core.M1

Source: https://github.com/oriansj/stage0-posix-x86/blob/main/libc-core.M1

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/stringify.c

Source: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=stringify.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/blood-elf.c

Source: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=blood-elf.c

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/x86_defs.M1

Source: https://github.com/oriansj/M2libc/blob/main/x86/x86_defs.M1

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/libc-core.M1

Source: https://github.com/oriansj/M2libc/blob/main/x86/libc-core.M1

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/ELF-x86.hex2

Source: https://github.com/oriansj/M2libc/blob/main/x86/ELF-x86.hex2

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/M1-macro.c

Source: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=M1-macro.c

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/ELF-x86-debug.hex2

Source: https://github.com/oriansj/M2libc/blob/main/x86/ELF-x86-debug.hex2

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/sys/types.h

Source: https://github.com/oriansj/M2libc/blob/main/sys/types.h

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/stddef.h

Source: https://github.com/oriansj/M2libc/blob/main/stddef.h

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/linux/unistd.c

Source: https://github.com/oriansj/M2libc/blob/main/x86/linux/unistd.c

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/linux/fcntl.c

Source: https://github.com/oriansj/M2libc/blob/main/x86/linux/fcntl.c

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/fcntl.c

Source: https://github.com/oriansj/M2libc/blob/main/fcntl.c

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/linux/sys/stat.c

Source: https://github.com/oriansj/M2libc/blob/main/x86/linux/sys/stat.c

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/stdlib.c

Source: https://github.com/oriansj/M2libc/blob/main/stdlib.c

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/stdio.h

Source: https://github.com/oriansj/M2libc/blob/main/stdio.h

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/stdio.c

Source: https://github.com/oriansj/M2libc/blob/main/stdio.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/hex2.h

Source: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=hex2.h

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/hex2_linker.c

Source: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=hex2_linker.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/hex2_word.c

Source: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=hex2_word.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/hex2.c

Source: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=hex2.c

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/libc-full.M1

Source: https://github.com/oriansj/M2libc/blob/main/x86/libc-full.M1

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/string.c

Source: https://github.com/oriansj/M2libc/blob/main/string.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/Kaem/kaem.h

Source: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=Kaem/kaem.h

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/Kaem/variable.c

Source: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=Kaem/variable.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/Kaem/kaem_globals.c

Source: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=Kaem/kaem_globals.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/Kaem/kaem.c

Source: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=Kaem/kaem.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc.h

Source: https://github.com/oriansj/M2-Mesoplanet/blob/main/cc.h

../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc_globals.c

Source: https://github.com/oriansj/M2-Mesoplanet/blob/main/cc_globals.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc_env.c

Source: https://github.com/oriansj/M2-Mesoplanet/blob/main/cc_env.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc_reader.c

Source: https://github.com/oriansj/M2-Mesoplanet/blob/main/cc_reader.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc_spawn.c

Source: https://github.com/oriansj/M2-Mesoplanet/blob/main/cc_spawn.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc_core.c

Source: https://github.com/oriansj/M2-Mesoplanet/blob/main/cc_core.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc_macro.c

Source: https://github.com/oriansj/M2-Mesoplanet/blob/main/cc_macro.c

../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc.c

Source: https://github.com/oriansj/M2-Mesoplanet/blob/main/cc.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/get_machine.c

Source: https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=get_machine.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/sha256sum.c

Source: https://github.com/oriansj/mescc-tools-extra/blob/main/sha256sum.c

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/string.h

Source: https://github.com/oriansj/M2libc/blob/main/string.h

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/sys/stat.h

Source: https://github.com/oriansj/M2libc/blob/main/sys/stat.h

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/fcntl.h

Source: https://github.com/oriansj/M2libc/blob/main/fcntl.h

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/unistd.h

Source: https://github.com/oriansj/M2libc/blob/main/unistd.h

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/stdlib.h

Source: https://github.com/oriansj/M2libc/blob/main/stdlib.h

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/M2libc/bootstrappable.h

Source: https://github.com/oriansj/M2libc/blob/main/bootstrappable.h

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/match.c

Source: https://github.com/oriansj/mescc-tools-extra/blob/main/match.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/mkdir.c

Source: https://github.com/oriansj/mescc-tools-extra/blob/main/mkdir.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/untar.c

Source: https://github.com/oriansj/mescc-tools-extra/blob/main/untar.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/ungz.c

Source: https://github.com/oriansj/mescc-tools-extra/blob/main/ungz.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/unbz2.c

Source: https://github.com/oriansj/mescc-tools-extra/blob/main/unbz2.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/catm.c

Source: https://github.com/oriansj/mescc-tools-extra/blob/main/catm.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/cp.c

Source: https://github.com/oriansj/mescc-tools-extra/blob/main/cp.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/chmod.c

Source: https://github.com/oriansj/mescc-tools-extra/blob/main/chmod.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/rm.c

Source: https://github.com/oriansj/mescc-tools-extra/blob/main/rm.c

../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/replace.c

Source: https://github.com/oriansj/mescc-tools-extra/blob/main/replace.c

../git/live-bootstrap/sysa/stage0-posix/src/x86.answers

Source: https://github.com/oriansj/stage0-posix/blob/main/x86.answers

/M2libc/x86/linux/sys/stat.c

Source: https://github.com/oriansj/M2libc/blob/main/x86/linux/sys/stat.c

/M2libc/stdlib.c

Source: https://github.com/oriansj/M2libc/blob/main/stdlib.c

/M2libc/stdio.h

Source: https://github.com/oriansj/M2libc/blob/main/stdio.h

/M2libc/stdio.c

Source: https://github.com/oriansj/M2libc/blob/main/stdio.c

/M2libc/x86/libc-full.M1

Source: https://github.com/oriansj/M2libc/blob/main/x86/libc-full.M1

/M2libc/string.c

Source: https://github.com/oriansj/M2libc/blob/main/string.c

/M2libc/string.h

Source: https://github.com/oriansj/M2libc/blob/main/string.h

../git/live-bootstrap/sysa/stage0-posix/src/M2libc/bootstrappable.h

Source: https://github.com/oriansj/M2libc/blob/main/bootstrappable.h

../git/live-bootstrap/sysa/checksum-transcriber.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/checksum-transcriber.c

../git/live-bootstrap/sysa/checksum-transcriber.x86.SHA256SUM

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/checksum-transcriber.x86.SHA256SUM

../git/live-bootstrap/sysa/simple-patch.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/simple-patch.c

../git/live-bootstrap/sysa/mes-0.24.2/sources

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/mes-0.24.2/sources

/sysa/distfiles/mes-0.24.2.tar.gz

Source: https://mirrors.kernel.org/gnu/mes/mes-0.24.2.tar.gz

/sysa/distfiles/nyacc-1.00.2.tar.gz

Source: https://download.savannah.gnu.org/releases/nyacc/nyacc-1.00.2.tar.gz

../git/live-bootstrap/sysa/mes-0.24.2/files/config.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/mes-0.24.2/files/config.h

/sysa/mes-0.24.2/build/mes-0.24.2/include/linux/x86/syscall.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/linux/x86/syscall.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/linux/x86/kernel-stat.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/linux/x86/kernel-stat.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/mes/module/mes/psyntax.pp

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/mes/module/mes/psyntax.pp?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/mes/module/mes/psyntax.pp.header

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/mes/module/mes/psyntax.pp.header?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/mes/module/srfi/srfi-9-struct.mes

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/mes/module/srfi/srfi-9-struct.mes?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/mes/module/srfi/srfi-9/gnu-struct.mes

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/mes/module/srfi/srfi-9/gnu-struct.mes?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/m2/lib.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/m2/lib.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-m2/crt1.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-m2/crt1.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-m2/_exit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-m2/_exit.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-m2/_write.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-m2/_write.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/globals.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/globals.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/cast.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/cast.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/exit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/exit.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/mini-write.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/mini-write.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-m2/syscall.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-m2/syscall.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stub/__raise.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/__raise.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/brk.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/brk.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/malloc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/malloc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/memset.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memset.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/read.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/read.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/fdgetc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdgetc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/getchar.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/getchar.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/putchar.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/putchar.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/open.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/open.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/mes_open.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/mes_open.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/strlen.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strlen.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/eputs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/eputs.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/fdputc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdputc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/eputc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/eputc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/m2/types.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/m2/types.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/mes/mes.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/mes.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/mes/builtins.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/builtins.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/mes/constants.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/constants.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/mes/symbols.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/symbols.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/__assert_fail.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/__assert_fail.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/assert_msg.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/assert_msg.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/strncmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strncmp.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/posix/getenv.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/getenv.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/fdputs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdputs.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/ntoab.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ntoab.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/ctype/isdigit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isdigit.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/ctype/isxdigit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isxdigit.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/ctype/isspace.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isspace.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/ctype/isnumber.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isnumber.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/abtol.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/abtol.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/atoi.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/atoi.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/memcpy.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memcpy.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/free.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/free.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/realloc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/realloc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/strcpy.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcpy.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/itoa.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/itoa.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/ltoa.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ltoa.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/fdungetc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdungetc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/posix/setenv.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/setenv.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/access.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/access.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/linux/m2/kernel-stat.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/linux/m2/kernel-stat.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/stat.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/stat.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/chmod.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/chmod.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/ioctl3.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/ioctl3.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/isatty.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/isatty.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/fork.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fork.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/execve.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/execve.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/execv.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/execv.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/waitpid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/waitpid.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/gettimeofday.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/gettimeofday.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/clock_gettime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/clock_gettime.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/time.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/time.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/_getcwd.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/_getcwd.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/getcwd.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/getcwd.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/dup.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/dup.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/dup2.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/dup2.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/strcmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcmp.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/memcmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memcmp.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/unlink.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/unlink.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/builtins.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/builtins.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/core.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/core.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/display.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/display.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/eval-apply.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/eval-apply.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/gc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/gc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/hash.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/hash.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/lib.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/lib.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/m2.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/m2.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/math.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/math.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/mes.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/mes.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/module.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/module.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/posix.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/posix.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/reader.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/reader.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/stack.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/stack.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/string.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/string.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/struct.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/struct.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/symbol.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/symbol.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/vector.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/vector.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/x86-mes/x86.M1

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/x86-mes/x86.M1?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-m2/crt1.M1

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-m2/crt1.M1?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/x86/ELF-x86.hex2

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/x86/ELF-x86.hex2?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-mescc/crt1.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-mescc/crt1.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/mes/lib-mini.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/lib-mini.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/string.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/string.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/oputs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/oputs.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/exit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/exit.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/mes/lib.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/lib.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-mescc/_exit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-mescc/_exit.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-mescc/_write.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-mescc/_write.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/puts.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/puts.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/errno.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/errno.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-mescc/syscall-internal.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-mescc/syscall-internal.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/ctype.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/ctype.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/cast.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/cast.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/limits.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/limits.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/stdlib.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdlib.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/resource.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/resource.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/unistd.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/unistd.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/ltoab.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ltoab.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/mes_open.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/mes_open.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/assert.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/assert.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/oputc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/oputc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/ultoa.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ultoa.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/utoa.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/utoa.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/posix/write.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/write.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/stdio.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdio.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/lseek.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/lseek.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/linux/syscall.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/linux/syscall.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/types.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/types.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/__buffered_read.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/__buffered_read.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/__mes_debug.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/__mes_debug.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/posix/execv.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execv.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/posix/getcwd.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/getcwd.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/posix/isatty.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/isatty.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/ioctl.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/ioctl.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/termio.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/termio.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/posix/open.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/open.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/fcntl.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/fcntl.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/stdarg.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdarg.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/posix/buffered-read.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/buffered-read.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/posix/wait.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/wait.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/wait.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/wait.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/fgetc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fgetc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/fputc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fputc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/fputs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fputs.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/getc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/getc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/putc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/putc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/ungetc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/ungetc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/memchr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memchr.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/memmove.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memmove.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/posix/raise.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/raise.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/signal.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/signal.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/chmod.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/chmod.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/clock_gettime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/clock_gettime.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/time.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/time.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/execve.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/execve.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/fsync.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fsync.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/time.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/time.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/malloc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/malloc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/stddef.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stddef.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/stdint.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdint.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/_open3.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/_open3.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/_read.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/_read.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/time.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/time.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/waitpid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/waitpid.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-mescc/syscall.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-mescc/syscall.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/getpid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getpid.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/kill.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/kill.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/mes/cc.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/cc.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/cc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/cc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/src/globals.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/src/globals.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/ctype/islower.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/islower.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/ctype/isupper.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isupper.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/ctype/tolower.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/tolower.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/ctype/toupper.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/toupper.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/abtod.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/abtod.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/dtoab.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/dtoab.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/search-path.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/search-path.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/posix/execvp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execvp.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/fclose.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fclose.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/fdopen.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fdopen.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/ferror.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/ferror.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/fflush.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fflush.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/fopen.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fopen.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/fprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fprintf.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/fread.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fread.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/fseek.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fseek.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/ftell.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/ftell.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/fwrite.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fwrite.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/printf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/printf.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/remove.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/remove.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/snprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/snprintf.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/sprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/sprintf.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/sscanf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/sscanf.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/vfprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vfprintf.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/vprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vprintf.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/vsnprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vsnprintf.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/vsprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vsprintf.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdio/vsscanf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vsscanf.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/calloc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/calloc.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/qsort.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/qsort.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/strtod.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtod.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/strtof.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtof.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/strtol.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtol.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/strtold.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtold.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/strtoll.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtoll.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/strtoul.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtoul.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stdlib/strtoull.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtoull.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/memmem.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memmem.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/strcat.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcat.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/strchr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strchr.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/strlwr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strlwr.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/strncpy.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strncpy.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/strrchr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strrchr.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/strstr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strstr.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/mman.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/mman.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/string/strupr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strupr.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stub/sigaction.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigaction.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stub/ldexp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/ldexp.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/math.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/math.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stub/mprotect.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/mprotect.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stub/localtime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/localtime.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/stub/sigemptyset.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigemptyset.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/x86-mes-mescc/setjmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/x86-mes-mescc/setjmp.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/setjmp.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/setjmp.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/close.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/close.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/rmdir.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/rmdir.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/stat.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/stat.c?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/x86-mes/crt1.s

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/x86-mes/crt1.s?h=v0.24.2

NoInput

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes/elf32-footer-single-main.hex2

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes/elf32-footer-single-main.hex2?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes/elf32-header.hex2

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes/elf32-header.hex2?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/alloca.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/alloca.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/argz.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/argz.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/ar.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/ar.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/dirent.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/dirent.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/dirstream.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/dirstream.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/dlfcn.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/dlfcn.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/endian.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/endian.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/features.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/features.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/float.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/float.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/getopt.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/getopt.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/grp.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/grp.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/inttypes.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/inttypes.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/libgen.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/libgen.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/locale.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/locale.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/memory.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/memory.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/pwd.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/pwd.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/stdbool.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdbool.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/stdnoreturn.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdnoreturn.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/strings.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/strings.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/cdefs.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/cdefs.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/dir.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/dir.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/file.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/file.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/param.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/param.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/select.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/select.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/timeb.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/timeb.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/times.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/times.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/ucontext.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/ucontext.h?h=v0.24.2

/sysa/mes-0.24.2/build/mes-0.24.2/include/sys/user.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/user.h?h=v0.24.2

/sysa/mes-0.24.2/mes-0.24.2.checksums

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/mes-0.24.2/mes-0.24.2.checksums

../git/live-bootstrap/sysa/tcc-0.9.26/sources

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.26/sources

/sysa/distfiles/tcc-0.9.26.tar.gz

Source: https://lilypond.org/janneke/tcc/tcc-0.9.26-1136-g5bba73cc.tar.gz

../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tcctools.c

Source: https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tcctools.c

../git/live-bootstrap/sysa/tcc-0.9.26/simple-patches/remove-fileopen.before

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.26/simple-patches/remove-fileopen.before

../git/live-bootstrap/sysa/tcc-0.9.26/simple-patches/remove-fileopen.after

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.26/simple-patches/remove-fileopen.after

../git/live-bootstrap/sysa/tcc-0.9.26/simple-patches/addback-fileopen.before

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.26/simple-patches/addback-fileopen.before

../git/live-bootstrap/sysa/tcc-0.9.26/simple-patches/addback-fileopen.after

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.26/simple-patches/addback-fileopen.after

../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tcc.c

Source: https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tcc.c

../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/libtcc.c

Source: https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/libtcc.c

../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tcc.h

Source: https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tcc.h

../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tccpp.c

Source: https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tccpp.c

../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tccgen.c

Source: https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tccgen.c

../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tccelf.c

Source: https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tccelf.c

../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tccrun.c

Source: https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tccrun.c

../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/i386-gen.c

Source: https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/i386-gen.c

../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/i386-link.c

Source: https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/i386-link.c

../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/i386-asm.c

Source: https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/i386-asm.c

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/isalnum.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isalnum.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/isalpha.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isalpha.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/isascii.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isascii.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/iscntrl.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/iscntrl.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/isdigit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isdigit.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/isgraph.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isgraph.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/islower.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/islower.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/isnumber.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isnumber.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/isprint.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isprint.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/ispunct.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/ispunct.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/isspace.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isspace.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/isupper.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isupper.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/isxdigit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isxdigit.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/tolower.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/tolower.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/ctype/toupper.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/toupper.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/dirent/closedir.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/dirent/closedir.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/dirent/__getdirentries.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/dirent/__getdirentries.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/dirent/opendir.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/dirent/opendir.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/dirent/readdir.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/dirent/readdir.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/access.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/access.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/brk.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/brk.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/chdir.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/chdir.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/chmod.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/chmod.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/clock_gettime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/clock_gettime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/close.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/close.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/dup2.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/dup2.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/dup.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/dup.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/execve.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/execve.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/fcntl.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fcntl.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/fork.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fork.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/fsync.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fsync.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/fstat.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fstat.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/_getcwd.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/_getcwd.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/getdents.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getdents.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/getegid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getegid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/geteuid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/geteuid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/getgid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getgid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/getpid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getpid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/getppid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getppid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/getrusage.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getrusage.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/gettimeofday.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/gettimeofday.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/getuid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getuid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/ioctl.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/ioctl.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/ioctl3.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/ioctl3.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/kill.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/kill.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/link.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/link.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/lseek.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/lseek.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/lstat.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/lstat.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/malloc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/malloc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/mkdir.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/mkdir.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/mknod.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/mknod.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/nanosleep.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/nanosleep.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/_open3.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/_open3.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/pipe.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/pipe.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/_read.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/_read.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/readlink.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/readlink.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/rename.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/rename.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/rmdir.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/rmdir.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/setgid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/setgid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/settimer.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/settimer.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/setuid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/setuid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/signal.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/signal.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/sigprogmask.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/sigprogmask.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/symlink.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/symlink.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/stat.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/stat.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/time.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/time.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/unlink.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/unlink.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/waitpid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/waitpid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/x86-mes-gcc/_exit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/_exit.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/x86-mes-gcc/syscall.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/syscall.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/x86-mes-gcc/_write.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/_write.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/math/ceil.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/math/ceil.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/math/fabs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/math/fabs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/math/floor.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/math/floor.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/abtod.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/abtod.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/abtol.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/abtol.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/__assert_fail.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/__assert_fail.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/assert_msg.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/assert_msg.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/__buffered_read.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/__buffered_read.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/cast.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/cast.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/dtoab.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/dtoab.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/eputc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/eputc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/eputs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/eputs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/fdgetc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdgetc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/fdgets.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdgets.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/fdputc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdputc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/fdputs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdputs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/fdungetc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdungetc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/globals.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/globals.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/itoa.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/itoa.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/ltoab.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ltoab.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/ltoa.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ltoa.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/__mes_debug.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/__mes_debug.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/mes_open.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/mes_open.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/ntoab.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ntoab.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/oputc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/oputc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/oputs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/oputs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/search-path.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/search-path.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/ultoa.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ultoa.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/mes/utoa.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/utoa.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/alarm.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/alarm.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/buffered-read.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/buffered-read.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/execl.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execl.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/execlp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execlp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/execv.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execv.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/execvp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execvp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/getcwd.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/getcwd.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/getenv.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/getenv.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/isatty.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/isatty.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/mktemp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/mktemp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/open.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/open.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/raise.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/raise.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/sbrk.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/sbrk.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/setenv.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/setenv.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/sleep.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/sleep.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/unsetenv.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/unsetenv.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/wait.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/wait.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/write.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/write.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/clearerr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/clearerr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fclose.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fclose.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fdopen.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fdopen.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/feof.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/feof.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/ferror.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/ferror.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fflush.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fflush.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fgetc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fgetc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fgets.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fgets.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fileno.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fileno.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fopen.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fopen.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fputc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fputc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fputs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fputs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fread.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fread.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/freopen.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/freopen.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fscanf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fscanf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fseek.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fseek.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/ftell.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/ftell.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/fwrite.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fwrite.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/getc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/getc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/getchar.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/getchar.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/perror.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/perror.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/printf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/printf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/putc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/putc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/putchar.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/putchar.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/remove.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/remove.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/snprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/snprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/sprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/sprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/sscanf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/sscanf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/ungetc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/ungetc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/vfprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vfprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/vfscanf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vfscanf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/vprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/vsnprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vsnprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/vsprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vsprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdio/vsscanf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vsscanf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/abort.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/abort.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/abs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/abs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/alloca.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/alloca.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/atexit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/atexit.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/atof.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/atof.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/atoi.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/atoi.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/atol.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/atol.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/calloc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/calloc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/__exit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/__exit.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/exit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/exit.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/free.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/free.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/mbstowcs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/mbstowcs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/puts.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/puts.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/qsort.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/qsort.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/realloc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/realloc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/strtod.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtod.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/strtof.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtof.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/strtol.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtol.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/strtold.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtold.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/strtoll.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtoll.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/strtoul.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtoul.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stdlib/strtoull.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtoull.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/bcmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/bcmp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/bcopy.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/bcopy.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/bzero.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/bzero.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/index.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/index.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/memchr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memchr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/memcmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memcmp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/memcpy.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memcpy.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/memmem.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memmem.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/memmove.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memmove.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/memset.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memset.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/rindex.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/rindex.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strcat.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcat.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strchr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strchr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strcmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcmp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strcpy.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcpy.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strcspn.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcspn.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strdup.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strdup.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strerror.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strerror.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strlen.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strlen.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strlwr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strlwr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strncat.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strncat.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strncmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strncmp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strncpy.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strncpy.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strpbrk.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strpbrk.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strrchr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strrchr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strspn.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strspn.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strstr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strstr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/string/strupr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strupr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/atan2.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/atan2.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/bsearch.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/bsearch.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/chown.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/chown.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/__cleanup.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/__cleanup.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/cos.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/cos.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/ctime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/ctime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/exp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/exp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/fpurge.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/fpurge.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/freadahead.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/freadahead.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/frexp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/frexp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/getgrgid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getgrgid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/getgrnam.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getgrnam.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/getlogin.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getlogin.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/getpgid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getpgid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/getpgrp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getpgrp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/getpwnam.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getpwnam.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/getpwuid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getpwuid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/gmtime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/gmtime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/ldexp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/ldexp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/localtime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/localtime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/log.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/log.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/mktime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/mktime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/modf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/modf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/mprotect.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/mprotect.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/pclose.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/pclose.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/popen.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/popen.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/pow.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/pow.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/rand.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/rand.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/rewind.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/rewind.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/setbuf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/setbuf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/setgrent.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/setgrent.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/setlocale.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/setlocale.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/setvbuf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/setvbuf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/sigaction.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigaction.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/sigaddset.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigaddset.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/sigblock.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigblock.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/sigdelset.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigdelset.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/sigemptyset.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigemptyset.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/sigsetmask.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigsetmask.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/sin.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sin.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/sys_siglist.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sys_siglist.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/system.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/system.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/sqrt.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sqrt.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/strftime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/strftime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/times.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/times.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/ttyname.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/ttyname.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/umask.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/umask.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/stub/utime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/utime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/x86-mes-gcc/setjmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/x86-mes-gcc/setjmp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/x86-mes-gcc/crt1.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/crt1.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/mes/lib-mini.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/lib-mini.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/x86-mes-gcc/crtn.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/crtn.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/x86-mes-gcc/crti.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/crti.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/ctype.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/ctype.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/mes/lib.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/lib.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/errno.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/errno.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/stddef.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stddef.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/stdlib.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdlib.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/dirent.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/dirent.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/unistd.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/unistd.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/dirstream.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/dirstream.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/stdio.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdio.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/limits.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/limits.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/fcntl.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/fcntl.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/sys/types.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/types.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/sys/stat.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/stat.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/string.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/string.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/assert.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/assert.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/linux/syscall.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/linux/syscall.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/time.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/time.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/stdarg.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdarg.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/sys/resource.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/resource.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/sys/time.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/time.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/sys/ioctl.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/ioctl.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/stdint.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdint.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/signal.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/signal.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/linux/x86/syscall.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/linux/x86/syscall.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/math.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/math.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/termio.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/termio.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/sys/wait.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/wait.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/sys/mman.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/mman.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/grp.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/grp.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/pwd.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/pwd.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/locale.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/locale.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/sys/times.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/times.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/setjmp.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/setjmp.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/libtcc1.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/libtcc1.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/getopt.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/getopt.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/include/getopt.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/getopt.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/lib/libtcc1.c

Source: https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/lib/libtcc1.c

../git/live-bootstrap/sysa/tcc-0.9.26/tcc-0.9.26.checksums

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.26/tcc-0.9.26.checksums

../git/live-bootstrap/sysa/tcc-0.9.27/sources

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/sources

/sysa/distfiles/tcc-0.9.27.tar.bz2

Source: https://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27.tar.bz2

/sysa/tcc-0.9.27/simple-patches/remove-fileopen.before

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/remove-fileopen.before

/sysa/tcc-0.9.27/simple-patches/remove-fileopen.after

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/remove-fileopen.after

/sysa/tcc-0.9.27/simple-patches/addback-fileopen.before

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/addback-fileopen.before

/sysa/tcc-0.9.27/simple-patches/addback-fileopen.after

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/addback-fileopen.after

/sysa/tcc-0.9.27/build/tcc-0.9.27/tccelf.c

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tccelf.c

/sysa/tcc-0.9.27/simple-patches/fiwix-paddr.before

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/fiwix-paddr.before

/sysa/tcc-0.9.27/simple-patches/fiwix-paddr.after

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/fiwix-paddr.after

/sysa/tcc-0.9.27/simple-patches/check-reloc-null.before

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/check-reloc-null.before

/sysa/tcc-0.9.27/simple-patches/check-reloc-null.after

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/check-reloc-null.after

../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tcc.c

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tcc.c

../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tcc.h

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tcc.h

../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/libtcc.c

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/libtcc.c

../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tccpp.c

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tccpp.c

../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tcctok.h

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tcctok.h

../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tccgen.c

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tccgen.c

../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tccelf.c

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tccelf.c

../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tccrun.c

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tccrun.c

../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/i386-gen.c

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/i386-gen.c

../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/i386-link.c

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/i386-link.c

../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/i386-asm.c

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/i386-asm.c

../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tcctools.c

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tcctools.c

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/isalnum.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isalnum.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/isalpha.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isalpha.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/isascii.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isascii.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/iscntrl.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/iscntrl.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/isdigit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isdigit.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/isgraph.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isgraph.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/islower.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/islower.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/isnumber.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isnumber.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/isprint.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isprint.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/ispunct.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/ispunct.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/isspace.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isspace.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/isupper.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isupper.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/isxdigit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isxdigit.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/tolower.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/tolower.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/ctype/toupper.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/toupper.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/dirent/closedir.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/dirent/closedir.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/dirent/__getdirentries.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/dirent/__getdirentries.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/dirent/opendir.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/dirent/opendir.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/dirent/readdir.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/dirent/readdir.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/access.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/access.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/brk.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/brk.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/chdir.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/chdir.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/chmod.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/chmod.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/clock_gettime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/clock_gettime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/close.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/close.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/dup2.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/dup2.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/dup.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/dup.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/execve.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/execve.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/fcntl.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fcntl.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/fork.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fork.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/fsync.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fsync.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/fstat.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fstat.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/_getcwd.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/_getcwd.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/getdents.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getdents.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/getegid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getegid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/geteuid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/geteuid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/getgid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getgid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/getpid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getpid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/getppid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getppid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/getrusage.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getrusage.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/gettimeofday.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/gettimeofday.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/getuid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getuid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/ioctl.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/ioctl.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/ioctl3.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/ioctl3.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/kill.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/kill.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/link.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/link.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/lseek.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/lseek.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/lstat.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/lstat.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/malloc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/malloc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/mkdir.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/mkdir.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/mknod.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/mknod.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/nanosleep.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/nanosleep.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/_open3.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/_open3.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/pipe.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/pipe.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/_read.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/_read.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/readlink.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/readlink.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/rename.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/rename.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/rmdir.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/rmdir.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/setgid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/setgid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/settimer.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/settimer.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/setuid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/setuid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/signal.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/signal.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/sigprogmask.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/sigprogmask.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/symlink.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/symlink.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/stat.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/stat.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/time.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/time.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/unlink.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/unlink.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/waitpid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/waitpid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/x86-mes-gcc/_exit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/_exit.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/x86-mes-gcc/syscall.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/syscall.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/x86-mes-gcc/_write.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/_write.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/math/ceil.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/math/ceil.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/math/fabs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/math/fabs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/math/floor.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/math/floor.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/abtod.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/abtod.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/abtol.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/abtol.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/__assert_fail.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/__assert_fail.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/assert_msg.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/assert_msg.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/__buffered_read.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/__buffered_read.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/cast.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/cast.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/dtoab.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/dtoab.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/eputc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/eputc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/eputs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/eputs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/fdgetc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdgetc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/fdgets.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdgets.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/fdputc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdputc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/fdputs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdputs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/fdungetc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdungetc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/globals.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/globals.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/itoa.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/itoa.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/ltoab.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ltoab.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/ltoa.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ltoa.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/__mes_debug.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/__mes_debug.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/mes_open.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/mes_open.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/ntoab.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ntoab.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/oputc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/oputc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/oputs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/oputs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/search-path.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/search-path.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/ultoa.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ultoa.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/mes/utoa.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/utoa.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/alarm.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/alarm.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/buffered-read.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/buffered-read.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/execl.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execl.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/execlp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execlp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/execv.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execv.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/execvp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execvp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/getcwd.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/getcwd.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/getenv.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/getenv.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/isatty.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/isatty.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/mktemp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/mktemp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/open.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/open.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/raise.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/raise.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/sbrk.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/sbrk.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/setenv.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/setenv.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/sleep.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/sleep.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/unsetenv.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/unsetenv.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/wait.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/wait.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/posix/write.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/write.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/clearerr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/clearerr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fclose.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fclose.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fdopen.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fdopen.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/feof.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/feof.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/ferror.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/ferror.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fflush.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fflush.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fgetc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fgetc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fgets.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fgets.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fileno.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fileno.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fopen.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fopen.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fputc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fputc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fputs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fputs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fread.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fread.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/freopen.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/freopen.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fscanf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fscanf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fseek.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fseek.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/ftell.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/ftell.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/fwrite.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fwrite.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/getc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/getc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/getchar.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/getchar.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/perror.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/perror.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/printf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/printf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/putc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/putc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/putchar.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/putchar.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/remove.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/remove.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/snprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/snprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/sprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/sprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/sscanf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/sscanf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/ungetc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/ungetc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/vfprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vfprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/vfscanf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vfscanf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/vprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/vsnprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vsnprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/vsprintf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vsprintf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdio/vsscanf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vsscanf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/abort.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/abort.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/abs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/abs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/alloca.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/alloca.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/atexit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/atexit.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/atof.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/atof.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/atoi.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/atoi.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/atol.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/atol.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/calloc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/calloc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/__exit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/__exit.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/exit.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/exit.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/free.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/free.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/mbstowcs.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/mbstowcs.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/puts.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/puts.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/qsort.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/qsort.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/realloc.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/realloc.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/strtod.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtod.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/strtof.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtof.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/strtol.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtol.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/strtold.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtold.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/strtoll.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtoll.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/strtoul.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtoul.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stdlib/strtoull.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtoull.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/bcmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/bcmp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/bcopy.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/bcopy.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/bzero.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/bzero.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/index.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/index.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/memchr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memchr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/memcmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memcmp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/memcpy.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memcpy.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/memmem.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memmem.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/memmove.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memmove.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/memset.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memset.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/rindex.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/rindex.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strcat.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcat.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strchr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strchr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strcmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcmp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strcpy.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcpy.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strcspn.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcspn.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strdup.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strdup.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strerror.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strerror.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strlen.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strlen.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strlwr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strlwr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strncat.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strncat.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strncmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strncmp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strncpy.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strncpy.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strpbrk.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strpbrk.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strrchr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strrchr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strspn.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strspn.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strstr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strstr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/string/strupr.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strupr.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/atan2.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/atan2.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/bsearch.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/bsearch.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/chown.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/chown.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/__cleanup.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/__cleanup.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/cos.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/cos.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/ctime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/ctime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/exp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/exp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/fpurge.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/fpurge.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/freadahead.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/freadahead.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/frexp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/frexp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/getgrgid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getgrgid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/getgrnam.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getgrnam.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/getlogin.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getlogin.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/getpgid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getpgid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/getpgrp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getpgrp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/getpwnam.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getpwnam.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/getpwuid.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getpwuid.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/gmtime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/gmtime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/ldexp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/ldexp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/localtime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/localtime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/log.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/log.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/mktime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/mktime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/modf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/modf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/mprotect.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/mprotect.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/pclose.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/pclose.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/popen.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/popen.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/pow.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/pow.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/rand.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/rand.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/rewind.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/rewind.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/setbuf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/setbuf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/setgrent.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/setgrent.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/setlocale.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/setlocale.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/setvbuf.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/setvbuf.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/sigaction.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigaction.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/sigaddset.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigaddset.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/sigblock.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigblock.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/sigdelset.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigdelset.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/sigemptyset.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigemptyset.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/sigsetmask.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigsetmask.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/sin.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sin.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/sys_siglist.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sys_siglist.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/system.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/system.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/sqrt.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sqrt.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/strftime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/strftime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/times.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/times.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/ttyname.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/ttyname.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/umask.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/umask.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/stub/utime.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/utime.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/x86-mes-gcc/setjmp.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/x86-mes-gcc/setjmp.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/x86-mes-gcc/crt1.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/crt1.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/mes/lib-mini.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/lib-mini.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/x86-mes-gcc/crtn.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/crtn.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/linux/x86-mes-gcc/crti.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/crti.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/lib/libtcc1.c

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/lib/libtcc1.c?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/mes/lib.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/lib.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/lib/libtcc1.c

Source: https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/lib/libtcc1.c

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/ctype.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/ctype.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/errno.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/errno.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/stddef.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stddef.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/stdlib.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdlib.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/dirent.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/dirent.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/unistd.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/unistd.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/dirstream.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/dirstream.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/stdio.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdio.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/limits.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/limits.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/fcntl.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/fcntl.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/sys/types.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/types.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/sys/stat.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/stat.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/string.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/string.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/assert.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/assert.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/linux/syscall.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/linux/syscall.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/time.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/time.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/stdarg.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdarg.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/sys/resource.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/resource.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/sys/time.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/time.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/sys/ioctl.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/ioctl.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/stdint.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdint.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/signal.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/signal.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/linux/x86/syscall.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/linux/x86/syscall.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/math.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/math.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/termio.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/termio.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/sys/wait.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/wait.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/sys/mman.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/mman.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/grp.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/grp.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/pwd.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/pwd.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/locale.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/locale.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/sys/times.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/times.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/include/setjmp.h

Source: https://git.savannah.gnu.org/cgit/mes.git/tree/include/setjmp.h?h=v0.24.2

../git/live-bootstrap/sysa/tcc-0.9.27/tcc-0.9.27.checksums

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/tcc-0.9.27.checksums

../git/live-bootstrap/sysa/make-3.82/sources

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/make-3.82/sources

/sysa/distfiles/make-3.82.tar.bz2

Source: https://mirrors.kernel.org/gnu/make/make-3.82.tar.bz2

../git/live-bootstrap/sysa/make-3.82/files/putenv_stub.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/make-3.82/files/putenv_stub.c

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/getopt.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/getopt.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/getopt1.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/getopt1.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/ar.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/ar.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/make.h

Source: https://git.savannah.gnu.org/cgit/make.git/tree/make.h?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/filedef.h

Source: https://git.savannah.gnu.org/cgit/make.git/tree/filedef.h?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/hash.h

Source: https://git.savannah.gnu.org/cgit/make.git/tree/hash.h?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/dep.h

Source: https://git.savannah.gnu.org/cgit/make.git/tree/dep.h?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/glob/fnmatch.h

Source: https://git.savannah.gnu.org/cgit/make.git/tree/glob/fnmatch.h?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/arscan.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/arscan.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/commands.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/commands.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/variable.h

Source: https://git.savannah.gnu.org/cgit/make.git/tree/variable.h?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/job.h

Source: https://git.savannah.gnu.org/cgit/make.git/tree/job.h?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/commands.h

Source: https://git.savannah.gnu.org/cgit/make.git/tree/commands.h?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/default.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/default.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/rule.h

Source: https://git.savannah.gnu.org/cgit/make.git/tree/rule.h?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/dir.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/dir.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/expand.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/expand.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/file.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/file.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/debug.h

Source: https://git.savannah.gnu.org/cgit/make.git/tree/debug.h?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/function.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/function.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/implicit.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/implicit.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/job.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/job.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/main.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/main.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/getopt.h

Source: https://git.savannah.gnu.org/cgit/make.git/tree/getopt.h?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/misc.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/misc.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/read.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/read.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/glob/glob.h

Source: https://git.savannah.gnu.org/cgit/make.git/tree/glob/glob.h?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/remake.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/remake.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/rule.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/rule.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/signame.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/signame.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/strcache.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/strcache.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/variable.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/variable.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/version.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/version.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/vpath.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/vpath.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/hash.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/hash.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/remote-stub.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/remote-stub.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/getloadavg.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/getloadavg.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/glob/fnmatch.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/glob/fnmatch.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/build/make-3.82/glob/glob.c

Source: https://git.savannah.gnu.org/cgit/make.git/tree/glob/glob.c?h=3.82

../git/live-bootstrap/sysa/make-3.82/make-3.82.checksums

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/make-3.82/make-3.82.checksums

../git/live-bootstrap/sysa/patch-2.5.9/sources

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/sources

/sysa/distfiles/patch-2.5.9.tar.gz

Source: https://mirrors.kernel.org/gnu/patch/patch-2.5.9.tar.gz

../git/live-bootstrap/sysa/patch-2.5.9/mk/main.mk

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/mk/main.mk

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/error.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/error.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/getopt.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/getopt.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/getopt1.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/getopt1.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/addext.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/addext.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/argmatch.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/argmatch.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/backupfile.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/backupfile.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/basename.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/basename.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/dirname.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/dirname.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/inp.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/inp.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/common.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/common.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/backupfile.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/backupfile.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/pch.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/pch.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/quotearg.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/quotearg.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/util.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/util.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/xalloc.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/xalloc.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/inp.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/inp.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/maketime.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/maketime.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/partime.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/partime.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/patch.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/patch.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/argmatch.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/argmatch.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/getopt.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/getopt.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/version.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/version.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/pch.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/pch.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/dirname.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/dirname.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/quote.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/quote.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/quotearg.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/quotearg.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/quotesys.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/quotesys.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/util.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/util.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/quotesys.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/quotesys.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/maketime.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/maketime.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/partime.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/partime.h

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/version.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/version.c

../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/xmalloc.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/xmalloc.c

../git/live-bootstrap/sysa/patch-2.5.9/patch-2.5.9.checksums

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/patch-2.5.9.checksums

../git/live-bootstrap/sysa/gzip-1.2.4/sources

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/sources

/sysa/distfiles/gzip-1.2.4.tar.gz

Source: https://mirrors.kernel.org/gnu/gzip/gzip-1.2.4.tar.gz

../git/live-bootstrap/sysa/gzip-1.2.4/mk/main.mk

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/mk/main.mk

../git/live-bootstrap/sysa/gzip-1.2.4/files/stat_override.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/files/stat_override.c

../git/live-bootstrap/sysa/gzip-1.2.4/patches/removecrc.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/patches/removecrc.patch

../git/live-bootstrap/sysa/gzip-1.2.4/patches/makecrc-write-to-file.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/patches/makecrc-write-to-file.patch

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/sample/makecrc.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/sample/makecrc.c

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/crc.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/crc.c

NoInput

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/bits.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/bits.c

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/tailor.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/tailor.h

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/gzip.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/gzip.h

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/crypt.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/crypt.h

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/crypt.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/crypt.c

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/deflate.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/deflate.c

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/lzw.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/lzw.h

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/getopt.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/getopt.c

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/inflate.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/inflate.c

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/lzw.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/lzw.c

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/trees.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/trees.c

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/unlzh.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/unlzh.c

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/unlzw.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/unlzw.c

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/unpack.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/unpack.c

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/unzip.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/unzip.c

../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/zip.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/zip.c

../git/live-bootstrap/sysa/gzip-1.2.4/gzip-1.2.4.checksums

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/gzip-1.2.4.checksums

../git/live-bootstrap/sysa/tar-1.12/sources

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tar-1.12/sources

/sysa/distfiles/tar-1.12.tar.gz

Source: https://mirrors.kernel.org/gnu/tar/tar-1.12.tar.gz

../git/live-bootstrap/sysa/tar-1.12/mk/main.mk

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tar-1.12/mk/main.mk

../git/live-bootstrap/sysa/tar-1.12/files/getdate_stub.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tar-1.12/files/getdate_stub.c

../git/live-bootstrap/sysa/tar-1.12/files/stat_override.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tar-1.12/files/stat_override.c

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/argmatch.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/argmatch.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/backupfile.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/backupfile.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/error.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/error.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/fnmatch.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/fnmatch.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/ftruncate.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/ftruncate.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/getdate.h

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/getdate.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/getopt.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/getopt.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/getopt1.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/getopt1.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/getversion.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/getversion.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/modechange.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/modechange.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/msleep.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/msleep.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/xgetcwd.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/xgetcwd.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/xmalloc.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/xmalloc.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/xstrdup.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/xstrdup.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/arith.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/arith.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/system.h

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/system.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/common.h

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/common.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/tar.h

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/tar.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/buffer.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/buffer.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/compare.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/compare.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/delete.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/delete.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/rmt.h

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/rmt.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/extract.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/extract.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/incremen.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/incremen.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/list.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/list.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/mangle.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/mangle.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/misc.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/misc.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/backupfile.h

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/backupfile.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/names.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/names.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/fnmatch.h

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/fnmatch.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/open3.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/open3.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/rtapelib.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/rtapelib.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/tar.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/tar.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/getopt.h

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//lib/getopt.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/update.c

Source: https://git.savannah.gnu.org/cgit/tar.git/tree//src/update.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

../git/live-bootstrap/sysa/tar-1.12/tar-1.12.checksums

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tar-1.12/tar-1.12.checksums

../git/live-bootstrap/sysa/sed-4.0.9/sources

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/sed-4.0.9/sources

/sysa/distfiles/sed-4.0.9.tar.gz

Source: https://mirrors.kernel.org/gnu/sed/sed-4.0.9.tar.gz

../git/live-bootstrap/sysa/sed-4.0.9/mk/main.mk

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/sed-4.0.9/mk/main.mk

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/alloca.c

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/lib/alloca.c?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/getopt1.c

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/lib/getopt1.c?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/getopt.c

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/lib/getopt.c?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/utils.c

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/lib/utils.c?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/regex_.h

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/lib/regex_.h?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/regex.c

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/lib/regex.c?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/obstack.c

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/lib/obstack.c?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/obstack.h

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/lib/obstack.h?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/strverscmp.c

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/lib/strverscmp.c?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/mkstemp.c

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/lib/mkstemp.c?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/sed/compile.c

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/sed/compile.c?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/sed/sed.h

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.h?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/strverscmp.h

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/lib/strverscmp.h?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/sed/execute.c

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/sed/execute.c?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/sed/regexp.c

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/sed/regexp.c?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/sed/fmt.c

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/sed/fmt.c?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/sed/sed.c

Source: https://git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c?id=9c9919efe2166efd32409054005619062624226c

../git/live-bootstrap/sysa/sed-4.0.9/sed-4.0.9.checksums

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/sed-4.0.9/sed-4.0.9.checksums

../git/live-bootstrap/sysa/bzip2-1.0.8/sources

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bzip2-1.0.8/sources

/sysa/distfiles/bzip2-1.0.8.tar.gz

Source: https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz

../git/live-bootstrap/sysa/bzip2-1.0.8/patches/mes-libc.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bzip2-1.0.8/patches/mes-libc.patch

../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/bzip2.c

Source: https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/bzip2.c

../git/live-bootstrap/sysa/bzip2-1.0.8/patches/coreutils.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bzip2-1.0.8/patches/coreutils.patch

../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/Makefile

Source: https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/Makefile

../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/blocksort.c

Source: https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/blocksort.c

../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/bzlib_private.h

Source: https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/bzlib_private.h

../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/bzlib.h

Source: https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/bzlib.h

../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/huffman.c

Source: https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/huffman.c

../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/crctable.c

Source: https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/crctable.c

../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/randtable.c

Source: https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/randtable.c

../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/compress.c

Source: https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/compress.c

../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/decompress.c

Source: https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/decompress.c

../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/bzlib.c

Source: https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/bzlib.c

../git/live-bootstrap/sysa/bzip2-1.0.8/bzip2-1.0.8.checksums

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bzip2-1.0.8/bzip2-1.0.8.checksums

../git/live-bootstrap/sysa/coreutils-5.0/sources

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/sources

/sysa/distfiles/coreutils-5.0.tar.bz2

Source: https://mirrors.kernel.org/gnu/coreutils/coreutils-5.0.tar.bz2

../git/live-bootstrap/sysa/coreutils-5.0/mk/main.mk

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/mk/main.mk

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/fnmatch_.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/fnmatch_.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/ftw_.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/ftw_.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/search_.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/search_.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/patches/modechange.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/modechange.patch

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/modechange.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/modechange.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/patches/mbstate.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/mbstate.patch

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/quotearg.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/quotearg.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/patches/ls-strcmp.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/ls-strcmp.patch

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/ls.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/patches/touch-getdate.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/touch-getdate.patch

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/touch.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/touch.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/patches/touch-dereference.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/touch-dereference.patch

../git/live-bootstrap/sysa/coreutils-5.0/patches/tac-uint64.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/tac-uint64.patch

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/tempname.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/tempname.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/patches/expr-strcmp.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/expr-strcmp.patch

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/expr.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/expr.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/patches/sort-locale.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/sort-locale.patch

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/memcoll.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/memcoll.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/sort.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/sort.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/patches/uniq-fopen.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/uniq-fopen.patch

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/uniq.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/uniq.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/basename.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/basename.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/system.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/system.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/long-options.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/long-options.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/dirname.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/dirname.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/error.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/error.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/closeout.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/closeout.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/acl.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/acl.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/posixtm.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/posixtm.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/posixver.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/posixver.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/strftime.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/strftime.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getopt.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getopt.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getopt1.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getopt1.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/hash.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/hash.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/hash-pjw.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/hash-pjw.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/addext.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/addext.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/argmatch.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/argmatch.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/backupfile.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/backupfile.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/basename.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/basename.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/canon-host.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/canon-host.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/closeout.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/closeout.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/cycle-check.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/cycle-check.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/diacrit.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/diacrit.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/dirname.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/dirname.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/dup-safer.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/dup-safer.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/error.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/error.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/exclude.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/exclude.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/exitfail.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/exitfail.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/filemode.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/filemode.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/__fpending.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/__fpending.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/file-type.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/file-type.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/fnmatch.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/fnmatch.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/fopen-safer.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/fopen-safer.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/full-read.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/full-read.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/full-write.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/full-write.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/gethostname.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/gethostname.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getline.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getline.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getstr.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getstr.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/gettime.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/gettime.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/hard-locale.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/hard-locale.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/human.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/human.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/idcache.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/idcache.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/isdir.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/isdir.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/imaxtostr.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/imaxtostr.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/inttostr.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/inttostr.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/inttostr.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/inttostr.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/linebuffer.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/linebuffer.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/localcharset.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/localcharset.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/long-options.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/long-options.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/makepath.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/makepath.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/mbswidth.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/mbswidth.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/md5.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/md5.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/memcasecmp.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/memcasecmp.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/offtostr.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/offtostr.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/path-concat.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/path-concat.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/physmem.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/physmem.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/quote.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/quote.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/readtokens.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/readtokens.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/rpmatch.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/rpmatch.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/safe-read.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/safe-read.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/safe-write.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/safe-write.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/same.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/same.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/save-cwd.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/save-cwd.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/savedir.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/savedir.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/settime.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/settime.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/sha.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/sha.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/stpcpy.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/stpcpy.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/stripslash.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/stripslash.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/strtoimax.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/strtoimax.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/strtoumax.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/strtoumax.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/umaxtostr.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/umaxtostr.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/unicodeio.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/unicodeio.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/userspec.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/userspec.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/version-etc.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/version-etc.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xgetcwd.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xgetcwd.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xgethostname.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xgethostname.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xmalloc.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xmalloc.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xmemcoll.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xmemcoll.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xnanosleep.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xnanosleep.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xreadlink.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xreadlink.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrdup.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrdup.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtod.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtod.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtol.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtol.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtoul.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtoul.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtoimax.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtoimax.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtoumax.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtoumax.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/yesno.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/yesno.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/strnlen.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/strnlen.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getcwd.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getcwd.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/pathmax.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/pathmax.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/same.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/same.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/sig2str.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/sig2str.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/mountlist.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/mountlist.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/regex.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/regex.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/canonicalize.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/canonicalize.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/mkstemp.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/mkstemp.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/memrchr.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/memrchr.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/euidaccess.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/euidaccess.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/ftw.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/ftw.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/dirfd.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/dirfd.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/obstack.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/obstack.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/strverscmp.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/strverscmp.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/tsearch.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/tsearch.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/cat.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/cat.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getopt.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getopt.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/full-write.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/full-write.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/safe-read.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/safe-read.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/chmod.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/chmod.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/filemode.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/filemode.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/modechange.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/modechange.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/quote.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/quote.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/savedir.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/savedir.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/cksum.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/cksum.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/csplit.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/csplit.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/regex.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/regex.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtol.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtol.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/cut.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/cut.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getstr.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getstr.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/dirname.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/dirname.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/echo.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/echo.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/expand.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/expand.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/posixver.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/posixver.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/factor.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/factor.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/readtokens.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/readtokens.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/wheel-size.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/wheel-size.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/wheel.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/wheel.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/true.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/true.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/version-etc.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/version-etc.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/fmt.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/fmt.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/fold.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/fold.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/head.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/head.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/hostname.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/hostname.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/id.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/id.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/join.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/join.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/hard-locale.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/hard-locale.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/linebuffer.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/linebuffer.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/memcasecmp.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/memcasecmp.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xmemcoll.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xmemcoll.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/kill.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/kill.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/link.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/link.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/ln.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ln.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/logname.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/logname.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/mkfifo.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/mkfifo.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/mkdir.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/mkdir.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/makepath.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/makepath.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/mknod.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/mknod.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/nl.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/nl.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/od.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/od.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/paste.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/paste.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/pathchk.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/pathchk.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/pr.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/pr.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/mbswidth.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/mbswidth.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/printf.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/printf.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/unicodeio.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/unicodeio.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/ptx.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ptx.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/argmatch.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/argmatch.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/bumpalloc.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/bumpalloc.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/diacrit.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/diacrit.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/pwd.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/pwd.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xgetcwd.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xgetcwd.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/readlink.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/readlink.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/canonicalize.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/canonicalize.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xreadlink.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xreadlink.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/rmdir.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/rmdir.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/seq.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/seq.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtod.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtod.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/sleep.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/sleep.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xnanosleep.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xnanosleep.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/physmem.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/physmem.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/stdio-safer.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/stdio-safer.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/split.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/split.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/full-read.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/full-read.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/sum.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/sum.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/human.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/human.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/tail.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/tail.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/tee.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/tee.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/tr.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/tr.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/tsort.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/tsort.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/unexpand.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/unexpand.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/unlink.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/unlink.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/wc.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/wc.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/whoami.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/whoami.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/tac.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/tac.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/test.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/test.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getdate.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getdate.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/posixtm.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/posixtm.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/yes.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/yes.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/cp.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/cp.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/copy.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/copy.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/cp-hash.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/cp-hash.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/hash.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/hash.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/cp-hash.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/cp-hash.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/ls-ls.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls-ls.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/ls.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/install.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/install.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/md5.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/md5.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/checksum.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/checksum.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/md5sum.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/md5sum.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/md5.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/md5.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/sha.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/sha.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getline.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getline.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/mv.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/mv.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/remove.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/remove.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/rm.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/rm.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/remove.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/remove.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/save-cwd.h

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/save-cwd.h?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/sha1sum.c

Source: https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/sha1sum.c?h=COREUTILS-5_0

../git/live-bootstrap/sysa/coreutils-5.0/coreutils-5.0.checksums

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/coreutils-5.0.checksums

../git/live-bootstrap/sysa/heirloom-devtools-070527/sources

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/sources

/sysa/distfiles/heirloom-devtools-070527.tar.bz2

Source: http://downloads.sourceforge.net/project/heirloom/heirloom-devtools/070527/heirloom-devtools-070527.tar.bz2

../git/live-bootstrap/sysa/heirloom-devtools-070527/patches/yacc_remove_wchar.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/patches/yacc_remove_wchar.patch

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/dextern

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/dextern

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y1.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y1.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y2.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y2.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y3.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y3.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y4.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y4.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/Makefile.mk

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/Makefile.mk

../git/live-bootstrap/sysa/heirloom-devtools-070527/patches/lex_remove_wchar.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/patches/lex_remove_wchar.patch

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/Makefile.mk

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/Makefile.mk

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/allprint.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/allprint.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/ldefs.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/ldefs.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/main.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/main.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/parser.y

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/parser.y

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/reject.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/reject.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sub1.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sub1.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sub3.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sub3.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/sgs.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/sgs.h

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/getopt.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/getopt.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/libmai.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/libmai.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/libzer.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/libzer.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/yaccpar

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/yaccpar

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/once.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/once.h

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sgs.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sgs.h

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sub2.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sub2.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/header.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/header.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/getopt.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/getopt.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/lsearch.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/lsearch.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/search.h

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/search.h

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/libmain.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/libmain.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/yyless.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/yyless.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/yywrap.c

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/yywrap.c

../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/ncform

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/ncform

../git/live-bootstrap/sysa/heirloom-devtools-070527/heirloom-devtools-070527.checksums

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/heirloom-devtools-070527.checksums

../git/live-bootstrap/sysa/bash-2.05b/sources

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/sources

/sysa/distfiles/bash-2.05b.tar.gz

Source: https://mirrors.kernel.org/gnu/bash/bash-2.05b.tar.gz

../git/live-bootstrap/sysa/bash-2.05b/mk/main.mk

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/mk/main.mk

../git/live-bootstrap/sysa/bash-2.05b/mk/builtins.mk

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/mk/builtins.mk

../git/live-bootstrap/sysa/bash-2.05b/mk/common.mk

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/mk/common.mk

../git/live-bootstrap/sysa/bash-2.05b/patches/mes-libc.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/patches/mes-libc.patch

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/snprintf.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/snprintf.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/patches/tinycc.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/patches/tinycc.patch

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/oslib.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/oslib.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/patches/missing-defines.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/patches/missing-defines.patch

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/execute_cmd.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/execute_cmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/patches/locale.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/patches/locale.patch

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/locale.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/locale.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/patches/dev-tty.patch

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/patches/dev-tty.patch

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/shell.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/shell.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/mkbuiltins.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/mkbuiltins.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/alias.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/alias.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/bind.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/bind.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/break.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/break.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/builtin.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/builtin.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/cd.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/cd.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/colon.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/colon.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/command.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/command.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/complete.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/complete.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/declare.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/declare.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/echo.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/echo.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/enable.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/enable.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/eval.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/eval.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/exec.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/exec.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/exit.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/exit.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/fc.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/fc.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/fg_bg.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/fg_bg.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/hash.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/hash.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/history.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/history.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/jobs.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/jobs.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/kill.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/kill.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/let.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/let.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/read.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/read.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/return.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/return.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/set.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/set.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/setattr.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/setattr.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/shift.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/shift.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/source.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/source.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/suspend.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/suspend.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/test.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/test.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/times.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/times.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/trap.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/trap.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/type.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/type.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/ulimit.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/ulimit.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/umask.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/umask.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/wait.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/wait.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/getopts.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/getopts.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/pushd.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/pushd.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/shopt.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/shopt.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/printf.def

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/printf.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/bashtypes.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/bashtypes.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/posixstat.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/include/posixstat.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/filecntl.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/include/filecntl.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/chartypes.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/include/chartypes.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/bashansi.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/bashansi.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/shell.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/shell.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/stdc.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/include/stdc.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/bashgetopt.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/bashgetopt.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/common.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/common.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/common.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/common.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/evalstring.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/evalstring.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/evalfile.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/evalfile.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/getopt.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/getopt.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/bashgetopt.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/bashgetopt.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/clktck.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/clktck.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/getcwd.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/getcwd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/getenv.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/getenv.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/setlinebuf.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/setlinebuf.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/xmalloc.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/xmalloc.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strcasecmp.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strcasecmp.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strerror.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strerror.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strtod.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strtod.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/vprint.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/vprint.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/itos.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/itos.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/rename.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/rename.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/zread.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/zread.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/zwrite.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/zwrite.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/shtty.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/shtty.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/inet_aton.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/inet_aton.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/netopen.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/netopen.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strpbrk.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strpbrk.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/timeval.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/timeval.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/clock.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/clock.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/makepath.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/makepath.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/pathcanon.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/pathcanon.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/pathphys.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/pathphys.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/stringlist.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/stringlist.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/stringvec.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/stringvec.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/tmpfile.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/tmpfile.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/posixtime.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/include/posixtime.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/spell.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/spell.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strtrans.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strtrans.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strindex.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strindex.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/shquote.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/shquote.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/mailstat.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/mailstat.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/posixdir.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/include/posixdir.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/maxpath.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/include/maxpath.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/fmtulong.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/fmtulong.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/fmtullong.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/fmtullong.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strtoll.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strtoll.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strtoull.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strtoull.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strtoimax.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strtoimax.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strtoumax.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strtoumax.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/fmtumax.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/fmtumax.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/netconn.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/netconn.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/mktime.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/mktime.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strftime.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strftime.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/xstrchr.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/xstrchr.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/zcatfd.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/zcatfd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/glob/glob.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/glob/glob.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/glob/strmatch.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/glob/strmatch.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/glob/strmatch.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/glob/strmatch.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/glob/smatch.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/glob/smatch.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/shmbutil.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/include/shmbutil.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/glob/xmbsrtowcs.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/glob/xmbsrtowcs.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/tilde/tilde.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/lib/tilde/tilde.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/eval.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/eval.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/parse.y

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/parse.y?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/general.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/general.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/make_cmd.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/make_cmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/print_cmd.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/print_cmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/dispose_cmd.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/dispose_cmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/variables.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/variables.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/copy_cmd.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/copy_cmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/error.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/error.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/expr.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/expr.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/flags.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/flags.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/nojobs.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/nojobs.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/subst.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/subst.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/hashcmd.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/hashcmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/hashlib.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/hashlib.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/mailcheck.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/mailcheck.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/support/mksignames.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/support/mksignames.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/trap.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/trap.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/input.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/input.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/unwind_prot.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/unwind_prot.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/pathexp.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/pathexp.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/sig.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/sig.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/test.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/test.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/version.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/version.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/patchlevel.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/patchlevel.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/conftypes.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/conftypes.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/alias.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/alias.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/array.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/array.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/arrayfunc.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/arrayfunc.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/braces.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/braces.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/bracecomp.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/bracecomp.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/bashhist.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/bashhist.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/bashline.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/bashline.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/list.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/list.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/stringlib.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/stringlib.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/findcmd.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/findcmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/redir.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/redir.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/pcomplete.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/pcomplete.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/pcomplib.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/pcomplib.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/mksyntax.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/mksyntax.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/syntax.h

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/syntax.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/xmalloc.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/xmalloc.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/siglist.c

Source: https://git.savannah.gnu.org/cgit/bash.git/tree/siglist.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

../git/live-bootstrap/sysa/bash-2.05b/bash-2.05b.checksums

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/bash-2.05b.checksums

../git/live-bootstrap/sysa/run.sh

Source: https://github.com/fosslinux/live-bootstrap/blob/master/sysa/run.sh

Steps

Step 1

Step 2

Step 3

Step 4

Step 5

Step 6

Step 7

Step 8

Step 9

Step 10

Step 11

Step 12

Step 13

Step 14

Step 15

Step 16

Step 17

Step 18

Step 19

Step 20

Step 21

Step 22

Step 23

Step 24

Step 25

Step 26

Step 27

Step 28

Step 29

Step 30

Step 31

Step 32

Step 33

Step 34

Step 35

Step 36

Step 37

Step 38

Step 39

Step 40

Step 41

Step 42

Step 43

Step 44

Step 45

Step 46

Step 47

Step 48

Step 49

Step 50

Step 51

Step 52

Step 53

Step 54

Step 55

Step 56

Step 57

Step 58

Step 59

Step 60

Step 61

Step 62

Step 63

Step 64

Step 65

Step 66

Step 67

Step 68

Step 69

Step 70

Step 71

Step 72

Step 73

Step 74

Step 75

Step 76

Step 77

Step 78

Step 79

Step 80

Step 81

Step 82

Step 83

Step 84

Step 85

Step 86

Step 87

Step 88

Step 89

Step 90

Step 91

Step 92

Step 93

Step 94

Step 95

Step 96

Step 97

Step 98

Step 99

Step 100

Step 101

Step 102

Step 103

Step 104

Step 105

Step 106

Step 107

Step 108

Step 109

Step 110

Step 111

Step 112

Step 113

Step 114

Step 115

Step 116

Step 117

Step 118

Step 119

Step 120

Step 121

Step 122

Step 123

Step 124

Step 125

Step 126

Step 127

Step 128

Step 129

Step 130

Step 131

Step 132

Step 133

Step 134

Step 135

Step 136

Step 137

Step 138

Step 139

Step 140

Step 141

Step 142

Step 143

Step 144

Step 145

Step 146

Step 147

Step 148

Step 149

Step 150

Step 151

Step 152

Step 153

Step 154

Step 155

Step 156

Step 157

Step 158

Step 159

Step 160

Step 161

Step 162

Step 163

Step 164

Step 165

Step 166

Step 167

Step 168

Step 169

Step 170

Step 171

Step 172

Step 173

Step 174

Step 175

Step 176

Step 177

Step 178

Step 179

Step 180

Step 181

Step 182

Step 183

Step 184

Step 185

Step 186

Step 187

Step 188

Step 189

Step 190

Step 191

Step 192

Step 193

Step 194

Step 195

Step 196

Step 197

Step 198

Step 199

Step 200

Step 201

Step 202

Step 203

Step 204

Step 205

Step 206

Step 207

Step 208

Step 209

Step 210

Step 211

Step 212

Step 213

Step 214

Step 215

Step 216

Step 217

Step 218

Step 219

Step 220

Step 221

Step 222

Step 223

Step 224

Step 225

Step 226

Step 227

Step 228

Step 229

Step 230

Step 231

Step 232

Step 233

Step 234

Step 235

Step 236

Step 237

Step 238

Step 239

Step 240

Step 241

Step 242

Step 243

Step 244

Step 245

Step 246

Step 247

Step 248

Step 249

Step 250

Step 251

Step 252

Step 253

Step 254

Step 255

Step 256

Step 257

Step 258

Step 259

Step 260

Step 261

Step 262

Step 263

Step 264

Step 265

Step 266

Step 267

Step 268

Step 269

Step 270

Step 271

Step 272

Step 273

Step 274

Step 275

Step 276

Step 277

Step 278

Step 279

Step 280

Step 281

Step 282

Step 283

Step 284

Step 285

Step 286

Step 287

Step 288

Step 289

Step 290

Step 291

Step 292

Step 293

Step 294

Step 295

Step 296

Step 297

Step 298

Step 299

Step 300

Step 301

Step 302

Step 303

Step 304

Step 305

Step 306

Step 307

Step 308

Step 309

Step 310

Step 311

Step 312

Step 313

Step 314

Step 315

Step 316

Step 317

Step 318

Step 319

Step 320

Step 321

Step 322

Step 323

Step 324

Step 325

Step 326

Step 327

Step 328

Step 329

Step 330

Step 331

Step 332

Step 333

Step 334

Step 335

Step 336

Step 337

Step 338

Step 339

Step 340

Step 341

Step 342

Step 343

Step 344

Step 345

Step 346

Step 347

Step 348

Step 349

Step 350

Step 351

Step 352

Step 353

Step 354

Step 355

Step 356

Step 357

Step 358

Step 359

Step 360

Step 361

Step 362

Step 363

Step 364

Step 365

Step 366

Step 367

Step 368

Step 369

Step 370

Step 371

Step 372

Step 373

Step 374

Step 375

Step 376

Step 377

Step 378

Step 379

Step 380

Step 381

Step 382

Step 383

Step 384

Step 385

Step 386

Step 387

Step 388

Step 389

Step 390

Step 391

Step 392

Step 393

Step 394

Step 395

Step 396

Step 397

Step 398

Step 399

Step 400

Step 401

Step 402

Step 403

Step 404

Step 405

Step 406

Step 407

Step 408

Step 409

Step 410

Step 411

Step 412

Step 413

Step 414

Step 415

Step 416

Step 417

Step 418

Step 419

Step 420

Step 421

Step 422

Step 423

Step 424

Step 425

Step 426

Step 427

Step 428

Step 429

Step 430

Step 431

Step 432

Step 433

Step 434

Step 435

Step 436

Step 437

Step 438

Step 439

Step 440

Step 441

Step 442

Step 443

Step 444

Step 445

Step 446

Step 447

Step 448

Step 449

Step 450

Step 451

Step 452

Step 453

Step 454

Step 455

Step 456

Step 457

Step 458

Step 459

Step 460

Step 461

Step 462

Step 463

Step 464

Step 465

Step 466

Step 467

Step 468

Step 469

Step 470

Step 471

Step 472

Step 473

Step 474

Step 475

Step 476

Step 477

Step 478

Step 479

Step 480

Step 481

Step 482

Step 483

Step 484

Step 485

Step 486

Step 487

Step 488

Step 489

Step 490

Step 491

Step 492

Step 493

Step 494

Step 495

Step 496

Step 497

Step 498

Step 499

Step 500

Step 501

Step 502

Step 503

Step 504

Step 505

Step 506

Step 507

Step 508

Step 509

Step 510

Step 511

Step 512

Step 513

Step 514

Step 515

Step 516

Step 517

Step 518

Step 519

Step 520

Step 521

Step 522

Step 523

Step 524

Step 525

Step 526

Step 527

Step 528

Step 529

Step 530

Step 531

Step 532

Step 533

Step 534

Step 535

Step 536

Step 537

Step 538

Step 539

Step 540

Step 541

Step 542

Step 543

Step 544

Step 545

Step 546

Step 547

Step 548

Step 549

Step 550

Step 551

Step 552

Step 553

Step 554

Step 555

Step 556

Step 557

Step 558

Step 559

Step 560

Step 561

Step 562

Step 563

Step 564

Step 565

Step 566

Step 567

Step 568

Step 569

Step 570

Step 571

Step 572

Step 573

Step 574

Step 575

Step 576

Step 577

Step 578

Step 579

Step 580

Step 581

Step 582

Step 583

Step 584

Step 585

Step 586

Step 587

Step 588

Step 589

Step 590

Step 591

Step 592

Step 593

Step 594

Step 595

Step 596

Step 597

Step 598

Step 599

Step 600

Step 601

Step 602

Step 603

Step 604

Step 605

Step 606

Step 607

Step 608

Step 609

Step 610

Step 611

Step 612

Step 613

Step 614

Step 615

Step 616

Step 617

Step 618

Step 619

Step 620

Step 621

Step 622

Step 623

Step 624

Step 625

Step 626

Step 627

Step 628

Step 629

Step 630

Step 631

Step 632

Step 633

Step 634

Step 635

Step 636

Step 637

Step 638

Step 639

Step 640

Step 641

Step 642

Step 643

Step 644

Step 645

Step 646

Step 647

Step 648

Step 649

Step 650

Step 651

Step 652

Step 653

Step 654

Step 655

Step 656

Step 657

Step 658

Step 659

Step 660

Step 661

Step 662

Step 663

Step 664

Step 665

Step 666

Step 667

Step 668

Step 669

Step 670

Step 671

Step 672

Step 673

Step 674

Step 675

Step 676

Step 677

Step 678

Step 679

Step 680

Step 681

Step 682

Step 683

Step 684

Step 685

Step 686

Step 687

Step 688

Step 689

Step 690

Step 691

Step 692

Step 693

Step 694

Step 695

Step 696

Step 697

Step 698

Step 699

Step 700

Step 701

Step 702

Step 703

Step 704

Step 705

Step 706

Step 707

Step 708

Step 709

Step 710

Step 711

Step 712

Step 713

Step 714

Step 715

Step 716

Step 717

Step 718

Step 719

Step 720

Step 721

Step 722

Step 723

Step 724

Step 725

Step 726

Step 727

Step 728

Step 729

Step 730

Step 731

Step 732

Step 733

Step 734

Step 735

Step 736

Step 737

Step 738

Step 739

Step 740

Step 741

Step 742

Step 743

Step 744

Step 745

Step 746

Step 747

Step 748

Step 749

Step 750

Step 751

Step 752

Step 753

Step 754

Step 755

Step 756

Step 757

Step 758

Step 759

Step 760

Step 761

Step 762

Step 763

Step 764

Step 765

Step 766

Step 767

Step 768

Step 769

Step 770

Step 771

Step 772

Step 773

Step 774

Step 775

Step 776

Step 777

Step 778

Step 779

Step 780

Step 781

Step 782

Step 783

Step 784

Step 785

Step 786

Step 787

Step 788

Step 789

Step 790

Step 791

Step 792

Step 793

Step 794

Step 795

Step 796

Step 797

Step 798

Step 799

Step 800

Step 801

Step 802

Step 803

Step 804

Step 805

Step 806

Step 807

Step 808

Step 809

Step 810

Step 811

Step 812

Step 813

Step 814

Step 815

Step 816

Step 817

Step 818

Step 819

Step 820

Step 821

Step 822

Step 823

Step 824

Step 825

Step 826

Step 827

Step 828

Step 829

Step 830

Step 831

Step 832

Step 833

Step 834

Step 835

Step 836

Step 837

Step 838

Step 839

Step 840

Step 841

Step 842

Step 843

Step 844

Step 845

Step 846

Step 847

Step 848

Step 849

Step 850

Step 851

Step 852

Step 853

Step 854

Step 855

Step 856

Step 857

Step 858

Step 859

Step 860

Step 861

Step 862

Step 863

Step 864

Step 865

Step 866

Step 867

Step 868

Step 869

Step 870

Step 871

Step 872

Step 873

Step 874

Step 875

Step 876

Step 877

Step 878

Step 879

Step 880

Step 881

Step 882

Step 883

Step 884

Step 885

Step 886

Step 887

Step 888

Step 889

Step 890

Step 891

Step 892

Step 893

Step 894

Step 895

Step 896

Step 897

Step 898

Step 899

Step 900

Step 901

Step 902

Step 903

Step 904

Step 905

Step 906

Step 907

Step 908

Step 909

Step 910

Step 911

Step 912

Step 913

Step 914

Step 915

Step 916

Step 917

Step 918

Step 919

Step 920

Step 921

Step 922

Step 923

Step 924

Step 925

Step 926

Step 927

Step 928

Step 929

Step 930

Step 931

Step 932

Step 933

Step 934

Step 935

Step 936

Step 937

Step 938

Step 939

Step 940

Step 941

Step 942

Step 943

Step 944

Step 945

Step 946

Step 947

Step 948

Step 949

Step 950

Step 951

Step 952

Step 953

Step 954

Step 955

Step 956

Step 957

Step 958

Step 959

Step 960

Step 961

Step 962

Step 963

Step 964

Step 965

Step 966

Step 967

Step 968

Step 969

Step 970

Step 971

Step 972

Step 973

Step 974

Step 975

Step 976

Step 977

Step 978

Step 979

Step 980

Step 981

Step 982

Step 983

Step 984

Step 985

Step 986

Step 987

Step 988

Step 989

Step 990

Step 991

Step 992

Step 993

Step 994

Step 995

Step 996

Step 997

Step 998

Step 999

Step 1000

Step 1001

Step 1002

Step 1003

Step 1004

Step 1005

Step 1006

Step 1007

Step 1008

Step 1009

Step 1010

Step 1011

Step 1012

Step 1013

Step 1014

Step 1015

Step 1016

Step 1017

Step 1018

Step 1019

Step 1020

Step 1021

Step 1022

Step 1023

Step 1024

Step 1025

Step 1026

Step 1027

Step 1028

Step 1029

Step 1030

Step 1031

Step 1032

Step 1033

Step 1034

Step 1035

Step 1036

Step 1037

Step 1038

Step 1039

Step 1040

Step 1041

Step 1042

Step 1043

Step 1044

Step 1045

Step 1046

Step 1047

Step 1048

Step 1049

Step 1050

Step 1051

Step 1052

Step 1053

Step 1054

Step 1055

Step 1056

Step 1057

Step 1058

Step 1059

Step 1060

Step 1061

Step 1062

Step 1063

Step 1064

Step 1065

Step 1066

Step 1067

Step 1068

Step 1069

Step 1070

Step 1071

Step 1072

Step 1073

Step 1074

Step 1075

Step 1076

Step 1077

Step 1078

Step 1079

Step 1080

Step 1081

Step 1082

Step 1083

Step 1084

Step 1085

Step 1086

Step 1087

Step 1088

Step 1089

Step 1090

Step 1091

Step 1092

Step 1093

Step 1094

Step 1095

Step 1096

Step 1097

Step 1098

Step 1099

Step 1100

Step 1101

Step 1102

Step 1103

Step 1104

Step 1105

Step 1106

Step 1107

Step 1108

Step 1109

Step 1110

Step 1111

Step 1112

Step 1113

Step 1114

Step 1115

Step 1116

Step 1117

Step 1118

Step 1119

Step 1120

Step 1121

Step 1122

Step 1123

Step 1124

Step 1125

Step 1126

Step 1127

Step 1128

Step 1129

Step 1130

Step 1131

Step 1132

Step 1133

Step 1134

Step 1135

Step 1136

Step 1137

Step 1138

Step 1139

Step 1140

Step 1141

Step 1142

Step 1143

Step 1144

Step 1145

Step 1146

Step 1147

Step 1148

Step 1149

Step 1150

Step 1151

Step 1152

Step 1153

Step 1154

Step 1155

Step 1156

Step 1157

Step 1158

Step 1159

Step 1160

Step 1161

Step 1162

Step 1163

Step 1164

Step 1165

Step 1166

Step 1167

Step 1168

Step 1169

Step 1170

Step 1171

Step 1172

Step 1173

Step 1174

Step 1175

Step 1176

Step 1177

Step 1178

Step 1179

Step 1180

Step 1181

Step 1182

Step 1183

Step 1184

Step 1185

Step 1186

Step 1187

Step 1188

Step 1189

Step 1190

Step 1191

Step 1192

Step 1193

Step 1194

Step 1195

Step 1196

Step 1197

Step 1198

Step 1199

Step 1200

Step 1201

Step 1202

Step 1203

Step 1204

Step 1205

Step 1206

Step 1207

Step 1208

Step 1209

Step 1210

Step 1211

Step 1212

Step 1213

Step 1214

Step 1215

Step 1216

Step 1217

Step 1218

Step 1219

Step 1220

Step 1221

Step 1222

Step 1223

Step 1224

Step 1225

Step 1226

Step 1227

Step 1228

Step 1229

Step 1230

Step 1231

Step 1232

Step 1233

Step 1234

Step 1235

Step 1236

Step 1237

Step 1238

Step 1239

Step 1240

Step 1241

Step 1242

Step 1243

Step 1244

Step 1245

Step 1246

Step 1247

Step 1248

Step 1249

Step 1250

Step 1251

Step 1252

Step 1253

Step 1254

Step 1255

Step 1256

Step 1257

Step 1258

Step 1259

Output files

Sources

https://mirrors.kernel.org/gnu/mes/mes-0.24.2.tar.gz

File: /sysa/distfiles/mes-0.24.2.tar.gz used in: Step 89, Step 372, Step 480

https://download.savannah.gnu.org/releases/nyacc/nyacc-1.00.2.tar.gz

File: /sysa/distfiles/nyacc-1.00.2.tar.gz used in: Step 88

https://lilypond.org/janneke/tcc/tcc-0.9.26-1136-g5bba73cc.tar.gz

File: /sysa/distfiles/tcc-0.9.26.tar.gz used in: Step 371

https://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27.tar.bz2

File: /sysa/distfiles/tcc-0.9.27.tar.bz2 used in: Step 479

https://mirrors.kernel.org/gnu/make/make-3.82.tar.bz2

File: /sysa/distfiles/make-3.82.tar.bz2 used in: Step 510

https://mirrors.kernel.org/gnu/patch/patch-2.5.9.tar.gz

File: /sysa/distfiles/patch-2.5.9.tar.gz used in: Step 551

https://mirrors.kernel.org/gnu/gzip/gzip-1.2.4.tar.gz

File: /sysa/distfiles/gzip-1.2.4.tar.gz used in: Step 586

https://mirrors.kernel.org/gnu/tar/tar-1.12.tar.gz

File: /sysa/distfiles/tar-1.12.tar.gz used in: Step 624

https://mirrors.kernel.org/gnu/sed/sed-4.0.9.tar.gz

File: /sysa/distfiles/sed-4.0.9.tar.gz used in: Step 672

https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz

File: /sysa/distfiles/bzip2-1.0.8.tar.gz used in: Step 702

https://mirrors.kernel.org/gnu/coreutils/coreutils-5.0.tar.bz2

File: /sysa/distfiles/coreutils-5.0.tar.bz2 used in: Step 730

http://downloads.sourceforge.net/project/heirloom/heirloom-devtools/070527/heirloom-devtools-070527.tar.bz2

File: /sysa/distfiles/heirloom-devtools-070527.tar.bz2 used in: Step 985

https://mirrors.kernel.org/gnu/bash/bash-2.05b.tar.gz

File: /sysa/distfiles/bash-2.05b.tar.gz used in: Step 1038

https://github.com/oriansj/bootstrap-seeds/blob/main/POSIX/x86/kaem-optional-seed

File: ../git/live-bootstrap/sysa/stage0-posix/src/bootstrap-seeds/POSIX/x86/kaem-optional-seed used in:
 7F 45 4C 46 01 01 01 03 00 00
 00 00 00 00 00 00 02 00 03 00
 01 00 00 00 54 80 04 08 34 00
 00 00 00 00 00 00 00 00 00 00
 34 00 20 00 01 00 00 00 00 00
 00 00 01 00 00 00 00 00 00 00
 00 80 04 08 00 80 04 08 80 02
 00 00 80 02 00 00 07 00 00 00
 01 00 00 00 58 5B 5B 85 DB 75
 06 50 BB 3A 82 04 08 31 C9 6A
 05 58 CD 80 85 C0 7E 6C A3 68
 82 04 08 58 89 E5 6A 2D 58 31
 DB CD 80 A3 78 82 04 08 A1 70
 82 04 08 E8 3F 01 00 00 89 C3
 89 D9 31 C0 A3 7C 82 04 08 A3
 6C 82 04 08 E8 95 00 00 00 85
 C0 74 05 89 01 83 C1 04 A1 6C
 82 04 08 85 C0 74 E9 39 CB 74
 CB E8 4F 00 00 00 8B 03 85 C0
 74 18 53 6A 02 58 CD 80 5B 85
 C0 7C 0D 75 12 6A 0B 58 89 EA
 89 D9 8B 1B CD 80 6A 01 5B 89
 D8 CD 80 89 C3 B9 7C 82 04 08
 31 D2 6A 07 58 CD 80 A1 7C 82
 04 08 85 C0 74 8A B8 48 82 04
 08 E8 07 01 00 00 EB D6 31 DB
 6A 01 58 CD 80 53 B8 43 82 04
 08 E8 F3 00 00 00 8B 03 E8 EC
 00 00 00 83 C3 04 6A 20 58 E8
 FE 00 00 00 39 CB 75 EA 6A 0A
 58 E8 F2 00 00 00 5B C3 53 51
 A1 74 82 04 08 E8 89 00 00 00
 89 C3 89 C1 E8 9F 00 00 00 3C
 FC 74 B5 3C 20 74 42 3C 09 74
 3E 3C 0A 75 0A 6A 01 58 A3 6C
 82 04 08 EB 30 3C 22 75 07 E8
 32 00 00 00 EB 25 3C 23 75 0F
 E8 40 00 00 00 6A 01 58 A3 6C
 82 04 08 EB 12 3C 5C 75 07 E8
 5E 00 00 00 EB 07 88 01 83 C1
 01 EB B1 39 CB 75 02 31 DB 89
 D8 59 5B C3 E8 45 00 00 00 3C
 FC 0F 84 2D FF FF FF 3C 22 74
 07 88 01 83 C1 01 EB E8 C3 E8
 2C 00 00 00 3C FC 0F 84 14 FF
 FF FF 3C 0A 75 EF C3 53 51 52
 8B 1D 78 82 04 08 01 C3 6A 2D
 58 CD 80 A1 78 82 04 08 89 1D
 78 82 04 08 5A 59 5B C3 53 51
 52 6A FC 58 50 8D 0C 24 8B 1D
 68 82 04 08 6A 03 58 6A 01 5A
 CD 80 58 3C FC 5A 59 5B C3 53
 51 89 C3 85 C0 74 12 31 C0 8A
 03 85 C0 74 0A E8 08 00 00 00
 83 C3 01 EB EE 59 5B C3 53 51
 52 50 8D 0C 24 6A 01 5B 6A 04
 58 89 DA CD 80 58 5A 59 5B C3
 6B 61 65 6D 2E 78 38 36 00 20
 2B 3E 20 00 53 75 62 70 72 6F
 63 65 73 73 20 65 72 72 6F 72
 0A 41 42 4F 52 54 49 4E 47 20
 48 41 52 44 0A 00 00 00 00 00
 00 00 00 00 00 04 00 00 00 10
 00 00 00 00 00 00 00 00 00 00

https://github.com/oriansj/bootstrap-seeds/blob/main/POSIX/x86/hex0-seed

File: ../git/live-bootstrap/sysa/stage0-posix/src/bootstrap-seeds/POSIX/x86/hex0-seed used in:
 7F 45 4C 46 01 01 01 03 00 00
 00 00 00 00 00 00 02 00 03 00
 01 00 00 00 54 80 04 08 34 00
 00 00 00 00 00 00 00 00 00 00
 34 00 20 00 01 00 00 00 00 00
 00 00 01 00 00 00 00 00 00 00
 00 80 04 08 00 80 04 08 00 01
 00 00 00 01 00 00 07 00 00 00
 01 00 00 00 58 5B 5B 31 C9 31
 D2 6A 05 58 CD 80 89 C6 5B 66
 B9 41 02 66 BA C0 01 6A 05 58
 CD 80 89 C2 6A FF 5D 31 FF E8
 68 00 00 00 E8 1B 00 00 00 85
 C0 7C F2 85 ED 7D 06 89 C7 31
 ED EB E8 C1 E7 04 01 F8 4D E8
 39 00 00 00 EB DB 3C 23 74 1E
 3C 3B 74 1A 3C 30 7C 1F 3C 3A
 7C 1F 3C 41 7C 17 3C 47 7C 1C
 3C 61 7C 0F 3C 67 7C 12 EB 09
 E8 21 00 00 00 3C 0A 75 F7 6A
 FF 58 C3 2C 30 C3 2C 20 2C 37
 C3 89 D3 52 6A 01 5A 50 89 E1
 6A 04 58 CD 80 5B 5A C3 52 6A
 01 5A 53 89 E1 89 F3 6A 03 58
 CD 80 85 C0 74 03 58 5A C3 31
 DB 6A 01 58 CD 80

https://github.com/oriansj/stage0-posix-x86/blob/main/hex0_x86.hex0

File: ../git/live-bootstrap/sysa/stage0-posix/src/x86/hex0_x86.hex0 used in: Step 2
## Copyright (C) 2019 Jeremiah Orians
## Copyright (C) 2022 Andrius Å tikonas
#
# SPDX-License-Identifier: GPL-3.0-or-later

## ELF Header
#:ELF_base
7F 45 4C 46                     # e_ident[EI_MAG0-3] ELF's magic number

01                              # e_ident[EI_CLASS] Indicating 32 bit
01                              # e_ident[EI_DATA] Indicating little endianness
01                              # e_ident[EI_VERSION] Indicating original elf

03                              # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                              # e_ident[EI_ABIVERSION] Set at 0 because no one cares

00 00 00 00 00 00 00            # e_ident[EI_PAD]

02 00                           # e_type Indicating Executable
03 00                           # e_machine Indicating x86
01 00 00 00                     # e_version Indicating original elf

54 80 04 08                     # e_entry Address of the entry point
34 00 00 00                     # e_phoff Address of program header table
00 00 00 00                     # e_shoff Address of section header table

00 00 00 00                     # e_flags

34 00                           # e_ehsize Indicating our 52 Byte header

20 00                           # e_phentsize size of a program header table
01 00                           # e_phnum number of entries in program table

00 00                           # e_shentsize size of a section header table
00 00                           # e_shnum number of entries in section table

00 00                           # e_shstrndx index of the section names

## Program Header
#:ELF_program_headers
#:ELF_program_header__text
01 00 00 00                     # ph_type: PT-LOAD = 1
00 00 00 00                     # ph_offset

00 80 04 08                     # ph_vaddr
00 80 04 08                     # ph_physaddr

00 01 00 00                     # ph_filesz
00 01 00 00                     # ph_memsz

07 00 00 00                     # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                     # ph_align

#:ELF_text

# Where the ELF Header is going to hit
# Simply jump to _start
# Our main function
# :_start ; (0x8048054)
    58                          ; pop_eax                     # Get the number of arguments
    5B                          ; pop_ebx                     # Get the program name
    5B                          ; pop_ebx                     # Get the actual input name
    31C9                        ; xor_ecx,ecx                 # prepare read_only, ecx = 0
    31D2                        ; xor_edx,edx                 # Extra sure, edx = 0
    6A 05                       ; push !5                     # prepare to set eax to 5
    58                          ; pop_eax                     # the syscall number for open()
    CD 80                       ; int !0x80                   # Now open that damn file
    89C6                        ; mov_esi,eax                 # Preserve the file pointer we were given

    5B                          ; pop_ebx                     # Get the actual output name
    66B9 4102                   ; mov_cx, @577                # Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    66BA C001                   ; mov_dx, @448                # Prepare file as RWX for owner only (700 in octal)
    6A 05                       ; push !5                     # prepare to set eax to 5
    58                          ; pop_eax                     # the syscall number for open()
    CD 80                       ; int !0x80                   # Now open that damn file
    89C2                        ; mov_edx,eax                 # Preserve the file pointer we were given

    # Our flag for byte processing
    6A FF                       ; push !-1
    5D                          ; pop_ebp                     # ebp = -1

    # temp storage for the sum
    31FF                        ; xor_edi,edi                 # edi = 0

#:loop ; (0x8048077)
    # Read a byte
    E8 68000000                 ; call %Read_byte

    # process byte
    E8 1B000000                 ; call %hex

    # Deal with -1 values
    85C0                        ; test_eax,eax
    7C F2                       ; jl !loop

    # deal with toggle
    85ED                        ; test_ebp,ebp                # jump if ebp >= 0
    7D 06                       ; jge !print

    # process first byte of pair
    89C7                        ; mov_edi,eax
    31ED                        ; xor_ebp,ebp                 # ebp = 0
    EB E8                       ; jmp !loop

# process second byte of pair
#:print ; (0x804808F)
    # update the sum and store in output
    C1E7 04                     ; shl_edi, !4
    01F8                        ; add_eax,edi

    # flip the toggle
    4D                          ; DEC_EBP                     # ebp = -1

    E8 39000000                 ; call %write_byte

    EB DB                       ; jmp !loop

#:hex ; (0x804809C)
    # Purge Comment Lines (#)
    3C 23                       ; cmp_al, !35
    74 1E                       ; je !purge_comment

    # Purge Comment Lines (;)
    3C 3B                       ; cmp_al, !59
    74 1A                       ; je !purge_comment

    # deal all ascii less than 0
    3C 30                       ; cmp_al, !48
    7C 1F                       ; jl !ascii_other

    # deal with 0-9
    3C 3A                       ; cmp_al, !58
    7C 1F                       ; jl !ascii_num

    # deal with all ascii less than A
    3C 41                       ; cmp_al, !65
    7C 17                       ; jl !ascii_other

    # deal with A-F
    3C 47                       ; cmp_al, !71
    7C 1C                       ; jl !ascii_high

    # deal with all ascii less than a
    3C 61                       ; cmp_al, !97
    7C 0F                       ; jl !ascii_other

    # deal with a-f
    3C 67                       ; cmp_al, !103
    7C 12                       ; jl !ascii_low

    # The rest that remains needs to be ignored
    EB 09                       ; jmp !ascii_other

#:purge_comment ; (0x80480BE)
    # Read a byte
    E8 21000000                 ; call %Read_byte

    # Loop if not LF
    3C 0A                       ; cmp_al, !10
    75 F7                       ; jne !purge_comment

    # Otherwise return -1

#:ascii_other ; (0x80480C7)
    6A FF                       ; push !-1
    58                          ; pop_eax                     # return -1
    C3                          ; ret

#:ascii_num ; (0x80480CB)
    2C 30                       ; sub_al, !48
    C3                          ; ret

#:ascii_low ; (0x80480CE)
    2C 20                       ; sub_al, !32                 # convert to uppercase

#:ascii_high ; (0x80480D0)
    2C 37                       ; sub_al, !55
    C3                          ; ret

# Writes byte stored in al
#:write_byte ; (0x80480D3)
    # Print our Hex
    89D3                        ; mov_ebx,edx                 # Where are we writing to
    52                          ; push_edx                    # protect fout
    6A 01                       ; push !1                     # prepare to set edx to 1
    5A                          ; pop_edx                     # set the size of chars we want
    50                          ; push_eax                    # Move output to stack
    89E1                        ; mov_ecx,esp                 # What we are writing
    6A 04                       ; push !4                     # prepare to set eax to 4
    58                          ; pop_eax                     # the syscall number for write
    CD 80                       ; int !0x80                   # call the Kernel
    5B                          ; pop_ebx                     # deallocate stack
    5A                          ; pop_edx                     # restore fout
    C3                          ; ret

#:Read_byte ; (0x80480E4)
    # Attempt to read 1 byte from Input file
    52                          ; push_edx                    # protect fout
    6A 01                       ; push !1                     # prepare to set edx to 1
    5A                          ; pop_edx                     # set the size of chars we want
    53                          ; push_ebx                    # allocate stack
    89E1                        ; mov_ecx,esp                 # Where to put it
    89F3                        ; mov_ebx,esi                 # Where are we reading from
    6A 03                       ; push !3                     # prepare to set eax to 3
    58                          ; pop_eax                     # the syscall number for read
    CD 80                       ; int !0x80                   # call the Kernel

    85C0                        ; test_eax,eax                # check what we got
    74 03                       ; je !Done                    # Got EOF call it done

    # load byte
    58                          ; pop_eax                     # load char
    5A                          ; pop_edx                     # restore fout
    C3                          ; ret

#:Done ; (0x80480F9)
    # program completed Successfully
    31DB                        ; xor_ebx,ebx                 # All is well, ebx = 0
    6A 01                       ; push !1
    58                          ; pop_eax                     # put the exit syscall number in eax
    CD 80                       ; int !0x80                   # Call it a good day

#:ELF_end

https://github.com/oriansj/stage0-posix-x86/blob/main/kaem-minimal.hex0

File: ../git/live-bootstrap/sysa/stage0-posix/src/x86/kaem-minimal.hex0 used in: Step 3
# SPDX-FileCopyrightText: 2020 Jeremiah Orians
# SPDX-FileCopyrightText: 2022 Andrius Å tikonas
#
# SPDX-License-Identifier: GPL-3.0-or-later

## ELF Header
# :ELF_base ; (0x8048000)
    7F 45 4C 46                     # e_ident[EI_MAG0-3] ELF's magic number

    01                              # e_ident[EI_CLASS] Indicating 32 bit
    01                              # e_ident[EI_DATA] Indicating little endianness
    01                              # e_ident[EI_VERSION] Indicating original elf

    03                              # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
    00                              # e_ident[EI_ABIVERSION] Set at 0 because none cares

    00 00 00 00 00 00 00            # e_ident[EI_PAD]

    02 00                           # e_type Indicating Executable
    03 00                           # e_machine Indicating x86
    01 00 00 00                     # e_version Indicating original elf

    54 80 04 08                     # e_entry Address of the entry point
    34 00 00 00                     # e_phoff Address of program header table
    00 00 00 00                     # e_shoff Address of section header table

    00 00 00 00                     # e_flags

    34 00                           # e_ehsize Indicating our 52 Byte header

    20 00                           # e_phentsize size of a program header table
    01 00                           # e_phnum number of entries in program table

    00 00                           # e_shentsize size of a section header table
    00 00                           # e_shnum number of entries in section table

    00 00                           # e_shstrndx index of the section names

## Program Header
# :ELF_program_headers
# :ELF_program_header__text
    01 00 00 00                     # ph_type: PT-LOAD = 1
    00 00 00 00                     # ph_offset

    00 80 04 08                     # ph_vaddr
    00 80 04 08                     # ph_physaddr

    80 02 00 00                     # ph_filesz
    80 02 00 00                     # ph_memsz

    07 00 00 00                     # ph_flags: PF-X|PF-W|PF-R = 7
    01 00 00 00                     # ph_align

# :ELF_text

# :_start ; (0x8048054)
    58                              ; pop_eax                           # Get the number of arguments
    5B                              ; pop_ebx                           # Get the program name
    5B                              ; pop_ebx                           # Get the actual input name
    85DB                            ; test_ebx,ebx                      # Check for missing output
    75 06                           ; jne !_start_out                   # Have real input
    50                              ; push_eax                          # Need to adjust stack
    BB 3A820408                     ; mov_ebx, &default_file            # Use "kaem.x86"

# :_start_out ; (0x8048061)
    31C9                            ; xor_ecx,ecx                       # prepare read_only
    6A 05                           ; push !5
    58                              ; pop_eax                           # the syscall number for open()
    CD80                            ; int !0x80                         # Now open that damn file

    85C0                            ; test_eax,eax                      # IF NULL We couldn't open the file
    7E 6C                           ; jle !Exit_Failure                 # Abort hard
    A3 68820408                     ; mov_[DWORD],eax &script           # Set input pointer

    58                              ; pop_eax                           # Get start of envp
    89E5                            ; mov_ebp,esp                       # Protect envp

    6A 2D                           ; push !45
    58                              ; pop_eax                           # the Syscall # for SYS_BRK
    31DB                            ; xor_ebx,ebx                       # Get current brk
    CD80                            ; int !0x80                         # Let the kernel do the work
    A3 78820408                     ; mov_[DWORD],eax &MALLOC           # Set our malloc pointer

    # Where the main work gets done
    # Using EBX for tokens and ECX for tokens[i]
# :main_loop ; (0x8048080)
    A1 70820408                     ; mov_eax,[DWORD] &max_args         # Using 256 char* of space
    E8 3F010000                     ; call %malloc                      # get it
    89C3                            ; mov_ebx,eax                       # set tokens

    89D9                            ; mov_ecx,ebx                       # I = 0
    31C0                            ; xor_eax,eax                       # Using 0
    A3 7C820408                     ; mov_[DWORD],eax &status           # status = 0
    A3 6C820408                     ; mov_[DWORD],eax &command_done     # command_done = 0

    # Using EAX for result and EBX for tokens[i]
# :collect_command ; (0x804809A)
    E8 95000000                     ; call %collect_token               # Get another token
    85C0                            ; test_eax,eax                      # if NULL == result
    74 05                           ; je !collect_command_comment       # It is a comment, don't store

    8901                            ; mov_[ecx],eax                     # tokens[i] = result
    83C1 04                         ; add_ecx, !4                       # i = i + 1 (adjusted for char* size)

# :collect_command_comment ; (0x80480A8)
    A1 6C820408                     ; mov_eax,[DWORD] &command_done     # Using command_done
    85C0                            ; test_eax,eax                      # IF 0 == command_done
    74 E9                           ; je !collect_command               # keep looping

    # Deal with line comments
    39CB                            ; cmp_ebx,ecx                       # if 0 < i
    74 CB                           ; je !main_loop                     # It was a comment

    E8 4F000000                     ; call %print_command               # print the command
    8B03                            ; mov_eax,[ebx]                     # program = tokens[0]
    85C0                            ; test_eax,eax                      # IF NULL == program
    74 18                           ; je !Exit_Failure                  # Some shit went down, abort

    53                              ; push_ebx                          # Protect Tokens
    6A 02                           ; push !2
    58                              ; pop_eax                           # FORKing
    CD 80                           ; int !0x80                         # int f = FORK()
    5B                              ; pop_ebx                           # Restore Tokens

    85C0                            ; test_eax,eax                      # Check fork
    7C 0D                           ; jl !Exit_Failure                  # IF f == -1 abort hard
    75 12                           ; jne !collect_command_parent       # IF f == 0 it is child

    # Deal with child case
    6A 0B                           ; push !11
    58                              ; pop_eax                           # EXECVE
    89EA                            ; mov_edx,ebp                       # third arg = envp
    89D9                            ; mov_ecx,ebx                       # second arg = tokens
    8B1B                            ; mov_ebx,[ebx]                     # program = tokens[0]
    CD 80                           ; int !0x80                         # execve(program, tokens, envp)
                                                                        # return error

# Exit_Failure function
# Receives nothing
# And aborts hard
# Does NOT return
# :Exit_Failure ; (0x80480D8)
    6A 01                           ; push !1
    5B                              ; pop_ebx                           # All is wrong
    89D8                            ; mov_eax,ebx                       # put the exit syscall number in eax
    CD 80                           ; int !0x80                         # Call it a bad day

# :collect_command_parent ; (0x80480DF)
    89C3                            ; mov_ebx,eax                       # first arg = f
    B9 7C820408                     ; mov_ecx, &status                  # second arg = &status
    31D2                            ; xor_edx,edx                       # third arg = NULL
    6A 07                           ; push !7
    58                              ; pop_eax                           # WAITPID
    CD 80                           ; int !0x80                         # waitpid(f, &status, 0)

    A1 7C820408                     ; mov_eax,[DWORD] &status           # Using status
    85C0                            ; test_eax,eax                      # IF 0 == status
    74 8A                           ; je !main_loop                     # Loop forever

    # Deal with very unhappy case
    B8 48820408                     ; mov_eax, &hard                    # Using "Subprocess error\nABORTING HARD\n"
    E8 07010000                     ; call %File_Print                  # Print it
    EB D6                           ; jmp !Exit_Failure                 # return error

# :Done ; (0x8048102)
    # program completed Successfully
    31DB                            ; xor_ebx,ebx                       # All is well
    6A 01                           ; push !1
    58                              ; pop_eax                           # put the exit syscall number in eax
    CD 80                           ; int !0x80                         # Call it a good day

# print_command function
# Receives tokens[j] in EBX and tokens[i] in ECX
# Modifies EAX
# :print_command ; (0x8048109)
    53                              ; push_ebx                          # Protect EBX
    B8 43820408                     ; mov_eax, &prefix                  # using " +> "
    E8 F3000000                     ; call %File_Print                  # print it
# :print_command_loop ; (0x8048114)
    8B03                            ; mov_eax,[ebx]                     # using tokens[j]
    E8 EC000000                     ; call %File_Print                  # print it
    83C3 04                         ; add_ebx, !4                       # j = j + 1
    6A 20                           ; push !32
    58                              ; pop_eax                           # using ' '
    E8 FE000000                     ; call %fputc                       # print it
    39CB                            ; cmp_ebx,ecx                       # IF j < i
    75 EA                           ; jne !print_command_loop           # otherwise keep looping

    6A 0A                           ; push !10
    58                              ; pop_eax                           # using '\n'
    E8 F2000000                     ; call %fputc                       # print it
    5B                              ; pop_ebx                           # Restore EBX
    C3                              ; ret


# collect_token function
# Receives nothing
# Overwrites EAX
# Uses EAX as C, EBX as token and ECX as token[i]
# :collect_token ; (0x8048134)
    53                              ; push_ebx                          # Protect EBX
    51                              ; push_ecx                          # Protect ECX
    A1 74820408                     ; mov_eax,[DWORD] &max_string       # Using max_string
    E8 89000000                     ; call %malloc                      # allocate space
    89C3                            ; mov_ebx,eax                       # token = malloc(max_string)
    89C1                            ; mov_ecx,eax                       # i = 0; set token[i]

# :collect_token_loop ; (0x8048144)
    E8 9F000000                     ; call %fgetc                       # c = fgetc(input)
    3C FC                           ; cmp_al, !-4                       # if C == EOF
    74 B5                           ; je !Done                          # We are done

    3C 20                           ; cmp_al, !32                       # IF C == ' '
    74 42                           ; je !collect_token_done            # Space terminates token

    3C 09                           ; cmp_al, !9                        # IF C == '\t'
    74 3E                           ; je !collect_token_done            # tab terminates token

    3C 0A                           ; cmp_al, !10                       # IF C == '\n'
    75 0A                           ; jne !collect_token_string         # otherwise check next

    # It is a newline
    6A 01                           ; push !1
    58                              ; pop_eax                           # Using 1
    A3 6C820408                     ; mov_[DWORD],eax &command_done     # Set command_done = TRUE
    EB 30                           ; jmp !collect_token_done           # Be done

# :collect_token_string ; (0x8048163)
    3C 22                           ; cmp_al, !34                       # IF C == '\"'
    75 07                           ; jne !collect_token_comment        # otherwise check next

    # It is a RAW STRING
    E8 32000000                     ; call %collect_string              # Get the rest of the string
    EB 25                           ; jmp !collect_token_done           # Be done

# :collect_token_comment ; (0x804816E)
    3C 23                           ; cmp_al, !35                       # IF C == '#'
    75 0F                           ; jne !collect_token_escape         # otherwise check next

    # It is a line comment
    E8 40000000                     ; call %collect_comment             # Read it all
    6A 01                           ; push !1
    58                              ; pop_eax                           # Using 1
    A3 6C820408                     ; mov_[DWORD],eax &command_done     # Set command_done = TRUE
    EB 12                           ; jmp !collect_token_done           # Be done

# :collect_token_escape ; (0x8048181)
    3C 5C                           ; cmp_al, !92                       # IF C == '\\'
    75 07                           ; jne !collect_token_other          # otherwise just store it

    # It is an escape char
    E8 5E000000                     ; call %fgetc                       # Read the char to drop
    EB 07                           ; jmp !collect_token_done           # Be done

# :collect_token_other ; (0x804818C)
    8801                            ; mov_[ecx],al                      # token[i] = C
    83C1 01                         ; add_ecx, !1                       # i = i + 1
    EB B1                           ; jmp !collect_token_loop           # Keep going

# :collect_token_done ; (0x8048193)
    39CB                            ; cmp_ebx,ecx                       # IF i == 0
    75 02                           ; jne !collect_token_good           # otherwise return the token
    31DB                            ; xor_ebx,ebx                       # token = NULL

# :collect_token_good ; (0x8048199)
    89D8                            ; mov_eax,ebx                       # Return token
    59                              ; pop_ecx                           # Restore ECX
    5B                              ; pop_ebx                           # Restore EBX
    C3                              ; ret


# collect_string function
# Receives target[index] in ECX
# Modifies EAX
# Uses EAX as C
# :collect_string ; (0x804819E)
    E8 45000000                     ; call %fgetc                       # C = fgetc(input)
    3C FC                           ; cmp_al, !-4                       # if C == EOF
    0F84 2DFFFFFF                   ; je32 %Exit_Failure                # Something went horribly wrong

    3C 22                           ; cmp_al, !34                       # IF C == '\"'
    74 07                           ; je !collect_string_done           # be done

    # deal with inside of string
    8801                            ; mov_[ecx],al                      # target[index] = C
    83C1 01                         ; add_ecx, !1                       # index = index + 1
    EB E8                           ; jmp !collect_string               # Keep going

# :collect_string_done ; (0x80481B6)
    C3                              ; ret


# collect_comment function
# Receives nothing
# Modifies EAX
# uses EAX as Int C
# Just throws away everything it reads
# :collect_comment ; (0x80481B7)
    E8 2C000000                     ; call %fgetc                       # C = fgetc(input)
    3C FC                           ; cmp_al, !-4                       # IF C == EOF
    0F84 14FFFFFF                   ; je32 %Exit_Failure                # abort hard

    3C 0A                           ; cmp_al, !10                       # IF C == '\n'
    75 EF                           ; jne !collect_comment              # otherwise keep looping
    C3                              ; ret


;; Malloc isn't actually required if the program being built fits in the initial memory
;; However, it doesn't take much to add it.
;; Requires [MALLOC] to be initialized and EAX to have the number of desired bytes
# :malloc ; (0x80481C9)
    53                              ; push_ebx                          # Protect EBX
    51                              ; push_ecx                          # Protect ECX
    52                              ; push_edx                          # Protect EDX
    8B1D 78820408                   ; mov_ebx,[DWORD] &MALLOC           # Using the current pointer
    01C3                            ; add_ebx,eax                       # Request the number of desired bytes
    6A 2D                           ; push !45
    58                              ; pop_eax                           # the Syscall # for SYS_BRK
    CD 80                           ; int !0x80                         # call the Kernel
    A1 78820408                     ; mov_eax,[DWORD] &MALLOC           # Return pointer
    891D 78820408                   ; mov_[DWORD],ebx &MALLOC           # Update pointer
    5A                              ; pop_edx                           # Restore EDX
    59                              ; pop_ecx                           # Restore ECX
    5B                              ; pop_ebx                           # Restore EBX
    C3                              ; ret


# fgetc function
# Loads FILE* from [script]
# Returns -4 (EOF) or char in AL
# :fgetc ; (0x80481E8)
    53                              ; push_ebx                          # Protect EBX
    51                              ; push_ecx                          # Protect ECX
    52                              ; push_edx                          # Protect EDX
    6A FC                           ; push !-4
    58                              ; pop_eax                           # Put EOF in eax
    50                              ; push_eax                          # Assume bad (If nothing read, value will remain EOF)
    8D0C24                          ; lea_ecx,[esp]                     # Get stack address
    8B1D 68820408                   ; mov_ebx,[DWORD] &script           # Where are we reading from
    6A 03                           ; push !3
    58                              ; pop_eax                           # the syscall number for read
    6A 01                           ; push !1
    5A                              ; pop_edx                           # set the size of chars we want
    CD 80                           ; int !0x80                         # call the Kernel
    58                              ; pop_eax                           # Get either char or EOF
    3C FC                           ; cmp_al, !-4                       # Check for EOF
# :fgetc_done ; (0x8048203)
    5A                              ; pop_edx                           # Restore EDX
    59                              ; pop_ecx                           # Restore ECX
    5B                              ; pop_ebx                           # Restore EBX
    C3                              ; ret


# File_Print function
# Receives CHAR* in EAX
# calls fputc for every non-null char
# :File_Print ; (0x8048207)
    53                              ; push_ebx                          # Protect EBX
    51                              ; push_ecx                          # Protect ECX
    89C3                            ; mov_ebx,eax                       # Protect S
    85C0                            ; test_eax,eax                      # Protect against nulls
    74 12                           ; je !File_Print_Done               # Simply don't try to print them
# :File_Print_Loop ; (0x804820F)
    31C0                            ; xor_eax,eax                       # Zero eax
    8A03                            ; mov_al,[ebx]                      # Read byte
    85C0                            ; test_eax,eax                      # Check for NULL
    74 0A                           ; je !File_Print_Done               # Stop at NULL

    E8 08000000                     ; call %fputc                       # write it
    83C3 01                         ; add_ebx, !1                       # S = S + 1
    EB EE                           ; jmp !File_Print_Loop              # Keep going

# :File_Print_Done ; (0x8048221)
    59                              ; pop_ecx                           # Restore ECX
    5B                              ; pop_ebx                           # Restore EBX
    C3                              ; ret


# fputc function
# receives CHAR in EAX and load FILE* from stdout
# writes char and returns
# :fputc ; (0x8048224)
    53                              ; push_ebx                          # Protect EBX
    51                              ; push_ecx                          # Protect ECX
    52                              ; push_edx                          # Protect EDX
    50                              ; push_eax                          # We are writing eax
    8D0C24                          ; lea_ecx,[esp]                     # Get stack address
    6A 01                           ; push !1
    5B                              ; pop_ebx                           # Write to target file
    6A 04                           ; push !4
    58                              ; pop_eax                           # the syscall number for write
    89DA                            ; mov_edx,ebx                       # set the size of chars we want
    CD 80                           ; int !0x80                         # call the Kernel
    58                              ; pop_eax                           # Restore stack
    5A                              ; pop_edx                           # Restore EDX
    59                              ; pop_ecx                           # Restore ECX
    5B                              ; pop_ebx                           # Restore EBX
    C3                              ; ret


# :default_file ; (0x804823A)
    6B61656D2E78383600              # "kaem.x86"
# :prefix ; (0x8048243)
    202B3E2000                      # " +> "
# :hard ; (0x8048248)
    53756270726F63657373206572726F720A41424F5254494E4720484152440A00 # "Subprocess error\nABORTING HARD\n"
# :script ; (0x8048268)
    00000000
# :command_done ; (0x804826C)
    00000000
# :max_args ; (0x8048270)
    00040000
# :max_string ; (0x8048274)
    00100000
# :MALLOC ; (0x8048278)
    00000000
# :status ; (0x804827C)
    00000000
# :ELF_end ; (0x8048280)

https://github.com/oriansj/stage0-posix-x86/blob/main/hex1_x86.hex0

File: ../git/live-bootstrap/sysa/stage0-posix/src/x86/hex1_x86.hex0 used in: Step 5
### Copyright (C) 2016 Jeremiah Orians
### This file is part of stage0.
###
### stage0 is free software: you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation, either version 3 of the License, or
### (at your option) any later version.
###
### stage0 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 stage0.  If not, see <http://www.gnu.org/licenses/>.

## ELF Header
#:ELF_base
7F 45 4C 46                     # e_ident[EI_MAG0-3] ELF's magic number

01                              # e_ident[EI_CLASS] Indicating 32 bit
01                              # e_ident[EI_DATA] Indicating little endianness
01                              # e_ident[EI_VERSION] Indicating original elf

03                              # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                              # e_ident[EI_ABIVERSION] Set at 0 because none cares

00 00 00 00 00 00 00            # e_ident[EI_PAD]

02 00                           # e_type Indicating Executable
03 00                           # e_machine Indicating x86
01 00 00 00                     # e_version Indicating original elf

54 80 04 08                     # e_entry Address of the entry point
34 00 00 00                     # e_phoff Address of program header table
00 00 00 00                     # e_shoff Address of section header table

00 00 00 00                     # e_flags

34 00                           # e_ehsize Indicating our 52 Byte header

20 00                           # e_phentsize size of a program header table
01 00                           # e_phnum number of entries in program table

00 00                           # e_shentsize size of a section header table
00 00                           # e_shnum number of entries in section table

00 00                           # e_shstrndx index of the section names

## Program Header
#:ELF_program_headers
#:ELF_program_header__text
01 00 00 00                     # ph_type: PT-LOAD = 1
00 00 00 00                     # ph_offset

00 80 04 08                     # ph_vaddr
00 80 04 08                     # ph_physaddr

B1 02 00 00                     # ph_filesz
B1 02 00 00                     # ph_memsz

07 00 00 00                     # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                     # ph_align

#:ELF_text

#:_start ; (0x8048054)
    58                          # POP_EAX                     ; Get the number of arguments
    5B                          # POP_EBX                     ; Get the program name
    5B                          # POP_EBX                     ; Get the actual input name
    B9 00000000                 # LOADI32_ECX %0              ; prepare read_only
    BA 00000000                 # LOADI32_EDX %0              ; extra sure
    B8 05000000                 # LOADI32_EAX %5              ; the syscall number for open()
    CD80                        # INT_80                      ; Now open that damn file
    A3 A9820408                 # STORE32_Absolute32_eax &fin ; Preserve the file pointer we were given

    5B                          # POP_EBX                     ; Get the actual output name
    B9 41020000                 # LOADI32_ECX %577            ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    BA C0010000                 # LOADI32_EDX %448            ; Prepare file as RWX for owner only (700 in octal)
    B8 05000000                 # LOADI32_EAX %5              ; the syscall number for open()
    CD80                        # INT_80                      ; Now open that damn file
    A3 AD820408                 # STORE32_Absolute32_eax &fout ; Preserve the file pointer we were given

    BD FFFFFFFF                 # LOADI32_EBP %-1             ; Our flag for byte processing
    BE 00000000                 # LOADI32_ESI %0              ; temp storage for the sum
    BF 00000000                 # LOADI32_EDI %0              ; Our starting IP
    E8 30000000                 # CALLI32 %First_pass         ; Process it

    ; rewind input file
    8B1D A9820408               # LOAD32_Absolute32_ebx &fin  ; Using our input file
    B9 00000000                 # LOADI32_ECX %0              ; Offset Zero
    BA 00000000                 # LOADI32_EDX %0              ; Whence Zero
    B8 13000000                 # LOADI32_EAX %19             ; lseek
    CD80                        # INT_80

    BD FFFFFFFF                 # LOADI32_EBP %-1             ; Our flag for byte processing
    BE 00000000                 # LOADI32_ESI %0              ; temp storage for the sum
    BF 00000000                 # LOADI32_EDI %0              ; Our starting IP
    E8 B8000000                 # CALLI32 %Second_pass        ; Process it

    E9 62010000                 # JMP32 %Done

#:First_pass ; (0x80480C8)
    E8 69010000                 # CALLI32 %Read_byte

    ; Deal with EOF
    83F8 FC                     # CMPI8_EAX !-4
    0F84 4E000000               # JE32 %First_pass_done

    ; Check for :
    83F8 3A                     # CMPI8_EAX !58
    0F85 05000000               # JNE32 %First_pass_0

    ; Deal with label
    E8 A1010000                 # CALLI32 %StoreLabel

#:First_pass_0 ; (0x80480E4)
    ; Check for %
    83F8 25                     # CMPI8_EAX !37
    0F84 2A000000               # JE32 %First_pass_pointer

    ; Deal with everything else
    E8 33000000                 # CALLI32 %hex                ; Process our char

    ; Deal with EOF
    83F8 FC                     # CMPI8_EAX !-4
    0F84 29000000               # JE32 %First_pass_done

    ; deal with -1 values
    83F8 00                     # CMPI8_EAX !0
    0F8C C4FFFFFF               # JL32 %First_pass

    ; deal with toggle
    83FD 00                     # CMPI8_EBP !0
    0F84 03000000               # JE32 %First_pass_1
    83C7 01                     # ADDI8_EDI !1                ; Increment IP

#:First_pass_1 ; (0x8048110)
    F7D5                        # NOT_EBP
    E9 B1FFFFFF                 # JMP32 %First_pass

#:First_pass_pointer ; (0x8048117)
    ; Deal with Pointer to label
    E8 1A010000                 # CALLI32 %Read_byte          ; Drop the char
    83C7 04                     # ADDI8_EDI !4                ; Increment IP
    E9 A4FFFFFF                 # JMP32 %First_pass           ; Loop again

#:First_pass_done ; (0x8048124)
    C3                          # RET

#:hex ; (0x8048125)
    ; deal with EOF
    83F8 FC                     # CMPI8_EAX !-4
    0F84 AE000000               # JE32 %EOF
    ; deal with line comments starting with #
    83F8 23                     # CMPI8_EAX !35
    0F84 B8000000               # JE32 %ascii_comment
    ; deal with line comments starting with ;
    83F8 3B                     # CMPI8_EAX !59
    0F84 AF000000               # JE32 %ascii_comment
    ; deal all ascii less than 0
    83F8 30                     # CMPI8_EAX !48
    0F8C A0000000               # JL32 %ascii_other
    ; deal with 0-9
    83F8 3A                     # CMPI8_EAX !58
    0F8C 8B000000               # JL32 %ascii_num
    ; deal with all ascii less than A
    83F8 41                     # CMPI8_EAX !65
    0F8C 8E000000               # JL32 %ascii_other
    ; deal with A-F
    83F8 47                     # CMPI8_EAX !71
    0F8C 81000000               # JL32 %ascii_high
    ;deal with all ascii less than a
    83F8 61                     # CMPI8_EAX !97
    0F8C 7C000000               # JL32 %ascii_other
    ;deal with a-f
    83F8 67                     # CMPI8_EAX !103
    0F8C 6B000000               # JL32 %ascii_low
    ; The rest that remains needs to be ignored
    E9 6E000000                 # JMP32 %ascii_other

#:Second_pass ; (0x804817B)
    E8 B6000000                 # CALLI32 %Read_byte

    ; Deal with EOF
    83F8 FC                     # CMPI8_EAX !-4
    0F84 52000000               # JE32 %Second_pass_done

    ; Simply drop the label
    83F8 3A                     # CMPI8_EAX !58
    0F85 0A000000               # JNE32 %Second_pass_0

    E8 9F000000                 # CALLI32 %Read_byte
    E9 DFFFFFFF                 # JMP32 %Second_pass

#:Second_pass_0 ; (0x804819C)
    ; Deal with  pointer
    83F8 25                     # CMPI8_EAX !37
    0F85 0A000000               # JNE32 %Second_pass_1

    E8 E3000000                 # CALLI32 %StorePointer
    E9 CCFFFFFF                 # JMP32 %Second_pass

#:Second_pass_1 ; (0x80481AF)
    ; Deal with everything else
    E8 71FFFFFF                 # CALLI32 %hex                ; Process our char

    ; Deal with EOF
    83F8 FC                     # CMPI8_EAX !-4
    0F84 1E000000               # JE32 %Second_pass_done

    ; deal with -1 values
    83F8 00                     # CMPI8_EAX !0
    0F8C B5FFFFFF               # JL32 %Second_pass

    ; deal with toggle
    83FD 00                     # CMPI8_EBP !0
    0F84 3D000000               # JE32 %print

    ; process first byte of pair
    89C6                        # COPY_EAX_to_ESI
    BD 00000000                 # LOADI32_EBP %0
    E9 A0FFFFFF                 # JMP32 %Second_pass

#:Second_pass_done ; (0x80481DB)
    C3                          # RET

#:EOF ; (0x80481DC)
    C3                          # RET
#:ascii_num ; (0x80481DD)
    83E8 30                     # SUBI8_EAX !48
    C3                          # RET
#:ascii_low ; (0x80481E1)
    83E8 57                     # SUBI8_EAX !87
    C3                          # RET
#:ascii_high ; (0x80481E5)
    83E8 37                     # SUBI8_EAX !55
    C3                          # RET
#:ascii_other ; (0x80481E9)
    B8 FFFFFFFF                 # LOADI32_EAX %-1
    C3                          # RET
#:ascii_comment ; (0x80481EF)
    E8 42000000                 # CALLI32 %Read_byte
    83F8 0D                     # CMPI8_EAX !13
    0F84 09000000               # JE32 %ascii_comment_cr
    83F8 0A                     # CMPI8_EAX !10
    0F85 E9FFFFFF               # JNE32 %ascii_comment
#:ascii_comment_cr ; (0x8048206)
    B8 FFFFFFFF                 # LOADI32_EAX %-1
    C3                          # RET

; process second byte of pair
#:print ; (0x804820C)
    ; update the sum and store in output
    C1E6 04                     # SHLI8_ESI !4
    01F0                        # ADD_ESI_to_EAX
    A2 B1820408                 # STORE8_Absolute32_al &table

    ; flip the toggle
    F7D5                        # NOT_EBP

    ; Print our first Hex
    BA 01000000                 # LOADI32_EDX %1              ; set the size of chars we want
    E8 42000000                 # CALLI32 %print_chars

    83C7 01                     # ADDI8_EDI !1                ; Increment IP
    E9 51FFFFFF                 # JMP32 %Second_pass

#:Done ; (0x804822A)
    ; program completed Successfully
    BB 00000000                 # LOADI32_EBX %0              ; All is well
    B8 01000000                 # LOADI32_EAX %1              ; put the exit syscall number in eax
    CD80                        # INT_80                      ; Call it a good day

#:Read_byte ; (0x8048236)
    ; Attempt to read 1 byte from STDIN
    BA 01000000                 # LOADI32_EDX %1              ; set the size of chars we want
    B9 B1820408                 # LOADI32_ECX &table          ; Where to put it
    8B1D A9820408               # LOAD32_Absolute32_ebx &fin  ; Where are we reading from
    B8 03000000                 # LOADI32_EAX %3              ; the syscall number for read
    CD80                        # INT_80                      ; call the Kernel

    85C0                        # TEST                        ; check what we got
    0F84 09000000               # JE32 %Read_byte_1           ; Got EOF call it done

    ; load byte
    A0 B1820408                 # LOAD8_Absolute32_al &table  ; load char
    0FB6C0                      # MOVZX                       ; We have to zero extend it to use it
    C3                          # RET

; Deal with EOF
#:Read_byte_1 ; (0x804825E)
    B8 FCFFFFFF                 # LOADI32_EAX %-4             ; Put EOF in eax
    C3                          # RET

#:print_chars ; (0x8048264)
    B9 B1820408                 # LOADI32_ECX &table          ; What we are writing
    8B1D AD820408               # LOAD32_Absolute32_ebx &fout ; Write to target file
    B8 04000000                 # LOADI32_EAX %4              ; the syscall number for write
    CD80                        # INT_80                      ; call the Kernel
    C3                          # RET

#:Get_table_target ; (0x8048277)
    E8 BAFFFFFF                 # CALLI32 %Read_byte          ; Get single char label
    C1E0 02                     # SHLI8_EAX !2                ; Each label in table takes 4 bytes to store
    05 B1820408                 # ADDI32_EAX &table           ; Calculate offset
    C3                          # RET

#:StoreLabel ; (0x8048285)
    E8 EDFFFFFF                 # CALLI32 %Get_table_target
    8938                        # STORE32_EDI_into_Address_EAX ; Write out pointer to table
    C3                          # RET

#:StorePointer ; (0x804828D)
    83C7 04                     # ADDI8_EDI !4                ; Increment IP
    E8 E2FFFFFF                 # CALLI32 %Get_table_target   ; Get address of pointer
    8B00                        # LOAD32_Address_EAX_into_EAX ; Get pointer
    29F8                        # SUB_EDI_from_EAX            ; target - ip
    A3 B1820408                 # STORE32_Absolute32_eax &table ; put value in output
    BA 04000000                 # LOADI32_EDX %4              ; set the size of chars we want
    E8 BCFFFFFF                 # CALLI32 %print_chars
    C3                          # RET

#:fin ; (0x80482A9)
    00000000                    # NULL
#:fout ; (0x80482AD)
    00000000                    # NULL
#:table ; (0x80482B1)
#:ELF_end

https://github.com/oriansj/stage0-posix-x86/blob/main/hex2_x86.hex1

File: ../git/live-bootstrap/sysa/stage0-posix/src/x86/hex2_x86.hex1 used in: Step 6
;; Copyright (C) 2017 Jeremiah Orians
;; This file is part of stage0.
;;
;; stage0 is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; stage0 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 stage0.  If not, see <http://www.gnu.org/licenses/>.

## ELF Header
7F 45 4C 46                     # e_ident[EI_MAG0-3] ELF's magic number

01                              # e_ident[EI_CLASS] Indicating 32 bit
01                              # e_ident[EI_DATA] Indicating little endianness
01                              # e_ident[EI_VERSION] Indicating original elf

03                              # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                              # e_ident[EI_ABIVERSION] Set at 0 because none cares

00 00 00 00 00 00 00            # e_ident[EI_PAD]

02 00                           # e_type Indicating Executable
03 00                           # e_machine Indicating x86
01 00 00 00                     # e_version Indicating original elf

54 80 04 08                     # e_entry Address of the entry point
34 00 00 00                     # e_phoff Address of program header table
00 00 00 00                     # e_shoff Address of section header table

00 00 00 00                     # e_flags

34 00                           # e_ehsize Indicating our 52 Byte header

20 00                           # e_phentsize size of a program header table
01 00                           # e_phnum number of entries in program table

00 00                           # e_shentsize size of a section header table
00 00                           # e_shnum number of entries in section table

00 00                           # e_shstrndx index of the section names

## Program Header
01 00 00 00                     # ph_type: PT-LOAD = 1
00 00 00 00                     # ph_offset

00 80 04 08                     # ph_vaddr
00 80 04 08                     # ph_physaddr

7E 05 00 00                     # ph_filesz
7E 05 00 00                     # ph_memsz

07 00 00 00                    # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                    # ph_align

    ;; Register usage:
    ;; EAX, EDX, ECX, EBX => Temps
    ;; EDI => IP
    ;; EBP => MALLOC
    ;; ESI => HEAD

    ;; Struct format: (size 24)
    ;; NEXT => 0
    ;; TARGET => 8
    ;; NAME => 16

; Where the ELF Header is going to hit
; Simply jump to _start
; Our main function

#:_start
    BB 00000000                 ; LOADI32_EBX %0              ; Get current pointer
    E8 %x                       ; CALL32 %malloc              ; Get current HEAP
    89C3                        ; COPY_EAX_to_EBX             ; Using current
    89C5                        ; COPY_EAX_to_EBP             ; Setup MALLOC
    81C3 0000C000               ; ADDI32_EBX %12582912        ; Create space for temp [12M]
    E8 %x                       ; CALL32 %malloc              ; Give ourselves 8192000 bytes to work with

    58                          ; POP_EAX                     ; Get the number of arguments
    5B                          ; POP_EBX                     ; Get the program name
    5B                          ; POP_EBX                     ; Get the actual input name
    B9 00000000                 ; LOADI32_ECX %0              ; prepare read_only
    BA 00000000                 ; LOADI32_EDX %0              ; Really sure
    B8 05000000                 ; LOADI32_EAX %5              ; the syscall number for open()
    CD80                        ; INT_80                      ; Now open that damn file
    A3 6E850408                 ; STORE32_Absolute32_eax &Input ; Preserve the file pointer we were given

    5B                          ; POP_EBX                     ; Get the actual output name
    B9 41020000                 ; LOADI32_ECX %577            ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    BA C0010000                 ; LOADI32_EDX %448            ; Prepare file as RWX for owner only (700 in octal)
    B8 05000000                 ; LOADI32_EAX %5              ; the syscall number for open()
    CD80                        ; INT_80                      ; Now open that damn file
    83F8 00                     ; CMPI8_EAX !0                ; Check for missing output
    0F8F %a                     ; JG32 %_start_out            ; Have real input
    B8 01000000                 ; LOADI32_EAX %1              ; Use stdout

:a #:_start_out
    A3 72850408                 ; STORE32_Absolute32_eax &Output ; Preserve the file pointer we were given


    E8 %I                       ; CALL32 %ClearScratch        ; Zero scratch
    B8 FFFFFFFF                 ; LOADI32_EAX %-1             ; Our flag for byte processing
    A3 6A850408                 ; STORE32_Absolute32_eax &Flag ; Set
    B8 00000000                 ; LOADI32_EAX %0              ; temp storage for the sum
    A3 66850408                 ; STORE32_Absolute32_eax &High ; Set
    BF 00800408                 ; LOADI32_EDI %0x8048000      ; Our starting IP
    BE 00000000                 ; LOADI32_ESI %0              ; HEAD = NULL
    E8 %b                       ; CALL32 %First_pass          ; Process it

    ; rewind input file
    8B1D 6E850408               ; LOAD32_Absolute32_ebx &Input ; Using our input file
    B9 00000000                 ; LOADI32_ECX %0              ; Offset Zero
    BA 00000000                 ; LOADI32_EDX %0              ; Whence Zero
    B8 13000000                 ; LOADI32_EAX %19             ; lseek
    56                          ; PUSH_ESI                    ; Protect HEAD
    CD80                        ; INT_80
    5E                          ; POP_ESI                     ; Restore HEAD

    B8 FFFFFFFF                 ; LOADI32_EAX %-1             ; Our flag for byte processing
    A3 6A850408                 ; STORE32_Absolute32_eax &Flag ; Set
    B8 00000000                 ; LOADI32_EAX %0              ; temp storage for the sum
    A3 66850408                 ; STORE32_Absolute32_eax &High ; Set
    BF 00800408                 ; LOADI32_EDI %0x8048000      ; Our starting IP
    E8 %l                       ; CALL32 %Second_pass         ; Process it

    E9 %w                       ; JMP32 %Done

:b #:First_pass
    E8 %y                       ; CALL32 %Read_byte

    ; Deal with EOF
    83F8 FC                     ; CMPI8_EAX !-4
    0F84 %j                     ; JE32 %First_pass_done

    ; Check for :
    83F8 3A                     ; CMPI8_EAX !0x3A
    0F85 %c                     ; JNE32 %First_pass_0

    ; Deal with label
    E9 %D                       ; JMP32 %StoreLabel

:c #:First_pass_0
    ; Check for !
    83F8 21                     ; CMPI8_EAX !0x21
    0F84 %i                     ; JE32 %First_pass_pointer

    ; Check for @
    83F8 40                     ; CMPI8_EAX !0x40
    0F84 %i                     ; JE32 %First_pass_pointer

    ; Check for $
    83F8 24                     ; CMPI8_EAX !0x24
    0F84 %i                     ; JE32 %First_pass_pointer

    ; Check for %
    83F8 25                     ; CMPI8_EAX !0x25
    0F84 %i                     ; JE32 %First_pass_pointer

    ; Check for &
    83F8 26                     ; CMPI8_EAX !0x26
    0F84 %i                     ; JE32 %First_pass_pointer

    ; Deal with everything else
    E8 %k                       ; CALL32 %hex                 ; Process our char

    ; Deal with EOF
    83F8 FC                     ; CMPI8_EAX !-4
    0F84 %j                     ; JE32 %First_pass_done

    ; deal with -1 values
    83F8 00                     ; CMPI8_EAX !0
    0F8C %b                     ; JL32 %First_pass

    ; deal with toggle
    A1 6A850408                 ; LOAD32_Absolute32_eax &Flag
    83F8 00                     ; CMPI8_EAX !0
    0F84 %d                     ; JE32 %First_pass_1
    83C7 01                     ; ADDI8_EDI !1                ; Increment IP

:d #:First_pass_1
    F7D0                        ; NOT_EAX
    A3 6A850408                 ; STORE32_Absolute32_eax &Flag
    E9 %b                       ; JMP32 %First_pass

:e #:Update_Pointer
    ; Check for !
    83F8 21                     ; CMPI8_EAX !0x21
    0F84 %h                     ; JE32 %Update_Pointer_1

    ; Check for @
    83F8 40                     ; CMPI8_EAX !0x40
    0F84 %g                     ; JE32 %Update_Pointer_2

    ; Check for $
    83F8 24                     ; CMPI8_EAX !0x24
    0F84 %g                     ; JE32 %Update_Pointer_2

    ; Check for %
    83F8 25                     ; CMPI8_EAX !0x25
    0F84 %f                     ; JE32 %Update_Pointer_4

    ; Check for &
    83F8 26                     ; CMPI8_EAX !0x26
    0F84 %f                     ; JE32 %Update_Pointer_4

    ;; deal with bad input
    E8 %R                       ; CALL32 %fail

:f #:Update_Pointer_4
    83C7 02                     ; ADDI8_EDI !2                ; Increment IP
:g #:Update_Pointer_2
    83C7 01                     ; ADDI8_EDI !1                ; Increment IP
:h #:Update_Pointer_1
    83C7 01                     ; ADDI8_EDI !1                ; Increment IP
    C3                          ; RET

:i #:First_pass_pointer
    ; Deal with Pointer to label
    E8 %e                       ; CALL32 %Update_Pointer      ; Increment IP
    BB 7A850408                 ; LOADI32_EBX &table          ; Using scratch
    E8 %B                       ; CALL32 %consume_token       ; Read token
    E8 %I                       ; CALL32 %ClearScratch        ; Throw away token
    83F8 3E                     ; CMPI8_EAX !0x3E             ; check for '>'
    0F85 %b                     ; JNE32 %First_pass           ; Loop again

    ;; Deal with %label>label case
    BB 7A850408                 ; LOADI32_EBX &table          ; Write to scratch
    E8 %B                       ; CALL32 %consume_token       ; get token
    E8 %I                       ; CALL32 %ClearScratch        ; Clean up after ourselves
    E9 %b                       ; JMP32 %First_pass           ; Loop again

:j # :First_pass_done
    C3                          ; RET

:k #:hex
    ; deal with EOF
    83F8 FC                     ; CMPI8_EAX !-4
    0F84 %o                     ; JE32 %EOF
    ; deal with line comments starting with #
    83F8 23                     ; CMPI8_EAX !0x23
    0F84 %t                     ; JE32 %ascii_comment
    ; deal with line comments starting with ;
    83F8 3B                     ; CMPI8_EAX !0x3B
    0F84 %t                     ; JE32 %ascii_comment
    ; deal all ascii less than 0
    83F8 30                     ; CMPI8_EAX !0x30
    0F8C %s                     ; JL32 %ascii_other
    ; deal with 0-9
    83F8 3A                     ; CMPI8_EAX !0x3A
    0F8C %p                     ; JL32 %ascii_num
    ; deal with all ascii less than A
    83F8 41                     ; CMPI8_EAX !0x41
    0F8C %s                     ; JL32 %ascii_other
    ; deal with A-F
    83F8 47                     ; CMPI8_EAX !0x47
    0F8C %r                     ; JL32 %ascii_high
    ;deal with all ascii less than a
    83F8 61                     ; CMPI8_EAX !0x61
    0F8C %s                     ; JL32 %ascii_other
    ;deal with a-f
    83F8 67                     ; CMPI8_EAX !0x67
    0F8C %q                     ; JL32 %ascii_low
    ; The rest that remains needs to be ignored
    E9 %s                       ; JMP32 %ascii_other

:l #:Second_pass
    E8 %y                       ; CALL32 %Read_byte

    ; Deal with EOF
    83F8 FC                     ; CMPI8_EAX !-4
    0F84 %n                     ; JE32 %Second_pass_done

    ; Simply drop the label
    83F8 3A                     ; CMPI8_EAX !0x3A
    0F85 %m                     ; JNE32 %Second_pass_0

    BB 7A850408                 ; LOADI32_EBX &table          ; Using scratch
    E8 %B                       ; CALL32 %consume_token       ; Read token
    E8 %I                       ; CALL32 %ClearScratch        ; Throw away token

    E9 %l                       ; JMP32 %Second_pass

:m #:Second_pass_0
    ; Deal with % pointer
    83F8 25                     ; CMPI8_EAX !0x25
    0F84 %M                     ; JE32 %StorePointer_rel4

    ; Deal with @ pointer
    83F8 40                     ; CMPI8_EAX !0x40
    0F84 %N                     ; JE32 %StorePointer_rel2

    ; Deal with ! pointer
    83F8 21                     ; CMPI8_EAX !0x21
    0F84 %O                     ; JE32 %StorePointer_rel1

    ; Deal with & pointer
    83F8 26                     ; CMPI8_EAX !0x26
    0F84 %P                     ; JE32 %StorePointer_abs4

    ; Deal with $ pointer
    83F8 24                     ; CMPI8_EAX !0x24
    0F84 %Q                     ; JE32 %StorePointer_abs2

#:Second_pass_1
    ; Deal with everything else
    E8 %k                       ; CALL32 %hex                 ; Process our char

    ; Deal with EOF
    83F8 FC                     ; CMPI8_EAX !-4
    0F84 %n                     ; JE32 %Second_pass_done

    ; deal with -1 values
    83F8 00                     ; CMPI8_EAX !0
    0F8C %l                     ; JL32 %Second_pass

    ; deal with toggle
    8B1D 6A850408               ; LOAD32_Absolute32_ebx &Flag
    83FB 00                     ; CMPI8_EBX !0
    0F84 %v                     ; JE32 %print

    ; process first byte of pair
    C1E0 04                     ; SHLI8_EAX !4
    A3 66850408                 ; STORE32_Absolute32_eax &High
    B8 00000000                 ; LOADI32_EAX %0
    A3 6A850408                 ; STORE32_Absolute32_eax &Flag
    E9 %l                       ; JMP32 %Second_pass

:n #:Second_pass_done
    C3                          ; RET

:o #:EOF
    C3                          ; RET
:p #:ascii_num
    83E8 30                     ; SUBI8_EAX !0x30
    C3                          ; RET
:q #:ascii_low
    83E8 57                     ; SUBI8_EAX !0x57
    C3                          ; RET
:r #:ascii_high
    83E8 37                     ; SUBI8_EAX !0x37
    C3                          ; RET
:s #:ascii_other
    B8 FFFFFFFF                 ; LOADI32_EAX %-1
    C3                          ; RET
:t #:ascii_comment
    E8 %y                       ; CALL32 %Read_byte
    83F8 0D                     ; CMPI8_EAX !0x0D
    0F84 %u                     ; JE32 %ascii_comment_cr
    83F8 0A                     ; CMPI8_EAX !0x0A
    0F85 %t                     ; JNE32 %ascii_comment
:u #:ascii_comment_cr
    B8 FFFFFFFF                 ; LOADI32_EAX %-1
    C3                          ; RET

; process second byte of pair
:v #:print
    ; update the sum and store in output
    0305 66850408               ; ADD32_Absolute32_eax &High

    ; Print our first Hex
    BA 01000000                 ; LOADI32_EDX %1              ; set the size of chars we want
    E8 %A                       ; CALL32 %print_chars

    ; flip the toggle
    A1 6A850408                 ; LOAD32_Absolute32_eax &Flag
    F7D0                        ; NOT_EAX
    A3 6A850408                 ; STORE32_Absolute32_eax &Flag

    83C7 01                     ; ADDI8_EDI !1                ; Increment IP
    E9 %l                       ; JMP32 %Second_pass

:w #:Done
    ; program completed Successfully
    BB 00000000                 ; LOADI32_EBX %0              ; All is well
    B8 01000000                 ; LOADI32_EAX %1              ; put the exit syscall number in eax
    CD80                        ; INT_80                      ; Call it a good day


;; Malloc isn't actually required if the program being built fits in the initial memory
;; However, it doesn't take much to add it.
;; Requires a value in EBX
:x #:malloc
    B8 2D000000                 ; LOADI32_EAX %45             ; the Syscall # for SYS_BRK
    56                          ; PUSH_ESI                    ; Protect esi
    57                          ; PUSH_EDI                    ; Protect edi
    CD80                        ; INT_80                      ; call the Kernel
    5F                          ; POP_EDI                     ; Restore edi
    5E                          ; POP_ESI                     ; Restore esi
    C3                          ; RET


:y #:Read_byte
    52                          ; PUSH_EDX                    ; Protect EDX
    51                          ; PUSH_ECX                    ; Protect ECX
    53                          ; PUSH_EBX                    ; Protect EBX
    B8 FCFFFFFF                 ; LOADI32_EAX %-4             ; Put EOF in eax
    50                          ; PUSH_EAX                    ; Assume bad (If nothing read, value will remain EOF)
    8D0C24                      ; LEA32_ECX_from_esp          ; Get stack address
    8B1D 6E850408               ; LOAD32_Absolute32_ebx &Input ; Where are we reading from
    B8 03000000                 ; LOADI32_EAX %3              ; the syscall number for read
    BA 01000000                 ; LOADI32_EDX %1              ; set the size of chars we want
    CD80                        ; INT_80                      ; call the Kernel
    85C0                        ; TEST                        ; check what we got
    58                          ; POP_EAX                     ; Get either char or EOF
    0F84 %z                     ; JE32 %Read_byte_1           ; Got EOF call it done
    0FB6C0                      ; MOVZX_al                    ; We have to zero extend it to use it
    5B                          ; POP_EBX                     ; Restore EBX
    59                          ; POP_ECX                     ; Restore ECX
    5A                          ; POP_EDX                     ; Restore EDX
    C3                          ; RET

; Deal with EOF
:z #:Read_byte_1
    5B
    59
    5A
    C3                          ; RET

:A #:print_chars
    52                          ; PUSH_EDX                    ; Protect EDX
    51                          ; PUSH_ECX                    ; protect ECX
    53                          ; PUSH_EBX                    ; protect EBX
    50                          ; PUSH_EAX                    ; We are writing eax
    8D0C24                      ; LEA32_ECX_from_esp          ; Get stack address
    8B1D 72850408               ; LOAD32_Absolute32_ebx &Output ; Write to target file
    B8 04000000                 ; LOADI32_EAX %4              ; the syscall number for write
    ;; EDX contains the number of bytes to write
    CD80                        ; INT_80                      ; call the Kernel
    58                          ; POP_EAX                     ; Restore stack
    5B                          ; POP_EBX                     ; Restore EBX
    59                          ; POP_ECX                     ; Restore ECX
    5A                          ; POP_EDX                     ; Restore EDX
    C3                          ; RET

;; Receives pointer in EBX
;; Writes out char and updates EBX
:B #:consume_token
    E8 %y                       ; CALL32 %Read_byte           ; Consume_token

    ; Check for \t
    83F8 09                     ; CMPI8_EAX !0x09
    0F84 %C                     ; JE32 %consume_token_done

    ; Check for \n
    83F8 0A                     ; CMPI8_EAX !0x0A
    0F84 %C                     ; JE32 %consume_token_done

    ; Check for ' '
    83F8 20                     ; CMPI8_EAX !0x20
    0F84 %C                     ; JE32 %consume_token_done

    ; Check for '>'
    83F8 3E                     ; CMPI8_EAX !0x3E
    0F84 %C                     ; JE32 %consume_token_done

    ;; Looks like we are still reading token
    8803                        ; STORE8_al_into_Address_EBX  ; Store char
    83C3 01                     ; ADDI8_EBX !1                ; Point to next spot
    E9 %B                       ; JMP32 %consume_token        ; loop until done

:C #:consume_token_done
    B9 00000000                 ; LOADI32_ECX %0              ; Pad with nulls
    890B                        ; STORE32_ECX_into_Address_EBX
    83C3 04                     ; ADDI8_EBX !4
    C3                          ; RET

:D #:StoreLabel
    89E8                        ; COPY_EBP_to_EAX             ; ENTRY
    83C5 18                     ; ADDI8_EBP !24               ; CALLOC
    8978 08                     ; STORE32_EDI_into_Address_EAX_Immediate8 !8 ; ENTRY->TARGET = IP
    8930                        ; STORE32_ESI_into_Address_EAX ; ENTRY->NEXT = JUMP_TABLE
    89C6                        ; COPY_EAX_to_ESI             ; JUMP_TABLE = ENTRY
    896E 10                     ; STORE32_EBP_into_Address_ESI_Immediate8 !16 ; ENTRY->NAME = TOKEN
    89EB                        ; COPY_EBP_to_EBX             ; Write Starting after struct
    E8 %B                       ; CALL32 %consume_token       ; Collect whole string
    89DD                        ; COPY_EBX_to_EBP             ; Update HEAP
    E9 %b                       ; JMP32 %First_pass

:E #:GetTarget
    53                          ; PUSH_EBX                    ; protect ebx
    51                          ; PUSH_ECX                    ; protect ecx
    52                          ; PUSH_EDX                    ; protect edx
    56                          ; PUSH_ESI                    ; protect JUMP_TABLE
    B9 7A850408                 ; LOADI32_ECX &table          ; Reset scratch
    8B56 10                     ; LOAD32_EDX_from_ESI_Immediate8 !16 ; I->NAME
:F #:GetTarget_loop
    8A01                        ; LOAD8_al_from_ECX           ; I->NAME[0]
    8A1A                        ; LOAD8_bl_from_EDX           ; scratch[0]
    0FB6DB                      ; MOVZX_bl                    ; Zero extend
    0FB6C0                      ; MOVZX_al                    ; Zero extend
    38D8                        ; CMP_al_bl                   ; IF TOKEN == I->NAME
    0F85 %G                     ; JNE32 %GetTarget_miss       ; Oops

    83C1 01                     ; ADDI8_ECX !1
    83C2 01                     ; ADDI8_EDX !1
    3C 00                       ; CMPI8_al !0
    0F85 %F                     ; JNE32 %GetTarget_loop       ; Loop until
    E9 %H                       ; JMP32 %GetTarget_done       ; Match

    ;; Miss
:G #:GetTarget_miss
    8B36                        ; LOAD32_ESI_from_ESI         ; I = I->NEXT
    83FE 00                     ; CMPI8_ESI !0                ; IF NULL == I
    0F84 %R                     ; JE32 %fail                  ; Abort hard

    8B56 10                     ; LOAD32_EDX_from_ESI_Immediate8 !16 ; I->NAME
    B9 7A850408                 ; LOADI32_ECX &table          ; Reset scratch
    E9 %F                       ; JMP32 %GetTarget_loop

:H #:GetTarget_done
    8B46 08                     ; LOAD32_EAX_from_ESI_Immediate8 !8 ; Get address
    5E                          ; POP_ESI                     ; Restore JUMP_TABLE
    5A                          ; POP_EDX                     ; Restore EDX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    C3                          ; RET

:I #:ClearScratch
    50                          ; PUSH_EAX                    ; Protect against changes
    53                          ; PUSH_EBX                    ; And overwrites
    51                          ; PUSH_ECX                    ; While we work
    BB 7A850408                 ; LOADI32_EBX &table          ; Where our table is
    B8 00000000                 ; LOADI32_EAX %0              ; Using null

:J #:ClearScratch_loop
    8B0B                        ; LOAD32_ECX_from_EBX         ; Get current value
    8803                        ; STORE8_al_into_Address_EBX  ; Because we want null
    83C3 01                     ; ADDI8_EBX !1                ; Increment
    83F9 00                     ; CMPI8_ECX !0                ; Check if we hit null
    0F85 %J                     ; JNE32 %ClearScratch_loop    ; Keep looping

    59                          ; POP_ECX                     ; Restore
    5B                          ; POP_EBX                     ; Damage
    58                          ; POP_EAX                     ; Entirely
    C3                          ; RET

:K #:StorePointer
    E8 %e                       ; CALL32 %Update_Pointer      ; Increment IP
    BB 7A850408                 ; LOADI32_EBX &table          ; Write to scratch
    E8 %B                       ; CALL32 %consume_token       ; get token
    50                          ; PUSH_EAX                    ; Protect base_sep_p
    B8 7A850408                 ; LOADI32_EAX &table          ; Pointer to scratch
    E8 %E                       ; CALL32 %GetTarget           ; Get address of pointer
    E8 %I                       ; CALL32 %ClearScratch        ; Clean up after ourselves
    89FA                        ; COPY_EDI_to_EDX             ; base = IP
    5B                          ; POP_EBX                     ; Restore base_sep_p
    83FB 3E                     ; CMPI8_EBX !0x3E             ; If base_sep_p == '>'
    0F85 %L                     ; JNE32 %StorePointer_done    ; If not

    ;; Deal with %label>label case
    50                          ; PUSH_EAX                    ; We need to preserve main target
    BB 7A850408                 ; LOADI32_EBX &table          ; Write to scratch
    E8 %B                       ; CALL32 %consume_token       ; get token
    B8 7A850408                 ; LOADI32_EAX &table          ; Pointer to scratch
    E8 %E                       ; CALL32 %GetTarget           ; Get address of pointer
    E8 %I                       ; CALL32 %ClearScratch        ; Clean up after ourselves
    89C2                        ; COPY_EAX_to_EDX             ; Use our new base
    58                          ; POP_EAX                     ; Restore main target

:L #:StorePointer_done
    C3                          ; RET

:M #:StorePointer_rel4
    E8 %K                       ; CALL32 %StorePointer        ; Do Common
    29D0                        ; SUB_EDX_from_EAX            ; target - ip
    BA 04000000                 ; LOADI32_EDX %4              ; set the size of chars we want
    E8 %A                       ; CALL32 %print_chars
    E8 %I                       ; CALL32 %ClearScratch        ; Clean up after ourselves
    E9 %l                       ; JMP32 %Second_pass

:N #:StorePointer_rel2
    E8 %K                       ; CALL32 %StorePointer        ; Do Common
    29D0                        ; SUB_EDX_from_EAX            ; target - ip
    BA 02000000                 ; LOADI32_EDX %2              ; set the size of chars we want
    E8 %A                       ; CALL32 %print_chars
    E8 %I                       ; CALL32 %ClearScratch        ; Clean up after ourselves
    E9 %l                       ; JMP32 %Second_pass

:O #:StorePointer_rel1
    E8 %K                       ; CALL32 %StorePointer        ; Do Common
    29D0                        ; SUB_EDX_from_EAX            ; target - ip
    BA 01000000                 ; LOADI32_EDX %1              ; set the size of chars we want
    E8 %A                       ; CALL32 %print_chars
    E8 %I                       ; CALL32 %ClearScratch        ; Clean up after ourselves
    E9 %l                       ; JMP32 %Second_pass

:P #:StorePointer_abs4
    E8 %K                       ; CALL32 %StorePointer        ; Do Common
    BA 04000000                 ; LOADI32_EDX %4              ; set the size of chars we want
    E8 %A                       ; CALL32 %print_chars
    E8 %I                       ; CALL32 %ClearScratch        ; Clean up after ourselves
    E9 %l                       ; JMP32 %Second_pass

:Q #:StorePointer_abs2
    E8 %K                       ; CALL32 %StorePointer        ; Do Common
    BA 02000000                 ; LOADI32_EDX %2              ; set the size of chars we want
    E8 %A                       ; CALL32 %print_chars
    E8 %I                       ; CALL32 %ClearScratch        ; Clean up after ourselves
    E9 %l                       ; JMP32 %Second_pass

:R #:fail
    ; Some shit went wrong
    BB 01000000                 ; LOADI32_EBX %1              ; All is wrong
    B8 01000000                 ; LOADI32_EAX %1              ; put the exit syscall number in eax
    CD80                        ; INT_80                      ; Call it a good day


#:High (0x8048566)
    00000000                    ; NULL
#:Flag (0x804856A)
    00000000                    ; NULL
#:Input (0x804856E)
    00000000                    ; NULL
#:Output (0x8048572)
    00000000                    ; NULL
#:write (0x8048576)
    00000000                    ; NULL
#:table (0x804857A)
    00000000                    ; NULL
#:ELF_end

https://github.com/oriansj/stage0-posix-x86/blob/main/catm_x86.hex2

File: ../git/live-bootstrap/sysa/stage0-posix/src/x86/catm_x86.hex2 used in: Step 7
;; Copyright (C) 2019 Jeremiah Orians
;; This file is part of mescc-tools.
;;
;; mescc-tools is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; mescc-tools 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 mescc-tools. If not, see <http://www.gnu.org/licenses/>.

    ;; Register usage:
    ;; EBP => OUTPUT
    ;; EDI => Buffer
    ;; ESI => INPUT

## ELF Header
:ELF_base
7F 45 4C 46                    # e_ident[EI_MAG0-3] ELF's magic number

01                             # e_ident[EI_CLASS] Indicating 32 bit
01                             # e_ident[EI_DATA] Indicating little endianness
01                             # e_ident[EI_VERSION] Indicating original elf

03                             # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                             # e_ident[EI_ABIVERSION] Set at 0 because none cares

00 00 00 00 00 00 00           # e_ident[EI_PAD]

02 00                          # e_type Indicating Executable
03 00                          # e_machine Indicating x86
01 00 00 00                    # e_version Indicating original elf

&_start                        # e_entry Address of the entry point
%ELF_program_headers>ELF_base  # e_phoff Address of program header table
00 00 00 00                    # e_shoff Address of section header table

00 00 00 00                    # e_flags

34 00                          # e_ehsize Indicating our 52 Byte header

20 00                          # e_phentsize size of a program header table
01 00                          # e_phnum number of entries in program table

00 00                          # e_shentsize size of a section header table
00 00                          # e_shnum number of entries in section table

00 00                          # e_shstrndx index of the section names

## Program Header
:ELF_program_headers
:ELF_program_header__text
01 00 00 00                    # ph_type: PT-LOAD = 1
00 00 00 00                    # ph_offset

&ELF_base                      # ph_vaddr
&ELF_base                      # ph_physaddr

%ELF_end>ELF_base              # ph_filesz
%ELF_end>ELF_base              # ph_memsz

07 00 00 00                    # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                    # ph_align

:ELF_text


; Where the ELF Header is going to hit
; Simply jump to _start
; Our main function
:_start
    58                          # POP_EAX                     ; Get the number of arguments
    5B                          # POP_EBX                     ; Get the program name
    5B                          # POP_EBX                     ; Get the actual output name
    66B9 4102                   # LOADI16_CX @577             ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    66BA 8001                   # LOADI16_DX @384             ; Prepare file as RW for owner only (600 in octal)
    6A 05                       # PUSH !5                     ; prepare to set eax to 5
    58                          # POP_EAX                     ; the syscall number for open()
    CD80                        # INT_80                      ; Now open that file
    89C5                        # COPY_EAX_to_EBP             ; Preserve the file pointer we were given

    6A 2D                       # PUSH !45
    58                          # POP_EAX                     ; the Syscall # for SYS_BRK
    31DB                        # XOR_EBX_EBX                 ; Get current brk
    CD80                        # INT_80                      ; Let the kernel do the work
    89C7                        # COPY_EAX_to_EDI             ; Set our malloc pointer

    6A 2D                       # PUSH !45
    58                          # POP_EAX                     ; the Syscall # for SYS_BRK
    89FB                        # COPY_EDI_to_EBX             ; Using current pointer
    81C3 00001000               # ADDI32_EBX %0x100000        ; Allocate 1MB
    CD80                        # INT_80                      ; Let the kernel do the work

:core
    5B                          # POP_EBX                     ; Get the actual input name
    85DB                        # TEST_EBX_EBX                ; Check for null string
    74 !done                    # JE8 !done                   ; Hit null be done

    31C9                        # XOR_ECX_ECX                 ; prepare read_only
    31D2                        # XOR_EDX_EDX                 ; prevent any interactions
    6A 05                       # PUSH !5
    58                          # POP_EAX                     ; the syscall number for open()
    CD80                        # INT_80                      ; Now open that damn file
    89C6                        # COPY_EAX_to_ESI             ; Protect INPUT
:keep
    BA 00001000                 # LOADI32_EDX %0x100000       ; set the size of chars we want
    89F9                        # COPY_EDI_to_ECX             ; Where to put it
    89F3                        # COPY_ESI_to_EBX             ; Where are we reading from
    6A03                        # PUSH !3
    58                          # POP_EAX                     ; the syscall number for read
    CD80                        # INT_80                      ; call the Kernel
    50                          # PUSH_EAX                    ; Protect the number of bytes read

    89C2                        # COPY_EAX_to_EDX             ; Number of bytes to write
    89F9                        # COPY_EDI_to_ECX             ; What we are writing
    89EB                        # COPY_EBP_to_EBX             ; Write to target file
    6A 04                       # PUSH !4
    58                          # POP_EAX                     ; the syscall number for write
    CD80                        # INT_80                      ; call the Kernel

    58                          # POP_EAX                     ; Get bytes read
    3D 00001000                 # CMPI32_EAX %0x100000        ; Check if buffer was fully used
    74 !keep                    # JE8 !keep                   ; Keep looping if was full
    EB !core                    # JMP8 !core                  ; Otherwise move to next file

:done
    ; program completed Successfully
    31DB                        # XOR_EBX_EBX                 ; All is well
    6A 01                       # PUSH !1
    58                          # POP_EAX                     ; put the exit syscall number in eax
    CD80                        # INT_80                      ; Call it a good day

:ELF_end

https://github.com/oriansj/stage0-posix-x86/blob/main/ELF-i386.hex2

File: ../git/live-bootstrap/sysa/stage0-posix/src/x86/ELF-i386.hex2 used in: Step 8, Step 11, Step 17
### Copyright (C) 2016 Jeremiah Orians
### Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
### This file is part of stage0.
###
### stage0 is free software: you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation, either version 3 of the License, or
### (at your option) any later version.
###
### stage0 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 stage0.  If not, see <http://www.gnu.org/licenses/>.

### stage0's hex2 format
###    !<label>          1 byte relative
###    $<label>          2 byte address
###    @<label>          2 byte relative
###    &<label>          4 byte address
###    %<label>          4 byte relative

### elf32.hex2: 32 bit elf header in hex2
### if you wish to use this header, you need to add :ELF_end to the end of your
### M1 or hex2 files.

## ELF Header
:ELF_base
7F 45 4C 46                    # e_ident[EI_MAG0-3] ELF's magic number

01                             # e_ident[EI_CLASS] Indicating 32 bit
01                             # e_ident[EI_DATA] Indicating little endianness
01                             # e_ident[EI_VERSION] Indicating original elf

03                             # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                             # e_ident[EI_ABIVERSION] Set at 0 because none cares

00 00 00 00 00 00 00           # e_ident[EI_PAD]

02 00                          # e_type Indicating Executable
03 00                          # e_machine Indicating x86
01 00 00 00                    # e_version Indicating original elf

&_start                        # e_entry Address of the entry point
%ELF_program_headers>ELF_base  # e_phoff Address of program header table
00 00 00 00                    # e_shoff Address of section header table

00 00 00 00                    # e_flags

34 00                          # e_ehsize Indicating our 52 Byte header

20 00                          # e_phentsize size of a program header table
01 00                          # e_phnum number of entries in program table

00 00                          # e_shentsize size of a section header table
00 00                          # e_shnum number of entries in section table

00 00                          # e_shstrndx index of the section names

## Program Header
:ELF_program_headers
:ELF_program_header__text
01 00 00 00                    # ph_type: PT-LOAD = 1
00 00 00 00                    # ph_offset

&ELF_base                      # ph_vaddr
&ELF_base                      # ph_physaddr

%ELF_end>ELF_base              # ph_filesz
%ELF_end>ELF_base              # ph_memsz

07 00 00 00                    # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                    # ph_align

:ELF_text

https://github.com/oriansj/stage0-posix-x86/blob/main/M0_x86.hex2

File: ../git/live-bootstrap/sysa/stage0-posix/src/x86/M0_x86.hex2 used in: Step 8
;; Copyright (C) 2017 Jeremiah Orians
;; This file is part of stage0.
;;
;; stage0 is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; stage0 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 stage0.  If not, see <http://www.gnu.org/licenses/>.

    ;; Register usage:
    ;; EAX, ECX, EBX => Temps
    ;; EDI => MALLOC
    ;; EBP => HEAD
    ;; [Output] => Output_file
    ;; [Input] => Input_file

    ;; Struct format: (size 32)
    ;; NEXT => 0
    ;; TYPE => 8
    ;; TEXT => 16
    ;; EXPRESSION => 24

    ;; Types
    ;; None => 0
    ;; MACRO => 1
    ;; STRING => 2

; Where the ELF Header is going to hit
; Simply jump to _start
; Our main function
:_start
    58                          ; POP_EAX                     ; Get the number of arguments
    5B                          ; POP_EBX                     ; Get the program name
    5B                          ; POP_EBX                     ; Get the actual input name
    B9 00000000                 ; LOADI32_ECX %0              ; prepare read_only
    B8 05000000                 ; LOADI32_EAX %5              ; the syscall number for open()
    CD80                        ; INT_80                      ; Now open that damn file
    A3 &Input                   ; STORE32_Absolute32_eax &Input ; Preserve the file pointer we were given

    5B                          ; POP_EBX                     ; Get the actual output name
    B9 41020000                 ; LOADI32_ECX %577            ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    BA 80010000                 ; LOADI32_EDX %384            ; Prepare file as RW for owner only (600 in octal)
    B8 05000000                 ; LOADI32_EAX %5              ; the syscall number for open()
    CD80                        ; INT_80                      ; Now open that damn file
    83F8 00                     ; CMPI8_EAX !0                ; Check for missing output
    0F8F %_start_out            ; JG32 %_start_out            ; Have real input
    B8 01000000                 ; LOADI32_EAX %1              ; Use stdout

:_start_out
    A3 &Output                  ; STORE32_Absolute32_eax &Output ; Preserve the file pointer we were given

    B8 2D000000                 ; LOADI32_EAX %45             ; the Syscall # for SYS_BRK
    BB 00000000                 ; LOADI32_EBX %0              ; Get current brk
    CD80                        ; INT_80                      ; Let the kernel do the work
    89C7                        ; COPY_EAX_to_EDI             ; Set our malloc pointer

    E8 %Tokenize_Line           ; CALL32 %Tokenize_Line       ; Get all lines
    89E8                        ; COPY_EBP_to_EAX             ; prepare for Reverse_List
    E8 %Reverse_List            ; CALL32 %Reverse_List        ; Correct order
    89C5                        ; COPY_EAX_to_EBP             ; Update HEAD
    E8 %Identify_Macros         ; CALL32 %Identify_Macros     ; Find the DEFINEs
    E8 %Line_Macro              ; CALL32 %Line_Macro          ; Apply the DEFINEs
    E8 %Process_String          ; CALL32 %Process_String      ; Handle strings
    E8 %Eval_Immediates         ; CALL32 %Eval_Immediates     ; Handle Numbers
    E8 %Preserve_Other          ; CALL32 %Preserve_Other      ; Collect the remaining
    E8 %Print_Hex               ; CALL32 %Print_Hex           ; Output our results

:Done
    ; program completed Successfully
    BB 00000000                 ; LOADI32_EBX %0              ; All is well
    B8 01000000                 ; LOADI32_EAX %1              ; put the exit syscall number in eax
    CD80                        ; INT_80                      ; Call it a good day


;; Tokenize_Line Function
;; Using input file [Input] and Head EBP
;; Creates a linked list of structs
;; Uses EBX for in_set strings, ECX for Int C and EDX for Struct Token* p
:Tokenize_Line
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    52                          ; PUSH_EDX                    ; Protect EDX
:restart
    E8 %fgetc                   ; CALL32 %fgetc               ; Read a char
    83F8 FC                     ; CMPI8_EAX !-4               ; Check for EOF
    0F84 %done                  ; JE32 %done                  ; File is collected

    0FB6C0                      ; MOVZX_al                    ; We have to zero extend it to use it
    89C1                        ; COPY_EAX_to_ECX             ; Protect C

    BB &comments                ; LOADI32_EBX &comments       ; Get pointer to "#;"
    E8 %In_Set                  ; CALL32 %In_Set              ; Check for comments
    83F8 01                     ; CMPI8_EAX !1                ; If comments
    0F84 %Purge_LineComment     ; JE32 %Purge_LineComment     ; try again

    89C8                        ; COPY_ECX_to_EAX             ; put C in place for check
    BB &terminators             ; LOADI32_EBX &terminators    ; Get pointer to "\n\t "
    E8 %In_Set                  ; CALL32 %In_Set              ; Check for terminators
    83F8 01                     ; CMPI8_EAX !1                ; If terminator
    0F84 %restart               ; JE32 %restart               ; try again

    B8 20000000                 ; LOADI32_EAX %32             ; Malloc the struct P
    E8 %malloc                  ; CALL32 %malloc              ; Get pointer to P
    89C2                        ; COPY_EAX_to_EDX             ; Protect P
    892A                        ; STORE32_EBP_into_Address_EDX ; P->NEXT = HEAD
    89D5                        ; COPY_EDX_to_EBP             ; HEAD = P

    89C8                        ; COPY_ECX_to_EAX             ; put C in place for check
    BB &string_char             ; LOADI32_EBX &string_char    ; Get pointer to "\"'"
    E8 %In_Set                  ; CALL32 %In_Set              ; Check for string chars
    83F8 01                     ; CMPI8_EAX !1                ; If string char
    0F84 %Store_String          ; JE32 %Store_String          ; Get string

    E8 %Store_Atom              ; CALL32 %Store_Atom          ; Get whole token
    E9 %restart                 ; JMP32 %restart

:done
    5A                          ; POP_EDX                     ; Restore EDX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    C3                          ; RET


;; fgetc function
;; Receives FILE* in [Input]
;; Returns -4 (EOF) or char in EAX
:fgetc
    52                          ; PUSH_EDX                    ; Protect EDX
    51                          ; PUSH_ECX                    ; Protect ECX
    53                          ; PUSH_EBX                    ; Protect EBX
    B8 FCFFFFFF                 ; LOADI32_EAX %-4             ; Put EOF in eax
    50                          ; PUSH_EAX                    ; Assume bad (If nothing read, value will remain EOF)
    8D0C24                      ; LEA32_ECX_from_esp          ; Get stack address
    8B1D &Input                 ; LOAD32_Absolute32_ebx &Input ; Where are we reading from
    B8 03000000                 ; LOADI32_EAX %3              ; the syscall number for read
    BA 01000000                 ; LOADI32_EDX %1              ; set the size of chars we want
    CD80                        ; INT_80                      ; call the Kernel
    58                          ; POP_EAX                     ; Get either char or EOF
    5B                          ; POP_EBX                     ; Restore EBX
    59                          ; POP_ECX                     ; Restore ECX
    5A                          ; POP_EDX                     ; Restore EDX
    C3                          ; RET


;; Malloc isn't actually required if the program being built fits in the initial memory
;; However, it doesn't take much to add it.
;; Requires EDI to be initialized and EAX to have the number of desired bytes
:malloc
    52                          ; PUSH_EDX                    ; Protect EDX
    51                          ; PUSH_ECX                    ; Protect ECX
    53                          ; PUSH_EBX                    ; Protect EBX

    89FB                        ; COPY_EDI_to_EBX             ; Using the current pointer
    01C3                        ; ADD_EAX_to_EBX              ; Request the number of desired bytes
    B8 2D000000                 ; LOADI32_EAX %45             ; the Syscall # for SYS_BRK
    CD80                        ; INT_80                      ; call the Kernel
    89F8                        ; COPY_EDI_to_EAX             ; Return pointer
    89DF                        ; COPY_EBX_to_EDI             ; Update pointer

    5B                          ; POP_EBX                     ; Restore EBX
    59                          ; POP_ECX                     ; Restore ECX
    5A                          ; POP_EDX                     ; Restore EDX
    C3                          ; RET


;; Purge_LineComment function
;; Reads chars until LF and jumps to restart
:Purge_LineComment
    E8 %fgetc                   ; CALL32 %fgetc               ; Get a char
    0FB6C0                      ; MOVZX_al                    ; Zero extend
    83F8 0A                     ; CMPI8_EAX !10               ; While not LF
    0F85 %Purge_LineComment     ; JNE32 %Purge_LineComment    ; Keep reading
    E9 %restart                 ; JMP32 %restart


;; Store_String Function
;; Receives C in ECX, HEAD in EDX and Input file in [Output]
;; Uses EBX for terminator, ECX for C and EDX for string
:Store_String
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    52                          ; PUSH_EDX                    ; Protect EDX

    B8 02000000                 ; LOADI32_EAX %2              ; Using TYPE STRING
    8942 08                     ; STORE32_EAX_into_Address_EDX_Immediate8 !8 ; HEAD->TYPE = STRING
    B8 00010000                 ; LOADI32_EAX %256            ; Malloc the string
    E8 %malloc                  ; CALL32 %malloc              ; Get pointer to P
    8942 10                     ; STORE32_EAX_into_Address_EDX_Immediate8 !16 ; HEAD->TEXT = STRING
    89CB                        ; COPY_ECX_to_EBX             ; Protect terminator
    89C2                        ; COPY_EAX_to_EDX             ; Protect string pointer
:Store_String_Loop
    880A                        ; STORE8_cl_into_Address_EDX  ; write byte
    E8 %fgetc                   ; CALL32 %fgetc               ; read next char
    0FB6C0                      ; MOVZX_al                    ; Zero extend it
    89C1                        ; COPY_EAX_to_ECX             ; Update C
    83C2 01                     ; ADDI8_EDX !1                ; STRING = STRING + 1
    39D9                        ; CMP_EBX_ECX                 ; See if we hit terminator
    0F85 %Store_String_Loop     ; JNE32 %Store_String_Loop    ; Otherwise keep looping

    5A                          ; POP_EDX                     ; Restore EDX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    89D0                        ; COPY_EDX_to_EAX             ; return HEAD
    E9 %restart                 ; JMP32 %restart


;; Store_Atom Function
;; Receives C in ECX, HEAD in EDX and Input file in [Input]
;; Uses EBX for in_set strings, ECX for C and EDX for string
:Store_Atom
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    52                          ; PUSH_EDX                    ; Protect EDX

    B8 00010000                 ; LOADI32_EAX %256            ; Malloc the string
    E8 %malloc                  ; CALL32 %malloc              ; Get pointer to P
    8942 10                     ; STORE32_EAX_into_Address_EDX_Immediate8 !16 ; HEAD->TEXT = STRING
    BB &terminators             ; LOADI32_EBX &terminators    ; Get pointer to "\n\t "
    89C2                        ; COPY_EAX_to_EDX             ; Protect string pointer
:Store_Atom_loop
    880A                        ; STORE8_cl_into_Address_EDX  ; write byte
    E8 %fgetc                   ; CALL32 %fgetc               ; read next char
    0FB6C0                      ; MOVZX_al                    ; Zero extend it
    89C1                        ; COPY_EAX_to_ECX             ; Update C
    83C2 01                     ; ADDI8_EDX !1                ; STRING = STRING + 1
    E8 %In_Set                  ; CALL32 %In_Set              ; Check for terminators
    83F8 00                     ; CMPI8_EAX !0                ; Check for "\n\t "
    0F84 %Store_Atom_loop       ; JE32 %Store_Atom_loop       ; Loop otherwise

    5A                          ; POP_EDX                     ; Restore EDX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    89D0                        ; COPY_EDX_to_EAX             ; return HEAD
    C3                          ; RET


;; In_Set function
;; Receives Char C in EAX and CHAR* in EBX
;; Returns 1 if true, zero if false in EAX
:In_Set
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
:In_Set_loop
    8A0B                        ; LOAD8_cl_from_EBX           ; Read char
    0FB6C9                      ; MOVZX_cl                    ; Zero extend it

    39C8                        ; CMP_EAX_ECX                 ; See if they match
    0F84 %In_Set_True           ; JE32 %In_Set_True           ; return true

    83F9 00                     ; CMPI8_ECX !0                ; Check for NULL
    0F84 %In_Set_False          ; JE32 %In_Set_False          ; return false

    83C3 01                     ; ADDI8_EBX !1                ; s = s + 1
    E9 %In_Set_loop             ; JMP32 %In_Set_loop          ; Keep looping

:In_Set_True
    B8 01000000                 ; LOADI32_EAX %1              ; Set True
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    C3                          ; RET

:In_Set_False
    B8 00000000                 ; LOADI32_EAX %0              ; Set FALSE
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    C3                          ; RET

;; Char sets
:terminators
0A092000                        ; "\n\t "

:comments
3B2300                          ; ";#"

:string_char
22 27 00                        ; "\"\'"


;; Reverse_List function
;; Receives List in EAX
;; Returns the list reversed in EAX
:Reverse_List
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    89C3                        ; COPY_EAX_to_EBX             ; Set HEAD
    B8 00000000                 ; LOADI32_EAX %0              ; ROOT = NULL
:Reverse_List_Loop
    83FB 00                     ; CMPI8_EBX !0                ; WHILE HEAD != NULL
    0F84 %Reverse_List_Done     ; JE32 %Reverse_List_Done     ; Stop otherwise

    8B0B                        ; LOAD32_ECX_from_EBX         ; NEXT = HEAD->NEXT
    8903                        ; STORE32_EAX_into_Address_EBX ; HEAD->NEXT = ROOT
    89D8                        ; COPY_EBX_to_EAX             ; ROOT = HEAD
    89CB                        ; COPY_ECX_to_EBX             ; HEAD = NEXT
    E9 %Reverse_List_Loop       ; JMP32 %Reverse_List_Loop    ; Keep Going

:Reverse_List_Done
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    C3                          ; RET


;; Identify_Macros function
;; Receives List in EAX
;; Updates the list in place; does not modify registers
;; Uses EBX for DEFINE, ECX for I
:Identify_Macros
    50                          ; PUSH_EAX                    ; Protect EAX
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    52                          ; PUSH_EDX                    ; Protect EDX
    BB &DEFINE_str              ; LOADI32_EBX &DEFINE_str     ; Setup define string
    89C1                        ; COPY_EAX_to_ECX             ; I = HEAD
:Identify_Macros_Loop
    8B41 10                     ; LOAD32_EAX_from_ECX_Immediate8 !16 ; I->TEXT
    E8 %match                   ; CALL32 %match               ; IF "DEFINE" == I->TEXT
    83F8 00                     ; CMPI8_EAX !0                ; Check if match
    0F85 %Identify_Macros_Next  ; JNE32 %Identify_Macros_Next ; Skip the work

    ;; Deal with MACRO
    B8 01000000                 ; LOADI32_EAX %1              ; Using MACRO
    8941 08                     ; STORE32_EAX_into_Address_ECX_Immediate8 !8 ; I->TYPE = MACRO

    8B01                        ; LOAD32_EAX_from_ECX         ; I->NEXT
    8B40 10                     ; LOAD32_EAX_from_EAX_Immediate8 !16 ; I->NEXT->TEXT
    8941 10                     ; STORE32_EAX_into_Address_ECX_Immediate8 !16 ; I->TEXT = I->NEXT->TEXT

    8B01                        ; LOAD32_EAX_from_ECX         ; I->NEXT
    8B00                        ; LOAD32_EAX_from_EAX         ; I->NEXT->NEXT
    8B40 10                     ; LOAD32_EAX_from_EAX_Immediate8 !16 ; I->NEXT->NEXT->TEXT
    8941 18                     ; STORE32_EAX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = I->NEXT->NEXT->TEXT

    8B01                        ; LOAD32_EAX_from_ECX         ; I->NEXT
    8B00                        ; LOAD32_EAX_from_EAX         ; I->NEXT->NEXT
    8B00                        ; LOAD32_EAX_from_EAX         ; I->NEXT->NEXT->NEXT
    8901                        ; STORE32_EAX_into_Address_ECX ; I->NEXT = I->NEXT->NEXT->NEXT

:Identify_Macros_Next
    8B09                        ; LOAD32_ECX_from_ECX         ; I = I->NEXT
    83F9 00                     ; CMPI8_ECX !0                ; Check for NULL
    0F85 %Identify_Macros_Loop  ; JNE32 %Identify_Macros_Loop ; Keep looping otherwise

    5A                          ; POP_EDX                     ; Restore EDX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    58                          ; POP_EAX                     ; Restore EAX
    C3                          ; RET

:DEFINE_str
444546494E4500                  ; "DEFINE"


;; match function
;; Receives CHAR* in EAX and CHAR* in EBX
;; Returns 0 (TRUE) or 1 (FALSE) in EAX
:match
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    52                          ; PUSH_EDX                    ; Protect EDX
    89C1                        ; COPY_EAX_to_ECX             ; S1 in place
    89DA                        ; COPY_EBX_to_EDX             ; S2 in place
:match_Loop
    8A01                        ; LOAD8_al_from_ECX           ; S1[0]
    0FB6C0                      ; MOVZX_al                    ; Make it useful
    8A1A                        ; LOAD8_bl_from_EDX           ; S2[0]
    0FB6DB                      ; MOVZX_bl                    ; Make it useful
    39D8                        ; CMP_EAX_EBX                 ; See if they match
    0F85 %match_False           ; JNE32 %match_False          ; If not

    83C1 01                     ; ADDI8_ECX !1                ; S1 = S1 + 1
    83C2 01                     ; ADDI8_EDX !1                ; S2 = S2 + 1
    83F8 00                     ; CMPI8_EAX !0                ; If reached end of string
    0F84 %match_Done            ; JE32 %match_Done            ; Perfect match
    E9 %match_Loop              ; JMP32 %match_Loop           ; Otherwise keep looping

:match_False
    B8 01000000                 ; LOADI32_EAX %1              ; Return false
:match_Done
    5A                          ; POP_EDX                     ; Restore EDX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    C3                          ; RET


;; Line_Macro function
;; Receives List in EAX
;; Updates the list in place; does not modify registers
;; Uses EAX for I, EBX for I->TEXT, ECX for I->EXPRESSION
:Line_Macro
    50                          ; PUSH_EAX                    ; Protect EAX
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    52                          ; PUSH_EDX                    ; Protect EDX
:Line_Macro_Loop
    8B58 08                     ; LOAD32_EBX_from_EAX_Immediate8 !8 ; I->TYPE
    83FB 01                     ; CMPI8_EBX !1                ; IF MACRO == I->TYPE
    0F85 %Line_Macro_Next       ; JNE32 %Line_Macro_Next      ; Otherwise move on

    ;; Is a macro apply
    8B58 10                     ; LOAD32_EBX_from_EAX_Immediate8 !16 ; I->TEXT
    8B48 18                     ; LOAD32_ECX_from_EAX_Immediate8 !24 ; I->EXPRESSION
    8B00                        ; LOAD32_EAX_from_EAX         ; I->NEXT
    E8 %Set_Expression          ; CALL32 %Set_Expression      ; Apply it
    E9 %Line_Macro_Loop         ; JMP32 %Line_Macro_Loop      ; Move on to next

:Line_Macro_Next
    8B00                        ; LOAD32_EAX_from_EAX         ; I->NEXT
    83F8 00                     ; CMPI8_EAX !0                ; Check for NULL
    0F85 %Line_Macro_Loop       ; JNE32 %Line_Macro_Loop      ; Keep going

    5A                          ; POP_EDX                     ; Restore EDX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    58                          ; POP_EAX                     ; Restore EAX
    C3                          ; RET


;; Set_Expression function
;; Receives List in EAX, CHAR* in EBX and CHAR* in ECX
;; Updates the list in place; does not modify registers
;; Uses EBX for C, ECX for EXP and EDX for I
:Set_Expression
    50                          ; PUSH_EAX                    ; Protect EAX
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    52                          ; PUSH_EDX                    ; Protect EDX
    89C2                        ; COPY_EAX_to_EDX             ; Set I
:Set_Expression_Loop
    8B42 08                     ; LOAD32_EAX_from_EDX_Immediate8 !8 ; I->TYPE
    83F8 01                     ; CMPI8_EAX !1                ; IF MACRO == I->TYPE
    0F84 %Set_Expression_Next   ; JE32 %Set_Expression_Next   ; Ignore and move on

    8B42 10                     ; LOAD32_EAX_from_EDX_Immediate8 !16 ; I->TEXT
    E8 %match                   ; CALL32 %match               ; Check for match
    83F8 00                     ; CMPI8_EAX !0                ; If match
    0F85 %Set_Expression_Next   ; JNE32 %Set_Expression_Next  ; Otherwise next

    ;; We have a non-macro match
    894A 18                     ; STORE32_ECX_into_Address_EDX_Immediate8 !24 ; I->EXPRESSION = EXP

:Set_Expression_Next
    8B12                        ; LOAD32_EDX_from_EDX         ; I = I->NEXT
    83FA 00                     ; CMPI8_EDX !0                ; IF NULL == I
    0F85 %Set_Expression_Loop   ; JNE32 %Set_Expression_Loop  ; Otherwise keep looping

    5A                          ; POP_EDX                     ; Restore EDX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    58                          ; POP_EAX                     ; Restore EAX
    C3                          ; RET


;; Process_String function
;; Receives List in EAX
;; Update the list in place; does not modify registers
;; Uses EBX for I->TEXT, ECX for I and EDX for S
:Process_String
    50                          ; PUSH_EAX                    ; Protect EAX
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    52                          ; PUSH_EDX                    ; Protect EDX
    89C1                        ; COPY_EAX_to_ECX             ; I = HEAD
:Process_String_loop
    8B41 08                     ; LOAD32_EAX_from_ECX_Immediate8 !8 ; I->TYPE
    83F8 02                     ; CMPI8_EAX !2                ; IF STRING == I->TYPE
    0F85 %Process_String_Next   ; JNE32 %Process_String_Next  ; Skip to next

    8B59 10                     ; LOAD32_EBX_from_ECX_Immediate8 !16 ; I->TEXT
    8A03                        ; LOAD8_al_from_EBX           ; I->TEXT[0]
    0FB6C0                      ; MOVZX_al                    ; make it useful
    83F8 27                     ; CMPI8_EAX !39               ; IF '\'' == I->TEXT[0]
    0F85 %Process_String_Raw    ; JNE32 %Process_String_Raw   ; Deal with '\"'

    ;; Deal with '\''
    83C3 01                     ; ADDI8_EBX !1                ; I->TEXT + 1
    8959 18                     ; STORE32_EBX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = I->TEXT + 1
    E9 %Process_String_Next     ; JMP32 %Process_String_Next  ; Move on to next

:Process_String_Raw
    89D8                        ; COPY_EBX_to_EAX             ; Get length of I->TEXT
    E8 %string_length           ; CALL32 %string_length       ; Do it
    C1E8 02                     ; SHRI8_EAX !2                ; LENGTH = LENGTH >> 2
    83C0 01                     ; ADDI8_EAX !1                ; LENGTH = LENGTH + 1
    C1E0 03                     ; SHLI8_EAX !3                ; LENGTH = LENGTH << 3
    E8 %malloc                  ; CALL32 %malloc              ; Get string
    89DA                        ; COPY_EBX_to_EDX             ; S = I->TEXT
    83C2 01                     ; ADDI8_EDX !1                ; S = S + 1
    8941 18                     ; STORE32_EAX_into_Address_ECX_Immediate8 !24 ; I->EXPRESSION = hexify
    89C3                        ; COPY_EAX_to_EBX             ; Put hexify buffer in ebx

:Process_String_Raw_Loop
    8A02                        ; LOAD8_al_from_EDX           ; Read 1 chars
    0FB6C0                      ; MOVZX_al                    ; Make it useful
    83C2 01                     ; ADDI8_EDX !1                ; S = S + 1
    3C 00                       ; CMPI8_AL !0                 ; Check for NULL
    9C                          ; PUSH_FLAGS                  ; Protect condition
    E8 %hex8                    ; CALL32 %hex8                ; write them all
    9D                          ; POP_FLAGS                   ; restore condition
    0F85 %Process_String_Raw_Loop ; JNE32 %Process_String_Raw_Loop ; Keep looping

:Process_String_Next
    8B09                        ; LOAD32_ECX_from_ECX         ; I = I->NEXT
    83F9 00                     ; CMPI8_ECX !0                ; IF NULL == I
    0F85 %Process_String_loop   ; JNE32 %Process_String_loop  ; Otherwise keep looping

    5A                          ; POP_EDX                     ; Restore EDX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    58                          ; POP_EAX                     ; Restore EAX
    C3                          ; RET


;; string_length function
;; Receives CHAR* in EAX
;; Returns INT in EAX
;; Uses EAX for CH, EBX for S and ECX for INDEX
:string_length
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    89C3                        ; COPY_EAX_to_EBX             ; Set S
    B9 00000000                 ; LOADI32_ECX %0              ; INDEX = 0
:string_length_loop
    8A040B                      ; LOAD8_al_from_EBX_indexed_ECX ; S[0]
    0FB6C0                      ; MOVZX_al                    ; make it useful
    83F8 00                     ; CMPI8_EAX !0                ; IF NULL == S[0]
    0F84 %string_length_done    ; JE32 %string_length_done    ; Stop

    83C1 01                     ; ADDI8_ECX !1                ; INDEX = INDEX + 1
    E9 %string_length_loop      ; JMP32 %string_length_loop   ; Keep going

:string_length_done
    89C8                        ; COPY_ECX_to_EAX             ; RETURN INDEX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    C3                          ; RET


;; Eval_Immediates function
;; Receives List in EAX
;; Updates the list in place; does not modify registers
;; Uses EBX for I->TEXT[0], ECX for I->TEXT[1] and EDX for I
:Eval_Immediates
    50                          ; PUSH_EAX                    ; Protect EAX
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    52                          ; PUSH_EDX                    ; Protect EDX
    89C2                        ; COPY_EAX_to_EDX             ; I = HEAD
:Eval_Immediates_Loop
    ;; Check for MACRO
    8B42 08                     ; LOAD32_EAX_from_EDX_Immediate8 !8 ; I->TYPE
    83F8 01                     ; CMPI8_EAX !1                ; IF MACRO == I-TYPE
    0F84 %Eval_Immediates_Next  ; JE32 %Eval_Immediates_Next  ; Skip to next

    ;; Check for NULL EXPRESSION
    8B42 18                     ; LOAD32_EAX_from_EDX_Immediate8 !24 ; I->EXPRESSION
    83F8 00                     ; CMPI8_EAX !0                ; IF NULL == I->EXPRESSION
    0F85 %Eval_Immediates_Next  ; JNE32 %Eval_Immediates_Next ; Skip to next

    ;; Check if number
    8B42 10                     ; LOAD32_EAX_from_EDX_Immediate8 !16 ; I->TEXT
    8A18                        ; LOAD8_bl_from_EAX           ; I->TEXT[0]
    0FB6DB                      ; MOVZX_bl                    ; Extend to use
    83C0 01                     ; ADDI8_EAX !1                ; I->TEXT + 1
    8A08                        ; LOAD8_cl_from_EAX           ; I->TEXT[1]
    0FB6C9                      ; MOVZX_cl                    ; Extend to use
    E8 %numerate_string         ; CALL32 %numerate_string     ; Convert string to INT
    83F8 00                     ; CMPI8_EAX !0                ; IF 0 == numerate_number(I->TEXT + 1)
    0F85 %Eval_Immediates_value ; JNE32 %Eval_Immediates_value ; Has a value

    ;; Last chance for Immediate
    83F9 30                     ; CMPI8_ECX !48               ; If '0' == I->TEXT[1]
    0F85 %Eval_Immediates_Next  ; JNE32 %Eval_Immediates_Next ; Skip to next

:Eval_Immediates_value
    E8 %express_number          ; CALL32 %express_number      ; Convert value to hex string
    8942 18                     ; STORE32_EAX_into_Address_EDX_Immediate8 !24 ; I->EXPRESSION = express_number(value, I-TEXT[0])

:Eval_Immediates_Next
    8B12                        ; LOAD32_EDX_from_EDX         ; I = I->NEXT
    83FA 00                     ; CMPI8_EDX !0                ; IF NULL == I
    0F85 %Eval_Immediates_Loop  ; JNE32 %Eval_Immediates_Loop ; Otherwise keep looping

    5A                          ; POP_EDX                     ; Restore EDX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    58                          ; POP_EAX                     ; Restore EAX
    C3                          ; RET


;; numerate_string function
;; Receives CHAR* in EAX
;; Returns value of CHAR* in EAX
;; Uses EAX for VALUE, EBX for S, ECX for CH and EDI for NEGATIVE?
:numerate_string
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    52                          ; PUSH_EDX                    ; Protect EDX
    57                          ; PUSH_EDI                    ; Protect EDI
    89C3                        ; COPY_EAX_to_EBX             ; put S in correct place
    B8 00000000                 ; LOADI32_EAX %0              ; Initialize to Zero
:numerate_string_loop
    8A4B 01                     ; LOAD8_cl_from_EBX_Immediate8 !1 ; S[1]
    0FB6C9                      ; MOVZX_cl                    ; make it useful
    83F9 78                     ; CMPI8_ECX !120              ; IF 'x' == S[1]
    0F84 %numerate_hex          ; JE32 %numerate_hex          ; Deal with hex input

    ;; Assume decimal input
    B9 00000000                 ; LOADI32_ECX %0              ; Assume no negation
    8A0B                        ; LOAD8_cl_from_EBX           ; S[0]
    0FB6C9                      ; MOVZX_cl                    ; make it useful
    83F9 2D                     ; CMPI8_ECX !45               ; IF '-' == S[0]
    0F85 %numerate_decimal      ; JNE32 %numerate_decimal     ; Skip negation

    BF 01000000                 ; LOADI32_EDI %1              ; Set FLAG
    83C3 01                     ; ADDI8_EBX !1                ; S = S + 1

:numerate_decimal
    8A0B                        ; LOAD8_cl_from_EBX           ; S[0]
    0FB6C9                      ; MOVZX_cl                    ; make it useful
    83F9 00                     ; CMPI8_ECX !0                ; IF NULL == S[0]
    0F84 %numerate_decimal_done ; JE32 %numerate_decimal_done ; We are done

    6BC0 0A                     ; IMULI8_EAX !10              ; VALUE = VALUE * 10
    83E9 30                     ; SUBI8_ECX !48               ; CH = CH - '0'
    83F9 09                     ; CMPI8_ECX !9                ; Check for illegal
    0F8F %numerate_string_fail  ; JG32 %numerate_string_fail  ; If CH > '9'
    83F9 00                     ; CMPI8_ECX !0                ; Check for illegal
    0F8C %numerate_string_fail  ; JL32 %numerate_string_fail  ; IF CH < 0
    01C8                        ; ADD_ECX_to_EAX              ; VALUE = VALUE + CH
    83C3 01                     ; ADDI8_EBX !1                ; S = S + 1
    E9 %numerate_decimal        ; JMP32 %numerate_decimal     ; Keep looping

:numerate_decimal_done
    83FF 01                     ; CMPI8_EDI !1                ; Check if need to negate
    0F85 %numerate_string_done  ; JNE32 %numerate_string_done ; Nope

    6BC0 FF                     ; IMULI8_EAX !-1              ; VALUE = VALUE * -1
    E9 %numerate_string_done    ; JMP32 %numerate_string_done ; Done

:numerate_hex
    83C3 02                     ; ADDI8_EBX !2                ; S = S + 2
:numerate_hex_loop
    8A0B                        ; LOAD8_cl_from_EBX           ; S[0]
    0FB6C9                      ; MOVZX_cl                    ; make it useful
    83F9 00                     ; CMPI8_ECX !0                ; IF NULL == S[0]
    0F84 %numerate_string_done  ; JE32 %numerate_string_done  ; We are done

    C1E0 04                     ; SHLI8_EAX !4                ; VALUE = VALUE << 4
    83E9 30                     ; SUBI8_ECX !48               ; CH = CH - '0'
    83F9 0A                     ; CMPI8_ECX !10               ; IF 10 >= CH
    0F8C %numerate_hex_digit    ; JL32 %numerate_hex_digit    ; NO
    83E9 07                     ; SUBI8_ECX !7                ; Push A-F into range
:numerate_hex_digit
    83F9 0F                     ; CMPI8_ECX !15               ; Check for illegal
    0F8F %numerate_string_fail  ; JG32 %numerate_string_fail  ; If CH > 'F'
    83F9 00                     ; CMPI8_ECX !0                ; Check for illegal
    0F8C %numerate_string_fail  ; JL32 %numerate_string_fail  ; IF CH < 0
    01C8                        ; ADD_ECX_to_EAX              ; VALUE = VALUE + CH
    83C3 01                     ; ADDI8_EBX !1                ; S = S + 1
    E9 %numerate_hex_loop       ; JMP32 %numerate_hex_loop    ; Keep looping

:numerate_string_fail
    B8 00000000                 ; LOADI32_EAX %0              ; return ZERO

:numerate_string_done
    5F                          ; POP_EDI                     ; Restore EDI
    5A                          ; POP_EDX                     ; Restore EDX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    C3                          ; RET


;; express_number function
;; Receives INT in EAX and CHAR in EBX
;; Allocates a string and expresses the value in hex
;; Returns string in EAX
;; Uses EAX for VALUE, EBX for S and ECX for CH
:express_number
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    52                          ; PUSH_EDX                    ; Protect EDX
    89D9                        ; COPY_EBX_to_ECX             ; Put CH in right place
    89C3                        ; COPY_EAX_to_EBX             ; Protect VALUE
    83F9 25                     ; CMPI8_ECX !37               ; IF '%' == CH
    0F85 %express_number2       ; JNE32 %express_number2      ; Otherwise try @

    B8 09000000                 ; LOADI32_EAX %9              ; We need 3bytes
    E8 %malloc                  ; CALL32 %malloc              ; Get S pointer
    93                          ; XCHG_EAX_EBX                ; Put S and VALUE in place
    53                          ; PUSH_EBX                    ; Protect S
    E8 %hex32l                  ; CALL32 %hex32l              ; Store 32bits
    E9 %express_number_done     ; JMP32 %express_number_done  ; done

:express_number2
    83F9 40                     ; CMPI8_ECX !64               ; IF '@' == CH
    0F85 %express_number1       ; JNE32 %express_number1      ; Othrewise try !

    B8 05000000                 ; LOADI32_EAX %5              ; We need 3bytes
    E8 %malloc                  ; CALL32 %malloc              ; Get S pointer
    93                          ; XCHG_EAX_EBX                ; Put S and VALUE in place
    53                          ; PUSH_EBX                    ; Protect S
    E8 %hex16l                  ; CALL32 %hex16l              ; Store 16bits
    E9 %express_number_done     ; JMP32 %express_number_done  ; done

:express_number1
    B8 03000000                 ; LOADI32_EAX %3              ; We need 3bytes
    E8 %malloc                  ; CALL32 %malloc              ; Get S pointer
    93                          ; XCHG_EAX_EBX                ; Put S and VALUE in place
    53                          ; PUSH_EBX                    ; Protect S
    E8 %hex8                    ; CALL32 %hex8                ; Store 8bit

:express_number_done
    58                          ; POP_EAX                     ; Restore S
    5A                          ; POP_EDX                     ; Restore EDX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    C3                          ; RET


;; HEX to ascii routine
;; Receives INT in EAX and CHAR* in EBX
;; Stores ascii of INT in CHAR*
;; Returns only modifying EAX
:hex64l
    50                          ; PUSH_EAX                    ; Protect top 32
    E8 %hex32l                  ; CALL32 %hex32l              ; Store it
    58                          ; POP_EAX                     ; do top 32
    C1E8 20                     ; SHRI8_EAX !32               ; do bottom 32 first
:hex32l
    50                          ; PUSH_EAX                    ; Protect top 16
    E8 %hex16l                  ; CALL32 %hex16l              ; Store it
    58                          ; POP_EAX                     ; do top 16
    C1E8 10                     ; SHRI8_EAX !16               ; do bottom 16 first
:hex16l
    50                          ; PUSH_EAX                    ; Protect top byte
    E8 %hex8                    ; CALL32 %hex8                ; Store it
    58                          ; POP_EAX                     ; do high byte
    C1E8 08                     ; SHRI8_EAX !8                ; do bottom byte first
:hex8
    50                          ; PUSH_EAX                    ; Protect bottom nibble
    C1E8 04                     ; SHRI8_EAX !4                ; do high nibble first
    E8 %hex4                    ; CALL32 %hex4                ; Store it
    58                          ; POP_EAX                     ; do low nibble
:hex4
    83E0 0F                     ; ANDI8_EAX !0xF              ; isolate nibble
    04 30                       ; ADDI8_AL !48                ; convert to ascii
    3C 39                       ; CMPI8_AL !57                ; valid digit?
    0F86 %hex1                  ; JBE32 %hex1                 ; yes
    04 07                       ; ADDI8_AL !7                 ; use alpha range
:hex1
    8803                        ; STORE8_al_into_Address_EBX  ; store result
    83C3 01                     ; ADDI8_EBX !1                ; next position
    C3                          ; RET


;; Preserve_Other function
;; Receives List in EAX
;; Updates the list in place; does not modify registers
;; Uses EAX for I, EBX for I->TEXT
:Preserve_Other
    50                          ; PUSH_EAX                    ; Protect EAX
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    52                          ; PUSH_EDX                    ; Protect EDX
:Preserve_Other_Loop
    8B58 18                     ; LOAD32_EBX_from_EAX_Immediate8 !24 ; I->EXPRESSION
    83FB 00                     ; CMPI8_EBX !0                ; IF NULL == I->EXPRESSION
    0F85 %Preserve_Other_Next   ; JNE32 %Preserve_Other_Next  ; Otherwise next

    ;; Needs preserving
    8B58 10                     ; LOAD32_EBX_from_EAX_Immediate8 !16 ; I->TEXT
    8958 18                     ; STORE32_EBX_into_Address_EAX_Immediate8 !24 ; I->EXPRESSION = I->TEXT

:Preserve_Other_Next
    8B00                        ; LOAD32_EAX_from_EAX         ; I = I->NEXT
    83F8 00                     ; CMPI8_EAX !0                ; IF NULL == I
    0F85 %Preserve_Other_Loop   ; JNE32 %Preserve_Other_Loop  ; Otherwise keep looping

    5A                          ; POP_EDX                     ; Restore EDX
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    58                          ; POP_EAX                     ; Restore EAX
    C3                          ; RET


;; Print_Hex function
;; Receives list in EAX
;; walks the list and prints the I->EXPRESSION for all nodes followed by newline
;; Uses EBX for I
:Print_Hex
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    89EB                        ; COPY_EBP_to_EBX             ; I = Head
:Print_Hex_Loop
    8B43 08                     ; LOAD32_EAX_from_EBX_Immediate8 !8 ; I->TYPE
    83F8 01                     ; CMPI8_EAX !1                ; IF MACRO == I->TYPE
    0F84 %Print_Hex_Next        ; JE32 %Print_Hex_Next        ; Skip

    8B43 18                     ; LOAD32_EAX_from_EBX_Immediate8 !24 ; Using EXPRESSION
    E8 %File_Print              ; CALL32 %File_Print          ; Print it
    B8 0A000000                 ; LOADI32_EAX %10             ; NEWLINE
    E8 %fputc                   ; CALL32 %fputc               ; Append it

:Print_Hex_Next
    8B1B                        ; LOAD32_EBX_from_EBX         ; Iterate to next Token
    83FB 00                     ; CMPI8_EBX !0                ; Check for NULL
    0F85 %Print_Hex_Loop        ; JNE32 %Print_Hex_Loop       ; Otherwise keep looping

    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    C3                          ; RET


;; File_Print function
;; Receives CHAR* in EAX
;; calls fputc for every non-null char
:File_Print
    53                          ; PUSH_EBX                    ; Protect EBX
    51                          ; PUSH_ECX                    ; Protect ECX
    89C3                        ; COPY_EAX_to_EBX             ; Protect S
    83F8 00                     ; CMPI8_EAX !0                ; Protect against nulls
    0F84 %File_Print_Done       ; JE32 %File_Print_Done       ; Simply don't try to print them
:File_Print_Loop
    8A03                        ; LOAD8_al_from_EBX           ; Read byte
    0FB6C0                      ; MOVZX_al                    ; zero extend
    83F8 00                     ; CMPI8_EAX !0                ; Check for NULL
    0F84 %File_Print_Done       ; JE32 %File_Print_Done       ; Stop at NULL

    E8 %fputc                   ; CALL32 %fputc               ; write it
    83C3 01                     ; ADDI8_EBX !1                ; S = S + 1
    E9 %File_Print_Loop         ; JMP32 %File_Print_Loop      ; Keep going

:File_Print_Done
    59                          ; POP_ECX                     ; Restore ECX
    5B                          ; POP_EBX                     ; Restore EBX
    C3                          ; RET


;; fputc function
;; receives CHAR in EAX and FILE* in [Output]
;; writes char and returns
:fputc
    52                          ; PUSH_EDX                    ; Protect EDX
    51                          ; PUSH_ECX                    ; protect ECX
    53                          ; PUSH_EBX                    ; protect EBX
    50                          ; PUSH_EAX                    ; We are writing eax
    8D0C24                      ; LEA32_ECX_from_esp          ; Get stack address
    8B1D &Output                ; LOAD32_Absolute32_ebx &Output ; Write to target file
    B8 04000000                 ; LOADI32_EAX %4              ; the syscall number for write
    BA 01000000                 ; LOADI32_EDX %1              ; set the size of chars we want
    CD80                        ; INT_80                      ; call the Kernel
    58                          ; POP_EAX                     ; Restore stack
    5B                          ; POP_EBX                     ; Restore EBX
    59                          ; POP_ECX                     ; Restore ECX
    5A                          ; POP_EDX                     ; Restore EDX
    C3                          ; RET

:Output
    00000000                    ; NULL
:Input
    00000000                    ; NULL
:ELF_end

https://github.com/oriansj/stage0-posix-x86/blob/main/cc_x86.M1

File: ../git/live-bootstrap/sysa/stage0-posix/src/x86/cc_x86.M1 used in: Step 10
;; Copyright (C) 2017 Jeremiah Orians
;; This file is part of stage0.
;;
;; stage0 is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; stage0 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 stage0.  If not, see <http://www.gnu.org/licenses/>.

DEFINE ADDI8_EAX 83C0
DEFINE ADDI8_EBX 83C3
DEFINE ADDI8_ECX 83C1
DEFINE ADDI8_EDX 83C2
DEFINE ADDI8_ESI 83C6
DEFINE ADD_eax_into_ebx 01C3
DEFINE ADD_ebx_into_eax 01D8
DEFINE ADD_ecx_into_eax 01C8
DEFINE ADD_edi_into_ecx 01F9
DEFINE AND_EAX_EBX 21D8
DEFINE ANDI32_EAX 25
DEFINE CALL_EAX FFD0
DEFINE CALL32 E8
DEFINE CMPI8_EAX 83F8
DEFINE CMPI8_EBP 83fd
DEFINE CMPI8_EBX 83FB
DEFINE CMPI8_ECX 83F9
DEFINE CMPI8_EDX 83FA
DEFINE CMPI8_ESI 83FE
DEFINE CMP_EAX_EBX 39D8
DEFINE CMP_EAX_ECX 39C8
DEFINE CMP_EBX_ECX 39D9
DEFINE CMP_ECX_EBX 39CB
DEFINE CMP_EDI_ESI 39FE
DEFINE CMP_EBX_EDX 39D3
DEFINE COPY_EAX_to_EBP 89C5
DEFINE COPY_EAX_to_EBX 89C3
DEFINE COPY_EAX_to_ECX 89C1
DEFINE COPY_EAX_to_EDX 89C2
DEFINE COPY_EAX_to_ESI 89C6
DEFINE COPY_EBP_to_EAX 89E8
DEFINE COPY_EBX_to_EAX 89D8
DEFINE COPY_EBX_to_ECX 89D9
DEFINE COPY_EBX_to_EDX 89DA
DEFINE COPY_ECX_to_EAX 89C8
DEFINE COPY_ECX_to_EBX 89CB
DEFINE COPY_EDI_to_ESI 89FE
DEFINE COPY_EDX_to_EAX 89D0
DEFINE COPY_EDX_to_EBP 89D5
DEFINE COPY_EDX_to_EBX 89D3
DEFINE COPY_ESI_to_EAX 89F0
DEFINE COPY_ESI_to_EDI 89F7
DEFINE IDIV_EBX F7FB
DEFINE IMUL_EAX_by_EBX 0FAFC3
DEFINE IMULI8_EAX 6BC0
DEFINE IMULI8_EBP 6BED
DEFINE INT_80 CD80
DEFINE JBE8 76
DEFINE JE32 0F84
DEFINE JG32 0F8F
DEFINE JG8 7F
DEFINE JL32 0F8C
DEFINE JLE32 0F8E
DEFINE JMP32 E9
DEFINE JNE32 0F85
DEFINE LEA32_ECX_from_esp 8D0C24
DEFINE LOAD32_Absolute32_eax A1
DEFINE LOAD32_Absolute32_ebx 8B1D
DEFINE LOAD32_Absolute32_ecx 8B0D
DEFINE LOAD32_EAX_from_EAX 8B00
DEFINE LOAD32_EAX_from_EAX_Immediate8 8B40
DEFINE LOAD32_EAX_from_EBP_Immediate8 8B45
DEFINE LOAD32_EAX_from_EBX 8B03
DEFINE LOAD32_EAX_from_EBX_Immediate8 8B43
DEFINE LOAD32_EAX_from_ECX_Immediate8 8B41
DEFINE LOAD32_EAX_from_EDX_Immediate8 8B42
DEFINE LOAD32_EBP_from_EBP 8B6D00
DEFINE LOAD32_EBX_from_EAX_Immediate8 8B58
DEFINE LOAD32_EBX_from_EBX 8B1B
DEFINE LOAD32_EBX_from_EBX_Immediate8 8B5B
DEFINE LOAD32_EBX_from_ECX_Immediate8 8B59
DEFINE LOAD32_ECX_from_EAX_Immediate8 8B48
DEFINE LOAD32_ECX_from_EBX 8B0B
DEFINE LOAD32_ECX_from_ECX 8B09
DEFINE LOAD32_ECX_from_ECX_Immediate8 8B49
DEFINE LOAD32_ECX_from_EDX_Immediate8 8B4A
DEFINE LOAD32_EDI_from_EDX_Immediate8 8B7A
DEFINE LOAD32_EDX_from_EDX_Immediate8 8B52
DEFINE LOAD8_al_from_EAX 8A00
DEFINE LOAD8_al_from_EBX 8A03
DEFINE LOAD8_al_from_ECX 8A01
DEFINE LOAD8_al_from_EDX 8A02
DEFINE LOAD8_bl_from_EBX 8A1B
DEFINE LOAD8_bl_from_ECX 8A19
DEFINE LOAD8_bl_from_EDX 8A1A
DEFINE LOAD8_cl_from_EBX 8A0B
DEFINE LOAD8_cl_from_EBX_Immediate8 8A4B
DEFINE LOADI32_EAX B8
DEFINE LOADI32_EBX BB
DEFINE LOADI32_ECX B9
DEFINE LOADI32_EDI BF
DEFINE LOADI32_EDX BA
DEFINE LOADI32_ESI BE
DEFINE MOVZX_al 0FB6C0
DEFINE MOVZX_bl 0FB6DB
DEFINE MOVZX_cl 0FB6C9
DEFINE NULL 00000000
DEFINE POP_EAX 58
DEFINE POP_EBP 5D
DEFINE POP_EBX 5B
DEFINE POP_ECX 59
DEFINE POP_EDI 5F
DEFINE POP_EDX 5A
DEFINE POP_ESI 5E
DEFINE PUSH_EAX 50
DEFINE PUSH_EBP 55
DEFINE PUSH_EBX 53
DEFINE PUSH_ECX 51
DEFINE PUSH_EDI 57
DEFINE PUSH_EDX 52
DEFINE PUSH_ESI 56
DEFINE RETURN C3
DEFINE SALI8_EAX C1E0
DEFINE SHRI8_EAX C1E8
DEFINE SHRI8_EBX C1EB
DEFINE STORE32_Absolute32_eax A3
DEFINE STORE32_Absolute32_ebx 891D
DEFINE STORE32_Absolute32_ecx 890D
DEFINE STORE32_Absolute32_edx 8915
DEFINE STORE32_EAX_into_EBP_Immediate8 8945
DEFINE STORE32_EAX_into_EBX 8903
DEFINE STORE32_EAX_into_ECX_Immediate8 8941
DEFINE STORE32_EAX_into_EDX 8902
DEFINE STORE32_EAX_into_EDX_Immediate8 8942
DEFINE STORE32_EBP_into_EDX_Immediate8 896A
DEFINE STORE32_EBX_into_EAX 8918
DEFINE STORE32_EBX_into_EAX_Immediate8 8958
DEFINE STORE32_EBX_into_EDX_Immediate8 895A
DEFINE STORE32_ECX_into_EAX 8908
DEFINE STORE32_ECX_into_EAX_Immediate8 8948
DEFINE STORE32_ECX_into_EDX_Immediate8 894A
DEFINE STORE32_EDX_into_EAX_Immediate8 8950
DEFINE STORE32_EDX_into_EBP_Immediate8 8955
DEFINE STORE32_ESI_into_EBP_Immedate8 8975
DEFINE STORE32_ESI_into_EDX_Immedate8 8972
DEFINE STORE8_al_into_Address_EBX 8803
DEFINE STORE8_al_into_Address_ECX 8801
DEFINE STORE8_al_into_Address_ESI 8806
DEFINE STORE8_bl_into_Address_ECX 8819
DEFINE SUBI8_EAX 83E8
DEFINE SUBI8_ECX 83E9
DEFINE SUBI8_ESI 83EE
DEFINE SWAP_EAX_EBX 93


    ;; Register usage:
    ;; EAX => Temps

    ;; Struct TYPE format: (size 28)
    ;; NEXT => 0
    ;; SIZE => 4
    ;; OFFSET => 8
    ;; INDIRECT => 12
    ;; MEMBERS => 16
    ;; TYPE => 20
    ;; NAME => 24

    ;; Struct TOKEN_LIST format: (size 20)
    ;; NEXT => 0
    ;; LOCALS/PREV => 4
    ;; S => 8
    ;; TYPE => 12
    ;; ARGS/DEPTH => 16

; Where the ELF Header is going to hit
; Simply jump to _start
; Our main function
:_start
    POP_EAX                                     ; Get the number of arguments
    POP_EBX                                     ; Get the program name
    POP_EBX                                     ; Get the actual input name
    LOADI32_ECX %0                              ; prepare read_only
    LOADI32_EAX %5                              ; the syscall number for open()
    INT_80                                      ; Now open that damn file
    STORE32_Absolute32_eax &Input_file          ; Preserve the file pointer we were given

    POP_EBX                                     ; Get the actual output name
    LOADI32_ECX %577                            ; Prepare file as O_WRONLY|O_CREAT|O_TRUNC
    LOADI32_EDX %384                            ; Prepare file as RW for owner only (600 in octal)
    LOADI32_EAX %5                              ; the syscall number for open()
    INT_80                                      ; Now open that damn file
    CMPI8_EAX !0                                ; Check for missing output
    JG32 %_start_out                            ; Have real input
    LOADI32_EAX %1                              ; Use stdout

:_start_out
    STORE32_Absolute32_eax &Output_file         ; Preserve the file pointer we were given

    LOADI32_EAX %45                             ; the Syscall # for SYS_BRK
    LOADI32_EBX %0                              ; Get current brk
    INT_80                                      ; Let the kernel do the work
    STORE32_Absolute32_eax &MALLOC              ; Set our malloc pointer
    LOADI32_EAX %0                              ; HEAD = NULL
    CALL32 %read_all_tokens                     ; Read all tokens
    CALL32 %Reverse_List                        ; Reverse order
;   CALL32 %debug_list                          ; Try to figure out what is wrong
    STORE32_Absolute32_eax &global_token        ; Set global_token
    CALL32 %program                             ; Convert into program
    LOADI32_EAX &header_string1                 ; Our header string
    CALL32 %File_Print                          ; Print it
    LOAD32_Absolute32_eax &output_list          ; Our output_list
    CALL32 %recursive_output                    ; Print core program
;   LOADI32_EAX &header_string2                 ; Our Enable debug
;   CALL32 %File_Print                          ; Print it
    LOADI32_EAX &header_string3                 ; Our second label
    CALL32 %File_Print                          ; Print it
    LOAD32_Absolute32_eax &globals_list         ; Our globals
    CALL32 %recursive_output                    ; Get them
    LOADI32_EAX &header_string4                 ; Our final header
    CALL32 %File_Print                          ; Print it
    LOAD32_Absolute32_eax &strings_list         ; Our strings
    CALL32 %recursive_output                    ; Get them
    LOADI32_EAX &header_string5                 ; Make this a bare assembly
    CALL32 %File_Print                          ; Print it

:Done
    ; program completed Successfully
    LOADI32_EBX %0                              ; All is well
    LOADI32_EAX %1                              ; put the exit syscall number in eax
    INT_80                                      ; Call it a good day

:header_string1  "
# Core program
"
:header_string2  "
:ELF_data
"
:header_string3  "
# Program global variables
"
:header_string4  "
# Program strings
"
:header_string5  "
:ELF_end
"


;; read_all_tokens function
;; Receives Token_List* in EAX
;; Tokenizes all input and returns updated list in EAX
;; Returns TOKEN in EAX
;; Uses EAX for C
:read_all_tokens
    STORE32_Absolute32_eax &Token
    CALL32 %fgetc
:read_all_tokens_loop
    CMPI8_EAX !-4                               ; Check for EOF
    JE32 %read_all_tokens_done                  ; Stop if found
    CALL32 %get_token                           ; Read all tokens
    JMP32 %read_all_tokens_loop                 ; Loop
:read_all_tokens_done
    LOAD32_Absolute32_eax &Token
    RETURN


;; get_token function
;; Receives INT in EAX
;; Makes a list of TOKEN_LIST
;; C and STRING_INDEX are stored in memory, ECX is used for S and EDX is used for current
;; Returns C in EAX
:get_token
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX

    STORE32_Absolute32_eax &C                   ; Set C

    LOADI32_EAX %20                             ; Malloc CURRENT
    CALL32 %malloc                              ; Get Pointer
    COPY_EAX_to_EDX                             ; Set CURRENT

    LOADI32_EAX %256                            ; Malloc the string
    CALL32 %malloc                              ; Get pointer to S
    COPY_EAX_to_ECX                             ; Set S
    STORE32_ECX_into_EDX_Immediate8 !8          ; CURRENT->S = S
:reset
    STORE32_Absolute32_ecx &string_index        ; S[0]
    LOAD32_Absolute32_eax &C                    ; Using C

    CALL32 %clear_white_space                   ; Clear WhiteSpace
    STORE32_Absolute32_eax &C                   ; Set C

    CMPI8_EAX !-4                               ; Check for EOF
    JE32 %get_token_abort                       ; if EOF abort

    CMPI8_EAX !35                               ; Check for '#'
    JNE32 %get_token_alpha                      ; Nope

    ;; Deal with # line comments
    CALL32 %purge_macro                         ; Let it handle it
    STORE32_Absolute32_eax &C                   ; Set C
    JMP32 %reset                                ; Try again

:get_token_alpha
    LOAD32_Absolute32_eax &C                    ; Send C
    LOADI32_EBX &alphas                         ; Get alphanumerics
    CALL32 %In_Set                              ; See if in set
    CMPI8_EAX !1                                ; IF TRUE
    JNE32 %get_token_symbol                     ; Otherwise

    ;; Store keywords
    LOAD32_Absolute32_eax &C                    ; Send C
    CALL32 %preserve_keyword                    ; Store
    STORE32_Absolute32_eax &C                   ; Set C
    JMP32 %get_token_done                       ; Be done with this token

:get_token_symbol
    LOAD32_Absolute32_eax &C                    ; Send C
    LOADI32_EBX &symbols                        ; Get symbols
    CALL32 %In_Set                              ; See if in set
    CMPI8_EAX !1                                ; IF TRUE
    JNE32 %get_token_strings                    ; Otherwise

    ;; Store symbols
    LOAD32_Absolute32_eax &C                    ; Send C
    CALL32 %preserve_symbol                     ; Store
    STORE32_Absolute32_eax &C                   ; Set C
    JMP32 %get_token_done                       ; Be done with this token

:get_token_strings
    LOAD32_Absolute32_eax &C                    ; Send C
    LOADI32_EBX &strings                        ; Get strings
    CALL32 %In_Set                              ; See if in set
    CMPI8_EAX !1                                ; IF TRUE
    JNE32 %get_token_comment                    ; Otherwise

    ;; Store String
    LOAD32_Absolute32_eax &C                    ; Send C
    CALL32 %consume_word                        ; Store
    STORE32_Absolute32_eax &C                   ; Set C
    JMP32 %get_token_done                       ; Be done with this token

:get_token_comment
    LOAD32_Absolute32_eax &C                    ; Send C
    CMPI8_EAX !47                               ; IF '/' == C
    JNE32 %get_token_else                       ; Otherwise

    CALL32 %consume_byte                        ; Hope it just is '/'
    STORE32_Absolute32_eax &C                   ; Set C

    CMPI8_EAX !42                               ; IF '*' we have '/*'
    JNE32 %get_token_comment_line               ; Check for '//'

    ;; Deal with /* block comments */
    CALL32 %fgetc                               ; get next C
    STORE32_Absolute32_eax &C                   ; Set C
:get_token_comment_block_outer
    LOAD32_Absolute32_eax &C                    ; Using C
    CMPI8_EAX !47                               ; IF '/' != C
    JE32 %get_token_comment_block_done          ; be done

:get_token_comment_block_inner
    LOAD32_Absolute32_eax &C                    ; Using C
    CMPI8_EAX !42                               ; IF '*' != C
    JE32 %get_token_comment_block_iter          ; jump over

    ;; Deal with inner loop
    CALL32 %fgetc                               ; get next C
    STORE32_Absolute32_eax &C                   ; Set C
    JMP32 %get_token_comment_block_inner        ; keep going

:get_token_comment_block_iter
    CALL32 %fgetc                               ; get next C
    STORE32_Absolute32_eax &C                   ; Set C
    JMP32 %get_token_comment_block_outer

:get_token_comment_block_done
    CALL32 %fgetc                               ; get next C
    STORE32_Absolute32_eax &C                   ; Set C
    JMP32 %reset                                ; throw away, try again

:get_token_comment_line
    CMPI8_EAX !47                               ; IF '/' we have //
    JNE32 %get_token_done                       ; keep if just '/'

    ;; Deal with // line comment
    CALL32 %fgetc                               ; drop to match
    STORE32_Absolute32_eax &C                   ; Set C
    JMP32 %reset                                ; throw away, try again

:get_token_else
    LOAD32_Absolute32_eax &C                    ; Send C
    CALL32 %consume_byte
    STORE32_Absolute32_eax &C                   ; Set C

:get_token_done
    LOAD32_Absolute32_eax &Token                ; TOKEN
    STORE32_EAX_into_EDX_Immediate8 !4          ; CURRENT->PREV = TOKEN
    STORE32_EAX_into_EDX                        ; CURRENT->NEXT = TOKEN
    STORE32_Absolute32_edx &Token               ; TOKEN = CURRENT

:get_token_abort
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    LOAD32_Absolute32_eax &C                    ; Return C
    RETURN


;; Malloc isn't actually required if the program being built fits in the initial memory
;; However, it doesn't take much to add it.
;; Requires [MALLOC] to be initialized and EAX to have the number of desired bytes
:malloc
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    LOAD32_Absolute32_ebx &MALLOC               ; Using the current pointer
    ADD_eax_into_ebx                            ; Request the number of desired bytes
    LOADI32_EAX %45                             ; the Syscall # for SYS_BRK
    INT_80                                      ; call the Kernel
    LOAD32_Absolute32_eax &MALLOC               ; Return pointer
    STORE32_Absolute32_ebx &MALLOC              ; Update pointer
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; clear_white_space function
;; Receives INT C in EAX
;; Returns first non-whitespace char in EAX
:clear_white_space
    CMPI8_EAX !32                               ; Check for ' '
    JE32 %clear_white_space_wipe                ; wipe it out

    CMPI8_EAX !10                               ; Check for '\n'
    JE32 %clear_white_space_wipe                ; wipe it output

    CMPI8_EAX !9                                ; Check for '\t'
    JNE32 %clear_white_space_done               ; looks like non-whitespace

:clear_white_space_wipe
    CALL32 %fgetc                               ; Read a new byte
    CMPI8_EAX !-4                               ; Check for EOF
    JE32 %clear_white_space_done                ; Short circuit
    JMP32 %clear_white_space                    ; iterate

:clear_white_space_done
    RETURN


;; In_Set function
;; Receives Char C in EAX and CHAR* in EBX
;; Returns 1 if true, zero if false in EAX
:In_Set
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
:In_Set_loop
    LOAD8_cl_from_EBX                           ; Read char
    MOVZX_cl                                    ; Zero extend it

    CMP_EAX_ECX                                 ; See if they match
    JE32 %In_Set_True                           ; return true

    CMPI8_ECX !0                                ; Check for NULL
    JE32 %In_Set_False                          ; return false

    ADDI8_EBX !1                                ; s = s + 1
    JMP32 %In_Set_loop                          ; Keep looping

:In_Set_True
    LOADI32_EAX %1                              ; Set True
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:In_Set_False
    LOADI32_EAX %0                              ; Set FALSE
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:alphas  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
:symbols  "<=>|&!-"
:strings  '22 27 00'


;; purge_macro function
;; Receives CH in EAX
;; Reads chars until Line feed is read
;; returns line feed
:purge_macro
    CALL32 %fgetc                               ; read next char
    CMPI8_EAX !10                               ; Check for '\n'
    JNE32 %purge_macro                          ; Keep going
    RETURN


;; preserve_keyword function
;; Receives INT C in EAX
;; collects all chars in keyword
;; Returns C in EAX
;; Uses ECX for INT C
:preserve_keyword
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    COPY_EAX_to_ECX                             ; Setup C
    LOADI32_EBX &alphas                         ; Concerning ourselves with "abc.."
:preserve_keyword_loop
    CALL32 %In_Set                              ; Check if alphanumerics
    CMPI8_EAX !1                                ; IF TRUE
    JNE32 %preserve_keyword_label               ; Otherwise check for label

    COPY_ECX_to_EAX                             ; Pass C
    CALL32 %consume_byte                        ; consume that byte
    COPY_EAX_to_ECX                             ; Update C
    JMP32 %preserve_keyword_loop                ; keep looping

:preserve_keyword_label
    COPY_ECX_to_EAX                             ; Fix return
    CMPI8_EAX !58                               ; Check for ':'
    JNE32 %preserve_keyword_done                ; be done

    ;; Fix our goto label
    CALL32 %fixup_label                         ; Fix the label
    LOADI32_EAX %32                             ; Return Whitespace

:preserve_keyword_done
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; preserve_symbol function
;; Receives INT C in EAX
;; collects all chars in symbol
;; Returns C in EAX
;; Uses ECX for INT C
:preserve_symbol
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    COPY_EAX_to_ECX                             ; Setup C
    LOADI32_EBX &symbols                        ; Concerning ourselves with "<=>.."
:preserve_symbol_loop
    CALL32 %In_Set                              ; Check if symbol
    CMPI8_EAX !1                                ; IF TRUE
    JNE32 %preserve_symbol_done                 ; Otherwise be done

    COPY_ECX_to_EAX                             ; Pass C
    CALL32 %consume_byte                        ; consume that byte
    COPY_EAX_to_ECX                             ; Update C
    JMP32 %preserve_symbol_loop                 ; keep looping

:preserve_symbol_done
    COPY_ECX_to_EAX                             ; Fix return
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; consume_word function
;; receives INT C in EAX
;; returns INT C in EAX
;; Uses EAX for C, EBX for FREQ and ECX for ESCAPE
:consume_word
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    COPY_EAX_to_EBX                             ; FREQ = C
    LOADI32_ECX %0                              ; ESCAPE = FALSE
:consume_word_loop
    CMPI8_ECX !0                                ; IF !ESCAPE
    JNE32 %consume_word_escape                  ; Enable escape

    CMPI8_EAX !92                               ; if '\\'
    JNE32 %consume_word_iter                    ; keep state

    LOADI32_ECX %1                              ; ESCAPE = TRUE
    JMP32 %consume_word_iter                    ; keep going

:consume_word_escape
    LOADI32_ECX %0                              ; ESCAPE = FALSE

:consume_word_iter
    CALL32 %consume_byte                        ; read next char

    CMPI8_ECX !0                                ; IF ESCAPE
    JNE32 %consume_word_loop                    ; keep looping

    CMP_EAX_EBX                                 ; IF C != FREQ
    JNE32 %consume_word_loop                    ; keep going

    CALL32 %fgetc                               ; return next char
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; consume_byte function
;; Receives INT C in EAX
;; Inserts C into string S, updates String S
;; Returns Next char in EAX
:consume_byte
    PUSH_EBX                                    ; Protect EBX
    LOAD32_Absolute32_ebx &string_index         ; S[0]
    STORE8_al_into_Address_EBX                  ; S[0] = C
    ADDI8_EBX !1                                ; S = S + 1
    STORE32_Absolute32_ebx &string_index        ; Update S
    CALL32 %fgetc
    POP_EBX                                     ; Restore EBX
    RETURN


;; fixup_label function
;; Receives S in ECX
;; prepends ':' to string and returns registers unchanged
;; Uses EAX for HOLD, EBX for PREV and ECX for S[0]
:fixup_label
    PUSH_EAX                                    ; Protect EAX
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOADI32_EAX %58                             ; HOLD = ':'
    LOAD32_ECX_from_EDX_Immediate8 !8           ; HOLD_STRING[0]
:fixup_label_loop
    COPY_EAX_to_EBX                             ; PREV = HOLD
    LOAD8_al_from_ECX                           ; HOLD = HOLD_STRING[I]
    MOVZX_al                                    ; make useful
    STORE8_bl_into_Address_ECX                  ; HOLD_STRING[I] = PREV
    ADDI8_ECX !1                                ; I = I + 1
    CMPI8_EAX !0                                ; IF NULL == HOLD
    JNE32 %fixup_label_loop                     ; Keep looping

    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    POP_EAX                                     ; Restore EAX
    RETURN


;; fgetc function
;; Loads FILE* from [INPUT_FILE]
;; Returns -4 (EOF) or char in EAX
:fgetc
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    LOADI32_EAX %-4                             ; Put EOF in eax
    PUSH_EAX                                    ; Assume bad (If nothing read, value will remain EOF)
    LEA32_ECX_from_esp                          ; Get stack address
    LOAD32_Absolute32_ebx &Input_file           ; Where are we reading from
    LOADI32_EAX %3                              ; the syscall number for read
    LOADI32_EDX %1                              ; set the size of chars we want
    INT_80                                      ; call the Kernel
    POP_EAX                                     ; Get either char or EOF
    CMPI8_EAX !-4                               ; Check for EOF
    JE32 %fgetc_done                            ; Return as is
    MOVZX_al                                    ; Make it useful
:fgetc_done
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; Reverse_List function
;; Receives List in EAX
;; Returns the list reversed in EAX
:Reverse_List
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    COPY_EAX_to_EBX                             ; Set HEAD
    LOADI32_EAX %0                              ; ROOT = NULL
:Reverse_List_Loop
    CMPI8_EBX !0                                ; WHILE HEAD != NULL
    JE32 %Reverse_List_Done                     ; Stop otherwise

    LOAD32_ECX_from_EBX                         ; NEXT = HEAD->NEXT
    STORE32_EAX_into_EBX                        ; HEAD->NEXT = ROOT
    COPY_EBX_to_EAX                             ; ROOT = HEAD
    COPY_ECX_to_EBX                             ; HEAD = NEXT
    JMP32 %Reverse_List_Loop                    ; Keep Going

:Reverse_List_Done
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; recursive_output function
;; Receives list in EAX
;; walks the list and prints the I->S for all nodes backwards
;; Uses EBX for I
:recursive_output
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    CMPI8_EAX !0                                ; Check for NULL
    JE32 %recursive_output_done                 ; Skip the work
    COPY_EAX_to_EBX                             ; I = Head

    LOAD32_EAX_from_EBX                         ; Iterate to next Token
    CALL32 %recursive_output                    ; Recurse

    LOAD32_EAX_from_EBX_Immediate8 !8           ; Using S
    CALL32 %File_Print                          ; Print it

:recursive_output_done
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; File_Print function
;; Receives CHAR* in EAX
;; calls fputc for every non-null char
:File_Print
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    COPY_EAX_to_EBX                             ; Protect S
    CMPI8_EAX !0                                ; Protect against nulls
    JE32 %File_Print_Done                       ; Simply don't try to print them
:File_Print_Loop
    LOAD8_al_from_EBX                           ; Read byte
    MOVZX_al                                    ; zero extend
    CMPI8_EAX !0                                ; Check for NULL
    JE32 %File_Print_Done                       ; Stop at NULL

    CALL32 %fputc                               ; write it
    ADDI8_EBX !1                                ; S = S + 1
    JMP32 %File_Print_Loop                      ; Keep going

:File_Print_Done
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; fputc function
;; receives CHAR in EAX and load FILE* from [OUTPUT_FILE]
;; writes char and returns
:fputc
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    PUSH_EAX                                    ; We are writing eax
    LEA32_ECX_from_esp                          ; Get stack address
    LOAD32_Absolute32_ebx &Output_file          ; Write to target file
    LOADI32_EAX %4                              ; the syscall number for write
    LOADI32_EDX %1                              ; set the size of chars we want
    INT_80                                      ; call the Kernel
    POP_EAX                                     ; Restore stack
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; program function
;; receives nothing, returns nothing
;; Uses EAX for type_size
:program
    ;; The binary initialized the globals to null, so we can skip those steps
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX

:new_type
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    CMPI8_EAX !0                                ; Check if NULL
    JE32 %program_done                          ; Be done if null

    LOAD32_EBX_from_EAX_Immediate8 !8           ; GLOBAL_TOKEN->S
    LOADI32_EAX &constant                       ; "CONSTANT"
    CALL32 %match                               ; IF GLOBAL_TOKEN->S == "CONSTANT"
    CMPI8_EAX !0                                ; If true
    JNE32 %program_else                         ; Looks like not a constant

    ;; Deal with minimal constant case
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next

    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOADI32_EBX %0                              ; NULL
    LOAD32_Absolute32_ecx &global_constant_list ; global_constant_list
    CALL32 %sym_declare                         ; Declare that constant
    STORE32_Absolute32_eax &global_constant_list ; global_constant_list = sym_declare(global_token->s, NULL, global_constant_list);

    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX                         ; global_token->next
    STORE32_EBX_into_EAX_Immediate8 !16         ; global_constant_list->arguments = global_token->next

    LOAD32_EBX_from_EBX                         ; global_token->next->next
    STORE32_Absolute32_ebx &global_token        ; global_token = global_token->next->next
    JMP32 %new_type                             ; go around again

:program_else
    CALL32 %type_name                           ; Figure out the type_size
    CMPI8_EAX !0                                ; IF NULL == type_size
    JE32 %new_type                              ; it was a new type

    ;; Add to global symbol table
    COPY_EAX_to_EBX                             ; put type_size in the right spot
    LOAD32_Absolute32_eax &global_token         ; Using global token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD32_Absolute32_ecx &global_symbol_list   ; Using global_symbol_list
    CALL32 %sym_declare                         ; Declare symbol
    STORE32_Absolute32_eax &global_symbol_list  ; global_symbol_list = sym_declare(global_token->s, type_size, global_symbol_list);
    LOAD32_Absolute32_ebx &global_token         ; Using global token
    LOAD32_EBX_from_EBX                         ; global_token->next
    STORE32_Absolute32_ebx &global_token        ; global_token = global_token->next

    LOAD32_Absolute32_ebx &global_token         ; Using global token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &semicolon                      ; ";"
    CALL32 %match                               ; if(match(";", global_token->s))
    CMPI8_EAX !0                                ; If true
    JNE32 %program_function                     ; looks like not a match

    ;; Deal with the global variable
    LOAD32_Absolute32_ebx &globals_list         ; Using globals_list
    LOADI32_EAX &program_string_0               ; ":GLOBAL_"
    CALL32 %emit                                ; Emit it
    COPY_EAX_to_EBX                             ; update globals_list

    LOAD32_Absolute32_eax &global_token         ; Using global token
    LOAD32_EAX_from_EAX_Immediate8 !4           ; global token->prev
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global token->prev->s
    CALL32 %emit                                ; Emit it

    COPY_EAX_to_EBX                             ; update globals_list
    LOADI32_EAX &program_string_1               ; "\nNULL\n"
    CALL32 %emit                                ; Emit it
    STORE32_Absolute32_eax &globals_list        ; update globals_list

    LOAD32_Absolute32_eax &global_token         ; Using global token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next
    JMP32 %new_type                             ; go around again

:program_function
    LOAD32_Absolute32_ebx &global_token         ; Using global token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &open_paren                     ; "("
    CALL32 %match                               ; if(match(";", global_token->s))
    CMPI8_EAX !0                                ; If true
    JNE32 %program_error                        ; Otherwise deal with error case

    ;; Deal with function definition
    CALL32 %declare_function                    ; Lets get the parsing rolling
    JMP32 %new_type                             ; Keep looping through functions

:program_error
    ;; Deal with the case of something we don't support
    ;; NOT IMPLEMENTED

:program_done
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

;; Strings needed by the program function
:program_string_0  ":GLOBAL_"
:program_string_1  "
NULL
"


;; declare_function function
;; Receives nothing and returns nothing
;; Sets current function and adds it to the global function list
:declare_function
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOADI32_EAX %0                              ; Using NULL
    STORE32_Absolute32_eax &current_count       ; current_count = 0

    LOAD32_Absolute32_eax &global_token         ; Using global token
    LOAD32_EAX_from_EAX_Immediate8 !4           ; global token->prev
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global token->prev->s
    LOADI32_EBX %0                              ; NULL
    LOAD32_Absolute32_ecx &global_function_list ; global_function_list
    CALL32 %sym_declare                         ; sym_declare(global_token->prev->s, NULL, global_function_list);
    STORE32_Absolute32_eax &function            ; function = sym_declare(global_token->prev->s, NULL, global_function_list);
    STORE32_Absolute32_eax &global_function_list ; global_function_list = function

    CALL32 %collect_arguments                   ; collect all of the function arguments

    LOAD32_Absolute32_eax &global_token         ; Using global token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global token->s
    LOADI32_EBX &semicolon                      ; ";"
    CALL32 %match                               ; IF global token->s == ";"
    CMPI8_EAX !0                                ; If true
    JNE32 %declare_function_full                ; It was a prototype

    ;; Deal with prototypes
    LOAD32_Absolute32_eax &global_token         ; Using global token
    LOAD32_EAX_from_EAX                         ; global token->next
    STORE32_Absolute32_eax &global_token        ; global token = global token->next
    JMP32 %declare_function_done                ; Move on

:declare_function_full
    ;; Deal with full function definitions
    LOADI32_EAX &declare_function_string_0      ; "# Defining function "
    CALL32 %emit_out                            ; emit it

    LOAD32_Absolute32_eax &function             ; function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->s
    CALL32 %emit_out                            ; emit it

    LOADI32_EAX &declare_function_string_1      ; "\n:FUNCTION_"
    CALL32 %emit_out                            ; emit it

    LOAD32_Absolute32_eax &function             ; function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->s
    CALL32 %emit_out                            ; emit it

    LOADI32_EAX &declare_function_string_3      ; "\n"
    CALL32 %emit_out                            ; emit it

    CALL32 %statement                           ; Recursively get the function pieces

    LOAD32_Absolute32_eax &output_list          ; output
    LOAD32_EAX_from_EAX_Immediate8 !8           ; output->s
    LOADI32_EBX &declare_function_string_2      ; "ret\n"
    CALL32 %match                               ; IF output->s == "ret\n"
    CMPI8_EAX !0                                ; If true we can skip adding it
    JE32 %declare_function_done                 ; otherwise we need to add it

    ;; Add the return to the end of a function lacking a return;
    LOADI32_EAX &declare_function_string_2      ; "ret\n"
    CALL32 %emit_out                            ; emit it

:declare_function_done
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:declare_function_string_0  "# Defining function "
:declare_function_string_1  "
:FUNCTION_"
:declare_function_string_2  "ret
"
:declare_function_string_3  "
"


;; collect_arguments function
;; Receives nothing
;; Returns Nothing
;; Adds arguments to the function definition
;; holds struct type* type_size in ECX, then replace with struct token_list* a in ECX when type_size is used
:collect_arguments
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next
:collect_arguments_loop
    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &close_paren                    ; ")"
    CALL32 %match                               ; IF global_token->S == ")"
    CMPI8_EAX !0                                ; we reached the end
    JE32 %collect_arguments_done                ; be done

    ;; deal with the case of there are arguments
    CALL32 %type_name                           ; Get the type
    COPY_EAX_to_ECX                             ; put type_size safely out of the way

    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &close_paren                    ; ")"
    CALL32 %match                               ; IF global_token->S == ")"
    CMPI8_EAX !0                                ; is a foo(int, char,void) case
    JE32 %collect_arguments_common              ; deal with commas

    ;; Trying second else
    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &comma                          ; ","
    CALL32 %match                               ; IF global_token->S == ","
    CMPI8_EAX !0                                ; then deal with the common
    JE32 %collect_arguments_common              ; case of commas between arguments

    ;; deal with foo(int a, char b)
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    COPY_ECX_to_EBX                             ; put type_size in the right place
    LOAD32_Absolute32_ecx &function             ; Using function
    LOAD32_ECX_from_ECX_Immediate8 !16          ; function->args
    CALL32 %sym_declare                         ; sym_declare(global_token->s, type_size, function->arguments);
    COPY_EAX_to_ECX                             ; put a in a safe place

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !16          ; function->args
    CMPI8_EAX !0                                ; IF function->args == NULL
    JNE32 %collect_arguments_another            ; otherwise it isn't the first

    ;; Deal with the case of first argument in the function
    LOADI32_EAX %-4                             ; -4
    STORE32_EAX_into_ECX_Immediate8 !16         ; a->depth = -4
    JMP32 %collect_arguments_next               ; get to next

:collect_arguments_another
    ;; deal with the case of non-first arguments
    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !16          ; function->args
    LOAD32_EAX_from_EAX_Immediate8 !16          ; function->args->depth
    SUBI8_EAX !4                                ; function->args->depth - 4
    STORE32_EAX_into_ECX_Immediate8 !16         ; a->depth = function->args->depth - 4

:collect_arguments_next
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next

    LOAD32_Absolute32_eax &function             ; Using function
    STORE32_ECX_into_EAX_Immediate8 !16         ; function->args = a

:collect_arguments_common
    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &comma                          ; ","
    CALL32 %match                               ; IF global_token->S == ","
    CMPI8_EAX !0                                ; then deal with the comma
    JNE32 %collect_arguments_loop               ; otherwise loop

    ;; keep foo(bar(), 1) expressions working
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next
    JMP32 %collect_arguments_loop               ; keep going

:collect_arguments_done
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; statement function
;; Receives nothing
;; Returns nothing
;; Walks down global_token recursively to collect the contents of the function
:statement
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX

    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &open_curly_brace               ; "{"
    CALL32 %match                               ; IF global_token->S == "{"
    JNE32 %statement_label                      ; otherwise try label

    ;; deal with { statement }
    CALL32 %recursive_statement                 ; Statements inside of statements for days
    JMP32 %statement_done                       ; Be done

:statement_label
    LOAD8_al_from_EBX                           ; global_token->S[0]
    MOVZX_al                                    ; make it useful
    CMPI8_EAX !58                               ; IF global_token->S == ':'
    JNE32 %statement_local                      ; otherwise try locals

    ;; deal with labels
    COPY_EBX_to_EAX                             ; put global_token->S in the right spot
    CALL32 %emit_out                            ; emit it

    LOADI32_EAX &statement_string_0             ; Using "\t#C goto label\n"
    CALL32 %emit_out                            ; emit it

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next
    JMP32 %statement_done                       ; be done

:statement_local
    COPY_EBX_to_EAX                             ; put global_token->S in the right place
    LOADI32_EBX &prim_types                     ; pointer to primative types
    CALL32 %lookup_type                         ; See if found
    CMPI8_EAX !0                                ; IF NULL == lookup_type(global_token->S, prim_types)
    JNE32 %statement_local_success              ; Sweet a new local

    ;; Second chance
    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &struct                         ; "struct"
    CALL32 %match                               ; IF global_token->S == "struct"
    CMPI8_EAX !0                                ; then we are a local
    JNE32 %statement_if                         ; otherwise try IF

:statement_local_success
    CALL32 %collect_local                       ; Grab those locals
    JMP32 %statement_done                       ; be done

:statement_if
    LOADI32_EAX &if_string                      ; Using "if"
    CALL32 %match                               ; IF global_token->S == "if"
    CMPI8_EAX !0                                ; then we have an if statement
    JNE32 %statement_do                         ; otherwise try DO

    ;; Deal with IF statement
    CALL32 %process_if                          ; DO IT
    JMP32 %statement_done                       ; be done

:statement_do
    LOADI32_EAX &do_string                      ; Using "do"
    CALL32 %match                               ; IF global_token->S == "do"
    CMPI8_EAX !0                                ; then we have a do statement
    JNE32 %statement_while                      ; otherwise try WHILE

    ;; Deal with DO statement
    CALL32 %process_do                          ; DO IT
    JMP32 %statement_done                       ; be done

:statement_while
    LOADI32_EAX &while_string                   ; Using "while"
    CALL32 %match                               ; IF global_token->S == "while"
    CMPI8_EAX !0                                ; then we have a while statement
    JNE32 %statement_for                        ; otherwise try FOR

    ;; Deal with WHILE statement
    CALL32 %process_while                       ; DO IT
    JMP32 %statement_done                       ; be done

:statement_for
    LOADI32_EAX &for_string                     ; Using "for"
    CALL32 %match                               ; IF global_token->S == "for"
    CMPI8_EAX !0                                ; then we have a for statement
    JNE32 %statement_asm                        ; otherwise try ASM

    ;; Deal with FOR statement
    CALL32 %process_for                         ; DO IT
    JMP32 %statement_done                       ; be done

:statement_asm
    LOADI32_EAX &asm_string                     ; Using "asm"
    CALL32 %match                               ; IF global_token->S == "asm"
    CMPI8_EAX !0                                ; then we have an asm statement
    JNE32 %statement_goto                       ; otherwise try GOTO

    ;; Deal with ASM statement
    CALL32 %process_asm                         ; Hit it
    JMP32 %statement_done                       ; be done

:statement_goto
    LOADI32_EAX &goto_string                    ; Using "goto"
    CALL32 %match                               ; IF global_token->S == "goto"
    CMPI8_EAX !0                                ; then we have a goto statement
    JNE32 %statement_return                     ; Otherwise try RETURN

    ;; Deal with GOTO statement
    LOADI32_EAX &statement_string_1             ; Using "jmp %"
    CALL32 %emit_out                            ; emit it

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next

    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    CALL32 %emit_out                            ; emit it

    LOADI32_EAX &statement_string_2             ; Using "\n"
    CALL32 %emit_out                            ; emit it

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next

    LOADI32_EAX &statement_string_4             ; Using "ERROR in statement\nMissing ;\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; Make sure it has the required
    JMP32 %statement_done                       ; Be done

:statement_return
    LOADI32_EAX &return_string                  ; Using "return"
    CALL32 %match                               ; IF global_token->S == "return"
    CMPI8_EAX !0                                ; then we have a return statement
    JNE32 %statement_break                      ; Otherwise try BREAK

    ;; Deal with RETURN Statement
    CALL32 %return_result                       ; Return anything they want
    JMP32 %statement_done                       ; be done

:statement_break
    LOADI32_EAX &break_string                   ; Using "break"
    CALL32 %match                               ; IF global_token->S == "break"
    CMPI8_EAX !0                                ; then we have a break statement
    JNE32 %statement_continue                   ; Otherwise try CONTINUE

    ;; Deal with BREAK statement
    CALL32 %process_break                       ; Lets do some damage
    JMP32 %statement_done                       ; be done

:statement_continue
    LOADI32_EAX &continue_string                ; Using "continue"
    CALL32 %match                               ; IF global_token->S == "continue"
    CMPI8_EAX !0                                ; then we have a continue statement
    JNE32 %statement_else                       ; Otherwise we are punting to an expression

    ;; Deal with CONTINUE statement
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next

    LOADI32_EAX &statement_string_3             ; Using "\n#continue statement\n"
    CALL32 %emit_out                            ; emit it

    LOADI32_EAX &statement_string_4             ; Using "ERROR in statement\nMissing ;\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; Don't forget the ";"
    JMP32 %statement_done                       ; Be done

:statement_else
    CALL32 %expression                          ; Collect expression
    LOADI32_EAX &statement_string_4             ; Using "ERROR in statement\nMissing ;\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; make sure we have it

:statement_done
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:statement_string_0  "  #C goto label
"
:statement_string_1  "jmp %"
:statement_string_2  "
"
:statement_string_3  "
#continue statement
"
:statement_string_4  "ERROR in statement
Missing ;
"


;; recursive_statement function
;; Receives nothing
;; Returns nothing
;; Walks the global_token list to build the contents of statements
;; Uses struct token_list* frame in ECX
:recursive_statement
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next

    LOAD32_Absolute32_ecx &function             ; Using function
    LOAD32_ECX_from_ECX_Immediate8 !4           ; frame = function->locals

:recursive_statement_loop
    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &close_curly_brace              ; Using "}"
    CALL32 %match                               ; IF global_token->S == "}"
    CMPI8_EAX !0                                ; Then we are done recursing
    JE32 %recursive_statement_cleanup           ; and then we clean up

    ;; Deal with the recursive calls
    CALL32 %statement                           ; Deal with another statement
    JMP32 %recursive_statement_loop             ; loop some more

:recursive_statement_cleanup
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next

    LOADI32_EAX &recursive_statement_string_0   ; Using "ret\n"
    LOAD32_Absolute32_ebx &output_list          ; Using output
    LOAD32_EBX_from_EBX_Immediate8 !8           ; output->S
    CALL32 %match                               ; IF output->S == "ret\n"
    CMPI8_EAX !0                                ; Then we can skip the clean up
    JE32 %recursive_statement_done              ; and be done

    ;; Deal with cleanup
    LOAD32_Absolute32_ebx &function             ; Using function
    LOAD32_EBX_from_EBX_Immediate8 !4           ; i = function->locals
    LOADI32_EAX &recursive_statement_string_1   ; Using "pop_ebx\t# _recursive_statement_locals\n"

:recursive_statement_locals
    CMP_ECX_EBX                                 ; IF frame != i
    JE32 %recursive_statement_done              ; Otherwise be done

    ;; Lets emit
    CALL32 %emit_out                            ; emit it
    LOAD32_EBX_from_EBX                         ; i = i->next
    JMP32 %recursive_statement_locals           ; keep going

:recursive_statement_done
    LOAD32_Absolute32_eax &function             ; Using function
    STORE32_ECX_into_EAX_Immediate8 !4          ; function->locals = frame
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:recursive_statement_string_0  "ret
"
:recursive_statement_string_1  "pop_ebx # _recursive_statement_locals
"


;; return_result function
;; Receives nothing
;; Returns nothing
;; Cleans up function and generates return
;; Also handles returning expressions
:return_result
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next

    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; make it useful
    CMPI8_EAX !59                               ; If global_token->S[0] == ';'
    JE32 %return_result_cleanup                 ; Go straight to cleanup

    CALL32 %expression                          ; get the expression we are returning

:return_result_cleanup
    LOADI32_EAX &return_result_string_0         ; Using "ERROR in return_result\nMISSING ;\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; Make sure we have it

    LOAD32_Absolute32_ebx &function             ; Using function
    LOAD32_EBX_from_EBX_Immediate8 !4           ; function->locals
    LOADI32_EAX &return_result_string_1         ; Using "pop_ebx\t# _return_result_locals\n"
:return_result_locals
    CMPI8_EBX !0                                ; IF NULL == i
    JE32 %return_result_done                    ; Be done

    CALL32 %emit_out                            ; Emit out pop
    LOAD32_EBX_from_EBX                         ; i = i->NEXT
    JMP32 %return_result_locals                 ; Keep going

:return_result_done
    LOADI32_EAX &return_result_string_2         ; Using "ret\n"
    CALL32 %emit_out                            ; Emit it
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:return_result_string_0  "ERROR in return_result
MISSING ;
"
:return_result_string_1  "pop_ebx   # _return_result_locals
"
:return_result_string_2  "ret
"


;; collect_local function
;; Receives nothing
;; Returns nothing
;; Walks global_token list to create function locals
;; Uses ECX for struct token_list* A
:collect_local
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    CALL32 %type_name                           ; Get the local's type

    COPY_EAX_to_EBX                             ; Put struct type* type_size in the right place
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD32_Absolute32_ecx &function             ; Using function
    LOAD32_ECX_from_ECX_Immediate8 !4           ; function->locals
    CALL32 %sym_declare                         ; Declare it
    COPY_EAX_to_ECX                             ; put it away safely

    ;; Try for main
    LOADI32_EAX &main_string                    ; Using "main"
    LOAD32_Absolute32_ebx &function             ; Using function
    LOAD32_EBX_from_EBX_Immediate8 !8           ; function->S
    CALL32 %match                               ; IF match("main", function->s)
    CMPI8_EAX !0                                ; possible
    JNE32 %collect_local_fresh                  ; try to see if fresh function

    ;; Ok we are in main, now to see if main is fresh
    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !4           ; function->locals
    CMPI8_EAX !0                                ; IF NULL == function->locals
    JNE32 %collect_local_fresh                  ; try to see if fresh function

    ;; Sweet we are in a fresh main
    LOADI32_EAX %-20                            ; We start at -20
    STORE32_EAX_into_ECX_Immediate8 !16         ; a->DEPTH = -20
    JMP32 %collect_local_common                 ; Go to the commons

:collect_local_fresh
    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !16          ; function->args
    CMPI8_EAX !0                                ; IF NULL == function->args
    JNE32 %collect_local_first                  ; Otherwise see if first

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !4           ; function->locals
    CMPI8_EAX !0                                ; IF NULL == function->locals
    JNE32 %collect_local_first                  ; Otherwise try first

    ;; Sweet we are in a fresh function
    LOADI32_EAX %-8                             ; We start at -8
    STORE32_EAX_into_ECX_Immediate8 !16         ; a->DEPTH = -8
    JMP32 %collect_local_common                 ; Go to the commons

:collect_local_first
    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !4           ; function->locals
    CMPI8_EAX !0                                ; IF NULL == function->locals
    JNE32 %collect_local_else                   ; Looks like we are just another local

    ;; Ok we are the first local
    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !16          ; function->args
    LOAD32_EAX_from_EAX_Immediate8 !16          ; function->args->depth
    SUBI8_EAX !8                                ; function->arguments->depth - 8
    STORE32_EAX_into_ECX_Immediate8 !16         ; a->DEPTH = function->arguments->depth - 8
    JMP32 %collect_local_common                 ; Go to the commons

:collect_local_else
    ;; Always the last to know
    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !4           ; function->locals
    LOAD32_EAX_from_EAX_Immediate8 !16          ; function->locals->depth
    SUBI8_EAX !4                                ; function->locals->depth - 4
    STORE32_EAX_into_ECX_Immediate8 !16         ; a->DEPTH = function->locals->depth - 4

:collect_local_common
    LOAD32_Absolute32_eax &function             ; Using function

    STORE32_ECX_into_EAX_Immediate8 !4          ; function->locals = a
    LOAD32_ECX_from_ECX_Immediate8 !8           ; a->S

    LOADI32_EAX &collect_local_string_0         ; Using "# Defining local "
    CALL32 %emit_out                            ; emit it

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    CALL32 %emit_out                            ; emit it

    LOADI32_EAX &collect_local_string_1         ; Using "\n"
    CALL32 %emit_out                            ; emit it

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOAD32_EBX_from_EAX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &equal                          ; Using "="
    CALL32 %match                               ; IF match("=", global_token->s)
    CMPI8_EAX !0                                ; Deal with assignment
    JNE32 %collect_local_done                   ; Otherwise finish it

    ;; Deal with assignment
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    CALL32 %expression                          ; Recurse

:collect_local_done
    LOADI32_EAX &collect_local_string_2         ; Using "ERROR in collect_local\nMissing ;\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; Make sure we have it

    LOADI32_EAX &collect_local_string_3         ; Using "push_eax\t#"
    CALL32 %emit_out                            ; emit it

    COPY_ECX_to_EAX                             ; put A->S where it belongs
    CALL32 %emit_out                            ; emit it

    LOADI32_EAX &collect_local_string_1         ; Using "\n"
    CALL32 %emit_out                            ; emit it

    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:collect_local_string_0  "# Defining local "
:collect_local_string_1  "
"
:collect_local_string_2  "ERROR in collect_local
Missing ;
"
:collect_local_string_3  "push_eax  #"


;; process_asm function
;; Receives nothing
;; Returns nothing
;; Simply inlines the asm statements
;; Uses EBX for global_token temp storage
:process_asm
    PUSH_EBX                                    ; Protect EBX
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOADI32_EAX &process_asm_string_0           ; Using "ERROR in process_asm\nMISSING (\n"
    LOADI32_EBX &open_paren                     ; Using "("
    CALL32 %require_match                       ; Make sure we have it

    LOAD32_Absolute32_ebx &global_token         ; Using global_token
:process_asm_iter
    LOAD32_EAX_from_EBX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !34                               ; IF global_token->S[0] == '\"'
    JNE32 %process_asm_done                     ; Otherwise be done

    LOAD32_EAX_from_EBX_Immediate8 !8           ; global_token->S
    ADDI8_EAX !1                                ; global_token->S + 1
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &process_asm_string_1           ; Using "\n"
    CALL32 %emit_out                            ; Emit it

    LOAD32_EBX_from_EBX                         ; global_token->NEXT
    STORE32_Absolute32_ebx &global_token        ; global_token = global_token->NEXT
    JMP32 %process_asm_iter                     ; keep going

:process_asm_done
    LOADI32_EAX &process_asm_string_2           ; Using "ERROR in process_asm\nMISSING )\n"
    LOADI32_EBX &close_paren                    ; Using ")"
    CALL32 %require_match                       ; Make sure we have it

    LOADI32_EAX &process_asm_string_3           ; Using "ERROR in process_asm\nMISSING ;\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; Make sure we have it

    POP_EBX                                     ; Restore EBX
    RETURN

:process_asm_string_0  "ERROR in process_asm
MISSING (
"
:process_asm_string_1  "
"
:process_asm_string_2  "ERROR in process_asm
MISSING )
"
:process_asm_string_3  "ERROR in process_asm
MISSING ;
"


;; process_if function
;; Receives nothing
;; Returns Nothing
;; Increments current_count recurses into expression + statement
;; Uses ECX for char* NUMBER_STRING
:process_if
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOAD32_Absolute32_eax &current_count        ; Using current count
    COPY_EAX_to_EBX                             ; Preparing for update
    ADDI8_EBX !1                                ; current_count + 1
    STORE32_Absolute32_ebx &current_count       ; current_count = current_count + 1
    CALL32 %numerate_number                     ; convert to string
    COPY_EAX_to_ECX                             ; put NUMBER_STRING in place

    LOADI32_EAX &process_if_string_0            ; Using "# IF_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOADI32_EAX &process_if_string_1            ; Using "ERROR in process_if\nMISSING (\n"
    LOADI32_EBX &open_paren                     ; Using "("
    CALL32 %require_match                       ; Make sure we have it

    CALL32 %expression                          ; Recurse to get the IF(...) part

    LOADI32_EAX &process_if_string_2            ; Using "test_eax,eax\nje %ELSE_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOADI32_EAX &process_if_string_3            ; Using "ERROR in process_if\nMISSING )\n"
    LOADI32_EBX &close_paren                    ; Using ")"
    CALL32 %require_match                       ; Make sure we have it

    CALL32 %statement                           ; Recursive to get the IF(){...} part

    LOADI32_EAX &process_if_string_4            ; Using "jmp %_END_IF_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOADI32_EAX &process_if_string_5            ; Using ":ELSE_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &else_string                    ; Using "else"
    CALL32 %match                               ; IF global_token->S == "else"
    CMPI8_EAX !0                                ; Then we need to collect the else too
    JNE32 %process_if_done                      ; Otherwise finish up

    ;; deal with else statement
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    CALL32 %statement                           ; Recurse to get the ELSE {...} part

:process_if_done
    LOADI32_EAX &process_if_string_6            ; Using ":_END_IF_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:process_if_string_0  "# IF_"
:process_if_string_1  "ERROR in process_if
MISSING (
"
:process_if_string_2  "test_eax,eax
je %ELSE_"
:process_if_string_3  "ERROR in process_if
MISSING )
"
:process_if_string_4  "jmp %_END_IF_"
:process_if_string_5  ":ELSE_"
:process_if_string_6  ":_END_IF_"


;; save_break_frame microfunction
;; Overwrites EAX and EBX
;; Saves break frame on stack
;; Returns to caller
:save_break_frame
    POP_EBX                                     ; Save return Address
    LOAD32_Absolute32_eax &break_frame          ; Get break_frame
    PUSH_EAX                                    ; Store as nested_locals
    LOAD32_Absolute32_eax &break_target_head    ; Get break_target_head
    PUSH_EAX                                    ; Store as nested_break_head
    LOAD32_Absolute32_eax &break_target_func    ; Get break_target_func
    PUSH_EAX                                    ; Store as nested_break_func
    LOAD32_Absolute32_eax &break_target_num     ; Get break_target_num
    PUSH_EAX                                    ; Store as nested_break_num
    PUSH_EBX                                    ; Put return back in place
    RETURN                                      ; Return to caller


;; restore_break_frame microfunction
;; Overwrites EAX and EBX
;; Restores break frame from stack
;; Returns to caller
:restore_break_frame
    POP_EBX                                     ; Save return Address
    POP_EAX                                     ; Get nested_break_num
    STORE32_Absolute32_eax &break_target_num    ; Restore break_target_num
    POP_EAX                                     ; Get nested_break_func
    STORE32_Absolute32_eax &break_target_func   ; Restore break_target_func
    POP_EAX                                     ; Get nested_break_head
    STORE32_Absolute32_eax &break_target_head   ; Restore break_target_head
    POP_EAX                                     ; Get nested_locals
    STORE32_Absolute32_eax &break_frame         ; Restore break_frame
    PUSH_EBX                                    ; Put return back in place
    RETURN                                      ; Return to caller


;; set_break_frame microfunction
;; Receives char* head in EAX and char* num in EBX
;; Overwrites EAX and EBX
;; Returns to calling function
:set_break_frame
    STORE32_Absolute32_eax &break_target_head   ; update break_target_head
    STORE32_Absolute32_ebx &break_target_num    ; update break_target_num
    LOAD32_Absolute32_ebx &function             ; Using function
    LOAD32_EAX_from_EBX_Immediate8 !4           ; function->LOCALS
    STORE32_Absolute32_eax &break_frame         ; break_frame = function->LOCALS
    LOAD32_EAX_from_EBX_Immediate8 !8           ; function->S
    STORE32_Absolute32_eax &break_target_func   ; break_target_func = function->S
    RETURN                                      ; Return to sender


;; process_do function
;; Receives Nothing
;; Returns Nothing
;; Increments current_count and leverages save/restore_break_frame pieces
;; Uses ECX for char* NUMBER_STRING
:process_do
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    CALL32 %save_break_frame                    ; Save the frame

    LOAD32_Absolute32_eax &current_count        ; Using current count
    COPY_EAX_to_EBX                             ; Preparing for update
    ADDI8_EBX !1                                ; current_count + 1
    STORE32_Absolute32_ebx &current_count       ; current_count = current_count + 1
    CALL32 %numerate_number                     ; convert to string
    COPY_EAX_to_ECX                             ; put NUMBER_STRING in place

    LOADI32_EAX &process_do_string_0            ; Using "DO_END_"
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %set_break_frame                     ; Set the frame

    LOADI32_EAX &process_do_string_1            ; Using ":DO_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    CALL32 %statement                           ; Do the DO {...} part

    LOADI32_EAX &process_do_string_2            ; Using "ERROR in process_do\nMISSING while\n"
    LOADI32_EBX &while_string                   ; Using "while"
    CALL32 %require_match                       ; Make sure we have it

    LOADI32_EAX &process_do_string_3            ; Using "ERROR in process_do\nMISSING (\n"
    LOADI32_EBX &open_paren                     ; Using "("
    CALL32 %require_match                       ; Make sure we have it

    CALL32 %expression                          ; Do the WHILE (...) part

    LOADI32_EAX &process_do_string_4            ; Using "ERROR in process_do\nMISSING )\n"
    LOADI32_EBX &close_paren                    ; Using ")"
    CALL32 %require_match                       ; Make sure we have it

    LOADI32_EAX &process_do_string_5            ; Using "ERROR in process_do\nMISSING ;\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; Make sure we have it

    LOADI32_EAX &process_do_string_6            ; Using "test_eax,eax\njne %DO_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOADI32_EAX &process_do_string_7            ; Using ":DO_END_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    CALL32 %restore_break_frame                 ; Restore the old break frame

    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:process_do_string_0  "DO_END_"
:process_do_string_1  ":DO_"
:process_do_string_2  "ERROR in process_do
MISSING while
"
:process_do_string_3  "ERROR in process_do
MISSING (
"
:process_do_string_4  "ERROR in process_do
MISSING )
"
:process_do_string_5  "ERROR in process_do
MISSING ;
"
:process_do_string_6  "test_eax,eax
jne %DO_"
:process_do_string_7  ":DO_END_"


;; process_while function
;; Receives nothing
;; Returns nothing
;; Increments current_count and leverages save/restore_break_frame pieces
;; Uses ECX for char* NUMBER_STRING
:process_while
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    CALL32 %save_break_frame                    ; Save break_frame

    LOAD32_Absolute32_eax &current_count        ; Using current count
    COPY_EAX_to_EBX                             ; Preparing for update
    ADDI8_EBX !1                                ; current_count + 1
    STORE32_Absolute32_ebx &current_count       ; current_count = current_count + 1
    CALL32 %numerate_number                     ; convert to string
    COPY_EAX_to_ECX                             ; put NUMBER_STRING in place

    LOADI32_EAX &process_while_string_0         ; Using "END_WHILE_"
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %set_break_frame                     ; Set it and forget it

    LOADI32_EAX &process_while_string_1         ; Using ":WHILE_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOADI32_EAX &process_while_string_2         ; Using "ERROR in process_while\nMISSING (\n"
    LOADI32_EBX &open_paren                     ; Using "("
    CALL32 %require_match                       ; Make sure we have it

    CALL32 %expression                          ; Deal with the WHILE (...) part

    LOADI32_EAX &process_while_string_3         ; Using "test_eax,eax\nje %END_WHILE_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOADI32_EAX &process_while_string_4         ; Using "# THEN_while_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOADI32_EAX &process_while_string_5         ; Using "ERROR in process_while\nMISSING )\n"
    LOADI32_EBX &close_paren                    ; Using ")"
    CALL32 %require_match                       ; Make sure we have it

    CALL32 %statement                           ; Deal with the {....} part

    LOADI32_EAX &process_while_string_6         ; Using "jmp %WHILE_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOADI32_EAX &process_while_string_7         ; Using ":END_WHILE_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    CALL32 %restore_break_frame                 ; Restore the old break frame

    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:process_while_string_0  "END_WHILE_"
:process_while_string_1  ":WHILE_"
:process_while_string_2  "ERROR in process_while
MISSING (
"
:process_while_string_3  "test_eax,eax
je %END_WHILE_"
:process_while_string_4  "# THEN_while_"
:process_while_string_5  "ERROR in process_while
MISSING )
"
:process_while_string_6  "jmp %WHILE_"
:process_while_string_7  ":END_WHILE_"


;; process_for function
;; Receives Nothing
;; Returns Nothing
;; Increments current_count and leverages save/restore_break_frame pieces
;; Uses ECX for char* NUMBER_STRING
:process_for
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    CALL32 %save_break_frame                    ; Save the frame

    LOAD32_Absolute32_eax &current_count        ; Using current count
    COPY_EAX_to_EBX                             ; Preparing for update
    ADDI8_EBX !1                                ; current_count + 1
    STORE32_Absolute32_ebx &current_count       ; current_count = current_count + 1
    CALL32 %numerate_number                     ; convert to string
    COPY_EAX_to_ECX                             ; put NUMBER_STRING in place

    LOADI32_EAX &process_for_string_0           ; Using "FOR_END_"
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %set_break_frame                     ; Set it and forget it

    LOADI32_EAX &process_for_string_1           ; Using "# FOR_initialization_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOADI32_EAX &process_for_string_2           ; Using "ERROR in process_for\nMISSING (\n"
    LOADI32_EBX &open_paren                     ; Using "("
    CALL32 %require_match                       ; Make Sure we have it

    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &semicolon                      ; Using ";"
    CALL32 %match                               ; IF global_token->S == ";"
    CMPI8_EAX !0                                ; Then no initializer
    JE32 %process_for_terminator                ; And skip getting the expression

    ;; Deal with FOR (...; case
    CALL32 %expression                          ; Get the FOR ( ... ; part

:process_for_terminator
    LOADI32_EAX &process_for_string_3           ; Using ":FOR_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOADI32_EAX &process_for_string_4           ; Using "ERROR in process_for\nMISSING ;1\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; Make sure we have it

    CALL32 %expression                          ; Get the FOR ( ; ... ; Part

    LOADI32_EAX &process_for_string_5           ; Using "test_eax,eax\nje %FOR_END_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOADI32_EAX &process_for_string_6           ; Using "jmp %FOR_THEN_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOADI32_EAX &process_for_string_7           ; Using ":FOR_ITER_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOADI32_EAX &process_for_string_8           ; Using "ERROR in process_for\nMISSING ;2\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; Make sure we have it

    CALL32 %expression                          ; Get the FOR (;;...) part

    LOADI32_EAX &process_for_string_9           ; Using "jmp %FOR_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOADI32_EAX &process_for_string_10          ; Using ":FOR_THEN_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOADI32_EAX &process_for_string_11          ; Using "ERROR in process_for\nMISSING )\n"
    LOADI32_EBX &close_paren                    ; Using ")"
    CALL32 %require_match                       ; Make sure we have it

    CALL32 %statement                           ; Get FOR (;;) {...} part

    LOADI32_EAX &process_for_string_12          ; Using "jmp %FOR_ITER_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Passing NUMBER_STRING
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    LOADI32_EAX &process_for_string_13          ; Using ":FOR_END_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    CALL32 %uniqueID_out                        ; uniqueID_out(function->s, number_string)

    CALL32 %restore_break_frame                 ; Restore the old break frame

    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:process_for_string_0  "FOR_END_"
:process_for_string_1  "# FOR_initialization_"
:process_for_string_2  "ERROR in process_for
MISSING (
"
:process_for_string_3  ":FOR_"
:process_for_string_4  "ERROR in process_for
MISSING ;1
"
:process_for_string_5  "test_eax,eax
je %FOR_END_"
:process_for_string_6  "jmp %FOR_THEN_"
:process_for_string_7  ":FOR_ITER_"
:process_for_string_8  "ERROR in process_for
MISSING ;2
"
:process_for_string_9  "jmp %FOR_"
:process_for_string_10 ":FOR_THEN_"
:process_for_string_11 "ERROR in process_for
MISSING )
"
:process_for_string_12 "jmp %FOR_ITER_"
:process_for_string_13 ":FOR_END_"


;; process_break function
;; Receives nothing
;; Returns nothing
;; Handles the break out of loops case
;; Uses EBX for struct token_list* break_frame and ECX for struct token_list* I
:process_break
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOAD32_Absolute32_eax &break_target_head    ; Catch big error
    CMPI8_EAX !0                                ; IF(NULL == break_target_head)
    JE32 %process_break_bad                     ; I'm sorry Mr White but you have stage-3 lung cancer

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_ECX_from_EAX_Immediate8 !4           ; I = function->LOCALS
    LOAD32_Absolute32_ebx &break_frame          ; Put break_frame in the right spot
    LOADI32_EAX &process_break_string_1         ; Using "pop_ebx\t# break_cleanup_locals\n"

:process_break_iter
    CMPI8_ECX !0                                ; IF (NULL == I)
    JE32 %process_break_cleaned                 ; We are done

    CMP_EBX_ECX                                 ; IF I != break_frame
    JE32 %process_break_cleaned                 ; We are done

    CALL32 %emit_out                            ; Emit it
    LOAD32_ECX_from_ECX                         ; I = I->NEXT
    JMP32 %process_break_iter                   ; Keep looping

:process_break_cleaned
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOADI32_EAX &process_break_string_2         ; Using "jmp %"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &break_target_head    ; Get what we are in
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &break_target_func    ; Get what function we are in
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &underline                      ; Using "_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &break_target_num     ; Get dem digits
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &process_break_string_3         ; Using "\n"
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &process_break_string_4         ; Using "ERROR in break statement\nMissing ;\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; Make sure we have it

    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:process_break_bad
    ;; Breaking badly
    LOADI32_EAX %2                              ; Using standard error
    STORE32_Absolute32_eax &Output_file         ; write to standard error
;   CALL32 %line_error                          ; Write useful debug info
    COPY_ECX_to_EAX                             ; put S in the right place
    CALL32 %File_Print                          ; print it

    LOADI32_EAX &process_break_string_0         ; Ending string
    CALL32 %File_Print                          ; print it
    JMP32 %Exit_Failure                         ; Abort Hard

:process_break_string_0  "Not inside of a loop or case statement"
:process_break_string_1  "pop_ebx   # break_cleanup_locals
"
:process_break_string_2  "jmp %"
:process_break_string_3  "
"
:process_break_string_4  "ERROR in break statement
Missing ;
"


;; expression function
;; Receives Nothing
;; Returns Nothing
;; Walks global_token and updates output_list
;; Uses EAX and EBX for match and ECX for char* store
:expression
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    CALL32 %bitwise_expr                        ; Collect bitwise expressions

    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &equal                          ; "="
    CALL32 %match                               ; IF global_token->S == "="
    CMPI8_EAX !0                                ; We have to deal with assignment
    JNE32 %expression_done                      ; Looks like nope

    ;; Deal with possible assignment
    LOADI32_ECX &expression_string_1            ; Assume "mov_[ebx],al\n" by default
    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !4           ; global_token->PREV
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->PREV->S
    LOADI32_EAX &close_bracket                  ; Using "]"
    CALL32 %match                               ; IF global_token->S == "]"
    CMPI8_EAX !0                                ; Then we might have a char
    JNE32 %expression_int                       ; Otherwise INT

    LOAD32_Absolute32_ebx &current_target       ; Using current_target
    LOAD32_EBX_from_EBX_Immediate8 !24          ; current_target->NAME
    LOADI32_EAX &type_char_indirect_name        ; Using "char*"
    CALL32 %match                               ; Intentional inefficiency because I feel like it
    CMPI8_EAX !0                                ; IF current_target->NAME == "char*"
    JNE32 %expression_int                       ; Do char anyway

    JMP32 %expression_common                    ; Looks like we have to use "mov_[ebx],al\n"

:expression_int
    LOADI32_ECX &expression_string_0            ; Use "mov_[ebx],eax\n"

:expression_common
    LOADI32_EAX &expression                     ; Passing expression
    CALL32 %common_recursion                    ; Recurse
    COPY_ECX_to_EAX                             ; Using Store
    CALL32 %emit_out                            ; Emit it
    LOADI32_EAX %0                              ; Using NULL
    STORE32_Absolute32_eax &current_target      ; current_target = NULL

:expression_done
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:expression_string_0  "mov_[ebx],eax
"
:expression_string_1  "mov_[ebx],al
"


;; bitwise_expr function
;; Receives nothing
;; Returns nothing
;; Walks global_token list and updates output list
;; Just calls other functions
:bitwise_expr
    CALL32 %relational_expr                     ; Walk up the tree
    CALL32 %bitwise_expr_stub                   ; Let general recursion do the work
    RETURN


;; bitwise_expr_stub function
;; Receives nothing
;; Returns Nothing
;; Just calls general_recursion a bunch
;; Uses EAX, EBX, ECX and EDX for passing constants to general recursion
:bitwise_expr_stub
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX

    LOADI32_EAX &relational_expr                ; Using relational_expr
    LOADI32_EBX &bitwise_expr_stub_string_0     ; Using "and_eax,ebx\n"
    LOADI32_ECX &bitwise_and                    ; Using "&"
    LOADI32_EDX &bitwise_expr_stub              ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &relational_expr                ; Using relational_expr
    LOADI32_EBX &bitwise_expr_stub_string_0     ; Using "and_eax,ebx\n"
    LOADI32_ECX &logical_and                    ; Using "&&"
    LOADI32_EDX &bitwise_expr_stub              ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &relational_expr                ; Using relational_expr
    LOADI32_EBX &bitwise_expr_stub_string_1     ; Using "or_eax,ebx\n"
    LOADI32_ECX &bitwise_or                     ; Using "|"
    LOADI32_EDX &bitwise_expr_stub              ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &relational_expr                ; Using relational_expr
    LOADI32_EBX &bitwise_expr_stub_string_1     ; Using "or_eax,ebx\n"
    LOADI32_ECX &logical_or                     ; Using "||"
    LOADI32_EDX &bitwise_expr_stub              ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &relational_expr                ; Using relational_expr
    LOADI32_EBX &bitwise_expr_stub_string_2     ; Using "xor_eax,ebx\n"
    LOADI32_ECX &bitwise_xor                    ; Using "^"
    LOADI32_EDX &bitwise_expr_stub              ; And recurse
    CALL32 %general_recursion                   ; Hit it

    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:bitwise_expr_stub_string_0  "and_eax,ebx
"
:bitwise_expr_stub_string_1  "or_eax,ebx
"
:bitwise_expr_stub_string_2  "xor_eax,ebx
"


;; relational_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
:relational_expr
    CALL32 %additive_expr                       ; Walk up the tree
    CALL32 %relational_expr_stub                ; Recurse
    RETURN


;; relational_expr_stub function
;; Receives nothing
;; Returns Nothing
;; Just calls general_recursion a bunch
;; Uses EAX, EBX, ECX and EDX for passing constants to general recursion
:relational_expr_stub
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX

    LOADI32_EAX &additive_expr                  ; Using additive_expr
    LOADI32_EBX &relational_expr_stub_string_0  ; Using "cmp\nsetl_al\nmovzx_eax,al\n"
    LOADI32_ECX &less_than_string               ; Using "<"
    LOADI32_EDX &relational_expr_stub           ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &additive_expr                  ; Using additive_expr
    LOADI32_EBX &relational_expr_stub_string_1  ; Using "cmp\nsetle_al\nmovzx_eax,al\n"
    LOADI32_ECX &less_than_equal_string         ; Using "<="
    LOADI32_EDX &relational_expr_stub           ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &additive_expr                  ; Using additive_expr
    LOADI32_EBX &relational_expr_stub_string_2  ; Using "cmp\nsetge_al\nmovzx_eax,al\n"
    LOADI32_ECX &greater_than_equal_string      ; Using ">="
    LOADI32_EDX &relational_expr_stub           ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &additive_expr                  ; Using additive_expr
    LOADI32_EBX &relational_expr_stub_string_3  ; Using "cmp\nsetg_al\nmovzx_eax,al\n"
    LOADI32_ECX &greater_than_string            ; Using ">"
    LOADI32_EDX &relational_expr_stub           ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &additive_expr                  ; Using additive_expr
    LOADI32_EBX &relational_expr_stub_string_4  ; Using "cmp\nsete_al\nmovzx_eax,al\n"
    LOADI32_ECX &equal_to_string                ; Using "=="
    LOADI32_EDX &relational_expr_stub           ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &additive_expr                  ; Using additive_expr
    LOADI32_EBX &relational_expr_stub_string_5  ; Using "cmp\nsetne_al\nmovzx_eax,al\n"
    LOADI32_ECX &not_equal_string               ; Using "!="
    LOADI32_EDX &relational_expr_stub           ; And recurse
    CALL32 %general_recursion                   ; Hit it

    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:relational_expr_stub_string_0  "cmp
setl_al
movzx_eax,al
"
:relational_expr_stub_string_1  "cmp
setle_al
movzx_eax,al
"
:relational_expr_stub_string_2  "cmp
setge_al
movzx_eax,al
"
:relational_expr_stub_string_3  "cmp
setg_al
movzx_eax,al
"
:relational_expr_stub_string_4  "cmp
sete_al
movzx_eax,al
"
:relational_expr_stub_string_5  "cmp
setne_al
movzx_eax,al
"


;; additive_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
:additive_expr
    CALL32 %postfix_expr                        ; Walk up the tree
    CALL32 %additive_expr_stub                  ; Recurse
    RETURN


;; additive_expr_stub function
;; Receives nothing
;; Returns Nothing
;; Just calls general_recursion a bunch
;; Uses EAX, EBX, ECX and EDX for passing constants to general recursion
:additive_expr_stub
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX

    LOADI32_EAX &postfix_expr                   ; Using postfix_expr
    LOADI32_EBX &additive_expr_stub_string_0    ; Using "add_eax,ebx\n"
    LOADI32_ECX &plus_string                    ; Using "+"
    LOADI32_EDX &additive_expr_stub             ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &postfix_expr                   ; Using postfix_expr
    LOADI32_EBX &additive_expr_stub_string_1    ; Using "sub_ebx,eax\nmov_eax,ebx\n"
    LOADI32_ECX &minus_string                   ; Using "-"
    LOADI32_EDX &additive_expr_stub             ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &postfix_expr                   ; Using postfix_expr
    LOADI32_EBX &additive_expr_stub_string_2    ; Using "mul_ebx\n"
    LOADI32_ECX &multiply_string                ; Using "*"
    LOADI32_EDX &additive_expr_stub             ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &postfix_expr                   ; Using postfix_expr
    LOADI32_EBX &additive_expr_stub_string_3    ; Using "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\n"
    LOADI32_ECX &divide_string                  ; Using "/"
    LOADI32_EDX &additive_expr_stub             ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &postfix_expr                   ; Using postfix_expr
    LOADI32_EBX &additive_expr_stub_string_4    ; Using "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\nmov_eax,edx\n"
    LOADI32_ECX &modulus_string                 ; Using "%"
    LOADI32_EDX &additive_expr_stub             ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &postfix_expr                   ; Using postfix_expr
    LOADI32_EBX &additive_expr_stub_string_5    ; Using "mov_ecx,eax\nmov_eax,ebx\nsal_eax,cl\n"
    LOADI32_ECX &left_shift_string              ; Using "<<"
    LOADI32_EDX &additive_expr_stub             ; And recurse
    CALL32 %general_recursion                   ; Hit it

    LOADI32_EAX &postfix_expr                   ; Using postfix_expr
    LOADI32_EBX &additive_expr_stub_string_6    ; Using "mov_ecx,eax\nmov_eax,ebx\nsar_eax,cl\n"
    LOADI32_ECX &right_shift_string             ; Using ">>"
    LOADI32_EDX &additive_expr_stub             ; And recurse
    CALL32 %general_recursion                   ; Hit it

    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:additive_expr_stub_string_0  "add_eax,ebx
"
:additive_expr_stub_string_1  "sub_ebx,eax
mov_eax,ebx
"
:additive_expr_stub_string_2  "mul_ebx
"
:additive_expr_stub_string_3  "xchg_ebx,eax
mov_edx, %0
div_ebx
"
:additive_expr_stub_string_4  "xchg_ebx,eax
mov_edx, %0
div_ebx
mov_eax,edx
"
:additive_expr_stub_string_5  "mov_ecx,eax
mov_eax,ebx
sal_eax,cl
"
:additive_expr_stub_string_6  "mov_ecx,eax
mov_eax,ebx
sar_eax,cl
"


;; postfix_expr function
;; Receives nothing
;; Returns Nothing
;; Walks global_token list and updates output list
;; just calls other function
:postfix_expr
    CALL32 %primary_expr                        ; Walk up the tree
    CALL32 %postfix_expr_stub                   ; Recurse
    RETURN


;; postfix_expr_stub function
;; Receives nothing
;; Returns Nothing
;; Checks for "[" and "->" and deals with them otherwise does nothing
;; Uses EAX, EBX, ECX and EDX for passing constants to general recursion
:postfix_expr_stub
    PUSH_EBX                                    ; Protect EBX
    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &open_bracket                   ; Using "["
    CALL32 %match                               ; IF global_token->S == "["
    CMPI8_EAX !0                                ; then we have an array
    JNE32 %postfix_expr_stub_arrow              ; Otherwise try arrow

    ;; Deal with array
    CALL32 %postfix_expr_array                  ; Get it
    CALL32 %postfix_expr_stub                   ; Recurse

:postfix_expr_stub_arrow
    LOADI32_EAX &arrow_string                   ; Using "->"
    CALL32 %match                               ; IF global_token->S == "->"
    CMPI8_EAX !0                                ; Then we need to deal with struct offsets
    JNE32 %postfix_expr_stub_done               ; Otherwise be done

    ;; Deal with arrow
    CALL32 %postfix_expr_arrow                  ; Get it
    CALL32 %postfix_expr_stub                   ; Recurse

:postfix_expr_stub_done
    POP_EBX                                     ; Restore EBX
    RETURN


;; unary_expr_sizeof function
;; Receives nothing
;; Returns nothing
;; Uses ECX for A->SIZE
:unary_expr_sizeof
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOADI32_EAX &unary_expr_sizeof_string_0     ; Using "ERROR in unary_expr\nMissing (\n"
    LOADI32_EBX &open_paren                     ; Using "("
    CALL32 %require_match                       ; Make sure we have it

    CALL32 %type_name                           ; Get the type
    LOAD32_ECX_from_EAX_Immediate8 !4           ; Set A->TYPE

    LOADI32_EAX &unary_expr_sizeof_string_1     ; Using "ERROR in unary_expr\nMissing )\n"
    LOADI32_EBX &close_paren                    ; Using ")"
    CALL32 %require_match                       ; Make sure we have it

    LOADI32_EAX &unary_expr_sizeof_string_2     ; Using "mov_eax, %"
    CALL32 %emit_out                            ; Emit it

    COPY_ECX_to_EAX                             ; Put A->SIZE in the right place
    CALL32 %numerate_number                     ; Turn into string
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &unary_expr_sizeof_string_3     ; Using "\n"
    CALL32 %emit_out                            ; Emit it

    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:unary_expr_sizeof_string_0  "ERROR in unary_expr
Missing (
"
:unary_expr_sizeof_string_1  "ERROR in unary_expr
Missing )
"
:unary_expr_sizeof_string_2  "mov_eax, %"
:unary_expr_sizeof_string_3  "
"


;; postfix_expr_array function
;; Receives Nothing
;; Returns Nothing
;; Uses EBX for struct type* ARRAY and ECX for char* ASSIGN
:postfix_expr_array
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOAD32_Absolute32_eax &current_target       ; ARRAY = current_target
    PUSH_EAX                                    ; Protect it

    LOADI32_EAX &expression                     ; Using expression
    CALL32 %common_recursion                    ; Recurse

    POP_EBX                                     ; Restore array
    STORE32_Absolute32_ebx &current_target      ; current_target = ARRAY

    LOADI32_ECX &postfix_expr_array_string_0    ; ASSIGN = "mov_eax,[eax]\n"

    LOADI32_EAX &type_char_indirect_name        ; Using "char*"
    LOAD32_EBX_from_EBX_Immediate8 !24          ; current_target->NAME
    CALL32 %match                               ; IF current_target->NAME == "char*"
    CMPI8_EAX !0                                ; load a byte
    JNE32 %postfix_expr_array_large             ; Otherwise adjust

    ;; Deal with loading byte
    LOADI32_ECX &postfix_expr_array_string_1    ; ASSIGN = "movsx_eax,BYTE_PTR_[eax]\n"
    JMP32 %postfix_expr_array_common            ; Do the next bit

:postfix_expr_array_large
    ;; deal with arrays made of things other than chars
    LOADI32_EAX &postfix_expr_array_string_2    ; Using "sal_eax, !"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &current_target       ; Using current_target
    LOAD32_EAX_from_EAX_Immediate8 !12          ; current_target->INDIRECT
    LOAD32_EAX_from_EAX_Immediate8 !4           ; current_target->INDIRECT->SIZE
    CALL32 %ceil_log2                           ; ceil_log2(current_target->indirect->size)
    CALL32 %numerate_number                     ; numerate_number(ceil_log2(current_target->indirect->size))
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &postfix_expr_array_string_3    ; Using "\n"
    CALL32 %emit_out                            ; Emit it

:postfix_expr_array_common
    LOADI32_EAX &postfix_expr_array_string_4    ; Using "add_eax,ebx\n"
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &postfix_expr_array_string_5    ; Using "ERROR in postfix_expr\nMissing ]\n"
    LOADI32_EBX &close_bracket                  ; Using "]"
    CALL32 %require_match                       ; Make sure we have it

    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &equal                          ; Using "="
    CALL32 %match                               ; IF global_token->S == "="
    CMPI8_EAX !0                                ; We need to preserve address
    JNE32 %postfix_expr_array_done              ; Otherwise be done

    ;; Clearing out assign
    LOADI32_ECX &postfix_expr_array_string_6    ; ASSIGN = ""

:postfix_expr_array_done
    COPY_ECX_to_EAX                             ; Using ASSIGN
    CALL32 %emit_out                            ; Emit it

    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:postfix_expr_array_string_0  "mov_eax,[eax]
"
:postfix_expr_array_string_1  "movsx_eax,BYTE_PTR_[eax]
"
:postfix_expr_array_string_2  "sal_eax, !"
:postfix_expr_array_string_3  "
"
:postfix_expr_array_string_4  "add_eax,ebx
"
:postfix_expr_array_string_5  "ERROR in postfix_expr
Missing ]
"
:postfix_expr_array_string_6  ""


;; ceil_log2 function
;; Receives int a in EAX
;; Performs log2 on A and
;; Returns result in EAX
;; Uses EBX for INT A and ECX for INT RESULT
:ceil_log2
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOADI32_ECX %0                              ; RESULT = 0

    COPY_EAX_to_EBX                             ; put A in right place
    SUBI8_EAX !1                                ; (A - 1)
    AND_EAX_EBX                                 ; A & (A - 1)
    CMPI8_EAX !0                                ; IF 0 == (A & (A - 1))
    JNE32 %ceil_log2_iter                       ; Starting from -1

    LOADI32_ECX %-1                             ; RESULT = -1

:ceil_log2_iter
    CMPI8_EBX !0                                ; IF A > 0
    JLE32 %ceil_log2_done                       ; Otherwise be done

    ADDI8_ECX !1                                ; RESULT = RESULT + 1
    SHRI8_EBX !1                                ; A = A >> 1
    JMP32 %ceil_log2_iter                       ; Keep looping

:ceil_log2_done
    COPY_ECX_to_EAX                             ; Return RESULT
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; postfix_expr_arrow function
;; Receives nothing
;; Returns nothing
;; Emits a bunch and updates current_target
;; Uses EBX for struct type* I
:postfix_expr_arrow
    PUSH_EBX                                    ; Protect EBX
    LOADI32_EAX &postfix_expr_arrow_string_0    ; Using "# looking up offset\n"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOAD32_EBX_from_EAX_Immediate8 !8           ; Using global_token->S
    LOAD32_Absolute32_eax &current_target       ; Using current_target
    CALL32 %lookup_member                       ; lookup_member(current_target, global_token->s)
    COPY_EAX_to_EBX                             ; struct type* I = lookup_member(current_target, global_token->s)

    LOAD32_EAX_from_EAX_Immediate8 !20          ; I->TYPE
    STORE32_Absolute32_eax &current_target      ; current_target = I->TYPE

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOAD32_EAX_from_EBX_Immediate8 !8           ; I->OFFSET
    CMPI8_EAX !0                                ; IF 0 != I->OFFSET
    JE32 %postfix_expr_arrow_first              ; Then we don't need to do an offset

    ;; Deal with needing an offset
    LOADI32_EAX &postfix_expr_arrow_string_1    ; Using "# -> offset calculation\nmov_ebx, %"
    CALL32 %emit_out                            ; Emit it

    LOAD32_EAX_from_EBX_Immediate8 !8           ; I->OFFSET
    CALL32 %numerate_number                     ; Convert to string
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &postfix_expr_arrow_string_2    ; Using "\nadd_eax,ebx\n"
    CALL32 %emit_out                            ; Emit it

:postfix_expr_arrow_first
    LOAD32_EAX_from_EBX_Immediate8 !4           ; I->SIZE
    CMPI8_EAX !4                                ; IF I->SIZE >= 4
    JL32 %postfix_expr_arrow_done               ; Otherwise be done

    ;; Last chance for load
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EBX_from_EAX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &equal                          ; Using "="
    CALL32 %match                               ; IF global_token->S == "="
    CMPI8_EAX !0                                ; Then we have assignment and should not load
    JE32 %postfix_expr_arrow_done               ; Be done

    ;; Deal with load case
    LOADI32_EAX &postfix_expr_arrow_string_3    ; Using "mov_eax,[eax]\n"
    CALL32 %emit_out                            ; Emit it

:postfix_expr_arrow_done
    POP_EBX                                     ; Restore EBX
    RETURN

:postfix_expr_arrow_string_0  "# looking up offset
"
:postfix_expr_arrow_string_1  "# -> offset calculation
mov_ebx, %"
:postfix_expr_arrow_string_2  "
add_eax,ebx
"
:postfix_expr_arrow_string_3  "mov_eax,[eax]
"


;; primary_expr function
;; Receives nothing
;; Returns nothing
:primary_expr
    PUSH_EBX                                    ; Protect EBX

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EBX_from_EAX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &sizeof_string                  ; Using "sizeof"
    CALL32 %match                               ; See if match
    CMPI8_EAX !0                                ; IF match
    JNE32 %primary_expr_neg                     ; Otherwise try negatives

    ;; Deal with sizeof
    CALL32 %unary_expr_sizeof                   ; Lets do this
    JMP32 %primary_expr_done                    ; Be done

:primary_expr_neg
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !45                               ; IF global_token->S[0] == "-"
    JNE32 %primary_expr_not                     ; Otherwise try logical NOT

    ;; Deal with negative numbers
    LOADI32_EAX &primary_expr_string_0          ; Using "mov_eax, %0\n"
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &postfix_expr                   ; Passing postfix_expr
    CALL32 %common_recursion                    ; Get what it is notting

    LOADI32_EAX &primary_expr_string_1          ; Using "sub_ebx,eax\nmov_eax,ebx\n"
    CALL32 %emit_out                            ; Emit it
    JMP32 %primary_expr_done                    ; Be done

:primary_expr_not
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !33                               ; IF global_token->S[0] == "!"
    JNE32 %primary_expr_bin                     ; Otherwise try '~'

    ;; Deal with logical not
    LOADI32_EAX &primary_expr_string_2          ; Using "mov_eax, %1\n"
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &postfix_expr                   ; Passing postfix_expr
    CALL32 %common_recursion                    ; Get what it is notting

    LOADI32_EAX &primary_expr_string_3          ; Using "xor_eax,ebx\n"
    CALL32 %emit_out                            ; Emit it
    JMP32 %primary_expr_done                    ; Be done

:primary_expr_bin
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !126                              ; IF global_token->S[0] == "~"
    JNE32 %primary_expr_paren                   ; Otherwise try paren

    ;; Deal with binary NOT
    LOADI32_EAX &postfix_expr                   ; Passing postfix_expr
    CALL32 %common_recursion                    ; Get what it is notting
    LOADI32_EAX &primary_expr_string_4          ; Using "not_eax\n"
    CALL32 %emit_out                            ; Emit it
    JMP32 %primary_expr_done                    ; Be done

:primary_expr_paren
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !40                               ; IF global_token->S[0] == "("
    JNE32 %primary_expr_ch                      ; Otherwise try char

    ;; deal with nesting
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT
    CALL32 %expression                          ; Lets recurse
    LOADI32_EAX &primary_expr_string_5          ; Using "Error in Primary expression\nDidn't get )\n"
    LOADI32_EBX &close_paren                    ; Using ")"
    CALL32 %require_match                       ; Make sure we have it
    JMP32 %primary_expr_done                    ; Be done

:primary_expr_ch
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !39                               ; Using "'"
    JNE32 %primary_expr_str                     ; Otherwise try string

    ;; Deal with chars
    CALL32 %primary_expr_char                   ; Handle that char
    JMP32 %primary_expr_done                    ; Be done

:primary_expr_str
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !34                               ; Using '\"'
    JNE32 %primary_expr_var                     ; Otherwise try a variable

    ;; Deal with strings
    CALL32 %primary_expr_string                 ; Handle that string
    JMP32 %primary_expr_done                    ; Be done

:primary_expr_var
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; Make it useful
    LOADI32_EBX &primary_expr_string_6          ; Using "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
    CALL32 %In_Set                              ; See if we have a match
    CMPI8_EAX !1                                ; IF match
    JNE32 %primary_expr_num                     ; otherwise try number

    ;; Deal with variables
    CALL32 %primary_expr_variable               ; Deal with variable
    JMP32 %primary_expr_done                    ; Be done

:primary_expr_num
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; Make it useful
    LOADI32_EBX &primary_expr_string_7          ; Using "0123456789"
    CALL32 %In_Set                              ; See if we have a match
    CMPI8_EAX !1                                ; IF match
    JNE32 %primary_expr_fail                    ; otherwise we failed hard

    ;; Deal with numbers
    CALL32 %primary_expr_number                 ; Collect the number
    JMP32 %primary_expr_done                    ; Be done

:primary_expr_fail
    ;; looks like we hit bad input
    ;; abort before it gets bad
    CALL32 %primary_expr_failure                ; No match means failure
:primary_expr_done
    POP_EBX                                     ; Restore EBX
    RETURN

:primary_expr_string_0  "mov_eax, %0
"
:primary_expr_string_1  "sub_ebx,eax
mov_eax,ebx
"
:primary_expr_string_2  "mov_eax, %1
"
:primary_expr_string_3  "xor_eax,ebx
"
:primary_expr_string_4  "not_eax
"
:primary_expr_string_5  "Error in Primary expression
Didn't get )
"
:primary_expr_string_6  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"
:primary_expr_string_7  "0123456789"


;; primary_expr_variable function
;; Receives nothing
;; Returns nothing
;; Walks global and updates output
;; Uses EAX for struct token_list* a and ECX for char* S
:primary_expr_variable
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_ECX_from_EAX_Immediate8 !8           ; S = global_token->S
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    COPY_ECX_to_EAX                             ; Using S
    LOAD32_Absolute32_ebx &global_constant_list ; Using global_constant_list
    CALL32 %sym_lookup                          ; sym_lookup(s, global_constant_list)
    CMPI8_EAX !0                                ; IF NULL == sym_lookup(s, global_constant_list)
    JE32 %primary_expr_variable_local           ; Try locals next

    ;; Deal with constant load
    LOAD32_EBX_from_EAX_Immediate8 !16          ; a->ARGS
    LOADI32_EAX &primary_expr_variable_string_2 ; Using "mov_eax, %"
    CALL32 %emit_out                            ; Emit it

    LOAD32_EAX_from_EBX_Immediate8 !8           ; a->ARGS->S
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &primary_expr_variable_string_1 ; Using "\n"
    CALL32 %emit_out                            ; Emit it
    JMP32 %primary_expr_variable_done           ; Be done

:primary_expr_variable_local
    COPY_ECX_to_EAX                             ; Using S
    LOAD32_Absolute32_ebx &function             ; Using function
    LOAD32_EBX_from_EBX_Immediate8 !4           ; function->locals
    CALL32 %sym_lookup                          ; sym_lookup(s, function->locals)
    CMPI8_EAX !0                                ; IF NULL == sym_lookup(s, function->locals)
    JE32 %primary_expr_variable_arguments       ; try arguments next

    ;; Deal with local load
    CALL32 %variable_load                       ; Collect it
    JMP32 %primary_expr_variable_done           ; Be done

:primary_expr_variable_arguments
    COPY_ECX_to_EAX                             ; Using S
    LOAD32_Absolute32_ebx &function             ; Using function
    LOAD32_EBX_from_EBX_Immediate8 !16          ; function->args
    CALL32 %sym_lookup                          ; sym_lookup(s, function->args)
    CMPI8_EAX !0                                ; IF NULL == sym_lookup(s, global_constant_list)
    JE32 %primary_expr_variable_function        ; try functions next

    ;; Deal with argument load
    CALL32 %variable_load                       ; Collect it
    JMP32 %primary_expr_variable_done           ; Be done

:primary_expr_variable_function
    COPY_ECX_to_EAX                             ; Using S
    LOAD32_Absolute32_ebx &global_function_list ; Using global_function_list
    CALL32 %sym_lookup                          ; sym_lookup(s, global_function_list)
    CMPI8_EAX !0                                ; IF NULL == sym_lookup(s, global_function_list)
    JE32 %primary_expr_variable_global          ; try globals next

    ;; Deal with functions
    CALL32 %function_load                       ; Deal with the function
    JMP32 %primary_expr_variable_done           ; Be done

:primary_expr_variable_global
    COPY_ECX_to_EAX                             ; Using S
    LOAD32_Absolute32_ebx &global_symbol_list   ; Using global_symbol_list
    CALL32 %sym_lookup                          ; sym_lookup(s, global_symbol_list)
    CMPI8_EAX !0                                ; IF NULL == sym_lookup(s, global_symbol_list)
    JE32 %primary_expr_variable_error           ; Give up

    ;; Deal with globals
    CALL32 %global_load                         ; Collect that global
    JMP32 %primary_expr_variable_done           ; Be done

:primary_expr_variable_error
    LOADI32_EAX %2                              ; Using standard error
    STORE32_Absolute32_eax &Output_file         ; write to standard error
;   CALL32 %line_error                          ; Write useful debug info
    COPY_ECX_to_EAX                             ; put S in the right place
    CALL32 %File_Print                          ; print it

    LOADI32_EAX &primary_expr_variable_string_0 ; Ending string
    CALL32 %File_Print                          ; print it
    JMP32 %Exit_Failure                         ; Abort Hard

:primary_expr_variable_done
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:primary_expr_variable_string_0  " is not a defined symbol
"
:primary_expr_variable_string_1  "
"
:primary_expr_variable_string_2  "mov_eax, %"


;; function_call function
;; Receives char* S in EAX and int BOOL in EBX
;; Builds stack frames before and tears them down after function calls
;; Uses ECX for char* S, EDX for int BOOL, ESI for PASSED
:function_call
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    PUSH_ESI                                    ; Protect ESI
    COPY_EAX_to_ECX                             ; Put S in place
    COPY_EBX_to_EDX                             ; Put BOOL in place
    LOADI32_ESI %0                              ; PASSED = 0

    LOADI32_EAX &function_call_string_0         ; Using "ERROR in process_expression_list\nNo ( was found\n"
    LOADI32_EBX &open_paren                     ; Using "("
    CALL32 %require_match                       ; Make sure we have it

    LOADI32_EAX &function_call_string_1         ; Using "push_edi\t# Prevent overwriting in recursion\n"
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &function_call_string_2         ; Using "push_ebp\t# Protect the old base pointer\n"
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &function_call_string_3         ; Using "mov_edi,esp\t# Copy new base pointer\n"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !41                               ; IF global_token->S[0] == ")"
    JE32 %function_call_gen_done                ; Then no arguments to send

    ;; looks like we have arguments to collect
    CALL32 %expression                          ; Collect the argument

    LOADI32_EAX &function_call_string_4         ; Using "push_eax\t#_process_expression1\n"
    CALL32 %emit_out                            ; Emit it
    LOADI32_ESI %1                              ; PASSED = 1

:function_call_gen_iter
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !44                               ; IF global_token->S[0] == ","
    JNE32 %function_call_gen_done               ; Otherwise we are done

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    CALL32 %expression                          ; Collect the argument

    LOADI32_EAX &function_call_string_5         ; Using "push_eax\t#_process_expression2\n"
    CALL32 %emit_out                            ; Emit it
    ADDI8_ESI !1                                ; PASSED = PASSED + 1
    JMP32 %function_call_gen_iter               ; Keep trying

:function_call_gen_done
    ;; All is collected
    LOADI32_EAX &function_call_string_6         ; Using "ERROR in process_expression_list\nNo ) was found\n"
    LOADI32_EBX &close_paren                    ; Using ")"
    CALL32 %require_match                       ; Make sure we have it

    CMPI8_EDX !0                                ; IF(BOOL == TRUE)
    JNE32 %function_call_static                 ; Otherwise it is a static call

    ;; Deal with a passed function pointer
    LOADI32_EAX &function_call_string_7         ; Using "lea_eax,[ebp+DWORD] %"
    CALL32 %emit_out                            ; Emit it

    COPY_ECX_to_EAX                             ; Using S
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &function_call_string_8         ; Using "\nmov_eax,[eax]\n"
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &function_call_string_9         ; Using "mov_ebp,edi\n"
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &function_call_string_10        ; Using "call_eax\n"
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &function_call_string_13        ; Using "pop_ebx\t# _process_expression_locals\n"
    JMP32 %function_call_cleanup                ; Clean up

:function_call_static
    ;; Deal with fixed function name
    LOADI32_EAX &function_call_string_9         ; Using "mov_ebp,edi\n"
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &function_call_string_11        ; Using "call %FUNCTION_"
    CALL32 %emit_out                            ; Emit it

    COPY_ECX_to_EAX                             ; Using S
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &function_call_string_12        ; Using "\n"
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &function_call_string_13        ; Using "pop_ebx\t# _process_expression_locals\n"

:function_call_cleanup
    CMPI8_ESI !0                                ; IF PASSED > 0
    JLE32 %function_call_done                   ; Otherwise be done

    ;; The desired string is already in EAX
    CALL32 %emit_out                            ; Emit it

    SUBI8_ESI !1                                ; PASSED = PASSED - 1
    JMP32 %function_call_cleanup                ; Keep going

:function_call_done
    LOADI32_EAX &function_call_string_14        ; Using "pop_ebp\t# Restore old base pointer\n"
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &function_call_string_15        ; Using "pop_edi\t# Prevent overwrite\n"
    CALL32 %emit_out                            ; Emit it

    POP_ESI                                     ; Restore ESI
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:function_call_string_0  "ERROR in process_expression_list
No ( was found
"
:function_call_string_1  "push_edi  # Prevent overwriting in recursion
"
:function_call_string_2  "push_ebp  # Protect the old base pointer
"
:function_call_string_3  "mov_edi,esp   # Copy new base pointer
"
:function_call_string_4  "push_eax  #_process_expression1
"
:function_call_string_5  "push_eax  #_process_expression2
"
:function_call_string_6  "ERROR in process_expression_list
No ) was found
"
:function_call_string_7  "lea_eax,[ebp+DWORD] %"
:function_call_string_8  "
mov_eax,[eax]
"
:function_call_string_9  "mov_ebp,edi
"
:function_call_string_10 "call_eax
"
:function_call_string_11 "call %FUNCTION_"
:function_call_string_12 "
"
:function_call_string_13 "pop_ebx   # _process_expression_locals
"
:function_call_string_14 "pop_ebp   # Restore old base pointer
"
:function_call_string_15 "pop_edi   # Prevent overwrite
"


;; variable_load function
;; Receives struct token_list* A in EAX
;; Returns nothing
;; Updates output and current_target
;; Uses ECX for A
:variable_load
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    COPY_EAX_to_ECX                             ; Protect A

    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &open_paren                     ; Using "("
    CALL32 %match                               ; IF global_token->S == "("
    CMPI8_EAX !0                                ; Then it might be a function
    JNE32 %variable_load_regular                ; Otherwise it is regular

    LOAD32_EBX_from_ECX_Immediate8 !12          ; A->TYPE
    LOAD32_EBX_from_EBX_Immediate8 !24          ; A->TYPE->NAME
    LOADI32_EAX &type_function_name             ; Using "FUNCTION"
    CALL32 %match                               ; IF A->TYPE->NAME == "FUNCTION"
    CMPI8_EAX !0                                ; Then it must be a function
    JNE32 %variable_load_regular                ; otherwise just another regular

    ;; deal with function
    LOAD32_EAX_from_ECX_Immediate8 !16          ; A->DEPTH
    CALL32 %numerate_number                     ; Convert to string
    LOADI32_EBX %0                              ; pass 0 for true
    CALL32 %function_call                       ; Create the function call
    JMP32 %variable_load_done                   ; Be done

:variable_load_regular
    LOAD32_EAX_from_ECX_Immediate8 !12          ; A->TYPE
    STORE32_Absolute32_eax &current_target      ; current_target = A->TYPE

    LOADI32_EAX &variable_load_string_0         ; Using "lea_eax,[ebp+DWORD] %"
    CALL32 %emit_out                            ; Emit it

    LOAD32_EAX_from_ECX_Immediate8 !16          ; A->DEPTH
    CALL32 %numerate_number                     ; Convert to string
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &variable_load_string_1         ; Using "\n"
    CALL32 %emit_out                            ; Emit it

    ;; Check for special case of assignment
    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &equal                          ; Using "="
    CALL32 %match                               ; IF global_token->S == "="
    CMPI8_EAX !0                                ; Then we skip loading
    JE32 %variable_load_done                    ; And be done

    ;; Deal with common case
    LOADI32_EAX &variable_load_string_2         ; Using "mov_eax,[eax]\n"
    CALL32 %emit_out                            ; Emit it

:variable_load_done
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:variable_load_string_0  "lea_eax,[ebp+DWORD] %"
:variable_load_string_1  "
"
:variable_load_string_2  "mov_eax,[eax]
"


;; function_load function
;; Receives struct token_list* a in EAX
;; Returns nothing
;; Uses ECX to hold A->S
:function_load
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOAD32_EAX_from_EAX_Immediate8 !8           ; A->S
    COPY_EAX_to_ECX                             ; Protect A->S
    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &open_paren                     ; Using "("
    CALL32 %match                               ; IF global_token->S == "("
    CMPI8_EAX !0                                ; The we need to do a function call
    JNE32 %function_load_regular                ; Otherwise just load it's address

    ;; Deal with function call
    COPY_ECX_to_EAX                             ; Using A->S
    LOADI32_EBX %1                              ; Using FALSE
    CALL32 %function_call                       ; Deal with it
    JMP32 %function_load_done                   ; Be done

:function_load_regular
    LOADI32_EAX &function_load_string_0         ; Using "mov_eax, &FUNCTION_"
    CALL32 %emit_out                            ; Emit it

    COPY_ECX_to_EAX                             ; Using A->S
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &function_load_string_1         ; Using "\n"
    CALL32 %emit_out                            ; Emit it

:function_load_done
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:function_load_string_0  "mov_eax, &FUNCTION_"
:function_load_string_1  "
"


;; global_load function
;; Receives struct token_list* A in EAX
;; Returns nothing
;; Uses EBX to hold A->S
:global_load
    PUSH_EBX                                    ; Protect EBX
    COPY_EAX_to_EBX                             ; Set as A
    LOAD32_EBX_from_EBX_Immediate8 !8           ; Set as A->S

    LOAD32_EAX_from_EAX_Immediate8 !12          ; A->TYPE
    STORE32_Absolute32_eax &current_target      ; current_target = A->TYPE

    LOADI32_EAX &global_load_string_0           ; Using "mov_eax, &GLOBAL_"
    CALL32 %emit_out                            ; Emit it

    COPY_EBX_to_EAX                             ; Using A->S
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &global_load_string_1           ; Using "\n"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &equal                          ; "="
    CALL32 %match                               ; IF global_token->S == "="
    CMPI8_EAX !0                                ; We need to skip for assignment
    JE32 %global_load_done                      ; and be done

    ;; Otherwise we are loading the contents
    LOADI32_EAX &global_load_string_2           ; Using "mov_eax,[eax]\n"
    CALL32 %emit_out                            ; Emit it

:global_load_done
    POP_EBX                                     ; Restore EBX
    RETURN

:global_load_string_0  "mov_eax, &GLOBAL_"
:global_load_string_1  "
"
:global_load_string_2  "mov_eax,[eax]
"


;; sym_lookup function
;; Receives char* S in EAX and struct token_list* symbol_list in EBX
;; Uses I->S in EAX, S in EBX and I in ECX
;; Returns match or NULL
:sym_lookup
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    COPY_EBX_to_ECX                             ; I = symbol_list
    COPY_EAX_to_EBX                             ; Put S in the right place
:sym_lookup_iter
    CMPI8_ECX !0                                ; IF NULL == I
    JE32 %sym_lookup_done                       ; We failed to find match

    LOAD32_EAX_from_ECX_Immediate8 !8           ; Using I->S
    CALL32 %match                               ; IF I->S == S
    CMPI8_EAX !0                                ; then be done
    JE32 %sym_lookup_done                       ; Failed

    LOAD32_ECX_from_ECX                         ; I = I->NEXT
    JMP32 %sym_lookup_iter                      ; otherwise keep looping

:sym_lookup_done
    COPY_ECX_to_EAX                             ; Return I
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; primary_expr_number function
;; Receives nothing
;; Returns nothing
;; Simply uses current global token to update output and then steps to next global_token
:primary_expr_number
    LOADI32_EAX &primary_expr_number_string_0   ; Using "mov_eax, %"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    CALL32 %emit_out                            ; Emit it

    LOADI32_EAX &primary_expr_number_string_1   ; Using "\n"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT
    RETURN

:primary_expr_number_string_0  "mov_eax, %"
:primary_expr_number_string_1  "
"


;; primary_expr_string function
;; receives nothing
;; Returns nothing
;; creates entries for string and calls to generate string output
;; uses ECX for char* number_string
:primary_expr_string
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOAD32_Absolute32_ebx &current_count        ; Using current_count
    COPY_EBX_to_EAX                             ; And putting it in the right place
    CALL32 %numerate_number                     ; Get the string
    COPY_EAX_to_ECX                             ; protect number_string

    ADDI8_EBX !1                                ; current_count + 1
    STORE32_Absolute32_ebx &current_count       ; current_count = current_count + 1

    LOADI32_EAX &primary_expr_string_string_0   ; Using "mov_eax, &STRING_"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    COPY_ECX_to_EBX                             ; Put number_string in the right place
    CALL32 %uniqueID_out                        ; Make it unique

    ;; Generate the target
    LOADI32_EAX &primary_expr_string_string_1   ; Using ":STRING_"
    LOAD32_Absolute32_ebx &strings_list         ; Using strings_list
    CALL32 %emit                                ; Emit it
    COPY_EAX_to_EBX                             ; put new strings_list in place

    LOAD32_Absolute32_eax &function             ; Using function
    LOAD32_EAX_from_EAX_Immediate8 !8           ; function->S
    CALL32 %uniqueID                            ; Make it unique
    COPY_EAX_to_EBX                             ; put new strings_list in place

    ;; Parse the string
    LOAD32_Absolute32_eax &global_token         ; Using global token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    CALL32 %parse_string                        ; convert to useful form
    CALL32 %emit                                ; Emit it
    STORE32_Absolute32_eax &strings_list        ; Update Strings _list

    LOAD32_Absolute32_eax &global_token         ; Using global token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:primary_expr_string_string_0  "mov_eax, &STRING_"
:primary_expr_string_string_1  ":STRING_"


;; primary_expr_char function
;; Receives nothing
;; Returns nothing
;; Updates output_list using global_token
:primary_expr_char
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOADI32_EAX &primary_expr_char_string_0     ; Using "mov_eax, %"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &global_token         ; Using global token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    ADDI8_EAX !1                                ; global_token->S + 1
    CALL32 %escape_lookup                       ; Get the char
    CALL32 %numerate_number                     ; Convert to string
    CALL32 %emit_out                            ; emit it

    LOADI32_EAX &primary_expr_char_string_1     ; Using "\n"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &global_token         ; Using global token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:primary_expr_char_string_0  "mov_eax, %"
:primary_expr_char_string_1  "
"


;; primary_expr_failure function
;; Receives nothing
;; Does not return but aborts hard
;; Complains about the bad input
:primary_expr_failure
;   CALL32 %line_error                          ; Get line of issue
    LOADI32_EAX %2                              ; Using Standard error
    STORE32_Absolute32_eax &Output_file         ; write to standard error
    LOADI32_EAX &primary_expr_failure_string_0  ; Using "Received "
    CALL32 %File_Print                          ; Print it

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    CALL32 %File_Print                          ; Print it

    LOADI32_EAX &primary_expr_failure_string_1  ; Using " in primary_expr\n"
    CALL32 %File_Print                          ; Print it
    JMP32 %Exit_Failure                         ; Abort Hard

:primary_expr_failure_string_0  "Received "
:primary_expr_failure_string_1  " in primary_expr
"


;; general_recursion function
;; Receives FUNCTION F in EAX, char* S in EBX, char* name in ECX and FUNCTION iterate in EDX
;; Returns nothing
;; Uses ECX for char* S, EDX for FUNCTION iterate and EBP for FUNCTION F
;; But generally recurses a shitload
:general_recursion
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    PUSH_EBP                                    ; Protect EBP
    COPY_EAX_to_EBP                             ; Protect F
    COPY_ECX_to_EAX                             ; Put name in the right place
    COPY_EBX_to_ECX                             ; Protect S

    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    CALL32 %match                               ; IF match(name, global_token->s)
    CMPI8_EAX !0                                ; If true we do
    JNE32 %general_recursion_done               ; Otherwise skip it

    ;; Deal with the recursion
    COPY_EBP_to_EAX                             ; Put F in the right place
    CALL32 %common_recursion                    ; Recurse

    COPY_ECX_to_EAX                             ; Put S in the right place
    CALL32 %emit_out                            ; Emit it

    COPY_EDX_to_EAX                             ; Put iterate in the right place
    CALL_EAX                                    ; Down the rabbit hole

:general_recursion_done
    POP_EBP                                     ; Restore EBP
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; promote_type function
;; Receives struct type* a in EAX and struct type* b in EBX
;; Returns the most recent type in EAX
;; Uses EAX for struct type* I, ECX for struct type* A and EDX for struct type* B
:promote_type
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    CMPI8_EBX !0                                ; IF NULL == B
    JE32 %promote_type_done                     ; Just return A

    COPY_EAX_to_ECX                             ; Put A in place
    COPY_EBX_to_EDX                             ; Put B in place
    COPY_EBX_to_EAX                             ; IF NULL == A
    CMPI8_ECX !0                                ; Then we just return B
    JE32 %promote_type_done                     ; Be done

    ;; Looks like we need to walk the list
    LOAD32_ECX_from_ECX_Immediate8 !24          ; A->NAME
    LOAD32_EDX_from_EDX_Immediate8 !24          ; B->NAME
    LOAD32_Absolute32_eax &global_types         ; I = global_types
:promote_type_iter
    CMPI8_EAX !0                                ; IF NULL == I
    JE32 %promote_type_done                     ; Just be done

    LOAD32_EBX_from_EAX_Immediate8 !24          ; I->NAME
    CMP_ECX_EBX                                 ; IF(A->NAME == I->NAME)
    JE32 %promote_type_done                     ; Be done

    CMP_EBX_EDX                                 ; IF(B->NAME == I->NAME)
    JE32 %promote_type_done                     ; Be done

    LOAD32_EBX_from_EAX_Immediate8 !12          ; I->INDIRECT
    LOAD32_EBX_from_EBX_Immediate8 !24          ; I->INDIRECT->NAME

    CMP_ECX_EBX                                 ; IF(A->NAME == I->INDIRECT->NAME)
    JE32 %promote_type_done                     ; Be done

    CMP_EBX_EDX                                 ; IF(B->NAME == I->INDIRECT->NAME)
    JE32 %promote_type_done                     ; Be done

    LOAD32_EAX_from_EAX                         ; I = I->NEXT
    JMP32 %promote_type_iter                    ; Keep going

:promote_type_done
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; common_recursion function
;; Receives FUNCTION F in EAX
;; Returns Nothing
;; Walks global_token list and update output_list
;; Updates current_target
;; Uses EBX to hold FUNCTION F and struct type* last_type
:common_recursion
    PUSH_EBX                                    ; Protect EBX
    COPY_EAX_to_EBX                             ; Put FUNCTION F safely out of the way
    LOADI32_EAX &common_recursion_string_0      ; Using "push_eax\t#_common_recursion\n"
    CALL32 %emit_out                            ; Emit it

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    COPY_EBX_to_EAX                             ; Prepare for function call
    LOAD32_Absolute32_ebx &current_target       ; Get last type
    CALL_EAX                                    ; F();
    LOAD32_Absolute32_eax &current_target       ; Get current_target
    CALL32 %promote_type                        ; get the right type
    STORE32_Absolute32_eax &current_target      ; Set new current_target

    LOADI32_EAX &common_recursion_string_1      ; Using "pop_ebx\t# _common_recursion\n"
    CALL32 %emit_out                            ; Emit it
    POP_EBX                                     ; Restore EBX
    RETURN

:common_recursion_string_0  "push_eax   #_common_recursion
"
:common_recursion_string_1  "pop_ebx    # _common_recursion
"


;; require_match function
;; Receives char* message in EAX and char* required in EBX
;; Returns nothing
;; Uses ECX to hold message and updates global_token
:require_match
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    COPY_EAX_to_ECX                             ; put the message somewhere safe
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    CALL32 %match                               ; IF required == global_token->S
    CMPI8_EAX !0                                ; we are fine
    JE32 %require_match_good                    ; otherwise pain

    ;; Deal will bad times
;   CALL32 %line_error                          ; Tell user what went wrong
    LOADI32_EAX %2                              ; Using standard error
    STORE32_Absolute32_eax &Output_file         ; write to standard error
    COPY_ECX_to_EAX                             ; using our message
    CALL32 %File_Print                          ; Print it
    JMP32 %Exit_Failure                         ; Abort HARD

:require_match_good
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->next
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->next
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; uniqueID Function
;; Receives char *S in EAX, struct token_list* l in EBX and char* num in ECX
;; Returns updated struct token_list* L in EAX
:uniqueID
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    CALL32 %emit                                ; emit(s, l)
    COPY_EAX_to_EBX                             ; Put L in correct place
    LOADI32_EAX &underline                      ; Using "_"
    CALL32 %emit                                ; emit("_", l)
    COPY_EAX_to_EBX                             ; Put L in correct place
    COPY_ECX_to_EAX                             ; Put num in correct place
    CALL32 %emit                                ; emit(num, l)
    COPY_EAX_to_EBX                             ; Put L in correct place
    LOADI32_EAX &uniqueID_string_0              ; Using "\n"
    CALL32 %emit                                ; emit("\n", l)
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:uniqueID_string_0  "
"


;; uniqueID_out function
;; Receives char* S in EAX and char* num in EBX
;; Returns nothing
:uniqueID_out
    PUSH_EAX                                    ; Protect EAX
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    COPY_EBX_to_ECX                             ; Put num in right spot
    LOAD32_Absolute32_ebx &output_list          ; Using output_list
    CALL32 %uniqueID                            ; Get updated list
    STORE32_Absolute32_eax &output_list         ; output_list = uniqueID(s, output_list, num)
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    POP_EAX                                     ; Restore EAX
    RETURN


;; emit_out function
;; Receives char* S in EAX
;; Returns nothing
;; Updates output_list
;; MUST NOT ALTER REGISTERS
:emit_out
    PUSH_EAX                                    ; Protect EAX
    PUSH_EBX                                    ; Protect EBX
    LOAD32_Absolute32_ebx &output_list          ; Using output_list
    CALL32 %emit                                ; emit it
    STORE32_Absolute32_eax &output_list         ; update it
    POP_EBX                                     ; Restore EBX
    POP_EAX                                     ; Restore EAX
    RETURN


;; emit function
;; Receives char *s in EAX and struct token_list* head in EBX
;; Returns struct token_list* T in EAX
:emit
    PUSH_ECX                                    ; Protect ECX
    COPY_EAX_to_ECX                             ; put S out of the way
    LOADI32_EAX %20                             ; sizeof(struct token_list)
    CALL32 %malloc                              ; get T
    STORE32_EBX_into_EAX                        ; t->next = head;
    STORE32_ECX_into_EAX_Immediate8 !8          ; t->s = s;
    POP_ECX                                     ; Restore ECX
    RETURN


;; escape_lookup function
;; Receives char* c in EAX
;; Returns integer value of char in EAX
;; Aborts hard if unknown escape is received
;; Uses ECX to hold char* C
:escape_lookup
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    COPY_EAX_to_ECX                             ; Put char* C in safe place
    LOAD8_al_from_ECX                           ; Load c[0]
    MOVZX_al                                    ; make it useful
    CMPI8_EAX !92                               ; If '\\' != c[0]
    JNE32 %escape_lookup_done                   ; Be done

    COPY_ECX_to_EBX                             ; Prepare for walk
    ADDI8_EBX !1                                ; increment
    LOAD8_bl_from_EBX                           ; load c[1]
    MOVZX_bl                                    ; make it useful

    CMPI8_EBX !120                              ; Check if \x??
    JE32 %escape_lookup_hex                     ; Deal with hex

    ;; Deal with \? escapes
    LOADI32_EAX %10                             ; Guess "\n"
    CMPI8_EBX !110                              ; If n
    JE32 %escape_lookup_done                    ; Be done

    LOADI32_EAX %9                              ; Guess "\t"
    CMPI8_EBX !116                              ; If t
    JE32 %escape_lookup_done                    ; Be done

    COPY_EBX_to_EAX                             ; "\\", "'" and '\"' all encode as themselves
    CMPI8_EBX !92                               ; If "\\"
    JE32 %escape_lookup_done                    ; Be done
    CMPI8_EBX !39                               ; IF "'"
    JE32 %escape_lookup_done                    ; Be done
    CMPI8_EBX !34                               ; IF '\"'
    JE32 %escape_lookup_done                    ; Be done

    LOADI32_EAX %13                             ; Guess "\r"
    CMPI8_EBX !114                              ; IF r
    JE32 %escape_lookup_done                    ; Be done

    ;; Looks like we have no clue what we are doing
    ;; Aborting hard
    LOADI32_EAX %2                              ; Using Standard error
    STORE32_Absolute32_eax &Output_file         ; write to standard error
    LOADI32_EAX &escape_lookup_string_0         ; Using "Unknown escape received: "
    CALL32 %File_Print                          ; Print it
    COPY_ECX_to_EAX                             ; Using C
    CALL32 %File_Print                          ; Print it
    LOADI32_EAX &escape_lookup_string_1         ; Using " Unable to process\n"
    CALL32 %File_Print                          ; Print it
    JMP32 %Exit_Failure                         ; Abort Hard

:escape_lookup_done
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:escape_lookup_hex
    ;; Give up on C and just assume they know what they are doing
    ADDI8_ECX !2                                ; increment
    LOAD8_al_from_ECX                           ; c[2]
    MOVZX_al                                    ; make it useful
    ADDI8_ECX !1                                ; increment
    CALL32 %char2hex                            ; Get the hex value
    SALI8_EAX !4                                ; c << 4
    LOAD8_bl_from_ECX                           ; c[3]
    MOVZX_bl                                    ; make it useful
    SWAP_EAX_EBX                                ; protect c << 4
    CALL32 %char2hex                            ; Get the hex value
    ADD_ebx_into_eax                            ; hex(c[2]) << 4 + hex(c[3])
    JMP32 %escape_lookup_done                   ; Be done

:escape_lookup_string_0  "Unknown escape received: "
:escape_lookup_string_1  " Unable to process
"


;; char2hex function
;; Receives char in EAX
;; Returns hex or aborts hard
:char2hex
    SUBI8_EAX !48                               ; Try 0-9
    CMPI8_EAX !10                               ; Otherwise fun times
    JL32 %char2hex_done                         ; Be done

    ;; Deal with A-F
    ANDI32_EAX %0xDF                            ; Unset High bit turning a-f into A-F
    SUBI8_EAX !7                                ; Shift down into position
    CMPI8_EAX !10                               ; Everything below A is bad
    JL32 %char2hex_fail                         ; And should fail
    CMPI8_EAX !16                               ; Make sure we are below F
    JL32 %char2hex_done                         ; If so be done

:char2hex_fail
    ;; Time to fail hard
    LOADI32_EAX %2                              ; Using Standard error
    STORE32_Absolute32_eax &Output_file         ; write to standard error
    LOADI32_EAX &char2hex_string_0              ; Using "Tried to print non-hex number\n"
    CALL32 %File_Print                          ; Print it
    JMP32 %Exit_Failure                         ; Abort Hard

:char2hex_done
    RETURN

:char2hex_string_0  "Tried to print non-hex number
"


;; parse_string function
;; Receives char* string in EAX
;; Returns cleaned up string
;; Protects char* string in EBX
:parse_string
    PUSH_EBX                                    ; Protect EBX
    COPY_EAX_to_EBX                             ; Protect char* string
    CALL32 %weird                               ; Determine if we have a weird string
    CMPI8_EAX !0                                ; If weird
    JE32 %parse_string_weird                    ; Deal with it

    ;; Dealing with regular string
    COPY_EBX_to_EAX                             ; Passing Char* string
    CALL32 %collect_regular_string              ; Collect it
    JMP32 %parse_string_done                    ; Be done

:parse_string_weird
    COPY_EBX_to_EAX                             ; Passing Char* string
    CALL32 %collect_weird_string                ; Collect it

:parse_string_done
    POP_EBX                                     ; Restore EBX
    RETURN


;; weird function
;; Receives char* string in EAX
;; Returns true(0) or false(1) in EAX
;; Uses ECX to hold char* string
:weird
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    COPY_EAX_to_ECX                             ; Place string in safe place
    ADDI8_ECX !1                                ; increment past the '\"'
:weird_reset
    LOAD8_al_from_ECX                           ; Load a char
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !0                                ; IF NULL == C
    JE32 %weird_false                           ; Nothing weird found

    CMPI8_EAX !92                               ; IF '\\'
    JNE32 %weird_escaped                        ; Deal with escaping

    ;; Deal with escape
    COPY_ECX_to_EAX                             ; We are passing the string
    CALL32 %escape_lookup                       ; to look it up

    ADDI8_ECX !1                                ; string = string + 1
    LOAD8_bl_from_ECX                           ; get string[1]
    MOVZX_bl                                    ; make it useful
    CMPI8_EBX !120                              ; IF 'x' == string[1]
    JNE32 %weird_escaped                        ; otherwise skip the gap

    ADDI8_ECX !2                                ; string = string + 2

:weird_escaped
    PUSH_EAX                                    ; Protect C in case we need it
    LOADI32_EBX &weird_string_0                 ; Use "\t\n !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
    CALL32 %In_Set                              ; To find if weird
    CMPI8_EAX !1                                ; IF TRUE
    POP_EAX                                     ; Restore C
    JNE32 %weird_true                           ; Then not weird

    ADDI8_ECX !1                                ; string = string + 1

    ;; Last chance for weird
    LOADI32_EBX &weird_string_1                 ; Use "\t\n\r "
    CALL32 %In_Set                              ; Check for special case
    CMPI8_EAX !1                                ; IF TRUE
    JNE32 %weird_reset                          ; Otherwise not in the special case

    ;; Deal with possible special case
    LOAD8_al_from_ECX                           ; Load string[1]
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !58                               ; IF string[1] == ":"
    JE32 %weird_true                            ; Then we hit the special case
    JMP32 %weird_reset                          ; Keep trying

:weird_done
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:weird_true
    LOADI32_EAX %0                              ; Return true
    JMP32 %weird_done                           ; Be done

:weird_false
    LOADI32_EAX %1                              ; Return false
    JMP32 %weird_done                           ; Be done

:weird_string_0  "  
 !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
:weird_string_1  '09 0A 0D 20' ; "\t\n\r "


;; collect_regular_string function
;; Receives char* string in EAX
;; Malloc and creates new string to return in EAX
;; Uses ECX for return string and EDX for passed string
:collect_regular_string
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    COPY_EAX_to_EDX                             ; Protect our passed string
    LOADI32_EAX %256                            ; We need 256bytes of storage
    CALL32 %malloc                              ; Get our new pointer
    COPY_EAX_to_ECX                             ; put it in place
    PUSH_EAX                                    ; protect until done
:collect_regular_string_reset
    LOAD8_al_from_EDX                           ; string[0]
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !0                                ; See if we hit the end
    JE32 %collect_regular_string_done           ; And be done

    CMPI8_EAX !92                               ; IF string[0] == '\\'
    JE32 %collect_regular_string_escaped        ; Deal with that mess

    ;; deal with boring char
    STORE8_al_into_Address_ECX                  ; hold_string[index] = string[0]
    ADDI8_ECX !1                                ; Increment it
    ADDI8_EDX !1                                ; Increment it
    JMP32 %collect_regular_string_reset         ; And keep going

:collect_regular_string_escaped
    COPY_EDX_to_EAX                             ; Using string
    CALL32 %escape_lookup                       ; Get the char
    STORE8_al_into_Address_ECX                  ; hold_string[index] = escape_lookup(string)
    ADDI8_EDX !1                                ; Increment it
    ADDI8_ECX !1                                ; Increment it
    LOAD8_al_from_EDX                           ; string[0]
    MOVZX_al                                    ; Make it useful
    ADDI8_EDX !1                                ; Increment it
    CMPI8_EAX !120                              ; IF 'x' == string[1]
    JNE32 %collect_regular_string_reset         ; Otherwise keep going

    ADDI8_EDX !2                                ; Increment it
    JMP32 %collect_regular_string_reset         ; Keep going

:collect_regular_string_done
    LOADI32_EAX %34                             ; Using '\"'
    STORE8_al_into_Address_ECX                  ; hold_string[index] = '\"'
    ADDI8_ECX !1                                ; Increment it
    LOADI32_EAX %10                             ; Using "\n"
    STORE8_al_into_Address_ECX                  ; hold_string[index] = '\n'
    POP_EAX                                     ; Return our new string
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; collect_weird_string function
;; Receives char* string in EAX
;; Mallocs and returns char* hold in EAX
;; Uses ECX for char* hold and EDX for char* string
:collect_weird_string
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    COPY_EAX_to_EDX                             ; Protect our passed string
    LOADI32_EAX %512                            ; We need 512bytes of storage
    CALL32 %malloc                              ; Get our new pointer
    COPY_EAX_to_ECX                             ; put it in place
    PUSH_EAX                                    ; protect until done

    LOADI32_EAX %39                             ; Using "'"
    STORE8_al_into_Address_ECX                  ; hold_string[index] = "'"
    ADDI8_ECX !1                                ; Increment it
    ADDI8_EDX !1                                ; Increment it
:collect_weird_string_reset
    LOAD8_al_from_EDX                           ; Read a byte
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !0                                ; IF NULL == string[0]
    JE32 %collect_weird_string_done             ; Be done

    LOADI32_EAX %32                             ; Using ' '
    STORE8_al_into_Address_ECX                  ; hold_string[index] = ' '
    ADDI8_ECX !1                                ; Increment it

    COPY_EDX_to_EAX                             ; Using string
    CALL32 %escape_lookup                       ; Get the char
    CALL32 %hex8                                ; Update ECX

    LOAD8_al_from_EDX                           ; Read a byte
    MOVZX_al                                    ; Make it useful
    ADDI8_EDX !1                                ; Increment it
    CMPI8_EAX !92                               ; IF string[0] == '\\'
    JNE32 %collect_weird_string_reset           ; Otherwise keep going

    LOAD8_al_from_EDX                           ; Read a byte
    MOVZX_al                                    ; Make it useful
    ADDI8_EDX !1                                ; Increment it
    CMPI8_EAX !120                              ; IF 'x' == string[1]
    JNE32 %collect_weird_string_reset           ; Otherwise keep going

    ADDI8_EDX !2                                ; Increment it
    JMP32 %collect_weird_string_reset           ; Keep going

:collect_weird_string_done
    LOADI32_EAX %32                             ; Using ' '
    STORE8_al_into_Address_ECX                  ; hold_string[index] = ' '
    ADDI8_ECX !1                                ; Increment it
    LOADI32_EAX %48                             ; Using '0'
    STORE8_al_into_Address_ECX                  ; hold_string[index] = '0'
    ADDI8_ECX !1                                ; Increment it
    STORE8_al_into_Address_ECX                  ; hold_string[index] = '0'
    ADDI8_ECX !1                                ; Increment it
    LOADI32_EAX %39                             ; Using "'"
    STORE8_al_into_Address_ECX                  ; hold_string[index] = "'"
    ADDI8_ECX !1                                ; Increment it
    LOADI32_EAX %10                             ; Using "\n"
    STORE8_al_into_Address_ECX                  ; hold_string[index] = '\n'
    POP_EAX                                     ; Return our new string
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; HEX to ascii routine
;; Receives INT in EAX and CHAR* in ECX
;; Stores ascii of INT in CHAR*
;; Returns only modifying EAX and ECX
:hex8
    PUSH_EAX                                    ; Protect bottom nibble
    SHRI8_EAX !4                                ; do high nibble first
    CALL32 %hex4                                ; Store it
    POP_EAX                                     ; do low nibble
:hex4
    ANDI32_EAX %0xF                             ; isolate nibble
    ADDI8_EAX !48                               ; convert to ascii
    CMPI8_EAX !57                               ; valid digit?
    JBE8 !hex1                                  ; yes
    ADDI8_EAX !7                                ; use alpha range
:hex1
    STORE8_al_into_Address_ECX                  ; store result
    ADDI8_ECX !1                                ; next position
    RETURN


;; type_name function
;; Receives nothing
;; Returns type_size in EAX
;; Uses ECX for STRUCT TYPE* RET
:type_name
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &struct                         ; Using "struct"
    CALL32 %match                               ; IF global_token->S == "struct"
    COPY_EAX_to_ECX                             ; Protect structure
    CMPI8_EAX !0                                ; need to skip over "struct"
    JNE32 %type_name_native                     ; otherwise keep going

    ;; Deal with possible STRUCTs
    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX                         ; global_token->next
    STORE32_Absolute32_ebx &global_token        ; global_token = global_token->next
    LOAD32_EAX_from_EBX_Immediate8 !8           ; global_token->S
    LOAD32_Absolute32_ebx &global_types         ; get all known types
    CALL32 %lookup_type                         ; Find type if possible
    COPY_EAX_to_ECX                             ; Set ret

    CMPI8_EAX !0                                ; IF NULL == ret
    JNE32 %type_name_common                     ; We have to create struct

    ;; Create a struct
    CALL32 %create_struct                       ; Create a new struct
    LOADI32_ECX %0                              ; We wish to return NULL
    JMP32 %type_name_done                       ; be done

:type_name_native
    ;; Deal only with native types
    COPY_EBX_to_EAX                             ; Put global_token->S in the right place
    LOAD32_Absolute32_ebx &global_types         ; get all known types
    CALL32 %lookup_type                         ; Find the type if possible
    COPY_EAX_to_ECX                             ; Set ret

    CMPI8_EAX !0                                ; IF NULL == ret
    JNE32 %type_name_common                     ; We need to abort hard

    ;; Aborting hard
    LOADI32_EAX %2                              ; Using Standard error
    STORE32_Absolute32_eax &Output_file         ; write to standard error
    LOADI32_EAX &type_name_string_0             ; Print header
    CALL32 %File_Print                          ; Print it

    LOAD32_Absolute32_eax &global_token         ; Using global token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    CALL32 %File_Print                          ; Print it

    LOADI32_EAX &type_name_string_1             ; Print footer
    CALL32 %File_Print                          ; Print it

;   CALL32 %line_error                          ; Give details
    JMP32 %Exit_Failure                         ; Abort

:type_name_common
    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX                         ; global_token->next
    STORE32_Absolute32_ebx &global_token        ; global_token = global_token->next

:type_name_iter
    LOAD32_EAX_from_EBX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; make it useful
    CMPI8_EAX !42                               ; IF global_token->S[0] == '*'
    JNE32 %type_name_done                       ; recurse

    ;; Deal with char**
    LOAD32_ECX_from_ECX_Immediate8 !12          ; ret = ret->indirect
    LOAD32_Absolute32_ebx &global_token         ; Using global_token
    LOAD32_EBX_from_EBX                         ; global_token->next
    STORE32_Absolute32_ebx &global_token        ; global_token = global_token->next
    JMP32 %type_name_iter                       ; keep looping

:type_name_done
    COPY_ECX_to_EAX                             ; put ret in the right place
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:type_name_string_0  "Unknown type "
:type_name_string_1  "
"


;; lookup_type function
;; Receives char* s in EAX and struct type* start in EBX
;; Returns struct type* in EAX
;; Uses EBX for S and ECX for I
:lookup_type
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    COPY_EBX_to_ECX                             ; I = Start
    COPY_EAX_to_EBX                             ; Put S in place
:lookup_type_iter
    CMPI8_ECX !0                                ; Check if I == NULL
    JE32 %lookup_type_done                      ; return NULL

    LOAD32_EAX_from_ECX_Immediate8 !24          ; I->NAME
    CALL32 %match                               ; Check if matching
    CMPI8_EAX !0                                ; IF I->NAME == S
    JE32 %lookup_type_done                      ; return it

    LOAD32_ECX_from_ECX                         ; Otherwise I = I->NEXT
    JMP32 %lookup_type_iter                     ; And keep looping

:lookup_type_done
    COPY_ECX_to_EAX                             ; return either I or NULL
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; create_struct function
;; Receives nothing
;; Returns nothing
;; Uses global_token to malloc a struct's definition
;; Uses ECX for int OFFSET, EDX for struct type* head, EBP for struct type* I,
;; EDI for member_size (Which is passed) and ESI for LAST
;; EAX and EBX are used for scratch
:create_struct
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    PUSH_EBP                                    ; Protect EBP
    PUSH_EDI                                    ; Protect EDI
    PUSH_ESI                                    ; Protect ESI
    LOADI32_ECX %0                              ; OFFSET = 0
    LOADI32_EDI %0                              ; member_size = 0

    LOADI32_EAX %28                             ; sizeof(struct type)
    CALL32 %malloc                              ; malloc(sizeof(struct type))
    COPY_EAX_to_EDX                             ; Set HEAD

    LOADI32_EAX %28                             ; sizeof(struct type)
    CALL32 %malloc                              ; malloc(sizeof(struct type))
    COPY_EAX_to_EBP                             ; Set I

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    STORE32_EAX_into_EDX_Immediate8 !24         ; HEAD->NAME = global_token->S
    STORE32_EAX_into_EBP_Immediate8 !24         ; I->NAME = global_token->S

    STORE32_EBP_into_EDX_Immediate8 !12         ; HEAD->INDIRECT = I
    STORE32_EDX_into_EBP_Immediate8 !12         ; I->INDIRECT = HEAD

    LOAD32_Absolute32_eax &global_types         ; Using global_types
    STORE32_EAX_into_EDX                        ; HEAD->NEXT = global_types
    STORE32_Absolute32_edx &global_types        ; global_types = HEAD

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOADI32_EAX %4                              ; Using register size
    STORE32_EAX_into_EBP_Immediate8 !4          ; I->SIZE = register size

    LOADI32_EAX &create_struct_string_0         ; Using "ERROR in create_struct\n Missing {\n"
    LOADI32_EBX &open_curly_brace               ; Using "{"
    CALL32 %require_match                       ; Make sure we have it

    LOADI32_ESI %0                              ; LAST = NULL

:create_struct_iter
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; Make it useful
    CMPI8_EAX !125                              ; IF global_token->S[0] == "}"
    JE32 %create_struct_done                    ; be done

    ;; Looks like we are adding members
    ;; Lets see if it is a union
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOADI32_EBX &union                          ; Using "union"
    CALL32 %match                               ; IF match(global_token->s, "union")
    CMPI8_EAX !0                                ; Deal with union
    JNE32 %create_struct_single                 ; Otherwise deal with singles

    ;; Deal with union
    COPY_ESI_to_EAX                             ; Put last in right place
    COPY_ECX_to_EBX                             ; put offset in right place
    CALL32 %build_union                         ; ASSEMBLE
    COPY_EAX_to_ESI                             ; last = build_union(last, offset)
    ADD_edi_into_ecx                            ; offset = offset + member_size

    LOADI32_EAX &create_struct_string_1         ; Using "ERROR in create_struct\n Missing ;\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; Make sure we have it
    JMP32 %create_struct_iter                   ; keep going

:create_struct_single
    ;; deal with singles
    COPY_ESI_to_EAX                             ; Put last in right place
    COPY_ECX_to_EBX                             ; put offset in right place
    CALL32 %build_member                        ; ASSEMBLE
    COPY_EAX_to_ESI                             ; last = build_union(last, offset)
    ADD_edi_into_ecx                            ; offset = offset + member_size

    LOADI32_EAX &create_struct_string_1         ; Using "ERROR in create_struct\n Missing ;\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; Make sure we have it
    JMP32 %create_struct_iter                   ; keep going

:create_struct_done
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOADI32_EAX &create_struct_string_1         ; Using "ERROR in create_struct\n Missing ;\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; Make sure we have it

    STORE32_ECX_into_EDX_Immediate8 !4          ; HEAD->SIZE = OFFSET
    STORE32_ESI_into_EDX_Immedate8 !16          ; HEAD->MEMBERS = LAST
    STORE32_ESI_into_EBP_Immedate8 !16          ; I->MEMBERS = LAST

    POP_ESI                                     ; Restore ESI
    POP_EDI                                     ; Restore EDI
    POP_EBP                                     ; Restore EBP
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:create_struct_string_0  "ERROR in create_struct
 Missing {
"
:create_struct_string_1  "ERROR in create_struct
 Missing ;
"


;; lookup_member function
;; Receives struct type* parent in EAX and char* name in EBX
;; Returns struct type* I in EAX
;; Uses char* NAME in EBX, ECX for struct type* I and EDX to hold parent for errors
;; Aborts hard if not found
:lookup_member
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    COPY_EAX_to_EDX                             ; Protect Parent
    LOAD32_ECX_from_EAX_Immediate8 !16          ; struct type* I = parent->MEMBERS
:lookup_member_iter
    CMPI8_ECX !0                                ; IF I == NULL
    JE32 %lookup_member_fail                    ; Abort HARD

    LOAD32_EAX_from_ECX_Immediate8 !24          ; Using I->NAME
    CALL32 %match                               ; IF I->NAME == NAME
    CMPI8_EAX !0                                ; Then we have found the member
    COPY_ECX_to_EAX                             ; Prepare for return
    LOAD32_ECX_from_ECX_Immediate8 !16          ; Prepare for loop I = I->MEMBERS
    JNE32 %lookup_member_iter                   ; Looks like we are looping

    ;; I is already in EAX
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:lookup_member_fail
    LOADI32_EAX %2                              ; Using Standard error
    STORE32_Absolute32_eax &Output_file         ; write to standard error
    LOADI32_EAX &lookup_member_string_0         ; Using "ERROR in lookup_member "
    CALL32 %File_Print                          ; print it

    LOAD32_EAX_from_EDX_Immediate8 !24          ; PARENT->NAME
    CALL32 %File_Print                          ; print it

    LOADI32_EAX &arrow_string                   ; Using "->"
    CALL32 %File_Print                          ; print it

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    CALL32 %File_Print                          ; print it

    LOADI32_EAX &lookup_member_string_1         ; Using " does not exist\n"
    CALL32 %File_Print                          ; print it

;   CALL32 %line_error                          ; Write useful debug info

    LOADI32_EAX &lookup_member_string_2         ; Using "\n"
    CALL32 %File_Print                          ; print it
    JMP32 %Exit_Failure                         ; Abort Hard

:lookup_member_string_0  "ERROR in lookup_member "
:lookup_member_string_1  " does not exist
"
:lookup_member_string_2  "
"


;; build_member function
;; Receives struct type* last in EAX, int offset in EBX and global member_size in EDI
;; Updates member_size in EDI and returns struct type* I in EAX
;; Uses ECX for struct type* member_type and EDX for struct type* I
:build_member
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    COPY_EAX_to_EDX                             ; Put last out of the way
    LOADI32_EAX %28                             ; Allocate type
    CALL32 %malloc                              ; Get I
    STORE32_EDX_into_EAX_Immediate8 !16         ; I->MEMBERS = LAST
    STORE32_EBX_into_EAX_Immediate8 !8          ; I->OFFSET = OFFSET
    COPY_EAX_to_EDX                             ; Put I in place

    CALL32 %type_name                           ; Get member_type
    COPY_EAX_to_ECX                             ; Put in place
    STORE32_ECX_into_EDX_Immediate8 !20         ; I->TYPE = MEMBER_TYPE
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EBX_from_EAX_Immediate8 !8           ; global_token->S
    STORE32_EBX_into_EDX_Immediate8 !24         ; I->NAME = global_token->S
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    ;; Check if we have an array
    LOAD32_EBX_from_EAX_Immediate8 !8           ; global_token->S
    LOADI32_EAX &open_bracket                   ; Using "["
    CALL32 %match                               ; IF global_token->S == "["
    CMPI8_EAX !0                                ; Then we have to deal with arrays in our structs
    JE32 %build_member_array                    ; So deal with that pain

    ;; Deal with non-array case
    LOAD32_EAX_from_ECX_Immediate8 !4           ; member_type->SIZE
    STORE32_EAX_into_EDX_Immediate8 !4          ; I->SIZE = member_type->SIZE
    JMP32 %build_member_done                    ; Be done

:build_member_array
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    CALL32 %numerate_string                     ; convert number
    LOAD32_EBX_from_ECX_Immediate8 !20          ; member_type->TYPE
    LOAD32_EBX_from_EBX_Immediate8 !4           ; member_type->TYPE->SIZE
    IMUL_EAX_by_EBX                             ; member_type->type->size * numerate_string(global_token->s)
    STORE32_EAX_into_EDX_Immediate8 !4          ; I->SIZE = member_type->type->size * numerate_string(global_token->s)

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOADI32_EAX &build_member_string_0          ; Using "Struct only supports [num] form\n"
    LOADI32_EBX &close_bracket                  ; Using "]"
    CALL32 %require_match                       ; Make sure we have it

:build_member_done
    LOAD32_EDI_from_EDX_Immediate8 !4           ; MEMBER_SIZE = I->SIZE
    STORE32_ECX_into_EDX_Immediate8 !20         ; I->TYPE = MEMBER_TYPE
    COPY_EDX_to_EAX                             ; Return I

    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:build_member_string_0  "Struct only supports [num] form
"


;; build_union function
;; Receives struct type* last in EAX, int offset in EBX and global member_size in EDI
;; Updates member_size in EDI and returns struct type* LAST in EAX
;; Uses ECX for struct type* last, EDX for int offset, ESI for int size and EDI for int member_size
:build_union
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    PUSH_ESI                                    ; Protect ESI
    COPY_EAX_to_ECX                             ; Put LAST in right spot
    COPY_EBX_to_EDX                             ; Put OFFSET in right spot
    LOADI32_ESI %0                              ; SIZE = 0

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    LOADI32_EAX &build_union_string_0           ; Using "ERROR in build_union\nMissing {\n"
    LOADI32_EBX &open_curly_brace               ; Using "{"
    CALL32 %require_match                       ; Make sure we have it

:build_union_iter
    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX_Immediate8 !8           ; global_token->S
    LOAD8_al_from_EAX                           ; global_token->S[0]
    MOVZX_al                                    ; make it useful
    CMPI8_EAX !125                              ; IF global_token->S[0] == "}"
    JE32 %build_union_done                      ; Be done

    ;; Collect union member
    COPY_ECX_to_EAX                             ; Passing LAST
    COPY_EDX_to_EBX                             ; Passing offset
    CALL32 %build_member                        ; build_member(last, offset)
    COPY_EAX_to_ECX                             ; last = build_member(last, offset)

    CMP_EDI_ESI                                 ; IF member_size > size
    JG32 %build_union_size                      ; Then update size

    ;; deal with member_size > size
    COPY_EDI_to_ESI                             ; SIZE = MEMBER_SIZE

:build_union_size
    LOADI32_EAX &build_union_string_1           ; Using "ERROR in build_union\nMissing ;\n"
    LOADI32_EBX &semicolon                      ; Using ";"
    CALL32 %require_match                       ; Make sure we have it
    JMP32 %build_union_iter                     ; Keep going

:build_union_done
    COPY_ESI_to_EDI                             ; MEMBER_SIZE = SIZE

    LOAD32_Absolute32_eax &global_token         ; Using global_token
    LOAD32_EAX_from_EAX                         ; global_token->NEXT
    STORE32_Absolute32_eax &global_token        ; global_token = global_token->NEXT

    COPY_ECX_to_EAX                             ; Return last

    POP_ESI                                     ; Restore ESI
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:build_union_string_0  "ERROR in build_union
Missing {
"
:build_union_string_1  "ERROR in build_union
Missing ;
"


;; sym_declare function
;; Receives char *s in EAX, struct type* t in EBX, and struct token_list* list in ECX
;; Returns struct token_list* in EAX
;; Uses EAX for A
:sym_declare
    PUSH_EDX                                    ; Protect EDX
    COPY_EAX_to_EDX                             ; Get char *S safely out of the way
    LOADI32_EAX %20                             ; Using sizeof(struct token_list)
    CALL32 %malloc                              ; Get pointer to A
    STORE32_ECX_into_EAX                        ; A->NEXT = LIST
    STORE32_EDX_into_EAX_Immediate8 !8          ; A->S = S
    STORE32_EBX_into_EAX_Immediate8 !12         ; A->TYPE = T
    POP_EDX                                     ; Restore EDX
    RETURN


;; match function
;; Receives CHAR* in EAX and CHAR* in EBX
;; Returns 0 (TRUE) or 1 (FALSE) in EAX
:match
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    COPY_EAX_to_ECX                             ; S1 in place
    COPY_EBX_to_EDX                             ; S2 in place
:match_Loop
    LOAD8_al_from_ECX                           ; S1[0]
    MOVZX_al                                    ; Make it useful
    LOAD8_bl_from_EDX                           ; S2[0]
    MOVZX_bl                                    ; Make it useful
    CMP_EAX_EBX                                 ; See if they match
    JNE32 %match_False                          ; If not

    ADDI8_ECX !1                                ; S1 = S1 + 1
    ADDI8_EDX !1                                ; S2 = S2 + 1
    CMPI8_EAX !0                                ; If reached end of string
    JE32 %match_Done                            ; Perfect match
    JMP32 %match_Loop                           ; Otherwise keep looping

:match_False
    LOADI32_EAX %1                              ; Return false
:match_Done
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; numerate_number function
;; Receives an INT A in EAX
;; Returns char* result in EAX
;; Allocates 16 bytes of memory
;; Behaves badly when given a negative number too large
;; Uses EAX for temp, EBX for DIVISOR, EDX for mod/0, ESI for result[i] and EBP for A
:numerate_number
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    PUSH_ESI                                    ; Protect ESI
    PUSH_EBP                                    ; Protect EBP
    COPY_EAX_to_EBP                             ; Protect A

    LOADI32_EAX %16                             ; 16bytes
    CALL32 %malloc                              ; Get our pointer
    PUSH_EAX                                    ; Protect our pointer
    COPY_EAX_to_ESI                             ; put pointer in right place
    LOADI32_EBX %0x3B9ACA00                     ; Set divisor to largest positive number that fits in 32bits

    CMPI8_EBP !0                                ; Deal with 0 case
    JE32 %numerate_number_ZERO                  ; If it is
    JG32 %numerate_number_positive              ; If it is positive

    ;; Deal with negative case
    LOADI32_EAX %45                             ; Using "-"
    STORE8_al_into_Address_ESI                  ; Write it
    ADDI8_ESI !1                                ; increment
    IMULI8_EBP !-1                              ; A = A * -1

:numerate_number_positive
    LOADI32_EDX %0                              ; Set top to 0
    COPY_EBP_to_EAX                             ; Using A as bottom
    IDIV_EBX                                    ; edx:eax % ebx -> edx + edx:eax / ebx -> eax [Even if we don't want it]
    CMPI8_EAX !0                                ; IF 0 == (a / divisor)
    JNE32 %numerate_number_iter                 ; Clean up those leading Zeros

    LOADI32_EDX %0                              ; Set top to 0
    COPY_EBX_to_EAX                             ; Using Divisor for bottom
    LOADI32_EBX %10                             ; Make this shit work because idiv 10 doesn't work
    IDIV_EBX                                    ; edx:eax % 10 -> edx + edx:eax / 10 -> eax [Even if we don't want it]
    COPY_EAX_to_EBX                             ; Update divisor
    JMP32 %numerate_number_positive             ; Keep collecting

:numerate_number_iter
    CMPI8_EBX !0                                ; IF DIVISOR < 0
    JLE32 %numerate_number_done                 ; Be done

    LOADI32_EDX %0                              ; Set top to 0
    COPY_EBP_to_EAX                             ; Using A as bottom
    IDIV_EBX                                    ; edx:eax % ebx -> edx + edx:eax / ebx -> eax [Even if we don't want it]
    ADDI8_EAX !48                               ; ((a / divisor) + 48)
    STORE8_al_into_Address_ESI                  ; Write it
    COPY_EDX_to_EBP                             ; a = a % divisor

    LOADI32_EDX %0                              ; Set top to 0
    COPY_EBX_to_EAX                             ; Using Divisor for bottom
    LOADI32_EBX %10                             ; Make this shit work because idiv 10 doesn't work
    IDIV_EBX                                    ; edx:eax % 10 -> edx + edx:eax / 10 -> eax [Even if we don't want it]
    COPY_EAX_to_EBX                             ; Update divisor

    ADDI8_ESI !1                                ; increment
    JMP32 %numerate_number_iter                 ; Keep going

:numerate_number_done
    POP_EAX                                     ; Restore our result
    POP_EBP                                     ; Restore EBP
    POP_ESI                                     ; Restore ESI
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN

:numerate_number_ZERO
    LOADI32_EAX %48                             ; Using '0'
    STORE8_al_into_Address_ESI                  ; Write it
    ADDI8_ESI !1                                ; increment
    JMP32 %numerate_number_done                 ; Be done


;; numerate_string function
;; Receives CHAR* in EAX
;; Returns value of CHAR* in EAX
;; Uses EAX for VALUE, EBX for S, ECX for CH and ESI for NEGATIVE?
:numerate_string
    PUSH_EBX                                    ; Protect EBX
    PUSH_ECX                                    ; Protect ECX
    PUSH_EDX                                    ; Protect EDX
    PUSH_ESI                                    ; Protect ESI
    COPY_EAX_to_EBX                             ; put S in correct place
    LOADI32_EAX %0                              ; Initialize to Zero
:numerate_string_loop
    LOAD8_cl_from_EBX_Immediate8 !1             ; S[1]
    MOVZX_cl                                    ; make it useful
    CMPI8_ECX !120                              ; IF 'x' == S[1]
    JE32 %numerate_hex                          ; Deal with hex input

    ;; Assume decimal input
    LOADI32_ESI %0                              ; Assume no negation
    LOAD8_cl_from_EBX                           ; S[0]
    MOVZX_cl                                    ; make it useful
    CMPI8_ECX !45                               ; IF '-' == S[0]
    JNE32 %numerate_decimal                     ; Skip negation

    LOADI32_ESI %1                              ; Set FLAG
    ADDI8_EBX !1                                ; S = S + 1

:numerate_decimal
    LOAD8_cl_from_EBX                           ; S[0]
    MOVZX_cl                                    ; make it useful
    CMPI8_ECX !0                                ; IF NULL == S[0]
    JE32 %numerate_decimal_done                 ; We are done

    IMULI8_EAX !10                              ; VALUE = VALUE * 10
    SUBI8_ECX !48                               ; CH = CH - '0'
    CMPI8_ECX !9                                ; Check for illegal
    JG32 %numerate_string_fail                  ; If CH > '9'
    CMPI8_ECX !0                                ; Check for illegal
    JL32 %numerate_string_fail                  ; IF CH < 0
    ADD_ecx_into_eax                            ; VALUE = VALUE + CH
    ADDI8_EBX !1                                ; S = S + 1
    JMP32 %numerate_decimal                     ; Keep looping

:numerate_decimal_done
    CMPI8_ESI !1                                ; Check if need to negate
    JNE32 %numerate_string_done                 ; Nope

    IMULI8_EAX !-1                              ; VALUE = VALUE * -1
    JMP32 %numerate_string_done                 ; Done

:numerate_hex
    ADDI8_EBX !2                                ; S = S + 2
:numerate_hex_loop
    LOAD8_cl_from_EBX                           ; S[0]
    MOVZX_cl                                    ; make it useful
    CMPI8_ECX !0                                ; IF NULL == S[0]
    JE32 %numerate_string_done                  ; We are done

    SALI8_EAX !4                                ; VALUE = VALUE << 4
    SUBI8_ECX !48                               ; CH = CH - '0'
    CMPI8_ECX !10                               ; IF 10 >= CH
    JL32 %numerate_hex_digit                    ; NO
    SUBI8_ECX !7                                ; Push A-F into range
:numerate_hex_digit
    CMPI8_ECX !15                               ; Check for illegal
    JG32 %numerate_string_fail                  ; If CH > 'F'
    CMPI8_ECX !0                                ; Check for illegal
    JL32 %numerate_string_fail                  ; IF CH < 0
    ADD_ecx_into_eax                            ; VALUE = VALUE + CH
    ADDI8_EBX !1                                ; S = S + 1
    JMP32 %numerate_hex_loop                    ; Keep looping

:numerate_string_fail
    LOADI32_EAX %0                              ; return ZERO

:numerate_string_done
    POP_ESI                                     ; Restore ESI
    POP_EDX                                     ; Restore EDX
    POP_ECX                                     ; Restore ECX
    POP_EBX                                     ; Restore EBX
    RETURN


;; Exit_Failure function
;; Receives nothing
;; And aborts hard
;; Does NOT return
:Exit_Failure
    LOADI32_EBX %1                              ; All is wrong
    LOADI32_EAX %1                              ; put the exit syscall number in eax
    INT_80                                      ; Call it a bad day

;; Keywords
:union  "union"
:struct  "struct"
:constant  "CONSTANT"
:main_string  "main"
:argc_string  "argc"
:argv_string  "argv"
:if_string  "if"
:else_string  "else"
:do_string  "do"
:while_string  "while"
:for_string  "for"
:asm_string  "asm"
:goto_string  "goto"
:return_string  "return"
:break_string  "break"
:continue_string  "continue"
:sizeof_string  "sizeof"
:plus_string  "+"
:minus_string  "-"
:multiply_string  "*"
:divide_string  "/"
:modulus_string  "%"
:left_shift_string  "<<"
:right_shift_string  ">>"
:less_than_string  "<"
:less_than_equal_string  "<="
:greater_than_equal_string  ">="
:greater_than_string  ">"
:equal_to_string  "=="
:not_equal_string  "!="
:bitwise_and  "&"
:logical_and  "&&"
:bitwise_or  "|"
:logical_or  "||"
:bitwise_xor  "^"
:arrow_string  "->"


;; Frequently Used strings
;; Generally used by require_match
:open_curly_brace  "{"
:close_curly_brace  "}"
:open_paren  "("
:close_paren  ")"
:open_bracket  "["
:close_bracket  "]"
:comma  ","
:semicolon  ";"
:equal  "="
:percent  "%"
:newline  "\n"
:underline  "_"


:prim_types
:type_void
    &type_int                                   ; NEXT
    %4                                          ; SIZE
    %0                                          ; OFFSET
    &type_void                                  ; INDIRECT
    %0                                          ; MEMBERS
    &type_void                                  ; TYPE
    &type_void_name                             ; NAME
:type_void_name  "void"

:type_int
    &type_char                                  ; NEXT
    %4                                          ; SIZE
    %0                                          ; OFFSET
    &type_int                                   ; INDIRECT
    %0                                          ; MEMBERS
    &type_int                                   ; TYPE
    &type_int_name                              ; NAME
:type_int_name  "int"

:type_char
    &type_file                                  ; NEXT
    %1                                          ; SIZE
    %0                                          ; OFFSET
    &type_char_indirect                         ; INDIRECT
    %0                                          ; MEMBERS
    &type_char                                  ; TYPE
    &type_char_name                             ; NAME
:type_char_name  "char"

:type_char_indirect
    &type_file                                  ; NEXT
    %4                                          ; SIZE
    %0                                          ; OFFSET
    &type_char_double_indirect                  ; INDIRECT
    %0                                          ; MEMBERS
    &type_char_indirect                         ; TYPE
    &type_char_indirect_name                    ; NAME
:type_char_indirect_name  "char*"

:type_char_double_indirect
    &type_file                                  ; NEXT
    %4                                          ; SIZE
    %0                                          ; OFFSET
    &type_char_double_indirect                  ; INDIRECT
    %0                                          ; MEMBERS
    &type_char_indirect                         ; TYPE
    &type_char_double_indirect_name             ; NAME
:type_char_double_indirect_name  "char**"

:type_file
    &type_function                              ; NEXT
    %4                                          ; SIZE
    %0                                          ; OFFSET
    &type_file                                  ; INDIRECT
    %0                                          ; MEMBERS
    &type_file                                  ; TYPE
    &type_file_name                             ; NAME
:type_file_name  "FILE"

:type_function
    &type_unsigned                              ; NEXT
    %4                                          ; SIZE
    %0                                          ; OFFSET
    &type_function                              ; INDIRECT
    %0                                          ; MEMBERS
    &type_function                              ; TYPE
    &type_function_name                         ; NAME
:type_function_name  "FUNCTION"

:type_unsigned
    &type_long                                  ; NEXT
    %4                                          ; SIZE
    %0                                          ; OFFSET
    &type_unsigned                              ; INDIRECT
    %0                                          ; MEMBERS
    &type_unsigned                              ; TYPE
    &type_unsigned_name                         ; NAME
:type_unsigned_name  "unsigned"

:type_long
    %0                                          ; NEXT
    %4                                          ; SIZE
    %0                                          ; OFFSET
    &type_long                                  ; INDIRECT
    %0                                          ; MEMBERS
    &type_long                                  ; TYPE
    &type_long_name                             ; NAME
:type_long_name  "long"

;; debug_list function
;; Receives struct token_list* in EAX
;; Prints contents of list and exits
;; Does NOT return
:debug_list
    COPY_EAX_to_EBP                             ; Protect the list pointer
    LOADI32_EAX %2                              ; Using Standard error
    STORE32_Absolute32_eax &Output_file         ; write to standard error

:debug_list_iter
    ;; Header
    LOADI32_EAX &debug_list_string0             ; Using our first string
    CALL32 %File_Print                          ; Print it
    COPY_EBP_to_EAX                             ; Use address of pointer
    CALL32 %numerate_number                     ; Convert it into string
    CALL32 %File_Print                          ; Print it

    ;; NEXT
    LOADI32_EAX &debug_list_string1             ; Using our second string
    CALL32 %File_Print                          ; Print it
    LOAD32_EAX_from_EBP_Immediate8 !0           ; Use address of pointer
    CALL32 %numerate_number                     ; Convert it into string
    CALL32 %File_Print                          ; Print it

    ;; PREV
    LOADI32_EAX &debug_list_string2             ; Using our third string
    CALL32 %File_Print                          ; Print it
    LOAD32_EAX_from_EBP_Immediate8 !4           ; Use address of pointer
    CALL32 %numerate_number                     ; Convert it into string
    CALL32 %File_Print                          ; Print it

    ;; S
    LOADI32_EAX &debug_list_string3             ; Using our fourth string
    CALL32 %File_Print                          ; Print it
    LOAD32_EAX_from_EBP_Immediate8 !8           ; Use address of pointer
    CALL32 %numerate_number                     ; Convert it into string
    CALL32 %File_Print                          ; Print it

    ;; S Contents
    LOADI32_EAX &debug_list_string4             ; Using our fifth string
    CALL32 %File_Print                          ; Print it
    LOAD32_EAX_from_EBP_Immediate8 !8           ; Use address of string
    CMPI8_EAX !0                                ; IF NULL Pointer
    JNE32 %debug_list_null                      ; otherwise display
    LOADI32_EAX &debug_list_string_null         ; Give meaningful message instead
:debug_list_null
    CALL32 %File_Print                          ; Print it

    ;; TYPE
    LOADI32_EAX &debug_list_string5             ; Using our sixth string
    CALL32 %File_Print                          ; Print it
    LOAD32_EAX_from_EBP_Immediate8 !12          ; Use address of pointer
    CALL32 %numerate_number                     ; Convert it into string
    CALL32 %File_Print                          ; Print it

    ;; ARGS/DEPTH
    LOADI32_EAX &debug_list_string6             ; Using our seventh string
    CALL32 %File_Print                          ; Print it
    LOAD32_EAX_from_EBP_Immediate8 !16          ; Use address of pointer
    CALL32 %numerate_number                     ; Convert it into string
    CALL32 %File_Print                          ; Print it

    LOADI32_EAX %10                             ; Add "\n"
    CALL32 %fputc                               ; print it
    CALL32 %fputc                               ; print it

    LOAD32_EBP_from_EBP                         ; TOKEN = TOKEN->NEXT
    CMPI8_EBP !0                                ; Check if NULL
    JNE32 %debug_list_iter                      ; iterate otherwise

    LOADI32_EBX %666                            ; All is HELL
    LOADI32_EAX %1                              ; put the exit syscall number in eax
    INT_80                                      ; Call it a bad day

:debug_list_string0  "Token_list node at address: "
:debug_list_string1  "
NEXT address: "
:debug_list_string2  "
PREV address: "
:debug_list_string3  "
S address: "
:debug_list_string4  "
The contents of S are: "
:debug_list_string5  "
TYPE address: "
:debug_list_string6  "
ARGUMENTS address: "
:debug_list_string_null  ">::<NULL>::<"


:Address_of
    NULL
:C
    NULL
:Input_file
    NULL
:MALLOC
    NULL
:Output_file
    NULL
:Token
    NULL
:break_frame
    NULL
:break_target_func
    NULL
:break_target_head
    NULL
:break_target_num
    NULL
:current_count
    NULL
:current_target
    NULL
:function
    NULL
:global_constant_list
    NULL
:global_function_list
    NULL
:global_symbol_list
    NULL
:global_token
    NULL
:global_types
    &prim_types
:globals_list
    NULL
:output_list
    NULL
:string_index
    NULL
:strings_list
    NULL

:ELF_end

https://github.com/oriansj/M2libc/blob/main/x86/linux/bootstrap.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/linux/bootstrap.c used in: Step 13, Step 19, Step 24
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

// CONSTANT stdin 0
// CONSTANT stdout 1
// CONSTANT stderr 2
// CONSTANT EOF 0xFFFFFFFF
// CONSTANT NULL 0
// CONSTANT EXIT_FAILURE 1
// CONSTANT EXIT_SUCCESS 0
// CONSTANT TRUE 1
// CONSTANT FALSE 0


int fgetc(FILE* f)
{
    asm("mov_eax, %3"
        "lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "push_ebx"
        "mov_ecx,esp"
        "mov_edx, %1"
        "int !0x80"
        "test_eax,eax"
        "pop_eax"
        "jne %FUNCTION_fgetc_Done"
        "mov_eax, %-1"
        ":FUNCTION_fgetc_Done");
}

void fputc(char s, FILE* f)
{
    asm("mov_eax, %4"
        "lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_edx, %1"
        "int !0x80");
}

void fputs(char* s, FILE* f)
{
    while(0 != s[0])
    {
        fputc(s[0], f);
        s = s + 1;
    }
}

FILE* open(char* name, int flag, int mode)
{
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %5"
        "int !0x80");
}

FILE* fopen(char* filename, char* mode)
{
    FILE* f;
    if('w' == mode[0])
    { /* 577 is O_WRONLY|O_CREAT|O_TRUNC, 384 is 600 in octal */
        f = open(filename, 577 , 384);
    }
    else
    { /* Everything else is a read */
        f = open(filename, 0, 0);
    }

    /* Negative numbers are error codes */
    if(0 > f)
    {
        return 0;
    }
    return f;
}

int close(int fd)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %6"
        "int !0x80");
}

int fclose(FILE* stream)
{
    int error = close(stream);
    return error;
}

int brk(void *addr)
{
    asm("mov_eax,[esp+DWORD] %4"
        "push_eax"
        "mov_eax, %45"
        "pop_ebx"
        "int !0x80");
}

long _malloc_ptr;
long _brk_ptr;

void* malloc(int size)
{
    if(NULL == _brk_ptr)
    {
        _brk_ptr = brk(0);
        _malloc_ptr = _brk_ptr;
    }

    if(_brk_ptr < _malloc_ptr + size)
    {
        _brk_ptr = brk(_malloc_ptr + size);
        if(-1 == _brk_ptr) return 0;
    }

    long old_malloc = _malloc_ptr;
    _malloc_ptr = _malloc_ptr + size;
    return old_malloc;
}

int strlen(char* str )
{
    int i = 0;
    while(0 != str[i]) i = i + 1;
    return i;
}

void* memset(void* ptr, int value, int num)
{
    char* s;
    for(s = ptr; 0 < num; num = num - 1)
    {
        s[0] = value;
        s = s + 1;
    }
}

void* calloc(int count, int size)
{
    void* ret = malloc(count * size);
    if(NULL == ret) return NULL;
    memset(ret, 0, (count * size));
    return ret;
}

void free(void* l)
{
    return;
}

void exit(int value)
{
    asm("pop_ebx"
        "pop_ebx"
        "mov_eax, %1"
        "int !0x80");
}

https://github.com/oriansj/M2-Planet/blob/main/cc.h

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc.h used in: Step 13, Step 61
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// CONSTANT FALSE 0
#define FALSE 0
// CONSTANT TRUE 1
#define TRUE 1

// CONSTANT KNIGHT_NATIVE 1
#define KNIGHT_NATIVE 1
// CONSTANT KNIGHT_POSIX 2
#define KNIGHT_POSIX 2
// CONSTANT X86 3
#define X86 3
// CONSTANT AMD64 4
#define AMD64 4
// CONSTANT ARMV7L 5
#define ARMV7L 5
// CONSTANT AARCH64 6
#define AARCH64 6
// CONSTANT RISCV32 7
#define RISCV32 7
// CONSTANT RISCV64 8
#define RISCV64 8


void copy_string(char* target, char* source, int max);
int in_set(int c, char* s);
int match(char* a, char* b);
void require(int bool, char* error);
void reset_hold_string();


struct type
{
    struct type* next;
    int size;
    int offset;
    int is_signed;
    struct type* indirect;
    struct type* members;
    struct type* type;
    char* name;
};

struct token_list
{
    struct token_list* next;
    union
    {
        struct token_list* locals;
        struct token_list* prev;
    };
    char* s;
    union
    {
        struct type* type;
        char* filename;
    };
    union
    {
        struct token_list* arguments;
        int depth;
        int linenumber;
    };
};

#include "cc_globals.h"

https://github.com/oriansj/M2libc/blob/main/bootstrappable.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/bootstrappable.c used in: Step 13, Step 19, Step 24, Step 30, Step 35, Step 39, Step 43, Step 49, Step 53, Step 57, Step 61, Step 66, Step 67, Step 68, Step 69, Step 70, Step 71, Step 73, Step 74, Step 75, Step 76, Step 79, Step 81
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>


#define TRUE 1
#define FALSE 0


void require(int bool, char* error)
{
    if(!bool)
    {
        fputs(error, stderr);
        exit(EXIT_FAILURE);
    }
}


int match(char* a, char* b)
{
    if((NULL == a) && (NULL == b)) return TRUE;
    if(NULL == a) return FALSE;
    if(NULL == b) return FALSE;

    int i = -1;
    do
    {
        i = i + 1;
        if(a[i] != b[i])
        {
            return FALSE;
        }
    } while((0 != a[i]) && (0 !=b[i]));
    return TRUE;
}


int in_set(int c, char* s)
{
    /* NULL set is always false */
    if(NULL == s) return FALSE;

    while(0 != s[0])
    {
        if(c == s[0]) return TRUE;
        s = s + 1;
    }
    return FALSE;
}

/* INTERNAL ONLY */
int __index_number(char* s, char c)
{
    int i = 0;
    while(s[i] != c)
    {
        i = i + 1;
        if(0 == s[i]) return -1;
    }
    return i;
}

/* INTERNAL ONLY */
int __toupper(int c)
{
    if(in_set(c, "abcdefghijklmnopqrstuvwxyz")) return (c & 0xDF);
    return c;
}

/* INTERNAL ONLY */
int __set_reader(char* set, int mult, char* input)
{
    int n = 0;
    int i = 0;
    int hold;
    int negative_p = FALSE;

    if(input[0] == '-')
    {
        negative_p = TRUE;
        i = i + 1;
    }

    while(in_set(input[i], set))
    {
        n = n * mult;
        hold = __index_number(set, __toupper(input[i]));

        /* Input managed to change between in_set and index_number */
        if(-1 == hold) return 0;
        n = n + hold;
        i = i + 1;
    }

    /* loop exited before NULL and thus invalid input */
    if(0 != input[i]) return 0;

    if(negative_p)
    {
        n = 0 - n;
    }

    return n;
}

int strtoint(char *a)
{
    int result = 0;
    /* If NULL string */
    if(0 == a[0])
    {
        result = 0;
    }
    /* Deal with binary */
    else if ('0' == a[0] && 'b' == a[1])
    {
        result = __set_reader("01", 2, a+2);
    }
    /* Deal with hex */
    else if ('0' == a[0] &&  'x' == a[1])
    {
        result = __set_reader("0123456789ABCDEFabcdef", 16, a+2);
    }
    /* Deal with octal */
    else if('0' == a[0])
    {
        result = __set_reader("01234567", 8, a+1);
    }
    /* Deal with decimal */
    else
    {
        result = __set_reader("0123456789", 10, a);
    }

    /* Deal with sign extension for 64bit hosts */
    if(0 != (0x80000000 & result)) result = (0xFFFFFFFF << 31) | result;
    return result;
}

char* int2str(int x, int base, int signed_p)
{
    require(1 < base, "int2str doesn't support a base less than 2\n");
    require(37 > base, "int2str doesn't support a base more than 36\n");
    /* Be overly conservative and save space for 32binary digits and padding null */
    char* p = calloc(34, sizeof(char));
    /* if calloc fails return null to let calling code deal with it */
    if(NULL == p) return p;

    p = p + 32;
    unsigned i;
    int sign_p = FALSE;
    char* table = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    if(signed_p && (10 == base) && (0 != (x & 0x80000000)))
    {
        /* Truncate to 31bits */
        i = -x & 0x7FFFFFFF;
        if(0 == i) return "-2147483648";
        sign_p = TRUE;
    } /* Truncate to 32bits */
    else i = x & (0x7FFFFFFF | (1 << 31));

    do
    {
        p[0] = table[i % base];
        p = p - 1;
        i = i / base;
    } while(0 < i);

    if(sign_p)
    {
        p[0] = '-';
        p = p - 1;
    }

    return p + 1;
}

https://github.com/oriansj/M2-Planet/blob/main/cc_globals.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc_globals.c used in: Step 13, Step 61
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

/* What types we have */
struct type* global_types;
struct type* prim_types;

/* What we are currently working on */
struct token_list* global_token;

/* Output reorder collections*/
struct token_list* output_list;
struct token_list* strings_list;
struct token_list* globals_list;

/* Make our string collection more efficient */
char* hold_string;
int string_index;

/* Our Target Architecture */
int Architecture;
int register_size;

int MAX_STRING;
struct type* integer;

/* enable bootstrap-mode */
int BOOTSTRAP_MODE;

/* enable preprocessor-only mode */
int PREPROCESSOR_MODE;

https://github.com/oriansj/M2-Planet/blob/main/cc_reader.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc_reader.c used in: Step 13, Step 61
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "cc.h"

int strtoint(char *a);

/* Globals */
FILE* input;
struct token_list* token;
int line;
char* file;

int grab_byte()
{
    int c = fgetc(input);
    if(10 == c) line = line + 1;
    return c;
}

int clearWhiteSpace(int c)
{
    if((32 == c) || (9 == c)) return clearWhiteSpace(grab_byte());
    return c;
}

int consume_byte(int c)
{
    hold_string[string_index] = c;
    string_index = string_index + 1;
    require(MAX_STRING > string_index, "Token exceeded MAX_STRING char limit\nuse --max-string number to increase\n");
    return grab_byte();
}

int preserve_string(int c)
{
    int frequent = c;
    int escape = FALSE;
    do
    {
        if(!escape && '\\' == c ) escape = TRUE;
        else escape = FALSE;
        c = consume_byte(c);
        require(EOF != c, "Unterminated string\n");
    } while(escape || (c != frequent));
    return grab_byte();
}


void copy_string(char* target, char* source, int max)
{
    int i = 0;
    while(0 != source[i])
    {
        target[i] = source[i];
        i = i + 1;
        if(i == max) break;
    }
}


void fixup_label()
{
    int hold = ':';
    int prev;
    int i = 0;
    do
    {
        prev = hold;
        hold = hold_string[i];
        hold_string[i] = prev;
        i = i + 1;
    } while(0 != hold);
}

int preserve_keyword(int c, char* S)
{
    while(in_set(c, S))
    {
        c = consume_byte(c);
    }
    return c;
}

void reset_hold_string()
{
    int i = MAX_STRING;
    while(0 <= i)
    {
        hold_string[i] = 0;
        i = i - 1;
    }
    string_index = 0;
}

/* note if this is the first token in the list, head needs fixing up */
struct token_list* eat_token(struct token_list* token)
{
    if(NULL != token->prev)
    {
        token->prev->next = token->next;
    }

    /* update backlinks */
    if(NULL != token->next)
    {
        token->next->prev = token->prev;
    }

    return token->next;
}

struct token_list* eat_until_newline(struct token_list* head)
{
    while (NULL != head)
    {
        if('\n' == head->s[0])
        {
            return head;
        }
        else
        {
            head = eat_token(head);
        }
    }

    return NULL;
}

struct token_list* remove_line_comments(struct token_list* head)
{
    struct token_list* first = NULL;

    while (NULL != head)
    {
        if(match("//", head->s))
        {
            head = eat_until_newline(head);
        }
        else
        {
            if(NULL == first)
            {
                first = head;
            }
            head = head->next;
        }
    }

    return first;
}

struct token_list* remove_line_comment_tokens(struct token_list* head)
{
    struct token_list* first = NULL;

    while (NULL != head)
    {
        if(match("//", head->s))
        {
            head = eat_token(head);
        }
        else
        {
            if(NULL == first)
            {
                first = head;
            }
            head = head->next;
        }
    }

    return first;
}

struct token_list* remove_preprocessor_directives(struct token_list* head)
{
    struct token_list* first = NULL;

    while (NULL != head)
    {
        if('#' == head->s[0])
        {
            head = eat_until_newline(head);
        }
        else
        {
            if(NULL == first)
            {
                first = head;
            }
            head = head->next;
        }
    }

    return first;
}

void new_token(char* s, int size)
{
    struct token_list* current = calloc(1, sizeof(struct token_list));
    require(NULL != current, "Exhausted memory while getting token\n");

    /* More efficiently allocate memory for string */
    current->s = calloc(size, sizeof(char));
    require(NULL != current->s, "Exhausted memory while trying to copy a token\n");
    copy_string(current->s, s, MAX_STRING);

    current->prev = token;
    current->next = token;
    current->linenumber = line;
    current->filename = file;
    token = current;
}

int get_token(int c)
{
    struct token_list* current = calloc(1, sizeof(struct token_list));
    require(NULL != current, "Exhausted memory while getting token\n");

reset:
    reset_hold_string();
    string_index = 0;

    c = clearWhiteSpace(c);
    if(c == EOF)
    {
        free(current);
        return c;
    }
    else if('#' == c)
    {
        c = consume_byte(c);
        c = preserve_keyword(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
    }
    else if(in_set(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"))
    {
        c = preserve_keyword(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
        if(':' == c)
        {
            fixup_label();
            c = ' ';
        }
    }
    else if(in_set(c, "<=>|&!^%"))
    {
        c = preserve_keyword(c, "<=>|&!^%");
    }
    else if(in_set(c, "'\""))
    {
        c = preserve_string(c);
    }
    else if(c == '/')
    {
        c = consume_byte(c);
        if(c == '*')
        {
            c = grab_byte();
            while(c != '/')
            {
                while(c != '*')
                {
                    c = grab_byte();
                    require(EOF != c, "Hit EOF inside of block comment\n");
                }
                c = grab_byte();
                require(EOF != c, "Hit EOF inside of block comment\n");
            }
            c = grab_byte();
            goto reset;
        }
        else if(c == '/')
        {
            c = consume_byte(c);
        }
        else if(c == '=')
        {
            c = consume_byte(c);
        }
    }
    else if (c == '\n')
    {
        c = consume_byte(c);
    }
    else if(c == '*')
    {
        c = consume_byte(c);
        if(c == '=')
        {
            c = consume_byte(c);
        }
    }
    else if(c == '+')
    {
        c = consume_byte(c);
        if(c == '=')
        {
            c = consume_byte(c);
        }
        if(c == '+')
        {
            c = consume_byte(c);
        }
    }
    else if(c == '-')
    {
        c = consume_byte(c);
        if(c == '=')
        {
            c = consume_byte(c);
        }
        if(c == '>')
        {
            c = consume_byte(c);
        }
        if(c == '-')
        {
            c = consume_byte(c);
        }
    }
    else
    {
        c = consume_byte(c);
    }

    new_token(hold_string, string_index + 2);
    return c;
}


int consume_filename(int c)
{
    reset_hold_string();
    int done = FALSE;

    while(!done)
    {
        if(c == EOF)
        {
            fputs("we don't support EOF as a filename in #FILENAME statements\n", stderr);
            exit(EXIT_FAILURE);
        }
        else if((32 == c) || (9 == c) || (c == '\n'))
        {
            c = grab_byte();
        }
        else
        {
            do
            {
                c = consume_byte(c);
                require(EOF != c, "Unterminated filename in #FILENAME\n");
            } while((32 != c) && (9 != c) && ('\n' != c));
            done = TRUE;
        }
    }

    /* with just a little extra to put in the matching at the end */
    new_token(hold_string, string_index + 3);
    return c;
}


int change_filename(int ch)
{
    require(EOF != ch, "#FILENAME failed to receive filename\n");
    /* Remove the #FILENAME */
    token = token->next;

    /* Get new filename */
    ch = consume_filename(ch);
    file = token->s;
    /* Remove it from the processing list */
    token = token->next;
    require(EOF != ch, "#FILENAME failed to receive filename\n");

    /* Get new line number */
    ch = get_token(ch);
    line = strtoint(token->s);
    if(0 == line)
    {
        if('0' != token->s[0])
        {
            fputs("non-line number: ", stderr);
            fputs(token->s, stderr);
            fputs(" provided to #FILENAME\n", stderr);
            exit(EXIT_FAILURE);
        }
    }
    /* Remove it from the processing list */
    token = token->next;

    return ch;
}

struct token_list* reverse_list(struct token_list* head)
{
    struct token_list* root = NULL;
    struct token_list* next;
    while(NULL != head)
    {
        next = head->next;
        head->next = root;
        root = head;
        head = next;
    }
    return root;
}

struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename)
{
    input  = a;
    line = 1;
    file = filename;
    token = current;
    int ch = grab_byte();
    while(EOF != ch)
    {
        ch = get_token(ch);
        require(NULL != token, "Empty files don't need to be compiled\n");
        if(match("#FILENAME", token->s)) ch = change_filename(ch);
    }

    return token;
}

https://github.com/oriansj/M2-Planet/blob/main/cc_strings.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc_strings.c used in: Step 13, Step 61
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "cc.h"
#include <stdint.h>

struct token_list* emit(char *s, struct token_list* head);
void require(int bool, char* error);

char upcase(char a)
{
    if(in_set(a, "abcdefghijklmnopqrstuvwxyz"))
    {
        a = a - 32;
    }

    return a;
}


int char2hex(int c)
{
    if (c >= '0' && c <= '9') return (c - 48);
    else if (c >= 'a' && c <= 'f') return (c - 87);
    else if (c >= 'A' && c <= 'F') return (c - 55);
    else return -1;
}

int hexify(int c, int high)
{
    int i = char2hex(c);

    if(0 > i)
    {
        fputs("Tried to print non-hex number\n", stderr);
        exit(EXIT_FAILURE);
    }

    if(high)
    {
        i = i << 4;
    }
    return i;
}

int escape_lookup(char* c);
int weird(char* string)
{
    int c;
    string = string + 1;
weird_reset:
    c = string[0];
    if(0 == c) return FALSE;
    if('\\' == c)
    {
        c = escape_lookup(string);
        if('x' == string[1]) string = string + 2;
        string = string + 1;
    }

    if(!in_set(c, "\t\n !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")) return TRUE;
    if(in_set(c, " \t\n\r") && (':' == string[1])) return TRUE;
    string = string + 1;
    goto weird_reset;
}

/* Lookup escape values */
int escape_lookup(char* c)
{
    if('\\' != c[0]) return c[0];

    if(c[1] == 'x')
    {
        int t1 = hexify(c[2], TRUE);
        int t2 = hexify(c[3], FALSE);
        return t1 + t2;
    }
    else if(c[1] == '0') return 0;
    else if(c[1] == 'a') return 7;
    else if(c[1] == 'b') return 8;
    else if(c[1] == 't') return 9;
    else if(c[1] == 'n') return 10;
    else if(c[1] == 'v') return 11;
    else if(c[1] == 'f') return 12;
    else if(c[1] == 'r') return 13;
    else if(c[1] == 'e') return 27;
    else if(c[1] == '"') return 34;
    else if(c[1] == '\'') return 39;
    else if(c[1] == '\\') return 92;

    fputs("Unknown escape received: ", stderr);
    fputs(c, stderr);
    fputs(" Unable to process\n", stderr);
    exit(EXIT_FAILURE);
}

/* Deal with human strings */
char* collect_regular_string(char* string)
{
    string_index = 0;

collect_regular_string_reset:
    require((MAX_STRING - 3) > string_index, "Attempt at parsing regular string exceeds max length\n");
    if(string[0] == '\\')
    {
        hold_string[string_index] = escape_lookup(string);
        if (string[1] == 'x') string = string + 2;
        string = string + 2;
    }
    else
    {
        hold_string[string_index] = string[0];
        string = string + 1;
    }

    string_index = string_index + 1;
    if(string[0] != 0) goto collect_regular_string_reset;

    hold_string[string_index] = '"';
    hold_string[string_index + 1] = '\n';
    char* message = calloc(string_index + 3, sizeof(char));
    require(NULL != message, "Exhausted memory while storing regular string\n");
    copy_string(message, hold_string, string_index + 2);
    reset_hold_string();
    return message;
}

/* Deal with non-human strings */
char* collect_weird_string(char* string)
{
    string_index = 1;
    int temp;
    char* table = "0123456789ABCDEF";

    hold_string[0] = '\'';
collect_weird_string_reset:
    require((MAX_STRING - 6) > string_index, "Attempt at parsing weird string exceeds max length\n");
    string = string + 1;
    hold_string[string_index] = ' ';
    temp = escape_lookup(string) & 0xFF;
    hold_string[string_index + 1] = table[(temp >> 4)];
    hold_string[string_index + 2] = table[(temp & 15)];

    if(string[0] == '\\')
    {
        if(string[1] == 'x') string = string + 2;
        string = string + 1;
    }

    string_index = string_index + 3;
    if(string[1] != 0) goto collect_weird_string_reset;

    hold_string[string_index] = ' ';
    hold_string[string_index + 1] = '0';
    hold_string[string_index + 2] = '0';
    hold_string[string_index + 3] = '\'';
    hold_string[string_index + 4] = '\n';

    char* hold = calloc(string_index + 6, sizeof(char));
    require(NULL != hold, "Exhausted available memory while attempting to collect a weird string\n");
    copy_string(hold, hold_string, string_index + 5);
    reset_hold_string();
    return hold;
}

/* Parse string to deal with hex characters*/
char* parse_string(char* string)
{
    /* the string */
    if(weird(string)) return collect_weird_string(string);
    else return collect_regular_string(string);
}

https://github.com/oriansj/M2-Planet/blob/main/cc_types.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc_types.c used in: Step 13, Step 61
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "cc.h"

/* Imported functions */
int strtoint(char *a);
void line_error();
void require(int bool, char* error);

/* enable easy primitive extension */
struct type* add_primitive(struct type* a)
{
    if(NULL == prim_types) return a;
    struct type* i = prim_types;
    while(NULL != i->next)
    {
        i = i->next;
    }
    i->next = a;

    return prim_types;
}

/* enable easy primitive creation */
struct type* new_primitive(char* name0, char* name1, char* name2, int size, int sign)
{
    /* Create type** */
    struct type* a = calloc(1, sizeof(struct type));
    require(NULL != a, "Exhausted memory while declaring new primitive**\n");
    a->name = name2;
    a->size = register_size;
    a->indirect = a;
    a->is_signed = sign;

    /* Create type* */
    struct type* b = calloc(1, sizeof(struct type));
    require(NULL != b, "Exhausted memory while declaring new primitive*\n");
    b->name = name1;
    b->size = register_size;
    b->is_signed = sign;
    b->indirect = a;
    a->type = b;

    struct type* r = calloc(1, sizeof(struct type));
    require(NULL != r, "Exhausted memory while declaring new primitive\n");
    r->name = name0;
    r->size = size;
    r->is_signed = sign;
    r->indirect = b;
    r->type = r;
    b->type = r;

    return r;
}

/* Initialize default types */
void initialize_types()
{
    if(AMD64 == Architecture || AARCH64 == Architecture || RISCV64 == Architecture) register_size = 8;
    else register_size = 4;

    /* Define void */
    struct type* hold = new_primitive("void", "void*", "void**", register_size, FALSE);
    prim_types = add_primitive(hold);

    /* Define unsigned LONG */
    hold = new_primitive("SCM","SCM*", "SCM**", register_size, FALSE);
    prim_types = add_primitive(hold);

    /* Define LONG */
    hold = new_primitive("long", "long*", "long**", register_size, TRUE);
    prim_types = add_primitive(hold);

    /* Define UNSIGNED */
    hold = new_primitive("unsigned", "unsigned*", "unsigned**", register_size, FALSE);
    prim_types = add_primitive(hold);

    /* Define int */
    integer = new_primitive("int", "int*", "int**", register_size, TRUE);
    prim_types = add_primitive(integer);

    /* Define uint32_t */
    hold = new_primitive("uint32_t", "uint32_t*", "uint32_t**", 4, FALSE);
    prim_types = add_primitive(hold);

    /* Define int32_t */
    hold = new_primitive("int32_t", "int32_t*", "int32_t**", 4, TRUE);
    prim_types = add_primitive(hold);

    /* Define uint16_t */
    hold = new_primitive("uint16_t", "uint16_t*", "uint16_t**", 2, FALSE);
    prim_types = add_primitive(hold);

    /* Define int16_t */
    hold = new_primitive("int16_t", "int16_t*", "int16_t**", 2, TRUE);
    prim_types = add_primitive(hold);

    /* Define uint8_t */
    hold = new_primitive("uint8_t", "uint8_t*", "uint8_t**", 1, FALSE);
    prim_types = add_primitive(hold);

    /* Define int8_t */
    hold = new_primitive("int8_t", "int8_t*", "int8_t**", 1, TRUE);
    prim_types = add_primitive(hold);

    /* Define char */
    hold = new_primitive("char", "char*", "char**", 1, TRUE);
    prim_types = add_primitive(hold);

    /* Define FUNCTION */
    hold = new_primitive("FUNCTION", "FUNCTION*", "FUNCTION**", register_size, FALSE);
    prim_types = add_primitive(hold);

    if(BOOTSTRAP_MODE)
    {
        /* Define FILE */
        hold = new_primitive("FILE", "FILE*", "FILE**", register_size, TRUE);
        prim_types = add_primitive(hold);

        /* Primitives mes.c wanted */
        hold = new_primitive("size_t", "size_t*", "size_t**", register_size, FALSE);
        prim_types = add_primitive(hold);

        hold = new_primitive("ssize_t", "ssize_t*", "ssize_t**", register_size, FALSE);
        prim_types = add_primitive(hold);
    }

    global_types = prim_types;
}

struct type* lookup_type(char* s, struct type* start)
{
    struct type* i;
    for(i = start; NULL != i; i = i->next)
    {
        if(match(i->name, s))
        {
            return i;
        }
    }
    return NULL;
}

struct type* lookup_member(struct type* parent, char* name)
{
    struct type* i;
    require(NULL != parent, "Not a valid struct type\n");
    for(i = parent->members; NULL != i; i = i->members)
    {
        if(match(i->name, name)) return i;
    }

    fputs("ERROR in lookup_member ", stderr);
    fputs(parent->name, stderr);
    fputs("->", stderr);
    fputs(global_token->s, stderr);
    fputs(" does not exist\n", stderr);
    line_error();
    fputs("\n", stderr);
    exit(EXIT_FAILURE);
}

struct type* type_name();
void require_match(char* message, char* required);

int member_size;
struct type* build_member(struct type* last, int offset)
{
    struct type* i = calloc(1, sizeof(struct type));
    require(NULL != i, "Exhausted memory while building a struct member\n");
    i->members = last;
    i->offset = offset;

    struct type* member_type = type_name();
    require(NULL != member_type, "struct member type can not be invalid\n");
    i->type = member_type;
    i->name = global_token->s;
    global_token = global_token->next;
    require(NULL != global_token, "struct member can not be EOF terminated\n");

    /* Check to see if array */
    if(match( "[", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "struct member arrays can not be EOF sized\n");
        i->size = member_type->type->size * strtoint(global_token->s);
        if(0 == i->size)
        {
            fputs("Struct only supports [num] form\n", stderr);
            exit(EXIT_FAILURE);
        }
        global_token = global_token->next;
        require_match("Struct only supports [num] form\n", "]");
    }
    else
    {
        i->size = member_type->size;
    }
    member_size = i->size;

    return i;
}

struct type* build_union(struct type* last, int offset)
{
    int size = 0;
    global_token = global_token->next;
    require_match("ERROR in build_union\nMissing {\n", "{");
    while('}' != global_token->s[0])
    {
        last = build_member(last, offset);
        if(member_size > size)
        {
            size = member_size;
        }
        require_match("ERROR in build_union\nMissing ;\n", ";");
        require(NULL != global_token, "Unterminated union\n");
    }
    member_size = size;
    global_token = global_token->next;
    return last;
}

void create_struct()
{
    int offset = 0;
    member_size = 0;
    struct type* head = calloc(1, sizeof(struct type));
    require(NULL != head, "Exhausted memory while creating a struct\n");
    struct type* i = calloc(1, sizeof(struct type));
    require(NULL != i, "Exhausted memory while creating a struct indirection\n");
    struct type* ii = calloc(1, sizeof(struct type));
    require(NULL != ii, "Exhausted memory while creating a struct double indirection\n");
    head->name = global_token->s;
    head->type = head;
    head->indirect = i;
    head->next = global_types;
    i->name = global_token->s;
    i->type = head;
    i->indirect = ii;
    i->size = register_size;
    ii->name = global_token->s;
    ii->type = i;
    ii->indirect = ii;
    ii->size = register_size;
    global_types = head;
    global_token = global_token->next;
    require_match("ERROR in create_struct\n Missing {\n", "{");
    struct type* last = NULL;
    require(NULL != global_token, "Incomplete struct definition at end of file\n");
    while('}' != global_token->s[0])
    {
        if(match(global_token->s, "union"))
        {
            last = build_union(last, offset);
        }
        else
        {
            last = build_member(last, offset);
        }
        offset = offset + member_size;
        require_match("ERROR in create_struct\n Missing ;\n", ";");
        require(NULL != global_token, "Unterminated struct\n");
    }

    global_token = global_token->next;
    require_match("ERROR in create_struct\n Missing ;\n", ";");

    head->size = offset;
    head->members = last;
    i->members = last;
}


struct type* type_name()
{
    struct type* ret;

    require(NULL != global_token, "Received EOF instead of type name\n");

    if(match("extern", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "unfinished type definition in extern\n");
    }

    if(match("struct", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "structs can not have a EOF type name\n");
        ret = lookup_type(global_token->s, global_types);
        if(NULL == ret)
        {
            create_struct();
            return NULL;
        }
    }
    else
    {
        ret = lookup_type(global_token->s, global_types);
        if(NULL == ret)
        {
            fputs("Unknown type ", stderr);
            fputs(global_token->s, stderr);
            fputs("\n", stderr);
            line_error();
            fputs("\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    global_token = global_token->next;
    require(NULL != global_token, "unfinished type definition\n");

    if(match("const", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "unfinished type definition in const\n");
    }

    while(global_token->s[0] == '*')
    {
        ret = ret->indirect;
        global_token = global_token->next;
        require(NULL != global_token, "unfinished type definition in indirection\n");
    }

    return ret;
}

struct type* mirror_type(struct type* source, char* name)
{
    struct type* head = calloc(1, sizeof(struct type));
    require(NULL != head, "Exhausted memory while creating a struct\n");
    struct type* i = calloc(1, sizeof(struct type));
    require(NULL != i, "Exhausted memory while creating a struct indirection\n");

    head->name = name;
    i->name = name;
    head->size = source->size;
    i->size = source->indirect->size;
    head->offset = source->offset;
    i->offset = source->indirect->offset;
    head->is_signed = source->is_signed;
    i->is_signed = source->indirect->is_signed;
    head->indirect = i;
    i->indirect = head;
    head->members = source->members;
    i->members =  source->indirect->members;
    head->type = head;
    i->type = i;

    return head;
}

https://github.com/oriansj/M2-Planet/blob/main/cc_core.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc_core.c used in: Step 13, Step 61
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "cc.h"
#include "gcc_req.h"
#include <stdint.h>

/* Global lists */
struct token_list* global_symbol_list;
struct token_list* global_function_list;
struct token_list* global_constant_list;

/* Core lists for this file */
struct token_list* function;

/* What we are currently working on */
struct type* current_target;
char* break_target_head;
char* break_target_func;
char* break_target_num;
char* continue_target_head;
struct token_list* break_frame;
int current_count;
int Address_of;

/* Imported functions */
char* int2str(int x, int base, int signed_p);
int strtoint(char *a);
char* parse_string(char* string);
int escape_lookup(char* c);
void require(int bool, char* error);
struct token_list* reverse_list(struct token_list* head);
struct type* mirror_type(struct type* source, char* name);
struct type* add_primitive(struct type* a);

struct token_list* emit(char *s, struct token_list* head)
{
    struct token_list* t = calloc(1, sizeof(struct token_list));
    require(NULL != t, "Exhausted memory while generating token to emit\n");
    t->next = head;
    t->s = s;
    return t;
}

void emit_out(char* s)
{
    output_list = emit(s, output_list);
}

struct token_list* uniqueID(char* s, struct token_list* l, char* num)
{
    l = emit("\n", emit(num, emit("_", emit(s, l))));
    return l;
}

void uniqueID_out(char* s, char* num)
{
    output_list = uniqueID(s, output_list, num);
}

struct token_list* sym_declare(char *s, struct type* t, struct token_list* list)
{
    struct token_list* a = calloc(1, sizeof(struct token_list));
    require(NULL != a, "Exhausted memory while attempting to declare a symbol\n");
    a->next = list;
    a->s = s;
    a->type = t;
    return a;
}

struct token_list* sym_lookup(char *s, struct token_list* symbol_list)
{
    struct token_list* i;
    for(i = symbol_list; NULL != i; i = i->next)
    {
        if(match(i->s, s)) return i;
    }
    return NULL;
}

void line_error_token(struct token_list *token)
{
    if(NULL == token)
    {
        fputs("EOF reached inside of line_error\n", stderr);
        fputs("problem at end of file\n", stderr);
        return;
    }
    fputs(token->filename, stderr);
    fputs(":", stderr);
    fputs(int2str(token->linenumber, 10, TRUE), stderr);
    fputs(":", stderr);
}

void line_error()
{
    line_error_token(global_token);
}

void require_match(char* message, char* required)
{
    if(NULL == global_token)
    {
        line_error();
        fputs("EOF reached inside of require match\n", stderr);
        fputs("problem at end of file\n", stderr);
        fputs(message, stderr);
        exit(EXIT_FAILURE);
    }
    if(!match(global_token->s, required))
    {
        line_error();
        fputs(message, stderr);
        exit(EXIT_FAILURE);
    }
    global_token = global_token->next;
}

void maybe_bootstrap_error(char* feature)
{
    if (BOOTSTRAP_MODE)
    {
        line_error();
        fputs(feature, stderr);
        fputs(" is not supported in --bootstrap-mode\n", stderr);
        exit(EXIT_FAILURE);
    }
}

void expression();
void function_call(char* s, int bool)
{
    require_match("ERROR in process_expression_list\nNo ( was found\n", "(");
    require(NULL != global_token, "Improper function call\n");
    int passed = 0;

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        emit_out("PUSHR R13 R15\t# Prevent overwriting in recursion\n");
        emit_out("PUSHR R14 R15\t# Protect the old base pointer\n");
        emit_out("COPY R13 R15\t# Copy new base pointer\n");
    }
    else if(X86 == Architecture)
    {
        emit_out("push_edi\t# Prevent overwriting in recursion\n");
        emit_out("push_ebp\t# Protect the old base pointer\n");
        emit_out("mov_edi,esp\t# Copy new base pointer\n");
    }
    else if(AMD64 == Architecture)
    {
        emit_out("push_rdi\t# Prevent overwriting in recursion\n");
        emit_out("push_rbp\t# Protect the old base pointer\n");
        emit_out("mov_rdi,rsp\t# Copy new base pointer\n");
    }
    else if(ARMV7L == Architecture)
    {
        emit_out("{R11} PUSH_ALWAYS\t# Prevent overwriting in recursion\n");
        emit_out("{BP} PUSH_ALWAYS\t# Protect the old base pointer\n");
        emit_out("'0' SP R11 NO_SHIFT MOVE_ALWAYS\t# Copy new base pointer\n");
    }
    else if(AARCH64 == Architecture)
    {
        emit_out("PUSH_X16\t# Protect a tmp register we're going to use\n");
        emit_out("PUSH_LR\t# Protect the old return pointer (link)\n");
        emit_out("PUSH_BP\t# Protect the old base pointer\n");
        emit_out("SET_X16_FROM_SP\t# The base pointer to-be\n");
    }
    else if(RISCV32 == Architecture)
    {
        emit_out("RD_SP RS1_SP !-12 ADDI\t# Allocate stack\n");
        emit_out("RS1_SP RS2_RA @4 SW\t# Protect the old return pointer\n");
        emit_out("RS1_SP RS2_FP SW\t# Protect the old frame pointer\n");
        emit_out("RS1_SP RS2_TP @8 SW\t# Protect temp register we are going to use\n");
        emit_out("RD_TP RS1_SP MV\t# The base pointer to-be\n");
    }
    else if(RISCV64 == Architecture)
    {
        emit_out("RD_SP RS1_SP !-24 ADDI\t# Allocate stack\n");
        emit_out("RS1_SP RS2_RA @8 SD\t# Protect the old return pointer\n");
        emit_out("RS1_SP RS2_FP SD\t# Protect the old frame pointer\n");
        emit_out("RS1_SP RS2_TP @16 SD\t# Protect temp register we are going to use\n");
        emit_out("RD_TP RS1_SP MV\t# The base pointer to-be\n");
    }

    if(global_token->s[0] != ')')
    {
        expression();
        require(NULL != global_token, "incomplete function call, received EOF instead of )\n");
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R0 R15\t#_process_expression1\n");
        else if(X86 == Architecture) emit_out("push_eax\t#_process_expression1\n");
        else if(AMD64 == Architecture) emit_out("push_rax\t#_process_expression1\n");
        else if(ARMV7L == Architecture) emit_out("{R0} PUSH_ALWAYS\t#_process_expression1\n");
        else if(AARCH64 == Architecture) emit_out("PUSH_X0\t#_process_expression1\n");
        else if(RISCV32 == Architecture) emit_out("RD_SP RS1_SP !-4 ADDI\nRS1_SP RS2_A0 SW\t#_process_expression1\n");
        else if(RISCV64 == Architecture) emit_out("RD_SP RS1_SP !-8 ADDI\nRS1_SP RS2_A0 SD\t#_process_expression1\n");
        passed = 1;

        while(global_token->s[0] == ',')
        {
            global_token = global_token->next;
            require(NULL != global_token, "incomplete function call, received EOF instead of argument\n");
            expression();
            if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R0 R15\t#_process_expression2\n");
            else if(X86 == Architecture) emit_out("push_eax\t#_process_expression2\n");
            else if(AMD64 == Architecture) emit_out("push_rax\t#_process_expression2\n");
            else if(ARMV7L == Architecture) emit_out("{R0} PUSH_ALWAYS\t#_process_expression2\n");
            else if(AARCH64 == Architecture) emit_out("PUSH_X0\t#_process_expression2\n");
            else if(RISCV32 == Architecture) emit_out("RD_SP RS1_SP !-4 ADDI\nRS1_SP RS2_A0 SW\t#_process_expression2\n");
            else if(RISCV64 == Architecture) emit_out("RD_SP RS1_SP !-8 ADDI\nRS1_SP RS2_A0 SD\t#_process_expression2\n");
            passed = passed + 1;
        }
    }

    require_match("ERROR in process_expression_list\nNo ) was found\n", ")");

    if(TRUE == bool)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            emit_out("LOAD R0 R14 ");
            emit_out(s);
            emit_out("\nMOVE R14 R13\n");
            emit_out("CALL R0 R15\n");
        }
        else if(X86 == Architecture)
        {
            emit_out("lea_eax,[ebp+DWORD] %");
            emit_out(s);
            emit_out("\nmov_eax,[eax]\n");
            emit_out("mov_ebp,edi\n");
            emit_out("call_eax\n");
        }
        else if(AMD64 == Architecture)
        {
            emit_out("lea_rax,[rbp+DWORD] %");
            emit_out(s);
            emit_out("\nmov_rax,[rax]\n");
            emit_out("mov_rbp,rdi\n");
            emit_out("call_rax\n");
        }
        else if(ARMV7L == Architecture)
        {
            emit_out("!");
            emit_out(s);
            emit_out(" R0 SUB BP ARITH_ALWAYS\n");
            emit_out("!0 R0 LOAD32 R0 MEMORY\n");
            emit_out("{LR} PUSH_ALWAYS\t# Protect the old link register\n");
            emit_out("'0' R11 BP NO_SHIFT MOVE_ALWAYS\n");
            emit_out("'3' R0 CALL_REG_ALWAYS\n");
            emit_out("{LR} POP_ALWAYS\t# Prevent overwrite\n");
        }
        else if(AARCH64 == Architecture)
        {
            emit_out("SET_X0_FROM_BP\n");
            emit_out("LOAD_W1_AHEAD\nSKIP_32_DATA\n%");
            emit_out(s);
            emit_out("\nSUB_X0_X0_X1\n");
            emit_out("DEREF_X0\n");
            emit_out("SET_BP_FROM_X16\n");
            emit_out("SET_X16_FROM_X0\n");
            emit_out("BLR_X16\n");
        }
        else if(RISCV32 == Architecture)
        {
            emit_out("RD_A0 RS1_FP !");
            emit_out(s);
            emit_out(" ADDI\n");
            emit_out("RD_A0 RS1_A0 LW\n");
            emit_out("RD_FP RS1_TP MV\n");
            emit_out("RD_RA RS1_A0 JALR\n");
        }
        else if(RISCV64 == Architecture)
        {
            emit_out("RD_A0 RS1_FP !");
            emit_out(s);
            emit_out(" ADDI\n");
            emit_out("RD_A0 RS1_A0 LD\n");
            emit_out("RD_FP RS1_TP MV\n");
            emit_out("RD_RA RS1_A0 JALR\n");
        }
    }
    else
    {
        if((KNIGHT_NATIVE == Architecture) || (KNIGHT_POSIX == Architecture))
        {
            emit_out("MOVE R14 R13\n");
            emit_out("LOADR R0 4\nJUMP 4\n&FUNCTION_");
            emit_out(s);
            emit_out("\nCALL R0 R15\n");
        }
        else if(X86 == Architecture)
        {
            emit_out("mov_ebp,edi\n");
            emit_out("call %FUNCTION_");
            emit_out(s);
            emit_out("\n");
        }
        else if(AMD64 == Architecture)
        {
            emit_out("mov_rbp,rdi\n");
            emit_out("call %FUNCTION_");
            emit_out(s);
            emit_out("\n");
        }
        else if(ARMV7L == Architecture)
        {
            emit_out("{LR} PUSH_ALWAYS\t# Protect the old link register\n");
            emit_out("'0' R11 BP NO_SHIFT MOVE_ALWAYS\n");
            emit_out("^~FUNCTION_");
            emit_out(s);
            emit_out(" CALL_ALWAYS\n");
            emit_out("{LR} POP_ALWAYS\t# Restore the old link register\n");
        }
        else if(AARCH64 == Architecture)
        {
            emit_out("SET_BP_FROM_X16\n");
            emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&FUNCTION_");
            emit_out(s);
            emit_out("\n");
            emit_out("BLR_X16\n");
        }
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
        {
            emit_out("RD_FP RS1_TP MV\n");
            emit_out("RD_RA $FUNCTION_");
            emit_out(s);
            emit_out(" JAL\n");
        }
    }

    for(; passed > 0; passed = passed - 1)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# _process_expression_locals\n");
        else if(X86 == Architecture) emit_out("pop_ebx\t# _process_expression_locals\n");
        else if(AMD64 == Architecture) emit_out("pop_rbx\t# _process_expression_locals\n");
        else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# _process_expression_locals\n");
        else if(AARCH64 == Architecture) emit_out("POP_X1\t# _process_expression_locals\n");
        else if(RISCV32 == Architecture) emit_out("RD_A1 RS1_SP LW\t# _process_expression_locals\nRD_SP RS1_SP !4 ADDI\n");
        else if(RISCV64 == Architecture) emit_out("RD_A1 RS1_SP LD\t# _process_expression_locals\nRD_SP RS1_SP !8 ADDI\n");
    }

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        emit_out("POPR R14 R15\t# Restore old base pointer\n");
        emit_out("POPR R13 R15\t# Prevent overwrite\n");
    }
    else if(X86 == Architecture)
    {
        emit_out("pop_ebp\t# Restore old base pointer\n");
        emit_out("pop_edi\t# Prevent overwrite\n");
    }
    else if(AMD64 == Architecture)
    {
        emit_out("pop_rbp\t# Restore old base pointer\n");
        emit_out("pop_rdi\t# Prevent overwrite\n");
    }
    else if(ARMV7L == Architecture)
    {
        emit_out("{BP} POP_ALWAYS\t# Restore old base pointer\n");
        emit_out("{R11} POP_ALWAYS\t# Prevent overwrite\n");
    }
    else if(AARCH64 == Architecture)
    {
        emit_out("POP_BP\t# Restore the old base pointer\n");
        emit_out("POP_LR\t# Restore the old return pointer (link)\n");
        emit_out("POP_X16\t# Restore a register we used as tmp\n");
    }
    else if(RISCV32 == Architecture)
    {
        emit_out("RD_FP RS1_SP LW\t# Restore old frame pointer\n");
        emit_out("RD_TP RS1_SP !8 LW\t# Restore temp register\n");
        emit_out("RD_RA RS1_SP !4 LW\t# Restore return address\n");
        emit_out("RD_SP RS1_SP !12 ADDI\t# Deallocate stack\n");
    }
    else if(RISCV64 == Architecture)
    {
        emit_out("RD_FP RS1_SP LD\t# Restore old frame pointer\n");
        emit_out("RD_TP RS1_SP !16 LD\t# Restore temp register\n");
        emit_out("RD_RA RS1_SP !8 LD\t# Restore return address\n");
        emit_out("RD_SP RS1_SP !24 ADDI\t# Deallocate stack\n");
    }
}

void constant_load(struct token_list* a)
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("LOADI R0 ");
    else if(X86 == Architecture) emit_out("mov_eax, %");
    else if(AMD64 == Architecture) emit_out("mov_rax, %");
    else if(ARMV7L == Architecture) emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
    else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n%");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        emit_out("RD_A0 ~");
        emit_out(a->arguments->s);
        emit_out(" LUI\nRD_A0 RS1_A0 !");
    }
    emit_out(a->arguments->s);
    if(RISCV32 == Architecture) emit_out(" ADDI\n");
    else if(RISCV64 == Architecture) emit_out(" ADDIW\n");
    emit_out("\n");
}

char* load_value_signed(unsigned size)
{
    if(size == 1)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOAD8 R0 R0 0\n";
        else if(X86 == Architecture) return "movsx_eax,BYTE_PTR_[eax]\n";
        else if(AMD64 == Architecture) return "movsx_rax,BYTE_PTR_[rax]\n";
        else if(ARMV7L == Architecture) return "LOADS8 R0 LOAD R0 HALF_MEMORY\n";
        else if(AARCH64 == Architecture) return "LDRSB_X0_[X0]\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "RD_A0 RS1_A0 LB\n";
    }
    else if(size == 2)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOAD16 R0 R0 0\n";
        else if(X86 == Architecture) return "movsx_eax,WORD_PTR_[eax]\n";
        else if(AMD64 == Architecture) return "movsx_rax,WORD_PTR_[rax]\n";
        else if(ARMV7L == Architecture) return "LOADS16 R0 LOAD R0 HALF_MEMORY\n";
        else if(AARCH64 == Architecture) return "LDRSH_X0_[X0]\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "RD_A0 RS1_A0 LH\n";
    }
    else if(size == 4)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOAD R0 R0 0\n";
        else if(X86 == Architecture) return "mov_eax,[eax]\n";
        else if(AMD64 == Architecture) return "movsx_rax,DWORD_PTR_[rax]\n";
        else if(ARMV7L == Architecture) return "!0 R0 LOAD32 R0 MEMORY\n";
        else if(AARCH64 == Architecture) return "LDR_W0_[X0]\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "RD_A0 RS1_A0 LW\n";
    }
    else if(size == 8)
    {
        if(AMD64 == Architecture) return "mov_rax,[rax]\n";
        else if(AARCH64 == Architecture) return "DEREF_X0\n";
        else if(RISCV64 == Architecture) return "RD_A0 RS1_A0 LD\n";
    }
    line_error();
    fputs(" Got unsupported size ", stderr);
    fputs(int2str(size, 10, TRUE), stderr);
    fputs(" when trying to load value.\n", stderr);
    exit(EXIT_FAILURE);
}

char* load_value_unsigned(unsigned size)
{
    if(size == 1)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOADU8 R0 R0 0\n";
        else if(X86 == Architecture) return "movzx_eax,BYTE_PTR_[eax]\n";
        else if(AMD64 == Architecture) return "movzx_rax,BYTE_PTR_[rax]\n";
        else if(ARMV7L == Architecture) return "!0 R0 LOAD R0 MEMORY\n";
        else if(AARCH64 == Architecture) return "DEREF_X0_BYTE\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "RD_A0 RS1_A0 LBU\n";
    }
    else if(size == 2)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOADU16 R0 R0 0\n";
        else if(X86 == Architecture) return "movzx_eax,WORD_PTR_[eax]\n";
        else if(AMD64 == Architecture) return "movzx_rax,WORD_PTR_[rax]\n";
        else if(ARMV7L == Architecture) return "NO_OFFSET R0 LOAD R0 HALF_MEMORY\n";
        else if(AARCH64 == Architecture) return "LDRH_W0_[X0]\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) return "RD_A0 RS1_A0 LHU\n";
    }
    else if(size == 4)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "LOAD R0 R0 0\n";
        else if(X86 == Architecture) return "mov_eax,[eax]\n";
        else if(AMD64 == Architecture) return "mov_eax,[rax]\n";
        else if(ARMV7L == Architecture) return "!0 R0 LOAD32 R0 MEMORY\n";
        else if(AARCH64 == Architecture) return "LDR_W0_[X0]\n";
        else if(RISCV32 == Architecture) return "RD_A0 RS1_A0 LW\n";
        else if(RISCV64 == Architecture) return "RD_A0 RS1_A0 LWU\n";
    }
    else if(size == 8)
    {
        if(AMD64 == Architecture) return "mov_rax,[rax]\n";
        else if(AARCH64 == Architecture) return "DEREF_X0\n";
        else if(RISCV64 == Architecture) return "RD_A0 RS1_A0 LD\n";
    }
    line_error();
    fputs(" Got unsupported size ", stderr);
    fputs(int2str(size, 10, TRUE), stderr);
    fputs(" when trying to load value.\n", stderr);
    exit(EXIT_FAILURE);
}

char* load_value(unsigned size, int is_signed)
{
    if(is_signed) return load_value_signed(size);
    return load_value_unsigned(size);
}

char* store_value(unsigned size)
{
    if(size == 1)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "STORE8 R0 R1 0\n";
        else if(X86 == Architecture) return "mov_[ebx],al\n";
        else if(AMD64 == Architecture) return "mov_[rbx],al\n";
        else if(ARMV7L == Architecture) return "!0 R0 STORE8 R1 MEMORY\n";
        else if(AARCH64 == Architecture) return "STR_BYTE_W0_[X1]\n";
        else if(RISCV32 == Architecture) return "RS1_A1 RS2_A0 SB\n";
        else if(RISCV64 == Architecture) return "RS1_A1 RS2_A0 SB\n";
    }
    else if(size == 2)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "STORE16 R0 R1 0\n";
        else if(X86 == Architecture) return "mov_[ebx],ax\n";
        else if(AMD64 == Architecture) return "mov_[rbx],ax\n";
        else if(ARMV7L == Architecture) return "NO_OFFSET R0 STORE16 R1 HALF_MEMORY\n";
        else if(AARCH64 == Architecture) return "STRH_W0_[X1]\n";
        else if(RISCV32 == Architecture || RISCV64 == Architecture) return "RS1_A1 RS2_A0 SH\n";
    }
    else if(size == 4)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) return "STORE R0 R1 0\n";
        else if(X86 == Architecture) return "mov_[ebx],eax\n";
        else if(AMD64 == Architecture) return "mov_[rbx],eax\n";
        else if(ARMV7L == Architecture) return "!0 R0 STORE32 R1 MEMORY\n";
        else if(AARCH64 == Architecture) return "STR_W0_[X1]\n";
        else if(RISCV32 == Architecture || RISCV64 == Architecture) return "RS1_A1 RS2_A0 SW\n";
    }
    else if(size == 8)
    {
        if(AMD64 == Architecture) return "mov_[rbx],rax\n";
        else if(AARCH64 == Architecture) return "STR_X0_[X1]\n";
        else if(RISCV64 == Architecture) return "RS1_A1 RS2_A0 SD\n";
    }
    /* Should not happen but print error message. */
    fputs("Got unsupported size ", stderr);
    fputs(int2str(size, 10, TRUE), stderr);
    fputs(" when storing number in register.\n", stderr);
    line_error();
    exit(EXIT_FAILURE);
}

int is_compound_assignment(char* token)
{
    if(match("+=", token)) return TRUE;
    else if(match("-=", token)) return TRUE;
    else if(match("*=", token)) return TRUE;
    else if(match("/=", token)) return TRUE;
    else if(match("%=", token)) return TRUE;
    else if(match("<<=", token)) return TRUE;
    else if(match(">>=", token)) return TRUE;
    else if(match("&=", token)) return TRUE;
    else if(match("^=", token)) return TRUE;
    else if(match("|=", token)) return TRUE;
    return FALSE;
}

void postfix_expr_stub();
void variable_load(struct token_list* a, int num_dereference)
{
    require(NULL != global_token, "incomplete variable load received\n");
    if((match("FUNCTION", a->type->name) || match("FUNCTION*", a->type->name)) && match("(", global_token->s))
    {
        function_call(int2str(a->depth, 10, TRUE), TRUE);
        return;
    }
    current_target = a->type;

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("ADDI R0 R14 ");
    else if(X86 == Architecture) emit_out("lea_eax,[ebp+DWORD] %");
    else if(AMD64 == Architecture) emit_out("lea_rax,[rbp+DWORD] %");
    else if(ARMV7L == Architecture) emit_out("!");
    else if(AARCH64 == Architecture) emit_out("SET_X0_FROM_BP\nLOAD_W1_AHEAD\nSKIP_32_DATA\n%");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 RS1_FP !");

    emit_out(int2str(a->depth, 10, TRUE));
    if(ARMV7L == Architecture) emit_out(" R0 SUB BP ARITH_ALWAYS");
    else if(AARCH64 == Architecture) emit_out("\nSUB_X0_X0_X1\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" ADDI");
    emit_out("\n");

    if(TRUE == Address_of) return;
    if(match(".", global_token->s))
    {
        postfix_expr_stub();
        return;
    }
    if(!match("=", global_token->s) && !is_compound_assignment(global_token->s))
    {
        emit_out(load_value(current_target->size, current_target->is_signed));
    }

    while (num_dereference > 0)
    {
        current_target = current_target->type;
        emit_out(load_value(current_target->size, current_target->is_signed));
        num_dereference = num_dereference - 1;
    }
}

void function_load(struct token_list* a)
{
    require(NULL != global_token, "incomplete function load\n");
    if(match("(", global_token->s))
    {
        function_call(a->s, FALSE);
        return;
    }

    if((KNIGHT_NATIVE == Architecture) || (KNIGHT_POSIX == Architecture)) emit_out("LOADR R0 4\nJUMP 4\n&FUNCTION_");
    else if(X86 == Architecture) emit_out("mov_eax, &FUNCTION_");
    else if(AMD64 == Architecture) emit_out("lea_rax,[rip+DWORD] %FUNCTION_");
    else if(ARMV7L == Architecture) emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n&FUNCTION_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n&FUNCTION_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 ~FUNCTION_");
    emit_out(a->s);
    if(RISCV32 == Architecture)
    {
        emit_out(" AUIPC\n");
        emit_out("RD_A0 RS1_A0 !FUNCTION_");
        emit_out(a->s);
        emit_out(" ADDI");
    }
    else if(RISCV64 == Architecture)
    {
        emit_out(" AUIPC\n");
        emit_out("RD_A0 RS1_A0 !FUNCTION_");
        emit_out(a->s);
        emit_out(" ADDIW");
    }
    emit_out("\n");
}

void global_load(struct token_list* a)
{
    current_target = a->type;
    if((KNIGHT_NATIVE == Architecture) || (KNIGHT_POSIX == Architecture)) emit_out("LOADR R0 4\nJUMP 4\n&GLOBAL_");
    else if(X86 == Architecture) emit_out("mov_eax, &GLOBAL_");
    else if(AMD64 == Architecture) emit_out("lea_rax,[rip+DWORD] %GLOBAL_");
    else if(ARMV7L == Architecture) emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n&GLOBAL_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n&GLOBAL_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 ~GLOBAL_");
    emit_out(a->s);
    if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        emit_out(" AUIPC\n");
        emit_out("RD_A0 RS1_A0 !GLOBAL_");
        emit_out(a->s);
        emit_out(" ADDI");
    }
    emit_out("\n");

    require(NULL != global_token, "unterminated global load\n");
    if(TRUE == Address_of) return;
    if(match(".", global_token->s))
    {
        postfix_expr_stub();
        return;
    }
    if(match("=", global_token->s) || is_compound_assignment(global_token->s)) return;

    emit_out(load_value(register_size, current_target->is_signed));
}

/*
 * primary-expr:
 * FAILURE
 * "String"
 * 'Char'
 * [0-9]*
 * [a-z,A-Z]*
 * ( expression )
 */

void primary_expr_failure()
{
    require(NULL != global_token, "hit EOF when expecting primary expression\n");
    line_error();
    fputs("Received ", stderr);
    fputs(global_token->s, stderr);
    fputs(" in primary_expr\n", stderr);
    exit(EXIT_FAILURE);
}

void primary_expr_string()
{
    char* number_string = int2str(current_count, 10, TRUE);
    current_count = current_count + 1;
    if((KNIGHT_NATIVE == Architecture) || (KNIGHT_POSIX == Architecture)) emit_out("LOADR R0 4\nJUMP 4\n&STRING_");
    else if(X86 == Architecture) emit_out("mov_eax, &STRING_");
    else if(AMD64 == Architecture) emit_out("lea_rax,[rip+DWORD] %STRING_");
    else if(ARMV7L == Architecture) emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n&STRING_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n&STRING_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 ~STRING_");
    uniqueID_out(function->s, number_string);
    if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        emit_out("AUIPC\n");
        emit_out("RD_A0 RS1_A0 !STRING_");
        uniqueID_out(function->s, number_string);
        emit_out("ADDI\n");
    }

    /* The target */
    strings_list = emit(":STRING_", strings_list);
    strings_list = uniqueID(function->s, strings_list, number_string);

    /* catch case of just "foo" from segfaulting */
    require(NULL != global_token->next, "a string by itself is not valid C\n");

    /* Parse the string */
    if('"' != global_token->next->s[0])
    {
        strings_list = emit(parse_string(global_token->s), strings_list);
        global_token = global_token->next;
    }
    else
    {
        char* s = calloc(MAX_STRING, sizeof(char));

        /* prefix leading string */
        s[0] = '"';
        int i = 1;

        int j;
        while('"' == global_token->s[0])
        {
            /* Step past the leading '"' */
            j = 1;

            /* Copy the rest of the string as is */
            while(0 != global_token->s[j])
            {
                require(i < MAX_STRING, "concat string exceeded max string length\n");
                s[i] = global_token->s[j];
                i = i + 1;
                j = j + 1;
            }

            /* Move on to the next token */
            global_token = global_token->next;
            require(NULL != global_token, "multi-string null is not valid C\n");
        }

        /* Now use it */
        strings_list = emit(parse_string(s), strings_list);
    }
}

void primary_expr_char()
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("LOADI R0 ");
    else if(X86 == Architecture) emit_out("mov_eax, %");
    else if(AMD64 == Architecture) emit_out("mov_rax, %");
    else if(ARMV7L == Architecture) emit_out("!");
    else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n%");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 !");
    emit_out(int2str(escape_lookup(global_token->s + 1), 10, TRUE));
    if(ARMV7L == Architecture) emit_out(" R0 LOADI8_ALWAYS");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" ADDI");
    emit_out("\n");
    global_token = global_token->next;
}

int hex2char(int c)
{
    if((c >= 0) && (c <= 9)) return (c + 48);
    else if((c >= 10) && (c <= 15)) return (c + 55);
    else return -1;
}

char* number_to_hex(int a, int bytes)
{
    require(bytes > 0, "number to hex must have a positive number of bytes greater than zero\n");
    char* result = calloc(1 + (bytes << 1), sizeof(char));
    if(NULL == result)
    {
        fputs("calloc failed in number_to_hex\n", stderr);
        exit(EXIT_FAILURE);
    }
    int i = 0;

    int divisor = (bytes << 3);
    require(divisor > 0, "unexpected wrap around in number_to_hex\n");

    /* Simply collect numbers until divisor is gone */
    while(0 != divisor)
    {
        divisor = divisor - 4;
        result[i] = hex2char((a >> divisor) & 0xF);
        i = i + 1;
    }

    return result;
}

void primary_expr_number()
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        int size = strtoint(global_token->s);
        if((32767 > size) && (size > -32768))
        {
            emit_out("LOADI R0 ");
            emit_out(global_token->s);
        }
        else
        {
            emit_out("LOADR R0 4\nJUMP 4\n'");
            emit_out(number_to_hex(size, register_size));
            emit_out("'");
        }
    }
    else if(X86 == Architecture)
    {
        emit_out("mov_eax, %");
        emit_out(global_token->s);
    }
    else if(AMD64 == Architecture)
    {
        emit_out("mov_rax, %");
        emit_out(global_token->s);
    }
    else if(ARMV7L == Architecture)
    {
        emit_out("!0 R0 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
        emit_out(global_token->s);
    }
    else if(AARCH64 == Architecture)
    {
        emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n%");
        emit_out(global_token->s);
    }
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        int size = strtoint(global_token->s);
        if((2047 > size) && (size > -2048))
        {
            emit_out("RD_A0 !");
            emit_out(global_token->s);
            emit_out(" ADDI");
        }
        else if (0 == (size >> 30))
        {
            emit_out("RD_A0 ~");
            emit_out(global_token->s);
            emit_out(" LUI\n");
            emit_out("RD_A0 RS1_A0 !");
            emit_out(global_token->s);
            emit_out(" ADDI");
        }
        else
        {
            int high = size >> 30;
            int low = ((size >> 30) << 30) ^ size;
            emit_out("RD_A0 ~");
            emit_out(int2str(high, 10, TRUE));
            emit_out(" LUI\n");
            emit_out("RD_A0 RS1_A0 !");
            emit_out(int2str(high, 10, TRUE));
            emit_out(" ADDI\n");
            emit_out("RD_A0 RS1_A0 RS2_X30 SLLI\n");
            emit_out("RD_T1 ~");
            emit_out(int2str(low, 10, TRUE));
            emit_out(" LUI\n");
            emit_out("RD_T1 RS1_T1 !");
            emit_out(int2str(low, 10, TRUE));
            emit_out(" ADDI\n");
            emit_out("RD_A0 RS1_A0 RS2_T1 OR\n");
        }
    }
    emit_out("\n");
    global_token = global_token->next;
}

void primary_expr_variable()
{
    int num_dereference = 0;
    while(global_token->s[0] == '*') {
        global_token = global_token->next;
        require(NULL != global_token, "Walked off the end of a variable dereference\n");
        num_dereference = num_dereference + 1;
    }
    char* s = global_token->s;
    global_token = global_token->next;
    struct token_list* a = sym_lookup(s, global_constant_list);
    if(NULL != a)
    {
        constant_load(a);
        return;
    }

    a = sym_lookup(s, function->locals);
    if(NULL != a)
    {
        variable_load(a, num_dereference);
        return;
    }

    a = sym_lookup(s, function->arguments);
    if(NULL != a)
    {
        variable_load(a, num_dereference);
        return;
    }

    a = sym_lookup(s, global_function_list);
    if(NULL != a)
    {
        function_load(a);
        return;
    }

    a = sym_lookup(s, global_symbol_list);
    if(NULL != a)
    {
        global_load(a);
        return;
    }

    line_error();
    fputs(s ,stderr);
    fputs(" is not a defined symbol\n", stderr);
    exit(EXIT_FAILURE);
}

void primary_expr();
struct type* promote_type(struct type* a, struct type* b)
{
    require(NULL != b, "impossible case 1 in promote_type\n");
    require(NULL != a, "impossible case 2 in promote_type\n");

    if(a == b) return a;

    struct type* i;
    for(i = global_types; NULL != i; i = i->next)
    {
        if(a->name == i->name) break;
        if(b->name == i->name) break;
        if(a->name == i->indirect->name) break;
        if(b->name == i->indirect->name) break;
        if(a->name == i->indirect->indirect->name) break;
        if(b->name == i->indirect->indirect->name) break;
    }
    require(NULL != i, "impossible case 3 in promote_type\n");
    return i;
}

void common_recursion(FUNCTION f)
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R0 R15\t#_common_recursion\n");
    else if(X86 == Architecture) emit_out("push_eax\t#_common_recursion\n");
    else if(AMD64 == Architecture) emit_out("push_rax\t#_common_recursion\n");
    else if(ARMV7L == Architecture) emit_out("{R0} PUSH_ALWAYS\t#_common_recursion\n");
    else if(AARCH64 == Architecture) emit_out("PUSH_X0\t#_common_recursion\n");
    else if(RISCV32 == Architecture) emit_out("RD_SP RS1_SP !-4 ADDI\t# _common_recursion\nRS1_SP RS2_A0 SW\n");
    else if(RISCV64 == Architecture) emit_out("RD_SP RS1_SP !-8 ADDI\t# _common_recursion\nRS1_SP RS2_A0 SD\n");

    struct type* last_type = current_target;
    global_token = global_token->next;
    require(NULL != global_token, "Received EOF in common_recursion\n");
    f();
    current_target = promote_type(current_target, last_type);

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# _common_recursion\n");
    else if(X86 == Architecture) emit_out("pop_ebx\t# _common_recursion\n");
    else if(AMD64 == Architecture) emit_out("pop_rbx\t# _common_recursion\n");
    else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# _common_recursion\n");
    else if(AARCH64 == Architecture) emit_out("POP_X1\t# _common_recursion\n");
    else if(RISCV32 == Architecture) emit_out("RD_A1 RS1_SP LW\nRD_SP RS1_SP !4 ADDI\t# _common_recursion\n");
    else if(RISCV64 == Architecture) emit_out("RD_A1 RS1_SP LD\nRD_SP RS1_SP !8 ADDI\t# _common_recursion\n");
}

void general_recursion(FUNCTION f, char* s, char* name, FUNCTION iterate)
{
    require(NULL != global_token, "Received EOF in general_recursion\n");
    if(match(name, global_token->s))
    {
        common_recursion(f);
        emit_out(s);
        iterate();
    }
}

void arithmetic_recursion(FUNCTION f, char* s1, char* s2, char* name, FUNCTION iterate)
{
    require(NULL != global_token, "Received EOF in arithmetic_recursion\n");
    if(match(name, global_token->s))
    {
        common_recursion(f);
        if(NULL == current_target)
        {
            emit_out(s1);
        }
        else if(current_target->is_signed)
        {
            emit_out(s1);
        }
        else
        {
            emit_out(s2);
        }
        iterate();
    }
}


/*
 * postfix-expr:
 *         primary-expr
 *         postfix-expr [ expression ]
 *         postfix-expr ( expression-list-opt )
 *         postfix-expr -> member
 *         postfix-expr . member
 */
struct type* lookup_member(struct type* parent, char* name);
void postfix_expr_arrow()
{
    emit_out("# looking up offset\n");
    global_token = global_token->next;
    require(NULL != global_token, "naked -> not allowed\n");

    struct type* i = lookup_member(current_target, global_token->s);
    current_target = i->type;
    global_token = global_token->next;
    require(NULL != global_token, "Unterminated -> expression not allowed\n");

    if(0 != i->offset)
    {
        emit_out("# -> offset calculation\n");
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            emit_out("ADDUI R0 R0 ");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\n");
        }
        else if(X86 == Architecture)
        {
            emit_out("mov_ebx, %");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\nadd_eax,ebx\n");
        }
        else if(AMD64 == Architecture)
        {
            emit_out("mov_rbx, %");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\nadd_rax,rbx\n");
        }
        else if(ARMV7L == Architecture)
        {
            emit_out("!0 R1 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\n'0' R0 R0 ADD R1 ARITH2_ALWAYS\n");
        }
        else if(AARCH64 == Architecture)
        {
            emit_out("LOAD_W1_AHEAD\nSKIP_32_DATA\n%");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\nADD_X0_X1_X0\n");
        }
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
        {
            emit_out("RD_A1 !");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out(" ADDI\n");
            emit_out("RD_A0 RS1_A1 RS2_A0 ADD\n");
        }
    }

    /* We don't yet support assigning structs to structs */
    if((!match("=", global_token->s) && !is_compound_assignment(global_token->s) && (register_size >= i->size)))
    {
        emit_out(load_value(i->size, i->is_signed));
    }
}

void postfix_expr_dot()
{
    maybe_bootstrap_error("Member access using .");
    emit_out("# looking up offset\n");
    global_token = global_token->next;
    require(NULL != global_token, "naked . not allowed\n");

    struct type* i = lookup_member(current_target, global_token->s);
    current_target = i->type;
    global_token = global_token->next;
    require(NULL != global_token, "Unterminated . expression not allowed\n");

    if(0 != i->offset)
    {
        emit_out("# . offset calculation\n");
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            emit_out("ADDUI R0 R0 ");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\n");
        }
        else if(X86 == Architecture)
        {
            emit_out("mov_ebx, %");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\nadd_eax,ebx\n");
        }
        else if(AMD64 == Architecture)
        {
            emit_out("mov_rbx, %");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\nadd_rax,rbx\n");
        }
        else if(ARMV7L == Architecture)
        {
            emit_out("!0 R1 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\n'0' R0 R0 ADD R1 ARITH2_ALWAYS\n");
        }
        else if(AARCH64 == Architecture)
        {
            emit_out("LOAD_W1_AHEAD\nSKIP_32_DATA\n%");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out("\nADD_X0_X1_X0\n");
        }
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
        {
            emit_out("RD_A1 !");
            emit_out(int2str(i->offset, 10, TRUE));
            emit_out(" ADDI\n");
            emit_out("RD_A0 RS1_A1 RS2_A0 ADD\n");
        }
    }
    if(match("=", global_token->s) || is_compound_assignment(global_token->s)) return;
    if(match("[", global_token->s)) return;

    emit_out(load_value(current_target->size, current_target->is_signed));
}

void postfix_expr_array()
{
    struct type* array = current_target;
    common_recursion(expression);
    current_target = array;
    require(NULL != current_target, "Arrays only apply to variables\n");

    char* assign = load_value(register_size, current_target->is_signed);

    /* Add support for Ints */
    if(match("char*", current_target->name))
    {
        assign = load_value(1, TRUE);
    }
    else
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R1 R15\nLOADI R1 ");
        else if(X86 == Architecture) emit_out("push_ebx\nmov_ebx, %");
        else if(AMD64 == Architecture) emit_out("push_rbx\nmov_rbx, %");
        else if(ARMV7L == Architecture) emit_out("{R1} PUSH_ALWAYS\n!0 R1 LOAD32 R15 MEMORY\n~0 JUMP_ALWAYS\n%");
        else if(AARCH64 == Architecture) emit_out("PUSH_X1\nLOAD_W1_AHEAD\nSKIP_32_DATA\n%");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A2 RS1_A1 ADDI\nRD_A1 !");
        emit_out(int2str(current_target->type->size, 10, TRUE));
        if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" ADDI");

        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("\nMULU R0 R1 R0\nPOPR R1 R15\n");
        else if(X86 == Architecture) emit_out("\nmul_ebx\npop_ebx\n");
        else if(AMD64 == Architecture) emit_out("\nmul_rbx\npop_rbx\n");
        else if(ARMV7L == Architecture) emit_out("\n'9' R0 '0' R1 MUL R0 ARITH2_ALWAYS\n{R1} POP_ALWAYS\n");
        else if(AARCH64 == Architecture) emit_out("\nMUL_X0_X1_X0\nPOP_X1\n");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("\nRD_A0 RS1_A1 RS2_A0 MUL\nRD_A1 RS1_A2 ADDI\n");
    }

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("ADD R0 R0 R1\n");
    else if(X86 == Architecture) emit_out("add_eax,ebx\n");
    else if(AMD64 == Architecture) emit_out("add_rax,rbx\n");
    else if(ARMV7L == Architecture) emit_out("'0' R0 R0 ADD R1 ARITH2_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("ADD_X0_X1_X0\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 RS1_A1 RS2_A0 ADD\n");

    require_match("ERROR in postfix_expr\nMissing ]\n", "]");
    require(NULL != global_token, "truncated array expression\n");

    if(match("=", global_token->s) || is_compound_assignment(global_token->s) || match(".", global_token->s))
    {
        assign = "";
    }
    if(match("[", global_token->s))
    {
        current_target = current_target->type;
    }

    emit_out(assign);
}

/*
 * unary-expr:
 *         &postfix-expr
 *         - postfix-expr
 *         !postfix-expr
 *         sizeof ( type )
 */
struct type* type_name();
void unary_expr_sizeof()
{
    global_token = global_token->next;
    require(NULL != global_token, "Received EOF when starting sizeof\n");
    require_match("ERROR in unary_expr\nMissing (\n", "(");
    struct type* a = type_name();
    require_match("ERROR in unary_expr\nMissing )\n", ")");

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("LOADUI R0 ");
    else if(X86 == Architecture) emit_out("mov_eax, %");
    else if(AMD64 == Architecture) emit_out("mov_rax, %");
    else if(ARMV7L == Architecture) emit_out("!");
    else if(AARCH64 == Architecture) emit_out("LOAD_W0_AHEAD\nSKIP_32_DATA\n%");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 !");
    emit_out(int2str(a->size, 10, TRUE));
    if(ARMV7L == Architecture) emit_out(" R0 LOADI8_ALWAYS");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" ADDI");
    emit_out("\n");
}

void postfix_expr_stub()
{
    require(NULL != global_token, "Unexpected EOF, improperly terminated primary expression\n");
    if(match("[", global_token->s))
    {
        postfix_expr_array();
        postfix_expr_stub();
    }

    if(match("->", global_token->s))
    {
        postfix_expr_arrow();
        postfix_expr_stub();
    }

    if(match(".", global_token->s))
    {
        postfix_expr_dot();
        postfix_expr_stub();
    }
}

void postfix_expr()
{
    primary_expr();
    postfix_expr_stub();
}

/*
 * additive-expr:
 *         postfix-expr
 *         additive-expr * postfix-expr
 *         additive-expr / postfix-expr
 *         additive-expr % postfix-expr
 *         additive-expr + postfix-expr
 *         additive-expr - postfix-expr
 *         additive-expr << postfix-expr
 *         additive-expr >> postfix-expr
 */
void additive_expr_stub()
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        arithmetic_recursion(postfix_expr, "ADD R0 R1 R0\n", "ADDU R0 R1 R0\n", "+", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "SUB R0 R1 R0\n", "SUBU R0 R1 R0\n", "-", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "MUL R0 R1 R0\n", "MULU R0 R1 R0\n", "*", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "DIV R0 R1 R0\n", "DIVU R0 R1 R0\n", "/", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "MOD R0 R1 R0\n", "MODU R0 R1 R0\n", "%", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "SAL R0 R1 R0\n", "SL0 R0 R1 R0\n", "<<", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "SAR R0 R1 R0\n", "SR0 R0 R1 R0\n", ">>", additive_expr_stub);
    }
    else if(X86 == Architecture)
    {
        arithmetic_recursion(postfix_expr, "add_eax,ebx\n", "add_eax,ebx\n", "+", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "sub_ebx,eax\nmov_eax,ebx\n", "sub_ebx,eax\nmov_eax,ebx\n", "-", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "imul_ebx\n", "mul_ebx\n", "*", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "xchg_ebx,eax\ncdq\nidiv_ebx\n", "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\n", "/", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "xchg_ebx,eax\ncdq\nidiv_ebx\nmov_eax,edx\n", "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\nmov_eax,edx\n", "%", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "mov_ecx,eax\nmov_eax,ebx\nsal_eax,cl\n", "mov_ecx,eax\nmov_eax,ebx\nshl_eax,cl\n", "<<", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "mov_ecx,eax\nmov_eax,ebx\nsar_eax,cl\n", "mov_ecx,eax\nmov_eax,ebx\nshr_eax,cl\n", ">>", additive_expr_stub);
    }
    else if(AMD64 == Architecture)
    {
        arithmetic_recursion(postfix_expr, "add_rax,rbx\n", "add_rax,rbx\n", "+", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "sub_rbx,rax\nmov_rax,rbx\n", "sub_rbx,rax\nmov_rax,rbx\n", "-", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "imul_rbx\n", "mul_rbx\n", "*", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "xchg_rbx,rax\ncqo\nidiv_rbx\n", "xchg_rbx,rax\nmov_rdx, %0\ndiv_rbx\n", "/", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "xchg_rbx,rax\ncqo\nidiv_rbx\nmov_rax,rdx\n", "xchg_rbx,rax\nmov_rdx, %0\ndiv_rbx\nmov_rax,rdx\n", "%", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "mov_rcx,rax\nmov_rax,rbx\nsal_rax,cl\n", "mov_rcx,rax\nmov_rax,rbx\nshl_rax,cl\n", "<<", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "mov_rcx,rax\nmov_rax,rbx\nsar_rax,cl\n", "mov_rcx,rax\nmov_rax,rbx\nshr_rax,cl\n", ">>", additive_expr_stub);
    }
    else if(ARMV7L == Architecture)
    {
        arithmetic_recursion(postfix_expr, "'0' R0 R0 ADD R1 ARITH2_ALWAYS\n", "'0' R0 R0 ADD R1 ARITH2_ALWAYS\n", "+", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "'0' R0 R0 SUB R1 ARITH2_ALWAYS\n", "'0' R0 R0 SUB R1 ARITH2_ALWAYS\n", "-", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "'9' R0 '0' R1 MULS R0 ARITH2_ALWAYS\n", "'9' R0 '0' R1 MUL R0 ARITH2_ALWAYS\n", "*", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "{LR} PUSH_ALWAYS\n^~divides CALL_ALWAYS\n{LR} POP_ALWAYS\n", "{LR} PUSH_ALWAYS\n^~divide CALL_ALWAYS\n{LR} POP_ALWAYS\n", "/", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "{LR} PUSH_ALWAYS\n^~moduluss CALL_ALWAYS\n{LR} POP_ALWAYS\n", "{LR} PUSH_ALWAYS\n^~modulus CALL_ALWAYS\n{LR} POP_ALWAYS\n", "%", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "LEFT R1 R0 R0 SHIFT AUX_ALWAYS\n", "LEFT R1 R0 R0 SHIFT AUX_ALWAYS\n", "<<", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "ARITH_RIGHT R1 R0 R0 SHIFT AUX_ALWAYS\n", "RIGHT R1 R0 R0 SHIFT AUX_ALWAYS\n", ">>", additive_expr_stub);
    }
    else if(AARCH64 == Architecture)
    {
        general_recursion(postfix_expr, "ADD_X0_X1_X0\n", "+", additive_expr_stub);
        general_recursion(postfix_expr, "SUB_X0_X1_X0\n", "-", additive_expr_stub);
        general_recursion(postfix_expr, "MUL_X0_X1_X0\n", "*", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "SDIV_X0_X1_X0\n", "UDIV_X0_X1_X0\n", "/", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "SDIV_X2_X1_X0\nMSUB_X0_X0_X2_X1\n", "UDIV_X2_X1_X0\nMSUB_X0_X0_X2_X1\n", "%", additive_expr_stub);
        general_recursion(postfix_expr, "LSHIFT_X0_X1_X0\n", "<<", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "ARITH_RSHIFT_X0_X1_X0\n", "LOGICAL_RSHIFT_X0_X1_X0\n", ">>", additive_expr_stub);
    }
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        general_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 ADD\n", "+", additive_expr_stub);
        general_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 SUB\n", "-", additive_expr_stub);
        general_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 MUL\n", "*", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 DIV\n", "RD_A0 RS1_A1 RS2_A0 DIVU\n", "/", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 REM\n", "RD_A0 RS1_A1 RS2_A0 REMU\n", "%", additive_expr_stub);
        general_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 SLL\n", "<<", additive_expr_stub);
        arithmetic_recursion(postfix_expr, "RD_A0 RS1_A1 RS2_A0 SRA\n", "RD_A0 RS1_A1 RS2_A0 SRL\n", ">>", additive_expr_stub);
    }
}


void additive_expr()
{
    postfix_expr();
    additive_expr_stub();
}


/*
 * relational-expr:
 *         additive_expr
 *         relational-expr < additive_expr
 *         relational-expr <= additive_expr
 *         relational-expr >= additive_expr
 *         relational-expr > additive_expr
 */

void relational_expr_stub()
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        arithmetic_recursion(additive_expr, "CMP R0 R1 R0\nSET.L R0 R0 1\n", "CMPU R0 R1 R0\nSET.L R0 R0 1\n", "<", relational_expr_stub);
        arithmetic_recursion(additive_expr, "CMP R0 R1 R0\nSET.LE R0 R0 1\n", "CMPU R0 R1 R0\nSET.LE R0 R0 1\n", "<=", relational_expr_stub);
        arithmetic_recursion(additive_expr, "CMP R0 R1 R0\nSET.GE R0 R0 1\n", "CMPU R0 R1 R0\nSET.GE R0 R0 1\n", ">=", relational_expr_stub);
        arithmetic_recursion(additive_expr, "CMP R0 R1 R0\nSET.G R0 R0 1\n", "CMPU R0 R1 R0\nSET.G R0 R0 1\n", ">", relational_expr_stub);
        arithmetic_recursion(additive_expr, "CMP R0 R1 R0\nSET.E R0 R0 1\n", "CMPU R0 R1 R0\nSET.E R0 R0 1\n", "==", relational_expr_stub);
        arithmetic_recursion(additive_expr, "CMP R0 R1 R0\nSET.NE R0 R0 1\n", "CMPU R0 R1 R0\nSET.NE R0 R0 1\n", "!=", relational_expr_stub);
    }
    else if(X86 == Architecture)
    {
        arithmetic_recursion(additive_expr, "cmp\nsetl_al\nmovzx_eax,al\n", "cmp\nsetb_al\nmovzx_eax,al\n", "<", relational_expr_stub);
        arithmetic_recursion(additive_expr, "cmp\nsetle_al\nmovzx_eax,al\n", "cmp\nsetbe_al\nmovzx_eax,al\n", "<=", relational_expr_stub);
        arithmetic_recursion(additive_expr, "cmp\nsetge_al\nmovzx_eax,al\n", "cmp\nsetae_al\nmovzx_eax,al\n", ">=", relational_expr_stub);
        arithmetic_recursion(additive_expr, "cmp\nsetg_al\nmovzx_eax,al\n", "cmp\nseta_al\nmovzx_eax,al\n", ">", relational_expr_stub);
        general_recursion(additive_expr, "cmp\nsete_al\nmovzx_eax,al\n", "==", relational_expr_stub);
        general_recursion(additive_expr, "cmp\nsetne_al\nmovzx_eax,al\n", "!=", relational_expr_stub);
    }
    else if(AMD64 == Architecture)
    {
        arithmetic_recursion(additive_expr, "cmp_rbx,rax\nsetl_al\nmovzx_rax,al\n", "cmp_rbx,rax\nsetb_al\nmovzx_rax,al\n", "<", relational_expr_stub);
        arithmetic_recursion(additive_expr, "cmp_rbx,rax\nsetle_al\nmovzx_rax,al\n", "cmp_rbx,rax\nsetbe_al\nmovzx_rax,al\n", "<=", relational_expr_stub);
        arithmetic_recursion(additive_expr, "cmp_rbx,rax\nsetge_al\nmovzx_rax,al\n", "cmp_rbx,rax\nsetae_al\nmovzx_rax,al\n", ">=", relational_expr_stub);
        arithmetic_recursion(additive_expr, "cmp_rbx,rax\nsetg_al\nmovzx_rax,al\n", "cmp_rbx,rax\nseta_al\nmovzx_rax,al\n", ">", relational_expr_stub);
        general_recursion(additive_expr, "cmp_rbx,rax\nsete_al\nmovzx_rax,al\n", "==", relational_expr_stub);
        general_recursion(additive_expr, "cmp_rbx,rax\nsetne_al\nmovzx_rax,al\n", "!=", relational_expr_stub);
    }
    else if(ARMV7L == Architecture)
    {
        arithmetic_recursion(additive_expr, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_L\n", "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_LO\n", "<", relational_expr_stub);
        arithmetic_recursion(additive_expr, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_LE\n", "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_LS\n", "<=", relational_expr_stub);
        arithmetic_recursion(additive_expr, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_GE\n", "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_HS\n", ">=", relational_expr_stub);
        arithmetic_recursion(additive_expr, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_G\n", "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_HI\n", ">", relational_expr_stub);
        general_recursion(additive_expr, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_EQUAL\n", "==", relational_expr_stub);
        general_recursion(additive_expr, "'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_NE\n", "!=", relational_expr_stub);
    }
    else if(AARCH64 == Architecture)
    {
        arithmetic_recursion(additive_expr, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LT\nSET_X0_TO_0\n", "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LO\nSET_X0_TO_0\n", "<", relational_expr_stub);
        arithmetic_recursion(additive_expr, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LE\nSET_X0_TO_0\n", "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_LS\nSET_X0_TO_0\n", "<=", relational_expr_stub);
        arithmetic_recursion(additive_expr, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_GE\nSET_X0_TO_0\n", "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_HS\nSET_X0_TO_0\n", ">=", relational_expr_stub);
        arithmetic_recursion(additive_expr, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_GT\nSET_X0_TO_0\n", "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_HI\nSET_X0_TO_0\n", ">", relational_expr_stub);
        general_recursion(additive_expr, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_EQ\nSET_X0_TO_0\n", "==", relational_expr_stub);
        general_recursion(additive_expr, "CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_NE\nSET_X0_TO_0\n", "!=", relational_expr_stub);
    }
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        arithmetic_recursion(additive_expr, "RD_A0 RS1_A1 RS2_A0 SLT\n", "RD_A0 RS1_A1 RS2_A0 SLTU\n", "<", relational_expr_stub);
        arithmetic_recursion(additive_expr, "RD_A0 RS1_A0 RS2_A1 SLT\nRD_A0 RS1_A0 !1 XORI\n", "RD_A0 RS1_A0 RS2_A1 SLTU\nRD_A0 RS1_A0 !1 XORI\n", "<=", relational_expr_stub);
        arithmetic_recursion(additive_expr, "RD_A0 RS1_A1 RS2_A0 SLT\nRD_A0 RS1_A0 !1 XORI\n", "RD_A0 RS1_A1 RS2_A0 SLTU\nRD_A0 RS1_A0 !1 XORI\n", ">=", relational_expr_stub);
        arithmetic_recursion(additive_expr, "RD_A0 RS1_A0 RS2_A1 SLT\n", "RD_A0 RS1_A0 RS2_A1 SLTU\n", ">", relational_expr_stub);
        general_recursion(additive_expr, "RD_A0 RS1_A0 RS2_A1 SUB\nRD_A0 RS1_A0 !1 SLTIU\n", "==", relational_expr_stub);
        general_recursion(additive_expr, "RD_A0 RS1_A0 RS2_A1 SUB\nRD_A0 RS2_A0 SLTU\n", "!=", relational_expr_stub);
    }
}

void relational_expr()
{
    additive_expr();
    relational_expr_stub();
}

/*
 * bitwise-expr:
 *         relational-expr
 *         bitwise-expr & bitwise-expr
 *         bitwise-expr && bitwise-expr
 *         bitwise-expr | bitwise-expr
 *         bitwise-expr || bitwise-expr
 *         bitwise-expr ^ bitwise-expr
 */
void bitwise_expr_stub()
{
    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
    {
        general_recursion(relational_expr, "AND R0 R0 R1\n", "&", bitwise_expr_stub);
        general_recursion(relational_expr, "AND R0 R0 R1\n", "&&", bitwise_expr_stub);
        general_recursion(relational_expr, "OR R0 R0 R1\n", "|", bitwise_expr_stub);
        general_recursion(relational_expr, "OR R0 R0 R1\n", "||", bitwise_expr_stub);
        general_recursion(relational_expr, "XOR R0 R0 R1\n", "^", bitwise_expr_stub);
    }
    else if(X86 == Architecture)
    {
        general_recursion(relational_expr, "and_eax,ebx\n", "&", bitwise_expr_stub);
        general_recursion(relational_expr, "and_eax,ebx\n", "&&", bitwise_expr_stub);
        general_recursion(relational_expr, "or_eax,ebx\n", "|", bitwise_expr_stub);
        general_recursion(relational_expr, "or_eax,ebx\n", "||", bitwise_expr_stub);
        general_recursion(relational_expr, "xor_eax,ebx\n", "^", bitwise_expr_stub);
    }
    else if(AMD64 == Architecture)
    {
        general_recursion(relational_expr, "and_rax,rbx\n", "&", bitwise_expr_stub);
        general_recursion(relational_expr, "and_rax,rbx\n", "&&", bitwise_expr_stub);
        general_recursion(relational_expr, "or_rax,rbx\n", "|", bitwise_expr_stub);
        general_recursion(relational_expr, "or_rax,rbx\n", "||", bitwise_expr_stub);
        general_recursion(relational_expr, "xor_rax,rbx\n", "^", bitwise_expr_stub);
    }
    else if(ARMV7L == Architecture)
    {
        general_recursion(relational_expr, "NO_SHIFT R0 R0 AND R1 ARITH2_ALWAYS\n", "&", bitwise_expr_stub);
        general_recursion(relational_expr, "NO_SHIFT R0 R0 AND R1 ARITH2_ALWAYS\n", "&&", bitwise_expr_stub);
        general_recursion(relational_expr, "NO_SHIFT R0 R0 OR R1 AUX_ALWAYS\n", "|", bitwise_expr_stub);
        general_recursion(relational_expr, "NO_SHIFT R0 R0 OR R1 AUX_ALWAYS\n", "||", bitwise_expr_stub);
        general_recursion(relational_expr, "'0' R0 R0 XOR R1 ARITH2_ALWAYS\n", "^", bitwise_expr_stub);
    }
    else if(AARCH64 == Architecture)
    {
        general_recursion(relational_expr, "AND_X0_X1_X0\n", "&", bitwise_expr_stub);
        general_recursion(relational_expr, "AND_X0_X1_X0\n", "&&", bitwise_expr_stub);
        general_recursion(relational_expr, "OR_X0_X1_X0\n", "|", bitwise_expr_stub);
        general_recursion(relational_expr, "OR_X0_X1_X0\n", "||", bitwise_expr_stub);
        general_recursion(relational_expr, "XOR_X0_X1_X0\n", "^", bitwise_expr_stub);
    }
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        general_recursion(relational_expr, "RD_A0 RS1_A1 RS2_A0 AND\n", "&", bitwise_expr_stub);
        general_recursion(relational_expr, "RD_A0 RS1_A1 RS2_A0 AND\n", "&&", bitwise_expr_stub);
        general_recursion(relational_expr, "RD_A0 RS1_A1 RS2_A0 OR\n", "|", bitwise_expr_stub);
        general_recursion(relational_expr, "RD_A0 RS1_A1 RS2_A0 OR\n", "||", bitwise_expr_stub);
        general_recursion(relational_expr, "RD_A0 RS1_A1 RS2_A0 XOR\n", "^", bitwise_expr_stub);
    }
}


void bitwise_expr()
{
    relational_expr();
    bitwise_expr_stub();
}

/*
 * expression:
 *         bitwise-or-expr
 *         bitwise-or-expr = expression
 */

void primary_expr()
{
    require(NULL != global_token, "Received EOF where primary expression expected\n");
    if(match("&", global_token->s))
    {
        Address_of = TRUE;
        global_token = global_token->next;
        require(NULL != global_token, "Received EOF after & where primary expression expected\n");
    }
    else
    {
        Address_of = FALSE;
    }

    if(match("sizeof", global_token->s)) unary_expr_sizeof();
    else if('-' == global_token->s[0])
    {
        if(X86 == Architecture) emit_out("mov_eax, %0\n");
        else if(AMD64 == Architecture) emit_out("mov_rax, %0\n");
        else if(ARMV7L == Architecture) emit_out("!0 R0 LOADI8_ALWAYS\n");
        else if(AARCH64 == Architecture) emit_out("SET_X0_TO_0\n");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 MV\n");

        common_recursion(primary_expr);

        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("NEG R0 R0\n");
        else if(X86 == Architecture) emit_out("sub_ebx,eax\nmov_eax,ebx\n");
        else if(AMD64 == Architecture) emit_out("sub_rbx,rax\nmov_rax,rbx\n");
        else if(ARMV7L == Architecture) emit_out("'0' R0 R0 SUB R1 ARITH2_ALWAYS\n");
        else if(AARCH64 == Architecture) emit_out("SUB_X0_X1_X0\n");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 RS1_A1 RS2_A0 SUB\n");
    }
    else if('!' == global_token->s[0])
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))  emit_out("LOADI R0 1\n");
        else if(X86 == Architecture) emit_out("mov_eax, %1\n");
        else if(AMD64 == Architecture) emit_out("mov_rax, %1\n");
        else if(ARMV7L == Architecture) emit_out("!1 R0 LOADI8_ALWAYS\n");
        else if(AARCH64 == Architecture) emit_out("SET_X0_TO_1\n");

        common_recursion(postfix_expr);

        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("CMPU R0 R1 R0\nSET.G R0 R0 1\n");
        else if(X86 == Architecture) emit_out("cmp\nseta_al\nmovzx_eax,al\n");
        else if(AMD64 == Architecture) emit_out("cmp_rbx,rax\nseta_al\nmovzx_rax,al\n");
        else if(ARMV7L == Architecture) emit_out("'0' R0 CMP R1 AUX_ALWAYS\n!0 R0 LOADI8_ALWAYS\n!1 R0 LOADI8_HI\n");
        else if(AARCH64 == Architecture) emit_out("CMP_X1_X0\nSET_X0_TO_1\nSKIP_INST_HI\nSET_X0_TO_0\n");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 RS1_A0 !1 SLTIU\n");
    }
    else if('~' == global_token->s[0])
    {
        common_recursion(postfix_expr);

        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("NOT R0 R0\n");
        else if(X86 == Architecture) emit_out("not_eax\n");
        else if(AMD64 == Architecture) emit_out("not_rax\n");
        else if(ARMV7L == Architecture) emit_out("'0' R0 R0 MVN_ALWAYS\n");
        else if(AARCH64 == Architecture) emit_out("MVN_X0\n");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RD_A0 RS1_A0 NOT\n");
    }
    else if(global_token->s[0] == '(')
    {
        global_token = global_token->next;
        expression();
        require_match("Error in Primary expression\nDidn't get )\n", ")");
    }
    else if(global_token->s[0] == '\'') primary_expr_char();
    else if(global_token->s[0] == '"') primary_expr_string();
    else if(in_set(global_token->s[0], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_")) primary_expr_variable();
    else if(global_token->s[0] == '*') primary_expr_variable();
    else if(in_set(global_token->s[0], "0123456789")) primary_expr_number();
    else primary_expr_failure();
}

char* compound_operation(char* operator, int is_signed)
{
    char* operation = "";
    if(match("+=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "ADD R0 R1 R0\n";
            else operation = "ADDU R0 R1 R0\n";
        }
        else if(X86 == Architecture) operation = "add_eax,ebx\n";
        else if(AMD64 == Architecture) operation = "add_rax,rbx\n";
        else if(ARMV7L == Architecture) operation = "'0' R0 R0 ADD R1 ARITH2_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "ADD_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 ADD\n";
    }
    else if(match("-=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "SUB R0 R1 R0\n";
            else operation =  "SUBU R0 R1 R0\n";
        }
        else if(X86 == Architecture) operation = "sub_ebx,eax\nmov_eax,ebx\n";
        else if(AMD64 == Architecture) operation = "sub_rbx,rax\nmov_rax,rbx\n";
        else if(ARMV7L == Architecture) operation = "'0' R0 R0 SUB R1 ARITH2_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "SUB_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 SUB\n";
    }
    else if(match("*=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "MUL R0 R1 R0\n";
            else operation =  "MULU R0 R1 R0\n";
        }
        else if(X86 == Architecture)
        {
            if(is_signed) operation = "imul_ebx\n";
            else operation = "mul_ebx\n";
        }
        else if(AMD64 == Architecture)
        {
            if(is_signed) operation = "imul_rbx\n";
            else operation = "mul_rbx\n";
        }
        else if(ARMV7L == Architecture) operation = "'9' R0 '0' R1 MULS R0 ARITH2_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "MUL_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 MUL\n";
    }
    else if(match("/=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "DIV R0 R1 R0\n";
            else operation =  "DIVU R0 R1 R0\n";
        }
        else if(X86 == Architecture)
        {
            if (is_signed) operation = "xchg_ebx,eax\ncdq\nidiv_ebx\n";
            else operation = "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\n";
        }
        else if(AMD64 == Architecture)
        {
            if(is_signed) operation = "xchg_rbx,rax\ncqo\nidiv_rbx\n";
            else operation = "xchg_rbx,rax\nmov_rdx, %0\ndiv_rbx\n";
        }
        else if(ARMV7L == Architecture)
        {
            if(is_signed) operation = "{LR} PUSH_ALWAYS\n^~divides CALL_ALWAYS\n{LR} POP_ALWAYS\n";
            else operation = "{LR} PUSH_ALWAYS\n^~divide CALL_ALWAYS\n{LR} POP_ALWAYS\n";
        }
        else if(AARCH64 == Architecture)
        {
            if(is_signed) operation = "SDIV_X0_X1_X0\n";
            else operation = "UDIV_X0_X1_X0\n";
        }
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
        {
            if(is_signed) operation = "RD_A0 RS1_A1 RS2_A0 DIV\n";
            else operation = "RD_A0 RS1_A1 RS2_A0 DIVU\n";
        }
    }
    else if(match("%=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "MOD R0 R1 R0\n";
            else operation = "MODU R0 R1 R0\n";
        }
        else if(X86 == Architecture)
        {
            if(is_signed) operation = "xchg_ebx,eax\ncdq\nidiv_ebx\nmov_eax,edx\n";
            else operation = "xchg_ebx,eax\nmov_edx, %0\ndiv_ebx\nmov_eax,edx\n";
        }
        else if(AMD64 == Architecture)
        {
            if(is_signed) operation = "xchg_rbx,rax\ncqo\nidiv_rbx\nmov_rax,rdx\n";
            else operation = "xchg_rbx,rax\nmov_rdx, %0\ndiv_rbx\nmov_rax,rdx\n";
        }
        else if(ARMV7L == Architecture)
        {
            if(is_signed) operation = "{LR} PUSH_ALWAYS\n^~moduluss CALL_ALWAYS\n{LR} POP_ALWAYS\n";
            else operation = "{LR} PUSH_ALWAYS\n^~modulus CALL_ALWAYS\n{LR} POP_ALWAYS\n";
        }
        else if(AARCH64 == Architecture)
        {
            if(is_signed) operation = "SDIV_X2_X1_X0\nMSUB_X0_X0_X2_X1\n";
            else operation = "UDIV_X2_X1_X0\nMSUB_X0_X0_X2_X1\n";
        }
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
        {
            if(is_signed) operation = "RD_A0 RS1_A1 RS2_A0 REM\n";
            else operation = "RD_A0 RS1_A1 RS2_A0 REMU\n";
        }
    }
    else if(match("<<=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "SAL R0 R1 R0\n";
            else operation = "SL0 R0 R1 R0\n";
        }
        else if(X86 == Architecture)
        {
            if(is_signed) operation = "mov_ecx,eax\nmov_eax,ebx\nsal_eax,cl\n";
            else operation = "mov_ecx,eax\nmov_eax,ebx\nshl_eax,cl\n";
        }
        else if(AMD64 == Architecture)
        {
            if(is_signed) operation = "mov_rcx,rax\nmov_rax,rbx\nsal_rax,cl\n";
            else operation = "mov_rcx,rax\nmov_rax,rbx\nshl_rax,cl\n";
        }
        else if(ARMV7L == Architecture) operation = "LEFT R1 R0 R0 SHIFT AUX_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "LSHIFT_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 SLL\n";
    }
    else if(match(">>=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture))
        {
            if(is_signed) operation = "SAR R0 R1 R0\n";
            else operation = "SR0 R0 R1 R0\n";
        }
        else if(X86 == Architecture)
        {
            if(is_signed) operation = "mov_ecx,eax\nmov_eax,ebx\nsar_eax,cl\n";
            else operation = "mov_ecx,eax\nmov_eax,ebx\nshr_eax,cl\n";
        }
        else if(AMD64 == Architecture)
        {
            if(is_signed) operation = "mov_rcx,rax\nmov_rax,rbx\nsar_rax,cl\n";
            else operation = "mov_rcx,rax\nmov_rax,rbx\nshr_rax,cl\n";
        }
        else if(ARMV7L == Architecture)
        {
            if(is_signed) operation = "ARITH_RIGHT R1 R0 R0 SHIFT AUX_ALWAYS\n";
            else operation = "RIGHT R1 R0 R0 SHIFT AUX_ALWAYS\n";
        }
        else if(AARCH64 == Architecture)
        {
            if(is_signed) operation = "ARITH_RSHIFT_X0_X1_X0\n";
            else operation = "LOGICAL_RSHIFT_X0_X1_X0\n";
        }
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
        {
            if(is_signed) operation = "RD_A0 RS1_A1 RS2_A0 SRA\n";
            else operation = "RD_A0 RS1_A1 RS2_A0 SRL\n";
        }
    }
    else if(match("&=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) operation = "AND R0 R0 R1\n";
        else if(X86 == Architecture) operation = "and_eax,ebx\n";
        else if(AMD64 == Architecture) operation = "and_rax,rbx\n";
        else if(ARMV7L == Architecture) operation = "NO_SHIFT R0 R0 AND R1 ARITH2_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "AND_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 AND\n";
    }
    else if(match("^=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) operation = "XOR R0 R0 R1\n";
        else if(X86 == Architecture) operation = "xor_eax,ebx\n";
        else if(AMD64 == Architecture) operation = "xor_rax,rbx\n";
        else if(ARMV7L == Architecture) operation = "'0' R0 R0 XOR R1 ARITH2_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "XOR_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 XOR\n";
    }
    else if(match("|=", operator))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) operation = "OR R0 R0 R1\n";
        else if(X86 == Architecture) operation = "or_eax,ebx\n";
        else if(AMD64 == Architecture) operation = "or_rax,rbx\n";
        else if(ARMV7L == Architecture) operation = "NO_SHIFT R0 R0 OR R1 AUX_ALWAYS\n";
        else if(AARCH64 == Architecture) operation = "OR_X0_X1_X0\n";
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) operation = "RD_A0 RS1_A1 RS2_A0 OR\n";
    }
    else
    {
        fputs("Found illegal compound assignment operator: ", stderr);
        fputs(operator, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }
    return operation;
}


void expression()
{
    bitwise_expr();
    if(match("=", global_token->s))
    {
        char* store = "";
        if(match("]", global_token->prev->s))
        {
            store = store_value(current_target->type->size);
        }
        else
        {
            store = store_value(current_target->size);
        }

        common_recursion(expression);
        emit_out(store);
        current_target = integer;
    }
    else if(is_compound_assignment(global_token->s))
    {
        maybe_bootstrap_error("compound operator");
        char* push = "";
        char* load = "";
        char* operation = "";
        char* pop = "";
        char* store = "";
        struct type* last_type = current_target;

        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) push = "PUSHR R1 R15\n";
        else if(X86 == Architecture) push = "push_ebx\n";
        else if(AMD64 == Architecture) push = "push_rbx\n";
        else if(ARMV7L == Architecture) push = "{R1} PUSH_ALWAYS\n";
        else if(AARCH64 == Architecture) push = "PUSH_X1\n";
        else if(RISCV32 == Architecture) push = "RS1_SP RS2_A1 @-4 SW\n";
        else if(RISCV64 == Architecture) push = "RS1_SP RS2_A1 @-8 SD\n";

        if(!match("]", global_token->prev->s) || !match("char*", current_target->name))
        {
            if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) load = "LOAD R1 R1 0\n";
            else if(X86 == Architecture) load = "mov_ebx,[ebx]\n";
            else if(AMD64 == Architecture) load = "mov_rbx,[rbx]\n";
            else if(ARMV7L == Architecture) load = "!0 R1 LOAD32 R1 MEMORY\n";
            else if(AARCH64 == Architecture) load = "DEREF_X1\n";
            else if(RISCV32 == Architecture) load = "RD_A1 RS1_A1 LW\n";
            else if(RISCV64 == Architecture) load = "RD_A1 RS1_A1 LD\n";
        }
        else
        {
            if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) load = "LOAD8 R1 R1 0\n";
            else if(X86 == Architecture) load = "movsx_ebx,BYTE_PTR_[ebx]\n";
            else if(AMD64 == Architecture) load = "movsx_rbx,BYTE_PTR_[rbx]\n";
            else if(ARMV7L == Architecture) load = "LOADU8 R1 LOAD R1 MEMORY\n";
            else if(AARCH64 == Architecture) load = "DEREF_X1_BYTE\n";
            else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) load = "RD_A1 RS1_A1 LBU\n";
        }

        char *operator = global_token->s;

        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) pop = "POPR R1 R15\n";
        else if(X86 == Architecture) pop = "pop_ebx\n";
        else if(AMD64 == Architecture) pop = "pop_rbx\n";
        else if(ARMV7L == Architecture) pop = "{R1} POP_ALWAYS\n";
        else if(AARCH64 == Architecture) pop = "POP_X1\n";
        else if(RISCV32 == Architecture) pop = "RD_A1 RS1_SP !-4 LW\n";
        else if(RISCV64 == Architecture) pop = "RD_A1 RS1_SP !-8 LD\n";

        if(match("]", global_token->prev->s))
        {
            store = store_value(current_target->type->size);
        }
        else
        {
            store = store_value(current_target->size);
        }

        common_recursion(expression);
        current_target = promote_type(current_target, last_type);
        emit_out(push);
        emit_out(load);
        operation = compound_operation(operator, current_target->is_signed);
        emit_out(operation);
        emit_out(pop);
        emit_out(store);
        current_target = integer;
    }
}


int iskeywordp(char* s)
{
    if(match("auto", s)) return TRUE;
    if(match("break", s)) return TRUE;
    if(match("case", s)) return TRUE;
    if(match("char", s)) return TRUE;
    if(match("const", s)) return TRUE;
    if(match("continue", s)) return TRUE;
    if(match("default", s)) return TRUE;
    if(match("do", s)) return TRUE;
    if(match("double", s)) return TRUE;
    if(match("else", s)) return TRUE;
    if(match("enum", s)) return TRUE;
    if(match("extern", s)) return TRUE;
    if(match("float", s)) return TRUE;
    if(match("for", s)) return TRUE;
    if(match("goto", s)) return TRUE;
    if(match("if", s)) return TRUE;
    if(match("int", s)) return TRUE;
    if(match("long", s)) return TRUE;
    if(match("register", s)) return TRUE;
    if(match("return", s)) return TRUE;
    if(match("short", s)) return TRUE;
    if(match("signed", s)) return TRUE;
    if(match("sizeof", s)) return TRUE;
    if(match("static", s)) return TRUE;
    if(match("struct", s)) return TRUE;
    if(match("switch", s)) return TRUE;
    if(match("typedef", s)) return TRUE;
    if(match("union", s)) return TRUE;
    if(match("unsigned", s)) return TRUE;
    if(match("void", s)) return TRUE;
    if(match("volatile", s)) return TRUE;
    if(match("while", s)) return TRUE;
    return FALSE;
}

/* Similar to integer division a / b but rounds up */
unsigned ceil_div(unsigned a, unsigned b)
{
    return (a + b - 1) / b;
}

/* Process local variable */
void collect_local()
{
    if(NULL != break_target_func)
    {
        fputs("Local variable initialized inside of loop in file: ", stderr);
        line_error();
        fputs("\nMove the variable outside of the loop to resolve\n", stderr);
        fputs("Otherwise the binary will segfault while running\n", stderr);
        exit(EXIT_FAILURE);
    }
    struct type* type_size = type_name();
    require(NULL != global_token, "Received EOF while collecting locals\n");
    require(!in_set(global_token->s[0], "[{(<=>)}]|&!^%;:'\""), "forbidden character in local variable name\n");
    require(!iskeywordp(global_token->s), "You are not allowed to use a keyword as a local variable name\n");
    require(NULL != type_size, "Must have non-null type\n");
    struct token_list* a = sym_declare(global_token->s, type_size, function->locals);
    if(match("main", function->s) && (NULL == function->locals))
    {
        if(KNIGHT_NATIVE == Architecture) a->depth = register_size;
        else if(KNIGHT_POSIX == Architecture) a->depth = 20;
        else if(X86 == Architecture) a->depth = -20;
        else if(AMD64 == Architecture) a->depth = -40;
        else if(ARMV7L == Architecture) a->depth = 16;
        else if(AARCH64 == Architecture) a->depth = 32; /* argc, argv, envp and the local (8 bytes each) */
        else if(RISCV32 == Architecture) a->depth = -16;
        else if(RISCV64 == Architecture) a->depth = -32;
    }
    else if((NULL == function->arguments) && (NULL == function->locals))
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = register_size;
        else if(X86 == Architecture) a->depth = -8;
        else if(AMD64 == Architecture) a->depth = -16;
        else if(ARMV7L == Architecture) a->depth = 8;
        else if(AARCH64 == Architecture) a->depth = register_size;
        else if(RISCV32 == Architecture) a->depth = -4;
        else if(RISCV64 == Architecture) a->depth = -8;
    }
    else if(NULL == function->locals)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = function->arguments->depth + 8;
        else if(X86 == Architecture) a->depth = function->arguments->depth - 8;
        else if(AMD64 == Architecture) a->depth = function->arguments->depth - 16;
        else if(ARMV7L == Architecture) a->depth = function->arguments->depth + 8;
        else if(AARCH64 == Architecture) a->depth = function->arguments->depth + register_size;
        else if(RISCV32 == Architecture) a->depth = function->arguments->depth - 4;
        else if(RISCV64 == Architecture) a->depth = function->arguments->depth - 8;
    }
    else
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = function->locals->depth + register_size;
        else if(X86 == Architecture) a->depth = function->locals->depth - register_size;
        else if(AMD64 == Architecture) a->depth = function->locals->depth - register_size;
        else if(ARMV7L == Architecture) a->depth = function->locals->depth + register_size;
        else if(AARCH64 == Architecture) a->depth = function->locals->depth + register_size;
        else if(RISCV32 == Architecture) a->depth = function->locals->depth - register_size;
        else if(RISCV64 == Architecture) a->depth = function->locals->depth - register_size;
    }

    /* Adjust the depth of local structs. When stack grows downwards, we want them to 
       start at the bottom of allocated space. */
    unsigned struct_depth_adjustment = (ceil_div(a->type->size, register_size) - 1) * register_size;
    if(KNIGHT_POSIX == Architecture) a->depth = a->depth + struct_depth_adjustment;
    else if(KNIGHT_NATIVE == Architecture) a->depth = a->depth + struct_depth_adjustment;
    else if(X86 == Architecture) a->depth = a->depth - struct_depth_adjustment;
    else if(AMD64 == Architecture) a->depth = a->depth - struct_depth_adjustment;
    else if(ARMV7L == Architecture) a->depth = a->depth + struct_depth_adjustment;
    else if(AARCH64 == Architecture) a->depth = a->depth + struct_depth_adjustment;
    else if(RISCV32 == Architecture) a->depth = a->depth - struct_depth_adjustment;
    else if(RISCV64 == Architecture) a->depth = a->depth - struct_depth_adjustment;

    function->locals = a;

    emit_out("# Defining local ");
    emit_out(global_token->s);
    emit_out("\n");

    global_token = global_token->next;
    require(NULL != global_token, "incomplete local missing name\n");

    if(match("=", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "incomplete local assignment\n");
        expression();
    }

    require_match("ERROR in collect_local\nMissing ;\n", ";");

    unsigned i = (a->type->size + register_size - 1) / register_size;
    while(i != 0)
    {
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("PUSHR R0 R15\t#");
        else if(X86 == Architecture) emit_out("push_eax\t#");
        else if(AMD64 == Architecture) emit_out("push_rax\t#");
        else if(ARMV7L == Architecture) emit_out("{R0} PUSH_ALWAYS\t#");
        else if(AARCH64 == Architecture) emit_out("PUSH_X0\t#");
        else if(RISCV32 == Architecture) emit_out("RD_SP RS1_SP !-4 ADDI\nRS1_SP RS2_A0 SW\t#");
        else if(RISCV64 == Architecture) emit_out("RD_SP RS1_SP !-8 ADDI\nRS1_SP RS2_A0 SD\t#");
        emit_out(a->s);
        emit_out("\n");
        i = i - 1;
    }
}

void statement();

/* Evaluate if statements */
void process_if()
{
    char* number_string = int2str(current_count, 10, TRUE);
    current_count = current_count + 1;

    emit_out("# IF_");
    uniqueID_out(function->s, number_string);

    global_token = global_token->next;
    require_match("ERROR in process_if\nMISSING (\n", "(");
    expression();

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP.Z R0 @ELSE_");
    else if(X86 == Architecture) emit_out("test_eax,eax\nje %ELSE_");
    else if(AMD64 == Architecture) emit_out("test_rax,rax\nje %ELSE_");
    else if(ARMV7L == Architecture) emit_out("!0 CMPI8 R0 IMM_ALWAYS\n^~ELSE_");
    else if(AARCH64 == Architecture) emit_out("CBNZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&ELSE_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RS1_A0 @8 BNEZ\n$ELSE_");

    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_EQUAL\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");

    require_match("ERROR in process_if\nMISSING )\n", ")");
    statement();
    require(NULL != global_token, "Reached EOF inside of function\n");

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @_END_IF_");
    else if(X86 == Architecture) emit_out("jmp %_END_IF_");
    else if(AMD64 == Architecture) emit_out("jmp %_END_IF_");
    else if(ARMV7L == Architecture) emit_out("^~_END_IF_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&_END_IF_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$_END_IF_");

    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");

    emit_out(":ELSE_");
    uniqueID_out(function->s, number_string);

    if(match("else", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "Received EOF where an else statement expected\n");
        statement();
        require(NULL != global_token, "Reached EOF inside of function\n");
    }
    emit_out(":_END_IF_");
    uniqueID_out(function->s, number_string);
}

void process_for()
{
    struct token_list* nested_locals = break_frame;
    char* nested_break_head = break_target_head;
    char* nested_break_func = break_target_func;
    char* nested_break_num = break_target_num;
    char* nested_continue_head = continue_target_head;

    char* number_string = int2str(current_count, 10, TRUE);
    current_count = current_count + 1;

    break_target_head = "FOR_END_";
    continue_target_head = "FOR_ITER_";
    break_target_num = number_string;
    break_frame = function->locals;
    break_target_func = function->s;

    emit_out("# FOR_initialization_");
    uniqueID_out(function->s, number_string);

    global_token = global_token->next;

    require_match("ERROR in process_for\nMISSING (\n", "(");
    if(!match(";",global_token->s))
    {
        expression();
    }

    emit_out(":FOR_");
    uniqueID_out(function->s, number_string);

    require_match("ERROR in process_for\nMISSING ;1\n", ";");
    expression();

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP.Z R0 @FOR_END_");
    else if(X86 == Architecture) emit_out("test_eax,eax\nje %FOR_END_");
    else if(AMD64 == Architecture) emit_out("test_rax,rax\nje %FOR_END_");
    else if(ARMV7L == Architecture) emit_out("!0 CMPI8 R0 IMM_ALWAYS\n^~FOR_END_");
    else if(AARCH64 == Architecture) emit_out("CBNZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_END_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RS1_A0 @8 BNEZ\n$FOR_END_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_EQUAL\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @FOR_THEN_");
    else if(X86 == Architecture) emit_out("jmp %FOR_THEN_");
    else if(AMD64 == Architecture) emit_out("jmp %FOR_THEN_");
    else if(ARMV7L == Architecture) emit_out("^~FOR_THEN_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_THEN_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$FOR_THEN_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");

    emit_out(":FOR_ITER_");
    uniqueID_out(function->s, number_string);

    require_match("ERROR in process_for\nMISSING ;2\n", ";");
    expression();

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @FOR_");
    else if(X86 == Architecture) emit_out("jmp %FOR_");
    else if(AMD64 == Architecture) emit_out("jmp %FOR_");
    else if(ARMV7L == Architecture) emit_out("^~FOR_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$FOR_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");

    emit_out(":FOR_THEN_");
    uniqueID_out(function->s, number_string);

    require_match("ERROR in process_for\nMISSING )\n", ")");
    statement();
    require(NULL != global_token, "Reached EOF inside of function\n");

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @FOR_ITER_");
    else if(X86 == Architecture) emit_out("jmp %FOR_ITER_");
    else if(AMD64 == Architecture) emit_out("jmp %FOR_ITER_");
    else if(ARMV7L == Architecture) emit_out("^~FOR_ITER_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&FOR_ITER_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$FOR_ITER_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");

    emit_out(":FOR_END_");
    uniqueID_out(function->s, number_string);

    break_target_head = nested_break_head;
    break_target_func = nested_break_func;
    break_target_num = nested_break_num;
    continue_target_head = nested_continue_head;
    break_frame = nested_locals;
}

/* Process Assembly statements */
void process_asm()
{
    global_token = global_token->next;
    require_match("ERROR in process_asm\nMISSING (\n", "(");
    while('"' == global_token->s[0])
    {
        emit_out((global_token->s + 1));
        emit_out("\n");
        global_token = global_token->next;
        require(NULL != global_token, "Received EOF inside asm statement\n");
    }
    require_match("ERROR in process_asm\nMISSING )\n", ")");
    require_match("ERROR in process_asm\nMISSING ;\n", ";");
}

/* Process do while loops */
void process_do()
{
    struct token_list* nested_locals = break_frame;
    char* nested_break_head = break_target_head;
    char* nested_break_func = break_target_func;
    char* nested_break_num = break_target_num;
    char* nested_continue_head = continue_target_head;

    char* number_string = int2str(current_count, 10, TRUE);
    current_count = current_count + 1;

    break_target_head = "DO_END_";
    continue_target_head = "DO_TEST_";
    break_target_num = number_string;
    break_frame = function->locals;
    break_target_func = function->s;

    emit_out(":DO_");
    uniqueID_out(function->s, number_string);

    global_token = global_token->next;
    require(NULL != global_token, "Received EOF where do statement is expected\n");
    statement();
    require(NULL != global_token, "Reached EOF inside of function\n");

    emit_out(":DO_TEST_");
    uniqueID_out(function->s, number_string);

    require_match("ERROR in process_do\nMISSING while\n", "while");
    require_match("ERROR in process_do\nMISSING (\n", "(");
    expression();
    require_match("ERROR in process_do\nMISSING )\n", ")");
    require_match("ERROR in process_do\nMISSING ;\n", ";");

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP.NZ R0 @DO_");
    else if(X86 == Architecture) emit_out("test_eax,eax\njne %DO_");
    else if(AMD64 == Architecture) emit_out("test_rax,rax\njne %DO_");
    else if(ARMV7L == Architecture) emit_out("!0 CMPI8 R0 IMM_ALWAYS\n^~DO_");
    else if(AARCH64 == Architecture) emit_out("CBZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&DO_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RS1_A0 @DO_END_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_NE\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        emit_out("BEQZ\n$DO_");
        uniqueID_out(function->s, number_string);
        emit_out("JAL\n");
    }

    emit_out(":DO_END_");
    uniqueID_out(function->s, number_string);

    break_frame = nested_locals;
    break_target_head = nested_break_head;
    break_target_func = nested_break_func;
    break_target_num = nested_break_num;
    continue_target_head = nested_continue_head;
}


/* Process while loops */
void process_while()
{
    struct token_list* nested_locals = break_frame;
    char* nested_break_head = break_target_head;
    char* nested_break_func = break_target_func;
    char* nested_break_num = break_target_num;
    char* nested_continue_head = continue_target_head;

    char* number_string = int2str(current_count, 10, TRUE);
    current_count = current_count + 1;

    break_target_head = "END_WHILE_";
    continue_target_head = "WHILE_";
    break_target_num = number_string;
    break_frame = function->locals;
    break_target_func = function->s;

    emit_out(":WHILE_");
    uniqueID_out(function->s, number_string);

    global_token = global_token->next;
    require_match("ERROR in process_while\nMISSING (\n", "(");
    expression();

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP.Z R0 @END_WHILE_");
    else if(X86 == Architecture) emit_out("test_eax,eax\nje %END_WHILE_");
    else if(AMD64 == Architecture) emit_out("test_rax,rax\nje %END_WHILE_");
    else if(ARMV7L == Architecture) emit_out("!0 CMPI8 R0 IMM_ALWAYS\n^~END_WHILE_");
    else if(AARCH64 == Architecture) emit_out("CBNZ_X0_PAST_BR\nLOAD_W16_AHEAD\nSKIP_32_DATA\n&END_WHILE_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RS1_A0 @8 BNEZ\n$END_WHILE_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_EQUAL\t");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");
    emit_out("# THEN_while_");
    uniqueID_out(function->s, number_string);

    require_match("ERROR in process_while\nMISSING )\n", ")");
    statement();
    require(NULL != global_token, "Reached EOF inside of function\n");

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @WHILE_");
    else if(X86 == Architecture) emit_out("jmp %WHILE_");
    else if(AMD64 == Architecture) emit_out("jmp %WHILE_");
    else if(ARMV7L == Architecture) emit_out("^~WHILE_");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&WHILE_");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$WHILE_");
    uniqueID_out(function->s, number_string);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS\n");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("JAL\n");
    emit_out(":END_WHILE_");
    uniqueID_out(function->s, number_string);

    break_target_head = nested_break_head;
    break_target_func = nested_break_func;
    break_target_num = nested_break_num;
    continue_target_head = nested_continue_head;
    break_frame = nested_locals;
}

/* Ensure that functions return */
void return_result()
{
    global_token = global_token->next;
    require(NULL != global_token, "Incomplete return statement received\n");
    if(global_token->s[0] != ';') expression();

    require_match("ERROR in return_result\nMISSING ;\n", ";");

    struct token_list* i;
    unsigned size_local_var;
    for(i = function->locals; NULL != i; i = i->next)
    {
        size_local_var = ceil_div(i->type->size, register_size);
        while(size_local_var != 0)
        {
            if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# _return_result_locals\n");
            else if(X86 == Architecture) emit_out("pop_ebx\t# _return_result_locals\n");
            else if(AMD64 == Architecture) emit_out("pop_rbx\t# _return_result_locals\n");
            else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# _return_result_locals\n");
            else if(AARCH64 == Architecture) emit_out("POP_X1\t# _return_result_locals\n");
            else if(RISCV32 == Architecture) emit_out("RD_A1 RS1_SP LW  # _return_result_locals\nRD_SP RS1_SP !4 ADDI\n");
            else if(RISCV64 == Architecture) emit_out("RD_A1 RS1_SP LD  # _return_result_locals\nRD_SP RS1_SP !8 ADDI\n");
            size_local_var = size_local_var - 1;
        }
    }

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("RET R15\n");
    else if(X86 == Architecture) emit_out("ret\n");
    else if(AMD64 == Architecture) emit_out("ret\n");
    else if(ARMV7L == Architecture) emit_out("'1' LR RETURN\n");
    else if(AARCH64 == Architecture) emit_out("RETURN\n");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("RETURN\n");
}

void process_break()
{
    if(NULL == break_target_head)
    {
        line_error();
        fputs("Not inside of a loop or case statement\n", stderr);
        exit(EXIT_FAILURE);
    }
    struct token_list* i = function->locals;
    while(i != break_frame)
    {
        if(NULL == i) break;
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# break_cleanup_locals\n");
        else if(X86 == Architecture) emit_out("pop_ebx\t# break_cleanup_locals\n");
        else if(AMD64 == Architecture) emit_out("pop_rbx\t# break_cleanup_locals\n");
        else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# break_cleanup_locals\n");
        else if(AARCH64 == Architecture) emit_out("POP_X1\t# break_cleanup_locals\n");
        else if(RISCV32 == Architecture) emit_out("RD_A1 RS1_SP LW\t# break_cleanup_locals\nRD_SP RS1_SP !4 ADDI\n");
        else if(RISCV64 == Architecture) emit_out("RD_A1 RS1_SP LD\t# break_cleanup_locals\nRD_SP RS1_SP !8 ADDI\n");
        i = i->next;
    }
    global_token = global_token->next;

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @");
    else if(X86 == Architecture) emit_out("jmp %");
    else if(AMD64 == Architecture) emit_out("jmp %");
    else if(ARMV7L == Architecture) emit_out("^~");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$");

    emit_out(break_target_head);
    emit_out(break_target_func);
    emit_out("_");
    emit_out(break_target_num);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" JAL");
    emit_out("\n");
    require_match("ERROR in break statement\nMissing ;\n", ";");
}

void process_continue()
{
    if(NULL == continue_target_head)
    {
        line_error();
        fputs("Not inside of a loop\n", stderr);
        exit(EXIT_FAILURE);
    }
    global_token = global_token->next;

    if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @");
    else if(X86 == Architecture) emit_out("jmp %");
    else if(AMD64 == Architecture) emit_out("jmp %");
    else if(ARMV7L == Architecture) emit_out("^~");
    else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$");

    emit_out(continue_target_head);
    emit_out(break_target_func);
    emit_out("_");
    emit_out(break_target_num);
    if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS");
    else if(AARCH64 == Architecture) emit_out("\nBR_X16");
    else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" JAL");
    emit_out("\n");
    require_match("ERROR in continue statement\nMissing ;\n", ";");
}

void recursive_statement()
{
    global_token = global_token->next;
    require(NULL != global_token, "Received EOF in recursive statement\n");
    struct token_list* frame = function->locals;

    while(!match("}", global_token->s))
    {
        statement();
        require(NULL != global_token, "Received EOF in recursive statement prior to }\n");
    }
    global_token = global_token->next;

    /* Clean up any locals added */

    if(((X86 == Architecture) && !match("ret\n", output_list->s)) ||
       ((AMD64 == Architecture) && !match("ret\n", output_list->s)) ||
       (((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) && !match("RET R15\n", output_list->s)) ||
       ((ARMV7L == Architecture) && !match("'1' LR RETURN\n", output_list->s)) ||
       ((AARCH64 == Architecture) && !match("RETURN\n", output_list->s)) ||
       (((RISCV32 == Architecture) || (RISCV64 == Architecture)) && !match("RETURN\n", output_list->s)))
    {
        struct token_list* i;
        for(i = function->locals; frame != i; i = i->next)
        {
            if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("POPR R1 R15\t# _recursive_statement_locals\n");
            else if(X86 == Architecture) emit_out( "pop_ebx\t# _recursive_statement_locals\n");
            else if(AMD64 == Architecture) emit_out("pop_rbx\t# _recursive_statement_locals\n");
            else if(ARMV7L == Architecture) emit_out("{R1} POP_ALWAYS\t# _recursive_statement_locals\n");
            else if(AARCH64 == Architecture) emit_out("POP_X1\t# _recursive_statement_locals\n");
            else if(RISCV32 == Architecture) emit_out("RD_A1 RS1_SP LW\t# _recursive_statement_locals\nRD_SP RS1_SP !4 ADDI\n");
            else if(RISCV64 == Architecture) emit_out("RD_A1 RS1_SP LD\t# _recursive_statement_locals\nRD_SP RS1_SP !8 ADDI\n");
        }
    }
    function->locals = frame;
}

/*
 * statement:
 *     { statement-list-opt }
 *     type-name identifier ;
 *     type-name identifier = expression;
 *     if ( expression ) statement
 *     if ( expression ) statement else statement
 *     do statement while ( expression ) ;
 *     while ( expression ) statement
 *     for ( expression ; expression ; expression ) statement
 *     asm ( "assembly" ... "assembly" ) ;
 *     goto label ;
 *     label:
 *     return ;
 *     break ;
 *     expr ;
 */

struct type* lookup_type(char* s, struct type* start);
void statement()
{
    require(NULL != global_token, "expected a C statement but received EOF\n");
    /* Always an integer until told otherwise */
    current_target = integer;

    if(global_token->s[0] == '{')
    {
        recursive_statement();
    }
    else if(':' == global_token->s[0])
    {
        emit_out(global_token->s);
        emit_out("\t#C goto label\n");
        global_token = global_token->next;
    }
    else if((NULL != lookup_type(global_token->s, prim_types)) ||
              match("struct", global_token->s))
    {
        collect_local();
    }
    else if(match("if", global_token->s))
    {
        process_if();
    }
    else if(match("do", global_token->s))
    {
        process_do();
    }
    else if(match("while", global_token->s))
    {
        process_while();
    }
    else if(match("for", global_token->s))
    {
        process_for();
    }
    else if(match("asm", global_token->s))
    {
        process_asm();
    }
    else if(match("goto", global_token->s))
    {
        global_token = global_token->next;
        require(NULL != global_token, "naked goto is not supported\n");
        if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) emit_out("JUMP @");
        else if(X86 == Architecture) emit_out("jmp %");
        else if(AMD64 == Architecture) emit_out("jmp %");
        else if(ARMV7L == Architecture) emit_out("^~");
        else if(AARCH64 == Architecture) emit_out("LOAD_W16_AHEAD\nSKIP_32_DATA\n&");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out("$");
        emit_out(global_token->s);
        if(ARMV7L == Architecture) emit_out(" JUMP_ALWAYS");
        else if(AARCH64 == Architecture) emit_out("\nBR_X16");
        else if((RISCV32 == Architecture) || (RISCV64 == Architecture)) emit_out(" JAL");
        emit_out("\n");
        global_token = global_token->next;
        require_match("ERROR in statement\nMissing ;\n", ";");
    }
    else if(match("return", global_token->s))
    {
        return_result();
    }
    else if(match("break", global_token->s))
    {
        process_break();
    }
    else if(match("continue", global_token->s))
    {
        process_continue();
    }
    else
    {
        expression();
        require_match("ERROR in statement\nMISSING ;\n", ";");
    }
}

/* Collect function arguments */
void collect_arguments()
{
    global_token = global_token->next;
    require(NULL != global_token, "Received EOF when attempting to collect arguments\n");
    struct type* type_size;
    struct token_list* a;

    while(!match(")", global_token->s))
    {
        type_size = type_name();
        require(NULL != global_token, "Received EOF when attempting to collect arguments\n");
        require(NULL != type_size, "Must have non-null type\n");
        if(global_token->s[0] == ')')
        {
            /* foo(int,char,void) doesn't need anything done */
            continue;
        }
        else if(global_token->s[0] != ',')
        {
            /* deal with foo(int a, char b) */
            require(!in_set(global_token->s[0], "[{(<=>)}]|&!^%;:'\""), "forbidden character in argument variable name\n");
            require(!iskeywordp(global_token->s), "You are not allowed to use a keyword as a argument variable name\n");
            a = sym_declare(global_token->s, type_size, function->arguments);
            if(NULL == function->arguments)
            {
                if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = 0;
                else if(X86 == Architecture) a->depth = -4;
                else if(AMD64 == Architecture) a->depth = -8;
                else if(ARMV7L == Architecture) a->depth = 4;
                else if(AARCH64 == Architecture) a->depth = register_size;
                else if(RISCV32 == Architecture) a->depth = -4;
                else if(RISCV64 == Architecture) a->depth = -8;
            }
            else
            {
                if((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) a->depth = function->arguments->depth + register_size;
                else if(X86 == Architecture) a->depth = function->arguments->depth - register_size;
                else if(AMD64 == Architecture) a->depth = function->arguments->depth - register_size;
                else if(ARMV7L == Architecture) a->depth = function->arguments->depth + register_size;
                else if(AARCH64 == Architecture) a->depth = function->arguments->depth + register_size;
                else if(RISCV32 == Architecture) a->depth = function->arguments->depth - register_size;
                else if(RISCV64 == Architecture) a->depth = function->arguments->depth - register_size;
            }

            global_token = global_token->next;
            require(NULL != global_token, "Incomplete argument list\n");
            function->arguments = a;
        }

        /* ignore trailing comma (needed for foo(bar(), 1); expressions*/
        if(global_token->s[0] == ',')
        {
            global_token = global_token->next;
            require(NULL != global_token, "naked comma in collect arguments\n");
        }

        require(NULL != global_token, "Argument list never completed\n");
    }
    global_token = global_token->next;
}

void declare_function()
{
    current_count = 0;
    function = sym_declare(global_token->prev->s, NULL, global_function_list);

    /* allow previously defined functions to be looked up */
    global_function_list = function;
    if((KNIGHT_NATIVE == Architecture) && match("main", function->s))
    {
        require_match("Impossible error ( vanished\n", "(");
        require_match("Reality ERROR (USING KNIGHT-NATIVE)\nHardware does not support arguments\nthus neither can main on this architecture\ntry tape_01 and tape_02 instead\n", ")");
    }
    else collect_arguments();

    require(NULL != global_token, "Function definitions either need to be prototypes or full\n");
    /* If just a prototype don't waste time */
    if(global_token->s[0] == ';') global_token = global_token->next;
    else
    {
        emit_out("# Defining function ");
        emit_out(function->s);
        emit_out("\n");
        emit_out(":FUNCTION_");
        emit_out(function->s);
        emit_out("\n");
        statement();

        /* Prevent duplicate RETURNS */
        if(((KNIGHT_POSIX == Architecture) || (KNIGHT_NATIVE == Architecture)) && !match("RET R15\n", output_list->s)) emit_out("RET R15\n");
        else if((X86 == Architecture) && !match("ret\n", output_list->s)) emit_out("ret\n");
        else if((AMD64 == Architecture) && !match("ret\n", output_list->s)) emit_out("ret\n");
        else if((ARMV7L == Architecture) && !match("'1' LR RETURN\n", output_list->s)) emit_out("'1' LR RETURN\n");
        else if((AARCH64 == Architecture) && !match("RETURN\n", output_list->s)) emit_out("RETURN\n");
        else if((RISCV32 == Architecture) && !match("RETURN\n", output_list->s)) emit_out("RETURN\n");
        else if((RISCV64 == Architecture) && !match("RETURN\n", output_list->s)) emit_out("RETURN\n");
    }
}

void global_constant()
{
    global_token = global_token->next;
    require(NULL != global_token, "CONSTANT lacks a name\n");
    global_constant_list = sym_declare(global_token->s, NULL, global_constant_list);

    require(NULL != global_token->next, "CONSTANT lacks a value\n");
    if(match("sizeof", global_token->next->s))
    {
        global_token = global_token->next->next;
        require_match("ERROR in CONSTANT with sizeof\nMissing (\n", "(");
        struct type* a = type_name();
        require_match("ERROR in CONSTANT with sizeof\nMissing )\n", ")");
        global_token->prev->s = int2str(a->size, 10, TRUE);
        global_constant_list->arguments = global_token->prev;
    }
    else
    {
        global_constant_list->arguments = global_token->next;
        global_token = global_token->next->next;
    }
}

struct type* global_typedef()
{
    struct type* type_size;
    /* typedef $TYPE $NAME; */
    global_token = global_token->next;
    type_size = type_name();
    require(NULL != global_token, "Received EOF while reading typedef\n");
    type_size = mirror_type(type_size, global_token->s);
    add_primitive(type_size);
    global_token = global_token->next;
    require_match("ERROR in typedef statement\nMissing ;\n", ";");
    return type_size;
}

void global_static_array(struct type* type_size, struct token_list* name)
{
    int size;
    maybe_bootstrap_error("global array definitions");
    globals_list = emit(":GLOBAL_", globals_list);
    globals_list = emit(name->s, globals_list);
    globals_list = emit("\n&GLOBAL_STORAGE_", globals_list);
    globals_list = emit(name->s, globals_list);
    if (AARCH64 == Architecture || AMD64 == Architecture || RISCV64 == Architecture)
    {
        globals_list = emit(" %0", globals_list);
    }
    globals_list = emit("\n:GLOBAL_STORAGE_", globals_list);
    globals_list = emit(name->s, globals_list);

    require(NULL != global_token->next, "Unterminated global\n");
    global_token = global_token->next;

    /* Make sure not negative */
    if(match("-", global_token->s))
    {
        line_error();
        fputs("Negative values are not supported for allocated arrays\n", stderr);
        exit(EXIT_FAILURE);
    }

    /* length */
    size = strtoint(global_token->s) * type_size->size;

    /* Stop bad states */
    if((size < 0) || (size > 0x100000))
    {
        line_error();
        fputs("M2-Planet is very inefficient so you probably don't want to allocate over 1MB into your binary for NULLs\n", stderr);
        exit(EXIT_FAILURE);
    }

    /* Ensure properly closed */
    global_token = global_token->next;
    require_match("missing close bracket\n", "]");
    require_match("missing ;\n", ";");

    globals_list = emit("\n'", globals_list);
    while (0 != size)
    {
        globals_list = emit(" 00", globals_list);
        size = size - 1;
    }
    globals_list = emit("'\n", globals_list);
}

void global_assignment()
{
    /* Store the global's value*/
    globals_list = emit(":GLOBAL_", globals_list);
    globals_list = emit(global_token->prev->s, globals_list);
    globals_list = emit("\n", globals_list);
    global_token = global_token->next;
    require(NULL != global_token, "Global locals value in assignment\n");
    if(in_set(global_token->s[0], "0123456789"))
    { /* Assume Int */
        globals_list = emit("%", globals_list);
        globals_list = emit(global_token->s, globals_list);
        globals_list = emit("\n", globals_list);
    }
    else if(('"' == global_token->s[0]))
    { /* Assume a string*/
        globals_list = emit("&GLOBAL_", globals_list);
        globals_list = emit(global_token->prev->prev->s, globals_list);
        globals_list = emit("_contents\n", globals_list);

        globals_list = emit(":GLOBAL_", globals_list);
        globals_list = emit(global_token->prev->prev->s, globals_list);
        globals_list = emit("_contents\n", globals_list);
        globals_list = emit(parse_string(global_token->s), globals_list);
    }
    else
    {
        line_error();
        fputs("Received ", stderr);
        fputs(global_token->s, stderr);
        fputs(" in program\n", stderr);
        exit(EXIT_FAILURE);
    }

    global_token = global_token->next;
    require_match("ERROR in Program\nMissing ;\n", ";");
}

/*
 * program:
 *     declaration
 *     declaration program
 *
 * declaration:
 *     CONSTANT identifer value
 *     typedef identifer type;
 *     type-name identifier ;
 *     type-name identifier = value ;
 *     type-name identifier [ value ];
 *     type-name identifier ( parameter-list ) ;
 *     type-name identifier ( parameter-list ) statement
 *
 * parameter-list:
 *     parameter-declaration
 *     parameter-list, parameter-declaration
 *
 * parameter-declaration:
 *     type-name identifier-opt
 */
void program()
{
    unsigned i;
    function = NULL;
    Address_of = FALSE;
    struct type* type_size;

new_type:
    /* Deal with garbage input */
    if (NULL == global_token) return;
    require('#' != global_token->s[0], "unhandled macro directive\n");
    require(!match("\n", global_token->s), "unexpected newline token\n");

    /* Handle cc_* CONSTANT statements */
    if(match("CONSTANT", global_token->s))
    {
        global_constant();
        goto new_type;
    }

    /* Handle c typedef statements */
    if(match("typedef", global_token->s))
    {
        type_size = global_typedef();
        goto new_type;
    }

    type_size = type_name();
    /* Deal with case of struct definitions */
    if(NULL == type_size) goto new_type;

    require(NULL != global_token->next, "Unterminated global\n");

    /* Add to global symbol table */
    global_symbol_list = sym_declare(global_token->s, type_size, global_symbol_list);
    global_token = global_token->next;

    /* Deal with global variables */
    if(match(";", global_token->s))
    {
        /* Ensure enough bytes are allocated to store global variable.
           In some cases it allocates too much but that is harmless. */
        globals_list = emit(":GLOBAL_", globals_list);
        globals_list = emit(global_token->prev->s, globals_list);

        /* round up division */
        i = ceil_div(type_size->size, register_size);
        globals_list = emit("\n", globals_list);
        while(i != 0)
        {
            globals_list = emit("NULL\n", globals_list);
            i = i - 1;
        }
        global_token = global_token->next;
        goto new_type;
    }

    /* Deal with global functions */
    if(match("(", global_token->s))
    {
        declare_function();
        goto new_type;
    }

    /* Deal with assignment to a global variable */
    if(match("=", global_token->s))
    {
        global_assignment();
        goto new_type;
    }

    /* Deal with global static arrays */
    if(match("[", global_token->s))
    {
        global_static_array(type_size, global_token->prev);
        goto new_type;
    }

    /* Everything else is just an error */
    line_error();
    fputs("Received ", stderr);
    fputs(global_token->s, stderr);
    fputs(" in program\n", stderr);
    exit(EXIT_FAILURE);
}

void recursive_output(struct token_list* head, FILE* out)
{
    struct token_list* i = reverse_list(head);
    while(NULL != i)
    {
        fputs(i->s, out);
        i = i->next;
    }
}

void output_tokens(struct token_list *i, FILE* out)
{
    while(NULL != i)
    {
        fputs(i->s, out);
        fputs(" ", out);
        i = i->next;
    }
}

https://github.com/oriansj/M2-Planet/blob/main/cc_macro.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc_macro.c used in: Step 13, Step 61
/* Copyright (C) 2021 Sanne Wouda
 * Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
 * Copyright (C) 2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "cc.h"
#include "gcc_req.h"

void require(int bool, char* error);
int strtoint(char* a);
void line_error_token(struct token_list* list);
struct token_list* eat_token(struct token_list* head);

struct conditional_inclusion
{
    struct conditional_inclusion* prev;
    int include; /* 1 == include, 0 == skip */
    int previous_condition_matched; /* 1 == all subsequent conditions treated as FALSE */
};

struct macro_list
{
    struct macro_list* next;
    char* symbol;
    struct token_list* expansion;
};

struct macro_list* macro_env;
struct conditional_inclusion* conditional_inclusion_top;

/* point where we are currently modifying the global_token list */
struct token_list* macro_token;

void init_macro_env(char* sym, char* value, char* source, int num)
{
    struct macro_list* hold = macro_env;
    macro_env = calloc(1, sizeof(struct macro_list));
    macro_env->symbol = sym;
    macro_env->next = hold;
    macro_env->expansion = calloc(1, sizeof(struct token_list));
    macro_env->expansion->s = value;
    macro_env->expansion->filename = source;
    macro_env->expansion->linenumber = num;
}

void eat_current_token()
{
    int update_global_token = FALSE;
    if (macro_token == global_token)
        update_global_token = TRUE;

    macro_token = eat_token(macro_token);

    if(update_global_token)
        global_token = macro_token;
}

void eat_newline_tokens()
{
    macro_token = global_token;

    while(TRUE)
    {
        if(NULL == macro_token) return;

        if(match("\n", macro_token->s))
        {
            eat_current_token();
        }
        else
        {
            macro_token = macro_token->next;
        }
    }
}

/* returns the first token inserted; inserts *before* point */
struct token_list* insert_tokens(struct token_list* point, struct token_list* token)
{
    struct token_list* copy;
    struct token_list* first = NULL;

    while (NULL != token)
    {
        copy = calloc(1, sizeof(struct token_list));
        copy->s = token->s;
        copy->filename = token->filename;
        copy->linenumber = token->linenumber;

        if(NULL == first)
        {
            first = copy;
        }

        copy->next = point;

        if (NULL != point)
        {
            copy->prev = point->prev;

            if(NULL != point->prev)
            {
                point->prev->next = copy;
            }

            point->prev = copy;
        }

        token = token->next;
    }

    return first;
}

struct macro_list* lookup_macro(struct token_list* token)
{
    if(NULL == token)
    {
        line_error_token(macro_token);
        fputs("null token received in lookup_macro\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct macro_list* hold = macro_env;

    while (NULL != hold)
    {
        if (match(token->s, hold->symbol))
        {
            /* found! */
            return hold;
        }

        hold = hold->next;
    }

    /* not found! */
    return NULL;
}

void remove_macro(struct token_list* token)
{
    if(NULL == token)
    {
        line_error_token(macro_token);
        fputs("received a null in remove_macro\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct macro_list* hold = macro_env;
    struct macro_list* temp;

    /* Deal with the first element */
    if (match(token->s, hold->symbol)) {
        macro_env = hold->next;
        free(hold);
        return;
    }

    /* Remove element form the middle of linked list */
    while (NULL != hold->next)
    {
        if (match(token->s, hold->next->symbol))
        {
            temp = hold->next;
            hold->next = hold->next->next;
            free(temp);
            return;
        }

        hold = hold->next;
    }

    /* nothing to undefine */
    return;
}

int macro_expression();
int macro_variable()
{
    int value = 0;
    struct macro_list* hold = lookup_macro(macro_token);
    if (NULL != hold)
    {
        if(NULL == hold->expansion)
        {
            line_error_token(macro_token);
            fputs("hold->expansion is a null\n", stderr);
            exit(EXIT_FAILURE);
        }
        value = strtoint(hold->expansion->s);
    }
    eat_current_token();
    return value;
}

int macro_number()
{
    int result = strtoint(macro_token->s);
    eat_current_token();
    return result;
}

int macro_primary_expr()
{
    int defined_has_paren = FALSE;
    int hold;
    require(NULL != macro_token, "got an EOF terminated macro primary expression\n");

    if('-' == macro_token->s[0])
    {
        eat_current_token();
        return -macro_primary_expr();
    }
    else if('!' == macro_token->s[0])
    {
        eat_current_token();
        return !macro_primary_expr();
    }
    else if('(' == macro_token->s[0])
    {
        eat_current_token();
        hold = macro_expression();
        require(')' == macro_token->s[0], "missing ) in macro expression\n");
        eat_current_token();
        return hold;
    }
    else if(match("defined", macro_token->s))
    {
        eat_current_token();

        require(NULL != macro_token, "got an EOF terminated macro defined expression\n");

        if('(' == macro_token->s[0])
        {
            defined_has_paren = TRUE;
            eat_current_token();
        }

        if (NULL != lookup_macro(macro_token))
        {
            hold = TRUE;
        }
        else
        {
            hold = FALSE;
        }
        eat_current_token();

        if(TRUE == defined_has_paren)
        {
            if(NULL == macro_token)
            {
                line_error_token(macro_token);
                fputs("unterminated define ( statement\n", stderr);
                exit(EXIT_FAILURE);
            }
            require(')' == macro_token->s[0], "missing close parenthesis for defined()\n");
            eat_current_token();
        }

        return hold;
    }
    else if(in_set(macro_token->s[0], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"))
    {
        return macro_variable();
    }
    else if(in_set(macro_token->s[0], "0123456789"))
    {
        return macro_number();
    }
    else
    {
        return 0;    /* FIXME: error handling */
    }
}

int macro_additive_expr()
{
    int lhs = macro_primary_expr();
    int hold;

    require(NULL != macro_token, "got an EOF terminated macro additive expression\n");
    if(match("+", macro_token->s))
    {
        eat_current_token();
        return lhs + macro_additive_expr();
    }
    else if(match("-", macro_token->s))
    {
        eat_current_token();
        return lhs - macro_additive_expr();
    }
    else if(match("*", macro_token->s))
    {
        eat_current_token();
        return lhs * macro_additive_expr();
    }
    else if(match("/", macro_token->s))
    {
        eat_current_token();
        hold = macro_additive_expr();
        require(0 != hold, "divide by zero not valid even in C macros\n");
        return lhs / hold;
    }
    else if(match("%", macro_token->s))
    {
        eat_current_token();
        hold = macro_additive_expr();
        require(0 != hold, "modulus by zero not valid even in C macros\n");
        return lhs % hold;
    }
    else if(match(">>", macro_token->s))
    {
        eat_current_token();
        return lhs >> macro_additive_expr();
    }
    else if(match("<<", macro_token->s))
    {
        eat_current_token();
        return lhs << macro_additive_expr();
    }
    else
    {
        return lhs;
    }
}

int macro_relational_expr()
{
    int lhs = macro_additive_expr();

    if(match("<", macro_token->s))
    {
        eat_current_token();
        return lhs < macro_relational_expr();
    }
    else if(match("<=", macro_token->s))
    {
        eat_current_token();
        return lhs <= macro_relational_expr();
    }
    else if(match(">=", macro_token->s))
    {
        eat_current_token();
        return lhs >= macro_relational_expr();
    }
    else if(match(">", macro_token->s))
    {
        eat_current_token();
        return lhs > macro_relational_expr();
    }
    else if(match("==", macro_token->s))
    {
        eat_current_token();
        return lhs == macro_relational_expr();
    }
    else if(match("!=", macro_token->s))
    {
        eat_current_token();
        return lhs != macro_relational_expr();
    }
    else
    {
        return lhs;
    }
}

int macro_bitwise_expr()
{
    int rhs;
    int lhs = macro_relational_expr();

    if(match("&", macro_token->s))
    {
        eat_current_token();
        return lhs & macro_bitwise_expr();
    }
    else if(match("&&", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs && rhs;
    }
    else if(match("|", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs | rhs;
    }
    else if(match("||", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs || rhs;
    }
    else if(match("^", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs ^ rhs;
    }
    else
    {
        return lhs;
    }
}

int macro_expression()
{
    return macro_bitwise_expr();
}

void handle_define()
{
    struct macro_list* hold;
    struct token_list* expansion_end = NULL;

    /* don't use #define statements from non-included blocks */
    int conditional_define = TRUE;
    if(NULL != conditional_inclusion_top)
    {
        if(FALSE == conditional_inclusion_top->include)
        {
            conditional_define = FALSE;
        }
    }

    eat_current_token();

    require(NULL != macro_token, "got an EOF terminated #define\n");
    require('\n' != macro_token->s[0], "unexpected newline after #define\n");

    /* insert new macro */
    hold = calloc(1, sizeof(struct macro_list));
    hold->symbol = macro_token->s;
    hold->next = macro_env;
    /* provided it isn't in a non-included block */
    if(conditional_define) macro_env = hold;

    /* discard the macro name */
    eat_current_token();

    while (TRUE)
    {
        require(NULL != macro_token, "got an EOF terminated #define\n");

        if ('\n' == macro_token->s[0])
        {
            if(NULL == expansion_end)
            {
                hold->expansion = NULL;
                expansion_end = macro_token;
                return;
            }
            expansion_end->next = NULL;
            return;
        }

        require(NULL != hold, "#define got something it can't handle\n");

        expansion_end = macro_token;

        /* in the first iteration, we set the first token of the expansion, if
           it exists */
        if (NULL == hold->expansion)
        {
            hold->expansion = macro_token;
        }

        /* throw away if not used */
        if(!conditional_define && (NULL != hold))
        {
            free(hold);
            hold = NULL;
        }

        eat_current_token();
    }
}

void handle_undef()
{
    eat_current_token();
    remove_macro(macro_token);
    eat_current_token();
}

void handle_error(int warning_p)
{
    /* don't use #error statements from non-included blocks */
    int conditional_error = TRUE;
    if(NULL != conditional_inclusion_top)
    {
        if(FALSE == conditional_inclusion_top->include)
        {
            conditional_error = FALSE;
        }
    }
    eat_current_token();
    /* provided it isn't in a non-included block */
    if(conditional_error)
    {
        line_error_token(macro_token);
        if(warning_p) fputs(" warning: #warning ", stderr);
        else fputs(" error: #error ", stderr);
        while (TRUE)
        {
            require(NULL != macro_token, "\nFailed to properly terminate error message with \\n\n");
            if ('\n' == macro_token->s[0]) break;
            fputs(macro_token->s, stderr);
            macro_token = macro_token->next;
            fputs(" ", stderr);
        }
        fputs("\n", stderr);
        if(!warning_p) exit(EXIT_FAILURE);
    }
    while (TRUE)
    {
        require(NULL != macro_token, "\nFailed to properly terminate error message with \\n\n");
        /* discard the error */
        if ('\n' == macro_token->s[0])
        {
            return;
        }
        eat_current_token();
    }
}

void eat_block();
void macro_directive()
{
    struct conditional_inclusion *t;
    int result;

    /* FIXME: whitespace is allowed between "#"" and "if" */
    if(match("#if", macro_token->s))
    {
        eat_current_token();
        /* evaluate constant integer expression */
        result = macro_expression();
        /* push conditional inclusion */
        t = calloc(1, sizeof(struct conditional_inclusion));
        t->prev = conditional_inclusion_top;
        conditional_inclusion_top = t;
        t->include = TRUE;

        if(FALSE == result)
        {
            t->include = FALSE;
            eat_block();
        }

        t->previous_condition_matched = t->include;
    }
    else if(match("#ifdef", macro_token->s))
    {
        eat_current_token();
        require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
        if (NULL != lookup_macro(macro_token))
        {
            result = TRUE;
            eat_current_token();
        }
        else
        {
            result = FALSE;
            eat_block();
        }

        /* push conditional inclusion */
        t = calloc(1, sizeof(struct conditional_inclusion));
        t->prev = conditional_inclusion_top;
        conditional_inclusion_top = t;
        t->include = TRUE;

        if(FALSE == result)
        {
            t->include = FALSE;
        }

        t->previous_condition_matched = t->include;
    }
    else if(match("#ifndef", macro_token->s))
    {
        eat_current_token();
        require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
        if (NULL != lookup_macro(macro_token))
        {
            result = FALSE;
        }
        else
        {
            result = TRUE;
            eat_current_token();
        }

        /* push conditional inclusion */
        t = calloc(1, sizeof(struct conditional_inclusion));
        t->prev = conditional_inclusion_top;
        conditional_inclusion_top = t;
        t->include = TRUE;

        if(FALSE == result)
        {
            t->include = FALSE;
            eat_block();
        }

        t->previous_condition_matched = t->include;
    }
    else if(match("#elif", macro_token->s))
    {
        eat_current_token();
        result = macro_expression();
        require(NULL != conditional_inclusion_top, "#elif without leading #if\n");
        conditional_inclusion_top->include = result && !conditional_inclusion_top->previous_condition_matched;
        conditional_inclusion_top->previous_condition_matched =
            conditional_inclusion_top->previous_condition_matched || conditional_inclusion_top->include;
        if(FALSE == result)
        {
            eat_block();
        }
    }
    else if(match("#else", macro_token->s))
    {
        eat_current_token();
        require(NULL != conditional_inclusion_top, "#else without leading #if\n");
        conditional_inclusion_top->include = !conditional_inclusion_top->previous_condition_matched;
        if(FALSE == conditional_inclusion_top->include)
        {
            eat_block();
        }
    }
    else if(match("#endif", macro_token->s))
    {
        if(NULL == conditional_inclusion_top)
        {
            line_error_token(macro_token);
            fputs("unexpected #endif\n", stderr);
            exit(EXIT_FAILURE);
        }

        eat_current_token();
        /* pop conditional inclusion */
        t = conditional_inclusion_top;
        conditional_inclusion_top = conditional_inclusion_top->prev;
        free(t);
    }
    else if(match("#define", macro_token->s))
    {
        handle_define();
    }
    else if(match("#undef", macro_token->s))
    {
        handle_undef();
    }
    else if(match("#error", macro_token->s))
    {
        handle_error(FALSE);
    }
    else if(match("#warning", macro_token->s))
    {
        handle_error(TRUE);
    }
    else
    {
        if(!match("#include", macro_token->s))
        {
            /* Put a big fat warning but see if we can just ignore */
            fputs(">>WARNING<<\n>>WARNING<<\n", stderr);
            line_error_token(macro_token);
            fputs("feature: ", stderr);
            fputs(macro_token->s, stderr);
            fputs(" unsupported in M2-Planet\nIgnoring line, may result in bugs\n>>WARNING<<\n>>WARNING<<\n\n", stderr);
        }

        /* unhandled macro directive; let's eat until a newline; om nom nom */
        while(TRUE)
        {
            if(NULL == macro_token)
            {
                return;
            }

            if('\n' == macro_token->s[0])
            {
                return;
            }

            eat_current_token();
        }
    }
}


void eat_until_endif()
{
    /* This #if block is nested inside of an #if block that needs to be dropped, lose EVERYTHING */
    do
    {
        require(NULL != macro_token, "Unterminated #if block\n");
        if(match("#if", macro_token->s) || match("#ifdef", macro_token->s) || match("#ifndef", macro_token->s))
        {
            eat_current_token();
            eat_until_endif();
        }

        eat_current_token();
        require(NULL != macro_token, "Unterminated #if block\n");
    } while(!match("#endif", macro_token->s));
}

void eat_block()
{
    /* This conditional #if block is wrong, drop everything until the #elif/#else/#endif */
    do
    {
        if(match("#if", macro_token->s) || match("#ifdef", macro_token->s) || match("#ifndef", macro_token->s))
        {
            eat_current_token();
            eat_until_endif();
        }

        eat_current_token();
        require(NULL != macro_token, "Unterminated #if block\n");
        if(match("#elif", macro_token->s)) break;
        if(match("#else", macro_token->s)) break;
        if(match("#endif", macro_token->s)) break;
    } while(TRUE);
    require(NULL != macro_token->prev, "impossible #if block\n");

    /* rewind the newline */
    if(match("\n", macro_token->prev->s)) macro_token = macro_token->prev;
}


struct token_list* maybe_expand(struct token_list* token)
{
    if(NULL == token)
    {
        line_error_token(macro_token);
        fputs("maybe_expand passed a null token\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct macro_list* hold = lookup_macro(token);
    struct token_list* hold2;
    if(NULL == token->next)
    {
        line_error_token(macro_token);
        fputs("we can't expand a null token: ", stderr);
        fputs(token->s, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }

    if (NULL == hold)
    {
        return token->next;
    }

    token = eat_token(token);

    if (NULL == hold->expansion)
    {
        return token->next;
    }
    hold2 = insert_tokens(token, hold->expansion);

    return hold2->next;
}

void preprocess()
{
    int start_of_line = TRUE;
    macro_token = global_token;

    while(NULL != macro_token)
    {
        if(start_of_line && '#' == macro_token->s[0])
        {
            macro_directive();

            if(macro_token)
            {
                if('\n' != macro_token->s[0])
                {
                    line_error_token(macro_token);
                    fputs("newline expected at end of macro directive\n", stderr);
                    fputs("found: '", stderr);
                    fputs(macro_token->s, stderr);
                    fputs("'\n", stderr);
                    exit(EXIT_FAILURE);
                }
            }
        }
        else if('\n' == macro_token->s[0])
        {
            start_of_line = TRUE;
            macro_token = macro_token->next;
        }
        else
        {
            start_of_line = FALSE;
            if(NULL == conditional_inclusion_top)
            {
                macro_token = maybe_expand(macro_token);
            }
            else if(!conditional_inclusion_top->include)
            {
                /* rewrite the token stream to exclude the current token */
                eat_block();
                start_of_line = TRUE;
            }
            else
            {
                macro_token = maybe_expand(macro_token);
            }
        }
    }
}

https://github.com/oriansj/M2-Planet/blob/main/cc.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Planet/cc.c used in: Step 13, Step 61
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include"cc.h"

/* The core functions */
void initialize_types();
struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename);
struct token_list* reverse_list(struct token_list* head);

struct token_list* remove_line_comments(struct token_list* head);
struct token_list* remove_line_comment_tokens(struct token_list* head);
struct token_list* remove_preprocessor_directives(struct token_list* head);

void eat_newline_tokens();
void init_macro_env(char* sym, char* value, char* source, int num);
void preprocess();
void program();
void recursive_output(struct token_list* i, FILE* out);
void output_tokens(struct token_list *i, FILE* out);
int strtoint(char *a);

int main(int argc, char** argv)
{
    MAX_STRING = 4096;
    BOOTSTRAP_MODE = FALSE;
    PREPROCESSOR_MODE = FALSE;
    int DEBUG = FALSE;
    FILE* in = stdin;
    FILE* destination_file = stdout;
    Architecture = 0; /* catch unset */
    init_macro_env("__M2__", "42", "__INTERNAL_M2__", 0); /* Setup __M2__ */
    char* arch;
    char* name;
    char* hold;
    int env=0;
    char* val;

    int i = 1;
    while(i <= argc)
    {
        if(NULL == argv[i])
        {
            i = i + 1;
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            if(NULL == hold_string)
            {
                hold_string = calloc(MAX_STRING + 4, sizeof(char));
                require(NULL != hold_string, "Impossible Exhaustion has occurred\n");
            }

            name = argv[i + 1];
            if(NULL == name)
            {
                fputs("did not receive a file name\n", stderr);
                exit(EXIT_FAILURE);
            }

            in = fopen(name, "r");
            if(NULL == in)
            {
                fputs("Unable to open for reading file: ", stderr);
                fputs(name, stderr);
                fputs("\n Aborting to avoid problems\n", stderr);
                exit(EXIT_FAILURE);
            }
            global_token = read_all_tokens(in, global_token, name);
            fclose(in);
            i = i + 2;
        }
        else if(match(argv[i], "-o") || match(argv[i], "--output"))
        {
            destination_file = fopen(argv[i + 1], "w");
            if(NULL == destination_file)
            {
                fputs("Unable to open for writing file: ", stderr);
                fputs(argv[i + 1], stderr);
                fputs("\n Aborting to avoid problems\n", stderr);
                exit(EXIT_FAILURE);
            }
            i = i + 2;
        }
        else if(match(argv[i], "-A") || match(argv[i], "--architecture"))
        {
            arch = argv[i + 1];
            if(match("knight-native", arch)) {
                Architecture = KNIGHT_NATIVE;
                init_macro_env("__knight__", "1", "--architecture", env);
                env = env + 1;
            }
            else if(match("knight-posix", arch)) {
                Architecture = KNIGHT_POSIX;
                init_macro_env("__knight_posix__", "1", "--architecture", env);
                env = env + 1;
            }
            else if(match("x86", arch))
            {
                Architecture = X86;
                init_macro_env("__i386__", "1", "--architecture", env);
                env = env + 1;
            }
            else if(match("amd64", arch))
            {
                Architecture = AMD64;
                init_macro_env("__x86_64__", "1", "--architecture", env);
                env = env + 1;
            }
            else if(match("armv7l", arch))
            {
                Architecture = ARMV7L;
                init_macro_env("__arm__", "1", "--architecture", env);
                env = env + 1;
            }
            else if(match("aarch64", arch))
            {
                Architecture = AARCH64;
                init_macro_env("__aarch64__", "1", "--architecture", env);
                env = env + 1;
            }
            else if(match("riscv32", arch))
            {
                Architecture = RISCV32;
                init_macro_env("__riscv", "1", "--architecture", env);
                init_macro_env("__riscv_xlen", "32", "--architecture", env + 1);
                env = env + 2;
            }
            else if(match("riscv64", arch))
            {
                Architecture = RISCV64;
                init_macro_env("__riscv", "1", "--architecture", env);
                init_macro_env("__riscv_xlen", "64", "--architecture", env + 1);
                env = env + 2;
            }
            else
            {
                fputs("Unknown architecture: ", stderr);
                fputs(arch, stderr);
                fputs(" know values are: knight-native, knight-posix, x86, amd64, armv7l, aarch64, riscv32 and riscv64\n", stderr);
                exit(EXIT_FAILURE);
            }
            i = i + 2;
        }
        else if(match(argv[i], "--max-string"))
        {
            hold = argv[i+1];
            if(NULL == hold)
            {
                fputs("--max-string requires a numeric argument\n", stderr);
                exit(EXIT_FAILURE);
            }
            MAX_STRING = strtoint(hold);
            require(0 < MAX_STRING, "Not a valid string size\nAbort and fix your --max-string\n");
            i = i + 2;
        }
        else if(match(argv[i], "--bootstrap-mode"))
        {
            BOOTSTRAP_MODE = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "-g") || match(argv[i], "--debug"))
        {
            DEBUG = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs(" -f input file\n -o output file\n --help for this message\n --version for file version\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-E"))
        {
            PREPROCESSOR_MODE = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "-D"))
        {
            val = argv[i+1];
            if(NULL == val)
            {
                fputs("-D requires an argument", stderr);
                exit(EXIT_FAILURE);
            }
            while(0 != val[0])
            {
                if('=' == val[0])
                {
                    val[0] = 0;
                    val = val + 1;
                    break;
                }
                val = val + 1;
            }
            init_macro_env(argv[i+1], val, "__ARGV__", env);
            env = env + 1;
            i = i + 2;
        }
        else if(match(argv[i], "-V") || match(argv[i], "--version"))
        {
            fputs("M2-Planet v1.10.0\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("UNKNOWN ARGUMENT\n", stdout);
            exit(EXIT_FAILURE);
        }
    }

    /* Deal with special case of architecture not being set */
    if(0 == Architecture)
    {
        Architecture = KNIGHT_NATIVE;
        init_macro_env("__knight__", "1", "--architecture", env);
    }

    /* Deal with special case of wanting to read from standard input */
    if(stdin == in)
    {
        hold_string = calloc(MAX_STRING + 4, sizeof(char));
        require(NULL != hold_string, "Impossible Exhaustion has occurred\n");
        global_token = read_all_tokens(in, global_token, "STDIN");
    }

    if(NULL == global_token)
    {
        fputs("Either no input files were given or they were empty\n", stderr);
        exit(EXIT_FAILURE);
    }
    global_token = reverse_list(global_token);

    if (BOOTSTRAP_MODE)
    {
        global_token = remove_line_comment_tokens(global_token);
        global_token = remove_preprocessor_directives(global_token);
    }
    else
    {
        global_token = remove_line_comments(global_token);
        preprocess();
    }

    if (PREPROCESSOR_MODE)
    {
        fputs("\n/* Preprocessed source */\n", destination_file);
        output_tokens(global_token, destination_file);
        goto exit_success;
    }

    /* the main parser doesn't know how to handle newline tokens */
    eat_newline_tokens();

    initialize_types();
    reset_hold_string();
    output_list = NULL;
    program();

    /* Output the program we have compiled */
    fputs("\n# Core program\n", destination_file);
    recursive_output(output_list, destination_file);
    if(KNIGHT_NATIVE == Architecture) fputs("\n", destination_file);
    else if(DEBUG) fputs("\n:ELF_data\n", destination_file);
    fputs("\n# Program global variables\n", destination_file);
    recursive_output(globals_list, destination_file);
    fputs("\n# Program strings\n", destination_file);
    recursive_output(strings_list, destination_file);
    if(KNIGHT_NATIVE == Architecture) fputs("\n:STACK\n", destination_file);
    else if(!DEBUG) fputs("\n:ELF_end\n", destination_file);

exit_success:
    if (destination_file != stdout)
    {
        fclose(destination_file);
    }
    return EXIT_SUCCESS;
}

https://github.com/oriansj/stage0-posix-x86/blob/main/x86_defs.M1

File: ../git/live-bootstrap/sysa/stage0-posix/src/x86/x86_defs.M1 used in: Step 15
## Copyright (C) 2017 Jeremiah Orians
## This file is part of M2-Planet.
##
## M2-Planet is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.


DEFINE add_eax, 81C0
DEFINE add_ebp, 81C5
DEFINE add_ebx,eax 01C3
DEFINE add_eax,ebp 01E8
DEFINE add_eax,ebx 01D8
DEFINE and_eax,ebx 21D8
DEFINE call E8
DEFINE call_eax FFD0
DEFINE cmp 39C3
DEFINE cdq 99
DEFINE div_ebx F7F3
DEFINE idiv_ebx F7FB
DEFINE int CD
DEFINE je 0F84
DEFINE jne 0F85
DEFINE jmp E9
DEFINE lea_eax,[ebp+DWORD] 8D85
DEFINE lea_eax,[esp+DWORD] 8D8424
DEFINE lea_ebx,[esp+DWORD] 8D9C24
DEFINE lea_ecx,[esp+DWORD] 8D8C24
DEFINE lea_edx,[esp+DWORD] 8D9424
DEFINE mov_eax,[esp+DWORD] 8B8424
DEFINE mov_eax,ebp 89E8
DEFINE mov_eax,ebx 89D8
DEFINE mov_eax,ebx 89D8
DEFINE mov_eax,edx 89D0
DEFINE mov_ebx,eax 89C3
DEFINE mov_ecx,eax 89C1
DEFINE mov_ecx,esp 89E1
DEFINE mov_edi,esp 89E7
DEFINE mov_ebp,edi 89fd
DEFINE mov_ebp,esp 89E5
DEFINE mov_eax, B8
DEFINE mov_ebx, BB
DEFINE mov_edx, BA
DEFINE mov_eax,[eax] 8B00
DEFINE mov_ebx,[ebx] 8B1B
DEFINE mov_ecx,[ecx] 8B09
DEFINE mov_edx,[edx] 8B12
DEFINE mov_[ebx],al 8803
DEFINE mov_[ebx],eax 8903
DEFINE movsx_eax,BYTE_PTR_[eax] 0FBE00
DEFINE movsx_ebx,BYTE_PTR_[ebx] 0FBE1B
DEFINE movzx_eax,al 0FB6C0
DEFINE mul_ebx F7E3
DEFINE imul_ebx F7EB
DEFINE NULL 00000000
DEFINE not_eax F7D0
DEFINE or_eax,ebx 09D8
DEFINE pop_eax 58
DEFINE pop_ebx 5B
DEFINE pop_ebp 5D
DEFINE pop_edi 5F
DEFINE push_eax 50
DEFINE push_ebx 53
DEFINE push_ebp 55
DEFINE push_edi 57
DEFINE ret C3
DEFINE sal_eax, C1E0
DEFINE sal_eax,cl D3F0
DEFINE shl_eax,cl D3E0
DEFINE sar_eax,cl D3F8
DEFINE shr_eax,cl D3E8
DEFINE seta_al 0F97C0
DEFINE setae_al 0F93C0
DEFINE setb_al 0F92C0
DEFINE setbe_al 0F96C0
DEFINE sete_al 0F94C0
DEFINE setle_al 0F9EC0
DEFINE setl_al 0F9CC0
DEFINE setge_al 0F9DC0
DEFINE setg_al 0F9FC0
DEFINE setne_al 0F95C0
DEFINE sub_ebx,eax 29C3
DEFINE test_eax,eax 85C0
DEFINE xchg_ebx,eax 93
DEFINE xor_eax,ebx 31D8

https://github.com/oriansj/stage0-posix-x86/blob/main/libc-core.M1

File: ../git/live-bootstrap/sysa/stage0-posix/src/x86/libc-core.M1 used in: Step 15
## Copyright (C) 2016 Jeremiah Orians
## This file is part of stage0.
##
## stage0 is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## stage0 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 stage0.  If not, see <http://www.gnu.org/licenses/>.

:_start

    mov_ebp,esp                 ; Protect esp

    ;; Prepare argv
    lea_eax,[ebp+DWORD] %4      ; ARGV_address = EBP + 4
    push_eax                    ; Put argv on the stack

    ;; Prepare envp
    mov_eax,ebp                 ; Address we need to load from
    mov_eax,[eax]               ; Get ARGC
    add_eax, %2                 ; OFFSET = ARGC + 2
    sal_eax, !2                 ; OFFSET = OFFSET * WORDSIZE
    add_eax,ebp                 ; ENVP_address = ESP + OFFSET
    push_eax                    ; Put envp on the stack

    ;; Stack offset
    add_ebp, %4                 ; Fix ebp

    ;; Perform the main loop
    call %FUNCTION_main

    ;; Exit to kernel
    mov_ebx,eax                 ; Using the return code given by main
    mov_eax, %1                 ; Syscall exit
    int !0x80                   ; Exit with that code

https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=stringify.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/stringify.c used in: Step 19, Step 24, Step 35, Step 53
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

// CONSTANT HEX 16
#define HEX 16
// CONSTANT OCTAL 8
#define OCTAL 8
// CONSTANT BINARY 2
#define BINARY 2

/***********************************************************
 * Needed for current implementation of little endian      *
 * Can be used to support little bit endian instruction    *
 * sets if we ever find one that might be useful           *
 * But I seriously doubt it                                *
 ***********************************************************/
void reverseBitOrder(char* c, int ByteMode)
{
    if(NULL == c) return;
    if(0 == c[1]) return;
    int hold = c[0];

    if(HEX == ByteMode)
    {
        c[0] = c[1];
        c[1] = hold;
        reverseBitOrder(c+2, ByteMode);
    }
    else if(OCTAL == ByteMode)
    {
        c[0] = c[2];
        c[2] = hold;
        reverseBitOrder(c+3, ByteMode);
    }
    else if(BINARY == ByteMode)
    {
        c[0] = c[7];
        c[7] = hold;
        hold = c[1];
        c[1] = c[6];
        c[6] = hold;
        hold = c[2];
        c[2] = c[5];
        c[5] = hold;
        hold = c[3];
        c[3] = c[4];
        c[4] = hold;
        reverseBitOrder(c+8, ByteMode);
    }
}

void LittleEndian(char* start, int ByteMode)
{
    char* end = start;
    char* c = start;
    while(0 != end[0]) end = end + 1;
    int hold;
    for(end = end - 1; start < end; start = start + 1)
    {
        hold = start[0];
        start[0] = end[0];
        end[0] = hold;
        end = end - 1;
    }

    /* The above makes a reversed bit order */
    reverseBitOrder(c, ByteMode);
}

int hex2char(int c)
{
    if((c >= 0) && (c <= 9)) return (c + 48);
    else if((c >= 10) && (c <= 15)) return (c + 55);
    else return -1;
}

int stringify(char* s, int digits, int divisor, int value, int shift)
{
    int i = value;
    if(digits > 1)
    {
        i = stringify(s+1, (digits - 1), divisor, value, shift);
    }
    s[0] = hex2char(i & (divisor - 1));
    return (i >> shift);
}

https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=blood-elf.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/blood-elf.c used in: Step 19, Step 53
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "M2libc/bootstrappable.h"

// CONSTANT max_string 4096
#define max_string 4096
int BITSIZE;
int BigEndian;
// CONSTANT HEX 16
#define HEX 16
// CONSTANT OCTAL 8
#define OCTAL 8
// CONSTANT BINARY 2
#define BINARY 2


/* Strings needed for constants */
char* zero_8;
char* zero_16;
char* zero_32;
char* one_16;
char* one_32;
char* two_8;
char* two_32;
char* three_32;
char* six_32;
char* sixteen_32;
char* twentyfour_32;

/* Imported from stringify.c */
int stringify(char* s, int digits, int divisor, int value, int shift);
void LittleEndian(char* start, int ByteMode);

struct entry
{
    struct entry* next;
    char* name;
};

FILE* output;
struct entry* jump_table;
int count;
char* entry;

void consume_token(FILE* source_file, char* s)
{
    int i = 0;
    int c = fgetc(source_file);
    require(EOF != c, "Can not have an EOF token\n");
    do
    {
        s[i] = c;
        i = i + 1;
        require(max_string > i, "Token exceeds token length restriction\n");
        c = fgetc(source_file);
        if(EOF == c) break;
    } while(!in_set(c, " \t\n>"));
}

void storeLabel(FILE* source_file)
{
    struct entry* entry = calloc(1, sizeof(struct entry));

    /* Prepend to list */
    entry->next = jump_table;
    jump_table = entry;

    /* Store string */
    entry->name = calloc((max_string + 1), sizeof(char));
    consume_token(source_file, entry->name);

    count = count + 1;
}

void line_Comment(FILE* source_file)
{
    int c = fgetc(source_file);
    while(!in_set(c, "\n\r"))
    {
        if(EOF == c) break;
        c = fgetc(source_file);
    }
}

void purge_string(FILE* source_file)
{
    int c = fgetc(source_file);
    while((EOF != c) && ('"' != c))
    {
        c = fgetc(source_file);
    }
}

void first_pass(struct entry* input)
{
    if(NULL == input) return;
    first_pass(input->next);

    FILE* source_file = fopen(input->name, "r");

    if(NULL == source_file)
    {
        fputs("The file: ", stderr);
        fputs(input->name, stderr);
        fputs(" can not be opened!\n", stderr);
        exit(EXIT_FAILURE);
    }

    int c;
    for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
    {
        /* Check for and deal with label */
        if(58 == c)
        {
            storeLabel(source_file);
        }
        /* Check for and deal with line comments */
        else if (c == '#' || c == ';')
        {
            line_Comment(source_file);
        }
        else if ('"' == c)
        {
            purge_string(source_file);
        }
    }
    fclose(source_file);
}

void output_string_table(struct entry* node)
{
    fputs("\n# Generated string table\n:ELF_str\n", output);
    fputs(zero_8, output);
    fputs("\t# NULL string\n", output);
    struct entry* i;
    for(i = node; NULL != i; i = i->next)
    {
        fputs(":ELF_str_", output);
        fputs(i->name, output);
        fputs("\t\"", output);
        fputs(i->name, output);
        fputs("\"\n", output);
    }
    fputs("# END Generated string table\n\n", output);
}

void output_symbol_table(struct entry* node)
{
    fputs("\n# Generated symbol table\n:ELF_sym\n# Required NULL symbol entry\n", output);
    if(64 == BITSIZE)
    {
        fputs(zero_32, output);
        fputs("\t# st_name\n", output);

        fputs(zero_8, output);
        fputs("\t# st_info\n", output);

        fputs(zero_8, output);
        fputs("\t# st_other\n", output);

        fputs(one_16, output);
        fputs("\t# st_shndx\n", output);

        fputs(zero_32, output);
        fputc(' ', output);
        fputs(zero_32, output);
        fputs("\t# st_value\n", output);

        fputs(zero_32, output);
        fputc(' ', output);
        fputs(zero_32, output);
        fputs("\t# st_size\n\n", output);
    }
    else
    {
        fputs(zero_32, output);
        fputs("\t# st_name\n", output);

        fputs(zero_32, output);
        fputs("\t# st_value\n", output);

        fputs(zero_32, output);
        fputs("\t# st_size\n", output);

        fputs(zero_8, output);
        fputs("\t# st_info\n", output);

        fputs(zero_8, output);
        fputs("\t# st_other\n", output);

        fputs(one_16, output);
        fputs("\t# st_shndx\n\n", output);
    }

    struct entry* i;
    for(i = node; NULL != i; i = i->next)
    {
        fputs("%ELF_str_", output);
        fputs(i->name, output);
        fputs(">ELF_str\t# st_name\n", output);

        if(64 == BITSIZE)
        {
            fputs(two_8, output);
            fputs("\t# st_info (FUNC)\n", output);

            if(('_' == i->name[0]) && !match(entry, i->name))
            {
                fputs(two_8, output);
                fputs("\t# st_other (hidden)\n", output);
            }
            else
            {
                fputs(zero_8, output);
                fputs("\t# st_other (other)\n", output);
            }

            fputs(one_16, output);
            fputs("\t# st_shndx\n", output);

            fputs("&", output);
            fputs(i->name, output);
            fputc(' ', output);
            fputs(zero_32, output);
            fputs("\t# st_value\n", output);

            fputs(zero_32, output);
            fputc(' ', output);
            fputs(zero_32, output);
            fputs("\t# st_size (unknown size)\n\n", output);
        }
        else
        {
            fputs("&", output);
            fputs(i->name, output);
            fputs("\t#st_value\n", output);

            fputs(zero_32, output);
            fputs("\t# st_size (unknown size)\n", output);

            fputs(two_8, output);
            fputs("\t# st_info (FUNC)\n", output);

            if(('_' == i->name[0]) && !match(entry, i->name))
            {
                fputs(two_8, output);
                fputs("\t# st_other (hidden)\n", output);
            }
            else
            {
                fputs(zero_8, output);
                fputs("\t# st_other (default)\n", output);
            }

            fputs(one_16, output);
            fputs("\t# st_shndx\n\n", output);
        }
    }

    fputs("# END Generated symbol table\n", output);
}

struct entry* reverse_list(struct entry* head)
{
    struct entry* root = NULL;
    struct entry* next;
    while(NULL != head)
    {
        next = head->next;
        head->next = root;
        root = head;
        head = next;
    }
    return root;
}

void write_int(char* field, char* label)
{
    fputs(field, output);
    fputs("\t#", output);
    fputs(label, output);
    fputc('\n', output);
}

void write_register(char* field, char* label)
{
    /* $field section in the section headers are different size for 32 and 64bits */
    /* The below is broken for BigEndian */
    fputs(field, output);
    if(64 == BITSIZE)
    {
        fputc(' ', output);
        fputs(zero_32, output);
    }

    fputs("\t#", output);
    fputs(label, output);
    fputc('\n', output);
}

void write_section(char* label, char* name, char* type, char* flags, char* address, char* offset, char* size, char* link, char* info, char* entry)
{
    /* Write label */
    fputc('\n', output);
    fputs(label, output);
    fputc('\n', output);

    write_int(name, "sh_name");
    write_int(type, "sh_type");
    write_register(flags, "sh_flags");
    write_register(address, "sh_addr");
    write_register(offset, "sh_offset");
    write_register(size, "sh_size");
    write_int(link, "sh_link");

    /* Deal with the ugly case of stubs */
    fputs(info, output);
    fputs("\t#sh_info\n", output);

    /* Alignment section in the section headers are different size for 32 and 64bits */
    /* The below is broken for BigEndian */
    if(64 == BITSIZE)
    {
        fputs(one_32, output);
        fputc(' ', output);
        fputs(zero_32, output);
        fputs("\t#sh_addralign\n", output);
    }
    else
    {
        fputs(one_32, output);
        fputs("\t#sh_addralign\n", output);
    }

    write_register(entry, "sh_entsize");
}

char* get_string(int value, int size, int ByteMode, int shift)
{
    char* ch = calloc(42, sizeof(char));
    require(NULL != ch, "Exhausted available memory\n");
    ch[0] = '\'';
    stringify(ch+1, size, ByteMode, value, shift);
    if(!BigEndian) LittleEndian(ch+1, ByteMode);
    int i = 0;
    while(0 != ch[i])
    {
        i = i + 1;
    }
    ch[i] = '\'';
    return ch;
}

char* setup_string(int value, int number_of_bytes, int ByteMode)
{
    int shift;
    int size;
    if(HEX == ByteMode)
    {
        size = 2;
        shift = 4;
    }
    else if(OCTAL == ByteMode)
    {
        size = 3;
        shift = 3;
    }
    else if(BINARY == ByteMode)
    {
        size = 8;
        shift = 1;
    }
    else
    {
        fputs("reached impossible mode\n", stderr);
        exit(EXIT_FAILURE);
    }

    return get_string(value, number_of_bytes *size, ByteMode, shift);
}

void setup_strings(int ByteMode)
{
    zero_8 = setup_string(0, 1, ByteMode);
    zero_16 = setup_string(0, 2, ByteMode);
    zero_32 = setup_string(0, 4, ByteMode);
    one_16 = setup_string(1, 2, ByteMode);
    one_32 = setup_string(1, 4, ByteMode);
    two_8 = setup_string(2, 1, ByteMode);
    two_32 = setup_string(2, 4, ByteMode);
    three_32 = setup_string(3, 4, ByteMode);
    six_32 = setup_string(6, 4, ByteMode);
    sixteen_32 = setup_string(16, 4, ByteMode);
    twentyfour_32 = setup_string(24, 4, ByteMode);
}

/* Standard C main program */
int main(int argc, char **argv)
{
    jump_table = NULL;
    struct entry* input = NULL;
    output = stdout;
    char* output_file = "";
    entry = "";
    BITSIZE = 32;
    count = 1;
    BigEndian = TRUE;
    int ByteMode = HEX;
    int set = FALSE;
    struct entry* temp;
    struct entry* head;

    int option_index = 1;
    while(option_index <= argc)
    {
        if(NULL == argv[option_index])
        {
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-h") || match(argv[option_index], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file FILENAME1 {--file FILENAME2} --output FILENAME\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "--64"))
        {
            BITSIZE = 64;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-f") || match(argv[option_index], "--file"))
        {
            temp = calloc(1, sizeof(struct entry));
            temp->name = argv[option_index + 1];
            temp->next = input;
            input = temp;
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-o") || match(argv[option_index], "--output"))
        {
            output_file = argv[option_index + 1];
            output = fopen(output_file, "w");

            if(NULL == output)
            {
                fputs("The file: ", stderr);
                fputs(input->name, stderr);
                fputs(" can not be opened!\n", stderr);
                exit(EXIT_FAILURE);
            }
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-b") || match(argv[option_index], "--binary"))
        {
            ByteMode = BINARY;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-O") || match(argv[option_index], "--octal"))
        {
            ByteMode = OCTAL;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-X") || match(argv[option_index], "--hex"))
        {
            ByteMode = HEX;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--big-endian"))
        {
            BigEndian = TRUE;
            set = TRUE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--little-endian"))
        {
            BigEndian = FALSE;
            set = TRUE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-V") || match(argv[option_index], "--version"))
        {
            fputs("blood-elf 2.0.0\n(Basically Launches Odd Object Dump ExecutabLe Files\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "--entry"))
        {
            head = calloc(1, sizeof(struct entry));
            /* Include _start or any other entry from your .hex2 */
            head->next = jump_table;
            jump_table = head;
            jump_table->name = argv[option_index + 1];
            /* However only the last one will be exempt from the _name hidden rule */
            entry = argv[option_index + 1];
            option_index = option_index + 2;
            count = count + 1;
        }
        else
        {
            fputs("Unknown option\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    /* Make sure we have a program tape to run */
    if (NULL == input)
    {
        return EXIT_FAILURE;
    }

    /* Force setting of endianness */
    if(!set)
    {
        fputs("either --little-endian or --big-endian MUST be set\n", stderr);
        return EXIT_FAILURE;
    }

    /* Setup the ugly formating because RISC-V sucks */
    setup_strings(ByteMode);

    /* Get all of the labels */
    first_pass(input);

    /* Reverse their order */
    jump_table = reverse_list(jump_table);

    /* Create sections */
    /* Create string names for sections */
    fputs("# Generated sections\n:ELF_shstr\n", output);
    fputs(zero_8, output);
    fputs("\t# NULL\n", output);
    fputs(":ELF_shstr__text\n\".text\"\n", output);
    fputs(":ELF_shstr__shstr\n\".shstrtab\"\n", output);
    fputs(":ELF_shstr__sym\n\".symtab\"\n", output);
    fputs(":ELF_shstr__str\n\".strtab\"\n", output);

    /* Create NULL section header as is required by the Spec. So dumb and waste of bytes*/
    write_section(":ELF_section_headers", zero_32, zero_32, zero_32, zero_32, zero_32, zero_32, zero_32, zero_32, zero_32);
    write_section(":ELF_section_header_text", "%ELF_shstr__text>ELF_shstr", one_32, six_32, "&ELF_text", "%ELF_text>ELF_base", "%ELF_data>ELF_text", zero_32, zero_32, zero_32);
    write_section(":ELF_section_header_shstr", "%ELF_shstr__shstr>ELF_shstr", three_32, zero_32, "&ELF_shstr", "%ELF_shstr>ELF_base", "%ELF_section_headers>ELF_shstr", zero_32, zero_32, zero_32);
    write_section(":ELF_section_header_str", "%ELF_shstr__str>ELF_shstr", three_32, zero_32, "&ELF_str", "%ELF_str>ELF_base", "%ELF_sym>ELF_str", zero_32, zero_32, zero_32);
    if(64 == BITSIZE) write_section(":ELF_section_header_sym", "%ELF_shstr__sym>ELF_shstr", two_32, zero_32, "&ELF_sym", "%ELF_sym>ELF_base", "%ELF_end>ELF_sym", three_32, setup_string(count, 4, ByteMode), twentyfour_32);
    else write_section(":ELF_section_header_sym", "%ELF_shstr__sym>ELF_shstr", two_32, zero_32, "&ELF_sym", "%ELF_sym>ELF_base", "%ELF_end>ELF_sym", three_32, setup_string(count, 4, ByteMode), sixteen_32);

    /* Create dwarf stubs needed for objdump -d to get function names */
    output_string_table(jump_table);
    output_symbol_table(jump_table);
    fputs("\n:ELF_end\n", output);

    /* Close output file */
    fclose(output);

    return EXIT_SUCCESS;
}

https://github.com/oriansj/M2libc/blob/main/x86/x86_defs.M1

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/x86_defs.M1 used in: Step 20, Step 26, Step 32, Step 37, Step 41, Step 45, Step 51, Step 55, Step 59, Step 63, Step 101
## Copyright (C) 2017 Jeremiah Orians
## This file is part of M2-Planet.
##
## M2-Planet is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.


DEFINE add_eax, 81C0
DEFINE add_ebp, 81C5
DEFINE add_ebx,eax 01C3
DEFINE add_eax,ebp 01E8
DEFINE add_eax,ebx 01D8
DEFINE and_eax,ebx 21D8
DEFINE call E8
DEFINE call_eax FFD0
DEFINE cmp 39C3
DEFINE cdq 99
DEFINE div_ebx F7F3
DEFINE idiv_ebx F7FB
DEFINE int CD
DEFINE je 0F84
DEFINE jne 0F85
DEFINE jmp E9
DEFINE lea_eax,[ebp+DWORD] 8D85
DEFINE lea_eax,[esp+DWORD] 8D8424
DEFINE lea_ebx,[esp+DWORD] 8D9C24
DEFINE lea_ecx,[esp+DWORD] 8D8C24
DEFINE lea_edx,[esp+DWORD] 8D9424
DEFINE mov_eax,[esp+DWORD] 8B8424
DEFINE mov_eax,ebp 89E8
DEFINE mov_eax,ebx 89D8
DEFINE mov_eax,ebx 89D8
DEFINE mov_eax,edx 89D0
DEFINE mov_ebx,eax 89C3
DEFINE mov_ecx,eax 89C1
DEFINE mov_ecx,esp 89E1
DEFINE mov_edi,esp 89E7
DEFINE mov_ebp,edi 89fd
DEFINE mov_ebp,esp 89E5
DEFINE mov_eax, B8
DEFINE mov_ebx, BB
DEFINE mov_edx, BA
DEFINE mov_eax,[eax] 8B00
DEFINE mov_ebx,[ebx] 8B1B
DEFINE mov_ecx,[ecx] 8B09
DEFINE mov_edx,[edx] 8B12
DEFINE mov_[ebx],al 8803
DEFINE mov_[ebx],ax 668903
DEFINE mov_[ebx],eax 8903
DEFINE movsx_eax,BYTE_PTR_[eax] 0FBE00
DEFINE movsx_ebx,BYTE_PTR_[ebx] 0FBE1B
DEFINE movsx_eax,WORD_PTR_[eax] 0FBF00
DEFINE movzx_eax,BYTE_PTR_[eax] 0FB600
DEFINE movzx_eax,WORD_PTR_[eax] 0FB700
DEFINE movzx_eax,al 0FB6C0
DEFINE mul_ebx F7E3
DEFINE imul_ebx F7EB
DEFINE NULL 00000000
DEFINE not_eax F7D0
DEFINE or_eax,ebx 09D8
DEFINE pop_eax 58
DEFINE pop_ebx 5B
DEFINE pop_ebp 5D
DEFINE pop_edi 5F
DEFINE push_eax 50
DEFINE push_ebx 53
DEFINE push_ebp 55
DEFINE push_edi 57
DEFINE ret C3
DEFINE sal_eax, C1E0
DEFINE sal_eax,cl D3F0
DEFINE shl_eax,cl D3E0
DEFINE sar_eax,cl D3F8
DEFINE shr_eax,cl D3E8
DEFINE seta_al 0F97C0
DEFINE setae_al 0F93C0
DEFINE setb_al 0F92C0
DEFINE setbe_al 0F96C0
DEFINE sete_al 0F94C0
DEFINE setle_al 0F9EC0
DEFINE setl_al 0F9CC0
DEFINE setge_al 0F9DC0
DEFINE setg_al 0F9FC0
DEFINE setne_al 0F95C0
DEFINE sub_ebx,eax 29C3
DEFINE test_eax,eax 85C0
DEFINE xchg_ebx,eax 93
DEFINE xor_eax,ebx 31D8

https://github.com/oriansj/M2libc/blob/main/x86/libc-core.M1

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/libc-core.M1 used in: Step 20, Step 26
## Copyright (C) 2016 Jeremiah Orians
## This file is part of M2-Planet.
##
## M2-Planet is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.

:_start

    mov_ebp,esp                 ; Protect esp

    ;; Prepare argv
    lea_eax,[ebp+DWORD] %4      ; ARGV_address = EBP + 4
    push_eax                    ; Put argv on the stack

    ;; Prepare envp
    mov_eax,ebp                 ; Address we need to load from
    mov_eax,[eax]               ; Get ARGC
    add_eax, %2                 ; OFFSET = ARGC + 2
    sal_eax, !2                 ; OFFSET = OFFSET * WORDSIZE
    add_eax,ebp                 ; ENVP_address = ESP + OFFSET
    push_eax                    ; Put envp on the stack

    ;; Stack offset
    add_ebp, %4                 ; Fix ebp

    ;; Perform the main loop
    call %FUNCTION_main
    push_eax                    ; Put return on stack
    push_eax                    ; so that _exit gets the value

    ;; Exit to kernel
:FUNCTION_exit
:FUNCTION__exit
    mov_ebx,eax                 ; Using the return code given by main
    mov_eax, %1                 ; Syscall exit
    int !0x80                   ; Exit with that code

https://github.com/oriansj/M2libc/blob/main/x86/ELF-x86.hex2

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/ELF-x86.hex2 used in: Step 22
### Copyright (C) 2016 Jeremiah Orians
### Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
### This file is part of M2-Planet.
###
### M2-Planet is free software: you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation, either version 3 of the License, or
### (at your option) any later version.
###
### M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.

### stage0's hex2 format
###    !<label>          1 byte relative
###    $<label>          2 byte address
###    @<label>          2 byte relative
###    &<label>          4 byte address
###    %<label>          4 byte relative

### if you wish to use this header, you need to add :ELF_end to the end of your
### M1 or hex2 files.

## ELF Header

:ELF_base
7F 45 4C 46                    # e_ident[EI_MAG0-3] ELF's magic number

01                             # e_ident[EI_CLASS] Indicating 32 bit
01                             # e_ident[EI_DATA] Indicating little endianness
01                             # e_ident[EI_VERSION] Indicating original elf

03                             # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                             # e_ident[EI_ABIVERSION] See above

00 00 00 00 00 00 00           # e_ident[EI_PAD]

02 00                          # e_type Indicating Executable
03 00                          # e_machine Indicating 386
01 00 00 00                    # e_version Indicating original elf

&_start                        # e_entry Address of the entry point
%ELF_program_headers>ELF_base  # e_phoff Address of program header table
00 00 00 00                    # e_shoff Address of section header table

00 00 00 00                    # e_flags

34 00                          # e_ehsize Indicating our 52 Byte header

20 00                          # e_phentsize size of a program header table
01 00                          # e_phnum number of entries in program table

00 00                          # e_shentsize size of a section header table
00 00                          # e_shnum number of entries in section table

00 00                          # e_shstrndx index of the section names


:ELF_program_headers
:ELF_program_header__text
01 00 00 00                    # ph_type: PT-LOAD = 1
00 00 00 00                    # ph_offset
&ELF_base                      # ph_vaddr
&ELF_base                      # ph_physaddr
%ELF_end>ELF_base              # ph_filesz
%ELF_end>ELF_base              # ph_memsz
07 00 00 00                    # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                    # ph_alignment

:ELF_text

https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=M1-macro.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/M1-macro.c used in: Step 24, Step 35
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "M2libc/bootstrappable.h"

/* Internal processing Constants */
// CONSTANT max_string 4096
#define max_string 4096
// CONSTANT PROCESSED 1
#define PROCESSED 1
// CONSTANT STR 2
#define STR 2
// CONSTANT NEWLINE 3
#define NEWLINE 3

/* Unique code for each architecture */
// CONSTANT KNIGHT 0
#define KNIGHT 0
// CONSTANT X86 3
#define X86 0x03
// CONSTANT AMD64 62
#define AMD64 0x3E
// CONSTANT ARMV7L 40
#define ARMV7L 0x28
// CONSTANT AARM64 183
#define AARM64 0xB7
// CONSTANT PPC64LE 21
#define PPC64LE 0x15
// CONSTANT RISCV32 243
#define RISCV32 0xF3
// CONSTANT RISCV64 65779
#define RISCV64 0x100F3 /* Because RISC-V unlike all other architectures does get a seperate e_machine when changing from 32 to 64bit */


/* How do you want that output? */
// CONSTANT HEX 16
#define HEX 16
// CONSTANT OCTAL 8
#define OCTAL 8
// CONSTANT BINARY 2
#define BINARY 2

/* Imported from stringify.c */
int stringify(char* s, int digits, int divisor, int value, int shift);
void LittleEndian(char* start, int ByteMode);

struct blob
{
    struct blob* next;
    int type;
    char* Text;
    char* Expression;
    struct blob* hash_next;
};

struct Token
{
    struct Token* next;
    struct blob* contents;
    char* filename;
    int linenumber;
};

/* Globals */
FILE* source_file;
FILE* destination_file;
int BigEndian;
int ByteMode;
int Architecture;
int linenumber;
struct Token* token_list;
struct blob* blob_list;
struct blob* define_blob;
struct blob* newline_blob;
int blob_count;
char* SCRATCH;
struct blob** hash_table;

void line_error(char* filename, int linenumber)
{
    fputs(filename, stderr);
    fputs(":", stderr);
    fputs(int2str(linenumber,10, FALSE), stderr);
    fputs(" :", stderr);
}

void ClearScratch()
{
    int i = 0;
    int c = SCRATCH[i];
    while(0 != c)
    {
        SCRATCH[i] = 0;
        i = i + 1;
        c = SCRATCH[i];
    }
}

int GetHash(char* s)
{
    int i = 5381;
    while(0 != s[0])
    {
        i = (i << 5) + i + s[0];
        s = s + 1;
    }
    return i & 0xFFFF;
}

struct blob* FindBlob()
{
    int hash = GetHash(SCRATCH);
    struct blob* i = hash_table[hash];
    while(NULL != i)
    {
        if(match(SCRATCH, i->Text)) return i;
        i = i->hash_next;
    }

    return NULL;
}

void AddHash(struct blob* a, char* s)
{
    int i = GetHash(s);
    a->hash_next = hash_table[i];
    hash_table[i] = a;
}

void NewBlob(int size)
{
    blob_count = blob_count + 1;
    struct blob* a = calloc(1, sizeof(struct blob));
    require(NULL != a, "Exhausted available memory\n");
    a->Text = calloc(size + 1, sizeof(char));
    require(NULL != a->Text, "Exhausted available memory\n");

    int i = 0;
    while(i <= size)
    {
        a->Text[i] = SCRATCH[i];
        i = i + 1;
    }
    a->next = blob_list;
    blob_list = a;
    AddHash(a, SCRATCH);
}

struct Token* newToken(char* filename, int linenumber)
{
    struct Token* p;

    p = calloc (1, sizeof (struct Token));
    require(NULL != p, "Exhausted available memory\n");

    p->filename = filename;
    p->linenumber = linenumber;

    return p;
}

struct Token* reverse_list(struct Token* head)
{
    struct Token* root = NULL;
    struct Token* next;
    while(NULL != head)
    {
        next = head->next;
        head->next = root;
        root = head;
        head = next;
    }
    return root;
}

void purge_lineComment()
{
    int c = fgetc(source_file);
    while(!in_set(c, "\n\r"))
    {
        if(EOF == c) break;
        c = fgetc(source_file);
    }
}

struct Token* append_newline(struct Token* head, char* filename)
{
    linenumber = linenumber + 1;
    if(NULL == head) return NULL;
    if(NEWLINE == head->contents->type)
    {/* Don't waste whitespace*/
        return head;
    }

    struct Token* lf = newToken(filename, linenumber);
    lf->contents = newline_blob;
    lf->next = head;
    return lf;
}


struct Token* store_atom(struct Token* head, char c, char* filename)
{
    ClearScratch();
    int ch = c;
    int i = 0;
    do
    {
        SCRATCH[i] = ch;
        ch = fgetc(source_file);
        i = i + 1;
        if(i >= max_string)
        {
            fputs("storing atom of size larger than max_string\n", stderr);
            line_error(filename, linenumber);
            fputc('\n', stderr);
            exit(EXIT_FAILURE);
        }
        if(EOF == ch) break;
    } while (!in_set(ch, "\t\n "));

    head->contents = FindBlob();
    if(NULL == head->contents)
    {
        NewBlob(i);
        head->contents = blob_list;
    }

    if('\n' == ch)
    {
        return append_newline(head, filename);
    }
    return head;
}

struct blob* store_string(char c, char* filename)
{
    ClearScratch();

    int ch = c;
    int i = 0;
    do
    {
        SCRATCH[i] = ch;
        i = i + 1;
        if('\n' == ch) linenumber = linenumber + 1;
        ch = fgetc(source_file);
        require(EOF != ch, "Unmatched \"!\n");

        if(max_string == i)
        {
            line_error(filename, linenumber);
            fputs("String: ", stderr);
            fputs(SCRATCH, stderr);
            fputs(" exceeds max string size\n", stderr);
            exit(EXIT_FAILURE);
        }
    } while(ch != c);

    struct blob* a = FindBlob();
    if(NULL == a)
    {
        NewBlob(i);
        a = blob_list;
        a->type = STR;
    }

    return a;
}

struct Token* Tokenize_Line(struct Token* head, char* filename)
{
    int c;
    struct Token* p;
    linenumber = 1;

    do
    {
restart:
        c = fgetc(source_file);

        if(in_set(c, ";#"))
        {
            purge_lineComment();
            head = append_newline(head, filename);
            goto restart;
        }

        if(in_set(c, "\t "))
        {
            goto restart;
        }

        if('\n' == c)
        {
            head = append_newline(head, filename);
            goto restart;
        }

        if(EOF == c)
        {
            head = append_newline(head, filename);
            goto done;
        }

        p = newToken(filename, linenumber);
        p->next = head;
        if(in_set(c, "'\""))
        {
            p->contents = store_string(c, filename);
        }
        else
        {
            p = store_atom(p, c, filename);
        }

        head = p;
    } while(TRUE);
done:
    return head;
}

void line_macro(struct Token* p)
{
    struct Token* i;
    for(i = p; NULL != i; i = i->next)
    {
        if(define_blob == i->contents)
        {
            require(NULL != i->next, "Macro name must exist\n");
            require(NULL != i->next->next, "Macro value must exist\n");
            if(PROCESSED == i->next->contents->type)
            {
                line_error(i->filename, i->linenumber);
                fputs("Multiple definitions for macro ", stderr);
                fputs(i->next->contents->Text, stderr);
                fputs("\n", stderr);
                exit(EXIT_FAILURE);
            }

            i->contents = newline_blob;

            if (STR == i->next->next->contents->type)
            {
                i->contents->Expression = i->next->next->contents->Text + 1;
            }
            else
            {
                i->next->contents->Expression = i->next->next->contents->Text;
            }
            i->next = i->next->next->next;
        }
    }
}

void hexify_string(struct blob* p)
{
    char* table = "0123456789ABCDEF";
    int i = strlen(p->Text);
    int size;

    if(HEX == ByteMode) size = (((i << 1) + i) + 12);
    else if(OCTAL == ByteMode) size = (i << 2) + 1;
    else if(BINARY == ByteMode) size = (i << 3) + i + 1;
    else size = 1;

    require(1 != size, "hexify_string lacked a valid bytemode\n");
    char* d = calloc(size, sizeof(char));
    require(NULL != d, "Exhausted available memory\n");
    p->Expression = d;
    char* S = p->Text;

    if((KNIGHT == Architecture) && (HEX == ByteMode))
    {
        i = (((((i - 1) >> 2) + 1) << 3) + i);
        while( 0 < i)
        {
            i = i - 1;
            d[i] = '0';
        }
    }

    if(HEX == ByteMode)
    {
        while(0 != S[0])
        {
            S = S + 1;
            d[0] = table[S[0] >> 4];
            d[1] = table[S[0] & 0xF];
            d[2] = ' ';
            d = d + 3;
        }
    }
    else if(OCTAL == ByteMode)
    {
        while(0 != S[0])
        {
            S = S + 1;
            d[0] = table[S[0] >> 6];
            d[1] = table[(S[0] >> 3) & 0x7];
            d[2] = table[S[0] & 0x7];
            d[3] = ' ';
            d = d + 4;
        }
    }
    else if(BINARY == ByteMode)
    {
        while(0 != S[0])
        {
            S = S + 1;
            d[0] = table[S[0] >> 7];
            d[1] = table[(S[0] >> 6) & 0x1];
            d[2] = table[(S[0] >> 5) & 0x1];
            d[3] = table[(S[0] >> 4) & 0x1];
            d[4] = table[(S[0] >> 3) & 0x1];
            d[5] = table[(S[0] >> 2) & 0x1];
            d[6] = table[(S[0] >> 1) & 0x1];
            d[7] = table[S[0] & 0x1];
            d[8] = ' ';
            d = d + 9;
        }
    }
}

void process_string(struct blob* p)
{
    struct blob* i;
    for(i = p; NULL != i; i = i->next)
    {
        if(STR == i->type)
        {
            if('\'' == i->Text[0])
            {
                i->Expression = i->Text + 1;
            }
            else if('"' == i->Text[0])
            {
                hexify_string(i);
            }
        }
    }
}

char* pad_nulls(int size, char* nil)
{
    if(0 == size) return nil;
    require(size > 0, "negative null padding not possible\n");
    if(HEX == ByteMode) size = size * 2;
    else if (OCTAL == ByteMode) size = size * 3;
    else if (BINARY == ByteMode) size = size * 8;

    char* s = calloc(size + 1, sizeof(char));
    require(NULL != s, "Exhausted available memory\n");

    int i = 0;
    while(i < size)
    {
        s[i] = '0';
        i = i + 1;
    }

    return s;
}

void preserve_other(struct blob* p)
{
    struct blob* i;
    char c;
    for(i = p; NULL != i; i = i->next)
    {
        if((NULL == i->Expression) && !(i->type & PROCESSED))
        {
            c = i->Text[0];

            if(in_set(c, "!@$~%&:^"))
            {
                i->Expression = i->Text;
            }
            else if('<' == c)
            {
                i->Expression = pad_nulls(strtoint(i->Text + 1), i->Text);
            }
        }
    }
}

void bound_values(int displacement, int number_of_bytes, int low, int high)
{
    if((high < displacement) || (displacement < low))
    {
        fputs("A displacement of ", stderr);
        fputs(int2str(displacement, 10, TRUE), stderr);
        fputs(" does not fit in ", stderr);
        fputs(int2str(number_of_bytes, 10, TRUE), stderr);
        fputs(" bytes\n", stderr);
        exit(EXIT_FAILURE);
    }
}

void range_check(int displacement, int number_of_bytes)
{
    if(4 == number_of_bytes) return;
    else if(3 == number_of_bytes)
    {
        bound_values(displacement, number_of_bytes, -8388608, 16777216);
        return;
    }
    else if(2 == number_of_bytes)
    {
        bound_values(displacement, number_of_bytes, -32768, 65535);
        return;
    }
    else if(1 == number_of_bytes)
    {
        bound_values(displacement, number_of_bytes, -128, 255);
        return;
    }

    fputs("Received an invalid number of bytes in range_check\n", stderr);
    exit(EXIT_FAILURE);
}

char* express_number(int value, char c)
{
    char* ch = calloc(42, sizeof(char));
    require(NULL != ch, "Exhausted available memory\n");
    int size;
    int number_of_bytes;
    int shift;
    if('!' == c)
    {
        number_of_bytes = 1;
        value = value & 0xFF;
    }
    else if('@' == c)
    {
        number_of_bytes = 2;
        value = value & 0xFFFF;
    }
    else if('~' == c)
    {
        number_of_bytes = 3;
        value = value & 0xFFFFFF;
    }
    else if('%' == c)
    {
        number_of_bytes = 4;
        value = value & 0xFFFFFFFF;
    }
    else
    {
        fputs("Given symbol ", stderr);
        fputc(c, stderr);
        fputs(" to express immediate value ", stderr);
        fputs(int2str(value, 10, TRUE), stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }

    range_check(value, number_of_bytes);

    if(HEX == ByteMode)
    {
        size = number_of_bytes * 2;
        shift = 4;
    }
    else if(OCTAL == ByteMode)
    {
        size = number_of_bytes * 3;
        shift = 3;
    }
    else if(BINARY == ByteMode)
    {
        size = number_of_bytes * 8;
        shift = 1;
    }
    else
    {
        fputs("Got invalid ByteMode in express_number\n", stderr);
        exit(EXIT_FAILURE);
    }

    stringify(ch, size, ByteMode, value, shift);

    if(!BigEndian) LittleEndian(ch, ByteMode);
    return ch;
}

char* express_word(int value, char c)
{
    char* s = calloc(43, sizeof(char));
    s[0] = '.';
    char* ch = s + 1;
    require(NULL != ch, "Exhausted available memory\n");
    int size;
    int shift;
    int immediate;
    if('!' == c)
    {
        /* Corresponds to RISC-V I format */
        immediate = (value & 0xFFF) << 20;
    }
    else if('@' == c)
    {
        /* Corresponds to RISC-V S format */
        immediate = ((value & 0x1F) << 7) | ((value & 0xFE0) << (31 - 11));
    }
    else if('~' == c)
    {
        /* Corresponds with RISC-V U format */
        if ((value & 0xFFF) < 0x800)
        {
            immediate = value & 0xFFFFF000;
        }
        else
        {
            immediate = (value & 0xFFFFF000) + 0x1000;
        }
    }
    else if('%' == c)
    {
        /* provides an option for 32bit immediate constants */
        immediate = value & 0xFFFFFFFF;
        /* Drop the leading . */
        ch = s;
    }
    else
    {
        fputs("Given symbol ", stderr);
        fputc(c, stderr);
        fputs(" to express immediate value ", stderr);
        fputs(int2str(value, 10, TRUE), stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }

    if(HEX == ByteMode)
    {
        size = 4 * 2;
        shift = 4;
    }
    else if(OCTAL == ByteMode)
    {
        size = 4 * 3;
        shift = 3;
    }
    else if(BINARY == ByteMode)
    {
        size = 4 * 8;
        shift = 1;
    }
    else
    {
        fputs("Got invalid ByteMode in express_number\n", stderr);
        exit(EXIT_FAILURE);
    }

    stringify(ch, size, ByteMode, immediate, shift);

    if(!BigEndian) LittleEndian(ch, ByteMode);
    return s;
}

void eval_immediates(struct blob* p)
{
    struct blob* i;
    int value;
    for(i = p; NULL != i; i = i->next)
    {
        if(PROCESSED == i->type) continue;
        else if(NEWLINE == i->type) continue;
        else if('<' == i->Text[0]) continue;
        else if(NULL == i->Expression)
        {
            if((X86 == Architecture) || (AMD64 == Architecture) || (ARMV7L == Architecture) || (AARM64 == Architecture) || (PPC64LE == Architecture))
            {
                if(in_set(i->Text[0], "%~@!"))
                {
                    value = strtoint(i->Text + 1);

                    if(('0' == i->Text[1]) || (0 != value))
                    {
                        i->Expression = express_number(value, i->Text[0]);
                    }
                }
            }
            else if((RISCV32 == Architecture) || (RISCV64 == Architecture))
            {
                if(in_set(i->Text[0], "%~@!"))
                {
                    value = strtoint(i->Text + 1);

                    if(('0' == i->Text[1]) || (0 != value))
                    {
                        i->Expression = express_word(value, i->Text[0]);
                    }
                }
            }
            else if(KNIGHT == Architecture)
            {
                value = strtoint(i->Text);
                if(('0' == i->Text[0]) || (0 != value))
                {
                    i->Expression = express_number(value, '@');
                }
            }
            else
            {
                fputs("Unknown architecture received in eval_immediates\n", stderr);
                exit(EXIT_FAILURE);
            }
        }
    }
}

void print_hex(struct Token* p)
{
    struct Token* i;
    for(i = p; NULL != i; i = i->next)
    {
        if(NEWLINE == i->contents->type)
        {
            if(NULL == i->next) fputc('\n', destination_file);
            else if(NEWLINE != i->next->contents->type) fputc('\n', destination_file);
        }
        else if(NULL != i->contents->Expression)
        {
            fputs(i->contents->Expression, destination_file);
            if(NEWLINE != i->next->contents->type) fputc(' ', destination_file);
        }
        else
        {
            line_error(i->filename, i->linenumber);
            fputs("Received invalid other; ", stderr);
            fputs(i->contents->Text, stderr);
            fputs("\n", stderr);
            exit(EXIT_FAILURE);
        }
    }
}

/* Standard C main program */
int main(int argc, char **argv)
{
    BigEndian = TRUE;
    Architecture = KNIGHT;
    destination_file = stdout;
    ByteMode = HEX;
    char* filename;
    char* arch;
    blob_count = 2;
    hash_table = calloc(65537, sizeof(struct blob*));
    require(NULL != hash_table, "failed to allocate hash_table\n");

    /* Create newline blob */
    newline_blob = calloc(1, sizeof(struct blob));
    require(NULL != newline_blob, "failed to allocate newline_blob\n");
    newline_blob->Text = "\n";
    newline_blob->Expression = "\n";
    newline_blob->type = NEWLINE;
    AddHash(newline_blob, "\n");

    /* Start the blob list with DEFINE and newline */
    blob_list = calloc(1, sizeof(struct blob));
    require(NULL != blob_list, "failed to allocate DEFINE blob\n");
    blob_list->Text = "DEFINE";
    define_blob = blob_list;
    blob_list->next = newline_blob;
    AddHash(define_blob, "DEFINE");

    /* Initialize scratch */
    SCRATCH = calloc(max_string + 1, sizeof(char));
    require(NULL != SCRATCH, "failed to allocate SCRATCH buffer");

    int option_index = 1;
    while(option_index <= argc)
    {
        if(NULL == argv[option_index])
        {
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--big-endian"))
        {
            BigEndian = TRUE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--little-endian"))
        {
            BigEndian = FALSE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-A") || match(argv[option_index], "--architecture"))
        {
            arch = argv[option_index + 1];
            if(match("knight-native", arch) || match("knight-posix", arch)) Architecture = KNIGHT;
            else if(match("x86", arch)) Architecture = X86;
            else if(match("amd64", arch)) Architecture = AMD64;
            else if(match("armv7l", arch)) Architecture = ARMV7L;
            else if(match("aarch64", arch)) Architecture = AARM64;
            else if(match("ppc64le", arch)) Architecture = PPC64LE;
            else if(match("riscv32", arch)) Architecture = RISCV32;
            else if(match("riscv64", arch)) Architecture = RISCV64;
            else
            {
                fputs("Unknown architecture: ", stderr);
                fputs(arch, stderr);
                fputs(" know values are: knight-native, knight-posix, x86, amd64, armv7l, aarch64, ppc64le, riscv32 and riscv64", stderr);
                exit(EXIT_FAILURE);
            }
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-b") || match(argv[option_index], "--binary"))
        {
            ByteMode = BINARY;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-h") || match(argv[option_index], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file FILENAME1 {-f FILENAME2} (--big-endian|--little-endian) ", stderr);
            fputs("[--architecture name]\nArchitectures: knight-native, knight-posix, x86, amd64, armv7, riscv32 and riscv64\n", stderr);
            fputs("To leverage octal or binary output: --octal, --binary\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "-f") || match(argv[option_index], "--file"))
        {
            filename = argv[option_index + 1];
            source_file = fopen(filename, "r");

            if(NULL == source_file)
            {
                fputs("The file: ", stderr);
                fputs(argv[option_index + 1], stderr);
                fputs(" can not be opened!\n", stderr);
                exit(EXIT_FAILURE);
            }

            token_list = Tokenize_Line(token_list, filename);

            fclose(source_file);

            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-o") || match(argv[option_index], "--output"))
        {
            destination_file = fopen(argv[option_index + 1], "w");

            if(NULL == destination_file)
            {
                fputs("The file: ", stderr);
                fputs(argv[option_index + 1], stderr);
                fputs(" can not be opened!\n", stderr);
                exit(EXIT_FAILURE);
            }
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-O") || match(argv[option_index], "--octal"))
        {
            ByteMode = OCTAL;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-V") || match(argv[option_index], "--version"))
        {
            fputs("M1 1.4.0\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    if(NULL == token_list)
    {
        fputs("Either no input files were given or they were empty\n", stderr);
        exit(EXIT_FAILURE);
    }

    token_list = reverse_list(token_list);
    line_macro(token_list);
    process_string(blob_list);
    eval_immediates(blob_list);
    preserve_other(blob_list);
    print_hex(token_list);

    fclose(destination_file);

    return EXIT_SUCCESS;
}

https://github.com/oriansj/M2libc/blob/main/x86/ELF-x86-debug.hex2

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/ELF-x86-debug.hex2 used in: Step 28, Step 33, Step 38, Step 42, Step 46, Step 52, Step 56, Step 60, Step 64
### Copyright (C) 2016 Jeremiah Orians
### Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
### This file is part of M2-Planet.
###
### M2-Planet is free software: you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation, either version 3 of the License, or
### (at your option) any later version.
###
### M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.

### stage0's hex2 format
###    !<label>          1 byte relative
###    $<label>          2 byte address
###    @<label>          2 byte relative
###    &<label>          4 byte address
###    %<label>          4 byte relative

### if you wish to use this header, you need to add :ELF_end to the end of your
### M1 or hex2 files.

## ELF Header

:ELF_base
7F 45 4C 46                    # e_ident[EI_MAG0-3] ELF's magic number

01                             # e_ident[EI_CLASS] Indicating 32 bit
01                             # e_ident[EI_DATA] Indicating little endianness
01                             # e_ident[EI_VERSION] Indicating original elf

03                             # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                             # e_ident[EI_ABIVERSION] See above

00 00 00 00 00 00 00           # e_ident[EI_PAD]

02 00                          # e_type Indicating Executable
03 00                          # e_machine Indicating 386
01 00 00 00                    # e_version Indicating original elf

&_start                        # e_entry Address of the entry point
%ELF_program_headers>ELF_base  # e_phoff Address of program header table
%ELF_section_headers>ELF_base  # e_shoff Address of section header table

00 00 00 00                    # e_flags

34 00                          # e_ehsize Indicating our 52 Byte header

20 00                          # e_phentsize size of a program header table
01 00                          # e_phnum number of entries in program table

28 00                          # e_shentsize size of a section header table
05 00                          # e_shnum number of entries in section table

02 00                          # e_shstrndx index of the section names


:ELF_program_headers
:ELF_program_header__text
01 00 00 00                    # ph_type: PT-LOAD = 1
00 00 00 00                    # ph_offset
&ELF_base                      # ph_vaddr
&ELF_base                      # ph_physaddr
%ELF_end>ELF_base              # ph_filesz
%ELF_end>ELF_base              # ph_memsz
07 00 00 00                    # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                    # ph_alignment

:ELF_text

https://github.com/oriansj/M2libc/blob/main/sys/types.h

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/sys/types.h used in: Step 30, Step 35, Step 39, Step 43, Step 49, Step 53, Step 57, Step 61, Step 66, Step 67, Step 68, Step 69, Step 70, Step 71, Step 72, Step 73, Step 74, Step 75, Step 76, Step 79, Step 81
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SYS_TYPES_H
#define _SYS_TYPES_H

#ifndef __M2__
#include "../gcc_req.h"
#endif

typedef SCM ulong;
typedef long ssize_t;
typedef int pid_t;
typedef long intptr_t;
typedef ulong uintptr_t;
typedef long clock_t;
typedef int mode_t;
typedef long dev_t;
#endif

https://github.com/oriansj/M2libc/blob/main/stddef.h

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/stddef.h used in: Step 30, Step 35, Step 39, Step 43, Step 49, Step 53, Step 57, Step 61, Step 66, Step 67, Step 68, Step 69, Step 70, Step 71, Step 72, Step 73, Step 74, Step 75, Step 76, Step 79, Step 81
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _STDDEF_H
#define _STDDEF_H
#include <sys/types.h>

#define NULL 0
typedef long ptrdiff_t;
typedef ulong size_t;

#endif

https://github.com/oriansj/M2libc/blob/main/x86/linux/unistd.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/linux/unistd.c used in: Step 30, Step 35, Step 39, Step 43, Step 49, Step 53, Step 57, Step 61
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _UNISTD_C
#define _UNISTD_C

#define NULL 0
#define __PATH_MAX 4096

void* malloc(unsigned size);

int access(char* pathname, int mode)
{
    asm("lea_ebx,[esp+DWORD] %8"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %4"
        "mov_ecx,[ecx]"
        "mov_eax, %33"
        "int !0x80");
}

int chdir(char* path)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %12"
        "int !0x80");
}

int fchdir(int fd)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %133"
        "int !0x80");
}

/* Defined in the libc */
void _exit(int value);

int fork()
{
    asm("mov_eax, %2"
        "mov_ebx, %0"
        "int !0x80");
}


int waitpid (int pid, int* status_ptr, int options)
{
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %7"
        "int !0x80");
}


int execve(char* file_name, char** argv, char** envp)
{
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %11"
        "int !0x80");
}

int read(int fd, char* buf, unsigned count) {
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %3"
        "int !0x80");
}

int write(int fd, char* buf, unsigned count) {
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %4"
        "int !0x80");
}

int lseek(int fd, int offset, int whence)
{
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %19"
        "int !0x80");
}

int close(int fd)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %6"
        "int !0x80");
}


int unlink (char *filename)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %10"
        "int !0x80");
}


int _getcwd(char* buf, int size)
{
    asm("lea_ebx,[esp+DWORD] %8"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %4"
        "mov_ecx,[ecx]"
        "mov_eax, %183"
        "int !0x80");
}


char* getcwd(char* buf, unsigned size)
{
    int c = _getcwd(buf, size);
    if(0 == c) return NULL;
    return buf;
}


char* getwd(char* buf)
{
    return getcwd(buf, __PATH_MAX);
}


char* get_current_dir_name()
{
    return getcwd(malloc(__PATH_MAX), __PATH_MAX);
}


int brk(void *addr)
{
    asm("mov_eax,[esp+DWORD] %4"
        "push_eax"
        "mov_eax, %45"
        "pop_ebx"
        "int !0x80");
}

struct utsname
{
    char sysname[65];    /* Operating system name (e.g., "Linux") */
    char nodename[65];   /* Name within "some implementation-defined network" */
    char release[65];    /* Operating system release (e.g., "2.6.28") */
    char version[65];    /* Operating system version */
    char machine[65];    /* Hardware identifier */
};

int uname(struct utsname* unameData)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %109"
        "int !0x80");
}

#endif

https://github.com/oriansj/M2libc/blob/main/x86/linux/fcntl.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/x86/linux/fcntl.c used in: Step 30, Step 35, Step 39, Step 43, Step 49, Step 53, Step 57, Step 61
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __FCNTL_C
#define __FCNTL_C

#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 00100
#define O_EXCL 00200
#define O_TRUNC 001000
#define O_APPEND 002000

#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_IRWXU 00700


int _open(char* name, int flag, int mode)
{
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %5"
        "int !0x80");
}

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif

https://github.com/oriansj/M2libc/blob/main/fcntl.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/fcntl.c used in: Step 30, Step 35, Step 39, Step 43, Step 49, Step 53, Step 57, Step 61, Step 66, Step 67, Step 68, Step 69, Step 70, Step 71, Step 72, Step 73, Step 74, Step 75, Step 76, Step 79, Step 81
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _FCNTL_C
#define _FCNTL_C

#ifdef __M2__
#if __uefi__
#include <uefi/fcntl.c>
#elif __i386__
#include <x86/linux/fcntl.c>
#elif __x86_64__
#include <amd64/linux/fcntl.c>
#elif __arm__
#include <armv7l/linux/fcntl.c>
#elif __aarch64__
#include <aarch64/linux/fcntl.c>
#elif __riscv && __riscv_xlen==32
#include <riscv32/linux/fcntl.c>
#elif __riscv && __riscv_xlen==64
#include <riscv64/linux/fcntl.c>
#elif __knight_posix__
#include <knight/linux/fcntl.c>
#elif __knight__
#include <knight/native/fcntl.c>
#else
#error arch not supported
#endif
#else
extern int _open(char* name, int flag, int mode);
#endif

int errno;

int open(char* name, int flag, int mode)
{
    int fd = _open(name, flag, mode);
    if(0 > fd)
    {
        errno = -fd;
        fd = -1;
    }
    return fd;
}

#endif

https://github.com/oriansj/M2libc/blob/main/x86/linux/sys/stat.c

Files:
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SYS_STAT_C
#define _SYS_STAT_C

#include <sys/types.h>

#define S_IRWXU 00700
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400

#define S_ISUID 04000
#define S_ISGID 02000
#define S_IXGRP 00010
#define S_IXOTH 00001
#define S_IRGRP 00040
#define S_IROTH 00004
#define S_IWGRP 00020
#define S_IWOTH 00002
#define S_IRWXG 00070
#define S_IRWXO 00007


int chmod(char *pathname, int mode)
{
    asm("lea_ebx,[esp+DWORD] %8"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %4"
        "mov_ecx,[ecx]"
        "mov_eax, %15"
        "int !0x80");
}


int fchmod(int a, mode_t b)
{
    asm("lea_ebx,[esp+DWORD] %8"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %4"
        "mov_ecx,[ecx]"
        "mov_eax, %94"
        "int !0x80");
}


int mkdir(char const* a, mode_t b)
{
    asm("lea_ebx,[esp+DWORD] %8"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %4"
        "mov_ecx,[ecx]"
        "mov_eax, %39"
        "int !0x80");
}


int mknod(char const* a, mode_t b, dev_t c)
{
    asm("lea_ebx,[esp+DWORD] %12"
        "mov_ebx,[ebx]"
        "lea_ecx,[esp+DWORD] %8"
        "mov_ecx,[ecx]"
        "lea_edx,[esp+DWORD] %4"
        "mov_edx,[edx]"
        "mov_eax, %14"
        "int !0x80");
}


mode_t umask(mode_t m)
{
    asm("lea_ebx,[esp+DWORD] %4"
        "mov_ebx,[ebx]"
        "mov_eax, %60"
        "int !0x80");
}

#endif

https://github.com/oriansj/M2libc/blob/main/stdlib.c

Files:
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>

#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0

#define _IN_USE 1
#define _NOT_IN_USE 0

typedef char wchar_t;

void exit(int value);

struct _malloc_node
{
    struct _malloc_node *next;
    void* block;
    size_t size;
    int used;
};

struct _malloc_node* _allocated_list;
struct _malloc_node* _free_list;

/********************************
 * The core POSIX malloc        *
 ********************************/
long _malloc_ptr;
long _brk_ptr;
void* _malloc_brk(unsigned size)
{
    if(NULL == _brk_ptr)
    {
        _brk_ptr = brk(0);
        _malloc_ptr = _brk_ptr;
    }

    if(_brk_ptr < _malloc_ptr + size)
    {
        _brk_ptr = brk(_malloc_ptr + size);
        if(-1 == _brk_ptr) return 0;
    }

    long old_malloc = _malloc_ptr;
    _malloc_ptr = _malloc_ptr + size;
    return old_malloc;
}

void __init_malloc()
{
    _free_list = NULL;
    _allocated_list = NULL;
    return;
}

/************************************************************************
 * Handle with the tricky insert behaviors for our nodes                *
 * As free lists must be sorted from smallest to biggest to enable      *
 * cheap first fit logic                                                *
 * The free function however is rarely called, so it can kick sand and  *
 * do things the hard way                                               *
 ************************************************************************/
void _malloc_insert_block(struct _malloc_node* n, int used)
{
    /* Allocated block doesn't care about order */
    if(_IN_USE == used)
    {
        /* Literally just be done as fast as possible */
        n->next = _allocated_list;
        _allocated_list = n;
        return;
    }

    /* sanity check garbage */
    if(_NOT_IN_USE != used) exit(EXIT_FAILURE);
    if(_NOT_IN_USE != n->used) exit(EXIT_FAILURE);
    if(NULL != n->next) exit(EXIT_FAILURE);

    /* Free block really does care about order */
    struct _malloc_node* i = _free_list;
    struct _malloc_node* last = NULL;
    while(NULL != i)
    {
        /* sort smallest to largest */
        if(n->size <= i->size)
        {
            /* Connect */
            n->next = i;
            /* If smallest yet */
            if(NULL == last) _free_list = n;
            /* or just another average block */
            else last->next = n;
            return;
        }

        /* iterate */
        last = i;
        i = i->next;
    }

    /* looks like we are the only one */
    if(NULL == last) _free_list = n;
    /* or we are the biggest yet */
    else last->next = n;
}

/************************************************************************
 * We only mark a block as unused, we don't actually deallocate it here *
 * But rather shove it into our _free_list                              *
 ************************************************************************/
void free(void* ptr)
{
/* just in case someone needs to quickly turn it off */
#ifndef _MALLOC_DISABLE_FREE
    struct _malloc_node* i = _allocated_list;
    struct _malloc_node* last = NULL;

    /* walk the whole freaking list if needed to do so */
    while(NULL != i)
    {
        /* did we find it? */
        if(i->block == ptr)
        {
            /* detach the block */
            if(NULL == last) _allocated_list = i->next;
            /* in a way that doesn't break the allocated list */
            else last->next = i->next;

            /* insert into free'd list */
            i->used = _NOT_IN_USE;
            i->next = NULL;
            _malloc_insert_block(i, _NOT_IN_USE);
            return;
        }

        /* iterate */
        last = i;
        i = i->next;
    }

    /* we received a pointer to a block that wasn't allocated */
    /* Bail *HARD* because I don't want to cover this edge case */
    exit(EXIT_FAILURE);
#endif
    /* if free is disabled, there is nothing to do */
    return;
}

/************************************************************************
 * find if there is any "FREED" blocks big enough to sit on our memory  *
 * budget's face and ruin its life. Respectfully of course              *
 ************************************************************************/
void* _malloc_find_free(unsigned size)
{
    struct _malloc_node* i = _free_list;
    struct _malloc_node* last = NULL;
    /* Walk the whole list if need be */
    while(NULL != i)
    {
        /* see if anything in it is equal or bigger than what I need */
        if((_NOT_IN_USE == i->used) && (i->size > size))
        {
            /* disconnect from list ensuring we don't break free doing so */
            if(NULL == last) _free_list = i->next;
            else last->next = i->next;

            /* insert into allocated list */
            i->used = _IN_USE;
            i->next = NULL;
            _malloc_insert_block(i, _IN_USE);
            return i->block;
        }

        /* iterate (will loop forever if you get this wrong) */
        last = i;
        i = i->next;
    }

    /* Couldn't find anything big enough */
    return NULL;
}

/************************************************************************
 * Well we couldn't find any memory good enough to satisfy our needs so *
 * we are going to have to go beg for some memory on the street corner  *
 ************************************************************************/
void* _malloc_add_new(unsigned size)
{
    struct _malloc_node* n;
#ifdef __uefi__
    n = _malloc_uefi(sizeof(struct _malloc_node));
    /* Check if we were beaten */
    if(NULL == n) return NULL;
    n->block = _malloc_uefi(size);
#else
    n = _malloc_brk(sizeof(struct _malloc_node));
    /* Check if we were beaten */
    if(NULL == n) return NULL;
    n->block = _malloc_brk(size);
#endif
    /* check if we were robbed */
    if(NULL == n->block) return NULL;

    /* Looks like we made it home safely */
    n->size = size;
    n->next = NULL;
    n->used = _IN_USE;
    /* lets pop the cork and party */
    _malloc_insert_block(n, _IN_USE);
    return n->block;
}

/************************************************************************
 * Safely iterates over all malloc nodes and frees them                 *
 ************************************************************************/
void __malloc_node_iter(struct _malloc_node* node, FUNCTION _free)
{
    struct _malloc_node* current;
    while(node != NULL)
    {
        current = node;
        node = node->next;
        _free(current->block);
        _free(current);
    }
}

/************************************************************************
 * Runs a callback with all previously allocated nodes.                 *
 * This can be useful if operating system does not do any clean up.     *
 ************************************************************************/
void* _malloc_release_all(FUNCTION _free)
{
    __malloc_node_iter(_allocated_list, _free);
    __malloc_node_iter(_free_list, _free);
}

/************************************************************************
 * Provide a POSIX standardish malloc function to keep things working   *
 ************************************************************************/
void* malloc(unsigned size)
{
    /* skip allocating nothing */
    if(0 == size) return NULL;

    /* use one of the standard block sizes */
    size_t max = 1 << 30;
    size_t used = 256;
    while(used < size)
    {
        used = used << 1;

        /* fail big allocations */
        if(used > max) return NULL;
    }

    /* try the cabinets around the house */
    void* ptr = _malloc_find_free(used);

    /* looks like we need to get some more from the street corner */
    if(NULL == ptr)
    {
        ptr = _malloc_add_new(used);
    }

    /* hopefully you can handle NULL pointers, good luck */
    return ptr;
}

/************************************************************************
 * Provide a POSIX standardish memset function to keep things working   *
 ************************************************************************/
void* memset(void* ptr, int value, int num)
{
    char* s;
    /* basically walk the block 1 byte at a time and set it to any value you want */
    for(s = ptr; 0 < num; num = num - 1)
    {
        s[0] = value;
        s = s + 1;
    }

    return ptr;
}

/************************************************************************
 * Provide a POSIX standardish calloc function to keep things working   *
 ************************************************************************/
void* calloc(int count, int size)
{
    /* if things get allocated, we are good*/
    void* ret = malloc(count * size);
    /* otherwise good luck */
    if(NULL == ret) return NULL;
    memset(ret, 0, (count * size));
    return ret;
}


/* USED EXCLUSIVELY BY MKSTEMP */
void __set_name(char* s, int i)
{
    s[5] = '0' + (i % 10);
    i = i / 10;
    s[4] = '0' + (i % 10);
    i = i / 10;
    s[3] = '0' + (i % 10);
    i = i / 10;
    s[2] = '0' + (i % 10);
    i = i / 10;
    s[1] = '0' + (i % 10);
    i = i / 10;
    s[0] = '0' + i;
}

/************************************************************************
 * Provide a POSIX standardish mkstemp function to keep things working  *
 ************************************************************************/
int mkstemp(char *template)
{
    /* get length of template */
    int i = 0;
    while(0 != template[i]) i = i + 1;
    i = i - 1;

    /* String MUST be more than 6 characters in length */
    if(i < 6) return -1;

    /* Sanity check the string matches the template requirements */
    int count = 6;
    int c;
    while(count > 0)
    {
        c = template[i];
        /* last 6 chars must be X */
        if('X' != c) return -1;
        template[i] = '0';
        i = i - 1;
        count = count - 1;
    }

    int fd = -1;
    count = -1;
    /* open will return -17 or other values */
    while(0 > fd)
    {
        /* Just give up after the planet has blown up */
        if(9000 < count) return -1;

        /* Try up to 9000 unique filenames before stopping */
        count = count + 1;
        __set_name(template+i+1, count);

        /* Pray we can */
        fd = open(template, O_RDWR | O_CREAT | O_EXCL, 00600);
    }

    /* well that only took count many tries */
    return fd;
}

/************************************************************************
 * wcstombs - convert a wide-character string to a multibyte string     *
 * because seriously UEFI??? UTF-16 is a bad design choice but I guess  *
 * they were drinking pretty hard when they designed UEFI; it is DOS    *
 * but somehow they magically found ways of making it worse             *
 ************************************************************************/
size_t wcstombs(char* dest, char* src, size_t n)
{
    int i = 0;

    do
    {
        /* UTF-16 is 2bytes per char and that first byte maps good enough to ASCII */
        dest[i] = src[2 * i];
        if(dest[i] == 0)
        {
            break;
        }
        i = i + 1;
        n = n - 1;
    } while (n != 0);

    return i;
}

/************************************************************************
 * getenv - get an environmental variable                               *
 ************************************************************************/
size_t _strlen(char const* str)
{
    size_t i = 0;
    while(0 != str[i]) i = i + 1;
    return i;
}
int _strncmp(char const* lhs, char const* rhs, size_t count)
{
    size_t i = 0;
    while(count > i)
    {
        if(0 == lhs[i]) break;
        if(lhs[i] != rhs[i]) return lhs[i] - rhs[i];
        i = i + 1;
    }

    return 0;
}
char** _envp;
char* getenv (char const* name)
{
    char** p = _envp;
    char* q;
    int length = _strlen(name);

    while (p[0] != 0)
    {
        if(_strncmp(name, p[0], length) == 0)
        {
            q = p[0] + length;
            if(q[0] == '=')
                return q + 1;
        }
        p += sizeof(char**); /* M2 pointer arithemtic */
    }

    return 0;
}

/************************************************************************
 * setenv - set an environmental variable                               *
 ************************************************************************/
char* _strcpy(char* dest, char const* src)
{
    int i = 0;

    while (0 != src[i])
    {
        dest[i] = src[i];
        i = i + 1;
    }
    dest[i] = 0;

    return dest;
}

int setenv(char const *s, char const *v, int overwrite_p)
{
    char** p = _envp;
    int length = _strlen(s);
    char* q;

    while (p[0] != 0)
    {
        if (_strncmp (s, p[0], length) == 0)
        {
            q = p[0] + length;
            if (q[0] == '=')
                break;
        }
        p += sizeof(char**); /* M2 pointer arithemtic */
    }
    char *entry = malloc (length + _strlen(v) + 2);
    int end_p = p[0] == 0;
    p[0] = entry;
    _strcpy(entry, s);
    _strcpy(entry + length, "=");
    _strcpy(entry + length + 1, v);
    entry[length + _strlen(v) + 2] = 0;
    if (end_p != 0)
        p[1] = 0;

    return 0;
}

https://github.com/oriansj/M2libc/blob/main/stdio.h

Files:
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _STDIO_H
#define _STDIO_H

#ifdef __M2__

/* Actual format of FILE */
struct __IO_FILE
{
    int fd;
    int bufmode; /* O_RDONLY = 0, O_WRONLY = 1 */
    int bufpos;
    int file_pos;
    int buflen;
    char* buffer;
    struct __IO_FILE* next;
    struct __IO_FILE* prev;
};

/* Now give us the FILE we all love */
typedef struct __IO_FILE FILE;

#include <stdio.c>
#else

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

/* Required constants */
/* For file I/O*/
#define EOF -1
#define BUFSIZ 4096

/* For lseek */
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2

/* Actual format of FILE */
struct __IO_FILE
{
    int fd;
    int bufmode; /* 0 = no buffer, 1 = read, 2 = write */
    int bufpos;
    int buflen;
    char* buffer;
};

/* Now give us the FILE we all love */
typedef struct __IO_FILE FILE;

/* Required variables */
extern FILE* stdin;
extern FILE* stdout;
extern FILE* stderr;

/* Standard C functions */
/* Getting */
extern int fgetc(FILE* f);
extern int getchar();
extern char* fgets(char* str, int count, FILE* stream);
extern size_t fread( void* buffer, size_t size, size_t count, FILE* stream );

/* Putting */
extern void fputc(char s, FILE* f);
extern void putchar(char s);
extern int fputs(char const* str, FILE* stream);
extern int puts(char const* str);
extern size_t fwrite(void const* buffer, size_t size, size_t count, FILE* stream );

/* File management */
extern FILE* fopen(char const* filename, char const* mode);
extern int fclose(FILE* stream);
extern int fflush(FILE* stream);

/* File Positioning */
extern int ungetc(int ch, FILE* stream);
extern long ftell(FILE* stream);
extern int fseek(FILE* f, long offset, int whence);
extern void rewind(FILE* f);
#endif
#endif

https://github.com/oriansj/M2libc/blob/main/stdio.c

Files:
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

/* Required constants */
/* For file I/O*/
#define EOF 0xFFFFFFFF
#define BUFSIZ 0x1000

/* For lseek */
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2

/* Required variables */
FILE* stdin;
FILE* stdout;
FILE* stderr;
FILE* __list;

void __init_io()
{
    __list = NULL;
    stdin = calloc(1, sizeof(FILE));
    stdin->fd = STDIN_FILENO;
    stdin->bufmode = O_RDONLY;
    stdin->buflen = 1;
    stdin->buffer = calloc(2, sizeof(char));

    stdout = calloc(1, sizeof(FILE));
    stdout->fd = STDOUT_FILENO;
    stdout->bufmode = O_WRONLY;
    stdout->buflen = 512;
    stdout->buffer = calloc(514, sizeof(char));

    stderr = calloc(1, sizeof(FILE));
    stderr->fd = STDERR_FILENO;
    stderr->bufmode = O_WRONLY;
    stderr->buflen = 512;
    stderr->buffer = calloc(514, sizeof(char));
}


/* Flush all IO on exit */
int fflush(FILE* stream);
void __kill_io()
{
    fflush(stdout);
    fflush(stderr);
    while(NULL != __list)
    {
        fflush(__list);
        __list = __list->next;
    }
}

/* Standard C functions */
/* Getting */
int read(int fd, char* buf, unsigned count);
int fgetc(FILE* f)
{
    /* Only read on read buffers */
    if(O_WRONLY == f->bufmode) return EOF;

    /* Deal with stdin */
    if(STDIN_FILENO == f->fd)
    {
        f->bufpos = 0;
        int r = read(f->fd, f->buffer, 1);

        /* Catch special case of STDIN gets nothing (AN EOF) */
        if(0 == r) return EOF;
    }

    /* Catch EOF */
    if(f->buflen <= f->bufpos) return EOF;

    /* Deal with standard case */
    int ret = f->buffer[f->bufpos];
    f->bufpos = f->bufpos + 1;

    /* Ensure 0xFF doesn't return EOF */
    return (ret & 0xFF);
}

size_t fread( void* buffer, size_t size, size_t count, FILE* stream )
{
    if(0 == size) return 0;
    if(0 == count) return 0;

    long n = size + count - 1;
    char* p = buffer;
    long i;
    unsigned c;
    for(i = 0; i < n; i = i + 1)
    {
        c = fgetc(stream);
        if(EOF == c) return (i/size);
        p[i] = c;
    }

    return (i/size);
}

int getchar()
{
    return fgetc(stdin);
}


char* fgets(char* str, int count, FILE* stream)
{
    int i = 0;
    int ch;
    while(i < count)
    {
        ch = fgetc(stream);
        if(EOF == ch) break;

        str[i] = ch;
        i = i + 1;

        if('\n' == ch) break;
    }

    return str;
}

/* Putting */
void fputc(char s, FILE* f)
{
    /* Only write on write buffers */
    if(O_RDONLY == f->bufmode) return;

    /* Add to buffer */
    f->buffer[f->bufpos] = s;
    f->bufpos = f->bufpos + 1;

    /* Flush if full or '\n' */
    if(f->bufpos == f->buflen) fflush(f);
    else if(('\n' == s) && (2 >= f->fd)) fflush(f);
}

size_t fwrite(void const* buffer, size_t size, size_t count, FILE* stream )
{
    long n = size * count;
    if(0 == n) return 0;

    char* p = buffer;
    int c;
    long i;
    for(i=0; i < n; i = i + 1)
    {
        c = p[i];
        fputc(c, stream);
    }

    return (i/size);
}

void putchar(char s)
{
    fputc(s, stdout);
}


int fputs(char const* str, FILE* stream)
{
    while(0 != str[0])
    {
        fputc(str[0], stream);
        str = str + 1;
    }
    return 0;
}


int puts(char const* str)
{
    fputs(str, stdout);
    fputc('\n', stdout);
    return 0;
}


int lseek(int fd, int offset, int whence);
/* File management */
FILE* fopen(char const* filename, char const* mode)
{
    int f;
    FILE* fi = calloc(1, sizeof(FILE));
    fi->next = __list;
    if(NULL != __list) __list->prev = fi;
    __list = fi;
    int size;

    if('w' == mode[0]) f = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 00600);
    else f = open(filename, 0, 0); /* Everything else is a read */

    /* Negative numbers are error codes */
    if(0 > f)
    {
        return 0;
    }

    if('w' == mode[0])
    {
        /* Buffer as much as possible */
        fi->buffer = malloc(BUFSIZ * sizeof(char));
        fi->buflen = BUFSIZ;
        fi->bufmode = O_WRONLY;
    }
    else
    {
        /* Get enough buffer to read it all */
        size = lseek(f, 0, SEEK_END);
        fi->buffer = malloc((size + 1) * sizeof(char));
        fi->buflen = size;
        fi->bufmode = O_RDONLY;

        /* Now read it all */
        lseek(f, 0, SEEK_SET);
        read(f, fi->buffer, size);
    }

    fi->fd = f;
    return fi;
}

FILE* fdopen(int fd, char* mode)
{
    FILE* fi = calloc(1, sizeof(FILE));
    fi->next = __list;
    if(NULL != __list) __list->prev = fi;
    __list = fi;
    int size;

    if('w' == mode[0])
    {
        /* Buffer as much as possible */
        fi->buffer = malloc(BUFSIZ * sizeof(char));
        fi->buflen = BUFSIZ;
        fi->bufmode = O_WRONLY;
    }
    else
    {
        /* Get enough buffer to read it all */
        size = lseek(fd, 0, SEEK_END);
        fi->buffer = malloc((size + 1) * sizeof(char));
        fi->buflen = size;
        fi->bufmode = O_RDONLY;

        /* Now read it all */
        lseek(fd, 0, SEEK_SET);
        read(fd, fi->buffer, size);
    }

    fi->fd = fd;
    return fi;
}


int write(int fd, char* buf, unsigned count);
int fflush(FILE* stream)
{
    /* We only need to flush on writes */
    if(O_RDONLY == stream->bufmode) return 0;

    /* If nothing to flush */
    if(0 ==stream->bufpos) return 0;

    /* The actual flushing */
    int error = write(stream->fd, stream->buffer, stream->bufpos);

    /* Keep track of position */
    stream->file_pos = stream->file_pos + stream->bufpos;
    stream->bufpos = 0;

    return error;
}


int close(int fd);
int fclose(FILE* stream)
{
    /* Deal with STDIN, STDOUT and STDERR */
    /* No close for you */
    if(2 >= stream->fd) return 0;

    /* We only need to flush on writes */
    if(O_WRONLY == stream->bufmode)
    {
        fflush(stream);
    }

    /* Need to keep the File Descriptor for a moment */
    int fd = stream->fd;

    /* Remove from __list */
    if(NULL != stream->prev) stream->prev->next = stream->next;
    if(NULL != stream->next) stream->next->prev = stream->prev;
    /* Deal with special case of first node in __list */
    if (__list == stream) __list = __list->next;

    /* Free up the buffer and struct used for FILE */
    free(stream->buffer);
    free(stream);

    /* Do the actual closing */
    return close(fd);
}


int unlink(char* filename);
/* File Removal */
int remove(char *pathname)
{
    return unlink(pathname);
}


/* File Positioning */
int ungetc(int ch, FILE* stream)
{
    /* Deal with STDIN, STDOUT and STDERR */
    /* No ungetc for you */
    if(2 >= stream->fd) return EOF;

    /* You can't unget on a write stream! */
    if(O_WRONLY == stream->bufmode) return EOF;

    /* Don't underflow */
    if(0 == stream->bufpos) return EOF;

    /* Don't let crap be shoved into read stream */
    if(stream->buffer[stream->bufpos - 1] != ch) return EOF;

    stream->bufpos = stream->bufpos - 1;

    return ch;
}


long ftell(FILE* stream)
{
    /* Deal with STDIN, STDOUT and STDERR */
    /* No ftell for you */
    if(2 >= stream->fd) return 0;

    /* Deal with buffered output */
    if(O_WRONLY == stream->bufmode) return stream->file_pos + stream->bufpos;

    /* Deal with read */
    return stream->bufpos;
}


int fseek(FILE* f, long offset, int whence)
{
    /* Deal with STDIN, STDOUT and STDERR */
    /* No seek and destroy missions */
    if(2 >= f->fd) return 0;

    /* Deal with ugly case */
    if(O_WRONLY == f->bufmode)
    {
        fflush(f);
        return lseek(f->fd, offset, whence);
    }

    /* Deal with read mode */
    int pos;

    if(SEEK_SET == whence)
    {
        pos = offset;
    }
    else if(SEEK_CUR == whence)
    {
        pos = f->bufpos + offset;
    }
    else if(SEEK_END == whence)
    {
        pos = f->buflen + offset;
    }
    else return -1;

    if(pos < 0) return -1;
    if(pos > f->buflen) return -1;

    f->bufpos = pos;
    return pos;
}


void rewind(FILE* f)
{
    fseek(f, 0, SEEK_SET);
}

https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=hex2.h

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/hex2.h used in: Step 30, Step 39
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "M2libc/bootstrappable.h"

#define max_string 4096
#define TRUE 1
#define FALSE 0

#define KNIGHT 0
#define X86 0x03
#define AMD64 0x3E
#define ARMV7L 0x28
#define AARM64 0xB7
#define PPC64LE 0x15
#define RISCV32 0xF3
#define RISCV64 0x100F3 /* Because RISC-V unlike all other architectures does get a seperate e_machine when changing from 32 to 64bit */

#define HEX 16
#define OCTAL 8
#define BINARY 2


struct input_files
{
    struct input_files* next;
    char* filename;
};

struct entry
{
    struct entry* next;
    unsigned target;
    char* name;
};

https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=hex2_linker.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/hex2_linker.c used in: Step 30, Step 39
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "hex2_globals.h"

/* Globals */
FILE* output;
struct entry** jump_tables;
int BigEndian;
int Base_Address;
int Architecture;
int ByteMode;
int exec_enable;
int ip;
char* scratch;
char* filename;
int linenumber;
int ALIGNED;

/* For processing bytes */
int hold;
int toggle;

void line_error()
{
    fputs(filename, stderr);
    fputs(":", stderr);
    fputs(int2str(linenumber, 10, FALSE), stderr);
    fputs(" :", stderr);
}

int consume_token(FILE* source_file)
{
    int i = 0;
    int c = fgetc(source_file);
    while(!in_set(c, " \t\n>"))
    {
        scratch[i] = c;
        i = i + 1;
        c = fgetc(source_file);
        require(max_string > i, "Consumed token exceeds length restriction\n");
        if(EOF == c) break;
    }

    return c;
}

int Throwaway_token(FILE* source_file)
{
    int c;
    do
    {
        c = fgetc(source_file);
        if(EOF == c) break;
    } while(!in_set(c, " \t\n>"));

    return c;
}

int length(char* s)
{
    int i = 0;
    while(0 != s[i]) i = i + 1;
    return i;
}

void Clear_Scratch(char* s)
{
    do
    {
        s[0] = 0;
        s = s + 1;
    } while(0 != s[0]);
}

void Copy_String(char* a, char* b)
{
    while(0 != a[0])
    {
        b[0] = a[0];
        a = a + 1;
        b = b + 1;
    }
}

int GetHash(char* s)
{
    int i = 5381;
    while(0 != s[0])
    {
        i = i * 31 + s[0];
        s = s + 1;
    }
    return (i & 0xFFFF);
}

unsigned GetTarget(char* c)
{
    struct entry* i;
    for(i = jump_tables[GetHash(c)]; NULL != i; i = i->next)
    {
        if(match(c, i->name))
        {
            return i->target;
        }
    }
    fputs("Target label ", stderr);
    fputs(c, stderr);
    fputs(" is not valid\n", stderr);
    exit(EXIT_FAILURE);
}

int storeLabel(FILE* source_file, int ip)
{
    struct entry* entry = calloc(1, sizeof(struct entry));
    require(NULL != entry, "failed to allocate entry\n");

    /* Ensure we have target address */
    entry->target = ip;

    /* Store string */
    int c = consume_token(source_file);
    entry->name = calloc(length(scratch) + 1, sizeof(char));
    require(NULL != entry->name, "failed to allocate entry->name\n");
    Copy_String(scratch, entry->name);
    Clear_Scratch(scratch);

    /* Prepend to list */
    int h = GetHash(entry->name);
    entry->next = jump_tables[h];
    jump_tables[h] = entry;

    return c;
}

void range_check(int displacement, int number_of_bytes)
{
    if(4 == number_of_bytes) return;
    else if (3 == number_of_bytes)
    {
        if((8388607 < displacement) || (displacement < -8388608))
        {
            fputs("A displacement of ", stderr);
            fputs(int2str(displacement, 10, TRUE), stderr);
            fputs(" does not fit in 3 bytes\n", stderr);
            exit(EXIT_FAILURE);
        }
        return;
    }
    else if (2 == number_of_bytes)
    {
        if((32767 < displacement) || (displacement < -32768))
        {
            fputs("A displacement of ", stderr);
            fputs(int2str(displacement, 10, TRUE), stderr);
            fputs(" does not fit in 2 bytes\n", stderr);
            exit(EXIT_FAILURE);
        }
        return;
    }
    else if (1 == number_of_bytes)
    {
        if((127 < displacement) || (displacement < -128))
        {
            fputs("A displacement of ", stderr);
            fputs(int2str(displacement, 10, TRUE), stderr);
            fputs(" does not fit in 1 byte\n", stderr);
            exit(EXIT_FAILURE);
        }
        return;
    }

    fputs("Invalid number of bytes given\n", stderr);
    exit(EXIT_FAILURE);
}

void outputPointer(int displacement, int number_of_bytes)
{
    unsigned value = displacement;

    /* HALT HARD if we are going to do something BAD*/
    range_check(displacement, number_of_bytes);

    if(BigEndian)
    { /* Deal with BigEndian */
        if(4 == number_of_bytes) fputc((value >> 24), output);
        if(3 <= number_of_bytes) fputc(((value >> 16)%256), output);
        if(2 <= number_of_bytes) fputc(((value >> 8)%256), output);
        if(1 <= number_of_bytes) fputc((value % 256), output);
    }
    else
    { /* Deal with LittleEndian */
        unsigned byte;
        while(number_of_bytes > 0)
        {
            byte = value % 256;
            value = value / 256;
            fputc(byte, output);
            number_of_bytes = number_of_bytes - 1;
        }
    }
}

int Architectural_displacement(int target, int base)
{
    if(KNIGHT == Architecture) return (target - base);
    else if(X86 == Architecture) return (target - base);
    else if(AMD64 == Architecture) return (target - base);
    else if(ALIGNED && (ARMV7L == Architecture))
    {
        ALIGNED = FALSE;
        /* Note: Branch displacements on ARM are in number of instructions to skip, basically. */
        if (target & 3)
        {
            line_error();
            fputs("error: Unaligned branch target: ", stderr);
            fputs(scratch, stderr);
            fputs(", aborting\n", stderr);
            exit(EXIT_FAILURE);
        }
        /*
         * The "fetch" stage already moved forward by 8 from the
         * beginning of the instruction because it is already
         * prefetching the next instruction.
         * Compensate for it by subtracting the space for
         * two instructions (including the branch instruction).
         * and the size of the aligned immediate.
         */
        return (((target - base + (base & 3)) >> 2) - 2);
    }
    else if(ARMV7L == Architecture)
    {
        /*
         * The size of the offset is 8 according to the spec but that value is
         * based on the end of the immediate, which the documentation gets wrong
         * and needs to be adjusted to the size of the immediate.
         * Eg 1byte immediate => -8 + 1 = -7
         */
        return ((target - base) - 8 + (3 & base));
    }
    else if(ALIGNED && (AARM64 == Architecture))
    {
            ALIGNED = FALSE;
            return (target - (~3 & base)) >> 2;
    }
    else if (AARM64 == Architecture)
    {
        return ((target - base) - 8 + (3 & base));
    }
    else if(ALIGNED && (PPC64LE == Architecture))
    {
        ALIGNED = FALSE;
        /* set Link register with branch */
        return (target - (base & 0xFFFFFFFC )) | 1;
    }
    else if(PPC64LE == Architecture)
    {
        /* DO *NOT* set link register with branch */
        return (target - (base & 0xFFFFFFFC));
    }
    else if(RISCV32 == Architecture || RISCV64 == Architecture) return (target - base);

    fputs("Unknown Architecture, aborting before harm is done\n", stderr);
    exit(EXIT_FAILURE);
}

void Update_Pointer(char ch)
{
    /* Calculate pointer size*/
    if(in_set(ch, "%&")) ip = ip + 4; /* Deal with % and & */
    else if(in_set(ch, "@$")) ip = ip + 2; /* Deal with @ and $ */
    else if('~' == ch) ip = ip + 3; /* Deal with ~ */
    else if('!' == ch) ip = ip + 1; /* Deal with ! */
    else
    {
        line_error();
        fputs("storePointer given unknown\n", stderr);
        exit(EXIT_FAILURE);
    }
}

void storePointer(char ch, FILE* source_file)
{
    /* Get string of pointer */
    Clear_Scratch(scratch);
    Update_Pointer(ch);
    int base_sep_p = consume_token(source_file);

    /* Lookup token */
    int target = GetTarget(scratch);
    int displacement;

    int base = ip;

    /* Change relative base address to :<base> */
    if ('>' == base_sep_p)
    {
        Clear_Scratch(scratch);
        consume_token (source_file);
        base = GetTarget (scratch);

        /* Force universality of behavior */
        displacement = (target - base);
    }
    else
    {
        displacement = Architectural_displacement(target, base);
    }

    /* output calculated difference */
    if('!' == ch) outputPointer(displacement, 1); /* Deal with ! */
    else if('$' == ch) outputPointer(target, 2); /* Deal with $ */
    else if('@' == ch) outputPointer(displacement, 2); /* Deal with @ */
    else if('~' == ch) outputPointer(displacement, 3); /* Deal with ~ */
    else if('&' == ch) outputPointer(target, 4); /* Deal with & */
    else if('%' == ch) outputPointer(displacement, 4);  /* Deal with % */
    else
    {
        line_error();
        fputs("error: storePointer reached impossible case: ch=", stderr);
        fputc(ch, stderr);
        fputs("\n", stderr);
        exit(EXIT_FAILURE);
    }
}

void line_Comment(FILE* source_file)
{
    int c = fgetc(source_file);
    while(!in_set(c, "\n\r"))
    {
        if(EOF == c) break;
        c = fgetc(source_file);
    }
    linenumber = linenumber + 1;
}

int hex(int c, FILE* source_file)
{
    if (in_set(c, "0123456789")) return (c - 48);
    else if (in_set(c, "abcdef")) return (c - 87);
    else if (in_set(c, "ABCDEF")) return (c - 55);
    else if (in_set(c, "#;")) line_Comment(source_file);
    else if ('\n' == c) linenumber = linenumber + 1;
    return -1;
}

int octal(int c, FILE* source_file)
{
    if (in_set(c, "01234567")) return (c - 48);
    else if (in_set(c, "#;")) line_Comment(source_file);
    else if ('\n' == c) linenumber = linenumber + 1;
    return -1;
}

int binary(int c, FILE* source_file)
{
    if (in_set(c, "01")) return (c - 48);
    else if (in_set(c, "#;")) line_Comment(source_file);
    else if ('\n' == c) linenumber = linenumber + 1;
    return -1;
}

void process_byte(char c, FILE* source_file, int write)
{
    if(HEX == ByteMode)
    {
        if(0 <= hex(c, source_file))
        {
            if(toggle)
            {
                if(write) fputc(((hold * 16)) + hex(c, source_file), output);
                ip = ip + 1;
                hold = 0;
            }
            else
            {
                hold = hex(c, source_file);
            }
            toggle = !toggle;
        }
    }
    else if(OCTAL ==ByteMode)
    {
        if(0 <= octal(c, source_file))
        {
            if(2 == toggle)
            {
                if(write) fputc(((hold * 8)) + octal(c, source_file), output);
                ip = ip + 1;
                hold = 0;
                toggle = 0;
            }
            else if(1 == toggle)
            {
                hold = ((hold * 8) + octal(c, source_file));
                toggle = 2;
            }
            else
            {
                hold = octal(c, source_file);
                toggle = 1;
            }
        }
    }
    else if(BINARY == ByteMode)
    {
        if(0 <= binary(c, source_file))
        {
            if(7 == toggle)
            {
                if(write) fputc((hold * 2) + binary(c, source_file), output);
                ip = ip + 1;
                hold = 0;
                toggle = 0;
            }
            else
            {
                hold = ((hold * 2) + binary(c, source_file));
                toggle = toggle + 1;
            }
        }
    }
}

void pad_to_align(int write)
{
    if((ARMV7L == Architecture) || (AARM64 == Architecture) || (RISCV32 == Architecture) || (RISCV64 == Architecture))
    {
        if(1 == (ip & 0x1))
        {
            ip = ip + 1;
            if(write) fputc('\0', output);
        }
        if(2 == (ip & 0x2))
        {
            ip = ip + 2;
            if(write)
            {
                fputc('\0', output);
                fputc('\0', output);
            }
        }
    }
}

void first_pass(struct input_files* input)
{
    if(NULL == input) return;
    first_pass(input->next);
    filename = input->filename;
    linenumber = 1;
    FILE* source_file = fopen(filename, "r");

    if(NULL == source_file)
    {
        fputs("The file: ", stderr);
        fputs(input->filename, stderr);
        fputs(" can not be opened!\n", stderr);
        exit(EXIT_FAILURE);
    }

    toggle = FALSE;
    int c;
    for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
    {
        /* Check for and deal with label */
        if(':' == c)
        {
            c = storeLabel(source_file, ip);
        }

        /* check for and deal with relative/absolute pointers to labels */
        if(in_set(c, "!@$~%&"))
        { /* deal with 1byte pointer !; 2byte pointers (@ and $); 3byte pointers ~; 4byte pointers (% and &) */
            Update_Pointer(c);
            c = Throwaway_token(source_file);
            if ('>' == c)
            { /* deal with label>base */
                c = Throwaway_token(source_file);
            }
        }
        else if('<' == c)
        {
            pad_to_align(FALSE);
        }
        else if('^' == c)
        {
            /* Just ignore */
            continue;
        }
        else process_byte(c, source_file, FALSE);
    }
    fclose(source_file);
}

void second_pass(struct input_files* input)
{
    if(NULL == input) return;
    second_pass(input->next);
    filename = input->filename;
    linenumber = 1;
    FILE* source_file = fopen(filename, "r");

    /* Something that should never happen */
    if(NULL == source_file)
    {
        fputs("The file: ", stderr);
        fputs(input->filename, stderr);
        fputs(" can not be opened!\nWTF-pass2\n", stderr);
        exit(EXIT_FAILURE);
    }

    toggle = FALSE;
    hold = 0;

    int c;
    for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
    {
        if(':' == c) c = Throwaway_token(source_file); /* Deal with : */
        else if(in_set(c, "!@$~%&")) storePointer(c, source_file);  /* Deal with !, @, $, ~, % and & */
        else if('<' == c) pad_to_align(TRUE);
        else if('^' == c) ALIGNED = TRUE;
        else process_byte(c, source_file, TRUE);
    }

    fclose(source_file);
}

https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=hex2_word.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/hex2_word.c used in: Step 30, Step 39
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "hex2_globals.h"

unsigned shiftregister;
unsigned tempword;
int updates;

void outOfRange(char* s, int value)
{
    line_error();
    fputs("error: value ", stderr);
    fputs(int2str(value, 10, TRUE), stderr);
    fputs(" out of range for field type ", stderr);
    fputs(s, stderr);
    fputs("\n", stderr);
    exit(EXIT_FAILURE);
}

void UpdateShiftRegister(char ch, int value)
{
    if ('.' == ch)
    {
        unsigned swap;
        /* Assume the user knows what they are doing */
        if(!BigEndian)
        {
            /* Swap from big-endian to little endian order */
            swap = (((value >> 24) & 0xFF) |
                    ((value << 8) & 0xFF0000) |
                    ((value >> 8) & 0xFF00) |
                    ((value & 0xFF) << 24));
        }
        else
        {
            /* Big endian needs no change */
            swap = value;
        }
        /* we just take the 4 bytes after the . and shove in the shift register */
        swap = swap & ((0xFFFF << 16) | 0xFFFF);
        shiftregister = shiftregister ^ swap;
    }
    else if ('!' == ch)
    {
        /* Corresponds to RISC-V I format */
        /* Will need architecture specific logic if more architectures go this route */
        /* no range check because it needs to work with labels for lui/addi + AUIPC combos */
        /* !label is used in the second instruction of AUIPC combo but we want an offset from */
        /* the first instruction */
        value = value + 4;
        tempword = (value & 0xFFF) << 20;
        /* Update shift register */
        tempword = tempword & ((0xFFFF << 16) | 0xFFFF);
        shiftregister = shiftregister ^ tempword;
    }
    else if ('@' == ch)
    {
        /* Corresponds to RISC-V B format (formerly known as SB) */
        /* Will need architecture specific logic if more architectures go this route */
        if ((value < -0x1000 || value > 0xFFF) || (value & 1)) outOfRange("B", value);

        /* Prepare the immediate's word */
        tempword = ((value & 0x1E) << 7)
            | ((value & 0x7E0) << (31 - 11))
            | ((value & 0x800) >> 4)
            | ((value & 0x1000) << (31 - 12));
        tempword = tempword & ((0xFFFF << 16) | 0xFFFF);
        /* Update shift register */
        shiftregister = shiftregister ^ tempword;
    }
    else if ('$' == ch)
    {
        /* Corresponds with RISC-V J format (formerly known as UJ) */
        /* Will need architecture specific logic if more architectures go this route */
        if ((value < -0x100000 || value > 0xFFFFF) || (value & 1)) outOfRange("J", value);

        tempword = ((value & 0x7FE) << (30 - 10))
            | ((value & 0x800) << (20 - 11))
            | ((value & 0xFF000))
            | ((value & 0x100000) << (31 - 20));
        tempword = tempword & ((0xFFFF << 16) | 0xFFFF);
        shiftregister = shiftregister ^ tempword;
    }
    else if ('~' == ch)
    {
        /* Corresponds with RISC-V U format */
        /* Will need architecture specific logic if more architectures go this route */
        if ((value & 0xFFF) < 0x800) tempword = value & (0xFFFFF << 12);
        else tempword = (value & (0xFFFFF << 12)) + 0x1000;
        tempword = tempword & ((0xFFFF << 16) | 0xFFFF);
        shiftregister = shiftregister ^ tempword;
    }
    else
    {
        line_error();
        fputs("error: UpdateShiftRegister reached impossible case: ch=", stderr);
        fputc(ch, stderr);
        fputs("\n", stderr);
        exit(EXIT_FAILURE);
    }
}

void WordStorePointer(char ch, FILE* source_file)
{
    /* Get string of pointer */
    ip = ip + 4;
    Clear_Scratch(scratch);
    int base_sep_p = consume_token(source_file);

    /* Lookup token */
    int target = GetTarget(scratch);
    int displacement;

    int base = ip;

    /* Change relative base address to :<base> */
    if ('>' == base_sep_p)
    {
        Clear_Scratch(scratch);
        consume_token (source_file);
        base = GetTarget (scratch);

        /* Force universality of behavior */
        displacement = (target - base);
    }
    else
    {
        displacement = Architectural_displacement(target, base);
    }

    /* output calculated difference */
    if('&' == ch) outputPointer(target, 4); /* Deal with & */
    else if('%' == ch) outputPointer(displacement, 4);  /* Deal with % */
    else
    {
        line_error();
        fputs("error: WordStorePointer reached impossible case: ch=", stderr);
        fputc(ch, stderr);
        fputs("\n", stderr);
        exit(EXIT_FAILURE);
    }
}

unsigned sr_nextb()
{
    unsigned rv = shiftregister & 0xff;
    shiftregister = shiftregister >> 8;
    return rv;
}

void DoByte(char c, FILE* source_file, int write, int update)
{
    if(HEX == ByteMode)
    {
        if(0 <= hex(c, source_file))
        {
            if(toggle)
            {
                if(write) fputc(((hold * 16)) + hex(c, source_file) ^ sr_nextb(), output);
                ip = ip + 1;
                if(update)
                {
                    hold = (hold * 16) + hex(c, source_file);
                    tempword = (tempword << 8) ^ hold;
                    updates = updates + 1;
                }
                hold = 0;
            }
            else
            {
                hold = hex(c, source_file);
            }
            toggle = !toggle;
        }
    }
    else if(OCTAL ==ByteMode)
    {
        if(0 <= octal(c, source_file))
        {
            if(2 == toggle)
            {
                if(write) fputc(((hold * 8)) + octal(c, source_file) ^ sr_nextb(), output);
                ip = ip + 1;
                if(update)
                {
                    hold = ((hold * 8) + octal(c, source_file));
                    tempword = (tempword << 8) ^ hold;
                    updates = updates + 1;
                }
                hold = 0;
                toggle = 0;
            }
            else if(1 == toggle)
            {
                hold = ((hold * 8) + octal(c, source_file));
                toggle = 2;
            }
            else
            {
                hold = octal(c, source_file);
                toggle = 1;
            }
        }
    }
    else if(BINARY == ByteMode)
    {
        if(0 <= binary(c, source_file))
        {
            if(7 == toggle)
            {
                if(write) fputc((hold * 2) + binary(c, source_file) ^ sr_nextb(), output);
                ip = ip + 1;
                if(update)
                {
                    hold = ((hold * 2) + binary(c, source_file));
                    tempword = (tempword << 8) ^ hold;
                    updates = updates + 1;
                }
                hold = 0;
                toggle = 0;
            }
            else
            {
                hold = ((hold * 2) + binary(c, source_file));
                toggle = toggle + 1;
            }
        }
    }
}

void WordFirstPass(struct input_files* input)
{
    if(NULL == input) return;
    WordFirstPass(input->next);
    filename = input->filename;
    linenumber = 1;
    FILE* source_file = fopen(filename, "r");

    if(NULL == source_file)
    {
        fputs("The file: ", stderr);
        fputs(input->filename, stderr);
        fputs(" can not be opened!\n", stderr);
        exit(EXIT_FAILURE);
    }

    toggle = FALSE;
    int c;
    for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
    {
        /* Check for and deal with label */
        if(':' == c)
        {
            c = storeLabel(source_file, ip);
        }

        /* check for and deal with relative/absolute pointers to labels */
        if('.' == c)
        {
            /* Read architecture specific number of bytes for what is defined as a word */
            /* 4bytes in RISC-V's case */
            updates = 0;
            tempword = 0;
            while (updates < 4)
            {
                c = fgetc(source_file);
                DoByte(c, source_file, FALSE, TRUE);
            }
            ip = ip - 4;
        }
        else if(in_set(c, "!@$~"))
        {
            /* Don't update IP */
            c = Throwaway_token(source_file);
        }
        else if(in_set(c, "%&"))
        {
            ip = ip + 4;
            c = Throwaway_token(source_file);
            if ('>' == c)
            { /* deal with label>base */
                c = Throwaway_token(source_file);
            }
        }
        else if('<' == c)
        {
            pad_to_align(FALSE);
        }
        else if('^' == c)
        {
            /* Just ignore */
            continue;
        }
        else DoByte(c, source_file, FALSE, FALSE);
    }
    fclose(source_file);
}

void WordSecondPass(struct input_files* input)
{
    shiftregister = 0;
    tempword = 0;

    if(NULL == input) return;
    WordSecondPass(input->next);
    filename = input->filename;
    linenumber = 1;
    FILE* source_file = fopen(filename, "r");

    /* Something that should never happen */
    if(NULL == source_file)
    {
        fputs("The file: ", stderr);
        fputs(input->filename, stderr);
        fputs(" can not be opened!\nWTF-pass2\n", stderr);
        exit(EXIT_FAILURE);
    }

    toggle = FALSE;
    hold = 0;

    int c;
    for(c = fgetc(source_file); EOF != c; c = fgetc(source_file))
    {
        if(':' == c) c = Throwaway_token(source_file); /* Deal with : */
        else if('.' == c)
        {
            /* Read architecture specific number of bytes for what is defined as a word */
            /* 4bytes in RISC-V's case */
            updates = 0;
            tempword = 0;
            while (updates < 4)
            {
                c = fgetc(source_file);
                DoByte(c, source_file, FALSE, TRUE);
            }
            UpdateShiftRegister('.', tempword);
            ip = ip - 4;
        }
        else if(in_set(c, "%&")) WordStorePointer(c, source_file);  /* Deal with % and & */
        else if(in_set(c, "!@$~"))
        {
            Clear_Scratch(scratch);
            consume_token(source_file);
            UpdateShiftRegister(c, Architectural_displacement(GetTarget(scratch), ip)); /* Play with shift register */
        }
        else if('<' == c) pad_to_align(TRUE);
        else if('^' == c) ALIGNED = TRUE;
        else DoByte(c, source_file, TRUE, FALSE);
    }

    fclose(source_file);
}

https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=hex2.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/hex2.c used in: Step 30, Step 39
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
 * Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "hex2_globals.h"

/* The essential functions */
void first_pass(struct input_files* input);
void second_pass(struct input_files* input);
void WordFirstPass(struct input_files* input);
void WordSecondPass(struct input_files* input);

/* Standard C main program */
int main(int argc, char **argv)
{
    int InsaneArchitecture = FALSE;
    ALIGNED = FALSE;
    BigEndian = TRUE;
    jump_tables = calloc(65537, sizeof(struct entry*));
    require(NULL != jump_tables, "Failed to allocate our jump_tables\n");

    Architecture = KNIGHT;
    Base_Address = 0;
    struct input_files* input = NULL;
    output = stdout;
    char* output_file = "";
    exec_enable = TRUE;
    ByteMode = HEX;
    scratch = calloc(max_string + 1, sizeof(char));
    require(NULL != scratch, "failed to allocate our scratch buffer\n");
    char* arch;
    struct input_files* temp;

    int option_index = 1;
    while(option_index <= argc)
    {
        if(NULL == argv[option_index])
        {
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--big-endian"))
        {
            BigEndian = TRUE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--little-endian"))
        {
            BigEndian = FALSE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--non-executable"))
        {
            exec_enable = FALSE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-A") || match(argv[option_index], "--architecture"))
        {
            arch = argv[option_index + 1];
            if(match("knight-native", arch) || match("knight-posix", arch)) Architecture = KNIGHT;
            else if(match("x86", arch)) Architecture = X86;
            else if(match("amd64", arch)) Architecture = AMD64;
            else if(match("armv7l", arch)) Architecture = ARMV7L;
            else if(match("aarch64", arch)) Architecture = AARM64;
            else if(match("ppc64le", arch)) Architecture = PPC64LE;
            else if(match("riscv32", arch)) Architecture = RISCV32;
            else if(match("riscv64", arch)) Architecture = RISCV64;
            else
            {
                fputs("Unknown architecture: ", stderr);
                fputs(arch, stderr);
                fputs(" know values are: knight-native, knight-posix, x86, amd64, armv7l, riscv32 and riscv64", stderr);
            }
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-b") || match(argv[option_index], "--binary"))
        {
            ByteMode = BINARY;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-B") || match(argv[option_index], "--base-address"))
        {
            Base_Address = strtoint(argv[option_index + 1]);
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-h") || match(argv[option_index], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file FILENAME1 {-f FILENAME2} (--big-endian|--little-endian)", stderr);
            fputs(" [--base-address 0x12345] [--architecture name]\nArchitecture:", stderr);
            fputs(" knight-native, knight-posix, x86, amd64, armv7l, aarch64, riscv32 and riscv64\n", stderr);
            fputs("To leverage octal or binary input: --octal, --binary\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "-f") || match(argv[option_index], "--file"))
        {
            temp = calloc(1, sizeof(struct input_files));
            require(NULL != temp, "failed to allocate file for processing\n");
            temp->filename = argv[option_index + 1];
            temp->next = input;
            input = temp;
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-o") || match(argv[option_index], "--output"))
        {
            output_file = argv[option_index + 1];
            output = fopen(output_file, "w");

            if(NULL == output)
            {
                fputs("The file: ", stderr);
                fputs(argv[option_index + 1], stderr);
                fputs(" can not be opened!\n", stderr);
                exit(EXIT_FAILURE);
            }
            option_index = option_index + 2;
        }
        else if(match(argv[option_index], "-O") || match(argv[option_index], "--octal"))
        {
            ByteMode = OCTAL;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "-V") || match(argv[option_index], "--version"))
        {
            fputs("hex2 1.4.0\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    if((Architecture == RISCV32) || (Architecture == RISCV64))
    {
        /* Forcing me to use words instead of just byting into the problem */
        InsaneArchitecture = TRUE;
    }

    /* Catch a common mistake */
    if((KNIGHT != Architecture) && (0 == Base_Address))
    {
        fputs(">> WARNING <<\n>> WARNING <<\n>> WARNING <<\n", stderr);
        fputs("If you are not generating a ROM image this binary will likely not work\n", stderr);
    }

    /* Catch implicitly false assumptions */
    if(BigEndian && ((X86 == Architecture) || ( AMD64 == Architecture) || (ARMV7L == Architecture) || (AARM64 == Architecture) || (RISCV32 == Architecture) || (RISCV64 == Architecture)))
    {
        fputs(">> WARNING <<\n>> WARNING <<\n>> WARNING <<\n", stderr);
        fputs("You have specified big endian output on likely a little endian processor\n", stderr);
        fputs("if this is a mistake please pass --little-endian next time\n", stderr);
    }

    /* Make sure we have a program tape to run */
    if (NULL == input)
    {
        return EXIT_FAILURE;
    }

    /* Get all of the labels */
    ip = Base_Address;
    if(InsaneArchitecture) WordFirstPass(input);
    else first_pass(input);

    /* Fix all the references*/
    ip = Base_Address;
    if(InsaneArchitecture) WordSecondPass(input);
    else second_pass(input);

    /* flush all writes */
    fflush(output);

    /* Set file as executable */
    if(exec_enable && (output != stdout))
    {
        /* Close output file */
        fclose(output);

        if(0 != chmod(output_file, 0750))
        {
            fputs("Unable to change permissions\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    return EXIT_SUCCESS;
}

https://github.com/oriansj/M2libc/blob/main/x86/libc-full.M1

Files:
## Copyright (C) 2016 Jeremiah Orians
## This file is part of M2-Planet.
##
## M2-Planet is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.

:_start

    mov_ebp,esp                 ; Protect esp

    ;; Prepare argv
    lea_eax,[ebp+DWORD] %4      ; ARGV_address = EBP + 4
    push_eax                    ; Put argv on the stack

    ;; Prepare envp
    mov_eax,ebp                 ; Address we need to load from
    mov_eax,[eax]               ; Get ARGC
    add_eax, %2                 ; OFFSET = ARGC + 2
    sal_eax, !2                 ; OFFSET = OFFSET * WORDSIZE
    add_eax,ebp                 ; ENVP_address = ESP + OFFSET
    push_eax                    ; Put envp on the stack
    mov_ebx, &GLOBAL__envp      ; Get _envp global
    mov_[ebx],eax               ; Save environment to _envp

    ;; Stack offset
    add_ebp, %4                 ; Fix ebp

    ;; Setup for malloc
    call %FUNCTION___init_malloc

    ;; Setup for FILE*
    call %FUNCTION___init_io

    ;; Perform the main loop
    call %FUNCTION_main
    push_eax                    ; Put return on stack
    push_eax                    ; so that _exit gets the value

:FUNCTION_exit
    call %FUNCTION___kill_io
:FUNCTION__exit
    pop_ebx
    pop_ebx
    mov_eax, %1
    int !0x80

:GLOBAL__envp
    NULL

https://github.com/oriansj/M2libc/blob/main/string.c

Files:
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stddef.h>

char* strcpy(char* dest, char const* src)
{
    int i = 0;

    while (0 != src[i])
    {
        dest[i] = src[i];
        i = i + 1;
    }
    dest[i] = 0;

    return dest;
}


char* strncpy(char* dest, char const* src, size_t count)
{
    if(0 == count) return dest;
    size_t i = 0;
    while(0 != src[i])
    {
        dest[i] = src[i];
        i = i + 1;
        if(count == i) return dest;
    }

    while(i <= count)
    {
        dest[i] = 0;
        i = i + 1;
    }

    return dest;
}


char* strcat(char* dest, char const* src)
{
    int i = 0;
    int j = 0;
    while(0 != dest[i]) i = i + 1;
    while(0 != src[j])
    {
        dest[i] = src[j];
        i = i + 1;
        j = j + 1;
    }
    dest[i] = 0;
    return dest;
}


char* strncat(char* dest, char const* src, size_t count)
{
    size_t i = 0;
    size_t j = 0;
    while(0 != dest[i]) i = i + 1;
    while(0 != src[j])
    {
        if(count == j)
        {
            dest[i] = 0;
            return dest;
        }
        dest[i] = src[j];
        i = i + 1;
        j = j + 1;
    }
    dest[i] = 0;
    return dest;
}


size_t strlen(char const* str )
{
    size_t i = 0;
    while(0 != str[i]) i = i + 1;
    return i;
}


size_t strnlen_s(char const* str, size_t strsz )
{
    size_t i = 0;
    while(0 != str[i])
    {
        if(strsz == i) return i;
        i = i + 1;
    }
    return i;
}


int strcmp(char const* lhs, char const* rhs )
{
    int i = 0;
    while(0 != lhs[i])
    {
        if(lhs[i] != rhs[i]) return lhs[i] - rhs[i];
        i = i + 1;
    }

    return lhs[i] - rhs[i];
}


int strncmp(char const* lhs, char const* rhs, size_t count)
{
    size_t i = 0;
    while(count > i)
    {
        if(0 == lhs[i]) break;
        if(lhs[i] != rhs[i]) return lhs[i] - rhs[i];
        i = i + 1;
    }

    return 0;
}


char* strchr(char const* str, int ch)
{
    char* p = str;
    while(ch != p[0])
    {
        if(0 == p[0]) return NULL;
        p = p + 1;
    }
    if(0 == p[0]) return NULL;
    return p;
}


char* strrchr(char const* str, int ch)
{
    char* p = str;
    int i = 0;
    while(0 != p[i]) i = i + 1;
    while(ch != p[i])
    {
        if(0 == i) return NULL;
        i = i - 1;
    }
    return (p + i);
}


size_t strspn(char const* dest, char const* src)
{
    if(0 == dest[0]) return 0;
    int i = 0;
    while(NULL != strchr(src, dest[i])) i = i + 1;
    return i;
}


size_t strcspn(char const* dest, char const* src)
{
    int i = 0;
    while(NULL == strchr(src, dest[i])) i = i + 1;
    return i;
}


char* strpbrk(char const* dest, char const* breakset)
{
    char* p = dest;
    char* s;
    while(0 != p[0])
    {
        s = strchr(breakset, p[0]);
        if(NULL != s) return strchr(p,  s[0]);
        p = p + 1;
    }
    return p;
}


void* memset(void* dest, int ch, size_t count)
{
    if(NULL == dest) return dest;
    size_t i = 0;
    char* s = dest;
    while(i < count)
    {
        s[i] = ch;
        i = i + 1;
    }
    return dest;
}


void* memcpy(void* dest, void const* src, size_t count)
{
    if(NULL == dest) return dest;
    if(NULL == src) return NULL;

    char* s1 = dest;
    char const* s2 = src;
    size_t i = 0;
    while(i < count)
    {
        s1[i] = s2[i];
        i = i + 1;
    }
    return dest;
}

void* memmove(void* dest, void const* src, size_t count)
{
    if (dest < src) return memcpy (dest, src, count);
    char *p = dest;
    char const *q = src;
    count = count - 1;
    while (count >= 0)
    {
        p[count] = q[count];
        count = count - 1;
    }
    return dest;
}


int memcmp(void const* lhs, void const* rhs, size_t count)
{
    if(0 == count) return 0;
    size_t i = 0;
    count = count - 1;
    char const* s1 = lhs;
    char const* s2 = rhs;
    while(i < count)
    {
        if(s1[i] != s2[i]) break;
        i = i + 1;
    }
    return (s1[i] - s2[i]);
}

https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=Kaem/kaem.h

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/Kaem/kaem.h used in: Step 43
/* Copyright (C) 2016-2020 Jeremiah Orians
 * Copyright (C) 2020 fosslinux
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include "../M2libc/bootstrappable.h"

/*
 * DEFINES
 */

#define FALSE 0
#define TRUE 1
// CONSTANT SUCCESS 0
#define SUCCESS 0
// CONSTANT FAILURE 1
#define FAILURE 1
#define MAX_STRING 4096
#define MAX_ARRAY 256


/*
 * Here is the token struct. It is used for both the token linked-list and
 * env linked-list.
 */
struct Token
{
    /*
     * For the token linked-list, this stores the token; for the env linked-list
     * this stores the value of the variable.
     */
    char* value;
    /*
     * Used only for the env linked-list. It holds a string containing the
     * name of the var.
     */
    char* var;
    /*
     * This struct stores a node of a singly linked list, store the pointer to
     * the next node.
     */
    struct Token* next;
};

#include "kaem_globals.h"

https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=Kaem/variable.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/Kaem/variable.c used in: Step 43
/*
 * Copyright (C) 2020 fosslinux
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "kaem.h"

/* Prototypes from other files */
int array_length(char** array);
char* env_lookup(char* variable);

/*
 * VARIABLE HANDLING FUNCTIONS
 */

/* Substitute a variable into n->value */
int run_substitution(char* var_name, struct Token* n)
{
    char* value = env_lookup(var_name);
    /* If there is nothing to substitute, don't substitute anything! */
    if(value != NULL)
    {
        char* s = calloc(MAX_STRING, sizeof(char));
        s = strcat(s, n->value);
        s = strcat(s, value);
        n->value = s;
        return TRUE;
    }
    return FALSE;
}

/* Handle ${var:-text} format of variables - i.e. ifset format */
int variable_substitute_ifset(char* input, struct Token* n, int index)
{
    /*
     * In ${var:-text} format, we evaluate like follows.
     * If var is set as an envar, then we substitute the contents of that
     * envar. If it is not set, we substitute alternative text.
     *
     * In this function, we assume that input is the raw token,
     * n->value is everything already done in variable_substitute,
     * index is where we are up to in input. offset is for n->value.
     */

    /*
     * Check if we should even be performing this function.
     * We perform this function when we come across ${var:-text} syntax.
     */
    int index_old = index;
    int perform = FALSE;
    int input_length = strlen(input);
    while(index < input_length)
    { /* Loop over each character */
        if(input[index] == ':' && input[index + 1] == '-')
        { /* Yes, this is (most likely) ${var:-text} format. */
            perform = TRUE;
            break;
        }
        index = index + 1;
    }

    /* Don't perform it if we shouldn't */
    if(perform == FALSE) return index_old;
    index = index_old;

    /*
     * Get offset.
     * offset is the difference between the index of the variable we write to
     * in the following blocks and input.
     * This stays relatively constant.
     */
    int offset = index;

    /* Get the variable name */
    char* var_name = calloc(MAX_STRING, sizeof(char));
    require(var_name != NULL, "Memory initialization of var_name in variable_substitute_ifset failed\n");
    while(input[index] != ':')
    { /* Copy into var_name until :- */
        var_name[index - offset] = input[index];
        index = index + 1;
    }

    /* Skip over :- */
    index = index + 2;
    offset = index;

    /* Get the alternative text */
    char* text = calloc(MAX_STRING, sizeof(char));
    require(text != NULL, "Memory initialization of text in variable_substitute_ifset failed\n");
    while(input[index] != '}')
    { /* Copy into text until } */
        require(input_length > index, "IMPROPERLY TERMINATED VARIABLE\nABORTING HARD\n");
        text[index - offset] = input[index];
        index = index + 1;
    }

    /* Do the substitution */
    if(run_substitution(var_name, n) == FALSE)
    { /* The variable was not found. Substitute the alternative text. */
        char* s = calloc(MAX_STRING, sizeof(char));
        s = strcat(s, n->value);
        s = strcat(s, text);
        n->value = s;
    }

    return index;
}

/* Controls substitution for ${variable} and derivatives */
int variable_substitute(char* input, struct Token* n, int index)
{
    /* NOTE: index is the pos of input */
    index = index + 1; /* We don't want the { */

    /*
     * Check for "special" types
     * If we do find a special type we delegate the substitution to it
     * and return here; as we are done... there's nothing more do do in
     * that case.
     */
    int index_old = index;
    index = variable_substitute_ifset(input, n, index);
    if(index != index_old) return index;

    /* Reset index */
    index = index_old;

    /*
     * If we reach here it is a normal substitution
     * Let's do it!
     */
    /* Initialize var_name and offset */
    char* var_name = calloc(MAX_STRING, sizeof(char));
    require(var_name != NULL, "Memory initialization of var_name in variable_substitute failed\n");
    int offset = index;

    /* Get the variable name */
    int substitute_done = FALSE;
    char c;
    while(substitute_done == FALSE)
    {
        c = input[index];
        require(MAX_STRING > index, "LINE IS TOO LONG\nABORTING HARD\n");
        if(EOF == c || '\n' == c || index > strlen(input))
        { /* We never should hit EOF, EOL or run past the end of the line 
             while collecting a variable */
            fputs("IMPROPERLY TERMINATED VARIABLE!\nABORTING HARD\n", stderr);
            exit(EXIT_FAILURE);
        }
        else if('\\' == c)
        { /* Drop the \ - poor mans escaping. */
            index = index + 1;
        }
        else if('}' == c)
        { /* End of variable name */
            substitute_done = TRUE;
        }
        else
        {
            var_name[index - offset] = c;
            index = index + 1;
        }
    }

    /* Substitute the variable */
    run_substitution(var_name, n);

    return index;
}

/* Function to concatenate all command line arguments */
void variable_all(char** argv, struct Token* n)
{
    fflush(stdout);
    /* index refernences the index of n->value, unlike other functions */
    int index = 0;
    int argv_length = array_length(argv);
    int i = 0;
    char* argv_element = calloc(MAX_STRING, sizeof(char));
    char* hold = argv[i];
    n->value = argv_element;
    /* Assuming the form kaem -f script or kaem -f script -- 123 we want matching results to bash, so skip the kaem, -f and script */
    while(!match("--", hold))
    {
        i = i + 1;
        hold = argv[i];
        if(argv_length == i) break;
    }

    /* put i = i + 1 in the for initialization to skip past the -- */
    for(; i < argv_length; i = i + 1)
    {
        /* Ends up with (n->value) (argv[i]) */
        /* If we don't do this we get jumbled results in M2-Planet */
        hold = argv[i];
        strcpy(argv_element + index, hold);
        index = index + strlen(hold);

        /* Add space on the end */
        n->value[index] = ' ';
        index = index + 1;
    }
    /* Remove trailing space */
    index = index - 1;
    n->value[index] = 0;
}

/* Function controlling substitution of variables */
void handle_variables(char** argv, struct Token* n)
{
    /* NOTE: index is the position of input */
    int index = 0;

    /* Create input */
    char* input = calloc(MAX_STRING, sizeof(char));
    require(input != NULL, "Memory initialization of input in collect_variable failed\n");
    strcpy(input, n->value);
    /* Reset n->value */
    n->value = calloc(MAX_STRING, sizeof(char));
    require(n->value != NULL, "Memory initialization of n->value in collect_variable failed\n");

    /* Copy everything up to the $ */
    /*
     * TODO: Not need allocation of input before this check if there is no
     * variable in it.
     */
    while(input[index] != '$')
    {
        if(input[index] == 0)
        { /* No variable in it */
            n->value = input;
            return; /* We don't need to do anything more */
        }
        n->value[index] = input[index];
        index = index + 1;
    }

    /* Must be outside the loop */
    int offset;

substitute:
    index = index + 1; /* We are uninterested in the $ */
    /* Run the substitution */
    if(input[index] == '{')
    { /* Handle everything ${ related */
        index = variable_substitute(input, n, index);
        index = index + 1; /* We don't want the closing } */
    }
    else if(input[index] == '@')
    { /* Handles $@ */
        index = index + 1; /* We don't want the @ */
        variable_all(argv, n);
    }
    else
    { /* We don't know that */
        fputs("IMPROPERLY USED VARIABLE!\nOnly ${foo} and $@ format are accepted at this time.\nABORTING HARD\n", stderr);
        exit(EXIT_FAILURE);
    }

    offset = strlen(n->value) - index;
    /* Copy everything from the end of the variable to the end of the token */
    while(input[index] != 0)
    {
        if(input[index] == '$')
        { /* We have found another variable */
            fflush(stdout);
            goto substitute;
        }
        n->value[index + offset] = input[index];
        index = index + 1;
    }
}

https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=Kaem/kaem_globals.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/Kaem/kaem_globals.c used in: Step 43
/* Copyright (C) 2016-2020 Jeremiah Orians
 * Copyright (C) 2020 fosslinux
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "kaem.h"

int command_done;
int VERBOSE;
int VERBOSE_EXIT;
int STRICT;
int INIT_MODE;
int FUZZING;
int WARNINGS;
char* KAEM_BINARY;
char* PATH;

/* Token linked-list; stores the tokens of each line */
struct Token* token;
/* Env linked-list; stores the environment variables */
struct Token* env;
/* Alias linked-list; stores the aliases */
struct Token* alias;

https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=Kaem/kaem.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/Kaem/kaem.c used in: Step 43
/* Copyright (C) 2016-2020 Jeremiah Orians
 * Copyright (C) 2020 fosslinux
 * Copyright (C) 2021 Andrius Å tikonas
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include "kaem.h"

/* Prototypes from other files */
void handle_variables(char** argv, struct Token* n);

/*
 * UTILITY FUNCTIONS
 */

/* Function to find a character in a string */
char* find_char(char* string, char a)
{
    if(0 == string[0])
    {
        return NULL;
    }

    while(a != string[0])
    {
        string = string + 1;

        if(0 == string[0])
        {
            return string;
        }
    }

    return string;
}

/* Function to find the length of a char**; an array of strings */
int array_length(char** array)
{
    int length = 0;

    while(array[length] != NULL)
    {
        length = length + 1;
    }

    return length;
}

/* Search for a variable in the token linked-list */
char* token_lookup(char* variable, struct Token* token)
{
    /* Start at the head */
    struct Token* n = token;

    /* Loop over the linked-list */
    while(n != NULL)
    {
        if(match(variable, n->var))
        {
            /* We have found the correct node */
            return n->value; /* Done */
        }

        /* Nope, try the next */
        n = n->next;
    }

    /* We didn't find anything! */
    return NULL;
}

/* Search for a variable in the env linked-list */
char* env_lookup(char* variable)
{
    return token_lookup(variable, env);
}

/* Search for a variable in the alias linked-list */
char* alias_lookup(char* variable)
{
    return token_lookup(variable, alias);
}

/* Find the full path to an executable */
char* find_executable(char* name)
{
    if(match("", name))
    {
        return NULL;
    }

    if(('.' == name[0]) || ('/' == name[0]))
    {
        /* assume names that start with . or / are relative or absolute */
        return name;
    }

    char* trial = calloc(MAX_STRING, sizeof(char));
    char* MPATH = calloc(MAX_STRING, sizeof(char)); /* Modified PATH */
    require(MPATH != NULL, "Memory initialization of MPATH in find_executable failed\n");
    strcpy(MPATH, PATH);
    FILE* t;
    char* next = find_char(MPATH, ':');
    int index;
    int offset;
    int mpath_length;
    int name_length;
    int trial_length;

    while(NULL != next)
    {
        /* Reset trial */
        trial_length = strlen(trial);

        for(index = 0; index < trial_length; index = index + 1)
        {
            trial[index] = 0;
        }

        next[0] = 0;
        /* prepend_string(MPATH, prepend_string("/", name)) */
        mpath_length = strlen(MPATH);

        for(index = 0; index < mpath_length; index = index + 1)
        {
            require(MAX_STRING > index, "Element of PATH is too long\n");
            trial[index] = MPATH[index];
        }

        trial[index] = '/';
        offset = strlen(trial);
        name_length = strlen(name);

        for(index = 0; index < name_length; index = index + 1)
        {
            require(MAX_STRING > index, "Element of PATH is too long\n");
            trial[index + offset] = name[index];
        }

        /* Try the trial */
        require(strlen(trial) < MAX_STRING, "COMMAND TOO LONG!\nABORTING HARD\n");
        t = fopen(trial, "r");

        if(NULL != t)
        {
            fclose(t);
            return trial;
        }

        MPATH = next + 1;
        next = find_char(MPATH, ':');
    }

    return NULL;
}

/* Function to convert a Token linked-list into an array of strings */
char** list_to_array(struct Token* s)
{
    struct Token* n;
    n = s;
    char** array = calloc(MAX_ARRAY, sizeof(char*));
    require(array != NULL, "Memory initialization of array in conversion of list to array failed\n");
    char* element = calloc(MAX_STRING, sizeof(char));
    require(element != NULL, "Memory initialization of element in conversion of list to array failed\n");
    int index = 0;
    int i;
    int value_length;
    int var_length;
    int offset;

    while(n != NULL)
    {
        /* Loop through each node and assign it to an array index */
        array[index] = calloc(MAX_STRING, sizeof(char));
        require(array[index] != NULL, "Memory initialization of array[index] in conversion of list to array failed\n");
        /* Bounds checking */
        /* No easy way to tell which it is, output generic message */
        require(index < MAX_ARRAY, "SCRIPT TOO LONG or TOO MANY ENVARS\nABORTING HARD\n");

        if(n->var == NULL)
        {
            /* It is a line */
            array[index] = n->value;
        }
        else
        {
            /* It is a var */
            /* prepend_string(n->var, prepend_string("=", n->value)) */
            var_length = strlen(n->var);

            for(i = 0; i < var_length; i = i + 1)
            {
                element[i] = n->var[i];
            }

            element[i] = '=';
            i = i + 1;
            offset = i;
            value_length = strlen(n->value);

            for(i = 0; i < value_length; i = i + 1)
            {
                element[i + offset] = n->value[i];
            }
        }

        /* Insert elements if not empty */
        if(!match("", element))
        {
            strcpy(array[index], element);
        }

        n = n->next;
        index = index + 1;

        /* Reset element */
        for(i = 0; i < MAX_STRING; i = i + 1)
        {
            element[i] = 0;
        }
    }

    return array;
}

/* Function to handle the correct options for escapes */
int handle_escape(int c)
{
    if(c == '\n')
    {
        /* Do nothing - eat up the newline */
        return -1;
    }
    else if('n' == c)
    {
        /* Add a newline to the token */
        return '\n';
    }
    else if('r' == c)
    {
        /* Add a return to the token */
        return '\r';
    }
    else if('\\' == c)
    {
        /* Add a real backslash to the token */
        return '\\';
    }
    else
    {
        /* Just add it to the token (eg, quotes) */
        return c;
    }
}

/*
 * TOKEN COLLECTION FUNCTIONS
 */

/* Function for skipping over line comments */
void collect_comment(FILE* input)
{
    int c;

    /* Eat up the comment, one character at a time */
    /*
     * Sanity check that the comment ends with \n.
     * Remove the comment from the FILE*
     */
    do
    {
        c = fgetc(input);
        /* We reached an EOF!! */
        require(EOF != c, "IMPROPERLY TERMINATED LINE COMMENT!\nABORTING HARD\n");
    } while('\n' != c); /* We can now be sure it ended with \n -- and have purged the comment */
}

/* Function for collecting strings and removing the "" pair that goes with them */
int collect_string(FILE* input, char* n, int index)
{
    int string_done = FALSE;
    int c;

    do
    {
        /* Bounds check */
        require(MAX_STRING > index, "LINE IS TOO LONG\nABORTING HARD\n");
        c = fgetc(input);
        require(EOF != c, "IMPROPERLY TERMINATED STRING!\nABORTING HARD\n");

        if('\\' == c)
        {
            /* We are escaping the next character */
            /* This correctly handles escaped quotes as it just returns the quote */
            c = fgetc(input);
            c = handle_escape(c);
            n[index] = c;
            index = index + 1;
        }
        else if('"' == c)
        {
            /* End of string */
            string_done = TRUE;
        }
        else
        {
            n[index] = c;
            index = index + 1;
        }
    } while(string_done == FALSE);

    return index;
}

/* Function to parse and assign token->value */
int collect_token(FILE* input, char* n, int last_index)
{
    int c;
    int cc;
    int token_done = FALSE;
    int index = 0;

    do
    {
        /* Loop over each character in the token */
        c = fgetc(input);
        /* Bounds checking */
        require(MAX_STRING > index, "LINE IS TOO LONG\nABORTING HARD\n");

        if(EOF == c)
        {
            /* End of file -- this means script complete */
            /* We don't actually exit here. This logically makes more sense;
             * let the code follow its natural path of execution and exit
             * sucessfuly at the end of main().
             */
            token_done = TRUE;
            command_done = TRUE;
            return -1;
        }
        else if((' ' == c) || ('\t' == c))
        {
            /* Space and tab are token separators */
            token_done = TRUE;
        }
        else if(('\n' == c) || (';' == c))
        {
            /* Command terminates at the end of a line or at semicolon */
            command_done = TRUE;
            token_done = TRUE;

            if(0 == index)
            {
                index = last_index;
            }
        }
        else if('"' == c)
        {
            /* Handle strings -- everything between a pair of "" */
            index = collect_string(input, n, index);
            token_done = TRUE;
        }
        else if('#' == c)
        {
            /* Handle line comments */
            collect_comment(input);
            command_done = TRUE;
            token_done = TRUE;

            if(0 == index)
            {
                index = last_index;
            }
        }
        else if('\\' == c)
        {
            /* Support for escapes */
            c = fgetc(input); /* Skips over \, gets the next char */
            cc = handle_escape(c);

            if(-1 != cc)
            {
                /* We need to put it into the token */
                n[index] = cc;
            }

            index = index + 1;
        }
        else if(0 == c)
        {
            /* We have come to the end of the token */
            token_done = TRUE;
        }
        else
        {
            /* It's a character to assign */
            n[index] = c;
            index = index + 1;
        }
    } while(token_done == FALSE);

    return index;
}

/* Function to parse string and assign token->value */
int collect_alias_token(char* input, char* n, int index)
{
    int c;
    int cc;
    int token_done = FALSE;
    int output_index = 0;

    do
    {
        /* Loop over each character in the token */
        c = input[index];
        index = index + 1;

        if((' ' == c) || ('\t' == c))
        {
            /* Space and tab are token separators */
            token_done = TRUE;
        }
        else if('\\' == c)
        {
            /* Support for escapes */
            c = input[index];
            index = index + 1;
            cc = handle_escape(c);

            /* We need to put it into the token */
            n[output_index] = cc;
            output_index = output_index + 1;
        }
        else if(0 == c)
        {
            /* We have come to the end of the token */
            token_done = TRUE;
            index = 0;
        }
        else
        {
            /* It's a character to assign */
            n[output_index] = c;
            output_index = output_index + 1;
        }
    } while(token_done == FALSE);

    return index;
}

/*
 * EXECUTION FUNCTIONS
 * Note: All of the builtins return SUCCESS (0) when they exit successfully
 * and FAILURE (1) when they fail.
 */

/* Function to check if the token is an envar */
int is_envar(char* token)
{
    int i = 0;
    int token_length = strlen(token);

    while(i < token_length)
    {
        if(token[i] == '=')
        {
            return FAILURE;
        }

        i = i + 1;
    }

    return SUCCESS;
}

/* Add an envar */
void add_envar()
{
    /* Pointers to strings we want */
    char* name = calloc(strlen(token->value) + 4, sizeof(char));
    char* value = token->value;
    char* newvalue;
    int i = 0;

    /* Isolate the name */
    while('=' != value[i])
    {
        name[i] = value[i];
        i = i + 1;
    }

    /* Isolate the value */
    newvalue = name + i + 2;
    value = value + i + 1;
    i = 0;
    require(0 != value[i], "add_envar received improper variable\n");

    while(0 != value[i])
    {
        newvalue[i] = value[i];
        i = i + 1;
    }

    /* If we are in init-mode and this is the first var env == NULL, rectify */
    if(env == NULL)
    {
        env = calloc(1, sizeof(struct Token));
        require(env != NULL, "Memory initialization of env failed\n");
        env->var = name; /* Add our first variable */
    }

    /*
     * If the name of the envar is PATH, then we need to set our (internal)
     * global PATH value.
     */
    if(match(name, "PATH"))
    {
        strcpy(PATH, newvalue);
    }

    struct Token* n = env;

    /* Find match if possible */
    while(!match(name, n->var))
    {
        if(NULL == n->next)
        {
            n->next = calloc(1, sizeof(struct Token));
            require(n->next != NULL, "Memory initialization of next env node in add_envar failed\n");
            n->next->var = name;
        } /* Loop will match and exit */

        n = n->next;
    }

    /* Since we found the variable we need only to set it to its new value */
    n->value = newvalue;
}

/* Add an alias */
void add_alias()
{
    token = token->next; /* Skip the actual alias */
    if(token->next == NULL)
    {
        /* No arguments */
        char** array = list_to_array(alias);
        int index = 0;
        while(array[index] != NULL) {
            fputs(array[index], stdout);
            fputc('\n', stdout);
            index = index + 1;
        }
        fflush(stdout);
        return;
    }
    if(!is_envar(token->value)) {
        char** array = list_to_array(token);
        int index = 0;
        while(array[index] != NULL) {
            fputs(array[index], stdout);
            fputc(' ', stdout);
            index = index + 1;
        }
        fputc('\n', stdout);
        fflush(stdout);
        return;
    }

    /* Pointers to strings we want */
    char* name = calloc(strlen(token->value) + 4, sizeof(char));
    char* value = token->value;
    char* newvalue;
    int i = 0;

    /* Isolate the name */
    while('=' != value[i])
    {
        name[i] = value[i];
        i = i + 1;
    }

    /* Isolate the value */
    newvalue = name + i + 2;
    value = value + i + 1;
    i = 0;
    require(0 != value[i], "add_alias received improper variable\n");

    while(0 != value[i])
    {
        newvalue[i] = value[i];
        i = i + 1;
    }

    /* If this is the first alias, rectify */
    if(alias == NULL)
    {
        alias = calloc(1, sizeof(struct Token));
        require(alias != NULL, "Memory initialization of alias failed\n");
        alias->var = name; /* Add our first variable */
    }

    struct Token* n = alias;

    /* Find match if possible */
    while(!match(name, n->var))
    {
        if(NULL == n->next)
        {
            n->next = calloc(1, sizeof(struct Token));
            require(n->next != NULL, "Memory initialization of next alias node in alias failed\n");
            n->next->var = name;
        } /* Loop will match and exit */

        n = n->next;
    }

    /* Since we found the variable we need only to set it to its new value */
    n->value = newvalue;
}

/* cd builtin */
int cd()
{
    if(NULL == token->next)
    {
        return FAILURE;
    }

    token = token->next;

    if(NULL == token->value)
    {
        return FAILURE;
    }

    int ret = chdir(token->value);

    if(0 > ret)
    {
        return FAILURE;
    }

    return SUCCESS;
}

/* pwd builtin */
int pwd()
{
    char* path = calloc(MAX_STRING, sizeof(char));
    require(path != NULL, "Memory initialization of path in pwd failed\n");
    getcwd(path, MAX_STRING);
    require(!match("", path), "getcwd() failed\n");
    fputs(path, stdout);
    fputs("\n", stdout);
    return SUCCESS;
}

/* set builtin */
int set()
{
    /* Get the options */
    int i;

    if(NULL == token->next)
    {
        goto cleanup_set;
    }

    token = token->next;

    if(NULL == token->value)
    {
        goto cleanup_set;
    }

    char* options = calloc(MAX_STRING, sizeof(char));
    require(options != NULL, "Memory initialization of options in set failed\n");
    int last_position = strlen(token->value) - 1;

    for(i = 0; i < last_position; i = i + 1)
    {
        options[i] = token->value[i + 1];
    }

    /* Parse the options */
    int options_length = strlen(options);

    for(i = 0; i < options_length; i = i + 1)
    {
        if(options[i] == 'a')
        {
            /* set -a is on by default and cannot be disabled at this time */
            if(WARNINGS)
            {
                fputs("set -a is on by default and cannot be disabled\n", stdout);
            }

            continue;
        }
        else if(options[i] == 'e')
        {
            /* Fail on failure */
            STRICT = TRUE;
        }
        else if(options[i] == 'x')
        {
            /* Show commands as executed */
            /* TODO: this currently behaves like -v. Make it do what it should */
            VERBOSE = TRUE;
            /*
             * Output the set -x because VERBOSE didn't catch it before.
             * We don't do just -x because we support multiple options in one command,
             * eg set -ex.
             */
            fputs(" +> set -", stdout);
            fputs(options, stdout);
            fputs("\n", stdout);
            fflush(stdout);
        }
        else
        {
            /* Invalid */
            fputc(options[i], stderr);
            fputs(" is an invalid set option!\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    return SUCCESS;
cleanup_set:
    return FAILURE;
}

/* echo builtin */
void echo()
{
    if(token->next == NULL)
    {
        /* No arguments */
        fputs("\n", stdout);
        return;
    }

    if(token->next->value == NULL)
    {
        /* No arguments */
        fputs("\n", stdout);
        return;
    }

    token = token->next; /* Skip the actual echo */

    while(token != NULL)
    {
        /* Output each argument to echo to stdout */
        if(token->value == NULL)
        {
            break;
        }

        fputs(token->value, stdout);
        if(NULL != token->next)
        {
            /* M2-Planet doesn't short circuit */
            if(NULL != token->next->value) fputc(' ', stdout);
        }
        token = token->next;
    }

    fputs("\n", stdout);
}

/* unset builtin */
void unset()
{
    struct Token* e;
    /* We support multiple variables on the same line */
    struct Token* t;
    t = token->next;

    while(t != NULL)
    {
        e = env;

        /* Look for the variable; we operate on ->next because we need to remove ->next */
        while(e->next != NULL)
        {
            if(NULL == t->value)
            {
                break;
            }

            if(match(e->next->var, t->value))
            {
                break;
            }

            e = e->next;
        }

        t = t->next;

        /* If it's NULL nothing was found */
        if(e->next == NULL)
        {
            continue;
        }

        /* Otherwise there is something to unset */
        e->next = e->next->next;
    }
}

void execute(FILE* script, char** argv);
int _execute(FILE* script, char** argv);
int collect_command(FILE* script, char** argv);

/* if builtin */
void if_cmd(FILE* script, char** argv)
{
    int index;
    int old_VERBOSE;
    token = token->next; /* Skip the actual if */
    /* Do not check for successful exit status */
    int if_status = _execute(script, argv);
    old_VERBOSE = VERBOSE;
    VERBOSE = VERBOSE && !if_status;

    do
    {
        index = collect_command(script, argv);
        require(index != -1, "Unexpected EOF, improperly terminated if statement.\n");

        if(0 == index)
        {
            continue;
        }

        if(0 == if_status)
        {
            /* Stuff to exec */
            execute(script, argv);
        }

        if(match(token->value, "else"))
        {
            if_status = !if_status;
        }
    } while(!match(token->value, "fi"));

    VERBOSE = old_VERBOSE;
}

int what_exit(char* program, int status)
{
    /***********************************************************************************
     * If the low-order 8 bits of w_status are equal to 0x7F or zero, the child        *
     * process has stopped. If the low-order 8 bits of w_status are non-zero and are   *
     * not equal to 0x7F, the child process terminated due to a signal otherwise, the  *
     * child process terminated due to an exit() call.                                 *
     *                                                                                 *
     * In the event it was a signal that stopped the process the top 8 bits of         *
     * w_status contain the signal that caused the process to stop.                    *
     *                                                                                 *
     * In the event it was terminated the bottom 7 bits of w_status contain the        *
     * terminating error number for the process.                                       *
     *                                                                                 *
     * If bit 0x80 of w_status is set, a core dump was produced.                       *
     ***********************************************************************************/

    int WIFEXITED = !(status & 0x7F);
    int WEXITSTATUS = (status & 0xFF00) >> 8;
    int WTERMSIG = status & 0x7F;
    int WCOREDUMP = status & 0x80;
    int WIFSIGNALED = !((0x7F == WTERMSIG) || (0 == WTERMSIG));
    int WIFSTOPPED = ((0x7F == WTERMSIG) && (0 == WCOREDUMP));

    if(WIFEXITED)
    {
        if(VERBOSE_EXIT)
        {
            fputc('\n', stderr);
            fputs(program, stderr);
            fputs(" normal termination, exit status = ", stderr);
            fputs(int2str(WEXITSTATUS, 10, TRUE), stderr);
            fputs("\n\n\n", stderr);
        }
        return WEXITSTATUS;
    }
    else if (WIFSIGNALED)
    {
        fputc('\n', stderr);
        fputs(program, stderr);
        fputs(" abnormal termination, signal number = ", stderr);
        fputs(int2str(WTERMSIG, 10, TRUE), stderr);
        fputc('\n', stderr);
        if(WCOREDUMP) fputs("core dumped\n", stderr);
        return WTERMSIG;
    }
    else if(WIFSTOPPED)
    {
        fputc('\n', stderr);
        fputs(program, stderr);
        fputs(" child stopped, signal number = ", stderr);
        fputs(int2str(WEXITSTATUS, 10, TRUE), stderr);
        fputc('\n', stderr);
        return WEXITSTATUS;
    }

    fputc('\n', stderr);
    fputs(program, stderr);
    fputs(" :: something crazy happened with execve\nI'm just gonna get the hell out of here\n", stderr);
    exit(EXIT_FAILURE);
}

/* Execute program and check for error */
void execute(FILE* script, char** argv)
{
    int status = _execute(script, argv);

    if(STRICT == TRUE && (0 != status))
    {
        /* Clearly the script hit an issue that should never have happened */
        fputs("Subprocess error ", stderr);
        fputs(int2str(status, 10, TRUE), stderr);
        fputs("\nABORTING HARD\n", stderr);
        exit(EXIT_FAILURE);
    }
}

/* Execute program */
int _execute(FILE* script, char** argv)
{
    /* Run the command */
    /* rc = return code */
    int rc;
    /* exec without forking */
    int exec = FALSE;

    /* Actually do the execution */
    if(is_envar(token->value) == TRUE)
    {
        add_envar();
        return 0;
    }
    else if(match(token->value, "cd"))
    {
        rc = cd();

        if(STRICT)
        {
            require(rc == SUCCESS, "cd failed!\n");
        }

        return 0;
    }
    else if(match(token->value, "set"))
    {
        rc = set();

        if(STRICT)
        {
            require(rc == SUCCESS, "set failed!\n");
        }

        return 0;
    }
    else if(match(token->value, "alias"))
    {
        add_alias();
        return 0;
    }
    else if(match(token->value, "pwd"))
    {
        rc = pwd();

        if(STRICT)
        {
            require(rc == SUCCESS, "pwd failed!\n");
        }

        return 0;
    }
    else if(match(token->value, "echo"))
    {
        echo();
        return 0;
    }
    else if(match(token->value, "unset"))
    {
        unset();
        return 0;
    }
    else if(match(token->value, "exec"))
    {
        token = token->next; /* Skip the actual exec */
        exec = TRUE;
    }
    else if(match(token->value, "if"))
    {
        if_cmd(script, argv);
        return 0;
    }
    else if(match(token->value, "then"))
    {
        /* ignore */
        return 0;
    }
    else if(match(token->value, "else"))
    {
        /* ignore */
        return 0;
    }
    else if(match(token->value, "fi"))
    {
        /* ignore */
        return 0;
    }

    /* If it is not a builtin, run it as an executable */
    int status; /* i.e. return code */
    char** array;
    char** envp;
    /* Get the full path to the executable */
    char* program = find_executable(token->value);

    /* Check we can find the executable */
    if(NULL == program)
    {
        if(STRICT == TRUE)
        {
            fputs("WHILE EXECUTING ", stderr);
            fputs(token->value, stderr);
            fputs(" NOT FOUND!\nABORTING HARD\n", stderr);
            exit(EXIT_FAILURE);
        }

        /* If we are not strict simply return */
        return 0;
    }

    int f = 0;

#ifdef __uefi__
    array = list_to_array(token);
    envp = list_to_array(env);
    return spawn(program, array, envp);
#else
    if(!exec)
    {
        f = fork();
    }

    /* Ensure fork succeeded */
    if(f == -1)
    {
        fputs("WHILE EXECUTING ", stderr);
        fputs(token->value, stderr);
        fputs(" fork() FAILED\nABORTING HARD\n", stderr);
        exit(EXIT_FAILURE);
    }
    else if(f == 0)
    {
        /* Child */
        /**************************************************************
         * Fuzzing produces random stuff; we don't want it running    *
         * dangerous commands. So we just don't execve.               *
         * But, we still do the list_to_array calls to check for      *
         * segfaults.                                                 *
         **************************************************************/
        array = list_to_array(token);
        envp = list_to_array(env);

        if(FALSE == FUZZING)
        {
            /* We are not fuzzing */
            /* execve() returns only on error */
            execve(program, array, envp);
        }

        /* Prevent infinite loops */
        _exit(EXIT_FAILURE);
    }

    /* Otherwise we are the parent */
    /* And we should wait for it to complete */
    waitpid(f, &status, 0);
    return what_exit(program, status);
#endif
}

int collect_command(FILE* script, char** argv)
{
    command_done = FALSE;
    /* Initialize token */
    struct Token* n;
    n = calloc(1, sizeof(struct Token));
    require(n != NULL, "Memory initialization of token in collect_command failed\n");
    char* s = calloc(MAX_STRING, sizeof(char));
    require(s != NULL, "Memory initialization of token in collect_command failed\n");
    token = n;
    int index = 0;
    int alias_index;
    char* alias_string;

    /* Get the tokens */
    while(command_done == FALSE)
    {
        index = collect_token(script, s, index);
        /* Don't allocate another node if the current one yielded nothing, OR
         * if we are done.
         */

        if(match(s, ""))
        {
            continue;
        }

        alias_string = alias_lookup(s);
        alias_index = 0;
        do
        {
            if(alias_string != NULL)
            {
                alias_index = collect_alias_token(alias_string, s, alias_index);
            }

            /* add to token */
            n->value = s;
            s = calloc(MAX_STRING, sizeof(char));
            require(s != NULL, "Memory initialization of next token node in collect_command failed\n");
            /* Deal with variables */
            handle_variables(argv, n);

            /* If the variable expands into nothing */
            if(match(n->value, " "))
            {
                n->value = NULL;
                continue;
            }

            /* Prepare for next loop */
            n->next = calloc(1, sizeof(struct Token));
            require(n->next != NULL, "Memory initialization of next token node in collect_command failed\n");
            n = n->next;
        }
        while(alias_index != 0);
    }

    /* -1 means the script is done */
    if(EOF == index)
    {
        return index;
    }

    /* Output the command if verbose is set */
    /* Also if there is nothing in the command skip over */
    if(VERBOSE && !match(token->value, "") && !match(token->value, NULL))
    {
        n = token;
        fputs(" +>", stdout);

        while(n != NULL)
        {
            /* Print out each token token */
            fputs(" ", stdout);

            /* M2-Planet doesn't let us do this in the while */
            if(n->value != NULL)
            {
                if(!match(n->value, ""))
                {
                    fputs(n->value, stdout);
                }
            }

            n = n->next;
        }

        fputc('\n', stdout);
        fflush(stdout);
    }

    return index;
}

/* Function for executing our programs with desired arguments */
void run_script(FILE* script, char** argv)
{
    int index;

    while(TRUE)
    {
        /*
         * Tokens has to be reset each time, as we need a new linked-list for
         * each line.
         * See, the program flows like this as a high level overview:
         * Get line -> Sanitize line and perform variable replacement etc ->
         * Execute line -> Next.
         * We don't need the previous lines once they are done with, so tokens
         * are hence for each line.
         */
        index = collect_command(script, argv);

        /* -1 means the script is done */
        if(EOF == index)
        {
            break;
        }

        if(0 == index)
        {
            continue;
        }

        /* Stuff to exec */
        execute(script, argv);
    }
}

/* Function to populate env */
void populate_env(char** envp)
{
    /* You can't populate a NULL environment */
    if(NULL == envp)
    {
        return;
    }

    /* avoid empty arrays */
    int max = array_length(envp);

    if(0 == max)
    {
        return;
    }

    /* Initialize env and n */
    env = calloc(1, sizeof(struct Token));
    require(env != NULL, "Memory initialization of env failed\n");
    struct Token* n;
    n = env;
    int i;
    int j;
    int k;
    char* envp_line;

    for(i = 0; i < max; i = i + 1)
    {
        n->var = calloc(MAX_STRING, sizeof(char));
        require(n->var != NULL, "Memory initialization of n->var in population of env failed\n");
        n->value = calloc(MAX_STRING, sizeof(char));
        require(n->value != NULL, "Memory initialization of n->var in population of env failed\n");
        j = 0;
        /*
         * envp is weird.
         * When referencing envp[i]'s characters directly, they were all jumbled.
         * So just copy envp[i] to envp_line, and work with that - that seems
         * to fix it.
         */
        envp_line = calloc(MAX_STRING, sizeof(char));
        require(envp_line != NULL, "Memory initialization of envp_line in population of env failed\n");
        strcpy(envp_line, envp[i]);

        while(envp_line[j] != '=')
        {
            /* Copy over everything up to = to var */
            n->var[j] = envp_line[j];
            j = j + 1;
        }

        /* If we get strange input, we need to ignore it */
        if(n->var == NULL)
        {
            continue;
        }

        j = j + 1; /* Skip over = */
        k = 0; /* As envp[i] will continue as j but n->value begins at 0 */

        while(envp_line[j] != 0)
        {
            /* Copy everything else to value */
            n->value[k] = envp_line[j];
            j = j + 1;
            k = k + 1;
        }

        /* Sometimes, we get lines like VAR=, indicating nothing is in the variable */
        if(n->value == NULL)
        {
            n->value = "";
        }

        /* Advance to next part of linked list */
        n->next = calloc(1, sizeof(struct Token));
        require(n->next != NULL, "Memory initialization of n->next in population of env failed\n");
        n = n->next;
    }

    /* Get rid of node on the end */
    n = NULL;
    /* Also destroy the n->next reference */
    n = env;

    while(n->next->var != NULL)
    {
        n = n->next;
    }

    n->next = NULL;
}

int main(int argc, char** argv, char** envp)
{
    VERBOSE = FALSE;
    VERBOSE_EXIT = FALSE;
    STRICT = TRUE;
    FUZZING = FALSE;
    WARNINGS = FALSE;
    char* filename = "kaem.run";
    FILE* script = NULL;
    /* Initalize structs */
    token = calloc(1, sizeof(struct Token));
    require(token != NULL, "Memory initialization of token failed\n");
    if(NULL != argv[0]) KAEM_BINARY = argv[0];
    else KAEM_BINARY = "./bin/kaem";
    int i = 1;

    /* Loop over arguments */
    while(i <= argc)
    {
        if(NULL == argv[i])
        {
            /* Ignore the argument */
            i = i + 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            /* Help information */
            fputs("Usage: ", stdout);
            fputs(argv[0], stdout);
            fputs(" [-h | --help] [-V | --version] [--file filename | -f filename] [-i | --init-mode] [-v | --verbose] [--non-strict] [--warn] [--fuzz]\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            /* Set the filename */
            if(argv[i + 1] != NULL)
            {
                filename = argv[i + 1];
            }

            i = i + 2;
        }
        else if(match(argv[i], "-i") || match(argv[i], "--init-mode"))
        {
            /* init mode does not populate env */
            INIT_MODE = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "-V") || match(argv[i], "--version"))
        {
            /* Output version */
            fputs("kaem version 1.4.0\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-v") || match(argv[i], "--verbose"))
        {
            /* Set verbose */
            VERBOSE = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "--strict"))
        {
            /* it is a NOP */
            STRICT = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "--non-strict"))
        {
            /* Set strict */
            STRICT = FALSE;
            i = i + 1;
        }
        else if(match(argv[i], "--warn"))
        {
            /* Set warnings */
            WARNINGS = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "--fuzz"))
        {
            /* Set fuzzing */
            FUZZING = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "--show-exit-codes"))
        {
            /* show exit codes */
            VERBOSE_EXIT = TRUE;
            i = i + 1;
        }
        else if(match(argv[i], "--"))
        {
            /* Nothing more after this */
            break;
        }
        else
        {
            /* We don't know this argument */
            fputs("UNKNOWN ARGUMENT\n", stdout);
            exit(EXIT_FAILURE);
        }
    }

    /* Populate env */
    if(INIT_MODE == FALSE)
    {
        populate_env(envp);
    }

    /* make sure SHELL is set */
    if(NULL == env_lookup("SHELL"))
    {
        struct Token* shell = calloc(1, sizeof(struct Token));
        require(NULL != shell, "unable to create SHELL environment variable\n");
        shell->next = env;
        shell->var = "SHELL";
        shell->value= KAEM_BINARY;
        env = shell;
    }

    /* Populate PATH variable
     * We don't need to calloc() because env_lookup() does this for us.
     */
    PATH = env_lookup("PATH");
    /* Populate USERNAME variable */
    char* USERNAME = env_lookup("LOGNAME");

    /* Handle edge cases */
    if((NULL == PATH) && (NULL == USERNAME))
    {
        /* We didn't find either of PATH or USERNAME -- use a generic PATH */
        PATH = calloc(MAX_STRING, sizeof(char));
        require(PATH != NULL, "Memory initialization of PATH failed\n");
        strcpy(PATH, "/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
    }
    else if(NULL == PATH)
    {
        /* We did find a username but not a PATH -- use a generic PATH but with /home/USERNAME */
        PATH = calloc(MAX_STRING, sizeof(char));
        PATH = strcat(PATH, "/home/");
        PATH = strcat(PATH, USERNAME);
        PATH = strcat(PATH, "/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games");
    }

    /* Open the script */
    script = fopen(filename, "r");

    if(NULL == script)
    {
        fputs("The file: ", stderr);
        fputs(filename, stderr);
        fputs(" can not be opened!\n", stderr);
        exit(EXIT_FAILURE);
    }

    /* Run the commands */
    run_script(script, argv);
    /* Cleanup */
    fclose(script);
    return EXIT_SUCCESS;
}

https://github.com/oriansj/M2-Mesoplanet/blob/main/cc.h

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc.h used in: Step 49
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>


#define FALSE 0
#define TRUE 1


int in_set(int c, char* s);
int match(char* a, char* b);
void require(int bool, char* error);
char* int2str(int x, int base, int signed_p);
void reset_hold_string();


struct type
{
    struct type* next;
    int size;
    int offset;
    int is_signed;
    struct type* indirect;
    struct type* members;
    struct type* type;
    char* name;
};

struct token_list
{
    struct token_list* next;
    union
    {
        struct token_list* locals;
        struct token_list* prev;
    };
    char* s;
    union
    {
        struct type* type;
        char* filename;
    };
    union
    {
        struct token_list* arguments;
        struct token_list* expansion;
        int depth;
        int linenumber;
    };
};

#include "cc_globals.h"

https://github.com/oriansj/M2-Mesoplanet/blob/main/cc_globals.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc_globals.c used in: Step 49
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

/* What types we have */
struct type* global_types;
struct type* prim_types;

/* What we are currently working on */
struct token_list* global_token;

/* Output reorder collections*/
struct token_list* output_list;
struct token_list* strings_list;
struct token_list* globals_list;

/* Make our string collection more efficient */
char* hold_string;
int string_index;

int MAX_STRING;

/* enable preprocessor-only mode */
int PREPROCESSOR_MODE;

/* enable spawn behavior to be effective */
char* M2LIBC_PATH;
char* Architecture;
char* OperatingSystem;
int WORDSIZE;
int ENDIAN;
char* BASEADDRESS;
int STDIO_USED;
char* TEMPDIR;

/* So we don't shoot ourself in the face */
int FUZZING;
int DIRTY_MODE;
int DEBUG_LEVEL;

https://github.com/oriansj/M2-Mesoplanet/blob/main/cc_env.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc_env.c used in: Step 49
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include"cc.h"
#include <sys/utsname.h>

void init_macro_env(char* sym, char* value, char* source, int num);
char* env_lookup(char* variable);
void clear_string(char* s);

struct utsname* get_uname_data()
{
    struct utsname* unameData = calloc(1, sizeof(struct utsname));
    require(NULL != unameData, "unameData calloc failed\n");
    uname(unameData);
    if(4 <= DEBUG_LEVEL)
    {
        fputs("utsname details: ", stderr);
        fputs(unameData->sysname, stderr);
        fputc(' ', stderr);
        fputs(unameData->machine, stderr);
        fputc('\n', stderr);
    }
    return unameData;
}

void setup_env()
{
    if(2 <= DEBUG_LEVEL) fputs("Starting setup_env\n", stderr);
    char* ARCH;
    if(NULL != Architecture)
    {
        ARCH = Architecture;
    }
    else
    {
        ARCH = NULL;
        struct utsname* unameData = get_uname_data();

        if(match("i386", unameData->machine) ||
           match("i486", unameData->machine) ||
           match("i586", unameData->machine) ||
           match("i686", unameData->machine) ||
           match("i686-pae", unameData->machine)) ARCH = "x86";
        else if(match("x86_64", unameData->machine)) ARCH = "amd64";
        else ARCH = unameData->machine;
        if(3 <= DEBUG_LEVEL)
        {
            fputs("Architecture selected: ", stderr);
            fputs(ARCH, stderr);
            fputc('\n', stderr);
        }

        /* Check for override */
        char* hold = env_lookup("ARCHITECTURE_OVERRIDE");
        if(NULL != hold)
        {
            ARCH = hold;
            if(3 <= DEBUG_LEVEL)
            {
                fputs("environmental override for ARCH: ", stderr);
                fputs(ARCH, stderr);
                fputc('\n', stderr);
            }
        }
        free(unameData);
    }


    /* Set desired architecture */
    WORDSIZE = 32;
    ENDIAN = FALSE;
    BASEADDRESS = "0x0";
    if(match("knight-native", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using knight-native architecture\n", stderr);
        ENDIAN = TRUE;
        Architecture = "knight-native";
    }
    else if(match("knight-posix", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using knight-posix architecture\n", stderr);
        ENDIAN = TRUE;
        Architecture = "knight-posix";
    }
    else if(match("x86", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using x86 architecture\n", stderr);
        BASEADDRESS = "0x8048000";
        Architecture = "x86";
        init_macro_env("__i386__", "1", "--architecture", 0);
    }
    else if(match("amd64", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using amd64 architecture\n", stderr);
        BASEADDRESS = "0x00600000";
        Architecture = "amd64";
        WORDSIZE = 64;
        init_macro_env("__x86_64__", "1", "--architecture", 0);
    }
    else if(match("armv7l", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using armv7l architecture\n", stderr);
        BASEADDRESS = "0x10000";
        Architecture = "armv7l";
        init_macro_env("__arm__", "1", "--architecture", 0);
    }
    else if(match("aarch64", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using aarch64 architecture\n", stderr);
        BASEADDRESS = "0x400000";
        Architecture = "aarch64";
        WORDSIZE = 64;
        init_macro_env("__aarch64__", "1", "--architecture", 0);
    }
    else if(match("riscv32", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using riscv32 architecture\n", stderr);
        BASEADDRESS = "0x600000";
        Architecture = "riscv32";
        init_macro_env("__riscv", "1", "--architecture", 0);
        init_macro_env("__riscv_xlen", "32", "--architecture", 1);
    }
    else if(match("riscv64", ARCH))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using riscv64 architecture\n", stderr);
        BASEADDRESS = "0x600000";
        Architecture = "riscv64";
        WORDSIZE = 64;
        init_macro_env("__riscv", "1", "--architecture", 0);
        init_macro_env("__riscv_xlen", "64", "--architecture", 1);
    }
    else
    {
        fputs("Unknown architecture: ", stderr);
        fputs(ARCH, stderr);
        fputs(" know values are: knight-native, knight-posix, x86, amd64, armv7l, aarch64, riscv32 and riscv64\n", stderr);
        exit(EXIT_FAILURE);
    }


    /* Setup Operating System */
    if(NULL == OperatingSystem)
    {
        OperatingSystem = "Linux";
        if(3 <= DEBUG_LEVEL)
        {
            fputs("Operating System selected: ", stderr);
            fputs(OperatingSystem, stderr);
            fputc('\n', stderr);
        }

        /* Check for override */
        char* hold = env_lookup("OS_OVERRIDE");
        if(NULL != hold)
        {
            OperatingSystem = hold;
            if(3 <= DEBUG_LEVEL)
            {
                fputs("environmental override for OS: ", stderr);
                fputs(OperatingSystem, stderr);
                fputc('\n', stderr);
            }
        }
    }

    if(match("UEFI", OperatingSystem))
    {
        if(4 <= DEBUG_LEVEL) fputs("Using UEFI\n", stderr);
        BASEADDRESS = "0x0";
        OperatingSystem = "UEFI";
        init_macro_env("__uefi__", "1", "--os", 0);
    }

    if(2 <= DEBUG_LEVEL) fputs("setup_env successful\n", stderr);
}

struct Token
{
    /*
     * For the token linked-list, this stores the token; for the env linked-list
     * this stores the value of the variable.
     */
    char* value;
    /*
     * Used only for the env linked-list. It holds a string containing the
     * name of the var.
     */
    char* var;
    /*
     * This struct stores a node of a singly linked list, store the pointer to
     * the next node.
     */
    struct Token* next;
};

struct Token* env;

int array_length(char** array)
{
    int length = 0;

    while(array[length] != NULL)
    {
        length = length + 1;
    }

    return length;
}

/* Search for a variable in the token linked-list */
char* token_lookup(char* variable, struct Token* token)
{
    if(6 <= DEBUG_LEVEL)
    {
        fputs("in token_lookup\nLooking for: ", stderr);
        fputs(variable, stderr);
        fputc('\n', stderr);
    }
    /* Start at the head */
    struct Token* n = token;

    /* Loop over the linked-list */
    while(n != NULL)
    {
        if(15 <= DEBUG_LEVEL)
        {
            fputs(n->var, stderr);
            fputc('\n', stderr);
        }
        if(match(variable, n->var))
        {
            if(6 <= DEBUG_LEVEL) fputs("match found in token_lookup\n", stderr);
            /* We have found the correct node */
            return n->value; /* Done */
        }

        /* Nope, try the next */
        n = n->next;
    }

    /* We didn't find anything! */
    return NULL;
}

/* Search for a variable in the env linked-list */
char* env_lookup(char* variable)
{
    return token_lookup(variable, env);
}

char* envp_hold;
int envp_index;

void reset_envp_hold()
{
    clear_string(envp_hold);
    envp_index = 0;
}

void push_env_byte(int c)
{
    envp_hold[envp_index] = c;
    envp_index = envp_index + 1;
    require(4096 > envp_index, "Token exceeded 4096 char envp limit\n");
}

struct Token* process_env_variable(char* envp_line, struct Token* n)
{
    struct Token* node = calloc(1, sizeof(struct Token));
    require(node != NULL, "Memory initialization of node failed\n");
    reset_envp_hold();
    int i = 0;

    while(envp_line[i] != '=')
    {
        /* Copy over everything up to = to var */
        push_env_byte(envp_line[i]);
        i = i + 1;
    }

    node->var = calloc(i + 2, sizeof(char));
    require(node->var != NULL, "Memory initialization of n->var in population of env failed\n");
    strcpy(node->var, envp_hold);

    i = i + 1; /* Skip over = */

    reset_envp_hold();
    while(envp_line[i] != 0)
    {
        /* Copy everything else to value */
        push_env_byte(envp_line[i]);
        i = i + 1;
    }

    /* Sometimes, we get lines like VAR=, indicating nothing is in the variable */
    if(0 == strlen(envp_hold))
    {
        node->value = "";
    }
    else
    {
        /* but looks like we got something so, lets use it */
        node->value = calloc(strlen(envp_hold) + 2, sizeof(char));
        require(node->value != NULL, "Memory initialization of n->var in population of env failed\n");
        strcpy(node->value, envp_hold);
    }

    node->next = n;
    return node;
}

void populate_env(char** envp)
{
    if(2 <= DEBUG_LEVEL) fputs("populate_env started\n", stderr);
    /* You can't populate a NULL environment */
    if(NULL == envp)
    {
        if(3 <= DEBUG_LEVEL) fputs("NULL envp\n", stderr);
        return;
    }

    /* avoid empty arrays */
    int max = array_length(envp);

    if(0 == max)
    {
        if(3 <= DEBUG_LEVEL) fputs("Empty envp\n", stderr);
        return;
    }

    /* Initialize env and n */
    env = NULL;
    int i;
    envp_hold = calloc(4096, sizeof(char));
    require(envp_hold != NULL, "Memory initialization of envp_hold in population of env failed\n");
    char* envp_line = calloc(4096, sizeof(char));
    require(envp_line != NULL, "Memory initialization of envp_line in population of env failed\n");

    if(3 <= DEBUG_LEVEL) fputs("starting env loop\n", stderr);
    for(i = 0; i < max; i = i + 1)
    {
        /*
         * envp is weird.
         * When referencing envp[i]'s characters directly, they were all jumbled.
         * So just copy envp[i] to envp_line, and work with that - that seems
         * to fix it.
         */
        clear_string(envp_line);
        require(4096 > strlen(envp[i]), "envp line exceeds 4096byte limit\n");
        strcpy(envp_line, envp[i]);

        if(9 <= DEBUG_LEVEL)
        {
            fputs("trying envp_line: ", stderr);
            fputs(envp_line, stderr);
            fputc('\n', stderr);
        }

        env = process_env_variable(envp_line, env);

        if(9 <= DEBUG_LEVEL)
        {
            fputs("got var of: ", stderr);
            fputs(env->var, stderr);
            fputs("\nAnd value of: ", stderr);
            fputs(env->value, stderr);
            fputc('\n', stderr);
        }
    }

    free(envp_line);
    free(envp_hold);
    if(3 <= DEBUG_LEVEL)
    {
        fputs("\n\nenv loop successful\n", stderr);
        fputs(int2str(i, 10, FALSE), stderr);
        fputs(" envp records processed\n\n", stderr);
    }

    require(NULL != env, "can't have an empty environment from the creation of a non-null environment\n");
    if(2 <= DEBUG_LEVEL) fputs("populate_env successful\n", stderr);
}

https://github.com/oriansj/M2-Mesoplanet/blob/main/cc_reader.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc_reader.c used in: Step 49
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "cc.h"
char* env_lookup(char* variable);
char* int2str(int x, int base, int signed_p);

struct visited
{
    struct visited* prev;
    char* name;
};

/* Globals */
FILE* input;
struct token_list* token;
int line;
char* file;
struct visited* vision;

int previously_seen(char* s)
{
    struct visited* v = vision;
    while(NULL != v)
    {
        if(match(v->name, s)) return TRUE;
        v = v->prev;
    }
    return FALSE;
}

void just_seen(char* s)
{
    struct visited* hold = calloc(1, sizeof(struct visited));
    hold->prev = vision;
    hold->name = s;
    vision = hold;
}

int grab_byte()
{
    int c = fgetc(input);
    if(10 == c) line = line + 1;
    return c;
}

void push_byte(int c)
{
    hold_string[string_index] = c;
    string_index = string_index + 1;
    require(MAX_STRING > string_index, "Token exceeded MAX_STRING char limit\nuse --max-string number to increase\n");
}

int consume_byte(int c)
{
    push_byte(c);
    return grab_byte();
}

int preserve_string(int c)
{
    int frequent = c;
    int escape = FALSE;
    do
    {
        if(!escape && '\\' == c ) escape = TRUE;
        else escape = FALSE;
        c = consume_byte(c);
        require(EOF != c, "Unterminated string\n");
    } while(escape || (c != frequent));
    c = consume_byte(frequent);
    return c;
}


void copy_string(char* target, char* source, int max)
{
    int i = 0;
    while(0 != source[i])
    {
        target[i] = source[i];
        i = i + 1;
        if(i == max) break;
    }
}


int preserve_keyword(int c, char* S)
{
    while(in_set(c, S))
    {
        c = consume_byte(c);
    }
    return c;
}

void clear_string(char* s)
{
    int i = 0;
    while(0 != s[i])
    {
        s[i] = 0;
        i = i + 1;
        require(i < MAX_STRING, "string exceeded max string size while clearing string\n");
    }
}

void reset_hold_string()
{
    clear_string(hold_string);
    string_index = 0;
}


/* note if this is the first token in the list, head needs fixing up */
struct token_list* eat_token(struct token_list* token)
{
    if(NULL != token->prev)
    {
        token->prev->next = token->next;
    }

    /* update backlinks */
    if(NULL != token->next)
    {
        token->next->prev = token->prev;
    }

    return token->next;
}


void new_token(char* s, int size)
{
    struct token_list* current = calloc(1, sizeof(struct token_list));
    require(NULL != current, "Exhausted memory while getting token\n");

    /* More efficiently allocate memory for string */
    current->s = calloc(size, sizeof(char));
    require(NULL != current->s, "Exhausted memory while trying to copy a token\n");
    copy_string(current->s, s, MAX_STRING);

    current->prev = token;
    current->next = token;
    current->linenumber = line;
    current->filename = file;
    token = current;
}

int get_token(int c)
{
    reset_hold_string();

    if(c == EOF)
    {
        return c;
    }
    else if((32 == c) || (9 == c) || (c == '\n'))
    {
        c = consume_byte(c);
    }
    else if('#' == c)
    {
        c = consume_byte(c);
        c = preserve_keyword(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_");
    }
    else if(in_set(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"))
    {
        c = preserve_keyword(c, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_:");
    }
    else if(in_set(c, "<=>|&!^%"))
    {
        c = preserve_keyword(c, "<=>|&!^%");
    }
    else if(in_set(c, "'\""))
    {
        c = preserve_string(c);
    }
    else if(c == '/')
    {
        c = consume_byte(c);
        if(c == '*')
        {
            c = consume_byte(c);
            while(c != '/')
            {
                while(c != '*')
                {
                    c = consume_byte(c);
                    require(EOF != c, "Hit EOF inside of block comment\n");
                }
                c = consume_byte(c);
                require(EOF != c, "Hit EOF inside of block comment\n");
            }
            c = consume_byte(c);
        }
        else if(c == '/')
        {
            while(c != '\n')
            {
                c = consume_byte(c);
                require(EOF != c, "Hit EOF inside of line comment\n");
            }
            c = consume_byte(c);
        }
        else if(c == '=')
        {
            c = consume_byte(c);
        }
    }
    else if(c == '*')
    {
        c = consume_byte(c);
        if(c == '=')
        {
            c = consume_byte(c);
        }
    }
    else if(c == '+')
    {
        c = consume_byte(c);
        if(c == '=')
        {
            c = consume_byte(c);
        }
        if(c == '+')
        {
            c = consume_byte(c);
        }
    }
    else if(c == '-')
    {
        c = consume_byte(c);
        if(c == '=')
        {
            c = consume_byte(c);
        }
        if(c == '>')
        {
            c = consume_byte(c);
        }
        if(c == '-')
        {
            c = consume_byte(c);
        }
    }
    else
    {
        c = consume_byte(c);
    }

    return c;
}

struct token_list* reverse_list(struct token_list* head)
{
    struct token_list* root = NULL;
    struct token_list* next;
    while(NULL != head)
    {
        next = head->next;
        head->next = root;
        root = head;
        head = next;
    }
    return root;
}

int read_include(int c)
{
    reset_hold_string();
    int done = FALSE;
    int ch;

    while(!done)
    {
        if(c == EOF)
        {
            fputs("we don't support EOF as a filename in #include statements\n", stderr);
            exit(EXIT_FAILURE);
        }
        else if((32 == c) || (9 == c) || (c == '\n'))
        {
            c = grab_byte();
        }
        else if(('"' == c) || ('<' == c))
        {
            if('<' == c) c = '>';
            ch = c;
            do
            {
                c = consume_byte(c);
                require(EOF != c, "Unterminated filename in #include\n");
            } while(c != ch);
            if('>' == ch) hold_string[0] = '<';
            done = TRUE;
        }
    }

    return c;
}

void insert_file_header(char* name, int line)
{
    char* hold_line = int2str(line, 10, FALSE);
    reset_hold_string();
    strcat(hold_string, "// #FILENAME ");
    strcat(hold_string, name);
    strcat(hold_string, " ");
    strcat(hold_string, hold_line);
    new_token(hold_string, strlen(hold_string)+2);
    new_token("\n", 3);
}

struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename, int include);
int include_file(int ch, int include_file)
{
    /* The old state to restore to */
    char* hold_filename = file;
    FILE* hold_input = input;
    int hold_number;

    /* The new file to load */
    char* new_filename;
    FILE* new_file;

    require(EOF != ch, "#include failed to receive filename\n");
    /* Remove the #include */
    token = token->next;

    /* Get new filename */
    read_include(ch);
    /* with just a little extra to put in the matching at the end */
    new_token(hold_string, string_index + 3);

    ch = '\n';
    new_filename = token->s;
    /* Remove name from stream */
    token = token->next;

    /* Try to open the file */
    if('<' == new_filename[0])
    {
        if(match("stdio.h", new_filename + 1)) STDIO_USED = TRUE;
        reset_hold_string();
        strcat(hold_string, M2LIBC_PATH);
        strcat(hold_string, "/");
        strcat(hold_string, new_filename + 1);
        strcat(new_filename, ">");
        new_file = fopen(hold_string, "r");
    }
    else
    {
        if(match("M2libc/bootstrappable.h", new_filename+1))
        {
            reset_hold_string();
            strcat(hold_string, M2LIBC_PATH);
            strcat(hold_string, "/bootstrappable.h");
            new_file = fopen(hold_string, "r");
        }
        else new_file = fopen(new_filename+1, "r");

        strcat(new_filename, "\"");
    }

    /* prevent multiple visits */
    if(previously_seen(new_filename)) return ch;
    just_seen(new_filename);

    /* special case this compatibility crap */
    if(match("\"../gcc_req.h\"", new_filename) || match("\"gcc_req.h\"", new_filename)) return ch;

    if(include_file)
    {
        fputs("reading file: ", stderr);
        fputs(new_filename, stderr);
        fputc('\n', stderr);
    }

    /* catch garbage input */
    if(NULL == new_file)
    {
        fputs("unable to read file: ", stderr);
        fputs(new_filename, stderr);
        fputs("\nAborting hard!\n", stderr);
        exit(EXIT_FAILURE);
    }

    /* protect our current line number */
    hold_number = line + 1;

    /* Read the new file */
    if(include_file) read_all_tokens(new_file, token, new_filename, include_file);

    /* put back old file info */
    insert_file_header(hold_filename, hold_number);

    /* resume reading old file */
    input = hold_input;
    line = hold_number;
    file = hold_filename;
    return ch;
}

struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename, int include)
{
    token = current;
    insert_file_header(filename, 1);
    input  = a;
    line = 1;
    file = filename;
    int ch = grab_byte();
    while(EOF != ch)
    {
        ch = get_token(ch);
        new_token(hold_string, string_index + 2);
        if(match("#include", token->s)) ch = include_file(ch, include);
    }

    return token;
}

https://github.com/oriansj/M2-Mesoplanet/blob/main/cc_spawn.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc_spawn.c used in: Step 49
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include"cc.h"
#include <unistd.h>
#include <sys/wait.h>
#define MAX_ARRAY 256

char* env_lookup(char* variable);

/* Function to find a character in a string */
char* find_char(char* string, char a)
{
    if(0 == string[0])
    {
        return NULL;
    }

    while(a != string[0])
    {
        string = string + 1;

        if(0 == string[0])
        {
            return string;
        }
    }

    return string;
}

/* Find the full path to an executable */
char* find_executable(char* name)
{
    char* PATH = env_lookup("PATH");
    require(NULL != PATH, "No PATH found\nAborting\n");
    if(match("", name))
    {
        return NULL;
    }

    if(('.' == name[0]) || ('/' == name[0]))
    {
        /* assume names that start with . or / are relative or absolute */
        return name;
    }

    char* trial = calloc(MAX_STRING, sizeof(char));
    char* MPATH = calloc(MAX_STRING, sizeof(char)); /* Modified PATH */
    require(MPATH != NULL, "Memory initialization of MPATH in find_executable failed\n");
    strcpy(MPATH, PATH);
    FILE* t;
    char* next = find_char(MPATH, ':');
    int index;
    int offset;
    int mpath_length;
    int name_length;
    int trial_length;

    while(NULL != next)
    {
        /* Reset trial */
        trial_length = strlen(trial);

        for(index = 0; index < trial_length; index = index + 1)
        {
            trial[index] = 0;
        }

        next[0] = 0;
        /* prepend_string(MPATH, prepend_string("/", name)) */
        mpath_length = strlen(MPATH);

        for(index = 0; index < mpath_length; index = index + 1)
        {
            require(MAX_STRING > index, "Element of PATH is too long\n");
            trial[index] = MPATH[index];
        }

        trial[index] = '/';
        offset = strlen(trial);
        name_length = strlen(name);

        for(index = 0; index < name_length; index = index + 1)
        {
            require(MAX_STRING > index, "Element of PATH is too long\n");
            trial[index + offset] = name[index];
        }

        /* Try the trial */
        trial_length = strlen(trial);
        require(trial_length < MAX_STRING, "COMMAND TOO LONG!\nABORTING HARD\n");
        t = fopen(trial, "r");

        if(NULL != t)
        {
            fclose(t);
            return trial;
        }

        MPATH = next + 1;
        next = find_char(MPATH, ':');
    }

    return NULL;
}

void sanity_command_check(char** array)
{
    int i = 0;
    char* s = array[0];
    while(NULL != s)
    {
        fputs(s, stderr);
        fputc(' ', stderr);
        i = i + 1;
        s = array[i];
    }
    fputc('\n', stderr);
}


int what_exit(char* program, int status)
{
    /***********************************************************************************
     * If the low-order 8 bits of w_status are equal to 0x7F or zero, the child        *
     * process has stopped. If the low-order 8 bits of w_status are non-zero and are   *
     * not equal to 0x7F, the child process terminated due to a signal otherwise, the  *
     * child process terminated due to an exit() call.                                 *
     *                                                                                 *
     * In the event it was a signal that stopped the process the top 8 bits of         *
     * w_status contain the signal that caused the process to stop.                    *
     *                                                                                 *
     * In the event it was terminated the bottom 7 bits of w_status contain the        *
     * terminating error number for the process.                                       *
     *                                                                                 *
     * If bit 0x80 of w_status is set, a core dump was produced.                       *
     ***********************************************************************************/

    if(DEBUG_LEVEL > 6)
    {
        fputs("in what_exit with char* program of: ", stderr);
        fputs(program, stderr);
        fputs("\nAnd int status of: 0x", stderr);
        fputs(int2str(status, 16, FALSE), stderr);
        fputc('\n', stderr);
    }

    int WIFEXITED = !(status & 0x7F);
    int WEXITSTATUS = (status & 0xFF00) >> 8;
    int WTERMSIG = status & 0x7F;
    int WCOREDUMP = status & 0x80;
    int WIFSIGNALED = !((0x7F == WTERMSIG) || (0 == WTERMSIG));
    int WIFSTOPPED = ((0x7F == WTERMSIG) && (0 == WCOREDUMP));

    if(WIFEXITED)
    {
        if(DEBUG_LEVEL > 2)
        {
            fputc('\n', stderr);
            fputs(program, stderr);
            fputs(" normal termination, exit status = ", stderr);
            fputs(int2str(WEXITSTATUS, 10, TRUE), stderr);
            fputc('\n', stderr);
        }
        return WEXITSTATUS;
    }
    else if (WIFSIGNALED)
    {
        fputc('\n', stderr);
        fputs(program, stderr);
        fputs(" abnormal termination, signal number = ", stderr);
        fputs(int2str(WTERMSIG, 10, TRUE), stderr);
        fputc('\n', stderr);
        if(WCOREDUMP) fputs("core dumped\n", stderr);
        return WTERMSIG;
    }
    else if(WIFSTOPPED)
    {
        fputc('\n', stderr);
        fputs(program, stderr);
        fputs(" child stopped, signal number = ", stderr);
        fputs(int2str(WEXITSTATUS, 10, TRUE), stderr);
        fputc('\n', stderr);
        return WEXITSTATUS;
    }

    fputc('\n', stderr);
    fputs(program, stderr);
    fputs(" :: something crazy happened with execve\nI'm just gonna get the hell out of here\n", stderr);
    exit(EXIT_FAILURE);
}


void _execute(char* name, char** array, char** envp)
{
    int status; /* i.e. return code */
    /* Get the full path to the executable */
    char* program = find_executable(name);

    /* Check we can find the executable */
    if(NULL == program)
    {
        fputs("WHILE EXECUTING ", stderr);
        fputs(name, stderr);
        fputs(" NOT FOUND!\nABORTING HARD\n", stderr);
        exit(EXIT_FAILURE);
    }

    sanity_command_check(array);

    int result;
#ifdef __uefi__
    result = spawn(program, array, envp);
#else
    int f = fork();

    /* Ensure fork succeeded */
    if(f == -1)
    {
        fputs("WHILE EXECUTING ", stderr);
        fputs(name, stderr);
        fputs("fork() FAILED\nABORTING HARD\n", stderr);
        exit(EXIT_FAILURE);
    }
    else if(f == 0)
    {
        /* Child */
        /**************************************************************
         * Fuzzing produces random stuff; we don't want it running    *
         * dangerous commands. So we just don't execve.               *
         **************************************************************/
        if(FALSE == FUZZING)
        {
            /* We are not fuzzing */
            /* execve() returns only on error */
            execve(program, array, envp);
            fputs("Unable to execute: ", stderr);
            fputs(program, stderr);
            fputs("\nPlease check file permissions and that it is a valid binary\n", stderr);
        }

        /* Prevent infinite loops */
        _exit(EXIT_FAILURE);
    }

    /* Otherwise we are the parent */
    /* And we should wait for it to complete */
    waitpid(f, &status, 0);

    result = what_exit(program, status);
#endif
    if(0 != result)
    {
        fputs("Subprocess: ", stderr);
        fputs(program, stderr);
        fputs(" error\nAborting for safety\n", stderr);
        exit(result);
    }
}

void insert_array(char** array, int index, char* string)
{
    int size = strlen(string);
    array[index] = calloc(size+2, sizeof(char));
    strcpy(array[index], string);
}


void spawn_hex2(char* input, char* output, char* architecture, char** envp, int debug)
{
    char* hex2;
#ifdef __uefi__
    hex2 = "hex2.efi";
#else
    hex2 = "hex2";
#endif

    char* elf_header = calloc(MAX_STRING, sizeof(char));
    elf_header = strcat(elf_header, M2LIBC_PATH);
    elf_header = strcat(elf_header, "/");
    elf_header = strcat(elf_header, architecture);
    if(match("UEFI", OperatingSystem)) elf_header = strcat(elf_header, "/uefi/PE32-");
    else elf_header = strcat(elf_header, "/ELF-");
    elf_header = strcat(elf_header, architecture);
    if(debug)
    {
        elf_header = strcat(elf_header, "-debug.hex2");
    }
    else
    {
        elf_header = strcat(elf_header, ".hex2");
    }

    fputs("# starting hex2 linking\n", stdout);
    char** array = calloc(MAX_ARRAY, sizeof(char*));
    insert_array(array, 0, hex2);
    insert_array(array, 1, "--file");
    insert_array(array, 2, elf_header);
    insert_array(array, 3, "--file");
    insert_array(array, 4, input);
    insert_array(array, 5, "--output");
    insert_array(array, 6, output);
    insert_array(array, 7, "--architecture");
    insert_array(array, 8, architecture);
    insert_array(array, 9, "--base-address");
    insert_array(array, 10, BASEADDRESS);
    if(ENDIAN)
    {
        insert_array(array, 11, "--big-endian");
    }
    else
    {
        insert_array(array, 11, "--little-endian");
    }
    _execute(hex2, array, envp);
}


void spawn_M1(char* input, char* debug_file, char* output, char* architecture, char** envp, int debug_flag)
{
    char* M1;
#ifdef __uefi__
    M1 = "M1.efi";
#else
    M1 = "M1";
#endif

    fputs("# starting M1 assembly\n", stdout);
    char* definitions = calloc(MAX_STRING, sizeof(char));
    definitions = strcat(definitions, M2LIBC_PATH);
    definitions = strcat(definitions, "/");
    definitions = strcat(definitions, architecture);
    definitions = strcat(definitions, "/");
    definitions = strcat(definitions, architecture);
    definitions = strcat(definitions, "_defs.M1");

    char* libc = calloc(MAX_STRING, sizeof(char));
    libc = strcat(libc, M2LIBC_PATH);
    libc = strcat(libc, "/");
    libc = strcat(libc, architecture);

    if(match("UEFI", OperatingSystem))
    {
        libc = strcat(libc, "/uefi/libc-full.M1");
    }
    else if(STDIO_USED)
    {
        libc = strcat(libc, "/libc-full.M1");
    }
    else
    {
        libc = strcat(libc, "/libc-core.M1");
    }

    char** array = calloc(MAX_ARRAY, sizeof(char*));
    insert_array(array, 0, M1);
    insert_array(array, 1, "--file");
    insert_array(array, 2, definitions);
    insert_array(array, 3, "--file");
    insert_array(array, 4, libc);
    insert_array(array, 5, "--file");
    insert_array(array, 6, input);
    if(ENDIAN)
    {
        insert_array(array, 7, "--big-endian");
    }
    else
    {
        insert_array(array, 7, "--little-endian");
    }
    insert_array(array, 8, "--architecture");
    insert_array(array, 9, architecture);

    if(debug_flag)
    {
        insert_array(array, 10, "--file");
        insert_array(array, 11, debug_file);
        insert_array(array, 12, "--output");
        insert_array(array, 13, output);
    }
    else
    {
        insert_array(array, 10, "--output");
        insert_array(array, 11, output);
    }
    _execute(M1, array, envp);
}


void spawn_blood_elf(char* input, char* output, char** envp, int large_flag)
{
    char* blood_elf;
#ifdef __uefi__
    blood_elf = "blood-elf.efi";
#else
    blood_elf = "blood-elf";
#endif

    fputs("# starting Blood-elf stub generation\n", stdout);
    char** array = calloc(MAX_ARRAY, sizeof(char*));
    insert_array(array, 0,blood_elf);
    insert_array(array, 1, "--file");
    insert_array(array, 2, input);
    if(ENDIAN)
    {
        insert_array(array, 3, "--big-endian");
    }
    else
    {
        insert_array(array, 3, "--little-endian");
    }
    insert_array(array, 4, "--output");
    insert_array(array, 5, output);
    if(large_flag) insert_array(array, 6, "--64");
    _execute(blood_elf, array, envp);
}

void spawn_M2(char* input, char* output, char* architecture, char** envp, int debug_flag)
{
    char* M2_Planet;
#ifdef __uefi__
    M2_Planet = "M2-Planet.efi";
#else
    M2_Planet = "M2-Planet";
#endif

    fputs("# starting M2-Planet build\n", stdout);
    char** array = calloc(MAX_ARRAY, sizeof(char*));
    insert_array(array, 0, M2_Planet);
    insert_array(array, 1, "--file");
    insert_array(array, 2, input);
    insert_array(array, 3, "--output");
    insert_array(array, 4, output);
    insert_array(array, 5, "--architecture");
    insert_array(array, 6, architecture);
    if(debug_flag) insert_array(array, 7, "--debug");
    _execute(M2_Planet, array, envp);
}

void spawn_processes(int debug_flag, char* prefix, char* preprocessed_file, char* destination, char** envp)
{
    int large_flag = FALSE;
    if(WORDSIZE > 32) large_flag = TRUE;

    if(match("UEFI", OperatingSystem))
    {
        debug_flag = FALSE;
    }

    char* M2_output = calloc(100, sizeof(char));
    strcpy(M2_output, prefix);
    strcat(M2_output, "/M2-Planet-XXXXXX");
    int i = mkstemp(M2_output);
    if(-1 != i)
    {
        close(i);
        spawn_M2(preprocessed_file, M2_output, Architecture, envp, debug_flag);
    }
    else
    {
        fputs("unable to get a tempfile for M2-Planet output\n", stderr);
        exit(EXIT_FAILURE);
    }

    char* blood_output = "";
    if(debug_flag)
    {
        blood_output = calloc(100, sizeof(char));
        strcpy(blood_output, prefix);
        strcat(blood_output, "/blood-elf-XXXXXX");
        i = mkstemp(blood_output);
        if(-1 != i)
        {
            close(i);
            spawn_blood_elf(M2_output, blood_output, envp, large_flag);
        }
        else
        {
            fputs("unable to get a tempfile for blood-elf output\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    char* M1_output = calloc(100, sizeof(char));
    strcpy(M1_output, prefix);
    strcat(M1_output, "/M1-macro-XXXXXX");
    i = mkstemp(M1_output);
    if(-1 != i)
    {
        close(i);
        spawn_M1(M2_output, blood_output, M1_output, Architecture, envp, debug_flag);
    }
    else
    {
        fputs("unable to get a tempfile for M1 output\n", stderr);
        exit(EXIT_FAILURE);
    }

    /* We no longer need the M2-Planet tempfile output */
    if(!DIRTY_MODE) remove(M2_output);
    /* Nor the blood-elf output anymore if it exists */
    if(!match("", blood_output))
    {
        if(!DIRTY_MODE) remove(blood_output);
    }

    /* Build the final binary */
    spawn_hex2(M1_output, destination, Architecture, envp, debug_flag);

    /* clean up after ourselves*/
    if(!DIRTY_MODE) remove(M1_output);
}

https://github.com/oriansj/M2-Mesoplanet/blob/main/cc_core.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc_core.c used in: Step 49
/* Copyright (C) 2016 Jeremiah Orians
 * Copyright (C) 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "cc.h"
#include "gcc_req.h"
#include <stdint.h>

/* Imported functions */
char* int2str(int x, int base, int signed_p);

void line_error_token(struct token_list *token)
{
    if(NULL == token)
    {
        fputs("EOF reached inside of line_error\n", stderr);
        fputs("problem at end of file\n", stderr);
        return;
    }
    fputs(token->filename, stderr);
    fputs(":", stderr);
    fputs(int2str(token->linenumber, 10, TRUE), stderr);
    fputs(":", stderr);
}

void line_error()
{
    line_error_token(global_token);
}

void require_match(char* message, char* required)
{
    require(NULL != global_token, "EOF reached inside of require match\n");
    if(!match(global_token->s, required))
    {
        line_error();
        fputs(message, stderr);
        exit(EXIT_FAILURE);
    }
    global_token = global_token->next;
    require(NULL != global_token, "EOF after require match occurred\n");
}

void output_tokens(struct token_list *i, FILE* out)
{
    while(NULL != i)
    {
        fputs(i->s, out);
        i = i->next;
    }
}

https://github.com/oriansj/M2-Mesoplanet/blob/main/cc_macro.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc_macro.c used in: Step 49
/* Copyright (C) 2021 Sanne Wouda
 * Copyright (C) 2021 Andrius Å tikonas <andrius@stikonas.eu>
 * Copyright (C) 2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "cc.h"
#include "gcc_req.h"

void require(int bool, char* error);
int strtoint(char* a);
void line_error_token(struct token_list* list);
struct token_list* eat_token(struct token_list* head);
struct token_list* reverse_list(struct token_list* head);

struct conditional_inclusion
{
    struct conditional_inclusion* prev;
    int include; /* 1 == include, 0 == skip */
    int previous_condition_matched; /* 1 == all subsequent conditions treated as FALSE */
};

struct macro_list
{
    struct macro_list* next;
    char* symbol;
    struct token_list* expansion;
    struct token_list* arguments;
};

struct macro_list* macro_env;
struct conditional_inclusion* conditional_inclusion_top;

/* point where we are currently modifying the global_token list */
struct token_list* macro_token;

void init_macro_env(char* sym, char* value, char* source, int num)
{
    struct macro_list* hold = macro_env;
    macro_env = calloc(1, sizeof(struct macro_list));
    macro_env->symbol = sym;
    macro_env->next = hold;
    macro_env->expansion = calloc(1, sizeof(struct token_list));
    macro_env->expansion->s = value;
    macro_env->expansion->filename = source;
    macro_env->expansion->linenumber = num;
}

void _eat_current_token(int eat_whitespace)
{
    int update_global_token = FALSE;
    if (macro_token == global_token)
        update_global_token = TRUE;

    macro_token = eat_token(macro_token);
    if(eat_whitespace)
    {
        while (macro_token->s[0] == ' ')
        macro_token = eat_token(macro_token);
    }

    if(update_global_token)
        global_token = macro_token;
}

void eat_current_token()
{
    _eat_current_token(TRUE);
}

void eat_current_token_without_space()
{
    _eat_current_token(FALSE);
}

struct token_list* lookup_token(struct token_list* token, struct token_list* arguments)
{
    if(NULL == token)
    {
        fputs("null token received in token\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct token_list* hold = arguments;

    while (NULL != hold)
    {
        if (match(token->s, hold->s))
        {
            /* found! */
            return hold->expansion;
        }

        hold = hold->next;
    }

    /* not found! */
    return NULL;
}

/* returns the first token inserted; inserts *before* point */
struct token_list* insert_tokens(struct token_list* point, struct token_list* token)
{
    struct token_list* copy;
    struct token_list* first = NULL;

    while (NULL != token)
    {
        copy = calloc(1, sizeof(struct token_list));
        copy->s = token->s;
        copy->filename = token->filename;
        copy->linenumber = token->linenumber;

        if(NULL == first)
        {
            first = copy;
        }

        copy->next = point;

        if (NULL != point)
        {
            copy->prev = point->prev;

            if(NULL != point->prev)
            {
                point->prev->next = copy;
            }

            point->prev = copy;
        }

        token = token->next;
    }

    return first;
}

/* returns the first token inserted; inserts *before* point */
struct token_list* copy_list(struct token_list* token)
{
    struct token_list* copy;
    struct token_list* prev = NULL;

    while (NULL != token)
    {
        copy = calloc(1, sizeof(struct token_list));
        copy->s = token->s;

        copy->next = prev;
        copy->prev = prev;
        prev = copy;
        token = token->next;
    }
    copy = reverse_list(copy);

    return copy;
}

struct macro_list* lookup_macro(struct token_list* token)
{
    if(NULL == token)
    {
        line_error_token(macro_token);
        fputs("null token received in lookup_macro\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct macro_list* hold = macro_env;

    while (NULL != hold)
    {
        if (match(token->s, hold->symbol))
        {
            /* found! */
            return hold;
        }

        hold = hold->next;
    }

    /* not found! */
    return NULL;
}

void remove_macro(struct token_list* token)
{
    if(NULL == token)
    {
        line_error_token(macro_token);
        fputs("received a null in remove_macro\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct macro_list* hold = macro_env;
    struct macro_list* temp;

    /* Deal with the first element */
    if (match(token->s, hold->symbol)) {
        macro_env = hold->next;
        free(hold);
        return;
    }

    /* Remove element form the middle of linked list */
    while (NULL != hold->next)
    {
        if (match(token->s, hold->next->symbol))
        {
            temp = hold->next;
            hold->next = hold->next->next;
            free(temp);
            return;
        }

        hold = hold->next;
    }

    /* nothing to undefine */
    return;
}

int macro_expression();
int macro_variable()
{
    int value = 0;
    struct macro_list* hold = lookup_macro(macro_token);
    if (NULL != hold)
    {
        if(NULL == hold->expansion)
        {
            line_error_token(macro_token);
            fputs("hold->expansion is a null\n", stderr);
            exit(EXIT_FAILURE);
        }
        value = strtoint(hold->expansion->s);
    }
    eat_current_token();
    return value;
}

int macro_number()
{
    int result = strtoint(macro_token->s);
    eat_current_token();
    return result;
}

int macro_primary_expr()
{
    int defined_has_paren = FALSE;
    int hold;
    require(NULL != macro_token, "got an EOF terminated macro primary expression\n");

    if('-' == macro_token->s[0])
    {
        eat_current_token();
        return -macro_primary_expr();
    }
    else if('!' == macro_token->s[0])
    {
        eat_current_token();
        return !macro_primary_expr();
    }
    else if('(' == macro_token->s[0])
    {
        eat_current_token();
        hold = macro_expression();
        require(')' == macro_token->s[0], "missing ) in macro expression\n");
        eat_current_token();
        return hold;
    }
    else if(match("defined", macro_token->s))
    {
        eat_current_token();

        require(NULL != macro_token, "got an EOF terminated macro defined expression\n");

        if('(' == macro_token->s[0])
        {
            defined_has_paren = TRUE;
            eat_current_token();
        }

        if (NULL != lookup_macro(macro_token))
        {
            hold = TRUE;
        }
        else
        {
            hold = FALSE;
        }
        eat_current_token();

        if(TRUE == defined_has_paren)
        {
            if(NULL == macro_token)
            {
                line_error_token(macro_token);
                fputs("unterminated define ( statement\n", stderr);
                exit(EXIT_FAILURE);
            }
            require(')' == macro_token->s[0], "missing close parenthesis for defined()\n");
            eat_current_token();
        }

        return hold;
    }
    else if(in_set(macro_token->s[0], "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"))
    {
        return macro_variable();
    }
    else if(in_set(macro_token->s[0], "0123456789"))
    {
        return macro_number();
    }
    else
    {
        return 0;    /* FIXME: error handling */
    }
}

int macro_additive_expr()
{
    int lhs = macro_primary_expr();
    int hold;

    require(NULL != macro_token, "got an EOF terminated macro additive expression\n");
    if(match("+", macro_token->s))
    {
        eat_current_token();
        return lhs + macro_additive_expr();
    }
    else if(match("-", macro_token->s))
    {
        eat_current_token();
        return lhs - macro_additive_expr();
    }
    else if(match("*", macro_token->s))
    {
        eat_current_token();
        return lhs * macro_additive_expr();
    }
    else if(match("/", macro_token->s))
    {
        eat_current_token();
        hold = macro_additive_expr();
        require(0 != hold, "divide by zero not valid even in C macros\n");
        return lhs / hold;
    }
    else if(match("%", macro_token->s))
    {
        eat_current_token();
        hold = macro_additive_expr();
        require(0 != hold, "modulus by zero not valid even in C macros\n");
        return lhs % hold;
    }
    else if(match(">>", macro_token->s))
    {
        eat_current_token();
        return lhs >> macro_additive_expr();
    }
    else if(match("<<", macro_token->s))
    {
        eat_current_token();
        return lhs << macro_additive_expr();
    }
    else
    {
        return lhs;
    }
}

int macro_relational_expr()
{
    int lhs = macro_additive_expr();

    if(match("<", macro_token->s))
    {
        eat_current_token();
        return lhs < macro_relational_expr();
    }
    else if(match("<=", macro_token->s))
    {
        eat_current_token();
        return lhs <= macro_relational_expr();
    }
    else if(match(">=", macro_token->s))
    {
        eat_current_token();
        return lhs >= macro_relational_expr();
    }
    else if(match(">", macro_token->s))
    {
        eat_current_token();
        return lhs > macro_relational_expr();
    }
    else if(match("==", macro_token->s))
    {
        eat_current_token();
        return lhs == macro_relational_expr();
    }
    else if(match("!=", macro_token->s))
    {
        eat_current_token();
        return lhs != macro_relational_expr();
    }
    else
    {
        return lhs;
    }
}

int macro_bitwise_expr()
{
    int rhs;
    int lhs = macro_relational_expr();

    if(match("&", macro_token->s))
    {
        eat_current_token();
        return lhs & macro_bitwise_expr();
    }
    else if(match("&&", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs && rhs;
    }
    else if(match("|", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs | rhs;
    }
    else if(match("||", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs || rhs;
    }
    else if(match("^", macro_token->s))
    {
        eat_current_token();
        rhs = macro_bitwise_expr();
        return lhs ^ rhs;
    }
    else
    {
        return lhs;
    }
}

int macro_expression()
{
    return macro_bitwise_expr();
}

void handle_define()
{
    struct macro_list* hold;
    struct token_list* arg;
    struct token_list* expansion_end = NULL;

    /* don't use #define statements from non-included blocks */
    int conditional_define = TRUE;
    if(NULL != conditional_inclusion_top)
    {
        if(FALSE == conditional_inclusion_top->include)
        {
            conditional_define = FALSE;
        }
    }

    eat_current_token();

    require(NULL != macro_token, "got an EOF terminated #define\n");
    require('\n' != macro_token->s[0], "unexpected newline after #define\n");

    /* insert new macro */
    hold = calloc(1, sizeof(struct macro_list));
    hold->symbol = macro_token->s;
    hold->next = macro_env;
    /* provided it isn't in a non-included block */
    if(conditional_define) macro_env = hold;

    /* discard the macro name */
    eat_current_token_without_space();

    /* Handle macro arguments */
    if(macro_token->s[0] == '(')
    {
        /* discard ( */
        eat_current_token();
        require(NULL != macro_token, "got an EOF terminated #define\n");
        if(macro_token->s[0] != ')')
        {
            arg = calloc(1, sizeof(struct token_list));
            arg->s = macro_token->s;
            hold->arguments = arg;
            eat_current_token();
            require(NULL != macro_token, "incomplete macro call\n");
            while(macro_token->s[0] == ',')
            {
                eat_current_token();
                require(NULL != macro_token, "incomplete macro call, got an EOF instead of an argument\n");
                arg = calloc(1, sizeof(struct token_list));
                arg->s = macro_token->s;
                arg->next = hold->arguments;
                hold->arguments = arg;
                eat_current_token();
                require(NULL != macro_token, "incomplete macro call\n");
            }
        }
        eat_current_token();

        /* Reverse argument list */
        hold->arguments = reverse_list(hold->arguments);

        require(NULL != macro_token, "got an EOF terminated #define\n");
    }
    else if(macro_token->s[0] == ' ')
    {
        eat_current_token();
    }

    while (TRUE)
    {
        require(NULL != macro_token, "got an EOF terminated #define\n");

        if ('\n' == macro_token->s[0])
        {
            if(NULL == expansion_end)
            {
                hold->expansion = NULL;
                expansion_end = macro_token;
                return;
            }
            expansion_end->next = NULL;
            return;
        }
        else if(('/' == macro_token->s[0]) && ('*' == macro_token->s[1]))
        {
            eat_current_token();
            continue;
        }
        else if(('/' == macro_token->s[0]) && ('/' == macro_token->s[1]))
        {
            macro_token->s = "\n";
            if(NULL == expansion_end)
            {
                hold->expansion = NULL;
                expansion_end = macro_token;
                return;
            }
            expansion_end->next = NULL;
            return;
        }

        if(NULL == hold)
        {
            eat_current_token();
            continue;
        }

        expansion_end = macro_token;

        /* in the first iteration, we set the first token of the expansion, if
           it exists */
        if (NULL == hold->expansion)
        {
            hold->expansion = macro_token;
        }

        /* throw away if not used */
        if(!conditional_define && (NULL != hold))
        {
            free(hold);
            hold = NULL;
        }

        eat_current_token();
    }
}

void handle_undef()
{
    eat_current_token();
    remove_macro(macro_token);
    eat_current_token();
}

void handle_error(int warning_p)
{
    /* don't use #error statements from non-included blocks */
    int conditional_error = TRUE;
    if(NULL != conditional_inclusion_top)
    {
        if(FALSE == conditional_inclusion_top->include)
        {
            conditional_error = FALSE;
        }
    }
    eat_current_token();
    /* provided it isn't in a non-included block */
    if(conditional_error)
    {
        line_error_token(macro_token);
        if(warning_p) fputs(" warning: #warning ", stderr);
        else fputs(" error: #error ", stderr);
        while (TRUE)
        {
            if ('\n' == macro_token->s[0]) break;
            fputs(macro_token->s, stderr);
            macro_token = macro_token->next;
        }
        fputs("\n", stderr);
        if(!warning_p) exit(EXIT_FAILURE);
    }
    while (TRUE)
    {
        /* discard the error */
        if ('\n' == macro_token->s[0])
        {
            return;
        }
        eat_current_token();
    }
}

void macro_directive()
{
    struct conditional_inclusion *t;
    int result;

    /* FIXME: whitespace is allowed between "#"" and "if" */
    if(match("#if", macro_token->s))
    {
        eat_current_token();
        /* evaluate constant integer expression */
        result = macro_expression();
        /* push conditional inclusion */
        t = calloc(1, sizeof(struct conditional_inclusion));
        t->prev = conditional_inclusion_top;
        conditional_inclusion_top = t;
        t->include = TRUE;

        if(FALSE == result)
        {
            t->include = FALSE;
        }

        t->previous_condition_matched = t->include;
    }
    else if(match("#ifdef", macro_token->s))
    {
        eat_current_token();
        require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
        if (NULL != lookup_macro(macro_token))
        {
            result = TRUE;
        }
        else
        {
            result = FALSE;
        }
        eat_current_token();

        /* push conditional inclusion */
        t = calloc(1, sizeof(struct conditional_inclusion));
        t->prev = conditional_inclusion_top;
        conditional_inclusion_top = t;
        t->include = TRUE;

        if(FALSE == result)
        {
            t->include = FALSE;
        }

        t->previous_condition_matched = t->include;
    }
    else if(match("#ifndef", macro_token->s))
    {
        eat_current_token();
        require(NULL != macro_token, "got an EOF terminated macro defined expression\n");
        if (NULL != lookup_macro(macro_token))
        {
            result = FALSE;
        }
        else
        {
            result = TRUE;
        }
        eat_current_token();

        /* push conditional inclusion */
        t = calloc(1, sizeof(struct conditional_inclusion));
        t->prev = conditional_inclusion_top;
        conditional_inclusion_top = t;
        t->include = TRUE;

        if(FALSE == result)
        {
            t->include = FALSE;
        }

        t->previous_condition_matched = t->include;
    }
    else if(match("#elif", macro_token->s))
    {
        eat_current_token();
        result = macro_expression();
        require(NULL != conditional_inclusion_top, "#elif without leading #if\n");
        conditional_inclusion_top->include = result && !conditional_inclusion_top->previous_condition_matched;
        conditional_inclusion_top->previous_condition_matched =
            conditional_inclusion_top->previous_condition_matched || conditional_inclusion_top->include;
    }
    else if(match("#else", macro_token->s))
    {
        eat_current_token();
        require(NULL != conditional_inclusion_top, "#else without leading #if\n");
        conditional_inclusion_top->include = !conditional_inclusion_top->previous_condition_matched;
    }
    else if(match("#endif", macro_token->s))
    {
        if(NULL == conditional_inclusion_top)
        {
            line_error_token(macro_token);
            fputs("unexpected #endif\n", stderr);
            exit(EXIT_FAILURE);
        }

        eat_current_token();
        /* pop conditional inclusion */
        t = conditional_inclusion_top;
        conditional_inclusion_top = conditional_inclusion_top->prev;
        free(t);
    }
    else if(match("#define", macro_token->s))
    {
        handle_define();
    }
    else if(match("#undef", macro_token->s))
    {
        handle_undef();
    }
    else if(match("#error", macro_token->s))
    {
        handle_error(FALSE);
    }
    else if(match("#warning", macro_token->s))
    {
        handle_error(TRUE);
    }
    else if(match("#FILENAME", macro_token->s))
    {
        while(TRUE)
        {
            if(NULL == macro_token)
            {
                return;
            }

            if('\n' == macro_token->s[0])
            {
                return;
            }

            eat_current_token();
        }
    }
    else
    {
        /* Put a big fat warning but see if we can just ignore */
        fputs(">>WARNING<<\n>>WARNING<<\n", stderr);
        line_error_token(macro_token);
        fputs("feature: ", stderr);
        fputs(macro_token->s, stderr);
        fputs(" unsupported in M2-Planet\nIgnoring line, may result in bugs\n>>WARNING<<\n>>WARNING<<\n\n", stderr);

        /* unhandled macro directive; let's eat until a newline; om nom nom */
        while(TRUE)
        {
            if(NULL == macro_token)
            {
                return;
            }

            if('\n' == macro_token->s[0])
            {
                return;
            }

            eat_current_token();
        }
    }
}

struct token_list* expand_macro_functions(struct token_list* expansion, struct token_list* arguments)
{
    struct token_list* expanded_token;
    struct token_list* head;
    struct token_list* hold; /* Same as head unless head == NULL */
    head = copy_list(expansion);
    while(NULL != head)
    {
        expanded_token = lookup_token(head, arguments);
        hold = head;
        if(NULL != expanded_token)
        {
            insert_tokens(head, expanded_token);
            hold = head->prev;
            head = eat_token(head);
        }
        else
        {
            head = head->next;
        }
    }
    while(NULL != hold->prev) hold = hold->prev;
    return hold;
}

void eat_until_endif()
{
    /* This #if block is nested inside of an #if block that needs to be dropped, lose EVERYTHING */
    do
    {
        if(match("#if", macro_token->s) || match("#ifdef", macro_token->s) || match("#ifndef", macro_token->s))
        {
            eat_current_token();
            eat_until_endif();
        }

        eat_current_token();
        require(NULL != macro_token, "Unterminated #if block\n");
    } while(!match("#endif", macro_token->s));
}

void eat_block()
{
    /* This conditional #if block is wrong, drop everything until the #elif/#else/#endif */
    do
    {
        if(match("#if", macro_token->s) || match("#ifdef", macro_token->s) || match("#ifndef", macro_token->s))
        {
            eat_current_token();
            eat_until_endif();
        }

        eat_current_token();
        require(NULL != macro_token, "Unterminated #if block\n");
    } while(!match("#elif", macro_token->s) && !match("#else", macro_token->s) && !match("#endif", macro_token->s));
}

struct token_list* maybe_expand(struct token_list* token)
{
    if(NULL == token)
    {
        line_error_token(macro_token);
        fputs("maybe_expand passed a null token\n", stderr);
        exit(EXIT_FAILURE);
    }

    struct macro_list* hold = lookup_macro(token);
    struct token_list* hold2;
    struct token_list* hold3;
    struct token_list* hold4;
    if(NULL == token->next)
    {
        line_error_token(macro_token);
        fputs("we can't expand a null token: ", stderr);
        fputs(token->s, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }

    if (NULL == hold)
    {
        return token->next;
    }

    if(match("__M2__", token->s)) return token->next;

    token = eat_token(token);

    if (NULL == hold->expansion)
    {
        return token->next;
    }

    /* Match macro arguments with stored names */
    hold3 = hold->arguments;
    if(NULL != hold3)
    {
        if(token->s[0] == ' ')
        {
            token = eat_token(token);
        }
        require('(' == token->s[0], "missing open parenthesis for macro function\n");
        token = eat_token(token);
        require(NULL != token, "got an EOF terminated macro function\n");
        do
        {
            hold2 = calloc(1, sizeof(struct token_list));
            hold2->s = token->s;
            hold2->next = hold->arguments->expansion;
            hold->arguments->expansion = hold2;
            token = eat_token(token);
            require(NULL != token, "incomplete macro call\n");
            if(token->s[0] == ',')
            {
                hold->arguments->expansion = reverse_list(hold->arguments->expansion);
                hold->arguments = hold->arguments->next;
                require(NULL != hold->arguments, "too many arguments in macro call\n");
                token = eat_token(token);
                require(NULL != token, "incomplete macro call\n");
            }
        } while(token->s[0] != ')');
        hold->arguments->expansion = reverse_list(hold->arguments->expansion);
        hold->arguments = hold3;
        token = eat_token(token);
    }
    hold4 = expand_macro_functions(hold->expansion, hold->arguments);
    hold4 = insert_tokens(token, hold4);

    return hold4;
}

void preprocess()
{
    int start_of_line = TRUE;
    macro_token = global_token;

    while(NULL != macro_token)
    {
        if(start_of_line && '#' == macro_token->s[0])
        {
            macro_directive();

            if(macro_token)
            {
                if('\n' != macro_token->s[0])
                {
                    line_error_token(macro_token);
                    fputs("newline expected at end of macro directive\n", stderr);
                    fputs("found: '", stderr);
                    fputs(macro_token->s, stderr);
                    fputs("'\n", stderr);
                    exit(EXIT_FAILURE);
                }
            }
        }
        else if('\n' == macro_token->s[0])
        {
            start_of_line = TRUE;
            macro_token = macro_token->next;
        }
        else
        {
            start_of_line = FALSE;
            if(NULL == conditional_inclusion_top)
            {
                macro_token = maybe_expand(macro_token);
            }
            else if(!conditional_inclusion_top->include)
            {
                /* rewrite the token stream to exclude the current token */
                eat_block();
                start_of_line = TRUE;
            }
            else
            {
                macro_token = maybe_expand(macro_token);
            }
        }
    }
}

https://github.com/oriansj/M2-Mesoplanet/blob/main/cc.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2-Mesoplanet/cc.c used in: Step 49
/* Copyright (C) 2016, 2021 Jeremiah Orians
 * Copyright (C) 2020 deesix <deesix@tuta.io>
 * Copyright (C) 2020 Gabriel Wicki
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#include"cc.h"
#include <unistd.h>

/* The core functions */
void populate_env(char** envp);
void setup_env();
char* env_lookup(char* variable);
void initialize_types();
struct token_list* read_all_tokens(FILE* a, struct token_list* current, char* filename, int include);
struct token_list* reverse_list(struct token_list* head);

void init_macro_env(char* sym, char* value, char* source, int num);
void preprocess();
void output_tokens(struct token_list *i, FILE* out);
int strtoint(char *a);
void spawn_processes(int debug_flag, char* prefix, char* preprocessed_file, char* destination, char** envp);

int follow_includes;

void prechecks(int argc, char** argv)
{
    int env = 0;
    char* hold;
    int i = 1;
    while(i <= argc)
    {
        if(NULL == argv[i])
        {
            i += 1;
        }
        else if(match(argv[i], "--debug-mode"))
        {
            hold = argv[i+1];
            require(NULL != hold, "--debug-mode requires an argument\n");
            DEBUG_LEVEL = strtoint(hold);
            if(0 == DEBUG_LEVEL)
            {
                require(match("0", hold), "--debug-mode values must be numbers\n"
                                          "and level 0 needed to be expressed as 0\n");
            }
            fputs("DEBUG_LEVEL set to: ", stderr);
            fputs(hold, stderr);
            fputc('\n', stderr);
            i+= 2;
        }
        else if(match(argv[i], "-A") || match(argv[i], "--architecture"))
        {
            hold = argv[i+1];
            require(NULL != hold, "--architecture needs to be passed an architecture\n");
            Architecture = hold;
            i += 2;
        }
        else if(match(argv[i], "--os") || match(argv[i], "--operating-system"))
        {
            hold = argv[i+1];
            require(NULL != hold, "--operating-system needs to be passed an operating system\n");
            OperatingSystem = hold;
            i += 2;
        }
        else if(match(argv[i], "--max-string"))
        {
            hold = argv[i+1];
            require(NULL != hold, "--max-string requires a numeric argument\n");
            MAX_STRING = strtoint(hold);
            require(0 < MAX_STRING, "Not a valid string size\nAbort and fix your --max-string\n");
            i += 2;
        }
        else if(match(argv[i], "--no-includes"))
        {
            follow_includes = FALSE;
            i+= 1;
        }
        else if(match(argv[i], "-I"))
        {
            hold = argv[i+1];
            if(NULL == hold)
            {
                fputs("-I requires a PATH\n", stderr);
                exit(EXIT_FAILURE);
            }
            if(1 <= DEBUG_LEVEL)
            {
                fputs("M2LIBC_PATH set by -I to ", stderr);
                fputs(hold, stderr);
                fputc('\n', stderr);
            }
            M2LIBC_PATH = hold;
            i += 2;
        }
        else if(match(argv[i], "-D"))
        {
            hold = argv[i+1];
            if(NULL == hold)
            {
                fputs("-D requires an argument", stderr);
                exit(EXIT_FAILURE);
            }
            while(0 != hold[0])
            {
                if('=' == hold[0])
                {
                    hold[0] = 0;
                    hold = hold + 1;
                    break;
                }
                hold = hold + 1;
            }
            init_macro_env(argv[i+1], hold, "__ARGV__", env);
            env = env + 1;
            i += 2;
        }
        else
        {
            i += 1;
        }
    }
}

int main(int argc, char** argv, char** envp)
{
    /****************************************************************************
     * Zero means no debugging messages and larger positive values means more   *
     * chatty output. Level 15 means EVERYTHING but 7 should cover most magic   *
     ****************************************************************************/
    DEBUG_LEVEL = 0;
    /* Setup __M2__ (It is very very special *DO NOT MESS WITH IT* ) */
    init_macro_env("__M2__", "__M2__", "__INTERNAL_M2__", 0);

    /* Our fun globals */
    FUZZING = FALSE;
    MAX_STRING = 65536;
    PREPROCESSOR_MODE = FALSE;
    STDIO_USED = FALSE;
    DIRTY_MODE = FALSE;
    Architecture = NULL;
    OperatingSystem = NULL;

    /* Our fun locals */
    int debug_flag = TRUE;
    FILE* in = stdin;
    FILE* tempfile;
    char* destination_name = "a.out";
    FILE* destination_file = stdout;
    char* name;
    int DUMP_MODE = FALSE;
    follow_includes = TRUE;

    /* Try to get our needed updates */
    prechecks(argc, argv);

    /* Get the environmental bits */
    if(1 <= DEBUG_LEVEL) fputs("Starting to setup Environment\n", stderr);
    populate_env(envp);
    setup_env();
    if(1 <= DEBUG_LEVEL) fputs("Environment setup\n", stderr);

    M2LIBC_PATH = env_lookup("M2LIBC_PATH");
    if(NULL == M2LIBC_PATH) M2LIBC_PATH = "./M2libc";
    else if(1 <= DEBUG_LEVEL)
    {
        fputs("M2LIBC_PATH set by environment variable to ", stderr);
        fputs(M2LIBC_PATH, stderr);
        fputc('\n', stderr);
    }

    TEMPDIR = env_lookup("TMPDIR");
    if(NULL == TEMPDIR) TEMPDIR = "/tmp";
    else if(1 <= DEBUG_LEVEL)
    {
        fputs("TEMPDIR set by environment variable to ", stderr);
        fputs(TEMPDIR, stderr);
        fputc('\n', stderr);
    }

    int i = 1;
    while(i <= argc)
    {
        if(NULL == argv[i])
        {
            i += 1;
        }
        else if(match(argv[i], "-E") || match(argv[i], "--preprocess-only"))
        {
            PREPROCESSOR_MODE = TRUE;
            i += 1;
        }
        else if(match(argv[i], "--dump-mode"))
        {
            DUMP_MODE = TRUE;
            i+= 1;
        }
        else if(match(argv[i], "--dirty-mode"))
        {
            DIRTY_MODE = TRUE;
            i+= 1;
        }
        else if(match(argv[i], "--no-includes"))
        {
            /* Handled by precheck*/
            i+= 1;
        }
        else if(match(argv[i], "--debug-mode"))
        {
            /* Handled by precheck */
            i+= 2;
        }
        else if(match(argv[i], "-A") || match(argv[i], "--architecture"))
        {
            /* Handled by precheck */
            i += 2;
        }
        else if(match(argv[i], "--os") || match(argv[i], "--operating-system"))
        {
            /* Handled by precheck */
            i += 2;
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            if(NULL == hold_string)
            {
                hold_string = calloc(MAX_STRING + 4, sizeof(char));
                require(NULL != hold_string, "Impossible Exhaustion has occured\n");
            }

            name = argv[i + 1];
            if(NULL == name)
            {
                fputs("did not receive a file name\n", stderr);
                exit(EXIT_FAILURE);
            }

            in = fopen(name, "r");
            if(NULL == in)
            {
                fputs("Unable to open for reading file: ", stderr);
                fputs(name, stderr);
                fputs("\n Aborting to avoid problems\n", stderr);
                exit(EXIT_FAILURE);
            }
            global_token = read_all_tokens(in, global_token, name, follow_includes);
            fclose(in);
            i += 2;
        }
        else if(match(argv[i], "-o") || match(argv[i], "--output"))
        {
            destination_name = argv[i + 1];
            require(NULL != destination_name, "--output option requires a filename to follow\n");
            destination_file = fopen(destination_name, "w");
            if(NULL == destination_file)
            {
                fputs("Unable to open for writing file: ", stderr);
                fputs(argv[i + 1], stderr);
                fputs("\n Aborting to avoid problems\n", stderr);
                exit(EXIT_FAILURE);
            }
            i += 2;
        }
        else if(match(argv[i], "--max-string"))
        {
            /* handled by precheck */
            i += 2;
        }
        else if(match(argv[i], "-I"))
        {
            /* Handled by precheck */
            i += 2;
        }
        else if(match(argv[i], "-D"))
        {
            /* Handled by precheck */
            i += 2;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs(" -f input file\n -o output file\n --help for this message\n --version for file version\n-E or --preprocess-only\n--max-string N (N is a number)\n--fuzz\n--no-debug\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-V") || match(argv[i], "--version"))
        {
            fputs("M2-Mesoplanet v1.10.0\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "--fuzz"))
        {
            /* Set fuzzing */
            FUZZING = TRUE;
            i += 1;
        }
        else if(match(argv[i], "--no-debug"))
        {
            /* strip things down */
            debug_flag = FALSE;
            i += 1;
        }
        else if(match(argv[i], "--temp-directory"))
        {
            name = argv[i+1];
            if(NULL == name)
            {
                fputs("--temp-directory requires a PATH\n", stderr);
                exit(EXIT_FAILURE);
            }
            if(1 <= DEBUG_LEVEL)
            {
                fputs("TEMPDIR set by --temp-directory to ", stderr);
                fputs(name, stderr);
                fputc('\n', stderr);
            }
            TEMPDIR = name;
            i += 2;
        }
        else
        {
            if(5 <= DEBUG_LEVEL)
            {
                fputs("on index: ", stderr);
                fputs(int2str(i, 10, TRUE), stderr);
                fputc('\n', stderr);
            }
            fputs("UNKNOWN ARGUMENT: ", stdout);
            fputs(argv[i], stdout);
            fputc('\n', stdout);
            exit(EXIT_FAILURE);
        }
    }

    if(1 <= DEBUG_LEVEL) fputs("READ all files\n", stderr);

    /* Deal with special case of wanting to read from standard input */
    if(stdin == in)
    {
        hold_string = calloc(MAX_STRING, sizeof(char));
        require(NULL != hold_string, "Impossible Exhaustion has occured\n");
        global_token = read_all_tokens(in, global_token, "STDIN", follow_includes);
    }

    if(NULL == global_token)
    {
        fputs("Either no input files were given or they were empty\n", stderr);
        exit(EXIT_FAILURE);
    }

    if(1 <= DEBUG_LEVEL) fputs("Start to reverse list\n", stderr);
    global_token = reverse_list(global_token);
    if(1 <= DEBUG_LEVEL) fputs("List reversed\n", stderr);

    if(DUMP_MODE)
    {
        output_tokens(global_token, destination_file);
        exit(EXIT_SUCCESS);
    }

    preprocess();

    if(PREPROCESSOR_MODE)
    {
        fputs("/* M2-Mesoplanet Preprocessed source */\n", destination_file);
        output_tokens(global_token, destination_file);
        fclose(destination_file);
    }
    else
    {
        /* Ensure we can write to the temp directory */
        int permissions = access(TEMPDIR, 0);
        if(0 != permissions)
        {
            fputs("unable to access: ", stderr);
            fputs(TEMPDIR, stderr);
            fputs(" for use as a temp directory\nPlease use --temp-directory to set a directory you can use or set the TMPDIR variable\n", stderr);
            exit(EXIT_FAILURE);
        }

        name = calloc(100, sizeof(char));
        strcpy(name, TEMPDIR);
        strcat(name, "/M2-Mesoplanet-XXXXXX");
        i = mkstemp(name);
        tempfile = fdopen(i, "w");
        if(NULL != tempfile)
        {
            /* Our preprocessed crap */
            output_tokens(global_token, tempfile);
            fclose(tempfile);

            /* Make me a real binary */
            spawn_processes(debug_flag, TEMPDIR, name, destination_name, envp);

            /* And clean up the donkey */
            if(!DIRTY_MODE) remove(name);
        }
        else
        {
            fputs("unable to get a tempfile for M2-Mesoplanet output\n", stderr);
            exit(EXIT_FAILURE);
        }
    }
    return EXIT_SUCCESS;
}

https://git.savannah.nongnu.org/gitweb/?p=mescc-tools.git;a=blob;f=get_machine.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools/get_machine.c used in: Step 57
/* -*- c-file-style: "linux";indent-tabs-mode:t -*- */
/* Copyright (C) 2017 Jeremiah Orians
 * This file is part of mescc-tools.
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */


#include <stdio.h>
#include <stdlib.h>
#include <sys/utsname.h>
int match(char* a, char* b);

#define TRUE 1
//CONSTANT TRUE 1
#define FALSE 0
//CONSTANT FALSE 0

/* Standard C main program */
int main(int argc, char **argv)
{
    int exact = FALSE;
    int override = FALSE;
    char* override_string;
    int option_index = 1;

    struct utsname* unameData = calloc(1, sizeof(struct utsname));
    uname(unameData);

    while(option_index <= argc)
    {
        if(NULL == argv[option_index])
        {
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--exact"))
        {
            exact = TRUE;
            option_index = option_index + 1;
        }
        else if(match(argv[option_index], "--override"))
        {
            override = TRUE;
            if((option_index + 1) < argc)
            {
                override_string = argv[option_index + 1];
                option_index = option_index + 2;
            }
            else
            {
                fputs("--override requires an actual override string\n", stderr);
                exit(EXIT_FAILURE);
            }
        }
        else if(match(argv[option_index], "--os") || match(argv[option_index], "--OS"))
        {
            if(override) fputs(override_string, stdout);
            else fputs(unameData->sysname, stdout);
            fputc('\n', stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "--blood"))
        {
            if(override) fputs(override_string, stdout);
            else if(match("aarch64", unameData->machine)
                 || match("amd64", unameData->machine)
                 || match("ppc64le", unameData->machine)
                 || match("riscv64", unameData->machine)
                 || match("x86_64", unameData->machine)) fputs("--64", stdout);
            fputc('\n', stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "--endian"))
        {
            if(override) fputs(override_string, stdout);
            else if(match("aarch64", unameData->machine)
                 || match("amd64", unameData->machine)
                 || match("ppc64le", unameData->machine)
                 || match("riscv64", unameData->machine)
                 || match("x86_64", unameData->machine)
                 || match("i386", unameData->machine)
                 || match("i486", unameData->machine)
                 || match("i586", unameData->machine)
                 || match("i686", unameData->machine)
                 || match("i686-pae", unameData->machine))fputs("--little-endian", stdout);
            else fputs("--big-endian", stdout);
            fputc('\n', stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "--hex2"))
        {
            if(override) fputs(override_string, stdout);
            else if(match("aarch64", unameData->machine)) fputs("0x400000", stdout);
            else if(match("armv7l", unameData->machine)) fputs("0x10000", stdout);
            else if(match("amd64", unameData->machine)
                 || match("x86_64", unameData->machine)) fputs("0x600000", stdout);
            else if(match("ppc64le", unameData->machine)) fputs("0x10000", stdout);
            else if(match("riscv64", unameData->machine)) fputs("0x600000", stdout);
            else if(match("i386", unameData->machine)
                 || match("i486", unameData->machine)
                 || match("i586", unameData->machine)
                 || match("i686", unameData->machine)
                 || match("i686-pae", unameData->machine)) fputs("0x08048000", stdout);
            else fputs("0x0", stdout);
            fputc('\n', stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "-V") || match(argv[option_index], "--version"))
        {
            fputs("get_machine 1.4.0\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[option_index], "-h") || match(argv[option_index], "--help"))
        {
            fputs("If you want exact architecture use --exact\n", stderr);
            fputs("If you want to know the Operating system use --os\n", stderr);
            fputs("If you wish to override the output to anything you want use --override\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    if(override) fputs(override_string, stdout);
    else if(!exact)
    {
        if(match("i386", unameData->machine) ||
           match("i486", unameData->machine) ||
           match("i586", unameData->machine) ||
           match("i686", unameData->machine) ||
           match("i686-pae", unameData->machine)) fputs("x86", stdout);
        else if(match("x86_64", unameData->machine)) fputs("amd64", stdout);
        else fputs(unameData->machine, stdout);
    }
    else fputs(unameData->machine, stdout);
    fputs("\n", stdout);
    return EXIT_SUCCESS;
}

https://github.com/oriansj/mescc-tools-extra/blob/main/sha256sum.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/sha256sum.c used in: Step 66
/* Copyright (C) 2021 Bastian Bittorf <bb@npl.de>
 * Copyright (C) 2021 Alain Mosnier <alain@wanamoon.net>
 * Copyright (C) 2017-2021 Jan Venekamp
 * Copyright (C) 2021 Jeremiah Orians
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "M2libc/bootstrappable.h"

#define CHUNK_SIZE 64
#define TOTAL_LEN_LEN 8

int mask;

/*
 * Initialize array of round constants:
 * (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311):
 */
unsigned* init_k()
{
    unsigned* k = calloc(65, sizeof(unsigned));
    k[0] = 0x428a2f98;
    k[1] = 0x71374491;
    k[2] = 0xb5c0fbcf;
    k[3] = 0xe9b5dba5;
    k[4] = 0x3956c25b;
    k[5] = 0x59f111f1;
    k[6] = 0x923f82a4;
    k[7] = 0xab1c5ed5;
    k[8] = 0xd807aa98;
    k[9] = 0x12835b01;
    k[10] = 0x243185be;
    k[11] = 0x550c7dc3;
    k[12] = 0x72be5d74;
    k[13] = 0x80deb1fe;
    k[14] = 0x9bdc06a7;
    k[15] = 0xc19bf174;
    k[16] = 0xe49b69c1;
    k[17] = 0xefbe4786;
    k[18] = 0x0fc19dc6;
    k[19] = 0x240ca1cc;
    k[20] = 0x2de92c6f;
    k[21] = 0x4a7484aa;
    k[22] = 0x5cb0a9dc;
    k[23] = 0x76f988da;
    k[24] = 0x983e5152;
    k[25] = 0xa831c66d;
    k[26] = 0xb00327c8;
    k[27] = 0xbf597fc7;
    k[28] = 0xc6e00bf3;
    k[29] = 0xd5a79147;
    k[30] = 0x06ca6351;
    k[31] = 0x14292967;
    k[32] = 0x27b70a85;
    k[33] = 0x2e1b2138;
    k[34] = 0x4d2c6dfc;
    k[35] = 0x53380d13;
    k[36] = 0x650a7354;
    k[37] = 0x766a0abb;
    k[38] = 0x81c2c92e;
    k[39] = 0x92722c85;
    k[40] = 0xa2bfe8a1;
    k[41] = 0xa81a664b;
    k[42] = 0xc24b8b70;
    k[43] = 0xc76c51a3;
    k[44] = 0xd192e819;
    k[45] = 0xd6990624;
    k[46] = 0xf40e3585;
    k[47] = 0x106aa070;
    k[48] = 0x19a4c116;
    k[49] = 0x1e376c08;
    k[50] = 0x2748774c;
    k[51] = 0x34b0bcb5;
    k[52] = 0x391c0cb3;
    k[53] = 0x4ed8aa4a;
    k[54] = 0x5b9cca4f;
    k[55] = 0x682e6ff3;
    k[56] = 0x748f82ee;
    k[57] = 0x78a5636f;
    k[58] = 0x84c87814;
    k[59] = 0x8cc70208;
    k[60] = 0x90befffa;
    k[61] = 0xa4506ceb;
    k[62] = 0xbef9a3f7;
    k[63] = 0xc67178f2;
    return k;
}

unsigned* init_h()
{
    unsigned* h = calloc(9, sizeof(unsigned));
    h[0] = 0x6a09e667;
    h[1] = 0xbb67ae85;
    h[2] = 0x3c6ef372;
    h[3] = 0xa54ff53a;
    h[4] = 0x510e527f;
    h[5] = 0x9b05688c;
    h[6] = 0x1f83d9ab;
    h[7] = 0x5be0cd19;
    return h;
}

struct buffer_state
{
    char* p;
    size_t len;
    size_t total_len;
    int single_one_delivered; /* bool */
    int total_len_delivered; /* bool */
};

unsigned right_rot(unsigned value, unsigned count)
{
    /*
     * Defined behaviour in standard C for all count where 0 < count < 32,
     * which is what we need here.
     */

    value &= mask;
    int hold1 = (value >> count) & mask;
    int hold2 = (value << (32 - count)) & mask;
    int hold = (hold1 | hold2) & mask;
    return hold;
}

void init_buf_state(struct buffer_state * state, char* input, size_t len)
{
    state->p = input;
    state->len = len;
    state->total_len = len;
    state->single_one_delivered = 0;
    state->total_len_delivered = 0;
}

/* Return value: bool */
int calc_chunk(char* chunk, struct buffer_state * state)
{
    size_t space_in_chunk;

    if(state->total_len_delivered)
    {
        return 0;
    }

    if(state->len >= CHUNK_SIZE)
    {
        memcpy(chunk, state->p, CHUNK_SIZE);
        state->p += CHUNK_SIZE;
        state->len -= CHUNK_SIZE;
        return 1;
    }

    memcpy(chunk, state->p, state->len);
    chunk += state->len;
    space_in_chunk = CHUNK_SIZE - state->len;
    state->p += state->len;
    state->len = 0;

    /* If we are here, space_in_chunk is one at minimum. */
    if(!state->single_one_delivered)
    {
        chunk[0] = 0x80;
        chunk += 1;
        space_in_chunk -= 1;
        state->single_one_delivered = 1;
    }

    /*
     * Now:
     * - either there is enough space left for the total length, and we can conclude,
     * - or there is too little space left, and we have to pad the rest of this chunk with zeroes.
     * In the latter case, we will conclude at the next invocation of this function.
     */
    if(space_in_chunk >= TOTAL_LEN_LEN)
    {
        size_t left = space_in_chunk - TOTAL_LEN_LEN;
        size_t len = state->total_len;
        int i;
        memset(chunk, 0x00, left);
        chunk += left;
        /* Storing of len * 8 as a big endian 64-bit without overflow. */
        chunk[7] = (len << 3);
        len >>= 5;

        for(i = 6; i >= 0; i -= 1)
        {
            chunk[i] = len;
            len >>= 8;
        }

        state->total_len_delivered = 1;
    }
    else
    {
        memset(chunk, 0x00, space_in_chunk);
    }

    return 1;
}

/*
 * Limitations:
 * - Since input is a pointer in RAM, the data to hash should be in RAM, which could be a problem
 *   for large data sizes.
 * - SHA algorithms theoretically operate on bit strings. However, this implementation has no support
 *   for bit string lengths that are not multiples of eight, and it really operates on arrays of bytes.
 *   In particular, the len parameter is a number of bytes.
 */
void calc_sha_256(char* hash, char* input, size_t len)
{
    /*
     * Note 1: All integers (expect indexes) are 32-bit unsigned integers and addition is calculated modulo 2^32.
     * Note 2: For each round, there is one round constant k[i] and one entry in the message schedule array w[i], 0 = i = 63
     * Note 3: The compression function uses 8 working variables, a through h
     * Note 4: Big-endian convention is used when expressing the constants in this pseudocode,
     *     and when parsing message block data from bytes to words, for example,
     *     the first word of the input message "abc" after padding is 0x61626380
     */
    /*
     * Initialize hash values:
     * (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
     */
    unsigned* k = init_k();
    unsigned* h = init_h();
    unsigned i;
    unsigned j;
    unsigned hold1;
    unsigned hold2;
    /* 512-bit chunks is what we will operate on. */
    char* chunk = calloc(65, sizeof(char));
    struct buffer_state* state = calloc(1, sizeof(struct buffer_state));
    init_buf_state(state, input, len);
    unsigned* ah = calloc(9, sizeof(unsigned));
    char *p;
    unsigned* w = calloc(17, sizeof(unsigned));
    unsigned s0;
    unsigned s1;
    unsigned ch;
    unsigned temp1;
    unsigned temp2;
    unsigned maj;

    while(calc_chunk(chunk, state))
    {
        p = chunk;

        /* Initialize working variables to current hash value: */
        for(i = 0; i < 8; i += 1)
        {
            ah[i] = h[i];
        }

        /* Compression function main loop: */
        for(i = 0; i < 4; i += 1)
        {
            /*
             * The w-array is really w[64], but since we only need
             * 16 of them at a time, we save stack by calculating
             * 16 at a time.
             *
             * This optimization was not there initially and the
             * rest of the comments about w[64] are kept in their
             * initial state.
             */
            /*
             * create a 64-entry message schedule array w[0..63] of 32-bit words
             * (The initial values in w[0..63] don't matter, so many implementations zero them here)
             * copy chunk into first 16 words w[0..15] of the message schedule array
             */

            for(j = 0; j < 16; j += 1)
            {
                if(i == 0)
                {
                    w[j] = ((p[0] & 0xFF) << 24) | ((p[1] & 0xFF) << 16) | ((p[2] & 0xFF) << 8) | (p[3] & 0xFF);
                    p += 4;
                }
                else
                {
                    /* Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array: */
                    hold1 = (j + 1) & 0xf;
                    hold2 = w[hold1];
                    s0 = right_rot(hold2, 7) ^ right_rot(hold2, 18) ^ ((hold2 & mask) >> 3);

                    hold1 = (j + 14) & 0xf;
                    hold2 = w[hold1];
                    s1 = right_rot(hold2, 17) ^ right_rot(hold2, 19) ^ ((hold2 & mask) >> 10);

                    w[j] += s0 + w[(j + 9) & 0xf] + s1;
                }

                s1 = right_rot(ah[4], 6) ^ right_rot(ah[4], 11) ^ right_rot(ah[4], 25);
                ch = (ah[4] & ah[5]) ^ (~ah[4] & ah[6]);
                temp1 = ah[7] + s1 + ch + k[i << 4 | j] + w[j];
                s0 = right_rot(ah[0], 2) ^ right_rot(ah[0], 13) ^ right_rot(ah[0], 22);
                maj = (ah[0] & ah[1]) ^ (ah[0] & ah[2]) ^ (ah[1] & ah[2]);
                temp2 = s0 + maj;
                ah[7] = ah[6];
                ah[6] = ah[5];
                ah[5] = ah[4];
                ah[4] = ah[3] + temp1;
                ah[3] = ah[2];
                ah[2] = ah[1];
                ah[1] = ah[0];
                ah[0] = temp1 + temp2;
            }
        }

        /* Add the compressed chunk to the current hash value: */
        for(i = 0; i < 8; i +=  1)
        {
            h[i] += ah[i];
        }
    }

    /* Produce the final hash value (big-endian): */
    i = 0;
    for(j = 0; i < 8; i += 1)
    {
        hash[j] = ((h[i] >> 24) & 0xFF);
        j += 1;
        hash[j] = ((h[i] >> 16) & 0xFF);
        j += 1;
        hash[j] = ((h[i] >> 8) & 0xFF);
        j += 1;
        hash[j] = (h[i] & 0xFF);
        j += 1;
    }
}

struct list
{
    int found;
    char* name;
    FILE* f;
    size_t size;
    char* buffer;
    char* hash;
    struct list* next;
};

void bad_checkfile(char* filename)
{
    fputs(filename, stdout);
    puts(": no properly formatted SHA256 checksum lines found");
}

int hex2int(char c, char* filename)
{
    if((c >= '0') && (c <= '9')) return (c - 48);
    else if((c >= 'a') && (c <= 'f')) return (c - 87);
    else if ((c >= 'F') && (c <= 'F')) return (c - 55);
    bad_checkfile(filename);
    exit(EXIT_FAILURE);
}

char* hash_to_string(char* a)
{
    char* table = "0123456789abcdef";
    char* r = calloc(66, sizeof(char));
    int i;
    int j = 0;
    int c;
    for(i = 0; i < 32; i += 1)
    {
        c = a[i] & 0xFF;
        r[j] = table[(c >> 4)];
        j += 1;
        r[j] = table[(c & 0xF)];
        j += 1;
    }
    return r;
}

int check_file(char* b, char* filename)
{
    int r = TRUE;
    size_t i;
    int hold1;
    int hold2;
    FILE* f;
    char* name = calloc(4097, sizeof(char));
    char* hash = calloc(33, sizeof(char));
    char* hash2 = calloc(33, sizeof(char));
    size_t size;
    char* buffer;
go_again:
    for(i = 0; i < 32; i += 1)
    {
        hold1 = hex2int(b[0], filename);
        hold2 = hex2int(b[1], filename);
        hash[i] = (hold1 << 4) + hold2;
        b += 2;
    }

    if((' ' != b[0]) || (' ' != b[1]))
    {
        bad_checkfile(filename);
        exit(EXIT_FAILURE);
    }

    b += 2;
    for(i = 0; i < 4096; i += 1)
    {
        if('\n' == b[0])
        {
            name[i] = 0;
            b += 1;
            break;
        }
        name[i] = b[0];
        b += 1;
    }

    f = fopen(name, "r");
    if(NULL == f)
    {
        fputs(name, stdout);
        puts(": No such file or directory");
        exit(EXIT_FAILURE);
    }
    else
    {
        fseek(f, 0, SEEK_END);
        size = ftell(f);
        rewind(f);
        buffer = calloc(size + 1, sizeof(char));
        fread(buffer, sizeof(char), size, f);
        calc_sha_256(hash2, buffer, size);
        if(match(hash_to_string(hash), hash_to_string(hash2)))
        {
            fputs(name, stdout);
            puts(": OK");
        }
        else
        {
            fputs(name, stdout);
            fputs(": FAILED\nWanted:   ", stdout);
            fputs(hash_to_string(hash), stdout);
            fputs("\nReceived: ", stdout);
            puts(hash_to_string(hash2));
            r = FALSE;
        }
    }

    if(0 == b[0]) return r;
    goto go_again;
}

/* reverse the linked list */
void reverse(struct list** head)
{
    struct list* prev = NULL;
    struct list* current = *head;
    struct list* next = NULL;
    while (current != NULL)
    {
        next = current->next;
        current->next = prev;
        prev = current;
        current = next;
    }
    *head = prev;
}

int main(int argc, char **argv)
{
    struct list* l = NULL;
    struct list* t = NULL;
    size_t read;
    int check = FALSE;
    int r = TRUE;
    char* output_file = "";
    FILE* output = stdout;
    mask = (0x7FFFFFFF << 1) | 0x1;

    int i = 1;
    while(i <= argc)
    {
        if(NULL == argv[i])
        {
            i += 1;
        }
        else if(match(argv[i], "-c") || match(argv[i], "--check"))
        {
            check = TRUE;
            i += 1;
        }
        else if (match(argv[i], "-o") || match(argv[i], "--output"))
        {
            output_file = argv[i + 1];
            i += 2;
            if (output != stdout) {
                fclose(output);
            }
            output = fopen(output_file, "w");
            require(output != NULL, "Output file cannot be opened!\n");
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            puts("Usage: sha256sum <file> [--check]");
            exit(EXIT_SUCCESS);
        }
        else
        {
            t = calloc(1, sizeof(struct list));
            t->hash = calloc(33, sizeof(char));
            t->name = argv[i];
            t->f = fopen(t->name, "r");
            if(NULL != t->f)
            {
                t->found = TRUE;
                fseek(t->f, 0, SEEK_END);
                t->size = ftell(t->f);
                rewind(t->f);
                t->buffer = calloc(t->size + 1, sizeof(char));
                read = fread(t->buffer, sizeof(char), t->size, t->f);
            }
            t->next = l;
            l = t;
            i += 1;
        }
    }
    reverse(&l);

    if(check)
    {
        while(NULL != l)
        {
            if(l->found)
            {
                if(!check_file(l->buffer, l->name)) r = FALSE;
            }
            else
            {
                fputs(l->name, stdout);
                puts(": No such file or directory");
                exit(EXIT_FAILURE);
            }
            l = l->next;
        }
    }
    else
    {
        while(NULL != l)
        {
            if(l->found)
            {
                calc_sha_256(l->hash, l->buffer, l->size);
                fputs(hash_to_string(l->hash), output);
                fputs("  ", output);
                fputs(l->name, output);
                fputc('\n', output);
            }
            else
            {
                fputs(l->name, output);
                fputs(": No such file or directory\n", output);
                exit(EXIT_FAILURE);
            }
            l = l->next;
        }
    }
    if (output != stdout) {
        fclose(output);
    }

    if(r) return 0;
    else return 1;
}

https://github.com/oriansj/M2libc/blob/main/string.h

Files:
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _STRING_H
#define _STRING_H

#ifdef __M2__
#include <string.c>
#else
#include <stddef.h>

/* String manipulation */
char* strcpy(char* dest, char const* src);
char* strncpy(char* dest, char const* src, size_t count);
char* strcat(char* dest, char const* src);
char* strncat(char* dest, char const* src, size_t count);

/* String examination */
size_t strlen(char const* str );
size_t strnlen_s(char const* str, size_t strsz );
int strcmp(char const* lhs, char const* rhs );
int strncmp(char const* lhs, char const* rhs, size_t count);
char* strchr(char const* str, int ch);
char* strrchr(char const* str, int ch);
size_t strspn(char const* dest, char const* src);
size_t strcspn(char const* dest, char const* src);
char* strpbrk(char const* dest, char const* breakset);

/* Memory manipulation */
void* memset(void* dest, int ch, size_t count);
void* memcpy(void* dest, void const* src, size_t count);
void* memmove(void* dest, void const* src, size_t count);
int memcmp(void const* lhs, void const* rhs, size_t count);
void* memchr(void const* ptr, int ch, size_t count);
#endif
#endif

https://github.com/oriansj/M2libc/blob/main/sys/stat.h

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/sys/stat.h used in: Step 66, Step 67, Step 68, Step 69, Step 70, Step 71, Step 72, Step 73, Step 74, Step 75, Step 76, Step 79, Step 81
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _SYS_STAT_H
#define _SYS_STAT_H

#ifdef __M2__
#if __uefi__
#include <uefi/sys/stat.c>
#elif __i386__
#include <x86/linux/sys/stat.c>
#elif __x86_64__
#include <amd64/linux/sys/stat.c>
#elif __arm__
#include <armv7l/linux/sys/stat.c>
#elif __aarch64__
#include <aarch64/linux/sys/stat.c>
#elif __riscv && __riscv_xlen==32
#include <riscv32/linux/sys/stat.c>
#elif __riscv && __riscv_xlen==64
#include <riscv64/linux/sys/stat.c>
#else
#error arch not supported
#endif

#else
#include <sys/types.h>

#define S_IRWXU 00700
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400

#define S_ISUID 04000
#define S_ISGID 02000
#define S_IXGRP 00010
#define S_IXOTH 00001
#define S_IRGRP 00040
#define S_IROTH 00004
#define S_IWGRP 00020
#define S_IWOTH 00002
#define S_IRWXG 00070
#define S_IRWXO 00007


int chmod(char *pathname, int mode);
int fchmod(int a, mode_t b);
int mkdir(char const* a, mode_t b);
int mknod(char const* a, mode_t b, dev_t c);
mode_t umask(mode_t m);

#endif
#endif

https://github.com/oriansj/M2libc/blob/main/fcntl.h

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/fcntl.h used in: Step 66, Step 67, Step 68, Step 69, Step 70, Step 71, Step 72, Step 73, Step 74, Step 75, Step 76, Step 79, Step 81
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _FCNTL_H
#define _FCNTL_H

#ifdef __M2__
#include <fcntl.c>
#else
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
#define O_CREAT 00100
#define O_EXCL 00200
#define O_TRUNC 001000
#define O_APPEND 002000

#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_IRWXU 00700


extern int open(char* name, int flag, int mode);

#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif
#endif

https://github.com/oriansj/M2libc/blob/main/unistd.h

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/unistd.h used in: Step 66, Step 67, Step 68, Step 69, Step 70, Step 71, Step 72, Step 73, Step 74, Step 75, Step 76, Step 79, Step 81
/* Copyright (C) 2020 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _UNISTD_H
#define _UNISTD_H

#ifdef __M2__
#if __uefi__
#include <uefi/unistd.c>
#elif __i386__
#include <x86/linux/unistd.c>
#elif __x86_64__
#include <amd64/linux/unistd.c>
#elif __arm__
#include <armv7l/linux/unistd.c>
#elif __aarch64__
#include <aarch64/linux/unistd.c>
#elif __riscv && __riscv_xlen==32
#include <riscv32/linux/unistd.c>
#elif __riscv && __riscv_xlen==64
#include <riscv64/linux/unistd.c>
#else
#error arch not supported
#endif

#else
#define NULL 0
#define __PATH_MAX 4096

void* malloc(unsigned size);
int access(char* pathname, int mode);
int chdir(char* path);
int fchdir(int fd);
void _exit(int value);
int fork();
int waitpid (int pid, int* status_ptr, int options);
int execve(char* file_name, char** argv, char** envp);
int read(int fd, char* buf, unsigned count);
int write(int fd, char* buf, unsigned count);
int lseek(int fd, int offset, int whence);
int close(int fd);
int unlink (char *filename);
int _getcwd(char* buf, int size);
char* getcwd(char* buf, unsigned size);
char* getwd(char* buf);
char* get_current_dir_name();
int brk(void *addr);

struct utsname
{
    char sysname[65];    /* Operating system name (e.g., "Linux") */
    char nodename[65];   /* Name within "some implementation-defined network" */
    char release[65];    /* Operating system release (e.g., "2.6.28") */
    char version[65];    /* Operating system version */
    char machine[65];    /* Hardware identifier */
};

int uname(struct utsname* unameData);

#endif
#endif

https://github.com/oriansj/M2libc/blob/main/stdlib.h

File: ../git/live-bootstrap/sysa/stage0-posix/src/M2libc/stdlib.h used in: Step 66, Step 67, Step 68, Step 69, Step 70, Step 71, Step 72, Step 73, Step 74, Step 75, Step 76, Step 79, Step 81
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _STDLIB_H
#define _STDLIB_H
#include <unistd.h>

#ifdef __M2__
#include <stdlib.c>
#else


#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0

extern void exit(int value);

extern long _malloc_ptr;
extern long _brk_ptr;

extern void free(void* l);
extern void* malloc(unsigned size);
extern void* memset(void* ptr, int value, int num);
extern void* calloc(int count, int size);
extern char *getenv(const char *name);

size_t wcstombs(char* dest, const wchar_t* src, size_t n);

#endif
#endif

https://github.com/oriansj/M2libc/blob/main/bootstrappable.h

Files:
/* Copyright (C) 2016 Jeremiah Orians
 * This file is part of M2-Planet.
 *
 * M2-Planet is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef _BOOTSTRAPPABLE_H
#define _BOOTSTRAPPABLE_H

/* Essential common CONSTANTS*/
#define TRUE 1
#define FALSE 0

#ifdef __M2__
#include <bootstrappable.c>
#else
/* Universally useful functions */
void require(int bool, char* error);
int match(char* a, char* b);
int in_set(int c, char* s);
int strtoint(char *a);
char* int2str(int x, int base, int signed_p);

#endif
#endif

https://github.com/oriansj/mescc-tools-extra/blob/main/match.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/match.c used in: Step 67
/* Copyright (C) 2021 Andrius Å tikonas
 * This file is part of mescc-tools-extra
 *
 * mescc-tools-extra is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools-extra 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 mescc-tools-extra.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * "match" can be used to compare strings. It is useful to write conditional
 * code in kaem.
 *
 * Usage: match string1 string2
 * Returns: 0 if strings match
 */ 

#include <stdio.h>
#include <string.h>

#include "M2libc/bootstrappable.h"

int main(int argc, char **argv)
{
    if(argc != 3)
    {
        fputs("match needs exactly 2 arguments.\n", stderr);
        return 2;
    }

    return !match(argv[1], argv[2]);
}

https://github.com/oriansj/mescc-tools-extra/blob/main/mkdir.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/mkdir.c used in: Step 68
/* Copyright (C) 2009 Tim Kientzle
 * Copyright (C) 2021 Jeremiah Orians
 * Copyright (C) 2021 Andrius Å tikonas
 * This file is part of mescc-tools-extra
 *
 * mescc-tools-extra is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools-extra 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 mescc-tools-extra.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * "mkdir" can be used to create empty directories. It can also create
 * required parent directories.
 *
 * Usage: mkdir <dir1>/<dir2> <dir3>
 *
 * These are all highly standard and portable headers. */
#include <stdio.h>
#include <string.h>

/* This is for mkdir(); this may need to be changed for some platforms. */
#include <sys/stat.h>  /* For mkdir() */
#include <stdlib.h>
#include "M2libc/bootstrappable.h"

int parents;

/* Create a directory, including parent directories as necessary. */
void create_dir(char *pathname, int mode)
{
    char *p;
    int r;

    /* Strip trailing '/' */
    if(pathname[strlen(pathname) - 1] == '/')
    {
        pathname[strlen(pathname) - 1] = '\0';
    }

    /* Try creating the directory. */
    r = mkdir(pathname, mode);

    if((r != 0) && parents)
    {
        /* On failure, try creating parent directory. */
        p = strrchr(pathname, '/');

        if(p != NULL)
        {
            p[0] = '\0';
            create_dir(pathname, 0755);
            p[0] = '/';
            r = mkdir(pathname, mode);
        }
    }

    if((r != 0) && !parents)
    {
        fputs("Could not create directory ", stderr);
        fputs(pathname, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char **argv)
{
    /* This adds some quasi-compatibility with GNU coreutils' mkdir. */
    parents = FALSE;
    int i;
    for(i = 1; argc > i; i = i + 1)
    {
        if(match(argv[i], "-p")) parents = TRUE;
        else create_dir(argv[i], 0755);
    }

    return 0;
}

https://github.com/oriansj/mescc-tools-extra/blob/main/untar.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/untar.c used in: Step 69
/* Copyright (C) 2009 Tim Kientzle
 * Copyright (C) 2021 Jeremiah Orians
 * This file is part of mescc-tools-extra
 *
 * mescc-tools-extra is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools-extra 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 mescc-tools-extra.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * "untar" is an extremely simple tar extractor:
 *  * A single C source file, so it should be easy to compile
 *    and run on any system with a C compiler.
 *  * Extremely portable standard C.  The only non-ANSI function
 *    used is mkdir().
 *  * Reads basic ustar tar archives.
 *  * Does not require libarchive or any other special library.
 *
 * To compile: cc -o untar untar.c
 *
 * Usage:  untar <archive>
 *
 * In particular, this program should be sufficient to extract the
 * distribution for libarchive, allowing people to bootstrap
 * libarchive on systems that do not already have a tar program.
 *
 * To unpack libarchive-x.y.z.tar.gz:
 *    * gunzip libarchive-x.y.z.tar.gz
 *    * untar libarchive-x.y.z.tar
 *
 * Written by Tim Kientzle, March 2009.
 *
 * Released into the public domain.
 */

/* These are all highly standard and portable headers. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* This is for mkdir(); this may need to be changed for some platforms. */
#include <sys/stat.h>  /* For mkdir() */
#include "M2libc/bootstrappable.h"

int FUZZING;
int VERBOSE;
int STRICT;

/* Parse an octal number, ignoring leading and trailing nonsense. */
int parseoct(char const* p, size_t n)
{
    int i = 0;
    int h;

    while(((p[0] < '0') || (p[0] > '7')) && (n > 0))
    {
        p = p + 1;
        n = n - 1;
    }

    while((p[0] >= '0') && (p[0] <= '7') && (n > 0))
    {
        i = i << 3;
        h = p[0];
        i = i + h - 48;
        p = p + 1;
        n = n - 1;
    }

    return i;
}

/* Returns true if this is 512 zero bytes. */
int is_end_of_archive(char const* p)
{
    int n;

    for(n = 511; n >= 0; n = n - 1)
    {
        if(p[n] != 0)
        {
            return FALSE;
        }
    }

    return TRUE;
}

/* Create a directory, including parent directories as necessary. */
void create_dir(char *pathname, int mode)
{
    char *p;
    int r;

    /* Strip trailing '/' */
    if(pathname[strlen(pathname) - 1] == '/')
    {
        pathname[strlen(pathname) - 1] = '\0';
    }

    /* Try creating the directory. */
    if(!FUZZING)
    {
        r = mkdir(pathname, mode);

        if(r != 0)
        {
            /* On failure, try creating parent directory. */
            p = strrchr(pathname, '/');

            if(p != NULL)
            {
                p[0] = '\0';
                create_dir(pathname, 0755);
                p[0] = '/';
                r = mkdir(pathname, mode);
            }
        }

        if(r != 0)
        {
            fputs("Could not create directory ", stderr);
            fputs(pathname, stderr);
            fputc('\n', stderr);
        }
    }
}

/* Create a file, including parent directory as necessary. */
FILE* create_file(char *pathname)
{
    if(FUZZING) return NULL;
    FILE* f;
    f = fopen(pathname, "w");

    if(f == NULL)
    {
        /* Try creating parent dir and then creating file. */
        char *p = strrchr(pathname, '/');

        if(p != NULL)
        {
            p[0] = '\0';
            create_dir(pathname, 0755);
            p[0] = '/';
            f = fopen(pathname, "w");
        }
    }

    return f;
}

/* Verify the tar checksum. */
int verify_checksum(char const* p)
{
    int n;
    int u = 0;
    unsigned h;

    for(n = 0; n < 512; n = n + 1)
    {
        /* Standard tar checksum adds unsigned bytes. */
        if((n < 148) || (n > 155))
        {
            h = p[n];
            u = u + h;
        }
        else
        {
            u = u + 0x20;
        }
    }

    int r = parseoct(p + 148, 8);

    return (u == r);
}

/* Extract a tar archive. */
int untar(FILE *a, char const* path)
{
    char* buff = calloc(514, sizeof(char));
    FILE* f = NULL;
    size_t bytes_read;
    size_t bytes_written;
    int filesize;
    int op;
    if(VERBOSE)
    {
        fputs("Extracting from ", stdout);
        puts(path);
    }

    while(TRUE)
    {
        memset(buff, 0, 514);
        bytes_read = fread(buff, sizeof(char), 512, a);

        if(bytes_read < 512)
        {
            fputs("Short read on ", stderr);
            fputs(path, stderr);
            fputs(": expected 512, got ", stderr);
            fputs(int2str(bytes_read, 10, TRUE), stderr);
            fputc('\n', stderr);
            return FALSE;
        }

        if(is_end_of_archive(buff))
        {
            if(VERBOSE)
            {
                fputs("End of ", stdout);
                puts(path);
            }
            return TRUE;
        }

        if(!verify_checksum(buff))
        {
            fputs("Checksum failure\n", stderr);
            return FALSE;
        }

        filesize = parseoct(buff + 124, 12);

        op = buff[156];
        if('1' == op)
        {
            if(STRICT)
            {
                fputs("unable to create hardlinks\n", stderr);
                exit(EXIT_FAILURE);
            }
            fputs(" Ignoring hardlink ", stdout);
            puts(buff);
        }
        else if('2' == op)
        {
            if(STRICT)
            {
                fputs("unable to create symlinks\n", stderr);
                exit(EXIT_FAILURE);
            }
            fputs(" Ignoring symlink ", stdout);
            puts(buff);
        }
        else if('3' == op)
        {
            if(STRICT)
            {
                fputs("unable to create character devices\n", stderr);
                exit(EXIT_FAILURE);
            }
            fputs(" Ignoring character device ", stdout);
            puts(buff);
        }
        else if('4' == op)
        {
            if(STRICT)
            {
                fputs("unable to create block devices\n", stderr);
                exit(EXIT_FAILURE);
            }
            fputs(" Ignoring block device ", stdout);
            puts(buff);
        }
        else if('5' == op)
        {
            if(VERBOSE)
            {
                fputs(" Extracting dir ", stdout);
                puts(buff);
            }
            create_dir(buff, parseoct(buff + 100, 8));
            filesize = 0;
        }
        else if('6' == op)
        {
            if(STRICT)
            {
                fputs("unable to create FIFO\n", stderr);
                exit(EXIT_FAILURE);
            }
            fputs(" Ignoring FIFO ", stdout);
            puts(buff);
        }
        else
        {
            if(VERBOSE)
            {
                fputs(" Extracting file ", stdout);
                puts(buff);
            }
            f = create_file(buff);
        }

        while(filesize > 0)
        {
            bytes_read = fread(buff, 1, 512, a);

            if(bytes_read < 512)
            {
                fputs("Short read on ", stderr);
                fputs(path, stderr);
                fputs(": Expected 512, got ", stderr);
                puts(int2str(bytes_read, 10, TRUE));
                return FALSE;
            }

            if(filesize < 512)
            {
                bytes_read = filesize;
            }

            if(f != NULL)
            {
                if(!FUZZING)
                {
                    bytes_written = fwrite(buff, 1, bytes_read, f);
                    if(bytes_written != bytes_read)
                    {
                        fputs("Failed write\n", stderr);
                        fclose(f);
                        f = NULL;
                    }
                }
            }

            filesize = filesize - bytes_read;
        }

        if(f != NULL)
        {
            fclose(f);
            f = NULL;
        }
    }
    return TRUE;
}

struct files_queue
{
    char* name;
    FILE* f;
    struct files_queue* next;
};

int main(int argc, char **argv)
{
    struct files_queue* list = NULL;
    struct files_queue* a;
    STRICT = TRUE;
    FUZZING = FALSE;
    int r;

    int i = 1;
    while (i < argc)
    {
        if(NULL == argv[i])
        {
            i = i + 1;
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            a = calloc(1, sizeof(struct files_queue));
            require(NULL != a, "failed to allocate enough memory to even get the file name\n");
            a->next = list;
            a->name = argv[i+1];
            require(NULL != a->name, "the --file option requires a filename to be given\n");
            a->f = fopen(a->name, "r");
            if(a->f == NULL)
            {
                fputs("Unable to open ", stderr);
                fputs(a->name, stderr);
                fputc('\n', stderr);
                if(STRICT) exit(EXIT_FAILURE);
            }
            list = a;
            i = i + 2;
        }
        else if(match(argv[i], "--chaos") || match(argv[i], "--fuzz-mode") || match(argv[i], "--fuzzing"))
        {
            FUZZING = TRUE;
            fputs("fuzz-mode enabled, preparing for chaos\n", stderr);
            i = i + 1;
        }
        else if(match(argv[i], "-v") || match(argv[i], "--verbose"))
        {
            VERBOSE = TRUE;
            i = i + 1;
        }

        else if(match(argv[i], "--non-strict") || match(argv[i], "--bad-decisions-mode") || match(argv[i], "--drunk-mode"))
        {
            STRICT = FALSE;
            fputs("non-strict mode enabled, preparing for chaos\n", stderr);
            i = i + 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file $input.gz\n", stderr);
            fputs("--verbose to print list of extracted files\n", stderr);
            fputs("--help to get this message\n", stderr);
            fputs("--fuzz-mode if you wish to fuzz this application safely\n", stderr);
            fputs("--non-strict if you wish to just ignore files not existing\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option:", stderr);
            fputs(argv[i], stderr);
            fputs("\nAborting to avoid problems\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    /* Process the queue one file at a time */
    while(NULL != list)
    {
        r = untar(list->f, list->name);
        fputs("The extraction of ", stderr);
        fputs(list->name, stderr);
        if(r) fputs(" was successful\n", stderr);
        else fputs(" produced errors\n", stderr);
        fclose(list->f);
        list = list->next;
    }

    return 0;
}

https://github.com/oriansj/mescc-tools-extra/blob/main/ungz.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/ungz.c used in: Step 70
/* Copyright (C) 2002-2013 Mark Adler, all rights reserved
 * Copyright (C) 2021 Jeremiah Orians
 * This file is part of mescc-tools-extra
 *
 * mescc-tools-extra is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools-extra 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 mescc-tools-extra.  If not, see <http://www.gnu.org/licenses/>.
 */

/* puff.c
 * Copyright (C) 2002-2013 Mark Adler, all rights reserved
 * version 2.3, 21 Jan 2013
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the author 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.
 * Mark Adler    madler@alumni.caltech.edu
 */

/* ungz.c is a gz file decompression utility that leverages puff.c to provide
 * the deflate algorithm with multiple modifications to enable being built by
 * M2-Planet with M2libc.
 *
 *
 * puff.c is a simple inflate written to be an unambiguous way to specify the
 * deflate format.  It is not written for speed but rather simplicity.  As a
 * side benefit, this code might actually be useful when small code is more
 * important than speed, such as bootstrap applications.  For typical deflate
 * data, zlib's inflate() is about four times as fast as puff().  zlib's
 * inflate compiles to around 20K on my machine, whereas puff.c compiles to
 * around 4K on my machine (a PowerPC using GNU cc).  If the faster decode()
 * function here is used, then puff() is only twice as slow as zlib's
 * inflate().
 *
 * All dynamically allocated memory comes from the stack.  The stack required
 * is less than 2K bytes.  This code is compatible with 16-bit int's and
 * assumes that long's are at least 32 bits.  puff.c uses the short data type,
 * assumed to be 16 bits, for arrays in order to conserve memory.  The code
 * works whether integers are stored big endian or little endian.
 *
 * In the comments below are "Format notes" that describe the inflate process
 * and document some of the less obvious aspects of the format.  This source
 * code is meant to supplement RFC 1951, which formally describes the deflate
 * format:
 *
 *    http://www.zlib.org/rfc-deflate.html
 */

/*
 * Change history:
 *
 * 1.0  10 Feb 2002     - First version
 * 1.1  17 Feb 2002     - Clarifications of some comments and notes
 *                      - Update puff() dest and source pointers on negative
 *                        errors to facilitate debugging deflators
 *                      - Remove longest from struct huffman -- not needed
 *                      - Simplify offs[] index in construct()
 *                      - Add input size and checking, using longjmp() to
 *                        maintain easy readability
 *                      - Use short data type for large arrays
 *                      - Use pointers instead of long to specify source and
 *                        destination sizes to avoid arbitrary 4 GB limits
 * 1.2  17 Mar 2002     - Add faster version of decode(), doubles speed (!),
 *                        but leave simple version for readabilty
 *                      - Make sure invalid distances detected if pointers
 *                        are 16 bits
 *                      - Fix fixed codes table error
 *                      - Provide a scanning mode for determining size of
 *                        uncompressed data
 * 1.3  20 Mar 2002     - Go back to lengths for puff() parameters [Gailly]
 *                      - Add a puff.h file for the interface
 *                      - Add braces in puff() for else do [Gailly]
 *                      - Use indexes instead of pointers for readability
 * 1.4  31 Mar 2002     - Simplify construct() code set check
 *                      - Fix some comments
 *                      - Add FIXLCODES #define
 * 1.5   6 Apr 2002     - Minor comment fixes
 * 1.6   7 Aug 2002     - Minor format changes
 * 1.7   3 Mar 2003     - Added test code for distribution
 *                      - Added zlib-like license
 * 1.8   9 Jan 2004     - Added some comments on no distance codes case
 * 1.9  21 Feb 2008     - Fix bug on 16-bit integer architectures [Pohland]
 *                      - Catch missing end-of-block symbol error
 * 2.0  25 Jul 2008     - Add #define to permit distance too far back
 *                      - Add option in TEST code for puff to write the data
 *                      - Add option in TEST code to skip input bytes
 *                      - Allow TEST code to read from piped stdin
 * 2.1   4 Apr 2010     - Avoid variable initialization for happier compilers
 *                      - Avoid unsigned comparisons for even happier compilers
 * 2.2  25 Apr 2010     - Fix bug in variable initializations [Oberhumer]
 *                      - Add const where appropriate [Oberhumer]
 *                      - Split if's and ?'s for coverage testing
 *                      - Break out test code to separate file
 *                      - Move NIL to puff.h
 *                      - Allow incomplete code only if single code length is 1
 *                      - Add full code coverage test to Makefile
 * 2.3  21 Jan 2013     - Check for invalid code length codes in dynamic blocks
 * ??   22 May 2021     - Convert to M2-Planet C subset for bootstrapping purposes.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "M2libc/bootstrappable.h"

/*
 * Maximums for allocations and loops.  It is not useful to change these --
 * they are fixed by the deflate format.
 */
#define MAXBITS 15              /* maximum bits in a code */
#define MAXLCODES 286           /* maximum number of literal/length codes */
#define MAXDCODES 30            /* maximum number of distance codes */
#define MAXCODES 316            /* maximum codes lengths to read (MAXLCODES+MAXDCODES) */
#define FIXLCODES 288           /* number of fixed literal/length codes */

/* input and output state */
struct state {
    /* output state */
    char *out;                  /* output buffer */
    size_t outlen;              /* available space at out */
    size_t outcnt;              /* bytes written to out so far */

    /* input state */
    char *in;                   /* input buffer */
    size_t inlen;               /* available input at in */
    size_t incnt;               /* bytes read so far */
    int bitbuf;                 /* bit buffer */
    int bitcnt;                 /* number of bits in bit buffer */
};

/*
 * Return need bits from the input stream.  This always leaves less than
 * eight bits in the buffer.  bits() works properly for need == 0.
 *
 * Format notes:
 *
 * - Bits are stored in bytes from the least significant bit to the most
 *   significant bit.  Therefore bits are dropped from the bottom of the bit
 *   buffer, using shift right, and new bytes are appended to the top of the
 *   bit buffer, using shift left.
 */
int bits(struct state *s, int need)
{
    long val;           /* bit accumulator (can use up to 20 bits) */
    long hold;

    /* load at least need bits into val */
    val = s->bitbuf;
    while (s->bitcnt < need)
    {
        if (s->incnt == s->inlen)
        {
            fputs("out of input\n", stderr);
            exit(EXIT_FAILURE);
        }
        hold = (s->in[s->incnt] & 0xFF);
        s->incnt = s->incnt + 1;
        val = val | (hold << s->bitcnt);  /* load eight bits */
        s->bitcnt = s->bitcnt + 8;
    }

    /* drop need bits and update buffer, always zero to seven bits left */
    s->bitbuf = (val >> need);
    s->bitcnt = s->bitcnt - need;

    /* return need bits, zeroing the bits above that */
    val = (val & ((1 << need) - 1));
    #if defined(DEBUG)
        fputs(int2str(val, 16, FALSE), stderr);
        fputs(" : bits\n", stderr);
    #endif
    return val;
}

/*
 * Process a stored block.
 *
 * Format notes:
 *
 * - After the two-bit stored block type (00), the stored block length and
 *   stored bytes are byte-aligned for fast copying.  Therefore any leftover
 *   bits in the byte that has the last bit of the type, as many as seven, are
 *   discarded.  The value of the discarded bits are not defined and should not
 *   be checked against any expectation.
 *
 * - The second inverted copy of the stored block length does not have to be
 *   checked, but it's probably a good idea to do so anyway.
 *
 * - A stored block can have zero length.  This is sometimes used to byte-align
 *   subsets of the compressed data for random access or partial recovery.
 */
int stored(struct state *s)
{
    unsigned len;       /* length of stored block */

    /* discard leftover bits from current byte (assumes s->bitcnt < 8) */
    s->bitbuf = 0;
    s->bitcnt = 0;

    /* get length and check against its one's complement */
    if ((s->incnt + 4) > s->inlen) return 2;    /* not enough input */
    len = s->in[s->incnt];
    s->incnt = s->incnt + 1;
    len = len | (s->in[s->incnt] << 8);
    s->incnt = s->incnt + 1;
    if(s->in[s->incnt] != (~len & 0xff)) return -2;                              /* didn't match complement! */
    s->incnt = s->incnt + 1;
    if(s->in[s->incnt] != ((~len >> 8) & 0xff)) return -2;                              /* didn't match complement! */
    s->incnt = s->incnt + 1;

    /* copy len bytes from in to out */
    if ((s->incnt + len) > s->inlen) return 2;                               /* not enough input */
    if (s->out != 0)
    {
        if ((s->outcnt + len) > s->outlen) return 1;                           /* not enough output space */
        while (0 != len)
        {
            len = len - 1;
            s->out[s->outcnt] = s->in[s->incnt];
            s->outcnt = s->outcnt + 1;
            s->incnt = s->incnt + 1;
        }
    }
    else
    {                                      /* just scanning */
        s->outcnt = s->outcnt + len;
        s->incnt = s->incnt + len;
    }

    /* done with a valid stored block */
    return 0;
}

/*
 * Huffman code decoding tables.  count[1..MAXBITS] is the number of symbols of
 * each length, which for a canonical code are stepped through in order.
 * symbol[] are the symbol values in canonical order, where the number of
 * entries is the sum of the counts in count[].  The decoding process can be
 * seen in the function decode() below.
 */
struct huffman
{
    int *count;       /* number of symbols of each length */
    int *symbol;      /* canonically ordered symbols */
};

/*
 * Decode a code from the stream s using huffman table h.  Return the symbol or
 * a negative value if there is an error.  If all of the lengths are zero, i.e.
 * an empty code, or if the code is incomplete and an invalid code is received,
 * then -10 is returned after reading MAXBITS bits.
 *
 * Format notes:
 *
 * - The codes as stored in the compressed data are bit-reversed relative to
 *   a simple integer ordering of codes of the same lengths.  Hence below the
 *   bits are pulled from the compressed data one at a time and used to
 *   build the code value reversed from what is in the stream in order to
 *   permit simple integer comparisons for decoding.  A table-based decoding
 *   scheme (as used in zlib) does not need to do this reversal.
 *
 * - The first code for the shortest length is all zeros.  Subsequent codes of
 *   the same length are simply integer increments of the previous code.  When
 *   moving up a length, a zero bit is appended to the code.  For a complete
 *   code, the last code of the longest length will be all ones.
 *
 * - Incomplete codes are handled by this decoder, since they are permitted
 *   in the deflate format.  See the format notes for fixed() and dynamic().
 */
int decode(struct state *s, struct huffman *h)
{
    int len;            /* current number of bits in code */
    int code = 0;       /* len bits being decoded */
    int first = 0;      /* first code of length len */
    int count;          /* number of codes of length len */
    int index = 0;      /* index of first code of length len in symbol table */
    long hold;

    for (len = 1; len <= MAXBITS; len = len + 1)
    {
        hold = bits(s, 1);              /* get next bit */
        code = code | hold;
        count = h->count[len];
        if ((code - count) < first)
        {
            hold = index + (code - first);
            return h->symbol[hold]; /* if length len, return symbol */
        }
        index = index + count;                 /* else update for next length */
        first = first + count;
        first = first << 1;
        code = code << 1;
    }
    return -10;                         /* ran out of codes */
}

/*
 * Given the list of code lengths length[0..n-1] representing a canonical
 * Huffman code for n symbols, construct the tables required to decode those
 * codes.  Those tables are the number of codes of each length, and the symbols
 * sorted by length, retaining their original order within each length.  The
 * return value is zero for a complete code set, negative for an over-
 * subscribed code set, and positive for an incomplete code set.  The tables
 * can be used if the return value is zero or positive, but they cannot be used
 * if the return value is negative.  If the return value is zero, it is not
 * possible for decode() using that table to return an error--any stream of
 * enough bits will resolve to a symbol.  If the return value is positive, then
 * it is possible for decode() using that table to return an error for received
 * codes past the end of the incomplete lengths.
 *
 * Not used by decode(), but used for error checking, h->count[0] is the number
 * of the n symbols not in the code.  So n - h->count[0] is the number of
 * codes.  This is useful for checking for incomplete codes that have more than
 * one symbol, which is an error in a dynamic block.
 *
 * Assumption: for all i in 0..n-1, 0 <= length[i] <= MAXBITS
 * This is assured by the construction of the length arrays in dynamic() and
 * fixed() and is not verified by construct().
 *
 * Format notes:
 *
 * - Permitted and expected examples of incomplete codes are one of the fixed
 *   codes and any code with a single symbol which in deflate is coded as one
 *   bit instead of zero bits.  See the format notes for fixed() and dynamic().
 *
 * - Within a given code length, the symbols are kept in ascending order for
 *   the code bits definition.
 */
int construct(struct huffman *h, int *length, int n)
{
    int symbol;         /* current symbol when stepping through length[] */
    int len;            /* current length when stepping through h->count[] */
    int left;           /* number of possible codes left of current length */
    int* offs;          /* offsets in symbol table for each length */
    offs = calloc(MAXBITS+1, sizeof(int));
    long hold;

    #if defined(DEBUG)
        int i;
        fputs(int2str(n, 16, FALSE), stderr);
        fputs(" : construct 0\n", stderr);

        for(i = 0; i < n; i = i + 1)
        {
            fputs(int2str(length[i], 16, FALSE), stderr);
            fputs(" : construct 2\n", stderr);
        }
    #endif

    /* count number of codes of each length */
    for (len = 0; len <= MAXBITS; len = len + 1)
    {
        h->count[len] = 0;
    }

    for (symbol = 0; symbol < n; symbol = symbol + 1)
    {
        hold = length[symbol];
        h->count[hold] = h->count[hold] + 1;    /* assumes lengths are within bounds */
    }

    if (h->count[0] == n) return 0;             /* no codes! complete, but decode() will fail */

    /* check for an over-subscribed or incomplete set of lengths */
    left = 1;                                   /* one possible code of zero length */
    for (len = 1; len <= MAXBITS; len = len + 1)
    {
        left = left << 1;                       /* one more bit, double codes left */
        left = left - h->count[len];            /* deduct count from possible codes */
        if (left < 0) return left;              /* over-subscribed--return negative */
    }                                           /* left > 0 means incomplete */

    /* generate offsets into symbol table for each length for sorting */
    offs[1] = 0;
    for (len = 1; len < MAXBITS; len = len + 1)
    {
        offs[len + 1] = offs[len] + h->count[len];
    }

    /*
     * put symbols in table sorted by length, by symbol order within each
     * length
     */
    for (symbol = 0; symbol < n; symbol = symbol + 1)
    {
        if (length[symbol] != 0)
        {
            hold = length[symbol];
            hold = offs[hold];
            h->symbol[hold] = symbol;
            hold = length[symbol];
            offs[hold] = offs[hold] + 1;
        }
    }

    /* return zero for complete set, positive for incomplete set */
    return left;
}

/*
 * Decode literal/length and distance codes until an end-of-block code.
 *
 * Format notes:
 *
 * - Compressed data that is after the block type if fixed or after the code
 *   description if dynamic is a combination of literals and length/distance
 *   pairs terminated by and end-of-block code.  Literals are simply Huffman
 *   coded bytes.  A length/distance pair is a coded length followed by a
 *   coded distance to represent a string that occurs earlier in the
 *   uncompressed data that occurs again at the current location.
 *
 * - Literals, lengths, and the end-of-block code are combined into a single
 *   code of up to 286 symbols.  They are 256 literals (0..255), 29 length
 *   symbols (257..285), and the end-of-block symbol (256).
 *
 * - There are 256 possible lengths (3..258), and so 29 symbols are not enough
 *   to represent all of those.  Lengths 3..10 and 258 are in fact represented
 *   by just a length symbol.  Lengths 11..257 are represented as a symbol and
 *   some number of extra bits that are added as an integer to the base length
 *   of the length symbol.  The number of extra bits is determined by the base
 *   length symbol.  These are in the static arrays below, lens[] for the base
 *   lengths and lext[] for the corresponding number of extra bits.
 *
 * - The reason that 258 gets its own symbol is that the longest length is used
 *   often in highly redundant files.  Note that 258 can also be coded as the
 *   base value 227 plus the maximum extra value of 31.  While a good deflate
 *   should never do this, it is not an error, and should be decoded properly.
 *
 * - If a length is decoded, including its extra bits if any, then it is
 *   followed a distance code.  There are up to 30 distance symbols.  Again
 *   there are many more possible distances (1..32768), so extra bits are added
 *   to a base value represented by the symbol.  The distances 1..4 get their
 *   own symbol, but the rest require extra bits.  The base distances and
 *   corresponding number of extra bits are below in the static arrays dist[]
 *   and dext[].
 *
 * - Literal bytes are simply written to the output.  A length/distance pair is
 *   an instruction to copy previously uncompressed bytes to the output.  The
 *   copy is from distance bytes back in the output stream, copying for length
 *   bytes.
 *
 * - Distances pointing before the beginning of the output data are not
 *   permitted.
 *
 * - Overlapped copies, where the length is greater than the distance, are
 *   allowed and common.  For example, a distance of one and a length of 258
 *   simply copies the last byte 258 times.  A distance of four and a length of
 *   twelve copies the last four bytes three times.  A simple forward copy
 *   ignoring whether the length is greater than the distance or not implements
 *   this correctly.  You should not use memcpy() since its behavior is not
 *   defined for overlapped arrays.  You should not use memmove() or bcopy()
 *   since though their behavior -is- defined for overlapping arrays, it is
 *   defined to do the wrong thing in this case.
 */

int* codes_lens()
{
    /* Size base for length codes 257..285 */
    int* r = calloc(30, sizeof(int));
    r[0] = 3;
    r[1] = 4;
    r[2] = 5;
    r[3] = 6;
    r[4] = 7;
    r[5] = 8;
    r[6] = 9;
    r[7] = 10;
    r[8] = 11;
    r[9] = 13;
    r[10] = 15;
    r[11] = 17;
    r[12] = 19;
    r[13] = 23;
    r[14] = 27;
    r[15] = 31;
    r[16] = 35;
    r[17] = 43;
    r[18] = 51;
    r[19] = 59;
    r[20] = 67;
    r[21] = 83;
    r[22] = 99;
    r[23] = 115;
    r[24] = 131;
    r[25] = 163;
    r[26] = 195;
    r[27] = 227;
    r[28] = 258;
    return r;
}

int* codes_lext()
{
    /* Extra bits for length codes 257..285 */
    int* r = calloc(30, sizeof(int));
    r[0] = 0;
    r[1] = 0;
    r[2] = 0;
    r[3] = 0;
    r[4] = 0;
    r[5] = 0;
    r[6] = 0;
    r[7] = 0;
    r[8] = 1;
    r[9] = 1;
    r[10] = 1;
    r[11] = 1;
    r[12] = 2;
    r[13] = 2;
    r[14] = 2;
    r[15] = 2;
    r[16] = 3;
    r[17] = 3;
    r[18] = 3;
    r[19] = 3;
    r[20] = 4;
    r[21] = 4;
    r[22] = 4;
    r[23] = 4;
    r[24] = 5;
    r[25] = 5;
    r[26] = 5;
    r[27] = 5;
    r[28] = 0;
    return r;
}

int* codes_dists()
{
    /* Offset base for distance codes 0..29 */
    int* r = calloc(31, sizeof(int));
    r[0] = 1;
    r[1] = 2;
    r[2] = 3;
    r[3] = 4;
    r[4] = 5;
    r[5] = 7;
    r[6] = 9;
    r[7] = 13;
    r[8] = 17;
    r[9] = 25;
    r[10] = 33;
    r[11] = 49;
    r[12] = 65;
    r[13] = 97;
    r[14] = 129;
    r[15] = 193;
    r[16] = 257;
    r[17] = 385;
    r[18] = 513;
    r[19] = 769;
    r[20] = 1025;
    r[21] = 1537;
    r[22] = 2049;
    r[23] = 3073;
    r[24] = 4097;
    r[25] = 6145;
    r[26] = 8193;
    r[27] = 12289;
    r[28] = 16385;
    r[29] = 24577;
    return r;
}

int* codes_dext()
{
    /* Extra bits for distance codes 0..29 */
    int* r = calloc(31, sizeof(int));
    r[0] = 0;
    r[1] = 0;
    r[2] = 0;
    r[3] = 0;
    r[4] = 1;
    r[5] = 1;
    r[6] = 2;
    r[7] = 2;
    r[8] = 3;
    r[9] = 3;
    r[10] = 4;
    r[11] = 4;
    r[12] = 5;
    r[13] = 5;
    r[14] = 6;
    r[15] = 6;
    r[16] = 7;
    r[17] = 7;
    r[18] = 8;
    r[19] = 8;
    r[20] = 9;
    r[21] = 9;
    r[22] = 10;
    r[23] = 10;
    r[24] = 11;
    r[25] = 11;
    r[26] = 12;
    r[27] = 12;
    r[28] = 13;
    r[29] = 13;
    return r;
}

int codes(struct state *s, struct huffman *lencode, struct huffman *distcode)
{
    int symbol;         /* decoded symbol */
    int len;            /* length for copy */
    unsigned dist;      /* distance for copy */
    int* lens = codes_lens();
    int* lext = codes_lext();
    int* dists = codes_dists();
    int* dext = codes_dext();

    /* decode literals and length/distance pairs */
    do
    {
        symbol = decode(s, lencode);
        if (symbol < 0) return symbol;          /* invalid symbol */
        if (symbol < 256)                       /* literal: symbol is the byte */
        {
            /* write out the literal */
            if (s->out != 0)
            {
                if (s->outcnt == s->outlen) return 1;
                s->out[s->outcnt] = symbol;
            }
            s->outcnt = s->outcnt + 1;
        }
        else if (symbol > 256)                  /* length */
        {
            /* get and compute length */
            symbol = symbol - 257;
            if (symbol >= 29) return -10;       /* invalid fixed code */
            len = lens[symbol] + bits(s, lext[symbol]);

            /* get and check distance */
            symbol = decode(s, distcode);
            if (symbol < 0) return symbol;      /* invalid symbol */
            dist = dists[symbol] + bits(s, dext[symbol]);
            if (dist > s->outcnt) return -11;   /* distance too far back */

            /* copy length bytes from distance bytes back */
            if (s->out != 0)
            {
                if (s->outcnt + len > s->outlen) return 1;
                while (0 != len)
                {
                    len = len - 1;
                    if(dist > s->outcnt) s->out[s->outcnt] = 0;
                    else s->out[s->outcnt] = s->out[s->outcnt - dist];
                    s->outcnt = s->outcnt + 1;
                }
            }
            else s->outcnt = s->outcnt + len;
        }
    } while (symbol != 256);            /* end of block symbol */

    /* done with a valid fixed or dynamic block */
    return 0;
}

/*
 * Process a fixed codes block.
 *
 * Format notes:
 *
 * - This block type can be useful for compressing small amounts of data for
 *   which the size of the code descriptions in a dynamic block exceeds the
 *   benefit of custom codes for that block.  For fixed codes, no bits are
 *   spent on code descriptions.  Instead the code lengths for literal/length
 *   codes and distance codes are fixed.  The specific lengths for each symbol
 *   can be seen in the "for" loops below.
 *
 * - The literal/length code is complete, but has two symbols that are invalid
 *   and should result in an error if received.  This cannot be implemented
 *   simply as an incomplete code since those two symbols are in the "middle"
 *   of the code.  They are eight bits long and the longest literal/length\
 *   code is nine bits.  Therefore the code must be constructed with those
 *   symbols, and the invalid symbols must be detected after decoding.
 *
 * - The fixed distance codes also have two invalid symbols that should result
 *   in an error if received.  Since all of the distance codes are the same
 *   length, this can be implemented as an incomplete code.  Then the invalid
 *   codes are detected while decoding.
 */
int fixed(struct state *s)
{
    int* lencnt = calloc((MAXBITS + 1), sizeof(int));
    int* lensym = calloc(FIXLCODES, sizeof(int));
    int* distcnt = calloc((MAXBITS + 1), sizeof(int));
    int* distsym = calloc(MAXDCODES, sizeof(int));
    struct huffman* lencode = calloc(1, sizeof(struct huffman));
    struct huffman* distcode = calloc(1, sizeof(struct huffman));
    int hold;

    /* build fixed huffman tables if first call (may not be thread safe) */
    int symbol;
    int* lengths = calloc(FIXLCODES, sizeof(int));

    /* construct lencode and distcode */
    lencode->count = lencnt;
    lencode->symbol = lensym;
    distcode->count = distcnt;
    distcode->symbol = distsym;

    /* literal/length table */
    for (symbol = 0; symbol < 144; symbol = symbol + 1)
    {
        lengths[symbol] = 8;
    }

    while(symbol < 256)
    {
        lengths[symbol] = 9;
        symbol = symbol + 1;
    }

    while(symbol < 280)
    {
        lengths[symbol] = 7;
        symbol = symbol + 1;
    }

    while(symbol < FIXLCODES)
    {
        lengths[symbol] = 8;
        symbol = symbol + 1;
    }

    construct(lencode, lengths, FIXLCODES);

    /* distance table */
    for (symbol = 0; symbol < MAXDCODES; symbol = symbol + 1)
    {
        lengths[symbol] = 5;
    }

    construct(distcode, lengths, MAXDCODES);

    /* decode data until end-of-block code */
    hold = codes(s, lencode, distcode);
    return hold;
}

/*
 * Process a dynamic codes block.
 *
 * Format notes:
 *
 * - A dynamic block starts with a description of the literal/length and
 *   distance codes for that block.  New dynamic blocks allow the compressor to
 *   rapidly adapt to changing data with new codes optimized for that data.
 *
 * - The codes used by the deflate format are "canonical", which means that
 *   the actual bits of the codes are generated in an unambiguous way simply
 *   from the number of bits in each code.  Therefore the code descriptions
 *   are simply a list of code lengths for each symbol.
 *
 * - The code lengths are stored in order for the symbols, so lengths are
 *   provided for each of the literal/length symbols, and for each of the
 *   distance symbols.
 *
 * - If a symbol is not used in the block, this is represented by a zero as
 *   as the code length.  This does not mean a zero-length code, but rather
 *   that no code should be created for this symbol.  There is no way in the
 *   deflate format to represent a zero-length code.
 *
 * - The maximum number of bits in a code is 15, so the possible lengths for
 *   any code are 1..15.
 *
 * - The fact that a length of zero is not permitted for a code has an
 *   interesting consequence.  Normally if only one symbol is used for a given
 *   code, then in fact that code could be represented with zero bits.  However
 *   in deflate, that code has to be at least one bit.  So for example, if
 *   only a single distance base symbol appears in a block, then it will be
 *   represented by a single code of length one, in particular one 0 bit.  This
 *   is an incomplete code, since if a 1 bit is received, it has no meaning,
 *   and should result in an error.  So incomplete distance codes of one symbol
 *   should be permitted, and the receipt of invalid codes should be handled.
 *
 * - It is also possible to have a single literal/length code, but that code
 *   must be the end-of-block code, since every dynamic block has one.  This
 *   is not the most efficient way to create an empty block (an empty fixed
 *   block is fewer bits), but it is allowed by the format.  So incomplete
 *   literal/length codes of one symbol should also be permitted.
 *
 * - If there are only literal codes and no lengths, then there are no distance
 *   codes.  This is represented by one distance code with zero bits.
 *
 * - The list of up to 286 length/literal lengths and up to 30 distance lengths
 *   are themselves compressed using Huffman codes and run-length encoding.  In
 *   the list of code lengths, a 0 symbol means no code, a 1..15 symbol means
 *   that length, and the symbols 16, 17, and 18 are run-length instructions.
 *   Each of 16, 17, and 18 are follwed by extra bits to define the length of
 *   the run.  16 copies the last length 3 to 6 times.  17 represents 3 to 10
 *   zero lengths, and 18 represents 11 to 138 zero lengths.  Unused symbols
 *   are common, hence the special coding for zero lengths.
 *
 * - The symbols for 0..18 are Huffman coded, and so that code must be
 *   described first.  This is simply a sequence of up to 19 three-bit values
 *   representing no code (0) or the code length for that symbol (1..7).
 *
 * - A dynamic block starts with three fixed-size counts from which is computed
 *   the number of literal/length code lengths, the number of distance code
 *   lengths, and the number of code length code lengths (ok, you come up with
 *   a better name!) in the code descriptions.  For the literal/length and
 *   distance codes, lengths after those provided are considered zero, i.e. no
 *   code.  The code length code lengths are received in a permuted order (see
 *   the order[] array below) to make a short code length code length list more
 *   likely.  As it turns out, very short and very long codes are less likely
 *   to be seen in a dynamic code description, hence what may appear initially
 *   to be a peculiar ordering.
 *
 * - Given the number of literal/length code lengths (nlen) and distance code
 *   lengths (ndist), then they are treated as one long list of nlen + ndist
 *   code lengths.  Therefore run-length coding can and often does cross the
 *   boundary between the two sets of lengths.
 *
 * - So to summarize, the code description at the start of a dynamic block is
 *   three counts for the number of code lengths for the literal/length codes,
 *   the distance codes, and the code length codes.  This is followed by the
 *   code length code lengths, three bits each.  This is used to construct the
 *   code length code which is used to read the remainder of the lengths.  Then
 *   the literal/length code lengths and distance lengths are read as a single
 *   set of lengths using the code length codes.  Codes are constructed from
 *   the resulting two sets of lengths, and then finally you can start
 *   decoding actual compressed data in the block.
 *
 * - For reference, a "typical" size for the code description in a dynamic
 *   block is around 80 bytes.
 */

int* dynamic_order()
{
    /* permutation of code length codes */
    int* r = calloc(20, sizeof(int));
    r[0] = 16;
    r[1] = 17;
    r[2] = 18;
    r[3] = 0;
    r[4] = 8;
    r[5] = 7;
    r[6] = 9;
    r[7] = 6;
    r[8] = 10;
    r[9] = 5;
    r[10] = 11;
    r[11] = 4;
    r[12] = 12;
    r[13] = 3;
    r[14] = 13;
    r[15] = 2;
    r[16] = 14;
    r[17] = 1;
    r[18] = 15;
    return r;
}

int dynamic(struct state *s)
{
    #if defined(__M2__)
        int array = sizeof(int);
    #else
        int array = 1;
    #endif

    int nlen;
    int ndist;
    int ncode;                          /* number of lengths in descriptor */
    int index;                          /* index of lengths[] */
    int err;                            /* construct() return value */
    int* lengths = calloc(MAXCODES, sizeof(int));       /* descriptor code lengths */
    int* lencnt = calloc((MAXBITS + 1), sizeof(int));
    int* lensym = calloc(MAXLCODES, sizeof(int));       /* lencode memory */
    int* distcnt = calloc((MAXBITS + 1), sizeof(int));
    int* distsym = calloc(MAXDCODES, sizeof(int));      /* distcode memory */
    struct huffman* lencode = calloc(1, sizeof(struct huffman));
    struct huffman* distcode = calloc(1, sizeof(struct huffman));
    int* order = dynamic_order();
    long hold;
    int* set;

    /* construct lencode and distcode */
    lencode->count = lencnt;
    lencode->symbol = lensym;
    distcode->count = distcnt;
    distcode->symbol = distsym;

    /* get number of lengths in each table, check lengths */
    nlen = bits(s, 5) + 257;
    ndist = bits(s, 5) + 1;
    ncode = bits(s, 4) + 4;
    if (nlen > MAXLCODES) return -3;    /* bad counts */
    if(ndist > MAXDCODES) return -3;    /* bad counts */

    /* read code length code lengths (really), missing lengths are zero */
    for (index = 0; index < ncode; index = index + 1)
    {
        hold = order[index];
        lengths[hold] = bits(s, 3);
    }

    while(index < 19)
    {
        hold = order[index];
        lengths[hold] = 0;
        index = index + 1;
    }

    /* build huffman table for code lengths codes (use lencode temporarily) */
    err = construct(lencode, lengths, 19);
    if (err != 0) return -4;            /* require complete code set here */

    /* read length/literal and distance code length tables */
    index = 0;
    int symbol;                         /* decoded value */
    int len;                            /* last length to repeat */
    while (index < (nlen + ndist))
    {
        symbol = decode(s, lencode);
        if (symbol < 0) return symbol;  /* invalid symbol */

        if (symbol < 16)                /* length in 0..15 */
        {
            lengths[index] = symbol;
            index = index + 1;
        }
        else                            /* repeat instruction */
        {
            len = 0;                    /* assume repeating zeros */
            if (symbol == 16)           /* repeat last length 3..6 times */
            {
                if (index == 0) return -5;      /* no last length! */
                len = lengths[index - 1];       /* last length */
                symbol = 3 + bits(s, 2);
            }
            else if (symbol == 17) symbol = 3 + bits(s, 3); /* repeat zero 3..10 times */
            else symbol = 11 + bits(s, 7);      /* == 18, repeat zero 11..138 times */

            if ((index + symbol) > (nlen + ndist)) return -6;   /* too many lengths! */

            while(0 != symbol)            /* repeat last or zero symbol times */
            {
                lengths[index] = len;
                index = index + 1;
                symbol = symbol - 1;
            }
        }
    }

    /* check for end-of-block code -- there better be one! */
    if (lengths[256] == 0) return -9;

    /* build huffman table for literal/length codes */
    err = construct(lencode, lengths, nlen);

    /* incomplete code ok only for single length 1 code */
    if (err < 0) return -7;
    if((0 != err) && (nlen != (lencode->count[0] + lencode->count[1]))) return -7;

    /* build huffman table for distance codes */
    set = lengths + (nlen * array);
    err = construct(distcode, set, ndist);

    /* incomplete code ok only for single length 1 code */
    if (err < 0) return -8;
    if((0 != err) && (ndist != (distcode->count[0] + distcode->count[1]))) return -8;

    /* decode data until end-of-block code */
    hold = codes(s, lencode, distcode);
    return hold;
}

/*
 * Inflate source to dest.  On return, destlen and sourcelen are updated to the
 * size of the uncompressed data and the size of the deflate data respectively.
 * On success, the return value of puff() is zero.  If there is an error in the
 * source data, i.e. it is not in the deflate format, then a negative value is
 * returned.  If there is not enough input available or there is not enough
 * output space, then a positive error is returned.  In that case, destlen and
 * sourcelen are not updated to facilitate retrying from the beginning with the
 * provision of more input data or more output space.  In the case of invalid
 * inflate data (a negative error), the dest and source pointers are updated to
 * facilitate the debugging of deflators.
 *
 * puff() also has a mode to determine the size of the uncompressed output with
 * no output written.  For this dest must be (unsigned char *)0.  In this case,
 * the input value of *destlen is ignored, and on return *destlen is set to the
 * size of the uncompressed output.
 *
 * The return codes are:
 *
 *   2:  available inflate data did not terminate
 *   1:  output space exhausted before completing inflate
 *   0:  successful inflate
 *  -1:  invalid block type (type == 3)
 *  -2:  stored block length did not match one's complement
 *  -3:  dynamic block code description: too many length or distance codes
 *  -4:  dynamic block code description: code lengths codes incomplete
 *  -5:  dynamic block code description: repeat lengths with no first length
 *  -6:  dynamic block code description: repeat more than specified lengths
 *  -7:  dynamic block code description: invalid literal/length code lengths
 *  -8:  dynamic block code description: invalid distance code lengths
 *  -9:  dynamic block code description: missing end-of-block code
 * -10:  invalid literal/length or distance code in fixed or dynamic block
 * -11:  distance is too far back in fixed or dynamic block
 *
 * Format notes:
 *
 * - Three bits are read for each block to determine the kind of block and
 *   whether or not it is the last block.  Then the block is decoded and the
 *   process repeated if it was not the last block.
 *
 * - The leftover bits in the last byte of the deflate data after the last
 *   block (if it was a fixed or dynamic block) are undefined and have no
 *   expected values to check.
 */

struct puffer
{
    int error;
    size_t destlen;
    size_t sourcelen;
};

struct puffer* puff(char* dest, size_t destlen, char* source, size_t sourcelen)
{
    struct state* s = calloc(1, sizeof(struct state));             /* input/output state */
    int last;
    int type;                   /* block information */
    int err;                    /* return value */

    /* initialize output state */
    s->out = dest;
    s->outlen = destlen;                /* ignored if dest is NIL */
    s->outcnt = 0;

    /* initialize input state */
    s->in = source;
    s->inlen = sourcelen;
    s->incnt = 0;
    s->bitbuf = 0;
    s->bitcnt = 0;

    /* process blocks until last block or error */
    do
    {
        last = bits(s, 1);         /* one if last block */
        type = bits(s, 2);         /* block type 0..3 */

        if(0 == type)
        {
            err = stored(s);
        }
        else if(1 == type)
        {
            err = fixed(s);
        }
        else if(2 == type)
        {
            err = dynamic(s);
        }
        else err = -1;

        if (err != 0) break;                  /* return with error */
    } while (!last);

    /* update the lengths and return */
    struct puffer* r = calloc(1, sizeof(struct puffer));
    r->error = err;
    r->destlen = s->outcnt;
    r->sourcelen = s->incnt;
    return r;
}

void write_blob(char* s, int start, int len, FILE* f)
{
    char* table = "0123456789ABCDEF";
    if(start > len) return;

    int i = s[start] & 0xFF;
    fputc(table[(i >> 4)], f);
    fputc(table[(i & 0xF)], f);
    fputc(' ', f);

    if(start == len) fputc('\n', f);
    else fputc(' ', f);
    write_blob(s, start + 1, len, f);
}

#define FTEXT 0x01
#define FHCRC 0x02
#define FEXTRA 0x04
#define FNAME 0x08
#define FCOMMENT 0x10


struct gz
{
    char* HEADER;
    int ID;
    int CM;
    int FLG;
    int MTIME;
    int XFL;
    int OS;
    int XLEN;
    char* FLG_FEXTRA;
    char* FLG_FNAME;
    char* FLG_FCOMMENT;
    int CRC16;
    char* FLG_FHCRC;
    char* block;
    int CRC32;
    size_t ISIZE;
    size_t file_size;
};

/* Read the input file *name, or stdin if name is NULL, into allocated memory.
   Reallocate to larger buffers until the entire file is read in.  Return a
   pointer to the allocated data, or NULL if there was a memory allocation
   failure.  *len is the number of bytes of data read from the input file (even
   if load() returns NULL).  If the input file was empty or could not be opened
   or read, *len is zero. */
struct gz* load(char* name)
{
    struct gz* r = calloc(1, sizeof(struct gz));
    char* scratch = calloc(5, sizeof(char));
    FILE* f = fopen(name, "r");
    int count;
    int ID1;
    int ID2;
    int count1;
    int count2;
    int count3;
    int count4;
    int c;
    int i;
    char* s = calloc(11, sizeof(char));

    if(NULL == f)
    {
        fputs("unable to open file: ", stderr);
        fputs(name, stderr);
        fputs("\nfor reading\n", stderr);
        return NULL;
    }

    fseek(f, 0, SEEK_END);
    r->file_size = ftell(f);
    fseek(f, 0, SEEK_SET);
    count = fread(s, sizeof(char), 10, f);

    if(10 != count)
    {
        fputs("incomplete gzip header\n", stderr);
        return NULL;
    }

    /* Verify header */
    r->HEADER = s;

    #if defined(DEBUG)
        write_blob(s, 0, 10, stderr);
    #endif

    ID1 = (s[0] & 0xFF);
    ID2 = (s[1] & 0xFF);
    r->ID = ((ID1 << 8) | ID2);
    if(0x1f8b != r->ID)
    {
        fputs("bad header\n", stderr);
        return NULL;
    }

    /* Verify Compression */
    r->CM = (r->HEADER[2] & 0xFF);
    if(8 != r->CM)
    {
        fputs("NOT DEFLATE COMPRESSION\n", stderr);
        return NULL;
    }

    /* Get specials specified in flag bits */
    r->FLG = (r->HEADER[3] & 0xFF);

    if(0 != (FEXTRA & r->FLG))
    {
        count = fread(scratch, sizeof(char), 4, f);
        count1 = (scratch[0] & 0xFF);
        count2 = (scratch[1] & 0xFF);
        count3 = (scratch[2] & 0xFF);
        count4 = (scratch[3] & 0xFF);
        count = (count1 << 24) | (count2 << 16) | (count3 << 8) | count4;
        require(0 < count, "FEXTRA field needs to be a positive number of bytes in size\n");
        require(100000000 > count, "we don't support FEXTRA fields greater than 100MB in size\n");
        r->FLG_FEXTRA = calloc(count + 1, sizeof(char));
        fread(r->FLG_FEXTRA, sizeof(char), count, f);
    }

    if(0 != (FNAME & r->FLG))
    {
        r->FLG_FNAME = calloc(r->file_size, sizeof(char));
        i = 0;
        do
        {
            c = fgetc(f);
            require(0 <= c, "received a non-null terminated filename in the file\n");
            r->FLG_FNAME[i] = c;
            i = i + 1;
        } while(0 != c);
    }

    if(0 != (FCOMMENT & r->FLG))
    {
        r->FLG_FCOMMENT = calloc(r->file_size, sizeof(char));
        i = 0;
        do
        {
            c = fgetc(f);
            require(0 <= c, "received a non-null terminated comment in the file\n");
            r->FLG_FCOMMENT[i] = c;
            i = i + 1;
        } while(0 != c);
    }

    if(0 != (FHCRC & r->FLG))
    {
        /* Not implemented */
        fputs("FHCRC is not implemented at this time\n", stderr);
        return NULL;
    }

    if(NULL == r->FLG_FNAME)
    {
        count = strlen(name) - 3;
        r->FLG_FNAME = calloc(count + 4, sizeof(char));
        i = 0;
        while(i < count)
        {
            r->FLG_FNAME[i] = name[i];
            i = i + 1;
        }
    }

    r->block = calloc(r->file_size, sizeof(char));
    count = fread(r->block, sizeof(char), r->file_size, f);
    r->ISIZE = count;
    fclose(f);
    return r;
}

int main(int argc, char **argv)
{
    struct puffer* ret;
    char* name;
    char* buffer;
    char *dest;
    struct gz* in;
    FILE* out;
    int FUZZING = FALSE;

    /* process arguments */
    int i = 1;
    while (i < argc)
    {
        if(NULL == argv[i])
        {
            i = i + 1;
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            name = argv[i+1];
            require(NULL != name, "the --file option requires a filename to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "-o") || match(argv[i], "--output"))
        {
            dest = argv[i+1];
            require(NULL != dest, "the --output option requires a filename to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "--chaos") || match(argv[i], "--fuzz-mode") || match(argv[i], "--fuzzing"))
        {
            FUZZING = TRUE;
            fputs("fuzz-mode enabled, preparing for chaos\n", stderr);
            i = i + 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file $input.gz", stderr);
            fputs(" [--output $output] (or it'll use the internal filename)\n", stderr);
            fputs("--help to get this message\n", stderr);
            fputs("--fuzz-mode if you wish to fuzz this application safely\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option:", stderr);
            fputs(argv[i], stderr);
            fputs("\nAborting to avoid problems\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    in = load(name);

    if (in == NULL)
    {
        fputs("memory allocation failure\nDidn't read file\n", stderr);
        exit(1);
    }

    ret = puff(0, 0, in->block, in->ISIZE);

    if(NULL == dest)
    {
        dest = in->FLG_FNAME;
    }

    fputs(name, stderr);
    fputs(" => ", stderr);
    fputs(dest, stderr);

    if (0 != ret->error)
    {
        fputs("puff() failed with return code ", stderr);
        fputs(int2str(ret->error, 10, TRUE), stderr);
        fputc('\n', stderr);
        exit(3);
    }
    else
    {
        fputs(": succeeded uncompressing ", stderr);
        fputs(int2str(ret->destlen, 10, FALSE), stderr);
        fputs(" bytes\n", stderr);
    }

    buffer = malloc(ret->destlen);
    if (buffer == NULL)
    {
        fputs("memory allocation failure\n", stderr);
        return 4;
    }

    ret = puff(buffer, ret->destlen, in->block, in->ISIZE);

    if(!FUZZING)
    {
        out = fopen(dest, "w");
        fwrite(buffer, 1, ret->destlen, out);
    }
    else
    {
        fputs("skipped write to file due to --fuzz-mode flag\n", stderr);
    }
    free(buffer);

    /* clean up */
    return 0;
}

https://github.com/oriansj/mescc-tools-extra/blob/main/unbz2.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/unbz2.c used in: Step 71
/* Copyright (C) 2003, 2007 Rob Landley <rob@landley.net>
 * Copyright (C) 2022 Paul Dersey <pdersey@gmail.com>
 * This file is part of mescc-tools-extra
 *
 * mescc-tools-extra is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools-extra 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 mescc-tools-extra.  If not, see <http://www.gnu.org/licenses/>.
 */

/* bzcat.c - bzip2 decompression
 *
 * Copyright 2003, 2007 Rob Landley <rob@landley.net>
 *
 * Based on a close reading (but not the actual code) of the original bzip2
 * decompression code by Julian R Seward (jseward@acm.org), which also
 * acknowledges contributions by Mike Burrows, David Wheeler, Peter Fenwick,
 * Alistair Moffat, Radford Neal, Ian H. Witten, Robert Sedgewick, and
 * Jon L. Bentley.
 *
 * No standard.
*/

/* unbz2.c is a bz2 file decompression utility based on bzcat.c with
 * modifications to enable being built by M2-Planet with M2libc.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include "M2libc/bootstrappable.h"

// Constants for huffman coding
#define MAX_GROUPS               6
#define GROUP_SIZE               50     /* 64 would have been more efficient */
#define MAX_HUFCODE_BITS         20     /* Longest huffman code allowed */
#define MAX_SYMBOLS              258    /* 256 literals + RUNA + RUNB */
#define SYMBOL_RUNA              0
#define SYMBOL_RUNB              1

// Other housekeeping constants
#define IOBUF_SIZE               4096

// Status return values
#define RETVAL_LAST_BLOCK        (-100)
#define RETVAL_NOT_BZIP_DATA     (-1)
#define RETVAL_DATA_ERROR        (-2)
#define RETVAL_OBSOLETE_INPUT    (-3)

#define INT_MAX 2147483647

// This is what we know about each huffman coding group
struct group_data
{
    int *limit;
    int *base;
    int *permute;
    char minLen;
    char maxLen;
};

// Data for burrows wheeler transform

struct bwdata
{
    unsigned origPtr;
    int *byteCount;
    // State saved when interrupting output
    int writePos;
    int writeRun;
    int writeCount;
    int writeCurrent;
    unsigned dataCRC;
    unsigned headerCRC;
    unsigned *dbuf;
};

// Structure holding all the housekeeping data, including IO buffers and
// memory that persists between calls to bunzip
struct bunzip_data
{
    // Input stream, input buffer, input bit buffer
    int in_fd;
    int inbufCount;
    int inbufPos;
    char *inbuf;
    unsigned inbufBitCount;
    unsigned inbufBits;

    // Output buffer
    char *outbuf;
    int outbufPos;

    unsigned totalCRC;

    // First pass decompression data (Huffman and MTF decoding)
    char *selectors;                  // nSelectors=15 bits
    struct group_data *groups;   // huffman coding tables
    int symTotal;
    int groupCount;
    int nSelectors;
    unsigned *symToByte;
    unsigned *mtfSymbol;

    // The CRC values stored in the block header and calculated from the data
    unsigned *crc32Table;

    // Second pass decompression data (burrows-wheeler transform)
    unsigned dbufSize;
    struct bwdata* bwdata;
};

int FUZZING;


void crc_init(unsigned *crc_table, int little_endian)
{
    unsigned i;
    unsigned j;
    unsigned c;

    // Init the CRC32 table (big endian)
    for(i = 0; i < 256; i += 1)
    {
        if(little_endian)
        {
            c = i;
        }
        else
        {
            c = i << 24;
        }

        for(j = 8; j > 0; j -= 1)
        {
            if(little_endian)
            {
                if(c & 1)
                {
                    c = (c >> 1) ^ 0xEDB88320;
                }
                else
                {
                    c = c >> 1;
                }
            }
            else
            {
                if(c & 0x80000000)
                {
                    c = (c << 1) ^ 0x04C11DB7;
#if defined(__M2__)

                    // & 0xFFFFFFFF not working
                    if(sizeof(unsigned) == 8)
                    {
                        c <<= 32;
                        c >>= 32;
                    }

#endif
                }
                else
                {
                    c = c << 1;
                }
            }
        }

        crc_table[i] = c;
    }
}

// Return the next nnn bits of input.  All reads from the compressed input
// are done through this function.  All reads are big endian.
unsigned get_bits(struct bunzip_data *bd, char bits_wanted)
{
    unsigned bits = 0;

    // If we need to get more data from the byte buffer, do so.  (Loop getting
    // one byte at a time to enforce endianness and avoid unaligned access.)
    while(bd->inbufBitCount < bits_wanted)
    {
        // If we need to read more data from file into byte buffer, do so
        if(bd->inbufPos == bd->inbufCount)
        {
            if(0 >= (bd->inbufCount = read(bd->in_fd, bd->inbuf, IOBUF_SIZE)))
            {
                exit(1);
            }

            bd->inbufPos = 0;
        }

        // Avoid 32-bit overflow (dump bit buffer to top of output)
        if(bd->inbufBitCount >= 24)
        {
            bits = bd->inbufBits & ((1 << bd->inbufBitCount) - 1);
            bits_wanted = bits_wanted - bd->inbufBitCount;
            bits = bits << bits_wanted;
            bd->inbufBitCount = 0;
        }

        // Grab next 8 bits of input from buffer.
        bd->inbufBits = (bd->inbufBits << 8) | (bd->inbuf[bd->inbufPos] & 0xFF);
        bd->inbufPos = bd->inbufPos + 1;
        bd->inbufBitCount = bd->inbufBitCount + 8;
    }

    // Calculate result
    bd->inbufBitCount = bd->inbufBitCount - bits_wanted;
    bits = bits | ((bd->inbufBits >> bd->inbufBitCount) & ((1 << bits_wanted) - 1));
    return bits;
}

/* Read block header at start of a new compressed data block.  Consists of:
 *
 * 48 bits : Block signature, either pi (data block) or e (EOF block).
 * 32 bits : bw->headerCRC
 * 1  bit  : obsolete feature flag.
 * 24 bits : origPtr (Burrows-wheeler unwind index, only 20 bits ever used)
 * 16 bits : Mapping table index.
 *[16 bits]: symToByte[symTotal] (Mapping table.  For each bit set in mapping
 *           table index above, read another 16 bits of mapping table data.
 *           If correspondig bit is unset, all bits in that mapping table
 *           section are 0.)
 *  3 bits : groupCount (how many huffman tables used to encode, anywhere
 *           from 2 to MAX_GROUPS)
 * variable: hufGroup[groupCount] (MTF encoded huffman table data.)
 */

int read_block_header(struct bunzip_data *bd, struct bwdata *bw)
{
    struct group_data *hufGroup;
    int hh;
    int ii;
    int jj;
    int kk;
    int symCount;
    int *base;
    int *limit;
    unsigned uc;
    unsigned *length = calloc(MAX_SYMBOLS, sizeof(unsigned));
    unsigned *temp = calloc(MAX_HUFCODE_BITS + 1, sizeof(unsigned));
    size_t minLen;
    size_t maxLen;
    int pp;
#if defined(__M2__)
    int int_array = sizeof(int);
    int group_data_array = sizeof(struct group_data);
#else
    int int_array = 1;
    int group_data_array = 1;
#endif
    size_t hold;
    // Read in header signature and CRC (which is stored big endian)
    ii = get_bits(bd, 24);
    jj = get_bits(bd, 24);
    bw->headerCRC = get_bits(bd, 32);

    // Is this the EOF block with CRC for whole file?  (Constant is "e")
    if(ii == 0x177245 && jj == 0x385090)
    {
        free(length);
        free(temp);
        return RETVAL_LAST_BLOCK;
    }

    // Is this a valid data block?  (Constant is "pi".)
    if(ii != 0x314159 || jj != 0x265359)
    {
        return RETVAL_NOT_BZIP_DATA;
    }

    // We can add support for blockRandomised if anybody complains.
    if(get_bits(bd, 1))
    {
        return RETVAL_OBSOLETE_INPUT;
    }

    if((bw->origPtr = get_bits(bd, 24)) > bd->dbufSize)
    {
        return RETVAL_DATA_ERROR;
    }

    // mapping table: if some byte values are never used (encoding things
    // like ascii text), the compression code removes the gaps to have fewer
    // symbols to deal with, and writes a sparse bitfield indicating which
    // values were present.  We make a translation table to convert the symbols
    // back to the corresponding bytes.
    hh = get_bits(bd, 16);
    bd->symTotal = 0;

    for(ii = 0; ii < 16; ii += 1)
    {
        if(hh & (1 << (15 - ii)))
        {
            kk = get_bits(bd, 16);

            for(jj = 0; jj < 16; jj += 1)
            {
                if(kk & (1 << (15 - jj)))
                {
                    bd->symToByte[bd->symTotal] = (16 * ii) + jj;
                    bd->symTotal += 1;
                }
            }
        }
    }

    // How many different huffman coding groups does this block use?
    bd->groupCount = get_bits(bd, 3);

    if(bd->groupCount < 2 || bd->groupCount > MAX_GROUPS)
    {
        return RETVAL_DATA_ERROR;
    }

    // nSelectors: Every GROUP_SIZE many symbols we switch huffman coding
    // tables.  Each group has a selector, which is an index into the huffman
    // coding table arrays.
    //
    // Read in the group selector array, which is stored as MTF encoded
    // bit runs.  (MTF = Move To Front.  Every time a symbol occurs its moved
    // to the front of the table, so it has a shorter encoding next time.)
    if(!(bd->nSelectors = get_bits(bd, 15)))
    {
        return RETVAL_DATA_ERROR;
    }

    for(ii = 0; ii < bd->groupCount; ii += 1)
    {
        bd->mtfSymbol[ii] = ii;
    }

    for(ii = 0; ii < bd->nSelectors; ii += 1)
    {
        // Get next value
        for(jj = 0; get_bits(bd, 1); jj += 1)
            if(jj >= bd->groupCount)
            {
                return RETVAL_DATA_ERROR;
            }

        // Decode MTF to get the next selector, and move it to the front.
        uc = bd->mtfSymbol[jj];

        while(jj)
        {
            jj = jj - 1;
            bd->mtfSymbol[jj + 1] = bd->mtfSymbol[jj];
        }

        bd->mtfSymbol[0] = bd->selectors[ii] = uc;
    }

    // Read the huffman coding tables for each group, which code for symTotal
    // literal symbols, plus two run symbols (RUNA, RUNB)
    symCount = bd->symTotal + 2;

    for(jj = 0; jj < bd->groupCount; jj += 1)
    {
        // Read lengths
        hh = get_bits(bd, 5);

        for(ii = 0; ii < symCount; ii += 1)
        {
            while(TRUE)
            {
                // !hh || hh > MAX_HUFCODE_BITS in one test.
                if(MAX_HUFCODE_BITS - 1 < hh - 1)
                {
                    return RETVAL_DATA_ERROR;
                }

                // Grab 2 bits instead of 1 (slightly smaller/faster).  Stop if
                // first bit is 0, otherwise second bit says whether to
                // increment or decrement.
                kk = get_bits(bd, 2);

                if(kk & 2)
                {
                    hh += (1 - ((kk & 1) << 1));
                }
                else
                {
                    bd->inbufBitCount += 1;
                    break;
                }
            }

            length[ii] = hh;
        }

        // Find largest and smallest lengths in this group
        minLen = maxLen = length[0];

        for(ii = 1; ii < symCount; ii += 1)
        {
            hold = length[ii];
            if(hold > maxLen)
            {
                maxLen = hold;
            }
            else if(hold < minLen)
            {
                minLen = hold;
            }
        }

        /* Calculate permute[], base[], and limit[] tables from length[].
         *
         * permute[] is the lookup table for converting huffman coded symbols
         * into decoded symbols.  It contains symbol values sorted by length.
         *
         * base[] is the amount to subtract from the value of a huffman symbol
         * of a given length when using permute[].
         *
         * limit[] indicates the largest numerical value a symbol with a given
         * number of bits can have.  It lets us know when to stop reading.
         *
         * To use these, keep reading bits until value <= limit[bitcount] or
         * youve read over 20 bits (error).  Then the decoded symbol
         * equals permute[hufcode_value - base[hufcode_bitcount]].
         */
        hufGroup = bd->groups + (group_data_array * jj);
        require(minLen > 0, "hufGroup minLen can't have negative values\n");
        require(minLen <= MAX_HUFCODE_BITS, "hufGroup minLen can't exceed MAX_HUFCODE_BITS\n");
        hufGroup->minLen = minLen;
        require(maxLen > 0, "hufGroup maxLen can't have negative values\n");
        require(maxLen <= MAX_HUFCODE_BITS, "hufGroup maxLen can't exceed MAX_HUFCODE_BITS\n");
        hufGroup->maxLen = maxLen;
        // Note that minLen cant be smaller than 1, so we adjust the base
        // and limit array pointers so were not always wasting the first
        // entry.  We do this again when using them (during symbol decoding).
        base = hufGroup->base - (int_array * 1);
        require(0 <= base, "can't have a negative hufGroup->base\n");
        limit = hufGroup->limit - (int_array * 1);
        // zero temp[] and limit[], and calculate permute[]
        pp = 0;

        for(ii = minLen; ii <= maxLen; ii += 1)
        {
            require(MAX_HUFCODE_BITS >= ii, "Invalid HUFCODE_BITS length\n");
            temp[ii] = 0;
            limit[ii] = 0;

            for(hh = 0; hh < symCount; hh += 1)
            {
                if(length[hh] == ii)
                {
                    require(MAX_SYMBOLS >= pp, "pp exceeded MAX_SYMBOLS\n");
                    hufGroup->permute[pp] = hh;
                    pp += 1;
                }
            }
        }

        // Count symbols coded for at each bit length
        for(ii = 0; ii < symCount; ii += 1)
        {
            hold = length[ii];
            require(MAX_HUFCODE_BITS >= hold, "Invalid HUFCODE_BITS length\n");
            temp[hold] += 1;
        }

        /* Calculate limit[] (the largest symbol-coding value at each bit
         * length, which is (previous limit<<1)+symbols at this level), and
         * base[] (number of symbols to ignore at each bit length, which is
         * limit minus the cumulative count of symbols coded for already). */
        pp = hh = 0;

        for(ii = minLen; ii < maxLen; ii += 1)
        {
            pp += temp[ii];
            limit[ii] = pp - 1;
            pp = pp << 1;
            hh += temp[ii];
            base[ii + 1] = pp - hh;
        }

        limit[maxLen] = pp + temp[maxLen] - 1;
        limit[maxLen + 1] = INT_MAX;
        base[minLen] = 0;
    }

    free(length);
    free(temp);
    return 0;
}

/* First pass, read blocks symbols into dbuf[dbufCount].
 *
 * This undoes three types of compression: huffman coding, run length encoding,
 * and move to front encoding.  We have to undo all those to know when weve
 * read enough input.
 */

int read_huffman_data(struct bunzip_data *bd, struct bwdata *bw)
{
    struct group_data *hufGroup;
    int ii;
    int jj;
    int kk;
    int runPos;
    int dbufCount;
    int symCount;
    int selector;
    int nextSym;
    int *byteCount;
    int *base;
    int *limit;
    unsigned hh;
    unsigned *dbuf = bw->dbuf;
    unsigned uc;
#if defined(__M2__)
    int int_array = sizeof(int);
    int group_data_array = sizeof(struct group_data);
#else
    int int_array = 1;
    int group_data_array = 1;
#endif
    // Weve finished reading and digesting the block header.  Now read this
    // blocks huffman coded symbols from the file and undo the huffman coding
    // and run length encoding, saving the result into dbuf[dbufCount++] = uc
    // Initialize symbol occurrence counters and symbol mtf table
    byteCount = bw->byteCount;

    for(ii = 0; ii < 256; ii += 1)
    {
        byteCount[ii] = 0;
        bd->mtfSymbol[ii] = ii;
    }

    // Loop through compressed symbols.  This is the first "tight inner loop"
    // that needs to be micro-optimized for speed.  (This one fills out dbuf[]
    // linearly, staying in cache more, so isnt as limited by DRAM access.)
    runPos = 0;
    dbufCount = 0;
    symCount = 0;
    selector = 0;
    // Some unnecessary initializations to shut gcc up.
    base = 0;
    limit = 0;
    hufGroup = 0;
    hh = 0;

    while(TRUE)
    {
        // Have we reached the end of this huffman group?
        if(!(symCount))
        {
            // Determine which huffman coding group to use.
            symCount = GROUP_SIZE - 1;

            if(selector >= bd->nSelectors)
            {
                return RETVAL_DATA_ERROR;
            }

            hufGroup = bd->groups + (group_data_array * bd->selectors[selector]);
            selector += 1;
            base = hufGroup->base - (int_array * 1);
            require(0 <= base, "can't have negative hufGroup->base\n");
            limit = hufGroup->limit - (int_array * 1);
        }
        else
        {
            symCount -= 1;
        }

        // Read next huffman-coded symbol (into jj).
        ii = hufGroup->minLen;
        jj = get_bits(bd, ii);

        while(jj > limit[ii])
        {
            // if (ii > hufGroup->maxLen) return RETVAL_DATA_ERROR;
            ii += 1;

            // Unroll get_bits() to avoid a function call when the datas in
            // the buffer already.
            if(bd->inbufBitCount)
            {
                bd->inbufBitCount -= 1;
                kk = (bd->inbufBits >> bd->inbufBitCount) & 1;
            }
            else
            {
                kk = get_bits(bd, 1);
            }

            jj = (jj << 1) | kk;
        }

        // Huffman decode jj into nextSym (with bounds checking)
        jj -= base[ii];

        if(ii > hufGroup->maxLen || jj >= MAX_SYMBOLS)
        {
            return RETVAL_DATA_ERROR;
        }

        nextSym = hufGroup->permute[jj];

        // If this is a repeated run, loop collecting data
        if(nextSym <= SYMBOL_RUNB)
        {
            // If this is the start of a new run, zero out counter
            if(!runPos)
            {
                runPos = 1;
                hh = 0;
            }

            /* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at
               each bit position, add 1 or 2 instead. For example,
               1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2.
               You can make any bit pattern that way using 1 less symbol than
               the basic or 0/1 method (except all bits 0, which would use no
               symbols, but a run of length 0 doesnt mean anything in this
               context). Thus space is saved. */
            hh += (runPos << nextSym); // +runPos if RUNA; +2*runPos if RUNB
            runPos = runPos << 1;
            continue;
        }

        /* When we hit the first non-run symbol after a run, we now know
           how many times to repeat the last literal, so append that many
           copies to our buffer of decoded symbols (dbuf) now. (The last
           literal used is the one at the head of the mtfSymbol array.) */
        if(runPos)
        {
            runPos = 0;

            // Check for integer overflow
            if(hh > bd->dbufSize || dbufCount + hh > bd->dbufSize)
            {
                return RETVAL_DATA_ERROR;
            }

            uc = bd->symToByte[bd->mtfSymbol[0]];
            byteCount[uc] += hh;

            while(hh)
            {
                hh -= 1;
                dbuf[dbufCount] = uc;
                dbufCount += 1;
            }
        }

        // Is this the terminating symbol?
        if(nextSym > bd->symTotal)
        {
            break;
        }

        /* At this point, the symbol we just decoded indicates a new literal
           character. Subtract one to get the position in the MTF array
           at which this literal is currently to be found. (Note that the
           result cant be -1 or 0, because 0 and 1 are RUNA and RUNB.
           Another instance of the first symbol in the mtf array, position 0,
           would have been handled as part of a run.) */
        if(dbufCount >= bd->dbufSize)
        {
            return RETVAL_DATA_ERROR;
        }

        ii = nextSym - 1;
        uc = bd->mtfSymbol[ii];

        // On my laptop, unrolling this memmove() into a loop shaves 3.5% off
        // the total running time.
        while(ii)
        {
            ii -= 1;
            bd->mtfSymbol[ii + 1] = bd->mtfSymbol[ii];
        }

        bd->mtfSymbol[0] = uc;
        uc = bd->symToByte[uc];
        // We have our literal byte.  Save it into dbuf.
        byteCount[uc] += 1;
        dbuf[dbufCount] = uc;
        dbufCount += 1;
    }

    // Now we know what dbufCount is, do a better sanity check on origPtr.
    if(bw->origPtr >= (bw->writeCount = dbufCount))
    {
        return RETVAL_DATA_ERROR;
    }

    return 0;
}

// Flush output buffer to disk
void flush_bunzip_outbuf(struct bunzip_data *bd, int out_fd)
{
    if(bd->outbufPos)
    {
        if(write(out_fd, bd->outbuf, bd->outbufPos) != bd->outbufPos)
        {
            exit(1);
        }

        bd->outbufPos = 0;
    }
}

void burrows_wheeler_prep(struct bunzip_data *bd, struct bwdata *bw)
{
    int ii;
    int jj;
    int kk;
    unsigned *dbuf = bw->dbuf;
    int *byteCount = bw->byteCount;
    unsigned uc;
    // Turn byteCount into cumulative occurrence counts of 0 to n-1.
    jj = 0;

    for(ii = 0; ii < 256; ii += 1)
    {
        kk = jj + byteCount[ii];
        byteCount[ii] = jj;
        jj = kk;
    }

    // Use occurrence counts to quickly figure out what order dbuf would be in
    // if we sorted it.
    for(ii = 0; ii < bw->writeCount; ii += 1)
    {
        uc = dbuf[ii] & 0xFF;
        dbuf[byteCount[uc]] = dbuf[byteCount[uc]] | (ii << 8);
        byteCount[uc] += 1;
    }

    // blockRandomised support would go here.
    // Using ii as position, jj as previous character, hh as current character,
    // and uc as run count.
    bw->dataCRC = 0xffffffff;

    /* Decode first byte by hand to initialize "previous" byte. Note that it
       doesnt get output, and if the first three characters are identical
       it doesnt qualify as a run (hence uc=255, which will either wrap
       to 1 or get reset). */
    if(bw->writeCount)
    {
        bw->writePos = dbuf[bw->origPtr];
        bw->writeCurrent = bw->writePos;
        bw->writePos = bw->writePos >> 8;
        bw->writeRun = -1;
    }
}

// Decompress a block of text to intermediate buffer
int read_bunzip_data(struct bunzip_data *bd)
{
    int rc = read_block_header(bd, bd->bwdata);

    if(!rc)
    {
        rc = read_huffman_data(bd, bd->bwdata);
    }

    // First thing that can be done by a background thread.
    burrows_wheeler_prep(bd, bd->bwdata);
    return rc;
}

// Undo burrows-wheeler transform on intermediate buffer to produce output.
// If !len, write up to len bytes of data to buf.  Otherwise write to out_fd.
// Returns len ? bytes written : 0.  Notice all errors are negative #s.
//
// Burrows-wheeler transform is described at:
// http://dogma.net/markn/articles/bwt/bwt.htm
// http://marknelson.us/1996/09/01/bwt/

int write_bunzip_data(struct bunzip_data *bd, struct bwdata *bw,
                      int out_fd, char *outbuf, int len)
{
    unsigned *dbuf = bw->dbuf;
    int count;
    int pos;
    int current;
    int run;
    int copies;
    int outbyte;
    int previous;
    int gotcount = 0;
    int i;
    int crc_index;

    while(TRUE)
    {
        // If last read was short due to end of file, return last block now
        if(bw->writeCount < 0)
        {
            return bw->writeCount;
        }

        // If we need to refill dbuf, do it.
        if(!bw->writeCount)
        {
            i = read_bunzip_data(bd);

            if(i)
            {
                if(i == RETVAL_LAST_BLOCK)
                {
                    bw->writeCount = i;
                    return gotcount;
                }
                else
                {
                    return i;
                }
            }
        }

        // loop generating output
        count = bw->writeCount;
        pos = bw->writePos;
        current = bw->writeCurrent;
        run = bw->writeRun;

        while(count)
        {
            // If somebody (like tar) wants a certain number of bytes of
            // data from memory instead of written to a file, humor them.
            if(len && bd->outbufPos >= len)
            {
                goto dataus_interruptus;
            }

            count -= 1;
            // Follow sequence vector to undo Burrows-Wheeler transform.
            previous = current;
            pos = dbuf[pos];
            current = pos & 0xff;
            pos = pos >> 8;

            // Whenever we see 3 consecutive copies of the same byte,
            // the 4th is a repeat count
            if(run == 3)
            {
                run += 1;
                copies = current;
                outbyte = previous;
                current = -1;
            }
            else
            {
                run += 1;
                copies = 1;
                outbyte = current;
            }

            // Output bytes to buffer, flushing to file if necessary
            while(copies)
            {
                copies -= 1;

                if(bd->outbufPos == IOBUF_SIZE)
                {
                    flush_bunzip_outbuf(bd, out_fd);
                }

                bd->outbuf[bd->outbufPos] = outbyte;
                bd->outbufPos += 1;
                crc_index = ((bw->dataCRC >> 24) ^ outbyte) & 0xFF;
                bw->dataCRC = (bw->dataCRC << 8) ^ bd->crc32Table[crc_index];
            }

            if(current != previous)
            {
                run = 0;
            }
        }

        // decompression of this block completed successfully
        bw->dataCRC = ~(bw->dataCRC);
#if defined(__M2__)

        // & 0xFFFFFFFF not working
        if(sizeof(unsigned) == 8)
        {
            bw->dataCRC <<= 32;
            bw->dataCRC >>= 32;
        }

#endif
        bd->totalCRC = ((bd->totalCRC << 1) | (bd->totalCRC >> 31)) ^ bw->dataCRC;

        // if this block had a crc error, force file level crc error.
        if(bw->dataCRC != bw->headerCRC)
        {
            bd->totalCRC = bw->headerCRC + 1;
            return RETVAL_LAST_BLOCK;
        }

dataus_interruptus:
        bw->writeCount = count;

        if(len)
        {
            gotcount += bd->outbufPos;
            memcpy(outbuf, bd->outbuf, len);
            // If we got enough data, checkpoint loop state and return
            len -= bd->outbufPos;

            if(len < 1)
            {
                bd->outbufPos -= len;

                if(bd->outbufPos)
                {
                    memmove(bd->outbuf, bd->outbuf + len, bd->outbufPos);
                }

                bw->writePos = pos;
                bw->writeCurrent = current;
                bw->writeRun = run;
                return gotcount;
            }
        }
    }
}

// Allocate the structure, read file header. If !len, src_fd contains
// filehandle to read from. Else inbuf contains data.
int start_bunzip(struct bunzip_data **bdp, int src_fd)
{
    struct bunzip_data *bd;
    unsigned i;
    // Figure out how much data to allocate.
    i = sizeof(struct bunzip_data);
    // Allocate bunzip_data. Most fields initialize to zero.
    *bdp = malloc(i);
    bd = *bdp;
    memset(bd, 0, i);
    bd->inbuf = calloc(IOBUF_SIZE, sizeof(char));
    bd->outbuf = calloc(IOBUF_SIZE, sizeof(char));
    bd->selectors = calloc(32768, sizeof(char));
    bd->groups = calloc(MAX_GROUPS, sizeof(struct group_data));

    for(i = 0; i < MAX_GROUPS; i += 1)
    {
        bd->groups[i].limit = calloc(MAX_HUFCODE_BITS + 1, sizeof(int));
        bd->groups[i].base = calloc(MAX_HUFCODE_BITS, sizeof(int));
        bd->groups[i].permute = calloc(MAX_SYMBOLS, sizeof(int));
    }

    bd->symToByte = calloc(256, sizeof(unsigned));
    bd->mtfSymbol = calloc(256, sizeof(unsigned));
    bd->crc32Table = calloc(256, sizeof(unsigned));
    bd->bwdata = calloc(1, sizeof(struct bwdata));
    bd->bwdata->byteCount = calloc(256, sizeof(int));
    unsigned *crc32Table;
    bd->in_fd = src_fd;
    crc_init(bd->crc32Table, 0);
    // Ensure that file starts with "BZh".
    char *header = "BZh";

    for(i = 0; i < 3; i += 1) if(get_bits(bd, 8) != header[i])
        {
            return RETVAL_NOT_BZIP_DATA;
        }

    // Next byte ascii 1-9, indicates block size in units of 100k of
    // uncompressed data. Allocate intermediate buffer for block.
    i = get_bits(bd, 8);

    if(i < 49 || i > 57)
    {
        return RETVAL_NOT_BZIP_DATA;
    }

    bd->dbufSize = 100000 * (i - 48);
    bd->bwdata[0].dbuf = malloc(bd->dbufSize * sizeof(int));
    return 0;
}

// Example usage: decompress src_fd to dst_fd. (Stops at end of bzip data,
// not end of file.)
int bunzipStream(int src_fd, int dst_fd)
{
    struct bunzip_data *bd;
    int i;
    int j;

    if(!(i = start_bunzip(&bd, src_fd)))
    {
        i = write_bunzip_data(bd, bd->bwdata, dst_fd, 0, 0);

        if(i == RETVAL_LAST_BLOCK)
        {
            if(bd->bwdata[0].headerCRC == bd->totalCRC)
            {
                i = 0;
            }
            else
            {
                i = RETVAL_DATA_ERROR;
            }
        }
    }

    flush_bunzip_outbuf(bd, dst_fd);
    free(bd->bwdata[0].dbuf);
    free(bd->inbuf);
    free(bd->outbuf);
    free(bd->selectors);

    for(j = 0; j < MAX_GROUPS; j += 1)
    {
        free(bd->groups[j].limit);
        free(bd->groups[j].base);
        free(bd->groups[j].permute);
    }

    free(bd->groups);
    free(bd->symToByte);
    free(bd->mtfSymbol);
    free(bd->crc32Table);
    free(bd->bwdata->byteCount);
    free(bd->bwdata);
    free(bd);
    return -i;
}

void do_bunzip2(int in_fd, int out_fd)
{
    int err = bunzipStream(in_fd, out_fd);

    if(err)
    {
        exit(1);
    }
}

int main(int argc, char **argv)
{
    char *name = NULL;
    char *dest = NULL;
    FUZZING = FALSE;

    /* process arguments */
    int i = 1;

    while(i < argc)
    {
        if(NULL == argv[i])
        {
            i += 1;
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            name = argv[i + 1];
            require(NULL != name, "the --file option requires a filename to be given\n");
            i += 2;
        }
        else if(match(argv[i], "-o") || match(argv[i], "--output"))
        {
            dest = argv[i + 1];
            require(NULL != dest, "the --output option requires a filename to be given\n");
            i += 2;
        }
        else if(match(argv[i], "--fuzzing-mode"))
        {
            FUZZING = TRUE;
            i += 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file $input.bz2", stderr);
            fputs(" --output $output\n", stderr);
            fputs("--help to get this message\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option:", stderr);
            fputs(argv[i], stderr);
            fputs("\nAborting to avoid problems\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    /* Deal with no input */
    if(NULL == name)
    {
        fputs("an input file (--file $name) must be provided\n", stderr);
        exit(EXIT_FAILURE);
    }

    int in_fd = open(name, 0, 0);

    if(in_fd < 0)
    {
        fputs("Unable to open input file\n", stderr);
        exit(EXIT_FAILURE);
    }

    /* If an output name isn't provided */
    if(NULL == dest)
    {
        int length = strlen(name);
        require(length > 4, "file name length not sufficient, please provide output name with --output $filename\n");
    }

    int out_fd;
    if(FUZZING)
    {
        /* Dump to /dev/null the garbage data produced during fuzzing */
        out_fd = open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0600);
    }
    else
    {
        out_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, 0600);
    }

    if(out_fd < 0)
    {
        fputs("Unable to open output file for writing\n", stderr);
        exit(EXIT_FAILURE);
    }

    do_bunzip2(in_fd, out_fd);
    close(in_fd);
    close(out_fd);
    exit(0);
}

https://github.com/oriansj/mescc-tools-extra/blob/main/catm.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/catm.c used in: Step 72
/* Copyright (C) 2019 Jeremiah Orians
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

// CONSTANT BUFFER_SIZE 4096
#define BUFFER_SIZE 4096

int main(int argc, char** argv)
{
    if(2 > argc)
    {
        fputs("catm requires 2 or more arguments\n", stderr);
        exit(EXIT_FAILURE);
    }

    int output = open(argv[1], 577 , 384);
    if(-1 == output)
    {
        fputs("The file: ", stderr);
        fputs(argv[1], stderr);
        fputs(" is not a valid output file name\n", stderr);
        exit(EXIT_FAILURE);
    }

    int i;
    int bytes;
    char* buffer = calloc(BUFFER_SIZE + 1, sizeof(char));
    int input;
    for(i = 2; i < argc ; i =  i + 1)
    {
        input = open(argv[i], 0, 0);
        if(-1 == input)
        {
            fputs("The file: ", stderr);
            fputs(argv[i], stderr);
            fputs(" is not a valid input file name\n", stderr);
            exit(EXIT_FAILURE);
        }
keep:
        bytes = read(input, buffer, BUFFER_SIZE);
        write(output, buffer, bytes);
        if(BUFFER_SIZE == bytes) goto keep;
    }

    free(buffer);
    return EXIT_SUCCESS;
}

https://github.com/oriansj/mescc-tools-extra/blob/main/cp.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/cp.c used in: Step 73
/* Copyright (C) 2020 fosslinux
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "M2libc/bootstrappable.h"

/* Define all of the constants */
// CONSTANT FALSE 0
#define FALSE 0
// CONSTANT TRUE 1
#define TRUE 1
// CONSTANT MAX_STRING 4096
#define MAX_STRING 4096
// CONSTANT MAX_ARRAY 256
#define MAX_ARRAY 256

/* Prototypes for external funcs */

/* Globals */
int verbose;

/* UTILITY FUNCTIONS */

/* Function to find a character's position in a string (last match) */
int find_last_char_pos(char* string, char a)
{
    int i = strlen(string) - 1;
    if(i < 0) return i;
    while(i >= 0)
    {
        /*
         * This conditional should be in the while conditional but we are
         * running into the M2-Planet short-circuit bug.
         */
        if(a == string[i]) break;
        i = i - 1;
    }
    return i;
}

/* Function to find the length of a char**; an array of strings */
int array_length(char** array)
{
    int length = 0;
    while(array[length] != NULL)
    {
        length = length + 1;
    }
    return length;
}

/* PROCESSING FUNCTIONS */

char* directory_dest(char* dest, char* source, int require_directory)
{
    /*
     * First, check if it is a directory to copy to.
     * We have two ways of knowing this:
     * - If the destination ends in a slash, the user has explicitly said
     *   it is a directory.
     * - Normally we would use stat() but we don't want to force support for
     *   that syscall onto the kernel, so we just attempt to chdir() into it
     *   and if it works then it must be a directory. A bit hacky, bit it
     *   works.
     */
    int isdirectory = FALSE;
    if(dest[strlen(dest) - 1] == '/')
    {
        isdirectory = TRUE;
    }
    if(!isdirectory)
    { /* Use the other testing method */
        /*
         * Get the current path so that we can chdir back to it if it does
         * chdir successfully.
         */
        char* current_path = calloc(MAX_STRING, sizeof(char));
        require(current_path != NULL, "Memory initialization of current_path in directory_dest failed\n");
        getcwd(current_path, MAX_STRING);
        require(!match("", current_path), "getcwd() failed\n");
        /*
         * chdir expects an absolute path.
         * If the first character is / then it is already absolute, otherwise
         * it is relative and needs to be changed (by appending current_path
         * to the dest path).
         */
        char* chdir_dest = calloc(MAX_STRING, sizeof(char));
        require(chdir_dest != NULL, "Memory initialization of chdir_dest in directory_dest failed\n");
        if(dest[0] != '/')
        { /* The path is relative, append current_path */
            strcat(chdir_dest, current_path);
            strcat(chdir_dest, "/");
            strcat(chdir_dest, dest);
        }
        else
        { /* The path is absolute */
            strcpy(chdir_dest, dest);
        }
        if(0 <= chdir(chdir_dest))
        { /* chdir returned successfully */
            /*
             * But because of M2-Planet, that dosen't mean anything actually
             * happened, check that before we go any further.
             */
            char* new_path = calloc(MAX_STRING, sizeof(char));
            require(new_path != NULL, "Memory initialization of new_path in directory_dest failed\n");
            getcwd(new_path, MAX_STRING);
            if(!match(current_path, new_path))
            {
                isdirectory = TRUE;
                chdir(current_path);
            }
        }
        free(chdir_dest);
        free(current_path);
    }

    /*
     * If it isn't a directory, and we require one, error out.
     * Otherwise, just return what we were given, we're done here.
     */
    if(require_directory) require(isdirectory, "Provide a directory destination for multiple source files\n");
    if(!isdirectory) return dest;

    /* If it is, we need to make dest a full path */
    /* 1. Get the basename of source */
    char* basename = calloc(MAX_STRING, sizeof(char));
    require(basename != NULL, "Memory initialization of basename in directory_dest failed\n");
    int last_slash_pos = find_last_char_pos(source, '/');
    if(last_slash_pos >= 0)
    { /* Yes, there is a slash in it, copy over everything after that pos */
        unsigned spos; /* source pos */
        unsigned bpos = 0; /* basename pos */
        /* Do the actual copy */
        for(spos = last_slash_pos + 1; spos < strlen(source); spos = spos + 1)
        {
            basename[bpos] = source[spos];
            bpos = bpos + 1;
        }
    }
    else
    { /* No, there is no slash in it, hence the basename is just the source */
        strcpy(basename, source);
    }
    /* 2. Ensure our dest (which is a directory) has a trailing slash */
    if(dest[strlen(dest) - 1] != '/')
    {
        strcat(dest, "/");
    }
    /* 3. Add the basename to the end of the directory */
    strcat(dest, basename);
    free(basename);

    /* Now we have a returnable path! */
    return dest;
}

void copy_file(char* source, char* dest)
{
    if(verbose)
    { /* Output message */
        /* Of the form 'source' -> 'dest' */
        fputs("'", stdout);
        fputs(source, stdout);
        fputs("' -> '", stdout);
        fputs(dest, stdout);
        fputs("'\n", stdout);
    }

    /* Open source and dest as FILE*s */
    FILE* fsource = fopen(source, "r");
    if(fsource == NULL)
    {
        fputs("Error opening source file ", stderr);
        fputs(source, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }
    FILE* fdest = fopen(dest, "w");
    if(fdest < 0)
    {
        fputs("Error opening destination file", stderr);
        fputs(dest, stderr);
        fputc('\n', stderr);
        exit(EXIT_FAILURE);
    }

    /*
     * The following loop reads a character from the source and writes it to the
     * dest file. This is all M2-Planet supports.
     */
    int c = fgetc(fsource);
    while(c != EOF)
    {
        fputc(c, fdest);
        c = fgetc(fsource);
    }

    /* Cleanup */
    fclose(fsource);
    fclose(fdest);
}

int main(int argc, char** argv)
{
    /* Initialize variables */
    char** sources = calloc(MAX_ARRAY, sizeof(char*));
    require(sources != NULL, "Memory initialization of sources failed\n");
    int sources_index = 0;
    char* dest = NULL;

    /* Set defaults */
    verbose = FALSE;

    int i = 1;
    int j;
    int args_found;
    /* Loop arguments */
    while(i <= argc)
    {
        if(NULL == argv[i])
        { /* Ignore and continue */
            i = i + 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("Usage: ", stdout);
            fputs(argv[0], stdout);
            fputs(" [-h | --help] [-V | --version] [-v | --verbose] source1 source2 sourcen destination\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-V") || match(argv[i], "--version"))
        { /* Output version */
            fputs("cp version 1.2.1\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-v") || match(argv[i], "--verbose"))
        {
            verbose = TRUE;
            i = i + 1;
        }
        else if(argv[i][0] != '-')
        { /* It is not an option */
            /*
             * We can tell if this is the source file or the destination file
             * through looking *ahead*. If it is the last of this type of argument then
             * it must be the destination. (1 destination, many sources).
             */
            j = i + 1;
            args_found = 0;
            while(j < array_length(argv))
            {
                if(argv[j][0] != '-')
                { /* It's one of these type of arguments */
                    args_found = args_found + 1;
                }
                j = j + 1;
            }
            if(args_found == 0)
            { /* We are setting the destination (there are no more left after this) */
                dest = calloc(MAX_STRING, sizeof(char));
                require(dest != NULL, "Memory initialization of dest failed\n");
                strcpy(dest, argv[i]);
            }
            else
            { /* We are setting a source */
                require(sources_index < MAX_ARRAY, "Too many files\n");
                sources[sources_index] = calloc(MAX_STRING, sizeof(char));
                require(sources[sources_index] != NULL, "Memory initialization of sources[source_index] failed\n");
                strcpy(sources[sources_index], argv[i]);
                sources_index = sources_index + 1;
            }
            i = i + 1;
        }
        else
        { /* Unknown argument */
            fputs("UNKNOWN_ARGUMENT\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    /* Sanitize values */
    /* Ensure the two values have values */
    /* Another workaround for short-circuit bug */
    int error = FALSE;
    if(sources[0] == NULL) error = TRUE;
    if(error == FALSE) if(match(sources[0], "")) error = TRUE;
    require(!error, "Provide a source file\n");
    error = FALSE;
    if(dest == NULL) error = TRUE;
    if(error == FALSE) if(match(dest, "")) error = TRUE;
    require(!error, "Provide a destination file\n");

    /* Loop through all of the sources, copying each one */
    char* this_dest;
    for(i = 0; i < array_length(sources); i = i + 1)
    {
        /* Convert the dest variable to a full path if it's a directory copying to */
        /*
         * Also, if there is more than one source, we have to be copying to
         * a directory destination...
         */
        if(array_length(sources) == 1)
        {
            dest = directory_dest(dest, sources[i], FALSE);
            copy_file(sources[i], dest);
        }
        else
        {
            this_dest = calloc(MAX_STRING, sizeof(char));
            require(this_dest != NULL, "Memory initalization of this_dest failed\n");
            this_dest = directory_dest(dest, sources[i], TRUE);
            copy_file(sources[i], this_dest);
        }
        /* Perform the actual copy */
        free(sources[i]);
    }

    free(sources);
    free(dest);

    return EXIT_SUCCESS;
}

https://github.com/oriansj/mescc-tools-extra/blob/main/chmod.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/chmod.c used in: Step 74
/* Copyright (C) 2020 fosslinux
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "M2libc/bootstrappable.h"

/* Define all of the constants */
// CONSTANT FALSE 0
#define FALSE 0
// CONSTANT TRUE 1
#define TRUE 1
// CONSTANT MAX_STRING 4096
#define MAX_STRING 4096
// CONSTANT MAX_ARRAY 256
#define MAX_ARRAY 256

struct files
{
    char* name;
    struct files* next;
};

/* Globals */
int verbose;

/* PROCESSING FUNCTIONS */
int main(int argc, char** argv)
{
    /* Initialize variables */
    char* mode = NULL;
    struct files* f = NULL;
    struct files* n;
    int ok;

    /* Set defaults */
    verbose = FALSE;

    int i = 1;
    /* Loop arguments */
    while(i <= argc)
    {
        if(NULL == argv[i])
        { /* Ignore and continue */
            i = i + 1;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("Usage: ", stdout);
            fputs(argv[0], stdout);
            fputs(" [-h | --help] [-V | --version] [-v | --verbose]\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-V") || match(argv[i], "--version"))
        { /* Output version */
            fputs("chmod version 1.2.1\n", stdout);
            exit(EXIT_SUCCESS);
        }
        else if(match(argv[i], "-v") || match(argv[i], "--verbose"))
        {
            verbose = TRUE;
            i = i + 1;
        }
        else
        { /* It must be the file or the mode */
            if(mode == NULL)
            { /* Mode always comes first */
                mode = calloc(MAX_STRING, sizeof(char));
                require(mode != NULL, "Memory initialization of mode failed\n");
                /* We need to indicate it is octal */
                strcat(mode, "0");
                strcat(mode, argv[i]);
            }
            else
            { /* It's a file, as the mode is already done */
                n = calloc(1, sizeof(struct files));
                require(n != NULL, "Memory initialization of files failed\n");
                n->next = f;
                f = n;
                f->name = argv[i];
            }
            i = i + 1;
        }
    }

    /* Ensure the two values have values */
    require(mode != NULL, "Provide a mode\n");
    require(f != NULL, "Provide a file\n");

    /* Convert the mode str into octal */
    int omode = strtoint(mode);

    /* Loop over files to be operated on */
    while(NULL != f)
    {
        /* Make sure the file can be opened */
        ok = access(f->name, 0);
        if(ok != 0)
        {
            fputs("The file: ", stderr);
            fputs(f->name, stderr);
            fputs(" does not exist\n", stderr);
            exit(EXIT_FAILURE);
        }

        /* Verbose message */
        if(verbose)
        {
            fputs("mode of '", stdout);
            fputs(f->name, stdout);
            fputs("' changed to ", stdout);
            fputs(mode, stdout);
            fputs("\n", stdout);
        }

        /* Perform the chmod */
        chmod(f->name, omode);
        f = f->next;
    }
}

https://github.com/oriansj/mescc-tools-extra/blob/main/rm.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/rm.c used in: Step 75
/* Copyright (C) 2021 Jeremiah Orians
 * This file is part of mescc-tools-extra
 *
 * mescc-tools-extra is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools-extra 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 mescc-tools-extra.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * "rm" can be used to delete files. It can also delete
 * parent directories.
 *
 * Usage: rm <dir1>/<file1> <file2>
 *
 * These are all highly standard and portable headers. */
#include <stdio.h>
#include <string.h>

/* This is for unlink() ; this may need to be changed for some platforms. */
#include <unistd.h>  /* For unlink() */
#include <stdlib.h>
#include "M2libc/bootstrappable.h"

void delete_dir(char* name)
{
    int r = unlink(name);
    if(0 != r)
    {
        fputs("unable to delete file: ", stderr);
        fputs(name, stderr);
        fputs(" !!!\n", stderr);
    }
}

int main(int argc, char **argv)
{
    int i;
    for(i = 1; argc > i; i = i + 1)
    {
        delete_dir(argv[i]);
    }

    return 0;
}

https://github.com/oriansj/mescc-tools-extra/blob/main/replace.c

File: ../git/live-bootstrap/sysa/stage0-posix/src/mescc-tools-extra/replace.c used in: Step 76
/* Copyright (C) 2019 Jeremiah Orians
 * This file is part of mescc-tools
 *
 * mescc-tools is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * mescc-tools 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 mescc-tools.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "M2libc/bootstrappable.h"

char* input_name;
FILE* input;
char* output_name;
FILE* output;
char* pattern;
size_t pattern_length;
char* replacement;
char* buffer;
size_t buffer_index;
char* hold;

void read_next_byte()
{
    int c= hold[0];
    size_t i = 0;
    while(i < pattern_length)
    {
        hold[i] = hold[i+1];
        i = i + 1;
    }

    hold[pattern_length-1] = buffer[buffer_index];
    buffer_index = buffer_index + 1;

    /* NEVER WRITE NULLS!!! */
    if(0 != c) fputc(c, output);
}

void clear_hold()
{
    /* FILL hold with NULLS */
    size_t i = 0;
    while(i < pattern_length)
    {
        hold[i] = 0;
        i = i + 1;
    }
}

void check_match()
{
    /* Do the actual replacing */
    if(match(pattern, hold))
    {
        fputs(replacement, output);
        clear_hold();
    }
}

int main(int argc, char** argv)
{
    output_name = "/dev/stdout";
    pattern = NULL;
    replacement = NULL;
    buffer_index = 0;

    int i = 1;
    while (i < argc)
    {
        if(NULL == argv[i])
        {
            i = i + 1;
        }
        else if(match(argv[i], "-f") || match(argv[i], "--file"))
        {
            input_name = argv[i+1];
            require(NULL != input_name, "the --file option requires a filename to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "-o") || match(argv[i], "--output"))
        {
            output_name = argv[i+1];
            require(NULL != output_name, "the --output option requires a filename to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "-m") || match(argv[i], "--match-on"))
        {
            pattern = argv[i+1];
            require(NULL != pattern, "the --match-on option requires a string to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "-r") || match(argv[i], "--replace-with"))
        {
            replacement = argv[i+1];
            require(NULL != replacement, "the --replace-with option requires a string to be given\n");
            i = i + 2;
        }
        else if(match(argv[i], "-h") || match(argv[i], "--help"))
        {
            fputs("Usage: ", stderr);
            fputs(argv[0], stderr);
            fputs(" --file $input", stderr);
            fputs(" --match-on $string", stderr);
            fputs(" --replace-with $string", stderr);
            fputs(" [--output $output] (or it'll dump to stdout)\n", stderr);
            fputs("--help to get this message\n", stderr);
            exit(EXIT_SUCCESS);
        }
        else
        {
            fputs("Unknown option:", stderr);
            fputs(argv[i], stderr);
            fputs("\nAborting to avoid problems\n", stderr);
            exit(EXIT_FAILURE);
        }
    }

    /* Sanity check that we got everything we need */
    require(NULL != input_name, "You need to pass an input file with --file\n");
    require(NULL != output_name, "You need to pass an output file with --output\n");
    require(NULL != pattern, "You can't do a replacement without something to match on\n");
    require(NULL != replacement, "You can't do a replacement without something to replace it with\n");

    input = fopen(input_name, "r");
    require(NULL != input, "unable to open requested input file!\n");

    /* Get enough buffer to read it all */
    fseek(input, 0, SEEK_END);
    size_t size = ftell(input);
    buffer = malloc((size + 8) * sizeof(char));

    /* Save ourself work if the input file is too small */
    pattern_length = strlen(pattern);
    require(pattern_length < size, "input file is to small for pattern\n");

    /* Now read it all into buffer */
    fseek(input, 0, SEEK_SET);
    size_t r = fread(buffer,sizeof(char), size, input);
    require(r == size, "incomplete read of input\n");
    fclose(input);

    /* Now we can safely open the output (which could have been the same as the input */
    output = fopen(output_name, "w");
    require(NULL != input, "unable to open requested output file!\n");

    /* build our match buffer */
    hold = calloc(pattern_length + 4, sizeof(char));
    require(NULL != hold, "temp memory allocation failed\n");

    /* Replace it all */
    while((size + pattern_length + 4) >= buffer_index)
    {
        read_next_byte();
        check_match();
    }
    fclose(output);
}

https://github.com/oriansj/stage0-posix/blob/main/x86.answers

File: ../git/live-bootstrap/sysa/stage0-posix/src/x86.answers used in: Step 77
275d135eea485d900553def672a53a1d3c627010660da3e5834658154bc9b8e6  x86/bin/blood-elf
dee788ac575fbaea8695c5f2f79fda5593fa23b5aae14420b74df0a6fa15be45  x86/bin/catm
7a2bd22b80a88d28d88df2193d71d506dbd09713b412287a71449ef8f727d90a  x86/bin/chmod
7b4e27df73125e37b2ca6fac8fec5ad14a6ffe3d9efdebbddeb675c59175ca25  x86/bin/cp
941863a4dc7188c44a38cf312b7259f4cd32dfe1b75775db108dd09ccc1595ca  x86/bin/get_machine
92b2414feba8744bc41ad4b165004ce42a4c47208094868d0b60e9ac6cdf229a  x86/bin/hex2
967caba7eb0f3223d685f67341e0df072c9700fed3dd61e892a3cce1f4404cb4  x86/bin/kaem
b7e7e05714748e919e2ec8f985afd4e5c8a08dca6384f3179721a2ca5845a57a  x86/bin/M1
f8c09c42c203fb21c3bb8ff6537df7864b85007a72380bd06cd1bfb5acd92588  x86/bin/M2-Mesoplanet
e33eb9c487d1e34bcca70e0d55c158cc24ed5b671fba72db8102182daa0d35a8  x86/bin/M2-Planet
481ad71972d5fece4de35dd2c357910d3740fbd5d18bcab1ae0353fcde45d846  x86/bin/match
1e3f266c93d56e4ee05fec8422f92e9261c674a09b22efa31b4198172be07b6f  x86/bin/mkdir
47bcca1d7f9a07df1256c6372fbcd14afb98b0e53f4cd3a42eb73bcef1468aa2  x86/bin/replace
91d2a629a99f9b23c66acdc5f156a21bf1b26306031ca2ad3da4869e142d29d8  x86/bin/rm
130cdb574ea4e78b703182387d610d27bff3fb5bc9f19c8f6a22d3b88e47a796  x86/bin/sha256sum
c43dc403413cdd11e5d10159219a6984c3571fd3bc025f23123257b6d03641b5  x86/bin/ungz
caf4adc1d50b35147f16a03cbca4b45339b11d3aec260d13eb21050185438d3a  x86/bin/unbz2
b8339525443c02be31b0b6d55cbc0cdebb7921ce641eff1596800a1be1a874e5  x86/bin/untar

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/checksum-transcriber.c

File: ../git/live-bootstrap/sysa/checksum-transcriber.c used in: Step 79
/*
 * SPDX-FileCopyrightText: 2022 fosslinux <fosslinux@aussies.space>
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <bootstrappable.h>
#include <unistd.h>

#define MAX_STRING 4096
#define MAX_TOKENS 3

char *get_distfiles(char **envp) {
    char *envvar = "distfiles=";
    int i = 0;
    while (envp[i] != NULL && strncmp(envp[i], envvar, strlen(envvar)) != 0) i += 1;
    // Now we have distfiles= - get just the part we want.
    require(envp[i] != NULL, "Unable to find distfiles environment variable");
    return envp[i] + strlen(envvar);
}

int main(int argc, char **argv, char **envp) {
    // Random file things
    require(argc == 2, "Usage: checksum-transcriber FILENAME");
    char *input = argv[1];
    FILE *in = fopen(input, "r");
    require(in != NULL, "File does not exist");
    char *output = calloc(MAX_STRING, sizeof(char));
    require(strcpy(output, input) != NULL, "Failed copying string");
    require(strcat(output, ".SHA256SUM") != NULL, "Failed concating string");
    FILE *out = fopen(output, "w+");
    require(out != NULL, "Failed opening output file");

    char *orig_line;
    char *line = calloc(MAX_STRING, sizeof(char));
    require(line != NULL, "Failed allocating string");
    char **tokens;
    char *new_line;
    char *checksum;
    char *filename;
    int i;
    fgets(line, MAX_STRING, in);
    while (strlen(line) != 0) {
        // Split each line into tokens
        orig_line = line;
        tokens = calloc(MAX_TOKENS, sizeof(char*));
        i = 0;
        while (i < MAX_TOKENS) {
            tokens[i] = line;
            new_line = strchr(line, ' ');
            // Occurs when there are only two tokens
            if (new_line == NULL) break;
            line = new_line;
            line[0] = '\0';
            line += 1;
            i += 1;
        }
        line = strchr(line, '\n');
        line[0] = '\0';
        // Get checksum and filename
        checksum = tokens[1];
        if (tokens[2] != NULL) {
            filename = tokens[2];
        } else {
            filename = strrchr(tokens[0], '/');
            filename += 1;
        }
        // Put it all together
        fputs(checksum, out);
        fputs("  ", out);
        fputs(get_distfiles(envp), out);
        fputc('/', out);
        fputs(filename, out);
        fputc('\n', out);
        // Cleanup
        i = 0;
        free(orig_line);
        free(tokens);
        line = calloc(MAX_STRING, sizeof(char));
        require(line != NULL, "Failed allocating string");
        fgets(line, MAX_STRING, in);
    }

    // Clean up
    fclose(in);
    fclose(out);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/checksum-transcriber.x86.SHA256SUM

File: ../git/live-bootstrap/sysa/checksum-transcriber.x86.SHA256SUM used in: Step 80
216fe50dd8e70928f382d40ced1d9a2c167f7a5aafd555901194c5559af8c526  /usr/bin/checksum-transcriber

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/simple-patch.c

File: ../git/live-bootstrap/sysa/simple-patch.c used in: Step 81
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "M2libc/bootstrappable.h"

/*
SPDX-FileCopyrightText: 2023 Richard Masters <grick23@gmail.com>
SPDX-License-Identifier: MIT

Simple Patch program.

This program is written in a subset of C called M2, which is from the
stage0-posix bootstrap project.

Example usage:
./simple-patch file_to_patch before_pattern_file after_pattern_file

*/

// function prototypes
void read_file_or_die(char *file_name, char **buffer, int *file_size);
void patch_buffer_or_die(char *patch_file_before_buffer, int patch_file_before_size,
                 char *before_pattern_buffer, int before_pattern_size,
                 char *after_pattern_buffer, int after_pattern_size,
                 char *patch_file_after_buffer);
void writestr_fd(int fd, char *str);
int memsame(char *search_buffer, int search_size,
            char *pattern_buffer, int pattern_size);


int main(int argc, char **argv) {
    char *patch_file_before_buffer;
    int patch_file_before_size;

    char *before_pattern_buffer;
    int before_pattern_size;

    char *after_pattern_buffer;
    int after_pattern_size;

    int patch_file_after_size;
    char *patch_file_after_buffer;

    int patch_file_fd;

    read_file_or_die(argv[1], &patch_file_before_buffer, &patch_file_before_size);
    read_file_or_die(argv[2], &before_pattern_buffer, &before_pattern_size);
    read_file_or_die(argv[3], &after_pattern_buffer, &after_pattern_size);

    patch_file_after_size = patch_file_before_size - before_pattern_size + after_pattern_size;
    patch_file_after_buffer = calloc(patch_file_after_size, sizeof(char));

    patch_buffer_or_die(patch_file_before_buffer, patch_file_before_size,
                 before_pattern_buffer, before_pattern_size,
                 after_pattern_buffer, after_pattern_size,
                 patch_file_after_buffer);
    
    patch_file_fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0);
    write(patch_file_fd, patch_file_after_buffer, patch_file_after_size);
    close(patch_file_fd);

    return EXIT_SUCCESS;
}


void read_file_or_die(char *file_name, char **buffer, int *file_size) {
    int file_fd;
    int num_bytes_read;

    file_fd = open(file_name, O_RDONLY, 0);
    if (file_fd == -1) {
        writestr_fd(2, "Could not open file: ");
        writestr_fd(2, file_name);
        writestr_fd(2, "\n");
        exit(1);
    }
    // determine file size
    *file_size = lseek(file_fd, 0, SEEK_END);
    // go back to beginning of file
    lseek(file_fd, 0, SEEK_SET);
    // alloc a buffer to read the entire file
    *buffer = calloc(*file_size, sizeof(char));

    // read the entire patch file
    num_bytes_read = read(file_fd, *buffer, *file_size);
    if (num_bytes_read != *file_size) {
        writestr_fd(2, "Could not read file: ");
        writestr_fd(2, file_name);
        writestr_fd(2, "\n");
        exit(1);
    }
    close(file_fd);
}

void patch_buffer_or_die(char *patch_file_before_buffer, int patch_file_before_size,
                 char *before_pattern_buffer, int before_pattern_size,
                 char *after_pattern_buffer, int after_pattern_size,
                 char *patch_file_after_buffer) {

    char *pos = patch_file_before_buffer;
    int prefix_len = 0;

    // look for the pattern at every offset
    while (prefix_len < patch_file_before_size) {
        // if we find the pattern, replace it and return
        if (memsame(pos, patch_file_before_size - prefix_len, before_pattern_buffer, before_pattern_size)) {
           memcpy(patch_file_after_buffer, patch_file_before_buffer, prefix_len);
           memcpy(patch_file_after_buffer + prefix_len, after_pattern_buffer, after_pattern_size);
           memcpy(patch_file_after_buffer + prefix_len + after_pattern_size, 
                  patch_file_before_buffer + prefix_len + before_pattern_size, 
                  patch_file_before_size - (prefix_len + before_pattern_size));
           return;
        }
        pos = pos + 1;
        prefix_len = prefix_len + 1;
    }

    /* if we don't find the pattern, something is wrong, so exit with error */
    exit(1);
}

/*
    Write the string to the given file descriptor.
*/
void writestr_fd(int fd, char *str) {
    write(fd, str, strlen(str));
}

/*
    Is the pattern located at the start of the search buffer
    (and not exceeding the length of the search buffer)?
*/

int memsame(char *search_buffer, int search_size,
            char *pattern_buffer, int pattern_size) {
    int check_offset = 0;

    if (pattern_size > search_size) {
        return FALSE;
    }
    while (check_offset < pattern_size) {
        if (search_buffer[check_offset] != pattern_buffer[check_offset]) {
             return FALSE;
        }
        check_offset = check_offset + 1;
    }
    return TRUE;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/mes-0.24.2/sources

File: ../git/live-bootstrap/sysa/mes-0.24.2/sources used in: Step 85
https://mirrors.kernel.org/gnu/mes/mes-0.24.2.tar.gz 7ddae0854e46ebfa18c13ab37e64839a7b86ea88aeed366a8d017efd11dae86e
https://download.savannah.gnu.org/releases/nyacc/nyacc-1.00.2.tar.gz f36e4fb7dd524dc3f4b354d3d5313f69e7ce5a6ae93711e8cf6d51eaa8d2b318

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/mes-0.24.2/files/config.h

File: ../git/live-bootstrap/sysa/mes-0.24.2/files/config.h used in: Step 94, Step 121, Step 122, Step 123, Step 124, Step 125, Step 128, Step 129, Step 130, Step 137, Step 138, Step 139, Step 140, Step 141, Step 142, Step 143, Step 144, Step 145, Step 146, Step 147, Step 148, Step 149, Step 150, Step 151, Step 152, Step 153, Step 154, Step 155, Step 156, Step 157, Step 158, Step 159, Step 160, Step 161, Step 162, Step 163, Step 164, Step 165, Step 166, Step 167, Step 168, Step 170, Step 171, Step 172, Step 173, Step 174, Step 175, Step 176, Step 177, Step 182, Step 189, Step 190, Step 191, Step 192, Step 198, Step 199, Step 200, Step 201, Step 202, Step 203, Step 205, Step 212, Step 213, Step 214, Step 215, Step 216, Step 217, Step 218, Step 219, Step 220, Step 221, Step 222, Step 223, Step 224, Step 225, Step 226, Step 227, Step 228, Step 229, Step 230, Step 236, Step 237, Step 238, Step 239, Step 240, Step 241, Step 242, Step 243, Step 244, Step 245, Step 246, Step 247, Step 248, Step 249, Step 250, Step 251, Step 252, Step 253, Step 254, Step 255, Step 256, Step 257, Step 259, Step 262, Step 264, Step 277, Step 278, Step 279, Step 280, Step 281, Step 283
// SPDX-FileCopyrightText: 2020 fosslinux <fosslinux@aussies.space>
// SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
//
// SPDX-License-Identifier: GPL-3.0-or-later

#undef SYSTEM_LIBC
#define MES_VERSION "0.24.2"

https://git.savannah.gnu.org/cgit/mes.git/tree/include/linux/x86/syscall.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_LINUX_X86_SYSCALL_H
#define __MES_LINUX_X86_SYSCALL_H 1

/* libc-mini */
#ifndef SYS_exit
// CONSTANT SYS_exit 0x01
#define SYS_exit    0x01
#endif
#ifndef SYS_write
// CONSTANT SYS_write 0x04
#define SYS_write   0x04
#endif

/* libc */
// CONSTANT SYS_fork 0x02
#define SYS_fork    0x02
// CONSTANT SYS_read 0x03
#define SYS_read    0x03
// CONSTANT SYS_open 0x05
#define SYS_open    0x05
// CONSTANT SYS_waitpid 0x07
#define SYS_waitpid 0x07
// CONSTANT SYS_wait4 0x72
#define SYS_wait4   0x72
// CONSTANT SYS_execve 0x0b
#define SYS_execve  0x0b
// CONSTANT SYS_chmod 0x0f
#define SYS_chmod   0x0f
// CONSTANT SYS_access 0x21
#define SYS_access  0x21
// CONSTANT SYS_brk 0x2d
#define SYS_brk     0x2d
// CONSTANT SYS_ioctl 0x36
#define SYS_ioctl   0x36
// CONSTANT SYS_fsync 0x76
#define SYS_fsync   0x76
// CONSTANT SYS_getcwd 0xb7
#define SYS_getcwd 0xb7
// CONSTANT SYS_dup 0x29
#define SYS_dup       0x29
// CONSTANT SYS_dup2 0x3f
#define SYS_dup2      0x3f
// CONSTANT SYS_unlink 0x0a
#define SYS_unlink 0x0a
// CONSTANT SYS_gettimeofday 0x4e
#define SYS_gettimeofday 0x4e
// CONSTANT SYS_clock_gettime 0x109
#define SYS_clock_gettime 0x109
// CONSTANT SYS_time 0x0d
#define SYS_time   0x0d

/* libc+tcc */
#define SYS_close  0x06
#define SYS_lseek  0x13
#define SYS_rmdir  0x28
#define SYS_stat   0x6a

/* libc+gnu */
#define SYS_chdir     0x0c
#define SYS_link      0x09
#define SYS_getpid    0x14
#define SYS_getuid    0x18
#define SYS_kill      0x25
#define SYS_rename    0x26
#define SYS_mkdir     0x27
#define SYS_pipe      0x2a
#define SYS_getgid    0x2f
#define SYS_signal    0x30
#define SYS_sigaction 0x43
#define SYS_rt_sigaction 0xae
#define SYS_signal    0x30
#define SYS_fcntl     0x37
#define SYS_getrusage 0x4d
#define SYS_lstat     0x6b
#define SYS_setitimer 0x68
#define SYS_fstat     0x6c
#define SYS_nanosleep 0xa2
#define SYS_getdents  0x8d

/* bash */
#define SYS_setuid    0x17
#define SYS_geteuid   0x31
#define SYS_getegid   0x32
#define SYS_setgid    0x3e
#define SYS_getppid   0x40

/* make+POSIX */
#define SYS_sigprocmask 0x7e

/* tar */
#define SYS_symlink   0x53
#define SYS_readlink  0x55
#define SYS_mknod     0x0e

#if __SIZEOF_LONG_LONG__ == 8

#define SYS_stat64     0xc3
#define SYS_lstat64    0xc4
#define SYS_fstat64    0xc5
#define SYS_fcntl64    0xdd
#define SYS_getdents64 0xdc

#undef SYS_stat
#define SYS_stat SYS_stat64

#undef SYS_lstat
#define SYS_lstat SYS_lstat64

#undef SYS_fstat
#define SYS_fstat SYS_fstat64

#endif  // __SIZEOF_LONG_LONG__ == 8

#endif /* __MES_LINUX_X86_SYSCALL_H */

https://git.savannah.gnu.org/cgit/mes.git/tree/include/linux/x86/kernel-stat.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/linux/x86/kernel-stat.h used in: Step 97
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_LINUX_X86_KERNEL_STAT_H
#define __MES_LINUX_X86_KERNEL_STAT_H 1

// https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/stat.h

#include <arch/syscall.h>

#if __SIZEOF_LONG_LONG__ != 8

// *INDENT-OFF*
struct stat
{
  unsigned long  st_dev;
  unsigned long  st_ino;
  unsigned short st_mode;
  unsigned short st_nlink;
  unsigned short st_uid;
  unsigned short st_gid;
  unsigned long  st_rdev;
  unsigned long  st_size;
  unsigned long  st_blksize;
  unsigned long  st_blocks;
  unsigned long  st_atime;
  unsigned long  st_atime_usec;
  unsigned long  st_mtime;
  unsigned long  st_mtime_usec;
  unsigned long  st_ctime;
  unsigned long  st_ctime_usec;
  unsigned long  __pad0;
  unsigned long  __pad1;
};

#else // __SIZEOF_LONG_LONG__ == 8

struct stat
{
  unsigned long long st_dev;
  unsigned char __pad0[4];
  unsigned long __st_ino;
  unsigned int st_mode;
  unsigned int st_nlink;
  unsigned long st_uid;
  unsigned long st_gid;
  unsigned long long st_rdev;
  unsigned char __pad3[4];
  long long st_size;
  unsigned long st_blksize;
  unsigned long long st_blocks;
  unsigned long st_atime;
  unsigned long st_atime_nsec;
  unsigned long st_mtime;
  unsigned int st_mtime_nsec;
  unsigned long st_ctime;
  unsigned long st_ctime_nsec;
  unsigned long long st_ino;
};

#endif // __SIZEOF_LONG_LONG__ == 8

#endif // __MES_LINUX_X86_KERNEL_STAT_H

https://git.savannah.gnu.org/cgit/mes.git/tree/mes/module/mes/psyntax.pp?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/mes/module/mes/psyntax.pp used in:
;;; -*-scheme-*-
;;; GNU Mes --- Maxwell Equations of Software
;;; Copyright (C) 2016, 2017, 2018 Free Software Foundation, Inc.
;;;
;;; This file is part of GNU Mes.
;;;
;;; GNU Mes is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;;; This file is generated from psyntax.ss.

;;; Code:
(letrec ((syntmp-lambda-var-list-167
           (lambda (syntmp-vars-552)
             (let syntmp-lvl-553 ((syntmp-vars-554 syntmp-vars-552)
                                  (syntmp-ls-555 (quote ()))
                                  (syntmp-w-556 (quote (()))))
               (cond ((pair? syntmp-vars-554)
                      (syntmp-lvl-553
                        (cdr syntmp-vars-554)
                        (cons (syntmp-wrap-146
                                (car syntmp-vars-554)
                                syntmp-w-556)
                              syntmp-ls-555)
                        syntmp-w-556))
                     ((syntmp-id?-118 syntmp-vars-554)
                      (cons (syntmp-wrap-146 syntmp-vars-554 syntmp-w-556)
                            syntmp-ls-555))
                     ((null? syntmp-vars-554) syntmp-ls-555)
                     ((syntmp-syntax-object?-104 syntmp-vars-554)
                      (syntmp-lvl-553
                        (syntmp-syntax-object-expression-105
                          syntmp-vars-554)
                        syntmp-ls-555
                        (syntmp-join-wraps-137
                          syntmp-w-556
                          (syntmp-syntax-object-wrap-106 syntmp-vars-554))))
                     ((syntmp-annotation?-92 syntmp-vars-554)
                      (syntmp-lvl-553
                        (annotation-expression syntmp-vars-554)
                        syntmp-ls-555
                        syntmp-w-556))
                     (else (cons syntmp-vars-554 syntmp-ls-555))))))
         (syntmp-gen-var-166
           (lambda (syntmp-id-557)
             (let ((syntmp-id-558
                     (if (syntmp-syntax-object?-104 syntmp-id-557)
                       (syntmp-syntax-object-expression-105
                         syntmp-id-557)
                       syntmp-id-557)))
               (if (syntmp-annotation?-92 syntmp-id-558)
                 (gensym
                   (symbol->string
                     (annotation-expression syntmp-id-558)))
                 (gensym (symbol->string syntmp-id-558))))))
         (syntmp-strip-165
           (lambda (syntmp-x-559 syntmp-w-560)
             (if (memq 'top
                       (syntmp-wrap-marks-121 syntmp-w-560))
               (if (or (syntmp-annotation?-92 syntmp-x-559)
                       (and (pair? syntmp-x-559)
                            (syntmp-annotation?-92 (car syntmp-x-559))))
                 (syntmp-strip-annotation-164 syntmp-x-559 #f)
                 syntmp-x-559)
               (let syntmp-f-561 ((syntmp-x-562 syntmp-x-559))
                 (cond ((syntmp-syntax-object?-104 syntmp-x-562)
                        (syntmp-strip-165
                          (syntmp-syntax-object-expression-105
                            syntmp-x-562)
                          (syntmp-syntax-object-wrap-106 syntmp-x-562)))
                       ((pair? syntmp-x-562)
                        (let ((syntmp-a-563 (syntmp-f-561 (car syntmp-x-562)))
                              (syntmp-d-564 (syntmp-f-561 (cdr syntmp-x-562))))
                          (if (and (eq? syntmp-a-563 (car syntmp-x-562))
                                   (eq? syntmp-d-564 (cdr syntmp-x-562)))
                            syntmp-x-562
                            (cons syntmp-a-563 syntmp-d-564))))
                       ((vector? syntmp-x-562)
                        (let ((syntmp-old-565 (vector->list syntmp-x-562)))
                          (let ((syntmp-new-566
                                  (map syntmp-f-561 syntmp-old-565)))
                            (if (andmap eq? syntmp-old-565 syntmp-new-566)
                              syntmp-x-562
                              (list->vector syntmp-new-566)))))
                       (else syntmp-x-562))))))
         (syntmp-strip-annotation-164
           (lambda (syntmp-x-567 syntmp-parent-568)
             (cond ((pair? syntmp-x-567)
                    (let ((syntmp-new-569 (cons #f #f)))
                      (begin
                        (when syntmp-parent-568
                              (set-annotation-stripped!
                                syntmp-parent-568
                                syntmp-new-569))
                        (set-car!
                          syntmp-new-569
                          (syntmp-strip-annotation-164
                            (car syntmp-x-567)
                            #f))
                        (set-cdr!
                          syntmp-new-569
                          (syntmp-strip-annotation-164
                            (cdr syntmp-x-567)
                            #f))
                        syntmp-new-569)))
                   ((syntmp-annotation?-92 syntmp-x-567)
                    (or (annotation-stripped syntmp-x-567)
                        (syntmp-strip-annotation-164
                          (annotation-expression syntmp-x-567)
                          syntmp-x-567)))
                   ((vector? syntmp-x-567)
                    (let ((syntmp-new-570
                            (make-vector (vector-length syntmp-x-567))))
                      (begin
                        (when syntmp-parent-568
                              (set-annotation-stripped!
                                syntmp-parent-568
                                syntmp-new-570))
                        (let syntmp-loop-571 ((syntmp-i-572
                                                (- (vector-length syntmp-x-567)
                                                   1)))
                          (unless
                            (syntmp-fx<-91 syntmp-i-572 0)
                            (vector-set!
                              syntmp-new-570
                              syntmp-i-572
                              (syntmp-strip-annotation-164
                                (vector-ref syntmp-x-567 syntmp-i-572)
                                #f))
                            (syntmp-loop-571 (syntmp-fx--89 syntmp-i-572 1))))
                        syntmp-new-570)))
                   (else syntmp-x-567))))
         (syntmp-ellipsis?-163
           (lambda (syntmp-e-573 syntmp-r-574)
             (and (syntmp-nonsymbol-id?-117 syntmp-e-573)
                  (let ((syntmp-id-575
                          (syntmp-make-syntax-object-103
                            '$sc-ellipsis
                            (syntmp-syntax-object-wrap-106 syntmp-e-573))))
                    (let ((syntmp-n-576
                            (syntmp-id-var-name-140
                              syntmp-id-575
                              '(()))))
                      (let ((syntmp-b-577
                              (syntmp-lookup-115 syntmp-n-576 syntmp-r-574)))
                        (if (eq? (syntmp-binding-type-110 syntmp-b-577)
                                 'ellipsis)
                          (syntmp-bound-id=?-142
                            syntmp-e-573
                            (syntmp-binding-value-111 syntmp-b-577))
                          (syntmp-free-id=?-141
                            syntmp-e-573
                            '#(syntax-object
                               ...
                               ((top)
                                #(ribcage () () ())
                                #(ribcage () () ())
                                #(ribcage #(b) #((top)) #("i"))
                                #(ribcage () () ())
                                #(ribcage #(n) #((top)) #("i"))
                                #(ribcage () () ())
                                #(ribcage #(id) #((top)) #("i"))
                                #(ribcage () () ())
                                #(ribcage #(e r) #((top) (top)) #("i" "i"))
                                #(ribcage
                                  (lambda-var-list
                                    gen-var
                                    strip
                                    strip-annotation
                                    ellipsis?
                                    chi-void
                                    eval-local-transformer
                                    chi-local-syntax
                                    chi-lambda-clause
                                    chi-body
                                    chi-macro
                                    chi-application
                                    chi-expr
                                    chi
                                    chi-top
                                    syntax-type
                                    chi-when-list
                                    chi-install-global
                                    chi-top-sequence
                                    chi-sequence
                                    source-wrap
                                    wrap
                                    bound-id-member?
                                    distinct-bound-ids?
                                    valid-bound-ids?
                                    bound-id=?
                                    free-id=?
                                    id-var-name
                                    same-marks?
                                    join-marks
                                    join-wraps
                                    smart-append
                                    make-binding-wrap
                                    extend-ribcage!
                                    make-empty-ribcage
                                    new-mark
                                    anti-mark
                                    the-anti-mark
                                    top-marked?
                                    top-wrap
                                    empty-wrap
                                    set-ribcage-labels!
                                    set-ribcage-marks!
                                    set-ribcage-symnames!
                                    ribcage-labels
                                    ribcage-marks
                                    ribcage-symnames
                                    ribcage?
                                    make-ribcage
                                    gen-labels
                                    gen-label
                                    make-rename
                                    rename-marks
                                    rename-new
                                    rename-old
                                    subst-rename?
                                    wrap-subst
                                    wrap-marks
                                    make-wrap
                                    id-sym-name&marks
                                    id-sym-name
                                    id?
                                    nonsymbol-id?
                                    global-extend
                                    lookup
                                    macros-only-env
                                    extend-var-env
                                    extend-env
                                    null-env
                                    binding-value
                                    binding-type
                                    make-binding
                                    arg-check
                                    source-annotation
                                    no-source
                                    unannotate
                                    set-syntax-object-wrap!
                                    set-syntax-object-expression!
                                    syntax-object-wrap
                                    syntax-object-expression
                                    syntax-object?
                                    make-syntax-object
                                    build-lexical-var
                                    build-letrec
                                    build-named-let
                                    build-let
                                    build-sequence
                                    build-data
                                    build-primref
                                    build-lambda
                                    build-global-definition
                                    build-global-assignment
                                    build-global-reference
                                    build-lexical-assignment
                                    build-lexical-reference
                                    build-conditional
                                    build-application
                                    get-global-definition-hook
                                    put-global-definition-hook
                                    gensym-hook
                                    error-hook
                                    local-eval-hook
                                    top-level-eval-hook
                                    annotation?
                                    fx<
                                    fx=
                                    fx-
                                    fx+
                                    noexpand)
                                  ((top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top)
                                   (top))
                                  ("i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"
                                   "i"))
                                #(ribcage
                                  (define-structure)
                                  ((top))
                                  ("i"))))))))))))
         (syntmp-chi-void-162
           (lambda () (list (quote void))))
         (syntmp-eval-local-transformer-161
           (lambda (syntmp-expanded-578)
             (let ((syntmp-p-579
                     (syntmp-local-eval-hook-94 syntmp-expanded-578)))
               (if (procedure? syntmp-p-579)
                 syntmp-p-579
                 (syntax-error
                   syntmp-p-579
                   "nonprocedure transformer")))))
         (syntmp-chi-local-syntax-160
           (lambda (syntmp-rec?-580
                    syntmp-e-581
                    syntmp-r-582
                    syntmp-w-583
                    syntmp-s-584
                    syntmp-k-585)
             ((lambda (syntmp-tmp-586)
                ((lambda (syntmp-tmp-587)
                   (if syntmp-tmp-587
                     (apply (lambda (syntmp-_-588
                                     syntmp-id-589
                                     syntmp-val-590
                                     syntmp-e1-591
                                     syntmp-e2-592)
                              (let ((syntmp-ids-593 syntmp-id-589))
                                (if (not (syntmp-valid-bound-ids?-143
                                           syntmp-ids-593))
                                  (syntax-error
                                    syntmp-e-581
                                    "duplicate bound keyword in")
                                  (let ((syntmp-labels-595
                                          (syntmp-gen-labels-124
                                            syntmp-ids-593)))
                                    (let ((syntmp-new-w-596
                                            (syntmp-make-binding-wrap-135
                                              syntmp-ids-593
                                              syntmp-labels-595
                                              syntmp-w-583)))
                                      (syntmp-k-585
                                        (cons syntmp-e1-591 syntmp-e2-592)
                                        (syntmp-extend-env-112
                                          syntmp-labels-595
                                          (let ((syntmp-w-598
                                                  (if syntmp-rec?-580
                                                    syntmp-new-w-596
                                                    syntmp-w-583))
                                                (syntmp-trans-r-599
                                                  (syntmp-macros-only-env-114
                                                    syntmp-r-582)))
                                            (map (lambda (syntmp-x-600)
                                                   (cons 'macro
                                                         (syntmp-eval-local-transformer-161
                                                           (syntmp-chi-154
                                                             syntmp-x-600
                                                             syntmp-trans-r-599
                                                             syntmp-w-598))))
                                                 syntmp-val-590))
                                          syntmp-r-582)
                                        syntmp-new-w-596
                                        syntmp-s-584))))))
                            syntmp-tmp-587)
                     ((lambda (syntmp-_-602)
                        (syntax-error
                          (syntmp-source-wrap-147
                            syntmp-e-581
                            syntmp-w-583
                            syntmp-s-584)))
                      syntmp-tmp-586)))
                 (syntax-dispatch
                   syntmp-tmp-586
                   '(any #(each (any any)) any . each-any))))
              syntmp-e-581)))
         (syntmp-chi-lambda-clause-159
           (lambda (syntmp-e-603
                    syntmp-c-604
                    syntmp-r-605
                    syntmp-w-606
                    syntmp-k-607)
             ((lambda (syntmp-tmp-608)
                ((lambda (syntmp-tmp-609)
                   (if syntmp-tmp-609
                     (apply (lambda (syntmp-id-610 syntmp-e1-611 syntmp-e2-612)
                              (let ((syntmp-ids-613 syntmp-id-610))
                                (if (not (syntmp-valid-bound-ids?-143
                                           syntmp-ids-613))
                                  (syntax-error
                                    syntmp-e-603
                                    "invalid parameter list in")
                                  (let ((syntmp-labels-615
                                          (syntmp-gen-labels-124
                                            syntmp-ids-613))
                                        (syntmp-new-vars-616
                                          (map syntmp-gen-var-166
                                               syntmp-ids-613)))
                                    (syntmp-k-607
                                      syntmp-new-vars-616
                                      (syntmp-chi-body-158
                                        (cons syntmp-e1-611 syntmp-e2-612)
                                        syntmp-e-603
                                        (syntmp-extend-var-env-113
                                          syntmp-labels-615
                                          syntmp-new-vars-616
                                          syntmp-r-605)
                                        (syntmp-make-binding-wrap-135
                                          syntmp-ids-613
                                          syntmp-labels-615
                                          syntmp-w-606)))))))
                            syntmp-tmp-609)
                     ((lambda (syntmp-tmp-618)
                        (if syntmp-tmp-618
                          (apply (lambda (syntmp-ids-619
                                          syntmp-e1-620
                                          syntmp-e2-621)
                                   (let ((syntmp-old-ids-622
                                           (syntmp-lambda-var-list-167
                                             syntmp-ids-619)))
                                     (if (not (syntmp-valid-bound-ids?-143
                                                syntmp-old-ids-622))
                                       (syntax-error
                                         syntmp-e-603
                                         "invalid parameter list in")
                                       (let ((syntmp-labels-623
                                               (syntmp-gen-labels-124
                                                 syntmp-old-ids-622))
                                             (syntmp-new-vars-624
                                               (map syntmp-gen-var-166
                                                    syntmp-old-ids-622)))
                                         (syntmp-k-607
                                           (let syntmp-f-625 ((syntmp-ls1-626
                                                                (cdr syntmp-new-vars-624))
                                                              (syntmp-ls2-627
                                                                (car syntmp-new-vars-624)))
                                             (if (null? syntmp-ls1-626)
                                               syntmp-ls2-627
                                               (syntmp-f-625
                                                 (cdr syntmp-ls1-626)
                                                 (cons (car syntmp-ls1-626)
                                                       syntmp-ls2-627))))
                                           (syntmp-chi-body-158
                                             (cons syntmp-e1-620 syntmp-e2-621)
                                             syntmp-e-603
                                             (syntmp-extend-var-env-113
                                               syntmp-labels-623
                                               syntmp-new-vars-624
                                               syntmp-r-605)
                                             (syntmp-make-binding-wrap-135
                                               syntmp-old-ids-622
                                               syntmp-labels-623
                                               syntmp-w-606)))))))
                                 syntmp-tmp-618)
                          ((lambda (syntmp-_-629)
                             (syntax-error syntmp-e-603))
                           syntmp-tmp-608)))
                      (syntax-dispatch
                        syntmp-tmp-608
                        '(any any . each-any)))))
                 (syntax-dispatch
                   syntmp-tmp-608
                   '(each-any any . each-any))))
              syntmp-c-604)))
         (syntmp-chi-body-158
           (lambda (syntmp-body-630
                    syntmp-outer-form-631
                    syntmp-r-632
                    syntmp-w-633)
             (let ((syntmp-r-634
                     (cons '("placeholder" placeholder)
                           syntmp-r-632)))
               (let ((syntmp-ribcage-635
                       (syntmp-make-ribcage-125
                         '()
                         '()
                         '())))
                 (let ((syntmp-w-636
                         (syntmp-make-wrap-120
                           (syntmp-wrap-marks-121 syntmp-w-633)
                           (cons syntmp-ribcage-635
                                 (syntmp-wrap-subst-122 syntmp-w-633)))))
                   (let syntmp-parse-637 ((syntmp-body-638
                                            (map (lambda (syntmp-x-644)
                                                   (cons syntmp-r-634
                                                         (syntmp-wrap-146
                                                           syntmp-x-644
                                                           syntmp-w-636)))
                                                 syntmp-body-630))
                                          (syntmp-ids-639 (quote ()))
                                          (syntmp-labels-640 (quote ()))
                                          (syntmp-vars-641 (quote ()))
                                          (syntmp-vals-642 (quote ()))
                                          (syntmp-bindings-643 (quote ())))
                     (if (null? syntmp-body-638)
                       (syntax-error
                         syntmp-outer-form-631
                         "no expressions in body")
                       (let ((syntmp-e-645 (cdar syntmp-body-638))
                             (syntmp-er-646 (caar syntmp-body-638)))
                         (call-with-values
                           (lambda ()
                             (syntmp-syntax-type-152
                               syntmp-e-645
                               syntmp-er-646
                               '(())
                               #f
                               syntmp-ribcage-635))
                           (lambda (syntmp-type-647
                                    syntmp-value-648
                                    syntmp-e-649
                                    syntmp-w-650
                                    syntmp-s-651)
                             (let ((syntmp-t-652 syntmp-type-647))
                               (if (memv syntmp-t-652 (quote (define-form)))
                                 (let ((syntmp-id-653
                                         (syntmp-wrap-146
                                           syntmp-value-648
                                           syntmp-w-650))
                                       (syntmp-label-654
                                         (syntmp-gen-label-123)))
                                   (let ((syntmp-var-655
                                           (syntmp-gen-var-166 syntmp-id-653)))
                                     (begin
                                       (syntmp-extend-ribcage!-134
                                         syntmp-ribcage-635
                                         syntmp-id-653
                                         syntmp-label-654)
                                       (syntmp-parse-637
                                         (cdr syntmp-body-638)
                                         (cons syntmp-id-653 syntmp-ids-639)
                                         (cons syntmp-label-654
                                               syntmp-labels-640)
                                         (cons syntmp-var-655 syntmp-vars-641)
                                         (cons (cons syntmp-er-646
                                                     (syntmp-wrap-146
                                                       syntmp-e-649
                                                       syntmp-w-650))
                                               syntmp-vals-642)
                                         (cons (cons 'lexical
                                                     syntmp-var-655)
                                               syntmp-bindings-643)))))
                                 (if (memv syntmp-t-652
                                           '(define-syntax-form))
                                   (let ((syntmp-id-656
                                           (syntmp-wrap-146
                                             syntmp-value-648
                                             syntmp-w-650))
                                         (syntmp-label-657
                                           (syntmp-gen-label-123)))
                                     (begin
                                       (syntmp-extend-ribcage!-134
                                         syntmp-ribcage-635
                                         syntmp-id-656
                                         syntmp-label-657)
                                       (syntmp-parse-637
                                         (cdr syntmp-body-638)
                                         (cons syntmp-id-656 syntmp-ids-639)
                                         (cons syntmp-label-657
                                               syntmp-labels-640)
                                         syntmp-vars-641
                                         syntmp-vals-642
                                         (cons (cons 'macro
                                                     (cons syntmp-er-646
                                                           (syntmp-wrap-146
                                                             syntmp-e-649
                                                             syntmp-w-650)))
                                               syntmp-bindings-643))))
                                   (if (memv syntmp-t-652 (quote (begin-form)))
                                     ((lambda (syntmp-tmp-658)
                                        ((lambda (syntmp-tmp-659)
                                           (if syntmp-tmp-659
                                             (apply (lambda (syntmp-_-660
                                                             syntmp-e1-661)
                                                      (syntmp-parse-637
                                                        (let syntmp-f-662 ((syntmp-forms-663
                                                                             syntmp-e1-661))
                                                          (if (null? syntmp-forms-663)
                                                            (cdr syntmp-body-638)
                                                            (cons (cons syntmp-er-646
                                                                        (syntmp-wrap-146
                                                                          (car syntmp-forms-663)
                                                                          syntmp-w-650))
                                                                  (syntmp-f-662
                                                                    (cdr syntmp-forms-663)))))
                                                        syntmp-ids-639
                                                        syntmp-labels-640
                                                        syntmp-vars-641
                                                        syntmp-vals-642
                                                        syntmp-bindings-643))
                                                    syntmp-tmp-659)
                                             (syntax-error syntmp-tmp-658)))
                                         (syntax-dispatch
                                           syntmp-tmp-658
                                           '(any . each-any))))
                                      syntmp-e-649)
                                     (if (memv syntmp-t-652
                                               '(local-syntax-form))
                                       (syntmp-chi-local-syntax-160
                                         syntmp-value-648
                                         syntmp-e-649
                                         syntmp-er-646
                                         syntmp-w-650
                                         syntmp-s-651
                                         (lambda (syntmp-forms-665
                                                  syntmp-er-666
                                                  syntmp-w-667
                                                  syntmp-s-668)
                                           (syntmp-parse-637
                                             (let syntmp-f-669 ((syntmp-forms-670
                                                                  syntmp-forms-665))
                                               (if (null? syntmp-forms-670)
                                                 (cdr syntmp-body-638)
                                                 (cons (cons syntmp-er-666
                                                             (syntmp-wrap-146
                                                               (car syntmp-forms-670)
                                                               syntmp-w-667))
                                                       (syntmp-f-669
                                                         (cdr syntmp-forms-670)))))
                                             syntmp-ids-639
                                             syntmp-labels-640
                                             syntmp-vars-641
                                             syntmp-vals-642
                                             syntmp-bindings-643)))
                                       (if (null? syntmp-ids-639)
                                         (syntmp-build-sequence-99
                                           #f
                                           (map (lambda (syntmp-x-671)
                                                  (syntmp-chi-154
                                                    (cdr syntmp-x-671)
                                                    (car syntmp-x-671)
                                                    '(())))
                                                (cons (cons syntmp-er-646
                                                            (syntmp-source-wrap-147
                                                              syntmp-e-649
                                                              syntmp-w-650
                                                              syntmp-s-651))
                                                      (cdr syntmp-body-638))))
                                         (begin
                                           (if (not (syntmp-valid-bound-ids?-143
                                                      syntmp-ids-639))
                                             (syntax-error
                                               syntmp-outer-form-631
                                               "invalid or duplicate identifier in definition"))
                                           (let syntmp-loop-672 ((syntmp-bs-673
                                                                   syntmp-bindings-643)
                                                                 (syntmp-er-cache-674
                                                                   #f)
                                                                 (syntmp-r-cache-675
                                                                   #f))
                                             (if (not (null? syntmp-bs-673))
                                               (let ((syntmp-b-676
                                                       (car syntmp-bs-673)))
                                                 (if (eq? (car syntmp-b-676)
                                                          'macro)
                                                   (let ((syntmp-er-677
                                                           (cadr syntmp-b-676)))
                                                     (let ((syntmp-r-cache-678
                                                             (if (eq? syntmp-er-677
                                                                      syntmp-er-cache-674)
                                                               syntmp-r-cache-675
                                                               (syntmp-macros-only-env-114
                                                                 syntmp-er-677))))
                                                       (begin
                                                         (set-cdr!
                                                           syntmp-b-676
                                                           (syntmp-eval-local-transformer-161
                                                             (syntmp-chi-154
                                                               (cddr syntmp-b-676)
                                                               syntmp-r-cache-678
                                                               '(()))))
                                                         (syntmp-loop-672
                                                           (cdr syntmp-bs-673)
                                                           syntmp-er-677
                                                           syntmp-r-cache-678))))
                                                   (syntmp-loop-672
                                                     (cdr syntmp-bs-673)
                                                     syntmp-er-cache-674
                                                     syntmp-r-cache-675)))))
                                           (set-cdr!
                                             syntmp-r-634
                                             (syntmp-extend-env-112
                                               syntmp-labels-640
                                               syntmp-bindings-643
                                               (cdr syntmp-r-634)))
                                           (syntmp-build-letrec-102
                                             #f
                                             syntmp-vars-641
                                             (map (lambda (syntmp-x-679)
                                                    (syntmp-chi-154
                                                      (cdr syntmp-x-679)
                                                      (car syntmp-x-679)
                                                      '(())))
                                                  syntmp-vals-642)
                                             (syntmp-build-sequence-99
                                               #f
                                               (map (lambda (syntmp-x-680)
                                                      (syntmp-chi-154
                                                        (cdr syntmp-x-680)
                                                        (car syntmp-x-680)
                                                        '(())))
                                                    (cons (cons syntmp-er-646
                                                                (syntmp-source-wrap-147
                                                                  syntmp-e-649
                                                                  syntmp-w-650
                                                                  syntmp-s-651))
                                                          (cdr syntmp-body-638))))))))))))))))))))))
         (syntmp-chi-macro-157
           (lambda (syntmp-p-681
                    syntmp-e-682
                    syntmp-r-683
                    syntmp-w-684
                    syntmp-rib-685)
             (letrec ((syntmp-rebuild-macro-output-686
                        (lambda (syntmp-x-687 syntmp-m-688)
                          (cond ((pair? syntmp-x-687)
                                 (cons (syntmp-rebuild-macro-output-686
                                         (car syntmp-x-687)
                                         syntmp-m-688)
                                       (syntmp-rebuild-macro-output-686
                                         (cdr syntmp-x-687)
                                         syntmp-m-688)))
                                ((syntmp-syntax-object?-104 syntmp-x-687)
                                 (let ((syntmp-w-689
                                         (syntmp-syntax-object-wrap-106
                                           syntmp-x-687)))
                                   (let ((syntmp-ms-690
                                           (syntmp-wrap-marks-121
                                             syntmp-w-689))
                                         (syntmp-s-691
                                           (syntmp-wrap-subst-122
                                             syntmp-w-689)))
                                     (syntmp-make-syntax-object-103
                                       (syntmp-syntax-object-expression-105
                                         syntmp-x-687)
                                       (if (and (pair? syntmp-ms-690)
                                                (eq? (car syntmp-ms-690) #f))
                                         (syntmp-make-wrap-120
                                           (cdr syntmp-ms-690)
                                           (if syntmp-rib-685
                                             (cons syntmp-rib-685
                                                   (cdr syntmp-s-691))
                                             (cdr syntmp-s-691)))
                                         (syntmp-make-wrap-120
                                           (cons syntmp-m-688 syntmp-ms-690)
                                           (if syntmp-rib-685
                                             (cons syntmp-rib-685
                                                   (cons 'shift
                                                         syntmp-s-691))
                                             (cons 'shift
                                                   syntmp-s-691))))))))
                                ((vector? syntmp-x-687)
                                 (let ((syntmp-n-692
                                         (vector-length syntmp-x-687)))
                                   (let ((syntmp-v-693
                                           (make-vector syntmp-n-692)))
                                     (let syntmp-doloop-694 ((syntmp-i-695 0))
                                       (if (syntmp-fx=-90
                                             syntmp-i-695
                                             syntmp-n-692)
                                         syntmp-v-693
                                         (begin
                                           (vector-set!
                                             syntmp-v-693
                                             syntmp-i-695
                                             (syntmp-rebuild-macro-output-686
                                               (vector-ref
                                                 syntmp-x-687
                                                 syntmp-i-695)
                                               syntmp-m-688))
                                           (syntmp-doloop-694
                                             (syntmp-fx+-88
                                               syntmp-i-695
                                               1))))))))
                                ((symbol? syntmp-x-687)
                                 (syntax-error
                                   syntmp-x-687
                                   "encountered raw symbol in macro output"))
                                (else syntmp-x-687)))))
               (syntmp-rebuild-macro-output-686
                 (syntmp-p-681
                   (syntmp-wrap-146
                     syntmp-e-682
                     (syntmp-anti-mark-133 syntmp-w-684)))
                 (string #\m)))))
         (syntmp-chi-application-156
           (lambda (syntmp-x-696
                    syntmp-e-697
                    syntmp-r-698
                    syntmp-w-699
                    syntmp-s-700)
             ((lambda (syntmp-tmp-701)
                ((lambda (syntmp-tmp-702)
                   (if syntmp-tmp-702
                     (apply (lambda (syntmp-e0-703 syntmp-e1-704)
                              (cons syntmp-x-696
                                    (map (lambda (syntmp-e-705)
                                           (syntmp-chi-154
                                             syntmp-e-705
                                             syntmp-r-698
                                             syntmp-w-699))
                                         syntmp-e1-704)))
                            syntmp-tmp-702)
                     (syntax-error syntmp-tmp-701)))
                 (syntax-dispatch
                   syntmp-tmp-701
                   '(any . each-any))))
              syntmp-e-697)))
         (syntmp-chi-expr-155
           (lambda (syntmp-type-707
                    syntmp-value-708
                    syntmp-e-709
                    syntmp-r-710
                    syntmp-w-711
                    syntmp-s-712)
             (let ((syntmp-t-713 syntmp-type-707))
               (if (memv syntmp-t-713 (quote (lexical)))
                 syntmp-value-708
                 (if (memv syntmp-t-713 (quote (core external-macro)))
                   (syntmp-value-708
                     syntmp-e-709
                     syntmp-r-710
                     syntmp-w-711
                     syntmp-s-712)
                   (if (memv syntmp-t-713 (quote (lexical-call)))
                     (syntmp-chi-application-156
                       syntmp-value-708
                       syntmp-e-709
                       syntmp-r-710
                       syntmp-w-711
                       syntmp-s-712)
                     (if (memv syntmp-t-713 (quote (global-call)))
                       (syntmp-chi-application-156
                         syntmp-value-708
                         syntmp-e-709
                         syntmp-r-710
                         syntmp-w-711
                         syntmp-s-712)
                       (if (memv syntmp-t-713 (quote (constant)))
                         (syntmp-build-data-98
                           syntmp-s-712
                           (syntmp-strip-165
                             (syntmp-source-wrap-147
                               syntmp-e-709
                               syntmp-w-711
                               syntmp-s-712)
                             '(())))
                         (if (memv syntmp-t-713 (quote (global)))
                           syntmp-value-708
                           (if (memv syntmp-t-713 (quote (call)))
                             (syntmp-chi-application-156
                               (syntmp-chi-154
                                 (car syntmp-e-709)
                                 syntmp-r-710
                                 syntmp-w-711)
                               syntmp-e-709
                               syntmp-r-710
                               syntmp-w-711
                               syntmp-s-712)
                             (if (memv syntmp-t-713 (quote (begin-form)))
                               ((lambda (syntmp-tmp-714)
                                  ((lambda (syntmp-tmp-715)
                                     (if syntmp-tmp-715
                                       (apply (lambda (syntmp-_-716
                                                       syntmp-e1-717
                                                       syntmp-e2-718)
                                                (syntmp-chi-sequence-148
                                                  (cons syntmp-e1-717
                                                        syntmp-e2-718)
                                                  syntmp-r-710
                                                  syntmp-w-711
                                                  syntmp-s-712))
                                              syntmp-tmp-715)
                                       (syntax-error syntmp-tmp-714)))
                                   (syntax-dispatch
                                     syntmp-tmp-714
                                     '(any any . each-any))))
                                syntmp-e-709)
                               (if (memv syntmp-t-713
                                         '(local-syntax-form))
                                 (syntmp-chi-local-syntax-160
                                   syntmp-value-708
                                   syntmp-e-709
                                   syntmp-r-710
                                   syntmp-w-711
                                   syntmp-s-712
                                   syntmp-chi-sequence-148)
                                 (if (memv syntmp-t-713
                                           '(eval-when-form))
                                   ((lambda (syntmp-tmp-720)
                                      ((lambda (syntmp-tmp-721)
                                         (if syntmp-tmp-721
                                           (apply (lambda (syntmp-_-722
                                                           syntmp-x-723
                                                           syntmp-e1-724
                                                           syntmp-e2-725)
                                                    (let ((syntmp-when-list-726
                                                            (syntmp-chi-when-list-151
                                                              syntmp-e-709
                                                              syntmp-x-723
                                                              syntmp-w-711)))
                                                      (if (memq 'eval
                                                                syntmp-when-list-726)
                                                        (syntmp-chi-sequence-148
                                                          (cons syntmp-e1-724
                                                                syntmp-e2-725)
                                                          syntmp-r-710
                                                          syntmp-w-711
                                                          syntmp-s-712)
                                                        (syntmp-chi-void-162))))
                                                  syntmp-tmp-721)
                                           (syntax-error syntmp-tmp-720)))
                                       (syntax-dispatch
                                         syntmp-tmp-720
                                         '(any each-any any . each-any))))
                                    syntmp-e-709)
                                   (if (memv syntmp-t-713
                                             '(define-form define-syntax-form))
                                     (syntax-error
                                       (syntmp-wrap-146
                                         syntmp-value-708
                                         syntmp-w-711)
                                       "invalid context for definition of")
                                     (if (memv syntmp-t-713 (quote (syntax)))
                                       (syntax-error
                                         (syntmp-source-wrap-147
                                           syntmp-e-709
                                           syntmp-w-711
                                           syntmp-s-712)
                                         "reference to pattern variable outside syntax form")
                                       (if (memv syntmp-t-713
                                                 '(displaced-lexical))
                                         (syntax-error
                                           (syntmp-source-wrap-147
                                             syntmp-e-709
                                             syntmp-w-711
                                             syntmp-s-712)
                                           "reference to identifier outside its scope")
                                         (syntax-error
                                           (syntmp-source-wrap-147
                                             syntmp-e-709
                                             syntmp-w-711
                                             syntmp-s-712))))))))))))))))))
         (syntmp-chi-154
           (lambda (syntmp-e-729 syntmp-r-730 syntmp-w-731)
             (call-with-values
               (lambda ()
                 (syntmp-syntax-type-152
                   syntmp-e-729
                   syntmp-r-730
                   syntmp-w-731
                   #f
                   #f))
               (lambda (syntmp-type-732
                        syntmp-value-733
                        syntmp-e-734
                        syntmp-w-735
                        syntmp-s-736)
                 (syntmp-chi-expr-155
                   syntmp-type-732
                   syntmp-value-733
                   syntmp-e-734
                   syntmp-r-730
                   syntmp-w-735
                   syntmp-s-736)))))
         (syntmp-chi-top-153
           (lambda (syntmp-e-737
                    syntmp-r-738
                    syntmp-w-739
                    syntmp-m-740
                    syntmp-esew-741)
             (call-with-values
               (lambda ()
                 (syntmp-syntax-type-152
                   syntmp-e-737
                   syntmp-r-738
                   syntmp-w-739
                   #f
                   #f))
               (lambda (syntmp-type-754
                        syntmp-value-755
                        syntmp-e-756
                        syntmp-w-757
                        syntmp-s-758)
                 (let ((syntmp-t-759 syntmp-type-754))
                   (if (memv syntmp-t-759 (quote (begin-form)))
                     ((lambda (syntmp-tmp-760)
                        ((lambda (syntmp-tmp-761)
                           (if syntmp-tmp-761
                             (apply (lambda (syntmp-_-762)
                                      (syntmp-chi-void-162))
                                    syntmp-tmp-761)
                             ((lambda (syntmp-tmp-763)
                                (if syntmp-tmp-763
                                  (apply (lambda (syntmp-_-764
                                                  syntmp-e1-765
                                                  syntmp-e2-766)
                                           (syntmp-chi-top-sequence-149
                                             (cons syntmp-e1-765 syntmp-e2-766)
                                             syntmp-r-738
                                             syntmp-w-757
                                             syntmp-s-758
                                             syntmp-m-740
                                             syntmp-esew-741))
                                         syntmp-tmp-763)
                                  (syntax-error syntmp-tmp-760)))
                              (syntax-dispatch
                                syntmp-tmp-760
                                '(any any . each-any)))))
                         (syntax-dispatch syntmp-tmp-760 (quote (any)))))
                      syntmp-e-756)
                     (if (memv syntmp-t-759 (quote (local-syntax-form)))
                       (syntmp-chi-local-syntax-160
                         syntmp-value-755
                         syntmp-e-756
                         syntmp-r-738
                         syntmp-w-757
                         syntmp-s-758
                         (lambda (syntmp-body-768
                                  syntmp-r-769
                                  syntmp-w-770
                                  syntmp-s-771)
                           (syntmp-chi-top-sequence-149
                             syntmp-body-768
                             syntmp-r-769
                             syntmp-w-770
                             syntmp-s-771
                             syntmp-m-740
                             syntmp-esew-741)))
                       (if (memv syntmp-t-759 (quote (eval-when-form)))
                         ((lambda (syntmp-tmp-772)
                            ((lambda (syntmp-tmp-773)
                               (if syntmp-tmp-773
                                 (apply (lambda (syntmp-_-774
                                                 syntmp-x-775
                                                 syntmp-e1-776
                                                 syntmp-e2-777)
                                          (let ((syntmp-when-list-778
                                                  (syntmp-chi-when-list-151
                                                    syntmp-e-756
                                                    syntmp-x-775
                                                    syntmp-w-757))
                                                (syntmp-body-779
                                                  (cons syntmp-e1-776
                                                        syntmp-e2-777)))
                                            (cond ((eq? syntmp-m-740 (quote e))
                                                   (if (memq 'eval
                                                             syntmp-when-list-778)
                                                     (syntmp-chi-top-sequence-149
                                                       syntmp-body-779
                                                       syntmp-r-738
                                                       syntmp-w-757
                                                       syntmp-s-758
                                                       'e
                                                       '(eval))
                                                     (syntmp-chi-void-162)))
                                                  ((memq 'load
                                                         syntmp-when-list-778)
                                                   (if (or (memq 'compile
                                                                 syntmp-when-list-778)
                                                           (and (eq? syntmp-m-740
                                                                     'c&e)
                                                                (memq 'eval
                                                                      syntmp-when-list-778)))
                                                     (syntmp-chi-top-sequence-149
                                                       syntmp-body-779
                                                       syntmp-r-738
                                                       syntmp-w-757
                                                       syntmp-s-758
                                                       'c&e
                                                       '(compile load))
                                                     (if (memq syntmp-m-740
                                                               '(c c&e))
                                                       (syntmp-chi-top-sequence-149
                                                         syntmp-body-779
                                                         syntmp-r-738
                                                         syntmp-w-757
                                                         syntmp-s-758
                                                         'c
                                                         '(load))
                                                       (syntmp-chi-void-162))))
                                                  ((or (memq 'compile
                                                             syntmp-when-list-778)
                                                       (and (eq? syntmp-m-740
                                                                 'c&e)
                                                            (memq 'eval
                                                                  syntmp-when-list-778)))
                                                   (syntmp-top-level-eval-hook-93
                                                     (syntmp-chi-top-sequence-149
                                                       syntmp-body-779
                                                       syntmp-r-738
                                                       syntmp-w-757
                                                       syntmp-s-758
                                                       'e
                                                       '(eval)))
                                                   (syntmp-chi-void-162))
                                                  (else
                                                   (syntmp-chi-void-162)))))
                                        syntmp-tmp-773)
                                 (syntax-error syntmp-tmp-772)))
                             (syntax-dispatch
                               syntmp-tmp-772
                               '(any each-any any . each-any))))
                          syntmp-e-756)
                         (if (memv syntmp-t-759 (quote (define-syntax-form)))
                           (let ((syntmp-n-782
                                   (syntmp-id-var-name-140
                                     syntmp-value-755
                                     syntmp-w-757))
                                 (syntmp-r-783
                                   (syntmp-macros-only-env-114 syntmp-r-738)))
                             (let ((syntmp-t-784 syntmp-m-740))
                               (if (memv syntmp-t-784 (quote (c)))
                                 (if (memq (quote compile) syntmp-esew-741)
                                   (let ((syntmp-e-785
                                           (syntmp-chi-install-global-150
                                             syntmp-n-782
                                             (syntmp-chi-154
                                               syntmp-e-756
                                               syntmp-r-783
                                               syntmp-w-757))))
                                     (begin
                                       (syntmp-top-level-eval-hook-93
                                         syntmp-e-785)
                                       (if (memq (quote load) syntmp-esew-741)
                                         syntmp-e-785
                                         (syntmp-chi-void-162))))
                                   (if (memq (quote load) syntmp-esew-741)
                                     (syntmp-chi-install-global-150
                                       syntmp-n-782
                                       (syntmp-chi-154
                                         syntmp-e-756
                                         syntmp-r-783
                                         syntmp-w-757))
                                     (syntmp-chi-void-162)))
                                 (if (memv syntmp-t-784 (quote (c&e)))
                                   (let ((syntmp-e-786
                                           (syntmp-chi-install-global-150
                                             syntmp-n-782
                                             (syntmp-chi-154
                                               syntmp-e-756
                                               syntmp-r-783
                                               syntmp-w-757))))
                                     (begin
                                       (syntmp-top-level-eval-hook-93
                                         syntmp-e-786)
                                       syntmp-e-786))
                                   (begin
                                     (if (memq (quote eval) syntmp-esew-741)
                                       (syntmp-top-level-eval-hook-93
                                         (syntmp-chi-install-global-150
                                           syntmp-n-782
                                           (syntmp-chi-154
                                             syntmp-e-756
                                             syntmp-r-783
                                             syntmp-w-757))))
                                     (syntmp-chi-void-162))))))
                           (if (memv syntmp-t-759 (quote (define-form)))
                             (let ((syntmp-n-787
                                     (syntmp-id-var-name-140
                                       syntmp-value-755
                                       syntmp-w-757)))
                               (let ((syntmp-type-788
                                       (syntmp-binding-type-110
                                         (syntmp-lookup-115
                                           syntmp-n-787
                                           syntmp-r-738))))
                                 (let ((syntmp-t-789 syntmp-type-788))
                                   (if (memv syntmp-t-789 (quote (global)))
                                     (let ((syntmp-x-790
                                             (list 'define
                                                   syntmp-n-787
                                                   (syntmp-chi-154
                                                     syntmp-e-756
                                                     syntmp-r-738
                                                     syntmp-w-757))))
                                       (begin
                                         (if (eq? syntmp-m-740 (quote c&e))
                                           (syntmp-top-level-eval-hook-93
                                             syntmp-x-790))
                                         syntmp-x-790))
                                     (if (memv syntmp-t-789
                                               '(displaced-lexical))
                                       (syntax-error
                                         (syntmp-wrap-146
                                           syntmp-value-755
                                           syntmp-w-757)
                                         "identifier out of context")
                                       (if (eq? syntmp-type-788
                                                'external-macro)
                                         (let ((syntmp-x-791
                                                 (list 'define
                                                       syntmp-n-787
                                                       (syntmp-chi-154
                                                         syntmp-e-756
                                                         syntmp-r-738
                                                         syntmp-w-757))))
                                           (begin
                                             (if (eq? syntmp-m-740 (quote c&e))
                                               (syntmp-top-level-eval-hook-93
                                                 syntmp-x-791))
                                             syntmp-x-791))
                                         (syntax-error
                                           (syntmp-wrap-146
                                             syntmp-value-755
                                             syntmp-w-757)
                                           "cannot define keyword at top level")))))))
                             (let ((syntmp-x-792
                                     (syntmp-chi-expr-155
                                       syntmp-type-754
                                       syntmp-value-755
                                       syntmp-e-756
                                       syntmp-r-738
                                       syntmp-w-757
                                       syntmp-s-758)))
                               (begin
                                 (if (eq? syntmp-m-740 (quote c&e))
                                   (syntmp-top-level-eval-hook-93
                                     syntmp-x-792))
                                 syntmp-x-792))))))))))))
         (syntmp-syntax-type-152
           (lambda (syntmp-e-793
                    syntmp-r-794
                    syntmp-w-795
                    syntmp-s-796
                    syntmp-rib-797)
             (cond ((symbol? syntmp-e-793)
                    (let ((syntmp-n-798
                            (syntmp-id-var-name-140
                              syntmp-e-793
                              syntmp-w-795)))
                      (let ((syntmp-b-799
                              (syntmp-lookup-115 syntmp-n-798 syntmp-r-794)))
                        (let ((syntmp-type-800
                                (syntmp-binding-type-110 syntmp-b-799)))
                          (let ((syntmp-t-801 syntmp-type-800))
                            (if (memv syntmp-t-801 (quote (lexical)))
                              (values
                                syntmp-type-800
                                (syntmp-binding-value-111 syntmp-b-799)
                                syntmp-e-793
                                syntmp-w-795
                                syntmp-s-796)
                              (if (memv syntmp-t-801 (quote (global)))
                                (values
                                  syntmp-type-800
                                  syntmp-n-798
                                  syntmp-e-793
                                  syntmp-w-795
                                  syntmp-s-796)
                                (if (memv syntmp-t-801 (quote (macro)))
                                  (syntmp-syntax-type-152
                                    (syntmp-chi-macro-157
                                      (syntmp-binding-value-111 syntmp-b-799)
                                      syntmp-e-793
                                      syntmp-r-794
                                      syntmp-w-795
                                      syntmp-rib-797)
                                    syntmp-r-794
                                    '(())
                                    syntmp-s-796
                                    syntmp-rib-797)
                                  (values
                                    syntmp-type-800
                                    (syntmp-binding-value-111 syntmp-b-799)
                                    syntmp-e-793
                                    syntmp-w-795
                                    syntmp-s-796)))))))))
                   ((pair? syntmp-e-793)
                    (let ((syntmp-first-802 (car syntmp-e-793)))
                      (if (syntmp-id?-118 syntmp-first-802)
                        (let ((syntmp-n-803
                                (syntmp-id-var-name-140
                                  syntmp-first-802
                                  syntmp-w-795)))
                          (let ((syntmp-b-804
                                  (syntmp-lookup-115
                                    syntmp-n-803
                                    syntmp-r-794)))
                            (let ((syntmp-type-805
                                    (syntmp-binding-type-110 syntmp-b-804)))
                              (let ((syntmp-t-806 syntmp-type-805))
                                (if (memv syntmp-t-806 (quote (lexical)))
                                  (values
                                    'lexical-call
                                    (syntmp-binding-value-111 syntmp-b-804)
                                    syntmp-e-793
                                    syntmp-w-795
                                    syntmp-s-796)
                                  (if (memv syntmp-t-806 (quote (global)))
                                    (values
                                      'global-call
                                      syntmp-n-803
                                      syntmp-e-793
                                      syntmp-w-795
                                      syntmp-s-796)
                                    (if (memv syntmp-t-806 (quote (macro)))
                                      (syntmp-syntax-type-152
                                        (syntmp-chi-macro-157
                                          (syntmp-binding-value-111
                                            syntmp-b-804)
                                          syntmp-e-793
                                          syntmp-r-794
                                          syntmp-w-795
                                          syntmp-rib-797)
                                        syntmp-r-794
                                        '(())
                                        syntmp-s-796
                                        syntmp-rib-797)
                                      (if (memv syntmp-t-806
                                                '(core external-macro))
                                        (values
                                          syntmp-type-805
                                          (syntmp-binding-value-111
                                            syntmp-b-804)
                                          syntmp-e-793
                                          syntmp-w-795
                                          syntmp-s-796)
                                        (if (memv syntmp-t-806
                                                  '(local-syntax))
                                          (values
                                            'local-syntax-form
                                            (syntmp-binding-value-111
                                              syntmp-b-804)
                                            syntmp-e-793
                                            syntmp-w-795
                                            syntmp-s-796)
                                          (if (memv syntmp-t-806
                                                    '(begin))
                                            (values
                                              'begin-form
                                              #f
                                              syntmp-e-793
                                              syntmp-w-795
                                              syntmp-s-796)
                                            (if (memv syntmp-t-806
                                                      '(eval-when))
                                              (values
                                                'eval-when-form
                                                #f
                                                syntmp-e-793
                                                syntmp-w-795
                                                syntmp-s-796)
                                              (if (memv syntmp-t-806
                                                        '(define))
                                                ((lambda (syntmp-tmp-807)
                                                   ((lambda (syntmp-tmp-808)
                                                      (if (if syntmp-tmp-808
                                                            (apply (lambda (syntmp-_-809
                                                                            syntmp-name-810
                                                                            syntmp-val-811)
                                                                     (syntmp-id?-118
                                                                       syntmp-name-810))
                                                                   syntmp-tmp-808)
                                                            #f)
                                                        (apply (lambda (syntmp-_-812
                                                                        syntmp-name-813
                                                                        syntmp-val-814)
                                                                 (values
                                                                   'define-form
                                                                   syntmp-name-813
                                                                   syntmp-val-814
                                                                   syntmp-w-795
                                                                   syntmp-s-796))
                                                               syntmp-tmp-808)
                                                        ((lambda (syntmp-tmp-815)
                                                           (if (if syntmp-tmp-815
                                                                 (apply (lambda (syntmp-_-816
                                                                                 syntmp-name-817
                                                                                 syntmp-args-818
                                                                                 syntmp-e1-819
                                                                                 syntmp-e2-820)
                                                                          (and (syntmp-id?-118
                                                                                 syntmp-name-817)
                                                                               (syntmp-valid-bound-ids?-143
                                                                                 (syntmp-lambda-var-list-167
                                                                                   syntmp-args-818))))
                                                                        syntmp-tmp-815)
                                                                 #f)
                                                             (apply (lambda (syntmp-_-821
                                                                             syntmp-name-822
                                                                             syntmp-args-823
                                                                             syntmp-e1-824
                                                                             syntmp-e2-825)
                                                                      (values
                                                                        'define-form
                                                                        (syntmp-wrap-146
                                                                          syntmp-name-822
                                                                          syntmp-w-795)
                                                                        (cons '#(syntax-object
                                                                                 lambda
                                                                                 ((top)
                                                                                  #(ribcage
                                                                                    #(_
                                                                                      name
                                                                                      args
                                                                                      e1
                                                                                      e2)
                                                                                    #((top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top))
                                                                                    #("i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(t)
                                                                                    #(("m"
                                                                                       top))
                                                                                    #("i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(type)
                                                                                    #((top))
                                                                                    #("i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(b)
                                                                                    #((top))
                                                                                    #("i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(n)
                                                                                    #((top))
                                                                                    #("i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(first)
                                                                                    #((top))
                                                                                    #("i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(e
                                                                                      r
                                                                                      w
                                                                                      s
                                                                                      rib)
                                                                                    #((top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top))
                                                                                    #("i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"))
                                                                                  #(ribcage
                                                                                    (lambda-var-list
                                                                                      gen-var
                                                                                      strip
                                                                                      strip-annotation
                                                                                      ellipsis?
                                                                                      chi-void
                                                                                      eval-local-transformer
                                                                                      chi-local-syntax
                                                                                      chi-lambda-clause
                                                                                      chi-body
                                                                                      chi-macro
                                                                                      chi-application
                                                                                      chi-expr
                                                                                      chi
                                                                                      chi-top
                                                                                      syntax-type
                                                                                      chi-when-list
                                                                                      chi-install-global
                                                                                      chi-top-sequence
                                                                                      chi-sequence
                                                                                      source-wrap
                                                                                      wrap
                                                                                      bound-id-member?
                                                                                      distinct-bound-ids?
                                                                                      valid-bound-ids?
                                                                                      bound-id=?
                                                                                      free-id=?
                                                                                      id-var-name
                                                                                      same-marks?
                                                                                      join-marks
                                                                                      join-wraps
                                                                                      smart-append
                                                                                      make-binding-wrap
                                                                                      extend-ribcage!
                                                                                      make-empty-ribcage
                                                                                      new-mark
                                                                                      anti-mark
                                                                                      the-anti-mark
                                                                                      top-marked?
                                                                                      top-wrap
                                                                                      empty-wrap
                                                                                      set-ribcage-labels!
                                                                                      set-ribcage-marks!
                                                                                      set-ribcage-symnames!
                                                                                      ribcage-labels
                                                                                      ribcage-marks
                                                                                      ribcage-symnames
                                                                                      ribcage?
                                                                                      make-ribcage
                                                                                      gen-labels
                                                                                      gen-label
                                                                                      make-rename
                                                                                      rename-marks
                                                                                      rename-new
                                                                                      rename-old
                                                                                      subst-rename?
                                                                                      wrap-subst
                                                                                      wrap-marks
                                                                                      make-wrap
                                                                                      id-sym-name&marks
                                                                                      id-sym-name
                                                                                      id?
                                                                                      nonsymbol-id?
                                                                                      global-extend
                                                                                      lookup
                                                                                      macros-only-env
                                                                                      extend-var-env
                                                                                      extend-env
                                                                                      null-env
                                                                                      binding-value
                                                                                      binding-type
                                                                                      make-binding
                                                                                      arg-check
                                                                                      source-annotation
                                                                                      no-source
                                                                                      unannotate
                                                                                      set-syntax-object-wrap!
                                                                                      set-syntax-object-expression!
                                                                                      syntax-object-wrap
                                                                                      syntax-object-expression
                                                                                      syntax-object?
                                                                                      make-syntax-object
                                                                                      build-lexical-var
                                                                                      build-letrec
                                                                                      build-named-let
                                                                                      build-let
                                                                                      build-sequence
                                                                                      build-data
                                                                                      build-primref
                                                                                      build-lambda
                                                                                      build-global-definition
                                                                                      build-global-assignment
                                                                                      build-global-reference
                                                                                      build-lexical-assignment
                                                                                      build-lexical-reference
                                                                                      build-conditional
                                                                                      build-application
                                                                                      get-global-definition-hook
                                                                                      put-global-definition-hook
                                                                                      gensym-hook
                                                                                      error-hook
                                                                                      local-eval-hook
                                                                                      top-level-eval-hook
                                                                                      annotation?
                                                                                      fx<
                                                                                      fx=
                                                                                      fx-
                                                                                      fx+
                                                                                      noexpand)
                                                                                    ((top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top))
                                                                                    ("i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"))
                                                                                  #(ribcage
                                                                                    (define-structure)
                                                                                    ((top))
                                                                                    ("i"))))
                                                                              (syntmp-wrap-146
                                                                                (cons syntmp-args-823
                                                                                      (cons syntmp-e1-824
                                                                                            syntmp-e2-825))
                                                                                syntmp-w-795))
                                                                        '(())
                                                                        syntmp-s-796))
                                                                    syntmp-tmp-815)
                                                             ((lambda (syntmp-tmp-827)
                                                                (if (if syntmp-tmp-827
                                                                      (apply (lambda (syntmp-_-828
                                                                                      syntmp-name-829)
                                                                               (syntmp-id?-118
                                                                                 syntmp-name-829))
                                                                             syntmp-tmp-827)
                                                                      #f)
                                                                  (apply (lambda (syntmp-_-830
                                                                                  syntmp-name-831)
                                                                           (values
                                                                             'define-form
                                                                             (syntmp-wrap-146
                                                                               syntmp-name-831
                                                                               syntmp-w-795)
                                                                             '(#(syntax-object
                                                                                 void
                                                                                 ((top)
                                                                                  #(ribcage
                                                                                    #(_
                                                                                      name)
                                                                                    #((top)
                                                                                      (top))
                                                                                    #("i"
                                                                                      "i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(t)
                                                                                    #(("m"
                                                                                       top))
                                                                                    #("i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(type)
                                                                                    #((top))
                                                                                    #("i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(b)
                                                                                    #((top))
                                                                                    #("i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(n)
                                                                                    #((top))
                                                                                    #("i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(first)
                                                                                    #((top))
                                                                                    #("i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(e
                                                                                      r
                                                                                      w
                                                                                      s
                                                                                      rib)
                                                                                    #((top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top))
                                                                                    #("i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"))
                                                                                  #(ribcage
                                                                                    (lambda-var-list
                                                                                      gen-var
                                                                                      strip
                                                                                      strip-annotation
                                                                                      ellipsis?
                                                                                      chi-void
                                                                                      eval-local-transformer
                                                                                      chi-local-syntax
                                                                                      chi-lambda-clause
                                                                                      chi-body
                                                                                      chi-macro
                                                                                      chi-application
                                                                                      chi-expr
                                                                                      chi
                                                                                      chi-top
                                                                                      syntax-type
                                                                                      chi-when-list
                                                                                      chi-install-global
                                                                                      chi-top-sequence
                                                                                      chi-sequence
                                                                                      source-wrap
                                                                                      wrap
                                                                                      bound-id-member?
                                                                                      distinct-bound-ids?
                                                                                      valid-bound-ids?
                                                                                      bound-id=?
                                                                                      free-id=?
                                                                                      id-var-name
                                                                                      same-marks?
                                                                                      join-marks
                                                                                      join-wraps
                                                                                      smart-append
                                                                                      make-binding-wrap
                                                                                      extend-ribcage!
                                                                                      make-empty-ribcage
                                                                                      new-mark
                                                                                      anti-mark
                                                                                      the-anti-mark
                                                                                      top-marked?
                                                                                      top-wrap
                                                                                      empty-wrap
                                                                                      set-ribcage-labels!
                                                                                      set-ribcage-marks!
                                                                                      set-ribcage-symnames!
                                                                                      ribcage-labels
                                                                                      ribcage-marks
                                                                                      ribcage-symnames
                                                                                      ribcage?
                                                                                      make-ribcage
                                                                                      gen-labels
                                                                                      gen-label
                                                                                      make-rename
                                                                                      rename-marks
                                                                                      rename-new
                                                                                      rename-old
                                                                                      subst-rename?
                                                                                      wrap-subst
                                                                                      wrap-marks
                                                                                      make-wrap
                                                                                      id-sym-name&marks
                                                                                      id-sym-name
                                                                                      id?
                                                                                      nonsymbol-id?
                                                                                      global-extend
                                                                                      lookup
                                                                                      macros-only-env
                                                                                      extend-var-env
                                                                                      extend-env
                                                                                      null-env
                                                                                      binding-value
                                                                                      binding-type
                                                                                      make-binding
                                                                                      arg-check
                                                                                      source-annotation
                                                                                      no-source
                                                                                      unannotate
                                                                                      set-syntax-object-wrap!
                                                                                      set-syntax-object-expression!
                                                                                      syntax-object-wrap
                                                                                      syntax-object-expression
                                                                                      syntax-object?
                                                                                      make-syntax-object
                                                                                      build-lexical-var
                                                                                      build-letrec
                                                                                      build-named-let
                                                                                      build-let
                                                                                      build-sequence
                                                                                      build-data
                                                                                      build-primref
                                                                                      build-lambda
                                                                                      build-global-definition
                                                                                      build-global-assignment
                                                                                      build-global-reference
                                                                                      build-lexical-assignment
                                                                                      build-lexical-reference
                                                                                      build-conditional
                                                                                      build-application
                                                                                      get-global-definition-hook
                                                                                      put-global-definition-hook
                                                                                      gensym-hook
                                                                                      error-hook
                                                                                      local-eval-hook
                                                                                      top-level-eval-hook
                                                                                      annotation?
                                                                                      fx<
                                                                                      fx=
                                                                                      fx-
                                                                                      fx+
                                                                                      noexpand)
                                                                                    ((top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top)
                                                                                     (top))
                                                                                    ("i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"
                                                                                     "i"))
                                                                                  #(ribcage
                                                                                    (define-structure)
                                                                                    ((top))
                                                                                    ("i")))))
                                                                             '(())
                                                                             syntmp-s-796))
                                                                         syntmp-tmp-827)
                                                                  (syntax-error
                                                                    syntmp-tmp-807)))
                                                              (syntax-dispatch
                                                                syntmp-tmp-807
                                                                '(any any)))))
                                                         (syntax-dispatch
                                                           syntmp-tmp-807
                                                           '(any (any . any)
                                                                 any
                                                                 .
                                                                 each-any)))))
                                                    (syntax-dispatch
                                                      syntmp-tmp-807
                                                      '(any any any))))
                                                 syntmp-e-793)
                                                (if (memv syntmp-t-806
                                                          '(define-syntax))
                                                  ((lambda (syntmp-tmp-832)
                                                     ((lambda (syntmp-tmp-833)
                                                        (if (if syntmp-tmp-833
                                                              (apply (lambda (syntmp-_-834
                                                                              syntmp-name-835
                                                                              syntmp-val-836)
                                                                       (syntmp-id?-118
                                                                         syntmp-name-835))
                                                                     syntmp-tmp-833)
                                                              #f)
                                                          (apply (lambda (syntmp-_-837
                                                                          syntmp-name-838
                                                                          syntmp-val-839)
                                                                   (values
                                                                     'define-syntax-form
                                                                     syntmp-name-838
                                                                     syntmp-val-839
                                                                     syntmp-w-795
                                                                     syntmp-s-796))
                                                                 syntmp-tmp-833)
                                                          (syntax-error
                                                            syntmp-tmp-832)))
                                                      (syntax-dispatch
                                                        syntmp-tmp-832
                                                        '(any any any))))
                                                   syntmp-e-793)
                                                  (if (memv syntmp-t-806
                                                            '(ellipsis))
                                                    (values
                                                      'ellipsis
                                                      (syntmp-make-syntax-object-103
                                                        (syntmp-syntax-object-expression-105
                                                          value)
                                                        (syntmp-anti-mark-133
                                                          (syntmp-syntax-object-wrap-106
                                                            value))))
                                                    (values
                                                      'call
                                                      #f
                                                      syntmp-e-793
                                                      syntmp-w-795
                                                      syntmp-s-796)))))))))))))))
                        (values
                          'call
                          #f
                          syntmp-e-793
                          syntmp-w-795
                          syntmp-s-796))))
                   ((syntmp-syntax-object?-104 syntmp-e-793)
                    (syntmp-syntax-type-152
                      (syntmp-syntax-object-expression-105
                        syntmp-e-793)
                      syntmp-r-794
                      (syntmp-join-wraps-137
                        syntmp-w-795
                        (syntmp-syntax-object-wrap-106 syntmp-e-793))
                      #f
                      syntmp-rib-797))
                   ((syntmp-annotation?-92 syntmp-e-793)
                    (syntmp-syntax-type-152
                      (annotation-expression syntmp-e-793)
                      syntmp-r-794
                      syntmp-w-795
                      (annotation-source syntmp-e-793)
                      syntmp-rib-797))
                   ((self-evaluating? syntmp-e-793)
                    (values
                      'constant
                      #f
                      syntmp-e-793
                      syntmp-w-795
                      syntmp-s-796))
                   (else
                    (values
                      'other
                      #f
                      syntmp-e-793
                      syntmp-w-795
                      syntmp-s-796)))))
         (syntmp-chi-when-list-151
           (lambda (syntmp-e-840 syntmp-when-list-841 syntmp-w-842)
             (let syntmp-f-843 ((syntmp-when-list-844 syntmp-when-list-841)
                                (syntmp-situations-845 (quote ())))
               (if (null? syntmp-when-list-844)
                 syntmp-situations-845
                 (syntmp-f-843
                   (cdr syntmp-when-list-844)
                   (cons (let ((syntmp-x-846 (car syntmp-when-list-844)))
                           (cond ((syntmp-free-id=?-141
                                    syntmp-x-846
                                    '#(syntax-object
                                       compile
                                       ((top)
                                        #(ribcage () () ())
                                        #(ribcage #(x) #((top)) #("i"))
                                        #(ribcage () () ())
                                        #(ribcage
                                          #(f when-list situations)
                                          #((top) (top) (top))
                                          #("i" "i" "i"))
                                        #(ribcage () () ())
                                        #(ribcage
                                          #(e when-list w)
                                          #((top) (top) (top))
                                          #("i" "i" "i"))
                                        #(ribcage
                                          (lambda-var-list
                                            gen-var
                                            strip
                                            strip-annotation
                                            ellipsis?
                                            chi-void
                                            eval-local-transformer
                                            chi-local-syntax
                                            chi-lambda-clause
                                            chi-body
                                            chi-macro
                                            chi-application
                                            chi-expr
                                            chi
                                            chi-top
                                            syntax-type
                                            chi-when-list
                                            chi-install-global
                                            chi-top-sequence
                                            chi-sequence
                                            source-wrap
                                            wrap
                                            bound-id-member?
                                            distinct-bound-ids?
                                            valid-bound-ids?
                                            bound-id=?
                                            free-id=?
                                            id-var-name
                                            same-marks?
                                            join-marks
                                            join-wraps
                                            smart-append
                                            make-binding-wrap
                                            extend-ribcage!
                                            make-empty-ribcage
                                            new-mark
                                            anti-mark
                                            the-anti-mark
                                            top-marked?
                                            top-wrap
                                            empty-wrap
                                            set-ribcage-labels!
                                            set-ribcage-marks!
                                            set-ribcage-symnames!
                                            ribcage-labels
                                            ribcage-marks
                                            ribcage-symnames
                                            ribcage?
                                            make-ribcage
                                            gen-labels
                                            gen-label
                                            make-rename
                                            rename-marks
                                            rename-new
                                            rename-old
                                            subst-rename?
                                            wrap-subst
                                            wrap-marks
                                            make-wrap
                                            id-sym-name&marks
                                            id-sym-name
                                            id?
                                            nonsymbol-id?
                                            global-extend
                                            lookup
                                            macros-only-env
                                            extend-var-env
                                            extend-env
                                            null-env
                                            binding-value
                                            binding-type
                                            make-binding
                                            arg-check
                                            source-annotation
                                            no-source
                                            unannotate
                                            set-syntax-object-wrap!
                                            set-syntax-object-expression!
                                            syntax-object-wrap
                                            syntax-object-expression
                                            syntax-object?
                                            make-syntax-object
                                            build-lexical-var
                                            build-letrec
                                            build-named-let
                                            build-let
                                            build-sequence
                                            build-data
                                            build-primref
                                            build-lambda
                                            build-global-definition
                                            build-global-assignment
                                            build-global-reference
                                            build-lexical-assignment
                                            build-lexical-reference
                                            build-conditional
                                            build-application
                                            get-global-definition-hook
                                            put-global-definition-hook
                                            gensym-hook
                                            error-hook
                                            local-eval-hook
                                            top-level-eval-hook
                                            annotation?
                                            fx<
                                            fx=
                                            fx-
                                            fx+
                                            noexpand)
                                          ((top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top))
                                          ("i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"))
                                        #(ribcage
                                          (define-structure)
                                          ((top))
                                          ("i")))))
                                  'compile)
                                 ((syntmp-free-id=?-141
                                    syntmp-x-846
                                    '#(syntax-object
                                       load
                                       ((top)
                                        #(ribcage () () ())
                                        #(ribcage #(x) #((top)) #("i"))
                                        #(ribcage () () ())
                                        #(ribcage
                                          #(f when-list situations)
                                          #((top) (top) (top))
                                          #("i" "i" "i"))
                                        #(ribcage () () ())
                                        #(ribcage
                                          #(e when-list w)
                                          #((top) (top) (top))
                                          #("i" "i" "i"))
                                        #(ribcage
                                          (lambda-var-list
                                            gen-var
                                            strip
                                            strip-annotation
                                            ellipsis?
                                            chi-void
                                            eval-local-transformer
                                            chi-local-syntax
                                            chi-lambda-clause
                                            chi-body
                                            chi-macro
                                            chi-application
                                            chi-expr
                                            chi
                                            chi-top
                                            syntax-type
                                            chi-when-list
                                            chi-install-global
                                            chi-top-sequence
                                            chi-sequence
                                            source-wrap
                                            wrap
                                            bound-id-member?
                                            distinct-bound-ids?
                                            valid-bound-ids?
                                            bound-id=?
                                            free-id=?
                                            id-var-name
                                            same-marks?
                                            join-marks
                                            join-wraps
                                            smart-append
                                            make-binding-wrap
                                            extend-ribcage!
                                            make-empty-ribcage
                                            new-mark
                                            anti-mark
                                            the-anti-mark
                                            top-marked?
                                            top-wrap
                                            empty-wrap
                                            set-ribcage-labels!
                                            set-ribcage-marks!
                                            set-ribcage-symnames!
                                            ribcage-labels
                                            ribcage-marks
                                            ribcage-symnames
                                            ribcage?
                                            make-ribcage
                                            gen-labels
                                            gen-label
                                            make-rename
                                            rename-marks
                                            rename-new
                                            rename-old
                                            subst-rename?
                                            wrap-subst
                                            wrap-marks
                                            make-wrap
                                            id-sym-name&marks
                                            id-sym-name
                                            id?
                                            nonsymbol-id?
                                            global-extend
                                            lookup
                                            macros-only-env
                                            extend-var-env
                                            extend-env
                                            null-env
                                            binding-value
                                            binding-type
                                            make-binding
                                            arg-check
                                            source-annotation
                                            no-source
                                            unannotate
                                            set-syntax-object-wrap!
                                            set-syntax-object-expression!
                                            syntax-object-wrap
                                            syntax-object-expression
                                            syntax-object?
                                            make-syntax-object
                                            build-lexical-var
                                            build-letrec
                                            build-named-let
                                            build-let
                                            build-sequence
                                            build-data
                                            build-primref
                                            build-lambda
                                            build-global-definition
                                            build-global-assignment
                                            build-global-reference
                                            build-lexical-assignment
                                            build-lexical-reference
                                            build-conditional
                                            build-application
                                            get-global-definition-hook
                                            put-global-definition-hook
                                            gensym-hook
                                            error-hook
                                            local-eval-hook
                                            top-level-eval-hook
                                            annotation?
                                            fx<
                                            fx=
                                            fx-
                                            fx+
                                            noexpand)
                                          ((top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top))
                                          ("i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"))
                                        #(ribcage
                                          (define-structure)
                                          ((top))
                                          ("i")))))
                                  'load)
                                 ((syntmp-free-id=?-141
                                    syntmp-x-846
                                    '#(syntax-object
                                       eval
                                       ((top)
                                        #(ribcage () () ())
                                        #(ribcage #(x) #((top)) #("i"))
                                        #(ribcage () () ())
                                        #(ribcage
                                          #(f when-list situations)
                                          #((top) (top) (top))
                                          #("i" "i" "i"))
                                        #(ribcage () () ())
                                        #(ribcage
                                          #(e when-list w)
                                          #((top) (top) (top))
                                          #("i" "i" "i"))
                                        #(ribcage
                                          (lambda-var-list
                                            gen-var
                                            strip
                                            strip-annotation
                                            ellipsis?
                                            chi-void
                                            eval-local-transformer
                                            chi-local-syntax
                                            chi-lambda-clause
                                            chi-body
                                            chi-macro
                                            chi-application
                                            chi-expr
                                            chi
                                            chi-top
                                            syntax-type
                                            chi-when-list
                                            chi-install-global
                                            chi-top-sequence
                                            chi-sequence
                                            source-wrap
                                            wrap
                                            bound-id-member?
                                            distinct-bound-ids?
                                            valid-bound-ids?
                                            bound-id=?
                                            free-id=?
                                            id-var-name
                                            same-marks?
                                            join-marks
                                            join-wraps
                                            smart-append
                                            make-binding-wrap
                                            extend-ribcage!
                                            make-empty-ribcage
                                            new-mark
                                            anti-mark
                                            the-anti-mark
                                            top-marked?
                                            top-wrap
                                            empty-wrap
                                            set-ribcage-labels!
                                            set-ribcage-marks!
                                            set-ribcage-symnames!
                                            ribcage-labels
                                            ribcage-marks
                                            ribcage-symnames
                                            ribcage?
                                            make-ribcage
                                            gen-labels
                                            gen-label
                                            make-rename
                                            rename-marks
                                            rename-new
                                            rename-old
                                            subst-rename?
                                            wrap-subst
                                            wrap-marks
                                            make-wrap
                                            id-sym-name&marks
                                            id-sym-name
                                            id?
                                            nonsymbol-id?
                                            global-extend
                                            lookup
                                            macros-only-env
                                            extend-var-env
                                            extend-env
                                            null-env
                                            binding-value
                                            binding-type
                                            make-binding
                                            arg-check
                                            source-annotation
                                            no-source
                                            unannotate
                                            set-syntax-object-wrap!
                                            set-syntax-object-expression!
                                            syntax-object-wrap
                                            syntax-object-expression
                                            syntax-object?
                                            make-syntax-object
                                            build-lexical-var
                                            build-letrec
                                            build-named-let
                                            build-let
                                            build-sequence
                                            build-data
                                            build-primref
                                            build-lambda
                                            build-global-definition
                                            build-global-assignment
                                            build-global-reference
                                            build-lexical-assignment
                                            build-lexical-reference
                                            build-conditional
                                            build-application
                                            get-global-definition-hook
                                            put-global-definition-hook
                                            gensym-hook
                                            error-hook
                                            local-eval-hook
                                            top-level-eval-hook
                                            annotation?
                                            fx<
                                            fx=
                                            fx-
                                            fx+
                                            noexpand)
                                          ((top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top)
                                           (top))
                                          ("i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"
                                           "i"))
                                        #(ribcage
                                          (define-structure)
                                          ((top))
                                          ("i")))))
                                  'eval)
                                 (else
                                  (syntax-error
                                    (syntmp-wrap-146 syntmp-x-846 syntmp-w-842)
                                    "invalid eval-when situation"))))
                         syntmp-situations-845))))))
         (syntmp-chi-install-global-150
           (lambda (syntmp-name-847 syntmp-e-848)
             (list 'install-global-transformer
                   (syntmp-build-data-98 #f syntmp-name-847)
                   syntmp-e-848)))
         (syntmp-chi-top-sequence-149
           (lambda (syntmp-body-849
                    syntmp-r-850
                    syntmp-w-851
                    syntmp-s-852
                    syntmp-m-853
                    syntmp-esew-854)
             (syntmp-build-sequence-99
               syntmp-s-852
               (let syntmp-dobody-855 ((syntmp-body-856 syntmp-body-849)
                                       (syntmp-r-857 syntmp-r-850)
                                       (syntmp-w-858 syntmp-w-851)
                                       (syntmp-m-859 syntmp-m-853)
                                       (syntmp-esew-860 syntmp-esew-854))
                 (if (null? syntmp-body-856)
                   '()
                   (let ((syntmp-first-861
                           (syntmp-chi-top-153
                             (car syntmp-body-856)
                             syntmp-r-857
                             syntmp-w-858
                             syntmp-m-859
                             syntmp-esew-860)))
                     (cons syntmp-first-861
                           (syntmp-dobody-855
                             (cdr syntmp-body-856)
                             syntmp-r-857
                             syntmp-w-858
                             syntmp-m-859
                             syntmp-esew-860))))))))
         (syntmp-chi-sequence-148
           (lambda (syntmp-body-862
                    syntmp-r-863
                    syntmp-w-864
                    syntmp-s-865)
             (syntmp-build-sequence-99
               syntmp-s-865
               (let syntmp-dobody-866 ((syntmp-body-867 syntmp-body-862)
                                       (syntmp-r-868 syntmp-r-863)
                                       (syntmp-w-869 syntmp-w-864))
                 (if (null? syntmp-body-867)
                   '()
                   (let ((syntmp-first-870
                           (syntmp-chi-154
                             (car syntmp-body-867)
                             syntmp-r-868
                             syntmp-w-869)))
                     (cons syntmp-first-870
                           (syntmp-dobody-866
                             (cdr syntmp-body-867)
                             syntmp-r-868
                             syntmp-w-869))))))))
         (syntmp-source-wrap-147
           (lambda (syntmp-x-871 syntmp-w-872 syntmp-s-873)
             (syntmp-wrap-146
               (if syntmp-s-873
                 (make-annotation syntmp-x-871 syntmp-s-873 #f)
                 syntmp-x-871)
               syntmp-w-872)))
         (syntmp-wrap-146
           (lambda (syntmp-x-874 syntmp-w-875)
             (cond ((and (null? (syntmp-wrap-marks-121 syntmp-w-875))
                         (null? (syntmp-wrap-subst-122 syntmp-w-875)))
                    syntmp-x-874)
                   ((syntmp-syntax-object?-104 syntmp-x-874)
                    (syntmp-make-syntax-object-103
                      (syntmp-syntax-object-expression-105
                        syntmp-x-874)
                      (syntmp-join-wraps-137
                        syntmp-w-875
                        (syntmp-syntax-object-wrap-106 syntmp-x-874))))
                   ((null? syntmp-x-874) syntmp-x-874)
                   (else
                    (syntmp-make-syntax-object-103
                      syntmp-x-874
                      syntmp-w-875)))))
         (syntmp-bound-id-member?-145
           (lambda (syntmp-x-876 syntmp-list-877)
             (and (not (null? syntmp-list-877))
                  (or (syntmp-bound-id=?-142
                        syntmp-x-876
                        (car syntmp-list-877))
                      (syntmp-bound-id-member?-145
                        syntmp-x-876
                        (cdr syntmp-list-877))))))
         (syntmp-distinct-bound-ids?-144
           (lambda (syntmp-ids-878)
             (let syntmp-distinct?-879 ((syntmp-ids-880 syntmp-ids-878))
               (or (null? syntmp-ids-880)
                   (and (not (syntmp-bound-id-member?-145
                               (car syntmp-ids-880)
                               (cdr syntmp-ids-880)))
                        (syntmp-distinct?-879 (cdr syntmp-ids-880)))))))
         (syntmp-valid-bound-ids?-143
           (lambda (syntmp-ids-881)
             (and (let syntmp-all-ids?-882 ((syntmp-ids-883 syntmp-ids-881))
                    (or (null? syntmp-ids-883)
                        (and (syntmp-id?-118 (car syntmp-ids-883))
                             (syntmp-all-ids?-882 (cdr syntmp-ids-883)))))
                  (syntmp-distinct-bound-ids?-144 syntmp-ids-881))))
         (syntmp-bound-id=?-142
           (lambda (syntmp-i-884 syntmp-j-885)
             (if (and (syntmp-syntax-object?-104 syntmp-i-884)
                      (syntmp-syntax-object?-104 syntmp-j-885))
               (and (eq? (let ((syntmp-e-886
                                 (syntmp-syntax-object-expression-105
                                   syntmp-i-884)))
                           (if (syntmp-annotation?-92 syntmp-e-886)
                             (annotation-expression syntmp-e-886)
                             syntmp-e-886))
                         (let ((syntmp-e-887
                                 (syntmp-syntax-object-expression-105
                                   syntmp-j-885)))
                           (if (syntmp-annotation?-92 syntmp-e-887)
                             (annotation-expression syntmp-e-887)
                             syntmp-e-887)))
                    (syntmp-same-marks?-139
                      (syntmp-wrap-marks-121
                        (syntmp-syntax-object-wrap-106 syntmp-i-884))
                      (syntmp-wrap-marks-121
                        (syntmp-syntax-object-wrap-106 syntmp-j-885))))
               (eq? (let ((syntmp-e-888 syntmp-i-884))
                      (if (syntmp-annotation?-92 syntmp-e-888)
                        (annotation-expression syntmp-e-888)
                        syntmp-e-888))
                    (let ((syntmp-e-889 syntmp-j-885))
                      (if (syntmp-annotation?-92 syntmp-e-889)
                        (annotation-expression syntmp-e-889)
                        syntmp-e-889))))))
         (syntmp-free-id=?-141
           (lambda (syntmp-i-890 syntmp-j-891)
             (and (eq? (let ((syntmp-x-892 syntmp-i-890))
                         (let ((syntmp-e-893
                                 (if (syntmp-syntax-object?-104 syntmp-x-892)
                                   (syntmp-syntax-object-expression-105
                                     syntmp-x-892)
                                   syntmp-x-892)))
                           (if (syntmp-annotation?-92 syntmp-e-893)
                             (annotation-expression syntmp-e-893)
                             syntmp-e-893)))
                       (let ((syntmp-x-894 syntmp-j-891))
                         (let ((syntmp-e-895
                                 (if (syntmp-syntax-object?-104 syntmp-x-894)
                                   (syntmp-syntax-object-expression-105
                                     syntmp-x-894)
                                   syntmp-x-894)))
                           (if (syntmp-annotation?-92 syntmp-e-895)
                             (annotation-expression syntmp-e-895)
                             syntmp-e-895))))
                  (eq? (syntmp-id-var-name-140
                         syntmp-i-890
                         '(()))
                       (syntmp-id-var-name-140
                         syntmp-j-891
                         '(()))))))
         (syntmp-id-var-name-140
           (lambda (syntmp-id-896 syntmp-w-897)
             (letrec ((syntmp-search-vector-rib-900
                        (lambda (syntmp-sym-911
                                 syntmp-subst-912
                                 syntmp-marks-913
                                 syntmp-symnames-914
                                 syntmp-ribcage-915)
                          (let ((syntmp-n-916
                                  (vector-length syntmp-symnames-914)))
                            (let syntmp-f-917 ((syntmp-i-918 0))
                              (cond ((syntmp-fx=-90 syntmp-i-918 syntmp-n-916)
                                     (syntmp-search-898
                                       syntmp-sym-911
                                       (cdr syntmp-subst-912)
                                       syntmp-marks-913))
                                    ((and (eq? (vector-ref
                                                 syntmp-symnames-914
                                                 syntmp-i-918)
                                               syntmp-sym-911)
                                          (syntmp-same-marks?-139
                                            syntmp-marks-913
                                            (vector-ref
                                              (syntmp-ribcage-marks-128
                                                syntmp-ribcage-915)
                                              syntmp-i-918)))
                                     (values
                                       (vector-ref
                                         (syntmp-ribcage-labels-129
                                           syntmp-ribcage-915)
                                         syntmp-i-918)
                                       syntmp-marks-913))
                                    (else
                                     (syntmp-f-917
                                       (syntmp-fx+-88 syntmp-i-918 1))))))))
                      (syntmp-search-list-rib-899
                        (lambda (syntmp-sym-919
                                 syntmp-subst-920
                                 syntmp-marks-921
                                 syntmp-symnames-922
                                 syntmp-ribcage-923)
                          (let syntmp-f-924 ((syntmp-symnames-925
                                               syntmp-symnames-922)
                                             (syntmp-i-926 0))
                            (cond ((null? syntmp-symnames-925)
                                   (syntmp-search-898
                                     syntmp-sym-919
                                     (cdr syntmp-subst-920)
                                     syntmp-marks-921))
                                  ((and (eq? (car syntmp-symnames-925)
                                             syntmp-sym-919)
                                        (syntmp-same-marks?-139
                                          syntmp-marks-921
                                          (list-ref
                                            (syntmp-ribcage-marks-128
                                              syntmp-ribcage-923)
                                            syntmp-i-926)))
                                   (values
                                     (list-ref
                                       (syntmp-ribcage-labels-129
                                         syntmp-ribcage-923)
                                       syntmp-i-926)
                                     syntmp-marks-921))
                                  (else
                                   (syntmp-f-924
                                     (cdr syntmp-symnames-925)
                                     (syntmp-fx+-88 syntmp-i-926 1)))))))
                      (syntmp-search-898
                        (lambda (syntmp-sym-927
                                 syntmp-subst-928
                                 syntmp-marks-929)
                          (if (null? syntmp-subst-928)
                            (values #f syntmp-marks-929)
                            (let ((syntmp-fst-930 (car syntmp-subst-928)))
                              (if (eq? syntmp-fst-930 (quote shift))
                                (syntmp-search-898
                                  syntmp-sym-927
                                  (cdr syntmp-subst-928)
                                  (cdr syntmp-marks-929))
                                (let ((syntmp-symnames-931
                                        (syntmp-ribcage-symnames-127
                                          syntmp-fst-930)))
                                  (if (vector? syntmp-symnames-931)
                                    (syntmp-search-vector-rib-900
                                      syntmp-sym-927
                                      syntmp-subst-928
                                      syntmp-marks-929
                                      syntmp-symnames-931
                                      syntmp-fst-930)
                                    (syntmp-search-list-rib-899
                                      syntmp-sym-927
                                      syntmp-subst-928
                                      syntmp-marks-929
                                      syntmp-symnames-931
                                      syntmp-fst-930)))))))))
               (cond ((symbol? syntmp-id-896)
                      (or (call-with-values
                            (lambda ()
                              (syntmp-search-898
                                syntmp-id-896
                                (syntmp-wrap-subst-122 syntmp-w-897)
                                (syntmp-wrap-marks-121 syntmp-w-897)))
                            (lambda (syntmp-x-933 . syntmp-ignore-932)
                              syntmp-x-933))
                          syntmp-id-896))
                     ((syntmp-syntax-object?-104 syntmp-id-896)
                      (let ((syntmp-id-934
                              (let ((syntmp-e-936
                                      (syntmp-syntax-object-expression-105
                                        syntmp-id-896)))
                                (if (syntmp-annotation?-92 syntmp-e-936)
                                  (annotation-expression syntmp-e-936)
                                  syntmp-e-936)))
                            (syntmp-w1-935
                              (syntmp-syntax-object-wrap-106 syntmp-id-896)))
                        (let ((syntmp-marks-937
                                (syntmp-join-marks-138
                                  (syntmp-wrap-marks-121 syntmp-w-897)
                                  (syntmp-wrap-marks-121 syntmp-w1-935))))
                          (call-with-values
                            (lambda ()
                              (syntmp-search-898
                                syntmp-id-934
                                (syntmp-wrap-subst-122 syntmp-w-897)
                                syntmp-marks-937))
                            (lambda (syntmp-new-id-938 syntmp-marks-939)
                              (or syntmp-new-id-938
                                  (call-with-values
                                    (lambda ()
                                      (syntmp-search-898
                                        syntmp-id-934
                                        (syntmp-wrap-subst-122 syntmp-w1-935)
                                        syntmp-marks-939))
                                    (lambda (syntmp-x-941 . syntmp-ignore-940)
                                      syntmp-x-941))
                                  syntmp-id-934))))))
                     ((syntmp-annotation?-92 syntmp-id-896)
                      (let ((syntmp-id-942
                              (let ((syntmp-e-943 syntmp-id-896))
                                (if (syntmp-annotation?-92 syntmp-e-943)
                                  (annotation-expression syntmp-e-943)
                                  syntmp-e-943))))
                        (or (call-with-values
                              (lambda ()
                                (syntmp-search-898
                                  syntmp-id-942
                                  (syntmp-wrap-subst-122 syntmp-w-897)
                                  (syntmp-wrap-marks-121 syntmp-w-897)))
                              (lambda (syntmp-x-945 . syntmp-ignore-944)
                                syntmp-x-945))
                            syntmp-id-942)))
                     (else
                      (syntmp-error-hook-95
                        'id-var-name
                        "invalid id"
                        syntmp-id-896))))))
         (syntmp-same-marks?-139
           (lambda (syntmp-x-946 syntmp-y-947)
             (or (eq? syntmp-x-946 syntmp-y-947)
                 (and (not (null? syntmp-x-946))
                      (not (null? syntmp-y-947))
                      (eq? (car syntmp-x-946) (car syntmp-y-947))
                      (syntmp-same-marks?-139
                        (cdr syntmp-x-946)
                        (cdr syntmp-y-947))))))
         (syntmp-join-marks-138
           (lambda (syntmp-m1-948 syntmp-m2-949)
             (syntmp-smart-append-136
               syntmp-m1-948
               syntmp-m2-949)))
         (syntmp-join-wraps-137
           (lambda (syntmp-w1-950 syntmp-w2-951)
             (let ((syntmp-m1-952
                     (syntmp-wrap-marks-121 syntmp-w1-950))
                   (syntmp-s1-953
                     (syntmp-wrap-subst-122 syntmp-w1-950)))
               (if (null? syntmp-m1-952)
                 (if (null? syntmp-s1-953)
                   syntmp-w2-951
                   (syntmp-make-wrap-120
                     (syntmp-wrap-marks-121 syntmp-w2-951)
                     (syntmp-smart-append-136
                       syntmp-s1-953
                       (syntmp-wrap-subst-122 syntmp-w2-951))))
                 (syntmp-make-wrap-120
                   (syntmp-smart-append-136
                     syntmp-m1-952
                     (syntmp-wrap-marks-121 syntmp-w2-951))
                   (syntmp-smart-append-136
                     syntmp-s1-953
                     (syntmp-wrap-subst-122 syntmp-w2-951)))))))
         (syntmp-smart-append-136
           (lambda (syntmp-m1-954 syntmp-m2-955)
             (if (null? syntmp-m2-955)
               syntmp-m1-954
               (append syntmp-m1-954 syntmp-m2-955))))
         (syntmp-make-binding-wrap-135
           (lambda (syntmp-ids-956 syntmp-labels-957 syntmp-w-958)
             (if (null? syntmp-ids-956)
               syntmp-w-958
               (syntmp-make-wrap-120
                 (syntmp-wrap-marks-121 syntmp-w-958)
                 (cons (let ((syntmp-labelvec-959
                               (list->vector syntmp-labels-957)))
                         (let ((syntmp-n-960
                                 (vector-length syntmp-labelvec-959)))
                           (let ((syntmp-symnamevec-961
                                   (make-vector syntmp-n-960))
                                 (syntmp-marksvec-962
                                   (make-vector syntmp-n-960)))
                             (begin
                               (let syntmp-f-963 ((syntmp-ids-964
                                                    syntmp-ids-956)
                                                  (syntmp-i-965 0))
                                 (if (not (null? syntmp-ids-964))
                                   (call-with-values
                                     (lambda ()
                                       (syntmp-id-sym-name&marks-119
                                         (car syntmp-ids-964)
                                         syntmp-w-958))
                                     (lambda (syntmp-symname-966
                                              syntmp-marks-967)
                                       (begin
                                         (vector-set!
                                           syntmp-symnamevec-961
                                           syntmp-i-965
                                           syntmp-symname-966)
                                         (vector-set!
                                           syntmp-marksvec-962
                                           syntmp-i-965
                                           syntmp-marks-967)
                                         (syntmp-f-963
                                           (cdr syntmp-ids-964)
                                           (syntmp-fx+-88 syntmp-i-965 1)))))))
                               (syntmp-make-ribcage-125
                                 syntmp-symnamevec-961
                                 syntmp-marksvec-962
                                 syntmp-labelvec-959)))))
                       (syntmp-wrap-subst-122 syntmp-w-958))))))
         (syntmp-extend-ribcage!-134
           (lambda (syntmp-ribcage-968
                    syntmp-id-969
                    syntmp-label-970)
             (begin
               (syntmp-set-ribcage-symnames!-130
                 syntmp-ribcage-968
                 (cons (let ((syntmp-e-971
                               (syntmp-syntax-object-expression-105
                                 syntmp-id-969)))
                         (if (syntmp-annotation?-92 syntmp-e-971)
                           (annotation-expression syntmp-e-971)
                           syntmp-e-971))
                       (syntmp-ribcage-symnames-127 syntmp-ribcage-968)))
               (syntmp-set-ribcage-marks!-131
                 syntmp-ribcage-968
                 (cons (syntmp-wrap-marks-121
                         (syntmp-syntax-object-wrap-106 syntmp-id-969))
                       (syntmp-ribcage-marks-128 syntmp-ribcage-968)))
               (syntmp-set-ribcage-labels!-132
                 syntmp-ribcage-968
                 (cons syntmp-label-970
                       (syntmp-ribcage-labels-129 syntmp-ribcage-968))))))
         (syntmp-anti-mark-133
           (lambda (syntmp-w-972)
             (syntmp-make-wrap-120
               (cons #f (syntmp-wrap-marks-121 syntmp-w-972))
               (cons 'shift
                     (syntmp-wrap-subst-122 syntmp-w-972)))))
         (syntmp-set-ribcage-labels!-132
           (lambda (syntmp-x-973 syntmp-update-974)
             (vector-set! syntmp-x-973 3 syntmp-update-974)))
         (syntmp-set-ribcage-marks!-131
           (lambda (syntmp-x-975 syntmp-update-976)
             (vector-set! syntmp-x-975 2 syntmp-update-976)))
         (syntmp-set-ribcage-symnames!-130
           (lambda (syntmp-x-977 syntmp-update-978)
             (vector-set! syntmp-x-977 1 syntmp-update-978)))
         (syntmp-ribcage-labels-129
           (lambda (syntmp-x-979)
             (vector-ref syntmp-x-979 3)))
         (syntmp-ribcage-marks-128
           (lambda (syntmp-x-980)
             (vector-ref syntmp-x-980 2)))
         (syntmp-ribcage-symnames-127
           (lambda (syntmp-x-981)
             (vector-ref syntmp-x-981 1)))
         (syntmp-ribcage?-126
           (lambda (syntmp-x-982)
             (and (vector? syntmp-x-982)
                  (= (vector-length syntmp-x-982) 4)
                  (eq? (vector-ref syntmp-x-982 0) (quote ribcage)))))
         (syntmp-make-ribcage-125
           (lambda (syntmp-symnames-983
                    syntmp-marks-984
                    syntmp-labels-985)
             (vector
               'ribcage
               syntmp-symnames-983
               syntmp-marks-984
               syntmp-labels-985)))
         (syntmp-gen-labels-124
           (lambda (syntmp-ls-986)
             (if (null? syntmp-ls-986)
               '()
               (cons (syntmp-gen-label-123)
                     (syntmp-gen-labels-124 (cdr syntmp-ls-986))))))
         (syntmp-gen-label-123 (lambda () (string #\i)))
         (syntmp-wrap-subst-122 cdr)
         (syntmp-wrap-marks-121 car)
         (syntmp-make-wrap-120 cons)
         (syntmp-id-sym-name&marks-119
           (lambda (syntmp-x-987 syntmp-w-988)
             (if (syntmp-syntax-object?-104 syntmp-x-987)
               (values
                 (let ((syntmp-e-989
                         (syntmp-syntax-object-expression-105
                           syntmp-x-987)))
                   (if (syntmp-annotation?-92 syntmp-e-989)
                     (annotation-expression syntmp-e-989)
                     syntmp-e-989))
                 (syntmp-join-marks-138
                   (syntmp-wrap-marks-121 syntmp-w-988)
                   (syntmp-wrap-marks-121
                     (syntmp-syntax-object-wrap-106 syntmp-x-987))))
               (values
                 (let ((syntmp-e-990 syntmp-x-987))
                   (if (syntmp-annotation?-92 syntmp-e-990)
                     (annotation-expression syntmp-e-990)
                     syntmp-e-990))
                 (syntmp-wrap-marks-121 syntmp-w-988)))))
         (syntmp-id?-118
           (lambda (syntmp-x-991)
             (cond ((symbol? syntmp-x-991) #t)
                   ((syntmp-syntax-object?-104 syntmp-x-991)
                    (symbol?
                      (let ((syntmp-e-992
                              (syntmp-syntax-object-expression-105
                                syntmp-x-991)))
                        (if (syntmp-annotation?-92 syntmp-e-992)
                          (annotation-expression syntmp-e-992)
                          syntmp-e-992))))
                   ((syntmp-annotation?-92 syntmp-x-991)
                    (symbol? (annotation-expression syntmp-x-991)))
                   (else #f))))
         (syntmp-nonsymbol-id?-117
           (lambda (syntmp-x-993)
             (and (syntmp-syntax-object?-104 syntmp-x-993)
                  (symbol?
                    (let ((syntmp-e-994
                            (syntmp-syntax-object-expression-105
                              syntmp-x-993)))
                      (if (syntmp-annotation?-92 syntmp-e-994)
                        (annotation-expression syntmp-e-994)
                        syntmp-e-994))))))
         (syntmp-global-extend-116
           (lambda (syntmp-type-995 syntmp-sym-996 syntmp-val-997)
             (syntmp-put-global-definition-hook-96
               syntmp-sym-996
               (cons syntmp-type-995 syntmp-val-997))))
         (syntmp-lookup-115
           (lambda (syntmp-x-998 syntmp-r-999)
             (cond ((assq syntmp-x-998 syntmp-r-999) => cdr)
                   ((symbol? syntmp-x-998)
                    (or (syntmp-get-global-definition-hook-97
                          syntmp-x-998)
                        '(global)))
                   (else (quote (displaced-lexical))))))
         (syntmp-macros-only-env-114
           (lambda (syntmp-r-1000)
             (if (null? syntmp-r-1000)
               '()
               (let ((syntmp-a-1001 (car syntmp-r-1000)))
                 (if (memq (cadr syntmp-a-1001)
                           '(macro ellipsis))
                   (cons syntmp-a-1001
                         (syntmp-macros-only-env-114 (cdr syntmp-r-1000)))
                   (syntmp-macros-only-env-114 (cdr syntmp-r-1000)))))))
         (syntmp-extend-var-env-113
           (lambda (syntmp-labels-1002
                    syntmp-vars-1003
                    syntmp-r-1004)
             (if (null? syntmp-labels-1002)
               syntmp-r-1004
               (syntmp-extend-var-env-113
                 (cdr syntmp-labels-1002)
                 (cdr syntmp-vars-1003)
                 (cons (cons (car syntmp-labels-1002)
                             (cons (quote lexical) (car syntmp-vars-1003)))
                       syntmp-r-1004)))))
         (syntmp-extend-env-112
           (lambda (syntmp-labels-1005
                    syntmp-bindings-1006
                    syntmp-r-1007)
             (if (null? syntmp-labels-1005)
               syntmp-r-1007
               (syntmp-extend-env-112
                 (cdr syntmp-labels-1005)
                 (cdr syntmp-bindings-1006)
                 (cons (cons (car syntmp-labels-1005)
                             (car syntmp-bindings-1006))
                       syntmp-r-1007)))))
         (syntmp-binding-value-111 cdr)
         (syntmp-binding-type-110 car)
         (syntmp-source-annotation-109
           (lambda (syntmp-x-1008)
             (cond ((syntmp-annotation?-92 syntmp-x-1008)
                    (annotation-source syntmp-x-1008))
                   ((syntmp-syntax-object?-104 syntmp-x-1008)
                    (syntmp-source-annotation-109
                      (syntmp-syntax-object-expression-105
                        syntmp-x-1008)))
                   (else #f))))
         (syntmp-set-syntax-object-wrap!-108
           (lambda (syntmp-x-1009 syntmp-update-1010)
             (vector-set! syntmp-x-1009 2 syntmp-update-1010)))
         (syntmp-set-syntax-object-expression!-107
           (lambda (syntmp-x-1011 syntmp-update-1012)
             (vector-set! syntmp-x-1011 1 syntmp-update-1012)))
         (syntmp-syntax-object-wrap-106
           (lambda (syntmp-x-1013)
             (vector-ref syntmp-x-1013 2)))
         (syntmp-syntax-object-expression-105
           (lambda (syntmp-x-1014)
             (vector-ref syntmp-x-1014 1)))
         (syntmp-syntax-object?-104
           (lambda (syntmp-x-1015)
             (and (vector? syntmp-x-1015)
                  (= (vector-length syntmp-x-1015) 3)
                  (eq? (vector-ref syntmp-x-1015 0)
                       'syntax-object))))
         (syntmp-make-syntax-object-103
           (lambda (syntmp-expression-1016 syntmp-wrap-1017)
             (vector
               'syntax-object
               syntmp-expression-1016
               syntmp-wrap-1017)))
         (syntmp-build-letrec-102
           (lambda (syntmp-src-1018
                    syntmp-vars-1019
                    syntmp-val-exps-1020
                    syntmp-body-exp-1021)
             (if (null? syntmp-vars-1019)
               syntmp-body-exp-1021
               (list 'letrec
                     (map list syntmp-vars-1019 syntmp-val-exps-1020)
                     syntmp-body-exp-1021))))
         (syntmp-build-named-let-101
           (lambda (syntmp-src-1022
                    syntmp-vars-1023
                    syntmp-val-exps-1024
                    syntmp-body-exp-1025)
             (if (null? syntmp-vars-1023)
               syntmp-body-exp-1025
               (list 'let
                     (car syntmp-vars-1023)
                     (map list
                          (cdr syntmp-vars-1023)
                          syntmp-val-exps-1024)
                     syntmp-body-exp-1025))))
         (syntmp-build-let-100
           (lambda (syntmp-src-1026
                    syntmp-vars-1027
                    syntmp-val-exps-1028
                    syntmp-body-exp-1029)
             (if (null? syntmp-vars-1027)
               syntmp-body-exp-1029
               (list 'let
                     (map list syntmp-vars-1027 syntmp-val-exps-1028)
                     syntmp-body-exp-1029))))
         (syntmp-build-sequence-99
           (lambda (syntmp-src-1030 syntmp-exps-1031)
             (if (null? (cdr syntmp-exps-1031))
               (car syntmp-exps-1031)
               (cons (quote begin) syntmp-exps-1031))))
         (syntmp-build-data-98
           (lambda (syntmp-src-1032 syntmp-exp-1033)
             (if (and (self-evaluating? syntmp-exp-1033)
                      (not (vector? syntmp-exp-1033)))
               syntmp-exp-1033
               (list (quote quote) syntmp-exp-1033))))
         (syntmp-get-global-definition-hook-97
           (lambda (syntmp-symbol-1034)
             (getprop
               syntmp-symbol-1034
               '*sc-expander*)))
         (syntmp-put-global-definition-hook-96
           (lambda (syntmp-symbol-1035 syntmp-binding-1036)
             (putprop
               syntmp-symbol-1035
               '*sc-expander*
               syntmp-binding-1036)))
         (syntmp-error-hook-95
           (lambda (syntmp-who-1037
                    syntmp-why-1038
                    syntmp-what-1039)
             (error syntmp-who-1037
                    "~a ~s"
                    syntmp-why-1038
                    syntmp-what-1039)))
         (syntmp-local-eval-hook-94
           (lambda (syntmp-x-1040)
             (eval (list syntmp-noexpand-87 syntmp-x-1040)
                   (interaction-environment))))
         (syntmp-top-level-eval-hook-93
           (lambda (syntmp-x-1041)
             (eval (list syntmp-noexpand-87 syntmp-x-1041)
                   (interaction-environment))))
         (syntmp-annotation?-92
           (lambda (syntmp-x-1042) #f))
         (syntmp-fx<-91 <)
         (syntmp-fx=-90 =)
         (syntmp-fx--89 -)
         (syntmp-fx+-88 +)
         (syntmp-noexpand-87 "noexpand"))
  (begin
    (syntmp-global-extend-116
      'local-syntax
      'letrec-syntax
      #t)
    (syntmp-global-extend-116
      'local-syntax
      'let-syntax
      #f)
    (syntmp-global-extend-116
      'core
      'fluid-let-syntax
      (lambda (syntmp-e-1043
               syntmp-r-1044
               syntmp-w-1045
               syntmp-s-1046)
        ((lambda (syntmp-tmp-1047)
           ((lambda (syntmp-tmp-1048)
              (if (if syntmp-tmp-1048
                    (apply (lambda (syntmp-_-1049
                                    syntmp-var-1050
                                    syntmp-val-1051
                                    syntmp-e1-1052
                                    syntmp-e2-1053)
                             (syntmp-valid-bound-ids?-143 syntmp-var-1050))
                           syntmp-tmp-1048)
                    #f)
                (apply (lambda (syntmp-_-1055
                                syntmp-var-1056
                                syntmp-val-1057
                                syntmp-e1-1058
                                syntmp-e2-1059)
                         (let ((syntmp-names-1060
                                 (map (lambda (syntmp-x-1061)
                                        (syntmp-id-var-name-140
                                          syntmp-x-1061
                                          syntmp-w-1045))
                                      syntmp-var-1056)))
                           (begin
                             (for-each
                               (lambda (syntmp-id-1063 syntmp-n-1064)
                                 (let ((syntmp-t-1065
                                         (syntmp-binding-type-110
                                           (syntmp-lookup-115
                                             syntmp-n-1064
                                             syntmp-r-1044))))
                                   (if (memv syntmp-t-1065
                                             '(displaced-lexical))
                                     (syntax-error
                                       (syntmp-source-wrap-147
                                         syntmp-id-1063
                                         syntmp-w-1045
                                         syntmp-s-1046)
                                       "identifier out of context"))))
                               syntmp-var-1056
                               syntmp-names-1060)
                             (syntmp-chi-body-158
                               (cons syntmp-e1-1058 syntmp-e2-1059)
                               (syntmp-source-wrap-147
                                 syntmp-e-1043
                                 syntmp-w-1045
                                 syntmp-s-1046)
                               (syntmp-extend-env-112
                                 syntmp-names-1060
                                 (let ((syntmp-trans-r-1068
                                         (syntmp-macros-only-env-114
                                           syntmp-r-1044)))
                                   (map (lambda (syntmp-x-1069)
                                          (cons 'macro
                                                (syntmp-eval-local-transformer-161
                                                  (syntmp-chi-154
                                                    syntmp-x-1069
                                                    syntmp-trans-r-1068
                                                    syntmp-w-1045))))
                                        syntmp-val-1057))
                                 syntmp-r-1044)
                               syntmp-w-1045))))
                       syntmp-tmp-1048)
                ((lambda (syntmp-_-1071)
                   (syntax-error
                     (syntmp-source-wrap-147
                       syntmp-e-1043
                       syntmp-w-1045
                       syntmp-s-1046)))
                 syntmp-tmp-1047)))
            (syntax-dispatch
              syntmp-tmp-1047
              '(any #(each (any any)) any . each-any))))
         syntmp-e-1043)))
    (syntmp-global-extend-116
      'core
      'quote
      (lambda (syntmp-e-1072
               syntmp-r-1073
               syntmp-w-1074
               syntmp-s-1075)
        ((lambda (syntmp-tmp-1076)
           ((lambda (syntmp-tmp-1077)
              (if syntmp-tmp-1077
                (apply (lambda (syntmp-_-1078 syntmp-e-1079)
                         (syntmp-build-data-98
                           syntmp-s-1075
                           (syntmp-strip-165 syntmp-e-1079 syntmp-w-1074)))
                       syntmp-tmp-1077)
                ((lambda (syntmp-_-1080)
                   (syntax-error
                     (syntmp-source-wrap-147
                       syntmp-e-1072
                       syntmp-w-1074
                       syntmp-s-1075)))
                 syntmp-tmp-1076)))
            (syntax-dispatch
              syntmp-tmp-1076
              '(any any))))
         syntmp-e-1072)))
    (syntmp-global-extend-116
      'core
      'syntax
      (letrec ((syntmp-regen-1088
                 (lambda (syntmp-x-1089)
                   (let ((syntmp-t-1090 (car syntmp-x-1089)))
                     (if (memv syntmp-t-1090 (quote (ref)))
                       (cadr syntmp-x-1089)
                       (if (memv syntmp-t-1090 (quote (primitive)))
                         (cadr syntmp-x-1089)
                         (if (memv syntmp-t-1090 (quote (quote)))
                           (syntmp-build-data-98 #f (cadr syntmp-x-1089))
                           (if (memv syntmp-t-1090 (quote (lambda)))
                             (list 'lambda
                                   (cadr syntmp-x-1089)
                                   (syntmp-regen-1088 (caddr syntmp-x-1089)))
                             (if (memv syntmp-t-1090 (quote (map)))
                               (let ((syntmp-ls-1091
                                       (map syntmp-regen-1088
                                            (cdr syntmp-x-1089))))
                                 (cons (if (syntmp-fx=-90
                                             (length syntmp-ls-1091)
                                             2)
                                         'map
                                         'map)
                                       syntmp-ls-1091))
                               (cons (car syntmp-x-1089)
                                     (map syntmp-regen-1088
                                          (cdr syntmp-x-1089)))))))))))
               (syntmp-gen-vector-1087
                 (lambda (syntmp-x-1092)
                   (cond ((eq? (car syntmp-x-1092) (quote list))
                          (cons (quote vector) (cdr syntmp-x-1092)))
                         ((eq? (car syntmp-x-1092) (quote quote))
                          (list 'quote
                                (list->vector (cadr syntmp-x-1092))))
                         (else (list (quote list->vector) syntmp-x-1092)))))
               (syntmp-gen-append-1086
                 (lambda (syntmp-x-1093 syntmp-y-1094)
                   (if (equal? syntmp-y-1094 (quote (quote ())))
                     syntmp-x-1093
                     (list (quote append) syntmp-x-1093 syntmp-y-1094))))
               (syntmp-gen-cons-1085
                 (lambda (syntmp-x-1095 syntmp-y-1096)
                   (let ((syntmp-t-1097 (car syntmp-y-1096)))
                     (if (memv syntmp-t-1097 (quote (quote)))
                       (if (eq? (car syntmp-x-1095) (quote quote))
                         (list 'quote
                               (cons (cadr syntmp-x-1095)
                                     (cadr syntmp-y-1096)))
                         (if (eq? (cadr syntmp-y-1096) (quote ()))
                           (list (quote list) syntmp-x-1095)
                           (list (quote cons) syntmp-x-1095 syntmp-y-1096)))
                       (if (memv syntmp-t-1097 (quote (list)))
                         (cons 'list
                               (cons syntmp-x-1095 (cdr syntmp-y-1096)))
                         (list (quote cons) syntmp-x-1095 syntmp-y-1096))))))
               (syntmp-gen-map-1084
                 (lambda (syntmp-e-1098 syntmp-map-env-1099)
                   (let ((syntmp-formals-1100
                           (map cdr syntmp-map-env-1099))
                         (syntmp-actuals-1101
                           (map (lambda (syntmp-x-1102)
                                  (list (quote ref) (car syntmp-x-1102)))
                                syntmp-map-env-1099)))
                     (cond ((eq? (car syntmp-e-1098) (quote ref))
                            (car syntmp-actuals-1101))
                           ((andmap
                              (lambda (syntmp-x-1103)
                                (and (eq? (car syntmp-x-1103) (quote ref))
                                     (memq (cadr syntmp-x-1103)
                                           syntmp-formals-1100)))
                              (cdr syntmp-e-1098))
                            (cons 'map
                                  (cons (list 'primitive
                                              (car syntmp-e-1098))
                                        (map (let ((syntmp-r-1104
                                                     (map cons
                                                          syntmp-formals-1100
                                                          syntmp-actuals-1101)))
                                               (lambda (syntmp-x-1105)
                                                 (cdr (assq (cadr syntmp-x-1105)
                                                            syntmp-r-1104))))
                                             (cdr syntmp-e-1098)))))
                           (else
                            (cons 'map
                                  (cons (list 'lambda
                                              syntmp-formals-1100
                                              syntmp-e-1098)
                                        syntmp-actuals-1101)))))))
               (syntmp-gen-mappend-1083
                 (lambda (syntmp-e-1106 syntmp-map-env-1107)
                   (list 'apply
                         '(primitive append)
                         (syntmp-gen-map-1084
                           syntmp-e-1106
                           syntmp-map-env-1107))))
               (syntmp-gen-ref-1082
                 (lambda (syntmp-src-1108
                          syntmp-var-1109
                          syntmp-level-1110
                          syntmp-maps-1111)
                   (if (syntmp-fx=-90 syntmp-level-1110 0)
                     (values syntmp-var-1109 syntmp-maps-1111)
                     (if (null? syntmp-maps-1111)
                       (syntax-error
                         syntmp-src-1108
                         "missing ellipsis in syntax form")
                       (call-with-values
                         (lambda ()
                           (syntmp-gen-ref-1082
                             syntmp-src-1108
                             syntmp-var-1109
                             (syntmp-fx--89 syntmp-level-1110 1)
                             (cdr syntmp-maps-1111)))
                         (lambda (syntmp-outer-var-1112 syntmp-outer-maps-1113)
                           (let ((syntmp-b-1114
                                   (assq syntmp-outer-var-1112
                                         (car syntmp-maps-1111))))
                             (if syntmp-b-1114
                               (values (cdr syntmp-b-1114) syntmp-maps-1111)
                               (let ((syntmp-inner-var-1115
                                       (syntmp-gen-var-166 (quote tmp))))
                                 (values
                                   syntmp-inner-var-1115
                                   (cons (cons (cons syntmp-outer-var-1112
                                                     syntmp-inner-var-1115)
                                               (car syntmp-maps-1111))
                                         syntmp-outer-maps-1113)))))))))))
               (syntmp-gen-syntax-1081
                 (lambda (syntmp-src-1116
                          syntmp-e-1117
                          syntmp-r-1118
                          syntmp-maps-1119
                          syntmp-ellipsis?-1120)
                   (if (syntmp-id?-118 syntmp-e-1117)
                     (let ((syntmp-label-1121
                             (syntmp-id-var-name-140
                               syntmp-e-1117
                               '(()))))
                       (let ((syntmp-b-1122
                               (syntmp-lookup-115
                                 syntmp-label-1121
                                 syntmp-r-1118)))
                         (if (eq? (syntmp-binding-type-110 syntmp-b-1122)
                                  'syntax)
                           (call-with-values
                             (lambda ()
                               (let ((syntmp-var.lev-1123
                                       (syntmp-binding-value-111
                                         syntmp-b-1122)))
                                 (syntmp-gen-ref-1082
                                   syntmp-src-1116
                                   (car syntmp-var.lev-1123)
                                   (cdr syntmp-var.lev-1123)
                                   syntmp-maps-1119)))
                             (lambda (syntmp-var-1124 syntmp-maps-1125)
                               (values
                                 (list (quote ref) syntmp-var-1124)
                                 syntmp-maps-1125)))
                           (if (syntmp-ellipsis?-1120
                                 syntmp-e-1117
                                 syntmp-r-1118)
                             (syntax-error
                               syntmp-src-1116
                               "misplaced ellipsis in syntax form")
                             (values
                               (list (quote quote) syntmp-e-1117)
                               syntmp-maps-1119)))))
                     ((lambda (syntmp-tmp-1126)
                        ((lambda (syntmp-tmp-1127)
                           (if (if syntmp-tmp-1127
                                 (apply (lambda (syntmp-dots-1128
                                                 syntmp-e-1129)
                                          (syntmp-ellipsis?-1120
                                            syntmp-dots-1128
                                            syntmp-r-1118))
                                        syntmp-tmp-1127)
                                 #f)
                             (apply (lambda (syntmp-dots-1130 syntmp-e-1131)
                                      (syntmp-gen-syntax-1081
                                        syntmp-src-1116
                                        syntmp-e-1131
                                        syntmp-r-1118
                                        syntmp-maps-1119
                                        (lambda (syntmp-e-1132 syntmp-r-1133)
                                          #f)))
                                    syntmp-tmp-1127)
                             ((lambda (syntmp-tmp-1134)
                                (if (if syntmp-tmp-1134
                                      (apply (lambda (syntmp-x-1135
                                                      syntmp-dots-1136
                                                      syntmp-y-1137)
                                               (syntmp-ellipsis?-1120
                                                 syntmp-dots-1136
                                                 syntmp-r-1118))
                                             syntmp-tmp-1134)
                                      #f)
                                  (apply (lambda (syntmp-x-1138
                                                  syntmp-dots-1139
                                                  syntmp-y-1140)
                                           (let syntmp-f-1141 ((syntmp-y-1142
                                                                 syntmp-y-1140)
                                                               (syntmp-k-1143
                                                                 (lambda (syntmp-maps-1144)
                                                                   (call-with-values
                                                                     (lambda ()
                                                                       (syntmp-gen-syntax-1081
                                                                         syntmp-src-1116
                                                                         syntmp-x-1138
                                                                         syntmp-r-1118
                                                                         (cons '()
                                                                               syntmp-maps-1144)
                                                                         syntmp-ellipsis?-1120))
                                                                     (lambda (syntmp-x-1145
                                                                              syntmp-maps-1146)
                                                                       (if (null? (car syntmp-maps-1146))
                                                                         (syntax-error
                                                                           syntmp-src-1116
                                                                           "extra ellipsis in syntax form")
                                                                         (values
                                                                           (syntmp-gen-map-1084
                                                                             syntmp-x-1145
                                                                             (car syntmp-maps-1146))
                                                                           (cdr syntmp-maps-1146))))))))
                                             ((lambda (syntmp-tmp-1147)
                                                ((lambda (syntmp-tmp-1148)
                                                   (if (if syntmp-tmp-1148
                                                         (apply (lambda (syntmp-dots-1149
                                                                         syntmp-y-1150)
                                                                  (syntmp-ellipsis?-1120
                                                                    syntmp-dots-1149
                                                                    syntmp-r-1118))
                                                                syntmp-tmp-1148)
                                                         #f)
                                                     (apply (lambda (syntmp-dots-1151
                                                                     syntmp-y-1152)
                                                              (syntmp-f-1141
                                                                syntmp-y-1152
                                                                (lambda (syntmp-maps-1153)
                                                                  (call-with-values
                                                                    (lambda ()
                                                                      (syntmp-k-1143
                                                                        (cons '()
                                                                              syntmp-maps-1153)))
                                                                    (lambda (syntmp-x-1154
                                                                             syntmp-maps-1155)
                                                                      (if (null? (car syntmp-maps-1155))
                                                                        (syntax-error
                                                                          syntmp-src-1116
                                                                          "extra ellipsis in syntax form")
                                                                        (values
                                                                          (syntmp-gen-mappend-1083
                                                                            syntmp-x-1154
                                                                            (car syntmp-maps-1155))
                                                                          (cdr syntmp-maps-1155))))))))
                                                            syntmp-tmp-1148)
                                                     ((lambda (syntmp-_-1156)
                                                        (call-with-values
                                                          (lambda ()
                                                            (syntmp-gen-syntax-1081
                                                              syntmp-src-1116
                                                              syntmp-y-1142
                                                              syntmp-r-1118
                                                              syntmp-maps-1119
                                                              syntmp-ellipsis?-1120))
                                                          (lambda (syntmp-y-1157
                                                                   syntmp-maps-1158)
                                                            (call-with-values
                                                              (lambda ()
                                                                (syntmp-k-1143
                                                                  syntmp-maps-1158))
                                                              (lambda (syntmp-x-1159
                                                                       syntmp-maps-1160)
                                                                (values
                                                                  (syntmp-gen-append-1086
                                                                    syntmp-x-1159
                                                                    syntmp-y-1157)
                                                                  syntmp-maps-1160))))))
                                                      syntmp-tmp-1147)))
                                                 (syntax-dispatch
                                                   syntmp-tmp-1147
                                                   '(any . any))))
                                              syntmp-y-1142)))
                                         syntmp-tmp-1134)
                                  ((lambda (syntmp-tmp-1161)
                                     (if syntmp-tmp-1161
                                       (apply (lambda (syntmp-x-1162
                                                       syntmp-y-1163)
                                                (call-with-values
                                                  (lambda ()
                                                    (syntmp-gen-syntax-1081
                                                      syntmp-src-1116
                                                      syntmp-x-1162
                                                      syntmp-r-1118
                                                      syntmp-maps-1119
                                                      syntmp-ellipsis?-1120))
                                                  (lambda (syntmp-x-1164
                                                           syntmp-maps-1165)
                                                    (call-with-values
                                                      (lambda ()
                                                        (syntmp-gen-syntax-1081
                                                          syntmp-src-1116
                                                          syntmp-y-1163
                                                          syntmp-r-1118
                                                          syntmp-maps-1165
                                                          syntmp-ellipsis?-1120))
                                                      (lambda (syntmp-y-1166
                                                               syntmp-maps-1167)
                                                        (values
                                                          (syntmp-gen-cons-1085
                                                            syntmp-x-1164
                                                            syntmp-y-1166)
                                                          syntmp-maps-1167))))))
                                              syntmp-tmp-1161)
                                       ((lambda (syntmp-tmp-1168)
                                          (if syntmp-tmp-1168
                                            (apply (lambda (syntmp-e1-1169
                                                            syntmp-e2-1170)
                                                     (call-with-values
                                                       (lambda ()
                                                         (syntmp-gen-syntax-1081
                                                           syntmp-src-1116
                                                           (cons syntmp-e1-1169
                                                                 syntmp-e2-1170)
                                                           syntmp-r-1118
                                                           syntmp-maps-1119
                                                           syntmp-ellipsis?-1120))
                                                       (lambda (syntmp-e-1172
                                                                syntmp-maps-1173)
                                                         (values
                                                           (syntmp-gen-vector-1087
                                                             syntmp-e-1172)
                                                           syntmp-maps-1173))))
                                                   syntmp-tmp-1168)
                                            ((lambda (syntmp-_-1174)
                                               (values
                                                 (list 'quote
                                                       syntmp-e-1117)
                                                 syntmp-maps-1119))
                                             syntmp-tmp-1126)))
                                        (syntax-dispatch
                                          syntmp-tmp-1126
                                          '#(vector (any . each-any))))))
                                   (syntax-dispatch
                                     syntmp-tmp-1126
                                     '(any . any)))))
                              (syntax-dispatch
                                syntmp-tmp-1126
                                '(any any . any)))))
                         (syntax-dispatch
                           syntmp-tmp-1126
                           '(any any))))
                      syntmp-e-1117)))))
        (lambda (syntmp-e-1175
                 syntmp-r-1176
                 syntmp-w-1177
                 syntmp-s-1178)
          (let ((syntmp-e-1179
                  (syntmp-source-wrap-147
                    syntmp-e-1175
                    syntmp-w-1177
                    syntmp-s-1178)))
            ((lambda (syntmp-tmp-1180)
               ((lambda (syntmp-tmp-1181)
                  (if syntmp-tmp-1181
                    (apply (lambda (syntmp-_-1182 syntmp-x-1183)
                             (call-with-values
                               (lambda ()
                                 (syntmp-gen-syntax-1081
                                   syntmp-e-1179
                                   syntmp-x-1183
                                   syntmp-r-1176
                                   '()
                                   syntmp-ellipsis?-163))
                               (lambda (syntmp-e-1184 syntmp-maps-1185)
                                 (syntmp-regen-1088 syntmp-e-1184))))
                           syntmp-tmp-1181)
                    ((lambda (syntmp-_-1186)
                       (syntax-error syntmp-e-1179))
                     syntmp-tmp-1180)))
                (syntax-dispatch
                  syntmp-tmp-1180
                  '(any any))))
             syntmp-e-1179)))))
    (syntmp-global-extend-116
      'core
      'lambda
      (lambda (syntmp-e-1187
               syntmp-r-1188
               syntmp-w-1189
               syntmp-s-1190)
        ((lambda (syntmp-tmp-1191)
           ((lambda (syntmp-tmp-1192)
              (if syntmp-tmp-1192
                (apply (lambda (syntmp-_-1193 syntmp-c-1194)
                         (syntmp-chi-lambda-clause-159
                           (syntmp-source-wrap-147
                             syntmp-e-1187
                             syntmp-w-1189
                             syntmp-s-1190)
                           syntmp-c-1194
                           syntmp-r-1188
                           syntmp-w-1189
                           (lambda (syntmp-vars-1195 syntmp-body-1196)
                             (list 'lambda
                                   syntmp-vars-1195
                                   syntmp-body-1196))))
                       syntmp-tmp-1192)
                (syntax-error syntmp-tmp-1191)))
            (syntax-dispatch
              syntmp-tmp-1191
              '(any . any))))
         syntmp-e-1187)))
    (syntmp-global-extend-116
      'core
      'with-ellipsis
      (lambda (syntmp-e-1197
               syntmp-r-1198
               syntmp-w-1199
               syntmp-s-1200)
        (let ((syntmp-tmp-1201 syntmp-e-1197))
          (let ((syntmp-tmp-1202
                  (syntax-dispatch
                    syntmp-tmp-1201
                    '(_ any any . each-any))))
            (if (and syntmp-tmp-1202
                     (apply (lambda (syntmp-dots-1203
                                     syntmp-e1-1204
                                     syntmp-e2-1205)
                              (syntmp-id?-118 syntmp-dots-1203))
                            syntmp-tmp-1202))
              (apply (lambda (syntmp-dots-1206 syntmp-e1-1207 syntmp-e2-1208)
                       (let ((syntmp-id-1209
                               (if (symbol? syntmp-dots-1206)
                                 '$sc-ellipsis
                                 (syntmp-make-syntax-object-103
                                   '$sc-ellipsis
                                   (syntmp-syntax-object-wrap-106
                                     syntmp-dots-1206)))))
                         (let ((syntmp-ids-1210 (list syntmp-id-1209))
                               (syntmp-labels-1211
                                 (list (syntmp-gen-label-123)))
                               (syntmp-bindings-1212
                                 (list (cons 'ellipsis
                                             (syntmp-source-wrap-147
                                               syntmp-dots-1206
                                               syntmp-w-1199
                                               syntmp-s-1200)))))
                           (let ((syntmp-nw-1213
                                   (syntmp-make-binding-wrap-135
                                     syntmp-ids-1210
                                     syntmp-labels-1211
                                     syntmp-w-1199))
                                 (syntmp-nr-1214
                                   (syntmp-extend-env-112
                                     syntmp-labels-1211
                                     syntmp-bindings-1212
                                     syntmp-r-1198)))
                             (syntmp-chi-body-158
                               (cons syntmp-e1-1207 syntmp-e2-1208)
                               (syntmp-source-wrap-147
                                 syntmp-e-1197
                                 syntmp-nw-1213
                                 syntmp-s-1200)
                               syntmp-nr-1214
                               syntmp-nw-1213)))))
                     syntmp-tmp-1202)
              (syntax-error (quote with-ellipsis) "bad syntax"))))))
    (syntmp-global-extend-116
      'core
      'let
      (letrec ((syntmp-chi-let-1215
                 (lambda (syntmp-e-1216
                          syntmp-r-1217
                          syntmp-w-1218
                          syntmp-s-1219
                          syntmp-constructor-1220
                          syntmp-ids-1221
                          syntmp-vals-1222
                          syntmp-exps-1223)
                   (if (not (syntmp-valid-bound-ids?-143 syntmp-ids-1221))
                     (syntax-error
                       syntmp-e-1216
                       "duplicate bound variable in")
                     (let ((syntmp-labels-1224
                             (syntmp-gen-labels-124 syntmp-ids-1221))
                           (syntmp-new-vars-1225
                             (map syntmp-gen-var-166 syntmp-ids-1221)))
                       (let ((syntmp-nw-1226
                               (syntmp-make-binding-wrap-135
                                 syntmp-ids-1221
                                 syntmp-labels-1224
                                 syntmp-w-1218))
                             (syntmp-nr-1227
                               (syntmp-extend-var-env-113
                                 syntmp-labels-1224
                                 syntmp-new-vars-1225
                                 syntmp-r-1217)))
                         (syntmp-constructor-1220
                           syntmp-s-1219
                           syntmp-new-vars-1225
                           (map (lambda (syntmp-x-1228)
                                  (syntmp-chi-154
                                    syntmp-x-1228
                                    syntmp-r-1217
                                    syntmp-w-1218))
                                syntmp-vals-1222)
                           (syntmp-chi-body-158
                             syntmp-exps-1223
                             (syntmp-source-wrap-147
                               syntmp-e-1216
                               syntmp-nw-1226
                               syntmp-s-1219)
                             syntmp-nr-1227
                             syntmp-nw-1226))))))))
        (lambda (syntmp-e-1229
                 syntmp-r-1230
                 syntmp-w-1231
                 syntmp-s-1232)
          ((lambda (syntmp-tmp-1233)
             ((lambda (syntmp-tmp-1234)
                (if syntmp-tmp-1234
                  (apply (lambda (syntmp-_-1235
                                  syntmp-id-1236
                                  syntmp-val-1237
                                  syntmp-e1-1238
                                  syntmp-e2-1239)
                           (syntmp-chi-let-1215
                             syntmp-e-1229
                             syntmp-r-1230
                             syntmp-w-1231
                             syntmp-s-1232
                             syntmp-build-let-100
                             syntmp-id-1236
                             syntmp-val-1237
                             (cons syntmp-e1-1238 syntmp-e2-1239)))
                         syntmp-tmp-1234)
                  ((lambda (syntmp-tmp-1243)
                     (if (if syntmp-tmp-1243
                           (apply (lambda (syntmp-_-1244
                                           syntmp-f-1245
                                           syntmp-id-1246
                                           syntmp-val-1247
                                           syntmp-e1-1248
                                           syntmp-e2-1249)
                                    (syntmp-id?-118 syntmp-f-1245))
                                  syntmp-tmp-1243)
                           #f)
                       (apply (lambda (syntmp-_-1250
                                       syntmp-f-1251
                                       syntmp-id-1252
                                       syntmp-val-1253
                                       syntmp-e1-1254
                                       syntmp-e2-1255)
                                (syntmp-chi-let-1215
                                  syntmp-e-1229
                                  syntmp-r-1230
                                  syntmp-w-1231
                                  syntmp-s-1232
                                  syntmp-build-named-let-101
                                  (cons syntmp-f-1251 syntmp-id-1252)
                                  syntmp-val-1253
                                  (cons syntmp-e1-1254 syntmp-e2-1255)))
                              syntmp-tmp-1243)
                       ((lambda (syntmp-_-1259)
                          (syntax-error
                            (syntmp-source-wrap-147
                              syntmp-e-1229
                              syntmp-w-1231
                              syntmp-s-1232)))
                        syntmp-tmp-1233)))
                   (syntax-dispatch
                     syntmp-tmp-1233
                     '(any any #(each (any any)) any . each-any)))))
              (syntax-dispatch
                syntmp-tmp-1233
                '(any #(each (any any)) any . each-any))))
           syntmp-e-1229))))
    (syntmp-global-extend-116
      'core
      'letrec
      (lambda (syntmp-e-1260
               syntmp-r-1261
               syntmp-w-1262
               syntmp-s-1263)
        ((lambda (syntmp-tmp-1264)
           ((lambda (syntmp-tmp-1265)
              (if syntmp-tmp-1265
                (apply (lambda (syntmp-_-1266
                                syntmp-id-1267
                                syntmp-val-1268
                                syntmp-e1-1269
                                syntmp-e2-1270)
                         (let ((syntmp-ids-1271 syntmp-id-1267))
                           (if (not (syntmp-valid-bound-ids?-143
                                      syntmp-ids-1271))
                             (syntax-error
                               syntmp-e-1260
                               "duplicate bound variable in")
                             (let ((syntmp-labels-1273
                                     (syntmp-gen-labels-124 syntmp-ids-1271))
                                   (syntmp-new-vars-1274
                                     (map syntmp-gen-var-166 syntmp-ids-1271)))
                               (let ((syntmp-w-1275
                                       (syntmp-make-binding-wrap-135
                                         syntmp-ids-1271
                                         syntmp-labels-1273
                                         syntmp-w-1262))
                                     (syntmp-r-1276
                                       (syntmp-extend-var-env-113
                                         syntmp-labels-1273
                                         syntmp-new-vars-1274
                                         syntmp-r-1261)))
                                 (syntmp-build-letrec-102
                                   syntmp-s-1263
                                   syntmp-new-vars-1274
                                   (map (lambda (syntmp-x-1277)
                                          (syntmp-chi-154
                                            syntmp-x-1277
                                            syntmp-r-1276
                                            syntmp-w-1275))
                                        syntmp-val-1268)
                                   (syntmp-chi-body-158
                                     (cons syntmp-e1-1269 syntmp-e2-1270)
                                     (syntmp-source-wrap-147
                                       syntmp-e-1260
                                       syntmp-w-1275
                                       syntmp-s-1263)
                                     syntmp-r-1276
                                     syntmp-w-1275)))))))
                       syntmp-tmp-1265)
                ((lambda (syntmp-_-1280)
                   (syntax-error
                     (syntmp-source-wrap-147
                       syntmp-e-1260
                       syntmp-w-1262
                       syntmp-s-1263)))
                 syntmp-tmp-1264)))
            (syntax-dispatch
              syntmp-tmp-1264
              '(any #(each (any any)) any . each-any))))
         syntmp-e-1260)))
    (syntmp-global-extend-116
      'core
      'set!
      (lambda (syntmp-e-1281
               syntmp-r-1282
               syntmp-w-1283
               syntmp-s-1284)
        ((lambda (syntmp-tmp-1285)
           ((lambda (syntmp-tmp-1286)
              (if (if syntmp-tmp-1286
                    (apply (lambda (syntmp-_-1287
                                    syntmp-id-1288
                                    syntmp-val-1289)
                             (syntmp-id?-118 syntmp-id-1288))
                           syntmp-tmp-1286)
                    #f)
                (apply (lambda (syntmp-_-1290 syntmp-id-1291 syntmp-val-1292)
                         (let ((syntmp-val-1293
                                 (syntmp-chi-154
                                   syntmp-val-1292
                                   syntmp-r-1282
                                   syntmp-w-1283))
                               (syntmp-n-1294
                                 (syntmp-id-var-name-140
                                   syntmp-id-1291
                                   syntmp-w-1283)))
                           (let ((syntmp-b-1295
                                   (syntmp-lookup-115
                                     syntmp-n-1294
                                     syntmp-r-1282)))
                             (let ((syntmp-t-1296
                                     (syntmp-binding-type-110 syntmp-b-1295)))
                               (if (memv syntmp-t-1296 (quote (lexical)))
                                 (list 'set!
                                       (syntmp-binding-value-111 syntmp-b-1295)
                                       syntmp-val-1293)
                                 (if (memv syntmp-t-1296 (quote (global)))
                                   (list 'set!
                                         syntmp-n-1294
                                         syntmp-val-1293)
                                   (if (memv syntmp-t-1296
                                             '(displaced-lexical))
                                     (syntax-error
                                       (syntmp-wrap-146
                                         syntmp-id-1291
                                         syntmp-w-1283)
                                       "identifier out of context")
                                     (syntax-error
                                       (syntmp-source-wrap-147
                                         syntmp-e-1281
                                         syntmp-w-1283
                                         syntmp-s-1284)))))))))
                       syntmp-tmp-1286)
                ((lambda (syntmp-tmp-1297)
                   (if syntmp-tmp-1297
                     (apply (lambda (syntmp-_-1298
                                     syntmp-getter-1299
                                     syntmp-arg-1300
                                     syntmp-val-1301)
                              (cons (syntmp-chi-154
                                      (list '#(syntax-object
                                               setter
                                               ((top)
                                                #(ribcage
                                                  #(_ getter arg val)
                                                  #((top) (top) (top) (top))
                                                  #("i" "i" "i" "i"))
                                                #(ribcage () () ())
                                                #(ribcage
                                                  #(e r w s)
                                                  #((top) (top) (top) (top))
                                                  #("i" "i" "i" "i"))
                                                #(ribcage
                                                  (lambda-var-list
                                                    gen-var
                                                    strip
                                                    strip-annotation
                                                    ellipsis?
                                                    chi-void
                                                    eval-local-transformer
                                                    chi-local-syntax
                                                    chi-lambda-clause
                                                    chi-body
                                                    chi-macro
                                                    chi-application
                                                    chi-expr
                                                    chi
                                                    chi-top
                                                    syntax-type
                                                    chi-when-list
                                                    chi-install-global
                                                    chi-top-sequence
                                                    chi-sequence
                                                    source-wrap
                                                    wrap
                                                    bound-id-member?
                                                    distinct-bound-ids?
                                                    valid-bound-ids?
                                                    bound-id=?
                                                    free-id=?
                                                    id-var-name
                                                    same-marks?
                                                    join-marks
                                                    join-wraps
                                                    smart-append
                                                    make-binding-wrap
                                                    extend-ribcage!
                                                    make-empty-ribcage
                                                    new-mark
                                                    anti-mark
                                                    the-anti-mark
                                                    top-marked?
                                                    top-wrap
                                                    empty-wrap
                                                    set-ribcage-labels!
                                                    set-ribcage-marks!
                                                    set-ribcage-symnames!
                                                    ribcage-labels
                                                    ribcage-marks
                                                    ribcage-symnames
                                                    ribcage?
                                                    make-ribcage
                                                    gen-labels
                                                    gen-label
                                                    make-rename
                                                    rename-marks
                                                    rename-new
                                                    rename-old
                                                    subst-rename?
                                                    wrap-subst
                                                    wrap-marks
                                                    make-wrap
                                                    id-sym-name&marks
                                                    id-sym-name
                                                    id?
                                                    nonsymbol-id?
                                                    global-extend
                                                    lookup
                                                    macros-only-env
                                                    extend-var-env
                                                    extend-env
                                                    null-env
                                                    binding-value
                                                    binding-type
                                                    make-binding
                                                    arg-check
                                                    source-annotation
                                                    no-source
                                                    unannotate
                                                    set-syntax-object-wrap!
                                                    set-syntax-object-expression!
                                                    syntax-object-wrap
                                                    syntax-object-expression
                                                    syntax-object?
                                                    make-syntax-object
                                                    build-lexical-var
                                                    build-letrec
                                                    build-named-let
                                                    build-let
                                                    build-sequence
                                                    build-data
                                                    build-primref
                                                    build-lambda
                                                    build-global-definition
                                                    build-global-assignment
                                                    build-global-reference
                                                    build-lexical-assignment
                                                    build-lexical-reference
                                                    build-conditional
                                                    build-application
                                                    get-global-definition-hook
                                                    put-global-definition-hook
                                                    gensym-hook
                                                    error-hook
                                                    local-eval-hook
                                                    top-level-eval-hook
                                                    annotation?
                                                    fx<
                                                    fx=
                                                    fx-
                                                    fx+
                                                    noexpand)
                                                  ((top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top))
                                                  ("i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"
                                                   "i"))
                                                #(ribcage
                                                  (define-structure)
                                                  ((top))
                                                  ("i"))))
                                            syntmp-getter-1299)
                                      syntmp-r-1282
                                      syntmp-w-1283)
                                    (map (lambda (syntmp-e-1302)
                                           (syntmp-chi-154
                                             syntmp-e-1302
                                             syntmp-r-1282
                                             syntmp-w-1283))
                                         (append
                                           syntmp-arg-1300
                                           (list syntmp-val-1301)))))
                            syntmp-tmp-1297)
                     ((lambda (syntmp-_-1304)
                        (syntax-error
                          (syntmp-source-wrap-147
                            syntmp-e-1281
                            syntmp-w-1283
                            syntmp-s-1284)))
                      syntmp-tmp-1285)))
                 (syntax-dispatch
                   syntmp-tmp-1285
                   '(any (any . each-any) any)))))
            (syntax-dispatch
              syntmp-tmp-1285
              '(any any any))))
         syntmp-e-1281)))
    (syntmp-global-extend-116
      'begin
      'begin
      '())
    (syntmp-global-extend-116
      'define
      'define
      '())
    (syntmp-global-extend-116
      'define-syntax
      'define-syntax
      '())
    (syntmp-global-extend-116
      'eval-when
      'eval-when
      '())
    (syntmp-global-extend-116
      'core
      'syntax-case
      (letrec ((syntmp-gen-syntax-case-1308
                 (lambda (syntmp-x-1309
                          syntmp-keys-1310
                          syntmp-clauses-1311
                          syntmp-r-1312)
                   (if (null? syntmp-clauses-1311)
                     (list (quote syntax-error) syntmp-x-1309)
                     ((lambda (syntmp-tmp-1313)
                        ((lambda (syntmp-tmp-1314)
                           (if syntmp-tmp-1314
                             (apply (lambda (syntmp-pat-1315 syntmp-exp-1316)
                                      (if (and (syntmp-id?-118 syntmp-pat-1315)
                                               (andmap
                                                 (lambda (syntmp-x-1317)
                                                   (not (syntmp-free-id=?-141
                                                          syntmp-pat-1315
                                                          syntmp-x-1317)))
                                                 (cons '#(syntax-object
                                                          ...
                                                          ((top)
                                                           #(ribcage
                                                             #(pat exp)
                                                             #((top) (top))
                                                             #("i" "i"))
                                                           #(ribcage () () ())
                                                           #(ribcage
                                                             #(x
                                                               keys
                                                               clauses
                                                               r)
                                                             #((top)
                                                               (top)
                                                               (top)
                                                               (top))
                                                             #("i"
                                                               "i"
                                                               "i"
                                                               "i"))
                                                           #(ribcage
                                                             (gen-syntax-case
                                                               gen-clause
                                                               build-dispatch-call
                                                               convert-pattern)
                                                             ((top)
                                                              (top)
                                                              (top)
                                                              (top))
                                                             ("i" "i" "i" "i"))
                                                           #(ribcage
                                                             (lambda-var-list
                                                               gen-var
                                                               strip
                                                               strip-annotation
                                                               ellipsis?
                                                               chi-void
                                                               eval-local-transformer
                                                               chi-local-syntax
                                                               chi-lambda-clause
                                                               chi-body
                                                               chi-macro
                                                               chi-application
                                                               chi-expr
                                                               chi
                                                               chi-top
                                                               syntax-type
                                                               chi-when-list
                                                               chi-install-global
                                                               chi-top-sequence
                                                               chi-sequence
                                                               source-wrap
                                                               wrap
                                                               bound-id-member?
                                                               distinct-bound-ids?
                                                               valid-bound-ids?
                                                               bound-id=?
                                                               free-id=?
                                                               id-var-name
                                                               same-marks?
                                                               join-marks
                                                               join-wraps
                                                               smart-append
                                                               make-binding-wrap
                                                               extend-ribcage!
                                                               make-empty-ribcage
                                                               new-mark
                                                               anti-mark
                                                               the-anti-mark
                                                               top-marked?
                                                               top-wrap
                                                               empty-wrap
                                                               set-ribcage-labels!
                                                               set-ribcage-marks!
                                                               set-ribcage-symnames!
                                                               ribcage-labels
                                                               ribcage-marks
                                                               ribcage-symnames
                                                               ribcage?
                                                               make-ribcage
                                                               gen-labels
                                                               gen-label
                                                               make-rename
                                                               rename-marks
                                                               rename-new
                                                               rename-old
                                                               subst-rename?
                                                               wrap-subst
                                                               wrap-marks
                                                               make-wrap
                                                               id-sym-name&marks
                                                               id-sym-name
                                                               id?
                                                               nonsymbol-id?
                                                               global-extend
                                                               lookup
                                                               macros-only-env
                                                               extend-var-env
                                                               extend-env
                                                               null-env
                                                               binding-value
                                                               binding-type
                                                               make-binding
                                                               arg-check
                                                               source-annotation
                                                               no-source
                                                               unannotate
                                                               set-syntax-object-wrap!
                                                               set-syntax-object-expression!
                                                               syntax-object-wrap
                                                               syntax-object-expression
                                                               syntax-object?
                                                               make-syntax-object
                                                               build-lexical-var
                                                               build-letrec
                                                               build-named-let
                                                               build-let
                                                               build-sequence
                                                               build-data
                                                               build-primref
                                                               build-lambda
                                                               build-global-definition
                                                               build-global-assignment
                                                               build-global-reference
                                                               build-lexical-assignment
                                                               build-lexical-reference
                                                               build-conditional
                                                               build-application
                                                               get-global-definition-hook
                                                               put-global-definition-hook
                                                               gensym-hook
                                                               error-hook
                                                               local-eval-hook
                                                               top-level-eval-hook
                                                               annotation?
                                                               fx<
                                                               fx=
                                                               fx-
                                                               fx+
                                                               noexpand)
                                                             ((top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top))
                                                             ("i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"))
                                                           #(ribcage
                                                             (define-structure)
                                                             ((top))
                                                             ("i"))))
                                                       syntmp-keys-1310)))
                                        (let ((syntmp-labels-1318
                                                (list (syntmp-gen-label-123)))
                                              (syntmp-var-1319
                                                (syntmp-gen-var-166
                                                  syntmp-pat-1315)))
                                          (list (list 'lambda
                                                      (list syntmp-var-1319)
                                                      (syntmp-chi-154
                                                        syntmp-exp-1316
                                                        (syntmp-extend-env-112
                                                          syntmp-labels-1318
                                                          (list (cons 'syntax
                                                                      (cons syntmp-var-1319
                                                                            0)))
                                                          syntmp-r-1312)
                                                        (syntmp-make-binding-wrap-135
                                                          (list syntmp-pat-1315)
                                                          syntmp-labels-1318
                                                          '(()))))
                                                syntmp-x-1309))
                                        (syntmp-gen-clause-1307
                                          syntmp-x-1309
                                          syntmp-keys-1310
                                          (cdr syntmp-clauses-1311)
                                          syntmp-r-1312
                                          syntmp-pat-1315
                                          #t
                                          syntmp-exp-1316)))
                                    syntmp-tmp-1314)
                             ((lambda (syntmp-tmp-1320)
                                (if syntmp-tmp-1320
                                  (apply (lambda (syntmp-pat-1321
                                                  syntmp-fender-1322
                                                  syntmp-exp-1323)
                                           (syntmp-gen-clause-1307
                                             syntmp-x-1309
                                             syntmp-keys-1310
                                             (cdr syntmp-clauses-1311)
                                             syntmp-r-1312
                                             syntmp-pat-1321
                                             syntmp-fender-1322
                                             syntmp-exp-1323))
                                         syntmp-tmp-1320)
                                  ((lambda (syntmp-_-1324)
                                     (syntax-error
                                       (car syntmp-clauses-1311)
                                       "invalid syntax-case clause"))
                                   syntmp-tmp-1313)))
                              (syntax-dispatch
                                syntmp-tmp-1313
                                '(any any any)))))
                         (syntax-dispatch
                           syntmp-tmp-1313
                           '(any any))))
                      (car syntmp-clauses-1311)))))
               (syntmp-gen-clause-1307
                 (lambda (syntmp-x-1325
                          syntmp-keys-1326
                          syntmp-clauses-1327
                          syntmp-r-1328
                          syntmp-pat-1329
                          syntmp-fender-1330
                          syntmp-exp-1331)
                   (call-with-values
                     (lambda ()
                       (syntmp-convert-pattern-1305
                         syntmp-pat-1329
                         syntmp-keys-1326
                         (lambda (syntmp-e-1332)
                           (syntmp-ellipsis?-163
                             syntmp-e-1332
                             syntmp-r-1328))))
                     (lambda (syntmp-p-1333 syntmp-pvars-1334)
                       (cond ((not (syntmp-distinct-bound-ids?-144
                                     (map car syntmp-pvars-1334)))
                              (syntax-error
                                syntmp-pat-1329
                                "duplicate pattern variable in syntax-case pattern"))
                             ((not (andmap
                                     (lambda (syntmp-x-1335)
                                       (not (syntmp-ellipsis?-163
                                              (car syntmp-x-1335)
                                              syntmp-r-1328)))
                                     syntmp-pvars-1334))
                              (syntax-error
                                syntmp-pat-1329
                                "misplaced ellipsis in syntax-case pattern"))
                             (else
                              (let ((syntmp-y-1336
                                      (syntmp-gen-var-166 (quote tmp))))
                                (list (list 'lambda
                                            (list syntmp-y-1336)
                                            (let ((syntmp-y-1337
                                                    syntmp-y-1336))
                                              (list 'if
                                                    ((lambda (syntmp-tmp-1338)
                                                       ((lambda (syntmp-tmp-1339)
                                                          (if syntmp-tmp-1339
                                                            (apply (lambda ()
                                                                     syntmp-y-1337)
                                                                   syntmp-tmp-1339)
                                                            ((lambda (syntmp-_-1340)
                                                               (list 'if
                                                                     syntmp-y-1337
                                                                     (syntmp-build-dispatch-call-1306
                                                                       syntmp-pvars-1334
                                                                       syntmp-fender-1330
                                                                       syntmp-y-1337
                                                                       syntmp-r-1328)
                                                                     (syntmp-build-data-98
                                                                       #f
                                                                       #f)))
                                                             syntmp-tmp-1338)))
                                                        (syntax-dispatch
                                                          syntmp-tmp-1338
                                                          '#(atom #t))))
                                                     syntmp-fender-1330)
                                                    (syntmp-build-dispatch-call-1306
                                                      syntmp-pvars-1334
                                                      syntmp-exp-1331
                                                      syntmp-y-1337
                                                      syntmp-r-1328)
                                                    (syntmp-gen-syntax-case-1308
                                                      syntmp-x-1325
                                                      syntmp-keys-1326
                                                      syntmp-clauses-1327
                                                      syntmp-r-1328))))
                                      (if (eq? syntmp-p-1333 (quote any))
                                        (list (quote list) syntmp-x-1325)
                                        (list 'syntax-dispatch
                                              syntmp-x-1325
                                              (syntmp-build-data-98
                                                #f
                                                syntmp-p-1333)))))))))))
               (syntmp-build-dispatch-call-1306
                 (lambda (syntmp-pvars-1341
                          syntmp-exp-1342
                          syntmp-y-1343
                          syntmp-r-1344)
                   (let ((syntmp-ids-1345 (map car syntmp-pvars-1341))
                         (syntmp-levels-1346 (map cdr syntmp-pvars-1341)))
                     (let ((syntmp-labels-1347
                             (syntmp-gen-labels-124 syntmp-ids-1345))
                           (syntmp-new-vars-1348
                             (map syntmp-gen-var-166 syntmp-ids-1345)))
                       (list 'apply
                             (list 'lambda
                                   syntmp-new-vars-1348
                                   (syntmp-chi-154
                                     syntmp-exp-1342
                                     (syntmp-extend-env-112
                                       syntmp-labels-1347
                                       (map (lambda (syntmp-var-1349
                                                     syntmp-level-1350)
                                              (cons 'syntax
                                                    (cons syntmp-var-1349
                                                          syntmp-level-1350)))
                                            syntmp-new-vars-1348
                                            (map cdr syntmp-pvars-1341))
                                       syntmp-r-1344)
                                     (syntmp-make-binding-wrap-135
                                       syntmp-ids-1345
                                       syntmp-labels-1347
                                       '(()))))
                             syntmp-y-1343)))))
               (syntmp-convert-pattern-1305
                 (lambda (syntmp-pattern-1351
                          syntmp-keys-1352
                          syntmp-ellipsis?-1353)
                   (let syntmp-cvt-1354 ((syntmp-p-1355 syntmp-pattern-1351)
                                         (syntmp-n-1356 0)
                                         (syntmp-ids-1357 (quote ())))
                     (if (syntmp-id?-118 syntmp-p-1355)
                       (if (syntmp-bound-id-member?-145
                             syntmp-p-1355
                             syntmp-keys-1352)
                         (values
                           (vector (quote free-id) syntmp-p-1355)
                           syntmp-ids-1357)
                         (values
                           'any
                           (cons (cons syntmp-p-1355 syntmp-n-1356)
                                 syntmp-ids-1357)))
                       ((lambda (syntmp-tmp-1358)
                          ((lambda (syntmp-tmp-1359)
                             (if (if syntmp-tmp-1359
                                   (apply (lambda (syntmp-x-1360
                                                   syntmp-dots-1361)
                                            (syntmp-ellipsis?-1353
                                              syntmp-dots-1361))
                                          syntmp-tmp-1359)
                                   #f)
                               (apply (lambda (syntmp-x-1362 syntmp-dots-1363)
                                        (call-with-values
                                          (lambda ()
                                            (syntmp-cvt-1354
                                              syntmp-x-1362
                                              (syntmp-fx+-88 syntmp-n-1356 1)
                                              syntmp-ids-1357))
                                          (lambda (syntmp-p-1364
                                                   syntmp-ids-1365)
                                            (values
                                              (if (eq? syntmp-p-1364
                                                       'any)
                                                'each-any
                                                (vector
                                                  'each
                                                  syntmp-p-1364))
                                              syntmp-ids-1365))))
                                      syntmp-tmp-1359)
                               ((lambda (syntmp-tmp-1366)
                                  (if syntmp-tmp-1366
                                    (apply (lambda (syntmp-x-1367
                                                    syntmp-y-1368)
                                             (call-with-values
                                               (lambda ()
                                                 (syntmp-cvt-1354
                                                   syntmp-y-1368
                                                   syntmp-n-1356
                                                   syntmp-ids-1357))
                                               (lambda (syntmp-y-1369
                                                        syntmp-ids-1370)
                                                 (call-with-values
                                                   (lambda ()
                                                     (syntmp-cvt-1354
                                                       syntmp-x-1367
                                                       syntmp-n-1356
                                                       syntmp-ids-1370))
                                                   (lambda (syntmp-x-1371
                                                            syntmp-ids-1372)
                                                     (values
                                                       (cons syntmp-x-1371
                                                             syntmp-y-1369)
                                                       syntmp-ids-1372))))))
                                           syntmp-tmp-1366)
                                    ((lambda (syntmp-tmp-1373)
                                       (if syntmp-tmp-1373
                                         (apply (lambda ()
                                                  (values
                                                    '()
                                                    syntmp-ids-1357))
                                                syntmp-tmp-1373)
                                         ((lambda (syntmp-tmp-1374)
                                            (if syntmp-tmp-1374
                                              (apply (lambda (syntmp-x-1375)
                                                       (call-with-values
                                                         (lambda ()
                                                           (syntmp-cvt-1354
                                                             syntmp-x-1375
                                                             syntmp-n-1356
                                                             syntmp-ids-1357))
                                                         (lambda (syntmp-p-1377
                                                                  syntmp-ids-1378)
                                                           (values
                                                             (vector
                                                               'vector
                                                               syntmp-p-1377)
                                                             syntmp-ids-1378))))
                                                     syntmp-tmp-1374)
                                              ((lambda (syntmp-x-1379)
                                                 (values
                                                   (vector
                                                     'atom
                                                     (syntmp-strip-165
                                                       syntmp-p-1355
                                                       '(())))
                                                   syntmp-ids-1357))
                                               syntmp-tmp-1358)))
                                          (syntax-dispatch
                                            syntmp-tmp-1358
                                            '#(vector each-any)))))
                                     (syntax-dispatch
                                       syntmp-tmp-1358
                                       '()))))
                                (syntax-dispatch
                                  syntmp-tmp-1358
                                  '(any . any)))))
                           (syntax-dispatch
                             syntmp-tmp-1358
                             '(any any))))
                        syntmp-p-1355))))))
        (lambda (syntmp-e-1380
                 syntmp-r-1381
                 syntmp-w-1382
                 syntmp-s-1383)
          (let ((syntmp-e-1384
                  (syntmp-source-wrap-147
                    syntmp-e-1380
                    syntmp-w-1382
                    syntmp-s-1383)))
            ((lambda (syntmp-tmp-1385)
               ((lambda (syntmp-tmp-1386)
                  (if syntmp-tmp-1386
                    (apply (lambda (syntmp-_-1387
                                    syntmp-val-1388
                                    syntmp-key-1389
                                    syntmp-m-1390)
                             (if (andmap
                                   (lambda (syntmp-x-1391)
                                     (and (syntmp-id?-118 syntmp-x-1391)
                                          (not (syntmp-ellipsis?-163
                                                 syntmp-x-1391
                                                 syntmp-r-1381))))
                                   syntmp-key-1389)
                               (let ((syntmp-x-1393
                                       (syntmp-gen-var-166 (quote tmp))))
                                 (list (list 'lambda
                                             (list syntmp-x-1393)
                                             (syntmp-gen-syntax-case-1308
                                               syntmp-x-1393
                                               syntmp-key-1389
                                               syntmp-m-1390
                                               syntmp-r-1381))
                                       (syntmp-chi-154
                                         syntmp-val-1388
                                         syntmp-r-1381
                                         '(()))))
                               (syntax-error
                                 syntmp-e-1384
                                 "invalid literals list in")))
                           syntmp-tmp-1386)
                    (syntax-error syntmp-tmp-1385)))
                (syntax-dispatch
                  syntmp-tmp-1385
                  '(any any each-any . each-any))))
             syntmp-e-1384)))))
    (set! sc-expand
      (let ((syntmp-m-1396 (quote e))
            (syntmp-esew-1397 (quote (eval))))
        (lambda (syntmp-x-1398)
          (if (and (pair? syntmp-x-1398)
                   (equal? (car syntmp-x-1398) syntmp-noexpand-87))
            (cadr syntmp-x-1398)
            (syntmp-chi-top-153
              syntmp-x-1398
              '()
              '((top))
              syntmp-m-1396
              syntmp-esew-1397)))))
    (set! sc-expand3
      (let ((syntmp-m-1399 (quote e))
            (syntmp-esew-1400 (quote (eval))))
        (lambda (syntmp-x-1402 . syntmp-rest-1401)
          (if (and (pair? syntmp-x-1402)
                   (equal? (car syntmp-x-1402) syntmp-noexpand-87))
            (cadr syntmp-x-1402)
            (syntmp-chi-top-153
              syntmp-x-1402
              '()
              '((top))
              (if (null? syntmp-rest-1401)
                syntmp-m-1399
                (car syntmp-rest-1401))
              (if (or (null? syntmp-rest-1401)
                      (null? (cdr syntmp-rest-1401)))
                syntmp-esew-1400
                (cadr syntmp-rest-1401)))))))
    (set! identifier?
      (lambda (syntmp-x-1403)
        (syntmp-nonsymbol-id?-117 syntmp-x-1403)))
    (set! datum->syntax-object
      (lambda (syntmp-id-1404 syntmp-datum-1405)
        (syntmp-make-syntax-object-103
          syntmp-datum-1405
          (syntmp-syntax-object-wrap-106 syntmp-id-1404))))
    (set! syntax-object->datum
      (lambda (syntmp-x-1406)
        (syntmp-strip-165 syntmp-x-1406 (quote (())))))
    (set! generate-temporaries
      (lambda (syntmp-ls-1407)
        (begin
          (let ((syntmp-x-1408 syntmp-ls-1407))
            (if (not (list? syntmp-x-1408))
              (syntmp-error-hook-95
                'generate-temporaries
                "invalid argument"
                syntmp-x-1408)))
          (map (lambda (syntmp-x-1409)
                 (syntmp-wrap-146 (gensym) (quote ((top)))))
               syntmp-ls-1407))))
    (set! free-identifier=?
      (lambda (syntmp-x-1410 syntmp-y-1411)
        (begin
          (let ((syntmp-x-1412 syntmp-x-1410))
            (if (not (syntmp-nonsymbol-id?-117 syntmp-x-1412))
              (syntmp-error-hook-95
                'free-identifier=?
                "invalid argument"
                syntmp-x-1412)))
          (let ((syntmp-x-1413 syntmp-y-1411))
            (if (not (syntmp-nonsymbol-id?-117 syntmp-x-1413))
              (syntmp-error-hook-95
                'free-identifier=?
                "invalid argument"
                syntmp-x-1413)))
          (syntmp-free-id=?-141
            syntmp-x-1410
            syntmp-y-1411))))
    (set! bound-identifier=?
      (lambda (syntmp-x-1414 syntmp-y-1415)
        (begin
          (let ((syntmp-x-1416 syntmp-x-1414))
            (if (not (syntmp-nonsymbol-id?-117 syntmp-x-1416))
              (syntmp-error-hook-95
                'bound-identifier=?
                "invalid argument"
                syntmp-x-1416)))
          (let ((syntmp-x-1417 syntmp-y-1415))
            (if (not (syntmp-nonsymbol-id?-117 syntmp-x-1417))
              (syntmp-error-hook-95
                'bound-identifier=?
                "invalid argument"
                syntmp-x-1417)))
          (syntmp-bound-id=?-142
            syntmp-x-1414
            syntmp-y-1415))))
    (set! syntax-error
      (lambda (syntmp-object-1419 . syntmp-messages-1418)
        (begin
          (for-each
            (lambda (syntmp-x-1420)
              (let ((syntmp-x-1421 syntmp-x-1420))
                (if (not (string? syntmp-x-1421))
                  (syntmp-error-hook-95
                    'syntax-error
                    "invalid argument"
                    syntmp-x-1421))))
            syntmp-messages-1418)
          (let ((syntmp-message-1422
                  (if (null? syntmp-messages-1418)
                    "invalid syntax"
                    (apply string-append syntmp-messages-1418))))
            (syntmp-error-hook-95
              #f
              syntmp-message-1422
              (syntmp-strip-165
                syntmp-object-1419
                '(())))))))
    (set! install-global-transformer
      (lambda (syntmp-sym-1423 syntmp-v-1424)
        (begin
          (let ((syntmp-x-1425 syntmp-sym-1423))
            (if (not (symbol? syntmp-x-1425))
              (syntmp-error-hook-95
                'define-syntax
                "invalid argument"
                syntmp-x-1425)))
          (let ((syntmp-x-1426 syntmp-v-1424))
            (if (not (procedure? syntmp-x-1426))
              (syntmp-error-hook-95
                'define-syntax
                "invalid argument"
                syntmp-x-1426)))
          (syntmp-global-extend-116
            'macro
            syntmp-sym-1423
            syntmp-v-1424))))
    (letrec ((syntmp-match-1431
               (lambda (syntmp-e-1432
                        syntmp-p-1433
                        syntmp-w-1434
                        syntmp-r-1435)
                 (cond ((not syntmp-r-1435) #f)
                       ((eq? syntmp-p-1433 (quote _)) syntmp-r-1435)
                       ((eq? syntmp-p-1433 (quote any))
                        (cons (syntmp-wrap-146 syntmp-e-1432 syntmp-w-1434)
                              syntmp-r-1435))
                       ((syntmp-syntax-object?-104 syntmp-e-1432)
                        (syntmp-match*-1430
                          (let ((syntmp-e-1436
                                  (syntmp-syntax-object-expression-105
                                    syntmp-e-1432)))
                            (if (syntmp-annotation?-92 syntmp-e-1436)
                              (annotation-expression syntmp-e-1436)
                              syntmp-e-1436))
                          syntmp-p-1433
                          (syntmp-join-wraps-137
                            syntmp-w-1434
                            (syntmp-syntax-object-wrap-106 syntmp-e-1432))
                          syntmp-r-1435))
                       (else
                        (syntmp-match*-1430
                          (let ((syntmp-e-1437 syntmp-e-1432))
                            (if (syntmp-annotation?-92 syntmp-e-1437)
                              (annotation-expression syntmp-e-1437)
                              syntmp-e-1437))
                          syntmp-p-1433
                          syntmp-w-1434
                          syntmp-r-1435)))))
             (syntmp-match*-1430
               (lambda (syntmp-e-1438
                        syntmp-p-1439
                        syntmp-w-1440
                        syntmp-r-1441)
                 (cond ((null? syntmp-p-1439)
                        (and (null? syntmp-e-1438) syntmp-r-1441))
                       ((pair? syntmp-p-1439)
                        (and (pair? syntmp-e-1438)
                             (syntmp-match-1431
                               (car syntmp-e-1438)
                               (car syntmp-p-1439)
                               syntmp-w-1440
                               (syntmp-match-1431
                                 (cdr syntmp-e-1438)
                                 (cdr syntmp-p-1439)
                                 syntmp-w-1440
                                 syntmp-r-1441))))
                       ((eq? syntmp-p-1439 (quote each-any))
                        (let ((syntmp-l-1442
                                (syntmp-match-each-any-1428
                                  syntmp-e-1438
                                  syntmp-w-1440)))
                          (and syntmp-l-1442
                               (cons syntmp-l-1442 syntmp-r-1441))))
                       (else
                        (let ((syntmp-t-1443 (vector-ref syntmp-p-1439 0)))
                          (if (memv syntmp-t-1443 (quote (each)))
                            (if (null? syntmp-e-1438)
                              (syntmp-match-empty-1429
                                (vector-ref syntmp-p-1439 1)
                                syntmp-r-1441)
                              (let ((syntmp-l-1444
                                      (syntmp-match-each-1427
                                        syntmp-e-1438
                                        (vector-ref syntmp-p-1439 1)
                                        syntmp-w-1440)))
                                (and syntmp-l-1444
                                     (let syntmp-collect-1445 ((syntmp-l-1446
                                                                 syntmp-l-1444))
                                       (if (null? (car syntmp-l-1446))
                                         syntmp-r-1441
                                         (cons (map car syntmp-l-1446)
                                               (syntmp-collect-1445
                                                 (map cdr syntmp-l-1446))))))))
                            (if (memv syntmp-t-1443 (quote (free-id)))
                              (and (syntmp-id?-118 syntmp-e-1438)
                                   (syntmp-free-id=?-141
                                     (syntmp-wrap-146
                                       syntmp-e-1438
                                       syntmp-w-1440)
                                     (vector-ref syntmp-p-1439 1))
                                   syntmp-r-1441)
                              (if (memv syntmp-t-1443 (quote (atom)))
                                (and (equal?
                                       (vector-ref syntmp-p-1439 1)
                                       (syntmp-strip-165
                                         syntmp-e-1438
                                         syntmp-w-1440))
                                     syntmp-r-1441)
                                (if (memv syntmp-t-1443 (quote (vector)))
                                  (and (vector? syntmp-e-1438)
                                       (syntmp-match-1431
                                         (vector->list syntmp-e-1438)
                                         (vector-ref syntmp-p-1439 1)
                                         syntmp-w-1440
                                         syntmp-r-1441)))))))))))
             (syntmp-match-empty-1429
               (lambda (syntmp-p-1447 syntmp-r-1448)
                 (cond ((null? syntmp-p-1447) syntmp-r-1448)
                       ((eq? syntmp-p-1447 (quote _)) syntmp-r-1448)
                       ((eq? syntmp-p-1447 (quote any))
                        (cons (quote ()) syntmp-r-1448))
                       ((pair? syntmp-p-1447)
                        (syntmp-match-empty-1429
                          (car syntmp-p-1447)
                          (syntmp-match-empty-1429
                            (cdr syntmp-p-1447)
                            syntmp-r-1448)))
                       ((eq? syntmp-p-1447 (quote each-any))
                        (cons (quote ()) syntmp-r-1448))
                       (else
                        (let ((syntmp-t-1449 (vector-ref syntmp-p-1447 0)))
                          (if (memv syntmp-t-1449 (quote (each)))
                            (syntmp-match-empty-1429
                              (vector-ref syntmp-p-1447 1)
                              syntmp-r-1448)
                            (if (memv syntmp-t-1449 (quote (free-id atom)))
                              syntmp-r-1448
                              (if (memv syntmp-t-1449 (quote (vector)))
                                (syntmp-match-empty-1429
                                  (vector-ref syntmp-p-1447 1)
                                  syntmp-r-1448)))))))))
             (syntmp-match-each-any-1428
               (lambda (syntmp-e-1450 syntmp-w-1451)
                 (cond ((syntmp-annotation?-92 syntmp-e-1450)
                        (syntmp-match-each-any-1428
                          (annotation-expression syntmp-e-1450)
                          syntmp-w-1451))
                       ((pair? syntmp-e-1450)
                        (let ((syntmp-l-1452
                                (syntmp-match-each-any-1428
                                  (cdr syntmp-e-1450)
                                  syntmp-w-1451)))
                          (and syntmp-l-1452
                               (cons (syntmp-wrap-146
                                       (car syntmp-e-1450)
                                       syntmp-w-1451)
                                     syntmp-l-1452))))
                       ((null? syntmp-e-1450) (quote ()))
                       ((syntmp-syntax-object?-104 syntmp-e-1450)
                        (syntmp-match-each-any-1428
                          (syntmp-syntax-object-expression-105
                            syntmp-e-1450)
                          (syntmp-join-wraps-137
                            syntmp-w-1451
                            (syntmp-syntax-object-wrap-106 syntmp-e-1450))))
                       (else #f))))
             (syntmp-match-each-1427
               (lambda (syntmp-e-1453 syntmp-p-1454 syntmp-w-1455)
                 (cond ((syntmp-annotation?-92 syntmp-e-1453)
                        (syntmp-match-each-1427
                          (annotation-expression syntmp-e-1453)
                          syntmp-p-1454
                          syntmp-w-1455))
                       ((pair? syntmp-e-1453)
                        (let ((syntmp-first-1456
                                (syntmp-match-1431
                                  (car syntmp-e-1453)
                                  syntmp-p-1454
                                  syntmp-w-1455
                                  '())))
                          (and syntmp-first-1456
                               (let ((syntmp-rest-1457
                                       (syntmp-match-each-1427
                                         (cdr syntmp-e-1453)
                                         syntmp-p-1454
                                         syntmp-w-1455)))
                                 (and syntmp-rest-1457
                                      (cons syntmp-first-1456
                                            syntmp-rest-1457))))))
                       ((null? syntmp-e-1453) (quote ()))
                       ((syntmp-syntax-object?-104 syntmp-e-1453)
                        (syntmp-match-each-1427
                          (syntmp-syntax-object-expression-105
                            syntmp-e-1453)
                          syntmp-p-1454
                          (syntmp-join-wraps-137
                            syntmp-w-1455
                            (syntmp-syntax-object-wrap-106 syntmp-e-1453))))
                       (else #f)))))
      (begin
        (set! syntax-dispatch
          (lambda (syntmp-e-1458 syntmp-p-1459)
            (cond ((eq? syntmp-p-1459 (quote any))
                   (list syntmp-e-1458))
                  ((eq? syntmp-p-1459 (quote _)) (quote ()))
                  ((syntmp-syntax-object?-104 syntmp-e-1458)
                   (syntmp-match*-1430
                     (let ((syntmp-e-1460
                             (syntmp-syntax-object-expression-105
                               syntmp-e-1458)))
                       (if (syntmp-annotation?-92 syntmp-e-1460)
                         (annotation-expression syntmp-e-1460)
                         syntmp-e-1460))
                     syntmp-p-1459
                     (syntmp-syntax-object-wrap-106 syntmp-e-1458)
                     '()))
                  (else
                   (syntmp-match*-1430
                     (let ((syntmp-e-1461 syntmp-e-1458))
                       (if (syntmp-annotation?-92 syntmp-e-1461)
                         (annotation-expression syntmp-e-1461)
                         syntmp-e-1461))
                     syntmp-p-1459
                     '(())
                     '())))))
        (set! sc-chi syntmp-chi-154)))))

(install-global-transformer
  'with-syntax
  (lambda (syntmp-x-1462)
    ((lambda (syntmp-tmp-1463)
       ((lambda (syntmp-tmp-1464)
          (if syntmp-tmp-1464
            (apply (lambda (syntmp-_-1465 syntmp-e1-1466 syntmp-e2-1467)
                     (cons '#(syntax-object
                              let
                              ((top)
                               #(ribcage
                                 #(_ e1 e2)
                                 #((top) (top) (top))
                                 #("i" "i" "i"))
                               #(ribcage () () ())
                               #(ribcage #(x) #((top)) #("i"))))
                           (cons '()
                                 (cons syntmp-e1-1466 syntmp-e2-1467))))
                   syntmp-tmp-1464)
            ((lambda (syntmp-tmp-1469)
               (if syntmp-tmp-1469
                 (apply (lambda (syntmp-_-1470
                                 syntmp-out-1471
                                 syntmp-in-1472
                                 syntmp-e1-1473
                                 syntmp-e2-1474)
                          (list '#(syntax-object
                                   syntax-case
                                   ((top)
                                    #(ribcage
                                      #(_ out in e1 e2)
                                      #((top) (top) (top) (top) (top))
                                      #("i" "i" "i" "i" "i"))
                                    #(ribcage () () ())
                                    #(ribcage #(x) #((top)) #("i"))))
                                syntmp-in-1472
                                '()
                                (list syntmp-out-1471
                                      (cons '#(syntax-object
                                               let
                                               ((top)
                                                #(ribcage
                                                  #(_ out in e1 e2)
                                                  #((top)
                                                    (top)
                                                    (top)
                                                    (top)
                                                    (top))
                                                  #("i" "i" "i" "i" "i"))
                                                #(ribcage () () ())
                                                #(ribcage
                                                  #(x)
                                                  #((top))
                                                  #("i"))))
                                            (cons '()
                                                  (cons syntmp-e1-1473
                                                        syntmp-e2-1474))))))
                        syntmp-tmp-1469)
                 ((lambda (syntmp-tmp-1476)
                    (if syntmp-tmp-1476
                      (apply (lambda (syntmp-_-1477
                                      syntmp-out-1478
                                      syntmp-in-1479
                                      syntmp-e1-1480
                                      syntmp-e2-1481)
                               (list '#(syntax-object
                                        syntax-case
                                        ((top)
                                         #(ribcage
                                           #(_ out in e1 e2)
                                           #((top) (top) (top) (top) (top))
                                           #("i" "i" "i" "i" "i"))
                                         #(ribcage () () ())
                                         #(ribcage #(x) #((top)) #("i"))))
                                     (cons '#(syntax-object
                                              list
                                              ((top)
                                               #(ribcage
                                                 #(_ out in e1 e2)
                                                 #((top)
                                                   (top)
                                                   (top)
                                                   (top)
                                                   (top))
                                                 #("i" "i" "i" "i" "i"))
                                               #(ribcage () () ())
                                               #(ribcage
                                                 #(x)
                                                 #((top))
                                                 #("i"))))
                                           syntmp-in-1479)
                                     '()
                                     (list syntmp-out-1478
                                           (cons '#(syntax-object
                                                    let
                                                    ((top)
                                                     #(ribcage
                                                       #(_ out in e1 e2)
                                                       #((top)
                                                         (top)
                                                         (top)
                                                         (top)
                                                         (top))
                                                       #("i" "i" "i" "i" "i"))
                                                     #(ribcage () () ())
                                                     #(ribcage
                                                       #(x)
                                                       #((top))
                                                       #("i"))))
                                                 (cons '()
                                                       (cons syntmp-e1-1480
                                                             syntmp-e2-1481))))))
                             syntmp-tmp-1476)
                      (syntax-error syntmp-tmp-1463)))
                  (syntax-dispatch
                    syntmp-tmp-1463
                    '(any #(each (any any)) any . each-any)))))
             (syntax-dispatch
               syntmp-tmp-1463
               '(any ((any any)) any . each-any)))))
        (syntax-dispatch
          syntmp-tmp-1463
          '(any () any . each-any))))
     syntmp-x-1462)))

(install-global-transformer
  'syntax-rules
  (lambda (syntmp-xx-1503)
    (letrec ((syntmp-expand-syntax-rules-1504
               (lambda (syntmp-dots-1505
                        syntmp-keys-1506
                        syntmp-docstrings-1507
                        syntmp-clauses-1508)
                 ((lambda (syntmp-tmp-1509)
                    ((lambda (syntmp-tmp-1510)
                       (if syntmp-tmp-1510
                         (apply (lambda (syntmp-k-1511
                                         syntmp-docstring-1512
                                         syntmp-keyword-1513
                                         syntmp-pattern-1514
                                         syntmp-template-1515)
                                  ((lambda (syntmp-tmp-1516)
                                     ((lambda (syntmp-form-1517)
                                        (if syntmp-dots-1505
                                          ((lambda (syntmp-tmp-1518)
                                             ((lambda (syntmp-dots-1519)
                                                (list '#(syntax-object
                                                         with-ellipsis
                                                         ((top)
                                                          #(ribcage () () ())
                                                          #(ribcage
                                                            #(dots)
                                                            #((top))
                                                            #("i"))
                                                          #(ribcage () () ())
                                                          #(ribcage
                                                            #(form)
                                                            #((top))
                                                            #("i"))
                                                          #(ribcage () () ())
                                                          #(ribcage () () ())
                                                          #(ribcage
                                                            #(k
                                                              docstring
                                                              keyword
                                                              pattern
                                                              template)
                                                            #((top)
                                                              (top)
                                                              (top)
                                                              (top)
                                                              (top))
                                                            #("i"
                                                              "i"
                                                              "i"
                                                              "i"
                                                              "i"))
                                                          #(ribcage () () ())
                                                          #(ribcage () () ())
                                                          #(ribcage
                                                            #(dots
                                                              keys
                                                              docstrings
                                                              clauses)
                                                            #((top)
                                                              (top)
                                                              (top)
                                                              (top))
                                                            #("i" "i" "i" "i"))
                                                          #(ribcage
                                                            (expand-syntax-rules)
                                                            ((top))
                                                            ("i"))
                                                          #(ribcage
                                                            #(xx)
                                                            #((top))
                                                            #("i"))))
                                                      syntmp-dots-1519
                                                      syntmp-form-1517))
                                              syntmp-tmp-1518))
                                           syntmp-dots-1505)
                                          syntmp-form-1517))
                                      syntmp-tmp-1516))
                                   (cons '#(syntax-object
                                            lambda
                                            ((top)
                                             #(ribcage () () ())
                                             #(ribcage () () ())
                                             #(ribcage
                                               #(k
                                                 docstring
                                                 keyword
                                                 pattern
                                                 template)
                                               #((top) (top) (top) (top) (top))
                                               #("i" "i" "i" "i" "i"))
                                             #(ribcage () () ())
                                             #(ribcage () () ())
                                             #(ribcage
                                               #(dots keys docstrings clauses)
                                               #((top) (top) (top) (top))
                                               #("i" "i" "i" "i"))
                                             #(ribcage
                                               (expand-syntax-rules)
                                               ((top))
                                               ("i"))
                                             #(ribcage #(xx) #((top)) #("i"))))
                                         (cons '(#(syntax-object
                                                   x
                                                   ((top)
                                                    #(ribcage () () ())
                                                    #(ribcage () () ())
                                                    #(ribcage
                                                      #(k
                                                        docstring
                                                        keyword
                                                        pattern
                                                        template)
                                                      #((top)
                                                        (top)
                                                        (top)
                                                        (top)
                                                        (top))
                                                      #("i" "i" "i" "i" "i"))
                                                    #(ribcage () () ())
                                                    #(ribcage () () ())
                                                    #(ribcage
                                                      #(dots
                                                        keys
                                                        docstrings
                                                        clauses)
                                                      #((top)
                                                        (top)
                                                        (top)
                                                        (top))
                                                      #("i" "i" "i" "i"))
                                                    #(ribcage
                                                      (expand-syntax-rules)
                                                      ((top))
                                                      ("i"))
                                                    #(ribcage
                                                      #(xx)
                                                      #((top))
                                                      #("i")))))
                                               (append
                                                 syntmp-docstring-1512
                                                 (list (cons '#(syntax-object
                                                                syntax-case
                                                                ((top)
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(k
                                                                     docstring
                                                                     keyword
                                                                     pattern
                                                                     template)
                                                                   #((top)
                                                                     (top)
                                                                     (top)
                                                                     (top)
                                                                     (top))
                                                                   #("i"
                                                                     "i"
                                                                     "i"
                                                                     "i"
                                                                     "i"))
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(dots
                                                                     keys
                                                                     docstrings
                                                                     clauses)
                                                                   #((top)
                                                                     (top)
                                                                     (top)
                                                                     (top))
                                                                   #("i"
                                                                     "i"
                                                                     "i"
                                                                     "i"))
                                                                 #(ribcage
                                                                   (expand-syntax-rules)
                                                                   ((top))
                                                                   ("i"))
                                                                 #(ribcage
                                                                   #(xx)
                                                                   #((top))
                                                                   #("i"))))
                                                             (cons '#(syntax-object
                                                                      x
                                                                      ((top)
                                                                       #(ribcage
                                                                         ()
                                                                         ()
                                                                         ())
                                                                       #(ribcage
                                                                         ()
                                                                         ()
                                                                         ())
                                                                       #(ribcage
                                                                         #(k
                                                                           docstring
                                                                           keyword
                                                                           pattern
                                                                           template)
                                                                         #((top)
                                                                           (top)
                                                                           (top)
                                                                           (top)
                                                                           (top))
                                                                         #("i"
                                                                           "i"
                                                                           "i"
                                                                           "i"
                                                                           "i"))
                                                                       #(ribcage
                                                                         ()
                                                                         ()
                                                                         ())
                                                                       #(ribcage
                                                                         ()
                                                                         ()
                                                                         ())
                                                                       #(ribcage
                                                                         #(dots
                                                                           keys
                                                                           docstrings
                                                                           clauses)
                                                                         #((top)
                                                                           (top)
                                                                           (top)
                                                                           (top))
                                                                         #("i"
                                                                           "i"
                                                                           "i"
                                                                           "i"))
                                                                       #(ribcage
                                                                         (expand-syntax-rules)
                                                                         ((top))
                                                                         ("i"))
                                                                       #(ribcage
                                                                         #(xx)
                                                                         #((top))
                                                                         #("i"))))
                                                                   (cons syntmp-k-1511
                                                                         (map (lambda (syntmp-tmp-1522
                                                                                       syntmp-tmp-1521)
                                                                                (list (cons '#(syntax-object
                                                                                               dummy
                                                                                               ((top)
                                                                                                #(ribcage
                                                                                                  ()
                                                                                                  ()
                                                                                                  ())
                                                                                                #(ribcage
                                                                                                  ()
                                                                                                  ()
                                                                                                  ())
                                                                                                #(ribcage
                                                                                                  #(k
                                                                                                    docstring
                                                                                                    keyword
                                                                                                    pattern
                                                                                                    template)
                                                                                                  #((top)
                                                                                                    (top)
                                                                                                    (top)
                                                                                                    (top)
                                                                                                    (top))
                                                                                                  #("i"
                                                                                                    "i"
                                                                                                    "i"
                                                                                                    "i"
                                                                                                    "i"))
                                                                                                #(ribcage
                                                                                                  ()
                                                                                                  ()
                                                                                                  ())
                                                                                                #(ribcage
                                                                                                  ()
                                                                                                  ()
                                                                                                  ())
                                                                                                #(ribcage
                                                                                                  #(dots
                                                                                                    keys
                                                                                                    docstrings
                                                                                                    clauses)
                                                                                                  #((top)
                                                                                                    (top)
                                                                                                    (top)
                                                                                                    (top))
                                                                                                  #("i"
                                                                                                    "i"
                                                                                                    "i"
                                                                                                    "i"))
                                                                                                #(ribcage
                                                                                                  (expand-syntax-rules)
                                                                                                  ((top))
                                                                                                  ("i"))
                                                                                                #(ribcage
                                                                                                  #(xx)
                                                                                                  #((top))
                                                                                                  #("i"))))
                                                                                            syntmp-tmp-1521)
                                                                                      (list '#(syntax-object
                                                                                               syntax
                                                                                               ((top)
                                                                                                #(ribcage
                                                                                                  ()
                                                                                                  ()
                                                                                                  ())
                                                                                                #(ribcage
                                                                                                  ()
                                                                                                  ()
                                                                                                  ())
                                                                                                #(ribcage
                                                                                                  #(k
                                                                                                    docstring
                                                                                                    keyword
                                                                                                    pattern
                                                                                                    template)
                                                                                                  #((top)
                                                                                                    (top)
                                                                                                    (top)
                                                                                                    (top)
                                                                                                    (top))
                                                                                                  #("i"
                                                                                                    "i"
                                                                                                    "i"
                                                                                                    "i"
                                                                                                    "i"))
                                                                                                #(ribcage
                                                                                                  ()
                                                                                                  ()
                                                                                                  ())
                                                                                                #(ribcage
                                                                                                  ()
                                                                                                  ()
                                                                                                  ())
                                                                                                #(ribcage
                                                                                                  #(dots
                                                                                                    keys
                                                                                                    docstrings
                                                                                                    clauses)
                                                                                                  #((top)
                                                                                                    (top)
                                                                                                    (top)
                                                                                                    (top))
                                                                                                  #("i"
                                                                                                    "i"
                                                                                                    "i"
                                                                                                    "i"))
                                                                                                #(ribcage
                                                                                                  (expand-syntax-rules)
                                                                                                  ((top))
                                                                                                  ("i"))
                                                                                                #(ribcage
                                                                                                  #(xx)
                                                                                                  #((top))
                                                                                                  #("i"))))
                                                                                            syntmp-tmp-1522)))
                                                                              syntmp-template-1515
                                                                              syntmp-pattern-1514))))))))))
                                syntmp-tmp-1510)
                         (syntax-error syntmp-tmp-1509)))
                     (syntax-dispatch
                       syntmp-tmp-1509
                       '(each-any each-any #(each ((any . any) any))))))
                  (list syntmp-keys-1506
                        syntmp-docstrings-1507
                        syntmp-clauses-1508)))))
      ((lambda (syntmp-tmp-1524)
         ((lambda (syntmp-tmp-1525)
            (if syntmp-tmp-1525
              (apply (lambda (syntmp-_-1526
                              syntmp-k-1527
                              syntmp-keyword-1528
                              syntmp-pattern-1529
                              syntmp-template-1530)
                       (syntmp-expand-syntax-rules-1504
                         #f
                         syntmp-k-1527
                         '()
                         (map (lambda (syntmp-tmp-1534
                                       syntmp-tmp-1533
                                       syntmp-tmp-1532)
                                (list (cons syntmp-tmp-1532 syntmp-tmp-1533)
                                      syntmp-tmp-1534))
                              syntmp-template-1530
                              syntmp-pattern-1529
                              syntmp-keyword-1528)))
                     syntmp-tmp-1525)
              ((lambda (syntmp-tmp-1535)
                 (if (if syntmp-tmp-1535
                       (apply (lambda (syntmp-_-1536
                                       syntmp-k-1537
                                       syntmp-docstring-1538
                                       syntmp-keyword-1539
                                       syntmp-pattern-1540
                                       syntmp-template-1541)
                                (string?
                                  (syntax-object->datum
                                    syntmp-docstring-1538)))
                              syntmp-tmp-1535)
                       #f)
                   (apply (lambda (syntmp-_-1542
                                   syntmp-k-1543
                                   syntmp-docstring-1544
                                   syntmp-keyword-1545
                                   syntmp-pattern-1546
                                   syntmp-template-1547)
                            (syntmp-expand-syntax-rules-1504
                              #f
                              syntmp-k-1543
                              (list syntmp-docstring-1544)
                              (map (lambda (syntmp-tmp-1551
                                            syntmp-tmp-1550
                                            syntmp-tmp-1549)
                                     (list (cons syntmp-tmp-1549
                                                 syntmp-tmp-1550)
                                           syntmp-tmp-1551))
                                   syntmp-template-1547
                                   syntmp-pattern-1546
                                   syntmp-keyword-1545)))
                          syntmp-tmp-1535)
                   ((lambda (syntmp-tmp-1552)
                      (if (if syntmp-tmp-1552
                            (apply (lambda (syntmp-_-1553
                                            syntmp-dots-1554
                                            syntmp-k-1555
                                            syntmp-keyword-1556
                                            syntmp-pattern-1557
                                            syntmp-template-1558)
                                     (identifier? syntmp-dots-1554))
                                   syntmp-tmp-1552)
                            #f)
                        (apply (lambda (syntmp-_-1559
                                        syntmp-dots-1560
                                        syntmp-k-1561
                                        syntmp-keyword-1562
                                        syntmp-pattern-1563
                                        syntmp-template-1564)
                                 (syntmp-expand-syntax-rules-1504
                                   syntmp-dots-1560
                                   syntmp-k-1561
                                   '()
                                   (map (lambda (syntmp-tmp-1568
                                                 syntmp-tmp-1567
                                                 syntmp-tmp-1566)
                                          (list (cons syntmp-tmp-1566
                                                      syntmp-tmp-1567)
                                                syntmp-tmp-1568))
                                        syntmp-template-1564
                                        syntmp-pattern-1563
                                        syntmp-keyword-1562)))
                               syntmp-tmp-1552)
                        ((lambda (syntmp-tmp-1569)
                           (if (if syntmp-tmp-1569
                                 (apply (lambda (syntmp-_-1570
                                                 syntmp-dots-1571
                                                 syntmp-k-1572
                                                 syntmp-docstring-1573
                                                 syntmp-keyword-1574
                                                 syntmp-pattern-1575
                                                 syntmp-template-1576)
                                          (and (identifier? syntmp-dots-1571)
                                               (string?
                                                 (syntax-object->datum
                                                   syntmp-docstring-1573))))
                                        syntmp-tmp-1569)
                                 #f)
                             (apply (lambda (syntmp-_-1577
                                             syntmp-dots-1578
                                             syntmp-k-1579
                                             syntmp-docstring-1580
                                             syntmp-keyword-1581
                                             syntmp-pattern-1582
                                             syntmp-template-1583)
                                      (syntmp-expand-syntax-rules-1504
                                        syntmp-dots-1578
                                        syntmp-k-1579
                                        (list syntmp-docstring-1580)
                                        (map (lambda (syntmp-tmp-1587
                                                      syntmp-tmp-1586
                                                      syntmp-tmp-1585)
                                               (list (cons syntmp-tmp-1585
                                                           syntmp-tmp-1586)
                                                     syntmp-tmp-1587))
                                             syntmp-template-1583
                                             syntmp-pattern-1582
                                             syntmp-keyword-1581)))
                                    syntmp-tmp-1569)
                             (syntax-error syntmp-tmp-1524)))
                         (syntax-dispatch
                           syntmp-tmp-1524
                           '(any any
                                 each-any
                                 any
                                 .
                                 #(each ((any . any) any)))))))
                    (syntax-dispatch
                      syntmp-tmp-1524
                      '(any any each-any . #(each ((any . any) any)))))))
               (syntax-dispatch
                 syntmp-tmp-1524
                 '(any each-any any . #(each ((any . any) any)))))))
          (syntax-dispatch
            syntmp-tmp-1524
            '(any each-any . #(each ((any . any) any))))))
       syntmp-xx-1503))))

(install-global-transformer
  'define-syntax-rule
  (lambda (syntmp-x-1667)
    ((lambda (syntmp-tmp-1668)
       ((lambda (syntmp-tmp-1669)
          (if syntmp-tmp-1669
            (apply (lambda (syntmp-_-1670
                            syntmp-name-1671
                            syntmp-pattern-1672
                            syntmp-template-1673)
                     (list '#(syntax-object
                              define-syntax
                              ((top)
                               #(ribcage
                                 #(_ name pattern template)
                                 #((top) (top) (top) (top))
                                 #("i" "i" "i" "i"))
                               #(ribcage () () ())
                               #(ribcage #(x) #((top)) #("i"))))
                           syntmp-name-1671
                           (list '#(syntax-object
                                    syntax-rules
                                    ((top)
                                     #(ribcage
                                       #(_ name pattern template)
                                       #((top) (top) (top) (top))
                                       #("i" "i" "i" "i"))
                                     #(ribcage () () ())
                                     #(ribcage #(x) #((top)) #("i"))))
                                 '()
                                 (list (cons syntmp-_-1670 syntmp-pattern-1672)
                                       syntmp-template-1673))))
                   syntmp-tmp-1669)
            ((lambda (syntmp-tmp-1674)
               (if (if syntmp-tmp-1674
                     (apply (lambda (syntmp-_-1675
                                     syntmp-name-1676
                                     syntmp-pattern-1677
                                     syntmp-docstring-1678
                                     syntmp-template-1679)
                              (string?
                                (syntax-object->datum syntmp-docstring-1678)))
                            syntmp-tmp-1674)
                     #f)
                 (apply (lambda (syntmp-_-1680
                                 syntmp-name-1681
                                 syntmp-pattern-1682
                                 syntmp-docstring-1683
                                 syntmp-template-1684)
                          (list '#(syntax-object
                                   define-syntax
                                   ((top)
                                    #(ribcage
                                      #(_ name pattern docstring template)
                                      #((top) (top) (top) (top) (top))
                                      #("i" "i" "i" "i" "i"))
                                    #(ribcage () () ())
                                    #(ribcage #(x) #((top)) #("i"))))
                                syntmp-name-1681
                                (list '#(syntax-object
                                         syntax-rules
                                         ((top)
                                          #(ribcage
                                            #(_
                                              name
                                              pattern
                                              docstring
                                              template)
                                            #((top) (top) (top) (top) (top))
                                            #("i" "i" "i" "i" "i"))
                                          #(ribcage () () ())
                                          #(ribcage #(x) #((top)) #("i"))))
                                      '()
                                      syntmp-docstring-1683
                                      (list (cons syntmp-_-1680
                                                  syntmp-pattern-1682)
                                            syntmp-template-1684))))
                        syntmp-tmp-1674)
                 (syntax-error syntmp-tmp-1668)))
             (syntax-dispatch
               syntmp-tmp-1668
               '(any (any . any) any any)))))
        (syntax-dispatch
          syntmp-tmp-1668
          '(any (any . any) any))))
     syntmp-x-1667)))

(install-global-transformer
  'let*
  (lambda (syntmp-x-1703)
    ((lambda (syntmp-tmp-1704)
       ((lambda (syntmp-tmp-1705)
          (if (if syntmp-tmp-1705
                (apply (lambda (syntmp-let*-1706
                                syntmp-x-1707
                                syntmp-v-1708
                                syntmp-e1-1709
                                syntmp-e2-1710)
                         (andmap identifier? syntmp-x-1707))
                       syntmp-tmp-1705)
                #f)
            (apply (lambda (syntmp-let*-1712
                            syntmp-x-1713
                            syntmp-v-1714
                            syntmp-e1-1715
                            syntmp-e2-1716)
                     (let syntmp-f-1717 ((syntmp-bindings-1718
                                           (map list
                                                syntmp-x-1713
                                                syntmp-v-1714)))
                       (if (null? syntmp-bindings-1718)
                         (cons '#(syntax-object
                                  let
                                  ((top)
                                   #(ribcage () () ())
                                   #(ribcage
                                     #(f bindings)
                                     #((top) (top))
                                     #("i" "i"))
                                   #(ribcage
                                     #(let* x v e1 e2)
                                     #((top) (top) (top) (top) (top))
                                     #("i" "i" "i" "i" "i"))
                                   #(ribcage () () ())
                                   #(ribcage #(x) #((top)) #("i"))))
                               (cons '()
                                     (cons syntmp-e1-1715 syntmp-e2-1716)))
                         ((lambda (syntmp-tmp-1722)
                            ((lambda (syntmp-tmp-1723)
                               (if syntmp-tmp-1723
                                 (apply (lambda (syntmp-body-1724
                                                 syntmp-binding-1725)
                                          (list '#(syntax-object
                                                   let
                                                   ((top)
                                                    #(ribcage () () ())
                                                    #(ribcage
                                                      #(body binding)
                                                      #((top) (top))
                                                      #("i" "i"))
                                                    #(ribcage () () ())
                                                    #(ribcage
                                                      #(f bindings)
                                                      #((top) (top))
                                                      #("i" "i"))
                                                    #(ribcage
                                                      #(let* x v e1 e2)
                                                      #((top)
                                                        (top)
                                                        (top)
                                                        (top)
                                                        (top))
                                                      #("i" "i" "i" "i" "i"))
                                                    #(ribcage () () ())
                                                    #(ribcage
                                                      #(x)
                                                      #((top))
                                                      #("i"))))
                                                (list syntmp-binding-1725)
                                                syntmp-body-1724))
                                        syntmp-tmp-1723)
                                 (syntax-error syntmp-tmp-1722)))
                             (syntax-dispatch
                               syntmp-tmp-1722
                               '(any any))))
                          (list (syntmp-f-1717 (cdr syntmp-bindings-1718))
                                (car syntmp-bindings-1718))))))
                   syntmp-tmp-1705)
            (syntax-error syntmp-tmp-1704)))
        (syntax-dispatch
          syntmp-tmp-1704
          '(any #(each (any any)) any . each-any))))
     syntmp-x-1703)))

(install-global-transformer
  'do
  (lambda (syntmp-orig-x-1745)
    ((lambda (syntmp-tmp-1746)
       ((lambda (syntmp-tmp-1747)
          (if syntmp-tmp-1747
            (apply (lambda (syntmp-_-1748
                            syntmp-var-1749
                            syntmp-init-1750
                            syntmp-step-1751
                            syntmp-e0-1752
                            syntmp-e1-1753
                            syntmp-c-1754)
                     ((lambda (syntmp-tmp-1755)
                        ((lambda (syntmp-tmp-1756)
                           (if syntmp-tmp-1756
                             (apply (lambda (syntmp-step-1757)
                                      ((lambda (syntmp-tmp-1758)
                                         ((lambda (syntmp-tmp-1759)
                                            (if syntmp-tmp-1759
                                              (apply (lambda ()
                                                       (list '#(syntax-object
                                                                let
                                                                ((top)
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(step)
                                                                   #((top))
                                                                   #("i"))
                                                                 #(ribcage
                                                                   #(_
                                                                     var
                                                                     init
                                                                     step
                                                                     e0
                                                                     e1
                                                                     c)
                                                                   #((top)
                                                                     (top)
                                                                     (top)
                                                                     (top)
                                                                     (top)
                                                                     (top)
                                                                     (top))
                                                                   #("i"
                                                                     "i"
                                                                     "i"
                                                                     "i"
                                                                     "i"
                                                                     "i"
                                                                     "i"))
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(orig-x)
                                                                   #((top))
                                                                   #("i"))))
                                                             '#(syntax-object
                                                                doloop
                                                                ((top)
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(step)
                                                                   #((top))
                                                                   #("i"))
                                                                 #(ribcage
                                                                   #(_
                                                                     var
                                                                     init
                                                                     step
                                                                     e0
                                                                     e1
                                                                     c)
                                                                   #((top)
                                                                     (top)
                                                                     (top)
                                                                     (top)
                                                                     (top)
                                                                     (top)
                                                                     (top))
                                                                   #("i"
                                                                     "i"
                                                                     "i"
                                                                     "i"
                                                                     "i"
                                                                     "i"
                                                                     "i"))
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(orig-x)
                                                                   #((top))
                                                                   #("i"))))
                                                             (map list
                                                                  syntmp-var-1749
                                                                  syntmp-init-1750)
                                                             (list '#(syntax-object
                                                                      if
                                                                      ((top)
                                                                       #(ribcage
                                                                         ()
                                                                         ()
                                                                         ())
                                                                       #(ribcage
                                                                         #(step)
                                                                         #((top))
                                                                         #("i"))
                                                                       #(ribcage
                                                                         #(_
                                                                           var
                                                                           init
                                                                           step
                                                                           e0
                                                                           e1
                                                                           c)
                                                                         #((top)
                                                                           (top)
                                                                           (top)
                                                                           (top)
                                                                           (top)
                                                                           (top)
                                                                           (top))
                                                                         #("i"
                                                                           "i"
                                                                           "i"
                                                                           "i"
                                                                           "i"
                                                                           "i"
                                                                           "i"))
                                                                       #(ribcage
                                                                         ()
                                                                         ()
                                                                         ())
                                                                       #(ribcage
                                                                         #(orig-x)
                                                                         #((top))
                                                                         #("i"))))
                                                                   (list '#(syntax-object
                                                                            not
                                                                            ((top)
                                                                             #(ribcage
                                                                               ()
                                                                               ()
                                                                               ())
                                                                             #(ribcage
                                                                               #(step)
                                                                               #((top))
                                                                               #("i"))
                                                                             #(ribcage
                                                                               #(_
                                                                                 var
                                                                                 init
                                                                                 step
                                                                                 e0
                                                                                 e1
                                                                                 c)
                                                                               #((top)
                                                                                 (top)
                                                                                 (top)
                                                                                 (top)
                                                                                 (top)
                                                                                 (top)
                                                                                 (top))
                                                                               #("i"
                                                                                 "i"
                                                                                 "i"
                                                                                 "i"
                                                                                 "i"
                                                                                 "i"
                                                                                 "i"))
                                                                             #(ribcage
                                                                               ()
                                                                               ()
                                                                               ())
                                                                             #(ribcage
                                                                               #(orig-x)
                                                                               #((top))
                                                                               #("i"))))
                                                                         syntmp-e0-1752)
                                                                   (cons '#(syntax-object
                                                                            begin
                                                                            ((top)
                                                                             #(ribcage
                                                                               ()
                                                                               ()
                                                                               ())
                                                                             #(ribcage
                                                                               #(step)
                                                                               #((top))
                                                                               #("i"))
                                                                             #(ribcage
                                                                               #(_
                                                                                 var
                                                                                 init
                                                                                 step
                                                                                 e0
                                                                                 e1
                                                                                 c)
                                                                               #((top)
                                                                                 (top)
                                                                                 (top)
                                                                                 (top)
                                                                                 (top)
                                                                                 (top)
                                                                                 (top))
                                                                               #("i"
                                                                                 "i"
                                                                                 "i"
                                                                                 "i"
                                                                                 "i"
                                                                                 "i"
                                                                                 "i"))
                                                                             #(ribcage
                                                                               ()
                                                                               ()
                                                                               ())
                                                                             #(ribcage
                                                                               #(orig-x)
                                                                               #((top))
                                                                               #("i"))))
                                                                         (append
                                                                           syntmp-c-1754
                                                                           (list (cons '#(syntax-object
                                                                                          doloop
                                                                                          ((top)
                                                                                           #(ribcage
                                                                                             ()
                                                                                             ()
                                                                                             ())
                                                                                           #(ribcage
                                                                                             #(step)
                                                                                             #((top))
                                                                                             #("i"))
                                                                                           #(ribcage
                                                                                             #(_
                                                                                               var
                                                                                               init
                                                                                               step
                                                                                               e0
                                                                                               e1
                                                                                               c)
                                                                                             #((top)
                                                                                               (top)
                                                                                               (top)
                                                                                               (top)
                                                                                               (top)
                                                                                               (top)
                                                                                               (top))
                                                                                             #("i"
                                                                                               "i"
                                                                                               "i"
                                                                                               "i"
                                                                                               "i"
                                                                                               "i"
                                                                                               "i"))
                                                                                           #(ribcage
                                                                                             ()
                                                                                             ()
                                                                                             ())
                                                                                           #(ribcage
                                                                                             #(orig-x)
                                                                                             #((top))
                                                                                             #("i"))))
                                                                                       syntmp-step-1757)))))))
                                                     syntmp-tmp-1759)
                                              ((lambda (syntmp-tmp-1764)
                                                 (if syntmp-tmp-1764
                                                   (apply (lambda (syntmp-e1-1765
                                                                   syntmp-e2-1766)
                                                            (list '#(syntax-object
                                                                     let
                                                                     ((top)
                                                                      #(ribcage
                                                                        #(e1
                                                                          e2)
                                                                        #((top)
                                                                          (top))
                                                                        #("i"
                                                                          "i"))
                                                                      #(ribcage
                                                                        ()
                                                                        ()
                                                                        ())
                                                                      #(ribcage
                                                                        #(step)
                                                                        #((top))
                                                                        #("i"))
                                                                      #(ribcage
                                                                        #(_
                                                                          var
                                                                          init
                                                                          step
                                                                          e0
                                                                          e1
                                                                          c)
                                                                        #((top)
                                                                          (top)
                                                                          (top)
                                                                          (top)
                                                                          (top)
                                                                          (top)
                                                                          (top))
                                                                        #("i"
                                                                          "i"
                                                                          "i"
                                                                          "i"
                                                                          "i"
                                                                          "i"
                                                                          "i"))
                                                                      #(ribcage
                                                                        ()
                                                                        ()
                                                                        ())
                                                                      #(ribcage
                                                                        #(orig-x)
                                                                        #((top))
                                                                        #("i"))))
                                                                  '#(syntax-object
                                                                     doloop
                                                                     ((top)
                                                                      #(ribcage
                                                                        #(e1
                                                                          e2)
                                                                        #((top)
                                                                          (top))
                                                                        #("i"
                                                                          "i"))
                                                                      #(ribcage
                                                                        ()
                                                                        ()
                                                                        ())
                                                                      #(ribcage
                                                                        #(step)
                                                                        #((top))
                                                                        #("i"))
                                                                      #(ribcage
                                                                        #(_
                                                                          var
                                                                          init
                                                                          step
                                                                          e0
                                                                          e1
                                                                          c)
                                                                        #((top)
                                                                          (top)
                                                                          (top)
                                                                          (top)
                                                                          (top)
                                                                          (top)
                                                                          (top))
                                                                        #("i"
                                                                          "i"
                                                                          "i"
                                                                          "i"
                                                                          "i"
                                                                          "i"
                                                                          "i"))
                                                                      #(ribcage
                                                                        ()
                                                                        ()
                                                                        ())
                                                                      #(ribcage
                                                                        #(orig-x)
                                                                        #((top))
                                                                        #("i"))))
                                                                  (map list
                                                                       syntmp-var-1749
                                                                       syntmp-init-1750)
                                                                  (list '#(syntax-object
                                                                           if
                                                                           ((top)
                                                                            #(ribcage
                                                                              #(e1
                                                                                e2)
                                                                              #((top)
                                                                                (top))
                                                                              #("i"
                                                                                "i"))
                                                                            #(ribcage
                                                                              ()
                                                                              ()
                                                                              ())
                                                                            #(ribcage
                                                                              #(step)
                                                                              #((top))
                                                                              #("i"))
                                                                            #(ribcage
                                                                              #(_
                                                                                var
                                                                                init
                                                                                step
                                                                                e0
                                                                                e1
                                                                                c)
                                                                              #((top)
                                                                                (top)
                                                                                (top)
                                                                                (top)
                                                                                (top)
                                                                                (top)
                                                                                (top))
                                                                              #("i"
                                                                                "i"
                                                                                "i"
                                                                                "i"
                                                                                "i"
                                                                                "i"
                                                                                "i"))
                                                                            #(ribcage
                                                                              ()
                                                                              ()
                                                                              ())
                                                                            #(ribcage
                                                                              #(orig-x)
                                                                              #((top))
                                                                              #("i"))))
                                                                        syntmp-e0-1752
                                                                        (cons '#(syntax-object
                                                                                 begin
                                                                                 ((top)
                                                                                  #(ribcage
                                                                                    #(e1
                                                                                      e2)
                                                                                    #((top)
                                                                                      (top))
                                                                                    #("i"
                                                                                      "i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(step)
                                                                                    #((top))
                                                                                    #("i"))
                                                                                  #(ribcage
                                                                                    #(_
                                                                                      var
                                                                                      init
                                                                                      step
                                                                                      e0
                                                                                      e1
                                                                                      c)
                                                                                    #((top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top))
                                                                                    #("i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(orig-x)
                                                                                    #((top))
                                                                                    #("i"))))
                                                                              (cons syntmp-e1-1765
                                                                                    syntmp-e2-1766))
                                                                        (cons '#(syntax-object
                                                                                 begin
                                                                                 ((top)
                                                                                  #(ribcage
                                                                                    #(e1
                                                                                      e2)
                                                                                    #((top)
                                                                                      (top))
                                                                                    #("i"
                                                                                      "i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(step)
                                                                                    #((top))
                                                                                    #("i"))
                                                                                  #(ribcage
                                                                                    #(_
                                                                                      var
                                                                                      init
                                                                                      step
                                                                                      e0
                                                                                      e1
                                                                                      c)
                                                                                    #((top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top)
                                                                                      (top))
                                                                                    #("i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"
                                                                                      "i"))
                                                                                  #(ribcage
                                                                                    ()
                                                                                    ()
                                                                                    ())
                                                                                  #(ribcage
                                                                                    #(orig-x)
                                                                                    #((top))
                                                                                    #("i"))))
                                                                              (append
                                                                                syntmp-c-1754
                                                                                (list (cons '#(syntax-object
                                                                                               doloop
                                                                                               ((top)
                                                                                                #(ribcage
                                                                                                  #(e1
                                                                                                    e2)
                                                                                                  #((top)
                                                                                                    (top))
                                                                                                  #("i"
                                                                                                    "i"))
                                                                                                #(ribcage
                                                                                                  ()
                                                                                                  ()
                                                                                                  ())
                                                                                                #(ribcage
                                                                                                  #(step)
                                                                                                  #((top))
                                                                                                  #("i"))
                                                                                                #(ribcage
                                                                                                  #(_
                                                                                                    var
                                                                                                    init
                                                                                                    step
                                                                                                    e0
                                                                                                    e1
                                                                                                    c)
                                                                                                  #((top)
                                                                                                    (top)
                                                                                                    (top)
                                                                                                    (top)
                                                                                                    (top)
                                                                                                    (top)
                                                                                                    (top))
                                                                                                  #("i"
                                                                                                    "i"
                                                                                                    "i"
                                                                                                    "i"
                                                                                                    "i"
                                                                                                    "i"
                                                                                                    "i"))
                                                                                                #(ribcage
                                                                                                  ()
                                                                                                  ()
                                                                                                  ())
                                                                                                #(ribcage
                                                                                                  #(orig-x)
                                                                                                  #((top))
                                                                                                  #("i"))))
                                                                                            syntmp-step-1757)))))))
                                                          syntmp-tmp-1764)
                                                   (syntax-error
                                                     syntmp-tmp-1758)))
                                               (syntax-dispatch
                                                 syntmp-tmp-1758
                                                 '(any . each-any)))))
                                          (syntax-dispatch
                                            syntmp-tmp-1758
                                            '())))
                                       syntmp-e1-1753))
                                    syntmp-tmp-1756)
                             (syntax-error syntmp-tmp-1755)))
                         (syntax-dispatch
                           syntmp-tmp-1755
                           'each-any)))
                      (map (lambda (syntmp-v-1773 syntmp-s-1774)
                             ((lambda (syntmp-tmp-1775)
                                ((lambda (syntmp-tmp-1776)
                                   (if syntmp-tmp-1776
                                     (apply (lambda () syntmp-v-1773)
                                            syntmp-tmp-1776)
                                     ((lambda (syntmp-tmp-1777)
                                        (if syntmp-tmp-1777
                                          (apply (lambda (syntmp-e-1778)
                                                   syntmp-e-1778)
                                                 syntmp-tmp-1777)
                                          ((lambda (syntmp-_-1779)
                                             (syntax-error syntmp-orig-x-1745))
                                           syntmp-tmp-1775)))
                                      (syntax-dispatch
                                        syntmp-tmp-1775
                                        '(any)))))
                                 (syntax-dispatch syntmp-tmp-1775 (quote ()))))
                              syntmp-s-1774))
                           syntmp-var-1749
                           syntmp-step-1751)))
                   syntmp-tmp-1747)
            (syntax-error syntmp-tmp-1746)))
        (syntax-dispatch
          syntmp-tmp-1746
          '(any #(each (any any . any))
                (any . each-any)
                .
                each-any))))
     syntmp-orig-x-1745)))

(install-global-transformer
  'quasiquote
  (letrec ((syntmp-quasicons-1807
             (lambda (syntmp-x-1811 syntmp-y-1812)
               ((lambda (syntmp-tmp-1813)
                  ((lambda (syntmp-tmp-1814)
                     (if syntmp-tmp-1814
                       (apply (lambda (syntmp-x-1815 syntmp-y-1816)
                                ((lambda (syntmp-tmp-1817)
                                   ((lambda (syntmp-tmp-1818)
                                      (if syntmp-tmp-1818
                                        (apply (lambda (syntmp-dy-1819)
                                                 ((lambda (syntmp-tmp-1820)
                                                    ((lambda (syntmp-tmp-1821)
                                                       (if syntmp-tmp-1821
                                                         (apply (lambda (syntmp-dx-1822)
                                                                  (list '#(syntax-object
                                                                           quote
                                                                           ((top)
                                                                            #(ribcage
                                                                              #(dx)
                                                                              #((top))
                                                                              #("i"))
                                                                            #(ribcage
                                                                              #(dy)
                                                                              #((top))
                                                                              #("i"))
                                                                            #(ribcage
                                                                              ()
                                                                              ()
                                                                              ())
                                                                            #(ribcage
                                                                              #(x
                                                                                y)
                                                                              #((top)
                                                                                (top))
                                                                              #("i"
                                                                                "i"))
                                                                            #(ribcage
                                                                              ()
                                                                              ()
                                                                              ())
                                                                            #(ribcage
                                                                              ()
                                                                              ()
                                                                              ())
                                                                            #(ribcage
                                                                              #(x
                                                                                y)
                                                                              #((top)
                                                                                (top))
                                                                              #("i"
                                                                                "i"))
                                                                            #(ribcage
                                                                              #(quasicons
                                                                                quasiappend
                                                                                quasivector
                                                                                quasi)
                                                                              #((top)
                                                                                (top)
                                                                                (top)
                                                                                (top))
                                                                              #("i"
                                                                                "i"
                                                                                "i"
                                                                                "i"))))
                                                                        (cons syntmp-dx-1822
                                                                              syntmp-dy-1819)))
                                                                syntmp-tmp-1821)
                                                         ((lambda (syntmp-_-1823)
                                                            (if (null? syntmp-dy-1819)
                                                              (list '#(syntax-object
                                                                       list
                                                                       ((top)
                                                                        #(ribcage
                                                                          #(_)
                                                                          #((top))
                                                                          #("i"))
                                                                        #(ribcage
                                                                          #(dy)
                                                                          #((top))
                                                                          #("i"))
                                                                        #(ribcage
                                                                          ()
                                                                          ()
                                                                          ())
                                                                        #(ribcage
                                                                          #(x
                                                                            y)
                                                                          #((top)
                                                                            (top))
                                                                          #("i"
                                                                            "i"))
                                                                        #(ribcage
                                                                          ()
                                                                          ()
                                                                          ())
                                                                        #(ribcage
                                                                          ()
                                                                          ()
                                                                          ())
                                                                        #(ribcage
                                                                          #(x
                                                                            y)
                                                                          #((top)
                                                                            (top))
                                                                          #("i"
                                                                            "i"))
                                                                        #(ribcage
                                                                          #(quasicons
                                                                            quasiappend
                                                                            quasivector
                                                                            quasi)
                                                                          #((top)
                                                                            (top)
                                                                            (top)
                                                                            (top))
                                                                          #("i"
                                                                            "i"
                                                                            "i"
                                                                            "i"))))
                                                                    syntmp-x-1815)
                                                              (list '#(syntax-object
                                                                       cons
                                                                       ((top)
                                                                        #(ribcage
                                                                          #(_)
                                                                          #((top))
                                                                          #("i"))
                                                                        #(ribcage
                                                                          #(dy)
                                                                          #((top))
                                                                          #("i"))
                                                                        #(ribcage
                                                                          ()
                                                                          ()
                                                                          ())
                                                                        #(ribcage
                                                                          #(x
                                                                            y)
                                                                          #((top)
                                                                            (top))
                                                                          #("i"
                                                                            "i"))
                                                                        #(ribcage
                                                                          ()
                                                                          ()
                                                                          ())
                                                                        #(ribcage
                                                                          ()
                                                                          ()
                                                                          ())
                                                                        #(ribcage
                                                                          #(x
                                                                            y)
                                                                          #((top)
                                                                            (top))
                                                                          #("i"
                                                                            "i"))
                                                                        #(ribcage
                                                                          #(quasicons
                                                                            quasiappend
                                                                            quasivector
                                                                            quasi)
                                                                          #((top)
                                                                            (top)
                                                                            (top)
                                                                            (top))
                                                                          #("i"
                                                                            "i"
                                                                            "i"
                                                                            "i"))))
                                                                    syntmp-x-1815
                                                                    syntmp-y-1816)))
                                                          syntmp-tmp-1820)))
                                                     (syntax-dispatch
                                                       syntmp-tmp-1820
                                                       '(#(free-id
                                                           #(syntax-object
                                                             quote
                                                             ((top)
                                                              #(ribcage
                                                                #(dy)
                                                                #((top))
                                                                #("i"))
                                                              #(ribcage
                                                                ()
                                                                ()
                                                                ())
                                                              #(ribcage
                                                                #(x y)
                                                                #((top) (top))
                                                                #("i" "i"))
                                                              #(ribcage
                                                                ()
                                                                ()
                                                                ())
                                                              #(ribcage
                                                                ()
                                                                ()
                                                                ())
                                                              #(ribcage
                                                                #(x y)
                                                                #((top) (top))
                                                                #("i" "i"))
                                                              #(ribcage
                                                                #(quasicons
                                                                  quasiappend
                                                                  quasivector
                                                                  quasi)
                                                                #((top)
                                                                  (top)
                                                                  (top)
                                                                  (top))
                                                                #("i"
                                                                  "i"
                                                                  "i"
                                                                  "i")))))
                                                         any))))
                                                  syntmp-x-1815))
                                               syntmp-tmp-1818)
                                        ((lambda (syntmp-tmp-1824)
                                           (if syntmp-tmp-1824
                                             (apply (lambda (syntmp-stuff-1825)
                                                      (cons '#(syntax-object
                                                               list
                                                               ((top)
                                                                #(ribcage
                                                                  #(stuff)
                                                                  #((top))
                                                                  #("i"))
                                                                #(ribcage
                                                                  ()
                                                                  ()
                                                                  ())
                                                                #(ribcage
                                                                  #(x y)
                                                                  #((top)
                                                                    (top))
                                                                  #("i" "i"))
                                                                #(ribcage
                                                                  ()
                                                                  ()
                                                                  ())
                                                                #(ribcage
                                                                  ()
                                                                  ()
                                                                  ())
                                                                #(ribcage
                                                                  #(x y)
                                                                  #((top)
                                                                    (top))
                                                                  #("i" "i"))
                                                                #(ribcage
                                                                  #(quasicons
                                                                    quasiappend
                                                                    quasivector
                                                                    quasi)
                                                                  #((top)
                                                                    (top)
                                                                    (top)
                                                                    (top))
                                                                  #("i"
                                                                    "i"
                                                                    "i"
                                                                    "i"))))
                                                            (cons syntmp-x-1815
                                                                  syntmp-stuff-1825)))
                                                    syntmp-tmp-1824)
                                             ((lambda (syntmp-else-1826)
                                                (list '#(syntax-object
                                                         cons
                                                         ((top)
                                                          #(ribcage
                                                            #(else)
                                                            #((top))
                                                            #("i"))
                                                          #(ribcage () () ())
                                                          #(ribcage
                                                            #(x y)
                                                            #((top) (top))
                                                            #("i" "i"))
                                                          #(ribcage () () ())
                                                          #(ribcage () () ())
                                                          #(ribcage
                                                            #(x y)
                                                            #((top) (top))
                                                            #("i" "i"))
                                                          #(ribcage
                                                            #(quasicons
                                                              quasiappend
                                                              quasivector
                                                              quasi)
                                                            #((top)
                                                              (top)
                                                              (top)
                                                              (top))
                                                            #("i"
                                                              "i"
                                                              "i"
                                                              "i"))))
                                                      syntmp-x-1815
                                                      syntmp-y-1816))
                                              syntmp-tmp-1817)))
                                         (syntax-dispatch
                                           syntmp-tmp-1817
                                           '(#(free-id
                                               #(syntax-object
                                                 list
                                                 ((top)
                                                  #(ribcage () () ())
                                                  #(ribcage
                                                    #(x y)
                                                    #((top) (top))
                                                    #("i" "i"))
                                                  #(ribcage () () ())
                                                  #(ribcage () () ())
                                                  #(ribcage
                                                    #(x y)
                                                    #((top) (top))
                                                    #("i" "i"))
                                                  #(ribcage
                                                    #(quasicons
                                                      quasiappend
                                                      quasivector
                                                      quasi)
                                                    #((top) (top) (top) (top))
                                                    #("i" "i" "i" "i")))))
                                             .
                                             any)))))
                                    (syntax-dispatch
                                      syntmp-tmp-1817
                                      '(#(free-id
                                          #(syntax-object
                                            quote
                                            ((top)
                                             #(ribcage () () ())
                                             #(ribcage
                                               #(x y)
                                               #((top) (top))
                                               #("i" "i"))
                                             #(ribcage () () ())
                                             #(ribcage () () ())
                                             #(ribcage
                                               #(x y)
                                               #((top) (top))
                                               #("i" "i"))
                                             #(ribcage
                                               #(quasicons
                                                 quasiappend
                                                 quasivector
                                                 quasi)
                                               #((top) (top) (top) (top))
                                               #("i" "i" "i" "i")))))
                                        any))))
                                 syntmp-y-1816))
                              syntmp-tmp-1814)
                       (syntax-error syntmp-tmp-1813)))
                   (syntax-dispatch
                     syntmp-tmp-1813
                     '(any any))))
                (list syntmp-x-1811 syntmp-y-1812))))
           (syntmp-quasiappend-1808
             (lambda (syntmp-x-1827 syntmp-y-1828)
               ((lambda (syntmp-tmp-1829)
                  ((lambda (syntmp-tmp-1830)
                     (if syntmp-tmp-1830
                       (apply (lambda (syntmp-x-1831 syntmp-y-1832)
                                ((lambda (syntmp-tmp-1833)
                                   ((lambda (syntmp-tmp-1834)
                                      (if syntmp-tmp-1834
                                        (apply (lambda () syntmp-x-1831)
                                               syntmp-tmp-1834)
                                        ((lambda (syntmp-_-1835)
                                           (list '#(syntax-object
                                                    append
                                                    ((top)
                                                     #(ribcage
                                                       #(_)
                                                       #((top))
                                                       #("i"))
                                                     #(ribcage () () ())
                                                     #(ribcage
                                                       #(x y)
                                                       #((top) (top))
                                                       #("i" "i"))
                                                     #(ribcage () () ())
                                                     #(ribcage () () ())
                                                     #(ribcage
                                                       #(x y)
                                                       #((top) (top))
                                                       #("i" "i"))
                                                     #(ribcage
                                                       #(quasicons
                                                         quasiappend
                                                         quasivector
                                                         quasi)
                                                       #((top)
                                                         (top)
                                                         (top)
                                                         (top))
                                                       #("i" "i" "i" "i"))))
                                                 syntmp-x-1831
                                                 syntmp-y-1832))
                                         syntmp-tmp-1833)))
                                    (syntax-dispatch
                                      syntmp-tmp-1833
                                      '(#(free-id
                                          #(syntax-object
                                            quote
                                            ((top)
                                             #(ribcage () () ())
                                             #(ribcage
                                               #(x y)
                                               #((top) (top))
                                               #("i" "i"))
                                             #(ribcage () () ())
                                             #(ribcage () () ())
                                             #(ribcage
                                               #(x y)
                                               #((top) (top))
                                               #("i" "i"))
                                             #(ribcage
                                               #(quasicons
                                                 quasiappend
                                                 quasivector
                                                 quasi)
                                               #((top) (top) (top) (top))
                                               #("i" "i" "i" "i")))))
                                        ()))))
                                 syntmp-y-1832))
                              syntmp-tmp-1830)
                       (syntax-error syntmp-tmp-1829)))
                   (syntax-dispatch
                     syntmp-tmp-1829
                     '(any any))))
                (list syntmp-x-1827 syntmp-y-1828))))
           (syntmp-quasivector-1809
             (lambda (syntmp-x-1836)
               ((lambda (syntmp-tmp-1837)
                  ((lambda (syntmp-x-1838)
                     ((lambda (syntmp-tmp-1839)
                        ((lambda (syntmp-tmp-1840)
                           (if syntmp-tmp-1840
                             (apply (lambda (syntmp-x-1841)
                                      (list '#(syntax-object
                                               quote
                                               ((top)
                                                #(ribcage #(x) #((top)) #("i"))
                                                #(ribcage () () ())
                                                #(ribcage #(x) #((top)) #("i"))
                                                #(ribcage () () ())
                                                #(ribcage () () ())
                                                #(ribcage #(x) #((top)) #("i"))
                                                #(ribcage
                                                  #(quasicons
                                                    quasiappend
                                                    quasivector
                                                    quasi)
                                                  #((top) (top) (top) (top))
                                                  #("i" "i" "i" "i"))))
                                            (list->vector syntmp-x-1841)))
                                    syntmp-tmp-1840)
                             ((lambda (syntmp-tmp-1843)
                                (if syntmp-tmp-1843
                                  (apply (lambda (syntmp-x-1844)
                                           (cons '#(syntax-object
                                                    vector
                                                    ((top)
                                                     #(ribcage
                                                       #(x)
                                                       #((top))
                                                       #("i"))
                                                     #(ribcage () () ())
                                                     #(ribcage
                                                       #(x)
                                                       #((top))
                                                       #("i"))
                                                     #(ribcage () () ())
                                                     #(ribcage () () ())
                                                     #(ribcage
                                                       #(x)
                                                       #((top))
                                                       #("i"))
                                                     #(ribcage
                                                       #(quasicons
                                                         quasiappend
                                                         quasivector
                                                         quasi)
                                                       #((top)
                                                         (top)
                                                         (top)
                                                         (top))
                                                       #("i" "i" "i" "i"))))
                                                 syntmp-x-1844))
                                         syntmp-tmp-1843)
                                  ((lambda (syntmp-_-1846)
                                     (list '#(syntax-object
                                              list->vector
                                              ((top)
                                               #(ribcage #(_) #((top)) #("i"))
                                               #(ribcage () () ())
                                               #(ribcage #(x) #((top)) #("i"))
                                               #(ribcage () () ())
                                               #(ribcage () () ())
                                               #(ribcage #(x) #((top)) #("i"))
                                               #(ribcage
                                                 #(quasicons
                                                   quasiappend
                                                   quasivector
                                                   quasi)
                                                 #((top) (top) (top) (top))
                                                 #("i" "i" "i" "i"))))
                                           syntmp-x-1838))
                                   syntmp-tmp-1839)))
                              (syntax-dispatch
                                syntmp-tmp-1839
                                '(#(free-id
                                    #(syntax-object
                                      list
                                      ((top)
                                       #(ribcage () () ())
                                       #(ribcage #(x) #((top)) #("i"))
                                       #(ribcage () () ())
                                       #(ribcage () () ())
                                       #(ribcage #(x) #((top)) #("i"))
                                       #(ribcage
                                         #(quasicons
                                           quasiappend
                                           quasivector
                                           quasi)
                                         #((top) (top) (top) (top))
                                         #("i" "i" "i" "i")))))
                                  .
                                  each-any)))))
                         (syntax-dispatch
                           syntmp-tmp-1839
                           '(#(free-id
                               #(syntax-object
                                 quote
                                 ((top)
                                  #(ribcage () () ())
                                  #(ribcage #(x) #((top)) #("i"))
                                  #(ribcage () () ())
                                  #(ribcage () () ())
                                  #(ribcage #(x) #((top)) #("i"))
                                  #(ribcage
                                    #(quasicons quasiappend quasivector quasi)
                                    #((top) (top) (top) (top))
                                    #("i" "i" "i" "i")))))
                             each-any))))
                      syntmp-x-1838))
                   syntmp-tmp-1837))
                syntmp-x-1836)))
           (syntmp-quasi-1810
             (lambda (syntmp-p-1847 syntmp-lev-1848)
               ((lambda (syntmp-tmp-1849)
                  ((lambda (syntmp-tmp-1850)
                     (if syntmp-tmp-1850
                       (apply (lambda (syntmp-p-1851)
                                (if (= syntmp-lev-1848 0)
                                  syntmp-p-1851
                                  (syntmp-quasicons-1807
                                    '(#(syntax-object
                                        quote
                                        ((top)
                                         #(ribcage #(p) #((top)) #("i"))
                                         #(ribcage () () ())
                                         #(ribcage
                                           #(p lev)
                                           #((top) (top))
                                           #("i" "i"))
                                         #(ribcage
                                           #(quasicons
                                             quasiappend
                                             quasivector
                                             quasi)
                                           #((top) (top) (top) (top))
                                           #("i" "i" "i" "i"))))
                                      #(syntax-object
                                        unquote
                                        ((top)
                                         #(ribcage #(p) #((top)) #("i"))
                                         #(ribcage () () ())
                                         #(ribcage
                                           #(p lev)
                                           #((top) (top))
                                           #("i" "i"))
                                         #(ribcage
                                           #(quasicons
                                             quasiappend
                                             quasivector
                                             quasi)
                                           #((top) (top) (top) (top))
                                           #("i" "i" "i" "i")))))
                                    (syntmp-quasi-1810
                                      (list syntmp-p-1851)
                                      (- syntmp-lev-1848 1)))))
                              syntmp-tmp-1850)
                       ((lambda (syntmp-tmp-1852)
                          (if syntmp-tmp-1852
                            (apply (lambda (syntmp-p-1853 syntmp-q-1854)
                                     (if (= syntmp-lev-1848 0)
                                       (syntmp-quasiappend-1808
                                         syntmp-p-1853
                                         (syntmp-quasi-1810
                                           syntmp-q-1854
                                           syntmp-lev-1848))
                                       (syntmp-quasicons-1807
                                         (syntmp-quasicons-1807
                                           '(#(syntax-object
                                               quote
                                               ((top)
                                                #(ribcage
                                                  #(p q)
                                                  #((top) (top))
                                                  #("i" "i"))
                                                #(ribcage () () ())
                                                #(ribcage
                                                  #(p lev)
                                                  #((top) (top))
                                                  #("i" "i"))
                                                #(ribcage
                                                  #(quasicons
                                                    quasiappend
                                                    quasivector
                                                    quasi)
                                                  #((top) (top) (top) (top))
                                                  #("i" "i" "i" "i"))))
                                             #(syntax-object
                                               unquote-splicing
                                               ((top)
                                                #(ribcage
                                                  #(p q)
                                                  #((top) (top))
                                                  #("i" "i"))
                                                #(ribcage () () ())
                                                #(ribcage
                                                  #(p lev)
                                                  #((top) (top))
                                                  #("i" "i"))
                                                #(ribcage
                                                  #(quasicons
                                                    quasiappend
                                                    quasivector
                                                    quasi)
                                                  #((top) (top) (top) (top))
                                                  #("i" "i" "i" "i")))))
                                           (syntmp-quasi-1810
                                             (list syntmp-p-1853)
                                             (- syntmp-lev-1848 1)))
                                         (syntmp-quasi-1810
                                           syntmp-q-1854
                                           syntmp-lev-1848))))
                                   syntmp-tmp-1852)
                            ((lambda (syntmp-tmp-1855)
                               (if syntmp-tmp-1855
                                 (apply (lambda (syntmp-p-1856)
                                          (syntmp-quasicons-1807
                                            '(#(syntax-object
                                                quote
                                                ((top)
                                                 #(ribcage
                                                   #(p)
                                                   #((top))
                                                   #("i"))
                                                 #(ribcage () () ())
                                                 #(ribcage
                                                   #(p lev)
                                                   #((top) (top))
                                                   #("i" "i"))
                                                 #(ribcage
                                                   #(quasicons
                                                     quasiappend
                                                     quasivector
                                                     quasi)
                                                   #((top) (top) (top) (top))
                                                   #("i" "i" "i" "i"))))
                                              #(syntax-object
                                                quasiquote
                                                ((top)
                                                 #(ribcage
                                                   #(p)
                                                   #((top))
                                                   #("i"))
                                                 #(ribcage () () ())
                                                 #(ribcage
                                                   #(p lev)
                                                   #((top) (top))
                                                   #("i" "i"))
                                                 #(ribcage
                                                   #(quasicons
                                                     quasiappend
                                                     quasivector
                                                     quasi)
                                                   #((top) (top) (top) (top))
                                                   #("i" "i" "i" "i")))))
                                            (syntmp-quasi-1810
                                              (list syntmp-p-1856)
                                              (+ syntmp-lev-1848 1))))
                                        syntmp-tmp-1855)
                                 ((lambda (syntmp-tmp-1857)
                                    (if syntmp-tmp-1857
                                      (apply (lambda (syntmp-p-1858
                                                      syntmp-q-1859)
                                               (syntmp-quasicons-1807
                                                 (syntmp-quasi-1810
                                                   syntmp-p-1858
                                                   syntmp-lev-1848)
                                                 (syntmp-quasi-1810
                                                   syntmp-q-1859
                                                   syntmp-lev-1848)))
                                             syntmp-tmp-1857)
                                      ((lambda (syntmp-tmp-1860)
                                         (if syntmp-tmp-1860
                                           (apply (lambda (syntmp-x-1861)
                                                    (syntmp-quasivector-1809
                                                      (syntmp-quasi-1810
                                                        syntmp-x-1861
                                                        syntmp-lev-1848)))
                                                  syntmp-tmp-1860)
                                           ((lambda (syntmp-p-1863)
                                              (list '#(syntax-object
                                                       quote
                                                       ((top)
                                                        #(ribcage
                                                          #(p)
                                                          #((top))
                                                          #("i"))
                                                        #(ribcage () () ())
                                                        #(ribcage
                                                          #(p lev)
                                                          #((top) (top))
                                                          #("i" "i"))
                                                        #(ribcage
                                                          #(quasicons
                                                            quasiappend
                                                            quasivector
                                                            quasi)
                                                          #((top)
                                                            (top)
                                                            (top)
                                                            (top))
                                                          #("i" "i" "i" "i"))))
                                                    syntmp-p-1863))
                                            syntmp-tmp-1849)))
                                       (syntax-dispatch
                                         syntmp-tmp-1849
                                         '#(vector each-any)))))
                                  (syntax-dispatch
                                    syntmp-tmp-1849
                                    '(any . any)))))
                             (syntax-dispatch
                               syntmp-tmp-1849
                               '(#(free-id
                                   #(syntax-object
                                     quasiquote
                                     ((top)
                                      #(ribcage () () ())
                                      #(ribcage
                                        #(p lev)
                                        #((top) (top))
                                        #("i" "i"))
                                      #(ribcage
                                        #(quasicons
                                          quasiappend
                                          quasivector
                                          quasi)
                                        #((top) (top) (top) (top))
                                        #("i" "i" "i" "i")))))
                                 any)))))
                        (syntax-dispatch
                          syntmp-tmp-1849
                          '((#(free-id
                               #(syntax-object
                                 unquote-splicing
                                 ((top)
                                  #(ribcage () () ())
                                  #(ribcage #(p lev) #((top) (top)) #("i" "i"))
                                  #(ribcage
                                    #(quasicons quasiappend quasivector quasi)
                                    #((top) (top) (top) (top))
                                    #("i" "i" "i" "i")))))
                             any)
                            .
                            any)))))
                   (syntax-dispatch
                     syntmp-tmp-1849
                     '(#(free-id
                         #(syntax-object
                           unquote
                           ((top)
                            #(ribcage () () ())
                            #(ribcage #(p lev) #((top) (top)) #("i" "i"))
                            #(ribcage
                              #(quasicons quasiappend quasivector quasi)
                              #((top) (top) (top) (top))
                              #("i" "i" "i" "i")))))
                       any))))
                syntmp-p-1847))))
    (lambda (syntmp-x-1864)
      ((lambda (syntmp-tmp-1865)
         ((lambda (syntmp-tmp-1866)
            (if syntmp-tmp-1866
              (apply (lambda (syntmp-_-1867 syntmp-e-1868)
                       (syntmp-quasi-1810 syntmp-e-1868 0))
                     syntmp-tmp-1866)
              (syntax-error syntmp-tmp-1865)))
          (syntax-dispatch
            syntmp-tmp-1865
            '(any any))))
       syntmp-x-1864))))

(install-global-transformer
  'include
  (lambda (syntmp-x-1928)
    (letrec ((syntmp-read-file-1929
               (lambda (syntmp-fn-1930 syntmp-k-1931)
                 (let ((syntmp-p-1932 (open-input-file syntmp-fn-1930)))
                   (let syntmp-f-1933 ((syntmp-x-1934 (read syntmp-p-1932)))
                     (if (eof-object? syntmp-x-1934)
                       (begin
                         (close-input-port syntmp-p-1932)
                         '())
                       (cons (datum->syntax-object
                               syntmp-k-1931
                               syntmp-x-1934)
                             (syntmp-f-1933 (read syntmp-p-1932)))))))))
      ((lambda (syntmp-tmp-1935)
         ((lambda (syntmp-tmp-1936)
            (if syntmp-tmp-1936
              (apply (lambda (syntmp-k-1937 syntmp-filename-1938)
                       (let ((syntmp-fn-1939
                               (syntax-object->datum syntmp-filename-1938)))
                         ((lambda (syntmp-tmp-1940)
                            ((lambda (syntmp-tmp-1941)
                               (if syntmp-tmp-1941
                                 (apply (lambda (syntmp-exp-1942)
                                          (cons '#(syntax-object
                                                   begin
                                                   ((top)
                                                    #(ribcage () () ())
                                                    #(ribcage
                                                      #(exp)
                                                      #((top))
                                                      #("i"))
                                                    #(ribcage () () ())
                                                    #(ribcage () () ())
                                                    #(ribcage
                                                      #(fn)
                                                      #((top))
                                                      #("i"))
                                                    #(ribcage
                                                      #(k filename)
                                                      #((top) (top))
                                                      #("i" "i"))
                                                    #(ribcage
                                                      (read-file)
                                                      ((top))
                                                      ("i"))
                                                    #(ribcage
                                                      #(x)
                                                      #((top))
                                                      #("i"))))
                                                syntmp-exp-1942))
                                        syntmp-tmp-1941)
                                 (syntax-error syntmp-tmp-1940)))
                             (syntax-dispatch
                               syntmp-tmp-1940
                               'each-any)))
                          (syntmp-read-file-1929
                            syntmp-fn-1939
                            syntmp-k-1937))))
                     syntmp-tmp-1936)
              (syntax-error syntmp-tmp-1935)))
          (syntax-dispatch
            syntmp-tmp-1935
            '(any any))))
       syntmp-x-1928))))

(install-global-transformer
  'unquote
  (lambda (syntmp-x-1959)
    ((lambda (syntmp-tmp-1960)
       ((lambda (syntmp-tmp-1961)
          (if syntmp-tmp-1961
            (apply (lambda (syntmp-_-1962 syntmp-e-1963)
                     (error 'unquote
                            "expression ,~s not valid outside of quasiquote"
                            (syntax-object->datum syntmp-e-1963)))
                   syntmp-tmp-1961)
            (syntax-error syntmp-tmp-1960)))
        (syntax-dispatch
          syntmp-tmp-1960
          '(any any))))
     syntmp-x-1959)))

(install-global-transformer
  'unquote-splicing
  (lambda (syntmp-x-1969)
    ((lambda (syntmp-tmp-1970)
       ((lambda (syntmp-tmp-1971)
          (if syntmp-tmp-1971
            (apply (lambda (syntmp-_-1972 syntmp-e-1973)
                     (error 'unquote-splicing
                            "expression ,@~s not valid outside of quasiquote"
                            (syntax-object->datum syntmp-e-1973)))
                   syntmp-tmp-1971)
            (syntax-error syntmp-tmp-1970)))
        (syntax-dispatch
          syntmp-tmp-1970
          '(any any))))
     syntmp-x-1969)))

(install-global-transformer
  'case
  (lambda (syntmp-x-1979)
    ((lambda (syntmp-tmp-1980)
       ((lambda (syntmp-tmp-1981)
          (if syntmp-tmp-1981
            (apply (lambda (syntmp-_-1982
                            syntmp-e-1983
                            syntmp-m1-1984
                            syntmp-m2-1985)
                     ((lambda (syntmp-tmp-1986)
                        ((lambda (syntmp-body-1987)
                           (list '#(syntax-object
                                    let
                                    ((top)
                                     #(ribcage () () ())
                                     #(ribcage #(body) #((top)) #("i"))
                                     #(ribcage
                                       #(_ e m1 m2)
                                       #((top) (top) (top) (top))
                                       #("i" "i" "i" "i"))
                                     #(ribcage () () ())
                                     #(ribcage #(x) #((top)) #("i"))))
                                 (list (list '#(syntax-object
                                                t
                                                ((top)
                                                 #(ribcage () () ())
                                                 #(ribcage
                                                   #(body)
                                                   #((top))
                                                   #("i"))
                                                 #(ribcage
                                                   #(_ e m1 m2)
                                                   #((top) (top) (top) (top))
                                                   #("i" "i" "i" "i"))
                                                 #(ribcage () () ())
                                                 #(ribcage
                                                   #(x)
                                                   #((top))
                                                   #("i"))))
                                             syntmp-e-1983))
                                 syntmp-body-1987))
                         syntmp-tmp-1986))
                      (let syntmp-f-1988 ((syntmp-clause-1989 syntmp-m1-1984)
                                          (syntmp-clauses-1990 syntmp-m2-1985))
                        (if (null? syntmp-clauses-1990)
                          ((lambda (syntmp-tmp-1992)
                             ((lambda (syntmp-tmp-1993)
                                (if syntmp-tmp-1993
                                  (apply (lambda (syntmp-e1-1994
                                                  syntmp-e2-1995)
                                           (cons '#(syntax-object
                                                    begin
                                                    ((top)
                                                     #(ribcage
                                                       #(e1 e2)
                                                       #((top) (top))
                                                       #("i" "i"))
                                                     #(ribcage () () ())
                                                     #(ribcage
                                                       #(f clause clauses)
                                                       #((top) (top) (top))
                                                       #("i" "i" "i"))
                                                     #(ribcage
                                                       #(_ e m1 m2)
                                                       #((top)
                                                         (top)
                                                         (top)
                                                         (top))
                                                       #("i" "i" "i" "i"))
                                                     #(ribcage () () ())
                                                     #(ribcage
                                                       #(x)
                                                       #((top))
                                                       #("i"))))
                                                 (cons syntmp-e1-1994
                                                       syntmp-e2-1995)))
                                         syntmp-tmp-1993)
                                  ((lambda (syntmp-tmp-1997)
                                     (if syntmp-tmp-1997
                                       (apply (lambda (syntmp-k-1998
                                                       syntmp-e1-1999
                                                       syntmp-e2-2000)
                                                (list '#(syntax-object
                                                         if
                                                         ((top)
                                                          #(ribcage
                                                            #(k e1 e2)
                                                            #((top)
                                                              (top)
                                                              (top))
                                                            #("i" "i" "i"))
                                                          #(ribcage () () ())
                                                          #(ribcage
                                                            #(f clause clauses)
                                                            #((top)
                                                              (top)
                                                              (top))
                                                            #("i" "i" "i"))
                                                          #(ribcage
                                                            #(_ e m1 m2)
                                                            #((top)
                                                              (top)
                                                              (top)
                                                              (top))
                                                            #("i" "i" "i" "i"))
                                                          #(ribcage () () ())
                                                          #(ribcage
                                                            #(x)
                                                            #((top))
                                                            #("i"))))
                                                      (list '#(syntax-object
                                                               memv
                                                               ((top)
                                                                #(ribcage
                                                                  #(k e1 e2)
                                                                  #((top)
                                                                    (top)
                                                                    (top))
                                                                  #("i"
                                                                    "i"
                                                                    "i"))
                                                                #(ribcage
                                                                  ()
                                                                  ()
                                                                  ())
                                                                #(ribcage
                                                                  #(f
                                                                    clause
                                                                    clauses)
                                                                  #((top)
                                                                    (top)
                                                                    (top))
                                                                  #("i"
                                                                    "i"
                                                                    "i"))
                                                                #(ribcage
                                                                  #(_ e m1 m2)
                                                                  #((top)
                                                                    (top)
                                                                    (top)
                                                                    (top))
                                                                  #("i"
                                                                    "i"
                                                                    "i"
                                                                    "i"))
                                                                #(ribcage
                                                                  ()
                                                                  ()
                                                                  ())
                                                                #(ribcage
                                                                  #(x)
                                                                  #((top))
                                                                  #("i"))))
                                                            '#(syntax-object
                                                               t
                                                               ((top)
                                                                #(ribcage
                                                                  #(k e1 e2)
                                                                  #((top)
                                                                    (top)
                                                                    (top))
                                                                  #("i"
                                                                    "i"
                                                                    "i"))
                                                                #(ribcage
                                                                  ()
                                                                  ()
                                                                  ())
                                                                #(ribcage
                                                                  #(f
                                                                    clause
                                                                    clauses)
                                                                  #((top)
                                                                    (top)
                                                                    (top))
                                                                  #("i"
                                                                    "i"
                                                                    "i"))
                                                                #(ribcage
                                                                  #(_ e m1 m2)
                                                                  #((top)
                                                                    (top)
                                                                    (top)
                                                                    (top))
                                                                  #("i"
                                                                    "i"
                                                                    "i"
                                                                    "i"))
                                                                #(ribcage
                                                                  ()
                                                                  ()
                                                                  ())
                                                                #(ribcage
                                                                  #(x)
                                                                  #((top))
                                                                  #("i"))))
                                                            (list '#(syntax-object
                                                                     quote
                                                                     ((top)
                                                                      #(ribcage
                                                                        #(k
                                                                          e1
                                                                          e2)
                                                                        #((top)
                                                                          (top)
                                                                          (top))
                                                                        #("i"
                                                                          "i"
                                                                          "i"))
                                                                      #(ribcage
                                                                        ()
                                                                        ()
                                                                        ())
                                                                      #(ribcage
                                                                        #(f
                                                                          clause
                                                                          clauses)
                                                                        #((top)
                                                                          (top)
                                                                          (top))
                                                                        #("i"
                                                                          "i"
                                                                          "i"))
                                                                      #(ribcage
                                                                        #(_
                                                                          e
                                                                          m1
                                                                          m2)
                                                                        #((top)
                                                                          (top)
                                                                          (top)
                                                                          (top))
                                                                        #("i"
                                                                          "i"
                                                                          "i"
                                                                          "i"))
                                                                      #(ribcage
                                                                        ()
                                                                        ()
                                                                        ())
                                                                      #(ribcage
                                                                        #(x)
                                                                        #((top))
                                                                        #("i"))))
                                                                  syntmp-k-1998))
                                                      (cons '#(syntax-object
                                                               begin
                                                               ((top)
                                                                #(ribcage
                                                                  #(k e1 e2)
                                                                  #((top)
                                                                    (top)
                                                                    (top))
                                                                  #("i"
                                                                    "i"
                                                                    "i"))
                                                                #(ribcage
                                                                  ()
                                                                  ()
                                                                  ())
                                                                #(ribcage
                                                                  #(f
                                                                    clause
                                                                    clauses)
                                                                  #((top)
                                                                    (top)
                                                                    (top))
                                                                  #("i"
                                                                    "i"
                                                                    "i"))
                                                                #(ribcage
                                                                  #(_ e m1 m2)
                                                                  #((top)
                                                                    (top)
                                                                    (top)
                                                                    (top))
                                                                  #("i"
                                                                    "i"
                                                                    "i"
                                                                    "i"))
                                                                #(ribcage
                                                                  ()
                                                                  ()
                                                                  ())
                                                                #(ribcage
                                                                  #(x)
                                                                  #((top))
                                                                  #("i"))))
                                                            (cons syntmp-e1-1999
                                                                  syntmp-e2-2000))))
                                              syntmp-tmp-1997)
                                       ((lambda (syntmp-_-2003)
                                          (syntax-error syntmp-x-1979))
                                        syntmp-tmp-1992)))
                                   (syntax-dispatch
                                     syntmp-tmp-1992
                                     '(each-any any . each-any)))))
                              (syntax-dispatch
                                syntmp-tmp-1992
                                '(#(free-id
                                    #(syntax-object
                                      else
                                      ((top)
                                       #(ribcage () () ())
                                       #(ribcage
                                         #(f clause clauses)
                                         #((top) (top) (top))
                                         #("i" "i" "i"))
                                       #(ribcage
                                         #(_ e m1 m2)
                                         #((top) (top) (top) (top))
                                         #("i" "i" "i" "i"))
                                       #(ribcage () () ())
                                       #(ribcage #(x) #((top)) #("i")))))
                                  any
                                  .
                                  each-any))))
                           syntmp-clause-1989)
                          ((lambda (syntmp-tmp-2004)
                             ((lambda (syntmp-rest-2005)
                                ((lambda (syntmp-tmp-2006)
                                   ((lambda (syntmp-tmp-2007)
                                      (if syntmp-tmp-2007
                                        (apply (lambda (syntmp-k-2008
                                                        syntmp-e1-2009
                                                        syntmp-e2-2010)
                                                 (list '#(syntax-object
                                                          if
                                                          ((top)
                                                           #(ribcage
                                                             #(k e1 e2)
                                                             #((top)
                                                               (top)
                                                               (top))
                                                             #("i" "i" "i"))
                                                           #(ribcage () () ())
                                                           #(ribcage
                                                             #(rest)
                                                             #((top))
                                                             #("i"))
                                                           #(ribcage () () ())
                                                           #(ribcage
                                                             #(f
                                                               clause
                                                               clauses)
                                                             #((top)
                                                               (top)
                                                               (top))
                                                             #("i" "i" "i"))
                                                           #(ribcage
                                                             #(_ e m1 m2)
                                                             #((top)
                                                               (top)
                                                               (top)
                                                               (top))
                                                             #("i"
                                                               "i"
                                                               "i"
                                                               "i"))
                                                           #(ribcage () () ())
                                                           #(ribcage
                                                             #(x)
                                                             #((top))
                                                             #("i"))))
                                                       (list '#(syntax-object
                                                                memv
                                                                ((top)
                                                                 #(ribcage
                                                                   #(k e1 e2)
                                                                   #((top)
                                                                     (top)
                                                                     (top))
                                                                   #("i"
                                                                     "i"
                                                                     "i"))
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(rest)
                                                                   #((top))
                                                                   #("i"))
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(f
                                                                     clause
                                                                     clauses)
                                                                   #((top)
                                                                     (top)
                                                                     (top))
                                                                   #("i"
                                                                     "i"
                                                                     "i"))
                                                                 #(ribcage
                                                                   #(_ e m1 m2)
                                                                   #((top)
                                                                     (top)
                                                                     (top)
                                                                     (top))
                                                                   #("i"
                                                                     "i"
                                                                     "i"
                                                                     "i"))
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(x)
                                                                   #((top))
                                                                   #("i"))))
                                                             '#(syntax-object
                                                                t
                                                                ((top)
                                                                 #(ribcage
                                                                   #(k e1 e2)
                                                                   #((top)
                                                                     (top)
                                                                     (top))
                                                                   #("i"
                                                                     "i"
                                                                     "i"))
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(rest)
                                                                   #((top))
                                                                   #("i"))
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(f
                                                                     clause
                                                                     clauses)
                                                                   #((top)
                                                                     (top)
                                                                     (top))
                                                                   #("i"
                                                                     "i"
                                                                     "i"))
                                                                 #(ribcage
                                                                   #(_ e m1 m2)
                                                                   #((top)
                                                                     (top)
                                                                     (top)
                                                                     (top))
                                                                   #("i"
                                                                     "i"
                                                                     "i"
                                                                     "i"))
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(x)
                                                                   #((top))
                                                                   #("i"))))
                                                             (list '#(syntax-object
                                                                      quote
                                                                      ((top)
                                                                       #(ribcage
                                                                         #(k
                                                                           e1
                                                                           e2)
                                                                         #((top)
                                                                           (top)
                                                                           (top))
                                                                         #("i"
                                                                           "i"
                                                                           "i"))
                                                                       #(ribcage
                                                                         ()
                                                                         ()
                                                                         ())
                                                                       #(ribcage
                                                                         #(rest)
                                                                         #((top))
                                                                         #("i"))
                                                                       #(ribcage
                                                                         ()
                                                                         ()
                                                                         ())
                                                                       #(ribcage
                                                                         #(f
                                                                           clause
                                                                           clauses)
                                                                         #((top)
                                                                           (top)
                                                                           (top))
                                                                         #("i"
                                                                           "i"
                                                                           "i"))
                                                                       #(ribcage
                                                                         #(_
                                                                           e
                                                                           m1
                                                                           m2)
                                                                         #((top)
                                                                           (top)
                                                                           (top)
                                                                           (top))
                                                                         #("i"
                                                                           "i"
                                                                           "i"
                                                                           "i"))
                                                                       #(ribcage
                                                                         ()
                                                                         ()
                                                                         ())
                                                                       #(ribcage
                                                                         #(x)
                                                                         #((top))
                                                                         #("i"))))
                                                                   syntmp-k-2008))
                                                       (cons '#(syntax-object
                                                                begin
                                                                ((top)
                                                                 #(ribcage
                                                                   #(k e1 e2)
                                                                   #((top)
                                                                     (top)
                                                                     (top))
                                                                   #("i"
                                                                     "i"
                                                                     "i"))
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(rest)
                                                                   #((top))
                                                                   #("i"))
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(f
                                                                     clause
                                                                     clauses)
                                                                   #((top)
                                                                     (top)
                                                                     (top))
                                                                   #("i"
                                                                     "i"
                                                                     "i"))
                                                                 #(ribcage
                                                                   #(_ e m1 m2)
                                                                   #((top)
                                                                     (top)
                                                                     (top)
                                                                     (top))
                                                                   #("i"
                                                                     "i"
                                                                     "i"
                                                                     "i"))
                                                                 #(ribcage
                                                                   ()
                                                                   ()
                                                                   ())
                                                                 #(ribcage
                                                                   #(x)
                                                                   #((top))
                                                                   #("i"))))
                                                             (cons syntmp-e1-2009
                                                                   syntmp-e2-2010))
                                                       syntmp-rest-2005))
                                               syntmp-tmp-2007)
                                        ((lambda (syntmp-_-2013)
                                           (syntax-error syntmp-x-1979))
                                         syntmp-tmp-2006)))
                                    (syntax-dispatch
                                      syntmp-tmp-2006
                                      '(each-any any . each-any))))
                                 syntmp-clause-1989))
                              syntmp-tmp-2004))
                           (syntmp-f-1988
                             (car syntmp-clauses-1990)
                             (cdr syntmp-clauses-1990)))))))
                   syntmp-tmp-1981)
            (syntax-error syntmp-tmp-1980)))
        (syntax-dispatch
          syntmp-tmp-1980
          '(any any any . each-any))))
     syntmp-x-1979)))

(install-global-transformer
  'identifier-syntax
  (lambda (syntmp-x-2043)
    ((lambda (syntmp-tmp-2044)
       ((lambda (syntmp-tmp-2045)
          (if syntmp-tmp-2045
            (apply (lambda (syntmp-_-2046 syntmp-e-2047)
                     (list '#(syntax-object
                              lambda
                              ((top)
                               #(ribcage #(_ e) #((top) (top)) #("i" "i"))
                               #(ribcage () () ())
                               #(ribcage #(x) #((top)) #("i"))))
                           '(#(syntax-object
                               x
                               ((top)
                                #(ribcage #(_ e) #((top) (top)) #("i" "i"))
                                #(ribcage () () ())
                                #(ribcage #(x) #((top)) #("i")))))
                           (list '#(syntax-object
                                    syntax-case
                                    ((top)
                                     #(ribcage
                                       #(_ e)
                                       #((top) (top))
                                       #("i" "i"))
                                     #(ribcage () () ())
                                     #(ribcage #(x) #((top)) #("i"))))
                                 '#(syntax-object
                                    x
                                    ((top)
                                     #(ribcage
                                       #(_ e)
                                       #((top) (top))
                                       #("i" "i"))
                                     #(ribcage () () ())
                                     #(ribcage #(x) #((top)) #("i"))))
                                 '()
                                 (list '#(syntax-object
                                          id
                                          ((top)
                                           #(ribcage
                                             #(_ e)
                                             #((top) (top))
                                             #("i" "i"))
                                           #(ribcage () () ())
                                           #(ribcage #(x) #((top)) #("i"))))
                                       '(#(syntax-object
                                           identifier?
                                           ((top)
                                            #(ribcage
                                              #(_ e)
                                              #((top) (top))
                                              #("i" "i"))
                                            #(ribcage () () ())
                                            #(ribcage #(x) #((top)) #("i"))))
                                         (#(syntax-object
                                            syntax
                                            ((top)
                                             #(ribcage
                                               #(_ e)
                                               #((top) (top))
                                               #("i" "i"))
                                             #(ribcage () () ())
                                             #(ribcage #(x) #((top)) #("i"))))
                                          #(syntax-object
                                            id
                                            ((top)
                                             #(ribcage
                                               #(_ e)
                                               #((top) (top))
                                               #("i" "i"))
                                             #(ribcage () () ())
                                             #(ribcage
                                               #(x)
                                               #((top))
                                               #("i"))))))
                                       (list '#(syntax-object
                                                syntax
                                                ((top)
                                                 #(ribcage
                                                   #(_ e)
                                                   #((top) (top))
                                                   #("i" "i"))
                                                 #(ribcage () () ())
                                                 #(ribcage
                                                   #(x)
                                                   #((top))
                                                   #("i"))))
                                             syntmp-e-2047))
                                 (list (cons syntmp-_-2046
                                             '(#(syntax-object
                                                 x
                                                 ((top)
                                                  #(ribcage
                                                    #(_ e)
                                                    #((top) (top))
                                                    #("i" "i"))
                                                  #(ribcage () () ())
                                                  #(ribcage
                                                    #(x)
                                                    #((top))
                                                    #("i"))))
                                               #(syntax-object
                                                 ...
                                                 ((top)
                                                  #(ribcage
                                                    #(_ e)
                                                    #((top) (top))
                                                    #("i" "i"))
                                                  #(ribcage () () ())
                                                  #(ribcage
                                                    #(x)
                                                    #((top))
                                                    #("i"))))))
                                       (list '#(syntax-object
                                                syntax
                                                ((top)
                                                 #(ribcage
                                                   #(_ e)
                                                   #((top) (top))
                                                   #("i" "i"))
                                                 #(ribcage () () ())
                                                 #(ribcage
                                                   #(x)
                                                   #((top))
                                                   #("i"))))
                                             (cons syntmp-e-2047
                                                   '(#(syntax-object
                                                       x
                                                       ((top)
                                                        #(ribcage
                                                          #(_ e)
                                                          #((top) (top))
                                                          #("i" "i"))
                                                        #(ribcage () () ())
                                                        #(ribcage
                                                          #(x)
                                                          #((top))
                                                          #("i"))))
                                                     #(syntax-object
                                                       ...
                                                       ((top)
                                                        #(ribcage
                                                          #(_ e)
                                                          #((top) (top))
                                                          #("i" "i"))
                                                        #(ribcage () () ())
                                                        #(ribcage
                                                          #(x)
                                                          #((top))
                                                          #("i")))))))))))
                   syntmp-tmp-2045)
            (syntax-error syntmp-tmp-2044)))
        (syntax-dispatch
          syntmp-tmp-2044
          '(any any))))
     syntmp-x-2043)))

https://git.savannah.gnu.org/cgit/mes.git/tree/mes/module/mes/psyntax.pp.header?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/mes/module/mes/psyntax.pp.header used in:
;;; -*-scheme-*-
;;; GNU Mes --- Maxwell Equations of Software
;;; Copyright (C) 2016, 2017, 2018 Free Software Foundation, Inc.
;;;
;;; This file is part of GNU Mes.
;;;
;;; GNU Mes is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;;; This file is generated from psyntax.ss.

;;; Code:

https://git.savannah.gnu.org/cgit/mes.git/tree/mes/module/srfi/srfi-9-struct.mes?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/mes/module/srfi/srfi-9-struct.mes used in: Step 99
;;; -*-scheme-*-

;;; GNU Mes --- Maxwell Equations of Software
;;; Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
;;;
;;; This file is part of GNU Mes.
;;;
;;; GNU Mes is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;;; srfi-9.mes - records, based on struct.

(define-macro (define-record-type name constructor+params predicate . fields)
  (let ((type (make-record-type name (map car fields))))
   `(begin
      (define ,name ,type)
      (define ,(car constructor+params) ,(record-constructor type name (cdr constructor+params)))
      (define ,predicate ,(record-predicate type))
      (define-record-accessors ,type ,@fields))))

(define (make-record-type type fields . printer)
  (let ((printer (if (pair? printer) (car printer))))
    (make-struct '<record-type> (cons type (list fields)) printer)))

(define (record-type? o)
  (eq? (struct-vtable o) '<record-type>))

(define (struct-vtable o)
  (struct-ref o 0))

(define (record-type o)
  (struct-ref o 2))

(define (record-predicate type)
  (lambda (o)
    (and (record? o)
         (eq? (record-type o) (record-type type)))))

(define (record? o)
  (and (struct? o)
       (record-type? (struct-vtable o))))

(define (record-constructor type name params)
  (let ((fields (record-fields type))
        (record-type (record-type type)))
    (lambda (. o)
      (if (not (= (length o) (length params))) (error "wrong number of arguments for record-constructor")
          (let ((rest (make-list (- (length fields) (length params)))))
            (make-struct type (cons name (append o rest)) record-printer))))))

(define record-printer *unspecified*)   ; TODO
(define (record-printer o)
  (display "#<")
  (display (record-type o))
  (let* ((vtable (struct-vtable o))
         (fields (record-fields vtable)))
    (for-each (lambda (field)
                (display " ")
                (display field)
                (display ": ")
                (display ((record-getter vtable field) o)))
              fields))
  (display ">"))

(define (record-fields o)
  (struct-ref o 3))

(define-macro (define-record-accessors type . fields)
  `(begin
     ,@(map (lambda (field)
              `(define-record-accessor ,type ,field))
            fields)))

(define-macro (define-record-accessor type field)
  `(begin
     (define ,(cadr field) ,(record-getter type (car field)))
     (if ,(pair? (cddr field))
         (define ,(if (pair? (cddr field)) (caddr field)) ,(record-setter type (car field))))))

(define (record-getter type field)
  (let ((i (record-field-index type field)))
    (lambda (o . field?)
      (if (not (eq? (record-type o) (record-type type))) (error "record getter: record expected" type o)
          (if (pair? field?) field
              (struct-ref o i))))))

(define (record-setter type field)
  (let ((i (record-field-index type field)))
    (lambda (o v)
      (if (not (eq? (record-type o) (record-type type))) (error "record setter: record expected" type o)
          (struct-set! o i v)))))

(define (record-field-index type field)
  (+ 3 (or (lst-index (record-fields type) field)
           (error "no such field" type field))))

(define (lst-index lst o)
  (let loop ((lst lst) (i 0))
    (and (pair? lst)
         (if (eq? o (car lst)) i
             (loop (cdr lst) (1+ i))))))

;; (define-record-type <employee>
;;   (make-employee name age salary)
;;   employee?
;;   (name employe-name)
;;   (age employee-age set-employee-age!)
;;   (salary employee-salary))

;; (display <employee>)
;; (newline)

;; (display make-employee)
;; (newline)
;; (display "employee-age ")
;; (display employee-age)
;; (newline)

;; (display "set-employee-age! ")
;; (display set-employee-age!)
;; (newline)

;; (define janneke (make-employee "janneke" 49 42))
;; (display janneke)
;; (newline)

;; (display (employee-age janneke))
;; (newline)

;; (display (set-employee-age! janneke 33))
;; (newline)
;; (display (employee-age janneke))
;; (newline)

https://git.savannah.gnu.org/cgit/mes.git/tree/mes/module/srfi/srfi-9/gnu-struct.mes?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/mes/module/srfi/srfi-9/gnu-struct.mes used in: Step 100
;;; -*-scheme-*-

;;; GNU Mes --- Maxwell Equations of Software
;;; Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
;;;
;;; This file is part of GNU Mes.
;;;
;;; GNU Mes is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;;; srfi-9.mes - GNU immutable records.

(define-macro (define-immutable-record-type type constructor+params predicate . fields)
  `(define-record-type ,type ,constructor+params ,predicate ,@(map (lambda (f) (list-head f 2)) fields)))

(define-macro (set-field o getters value)
  `(let ((getter ,(car getters)))
     (let* ((type (struct-vtable ,o))
            (name (record-type ,o))
            (set (getter ,o #t)))
       (define (field->value field)
         (if (eq? set field) ,value
             ((record-getter type field) ,o)))
       (let* ((fields (record-fields type))
              (values (map field->value fields)))
         (apply (record-constructor type name fields) values)))))

https://git.savannah.gnu.org/cgit/mes.git/tree/include/m2/lib.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/m2/lib.h used in: Step 106
/*
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __M2_LIB_H
#define __M2_LIB_H

char **environ;
int __stdin;
int __stdout;
int __stderr;
int errno;

// CONSTANT EOF 0xffffffff
// CONSTANT __FILEDES_MAX 512

char* cast_intp_to_charp (int *i);
char* cast_long_to_charp (long i);
long cast_charp_to_long (char const *);
long cast_int_to_long (int i);
long cast_voidp_to_long (void const *);

char *itoa (int number);
char *ltoa (long number);
int __ungetc_p (int filedes);
int eputs (char *s);
int oputs (char *s);
int puts (char *s);
size_t strlen (char *s);
ssize_t _write ();
ssize_t write (int filedes, void *buffer, size_t size);
void __ungetc_clear (int filedes);
void __ungetc_init ();
void __ungetc_set (int filedes, int c);

struct timezone
{
  int tz_minuteswest;
  int tz_dsttime;
};

struct timespec
{
  long tv_sec;
  long tv_nsec;
};

struct timeval
{
  long tv_sec;
  long tv_usec;
};

#endif /* __M2_LIB_H */

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-m2/crt1.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-m2/crt1.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib-mini.h"

int __stdin;
int __stdout;
int __stderr;
char **environ;
int main (int argc, char **argv, char **envp);

/* FIXME: this is going to be called `FUNCTION__start' */
//#int
//#_start ()
//#{
//# ..
//#}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-m2/_exit.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-m2/_exit.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

void
_exit ()
{
  asm ("mov____$i32,%eax SYS_exit");
  asm ("mov____0x8(%ebp),%ebx !-4");
  asm ("int____$0x80");
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-m2/_write.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-m2/_write.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

void
_write (int filedes, void *buffer, int size)
{
  asm ("mov____$i32,%eax SYS_write");
  asm ("mov____0x8(%ebp),%ebx !-4");
  asm ("mov____0x8(%ebp),%ecx !-8");
  asm ("mov____0x8(%ebp),%edx !-12");
  asm ("int____$0x80");
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/globals.c?h=v0.24.2

Files:
/*
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib-mini.h>

int errno;
char **environ;
int __stdin;
int __stdout;
int __stderr;

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/cast.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/cast.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

char*
cast_intp_to_charp (int const *i)
{
  return i;
}

char*
cast_long_to_charp (long i)
{
  return i;
}

long
cast_charp_to_long (char const *i)
{
  return i;
}

long
cast_int_to_long (int i)
{
  return i;
}

long
cast_voidp_to_long (void const *i)
{
  return i;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/exit.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/exit.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

void _exit (int code);

void
exit (int code)
{
  _exit (code);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/mini-write.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/mes/mini-write.c used in: Step 106, Step 130
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>

ssize_t
write (int filedes, void const *buffer, size_t size)
{
  int r = _write (filedes, buffer, size);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-m2/syscall.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-m2/syscall.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <errno.h>
#include <linux/x86/syscall.h>

int errno;

int
__sys_call (int sys_call)
{
  asm ("mov____0x8(%ebp),%eax !-4");
  asm ("int____$0x80");
}

int
__sys_call1 (int sys_call, int one)
{
  asm ("mov____0x8(%ebp),%eax !-4");
  asm ("mov____0x8(%ebp),%ebx !-8");
  asm ("int____$0x80");
}

int
__sys_call2 (int sys_call, int one, int two)
{
  asm ("mov____0x8(%ebp),%eax !-4");
  asm ("mov____0x8(%ebp),%ebx !-8");
  asm ("mov____0x8(%ebp),%ecx !-12");
  asm ("int____$0x80");
}

int
__sys_call3 (int sys_call, int one, int two, int three)
{
  asm ("mov____0x8(%ebp),%eax !-4");
  asm ("mov____0x8(%ebp),%ebx !-8");
  asm ("mov____0x8(%ebp),%ecx !-12");
  asm ("mov____0x8(%ebp),%edx !-16");
  asm ("int____$0x80");
}

int
__sys_call4 (int sys_call, int one, int two, int three, int four)
{
  asm ("mov____0x8(%ebp),%eax !-4");
  asm ("mov____0x8(%ebp),%ebx !-8");
  asm ("mov____0x8(%ebp),%ecx !-12");
  asm ("mov____0x8(%ebp),%edx !-16");
  asm ("mov____0x8(%ebp),%esi !-20");
  asm ("int____$0x80");
}

int
_sys_call (int sys_call)
{
  int r = __sys_call (sys_call);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

int
_sys_call1 (int sys_call, int one)
{
  int r = __sys_call1 (sys_call, one);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

int
_sys_call2 (int sys_call, int one, int two)
{
  int r = __sys_call2 (sys_call, one, two);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

int
_sys_call3 (int sys_call, int one, int two, int three)
{
  int r = __sys_call3 (sys_call, one, two, three);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

int
_sys_call4 (int sys_call, int one, int two, int three, int four)
{
  int r = __sys_call4 (sys_call, one, two, three, four);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/__raise.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/stub/__raise.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

// CONSTANT SIGABRT 0

int
__raise (int signum)
{
  return -1;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/brk.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>

long
brk (void *addr)
{
  long long_addr = cast_voidp_to_long (addr);
  return _sys_call1 (SYS_brk, long_addr);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/malloc.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/malloc.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <string.h>

char *__brk = 0;

void *
malloc (size_t size)
{
  if (!__brk)
    __brk = cast_long_to_charp (brk (0));
  if (brk (__brk + size) == -1)
    return 0;
  char *p = __brk;
  __brk = __brk + size;
  return p;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memset.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

char *
_memset (char *s, int c, size_t n)
{
  char *p = s;
  while (n != 0)
    {
      n = n - 1;
      s[0] = c;
      s = s + 1;
    }
  return p;
}

void *
memset (void *s, int c, size_t n)
{
  return _memset (s, c, n);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/read.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/read.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <syscall.h>
#include <mes/lib.h>
#include <fcntl.h>

long
read (int filedes, void *buffer, long size)
{
  long bytes = _sys_call3 (SYS_read, filedes, buffer, size);
  return bytes;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdgetc.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <unistd.h>

int *__ungetc_buf;

int
__ungetc_p (int filedes)
{
  if (__ungetc_buf == 0)
    __ungetc_init ();
  return __ungetc_buf[filedes] >= 0;
}

void
__ungetc_init ()
{
  if (__ungetc_buf == 0)
    {
      int save_errno = errno;
      __ungetc_buf = malloc ((__FILEDES_MAX + 1) * sizeof (int));
      errno = save_errno;
      memset (__ungetc_buf, -1, (__FILEDES_MAX + 1) * sizeof (int));
    }
}

void
__ungetc_clear (int filedes)
{
  if (__ungetc_buf == 0)
    __ungetc_init ();
  __ungetc_buf[filedes] = -1;
}

void
__ungetc_set (int filedes, int c)
{
  if (__ungetc_buf == 0)
    __ungetc_init ();
  __ungetc_buf[filedes] = c;
}

int
fdgetc (int fd)
{
  if (__ungetc_buf == 0)
    __ungetc_init ();

  char c;
  int i = __ungetc_buf[fd];
  if (i >= 0)
    __ungetc_buf[fd] = -1;
  else
    {
      int r = read (fd, &c, 1);
      if (r < 1)
        return -1;
      i = c;
    }
  if (i < 0)
    i = i + 256;

  return i;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/getchar.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

int
getchar ()
{
  return fdgetc (__stdin);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/putchar.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdio.h>

int
putchar (int c)
{
  char *p = cast_intp_to_charp (&c);
  write (__stdout, p, 1);
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/open.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/open.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <mes/lib.h>
#include <fcntl.h>
#include <stdarg.h>

int
open (char *file_name, int flags, int mask)
{
  int r = _sys_call3 (SYS_open, file_name, flags, mask);
  if (r > 2)
    __ungetc_clear (r);
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/mes_open.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/mes_open.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

int
mes_open (char *file_name, int flags, int mask)
{
  int filedes = open (file_name, flags, mask);
  if (filedes > 2)
    __ungetc_clear (filedes);
  return filedes;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strlen.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

size_t
strlen (char const *s)
{
  int i = 0;

  while (s[i] != 0)
    i = i + 1;

  return i;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/eputs.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib-mini.h>
#include <string.h>

int
eputs (char const *s)
{
  int i = strlen (s);
  write (__stderr, s, i);
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdputc.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

int
fdputc (int c, int fd)
{
  char *p = cast_intp_to_charp (&c);
  write (fd, p, 1);
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/eputc.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

int
eputc (int c)
{
  return fdputc (c, __stderr);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/m2/types.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/m2/types.h used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2022,2023 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __M2_TYPES_H
#define __M2_TYPES_H 1

/*
#ifndef __MES_CLOCK_T
#define __MES_CLOCK_T
#undef clock_t
typedef long clock_t;
#endif
*/

#ifndef __MES_DEV_T
#define __MES_DEV_T
#undef dev_t
typedef long dev_t;
#endif

/*
#if !defined (__MES_FILE_T) && ! defined (_FILE_T)
#define __MES_FILE_T
#define _FILE_T
typedef long FILE;
#endif
*/

#ifndef __MES_GID_T
#define __MES_GID_T
#undef gid_t
typedef unsigned gid_t;
#endif

#ifndef __MES_INO_T
#define __MES_INO_T
#undef ino_t
typedef unsigned ino_t;
#endif

#if __SIZEOF_LONG_LONG__ == 8
#ifndef __MES_INO64_T
#define __MES_INO64_T
#undef ino64_t
typedef unsigned ino64_t;
#endif
#endif // __SIZEOF_LONG_LONG__ == 8

#if !defined (__MES_INTPTR_T) && !defined (__intptr_t_defined)
#define __MES_INTPTR_T
#define __intptr_t_defined
#undef intptr_t
typedef long intptr_t;
#undef uintptr_t
typedef unsigned uintptr_t;
#endif

#ifndef __MES_OFF_T
#define __MES_OFF_T
#undef off_t
typedef long off_t;
#endif

#if __SIZEOF_LONG_LONG__ == 8
#ifndef __MES_OFF64_T
#define __MES_OFF64_T
#undef off64_t
typedef unsigned off64_t;
#endif
#endif // __SIZEOF_LONG_LONG__ == 8

#ifndef __MES_PID_T
#define __MES_PID_T
#undef pid_t
typedef int pid_t;
#endif

#ifndef __PTRDIFF_T
#define __PTRDIFF_T
#ifndef __MES_PTRDIFF_T
#define __MES_PTRDIFF_T
#undef ptrdiff_t
typedef long ptrdiff_t;
#endif
#endif

#ifndef __MES_SIGVAL_T
#define __MES_SIGVAL_T
#undef clock_t
typedef long sigval_t;
#endif

#ifndef __SIZE_T
#define __SIZE_T
#ifndef __MES_SIZE_T
#define __MES_SIZE_T
typedef unsigned size_t;
#endif
#endif

#ifndef __MES_SSIZE_T
#define __MES_SSIZE_T
#undef ssize_t
typedef long ssize_t;
#endif

#ifndef __MES_UID_T
#define __MES_UID_T
#undef uid_t
typedef unsigned uid_t;
#endif

#ifndef __WCHAR_T
#define __WCHAR_T
#ifndef __MES_WCHAR_T
#define __MES_WCHAR_T
#undef wchar_t
typedef int wchar_t;
#endif
#endif

#endif // __M2_TYPES_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/mes.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/mes/mes.h used in: Step 106, Step 212, Step 213, Step 214, Step 215, Step 216, Step 217, Step 218, Step 219, Step 220, Step 221, Step 222, Step 223, Step 224, Step 225, Step 226, Step 227, Step 228, Step 229, Step 230, Step 347
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __MES_MES_H
#define __MES_MES_H

#include <sys/types.h>
#include "mes/cc.h"

struct scm
{
  long type;
  union
  {
    struct scm *car;
    long car_value;
    char *bytes;
    long length;
    struct scm *ref;
    struct scm *variable;
    struct scm *macro;
    long port;
  };
  union
  {
    struct scm *cdr;
    long cdr_value;
    struct scm *closure;
    struct scm *continuation;
    char *name;
    struct scm *string;
    struct scm *structure;
    long value;
    FUNCTION function;
    struct scm *vector;
  };
};

/* mes */
extern char *g_datadir;
extern int g_debug;
extern char *g_buf;
extern int g_continuations;
extern struct scm *g_symbols;
extern struct scm *g_symbol_max;
extern int g_mini;

/* a/env */
extern struct scm *R0;
/* param 1 */
extern struct scm *R1;
/* save 2 */
extern struct scm *R2;
/* continuation */
extern struct scm *R3;
/* current-module */
extern struct scm *M0;
/* macro */
extern struct scm *g_macros;
extern struct scm *g_ports;

/* gc */
extern size_t ARENA_SIZE;
extern size_t MAX_ARENA_SIZE;
extern size_t STACK_SIZE;
extern size_t JAM_SIZE;
extern size_t GC_SAFETY;
extern size_t MAX_STRING;
extern char *g_arena;
extern struct scm *cell_arena;
extern struct scm *cell_zero;

extern struct scm *g_free;
extern struct scm *g_symbol;

extern struct scm **g_stack_array;
extern struct scm *g_cells;
extern struct scm *g_news;
extern long g_stack;
extern size_t gc_count;
extern struct timespec *gc_start_time;
extern struct timespec *gc_end_time;
extern size_t gc_time;

extern char **__execl_c_argv;
extern char *__open_boot_buf;
extern char *__open_boot_file_name;
extern char *__setenv_buf;
extern char *__reader_read_char_buf;
extern struct timespec *g_start_time;
extern struct timeval *__gettimeofday_time;
extern struct timespec *__get_internal_run_time_ts;

struct scm *cast_charp_to_scmp (char const *i);
struct scm **cast_charp_to_scmpp (char const *i);
char *cast_voidp_to_charp (void const *i);
long cast_scmp_to_long (struct scm *i);
char *cast_scmp_to_charp (struct scm *i);

struct scm *alloc (long n);
struct scm *apply (struct scm *f, struct scm *x, struct scm *a);
struct scm *apply_builtin (struct scm *fn, struct scm *x);
struct scm *apply_builtin0 (struct scm *fn);
struct scm *apply_builtin1 (struct scm *fn, struct scm *x);
struct scm *apply_builtin2 (struct scm *fn, struct scm *x, struct scm *y);
struct scm *apply_builtin3 (struct scm *fn, struct scm *x, struct scm *y, struct scm *z);
struct scm *builtin_name (struct scm *builtin);
struct scm *cstring_to_list (char const *s);
struct scm *cstring_to_symbol (char const *s);
struct scm *cell_ref (struct scm *cell, long index);
struct scm *fdisplay_ (struct scm *, int, int);
struct scm *init_symbols ();
struct scm *init_time (struct scm *a);
struct scm *make_builtin_type ();
struct scm *make_bytes (char const *s, size_t length);
struct scm *make_cell (long type, struct scm *car, struct scm *cdr);
struct scm *make_pointer_cell (long type, long car, void *cdr);
struct scm *make_value_cell (long type, long car, long cdr);
struct scm *make_char (int n);
struct scm *make_continuation (long n);
struct scm *make_hash_table_ (long size);
struct scm *make_hashq_type ();
struct scm *make_initial_module (struct scm *a);
struct scm *make_macro (struct scm *name, struct scm *x);
struct scm *make_number (long n);
struct scm *make_ref (struct scm *x);
struct scm *make_string (char const *s, size_t length);
struct scm *make_string0 (char const *s);
struct scm *make_string_port (struct scm *x);
struct scm *make_vector_ (long k, struct scm *e);
struct scm *mes_builtins (struct scm *a);
struct scm *push_cc (struct scm *p1, struct scm *p2, struct scm *a, struct scm *c);
struct scm *struct_ref_ (struct scm *x, long i);
struct scm *struct_set_x_ (struct scm *x, long i, struct scm *e);
struct scm *vector_ref_ (struct scm *x, long i);
struct scm *vector_set_x_ (struct scm *x, long i, struct scm *e);
FUNCTION builtin_function (struct scm *builtin);
char *cell_bytes (struct scm *x);
int peekchar ();
int readchar ();
int unreadchar ();
long gc_free ();
long length__ (struct scm *x);
size_t bytes_cells (size_t length);
void assert_max_string (size_t i, char const *msg, char const *string);
void assert_msg (int check, char *msg);
void assert_number (char const *name, struct scm *x);
void copy_cell (struct scm *to, struct scm *from);
void gc_ ();
void gc_dump_arena (struct scm *cells, long size);
void gc_init ();
void gc_peek_frame ();
void gc_pop_frame ();
void gc_push_frame ();
void gc_stats_ (char const* where);
void init_symbols_ ();
long seconds_and_nanoseconds_to_long (long s, long ns);

#include "mes/builtins.h"
#include "mes/constants.h"
#include "mes/symbols.h"

#endif /* __MES_MES_H */

https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/builtins.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/mes/builtins.h used in: Step 106, Step 212, Step 213, Step 214, Step 215, Step 216, Step 217, Step 218, Step 219, Step 220, Step 221, Step 222, Step 223, Step 224, Step 225, Step 226, Step 227, Step 228, Step 229, Step 230, Step 341
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __MES_BUILTINS_H
#define __MES_BUILTINS_H

/* src/builtins.c */
struct scm *make_builtin (struct scm *builtin_type, struct scm *name, struct scm *arity, struct scm *function);
struct scm *builtin_name (struct scm *builtin);
struct scm *builtin_arity (struct scm *builtin);
struct scm *builtin_p (struct scm *x);
struct scm *builtin_printer (struct scm *builtin);
/* src/core.c */
struct scm *car (struct scm *x);
struct scm *cdr (struct scm *x);
struct scm *list (struct scm *x);
struct scm *null_p (struct scm *x);
struct scm *eq_p (struct scm *x, struct scm *y);
struct scm *values (struct scm *x);
struct scm *acons (struct scm *key, struct scm *value, struct scm *alist);
struct scm *length (struct scm *x);
struct scm *error (struct scm *key, struct scm *x);
struct scm *append2 (struct scm *x, struct scm *y);
struct scm *append_reverse (struct scm *x, struct scm *y);
struct scm *reverse_x_ (struct scm *x, struct scm *t);
struct scm *assq (struct scm *x, struct scm *a);
struct scm *assoc (struct scm *x, struct scm *a);
/* src/display.c */
struct scm *display_ (struct scm *x);
struct scm *display_error_ (struct scm *x);
struct scm *display_port_ (struct scm *x, struct scm *p);
struct scm *write_ (struct scm *x);
struct scm *write_error_ (struct scm *x);
struct scm *write_port_ (struct scm *x, struct scm *p);
/* src/eval-apply.c */
struct scm *pairlis (struct scm *x, struct scm *y, struct scm *a);
struct scm *set_car_x (struct scm *x, struct scm *e);
struct scm *set_cdr_x (struct scm *x, struct scm *e);
struct scm *set_env_x (struct scm *x, struct scm *e, struct scm *a);
struct scm *add_formals (struct scm *formals, struct scm *x);
struct scm *eval_apply ();
/* src/gc.c */
struct scm *gc_stats ();
struct scm *cons (struct scm *x, struct scm *y);
struct scm *gc_check ();
struct scm *gc ();
/* src/hash.c */
struct scm *hashq (struct scm *x, struct scm *size);
struct scm *hash (struct scm *x, struct scm *size);
struct scm *hashq_get_handle (struct scm *table, struct scm *key, struct scm *dflt);
struct scm *hashq_ref (struct scm *table, struct scm *key, struct scm *dflt);
struct scm *hash_ref (struct scm *table, struct scm *key, struct scm *dflt);
struct scm *hashq_set_x (struct scm *table, struct scm *key, struct scm *value);
struct scm *hash_set_x (struct scm *table, struct scm *key, struct scm *value);
struct scm *hash_table_printer (struct scm *table);
struct scm *make_hash_table (struct scm *x);
/* src/lib.c */
struct scm *type_ (struct scm *x);
struct scm *car_ (struct scm *x);
struct scm *cdr_ (struct scm *x);
struct scm *xassq (struct scm *x, struct scm *a);
struct scm *memq (struct scm *x, struct scm *a);
struct scm *equal2_p (struct scm *a, struct scm *b);
struct scm *last_pair (struct scm *x);
struct scm *pair_p (struct scm *x);
struct scm *char_to_integer (struct scm *x);
struct scm *integer_to_char (struct scm *x);
/* src/math.c */
struct scm *greater_p (struct scm *x);
struct scm *less_p (struct scm *x);
struct scm *is_p (struct scm *x);
struct scm *minus (struct scm *x);
struct scm *plus (struct scm *x);
struct scm *divide (struct scm *x);
struct scm *modulo (struct scm *a, struct scm *b);
struct scm *multiply (struct scm *x);
struct scm *logand (struct scm *x);
struct scm *logior (struct scm *x);
struct scm *lognot (struct scm *x);
struct scm *logxor (struct scm *x);
struct scm *ash (struct scm *n, struct scm *count);
/* src/module.c */
struct scm *make_module_type ();
struct scm *module_printer (struct scm *module);
struct scm *module_variable (struct scm *module, struct scm *name);
struct scm *module_ref (struct scm *module, struct scm *name);
struct scm *module_define_x (struct scm *module, struct scm *name, struct scm *value);
/* src/posix.c */
struct scm *abort_ ();
struct scm *exit_ (struct scm *x);
struct scm *peek_byte ();
struct scm *read_byte ();
struct scm *unread_byte (struct scm *i);
struct scm *peek_char ();
struct scm *read_char (struct scm *port);
struct scm *unread_char (struct scm *i);
struct scm *write_char (struct scm *i);
struct scm *write_byte (struct scm *x);
struct scm *getenv_ (struct scm *s);
struct scm *setenv_ (struct scm *s, struct scm *v);
struct scm *access_p (struct scm *file_name, struct scm *mode);
struct scm *current_input_port ();
struct scm *open_input_file (struct scm *file_name);
struct scm *open_input_string (struct scm *string);
struct scm *set_current_input_port (struct scm *port);
struct scm *current_output_port ();
struct scm *current_error_port ();
struct scm *open_output_file (struct scm *x);
struct scm *set_current_output_port (struct scm *port);
struct scm *set_current_error_port (struct scm *port);
struct scm *chmod_ (struct scm *file_name, struct scm *mode);
struct scm *isatty_p (struct scm *port);
struct scm *primitive_fork ();
struct scm *execl_ (struct scm *file_name, struct scm *args);
struct scm *waitpid_ (struct scm *pid, struct scm *options);
struct scm *current_time ();
struct scm *gettimeofday_ ();
struct scm *get_internal_run_time ();
struct scm *getcwd_ ();
struct scm *dup_ (struct scm *port);
struct scm *dup2_ (struct scm *old, struct scm *new);
struct scm *delete_file (struct scm *file_name);
/* src/reader.c */
struct scm *read_input_file_env_ (struct scm *e, struct scm *a);
struct scm *read_input_file_env (struct scm *a);
struct scm *read_env (struct scm *a);
struct scm *reader_read_sexp (struct scm *c, struct scm *s, struct scm *a);
struct scm *reader_read_character ();
struct scm *reader_read_binary ();
struct scm *reader_read_octal ();
struct scm *reader_read_hex ();
struct scm *reader_read_string ();
/* src/stack.c */
struct scm *frame_printer (struct scm *frame);
struct scm *make_stack (struct scm *stack);
struct scm *stack_length (struct scm *stack);
struct scm *stack_ref (struct scm *stack, struct scm *index);
/* src/string.c */
struct scm *string_equal_p (struct scm *a, struct scm *b);
struct scm *symbol_to_string (struct scm *symbol);
struct scm *symbol_to_keyword (struct scm *symbol);
struct scm *keyword_to_string (struct scm *keyword);
struct scm *string_to_symbol (struct scm *string);
struct scm *make_symbol (struct scm *string);
struct scm *string_to_list (struct scm *string);
struct scm *list_to_string (struct scm *list);
struct scm *read_string (struct scm *port);
struct scm *string_append (struct scm *x);
struct scm *string_length (struct scm *string);
struct scm *string_ref (struct scm *str, struct scm *k);
/* src/struct.c */
struct scm *make_struct (struct scm *type, struct scm *fields, struct scm *printer);
struct scm *struct_length (struct scm *x);
struct scm *struct_ref (struct scm *x, struct scm *i);
struct scm *struct_set_x (struct scm *x, struct scm *i, struct scm *e);
/* src/vector.c */
struct scm *make_vector (struct scm *x);
struct scm *vector_length (struct scm *x);
struct scm *vector_ref (struct scm *x, struct scm *i);
struct scm *vector_entry (struct scm *x);
struct scm *vector_set_x (struct scm *x, struct scm *i, struct scm *e);
struct scm *list_to_vector (struct scm *x);
struct scm *vector_to_list (struct scm *v);

#endif /* __MES_BUILTINS_H */

https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/constants.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/mes/constants.h used in: Step 106, Step 212, Step 213, Step 214, Step 215, Step 216, Step 217, Step 218, Step 219, Step 220, Step 221, Step 222, Step 223, Step 224, Step 225, Step 226, Step 227, Step 228, Step 229, Step 230, Step 344
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __MES_CONSTANTS_H
#define __MES_CONSTANTS_H

/* Cell types */

// CONSTANT TCHAR          0
#define TCHAR              0

// CONSTANT TBYTES         1
#define TBYTES             1
// CONSTANT TCLOSURE       2
#define TCLOSURE           2
// CONSTANT TCONTINUATION  3
#define TCONTINUATION      3
// CONSTANT TKEYWORD       4
#define TKEYWORD           4
// CONSTANT TMACRO         5
#define TMACRO             5
// CONSTANT TNUMBER        6
#define TNUMBER            6
// CONSTANT TPAIR          7
#define TPAIR              7
// CONSTANT TPORT          8
#define TPORT              8
// CONSTANT TREF           9
#define TREF               9
// CONSTANT TSPECIAL      10
#define TSPECIAL          10
// CONSTANT TSTRING       11
#define TSTRING           11
// CONSTANT TSTRUCT       12
#define TSTRUCT           12
// CONSTANT TSYMBOL       13
#define TSYMBOL           13
// CONSTANT TVALUES       14
#define TVALUES           14
// CONSTANT TVARIABLE     15
#define TVARIABLE         15
// CONSTANT TVECTOR       16
#define TVECTOR           16
// CONSTANT TBROKEN_HEART 17
#define TBROKEN_HEART     17

/* Struct types */

// CONSTANT STRUCT_TYPE 0
#define STRUCT_TYPE 0
// CONSTANT STRUCT_PRINTER 1
#define STRUCT_PRINTER 1

// CONSTANT GC_FRAME_SIZE 5
#define GC_FRAME_SIZE 5
// CONSTANT GC_FRAME_PROCEDURE 4
#define GC_FRAME_PROCEDURE 4

// CONSTANT STDIN 0
// CONSTANT STDOUT 1
// CONSTANT STDERR 2

/* Unknown type 1
// CONSTANT EOF -1
*/

// CONSTANT O_RDONLY 0
// CONSTANT O_WRONLY 1
// CONSTANT O_CREAT 0x40
// CONSTANT O_TRUNC 0x200

// CONSTANT PATH_MAX 1024
// CONSTANT __FILEDES_MAX 512

// CONSTANT S_IRUSR 00400
// CONSTANT S_IWUSR 00200

// CONSTANT CLOCK_PROCESS_CPUTIME_ID 2


#endif /* __MES_CONSTANTS_H */

https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/symbols.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/mes/symbols.h used in: Step 106, Step 212, Step 213, Step 214, Step 215, Step 216, Step 217, Step 218, Step 219, Step 220, Step 221, Step 222, Step 223, Step 224, Step 225, Step 226, Step 227, Step 228, Step 229, Step 230, Step 348
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __MES_SYMBOLS_H
#define __MES_SYMBOLS_H

extern struct scm *cell_nil;
extern struct scm *cell_f;
extern struct scm *cell_t;
extern struct scm *cell_dot;
extern struct scm *cell_arrow;
extern struct scm *cell_undefined;
extern struct scm *cell_unspecified;
extern struct scm *cell_closure;
extern struct scm *cell_circular;

extern struct scm *cell_vm_apply;
extern struct scm *cell_vm_apply2;
extern struct scm *cell_vm_begin;
extern struct scm *cell_vm_begin_eval;
extern struct scm *cell_vm_begin_expand;
extern struct scm *cell_vm_begin_expand_eval;
extern struct scm *cell_vm_begin_expand_macro;
extern struct scm *cell_vm_begin_expand_primitive_load;
extern struct scm *cell_vm_begin_primitive_load;
extern struct scm *cell_vm_begin_read_input_file;
extern struct scm *cell_vm_call_with_current_continuation2;
extern struct scm *cell_vm_call_with_values2;
extern struct scm *cell_vm_eval;
extern struct scm *cell_vm_eval2;
extern struct scm *cell_vm_eval_check_func;
extern struct scm *cell_vm_eval_define;
extern struct scm *cell_vm_eval_macro_expand_eval;
extern struct scm *cell_vm_eval_macro_expand_expand;
extern struct scm *cell_vm_eval_pmatch_car;
extern struct scm *cell_vm_eval_pmatch_cdr;
extern struct scm *cell_vm_eval_set_x;
extern struct scm *cell_vm_evlis;
extern struct scm *cell_vm_evlis2;
extern struct scm *cell_vm_evlis3;
extern struct scm *cell_vm_if;
extern struct scm *cell_vm_if_expr;
extern struct scm *cell_vm_macro_expand;
extern struct scm *cell_vm_macro_expand_car;
extern struct scm *cell_vm_macro_expand_cdr;
extern struct scm *cell_vm_macro_expand_define;
extern struct scm *cell_vm_macro_expand_define_macro;
extern struct scm *cell_vm_macro_expand_lambda;
extern struct scm *cell_vm_macro_expand_set_x;
extern struct scm *cell_vm_return;

extern struct scm *cell_symbol_lambda;
extern struct scm *cell_symbol_begin;
extern struct scm *cell_symbol_if;
extern struct scm *cell_symbol_quote;
extern struct scm *cell_symbol_define;
extern struct scm *cell_symbol_define_macro;
extern struct scm *cell_symbol_quasiquote;
extern struct scm *cell_symbol_unquote;
extern struct scm *cell_symbol_unquote_splicing;
extern struct scm *cell_symbol_syntax;
extern struct scm *cell_symbol_quasisyntax;
extern struct scm *cell_symbol_unsyntax;
extern struct scm *cell_symbol_unsyntax_splicing;
extern struct scm *cell_symbol_set_x;
extern struct scm *cell_symbol_sc_expand;
extern struct scm *cell_symbol_macro_expand;
extern struct scm *cell_symbol_portable_macro_expand;
extern struct scm *cell_symbol_sc_expander_alist;
extern struct scm *cell_symbol_call_with_values;
extern struct scm *cell_symbol_call_with_current_continuation;
extern struct scm *cell_symbol_boot_module;
extern struct scm *cell_symbol_current_module;
extern struct scm *cell_symbol_primitive_load;
extern struct scm *cell_symbol_car;
extern struct scm *cell_symbol_cdr;
extern struct scm *cell_symbol_not_a_number;
extern struct scm *cell_symbol_not_a_pair;
extern struct scm *cell_symbol_system_error;
extern struct scm *cell_symbol_throw;
extern struct scm *cell_symbol_unbound_variable;
extern struct scm *cell_symbol_wrong_number_of_args;
extern struct scm *cell_symbol_wrong_type_arg;
extern struct scm *cell_symbol_buckets;
extern struct scm *cell_symbol_builtin;
extern struct scm *cell_symbol_frame;
extern struct scm *cell_symbol_hashq_table;
extern struct scm *cell_symbol_module;
extern struct scm *cell_symbol_procedure;
extern struct scm *cell_symbol_record_type;
extern struct scm *cell_symbol_size;
extern struct scm *cell_symbol_stack;
extern struct scm *cell_symbol_argv;
extern struct scm *cell_symbol_mes_datadir;
extern struct scm *cell_symbol_mes_version;
extern struct scm *cell_symbol_internal_time_units_per_second;
extern struct scm *cell_symbol_compiler;
extern struct scm *cell_symbol_arch;
extern struct scm *cell_symbol_pmatch_car;
extern struct scm *cell_symbol_pmatch_cdr;
extern struct scm *cell_type_bytes;
extern struct scm *cell_type_char;
extern struct scm *cell_type_closure;
extern struct scm *cell_type_continuation;
extern struct scm *cell_type_function;
extern struct scm *cell_type_keyword;
extern struct scm *cell_type_macro;
extern struct scm *cell_type_number;
extern struct scm *cell_type_pair;
extern struct scm *cell_type_port;
extern struct scm *cell_type_ref;
extern struct scm *cell_type_special;
extern struct scm *cell_type_string;
extern struct scm *cell_type_struct;
extern struct scm *cell_type_symbol;
extern struct scm *cell_type_values;
extern struct scm *cell_type_variable;
extern struct scm *cell_type_vector;
extern struct scm *cell_type_broken_heart;
extern struct scm *cell_symbol_program;
extern struct scm *cell_symbol_test;

// CONSTANT SYMBOL_MAX 114
#define SYMBOL_MAX 114

// CONSTANT CELL_UNSPECIFIED 7
#define CELL_UNSPECIFIED 7

// CONSTANT CELL_SYMBOL_RECORD_TYPE 82
#define CELL_SYMBOL_RECORD_TYPE 82


#endif /* __MES_SYMBOLS_H */

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/__assert_fail.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <assert.h>

void
__assert_fail (char const *msg, char const *file, unsigned line,
               char const *function)
{
  if (file && file[0])
    {
      eputs (file);
      eputs (":");
    }
  if (line)
    {
      eputs (itoa (line));
      eputs (":");
    }
  if (function && function[0])
    {
      eputs (function);
      eputs (":");
    }
  eputs ("assert fail: ");
  eputs (msg);
  eputs ("\n");
  char *fail = 0;
  fail[0] = 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/assert_msg.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jeremiah Orians <jeremiah@pdp10.guru>
 * Copyright © 2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <assert.h>
#include <stdlib.h>

void
assert_msg (int bool, char *msg)
{
  if (bool == 0)
    __assert_fail (msg, 0, 0, 0);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strncmp.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

int
strncmp (char const *a, char const *b, size_t size)
{
  if (size == 0)
    return 0;

  while (a[0] != 0 && b[0] != 0 && a[0] == b[0] && size > 1)
    {
      size = size - 1;
      a = a + 1;
      b = b + 1;
    }

  return a[0] - b[0];
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/getenv.c?h=v0.24.2

Files:
/*
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <string.h>
#include <stdlib.h>

// CONSTANT M2_PTR_SIZE 4
#define M2_PTR_SIZE 1

char *
getenv (char const *s)
{
  /* eputs ("\ngetenv s="); eputs (s); eputs ("\n"); */
  char **p = environ;
  char *q;
  int length = strlen (s);

  while (p[0] != 0)
    {
      /* eputs ("getenv p[0]="); eputs (p[0]); eputs ("\n"); */
      if (strncmp (s, p[0], length) == 0)
        {
          /* eputs ("found!\n"); */
          q = p[0] + length;
          if (q[0] == '=')
            return q + 1;
        }
      /* else */
      /*   eputs ("not found!\n"); */
      p = p + M2_PTR_SIZE;
    }

  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdputs.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <string.h>

int
fdputs (char const *s, int fd)
{
  int i = strlen (s);
  write (fd, s, i);
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ntoab.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#if __M2_PLANET__ || (!(__MESC__ && __arm__) && !(__TINYC__ && __arm__ && BOOTSTRAP))
size_t
__mesabi_uldiv (size_t a, size_t b, size_t *remainder)
{
  remainder[0] = a % b;
  return a / b;
}
#endif

char *__itoa_buf;

char *
ntoab (long x, unsigned base, int signed_p)
{
  if (__itoa_buf == 0)
    __itoa_buf = malloc (20);
  char *p = __itoa_buf + 11;

  p[0] = 0;
  p = p - 1;
  assert_msg (base > 0, "base > 0");

  int sign_p = 0;
  size_t i;
  size_t u;
  size_t b = base;
  if (signed_p != 0 && x < 0)
    {
      sign_p = 1;
      /* Avoid LONG_MIN */
      u = (-(x + 1));
      u = u + 1;
    }
  else
    u = x;

  do
    {
      u = __mesabi_uldiv (u, b, &i);
      if (i > 9)
        p[0] = 'a' + i - 10;
      else
        p[0] = '0' + i;
      p = p - 1;
    }
  while (u != 0);

  if (sign_p && p[1] != '0')
    {
      p[0] = '-';
      p = p - 1;
    }

  return p + 1;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isdigit.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2023 Rick Masters <grick23@gmail.com>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

int
isdigit (int c)
{
  return c >= '0' && c <= '9';
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isxdigit.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <ctype.h>

int
isxdigit (int c)
{
  return isdigit (c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isspace.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

int
isspace (int c)
{
  return (c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r' || c == ' ');
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isnumber.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>

int
isnumber (int c, int base)
{
  if (base == 2)
    return (c >= '0') && (c <= '1');
  if (base == 8)
    return (c >= '0') && (c <= '7');
  if (base == 10)
    return isdigit (c);
  if (base == 16)
    return isxdigit (c);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/abtol.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2022,2023 Rick Masters <grick23@gmail.com>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <ctype.h>

long
abtol (char const **p, int base)
{
  char const *s = p[0];
  int i = 0;
  int sign_p = 0;
  int m = '0';
  if (base == 0)
    base = 10;
  while (isspace (s[0]) != 0)
    s = s + 1;
  if (s[0] != 0 && s[0] == '+')
    s = s + 1;
  if (s[0] != 0 && s[0] == '-')
    {
      sign_p = 1;
      s = s + 1;
    }
  while (isnumber (s[0], base) != 0)
    {
      i = i * base;
      if (s[0] >= 'a')
        m = 'a' - 10;
      else
        {
          if (s[0] >= 'A')
            m = 'A' - 10;
          else
            m = '0';
        }
      i = i + s[0] - m;
      s = s + 1;
    }
  p[0] = s;
  if (sign_p != 0)
    return -i;

  return i;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/atoi.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

int
atoi (char const *string)
{
  char const *p = string;
  return abtol (&p, 0);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memcpy.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <string.h>

char *
_memcpy (char *dest, char const *src, size_t n)
{
  char *p = dest;

  while (n != 0)
    {
      n = n - 1;
      dest[0] = src[0];
      dest = dest + 1;
      src = src + 1;
    }

  return p;
}

void *
memcpy (void *dest, void const *src, size_t n)
{
  return _memcpy (dest, src, n);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/free.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>

void
free (void *ptr)
{
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/realloc.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <string.h>

void *
realloc (void *ptr, size_t size)
{
  void *new = malloc (size);
  if (ptr != 0 && new != 0)
    {
      memcpy (new, ptr, size);
      free (ptr);
    }
  return new;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcpy.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

char *
strcpy (char *dest, char const *src)
{
  char *p = dest;

  while (src[0] != 0)
    {
      p[0] = src[0];
      p = p + 1;
      src = src + 1;
    }
  p[0] = 0;

  return dest;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/itoa.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

char *
itoa (int x)
{
  return ntoab (x, 10, 1);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ltoa.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

char *
ltoa (long x)
{
  return ntoab (x, 10, 1);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdungetc.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

int
fdungetc (int c, int fd)
{
  if (c == -1)
    return c;
  else if (__ungetc_p (fd))
    {
      eputs (" ***MES C LIB*** fdungetc ungetc buffer overflow fd=");
      eputs (itoa (fd));
      eputs ("\n");
      assert_msg (0, "0");
    }
  __ungetc_set (fd, c);
  return c;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/setenv.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <string.h>
#include <stdlib.h>

// CONSTANT M2_PTR_SIZE 4
#define M2_PTR_SIZE 1

int
setenv (char const *s, char const *v, int overwrite_p)
{
  char **p = environ;
  int length = strlen (s);
  char* q;

  while (p[0] != 0)
    {
      if (strncmp (s, p[0], length) == 0)
        {
          q = p[0] + length;
          if (q[0] == '=')
            break;
        }
      p = p + M2_PTR_SIZE;
    }
  char *entry = malloc (length + strlen (v) + 2);
  int end_p = p[0] == 0;
  p[0] = entry;
  strcpy (entry, s);
  strcpy (entry + length, "=");
  strcpy (entry + length + 1, v);
  entry[length + strlen (v) + 2] = 0;
  if (end_p != 0)
    p[1] = 0;

  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/access.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>

int
access (char const *file_name, int how)
{
  long long_file_name = cast_charp_to_long (file_name);
  long long_how = cast_int_to_long (how);
  return _sys_call2 (SYS_access, long_file_name, long_how);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/linux/m2/kernel-stat.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/linux/m2/kernel-stat.h used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __LINUX_M2_KERNEL_STAT_H
#define __LINUX_M2_KERNEL_STAT_H

/* https://github.com/torvalds/linux/blob/master/arch/x86/include/uapi/asm/stat.h */

/* *INDENT-OFF* */
struct stat
{
  unsigned st_dev;
  unsigned st_ino;
  char st_mode[2];
  char st_nlink[2];
  char st_uid[2];
  char st_gid[2];
  unsigned st_rdev;
  unsigned st_size;
  unsigned st_blksize;
  unsigned st_blocks;
  unsigned st_atime;
  unsigned st_atime_usec;
  unsigned st_mtime;
  unsigned st_mtime_usec;
  unsigned st_ctime;
  unsigned st_ctime_usec;
  unsigned __pad0;
  unsigned __pad1;
};
#endif /* __LINUX_M2_KERNEL_STAT_H */

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/stat.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2022 Dor Askayo <dor.askayo@gmail.com>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_STAT_H
#define __MES_SYS_STAT_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_STAT_H
#include_next <sys/stat.h>

#else // ! SYSTEM_LIBC

#include <time.h>
#include <sys/types.h>
#include <arch/kernel-stat.h>

#ifndef __MES_MODE_T
#define __MES_MODE_T
typedef int mode_t;
#endif

int chmod (char const *file_name, mode_t mode);
int fstat (int filedes, struct stat *buf);
int mkdir (char const *file_name, mode_t mode);
int mknod (char const *file_name, mode_t mode, dev_t dev);
int chown (char const *file_name, uid_t owner, gid_t group);
int rmdir (char const *file_name);
int stat (char const *file_name, struct stat *buf);

#define S_IFIFO 0010000
#define S_IFCHR 0020000
#define S_IFDIR 0040000
#define S_IFBLK 0060000
#define S_IFREG 0100000
#define S_IFLNK 0120000
#define S_IFMT  0170000

#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)

#define S_IRWXU 00700
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400

#define S_ISUID 04000
#define S_ISGID 02000
#define S_IXGRP 00010
#define S_IXOTH 00001
#define S_IRGRP 00040
#define S_IROTH 00004
#define S_IWGRP 00020
#define S_IWOTH 00002
#define S_IRWXG 00070
#define S_IRWXO 00007

#endif // ! SYSTEM_LIBC

#endif // __MES_SYS_STAT_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/chmod.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/chmod.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <syscall.h>
#include <sys/stat.h>

int
chmod (char const *file_name, int mask)
{
  long long_file_name = file_name;
  long long_mask = mask;
  return _sys_call2 (SYS_chmod, long_file_name, long_mask);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/ioctl3.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <mes/lib.h>

int
ioctl3 (int filedes, size_t command, long data)
{
  long long_filedes = cast_int_to_long (filedes);
  int r = _sys_call3 (SYS_ioctl, long_filedes, command, data);
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/isatty.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/isatty.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <termio.h>

// CONSTANT TCGETS 0x5401
#define TCGETS 0x5401

struct ktermios
{
  unsigned c_iflag;
  unsigned c_oflag;
  unsigned c_cflag;
  unsigned c_lflag;
  char c_line;
  char c_cc[19];
  unsigned c_ispeed;
  unsigned c_ospeed;
};

struct ktermios *__isatty_kernel_termios;

int
isatty (int filedes)
{
  if (__isatty_kernel_termios == 0)
    __isatty_kernel_termios = malloc (sizeof (struct ktermios));
  int r = ioctl3 (filedes, TCGETS, __isatty_kernel_termios);
  return r == 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fork.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>

int
fork ()
{
  return _sys_call (SYS_fork);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/execve.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/execve.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>

int
execve (char const *file_name, char **argv, char **env)
{
  long long_file_name = file_name;
  long long_argv = argv;
  long long_env = env;
  return _sys_call3 (SYS_execve, file_name, argv, env);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/execv.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/execv.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

int
execv (char *file_name, char **argv)
{
  return execve (file_name, argv, environ);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/waitpid.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/waitpid.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/types.h>

int
waitpid (int pid, int *status_ptr, int options)
{
  long long_pid = pid;
  long long_status_ptr = cast_voidp_to_long (status_ptr);
  long long_options = options;
//##if __i386__
//#  return _sys_call3 (SYS_waitpid, long_pid, long_status_ptr, long_options);
//##elif __x86_64__ || __arm__
  return _sys_call4 (SYS_wait4, long_pid, long_status_ptr, long_options, 0);
//##else
//##error arch not supported
//##endif
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/gettimeofday.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/time.h>

int
gettimeofday (struct timeval *tv, struct timezone *tz)
{
  long long_tv = cast_voidp_to_long (tv);
  long long_tz = cast_voidp_to_long (tz);
  return _sys_call2 (SYS_gettimeofday, long_tv, long_tz);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/clock_gettime.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/clock_gettime.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <syscall.h>
#include <time.h>

int
clock_gettime (long clk_id, struct timespec *tp)
{
  long long_tp = tp;
  return _sys_call2 (SYS_clock_gettime, clk_id, tp);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/time.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/time.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022,2023 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <time.h>

char *__tv;

long
time (long* result)
{
  int r;
  if (__tv == 0)
    __tv = malloc (sizeof (struct timeval));
  struct timeval *tv = __tv;
  r = _sys_call2 (SYS_gettimeofday, tv, 0);
  if (r != 0)
    return -1;
  if (result != 0)
    result[0] = tv->tv_sec;
  return tv->tv_sec;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/_getcwd.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>

char *
_getcwd (char *buffer, size_t size)
{
  long long_buffer = cast_charp_to_long (buffer);
  int r = _sys_call2 (SYS_getcwd, long_buffer, size);
  if (r >= 0)
    return buffer;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/getcwd.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/getcwd.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <mes/mes.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/types.h>

// CONSTANT PATH_MAX 1024

char *__getcwd_buf;

char *
getcwd (char *buffer, int size)
{
  if (buffer == 0)
    buffer = __getcwd_buf;
  if (buffer == 0)
    {
      __getcwd_buf = malloc (PATH_MAX);
      buffer = __getcwd_buf;
    }
  return _getcwd (buffer, size);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/dup.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>

int
dup (int old)
{
  long long_old = old;
  return _sys_call1 (SYS_dup, long_old);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/dup2.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>

int
dup2 (int old, int new)
{
  long long_old = old;
  long long_new = new;
  return _sys_call2 (SYS_dup2, long_old, long_new);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcmp.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

int
strcmp (char const *a, char const *b)
{
  while (a[0] != 0 && b[0] != 0 && a[0] == b[0])
    {
      a = a + 1;
      b = b + 1;
    }

  return a[0] - b[0];
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memcmp.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

int
memcmp (void const *s1, void const *s2, size_t size)
{
  if (size == 0)
    return 0;

  char const *a = s1;
  char const *b = s2;

  while (a[0] == b[0] && size > 1)
    {
      size = size - 1;
      a = a + 1;
      b = b + 1;
    }

  return a[0] - b[0];
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/unlink.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>

int
unlink (char const *file_name)
{
  long long_file_name = cast_charp_to_long (file_name);
  return _sys_call1 (SYS_unlink, long_file_name);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/builtins.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/builtins.c used in: Step 106, Step 212
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

struct scm *
make_builtin_type ()            /*:((internal)) */
{
  struct scm *record_type = cell_symbol_record_type;
  struct scm *fields = cell_nil;
  fields = cons (cstring_to_symbol ("address"), fields);
  fields = cons (cstring_to_symbol ("arity"), fields);
  fields = cons (cstring_to_symbol ("name"), fields);
  fields = cons (fields, cell_nil);
  fields = cons (cell_symbol_builtin, fields);
  return make_struct (record_type, fields, cell_unspecified);
}

struct scm *
make_builtin (struct scm *builtin_type, struct scm *name, struct scm *arity, struct scm *function)
{
  struct scm *values = cell_nil;
  values = cons (function, values);
  values = cons (arity, values);
  values = cons (name, values);
  values = cons (cell_symbol_builtin, values);
  return make_struct (builtin_type, values, cstring_to_symbol ("builtin-printer"));
}

struct scm *
builtin_name (struct scm *builtin)
{
  return struct_ref_ (builtin, 3);
}

struct scm *
builtin_arity (struct scm *builtin)
{
  return struct_ref_ (builtin, 4);
}

FUNCTION
builtin_function (struct scm *builtin)
{
  struct scm *x = struct_ref_ (builtin, 5);
  return x->function;
}

struct scm *
builtin_p (struct scm *x)
{
  if (x->type == TSTRUCT)
    if (struct_ref_ (x, 2) == cell_symbol_builtin)
      return cell_t;
  return cell_f;
}

struct scm *
builtin_printer (struct scm *builtin)
{
  fdputs ("#<procedure ", __stdout);
  display_ (builtin_name (builtin));
  fdputc (' ', __stdout);
  struct scm *x = builtin_arity (builtin);
  int arity = x->value;
  if (arity == -1)
    fdputc ('_', __stdout);
  else
    {
      fdputc ('(', __stdout);
      int i;
      for (i = 0; i < arity; i = i + 1)
        {
          if (i != 0)
            fdputc (' ', __stdout);
          fdputc ('_', __stdout);
        }
    }
  fdputc ('>', __stdout);
}

struct scm *
init_builtin (struct scm *builtin_type, char const *name, int arity, void* function, struct scm *a)
{
  struct scm *s = cstring_to_symbol (name);
  long n = cast_voidp_to_long (function);
  return acons (s,
                make_builtin (builtin_type, symbol_to_string (s), make_number (arity),
                              make_number (n)), a);
}

struct scm *
mes_builtins (struct scm *a)            /*:((internal)) */
{
  struct scm *builtin_type = make_builtin_type ();

  if (g_mini != 0)
    {
      a = init_builtin (builtin_type, "cons", 2, &cons, a);
      a = init_builtin (builtin_type, "car", 1, &car, a);
      a = init_builtin (builtin_type, "list", -1, &list, a);
      a = init_builtin (builtin_type, "eq?", 2, &eq_p, a);
      a = init_builtin (builtin_type, "-", -1, &minus, a);
      a = init_builtin (builtin_type, "+", -1, &plus, a);
      a = init_builtin (builtin_type, "core:display", 1, &display_, a);
      a = init_builtin (builtin_type, "core:write", 1, &write_, a);
      a = init_builtin (builtin_type, "core:display-error", 1, &display_error_, a);
      a = init_builtin (builtin_type, "getenv", 1, &getenv_, a);
      a = init_builtin (builtin_type, "gc", 0, &gc, a);
      a = init_builtin (builtin_type, ">", -1, &greater_p, a);
      a = init_builtin (builtin_type, "<", -1, &less_p, a);
      a = init_builtin (builtin_type, "make-vector", -1, &make_vector, a);
      return a;
    }

  /* src/builtins.c */
  a = init_builtin (builtin_type, "make-builtin", 4, &make_builtin, a);
  a = init_builtin (builtin_type, "builtin-name", 1, &builtin_name, a);
  a = init_builtin (builtin_type, "builtin-arity", 1, &builtin_arity, a);
  a = init_builtin (builtin_type, "builtin?", 1, &builtin_p, a);
  a = init_builtin (builtin_type, "builtin-printer", 1, &builtin_printer, a);
  /* src/core.c */
  a = init_builtin (builtin_type, "car", 1, &car, a);
  a = init_builtin (builtin_type, "cdr", 1, &cdr, a);
  a = init_builtin (builtin_type, "list", -1, &list, a);
  a = init_builtin (builtin_type, "null?", 1, &null_p, a);
  a = init_builtin (builtin_type, "eq?", 2, &eq_p, a);
  a = init_builtin (builtin_type, "values", -1, &values, a);
  a = init_builtin (builtin_type, "acons", 3, &acons, a);
  a = init_builtin (builtin_type, "length", 1, &length, a);
  a = init_builtin (builtin_type, "error", 2, &error, a);
  a = init_builtin (builtin_type, "append2", 2, &append2, a);
  a = init_builtin (builtin_type, "append-reverse", 2, &append_reverse, a);
  a = init_builtin (builtin_type, "core:reverse!", 2, &reverse_x_, a);
  a = init_builtin (builtin_type, "assq", 2, &assq, a);
  a = init_builtin (builtin_type, "assoc", 2, &assoc, a);
  /* src/display.c */
  a = init_builtin (builtin_type, "core:display", 1, &display_, a);
  a = init_builtin (builtin_type, "core:display-error", 1, &display_error_, a);
  a = init_builtin (builtin_type, "core:display-port", 2, &display_port_, a);
  a = init_builtin (builtin_type, "core:write", 1, &write_, a);
  a = init_builtin (builtin_type, "core:write-error", 1, &write_error_, a);
  a = init_builtin (builtin_type, "core:write-port", 2, &write_port_, a);
  /* src/eval-apply.c */
  a = init_builtin (builtin_type, "pairlis", 3, &pairlis, a);
  a = init_builtin (builtin_type, "set-car!", 2, &set_car_x, a);
  a = init_builtin (builtin_type, "set-cdr!", 2, &set_cdr_x, a);
  a = init_builtin (builtin_type, "set-env!", 3, &set_env_x, a);
  a = init_builtin (builtin_type, "add-formals", 2, &add_formals, a);
  a = init_builtin (builtin_type, "eval-apply", 0, &eval_apply, a);
  /* src/gc.c */
  a = init_builtin (builtin_type, "gc-stats", 0, &gc_stats, a);
  a = init_builtin (builtin_type, "cons", 2, &cons, a);
  a = init_builtin (builtin_type, "gc-check", 0, &gc_check, a);
  a = init_builtin (builtin_type, "gc", 0, &gc, a);
  /* src/hash.c */
  a = init_builtin (builtin_type, "hashq", 2, &hashq, a);
  a = init_builtin (builtin_type, "hash", 2, &hash, a);
  a = init_builtin (builtin_type, "hashq-get-handle", 3, &hashq_get_handle, a);
  a = init_builtin (builtin_type, "hashq-ref", 3, &hashq_ref, a);
  a = init_builtin (builtin_type, "hash-ref", 3, &hash_ref, a);
  a = init_builtin (builtin_type, "hashq-set!", 3, &hashq_set_x, a);
  a = init_builtin (builtin_type, "hash-set!", 3, &hash_set_x, a);
  a = init_builtin (builtin_type, "hash-table-printer", 1, &hash_table_printer, a);
  a = init_builtin (builtin_type, "make-hash-table", 1, &make_hash_table, a);
  /* src/lib.c */
  a = init_builtin (builtin_type, "core:type", 1, &type_, a);
  a = init_builtin (builtin_type, "core:car", 1, &car_, a);
  a = init_builtin (builtin_type, "core:cdr", 1, &cdr_, a);
  a = init_builtin (builtin_type, "xassq", 2, &xassq, a);
  a = init_builtin (builtin_type, "memq", 2, &memq, a);
  a = init_builtin (builtin_type, "equal2?", 2, &equal2_p, a);
  a = init_builtin (builtin_type, "last-pair", 1, &last_pair, a);
  a = init_builtin (builtin_type, "pair?", 1, &pair_p, a);
  a = init_builtin (builtin_type, "char->integer", 1, &char_to_integer, a);
  a = init_builtin (builtin_type, "integer->char", 1, &integer_to_char, a);
  /* src/math.c */
  a = init_builtin (builtin_type, ">", -1, &greater_p, a);
  a = init_builtin (builtin_type, "<", -1, &less_p, a);
  a = init_builtin (builtin_type, "=", -1, &is_p, a);
  a = init_builtin (builtin_type, "-", -1, &minus, a);
  a = init_builtin (builtin_type, "+", -1, &plus, a);
  a = init_builtin (builtin_type, "/", -1, &divide, a);
  a = init_builtin (builtin_type, "modulo", 2, &modulo, a);
  a = init_builtin (builtin_type, "*", -1, &multiply, a);
  a = init_builtin (builtin_type, "logand", -1, &logand, a);
  a = init_builtin (builtin_type, "logior", -1, &logior, a);
  a = init_builtin (builtin_type, "lognot", 1, &lognot, a);
  a = init_builtin (builtin_type, "logxor", -1, &logxor, a);
  a = init_builtin (builtin_type, "ash", 2, &ash, a);
  /* src/module.c */
  a = init_builtin (builtin_type, "make-module-type", 0, &make_module_type, a);
  a = init_builtin (builtin_type, "module-printer", 1, &module_printer, a);
  a = init_builtin (builtin_type, "module-variable", 2, &module_variable, a);
  a = init_builtin (builtin_type, "module-ref", 2, &module_ref, a);
  a = init_builtin (builtin_type, "module-define!", 3, &module_define_x, a);
  /* src/posix.c */
  a = init_builtin (builtin_type, "abort", 0, &abort_, a);
  a = init_builtin (builtin_type, "exit", 1, &exit_, a);
  a = init_builtin (builtin_type, "peek-byte", 0, &peek_byte, a);
  a = init_builtin (builtin_type, "read-byte", 0, &read_byte, a);
  a = init_builtin (builtin_type, "unread-byte", 1, &unread_byte, a);
  a = init_builtin (builtin_type, "peek-char", 0, &peek_char, a);
  a = init_builtin (builtin_type, "read-char", -1, &read_char, a);
  a = init_builtin (builtin_type, "unread-char", 1, &unread_char, a);
  a = init_builtin (builtin_type, "write-char", -1, &write_char, a);
  a = init_builtin (builtin_type, "write-byte", -1, &write_byte, a);
  a = init_builtin (builtin_type, "getenv", 1, &getenv_, a);
  a = init_builtin (builtin_type, "setenv", 2, &setenv_, a);
  a = init_builtin (builtin_type, "access?", 2, &access_p, a);
  a = init_builtin (builtin_type, "current-input-port", 0, &current_input_port, a);
  a = init_builtin (builtin_type, "open-input-file", 1, &open_input_file, a);
  a = init_builtin (builtin_type, "open-input-string", 1, &open_input_string, a);
  a = init_builtin (builtin_type, "set-current-input-port", 1, &set_current_input_port, a);
  a = init_builtin (builtin_type, "current-output-port", 0, &current_output_port, a);
  a = init_builtin (builtin_type, "current-error-port", 0, &current_error_port, a);
  a = init_builtin (builtin_type, "open-output-file", -1, &open_output_file, a);
  a = init_builtin (builtin_type, "set-current-output-port", 1, &set_current_output_port, a);
  a = init_builtin (builtin_type, "set-current-error-port", 1, &set_current_error_port, a);
  a = init_builtin (builtin_type, "chmod", 2, &chmod_, a);
  a = init_builtin (builtin_type, "isatty?", 1, &isatty_p, a);
  a = init_builtin (builtin_type, "primitive-fork", 0, &primitive_fork, a);
  a = init_builtin (builtin_type, "execl", 2, &execl_, a);
  a = init_builtin (builtin_type, "core:waitpid", 2, &waitpid_, a);
  a = init_builtin (builtin_type, "current-time", 0, &current_time, a);
  a = init_builtin (builtin_type, "gettimeofday", 0, &gettimeofday_, a);
  a = init_builtin (builtin_type, "get-internal-run-time", 0, &get_internal_run_time, a);
  a = init_builtin (builtin_type, "getcwd", 0, &getcwd_, a);
  a = init_builtin (builtin_type, "dup", 1, &dup_, a);
  a = init_builtin (builtin_type, "dup2", 2, &dup2_, a);
  a = init_builtin (builtin_type, "delete-file", 1, &delete_file, a);
  /* src/reader.c */
  a = init_builtin (builtin_type, "core:read-input-file-env", 2, &read_input_file_env_, a);
  a = init_builtin (builtin_type, "read-input-file-env", 1, &read_input_file_env, a);
  a = init_builtin (builtin_type, "read-env", 1, &read_env, a);
  a = init_builtin (builtin_type, "reader-read-sexp", 3, &reader_read_sexp, a);
  a = init_builtin (builtin_type, "reader-read-character", 0, &reader_read_character, a);
  a = init_builtin (builtin_type, "reader-read-binary", 0, &reader_read_binary, a);
  a = init_builtin (builtin_type, "reader-read-octal", 0, &reader_read_octal, a);
  a = init_builtin (builtin_type, "reader-read-hex", 0, &reader_read_hex, a);
  a = init_builtin (builtin_type, "reader-read-string", 0, &reader_read_string, a);
  /* src/stack.c */
  a = init_builtin (builtin_type, "frame-printer", 1, &frame_printer, a);
  a = init_builtin (builtin_type, "make-stack", -1, &make_stack, a);
  a = init_builtin (builtin_type, "stack-length", 1, &stack_length, a);
  a = init_builtin (builtin_type, "stack-ref", 2, &stack_ref, a);
  /* src/string.c */
  a = init_builtin (builtin_type, "string=?", 2, &string_equal_p, a);
  a = init_builtin (builtin_type, "symbol->string", 1, &symbol_to_string, a);
  a = init_builtin (builtin_type, "symbol->keyword", 1, &symbol_to_keyword, a);
  a = init_builtin (builtin_type, "keyword->string", 1, &keyword_to_string, a);
  a = init_builtin (builtin_type, "string->symbol", 1, &string_to_symbol, a);
  a = init_builtin (builtin_type, "make-symbol", 1, &make_symbol, a);
  a = init_builtin (builtin_type, "string->list", 1, &string_to_list, a);
  a = init_builtin (builtin_type, "list->string", 1, &list_to_string, a);
  a = init_builtin (builtin_type, "read-string", -1, &read_string, a);
  a = init_builtin (builtin_type, "string-append", -1, &string_append, a);
  a = init_builtin (builtin_type, "string-length", 1, &string_length, a);
  a = init_builtin (builtin_type, "string-ref", 2, &string_ref, a);
  /* src/struct.c */
  a = init_builtin (builtin_type, "make-struct", 3, &make_struct, a);
  a = init_builtin (builtin_type, "struct-length", 1, &struct_length, a);
  a = init_builtin (builtin_type, "struct-ref", 2, &struct_ref, a);
  a = init_builtin (builtin_type, "struct-set!", 3, &struct_set_x, a);
  /* src/vector.c */
  a = init_builtin (builtin_type, "make-vector", -1, &make_vector, a);
  a = init_builtin (builtin_type, "vector-length", 1, &vector_length, a);
  a = init_builtin (builtin_type, "vector-ref", 2, &vector_ref, a);
  a = init_builtin (builtin_type, "vector-entry", 1, &vector_entry, a);
  a = init_builtin (builtin_type, "vector-set!", 3, &vector_set_x, a);
  a = init_builtin (builtin_type, "list->vector", 1, &list_to_vector, a);
  a = init_builtin (builtin_type, "vector->list", 1, &vector_to_list, a);

  return a;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/core.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/core.c used in: Step 106, Step 214
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

/** Commentary:
    Essential functions, used by the eval/apply core.
 */

/** Code: */

#include "mes/lib.h"
#include "mes/mes.h"

#include <stdlib.h>

struct scm *
assoc_string (struct scm *x, struct scm *a)     /*:((internal)) */
{
  struct scm *b;
  while (a != cell_nil)
    {
      b = a->car;
      if (b->car->type == TSTRING)
        if (string_equal_p (x, b->car) == cell_t)
          return b;
      a = a->cdr;
    }
  if (a != cell_nil)
    return a->car;
  return cell_f;
}

struct scm *
car (struct scm *x)
{
#if !__MESC_MES__
  if (x->type != TPAIR)
    error (cell_symbol_not_a_pair, cons (x, cell_symbol_car));
#endif
  return x->car;
}

struct scm *
cdr (struct scm *x)
{
#if !__MESC_MES__
  if (x->type != TPAIR)
    error (cell_symbol_not_a_pair, cons (x, cell_symbol_cdr));
#endif
  return x->cdr;
}

struct scm *
list (struct scm *x)                    /*:((arity . n)) */
{
  return x;
}

struct scm *
null_p (struct scm *x)
{
  if (x == cell_nil)
    return cell_t;
  return cell_f;
}

struct scm *
eq_p (struct scm *x, struct scm *y)
{
  if (x == y)
    return cell_t;
  int t = x->type;
  if (t == TKEYWORD)
    {
      if (y->type == TKEYWORD)
        return string_equal_p (x, y);
      return cell_f;
    }
  if (t == TCHAR)
    {
      if (y->type != TCHAR)
        return cell_f;
      if (x->value == y->value)
        return cell_t;
      return cell_f;
    }
  if (t == TNUMBER)
    {
      if (y->type != TNUMBER)
        return cell_f;
      if (x->value == y->value)
        return cell_t;
      return cell_f;
    }
  return cell_f;
}

struct scm *
values (struct scm *x)                  /*:((arity . n)) */
{
  struct scm *v = cons (0, x);
  v->type = TVALUES;
  return v;
}

struct scm *
acons (struct scm *key, struct scm *value, struct scm *alist)
{
  return cons (cons (key, value), alist);
}

long
length__ (struct scm *x)                /*:((internal)) */
{
  long n = 0;
  while (x != cell_nil)
    {
      n = n + 1;
      if (x->type != TPAIR)
        return -1;
      x = x->cdr;
    }
  return n;
}

struct scm *
length (struct scm *x)
{
  return make_number (length__ (x));
}

struct scm *
error (struct scm *key, struct scm *x)
{
#if !__MESC_MES__ && !__M2_PLANET__
  struct scm *throw = module_ref (R0, cell_symbol_throw);
  if (throw != cell_undefined)
    return apply (throw, cons (key, cons (x, cell_nil)), R0);
#endif
  display_error_ (key);
  eputs (": ");
  write_error_ (x);
  eputs ("\n");
  assert_msg (0, "ERROR");
  exit (1);
}

struct scm *
append2 (struct scm *x, struct scm *y)
{
  if (x == cell_nil)
    return y;
  if (x->type != TPAIR)
    error (cell_symbol_not_a_pair, cons (x, cstring_to_symbol ("append2")));
  struct scm *r = cell_nil;
  while (x != cell_nil)
    {
      r = cons (x->car, r);
      x = x->cdr;
    }
  return reverse_x_ (r, y);
}

struct scm *
append_reverse (struct scm *x, struct scm *y)
{
  if (x == cell_nil)
    return y;
  if (x->type != TPAIR)
    error (cell_symbol_not_a_pair, cons (x, cstring_to_symbol ("append-reverse")));
  while (x != cell_nil)
    {
      y = cons (x->car, y);
      x = x->cdr;
    }
  return y;
}

struct scm *
reverse_x_ (struct scm *x, struct scm *t)
{
  if (x != cell_nil && x->type != TPAIR)
    error (cell_symbol_not_a_pair, cons (x, cstring_to_symbol ("core:reverse!")));
  struct scm *r = t;
  while (x != cell_nil)
    {
      t = x->cdr;
      x->cdr = r;
      r = x;
      x = t;
    }
  return r;
}

struct scm *
assq (struct scm *x, struct scm *a)
{
  if (a->type != TPAIR)
    return cell_f;
  int t = x->type;

  if (t == TSYMBOL || t == TSPECIAL)
    while (a != cell_nil)
      {
        if (x == a->car->car)
          return a->car;
        a = a->cdr;
      }
  else if (t == TCHAR || t == TNUMBER)
    {
      long v = x->value;
      while (a != cell_nil)
        {
          if (v == a->car->car->value)
            return a->car;
          a = a->cdr;
        }
    }
  else if (t == TKEYWORD)
    {
      while (a != cell_nil)
        {
          if (string_equal_p (x, a->car->car) == cell_t)
            return a->car;
          a = a->cdr;
        }
    }
  else
    /* pointer equality, e.g. on strings. */
    while (a != cell_nil)
      {
        if (x == a->car->car)
          return a->car;
        a = a->cdr;
      }
  return cell_f;
}

struct scm *
assoc (struct scm *x, struct scm *a)
{
  if (x->type == TSTRING)
    return assoc_string (x, a);
  while (a != cell_nil)
    {
      if (equal2_p (x, a->car->car) == cell_t)
        return a->car;
      a = a->cdr;
    }
  return cell_f;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/display.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/display.c used in: Step 106, Step 215
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

#include <stdlib.h>

int g_depth;

void
fdwrite_char (char v, int fd)
{
  if (v == '\0')
    fdputs ("\\nul", fd);
  else if (v == '\a')
    fdputs ("\\alarm", fd);
  else if (v == '\b')
    fdputs ("\\backspace", fd);
  else if (v == '\t')
    fdputs ("\\tab", fd);
  else if (v == '\n')
    fdputs ("\\newline", fd);
  else if (v == '\v')
    fdputs ("\\vtab", fd);
  else if (v == '\f')
    fdputs ("\\page", fd);
  /* Nyacc bug
     else if (v == '\r') fdputs ("return", fd);
  */
  else if (v == 13)
    fdputs ("\\return", fd);
  else if (v == ' ')
    fdputs ("\\space", fd);
  else
    {
      if (v >= 32 && v <= 127)
        fdputc ('\\', fd);
      fdputc (v, fd);
    }
}

void
fdwrite_string_char (char v, int fd)
{
  if (v == '\0')
    fdputs ("\\0", fd);
  else if (v == '\a')
    fdputs ("\\a", fd);
  else if (v == '\b')
    fdputs ("\\b", fd);
  else if (v == '\t')
    fdputs ("\\t", fd);
  else if (v == '\v')
    fdputs ("\\v", fd);
  else if (v == '\n')
    fdputs ("\\n", fd);
  else if (v == '\f')
    fdputs ("\\f", fd);
  /* Nyacc bug
     else if (v == '\r') fdputs ("\\r", fd);
     else if (v == '\e') fdputs ("\\e", fd);
  */
  else if (v == 13)
    fdputs ("\\r", fd);
  else if (v == 27)
    fdputs ("\\e", fd);
  else if (v == '\\')
    fdputs ("\\\\", fd);
  else if (v == '"')
    fdputs ("\\\"", fd);
  else
    fdputc (v, fd);
}

void
fdwrite_string (char *s, int length, int fd)
{
  int i;
  for (i = 0; i < length; i = i + 1)
    fdwrite_string_char (s[i], fd);
}

struct scm *display_helper (struct scm *x, int cont, char *sep, int fd, int write_p);

struct scm *
display_helper (struct scm *x, int cont, char *sep, int fd, int write_p)
{
  fdputs (sep, fd);
  if (g_depth == 0)
    return cell_unspecified;
  g_depth = g_depth - 1;

  int t = x->type;
  if (t == TCHAR)
    {
      if (write_p == 0)
        fdputc (x->value, fd);
      else
        {
          fdputs ("#", fd);
          fdwrite_char (x->value, fd);
        }
    }
  else if (t == TCLOSURE)
    {
      fdputs ("#<closure ", fd);
      struct scm *circ = x->cdr->car;
      struct scm *name = circ->cdr->car;
      struct scm *args = x->cdr->cdr->car;
      display_helper (name->car, 0, "", fd, 0);
      fdputc (' ', fd);
      display_helper (args, 0, "", fd, 0);
      fdputs (">", fd);
    }
  else if (t == TMACRO)
    {
      fdputs ("#<macro ", fd);
      display_helper (x->cdr, cont, "", fd, 0);
      fdputs (">", fd);
    }
  else if (t == TVARIABLE)
    {
      fdputs ("#<variable ", fd);
      display_helper (x->variable->car, cont, "", fd, 0);
      fdputs (">", fd);
    }
  else if (t == TNUMBER)
    {
      fdputs (itoa (x->value), fd);
    }
  else if (t == TPAIR)
    {
      if (cont == 0)
        fdputs ("(", fd);
      if (x->car == cell_circular && x->cdr->car != cell_closure)
        {
          fdputs ("(*circ* . ", fd);
          int i = 0;
          x = x->cdr;
          while (x != cell_nil && i < 10)
            {
              i = i + 1;
              fdisplay_ (x->car->car, fd, write_p);
              fdputs (" ", fd);
              x = x->cdr;
            }
          fdputs (" ...)", fd);
        }
      else
        {
          if (x != 0 && x != cell_nil)
            fdisplay_ (x->car, fd, write_p);
          if (x->cdr != 0 && x->cdr->type == TPAIR)
            display_helper (x->cdr, 1, " ", fd, write_p);
          else if (x->cdr != 0 && x->cdr != cell_nil)
            {
              if (x->cdr->type != TPAIR)
                fdputs (" . ", fd);
              fdisplay_ (x->cdr, fd, write_p);
            }
        }
      if (cont == 0)
        fdputs (")", fd);
    }
  else if (t == TPORT)
    {
      fdputs ("#<port ", fd);
      fdputs (itoa (x->port), fd);
      fdputs (" ", fd);
      x = x->string;
      fdputc ('"', fd);
      fdwrite_string (cell_bytes (x->string), x->length, fd);
      fdputc ('"', fd);
      fdputs (">", fd);
    }
  else if (t == TKEYWORD)
    {
      fdputs ("#:", fd);
      fdwrite_string (cell_bytes (x->string), x->length, fd);
    }
  else if (t == TSTRING)
    {
      if (write_p == 1)
        {
          fdputc ('"', fd);
          fdwrite_string (cell_bytes (x->string), x->length, fd);
          fdputc ('"', fd);
        }
      else
        fdputs (cell_bytes (x->string), fd);
    }
  else if (t == TSPECIAL || t == TSYMBOL)
    fdwrite_string (cell_bytes (x->string), x->length, fd);
  else if (t == TREF)
    fdisplay_ (x->ref, fd, write_p);
  else if (t == TSTRUCT)
    {
      struct scm *printer = struct_ref_ (x, STRUCT_PRINTER);
      if (printer->type == TREF)
        printer = printer->ref;
      if (printer->type == TCLOSURE || builtin_p (printer) == cell_t)
        apply (printer, cons (x, cell_nil), R0);
      else
        {
          fdputs ("#<", fd);
          fdisplay_ (x->structure, fd, write_p);
          struct scm *t = x->car;
          long size = x->length;
          long i;
          for (i = 2; i < size; i = i + 1)
            {
              fdputc (' ', fd);
              fdisplay_ (cell_ref (x->structure, i), fd, write_p);
            }
          fdputc ('>', fd);
        }
    }
  else if (t == TVECTOR)
    {
      fdputs ("#(", fd);
      struct scm *t = x->car;
      long i;
      for (i = 0; i < x->length; i = i + 1)
        {
          if (i != 0)
            fdputc (' ', fd);
          fdisplay_ (cell_ref (x->vector, i), fd, write_p);
        }
      fdputc (')', fd);
    }
  else
    {
      fdputs ("<", fd);
      fdputs (itoa (t), fd);
      fdputs (":", fd);
      fdputs (ltoa (cast_voidp_to_long (x)), fd);
      fdputs (">", fd);
    }
  return cell_unspecified;
}

struct scm *
display_ (struct scm *x)
{
  g_depth = 5;
  return display_helper (x, 0, "", __stdout, 0);
}

struct scm *
display_error_ (struct scm *x)
{
  g_depth = 5;
  return display_helper (x, 0, "", __stderr, 0);
}

struct scm *
display_port_ (struct scm *x, struct scm *p)
{
  assert_msg (p->type == TNUMBER, "p->type == TNUMBER");
  return fdisplay_ (x, p->value, 0);
}

struct scm *
write_ (struct scm *x)
{
  g_depth = 5;
  return display_helper (x, 0, "", __stdout, 1);
}

struct scm *
write_error_ (struct scm *x)
{
  g_depth = 5;
  return display_helper (x, 0, "", __stderr, 1);
}

struct scm *
write_port_ (struct scm *x, struct scm *p)
{
  assert_msg (p->type == TNUMBER, "p->type == TNUMBER");
  return fdisplay_ (x, p->value, 1);
}

struct scm *
fdisplay_ (struct scm *x, int fd, int write_p)  /*:((internal)) */
{
  g_depth = 5;
  return display_helper (x, 0, "", fd, write_p);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/eval-apply.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/eval-apply.c used in: Step 106, Step 216
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

#include <string.h>

struct scm *
assert_defined (struct scm *x, struct scm *e)   /*:((internal)) */
{
  if (e == cell_undefined)
    return error (cell_symbol_unbound_variable, x);
  return e;
}

struct scm *
check_formals (struct scm *f, struct scm *formals, struct scm *args)    /*:((internal)) */
{
  long flen;
  if (formals->type == TNUMBER)
    flen = formals->value;
  else
    flen = length__ (formals);
  long alen = length__ (args);
  if (alen != flen && alen != -1 && flen != -1)
    {
      char *s = "apply: wrong number of arguments; expected: ";
      eputs (s);
      eputs (itoa (flen));
      eputs (", got: ");
      eputs (itoa (alen));
      eputs ("\n");
      write_error_ (f);
      struct scm *e = make_string0 (s);
      return error (cell_symbol_wrong_number_of_args, cons (e, f));
    }
  return cell_unspecified;
}

struct scm *
check_apply (struct scm *f, struct scm *e)      /*:((internal)) */
{
  char *type = 0;
  if (f == cell_f || f == cell_t)
    type = "bool";
  if (f == cell_nil)
    type = "nil";
  if (f == cell_unspecified)
    type = "*unspecified*";
  if (f == cell_undefined)
    type = "*undefined*";
  if (f->type == TCHAR)
    type = "char";
  if (f->type == TNUMBER)
    type = "number";
  if (f->type == TSTRING)
    type = "string";
  if (f->type == TSTRUCT && builtin_p (f) == cell_f)
    type = "#<...>";
  if (f->type == TBROKEN_HEART)
    type = "<3";

  if (type != 0)
    {
      char *s = "cannot apply: ";
      eputs (s);
      eputs (type);
      eputs ("[");
      write_error_ (e);
      eputs ("]\n");
      struct scm *e = make_string0 (s);
      return error (cell_symbol_wrong_type_arg, cons (e, f));
    }
  return cell_unspecified;
}

struct scm *
pairlis (struct scm *x, struct scm *y, struct scm *a)
{
  if (x == cell_nil)
    return a;
  if (x->type != TPAIR)
    return cons (cons (x, y), a);
  return cons (cons (car (x), car (y)), pairlis (cdr (x), cdr (y), a));
}

struct scm *
set_car_x (struct scm *x, struct scm *e)
{
  if (x->type != TPAIR)
    error (cell_symbol_not_a_pair, cons (x, cstring_to_symbol ("set-car!")));
  x->car = e;
  return cell_unspecified;
}

struct scm *
set_cdr_x (struct scm *x, struct scm *e)
{
  if (x->type != TPAIR)
    error (cell_symbol_not_a_pair, cons (x, cstring_to_symbol ("set-cdr!")));
  x->cdr = e;
  return cell_unspecified;
}

struct scm *
set_env_x (struct scm *x, struct scm *e, struct scm *a)
{
  struct scm *p;
  if (x->type == TVARIABLE)
    p = x->variable;
  else
    p = assert_defined (x, module_variable (a, x));
  if (p->type != TPAIR)
    error (cell_symbol_not_a_pair, cons (p, x));
  return set_cdr_x (p, e);
}

struct scm *
call_lambda (struct scm *e, struct scm *x, struct scm *aa, struct scm *a)       /*:((internal)) */
{
  struct scm *cl = cons (cons (cell_closure, x), x);
  R1 = e;
  R0 = cl;
  return cell_unspecified;
}

struct scm *
make_closure_ (struct scm *args, struct scm *body, struct scm *a)       /*:((internal)) */
{
  return make_cell (TCLOSURE, cell_f, cons (cons (cell_circular, a), cons (args, body)));
}

struct scm *
make_variable_ (struct scm *var)        /*:((internal)) */
{
  return make_cell (TVARIABLE, var, 0);
}

struct scm *
macro_get_handle (struct scm *name)     /*:((internal)) */
{
  if (name->type == TSYMBOL)
    return hashq_get_handle (g_macros, name, cell_nil);
  return cell_f;
}

struct scm *
get_macro (struct scm *name)            /*:((internal)) */
{
  struct scm *m = macro_get_handle (name);
  if (m != cell_f)
    {
      struct scm *d = m->cdr;
      return d->macro;
    }
  return cell_f;
}

struct scm *
macro_set_x (struct scm *name, struct scm *value)       /*:((internal)) */
{
  return hashq_set_x (g_macros, name, value);
}

struct scm *
push_cc (struct scm *p1, struct scm *p2, struct scm *a, struct scm *c)  /*:((internal)) */
{
  struct scm *x = R3;
  R3 = c;
  R2 = p2;
  gc_push_frame ();
  R1 = p1;
  R0 = a;
  R3 = x;
  return cell_unspecified;
}

struct scm *
add_formals (struct scm *formals, struct scm *x)
{
  while (x->type == TPAIR)
    {
      formals = cons (x->car, formals);
      x = x->cdr;
    }
  if (x->type == TSYMBOL)
    formals = cons (x, formals);
  return formals;
}

int
formal_p (struct scm *x, struct scm *formals)   /*:((internal)) */
{
  if (formals->type == TSYMBOL)
    {
      if (x == formals)
        return 1;
      else
        return 0;
    }
  while (formals->type == TPAIR)
    {
      if (formals->car == x)
        break;
      formals = formals->cdr;
    }
  if (formals->type == TSYMBOL)
    return formals == x;
  return formals->type == TPAIR;
}

struct scm *
expand_variable_ (struct scm *x, struct scm *formals, int top_p)        /*:((internal)) */
{
  struct scm *a;
  struct scm *f;
  struct scm *v;
  while (x->type == TPAIR)
    {
      a = x->car;
      if (a->type == TPAIR)
        {
          if (a->car == cell_symbol_lambda)
            {
              f = a->cdr->car;
              formals = add_formals (formals, f);
            }
          else if (a->car == cell_symbol_define || a->car == cell_symbol_define_macro)
            {
              f = a->cdr->car;
              formals = add_formals (formals, f);
            }
          if (a->car != cell_symbol_quote)
            expand_variable_ (a, formals, 0);
        }
      else
        {
          if (a == cell_symbol_lambda)
            {
              f = x->cdr->car;
              formals = add_formals (formals, f);
              x = x->cdr;
            }
          else if (a == cell_symbol_define || a == cell_symbol_define_macro)
            {
              f = x->cdr->car;
              if (top_p != 0 && f->type == TPAIR)
                f = f->cdr;
              formals = add_formals (formals, f);
              x = x->cdr;
            }
          else if (a == cell_symbol_quote)
            return cell_unspecified;
          else if (a->type == TSYMBOL
                   && a != cell_symbol_boot_module
                   && a != cell_symbol_current_module
                   && a != cell_symbol_primitive_load
                   && formal_p (x->car, formals) == 0)
            {
              v = module_variable (R0, a);
              if (v != cell_f)
                x->car = make_variable_ (v);
            }
        }
      x = x->cdr;
      top_p = 0;
    }
  return cell_unspecified;
}

struct scm *
expand_variable (struct scm *x, struct scm *formals)    /*:((internal)) */
{
  return expand_variable_ (x, formals, 1);
}

struct scm *
apply_builtin (struct scm *fn, struct scm *x)   /*:((internal)) */
{
  struct scm *a = builtin_arity (fn);
  struct scm *d;
  int arity = a->value;
  if ((arity > 0 || arity == -1) && x != cell_nil)
    {
      a = x->car;
      if (a->type == TVALUES)
        x = cons (a->cdr->car, x->cdr);
    }
  if ((arity > 1 || arity == -1) && x != cell_nil)
    {
      a = x->car;
      d = x->cdr;
      if (d->type == TPAIR)
        if (d->car->type == TVALUES)
          x = cons (a, cons (d->car->cdr->car, d));
    }

  if (arity == 0)
    return apply_builtin0 (fn);
  if (arity == 1)
    return apply_builtin1 (fn, x->car);
  else if (arity == 2)
    return apply_builtin2 (fn, x->car, x->cdr->car);
  else if (arity == 3)
    return apply_builtin3 (fn, x->car, x->cdr->car, x->cdr->cdr->car);
  else if (arity == -1)
    return apply_builtin1 (fn, x);

  return cell_unspecified;
}

struct scm *
eval_apply ()
{
  struct scm *aa;
  struct scm *args;
  struct scm *body;
  struct scm *cl;
  struct scm *entry;
  struct scm *expanders;
  struct scm *formals;
  struct scm *input;
  struct scm *name;
  struct scm *macro;
  struct scm *p;
  struct scm *program;
  struct scm *sc_expand;
  struct scm *v;
  struct scm *x;
  int global_p;
  int macro_p;
  struct scm *a;
  struct scm *c;
  struct scm *d;
  int t;
  long i;

eval_apply:
  if (R3 == cell_vm_evlis2)
    goto evlis2;
  else if (R3 == cell_vm_evlis3)
    goto evlis3;
  else if (R3 == cell_vm_eval_check_func)
    goto eval_check_func;
  else if (R3 == cell_vm_eval2)
    goto eval2;
  else if (R3 == cell_vm_apply2)
    goto apply2;
  else if (R3 == cell_vm_if_expr)
    goto if_expr;
  else if (R3 == cell_vm_begin_eval)
    goto begin_eval;
  else if (R3 == cell_vm_eval_set_x)
    goto eval_set_x;
  else if (R3 == cell_vm_macro_expand_car)
    goto macro_expand_car;
  else if (R3 == cell_vm_return)
    goto vm_return;
  else if (R3 == cell_vm_macro_expand_cdr)
    goto macro_expand_cdr;
  else if (R3 == cell_vm_eval_define)
    goto eval_define;
  else if (R3 == cell_vm_macro_expand)
    goto macro_expand;
  else if (R3 == cell_vm_macro_expand_lambda)
    goto macro_expand_lambda;
  else if (R3 == cell_vm_eval_pmatch_car)
    goto eval_pmatch_car;
  else if (R3 == cell_vm_begin_expand_macro)
    goto begin_expand_macro;
  else if (R3 == cell_vm_macro_expand_define)
    goto macro_expand_define;
  else if (R3 == cell_vm_begin_expand_eval)
    goto begin_expand_eval;
  else if (R3 == cell_vm_call_with_current_continuation2)
    goto call_with_current_continuation2;
  else if (R3 == cell_vm_macro_expand_set_x)
    goto macro_expand_set_x;
  else if (R3 == cell_vm_eval_pmatch_cdr)
    goto eval_pmatch_cdr;
  else if (R3 == cell_vm_macro_expand_define_macro)
    goto macro_expand_define_macro;
  else if (R3 == cell_vm_begin_primitive_load)
    goto begin_primitive_load;

  else if (R3 == cell_vm_evlis)
    goto evlis;
  else if (R3 == cell_vm_apply)
    goto apply;
  else if (R3 == cell_vm_eval)
    goto eval;
  else if (R3 == cell_vm_eval_macro_expand_eval)
    goto eval_macro_expand_eval;
  else if (R3 == cell_vm_eval_macro_expand_expand)
    goto eval_macro_expand_expand;
  else if (R3 == cell_vm_begin)
    goto begin;
  else if (R3 == cell_vm_begin_expand)
    goto begin_expand;
  else if (R3 == cell_vm_begin_expand_primitive_load)
    goto begin_expand_primitive_load;
  else if (R3 == cell_vm_if)
    goto vm_if;
  else if (R3 == cell_vm_call_with_values2)
    goto call_with_values2;
  else if (R3 == cell_unspecified)
    return R1;
  else
    assert_msg (0, "eval/apply unknown continuation");

evlis:
  if (R1 == cell_nil)
    goto vm_return;
  if (R1->type != TPAIR)
    goto eval;
  push_cc (R1->car, R1, R0, cell_vm_evlis2);
  goto eval;
evlis2:
  push_cc (R2->cdr, R1, R0, cell_vm_evlis3);
  goto evlis;
evlis3:
  R1 = cons (R2, R1);
  goto vm_return;

apply:
  g_stack_array[g_stack + GC_FRAME_PROCEDURE] = R1->car;
  a = R1->car;
  t = a->type;
  if (t == TSTRUCT && builtin_p (R1->car) == cell_t)
    {
      check_formals (R1->car, builtin_arity (R1->car), R1->cdr);
      R1 = apply_builtin (R1->car, R1->cdr);
      goto vm_return;
    }
  else if (t == TCLOSURE)
    {
      cl = R1->car->closure;
      body = cl->cdr->cdr;
      formals = cl->cdr->car;
      args = R1->cdr;
      aa = cl->car->cdr;
      aa = aa->cdr;
      check_formals (R1->car, formals, R1->cdr);
      p = pairlis (formals, args, aa);
      call_lambda (body, p, aa, R0);
      goto begin;
    }
  else if (t == TCONTINUATION)
    {
      a = R1->car;
      v = a->continuation;
      if (v->length != 0)
        {
          for (i = 0; i < v->length; i = i + 1)
            g_stack_array[STACK_SIZE - v->length + i] = vector_ref_ (v, i);
          g_stack = STACK_SIZE - v->length;
        }
      x = R1;
      gc_pop_frame ();
      R1 = x->cdr->car;
      goto eval_apply;
    }
  else if (t == TSPECIAL)
    {
      c = R1->car;
      if (c == cell_vm_apply)
        {
          push_cc (cons (R1->cdr->car, R1->cdr->cdr->car), R1, R0, cell_vm_return);
          goto apply;
        }
      else if (c == cell_vm_eval)
        {
          push_cc (R1->cdr->car, R1, R1->cdr->cdr->car, cell_vm_return);
          goto eval;
        }
      else if (c == cell_vm_begin_expand)
        {
          push_cc (cons (R1->cdr->car, cell_nil), R1, R1->cdr->cdr->car, cell_vm_return);
          goto begin_expand;
        }
      else
        check_apply (cell_f, R1->car);
    }
  else if (t == TSYMBOL)
    {
      c = R1->car;
      if (c == cell_symbol_call_with_current_continuation)
        {
          R1 = R1->cdr;
          goto call_with_current_continuation;
        }
      if (c == cell_symbol_call_with_values)
        {
          R1 = R1->cdr;
          goto call_with_values;
        }
      if (c == cell_symbol_current_module)
        {
          R1 = R0;
          goto vm_return;
        }
      if (c == cell_symbol_boot_module)
        {
          R1 = M0;
          goto vm_return;
        }
    }
  else if (t == TPAIR)
    {
      if (R1->car->car == cell_symbol_lambda)
        {
          formals = R1->car->cdr->car;
          args = R1->cdr;
          body = R1->car->cdr->cdr;
          p = pairlis (formals, R1->cdr, R0);
          check_formals (R1, formals, args);
          call_lambda (body, p, p, R0);
          goto begin;
        }
    }
  push_cc (R1->car, R1, R0, cell_vm_apply2);
  goto eval;
apply2:
  check_apply (R1, R2->car);
  R1 = cons (R1, R2->cdr);
  goto apply;

eval:
  t = R1->type;
  if (t == TPAIR)
    {
      c = R1->car;
      if (c == cell_symbol_pmatch_car)
        {
          push_cc (R1->cdr->car, R1, R0, cell_vm_eval_pmatch_car);
          goto eval;
        eval_pmatch_car:
          x = R1;
          gc_pop_frame ();
          R1 = x->car;
          goto eval_apply;
        }
      else if (c == cell_symbol_pmatch_cdr)
        {
          push_cc (R1->cdr->car, R1, R0, cell_vm_eval_pmatch_cdr);
          goto eval;
        eval_pmatch_cdr:
          x = R1;
          gc_pop_frame ();
          R1 = x->cdr;
          goto eval_apply;
        }
      else if (c == cell_symbol_quote)
        {
          x = R1;
          gc_pop_frame ();
          R1 = x->cdr->car;
          goto eval_apply;
        }
      else if (c == cell_symbol_begin)
        goto begin;
      else if (c == cell_symbol_lambda)
        {
          R1 = make_closure_ (R1->cdr->car, R1->cdr->cdr, R0);
          goto vm_return;
        }
      else if (c == cell_symbol_if)
        {
          R1 = R1->cdr;
          goto vm_if;
        }
      else if (c == cell_symbol_set_x)
        {
          push_cc (R1->cdr->cdr->car, R1, R0, cell_vm_eval_set_x);
          goto eval;
        eval_set_x:
          R1 = set_env_x (R2->cdr->car, R1, R0);
          goto vm_return;
        }
      else if (c == cell_vm_macro_expand)
        {
          push_cc (R1->cdr->car, R1, R0, cell_vm_eval_macro_expand_eval);
          goto eval;
        eval_macro_expand_eval:
          push_cc (R1, R2, R0, cell_vm_eval_macro_expand_expand);
          goto macro_expand;
        eval_macro_expand_expand:
          goto vm_return;
        }
      else
        {
          if (R1->type == TPAIR)
            if (R1->car == cell_symbol_define || R1->car == cell_symbol_define_macro)
              {
                global_p = 0;
                if (R0->car->car != cell_closure)
                  global_p = 1;
                macro_p = 0;
                if (R1->car == cell_symbol_define_macro)
                  macro_p = 1;
                if (global_p != 0)
                  {
                    name = R1->cdr->car;
                    aa = R1->cdr->car;
                    if (aa->type == TPAIR)
                      name = name->car;
                    if (macro_p != 0)
                      {
                        entry = assq (name, g_macros);
                        if (entry == cell_f)
                          macro_set_x (name, cell_f);
                      }
                    else
                      {
                        entry = module_variable (R0, name);
                        if (entry == cell_f)
                          module_define_x (M0, name, cell_f);
                      }
                  }
                R2 = R1;
                aa = R1->cdr->car;
                if (aa->type != TPAIR)
                  {
                    push_cc (R1->cdr->cdr->car, R2, cons (cons (R1->cdr->car, R1->cdr->car), R0), cell_vm_eval_define);
                    goto eval;
                  }
                else
                  {
                    p = pairlis (R1->cdr->car, R1->cdr->car, R0);
                    formals = R1->cdr->car->cdr;
                    body = R1->cdr->cdr;

                    if (macro_p != 0 || global_p != 0)
                      expand_variable (body, formals);
                    R1 = cons (cell_symbol_lambda, cons (formals, body));
                    push_cc (R1, R2, p, cell_vm_eval_define);
                    goto eval;
                  }
              eval_define:
                name = R2->cdr->car;
                aa = R2->cdr->car;
                if (aa->type == TPAIR)
                  name = name->car;
                if (macro_p != 0)
                  {
                    entry = macro_get_handle (name);
                    R1 = make_macro (name, R1);
                    set_cdr_x (entry, R1);
                  }
                else if (global_p != 0)
                  {
                    entry = module_variable (R0, name);
                    set_cdr_x (entry, R1);
                  }
                else
                  {
                    entry = cons (name, R1);
                    aa = cons (entry, cell_nil);
                    set_cdr_x (aa, cdr (R0));
                    set_cdr_x (R0, aa);
                    cl = module_variable (R0, cell_closure);
                    set_cdr_x (cl, aa);
                  }
                R1 = cell_unspecified;
                goto vm_return;
              }
          push_cc (R1->car, R1, R0, cell_vm_eval_check_func);
          gc_check ();
          goto eval;
        eval_check_func:
          push_cc (R2->cdr, R2, R0, cell_vm_eval2);
          goto evlis;
        eval2:
          R1 = cons (R2->car, R1);
          goto apply;
        }
    }
  else if (t == TSYMBOL)
    {
      if (R1 == cell_symbol_boot_module)
        goto vm_return;
      if (R1 == cell_symbol_current_module)
        goto vm_return;
      if (R1 == cell_symbol_begin)
        goto vm_return;
      if (R1 == cell_symbol_call_with_current_continuation)
        goto vm_return;
      R1 = assert_defined (R1, module_ref (R0, R1));
      goto vm_return;
    }
  else if (t == TVARIABLE)
    {
      x = R1->variable;
      R1 = x->cdr;
      goto vm_return;
    }
  else if (t == TBROKEN_HEART)
    error (cell_symbol_system_error, R1);
  else
    goto vm_return;

macro_expand:
  if (R1->type != TPAIR || R1->car == cell_symbol_quote)
    goto vm_return;

  if (R1->car == cell_symbol_lambda)
    {
      push_cc (R1->cdr->cdr, R1, R0, cell_vm_macro_expand_lambda);
      goto macro_expand;
    macro_expand_lambda:
      R2->cdr->cdr = R1;
      R1 = R2;
      goto vm_return;
    }

  if (R1->type == TPAIR)
    {
      macro = get_macro (R1->car);
      if (macro != cell_f)
        {
          R1 = cons (macro, R1->cdr);
          push_cc (R1, cell_nil, R0, cell_vm_macro_expand);
          goto apply;
        }
    }

  if (R1->car == cell_symbol_define || R1->car == cell_symbol_define_macro)
    {
      push_cc (R1->cdr->cdr, R1, R0, cell_vm_macro_expand_define);
      goto macro_expand;
    macro_expand_define:
      R2->cdr->cdr = R1;
      R1 = R2;
      if (R1->car == cell_symbol_define_macro)
        {
          push_cc (R1, R1, R0, cell_vm_macro_expand_define_macro);
          goto eval;
        macro_expand_define_macro:
          R1 = R2;
        }
      goto vm_return;
    }

  if (R1->car == cell_symbol_set_x)
    {
      push_cc (R1->cdr->cdr, R1, R0, cell_vm_macro_expand_set_x);
      goto macro_expand;
    macro_expand_set_x:
      R2->cdr->cdr = R1;
      R1 = R2;
      goto vm_return;
    }

  if (R1->type == TPAIR)
    {
      a = R1->car;
      if (a->type == TSYMBOL && a != cell_symbol_begin)
        {
          macro = macro_get_handle (cell_symbol_portable_macro_expand);
          if (macro != cell_f)
            {
              expanders = module_ref (R0, cell_symbol_sc_expander_alist);
              if (expanders != cell_undefined)
                {
                  macro = assq (R1->car, expanders);
                  if (macro != cell_f)
                    {
                      sc_expand = module_ref (R0, cell_symbol_macro_expand);
                      R2 = R1;
                      if (sc_expand != cell_undefined && sc_expand != cell_f)
                        {
                          R1 = cons (sc_expand, cons (R1, cell_nil));
                          goto apply;
                        }
                    }
                }
            }
        }
    }

  push_cc (R1->car, R1, R0, cell_vm_macro_expand_car);
  goto macro_expand;

macro_expand_car:
  R2->car = R1;
  R1 = R2;
  if (R1->cdr == cell_nil)
    goto vm_return;

  push_cc (R1->cdr, R1, R0, cell_vm_macro_expand_cdr);
  goto macro_expand;

macro_expand_cdr:
  R2->cdr = R1;
  R1 = R2;

  goto vm_return;

begin:
  x = cell_unspecified;
  while (R1 != cell_nil)
    {
      gc_check ();
      if (R1->type == TPAIR)
        {
          if (R1->car->car == cell_symbol_primitive_load)
            {
              program = cons (R1->car, cell_nil);
              push_cc (program, R1, R0, cell_vm_begin_primitive_load);
              goto begin_expand;
            begin_primitive_load:
              R2->car = R1;
              R1 = R2;
            }
        }

      if (R1->type == TPAIR)
        {
          a = R1->car;
          if (a->type == TPAIR)
            {
              if (a->car == cell_symbol_begin)
                R1 = append2 (a->cdr, R1->cdr);
            }
        }
      if (R1->cdr == cell_nil)
        {
          R1 = R1->car;
          goto eval;
        }
      push_cc (R1->car, R1, R0, cell_vm_begin_eval);
      goto eval;
    begin_eval:
      x = R1;
      R1 = R2->cdr;
    }
  R1 = x;
  goto vm_return;


begin_expand:
  x = cell_unspecified;
  while (R1 != cell_nil)
    {
    begin_expand_while:
      gc_check ();

      if (R1->type == TPAIR)
        {
          a = R1->car;
          if (a->type == TPAIR)
            if (R1->car->car == cell_symbol_begin)
              R1 = append2 (R1->car->cdr, R1->cdr);
          if (R1->car->car == cell_symbol_primitive_load)
            {
              push_cc (R1->car->cdr->car, R1, R0, cell_vm_begin_expand_primitive_load);
              goto eval;
            begin_expand_primitive_load:
              if ((R1->type == TNUMBER) && R1->value == 0)
                0;
              else if (R1->type == TSTRING)
                input = set_current_input_port (open_input_file (R1));
              else if (R1->type == TPORT)
                input = set_current_input_port (R1);
              else
                {
                  eputs ("begin_expand failed, R1=");
                  display_error_ (R1);
                  assert_msg (0, "begin-expand-boom 0");
                }

              push_cc (input, R2, R0, cell_vm_return);
              x = read_input_file_env (R0);
              if (g_debug > 5)
                module_printer (M0);
              gc_pop_frame ();
              input = R1;
              R1 = x;
              set_current_input_port (input);
              R1 = cons (cell_symbol_begin, R1);
              R2->car = R1;
              R1 = R2;
              goto begin_expand_while;
              continue; /* FIXME: M2-PLanet */
            }
        }

      push_cc (R1->car, R1, R0, cell_vm_begin_expand_macro);
      goto macro_expand;
    begin_expand_macro:
      if (R1 != R2->car)
        {
          R2->car = R1;
          R1 = R2;
          goto begin_expand_while;
          continue; /* FIXME: M2-PLanet */
        }
      R1 = R2;
      expand_variable (R1->car, cell_nil);
      push_cc (R1->car, R1, R0, cell_vm_begin_expand_eval);
      goto eval;
    begin_expand_eval:
      x = R1;
      R1 = R2->cdr;
    }
  R1 = x;
  goto vm_return;

vm_if:
  push_cc (R1->car, R1, R0, cell_vm_if_expr);
  goto eval;
if_expr:
  x = R1;
  R1 = R2;
  if (x != cell_f)
    {
      R1 = R1->cdr->car;
      goto eval;
    }
  if (R1->cdr->cdr != cell_nil)
    {
      R1 = R1->cdr->cdr->car;
      goto eval;
    }
  R1 = cell_unspecified;
  goto vm_return;

call_with_current_continuation:
  gc_push_frame ();
  x = make_continuation (g_continuations);
  g_continuations = g_continuations + 1;
  v = make_vector_ (STACK_SIZE - g_stack, cell_unspecified);
  for (i = g_stack; i < STACK_SIZE; i = i + 1)
    vector_set_x_ (v, i - g_stack, g_stack_array[i]);
  x->continuation = v;
  gc_pop_frame ();
  push_cc (cons (R1->car, cons (x, cell_nil)), x, R0, cell_vm_call_with_current_continuation2);
  goto apply;
call_with_current_continuation2:
  v = make_vector_ (STACK_SIZE - g_stack, cell_unspecified);
  for (i = g_stack; i < STACK_SIZE; i = i + 1)
    vector_set_x_ (v, i - g_stack, g_stack_array[i]);
  R2->continuation = v;
  goto vm_return;

call_with_values:
  push_cc (cons (R1->car, cell_nil), R1, R0, cell_vm_call_with_values2);
  goto apply;
call_with_values2:
  if (R1->type == TVALUES)
    R1 = R1->cdr;
  R1 = cons (R2->cdr->car, R1);
  goto apply;

vm_return:
  x = R1;
  gc_pop_frame ();
  R1 = x;
  goto eval_apply;
}

struct scm *
apply (struct scm *f, struct scm *x, struct scm *a)     /*:((internal)) */
{
  push_cc (cons (f, x), cell_unspecified, R0, cell_unspecified);
  R3 = cell_vm_apply;
  return eval_apply ();
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/gc.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/gc.c used in: Step 106, Step 217
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2022 Gabriel Wicki <gabriel@erlikon.ch>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <time.h>

int g_dump_filedes;

#define M2_CELL_SIZE 1U
// CONSTANT M2_CELL_SIZE 12

char *
cell_bytes (struct scm *x)
{
  char *p = cast_voidp_to_charp (x);
  return p + (2 * sizeof (long));
}

#define U10 10U
// CONSTANT U10 10
#define U100 100U
// CONSTANT U100 100
void
gc_init ()
{
#if SYSTEM_LIBC
  ARENA_SIZE = 100000000;       /* 2.3GiB */
#elif ! __M2_PLANET__
  ARENA_SIZE = 300000;          /* 32b: 3MiB, 64b: 6 MiB */
#else
  ARENA_SIZE = 20000000;
#endif
  STACK_SIZE = 20000;

  JAM_SIZE = 10;
  MAX_ARENA_SIZE = 10000000;
  GC_SAFETY = 2000;
  MAX_STRING = 524288;

  char *p;
  p = getenv ("MES_MAX_ARENA");
  if (p != 0)
    MAX_ARENA_SIZE = atoi (p);
  p = getenv ("MES_ARENA");
  if (p != 0)
    ARENA_SIZE = atoi (p);
  JAM_SIZE = ARENA_SIZE / U10;
  p = getenv ("MES_JAM");
  if (p != 0)
    JAM_SIZE = atoi (p);
  GC_SAFETY = ARENA_SIZE / U100;
  p = getenv ("MES_SAFETY");
  if (p != 0)
    GC_SAFETY = atoi (p);
  p = getenv ("MES_STACK");
  if (p != 0)
    STACK_SIZE = atoi (p);
  p = getenv ("MES_MAX_STRING");
  if (p != 0)
    MAX_STRING = atoi (p);

  long arena_bytes = (ARENA_SIZE + JAM_SIZE) * sizeof (struct scm);
  long alloc_bytes = arena_bytes + (STACK_SIZE * sizeof (struct scm));

  g_arena = malloc (alloc_bytes);
  g_cells = cast_charp_to_scmp (g_arena);
  g_stack_array = cast_charp_to_scmpp (g_arena + arena_bytes);

  /* The vector that holds the arenea. */
  cell_arena = g_cells;

  cell_zero = cell_arena + M2_CELL_SIZE;

  g_cells = g_cells + M2_CELL_SIZE; /* Hmm? */

  cell_arena->type = TVECTOR;
  cell_arena->length = 1000;
  cell_arena->vector = cell_zero;

  cell_zero->type = TCHAR;
  cell_zero->value = 'c';

  g_free = g_cells + M2_CELL_SIZE;

  /* FIXME: remove MES_MAX_STRING, grow dynamically. */
  g_buf = malloc (MAX_STRING);
}

long
gc_free ()
{
  return (g_free - g_cells) / M2_CELL_SIZE;
}

void
gc_stats_ (char const* where)
{
  size_t i = g_free - g_cells;
  i = i / M2_CELL_SIZE;
  if (where)
    {
      eputs (where);
      eputs (": ");
    }
  eputs ("[");
  eputs (ltoa (i));
  eputs ("]\n");
}

struct scm *
gc_stats ()
{
  gc_stats_ (0);
  size_t arena_used = g_free - g_cells;
  arena_used = arena_used / M2_CELL_SIZE;
  size_t arena_free = ARENA_SIZE - arena_used;
  struct scm *r = cell_nil;
  r =  acons (cstring_to_symbol ("gc-count"), make_number (gc_count), r);
  r =  acons (cstring_to_symbol ("gc-time"), make_number (gc_time), r);
  r =  acons (cstring_to_symbol ("arena-free"), make_number (arena_free), r);
  r =  acons (cstring_to_symbol ("arena-size"), make_number (ARENA_SIZE), r);
  return r;
}

struct scm *
alloc (long n)
{
  struct scm *x = g_free;
  g_free = g_free + (n * M2_CELL_SIZE);
  long i = g_free - g_cells;
  i = i / M2_CELL_SIZE;

  if (i > ARENA_SIZE)
    assert_msg (0, "alloc: out of memory");
  return x;
}

struct scm *
make_cell (long type, struct scm *car, struct scm *cdr)
{
  struct scm *x = g_free;
  g_free = g_free + M2_CELL_SIZE;
  long i = g_free - g_cells;
  i = i / M2_CELL_SIZE;
  if (i > ARENA_SIZE)
    assert_msg (0, "make_cell: out of memory");
  x->type = type;
  x->car = car;
  x->cdr = cdr;
  return x;
}

struct scm *
make_pointer_cell (long type, long car, void *cdr)
{
  struct scm *x = g_free;
  g_free = g_free + M2_CELL_SIZE;
  long i = g_free - g_cells;
  i = i / M2_CELL_SIZE;
  if (i > ARENA_SIZE)
    assert_msg (0, "make_pointer_cell: out of memory");
  x->type = type;
  x->length = car;
  x->cdr = cdr;
  return x;
}

struct scm *
make_value_cell (long type, long car, long cdr)
{
  struct scm *x = g_free;
  g_free = g_free + M2_CELL_SIZE;
  long i = g_free - g_cells;
  i = i / M2_CELL_SIZE;
  if (i > ARENA_SIZE)
    assert_msg (0, "make_value_cell: out of memory");
  x->type = type;
  x->length = car;
  x->value = cdr;
  return x;
}

void
copy_cell (struct scm *to, struct scm *from)
{
  to->type = from->type;
  to->car = from->car;
  to->cdr = from->cdr;
}

void
copy_stack (long index, struct scm *from)
{
  g_stack_array[index] = from;
}

struct scm *
cell_ref (struct scm *cell, long index)
{
  return cell + (index * M2_CELL_SIZE);
}

struct scm *
cons (struct scm *x, struct scm *y)
{
  return make_cell (TPAIR, x, y);
}

size_t
bytes_cells (size_t length)
{
  return (sizeof (long) + sizeof (long) + length - 1 + sizeof (struct scm *)) / sizeof (struct scm *);
}

struct scm *
make_bytes (char const *s, size_t length)
{
  size_t size = bytes_cells (length);
  struct scm *x = alloc (size);
  x->type = TBYTES;
  x->length = length;
  char *p = cell_bytes (x);
  if (length == 0)
    p[0] = 0;
  else
    memcpy (p, s, length);

  return x;
}

struct scm *
make_char (int n)
{
  return make_value_cell (TCHAR, 0, n);
}

struct scm *
make_continuation (long n)
{
  return make_value_cell (TCONTINUATION, n, g_stack);
}

struct scm *
make_macro (struct scm *name, struct scm *x)    /*:((internal)) */
{
  return make_cell (TMACRO, x, name->string);
}

struct scm *
make_number (long n)
{
  return make_value_cell (TNUMBER, 0, n);
}

struct scm *
make_ref (struct scm *x)                /*:((internal)) */
{
  return make_cell (TREF, x, 0);
}

struct scm *
make_string (char const *s, size_t length)
{
  if (length > MAX_STRING)
    assert_max_string (length, "make_string", s);
  struct scm *x = make_pointer_cell (TSTRING, length, 0);
  struct scm *v = make_bytes (s, length + 1);
  x->cdr = v;
  return x;
}

struct scm *
make_string0 (char const *s)
{
  return make_string (s, strlen (s));
}

struct scm *
make_string_port (struct scm *x)        /*:((internal)) */
{
  return make_pointer_cell (TPORT, -length__ (g_ports) - 2, x);
}

void
gc_init_news ()
{
  g_news = g_free;
  struct scm *ncell_arena = g_news;
  struct scm *ncell_zero = ncell_arena + M2_CELL_SIZE;

  g_news = g_news + M2_CELL_SIZE;

  ncell_arena->type = TVECTOR;
  ncell_arena->length = cell_arena->length;
  ncell_arena->vector = g_news;

  ncell_zero->type = TCHAR;
  ncell_zero->value = 'n';
}

void
gc_up_arena ()
{
  long old_arena_bytes = (ARENA_SIZE + JAM_SIZE) * sizeof (struct scm);
  if (ARENA_SIZE / 2 < MAX_ARENA_SIZE / 4)
    {
      ARENA_SIZE = ARENA_SIZE * 2;
      JAM_SIZE = JAM_SIZE * 2;
      GC_SAFETY = GC_SAFETY * 2;
    }
  else
    ARENA_SIZE = MAX_ARENA_SIZE - JAM_SIZE;
  long arena_bytes = (ARENA_SIZE + JAM_SIZE) * sizeof (struct scm);
  long stack_offset = (arena_bytes * 2);
  long realloc_bytes = (arena_bytes * 2) + (STACK_SIZE * sizeof (struct scm));
  void *p = realloc (g_cells - M2_CELL_SIZE, realloc_bytes);
  if (p == 0)
    {
      eputs ("realloc failed, g_free=");
      eputs (ltoa (cast_voidp_to_long (g_free)));
      eputs (":");
      long i = g_free - g_cells;
      i = i / M2_CELL_SIZE;
      eputs (ltoa (ARENA_SIZE - i));
      eputs ("\n");
      assert_msg (0, "0");
      exit (1);
    }
  g_cells = p;
  memcpy (p + stack_offset, p + old_arena_bytes, STACK_SIZE * sizeof (struct scm *));
  g_cells = g_cells + M2_CELL_SIZE;
}

/* A pointer relocating memcpy for pointer cells to avoid using only
   half of the allocated cells.

   For number based cells a simply memcpy could be used, as number
   references are relative.

   A simple stop and copy (SICP 5.3) garbage collector allocates twice
   the cell arena size only for the garbage collector.  The garbage
   collector switches back and forth between cells and news, thus
   utilizing only half the allocated memory. */
void
gc_cellcpy (struct scm *dest, struct scm *src, size_t n)
{
  void *p = src;
  void *q = dest;
  long dist = p - q;
  long t;
  long a;
  long d;
  int i;
  int c;
  while (n != 0)
    {
      t = src->type;
      a = src->car_value;
      d = src->cdr_value;
      dest->type = t;
      if (t == TBROKEN_HEART)
        assert_msg (0, "gc_cellcpy: broken heart");
      if (t == TMACRO
          || t == TPAIR
          || t == TREF
          || t == TVARIABLE)
        dest->car_value = a - dist;
      else
        dest->car_value = a;
      if (t == TBYTES
          || t == TCLOSURE
          || t == TCONTINUATION
          || t == TKEYWORD
          || t == TMACRO
          || t == TPAIR
          || t == TPORT
          || t == TSPECIAL
          || t == TSTRING
          || t == TSTRUCT
          || t == TSYMBOL
          || t == TVALUES
          || t == TVECTOR)
        dest->cdr_value = d - dist;
      else
        dest->cdr_value = d;
      if (t == TBYTES)
        {
          if (g_debug > 5)
            {
              eputs ("copying bytes[");
              eputs (ntoab (cast_voidp_to_long (cell_bytes (src)), 16, 0));
              eputs (", ");
              eputs (ntoab (a, 10, 0));
              eputs ("]: ");
              eputs (cell_bytes (src));
              eputs ("\n to [");
              eputs (ntoab (cast_voidp_to_long (cell_bytes (dest)), 16, 0));
            }
          memcpy (cell_bytes (dest), cell_bytes (src), a);
          if (g_debug > 5)
            {
              eputs ("]: ");
              eputs (cell_bytes (dest));
              eputs ("\n");
            }
          i = bytes_cells (a);
          n = n - i;
          c = i * M2_CELL_SIZE;
          dest = dest + c;
          src = src + c;
        }
      else
        {
          n = n - 1;
          dest = dest + M2_CELL_SIZE;
          src = src + M2_CELL_SIZE;
        }
    }
}

/* We do not actually flip cells and news, instead we move news back to
   cells. */
void
gc_flip ()
{
  if (g_free - g_news > JAM_SIZE)
    JAM_SIZE = ((g_free - g_news) * 3) / 2;

  cell_arena = g_cells - M2_CELL_SIZE; /* For debugging. */
  gc_cellcpy (g_cells, g_news, (g_free - g_news) / M2_CELL_SIZE);

  long dist = g_news - g_cells;
  g_free = g_free - dist;
  g_symbols = g_symbols - dist;
  g_macros = g_macros - dist;
  g_ports = g_ports - dist;
  M0 = M0 - dist;

  long i;
  for (i = g_stack; i < STACK_SIZE; i = i + 1)
    g_stack_array[i] = g_stack_array[i] - dist;

  if (g_debug > 2)
    gc_stats_ (";;; => jam");
}

struct scm *
gc_copy (struct scm *old)               /*:((internal)) */
{
  if (old->type == TBROKEN_HEART)
    return old->car;
  struct scm *new = g_free;
  g_free = g_free + M2_CELL_SIZE;
  copy_cell (new, old);
  if (new->type == TSTRUCT || new->type == TVECTOR)
    {
      new->vector = g_free;
      long i;
      for (i = 0; i < old->length; i = i + 1)
        {
          copy_cell (g_free, cell_ref (old->vector, i));
          g_free = g_free + M2_CELL_SIZE;
        }
    }
  else if (new->type == TBYTES)
    {
      char const *src = cell_bytes (old);
      char *dest = cell_bytes (new);
      size_t length = new->length;
      memcpy (dest, src, length);
      g_free = g_free + ((bytes_cells (length) - 1) * M2_CELL_SIZE);

      if (g_debug > 4)
        {
          eputs ("gc copy bytes: ");
          eputs (src);
          eputs ("\n");
          eputs ("    length: ");
          eputs (ltoa (old->length));
          eputs ("\n");
          eputs ("    nlength: ");
          eputs (ltoa (new->length));
          eputs ("\n");
          eputs ("        ==> ");
          eputs (dest);
          eputs ("\n");
        }
    }
  old->type = TBROKEN_HEART;
  old->car = new;
  return new;
}

struct scm *
gc_relocate_car (struct scm *new, struct scm *car)      /*:((internal)) */
{
  new->car = car;
  return cell_unspecified;
}

struct scm *
gc_relocate_cdr (struct scm *new, struct scm *cdr)      /*:((internal)) */
{
  new->cdr = cdr;
  return cell_unspecified;
}

void
gc_loop (struct scm *scan)
{
  struct scm *car;
  struct scm *cdr;
  long t;
  while (scan < g_free)
    {
      t = scan->type;
      if (t == TBROKEN_HEART)
        assert_msg (0, "gc_loop: broken heart");
      /* *INDENT-OFF* */
      if (t == TMACRO
          || t == TPAIR
          || t == TREF
          || t == TVARIABLE)
        /* *INDENT-ON* */
        {
          car = gc_copy (scan->car);
          gc_relocate_car (scan, car);
        }
      /* *INDENT-OFF* */
      if (t == TCLOSURE
          || t == TCONTINUATION
          || t == TKEYWORD
          || t == TMACRO
          || t == TPAIR
          || t == TPORT
          || t == TSPECIAL
          || t == TSTRING
          /*|| t == TSTRUCT handled by gc_copy */
          || t == TSYMBOL
          || t == TVALUES
          /*|| t == TVECTOR handled by gc_copy */
          )
        /* *INDENT-ON* */
        {
          cdr = gc_copy (scan->cdr);
          gc_relocate_cdr (scan, cdr);
        }
      if (t == TBYTES)
        scan = scan + (bytes_cells (scan->length) * M2_CELL_SIZE);
      else
        scan = scan + M2_CELL_SIZE;
    }
  gc_flip ();
}

struct scm *
gc_check ()
{
  long used = ((g_free - g_cells) / M2_CELL_SIZE) + GC_SAFETY;
  if (used >= ARENA_SIZE)
    return gc ();
  return cell_unspecified;
}

void
gc_ ()
{
  gc_init_news ();
  if (g_debug == 2)
    eputs (".");
  if (g_debug > 2)
    {
      gc_stats_ (";;; gc");
      eputs (";;; free: [");
      eputs (ltoa (ARENA_SIZE - gc_free ()));
      eputs ("]...");
    }
  g_free = g_news + M2_CELL_SIZE;

  if (ARENA_SIZE < MAX_ARENA_SIZE
      && cast_voidp_to_charp (g_cells) == g_arena + M2_CELL_SIZE)
    {
      if (g_debug == 2)
        eputs ("+");
      if (g_debug > 2)
        {
          eputs (" up[");
          eputs (ltoa (cast_voidp_to_long (g_cells)));
          eputs (",");
          eputs (ltoa (cast_voidp_to_long (g_news)));
          eputs (":");
          eputs (ltoa (ARENA_SIZE));
          eputs (",");
          eputs (ltoa (MAX_ARENA_SIZE));
          eputs ("]...");
        }
      gc_up_arena ();
    }

  struct scm *new_cell_nil = g_free;
  struct scm *s;
  for (s = cell_nil; s < g_symbol_max; s = s + M2_CELL_SIZE)
    gc_copy (s);

  g_symbols = gc_copy (g_symbols);
  g_macros = gc_copy (g_macros);
  g_ports = gc_copy (g_ports);
  M0 = gc_copy (M0);

  long i;
  for (i = g_stack; i < STACK_SIZE; i = i + 1)
    copy_stack (i, gc_copy (g_stack_array[i]));

  gc_loop (new_cell_nil);
}

struct scm *
gc ()
{
  if (getenv ("MES_DUMP") != 0)
    gc_dump_arena (g_cells, gc_free ());
  if (g_debug > 5)
    {
      eputs ("symbols: ");
      write_error_ (g_symbols);
      eputs ("\n");
      eputs ("R0: ");
      write_error_ (R0);
      eputs ("\n");
    }
  clock_gettime (CLOCK_PROCESS_CPUTIME_ID, gc_start_time);
  gc_push_frame ();
  gc_ ();
  gc_pop_frame ();
  clock_gettime (CLOCK_PROCESS_CPUTIME_ID, gc_end_time);
  long time = seconds_and_nanoseconds_to_long
    (gc_end_time->tv_sec - gc_start_time->tv_sec,
     gc_end_time->tv_nsec - gc_start_time->tv_nsec);
  gc_time = gc_time + time;
  gc_count = gc_count + 1;
  if (g_debug > 5)
    {
      eputs ("symbols: ");
      write_error_ (g_symbols);
      eputs ("\n");
      eputs ("R0: ");
      write_error_ (R0);
      eputs ("\n");
    }
  if (getenv ("MES_DUMP") != 0)
    gc_dump_arena (g_cells, gc_free ());
  return cell_unspecified;
}

void
gc_push_frame ()
{
  if (g_stack < GC_FRAME_SIZE)
    assert_msg (0, "STACK FULL");
  g_stack_array[g_stack - 1] = cell_f;
  g_stack_array[g_stack - 2] = R0;
  g_stack_array[g_stack - 3] = R1;
  g_stack_array[g_stack - 4] = R2;
  g_stack_array[g_stack - 5] = R3;
  g_stack = g_stack - GC_FRAME_SIZE;
}

void
gc_peek_frame ()
{
  R3 = g_stack_array[g_stack];
  R2 = g_stack_array[g_stack + 1];
  R1 = g_stack_array[g_stack + 2];
  R0 = g_stack_array[g_stack + 3];
  g_stack_array[g_stack + GC_FRAME_PROCEDURE];
}

void
gc_pop_frame ()
{
  gc_peek_frame ();
  g_stack = g_stack + GC_FRAME_SIZE;
}

void
dumpc (char c)
{
  fdputc (c, g_dump_filedes);
}

void
dumps (char const *s)
{
  fdputs (s, g_dump_filedes);
}

void
gc_dump_register (char const* n, struct scm *r)
{
  dumps (n); dumps (": ");
  long i = cast_scmp_to_long (r);
  long a = cast_charp_to_long (g_arena);
  i = i - a;
  i = i / M2_CELL_SIZE;
  dumps (ltoa (i));
  dumps ("\n");
}

void
gc_dump_state ()
{
  gc_dump_register ("R0", R0);
  gc_dump_register ("R1", R1);
  gc_dump_register ("R2", R2);
  gc_dump_register ("R3", R3);
  gc_dump_register ("M0", M0);
  gc_dump_register ("g_symbols", g_symbols);
  gc_dump_register ("g_symbol_max", g_symbol_max);
  gc_dump_register ("g_macros", g_macros);
  gc_dump_register ("g_ports", g_ports);
  gc_dump_register ("cell_zero", cell_zero);
  gc_dump_register ("cell_nil", cell_nil);
}

void
gc_dump_stack ()
{
  long i = g_stack;
  while (i < STACK_SIZE)
    {
      gc_dump_register (itoa (i), g_stack_array[i]);
      i = i + 1;
    }
}

void
gc_dump_arena (struct scm *cells, long size)
{
  struct scm *end = g_cells + (size * M2_CELL_SIZE);
  struct scm *dist = cells;
  int i;
  long t;
  long a;
  long d;
  int c;
  char* p;
  if (g_dump_filedes == 0)
    g_dump_filedes = mes_open ("dump.mo", O_CREAT|O_WRONLY, 0644);
  dumps ("stack="); dumps (ltoa (g_stack)); dumpc ('\n');
  dumps ("size="); dumps (ltoa (size)); dumpc ('\n');
  gc_dump_state ();
  gc_dump_stack ();
  while (end->type == 0 && end->car == 0 && end->cdr == 0)
    {
      end = end - M2_CELL_SIZE;
      size = size - 1;
    }
  while (size > 0)
    {
      for (i=0; i < 16; i = i + 1)
        {
          t = cells->type;
          a = cells->car_value;
          d = cells->cdr_value;
          if (size == 0)
            dumps ("0 0 0");
          else
            {
              dumps (ltoa (t));
              dumpc (' ');
              if (t == TMACRO
                  || t == TPAIR
                  || t == TREF
                  || t == TVARIABLE)
                {
                  dumps (ltoa ((cells->car - dist) / M2_CELL_SIZE));
                  /* dumps ("["); dumps (ltoa (a)); dumps ("]"); */
                }
              else
                dumps (ltoa (a));
              dumpc (' ');
              if (t != TBYTES)
                {
                  if (t == TCLOSURE
                      || t == TCONTINUATION
                      || t == TKEYWORD
                      || t == TMACRO
                      || t == TPAIR
                      || t == TPORT
                      || t == TSPECIAL
                      || t == TSTRING
                      || t == TSTRUCT
                      || t == TSYMBOL
                      || t == TVALUES
                      || t == TVECTOR)
                    {
                      dumps (ltoa ((cells->cdr - dist) / M2_CELL_SIZE));
                      /* dumps ("["); dumps (ltoa (d)); dumps ("]"); */
                    }
                  else if (t == TNUMBER && d > 1000)
                    dumps (ltoa (1001));
                  else
                    dumps (ltoa (d));
                }
              if (t == TBYTES)
                {
                  c = bytes_cells (a);
                  p = cell_bytes (cells);
                  size = size - c;
                  dumpc ('"');
                  while (a > 0)
                    {
                      if (p[0] != 0)
                        dumpc (p[0]);
                      p = p + 1;
                      a = a - 1;
                    }
                  dumpc ('"');
                  cells = cells + c * M2_CELL_SIZE;
                  size = size - c;
                }
              else
                {
                  cells = cells + M2_CELL_SIZE;
                  size = size - 1;
                }
            }
          if (i != 15)
            dumps ("  ");
          else
            dumpc ('\n');
        }
      dumpc ('\n');
    }
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/hash.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/hash.c used in: Step 106, Step 219
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

#include <string.h>

int
hash_cstring (char const *s, long size)
{
  int hash = s[0] * 37;
  if (s[0] != 0)
    if (s[1] != 0)
      hash = hash + (s[1] * 43);
  assert_msg (size != 0, "size");
  hash = hash % size;
  return hash;
}

int
hashq_ (struct scm *x, long size)
{
  if (x->type == TSPECIAL || x->type == TSYMBOL)
    return hash_cstring (cell_bytes (x->string), size);    /* FIXME: hash x directly. */
  error (cell_symbol_system_error, cons (make_string0 ("hashq_: not a symbol"), x));
}

int
hash_ (struct scm *x, long size)
{
  if (x->type != TSTRING)
    {
      eputs ("hash_ failed, not a string:");
      display_error_ (x);
      assert_msg (0, "0");
    }
  return hash_cstring (cell_bytes (x->string), size);
}

struct scm *
hashq (struct scm *x, struct scm *size)
{
  eputs ("hashq not supporteed\n");
  assert_msg (0, "0");
}

struct scm *
hash (struct scm *x, struct scm *size)
{
  eputs ("hash not supporteed\n");
  assert_msg (0, "0");
}

struct scm *
hashq_get_handle (struct scm *table, struct scm *key, struct scm *dflt)
{
  struct scm *s = struct_ref_ (table, 3);
  long size = s->value;
  unsigned hash = hashq_ (key, size);
  struct scm *buckets = struct_ref_ (table, 4);
  struct scm *bucket = vector_ref_ (buckets, hash);
  struct scm *x = cell_f;
  if (dflt->type == TPAIR)
    x = dflt->car;
  if (bucket->type == TPAIR)
    x = assq (key, bucket);
  return x;
}

struct scm *
hashq_ref (struct scm *table, struct scm *key, struct scm *dflt)
{
  struct scm *x = hashq_get_handle (table, key, dflt);
  if (x != cell_f)
    x = x->cdr;
  return x;
}

struct scm *
hash_ref (struct scm *table, struct scm *key, struct scm *dflt)
{
  struct scm *s = struct_ref_ (table, 3);
  long size = s->value;
  unsigned hash = hash_ (key, size);
  struct scm *buckets = struct_ref_ (table, 4);
  struct scm *bucket = vector_ref_ (buckets, hash);
  struct scm *x = cell_f;
  if (dflt->type == TPAIR)
    x = dflt->car;
  if (bucket->type == TPAIR)
    {
      x = assoc (key, bucket);
      if (x != cell_f)
        x = x->cdr;
    }
  return x;
}

struct scm *
hash_set_x_ (struct scm *table, unsigned hash, struct scm *key, struct scm *value)
{
  struct scm *buckets = struct_ref_ (table, 4);
  struct scm *bucket = vector_ref_ (buckets, hash);
  if (bucket->type != TPAIR)
    bucket = cell_nil;
  bucket = acons (key, value, bucket);
  vector_set_x_ (buckets, hash, bucket);
  return value;
}

struct scm *
hashq_set_x (struct scm *table, struct scm *key, struct scm *value)
{
  struct scm *s = struct_ref_ (table, 3);
  long size = s->value;
  unsigned hash = hashq_ (key, size);
  return hash_set_x_ (table, hash, key, value);
}

struct scm *
hash_set_x (struct scm *table, struct scm *key, struct scm *value)
{
  struct scm *s = struct_ref_ (table, 3);
  long size = s->value;
  unsigned hash = hash_ (key, size);
  return hash_set_x_ (table, hash, key, value);
}

struct scm *
hash_table_printer (struct scm *table)
{
  fdputs ("#<", __stdout);
  display_ (struct_ref_ (table, 2));
  fdputc (' ', __stdout);
  fdputs ("size: ", __stdout);
  display_ (struct_ref_ (table, 3));
  fdputc (' ', __stdout);
  struct scm *buckets = struct_ref_ (table, 4);
  fdputs ("buckets: ", __stdout);
  int i;
  struct scm *e;
  for (i = 0; i < buckets->length; i = i + 1)
    {
      e = vector_ref_ (buckets, i);
      if (e != cell_unspecified)
        {
          fdputc ('[', __stdout);
          while (e->type == TPAIR)
            {
              write_ (e->car->car);
              e = e->cdr;
              if (e->type == TPAIR)
                fdputc (' ', __stdout);
            }
          fdputs ("]\n  ", __stdout);
        }
    }
  fdputc ('>', __stdout);
}

struct scm *
make_hashq_type ()              /*:((internal)) */
{
  struct scm *fields = cell_nil;
  fields = cons (cell_symbol_buckets, fields);
  fields = cons (cell_symbol_size, fields);
  fields = cons (fields, cell_nil);
  fields = cons (cell_symbol_hashq_table, fields);
  return make_struct (cell_symbol_record_type, fields, cell_unspecified);
}

struct scm *
make_hash_table_ (long size)
{
  if (size == 0)
    size = 100;
  struct scm *hashq_type = make_hashq_type ();

  struct scm *buckets = make_vector_ (size, cell_unspecified);
  struct scm *values = cell_nil;
  values = cons (buckets, values);
  values = cons (make_number (size), values);
  values = cons (cell_symbol_hashq_table, values);
  /*FIXME: symbol/printer
    return make_struct (hashq_type, values, cstring_to_symbol ("hash-table-printer");*/
  return make_struct (hashq_type, values, cell_unspecified);
}

struct scm *
make_hash_table (struct scm *x)
{
  long size = 0;
  if (x->type == TPAIR)
    {
      assert_msg (x->type == TNUMBER, "x->type == TNUMBER");
      size = x->value;
    }
  return make_hash_table_ (size);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/lib.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/lib.c used in: Step 106, Step 220
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

/** Commentary:
    Scheme library functions not used by the eval/apply core.
 */

/** Code: */

#include "mes/lib.h"
#include "mes/mes.h"

#include <stdlib.h>

struct scm *
type_ (struct scm *x)
{
  return make_number (x->type);
}

struct scm *
car_ (struct scm *x)
{
  struct scm *a = x->car;
  if (x->type == TPAIR)
    return a;
  return make_number (cast_scmp_to_long (a));
}

struct scm *
cdr_ (struct scm *x)
{
  struct scm *d = x->cdr;
  if (x->type == TPAIR || x->type == TCLOSURE)
    return d;
  return make_number (cast_scmp_to_long (d));
}

struct scm *
xassq (struct scm *x, struct scm *a)            /* For speed in core. */
{
  while (a != cell_nil)
    {
      if (x == a->car->cdr)
        return a->car;
      a = a->cdr;
    }
  return cell_f;
}

struct scm *
memq (struct scm *x, struct scm *a)
{
  int t = x->type;
  if (t == TCHAR || t == TNUMBER)
    {
      long v = x->value;
      while (a != cell_nil)
        {
          if (v == a->car->value)
            return a;
          a = a->cdr;
        }
      return cell_f;
    }
  if (t == TKEYWORD)
    {
      while (a != cell_nil)
        {
          if (a->car->type == TKEYWORD)
            if (string_equal_p (x, a->car) == cell_t)
              return a;
          a = a->cdr;
        }
      return cell_f;
    }
  while (a != cell_nil)
    {
      if (x == a->car)
        return a;
      a = a->cdr;
    }
  return cell_f;
}

struct scm *
equal2_p (struct scm *a, struct scm *b)
{
  long i;
  struct scm *ai;
  struct scm *bi;

equal2:
  if (a == b)
    return cell_t;
  if (a->type == TPAIR && b->type == TPAIR)
    {
      if (equal2_p (a->car, b->car) == cell_t)
        {
          a = a->cdr;
          b = b->cdr;
          goto equal2;
        }
      return cell_f;
    }
  if (a->type == TSTRING && b->type == TSTRING)
    return string_equal_p (a, b);
  if (a->type == TVECTOR && b->type == TVECTOR)
    {
      if (a->length != b->length)
        return cell_f;
      for (i = 0; i < a->length; i = i + 1)
        {
          ai = cell_ref (a->vector, i);
          bi = cell_ref (b->vector, i);
          if (ai->type == TREF)
            ai = ai->ref;
          if (bi->type == TREF)
            bi = bi->ref;
          if (equal2_p (ai, bi) == cell_f)
            return cell_f;
        }
      return cell_t;
    }
  return eq_p (a, b);
}

struct scm *
last_pair (struct scm *x)
{
  while (x != cell_nil)
    {
      if (x->cdr == cell_nil)
        return x;
      x = x->cdr;
    }
  return x;
}

struct scm *
pair_p (struct scm *x)
{
  if (x->type == TPAIR)
    return cell_t;
  return cell_f;
}

struct scm *
char_to_integer (struct scm *x)
{
  return make_number (x->value);
}

struct scm *
integer_to_char (struct scm *x)
{
  return make_char (x->value);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/m2.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/m2.c used in: Step 106
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

struct scm *
apply_builtin0 (struct scm *fn)
{
  FUNCTION fp = builtin_function (fn);
  return fp ();
}

struct scm *
apply_builtin1 (struct scm *fn, struct scm *x)
{
  FUNCTION fp = builtin_function (fn);
  return fp (x);
}

struct scm *
apply_builtin2 (struct scm *fn, struct scm *x, struct scm *y)
{
  FUNCTION fp = builtin_function (fn);
  return fp (x, y);
}

struct scm *
apply_builtin3 (struct scm *fn, struct scm *x, struct scm *y, struct scm *z)
{
  FUNCTION fp = builtin_function (fn);
  return fp (x, y, z);
}

#undef cast_charp_to_scmp
#undef cast_charp_to_scmpp
#undef cast_voidp_to_charp
#undef cast_scmp_to_long
#undef cast_scmp_to_charp

struct scm *
cast_charp_to_scmp (char const *i)
{
  return i;
}

struct scm **
cast_charp_to_scmpp (char const *i)
{
  return i;
}

char*
cast_voidp_to_charp (void const *i)
{
  return i;
}

long
cast_scmp_to_long (struct scm *i)
{
  return i;
}

char*
cast_scmp_to_charp (struct scm *i)
{
  return i;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/math.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/math.c used in: Step 106, Step 221
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2021 W. J. van der Laan <laanwj@protonmail.com>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>

void
assert_number (char const *name, struct scm *x)
{
  if (x->type != TNUMBER)
    {
      eputs (name);
      error (cell_symbol_not_a_number, x);
    }
}

struct scm *
greater_p (struct scm *x)               /*:((name . ">") (arity . n)) */
{
  if (x == cell_nil)
    return cell_t;
  assert_number ("greater_p", x->car);
  long n = x->car->value;
  x = x->cdr;
  struct scm *i;
  long v;
  while (x != cell_nil)
    {
      assert_number ("greater_p", x->car);
      i = car (x);
      v = i->value;
      if (v >= n)
        return cell_f;
      n = v;
      x = cdr (x);
    }
  return cell_t;
}

struct scm *
less_p (struct scm *x)                  /*:((name . "<") (arity . n)) */
{
  if (x == cell_nil)
    return cell_t;
  assert_number ("less_p", x->car);
  long n = x->car->value;
  x = x->cdr;
  struct scm *i;
  long v;
  while (x != cell_nil)
    {
      assert_number ("less_p", x->car);
      i = car (x);
      v = i->value;
      if (v <= n)
        return cell_f;
      n = v;
      x = cdr (x);
    }
  return cell_t;
}

struct scm *
is_p (struct scm *x)                    /*:((name . "=") (arity . n)) */
{
  if (x == cell_nil)
    return cell_t;
  assert_number ("is_p", x->car);
  long n = x->car->value;
  x = cdr (x);
  struct scm *i;
  long v;
  while (x != cell_nil)
    {
      i = car (x);
      v = i->value;
      if (v != n)
        return cell_f;
      x = cdr (x);
    }
  return cell_t;
}

struct scm *
minus (struct scm *x)                   /*:((name . "-") (arity . n)) */
{
  assert_number ("minus", x->car);
  long n = x->car->value;
  x = cdr (x);
  if (x == cell_nil)
    n = -n;
  struct scm *i;
  long v;
  while (x != cell_nil)
    {
      i = car (x);
      assert_number ("minus", i);
      v = i->value;
      n = n - v;
      x = cdr (x);
    }
  return make_number (n);
}

struct scm *
plus (struct scm *x)                    /*:((name . "+") (arity . n)) */
{
  long n = 0;
  struct scm *i;
  long v;
  while (x != cell_nil)
    {
      i = car (x);
      assert_number ("plus", i);
      v = i->value;
      n = n + v;
      x = cdr (x);
    }
  return make_number (n);
}

struct scm *
divide (struct scm *x)                  /*:((name . "/") (arity . n)) */
{
  long n = 1;
  struct scm *i;
  long v;
  if (x != cell_nil)
    {
      i = car (x);
      assert_number ("divide", i);
      v = i->value;
      n = v;
      x = cdr (x);
    }
  int sign_p = 0;
  size_t u = n;
  if (n < 0)
    {
      sign_p = 1;
      u = -n;
    }
  size_t w;
  while (x != cell_nil)
    {
      i = car (x);
      assert_number ("divide", i);
      v = i->value;
      sign_p = sign_p && v > 0 || !sign_p && v < 0;
      w = v;
      if (v == 0)
        error (cstring_to_symbol ("divide-by-zero"), x);
      if (u == 0)
        break;
      if (w != 1)
        u = u / w;
      x = cdr (x);
    }
  n = u;
  if (sign_p)
    n = -n;
  return make_number (n);
}

struct scm *
modulo (struct scm *a, struct scm *b)
{
  assert_number ("modulo", a);
  assert_number ("modulo", b);
  long n = a->value;
  long v = b->value;
  if (v == 0)
    error (cstring_to_symbol ("divide-by-zero"), a);
  int sign_p = 0;
  size_t w = v;
  if (v < 0)
    {
      sign_p = 1;
      w = -v;
    }
  while (n < 0)
    n = n + w;
  size_t u = n;
  if (u != 0)
    u = u % w;
  n = u;
  if (sign_p)
    n = -n;
  return make_number (n);
}

struct scm *
multiply (struct scm *x)                /*:((name . "*") (arity . n)) */
{
  long n = 1;
  struct scm *i;
  long v;
  while (x != cell_nil)
    {
      i = car (x);
      assert_number ("multiply", i);
      v = i->value;
      n = n * v;
      x = cdr (x);
    }
  return make_number (n);
}

struct scm *
logand (struct scm *x)                  /*:((arity . n)) */
{
  long n = -1;
  struct scm *i;
  long v;
  while (x != cell_nil)
    {
      i = car (x);
      assert_number ("multiply", i);
      v = i->value;
      n = n & v;
      x = cdr (x);
    }
  return make_number (n);
}

struct scm *
logior (struct scm *x)                  /*:((arity . n)) */
{
  long n = 0;
  struct scm *i;
  long v;
  while (x != cell_nil)
    {
      i = car (x);
      assert_number ("logior", i);
      v = i->value;
      n = n | v;
      x = cdr (x);
    }
  return make_number (n);
}

struct scm *
lognot (struct scm *x)
{
  assert_number ("lognot", x);
  long n = ~x->value;
  return make_number (n);
}

struct scm *
logxor (struct scm *x)                  /*:((arity . n)) */
{
  long n = 0;
  struct scm *i;
  long v;
  while (x != cell_nil)
    {
      i = car (x);
      assert_number ("logxor", i);
      v = i->value;
      n = n ^ v;
      x = cdr (x);
    }
  return make_number (n);
}

struct scm *
ash (struct scm *n, struct scm *count)
{
  assert_number ("ash", n);
  assert_number ("ash", count);
  long cn = n->value;
  long ccount = count->value;
  long result;
  if (ccount < 0)
    result = cn >> -ccount;
  else
    result = cn << ccount;
  return make_number (result);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/mes.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/mes.c used in: Step 106, Step 222
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>

struct scm *
mes_g_stack (struct scm *a)             /*:((internal)) */
{
  g_stack = STACK_SIZE;
  R0 = a;
  R1 = make_char (0);
  R2 = make_char (0);
  R3 = make_char (0);
  return R0;
}

struct scm *
mes_environment (int argc, char **argv)
{
  struct scm *a = init_symbols ();

  char *compiler = "gnuc";
#if __MESC__
  compiler = "mesc";
#elif __TINYC__
  compiler = "tcc";
#endif
  a = acons (cell_symbol_compiler, make_string0 (compiler), a);

  char *arch;
#if __i386__
  arch = "x86";
#elif __arm__
  arch = "arm";
#elif __x86_64__
  arch = "x86_64";
#else
#error arch not supported
#endif
  a = acons (cell_symbol_arch, make_string0 (arch), a);

#if !MES_MINI
  struct scm *lst = cell_nil;
  int i;
  for (i = argc - 1; i >= 0; i = i - 1)
    lst = cons (make_string0 (argv[i]), lst);
  a = acons (cell_symbol_argv, lst, a);
#endif

  return mes_g_stack (a);
}

int
try_open_boot (char *file_name, char const *boot, char const *location)
{
  strcpy (file_name + strlen (file_name), boot);
  if (g_debug > 1)
    {
      eputs ("mes: reading boot-0 [");
      eputs (location);
      eputs ("]: ");
      eputs (file_name);
      eputs ("\n");
    }
  int fd = mes_open (file_name, O_RDONLY, 0);
  if (g_debug != 0 && fd > 0)
    {
      eputs ("mes: read boot-0: ");
      eputs (file_name);
      eputs ("\n");
    }
  return fd;
}

void
open_boot ()
{
  __stdin = -1;
  char *boot = __open_boot_buf;
  char *file_name = __open_boot_file_name;
  strcpy (g_datadir, ".");
  if (getenv ("MES_BOOT") != 0)
    strcpy (boot, getenv ("MES_BOOT"));
  else
    strcpy (boot, "boot-0.scm");
  if (getenv ("MES_PREFIX") != 0)
    {
      strcpy (g_datadir, getenv ("MES_PREFIX"));
      strcpy (g_datadir + strlen (g_datadir), "/mes");
      strcpy (file_name, g_datadir);
      strcpy (file_name + strlen (file_name), "/module/mes/");
      __stdin = try_open_boot (file_name, boot, "MES_PREFIX");
      if (__stdin < 0)
        {
          strcpy (g_datadir, getenv ("MES_PREFIX"));
          strcpy (g_datadir + strlen (g_datadir), "/share/mes");
          strcpy (file_name, g_datadir);
          strcpy (file_name + strlen (file_name), "/module/mes/");
          __stdin = try_open_boot (file_name, boot, "MES_PREFIX/share/mes");
        }
    }
  if (__stdin < 0)
    {
      g_datadir[0] = 0;
      if (getenv ("srcdest") != 0)
        strcpy (g_datadir, getenv ("srcdest"));
      strcpy (g_datadir + strlen (g_datadir), "mes");
      strcpy (file_name, g_datadir);
      strcpy (file_name + strlen (file_name), "/module/mes/");
      __stdin = try_open_boot (file_name, boot, "${srcdest}mes");
    }
  if (__stdin < 0)
    {
      file_name[0] = 0;
      __stdin = try_open_boot (file_name, boot, "<boot>");
    }
  if (__stdin < 0)
    {
      eputs ("mes: boot failed: no such file: ");
      eputs (boot);
      eputs ("\n");
      exit (1);
    }
}

struct scm *
read_boot ()                    /*:((internal)) */
{
  R2 = read_input_file_env (R0);
  __stdin = STDIN;
  return R2;
}

void
init (char **envp)
{
  environ = envp;
  __execl_c_argv = malloc (1024 * sizeof (char *));     /* POSIX minimum: 4096 */
  __gettimeofday_time = malloc (sizeof (struct timeval));
  __get_internal_run_time_ts = malloc (sizeof (struct timespec));
  __open_boot_buf = malloc (PATH_MAX);
  __open_boot_file_name = malloc (PATH_MAX);
  __reader_read_char_buf = malloc (10);
  __setenv_buf = malloc (1024);
  g_datadir = malloc (1024);
  g_start_time = malloc (sizeof (struct timespec));
  memset (g_start_time, 0, sizeof (struct timespec));
  gc_start_time = malloc (sizeof (struct timespec));
  gc_end_time = malloc (sizeof (struct timespec));

  char *p = getenv ("MES_DEBUG");
  if (p != 0)
    g_debug = atoi (p);
  g_mini = cast_charp_to_long (getenv ("MES_MINI"));
  open_boot ();
  gc_init ();
}

int
main (int argc, char **argv, char **envp)
{
  init (envp);

  struct scm *a = mes_environment (argc, argv);
  a = mes_builtins (a);
  a = init_time (a);
  M0 = make_initial_module (a);
  g_macros = make_hash_table_ (0);

  if (g_debug > 5)
    module_printer (M0);

  struct scm *program = read_boot ();
  R0 = acons (cell_symbol_program, program, R0);
  push_cc (R2, cell_unspecified, R0, cell_unspecified);

  if (g_debug > 2)
    gc_stats_ ("\n gc boot");
  if (g_debug > 3)
    {
      eputs ("program: ");
      write_error_ (R1);
      eputs ("\n");
    }
  R3 = cell_vm_begin_expand;
  R1 = eval_apply ();
  if (g_debug != 0)
    {
      write_error_ (R1);
      eputs ("\n");
    }
  if (g_debug != 0)
    {
      if (g_debug > 5)
        module_printer (M0);

      if (g_debug < 3)
        gc_stats_ ("\ngc run");
      MAX_ARENA_SIZE = 0;

      gc (g_stack);
      if (g_debug < 3)
        gc_stats_ (" => ");

      if (g_debug > 5)
        {
          eputs ("\nports:");
          write_error_ (g_ports);
          eputs ("\n");
        }
      eputs ("\n");
    }

  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/module.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/module.c used in: Step 106, Step 223
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

struct scm *
make_module_type ()             /*:(internal)) */
{
  struct scm *fields = cell_nil;
  fields = cons (cstring_to_symbol ("globals"), fields);
  fields = cons (cstring_to_symbol ("locals"), fields);
  fields = cons (cstring_to_symbol ("name"), fields);
  fields = cons (fields, cell_nil);
  fields = cons (cell_symbol_module, fields);
  return make_struct (cell_symbol_record_type, fields, cell_unspecified);
}

struct scm *
make_initial_module (struct scm *a)     /*:((internal)) */
{
  struct scm *module_type = make_module_type ();
  a = acons (cell_symbol_module, module_type, a);

  struct scm *hashq_type = make_hashq_type ();
  a = acons (cell_symbol_hashq_table, hashq_type, a);

  struct scm *name = cons (cstring_to_symbol ("boot"), cell_nil);
  struct scm *globals = make_hash_table_ (0);
  struct scm *locals = cell_nil;

  struct scm *values = cell_nil;
  values = cons (globals, values);
  values = cons (locals, values);
  values = cons (name, values);
  values = cons (cell_symbol_module, values);
  struct scm *module = make_struct (module_type, values, cstring_to_symbol ("module-printer"));
  R0 = cell_nil;
  R0 = cons (a->cdr->car, R0);
  R0 = cons (a->car, R0);
  M0 = module;
  while (a->type == TPAIR)
    {
      module_define_x (module, a->car->car, a->car->cdr);
      a = a->cdr;
    }

  return module;
}

struct scm *
module_printer (struct scm *module)
{
  fdputs ("#<", __stdout);
  display_ (struct_ref_ (module, 2));
  fdputc (' ', __stdout);
  fdputs ("name: ", __stdout);
  display_ (struct_ref_ (module, 3));
  fdputc (' ', __stdout);
  fdputs ("locals: ", __stdout);
  display_ (struct_ref_ (module, 4));
  fdputc (' ', __stdout);
  struct scm *table = struct_ref_ (module, 5);
  fdputs ("globals:\n  ", __stdout);
  display_ (table);
  fdputc ('>', __stdout);
}

struct scm *
module_variable (struct scm *module, struct scm *name)
{
  /*struct scm *locals = struct_ref_ (module, 3);*/
  struct scm *locals = module;
  struct scm *x = assq (name, locals);
  if (x == cell_f)
    {
      module = M0;
      struct scm *globals = struct_ref_ (module, 5);
      x = hashq_get_handle (globals, name, cell_f);
    }
  return x;
}

struct scm *
module_ref (struct scm *module, struct scm *name)
{
  struct scm *x = module_variable (module, name);
  if (x == cell_f)
    return cell_undefined;
  return x->cdr;
}

struct scm *
module_define_x (struct scm *module, struct scm *name, struct scm *value)
{
  module = M0;
  struct scm *globals = struct_ref_ (module, 5);
  return hashq_set_x (globals, name, value);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/posix.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/posix.c used in: Step 106, Step 224
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#if SYSTEM_LIBC
#define __raise(x) -1
#endif

struct scm *
abort_ ()                   /*:((name . "abort")) */
{
  if (g_debug > 0)
    eputs ("abort!\n");
  if (__raise (SIGABRT) < 0) /* could not raise SIGABRT */
    {
      /* Fail in any way possible */
      char* x = 0;
      x[0] = 2;
    }
  return cell_unspecified;
}

struct scm *
exit_ (struct scm *x)                   /*:((name . "exit")) */
{
  assert_msg (x->type == TNUMBER, "x->type == TNUMBER");
  exit (x->value);
}

int
peekchar ()
{
  if (__stdin >= 0)
    {
      int c = readchar ();
      unreadchar (c);
      return c;
    }
  struct scm *port = current_input_port ();
  struct scm *string = port->string;
  size_t length = string->length;
  if (length == 0)
    return -1;
  char const *p = cell_bytes (string->string);
  return p[0];
}

int
readchar ()
{
  if (__stdin >= 0)
    return fdgetc (__stdin);
  struct scm *port = current_input_port ();
  struct scm *string = port->string;
  size_t length = string->length;
  if (length == 0)
    return -1;
  char const *p = cell_bytes (string->string);
  int c = p[0];
  p = p + 1;
  port->string = make_string (p, length - 1);
  return c;
}

int
unreadchar (int c)
{
  if (__stdin >= 0)
    return fdungetc (c, __stdin);
  if (c == EOF) /* can't unread EOF */
    return c;
  struct scm *port = current_input_port ();
  struct scm *string = port->string;
  size_t length = string->length;
  char *p = cell_bytes (string->string);
  p = p - 1;
  string = make_string (p, length + 1);
  p = cell_bytes (string->string);
  p[0] = c;
  port->string = string;
  return c;
}

struct scm *
peek_byte ()
{
  return make_number (peekchar ());
}

struct scm *
read_byte ()
{
  return make_number (readchar ());
}

struct scm *
unread_byte (struct scm *i)
{
  unreadchar (i->value);
  return i;
}

struct scm *
peek_char ()
{
  return make_char (peekchar ());
}

struct scm *
read_char (struct scm *port)            /*:((arity . n)) */
{
  int fd = __stdin;
  if (port->type == TPAIR)
    if (port->car->type == TNUMBER)
      __stdin = port->car->value;
  struct scm *c = make_char (readchar ());
  __stdin = fd;
  return c;
}

struct scm *
unread_char (struct scm *i)
{
  unreadchar (i->value);
  return i;
}

struct scm *
write_char (struct scm *i)              /*:((arity . n)) */
{
  write_byte (i);
  return i;
}

struct scm *
write_byte (struct scm *x)              /*:((arity . n)) */
{
  struct scm *c = car (x);
  struct scm *p = cdr (x);
  int fd = __stdout;
  if (p->type == TPAIR)
    {
      struct scm *f = p->car;
      if (f->type == TNUMBER)
        {
          long v = f->value;
          if (v != 1)
            fd = v;
          if (v == 2)
            fd = __stderr;
        }
    }
  char cc = c->value;
  write (fd, &cc, 1);
#if !__MESC__
  assert_msg (c->type == TNUMBER || c->type == TCHAR, "c->type == TNUMBER || c->type == TCHAR");
#endif
  return c;
}

struct scm *
getenv_ (struct scm *s)                 /*:((name . "getenv")) */
{
  char *p;
  p = getenv (cell_bytes (s->string));
  if (p != 0)
    return make_string0 (p);
  return cell_f;
}

struct scm *
setenv_ (struct scm *s, struct scm *v)          /*:((name . "setenv")) */
{
  char *buf = __setenv_buf;
  strcpy (buf, cell_bytes (s->string));
  setenv (buf, cell_bytes (v->string), 1);
  return cell_unspecified;
}

struct scm *
access_p (struct scm *file_name, struct scm *mode)
{
  int result = access (cell_bytes (file_name->string), mode->value);
  if (result == 0)
    return cell_t;
  return cell_f;
}

struct scm *
current_input_port ()
{
  if (__stdin >= 0)
    return make_number (__stdin);
  struct scm *x = g_ports;
  struct scm *a;
  while (x != 0)
    {
      a = x->car;
      if (a->port == __stdin)
        return a;
      x = x->cdr;
    }
  return x->car;
}

struct scm *
open_input_file (struct scm *file_name)
{
  int filedes = mes_open (cell_bytes (file_name->string), O_RDONLY, 0);
  if (filedes == -1)
    error (cell_symbol_system_error, cons (make_string0 ("No such file or directory"), file_name));
  return make_number (filedes);
}

struct scm *
open_input_string (struct scm *string)
{
  struct scm *port = make_string_port (string);
  g_ports = cons (port, g_ports);
  return port;
}

struct scm *
set_current_input_port (struct scm *port)
{
  struct scm *prev = current_input_port ();
  if (port->type == TNUMBER)
    {
      int p = port->value;
      if (p != 0)
        __stdin = p;
      else
        __stdin = STDIN;
    }
  else if (port->type == TPORT)
    __stdin = port->port;
  return prev;
}

struct scm *
current_output_port ()
{
  return make_number (__stdout);
}

struct scm *
current_error_port ()
{
  return make_number (__stderr);
}

struct scm *
open_output_file (struct scm *x)        /*:((arity . n)) */
{
  struct scm *file_name = car (x);
  x = cdr (x);
  int mode = S_IRUSR | S_IWUSR;
  if (x->type == TPAIR)
    {
      struct scm *i = car (x);
      if (i->type == TNUMBER)
        mode = i->value;
    }
  return make_number (mes_open (cell_bytes (file_name->string), O_WRONLY | O_CREAT | O_TRUNC, mode));
}

struct scm *
set_current_output_port (struct scm *port)
{
  if (port->value != 0)
    __stdout = port->value;
  else
    __stdout = STDOUT;
  return current_output_port ();
}

struct scm *
set_current_error_port (struct scm *port)
{
  if (port->value != 0)
    __stderr = port->value;
  else
    __stderr = STDERR;
  return current_error_port ();
}

struct scm *
chmod_ (struct scm *file_name, struct scm *mode)        /*:((name . "chmod")) */
{
  chmod (cell_bytes (file_name->string), mode->value);
  return cell_unspecified;
}

struct scm *
isatty_p (struct scm *port)
{
  if (isatty (port->value) != 0)
    return cell_t;
  return cell_f;
}

struct scm *
primitive_fork ()
{
  return make_number (fork ());
}

struct scm *
execl_ (struct scm *file_name, struct scm *args)        /*:((name . "execl")) */
{
  char **c_argv = __execl_c_argv;
  int i = 0;

  if (length__ (args) > 1000)
    error (cell_symbol_system_error,
           cons (file_name, cons (make_string0 ("too many arguments"), cons (file_name, args))));
  c_argv[i] = cell_bytes (file_name->string);
  i = i + 1;
  struct scm *arg;
  while (args != cell_nil)
    {
      assert_msg (args->car->type == TSTRING, "args->car->type == TSTRING");
      arg = args->car;
      c_argv[i] = cell_bytes (arg->string);
      i = i + 1;
      args = args->cdr;
      if (g_debug > 2)
        {
          eputs ("arg[");
          eputs (itoa (i));
          eputs ("]: ");
          eputs (c_argv[i - 1]);
          eputs ("\n");
        }
    }
  c_argv[i] = 0;
  return make_number (execv (c_argv[0], c_argv));
}

struct scm *
waitpid_ (struct scm *pid, struct scm *options)
{
  int status;
  int child = waitpid (pid->value, &status, options->value);
  return cons (make_number (child), make_number (status));
}

#if __x86_64__
/* Nanoseconds on 64-bit systems with POSIX timers.  */
// CONSTANT TIME_UNITS_PER_SECOND 1000000000
#define TIME_UNITS_PER_SECOND 1000000000U
#else
/* Milliseconds for everyone else.  */
// CONSTANT TIME_UNITS_PER_SECOND 1000
#define TIME_UNITS_PER_SECOND 1000U
#endif

struct scm *
init_time (struct scm *a)               /*:((internal)) */
{
  clock_gettime (CLOCK_PROCESS_CPUTIME_ID, g_start_time);
  a = acons (cell_symbol_internal_time_units_per_second, make_number (TIME_UNITS_PER_SECOND), a);
}

struct scm *
current_time ()
{
  return make_number (time (0));
}

struct scm *
gettimeofday_ ()                /*:((name . "gettimeofday")) */
{
  struct timeval *time = __gettimeofday_time;
  gettimeofday (time, 0);
  return cons (make_number (time->tv_sec), make_number (time->tv_usec));
}

#define UL1000000000 1000000000UL
// CONSTANT UL1000000000 1000000000
long
seconds_and_nanoseconds_to_long (long s, long ns)
{
  size_t uns = ns;
  if (ns < 0)
    {
      uns = - ns;
      return s * TIME_UNITS_PER_SECOND - uns / (UL1000000000 / TIME_UNITS_PER_SECOND);
    }
  return s * TIME_UNITS_PER_SECOND + uns / (UL1000000000 / TIME_UNITS_PER_SECOND);
}

struct scm *
get_internal_run_time ()
{
  struct timespec *ts = __get_internal_run_time_ts;
  clock_gettime (CLOCK_PROCESS_CPUTIME_ID, ts);
  long time = seconds_and_nanoseconds_to_long (ts->tv_sec - g_start_time->tv_sec,
                                               ts->tv_nsec - g_start_time->tv_nsec);
  return make_number (time);
}

struct scm *
getcwd_ ()                      /*:((name . "getcwd")) */
{
  return make_string0 (getcwd (0, PATH_MAX));
}

struct scm *
dup_ (struct scm *port)                 /*:((name . "dup")) */
{
  return make_number (dup (port->value));
}

struct scm *
dup2_ (struct scm *old, struct scm *new)        /*:((name . "dup2")) */
{
  dup2 (old->value, new->value);
  return cell_unspecified;
}

struct scm *
delete_file (struct scm *file_name)
{
  unlink (cell_bytes (file_name->string));
  return cell_unspecified;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/reader.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/reader.c used in: Step 106, Step 225
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2018 Jeremiah Orians <jeremiah@pdp10.guru>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

#include <ctype.h>
#include <stdio.h>
#include <string.h>

struct scm *
read_input_file_env_ (struct scm *e, struct scm *a)
{
  if (e == cell_nil)
    return e;
  return cons (e, read_input_file_env_ (read_env (a), a));
}

struct scm *
read_input_file_env (struct scm *a)
{
  return read_input_file_env_ (read_env (cell_nil), cell_nil);
}

int
reader_read_line_comment (int c)
{
  while (c != EOF)
    {
      if (c == '\n')
        return c;
      c = readchar ();
    }
  error (cell_symbol_system_error, make_string0 ("reader_read_line_comment"));
}

struct scm *reader_read_block_comment (int s, int c);
struct scm *reader_read_hash (int c, struct scm *a);
struct scm *reader_read_list (int c, struct scm *a);

int
reader_identifier_p (int c)
{
  return (c > ' ' && c <= '~' && c != '"' && c != ';' && c != '(' && c != ')' && c != EOF);
}

int
reader_end_of_word_p (int c)
{
  return (c == '"' || c == ';' || c == '(' || c == ')' || isspace (c) || c == EOF);
}

struct scm *
reader_read_identifier_or_number (int c)
{
  int i = 0;
  long n = 0;
  int negative_p = 0;
  if (c == '+')
    if (isdigit (peekchar ()) != 0)
      c = readchar ();
  if (c == '-')
    if (isdigit (peekchar ()) != 0)
      {
        negative_p = 1;
        c = readchar ();
      }
  while (isdigit (c) != 0)
    {
      g_buf[i] = c;
      i = i + 1;
      n = n * 10;
      n = n + c - '0';
      c = readchar ();
    }
  if (reader_end_of_word_p (c) != 0)
    {
      unreadchar (c);
      if (negative_p != 0)
        n = 0 - n;
      return make_number (n);
    }
  /* Fallthrough: Note that `4a', `+1b' are identifiers */
  while (reader_end_of_word_p (c) == 0)
    {
      g_buf[i] = c;
      i = i + 1;
      c = readchar ();
    }
  unreadchar (c);
  g_buf[i] = 0;
  return cstring_to_symbol (g_buf);
}

struct scm *
reader_read_sexp_ (int c, struct scm *a)
{
reset_reader:
  if (c == EOF)
    return cell_nil;
  if (c == ';')
    {
      c = reader_read_line_comment (c);
      goto reset_reader;
    }
  if ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\f'))
    {
      c = readchar ();
      goto reset_reader;
    }
  if (c == '(')
    return reader_read_list (readchar (), a);
  if (c == ')')
    return cell_nil;
  if (c == '#')
    return reader_read_hash (readchar (), a);
  if (c == '`')
    return cons (cell_symbol_quasiquote, cons (reader_read_sexp_ (readchar (), a), cell_nil));
  if (c == ',')
    {
      if (peekchar () == '@')
        {
          readchar ();
          return cons (cell_symbol_unquote_splicing, cons (reader_read_sexp_ (readchar (), a), cell_nil));
        }
      return cons (cell_symbol_unquote, cons (reader_read_sexp_ (readchar (), a), cell_nil));
    }
  if (c == '\'')
    return cons (cell_symbol_quote, cons (reader_read_sexp_ (readchar (), a), cell_nil));
  if (c == '"')
    return reader_read_string ();
  if (c == '.')
    if (reader_identifier_p (peekchar ()) == 0)
      return cell_dot;
  return reader_read_identifier_or_number (c);
}

int
reader_eat_whitespace (int c)
{
  while (isspace (c) != 0)
    c = readchar ();
  if (c == ';')
    return reader_eat_whitespace (reader_read_line_comment (c));
  if (c == '#')
    {
      int p = peekchar ();
      if (p == '!' || p == '|')
        {
          c = readchar ();
          reader_read_block_comment (c, readchar ());
          return reader_eat_whitespace (readchar ());
        }
    }
  return c;
}

struct scm *
reader_read_list (int c, struct scm *a)
{
  c = reader_eat_whitespace (c);
  if (c == ')')
    return cell_nil;
  if (c == EOF)
    error (cell_symbol_not_a_pair, make_string0 ("EOF in list"));
  struct scm *s = reader_read_sexp_ (c, a);
  if (s == cell_dot)
    {
      s = reader_read_list (readchar (), a);
      return s->car;
    }
  return cons (s, reader_read_list (readchar (), a));
}

struct scm *
read_env (struct scm *a)
{
  return reader_read_sexp_ (readchar (), a);
}

struct scm *
reader_read_block_comment (int s, int c)
{
  if (c == s)
    if (peekchar () == '#')
      {
        readchar ();
        return cell_unspecified;
      }
  return reader_read_block_comment (s, readchar ());
}

struct scm *
reader_read_hash (int c, struct scm *a)
{
  if (c == '!')
    {
      reader_read_block_comment (c, readchar ());
      return reader_read_sexp_ (readchar (), a);
    }
  if (c == '|')
    {
      reader_read_block_comment (c, readchar ());
      return reader_read_sexp_ (readchar (), a);
    }
  if (c == 'f')
    return cell_f;
  if (c == 't')
    return cell_t;
  if (c == ',')
    {
      if (peekchar () == '@')
        {
          readchar ();
          return cons (cell_symbol_unsyntax_splicing, cons (reader_read_sexp_ (readchar (), a), cell_nil));
        }

      return cons (cell_symbol_unsyntax, cons (reader_read_sexp_ (readchar (), a), cell_nil));
    }
  if (c == '\'')
    return cons (cell_symbol_syntax, cons (reader_read_sexp_ (readchar (), a), cell_nil));
  if (c == '`')
    return cons (cell_symbol_quasisyntax, cons (reader_read_sexp_ (readchar (), a), cell_nil));
  if (c == ':')
    {
      struct scm *x = reader_read_identifier_or_number (readchar ());
      struct scm *msg = make_string0 ("keyword perifx ':' not followed by a symbol: ");
      if (x->type == TNUMBER)
        error (cell_symbol_system_error, cons (msg, x));
      return symbol_to_keyword (x);
    }
  if (c == 'b')
    return reader_read_binary ();
  if (c == 'o')
    return reader_read_octal ();
  if (c == 'x')
    return reader_read_hex ();
  if (c == '\\')
    return reader_read_character ();
  if (c == '(')
    return list_to_vector (reader_read_list (readchar (), a));
  if (c == ';')
    {
      reader_read_sexp_ (readchar (), a);
      return reader_read_sexp_ (readchar (), a);
    }
  return reader_read_sexp_ (readchar (), a);
}

struct scm *
reader_read_sexp (struct scm *c, struct scm *s, struct scm *a)
{
  return reader_read_sexp_ (c->value, a);
}

struct scm *
reader_read_character ()
{
  int c = readchar ();
  int p = peekchar ();
  int i = 0;
  if (c >= '0' && c <= '7' && p >= '0' && p <= '7')
    {
      c = c - '0';
      while (p >= '0' && p <= '7')
        {
          c = c << 3;
          c = c + readchar () - '0';
          p = peekchar ();
        }
    }
  else if (c == 'x' && ((p >= '0' && p <= '9') || (p >= 'a' && p <= 'f') || (p >= 'F' && p <= 'F')))
    {
      struct scm *n = reader_read_hex ();
      c = n->value;
      eputs ("reading hex c=");
      eputs (itoa (c));
      eputs ("\n");
    }
  else if (((c >= 'a' && c <= 'z') || c == '*') && ((p >= 'a' && p <= 'z') || p == '*'))
    {
      char *buf = __reader_read_char_buf;
      buf[i] = c;
      i = i + 1;
      while ((p >= 'a' && p <= 'z') || p == '*')
        {
          buf[i] = readchar ();
          i = i + 1;
          p = peekchar ();
        }
      buf[i] = 0;
      if (strcmp (buf, "*eof*") == 0)
        c = EOF;
      else if (strcmp (buf, "nul") == 0)
        c = '\0';
      else if (strcmp (buf, "alarm") == 0)
        c = '\a';
      else if (strcmp (buf, "backspace") == 0)
        c = '\b';
      else if (strcmp (buf, "tab") == 0)
        c = '\t';
      else if (strcmp (buf, "linefeed") == 0)
        c = '\n';
      else if (strcmp (buf, "newline") == 0)
        c = '\n';
      else if (strcmp (buf, "vtab") == 0)
        c = '\v';
      else if (strcmp (buf, "page") == 0)
        c = '\f';
      else if (strcmp (buf, "return") == 0)
        /* Nyacc bug
           c = '\r'; */
        c = 13;
      else if (strcmp (buf, "esc") == 0)
        c = 27;
      else if (strcmp (buf, "space") == 0)
        c = ' ';
      /* Nyacc uses old abbrevs */
      else if (strcmp (buf, "bel") == 0)
        c = '\a';
      else if (strcmp (buf, "bs") == 0)
        c = '\b';
      else if (strcmp (buf, "ht") == 0)
        c = '\t';
      else if (strcmp (buf, "vt") == 0)
        c = '\v';
      else if (strcmp (buf, "cr") == 0)
        /* Nyacc bug
           c = '\r'; */
        c = 13;
      else
        {
          eputs ("char not supported: ");
          eputs (buf);
          eputs ("\n");
          error (cell_symbol_system_error, make_string0 ("char not supported"));
        }
    }
  return make_char (c);
}

struct scm *
reader_read_binary ()
{
  long n = 0;
  int c = peekchar ();
  int negative_p = 0;
  if (c == '-')
    {
      negative_p = 1;
      readchar ();
      c = peekchar ();
    }
  while (c == '0' || c == '1')
    {
      n = n << 1;
      n = n + c - '0';
      readchar ();
      c = peekchar ();
    }
  if (negative_p != 0)
    n = 0 - n;
  return make_number (n);
}

struct scm *
reader_read_octal ()
{
  long n = 0;
  int c = peekchar ();
  int negative_p = 0;
  if (c == '-')
    {
      negative_p = 1;
      readchar ();
      c = peekchar ();
    }
  while (c >= '0' && c <= '7')
    {
      n = n << 3;
      n = n + c - '0';
      readchar ();
      c = peekchar ();
    }
  if (negative_p != 0)
    n = 0 - n;
  return make_number (n);
}

struct scm *
reader_read_hex ()
{
  long n = 0;
  int c = peekchar ();
  int negative_p = 0;
  if (c == '-')
    {
      negative_p = 1;
      readchar ();
      c = peekchar ();
    }
  while ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))
    {
      n = n << 4;
      if (c >= 'a')
        n = n + c - 'a' + 10;
      else if (c >= 'A')
        n = n + c - 'A' + 10;
      else
        n = n + c - '0';
      readchar ();
      c = peekchar ();
    }
  if (negative_p != 0)
    n = 0 - n;
  return make_number (n);
}

struct scm *
reader_read_string ()
{
  size_t i = 0;
  int c;
  struct scm *n;
  do
    {
      if (i > MAX_STRING)
        assert_max_string (i, "reader_read_string", g_buf);
      c = readchar ();
      if (c == '"')
        break;
      if (c == '\\')
        {
          c = readchar ();
          if (c == '\\' || c == '"')
            0;
          else if (c == '0')
            c = '\0';
          else if (c == 'a')
            c = '\a';
          else if (c == 'b')
            c = '\b';
          else if (c == 't')
            c = '\t';
          else if (c == 'n')
            c = '\n';
          else if (c == 'v')
            c = '\v';
          else if (c == 'f')
            c = '\f';
          else if (c == 'r')
            /* Nyacc bug
               c = '\r'; */
            c = 13;
          else if (c == 'e')
            /* Nyacc bug
               c = '\e'; */
            c = 27;
          else if (c == 'x')
            {
              n = reader_read_hex ();
              c = n->value;
            }
        }
      g_buf[i] = c;
      i = i + 1;
    }
  while (1);
  g_buf[i] = 0;
  return make_string (g_buf, i);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/stack.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/stack.c used in: Step 106, Step 226
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2021 W. J. van der Laan <laanwj@protonmail.com>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

#include <stdlib.h>

struct scm *
frame_printer (struct scm *frame)
{
  fdputs ("#<", __stdout);
  display_ (struct_ref_ (frame, 2));
  fdputc (' ', __stdout);
  fdputs ("procedure: ", __stdout);
  display_ (struct_ref_ (frame, 3));
  fdputc ('>', __stdout);
}

struct scm *
make_frame_type ()              /*:((internal)) */
{
  struct scm *fields = cell_nil;
  fields = cons (cell_symbol_procedure, fields);
  fields = cons (fields, cell_nil);
  fields = cons (cell_symbol_frame, fields);
  return make_struct (cell_symbol_record_type, fields, cell_unspecified);
}

struct scm *
make_frame (struct scm *stack, long index)
{
  struct scm *frame_type = make_frame_type ();
  long array_index = 0;
  struct scm *procedure = 0;
  if (index != 0)
    {
      array_index = (STACK_SIZE - (index * GC_FRAME_SIZE));
      procedure = g_stack_array[array_index + GC_FRAME_PROCEDURE];
    }
  if (procedure == 0)
    procedure = cell_f;
  struct scm *values = cell_nil;
  values = cons (procedure, values);
  values = cons (cell_symbol_frame, values);
  return make_struct (frame_type, values, cstring_to_symbol ("frame-printer"));
}

struct scm *
make_stack_type ()              /*:((internal)) */
{
  struct scm *fields = cell_nil;
  fields = cons (cstring_to_symbol ("frames"), fields);
  fields = cons (fields, cell_nil);
  fields = cons (cell_symbol_stack, fields);
  return make_struct (cell_symbol_record_type, fields, cell_unspecified);
}

struct scm *
make_stack (struct scm *stack)          /*:((arity . n)) */
{
  struct scm *stack_type = make_stack_type ();
  long size = (STACK_SIZE - g_stack) / GC_FRAME_SIZE;
  struct scm *frames = make_vector_ (size, cell_unspecified);
  long i;
  struct scm* frame;
  for (i = 0; i < size; i = i + 1)
    {
      frame = make_frame (stack, i);
      vector_set_x_ (frames, i, frame);
    }
  struct scm *values = cell_nil;
  values = cons (frames, values);
  values = cons (cell_symbol_stack, values);
  return make_struct (stack_type, values, cell_unspecified);
}

struct scm *
stack_length (struct scm *stack)
{
  struct scm *frames = struct_ref_ (stack, 3);
  return vector_length (frames);
}

struct scm *
stack_ref (struct scm *stack, struct scm *index)
{
  struct scm *frames = struct_ref_ (stack, 3);
  return vector_ref (frames, index);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/string.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/string.c used in: Step 106, Step 227
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

#include <limits.h>
#include <string.h>

void
assert_max_string (size_t i, char const *msg, char const *string)
{
  if (i > MAX_STRING)
    {
      eputs (msg);
      eputs (":string too long[");
      eputs (itoa (i));
      eputs ("]:");
      char *p = cast_voidp_to_charp (string);
      p[MAX_STRING - 1] = 0;
      eputs (p);
      error (cell_symbol_system_error, cell_f);
    }
}

char const *
list_to_cstring (struct scm *list, size_t *size)
{
  size_t i = 0;
  char *p = g_buf;
  struct scm *x;
  while (list != cell_nil)
    {
      if (i > MAX_STRING)
        assert_max_string (i, "list_to_string", g_buf);
      x = car (list);
      g_buf[i] = x->value;
      i = i + 1;
      list = cdr (list);
    }
  g_buf[i] = 0;
  size[0] = i;

  return g_buf;
}

struct scm *
string_equal_p (struct scm *a, struct scm *b)   /*:((name . "string=?")) */
{
  if (!((a->type == TSTRING && b->type == TSTRING) || (a->type == TKEYWORD || b->type == TKEYWORD)))
    {
      eputs ("type a: ");
      eputs (itoa (a->type));
      eputs ("\n");
      eputs ("type b: ");
      eputs (itoa (b->type));
      eputs ("\n");
      eputs ("a= ");
      write_error_ (a);
      eputs ("\n");
      eputs ("b= ");
      write_error_ (b);
      eputs ("\n");
      assert_msg ((a->type == TSTRING && b->type == TSTRING) || (a->type == TKEYWORD || b->type == TKEYWORD), "(a->type == TSTRING && b->type == TSTRING) || (a->type == TKEYWORD || b->type == TKEYWORD)");
    }
  if (a == b)
    return cell_t;
  if (a->string == b->string)
    return cell_t;
  if (a->length == 0 && b->length == 0)
    return cell_t;
  if (a->length == b->length)
    if (memcmp (cell_bytes (a->string), cell_bytes (b->string), a->length) == 0)
      return cell_t;

  return cell_f;
}

struct scm *
symbol_to_string (struct scm *symbol)
{
  return make_cell (TSTRING, symbol->car, symbol->cdr);
}

struct scm *
symbol_to_keyword (struct scm *symbol)
{
  return make_cell (TKEYWORD, symbol->car, symbol->cdr);
}

struct scm *
keyword_to_string (struct scm *keyword)
{
  return make_cell (TSTRING, keyword->car, keyword->cdr);
}

struct scm *
string_to_symbol (struct scm *string)
{
  struct scm *x = hash_ref (g_symbols, string, cell_f);
  if (x == cell_f)
    x = make_symbol (string);
  return x;
}

struct scm *
make_symbol (struct scm *string)
{
  struct scm *x = make_pointer_cell (TSYMBOL, string->length, string->string);
  hash_set_x (g_symbols, string, x);
  return x;
}

struct scm *
bytes_to_list (char const *s, size_t i)
{
  struct scm *p = cell_nil;
  int c;
  while (i != 0)
    {
      i = i - 1;
      c = (0x100 + s[i]) % 0x100;
      p = cons (make_char (c), p);
    }
  return p;
}

struct scm *
cstring_to_list (char const *s)
{
  return bytes_to_list (s, strlen (s));
}

struct scm *
cstring_to_symbol (char const *s)
{
  struct scm *string = make_string0 (s);
  return string_to_symbol (string);
}

struct scm *
string_to_list (struct scm *string)
{
  return bytes_to_list (cell_bytes (string->string), string->length);
}

struct scm *
list_to_string (struct scm *list)
{
  size_t size;
  char const *s = list_to_cstring (list, &size);
  return make_string (s, size);
}

struct scm *
read_string (struct scm *port)          /*:((arity . n)) */
{
  int fd = __stdin;
  if (port->type == TPAIR)
    {
      struct scm *p = car (port);
      if (p->type == TNUMBER)
        __stdin = p->value;
    }
  int c = readchar ();
  size_t i = 0;
  while (c != -1)
    {
      if (i > MAX_STRING)
        assert_max_string (i, "read_string", g_buf);
      g_buf[i] = c;
      i = i + 1;
      c = readchar ();
    }
  g_buf[i] = 0;
  __stdin = fd;
  return make_string (g_buf, i);
}

struct scm *
string_append (struct scm *x)           /*:((arity . n)) */
{
  char *p = g_buf;
  g_buf[0] = 0;
  size_t size = 0;
  struct scm *string;
  while (x != cell_nil)
    {
      string = x->car;
      assert_msg (string->type == TSTRING, "string->type == TSTRING");
      memcpy (p, cell_bytes (string->string), string->length + 1);
      p = p + string->length;
      size = size + string->length;
      if (size > MAX_STRING)
        assert_max_string (size, "string_append", g_buf);
      x = x->cdr;
    }
  return make_string (g_buf, size);
}

struct scm *
string_length (struct scm *string)
{
  assert_msg (string->type == TSTRING, "string->type == TSTRING");
  return make_number (string->length);
}

struct scm *
string_ref (struct scm *str, struct scm *k)
{
  assert_msg (str->type == TSTRING, "str->type == TSTRING");
  assert_msg (k->type == TNUMBER, "k->type == TNUMBER");
  size_t size = str->length;
  size_t i = k->value;
  if (i > size)
    error (cell_symbol_system_error, cons (make_string0 ("value out of range"), k));
  char const *p = cell_bytes (str->string);
  return make_char (p[i]);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/struct.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/struct.c used in: Step 106, Step 228
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

struct scm *
make_struct (struct scm *type, struct scm *fields, struct scm *printer)
{
  long size = 2 + length__ (fields);
  struct scm *x = alloc (1);
  struct scm *v = alloc (size);
  x->type = TSTRUCT;
  x->length = size;
  x->structure = v;
  copy_cell (v, vector_entry (type));
  copy_cell (cell_ref (v, 1), vector_entry (printer));
  long i;
  struct scm *e;
  for (i = 2; i < size; i = i + 1)
    {
      e = cell_unspecified;
      if (fields != cell_nil)
        {
          e = fields->car;
          fields = fields->cdr;
        }
      copy_cell (cell_ref (v, i), vector_entry (e));
    }
  return x;
}

struct scm *
struct_length (struct scm *x)
{
  assert_msg (x->type == TSTRUCT, "x->type == TSTRUCT");
  return make_number (x->length);
}

struct scm *
struct_ref_ (struct scm *x, long i)
{
  assert_msg (x->type == TSTRUCT, "x->type == TSTRUCT");
  assert_msg (i < x->length, "i < x->length");
  struct scm *e = cell_ref (x->structure, i);
  if (e->type == TREF)
    e = e->ref;
  if (e->type == TCHAR)
    e = make_char (e->value);
  if (e->type == TNUMBER)
    e = make_number (e->value);
  return e;
}

struct scm *
struct_set_x_ (struct scm *x, long i, struct scm *e)
{
  assert_msg (x->type == TSTRUCT, "x->type == TSTRUCT");
  assert_msg (i < x->length, "i < x->length");
  copy_cell (cell_ref (x->structure, i), vector_entry (e));
  return cell_unspecified;
}

struct scm *
struct_ref (struct scm *x, struct scm *i)
{
  return struct_ref_ (x, i->value);
}

struct scm *
struct_set_x (struct scm *x, struct scm *i, struct scm *e)
{
  return struct_set_x_ (x, i->value, e);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/symbol.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/symbol.c used in: Step 106, Step 229
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

#include <string.h>

// char const *MES_VERSION = "0.24.2";

#if __M2_PLANET__
#define M2_CELL_SIZE 12
// CONSTANT M2_CELL_SIZE 12
#else
#define M2_CELL_SIZE 1
// CONSTANT M2_CELL_SIZE 12
#endif

struct scm *
init_symbol (struct scm *x, long type, char const *name)
{
  x->type = type;
  if (g_symbols == 0)
    g_free = g_free + M2_CELL_SIZE;
  else
    {
      int length = strlen (name);
      struct scm *string = make_string (name, length);
      x->car_value = length;
      x->cdr = string->string;
      hash_set_x (g_symbols, string, x);
    }
  g_symbol = g_symbol + M2_CELL_SIZE;
  return x;
}

void
init_symbols_ ()                  /*:((internal)) */
{
  g_symbol = cell_nil;
  cell_nil = init_symbol (g_symbol, TSPECIAL, "()");
  cell_f = init_symbol (g_symbol, TSPECIAL, "#f");
  cell_t = init_symbol (g_symbol, TSPECIAL, "#t");
  cell_dot = init_symbol (g_symbol, TSPECIAL, ".");
  cell_arrow = init_symbol (g_symbol, TSPECIAL, "=>");
  cell_undefined = init_symbol (g_symbol, TSPECIAL, "*undefined*");
  cell_unspecified = init_symbol (g_symbol, TSPECIAL, "*unspecified*");
  cell_closure = init_symbol (g_symbol, TSPECIAL, "*closure*");
  cell_circular = init_symbol (g_symbol, TSPECIAL, "*circular*");

  cell_vm_apply = init_symbol (g_symbol, TSPECIAL, "core:apply");
  cell_vm_apply2 = init_symbol (g_symbol, TSPECIAL, "*vm-apply2*");
  cell_vm_begin = init_symbol (g_symbol, TSPECIAL, "*vm-begin*");
  cell_vm_begin_eval = init_symbol (g_symbol, TSPECIAL, "*vm:begin-eval*");
  cell_vm_begin_expand = init_symbol (g_symbol, TSPECIAL, "core:eval");
  cell_vm_begin_expand_eval = init_symbol (g_symbol, TSPECIAL, "*vm:begin-expand-eval*");
  cell_vm_begin_expand_macro = init_symbol (g_symbol, TSPECIAL, "*vm:begin-expand-macro*");
  cell_vm_begin_expand_primitive_load = init_symbol (g_symbol, TSPECIAL, "*vm:core:begin-expand-primitive-load*");
  cell_vm_begin_primitive_load = init_symbol (g_symbol, TSPECIAL, "*vm:core:begin-primitive-load*");
  cell_vm_begin_read_input_file = init_symbol (g_symbol, TSPECIAL, "*vm-begin-read-input-file*");
  cell_vm_call_with_current_continuation2 = init_symbol (g_symbol, TSPECIAL, "*vm-call-with-current-continuation2*");
  cell_vm_call_with_values2 = init_symbol (g_symbol, TSPECIAL, "*vm-call-with-values2*");
  cell_vm_eval = init_symbol (g_symbol, TSPECIAL, "core:eval-expanded");
  cell_vm_eval2 = init_symbol (g_symbol, TSPECIAL, "*vm-eval2*");
  cell_vm_eval_check_func = init_symbol (g_symbol, TSPECIAL, "*vm-eval-check-func*");
  cell_vm_eval_define = init_symbol (g_symbol, TSPECIAL, "*vm-eval-define*");
  cell_vm_eval_macro_expand_eval = init_symbol (g_symbol, TSPECIAL, "*vm:eval-macro-expand-eval*");
  cell_vm_eval_macro_expand_expand = init_symbol (g_symbol, TSPECIAL, "*vm:eval-macro-expand-expand*");
  cell_vm_eval_pmatch_car = init_symbol (g_symbol, TSPECIAL, "*vm-eval-pmatch-car*");
  cell_vm_eval_pmatch_cdr = init_symbol (g_symbol, TSPECIAL, "*vm-eval-pmatch-cdr*");
  cell_vm_eval_set_x = init_symbol (g_symbol, TSPECIAL, "*vm-eval-set!*");
  cell_vm_evlis = init_symbol (g_symbol, TSPECIAL, "*vm-evlis*");
  cell_vm_evlis2 = init_symbol (g_symbol, TSPECIAL, "*vm-evlis2*");
  cell_vm_evlis3 = init_symbol (g_symbol, TSPECIAL, "*vm-evlis3*");
  cell_vm_if = init_symbol (g_symbol, TSPECIAL, "*vm-if*");
  cell_vm_if_expr = init_symbol (g_symbol, TSPECIAL, "*vm-if-expr*");
  cell_vm_macro_expand = init_symbol (g_symbol, TSPECIAL, "core:macro-expand");
  cell_vm_macro_expand_car = init_symbol (g_symbol, TSPECIAL, "*vm:core:macro-expand-car*");
  cell_vm_macro_expand_cdr = init_symbol (g_symbol, TSPECIAL, "*vm:macro-expand-cdr*");
  cell_vm_macro_expand_define = init_symbol (g_symbol, TSPECIAL, "*vm:core:macro-expand-define*");
  cell_vm_macro_expand_define_macro = init_symbol (g_symbol, TSPECIAL, "*vm:core:macro-expand-define-macro*");
  cell_vm_macro_expand_lambda = init_symbol (g_symbol, TSPECIAL, "*vm:core:macro-expand-lambda*");
  cell_vm_macro_expand_set_x = init_symbol (g_symbol, TSPECIAL, "*vm:core:macro-expand-set!*");
  cell_vm_return = init_symbol (g_symbol, TSPECIAL, "*vm-return*");

  cell_symbol_lambda = init_symbol (g_symbol, TSYMBOL, "lambda");
  cell_symbol_begin = init_symbol (g_symbol, TSYMBOL, "begin");
  cell_symbol_if = init_symbol (g_symbol, TSYMBOL, "if");
  cell_symbol_quote = init_symbol (g_symbol, TSYMBOL, "quote");
  cell_symbol_define = init_symbol (g_symbol, TSYMBOL, "define");
  cell_symbol_define_macro = init_symbol (g_symbol, TSYMBOL, "define-macro");
  cell_symbol_quasiquote = init_symbol (g_symbol, TSYMBOL, "quasiquote");
  cell_symbol_unquote = init_symbol (g_symbol, TSYMBOL, "unquote");
  cell_symbol_unquote_splicing = init_symbol (g_symbol, TSYMBOL, "unquote-splicing");
  cell_symbol_syntax = init_symbol (g_symbol, TSYMBOL, "syntax");
  cell_symbol_quasisyntax = init_symbol (g_symbol, TSYMBOL, "quasisyntax");
  cell_symbol_unsyntax = init_symbol (g_symbol, TSYMBOL, "unsyntax");
  cell_symbol_unsyntax_splicing = init_symbol (g_symbol, TSYMBOL, "unsyntax-splicing");
  cell_symbol_set_x = init_symbol (g_symbol, TSYMBOL, "set!");
  cell_symbol_sc_expand = init_symbol (g_symbol, TSYMBOL, "sc-expand");
  cell_symbol_macro_expand = init_symbol (g_symbol, TSYMBOL, "macro-expand");
  cell_symbol_portable_macro_expand = init_symbol (g_symbol, TSYMBOL, "portable-macro-expand");
  cell_symbol_sc_expander_alist = init_symbol (g_symbol, TSYMBOL, "*sc-expander-alist*");
  cell_symbol_call_with_values = init_symbol (g_symbol, TSYMBOL, "call-with-values");
  cell_symbol_call_with_current_continuation = init_symbol (g_symbol, TSYMBOL, "call-with-current-continuation");
  cell_symbol_boot_module = init_symbol (g_symbol, TSYMBOL, "boot-module");
  cell_symbol_current_module = init_symbol (g_symbol, TSYMBOL, "current-module");
  cell_symbol_primitive_load = init_symbol (g_symbol, TSYMBOL, "primitive-load");
  cell_symbol_car = init_symbol (g_symbol, TSYMBOL, "car");
  cell_symbol_cdr = init_symbol (g_symbol, TSYMBOL, "cdr");
  cell_symbol_not_a_number = init_symbol (g_symbol, TSYMBOL, "not-a-number");
  cell_symbol_not_a_pair = init_symbol (g_symbol, TSYMBOL, "not-a-pair");
  cell_symbol_system_error = init_symbol (g_symbol, TSYMBOL, "system-error");
  cell_symbol_throw = init_symbol (g_symbol, TSYMBOL, "throw");
  cell_symbol_unbound_variable = init_symbol (g_symbol, TSYMBOL, "unbound-variable");
  cell_symbol_wrong_number_of_args = init_symbol (g_symbol, TSYMBOL, "wrong-number-of-args");
  cell_symbol_wrong_type_arg = init_symbol (g_symbol, TSYMBOL, "wrong-type-arg");

  cell_symbol_buckets = init_symbol (g_symbol, TSYMBOL, "buckets");
  cell_symbol_builtin = init_symbol (g_symbol, TSYMBOL, "<builtin>");
  cell_symbol_frame = init_symbol (g_symbol, TSYMBOL, "<frame>");
  cell_symbol_hashq_table = init_symbol (g_symbol, TSYMBOL, "<hashq-table>");
  cell_symbol_module = init_symbol (g_symbol, TSYMBOL, "<module>");
  cell_symbol_procedure = init_symbol (g_symbol, TSYMBOL, "procedure");
  cell_symbol_record_type = init_symbol (g_symbol, TSYMBOL, "<record-type>");
  cell_symbol_size = init_symbol (g_symbol, TSYMBOL, "size");
  cell_symbol_stack = init_symbol (g_symbol, TSYMBOL, "<stack>");
  cell_symbol_argv = init_symbol (g_symbol, TSYMBOL, "%argv");
  cell_symbol_mes_datadir = init_symbol (g_symbol, TSYMBOL, "%datadir");
  cell_symbol_mes_version = init_symbol (g_symbol, TSYMBOL, "%version");
  cell_symbol_internal_time_units_per_second = init_symbol (g_symbol, TSYMBOL, "internal-time-units-per-second");
  cell_symbol_compiler = init_symbol (g_symbol, TSYMBOL, "%compiler");
  cell_symbol_arch = init_symbol (g_symbol, TSYMBOL, "%arch");
  cell_symbol_pmatch_car = init_symbol (g_symbol, TSYMBOL, "pmatch-car");
  cell_symbol_pmatch_cdr = init_symbol (g_symbol, TSYMBOL, "pmatch-cdr");

  cell_type_bytes = init_symbol (g_symbol, TSYMBOL, "<cell:bytes>");
  cell_type_char = init_symbol (g_symbol, TSYMBOL, "<cell:char>");
  cell_type_closure = init_symbol (g_symbol, TSYMBOL, "<cell:closure>");
  cell_type_continuation = init_symbol (g_symbol, TSYMBOL, "<cell:continuation>");
  cell_type_function = init_symbol (g_symbol, TSYMBOL, "<cell:function>");
  cell_type_keyword = init_symbol (g_symbol, TSYMBOL, "<cell:keyword>");
  cell_type_macro = init_symbol (g_symbol, TSYMBOL, "<cell:macro>");
  cell_type_number = init_symbol (g_symbol, TSYMBOL, "<cell:number>");
  cell_type_pair = init_symbol (g_symbol, TSYMBOL, "<cell:pair>");
  cell_type_port = init_symbol (g_symbol, TSYMBOL, "<cell:port>");
  cell_type_ref = init_symbol (g_symbol, TSYMBOL, "<cell:ref>");
  cell_type_special = init_symbol (g_symbol, TSYMBOL, "<cell:special>");
  cell_type_string = init_symbol (g_symbol, TSYMBOL, "<cell:string>");
  cell_type_struct = init_symbol (g_symbol, TSYMBOL, "<cell:struct>");
  cell_type_symbol = init_symbol (g_symbol, TSYMBOL, "<cell:symbol>");
  cell_type_values = init_symbol (g_symbol, TSYMBOL, "<cell:values>");
  cell_type_variable = init_symbol (g_symbol, TSYMBOL, "<cell:variable>");
  cell_type_vector = init_symbol (g_symbol, TSYMBOL, "<cell:vector>");
  cell_type_broken_heart = init_symbol (g_symbol, TSYMBOL, "<cell:broken-heart>");

  cell_symbol_program = init_symbol (g_symbol, TSYMBOL, "%program");
  cell_symbol_test = init_symbol (g_symbol, TSYMBOL, "%%test");
}

struct scm *
init_symbols ()                  /*:((internal)) */
{
  g_free = g_cells + M2_CELL_SIZE;

  g_symbols = 0;
  cell_nil = g_free;
  init_symbols_ ();
  g_symbol_max = g_symbol;
  g_symbols = make_hash_table_ (500);
  init_symbols_ ();
  g_ports = cell_nil;

  struct scm *a = cell_nil;
  a = acons (cell_symbol_call_with_values, cell_symbol_call_with_values, a);
  a = acons (cell_symbol_boot_module, cell_symbol_boot_module, a);
  a = acons (cell_symbol_current_module, cell_symbol_current_module, a);

  a = acons (cell_symbol_mes_version, make_string0 (MES_VERSION), a);
  a = acons (cell_symbol_mes_datadir, make_string0 (g_datadir), a);

  a = acons (cell_type_bytes, make_number (TBYTES), a);
  a = acons (cell_type_char, make_number (TCHAR), a);
  a = acons (cell_type_closure, make_number (TCLOSURE), a);
  a = acons (cell_type_continuation, make_number (TCONTINUATION), a);
  a = acons (cell_type_keyword, make_number (TKEYWORD), a);
  a = acons (cell_type_macro, make_number (TMACRO), a);
  a = acons (cell_type_number, make_number (TNUMBER), a);
  a = acons (cell_type_pair, make_number (TPAIR), a);
  a = acons (cell_type_port, make_number (TPORT), a);
  a = acons (cell_type_ref, make_number (TREF), a);
  a = acons (cell_type_special, make_number (TSPECIAL), a);
  a = acons (cell_type_string, make_number (TSTRING), a);
  a = acons (cell_type_struct, make_number (TSTRUCT), a);
  a = acons (cell_type_symbol, make_number (TSYMBOL), a);
  a = acons (cell_type_values, make_number (TVALUES), a);
  a = acons (cell_type_variable, make_number (TVARIABLE), a);
  a = acons (cell_type_vector, make_number (TVECTOR), a);
  a = acons (cell_type_broken_heart, make_number (TBROKEN_HEART), a);

  a = acons (cell_closure, a, a);

  return a;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/vector.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/vector.c used in: Step 106, Step 230
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

#if __M2_PLANET__
#define M2_CELL_SIZE 12
// CONSTANT M2_CELL_SIZE 12
#else
#define M2_CELL_SIZE 1
// CONSTANT M2_CELL_SIZE 12
#endif

struct scm *
make_vector_ (long k, struct scm *e)
{
  struct scm *x = alloc (1);
  struct scm *v = alloc (k);
  x->type = TVECTOR;
  x->length = k;
  x->vector = v;
  long i;
  for (i = 0; i < k; i = i + 1)
    copy_cell (cell_ref (v, i), vector_entry (e));

  return x;
}

struct scm *
make_vector (struct scm *x)               /*:((arity . n)) */
{
  struct scm *k = x->car;
  assert_number ("make-vector", k);
  long n = k->value;
  struct scm *e = cell_unspecified;
  if (x->cdr != cell_nil)
    e = x->cdr->car;

  return make_vector_ (n, e);
}

struct scm *
vector_length (struct scm *x)
{
  assert_msg (x->type == TVECTOR, "x->type == TVECTOR");
  return make_number (x->length);
}

struct scm *
vector_ref_ (struct scm *x, long i)
{
  assert_msg (x->type == TVECTOR, "x->type == TVECTOR");
  assert_msg (i < x->length, "i < x->length");
  struct scm *e = cell_ref (x->vector, i);
  if (e->type == TREF)
    e = e->ref;
  if (e->type == TCHAR)
    e = make_char (e->value);
  if (e->type == TNUMBER)
    e = make_number (e->value);
  return e;
}

struct scm *
vector_ref (struct scm *x, struct scm *i)
{
  return vector_ref_ (x, i->value);
}

struct scm *
vector_entry (struct scm *x)
{
  if (x->type != TCHAR && x->type != TNUMBER)
    x = make_ref (x);
  return x;
}

struct scm *
vector_set_x_ (struct scm *x, long i, struct scm *e)
{
  assert_msg (x->type == TVECTOR, "x->type == TVECTOR");
  assert_msg (i < x->length, "i < x->length");
  copy_cell (cell_ref (x->vector, i), vector_entry (e));
  return cell_unspecified;
}

struct scm *
vector_set_x (struct scm *x, struct scm *i, struct scm *e)
{
  return vector_set_x_ (x, i->value, e);
}

struct scm *
list_to_vector (struct scm *x)
{
  struct scm *v = make_vector_ (length__ (x), cell_unspecified);
  struct scm *p = v->vector;
  while (x != cell_nil)
    {
      copy_cell (p, vector_entry (car (x)));
      p = p + M2_CELL_SIZE;
      x = cdr (x);
    }
  return v;
}

struct scm *
vector_to_list (struct scm *v)
{
  struct scm *x = cell_nil;
  long i;
  struct scm *e;
  for (i = v->length; i; i = i - 1)
    {
      e = cell_ref (v->vector, i - 1);
      if (e->type == TREF)
        e = e->ref;
      x = cons (e, x);
    }
  return x;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/x86-mes/x86.M1?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/x86-mes/x86.M1 used in: Step 108, Step 296
### GNU Mes --- Maxwell Equations of Software
### Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
###
### This file is part of GNU Mes.
###
### Mes is free software# you can redistribute it and/or modify it
### under the terms of the GNU General Public License as published by
### the Free Software Foundation# either version 3 of the License, or (at
### your option) any later version.
###
### GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.

# reduced instruction set: eax, ebx (some ecx for shift, edx for mul, div)
# 193 instructions

DEFINE add____$i32,%eax 05
DEFINE add____$i32,%ebx 81c3
DEFINE add____$i32,(%eax) 8100
DEFINE add____$i32,(%ebx) 8103
DEFINE add____$i32,0x32(%eax) 8180
DEFINE add____$i32,0x32(%ebp) 8185
DEFINE add____$i8,%eax 83c0
DEFINE add____$i8,%ebx 83c3
DEFINE add____$i8,%esp 83c4
DEFINE add____$i8,(%eax) 8300
DEFINE add____$i8,(%ebx) 8303
DEFINE add____$i8,0x32 8305
DEFINE add____$i8,0x32(%eax) 8380
DEFINE add____$i8,0x32(%ebp) 8385
DEFINE add____$i8,0x8(%eax) 8340
DEFINE add____$i8,0x8(%ebp) 8345
DEFINE add____%eax,%eax 01c0
DEFINE add____%ebp,%eax 01e8
DEFINE add____%ebx,%eax 01d8
DEFINE add____%ebx,%ebx 01db
DEFINE addb___$i8,(%eax) 8000
DEFINE addb___$i8,(%ebx) 8003
DEFINE addw___$i8,(%eax) 668100
DEFINE addw___$i8,(%ebx) 668103
DEFINE and____$i32,%eax 25
DEFINE and____$i32,%ebx 81e3
DEFINE and____%ebx,%eax 21d8
DEFINE call32 e8
DEFINE call___*%eax ffd0
DEFINE call___*%ebx ffd3
DEFINE cltd 99
DEFINE cmp____$0x32,%eax 3d
DEFINE cmp____$i32,%eax 3d
DEFINE cmp____$i8,%eax 83f8
DEFINE cmp____$i8,%ebx 81fb
DEFINE div___%ebx f7f3
DEFINE hlt f4
DEFINE idiv___%ebx f7fb
DEFINE int cd
DEFINE int____$0x80 cd80
DEFINE ja32  0f87
DEFINE jae32 0f83
DEFINE jb32  0f82
DEFINE jbe32 0f86
DEFINE je32 0f84
DEFINE je8 74
DEFINE jg32 0f8f
DEFINE jge32 0f8d
DEFINE jl32 0f8c
DEFINE jle32 0f8e
DEFINE jmp32 e9
DEFINE jmp____*%ebx ffe3
DEFINE jne32 0f85
DEFINE lahf 9f
DEFINE lea____0x32(%ebp),%eax 8d85
DEFINE lea____0x8(%ebp),%eax 8d45
DEFINE leave c9
DEFINE mov____$i32,%eax b8
DEFINE mov____$i32,%ebx bb
DEFINE mov____$i32,(%eax) c700
DEFINE mov____$i32,0x32 c705
DEFINE mov____$i32,0x8(%eax) c740
DEFINE mov____$i32,0x8(%ebp) c745
DEFINE mov____$i8,%al b0
DEFINE mov____$i8,%eax b0
DEFINE mov____%al,(%ebx) 8803
DEFINE mov____%al,0x8(%ebp) 8845
DEFINE mov____%ax,(%ebx) 668903
DEFINE mov____%ax,0x8(%ebp) 668945
DEFINE mov____%bx,%bx 6689db
DEFINE mov____%cl,(%ebx) 8a08
DEFINE mov____%cx,(%ebx) 668b08
DEFINE mov____%dl,(%eax) 8810
DEFINE mov____%dl,0x8(%eax) 8850
DEFINE mov____%eax,%ebx 89c3
DEFINE mov____%eax,%ecx 89c1
DEFINE mov____%eax,(%ebx) 8903
DEFINE mov____%eax,0x32 a3
DEFINE mov____%eax,0x32(%ebp) 8985
DEFINE mov____%eax,0x8(%ebp) 8945
DEFINE mov____%ebp,%eax 89e8
DEFINE mov____%ebp,%ebx 89eb
DEFINE mov____%ebx,%eax 89d8
DEFINE mov____%ebx,%ecx 89d9
DEFINE mov____%ebx,0x32 891d
DEFINE mov____%ebx,0x32(%ebp) 899d
DEFINE mov____%ebx,0x8(%ebp) 895d
DEFINE mov____%ecx,(%ebx) 890b
DEFINE mov____%edi,%ebp 89fd
DEFINE mov____%edx,%eax 89d0
DEFINE mov____%edx,%ebx 89d3
DEFINE mov____%esp,%ebp 89e5
DEFINE mov____%esp,%edi 89e7
DEFINE mov____(%eax),%cl 8a08
DEFINE mov____(%eax),%cx 668b08
DEFINE mov____(%eax),%eax 8b00
DEFINE mov____(%eax),%ecx 8b08
DEFINE mov____(%ebx),%ebx 8b1b
DEFINE mov____0x32(%eax),%eax 8b80
DEFINE mov____0x32(%eax),%ebx 8b98
DEFINE mov____0x32(%ebp),%eax 8b85
DEFINE mov____0x32(%ebp),%ebx 8b9d
DEFINE mov____0x32,%eax a1
DEFINE mov____0x32,%ebx 8b1d
DEFINE mov____0x8(%eax),%eax 8b40
DEFINE mov____0x8(%eax),%ebx 8b58
DEFINE mov____0x8(%ebp),%eax 8b45
DEFINE mov____0x8(%ebp),%ebp 8b6d
DEFINE mov____0x8(%ebp),%ebx 8b5d
DEFINE mov____0x8(%ebp),%ecx 8b4d
DEFINE mov____0x8(%ebp),%edi 8b7d
DEFINE mov____0x8(%ebp),%edx 8b55
DEFINE mov____0x8(%ebp),%esi 8b75
DEFINE mov____0x8(%ebp),%esp 8b65
DEFINE movb___%al,0x32 a2
DEFINE movb___%bl,0x32 881d
DEFINE movsbl_%al,%eax 0fbec0
DEFINE movsbl_%bl,%ebx 0fbedb
DEFINE movswl_%ax,%eax 0fbfc0
DEFINE movswl_%bx,%ebx 0fbfdb
DEFINE movw___%ax,0x32 66a3
DEFINE movw___%bx,0x32 66891d
DEFINE movzbl_%al,%eax 0fb6c0
DEFINE movzbl_%bl,%ebx 0fb6db
DEFINE movzbl_(%eax),%eax 0fb600
DEFINE movzbl_(%ebx),%ebx 0fb61b
DEFINE movzbl_0x32(%eax),%eax 0fb680
DEFINE movzbl_0x8(%eax),%eax 0fb640
DEFINE movzbl_0x8(%ebp),%eax 0fb645
DEFINE movzwl_%ax,%eax 0fb7c0
DEFINE movzwl_%bx,%ebx 0fb7db
DEFINE movzwl_(%eax),%eax 0fb700
DEFINE movzwl_(%ebx),%ebx 0fb71b
DEFINE movzwl_0x32(%eax),%eax 0fb780
DEFINE movzwl_0x32(%ebp),%eax 0fb785
DEFINE movzwl_0x8(%eax),%eax 0fb740
DEFINE mul____%ebx f7e3
DEFINE mul_____%ebx f7e3
DEFINE nop 90
DEFINE not____%eax f7d0
DEFINE not____%ebx f7d3
DEFINE or_____%ebx,%eax 09d8
DEFINE pop____%eax 58
DEFINE pop____%ebx 5b
DEFINE pop____%edx 5a
DEFINE push___$i32 68
DEFINE push___%eax 50
DEFINE push___%ebp 55
DEFINE push___%ebx 53
DEFINE push___%edx 52
DEFINE push___(%eax) ff30
DEFINE push___0x32(%ebp) ffb5
DEFINE push___0x8(%ebp) ff75
DEFINE ret c3
DEFINE sahf 9e
DEFINE seta___%al 0f97c0
DEFINE seta___%bl 0f97c3
DEFINE setae__%al 0f93c0
DEFINE setae__%bl 0f93c3
DEFINE setb___%al 0f92c0
DEFINE setb___%bl 0f92c3
DEFINE setbe__%al 0f96c0
DEFINE setbe__%bl 0f96c3
DEFINE sete___%al 0f94c0
DEFINE sete___%bl 0f94c3
DEFINE setg___%al 0f9fc0
DEFINE setg___%bl 0f9fc3
DEFINE setge__%al 0f9dc0
DEFINE setge__%bl 0f9dc3
DEFINE setl___%al 0f9cc0
DEFINE setl___%bl 0f9cc3
DEFINE setle__%al 0f9ec0
DEFINE setle__%bl 0f9ec3
DEFINE setne__%al 0f95c0
DEFINE setne__%bl 0f95c3
DEFINE shl____$i8,%eax c1e0
DEFINE shl____$i8,%ebx c1e3
DEFINE shl____%cl,%eax d3e0
DEFINE shl____%cl,%ebx d3e3
DEFINE shr____%cl,%eax d3e8
DEFINE sub____$8,%esp 83ec
DEFINE sub____$i32,%esp 81ec
DEFINE sub____%al,%dl 28d0
DEFINE sub____%dl,%al 28c2
DEFINE sub____%ebx,%eax 29d8
DEFINE test___%al,%al 84c0
DEFINE test___%eax,%eax 85c0
DEFINE test___%ebx,%ebx 85db
DEFINE xchg___%eax,%ebx 93
DEFINE xchg___%eax,(%esp) 870424
DEFINE xchg___%ebx,(%esp) 871c24
DEFINE xor____$i32,%eax 35
DEFINE xor____$i8,%ah 80f4
DEFINE xor____%eax,%eax 31c0
DEFINE xor____%ebx,%eax 31d8
DEFINE xor____%ebx,%ebx 31db
DEFINE xor____%edx,%edx 31d2


# Enough for all of Mes + Mes C Libray when using all registers, i.e.,
# non-reduced instruction set
#DEFINE add____$i32,%ecx 81c1
#DEFINE add____$i32,%edx 81c2
#DEFINE add____$i32,%esi 81c6
#DEFINE add____$i8,%ecx 83c1
#DEFINE add____$i8,%edx 83c2
#DEFINE add____$i8,%esi 83c6
#DEFINE add____$i8,(%ecx) 8301
#DEFINE add____$i8,(%edx) 8302
#DEFINE add____%ecx,%ebx 01cb
#DEFINE add____%ecx,%ecx 01c9
#DEFINE add____%edx,%eax 01d0
#DEFINE add____%edx,%ecx 01d1
#DEFINE add____%edx,%edx 01d2
#DEFINE add____%esi,%edx 01f2
#DEFINE and____$i32,%ecx 81e1
#DEFINE and____$i32,%edx 81e2
#DEFINE and____%edx,%eax 21d0
#DEFINE and____(%edx),%eax 2302
#DEFINE cmp____%edx,%eax 39d0
#DEFINE idiv___%ecx f7f9
#DEFINE lea____0x32(%ebp),%edx 8d95
#DEFINE lea____0x8(%ebp),%edx 8d55
#DEFINE mov____$i32,%ecx b9
#DEFINE mov____$i32,%edx ba
#DEFINE mov____$i32,%esi be
#DEFINE mov____%al,(%edx) 8802
#DEFINE mov____%al,0x8(%edx) 8842
#DEFINE mov____%ax,(%edx) 668902
#DEFINE mov____%ax,0x32(%edx) 668982
#DEFINE mov____%ax,0x8(%edx) 668942
#DEFINE mov____%bl,(%ecx) 8819
#DEFINE mov____%eax,%edx 89c2
#DEFINE mov____%eax,%esi 89c6
#DEFINE mov____%eax,(%ecx) 8901
#DEFINE mov____%eax,(%edx) 8902
#DEFINE mov____%eax,0x32(%edx) 8982
#DEFINE mov____%eax,0x8(%edx) 8942
#DEFINE mov____%ebp,%ecx 89e9
#DEFINE mov____%ebp,%edx 89ea
#DEFINE mov____%ebp,%esi 89ee
#DEFINE mov____%ebx,(%ecx) 8919
#DEFINE mov____%ebx,0x8(%edx) 895a
#DEFINE mov____%ecx,%eax 89c8
#DEFINE mov____%ecx,%ecx 89c9
#DEFINE mov____%ecx,%edx 89ca
#DEFINE mov____%ecx,(%eax) 8908
#DEFINE mov____%ecx,(%edx) 890a
#DEFINE mov____%ecx,0x32(%ebp) 898d
#DEFINE mov____%ecx,0x8(%ebp) 894d
#DEFINE mov____%edi,%ebx 89fb
#DEFINE mov____%edx,%ecx 89d1
#DEFINE mov____%edx,(%eax) 8910
#DEFINE mov____%edx,0x32(%ebp) 8995
#DEFINE mov____%edx,0x8(%ebp) 8955
#DEFINE mov____%esi,%eax 89f0
#DEFINE mov____%esi,%ebx 89f3
#DEFINE mov____(%ecx),%ecx 8b09
#DEFINE mov____(%edx),%eax 8b02
#DEFINE mov____(%edx),%ecx 8b0a
#DEFINE mov____(%edx),%edx 8b12
#DEFINE mov____0x32(%eax),%ecx 8b88
#DEFINE mov____0x32(%ebp),%ecx 8b8d
#DEFINE mov____0x32(%ebp),%edx 8b95
#DEFINE mov____0x32,%ecx 8b0d
#DEFINE mov____0x32,%edx 8b15
#DEFINE mov____0x8(%eax),%ecx 8b48
#DEFINE movsbl_%cl,%ecx 0fbec9
#DEFINE movsbl_%dl,%edx 0fbed2
#DEFINE movswl_%cx,%ecx 0fbfc9
#DEFINE movzbl_%cl,%ecx 0fb6c9
#DEFINE movzbl_%dl,%edx 0fb6d2
#DEFINE movzbl_(%eax),%edx 0fb610
#DEFINE movzbl_(%ecx),%ecx 0fb609
#DEFINE movzbl_(%edx),%edx 0fb612
#DEFINE movzwl_(%ecx),%ecx 0fb709
#DEFINE mul____%ecx f7e1
#DEFINE mul____%edi f7e7
#DEFINE mul____%edx f7e2
#DEFINE mul____%esi f7e6
#DEFINE or_____%ecx,%ebx 09cb
#DEFINE or_____%edx,%eax 09d0
#DEFINE or_____(%edx),%eax 0b02
#DEFINE pop____%ecx 59
#DEFINE pop____%edi 5f
#DEFINE push___%ecx 51
#DEFINE push___%edi 57
#DEFINE push___%esi 56
#DEFINE shl____$i8,%ecx c1e1
#DEFINE shl____$i8,%edx c1e2
#DEFINE shl____%cl,%ecx d3e1
#DEFINE sub____%eax,%edx 29c2
#DEFINE sub____%ecx,%ebx 29cb
#DEFINE sub____%edx,%eax 29d0
#DEFINE sub____%edx,%ecx 29d1
#DEFINE xchg___%ebx,%ecx 87d9
#DEFINE xchg___%ecx,%edx 87ca
#DEFINE xor____%ecx,%ecx 31c9
#DEFINE xor____%edx,%eax 31d0


# deprecated, remove after 0.18
DEFINE sub____%esp,$i32 81ec
DEFINE sub____%esp,$i8 83ec

DEFINE SYS_exit   01000000
DEFINE SYS_fork   02000000
DEFINE SYS_read   03000000
DEFINE SYS_rmdir  28000000
DEFINE SYS_write  04000000
DEFINE SYS_open   05000000
DEFINE SYS_close  06000000
DEFINE SYS_waitpid 07000000
DEFINE SYS_unlink 0a000000
DEFINE SYS_execve 0b000000
DEFINE SYS_chmod  0f000000
DEFINE SYS_lseek  13000000
DEFINE SYS_access 21000000
DEFINE SYS_brk    2d000000
DEFINE SYS_ioctl  36000000
DEFINE SYS_stat   6a000000
DEFINE SYS_fsync  76000000
DEFINE SYS_getcwd b7000000

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-m2/crt1.M1?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-m2/crt1.M1 used in: Step 108
### GNU Mes --- Maxwell Equations of Software
### Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
###
### This file is part of GNU Mes.
###
### GNU Mes is free software; you can redistribute it and/or modify it
### under the terms of the GNU General Public License as published by
### the Free Software Foundation; either version 3 of the License, or (at
### your option) any later version.
###
### GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.

:_start
    push___%ebp
    mov____%esp,%ebp
    sub____$i32,%esp %0x1054
    mov____$i8,%eax !0
    mov____%eax,0x32 &GLOBAL___stdin
    mov____$i8,%eax !1
    mov____%eax,0x32 &GLOBAL___stdout
    mov____$i8,%eax !2
    mov____%eax,0x32 &GLOBAL___stderr
    mov____%ebp,%eax
    add____$i8,%eax !4
    mov____(%eax),%eax
    add____$i8,%eax !3
    shl____$i8,%eax !0x02
    add____%ebp,%eax
    mov____%eax,0x32 &GLOBAL_environ

    mov____%esp,%edi    ; M2-Planet calling convention pushes forward
    mov____%ebp,%eax    ; argc
    add____$i8,%eax !4
    mov____(%eax),%eax
    push___%eax

    mov____%ebp,%eax    ; argv
    add____$i8,%eax !8
    push___%eax

    mov____0x32,%eax &GLOBAL_environ
    push___%eax

    mov____%edi,%ebp        ; M2-Planet calling convention
    call32 %FUNCTION_main
    add____$i8,%esp !0x0
    test___%eax,%eax
    mov____%eax,%ebx
    mov____$i32,%eax %1
    int____$0x80
    hlt
    leave
    ret

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/m2/x86/ELF-x86.hex2?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/m2/x86/ELF-x86.hex2 used in: Step 110
### Copyright (C) 2016 Jeremiah Orians
### Copyright (C) 2017 Jan Nieuwenhuizen <janneke@gnu.org>
### This file is part of M2-Planet.
###
### M2-Planet is free software: you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation, either version 3 of the License, or
### (at your option) any later version.
###
### M2-Planet 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 M2-Planet.  If not, see <http://www.gnu.org/licenses/>.

### stage0's hex2 format
###    !<label>          1 byte relative
###    $<label>          2 byte address
###    @<label>          2 byte relative
###    &<label>          4 byte address
###    %<label>          4 byte relative

### if you wish to use this header, you need to add :ELF_end to the end of your
### M1 or hex2 files.

## ELF Header

:ELF_base
7F 45 4C 46                    # e_ident[EI_MAG0-3] ELF's magic number

01                             # e_ident[EI_CLASS] Indicating 32 bit
01                             # e_ident[EI_DATA] Indicating little endianness
01                             # e_ident[EI_VERSION] Indicating original elf

03                             # e_ident[EI_OSABI] Set at 3 because FreeBSD is strict
00                             # e_ident[EI_ABIVERSION] See above

00 00 00 00 00 00 00           # e_ident[EI_PAD]

02 00                          # e_type Indicating Executable
03 00                          # e_machine Indicating 386
01 00 00 00                    # e_version Indicating original elf

&_start                        # e_entry Address of the entry point
%ELF_program_headers>ELF_base  # e_phoff Address of program header table
%ELF_section_headers>ELF_base  # e_shoff Address of section header table

00 00 00 00                    # e_flags

34 00                          # e_ehsize Indicating our 52 Byte header

20 00                          # e_phentsize size of a program header table
01 00                          # e_phnum number of entries in program table

28 00                          # e_shentsize size of a section header table
05 00                          # e_shnum number of entries in section table

02 00                          # e_shstrndx index of the section names


:ELF_program_headers
:ELF_program_header__text
01 00 00 00                    # ph_type: PT-LOAD = 1
00 00 00 00                    # ph_offset
&ELF_base                      # ph_vaddr
&ELF_base                      # ph_physaddr
%ELF_end>ELF_base              # ph_filesz
%ELF_end>ELF_base              # ph_memsz
07 00 00 00                    # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                    # ph_alignment

:ELF_text

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-mescc/crt1.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-mescc/crt1.c used in: Step 121
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib-mini.h"

int main (int argc, char *argv[], char *envp[]);

int
_start ()
{
  asm ("mov____$i8,%eax !0");
  asm ("mov____%eax,0x32 &__stdin");

  asm ("mov____$i8,%eax !1");
  asm ("mov____%eax,0x32 &__stdout");

  asm ("mov____$i8,%eax !2");
  asm ("mov____%eax,0x32 &__stderr");

  asm ("mov____%ebp,%eax");
  asm ("add____$i8,%eax !4");

  asm ("mov____(%eax),%eax");
  asm ("add____$i8,%eax !3");

  asm ("shl____$i8,%eax !0x02");
  asm ("add____%ebp,%eax");
  asm ("mov____%eax,0x32 &environ");
  asm ("push___%eax");

  asm ("mov____%ebp,%eax");
  asm ("add____$i8,%eax !8");
  asm ("push___%eax");

  asm ("mov____%ebp,%eax");
  asm ("add____$i8,%eax !4");
  asm ("mov____(%eax),%eax");
  asm ("push___%eax");

  main ();

  asm ("mov____%eax,%ebx");
  asm ("mov____$i32,%eax %1");
  asm ("int____$0x80");
  asm ("hlt");
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/lib-mini.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __MES_LIB_MINI_H
#define __MES_LIB_MINI_H

#if HAVE_CONFIG_H
#include <mes/config.h>
#endif

// CONSTANT STDIN 0
#ifndef STDIN
#define STDIN 0
#endif

// CONSTANT STDOUT 1
#ifndef STDOUT
#define STDOUT 1
#endif

// CONSTANT STDERR 2
#ifndef STDERR
#define STDERR 2
#endif

extern char **environ;
extern int __stdin;
extern int __stdout;
extern int __stderr;

int eputs (char const *s);
int puts (char const *s);
int oputs (char const *s);

#if SYSTEM_LIBC

#include <sys/types.h>
#include <unistd.h>

#else //!SYSTEM_LIBC

#ifndef _SIZE_T
#define _SIZE_T
#ifndef __SIZE_T
#define __SIZE_T
#ifndef __MES_SIZE_T
#define __MES_SIZE_T
#undef size_t
typedef unsigned long size_t;
#endif
#endif
#endif

#ifndef _SSIZE_T
#define _SSIZE_T
#ifndef __SSIZE_T
#define __SSIZE_T
#ifndef __MES_SSIZE_T
#define __MES_SSIZE_T
#undef ssize_t
#if __i386__
typedef int ssize_t;
#else
typedef long ssize_t;
#endif
#endif
#endif
#endif

#ifndef __MES_ERRNO_T
#define __MES_ERRNO_T 1
typedef int error_t;
extern int errno;
#endif // !__MES_ERRNO_T

size_t strlen (char const *s);
ssize_t _write ();
ssize_t write (int filedes, void const *buffer, size_t size);
#endif // !SYSTEM_LIBC

#endif //__MES_LIB_MINI_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/string.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_STRING_H
#define __MES_STRING_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_STRING_H
#include_next <string.h>

#else // ! SYSTEM_LIBC

#include <sys/types.h>

void *memchr (void const *block, int c, size_t size);
void *memcpy (void *dest, void const *src, size_t n);
void *memmove (void *dest, void const *src, size_t n);
void *memset (void *s, int c, size_t n);
void *memchr (void const *block, int c, size_t size);
int memcmp (void const *s1, void const *s2, size_t n);
void *memmem (void const *haystack, int haystack_len, void const *needle, int needle_len);
char *strcat (char *dest, char const *src);
char *strchr (char const *s, int c);
int strcasecmp (char const *s1, char const *s2);
int strcmp (char const *, char const *);
char *strcpy (char *dest, char const *src);
size_t strlen (char const *);
char *strncpy (char *to, char const *from, size_t size);
int strncmp (char const *, char const *, size_t);
char *strrchr (char const *s, int c);
char *strstr (char const *haystack, char const *needle);
char *strlwr (char *string);
char *strupr (char *string);


char *strerror (int errnum);
void perror (char const *message);

#endif // ! SYSTEM_LIBC

#endif // __MES_STRING_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/oputs.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib-mini.h>
#include <string.h>

int
oputs (char const *s)
{
  int i = strlen (s);
  write (__stdout, s, i);
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/exit.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

void (*__call_at_exit) (void);

void
exit (int code)
{
  if (__call_at_exit)
    (*__call_at_exit) ();
  _exit (code);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/lib.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __MES_LIB_H
#define __MES_LIB_H

#include <mes/lib-mini.h>

char *cast_intp_to_charp (int const *i);
char *cast_long_to_charp (long i);
long cast_charp_to_long (char const *);
long cast_int_to_long (int i);
long cast_voidp_to_long (void const *);

int __mes_debug ();
void __ungetc_init ();
void __ungetc_clear (int filedes);
void __ungetc_set (int filedes, int c);
int __ungetc_p (int filedes);
double abtod (char const **p, int base);
long abtol (char const **p, int base);
char *dtoab (double number, int base, int signed_p);
char *itoa (int number);
char *ltoa (long number);
char *ltoab (long x, int base);
char *ntoab (long number, unsigned base, int signed_p);
char *ultoa (unsigned long number);
char *utoa (unsigned number);
int eputc (int c);
int fdgetc (int fd);
char * fdgets (char *s, int count, int fd);
int fdputc (int c, int fd);
int fdputs (char const *s, int fd);
int fdungetc (int c, int fd);
char *_getcwd (char *buffer, size_t size);
int ioctl3 (int filedes, size_t command, long data);
int isnumber (int c, int base);
int mes_open (char const *file_name, int flags, int mask);
int _open2 (char const *file_name, int flags);
int _open3 (char const *file_name, int flags, int mask);
int oputc (int c);
int oputs (char const *s);
char *search_path (char const *file_name);
ssize_t _read (int fd, void *buffer, size_t size);
void assert_msg (int check, char *msg);

extern char *__brk;
extern void (*__call_at_exit) (void);

#define __FILEDES_MAX 512

#if !SYSTEM_LIBC
void __assert_fail (char const *s, char const *file, unsigned line,
                    char const *function);
ssize_t __buffered_read (int filedes, void *buffer, size_t size);
size_t __buffered_read_clear (int filedes);
void _exit (int code);
long brk (void *addr);
#endif // !SYSTEM_LIBC

long __mesabi_imod (long a, long b);
long __mesabi_idiv (long a, long b);
unsigned long __mesabi_umod (unsigned long a, unsigned long b);
unsigned long __mesabi_udiv (unsigned long a, unsigned long b);
unsigned long __mesabi_uldiv (unsigned long a, unsigned long b,
                              unsigned long *remainder);

void *__memcpy (void *dest, void const *src, size_t n);
void *__memmove (void *dest, void const *src, size_t n);
void *__memset (void *s, int c, size_t n);
int __raise (int signal);

#endif //__MES_LIB_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-mescc/_exit.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-mescc/_exit.c used in: Step 126
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

void
_exit ()
{
  asm ("mov____$i32,%eax SYS_exit");
  asm ("mov____0x8(%ebp),%ebx !8");
  asm ("int____$0x80");
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-mescc/_write.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-mescc/_write.c used in: Step 127
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

void
_write ()
{
  asm ("mov____$i32,%eax SYS_write");
  asm ("mov____0x8(%ebp),%ebx !8");
  asm ("mov____0x8(%ebp),%ecx !12");
  asm ("mov____0x8(%ebp),%edx !16");
  asm ("int____$0x80");
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/puts.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

int
puts (char const *s)
{
  oputs (s);
  return oputs ("\n");
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/errno.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_ERRNO_H
#define __MES_ERRNO_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_ERRNO_H
#include_next <errno.h>
#else // ! SYSTEM_LIBC

#ifndef __MES_ERRNO_T
#define __MES_ERRNO_T 1
typedef int error_t;
#endif // !__MES_ERRNO_T

extern int errno;
#define ENOENT   2
#define EINTR    4
#define EIO      5
#define ENXIO    6
#define E2BIG    7
#define ENOEXEC  8
#define EBADF    9
#define ECHILD  10
#define EAGAIN  11
#define ENOMEM  12
#define EACCES  13
#define EEXIST  17
#define EXDEV   18
#define ENOTDIR 20
#define EISDIR  21
#define EINVAL  22
#define EMFILE  24
#define ENOSPC  28
#define ESPIPE  29
#define EPIPE   32
#define ERANGE  34

#define ENAMETOOLONG 36
#define ENOSYS  38
#define ELOOP   40

#if !__MESC__
//extern char const *const sys_errlist[];
extern char *sys_errlist[];
extern int sys_nerr;
#endif // !__MESC__

#endif // ! SYSTEM_LIBC

#endif // __MES_ERRNO_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-mescc/syscall-internal.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-mescc/syscall-internal.c used in: Step 133
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/x86/syscall.h>

static long
__sys_call_internal (long sys_call)
{
  asm ("mov____0x8(%ebp),%eax !8");
  asm ("int____$0x80");
}

static long
__sys_call2_internal (long sys_call, long one, long two)
{
  asm ("mov____0x8(%ebp),%eax !8");
  asm ("mov____0x8(%ebp),%ebx !12");
  asm ("mov____0x8(%ebp),%ecx !16");
  asm ("int____$0x80");
}

/* Return < 0 on error (errno-like value from kernel), or 0 on success */
int
__raise (int signum)
{
  int pid = (int) __sys_call_internal (SYS_getpid);
  if (pid < 0)
    return pid;
  else
    return (int) __sys_call2_internal (SYS_kill, pid, signum);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/ctype.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_CTYPE_H
#define __MES_CTYPE_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_CTYPE_H
#include_next <ctype.h>

#else // ! SYSTEM_LIBC
#include <endian.h>

int isalnum (int c);
int isalpha (int c);
int isascii (int c);
int iscntrl (int c);
int isdigit (int c);
int isgraph (int c);
int islower (int c);
int isnumber (int c, int base);
int isprint (int c);
int ispunct (int c);
int isspace (int c);
int isupper (int c);
int isxdigit (int c);
int tolower (int c);
int toupper (int c);

#endif // ! SYSTEM_LIBC

#endif // __MES_CTYPE_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/cast.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

#undef cast_intp_to_charp
#undef cast_long_to_charp
#undef cast_charp_to_long
#undef cast_int_to_long
#undef cast_voidp_to_long

char*
cast_intp_to_charp (int const *i)
{
  return (char*)i;
}

char*
cast_long_to_charp (long i)
{
  return (char*)i;
}

long
cast_charp_to_long (char const *i)
{
  return (long)i;
}

long
cast_int_to_long (int i)
{
  return (long)i;
}

long
cast_voidp_to_long (void const *i)
{
  return (long)i;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/limits.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_LIMITS_H
#define __MES_LIMITS_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_LIMITS_H
#include_next <limits.h>

#else // ! SYSTEM_LIBC

#include <stdint.h>

#define MB_CUR_MAX 1
#define NAME_MAX 255
#define PATH_MAX 512
#define _POSIX_OPEN_MAX 16

#endif // ! SYSTEM_LIBC

#endif // __MES_LIMITS_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdlib.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_STDLIB_H
#define __MES_STDLIB_H 1

#ifndef __MES_COMPARISON_FN_T
#define __MES_COMPARISON_FN_T
typedef int (*comparison_fn_t) (void const *, void const *);
#endif

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_STDLIB_H
#include_next <stdlib.h>
#else // ! SYSTEM_LIBC

#include <sys/types.h>
#include <alloca.h>

void abort (void);
double atof (char const *s);
int atoi (char const *s);
int atexit (void (*function) (void));
void *calloc (size_t nmemb, size_t size);
void _exit (int status);
void exit (int status);
void free (void *ptr);
char *getenv (char const *s);
int setenv (char const *s, char const *v, int overwrite_p);
void unsetenv (char const *name);
void *malloc (size_t);
void qsort (void *base, size_t nmemb, size_t size, int (*compar) (void const *, void const *));
int rand (void);
void *realloc (void *p, size_t size);
double strtod (char const *string, char **tailptr);
float strtof (char const *string, char **tailptr);
long double strtold (char const *string, char **tailptr);
long strtol (char const *string, char **tailptr, int base);
long long strtoll (char const *string, char **tailptr, int base);
unsigned long strtoul (char const *string, char **tailptr, int base);
unsigned long long strtoull (char const *string, char **tailptr, int base);

#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0

void *bsearch (void const *key, void const *array, size_t count, size_t size, comparison_fn_t compare);

#endif // ! SYSTEM_LIBC

#endif // __MES_STDLIB_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/resource.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_RESOURCE_H
#define __MES_SYS_RESOURCE_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_RESOURCE_H
#include_next <sys/resource.h>

#else // ! SYSTEM_LIBC

#include <sys/time.h>

struct rusage
{
  struct timeval ru_utime;
  struct timeval ru_stime;
  long int ru_maxrss;
  long int ru_ixrss;
  long int ru_idrss;
  long int ru_isrss;
  long int ru_minflt;
  long int ru_majflt;
  long int ru_nswap;
  long int ru_inblock;
  long int ru_oublock;
  long int ru_msgsnd;
  long int ru_msgrcv;
  long int ru_nsignals;
  long int ru_nvcsw;
  long int ru_nivcsw;
};

#define RUSAGE_SELF 0
#define RUSAGE_CHILDREN -1
#define RLIMIT_NOFILE 1024
#define OPEN_MAX RLIMIT_NOFILE

int getrusage (int processes, struct rusage *rusage);

#endif // ! SYSTEM_LIBC

#endif // __MES_SYS_RESOURCE_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/unistd.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_UNISTD_H
#define __MES_UNISTD_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_UNISTD_H
#include_next <unistd.h>

#else // ! SYSTEM_LIBC

#if defined (BOOTSTRAP_WITH_POSIX)
#define _POSIX_VERSION 199009L
#endif

#include <sys/types.h>
#ifndef NULL
#define NULL 0
#endif

#ifndef STDIN_FILENO
#define STDIN_FILENO  0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#endif // STDIN_FILENO

#ifndef STDIN_FILE_NO
#define STDIN_FILE_NO  0
#define STDOUT_FILE_NO 1
#define STDERR_FILE_NO 2
#endif // STDIN_FILE_NO

#ifndef R_OK
#define F_OK 0
#define X_OK 1
#define W_OK 2
#define R_OK 4
#endif

int access (char const *s, int mode);
unsigned int alarm (unsigned int seconds);
int close (int fd);
int execv (char const *file_name, char *const argv[]);
int execl (char const *file_name, char const *arg, ...);
int execlp (char const *file_name, char const *arg, ...);
int execve (char const *file, char *const argv[], char *const env[]);
int execvp (char const *file, char *const argv[]);
int fork (void);
int fsync (int filedes);
char *getcwd (char *buf, size_t size);
uid_t getuid (void);
gid_t getgid (void);
int setgid (gid_t newgid);
int setuid (uid_t newuid);
uid_t geteuid (void);
gid_t getegid (void);
pid_t getpgrp (void);
pid_t getpid (void);
pid_t getppid (void);
int getpgid (pid_t pid);
int isatty (int fd);
int link (char const *old_name, char const *new_name);
off_t lseek (int fd, off_t offset, int whence);
ssize_t read (int fd, void *buffer, size_t size);
ssize_t readlink (char const *file_name, char *buffer, size_t size);
#if __SBRK_CHAR_PTRDIFF
/* xmalloc in binutils <= 2.10.1 uses this old prototype */
char *sbrk (ptrdiff_t delta);
#else
void *sbrk (intptr_t delta);
#endif
int symlink (char const *old_name, char const *new_name);
int unlink (char const *file_name);
ssize_t write (int filedes, void const *buffer, size_t size);

#endif // ! SYSTEM_LIBC

#endif // __MES_UNISTD_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ltoab.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

char *
ltoab (long x, int base)
{
  return ntoab (x, base, 1);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/mes_open.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdlib.h>

#if SYSTEM_LIBC
#include <fcntl.h>
#include <stdarg.h>
// The Mes C Library defines and initializes these in crt1
int __stdin = STDIN;
int __stdout = STDOUT;
int __stderr = STDERR;

int
mes_open (char const *file_name, int flags, int mask)
{
  int filedes = open (file_name, flags, mask);
  if (filedes > 2)
    __ungetc_clear (filedes);
  return filedes;
}

#else // !SYSTEM_LIBC

int
mes_open (char const *file_name, int flags, int mask)
{
  return _open3 (file_name, flags, mask);
}

#endif // !SYSTEM_LIBC

https://git.savannah.gnu.org/cgit/mes.git/tree/include/assert.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_ASSERT_H
#define __MES_ASSERT_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_ASSERT_H
#include_next <assert.h>
#else // ! SYSTEM_LIBC
#define assert(x) ((x) ? (void)0 : __assert_fail (#x, 0, 0, 0))
void __assert_fail (char const *s, char const *file, unsigned line,
                    char const *function);
#endif // ! SYSTEM_LIBC

#endif // __MES_ASSERT_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/oputc.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

int
oputc (int c)
{
  return fdputc (c, __stdout);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/ultoa.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

char *
ultoa (unsigned long x)
{
  return ntoab (x, 10, 1);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/utoa.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

char *
utoa (unsigned x)
{
  return ntoab (x, 10, 0);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/write.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>

ssize_t
write (int filedes, void const *buffer, size_t size)
{
  size_t skip = __buffered_read_clear (filedes);
  if (skip)
    lseek (filedes, -skip, SEEK_CUR);
  int r = _write (filedes, buffer, size);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdio.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_STDIO_H
#define __MES_STDIO_H 1

#include <mes/lib.h>

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_STDIO_H
#include_next <stdio.h>

#else // ! SYSTEM_LIBC

#ifndef _IOFBF
#define _IOFBF 0                /* Fully buffered.  */
#define _IOLBF 1                /* Line buffered.  */
#define _IONBF 2                /* No buffering.  */
#endif

#ifndef BUFSIZ
#define BUFSIZ 256
#endif

#ifndef L_tmpnam
#define L_tmpnam 100
#endif

#include <sys/types.h>

#define stdin (FILE*)0
#define stdout (FILE*)1
#define stderr (FILE*)2

#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2

FILE *fdopen (int fd, char const *mode);
FILE *fopen (char const *file_name, char const *mode);
int eputc (int c);
int eputs (char const *s);
int fclose (FILE * stream);
int feof (FILE * stream);
int ferror (FILE * stream);
int fflush (FILE * stream);
int fgetc (FILE * stream);
char *fgets (char *s, int size, FILE * stream);
int fprintf (FILE * stream, char const *format, ...);
int fpurge (FILE * stream);
int fputc (int c, FILE * stream);
int fputs (char const *s, FILE * stream);
int fscanf (FILE * stream, char const *template, ...);
int fseek (FILE * stream, long offset, int whence);
int getc (FILE * stream);
int getchar (void);
char *getlogin (void);
int printf (char const *format, ...);
int putc (int c, FILE * stream);
int putchar (int c);
int puts (char const *s);
int remove (char const *file_name);
int setvbuf (FILE * stream, char *buf, int mode, size_t size);
int snprintf (char *str, size_t size, char const *format, ...);
int sprintf (char *str, char const *format, ...);
int sscanf (char const *str, char const *format, ...);
int ungetc (int c, FILE * stream);
long ftell (FILE * stream);
size_t fread (void *ptr, size_t size, size_t count, FILE * stream);
size_t freadahead (FILE * fp);
size_t fwrite (void const *ptr, size_t size, size_t count, FILE * stream);

#endif // ! SYSTEM_LIBC

#endif // __MES_STDIO_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/lseek.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <stdio.h>
#include <sys/types.h>

#if !__MESC__ /* FIXME: We want bin/mes-mescc's x86-linux sha256sum to stay the same. */
off_t
_lseek (int filedes, off_t offset, int whence)
{
  return _sys_call3 (SYS_lseek, (int) filedes, (long) offset, (int) whence);
}
#endif

off_t
lseek (int filedes, off_t offset, int whence)
{
#if !__MESC__ /* FIXME: We want bin/mes-mescc's x86-linux sha256sum to stay the same. */
  if (_lseek (filedes, 0, SEEK_CUR) == -1)
    return -1;
#endif
  size_t skip = __buffered_read_clear (filedes);
  if (whence == SEEK_CUR)
    offset -= skip;
  return _sys_call3 (SYS_lseek, (int) filedes, (long) offset, (int) whence);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/linux/syscall.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __MES_SYSCALL_H
#define __MES_SYSCALL_H

long _sys_call (long sys_call);
long _sys_call1 (long sys_call, long one);
long _sys_call2 (long sys_call, long one, long two);
long _sys_call3 (long sys_call, long one, long two, long three);
long _sys_call4 (long sys_call, long one, long two, long three, long four);
long _sys_call6 (long sys_call, long one, long two, long three, long four, long five, long six);

#endif //__MES_SYSCALL_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/types.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_TYPES_H
#define __MES_SYS_TYPES_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_TYPES_H
#include_next <sys/types.h>
#else // ! SYSTEM_LIBC

#include <endian.h>

#ifndef __MESCCLIB__
#define __MESCCLIB__ 15
#endif

#ifndef EOF
#define EOF -1
#endif

#ifndef NULL
#define NULL 0
#endif

#ifndef __MES_CLOCK_T
#define __MES_CLOCK_T
#undef clock_t
typedef long clock_t;
#endif

#ifndef __MES_DEV_T
#define __MES_DEV_T
#undef dev_t
typedef long dev_t;
#endif

#if !defined (__MES_FILE_T) && ! defined (_FILE_T)
#define __MES_FILE_T
#define _FILE_T
typedef long FILE;
#endif

#ifndef __MES_GID_T
#define __MES_GID_T
#undef gid_t
typedef unsigned gid_t;
#endif

#ifndef __MES_INO_T
#define __MES_INO_T
#undef ino_t
typedef unsigned long ino_t;
#endif

#if __SIZEOF_LONG_LONG__ == 8
#ifndef __MES_INO64_T
#define __MES_INO64_T
#undef ino64_t
typedef unsigned long long ino64_t;
#endif
#endif // __SIZEOF_LONG_LONG__ == 8

#if !defined (__MES_INTPTR_T) && !defined (__intptr_t_defined)
#define __MES_INTPTR_T
#define __intptr_t_defined
#undef intptr_t
typedef long intptr_t;
#undef uintptr_t
typedef unsigned long uintptr_t;
#endif

#ifndef __MES_OFF_T
#define __MES_OFF_T
#undef off_t
typedef long off_t;
#endif

#if __SIZEOF_LONG_LONG__ == 8
#ifndef __MES_OFF64_T
#define __MES_OFF64_T
#undef off64_t
typedef unsigned long long off64_t;
#endif
#endif // __SIZEOF_LONG_LONG__ == 8

#ifndef __MES_PID_T
#define __MES_PID_T
#undef pid_t
typedef int pid_t;
#endif

#ifndef __PTRDIFF_T
#define __PTRDIFF_T
#ifndef __MES_PTRDIFF_T
#define __MES_PTRDIFF_T
#undef ptrdiff_t
typedef long ptrdiff_t;
#endif
#endif

#ifndef __MES_SIGVAL_T
#define __MES_SIGVAL_T
#undef clock_t
typedef long sigval_t;
#endif

#ifndef __SIZE_T
#define __SIZE_T
#ifndef __MES_SIZE_T
#define __MES_SIZE_T
#undef size_t
typedef unsigned long size_t;
#endif
#endif

#ifndef __MES_SSIZE_T
#define __MES_SSIZE_T
#undef ssize_t
typedef long ssize_t;
#endif

#ifndef __MES_UID_T
#define __MES_UID_T
#undef uid_t
typedef unsigned uid_t;
#endif

#ifndef __WCHAR_T
#define __WCHAR_T
#ifndef __MES_WCHAR_T
#define __MES_WCHAR_T
#undef wchar_t
typedef int wchar_t;
#endif
#endif

#endif // ! SYSTEM_LIBC

#endif // __MES_SYS_TYPES_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/__buffered_read.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdlib.h>
#include <string.h>

#if !__MESC__
#define __READ_BUFFER_MAX 128
int __read_buffer_max;
#else /* FIXME: We want bin/mes-mescc's x86-linux sha256sum to stay the same. */
#define __READ_BUFFER_MAX 100
#define __read_buffer_max 100
#endif

struct __read_buffer
{
  ssize_t size;
  char string[__READ_BUFFER_MAX];
};

struct __read_buffer *__read_cache = 0;

void
__buffered_read_init (int filedes)
{
  if (!__read_cache)
    {
      __read_cache = (struct __read_buffer *) malloc (sizeof (struct __read_buffer) * __FILEDES_MAX);
#if !__MESC__
      __read_buffer_max = __READ_BUFFER_MAX;
      char *p = getenv ("MES_READ_BUFFER");
      if (p)
        {
          __read_buffer_max = atoi (p);
          if (__read_buffer_max < 0)
            __read_buffer_max = 0;
          if (__read_buffer_max > __READ_BUFFER_MAX)
            __read_buffer_max = __READ_BUFFER_MAX;
        }
#endif
    }
}

size_t
__buffered_read_clear (int filedes)
{
  __buffered_read_init (filedes);
  size_t size = __read_cache[filedes].size;
  __read_cache[filedes].size = 0;
  return size;
}

ssize_t
__buffered_read (int filedes, void *buffer, size_t size)
{
  size_t todo = size;
  __buffered_read_init (filedes);
  struct __read_buffer *cache = &__read_cache[filedes];
  char *p = buffer;
  if (!cache->size && size > __read_buffer_max)
    return _read (filedes, buffer, size);
  while (cache->size > 0 && todo)
    {
      todo--;
      *p++ = cache->string[__read_buffer_max - cache->size--];
    }
  if (todo)
    {
#if !__MESC__
      if (todo > __read_buffer_max)
        return size - todo + _read (filedes, p, todo);
      if (__mes_debug () > 4)
        {
          eputs ("__buffered_read: ");
          eputs (itoa (__read_buffer_max));
          eputs ("\n");
        }
#endif
      ssize_t bytes = _read (filedes, cache->string, __read_buffer_max);
      if (bytes < 0)
        return -1;
      if (bytes)
        {
          cache->size = bytes;
          if (bytes < __read_buffer_max)
            memmove (cache->string + __read_buffer_max - bytes, cache->string, bytes);
          return size - todo + __buffered_read (filedes, p, todo);
        }
    }
  return size - todo;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/__mes_debug.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include <stdlib.h>

#define MAX(a, b) (((a) > (b)) ? (a) : (b))

int
__mes_debug ()
{
  static int __mes_debug = -1;
  if (__mes_debug == -1)
    {
      char *p = getenv ("MES_DEBUG");
      __mes_debug = p ? MAX (atoi (p), 1) : 0;
    }
  return __mes_debug;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execv.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <unistd.h>

int
execv (char const *file_name, char *const argv[])
{
  return execve (file_name, argv, environ);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/getcwd.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <limits.h>
#include <sys/types.h>
#include <mes/lib.h>

char *__getcwd_buf;

char *
getcwd (char *buffer, size_t size)
{
  static char buf[PATH_MAX];
  if (buffer == 0)
    buffer = buf;
  return _getcwd (buffer, size);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/isatty.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <termio.h>

#if !SYSTEM_LIBC
typedef unsigned char cc_t;
typedef unsigned int speed_t;
typedef unsigned int tcflag_t;

// Use termio, termios2?
#define NCCS 19
struct termios
{
  tcflag_t c_iflag;
  tcflag_t c_oflag;
  tcflag_t c_cflag;
  tcflag_t c_lflag;
  cc_t c_line;
  cc_t c_cc[NCCS];
};
#endif

struct ktermios
{
  tcflag_t c_iflag;
  tcflag_t c_oflag;
  tcflag_t c_cflag;
  tcflag_t c_lflag;
  cc_t c_line;
  cc_t c_cc[NCCS];
  speed_t c_ispeed;
  speed_t c_ospeed;
};

int
__tcgetattr (int filedes, struct termios *termios_p)
{
  struct ktermios kernel_termios;
  int r = ioctl3 (filedes, TCGETS, (long) &kernel_termios);

  termios_p->c_iflag = kernel_termios.c_iflag;
  termios_p->c_oflag = kernel_termios.c_oflag;
  termios_p->c_cflag = kernel_termios.c_cflag;
  termios_p->c_lflag = kernel_termios.c_lflag;
  termios_p->c_line = kernel_termios.c_line;
#if 0
  termios_p->c_ispeed = kernel_termios.c_ispeed;
  termios_p->c_ospeed = kernel_termios.c_ospeed;
#endif
  memcpy (&termios_p->c_cc[0], &kernel_termios.c_cc[0], NCCS * sizeof (cc_t));
  return r;
}

int
isatty (int filedes)
{
  struct termios term;
  return __tcgetattr (filedes, &term) == 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/ioctl.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_IOCTL_H
#define __MES_SYS_IOCTL_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_IOCTL_H
#include_next <sys/ioctl.h>

#else // ! SYSTEM_LIBC

#define TCGETS 0x5401
#define TCGETA 0x5405
int ioctl (int fd, unsigned long request, ...);
int ioctl3 (int fd, unsigned long request, long data);

#endif // ! SYSTEM_LIBC

#endif // __MES_SYS_IOCTL_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/termio.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_TERMIO_H
#define __MES_TERMIO_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_TERMIO_H
#include_next <termio.h>

#else // ! SYSTEM_LIBC

#define TIOCGWINSZ  0x5413
#define TCGETA      0x5405
#define TCSETAW     0x5407

#define VTIME 5
#define VMIN 6

#define ISIG    0000001
#define ICANON  0000002
#define ECHO    0000010
#define ECHOK   0000040
#define ECHONL  0000100

#define ISTRIP  0000040
#define INLCR   0000100
#define ICRNL   0000400

#define   CS8   0000060
#define PARENB  0000400

struct winsize
{
  unsigned short ws_row;
  unsigned short ws_col;
  unsigned short ws_xpixel;
  unsigned short ws_ypixel;
};

struct termio
{
  unsigned short c_iflag;
  unsigned short c_oflag;
  unsigned short c_cflag;
  unsigned short c_lflag;
  unsigned char c_line;
  unsigned char c_cc[8];
};

#endif // ! SYSTEM_LIBC

#endif // __MES_TERMIO_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/open.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <fcntl.h>
#include <stdarg.h>

int
_open2 (char const *file_name, int flags)
{
  int mask = 0777;
  return _open3 (file_name, flags, mask);
}

int
open (char const *file_name, int flags, ...)
{
  if (flags & O_CREAT)
    {
      va_list ap;
      va_start (ap, flags);
      int mask = va_arg (ap, int);
      int r = _open3 (file_name, flags, mask);
      va_end (ap);
      return r;
    }
  else
    return _open2 (file_name, flags);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/fcntl.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_FCNTL_H
#define __MES_FCNTL_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_FCNTL_H
#include_next <fcntl.h>

#else // ! SYSTEM_LIBC

// *INDENT-OFF*
#if __linux__
#define O_RDONLY          0
#define O_WRONLY          1
#define O_RDWR            2
#define O_CREAT        0x40
#define O_EXCL         0x80
#define O_TRUNC       0x200
#define O_APPEND      0x400

#ifdef __arm__
#define O_DIRECTORY 0x4000
/*#define O_DIRECT 0x10000*/
#else
#define O_DIRECTORY 0x10000
#endif

#elif __GNU__
#define O_RDONLY      1
#define O_WRONLY      2
#define O_RDWR        3
#define O_CREAT        0x10
#define O_APPEND      0x100
#define O_TRUNC     0x10000
#else
#error platform not supported
#endif
// *INDENT-ON*

#define FD_CLOEXEC 1

#define F_DUPFD 0
#define F_GETFD 1
#define F_SETFD 2
#define F_GETFL 3
#define F_SETFL 4

#define creat(file_name, mode) open (file_name, O_WRONLY | O_CREAT | O_TRUNC, mode)
int dup (int old);
int dup2 (int old, int new);
int fcntl (int filedes, int command, ...);
int open (char const *s, int flags, ...);

#endif // ! SYSTEM_LIBC

#endif // __MES_FCNTL_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdarg.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_STDARG_H
#define __MES_STDARG_H 1

#if SYSTEM_LIBC
#undef __MES_STDARG_H
#include_next <stdarg.h>

#define va_arg8(ap, type) va_arg(ap, type)

#else // ! SYSTEM_LIBC

#include <sys/types.h>

#if __GNUC__ && __x86_64__
#define __FOO_VARARGS 1
#endif

typedef char *va_list;
#define va_start(ap, last) (void)((ap) = (char*)(&(last) + 1))
#define va_arg(ap, type) (type)(((long*)((ap) = ((ap) + sizeof (void*))))[-1])
#define va_align(ap, alignment) ((char*)((((unsigned long) (ap)) + (alignment) - 1) &~ ((alignment) - 1)))
#define va_arg8(ap, type) (type)(((double*)((ap) = (va_align((ap), 8) + sizeof(double))))[-1])
#define va_end(ap) (void)((ap) = 0)
#define va_copy(dest, src) dest = src

int vexec (char const *file_name, va_list ap);
int vfprintf (FILE * stream, char const *template, va_list ap);
int vfscanf (FILE * stream, char const *template, va_list ap);
int vprintf (char const *format, va_list ap);
int vsprintf (char *str, char const *format, va_list ap);
int vsnprintf (char *str, size_t size, char const *format, va_list ap);
int vsscanf (char const *s, char const *template, va_list ap);

#endif // ! SYSTEM_LIBC

#endif // __MES_STDARG_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/buffered-read.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <fcntl.h>

ssize_t
read (int filedes, void *buffer, size_t size)
{
  ssize_t bytes = __buffered_read (filedes, buffer, size);
  if (__mes_debug () > 4)
    {
      if (bytes == 1)
        {
          eputs ("read fd=");
          eputs (itoa ((int) filedes));
          eputs (" c=");
          eputc (*(char *) buffer);
          eputs ("\n");
        }
      else
        {
          eputs ("read fd=");
          eputs (itoa ((int) filedes));
          eputs (" bytes=");
          eputs (itoa (bytes));
          eputs ("\n");
        }
    }
  return bytes;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/wait.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <sys/wait.h>

pid_t
wait (int *status_ptr)
{
  return waitpid (-1, status_ptr, 0);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/wait.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_WAIT_H
#define __MES_SYS_WAIT_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_WAIT_H
#include_next <sys/wait.h>
#else // ! SYSTEM_LIBC

#ifndef __MES_PID_T
#define __MES_PID_T
typedef int pid_t;
#endif

#define WNOHANG 1
#define W_EXITCODE(status, signal) ((status) << 8 | (signal))

pid_t waitpid (pid_t pid, int *status_ptr, int options);
pid_t wait (int *status_ptr);

#endif // ! SYSTEM_LIBC

#endif // __MES_SYS_WAIT_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fgetc.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

int
fgetc (FILE * stream)
{
  return fdgetc ((long) stream);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fputc.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

int
fputc (int c, FILE * stream)
{
  return fdputc (c, (long) stream);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fputs.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

int
fputs (char const *s, FILE * stream)
{
  return fdputs (s, (long) stream);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/getc.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

int
getc (FILE * stream)
{
  return fdgetc ((long) stream);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/putc.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

int
putc (int c, FILE * stream)
{
  return fdputc (c, (long) stream);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/ungetc.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

int
ungetc (int c, FILE * stream)
{
  return fdungetc (c, (long) stream);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memchr.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

void *
memchr (void const *block, int c, size_t size)
{
  char const *p = block;

  while (size != 0)
    {
      size = size - 1;
      if (c == p[0])
        return (void*) p;
      p = p + 1;
    }

  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memmove.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

void *
memmove (void *dest, void const *src, size_t n)
{
  if (dest < src)
    return memcpy (dest, src, n);
  char *p = dest + n;
  char const *q = src + n;
  while (n--)
    *--p = *--q;
  return dest;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/raise.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <signal.h>
#include <unistd.h>

int
raise (int signum)
{
  kill (getpid (), signum);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/signal.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SIGNAL_H
#define __MES_SIGNAL_H 1

#if SYSTEM_LIBC
#undef __MES_SIGNAL_H
#include_next <signal.h>
#else //! SYSTEM_LIBC

#define _NSIG 64

#define _SIGSET_NITEMS (_NSIG / (8 * sizeof(unsigned long)))

typedef struct {
  unsigned long items[_SIGSET_NITEMS];
} sigset_t;
typedef long stack_t;

#include <sys/types.h>

// *INDENT-OFF*
#define NSIG 30
#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 FPE_INTDIV 1
#define FPE_INTOVF 2
#define FPE_FLTDIV 3
#define FPE_FLTOVF 4
#define FPE_FLTUND 5
#define FPE_FLTRES 6
#define FPE_FLTINV 7
#define FPE_FLTSUB 8

#define SA_NOCLDSTOP 0x00000001
#define SA_NOCLDWAIT 0x00000002
#define SA_SIGINFO   0x00000004
#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

typedef struct siginfo_t
{
  int          si_signo;
  int          si_errno;
  int          si_code;
  int          si_trapno;
  pid_t        si_pid;
  uid_t        si_uid;
  int          si_status;
  clock_t      si_utime;
  clock_t      si_stime;
  sigval_t     si_value;
  int          si_int;
  void        *si_ptr;
  int          si_overrun;
  int          si_timerid;
  void        *si_addr;
  long         si_band;
  int          si_fd;
  short        si_addr_lsb;
  void        *si_lower;
  void        *si_upper;
  int          si_pkey;
  void        *si_call_addr;
  int          si_syscall;
  unsigned int si_arch;
} siginfo_t;
// *INDENT-ON*

#if __MESC__
typedef long sighandler_t;
#else
typedef void (*sighandler_t) (int);
#endif

#if __i386__ || __x86_64__
struct sigaction
{
  union
  {
    sighandler_t sa_handler;
    void (*sa_sigaction) (int signum, siginfo_t *, void *);
  };
  unsigned long sa_flags;
#if __x86_64__
  long _foo0;
#endif
  sigset_t sa_mask;
#if __x86_64__
  long _foo1[15];
#endif
  //unsigned long sa_flags; // x86?
  void (*sa_restorer) (void);
};
#else /* uapi */
struct sigaction
{
  union
  {
    sighandler_t sa_handler;
    void (*sa_sigaction) (int signum, siginfo_t *, void *);
  };
  unsigned long sa_flags;
  void (*sa_restorer) (void);
  sigset_t sa_mask;
};
#endif

#define SIG_DFL ((sighandler_t)0)
#define SIG_IGN ((sighandler_t)1)
#define SIG_ERR ((sighandler_t)-1)

#ifdef __i386__

#define EBX 0
#define ECX 1
#define EDX 2
#define ESI 3
#define EDI 4
#define EBP 5
#define EAX 6
#define DS 7
#define ES 8
#define FS 9
#define GS 10
#define ORIG_EAX 11
#define EIP 12
#define CS  13
#define EFL 14
#define UESP 15
#define SS   16
#define FRAME_SIZE 17

/* Type for general register.  */
typedef int greg_t;

/* Number of general registers.  */
#define NGREG        19

/* Container for all general registers.  */
typedef greg_t gregset_t[NGREG];

/* Definitions taken from the kernel headers.  */
struct _libc_fpreg
{
  unsigned short int significand[4];
  unsigned short int exponent;
};

struct _libc_fpstate
{
  unsigned long int cw;
  unsigned long int sw;
  unsigned long int tag;
  unsigned long int ipoff;
  unsigned long int cssel;
  unsigned long int dataoff;
  unsigned long int datasel;
  struct _libc_fpreg _st[8];
  unsigned long int status;
};

/* Structure to describe FPU registers.  */
typedef struct _libc_fpstate *fpregset_t;

typedef struct
{
  gregset_t gregs;
  /* Due to Linux's history we have to use a pointer here.  The SysV/i386
     ABI requires a struct with the values.  */
  fpregset_t fpregs;
  unsigned long int oldmask;
  unsigned long int cr2;
} mcontext_t;

/* Userlevel context.  */
typedef struct ucontext
{
  unsigned long int uc_flags;
  struct ucontext *uc_link;
  stack_t uc_stack;
  mcontext_t uc_mcontext;
  sigset_t uc_sigmask;
  struct _libc_fpstate __fpregs_mem;
} ucontext_t;
#endif // !__i386__

int kill (pid_t pid, int signum);
int raise (int);
int sigaction (int signum, struct sigaction const *act, struct sigaction *oldact);
int sigaddset (sigset_t * set, int signum);
#if __MESC__
void *signal (int signum, void *action);
#else
sighandler_t signal (int signum, sighandler_t action);
#endif
int sigemptyset (sigset_t * set);
#ifndef SIG_BLOCK
#define SIG_BLOCK 0
#define SIG_UNBLOCK 1
#define SIG_SETMASK 2
#endif
int sigprocmask (int how, sigset_t const *set, sigset_t * oldset);

#endif //! SYSTEM_LIBC

#endif // __MES_SIGNAL_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/chmod.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>

int
chmod (char const *file_name, mode_t mask)
{
  long long_file_name = cast_charp_to_long (file_name);
  long long_mask = cast_int_to_long (mask);
  return _sys_call2 (SYS_chmod, long_file_name, long_mask);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/clock_gettime.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <linux/syscall.h>
#include <arch/syscall.h>
#include <time.h>

int
clock_gettime (clockid_t clk_id, struct timespec *tp)
{
  long long_clk_id = cast_int_to_long (clk_id);
  long long_tp = cast_voidp_to_long (tp);
  return _sys_call2 (SYS_clock_gettime, long_clk_id, long_tp);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/time.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_TIME_H
#define __MES_TIME_H 1

#if SYSTEM_LIBC
#undef __MES_TIME_H
#include_next <time.h>
#else // ! SYSTEM_LIBC

#ifndef __MES_TIME_T
#define __MES_TIME_T 1
typedef long int clockid_t;
typedef long int time_t;
#endif

struct tm
{
  int tm_sec;
  int tm_min;
  int tm_hour;
  int tm_mday;
  int tm_mon;
  int tm_year;
  int tm_wday;
  int tm_yday;
  int tm_isdst;
};

#ifndef __MES_STRUCT_TIMESPEC
#define __MES_STRUCT_TIMESPEC

struct timespec
{
  long tv_sec;
  long tv_nsec;
};

#endif // __MES_STRUCT_TIMESPEC

#define CLOCK_PROCESS_CPUTIME_ID 2
int clock_gettime (clockid_t clk_id, struct timespec *tp);
struct tm *localtime (time_t const *timep);
struct tm *gmtime (time_t const *time);
time_t mktime (struct tm *broken_time);
int nanosleep (struct timespec const *requested_time, struct timespec const *remaining);
time_t time (time_t * tloc);

#endif // ! SYSTEM_LIBC

#endif // __MES_TIME_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/execve.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>

int
execve (char const *file_name, char *const argv[], char *const env[])
{
  return _sys_call3 (SYS_execve, (long) file_name, (long) argv, (long) env);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fsync.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>

int
fsync (int filedes)
{
  return _sys_call1 (SYS_fsync, (int) filedes);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/time.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_TIME_H
#define __MES_SYS_TIME_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_TIME_H
#include_next <sys/time.h>

#else // ! SYSTEM_LIBC

struct timeval
{
  long tv_sec;
  long tv_usec;
};

struct timezone
{
  int tz_minuteswest;
  int tz_dsttime;
};

struct itimerval
{
  struct timeval it_interval;
  struct timeval it_value;
};

#define ITIMER_REAL    0
#define ITIMER_VIRTUAL 1
#define ITIMER_PROF    2

int gettimeofday (struct timeval *tv, struct timezone *tz);
int setitimer (int which, struct itimerval const *new, struct itimerval *old);

#endif // ! SYSTEM_LIBC

#endif // __MES_SYS_TIME_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/malloc.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2021 Danny Milosavljevic <dannym@scratchpost.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <string.h>
#include <stddef.h>
#include <stdint.h>

char *__brk = 0;

void *
malloc (size_t size)
{
  if (!__brk)
    __brk = cast_long_to_charp (brk (0));
  /* align what we give back. */
  __brk = (char*) (((uintptr_t) __brk
                    + sizeof (max_align_t) - 1) & -sizeof (max_align_t));
  if (brk (__brk + size) == -1)
    return 0;
  char *p = __brk;
  __brk = __brk + size;
  return p;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/stddef.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2021 Danny Milosavljevic <dannym@scratchpost.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_STDDEF_H
#define __MES_STDDEF_H 1

#if SYSTEM_LIBC
#undef __MES_STDDEF_H
#include_next <stddef.h>
#else // ! SYSTEM_LIBC

#include <sys/types.h>
#include <stdint.h>
#include <unistd.h>

#ifndef offsetof
#if __MESC__
#define offsetof(type, field) (&((type *)0)->field)
#else // !__MESC__
#define offsetof(type, field) ((size_t)&((type *)0)->field)
#endif // !__MESC__
#endif // offsetof

/* Note: on banana gcc, max_align_t is 16 Byte big instead! */

typedef double max_align_t;

#endif // ! SYSTEM_LIBC

#endif // __MES_STDDEF_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdint.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2018 Peter De Wachter <pdewacht@gmail.com>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_STDINT_H
#define __MES_STDINT_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_STDINT_H
#include_next <stdint.h>

#else // ! SYSTEM_LIBC

#undef unsigned
#undef uint8_t
#undef int8_t

#undef uint16_t
#undef int16_t

#undef uint32_t
#undef int32_t

#undef uint64_t
#undef int64_t

#undef uintptr_t
#undef intmax_t
#undef intptr_t
#undef uintmax_t
#undef ptrdiff_t

typedef unsigned char uint8_t;
typedef char int8_t;
typedef unsigned short uint16_t;
typedef short int16_t;
typedef unsigned uint32_t;
typedef int int32_t;
#if __SIZEOF_LONG_LONG__ == 8
typedef unsigned long long uint64_t;
typedef long long int64_t;
#endif // __SIZEOF_LONG_LONG__ == 8

typedef int intmax_t;
typedef unsigned uintmax_t;

#include <sys/types.h>

#define CHAR_BIT 8
#define CHAR_MAX 255
#define UCHAR_MAX 255

#define INT8_MAX 127
#define INT8_MIN (-INT8_MAX-1)
#define UINT8_MAX 255

#define INT16_MAX 32767
#define INT16_MIN (-INT16_MAX-1)
#define UINT16_MAX 65535

#define INT32_MAX 2147483647
#define INT32_MIN (-INT32_MAX-1)
#define UINT32_MAX 4294967295U

#define INT64_MAX 9223372036854775807LL
#define INT64_MIN (-INT64_MAX-1)
#define UINT64_MAX 18446744073709551615ULL

#define INT_MIN -2147483648
#define INT_MAX 2147483647
#if __i386__ || __arm__
#define LONG_MIN INT_MIN
#define LONG_MAX INT_MAX
#define UINT_MAX UINT32_MAX
#define ULONG_MAX UINT32_MAX
#define LLONG_MIN INT64_MIN
#define LLONG_MAX INT64_MAX
#define SIZE_MAX UINT32_MAX
#elif __x86_64__
#define LONG_MIN INT64_MIN
#define LONG_MAX INT64_MAX
#define UINT_MAX UINT32_MAX
#define ULONG_MAX UINT64_MAX
#define LLONG_MIN INT64_MIN
#define LLONG_MAX INT64_MAX
#define SIZE_MAX UINT64_MAX
#endif

#endif // ! SYSTEM_LIBC

#endif // __MES_STDINT_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/_open3.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <mes/lib.h>
#include <fcntl.h>

int
_open3 (char const *file_name, int flags, int mask)
{
  int r = _sys_call3 (SYS_open, (long) file_name, (int) flags, (int) mask);
  if (r > 2)
    {
      __ungetc_clear (r);
      __buffered_read_clear (r);
    }
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/_read.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <mes/lib.h>
#include <fcntl.h>

ssize_t
_read (int filedes, void *buffer, size_t size)
{
  return _sys_call3 (SYS_read, (int) filedes, (long) buffer, (long) size);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/time.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>

time_t
time (time_t * result)
{
  struct timeval tv;
  struct timezone tz;
  if (gettimeofday (&tv, &tz) != 0)
    return (time_t) - 1;
  if (result)
    *result = tv.tv_sec;
  return tv.tv_sec;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/waitpid.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/types.h>

pid_t
waitpid (pid_t pid, int *status_ptr, int options)
{
#if __i386__
  return _sys_call3 (SYS_waitpid, (long) pid, (long) status_ptr, (int) options);
#elif __x86_64__ || __arm__
  return _sys_call4 (SYS_wait4, (long) pid, (long) status_ptr, (int) options, 0);
#else
#error arch not supported
#endif
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-mescc/syscall.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes-mescc/syscall.c used in: Step 207
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <errno.h>
#include <linux/x86/syscall.h>

long
__sys_call (long sys_call)
{
  asm ("mov____0x8(%ebp),%eax !8");
  asm ("int____$0x80");
}

long
__sys_call1 (long sys_call, long one)
{
  asm ("mov____0x8(%ebp),%eax !8");
  asm ("mov____0x8(%ebp),%ebx !12");
  asm ("int____$0x80");
}

long
__sys_call2 (long sys_call, long one, long two)
{
  asm ("mov____0x8(%ebp),%eax !8");
  asm ("mov____0x8(%ebp),%ebx !12");
  asm ("mov____0x8(%ebp),%ecx !16");
  asm ("int____$0x80");
}

long
__sys_call3 (long sys_call, long one, long two, long three)
{
  asm ("mov____0x8(%ebp),%eax !8");
  asm ("mov____0x8(%ebp),%ebx !12");
  asm ("mov____0x8(%ebp),%ecx !16");
  asm ("mov____0x8(%ebp),%edx !20");
  asm ("int____$0x80");
}

long
__sys_call4 (long sys_call, long one, long two, long three, long four)
{
  asm ("mov____0x8(%ebp),%eax !8");
  asm ("mov____0x8(%ebp),%ebx !12");
  asm ("mov____0x8(%ebp),%ecx !16");
  asm ("mov____0x8(%ebp),%edx !20");
  asm ("mov____0x8(%ebp),%esi !24");
  asm ("int____$0x80");
}

long
_sys_call (long sys_call)
{
  long r = __sys_call (sys_call);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

long
_sys_call1 (long sys_call, long one)
{
  long r = __sys_call1 (sys_call, one);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

long
_sys_call2 (long sys_call, long one, long two)
{
  long r = __sys_call2 (sys_call, one, two);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

long
_sys_call3 (long sys_call, long one, long two, long three)
{
  long r = __sys_call3 (sys_call, one, two, three);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

long
_sys_call4 (long sys_call, long one, long two, long three, long four)
{
  long r = __sys_call4 (sys_call, one, two, three, four);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getpid.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>

pid_t
getpid ()
{
  return _sys_call (SYS_getpid);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/kill.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>

int
kill (pid_t pid, int signum)
{
  return _sys_call2 (SYS_kill, (long) pid, (long) signum);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/mes/cc.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/mes/cc.h used in: Step 212, Step 213, Step 214, Step 215, Step 216, Step 217, Step 218, Step 219, Step 220, Step 221, Step 222, Step 223, Step 224, Step 225, Step 226, Step 227, Step 228, Step 229, Step 230, Step 342
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __MES_CC_H
#define __MES_CC_H

typedef struct scm* SCM;

#if __MESC__
typedef long FUNCTION;
typedef long function0_t;
typedef long function1_t;
typedef long function2_t;
typedef long function3_t;
typedef long functionn_t;
#else // !__MESC__
typedef SCM (*FUNCTION) (void);
typedef SCM (*function0_t) (void);
typedef SCM (*function1_t) (SCM);
typedef SCM (*function2_t) (SCM, SCM);
typedef SCM (*function3_t) (SCM, SCM, SCM);
typedef SCM (*functionn_t) (SCM);
#endif // !__MESC__

#endif //__MES_CC_H

https://git.savannah.gnu.org/cgit/mes.git/tree/src/cc.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/cc.c used in: Step 213
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib.h"
#include "mes/mes.h"

struct scm *
apply_builtin0 (struct scm *fn)
{
  struct scm *(*fp) (void) = (function0_t) builtin_function (fn);
  return fp ();
}

struct scm *
apply_builtin1 (struct scm *fn, struct scm *x)
{
  struct scm *(*fp) (struct scm *) = (function1_t) builtin_function (fn);
  return fp (x);
}

struct scm *
apply_builtin2 (struct scm *fn, struct scm *x, struct scm *y)
{
  struct scm *(*fp) (struct scm *, struct scm *) = (function2_t) builtin_function (fn);
  return fp (x, y);
}

struct scm *
apply_builtin3 (struct scm *fn, struct scm *x, struct scm *y, struct scm *z)
{
  struct scm *(*fp) (struct scm *, struct scm *, struct scm *) = (function3_t) builtin_function (fn);
  return fp (x, y, z);
}

#undef cast_charp_to_scmp
#undef cast_charp_to_scmpp
#undef cast_voidp_to_charp
#undef cast_scmp_to_long
#undef cast_scmp_to_charp

struct scm *
cast_charp_to_scmp (char const *i)
{
  return (struct scm *)i;
}

struct scm **
cast_charp_to_scmpp (char const *i)
{
  return (struct scm **)i;
}

char*
cast_voidp_to_charp (void const *i)
{
  return (char*)i;
}

long
cast_scmp_to_long (struct scm *i)
{
  return (long)i;
}

char*
cast_scmp_to_charp (struct scm *i)
{
  return (char*)i;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/src/globals.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/src/globals.c used in: Step 218
/*
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2021 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib-mini.h>

#define extern
#include <mes/symbols.h>
#include <mes/mes.h>

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/islower.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>

int
islower (int c)
{
  return c >= 'a' && c <= 'z';
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isupper.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>

int
isupper (int c)
{
  return c >= 'A' && c <= 'Z';
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/tolower.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>

int
tolower (int c)
{
  if (isupper (c))
    return c + ('a' - 'A');
  return c;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/toupper.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>

int
toupper (int c)
{
  if (islower (c))
    return c - ('a' - 'A');
  return c;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/abtod.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

double
abtod (char const **p, int base)
{
  char const *s = *p;
  double d = 0;
  int sign_p = 0;
  if (!base)
    base = 10;
  double dbase = base;
  long i = abtol (&s, base);
  long f = 0;
  long e = 0;
  if (*s == '.')
    {
      s++;
      f = abtol (&s, base);
    }
  if (*s == 'e')
    {
      s++;
      e = abtol (&s, base);
    }
  d = i + f / dbase;
  if (e < 0)
    while (e++)
      d = d / dbase;
  while (e--)
    d = d * dbase;
  *p = s;
  return sign_p ? -d : d;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/dtoab.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <limits.h>
#include <string.h>

char *
dtoab (double d, int base, int signed_p)
{
  static char dtoa_buf[40];
  long i = (long) d;
  char *p = ntoab (i, base, signed_p);
  strcpy (dtoa_buf, p);
  long f = (d - (double) i) * (double) 100000000;
  if (f)
    {
      if (f < 0)
        f = -f;
      strcat (dtoa_buf, ".");
      p = ntoab (f, base, 1);
      strcat (dtoa_buf, p);
      p = strchr (dtoa_buf, 0);
      p--;
      while (*p && *p == '0')
        *p-- = 0;
    }
  return dtoa_buf;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/search-path.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

char *
search_path (char const *file_name)
{
  static char buf[256];
  char *path = getenv ("PATH");
  if (__mes_debug ())
    {
      eputs ("\n search-path: ");
      eputs (file_name);
      eputs ("\n");
    }
  while (*path)
    {
      char *end = strchr (path, ':');
      if (!end)
        end = strchr (path, '\0');
      strncpy (buf, path, end - path);
      buf[end - path] = 0;
      if (__mes_debug ())
        {
          eputs (" dir: ");
          eputs (buf);
          eputs ("\n");
        }
      if (buf[end - path] != '/')
        strcat (buf, "/");
      strcat (buf, file_name);
      if (!access (buf, X_OK))
        {
          if (__mes_debug ())
            {
              eputs (" found: ");
              eputs (buf);
              eputs ("\n");
            }
          return buf;
        }
      path = end + 1;
    }
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execvp.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

int
execvp (char const *file_name, char *const argv[])
{
  if (!strchr (file_name, '/'))
    file_name = search_path (file_name);
  if (!file_name)
    {
      errno = ENOENT;
      return -1;
    }
  if (__mes_debug ())
    {
      eputs (" EXEC: ");
      eputs (file_name);
      eputs ("\n");
      int i = 0;
      while (argv[i])
        {
          eputs (" arg[");
          eputs (itoa (i));
          eputs ("]: ");
          eputs (argv[i]);
          eputs ("\n");
          i++;
        }
    }
  return execve (file_name, argv, environ);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fclose.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <unistd.h>

int
fclose (FILE * stream)
{
  int fd = (long) stream;
  return close (fd);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fdopen.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

FILE *
fdopen (int fd, char const *mode)
{
  return (FILE *) (long) fd;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/ferror.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

int
ferror (FILE * stream)
{
  int fd = (long) stream;
  if (fd == -1)
    return -1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fflush.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

int
fflush (FILE * stream)
{
  int filedes = (long) stream;
  if (filedes < 3)
    return 0;
  return fsync (filedes);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fopen.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2018 Jeremiah Orians <jeremiah@pdp10.guru>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

FILE *
fopen (char const *file_name, char const *opentype)
{
  if (__mes_debug ())
    {
      eputs ("fopen ");
      eputs (file_name);
      eputs (" ");
      eputs (opentype);
      eputs ("\n");
    }

  int fd;
  int mode = 0600;
  if ((opentype[0] == 'a' || !strcmp (opentype, "r+")) && !access (file_name, O_RDONLY))
    {
      int flags = O_RDWR;
      if (opentype[0] == 'a')
        flags |= O_APPEND;
      fd = _open3 (file_name, flags, mode);
    }
  else if (opentype[0] == 'w' || opentype[0] == 'a' || !strcmp (opentype, "r+"))
    {
      char *plus_p = strchr (opentype, '+');
      int flags = plus_p ? O_RDWR | O_CREAT : O_WRONLY | O_CREAT | O_TRUNC;
      fd = _open3 (file_name, flags, mode);
    }
  else
    fd = _open3 (file_name, O_RDONLY, 0);

  if (__mes_debug ())
    {
      eputs (" => fd=");
      eputs (itoa (fd));
      eputs ("\n");
    }

  if (!fd)
    {
      eputs (" ***MES LIB C*** fopen of stdin: signal me in band\n");
      assert (0);
    }
  if (fd < 0)
    fd = 0;
  return (FILE *) (long) fd;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fprintf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>

int
fprintf (FILE * stream, char const *format, ...)
{
  va_list ap;
  va_start (ap, format);
  int r = vfprintf (stream, format, ap);
  va_end (ap);
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fread.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int
__fungetc_p (FILE * stream)
{
  return __ungetc_p ((int) (long) stream);
}

size_t
fread (void *data, size_t size, size_t count, FILE * stream)
{
  if (!size || !count)
    return 0;

  size_t todo = size * count;
  char *buf = (char *) data;

  int bytes = 0;
  while (__fungetc_p (stream) && todo-- && ++bytes)
    *buf++ = fgetc (stream);
  int filedes = (long) stream;
  if (todo)
    {
      int r = read (filedes, buf, todo);
      if (r < 0 && !bytes)
        bytes = r;
      else
        bytes += r;
    }

  if (__mes_debug ())
    {
      static char debug_buf[4096];
      eputs ("fread fd=");
      eputs (itoa (filedes));
      eputs (" bytes=");
      eputs (itoa (bytes));
      eputs ("\n");
      if (bytes > 0 && bytes < sizeof (debug_buf))
        {
          strncpy (debug_buf, data, bytes);
          buf[bytes] = 0;
          eputs ("fread buf=");
          eputs (debug_buf);
          eputs ("\n");
        }
    }

  if (bytes > 0)
    return bytes / size;

  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fseek.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdio.h>
#include <unistd.h>

int
fseek (FILE * stream, long offset, int whence)
{
  int filedes = (long) stream;
  off_t pos = lseek (filedes, offset, whence);
  if (__mes_debug ())
    {
      eputs ("fread fd=");
      eputs (itoa (filedes));
      eputs ("  =>");
      eputs (itoa (pos));
      eputs ("\n");
    }
  if (pos >= 0)
    return 0;
  return -1;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/ftell.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

long
ftell (FILE * stream)
{
  return lseek ((int) (long) stream, 0, SEEK_CUR);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fwrite.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdio.h>
#include <unistd.h>

size_t
fwrite (void const *data, size_t size, size_t count, FILE * stream)
{
  if (__mes_debug () > 1)
    {
      eputs ("fwrite ");
      eputs (itoa ((int) (long) stream));
      eputs ("  ");
      eputs (itoa (size));
      eputs ("\n");
    }

  if (!size || !count)
    return 0;
  int filedes = (long) stream;
  int bytes = write (filedes, data, size * count);

  if (__mes_debug () > 2)
    {
      eputs (" => ");
      eputs (itoa (bytes));
      eputs ("\n");
    }

  if (bytes > 0)
    return bytes / size;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/printf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>

int
printf (char const *format, ...)
{
  va_list ap;
  int r;
#if __GNUC__ && __x86_64__ && !SYSTEM_LIBC
#define __FUNCTION_ARGS 1
  ap += (__FOO_VARARGS + (__FUNCTION_ARGS << 1)) << 3;
#undef __FUNCTION_ARGS
#endif
  va_start (ap, format);
  r = vprintf (format, ap);
  va_end (ap);
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/remove.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

int
remove (char const *file_name)
{
  struct stat buf;
  if (stat (file_name, &buf) < 0)
    return -1;
  if (S_ISDIR (buf.st_mode))
    return rmdir (file_name);
  return unlink (file_name);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/snprintf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>

int
snprintf (char *str, size_t size, char const *format, ...)
{
  va_list ap;
  int r;
#if __GNUC__ && __x86_64__ && !SYSTEM_LIBC
#define __FUNCTION_ARGS 3
  ap += (__FOO_VARARGS + (__FUNCTION_ARGS << 1)) << 3;
#undef __FUNCTION_ARGS
#endif
  va_start (ap, format);
  r = vsnprintf (str, size, format, ap);
  va_end (ap);
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/sprintf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>

int
sprintf (char *str, char const *format, ...)
{
  va_list ap;
  int r;
#if __GNUC__ && __x86_64__ && !SYSTEM_LIBC
#define __FUNCTION_ARGS 2
  ap += (__FOO_VARARGS + (__FUNCTION_ARGS << 1)) << 3;
#undef __FUNCTION_ARGS
#endif
  va_start (ap, format);
  r = vsprintf (str, format, ap);
  va_end (ap);
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/sscanf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>

int
sscanf (char const *str, char const *template, ...)
{
  va_list ap;
  va_start (ap, template);
  int r = vsscanf (str, template, ap);
  va_end (ap);
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vfprintf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

int
vfprintf (FILE * f, char const *format, va_list ap)
{
  int fd = (long) f;
  char const *p = format;
  int count = 0;
  while (*p)
    if (*p != '%')
      {
        count++;
        fputc (*p++, f);
      }
    else
      {
        p++;
        char c = *p;
        int left_p = 0;
        int precision = -1;
        int width = -1;
        if (c == '-')
          {
            left_p = 1;
            c = *++p;
          }
        char pad = ' ';
        if (c == ' ')
          {
            pad = c;
            c = *p++;
          }
        if (c == '0')
          {
            pad = c;
            c = *p++;
          }
        if (c >= '0' && c <= '9')
          {
            width = abtol (&p, 10);
            c = *p;
          }
        else if (c == '*')
          {
            width = va_arg (ap, int);
            c = *++p;
          }
        if (c == '.')
          {
            c = *++p;
            if (c >= '0' && c <= '9')
              {
                precision = abtol (&p, 10);
                c = *p;
              }
            else if (c == '*')
              {
                precision = va_arg (ap, int);
                c = *++p;
              }
          }
        if (c == 'l')
          c = *++p;
        if (c == 'l')
          {
            eputs ("vfprintf: skipping second: l\n");
            c = *++p;
          }
        switch (c)
          {
          case '%':
            {
              fputc (*p, f);
              count++;
              break;
            }
          case 'c':
            {
              char _c;
              _c = va_arg (ap, long);
              fputc (_c, f);
              break;
            }
          case 'd':
          case 'i':
          case 'o':
          case 'u':
          case 'x':
          case 'X':
            {
              long d = va_arg (ap, long);
              int base = c == 'o' ? 8 : c == 'x' || c == 'X' ? 16 : 10;
              char *s = ntoab (d, base, c != 'u' && c != 'x' && c != 'X');
              if (c == 'X')
                strupr (s);
              int length = strlen (s);
              if (precision == -1)
                precision = length;
              if (!left_p)
                {
                  while (width-- > precision)
                    {
                      fputc (pad, f);
                      count++;
                    }
                  while (precision > length)
                    {
                      fputc ('0', f);
                      precision--;
                      width--;
                      count++;
                    }
                }
              while (*s)
                {
                  if (precision-- <= 0)
                    break;
                  width--;
                  fputc (*s++, f);
                  count++;
                }
              while (width > 0)
                {
                  width--;
                  fputc (pad, f);
                  count++;
                }
              break;
            }
          case 's':
            {
              char *s = va_arg (ap, char *);
              int length = strlen (s);
              if (precision == -1)
                precision = length;
              if (!left_p)
                {
                  while (width-- > precision)
                    {
                      fputc (pad, f);
                      count++;
                    }
                  while (precision > length)
                    {
                      fputc (' ', f);
                      precision--;
                      width--;
                      count++;
                    }
                }
              while (*s)
                {
                  if (precision-- <= 0)
                    break;
                  width--;
                  fputc (*s++, f);
                  count++;
                }
              while (width > 0)
                {
                  width--;
                  fputc (pad, f);
                  count++;
                }
              break;
            }
          case 'f':
          case 'e':
          case 'E':
          case 'g':
          case 'G':
            {
              double d = va_arg8 (ap, double);
              char *s = dtoab (d, 10, 1);
              if (c == 'E' || c == 'G')
                strupr (s);
              int length = strlen (s);
              if (precision == -1)
                precision = length;
              if (!left_p)
                {
                  while (width-- > precision)
                    {
                      fputc (pad, f);
                      count++;
                    }
                  while (precision > length)
                    {
                      fputc (' ', f);
                      precision--;
                      width--;
                      count++;
                    }
                }
              while (*s)
                {
                  if (precision-- <= 0)
                    break;
                  width--;
                  fputc (*s++, f);
                  count++;
                }
              while (width > 0)
                {
                  width--;
                  fputc (pad, f);
                  count++;
                }
              break;
            }
          case 'n':
            {
              int *n = va_arg (ap, int *);
              *n = count;
              break;
            }
          default:
            {
              eputs ("vfprintf: not supported: %:");
              eputc (c);
              eputs ("\n");
              p++;
            }
          }
        p++;
      }
  va_end (ap);
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vprintf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>

int
vprintf (char const *format, va_list ap)
{
  return vfprintf (stdout, format, ap);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vsnprintf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <ctype.h>
#include <stdarg.h>
#include <string.h>

int
vsnprintf (char *str, size_t size, char const *format, va_list ap)
{
  char const *p = format;
  int count = 0;
  char c;
  while (*p)
    if (*p != '%')
      {
        c = *p++;
        if (count < size)
          *str++ = c;
        count++;
      }
    else
      {
        p++;
        c = *p;
        int left_p = 0;
        int precision = -1;
        int width = -1;
        if (c == '-')
          {
            left_p = 1;
            c = *++p;
          }
        char pad = ' ';
        if (c == ' ')
          {
            pad = c;
            c = *p++;
          }
        if (c == '0')
          {
            pad = c;
            c = *p++;
          }
        if (c >= '0' && c <= '9')
          {
            width = abtol (&p, 10);
            c = *p;
          }
        else if (c == '*')
          {
            width = va_arg (ap, long);
            c = *++p;
          }
        if (c == '.')
          {
            c = *++p;
            if (c >= '0' && c <= '9')
              {
                precision = abtol (&p, 10);
                c = *p;
              }
            else if (c == '*')
              {
                precision = va_arg (ap, long);
                c = *++p;
              }
          }
        if (c == 'l')
          c = *++p;
        if (c == 'l')
          c = *++p;
        if (c == 'l')
          {
            eputs ("vsnprintf: skipping second: l\n");
            c = *++p;
          }
        switch (c)
          {
          case '%':
            {
              if (count < size)
                *str++ = *p;
              count++;
              break;
            }
          case 'c':
            {
              c = va_arg (ap, long);
              if (count < size)
                *str++ = c;
              count++;
              break;
            }
          case 'd':
          case 'i':
          case 'o':
          case 'u':
          case 'x':
          case 'X':
            {
              long d = va_arg (ap, long);
              int base = c == 'o' ? 8 : c == 'x' || c == 'X' ? 16 : 10;
              char *s = ntoab (d, base, c != 'u' && c != 'x' && c != 'X');
              if (c == 'X')
                strupr (s);
              int length = strlen (s);
              if (precision == -1)
                precision = length;
              if (!left_p)
                {
                  while (width-- > precision)
                    {
                      if (count < size)
                        *str++ = pad;
                      count++;
                    }
                  while (precision > length)
                    {
                      if (count < size)
                        *str++ = '0';
                      precision--;
                      width--;
                      count++;
                    }
                }
              while (*s)
                {
                  if (precision-- <= 0)
                    break;
                  width--;
                  c = *s++;
                  if (count < size)
                    *str++ = c;
                  count++;
                }
              while (width > 0)
                {
                  width--;
                  if (count < size)
                    *str++ = pad;
                  count++;
                }
              break;
            }
          case 's':
            {
              char *s = va_arg (ap, char *);
              int length = s ? strlen (s) : 0;
              if (precision == -1)
                precision = length;
              if (!left_p)
                {
                  while (width-- > precision)
                    {
                      if (count < size)
                        *str++ = pad;
                      count++;
                    }
                  while (width > length)
                    {
                      if (count < size)
                        *str++ = ' ';
                      precision--;
                      width--;
                      count++;
                    }
                }
              while (s && *s)
                {
                  if (precision-- <= 0)
                    break;
                  width--;
                  c = *s++;
                  if (count < size)
                    *str++ = c;
                  count++;
                }
              while (width > 0)
                {
                  width--;
                  if (count < size)
                    *str++ = pad;
                  count++;
                }
              break;
            }
          case 'f':
          case 'e':
          case 'E':
          case 'g':
          case 'G':
            {
              double d = va_arg8 (ap, double);
              char *s = dtoab (d, 10, 1);
              if (c == 'E' || c == 'G')
                strupr (s);
              int length = strlen (s);
              if (precision == -1)
                precision = length;
              if (!left_p)
                {
                  while (width-- > precision)
                    {
                      if (count < size)
                        *str++ = pad;
                      count++;
                    }
                  while (precision > length)
                    {
                      if (count < size)
                        *str++ = ' ';
                      precision--;
                      width--;
                      count++;
                    }
                }
              while (*s)
                {
                  if (precision-- <= 0)
                    break;
                  width--;
                  c = *s++;
                  if (count < size)
                    *str++ = c;
                  count++;
                }
              while (width > 0)
                {
                  width--;
                  if (count < size)
                    *str++ = pad;
                  count++;
                }
              break;
            }
          case 'n':
            {
              int *n = va_arg (ap, int *);
              *n = count;
              break;
            }
          default:
            {
              eputs ("vsnprintf: not supported: %:");
              eputc (c);
              eputs ("\n");
              p++;
            }
          }
        p++;
      }
  va_end (ap);
  if (count < size)
    *str = 0;
  return count;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vsprintf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <limits.h>
#include <stdarg.h>

int
vsprintf (char *str, char const *format, va_list ap)
{
  return vsnprintf (str, LONG_MAX, format, ap);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vsscanf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
vsscanf (char const *s, char const *template, va_list ap)
{
  char *p = (char *) s;
  char const *t = template;
  int count = 0;
  while (*t && *p)
    if (*t != '%')
      {
        t++;
        p++;
      }
    else
      {
        t++;
        char c = *t;
        if (c == 'l')
          c = *++t;
        switch (c)
          {
          case '%':
            {
              p++;
              break;
            }
          case 'c':
            {
              char *c = va_arg (ap, char *);
              *c = *p++;
              count++;
              break;
            }
          case 'd':
          case 'i':
          case 'u':
            {
              int *d = va_arg (ap, int *);
              *d = abtol ((char const **)&p, 10);
              count++;
              break;
            }
          case 'e':
          case 'f':
          case 'g':
          case 'E':
          case 'G':
            {
              float *f = va_arg (ap, float *);
              *f = strtod (p, &p);
              count++;
              break;
            }
          default:
            {
              eputs ("vsscanf: not supported: %:");
              eputc (c);
              eputs ("\n");
              t++;
              p++;
            }
          }
        t++;
      }
  va_end (ap);
  return count;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/calloc.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <string.h>

void *
calloc (size_t nmemb, size_t size)
{
  size_t count = nmemb * size;
  void *p = malloc (count);
  memset (p, 0, count);
  return p;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/qsort.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2021 Paul Dersey <pdersey@gmail.com>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <string.h>

void
qswap (void *a, void *b, size_t size)
{
  char *pa = a;
  char *pb = b;
  do
  {
    char tmp = *pa;
    *pa++ = *pb;
    *pb++ = tmp;
  } while (--size > 0);
}

size_t
qpart (void *base, size_t count, size_t size, int (*compare) (void const *, void const *))
{
  void *p = base + count * size;
  size_t i = 0;
  for (size_t j = 0; j < count; j++)
    {
      int c = compare (base + j * size, p);
      if (c < 0)
        {
#if 1                           //__x86_64__
          qswap (base + i * size, base + j * size, size);
#else
          int p1 = base + i * size;
          int p2 = base + j * size;
          qswap (p1, p2, size);
#endif
          i++;
        }
      else if (c == 0)
        i++;
    }
  if (compare (base + count * size, base + i * size) < 0)
    qswap (base + i * size, base + count * size, size);
  return i;
}

void
qsort (void *base, size_t count, size_t size, int (*compare) (void const *, void const *))
{
  if (count > 1)
    {
      int p = qpart (base, count - 1, size, compare);
      qsort (base, p, size, compare);
#if 1                           //__x86_64__
      qsort (base + p * size, count - p, size, compare);
#else
      int p1 = base + p * size;
      int p2 = count - p;
      qsort (p1, p2, size, compare);
#endif
    }
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtod.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdlib.h>
#include <string.h>

double
strtod (char const *string, char **tailptr)
{
  int base = 10;
  if (!strncmp (string, "0x", 2))
    {
      string += 2;
      base = 16;
    }
  if (tailptr)
    {
      *tailptr = (char *) string;
      return abtod ((char const **) tailptr, base);
    }
  char **p = (char **) &string;
  return abtod ((char const **) p, base);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtof.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>

float
strtof (char const *string, char **tailptr)
{
  return strtod (string, tailptr);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtol.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdlib.h>
#include <string.h>

long
strtol (char const *string, char **tailptr, int base)
{
  if (!strncmp (string, "0x", 2))
    {
      string += 2;
      base = 16;
    }
  if (tailptr)
    {
      *tailptr = (char *) string;
      return abtol ((char const **) tailptr, base);
    }
  char **p = (char **) &string;
  return abtol ((char const **) p, base);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtold.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>

long double
strtold (char const *string, char **tailptr)
{
  return strtod (string, tailptr);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtoll.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>

long long int
strtoll (char const *string, char **tailptr, int base)
{
  return strtol (string, tailptr, base);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtoul.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>

unsigned long
strtoul (char const *string, char **tailptr, int base)
{
  return strtol (string, tailptr, base);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/strtoull.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>

unsigned long long
strtoull (char const *string, char **tailptr, int base)
{
  return strtol (string, tailptr, base);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/memmem.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 1997--2015,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright (C) 1997--2015,2018 Han-Wen Nienhuys <hanwen@xs4all.nl>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

/** locate a substring. #memmem# finds the first occurrence of
    #needle# in #haystack#.  This is not ANSI-C.

    The prototype is not in accordance with the Linux Programmer's
    Manual v1.15, but it is with /usr/include/string.h   */

unsigned char *
_memmem (unsigned char const *haystack, int haystack_len, unsigned char const *needle, int needle_len)
{
  unsigned char const *end_haystack = haystack + haystack_len - needle_len + 1;
  unsigned char const *end_needle = needle + needle_len;

  /* Ahhh ... Some minimal lowlevel stuff. This *is* nice; Varation
     is the spice of life */
  while (haystack < end_haystack)
    {
      unsigned char const *subneedle = needle;
      unsigned char const *subhaystack = haystack;
      while (subneedle < end_needle)
        if (*subneedle++ != *subhaystack++)
          goto next;

      /* Completed the needle.  Gotcha.  */
      return (unsigned char *) haystack;
    next:
      haystack++;
    }
  return 0;
}

void *
memmem (void const *haystack, int haystack_len, void const *needle, int needle_len)
{
  unsigned char const *haystack_byte_c = (unsigned char const *) haystack;
  unsigned char const *needle_byte_c = (unsigned char const *) needle;
  return _memmem (haystack_byte_c, haystack_len, needle_byte_c, needle_len);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcat.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

char *
strcat (char *to, char const *from)
{
  char *p = strchr (to, '\0');
  while (*from)
    *p++ = *from++;
  *p = 0;
  return to;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strchr.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

char *
strchr (char const *s, int c)
{
  char const *p = s;
  while (*p || !c)
    {
      if (c == *p)
        return (char *) p;
      p++;
    }
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strlwr.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>
#include <string.h>

char *
strlwr (char *string)
{
  char *p = string;
  while (*p)
    {
      *p = tolower (*p);
      p++;
    }
  return string;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strncpy.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

char *
strncpy (char *to, char const *from, size_t size)
{
  if (size == 0)
    return to;
  char *p = to;
  while (*from && size--)
    *p++ = *from++;
  if (*from)
    size++;
  while (size--)
    *p++ = 0;
  return to;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strrchr.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

char *
strrchr (char const *s, int c)
{
  int n = strlen (s);
  if (!n)
    return 0;
  char const *p = s + n;
  if (!*p && !c)
    return (char *) p;
  p--;
  while (n-- && (*p || !c))
    {
      if (c == *p)
        return (char *) p;
      p--;
    }
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strstr.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>

char *
strstr (char const *haystack, char const *needle)
{
  return memmem (haystack, strlen (haystack), needle, strlen (needle));
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/mman.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_MMAN_H
#define __MES_SYS_MMAN_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_MMAN_H
#include_next <sys/mman.h>
#else // ! SYSTEM_LIBC

#ifndef __MES_SIZE_T
#define __MES_SIZE_T
typedef unsigned long size_t;
#endif

#define PROT_NONE 0
#define PROT_READ 1
#define PROT_WRITE 2
#define PROT_EXEC 4

int mprotect (void *addr, size_t len, int prot);

#endif // ! SYSTEM_LIBC

#endif // __MES_SYS_MMAN_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strupr.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>
#include <string.h>

char *
strupr (char *string)
{
  char *p = string;
  while (*p)
    {
      *p = toupper (*p);
      p++;
    }
  return string;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigaction.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <signal.h>

int
sigaction (int signum, struct sigaction const *act, struct sigaction *oldact)
{
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/ldexp.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <math.h>

double
ldexp (double value, int exponent)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("ldexp stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/math.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_MATH_H
#define __MES_MATH_H 1

#if SYSTEM_LIBC
#undef __MES_MATH_H
#include_next <math.h>
#else // ! SYSTEM_LIBC

double atan2 (double y, double x);
double ceil (double x);
double cos (double x);
double exp (double x);
double fabs (double number);
double floor (double x);
double ldexp (double value, int exponent);
double log (double x);
double modf (double value, double *integer_part);
double pow (double base, double power);
double sin (double x);
double sqrt (double x);

#endif // ! SYSTEM_LIBC

#endif // __MES_MATH_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/mprotect.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <string.h>

int
mprotect (void *addr, size_t len, int prot)
{
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/localtime.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>

struct tm *
localtime (time_t const *timep)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("localtime stub\n");
  stub = 1;
  errno = 0;
  static struct tm zero = { 0 };
  return &zero;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigemptyset.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <signal.h>

int
sigemptyset (sigset_t * set)
{
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/x86-mes-mescc/setjmp.c?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/x86-mes-mescc/setjmp.c used in: Step 282
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <setjmp.h>
#include <stdlib.h>

void
longjmp (jmp_buf env, int val)
{
  val = val == 0 ? 1 : val;
  ///asm ("mov____0x8(%ebp),%eax !0x0c"); // val
  asm ("mov____0x8(%ebp),%ebp !0x08");  // env*

  asm ("mov____0x8(%ebp),%ebx !0x4");   // env.__pc
  asm ("mov____0x8(%ebp),%esp !0x8");   // env.__sp
  asm ("mov____0x8(%ebp),%ebp !0x0");   // env.__bp
  asm ("jmp____*%ebx");
  // not reached
  exit (42);
}

int
setjmp (__jmp_buf * env)
{
  long *p = (long *) &env;
  env[0].__bp = p[-2];
  env[0].__pc = p[-1];
  env[0].__sp = (long) &env;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/include/setjmp.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2020 Danny Milosavljevic <dannym@scratchpost.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SETJMP_H
#define __MES_SETJMP_H 1

#if SYSTEM_LIBC
#undef __MES_SETJMP_H
#include_next <setjmp.h>
#else // ! SYSTEM_LIBC

#if __arm__
#if __GNUC__ || __TINYC__
#warning "It is not supported to use mes' setjmp implementation together with GCC.  Continuing with best-effort implementation."
typedef struct
{
  long __sp;
  long __lr;
  long __registers[8]; /* Note: Keep in sync with lib/arm-mes-gcc/setjmp.c */
} __jmp_buf;
#else
typedef struct
{
  long __fp;
  long __lr;
  long __sp;
} __jmp_buf;
#endif
#else
typedef struct
{
  long __bp;
  long __pc;
  long __sp;
} __jmp_buf;
#endif
typedef __jmp_buf jmp_buf[1];

void longjmp (jmp_buf env, int val);
int setjmp (jmp_buf env);

#endif // ! SYSTEM_LIBC

#endif // __MES_SETJMP_H

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/close.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <mes/lib.h>

int
close (int filedes)
{
  __ungetc_clear (filedes);
  __buffered_read_clear (filedes);
  return _sys_call1 (SYS_close, (int) filedes);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/rmdir.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>

int
rmdir (char const *file_name)
{
  return _sys_call1 (SYS_rmdir, (long) file_name);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/stat.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>

int
stat (char const *file_name, struct stat *statbuf)
{
  return _sys_call2 (SYS_stat, (long) file_name, (long) statbuf);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/x86-mes/crt1.s?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/x86-mes/crt1.s used in: Step 298

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes/elf32-footer-single-main.hex2?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes/elf32-footer-single-main.hex2 used in: Step 299
### Copyright (C) 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
### This file is part of stage0.
###
### stage0 is free software: you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation, either version 3 of the License, or
### (at your option) any later version.
###
### stage0 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 stage0.  If not, see <http://www.gnu.org/licenses/>.

### stage0's hex2 format for x86
###    !<label>          1 byte relative
###    $<label>          2 byte address
###    @<label>          2 byte relative
###    &<label>          4 byte address
###    %<label>          4 byte relative
###    local_<label>     function-local
###    string_<index>    string #<index>

### elf32-footer-single-main.hex2: 32 bit elf footer in hex2 for single main

# @230

:ELF_str
00                             # 0
:ELF_str__start
5f 73 74 61 72 74 00           # _start
:ELF_str__main
6d 61 69 6e 00                 # main

00 00 00

# @240
:ELF_sym
00 00 00 00                    # st-name
00 00 00 00                    # st-offset: &_start - BaseAddress
00 00 00 00                    # st-len   : &main - _start
00                             # st-info = stt-func= 2
00                             # st-other
01 00                          # st-shndx: 1

# _start
%ELF_str__start>ELF_str        # st-name
&_start
10 00 00 00                    # st-len   : &main - _start
02                             # st-info = stt-func= 2
00                             # st-other
01 00                          # st-shndx: 1

# main
%ELF_str__main>ELF_str         # st-name
&main
10 00 00 00                    # st-len   : &ELF_data - main
02                             # st-info = stt-func= 2
00                             # st-other
01 00                          # st-shndx: 1

:ELF_end

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes/elf32-header.hex2?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/lib/linux/x86-mes/elf32-header.hex2 used in: Step 300
### Copyright (C) 2016 Jeremiah Orians
### Copyright (C) 2017,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
###
### This file is part of GNU Mes.
###
### GNU Mes is free software; you can redistribute it and/or modify it
### under the terms of the GNU General Public License as published by
### the Free Software Foundation; either version 3 of the License, or (at
### your option) any later version.
###
### GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.

### Commentary:

# elf32-header.hex2: 32 bit elf header in hex2, with text segment, data
# segment and symbol tables.

# stage0's hex2 format for x86
#    !<label>          1 byte relative
#    $<label>          2 byte address
#    @<label>          2 byte relative
#    &<label>          4 byte address
#    %<label>          4 byte relative
#    local_<label>     function-local
#    string_<index>    string #<index>

### Code:

:ELF_base
7F 45 4C 46                    # e_ident[EI_MAG0-3] ELF's magic number

01                             # e_ident[EI_CLASS] Indicating 32 bit
01                             # e_ident[EI_DATA] Indicating little endianness
01                             # e_ident[EI_VERSION] Indicating original elf

00                             # e_ident[EI_OSABI] Set at 0 because none cares
00                             # e_ident[EI_ABIVERSION] See above

00 00 00 00 00 00 00           # e_ident[EI_PAD]

02 00                          # e_type Indicating Executable
03 00                          # e_machine Indicating 32bit x86
01 00 00 00                    # e_version Indicating original elf

&ELF_text                      # e_entry Address of the entry point
%ELF_program_headers>ELF_base  # e_phoff Address of program header table
%ELF_section_headers>ELF_base  # e_shoff Address of section header table

00 00 00 00                    # e_flags

34 00                          # e_ehsize Indicating our 52 Byte header

20 00                          # e_phentsize size of a program header table
01 00                          # e_phnum number of entries in program table

28 00                          # e_shentsize size of a section header table
07 00                          # e_shnum number of entries in section table

04 00                          # e_shstrndx index of the section names

# @34
00 00 00 00
00 00 00 00
00 00 00 00

# @40
:ELF_program_headers
:ELF_program_header__text
01 00 00 00                    # ph_type: PT-LOAD = 1
00 00 00 00                    # ph_offset
&ELF_base                      # ph_vaddr
&ELF_base                      # ph_physaddr
%ELF_end>ELF_base              # ph_filesz
%ELF_end>ELF_base              # ph_memsz
07 00 00 00                    # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                    # ph_align

# @60
#:ELF_program_header__data     # NOT USED
                               # FIXME: linux 4.17 does not allow this overlap
                               #    Uhuuh, elf segment at 0000000001000000
                               #    requested but the memory is mapped already
01 00 00 00                    # ph_type: PT-LOAD = 1
00 00 00 00                    # ph_offset
&ELF_base                      # ph_vaddr
&ELF_base                      # ph_physaddr
%ELF_end>ELF_base              # ph_filesz
%ELF_end>ELF_base              # ph_memsz
07 00 00 00                    # ph_flags: PF-X|PF-W|PF-R = 7
01 00 00 00                    # ph_align

# @80
:ELF_comment
4d 45 53 00                     # MES
00 00 00 00                     # align
00 00 00 00
00 00 00 00

4d 45 53 00                     # MES
00 00 00 00                     # align
00 00 00 00
00 00 00 00

# @a0
:ELF_shstr
00
:ELF_shstr__text
2e 74 65 78 74 00               # .text
:ELF_shstr__data
2e 64 61 74 61 00               # .data
:ELF_shstr__comment
2e 63 6f 6d 6d 65 6e 74 00      # .comment
:ELF_shstr__shstr
2e 73 68 73 74 72 74 61 62 00   # .shstrtab
:ELF_shstr__sym
2e 73 79 6d 74 61 62 00         # .symtab
:ELF_shstr__str
2e 73 74 72 74 61 62 00         # .strtab

# @d0

:ELF_section_headers
00 00 00 00                     # sh_name
00 00 00 00                     # sh_type
00 00 00 00                     # sh_flags
00 00 00 00                     # sh_addr
00 00 00 00                     # sh_offset
00 00 00 00                     # sh_length
00 00 00 00                     # sh_link
00 00 00 00                     # sh_info
01 00 00 00                     # sh_1?
00 00 00 00                     # sh_entsize

## FIXME: M0 for calculations?
:ELF_section_header_text
%ELF_shstr__text>ELF_shstr      # sh_name
01 00 00 00                     # sh_type = SHT_PROGBITS = 1
06 00 00 00                     # sh_flags = SHF-ALLOC|SHF-EXEC =2 | 4 = 6
&ELF_text                       # sh_addr
%ELF_text>ELF_base              # sh_offset
%ELF_data>ELF_text              # sh_length
00 00 00 00                     # sh_link
00 00 00 00                     # sh_info
01 00 00 00                     # sh_1?
00 00 00 00                     # sh_entsize

:ELF_section_header_data
%ELF_shstr__data>ELF_shstr     # sh_name
01 00 00 00                    # sh_type = SHT_PROGBITS = 1
03 00 00 00                    # sh_flags = SHF-WRITE|SHF-ALLOC = 1 | 2 = 3
&ELF_data                      # sh_addr
%ELF_data>ELF_base             # sh_offset
%ELF_sym>ELF_data              # sh_length
00 00 00 00                    # sh_link
00 00 00 00                    # sh_info
01 00 00 00                    # sh_1?
00 00 00 00                    # sh_entsize

:ELF_section_header_comment
%ELF_shstr__comment>ELF_shstr  # sh_name
01 00 00 00                    # sh_type = SHT_PROGBITS = 1
00 00 00 00                    # sh_flags
&ELF_comment                   # sh_addr
%ELF_comment>ELF_base          # sh_offset
%ELF_shstr>ELF_comment         # sh_length
00 00 00 00                    # sh_link
00 00 00 00                    # sh_info
01 00 00 00                    # sh_1?
00 00 00 00                    # sh_entsize

:ELF_section_header_shstr
%ELF_shstr__shstr>ELF_shstr    # sh_name
03 00 00 00                    # sh_type: str-sht-strtab
00 00 00 00                    # sh_flags
&ELF_shstr                     # sh_addr
%ELF_shstr>ELF_base            # sh_offset
%ELF_section_headers>ELF_shstr # sh_length
00 00 00 00                    # sh_link
00 00 00 00                    # sh_info
01 00 00 00                    # sh_1?
00 00 00 00                    # sh_entsize

:ELF_section_header_sym
%ELF_shstr__sym>ELF_shstr      # sh_name
02 00 00 00                    # sh_type: str-sht-symtab
00 00 00 00                    # sh_flags
&ELF_sym                       # sh_addr
%ELF_sym>ELF_base              # sh_offset
%ELF_end>ELF_sym               # sh_length
06 00 00 00                    # sh_link:6
00 00 00 00                    # sh_info
01 00 00 00                    # sh_1?
10 00 00 00                    # sh_entsize

:ELF_section_header_str
%ELF_shstr__str>ELF_shstr      # sh_name
03 00 00 00                    # sh_type: str-sht-strtab
00 00 00 00                    # sh_flags
&ELF_str                       # sh_addr
%ELF_str>ELF_base              # sh_offset
%ELF_sym>ELF_str               # sh_length
00 00 00 00                    # sh_link
00 00 00 00                    # sh_info
01 00 00 00                    # sh_1?
00 00 00 00                    # sh_entsize

# @1e8
00 00 00 00  # align
00 00 00 00

# @1f0

00 00 00 00  # align
00 00 00 00
00 00 00 00
00 00 00 00

# @200
:ELF_text

https://git.savannah.gnu.org/cgit/mes.git/tree/include/alloca.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/alloca.h used in: Step 301
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_ALLOCA_H
#define __MES_ALLOCA_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_ALLOCA_H
#include_next <alloca.h>

#else // ! SYSTEM_LIBC

#include <sys/types.h>

#if _ALLOCA_UNSIGNED
void *alloca (unsigned size);
#elif _ALLOCA_CHAR
char *alloca (int);
#else
void *alloca (size_t size);
#endif

#endif // ! SYSTEM_LIBC

#endif // __MES_ALLOCA_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/argz.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/argz.h used in: Step 302
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_ARGZ_H
#define __MES_ARGZ_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_ARGZ_H
#include_next <argz.h>

#else // ! SYSTEM_LIBC

#include <mes/lib-mini.h>

size_t __argz_count (char const *argz, size_t len);
void __argz_extract (char const *argz, size_t len, char **argv);
size_t __argz_extract_count (char const *argz, size_t len, char **argv);

#endif // ! SYSTEM_LIBC

#endif // __MES_ARGZ_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/ar.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/ar.h used in: Step 303
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright (C) 1996 Free Software Foundation, Inc.
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_AR_H
#define __MES_AR_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_AR_H
#include_next <ar.h>

#else // ! SYSTEM_LIBC

// Taken from GNU C Library 2.2.5

/* Archive files start with the ARMAG identifying string.  Then follows a
   `struct ar_hdr', and as many bytes of member file data as its `ar_size'
   member indicates, for each member file.  */

#define ARMAG   "!<arch>\n"     /* String that begins an archive file.  */
#define SARMAG  8               /* Size of that string.  */

#define ARFMAG  "`\n"           /* String in ar_fmag at end of each header.  */

struct ar_hdr
{
  char ar_name[16];             /* Member file name, sometimes / terminated. */
  char ar_date[12];             /* File date, decimal seconds since Epoch.  */
  char ar_uid[6], ar_gid[6];    /* User and group IDs, in ASCII decimal.  */
  char ar_mode[8];              /* File mode, in ASCII octal.  */
  char ar_size[10];             /* File size, in ASCII decimal.  */
  char ar_fmag[2];              /* Always contains ARFMAG.  */
};

#endif // ! SYSTEM_LIBC

#endif // __MES_ARGZ_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/dirent.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright (C) 1991, 1992 Free Software Foundation, Inc.
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __MES_DIRENT_H
#define __MES_DIRENT_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_DIRENT_H
#include_next <dirent.h>

#else // ! SYSTEM_LIBC

#include <dirstream.h>

// Taken from GNU C Library 1.06.4, 2.2.5

/*
 *  POSIX Standard: 5.1.2 Directory Operations  <dirent.h>
 */

#include <stddef.h>

int __getdirentries (int filedes, char *buffer, size_t nbytes, off_t * basep);

struct dirent
{
  ino_t d_ino;
  off_t d_off;
  unsigned short int d_reclen;
#if 0
  unsigned char d_type;
#endif
  char d_name[256];             /* We must not include limits.h! */
};

/* Open a directory stream on NAME.
   Return a DIR stream on the directory, or NULL if it could not be opened.  */
DIR *opendir (char const *name);

/* Close the directory stream DIRP.
   Return 0 if successful, -1 if not.  */
int closedir (DIR * dirp);

/* Read a directory entry from DIRP.
   Return a pointer to a `struct dirent' describing the entry,
   or NULL for EOF or error.  The storage returned may be overwritten
   by a later readdir call on the same DIR stream.  */
struct dirent *readdir (DIR * dirp);

/* Rewind DIRP to the beginning of the directory.  */
extern void rewinddir (DIR * dirp);

#endif // ! SYSTEM_LIBC

#endif // __MES_DIRENT_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/dirstream.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 *Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __MES_DIRSTREAM_H
#define __MES_DIRSTREAM_H 1

#if SYSTEM_LIBC && HAVE_DIRSTREAM_H
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_DIRSTREAM_H
#include_next <dirstream.h>

#else // ! SYSTEM_LIBC

#include <sys/types.h>

// Taken from GNU C Library 2.2.5

/* Directory stream type.  */
struct __dirstream
{
  int fd;                       /* File descriptor.  */

  char *data;                   /* Directory block.  */
  size_t allocation;            /* Space allocated for the block.  */
  size_t size;                  /* Total valid data in the block.  */
  size_t offset;                /* Current offset into the block.  */

  off_t filepos;                /* Position of next entry to read.  */
};

typedef struct __dirstream DIR;

#endif // ! SYSTEM_LIBC

#endif // __MES_DIRSTREAM_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/dlfcn.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/dlfcn.h used in: Step 308
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_DLFCN_H
#define __MES_DLFCN_H 1

#if SYSTEM_LIBC
#undef __MES_DLFCN_H
#include_next <dlfcn.h>

#else // ! SYSTEM_LIBC

#define RTLD_LAZY   0x00001
#define RTLD_NOW    0x00002
#define RTLD_BINDING_MASK   0x3
#define RTLD_NOLOAD 0x00004
#define RTLD_DEEPBIND   0x00008
#define RTLD_GLOBAL 0x00100
#define RTLD_LOCAL  0
#define RTLD_NODELETE   0x01000
#define RTLD_DEFAULT    0

void *dlopen (char const *filename, int flags);
int dlclose (void *handle);

#endif // ! SYSTEM_LIBC

#endif // __MES_DLFCN_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/endian.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/endian.h used in: Step 309
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_ENDIAN_H
#define __MES_ENDIAN_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_ENDIAN_H
#include_next <endian.h>

#else // ! SYSTEM_LIBC
#define __LITTLE_ENDIAN 1234
#define __BIG_ENDIAN 4321
#define __BYTE_ORDER __LITTLE_ENDIAN
#endif // ! SYSTEM_LIBC

#endif // __MES_ENDIAN_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/features.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/features.h used in: Step 312
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_FEATURES_H
#define __MES_FEATURES_H 1

#if SYSTEM_LIBC
#undef __MES_FEATURES_H
#include_next <features.h>
#endif // (SYSTEM_LIBC)

#endif // __MES_FEATURES_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/float.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/float.h used in: Step 313, Step 901
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_FLOAT_H
#define __MES_FLOAT_H 1

#if SYSTEM_LIBC
#undef __MES_FLOAT_H
#include_next <float.h>
#else // ! SYSTEM_LIBC

#define MIN_EXP -1021
#define DBL_MIN_EXP -1021
#define LDBL_MIN_EXP -1021

#endif // ! SYSTEM_LIBC

#endif // __MES_FLOAT_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/getopt.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_GETOPT_H
#define __MES_GETOPT_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_GETOPT_H
#include_next <getopt.h>

#else // ! SYSTEM_LIBC
#include <endian.h>
int isdigit (int);
int isxdigit (int);

char *optarg;
int optind;
int opterr;
struct option
{
  char const *name;
  int has_arg;
  int *flag;
  int val;
};

enum _argtype
{
  no_argument,
  required_argument,
  optional_argument
};

int getopt (int argc, char *const *argv, char const *options);
int getopt_long (int argc, char *const *argv, char const *options,
                 struct option const *long_options, int *opt_index);
int getopt_long_only (int argc, char *const *argv, char const *options,
                      struct option const *long_options, int *opt_index);

#endif // ! SYSTEM_LIBC

#endif // __MES_GETOPT_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/grp.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_GRP_H
#define __MES_GRP_H 1

#if SYSTEM_LIBC
#undef __MES_GRP_H
#include_next <grp.h>
#else // ! SYSTEM_LIBC

#include <sys/types.h>

struct group
{
  char *gr_name;
  gid_t gr_gid;
  char **gr_mem;
};

struct group *getgrent (void);
void endgrent (void);
void setgrent (void);
struct group *getgrgid (gid_t gid);
struct group *getgrnam (char const *name);

#endif // ! SYSTEM_LIBC

#endif // __MES_GRP_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/inttypes.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/inttypes.h used in: Step 316, Step 855, Step 867, Step 873, Step 875, Step 877, Step 883, Step 897, Step 899, Step 901, Step 907, Step 919, Step 923, Step 925, Step 929, Step 933, Step 939, Step 1005, Step 1006, Step 1007, Step 1009, Step 1012, Step 1136
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_INTTYPES_H
#define __MES_INTTYPES_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_INTTYPES_H
#include_next <inttypes.h>
#else // ! SYSTEM_LIBC

#include <stdint.h>
#endif // ! SYSTEM_LIBC

#endif // __MES_INTTYPES_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/libgen.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/libgen.h used in: Step 317
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_LIBGEN_H
#define __MES_LIBGEN_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_LIBGEN_H
#include_next <libgen.h>

#else // ! SYSTEM_LIBC
char *dirname (char *);
#endif // ! SYSTEM_LIBC

#endif // __MES_LIBGEN_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/locale.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_LOCALE_H
#define __MES_LOCALE_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_LOCALE_H
#include_next <locale.h>

#else // ! SYSTEM_LIBC

// *INDENT-OFF*
#ifndef LC_ALL
#define LC_CTYPE   0
#define LC_NUMERIC 1
#define LC_COLLATE 3
#define LC_ALL     6
#endif
// *INDENT-ON*

char *setlocale (int category, char const *locale);

#endif // ! SYSTEM_LIBC

#endif // __MES_LOCALE_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/memory.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/memory.h used in: Step 321, Step 600, Step 602, Step 606
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_MEMORY_H
#define __MES_MEMORY_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_MEMORY_H
#include_next <memory.h>

#else // ! SYSTEM_LIBC

#include <string.h>

#endif // ! SYSTEM_LIBC

#endif // __MES_MEMORY_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/pwd.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_PWD_H
#define __MES_PWD_H 1

#if SYSTEM_LIBC
#undef __MES_PWD_H
#include_next <pwd.h>
#else // ! SYSTEM_LIBC

#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;
};

struct passwd *getpwuid ();


#endif // ! SYSTEM_LIBC

#endif // __MES_PWD_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdbool.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/stdbool.h used in: Step 326, Step 566, Step 569, Step 570, Step 574, Step 575
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_STDBOOL_H
#define __MES_STDBOOL_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_STDBOOL_H
#include_next <stdbool.h>

#else // ! SYSTEM_LIBC
typedef int bool;
#define false 0
#define true 1
#endif // ! SYSTEM_LIBC

#endif // __MES_STDBOOL_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/stdnoreturn.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/stdnoreturn.h used in: Step 331
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_STDNORETURN_H
#define __MES_STDNORETURN_H 1

#if SYSTEM_LIBC
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#undef __MES_STDNORETURN_H
#include_next <stdnoreturn.h>

#else // ! SYSTEM_LIBC
// whut?
#endif // ! SYSTEM_LIBC

#endif // __MES_STDNORETURN_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/strings.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/strings.h used in: Step 333, Step 600, Step 602, Step 606
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_STRINGS_H
#define __MES_STRINGS_H 1

#if SYSTEM_LIBC
#undef __MES_STRINGS_H
#include_next <strings.h>
#endif // (SYSTEM_LIBC)

#endif // __MES_STRINGS_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/cdefs.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/sys/cdefs.h used in: Step 349
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_CDEFS_H
#define __MES_SYS_CDEFS_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_CDEFS_H
#include_next <sys/cdefs.h>
#endif // (SYSTEM_LIBC)

#endif // __MES_SYS_CDEFS_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/dir.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/sys/dir.h used in: Step 350
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_DIR_H
#define __MES_SYS_DIR_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_DIR_H
#include_next <sys/dir.h>

#else // ! SYSTEM_LIBC


#endif // ! SYSTEM_LIBC

#endif // __MES_SYS_DIR_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/file.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/sys/file.h used in: Step 351, Step 518, Step 530, Step 1112, Step 1126, Step 1189, Step 1202, Step 1207, Step 1225, Step 1241
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_FILE_H
#define __MES_SYS_FILE_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_FILE_H
#include_next <sys/file.h>

#else // ! SYSTEM_LIBC


#endif // ! SYSTEM_LIBC

#endif // __MES_SYS_FILE_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/param.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/sys/param.h used in: Step 354, Step 992, Step 1124, Step 1151, Step 1154, Step 1171, Step 1172, Step 1181, Step 1206, Step 1220
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_PARAM_H
#define __MES_SYS_PARAM_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_PARAM_H
#include_next <sys/param.h>

#else // ! SYSTEM_LIBC

#endif // ! SYSTEM_LIBC

#endif // __MES_SYS_PARAM_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/select.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/sys/select.h used in: Step 356
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_SELECT_H
#define __MES_SYS_SELECT_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_SELECT_H
#include_next <sys/select.h>
#else //! SYSTEM_LIBC
typedef int fd_set;
#endif //! SYSTEM_LIBC

#endif // __MES_SYS_SELECT_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/timeb.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/sys/timeb.h used in: Step 358
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_TIMEB_H
#define __MES_SYS_TIMEB_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_TIMEB_H
#include_next <sys/timeb.h>
#endif // (SYSTEM_LIBC)

#endif // __MES_SYS_TIMEB_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/times.h?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_TIMES_H
#define __MES_SYS_TIMES_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_TIMES_H
#include_next <sys/times.h>

#else // ! SYSTEM_LIBC

#ifndef __MES_CLOCK_T
#define __MES_CLOCK_T
#undef clock_t
typedef long clock_t;
#endif

#ifndef CLOCKS_PER_SEC
#define CLOCKS_PER_SEC 1000000
#endif

#ifndef HZ
#define HZ 100
#endif

struct tms
{
  clock_t tms_utime;
  clock_t tms_stime;
  clock_t tms_cutime;
  clock_t tms_cstime;
};

#endif // ! SYSTEM_LIBC

#endif // __MES_SYS_TIMES_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/ucontext.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/sys/ucontext.h used in: Step 362
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_UCONTEXT_H
#define __MES_SYS_UCONTEXT_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_UCONTEXT_H
#include_next <sys/ucontext.h>
#endif // (SYSTEM_LIBC)

#endif // __MES_SYS_UCONTEXT_H

https://git.savannah.gnu.org/cgit/mes.git/tree/include/sys/user.h?h=v0.24.2

File: /sysa/mes-0.24.2/build/mes-0.24.2/include/sys/user.h used in: Step 363
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef __MES_SYS_USER_H
#define __MES_SYS_USER_H 1

#if SYSTEM_LIBC
#undef __MES_SYS_USER_H
#include_next <sys/user.h>

#else // ! SYSTEM_LIBC

/* These are the 32-bit x86 structures.  */
struct user_fpregs_struct
{
  long int cwd;
  long int swd;
  long int twd;
  long int fip;
  long int fcs;
  long int foo;
  long int fos;
  long int st_space[20];
};

struct user_fpxregs_struct
{
  unsigned short int cwd;
  unsigned short int swd;
  unsigned short int twd;
  unsigned short int fop;
  long int fip;
  long int fcs;
  long int foo;
  long int fos;
  long int mxcsr;
  long int reserved;
  long int st_space[32];        /* 8*16 bytes for each FP-reg = 128 bytes */
  long int xmm_space[32];       /* 8*16 bytes for each XMM-reg = 128 bytes */
  long int padding[56];
};

struct user_regs_struct
{
  long int ebx;
  long int ecx;
  long int edx;
  long int esi;
  long int edi;
  long int ebp;
  long int eax;
  long int xds;
  long int xes;
  long int xfs;
  long int xgs;
  long int orig_eax;
  long int eip;
  long int xcs;
  long int eflags;
  long int esp;
  long int xss;
};

// *INDENT-OFF*
struct user
{
  struct user_regs_struct    regs;
  int                        u_fpvalid;
  struct user_fpregs_struct  i387;
  unsigned long int          u_tsize;
  unsigned long int          u_dsize;
  unsigned long int          u_ssize;
  unsigned long int          start_code;
  unsigned long int          start_stack;
  long int                   signal;
  int                        reserved;
  struct user_regs_struct   *u_ar0;
  struct user_fpregs_struct *u_fpstate;
  unsigned long int          magic;
  char                       u_comm [32];
  int                        u_debugreg [8];
};

#define PAGE_SHIFT           12
#define PAGE_SIZE            (1UL << PAGE_SHIFT)
#define PAGE_MASK            (~(PAGE_SIZE-1))
#define NBPG                 PAGE_SIZE
#define UPAGES               1
#define HOST_TEXT_START_ADDR (u.start_code)
#define HOST_STACK_END_ADDR  (u.start_stack + u.u_ssize * NBPG)
// *INDENT-ON*

#endif // ! SYSTEM_LIBC

#endif // __MES_SYS_USER_H

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/mes-0.24.2/mes-0.24.2.checksums

File: /sysa/mes-0.24.2/mes-0.24.2.checksums used in: Step 366
b2a48b2724a7b791df66efc2384a405a91d26579459a6797514dec89c2e76658  /usr/bin/mes
148ce096422535832802494f1128c26b6580cf3b66635e56abb33f7b3fc26043  /usr/bin/mes-m2
17c86665c2f925d88e3c1f827f312bbc7575b9e6006ef57833e895b6e73b8087  /usr/bin/mescc.scm
4feafab424611c976dc4628aa862e37b4e92b5869aebbcf7655e684c3f589041  /usr/lib/x86-mes/crt1.s
69e9ec2ee2ebf065575cdaf878629178b85330257dde0da790a428c0280d1ecb  /usr/lib/x86-mes/crt1.o
c9944a799d584abfa76f385c14ac0caf6f46d03b34bf2712493602b12826c6b2  /usr/lib/x86-mes/x86.M1
d8646707db6aa2a76fdc5dbb3521376439e357f9f1de1d67f02a1afeefd342ac  /usr/lib/x86-mes/libmescc.s
7ec49fbcbc70b49648150b021a2ff9ab2b27e89eb8815919a7ffabe375ab1edc  /usr/lib/x86-mes/libc+tcc.s
b935d4c30213b49d899ab8bccce33117057ea0f3a1a8d3afc5e07fb8ce4590db  /usr/lib/x86-mes/libc.s
52f697278ccdff5e457f27e10f465a91ab9858f0c6cee0683831cadb3109bbb7  /usr/lib/x86-mes/libmescc.a
840ee884db456e1982f40bce82209515a8e0b09bc404c4bc2a26d237bfa4d9ca  /usr/lib/x86-mes/libc+tcc.a
32be26479096c52dbffe9c8bf14765f753d6882c3ae43760eda03406c0cf2539  /usr/lib/x86-mes/libc.a
b16ab368bc4c7b8bd896d03cba565a60e97760dea4da9f5c8a1a3d2902a79df6  /usr/lib/linux/x86-mes/elf32-header.hex2
f9873d9aab12e70f24d97f8319e17a1e698ca60779ae9a6ab3ede648cd60fc61  /usr/lib/linux/x86-mes/elf32-footer-single-main.hex2

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.26/sources

File: ../git/live-bootstrap/sysa/tcc-0.9.26/sources used in: Step 368
https://lilypond.org/janneke/tcc/tcc-0.9.26-1136-g5bba73cc.tar.gz 23cacd448cff2baf6ed76c2d1e2d654ff4e557046e311dfb6be7e1c631014ef8 tcc-0.9.26.tar.gz

https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tcctools.c

File: ../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tcctools.c used in: Step 374, Step 375, Step 378, Step 395, Step 407, Step 419, Step 431, Step 443, Step 455
/* -------------------------------------------------------------- */
/*
 *  TCC - Tiny C Compiler
 *
 *  tcctools.c - extra tools and and -m32/64 support
 *
 */

/* -------------------------------------------------------------- */
/*
 * This program is for making libtcc1.a without ar
 * tiny_libmaker - tiny elf lib maker
 * usage: tiny_libmaker [lib] files...
 * Copyright (c) 2007 Timppa
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef _TCC_H
#include "tcc.h"
#endif

//#define ARMAG  "!<arch>\n"
#define ARFMAG "`\n"

typedef struct {
    char ar_name[16];
    char ar_date[12];
    char ar_uid[6];
    char ar_gid[6];
    char ar_mode[8];
    char ar_size[10];
    char ar_fmag[2];
} ArHdr;

static unsigned long le2belong(unsigned long ul) {
    return ((ul & 0xFF0000)>>8)+((ul & 0xFF000000)>>24) +
        ((ul & 0xFF)<<24)+((ul & 0xFF00)<<8);
}

/* Returns 1 if s contains any of the chars of list, else 0 */
static int contains_any(const char *s, const char *list) {
  const char *l;
  for (; *s; s++) {
      for (l = list; *l; l++) {
          if (*s == *l)
              return 1;
      }
  }
  return 0;
}

static int ar_usage(int ret) {
    fprintf(stderr, "usage: tcc -ar [rcsv] lib file...\n");
    fprintf(stderr, "create library ([abdioptxN] not supported).\n");
    return ret;
}

ST_FUNC int tcc_tool_ar(TCCState *s1, int argc, char **argv)
{
    static ArHdr arhdr = {
        "/               ",
        "            ",
        "0     ",
        "0     ",
        "0       ",
        "          ",
        ARFMAG
        };

    static ArHdr arhdro = {
        "                ",
        "            ",
        "0     ",
        "0     ",
        "0       ",
        "          ",
        ARFMAG
        };

    FILE *fi, *fh = NULL, *fo = NULL;
    ElfW(Ehdr) *ehdr;
    ElfW(Shdr) *shdr;
    ElfW(Sym) *sym;
    int i, fsize, i_lib, i_obj;
    char *buf, *shstr, *symtab = NULL, *strtab = NULL;
    int symtabsize = 0;//, strtabsize = 0;
    char *anames = NULL;
    int *afpos = NULL;
    int istrlen, strpos = 0, fpos = 0, funccnt = 0, funcmax, hofs;
    char tfile[260], stmp[20];
    char *file, *name;
    int ret = 2;
    const char *ops_conflict = "habdioptxN";  // unsupported but destructive if ignored.
    int verbose = 0;

    i_lib = 0; i_obj = 0;  // will hold the index of the lib and first obj
    for (i = 1; i < argc; i++) {
        const char *a = argv[i];
        if (*a == '-' && strstr(a, "."))
            ret = 1; // -x.y is always invalid (same as gnu ar)
        if ((*a == '-') || (i == 1 && !strstr(a, "."))) {  // options argument
            if (contains_any(a, ops_conflict))
                ret = 1;
            if (strstr(a, "v"))
                verbose = 1;
        } else {  // lib or obj files: don't abort - keep validating all args.
            if (!i_lib)  // first file is the lib
                i_lib = i;
            else if (!i_obj)  // second file is the first obj
                i_obj = i;
        }
    }

    if (!i_obj)  // i_obj implies also i_lib. we require both.
        ret = 1;

    if (ret == 1)
        return ar_usage(ret);

    if ((fh = fopen(argv[i_lib], "wb")) == NULL)
    {
        fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
        goto the_end;
    }

    sprintf(tfile, "%s.tmp", argv[i_lib]);
    if ((fo = fopen(tfile, "wb+")) == NULL)
    {
        fprintf(stderr, "tcc: ar: can't create temporary file %s\n", tfile);
        goto the_end;
    }

    funcmax = 250;
    afpos = tcc_realloc(NULL, funcmax * sizeof *afpos); // 250 func
    memcpy(&arhdro.ar_mode, "100666", 6);

    // i_obj = first input object file
    while (i_obj < argc)
    {
        if (*argv[i_obj] == '-') {  // by now, all options start with '-'
            i_obj++;
            continue;
        }
        if ((fi = fopen(argv[i_obj], "rb")) == NULL) {
            fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_obj]);
            goto the_end;
        }
        if (verbose)
            printf("a - %s\n", argv[i_obj]);

        fseek(fi, 0, SEEK_END);
        fsize = ftell(fi);
        fseek(fi, 0, SEEK_SET);
        buf = tcc_malloc(fsize + 1);
        fread(buf, fsize, 1, fi);
        fclose(fi);

        // elf header
        ehdr = (ElfW(Ehdr) *)buf;
        if (ehdr->e_ident[4] != ELFCLASSW)
        {
            fprintf(stderr, "tcc: ar: Unsupported Elf Class: %s\n", argv[i_obj]);
            goto the_end;
        }

        shdr = (ElfW(Shdr) *) (buf + ehdr->e_shoff + ehdr->e_shstrndx * ehdr->e_shentsize);
        shstr = (char *)(buf + shdr->sh_offset);
        for (i = 0; i < ehdr->e_shnum; i++)
        {
            shdr = (ElfW(Shdr) *) (buf + ehdr->e_shoff + i * ehdr->e_shentsize);
            if (!shdr->sh_offset)
                continue;
            if (shdr->sh_type == SHT_SYMTAB)
            {
                symtab = (char *)(buf + shdr->sh_offset);
                symtabsize = shdr->sh_size;
            }
            if (shdr->sh_type == SHT_STRTAB)
            {
                if (!strcmp(shstr + shdr->sh_name, ".strtab"))
                {
                    strtab = (char *)(buf + shdr->sh_offset);
                    //strtabsize = shdr->sh_size;
                }
            }
        }

        if (symtab && symtabsize)
        {
            int nsym = symtabsize / sizeof(ElfW(Sym));
            //printf("symtab: info size shndx name\n");
            for (i = 1; i < nsym; i++)
            {
                sym = (ElfW(Sym) *) (symtab + i * sizeof(ElfW(Sym)));
                if (sym->st_shndx &&
                    (sym->st_info == 0x10
                    || sym->st_info == 0x11
                    || sym->st_info == 0x12
                    )) {
                    //printf("symtab: %2Xh %4Xh %2Xh %s\n", sym->st_info, sym->st_size, sym->st_shndx, strtab + sym->st_name);
                    istrlen = strlen(strtab + sym->st_name)+1;
                    anames = tcc_realloc(anames, strpos+istrlen);
                    strcpy(anames + strpos, strtab + sym->st_name);
                    strpos += istrlen;
                    if (++funccnt >= funcmax) {
                        funcmax += 250;
                        afpos = tcc_realloc(afpos, funcmax * sizeof *afpos); // 250 func more
                    }
                    afpos[funccnt] = fpos;
                }
            }
        }

        file = argv[i_obj];
        for (name = strchr(file, 0);
             name > file && name[-1] != '/' && name[-1] != '\\';
             --name);
        istrlen = strlen(name);
        if (istrlen >= sizeof(arhdro.ar_name))
            istrlen = sizeof(arhdro.ar_name) - 1;
        memset(arhdro.ar_name, ' ', sizeof(arhdro.ar_name));
        memcpy(arhdro.ar_name, name, istrlen);
        arhdro.ar_name[istrlen] = '/';
        sprintf(stmp, "%-10d", fsize);
        memcpy(&arhdro.ar_size, stmp, 10);
        fwrite(&arhdro, sizeof(arhdro), 1, fo);
        fwrite(buf, fsize, 1, fo);
        tcc_free(buf);
        i_obj++;
        fpos += (fsize + sizeof(arhdro));
    }
    hofs = 8 + sizeof(arhdr) + strpos + (funccnt+1) * sizeof(int);
    fpos = 0;
    if ((hofs & 1)) // align
        hofs++, fpos = 1;
    // write header
    fwrite("!<arch>\n", 8, 1, fh);
    sprintf(stmp, "%-10d", (int)(strpos + (funccnt+1) * sizeof(int)));
    memcpy(&arhdr.ar_size, stmp, 10);
    fwrite(&arhdr, sizeof(arhdr), 1, fh);
    afpos[0] = le2belong(funccnt);
    for (i=1; i<=funccnt; i++)
        afpos[i] = le2belong(afpos[i] + hofs);
    fwrite(afpos, (funccnt+1) * sizeof(int), 1, fh);
    fwrite(anames, strpos, 1, fh);
    if (fpos)
        fwrite("", 1, 1, fh);
    // write objects
    fseek(fo, 0, SEEK_END);
    fsize = ftell(fo);
    fseek(fo, 0, SEEK_SET);
    buf = tcc_malloc(fsize + 1);
    fread(buf, fsize, 1, fo);
    fwrite(buf, fsize, 1, fh);
    tcc_free(buf);
    ret = 0;
the_end:
    if (anames)
        tcc_free(anames);
    if (afpos)
        tcc_free(afpos);
    if (fh)
        fclose(fh);
    if (fo)
        fclose(fo), remove(tfile);
    return ret;
}

/* -------------------------------------------------------------- */
/*
 * tiny_impdef creates an export definition file (.def) from a dll
 * on MS-Windows. Usage: tiny_impdef library.dll [-o outputfile]"
 *
 *  Copyright (c) 2005,2007 grischka
 *
 * 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.
 */

#ifdef TCC_TARGET_PE

ST_FUNC int tcc_tool_impdef(TCCState *s1, int argc, char **argv)
{
    int ret, v, i;
    char infile[260];
    char outfile[260];

    const char *file;
    char *p, *q;
    FILE *fp, *op;

#ifdef _WIN32
    char path[260];
#endif

    infile[0] = outfile[0] = 0;
    fp = op = NULL;
    ret = 1;
    p = NULL;
    v = 0;

    for (i = 1; i < argc; ++i) {
        const char *a = argv[i];
        if ('-' == a[0]) {
            if (0 == strcmp(a, "-v")) {
                v = 1;
            } else if (0 == strcmp(a, "-o")) {
                if (++i == argc)
                    goto usage;
                strcpy(outfile, argv[i]);
            } else
                goto usage;
        } else if (0 == infile[0])
            strcpy(infile, a);
        else
            goto usage;
    }

    if (0 == infile[0]) {
usage:
        fprintf(stderr,
            "usage: tcc -impdef library.dll [-v] [-o outputfile]\n"
            "create export definition file (.def) from dll\n"
            );
        goto the_end;
    }

    if (0 == outfile[0]) {
        strcpy(outfile, tcc_basename(infile));
        q = strrchr(outfile, '.');
        if (NULL == q)
            q = strchr(outfile, 0);
        strcpy(q, ".def");
    }

    file = infile;
#ifdef _WIN32
    if (SearchPath(NULL, file, ".dll", sizeof path, path, NULL))
        file = path;
#endif
    ret = tcc_get_dllexports(file, &p);
    if (ret || !p) {
        fprintf(stderr, "tcc: impdef: %s '%s'\n",
            ret == 32 ? "can't read symbols from 32bit" :
            ret == 64 ? "can't read symbols from 64bit" :
            ret == -1 ? "can't find file" :
            ret ==  0 ? "no symbols found in" :
            "unknown file type", file);
        ret = 1;
        goto the_end;
    }

    if (v)
        printf("-> %s\n", file);

    op = fopen(outfile, "w");
    if (NULL == op) {
        fprintf(stderr, "tcc: impdef: could not create output file: %s\n", outfile);
        goto the_end;
    }

    fprintf(op, "LIBRARY %s\n\nEXPORTS\n", tcc_basename(file));
    for (q = p, i = 0; *q; ++i) {
        fprintf(op, "%s\n", q);
        q += strlen(q) + 1;
    }

    if (v)
        printf("<- %s (%d symbol%s)\n", outfile, i, &"s"[i<2]);

    ret = 0;

the_end:
    /* cannot free memory received from tcc_get_dllexports
       if it came from a dll */
    /* if (p)
        tcc_free(p); */
    if (fp)
        fclose(fp);
    if (op)
        fclose(op);
    return ret;
}

#endif /* TCC_TARGET_PE */

/* -------------------------------------------------------------- */
/*
 *  TCC - Tiny C Compiler
 *
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/* re-execute the i386/x86_64 cross-compilers with tcc -m32/-m64: */

#if !defined TCC_TARGET_I386 && !defined TCC_TARGET_X86_64

ST_FUNC void tcc_tool_cross(TCCState *s, char **argv, int option)
{
    tcc_error("-m%d not implemented.", option);
}

#else
#ifdef _WIN32
#include <process.h>

static char *str_replace(const char *str, const char *p, const char *r)
{
    const char *s, *s0;
    char *d, *d0;
    int sl, pl, rl;

    sl = strlen(str);
    pl = strlen(p);
    rl = strlen(r);
    for (d0 = NULL;; d0 = tcc_malloc(sl + 1)) {
        for (d = d0, s = str; s0 = s, s = strstr(s, p), s; s += pl) {
            if (d) {
                memcpy(d, s0, sl = s - s0), d += sl;
                memcpy(d, r, rl), d += rl;
            } else
                sl += rl - pl;
        }
        if (d) {
            strcpy(d, s0);
            return d0;
        }
    }
}

static int execvp_win32(const char *prog, char **argv)
{
    int ret; char **p;
    /* replace all " by \" */
    for (p = argv; *p; ++p)
        if (strchr(*p, '"'))
            *p = str_replace(*p, "\"", "\\\"");
    ret = _spawnvp(P_NOWAIT, prog, (const char *const*)argv);
    if (-1 == ret)
        return ret;
    _cwait(&ret, ret, WAIT_CHILD);
    exit(ret);
}
#define execvp execvp_win32
#endif /* _WIN32 */

ST_FUNC void tcc_tool_cross(TCCState *s, char **argv, int target)
{
    char program[4096];
    char *a0 = argv[0];
    int prefix = tcc_basename(a0) - a0;

    snprintf(program, sizeof program,
        "%.*s%s"
#ifdef TCC_TARGET_PE
        "-win32"
#endif
        "-tcc"
#ifdef _WIN32
        ".exe"
#endif
        , prefix, a0, target == 64 ? "x86_64" : "i386");

    if (strcmp(a0, program))
        execvp(argv[0] = program, argv);
    tcc_error("could not run '%s'", program);
}

#endif /* TCC_TARGET_I386 && TCC_TARGET_X86_64 */
/* -------------------------------------------------------------- */
/* enable commandline wildcard expansion (tcc -o x.exe *.c) */

#ifdef _WIN32
int _CRT_glob = 1;
#ifndef _CRT_glob
int _dowildcard = 1;
#endif
#endif

/* -------------------------------------------------------------- */
/* generate xxx.d file */

ST_FUNC void gen_makedeps(TCCState *s, const char *target, const char *filename)
{
    FILE *depout;
    char buf[1024];
    int i;

    if (!filename) {
        /* compute filename automatically: dir/file.o -> dir/file.d */
        snprintf(buf, sizeof buf, "%.*s.d",
            (int)(tcc_fileextension(target) - target), target);
        filename = buf;
    }

    if (s->verbose)
        printf("<- %s\n", filename);

    /* XXX return err codes instead of error() ? */
    depout = fopen(filename, "w");
    if (!depout)
        tcc_error("could not open '%s'", filename);

    fprintf(depout, "%s: \\\n", target);
    for (i=0; i<s->nb_target_deps; ++i)
        fprintf(depout, " %s \\\n", s->target_deps[i]);
    fprintf(depout, "\n");
    fclose(depout);
}

/* -------------------------------------------------------------- */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.26/simple-patches/remove-fileopen.before

File: ../git/live-bootstrap/sysa/tcc-0.9.26/simple-patches/remove-fileopen.before used in: Step 374
    if (ret == 1)
        return ar_usage(ret);

    if ((fh = fopen(argv[i_lib], "wb")) == NULL)
    {
        fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
        goto the_end;
    }

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.26/simple-patches/remove-fileopen.after

File: ../git/live-bootstrap/sysa/tcc-0.9.26/simple-patches/remove-fileopen.after used in: Step 374
    if (ret == 1)
        return ar_usage(ret);

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.26/simple-patches/addback-fileopen.before

File: ../git/live-bootstrap/sysa/tcc-0.9.26/simple-patches/addback-fileopen.before used in: Step 375
    // write header

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.26/simple-patches/addback-fileopen.after

File: ../git/live-bootstrap/sysa/tcc-0.9.26/simple-patches/addback-fileopen.after used in: Step 375
    if ((fh = fopen(argv[i_lib], "wb")) == NULL)
    {
        fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
        goto the_end;
    }

    // write header

https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tcc.c

File: ../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tcc.c used in: Step 378, Step 395, Step 407, Step 419, Step 431, Step 443, Step 455
/*
 *  TCC - Tiny C Compiler
 * 
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef ONE_SOURCE
#include "libtcc.c"
#else
#include "tcc.h"
#endif
#include "tcctools.c"

static const char help[] =
    "Tiny C Compiler "TCC_VERSION" - Copyright (C) 2001-2006 Fabrice Bellard\n"
    "Usage: tcc [options...] [-o outfile] [-c] infile(s)...\n"
    "       tcc [options...] -run infile [arguments...]\n"
    "General options:\n"
    "  -c          compile only - generate an object file\n"
    "  -o outfile  set output filename\n"
    "  -run        run compiled source\n"
    "  -fflag      set or reset (with 'no-' prefix) 'flag' (see tcc -hh)\n"
    "  -Wwarning   set or reset (with 'no-' prefix) 'warning' (see tcc -hh)\n"
    "  -w          disable all warnings\n"
    "  -v -vv      show version, show search paths or loaded files\n"
    "  -h -hh      show this, show more help\n"
    "  -bench      show compilation statistics\n"
    "  -           use stdin pipe as infile\n"
    "  @listfile   read arguments from listfile\n"
    "Preprocessor options:\n"
    "  -Idir       add include path 'dir'\n"
    "  -Dsym[=val] define 'sym' with value 'val'\n"
    "  -Usym       undefine 'sym'\n"
    "  -E          preprocess only\n"
    "Linker options:\n"
    "  -Ldir       add library path 'dir'\n"
    "  -llib       link with dynamic or static library 'lib'\n"
    "  -r          generate (relocatable) object file\n"
    "  -shared     generate a shared library/dll\n"
    "  -rdynamic   export all global symbols to dynamic linker\n"
    "  -soname     set name for shared library to be used at runtime\n"
    "  -Wl,-opt[=val]  set linker option (see tcc -hh)\n"
    "Debugger options:\n"
    "  -g          generate runtime debug info\n"
#ifdef CONFIG_TCC_BCHECK
    "  -b          compile with built-in memory and bounds checker (implies -g)\n"
#endif
#ifdef CONFIG_TCC_BACKTRACE
    "  -bt N       show N callers in stack traces\n"
#endif
    "Misc. options:\n"
    "  -x[c|a|n]   specify type of the next infile\n"
    "  -nostdinc   do not use standard system include paths\n"
    "  -nostdlib   do not link with standard crt and libraries\n"
    "  -Bdir       set tcc's private include/library dir\n"
    "  -MD         generate dependency file for make\n"
    "  -MF file    specify dependency file name\n"
    "  -m32/64     defer to i386/x86_64 cross compiler\n"
    "Tools:\n"
    "  create library  : tcc -ar [rcsv] lib.a files\n"
#ifdef TCC_TARGET_PE
    "  create def file : tcc -impdef lib.dll [-v] [-o lib.def]\n"
#endif
    ;

static const char help2[] =
    "Tiny C Compiler "TCC_VERSION" - More Options\n"
    "Special options:\n"
    "  -P -P1                        with -E: no/alternative #line output\n"
    "  -dD -dM                       with -E: output #define directives\n"
    "  -pthread                      same as -D_REENTRANT and -lpthread\n"
    "  -On                           same as -D__OPTIMIZE__ for n > 0\n"
    "  -Wp,-opt                      same as -opt\n"
    "  -include file                 include 'file' above each input file\n"
    "  -isystem dir                  add 'dir' to system include path\n"
    "  -iwithprefix dir              set tcc's private include/library subdir\n"
    "  -static                       link to static libraries (not recommended)\n"
    "  -dumpversion                  print version\n"
    "  -print-search-dirs            print search paths\n"
    "Ignored options:\n"
    "  --param  -pedantic  -pipe  -s  -std  -traditional\n"
    "-W... warnings:\n"
    "  all                           turn on some (*) warnings\n"
    "  error                         stop after first warning\n"
    "  unsupported                   warn about ignored options, pragmas, etc.\n"
    "  write-strings                 strings are const\n"
    "  implicit-function-declaration   warn for missing prototype (*)\n"
    "-f[no-]... flags:\n"
    "  unsigned-char                 default char is unsigned\n"
    "  signed-char                   default char is signed\n"
    "  common                        use common section instead of bss\n"
    "  leading-underscore            decorate extern symbols\n"
    "  ms-extensions                 allow anonymous struct in struct\n"
    "  dollars-in-identifiers        allow '$' in C symbols\n"
    "-m... target specific options:\n"
    "  ms-bitfields                  use MSVC bitfield layout\n"
#ifdef TCC_TARGET_ARM
    "  float-abi                     hard/softfp on arm\n"
#endif
#ifdef TCC_TARGET_X86_64
    "  no-sse                        disable floats on x86_64\n"
#endif
    "-Wl,... linker options:\n"
    "  -nostdlib                     do not link with standard crt/libs\n"
    "  -[no-]whole-archive           load lib(s) fully/only as needed\n"
    "  -export-all-symbols           same as -rdynamic\n"
    "  -image-base= -Ttext=          set base address of executable\n"
    "  -section-alignment=           set section alignment in executable\n"
#ifdef TCC_TARGET_PE
    "  -file-alignment=              set PE file alignment\n"
    "  -stack=                       set PE stack reserve\n"
    "  -large-address-aware          set related PE option\n"
    "  -subsystem=[console/windows]  set PE subsystem\n"
    "  -oformat=[pe-* binary]        set executable output format\n"
    "Predefined macros:\n"
    "  tcc -E -dM - < nul\n"
#else
    "  -rpath=                       set dynamic library search path\n"
    "  -enable-new-dtags             set DT_RUNPATH instead of DT_RPATH\n"
    "  -soname=                      set DT_SONAME elf tag\n"
    "  -Bsymbolic                    set DT_SYMBOLIC elf tag\n"
    "  -oformat=[elf32/64-* binary]  set executable output format\n"
    "  -init= -fini= -as-needed -O   (ignored)\n"
    "Predefined macros:\n"
    "  tcc -E -dM - < /dev/null\n"
#endif
    "See also the manual for more details.\n"
    ;

static const char version[] =
    "tcc version "TCC_VERSION" ("
#ifdef TCC_TARGET_I386
        "i386"
#elif defined TCC_TARGET_X86_64
        "x86_64"
#elif defined TCC_TARGET_C67
        "C67"
#elif defined TCC_TARGET_ARM
        "ARM"
#elif defined TCC_TARGET_ARM64
        "AArch64"
#endif
#ifdef TCC_ARM_HARDFLOAT
        " Hard Float"
#endif
#ifdef TCC_TARGET_PE
        " Windows"
#elif defined(TCC_TARGET_MACHO)
        " Darwin"
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
        " FreeBSD"
#else
        " Linux"
#endif
    ")\n"
    ;

static void print_dirs(const char *msg, char **paths, int nb_paths)
{
    int i;
    printf("%s:\n%s", msg, nb_paths ? "" : "  -\n");
    for(i = 0; i < nb_paths; i++)
        printf("  %s\n", paths[i]);
}

static void print_search_dirs(TCCState *s)
{
    printf("install: %s\n", s->tcc_lib_path);
    /* print_dirs("programs", NULL, 0); */
    print_dirs("include", s->sysinclude_paths, s->nb_sysinclude_paths);
    print_dirs("libraries", s->library_paths, s->nb_library_paths);
#ifndef TCC_TARGET_PE
    print_dirs("crt", s->crt_paths, s->nb_crt_paths);
    printf("libtcc1:\n  %s/"TCC_LIBTCC1"\n", s->tcc_lib_path);
    printf("elfinterp:\n  %s\n",  DEFAULT_ELFINTERP(s));
#endif
}

static void set_environment(TCCState *s)
{
    char * path;

    path = getenv("C_INCLUDE_PATH");
    if(path != NULL) {
        tcc_add_include_path(s, path);
    }
    path = getenv("CPATH");
    if(path != NULL) {
        tcc_add_include_path(s, path);
    }
    path = getenv("LIBRARY_PATH");
    if(path != NULL) {
        tcc_add_library_path(s, path);
    }
}

static char *default_outputfile(TCCState *s, const char *first_file)
{
    char buf[1024];
    char *ext;
    const char *name = "a";

    if (first_file && strcmp(first_file, "-"))
        name = tcc_basename(first_file);
    snprintf(buf, sizeof(buf), "%s", name);
    ext = tcc_fileextension(buf);
#ifdef TCC_TARGET_PE
    if (s->output_type == TCC_OUTPUT_DLL)
        strcpy(ext, ".dll");
    else
    if (s->output_type == TCC_OUTPUT_EXE)
        strcpy(ext, ".exe");
    else
#endif
    if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r && *ext)
        strcpy(ext, ".o");
    else
        strcpy(buf, "a.out");
    return tcc_strdup(buf);
}

static unsigned getclock_ms(void)
{
#ifdef _WIN32
    return GetTickCount();
#else
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec*1000 + (tv.tv_usec+500)/1000;
#endif
}

int main(int argc, char **argv)
{
    TCCState *s;
    int ret, opt, n = 0;
    unsigned start_time = 0;
    const char *first_file;

redo:
    s = tcc_new();
    opt = tcc_parse_args(s, &argc, &argv, 1);

    if (n == 0) {
        if (opt == OPT_HELP)
            return printf(help), 1;
        if (opt == OPT_HELP2)
            return printf(help2), 1;
        if (opt == OPT_M32 || opt == OPT_M64)
            tcc_tool_cross(s, argv, opt); /* never returns */
        if (s->verbose)
            printf(version);
        if (opt == OPT_AR)
            return tcc_tool_ar(s, argc, argv);
#ifdef TCC_TARGET_PE
        if (opt == OPT_IMPDEF)
            return tcc_tool_impdef(s, argc, argv);
#endif
        if (opt == OPT_V)
            return 0;
        if (opt == OPT_PRINT_DIRS) {
            /* initialize search dirs */
            tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
            print_search_dirs(s);
            return 0;
        }

        n = s->nb_files;
        if (n == 0)
            tcc_error("no input files\n");

        if (s->output_type == TCC_OUTPUT_PREPROCESS) {
            if (!s->outfile) {
                s->ppfp = stdout;
            } else {
                s->ppfp = fopen(s->outfile, "w");
                if (!s->ppfp)
                    tcc_error("could not write '%s'", s->outfile);
            }
        } else if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r) {
            if (s->nb_libraries)
                tcc_error("cannot specify libraries with -c");
            if (n > 1 && s->outfile)
                tcc_error("cannot specify output file with -c many files");
        } else {
            if (s->option_pthread)
                tcc_set_options(s, "-lpthread");
        }

        if (s->do_bench)
            start_time = getclock_ms();
    }

    set_environment(s);
    if (s->output_type == 0)
        s->output_type = TCC_OUTPUT_EXE;
    tcc_set_output_type(s, s->output_type);

    /* compile or add each files or library */
    for (first_file = NULL, ret = 0;;) {
        struct filespec *f = s->files[s->nb_files - n];
        s->filetype = f->type;
        s->alacarte_link = f->alacarte;
        if (f->type == AFF_TYPE_LIB) {
            if (tcc_add_library_err(s, f->name) < 0)
                ret = 1;
        } else {
            if (1 == s->verbose)
                printf("-> %s\n", f->name);
            if (!first_file)
                first_file = f->name;
            if (tcc_add_file(s, f->name) < 0)
                ret = 1;
        }
        s->filetype = 0;
        s->alacarte_link = 1;
        if (ret || --n == 0
            || (s->output_type == TCC_OUTPUT_OBJ && !s->option_r))
            break;
    }

    if (s->output_type == TCC_OUTPUT_PREPROCESS) {
        if (s->outfile)
            fclose(s->ppfp);
    } else if (0 == ret) {
        if (s->output_type == TCC_OUTPUT_MEMORY) {
#ifdef TCC_IS_NATIVE
            ret = tcc_run(s, argc, argv);
#endif
        } else {
            if (!s->outfile)
                s->outfile = default_outputfile(s, first_file);
            if (tcc_output_file(s, s->outfile))
                ret = 1;
            else if (s->gen_deps)
                gen_makedeps(s, s->outfile, s->deps_outfile);
        }
    }

    if (s->do_bench && ret == 0 && n == 0)
        tcc_print_stats(s, getclock_ms() - start_time);
    tcc_delete(s);
    if (ret == 0 && n)
        goto redo; /* compile more files with -c */
    return ret;
}

https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/libtcc.c

File: ../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/libtcc.c used in: Step 378, Step 395, Step 407, Step 419, Step 431, Step 443, Step 455
/*
 *  TCC - Tiny C Compiler
 *
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef _TCC_H
#include "tcc.h"
#endif

/********************************************************/
/* global variables */

/* use GNU C extensions */
ST_DATA int gnu_ext = 1;

/* use TinyCC extensions */
ST_DATA int tcc_ext = 1;

/* XXX: get rid of this ASAP */
ST_DATA struct TCCState *tcc_state;

static int nb_states;

/********************************************************/

#ifdef ONE_SOURCE
#include "tccpp.c"
#include "tccgen.c"
#include "tccelf.c"
#include "tccrun.c"
#ifdef TCC_TARGET_I386
#include "i386-gen.c"
#include "i386-link.c"
#include "i386-asm.c"
#endif
#ifdef TCC_TARGET_ARM
#include "arm-gen.c"
#include "arm-link.c"
#include "arm-asm.c"
#endif
#ifdef TCC_TARGET_ARM64
#include "arm64-gen.c"
#include "arm64-link.c"
#endif
#ifdef TCC_TARGET_C67
#include "c67-gen.c"
#include "c67-link.c"
#endif
#ifdef TCC_TARGET_X86_64
#include "x86_64-gen.c"
#include "x86_64-link.c"
#include "i386-asm.c"
#endif
#ifdef CONFIG_TCC_ASM
#include "tccasm.c"
#endif
#ifdef TCC_TARGET_COFF
#include "tcccoff.c"
#endif
#ifdef TCC_TARGET_PE
#include "tccpe.c"
#endif
#endif /* ONE_SOURCE */

/********************************************************/
#ifndef CONFIG_TCC_ASM
ST_FUNC void asm_instr(void)
{
    tcc_error("inline asm() not supported");
}
ST_FUNC void asm_global_instr(void)
{
    tcc_error("inline asm() not supported");
}
#endif

/********************************************************/
#ifdef _WIN32
ST_FUNC char *normalize_slashes(char *path)
{
    char *p;
    for (p = path; *p; ++p)
        if (*p == '\\')
            *p = '/';
    return path;
}

static HMODULE tcc_module;

/* on win32, we suppose the lib and includes are at the location of 'tcc.exe' */
static void tcc_set_lib_path_w32(TCCState *s)
{
    char path[1024], *p;
    GetModuleFileNameA(tcc_module, path, sizeof path);
    p = tcc_basename(normalize_slashes(strlwr(path)));
    if (p - 5 > path && 0 == strncmp(p - 5, "/bin/", 5))
        p -= 5;
    else if (p > path)
        p--;
    *p = 0;
    tcc_set_lib_path(s, path);
}

#ifdef TCC_TARGET_PE
static void tcc_add_systemdir(TCCState *s)
{
    char buf[1000];
    GetSystemDirectory(buf, sizeof buf);
    tcc_add_library_path(s, normalize_slashes(buf));
}
#endif

#ifdef LIBTCC_AS_DLL
BOOL WINAPI DllMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved)
{
    if (DLL_PROCESS_ATTACH == dwReason)
        tcc_module = hDll;
    return TRUE;
}
#endif
#endif

/********************************************************/
/* copy a string and truncate it. */
ST_FUNC char *pstrcpy(char *buf, int buf_size, const char *s)
{
    char *q, *q_end;
    int c;

    if (buf_size > 0) {
        q = buf;
        q_end = buf + buf_size - 1;
        while (q < q_end) {
            c = *s++;
            if (c == '\0')
                break;
            *q++ = c;
        }
        *q = '\0';
    }
    return buf;
}

/* strcat and truncate. */
ST_FUNC char *pstrcat(char *buf, int buf_size, const char *s)
{
    int len;
    len = strlen(buf);
    if (len < buf_size)
        pstrcpy(buf + len, buf_size - len, s);
    return buf;
}

ST_FUNC char *pstrncpy(char *out, const char *in, size_t num)
{
    memcpy(out, in, num);
    out[num] = '\0';
    return out;
}

/* extract the basename of a file */
PUB_FUNC char *tcc_basename(const char *name)
{
    char *p = strchr(name, 0);
    while (p > name && !IS_DIRSEP(p[-1]))
        --p;
    return p;
}

/* extract extension part of a file
 *
 * (if no extension, return pointer to end-of-string)
 */
PUB_FUNC char *tcc_fileextension (const char *name)
{
    char *b = tcc_basename(name);
    char *e = strrchr(b, '.');
    return e ? e : strchr(b, 0);
}

/********************************************************/
/* memory management */

#undef free
#undef malloc
#undef realloc

#ifndef MEM_DEBUG

PUB_FUNC void tcc_free(void *ptr)
{
    free(ptr);
}

PUB_FUNC void *tcc_malloc(unsigned long size)
{
    void *ptr;
    ptr = malloc(size);
    if (!ptr && size)
        tcc_error("memory full (malloc)");
    return ptr;
}

PUB_FUNC void *tcc_mallocz(unsigned long size)
{
    void *ptr;
    ptr = tcc_malloc(size);
    memset(ptr, 0, size);
    return ptr;
}

PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size)
{
    void *ptr1;
    ptr1 = realloc(ptr, size);
    if (!ptr1 && size)
        tcc_error("memory full (realloc)");
    return ptr1;
}

PUB_FUNC char *tcc_strdup(const char *str)
{
    char *ptr;
    ptr = tcc_malloc(strlen(str) + 1);
    strcpy(ptr, str);
    return ptr;
}

PUB_FUNC void tcc_memcheck(void)
{
}

#else

#define MEM_DEBUG_MAGIC1 0xFEEDDEB1
#define MEM_DEBUG_MAGIC2 0xFEEDDEB2
#define MEM_DEBUG_MAGIC3 0xFEEDDEB3
#define MEM_DEBUG_FILE_LEN 40
#define MEM_DEBUG_CHECK3(header) \
    ((mem_debug_header_t*)((char*)header + header->size))->magic3
#define MEM_USER_PTR(header) \
    ((char *)header + offsetof(mem_debug_header_t, magic3))
#define MEM_HEADER_PTR(ptr) \
    (mem_debug_header_t *)((char*)ptr - offsetof(mem_debug_header_t, magic3))

struct mem_debug_header {
    unsigned magic1;
    unsigned size;
    struct mem_debug_header *prev;
    struct mem_debug_header *next;
    int line_num;
    char file_name[MEM_DEBUG_FILE_LEN + 1];
    unsigned magic2;
    __attribute__((aligned(16))) unsigned magic3;
};

typedef struct mem_debug_header mem_debug_header_t;

static mem_debug_header_t *mem_debug_chain;
static unsigned mem_cur_size;
static unsigned mem_max_size;

static mem_debug_header_t *malloc_check(void *ptr, const char *msg)
{
    mem_debug_header_t * header = MEM_HEADER_PTR(ptr);
    if (header->magic1 != MEM_DEBUG_MAGIC1 ||
        header->magic2 != MEM_DEBUG_MAGIC2 ||
        MEM_DEBUG_CHECK3(header) != MEM_DEBUG_MAGIC3 ||
        header->size == (unsigned)-1) {
        fprintf(stderr, "%s check failed\n", msg);
        if (header->magic1 == MEM_DEBUG_MAGIC1)
            fprintf(stderr, "%s:%u: block allocated here.\n",
                header->file_name, header->line_num);
        exit(1);
    }
    return header;
}

PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line)
{
    int ofs;
    mem_debug_header_t *header;

    header = malloc(sizeof(mem_debug_header_t) + size);
    if (!header)
        tcc_error("memory full (malloc)");

    header->magic1 = MEM_DEBUG_MAGIC1;
    header->magic2 = MEM_DEBUG_MAGIC2;
    header->size = size;
    MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3;
    header->line_num = line;
    ofs = strlen(file) - MEM_DEBUG_FILE_LEN;
    strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), MEM_DEBUG_FILE_LEN);
    header->file_name[MEM_DEBUG_FILE_LEN] = 0;

    header->next = mem_debug_chain;
    header->prev = NULL;
    if (header->next)
        header->next->prev = header;
    mem_debug_chain = header;

    mem_cur_size += size;
    if (mem_cur_size > mem_max_size)
        mem_max_size = mem_cur_size;

    return MEM_USER_PTR(header);
}

PUB_FUNC void tcc_free_debug(void *ptr)
{
    mem_debug_header_t *header;
    if (!ptr)
        return;
    header = malloc_check(ptr, "tcc_free");
    mem_cur_size -= header->size;
    header->size = (unsigned)-1;
    if (header->next)
        header->next->prev = header->prev;
    if (header->prev)
        header->prev->next = header->next;
    if (header == mem_debug_chain)
        mem_debug_chain = header->next;
    free(header);
}

PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line)
{
    void *ptr;
    ptr = tcc_malloc_debug(size,file,line);
    memset(ptr, 0, size);
    return ptr;
}

PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line)
{
    mem_debug_header_t *header;
    int mem_debug_chain_update = 0;
    if (!ptr)
        return tcc_malloc_debug(size, file, line);
    header = malloc_check(ptr, "tcc_realloc");
    mem_cur_size -= header->size;
    mem_debug_chain_update = (header == mem_debug_chain);
    header = realloc(header, sizeof(mem_debug_header_t) + size);
    if (!header)
        tcc_error("memory full (realloc)");
    header->size = size;
    MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3;
    if (header->next)
        header->next->prev = header;
    if (header->prev)
        header->prev->next = header;
    if (mem_debug_chain_update)
        mem_debug_chain = header;
    mem_cur_size += size;
    if (mem_cur_size > mem_max_size)
        mem_max_size = mem_cur_size;
    return MEM_USER_PTR(header);
}

PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line)
{
    char *ptr;
    ptr = tcc_malloc_debug(strlen(str) + 1, file, line);
    strcpy(ptr, str);
    return ptr;
}

PUB_FUNC void tcc_memcheck(void)
{
    if (mem_cur_size) {
        mem_debug_header_t *header = mem_debug_chain;
        fprintf(stderr, "MEM_DEBUG: mem_leak= %d bytes, mem_max_size= %d bytes\n",
            mem_cur_size, mem_max_size);
        while (header) {
            fprintf(stderr, "%s:%u: error: %u bytes leaked\n",
                header->file_name, header->line_num, header->size);
            header = header->next;
        }
#if MEM_DEBUG-0 == 2
        exit(2);
#endif
    }
}
#endif /* MEM_DEBUG */

#define free(p) use_tcc_free(p)
#define malloc(s) use_tcc_malloc(s)
#define realloc(p, s) use_tcc_realloc(p, s)

/********************************************************/
/* dynarrays */

ST_FUNC void dynarray_add(void *ptab, int *nb_ptr, void *data)
{
    int nb, nb_alloc;
    void **pp;

    nb = *nb_ptr;
    pp = *(void ***)ptab;
    /* every power of two we double array size */
    if ((nb & (nb - 1)) == 0) {
        if (!nb)
            nb_alloc = 1;
        else
            nb_alloc = nb * 2;
        pp = tcc_realloc(pp, nb_alloc * sizeof(void *));
        *(void***)ptab = pp;
    }
    pp[nb++] = data;
    *nb_ptr = nb;
}

ST_FUNC void dynarray_reset(void *pp, int *n)
{
    void **p;
    for (p = *(void***)pp; *n; ++p, --*n)
        if (*p)
            tcc_free(*p);
    tcc_free(*(void**)pp);
    *(void**)pp = NULL;
}

static void tcc_split_path(TCCState *s, void *p_ary, int *p_nb_ary, const char *in)
{
    const char *p;
    do {
        int c;
        CString str;

        cstr_new(&str);
        for (p = in; c = *p, c != '\0' && c != PATHSEP; ++p) {
            if (c == '{' && p[1] && p[2] == '}') {
                c = p[1], p += 2;
                if (c == 'B')
                    cstr_cat(&str, s->tcc_lib_path, -1);
            } else {
                cstr_ccat(&str, c);
            }
        }
        if (str.size) {
            cstr_ccat(&str, '\0');
            dynarray_add(p_ary, p_nb_ary, tcc_strdup(str.data));
        }
        cstr_free(&str);
        in = p+1;
    } while (*p);
}

/********************************************************/

static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap)
{
    int len;
    len = strlen(buf);
    vsnprintf(buf + len, buf_size - len, fmt, ap);
}

static void strcat_printf(char *buf, int buf_size, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    strcat_vprintf(buf, buf_size, fmt, ap);
    va_end(ap);
}

static void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap)
{
    char buf[2048];
    BufferedFile **pf, *f;

    buf[0] = '\0';
    /* use upper file if inline ":asm:" or token ":paste:" */
    for (f = file; f && f->filename[0] == ':'; f = f->prev)
     ;
    if (f) {
        for(pf = s1->include_stack; pf < s1->include_stack_ptr; pf++)
            strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n",
                (*pf)->filename, (*pf)->line_num);
        if (f->line_num > 0) {
            strcat_printf(buf, sizeof(buf), "%s:%d: ",
                f->filename, f->line_num - !!(tok_flags & TOK_FLAG_BOL));
        } else {
            strcat_printf(buf, sizeof(buf), "%s: ",
                f->filename);
        }
    } else {
        strcat_printf(buf, sizeof(buf), "tcc: ");
    }
    if (is_warning)
        strcat_printf(buf, sizeof(buf), "warning: ");
    else
        strcat_printf(buf, sizeof(buf), "error: ");
    strcat_vprintf(buf, sizeof(buf), fmt, ap);

    if (!s1->error_func) {
        /* default case: stderr */
        if (s1->ppfp) /* print a newline during tcc -E */
            fprintf(s1->ppfp, "\n"), fflush(s1->ppfp);
        fflush(stdout); /* flush -v output */
        fprintf(stderr, "%s\n", buf);
        fflush(stderr); /* print error/warning now (win32) */
    } else {
        s1->error_func(s1->error_opaque, buf);
    }
    if (!is_warning || s1->warn_error)
        s1->nb_errors++;
}

LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque,
                        void (*error_func)(void *opaque, const char *msg))
{
    s->error_opaque = error_opaque;
    s->error_func = error_func;
}

/* error without aborting current compilation */
PUB_FUNC void tcc_error_noabort(const char *fmt, ...)
{
    TCCState *s1 = tcc_state;
    va_list ap;

    va_start(ap, fmt);
    error1(s1, 0, fmt, ap);
    va_end(ap);
}

PUB_FUNC void tcc_error(const char *fmt, ...)
{
    TCCState *s1 = tcc_state;
    va_list ap;

    va_start(ap, fmt);
    error1(s1, 0, fmt, ap);
    va_end(ap);
    /* better than nothing: in some cases, we accept to handle errors */
    if (s1->error_set_jmp_enabled) {
        longjmp(s1->error_jmp_buf, 1);
    } else {
        /* XXX: eliminate this someday */
        exit(1);
    }
}

PUB_FUNC void tcc_warning(const char *fmt, ...)
{
    TCCState *s1 = tcc_state;
    va_list ap;

    if (s1->warn_none)
        return;

    va_start(ap, fmt);
    error1(s1, 1, fmt, ap);
    va_end(ap);
}

/********************************************************/
/* I/O layer */

ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen)
{
    BufferedFile *bf;
    int buflen = initlen ? initlen : IO_BUF_SIZE;

    bf = tcc_mallocz(sizeof(BufferedFile) + buflen);
    bf->buf_ptr = bf->buffer;
    bf->buf_end = bf->buffer + initlen;
    bf->buf_end[0] = CH_EOB; /* put eob symbol */
    pstrcpy(bf->filename, sizeof(bf->filename), filename);
    pstrcpy(bf->filename2, sizeof(bf->filename2), filename);
#ifdef _WIN32
    normalize_slashes(bf->filename);
#endif
    bf->line_num = 1;
    bf->ifdef_stack_ptr = s1->ifdef_stack_ptr;
    bf->fd = -1;
    bf->prev = file;
    file = bf;
}

ST_FUNC void tcc_close(void)
{
    BufferedFile *bf = file;
    if (bf->fd > 0) {
        close(bf->fd);
        total_lines += bf->line_num;
    }
    file = bf->prev;
    tcc_free(bf);
}

ST_FUNC int tcc_open(TCCState *s1, const char *filename)
{
    int fd;
    if (strcmp(filename, "-") == 0)
        fd = 0, filename = "<stdin>";
    else
        fd = open(filename, O_RDONLY | O_BINARY);
    if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3)
        printf("%s %*s%s\n", fd < 0 ? "nf":"->",
               (int)(s1->include_stack_ptr - s1->include_stack), "", filename);
    if (fd < 0)
        return -1;

    tcc_open_bf(s1, filename, 0);
    file->fd = fd;
    return fd;
}

/* compile the C file opened in 'file'. Return non zero if errors. */
static int tcc_compile(TCCState *s1)
{
    Sym *define_start;

    define_start = define_stack;
#if HAVE_SETJMP
    if (setjmp(s1->error_jmp_buf) == 0) {
#else
    if (1) {
#endif
        s1->nb_errors = 0;
        s1->error_set_jmp_enabled = 1;

        preprocess_start(s1);
        tccgen_start(s1);

#ifdef INC_DEBUG
        printf("%s: **** new file\n", file->filename);
#endif

        ch = file->buf_ptr[0];
        tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
        parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM | PARSE_FLAG_TOK_STR;
        next();
        decl(VT_CONST);
        if (tok != TOK_EOF)
            expect("declaration");
        /* free defines here already on behalf of of M.M.'s possibly existing
           experimental preprocessor implementation. The normal call below
           is still there to free after error-longjmp */
        free_defines(define_start);
        tccgen_end(s1);
    }
    s1->error_set_jmp_enabled = 0;

    free_inline_functions(s1);
    /* reset define stack, but keep -D and built-ins */
    free_defines(define_start);
    sym_pop(&global_stack, NULL, 0);
    sym_pop(&local_stack, NULL, 0);
    return s1->nb_errors != 0 ? -1 : 0;
}

LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str)
{
    int len, ret;

    len = strlen(str);
    tcc_open_bf(s, "<string>", len);
    memcpy(file->buffer, str, len);
    ret = tcc_compile(s);
    tcc_close();
    return ret;
}

/* define a preprocessor symbol. A value can also be provided with the '=' operator */
LIBTCCAPI void tcc_define_symbol(TCCState *s1, const char *sym, const char *value)
{
    int len1, len2;
    /* default value */
    if (!value)
        value = "1";
    len1 = strlen(sym);
    len2 = strlen(value);

    /* init file structure */
    tcc_open_bf(s1, "<define>", len1 + len2 + 1);
    memcpy(file->buffer, sym, len1);
    file->buffer[len1] = ' ';
    memcpy(file->buffer + len1 + 1, value, len2);

    /* parse with define parser */
    ch = file->buf_ptr[0];
    next_nomacro();
    parse_define();

    tcc_close();
}

/* undefine a preprocessor symbol */
LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym)
{
    TokenSym *ts;
    Sym *s;
    ts = tok_alloc(sym, strlen(sym));
    s = define_find(ts->tok);
    /* undefine symbol by putting an invalid name */
    if (s)
        define_undef(s);
}

/* cleanup all static data used during compilation */
static void tcc_cleanup(void)
{
    if (NULL == tcc_state)
        return;
    tccpp_delete(tcc_state);
    tcc_state = NULL;
    /* free sym_pools */
    dynarray_reset(&sym_pools, &nb_sym_pools);
    /* reset symbol stack */
    sym_free_first = NULL;
}

LIBTCCAPI TCCState *tcc_new(void)
{
    TCCState *s;

    tcc_cleanup();

    s = tcc_mallocz(sizeof(TCCState));
    if (!s)
        return NULL;
    tcc_state = s;
    ++nb_states;

#if BOOTSTRAP
    s->static_link = 1;
#endif
    s->alacarte_link = 1;
    s->nocommon = 1;
    s->warn_implicit_function_declaration = 1;

#ifdef CHAR_IS_UNSIGNED
    s->char_is_unsigned = 1;
#endif
#ifdef TCC_TARGET_I386
    s->seg_size = 32;
#endif
    /* enable this if you want symbols with leading underscore on windows: */
#if 0 /* def TCC_TARGET_PE */
    s->leading_underscore = 1;
#endif
#ifdef _WIN32
    tcc_set_lib_path_w32(s);
#else
    tcc_set_lib_path(s, CONFIG_TCCDIR);
#endif
    tccelf_new(s);
    tccpp_new(s);

    /* we add dummy defines for some special macros to speed up tests
       and to have working defined() */
    define_push(TOK___LINE__, MACRO_OBJ, NULL, NULL);
    define_push(TOK___FILE__, MACRO_OBJ, NULL, NULL);
    define_push(TOK___DATE__, MACRO_OBJ, NULL, NULL);
    define_push(TOK___TIME__, MACRO_OBJ, NULL, NULL);
    {
        /* define __TINYC__ 92X  */
        char buffer[32]; int a,b,c;
        sscanf(TCC_VERSION, "%d.%d.%d", &a, &b, &c);
        sprintf(buffer, "%d", a*10000 + b*100 + c);
        tcc_define_symbol(s, "__TINYC__", buffer);
    }

    /* standard defines */
    tcc_define_symbol(s, "__STDC__", NULL);
    tcc_define_symbol(s, "__STDC_VERSION__", "199901L");
    tcc_define_symbol(s, "__STDC_HOSTED__", NULL);

    tcc_define_symbol(s, "__SIZEOF_LONG_LONG__", "8");

    /* target defines */
#if defined(TCC_TARGET_I386)
    tcc_define_symbol(s, "__i386__", NULL);
    tcc_define_symbol(s, "__i386", NULL);
    tcc_define_symbol(s, "i386", NULL);
#elif defined(TCC_TARGET_X86_64)
    tcc_define_symbol(s, "__x86_64__", NULL);
#elif defined(TCC_TARGET_ARM)
    tcc_define_symbol(s, "__ARM_ARCH_4__", NULL);
    tcc_define_symbol(s, "__arm_elf__", NULL);
    tcc_define_symbol(s, "__arm_elf", NULL);
    tcc_define_symbol(s, "arm_elf", NULL);
    tcc_define_symbol(s, "__arm__", NULL);
    tcc_define_symbol(s, "__arm", NULL);
    tcc_define_symbol(s, "arm", NULL);
    tcc_define_symbol(s, "__APCS_32__", NULL);
    tcc_define_symbol(s, "__ARMEL__", NULL);
#if defined(TCC_ARM_EABI)
    tcc_define_symbol(s, "__ARM_EABI__", NULL);
#endif
#if defined(TCC_ARM_HARDFLOAT)
    s->float_abi = ARM_HARD_FLOAT;
    tcc_define_symbol(s, "__ARM_PCS_VFP", NULL);
#else
    s->float_abi = ARM_SOFTFP_FLOAT;
#endif
#elif defined(TCC_TARGET_ARM64)
    tcc_define_symbol(s, "__aarch64__", NULL);
#endif

#ifdef TCC_TARGET_PE
    tcc_define_symbol(s, "_WIN32", NULL);
# ifdef TCC_TARGET_X86_64
    tcc_define_symbol(s, "_WIN64", NULL);
# endif
#else
    tcc_define_symbol(s, "__unix__", NULL);
    tcc_define_symbol(s, "__unix", NULL);
    tcc_define_symbol(s, "unix", NULL);
# if defined(__linux__)
    tcc_define_symbol(s, "__linux__", NULL);
    tcc_define_symbol(s, "__linux", NULL);
# endif
# if defined(__FreeBSD__)
    tcc_define_symbol(s, "__FreeBSD__", "__FreeBSD__");
    /* No 'Thread Storage Local' on FreeBSD with tcc */
    tcc_define_symbol(s, "__NO_TLS", NULL);
# endif
# if defined(__FreeBSD_kernel__)
    tcc_define_symbol(s, "__FreeBSD_kernel__", NULL);
# endif
#endif
# if defined(__NetBSD__)
    tcc_define_symbol(s, "__NetBSD__", "__NetBSD__");
# endif
# if defined(__OpenBSD__)
    tcc_define_symbol(s, "__OpenBSD__", "__OpenBSD__");
# endif

    /* TinyCC & gcc defines */
#if defined(TCC_TARGET_PE) && PTR_SIZE == 8
    /* 64bit Windows. */
    tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long long");
    tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long long");
    tcc_define_symbol(s, "__LLP64__", NULL);
#elif PTR_SIZE == 8
    /* Other 64bit systems. */
    tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long");
    tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long");
    tcc_define_symbol(s, "__LP64__", NULL);
#else
    /* Other 32bit systems. */
    tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long");
    tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long");
    tcc_define_symbol(s, "__ILP32__", NULL);
#endif

#if defined(TCC_MUSL)
    tcc_define_symbol(s, "__builtin_va_list", "void *");
#endif /* TCC_MUSL */

#ifdef TCC_TARGET_PE
    tcc_define_symbol(s, "__WCHAR_TYPE__", "unsigned short");
    tcc_define_symbol(s, "__WINT_TYPE__", "unsigned short");
#else
    tcc_define_symbol(s, "__WCHAR_TYPE__", "int");
    /* wint_t is unsigned int by default, but (signed) int on BSDs
       and unsigned short on windows.  Other OSes might have still
       other conventions, sigh.  */
# if defined(__FreeBSD__) || defined (__FreeBSD_kernel__) \
  || defined(__NetBSD__) || defined(__OpenBSD__)
    tcc_define_symbol(s, "__WINT_TYPE__", "int");
#  ifdef __FreeBSD__
    /* define __GNUC__ to have some useful stuff from sys/cdefs.h
       that are unconditionally used in FreeBSDs other system headers :/ */
    tcc_define_symbol(s, "__GNUC__", "2");
    tcc_define_symbol(s, "__GNUC_MINOR__", "7");
    tcc_define_symbol(s, "__builtin_alloca", "alloca");
#  endif
# else
    tcc_define_symbol(s, "__WINT_TYPE__", "unsigned int");
    /* glibc defines */
    tcc_define_symbol(s, "__REDIRECT(name, proto, alias)",
        "name proto __asm__ (#alias)");
    tcc_define_symbol(s, "__REDIRECT_NTH(name, proto, alias)",
        "name proto __asm__ (#alias) __THROW");
# endif
    /* Some GCC builtins that are simple to express as macros.  */
    tcc_define_symbol(s, "__builtin_extract_return_addr(x)", "x");
#endif /* ndef TCC_TARGET_PE */
    return s;
}

LIBTCCAPI void tcc_delete(TCCState *s1)
{
    tcc_cleanup();

    /* free sections */
    tccelf_delete(s1);

    /* free library paths */
    dynarray_reset(&s1->library_paths, &s1->nb_library_paths);
    dynarray_reset(&s1->crt_paths, &s1->nb_crt_paths);

    /* free include paths */
    dynarray_reset(&s1->cached_includes, &s1->nb_cached_includes);
    dynarray_reset(&s1->include_paths, &s1->nb_include_paths);
    dynarray_reset(&s1->sysinclude_paths, &s1->nb_sysinclude_paths);
    dynarray_reset(&s1->cmd_include_files, &s1->nb_cmd_include_files);

    tcc_free(s1->tcc_lib_path);
    tcc_free(s1->soname);
    tcc_free(s1->rpath);
    tcc_free(s1->init_symbol);
    tcc_free(s1->fini_symbol);
    tcc_free(s1->outfile);
    tcc_free(s1->deps_outfile);
    dynarray_reset(&s1->files, &s1->nb_files);
    dynarray_reset(&s1->target_deps, &s1->nb_target_deps);
    dynarray_reset(&s1->pragma_libs, &s1->nb_pragma_libs);
    dynarray_reset(&s1->argv, &s1->argc);

#ifdef TCC_IS_NATIVE
    /* free runtime memory */
    tcc_run_free(s1);
#endif

    tcc_free(s1);
    if (0 == --nb_states)
        tcc_memcheck();
}

LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type)
{
    s->output_type = output_type;

    /* always elf for objects */
    if (output_type == TCC_OUTPUT_OBJ)
        s->output_format = TCC_OUTPUT_FORMAT_ELF;

    if (s->char_is_unsigned)
        tcc_define_symbol(s, "__CHAR_UNSIGNED__", NULL);

    if (!s->nostdinc) {
        /* default include paths */
        /* -isystem paths have already been handled */
        tcc_add_sysinclude_path(s, CONFIG_TCC_SYSINCLUDEPATHS);
    }

#ifdef CONFIG_TCC_BCHECK
    if (s->do_bounds_check) {
        /* if bound checking, then add corresponding sections */
        tccelf_bounds_new(s);
        /* define symbol */
        tcc_define_symbol(s, "__BOUNDS_CHECKING_ON", NULL);
    }
#endif
    if (s->do_debug) {
        /* add debug sections */
        tccelf_stab_new(s);
    }

    tcc_add_library_path(s, CONFIG_TCC_LIBPATHS);

#ifdef TCC_TARGET_PE
# ifdef _WIN32
    if (!s->nostdlib && output_type != TCC_OUTPUT_OBJ)
        tcc_add_systemdir(s);
# endif
#else
    /* paths for crt objects */
    tcc_split_path(s, &s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX);
    /* add libc crt1/crti objects */
    if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) &&
        !s->nostdlib) {
        if (output_type != TCC_OUTPUT_DLL)
            tcc_add_crt(s, "crt1.o");
        tcc_add_crt(s, "crti.o");
    }
#endif
    return 0;
}

LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname)
{
    tcc_split_path(s, &s->include_paths, &s->nb_include_paths, pathname);
    return 0;
}

LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname)
{
    tcc_split_path(s, &s->sysinclude_paths, &s->nb_sysinclude_paths, pathname);
    return 0;
}

ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
{
    int ret, filetype;

    filetype = flags & 0x0F;
    if (filetype == 0) {
        /* use a file extension to detect a filetype */
        const char *ext = tcc_fileextension(filename);
        if (ext[0]) {
            ext++;
            if (!strcmp(ext, "S"))
                filetype = AFF_TYPE_ASMPP;
            else if (!strcmp(ext, "s"))
                filetype = AFF_TYPE_ASM;
            else if (!PATHCMP(ext, "c") || !PATHCMP(ext, "i"))
                filetype = AFF_TYPE_C;
            else
                filetype = AFF_TYPE_BIN;
        } else {
            filetype = AFF_TYPE_C;
        }
    }

    /* open the file */
    ret = tcc_open(s1, filename);
    if (ret < 0) {
        if (flags & AFF_PRINT_ERROR)
            tcc_error_noabort("file '%s' not found", filename);
        return ret;
    }

    /* update target deps */
    dynarray_add(&s1->target_deps, &s1->nb_target_deps,
            tcc_strdup(filename));

    parse_flags = 0;
    /* if .S file, define __ASSEMBLER__ like gcc does */
    if (filetype == AFF_TYPE_ASM || filetype == AFF_TYPE_ASMPP) {
        tcc_define_symbol(s1, "__ASSEMBLER__", NULL);
        parse_flags = PARSE_FLAG_ASM_FILE;
    }

    if (flags & AFF_PREPROCESS) {
        ret = tcc_preprocess(s1);
    } else if (filetype == AFF_TYPE_C) {
        ret = tcc_compile(s1);
#ifdef CONFIG_TCC_ASM
    } else if (filetype == AFF_TYPE_ASMPP) {
        /* non preprocessed assembler */
        ret = tcc_assemble(s1, 1);
    } else if (filetype == AFF_TYPE_ASM) {
        /* preprocessed assembler */
        ret = tcc_assemble(s1, 0);
#endif
    } else {
        ElfW(Ehdr) ehdr;
        int fd, obj_type;

        fd = file->fd;
        obj_type = tcc_object_type(fd, &ehdr);
        lseek(fd, 0, SEEK_SET);

        /* do not display line number if error */
        file->line_num = 0;

#ifdef TCC_TARGET_MACHO
        if (0 == obj_type && 0 == strcmp(tcc_fileextension(filename), "dylib"))
            obj_type = AFF_BINTYPE_DYN;
#endif

        switch (obj_type) {
        case AFF_BINTYPE_REL:
            ret = tcc_load_object_file(s1, fd, 0);
            break;
#ifndef TCC_TARGET_PE
        case AFF_BINTYPE_DYN:
            if (s1->output_type == TCC_OUTPUT_MEMORY) {
                ret = 0;
#ifdef TCC_IS_NATIVE
                if (NULL == dlopen(filename, RTLD_GLOBAL | RTLD_LAZY))
                    ret = -1;
#endif
            } else {
                ret = tcc_load_dll(s1, fd, filename,
                                   (flags & AFF_REFERENCED_DLL) != 0);
            }
            break;
#endif
        case AFF_BINTYPE_AR:
            ret = tcc_load_archive(s1, fd);
            break;
#ifdef TCC_TARGET_COFF
        case AFF_BINTYPE_C67:
            ret = tcc_load_coff(s1, fd);
            break;
#endif
        default:
#ifdef TCC_TARGET_PE
            ret = pe_load_file(s1, filename, fd);
#else
            /* as GNU ld, consider it is an ld script if not recognized */
            ret = tcc_load_ldscript(s1);
#endif
            if (ret < 0)
                tcc_error_noabort("unrecognized file type");
            break;
        }
    }
    tcc_close();
    return ret;
}

LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename)
{
    if (s->output_type == TCC_OUTPUT_PREPROCESS)
        return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR | AFF_PREPROCESS | s->filetype);
    else
        return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR | s->filetype);
}

LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname)
{
    tcc_split_path(s, &s->library_paths, &s->nb_library_paths, pathname);
    return 0;
}

static int tcc_add_library_internal(TCCState *s, const char *fmt,
    const char *filename, int flags, char **paths, int nb_paths)
{
    char buf[1024];
    int i;

    for(i = 0; i < nb_paths; i++) {
        snprintf(buf, sizeof(buf), fmt, paths[i], filename);
        if (tcc_add_file_internal(s, buf, flags | AFF_TYPE_BIN) == 0)
            return 0;
    }
    return -1;
}

/* find and load a dll. Return non zero if not found */
/* XXX: add '-rpath' option support ? */
ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags)
{
    return tcc_add_library_internal(s, "%s/%s", filename, flags,
        s->library_paths, s->nb_library_paths);
}

ST_FUNC int tcc_add_crt(TCCState *s, const char *filename)
{
    if (-1 == tcc_add_library_internal(s, "%s/%s",
        filename, 0, s->crt_paths, s->nb_crt_paths))
        tcc_error_noabort("file '%s' not found", filename);
    return 0;
}

/* the library name is the same as the argument of the '-l' option */
LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname)
{
#if defined TCC_TARGET_PE
    const char *libs[] = { "%s/%s.def", "%s/lib%s.def", "%s/%s.dll", "%s/lib%s.dll", "%s/lib%s.a", NULL };
    const char **pp = s->static_link ? libs + 4 : libs;
#elif defined TCC_TARGET_MACHO
    const char *libs[] = { "%s/lib%s.dylib", "%s/lib%s.a", NULL };
    const char **pp = s->static_link ? libs + 1 : libs;
#else
    const char *libs[] = { "%s/lib%s.so", "%s/lib%s.a", NULL };
    const char **pp = s->static_link ? libs + 1 : libs;
#endif
    while (*pp) {
        if (0 == tcc_add_library_internal(s, *pp,
            libraryname, 0, s->library_paths, s->nb_library_paths))
            return 0;
        ++pp;
    }
    return -1;
}

PUB_FUNC int tcc_add_library_err(TCCState *s, const char *libname)
{
    int ret = tcc_add_library(s, libname);
    if (ret < 0)
        tcc_error_noabort("library '%s' not found", libname);
    return ret;
}

/* handle #pragma comment(lib,) */
ST_FUNC void tcc_add_pragma_libs(TCCState *s1)
{
    int i;
    for (i = 0; i < s1->nb_pragma_libs; i++)
        tcc_add_library_err(s1, s1->pragma_libs[i]);
}

LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val)
{
#ifdef TCC_TARGET_PE
    /* On x86_64 'val' might not be reachable with a 32bit offset.
       So it is handled here as if it were in a DLL. */
    pe_putimport(s, 0, name, (uintptr_t)val);
#else
    set_elf_sym(symtab_section, (uintptr_t)val, 0,
        ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
        SHN_ABS, name);
#endif
    return 0;
}

LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path)
{
    tcc_free(s->tcc_lib_path);
    s->tcc_lib_path = tcc_strdup(path);
}

#define WD_ALL    0x0001 /* warning is activated when using -Wall */
#define FD_INVERT 0x0002 /* invert value before storing */

typedef struct FlagDef {
    uint16_t offset;
    uint16_t flags;
    const char *name;
} FlagDef;

static int no_flag(const char **pp)
{
    const char *p = *pp;
    if (*p != 'n' || *++p != 'o' || *++p != '-')
        return 0;
    *pp = p + 1;
    return 1;
}

ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, const char *name)
{
    int value, ret;
    const FlagDef *p;
    const char *r;

    value = 1;
    r = name;
    if (no_flag(&r))
        value = 0;

    for (ret = -1, p = flags; p->name; ++p) {
        if (ret) {
            if (strcmp(r, p->name))
                continue;
        } else {
            if (0 == (p->flags & WD_ALL))
                continue;
        }
        if (p->offset) {
            *(int*)((char *)s + p->offset) =
                p->flags & FD_INVERT ? !value : value;
            if (ret)
                return 0;
        } else {
            ret = 0;
        }
    }
    return ret;
}

static int strstart(const char *val, const char **str)
{
    const char *p, *q;
    p = *str;
    q = val;
    while (*q) {
        if (*p != *q)
            return 0;
        p++;
        q++;
    }
    *str = p;
    return 1;
}

/* Like strstart, but automatically takes into account that ld options can
 *
 * - start with double or single dash (e.g. '--soname' or '-soname')
 * - arguments can be given as separate or after '=' (e.g. '-Wl,-soname,x.so'
 *   or '-Wl,-soname=x.so')
 *
 * you provide `val` always in 'option[=]' form (no leading -)
 */
static int link_option(const char *str, const char *val, const char **ptr)
{
    const char *p, *q;
    int ret;

    /* there should be 1 or 2 dashes */
    if (*str++ != '-')
        return 0;
    if (*str == '-')
        str++;

    /* then str & val should match (potentially up to '=') */
    p = str;
    q = val;

    ret = 1;
    if (q[0] == '?') {
        ++q;
        if (no_flag(&p))
            ret = -1;
    }

    while (*q != '\0' && *q != '=') {
        if (*p != *q)
            return 0;
        p++;
        q++;
    }

    /* '=' near eos means ',' or '=' is ok */
    if (*q == '=') {
        if (*p == 0)
            *ptr = p;
        if (*p != ',' && *p != '=')
            return 0;
        p++;
    } else if (*p) {
        return 0;
    }
    *ptr = p;
    return ret;
}

static const char *skip_linker_arg(const char **str)
{
    const char *s1 = *str;
    const char *s2 = strchr(s1, ',');
    *str = s2 ? s2++ : (s2 = s1 + strlen(s1));
    return s2;
}

static void copy_linker_arg(char **pp, const char *s, int sep)
{
    const char *q = s;
    char *p = *pp;
    int l = 0;
    if (p && sep)
        p[l = strlen(p)] = sep, ++l;
    skip_linker_arg(&q);
    pstrncpy(l + (*pp = tcc_realloc(p, q - s + l + 1)), s, q - s);
}

/* set linker options */
static int tcc_set_linker(TCCState *s, const char *option)
{
    while (*option) {

        const char *p = NULL;
        char *end = NULL;
        int ignoring = 0;
        int ret;

        if (link_option(option, "Bsymbolic", &p)) {
            s->symbolic = 1;
        } else if (link_option(option, "nostdlib", &p)) {
            s->nostdlib = 1;
        } else if (link_option(option, "fini=", &p)) {
            copy_linker_arg(&s->fini_symbol, p, 0);
            ignoring = 1;
        } else if (link_option(option, "image-base=", &p)
                || link_option(option, "Ttext=", &p)) {
            s->text_addr = strtoull(p, &end, 16);
            s->has_text_addr = 1;
        } else if (link_option(option, "init=", &p)) {
            copy_linker_arg(&s->init_symbol, p, 0);
            ignoring = 1;
        } else if (link_option(option, "oformat=", &p)) {
#if defined(TCC_TARGET_PE)
            if (strstart("pe-", &p)) {
#elif PTR_SIZE == 8
            if (strstart("elf64-", &p)) {
#else
            if (strstart("elf32-", &p)) {
#endif
                s->output_format = TCC_OUTPUT_FORMAT_ELF;
            } else if (!strcmp(p, "binary")) {
                s->output_format = TCC_OUTPUT_FORMAT_BINARY;
#ifdef TCC_TARGET_COFF
            } else if (!strcmp(p, "coff")) {
                s->output_format = TCC_OUTPUT_FORMAT_COFF;
#endif
            } else
                goto err;

        } else if (link_option(option, "as-needed", &p)) {
            ignoring = 1;
        } else if (link_option(option, "O", &p)) {
            ignoring = 1;
        } else if (link_option(option, "export-all-symbols", &p)) {
            s->rdynamic = 1;
        } else if (link_option(option, "rpath=", &p)) {
            copy_linker_arg(&s->rpath, p, ':');
        } else if (link_option(option, "enable-new-dtags", &p)) {
            s->enable_new_dtags = 1;
        } else if (link_option(option, "section-alignment=", &p)) {
            s->section_align = strtoul(p, &end, 16);
        } else if (link_option(option, "soname=", &p)) {
            copy_linker_arg(&s->soname, p, 0);
#ifdef TCC_TARGET_PE
        } else if (link_option(option, "large-address-aware", &p)) {
            s->pe_characteristics |= 0x20;
        } else if (link_option(option, "file-alignment=", &p)) {
            s->pe_file_align = strtoul(p, &end, 16);
        } else if (link_option(option, "stack=", &p)) {
            s->pe_stack_size = strtoul(p, &end, 10);
        } else if (link_option(option, "subsystem=", &p)) {
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
            if (!strcmp(p, "native")) {
                s->pe_subsystem = 1;
            } else if (!strcmp(p, "console")) {
                s->pe_subsystem = 3;
            } else if (!strcmp(p, "gui") || !strcmp(p, "windows")) {
                s->pe_subsystem = 2;
            } else if (!strcmp(p, "posix")) {
                s->pe_subsystem = 7;
            } else if (!strcmp(p, "efiapp")) {
                s->pe_subsystem = 10;
            } else if (!strcmp(p, "efiboot")) {
                s->pe_subsystem = 11;
            } else if (!strcmp(p, "efiruntime")) {
                s->pe_subsystem = 12;
            } else if (!strcmp(p, "efirom")) {
                s->pe_subsystem = 13;
#elif defined(TCC_TARGET_ARM)
            if (!strcmp(p, "wince")) {
                s->pe_subsystem = 9;
#endif
            } else
                goto err;
#endif
        } else if (ret = link_option(option, "?whole-archive", &p), ret) {
            s->alacarte_link = ret < 0;
        } else if (p) {
            return 0;
        } else {
    err:
            tcc_error("unsupported linker option '%s'", option);
        }

        if (ignoring && s->warn_unsupported)
            tcc_warning("unsupported linker option '%s'", option);

        option = skip_linker_arg(&p);
    }
    return 1;
}

typedef struct TCCOption {
    const char *name;
    uint16_t index;
    uint16_t flags;
} TCCOption;

enum {
    TCC_OPTION_HELP,
    TCC_OPTION_HELP2,
    TCC_OPTION_v,
    TCC_OPTION_I,
    TCC_OPTION_D,
    TCC_OPTION_U,
    TCC_OPTION_P,
    TCC_OPTION_L,
    TCC_OPTION_B,
    TCC_OPTION_l,
    TCC_OPTION_bench,
    TCC_OPTION_bt,
    TCC_OPTION_b,
    TCC_OPTION_g,
    TCC_OPTION_c,
    TCC_OPTION_dumpversion,
    TCC_OPTION_d,
    TCC_OPTION_static,
    TCC_OPTION_std,
    TCC_OPTION_shared,
    TCC_OPTION_soname,
    TCC_OPTION_o,
    TCC_OPTION_r,
    TCC_OPTION_s,
    TCC_OPTION_traditional,
    TCC_OPTION_Wl,
    TCC_OPTION_Wp,
    TCC_OPTION_W,
    TCC_OPTION_O,
    TCC_OPTION_mfloat_abi,
    TCC_OPTION_m,
    TCC_OPTION_f,
    TCC_OPTION_isystem,
    TCC_OPTION_iwithprefix,
    TCC_OPTION_include,
    TCC_OPTION_nostdinc,
    TCC_OPTION_nostdlib,
    TCC_OPTION_print_search_dirs,
    TCC_OPTION_rdynamic,
    TCC_OPTION_param,
    TCC_OPTION_pedantic,
    TCC_OPTION_pthread,
    TCC_OPTION_run,
    TCC_OPTION_w,
    TCC_OPTION_pipe,
    TCC_OPTION_E,
    TCC_OPTION_MD,
    TCC_OPTION_MF,
    TCC_OPTION_x,
    TCC_OPTION_ar,
    TCC_OPTION_impdef
};

#define TCC_OPTION_HAS_ARG 0x0001
#define TCC_OPTION_NOSEP   0x0002 /* cannot have space before option and arg */

static const TCCOption tcc_options[] = {
    { "h", TCC_OPTION_HELP, 0 },
    { "-help", TCC_OPTION_HELP, 0 },
    { "?", TCC_OPTION_HELP, 0 },
    { "hh", TCC_OPTION_HELP2, 0 },
    { "v", TCC_OPTION_v, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "I", TCC_OPTION_I, TCC_OPTION_HAS_ARG },
    { "D", TCC_OPTION_D, TCC_OPTION_HAS_ARG },
    { "U", TCC_OPTION_U, TCC_OPTION_HAS_ARG },
    { "P", TCC_OPTION_P, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "L", TCC_OPTION_L, TCC_OPTION_HAS_ARG },
    { "B", TCC_OPTION_B, TCC_OPTION_HAS_ARG },
    { "l", TCC_OPTION_l, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "bench", TCC_OPTION_bench, 0 },
#ifdef CONFIG_TCC_BACKTRACE
    { "bt", TCC_OPTION_bt, TCC_OPTION_HAS_ARG },
#endif
#ifdef CONFIG_TCC_BCHECK
    { "b", TCC_OPTION_b, 0 },
#endif
    { "g", TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "c", TCC_OPTION_c, 0 },
    { "dumpversion", TCC_OPTION_dumpversion, 0},
    { "d", TCC_OPTION_d, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "static", TCC_OPTION_static, 0 },
    { "std", TCC_OPTION_std, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "shared", TCC_OPTION_shared, 0 },
    { "soname", TCC_OPTION_soname, TCC_OPTION_HAS_ARG },
    { "o", TCC_OPTION_o, TCC_OPTION_HAS_ARG },
    { "-param", TCC_OPTION_param, TCC_OPTION_HAS_ARG },
    { "pedantic", TCC_OPTION_pedantic, 0},
    { "pthread", TCC_OPTION_pthread, 0},
    { "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "rdynamic", TCC_OPTION_rdynamic, 0 },
    { "r", TCC_OPTION_r, 0 },
    { "s", TCC_OPTION_s, 0 },
    { "traditional", TCC_OPTION_traditional, 0 },
    { "Wl,", TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "Wp,", TCC_OPTION_Wp, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "W", TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "O", TCC_OPTION_O, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
#ifdef TCC_TARGET_ARM
    { "mfloat-abi", TCC_OPTION_mfloat_abi, TCC_OPTION_HAS_ARG },
#endif
    { "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "isystem", TCC_OPTION_isystem, TCC_OPTION_HAS_ARG },
    { "iwithprefix", TCC_OPTION_iwithprefix, TCC_OPTION_HAS_ARG },
    { "include", TCC_OPTION_include, TCC_OPTION_HAS_ARG },
    { "nostdinc", TCC_OPTION_nostdinc, 0 },
    { "nostdlib", TCC_OPTION_nostdlib, 0 },
    { "print-search-dirs", TCC_OPTION_print_search_dirs, 0 },
    { "w", TCC_OPTION_w, 0 },
    { "pipe", TCC_OPTION_pipe, 0},
    { "E", TCC_OPTION_E, 0},
    { "MD", TCC_OPTION_MD, 0},
    { "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG },
    { "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG },
    { "ar", TCC_OPTION_ar, 0},
#ifdef TCC_TARGET_PE
    { "impdef", TCC_OPTION_impdef, 0},
#endif
    { NULL, 0, 0 },
};

static const FlagDef options_W[] = {
    { 0, 0, "all" },
    { offsetof(TCCState, warn_unsupported), 0, "unsupported" },
    { offsetof(TCCState, warn_write_strings), 0, "write-strings" },
    { offsetof(TCCState, warn_error), 0, "error" },
    { offsetof(TCCState, warn_gcc_compat), 0, "gcc-compat" },
    { offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
      "implicit-function-declaration" },
    { 0, 0, NULL }
};

static const FlagDef options_f[] = {
    { offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" },
    { offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" },
    { offsetof(TCCState, nocommon), FD_INVERT, "common" },
    { offsetof(TCCState, leading_underscore), 0, "leading-underscore" },
    { offsetof(TCCState, ms_extensions), 0, "ms-extensions" },
    { offsetof(TCCState, dollars_in_identifiers), 0, "dollars-in-identifiers" },
    { 0, 0, NULL }
};

static const FlagDef options_m[] = {
    { offsetof(TCCState, ms_bitfields), 0, "ms-bitfields" },
#ifdef TCC_TARGET_X86_64
    { offsetof(TCCState, nosse), FD_INVERT, "sse" },
#endif
    { 0, 0, NULL }
};

static void parse_option_D(TCCState *s1, const char *optarg)
{
    char *sym = tcc_strdup(optarg);
    char *value = strchr(sym, '=');
    if (value)
        *value++ = '\0';
    tcc_define_symbol(s1, sym, value);
    tcc_free(sym);
}

static void args_parser_add_file(TCCState *s, const char* filename, int filetype)
{
    struct filespec *f = tcc_malloc(sizeof *f + strlen(filename));
    f->type = filetype;
    f->alacarte = s->alacarte_link;
    strcpy(f->name, filename);
    dynarray_add(&s->files, &s->nb_files, f);
}

static int args_parser_make_argv(const char *r, int *argc, char ***argv)
{
    int ret = 0, q, c;
    CString str;
    for(;;) {
        while (c = (unsigned char)*r, c && c <= ' ')
        ++r;
        if (c == 0)
            break;
        q = 0;
        cstr_new(&str);
        while (c = (unsigned char)*r, c) {
            ++r;
            if (c == '\\' && (*r == '"' || *r == '\\')) {
                c = *r++;
            } else if (c == '"') {
                q = !q;
                continue;
            } else if (q == 0 && c <= ' ') {
                break;
            }
            cstr_ccat(&str, c);
        }
        cstr_ccat(&str, 0);
        //printf("<%s>\n", str.data), fflush(stdout);
        dynarray_add(argv, argc, tcc_strdup(str.data));
        cstr_free(&str);
        ++ret;
    }
    return ret;
}

/* read list file */
static void args_parser_listfile(TCCState *s,
    const char *filename, int optind, int *pargc, char ***pargv)
{
    int fd, i;
    size_t len;
    char *p;
    int argc = 0;
    char **argv = NULL;

    fd = open(filename, O_RDONLY | O_BINARY);
    if (fd < 0)
        tcc_error("listfile '%s' not found", filename);

    len = lseek(fd, 0, SEEK_END);
    p = tcc_malloc(len + 1), p[len] = 0;
    lseek(fd, 0, SEEK_SET), read(fd, p, len), close(fd);

    for (i = 0; i < *pargc; ++i)
        if (i == optind)
            args_parser_make_argv(p, &argc, &argv);
        else
            dynarray_add(&argv, &argc, tcc_strdup((*pargv)[i]));

    tcc_free(p);
    dynarray_reset(&s->argv, &s->argc);
    *pargc = s->argc = argc, *pargv = s->argv = argv;
}

PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv, int optind)
{
    const TCCOption *popt;
    const char *optarg, *r;
    const char *run = NULL;
    int last_o = -1;
    int x;
    CString linker_arg; /* collect -Wl options */
    char buf[1024];
    int tool = 0, arg_start = 0, noaction = optind;
    char **argv = *pargv;
    int argc = *pargc;

    cstr_new(&linker_arg);

    while (optind < argc) {
        r = argv[optind];
        if (r[0] == '@' && r[1] != '\0') {
            args_parser_listfile(s, r + 1, optind, &argc, &argv);
        continue;
        }
        optind++;
        if (tool) {
            if (r[0] == '-' && r[1] == 'v' && r[2] == 0)
                ++s->verbose;
            continue;
        }
reparse:
        if (r[0] != '-' || r[1] == '\0') {
            if (r[0] != '@') /* allow "tcc file(s) -run @ args ..." */
                args_parser_add_file(s, r, s->filetype);
            if (run) {
                tcc_set_options(s, run);
                arg_start = optind - 1;
                break;
            }
            continue;
        }

        /* find option in table */
        for(popt = tcc_options; ; ++popt) {
            const char *p1 = popt->name;
            const char *r1 = r + 1;
            if (p1 == NULL)
                tcc_error("invalid option -- '%s'", r);
            if (!strstart(p1, &r1))
                continue;
            optarg = r1;
            if (popt->flags & TCC_OPTION_HAS_ARG) {
                if (*r1 == '\0' && !(popt->flags & TCC_OPTION_NOSEP)) {
                    if (optind >= argc)
                arg_err:
                        tcc_error("argument to '%s' is missing", r);
                    optarg = argv[optind++];
                }
            } else if (*r1 != '\0')
                continue;
            break;
        }

        switch(popt->index) {
        case TCC_OPTION_HELP:
            return OPT_HELP;
        case TCC_OPTION_HELP2:
            return OPT_HELP2;
        case TCC_OPTION_I:
            tcc_add_include_path(s, optarg);
            break;
        case TCC_OPTION_D:
            parse_option_D(s, optarg);
            break;
        case TCC_OPTION_U:
            tcc_undefine_symbol(s, optarg);
            break;
        case TCC_OPTION_L:
            tcc_add_library_path(s, optarg);
            break;
        case TCC_OPTION_B:
            /* set tcc utilities path (mainly for tcc development) */
            tcc_set_lib_path(s, optarg);
            break;
        case TCC_OPTION_l:
            args_parser_add_file(s, optarg, AFF_TYPE_LIB);
            s->nb_libraries++;
            break;
        case TCC_OPTION_pthread:
            parse_option_D(s, "_REENTRANT");
            s->option_pthread = 1;
            break;
        case TCC_OPTION_bench:
            s->do_bench = 1;
            break;
#ifdef CONFIG_TCC_BACKTRACE
        case TCC_OPTION_bt:
            tcc_set_num_callers(atoi(optarg));
            break;
#endif
#ifdef CONFIG_TCC_BCHECK
        case TCC_OPTION_b:
            s->do_bounds_check = 1;
            s->do_debug = 1;
            break;
#endif
        case TCC_OPTION_g:
            s->do_debug = 1;
            break;
        case TCC_OPTION_c:
            x = TCC_OUTPUT_OBJ;
        set_output_type:
            if (s->output_type)
                tcc_warning("-%s: overriding compiler action already specified", popt->name);
            s->output_type = x;
            break;
        case TCC_OPTION_d:
            if (*optarg == 'D')
                s->dflag = 3;
            else if (*optarg == 'M')
                s->dflag = 7;
            else
                goto unsupported_option;
            break;
        case TCC_OPTION_static:
            s->static_link = 1;
            break;
        case TCC_OPTION_std:
            /* silently ignore, a current purpose:
               allow to use a tcc as a reference compiler for "make test" */
            break;
        case TCC_OPTION_shared:
            x = TCC_OUTPUT_DLL;
            goto set_output_type;
        case TCC_OPTION_soname:
            s->soname = tcc_strdup(optarg);
            break;
        case TCC_OPTION_o:
            if (s->outfile) {
                tcc_warning("multiple -o option");
                tcc_free(s->outfile);
            }
            s->outfile = tcc_strdup(optarg);
            break;
        case TCC_OPTION_r:
            /* generate a .o merging several output files */
            s->option_r = 1;
            x = TCC_OUTPUT_OBJ;
            goto set_output_type;
        case TCC_OPTION_isystem:
            tcc_add_sysinclude_path(s, optarg);
            break;
        case TCC_OPTION_iwithprefix:
            snprintf(buf, sizeof buf, "{B}/%s", optarg);
            tcc_add_sysinclude_path(s, buf);
            break;
    case TCC_OPTION_include:
        dynarray_add(&s->cmd_include_files,
             &s->nb_cmd_include_files, tcc_strdup(optarg));
        break;
        case TCC_OPTION_nostdinc:
            s->nostdinc = 1;
            break;
        case TCC_OPTION_nostdlib:
            s->nostdlib = 1;
            break;
        case TCC_OPTION_run:
#ifndef TCC_IS_NATIVE
            tcc_error("-run is not available in a cross compiler");
#endif
            run = optarg;
            x = TCC_OUTPUT_MEMORY;
            goto set_output_type;
        case TCC_OPTION_v:
            do ++s->verbose; while (*optarg++ == 'v');
            ++noaction;
            break;
        case TCC_OPTION_f:
            if (set_flag(s, options_f, optarg) < 0)
                goto unsupported_option;
            break;
#ifdef TCC_TARGET_ARM
        case TCC_OPTION_mfloat_abi:
            /* tcc doesn't support soft float yet */
            if (!strcmp(optarg, "softfp")) {
                s->float_abi = ARM_SOFTFP_FLOAT;
                tcc_undefine_symbol(s, "__ARM_PCS_VFP");
            } else if (!strcmp(optarg, "hard"))
                s->float_abi = ARM_HARD_FLOAT;
            else
                tcc_error("unsupported float abi '%s'", optarg);
            break;
#endif
        case TCC_OPTION_m:
            if (set_flag(s, options_m, optarg) < 0) {
                if (x = atoi(optarg), x != 32 && x != 64)
                    goto unsupported_option;
                if (PTR_SIZE != x/8)
                    return x;
                ++noaction;
            }
            break;
        case TCC_OPTION_W:
            if (set_flag(s, options_W, optarg) < 0)
                goto unsupported_option;
            break;
        case TCC_OPTION_w:
            s->warn_none = 1;
            break;
        case TCC_OPTION_rdynamic:
            s->rdynamic = 1;
            break;
        case TCC_OPTION_Wl:
            if (linker_arg.size)
                --linker_arg.size, cstr_ccat(&linker_arg, ',');
            cstr_cat(&linker_arg, optarg, 0);
            if (tcc_set_linker(s, linker_arg.data))
                cstr_free(&linker_arg);
            break;
    case TCC_OPTION_Wp:
        r = optarg;
        goto reparse;
        case TCC_OPTION_E:
            x = TCC_OUTPUT_PREPROCESS;
            goto set_output_type;
        case TCC_OPTION_P:
            s->Pflag = atoi(optarg) + 1;
            break;
        case TCC_OPTION_MD:
            s->gen_deps = 1;
            break;
        case TCC_OPTION_MF:
            s->deps_outfile = tcc_strdup(optarg);
            break;
        case TCC_OPTION_dumpversion:
            printf ("%s\n", TCC_VERSION);
            exit(0);
            break;
        case TCC_OPTION_x:
            if (*optarg == 'c')
                s->filetype = AFF_TYPE_C;
            else if (*optarg == 'a')
                s->filetype = AFF_TYPE_ASMPP;
            else if (*optarg == 'n')
                s->filetype = AFF_TYPE_NONE;
            else
                tcc_warning("unsupported language '%s'", optarg);
            break;
        case TCC_OPTION_O:
            last_o = atoi(optarg);
            break;
        case TCC_OPTION_print_search_dirs:
            x = OPT_PRINT_DIRS;
            goto extra_action;
        case TCC_OPTION_impdef:
            x = OPT_IMPDEF;
            goto extra_action;
        case TCC_OPTION_ar:
            x = OPT_AR;
        extra_action:
            arg_start = optind - 1;
            if (arg_start != noaction)
                tcc_error("cannot parse %s here", r);
            tool = x;
            break;
        case TCC_OPTION_traditional:
        case TCC_OPTION_pedantic:
        case TCC_OPTION_pipe:
        case TCC_OPTION_s:
            /* ignored */
            break;
        default:
unsupported_option:
            if (s->warn_unsupported)
                tcc_warning("unsupported option '%s'", r);
            break;
        }
    }
    if (last_o > 0)
        tcc_define_symbol(s, "__OPTIMIZE__", NULL);
    if (linker_arg.size) {
        r = linker_arg.data;
        goto arg_err;
    }
    *pargc = argc - arg_start;
    *pargv = argv + arg_start;
    if (tool)
        return tool;
    if (optind != noaction)
        return 0;
    if (s->verbose == 2)
        return OPT_PRINT_DIRS;
    if (s->verbose)
        return OPT_V;
    return OPT_HELP;
}

LIBTCCAPI void tcc_set_options(TCCState *s, const char *r)
{
    char **argv = NULL;
    int argc = 0;
    args_parser_make_argv(r, &argc, &argv);
    tcc_parse_args(s, &argc, &argv, 0);
    dynarray_reset(&argv, &argc);
}

PUB_FUNC void tcc_print_stats(TCCState *s, unsigned total_time)
{
    if (total_time < 1)
        total_time = 1;
    if (total_bytes < 1)
        total_bytes = 1;
    fprintf(stderr, "* %d idents, %d lines, %d bytes\n"
                    "* %0.3f s, %u lines/s, %0.1f MB/s\n",
           tok_ident - TOK_IDENT, total_lines, total_bytes,
           (double)total_time/1000,
           (unsigned)total_lines*1000/total_time,
           (double)total_bytes/1000/total_time);
#ifdef MEM_DEBUG
    fprintf(stderr, "* %d bytes memory used\n", mem_max_size);
#endif
}

https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tcc.h

File: ../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tcc.h used in: Step 378, Step 395, Step 407, Step 419, Step 431, Step 443, Step 455
/*
 *  TCC - Tiny C Compiler
 * 
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef _TCC_H
#define _TCC_H

#define _GNU_SOURCE
#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <fcntl.h>
#include <setjmp.h>
#include <time.h>

#ifndef _WIN32
# include <unistd.h>
# include <sys/time.h>
# ifndef CONFIG_TCC_STATIC
#  include <dlfcn.h>
# endif
/* XXX: need to define this to use them in non ISOC99 context */
 extern float strtof (const char *__nptr, char **__endptr);
 extern long double strtold (const char *__nptr, char **__endptr);

#else /* on _WIN32: */
# include <windows.h>
# include <io.h> /* open, close etc. */
# include <direct.h> /* getcwd */
# ifdef __GNUC__
#  include <stdint.h>
# endif
# define inline __inline
# define inp next_inp /* inp is an intrinsic on msvc */
# define snprintf _snprintf
# define vsnprintf _vsnprintf
# ifndef __GNUC__
#  define strtold (long double)strtod
#  define strtof (float)strtod
#  define strtoll _strtoi64
#  define strtoull _strtoui64
# endif
# ifdef LIBTCC_AS_DLL
#  define LIBTCCAPI __declspec(dllexport)
#  define PUB_FUNC LIBTCCAPI
# endif
# ifdef _MSC_VER
#  pragma warning (disable : 4244)  // conversion from 'uint64_t' to 'int', possible loss of data
#  pragma warning (disable : 4267)  // conversion from 'size_t' to 'int', possible loss of data
#  pragma warning (disable : 4996)  // The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
#  pragma warning (disable : 4018)  // signed/unsigned mismatch
#  pragma warning (disable : 4146)  // unary minus operator applied to unsigned type, result still unsigned
#  define ssize_t intptr_t
#  define __attribute__(x) __declspec x
#  define aligned align
# else
# endif
# undef CONFIG_TCC_STATIC
#endif

#ifndef O_BINARY
# define O_BINARY 0
#endif

#ifdef __GNUC__
# define NORETURN __attribute__ ((noreturn))
#elif defined _MSC_VER
# define NORETURN __declspec(noreturn)
#else
# define NORETURN
#endif

#ifdef _WIN32
# define IS_DIRSEP(c) (c == '/' || c == '\\')
# define IS_ABSPATH(p) (IS_DIRSEP(p[0]) || (p[0] && p[1] == ':' && IS_DIRSEP(p[2])))
# define PATHCMP stricmp
#else
# define IS_DIRSEP(c) (c == '/')
# define IS_ABSPATH(p) IS_DIRSEP(p[0])
# define PATHCMP strcmp
#endif

#ifdef TCC_TARGET_PE
#define PATHSEP ';'
#else
#define PATHSEP ':'
#endif

/* -------------------------------------------- */

/* parser debug */
/* #define PARSE_DEBUG */
/* preprocessor debug */
/* #define PP_DEBUG */
/* include file debug */
/* #define INC_DEBUG */
/* memory leak debug */
/* #define MEM_DEBUG */
/* assembler debug */
/* #define ASM_DEBUG */

/* target selection */
/* #define TCC_TARGET_I386   *//* i386 code generator */
/* #define TCC_TARGET_X86_64 *//* x86-64 code generator */
/* #define TCC_TARGET_ARM    *//* ARMv4 code generator */
/* #define TCC_TARGET_ARM64  *//* ARMv8 code generator */
/* #define TCC_TARGET_C67    *//* TMS320C67xx code generator */

/* default target is I386 */
#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_ARM) && \
    !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_C67) && \
    !defined(TCC_TARGET_X86_64)
#define TCC_TARGET_I386
#endif

/* object format selection */
#if defined(TCC_TARGET_C67)
#define TCC_TARGET_COFF
#endif

/* only native compiler supports -run */
#if defined _WIN32 == defined TCC_TARGET_PE
# if (defined __i386__ || defined _X86_) && defined TCC_TARGET_I386
#  define TCC_IS_NATIVE
# elif (defined __x86_64__ || defined _AMD64_) && defined TCC_TARGET_X86_64
#  define TCC_IS_NATIVE
# elif defined __arm__ && defined TCC_TARGET_ARM
#  define TCC_IS_NATIVE
# elif defined __aarch64__ && defined TCC_TARGET_ARM64
#  define TCC_IS_NATIVE
# endif
#endif

#if defined TCC_IS_NATIVE && !defined CONFIG_TCCBOOT
# define CONFIG_TCC_BACKTRACE
# if (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64) \
  && !defined TCC_UCLIBC && !defined TCC_MUSL
# define CONFIG_TCC_BCHECK /* enable bound checking code */
# endif
#endif

/* ------------ path configuration ------------ */

#ifndef CONFIG_SYSROOT
# define CONFIG_SYSROOT ""
#endif
#ifndef CONFIG_TCCDIR
# define CONFIG_TCCDIR "."
#endif
#ifndef CONFIG_LDDIR
# define CONFIG_LDDIR "lib"
#endif
#ifdef CONFIG_TRIPLET
# define USE_TRIPLET(s) s "/" CONFIG_TRIPLET
# define ALSO_TRIPLET(s) USE_TRIPLET(s) ":" s
#else
# define USE_TRIPLET(s) s
# define ALSO_TRIPLET(s) s
#endif

/* path to find crt1.o, crti.o and crtn.o */
#ifndef CONFIG_TCC_CRTPREFIX
# define CONFIG_TCC_CRTPREFIX USE_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR)
#endif

/* Below: {B} is substituted by CONFIG_TCCDIR (rsp. -B option) */

/* system include paths */
#ifndef CONFIG_TCC_SYSINCLUDEPATHS
# ifdef TCC_TARGET_PE
#  define CONFIG_TCC_SYSINCLUDEPATHS "{B}/include;{B}/include/winapi"
# else
#  define CONFIG_TCC_SYSINCLUDEPATHS \
        "{B}/include" \
    ":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/include") \
    ":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/include")
# endif
#endif

/* library search paths */
#ifndef CONFIG_TCC_LIBPATHS
# ifdef TCC_TARGET_PE
#  define CONFIG_TCC_LIBPATHS "{B}/lib"
# else
#  define CONFIG_TCC_LIBPATHS \
        ALSO_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) \
    ":" ALSO_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) \
    ":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/" CONFIG_LDDIR)
# endif
#endif

/* name of ELF interpreter */
#ifndef CONFIG_TCC_ELFINTERP
# if defined __FreeBSD__
#  define CONFIG_TCC_ELFINTERP "/libexec/ld-elf.so.1"
# elif defined __FreeBSD_kernel__
#  if defined(TCC_TARGET_X86_64)
#   define CONFIG_TCC_ELFINTERP "/lib/ld-kfreebsd-x86-64.so.1"
#  else
#   define CONFIG_TCC_ELFINTERP "/lib/ld.so.1"
#  endif
# elif defined __DragonFly__
#  define CONFIG_TCC_ELFINTERP "/usr/libexec/ld-elf.so.2"
# elif defined __NetBSD__
#  define CONFIG_TCC_ELFINTERP "/usr/libexec/ld.elf_so"
# elif defined __GNU__
#  define CONFIG_TCC_ELFINTERP "/lib/ld.so"
# elif defined(TCC_TARGET_PE)
#  define CONFIG_TCC_ELFINTERP "-"
# elif defined(TCC_UCLIBC)
#  define CONFIG_TCC_ELFINTERP "/lib/ld-uClibc.so.0" /* is there a uClibc for x86_64 ? */
# elif defined TCC_TARGET_ARM64
#  if defined(TCC_MUSL)
#   define CONFIG_TCC_ELFINTERP "/lib/ld-musl-aarch64.so.1"
#  else
#   define CONFIG_TCC_ELFINTERP "/lib/ld-linux-aarch64.so.1"
#  endif
# elif defined(TCC_TARGET_X86_64)
#  if defined(TCC_MUSL)
#   define CONFIG_TCC_ELFINTERP "/lib/ld-musl-x86_64.so.1"
#  else
#   define CONFIG_TCC_ELFINTERP "/lib64/ld-linux-x86-64.so.2"
#  endif
# elif !defined(TCC_ARM_EABI)
#  if defined(TCC_MUSL)
#   define CONFIG_TCC_ELFINTERP "/lib/ld-musl-arm.so.1"
#  else
#   define CONFIG_TCC_ELFINTERP "/lib/ld-linux.so.2"
#  endif
# endif
#endif

/* var elf_interp dans *-gen.c */
#ifdef CONFIG_TCC_ELFINTERP
# define DEFAULT_ELFINTERP(s) CONFIG_TCC_ELFINTERP
#else
# define DEFAULT_ELFINTERP(s) default_elfinterp(s)
#endif

/* (target specific) libtcc1.a */
#ifndef TCC_LIBTCC1
# define TCC_LIBTCC1 "libtcc1.a"
#endif

/* library to use with CONFIG_USE_LIBGCC instead of libtcc1.a */
#if defined CONFIG_USE_LIBGCC && !defined TCC_LIBGCC
#define TCC_LIBGCC USE_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) "/libgcc_s.so.1"
#endif

/* -------------------------------------------- */

#include "libtcc.h"
#include "elf.h"
#include "stab.h"

/* -------------------------------------------- */

#ifndef PUB_FUNC /* functions used by tcc.c but not in libtcc.h */
# define PUB_FUNC
#endif

#ifdef ONE_SOURCE
#define ST_INLN static inline
#define ST_FUNC static
#define ST_DATA static
#else
#define ST_INLN
#define ST_FUNC
#define ST_DATA extern
#endif

#ifdef TCC_PROFILE /* profile all functions */
# define static
#endif

/* -------------------------------------------- */
/* include the target specific definitions */

#define TARGET_DEFS_ONLY
#ifdef TCC_TARGET_I386
# include "i386-gen.c"
# include "i386-link.c"
#endif
#ifdef TCC_TARGET_X86_64
# include "x86_64-gen.c"
# include "x86_64-link.c"
#endif
#ifdef TCC_TARGET_ARM
# include "arm-gen.c"
# include "arm-link.c"
# include "arm-asm.c"
#endif
#ifdef TCC_TARGET_ARM64
# include "arm64-gen.c"
# include "arm64-link.c"
#endif
#ifdef TCC_TARGET_C67
# include "coff.h"
# include "c67-gen.c"
# include "c67-link.c"
#endif
#undef TARGET_DEFS_ONLY

/* -------------------------------------------- */

#if PTR_SIZE == 8
# define ELFCLASSW ELFCLASS64
# define ElfW(type) Elf##64##_##type
# define ELFW(type) ELF##64##_##type
# define ElfW_Rel ElfW(Rela)
# define SHT_RELX SHT_RELA
# define REL_SECTION_FMT ".rela%s"
#else
# define ELFCLASSW ELFCLASS32
# define ElfW(type) Elf##32##_##type
# define ELFW(type) ELF##32##_##type
# define ElfW_Rel ElfW(Rel)
# define SHT_RELX SHT_REL
# define REL_SECTION_FMT ".rel%s"
#endif
/* target address type */
#define addr_t ElfW(Addr)

/* -------------------------------------------- */

#define INCLUDE_STACK_SIZE  32
#define IFDEF_STACK_SIZE    64
#define VSTACK_SIZE         256
#define STRING_MAX_SIZE     1024
#define TOKSTR_MAX_SIZE     256
#define PACK_STACK_SIZE     8

#define TOK_HASH_SIZE       16384 /* must be a power of two */
#define TOK_ALLOC_INCR      512  /* must be a power of two */
#define TOK_MAX_SIZE        4 /* token max size in int unit when stored in string */

/* token symbol management */
typedef struct TokenSym {
    struct TokenSym *hash_next;
    struct Sym *sym_define; /* direct pointer to define */
    struct Sym *sym_label; /* direct pointer to label */
    struct Sym *sym_struct; /* direct pointer to structure */
    struct Sym *sym_identifier; /* direct pointer to identifier */
    int tok; /* token number */
    int len;
    char str[1];
} TokenSym;

#ifdef TCC_TARGET_PE
typedef unsigned short nwchar_t;
#else
typedef int nwchar_t;
#endif

typedef struct CString {
    int size; /* size in bytes */
    void *data; /* either 'char *' or 'nwchar_t *' */
    int size_allocated;
} CString;

/* type definition */
typedef struct CType {
    int t;
    struct Sym *ref;
} CType;

/* constant value */
typedef union CValue {
    long double ld;
    double d;
    float f;
#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
    uint64_t i;
#else
    uint32_t i;
    uint32_t i_padding;
#endif
    struct {
        int size;
        const void *data;
    } str;
#if BOOTSTRAP && __arm__
    int tab[4];
#else
    int tab[LDOUBLE_SIZE/4];
#endif
} CValue;

/* value on stack */
typedef struct SValue {
    CType type;      /* type */
    unsigned short r;      /* register + flags */
    unsigned short r2;     /* second register, used for 'long long'
                              type. If not used, set to VT_CONST */
    CValue c;              /* constant, if VT_CONST */
    struct Sym *sym;       /* symbol, if (VT_SYM | VT_CONST), or if
                      result of unary() for an identifier. */
} SValue;

struct Attribute {
#if HAVE_BITFIELD
    unsigned
        func_call     : 3, /* calling convention (0..5), see below */
        aligned       : 5, /* alignment as log2+1 (0 == unspecified) */
        packed        : 1,
        func_export   : 1,
        func_import   : 1,
        func_args     : 5,
        func_body     : 1,
        mode          : 4,
        weak          : 1,
        visibility    : 2,
    unsigned_enum : 1,
        fill          : 7; // 7 bits left to fit well in union below
#else // !HAVE_BITFIELD
  struct {
    unsigned func_call;
    unsigned aligned;
    unsigned packed;
    unsigned func_export;
    unsigned func_import;
    unsigned func_args;
    unsigned func_body;
    unsigned mode;
    unsigned weak;
    unsigned visibility;
    unsigned unsigned_enum;
    unsigned fill;
  };
#endif // !HAVE_BITFIELD
};

/* GNUC attribute definition */
typedef struct AttributeDef {
    struct Attribute a;
    struct Section *section;
    int alias_target;    /* token */
    int asm_label;    /* associated asm label */
} AttributeDef;

/* symbol management */
typedef struct Sym {
    int v;    /* symbol token */
    union {
        int asm_label;    /* associated asm label */
        int scope;  /* scope level for locals */
    };
    union {
        long r;    /* associated register or VT_CONST/VT_LOCAL and LVAL type */
        struct Attribute a;
    };
    union {
        long c;    /* associated number */
        int *d;   /* define token stream */
    };
    CType type;    /* associated type */
    union {
        struct Sym *next; /* next related symbol (for fields and anoms) */
        long jnext; /* next jump label */
    };
    struct Sym *prev; /* prev symbol in stack */
    struct Sym *prev_tok; /* previous symbol for this token */
} Sym;

/* section definition */
/* XXX: use directly ELF structure for parameters ? */
/* special flag to indicate that the section should not be linked to
   the other ones */
#define SHF_PRIVATE 0x80000000

/* special flag, too */
#define SECTION_ABS ((void *)1)

typedef struct Section {
    unsigned long data_offset; /* current data offset */
    unsigned char *data;       /* section data */
    unsigned long data_allocated; /* used for realloc() handling */
    int sh_name;             /* elf section name (only used during output) */
    int sh_num;              /* elf section number */
    int sh_type;             /* elf section type */
    int sh_flags;            /* elf section flags */
    int sh_info;             /* elf section info */
    int sh_addralign;        /* elf section alignment */
    int sh_entsize;          /* elf entry size */
    unsigned long sh_size;   /* section size (only used during output) */
    addr_t sh_addr;          /* address at which the section is relocated */
    unsigned long sh_offset; /* file offset */
    int nb_hashed_syms;      /* used to resize the hash table */
    struct Section *link;    /* link to another section */
    struct Section *reloc;   /* corresponding section for relocation, if any */
    struct Section *hash;    /* hash table for symbols */
    struct Section *prev;    /* previous section on section stack */
    char name[1];           /* section name */
} Section;

typedef struct DLLReference {
    int level;
    void *handle;
    char name[1];
} DLLReference;

/* -------------------------------------------------- */

#define SYM_STRUCT     0x40000000 /* struct/union/enum symbol space */
#define SYM_FIELD      0x20000000 /* struct/union field symbol space */
#define SYM_FIRST_ANOM 0x10000000 /* first anonymous sym */

/* stored in 'Sym.c' field */
#define FUNC_NEW       1 /* ansi function prototype */
#define FUNC_OLD       2 /* old function prototype */
#define FUNC_ELLIPSIS  3 /* ansi function prototype with ... */

/* stored in 'Sym.r' field */
#define FUNC_CDECL     0 /* standard c call */
#define FUNC_STDCALL   1 /* pascal c call */
#define FUNC_FASTCALL1 2 /* first param in %eax */
#define FUNC_FASTCALL2 3 /* first parameters in %eax, %edx */
#define FUNC_FASTCALL3 4 /* first parameter in %eax, %edx, %ecx */
#define FUNC_FASTCALLW 5 /* first parameter in %ecx, %edx */

/* field 'Sym.t' for macros */
#define MACRO_OBJ      0 /* object like macro */
#define MACRO_FUNC     1 /* function like macro */

/* field 'Sym.r' for C labels */
#define LABEL_DEFINED  0 /* label is defined */
#define LABEL_FORWARD  1 /* label is forward defined */
#define LABEL_DECLARED 2 /* label is declared but never used */

/* type_decl() types */
#define TYPE_ABSTRACT  1 /* type without variable */
#define TYPE_DIRECT    2 /* type with variable */

#define IO_BUF_SIZE 8192

typedef struct BufferedFile {
    uint8_t *buf_ptr;
    uint8_t *buf_end;
    int fd;
    struct BufferedFile *prev;
    int line_num;    /* current line number - here to simplify code */
    int line_ref;    /* tcc -E: last printed line */
    int ifndef_macro;  /* #ifndef macro / #endif search */
    int ifndef_macro_saved; /* saved ifndef_macro */
    int *ifdef_stack_ptr; /* ifdef_stack value at the start of the file */
    int include_next_index; /* next search path */
    char filename[1024];    /* filename */
    char filename2[1024];    /* filename not modified by # line directive */
    unsigned char unget[4];
    unsigned char buffer[1]; /* extra size for CH_EOB char */
} BufferedFile;

#define CH_EOB   '\\'       /* end of buffer or '\0' char in file */
#define CH_EOF   (-1)   /* end of file */

/* parsing state (used to save parser state to reparse part of the
   source several times) */
typedef struct ParseState {
    const int *macro_ptr;
    int line_num;
    int tok;
    CValue tokc;
} ParseState;

/* used to record tokens */
typedef struct TokenString {
    int *str;
    int len;
    int allocated_len;
    int last_line_num;
    /* used to chain token-strings with begin/end_macro() */
    struct TokenString *prev;
    const int *prev_ptr;
    char alloc;
} TokenString;

/* inline functions */
typedef struct InlineFunc {
    TokenString *func_str;
    Sym *sym;
    char filename[1];
} InlineFunc;

/* include file cache, used to find files faster and also to eliminate
   inclusion if the include file is protected by #ifndef ... #endif */
typedef struct CachedInclude {
    int ifndef_macro;
    int once;
    int hash_next; /* -1 if none */
    char filename[1]; /* path specified in #include */
} CachedInclude;

#define CACHED_INCLUDES_HASH_SIZE 32

#ifdef CONFIG_TCC_ASM
typedef struct ExprValue {
#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
    uint64_t v;
#else
    uint32_t v;
    uint32_t v_padding;
#endif
    Sym *sym;
    int pcrel;
} ExprValue;

#define MAX_ASM_OPERANDS 30
typedef struct ASMOperand {
    int id; /* GCC 3 optional identifier (0 if number only supported */
    char *constraint;
    char asm_str[16]; /* computed asm string for operand */
    SValue *vt; /* C value of the expression */
    int ref_index; /* if >= 0, gives reference to a output constraint */
    int input_index; /* if >= 0, gives reference to an input constraint */
    int priority; /* priority, used to assign registers */
    int reg; /* if >= 0, register number used for this operand */
    int is_llong; /* true if double register value */
    int is_memory; /* true if memory operand */
    int is_rw;     /* for '+' modifier */
} ASMOperand;
#endif

/* extra symbol attributes (not in symbol table) */
struct sym_attr {
    unsigned got_offset;
    unsigned plt_offset;
    int plt_sym;
    int dyn_index;
#ifdef TCC_TARGET_ARM
    unsigned char plt_thumb_stub:1;
#endif
};

struct TCCState {

    int verbose; /* if true, display some information during compilation */
    int nostdinc; /* if true, no standard headers are added */
    int nostdlib; /* if true, no standard libraries are added */
    int nocommon; /* if true, do not use common symbols for .bss data */
    int static_link; /* if true, static linking is performed */
    int rdynamic; /* if true, all symbols are exported */
    int symbolic; /* if true, resolve symbols in the current module first */
    int alacarte_link; /* if true, only link in referenced objects from archive */

    char *tcc_lib_path; /* CONFIG_TCCDIR or -B option */
    char *soname; /* as specified on the command line (-soname) */
    char *rpath; /* as specified on the command line (-Wl,-rpath=) */
    int enable_new_dtags; /* ditto, (-Wl,--enable-new-dtags) */

    /* output type, see TCC_OUTPUT_XXX */
    int output_type;
    /* output format, see TCC_OUTPUT_FORMAT_xxx */
    int output_format;

    /* C language options */
    int char_is_unsigned;
    int leading_underscore;
    int ms_extensions;  /* allow nested named struct w/o identifier behave like unnamed */
    int dollars_in_identifiers; /* allows '$' char in identifiers */
    int ms_bitfields; /* if true, emulate MS algorithm for aligning bitfields */

    /* warning switches */
    int warn_write_strings;
    int warn_unsupported;
    int warn_error;
    int warn_none;
    int warn_implicit_function_declaration;
    int warn_gcc_compat;

    /* compile with debug symbol (and use them if error during execution) */
    int do_debug;
#ifdef CONFIG_TCC_BCHECK
    /* compile with built-in memory and bounds checker */
    int do_bounds_check;
#endif
#ifdef TCC_TARGET_ARM
    enum float_abi float_abi; /* float ABI of the generated code*/
#endif

    addr_t text_addr; /* address of text section */
    int has_text_addr;

    unsigned section_align; /* section alignment */

    char *init_symbol; /* symbols to call at load-time (not used currently) */
    char *fini_symbol; /* symbols to call at unload-time (not used currently) */
    
#ifdef TCC_TARGET_I386
    int seg_size; /* 32. Can be 16 with i386 assembler (.code16) */
#endif
#ifdef TCC_TARGET_X86_64
    int nosse; /* For -mno-sse support. */
#endif

    /* array of all loaded dlls (including those referenced by loaded dlls) */
    DLLReference **loaded_dlls;
    int nb_loaded_dlls;

    /* include paths */
    char **include_paths;
    int nb_include_paths;

    char **sysinclude_paths;
    int nb_sysinclude_paths;

    /* library paths */
    char **library_paths;
    int nb_library_paths;

    /* crt?.o object path */
    char **crt_paths;
    int nb_crt_paths;

    /* -include files */
    char **cmd_include_files;
    int nb_cmd_include_files;

    /* error handling */
    void *error_opaque;
    void (*error_func)(void *opaque, const char *msg);
    int error_set_jmp_enabled;
    jmp_buf error_jmp_buf;
    int nb_errors;

    /* output file for preprocessing (-E) */
    FILE *ppfp;
    enum {
    LINE_MACRO_OUTPUT_FORMAT_GCC,
    LINE_MACRO_OUTPUT_FORMAT_NONE,
    LINE_MACRO_OUTPUT_FORMAT_STD,
    LINE_MACRO_OUTPUT_FORMAT_P10 = 11
    } Pflag; /* -P switch */
    char dflag; /* -dX value */

    /* for -MD/-MF: collected dependencies for this compilation */
    char **target_deps;
    int nb_target_deps;

    /* compilation */
    BufferedFile *include_stack[INCLUDE_STACK_SIZE];
    BufferedFile **include_stack_ptr;

    int ifdef_stack[IFDEF_STACK_SIZE];
    int *ifdef_stack_ptr;

    /* included files enclosed with #ifndef MACRO */
    int cached_includes_hash[CACHED_INCLUDES_HASH_SIZE];
    CachedInclude **cached_includes;
    int nb_cached_includes;

    /* #pragma pack stack */
    int pack_stack[PACK_STACK_SIZE];
    int *pack_stack_ptr;
    char **pragma_libs;
    int nb_pragma_libs;

    /* inline functions are stored as token lists and compiled last
       only if referenced */
    struct InlineFunc **inline_fns;
    int nb_inline_fns;

    /* sections */
    Section **sections;
    int nb_sections; /* number of sections, including first dummy section */

    Section **priv_sections;
    int nb_priv_sections; /* number of private sections */

    /* got & plt handling */
    Section *got;
    Section *plt;

    /* temporary dynamic symbol sections (for dll loading) */
    Section *dynsymtab_section;
    /* exported dynamic symbol section */
    Section *dynsym;
    /* copy of the global symtab_section variable */
    Section *symtab;
    /* extra attributes (eg. GOT/PLT value) for symtab symbols */
    struct sym_attr *sym_attrs;
    int nb_sym_attrs;
    /* tiny assembler state */
    Sym *asm_labels;

#ifdef TCC_TARGET_PE
    /* PE info */
    int pe_subsystem;
    unsigned pe_characteristics;
    unsigned pe_file_align;
    unsigned pe_stack_size;
# ifdef TCC_TARGET_X86_64
    Section *uw_pdata;
    int uw_sym;
    unsigned uw_offs;
# endif
#endif

#ifdef TCC_IS_NATIVE
    const char *runtime_main;
    void **runtime_mem;
    int nb_runtime_mem;
#endif

    /* used by main and tcc_parse_args only */
    struct filespec **files; /* files seen on command line */
    int nb_files; /* number thereof */
    int nb_libraries; /* number of libs thereof */
    int filetype;
    char *outfile; /* output filename */
    int option_r; /* option -r */
    int do_bench; /* option -bench */
    int gen_deps; /* option -MD  */
    char *deps_outfile; /* option -MF */
    int option_pthread; /* -pthread option */
    int argc;
    char **argv;
};

struct filespec {
    char type;
    char alacarte;
    char name[1];
};

/* The current value can be: */
#define VT_VALMASK   0x003f  /* mask for value location, register or: */
#define VT_CONST     0x0030  /* constant in vc (must be first non register value) */
#define VT_LLOCAL    0x0031  /* lvalue, offset on stack */
#define VT_LOCAL     0x0032  /* offset on stack */
#define VT_CMP       0x0033  /* the value is stored in processor flags (in vc) */
#define VT_JMP       0x0034  /* value is the consequence of jmp true (even) */
#define VT_JMPI      0x0035  /* value is the consequence of jmp false (odd) */
#define VT_LVAL      0x0100  /* var is an lvalue */
#define VT_SYM       0x0200  /* a symbol value is added */
#define VT_MUSTCAST  0x0400  /* value must be casted to be correct (used for
                                char/short stored in integer registers) */
#define VT_MUSTBOUND 0x0800  /* bound checking must be done before
                                dereferencing value */
#define VT_BOUNDED   0x8000  /* value is bounded. The address of the
                                bounding function call point is in vc */
#define VT_LVAL_BYTE     0x1000  /* lvalue is a byte */
#define VT_LVAL_SHORT    0x2000  /* lvalue is a short */
#define VT_LVAL_UNSIGNED 0x4000  /* lvalue is unsigned */
#define VT_LVAL_TYPE     (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED)

/* types */
#define VT_BTYPE       0x000f  /* mask for basic type */
#define VT_INT              0  /* integer type */
#define VT_BYTE             1  /* signed byte type */
#define VT_SHORT            2  /* short type */
#define VT_VOID             3  /* void type */
#define VT_PTR              4  /* pointer */
#define VT_ENUM             5  /* enum definition */
#define VT_FUNC             6  /* function type */
#define VT_STRUCT           7  /* struct/union definition */
#define VT_FLOAT            8  /* IEEE float */
#define VT_DOUBLE           9  /* IEEE double */
#define VT_LDOUBLE         10  /* IEEE long double */
#define VT_BOOL            11  /* ISOC99 boolean type */
#define VT_LLONG           12  /* 64 bit integer */
#define VT_LONG            13  /* long integer (NEVER USED as type, only
                                  during parsing) */
#define VT_QLONG           14  /* 128-bit integer. Only used for x86-64 ABI */
#define VT_QFLOAT          15  /* 128-bit float. Only used for x86-64 ABI */
#define VT_UNSIGNED    0x0010  /* unsigned type */
#define VT_ARRAY       0x0020  /* array type (also has VT_PTR) */
#define VT_BITFIELD    0x0040  /* bitfield modifier */
#define VT_CONSTANT    0x0800  /* const modifier */
#define VT_VOLATILE    0x1000  /* volatile modifier */
#define VT_DEFSIGN     0x2000  /* signed type */
#define VT_VLA     0x00020000  /* VLA type (also has VT_PTR and VT_ARRAY) */

/* storage */
#define VT_EXTERN  0x00000080  /* extern definition */
#define VT_STATIC  0x00000100  /* static variable */
#define VT_TYPEDEF 0x00000200  /* typedef definition */
#define VT_INLINE  0x00000400  /* inline definition */
#define VT_IMPORT  0x00004000  /* win32: extern data imported from dll */
#define VT_EXPORT  0x00008000  /* win32: data exported from dll */
#define VT_WEAK    0x00010000  /* weak symbol */
#define VT_TLS     0x00040000  /* thread-local storage */
#define VT_VIS_SHIFT    19     /* shift for symbol visibility, overlapping
                  bitfield values, because bitfields never
                  have linkage and hence never have
                  visibility.  */
#define VT_VIS_SIZE      2     /* We have four visibilities.  */
#define VT_VIS_MASK (((1 << VT_VIS_SIZE)-1) << VT_VIS_SHIFT)

#define VT_STRUCT_SHIFT 19     /* shift for bitfield shift values (max: 32 - 2*6) */


/* type mask (except storage) */
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_IMPORT | VT_EXPORT | VT_WEAK | VT_VIS_MASK)
#define VT_TYPE (~(VT_STORAGE))

/* token values */

/* warning: the following compare tokens depend on i386 asm code */
#define TOK_ULT 0x92
#define TOK_UGE 0x93
#define TOK_EQ  0x94
#define TOK_NE  0x95
#define TOK_ULE 0x96
#define TOK_UGT 0x97
#define TOK_Nset 0x98
#define TOK_Nclear 0x99
#define TOK_LT  0x9c
#define TOK_GE  0x9d
#define TOK_LE  0x9e
#define TOK_GT  0x9f

#define TOK_LAND  0xa0
#define TOK_LOR   0xa1
#define TOK_DEC   0xa2
#define TOK_MID   0xa3 /* inc/dec, to void constant */
#define TOK_INC   0xa4
#define TOK_UDIV  0xb0 /* unsigned division */
#define TOK_UMOD  0xb1 /* unsigned modulo */
#define TOK_PDIV  0xb2 /* fast division with undefined rounding for pointers */

/* tokens that carry values (in additional token string space / tokc) --> */
#define TOK_CCHAR   0xb3 /* char constant in tokc */
#define TOK_LCHAR   0xb4
#define TOK_CINT    0xb5 /* number in tokc */
#define TOK_CUINT   0xb6 /* unsigned int constant */
#define TOK_CLLONG  0xb7 /* long long constant */
#define TOK_CULLONG 0xb8 /* unsigned long long constant */
#define TOK_STR     0xb9 /* pointer to string in tokc */
#define TOK_LSTR    0xba
#define TOK_CFLOAT  0xbb /* float constant */
#define TOK_CDOUBLE 0xbc /* double constant */
#define TOK_CLDOUBLE 0xbd /* long double constant */
#define TOK_PPNUM   0xbe /* preprocessor number */
#define TOK_PPSTR   0xbf /* preprocessor string */
#define TOK_LINENUM 0xc0 /* line number info */
/* <-- */

#define TOK_UMULL    0xc2 /* unsigned 32x32 -> 64 mul */
#define TOK_ADDC1    0xc3 /* add with carry generation */
#define TOK_ADDC2    0xc4 /* add with carry use */
#define TOK_SUBC1    0xc5 /* add with carry generation */
#define TOK_SUBC2    0xc6 /* add with carry use */
#define TOK_ARROW    0xc7
#define TOK_DOTS     0xc8 /* three dots */
#define TOK_SHR      0xc9 /* unsigned shift right */
#define TOK_TWOSHARPS 0xca /* ## preprocessing token */
#define TOK_PLCHLDR  0xcb /* placeholder token as defined in C99 */
#define TOK_NOSUBST  0xcc /* means following token has already been pp'd */
#define TOK_PPJOIN   0xce /* A '##' in the right position to mean pasting */

#define TOK_SHL   0x01 /* shift left */
#define TOK_SAR   0x02 /* signed shift right */
  
/* assignment operators : normal operator or 0x80 */
#define TOK_A_MOD 0xa5
#define TOK_A_AND 0xa6
#define TOK_A_MUL 0xaa
#define TOK_A_ADD 0xab
#define TOK_A_SUB 0xad
#define TOK_A_DIV 0xaf
#define TOK_A_XOR 0xde
#define TOK_A_OR  0xfc
#define TOK_A_SHL 0x81
#define TOK_A_SAR 0x82

#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif

#ifndef countof
#define countof(tab) (sizeof(tab) / sizeof((tab)[0]))
#endif

#define TOK_EOF       (-1)  /* end of file */
#define TOK_LINEFEED  10    /* line feed */

/* all identifiers and strings have token above that */
#define TOK_IDENT 256

#define DEF_ASM(x) DEF(TOK_ASM_ ## x, #x)
#define TOK_ASM_int TOK_INT
#define DEF_ASMDIR(x) DEF(TOK_ASMDIR_ ## x, "." #x)
#define TOK_ASMDIR_FIRST TOK_ASMDIR_byte
#define TOK_ASMDIR_LAST TOK_ASMDIR_section

#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
/* only used for i386 asm opcodes definitions */
#define DEF_BWL(x) \
 DEF(TOK_ASM_ ## x ## b, #x "b") \
 DEF(TOK_ASM_ ## x ## w, #x "w") \
 DEF(TOK_ASM_ ## x ## l, #x "l") \
 DEF(TOK_ASM_ ## x, #x)
#define DEF_WL(x) \
 DEF(TOK_ASM_ ## x ## w, #x "w") \
 DEF(TOK_ASM_ ## x ## l, #x "l") \
 DEF(TOK_ASM_ ## x, #x)
#ifdef TCC_TARGET_X86_64
# define DEF_BWLQ(x) \
 DEF(TOK_ASM_ ## x ## b, #x "b") \
 DEF(TOK_ASM_ ## x ## w, #x "w") \
 DEF(TOK_ASM_ ## x ## l, #x "l") \
 DEF(TOK_ASM_ ## x ## q, #x "q") \
 DEF(TOK_ASM_ ## x, #x)
# define DEF_WLQ(x) \
 DEF(TOK_ASM_ ## x ## w, #x "w") \
 DEF(TOK_ASM_ ## x ## l, #x "l") \
 DEF(TOK_ASM_ ## x ## q, #x "q") \
 DEF(TOK_ASM_ ## x, #x)
# define DEF_BWLX DEF_BWLQ
# define DEF_WLX DEF_WLQ
/* number of sizes + 1 */
# define NBWLX 5
#else
# define DEF_BWLX DEF_BWL
# define DEF_WLX DEF_WL
/* number of sizes + 1 */
# define NBWLX 4
#endif

#define DEF_FP1(x) \
 DEF(TOK_ASM_ ## f ## x ## s, "f" #x "s") \
 DEF(TOK_ASM_ ## fi ## x ## l, "fi" #x "l") \
 DEF(TOK_ASM_ ## f ## x ## l, "f" #x "l") \
 DEF(TOK_ASM_ ## fi ## x ## s, "fi" #x "s")

#define DEF_FP(x) \
 DEF(TOK_ASM_ ## f ## x, "f" #x ) \
 DEF(TOK_ASM_ ## f ## x ## p, "f" #x "p") \
 DEF_FP1(x)

#define DEF_ASMTEST(x,suffix) \
 DEF_ASM(x ## o ## suffix) \
 DEF_ASM(x ## no ## suffix) \
 DEF_ASM(x ## b ## suffix) \
 DEF_ASM(x ## c ## suffix) \
 DEF_ASM(x ## nae ## suffix) \
 DEF_ASM(x ## nb ## suffix) \
 DEF_ASM(x ## nc ## suffix) \
 DEF_ASM(x ## ae ## suffix) \
 DEF_ASM(x ## e ## suffix) \
 DEF_ASM(x ## z ## suffix) \
 DEF_ASM(x ## ne ## suffix) \
 DEF_ASM(x ## nz ## suffix) \
 DEF_ASM(x ## be ## suffix) \
 DEF_ASM(x ## na ## suffix) \
 DEF_ASM(x ## nbe ## suffix) \
 DEF_ASM(x ## a ## suffix) \
 DEF_ASM(x ## s ## suffix) \
 DEF_ASM(x ## ns ## suffix) \
 DEF_ASM(x ## p ## suffix) \
 DEF_ASM(x ## pe ## suffix) \
 DEF_ASM(x ## np ## suffix) \
 DEF_ASM(x ## po ## suffix) \
 DEF_ASM(x ## l ## suffix) \
 DEF_ASM(x ## nge ## suffix) \
 DEF_ASM(x ## nl ## suffix) \
 DEF_ASM(x ## ge ## suffix) \
 DEF_ASM(x ## le ## suffix) \
 DEF_ASM(x ## ng ## suffix) \
 DEF_ASM(x ## nle ## suffix) \
 DEF_ASM(x ## g ## suffix)

#endif /* defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 */

enum tcc_token {
    TOK_LAST = TOK_IDENT - 1
#define DEF(id, str) ,id
#include "tcctok.h"
#undef DEF
};

/* keywords: tok >= TOK_IDENT && tok < TOK_UIDENT */
#define TOK_UIDENT TOK_DEFINE

/* ------------ libtcc.c ------------ */

/* use GNU C extensions */
ST_DATA int gnu_ext;
/* use Tiny C extensions */
ST_DATA int tcc_ext;
/* XXX: get rid of this ASAP */
ST_DATA struct TCCState *tcc_state;

/* public functions currently used by the tcc main function */
ST_FUNC char *pstrcpy(char *buf, int buf_size, const char *s);
ST_FUNC char *pstrcat(char *buf, int buf_size, const char *s);
ST_FUNC char *pstrncpy(char *out, const char *in, size_t num);
PUB_FUNC char *tcc_basename(const char *name);
PUB_FUNC char *tcc_fileextension (const char *name);

#ifndef MEM_DEBUG
PUB_FUNC void tcc_free(void *ptr);
PUB_FUNC void *tcc_malloc(unsigned long size);
PUB_FUNC void *tcc_mallocz(unsigned long size);
PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size);
PUB_FUNC char *tcc_strdup(const char *str);
#else
#define tcc_free(ptr)           tcc_free_debug(ptr)
#define tcc_malloc(size)        tcc_malloc_debug(size, __FILE__, __LINE__)
#define tcc_mallocz(size)       tcc_mallocz_debug(size, __FILE__, __LINE__)
#define tcc_realloc(ptr,size)   tcc_realloc_debug(ptr, size, __FILE__, __LINE__)
#define tcc_strdup(str)         tcc_strdup_debug(str, __FILE__, __LINE__)
PUB_FUNC void tcc_free_debug(void *ptr);
PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line);
PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line);
PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line);
PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line);
#endif

#define free(p) use_tcc_free(p)
#define malloc(s) use_tcc_malloc(s)
#define realloc(p, s) use_tcc_realloc(p, s)
#undef strdup
#define strdup(s) use_tcc_strdup(s)
PUB_FUNC void tcc_memcheck(void);
PUB_FUNC void tcc_error_noabort(const char *fmt, ...);
PUB_FUNC NORETURN void tcc_error(const char *fmt, ...);
PUB_FUNC void tcc_warning(const char *fmt, ...);

/* other utilities */
ST_FUNC void dynarray_add(void *ptab, int *nb_ptr, void *data);
ST_FUNC void dynarray_reset(void *pp, int *n);
ST_INLN void cstr_ccat(CString *cstr, int ch);
ST_FUNC void cstr_cat(CString *cstr, const char *str, int len);
ST_FUNC void cstr_wccat(CString *cstr, int ch);
ST_FUNC void cstr_new(CString *cstr);
ST_FUNC void cstr_free(CString *cstr);
ST_FUNC void cstr_reset(CString *cstr);

ST_INLN void sym_free(Sym *sym);
ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, long c);
ST_FUNC Sym *sym_find2(Sym *s, int v);
ST_FUNC Sym *sym_push(int v, CType *type, int r, long c);
ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep);
ST_INLN Sym *struct_find(int v);
ST_INLN Sym *sym_find(int v);
ST_FUNC Sym *global_identifier_push(int v, int t, long c);

ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen);
ST_FUNC int tcc_open(TCCState *s1, const char *filename);
ST_FUNC void tcc_close(void);

ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags);
/* flags: */
#define AFF_PRINT_ERROR     0x10 /* print error if file not found */
#define AFF_REFERENCED_DLL  0x20 /* load a referenced dll from another dll */
#define AFF_PREPROCESS      0x40 /* preprocess file */
/* combined with: */
#define AFF_TYPE_NONE   0
#define AFF_TYPE_C      1
#define AFF_TYPE_ASM    2
#define AFF_TYPE_ASMPP  3
#define AFF_TYPE_BIN    4
#define AFF_TYPE_LIB    5
/* values from tcc_object_type(...) */
#define AFF_BINTYPE_REL 1
#define AFF_BINTYPE_DYN 2
#define AFF_BINTYPE_AR  3
#define AFF_BINTYPE_C67 4


ST_FUNC int tcc_add_crt(TCCState *s, const char *filename);
ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags);
ST_FUNC void tcc_add_pragma_libs(TCCState *s1);
PUB_FUNC int tcc_add_library_err(TCCState *s, const char *f);
PUB_FUNC void tcc_print_stats(TCCState *s, unsigned total_time);
PUB_FUNC int tcc_parse_args(TCCState *s, int *argc, char ***argv, int optind);
#ifdef _WIN32
ST_FUNC char *normalize_slashes(char *path);
#endif

/* tcc_parse_args return codes: */
#define OPT_HELP 1
#define OPT_HELP2 2
#define OPT_V 3
#define OPT_PRINT_DIRS 4
#define OPT_AR 5
#define OPT_IMPDEF 6
#define OPT_M32 32
#define OPT_M64 64

/* ------------ tccpp.c ------------ */

ST_DATA struct BufferedFile *file;
ST_DATA int ch, tok;
ST_DATA CValue tokc;
ST_DATA const int *macro_ptr;
ST_DATA int parse_flags;
ST_DATA int tok_flags;
ST_DATA CString tokcstr; /* current parsed string, if any */

/* display benchmark infos */
ST_DATA int total_lines;
ST_DATA int total_bytes;
ST_DATA int tok_ident;
ST_DATA TokenSym **table_ident;

#define TOK_FLAG_BOL   0x0001 /* beginning of line before */
#define TOK_FLAG_BOF   0x0002 /* beginning of file before */
#define TOK_FLAG_ENDIF 0x0004 /* a endif was found matching starting #ifdef */
#define TOK_FLAG_EOF   0x0008 /* end of file */

#define PARSE_FLAG_PREPROCESS 0x0001 /* activate preprocessing */
#define PARSE_FLAG_TOK_NUM    0x0002 /* return numbers instead of TOK_PPNUM */
#define PARSE_FLAG_LINEFEED   0x0004 /* line feed is returned as a
                                        token. line feed is also
                                        returned at eof */
#define PARSE_FLAG_ASM_FILE 0x0008 /* we processing an asm file: '#' can be used for line comment, etc. */
#define PARSE_FLAG_SPACES     0x0010 /* next() returns space tokens (for -E) */
#define PARSE_FLAG_ACCEPT_STRAYS 0x0020 /* next() returns '\\' token */
#define PARSE_FLAG_TOK_STR    0x0040 /* return parsed strings instead of TOK_PPSTR */

/* isidnum_table flags: */
#define IS_SPC 1
#define IS_ID  2
#define IS_NUM 4

ST_FUNC TokenSym *tok_alloc(const char *str, int len);
ST_FUNC const char *get_tok_str(int v, CValue *cv);
ST_FUNC void begin_macro(TokenString *str, int alloc);
ST_FUNC void end_macro(void);
ST_FUNC void set_idnum(int c, int val);
ST_FUNC void save_parse_state(ParseState *s);
ST_FUNC void restore_parse_state(ParseState *s);
ST_INLN void tok_str_new(TokenString *s);
ST_FUNC TokenString *tok_str_alloc(void);
ST_FUNC void tok_str_free(TokenString *s);
ST_FUNC void tok_str_free_str(int *str);
ST_FUNC void tok_str_add(TokenString *s, int t);
ST_FUNC void tok_str_add_tok(TokenString *s);
ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg);
ST_FUNC void define_undef(Sym *s);
ST_INLN Sym *define_find(int v);
ST_FUNC void free_defines(Sym *b);
ST_FUNC Sym *label_find(int v);
ST_FUNC Sym *label_push(Sym **ptop, int v, int flags);
ST_FUNC void label_pop(Sym **ptop, Sym *slast);
ST_FUNC void parse_define(void);
ST_FUNC void preprocess(int is_bof);
ST_FUNC void next_nomacro(void);
ST_FUNC void next(void);
ST_INLN void unget_tok(int last_tok);
ST_FUNC void preprocess_start(TCCState *s1);
ST_FUNC void tccpp_new(TCCState *s);
ST_FUNC void tccpp_delete(TCCState *s);
ST_FUNC int tcc_preprocess(TCCState *s1);
ST_FUNC void skip(int c);
ST_FUNC NORETURN void expect(const char *msg);

/* space excluding newline */
static inline int is_space(int ch) {
    return ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f' || ch == '\r';
}
static inline int isid(int c) {
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
static inline int isnum(int c) {
    return c >= '0' && c <= '9';
}
static inline int isoct(int c) {
    return c >= '0' && c <= '7';
}
static inline int toup(int c) {
    return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c;
}

/* ------------ tccgen.c ------------ */

#define SYM_POOL_NB (8192 / sizeof(Sym))
ST_DATA Sym *sym_free_first;
ST_DATA void **sym_pools;
ST_DATA int nb_sym_pools;

ST_DATA Sym *global_stack;
ST_DATA Sym *local_stack;
ST_DATA Sym *local_label_stack;
ST_DATA Sym *global_label_stack;
ST_DATA Sym *define_stack;
ST_DATA CType char_pointer_type, func_old_type, int_type, size_type;
ST_DATA SValue __vstack[1+/*to make bcheck happy*/ VSTACK_SIZE], *vtop, *pvtop;
#define vstack  (__vstack + 1)
ST_DATA int rsym, anon_sym, ind, loc;

ST_DATA int const_wanted; /* true if constant wanted */
ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */
ST_DATA int global_expr;  /* true if compound literals must be allocated globally (used during initializers parsing */
ST_DATA CType func_vt; /* current function return type (used by return instruction) */
ST_DATA int func_var; /* true if current function is variadic */
ST_DATA int func_vc;
ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
ST_DATA const char *funcname;

ST_FUNC void tcc_debug_start(TCCState *s1);
ST_FUNC void tcc_debug_end(TCCState *s1);
ST_FUNC void tcc_debug_funcstart(TCCState *s1, Sym *sym);
ST_FUNC void tcc_debug_funcend(TCCState *s1, int size);
ST_FUNC void tcc_debug_line(TCCState *s1);

ST_FUNC void tccgen_start(TCCState *s1);
ST_FUNC void tccgen_end(TCCState *s1);

ST_FUNC void free_inline_functions(TCCState *s);
ST_FUNC void check_vstack(void);

ST_INLN int is_float(int t);
ST_FUNC int ieee_finite(double d);
ST_FUNC void test_lvalue(void);
ST_FUNC void vpushi(int v);
ST_FUNC Sym *external_global_sym(int v, CType *type, int r);
ST_FUNC void vset(CType *type, int r, long v);
ST_FUNC void vswap(void);
ST_FUNC void vpush_global_sym(CType *type, int v);
ST_FUNC void vrote(SValue *e, int n);
ST_FUNC void vrott(int n);
ST_FUNC void vrotb(int n);
#ifdef TCC_TARGET_ARM
ST_FUNC int get_reg_ex(int rc, int rc2);
ST_FUNC void lexpand_nr(void);
#endif
ST_FUNC void vpushv(SValue *v);
ST_FUNC void save_reg(int r);
ST_FUNC void save_reg_upstack(int r, int n);
ST_FUNC int get_reg(int rc);
ST_FUNC void save_regs(int n);
ST_FUNC void gaddrof(void);
ST_FUNC int gv(int rc);
ST_FUNC void gv2(int rc1, int rc2);
ST_FUNC void vpop(void);
ST_FUNC void gen_op(int op);
ST_FUNC int type_size(CType *type, int *a);
ST_FUNC void mk_pointer(CType *type);
ST_FUNC void vstore(void);
ST_FUNC void inc(int post, int c);
ST_FUNC void parse_mult_str (CString *astr, const char *msg);
ST_FUNC void parse_asm_str(CString *astr);
ST_FUNC int lvalue_type(int t);
ST_FUNC void indir(void);
ST_FUNC void unary(void);
ST_FUNC void expr_prod(void);
ST_FUNC void expr_sum(void);
ST_FUNC void gexpr(void);
ST_FUNC int expr_const(void);
ST_FUNC void decl(int l);
#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_C67
ST_FUNC Sym *get_sym_ref(CType *type, Section *sec, unsigned long offset, unsigned long size);
#endif
#if defined TCC_TARGET_X86_64 && !defined TCC_TARGET_PE
ST_FUNC int classify_x86_64_va_arg(CType *ty);
#endif

/* ------------ tccelf.c ------------ */

#define TCC_OUTPUT_FORMAT_ELF    0 /* default output format: ELF */
#define TCC_OUTPUT_FORMAT_BINARY 1 /* binary image output */
#define TCC_OUTPUT_FORMAT_COFF   2 /* COFF */

#define ARMAG  "!<arch>\012"    /* For COFF and a.out archives */

typedef struct {
    unsigned int n_strx;         /* index into string table of name */
    unsigned char n_type;         /* type of symbol */
    unsigned char n_other;        /* misc info (usually empty) */
    unsigned short n_desc;        /* description field */
    unsigned int n_value;        /* value of symbol */
} Stab_Sym;

ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */
ST_DATA Section *common_section;
ST_DATA Section *cur_text_section; /* current section where function code is generated */
#ifdef CONFIG_TCC_ASM
ST_DATA Section *last_text_section; /* to handle .previous asm directive */
#endif
#ifdef CONFIG_TCC_BCHECK
/* bound check related sections */
ST_DATA Section *bounds_section; /* contains global data bound description */
ST_DATA Section *lbounds_section; /* contains local data bound description */
ST_FUNC void tccelf_bounds_new(TCCState *s);
#endif
/* symbol sections */
ST_DATA Section *symtab_section, *strtab_section;
/* debug sections */
ST_DATA Section *stab_section, *stabstr_section;

ST_FUNC void tccelf_new(TCCState *s);
ST_FUNC void tccelf_delete(TCCState *s);
ST_FUNC void tccelf_stab_new(TCCState *s);

ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags);
ST_FUNC void section_realloc(Section *sec, unsigned long new_size);
ST_FUNC size_t section_add(Section *sec, addr_t size, int align);
ST_FUNC void *section_ptr_add(Section *sec, addr_t size);
ST_FUNC void section_reserve(Section *sec, unsigned long size);
ST_FUNC Section *find_section(TCCState *s1, const char *name);
ST_FUNC Section *new_symtab(TCCState *s1, const char *symtab_name, int sh_type, int sh_flags, const char *strtab_name, const char *hash_name, int hash_sh_flags);

ST_FUNC void put_extern_sym2(Sym *sym, Section *section, addr_t value, unsigned long size, int can_add_underscore);
ST_FUNC void put_extern_sym(Sym *sym, Section *section, addr_t value, unsigned long size);
#if PTR_SIZE == 4
ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type);
#endif
ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type, addr_t addend);

ST_FUNC int put_elf_str(Section *s, const char *sym);
ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int shndx, const char *name);
ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int shndx, const char *name);
ST_FUNC int find_elf_sym(Section *s, const char *name);
ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, int type, int symbol);
ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset, int type, int symbol, addr_t addend);

ST_FUNC void put_stabs(const char *str, int type, int other, int desc, unsigned long value);
ST_FUNC void put_stabs_r(const char *str, int type, int other, int desc, unsigned long value, Section *sec, int sym_index);
ST_FUNC void put_stabn(int type, int other, int desc, int value);
ST_FUNC void put_stabd(int type, int other, int desc);

ST_FUNC void relocate_common_syms(void);
ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve);
ST_FUNC void relocate_section(TCCState *s1, Section *s);

ST_FUNC void tcc_add_linker_symbols(TCCState *s1);
ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h);
ST_FUNC int tcc_load_object_file(TCCState *s1, int fd, unsigned long file_offset);
ST_FUNC int tcc_load_archive(TCCState *s1, int fd);
ST_FUNC void tcc_add_bcheck(TCCState *s1);
ST_FUNC void tcc_add_runtime(TCCState *s1);

ST_FUNC void build_got_entries(TCCState *s1);
ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc);
ST_FUNC void squeeze_multi_relocs(Section *sec, size_t oldrelocoffset);

ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err);
#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE
ST_FUNC void *tcc_get_symbol_err(TCCState *s, const char *name);
#endif

#ifndef TCC_TARGET_PE
ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level);
ST_FUNC int tcc_load_ldscript(TCCState *s1);
ST_FUNC uint8_t *parse_comment(uint8_t *p);
ST_FUNC void minp(void);
ST_INLN void inp(void);
ST_FUNC int handle_eob(void);
#endif

/* ------------ xxx-link.c ------------ */

/* Wether to generate a GOT/PLT entry and when. NO_GOTPLT_ENTRY is first so
   that unknown relocation don't create a GOT or PLT entry */
enum gotplt_entry {
    NO_GOTPLT_ENTRY,    /* never generate (eg. GLOB_DAT & JMP_SLOT relocs) */
    BUILD_GOT_ONLY, /* only build GOT (eg. TPOFF relocs) */
    AUTO_GOTPLT_ENTRY,  /* generate if sym is UNDEF */
    ALWAYS_GOTPLT_ENTRY /* always generate (eg. PLTOFF relocs) */
};

ST_FUNC int code_reloc (int reloc_type);
ST_FUNC int gotplt_entry_type (int reloc_type);
ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr);
ST_FUNC void relocate_init(Section *sr);
ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val);
ST_FUNC void relocate_plt(TCCState *s1);

/* ------------ xxx-gen.c ------------ */

ST_DATA const int reg_classes[NB_REGS];

ST_FUNC void gsym_addr(int t, int a);
ST_FUNC void gsym(int t);
ST_FUNC void load(int r, SValue *sv);
ST_FUNC void store(int r, SValue *v);
ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize);
ST_FUNC void gfunc_call(int nb_args);
ST_FUNC void gfunc_prolog(CType *func_type);
ST_FUNC void gfunc_epilog(void);
ST_FUNC int gjmp(int t);
ST_FUNC void gjmp_addr(int a);
ST_FUNC int gtst(int inv, int t);
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
ST_FUNC void gtst_addr(int inv, int a);
#else
#define gtst_addr(inv, a) gsym_addr(gtst(inv, 0), a)
#endif
ST_FUNC void gen_opi(int op);
ST_FUNC void gen_opf(int op);
ST_FUNC void gen_cvt_ftoi(int t);
ST_FUNC void gen_cvt_ftof(int t);
ST_FUNC void ggoto(void);
#ifndef TCC_TARGET_C67
ST_FUNC void o(unsigned int c);
#endif
#ifndef TCC_TARGET_ARM
ST_FUNC void gen_cvt_itof(int t);
#endif
ST_FUNC void gen_vla_sp_save(int addr);
ST_FUNC void gen_vla_sp_restore(int addr);
ST_FUNC void gen_vla_alloc(CType *type, int align);

static inline uint16_t read16le(unsigned char *p) {
    return p[0] | (uint16_t)p[1] << 8;
}
static inline void write16le(unsigned char *p, uint16_t x) {
    p[0] = x & 255, p[1] = x >> 8 & 255;
}
static inline uint32_t read32le(unsigned char *p) {
  return read16le(p) | (uint32_t)read16le(p + 2) << 16;
}
static inline void write32le(unsigned char *p, uint32_t x) {
    write16le(p, x), write16le(p + 2, x >> 16);
}
static inline void add32le(unsigned char *p, int32_t x) {
    write32le(p, read32le(p) + x);
}
#if HAVE_LONG_LONG
static inline uint64_t read64le(unsigned char *p) {
  return read32le(p) | (uint64_t)read32le(p + 4) << 32;
}
static inline void write64le(unsigned char *p, uint64_t x) {
    write32le(p, x), write32le(p + 4, x >> 32);
}
static inline void add64le(unsigned char *p, int64_t x) {
    write64le(p, read64le(p) + x);
}
#endif
/* ------------ i386-gen.c ------------ */
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
ST_FUNC void g(int c);
ST_FUNC void gen_le16(int c);
ST_FUNC void gen_le32(int c);
ST_FUNC void gen_addr32(int r, Sym *sym, long c);
ST_FUNC void gen_addrpc32(int r, Sym *sym, long c);
#endif

#ifdef CONFIG_TCC_BCHECK
ST_FUNC void gen_bounded_ptr_add(void);
ST_FUNC void gen_bounded_ptr_deref(void);
#endif

/* ------------ x86_64-gen.c ------------ */
#ifdef TCC_TARGET_X86_64
ST_FUNC void gen_addr64(int r, Sym *sym, int64_t c);
ST_FUNC void gen_opl(int op);
#endif

/* ------------ arm-gen.c ------------ */
#ifdef TCC_TARGET_ARM
#if defined(TCC_ARM_EABI) && !defined(CONFIG_TCC_ELFINTERP)
PUB_FUNC const char *default_elfinterp(struct TCCState *s);
#endif
ST_FUNC void arm_init(struct TCCState *s);
ST_FUNC void gen_cvt_itof1(int t);
#endif

/* ------------ arm64-gen.c ------------ */
#ifdef TCC_TARGET_ARM64
ST_FUNC void gen_cvt_sxtw(void);
ST_FUNC void gen_opl(int op);
ST_FUNC void gfunc_return(CType *func_type);
ST_FUNC void gen_va_start(void);
ST_FUNC void gen_va_arg(CType *t);
ST_FUNC void gen_clear_cache(void);
#endif

/* ------------ c67-gen.c ------------ */
#ifdef TCC_TARGET_C67
#endif

/* ------------ tcccoff.c ------------ */

#ifdef TCC_TARGET_COFF
ST_FUNC int tcc_output_coff(TCCState *s1, FILE *f);
ST_FUNC int tcc_load_coff(TCCState * s1, int fd);
#endif

/* ------------ tccasm.c ------------ */
ST_FUNC void asm_instr(void);
ST_FUNC void asm_global_instr(void);
#ifdef CONFIG_TCC_ASM
ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands, const char *name, const char **pp);
ST_FUNC Sym* get_asm_sym(int name, Sym *csym);
ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
ST_FUNC int asm_int_expr(TCCState *s1);
ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess);
/* ------------ i386-asm.c ------------ */
ST_FUNC void gen_expr32(ExprValue *pe);
#ifdef TCC_TARGET_X86_64
ST_FUNC void gen_expr64(ExprValue *pe);
#endif
ST_FUNC void asm_opcode(TCCState *s1, int opcode);
ST_FUNC int asm_parse_regvar(int t);
ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg);
ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier);
ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg);
ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str);
#endif

/* ------------ tccpe.c -------------- */
#ifdef TCC_TARGET_PE
ST_FUNC int pe_load_file(struct TCCState *s1, const char *filename, int fd);
ST_FUNC int pe_output_file(TCCState * s1, const char *filename);
ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t value);
#ifndef TCC_TARGET_ARM
ST_FUNC SValue *pe_getimport(SValue *sv, SValue *v2);
#endif
#ifdef TCC_TARGET_X86_64
ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack);
#endif
PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp);
/* symbol properties stored in Elf32_Sym->st_other */
# define ST_PE_EXPORT 0x10
# define ST_PE_IMPORT 0x20
# define ST_PE_STDCALL 0x40
#endif

/* ------------ tccrun.c ----------------- */
#ifdef TCC_IS_NATIVE
#ifdef CONFIG_TCC_STATIC
#define RTLD_LAZY       0x001
#define RTLD_NOW        0x002
#define RTLD_GLOBAL     0x100
#define RTLD_DEFAULT    NULL
/* dummy function for profiling */
ST_FUNC void *dlopen(const char *filename, int flag);
ST_FUNC void dlclose(void *p);
ST_FUNC const char *dlerror(void);
ST_FUNC void *dlsym(void *handle, const char *symbol);
#endif
#ifdef CONFIG_TCC_BACKTRACE
ST_DATA int rt_num_callers;
ST_DATA const char **rt_bound_error_msg;
ST_DATA void *rt_prog_main;
ST_FUNC void tcc_set_num_callers(int n);
#endif
ST_FUNC void tcc_run_free(TCCState *s1);
#endif

/* ------------ tcctools.c ----------------- */
#if 0 /* included in tcc.c */
ST_FUNC int tcc_tool_ar(TCCState *s, int argc, char **argv);
#ifdef TCC_TARGET_PE
ST_FUNC int tcc_tool_impdef(TCCState *s, int argc, char **argv);
#endif
ST_FUNC void tcc_tool_cross(TCCState *s, char **argv, int option);
ST_FUNC void gen_makedeps(TCCState *s, const char *target, const char *filename);
#endif

/********************************************************/
#undef ST_DATA
#ifdef ONE_SOURCE
#define ST_DATA static
#else
#define ST_DATA
#endif
/********************************************************/
#endif /* _TCC_H */

https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tccpp.c

File: ../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tccpp.c used in: Step 378, Step 395, Step 407, Step 419, Step 431, Step 443, Step 455
/*
 *  TCC - Tiny C Compiler
 * 
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef _TCC_H
#include "tcc.h"
#endif

#if BOOTSTRAP && __arm__
void
tccpp_ok ()
{
}
#endif

/********************************************************/
/* global variables */

ST_DATA int tok_flags;
ST_DATA int parse_flags;

ST_DATA struct BufferedFile *file;
ST_DATA int ch, tok;
ST_DATA CValue tokc;
ST_DATA const int *macro_ptr;
ST_DATA CString tokcstr; /* current parsed string, if any */

/* display benchmark infos */
ST_DATA int total_lines;
ST_DATA int total_bytes;
ST_DATA int tok_ident;
ST_DATA TokenSym **table_ident;

/* ------------------------------------------------------------------------- */

static TokenSym *hash_ident[TOK_HASH_SIZE];
static char token_buf[STRING_MAX_SIZE + 1];
static CString cstr_buf;
static CString macro_equal_buf;
static TokenString tokstr_buf;
static unsigned char isidnum_table[256 - CH_EOF];
static int pp_debug_tok, pp_debug_symv;
static int pp_once;
static void tok_print(const char *msg, const int *str);

static struct TinyAlloc *toksym_alloc;
static struct TinyAlloc *tokstr_alloc;
static struct TinyAlloc *cstr_alloc;

static TokenString *macro_stack;

static const char tcc_keywords[] = 
#define DEF(id, str) str "\0"
#include "tcctok.h"
#undef DEF
;

/* WARNING: the content of this string encodes token numbers */
static const unsigned char tok_two_chars[] =
/* outdated -- gr
    "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253"
    "-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266";
*/{
    '<','=', TOK_LE,
    '>','=', TOK_GE,
    '!','=', TOK_NE,
    '&','&', TOK_LAND,
    '|','|', TOK_LOR,
    '+','+', TOK_INC,
    '-','-', TOK_DEC,
    '=','=', TOK_EQ,
    '<','<', TOK_SHL,
    '>','>', TOK_SAR,
    '+','=', TOK_A_ADD,
    '-','=', TOK_A_SUB,
    '*','=', TOK_A_MUL,
    '/','=', TOK_A_DIV,
    '%','=', TOK_A_MOD,
    '&','=', TOK_A_AND,
    '^','=', TOK_A_XOR,
    '|','=', TOK_A_OR,
    '-','>', TOK_ARROW,
    '.','.', 0xa8, // C++ token ?
    '#','#', TOK_TWOSHARPS,
    0
};

static void next_nomacro_spc(void);

ST_FUNC void skip(int c)
{
    if (tok != c)
        tcc_error("'%c' expected (got \"%s\")", c, get_tok_str(tok, &tokc));
    next();
}

ST_FUNC void expect(const char *msg)
{
    tcc_error("%s expected", msg);
}

/* ------------------------------------------------------------------------- */
/* Custom allocator for tiny objects */

#define USE_TAL

#ifndef USE_TAL
#define tal_free(al, p) tcc_free(p)
#define tal_realloc(al, p, size) tcc_realloc(p, size)
#define tal_new(a,b,c)
#define tal_delete(a)
#else
#if !defined(MEM_DEBUG)
#define tal_free(al, p) tal_free_impl(al, p)
#define tal_realloc(al, p, size) tal_realloc_impl(&al, p, size)
#define TAL_DEBUG_PARAMS
#else
#define TAL_DEBUG 1
//#define TAL_INFO 1 /* collect and dump allocators stats */
#define tal_free(al, p) tal_free_impl(al, p, __FILE__, __LINE__)
#define tal_realloc(al, p, size) tal_realloc_impl(&al, p, size, __FILE__, __LINE__)
#define TAL_DEBUG_PARAMS , const char *file, int line
#define TAL_DEBUG_FILE_LEN 15
#endif

#define TOKSYM_TAL_SIZE     (768 * 1024) /* allocator for tiny TokenSym in table_ident */
#define TOKSTR_TAL_SIZE     (768 * 1024) /* allocator for tiny TokenString instances */
#define CSTR_TAL_SIZE       (256 * 1024) /* allocator for tiny CString instances */
#define TOKSYM_TAL_LIMIT    256 /* prefer unique limits to distinguish allocators debug msgs */
#define TOKSTR_TAL_LIMIT    128 /* 32 * sizeof(int) */
#define CSTR_TAL_LIMIT      1024

typedef struct TinyAlloc {
    unsigned  limit;
    unsigned  size;
    uint8_t *buffer;
    uint8_t *p;
    unsigned  nb_allocs;
    struct TinyAlloc *next, *top;
#ifdef TAL_INFO
    unsigned  nb_peak;
    unsigned  nb_total;
    unsigned  nb_missed;
    uint8_t *peak_p;
#endif
} TinyAlloc;

typedef struct tal_header_t {
    unsigned  size;
#ifdef TAL_DEBUG
    int     line_num; /* negative line_num used for double free check */
    char    file_name[TAL_DEBUG_FILE_LEN + 1];
#endif
} tal_header_t;

/* ------------------------------------------------------------------------- */

static TinyAlloc *tal_new(TinyAlloc **pal, unsigned limit, unsigned size)
{
    TinyAlloc *al = tcc_mallocz(sizeof(TinyAlloc));
    al->p = al->buffer = tcc_malloc(size);
    al->limit = limit;
    al->size = size;
    if (pal) *pal = al;
    return al;
}

static void tal_delete(TinyAlloc *al)
{
    TinyAlloc *next;

tail_call:
    if (!al)
        return;
#ifdef TAL_INFO
    fprintf(stderr, "limit=%5d, size=%5g MB, nb_peak=%6d, nb_total=%8d, nb_missed=%6d, usage=%5.1f%%\n",
            al->limit, al->size / 1024.0 / 1024.0, al->nb_peak, al->nb_total, al->nb_missed,
            (al->peak_p - al->buffer) * 100.0 / al->size);
#endif
#ifdef TAL_DEBUG
    if (al->nb_allocs > 0) {
        uint8_t *p;
        fprintf(stderr, "TAL_DEBUG: memory leak %d chunk(s) (limit= %d)\n",
                al->nb_allocs, al->limit);
        p = al->buffer;
        while (p < al->p) {
            tal_header_t *header = (tal_header_t *)p;
            if (header->line_num > 0) {
                fprintf(stderr, "%s:%d: chunk of %d bytes leaked\n",
                        header->file_name, header->line_num, header->size);
            }
            p += header->size + sizeof(tal_header_t);
        }
#if MEM_DEBUG-0 == 2
        exit(2);
#endif
    }
#endif
    next = al->next;
    tcc_free(al->buffer);
    tcc_free(al);
    al = next;
    goto tail_call;
}

static void tal_free_impl(TinyAlloc *al, void *p TAL_DEBUG_PARAMS)
{
    if (!p)
        return;
tail_call:
    if (al->buffer <= (uint8_t *)p && (uint8_t *)p < al->buffer + al->size) {
#ifdef TAL_DEBUG
        tal_header_t *header = (((tal_header_t *)p) - 1);
        if (header->line_num < 0) {
            fprintf(stderr, "%s:%d: TAL_DEBUG: double frees chunk from\n",
                    file, line);
            fprintf(stderr, "%s:%d: %d bytes\n",
                    header->file_name, (int)-header->line_num, (int)header->size);
        } else
            header->line_num = -header->line_num;
#endif
        al->nb_allocs--;
        if (!al->nb_allocs)
            al->p = al->buffer;
    } else if (al->next) {
        al = al->next;
        goto tail_call;
    }
    else
        tcc_free(p);
}

static void *tal_realloc_impl(TinyAlloc **pal, void *p, unsigned size TAL_DEBUG_PARAMS)
{
    tal_header_t *header;
    void *ret;
    int is_own;
    unsigned adj_size = (size + 3) & -4;
    TinyAlloc *al = *pal;

tail_call:
    is_own = (al->buffer <= (uint8_t *)p && (uint8_t *)p < al->buffer + al->size);
    if ((!p || is_own) && size <= al->limit) {
        if (al->p + adj_size + sizeof(tal_header_t) < al->buffer + al->size) {
            header = (tal_header_t *)al->p;
            header->size = adj_size;
#ifdef TAL_DEBUG
            { int ofs = strlen(file) - TAL_DEBUG_FILE_LEN;
            strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), TAL_DEBUG_FILE_LEN);
            header->file_name[TAL_DEBUG_FILE_LEN] = 0;
            header->line_num = line; }
#endif
            ret = al->p + sizeof(tal_header_t);
            al->p += adj_size + sizeof(tal_header_t);
            if (is_own) {
                header = (((tal_header_t *)p) - 1);
                memcpy(ret, p, header->size);
#ifdef TAL_DEBUG
                header->line_num = -header->line_num;
#endif
            } else {
                al->nb_allocs++;
            }
#ifdef TAL_INFO
            if (al->nb_peak < al->nb_allocs)
                al->nb_peak = al->nb_allocs;
            if (al->peak_p < al->p)
                al->peak_p = al->p;
            al->nb_total++;
#endif
            return ret;
        } else if (is_own) {
            al->nb_allocs--;
            ret = tal_realloc(*pal, 0, size);
            header = (((tal_header_t *)p) - 1);
            memcpy(ret, p, header->size);
#ifdef TAL_DEBUG
            header->line_num = -header->line_num;
#endif
            return ret;
        }
        if (al->next) {
            al = al->next;
        } else {
            TinyAlloc *bottom = al, *next = al->top ? al->top : al;

            al = tal_new(pal, next->limit, next->size * 2);
            al->next = next;
            bottom->top = al;
        }
        goto tail_call;
    }
    if (is_own) {
        al->nb_allocs--;
        ret = tcc_malloc(size);
        header = (((tal_header_t *)p) - 1);
        memcpy(ret, p, header->size);
#ifdef TAL_DEBUG
        header->line_num = -header->line_num;
#endif
    } else if (al->next) {
        al = al->next;
        goto tail_call;
    } else
        ret = tcc_realloc(p, size);
#ifdef TAL_INFO
    al->nb_missed++;
#endif
    return ret;
}

#endif /* USE_TAL */

/* ------------------------------------------------------------------------- */
/* CString handling */
static void cstr_realloc(CString *cstr, int new_size)
{
    int size;

    size = cstr->size_allocated;
    if (size < 8)
        size = 8; /* no need to allocate a too small first string */
    while (size < new_size)
        size = size * 2;
    cstr->data = tal_realloc(cstr_alloc, cstr->data, size);
    cstr->size_allocated = size;
}

/* add a byte */
ST_INLN void cstr_ccat(CString *cstr, int ch)
{
    int size;
    size = cstr->size + 1;
    if (size > cstr->size_allocated)
        cstr_realloc(cstr, size);
    ((unsigned char *)cstr->data)[size - 1] = ch;
    cstr->size = size;
}

ST_FUNC void cstr_cat(CString *cstr, const char *str, int len)
{
    int size;
    if (len <= 0)
        len = strlen(str) + 1 + len;
    size = cstr->size + len;
    if (size > cstr->size_allocated)
        cstr_realloc(cstr, size);
    memmove(((unsigned char *)cstr->data) + cstr->size, str, len);
    cstr->size = size;
}

/* add a wide char */
ST_FUNC void cstr_wccat(CString *cstr, int ch)
{
    int size;
    size = cstr->size + sizeof(nwchar_t);
    if (size > cstr->size_allocated)
        cstr_realloc(cstr, size);
    *(nwchar_t *)(((unsigned char *)cstr->data) + size - sizeof(nwchar_t)) = ch;
    cstr->size = size;
}

ST_FUNC void cstr_new(CString *cstr)
{
    memset(cstr, 0, sizeof(CString));
}

/* free string and reset it to NULL */
ST_FUNC void cstr_free(CString *cstr)
{
    tal_free(cstr_alloc, cstr->data);
    cstr_new(cstr);
}

/* reset string to empty */
ST_FUNC void cstr_reset(CString *cstr)
{
    cstr->size = 0;
}

/* XXX: unicode ? */
static void add_char(CString *cstr, int c)
{
    if (c == '\'' || c == '\"' || c == '\\') {
        /* XXX: could be more precise if char or string */
        cstr_ccat(cstr, '\\');
    }
    if (c >= 32 && c <= 126) {
        cstr_ccat(cstr, c);
    } else {
        cstr_ccat(cstr, '\\');
        if (c == '\n') {
            cstr_ccat(cstr, 'n');
        } else {
            cstr_ccat(cstr, '0' + ((c >> 6) & 7));
            cstr_ccat(cstr, '0' + ((c >> 3) & 7));
            cstr_ccat(cstr, '0' + (c & 7));
        }
    }
}

/* ------------------------------------------------------------------------- */
/* allocate a new token */
static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len)
{
    TokenSym *ts, **ptable;
    int i;

    if (tok_ident >= SYM_FIRST_ANOM) 
        tcc_error("memory full (symbols)");

    /* expand token table if needed */
    i = tok_ident - TOK_IDENT;
    if ((i % TOK_ALLOC_INCR) == 0) {
        ptable = tcc_realloc(table_ident, (i + TOK_ALLOC_INCR) * sizeof(TokenSym *));
        table_ident = ptable;
    }

    ts = tal_realloc(toksym_alloc, 0, sizeof(TokenSym) + len);
    table_ident[i] = ts;
    ts->tok = tok_ident++;
    ts->sym_define = NULL;
    ts->sym_label = NULL;
    ts->sym_struct = NULL;
    ts->sym_identifier = NULL;
    ts->len = len;
    ts->hash_next = NULL;
    memcpy(ts->str, str, len);
    ts->str[len] = '\0';
    *pts = ts;
    return ts;
}

#define TOK_HASH_INIT 1
#define TOK_HASH_FUNC(h, c) ((h) + ((h) << 5) + ((h) >> 27) + (c))


/* find a token and add it if not found */
ST_FUNC TokenSym *tok_alloc(const char *str, int len)
{
    TokenSym *ts, **pts;
    int i;
    unsigned int h;
    
    h = TOK_HASH_INIT;
    for(i=0;i<len;i++)
        h = TOK_HASH_FUNC(h, ((unsigned char *)str)[i]);
    h &= (TOK_HASH_SIZE - 1);

    pts = &hash_ident[h];
    for(;;) {
        ts = *pts;
        if (!ts)
            break;
        if (ts->len == len && !memcmp(ts->str, str, len))
            return ts;
        pts = &(ts->hash_next);
    }
    return tok_alloc_new(pts, str, len);
}

/* XXX: buffer overflow */
/* XXX: float tokens */
ST_FUNC const char *get_tok_str(int v, CValue *cv)
{
    char *p;
    int i, len;

    cstr_reset(&cstr_buf);
    p = cstr_buf.data;

    switch(v) {
    case TOK_CINT:
    case TOK_CUINT:
    case TOK_CLLONG:
    case TOK_CULLONG:
        /* XXX: not quite exact, but only useful for testing  */
#ifdef _WIN32
        sprintf(p, "%u", (unsigned)cv->i);
#else
        sprintf(p, "%llu", (unsigned long long)cv->i);
#endif
        break;
    case TOK_LCHAR:
        cstr_ccat(&cstr_buf, 'L');
    case TOK_CCHAR:
        cstr_ccat(&cstr_buf, '\'');
        add_char(&cstr_buf, cv->i);
        cstr_ccat(&cstr_buf, '\'');
        cstr_ccat(&cstr_buf, '\0');
        break;
    case TOK_PPNUM:
    case TOK_PPSTR:
        return (char*)cv->str.data;
    case TOK_LSTR:
        cstr_ccat(&cstr_buf, 'L');
    case TOK_STR:
        cstr_ccat(&cstr_buf, '\"');
        if (v == TOK_STR) {
            len = cv->str.size - 1;
            for(i=0;i<len;i++)
                add_char(&cstr_buf, ((unsigned char *)cv->str.data)[i]);
        } else {
            len = (cv->str.size / sizeof(nwchar_t)) - 1;
            for(i=0;i<len;i++)
                add_char(&cstr_buf, ((nwchar_t *)cv->str.data)[i]);
        }
        cstr_ccat(&cstr_buf, '\"');
        cstr_ccat(&cstr_buf, '\0');
        break;

    case TOK_CFLOAT:
        cstr_cat(&cstr_buf, "<float>", 0);
        break;
    case TOK_CDOUBLE:
    cstr_cat(&cstr_buf, "<double>", 0);
    break;
    case TOK_CLDOUBLE:
    cstr_cat(&cstr_buf, "<long double>", 0);
    break;
    case TOK_LINENUM:
    cstr_cat(&cstr_buf, "<linenumber>", 0);
    break;

    /* above tokens have value, the ones below don't */

    case TOK_LT:
        v = '<';
        goto addv;
    case TOK_GT:
        v = '>';
        goto addv;
    case TOK_DOTS:
        return strcpy(p, "...");
    case TOK_A_SHL:
        return strcpy(p, "<<=");
    case TOK_A_SAR:
        return strcpy(p, ">>=");
    default:
        if (v < TOK_IDENT) {
            /* search in two bytes table */
            const unsigned char *q = tok_two_chars;
            while (*q) {
                if (q[2] == v) {
                    *p++ = q[0];
                    *p++ = q[1];
                    *p = '\0';
                    return cstr_buf.data;
                }
                q += 3;
            }
        if (v >= 127) {
            sprintf(cstr_buf.data, "<%02x>", v);
            return cstr_buf.data;
        }
        addv:
            *p++ = v;
            *p = '\0';
        } else if (v < tok_ident) {
            return table_ident[v - TOK_IDENT]->str;
        } else if (v >= SYM_FIRST_ANOM) {
            /* special name for anonymous symbol */
            sprintf(p, "L.%u", v - SYM_FIRST_ANOM);
        } else {
            /* should never happen */
            return NULL;
        }
        break;
    }
    return cstr_buf.data;
}

/* return the current character, handling end of block if necessary
   (but not stray) */
ST_FUNC int handle_eob(void)
{
    BufferedFile *bf = file;
    int len;

    /* only tries to read if really end of buffer */
    if (bf->buf_ptr >= bf->buf_end) {
        if (bf->fd != -1) {
#if defined(PARSE_DEBUG)
            len = 1;
#else
            len = IO_BUF_SIZE;
#endif
            len = read(bf->fd, bf->buffer, len);
            if (len < 0)
                len = 0;
        } else {
            len = 0;
        }
        total_bytes += len;
        bf->buf_ptr = bf->buffer;
        bf->buf_end = bf->buffer + len;
        *bf->buf_end = CH_EOB;
    }
    if (bf->buf_ptr < bf->buf_end) {
        return bf->buf_ptr[0];
    } else {
        bf->buf_ptr = bf->buf_end;
        return CH_EOF;
    }
}

/* read next char from current input file and handle end of input buffer */
ST_INLN void inp(void)
{
    ch = *(++(file->buf_ptr));
    /* end of buffer/file handling */
    if (ch == CH_EOB)
        ch = handle_eob();
}

/* handle '\[\r]\n' */
static int handle_stray_noerror(void)
{
    while (ch == '\\') {
        inp();
        if (ch == '\n') {
            file->line_num++;
            inp();
        } else if (ch == '\r') {
            inp();
            if (ch != '\n')
                goto fail;
            file->line_num++;
            inp();
        } else {
        fail:
            return 1;
        }
    }
    return 0;
}

static void handle_stray(void)
{
    if (handle_stray_noerror())
        tcc_error("stray '\\' in program");
}

/* skip the stray and handle the \\n case. Output an error if
   incorrect char after the stray */
static int handle_stray1(uint8_t *p)
{
    int c;

    file->buf_ptr = p;
    if (p >= file->buf_end) {
        c = handle_eob();
        if (c != '\\')
            return c;
        p = file->buf_ptr;
    }
    ch = *p;
    if (handle_stray_noerror()) {
        if (!(parse_flags & PARSE_FLAG_ACCEPT_STRAYS))
            tcc_error("stray '\\' in program");
        *--file->buf_ptr = '\\';
    }
    p = file->buf_ptr;
    c = *p;
    return c;
}

/* handle just the EOB case, but not stray */
#define PEEKC_EOB(c, p)\
{\
    p++;\
    c = *p;\
    if (c == '\\') {\
        file->buf_ptr = p;\
        c = handle_eob();\
        p = file->buf_ptr;\
    }\
}

/* handle the complicated stray case */
#define PEEKC(c, p)\
{\
    p++;\
    c = *p;\
    if (c == '\\') {\
        c = handle_stray1(p);\
        p = file->buf_ptr;\
    }\
}

/* input with '\[\r]\n' handling. Note that this function cannot
   handle other characters after '\', so you cannot call it inside
   strings or comments */
ST_FUNC void minp(void)
{
    inp();
    if (ch == '\\') 
        handle_stray();
}

/* single line C++ comments */
static uint8_t *parse_line_comment(uint8_t *p)
{
    int c;

    p++;
    for(;;) {
        c = *p;
    redo:
        if (c == '\n' || c == CH_EOF) {
            break;
        } else if (c == '\\') {
            file->buf_ptr = p;
            c = handle_eob();
            p = file->buf_ptr;
            if (c == '\\') {
                PEEKC_EOB(c, p);
                if (c == '\n') {
                    file->line_num++;
                    PEEKC_EOB(c, p);
                } else if (c == '\r') {
                    PEEKC_EOB(c, p);
                    if (c == '\n') {
                        file->line_num++;
                        PEEKC_EOB(c, p);
                    }
                }
            } else {
                goto redo;
            }
        } else {
            p++;
        }
    }
    return p;
}

/* C comments */
ST_FUNC uint8_t *parse_comment(uint8_t *p)
{
    int c;

    p++;
    for(;;) {
        /* fast skip loop */
        for(;;) {
            c = *p;
            if (c == '\n' || c == '*' || c == '\\')
                break;
            p++;
            c = *p;
            if (c == '\n' || c == '*' || c == '\\')
                break;
            p++;
        }
        /* now we can handle all the cases */
        if (c == '\n') {
            file->line_num++;
            p++;
        } else if (c == '*') {
            p++;
            for(;;) {
                c = *p;
                if (c == '*') {
                    p++;
                } else if (c == '/') {
                    goto end_of_comment;
                } else if (c == '\\') {
                    file->buf_ptr = p;
                    c = handle_eob();
                    p = file->buf_ptr;
                    if (c == CH_EOF)
                        tcc_error("unexpected end of file in comment");
                    if (c == '\\') {
                        /* skip '\[\r]\n', otherwise just skip the stray */
                        while (c == '\\') {
                            PEEKC_EOB(c, p);
                            if (c == '\n') {
                                file->line_num++;
                                PEEKC_EOB(c, p);
                            } else if (c == '\r') {
                                PEEKC_EOB(c, p);
                                if (c == '\n') {
                                    file->line_num++;
                                    PEEKC_EOB(c, p);
                                }
                            } else {
                                goto after_star;
                            }
                        }
                    }
                } else {
                    break;
                }
            }
        after_star: ;
        } else {
            /* stray, eob or eof */
            file->buf_ptr = p;
            c = handle_eob();
            p = file->buf_ptr;
            if (c == CH_EOF) {
                tcc_error("unexpected end of file in comment");
            } else if (c == '\\') {
                p++;
            }
        }
    }
 end_of_comment:
    p++;
    return p;
}

ST_FUNC void set_idnum(int c, int val)
{
    isidnum_table[c - CH_EOF] = val;
}

#define cinp minp

static inline void skip_spaces(void)
{
    while (isidnum_table[ch - CH_EOF] & IS_SPC)
        cinp();
}

static inline int check_space(int t, int *spc) 
{
    if (t < 256 && (isidnum_table[t - CH_EOF] & IS_SPC)) {
        if (*spc) 
            return 1;
        *spc = 1;
    } else 
        *spc = 0;
    return 0;
}

/* parse a string without interpreting escapes */
static uint8_t *parse_pp_string(uint8_t *p,
                                int sep, CString *str)
{
    int c;
    p++;
    for(;;) {
        c = *p;
        if (c == sep) {
            break;
        } else if (c == '\\') {
            file->buf_ptr = p;
            c = handle_eob();
            p = file->buf_ptr;
            if (c == CH_EOF) {
            unterminated_string:
                /* XXX: indicate line number of start of string */
                tcc_error("missing terminating %c character", sep);
            } else if (c == '\\') {
                /* escape : just skip \[\r]\n */
                PEEKC_EOB(c, p);
                if (c == '\n') {
                    file->line_num++;
                    p++;
                } else if (c == '\r') {
                    PEEKC_EOB(c, p);
                    if (c != '\n')
                        expect("'\n' after '\r'");
                    file->line_num++;
                    p++;
                } else if (c == CH_EOF) {
                    goto unterminated_string;
                } else {
                    if (str) {
                        cstr_ccat(str, '\\');
                        cstr_ccat(str, c);
                    }
                    p++;
                }
            }
        } else if (c == '\n') {
            file->line_num++;
            goto add_char;
        } else if (c == '\r') {
            PEEKC_EOB(c, p);
            if (c != '\n') {
                if (str)
                    cstr_ccat(str, '\r');
            } else {
                file->line_num++;
                goto add_char;
            }
        } else {
        add_char:
            if (str)
                cstr_ccat(str, c);
            p++;
        }
    }
    p++;
    return p;
}

/* skip block of text until #else, #elif or #endif. skip also pairs of
   #if/#endif */
static void preprocess_skip(void)
{
    int a, start_of_line, c, in_warn_or_error;
    uint8_t *p;

    p = file->buf_ptr;
    a = 0;
redo_start:
    start_of_line = 1;
    in_warn_or_error = 0;
    for(;;) {
    redo_no_start:
        c = *p;
        switch(c) {
        case ' ':
        case '\t':
        case '\f':
        case '\v':
        case '\r':
            p++;
            goto redo_no_start;
        case '\n':
            file->line_num++;
            p++;
            goto redo_start;
        case '\\':
            file->buf_ptr = p;
            c = handle_eob();
            if (c == CH_EOF) {
                expect("#endif");
            } else if (c == '\\') {
                ch = file->buf_ptr[0];
                handle_stray_noerror();
            }
            p = file->buf_ptr;
            goto redo_no_start;
        /* skip strings */
        case '\"':
        case '\'':
            if (in_warn_or_error)
                goto _default;
            p = parse_pp_string(p, c, NULL);
            break;
        /* skip comments */
        case '/':
            if (in_warn_or_error)
                goto _default;
            file->buf_ptr = p;
            ch = *p;
            minp();
            p = file->buf_ptr;
            if (ch == '*') {
                p = parse_comment(p);
            } else if (ch == '/') {
                p = parse_line_comment(p);
            }
            break;
        case '#':
            p++;
            if (start_of_line) {
                file->buf_ptr = p;
                next_nomacro();
                p = file->buf_ptr;
                if (a == 0 && 
                    (tok == TOK_ELSE || tok == TOK_ELIF || tok == TOK_ENDIF))
                    goto the_end;
                if (tok == TOK_IF || tok == TOK_IFDEF || tok == TOK_IFNDEF)
                    a++;
                else if (tok == TOK_ENDIF)
                    a--;
                else if( tok == TOK_ERROR || tok == TOK_WARNING)
                    in_warn_or_error = 1;
                else if (tok == TOK_LINEFEED)
                    goto redo_start;
                else if (parse_flags & PARSE_FLAG_ASM_FILE)
                    p = parse_line_comment(p - 1);
            } else if (parse_flags & PARSE_FLAG_ASM_FILE)
                p = parse_line_comment(p - 1);
            break;
_default:
        default:
            p++;
            break;
        }
        start_of_line = 0;
    }
 the_end: ;
    file->buf_ptr = p;
}

/* ParseState handling */

/* XXX: currently, no include file info is stored. Thus, we cannot display
   accurate messages if the function or data definition spans multiple
   files */

/* save current parse state in 's' */
ST_FUNC void save_parse_state(ParseState *s)
{
    s->line_num = file->line_num;
    s->macro_ptr = macro_ptr;
    s->tok = tok;
    s->tokc = tokc;
}

/* restore parse state from 's' */
ST_FUNC void restore_parse_state(ParseState *s)
{
    file->line_num = s->line_num;
    macro_ptr = s->macro_ptr;
    tok = s->tok;
    tokc = s->tokc;
}

#if 0
/* return the number of additional 'ints' necessary to store the
   token */
static inline int tok_size(const int *p)
{
    switch(*p) {
        /* 4 bytes */
    case TOK_CINT:
    case TOK_CUINT:
    case TOK_CCHAR:
    case TOK_LCHAR:
    case TOK_CFLOAT:
    case TOK_LINENUM:
        return 1 + 1;
    case TOK_STR:
    case TOK_LSTR:
    case TOK_PPNUM:
    case TOK_PPSTR:
        return 1 + ((sizeof(CString) + ((CString *)(p+1))->size + 3) >> 2);
    case TOK_CDOUBLE:
    case TOK_CLLONG:
    case TOK_CULLONG:
        return 1 + 2;
    case TOK_CLDOUBLE:
        return 1 + LDOUBLE_SIZE / 4;
    default:
        return 1 + 0;
    }
}
#endif

/* token string handling */
ST_INLN void tok_str_new(TokenString *s)
{
    s->str = NULL;
    s->len = 0;
    s->allocated_len = 0;
    s->last_line_num = -1;
}

ST_FUNC TokenString *tok_str_alloc(void)
{
    TokenString *str = tal_realloc(tokstr_alloc, 0, sizeof *str);
    tok_str_new(str);
    return str;
}

ST_FUNC int *tok_str_dup(TokenString *s)
{
    int *str;

    str = tal_realloc(tokstr_alloc, 0, s->len * sizeof(int));
    memcpy(str, s->str, s->len * sizeof(int));
    return str;
}

ST_FUNC void tok_str_free_str(int *str)
{
    tal_free(tokstr_alloc, str);
}

ST_FUNC void tok_str_free(TokenString *str)
{
    tok_str_free_str(str->str);
    tal_free(tokstr_alloc, str);
}

ST_FUNC int *tok_str_realloc(TokenString *s, int new_size)
{
    int *str, size;

    size = s->allocated_len;
    if (size < 16)
        size = 16;
    while (size < new_size)
        size = size * 2;
    if (size > s->allocated_len) {
        str = tal_realloc(tokstr_alloc, s->str, size * sizeof(int));
        s->allocated_len = size;
        s->str = str;
    }
    return s->str;
}

ST_FUNC void tok_str_add(TokenString *s, int t)
{
    int len, *str;

    len = s->len;
    str = s->str;
    if (len >= s->allocated_len)
        str = tok_str_realloc(s, len + 1);
    str[len++] = t;
    s->len = len;
}

ST_FUNC void begin_macro(TokenString *str, int alloc)
{
    str->alloc = alloc;
    str->prev = macro_stack;
    str->prev_ptr = macro_ptr;
    macro_ptr = str->str;
    macro_stack = str;
}

ST_FUNC void end_macro(void)
{
    TokenString *str = macro_stack;
    macro_stack = str->prev;
    macro_ptr = str->prev_ptr;
    if (str->alloc == 2) {
        str->alloc = 3; /* just mark as finished */
    } else {
        tok_str_free(str);
    }
}

static void tok_str_add2(TokenString *s, int t, CValue *cv)
{
    int len, *str;

    len = s->len;
    str = s->str;

    /* allocate space for worst case */
    if (len + TOK_MAX_SIZE >= s->allocated_len)
        str = tok_str_realloc(s, len + TOK_MAX_SIZE + 1);
    str[len++] = t;
    switch(t) {
    case TOK_CINT:
    case TOK_CUINT:
    case TOK_CCHAR:
    case TOK_LCHAR:
    case TOK_CFLOAT:
    case TOK_LINENUM:
        str[len++] = cv->tab[0];
        break;
    case TOK_PPNUM:
    case TOK_PPSTR:
    case TOK_STR:
    case TOK_LSTR:
        {
            /* Insert the string into the int array. */
            size_t nb_words =
                1 + (cv->str.size + sizeof(int) - 1) / sizeof(int);
            if (len + nb_words >= s->allocated_len)
                str = tok_str_realloc(s, len + nb_words + 1);
            str[len] = cv->str.size;
            memcpy(&str[len + 1], cv->str.data, cv->str.size);
            len += nb_words;
        }
        break;
    case TOK_CDOUBLE:
    case TOK_CLLONG:
    case TOK_CULLONG:
#if LDOUBLE_SIZE == 8
    case TOK_CLDOUBLE:
#endif
        str[len++] = cv->tab[0];
        str[len++] = cv->tab[1];
        break;
#if LDOUBLE_SIZE == 12
    case TOK_CLDOUBLE:
        str[len++] = cv->tab[0];
        str[len++] = cv->tab[1];
        str[len++] = cv->tab[2];
#elif LDOUBLE_SIZE == 16
    case TOK_CLDOUBLE:
        str[len++] = cv->tab[0];
        str[len++] = cv->tab[1];
        str[len++] = cv->tab[2];
        str[len++] = cv->tab[3];
#elif LDOUBLE_SIZE != 8
#error add long double size support
#endif
        break;
    default:
        break;
    }
    s->len = len;
}

/* add the current parse token in token string 's' */
ST_FUNC void tok_str_add_tok(TokenString *s)
{
    CValue cval;

    /* save line number info */
    if (file->line_num != s->last_line_num) {
        s->last_line_num = file->line_num;
        cval.i = s->last_line_num;
        tok_str_add2(s, TOK_LINENUM, &cval);
    }
    tok_str_add2(s, tok, &tokc);
}

/* get a token from an integer array and increment pointer
   accordingly. we code it as a macro to avoid pointer aliasing. */
static inline void TOK_GET(int *t, const int **pp, CValue *cv)
{
    const int *p = *pp;
    int n, *tab;

    tab = cv->tab;
    switch(*t = *p++) {
    case TOK_CINT:
    case TOK_CUINT:
    case TOK_CCHAR:
    case TOK_LCHAR:
    case TOK_LINENUM:
        tab[0] = *p++;
    cv->i = (*t == TOK_CUINT) ? (unsigned)cv->i : (int)cv->i;
        break;
    case TOK_CFLOAT:
    tab[0] = *p++;
    break;
    case TOK_STR:
    case TOK_LSTR:
    case TOK_PPNUM:
    case TOK_PPSTR:
        cv->str.size = *p++;
        cv->str.data = p;
        p += (cv->str.size + sizeof(int) - 1) / sizeof(int);
        break;
    case TOK_CDOUBLE:
    case TOK_CLLONG:
    case TOK_CULLONG:
        n = 2;
        goto copy;
    case TOK_CLDOUBLE:
#if LDOUBLE_SIZE == 16
        n = 4;
#elif LDOUBLE_SIZE == 12
        n = 3;
#elif LDOUBLE_SIZE == 8
        n = 2;
#else
# error add long double size support
#endif
    copy:
        do
            *tab++ = *p++;
        while (--n);
        break;
    default:
        break;
    }
    *pp = p;
}

/* Calling this function is expensive, but it is not possible
   to read a token string backwards. */
static int tok_last(const int *str0, const int *str1)
{
    const int *str = str0;
    int tok = 0;
    CValue cval;

    while (str < str1)
        TOK_GET(&tok, &str, &cval);
    return tok;
}

static int macro_is_equal(const int *a, const int *b)
{
    CValue cv;
    int t;

    if (!a || !b)
        return 1;

    while (*a && *b) {
        /* first time preallocate macro_equal_buf, next time only reset position to start */
        cstr_reset(&macro_equal_buf);
        TOK_GET(&t, &a, &cv);
        cstr_cat(&macro_equal_buf, get_tok_str(t, &cv), 0);
        TOK_GET(&t, &b, &cv);
        if (strcmp(macro_equal_buf.data, get_tok_str(t, &cv)))
            return 0;
    }
    return !(*a || *b);
}

/* defines handling */
ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg)
{
    Sym *s, *o;

    o = define_find(v);
    s = sym_push2(&define_stack, v, macro_type, 0);
    s->d = str;
    s->next = first_arg;
    table_ident[v - TOK_IDENT]->sym_define = s;

    if (o && !macro_is_equal(o->d, s->d))
    tcc_warning("%s redefined", get_tok_str(v, NULL));
}

/* undefined a define symbol. Its name is just set to zero */
ST_FUNC void define_undef(Sym *s)
{
    int v = s->v;
    if (v >= TOK_IDENT && v < tok_ident)
        table_ident[v - TOK_IDENT]->sym_define = NULL;
}

ST_INLN Sym *define_find(int v)
{
    v -= TOK_IDENT;
    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
        return NULL;
    return table_ident[v]->sym_define;
}

/* free define stack until top reaches 'b' */
ST_FUNC void free_defines(Sym *b)
{
    while (define_stack != b) {
        Sym *top = define_stack;
        define_stack = top->prev;
        tok_str_free_str(top->d);
        define_undef(top);
        sym_free(top);
    }

    /* restore remaining (-D or predefined) symbols if they were
       #undef'd in the file */
    while (b) {
        int v = b->v;
        if (v >= TOK_IDENT && v < tok_ident) {
            Sym **d = &table_ident[v - TOK_IDENT]->sym_define;
            if (!*d)
                *d = b;
        }
        b = b->prev;
    }
}

/* label lookup */
ST_FUNC Sym *label_find(int v)
{
    v -= TOK_IDENT;
    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
        return NULL;
    return table_ident[v]->sym_label;
}

ST_FUNC Sym *label_push(Sym **ptop, int v, int flags)
{
    Sym *s, **ps;
    s = sym_push2(ptop, v, 0, 0);
    s->r = flags;
    ps = &table_ident[v - TOK_IDENT]->sym_label;
    if (ptop == &global_label_stack) {
        /* modify the top most local identifier, so that
           sym_identifier will point to 's' when popped */
        while (*ps != NULL)
            ps = &(*ps)->prev_tok;
    }
    s->prev_tok = *ps;
    *ps = s;
    return s;
}

/* pop labels until element last is reached. Look if any labels are
   undefined. Define symbols if '&&label' was used. */
ST_FUNC void label_pop(Sym **ptop, Sym *slast)
{
    Sym *s, *s1;
    for(s = *ptop; s != slast; s = s1) {
        s1 = s->prev;
        if (s->r == LABEL_DECLARED) {
            tcc_warning("label '%s' declared but not used", get_tok_str(s->v, NULL));
        } else if (s->r == LABEL_FORWARD) {
                tcc_error("label '%s' used but not defined",
                      get_tok_str(s->v, NULL));
        } else {
            if (s->c) {
                /* define corresponding symbol. A size of
                   1 is put. */
                put_extern_sym(s, cur_text_section, s->jnext, 1);
            }
        }
        /* remove label */
        table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok;
        sym_free(s);
    }
    *ptop = slast;
}

/* eval an expression for #if/#elif */
static int expr_preprocess(void)
{
    int c, t;
    TokenString *str;
    
    str = tok_str_alloc();
    while (tok != TOK_LINEFEED && tok != TOK_EOF) {
        next(); /* do macro subst */
        if (tok == TOK_DEFINED) {
            next_nomacro();
            t = tok;
            if (t == '(') 
                next_nomacro();
            c = define_find(tok) != 0;
            if (t == '(')
                next_nomacro();
            tok = TOK_CINT;
            tokc.i = c;
        } else if (tok >= TOK_IDENT) {
            /* if undefined macro */
            tok = TOK_CINT;
            tokc.i = 0;
        }
        tok_str_add_tok(str);
    }
    tok_str_add(str, -1); /* simulate end of file */
    tok_str_add(str, 0);
    /* now evaluate C constant expression */
    begin_macro(str, 1);
    next();
    c = expr_const();
    end_macro();
    return c != 0;
}


/* parse after #define */
ST_FUNC void parse_define(void)
{
    Sym *s, *first, **ps;
    int v, t, varg, is_vaargs, spc;
    int saved_parse_flags = parse_flags;

    v = tok;
    if (v < TOK_IDENT)
        tcc_error("invalid macro name '%s'", get_tok_str(tok, &tokc));
    /* XXX: should check if same macro (ANSI) */
    first = NULL;
    t = MACRO_OBJ;
    /* We have to parse the whole define as if not in asm mode, in particular
       no line comment with '#' must be ignored.  Also for function
       macros the argument list must be parsed without '.' being an ID
       character.  */
    parse_flags = ((parse_flags & ~PARSE_FLAG_ASM_FILE) | PARSE_FLAG_SPACES);
    /* '(' must be just after macro definition for MACRO_FUNC */
    next_nomacro_spc();
    if (tok == '(') {
        set_idnum('.', 0);
        next_nomacro();
        ps = &first;
        if (tok != ')') for (;;) {
            varg = tok;
            next_nomacro();
            is_vaargs = 0;
            if (varg == TOK_DOTS) {
                varg = TOK___VA_ARGS__;
                is_vaargs = 1;
            } else if (tok == TOK_DOTS && gnu_ext) {
                is_vaargs = 1;
                next_nomacro();
            }
            if (varg < TOK_IDENT)
        bad_list:
                tcc_error("bad macro parameter list");
            s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0);
            *ps = s;
            ps = &s->next;
            if (tok == ')')
                break;
            if (tok != ',' || is_vaargs)
                goto bad_list;
            next_nomacro();
        }
        next_nomacro_spc();
        t = MACRO_FUNC;
    }

    tokstr_buf.len = 0;
    spc = 2;
    parse_flags |= PARSE_FLAG_ACCEPT_STRAYS | PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED;
    /* The body of a macro definition should be parsed such that identifiers
       are parsed like the file mode determines (i.e. with '.' being an
       ID character in asm mode).  But '#' should be retained instead of
       regarded as line comment leader, so still don't set ASM_FILE
       in parse_flags. */
    set_idnum('.', (saved_parse_flags & PARSE_FLAG_ASM_FILE) ? IS_ID : 0);
    while (tok != TOK_LINEFEED && tok != TOK_EOF) {
        /* remove spaces around ## and after '#' */
        if (TOK_TWOSHARPS == tok) {
            if (2 == spc)
                goto bad_twosharp;
            if (1 == spc)
                --tokstr_buf.len;
            spc = 3;
        tok = TOK_PPJOIN;
        } else if ('#' == tok) {
            spc = 4;
        } else if (check_space(tok, &spc)) {
            goto skip;
        }
        tok_str_add2(&tokstr_buf, tok, &tokc);
    skip:
        next_nomacro_spc();
    }

    parse_flags = saved_parse_flags;
    if (spc == 1)
        --tokstr_buf.len; /* remove trailing space */
    tok_str_add(&tokstr_buf, 0);
    if (3 == spc)
bad_twosharp:
        tcc_error("'##' cannot appear at either end of macro");
    define_push(v, t, tok_str_dup(&tokstr_buf), first);
}

static CachedInclude *search_cached_include(TCCState *s1, const char *filename, int add)
{
    const unsigned char *s;
    unsigned int h;
    CachedInclude *e;
    int i;

    h = TOK_HASH_INIT;
    s = (unsigned char *) filename;
    while (*s) {
#ifdef _WIN32
        h = TOK_HASH_FUNC(h, toup(*s));
#else
        h = TOK_HASH_FUNC(h, *s);
#endif
        s++;
    }
    h &= (CACHED_INCLUDES_HASH_SIZE - 1);

    i = s1->cached_includes_hash[h];
    for(;;) {
        if (i == 0)
            break;
        e = s1->cached_includes[i - 1];
        if (0 == PATHCMP(e->filename, filename))
            return e;
        i = e->hash_next;
    }
    if (!add)
        return NULL;

    e = tcc_malloc(sizeof(CachedInclude) + strlen(filename));
    strcpy(e->filename, filename);
    e->ifndef_macro = e->once = 0;
    dynarray_add(&s1->cached_includes, &s1->nb_cached_includes, e);
    /* add in hash table */
    e->hash_next = s1->cached_includes_hash[h];
    s1->cached_includes_hash[h] = s1->nb_cached_includes;
#ifdef INC_DEBUG
    printf("adding cached '%s'\n", filename);
#endif
    return e;
}

static void pragma_parse(TCCState *s1)
{
    next_nomacro();
    if (tok == TOK_push_macro || tok == TOK_pop_macro) {
        int t = tok, v;
        Sym *s;

        if (next(), tok != '(')
            goto pragma_err;
        if (next(), tok != TOK_STR)
            goto pragma_err;
        v = tok_alloc(tokc.str.data, tokc.str.size - 1)->tok;
        if (next(), tok != ')')
            goto pragma_err;
        if (t == TOK_push_macro) {
            while (NULL == (s = define_find(v)))
                define_push(v, 0, NULL, NULL);
            s->type.ref = s; /* set push boundary */
        } else {
            for (s = define_stack; s; s = s->prev)
                if (s->v == v && s->type.ref == s) {
                    s->type.ref = NULL;
                    break;
                }
        }
        if (s)
            table_ident[v - TOK_IDENT]->sym_define = s->d ? s : NULL;
        else
            tcc_warning("unbalanced #pragma pop_macro");
        pp_debug_tok = t, pp_debug_symv = v;

    } else if (tok == TOK_once) {
        search_cached_include(s1, file->filename, 1)->once = pp_once;

    } else if (s1->ppfp) {
        /* tcc -E: keep pragmas below unchanged */
        unget_tok(' ');
        unget_tok(TOK_PRAGMA);
        unget_tok('#');
        unget_tok(TOK_LINEFEED);

    } else if (tok == TOK_pack) {
        /* This may be:
           #pragma pack(1) // set
           #pragma pack() // reset to default
           #pragma pack(push,1) // push & set
           #pragma pack(pop) // restore previous */
        next();
        skip('(');
        if (tok == TOK_ASM_pop) {
            next();
            if (s1->pack_stack_ptr <= s1->pack_stack) {
            stk_error:
                tcc_error("out of pack stack");
            }
            s1->pack_stack_ptr--;
        } else {
            int val = 0;
            if (tok != ')') {
                if (tok == TOK_ASM_push) {
                    next();
                    if (s1->pack_stack_ptr >= s1->pack_stack + PACK_STACK_SIZE - 1)
                        goto stk_error;
                    s1->pack_stack_ptr++;
                    skip(',');
                }
                if (tok != TOK_CINT)
                    goto pragma_err;
                val = tokc.i;
                if (val < 1 || val > 16 || (val & (val - 1)) != 0)
                    goto pragma_err;
                next();
            }
            *s1->pack_stack_ptr = val;
        }
        if (tok != ')')
            goto pragma_err;

    } else if (tok == TOK_comment) {
        char *file;
        next();
        skip('(');
        if (tok != TOK_lib)
            goto pragma_warn;
        next();
        skip(',');
        if (tok != TOK_STR)
            goto pragma_err;
        file = tcc_strdup((char *)tokc.str.data);
        dynarray_add(&s1->pragma_libs, &s1->nb_pragma_libs, file);
        next();
        if (tok != ')')
            goto pragma_err;
    } else {
pragma_warn:
        if (s1->warn_unsupported)
            tcc_warning("#pragma %s is ignored", get_tok_str(tok, &tokc));
    }
    return;

pragma_err:
    tcc_error("malformed #pragma directive");
    return;
}

/* is_bof is true if first non space token at beginning of file */
ST_FUNC void preprocess(int is_bof)
{
    TCCState *s1 = tcc_state;
    int i, c, n, saved_parse_flags;
    char buf[1024], *q;
    Sym *s;

    saved_parse_flags = parse_flags;
    parse_flags = PARSE_FLAG_PREPROCESS
        | PARSE_FLAG_TOK_NUM
        | PARSE_FLAG_TOK_STR
        | PARSE_FLAG_LINEFEED
        | (parse_flags & PARSE_FLAG_ASM_FILE)
        ;

    next_nomacro();
 redo:
    switch(tok) {
    case TOK_DEFINE:
        pp_debug_tok = tok;
        next_nomacro();
        pp_debug_symv = tok;
        parse_define();
        break;
    case TOK_UNDEF:
        pp_debug_tok = tok;
        next_nomacro();
        pp_debug_symv = tok;
        s = define_find(tok);
        /* undefine symbol by putting an invalid name */
        if (s)
            define_undef(s);
        break;
    case TOK_INCLUDE:
    case TOK_INCLUDE_NEXT:
        ch = file->buf_ptr[0];
        /* XXX: incorrect if comments : use next_nomacro with a special mode */
        skip_spaces();
        if (ch == '<') {
            c = '>';
            goto read_name;
        } else if (ch == '\"') {
            c = ch;
        read_name:
            inp();
            q = buf;
            while (ch != c && ch != '\n' && ch != CH_EOF) {
                if ((q - buf) < sizeof(buf) - 1)
                    *q++ = ch;
                if (ch == '\\') {
                    if (handle_stray_noerror() == 0)
                        --q;
                } else
                    inp();
            }
            *q = '\0';
            minp();
#if 0
            /* eat all spaces and comments after include */
            /* XXX: slightly incorrect */
            while (ch1 != '\n' && ch1 != CH_EOF)
                inp();
#endif
        } else {
        int len;
            /* computed #include : concatenate everything up to linefeed,
           the result must be one of the two accepted forms.
           Don't convert pp-tokens to tokens here.  */
        parse_flags = (PARSE_FLAG_PREPROCESS
               | PARSE_FLAG_LINEFEED
               | (parse_flags & PARSE_FLAG_ASM_FILE));
            next();
            buf[0] = '\0';
        while (tok != TOK_LINEFEED) {
        pstrcat(buf, sizeof(buf), get_tok_str(tok, &tokc));
        next();
        }
        len = strlen(buf);
        /* check syntax and remove '<>|""' */
        if ((len < 2 || ((buf[0] != '"' || buf[len-1] != '"') &&
                 (buf[0] != '<' || buf[len-1] != '>'))))
            tcc_error("'#include' expects \"FILENAME\" or <FILENAME>");
        c = buf[len-1];
        memmove(buf, buf + 1, len - 2);
        buf[len - 2] = '\0';
        }

        if (s1->include_stack_ptr >= s1->include_stack + INCLUDE_STACK_SIZE)
            tcc_error("#include recursion too deep");
        /* store current file in stack, but increment stack later below */
        *s1->include_stack_ptr = file;
        i = tok == TOK_INCLUDE_NEXT ? file->include_next_index : 0;
        n = 2 + s1->nb_include_paths + s1->nb_sysinclude_paths;
        for (; i < n; ++i) {
            char buf1[sizeof file->filename];
            CachedInclude *e;
            const char *path;

            if (i == 0) {
                /* check absolute include path */
                if (!IS_ABSPATH(buf))
                    continue;
                buf1[0] = 0;

            } else if (i == 1) {
                /* search in file's dir if "header.h" */
                if (c != '\"')
                    continue;
                /* https://savannah.nongnu.org/bugs/index.php?50847 */
                path = file->filename2;
                pstrncpy(buf1, path, tcc_basename(path) - path);

            } else {
                /* search in all the include paths */
                int j = i - 2, k = j - s1->nb_include_paths;
                path = k < 0 ? s1->include_paths[j] : s1->sysinclude_paths[k];
                pstrcpy(buf1, sizeof(buf1), path);
                pstrcat(buf1, sizeof(buf1), "/");
            }

            pstrcat(buf1, sizeof(buf1), buf);
            e = search_cached_include(s1, buf1, 0);
            if (e && (define_find(e->ifndef_macro) || e->once == pp_once)) {
                /* no need to parse the include because the 'ifndef macro'
                   is defined (or had #pragma once) */
#ifdef INC_DEBUG
                printf("%s: skipping cached %s\n", file->filename, buf1);
#endif
                goto include_done;
            }

            if (tcc_open(s1, buf1) < 0)
                continue;

            file->include_next_index = i + 1;
#ifdef INC_DEBUG
            printf("%s: including %s\n", file->prev->filename, file->filename);
#endif
            /* update target deps */
            dynarray_add(&s1->target_deps, &s1->nb_target_deps,
                    tcc_strdup(buf1));
            /* push current file in stack */
            ++s1->include_stack_ptr;
            /* add include file debug info */
            if (s1->do_debug)
                put_stabs(file->filename, N_BINCL, 0, 0, 0);
            tok_flags |= TOK_FLAG_BOF | TOK_FLAG_BOL;
            ch = file->buf_ptr[0];
            goto the_end;
        }
        tcc_error("include file '%s' not found", buf);
include_done:
        break;
    case TOK_IFNDEF:
        c = 1;
        goto do_ifdef;
    case TOK_IF:
        c = expr_preprocess();
        goto do_if;
    case TOK_IFDEF:
        c = 0;
    do_ifdef:
        next_nomacro();
        if (tok < TOK_IDENT)
            tcc_error("invalid argument for '#if%sdef'", c ? "n" : "");
        if (is_bof) {
            if (c) {
#ifdef INC_DEBUG
                printf("#ifndef %s\n", get_tok_str(tok, NULL));
#endif
                file->ifndef_macro = tok;
            }
        }
        c = (define_find(tok) != 0) ^ c;
    do_if:
        if (s1->ifdef_stack_ptr >= s1->ifdef_stack + IFDEF_STACK_SIZE)
            tcc_error("memory full (ifdef)");
        *s1->ifdef_stack_ptr++ = c;
        goto test_skip;
    case TOK_ELSE:
        if (s1->ifdef_stack_ptr == s1->ifdef_stack)
            tcc_error("#else without matching #if");
        if (s1->ifdef_stack_ptr[-1] & 2)
            tcc_error("#else after #else");
        c = (s1->ifdef_stack_ptr[-1] ^= 3);
        goto test_else;
    case TOK_ELIF:
        if (s1->ifdef_stack_ptr == s1->ifdef_stack)
            tcc_error("#elif without matching #if");
        c = s1->ifdef_stack_ptr[-1];
        if (c > 1)
            tcc_error("#elif after #else");
        /* last #if/#elif expression was true: we skip */
        if (c == 1) {
            c = 0;
        } else {
            c = expr_preprocess();
            s1->ifdef_stack_ptr[-1] = c;
        }
    test_else:
        if (s1->ifdef_stack_ptr == file->ifdef_stack_ptr + 1)
            file->ifndef_macro = 0;
    test_skip:
        if (!(c & 1)) {
            preprocess_skip();
            is_bof = 0;
            goto redo;
        }
        break;
    case TOK_ENDIF:
        if (s1->ifdef_stack_ptr <= file->ifdef_stack_ptr)
            tcc_error("#endif without matching #if");
        s1->ifdef_stack_ptr--;
        /* '#ifndef macro' was at the start of file. Now we check if
           an '#endif' is exactly at the end of file */
        if (file->ifndef_macro &&
            s1->ifdef_stack_ptr == file->ifdef_stack_ptr) {
            file->ifndef_macro_saved = file->ifndef_macro;
            /* need to set to zero to avoid false matches if another
               #ifndef at middle of file */
            file->ifndef_macro = 0;
            while (tok != TOK_LINEFEED)
                next_nomacro();
            tok_flags |= TOK_FLAG_ENDIF;
            goto the_end;
        }
        break;
    case TOK_PPNUM:
        n = strtoul((char*)tokc.str.data, &q, 10);
        goto _line_num;
    case TOK_LINE:
        next();
        if (tok != TOK_CINT)
    _line_err:
            tcc_error("wrong #line format");
        n = tokc.i;
    _line_num:
        next();
        if (tok != TOK_LINEFEED) {
            if (tok == TOK_STR)
                pstrcpy(file->filename, sizeof(file->filename), (char *)tokc.str.data);
            else if (parse_flags & PARSE_FLAG_ASM_FILE)
                break;
            else
                goto _line_err;
            --n;
        }
        if (file->fd > 0)
            total_lines += file->line_num - n;
        file->line_num = n;
        if (s1->do_debug)
            put_stabs(file->filename, N_BINCL, 0, 0, 0);
        break;
    case TOK_ERROR:
    case TOK_WARNING:
        c = tok;
        ch = file->buf_ptr[0];
        skip_spaces();
        q = buf;
        while (ch != '\n' && ch != CH_EOF) {
            if ((q - buf) < sizeof(buf) - 1)
                *q++ = ch;
            if (ch == '\\') {
                if (handle_stray_noerror() == 0)
                    --q;
            } else
                inp();
        }
        *q = '\0';
        if (c == TOK_ERROR)
            tcc_error("#error %s", buf);
        else
            tcc_warning("#warning %s", buf);
        break;
    case TOK_PRAGMA:
        pragma_parse(s1);
        break;
    case TOK_LINEFEED:
        goto the_end;
    default:
        /* ignore gas line comment in an 'S' file. */
        if (saved_parse_flags & PARSE_FLAG_ASM_FILE)
            goto ignore;
        if (tok == '!' && is_bof)
            /* '!' is ignored at beginning to allow C scripts. */
            goto ignore;
        tcc_warning("Ignoring unknown preprocessing directive #%s", get_tok_str(tok, &tokc));
    ignore:
        file->buf_ptr = parse_line_comment(file->buf_ptr - 1);
        goto the_end;
    }
    /* ignore other preprocess commands or #! for C scripts */
    while (tok != TOK_LINEFEED)
        next_nomacro();
 the_end:
    parse_flags = saved_parse_flags;
}

/* evaluate escape codes in a string. */
static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long)
{
    int c, n;
    const uint8_t *p;

    p = buf;
    for(;;) {
        c = *p;
        if (c == '\0')
            break;
        if (c == '\\') {
            p++;
            /* escape */
            c = *p;
            switch(c) {
            case '0': case '1': case '2': case '3':
            case '4': case '5': case '6': case '7':
                /* at most three octal digits */
                n = c - '0';
                p++;
                c = *p;
                if (isoct(c)) {
                    n = n * 8 + c - '0';
                    p++;
                    c = *p;
                    if (isoct(c)) {
                        n = n * 8 + c - '0';
                        p++;
                    }
                }
                c = n;
                goto add_char_nonext;
            case 'x':
            case 'u':
            case 'U':
                p++;
                n = 0;
                for(;;) {
                    c = *p;
                    if (c >= 'a' && c <= 'f')
                        c = c - 'a' + 10;
                    else if (c >= 'A' && c <= 'F')
                        c = c - 'A' + 10;
                    else if (isnum(c))
                        c = c - '0';
                    else
                        break;
                    n = n * 16 + c;
                    p++;
                }
                c = n;
                goto add_char_nonext;
            case 'a':
                c = '\a';
                break;
            case 'b':
                c = '\b';
                break;
            case 'f':
                c = '\f';
                break;
            case 'n':
                c = '\n';
                break;
            case 'r':
                c = '\r';
                break;
            case 't':
                c = '\t';
                break;
            case 'v':
                c = '\v';
                break;
            case 'e':
                if (!gnu_ext)
                    goto invalid_escape;
                c = 27;
                break;
            case '\'':
            case '\"':
            case '\\': 
            case '?':
                break;
            default:
            invalid_escape:
                if (c >= '!' && c <= '~')
                    tcc_warning("unknown escape sequence: \'\\%c\'", c);
                else
                    tcc_warning("unknown escape sequence: \'\\x%x\'", c);
                break;
            }
        }
        p++;
    add_char_nonext:
        if (!is_long)
            cstr_ccat(outstr, c);
        else
            cstr_wccat(outstr, c);
    }
    /* add a trailing '\0' */
    if (!is_long)
        cstr_ccat(outstr, '\0');
    else
        cstr_wccat(outstr, '\0');
}

static void parse_string(const char *s, int len)
{
    uint8_t buf[1000], *p = buf;
    int is_long, sep;

    if ((is_long = *s == 'L'))
        ++s, --len;
    sep = *s++;
    len -= 2;
    if (len >= sizeof buf)
        p = tcc_malloc(len + 1);
    memcpy(p, s, len);
    p[len] = 0;

    cstr_reset(&tokcstr);
    parse_escape_string(&tokcstr, p, is_long);
    if (p != buf)
        tcc_free(p);

    if (sep == '\'') {
        int char_size;
        /* XXX: make it portable */
        if (!is_long)
            char_size = 1;
        else
            char_size = sizeof(nwchar_t);
        if (tokcstr.size <= char_size)
            tcc_error("empty character constant");
        if (tokcstr.size > 2 * char_size)
            tcc_warning("multi-character character constant");
        if (!is_long) {
            tokc.i = *(int8_t *)tokcstr.data;
            tok = TOK_CCHAR;
        } else {
            tokc.i = *(nwchar_t *)tokcstr.data;
            tok = TOK_LCHAR;
        }
    } else {
        tokc.str.size = tokcstr.size;
        tokc.str.data = tokcstr.data;
        if (!is_long)
            tok = TOK_STR;
        else
            tok = TOK_LSTR;
    }
}

/* we use 64 bit numbers */
#define BN_SIZE 2

/* bn = (bn << shift) | or_val */
static void bn_lshift(unsigned int *bn, int shift, int or_val)
{
    int i;
    unsigned int v;
    for(i=0;i<BN_SIZE;i++) {
        v = bn[i];
        bn[i] = (v << shift) | or_val;
        or_val = v >> (32 - shift);
    }
}

static void bn_zero(unsigned int *bn)
{
    int i;
    for(i=0;i<BN_SIZE;i++) {
        bn[i] = 0;
    }
}

/* parse number in null terminated string 'p' and return it in the
   current token */
static void parse_number(const char *p)
{
    int b, t, shift, frac_bits, s, exp_val, ch;
    char *q;
    unsigned int bn[BN_SIZE];
    double d;

    /* number */
    q = token_buf;
    ch = *p++;
    t = ch;
    ch = *p++;
    *q++ = t;
    b = 10;
    if (t == '.') {
        goto float_frac_parse;
    } else if (t == '0') {
        if (ch == 'x' || ch == 'X') {
            q--;
            ch = *p++;
            b = 16;
        } else if (tcc_ext && (ch == 'b' || ch == 'B')) {
            q--;
            ch = *p++;
            b = 2;
        }
    }
    /* parse all digits. cannot check octal numbers at this stage
       because of floating point constants */
    while (1) {
        if (ch >= 'a' && ch <= 'f')
            t = ch - 'a' + 10;
        else if (ch >= 'A' && ch <= 'F')
            t = ch - 'A' + 10;
        else if (isnum(ch))
            t = ch - '0';
        else
            break;
        if (t >= b)
            break;
        if (q >= token_buf + STRING_MAX_SIZE) {
        num_too_long:
            tcc_error("number too long");
        }
        *q++ = ch;
        ch = *p++;
    }
    if (ch == '.' ||
        ((ch == 'e' || ch == 'E') && b == 10) ||
        ((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) {
        if (b != 10) {
            /* NOTE: strtox should support that for hexa numbers, but
               non ISOC99 libcs do not support it, so we prefer to do
               it by hand */
            /* hexadecimal or binary floats */
            /* XXX: handle overflows */
            *q = '\0';
            if (b == 16)
                shift = 4;
            else 
                shift = 1;
            bn_zero(bn);
            q = token_buf;
            while (1) {
                t = *q++;
                if (t == '\0') {
                    break;
                } else if (t >= 'a') {
                    t = t - 'a' + 10;
                } else if (t >= 'A') {
                    t = t - 'A' + 10;
                } else {
                    t = t - '0';
                }
                bn_lshift(bn, shift, t);
            }
            frac_bits = 0;
            if (ch == '.') {
                ch = *p++;
                while (1) {
                    t = ch;
                    if (t >= 'a' && t <= 'f') {
                        t = t - 'a' + 10;
                    } else if (t >= 'A' && t <= 'F') {
                        t = t - 'A' + 10;
                    } else if (t >= '0' && t <= '9') {
                        t = t - '0';
                    } else {
                        break;
                    }
                    if (t >= b)
                        tcc_error("invalid digit");
                    bn_lshift(bn, shift, t);
                    frac_bits += shift;
                    ch = *p++;
                }
            }
            if (ch != 'p' && ch != 'P')
                expect("exponent");
            ch = *p++;
            s = 1;
            exp_val = 0;
            if (ch == '+') {
                ch = *p++;
            } else if (ch == '-') {
                s = -1;
                ch = *p++;
            }
            if (ch < '0' || ch > '9')
                expect("exponent digits");
            while (ch >= '0' && ch <= '9') {
                exp_val = exp_val * 10 + ch - '0';
                ch = *p++;
            }
            exp_val = exp_val * s;
            
            /* now we can generate the number */
            /* XXX: should patch directly float number */
#if HAVE_FLOAT
            d = (double)bn[1] * 4294967296.0 + (double)bn[0];
#endif
            d = ldexp(d, exp_val - frac_bits);
            t = toup(ch);
            if (t == 'F') {
                ch = *p++;
                tok = TOK_CFLOAT;
                /* float : should handle overflow */
                tokc.f = (float)d;
            } else if (t == 'L') {
                ch = *p++;
#ifdef TCC_TARGET_PE
                tok = TOK_CDOUBLE;
                tokc.d = d;
#else
                tok = TOK_CLDOUBLE;
                /* XXX: not large enough */
                tokc.ld = (long double)d;
#endif
            } else {
                tok = TOK_CDOUBLE;
                tokc.d = d;
            }
        } else {
            /* decimal floats */
            if (ch == '.') {
                if (q >= token_buf + STRING_MAX_SIZE)
                    goto num_too_long;
                *q++ = ch;
                ch = *p++;
            float_frac_parse:
                while (ch >= '0' && ch <= '9') {
                    if (q >= token_buf + STRING_MAX_SIZE)
                        goto num_too_long;
                    *q++ = ch;
                    ch = *p++;
                }
            }
            if (ch == 'e' || ch == 'E') {
                if (q >= token_buf + STRING_MAX_SIZE)
                    goto num_too_long;
                *q++ = ch;
                ch = *p++;
                if (ch == '-' || ch == '+') {
                    if (q >= token_buf + STRING_MAX_SIZE)
                        goto num_too_long;
                    *q++ = ch;
                    ch = *p++;
                }
                if (ch < '0' || ch > '9')
                    expect("exponent digits");
                while (ch >= '0' && ch <= '9') {
                    if (q >= token_buf + STRING_MAX_SIZE)
                        goto num_too_long;
                    *q++ = ch;
                    ch = *p++;
                }
            }
            *q = '\0';
            t = toup(ch);
            errno = 0;
            if (t == 'F') {
                ch = *p++;
                tok = TOK_CFLOAT;
                tokc.f = strtof(token_buf, NULL);
            } else if (t == 'L') {
                ch = *p++;
#ifdef TCC_TARGET_PE
                tok = TOK_CDOUBLE;
                tokc.d = strtod(token_buf, NULL);
#else
                tok = TOK_CLDOUBLE;
#if HAVE_FLOAT
                tokc.ld = strtold(token_buf, NULL);
#endif
#endif
            } else {
                tok = TOK_CDOUBLE;
#if HAVE_FLOAT
                tokc.d = strtod(token_buf, NULL);
#endif
            }
        }
    } else {
        unsigned long long n, n1;
        int lcount, ucount, must_64bit;
        const char *p1;

        /* integer number */
        *q = '\0';
        q = token_buf;
        if (b == 10 && *q == '0') {
            b = 8;
            q++;
        }
        n = 0;
        while(1) {
            t = *q++;
            /* no need for checks except for base 10 / 8 errors */
            if (t == '\0')
                break;
            else if (t >= 'a')
                t = t - 'a' + 10;
            else if (t >= 'A')
                t = t - 'A' + 10;
            else
                t = t - '0';
            if (t >= b)
                tcc_error("invalid digit");
            n1 = n;
            n = n * b + t;
#if HAVE_LONG_LONG
            /* detect overflow */
            /* XXX: this test is not reliable */
            if (n < n1)
                tcc_error("integer constant overflow");
#endif
        }

        /* Determine the characteristics (unsigned and/or 64bit) the type of
           the constant must have according to the constant suffix(es) */
        lcount = ucount = must_64bit = 0;
        p1 = p;
        for(;;) {
            t = toup(ch);
            if (t == 'L') {
                if (lcount >= 2)
                    tcc_error("three 'l's in integer constant");
                if (lcount && *(p - 1) != ch)
                    tcc_error("incorrect integer suffix: %s", p1);
                lcount++;
#if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE
                if (lcount == 2)
#endif
                    must_64bit = 1;
                ch = *p++;
            } else if (t == 'U') {
                if (ucount >= 1)
                    tcc_error("two 'u's in integer constant");
                ucount++;
                ch = *p++;
            } else {
                break;
            }
        }

#if HAVE_LONG_LONG
        /* Whether 64 bits are needed to hold the constant's value */
        if (n & 0xffffffff00000000LL || must_64bit) {
            tok = TOK_CLLONG;
            n1 = n >> 32;
    } else
#endif
        {
            tok = TOK_CINT;
            n1 = n;
        }

        /* Whether type must be unsigned to hold the constant's value */
        if (ucount || ((n1 >> 31) && (b != 10))) {
            if (tok == TOK_CLLONG)
                tok = TOK_CULLONG;
            else
                tok = TOK_CUINT;
        /* If decimal and no unsigned suffix, bump to 64 bits or throw error */
        } else if (n1 >> 31) {
            if (tok == TOK_CINT)
                tok = TOK_CLLONG;
            else
                tcc_error("integer constant overflow");
        }

        tokc.i = n;
    }
    if (ch)
        tcc_error("invalid number\n");
}


#define PARSE2(c1, tok1, c2, tok2)              \
    case c1:                                    \
        PEEKC(c, p);                            \
        if (c == c2) {                          \
            p++;                                \
            tok = tok2;                         \
        } else {                                \
            tok = tok1;                         \
        }                                       \
        break;

/* return next token without macro substitution */
static inline void next_nomacro1(void)
{
    int t, c, is_long, len;
    TokenSym *ts;
    uint8_t *p, *p1;
    unsigned int h;

    p = file->buf_ptr;
 redo_no_start:
    c = *p;
    switch(c) {
    case ' ':
    case '\t':
        tok = c;
        p++;
        if (parse_flags & PARSE_FLAG_SPACES)
            goto keep_tok_flags;
        while (isidnum_table[*p - CH_EOF] & IS_SPC)
            ++p;
        goto redo_no_start;
    case '\f':
    case '\v':
    case '\r':
        p++;
        goto redo_no_start;
    case '\\':
        /* first look if it is in fact an end of buffer */
        c = handle_stray1(p);
        p = file->buf_ptr;
        if (c == '\\')
            goto parse_simple;
        if (c != CH_EOF)
            goto redo_no_start;
        {
            TCCState *s1 = tcc_state;
            if ((parse_flags & PARSE_FLAG_LINEFEED)
                && !(tok_flags & TOK_FLAG_EOF)) {
                tok_flags |= TOK_FLAG_EOF;
                tok = TOK_LINEFEED;
                goto keep_tok_flags;
            } else if (!(parse_flags & PARSE_FLAG_PREPROCESS)) {
                tok = TOK_EOF;
            } else if (s1->ifdef_stack_ptr != file->ifdef_stack_ptr) {
                tcc_error("missing #endif");
            } else if (s1->include_stack_ptr == s1->include_stack) {
                /* no include left : end of file. */
                tok = TOK_EOF;
            } else {
                tok_flags &= ~TOK_FLAG_EOF;
                /* pop include file */
                
                /* test if previous '#endif' was after a #ifdef at
                   start of file */
                if (tok_flags & TOK_FLAG_ENDIF) {
#ifdef INC_DEBUG
                    printf("#endif %s\n", get_tok_str(file->ifndef_macro_saved, NULL));
#endif
                    search_cached_include(s1, file->filename, 1)
                        ->ifndef_macro = file->ifndef_macro_saved;
                    tok_flags &= ~TOK_FLAG_ENDIF;
                }

                /* add end of include file debug info */
                if (tcc_state->do_debug) {
                    put_stabd(N_EINCL, 0, 0);
                }
                /* pop include stack */
                tcc_close();
                s1->include_stack_ptr--;
                p = file->buf_ptr;
                goto redo_no_start;
            }
        }
        break;

    case '\n':
        file->line_num++;
        tok_flags |= TOK_FLAG_BOL;
        p++;
maybe_newline:
        if (0 == (parse_flags & PARSE_FLAG_LINEFEED))
            goto redo_no_start;
        tok = TOK_LINEFEED;
        goto keep_tok_flags;

    case '#':
        /* XXX: simplify */
        PEEKC(c, p);
        if ((tok_flags & TOK_FLAG_BOL) && 
            (parse_flags & PARSE_FLAG_PREPROCESS)) {
            file->buf_ptr = p;
            preprocess(tok_flags & TOK_FLAG_BOF);
            p = file->buf_ptr;
            goto maybe_newline;
        } else {
            if (c == '#') {
                p++;
                tok = TOK_TWOSHARPS;
            } else {
                if (parse_flags & PARSE_FLAG_ASM_FILE) {
                    p = parse_line_comment(p - 1);
                    goto redo_no_start;
                } else {
                    tok = '#';
                }
            }
        }
        break;
    
    /* dollar is allowed to start identifiers when not parsing asm */
    case '$':
        if (!(isidnum_table[c - CH_EOF] & IS_ID)
         || (parse_flags & PARSE_FLAG_ASM_FILE))
            goto parse_simple;

    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 'A': case 'B': case 'C': case 'D':
    case 'E': case 'F': case 'G': case 'H':
    case 'I': case 'J': case 'K': 
    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 '_':
    parse_ident_fast:
        p1 = p;
        h = TOK_HASH_INIT;
        h = TOK_HASH_FUNC(h, c);
        while (c = *++p, isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
            h = TOK_HASH_FUNC(h, c);
        len = p - p1;
        if (c != '\\') {
            TokenSym **pts;

            /* fast case : no stray found, so we have the full token
               and we have already hashed it */
            h &= (TOK_HASH_SIZE - 1);
            pts = &hash_ident[h];
            for(;;) {
                ts = *pts;
                if (!ts)
                    break;
                if (ts->len == len && !memcmp(ts->str, p1, len))
                    goto token_found;
                pts = &(ts->hash_next);
            }
            ts = tok_alloc_new(pts, (char *) p1, len);
        token_found: ;
        } else {
            /* slower case */
            cstr_reset(&tokcstr);
            cstr_cat(&tokcstr, (char *) p1, len);
            p--;
            PEEKC(c, p);
        parse_ident_slow:
            while (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
            {
                cstr_ccat(&tokcstr, c);
                PEEKC(c, p);
            }
            ts = tok_alloc(tokcstr.data, tokcstr.size);
        }
        tok = ts->tok;
        break;
    case 'L':
        t = p[1];
        if (t != '\\' && t != '\'' && t != '\"') {
            /* fast case */
            goto parse_ident_fast;
        } else {
            PEEKC(c, p);
            if (c == '\'' || c == '\"') {
                is_long = 1;
                goto str_const;
            } else {
                cstr_reset(&tokcstr);
                cstr_ccat(&tokcstr, 'L');
                goto parse_ident_slow;
            }
        }
        break;

    case '0': case '1': case '2': case '3':
    case '4': case '5': case '6': case '7':
    case '8': case '9':
        t = c;
        PEEKC(c, p);
        /* after the first digit, accept digits, alpha, '.' or sign if
           prefixed by 'eEpP' */
    parse_num:
        cstr_reset(&tokcstr);
        for(;;) {
            cstr_ccat(&tokcstr, t);
            if (!((isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
                  || c == '.'
                  || ((c == '+' || c == '-')
                      && (((t == 'e' || t == 'E')
                            && !(parse_flags & PARSE_FLAG_ASM_FILE
                                /* 0xe+1 is 3 tokens in asm */
                                && ((char*)tokcstr.data)[0] == '0'
                                && toup(((char*)tokcstr.data)[1]) == 'X'))
                          || t == 'p' || t == 'P'))))
                break;
            t = c;
            PEEKC(c, p);
        }
        /* We add a trailing '\0' to ease parsing */
        cstr_ccat(&tokcstr, '\0');
        tokc.str.size = tokcstr.size;
        tokc.str.data = tokcstr.data;
        tok = TOK_PPNUM;
        break;

    case '.':
        /* special dot handling because it can also start a number */
        PEEKC(c, p);
        if (isnum(c)) {
            t = '.';
            goto parse_num;
        } else if ((isidnum_table['.' - CH_EOF] & IS_ID)
                   && (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))) {
            *--p = c = '.';
            goto parse_ident_fast;
        } else if (c == '.') {
            PEEKC(c, p);
            if (c == '.') {
                p++;
                tok = TOK_DOTS;
            } else {
                *--p = '.'; /* may underflow into file->unget[] */
                tok = '.';
            }
        } else {
            tok = '.';
        }
        break;
    case '\'':
    case '\"':
        is_long = 0;
    str_const:
        cstr_reset(&tokcstr);
        if (is_long)
            cstr_ccat(&tokcstr, 'L');
        cstr_ccat(&tokcstr, c);
        p = parse_pp_string(p, c, &tokcstr);
        cstr_ccat(&tokcstr, c);
        cstr_ccat(&tokcstr, '\0');
        tokc.str.size = tokcstr.size;
        tokc.str.data = tokcstr.data;
        tok = TOK_PPSTR;
        break;

    case '<':
        PEEKC(c, p);
        if (c == '=') {
            p++;
            tok = TOK_LE;
        } else if (c == '<') {
            PEEKC(c, p);
            if (c == '=') {
                p++;
                tok = TOK_A_SHL;
            } else {
                tok = TOK_SHL;
            }
        } else {
            tok = TOK_LT;
        }
        break;
    case '>':
        PEEKC(c, p);
        if (c == '=') {
            p++;
            tok = TOK_GE;
        } else if (c == '>') {
            PEEKC(c, p);
            if (c == '=') {
                p++;
                tok = TOK_A_SAR;
            } else {
                tok = TOK_SAR;
            }
        } else {
            tok = TOK_GT;
        }
        break;
        
    case '&':
        PEEKC(c, p);
        if (c == '&') {
            p++;
            tok = TOK_LAND;
        } else if (c == '=') {
            p++;
            tok = TOK_A_AND;
        } else {
            tok = '&';
        }
        break;
        
    case '|':
        PEEKC(c, p);
        if (c == '|') {
            p++;
            tok = TOK_LOR;
        } else if (c == '=') {
            p++;
            tok = TOK_A_OR;
        } else {
            tok = '|';
        }
        break;

    case '+':
        PEEKC(c, p);
        if (c == '+') {
            p++;
            tok = TOK_INC;
        } else if (c == '=') {
            p++;
            tok = TOK_A_ADD;
        } else {
            tok = '+';
        }
        break;
        
    case '-':
        PEEKC(c, p);
        if (c == '-') {
            p++;
            tok = TOK_DEC;
        } else if (c == '=') {
            p++;
            tok = TOK_A_SUB;
        } else if (c == '>') {
            p++;
            tok = TOK_ARROW;
        } else {
            tok = '-';
        }
        break;

    PARSE2('!', '!', '=', TOK_NE)
    PARSE2('=', '=', '=', TOK_EQ)
    PARSE2('*', '*', '=', TOK_A_MUL)
    PARSE2('%', '%', '=', TOK_A_MOD)
    PARSE2('^', '^', '=', TOK_A_XOR)
        
        /* comments or operator */
    case '/':
        PEEKC(c, p);
        if (c == '*') {
            p = parse_comment(p);
            /* comments replaced by a blank */
            tok = ' ';
            goto keep_tok_flags;
        } else if (c == '/') {
            p = parse_line_comment(p);
            tok = ' ';
            goto keep_tok_flags;
        } else if (c == '=') {
            p++;
            tok = TOK_A_DIV;
        } else {
            tok = '/';
        }
        break;
        
        /* simple tokens */
    case '(':
    case ')':
    case '[':
    case ']':
    case '{':
    case '}':
    case ',':
    case ';':
    case ':':
    case '?':
    case '~':
    case '@': /* only used in assembler */
    parse_simple:
        tok = c;
        p++;
        break;
    default:
        if (c >= 0x80 && c <= 0xFF) /* utf8 identifiers */
        goto parse_ident_fast;
        if (parse_flags & PARSE_FLAG_ASM_FILE)
            goto parse_simple;
        tcc_error("unrecognized character \\x%02x", c);
        break;
    }
    tok_flags = 0;
keep_tok_flags:
    file->buf_ptr = p;
#if defined(PARSE_DEBUG)
    printf("token = %s\n", get_tok_str(tok, &tokc));
#endif
}

/* return next token without macro substitution. Can read input from
   macro_ptr buffer */
static void next_nomacro_spc(void)
{
    if (macro_ptr) {
    redo:
        tok = *macro_ptr;
        if (tok) {
            TOK_GET(&tok, &macro_ptr, &tokc);
            if (tok == TOK_LINENUM) {
                file->line_num = tokc.i;
                goto redo;
            }
        }
    } else {
        next_nomacro1();
    }
}

ST_FUNC void next_nomacro(void)
{
    do {
        next_nomacro_spc();
    } while (tok < 256 && (isidnum_table[tok - CH_EOF] & IS_SPC));
}
 

static void macro_subst(
    TokenString *tok_str,
    Sym **nested_list,
    const int *macro_str,
    int can_read_stream
    );

/* substitute arguments in replacement lists in macro_str by the values in
   args (field d) and return allocated string */
static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args)
{
    int t, t0, t1, spc;
    const int *st;
    Sym *s;
    CValue cval;
    TokenString str;
    CString cstr;

    tok_str_new(&str);
    t0 = t1 = 0;
    while(1) {
        TOK_GET(&t, &macro_str, &cval);
        if (!t)
            break;
        if (t == '#') {
            /* stringize */
            TOK_GET(&t, &macro_str, &cval);
            if (!t)
                goto bad_stringy;
            s = sym_find2(args, t);
            if (s) {
                cstr_new(&cstr);
                cstr_ccat(&cstr, '\"');
                st = s->d;
                spc = 0;
                while (*st) {
                    TOK_GET(&t, &st, &cval);
                    if (t != TOK_PLCHLDR
                     && t != TOK_NOSUBST
                     && 0 == check_space(t, &spc)) {
                        const char *s = get_tok_str(t, &cval);
                        while (*s) {
                            if (t == TOK_PPSTR && *s != '\'')
                                add_char(&cstr, *s);
                            else
                                cstr_ccat(&cstr, *s);
                            ++s;
                        }
                    }
                }
                cstr.size -= spc;
                cstr_ccat(&cstr, '\"');
                cstr_ccat(&cstr, '\0');
#ifdef PP_DEBUG
                printf("\nstringize: <%s>\n", (char *)cstr.data);
#endif
                /* add string */
                cval.str.size = cstr.size;
                cval.str.data = cstr.data;
                tok_str_add2(&str, TOK_PPSTR, &cval);
                cstr_free(&cstr);
            } else {
        bad_stringy:
                expect("macro parameter after '#'");
            }
        } else if (t >= TOK_IDENT) {
            s = sym_find2(args, t);
            if (s) {
                int l0 = str.len;
                st = s->d;
                /* if '##' is present before or after, no arg substitution */
                if (*macro_str == TOK_PPJOIN || t1 == TOK_PPJOIN) {
                    /* special case for var arg macros : ## eats the ','
                       if empty VA_ARGS variable. */
                    if (t1 == TOK_PPJOIN && t0 == ',' && gnu_ext && s->type.t) {
                        if (*st == 0) {
                            /* suppress ',' '##' */
                            str.len -= 2;
                        } else {
                            /* suppress '##' and add variable */
                            str.len--;
                            goto add_var;
                        }
                    } else {
                        for(;;) {
                            int t1;
                            TOK_GET(&t1, &st, &cval);
                            if (!t1)
                                break;
                            tok_str_add2(&str, t1, &cval);
                        }
                    }

                } else {
            add_var:
                    /* NOTE: the stream cannot be read when macro
                       substituting an argument */
                    macro_subst(&str, nested_list, st, 0);
                }
                if (str.len == l0) /* expanded to empty string */
                    tok_str_add(&str, TOK_PLCHLDR);
            } else {
                tok_str_add(&str, t);
            }
        } else {
            tok_str_add2(&str, t, &cval);
        }
        t0 = t1, t1 = t;
    }
    tok_str_add(&str, 0);
    return str.str;
}

static char const ab_month_name[12][4] =
{
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

/* peek or read [ws_str == NULL] next token from function macro call,
   walking up macro levels up to the file if necessary */
static int next_argstream(Sym **nested_list, int can_read_stream, TokenString *ws_str)
{
    int t;
    const int *p;
    Sym *sa;

    for (;;) {
        if (macro_ptr) {
            p = macro_ptr, t = *p;
            if (ws_str) {
                while (is_space(t) || TOK_LINEFEED == t || TOK_PLCHLDR == t)
                    tok_str_add(ws_str, t), t = *++p;
            }
            if (t == 0 && can_read_stream) {
                end_macro();
                /* also, end of scope for nested defined symbol */
                sa = *nested_list;
                while (sa && sa->v == 0)
                    sa = sa->prev;
                if (sa)
                    sa->v = 0;
                continue;
            }
        } else {
            ch = handle_eob();
            if (ws_str) {
                while (is_space(ch) || ch == '\n' || ch == '/') {
                    if (ch == '/') {
                        int c;
                        uint8_t *p = file->buf_ptr;
                        PEEKC(c, p);
                        if (c == '*') {
                            p = parse_comment(p);
                            file->buf_ptr = p - 1;
                        } else if (c == '/') {
                            p = parse_line_comment(p);
                            file->buf_ptr = p - 1;
                        } else
                            break;
                        ch = ' ';
                    }
                    if (!(ch == '\f' || ch == '\v' || ch == '\r'))
                        tok_str_add(ws_str, ch);
                    cinp();
                }
            }
            t = ch;
        }

        if (ws_str)
            return t;
        next_nomacro_spc();
        return tok;
    }
}

/* do macro substitution of current token with macro 's' and add
   result to (tok_str,tok_len). 'nested_list' is the list of all
   macros we got inside to avoid recursing. Return non zero if no
   substitution needs to be done */
static int macro_subst_tok(
    TokenString *tok_str,
    Sym **nested_list,
    Sym *s,
    int can_read_stream)
{
    Sym *args, *sa, *sa1;
    int parlevel, *mstr, t, t1, spc;
    TokenString str;
    char *cstrval;
    CValue cval;
    CString cstr;
    char buf[32];
    
    /* if symbol is a macro, prepare substitution */
    /* special macros */
    if (tok == TOK___LINE__) {
        snprintf(buf, sizeof(buf), "%d", file->line_num);
        cstrval = buf;
        t1 = TOK_PPNUM;
        goto add_cstr1;
    } else if (tok == TOK___FILE__) {
        cstrval = file->filename;
        goto add_cstr;
    } else if (tok == TOK___DATE__ || tok == TOK___TIME__) {
        time_t ti;
        struct tm *tm;

        time(&ti);
        tm = localtime(&ti);
        if (tok == TOK___DATE__) {
            snprintf(buf, sizeof(buf), "%s %2d %d", 
                     ab_month_name[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900);
        } else {
            snprintf(buf, sizeof(buf), "%02d:%02d:%02d", 
                     tm->tm_hour, tm->tm_min, tm->tm_sec);
        }
        cstrval = buf;
    add_cstr:
        t1 = TOK_STR;
    add_cstr1:
        cstr_new(&cstr);
        cstr_cat(&cstr, cstrval, 0);
        cval.str.size = cstr.size;
        cval.str.data = cstr.data;
        tok_str_add2(tok_str, t1, &cval);
        cstr_free(&cstr);
    } else {
        int saved_parse_flags = parse_flags;

        mstr = s->d;
        if (s->type.t == MACRO_FUNC) {
            /* whitespace between macro name and argument list */
            TokenString ws_str;
            tok_str_new(&ws_str);

            spc = 0;
            parse_flags |= PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED
                | PARSE_FLAG_ACCEPT_STRAYS;

            /* get next token from argument stream */
            t = next_argstream(nested_list, can_read_stream, &ws_str);
            if (t != '(') {
                /* not a macro substitution after all, restore the
                 * macro token plus all whitespace we've read.
                 * whitespace is intentionally not merged to preserve
                 * newlines. */
                parse_flags = saved_parse_flags;
                tok_str_add(tok_str, tok);
                if (parse_flags & PARSE_FLAG_SPACES) {
                    int i;
                    for (i = 0; i < ws_str.len; i++)
                        tok_str_add(tok_str, ws_str.str[i]);
                }
                tok_str_free_str(ws_str.str);
                return 0;
            } else {
                tok_str_free_str(ws_str.str);
            }
        do {
        next_nomacro(); /* eat '(' */
        } while (tok == TOK_PLCHLDR);

            /* argument macro */
            args = NULL;
            sa = s->next;
            /* NOTE: empty args are allowed, except if no args */
            for(;;) {
                do {
                    next_argstream(nested_list, can_read_stream, NULL);
                } while (is_space(tok) || TOK_LINEFEED == tok);
    empty_arg:
                /* handle '()' case */
                if (!args && !sa && tok == ')')
                    break;
                if (!sa)
                    tcc_error("macro '%s' used with too many args",
                          get_tok_str(s->v, 0));
                tok_str_new(&str);
                parlevel = spc = 0;
                /* NOTE: non zero sa->t indicates VA_ARGS */
                while ((parlevel > 0 || 
                        (tok != ')' && 
                         (tok != ',' || sa->type.t)))) {
                    if (tok == TOK_EOF || tok == 0)
                        break;
                    if (tok == '(')
                        parlevel++;
                    else if (tok == ')')
                        parlevel--;
                    if (tok == TOK_LINEFEED)
                        tok = ' ';
                    if (!check_space(tok, &spc))
                        tok_str_add2(&str, tok, &tokc);
                    next_argstream(nested_list, can_read_stream, NULL);
                }
                if (parlevel)
                    expect(")");
                str.len -= spc;
                tok_str_add(&str, 0);
                sa1 = sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, 0);
                sa1->d = str.str;
                sa = sa->next;
                if (tok == ')') {
                    /* special case for gcc var args: add an empty
                       var arg argument if it is omitted */
                    if (sa && sa->type.t && gnu_ext)
                        goto empty_arg;
                    break;
                }
                if (tok != ',')
                    expect(",");
            }
            if (sa) {
                tcc_error("macro '%s' used with too few args",
                      get_tok_str(s->v, 0));
            }

            parse_flags = saved_parse_flags;

            /* now subst each arg */
            mstr = macro_arg_subst(nested_list, mstr, args);
            /* free memory */
            sa = args;
            while (sa) {
                sa1 = sa->prev;
                tok_str_free_str(sa->d);
                sym_free(sa);
                sa = sa1;
            }
        }

        sym_push2(nested_list, s->v, 0, 0);
        parse_flags = saved_parse_flags;
        macro_subst(tok_str, nested_list, mstr, can_read_stream | 2);

        /* pop nested defined symbol */
        sa1 = *nested_list;
        *nested_list = sa1->prev;
        sym_free(sa1);
        if (mstr != s->d)
            tok_str_free_str(mstr);
    }
    return 0;
}

static int paste_tokens(int t1, CValue *v1, int t2, CValue *v2)
{
    CString cstr;
    int n, ret = 1;

    cstr_new(&cstr);
    if (t1 != TOK_PLCHLDR)
        cstr_cat(&cstr, get_tok_str(t1, v1), -1);
    n = cstr.size;
    if (t2 != TOK_PLCHLDR)
        cstr_cat(&cstr, get_tok_str(t2, v2), -1);
    cstr_ccat(&cstr, '\0');

    tcc_open_bf(tcc_state, ":paste:", cstr.size);
    memcpy(file->buffer, cstr.data, cstr.size);
    for (;;) {
        next_nomacro1();
        if (0 == *file->buf_ptr)
            break;
        if (is_space(tok))
            continue;
        tcc_warning("pasting \"%.*s\" and \"%s\" does not give a valid"
            " preprocessing token", n, cstr.data, (char*)cstr.data + n);
        ret = 0;
        break;
    }
    tcc_close();
    //printf("paste <%s>\n", (char*)cstr.data);
    cstr_free(&cstr);
    return ret;
}

/* handle the '##' operator. Return NULL if no '##' seen. Otherwise
   return the resulting string (which must be freed). */
static inline int *macro_twosharps(const int *ptr0)
{
    int t;
    CValue cval;
    TokenString macro_str1;
    int start_of_nosubsts = -1;
    const int *ptr;

    /* we search the first '##' */
    for (ptr = ptr0;;) {
        TOK_GET(&t, &ptr, &cval);
        if (t == TOK_PPJOIN)
            break;
        if (t == 0)
            return NULL;
    }

    tok_str_new(&macro_str1);

    //tok_print(" $$$", ptr0);
    for (ptr = ptr0;;) {
        TOK_GET(&t, &ptr, &cval);
        if (t == 0)
            break;
        if (t == TOK_PPJOIN)
            continue;
        while (*ptr == TOK_PPJOIN) {
            int t1; CValue cv1;
            /* given 'a##b', remove nosubsts preceding 'a' */
            if (start_of_nosubsts >= 0)
                macro_str1.len = start_of_nosubsts;
            /* given 'a##b', remove nosubsts preceding 'b' */
            while ((t1 = *++ptr) == TOK_NOSUBST)
                ;
            if (t1 && t1 != TOK_PPJOIN) {
                TOK_GET(&t1, &ptr, &cv1);
                if (t != TOK_PLCHLDR || t1 != TOK_PLCHLDR) {
                    if (paste_tokens(t, &cval, t1, &cv1)) {
                        t = tok, cval = tokc;
                    } else {
                        tok_str_add2(&macro_str1, t, &cval);
                        t = t1, cval = cv1;
                    }
                }
            }
        }
        if (t == TOK_NOSUBST) {
            if (start_of_nosubsts < 0)
                start_of_nosubsts = macro_str1.len;
        } else {
            start_of_nosubsts = -1;
        }
        tok_str_add2(&macro_str1, t, &cval);
    }
    tok_str_add(&macro_str1, 0);
    //tok_print(" ###", macro_str1.str);
    return macro_str1.str;
}

/* do macro substitution of macro_str and add result to
   (tok_str,tok_len). 'nested_list' is the list of all macros we got
   inside to avoid recursing. */
static void macro_subst(
    TokenString *tok_str,
    Sym **nested_list,
    const int *macro_str,
    int can_read_stream
    )
{
    Sym *s;
    const int *ptr;
    int t, spc, nosubst;
    CValue cval;
    int *macro_str1 = NULL;
    
    /* first scan for '##' operator handling */
    ptr = macro_str;
    spc = nosubst = 0;

    /* first scan for '##' operator handling */
    if (can_read_stream) {
        macro_str1 = macro_twosharps(ptr);
        if (macro_str1)
            ptr = macro_str1;
    }

    while (1) {
        TOK_GET(&t, &ptr, &cval);
        if (t == 0)
            break;

        if (t >= TOK_IDENT && 0 == nosubst) {
            s = define_find(t);
            if (s == NULL)
                goto no_subst;

            /* if nested substitution, do nothing */
            if (sym_find2(*nested_list, t)) {
                /* and mark it as TOK_NOSUBST, so it doesn't get subst'd again */
                tok_str_add2(tok_str, TOK_NOSUBST, NULL);
                goto no_subst;
            }

            {
                TokenString str;
                str.str = (int*)ptr;
                begin_macro(&str, 2);

                tok = t;
                macro_subst_tok(tok_str, nested_list, s, can_read_stream);

                if (str.alloc == 3) {
                    /* already finished by reading function macro arguments */
                    break;
                }

                ptr = macro_ptr;
                end_macro ();
            }

            spc = (tok_str->len &&
                   is_space(tok_last(tok_str->str,
                                     tok_str->str + tok_str->len)));

        } else {

            if (t == '\\' && !(parse_flags & PARSE_FLAG_ACCEPT_STRAYS))
                tcc_error("stray '\\' in program");

no_subst:
            if (!check_space(t, &spc))
                tok_str_add2(tok_str, t, &cval);
            nosubst = 0;
            if (t == TOK_NOSUBST)
                nosubst = 1;
        }
    }
    if (macro_str1)
        tok_str_free_str(macro_str1);

}

/* return next token with macro substitution */
ST_FUNC void next(void)
{
 redo:
    if (parse_flags & PARSE_FLAG_SPACES)
        next_nomacro_spc();
    else
        next_nomacro();

    if (macro_ptr) {
        if (tok == TOK_NOSUBST || tok == TOK_PLCHLDR) {
        /* discard preprocessor markers */
            goto redo;
        } else if (tok == 0) {
            /* end of macro or unget token string */
            end_macro();
            goto redo;
        }
    } else if (tok >= TOK_IDENT && (parse_flags & PARSE_FLAG_PREPROCESS)) {
        Sym *s;
        /* if reading from file, try to substitute macros */
        s = define_find(tok);
        if (s) {
            Sym *nested_list = NULL;
            tokstr_buf.len = 0;
            macro_subst_tok(&tokstr_buf, &nested_list, s, 1);
            tok_str_add(&tokstr_buf, 0);
            begin_macro(&tokstr_buf, 2);
            goto redo;
        }
    }
    /* convert preprocessor tokens into C tokens */
    if (tok == TOK_PPNUM) {
        if  (parse_flags & PARSE_FLAG_TOK_NUM)
            parse_number((char *)tokc.str.data);
    } else if (tok == TOK_PPSTR) {
        if (parse_flags & PARSE_FLAG_TOK_STR)
            parse_string((char *)tokc.str.data, tokc.str.size - 1);
    }
}

/* push back current token and set current token to 'last_tok'. Only
   identifier case handled for labels. */
ST_INLN void unget_tok(int last_tok)
{

    TokenString *str = tok_str_alloc();
    tok_str_add2(str, tok, &tokc);
    tok_str_add(str, 0);
    begin_macro(str, 1);
    tok = last_tok;
}

ST_FUNC void preprocess_start(TCCState *s1)
{
    char *buf;

    s1->include_stack_ptr = s1->include_stack;
    s1->ifdef_stack_ptr = s1->ifdef_stack;
    file->ifdef_stack_ptr = s1->ifdef_stack_ptr;
    pp_once++;
    pvtop = vtop = vstack - 1;
    s1->pack_stack[0] = 0;
    s1->pack_stack_ptr = s1->pack_stack;

    set_idnum('$', s1->dollars_in_identifiers ? IS_ID : 0);
    set_idnum('.', (parse_flags & PARSE_FLAG_ASM_FILE) ? IS_ID : 0);

    buf = tcc_malloc(3 + strlen(file->filename));
    sprintf(buf, "\"%s\"", file->filename);
    tcc_define_symbol(s1, "__BASE_FILE__", buf);
    tcc_free(buf);

    if (s1->nb_cmd_include_files) {
    CString cstr;
    int i;
    cstr_new(&cstr);
    for (i = 0; i < s1->nb_cmd_include_files; i++) {
        cstr_cat(&cstr, "#include \"", -1);
        cstr_cat(&cstr, s1->cmd_include_files[i], -1);
        cstr_cat(&cstr, "\"\n", -1);
    }
        *s1->include_stack_ptr++ = file;
    tcc_open_bf(s1, "<command line>", cstr.size);
    memcpy(file->buffer, cstr.data, cstr.size);
    cstr_free(&cstr);
    }
}

ST_FUNC void tccpp_new(TCCState *s)
{
    int i, c;
    const char *p, *r;

    /* might be used in error() before preprocess_start() */
    s->include_stack_ptr = s->include_stack;

    /* init isid table */
    for(i = CH_EOF; i<128; i++)
        set_idnum(i,
            is_space(i) ? IS_SPC
            : isid(i) ? IS_ID
            : isnum(i) ? IS_NUM
            : 0);

    for(i = 128; i<256; i++)
        set_idnum(i, IS_ID);

    /* init allocators */
    tal_new(&toksym_alloc, TOKSYM_TAL_LIMIT, TOKSYM_TAL_SIZE);
    tal_new(&tokstr_alloc, TOKSTR_TAL_LIMIT, TOKSTR_TAL_SIZE);
    tal_new(&cstr_alloc, CSTR_TAL_LIMIT, CSTR_TAL_SIZE);

    memset(hash_ident, 0, TOK_HASH_SIZE * sizeof(TokenSym *));
    cstr_new(&cstr_buf);
    cstr_realloc(&cstr_buf, STRING_MAX_SIZE);
    tok_str_new(&tokstr_buf);
    tok_str_realloc(&tokstr_buf, TOKSTR_MAX_SIZE);
    
    tok_ident = TOK_IDENT;
    p = tcc_keywords;
    while (*p) {
        r = p;
        for(;;) {
            c = *r++;
            if (c == '\0')
                break;
        }
        tok_alloc(p, r - p - 1);
        p = r;
    }
}

ST_FUNC void tccpp_delete(TCCState *s)
{
    int i, n;

    /* free -D and compiler defines */
    free_defines(NULL);

    /* cleanup from error/setjmp */
    while (macro_stack)
        end_macro();
    macro_ptr = NULL;

    while (file)
        tcc_close();

    /* free tokens */
    n = tok_ident - TOK_IDENT;
    for(i = 0; i < n; i++)
        tal_free(toksym_alloc, table_ident[i]);
    tcc_free(table_ident);
    table_ident = NULL;

    /* free static buffers */
    cstr_free(&tokcstr);
    cstr_free(&cstr_buf);
    cstr_free(&macro_equal_buf);
    tok_str_free_str(tokstr_buf.str);

    /* free allocators */
    tal_delete(toksym_alloc);
    toksym_alloc = NULL;
    tal_delete(tokstr_alloc);
    tokstr_alloc = NULL;
    tal_delete(cstr_alloc);
    cstr_alloc = NULL;
}

/* ------------------------------------------------------------------------- */
/* tcc -E [-P[1]] [-dD} support */

static void tok_print(const char *msg, const int *str)
{
    FILE *fp;
    int t;
    CValue cval;

    fp = tcc_state->ppfp;
    if (!fp || !tcc_state->dflag)
        fp = stdout;

    fprintf(fp, "%s ", msg);
    while (str) {
    TOK_GET(&t, &str, &cval);
    if (!t)
        break;
    fprintf(fp,"%s", get_tok_str(t, &cval));
    }
    fprintf(fp, "\n");
}

static void pp_line(TCCState *s1, BufferedFile *f, int level)
{
    int d = f->line_num - f->line_ref;

    if (s1->dflag & 4)
    return;

    if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_NONE) {
        ;
    } else if (level == 0 && f->line_ref && d < 8) {
    while (d > 0)
        fputs("\n", s1->ppfp), --d;
    } else if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_STD) {
    fprintf(s1->ppfp, "#line %d \"%s\"\n", f->line_num, f->filename);
    } else {
    fprintf(s1->ppfp, "# %d \"%s\"%s\n", f->line_num, f->filename,
        level > 0 ? " 1" : level < 0 ? " 2" : "");
    }
    f->line_ref = f->line_num;
}

static void define_print(TCCState *s1, int v)
{
    FILE *fp;
    Sym *s;

    s = define_find(v);
    if (NULL == s || NULL == s->d)
        return;

    fp = s1->ppfp;
    fprintf(fp, "#define %s", get_tok_str(v, NULL));
    if (s->type.t == MACRO_FUNC) {
        Sym *a = s->next;
        fprintf(fp,"(");
        if (a)
            for (;;) {
                fprintf(fp,"%s", get_tok_str(a->v & ~SYM_FIELD, NULL));
                if (!(a = a->next))
                    break;
                fprintf(fp,",");
            }
        fprintf(fp,")");
    }
    tok_print("", s->d);
}

static void pp_debug_defines(TCCState *s1)
{
    int v, t;
    const char *vs;
    FILE *fp;

    t = pp_debug_tok;
    if (t == 0)
        return;

    file->line_num--;
    pp_line(s1, file, 0);
    file->line_ref = ++file->line_num;

    fp = s1->ppfp;
    v = pp_debug_symv;
    vs = get_tok_str(v, NULL);
    if (t == TOK_DEFINE) {
        define_print(s1, v);
    } else if (t == TOK_UNDEF) {
        fprintf(fp, "#undef %s\n", vs);
    } else if (t == TOK_push_macro) {
        fprintf(fp, "#pragma push_macro(\"%s\")\n", vs);
    } else if (t == TOK_pop_macro) {
        fprintf(fp, "#pragma pop_macro(\"%s\")\n", vs);
    }
    pp_debug_tok = 0;
}

static void pp_debug_builtins(TCCState *s1)
{
    int v;
    for (v = TOK_IDENT; v < tok_ident; ++v)
        define_print(s1, v);
}

/* Add a space between tokens a and b to avoid unwanted textual pasting */
static int pp_need_space(int a, int b)
{
    return 'E' == a ? '+' == b || '-' == b
        : '+' == a ? TOK_INC == b || '+' == b
        : '-' == a ? TOK_DEC == b || '-' == b
        : a >= TOK_IDENT ? b >= TOK_IDENT
        : 0;
}

/* maybe hex like 0x1e */
static int pp_check_he0xE(int t, const char *p)
{
    if (t == TOK_PPNUM && toup(strchr(p, 0)[-1]) == 'E')
        return 'E';
    return t;
}

/* Preprocess the current file */
ST_FUNC int tcc_preprocess(TCCState *s1)
{
    BufferedFile **iptr;
    int token_seen, spcs, level;
    const char *p;
    Sym *define_start;

    define_start = define_stack;
    preprocess_start(s1);

    ch = file->buf_ptr[0];
    tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
    parse_flags = PARSE_FLAG_PREPROCESS
                | (parse_flags & PARSE_FLAG_ASM_FILE)
                | PARSE_FLAG_LINEFEED
                | PARSE_FLAG_SPACES
                | PARSE_FLAG_ACCEPT_STRAYS
                ;

    /* Credits to Fabrice Bellard's initial revision to demonstrate its
       capability to compile and run itself, provided all numbers are
       given as decimals. tcc -E -P10 will do. */
    if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_P10)
        parse_flags |= PARSE_FLAG_TOK_NUM, s1->Pflag = 1;

#ifdef PP_BENCH
    /* for PP benchmarks */
    do next(); while (tok != TOK_EOF);
    free_defines(define_start);
    return 0;
#endif

    if (s1->dflag & 1) {
        pp_debug_builtins(s1);
        s1->dflag &= ~1;
    }

    token_seen = TOK_LINEFEED, spcs = 0;
    pp_line(s1, file, 0);

    for (;;) {
        iptr = s1->include_stack_ptr;
        next();
        if (tok == TOK_EOF)
            break;
        level = s1->include_stack_ptr - iptr;
        if (level) {
            if (level > 0)
                pp_line(s1, *iptr, 0);
            pp_line(s1, file, level);
        }

        if (s1->dflag) {
            pp_debug_defines(s1);
            if (s1->dflag & 4)
                continue;
        }

        if (token_seen == TOK_LINEFEED) {
            if (tok == ' ') {
                ++spcs;
                continue;
            }
            if (tok == TOK_LINEFEED) {
                spcs = 0;
                continue;
            }
            pp_line(s1, file, 0);
        } else if (tok == TOK_LINEFEED) {
            ++file->line_ref;
        } else {
            spcs = pp_need_space(token_seen, tok);
        }

        while (spcs)
            fputs(" ", s1->ppfp), --spcs;
        fputs(p = get_tok_str(tok, &tokc), s1->ppfp);
        token_seen = pp_check_he0xE(tok, p);;
    }
    /* reset define stack, but keep -D and built-ins */
    free_defines(define_start);
    return 0;
}

/* ------------------------------------------------------------------------- */

https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tccgen.c

File: ../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tccgen.c used in: Step 378, Step 395, Step 407, Step 419, Step 431, Step 443, Step 455
/*
 *  TCC - Tiny C Compiler
 * 
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef _TCC_H
#include "tcc.h"
#endif

#if BOOTSTRAP && __arm__
void
tccgen_ok ()
{
}
#endif

/********************************************************/
/* global variables */

/* loc : local variable index
   ind : output code index
   rsym: return symbol
   anon_sym: anonymous symbol index
*/
ST_DATA int rsym, anon_sym, ind, loc;

ST_DATA Sym *sym_free_first;
ST_DATA void **sym_pools;
ST_DATA int nb_sym_pools;

ST_DATA Sym *global_stack;
ST_DATA Sym *local_stack;
ST_DATA Sym *define_stack;
ST_DATA Sym *global_label_stack;
ST_DATA Sym *local_label_stack;
static int local_scope;
static int in_sizeof;
static int section_sym;

ST_DATA int vlas_in_scope; /* number of VLAs that are currently in scope */
ST_DATA int vla_sp_root_loc; /* vla_sp_loc for SP before any VLAs were pushed */
ST_DATA int vla_sp_loc; /* Pointer to variable holding location to store stack pointer on the stack when modifying stack pointer */

ST_DATA SValue __vstack[1+VSTACK_SIZE], *vtop, *pvtop;

ST_DATA int const_wanted; /* true if constant wanted */
ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */
ST_DATA int global_expr;  /* true if compound literals must be allocated globally (used during initializers parsing */
ST_DATA CType func_vt; /* current function return type (used by return instruction) */
ST_DATA int func_var; /* true if current function is variadic (used by return instruction) */
ST_DATA int func_vc;
ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
ST_DATA const char *funcname;

ST_DATA CType char_pointer_type, func_old_type, int_type, size_type;

ST_DATA struct switch_t {
    struct case_t {
#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
        int64_t v1, v2;
#else
      int32_t v1, v1_padding, v2, v2_padding;
#endif
    int sym;
    } **p; int n; /* list of case ranges */
    int def_sym; /* default symbol */
} *cur_switch; /* current switch */

/* ------------------------------------------------------------------------- */

static void gen_cast(CType *type);
static inline CType *pointed_type(CType *type);
static int is_compatible_types(CType *type1, CType *type2);
static int parse_btype(CType *type, AttributeDef *ad);
static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td);
static void parse_expr_type(CType *type);
static void init_putv(CType *type, Section *sec, unsigned long c);
static void decl_initializer(CType *type, Section *sec, unsigned long c, int first, int size_only);
static void block(int *bsym, int *csym, int is_expr);
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);
static int decl0(int l, int is_for_loop_init, Sym *);
static void expr_eq(void);
static void vla_runtime_type_size(CType *type, int *a);
static void vla_sp_restore(void);
static void vla_sp_restore_root(void);
static int is_compatible_parameter_types(CType *type1, CType *type2);
#if HAVE_LONG_LONG
static inline int64_t expr_const64(void);
ST_FUNC void vpush64(int ty, unsigned long long v);
#else
static inline int32_t expr_const32(void);
#endif
ST_FUNC void vpush(CType *type);
ST_FUNC int gvtst(int inv, int t);
ST_FUNC int is_btype_size(int bt);
static void gen_inline_functions(TCCState *s);

ST_INLN int is_float(int t)
{
    int bt;
    bt = t & VT_BTYPE;
    return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT || bt == VT_QFLOAT;
}

/* we use our own 'finite' function to avoid potential problems with
   non standard math libs */
/* XXX: endianness dependent */
ST_FUNC int ieee_finite(double d)
{
    int p[4];
    memcpy(p, &d, sizeof(double));
    return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31;
}

ST_FUNC void test_lvalue(void)
{
    if (!(vtop->r & VT_LVAL))
        expect("lvalue");
}

ST_FUNC void check_vstack(void)
{
    if (pvtop != vtop)
        tcc_error("internal compiler error: vstack leak (%d)", vtop - pvtop);
}

/* ------------------------------------------------------------------------- */
/* vstack debugging aid */

#if 0
void pv (const char *lbl, int a, int b)
{
    int i;
    for (i = a; i < a + b; ++i) {
        SValue *p = &vtop[-i];
        printf("%s vtop[-%d] : type.t:%04x  r:%04x  r2:%04x  c.i:%d\n",
            lbl, i, p->type.t, p->r, p->r2, (int)p->c.i);
    }
}
#endif

/* ------------------------------------------------------------------------- */
/* start of translation unit info */
ST_FUNC void tcc_debug_start(TCCState *s1)
{
    if (s1->do_debug) {
        char buf[512];

        /* file info: full path + filename */
        section_sym = put_elf_sym(symtab_section, 0, 0,
                                  ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0,
                                  text_section->sh_num, NULL);
        getcwd(buf, sizeof(buf));
#ifdef _WIN32
        normalize_slashes(buf);
#endif
        pstrcat(buf, sizeof(buf), "/");
        put_stabs_r(buf, N_SO, 0, 0,
                    text_section->data_offset, text_section, section_sym);
        put_stabs_r(file->filename, N_SO, 0, 0,
                    text_section->data_offset, text_section, section_sym);
        last_ind = 0;
        last_line_num = 0;
    }

    /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
       symbols can be safely used */
    put_elf_sym(symtab_section, 0, 0,
                ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
                SHN_ABS, file->filename);
}

/* put end of translation unit info */
ST_FUNC void tcc_debug_end(TCCState *s1)
{
    if (!s1->do_debug)
        return;
    put_stabs_r(NULL, N_SO, 0, 0,
        text_section->data_offset, text_section, section_sym);

}

/* generate line number info */
ST_FUNC void tcc_debug_line(TCCState *s1)
{
    if (!s1->do_debug)
        return;
    if ((last_line_num != file->line_num || last_ind != ind)) {
        put_stabn(N_SLINE, 0, file->line_num, ind - func_ind);
        last_ind = ind;
        last_line_num = file->line_num;
    }
}

/* put function symbol */
ST_FUNC void tcc_debug_funcstart(TCCState *s1, Sym *sym)
{
    char buf[512];

    if (!s1->do_debug)
        return;

    /* stabs info */
    /* XXX: we put here a dummy type */
    snprintf(buf, sizeof(buf), "%s:%c1",
             funcname, sym->type.t & VT_STATIC ? 'f' : 'F');
    put_stabs_r(buf, N_FUN, 0, file->line_num, 0,
                cur_text_section, sym->c);
    /* //gr gdb wants a line at the function */
    put_stabn(N_SLINE, 0, file->line_num, 0);

    last_ind = 0;
    last_line_num = 0;
}

/* put function size */
ST_FUNC void tcc_debug_funcend(TCCState *s1, int size)
{
    if (!s1->do_debug)
        return;
    put_stabn(N_FUN, 0, 0, size);
}

/* ------------------------------------------------------------------------- */
ST_FUNC void tccgen_start(TCCState *s1)
{
    cur_text_section = NULL;
    funcname = "";
    anon_sym = SYM_FIRST_ANOM;
    section_sym = 0;
    const_wanted = 0;
    nocode_wanted = 1;

    /* define some often used types */
    int_type.t = VT_INT;
    char_pointer_type.t = VT_BYTE;
    mk_pointer(&char_pointer_type);
#if PTR_SIZE == 4
    size_type.t = VT_INT;
#else
    size_type.t = VT_LLONG;
#endif
    func_old_type.t = VT_FUNC;
    func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD);

    tcc_debug_start(s1);

#ifdef TCC_TARGET_ARM
    arm_init(s1);
#endif
}

ST_FUNC void tccgen_end(TCCState *s1)
{
    gen_inline_functions(s1);
    check_vstack();
    /* end of translation unit info */
    tcc_debug_end(s1);
}

/* ------------------------------------------------------------------------- */
/* apply storage attributes to Elf symbol */

static void update_storage(Sym *sym)
{
    int t;
    ElfW(Sym) *esym;

    if (0 == sym->c)
        return;

    t = sym->type.t;
    esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];

#if !__MESC__ // Nyacc 0.80.40 cpp-bug
    if (t & VT_VIS_MASK)
        esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
            | ((t & VT_VIS_MASK) >> VT_VIS_SHIFT);

    if (t & VT_WEAK)
        esym->st_info = ELFW(ST_INFO)(STB_WEAK, ELFW(ST_TYPE)(esym->st_info));
#else //__MESC__
    // #warning work around Nyacc 0.80.42 bug.
#endif // __MESC__
#ifdef TCC_TARGET_PE
    if (t & VT_EXPORT)
        esym->st_other |= ST_PE_EXPORT;
#endif
}

/* ------------------------------------------------------------------------- */
/* update sym->c so that it points to an external symbol in section
   'section' with value 'value' */

ST_FUNC void put_extern_sym2(Sym *sym, Section *section,
                            addr_t value, unsigned long size,
                            int can_add_underscore)
{
    int sym_type, sym_bind, sh_num, info, other, t;
    ElfW(Sym) *esym;
    const char *name;
    char buf1[256];
#ifdef CONFIG_TCC_BCHECK
    char buf[32];
#endif

    if (section == NULL)
        sh_num = SHN_UNDEF;
    else if (section == SECTION_ABS)
        sh_num = SHN_ABS;
    else
        sh_num = section->sh_num;

    if (!sym->c) {
        name = get_tok_str(sym->v, NULL);
#ifdef CONFIG_TCC_BCHECK
        if (tcc_state->do_bounds_check) {
            /* XXX: avoid doing that for statics ? */
            /* if bound checking is activated, we change some function
               names by adding the "__bound" prefix */
            switch(sym->v) {
#ifdef TCC_TARGET_PE
            /* XXX: we rely only on malloc hooks */
            case TOK_malloc:
            case TOK_free:
            case TOK_realloc:
            case TOK_memalign:
            case TOK_calloc:
#endif
            case TOK_memcpy:
            case TOK_memmove:
            case TOK_memset:
            case TOK_strlen:
            case TOK_strcpy:
            case TOK_alloca:
                strcpy(buf, "__bound_");
                strcat(buf, name);
                name = buf;
                break;
            }
        }
#endif
        t = sym->type.t;
        if ((t & VT_BTYPE) == VT_FUNC) {
            sym_type = STT_FUNC;
        } else if ((t & VT_BTYPE) == VT_VOID) {
            sym_type = STT_NOTYPE;
        } else {
            sym_type = STT_OBJECT;
        }
        if (t & VT_STATIC)
            sym_bind = STB_LOCAL;
        else
            sym_bind = STB_GLOBAL;
        other = 0;
#ifdef TCC_TARGET_PE
        if (sym_type == STT_FUNC && sym->type.ref) {
            Sym *ref = sym->type.ref;
            if (ref->a.func_call == FUNC_STDCALL && can_add_underscore) {
                sprintf(buf1, "_%s@%d", name, ref->a.func_args * PTR_SIZE);
                name = buf1;
                other |= ST_PE_STDCALL;
                can_add_underscore = 0;
            }
        }
        if (t & VT_IMPORT)
            other |= ST_PE_IMPORT;
#endif
        if (tcc_state->leading_underscore && can_add_underscore) {
            buf1[0] = '_';
            pstrcpy(buf1 + 1, sizeof(buf1) - 1, name);
            name = buf1;
        }
        if (sym->asm_label)
            name = get_tok_str(sym->asm_label, NULL);
        info = ELFW(ST_INFO)(sym_bind, sym_type);
        sym->c = set_elf_sym(symtab_section, value, size, info, other, sh_num, name);
    } else {
        esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
        esym->st_value = value;
        esym->st_size = size;
        esym->st_shndx = sh_num;
    }
    update_storage(sym);
}

ST_FUNC void put_extern_sym(Sym *sym, Section *section,
                           addr_t value, unsigned long size)
{
    put_extern_sym2(sym, section, value, size, 1);
}

/* add a new relocation entry to symbol 'sym' in section 's' */
ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type,
                     addr_t addend)
{
    int c = 0;

    if (nocode_wanted && s == cur_text_section)
        return;

    if (sym) {
        if (0 == sym->c)
            put_extern_sym(sym, NULL, 0, 0);
        c = sym->c;
    }

    /* now we can add ELF relocation info */
    put_elf_reloca(symtab_section, s, offset, type, c, addend);
}

#if PTR_SIZE == 4
ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type)
{
    greloca(s, sym, offset, type, 0);
}
#endif

/* ------------------------------------------------------------------------- */
/* symbol allocator */
static Sym *__sym_malloc(void)
{
    Sym *sym_pool, *sym, *last_sym;
    int i;

    sym_pool = tcc_malloc(SYM_POOL_NB * sizeof(Sym));
    dynarray_add(&sym_pools, &nb_sym_pools, sym_pool);

    last_sym = sym_free_first;
    sym = sym_pool;
    for(i = 0; i < SYM_POOL_NB; i++) {
        sym->next = last_sym;
        last_sym = sym;
        sym++;
    }
    sym_free_first = last_sym;
    return last_sym;
}

static inline Sym *sym_malloc(void)
{
    Sym *sym;
#ifndef SYM_DEBUG
    sym = sym_free_first;
    if (!sym)
        sym = __sym_malloc();
    sym_free_first = sym->next;
    return sym;
#else
    sym = tcc_malloc(sizeof(Sym));
    return sym;
#endif
}

ST_INLN void sym_free(Sym *sym)
{
#ifndef SYM_DEBUG
    sym->next = sym_free_first;
    sym_free_first = sym;
#else
    tcc_free(sym);
#endif
}

/* push, without hashing */
ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, long c)
{
    Sym *s;

    s = sym_malloc();
    s->scope = 0;
    s->v = v;
    s->type.t = t;
    s->type.ref = NULL;
#ifdef _WIN64
    s->d = NULL;
#endif
    s->c = c;
    s->next = NULL;
    /* add in stack */
    s->prev = *ps;
    *ps = s;
    return s;
}

/* find a symbol and return its associated structure. 's' is the top
   of the symbol stack */
ST_FUNC Sym *sym_find2(Sym *s, int v)
{
    while (s) {
        if (s->v == v)
            return s;
        else if (s->v == -1)
            return NULL;
        s = s->prev;
    }
    return NULL;
}

/* structure lookup */
ST_INLN Sym *struct_find(int v)
{
    v -= TOK_IDENT;
    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
        return NULL;
    return table_ident[v]->sym_struct;
}

/* find an identifier */
ST_INLN Sym *sym_find(int v)
{
    v -= TOK_IDENT;
    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
        return NULL;
    return table_ident[v]->sym_identifier;
}

/* push a given symbol on the symbol stack */
ST_FUNC Sym *sym_push(int v, CType *type, int r, long c)
{
    Sym *s, **ps;
    TokenSym *ts;

    if (local_stack)
        ps = &local_stack;
    else
        ps = &global_stack;
    s = sym_push2(ps, v, type->t, c);
    s->type.ref = type->ref;
    s->r = r;
    /* don't record fields or anonymous symbols */
    /* XXX: simplify */
    if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
        /* record symbol in token array */
        ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
        if (v & SYM_STRUCT)
            ps = &ts->sym_struct;
        else
            ps = &ts->sym_identifier;
        s->prev_tok = *ps;
        *ps = s;
        s->scope = local_scope;
        if (s->prev_tok && s->prev_tok->scope == s->scope)
            tcc_error("redeclaration of '%s'",
                get_tok_str(v & ~SYM_STRUCT, NULL));
    }
    return s;
}

/* push a global identifier */
ST_FUNC Sym *global_identifier_push(int v, int t, long c)
{
    Sym *s, **ps;
    s = sym_push2(&global_stack, v, t, c);
    /* don't record anonymous symbol */
    if (v < SYM_FIRST_ANOM) {
        ps = &table_ident[v - TOK_IDENT]->sym_identifier;
        /* modify the top most local identifier, so that
           sym_identifier will point to 's' when popped */
        while (*ps != NULL)
            ps = &(*ps)->prev_tok;
        s->prev_tok = NULL;
        *ps = s;
    }
    return s;
}

/* pop symbols until top reaches 'b'.  If KEEP is non-zero don't really
   pop them yet from the list, but do remove them from the token array.  */
ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep)
{
    Sym *s, *ss, **ps;
    TokenSym *ts;
    int v;

    s = *ptop;
    while(s != b) {
        ss = s->prev;
        v = s->v;
        /* remove symbol in token array */
        /* XXX: simplify */
        if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
            ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
            if (v & SYM_STRUCT)
                ps = &ts->sym_struct;
            else
                ps = &ts->sym_identifier;
            *ps = s->prev_tok;
        }
    if (!keep)
        sym_free(s);
        s = ss;
    }
    if (!keep)
    *ptop = b;
}

/* ------------------------------------------------------------------------- */

static void vsetc(CType *type, int r, CValue *vc)
{
    int v;

    if (vtop >= vstack + (VSTACK_SIZE - 1))
        tcc_error("memory full (vstack)");
    /* cannot let cpu flags if other instruction are generated. Also
       avoid leaving VT_JMP anywhere except on the top of the stack
       because it would complicate the code generator.

       Don't do this when nocode_wanted.  vtop might come from
       !nocode_wanted regions (see 88_codeopt.c) and transforming
       it to a register without actually generating code is wrong
       as their value might still be used for real.  All values
       we push under nocode_wanted will eventually be popped
       again, so that the VT_CMP/VT_JMP value will be in vtop
       when code is unsuppressed again.

       Same logic below in vswap(); */
    if (vtop >= vstack && !nocode_wanted) {
        v = vtop->r & VT_VALMASK;
        if (v == VT_CMP || (v & ~1) == VT_JMP)
            gv(RC_INT);
    }

    vtop++;
    vtop->type = *type;
    vtop->r = r;
    vtop->r2 = VT_CONST;
    vtop->c = *vc;
    vtop->sym = NULL;
}

ST_FUNC void vswap(void)
{
    SValue tmp;
    /* cannot vswap cpu flags. See comment at vsetc() above */
    if (vtop >= vstack && !nocode_wanted) {
        int v = vtop->r & VT_VALMASK;
        if (v == VT_CMP || (v & ~1) == VT_JMP)
            gv(RC_INT);
    }
    tmp = vtop[0];
    vtop[0] = vtop[-1];
    vtop[-1] = tmp;
}

/* pop stack value */
ST_FUNC void vpop(void)
{
    int v;
    v = vtop->r & VT_VALMASK;
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
    /* for x86, we need to pop the FP stack */
    if (v == TREG_ST0) {
        o(0xd8dd); /* fstp %st(0) */
    } else
#endif
    if (v == VT_JMP || v == VT_JMPI) {
        /* need to put correct jump if && or || without test */
        gsym(vtop->c.i);
    }
    vtop--;
}

/* push constant of type "type" with useless value */
ST_FUNC void vpush(CType *type)
{
    CValue cval;
    vsetc(type, VT_CONST, &cval);
}

/* push integer constant */
ST_FUNC void vpushi(int v)
{
    CValue cval;
    cval.i = v;
    vsetc(&int_type, VT_CONST, &cval);
}

/* push a pointer sized constant */
static void vpushs(addr_t v)
{
  CValue cval;
  cval.i = v;
  vsetc(&size_type, VT_CONST, &cval);
}

/* push arbitrary 64bit constant */
ST_FUNC void vpush64(int ty, unsigned long long v)
{
    CValue cval;
    CType ctype;
    ctype.t = ty;
    ctype.ref = NULL;
    cval.i = v;
    vsetc(&ctype, VT_CONST, &cval);
}

/* push long long constant */
static inline void vpushll(long long v)
{
    vpush64(VT_LLONG, v);
}

ST_FUNC void vset(CType *type, int r, long v)
{
    CValue cval;

    cval.i = v;
    vsetc(type, r, &cval);
}

static void vseti(int r, int v)
{
    CType type;
    type.t = VT_INT;
    type.ref = 0;
    vset(&type, r, v);
}

ST_FUNC void vpushv(SValue *v)
{
    if (vtop >= vstack + (VSTACK_SIZE - 1))
        tcc_error("memory full (vstack)");
    vtop++;
    *vtop = *v;
}

static void vdup(void)
{
    vpushv(vtop);
}

/* rotate n first stack elements to the bottom
   I1 ... In -> I2 ... In I1 [top is right]
*/
ST_FUNC void vrotb(int n)
{
    int i;
    SValue tmp;

    tmp = vtop[-n + 1];
    for(i=-n+1;i!=0;i++)
        vtop[i] = vtop[i+1];
    vtop[0] = tmp;
}

/* rotate the n elements before entry e towards the top
   I1 ... In ... -> In I1 ... I(n-1) ... [top is right]
 */
ST_FUNC void vrote(SValue *e, int n)
{
    int i;
    SValue tmp;

    tmp = *e;
    for(i = 0;i < n - 1; i++)
        e[-i] = e[-i - 1];
    e[-n + 1] = tmp;
}

/* rotate n first stack elements to the top
   I1 ... In -> In I1 ... I(n-1)  [top is right]
 */
ST_FUNC void vrott(int n)
{
    vrote(vtop, n);
}

/* push a symbol value of TYPE */
static inline void vpushsym(CType *type, Sym *sym)
{
    CValue cval;
    cval.i = 0;
    vsetc(type, VT_CONST | VT_SYM, &cval);
    vtop->sym = sym;
}

/* Return a static symbol pointing to a section */
ST_FUNC Sym *get_sym_ref(CType *type, Section *sec, unsigned long offset, unsigned long size)
{
    int v;
    Sym *sym;

    v = anon_sym++;
    sym = global_identifier_push(v, type->t | VT_STATIC, 0);
    sym->type.ref = type->ref;
    sym->r = VT_CONST | VT_SYM;
    put_extern_sym(sym, sec, offset, size);
    return sym;
}

/* push a reference to a section offset by adding a dummy symbol */
static void vpush_ref(CType *type, Section *sec, unsigned long offset, unsigned long size)
{
    vpushsym(type, get_sym_ref(type, sec, offset, size));  
}

/* define a new external reference to a symbol 'v' of type 'u' */
ST_FUNC Sym *external_global_sym(int v, CType *type, int r)
{
    Sym *s;

    s = sym_find(v);
    if (!s) {
        /* push forward reference */
        s = global_identifier_push(v, type->t | VT_EXTERN, 0);
        s->type.ref = type->ref;
        s->r = r | VT_CONST | VT_SYM;
    }
    return s;
}

/* Merge some storage attributes.  */
static void patch_storage(Sym *sym, CType *type)
{
    int t;
    if (!is_compatible_types(&sym->type, type))
        tcc_error("incompatible types for redefinition of '%s'",
              get_tok_str(sym->v, NULL));
    t = type->t;
#ifdef TCC_TARGET_PE
    if ((sym->type.t ^ t) & VT_IMPORT)
        tcc_error("incompatible dll linkage for redefinition of '%s'",
            get_tok_str(sym->v, NULL));
#endif
    sym->type.t |= t & (VT_EXPORT|VT_WEAK);
    if (t & VT_VIS_MASK) {
        int vis = sym->type.t & VT_VIS_MASK;
        int vis2 = t & VT_VIS_MASK;
        if (vis == (STV_DEFAULT << VT_VIS_SHIFT))
            vis = vis2;
        else if (vis2 != (STV_DEFAULT << VT_VIS_SHIFT))
            vis = (vis < vis2) ? vis : vis2;
        sym->type.t = (sym->type.t & ~VT_VIS_MASK) | vis;
    }
}

/* define a new external reference to a symbol 'v' */
static Sym *external_sym(int v, CType *type, int r)
{
    Sym *s;
    s = sym_find(v);
    if (!s) {
        /* push forward reference */
        s = sym_push(v, type, r | VT_CONST | VT_SYM, 0);
        s->type.t |= VT_EXTERN;
    } else {
        if (s->type.ref == func_old_type.ref) {
            s->type.ref = type->ref;
            s->r = r | VT_CONST | VT_SYM;
            s->type.t |= VT_EXTERN;
        }
        patch_storage(s, type);
        update_storage(s);
    }
    return s;
}

/* push a reference to global symbol v */
ST_FUNC void vpush_global_sym(CType *type, int v)
{
    vpushsym(type, external_global_sym(v, type, 0));
}

/* save registers up to (vtop - n) stack entry */
ST_FUNC void save_regs(int n)
{
    SValue *p, *p1;
    for(p = vstack, p1 = vtop - n; p <= p1; p++)
        save_reg(p->r);
}

/* save r to the memory stack, and mark it as being free */
ST_FUNC void save_reg(int r)
{
    save_reg_upstack(r, 0);
}

/* save r to the memory stack, and mark it as being free,
   if seen up to (vtop - n) stack entry */
ST_FUNC void save_reg_upstack(int r, int n)
{
    int l, saved, size, align;
    SValue *p, *p1, sv;
    CType *type;

    if ((r &= VT_VALMASK) >= VT_CONST)
        return;
    if (nocode_wanted)
        return;

    /* modify all stack values */
    saved = 0;
    l = 0;
    for(p = vstack, p1 = vtop - n; p <= p1; p++) {
        if ((p->r & VT_VALMASK) == r ||
            ((p->type.t & VT_BTYPE) == VT_LLONG && (p->r2 & VT_VALMASK) == r)) {
            /* must save value on stack if not already done */
            if (!saved) {
                /* NOTE: must reload 'r' because r might be equal to r2 */
                r = p->r & VT_VALMASK;
                /* store register in the stack */
                type = &p->type;
                if ((p->r & VT_LVAL) ||
                    (!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG))
#if PTR_SIZE == 8
                    type = &char_pointer_type;
#else
                    type = &int_type;
#endif
                size = type_size(type, &align);
                loc = (loc - size) & -align;
                sv.type.t = type->t;
                sv.r = VT_LOCAL | VT_LVAL;
                sv.c.i = loc;
                store(r, &sv);
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
                /* x86 specific: need to pop fp register ST0 if saved */
                if (r == TREG_ST0) {
                    o(0xd8dd); /* fstp %st(0) */
                }
#endif
#if PTR_SIZE == 4
                /* special long long case */
                if ((type->t & VT_BTYPE) == VT_LLONG) {
                    sv.c.i += 4;
                    store(p->r2, &sv);
                }
#endif
                l = loc;
                saved = 1;
            }
            /* mark that stack entry as being saved on the stack */
            if (p->r & VT_LVAL) {
                /* also clear the bounded flag because the
                   relocation address of the function was stored in
                   p->c.i */
                p->r = (p->r & ~(VT_VALMASK | VT_BOUNDED)) | VT_LLOCAL;
            } else {
                p->r = lvalue_type(p->type.t) | VT_LOCAL;
            }
            p->r2 = VT_CONST;
            p->c.i = l;
        }
    }
}

#ifdef TCC_TARGET_ARM
/* find a register of class 'rc2' with at most one reference on stack.
 * If none, call get_reg(rc) */
ST_FUNC int get_reg_ex(int rc, int rc2)
{
    int r;
    SValue *p;
    
    for(r=0;r<NB_REGS;r++) {
        if (reg_classes[r] & rc2) {
            int n;
            n=0;
            for(p = vstack; p <= vtop; p++) {
                if ((p->r & VT_VALMASK) == r ||
                    (p->r2 & VT_VALMASK) == r)
                    n++;
            }
            if (n <= 1)
                return r;
        }
    }
    return get_reg(rc);
}
#endif

/* find a free register of class 'rc'. If none, save one register */
ST_FUNC int get_reg(int rc)
{
    int r;
    SValue *p;

    /* find a free register */
    for(r=0;r<NB_REGS;r++) {
        if (reg_classes[r] & rc) {
            if (nocode_wanted)
                return r;
            for(p=vstack;p<=vtop;p++) {
                if ((p->r & VT_VALMASK) == r ||
                    (p->r2 & VT_VALMASK) == r)
                    goto notfound;
            }
            return r;
        }
    notfound: ;
    }
    
    /* no register left : free the first one on the stack (VERY
       IMPORTANT to start from the bottom to ensure that we don't
       spill registers used in gen_opi()) */
    for(p=vstack;p<=vtop;p++) {
        /* look at second register (if long long) */
        r = p->r2 & VT_VALMASK;
        if (r < VT_CONST && (reg_classes[r] & rc))
            goto save_found;
        r = p->r & VT_VALMASK;
        if (r < VT_CONST && (reg_classes[r] & rc)) {
        save_found:
            save_reg(r);
            return r;
        }
    }
    /* Should never comes here */
    return -1;
}

/* move register 's' (of type 't') to 'r', and flush previous value of r to memory
   if needed */
static void move_reg(int r, int s, int t)
{
    SValue sv;

    if (r != s) {
        save_reg(r);
        sv.type.t = t;
        sv.type.ref = NULL;
        sv.r = s;
        sv.c.i = 0;
        load(r, &sv);
    }
}

/* get address of vtop (vtop MUST BE an lvalue) */
ST_FUNC void gaddrof(void)
{
    vtop->r &= ~VT_LVAL;
    /* tricky: if saved lvalue, then we can go back to lvalue */
    if ((vtop->r & VT_VALMASK) == VT_LLOCAL)
        vtop->r = (vtop->r & ~(VT_VALMASK | VT_LVAL_TYPE)) | VT_LOCAL | VT_LVAL;


}

#ifdef CONFIG_TCC_BCHECK
/* generate lvalue bound code */
static void gbound(void)
{
    int lval_type;
    CType type1;

    vtop->r &= ~VT_MUSTBOUND;
    /* if lvalue, then use checking code before dereferencing */
    if (vtop->r & VT_LVAL) {
        /* if not VT_BOUNDED value, then make one */
        if (!(vtop->r & VT_BOUNDED)) {
            lval_type = vtop->r & (VT_LVAL_TYPE | VT_LVAL);
            /* must save type because we must set it to int to get pointer */
            type1 = vtop->type;
            vtop->type.t = VT_PTR;
            gaddrof();
            vpushi(0);
            gen_bounded_ptr_add();
            vtop->r |= lval_type;
            vtop->type = type1;
        }
        /* then check for dereferencing */
        gen_bounded_ptr_deref();
    }
}
#endif

/* store vtop a register belonging to class 'rc'. lvalues are
   converted to values. Cannot be used if cannot be converted to
   register value (such as structures). */
ST_FUNC int gv(int rc)
{
    int r, bit_pos, bit_size, size, align;
    int rc2;

    /* NOTE: get_reg can modify vstack[] */
    if (vtop->type.t & VT_BITFIELD) {
        CType type;
        int bits = 32;
        bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f;
        bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
        /* remove bit field info to avoid loops */
        vtop->type.t &= ~VT_BITFIELD & ((1 << VT_STRUCT_SHIFT) - 1);
        /* cast to int to propagate signedness in following ops */
        if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
            type.t = VT_LLONG;
            bits = 64;
        } else
            type.t = VT_INT;
        if((vtop->type.t & VT_UNSIGNED) ||
           (vtop->type.t & VT_BTYPE) == VT_BOOL ||
       (((vtop->type.t & VT_BTYPE) == VT_ENUM) &&
        vtop->type.ref->a.unsigned_enum))
            type.t |= VT_UNSIGNED;
        gen_cast(&type);
        /* generate shifts */
        vpushi(bits - (bit_pos + bit_size));
        gen_op(TOK_SHL);
        vpushi(bits - bit_size);
        /* NOTE: transformed to SHR if unsigned */
        gen_op(TOK_SAR);
        r = gv(rc);
    } else {
        if (is_float(vtop->type.t) && 
            (vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
            unsigned long offset;
            /* CPUs usually cannot use float constants, so we store them
               generically in data segment */
            size = type_size(&vtop->type, &align);
        offset = section_add(data_section, size, align);
            vpush_ref(&vtop->type, data_section, offset, size);
        vswap();
        init_putv(&vtop->type, data_section, offset);
        vtop->r |= VT_LVAL;
        }
#ifdef CONFIG_TCC_BCHECK
        if (vtop->r & VT_MUSTBOUND) 
            gbound();
#endif

        r = vtop->r & VT_VALMASK;
        rc2 = (rc & RC_FLOAT) ? RC_FLOAT : RC_INT;
#ifndef TCC_TARGET_ARM64
        if (rc == RC_IRET)
            rc2 = RC_LRET;
#ifdef TCC_TARGET_X86_64
        else if (rc == RC_FRET)
            rc2 = RC_QRET;
#endif
#endif
        /* need to reload if:
           - constant
           - lvalue (need to dereference pointer)
           - already a register, but not in the right class */
        if (r >= VT_CONST
         || (vtop->r & VT_LVAL)
         || !(reg_classes[r] & rc)
#if PTR_SIZE == 8
         || ((vtop->type.t & VT_BTYPE) == VT_QLONG && !(reg_classes[vtop->r2] & rc2))
         || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT && !(reg_classes[vtop->r2] & rc2))
#else
         || ((vtop->type.t & VT_BTYPE) == VT_LLONG && !(reg_classes[vtop->r2] & rc2))
#endif
            )
        {
            r = get_reg(rc);
#if PTR_SIZE == 8
            if (((vtop->type.t & VT_BTYPE) == VT_QLONG) || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT)) {
                int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
#else
            if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
                int addr_type = VT_INT, load_size = 4, load_type = VT_INT;
                unsigned long long ll;
#endif
                int r2, original_type;
                original_type = vtop->type.t;
                /* two register type load : expand to two words
                   temporarily */
#if PTR_SIZE == 4
                if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
                    /* load constant */
                    ll = vtop->c.i;
                    vtop->c.i = ll; /* first word */
                    load(r, vtop);
                    vtop->r = r; /* save register value */
                    vpushi(ll >> 32); /* second word */
                } else
#endif
                if (vtop->r & VT_LVAL) {
                    /* We do not want to modifier the long long
                       pointer here, so the safest (and less
                       efficient) is to save all the other registers
                       in the stack. XXX: totally inefficient. */
               #if 0
                    save_regs(1);
               #else
                    /* lvalue_save: save only if used further down the stack */
                    save_reg_upstack(vtop->r, 1);
               #endif
                    /* load from memory */
                    vtop->type.t = load_type;
                    load(r, vtop);
                    vdup();
                    vtop[-1].r = r; /* save register value */
                    /* increment pointer to get second word */
                    vtop->type.t = addr_type;
                    gaddrof();
                    vpushi(load_size);
                    gen_op('+');
                    vtop->r |= VT_LVAL;
                    vtop->type.t = load_type;
                } else {
                    /* move registers */
                    load(r, vtop);
                    vdup();
                    vtop[-1].r = r; /* save register value */
                    vtop->r = vtop[-1].r2;
                }
                /* Allocate second register. Here we rely on the fact that
                   get_reg() tries first to free r2 of an SValue. */
                r2 = get_reg(rc2);
                load(r2, vtop);
                vpop();
                /* write second register */
                vtop->r2 = r2;
                vtop->type.t = original_type;
            } else if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) {
                int t1, t;
                /* lvalue of scalar type : need to use lvalue type
                   because of possible cast */
                t = vtop->type.t;
                t1 = t;
                /* compute memory access type */
                if (vtop->r & VT_LVAL_BYTE)
                    t = VT_BYTE;
                else if (vtop->r & VT_LVAL_SHORT)
                    t = VT_SHORT;
                if (vtop->r & VT_LVAL_UNSIGNED)
                    t |= VT_UNSIGNED;
                vtop->type.t = t;
                load(r, vtop);
                /* restore wanted type */
                vtop->type.t = t1;
            } else {
                /* one register type load */
                load(r, vtop);
            }
        }
        vtop->r = r;
#ifdef TCC_TARGET_C67
        /* uses register pairs for doubles */
        if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) 
            vtop->r2 = r+1;
#endif
    }
    return r;
}

/* generate vtop[-1] and vtop[0] in resp. classes rc1 and rc2 */
ST_FUNC void gv2(int rc1, int rc2)
{
    int v;

    /* generate more generic register first. But VT_JMP or VT_CMP
       values must be generated first in all cases to avoid possible
       reload errors */
    v = vtop[0].r & VT_VALMASK;
    if (v != VT_CMP && (v & ~1) != VT_JMP && rc1 <= rc2) {
        vswap();
        gv(rc1);
        vswap();
        gv(rc2);
        /* test if reload is needed for first register */
        if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) {
            vswap();
            gv(rc1);
            vswap();
        }
    } else {
        gv(rc2);
        vswap();
        gv(rc1);
        vswap();
        /* test if reload is needed for first register */
        if ((vtop[0].r & VT_VALMASK) >= VT_CONST) {
            gv(rc2);
        }
    }
}

#ifndef TCC_TARGET_ARM64
/* wrapper around RC_FRET to return a register by type */
static int rc_fret(int t)
{
#ifdef TCC_TARGET_X86_64
    if (t == VT_LDOUBLE) {
        return RC_ST0;
    }
#endif
    return RC_FRET;
}
#endif

/* wrapper around REG_FRET to return a register by type */
static int reg_fret(int t)
{
#ifdef TCC_TARGET_X86_64
    if (t == VT_LDOUBLE) {
        return TREG_ST0;
    }
#endif
    return REG_FRET;
}

#if PTR_SIZE == 4
/* expand 64bit on stack in two ints */
static void lexpand(void)
{
    int u, v;
    u = vtop->type.t & (VT_DEFSIGN | VT_UNSIGNED);
    v = vtop->r & (VT_VALMASK | VT_LVAL);
    if (v == VT_CONST) {
        vdup();
#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
        vtop[0].c.i >>= 32;
#endif
    } else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) {
        vdup();
        vtop[0].c.i += 4;
    } else {
        gv(RC_INT);
        vdup();
        vtop[0].r = vtop[-1].r2;
        vtop[0].r2 = vtop[-1].r2 = VT_CONST;
    }
    vtop[0].type.t = vtop[-1].type.t = VT_INT | u;
}
#endif

#ifdef TCC_TARGET_ARM
/* expand long long on stack */
ST_FUNC void lexpand_nr(void)
{
    int u,v;

    u = vtop->type.t & (VT_DEFSIGN | VT_UNSIGNED);
    vdup();
    vtop->r2 = VT_CONST;
    vtop->type.t = VT_INT | u;
    v=vtop[-1].r & (VT_VALMASK | VT_LVAL);
    if (v == VT_CONST) {
      vtop[-1].c.i = vtop->c.i;
#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
      vtop->c.i = vtop->c.i >> 32;
#endif
      vtop->r = VT_CONST;
    } else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) {
      vtop->c.i += 4;
      vtop->r = vtop[-1].r;
    } else if (v > VT_CONST) {
      vtop--;
      lexpand();
    } else
      vtop->r = vtop[-1].r2;
    vtop[-1].r2 = VT_CONST;
    vtop[-1].type.t = VT_INT | u;
}
#endif

#if PTR_SIZE == 4
/* build a long long from two ints */
static void lbuild(int t)
{
    gv2(RC_INT, RC_INT);
    vtop[-1].r2 = vtop[0].r;
    vtop[-1].type.t = t;
    vpop();
}
#endif

/* convert stack entry to register and duplicate its value in another
   register */
static void gv_dup(void)
{
    int rc, t, r, r1;
    SValue sv;

    t = vtop->type.t;
#if PTR_SIZE == 4
    if ((t & VT_BTYPE) == VT_LLONG) {
        lexpand();
        gv_dup();
        vswap();
        vrotb(3);
        gv_dup();
        vrotb(4);
        /* stack: H L L1 H1 */
        lbuild(t);
        vrotb(3);
        vrotb(3);
        vswap();
        lbuild(t);
        vswap();
    } else
#endif
    {
        /* duplicate value */
        rc = RC_INT;
        sv.type.t = VT_INT;
        if (is_float(t)) {
            rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64
            if ((t & VT_BTYPE) == VT_LDOUBLE) {
                rc = RC_ST0;
            }
#endif
            sv.type.t = t;
        }
        r = gv(rc);
        r1 = get_reg(rc);
        sv.r = r;
        sv.c.i = 0;
        load(r1, &sv); /* move r to r1 */
        vdup();
        /* duplicates value */
        if (r != r1)
            vtop->r = r1;
    }
}

/* Generate value test
 *
 * Generate a test for any value (jump, comparison and integers) */
ST_FUNC int gvtst(int inv, int t)
{
    int v = vtop->r & VT_VALMASK;
    if (v != VT_CMP && v != VT_JMP && v != VT_JMPI) {
        vpushi(0);
        gen_op(TOK_NE);
    }
    if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
        /* constant jmp optimization */
        if ((vtop->c.i != 0) != inv)
            t = gjmp(t);
        vtop--;
        return t;
    }
    return gtst(inv, t);
}

#if PTR_SIZE == 4
/* generate CPU independent (unsigned) long long operations */
static void gen_opl(int op)
{
    int t, a, b, op1, c, i;
    int func;
    unsigned short reg_iret = REG_IRET;
    unsigned short reg_lret = REG_LRET;
    SValue tmp;

    switch(op) {
    case '/':
    case TOK_PDIV:
        func = TOK___divdi3;
        goto gen_func;
    case TOK_UDIV:
        func = TOK___udivdi3;
        goto gen_func;
    case '%':
        func = TOK___moddi3;
        goto gen_mod_func;
    case TOK_UMOD:
        func = TOK___umoddi3;
    gen_mod_func:
#ifdef TCC_ARM_EABI
        reg_iret = TREG_R2;
        reg_lret = TREG_R3;
#endif
    gen_func:
        /* call generic long long function */
        vpush_global_sym(&func_old_type, func);
        vrott(3);
        gfunc_call(2);
        vpushi(0);
        vtop->r = reg_iret;
        vtop->r2 = reg_lret;
        break;
    case '^':
    case '&':
    case '|':
    case '*':
    case '+':
    case '-':
        //pv("gen_opl A",0,2);
        t = vtop->type.t;
        vswap();
        lexpand();
        vrotb(3);
        lexpand();
        /* stack: L1 H1 L2 H2 */
        tmp = vtop[0];
        vtop[0] = vtop[-3];
        vtop[-3] = tmp;
        tmp = vtop[-2];
        vtop[-2] = vtop[-3];
        vtop[-3] = tmp;
        vswap();
        /* stack: H1 H2 L1 L2 */
        //pv("gen_opl B",0,4);
        if (op == '*') {
            vpushv(vtop - 1);
            vpushv(vtop - 1);
            gen_op(TOK_UMULL);
            lexpand();
            /* stack: H1 H2 L1 L2 ML MH */
            for(i=0;i<4;i++)
                vrotb(6);
            /* stack: ML MH H1 H2 L1 L2 */
            tmp = vtop[0];
            vtop[0] = vtop[-2];
            vtop[-2] = tmp;
            /* stack: ML MH H1 L2 H2 L1 */
            gen_op('*');
            vrotb(3);
            vrotb(3);
            gen_op('*');
            /* stack: ML MH M1 M2 */
            gen_op('+');
            gen_op('+');
        } else if (op == '+' || op == '-') {
            /* XXX: add non carry method too (for MIPS or alpha) */
            if (op == '+')
                op1 = TOK_ADDC1;
            else
                op1 = TOK_SUBC1;
            gen_op(op1);
            /* stack: H1 H2 (L1 op L2) */
            vrotb(3);
            vrotb(3);
            gen_op(op1 + 1); /* TOK_xxxC2 */
        } else {
            gen_op(op);
            /* stack: H1 H2 (L1 op L2) */
            vrotb(3);
            vrotb(3);
            /* stack: (L1 op L2) H1 H2 */
            gen_op(op);
            /* stack: (L1 op L2) (H1 op H2) */
        }
        /* stack: L H */
        lbuild(t);
        break;
    case TOK_SAR:
    case TOK_SHR:
    case TOK_SHL:
        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
            t = vtop[-1].type.t;
            vswap();
            lexpand();
            vrotb(3);
            /* stack: L H shift */
            c = (int)vtop->c.i;
            /* constant: simpler */
            /* NOTE: all comments are for SHL. the other cases are
               done by swapping words */
            vpop();
            if (op != TOK_SHL)
                vswap();
            if (c >= 32) {
                /* stack: L H */
                vpop();
                if (c > 32) {
                    vpushi(c - 32);
                    gen_op(op);
                }
                if (op != TOK_SAR) {
                    vpushi(0);
                } else {
                    gv_dup();
                    vpushi(31);
                    gen_op(TOK_SAR);
                }
                vswap();
            } else {
                vswap();
                gv_dup();
                /* stack: H L L */
                vpushi(c);
                gen_op(op);
                vswap();
                vpushi(32 - c);
                if (op == TOK_SHL)
                    gen_op(TOK_SHR);
                else
                    gen_op(TOK_SHL);
                vrotb(3);
                /* stack: L L H */
                vpushi(c);
                if (op == TOK_SHL)
                    gen_op(TOK_SHL);
                else
                    gen_op(TOK_SHR);
                gen_op('|');
            }
            if (op != TOK_SHL)
                vswap();
            lbuild(t);
        } else {
            /* XXX: should provide a faster fallback on x86 ? */
            switch(op) {
            case TOK_SAR:
                func = TOK___ashrdi3;
                goto gen_func;
            case TOK_SHR:
                func = TOK___lshrdi3;
                goto gen_func;
            case TOK_SHL:
                func = TOK___ashldi3;
                goto gen_func;
            }
        }
        break;
    default:
        /* compare operations */
        t = vtop->type.t;
        vswap();
        lexpand();
        vrotb(3);
        lexpand();
        /* stack: L1 H1 L2 H2 */
        tmp = vtop[-1];
        vtop[-1] = vtop[-2];
        vtop[-2] = tmp;
        /* stack: L1 L2 H1 H2 */
        /* compare high */
        op1 = op;
        /* when values are equal, we need to compare low words. since
           the jump is inverted, we invert the test too. */
        if (op1 == TOK_LT)
            op1 = TOK_LE;
        else if (op1 == TOK_GT)
            op1 = TOK_GE;
        else if (op1 == TOK_ULT)
            op1 = TOK_ULE;
        else if (op1 == TOK_UGT)
            op1 = TOK_UGE;
        a = 0;
        b = 0;
        gen_op(op1);
        if (op == TOK_NE) {
            b = gvtst(0, 0);
        } else {
            a = gvtst(1, 0);
            if (op != TOK_EQ) {
                /* generate non equal test */
                vpushi(TOK_NE);
                vtop->r = VT_CMP;
                b = gvtst(0, 0);
            }
        }
        /* compare low. Always unsigned */
        op1 = op;
        if (op1 == TOK_LT)
            op1 = TOK_ULT;
        else if (op1 == TOK_LE)
            op1 = TOK_ULE;
        else if (op1 == TOK_GT)
            op1 = TOK_UGT;
        else if (op1 == TOK_GE)
            op1 = TOK_UGE;
        gen_op(op1);
        a = gvtst(1, a);
        gsym(b);
        vseti(VT_JMPI, a);
        break;
    }
}
#endif

#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
static uint64_t gen_opic_sdiv(uint64_t a, uint64_t b)
{
#if HAVE_LONG_LONG
    uint64_t x = (a >> 63 ? -a : a) / (b >> 63 ? -b : b);
    return (a ^ b) >> 63 ? -x : x;
#else
    uint32_t x = (a >> 31 ? -a : a) / (b >> 31 ? -b : b);
    return (a ^ b) >> 31 ? -x : x;
#endif
}

static int gen_opic_lt(uint64_t a, uint64_t b)
{
#if HAVE_LONG_LONG
    return (a ^ (uint64_t)1 << 63) < (b ^ (uint64_t)1 << 63);
#else
    return (a ^ (uint32_t)1 << 31) < (b ^ (uint32_t)1 << 31);
#endif
}
#else
static uint32_t gen_opic_sdiv(uint32_t a, uint32_t b)
{
    uint32_t x = (a >> 31 ? -a : a) / (b >> 31 ? -b : b);
    return (a ^ b) >> 31 ? -x : x;
}

static int gen_opic_lt(uint32_t a, uint32_t b)
{
    return (a ^ (uint32_t)1 << 31) < (b ^ (uint32_t)1 << 31);
}
#endif

/* handle integer constant optimizations and various machine
   independent opt */
static void gen_opic(int op)
{
    SValue *v1 = vtop - 1;
    SValue *v2 = vtop;
    int t1 = v1->type.t & VT_BTYPE;
    int t2 = v2->type.t & VT_BTYPE;
    int c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
    int c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
#if HAVE_LONG_LONG
    uint64_t l1 = c1 ? v1->c.i : 0;
    uint64_t l2 = c2 ? v2->c.i : 0;
    int shm = (t1 == VT_LLONG) ? 63 : 31;
#else
    uint32_t l1 = c1 ? v1->c.i : 0;
    uint32_t l2 = c2 ? v2->c.i : 0;
    int shm = 31;
#endif
    if (t1 != VT_LLONG && (PTR_SIZE != 8 || t1 != VT_PTR))
        l1 = ((uint32_t)l1 |
              (v1->type.t & VT_UNSIGNED ? 0 : -(l1 & 0x80000000)));
    if (t2 != VT_LLONG && (PTR_SIZE != 8 || t2 != VT_PTR))
        l2 = ((uint32_t)l2 |
              (v2->type.t & VT_UNSIGNED ? 0 : -(l2 & 0x80000000)));

    if (c1 && c2) {
        switch(op) {
        case '+': l1 += l2; break;
        case '-': l1 -= l2; break;
        case '&': l1 &= l2; break;
        case '^': l1 ^= l2; break;
        case '|': l1 |= l2; break;
        case '*': l1 *= l2; break;

        case TOK_PDIV:
        case '/':
        case '%':
        case TOK_UDIV:
        case TOK_UMOD:
            /* if division by zero, generate explicit division */
            if (l2 == 0) {
                if (const_wanted)
                    tcc_error("division by zero in constant");
                goto general_case;
            }
            switch(op) {
            default: l1 = gen_opic_sdiv(l1, l2); break;
            case '%': l1 = l1 - l2 * gen_opic_sdiv(l1, l2); break;
            case TOK_UDIV: l1 = l1 / l2; break;
            case TOK_UMOD: l1 = l1 % l2; break;
            }
            break;
        case TOK_SHL: l1 <<= (l2 & shm); break;
        case TOK_SHR: l1 >>= (l2 & shm); break;
        case TOK_SAR:
#if HAVE_LONG_LONG
            l1 = (l1 >> 63) ? ~(~l1 >> (l2 & shm)) : l1 >> (l2 & shm);
#else
            l1 = (l1 >> 31) ? ~(~l1 >> (l2 & shm)) : l1 >> (l2 & shm);
#endif
            break;
            /* tests */
        case TOK_ULT: l1 = l1 < l2; break;
        case TOK_UGE: l1 = l1 >= l2; break;
        case TOK_EQ: l1 = l1 == l2; break;
        case TOK_NE: l1 = l1 != l2; break;
        case TOK_ULE: l1 = l1 <= l2; break;
        case TOK_UGT: l1 = l1 > l2; break;
        case TOK_LT: l1 = gen_opic_lt(l1, l2); break;
        case TOK_GE: l1 = !gen_opic_lt(l1, l2); break;
        case TOK_LE: l1 = !gen_opic_lt(l2, l1); break;
        case TOK_GT: l1 = gen_opic_lt(l2, l1); break;
            /* logical */
        case TOK_LAND: l1 = l1 && l2; break;
        case TOK_LOR: l1 = l1 || l2; break;
        default:
            goto general_case;
        }
    if (t1 != VT_LLONG && (PTR_SIZE != 8 || t1 != VT_PTR))
        l1 = ((uint32_t)l1 |
        (v1->type.t & VT_UNSIGNED ? 0 : -(l1 & 0x80000000)));
        v1->c.i = l1;
        vtop--;
    } else {
        /* if commutative ops, put c2 as constant */
        if (c1 && (op == '+' || op == '&' || op == '^' || 
                   op == '|' || op == '*')) {
            vswap();
            c2 = c1; //c = c1, c1 = c2, c2 = c;
            l2 = l1; //l = l1, l1 = l2, l2 = l;
        }
        if (!const_wanted &&
            c1 && ((l1 == 0 &&
                    (op == TOK_SHL || op == TOK_SHR || op == TOK_SAR)) ||
                   (l1 == -1 && op == TOK_SAR))) {
            /* treat (0 << x), (0 >> x) and (-1 >> x) as constant */
            vtop--;
        } else if (!const_wanted &&
                   c2 && ((l2 == 0 && (op == '&' || op == '*')) ||
                          (l2 == -1 && op == '|') ||
                          (l2 == 0xffffffff && t2 != VT_LLONG && op == '|') ||
                          (l2 == 1 && (op == '%' || op == TOK_UMOD)))) {
            /* treat (x & 0), (x * 0), (x | -1) and (x % 1) as constant */
            if (l2 == 1)
                vtop->c.i = 0;
            vswap();
            vtop--;
        } else if (c2 && (((op == '*' || op == '/' || op == TOK_UDIV ||
                          op == TOK_PDIV) &&
                           l2 == 1) ||
                          ((op == '+' || op == '-' || op == '|' || op == '^' ||
                            op == TOK_SHL || op == TOK_SHR || op == TOK_SAR) &&
                           l2 == 0) ||
                          (op == '&' &&
                           l2 == -1))) {
            /* filter out NOP operations like x*1, x-0, x&-1... */
            vtop--;
        } else if (c2 && (op == '*' || op == TOK_PDIV || op == TOK_UDIV)) {
            /* try to use shifts instead of muls or divs */
            if (l2 > 0 && (l2 & (l2 - 1)) == 0) {
                int n = -1;
                while (l2) {
                    l2 >>= 1;
                    n++;
                }
                vtop->c.i = n;
                if (op == '*')
                    op = TOK_SHL;
                else if (op == TOK_PDIV)
                    op = TOK_SAR;
                else
                    op = TOK_SHR;
            }
            goto general_case;
        } else if (c2 && (op == '+' || op == '-') &&
                   (((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM))
                    || (vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_LOCAL)) {
            /* symbol + constant case */
            if (op == '-')
                l2 = -l2;
        l2 += vtop[-1].c.i;
        /* The backends can't always deal with addends to symbols
           larger than +-1<<31.  Don't construct such.  */
        if ((int)l2 != l2)
            goto general_case;
            vtop--;
            vtop->c.i = l2;
        } else {
        general_case:
                /* call low level op generator */
                if (t1 == VT_LLONG || t2 == VT_LLONG ||
                    (PTR_SIZE == 8 && (t1 == VT_PTR || t2 == VT_PTR)))
                    gen_opl(op);
                else
                    gen_opi(op);
        }
    }
}

/* generate a floating point operation with constant propagation */
static void gen_opif(int op)
{
    int c1, c2;
    SValue *v1, *v2;
    long double f1, f2;

    v1 = vtop - 1;
    v2 = vtop;
    /* currently, we cannot do computations with forward symbols */
    c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
    c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
    if (c1 && c2) {
        if (v1->type.t == VT_FLOAT) {
            f1 = v1->c.f;
            f2 = v2->c.f;
        } else if (v1->type.t == VT_DOUBLE) {
            f1 = v1->c.d;
            f2 = v2->c.d;
        } else {
            f1 = v1->c.ld;
            f2 = v2->c.ld;
        }

        /* NOTE: we only do constant propagation if finite number (not
           NaN or infinity) (ANSI spec) */
        if (!ieee_finite(f1) || !ieee_finite(f2))
            goto general_case;

        switch(op) {
        case '+': f1 += f2; break;
        case '-': f1 -= f2; break;
        case '*': f1 *= f2; break;
        case '/': 
#if HAVE_FLOAT
            if (f2 == 0.0) {
                if (const_wanted)
                    tcc_error("division by zero in constant");
                goto general_case;
            }
#endif
            f1 /= f2; 
            break;
            /* XXX: also handles tests ? */
        default:
            goto general_case;
        }
        /* XXX: overflow test ? */
        if (v1->type.t == VT_FLOAT) {
            v1->c.f = f1;
        } else if (v1->type.t == VT_DOUBLE) {
            v1->c.d = f1;
        } else {
            v1->c.ld = f1;
        }
        vtop--;
    } else {
    general_case:
        gen_opf(op);
    }
}

static int pointed_size(CType *type)
{
    int align;
    return type_size(pointed_type(type), &align);
}

static void vla_runtime_pointed_size(CType *type)
{
    int align;
    vla_runtime_type_size(pointed_type(type), &align);
}

static inline int is_null_pointer(SValue *p)
{
    if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
        return 0;
    return ((p->type.t & VT_BTYPE) == VT_INT && (uint32_t)p->c.i == 0) ||
        ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.i == 0) ||
        ((p->type.t & VT_BTYPE) == VT_PTR &&
         (PTR_SIZE == 4 ? (uint32_t)p->c.i == 0 : p->c.i == 0));
}

static inline int is_integer_btype(int bt)
{
    return (bt == VT_BYTE || bt == VT_SHORT || 
            bt == VT_INT || bt == VT_LLONG);
}

/* check types for comparison or subtraction of pointers */
static void check_comparison_pointer_types(SValue *p1, SValue *p2, int op)
{
    CType *type1, *type2, tmp_type1, tmp_type2;
    int bt1, bt2;
    
    /* null pointers are accepted for all comparisons as gcc */
    if (is_null_pointer(p1) || is_null_pointer(p2))
        return;
    type1 = &p1->type;
    type2 = &p2->type;
    bt1 = type1->t & VT_BTYPE;
    bt2 = type2->t & VT_BTYPE;
    /* accept comparison between pointer and integer with a warning */
    if ((is_integer_btype(bt1) || is_integer_btype(bt2)) && op != '-') {
        if (op != TOK_LOR && op != TOK_LAND )
            tcc_warning("comparison between pointer and integer");
        return;
    }

    /* both must be pointers or implicit function pointers */
    if (bt1 == VT_PTR) {
        type1 = pointed_type(type1);
    } else if (bt1 != VT_FUNC) 
        goto invalid_operands;

    if (bt2 == VT_PTR) {
        type2 = pointed_type(type2);
    } else if (bt2 != VT_FUNC) { 
    invalid_operands:
        tcc_error("invalid operands to binary %s", get_tok_str(op, NULL));
    }
    if ((type1->t & VT_BTYPE) == VT_VOID || 
        (type2->t & VT_BTYPE) == VT_VOID)
        return;
    tmp_type1 = *type1;
    tmp_type2 = *type2;
    tmp_type1.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
    tmp_type2.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
    if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
        /* gcc-like error if '-' is used */
        if (op == '-')
            goto invalid_operands;
        else
            tcc_warning("comparison of distinct pointer types lacks a cast");
    }
}

/* generic gen_op: handles types problems */
ST_FUNC void gen_op(int op)
{
    int u, t1, t2, bt1, bt2, t;
    CType type1;

redo:
    t1 = vtop[-1].type.t;
    t2 = vtop[0].type.t;
    bt1 = t1 & VT_BTYPE;
    bt2 = t2 & VT_BTYPE;
        
    if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
        tcc_error("operation on a struct");
    } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) {
    if (bt2 == VT_FUNC) {
        mk_pointer(&vtop->type);
        gaddrof();
    }
    if (bt1 == VT_FUNC) {
        vswap();
        mk_pointer(&vtop->type);
        gaddrof();
        vswap();
    }
    goto redo;
    } else if (bt1 == VT_PTR || bt2 == VT_PTR) {
        /* at least one operand is a pointer */
        /* relational op: must be both pointers */
        if (op >= TOK_ULT && op <= TOK_LOR) {
            check_comparison_pointer_types(vtop - 1, vtop, op);
            /* pointers are handled are unsigned */
#if PTR_SIZE == 8
            t = VT_LLONG | VT_UNSIGNED;
#else
            t = VT_INT | VT_UNSIGNED;
#endif
            goto std_op;
        }
        /* if both pointers, then it must be the '-' op */
        if (bt1 == VT_PTR && bt2 == VT_PTR) {
            if (op != '-')
                tcc_error("cannot use pointers here");
            check_comparison_pointer_types(vtop - 1, vtop, op);
            /* XXX: check that types are compatible */
            if (vtop[-1].type.t & VT_VLA) {
                vla_runtime_pointed_size(&vtop[-1].type);
            } else {
                vpushi(pointed_size(&vtop[-1].type));
            }
            vrott(3);
            gen_opic(op);
            /* set to integer type */
#if PTR_SIZE == 8
            vtop->type.t = VT_LLONG;
#else
            vtop->type.t = VT_INT; 
#endif
            vswap();
            gen_op(TOK_PDIV);
        } else {
            /* exactly one pointer : must be '+' or '-'. */
            if (op != '-' && op != '+')
                tcc_error("cannot use pointers here");
            /* Put pointer as first operand */
            if (bt2 == VT_PTR) {
                vswap();
                t = t1, t1 = t2, t2 = t;
            }
#if PTR_SIZE == 4
            if ((vtop[0].type.t & VT_BTYPE) == VT_LLONG)
                /* XXX: truncate here because gen_opl can't handle ptr + long long */
                gen_cast(&int_type);
#endif
            type1 = vtop[-1].type;
            type1.t &= ~VT_ARRAY;
            if (vtop[-1].type.t & VT_VLA)
                vla_runtime_pointed_size(&vtop[-1].type);
            else {
                u = pointed_size(&vtop[-1].type);
                if (u < 0)
                    tcc_error("unknown array element size");
#if PTR_SIZE == 8
                vpushll(u);
#else
                /* XXX: cast to int ? (long long case) */
                vpushi(u);
#endif
            }
            gen_op('*');
#if 0
/* #ifdef CONFIG_TCC_BCHECK
    The main reason to removing this code:
    #include <stdio.h>
    int main ()
    {
        int v[10];
        int i = 10;
        int j = 9;
        fprintf(stderr, "v+i-j  = %p\n", v+i-j);
        fprintf(stderr, "v+(i-j)  = %p\n", v+(i-j));
    }
    When this code is on. then the output looks like 
    v+i-j = 0xfffffffe
    v+(i-j) = 0xbff84000
    */
            /* if evaluating constant expression, no code should be
               generated, so no bound check */
            if (tcc_state->do_bounds_check && !const_wanted) {
                /* if bounded pointers, we generate a special code to
                   test bounds */
                if (op == '-') {
                    vpushi(0);
                    vswap();
                    gen_op('-');
                }
                gen_bounded_ptr_add();
            } else
#endif
            {
                gen_opic(op);
            }
            /* put again type if gen_opic() swaped operands */
            vtop->type = type1;
        }
    } else if (is_float(bt1) || is_float(bt2)) {
        /* compute bigger type and do implicit casts */
        if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
            t = VT_LDOUBLE;
        } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
            t = VT_DOUBLE;
        } else {
            t = VT_FLOAT;
        }
        /* floats can only be used for a few operations */
        if (op != '+' && op != '-' && op != '*' && op != '/' &&
            (op < TOK_ULT || op > TOK_GT))
            tcc_error("invalid operands for binary operation");
        goto std_op;
    } else if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL) {
        t = bt1 == VT_LLONG ? VT_LLONG : VT_INT;
        if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (t | VT_UNSIGNED))
          t |= VT_UNSIGNED;
        goto std_op;
    } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
        /* cast to biggest op */
        t = VT_LLONG;
        /* convert to unsigned if it does not fit in a long long */
        if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED) ||
            (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED))
            t |= VT_UNSIGNED;
        goto std_op;
    } else {
        /* integer operations */
        t = VT_INT;
        /* convert to unsigned if it does not fit in an integer */
        if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED) ||
            (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED))
            t |= VT_UNSIGNED;
    std_op:
        /* XXX: currently, some unsigned operations are explicit, so
           we modify them here */
        if (t & VT_UNSIGNED) {
            if (op == TOK_SAR)
                op = TOK_SHR;
            else if (op == '/')
                op = TOK_UDIV;
            else if (op == '%')
                op = TOK_UMOD;
            else if (op == TOK_LT)
                op = TOK_ULT;
            else if (op == TOK_GT)
                op = TOK_UGT;
            else if (op == TOK_LE)
                op = TOK_ULE;
            else if (op == TOK_GE)
                op = TOK_UGE;
        }
        vswap();
        type1.t = t;
        gen_cast(&type1);
        vswap();
        /* special case for shifts and long long: we keep the shift as
           an integer */
        if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL)
            type1.t = VT_INT;
        gen_cast(&type1);
        if (is_float(t))
            gen_opif(op);
        else
            gen_opic(op);
        if (op >= TOK_ULT && op <= TOK_GT) {
            /* relational op: the result is an int */
            vtop->type.t = VT_INT;
        } else {
            vtop->type.t = t;
        }
    }
    // Make sure that we have converted to an rvalue:
    if (vtop->r & VT_LVAL)
        gv(is_float(vtop->type.t & VT_BTYPE) ? RC_FLOAT : RC_INT);
}

#ifndef TCC_TARGET_ARM
/* generic itof for unsigned long long case */
static void gen_cvt_itof1(int t)
{
#ifdef TCC_TARGET_ARM64
    gen_cvt_itof(t);
#else
    if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == 
        (VT_LLONG | VT_UNSIGNED)) {

        if (t == VT_FLOAT)
            vpush_global_sym(&func_old_type, TOK___floatundisf);
#if LDOUBLE_SIZE != 8
        else if (t == VT_LDOUBLE)
            vpush_global_sym(&func_old_type, TOK___floatundixf);
#endif
        else
            vpush_global_sym(&func_old_type, TOK___floatundidf);
        vrott(2);
        gfunc_call(1);
        vpushi(0);
        vtop->r = reg_fret(t);
    } else {
        gen_cvt_itof(t);
    }
#endif
}
#endif

/* generic ftoi for unsigned long long case */
static void gen_cvt_ftoi1(int t)
{
#ifdef TCC_TARGET_ARM64
    gen_cvt_ftoi(t);
#else
    int st;

    if (t == (VT_LLONG | VT_UNSIGNED)) {
        /* not handled natively */
        st = vtop->type.t & VT_BTYPE;
        if (st == VT_FLOAT)
            vpush_global_sym(&func_old_type, TOK___fixunssfdi);
#if LDOUBLE_SIZE != 8
        else if (st == VT_LDOUBLE)
            vpush_global_sym(&func_old_type, TOK___fixunsxfdi);
#endif
        else
            vpush_global_sym(&func_old_type, TOK___fixunsdfdi);
        vrott(2);
        gfunc_call(1);
        vpushi(0);
        vtop->r = REG_IRET;
        vtop->r2 = REG_LRET;
    } else {
        gen_cvt_ftoi(t);
    }
#endif
}

/* force char or short cast */
static void force_charshort_cast(int t)
{
    int bits, dbt;
    dbt = t & VT_BTYPE;
    /* XXX: add optimization if lvalue : just change type and offset */
    if (dbt == VT_BYTE)
        bits = 8;
    else
        bits = 16;
    if (t & VT_UNSIGNED) {
        vpushi((1 << bits) - 1);
        gen_op('&');
    } else {
        if ((vtop->type.t & VT_BTYPE) == VT_LLONG)
            bits = 64 - bits;
        else
            bits = 32 - bits;
        vpushi(bits);
        gen_op(TOK_SHL);
        /* result must be signed or the SAR is converted to an SHL
           This was not the case when "t" was a signed short
           and the last value on the stack was an unsigned int */
        vtop->type.t &= ~VT_UNSIGNED;
        vpushi(bits);
        gen_op(TOK_SAR);
    }
}

/* cast 'vtop' to 'type'. Casting to bitfields is forbidden. */
static void gen_cast(CType *type)
{
    int sbt, dbt, sf, df, c, p;

    /* special delayed cast for char/short */
    /* XXX: in some cases (multiple cascaded casts), it may still
       be incorrect */
    if (vtop->r & VT_MUSTCAST) {
        vtop->r &= ~VT_MUSTCAST;
        force_charshort_cast(vtop->type.t);
    }

    /* bitfields first get cast to ints */
    if (vtop->type.t & VT_BITFIELD) {
        gv(RC_INT);
    }

    dbt = type->t & (VT_BTYPE | VT_UNSIGNED);
    sbt = vtop->type.t & (VT_BTYPE | VT_UNSIGNED);

    if (sbt != dbt) {
        sf = is_float(sbt);
        df = is_float(dbt);
        c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
        p = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM);
        if (c) {
            /* constant case: we can do it now */
            /* XXX: in ISOC, cannot do it if error in convert */
#if HAVE_FLOAT
            if (sbt == VT_FLOAT)
                vtop->c.ld = vtop->c.f;
            else if (sbt == VT_DOUBLE)
                vtop->c.ld = vtop->c.d;

            if (df) {
                if ((sbt & VT_BTYPE) == VT_LLONG) {
                    if ((sbt & VT_UNSIGNED) || !(vtop->c.i >> 63))
                        vtop->c.ld = vtop->c.i;
                    else
                        vtop->c.ld = -(long double)-vtop->c.i;
                } else if(!sf) {
                    if ((sbt & VT_UNSIGNED) || !(vtop->c.i >> 31))
                        vtop->c.ld = (uint32_t)vtop->c.i;
                    else
                        vtop->c.ld = -(long double)-(uint32_t)vtop->c.i;
                }

                if (dbt == VT_FLOAT)
                    vtop->c.f = (float)vtop->c.ld;
                else if (dbt == VT_DOUBLE)
                    vtop->c.d = (double)vtop->c.ld;
            }
              else if (sf && dbt == (VT_LLONG|VT_UNSIGNED)) {
                vtop->c.i = vtop->c.ld;
            } else if (sf && dbt == VT_BOOL) {
                vtop->c.i = (vtop->c.ld != 0);
            } else
#endif
                   {
#if HAVE_FLOAT
                if(sf)
                    vtop->c.i = vtop->c.ld;
                else
#endif
                  if (sbt == (VT_LLONG|VT_UNSIGNED))
                    ;
                else if (sbt & VT_UNSIGNED)
                    vtop->c.i = (uint32_t)vtop->c.i;
#if PTR_SIZE == 8
                else if (sbt == VT_PTR)
                    ;
#endif
                else if (sbt != VT_LLONG)
                    vtop->c.i = ((uint32_t)vtop->c.i |
                                  -(vtop->c.i & 0x80000000));

                if (dbt == (VT_LLONG|VT_UNSIGNED))
                    ;
                else if (dbt == VT_BOOL)
                    vtop->c.i = (vtop->c.i != 0);
#if PTR_SIZE == 8
                else if (dbt == VT_PTR)
                    ;
#endif
                else if (dbt != VT_LLONG) {
                    uint32_t m = ((dbt & VT_BTYPE) == VT_BYTE ? 0xff :
                                  (dbt & VT_BTYPE) == VT_SHORT ? 0xffff :
                                  0xffffffff);
                    vtop->c.i &= m;
                    if (!(dbt & VT_UNSIGNED))
                        vtop->c.i |= -(vtop->c.i & ((m >> 1) + 1));
                }
            }
        } else if (p && dbt == VT_BOOL) {
            vtop->r = VT_CONST;
            vtop->c.i = 1;
        } else {
            /* non constant case: generate code */
            if (sf && df) {
                /* convert from fp to fp */
                gen_cvt_ftof(dbt);
            } else if (df) {
                /* convert int to fp */
                gen_cvt_itof1(dbt);
            } else if (sf) {
                /* convert fp to int */
                if (dbt == VT_BOOL) {
                     vpushi(0);
                     gen_op(TOK_NE);
                } else {
                    /* we handle char/short/etc... with generic code */
                    if (dbt != (VT_INT | VT_UNSIGNED) &&
                        dbt != (VT_LLONG | VT_UNSIGNED) &&
                        dbt != VT_LLONG)
                        dbt = VT_INT;
                    gen_cvt_ftoi1(dbt);
                    if (dbt == VT_INT && (type->t & (VT_BTYPE | VT_UNSIGNED)) != dbt) {
                        /* additional cast for char/short... */
                        vtop->type.t = dbt;
                        gen_cast(type);
                    }
                }
#if PTR_SIZE == 4
            } else if ((dbt & VT_BTYPE) == VT_LLONG) {
                if ((sbt & VT_BTYPE) != VT_LLONG) {
                    /* scalar to long long */
                    /* machine independent conversion */
                    gv(RC_INT);
                    /* generate high word */
                    if (sbt == (VT_INT | VT_UNSIGNED)) {
                        vpushi(0);
                        gv(RC_INT);
                    } else {
                        if (sbt == VT_PTR) {
                            /* cast from pointer to int before we apply
                               shift operation, which pointers don't support*/
                            gen_cast(&int_type);
                        }
                        gv_dup();
                        vpushi(31);
                        gen_op(TOK_SAR);
                    }
                    /* patch second register */
                    vtop[-1].r2 = vtop->r;
                    vpop();
                }
#else
            } else if ((dbt & VT_BTYPE) == VT_LLONG ||
                       (dbt & VT_BTYPE) == VT_PTR ||
                       (dbt & VT_BTYPE) == VT_FUNC) {
                if ((sbt & VT_BTYPE) != VT_LLONG &&
                    (sbt & VT_BTYPE) != VT_PTR &&
                    (sbt & VT_BTYPE) != VT_FUNC) {
                    /* need to convert from 32bit to 64bit */
                    gv(RC_INT);
                    if (sbt != (VT_INT | VT_UNSIGNED)) {
#if defined(TCC_TARGET_ARM64)
                        gen_cvt_sxtw();
#elif defined(TCC_TARGET_X86_64)
                        int r = gv(RC_INT);
                        /* x86_64 specific: movslq */
                        o(0x6348);
                        o(0xc0 + (REG_VALUE(r) << 3) + REG_VALUE(r));
#else
#error
#endif
                    }
                }
#endif
            } else if (dbt == VT_BOOL) {
                /* scalar to bool */
                vpushi(0);
                gen_op(TOK_NE);
            } else if ((dbt & VT_BTYPE) == VT_BYTE || 
                       (dbt & VT_BTYPE) == VT_SHORT) {
                if (sbt == VT_PTR) {
                    vtop->type.t = VT_INT;
                    tcc_warning("nonportable conversion from pointer to char/short");
                }
                force_charshort_cast(dbt);
#if PTR_SIZE == 4
            } else if ((dbt & VT_BTYPE) == VT_INT) {
                /* scalar to int */
                if ((sbt & VT_BTYPE) == VT_LLONG) {
                    /* from long long: just take low order word */
                    lexpand();
                    vpop();
                } 
                /* if lvalue and single word type, nothing to do because
                   the lvalue already contains the real type size (see
                   VT_LVAL_xxx constants) */
#endif
            }
        }
    } else if ((dbt & VT_BTYPE) == VT_PTR && !(vtop->r & VT_LVAL)) {
        /* if we are casting between pointer types,
           we must update the VT_LVAL_xxx size */
        vtop->r = (vtop->r & ~VT_LVAL_TYPE)
                  | (lvalue_type(type->ref->type.t) & VT_LVAL_TYPE);
    }
    vtop->type = *type;
}

/* return type size as known at compile time. Put alignment at 'a' */
ST_FUNC int type_size(CType *type, int *a)
{
    Sym *s;
    int bt;

    bt = type->t & VT_BTYPE;
    if (bt == VT_STRUCT) {
        /* struct/union */
        s = type->ref;
        *a = s->r;
        return s->c;
    } else if (bt == VT_PTR) {
        if (type->t & VT_ARRAY) {
            int ts;

            s = type->ref;
            ts = type_size(&s->type, a);

            if (ts < 0 && s->c < 0)
                ts = -ts;

            return ts * s->c;
        } else {
            *a = PTR_SIZE;
            return PTR_SIZE;
        }
    } else if (bt == VT_LDOUBLE) {
        *a = LDOUBLE_ALIGN;
        return LDOUBLE_SIZE;
    } else if (bt == VT_DOUBLE || bt == VT_LLONG) {
#ifdef TCC_TARGET_I386
#ifdef TCC_TARGET_PE
        *a = 8;
#else
        *a = 4;
#endif
#elif defined(TCC_TARGET_ARM)
#ifdef TCC_ARM_EABI
        *a = 8; 
#else
        *a = 4;
#endif
#else
        *a = 8;
#endif
        return 8;
    } else if (bt == VT_INT || bt == VT_FLOAT) {
        *a = 4;
        return 4;
    } else if (bt == VT_SHORT) {
        *a = 2;
        return 2;
    } else if (bt == VT_QLONG || bt == VT_QFLOAT) {
        *a = 8;
        return 16;
    } else if (bt == VT_ENUM) {
    *a = 4;
    /* Enums might be incomplete, so don't just return '4' here.  */
    return type->ref->c;
    } else {
        /* char, void, function, _Bool */
        *a = 1;
        return 1;
    }
}

/* push type size as known at runtime time on top of value stack. Put
   alignment at 'a' */
ST_FUNC void vla_runtime_type_size(CType *type, int *a)
{
    if (type->t & VT_VLA) {
        type_size(&type->ref->type, a);
        vset(&int_type, VT_LOCAL|VT_LVAL, type->ref->c);
    } else {
        vpushi(type_size(type, a));
    }
}

static void vla_sp_restore(void) {
    if (vlas_in_scope) {
        gen_vla_sp_restore(vla_sp_loc);
    }
}

static void vla_sp_restore_root(void) {
    if (vlas_in_scope) {
        gen_vla_sp_restore(vla_sp_root_loc);
    }
}

/* return the pointed type of t */
static inline CType *pointed_type(CType *type)
{
    return &type->ref->type;
}

/* modify type so that its it is a pointer to type. */
ST_FUNC void mk_pointer(CType *type)
{
    Sym *s;
    s = sym_push(SYM_FIELD, type, 0, -1);
    type->t = VT_PTR | (type->t & ~VT_TYPE);
    type->ref = s;
}

/* compare function types. OLD functions match any new functions */
static int is_compatible_func(CType *type1, CType *type2)
{
    Sym *s1, *s2;

    s1 = type1->ref;
    s2 = type2->ref;
    if (!is_compatible_types(&s1->type, &s2->type))
        return 0;
    /* check func_call */
    if (s1->a.func_call != s2->a.func_call)
        return 0;
    /* XXX: not complete */
    if (s1->c == FUNC_OLD || s2->c == FUNC_OLD)
        return 1;
    if (s1->c != s2->c)
        return 0;
    while (s1 != NULL) {
        if (s2 == NULL)
            return 0;
        if (!is_compatible_parameter_types(&s1->type, &s2->type))
            return 0;
        s1 = s1->next;
        s2 = s2->next;
    }
    if (s2)
        return 0;
    return 1;
}

/* return true if type1 and type2 are the same.  If unqualified is
   true, qualifiers on the types are ignored.

   - enums are not checked as gcc __builtin_types_compatible_p () 
 */
static int compare_types(CType *type1, CType *type2, int unqualified)
{
    int bt1, t1, t2;

    t1 = type1->t & VT_TYPE;
    t2 = type2->t & VT_TYPE;
    if (unqualified) {
        /* strip qualifiers before comparing */
        t1 &= ~(VT_CONSTANT | VT_VOLATILE);
        t2 &= ~(VT_CONSTANT | VT_VOLATILE);
    }
    /* Default Vs explicit signedness only matters for char */
    if ((t1 & VT_BTYPE) != VT_BYTE) {
        t1 &= ~VT_DEFSIGN;
        t2 &= ~VT_DEFSIGN;
    }
    /* An enum is compatible with (unsigned) int.  Ideally we would
       store the enums signedness in type->ref.a.<some_bit> and
       only accept unsigned enums with unsigned int and vice versa.
       But one of our callers (gen_assign_cast) always strips VT_UNSIGNED
       from pointer target types, so we can't add it here either.  */
    if ((t1 & VT_BTYPE) == VT_ENUM) {
    t1 = VT_INT;
    if (type1->ref->a.unsigned_enum)
        t1 |= VT_UNSIGNED;
    }
    if ((t2 & VT_BTYPE) == VT_ENUM) {
    t2 = VT_INT;
    if (type2->ref->a.unsigned_enum)
        t2 |= VT_UNSIGNED;
    }
    /* XXX: bitfields ? */
    if (t1 != t2)
        return 0;
    /* test more complicated cases */
    bt1 = t1 & VT_BTYPE;
    if (bt1 == VT_PTR) {
        type1 = pointed_type(type1);
        type2 = pointed_type(type2);
        return is_compatible_types(type1, type2);
    } else if (bt1 == VT_STRUCT) {
        return (type1->ref == type2->ref);
    } else if (bt1 == VT_FUNC) {
        return is_compatible_func(type1, type2);
    } else {
        return 1;
    }
}

/* return true if type1 and type2 are exactly the same (including
   qualifiers). 
*/
static int is_compatible_types(CType *type1, CType *type2)
{
    return compare_types(type1,type2,0);
}

/* return true if type1 and type2 are the same (ignoring qualifiers).
*/
static int is_compatible_parameter_types(CType *type1, CType *type2)
{
    return compare_types(type1,type2,1);
}

/* print a type. If 'varstr' is not NULL, then the variable is also
   printed in the type */
/* XXX: union */
/* XXX: add array and function pointers */
static void type_to_str(char *buf, int buf_size, 
                 CType *type, const char *varstr)
{
    int bt, v, t;
    Sym *s, *sa;
    char buf1[256];
    const char *tstr;

    t = type->t;
    bt = t & VT_BTYPE;
    buf[0] = '\0';
    if (t & VT_CONSTANT)
        pstrcat(buf, buf_size, "const ");
    if (t & VT_VOLATILE)
        pstrcat(buf, buf_size, "volatile ");
    if ((t & (VT_DEFSIGN | VT_UNSIGNED)) == (VT_DEFSIGN | VT_UNSIGNED))
        pstrcat(buf, buf_size, "unsigned ");
    else if (t & VT_DEFSIGN)
        pstrcat(buf, buf_size, "signed ");
    if (t & VT_EXTERN)
        pstrcat(buf, buf_size, "extern ");
    if (t & VT_STATIC)
        pstrcat(buf, buf_size, "static ");
    if (t & VT_TYPEDEF)
        pstrcat(buf, buf_size, "typedef ");
    if (t & VT_INLINE)
        pstrcat(buf, buf_size, "inline ");
    buf_size -= strlen(buf);
    buf += strlen(buf);
    switch(bt) {
    case VT_VOID:
        tstr = "void";
        goto add_tstr;
    case VT_BOOL:
        tstr = "_Bool";
        goto add_tstr;
    case VT_BYTE:
        tstr = "char";
        goto add_tstr;
    case VT_SHORT:
        tstr = "short";
        goto add_tstr;
    case VT_INT:
        tstr = "int";
        goto add_tstr;
    case VT_LONG:
        tstr = "long";
        goto add_tstr;
    case VT_LLONG:
        tstr = "long long";
        goto add_tstr;
    case VT_FLOAT:
        tstr = "float";
        goto add_tstr;
    case VT_DOUBLE:
        tstr = "double";
        goto add_tstr;
    case VT_LDOUBLE:
        tstr = "long double";
    add_tstr:
        pstrcat(buf, buf_size, tstr);
        break;
    case VT_ENUM:
    case VT_STRUCT:
        if (bt == VT_STRUCT)
            tstr = "struct ";
        else
            tstr = "enum ";
        pstrcat(buf, buf_size, tstr);
        v = type->ref->v & ~SYM_STRUCT;
        if (v >= SYM_FIRST_ANOM)
            pstrcat(buf, buf_size, "<anonymous>");
        else
            pstrcat(buf, buf_size, get_tok_str(v, NULL));
        break;
    case VT_FUNC:
        s = type->ref;
        type_to_str(buf, buf_size, &s->type, varstr);
        pstrcat(buf, buf_size, "(");
        sa = s->next;
        while (sa != NULL) {
            type_to_str(buf1, sizeof(buf1), &sa->type, NULL);
            pstrcat(buf, buf_size, buf1);
            sa = sa->next;
            if (sa)
                pstrcat(buf, buf_size, ", ");
        }
        pstrcat(buf, buf_size, ")");
        goto no_var;
    case VT_PTR:
        s = type->ref;
        if (t & VT_ARRAY) {
            snprintf(buf1, sizeof(buf1), "%s[%ld]", varstr ? varstr : "", s->c);
            type_to_str(buf, buf_size, &s->type, buf1);
            goto no_var;
        }
        pstrcpy(buf1, sizeof(buf1), "*");
        if (t & VT_CONSTANT)
            pstrcat(buf1, buf_size, "const ");
        if (t & VT_VOLATILE)
            pstrcat(buf1, buf_size, "volatile ");
        if (varstr)
            pstrcat(buf1, sizeof(buf1), varstr);
        type_to_str(buf, buf_size, &s->type, buf1);
        goto no_var;
    }
    if (varstr) {
        pstrcat(buf, buf_size, " ");
        pstrcat(buf, buf_size, varstr);
    }
 no_var: ;
}

/* verify type compatibility to store vtop in 'dt' type, and generate
   casts if needed. */
static void gen_assign_cast(CType *dt)
{
    CType *st, *type1, *type2, tmp_type1, tmp_type2;
    char buf1[256], buf2[256];
    int dbt, sbt;

    st = &vtop->type; /* source type */
    dbt = dt->t & VT_BTYPE;
    sbt = st->t & VT_BTYPE;
    if (sbt == VT_VOID || dbt == VT_VOID) {
    if (sbt == VT_VOID && dbt == VT_VOID)
        ; /*
          It is Ok if both are void
          A test program:
            void func1() {}
        void func2() {
          return func1();
        }
          gcc accepts this program
          */
    else
            tcc_error("cannot cast from/to void");
    }
    if (dt->t & VT_CONSTANT)
        tcc_warning("assignment of read-only location");
    switch(dbt) {
    case VT_PTR:
        /* special cases for pointers */
        /* '0' can also be a pointer */
        if (is_null_pointer(vtop))
            goto type_ok;
        /* accept implicit pointer to integer cast with warning */
        if (is_integer_btype(sbt)) {
            tcc_warning("assignment makes pointer from integer without a cast");
            goto type_ok;
        }
        type1 = pointed_type(dt);
        /* a function is implicitly a function pointer */
        if (sbt == VT_FUNC) {
            if ((type1->t & VT_BTYPE) != VT_VOID &&
                !is_compatible_types(pointed_type(dt), st))
                tcc_warning("assignment from incompatible pointer type");
            goto type_ok;
        }
        if (sbt != VT_PTR)
            goto error;
        type2 = pointed_type(st);
        if ((type1->t & VT_BTYPE) == VT_VOID || 
            (type2->t & VT_BTYPE) == VT_VOID) {
            /* void * can match anything */
        } else {
            /* exact type match, except for qualifiers */
            tmp_type1 = *type1;
            tmp_type2 = *type2;
            tmp_type1.t &= ~(VT_CONSTANT | VT_VOLATILE);
            tmp_type2.t &= ~(VT_CONSTANT | VT_VOLATILE);
            if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
        /* Like GCC don't warn by default for merely changes
           in pointer target signedness.  Do warn for different
           base types, though, in particular for unsigned enums
           and signed int targets.  */
        if ((tmp_type1.t & (VT_DEFSIGN | VT_UNSIGNED)) !=
            (tmp_type2.t & (VT_DEFSIGN | VT_UNSIGNED)) &&
            (tmp_type1.t & VT_BTYPE) == (tmp_type2.t & VT_BTYPE))
            ;
        else
            tcc_warning("assignment from incompatible pointer type");
        }
        }
        /* check const and volatile */
        if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) ||
            (!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE)))
            tcc_warning("assignment discards qualifiers from pointer target type");
        break;
    case VT_BYTE:
    case VT_SHORT:
    case VT_INT:
    case VT_LLONG:
        if (sbt == VT_PTR || sbt == VT_FUNC) {
            tcc_warning("assignment makes integer from pointer without a cast");
        } else if (sbt == VT_STRUCT) {
            goto case_VT_STRUCT;
        }
        /* XXX: more tests */
        break;
    case VT_STRUCT:
    case_VT_STRUCT:
        tmp_type1 = *dt;
        tmp_type2 = *st;
        tmp_type1.t &= ~(VT_CONSTANT | VT_VOLATILE);
        tmp_type2.t &= ~(VT_CONSTANT | VT_VOLATILE);
        if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
        error:
            type_to_str(buf1, sizeof(buf1), st, NULL);
            type_to_str(buf2, sizeof(buf2), dt, NULL);
            tcc_error("cannot cast '%s' to '%s'", buf1, buf2);
        }
        break;
    }
 type_ok:
    gen_cast(dt);
}

/* store vtop in lvalue pushed on stack */
ST_FUNC void vstore(void)
{
    int sbt, dbt, ft, r, t, size, align, bit_size, bit_pos, rc, delayed_cast;

    ft = vtop[-1].type.t;
    sbt = vtop->type.t & VT_BTYPE;
    dbt = ft & VT_BTYPE;
    if ((((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) ||
         (sbt == VT_INT && dbt == VT_SHORT))
    && !(vtop->type.t & VT_BITFIELD)) {
        /* optimize char/short casts */
        delayed_cast = VT_MUSTCAST;
        vtop->type.t = (ft & VT_TYPE & ~VT_BITFIELD &
                        ((1 << VT_STRUCT_SHIFT) - 1));
        /* XXX: factorize */
        if (ft & VT_CONSTANT)
            tcc_warning("assignment of read-only location");
    } else {
        delayed_cast = 0;
        if (!(ft & VT_BITFIELD))
            gen_assign_cast(&vtop[-1].type);
    }

    if (sbt == VT_STRUCT) {
        /* if structure, only generate pointer */
        /* structure assignment : generate memcpy */
        /* XXX: optimize if small size */
            size = type_size(&vtop->type, &align);

            /* destination */
            vswap();
            vtop->type.t = VT_PTR;
            gaddrof();

            /* address of memcpy() */
#ifdef TCC_ARM_EABI
            if(!(align & 7))
                vpush_global_sym(&func_old_type, TOK_memcpy8);
            else if(!(align & 3))
                vpush_global_sym(&func_old_type, TOK_memcpy4);
            else
#endif
            /* Use memmove, rather than memcpy, as dest and src may be same: */
#if BOOTSTRAP && __arm__
            vpush_global_sym(&func_old_type, TOK___memmove);
#else
            vpush_global_sym(&func_old_type, TOK_memmove);
#endif

            vswap();
            /* source */
            vpushv(vtop - 2);
            vtop->type.t = VT_PTR;
            gaddrof();
            /* type size */
            vpushi(size);
            gfunc_call(3);

        /* leave source on stack */
    } else if (ft & VT_BITFIELD) {
        /* bitfield store handling */

        /* save lvalue as expression result (example: s.b = s.a = n;) */
        vdup(), vtop[-1] = vtop[-2];

        bit_pos = (ft >> VT_STRUCT_SHIFT) & 0x3f;
        bit_size = (ft >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
        /* remove bit field info to avoid loops */
        vtop[-1].type.t = ft & ~VT_BITFIELD & ((1 << VT_STRUCT_SHIFT) - 1);

        if((ft & VT_BTYPE) == VT_BOOL) {
            gen_cast(&vtop[-1].type);
            vtop[-1].type.t = (vtop[-1].type.t & ~VT_BTYPE) | (VT_BYTE | VT_UNSIGNED);
        }

        /* duplicate destination */
        vdup();
        vtop[-1] = vtop[-2];

        /* mask and shift source */
        if((ft & VT_BTYPE) != VT_BOOL) {
            if((ft & VT_BTYPE) == VT_LLONG) {
                vpushll((1ULL << bit_size) - 1ULL);
            } else {
                vpushi((1 << bit_size) - 1);
            }
            gen_op('&');
        }
        vpushi(bit_pos);
        gen_op(TOK_SHL);
        /* load destination, mask and or with source */
        vswap();
        if((ft & VT_BTYPE) == VT_LLONG) {
            vpushll(~(((1ULL << bit_size) - 1ULL) << bit_pos));
        } else {
            vpushi(~(((1 << bit_size) - 1) << bit_pos));
        }
        gen_op('&');
        gen_op('|');
        /* store result */
        vstore();
        /* ... and discard */
        vpop();

    } else {
#ifdef CONFIG_TCC_BCHECK
            /* bound check case */
            if (vtop[-1].r & VT_MUSTBOUND) {
                vswap();
                gbound();
                vswap();
            }
#endif
            rc = RC_INT;
            if (is_float(ft)) {
                rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64
                if ((ft & VT_BTYPE) == VT_LDOUBLE) {
                    rc = RC_ST0;
                } else if ((ft & VT_BTYPE) == VT_QFLOAT) {
                    rc = RC_FRET;
                }
#endif
            }
            r = gv(rc);  /* generate value */
            /* if lvalue was saved on stack, must read it */
            if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) {
                SValue sv;
                t = get_reg(RC_INT);
#if PTR_SIZE == 8
                sv.type.t = VT_PTR;
#else
                sv.type.t = VT_INT;
#endif
                sv.r = VT_LOCAL | VT_LVAL;
                sv.c.i = vtop[-1].c.i;
                load(t, &sv);
                vtop[-1].r = t | VT_LVAL;
            }
            /* two word case handling : store second register at word + 4 (or +8 for x86-64)  */
#if PTR_SIZE == 8
            if (((ft & VT_BTYPE) == VT_QLONG) || ((ft & VT_BTYPE) == VT_QFLOAT)) {
                int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
#else
            if ((ft & VT_BTYPE) == VT_LLONG) {
                int addr_type = VT_INT, load_size = 4, load_type = VT_INT;
#endif
                vtop[-1].type.t = load_type;
                store(r, vtop - 1);
                vswap();
                /* convert to int to increment easily */
                vtop->type.t = addr_type;
                gaddrof();
                vpushi(load_size);
                gen_op('+');
                vtop->r |= VT_LVAL;
                vswap();
                vtop[-1].type.t = load_type;
                /* XXX: it works because r2 is spilled last ! */
                store(vtop->r2, vtop - 1);
            } else {
                store(r, vtop - 1);
            }

        vswap();
        vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
        vtop->r |= delayed_cast;
    }
}

/* post defines POST/PRE add. c is the token ++ or -- */
ST_FUNC void inc(int post, int c)
{
    test_lvalue();
    vdup(); /* save lvalue */
    if (post) {
        gv_dup(); /* duplicate value */
        vrotb(3);
        vrotb(3);
    }
    /* add constant */
    vpushi(c - TOK_MID); 
    gen_op('+');
    vstore(); /* store value */
    if (post)
        vpop(); /* if post op, return saved value */
}

ST_FUNC void parse_mult_str (CString *astr, const char *msg)
{
    /* read the string */
    if (tok != TOK_STR)
        expect(msg);
    cstr_new(astr);
    while (tok == TOK_STR) {
        /* XXX: add \0 handling too ? */
        cstr_cat(astr, tokc.str.data, -1);
        next();
    }
    cstr_ccat(astr, '\0');
}

/* If I is >= 1 and a power of two, returns log2(i)+1.
   If I is 0 returns 0.  */
static int exact_log2p1(int i)
{
  int ret;
  if (!i)
    return 0;
  for (ret = 1; i >= 1 << 8; ret += 8)
    i >>= 8;
  if (i >= 1 << 4)
    ret += 4, i >>= 4;
  if (i >= 1 << 2)
    ret += 2, i >>= 2;
  if (i >= 1 << 1)
    ret++;
  return ret;
}

/* Parse GNUC __attribute__ extension. Currently, the following
   extensions are recognized:
   - aligned(n) : set data/function alignment.
   - packed : force data alignment to 1
   - section(x) : generate data/code in this section.
   - unused : currently ignored, but may be used someday.
   - regparm(n) : pass function parameters in registers (i386 only)
 */
static void parse_attribute(AttributeDef *ad)
{
    int t, n;
    CString astr;
    
    while (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) {
    next();
    skip('(');
    skip('(');
    while (tok != ')') {
        if (tok < TOK_IDENT)
            expect("attribute name");
        t = tok;
        next();
        switch(t) {
        case TOK_SECTION1:
        case TOK_SECTION2:
            skip('(');
        parse_mult_str(&astr, "section name");
            ad->section = find_section(tcc_state, (char *)astr.data);
            skip(')');
        cstr_free(&astr);
            break;
        case TOK_ALIAS1:
        case TOK_ALIAS2:
            skip('(');
        parse_mult_str(&astr, "alias(\"target\")");
            ad->alias_target = /* save string as token, for later */
              tok_alloc((char*)astr.data, astr.size-1)->tok;
            skip(')');
        cstr_free(&astr);
            break;
    case TOK_VISIBILITY1:
    case TOK_VISIBILITY2:
            skip('(');
        parse_mult_str(&astr,
               "visibility(\"default|hidden|internal|protected\")");
        if (!strcmp (astr.data, "default"))
            ad->a.visibility = STV_DEFAULT;
        else if (!strcmp (astr.data, "hidden"))
            ad->a.visibility = STV_HIDDEN;
        else if (!strcmp (astr.data, "internal"))
            ad->a.visibility = STV_INTERNAL;
        else if (!strcmp (astr.data, "protected"))
            ad->a.visibility = STV_PROTECTED;
        else
                expect("visibility(\"default|hidden|internal|protected\")");
            skip(')');
        cstr_free(&astr);
            break;
        case TOK_ALIGNED1:
        case TOK_ALIGNED2:
            if (tok == '(') {
                next();
                n = expr_const();
                if (n <= 0 || (n & (n - 1)) != 0) 
                    tcc_error("alignment must be a positive power of two");
                skip(')');
            } else {
                n = MAX_ALIGN;
            }
            ad->a.aligned = exact_log2p1(n);
        if (n != 1 << (ad->a.aligned - 1))
          tcc_error("alignment of %d is larger than implemented", n);
            break;
        case TOK_PACKED1:
        case TOK_PACKED2:
            ad->a.packed = 1;
            break;
        case TOK_WEAK1:
        case TOK_WEAK2:
            ad->a.weak = 1;
            break;
        case TOK_UNUSED1:
        case TOK_UNUSED2:
            /* currently, no need to handle it because tcc does not
               track unused objects */
            break;
        case TOK_NORETURN1:
        case TOK_NORETURN2:
            /* currently, no need to handle it because tcc does not
               track unused objects */
            break;
        case TOK_CDECL1:
        case TOK_CDECL2:
        case TOK_CDECL3:
            ad->a.func_call = FUNC_CDECL;
            break;
        case TOK_STDCALL1:
        case TOK_STDCALL2:
        case TOK_STDCALL3:
            ad->a.func_call = FUNC_STDCALL;
            break;
#ifdef TCC_TARGET_I386
        case TOK_REGPARM1:
        case TOK_REGPARM2:
            skip('(');
            n = expr_const();
            if (n > 3) 
                n = 3;
            else if (n < 0)
                n = 0;
            if (n > 0)
                ad->a.func_call = FUNC_FASTCALL1 + n - 1;
            skip(')');
            break;
        case TOK_FASTCALL1:
        case TOK_FASTCALL2:
        case TOK_FASTCALL3:
            ad->a.func_call = FUNC_FASTCALLW;
            break;            
#endif
        case TOK_MODE:
            skip('(');
            switch(tok) {
                case TOK_MODE_DI:
                    ad->a.mode = VT_LLONG + 1;
                    break;
                case TOK_MODE_QI:
                    ad->a.mode = VT_BYTE + 1;
                    break;
                case TOK_MODE_HI:
                    ad->a.mode = VT_SHORT + 1;
                    break;
                case TOK_MODE_SI:
                case TOK_MODE_word:
                    ad->a.mode = VT_INT + 1;
                    break;
                default:
                    tcc_warning("__mode__(%s) not supported\n", get_tok_str(tok, NULL));
                    break;
            }
            next();
            skip(')');
            break;
        case TOK_DLLEXPORT:
            ad->a.func_export = 1;
            break;
        case TOK_DLLIMPORT:
            ad->a.func_import = 1;
            break;
        default:
            if (tcc_state->warn_unsupported)
                tcc_warning("'%s' attribute ignored", get_tok_str(t, NULL));
            /* skip parameters */
            if (tok == '(') {
                int parenthesis = 0;
                do {
                    if (tok == '(') 
                        parenthesis++;
                    else if (tok == ')') 
                        parenthesis--;
                    next();
                } while (parenthesis && tok != -1);
            }
            break;
        }
        if (tok != ',')
            break;
        next();
    }
    skip(')');
    skip(')');
    }
}

static Sym * find_field (CType *type, int v)
{
    Sym *s = type->ref;
    v |= SYM_FIELD;
    while ((s = s->next) != NULL) {
    if ((s->v & SYM_FIELD) &&
        (s->type.t & VT_BTYPE) == VT_STRUCT &&
        (s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM) {
        Sym *ret = find_field (&s->type, v);
        if (ret)
            return ret;
    }
    if (s->v == v)
      break;
    }
    return s;
}

static void struct_add_offset (Sym *s, int offset)
{
    while ((s = s->next) != NULL) {
    if ((s->v & SYM_FIELD) &&
        (s->type.t & VT_BTYPE) == VT_STRUCT &&
        (s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM) {
        struct_add_offset(s->type.ref, offset);
    } else
      s->c += offset;
    }
}

static void struct_layout(CType *type, AttributeDef *ad)
{
    int align, maxalign, offset, c, bit_pos, bt, prevbt, prev_bit_size;
    int pcc = !tcc_state->ms_bitfields;
    int packwarn = tcc_state->warn_gcc_compat;
    int typealign, bit_size, size;

    Sym *f;
    if (ad->a.aligned)
      maxalign = 1 << (ad->a.aligned - 1);
    else
      maxalign = 1;
    offset = 0;
    c = 0;
    bit_pos = 0;
    prevbt = VT_STRUCT; /* make it never match */
    prev_bit_size = 0;
    size = 0;

    for (f = type->ref->next; f; f = f->next) {
    size = type_size(&f->type, &typealign);
    if (f->type.t & VT_BITFIELD)
      bit_size = (f->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
    else
      bit_size = -1;
    if (bit_size == 0 && pcc) {
        /* Zero-width bit-fields in PCC mode aren't affected
           by any packing (attribute or pragma).  */
        align = typealign;
    } else if (f->r > 1) {
        align = f->r;
    } else if (ad->a.packed || f->r == 1) {
        align = 1;
        /* Packed fields or packed records don't let the base type
           influence the records type alignment.  */
        typealign = 1;
    } else {
        align = typealign;
    }
    if (type->ref->type.t != TOK_STRUCT) {
        if (pcc && bit_size >= 0)
          size = (bit_size + 7) >> 3;
        /* Bit position is already zero from our caller.  */
        offset = 0;
        if (size > c)
          c = size;
    } else if (bit_size < 0) {
        int addbytes = pcc ? (bit_pos + 7) >> 3 : 0;
        prevbt = VT_STRUCT;
        prev_bit_size = 0;
        c = (c + addbytes + align - 1) & -align;
        offset = c;
        if (size > 0)
          c += size;
        bit_pos = 0;
    } else {
        /* A bit-field.  Layout is more complicated.  There are two
           options TCC implements: PCC compatible and MS compatible
           (PCC compatible is what GCC uses for almost all targets).
           In PCC layout the overall size of the struct (in c) is
           _excluding_ the current run of bit-fields (that is,
           there's at least additional bit_pos bits after c).  In
           MS layout c does include the current run of bit-fields.
           
           This matters for calculating the natural alignment buckets
           in PCC mode.  */

        /* 'align' will be used to influence records alignment,
           so it's the max of specified and type alignment, except
           in certain cases that depend on the mode.  */
        if (align < typealign)
          align = typealign;
        if (pcc) {
        /* In PCC layout a non-packed bit-field is placed adjacent
           to the preceding bit-fields, except if it would overflow
           its container (depending on base type) or it's a zero-width
           bit-field.  Packed non-zero-width bit-fields always are
           placed adjacent.  */
        int ofs = (c * 8 + bit_pos) % (typealign * 8);
        int ofs2 = ofs + bit_size + (typealign * 8) - 1;
        if (bit_size == 0 ||
            (typealign != 1 &&
             (ofs2 / (typealign * 8)) > (size/typealign))) {
            c = (c + ((bit_pos + 7) >> 3) + typealign - 1) & -typealign;
            bit_pos = 0;
        } else if (bit_pos + bit_size > size * 8) {
            c += bit_pos >> 3;
            bit_pos &= 7;
                    if (bit_pos + bit_size > size * 8) {
                        c += 1, bit_pos = 0;
                        if ((ad->a.packed || f->r) && packwarn) {
                            tcc_warning("struct layout not compatible with GCC (internal limitation)");
                            packwarn = 0;
                        }
                    }
        }
        offset = c;
        /* In PCC layout named bit-fields influence the alignment
           of the containing struct using the base types alignment,
           except for packed fields (which here have correct
           align/typealign).  */
        if ((f->v & SYM_FIRST_ANOM))
          align = 1;
        } else {
        bt = f->type.t & VT_BTYPE;
        if ((bit_pos + bit_size > size * 8) ||
            (bit_size > 0) == (bt != prevbt)) {
            c = (c + typealign - 1) & -typealign;
            offset = c;
            bit_pos = 0;
            /* In MS bitfield mode a bit-field run always uses
               at least as many bits as the underlying type.
               To start a new run it's also required that this
               or the last bit-field had non-zero width.  */
            if (bit_size || prev_bit_size)
              c += size;
        }
        /* In MS layout the records alignment is normally
           influenced by the field, except for a zero-width
           field at the start of a run (but by further zero-width
           fields it is again).  */
        if (bit_size == 0 && prevbt != bt)
          align = 1;
        prevbt = bt;
        prev_bit_size = bit_size;
        }
        f->type.t = (f->type.t & ~(0x3f << VT_STRUCT_SHIFT))
                | (bit_pos << VT_STRUCT_SHIFT);
        bit_pos += bit_size;
        if (pcc && bit_pos >= size * 8) {
        c += size;
        bit_pos -= size * 8;
        }
    }
    if (align > maxalign)
      maxalign = align;
#if 0
    printf("set field %s offset=%d",
           get_tok_str(f->v & ~SYM_FIELD, NULL), offset);
    if (f->type.t & VT_BITFIELD) {
        printf(" pos=%d size=%d",
           (f->type.t >> VT_STRUCT_SHIFT) & 0x3f,
           (f->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f);
    }
    printf("\n");
#endif

    if (f->v & SYM_FIRST_ANOM && (f->type.t & VT_BTYPE) == VT_STRUCT) {
        Sym *ass;
        /* An anonymous struct/union.  Adjust member offsets
           to reflect the real offset of our containing struct.
           Also set the offset of this anon member inside
           the outer struct to be zero.  Via this it
           works when accessing the field offset directly
           (from base object), as well as when recursing
           members in initializer handling.  */
        int v2 = f->type.ref->v;
        if (!(v2 & SYM_FIELD) &&
        (v2 & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
        Sym **pps;
        /* This happens only with MS extensions.  The
           anon member has a named struct type, so it
           potentially is shared with other references.
           We need to unshare members so we can modify
           them.  */
        ass = f->type.ref;
        f->type.ref = sym_push(anon_sym++ | SYM_FIELD,
                       &f->type.ref->type, 0,
                       f->type.ref->c);
        pps = &f->type.ref->next;
        while ((ass = ass->next) != NULL) {
            *pps = sym_push(ass->v, &ass->type, 0, ass->c);
            pps = &((*pps)->next);
        }
        *pps = NULL;
        }
        struct_add_offset(f->type.ref, offset);
        f->c = 0;
    } else {
        f->c = offset;
    }

    f->r = 0;
    }
    /* store size and alignment */
    type->ref->c = (c + (pcc ? (bit_pos + 7) >> 3 : 0)
            + maxalign - 1) & -maxalign;
    type->ref->r = maxalign;
    if (offset + size > type->ref->c)
        tcc_warning("will touch memory past end of the struct (internal limitation)");
}

/* enum/struct/union declaration. u is either VT_ENUM or VT_STRUCT */
static void struct_decl(CType *type, AttributeDef *ad, int u)
{
    int a, v, size, align, flexible, alignoverride;
    long c;
    int bit_size, bsize, bt;
    Sym *s, *ss, **ps;
    AttributeDef ad1;
    CType type1, btype;

    a = tok; /* save decl type */
    next();
    if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
        parse_attribute(ad);
    if (tok != '{') {
        v = tok;
        next();
        /* struct already defined ? return it */
        if (v < TOK_IDENT)
            expect("struct/union/enum name");
        s = struct_find(v);
        if (s && (s->scope == local_scope || (tok != '{' && tok != ';'))) {
            if (s->type.t != a)
                tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
            goto do_decl;
        }
    } else {
        v = anon_sym++;
    }
    /* Record the original enum/struct/union token.  */
    type1.t = a;
    type1.ref = NULL;
    /* we put an undefined size for struct/union */
    s = sym_push(v | SYM_STRUCT, &type1, 0, -1);
    s->r = 0; /* default alignment is zero as gcc */
    /* put struct/union/enum name in type */
 do_decl:
    type->t = u;
    type->ref = s;
    
    if (tok == '{') {
        next();
        if (s->c != -1)
            tcc_error("struct/union/enum already defined");
        /* cannot be empty */
        c = 0;
        /* non empty enums are not allowed */
        if (a == TOK_ENUM) {
        int seen_neg = 0;
        int seen_wide = 0;
            for(;;) {
        CType *t = &int_type;
                v = tok;
                if (v < TOK_UIDENT)
                    expect("identifier");
                ss = sym_find(v);
                if (ss && !local_stack)
                    tcc_error("redefinition of enumerator '%s'",
                              get_tok_str(v, NULL));
                next();
                if (tok == '=') {
                    next();
#if PTR_SIZE == 8
            c = expr_const64();
#else
            /* We really want to support long long enums
               on i386 as well, but the Sym structure only
               holds a 'long' for associated constants,
               and enlarging it would bump its size (no
               available padding).  So punt for now.  */
                    c = expr_const();
#endif
                }
        if (c < 0)
            seen_neg = 1;
        if (c != (int)c && (unsigned long)c != (unsigned int)c)
            seen_wide = 1, t = &size_type;
                /* enum symbols have static storage */
                ss = sym_push(v, t, VT_CONST, c);
                ss->type.t |= VT_STATIC;
                if (tok != ',')
                    break;
                next();
                c++;
                /* NOTE: we accept a trailing comma */
                if (tok == '}')
                    break;
            }
        if (!seen_neg)
            s->a.unsigned_enum = 1;
            s->c = type_size(seen_wide ? &size_type : &int_type, &align);
            skip('}');
        } else {
            ps = &s->next;
            flexible = 0;
            while (tok != '}') {
                if (!parse_btype(&btype, &ad1)) {
            skip(';');
            continue;
        }
                while (1) {
            if (flexible)
                tcc_error("flexible array member '%s' not at the end of struct",
                              get_tok_str(v, NULL));
                    bit_size = -1;
                    v = 0;
                    type1 = btype;
                    if (tok != ':') {
            if (tok != ';')
                            type_decl(&type1, &ad1, &v, TYPE_DIRECT);
                        if (v == 0) {
                            if ((type1.t & VT_BTYPE) != VT_STRUCT)
                            expect("identifier");
                            else {
                int v = btype.ref->v;
                if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
                    if (tcc_state->ms_extensions == 0)
                                expect("identifier");
                }
                            }
                        }
                        if (type_size(&type1, &align) < 0) {
                if ((a == TOK_STRUCT) && (type1.t & VT_ARRAY) && c)
                    flexible = 1;
                else
                    tcc_error("field '%s' has incomplete type",
                                      get_tok_str(v, NULL));
                        }
                        if ((type1.t & VT_BTYPE) == VT_FUNC ||
                            (type1.t & VT_STORAGE))
                            tcc_error("invalid type for '%s'", 
                                  get_tok_str(v, NULL));
                    }
                    if (tok == ':') {
                        next();
                        bit_size = expr_const();
                        /* XXX: handle v = 0 case for messages */
                        if (bit_size < 0)
                            tcc_error("negative width in bit-field '%s'", 
                                  get_tok_str(v, NULL));
                        if (v && bit_size == 0)
                            tcc_error("zero width for bit-field '%s'", 
                                  get_tok_str(v, NULL));
            if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
                parse_attribute(&ad1);
                    }
                    size = type_size(&type1, &align);
            /* Only remember non-default alignment.  */
            alignoverride = 0;
                    if (ad1.a.aligned) {
            int speca = 1 << (ad1.a.aligned - 1);
            alignoverride = speca;
                    } else if (ad1.a.packed || ad->a.packed) {
                        alignoverride = 1;
                    } else if (*tcc_state->pack_stack_ptr) {
                        if (align >= *tcc_state->pack_stack_ptr)
                            alignoverride = *tcc_state->pack_stack_ptr;
                    }
                    if (bit_size >= 0) {
                        bt = type1.t & VT_BTYPE;
                        if (bt != VT_INT && 
                            bt != VT_BYTE && 
                            bt != VT_SHORT &&
                            bt != VT_BOOL &&
                            bt != VT_ENUM &&
                            bt != VT_LLONG)
                            tcc_error("bitfields must have scalar type");
                        bsize = size * 8;
                        if (bit_size > bsize) {
                            tcc_error("width of '%s' exceeds its type",
                                  get_tok_str(v, NULL));
                        } else if (bit_size == bsize) {
                            /* no need for bit fields */
                            ;
                        } else {
                            type1.t |= VT_BITFIELD | 
                                (0 << VT_STRUCT_SHIFT) |
                                (bit_size << (VT_STRUCT_SHIFT + 6));
                        }
                    }
                    if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) {
                        /* Remember we've seen a real field to check
               for placement of flexible array member. */
            c = 1;
                    }
            /* If member is a struct or bit-field, enforce
               placing into the struct (as anonymous).  */
                    if (v == 0 &&
            ((type1.t & VT_BTYPE) == VT_STRUCT ||
             bit_size >= 0)) {
                v = anon_sym++;
            }
                    if (v) {
                        ss = sym_push(v | SYM_FIELD, &type1, alignoverride, 0);
                        *ps = ss;
                        ps = &ss->next;
                    }
                    if (tok == ';' || tok == TOK_EOF)
                        break;
                    skip(',');
                }
                skip(';');
            }
            skip('}');
        if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
            parse_attribute(ad);
        struct_layout(type, ad);
        }
    }
}

/* return 1 if basic type is a type size (short, long, long long) */
ST_FUNC int is_btype_size(int bt)
{
  return bt == VT_SHORT || bt == VT_LONG || bt == VT_LLONG;
}

/* Add type qualifiers to a type. If the type is an array then the qualifiers
   are added to the element type, copied because it could be a typedef. */
static void parse_btype_qualify(CType *type, int qualifiers)
{
    while (type->t & VT_ARRAY) {
        type->ref = sym_push(SYM_FIELD, &type->ref->type, 0, type->ref->c);
        type = &type->ref->type;
    }
    type->t |= qualifiers;
}

/* return 0 if no type declaration. otherwise, return the basic type
   and skip it. 
 */
static int parse_btype(CType *type, AttributeDef *ad)
{
    int t, u, bt_size, complete, type_found, typespec_found, g;
    Sym *s;
    CType type1;

    memset(ad, 0, sizeof(AttributeDef));
    complete = 0;
    type_found = 0;
    typespec_found = 0;
    t = 0;
    while(1) {
        switch(tok) {
        case TOK_EXTENSION:
            /* currently, we really ignore extension */
            next();
            continue;

            /* basic types */
        case TOK_CHAR:
            u = VT_BYTE;
        basic_type:
            next();
        basic_type1:
            if (complete)
                tcc_error("too many basic types");
            t |= u;
            bt_size = is_btype_size (u & VT_BTYPE);
            if (u == VT_INT || (!bt_size && !(t & VT_TYPEDEF)))
                complete = 1;
            typespec_found = 1;
            break;
        case TOK_VOID:
            u = VT_VOID;
            goto basic_type;
        case TOK_SHORT:
            u = VT_SHORT;
            goto basic_type;
        case TOK_INT:
            u = VT_INT;
            goto basic_type;
        case TOK_LONG:
            next();
            if ((t & VT_BTYPE) == VT_DOUBLE) {
#ifndef TCC_TARGET_PE
                t = (t & ~VT_BTYPE) | VT_LDOUBLE;
#endif
            } else if ((t & VT_BTYPE) == VT_LONG) {
                t = (t & ~VT_BTYPE) | VT_LLONG;
            } else {
                u = VT_LONG;
                goto basic_type1;
            }
            break;
#ifdef TCC_TARGET_ARM64
        case TOK_UINT128:
            /* GCC's __uint128_t appears in some Linux header files. Make it a
               synonym for long double to get the size and alignment right. */
            u = VT_LDOUBLE;
            goto basic_type;
#endif
        case TOK_BOOL:
            u = VT_BOOL;
            goto basic_type;
        case TOK_FLOAT:
            u = VT_FLOAT;
            goto basic_type;
        case TOK_DOUBLE:
            next();
            if ((t & VT_BTYPE) == VT_LONG) {
#ifdef TCC_TARGET_PE
                t = (t & ~VT_BTYPE) | VT_DOUBLE;
#else
                t = (t & ~VT_BTYPE) | VT_LDOUBLE;
#endif
            } else {
                u = VT_DOUBLE;
                goto basic_type1;
            }
            break;
        case TOK_ENUM:
            struct_decl(&type1, ad, VT_ENUM);
        basic_type2:
            u = type1.t;
            type->ref = type1.ref;
            goto basic_type1;
        case TOK_STRUCT:
        case TOK_UNION:
            struct_decl(&type1, ad, VT_STRUCT);
            goto basic_type2;

            /* type modifiers */
        case TOK_CONST1:
        case TOK_CONST2:
        case TOK_CONST3:
            type->t = t;
            parse_btype_qualify(type, VT_CONSTANT);
            t = type->t;
            next();
            break;
        case TOK_VOLATILE1:
        case TOK_VOLATILE2:
        case TOK_VOLATILE3:
            type->t = t;
            parse_btype_qualify(type, VT_VOLATILE);
            t = type->t;
            next();
            break;
        case TOK_SIGNED1:
        case TOK_SIGNED2:
        case TOK_SIGNED3:
            if ((t & (VT_DEFSIGN|VT_UNSIGNED)) == (VT_DEFSIGN|VT_UNSIGNED))
                tcc_error("signed and unsigned modifier");
            typespec_found = 1;
            t |= VT_DEFSIGN;
            next();
            break;
        case TOK_REGISTER:
        case TOK_AUTO:
        case TOK_RESTRICT1:
        case TOK_RESTRICT2:
        case TOK_RESTRICT3:
            next();
            break;
        case TOK_UNSIGNED:
            if ((t & (VT_DEFSIGN|VT_UNSIGNED)) == VT_DEFSIGN)
                tcc_error("signed and unsigned modifier");
            t |= VT_DEFSIGN | VT_UNSIGNED;
            next();
            typespec_found = 1;
            break;

            /* storage */
        case TOK_EXTERN:
            g = VT_EXTERN;
            goto storage;
        case TOK_STATIC:
            g = VT_STATIC;
            goto storage;
        case TOK_TYPEDEF:
            g = VT_TYPEDEF;
            goto storage;
       storage:
            if (t & (VT_EXTERN|VT_STATIC|VT_TYPEDEF) & ~g)
                tcc_error("multiple storage classes");
            t |= g;
            next();
            break;
        case TOK_INLINE1:
        case TOK_INLINE2:
        case TOK_INLINE3:
            t |= VT_INLINE;
            next();
            break;

            /* GNUC attribute */
        case TOK_ATTRIBUTE1:
        case TOK_ATTRIBUTE2:
            parse_attribute(ad);
            if (ad->a.mode) {
                u = ad->a.mode -1;
                t = (t & ~VT_BTYPE) | u;
            }
            break;
            /* GNUC typeof */
        case TOK_TYPEOF1:
        case TOK_TYPEOF2:
        case TOK_TYPEOF3:
            next();
            parse_expr_type(&type1);
            /* remove all storage modifiers except typedef */
            type1.t &= ~(VT_STORAGE&~VT_TYPEDEF);
            goto basic_type2;
        default:
            if (typespec_found)
                goto the_end;
            s = sym_find(tok);
            if (!s || !(s->type.t & VT_TYPEDEF))
                goto the_end;

            type->t = ((s->type.t & ~VT_TYPEDEF) |
                       (t & ~(VT_CONSTANT | VT_VOLATILE)));
            type->ref = s->type.ref;
            if (t & (VT_CONSTANT | VT_VOLATILE))
                parse_btype_qualify(type, t & (VT_CONSTANT | VT_VOLATILE));
            t = type->t;

            if (s->r) {
                /* get attributes from typedef */
                if (0 == ad->a.aligned)
                    ad->a.aligned = s->a.aligned;
                if (0 == ad->a.func_call)
                    ad->a.func_call = s->a.func_call;
                ad->a.packed |= s->a.packed;
            }
            next();
            typespec_found = 1;
            break;
        }
        type_found = 1;
    }
the_end:
    if (tcc_state->char_is_unsigned) {
        if ((t & (VT_DEFSIGN|VT_BTYPE)) == VT_BYTE)
            t |= VT_UNSIGNED;
    }

    /* long is never used as type */
    if ((t & VT_BTYPE) == VT_LONG)
#if PTR_SIZE == 8 && !defined TCC_TARGET_PE
        t = (t & ~VT_BTYPE) | VT_LLONG;
#else
        t = (t & ~VT_BTYPE) | VT_INT;
#endif
    type->t = t;
    return type_found;
}

/* convert a function parameter type (array to pointer and function to
   function pointer) */
static inline void convert_parameter_type(CType *pt)
{
    /* remove const and volatile qualifiers (XXX: const could be used
       to indicate a const function parameter */
    pt->t &= ~(VT_CONSTANT | VT_VOLATILE);
    /* array must be transformed to pointer according to ANSI C */
    pt->t &= ~VT_ARRAY;
    if ((pt->t & VT_BTYPE) == VT_FUNC) {
        mk_pointer(pt);
    }
}

ST_FUNC void parse_asm_str(CString *astr)
{
    skip('(');
    parse_mult_str(astr, "string constant");
}

/* Parse an asm label and return the token */
static int asm_label_instr(void)
{
    int v;
    CString astr;

    next();
    parse_asm_str(&astr);
    skip(')');
#ifdef ASM_DEBUG
    printf("asm_alias: \"%s\"\n", (char *)astr.data);
#endif
    v = tok_alloc(astr.data, astr.size - 1)->tok;
    cstr_free(&astr);
    return v;
}

static int post_type(CType *type, AttributeDef *ad, int storage, int td)
{
    int n, l, t1, arg_size, align;
    Sym **plast, *s, *first;
    AttributeDef ad1;
    CType pt;

    if (tok == '(') {
        /* function type, or recursive declarator (return if so) */
        next();
    if (td && !(td & TYPE_ABSTRACT))
      return 0;
    if (tok == ')')
      l = 0;
    else if (parse_btype(&pt, &ad1))
      l = FUNC_NEW;
    else if (td)
      return 0;
    else
      l = FUNC_OLD;
        first = NULL;
        plast = &first;
        arg_size = 0;
        if (l) {
            for(;;) {
                /* read param name and compute offset */
                if (l != FUNC_OLD) {
                    if ((pt.t & VT_BTYPE) == VT_VOID && tok == ')')
                        break;
                    type_decl(&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT);
                    if ((pt.t & VT_BTYPE) == VT_VOID)
                        tcc_error("parameter declared as void");
                    arg_size += (type_size(&pt, &align) + PTR_SIZE - 1) / PTR_SIZE;
                } else {
                    n = tok;
                    if (n < TOK_UIDENT)
                        expect("identifier");
                    pt.t = VT_VOID; /* invalid type */
                    next();
                }
                convert_parameter_type(&pt);
                s = sym_push(n | SYM_FIELD, &pt, 0, 0);
                *plast = s;
                plast = &s->next;
                if (tok == ')')
                    break;
                skip(',');
                if (l == FUNC_NEW && tok == TOK_DOTS) {
                    l = FUNC_ELLIPSIS;
                    next();
                    break;
                }
        if (l == FUNC_NEW && !parse_btype(&pt, &ad1))
            tcc_error("invalid type");
            }
        } else
            /* if no parameters, then old type prototype */
            l = FUNC_OLD;
        skip(')');
        /* NOTE: const is ignored in returned type as it has a special
           meaning in gcc / C++ */
        type->t &= ~VT_CONSTANT; 
        /* some ancient pre-K&R C allows a function to return an array
           and the array brackets to be put after the arguments, such 
           that "int c()[]" means something like "int[] c()" */
        if (tok == '[') {
            next();
            skip(']'); /* only handle simple "[]" */
            mk_pointer(type);
        }
        /* we push a anonymous symbol which will contain the function prototype */
        ad->a.func_args = arg_size;
        s = sym_push(SYM_FIELD, type, 0, l);
        s->a = ad->a;
        s->next = first;
        type->t = VT_FUNC;
        type->ref = s;
    } else if (tok == '[') {
    int saved_nocode_wanted = nocode_wanted;
        /* array definition */
        next();
        if (tok == TOK_RESTRICT1)
            next();
        n = -1;
        t1 = 0;
        if (tok != ']') {
            if (!local_stack || (storage & VT_STATIC))
                vpushi(expr_const());
            else {
        /* VLAs (which can only happen with local_stack && !VT_STATIC)
           length must always be evaluated, even under nocode_wanted,
           so that its size slot is initialized (e.g. under sizeof
           or typeof).  */
        nocode_wanted = 0;
        gexpr();
        }
            if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
                n = vtop->c.i;
                if (n < 0)
                    tcc_error("invalid array size");
            } else {
                if (!is_integer_btype(vtop->type.t & VT_BTYPE))
                    tcc_error("size of variable length array should be an integer");
                t1 = VT_VLA;
            }
        }
        skip(']');
        /* parse next post type */
        post_type(type, ad, storage, 0);
        if (type->t == VT_FUNC)
            tcc_error("declaration of an array of functions");
        t1 |= type->t & VT_VLA;
        
        if (t1 & VT_VLA) {
            loc -= type_size(&int_type, &align);
            loc &= -align;
            n = loc;

            vla_runtime_type_size(type, &align);
            gen_op('*');
            vset(&int_type, VT_LOCAL|VT_LVAL, n);
            vswap();
            vstore();
        }
        if (n != -1)
            vpop();
    nocode_wanted = saved_nocode_wanted;
                
        /* we push an anonymous symbol which will contain the array
           element type */
        s = sym_push(SYM_FIELD, type, 0, n);
        type->t = (t1 ? VT_VLA : VT_ARRAY) | VT_PTR;
        type->ref = s;
    }
    return 1;
}

/* Parse a type declarator (except basic type), and return the type
   in 'type'. 'td' is a bitmask indicating which kind of type decl is
   expected. 'type' should contain the basic type. 'ad' is the
   attribute definition of the basic type. It can be modified by
   type_decl().  If this (possibly abstract) declarator is a pointer chain
   it returns the innermost pointed to type (equals *type, but is a different
   pointer), otherwise returns type itself, that's used for recursive calls.  */
static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td)
{
    CType *post, *ret;
    int qualifiers, storage;

    /* recursive type, remove storage bits first, apply them later again */
    storage = type->t & VT_STORAGE;
    type->t &= ~VT_STORAGE;
    post = ret = type;
    while (tok == '*') {
        qualifiers = 0;
    redo:
        next();
        switch(tok) {
        case TOK_CONST1:
        case TOK_CONST2:
        case TOK_CONST3:
            qualifiers |= VT_CONSTANT;
            goto redo;
        case TOK_VOLATILE1:
        case TOK_VOLATILE2:
        case TOK_VOLATILE3:
            qualifiers |= VT_VOLATILE;
            goto redo;
        case TOK_RESTRICT1:
        case TOK_RESTRICT2:
        case TOK_RESTRICT3:
            goto redo;
    /* XXX: clarify attribute handling */
    case TOK_ATTRIBUTE1:
    case TOK_ATTRIBUTE2:
        parse_attribute(ad);
        break;
        }
        mk_pointer(type);
        type->t |= qualifiers;
    if (ret == type)
        /* innermost pointed to type is the one for the first derivation */
        ret = pointed_type(type);
    }

    if (tok == '(') {
    /* This is possibly a parameter type list for abstract declarators
       ('int ()'), use post_type for testing this.  */
    if (!post_type(type, ad, 0, td)) {
        /* It's not, so it's a nested declarator, and the post operations
           apply to the innermost pointed to type (if any).  */
        /* XXX: this is not correct to modify 'ad' at this point, but
           the syntax is not clear */
        if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
          parse_attribute(ad);
        post = type_decl(type, ad, v, td);
        skip(')');
    }
    } else if (tok >= TOK_IDENT && (td & TYPE_DIRECT)) {
    /* type identifier */
    *v = tok;
    next();
    } else {
    if (!(td & TYPE_ABSTRACT))
      expect("identifier");
    *v = 0;
    }
    post_type(post, ad, storage, 0);
    if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
        parse_attribute(ad);
    type->t |= storage;
    return ret;
}

/* compute the lvalue VT_LVAL_xxx needed to match type t. */
ST_FUNC int lvalue_type(int t)
{
    int bt, r;
    r = VT_LVAL;
    bt = t & VT_BTYPE;
    if (bt == VT_BYTE || bt == VT_BOOL)
        r |= VT_LVAL_BYTE;
    else if (bt == VT_SHORT)
        r |= VT_LVAL_SHORT;
    else
        return r;
    if (t & VT_UNSIGNED)
        r |= VT_LVAL_UNSIGNED;
    return r;
}

/* indirection with full error checking and bound check */
ST_FUNC void indir(void)
{
    if ((vtop->type.t & VT_BTYPE) != VT_PTR) {
        if ((vtop->type.t & VT_BTYPE) == VT_FUNC)
            return;
        expect("pointer");
    }
    if (vtop->r & VT_LVAL)
        gv(RC_INT);
    vtop->type = *pointed_type(&vtop->type);
    /* Arrays and functions are never lvalues */
    if (!(vtop->type.t & VT_ARRAY) && !(vtop->type.t & VT_VLA)
        && (vtop->type.t & VT_BTYPE) != VT_FUNC) {
        vtop->r |= lvalue_type(vtop->type.t);
        /* if bound checking, the referenced pointer must be checked */
#ifdef CONFIG_TCC_BCHECK
        if (tcc_state->do_bounds_check)
            vtop->r |= VT_MUSTBOUND;
#endif
    }
}

/* pass a parameter to a function and do type checking and casting */
static void gfunc_param_typed(Sym *func, Sym *arg)
{
    int func_type;
    CType type;

    func_type = func->c;
    if (func_type == FUNC_OLD ||
        (func_type == FUNC_ELLIPSIS && arg == NULL)) {
        /* default casting : only need to convert float to double */
        if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) {
            type.t = VT_DOUBLE;
            gen_cast(&type);
        } else if (vtop->type.t & VT_BITFIELD) {
            type.t = vtop->type.t & (VT_BTYPE | VT_UNSIGNED);
        type.ref = vtop->type.ref;
            gen_cast(&type);
        }
    } else if (arg == NULL) {
        tcc_error("too many arguments to function");
    } else {
        type = arg->type;
        type.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
        gen_assign_cast(&type);
    }
}

/* parse an expression and return its type without any side effect.
   If UNRY we parse an unary expression, otherwise a full one.  */
static void expr_type(CType *type, int unry)
{
    nocode_wanted++;
    if (unry)
        unary();
    else
        gexpr();
    *type = vtop->type;
    vpop();
    nocode_wanted--;
}

/* parse an expression of the form '(type)' or '(expr)' and return its
   type */
static void parse_expr_type(CType *type)
{
    int n;
    AttributeDef ad;

    skip('(');
    if (parse_btype(type, &ad)) {
        type_decl(type, &ad, &n, TYPE_ABSTRACT);
    } else {
        expr_type(type, 0);
    }
    skip(')');
}

static void parse_type(CType *type)
{
    AttributeDef ad;
    int n;

    if (!parse_btype(type, &ad)) {
        expect("type");
    }
    type_decl(type, &ad, &n, TYPE_ABSTRACT);
}

static void parse_builtin_params(int nc, const char *args)
{
    char c, sep = '(';
    CType t;
    if (nc)
        nocode_wanted++;
    next();
    while ((c = *args++)) {
    skip(sep);
    sep = ',';
    switch (c) {
        case 'e': expr_eq(); continue;
        case 't': parse_type(&t); vpush(&t); continue;
        default: tcc_error("internal error"); break;
    }
    }
    skip(')');
    if (nc)
        nocode_wanted--;
}

ST_FUNC void unary(void)
{
    int n, t, align, size, r, sizeof_caller;
    CType type;
    Sym *s;
    AttributeDef ad;

    sizeof_caller = in_sizeof;
    in_sizeof = 0;
    /* XXX: GCC 2.95.3 does not generate a table although it should be
       better here */
 tok_next:
    switch(tok) {
    case TOK_EXTENSION:
        next();
        goto tok_next;
    case TOK_CINT:
    case TOK_CCHAR: 
    case TOK_LCHAR:
    t = VT_INT;
 push_tokc:
    type.t = t;
    type.ref = 0;
    vsetc(&type, VT_CONST, &tokc);
        next();
        break;
    case TOK_CUINT:
        t = VT_INT | VT_UNSIGNED;
        goto push_tokc;
    case TOK_CLLONG:
        t = VT_LLONG;
    goto push_tokc;
    case TOK_CULLONG:
        t = VT_LLONG | VT_UNSIGNED;
    goto push_tokc;
    case TOK_CFLOAT:
        t = VT_FLOAT;
    goto push_tokc;
    case TOK_CDOUBLE:
        t = VT_DOUBLE;
    goto push_tokc;
    case TOK_CLDOUBLE:
        t = VT_LDOUBLE;
    goto push_tokc;

    case TOK___FUNCTION__:
        if (!gnu_ext)
            goto tok_identifier;
        /* fall thru */
    case TOK___FUNC__:
        {
            void *ptr;
            int len;
            /* special function name identifier */
            len = strlen(funcname) + 1;
            /* generate char[len] type */
            type.t = VT_BYTE;
            mk_pointer(&type);
            type.t |= VT_ARRAY;
            type.ref->c = len;
            vpush_ref(&type, data_section, data_section->data_offset, len);
            ptr = section_ptr_add(data_section, len);
            memcpy(ptr, funcname, len);
            next();
        }
        break;
    case TOK_LSTR:
#ifdef TCC_TARGET_PE
        t = VT_SHORT | VT_UNSIGNED;
#else
        t = VT_INT;
#endif
        goto str_init;
    case TOK_STR:
        /* string parsing */
        t = VT_BYTE;
    str_init:
        if (tcc_state->warn_write_strings)
            t |= VT_CONSTANT;
        type.t = t;
        mk_pointer(&type);
        type.t |= VT_ARRAY;
        memset(&ad, 0, sizeof(AttributeDef));
        decl_initializer_alloc(&type, &ad, VT_CONST, 2, 0, 0);
        break;
    case '(':
        next();
        /* cast ? */
        if (parse_btype(&type, &ad)) {
            type_decl(&type, &ad, &n, TYPE_ABSTRACT);
            skip(')');
            /* check ISOC99 compound literal */
            if (tok == '{') {
                    /* data is allocated locally by default */
                if (global_expr)
                    r = VT_CONST;
                else
                    r = VT_LOCAL;
                /* all except arrays are lvalues */
                if (!(type.t & VT_ARRAY))
                    r |= lvalue_type(type.t);
                memset(&ad, 0, sizeof(AttributeDef));
                decl_initializer_alloc(&type, &ad, r, 1, 0, 0);
            } else {
                if (sizeof_caller) {
                    vpush(&type);
                    return;
                }
                unary();
                gen_cast(&type);
            }
        } else if (tok == '{') {
        int saved_nocode_wanted = nocode_wanted;
            if (const_wanted)
                tcc_error("expected constant");
            /* save all registers */
            save_regs(0);
            /* statement expression : we do not accept break/continue
               inside as GCC does.  We do retain the nocode_wanted state,
           as statement expressions can't ever be entered from the
           outside, so any reactivation of code emission (from labels
           or loop heads) can be disabled again after the end of it. */
            block(NULL, NULL, 1);
        nocode_wanted = saved_nocode_wanted;
            skip(')');
        } else {
            gexpr();
            skip(')');
        }
        break;
    case '*':
        next();
        unary();
        indir();
        break;
    case '&':
        next();
        unary();
        /* functions names must be treated as function pointers,
           except for unary '&' and sizeof. Since we consider that
           functions are not lvalues, we only have to handle it
           there and in function calls. */
        /* arrays can also be used although they are not lvalues */
        if ((vtop->type.t & VT_BTYPE) != VT_FUNC &&
            !(vtop->type.t & VT_ARRAY))
            test_lvalue();
        mk_pointer(&vtop->type);
        gaddrof();
        break;
    case '!':
        next();
        unary();
        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
            CType boolean;
            boolean.t = VT_BOOL;
            gen_cast(&boolean);
            vtop->c.i = !vtop->c.i;
        } else if ((vtop->r & VT_VALMASK) == VT_CMP)
            vtop->c.i ^= 1;
        else {
            save_regs(1);
            vseti(VT_JMP, gvtst(1, 0));
        }
        break;
    case '~':
        next();
        unary();
        vpushi(-1);
        gen_op('^');
        break;
    case '+':
        next();
        unary();
        if ((vtop->type.t & VT_BTYPE) == VT_PTR)
            tcc_error("pointer not accepted for unary plus");
        /* In order to force cast, we add zero, except for floating point
       where we really need an noop (otherwise -0.0 will be transformed
       into +0.0).  */
    if (!is_float(vtop->type.t)) {
        vpushi(0);
        gen_op('+');
    }
        break;
    case TOK_SIZEOF:
    case TOK_ALIGNOF1:
    case TOK_ALIGNOF2:
        t = tok;
        next();
        in_sizeof++;
        expr_type(&type, 1); // Perform a in_sizeof = 0;
        size = type_size(&type, &align);
        if (t == TOK_SIZEOF) {
            if (!(type.t & VT_VLA)) {
                if (size < 0)
                    tcc_error("sizeof applied to an incomplete type");
                vpushs(size);
            } else {
                vla_runtime_type_size(&type, &align);
            }
        } else {
            vpushs(align);
        }
        vtop->type.t |= VT_UNSIGNED;
        break;

    case TOK_builtin_expect:
    /* __builtin_expect is a no-op for now */
    parse_builtin_params(0, "ee");
    vpop();
        break;
    case TOK_builtin_types_compatible_p:
    parse_builtin_params(0, "tt");
    vtop[-1].type.t &= ~(VT_CONSTANT | VT_VOLATILE);
    vtop[0].type.t &= ~(VT_CONSTANT | VT_VOLATILE);
    n = is_compatible_types(&vtop[-1].type, &vtop[0].type);
    vtop -= 2;
    vpushi(n);
        break;
    case TOK_builtin_choose_expr:
    {
#if HAVE_LONG_LONG_STUB || HAVE_LONG_LONG
        int64_t c;
#else
        int32_t c;
#endif
        next();
        skip('(');
#if HAVE_LONG_LONG
        c = expr_const64();
#else
        c = expr_const32();
#endif
        skip(',');
        if (!c) {
        nocode_wanted++;
        }
        expr_eq();
        if (!c) {
        vpop();
        nocode_wanted--;
        }
        skip(',');
        if (c) {
        nocode_wanted++;
        }
        expr_eq();
        if (c) {
        vpop();
        nocode_wanted--;
        }
        skip(')');
    }
        break;
    case TOK_builtin_constant_p:
    parse_builtin_params(1, "e");
    n = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
    vtop--;
    vpushi(n);
        break;
    case TOK_builtin_frame_address:
    case TOK_builtin_return_address:
        {
            int tok1 = tok;
            int level;
            CType type;
            next();
            skip('(');
            if (tok != TOK_CINT) {
                tcc_error("%s only takes positive integers",
                          tok1 == TOK_builtin_return_address ?
                          "__builtin_return_address" :
                          "__builtin_frame_address");
            }
            level = (uint32_t)tokc.i;
            next();
            skip(')');
            type.t = VT_VOID;
            mk_pointer(&type);
            vset(&type, VT_LOCAL, 0);       /* local frame */
            while (level--) {
                mk_pointer(&vtop->type);
                indir();                    /* -> parent frame */
            }
            if (tok1 == TOK_builtin_return_address) {
                // assume return address is just above frame pointer on stack
                vpushi(PTR_SIZE);
                gen_op('+');
                mk_pointer(&vtop->type);
                indir();
            }
        }
        break;
#ifdef TCC_TARGET_X86_64
#ifdef TCC_TARGET_PE
    case TOK_builtin_va_start:
    parse_builtin_params(0, "ee");
        r = vtop->r & VT_VALMASK;
        if (r == VT_LLOCAL)
            r = VT_LOCAL;
        if (r != VT_LOCAL)
            tcc_error("__builtin_va_start expects a local variable");
        vtop->r = r;
    vtop->type = char_pointer_type;
    vtop->c.i += 8;
    vstore();
        break;
#else
    case TOK_builtin_va_arg_types:
    parse_builtin_params(0, "t");
    vpushi(classify_x86_64_va_arg(&vtop->type));
    vswap();
    vpop();
        break;
#endif
#endif

#ifdef TCC_TARGET_ARM64
    case TOK___va_start: {
    parse_builtin_params(0, "ee");
        //xx check types
        gen_va_start();
        vpushi(0);
        vtop->type.t = VT_VOID;
        break;
    }
    case TOK___va_arg: {
        CType type;
    parse_builtin_params(0, "et");
    type = vtop->type;
    vpop();
        //xx check types
        gen_va_arg(&type);
        vtop->type = type;
        break;
    }
    case TOK___arm64_clear_cache: {
    parse_builtin_params(0, "ee");
        gen_clear_cache();
        vpushi(0);
        vtop->type.t = VT_VOID;
        break;
    }
#endif
    /* pre operations */
    case TOK_INC:
    case TOK_DEC:
        t = tok;
        next();
        unary();
        inc(0, t);
        break;
    case '-':
        next();
        unary();
        t = vtop->type.t & VT_BTYPE;
    if (is_float(t)) {
            /* In IEEE negate(x) isn't subtract(0,x), but rather
           subtract(-0, x).  */
        vpush(&vtop->type);
#if HAVE_FLOAT
        if (t == VT_FLOAT)
            vtop->c.f = -1.0 * 0.0;
        else if (t == VT_DOUBLE)
            vtop->c.d = -1.0 * 0.0;
        else
            vtop->c.ld = -1.0 * 0.0;
#endif
    } else
        vpushi(0);
    vswap();
    gen_op('-');
        break;
    case TOK_LAND:
        if (!gnu_ext)
            goto tok_identifier;
        next();
        /* allow to take the address of a label */
        if (tok < TOK_UIDENT)
            expect("label identifier");
        s = label_find(tok);
        if (!s) {
            s = label_push(&global_label_stack, tok, LABEL_FORWARD);
        } else {
            if (s->r == LABEL_DECLARED)
                s->r = LABEL_FORWARD;
        }
        if (!s->type.t) {
            s->type.t = VT_VOID;
            mk_pointer(&s->type);
            s->type.t |= VT_STATIC;
        }
        vpushsym(&s->type, s);
        next();
        break;

    // special qnan , snan and infinity values
    case TOK___NAN__:
#if HAVE_LONG_LONG
        vpush64(VT_DOUBLE, 0x7ff8000000000000ULL);
#else
        vpush64(VT_DOUBLE, 0x7ff80000);
        vtop->c.i = vtop->c.i << 32;
#endif
        next();
        break;
    case TOK___SNAN__:
#if HAVE_LONG_LONG
        vpush64(VT_DOUBLE, 0x7ff0000000000001ULL);
#else
        vpush64(VT_DOUBLE, 0x7ff00000);
        vtop->c.i = vtop->c.i << 32;
        vtop->c.i = vtop->c.i | 1;
#endif
        next();
        break;
    case TOK___INF__:
#if HAVE_LONG_LONG
        vpush64(VT_DOUBLE, 0x7ff0000000000000ULL);
#else
        vpush64(VT_DOUBLE, 0x7ff00000);
        vtop->c.i = vtop->c.i << 32;
#endif
        next();
        break;

    default:
    tok_identifier:
        t = tok;
        next();
        if (t < TOK_UIDENT)
            expect("identifier");
        s = sym_find(t);
        if (!s) {
            const char *name = get_tok_str(t, NULL);
            if (tok != '(')
                tcc_error("'%s' undeclared", name);
            /* for simple function calls, we tolerate undeclared
               external reference to int() function */
            if (tcc_state->warn_implicit_function_declaration
#ifdef TCC_TARGET_PE
                /* people must be warned about using undeclared WINAPI functions
                   (which usually start with uppercase letter) */
                || (name[0] >= 'A' && name[0] <= 'Z')
#endif
            )
                tcc_warning("implicit declaration of function '%s'", name);
            s = external_global_sym(t, &func_old_type, 0); 
        }

        r = s->r;
        /* A symbol that has a register is a local register variable,
           which starts out as VT_LOCAL value.  */
        if ((r & VT_VALMASK) < VT_CONST)
            r = (r & ~VT_VALMASK) | VT_LOCAL;

        vset(&s->type, r, s->c);
        /* Point to s as backpointer (even without r&VT_SYM).
       Will be used by at least the x86 inline asm parser for
       regvars.  */
    vtop->sym = s;
        if (vtop->r & VT_SYM) {
            vtop->c.i = 0;
        }
        break;
    }
    
    /* post operations */
    while (1) {
        if (tok == TOK_INC || tok == TOK_DEC) {
            inc(1, tok);
            next();
        } else if (tok == '.' || tok == TOK_ARROW || tok == TOK_CDOUBLE) {
            int qualifiers;
            /* field */ 
            if (tok == TOK_ARROW) 
                indir();
            qualifiers = vtop->type.t & (VT_CONSTANT | VT_VOLATILE);
            test_lvalue();
            gaddrof();
            /* expect pointer on structure */
            if ((vtop->type.t & VT_BTYPE) != VT_STRUCT)
                expect("struct or union");
            if (tok == TOK_CDOUBLE)
                expect("field name");
            next();
            if (tok == TOK_CINT || tok == TOK_CUINT)
                expect("field name");
        s = find_field(&vtop->type, tok);
            if (!s)
                tcc_error("field not found: %s",  get_tok_str(tok & ~SYM_FIELD, &tokc));
            /* add field offset to pointer */
            vtop->type = char_pointer_type; /* change type to 'char *' */
            vpushi(s->c);
            gen_op('+');
            /* change type to field type, and set to lvalue */
            vtop->type = s->type;
            vtop->type.t |= qualifiers;
            /* an array is never an lvalue */
            if (!(vtop->type.t & VT_ARRAY)) {
                vtop->r |= lvalue_type(vtop->type.t);
#ifdef CONFIG_TCC_BCHECK
                /* if bound checking, the referenced pointer must be checked */
                if (tcc_state->do_bounds_check && (vtop->r & VT_VALMASK) != VT_LOCAL)
                    vtop->r |= VT_MUSTBOUND;
#endif
            }
            next();
        } else if (tok == '[') {
            next();
            gexpr();
            gen_op('+');
            indir();
            skip(']');
        } else if (tok == '(') {
            SValue ret;
            Sym *sa;
            int nb_args, ret_nregs, ret_align, regsize, variadic;

            /* function call  */
            if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
                /* pointer test (no array accepted) */
                if ((vtop->type.t & (VT_BTYPE | VT_ARRAY)) == VT_PTR) {
                    vtop->type = *pointed_type(&vtop->type);
                    if ((vtop->type.t & VT_BTYPE) != VT_FUNC)
                        goto error_func;
                } else {
                error_func:
                    expect("function pointer");
                }
            } else {
                vtop->r &= ~VT_LVAL; /* no lvalue */
            }
            /* get return type */
            s = vtop->type.ref;
            next();
            sa = s->next; /* first parameter */
            nb_args = regsize = 0;
            ret.r2 = VT_CONST;
            /* compute first implicit argument if a structure is returned */
            if ((s->type.t & VT_BTYPE) == VT_STRUCT) {
                variadic = (s->c == FUNC_ELLIPSIS);
                ret_nregs = gfunc_sret(&s->type, variadic, &ret.type,
                                       &ret_align, &regsize);
                if (!ret_nregs) {
                    /* get some space for the returned structure */
                    size = type_size(&s->type, &align);
#ifdef TCC_TARGET_ARM64
                /* On arm64, a small struct is return in registers.
                   It is much easier to write it to memory if we know
                   that we are allowed to write some extra bytes, so
                   round the allocated space up to a power of 2: */
                if (size < 16)
                    while (size & (size - 1))
                        size = (size | (size - 1)) + 1;
#endif
                    loc = (loc - size) & -align;
                    ret.type = s->type;
                    ret.r = VT_LOCAL | VT_LVAL;
                    /* pass it as 'int' to avoid structure arg passing
                       problems */
                    vseti(VT_LOCAL, loc);
                    ret.c = vtop->c;
                    nb_args++;
                }
            } else {
                ret_nregs = 1;
                ret.type = s->type;
            }

            if (ret_nregs) {
                /* return in register */
                if (is_float(ret.type.t)) {
                    ret.r = reg_fret(ret.type.t);
#ifdef TCC_TARGET_X86_64
                    if ((ret.type.t & VT_BTYPE) == VT_QFLOAT)
                      ret.r2 = REG_QRET;
#endif
                } else {
#ifndef TCC_TARGET_ARM64
#ifdef TCC_TARGET_X86_64
                    if ((ret.type.t & VT_BTYPE) == VT_QLONG)
#else
                    if ((ret.type.t & VT_BTYPE) == VT_LLONG)
#endif
                        ret.r2 = REG_LRET;
#endif
                    ret.r = REG_IRET;
                }
                ret.c.i = 0;
            }
            if (tok != ')') {
                for(;;) {
                    expr_eq();
                    gfunc_param_typed(s, sa);
                    nb_args++;
                    if (sa)
                        sa = sa->next;
                    if (tok == ')')
                        break;
                    skip(',');
                }
            }
            if (sa)
                tcc_error("too few arguments to function");
            skip(')');
            gfunc_call(nb_args);

            /* return value */
            for (r = ret.r + ret_nregs + !ret_nregs; r-- > ret.r;) {
                vsetc(&ret.type, r, &ret.c);
                vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */
            }

            /* handle packed struct return */
            if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) {
                int addr, offset;

                size = type_size(&s->type, &align);
        /* We're writing whole regs often, make sure there's enough
           space.  Assume register size is power of 2.  */
        if (regsize > align)
          align = regsize;
                loc = (loc - size) & -align;
                addr = loc;
                offset = 0;
                for (;;) {
                    vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset);
                    vswap();
                    vstore();
                    vtop--;
                    if (--ret_nregs == 0)
                        break;
                    offset += regsize;
                }
                vset(&s->type, VT_LOCAL | VT_LVAL, addr);
            }
        } else {
            break;
        }
    }
}

ST_FUNC void expr_prod(void)
{
    int t;

    unary();
    while (tok == '*' || tok == '/' || tok == '%') {
        t = tok;
        next();
        unary();
        gen_op(t);
    }
}

ST_FUNC void expr_sum(void)
{
    int t;

    expr_prod();
    while (tok == '+' || tok == '-') {
        t = tok;
        next();
        expr_prod();
        gen_op(t);
    }
}

static void expr_shift(void)
{
    int t;

    expr_sum();
    while (tok == TOK_SHL || tok == TOK_SAR) {
        t = tok;
        next();
        expr_sum();
        gen_op(t);
    }
}

static void expr_cmp(void)
{
    int t;

    expr_shift();
    while ((tok >= TOK_ULE && tok <= TOK_GT) ||
           tok == TOK_ULT || tok == TOK_UGE) {
        t = tok;
        next();
        expr_shift();
        gen_op(t);
    }
}

static void expr_cmpeq(void)
{
    int t;

    expr_cmp();
    while (tok == TOK_EQ || tok == TOK_NE) {
        t = tok;
        next();
        expr_cmp();
        gen_op(t);
    }
}

static void expr_and(void)
{
    expr_cmpeq();
    while (tok == '&') {
        next();
        expr_cmpeq();
        gen_op('&');
    }
}

static void expr_xor(void)
{
    expr_and();
    while (tok == '^') {
        next();
        expr_and();
        gen_op('^');
    }
}

static void expr_or(void)
{
    expr_xor();
    while (tok == '|') {
        next();
        expr_xor();
        gen_op('|');
    }
}

static void expr_land(void)
{
    expr_or();
    if (tok == TOK_LAND) {
    int t = 0;
    for(;;) {
        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
        CType ctb;
        ctb.t = VT_BOOL;
        gen_cast(&ctb);
        if (vtop->c.i) {
            vpop();
        } else {
            nocode_wanted++;
            while (tok == TOK_LAND) {
            next();
            expr_or();
            vpop();
            }
            nocode_wanted--;
            if (t)
              gsym(t);
            gen_cast(&int_type);
            break;
        }
        } else {
        if (!t)
          save_regs(1);
        t = gvtst(1, t);
        }
        if (tok != TOK_LAND) {
        if (t)
          vseti(VT_JMPI, t);
        else
          vpushi(1);
        break;
        }
        next();
        expr_or();
    }
    }
}

static void expr_lor(void)
{
    expr_land();
    if (tok == TOK_LOR) {
    int t = 0;
    for(;;) {
        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
        CType ctb;
        ctb.t = VT_BOOL;
        gen_cast(&ctb);
        if (!vtop->c.i) {
            vpop();
        } else {
            nocode_wanted++;
            while (tok == TOK_LOR) {
            next();
            expr_land();
            vpop();
            }
            nocode_wanted--;
            if (t)
              gsym(t);
            gen_cast(&int_type);
            break;
        }
        } else {
        if (!t)
          save_regs(1);
        t = gvtst(0, t);
        }
        if (tok != TOK_LOR) {
        if (t)
          vseti(VT_JMP, t);
        else
          vpushi(0);
        break;
        }
        next();
        expr_land();
    }
    }
}

/* Assuming vtop is a value used in a conditional context
   (i.e. compared with zero) return 0 if it's false, 1 if
   true and -1 if it can't be statically determined.  */
static int condition_3way(void)
{
    int c = -1;
    if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST &&
    (!(vtop->r & VT_SYM) ||
     !(vtop->sym->type.t & VT_WEAK))) {
    CType boolean;
    boolean.t = VT_BOOL;
    vdup();
    gen_cast(&boolean);
    c = vtop->c.i;
    vpop();
    }
    return c;
}

static void expr_cond(void)
{
    int tt, u, r1, r2, rc, t1, t2, bt1, bt2, islv, c, g;
    SValue sv;
    CType type, type1, type2;

    expr_lor();
    if (tok == '?') {
        next();
    c = condition_3way();
        g = (tok == ':' && gnu_ext);
        if (c < 0) {
            /* needed to avoid having different registers saved in
               each branch */
            if (is_float(vtop->type.t)) {
                rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64
                if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
                    rc = RC_ST0;
                }
#endif
            } else
                rc = RC_INT;
            gv(rc);
            save_regs(1);
            if (g)
                gv_dup();
            tt = gvtst(1, 0);

        } else {
            if (!g)
                vpop();
            tt = 0;
        }

        if (1) {
            if (c == 0)
                nocode_wanted++;
            if (!g)
                gexpr();

            type1 = vtop->type;
            sv = *vtop; /* save value to handle it later */
            vtop--; /* no vpop so that FP stack is not flushed */
            skip(':');

            u = 0;
            if (c < 0)
                u = gjmp(0);
            gsym(tt);

            if (c == 0)
                nocode_wanted--;
            if (c == 1)
                nocode_wanted++;
            expr_cond();
            if (c == 1)
                nocode_wanted--;

            type2 = vtop->type;
            t1 = type1.t;
            bt1 = t1 & VT_BTYPE;
            t2 = type2.t;
            bt2 = t2 & VT_BTYPE;
            /* cast operands to correct type according to ISOC rules */
            if (is_float(bt1) || is_float(bt2)) {
                if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
                    type.t = VT_LDOUBLE;

                } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
                    type.t = VT_DOUBLE;
                } else {
                    type.t = VT_FLOAT;
                }
            } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
                /* cast to biggest op */
                type.t = VT_LLONG;
                /* convert to unsigned if it does not fit in a long long */
                if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED) ||
                    (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED))
                    type.t |= VT_UNSIGNED;
            } else if (bt1 == VT_PTR || bt2 == VT_PTR) {
        /* If one is a null ptr constant the result type
           is the other.  */
        if (is_null_pointer (vtop))
          type = type1;
        else if (is_null_pointer (&sv))
          type = type2;
                /* XXX: test pointer compatibility, C99 has more elaborate
           rules here.  */
        else
          type = type1;
            } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) {
                /* XXX: test function pointer compatibility */
                type = bt1 == VT_FUNC ? type1 : type2;
            } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
                /* XXX: test structure compatibility */
                type = bt1 == VT_STRUCT ? type1 : type2;
            } else if (bt1 == VT_VOID || bt2 == VT_VOID) {
                /* NOTE: as an extension, we accept void on only one side */
                type.t = VT_VOID;
            } else {
                /* integer operations */
                type.t = VT_INT;
                /* convert to unsigned if it does not fit in an integer */
                if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED) ||
                    (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED))
                    type.t |= VT_UNSIGNED;
            }
            /* keep structs lvalue by transforming `(expr ? a : b)` to `*(expr ? &a : &b)` so
               that `(expr ? a : b).mem` does not error  with "lvalue expected" */
            islv = (vtop->r & VT_LVAL) && (sv.r & VT_LVAL) && VT_STRUCT == (type.t & VT_BTYPE);
            islv &= c < 0;

            /* now we convert second operand */
            if (c != 1) {
                gen_cast(&type);
                if (islv) {
                    mk_pointer(&vtop->type);
                    gaddrof();
                } else if (VT_STRUCT == (vtop->type.t & VT_BTYPE))
                    gaddrof();
            }

            rc = RC_INT;
            if (is_float(type.t)) {
                rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64
                if ((type.t & VT_BTYPE) == VT_LDOUBLE) {
                    rc = RC_ST0;
                }
#endif
            } else if ((type.t & VT_BTYPE) == VT_LLONG) {
                /* for long longs, we use fixed registers to avoid having
                   to handle a complicated move */
                rc = RC_IRET;
            }

            tt = r2 = 0;
            if (c < 0) {
                r2 = gv(rc);
                tt = gjmp(0);
            }
            gsym(u);

            /* this is horrible, but we must also convert first
               operand */
            if (c != 0) {
                *vtop = sv;
                gen_cast(&type);
                if (islv) {
                    mk_pointer(&vtop->type);
                    gaddrof();
                } else if (VT_STRUCT == (vtop->type.t & VT_BTYPE))
                    gaddrof();
            }

            if (c < 0) {
                r1 = gv(rc);
                move_reg(r2, r1, type.t);
                vtop->r = r2;
                gsym(tt);
                if (islv)
                    indir();
            }
        }
    }
}

static void expr_eq(void)
{
    int t;
    
    expr_cond();
    if (tok == '=' ||
        (tok >= TOK_A_MOD && tok <= TOK_A_DIV) ||
        tok == TOK_A_XOR || tok == TOK_A_OR ||
        tok == TOK_A_SHL || tok == TOK_A_SAR) {
        test_lvalue();
        t = tok;
        next();
        if (t == '=') {
            expr_eq();
        } else {
            vdup();
            expr_eq();
            gen_op(t & 0x7f);
        }
        vstore();
    }
}

ST_FUNC void gexpr(void)
{
    while (1) {
        expr_eq();
        if (tok != ',')
            break;
        vpop();
        next();
    }
}

/* parse a constant expression and return value in vtop.  */
static void expr_const1(void)
{
    const_wanted++;
    expr_cond();
    const_wanted--;
}

#if HAVE_LONG_LONG
/* parse an integer constant and return its value. */
static inline int64_t expr_const64(void)
{
    int64_t c;
    expr_const1();
    if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
        expect("constant expression");
    c = vtop->c.i;
    vpop();
    return c;
}
#else
/* parse an integer constant and return its value. */
static inline int32_t expr_const32(void)
{
    int32_t c;
    expr_const1();
    if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
        expect("constant expression");
    c = vtop->c.i;
    vpop();
    return c;
}
#endif

/* parse an integer constant and return its value.
   Complain if it doesn't fit 32bit (signed or unsigned).  */
ST_FUNC int expr_const(void)
{
    int c;
#if HAVE_LONG_LONG
    int64_t wc = expr_const64();
#else
    int32_t wc = expr_const32();
#endif
    c = wc;
    if (c != wc && (unsigned)c != wc)
        tcc_error("constant exceeds 32 bit");
    return c;
}

/* return the label token if current token is a label, otherwise
   return zero */
static int is_label(void)
{
    int last_tok;

    /* fast test first */
    if (tok < TOK_UIDENT)
        return 0;
    /* no need to save tokc because tok is an identifier */
    last_tok = tok;
    next();
    if (tok == ':') {
        return last_tok;
    } else {
        unget_tok(last_tok);
        return 0;
    }
}

#ifndef TCC_TARGET_ARM64
static void gfunc_return(CType *func_type)
{
    if ((func_type->t & VT_BTYPE) == VT_STRUCT) {
        CType type, ret_type;
        int ret_align, ret_nregs, regsize;
        ret_nregs = gfunc_sret(func_type, func_var, &ret_type,
                               &ret_align, &regsize);
        if (0 == ret_nregs) {
            /* if returning structure, must copy it to implicit
               first pointer arg location */
            type = *func_type;
            mk_pointer(&type);
            vset(&type, VT_LOCAL | VT_LVAL, func_vc);
            indir();
            vswap();
            /* copy structure value to pointer */
            vstore();
        } else {
            /* returning structure packed into registers */
            int r, size, addr, align;
            size = type_size(func_type,&align);
            if ((vtop->r != (VT_LOCAL | VT_LVAL) ||
                 (vtop->c.i & (ret_align-1)))
                && (align & (ret_align-1))) {
                loc = (loc - size) & -ret_align;
                addr = loc;
                type = *func_type;
                vset(&type, VT_LOCAL | VT_LVAL, addr);
                vswap();
                vstore();
                vpop();
                vset(&ret_type, VT_LOCAL | VT_LVAL, addr);
            }
            vtop->type = ret_type;
            if (is_float(ret_type.t))
                r = rc_fret(ret_type.t);
            else
                r = RC_IRET;

            if (ret_nregs == 1)
                gv(r);
            else {
                for (;;) {
                    vdup();
                    gv(r);
                    vpop();
                    if (--ret_nregs == 0)
                      break;
                    /* We assume that when a structure is returned in multiple
                       registers, their classes are consecutive values of the
                       suite s(n) = 2^n */
                    r <<= 1;
                    vtop->c.i += regsize;
                }
            }
        }
    } else if (is_float(func_type->t)) {
        gv(rc_fret(func_type->t));
    } else {
        gv(RC_IRET);
    }
    vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
}
#endif

static int case_cmp(const void *pa, const void *pb)
{
#if HAVE_LONG_LONG
    int64_t a = (*(struct case_t**) pa)->v1;
    int64_t b = (*(struct case_t**) pb)->v1;
#else
    int32_t a = (*(struct case_t**) pa)->v1;
    int32_t b = (*(struct case_t**) pb)->v1;
#endif
    return a < b ? -1 : a > b;
}

static void gcase(struct case_t **base, int len, int *bsym)
{
    struct case_t *p;
    int e;
    int ll = (vtop->type.t & VT_BTYPE) == VT_LLONG;
    gv(RC_INT);
    while (len > 4) {
        /* binary search */
        p = base[len/2];
        vdup();
    if (ll)
        vpushll(p->v2);
    else
        vpushi(p->v2);
        gen_op(TOK_LE);
        e = gtst(1, 0);
        vdup();
    if (ll)
        vpushll(p->v1);
    else
        vpushi(p->v1);
        gen_op(TOK_GE);
        gtst_addr(0, p->sym); /* v1 <= x <= v2 */
        /* x < v1 */
        gcase(base, len/2, bsym);
        if (cur_switch->def_sym)
            gjmp_addr(cur_switch->def_sym);
        else
            *bsym = gjmp(*bsym);
        /* x > v2 */
        gsym(e);
        e = len/2 + 1;
        base += e; len -= e;
    }
    /* linear scan */
    while (len--) {
        p = *base++;
        vdup();
    if (ll)
        vpushll(p->v2);
    else
        vpushi(p->v2);
        if (p->v1 == p->v2) {
            gen_op(TOK_EQ);
            gtst_addr(0, p->sym);
        } else {
            gen_op(TOK_LE);
            e = gtst(1, 0);
            vdup();
        if (ll)
            vpushll(p->v1);
        else
            vpushi(p->v1);
            gen_op(TOK_GE);
            gtst_addr(0, p->sym);
            gsym(e);
        }
    }
}

static void block(int *bsym, int *csym, int is_expr)
{
    int a, b, c, d, cond;
    Sym *s;

    /* generate line number info */
    if (tcc_state->do_debug)
        tcc_debug_line(tcc_state);

    if (is_expr) {
        /* default return value is (void) */
        vpushi(0);
        vtop->type.t = VT_VOID;
    }

    if (tok == TOK_IF) {
        /* if test */
    int saved_nocode_wanted = nocode_wanted;
        next();
        skip('(');
        gexpr();
        skip(')');
    cond = condition_3way();
        if (cond == 1)
            a = 0, vpop();
        else
            a = gvtst(1, 0);
        if (cond == 0)
        nocode_wanted |= 0x20000000;
        block(bsym, csym, 0);
    if (cond != 1)
        nocode_wanted = saved_nocode_wanted;
        c = tok;
        if (c == TOK_ELSE) {
            next();
            d = gjmp(0);
            gsym(a);
        if (cond == 1)
            nocode_wanted |= 0x20000000;
            block(bsym, csym, 0);
            gsym(d); /* patch else jmp */
        if (cond != 0)
        nocode_wanted = saved_nocode_wanted;
        } else
            gsym(a);
    } else if (tok == TOK_WHILE) {
    int saved_nocode_wanted;
    nocode_wanted &= ~0x20000000;
        next();
        d = ind;
        vla_sp_restore();
        skip('(');
        gexpr();
        skip(')');
        a = gvtst(1, 0);
        b = 0;
        ++local_scope;
    saved_nocode_wanted = nocode_wanted;
        block(&a, &b, 0);
    nocode_wanted = saved_nocode_wanted;
        --local_scope;
        gjmp_addr(d);
        gsym(a);
        gsym_addr(b, d);
    } else if (tok == '{') {
        Sym *llabel;
        int block_vla_sp_loc = vla_sp_loc, saved_vlas_in_scope = vlas_in_scope;

        next();
        /* record local declaration stack position */
        s = local_stack;
        llabel = local_label_stack;
        ++local_scope;
        
        /* handle local labels declarations */
        if (tok == TOK_LABEL) {
            next();
            for(;;) {
                if (tok < TOK_UIDENT)
                    expect("label identifier");
                label_push(&local_label_stack, tok, LABEL_DECLARED);
                next();
                if (tok == ',') {
                    next();
                } else {
                    skip(';');
                    break;
                }
            }
        }
        while (tok != '}') {
        if ((a = is_label()))
        unget_tok(a);
        else
            decl(VT_LOCAL);
            if (tok != '}') {
                if (is_expr)
                    vpop();
                block(bsym, csym, is_expr);
            }
        }
        /* pop locally defined labels */
        label_pop(&local_label_stack, llabel);
        /* pop locally defined symbols */
        --local_scope;
    /* In the is_expr case (a statement expression is finished here),
       vtop might refer to symbols on the local_stack.  Either via the
       type or via vtop->sym.  We can't pop those nor any that in turn
       might be referred to.  To make it easier we don't roll back
       any symbols in that case; some upper level call to block() will
       do that.  We do have to remove such symbols from the lookup
       tables, though.  sym_pop will do that.  */
    sym_pop(&local_stack, s, is_expr);

        /* Pop VLA frames and restore stack pointer if required */
        if (vlas_in_scope > saved_vlas_in_scope) {
            vla_sp_loc = saved_vlas_in_scope ? block_vla_sp_loc : vla_sp_root_loc;
            vla_sp_restore();
        }
        vlas_in_scope = saved_vlas_in_scope;
        
        next();
    } else if (tok == TOK_RETURN) {
        next();
        if (tok != ';') {
            gexpr();
            gen_assign_cast(&func_vt);
            gfunc_return(&func_vt);
        }
        skip(';');
        /* jump unless last stmt in top-level block */
        if (tok != '}' || local_scope != 1)
            rsym = gjmp(rsym);
    nocode_wanted |= 0x20000000;
    } else if (tok == TOK_BREAK) {
        /* compute jump */
        if (!bsym)
            tcc_error("cannot break");
        *bsym = gjmp(*bsym);
        next();
        skip(';');
    nocode_wanted |= 0x20000000;
    } else if (tok == TOK_CONTINUE) {
        /* compute jump */
        if (!csym)
            tcc_error("cannot continue");
        vla_sp_restore_root();
        *csym = gjmp(*csym);
        next();
        skip(';');
    } else if (tok == TOK_FOR) {
        int e;
    int saved_nocode_wanted;
    nocode_wanted &= ~0x20000000;
        next();
        skip('(');
        s = local_stack;
        ++local_scope;
        if (tok != ';') {
            /* c99 for-loop init decl? */
            if (!decl0(VT_LOCAL, 1, NULL)) {
                /* no, regular for-loop init expr */
                gexpr();
                vpop();
            }
        }
        skip(';');
        d = ind;
        c = ind;
        vla_sp_restore();
        a = 0;
        b = 0;
        if (tok != ';') {
            gexpr();
            a = gvtst(1, 0);
        }
        skip(';');
        if (tok != ')') {
            e = gjmp(0);
            c = ind;
            vla_sp_restore();
            gexpr();
            vpop();
            gjmp_addr(d);
            gsym(e);
        }
        skip(')');
    saved_nocode_wanted = nocode_wanted;
        block(&a, &b, 0);
    nocode_wanted = saved_nocode_wanted;
        gjmp_addr(c);
        gsym(a);
        gsym_addr(b, c);
        --local_scope;
        sym_pop(&local_stack, s, 0);

    } else 
    if (tok == TOK_DO) {
    int saved_nocode_wanted;
    nocode_wanted &= ~0x20000000;
        next();
        a = 0;
        b = 0;
        d = ind;
        vla_sp_restore();
    saved_nocode_wanted = nocode_wanted;
        block(&a, &b, 0);
        skip(TOK_WHILE);
        skip('(');
        gsym(b);
    gexpr();
    c = gvtst(0, 0);
    gsym_addr(c, d);
    nocode_wanted = saved_nocode_wanted;
        skip(')');
        gsym(a);
        skip(';');
    } else
    if (tok == TOK_SWITCH) {
        struct switch_t *saved, sw;
    int saved_nocode_wanted = nocode_wanted;
    SValue switchval;
        next();
        skip('(');
        gexpr();
        skip(')');
    switchval = *vtop--;
        a = 0;
        b = gjmp(0); /* jump to first case */
        sw.p = NULL; sw.n = 0; sw.def_sym = 0;
        saved = cur_switch;
        cur_switch = &sw;
        block(&a, csym, 0);
    nocode_wanted = saved_nocode_wanted;
        a = gjmp(a); /* add implicit break */
        /* case lookup */
        gsym(b);
        qsort(sw.p, sw.n, sizeof(void*), case_cmp);
        for (b = 1; b < sw.n; b++)
            if (sw.p[b - 1]->v2 >= sw.p[b]->v1)
                tcc_error("duplicate case value");
        /* Our switch table sorting is signed, so the compared
           value needs to be as well when it's 64bit.  */
        if ((switchval.type.t & VT_BTYPE) == VT_LLONG)
            switchval.type.t &= ~VT_UNSIGNED;
        vpushv(&switchval);
        gcase(sw.p, sw.n, &a);
        vpop();
        if (sw.def_sym)
          gjmp_addr(sw.def_sym);
        dynarray_reset(&sw.p, &sw.n);
        cur_switch = saved;
        /* break label */
        gsym(a);
    } else
    if (tok == TOK_CASE) {
        struct case_t *cr = tcc_malloc(sizeof(struct case_t));
        if (!cur_switch)
            expect("switch");
    nocode_wanted &= ~0x20000000;
        next();
#if HAVE_LONG_LONG
        cr->v1 = cr->v2 = expr_const64();
#else
        cr->v1 = cr->v2 = expr_const32();
#endif
        if (gnu_ext && tok == TOK_DOTS) {
            next();
#if HAVE_LONG_LONG
            cr->v2 = expr_const64();
#else
            cr->v2 = expr_const32();
#endif
            if (cr->v2 < cr->v1)
                tcc_warning("empty case range");
        }
        cr->sym = ind;
        dynarray_add(&cur_switch->p, &cur_switch->n, cr);
        skip(':');
        is_expr = 0;
        goto block_after_label;
    } else 
    if (tok == TOK_DEFAULT) {
        next();
        skip(':');
        if (!cur_switch)
            expect("switch");
        if (cur_switch->def_sym)
            tcc_error("too many 'default'");
        cur_switch->def_sym = ind;
        is_expr = 0;
        goto block_after_label;
    } else
    if (tok == TOK_GOTO) {
        next();
        if (tok == '*' && gnu_ext) {
            /* computed goto */
            next();
            gexpr();
            if ((vtop->type.t & VT_BTYPE) != VT_PTR)
                expect("pointer");
            ggoto();
        } else if (tok >= TOK_UIDENT) {
            s = label_find(tok);
            /* put forward definition if needed */
            if (!s) {
                s = label_push(&global_label_stack, tok, LABEL_FORWARD);
            } else {
                if (s->r == LABEL_DECLARED)
                    s->r = LABEL_FORWARD;
            }
            vla_sp_restore_root();
        if (s->r & LABEL_FORWARD)
                s->jnext = gjmp(s->jnext);
            else
                gjmp_addr(s->jnext);
            next();
        } else {
            expect("label identifier");
        }
        skip(';');
    } else if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3) {
        asm_instr();
    } else {
        b = is_label();
        if (b) {
            /* label case */
        next();
            s = label_find(b);
            if (s) {
                if (s->r == LABEL_DEFINED)
                    tcc_error("duplicate label '%s'", get_tok_str(s->v, NULL));
                gsym(s->jnext);
                s->r = LABEL_DEFINED;
            } else {
                s = label_push(&global_label_stack, b, LABEL_DEFINED);
            }
            s->jnext = ind;
            vla_sp_restore();
            /* we accept this, but it is a mistake */
        block_after_label:
        nocode_wanted &= ~0x20000000;
            if (tok == '}') {
                tcc_warning("deprecated use of label at end of compound statement");
            } else {
                if (is_expr)
                    vpop();
                block(bsym, csym, is_expr);
            }
        } else {
            /* expression case */
            if (tok != ';') {
                if (is_expr) {
                    vpop();
                    gexpr();
                } else {
                    gexpr();
                    vpop();
                }
            }
            skip(';');
        }
    }
}

/* This skips over a stream of tokens containing balanced {} and ()
   pairs, stopping at outer ',' ';' and '}'.  If STR then allocates
   and stores the skipped tokens in *STR.  This doesn't check if
   () and {} are nested correctly, i.e. "({)}" is accepted.  */
static void skip_or_save_block(TokenString **str)
{
    int level = 0;
    if (str)
      *str = tok_str_alloc();

    while ((level > 0 || (tok != '}' && tok != ',' && tok != ';'))) {
    int t;
    if (tok == TOK_EOF) {
         if (str || level > 0)
           tcc_error("unexpected end of file");
         else
           break;
    }
    if (str)
      tok_str_add_tok(*str);
    t = tok;
    next();
    if (t == '{' || t == '(') {
        level++;
    } else if (t == '}' || t == ')') {
        level--;
        if (level == 0)
          break;
    }
    }
    if (str) {
    tok_str_add(*str, -1);
    tok_str_add(*str, 0);
    }
}

#define EXPR_CONST 1
#define EXPR_ANY   2

static void parse_init_elem(int expr_type)
{
    int saved_global_expr;
    switch(expr_type) {
    case EXPR_CONST:
        /* compound literals must be allocated globally in this case */
        saved_global_expr = global_expr;
        global_expr = 1;
        expr_const1();
        global_expr = saved_global_expr;
        /* NOTE: symbols are accepted, as well as lvalue for anon symbols
       (compound literals).  */
        if (((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST
         && ((vtop->r & (VT_SYM|VT_LVAL)) != (VT_SYM|VT_LVAL)
         || vtop->sym->v < SYM_FIRST_ANOM))
#ifdef TCC_TARGET_PE
            || (vtop->type.t & VT_IMPORT)
#endif
        )
            tcc_error("initializer element is not constant");
        break;
    case EXPR_ANY:
        expr_eq();
        break;
    }
}

/* put zeros for variable based init */
static void init_putz(Section *sec, unsigned long c, int size)
{
    if (sec) {
        /* nothing to do because globals are already set to zero */
    } else {
#if BOOTSTRAP && __arm__
        vpush_global_sym(&func_old_type, TOK___memset);
#else
        vpush_global_sym(&func_old_type, TOK_memset);
#endif
        vseti(VT_LOCAL, c);
#ifdef TCC_TARGET_ARM
        vpushs(size);
        vpushi(0);
#else
        vpushi(0);
        vpushs(size);
#endif
        gfunc_call(3);
    }
}

/* t is the array or struct type. c is the array or struct
   address. cur_field is the pointer to the current
   field, for arrays the 'c' member contains the current start
   index.  'size_only' is true if only size info is needed (only used
   in arrays).  al contains the already initialized length of the
   current container (starting at c).  This returns the new length of that.  */
static int decl_designator(CType *type, Section *sec, unsigned long c,
                           Sym **cur_field, int size_only, int al)
{
    Sym *s, *f;
    int index, index_last, align, l, nb_elems, elem_size;
    unsigned long corig = c;

    elem_size = 0;
    nb_elems = 1;
    if (gnu_ext && (l = is_label()) != 0)
        goto struct_field;
    /* NOTE: we only support ranges for last designator */
    while (nb_elems == 1 && (tok == '[' || tok == '.')) {
        if (tok == '[') {
            if (!(type->t & VT_ARRAY))
                expect("array type");
            next();
            index = index_last = expr_const();
            if (tok == TOK_DOTS && gnu_ext) {
                next();
                index_last = expr_const();
            }
            skip(']');
            s = type->ref;
        if (index < 0 || (s->c >= 0 && index_last >= s->c) ||
        index_last < index)
            tcc_error("invalid index");
            if (cur_field)
        (*cur_field)->c = index_last;
            type = pointed_type(type);
            elem_size = type_size(type, &align);
            c += index * elem_size;
            nb_elems = index_last - index + 1;
        } else {
            next();
            l = tok;
        struct_field:
            next();
            if ((type->t & VT_BTYPE) != VT_STRUCT)
                expect("struct/union type");
        f = find_field(type, l);
            if (!f)
                expect("field");
            if (cur_field)
                *cur_field = f;
        type = &f->type;
            c += f->c;
        }
        cur_field = NULL;
    }
    if (!cur_field) {
        if (tok == '=') {
            next();
        } else if (!gnu_ext) {
        expect("=");
        }
    } else {
        if (type->t & VT_ARRAY) {
        index = (*cur_field)->c;
        if (type->ref->c >= 0 && index >= type->ref->c)
            tcc_error("index too large");
            type = pointed_type(type);
            c += index * type_size(type, &align);
        } else {
            f = *cur_field;
        while (f && (f->v & SYM_FIRST_ANOM) && (f->type.t & VT_BITFIELD))
            *cur_field = f = f->next;
            if (!f)
                tcc_error("too many field init");
        type = &f->type;
            c += f->c;
        }
    }
    /* must put zero in holes (note that doing it that way
       ensures that it even works with designators) */
    if (!size_only && c - corig > al)
    init_putz(sec, corig + al, c - corig - al);
    decl_initializer(type, sec, c, 0, size_only);

    /* XXX: make it more general */
    if (!size_only && nb_elems > 1) {
        unsigned long c_end;
        uint8_t *src, *dst;
        int i;

        if (!sec) {
        vset(type, VT_LOCAL|VT_LVAL, c);
        for (i = 1; i < nb_elems; i++) {
        vset(type, VT_LOCAL|VT_LVAL, c + elem_size * i);
        vswap();
        vstore();
        }
        vpop();
    } else {
        c_end = c + nb_elems * elem_size;
        if (c_end > sec->data_allocated)
            section_realloc(sec, c_end);
        src = sec->data + c;
        dst = src;
        for(i = 1; i < nb_elems; i++) {
        dst += elem_size;
        memcpy(dst, src, elem_size);
        }
    }
    }
    c += nb_elems * type_size(type, &align);
    if (c - corig > al)
      al = c - corig;
    return al;
}

#if BOOTSTRAP && HAVE_FLOAT
struct long_double
{
  int one;
  int two;
  int three;
};
#endif

/* store a value or an expression directly in global data or in local array */
static void init_putv(CType *type, Section *sec, unsigned long c)
{
    int bt, bit_pos, bit_size;
    void *ptr;
    unsigned long long bit_mask;
    CType dtype;

    dtype = *type;
    dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */

    if (sec) {
    int size, align;
        /* XXX: not portable */
        /* XXX: generate error if incorrect relocation */
        gen_assign_cast(&dtype);
        bt = type->t & VT_BTYPE;
    size = type_size(type, &align);
    section_reserve(sec, c + size);
        ptr = sec->data + c;
        /* XXX: make code faster ? */
        if (!(type->t & VT_BITFIELD)) {
            bit_pos = 0;
            bit_size = PTR_SIZE * 8;
            bit_mask = -1LL;
        } else {
            bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f;
            bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
            bit_mask = (1LL << bit_size) - 1;
        }
    if ((vtop->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST) &&
        vtop->sym->v >= SYM_FIRST_ANOM &&
        /* XXX This rejects compound literals like
           '(void *){ptr}'.  The problem is that '&sym' is
           represented the same way, which would be ruled out
           by the SYM_FIRST_ANOM check above, but also '"string"'
           in 'char *p = "string"' is represented the same
           with the type being VT_PTR and the symbol being an
           anonymous one.  That is, there's no difference in vtop
           between '(void *){x}' and '&(void *){x}'.  Ignore
           pointer typed entities here.  Hopefully no real code
           will every use compound literals with scalar type.  */
        (vtop->type.t & VT_BTYPE) != VT_PTR) {
        /* These come from compound literals, memcpy stuff over.  */
        Section *ssec;
        ElfW(Sym) *esym;
        ElfW_Rel *rel;
        esym = &((ElfW(Sym) *)symtab_section->data)[vtop->sym->c];
        ssec = tcc_state->sections[esym->st_shndx];
        memmove (ptr, ssec->data + esym->st_value, size);
        if (ssec->reloc) {
        /* We need to copy over all memory contents, and that
           includes relocations.  Use the fact that relocs are
           created it order, so look from the end of relocs
           until we hit one before the copied region.  */
        int num_relocs = ssec->reloc->data_offset / sizeof(*rel);
        rel = (ElfW_Rel*)(ssec->reloc->data + ssec->reloc->data_offset);
        while (num_relocs--) {
            rel--;
            if (rel->r_offset >= esym->st_value + size)
              continue;
            if (rel->r_offset < esym->st_value)
              break;
            /* Note: if the same fields are initialized multiple
               times (possible with designators) then we possibly
               add multiple relocations for the same offset here.
               That would lead to wrong code, the last reloc needs
               to win.  We clean this up later after the whole
               initializer is parsed.  */
            put_elf_reloca(symtab_section, sec,
                   c + rel->r_offset - esym->st_value,
                   ELFW(R_TYPE)(rel->r_info),
                   ELFW(R_SYM)(rel->r_info),
#if PTR_SIZE == 8
                   rel->r_addend
#else
                   0
#endif
                  );
        }
        }
    } else {
        if ((vtop->r & VT_SYM) &&
        (bt == VT_BYTE ||
         bt == VT_SHORT ||
         bt == VT_DOUBLE ||
         bt == VT_LDOUBLE ||
#if PTR_SIZE == 8
         (bt == VT_LLONG && bit_size != 64) ||
         bt == VT_INT
#else
         bt == VT_LLONG ||
         (bt == VT_INT && bit_size != 32)
#endif
        ))
          tcc_error("initializer element is not computable at load time");
        switch(bt) {
        /* XXX: when cross-compiling we assume that each type has the
           same representation on host and target, which is likely to
           be wrong in the case of long double */
        case VT_BOOL:
        vtop->c.i = (vtop->c.i != 0);
        case VT_BYTE:
        *(char *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
        break;
        case VT_SHORT:
        *(short *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
        break;
        case VT_FLOAT:
#if HAVE_FLOAT
#if !(BOOTSTRAP && __arm__)
        *(float*)ptr = vtop->c.f;
#else
                {
                  long *lptr = ptr;
                  *lptr = vtop->c.f;
                }
#endif
#endif
        break;
        case VT_DOUBLE:
#if HAVE_FLOAT
#if !(BOOTSTRAP && __arm__)
        *(double *)ptr = vtop->c.d;
#else
                {
                  long long *llptr = ptr;
                  *llptr = vtop->c.d;
                }
#endif
#endif
        break;
        case VT_LDOUBLE:
#if HAVE_FLOAT
                if (sizeof(long double) == LDOUBLE_SIZE)
#if !(BOOTSTRAP && __arm__)
            *(long double *)ptr = vtop->c.ld;
#else
                {
                  // XXX TODO: breaks on mescc/mes-tcc based build
                  // maybe disable with HAVE_LONG_DOUBLE?
                  //struct long_double *ldptr = ptr;
                  //struct long_double tmp = (struct long_double)vtop->c.ld;
                  //*ldptr = (struct long_double)tmp;
                }
#endif
        else if (sizeof(double) == LDOUBLE_SIZE)
            *(double *)ptr = (double)vtop->c.ld;
#if (defined __i386__ || defined __x86_64__) && (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64)
                else if (sizeof (long double) >= 10)
                    memcpy(memset(ptr, 0, LDOUBLE_SIZE), &vtop->c.ld, 10);
#if defined (__TINYC__)
                else if (sizeof (long double) == sizeof (double))
                    __asm__("fldl %1\nfstpt %0\n" : "=m"
                        (memset(ptr, 0, LDOUBLE_SIZE), ptr) : "m" (vtop->c.ld));
#endif
#endif
        else
                    tcc_error("can't cross compile long double constants");
#endif
        break;
#if PTR_SIZE != 8
        case VT_LLONG:
        *(long long *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
        break;
#else
        case VT_LLONG:
#endif
        case VT_PTR:
        {
            addr_t val = (vtop->c.i & bit_mask) << bit_pos;
#if PTR_SIZE == 8
            if (vtop->r & VT_SYM)
              greloca(sec, vtop->sym, c, R_DATA_PTR, val);
            else
              *(addr_t *)ptr |= val;
#else
            if (vtop->r & VT_SYM)
              greloc(sec, vtop->sym, c, R_DATA_PTR);
            *(addr_t *)ptr |= val;
#endif
            break;
        }
        default:
        {
            int val = (vtop->c.i & bit_mask) << bit_pos;
#if PTR_SIZE == 8
            if (vtop->r & VT_SYM)
              greloca(sec, vtop->sym, c, R_DATA_PTR, val);
            else
              *(int *)ptr |= val;
#else
            if (vtop->r & VT_SYM)
              greloc(sec, vtop->sym, c, R_DATA_PTR);
            *(int *)ptr |= val;
#endif
            break;
        }
        }
    }
        vtop--;
    } else {
        vset(&dtype, VT_LOCAL|VT_LVAL, c);
        vswap();
        vstore();
        vpop();
    }
}

/* 't' contains the type and storage info. 'c' is the offset of the
   object in section 'sec'. If 'sec' is NULL, it means stack based
   allocation. 'first' is true if array '{' must be read (multi
   dimension implicit array init handling). 'size_only' is true if
   size only evaluation is wanted (only for arrays). */
static void decl_initializer(CType *type, Section *sec, unsigned long c, 
                             int first, int size_only)
{
    int len, n, no_oblock, nb, i;
    int size1, align1;
    int have_elem;
    Sym *s, *f;
    Sym indexsym;
    CType *t1;

    /* If we currently are at an '}' or ',' we have read an initializer
       element in one of our callers, and not yet consumed it.  */
    have_elem = tok == '}' || tok == ',';
    if (!have_elem && tok != '{' &&
    /* In case of strings we have special handling for arrays, so
       don't consume them as initializer value (which would commit them
       to some anonymous symbol).  */
    tok != TOK_LSTR && tok != TOK_STR &&
    !size_only) {
    parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
    have_elem = 1;
    }

    if (have_elem &&
    !(type->t & VT_ARRAY) &&
    /* Use i_c_parameter_t, to strip toplevel qualifiers.
       The source type might have VT_CONSTANT set, which is
       of course assignable to non-const elements.  */
    is_compatible_parameter_types(type, &vtop->type)) {
        init_putv(type, sec, c);
    } else if (type->t & VT_ARRAY) {
        s = type->ref;
        n = s->c;
        t1 = pointed_type(type);
        size1 = type_size(t1, &align1);

        no_oblock = 1;
        if ((first && tok != TOK_LSTR && tok != TOK_STR) || 
            tok == '{') {
            if (tok != '{')
                tcc_error("character array initializer must be a literal,"
                    " optionally enclosed in braces");
            skip('{');
            no_oblock = 0;
        }

        /* only parse strings here if correct type (otherwise: handle
           them as ((w)char *) expressions */
        if ((tok == TOK_LSTR && 
#ifdef TCC_TARGET_PE
             (t1->t & VT_BTYPE) == VT_SHORT && (t1->t & VT_UNSIGNED)
#else
             (t1->t & VT_BTYPE) == VT_INT
#endif
            ) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_BYTE)) {
        len = 0;
            while (tok == TOK_STR || tok == TOK_LSTR) {
                int cstr_len, ch;

                /* compute maximum number of chars wanted */
                if (tok == TOK_STR)
                    cstr_len = tokc.str.size;
                else
                    cstr_len = tokc.str.size / sizeof(nwchar_t);
                cstr_len--;
                nb = cstr_len;
                if (n >= 0 && nb > (n - len))
                    nb = n - len;
                if (!size_only) {
                    if (cstr_len > nb)
                        tcc_warning("initializer-string for array is too long");
                    /* in order to go faster for common case (char
                       string in global variable, we handle it
                       specifically */
                    if (sec && tok == TOK_STR && size1 == 1) {
                        memcpy(sec->data + c + len, tokc.str.data, nb);
                    } else {
                        for(i=0;i<nb;i++) {
                            if (tok == TOK_STR)
                                ch = ((unsigned char *)tokc.str.data)[i];
                            else
                                ch = ((nwchar_t *)tokc.str.data)[i];
                vpushi(ch);
                            init_putv(t1, sec, c + (len + i) * size1);
                        }
                    }
                }
                len += nb;
                next();
            }
            /* only add trailing zero if enough storage (no
               warning in this case since it is standard) */
            if (n < 0 || len < n) {
                if (!size_only) {
            vpushi(0);
                    init_putv(t1, sec, c + (len * size1));
                }
                len++;
            }
        len *= size1;
        } else {
        indexsym.c = 0;
        f = &indexsym;

          do_init_list:
        len = 0;
        while (tok != '}' || have_elem) {
        len = decl_designator(type, sec, c, &f, size_only, len);
        have_elem = 0;
        if (type->t & VT_ARRAY) {
            ++indexsym.c;
            /* special test for multi dimensional arrays (may not
               be strictly correct if designators are used at the
               same time) */
            if (no_oblock && len >= n*size1)
                break;
        } else {
            if (s->type.t == TOK_UNION)
                f = NULL;
            else
                f = f->next;
            if (no_oblock && f == NULL)
                break;
        }

        if (tok == '}')
            break;
        skip(',');
        }
        }
        /* put zeros at the end */
    if (!size_only && len < n*size1)
        init_putz(sec, c + len, n*size1 - len);
        if (!no_oblock)
            skip('}');
        /* patch type size if needed, which happens only for array types */
        if (n < 0)
            s->c = size1 == 1 ? len : ((len + size1 - 1)/size1);
    } else if ((type->t & VT_BTYPE) == VT_STRUCT) {
    size1 = 1;
        no_oblock = 1;
        if (first || tok == '{') {
            skip('{');
            no_oblock = 0;
        }
        s = type->ref;
        f = s->next;
        n = s->c;
    goto do_init_list;
    } else if (tok == '{') {
        next();
        decl_initializer(type, sec, c, first, size_only);
        skip('}');
    } else if (size_only) {
    /* If we supported only ISO C we wouldn't have to accept calling
       this on anything than an array size_only==1 (and even then
       only on the outermost level, so no recursion would be needed),
       because initializing a flex array member isn't supported.
       But GNU C supports it, so we need to recurse even into
       subfields of structs and arrays when size_only is set.  */
        /* just skip expression */
    do {
        skip_or_save_block(NULL);
    } while (tok != '}' && tok != ',' && tok != -1);
    } else {
    if (!have_elem) {
        /* This should happen only when we haven't parsed
           the init element above for fear of committing a
           string constant to memory too early.  */
        if (tok != TOK_STR && tok != TOK_LSTR)
          expect("string constant");
        parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
    }
        init_putv(type, sec, c);
    }
}

/* parse an initializer for type 't' if 'has_init' is non zero, and
   allocate space in local or global data space ('r' is either
   VT_LOCAL or VT_CONST). If 'v' is non zero, then an associated
   variable 'v' of scope 'scope' is declared before initializers
   are parsed. If 'v' is zero, then a reference to the new object
   is put in the value stack. If 'has_init' is 2, a special parsing
   is done to handle string constants. */
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, 
                                   int has_init, int v, int scope)
{
    int size, align, addr;
    ParseState saved_parse_state = {0};
    TokenString *init_str = NULL;
    Section *sec;
    Sym *flexible_array;

    flexible_array = NULL;
    if ((type->t & VT_BTYPE) == VT_STRUCT) {
        Sym *field = type->ref->next;
        if (field) {
            while (field->next)
                field = field->next;
            if (field->type.t & VT_ARRAY && field->type.ref->c < 0)
                flexible_array = field;
        }
    }

    size = type_size(type, &align);
    /* If unknown size, we must evaluate it before
       evaluating initializers because
       initializers can generate global data too
       (e.g. string pointers or ISOC99 compound
       literals). It also simplifies local
       initializers handling */
    if (size < 0 || (flexible_array && has_init)) {
        if (!has_init) 
            tcc_error("unknown type size");
        /* get all init string */
        if (has_init == 2) {
        init_str = tok_str_alloc();
            /* only get strings */
            while (tok == TOK_STR || tok == TOK_LSTR) {
                tok_str_add_tok(init_str);
                next();
            }
        tok_str_add(init_str, -1);
        tok_str_add(init_str, 0);
        } else {
        skip_or_save_block(&init_str);
        }
        
        /* compute size */
        save_parse_state(&saved_parse_state);

        begin_macro(init_str, 1);
        next();
        decl_initializer(type, NULL, 0, 1, 1);
        /* prepare second initializer parsing */
        macro_ptr = init_str->str;
        next();
        
        /* if still unknown size, error */
        size = type_size(type, &align);
        if (size < 0) 
            tcc_error("unknown type size");
    }
    /* If there's a flex member and it was used in the initializer
       adjust size.  */
    if (flexible_array &&
    flexible_array->type.ref->c > 0)
        size += flexible_array->type.ref->c
            * pointed_size(&flexible_array->type);
    /* take into account specified alignment if bigger */
    if (ad->a.aligned) {
    int speca = 1 << (ad->a.aligned - 1);
        if (speca > align)
            align = speca;
    } else if (ad->a.packed) {
        align = 1;
    }
    if ((r & VT_VALMASK) == VT_LOCAL) {
        sec = NULL;
#ifdef CONFIG_TCC_BCHECK
        if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) {
            loc--;
        }
#endif
        loc = (loc - size) & -align;
        addr = loc;
#ifdef CONFIG_TCC_BCHECK
        /* handles bounds */
        /* XXX: currently, since we do only one pass, we cannot track
           '&' operators, so we add only arrays */
        if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) {
            addr_t *bounds_ptr;
            /* add padding between regions */
            loc--;
            /* then add local bound info */
            bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(addr_t));
            bounds_ptr[0] = addr;
            bounds_ptr[1] = size;
        }
#endif
        if (v) {
            /* local variable */
#ifdef CONFIG_TCC_ASM
        if (ad->asm_label) {
        int reg = asm_parse_regvar(ad->asm_label);
        if (reg >= 0)
            r = (r & ~VT_VALMASK) | reg;
        }
#endif
            sym_push(v, type, r, addr);
        } else {
            /* push local reference */
            vset(type, r, addr);
        }
    } else {
        Sym *sym = NULL;
        if (v && scope == VT_CONST) {
            /* see if the symbol was already defined */
            sym = sym_find(v);
            if (sym) {
                patch_storage(sym, type);
                if (sym->type.t & VT_EXTERN) {
                    /* if the variable is extern, it was not allocated */
                    sym->type.t &= ~VT_EXTERN;
                    /* set array size if it was omitted in extern
                       declaration */
                    if ((sym->type.t & VT_ARRAY) && 
                        sym->type.ref->c < 0 &&
                        type->ref->c >= 0)
                        sym->type.ref->c = type->ref->c;
                } else if (!has_init) {
                    /* we accept several definitions of the same
                       global variable. this is tricky, because we
                       must play with the SHN_COMMON type of the symbol */
                    /* no init data, we won't add more to the symbol */
                    update_storage(sym);
                    goto no_alloc;
                } else if (sym->c) {
                    ElfW(Sym) *esym;
                    esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
                    if (esym->st_shndx == data_section->sh_num)
                        tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
                }
            }
        }

        /* allocate symbol in corresponding section */
        sec = ad->section;
        if (!sec) {
            if (has_init)
                sec = data_section;
            else if (tcc_state->nocommon)
                sec = bss_section;
        }

        if (sec) {
        addr = section_add(sec, size, align);
#ifdef CONFIG_TCC_BCHECK
            /* add padding if bound check */
            if (tcc_state->do_bounds_check)
                section_add(sec, 1, 1);
#endif
        } else {
            addr = align; /* SHN_COMMON is special, symbol value is align */
        sec = common_section;
        }

        if (v) {
            if (!sym) {
                sym = sym_push(v, type, r | VT_SYM, 0);
                sym->asm_label = ad->asm_label;
            }
            /* update symbol definition */
        put_extern_sym(sym, sec, addr, size);
        } else {
            /* push global reference */
            sym = get_sym_ref(type, sec, addr, size);
        vpushsym(type, sym);
        vtop->r |= r;
        }

#ifdef CONFIG_TCC_BCHECK
        /* handles bounds now because the symbol must be defined
           before for the relocation */
        if (tcc_state->do_bounds_check) {
            addr_t *bounds_ptr;

            greloca(bounds_section, sym, bounds_section->data_offset, R_DATA_PTR, 0);
            /* then add global bound info */
            bounds_ptr = section_ptr_add(bounds_section, 2 * sizeof(addr_t));
            bounds_ptr[0] = 0; /* relocated */
            bounds_ptr[1] = size;
        }
#endif
    }

    if (type->t & VT_VLA) {
        int a;

        /* save current stack pointer */
        if (vlas_in_scope == 0) {
            if (vla_sp_root_loc == -1)
                vla_sp_root_loc = (loc -= PTR_SIZE);
            gen_vla_sp_save(vla_sp_root_loc);
        }

        vla_runtime_type_size(type, &a);
        gen_vla_alloc(type, a);
        gen_vla_sp_save(addr);
        vla_sp_loc = addr;
        vlas_in_scope++;

    } else if (has_init) {
    size_t oldreloc_offset = 0;
    if (sec && sec->reloc)
      oldreloc_offset = sec->reloc->data_offset;
        decl_initializer(type, sec, addr, 1, 0);
    if (sec && sec->reloc)
      squeeze_multi_relocs(sec, oldreloc_offset);
        /* patch flexible array member size back to -1, */
        /* for possible subsequent similar declarations */
        if (flexible_array)
            flexible_array->type.ref->c = -1;
    }

 no_alloc:
    /* restore parse state if needed */
    if (init_str) {
        end_macro();
        restore_parse_state(&saved_parse_state);
    }
}

/* parse a function defined by symbol 'sym' and generate its code in
   'cur_text_section' */
static void gen_function(Sym *sym)
{
    nocode_wanted = 0;
    ind = cur_text_section->data_offset;
    /* NOTE: we patch the symbol size later */
    put_extern_sym(sym, cur_text_section, ind, 0);
    funcname = get_tok_str(sym->v, NULL);
    func_ind = ind;
    /* Initialize VLA state */
    vla_sp_loc = -1;
    vla_sp_root_loc = -1;
    /* put debug symbol */
    tcc_debug_funcstart(tcc_state, sym);
    /* push a dummy symbol to enable local sym storage */
    sym_push2(&local_stack, SYM_FIELD, 0, 0);
    local_scope = 1; /* for function parameters */
    gfunc_prolog(&sym->type);
    local_scope = 0;
    rsym = 0;
    block(NULL, NULL, 0);
    nocode_wanted = 0;
    gsym(rsym);
    gfunc_epilog();
    cur_text_section->data_offset = ind;
    label_pop(&global_label_stack, NULL);
    /* reset local stack */
    local_scope = 0;
    sym_pop(&local_stack, NULL, 0);
    /* end of function */
    /* patch symbol size */
    ((ElfW(Sym) *)symtab_section->data)[sym->c].st_size = 
        ind - func_ind;
    tcc_debug_funcend(tcc_state, ind - func_ind);
    /* It's better to crash than to generate wrong code */
    cur_text_section = NULL;
    funcname = ""; /* for safety */
    func_vt.t = VT_VOID; /* for safety */
    func_var = 0; /* for safety */
    ind = 0; /* for safety */
    nocode_wanted = 1;
    check_vstack();
}

static void gen_inline_functions(TCCState *s)
{
    Sym *sym;
    int inline_generated, i, ln;
    struct InlineFunc *fn;

    ln = file->line_num;
    /* iterate while inline function are referenced */
    for(;;) {
        inline_generated = 0;
        for (i = 0; i < s->nb_inline_fns; ++i) {
            fn = s->inline_fns[i];
            sym = fn->sym;
            if (sym && sym->c) {
                /* the function was used: generate its code and
                   convert it to a normal function */
                fn->sym = NULL;
                if (file)
                    pstrcpy(file->filename, sizeof file->filename, fn->filename);
                sym->type.t &= ~VT_INLINE;

                begin_macro(fn->func_str, 1);
                next();
                cur_text_section = text_section;
                gen_function(sym);
                end_macro();

                inline_generated = 1;
            }
        }
        if (!inline_generated)
            break;
    }
    file->line_num = ln;
}

ST_FUNC void free_inline_functions(TCCState *s)
{
    int i;
    /* free tokens of unused inline functions */
    for (i = 0; i < s->nb_inline_fns; ++i) {
        struct InlineFunc *fn = s->inline_fns[i];
        if (fn->sym)
            tok_str_free(fn->func_str);
    }
    dynarray_reset(&s->inline_fns, &s->nb_inline_fns);
}

/* 'l' is VT_LOCAL or VT_CONST to define default storage type, or VT_CMP
   if parsing old style parameter decl list (and FUNC_SYM is set then) */
static int decl0(int l, int is_for_loop_init, Sym *func_sym)
{
    int v, has_init, r;
    CType type, btype;
    Sym *sym;
    AttributeDef ad;

    while (1) {
        if (!parse_btype(&btype, &ad)) {
            if (is_for_loop_init)
                return 0;
            /* skip redundant ';' if not in old parameter decl scope */
            if (tok == ';' && l != VT_CMP) {
                next();
                continue;
            }
            if (l == VT_CONST &&
                (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
                /* global asm block */
                asm_global_instr();
                continue;
            }
            /* special test for old K&R protos without explicit int
               type. Only accepted when defining global data */
            if (l != VT_CONST || tok < TOK_UIDENT)
                break;
            btype.t = VT_INT;
        }
        if (((btype.t & VT_BTYPE) == VT_ENUM ||
             (btype.t & VT_BTYPE) == VT_STRUCT) && 
            tok == ';') {
        if ((btype.t & VT_BTYPE) == VT_STRUCT) {
        int v = btype.ref->v;
        if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) >= SYM_FIRST_ANOM)
                tcc_warning("unnamed struct/union that defines no instances");
        }
            next();
            continue;
        }
        while (1) { /* iterate thru each declaration */
            type = btype;
        /* If the base type itself was an array type of unspecified
           size (like in 'typedef int arr[]; arr x = {1};') then
           we will overwrite the unknown size by the real one for
           this decl.  We need to unshare the ref symbol holding
           that size.  */
        if ((type.t & VT_ARRAY) && type.ref->c < 0) {
        type.ref = sym_push(SYM_FIELD, &type.ref->type, 0, type.ref->c);
        }
            type_decl(&type, &ad, &v, TYPE_DIRECT);
#if 0
            {
                char buf[500];
                type_to_str(buf, sizeof(buf), &type, get_tok_str(v, NULL));
                printf("type = '%s'\n", buf);
            }
#endif
            if ((type.t & VT_BTYPE) == VT_FUNC) {
                if ((type.t & VT_STATIC) && (l == VT_LOCAL)) {
                    tcc_error("function without file scope cannot be static");
                }
                /* if old style function prototype, we accept a
                   declaration list */
                sym = type.ref;
                if (sym->c == FUNC_OLD && l == VT_CONST)
                    decl0(VT_CMP, 0, sym);
            }

            if (gnu_ext && (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
                ad.asm_label = asm_label_instr();
                /* parse one last attribute list, after asm label */
                parse_attribute(&ad);
                if (tok == '{')
                    expect(";");
            }

            if (ad.a.weak)
                type.t |= VT_WEAK;
#ifdef TCC_TARGET_PE
            if (ad.a.func_import || ad.a.func_export) {
                if (type.t & (VT_STATIC|VT_TYPEDEF))
                    tcc_error("cannot have dll linkage with static or typedef");
                if (ad.a.func_export)
                    type.t |= VT_EXPORT;
                else if ((type.t & VT_BTYPE) != VT_FUNC)
                    type.t |= VT_IMPORT|VT_EXTERN;
            }
#endif
        type.t |= ad.a.visibility << VT_VIS_SHIFT;

            if (tok == '{') {
                if (l != VT_CONST)
                    tcc_error("cannot use local functions");
                if ((type.t & VT_BTYPE) != VT_FUNC)
                    expect("function definition");

                /* reject abstract declarators in function definition
           make old style params without decl have int type */
                sym = type.ref;
                while ((sym = sym->next) != NULL) {
                    if (!(sym->v & ~SYM_FIELD))
                        expect("identifier");
            if (sym->type.t == VT_VOID)
                sym->type = int_type;
        }
                
                /* XXX: cannot do better now: convert extern line to static inline */
                if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE))
                    type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
                
                sym = sym_find(v);
                if (sym) {
                    Sym *ref;
                    if ((sym->type.t & VT_BTYPE) != VT_FUNC)
                        goto func_error1;

                    ref = sym->type.ref;

                    /* use func_call from prototype if not defined */
                    if (ref->a.func_call != FUNC_CDECL
                     && type.ref->a.func_call == FUNC_CDECL)
                        type.ref->a.func_call = ref->a.func_call;

                    /* use static from prototype */
                    if (sym->type.t & VT_STATIC)
                        type.t = (type.t & ~VT_EXTERN) | VT_STATIC;

            /* If the definition has no visibility use the
               one from prototype.  */
            if (! (type.t & VT_VIS_MASK))
                type.t |= sym->type.t & VT_VIS_MASK;

                    /* apply other storage attributes from prototype */
                    type.t |= sym->type.t & (VT_EXPORT|VT_WEAK);

                    if (!is_compatible_types(&sym->type, &type)) {
                    func_error1:
                        tcc_error("incompatible types for redefinition of '%s'", 
                              get_tok_str(v, NULL));
                    }
                    if (ref->a.func_body)
                        tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
                    /* if symbol is already defined, then put complete type */
                    sym->type = type;

                } else {
                    /* put function symbol */
                    sym = global_identifier_push(v, type.t, 0);
                    sym->type.ref = type.ref;
                }

                sym->type.ref->a.func_body = 1;
                sym->r = VT_SYM | VT_CONST;

                /* static inline functions are just recorded as a kind
                   of macro. Their code will be emitted at the end of
                   the compilation unit only if they are used */
                if ((type.t & (VT_INLINE | VT_STATIC)) == 
                    (VT_INLINE | VT_STATIC)) {
                    struct InlineFunc *fn;
                    const char *filename;
                           
                    filename = file ? file->filename : "";
                    fn = tcc_malloc(sizeof *fn + strlen(filename));
                    strcpy(fn->filename, filename);
                    fn->sym = sym;
            skip_or_save_block(&fn->func_str);
                    dynarray_add(&tcc_state->inline_fns,
                 &tcc_state->nb_inline_fns, fn);
                } else {
                    /* compute text section */
                    cur_text_section = ad.section;
                    if (!cur_text_section)
                        cur_text_section = text_section;
                    gen_function(sym);
                }
                break;
            } else {
        if (l == VT_CMP) {
            /* find parameter in function parameter list */
            for (sym = func_sym->next; sym; sym = sym->next)
            if ((sym->v & ~SYM_FIELD) == v)
                goto found;
            tcc_error("declaration for parameter '%s' but no such parameter",
                  get_tok_str(v, NULL));
found:
            if (type.t & VT_STORAGE) /* 'register' is okay */
                tcc_error("storage class specified for '%s'",
                  get_tok_str(v, NULL));
            if (sym->type.t != VT_VOID)
                tcc_error("redefinition of parameter '%s'",
                  get_tok_str(v, NULL));
            convert_parameter_type(&type);
            sym->type = type;
        } else if (type.t & VT_TYPEDEF) {
                    /* save typedefed type  */
                    /* XXX: test storage specifiers ? */
                    sym = sym_find(v);
                    if (sym && sym->scope == local_scope) {
                        if (!is_compatible_types(&sym->type, &type)
                            || !(sym->type.t & VT_TYPEDEF))
                            tcc_error("incompatible redefinition of '%s'",
                                get_tok_str(v, NULL));
                        sym->type = type;
                    } else {
                        sym = sym_push(v, &type, 0, 0);
                    }
                    sym->a = ad.a;
                } else {
                    r = 0;
                    if ((type.t & VT_BTYPE) == VT_FUNC) {
                        /* external function definition */
                        /* specific case for func_call attribute */
                        type.ref->a = ad.a;
                    } else if (!(type.t & VT_ARRAY)) {
                        /* not lvalue if array */
                        r |= lvalue_type(type.t);
                    }
                    has_init = (tok == '=');
                    if (has_init && (type.t & VT_VLA))
                        tcc_error("variable length array cannot be initialized");
                    if (((type.t & VT_EXTERN) && (!has_init || l != VT_CONST)) ||
            ((type.t & VT_BTYPE) == VT_FUNC) ||
                        ((type.t & VT_ARRAY) && (type.t & VT_STATIC) &&
                         !has_init && l == VT_CONST && type.ref->c < 0)) {
                        /* external variable or function */
                        /* NOTE: as GCC, uninitialized global static
                           arrays of null size are considered as
                           extern */
                        sym = external_sym(v, &type, r);
                        sym->asm_label = ad.asm_label;
                        if (ad.alias_target) {
                            Section tsec;
                            ElfW(Sym) *esym;
                            Sym *alias_target;

                            alias_target = sym_find(ad.alias_target);
                            if (!alias_target || !alias_target->c)
                                tcc_error("unsupported forward __alias__ attribute");
                            esym = &((ElfW(Sym) *)symtab_section->data)[alias_target->c];
                            tsec.sh_num = esym->st_shndx;
                            put_extern_sym2(sym, &tsec, esym->st_value, esym->st_size, 0);
                        }
                    } else {
                        if (type.t & VT_STATIC)
                            r |= VT_CONST;
                        else
                            r |= l;
                        if (has_init)
                            next();
                        decl_initializer_alloc(&type, &ad, r, has_init, v, l);
                    }
                }
                if (tok != ',') {
                    if (is_for_loop_init)
                        return 1;
                    skip(';');
                    break;
                }
                next();
            }
            ad.a.aligned = 0;
        }
    }
    return 0;
}

ST_FUNC void decl(int l)
{
    decl0(l, 0, NULL);
}

/* ------------------------------------------------------------------------- */

https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tccelf.c

File: ../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tccelf.c used in: Step 378, Step 395, Step 407, Step 419, Step 431, Step 443, Step 455
/*
 *  ELF file handling for TCC
 *
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef _TCC_H
#include "tcc.h"
#endif

/* Define this to get some debug output during relocation processing.  */
#undef DEBUG_RELOC

/********************************************************/
/* global variables */

ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */
ST_DATA Section *common_section;
ST_DATA Section *cur_text_section; /* current section where function code is generated */
#ifdef CONFIG_TCC_ASM
ST_DATA Section *last_text_section; /* to handle .previous asm directive */
#endif
#ifdef CONFIG_TCC_BCHECK
/* bound check related sections */
ST_DATA Section *bounds_section; /* contains global data bound description */
ST_DATA Section *lbounds_section; /* contains local data bound description */
#endif
/* symbol sections */
ST_DATA Section *symtab_section, *strtab_section;
/* debug sections */
ST_DATA Section *stab_section, *stabstr_section;

/* XXX: avoid static variable */
static int new_undef_sym = 0; /* Is there a new undefined sym since last new_undef_sym() */

/* ------------------------------------------------------------------------- */

ST_FUNC void tccelf_new(TCCState *s)
{
    /* no section zero */
    dynarray_add(&s->sections, &s->nb_sections, NULL);

    /* create standard sections */
    text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
    data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
    bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
    common_section = new_section(s, ".common", SHT_NOBITS, SHF_PRIVATE);
    common_section->sh_num = SHN_COMMON;

    /* symbols are always generated for linking stage */
    symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0,
                                ".strtab",
                                ".hashtab", SHF_PRIVATE);
    strtab_section = symtab_section->link;
    s->symtab = symtab_section;

    /* private symbol table for dynamic symbols */
    s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE,
                                      ".dynstrtab",
                                      ".dynhashtab", SHF_PRIVATE);
    get_sym_attr(s, 0, 1);
}

#ifdef CONFIG_TCC_BCHECK
ST_FUNC void tccelf_bounds_new(TCCState *s)
{
    /* create bounds sections */
    bounds_section = new_section(s, ".bounds",
                                 SHT_PROGBITS, SHF_ALLOC);
    lbounds_section = new_section(s, ".lbounds",
                                  SHT_PROGBITS, SHF_ALLOC);
}
#endif

ST_FUNC void tccelf_stab_new(TCCState *s)
{
    stab_section = new_section(s, ".stab", SHT_PROGBITS, 0);
    stab_section->sh_entsize = sizeof(Stab_Sym);
    stabstr_section = new_section(s, ".stabstr", SHT_STRTAB, 0);
    put_elf_str(stabstr_section, "");
    stab_section->link = stabstr_section;
    /* put first entry */
    put_stabs("", 0, 0, 0, 0);
}

static void free_section(Section *s)
{
    tcc_free(s->data);
}

ST_FUNC void tccelf_delete(TCCState *s1)
{
    int i;

    /* free all sections */
    for(i = 1; i < s1->nb_sections; i++)
        free_section(s1->sections[i]);
    dynarray_reset(&s1->sections, &s1->nb_sections);

    for(i = 0; i < s1->nb_priv_sections; i++)
        free_section(s1->priv_sections[i]);
    dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections);

    /* free any loaded DLLs */
#ifdef TCC_IS_NATIVE
    for ( i = 0; i < s1->nb_loaded_dlls; i++) {
        DLLReference *ref = s1->loaded_dlls[i];
        if ( ref->handle )
# ifdef _WIN32
            FreeLibrary((HMODULE)ref->handle);
# else
            dlclose(ref->handle);
# endif
    }
#endif
    /* free loaded dlls array */
    dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
    tcc_free(s1->sym_attrs);
}

ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags)
{
    Section *sec;

    sec = tcc_mallocz(sizeof(Section) + strlen(name));
    strcpy(sec->name, name);
    sec->sh_type = sh_type;
    sec->sh_flags = sh_flags;
    switch(sh_type) {
    case SHT_HASH:
    case SHT_REL:
    case SHT_RELA:
    case SHT_DYNSYM:
    case SHT_SYMTAB:
    case SHT_DYNAMIC:
        sec->sh_addralign = 4;
        break;
    case SHT_STRTAB:
        sec->sh_addralign = 1;
        break;
    default:
        sec->sh_addralign =  PTR_SIZE; /* gcc/pcc default alignment */
        break;
    }

    if (sh_flags & SHF_PRIVATE) {
        dynarray_add(&s1->priv_sections, &s1->nb_priv_sections, sec);
    } else {
        sec->sh_num = s1->nb_sections;
        dynarray_add(&s1->sections, &s1->nb_sections, sec);
    }

    return sec;
}

ST_FUNC Section *new_symtab(TCCState *s1,
                           const char *symtab_name, int sh_type, int sh_flags,
                           const char *strtab_name,
                           const char *hash_name, int hash_sh_flags)
{
    Section *symtab, *strtab, *hash;
    int *ptr, nb_buckets;

    symtab = new_section(s1, symtab_name, sh_type, sh_flags);
    symtab->sh_entsize = sizeof(ElfW(Sym));
    strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags);
    put_elf_str(strtab, "");
    symtab->link = strtab;
    put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL);

    nb_buckets = 1;

    hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags);
    hash->sh_entsize = sizeof(int);
    symtab->hash = hash;
    hash->link = symtab;

    ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int));
    ptr[0] = nb_buckets;
    ptr[1] = 1;
    memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int));
    return symtab;
}

/* realloc section and set its content to zero */
ST_FUNC void section_realloc(Section *sec, unsigned long new_size)
{
    unsigned long size;
    unsigned char *data;

    size = sec->data_allocated;
    if (size == 0)
        size = 1;
    while (size < new_size)
        size = size * 2;
    data = tcc_realloc(sec->data, size);
    memset(data + sec->data_allocated, 0, size - sec->data_allocated);
    sec->data = data;
    sec->data_allocated = size;
}

/* reserve at least 'size' bytes aligned per 'align' in section
   'sec' from current offset, and return the aligned offset */
ST_FUNC size_t section_add(Section *sec, addr_t size, int align)
{
    size_t offset, offset1;

    offset = (sec->data_offset + align - 1) & -align;
    offset1 = offset + size;
    if (sec->sh_type != SHT_NOBITS && offset1 > sec->data_allocated)
        section_realloc(sec, offset1);
    sec->data_offset = offset1;
    if (align > sec->sh_addralign)
        sec->sh_addralign = align;
    return offset;
}

/* reserve at least 'size' bytes in section 'sec' from
   sec->data_offset. */
ST_FUNC void *section_ptr_add(Section *sec, addr_t size)
{
    size_t offset = section_add(sec, size, 1);
    return sec->data + offset;
}

/* reserve at least 'size' bytes from section start */
ST_FUNC void section_reserve(Section *sec, unsigned long size)
{
    if (size > sec->data_allocated)
        section_realloc(sec, size);
    if (size > sec->data_offset)
        sec->data_offset = size;
}

/* return a reference to a section, and create it if it does not
   exists */
ST_FUNC Section *find_section(TCCState *s1, const char *name)
{
    Section *sec;
    int i;
    for(i = 1; i < s1->nb_sections; i++) {
        sec = s1->sections[i];
        if (!strcmp(name, sec->name))
            return sec;
    }
    /* sections are created as PROGBITS */
    return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);
}

/* ------------------------------------------------------------------------- */

ST_FUNC int put_elf_str(Section *s, const char *sym)
{
    int offset, len;
    char *ptr;

    len = strlen(sym) + 1;
    offset = s->data_offset;
    ptr = section_ptr_add(s, len);
    memcpy(ptr, sym, len);
    return offset;
}

/* elf symbol hashing function */
static unsigned long elf_hash(const unsigned char *name)
{
    unsigned long h = 0, g;

    while (*name) {
        h = (h << 4) + *name++;
        g = h & 0xf0000000;
        if (g)
            h ^= g >> 24;
        h &= ~g;
    }
    return h;
}

/* rebuild hash table of section s */
/* NOTE: we do factorize the hash table code to go faster */
static void rebuild_hash(Section *s, unsigned int nb_buckets)
{
    ElfW(Sym) *sym;
    int *ptr, *hash, nb_syms, sym_index, h;
    unsigned char *strtab;

    strtab = s->link->data;
    nb_syms = s->data_offset / sizeof(ElfW(Sym));

    s->hash->data_offset = 0;
    ptr = section_ptr_add(s->hash, (2 + nb_buckets + nb_syms) * sizeof(int));
    ptr[0] = nb_buckets;
    ptr[1] = nb_syms;
    ptr += 2;
    hash = ptr;
    memset(hash, 0, (nb_buckets + 1) * sizeof(int));
    ptr += nb_buckets + 1;

    sym = (ElfW(Sym) *)s->data + 1;
    for(sym_index = 1; sym_index < nb_syms; sym_index++) {
        if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
#if !BOOTSTRAP
            h = elf_hash(strtab + sym->st_name) % nb_buckets;
#else
            h = elf_hash(strtab + sym->st_name);
            h = h % nb_buckets;
#endif
            *ptr = hash[h];
            hash[h] = sym_index;
        } else {
            *ptr = 0;
        }
        ptr++;
        sym++;
    }
}

/* return the symbol number */
ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size,
    int info, int other, int shndx, const char *name)
{
    int name_offset, sym_index;
    int nbuckets, h;
    ElfW(Sym) *sym;
    Section *hs;

    sym = section_ptr_add(s, sizeof(ElfW(Sym)));
    if (name)
        name_offset = put_elf_str(s->link, name);
    else
        name_offset = 0;
    /* XXX: endianness */
    sym->st_name = name_offset;
    sym->st_value = value;
    sym->st_size = size;
    sym->st_info = info;
    sym->st_other = other;
    sym->st_shndx = shndx;
    sym_index = sym - (ElfW(Sym) *)s->data;
    hs = s->hash;
    if (hs) {
        int *ptr, *base;
        ptr = section_ptr_add(hs, sizeof(int));
        base = (int *)hs->data;
        /* only add global or weak symbols */
        if (ELFW(ST_BIND)(info) != STB_LOCAL) {
            /* add another hashing entry */
            nbuckets = base[0];
#if !BOOTSTRAP
            h = elf_hash((unsigned char *) name) % nbuckets;
#else
            h = elf_hash((unsigned char *) name);
            h = h % nbuckets;
#endif
            *ptr = base[2 + h];
            base[2 + h] = sym_index;
            base[1]++;
            /* we resize the hash table */
            hs->nb_hashed_syms++;
            if (hs->nb_hashed_syms > 2 * nbuckets) {
                rebuild_hash(s, 2 * nbuckets);
            }
        } else {
            *ptr = 0;
            base[1]++;
        }
    }
    return sym_index;
}

/* find global ELF symbol 'name' and return its index. Return 0 if not
   found. */
ST_FUNC int find_elf_sym(Section *s, const char *name)
{
    ElfW(Sym) *sym;
    Section *hs;
    int nbuckets, sym_index, h;
    const char *name1;

    hs = s->hash;
    if (!hs)
        return 0;
    nbuckets = ((int *)hs->data)[0];
#if !BOOTSTRAP
    h = elf_hash((unsigned char *) name) % nbuckets;
#else
    h = elf_hash((unsigned char *) name);
    h = h % nbuckets;
#endif
    sym_index = ((int *)hs->data)[2 + h];
    while (sym_index != 0) {
        sym = &((ElfW(Sym) *)s->data)[sym_index];
        name1 = (char *) s->link->data + sym->st_name;
        if (!strcmp(name, name1))
            return sym_index;
        sym_index = ((int *)hs->data)[2 + nbuckets + sym_index];
    }
    return 0;
}

/* return elf symbol value, signal error if 'err' is nonzero */
ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err)
{
    int sym_index;
    ElfW(Sym) *sym;

    sym_index = find_elf_sym(s->symtab, name);
    sym = &((ElfW(Sym) *)s->symtab->data)[sym_index];
    if (!sym_index || sym->st_shndx == SHN_UNDEF) {
        if (err)
            tcc_error("%s not defined", name);
        return 0;
    }
    return sym->st_value;
}

#if __MESC_MES__ || !__MESC__
#if !defined (__intptr_t_defined) && !defined (__MES_INTPTR_T)
typedef unsigned long uintptr_t;
#endif
#else // guile
#undef uintptr_t
#endif

/* return elf symbol value */
LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name)
{
    return (void*)(uintptr_t)get_elf_sym_addr(s, name, 0);
}

#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE
/* return elf symbol value or error */
ST_FUNC void* tcc_get_symbol_err(TCCState *s, const char *name)
{
    return (void*)(uintptr_t)get_elf_sym_addr(s, name, 1);
}
#endif

/* add an elf symbol : check if it is already defined and patch
   it. Return symbol index. NOTE that sh_num can be SHN_UNDEF. */
ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size,
                       int info, int other, int shndx, const char *name)
{
    ElfW(Sym) *esym;
    int sym_bind, sym_index, sym_type, esym_bind;
    unsigned char sym_vis, esym_vis, new_vis;

    sym_bind = ELFW(ST_BIND)(info);
    sym_type = ELFW(ST_TYPE)(info);
    sym_vis = ELFW(ST_VISIBILITY)(other);

    sym_index = find_elf_sym(s, name);
    esym = &((ElfW(Sym) *)s->data)[sym_index];
    if (sym_index && esym->st_value == value && esym->st_size == size
    && esym->st_info == info && esym->st_other == other
    && esym->st_shndx == shndx)
        return sym_index;

    if (sym_bind != STB_LOCAL) {
        /* we search global or weak symbols */
        if (!sym_index)
            goto do_def;
        if (esym->st_shndx != SHN_UNDEF) {
            esym_bind = ELFW(ST_BIND)(esym->st_info);
            /* propagate the most constraining visibility */
            /* STV_DEFAULT(0)<STV_PROTECTED(3)<STV_HIDDEN(2)<STV_INTERNAL(1) */
            esym_vis = ELFW(ST_VISIBILITY)(esym->st_other);
            if (esym_vis == STV_DEFAULT) {
                new_vis = sym_vis;
            } else if (sym_vis == STV_DEFAULT) {
                new_vis = esym_vis;
            } else {
                new_vis = (esym_vis < sym_vis) ? esym_vis : sym_vis;
            }
            esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
                             | new_vis;
            other = esym->st_other; /* in case we have to patch esym */
            if (shndx == SHN_UNDEF) {
                /* ignore adding of undefined symbol if the
                   corresponding symbol is already defined */
            } else if (sym_bind == STB_GLOBAL && esym_bind == STB_WEAK) {
                /* global overrides weak, so patch */
                goto do_patch;
            } else if (sym_bind == STB_WEAK && esym_bind == STB_GLOBAL) {
                /* weak is ignored if already global */
            } else if (sym_bind == STB_WEAK && esym_bind == STB_WEAK) {
                /* keep first-found weak definition, ignore subsequents */
            } else if (sym_vis == STV_HIDDEN || sym_vis == STV_INTERNAL) {
                /* ignore hidden symbols after */
            } else if ((esym->st_shndx == SHN_COMMON
                            || esym->st_shndx == bss_section->sh_num)
                        && (shndx < SHN_LORESERVE
                            && shndx != bss_section->sh_num)) {
                /* data symbol gets precedence over common/bss */
                goto do_patch;
            } else if (shndx == SHN_COMMON || shndx == bss_section->sh_num) {
                /* data symbol keeps precedence over common/bss */
            } else if (s == tcc_state->dynsymtab_section) {
                /* we accept that two DLL define the same symbol */
            } else {
#if 0
                printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n",
                       sym_bind, shndx, new_vis, esym_bind, esym->st_shndx, esym_vis);
#endif
                tcc_error_noabort("'%s' defined twice", name);
            }
        } else {
        do_patch:
            esym->st_info = ELFW(ST_INFO)(sym_bind, sym_type);
            esym->st_shndx = shndx;
            new_undef_sym = 1;
            esym->st_value = value;
            esym->st_size = size;
            esym->st_other = other;
        }
    } else {
    do_def:
        sym_index = put_elf_sym(s, value, size,
                                ELFW(ST_INFO)(sym_bind, sym_type), other,
                                shndx, name);
    }
    return sym_index;
}

/* put relocation */
ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset,
                            int type, int symbol, addr_t addend)
{
    char buf[256];
    Section *sr;
    ElfW_Rel *rel;

    sr = s->reloc;
    if (!sr) {
        /* if no relocation section, create it */
        snprintf(buf, sizeof(buf), REL_SECTION_FMT, s->name);
        /* if the symtab is allocated, then we consider the relocation
           are also */
        sr = new_section(tcc_state, buf, SHT_RELX, symtab->sh_flags);
        sr->sh_entsize = sizeof(ElfW_Rel);
        sr->link = symtab;
        sr->sh_info = s->sh_num;
        s->reloc = sr;
    }
    rel = section_ptr_add(sr, sizeof(ElfW_Rel));
    rel->r_offset = offset;
    rel->r_info = ELFW(R_INFO)(symbol, type);
#if SHT_RELX == SHT_RELA
    rel->r_addend = addend;
#else
    if (addend)
        tcc_error("non-zero addend on REL architecture");
#endif
}

ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset,
                           int type, int symbol)
{
    put_elf_reloca(symtab, s, offset, type, symbol, 0);
}

/* Remove relocations for section S->reloc starting at oldrelocoffset
   that are to the same place, retaining the last of them.  As side effect
   the relocations are sorted.  Possibly reduces the number of relocs.  */
ST_FUNC void squeeze_multi_relocs(Section *s, size_t oldrelocoffset)
{
    Section *sr = s->reloc;
    ElfW_Rel *r, *dest;
    ssize_t a;
    ElfW(Addr) addr;

    if (oldrelocoffset + sizeof(*r) >= sr->data_offset)
      return;
    /* The relocs we're dealing with are the result of initializer parsing.
       So they will be mostly in order and there aren't many of them.
       Secondly we need a stable sort (which qsort isn't).  We use
       a simple insertion sort.  */
    for (a = oldrelocoffset + sizeof(*r); a < sr->data_offset; a += sizeof(*r)) {
    ssize_t i = a - sizeof(*r);
    ///addr = ((ElfW_Rel*)(sr->data + a))->r_offset;
    ElfW_Rel* ea = (ElfW_Rel*)(sr->data + a);
    ElfW_Rel* ei = (ElfW_Rel*)(sr->data + i);
    addr = ea->r_offset;
    for (; i >= (ssize_t)oldrelocoffset &&
               ei->r_offset > addr; i -= sizeof(*r)) {
            ei = (ElfW_Rel*)(sr->data + i);
        ElfW_Rel tmp = *(ElfW_Rel*)(sr->data + a);
        *(ElfW_Rel*)(sr->data + a) = *(ElfW_Rel*)(sr->data + i);
        *(ElfW_Rel*)(sr->data + i) = tmp;
    }
    }

    r = (ElfW_Rel*)(sr->data + oldrelocoffset);
    dest = r;
    for (; r < (ElfW_Rel*)(sr->data + sr->data_offset); r++) {
    if (dest->r_offset != r->r_offset)
      dest++;
    *dest = *r;
    }
    sr->data_offset = (unsigned char*)dest - sr->data + sizeof(*r);
}

/* put stab debug information */

ST_FUNC void put_stabs(const char *str, int type, int other, int desc,
                      unsigned long value)
{
    Stab_Sym *sym;

    sym = section_ptr_add(stab_section, sizeof(Stab_Sym));
    if (str) {
        sym->n_strx = put_elf_str(stabstr_section, str);
    } else {
        sym->n_strx = 0;
    }
    sym->n_type = type;
    sym->n_other = other;
    sym->n_desc = desc;
    sym->n_value = value;
}

ST_FUNC void put_stabs_r(const char *str, int type, int other, int desc,
                        unsigned long value, Section *sec, int sym_index)
{
    put_stabs(str, type, other, desc, value);
    put_elf_reloc(symtab_section, stab_section,
                  stab_section->data_offset - sizeof(unsigned int),
                  R_DATA_32, sym_index);
}

ST_FUNC void put_stabn(int type, int other, int desc, int value)
{
    put_stabs(NULL, type, other, desc, value);
}

ST_FUNC void put_stabd(int type, int other, int desc)
{
    put_stabs(NULL, type, other, desc, 0);
}

ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc)
{
    int n;
    struct sym_attr *tab;

    if (index >= s1->nb_sym_attrs) {
        if (!alloc)
            return s1->sym_attrs;
        /* find immediately bigger power of 2 and reallocate array */
        n = 1;
        while (index >= n)
            n *= 2;
        tab = tcc_realloc(s1->sym_attrs, n * sizeof(*s1->sym_attrs));
        s1->sym_attrs = tab;
        memset(s1->sym_attrs + s1->nb_sym_attrs, 0,
               (n - s1->nb_sym_attrs) * sizeof(*s1->sym_attrs));
        s1->nb_sym_attrs = n;
    }
    return &s1->sym_attrs[index];
}

/* Browse each elem of type <type> in section <sec> starting at elem <startoff>
   using variable <elem> */
#define for_each_elem(sec, startoff, elem, type) \
    for (elem = (type *) sec->data + startoff; \
         elem < (type *) (sec->data + sec->data_offset); elem++)

/* In an ELF file symbol table, the local symbols must appear below
   the global and weak ones. Since TCC cannot sort it while generating
   the code, we must do it after. All the relocation tables are also
   modified to take into account the symbol table sorting */
static void sort_syms(TCCState *s1, Section *s)
{
    int *old_to_new_syms;
    ElfW(Sym) *new_syms;
    int nb_syms, i;
    ElfW(Sym) *p, *q;
    ElfW_Rel *rel;
    Section *sr;
    int type, sym_index;

    nb_syms = s->data_offset / sizeof(ElfW(Sym));
    new_syms = tcc_malloc(nb_syms * sizeof(ElfW(Sym)));
    old_to_new_syms = tcc_malloc(nb_syms * sizeof(int));

    /* first pass for local symbols */
    p = (ElfW(Sym) *)s->data;
    q = new_syms;
    for(i = 0; i < nb_syms; i++) {
        if (ELFW(ST_BIND)(p->st_info) == STB_LOCAL) {
            old_to_new_syms[i] = q - new_syms;
            *q++ = *p;
        }
        p++;
    }
    /* save the number of local symbols in section header */
    s->sh_info = q - new_syms;

    /* then second pass for non local symbols */
    p = (ElfW(Sym) *)s->data;
    for(i = 0; i < nb_syms; i++) {
        if (ELFW(ST_BIND)(p->st_info) != STB_LOCAL) {
            old_to_new_syms[i] = q - new_syms;
            *q++ = *p;
        }
        p++;
    }

    /* we copy the new symbols to the old */
    memcpy(s->data, new_syms, nb_syms * sizeof(ElfW(Sym)));
    tcc_free(new_syms);

    /* now we modify all the relocations */
    for(i = 1; i < s1->nb_sections; i++) {
        sr = s1->sections[i];
        if (sr->sh_type == SHT_RELX && sr->link == s) {
            for_each_elem(sr, 0, rel, ElfW_Rel) {
                sym_index = ELFW(R_SYM)(rel->r_info);
                type = ELFW(R_TYPE)(rel->r_info);
                sym_index = old_to_new_syms[sym_index];
                rel->r_info = ELFW(R_INFO)(sym_index, type);
            }
        }
    }

    tcc_free(old_to_new_syms);
}

/* relocate common symbols in the .bss section */
ST_FUNC void relocate_common_syms(void)
{
    ElfW(Sym) *sym;

    for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
        if (sym->st_shndx == SHN_COMMON) {
            /* symbol alignment is in st_value for SHN_COMMONs */
        sym->st_value = section_add(bss_section, sym->st_size,
                    sym->st_value);
            sym->st_shndx = bss_section->sh_num;
        }
    }
}

/* relocate symbol table, resolve undefined symbols if do_resolve is
   true and output error if undefined symbol. */
ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve)
{
    ElfW(Sym) *sym;
    int sym_bind, sh_num;
    const char *name;

    for_each_elem(symtab, 1, sym, ElfW(Sym)) {
        sh_num = sym->st_shndx;
        if (sh_num == SHN_UNDEF) {
            name = (char *) strtab_section->data + sym->st_name;
            /* Use ld.so to resolve symbol for us (for tcc -run) */
            if (do_resolve) {
#if defined TCC_IS_NATIVE && !defined TCC_TARGET_PE
                void *addr = dlsym(RTLD_DEFAULT, name);
                if (addr) {
                    sym->st_value = (addr_t) addr;
#ifdef DEBUG_RELOC
            printf ("relocate_sym: %s -> 0x%lx\n", name, sym->st_value);
#endif
                    goto found;
                }
#endif
            /* if dynamic symbol exist, it will be used in relocate_section */
            } else if (s1->dynsym && find_elf_sym(s1->dynsym, name))
                goto found;
            /* XXX: _fp_hw seems to be part of the ABI, so we ignore
               it */
            if (!strcmp(name, "_fp_hw"))
                goto found;
            /* only weak symbols are accepted to be undefined. Their
               value is zero */
            sym_bind = ELFW(ST_BIND)(sym->st_info);
            if (sym_bind == STB_WEAK)
                sym->st_value = 0;
            else
                tcc_error_noabort("undefined symbol '%s'", name);
        } else if (sh_num < SHN_LORESERVE) {
            /* add section base */
            sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
        }
    found: ;
    }
}

/* relocate a given section (CPU dependent) by applying the relocations
   in the associated relocation section */
ST_FUNC void relocate_section(TCCState *s1, Section *s)
{
    Section *sr = s->reloc;
    ElfW_Rel *rel;
    ElfW(Sym) *sym;
    int type, sym_index;
    unsigned char *ptr;
    addr_t tgt, addr;

    relocate_init(sr);

    for_each_elem(sr, 0, rel, ElfW_Rel) {
        ptr = s->data + rel->r_offset;
        sym_index = ELFW(R_SYM)(rel->r_info);
        sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
        type = ELFW(R_TYPE)(rel->r_info);
        tgt = sym->st_value;
#if SHT_RELX == SHT_RELA
        tgt += rel->r_addend;
#endif
        addr = s->sh_addr + rel->r_offset;
        relocate(s1, rel, type, ptr, addr, tgt);
    }
    /* if the relocation is allocated, we change its symbol table */
    if (sr->sh_flags & SHF_ALLOC)
        sr->link = s1->dynsym;
}

/* relocate relocation table in 'sr' */
static void relocate_rel(TCCState *s1, Section *sr)
{
    Section *s;
    ElfW_Rel *rel;

    s = s1->sections[sr->sh_info];
    for_each_elem(sr, 0, rel, ElfW_Rel)
        rel->r_offset += s->sh_addr;
}

/* count the number of dynamic relocations so that we can reserve
   their space */
static int prepare_dynamic_rel(TCCState *s1, Section *sr)
{
    ElfW_Rel *rel;
    int sym_index, type, count;

    count = 0;
    for_each_elem(sr, 0, rel, ElfW_Rel) {
        sym_index = ELFW(R_SYM)(rel->r_info);
        type = ELFW(R_TYPE)(rel->r_info);
        switch(type) {
#if defined(TCC_TARGET_I386)
        case R_386_32:
#elif defined(TCC_TARGET_X86_64)
        case R_X86_64_32:
        case R_X86_64_32S:
        case R_X86_64_64:
#endif
            count++;
            break;
#if defined(TCC_TARGET_I386)
        case R_386_PC32:
#elif defined(TCC_TARGET_X86_64)
        case R_X86_64_PC32:
#endif
            if (get_sym_attr(s1, sym_index, 0)->dyn_index)
                count++;
            break;
        default:
            break;
        }
    }
    if (count) {
        /* allocate the section */
        sr->sh_flags |= SHF_ALLOC;
        sr->sh_size = count * sizeof(ElfW_Rel);
    }
    return count;
}

static void build_got(TCCState *s1)
{
    /* if no got, then create it */
    s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
    s1->got->sh_entsize = 4;
    set_elf_sym(symtab_section, 0, 4, ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT),
                0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_");
    /* keep space for _DYNAMIC pointer and two dummy got entries */
    section_ptr_add(s1->got, 3 * PTR_SIZE);
}

/* Create a GOT and (for function call) a PLT entry corresponding to a symbol
   in s1->symtab. When creating the dynamic symbol table entry for the GOT
   relocation, use 'size' and 'info' for the corresponding symbol metadata.
   Returns the offset of the GOT or (if any) PLT entry. */
static struct sym_attr * put_got_entry(TCCState *s1, int dyn_reloc_type,
                                       unsigned long size,
                                       int info, int sym_index)
{
    int need_plt_entry;
    const char *name;
    ElfW(Sym) *sym;
    struct sym_attr *attr;
    unsigned got_offset;
    char plt_name[100];
    int len;

    need_plt_entry = (dyn_reloc_type == R_JMP_SLOT);
    attr = get_sym_attr(s1, sym_index, 1);

    /* In case a function is both called and its address taken 2 GOT entries
       are created, one for taking the address (GOT) and the other for the PLT
       entry (PLTGOT).  */
    if (need_plt_entry ? attr->plt_offset : attr->got_offset)
        return attr;

    /* create the GOT entry */
    got_offset = s1->got->data_offset;
    section_ptr_add(s1->got, PTR_SIZE);

    /* Create the GOT relocation that will insert the address of the object or
       function of interest in the GOT entry. This is a static relocation for
       memory output (dlsym will give us the address of symbols) and dynamic
       relocation otherwise (executable and DLLs). The relocation should be
       done lazily for GOT entry with *_JUMP_SLOT relocation type (the one
       associated to a PLT entry) but is currently done at load time for an
       unknown reason. */

    sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
    name = (char *) symtab_section->link->data + sym->st_name;

    if (s1->dynsym) {
    if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL) {
        /* Hack alarm.  We don't want to emit dynamic symbols
           and symbol based relocs for STB_LOCAL symbols, but rather
           want to resolve them directly.  At this point the symbol
           values aren't final yet, so we must defer this.  We will later
           have to create a RELATIVE reloc anyway, so we misuse the
           relocation slot to smuggle the symbol reference until
           fill_local_got_entries.  Not that the sym_index is
           relative to symtab_section, not s1->dynsym!  Nevertheless
           we use s1->dyn_sym so that if this is the first call
           that got->reloc is correctly created.  Also note that
           RELATIVE relocs are not normally created for the .got,
           so the types serves as a marker for later (and is retained
           also for the final output, which is okay because then the
           got is just normal data).  */
        put_elf_reloc(s1->dynsym, s1->got, got_offset, R_RELATIVE,
              sym_index);
    } else {
        if (0 == attr->dyn_index)
                attr->dyn_index = set_elf_sym(s1->dynsym, sym->st_value, size,
                          info, 0, sym->st_shndx, name);
        put_elf_reloc(s1->dynsym, s1->got, got_offset, dyn_reloc_type,
              attr->dyn_index);
    }
    } else {
        put_elf_reloc(symtab_section, s1->got, got_offset, dyn_reloc_type,
                      sym_index);
    }

    if (need_plt_entry) {
        if (!s1->plt) {
            s1->plt = new_section(s1, ".plt", SHT_PROGBITS,
                          SHF_ALLOC | SHF_EXECINSTR);
            s1->plt->sh_entsize = 4;
        }

        attr->plt_offset = create_plt_entry(s1, got_offset, attr);

        /* create a symbol 'sym@plt' for the PLT jump vector */
        len = strlen(name);
        if (len > sizeof plt_name - 5)
            len = sizeof plt_name - 5;
        memcpy(plt_name, name, len);
        strcpy(plt_name + len, "@plt");
        attr->plt_sym = put_elf_sym(s1->symtab, attr->plt_offset, sym->st_size,
            ELFW(ST_INFO)(STB_GLOBAL, STT_FUNC), 0, s1->plt->sh_num, plt_name);

    } else {
        attr->got_offset = got_offset;
    }

    return attr;
}

/* build GOT and PLT entries */
ST_FUNC void build_got_entries(TCCState *s1)
{
    Section *s;
    ElfW_Rel *rel;
    ElfW(Sym) *sym;
    int i, type, gotplt_entry, reloc_type, sym_index;
    struct sym_attr *attr;

    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if (s->sh_type != SHT_RELX)
            continue;
        /* no need to handle got relocations */
        if (s->link != symtab_section)
            continue;
        for_each_elem(s, 0, rel, ElfW_Rel) {
            type = ELFW(R_TYPE)(rel->r_info);
            gotplt_entry = gotplt_entry_type(type);
            sym_index = ELFW(R_SYM)(rel->r_info);
            sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];

            if (gotplt_entry == NO_GOTPLT_ENTRY) {
                continue;
            }

            /* Automatically create PLT/GOT [entry] if it is an undefined
           reference (resolved at runtime), or the symbol is absolute,
           probably created by tcc_add_symbol, and thus on 64-bit
           targets might be too far from application code.  */
            if (gotplt_entry == AUTO_GOTPLT_ENTRY) {
                if (sym->st_shndx == SHN_UNDEF) {
                    ElfW(Sym) *esym;
            int dynindex;
                    if (s1->output_type == TCC_OUTPUT_DLL && ! PCRELATIVE_DLLPLT)
                        continue;
            /* Relocations for UNDEF symbols would normally need
               to be transferred into the executable or shared object.
               If that were done AUTO_GOTPLT_ENTRY wouldn't exist.
               But TCC doesn't do that (at least for exes), so we
               need to resolve all such relocs locally.  And that
               means PLT slots for functions in DLLs and COPY relocs for
               data symbols.  COPY relocs were generated in
               bind_exe_dynsyms (and the symbol adjusted to be defined),
               and for functions we were generated a dynamic symbol
               of function type.  */
            if (s1->dynsym) {
            /* dynsym isn't set for -run :-/  */
            dynindex = get_sym_attr(s1, sym_index, 0)->dyn_index;
            esym = (ElfW(Sym) *)s1->dynsym->data + dynindex;
            if (dynindex
                && (ELFW(ST_TYPE)(esym->st_info) == STT_FUNC
                || (ELFW(ST_TYPE)(esym->st_info) == STT_NOTYPE
                    && ELFW(ST_TYPE)(sym->st_info) == STT_FUNC)))
                goto jmp_slot;
            }
                } else if (!(sym->st_shndx == SHN_ABS
#ifndef TCC_TARGET_ARM
            && PTR_SIZE == 8
#endif
            ))
                    continue;
            }

#ifdef TCC_TARGET_X86_64
            if ((type == R_X86_64_PLT32 || type == R_X86_64_PC32) &&
                (ELFW(ST_VISIBILITY)(sym->st_other) != STV_DEFAULT ||
         ELFW(ST_BIND)(sym->st_info) == STB_LOCAL)) {
                rel->r_info = ELFW(R_INFO)(sym_index, R_X86_64_PC32);
                continue;
            }
#endif
            if (code_reloc(type)) {
            jmp_slot:
                reloc_type = R_JMP_SLOT;
            } else
                reloc_type = R_GLOB_DAT;

            if (!s1->got)
                build_got(s1);

            if (gotplt_entry == BUILD_GOT_ONLY)
                continue;

            attr = put_got_entry(s1, reloc_type, sym->st_size, sym->st_info,
                                 sym_index);

            if (reloc_type == R_JMP_SLOT)
                rel->r_info = ELFW(R_INFO)(attr->plt_sym, type);
        }
    }
}

/* put dynamic tag */
static void put_dt(Section *dynamic, int dt, addr_t val)
{
    ElfW(Dyn) *dyn;
    dyn = section_ptr_add(dynamic, sizeof(ElfW(Dyn)));
    dyn->d_tag = dt;
    dyn->d_un.d_val = val;
}

#ifndef TCC_TARGET_PE
static void add_init_array_defines(TCCState *s1, const char *section_name)
{
    Section *s;
    long end_offset;
    char sym_start[1024];
    char sym_end[1024];

    snprintf(sym_start, sizeof(sym_start), "__%s_start", section_name + 1);
    snprintf(sym_end, sizeof(sym_end), "__%s_end", section_name + 1);

    s = find_section(s1, section_name);
    if (!s) {
        end_offset = 0;
        s = data_section;
    } else {
        end_offset = s->data_offset;
    }

    set_elf_sym(symtab_section,
                0, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                s->sh_num, sym_start);
    set_elf_sym(symtab_section,
                end_offset, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                s->sh_num, sym_end);
}
#endif

static int tcc_add_support(TCCState *s1, const char *filename)
{
    char buf[1024];
    snprintf(buf, sizeof(buf), "%s/%s", s1->tcc_lib_path, filename);
    return tcc_add_file(s1, buf);
}

ST_FUNC void tcc_add_bcheck(TCCState *s1)
{
#ifdef CONFIG_TCC_BCHECK
    addr_t *ptr;
    int sym_index;

    if (0 == s1->do_bounds_check)
        return;
    /* XXX: add an object file to do that */
    ptr = section_ptr_add(bounds_section, sizeof(*ptr));
    *ptr = 0;
    set_elf_sym(symtab_section, 0, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                bounds_section->sh_num, "__bounds_start");
    /* pull bcheck.o from libtcc1.a */
    sym_index = set_elf_sym(symtab_section, 0, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                SHN_UNDEF, "__bound_init");
    if (s1->output_type != TCC_OUTPUT_MEMORY) {
        /* add 'call __bound_init()' in .init section */
        Section *init_section = find_section(s1, ".init");
        unsigned char *pinit = section_ptr_add(init_section, 5);
        pinit[0] = 0xe8;
        write32le(pinit + 1, -4);
        put_elf_reloc(symtab_section, init_section,
            init_section->data_offset - 4, R_386_PC32, sym_index);
            /* R_386_PC32 = R_X86_64_PC32 = 2 */
    }
#endif
}

/* add tcc runtime libraries */
ST_FUNC void tcc_add_runtime(TCCState *s1)
{
    tcc_add_bcheck(s1);
    tcc_add_pragma_libs(s1);
    /* add libc */
    if (!s1->nostdlib) {
        tcc_add_library_err(s1, "c");
#ifdef TCC_LIBGCC
        if (!s1->static_link) {
            if (TCC_LIBGCC[0] == '/')
                tcc_add_file(s1, TCC_LIBGCC);
            else
                tcc_add_dll(s1, TCC_LIBGCC, 0);
        }
#endif
        tcc_add_support(s1, TCC_LIBTCC1);
#if CONFIG_TCC_LIBTCC1_MES
        tcc_add_support(s1, TCC_LIBTCC1_MES);
#endif
        /* add crt end if not memory output */
        if (s1->output_type != TCC_OUTPUT_MEMORY)
            tcc_add_crt(s1, "crtn.o");
    }
}

/* add various standard linker symbols (must be done after the
   sections are filled (for example after allocating common
   symbols)) */
ST_FUNC void tcc_add_linker_symbols(TCCState *s1)
{
    char buf[1024];
    int i;
    Section *s;

    set_elf_sym(symtab_section,
                text_section->data_offset, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                text_section->sh_num, "_etext");
    set_elf_sym(symtab_section,
                data_section->data_offset, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                data_section->sh_num, "_edata");
    set_elf_sym(symtab_section,
                bss_section->data_offset, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                bss_section->sh_num, "_end");
#ifndef TCC_TARGET_PE
    /* horrible new standard ldscript defines */
    add_init_array_defines(s1, ".preinit_array");
    add_init_array_defines(s1, ".init_array");
    add_init_array_defines(s1, ".fini_array");
#endif

    /* add start and stop symbols for sections whose name can be
       expressed in C */
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if (s->sh_type == SHT_PROGBITS &&
            (s->sh_flags & SHF_ALLOC)) {
            const char *p;
            int ch;

            /* check if section name can be expressed in C */
            p = s->name;
            for(;;) {
                ch = *p;
                if (!ch)
                    break;
                if (!isid(ch) && !isnum(ch))
                    goto next_sec;
                p++;
            }
            snprintf(buf, sizeof(buf), "__start_%s", s->name);
            set_elf_sym(symtab_section,
                        0, 0,
                        ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                        s->sh_num, buf);
            snprintf(buf, sizeof(buf), "__stop_%s", s->name);
            set_elf_sym(symtab_section,
                        s->data_offset, 0,
                        ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                        s->sh_num, buf);
        }
    next_sec: ;
    }
}

static void tcc_output_binary(TCCState *s1, FILE *f,
                              const int *sec_order)
{
    Section *s;
    int i, offset, size;

    offset = 0;
    for(i=1;i<s1->nb_sections;i++) {
        s = s1->sections[sec_order[i]];
        if (s->sh_type != SHT_NOBITS &&
            (s->sh_flags & SHF_ALLOC)) {
            while (offset < s->sh_offset) {
                fputc(0, f);
                offset++;
            }
            size = s->sh_size;
            fwrite(s->data, 1, size, f);
            offset += size;
        }
    }
}

#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#define HAVE_PHDR       1
#define EXTRA_RELITEMS  14
#else
#define HAVE_PHDR      1
#define EXTRA_RELITEMS 9
#endif

ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel)
{
    int sym_index = ELFW(R_SYM) (rel->r_info);
    ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
    struct sym_attr *attr = get_sym_attr(s1, sym_index, 0);
    unsigned offset = attr->got_offset;

    if (0 == offset)
        return;
    section_reserve(s1->got, offset + PTR_SIZE);
#ifdef TCC_TARGET_X86_64
    write64le(s1->got->data + offset, sym->st_value);
#else
    write32le(s1->got->data + offset, sym->st_value);
#endif
}

/* Perform relocation to GOT or PLT entries */
ST_FUNC void fill_got(TCCState *s1)
{
    Section *s;
    ElfW_Rel *rel;
    int i;

    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if (s->sh_type != SHT_RELX)
            continue;
        /* no need to handle got relocations */
        if (s->link != symtab_section)
            continue;
        for_each_elem(s, 0, rel, ElfW_Rel) {
            switch (ELFW(R_TYPE) (rel->r_info)) {
                case R_X86_64_GOT32:
                case R_X86_64_GOTPCREL:
        case R_X86_64_GOTPCRELX:
        case R_X86_64_REX_GOTPCRELX:
                case R_X86_64_PLT32:
                    fill_got_entry(s1, rel);
                    break;
            }
        }
    }
}

/* See put_got_entry for a description.  This is the second stage
   where GOT references to local defined symbols are rewritten.  */
static void fill_local_got_entries(TCCState *s1)
{
    ElfW_Rel *rel;
    for_each_elem(s1->got->reloc, 0, rel, ElfW_Rel) {
    if (ELFW(R_TYPE)(rel->r_info) == R_RELATIVE) {
        int sym_index = ELFW(R_SYM) (rel->r_info);
        ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
        struct sym_attr *attr = get_sym_attr(s1, sym_index, 0);
        unsigned offset = attr->got_offset;
        if (offset != rel->r_offset - s1->got->sh_addr)
          tcc_error_noabort("huh");
        rel->r_info = ELFW(R_INFO)(0, R_RELATIVE);
#if SHT_RELX == SHT_RELA
        rel->r_addend = sym->st_value;
#else
        /* All our REL architectures also happen to be 32bit LE.  */
        write32le(s1->got->data + offset, sym->st_value);
#endif
    }
    }
}

/* Bind symbols of executable: resolve undefined symbols from exported symbols
   in shared libraries and export non local defined symbols to shared libraries
   if -rdynamic switch was given on command line */
static void bind_exe_dynsyms(TCCState *s1)
{
    const char *name;
    int sym_index, index;
    ElfW(Sym) *sym, *esym;
    int type;

    /* Resolve undefined symbols from dynamic symbols. When there is a match:
       - if STT_FUNC or STT_GNU_IFUNC symbol -> add it in PLT
       - if STT_OBJECT symbol -> add it in .bss section with suitable reloc */
    for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
        if (sym->st_shndx == SHN_UNDEF) {
            name = (char *) symtab_section->link->data + sym->st_name;
            sym_index = find_elf_sym(s1->dynsymtab_section, name);
            if (sym_index) {
                esym = &((ElfW(Sym) *)s1->dynsymtab_section->data)[sym_index];
                type = ELFW(ST_TYPE)(esym->st_info);
                if ((type == STT_FUNC) || (type == STT_GNU_IFUNC)) {
                    /* Indirect functions shall have STT_FUNC type in executable
                     * dynsym section. Indeed, a dlsym call following a lazy
                     * resolution would pick the symbol value from the
                     * executable dynsym entry which would contain the address
                     * of the function wanted by the caller of dlsym instead of
                     * the address of the function that would return that
                     * address */
                    int dynindex
              = put_elf_sym(s1->dynsym, 0, esym->st_size,
                    ELFW(ST_INFO)(STB_GLOBAL,STT_FUNC), 0, 0,
                    name);
            int index = sym - (ElfW(Sym) *) symtab_section->data;
            get_sym_attr(s1, index, 1)->dyn_index = dynindex;
                } else if (type == STT_OBJECT) {
                    unsigned long offset;
                    ElfW(Sym) *dynsym;
                    offset = bss_section->data_offset;
                    /* XXX: which alignment ? */
                    offset = (offset + 16 - 1) & -16;
                    set_elf_sym (s1->symtab, offset, esym->st_size,
                                 esym->st_info, 0, bss_section->sh_num, name);
                    index = put_elf_sym(s1->dynsym, offset, esym->st_size,
                                        esym->st_info, 0, bss_section->sh_num,
                                        name);

                    /* Ensure R_COPY works for weak symbol aliases */
                    if (ELFW(ST_BIND)(esym->st_info) == STB_WEAK) {
                        for_each_elem(s1->dynsymtab_section, 1, dynsym, ElfW(Sym)) {
                            if ((dynsym->st_value == esym->st_value)
                                && (ELFW(ST_BIND)(dynsym->st_info) == STB_GLOBAL)) {
                                char *dynname = (char *) s1->dynsymtab_section->link->data
                                                + dynsym->st_name;
                                put_elf_sym(s1->dynsym, offset, dynsym->st_size,
                                            dynsym->st_info, 0,
                                            bss_section->sh_num, dynname);
                                break;
                            }
                        }
                    }

                    put_elf_reloc(s1->dynsym, bss_section,
                                  offset, R_COPY, index);
                    offset += esym->st_size;
                    bss_section->data_offset = offset;
                }
            } else {
                /* STB_WEAK undefined symbols are accepted */
                /* XXX: _fp_hw seems to be part of the ABI, so we ignore it */
                if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK ||
                    !strcmp(name, "_fp_hw")) {
                } else {
                    tcc_error_noabort("undefined symbol '%s'", name);
                }
            }
        } else if (s1->rdynamic && ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
            /* if -rdynamic option, then export all non local symbols */
            name = (char *) symtab_section->link->data + sym->st_name;
            set_elf_sym(s1->dynsym, sym->st_value, sym->st_size, sym->st_info,
                        0, sym->st_shndx, name);
        }
    }
}

/* Bind symbols of libraries: export all non local symbols of executable that
   are referenced by shared libraries. The reason is that the dynamic loader
   search symbol first in executable and then in libraries. Therefore a
   reference to a symbol already defined by a library can still be resolved by
   a symbol in the executable. */
static void bind_libs_dynsyms(TCCState *s1)
{
    const char *name;
    int sym_index;
    ElfW(Sym) *sym, *esym;

    for_each_elem(s1->dynsymtab_section, 1, esym, ElfW(Sym)) {
        name = (char *) s1->dynsymtab_section->link->data + esym->st_name;
        sym_index = find_elf_sym(symtab_section, name);
        /* XXX: avoid adding a symbol if already present because of
                -rdynamic ? */
        sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
        if (sym_index && sym->st_shndx != SHN_UNDEF)
            set_elf_sym(s1->dynsym, sym->st_value, sym->st_size, sym->st_info,
                        0, sym->st_shndx, name);
        else if (esym->st_shndx == SHN_UNDEF) {
            /* weak symbols can stay undefined */
            if (ELFW(ST_BIND)(esym->st_info) != STB_WEAK)
                tcc_warning("undefined dynamic symbol '%s'", name);
        }
    }
}

/* Export all non local symbols. This is used by shared libraries so that the
   non local symbols they define can resolve a reference in another shared
   library or in the executable. Correspondingly, it allows undefined local
   symbols to be resolved by other shared libraries or by the executable. */
static void export_global_syms(TCCState *s1)
{
    int dynindex, index;
    const char *name;
    ElfW(Sym) *sym;

    for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
        if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
        name = (char *) symtab_section->link->data + sym->st_name;
        dynindex = put_elf_sym(s1->dynsym, sym->st_value, sym->st_size,
                   sym->st_info, 0, sym->st_shndx, name);
        index = sym - (ElfW(Sym) *) symtab_section->data;
            get_sym_attr(s1, index, 1)->dyn_index = dynindex;
        }
    }
}

/* Allocate strings for section names and decide if an unallocated section
   should be output.
   NOTE: the strsec section comes last, so its size is also correct ! */
static void alloc_sec_names(TCCState *s1, int file_type, Section *strsec)
{
    int i;
    Section *s;

    /* Allocate strings for section names */
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        /* when generating a DLL, we include relocations but we may
           patch them */
        if (file_type == TCC_OUTPUT_DLL &&
            s->sh_type == SHT_RELX &&
            !(s->sh_flags & SHF_ALLOC)) {
            /* gr: avoid bogus relocs for empty (debug) sections */
            if (s1->sections[s->sh_info]->sh_flags & SHF_ALLOC)
                prepare_dynamic_rel(s1, s);
            else if (s1->do_debug)
                s->sh_size = s->data_offset;
        } else if (s1->do_debug ||
            file_type == TCC_OUTPUT_OBJ ||
            (s->sh_flags & SHF_ALLOC) ||
        i == (s1->nb_sections - 1)) {
            /* we output all sections if debug or object file */
            s->sh_size = s->data_offset;
        }
    if (s->sh_size || (s->sh_flags & SHF_ALLOC))
            s->sh_name = put_elf_str(strsec, s->name);
    }
    strsec->sh_size = strsec->data_offset;
}

/* Info to be copied in dynamic section */
struct dyn_inf {
    Section *dynamic;
    Section *dynstr;
    unsigned long dyn_rel_off;
    addr_t rel_addr;
    addr_t rel_size;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
    addr_t bss_addr;
    addr_t bss_size;
#endif
};

/* Assign sections to segments and decide how are sections laid out when loaded
   in memory. This function also fills corresponding program headers. */
static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum,
                           Section *interp, Section* strsec,
                           struct dyn_inf *dyninf, int *sec_order)
{
    int i, j, k, file_type, sh_order_index, file_offset;
    unsigned long s_align;
    long long tmp;
    addr_t addr;
    ElfW(Phdr) *ph;
    Section *s;

    file_type = s1->output_type;
    sh_order_index = 1;
    file_offset = 0;
    if (s1->output_format == TCC_OUTPUT_FORMAT_ELF)
        file_offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr));
    s_align = ELF_PAGE_SIZE;
    if (s1->section_align)
        s_align = s1->section_align;

    if (phnum > 0) {
        if (s1->has_text_addr) {
            int a_offset, p_offset;
            addr = s1->text_addr;
            /* we ensure that (addr % ELF_PAGE_SIZE) == file_offset %
               ELF_PAGE_SIZE */
            a_offset = (int) (addr & (s_align - 1));
            p_offset = file_offset & (s_align - 1);
            if (a_offset < p_offset)
                a_offset += s_align;
            file_offset += (a_offset - p_offset);
        } else {
            if (file_type == TCC_OUTPUT_DLL)
                addr = 0;
            else
                addr = ELF_START_ADDR;
            /* compute address after headers */
            addr += (file_offset & (s_align - 1));
        }

        ph = &phdr[0];
        /* Leave one program headers for the program interpreter and one for
           the program header table itself if needed. These are done later as
           they require section layout to be done first. */
        if (interp)
            ph += 1 + HAVE_PHDR;

        /* dynamic relocation table information, for .dynamic section */
        dyninf->rel_addr = dyninf->rel_size = 0;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
        dyninf->bss_addr = dyninf->bss_size = 0;
#endif

        for(j = 0; j < 2; j++) {
            ph->p_type = PT_LOAD;
            if (j == 0)
                ph->p_flags = PF_R | PF_X;
            else
                ph->p_flags = PF_R | PF_W;
            ph->p_align = s_align;

            /* Decide the layout of sections loaded in memory. This must
               be done before program headers are filled since they contain
               info about the layout. We do the following ordering: interp,
               symbol tables, relocations, progbits, nobits */
            /* XXX: do faster and simpler sorting */
            for(k = 0; k < 5; k++) {
                for(i = 1; i < s1->nb_sections; i++) {
                    s = s1->sections[i];
                    /* compute if section should be included */
                    if (j == 0) {
                        if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
                            SHF_ALLOC)
                            continue;
                    } else {
                        if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
                            (SHF_ALLOC | SHF_WRITE))
                            continue;
                    }
                    if (s == interp) {
                        if (k != 0)
                            continue;
                    } else if (s->sh_type == SHT_DYNSYM ||
                               s->sh_type == SHT_STRTAB ||
                               s->sh_type == SHT_HASH) {
                        if (k != 1)
                            continue;
                    } else if (s->sh_type == SHT_RELX) {
                        if (k != 2)
                            continue;
                    } else if (s->sh_type == SHT_NOBITS) {
                        if (k != 4)
                            continue;
                    } else {
                        if (k != 3)
                            continue;
                    }
                    sec_order[sh_order_index++] = i;

                    /* section matches: we align it and add its size */
                    tmp = addr;
                    addr = (addr + s->sh_addralign - 1) &
                        ~(s->sh_addralign - 1);
                    file_offset += (int) ( addr - tmp );
                    s->sh_offset = file_offset;
                    s->sh_addr = addr;

                    /* update program header infos */
                    if (ph->p_offset == 0) {
                        ph->p_offset = file_offset;
                        ph->p_vaddr = addr;
                        ph->p_paddr = ph->p_vaddr;
                    }
                    /* update dynamic relocation infos */
                    if (s->sh_type == SHT_RELX) {
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
                        if (!strcmp(strsec->data + s->sh_name, ".rel.got")) {
                            dyninf->rel_addr = addr;
                            dyninf->rel_size += s->sh_size; /* XXX only first rel. */
                        }
                        if (!strcmp(strsec->data + s->sh_name, ".rel.bss")) {
                            dyninf->bss_addr = addr;
                            dyninf->bss_size = s->sh_size; /* XXX only first rel. */
                        }
#else
                        if (dyninf->rel_size == 0)
                            dyninf->rel_addr = addr;
                        dyninf->rel_size += s->sh_size;
#endif
                    }
                    addr += s->sh_size;
                    if (s->sh_type != SHT_NOBITS)
                        file_offset += s->sh_size;
                }
            }
        if (j == 0) {
        /* Make the first PT_LOAD segment include the program
           headers itself (and the ELF header as well), it'll
           come out with same memory use but will make various
           tools like binutils strip work better.  */
        ph->p_offset &= ~(ph->p_align - 1);
        ph->p_vaddr &= ~(ph->p_align - 1);
        ph->p_paddr &= ~(ph->p_align - 1);
        }
            ph->p_filesz = file_offset - ph->p_offset;
            ph->p_memsz = addr - ph->p_vaddr;
            ph++;
            if (j == 0) {
                if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) {
                    /* if in the middle of a page, we duplicate the page in
                       memory so that one copy is RX and the other is RW */
                    if ((addr & (s_align - 1)) != 0)
                        addr += s_align;
                } else {
                    addr = (addr + s_align - 1) & ~(s_align - 1);
                    file_offset = (file_offset + s_align - 1) & ~(s_align - 1);
                }
            }
        }
    }

    /* all other sections come after */
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if (phnum > 0 && (s->sh_flags & SHF_ALLOC))
            continue;
        sec_order[sh_order_index++] = i;

        file_offset = (file_offset + s->sh_addralign - 1) &
            ~(s->sh_addralign - 1);
        s->sh_offset = file_offset;
        if (s->sh_type != SHT_NOBITS)
            file_offset += s->sh_size;
    }

    return file_offset;
}

static void fill_unloadable_phdr(ElfW(Phdr) *phdr, int phnum, Section *interp,
                                 Section *dynamic)
{
    ElfW(Phdr) *ph;

    /* if interpreter, then add corresponding program header */
    if (interp) {
        ph = &phdr[0];

        if (HAVE_PHDR)
        {
            int len = phnum * sizeof(ElfW(Phdr));

            ph->p_type = PT_PHDR;
            ph->p_offset = sizeof(ElfW(Ehdr));
            ph->p_vaddr = interp->sh_addr - len;
            ph->p_paddr = ph->p_vaddr;
            ph->p_filesz = ph->p_memsz = len;
            ph->p_flags = PF_R | PF_X;
            ph->p_align = 4; /* interp->sh_addralign; */
            ph++;
        }

        ph->p_type = PT_INTERP;
        ph->p_offset = interp->sh_offset;
        ph->p_vaddr = interp->sh_addr;
        ph->p_paddr = ph->p_vaddr;
        ph->p_filesz = interp->sh_size;
        ph->p_memsz = interp->sh_size;
        ph->p_flags = PF_R;
        ph->p_align = interp->sh_addralign;
    }

    /* if dynamic section, then add corresponding program header */
    if (dynamic) {
        ph = &phdr[phnum - 1];

        ph->p_type = PT_DYNAMIC;
        ph->p_offset = dynamic->sh_offset;
        ph->p_vaddr = dynamic->sh_addr;
        ph->p_paddr = ph->p_vaddr;
        ph->p_filesz = dynamic->sh_size;
        ph->p_memsz = dynamic->sh_size;
        ph->p_flags = PF_R | PF_W;
        ph->p_align = dynamic->sh_addralign;
    }
}

/* Fill the dynamic section with tags describing the address and size of
   sections */
static void fill_dynamic(TCCState *s1, struct dyn_inf *dyninf)
{
    Section *dynamic;

    dynamic = dyninf->dynamic;

    /* put dynamic section entries */
    dynamic->data_offset = dyninf->dyn_rel_off;
    put_dt(dynamic, DT_HASH, s1->dynsym->hash->sh_addr);
    put_dt(dynamic, DT_STRTAB, dyninf->dynstr->sh_addr);
    put_dt(dynamic, DT_SYMTAB, s1->dynsym->sh_addr);
    put_dt(dynamic, DT_STRSZ, dyninf->dynstr->data_offset);
    put_dt(dynamic, DT_SYMENT, sizeof(ElfW(Sym)));
#if PTR_SIZE == 8
    put_dt(dynamic, DT_RELA, dyninf->rel_addr);
    put_dt(dynamic, DT_RELASZ, dyninf->rel_size);
    put_dt(dynamic, DT_RELAENT, sizeof(ElfW_Rel));
#else
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
    put_dt(dynamic, DT_PLTGOT, s1->got->sh_addr);
    put_dt(dynamic, DT_PLTRELSZ, dyninf->rel_size);
    put_dt(dynamic, DT_JMPREL, dyninf->rel_addr);
    put_dt(dynamic, DT_PLTREL, DT_REL);
    put_dt(dynamic, DT_REL, dyninf->bss_addr);
    put_dt(dynamic, DT_RELSZ, dyninf->bss_size);
#else
    put_dt(dynamic, DT_REL, dyninf->rel_addr);
    put_dt(dynamic, DT_RELSZ, dyninf->rel_size);
    put_dt(dynamic, DT_RELENT, sizeof(ElfW_Rel));
#endif
#endif
    if (s1->do_debug)
        put_dt(dynamic, DT_DEBUG, 0);
    put_dt(dynamic, DT_NULL, 0);
}

/* Relocate remaining sections and symbols (that is those not related to
   dynamic linking) */
static int final_sections_reloc(TCCState *s1)
{
    int i;
    Section *s;

    relocate_syms(s1, s1->symtab, 0);

    if (s1->nb_errors != 0)
        return -1;

    /* relocate sections */
    /* XXX: ignore sections with allocated relocations ? */
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
#if defined(TCC_TARGET_I386) || defined(TCC_MUSL)
        if (s->reloc && s != s1->got && (s->sh_flags & SHF_ALLOC)) //gr
        /* On X86 gdb 7.3 works in any case but gdb 6.6 will crash if SHF_ALLOC
        checking is removed */
#else
        if (s->reloc && s != s1->got)
        /* On X86_64 gdb 7.3 will crash if SHF_ALLOC checking is present */
#endif
            relocate_section(s1, s);
    }

    /* relocate relocation entries if the relocation tables are
       allocated in the executable */
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if ((s->sh_flags & SHF_ALLOC) &&
            s->sh_type == SHT_RELX) {
            relocate_rel(s1, s);
        }
    }
    return 0;
}

/* Create an ELF file on disk.
   This function handle ELF specific layout requirements */
static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr,
                           int file_offset, int *sec_order)
{
    int i, shnum, offset, size, file_type;
    Section *s;
    ElfW(Ehdr) ehdr;
    ElfW(Shdr) shdr, *sh;

    file_type = s1->output_type;
    shnum = s1->nb_sections;

    memset(&ehdr, 0, sizeof(ehdr));

    if (phnum > 0) {
        ehdr.e_phentsize = sizeof(ElfW(Phdr));
        ehdr.e_phnum = phnum;
        ehdr.e_phoff = sizeof(ElfW(Ehdr));
    }

    /* align to 4 */
    file_offset = (file_offset + 3) & -4;

    /* fill header */
    ehdr.e_ident[0] = ELFMAG0;
    ehdr.e_ident[1] = ELFMAG1;
    ehdr.e_ident[2] = ELFMAG2;
    ehdr.e_ident[3] = ELFMAG3;
    ehdr.e_ident[4] = ELFCLASSW;
    ehdr.e_ident[5] = ELFDATA2LSB;
    ehdr.e_ident[6] = EV_CURRENT;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
    ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
#endif
#ifdef TCC_TARGET_ARM
#if defined (TCC_ARM_EABI) || BOOTSTRAP
    ehdr.e_ident[EI_OSABI] = 0;
    ehdr.e_flags = EF_ARM_EABI_VER4;
    if (file_type == TCC_OUTPUT_EXE || file_type == TCC_OUTPUT_DLL)
        ehdr.e_flags |= EF_ARM_HASENTRY;
    if (s1->float_abi == ARM_HARD_FLOAT)
        ehdr.e_flags |= EF_ARM_VFP_FLOAT;
    else
        ehdr.e_flags |= EF_ARM_SOFT_FLOAT;
#else
    ehdr.e_ident[EI_OSABI] = ELFOSABI_ARM;
#endif
#endif
    switch(file_type) {
    default:
    case TCC_OUTPUT_EXE:
        ehdr.e_type = ET_EXEC;
        ehdr.e_entry = get_elf_sym_addr(s1, "_start", 1);
        break;
    case TCC_OUTPUT_DLL:
        ehdr.e_type = ET_DYN;
        ehdr.e_entry = text_section->sh_addr; /* XXX: is it correct ? */
        break;
    case TCC_OUTPUT_OBJ:
        ehdr.e_type = ET_REL;
        break;
    }
    ehdr.e_machine = EM_TCC_TARGET;
    ehdr.e_version = EV_CURRENT;
    ehdr.e_shoff = file_offset;
    ehdr.e_ehsize = sizeof(ElfW(Ehdr));
    ehdr.e_shentsize = sizeof(ElfW(Shdr));
    ehdr.e_shnum = shnum;
    ehdr.e_shstrndx = shnum - 1;

    fwrite(&ehdr, 1, sizeof(ElfW(Ehdr)), f);
    fwrite(phdr, 1, phnum * sizeof(ElfW(Phdr)), f);
    offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr));

    sort_syms(s1, symtab_section);
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[sec_order[i]];
        if (s->sh_type != SHT_NOBITS) {
            while (offset < s->sh_offset) {
                fputc(0, f);
                offset++;
            }
            size = s->sh_size;
            if (size)
                fwrite(s->data, 1, size, f);
            offset += size;
        }
    }

    /* output section headers */
    while (offset < ehdr.e_shoff) {
        fputc(0, f);
        offset++;
    }

    for(i = 0; i < s1->nb_sections; i++) {
        sh = &shdr;
        memset(sh, 0, sizeof(ElfW(Shdr)));
        s = s1->sections[i];
        if (s) {
            sh->sh_name = s->sh_name;
            sh->sh_type = s->sh_type;
            sh->sh_flags = s->sh_flags;
            sh->sh_entsize = s->sh_entsize;
            sh->sh_info = s->sh_info;
            if (s->link)
                sh->sh_link = s->link->sh_num;
            sh->sh_addralign = s->sh_addralign;
            sh->sh_addr = s->sh_addr;
            sh->sh_offset = s->sh_offset;
            sh->sh_size = s->sh_size;
        }
        fwrite(sh, 1, sizeof(ElfW(Shdr)), f);
    }
}

/* Write an elf, coff or "binary" file */
static int tcc_write_elf_file(TCCState *s1, const char *filename, int phnum,
                              ElfW(Phdr) *phdr, int file_offset, int *sec_order)
{
    int fd, mode, file_type;
    FILE *f;

    file_type = s1->output_type;
    if (file_type == TCC_OUTPUT_OBJ)
        mode = 0666;
    else
        mode = 0777;
    unlink(filename);
    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode);
    if (fd < 0) {
        tcc_error_noabort("could not write '%s'", filename);
        return -1;
    }
    f = fdopen(fd, "wb");
    if (s1->verbose)
        printf("<- %s\n", filename);

#ifdef TCC_TARGET_COFF
    if (s1->output_format == TCC_OUTPUT_FORMAT_COFF)
        tcc_output_coff(s1, f);
    else
#endif
    if (s1->output_format == TCC_OUTPUT_FORMAT_ELF)
        tcc_output_elf(s1, f, phnum, phdr, file_offset, sec_order);
    else
        tcc_output_binary(s1, f, sec_order);
    fclose(f);

    return 0;
}

/* Sort section headers by assigned sh_addr, remove sections
   that we aren't going to output.  */
static void tidy_section_headers(TCCState *s1, int *sec_order)
{
    int i, nnew, l, *backmap;
    Section **snew, *s;
    ElfW(Sym) *sym;

    snew = tcc_malloc(s1->nb_sections * sizeof(snew[0]));
    backmap = tcc_malloc(s1->nb_sections * sizeof(backmap[0]));
    for (i = 0, nnew = 0, l = s1->nb_sections; i < s1->nb_sections; i++) {
    s = s1->sections[sec_order[i]];
    if (!i || s->sh_name) {
        backmap[sec_order[i]] = nnew;
        snew[nnew] = s;
        ++nnew;
    } else {
        backmap[sec_order[i]] = 0;
        snew[--l] = s;
    }
    }
    for (i = 0; i < nnew; i++) {
    s = snew[i];
    if (s) {
        s->sh_num = i;
            if (s->sh_type == SHT_RELX)
        s->sh_info = backmap[s->sh_info];
    }
    }

    for_each_elem(symtab_section, 1, sym, ElfW(Sym))
    if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE)
        sym->st_shndx = backmap[sym->st_shndx];
    for_each_elem(s1->dynsym, 1, sym, ElfW(Sym))
    if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE)
        sym->st_shndx = backmap[sym->st_shndx];

    for (i = 0; i < s1->nb_sections; i++)
    sec_order[i] = i;
    tcc_free(s1->sections);
    s1->sections = snew;
    s1->nb_sections = nnew;
    tcc_free(backmap);
}

/* Output an elf, coff or binary file */
/* XXX: suppress unneeded sections */
static int elf_output_file(TCCState *s1, const char *filename)
{
    int i, ret, phnum, shnum, file_type, file_offset, *sec_order;
    struct dyn_inf dyninf = {0};
    ElfW(Phdr) *phdr;
    ElfW(Sym) *sym;
    Section *strsec, *interp, *dynamic, *dynstr;

    file_type = s1->output_type;
    s1->nb_errors = 0;

    /* if linking, also link in runtime libraries (libc, libgcc, etc.) */
    if (file_type != TCC_OUTPUT_OBJ) {
        tcc_add_runtime(s1);
    }

    phdr = NULL;
    sec_order = NULL;
    interp = dynamic = dynstr = NULL; /* avoid warning */

    if (file_type != TCC_OUTPUT_OBJ) {
        relocate_common_syms();

        tcc_add_linker_symbols(s1);

        if (!s1->static_link) {
            if (file_type == TCC_OUTPUT_EXE) {
                char *ptr;
                /* allow override the dynamic loader */
                const char *elfint = getenv("LD_SO");
                if (elfint == NULL)
                    elfint = DEFAULT_ELFINTERP(s1);
                /* add interpreter section only if executable */
                interp = new_section(s1, ".interp", SHT_PROGBITS, SHF_ALLOC);
                interp->sh_addralign = 1;
                ptr = section_ptr_add(interp, 1 + strlen(elfint));
                strcpy(ptr, elfint);
            }

            /* add dynamic symbol table */
            s1->dynsym = new_symtab(s1, ".dynsym", SHT_DYNSYM, SHF_ALLOC,
                                    ".dynstr",
                                    ".hash", SHF_ALLOC);
            dynstr = s1->dynsym->link;

            /* add dynamic section */
            dynamic = new_section(s1, ".dynamic", SHT_DYNAMIC,
                                  SHF_ALLOC | SHF_WRITE);
            dynamic->link = dynstr;
            dynamic->sh_entsize = sizeof(ElfW(Dyn));

            build_got(s1);

            if (file_type == TCC_OUTPUT_EXE) {
                bind_exe_dynsyms(s1);

                if (s1->nb_errors) {
                    ret = -1;
                    goto the_end;
                }

                bind_libs_dynsyms(s1);
            } else /* shared library case: simply export all global symbols */
                export_global_syms(s1);

            build_got_entries(s1);

            /* add a list of needed dlls */
            for(i = 0; i < s1->nb_loaded_dlls; i++) {
                DLLReference *dllref = s1->loaded_dlls[i];
                if (dllref->level == 0)
                    put_dt(dynamic, DT_NEEDED, put_elf_str(dynstr, dllref->name));
            }

            if (s1->rpath)
                put_dt(dynamic, s1->enable_new_dtags ? DT_RUNPATH : DT_RPATH,
                    put_elf_str(dynstr, s1->rpath));

            /* XXX: currently, since we do not handle PIC code, we
               must relocate the readonly segments */
            if (file_type == TCC_OUTPUT_DLL) {
                if (s1->soname)
                    put_dt(dynamic, DT_SONAME, put_elf_str(dynstr, s1->soname));
                put_dt(dynamic, DT_TEXTREL, 0);
            }

            if (s1->symbolic)
                put_dt(dynamic, DT_SYMBOLIC, 0);

            /* add necessary space for other entries */
            dyninf.dyn_rel_off = dynamic->data_offset;
            dynamic->data_offset += sizeof(ElfW(Dyn)) * EXTRA_RELITEMS;
        } else {
            /* still need to build got entries in case of static link */
            build_got_entries(s1);
        }
    }

    /* we add a section for symbols */
    strsec = new_section(s1, ".shstrtab", SHT_STRTAB, 0);
    put_elf_str(strsec, "");

    /* compute number of sections */
    shnum = s1->nb_sections;

    /* this array is used to reorder sections in the output file */
    sec_order = tcc_malloc(sizeof(int) * shnum);
    sec_order[0] = 0;

    /* compute number of program headers */
    switch(file_type) {
    default:
    case TCC_OUTPUT_OBJ:
        phnum = 0;
        break;
    case TCC_OUTPUT_EXE:
        if (!s1->static_link)
            phnum = 4 + HAVE_PHDR;
        else
            phnum = 2;
        break;
    case TCC_OUTPUT_DLL:
        phnum = 3;
        break;
    }

    /* Allocate strings for section names */
    alloc_sec_names(s1, file_type, strsec);

    /* allocate program segment headers */
    phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr)));

    /* compute section to program header mapping */
    file_offset = layout_sections(s1, phdr, phnum, interp, strsec, &dyninf,
                                  sec_order);

    /* Fill remaining program header and finalize relocation related to dynamic
       linking. */
    if (phnum > 0) {
        fill_unloadable_phdr(phdr, phnum, interp, dynamic);
        if (dynamic) {
            dyninf.dynamic = dynamic;
            dyninf.dynstr = dynstr;

            fill_dynamic(s1, &dyninf);

            /* put in GOT the dynamic section address and relocate PLT */
            write32le(s1->got->data, dynamic->sh_addr);
            if (file_type == TCC_OUTPUT_EXE
                || (RELOCATE_DLLPLT && file_type == TCC_OUTPUT_DLL))
                relocate_plt(s1);

            /* relocate symbols in .dynsym now that final addresses are known */
            for_each_elem(s1->dynsym, 1, sym, ElfW(Sym)) {
                if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE) {
                    /* do symbol relocation */
                    sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
                }
            }
        }
    }

    /* if building executable or DLL, then relocate each section
       except the GOT which is already relocated */
    if (file_type != TCC_OUTPUT_OBJ) {
        ret = final_sections_reloc(s1);
        if (ret)
            goto the_end;
#if !BOOTSTRAP
    tidy_section_headers(s1, sec_order);
#endif
    }

    /* Perform relocation to GOT or PLT entries */
    if (file_type == TCC_OUTPUT_EXE && s1->static_link)
        fill_got(s1);
    else if (s1->got)
        fill_local_got_entries(s1);

    /* Create the ELF file with name 'filename' */
    ret = tcc_write_elf_file(s1, filename, phnum, phdr, file_offset, sec_order);
    s1->nb_sections = shnum;
 the_end:
    tcc_free(sec_order);
    tcc_free(phdr);
    return ret;
}

LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename)
{
    int ret;
#ifdef TCC_TARGET_PE
    if (s->output_type != TCC_OUTPUT_OBJ) {
        ret = pe_output_file(s, filename);
    } else
#endif
        ret = elf_output_file(s, filename);
    return ret;
}

static void *load_data(int fd, unsigned long file_offset, unsigned long size)
{
    void *data;

    data = tcc_malloc(size);
    lseek(fd, file_offset, SEEK_SET);
    read(fd, data, size);
    return data;
}

typedef struct SectionMergeInfo {
    Section *s;            /* corresponding existing section */
    unsigned long offset;  /* offset of the new section in the existing section */
    uint8_t new_section;       /* true if section 's' was added */
    uint8_t link_once;         /* true if link once section */
} SectionMergeInfo;

ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h)
{
    int size = read(fd, h, sizeof *h);
    if (size == sizeof *h && 0 == memcmp(h, ELFMAG, 4)) {
        if (h->e_type == ET_REL)
            return AFF_BINTYPE_REL;
        if (h->e_type == ET_DYN)
            return AFF_BINTYPE_DYN;
    } else if (size >= 8) {
        if (0 == memcmp(h, ARMAG, 8))
            return AFF_BINTYPE_AR;
#ifdef TCC_TARGET_COFF
        if (((struct filehdr*)h)->f_magic == COFF_C67_MAGIC)
            return AFF_BINTYPE_C67;
#endif
    }
    return 0;
}

/* load an object file and merge it with current files */
/* XXX: handle correctly stab (debug) info */
ST_FUNC int tcc_load_object_file(TCCState *s1,
                                int fd, unsigned long file_offset)
{
    ElfW(Ehdr) ehdr;
    ElfW(Shdr) *shdr, *sh;
    int size, i, j, offset, offseti, nb_syms, sym_index, ret, seencompressed;
    unsigned char *strsec, *strtab;
    int *old_to_new_syms;
    char *sh_name, *name;
    SectionMergeInfo *sm_table, *sm;
    ElfW(Sym) *sym, *symtab;
    ElfW_Rel *rel;
    Section *s;

    int stab_index;
    int stabstr_index;

    stab_index = stabstr_index = 0;

    lseek(fd, file_offset, SEEK_SET);
    if (tcc_object_type(fd, &ehdr) != AFF_BINTYPE_REL)
        goto fail1;
    /* test CPU specific stuff */
    if (ehdr.e_ident[5] != ELFDATA2LSB ||
        ehdr.e_machine != EM_TCC_TARGET) {
    fail1:
        tcc_error_noabort("invalid object file");
        return -1;
    }
    /* read sections */
    shdr = load_data(fd, file_offset + ehdr.e_shoff,
                     sizeof(ElfW(Shdr)) * ehdr.e_shnum);
    sm_table = tcc_mallocz(sizeof(SectionMergeInfo) * ehdr.e_shnum);

    /* load section names */
    sh = &shdr[ehdr.e_shstrndx];
    strsec = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);

    /* load symtab and strtab */
    old_to_new_syms = NULL;
    symtab = NULL;
    strtab = NULL;
    nb_syms = 0;
    seencompressed = 0;
    for(i = 1; i < ehdr.e_shnum; i++) {
        sh = &shdr[i];
        if (sh->sh_type == SHT_SYMTAB) {
            if (symtab) {
                tcc_error_noabort("object must contain only one symtab");
            fail:
                ret = -1;
                goto the_end;
            }
            nb_syms = sh->sh_size / sizeof(ElfW(Sym));
            symtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
            sm_table[i].s = symtab_section;

            /* now load strtab */
            sh = &shdr[sh->sh_link];
            strtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
        }
    if (sh->sh_flags & SHF_COMPRESSED)
        seencompressed = 1;
    }

    /* now examine each section and try to merge its content with the
       ones in memory */
    for(i = 1; i < ehdr.e_shnum; i++) {
        /* no need to examine section name strtab */
        if (i == ehdr.e_shstrndx)
            continue;
        sh = &shdr[i];
        sh_name = (char *) strsec + sh->sh_name;
        /* ignore sections types we do not handle */
        if (sh->sh_type != SHT_PROGBITS &&
            sh->sh_type != SHT_RELX &&
#ifdef TCC_ARM_EABI
            sh->sh_type != SHT_ARM_EXIDX &&
#endif
            sh->sh_type != SHT_NOBITS &&
            sh->sh_type != SHT_PREINIT_ARRAY &&
            sh->sh_type != SHT_INIT_ARRAY &&
            sh->sh_type != SHT_FINI_ARRAY &&
            strcmp(sh_name, ".stabstr")
            )
            continue;
    if (seencompressed
        && (!strncmp(sh_name, ".debug_", sizeof(".debug_")-1)
        || (sh->sh_type == SHT_RELX
            && !strncmp((char*)strsec + shdr[sh->sh_info].sh_name,
                    ".debug_", sizeof(".debug_")-1))))
      continue;
        if (sh->sh_addralign < 1)
            sh->sh_addralign = 1;
        /* find corresponding section, if any */
        for(j = 1; j < s1->nb_sections;j++) {
            s = s1->sections[j];
            if (!strcmp(s->name, sh_name)) {
                if (!strncmp(sh_name, ".gnu.linkonce",
                             sizeof(".gnu.linkonce") - 1)) {
                    /* if a 'linkonce' section is already present, we
                       do not add it again. It is a little tricky as
                       symbols can still be defined in
                       it. */
                    sm_table[i].link_once = 1;
                    goto next;
                } else {
                    goto found;
                }
            }
        }
        /* not found: create new section */
        s = new_section(s1, sh_name, sh->sh_type, sh->sh_flags & ~SHF_GROUP);
        /* take as much info as possible from the section. sh_link and
           sh_info will be updated later */
        s->sh_addralign = sh->sh_addralign;
        s->sh_entsize = sh->sh_entsize;
        sm_table[i].new_section = 1;
    found:
        if (sh->sh_type != s->sh_type) {
            tcc_error_noabort("invalid section type");
            goto fail;
        }

        /* align start of section */
        offset = s->data_offset;

        if (0 == strcmp(sh_name, ".stab")) {
            stab_index = i;
            goto no_align;
        }
        if (0 == strcmp(sh_name, ".stabstr")) {
            stabstr_index = i;
            goto no_align;
        }

        size = sh->sh_addralign - 1;
        offset = (offset + size) & ~size;
        if (sh->sh_addralign > s->sh_addralign)
            s->sh_addralign = sh->sh_addralign;
        s->data_offset = offset;
    no_align:
        sm_table[i].offset = offset;
        sm_table[i].s = s;
        /* concatenate sections */
        size = sh->sh_size;
        if (sh->sh_type != SHT_NOBITS) {
            unsigned char *ptr;
            lseek(fd, file_offset + sh->sh_offset, SEEK_SET);
            ptr = section_ptr_add(s, size);
            read(fd, ptr, size);
        } else {
            s->data_offset += size;
        }
    next: ;
    }

    /* gr relocate stab strings */
    if (stab_index && stabstr_index) {
        Stab_Sym *a, *b;
        unsigned o;
        s = sm_table[stab_index].s;
        a = (Stab_Sym *)(s->data + sm_table[stab_index].offset);
        b = (Stab_Sym *)(s->data + s->data_offset);
        o = sm_table[stabstr_index].offset;
        while (a < b)
            a->n_strx += o, a++;
    }

    /* second short pass to update sh_link and sh_info fields of new
       sections */
    for(i = 1; i < ehdr.e_shnum; i++) {
        s = sm_table[i].s;
        if (!s || !sm_table[i].new_section)
            continue;
        sh = &shdr[i];
        if (sh->sh_link > 0)
            s->link = sm_table[sh->sh_link].s;
        if (sh->sh_type == SHT_RELX) {
            s->sh_info = sm_table[sh->sh_info].s->sh_num;
            /* update backward link */
            s1->sections[s->sh_info]->reloc = s;
        }
    }
    sm = sm_table;

    /* resolve symbols */
    old_to_new_syms = tcc_mallocz(nb_syms * sizeof(int));

    sym = symtab + 1;
    for(i = 1; i < nb_syms; i++, sym++) {
        if (sym->st_shndx != SHN_UNDEF &&
            sym->st_shndx < SHN_LORESERVE) {
            sm = &sm_table[sym->st_shndx];
            if (sm->link_once) {
                /* if a symbol is in a link once section, we use the
                   already defined symbol. It is very important to get
                   correct relocations */
                if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
                    name = (char *) strtab + sym->st_name;
                    sym_index = find_elf_sym(symtab_section, name);
                    if (sym_index)
                        old_to_new_syms[i] = sym_index;
                }
                continue;
            }
            /* if no corresponding section added, no need to add symbol */
            if (!sm->s)
                continue;
            /* convert section number */
            sym->st_shndx = sm->s->sh_num;
            /* offset value */
            sym->st_value += sm->offset;
        }
        /* add symbol */
        name = (char *) strtab + sym->st_name;
        sym_index = set_elf_sym(symtab_section, sym->st_value, sym->st_size,
                                sym->st_info, sym->st_other,
                                sym->st_shndx, name);
        old_to_new_syms[i] = sym_index;
    }

    /* third pass to patch relocation entries */
    for(i = 1; i < ehdr.e_shnum; i++) {
        s = sm_table[i].s;
        if (!s)
            continue;
        sh = &shdr[i];
        offset = sm_table[i].offset;
        switch(s->sh_type) {
        case SHT_RELX:
            /* take relocation offset information */
            offseti = sm_table[sh->sh_info].offset;
            for_each_elem(s, (offset / sizeof(*rel)), rel, ElfW_Rel) {
                int type;
                unsigned sym_index;
                /* convert symbol index */
                type = ELFW(R_TYPE)(rel->r_info);
                sym_index = ELFW(R_SYM)(rel->r_info);
                /* NOTE: only one symtab assumed */
                if (sym_index >= nb_syms)
                    goto invalid_reloc;
                sym_index = old_to_new_syms[sym_index];
                /* ignore link_once in rel section. */
                if (!sym_index && !sm->link_once
#ifdef TCC_TARGET_ARM
                    && type != R_ARM_V4BX
#endif
                   ) {
                invalid_reloc:
                    tcc_error_noabort("Invalid relocation entry [%2d] '%s' @ %.8x",
                        i, strsec + sh->sh_name, rel->r_offset);
                    goto fail;
                }
                rel->r_info = ELFW(R_INFO)(sym_index, type);
                /* offset the relocation offset */
                rel->r_offset += offseti;
#ifdef TCC_TARGET_ARM
                /* Jumps and branches from a Thumb code to a PLT entry need
                   special handling since PLT entries are ARM code.
                   Unconditional bl instructions referencing PLT entries are
                   handled by converting these instructions into blx
                   instructions. Other case of instructions referencing a PLT
                   entry require to add a Thumb stub before the PLT entry to
                   switch to ARM mode. We set bit plt_thumb_stub of the
                   attribute of a symbol to indicate such a case. */
                if (type == R_ARM_THM_JUMP24)
                    get_sym_attr(s1, sym_index, 1)->plt_thumb_stub = 1;
#endif
            }
            break;
        default:
            break;
        }
    }

    ret = 0;
 the_end:
    tcc_free(symtab);
    tcc_free(strtab);
    tcc_free(old_to_new_syms);
    tcc_free(sm_table);
    tcc_free(strsec);
    tcc_free(shdr);
    return ret;
}

typedef struct ArchiveHeader {
    char ar_name[16];           /* name of this member */
    char ar_date[12];           /* file mtime */
    char ar_uid[6];             /* owner uid; printed as decimal */
    char ar_gid[6];             /* owner gid; printed as decimal */
    char ar_mode[8];            /* file mode, printed as octal   */
    char ar_size[10];           /* file size, printed as decimal */
    char ar_fmag[2];            /* should contain ARFMAG */
} ArchiveHeader;

static int get_be32(const uint8_t *b)
{
    return b[3] | (b[2] << 8) | (b[1] << 16) | (b[0] << 24);
}

static long get_be64(const uint8_t *b)
{
  long long ret = get_be32(b);
  ret = (ret << 32) | (unsigned)get_be32(b+4);
  return (long)ret;
}

/* load only the objects which resolve undefined symbols */
static int tcc_load_alacarte(TCCState *s1, int fd, int size, int entrysize)
{
    long i, bound, nsyms, sym_index, off, ret;
    uint8_t *data;
    const char *ar_names, *p;
    const uint8_t *ar_index;
    ElfW(Sym) *sym;

    data = tcc_malloc(size);
    if (read(fd, data, size) != size)
        goto fail;
    nsyms = entrysize == 4 ? get_be32(data) : get_be64(data);
    ar_index = data + entrysize;
    ar_names = (char *) ar_index + nsyms * entrysize;

    do {
        bound = 0;
        for(p = ar_names, i = 0; i < nsyms; i++, p += strlen(p)+1) {
            sym_index = find_elf_sym(symtab_section, p);
            if(sym_index) {
                sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
                if(sym->st_shndx == SHN_UNDEF) {
                    off = (entrysize == 4
               ? get_be32(ar_index + i * 4)
               : get_be64(ar_index + i * 8))
              + sizeof(ArchiveHeader);
                    ++bound;
                    if(tcc_load_object_file(s1, fd, off) < 0) {
                    fail:
                        ret = -1;
                        goto the_end;
                    }
                }
            }
        }
    } while(bound);
    ret = 0;
 the_end:
    tcc_free(data);
    return ret;
}

/* load a '.a' file */
ST_FUNC int tcc_load_archive(TCCState *s1, int fd)
{
    ArchiveHeader hdr;
    char ar_size[11];
    char ar_name[17];
    char magic[8];
    int size, len, i;
    unsigned long file_offset;

    /* skip magic which was already checked */
    read(fd, magic, sizeof(magic));

    for(;;) {
        len = read(fd, &hdr, sizeof(hdr));
        if (len == 0)
            break;
        if (len != sizeof(hdr)) {
            tcc_error_noabort("invalid archive");
            return -1;
        }
        memcpy(ar_size, hdr.ar_size, sizeof(hdr.ar_size));
        ar_size[sizeof(hdr.ar_size)] = '\0';
        size = strtol(ar_size, NULL, 0);
        memcpy(ar_name, hdr.ar_name, sizeof(hdr.ar_name));
        for(i = sizeof(hdr.ar_name) - 1; i >= 0; i--) {
            if (ar_name[i] != ' ')
                break;
        }
        ar_name[i + 1] = '\0';
        file_offset = lseek(fd, 0, SEEK_CUR);
        /* align to even */
        size = (size + 1) & ~1;
        if (!strcmp(ar_name, "/")) {
            /* coff symbol table : we handle it */
            if(s1->alacarte_link)
                return tcc_load_alacarte(s1, fd, size, 4);
    } else if (!strcmp(ar_name, "/SYM64/")) {
            if(s1->alacarte_link)
                return tcc_load_alacarte(s1, fd, size, 8);
        } else {
            ElfW(Ehdr) ehdr;
            if (tcc_object_type(fd, &ehdr) == AFF_BINTYPE_REL) {
                if (tcc_load_object_file(s1, fd, file_offset) < 0)
                    return -1;
            }
        }
        lseek(fd, file_offset + size, SEEK_SET);
    }
    return 0;
}

#ifndef TCC_TARGET_PE
/* load a DLL and all referenced DLLs. 'level = 0' means that the DLL
   is referenced by the user (so it should be added as DT_NEEDED in
   the generated ELF file) */
ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level)
{
    ElfW(Ehdr) ehdr;
    ElfW(Shdr) *shdr, *sh, *sh1;
    int i, j, nb_syms, nb_dts, sym_bind, ret;
    ElfW(Sym) *sym, *dynsym;
    ElfW(Dyn) *dt, *dynamic;
    unsigned char *dynstr;
    const char *name, *soname;
    DLLReference *dllref;

    read(fd, &ehdr, sizeof(ehdr));

    /* test CPU specific stuff */
    if (ehdr.e_ident[5] != ELFDATA2LSB ||
        ehdr.e_machine != EM_TCC_TARGET) {
        tcc_error_noabort("bad architecture");
        return -1;
    }

    /* read sections */
    shdr = load_data(fd, ehdr.e_shoff, sizeof(ElfW(Shdr)) * ehdr.e_shnum);

    /* load dynamic section and dynamic symbols */
    nb_syms = 0;
    nb_dts = 0;
    dynamic = NULL;
    dynsym = NULL; /* avoid warning */
    dynstr = NULL; /* avoid warning */
    for(i = 0, sh = shdr; i < ehdr.e_shnum; i++, sh++) {
        switch(sh->sh_type) {
        case SHT_DYNAMIC:
            nb_dts = sh->sh_size / sizeof(ElfW(Dyn));
            dynamic = load_data(fd, sh->sh_offset, sh->sh_size);
            break;
        case SHT_DYNSYM:
            nb_syms = sh->sh_size / sizeof(ElfW(Sym));
            dynsym = load_data(fd, sh->sh_offset, sh->sh_size);
            sh1 = &shdr[sh->sh_link];
            dynstr = load_data(fd, sh1->sh_offset, sh1->sh_size);
            break;
        default:
            break;
        }
    }

    /* compute the real library name */
    soname = tcc_basename(filename);

    for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
        if (dt->d_tag == DT_SONAME) {
            soname = (char *) dynstr + dt->d_un.d_val;
        }
    }

    /* if the dll is already loaded, do not load it */
    for(i = 0; i < s1->nb_loaded_dlls; i++) {
        dllref = s1->loaded_dlls[i];
        if (!strcmp(soname, dllref->name)) {
            /* but update level if needed */
            if (level < dllref->level)
                dllref->level = level;
            ret = 0;
            goto the_end;
        }
    }

    /* add the dll and its level */
    dllref = tcc_mallocz(sizeof(DLLReference) + strlen(soname));
    dllref->level = level;
    strcpy(dllref->name, soname);
    dynarray_add(&s1->loaded_dlls, &s1->nb_loaded_dlls, dllref);

    /* add dynamic symbols in dynsym_section */
    for(i = 1, sym = dynsym + 1; i < nb_syms; i++, sym++) {
        sym_bind = ELFW(ST_BIND)(sym->st_info);
        if (sym_bind == STB_LOCAL)
            continue;
        name = (char *) dynstr + sym->st_name;
        set_elf_sym(s1->dynsymtab_section, sym->st_value, sym->st_size,
                    sym->st_info, sym->st_other, sym->st_shndx, name);
    }

    /* load all referenced DLLs */
    for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
        switch(dt->d_tag) {
        case DT_NEEDED:
            name = (char *) dynstr + dt->d_un.d_val;
            for(j = 0; j < s1->nb_loaded_dlls; j++) {
                dllref = s1->loaded_dlls[j];
                if (!strcmp(name, dllref->name))
                    goto already_loaded;
            }
            if (tcc_add_dll(s1, name, AFF_REFERENCED_DLL) < 0) {
                tcc_error_noabort("referenced dll '%s' not found", name);
                ret = -1;
                goto the_end;
            }
        already_loaded:
            break;
        }
    }
    ret = 0;
 the_end:
    tcc_free(dynstr);
    tcc_free(dynsym);
    tcc_free(dynamic);
    tcc_free(shdr);
    return ret;
}

#define LD_TOK_NAME 256
#define LD_TOK_EOF  (-1)

/* return next ld script token */
static int ld_next(TCCState *s1, char *name, int name_size)
{
    int c;
    char *q;

 redo:
    switch(ch) {
    case ' ':
    case '\t':
    case '\f':
    case '\v':
    case '\r':
    case '\n':
        inp();
        goto redo;
    case '/':
        minp();
        if (ch == '*') {
            file->buf_ptr = parse_comment(file->buf_ptr);
            ch = file->buf_ptr[0];
            goto redo;
        } else {
            q = name;
            *q++ = '/';
            goto parse_name;
        }
        break;
    case '\\':
        ch = handle_eob();
        if (ch != '\\')
            goto redo;
        /* fall through */
    /* case 'a' ... 'z': */
    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 'A' ... 'z': */
    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 '.':
    case '$':
    case '~':
        q = name;
    parse_name:
        for(;;) {
            if (!((ch >= 'a' && ch <= 'z') ||
                  (ch >= 'A' && ch <= 'Z') ||
                  (ch >= '0' && ch <= '9') ||
                  strchr("/.-_+=$:\\,~", ch)))
                break;
            if ((q - name) < name_size - 1) {
                *q++ = ch;
            }
            minp();
        }
        *q = '\0';
        c = LD_TOK_NAME;
        break;
    case CH_EOF:
        c = LD_TOK_EOF;
        break;
    default:
        c = ch;
        inp();
        break;
    }
    return c;
}

static int ld_add_file(TCCState *s1, const char filename[])
{
    if (filename[0] == '/') {
        if (CONFIG_SYSROOT[0] == '\0'
            && tcc_add_file_internal(s1, filename, AFF_TYPE_BIN) == 0)
            return 0;
        filename = tcc_basename(filename);
    }
    return tcc_add_dll(s1, filename, 0);
}

static inline int new_undef_syms(void)
{
    int ret = 0;
    ret = new_undef_sym;
    new_undef_sym = 0;
    return ret;
}

static int ld_add_file_list(TCCState *s1, const char *cmd, int as_needed)
{
    char filename[1024], libname[1024];
    int t, group, nblibs = 0, ret = 0;
    char **libs = NULL;

    group = !strcmp(cmd, "GROUP");
    if (!as_needed)
        new_undef_syms();
    t = ld_next(s1, filename, sizeof(filename));
    if (t != '(')
        expect("(");
    t = ld_next(s1, filename, sizeof(filename));
    for(;;) {
        libname[0] = '\0';
        if (t == LD_TOK_EOF) {
            tcc_error_noabort("unexpected end of file");
            ret = -1;
            goto lib_parse_error;
        } else if (t == ')') {
            break;
        } else if (t == '-') {
            t = ld_next(s1, filename, sizeof(filename));
            if ((t != LD_TOK_NAME) || (filename[0] != 'l')) {
                tcc_error_noabort("library name expected");
                ret = -1;
                goto lib_parse_error;
            }
            pstrcpy(libname, sizeof libname, &filename[1]);
            if (s1->static_link) {
                snprintf(filename, sizeof filename, "lib%s.a", libname);
            } else {
                snprintf(filename, sizeof filename, "lib%s.so", libname);
            }
        } else if (t != LD_TOK_NAME) {
            tcc_error_noabort("filename expected");
            ret = -1;
            goto lib_parse_error;
        }
        if (!strcmp(filename, "AS_NEEDED")) {
            ret = ld_add_file_list(s1, cmd, 1);
            if (ret)
                goto lib_parse_error;
        } else {
            /* TODO: Implement AS_NEEDED support. Ignore it for now */
            if (!as_needed) {
                ret = ld_add_file(s1, filename);
                if (ret)
                    goto lib_parse_error;
                if (group) {
                    /* Add the filename *and* the libname to avoid future conversions */
                    dynarray_add(&libs, &nblibs, tcc_strdup(filename));
                    if (libname[0] != '\0')
                        dynarray_add(&libs, &nblibs, tcc_strdup(libname));
                }
            }
        }
        t = ld_next(s1, filename, sizeof(filename));
        if (t == ',') {
            t = ld_next(s1, filename, sizeof(filename));
        }
    }
    if (group && !as_needed) {
        while (new_undef_syms()) {
            int i;

            for (i = 0; i < nblibs; i ++)
                ld_add_file(s1, libs[i]);
        }
    }
lib_parse_error:
    dynarray_reset(&libs, &nblibs);
    return ret;
}

/* interpret a subset of GNU ldscripts to handle the dummy libc.so
   files */
ST_FUNC int tcc_load_ldscript(TCCState *s1)
{
    char cmd[64];
    char filename[1024];
    int t, ret;

    ch = handle_eob();
    for(;;) {
        t = ld_next(s1, cmd, sizeof(cmd));
        if (t == LD_TOK_EOF)
            return 0;
        else if (t != LD_TOK_NAME)
            return -1;
        if (!strcmp(cmd, "INPUT") ||
            !strcmp(cmd, "GROUP")) {
            ret = ld_add_file_list(s1, cmd, 0);
            if (ret)
                return ret;
        } else if (!strcmp(cmd, "OUTPUT_FORMAT") ||
                   !strcmp(cmd, "TARGET")) {
            /* ignore some commands */
            t = ld_next(s1, cmd, sizeof(cmd));
            if (t != '(')
                expect("(");
            for(;;) {
                t = ld_next(s1, filename, sizeof(filename));
                if (t == LD_TOK_EOF) {
                    tcc_error_noabort("unexpected end of file");
                    return -1;
                } else if (t == ')') {
                    break;
                }
            }
        } else {
            return -1;
        }
    }
    return 0;
}
#endif /* !TCC_TARGET_PE */

https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/tccrun.c

File: ../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/tccrun.c used in: Step 378, Step 395, Step 407, Step 419, Step 431, Step 443, Step 455
/*
 *  TCC - Tiny C Compiler - Support for -run switch
 *
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef _TCC_H
#include "tcc.h"
#endif

/* only native compiler supports -run */
#ifdef TCC_IS_NATIVE

#ifndef _WIN32
# include <sys/mman.h>
#endif

#ifdef CONFIG_TCC_BACKTRACE
# ifndef _WIN32
#  include <signal.h>
#  ifndef __OpenBSD__
#   include <sys/ucontext.h>
#  endif
# else
#  define ucontext_t CONTEXT
# endif
ST_DATA int rt_num_callers = 6;
ST_DATA const char **rt_bound_error_msg;
ST_DATA void *rt_prog_main;
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level);
static void rt_error(ucontext_t *uc, const char *fmt, ...);
static void set_exception_handler(void);
#endif

static void set_pages_executable(void *ptr, unsigned long length);
static int tcc_relocate_ex(TCCState *s1, void *ptr);

#ifdef _WIN64
static void *win64_add_function_table(TCCState *s1);
static void win64_del_function_table(void *);
#endif

// #define HAVE_SELINUX

/* ------------------------------------------------------------- */
/* Do all relocations (needed before using tcc_get_symbol())
   Returns -1 on error. */

LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
{
    int size;

    if (TCC_RELOCATE_AUTO != ptr)
        return tcc_relocate_ex(s1, ptr);

    size = tcc_relocate_ex(s1, NULL);
    if (size < 0)
        return -1;

#ifdef HAVE_SELINUX
    /* Use mmap instead of malloc for Selinux. */
    ptr = mmap (NULL, size, PROT_READ|PROT_WRITE,
        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (ptr == MAP_FAILED)
        tcc_error("tccrun: could not map memory");
    dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size);
#else
    ptr = tcc_malloc(size);
#endif
    tcc_relocate_ex(s1, ptr); /* no more errors expected */
    dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr);
    return 0;
}

ST_FUNC void tcc_run_free(TCCState *s1)
{
    int i;

    for (i = 0; i < s1->nb_runtime_mem; ++i) {
#ifdef HAVE_SELINUX
        unsigned size = (unsigned)(addr_t)s1->runtime_mem[i++];
        munmap(s1->runtime_mem[i], size);
#else
#ifdef _WIN64
        win64_del_function_table(*(void**)s1->runtime_mem[i]);
#endif
        tcc_free(s1->runtime_mem[i]);
#endif
    }
    tcc_free(s1->runtime_mem);
}

/* launch the compiled program with the given arguments */
LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
{
    int (*prog_main)(int, char **);

    s1->runtime_main = "main";
    if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
        return -1;
    prog_main = tcc_get_symbol_err(s1, s1->runtime_main);

#ifdef CONFIG_TCC_BACKTRACE
    if (s1->do_debug) {
        set_exception_handler();
        rt_prog_main = prog_main;
    }
#endif

    errno = 0; /* clean errno value */

#ifdef CONFIG_TCC_BCHECK
    if (s1->do_bounds_check) {
        void (*bound_init)(void);
        void (*bound_exit)(void);
        void (*bound_new_region)(void *p, addr_t size);
        int  (*bound_delete_region)(void *p);
        int i, ret;

        /* set error function */
        rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
        /* XXX: use .init section so that it also work in binary ? */
        bound_init = tcc_get_symbol_err(s1, "__bound_init");
        bound_exit = tcc_get_symbol_err(s1, "__bound_exit");
        bound_new_region = tcc_get_symbol_err(s1, "__bound_new_region");
        bound_delete_region = tcc_get_symbol_err(s1, "__bound_delete_region");

        bound_init();
        /* mark argv area as valid */
        bound_new_region(argv, argc*sizeof(argv[0]));
        for (i=0; i<argc; ++i)
            bound_new_region(argv[i], strlen(argv[i]) + 1);

        ret = (*prog_main)(argc, argv);

        /* unmark argv area */
        for (i=0; i<argc; ++i)
            bound_delete_region(argv[i]);
        bound_delete_region(argv);
        bound_exit();
        return ret;
    }
#endif
    return (*prog_main)(argc, argv);
}

#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
 #define RUN_SECTION_ALIGNMENT 63
#else
 #define RUN_SECTION_ALIGNMENT 15
#endif

/* relocate code. Return -1 on error, required size if ptr is NULL,
   otherwise copy code into buffer passed by the caller */
static int tcc_relocate_ex(TCCState *s1, void *ptr)
{
    Section *s;
    unsigned offset, length, fill, i, k;
    addr_t mem;

    if (NULL == ptr) {
        s1->nb_errors = 0;
#ifdef TCC_TARGET_PE
        pe_output_file(s1, NULL);
#else
        tcc_add_runtime(s1);
        relocate_common_syms();
        tcc_add_linker_symbols(s1);
        build_got_entries(s1);
#endif
        if (s1->nb_errors)
            return -1;
    }

    offset = 0, mem = (addr_t)ptr;
    fill = -mem & RUN_SECTION_ALIGNMENT;
#ifdef _WIN64
    offset += sizeof (void*);
#endif
    for (k = 0; k < 2; ++k) {
        for(i = 1; i < s1->nb_sections; i++) {
            s = s1->sections[i];
            if (0 == (s->sh_flags & SHF_ALLOC))
                continue;
            if (k != !(s->sh_flags & SHF_EXECINSTR))
                continue;
            offset += fill;
            s->sh_addr = mem ? mem + offset : 0;
#if 0
            if (mem)
                printf("%-16s +%02lx %p %04x\n",
                    s->name, fill, (void*)s->sh_addr, (unsigned)s->data_offset);
#endif
            offset += s->data_offset;
            fill = -(mem + offset) & 15;
        }
#if RUN_SECTION_ALIGNMENT > 15
        /* To avoid that x86 processors would reload cached instructions each time
           when data is written in the near, we need to make sure that code and data
           do not share the same 64 byte unit */
        fill = -(mem + offset) & RUN_SECTION_ALIGNMENT;
#endif
    }

    /* relocate symbols */
    relocate_syms(s1, s1->symtab, 1);
    if (s1->nb_errors)
        return -1;

    if (0 == mem)
        return offset + RUN_SECTION_ALIGNMENT;

    /* relocate each section */
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if (s->reloc)
            relocate_section(s1, s);
    }
    relocate_plt(s1);

#ifdef _WIN64
    *(void**)ptr = win64_add_function_table(s1);
#endif

    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if (0 == (s->sh_flags & SHF_ALLOC))
            continue;
        length = s->data_offset;
        ptr = (void*)s->sh_addr;
        if (NULL == s->data || s->sh_type == SHT_NOBITS)
            memset(ptr, 0, length);
        else
            memcpy(ptr, s->data, length);
        /* mark executable sections as executable in memory */
        if (s->sh_flags & SHF_EXECINSTR)
            set_pages_executable(ptr, length);
    }
    return 0;
}

/* ------------------------------------------------------------- */
/* allow to run code in memory */

static void set_pages_executable(void *ptr, unsigned long length)
{
#ifdef _WIN32
    unsigned long old_protect;
    VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
#else
    void __clear_cache(void *beginning, void *end);
    addr_t start, end;
#ifndef PAGESIZE
# define PAGESIZE 4096
#endif
    start = (addr_t)ptr & ~(PAGESIZE - 1);
    end = (addr_t)ptr + length;
    end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
    if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC))
        tcc_error("mprotect failed: did you mean to configure --with-selinux?");
# if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
#if !BOOTSTRAP
    __clear_cache(ptr, (char *)ptr + length);
#endif
# endif
#endif
}

#ifdef _WIN64
static void *win64_add_function_table(TCCState *s1)
{
    void *p = NULL;
    if (s1->uw_pdata) {
        p = (void*)s1->uw_pdata->sh_addr;
        RtlAddFunctionTable(
            (RUNTIME_FUNCTION*)p,
            s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
            text_section->sh_addr
            );
        s1->uw_pdata = NULL;
    }
    return p;;
}

static void win64_del_function_table(void *p)
{
    if (p) {
        RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
    }
}
#endif

/* ------------------------------------------------------------- */
#ifdef CONFIG_TCC_BACKTRACE

ST_FUNC void tcc_set_num_callers(int n)
{
    rt_num_callers = n;
}

/* print the position in the source file of PC value 'pc' by reading
   the stabs debug information */
static addr_t rt_printline(addr_t wanted_pc, const char *msg)
{
    char func_name[128], last_func_name[128];
    addr_t func_addr, last_pc, pc;
    const char *incl_files[INCLUDE_STACK_SIZE];
    int incl_index, len, last_line_num, i;
    const char *str, *p;

    Stab_Sym *stab_sym = NULL, *stab_sym_end, *sym;
    int stab_len = 0;
    char *stab_str = NULL;

    if (stab_section) {
        stab_len = stab_section->data_offset;
        stab_sym = (Stab_Sym *)stab_section->data;
        stab_str = (char *) stabstr_section->data;
    }

    func_name[0] = '\0';
    func_addr = 0;
    incl_index = 0;
    last_func_name[0] = '\0';
    last_pc = (addr_t)-1;
    last_line_num = 1;

    if (!stab_sym)
        goto no_stabs;

    stab_sym_end = (Stab_Sym*)((char*)stab_sym + stab_len);
    for (sym = stab_sym + 1; sym < stab_sym_end; ++sym) {
        switch(sym->n_type) {
            /* function start or end */
        case N_FUN:
            if (sym->n_strx == 0) {
                /* we test if between last line and end of function */
                pc = sym->n_value + func_addr;
                if (wanted_pc >= last_pc && wanted_pc < pc)
                    goto found;
                func_name[0] = '\0';
                func_addr = 0;
            } else {
                str = stab_str + sym->n_strx;
                p = strchr(str, ':');
                if (!p) {
                    pstrcpy(func_name, sizeof(func_name), str);
                } else {
                    len = p - str;
                    if (len > sizeof(func_name) - 1)
                        len = sizeof(func_name) - 1;
                    memcpy(func_name, str, len);
                    func_name[len] = '\0';
                }
                func_addr = sym->n_value;
            }
            break;
            /* line number info */
        case N_SLINE:
            pc = sym->n_value + func_addr;
            if (wanted_pc >= last_pc && wanted_pc < pc)
                goto found;
            last_pc = pc;
            last_line_num = sym->n_desc;
            /* XXX: slow! */
            strcpy(last_func_name, func_name);
            break;
            /* include files */
        case N_BINCL:
            str = stab_str + sym->n_strx;
        add_incl:
            if (incl_index < INCLUDE_STACK_SIZE) {
                incl_files[incl_index++] = str;
            }
            break;
        case N_EINCL:
            if (incl_index > 1)
                incl_index--;
            break;
        case N_SO:
            if (sym->n_strx == 0) {
                incl_index = 0; /* end of translation unit */
            } else {
                str = stab_str + sym->n_strx;
                /* do not add path */
                len = strlen(str);
                if (len > 0 && str[len - 1] != '/')
                    goto add_incl;
            }
            break;
        }
    }

no_stabs:
    /* second pass: we try symtab symbols (no line number info) */
    incl_index = 0;
    if (symtab_section)
    {
        ElfW(Sym) *sym, *sym_end;
        int type;

        sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
        for(sym = (ElfW(Sym) *)symtab_section->data + 1;
            sym < sym_end;
            sym++) {
            type = ELFW(ST_TYPE)(sym->st_info);
            if (type == STT_FUNC || type == STT_GNU_IFUNC) {
                if (wanted_pc >= sym->st_value &&
                    wanted_pc < sym->st_value + sym->st_size) {
                    pstrcpy(last_func_name, sizeof(last_func_name),
                            (char *) strtab_section->data + sym->st_name);
                    func_addr = sym->st_value;
                    goto found;
                }
            }
        }
    }
    /* did not find any info: */
    fprintf(stderr, "%s %p ???\n", msg, (void*)wanted_pc);
    fflush(stderr);
    return 0;
 found:
    i = incl_index;
    if (i > 0)
        fprintf(stderr, "%s:%d: ", incl_files[--i], last_line_num);
    fprintf(stderr, "%s %p", msg, (void*)wanted_pc);
    if (last_func_name[0] != '\0')
        fprintf(stderr, " %s()", last_func_name);
    if (--i >= 0) {
        fprintf(stderr, " (included from ");
        for (;;) {
            fprintf(stderr, "%s", incl_files[i]);
            if (--i < 0)
                break;
            fprintf(stderr, ", ");
        }
        fprintf(stderr, ")");
    }
    fprintf(stderr, "\n");
    fflush(stderr);
    return func_addr;
}

/* emit a run time error at position 'pc' */
static void rt_error(ucontext_t *uc, const char *fmt, ...)
{
    va_list ap;
    addr_t pc;
    int i;

    fprintf(stderr, "Runtime error: ");
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
    fprintf(stderr, "\n");

    for(i=0;i<rt_num_callers;i++) {
        if (rt_get_caller_pc(&pc, uc, i) < 0)
            break;
        pc = rt_printline(pc, i ? "by" : "at");
        if (pc == (addr_t)rt_prog_main && pc)
            break;
    }
}

/* ------------------------------------------------------------- */
#ifndef _WIN32

/* signal handler for fatal errors */
static void sig_error(int signum, siginfo_t *siginf, void *puc)
{
    ucontext_t *uc = puc;

    switch(signum) {
    case SIGFPE:
        switch(siginf->si_code) {
        case FPE_INTDIV:
        case FPE_FLTDIV:
            rt_error(uc, "division by zero");
            break;
        default:
            rt_error(uc, "floating point exception");
            break;
        }
        break;
    case SIGBUS:
    case SIGSEGV:
        if (rt_bound_error_msg && *rt_bound_error_msg)
            rt_error(uc, *rt_bound_error_msg);
        else
            rt_error(uc, "dereferencing invalid pointer");
        break;
    case SIGILL:
        rt_error(uc, "illegal instruction");
        break;
    case SIGABRT:
        rt_error(uc, "abort() called");
        break;
    default:
        rt_error(uc, "caught signal %d", signum);
        break;
    }
    exit(255);
}

#ifndef SA_SIGINFO
# define SA_SIGINFO 0x00000004u
#endif

/* Generate a stack backtrace when a CPU exception occurs. */
static void set_exception_handler(void)
{
    struct sigaction sigact;
    /* install TCC signal handlers to print debug info on fatal
       runtime errors */
    sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
    sigact.sa_sigaction = sig_error;
    sigemptyset(&sigact.sa_mask);
    sigaction(SIGFPE, &sigact, NULL);
    sigaction(SIGILL, &sigact, NULL);
    sigaction(SIGSEGV, &sigact, NULL);
    sigaction(SIGBUS, &sigact, NULL);
    sigaction(SIGABRT, &sigact, NULL);
}

/* ------------------------------------------------------------- */
#ifdef __i386__

/* fix for glibc 2.1 */
#ifndef REG_EIP
#define REG_EIP EIP
#define REG_EBP EBP
#endif

/* return the PC at frame level 'level'. Return negative if not found */
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
    addr_t fp;
    int i;

    if (level == 0) {
#if defined(__APPLE__)
        *paddr = uc->uc_mcontext->__ss.__eip;
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
        *paddr = uc->uc_mcontext.mc_eip;
#elif defined(__dietlibc__)
        *paddr = uc->uc_mcontext.eip;
#elif defined(__NetBSD__)
        *paddr = uc->uc_mcontext.__gregs[_REG_EIP];
#elif defined(__OpenBSD__)
        *paddr = uc->sc_eip;
#else
        *paddr = uc->uc_mcontext.gregs[REG_EIP];
#endif
        return 0;
    } else {
#if defined(__APPLE__)
        fp = uc->uc_mcontext->__ss.__ebp;
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
        fp = uc->uc_mcontext.mc_ebp;
#elif defined(__dietlibc__)
        fp = uc->uc_mcontext.ebp;
#elif defined(__NetBSD__)
        fp = uc->uc_mcontext.__gregs[_REG_EBP];
#elif defined(__OpenBSD__)
        *paddr = uc->sc_ebp;
#else
        fp = uc->uc_mcontext.gregs[REG_EBP];
#endif
        for(i=1;i<level;i++) {
            /* XXX: check address validity with program info */
            if (fp <= 0x1000 || fp >= 0xc0000000)
                return -1;
            fp = ((addr_t *)fp)[0];
        }
        *paddr = ((addr_t *)fp)[1];
        return 0;
    }
}

/* ------------------------------------------------------------- */
#elif defined(__x86_64__)

/* return the PC at frame level 'level'. Return negative if not found */
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
    addr_t fp;
    int i;

    if (level == 0) {
        /* XXX: only support linux */
#if defined(__APPLE__)
        *paddr = uc->uc_mcontext->__ss.__rip;
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
        *paddr = uc->uc_mcontext.mc_rip;
#elif defined(__NetBSD__)
        *paddr = uc->uc_mcontext.__gregs[_REG_RIP];
#else
        *paddr = uc->uc_mcontext.gregs[REG_RIP];
#endif
        return 0;
    } else {
#if defined(__APPLE__)
        fp = uc->uc_mcontext->__ss.__rbp;
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
        fp = uc->uc_mcontext.mc_rbp;
#elif defined(__NetBSD__)
        fp = uc->uc_mcontext.__gregs[_REG_RBP];
#else
        fp = uc->uc_mcontext.gregs[REG_RBP];
#endif
        for(i=1;i<level;i++) {
            /* XXX: check address validity with program info */
            if (fp <= 0x1000)
                return -1;
            fp = ((addr_t *)fp)[0];
        }
        *paddr = ((addr_t *)fp)[1];
        return 0;
    }
}

/* ------------------------------------------------------------- */
#elif defined(__arm__)

/* return the PC at frame level 'level'. Return negative if not found */
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
    addr_t fp, sp;
    int i;

    if (level == 0) {
        /* XXX: only supports linux */
#if defined(__linux__)
        *paddr = uc->uc_mcontext.arm_pc;
#else
        return -1;
#endif
        return 0;
    } else {
#if defined(__linux__)
        fp = uc->uc_mcontext.arm_fp;
        sp = uc->uc_mcontext.arm_sp;
        if (sp < 0x1000)
            sp = 0x1000;
#else
        return -1;
#endif
        /* XXX: specific to tinycc stack frames */
        if (fp < sp + 12 || fp & 3)
            return -1;
        for(i = 1; i < level; i++) {
            sp = ((addr_t *)fp)[-2];
            if (sp < fp || sp - fp > 16 || sp & 3)
                return -1;
            fp = ((addr_t *)fp)[-3];
            if (fp <= sp || fp - sp < 12 || fp & 3)
                return -1;
        }
        /* XXX: check address validity with program info */
        *paddr = ((addr_t *)fp)[-1];
        return 0;
    }
}

/* ------------------------------------------------------------- */
#elif defined(__aarch64__)

static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
    if (level < 0)
        return -1;
    else if (level == 0) {
        *paddr = uc->uc_mcontext.pc;
        return 0;
    }
    else {
        addr_t *fp = (addr_t *)uc->uc_mcontext.regs[29];
        int i;
        for (i = 1; i < level; i++)
            fp = (addr_t *)fp[0];
        *paddr = fp[1];
        return 0;
    }
}

/* ------------------------------------------------------------- */
#else

#warning add arch specific rt_get_caller_pc()
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
    return -1;
}

#endif /* !__i386__ */

/* ------------------------------------------------------------- */
#else /* WIN32 */

static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
{
    EXCEPTION_RECORD *er = ex_info->ExceptionRecord;
    CONTEXT *uc = ex_info->ContextRecord;
    switch (er->ExceptionCode) {
    case EXCEPTION_ACCESS_VIOLATION:
        if (rt_bound_error_msg && *rt_bound_error_msg)
            rt_error(uc, *rt_bound_error_msg);
        else
        rt_error(uc, "access violation");
        break;
    case EXCEPTION_STACK_OVERFLOW:
        rt_error(uc, "stack overflow");
        break;
    case EXCEPTION_INT_DIVIDE_BY_ZERO:
        rt_error(uc, "division by zero");
        break;
    default:
        rt_error(uc, "exception caught");
        break;
    }
    return EXCEPTION_EXECUTE_HANDLER;
}

/* Generate a stack backtrace when a CPU exception occurs. */
static void set_exception_handler(void)
{
    SetUnhandledExceptionFilter(cpu_exception_handler);
}

/* return the PC at frame level 'level'. Return non zero if not found */
static int rt_get_caller_pc(addr_t *paddr, CONTEXT *uc, int level)
{
    addr_t fp, pc;
    int i;
#ifdef _WIN64
    pc = uc->Rip;
    fp = uc->Rbp;
#else
    pc = uc->Eip;
    fp = uc->Ebp;
#endif
    if (level > 0) {
        for(i=1;i<level;i++) {
        /* XXX: check address validity with program info */
        if (fp <= 0x1000 || fp >= 0xc0000000)
        return -1;
        fp = ((addr_t*)fp)[0];
    }
        pc = ((addr_t*)fp)[1];
    }
    *paddr = pc;
    return 0;
}

#endif /* _WIN32 */
#endif /* CONFIG_TCC_BACKTRACE */
/* ------------------------------------------------------------- */
#ifdef CONFIG_TCC_STATIC

/* dummy function for profiling */
ST_FUNC void *dlopen(const char *filename, int flag)
{
    return NULL;
}

ST_FUNC void dlclose(void *p)
{
}

ST_FUNC const char *dlerror(void)
{
    return "error";
}

typedef struct TCCSyms {
    char *str;
    void *ptr;
} TCCSyms;


/* add the symbol you want here if no dynamic linking is done */
static TCCSyms tcc_syms[] = {
#if !defined(CONFIG_TCCBOOT)
#define TCCSYM(a) { #a, &a, },
    TCCSYM(printf)
    TCCSYM(fprintf)
    TCCSYM(fopen)
    TCCSYM(fclose)
#undef TCCSYM
#endif
    { NULL, NULL },
};

ST_FUNC void *dlsym(void *handle, const char *symbol)
{
    TCCSyms *p;
    p = tcc_syms;
    while (p->str != NULL) {
        if (!strcmp(p->str, symbol))
            return p->ptr;
        p++;
    }
    return NULL;
}

#endif /* CONFIG_TCC_STATIC */
#endif /* TCC_IS_NATIVE */
/* ------------------------------------------------------------- */

https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/i386-gen.c

File: ../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/i386-gen.c used in: Step 378, Step 395, Step 407, Step 419, Step 431, Step 443, Step 455
/*
 *  X86 code generator for TCC
 * 
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef TARGET_DEFS_ONLY

/* number of available registers */
#define NB_REGS         5
#define NB_ASM_REGS     8
#define CONFIG_TCC_ASM

/* a register can belong to several classes. The classes must be
   sorted from more general to more precise (see gv2() code which does
   assumptions on it). */
#define RC_INT     0x0001 /* generic integer register */
#define RC_FLOAT   0x0002 /* generic float register */
#define RC_EAX     0x0004
#define RC_ST0     0x0008 
#define RC_ECX     0x0010
#define RC_EDX     0x0020
#define RC_EBX     0x0040

#define RC_IRET    RC_EAX /* function return: integer register */
#define RC_LRET    RC_EDX /* function return: second integer register */
#define RC_FRET    RC_ST0 /* function return: float register */

/* pretty names for the registers */
enum {
    TREG_EAX = 0,
    TREG_ECX,
    TREG_EDX,
    TREG_EBX,
    TREG_ST0,
    TREG_ESP = 4
};

/* return registers for function */
#define REG_IRET TREG_EAX /* single word int return register */
#define REG_LRET TREG_EDX /* second word return register (for long long) */
#define REG_FRET TREG_ST0 /* float return register */

/* defined if function parameters must be evaluated in reverse order */
#define INVERT_FUNC_PARAMS

/* defined if structures are passed as pointers. Otherwise structures
   are directly pushed on stack. */
/* #define FUNC_STRUCT_PARAM_AS_PTR */

/* pointer size, in bytes */
#define PTR_SIZE 4

/* long double size and alignment, in bytes */
#define LDOUBLE_SIZE  12
#define LDOUBLE_ALIGN 4
/* maximum alignment (for aligned attribute support) */
#define MAX_ALIGN     8

/******************************************************/
#else /* ! TARGET_DEFS_ONLY */
/******************************************************/
#ifndef _TCC_H
#include "tcc.h"
#endif

/* define to 1/0 to [not] have EBX as 4th register */
#define USE_EBX 0

ST_DATA const int reg_classes[NB_REGS] = {
    /* eax */ RC_INT | RC_EAX,
    /* ecx */ RC_INT | RC_ECX,
    /* edx */ RC_INT | RC_EDX,
    /* ebx */ (RC_INT | RC_EBX) * USE_EBX,
    /* st0 */ RC_FLOAT | RC_ST0,
};

static unsigned long func_sub_sp_offset;
static int func_ret_sub;
#ifdef CONFIG_TCC_BCHECK
static addr_t func_bound_offset;
static unsigned long func_bound_ind;
#endif

/* XXX: make it faster ? */
ST_FUNC void g(int c)
{
    int ind1;
    if (nocode_wanted)
        return;
    ind1 = ind + 1;
    if (ind1 > cur_text_section->data_allocated)
        section_realloc(cur_text_section, ind1);
    cur_text_section->data[ind] = c;
    ind = ind1;
}

ST_FUNC void o(unsigned int c)
{
    while (c) {
        g(c);
        c = c >> 8;
    }
}

ST_FUNC void gen_le16(int v)
{
    g(v);
    g(v >> 8);
}

ST_FUNC void gen_le32(int c)
{
    g(c);
    g(c >> 8);
    g(c >> 16);
    g(c >> 24);
}

/* output a symbol and patch all calls to it */
ST_FUNC void gsym_addr(int t, int a)
{
    while (t) {
        unsigned char *ptr = cur_text_section->data + t;
        uint32_t n = read32le(ptr); /* next value */
        write32le(ptr, a - t - 4);
        t = n;
    }
}

ST_FUNC void gsym(int t)
{
    gsym_addr(t, ind);
}

/* instruction + 4 bytes data. Return the address of the data */
static int oad(int c, int s)
{
    int t;
    if (nocode_wanted)
        return s;
    o(c);
    t = ind;
    gen_le32(s);
    return t;
}

/* generate jmp to a label */
#define gjmp2(instr,lbl) oad(instr,lbl)

/* output constant with relocation if 'r & VT_SYM' is true */
ST_FUNC void gen_addr32(int r, Sym *sym, long c)
{
    if (r & VT_SYM)
        greloc(cur_text_section, sym, ind, R_386_32);
    gen_le32(c);
}

ST_FUNC void gen_addrpc32(int r, Sym *sym, long c)
{
    if (r & VT_SYM)
        greloc(cur_text_section, sym, ind, R_386_PC32);
    gen_le32(c - 4);
}

/* generate a modrm reference. 'op_reg' contains the addtional 3
   opcode bits */
static void gen_modrm(int op_reg, int r, Sym *sym, int c)
{
    op_reg = op_reg << 3;
    if ((r & VT_VALMASK) == VT_CONST) {
        /* constant memory reference */
        o(0x05 | op_reg);
        gen_addr32(r, sym, c);
    } else if ((r & VT_VALMASK) == VT_LOCAL) {
        /* currently, we use only ebp as base */
        if (c == (char)c) {
            /* short reference */
            o(0x45 | op_reg);
            g(c);
        } else {
            oad(0x85 | op_reg, c);
        }
    } else {
        g(0x00 | op_reg | (r & VT_VALMASK));
    }
}

/* load 'r' from value 'sv' */
ST_FUNC void load(int r, SValue *sv)
{
    int v, t, ft, fc, fr;
    SValue v1;

#ifdef TCC_TARGET_PE
    SValue v2;
    sv = pe_getimport(sv, &v2);
#endif

    fr = sv->r;
    ft = sv->type.t;
    fc = sv->c.i;

    ft &= ~(VT_VOLATILE | VT_CONSTANT);

    v = fr & VT_VALMASK;
    if (fr & VT_LVAL) {
        if (v == VT_LLOCAL) {
            v1.type.t = VT_INT;
            v1.r = VT_LOCAL | VT_LVAL;
            v1.c.i = fc;
            fr = r;
            if (!(reg_classes[fr] & RC_INT))
                fr = get_reg(RC_INT);
            load(fr, &v1);
        }
        if ((ft & VT_BTYPE) == VT_FLOAT) {
            o(0xd9); /* flds */
            r = 0;
        } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
            o(0xdd); /* fldl */
            r = 0;
        } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
            o(0xdb); /* fldt */
            r = 5;
        } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) {
            o(0xbe0f);   /* movsbl */
        } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
            o(0xb60f);   /* movzbl */
        } else if ((ft & VT_TYPE) == VT_SHORT) {
            o(0xbf0f);   /* movswl */
        } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
            o(0xb70f);   /* movzwl */
        } else {
            o(0x8b);     /* movl */
        }
        gen_modrm(r, fr, sv->sym, fc);
    } else {
        if (v == VT_CONST) {
            o(0xb8 + r); /* mov $xx, r */
            gen_addr32(fr, sv->sym, fc);
        } else if (v == VT_LOCAL) {
            if (fc) {
                o(0x8d); /* lea xxx(%ebp), r */
                gen_modrm(r, VT_LOCAL, sv->sym, fc);
            } else {
                o(0x89);
                o(0xe8 + r); /* mov %ebp, r */
            }
        } else if (v == VT_CMP) {
            oad(0xb8 + r, 0); /* mov $0, r */
            o(0x0f); /* setxx %br */
            o(fc);
            o(0xc0 + r);
        } else if (v == VT_JMP || v == VT_JMPI) {
            t = v & 1;
            oad(0xb8 + r, t); /* mov $1, r */
            o(0x05eb); /* jmp after */
            gsym(fc);
            oad(0xb8 + r, t ^ 1); /* mov $0, r */
        } else if (v != r) {
            o(0x89);
            o(0xc0 + r + v * 8); /* mov v, r */
        }
    }
}

/* store register 'r' in lvalue 'v' */
ST_FUNC void store(int r, SValue *v)
{
    int fr, bt, ft, fc;

#ifdef TCC_TARGET_PE
    SValue v2;
    v = pe_getimport(v, &v2);
#endif

    ft = v->type.t;
    fc = v->c.i;
    fr = v->r & VT_VALMASK;
    ft &= ~(VT_VOLATILE | VT_CONSTANT);
    bt = ft & VT_BTYPE;
    /* XXX: incorrect if float reg to reg */
    if (bt == VT_FLOAT) {
        o(0xd9); /* fsts */
        r = 2;
    } else if (bt == VT_DOUBLE) {
        o(0xdd); /* fstpl */
        r = 2;
    } else if (bt == VT_LDOUBLE) {
        o(0xc0d9); /* fld %st(0) */
        o(0xdb); /* fstpt */
        r = 7;
    } else {
        if (bt == VT_SHORT)
            o(0x66);
        if (bt == VT_BYTE || bt == VT_BOOL)
            o(0x88);
        else
            o(0x89);
    }
    if (fr == VT_CONST ||
        fr == VT_LOCAL ||
        (v->r & VT_LVAL)) {
        gen_modrm(r, v->r, v->sym, fc);
    } else if (fr != r) {
        o(0xc0 + fr + r * 8); /* mov r, fr */
    }
}

static void gadd_sp(int val)
{
    if (val == (char)val) {
        o(0xc483);
        g(val);
    } else {
        oad(0xc481, val); /* add $xxx, %esp */
    }
}

#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_PE
static void gen_static_call(int v)
{
    Sym *sym;

    sym = external_global_sym(v, &func_old_type, 0);
    oad(0xe8, -4);
    greloc(cur_text_section, sym, ind-4, R_386_PC32);
}
#endif

/* 'is_jmp' is '1' if it is a jump */
static void gcall_or_jmp(int is_jmp)
{
    int r;
    if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
        int rt;
        /* constant case */
        if (vtop->r & VT_SYM) {
            /* relocation case */
            greloc(cur_text_section, vtop->sym, 
                   ind + 1, R_386_PC32);
        } else {
            /* put an empty PC32 relocation */
            put_elf_reloc(symtab_section, cur_text_section, 
                          ind + 1, R_386_PC32, 0);
        }
        oad(0xe8 + is_jmp, vtop->c.i - 4); /* call/jmp im */
        /* extend the return value to the whole register if necessary
           visual studio and gcc do not always set the whole eax register
           when assigning the return value of a function  */
        rt = vtop->type.ref->type.t;
        switch (rt & VT_BTYPE) {
            case VT_BYTE:
                if (rt & VT_UNSIGNED) {
                    o(0xc0b60f); /* movzx %al, %eax */
                }
                else {
                    o(0xc0be0f); /* movsx %al, %eax */
                }
                break;
            case VT_SHORT:
                if (rt & VT_UNSIGNED) {
                    o(0xc0b70f); /* movzx %ax, %eax */
                }
                else {
                    o(0xc0bf0f); /* movsx %ax, %eax */
                }
                break;
            default:
                break;
        }
    } else {
        /* otherwise, indirect call */
        r = gv(RC_INT);
        o(0xff); /* call/jmp *r */
        o(0xd0 + r + (is_jmp << 4));
    }
}

static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX };
static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX };

/* Return the number of registers needed to return the struct, or 0 if
   returning via struct pointer. */
ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize)
{
#ifdef TCC_TARGET_PE
    int size, align;
    *ret_align = 1; // Never have to re-align return values for x86
    *regsize = 4;
    size = type_size(vt, &align);
    if (size > 8 || (size & (size - 1)))
        return 0;
    if (size == 8)
        ret->t = VT_LLONG;
    else if (size == 4)
        ret->t = VT_INT;
    else if (size == 2)
        ret->t = VT_SHORT;
    else
        ret->t = VT_BYTE;
    ret->ref = NULL;
    return 1;
#else
    *ret_align = 1; // Never have to re-align return values for x86
    return 0;
#endif
}

/* Generate function call. The function address is pushed first, then
   all the parameters in call order. This functions pops all the
   parameters and the function address. */
ST_FUNC void gfunc_call(int nb_args)
{
    int size, align, r, args_size, i, func_call;
    Sym *func_sym;
    
    args_size = 0;
    for(i = 0;i < nb_args; i++) {
        if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
            size = type_size(&vtop->type, &align);
            /* align to stack align size */
            size = (size + 3) & ~3;
            /* allocate the necessary size on stack */
            oad(0xec81, size); /* sub $xxx, %esp */
            /* generate structure store */
            r = get_reg(RC_INT);
            o(0x89); /* mov %esp, r */
            o(0xe0 + r);
            vset(&vtop->type, r | VT_LVAL, 0);
            vswap();
            vstore();
            args_size += size;
        } else if (is_float(vtop->type.t)) {
            gv(RC_FLOAT); /* only one float register */
            if ((vtop->type.t & VT_BTYPE) == VT_FLOAT)
                size = 4;
            else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
                size = 8;
            else
                size = 12;
            oad(0xec81, size); /* sub $xxx, %esp */
            if (size == 12)
                o(0x7cdb);
            else
                o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */
            g(0x24);
            g(0x00);
            args_size += size;
        } else {
            /* simple type (currently always same size) */
            /* XXX: implicit cast ? */
            r = gv(RC_INT);
            if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
                size = 8;
                o(0x50 + vtop->r2); /* push r */
            } else {
                size = 4;
            }
            o(0x50 + r); /* push r */
            args_size += size;
        }
        vtop--;
    }
    save_regs(0); /* save used temporary registers */
    func_sym = vtop->type.ref;
    func_call = func_sym->a.func_call;
    /* fast call case */
    if ((func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) ||
        func_call == FUNC_FASTCALLW) {
        int fastcall_nb_regs;
        uint8_t *fastcall_regs_ptr;
        if (func_call == FUNC_FASTCALLW) {
            fastcall_regs_ptr = fastcallw_regs;
            fastcall_nb_regs = 2;
        } else {
            fastcall_regs_ptr = fastcall_regs;
            fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
        }
        for(i = 0;i < fastcall_nb_regs; i++) {
            if (args_size <= 0)
                break;
            o(0x58 + fastcall_regs_ptr[i]); /* pop r */
            /* XXX: incorrect for struct/floats */
            args_size -= 4;
        }
    }
#ifndef TCC_TARGET_PE
    else if ((vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT)
        args_size -= 4;
#endif
    gcall_or_jmp(0);

    if (args_size && func_call != FUNC_STDCALL)
        gadd_sp(args_size);
    vtop--;
}

#ifdef TCC_TARGET_PE
#define FUNC_PROLOG_SIZE (10 + USE_EBX)
#else
#define FUNC_PROLOG_SIZE (9 + USE_EBX)
#endif

/* generate function prolog of type 't' */
ST_FUNC void gfunc_prolog(CType *func_type)
{
    int addr, align, size, func_call, fastcall_nb_regs;
    int param_index, param_addr;
    uint8_t *fastcall_regs_ptr;
    Sym *sym;
    CType *type;

    sym = func_type->ref;
    func_call = sym->a.func_call;
    addr = 8;
    loc = 0;
    func_vc = 0;

    if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) {
        fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
        fastcall_regs_ptr = fastcall_regs;
    } else if (func_call == FUNC_FASTCALLW) {
        fastcall_nb_regs = 2;
        fastcall_regs_ptr = fastcallw_regs;
    } else {
        fastcall_nb_regs = 0;
        fastcall_regs_ptr = NULL;
    }
    param_index = 0;

    ind += FUNC_PROLOG_SIZE;
    func_sub_sp_offset = ind;
    /* if the function returns a structure, then add an
       implicit pointer parameter */
    func_vt = sym->type;
    func_var = (sym->c == FUNC_ELLIPSIS);
#ifdef TCC_TARGET_PE
    size = type_size(&func_vt,&align);
    if (((func_vt.t & VT_BTYPE) == VT_STRUCT)
        && (size > 8 || (size & (size - 1)))) {
#else
    if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
#endif
        /* XXX: fastcall case ? */
        func_vc = addr;
        addr += 4;
        param_index++;
    }
    /* define parameters */
    while ((sym = sym->next) != NULL) {
        type = &sym->type;
        size = type_size(type, &align);
        size = (size + 3) & ~3;
#ifdef FUNC_STRUCT_PARAM_AS_PTR
        /* structs are passed as pointer */
        if ((type->t & VT_BTYPE) == VT_STRUCT) {
            size = 4;
        }
#endif
        if (param_index < fastcall_nb_regs) {
            /* save FASTCALL register */
            loc -= 4;
            o(0x89);     /* movl */
            gen_modrm(fastcall_regs_ptr[param_index], VT_LOCAL, NULL, loc);
            param_addr = loc;
        } else {
            param_addr = addr;
            addr += size;
        }
        sym_push(sym->v & ~SYM_FIELD, type,
                 VT_LOCAL | lvalue_type(type->t), param_addr);
        param_index++;
    }
    func_ret_sub = 0;
    /* pascal type call ? */
    if (func_call == FUNC_STDCALL)
        func_ret_sub = addr - 8;
#ifndef TCC_TARGET_PE
    else if (func_vc)
        func_ret_sub = 4;
#endif

#ifdef CONFIG_TCC_BCHECK
    /* leave some room for bound checking code */
    if (tcc_state->do_bounds_check) {
        func_bound_offset = lbounds_section->data_offset;
        func_bound_ind = ind;
        oad(0xb8, 0); /* lbound section pointer */
        oad(0xb8, 0); /* call to function */
    }
#endif
}

/* generate function epilog */
ST_FUNC void gfunc_epilog(void)
{
    addr_t v, saved_ind;

#ifdef CONFIG_TCC_BCHECK
    if (tcc_state->do_bounds_check
     && func_bound_offset != lbounds_section->data_offset) {
        addr_t saved_ind;
        addr_t *bounds_ptr;
        Sym *sym_data;

        /* add end of table info */
        bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t));
        *bounds_ptr = 0;

        /* generate bound local allocation */
        saved_ind = ind;
        ind = func_bound_ind;
        sym_data = get_sym_ref(&char_pointer_type, lbounds_section, 
                               func_bound_offset, lbounds_section->data_offset);
        greloc(cur_text_section, sym_data,
               ind + 1, R_386_32);
        oad(0xb8, 0); /* mov %eax, xxx */
        gen_static_call(TOK___bound_local_new);
        ind = saved_ind;

        /* generate bound check local freeing */
        o(0x5250); /* save returned value, if any */
        greloc(cur_text_section, sym_data, ind + 1, R_386_32);
        oad(0xb8, 0); /* mov %eax, xxx */
        gen_static_call(TOK___bound_local_delete);
        o(0x585a); /* restore returned value, if any */
    }
#endif

    /* align local size to word & save local variables */
    v = (-loc + 3) & -4;

#if USE_EBX
    o(0x8b);
    gen_modrm(TREG_EBX, VT_LOCAL, NULL, -(v+4));
#endif

    o(0xc9); /* leave */
    if (func_ret_sub == 0) {
        o(0xc3); /* ret */
    } else {
        o(0xc2); /* ret n */
        g(func_ret_sub);
        g(func_ret_sub >> 8);
    }
    saved_ind = ind;
    ind = func_sub_sp_offset - FUNC_PROLOG_SIZE;
#ifdef TCC_TARGET_PE
    if (v >= 4096) {
        oad(0xb8, v); /* mov stacksize, %eax */
        gen_static_call(TOK___chkstk); /* call __chkstk, (does the stackframe too) */
    } else
#endif
    {
        o(0xe58955);  /* push %ebp, mov %esp, %ebp */
        o(0xec81);  /* sub esp, stacksize */
        gen_le32(v);
#ifdef TCC_TARGET_PE
        o(0x90);  /* adjust to FUNC_PROLOG_SIZE */
#endif
    }
    o(0x53 * USE_EBX); /* push ebx */
    ind = saved_ind;
}

/* generate a jump to a label */
ST_FUNC int gjmp(int t)
{
    return gjmp2(0xe9, t);
}

/* generate a jump to a fixed address */
ST_FUNC void gjmp_addr(int a)
{
    int r;
    r = a - ind - 2;
    if (r == (char)r) {
        g(0xeb);
        g(r);
    } else {
        oad(0xe9, a - ind - 5);
    }
}

ST_FUNC void gtst_addr(int inv, int a)
{
    int v = vtop->r & VT_VALMASK;
    if (v == VT_CMP) {
    inv ^= (vtop--)->c.i;
    a -= ind + 2;
    if (a == (char)a) {
        g(inv - 32);
        g(a);
    } else {
        g(0x0f);
        oad(inv - 16, a - 4);
    }
    } else if ((v & ~1) == VT_JMP) {
    if ((v & 1) != inv) {
        gjmp_addr(a);
        gsym(vtop->c.i);
    } else {
        gsym(vtop->c.i);
        o(0x05eb);
        gjmp_addr(a);
    }
    vtop--;
    }
}

/* generate a test. set 'inv' to invert test. Stack entry is popped */
ST_FUNC int gtst(int inv, int t)
{
    int v = vtop->r & VT_VALMASK;
    if (nocode_wanted) {
        ;
    } else if (v == VT_CMP) {
        /* fast case : can jump directly since flags are set */
        g(0x0f);
        t = gjmp2((vtop->c.i - 16) ^ inv, t);
    } else if (v == VT_JMP || v == VT_JMPI) {
        /* && or || optimization */
        if ((v & 1) == inv) {
            /* insert vtop->c jump list in t */
            uint32_t n1, n = vtop->c.i;
            if (n) {
                while ((n1 = read32le(cur_text_section->data + n)))
                    n = n1;
                write32le(cur_text_section->data + n, t);
                t = vtop->c.i;
            }
        } else {
            t = gjmp(t);
            gsym(vtop->c.i);
        }
    }
    vtop--;
    return t;
}

/* generate an integer binary operation */
ST_FUNC void gen_opi(int op)
{
    int r, fr, opc, c;

    switch(op) {
    case '+':
    case TOK_ADDC1: /* add with carry generation */
        opc = 0;
    gen_op8:
        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
            /* constant case */
            vswap();
            r = gv(RC_INT);
            vswap();
            c = vtop->c.i;
            if (c == (char)c) {
                /* generate inc and dec for smaller code */
                if (c==1 && opc==0 && op != TOK_ADDC1) {
                    o (0x40 | r); // inc
                } else if (c==1 && opc==5 && op != TOK_SUBC1) {
                    o (0x48 | r); // dec
                } else {
                    o(0x83);
                    o(0xc0 | (opc << 3) | r);
                    g(c);
                }
            } else {
                o(0x81);
                oad(0xc0 | (opc << 3) | r, c);
            }
        } else {
            gv2(RC_INT, RC_INT);
            r = vtop[-1].r;
            fr = vtop[0].r;
            o((opc << 3) | 0x01);
            o(0xc0 + r + fr * 8); 
        }
        vtop--;
        if (op >= TOK_ULT && op <= TOK_GT) {
            vtop->r = VT_CMP;
            vtop->c.i = op;
        }
        break;
    case '-':
    case TOK_SUBC1: /* sub with carry generation */
        opc = 5;
        goto gen_op8;
    case TOK_ADDC2: /* add with carry use */
        opc = 2;
        goto gen_op8;
    case TOK_SUBC2: /* sub with carry use */
        opc = 3;
        goto gen_op8;
    case '&':
        opc = 4;
        goto gen_op8;
    case '^':
        opc = 6;
        goto gen_op8;
    case '|':
        opc = 1;
        goto gen_op8;
    case '*':
        gv2(RC_INT, RC_INT);
        r = vtop[-1].r;
        fr = vtop[0].r;
        vtop--;
        o(0xaf0f); /* imul fr, r */
        o(0xc0 + fr + r * 8);
        break;
    case TOK_SHL:
        opc = 4;
        goto gen_shift;
    case TOK_SHR:
        opc = 5;
        goto gen_shift;
    case TOK_SAR:
        opc = 7;
    gen_shift:
        opc = 0xc0 | (opc << 3);
        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
            /* constant case */
            vswap();
            r = gv(RC_INT);
            vswap();
            c = vtop->c.i & 0x1f;
            o(0xc1); /* shl/shr/sar $xxx, r */
            o(opc | r);
            g(c);
        } else {
            /* we generate the shift in ecx */
            gv2(RC_INT, RC_ECX);
            r = vtop[-1].r;
            o(0xd3); /* shl/shr/sar %cl, r */
            o(opc | r);
        }
        vtop--;
        break;
    case '/':
    case TOK_UDIV:
    case TOK_PDIV:
    case '%':
    case TOK_UMOD:
    case TOK_UMULL:
        /* first operand must be in eax */
        /* XXX: need better constraint for second operand */
        gv2(RC_EAX, RC_ECX);
        r = vtop[-1].r;
        fr = vtop[0].r;
        vtop--;
        save_reg(TREG_EDX);
        /* save EAX too if used otherwise */
        save_reg_upstack(TREG_EAX, 1);
        if (op == TOK_UMULL) {
            o(0xf7); /* mul fr */
            o(0xe0 + fr);
            vtop->r2 = TREG_EDX;
            r = TREG_EAX;
        } else {
            if (op == TOK_UDIV || op == TOK_UMOD) {
                o(0xf7d231); /* xor %edx, %edx, div fr, %eax */
                o(0xf0 + fr);
            } else {
                o(0xf799); /* cltd, idiv fr, %eax */
                o(0xf8 + fr);
            }
            if (op == '%' || op == TOK_UMOD)
                r = TREG_EDX;
            else
                r = TREG_EAX;
        }
        vtop->r = r;
        break;
    default:
        opc = 7;
        goto gen_op8;
    }
}

/* generate a floating point operation 'v = t1 op t2' instruction. The
   two operands are guaranteed to have the same floating point type */
/* XXX: need to use ST1 too */
ST_FUNC void gen_opf(int op)
{
    int a, ft, fc, swapped, r;

    /* convert constants to memory references */
    if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
        vswap();
        gv(RC_FLOAT);
        vswap();
    }
    if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST)
        gv(RC_FLOAT);

    /* must put at least one value in the floating point register */
    if ((vtop[-1].r & VT_LVAL) &&
        (vtop[0].r & VT_LVAL)) {
        vswap();
        gv(RC_FLOAT);
        vswap();
    }
    swapped = 0;
    /* swap the stack if needed so that t1 is the register and t2 is
       the memory reference */
    if (vtop[-1].r & VT_LVAL) {
        vswap();
        swapped = 1;
    }
    if (op >= TOK_ULT && op <= TOK_GT) {
        /* load on stack second operand */
        load(TREG_ST0, vtop);
        save_reg(TREG_EAX); /* eax is used by FP comparison code */
        if (op == TOK_GE || op == TOK_GT)
            swapped = !swapped;
        else if (op == TOK_EQ || op == TOK_NE)
            swapped = 0;
        if (swapped)
            o(0xc9d9); /* fxch %st(1) */
        if (op == TOK_EQ || op == TOK_NE)
            o(0xe9da); /* fucompp */
        else
            o(0xd9de); /* fcompp */
        o(0xe0df); /* fnstsw %ax */
        if (op == TOK_EQ) {
            o(0x45e480); /* and $0x45, %ah */
            o(0x40fC80); /* cmp $0x40, %ah */
        } else if (op == TOK_NE) {
            o(0x45e480); /* and $0x45, %ah */
            o(0x40f480); /* xor $0x40, %ah */
            op = TOK_NE;
        } else if (op == TOK_GE || op == TOK_LE) {
            o(0x05c4f6); /* test $0x05, %ah */
            op = TOK_EQ;
        } else {
            o(0x45c4f6); /* test $0x45, %ah */
            op = TOK_EQ;
        }
        vtop--;
        vtop->r = VT_CMP;
        vtop->c.i = op;
    } else {
        /* no memory reference possible for long double operations */
        if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
            load(TREG_ST0, vtop);
            swapped = !swapped;
        }
        
        switch(op) {
        default:
        case '+':
            a = 0;
            break;
        case '-':
            a = 4;
            if (swapped)
                a++;
            break;
        case '*':
            a = 1;
            break;
        case '/':
            a = 6;
            if (swapped)
                a++;
            break;
        }
        ft = vtop->type.t;
        fc = vtop->c.i;
        if ((ft & VT_BTYPE) == VT_LDOUBLE) {
            o(0xde); /* fxxxp %st, %st(1) */
            o(0xc1 + (a << 3));
        } else {
            /* if saved lvalue, then we must reload it */
            r = vtop->r;
            if ((r & VT_VALMASK) == VT_LLOCAL) {
                SValue v1;
                r = get_reg(RC_INT);
                v1.type.t = VT_INT;
                v1.r = VT_LOCAL | VT_LVAL;
                v1.c.i = fc;
                load(r, &v1);
                fc = 0;
            }

            if ((ft & VT_BTYPE) == VT_DOUBLE)
                o(0xdc);
            else
                o(0xd8);
            gen_modrm(a, r, vtop->sym, fc);
        }
        vtop--;
    }
}

/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
   and 'long long' cases. */
ST_FUNC void gen_cvt_itof(int t)
{
    save_reg(TREG_ST0);
    gv(RC_INT);
    if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
        /* signed long long to float/double/long double (unsigned case
           is handled generically) */
        o(0x50 + vtop->r2); /* push r2 */
        o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
        o(0x242cdf); /* fildll (%esp) */
        o(0x08c483); /* add $8, %esp */
    } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == 
               (VT_INT | VT_UNSIGNED)) {
        /* unsigned int to float/double/long double */
        o(0x6a); /* push $0 */
        g(0x00);
        o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
        o(0x242cdf); /* fildll (%esp) */
        o(0x08c483); /* add $8, %esp */
    } else {
        /* int to float/double/long double */
        o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
        o(0x2404db); /* fildl (%esp) */
        o(0x04c483); /* add $4, %esp */
    }
    vtop->r = TREG_ST0;
}

/* convert fp to int 't' type */
ST_FUNC void gen_cvt_ftoi(int t)
{
#if HAVE_FLOAT
    int bt = vtop->type.t & VT_BTYPE;
    if (bt == VT_FLOAT)
        vpush_global_sym(&func_old_type, TOK___fixsfdi);
    else if (bt == VT_LDOUBLE)
        vpush_global_sym(&func_old_type, TOK___fixxfdi);
    else
        vpush_global_sym(&func_old_type, TOK___fixdfdi);
    vswap();
    gfunc_call(1);
    vpushi(0);
    vtop->r = REG_IRET;
    vtop->r2 = REG_LRET;
#endif // HAVE_FLOAT
}

/* convert from one floating point type to another */
ST_FUNC void gen_cvt_ftof(int t)
{
    /* all we have to do on i386 is to put the float in a register */
    gv(RC_FLOAT);
}

/* computed goto support */
ST_FUNC void ggoto(void)
{
    gcall_or_jmp(1);
    vtop--;
}

/* bound check support functions */
#ifdef CONFIG_TCC_BCHECK

/* generate a bounded pointer addition */
ST_FUNC void gen_bounded_ptr_add(void)
{
    /* prepare fast i386 function call (args in eax and edx) */
    gv2(RC_EAX, RC_EDX);
    /* save all temporary registers */
    vtop -= 2;
    save_regs(0);
    /* do a fast function call */
    gen_static_call(TOK___bound_ptr_add);
    /* returned pointer is in eax */
    vtop++;
    vtop->r = TREG_EAX | VT_BOUNDED;
    /* address of bounding function call point */
    vtop->c.i = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel));
}

/* patch pointer addition in vtop so that pointer dereferencing is
   also tested */
ST_FUNC void gen_bounded_ptr_deref(void)
{
    addr_t func;
    int  size, align;
    Elf32_Rel *rel;
    Sym *sym;

    size = 0;
    /* XXX: put that code in generic part of tcc */
    if (!is_float(vtop->type.t)) {
        if (vtop->r & VT_LVAL_BYTE)
            size = 1;
        else if (vtop->r & VT_LVAL_SHORT)
            size = 2;
    }
    if (!size)
        size = type_size(&vtop->type, &align);
    switch(size) {
    case  1: func = TOK___bound_ptr_indir1; break;
    case  2: func = TOK___bound_ptr_indir2; break;
    case  4: func = TOK___bound_ptr_indir4; break;
    case  8: func = TOK___bound_ptr_indir8; break;
    case 12: func = TOK___bound_ptr_indir12; break;
    case 16: func = TOK___bound_ptr_indir16; break;
    default:
        tcc_error("unhandled size when dereferencing bounded pointer");
        func = 0;
        break;
    }

    /* patch relocation */
    /* XXX: find a better solution ? */
    rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.i);
    sym = external_global_sym(func, &func_old_type, 0);
    if (!sym->c)
        put_extern_sym(sym, NULL, 0, 0);
    rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info));
}
#endif

/* Save the stack pointer onto the stack */
ST_FUNC void gen_vla_sp_save(int addr) {
    /* mov %esp,addr(%ebp)*/
    o(0x89);
    gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
}

/* Restore the SP from a location on the stack */
ST_FUNC void gen_vla_sp_restore(int addr) {
    o(0x8b);
    gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
}

/* Subtract from the stack pointer, and push the resulting value onto the stack */
ST_FUNC void gen_vla_alloc(CType *type, int align) {
#ifdef TCC_TARGET_PE
    /* alloca does more than just adjust %rsp on Windows */
    vpush_global_sym(&func_old_type, TOK_alloca);
    vswap(); /* Move alloca ref past allocation size */
    gfunc_call(1);
#else
    int r;
    r = gv(RC_INT); /* allocation size */
    /* sub r,%rsp */
    o(0x2b);
    o(0xe0 | r);
    /* We align to 16 bytes rather than align */
    /* and ~15, %esp */
    o(0xf0e483);
    vpop();
#endif
}

/* end of X86 code generator */
/*************************************************************/
#endif
/*************************************************************/

https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/i386-link.c

File: ../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/i386-link.c used in: Step 378, Step 395, Step 407, Step 419, Step 431, Step 443, Step 455
#ifdef TARGET_DEFS_ONLY

#define EM_TCC_TARGET EM_386

/* relocation type for 32 bit data relocation */
#define R_DATA_32   R_386_32
#define R_DATA_PTR  R_386_32
#define R_JMP_SLOT  R_386_JMP_SLOT
#define R_GLOB_DAT  R_386_GLOB_DAT
#define R_COPY      R_386_COPY
#define R_RELATIVE  R_386_RELATIVE

#define R_NUM       R_386_NUM

#define ELF_START_ADDR 0x08048000
#define ELF_PAGE_SIZE  0x1000

#define PCRELATIVE_DLLPLT 0
#define RELOCATE_DLLPLT 0

#else /* !TARGET_DEFS_ONLY */

#ifndef _TCC_H
#include "tcc.h"
#endif

/* Returns 1 for a code relocation, 0 for a data relocation. For unknown
   relocations, returns -1. */
int code_reloc (int reloc_type)
{
    switch (reloc_type) {
    case R_386_RELATIVE:
    case R_386_16:
        case R_386_32:
    case R_386_GOTPC:
    case R_386_GOTOFF:
    case R_386_GOT32:
    case R_386_GOT32X:
    case R_386_GLOB_DAT:
    case R_386_COPY:
            return 0;

    case R_386_PC16:
    case R_386_PC32:
    case R_386_PLT32:
    case R_386_JMP_SLOT:
            return 1;
    }

    tcc_error ("Unknown relocation type: %d", reloc_type);
    return -1;
}

/* Returns an enumerator to describe whether and when the relocation needs a
   GOT and/or PLT entry to be created. See tcc.h for a description of the
   different values. */
int gotplt_entry_type (int reloc_type)
{
    switch (reloc_type) {
    case R_386_RELATIVE:
    case R_386_16:
    case R_386_GLOB_DAT:
    case R_386_JMP_SLOT:
    case R_386_COPY:
            return NO_GOTPLT_ENTRY;

        case R_386_32:
        /* This relocations shouldn't normally need GOT or PLT
           slots if it weren't for simplicity in the code generator.
           See our caller for comments.  */
            return AUTO_GOTPLT_ENTRY;

    case R_386_PC16:
    case R_386_PC32:
            return AUTO_GOTPLT_ENTRY;

    case R_386_GOTPC:
    case R_386_GOTOFF:
            return BUILD_GOT_ONLY;

    case R_386_GOT32:
    case R_386_GOT32X:
    case R_386_PLT32:
            return ALWAYS_GOTPLT_ENTRY;
    }

    tcc_error ("Unknown relocation type: %d", reloc_type);
    return -1;
}

ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
{
    Section *plt = s1->plt;
    uint8_t *p;
    int modrm;
    unsigned plt_offset, relofs;

    /* on i386 if we build a DLL, we add a %ebx offset */
    if (s1->output_type == TCC_OUTPUT_DLL)
        modrm = 0xa3;
    else
        modrm = 0x25;

    /* empty PLT: create PLT0 entry that pushes the library identifier
       (GOT + PTR_SIZE) and jumps to ld.so resolution routine
       (GOT + 2 * PTR_SIZE) */
    if (plt->data_offset == 0) {
        p = section_ptr_add(plt, 16);
        p[0] = 0xff; /* pushl got + PTR_SIZE */
        p[1] = modrm + 0x10;
        write32le(p + 2, PTR_SIZE);
        p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */
        p[7] = modrm;
        write32le(p + 8, PTR_SIZE * 2);
    }
    plt_offset = plt->data_offset;

    /* The PLT slot refers to the relocation entry it needs via offset.
       The reloc entry is created below, so its offset is the current
       data_offset */
    relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0;

    /* Jump to GOT entry where ld.so initially put the address of ip + 4 */
    p = section_ptr_add(plt, 16);
    p[0] = 0xff; /* jmp *(got + x) */
    p[1] = modrm;
    write32le(p + 2, got_offset);
    p[6] = 0x68; /* push $xxx */
    write32le(p + 7, relofs);
    p[11] = 0xe9; /* jmp plt_start */
    write32le(p + 12, -(plt->data_offset));
    return plt_offset;
}

/* relocate the PLT: compute addresses and offsets in the PLT now that final
   address for PLT and GOT are known (see fill_program_header) */
ST_FUNC void relocate_plt(TCCState *s1)
{
    uint8_t *p, *p_end;

    if (!s1->plt)
      return;

    p = s1->plt->data;
    p_end = p + s1->plt->data_offset;

    if (p < p_end) {
        add32le(p + 2, s1->got->sh_addr);
        add32le(p + 8, s1->got->sh_addr);
        p += 16;
        while (p < p_end) {
            add32le(p + 2, s1->got->sh_addr);
            p += 16;
        }
    }
}

static ElfW_Rel *qrel; /* ptr to next reloc entry reused */

void relocate_init(Section *sr)
{
    qrel = (ElfW_Rel *) sr->data;
}

void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val)
{
    int sym_index, esym_index;

    sym_index = ELFW(R_SYM)(rel->r_info);

    switch (type) {
        case R_386_32:
            if (s1->output_type == TCC_OUTPUT_DLL) {
                esym_index = s1->sym_attrs[sym_index].dyn_index;
                qrel->r_offset = rel->r_offset;
                if (esym_index) {
                    qrel->r_info = ELFW(R_INFO)(esym_index, R_386_32);
                    qrel++;
                    return;
                } else {
                    qrel->r_info = ELFW(R_INFO)(0, R_386_RELATIVE);
                    qrel++;
                }
            }
            add32le(ptr, val);
            return;
        case R_386_PC32:
            if (s1->output_type == TCC_OUTPUT_DLL) {
                /* DLL relocation */
                esym_index = s1->sym_attrs[sym_index].dyn_index;
                if (esym_index) {
                    qrel->r_offset = rel->r_offset;
                    qrel->r_info = ELFW(R_INFO)(esym_index, R_386_PC32);
                    qrel++;
                    return;
                }
            }
            add32le(ptr, val - addr);
            return;
        case R_386_PLT32:
            add32le(ptr, val - addr);
            return;
        case R_386_GLOB_DAT:
        case R_386_JMP_SLOT:
            write32le(ptr, val);
            return;
        case R_386_GOTPC:
            add32le(ptr, s1->got->sh_addr - addr);
            return;
        case R_386_GOTOFF:
            add32le(ptr, val - s1->got->sh_addr);
            return;
        case R_386_GOT32:
        case R_386_GOT32X:
            /* we load the got offset */
            add32le(ptr, s1->sym_attrs[sym_index].got_offset);
            return;
        case R_386_16:
            if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) {
            output_file:
                tcc_error("can only produce 16-bit binary files");
            }
            write16le(ptr, read16le(ptr) + val);
            return;
        case R_386_PC16:
            if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY)
                goto output_file;
            write16le(ptr, read16le(ptr) + val - addr);
            return;
        case R_386_RELATIVE:
            /* do nothing */
            return;
        case R_386_COPY:
            /* This relocation must copy initialized data from the library
            to the program .bss segment. Currently made like for ARM
            (to remove noise of default case). Is this true?
            */
            return;
        default:
            fprintf(stderr,"FIXME: handle reloc type %d at %x [%p] to %x\n",
                type, (unsigned)addr, ptr, (unsigned)val);
            return;
    }
}

#endif /* !TARGET_DEFS_ONLY */

https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/i386-asm.c

File: ../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/i386-asm.c used in: Step 378, Step 395, Step 407, Step 419, Step 431, Step 443, Step 455
/*
 *  i386 specific functions for TCC assembler
 *
 *  Copyright (c) 2001, 2002 Fabrice Bellard
 *  Copyright (c) 2009 Frédéric Feret (x86_64 support)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef _TCC_H
#include "tcc.h"
#endif

#define MAX_OPERANDS 3

#define TOK_ASM_first TOK_ASM_clc
#define TOK_ASM_last TOK_ASM_emms
#define TOK_ASM_alllast TOK_ASM_subps

#define OPC_B          0x01  /* only used with OPC_WL */
#define OPC_WL         0x02  /* accepts w, l or no suffix */
#define OPC_BWL        (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */
#define OPC_REG        0x04 /* register is added to opcode */
#define OPC_MODRM      0x08 /* modrm encoding */

#define OPCT_MASK      0x70
#define OPC_FWAIT      0x10 /* add fwait opcode */
#define OPC_SHIFT      0x20 /* shift opcodes */
#define OPC_ARITH      0x30 /* arithmetic opcodes */
#define OPC_FARITH     0x40 /* FPU arithmetic opcodes */
#define OPC_TEST       0x50 /* test opcodes */
#define OPCT_IS(v,i) (((v) & OPCT_MASK) == (i))

#define OPC_0F        0x100 /* Is secondary map (0x0f prefix) */
#define OPC_48        0x200 /* Always has REX prefix */
#ifdef TCC_TARGET_X86_64
# define OPC_WLQ     0x1000  /* accepts w, l, q or no suffix */
# define OPC_BWLQ    (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */
# define OPC_WLX     OPC_WLQ
# define OPC_BWLX    OPC_BWLQ
#else
# define OPC_WLX     OPC_WL
# define OPC_BWLX    OPC_BWL
#endif

#define OPC_GROUP_SHIFT 13

/* in order to compress the operand type, we use specific operands and
   we or only with EA  */
enum {
    OPT_REG8=0, /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_REG16,  /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_REG32,  /* warning: value is hardcoded from TOK_ASM_xxx */
#ifdef TCC_TARGET_X86_64
    OPT_REG64,  /* warning: value is hardcoded from TOK_ASM_xxx */
#endif
    OPT_MMX,    /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_SSE,    /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_CR,     /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_TR,     /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_DB,     /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_SEG,
    OPT_ST,
#ifdef TCC_TARGET_X86_64
    OPT_REG8_LOW, /* %spl,%bpl,%sil,%dil, encoded like ah,ch,dh,bh, but
             with REX prefix, not used in insn templates */
#endif
    OPT_IM8,
    OPT_IM8S,
    OPT_IM16,
    OPT_IM32,
#ifdef TCC_TARGET_X86_64
    OPT_IM64,
#endif
    OPT_EAX,    /* %al, %ax, %eax or %rax register */
    OPT_ST0,    /* %st(0) register */
    OPT_CL,     /* %cl register */
    OPT_DX,     /* %dx register */
    OPT_ADDR,   /* OP_EA with only offset */
    OPT_INDIR,  /* *(expr) */
    /* composite types */
    OPT_COMPOSITE_FIRST,
    OPT_IM,     /* IM8 | IM16 | IM32 */
    OPT_REG,    /* REG8 | REG16 | REG32 | REG64 */
    OPT_REGW,   /* REG16 | REG32 | REG64 */
    OPT_IMW,    /* IM16 | IM32 */
    OPT_MMXSSE, /* MMX | SSE */
    OPT_DISP,   /* Like OPT_ADDR, but emitted as displacement (for jumps) */
    OPT_DISP8,  /* Like OPT_ADDR, but only 8bit (short jumps) */
    /* can be ored with any OPT_xxx */
    OPT_EA = 0x80
};

#define OP_REG8   (1 << OPT_REG8)
#define OP_REG16  (1 << OPT_REG16)
#define OP_REG32  (1 << OPT_REG32)
#define OP_MMX    (1 << OPT_MMX)
#define OP_SSE    (1 << OPT_SSE)
#define OP_CR     (1 << OPT_CR)
#define OP_TR     (1 << OPT_TR)
#define OP_DB     (1 << OPT_DB)
#define OP_SEG    (1 << OPT_SEG)
#define OP_ST     (1 << OPT_ST)
#define OP_IM8    (1 << OPT_IM8)
#define OP_IM8S   (1 << OPT_IM8S)
#define OP_IM16   (1 << OPT_IM16)
#define OP_IM32   (1 << OPT_IM32)
#define OP_EAX    (1 << OPT_EAX)
#define OP_ST0    (1 << OPT_ST0)
#define OP_CL     (1 << OPT_CL)
#define OP_DX     (1 << OPT_DX)
#define OP_ADDR   (1 << OPT_ADDR)
#define OP_INDIR  (1 << OPT_INDIR)
#ifdef TCC_TARGET_X86_64
# define OP_REG64 (1 << OPT_REG64)
# define OP_REG8_LOW (1 << OPT_REG8_LOW)
# define OP_IM64  (1 << OPT_IM64)
# define OP_EA32  (OP_EA << 1)
#else
# define OP_REG64 0
# define OP_REG8_LOW 0
# define OP_IM64  0
# define OP_EA32  0
#endif

#define OP_EA     0x40000000
#define OP_REG    (OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64)

#ifdef TCC_TARGET_X86_64
# define TREG_XAX   TREG_RAX
# define TREG_XCX   TREG_RCX
# define TREG_XDX   TREG_RDX
#else
# define TREG_XAX   TREG_EAX
# define TREG_XCX   TREG_ECX
# define TREG_XDX   TREG_EDX
#endif

typedef struct ASMInstr {
    uint16_t sym;
    uint16_t opcode;
    uint16_t instr_type;
    uint8_t nb_ops;
    uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */
} ASMInstr;

typedef struct Operand {
    uint32_t type;
    int8_t  reg; /* register, -1 if none */
    int8_t  reg2; /* second register, -1 if none */
    uint8_t shift;
    ExprValue e;
} Operand;

static const uint8_t reg_to_size[9] = {
/*
    [OP_REG8] = 0,
    [OP_REG16] = 1,
    [OP_REG32] = 2,
#ifdef TCC_TARGET_X86_64
    [OP_REG64] = 3,
#endif
*/
    0, 0, 1, 0, 2, 0, 0, 0, 3
};

#define NB_TEST_OPCODES 30

static const uint8_t test_bits[NB_TEST_OPCODES] = {
 0x00, /* o */
 0x01, /* no */
 0x02, /* b */
 0x02, /* c */
 0x02, /* nae */
 0x03, /* nb */
 0x03, /* nc */
 0x03, /* ae */
 0x04, /* e */
 0x04, /* z */
 0x05, /* ne */
 0x05, /* nz */
 0x06, /* be */
 0x06, /* na */
 0x07, /* nbe */
 0x07, /* a */
 0x08, /* s */
 0x09, /* ns */
 0x0a, /* p */
 0x0a, /* pe */
 0x0b, /* np */
 0x0b, /* po */
 0x0c, /* l */
 0x0c, /* nge */
 0x0d, /* nl */
 0x0d, /* ge */
 0x0e, /* le */
 0x0e, /* ng */
 0x0f, /* nle */
 0x0f, /* g */
};

static const uint8_t segment_prefixes[] = {
 0x26, /* es */
 0x2e, /* cs */
 0x36, /* ss */
 0x3e, /* ds */
 0x64, /* fs */
 0x65  /* gs */
};

static const ASMInstr asm_instrs[] = {
#define ALT(x) x
/* This removes a 0x0f in the second byte */
#if HAVE_LONG_LONG
#define O(o) ((uint64_t) ((((o) & 0xff00) == 0x0f00) ? ((((o) >> 8) & ~0xff) | ((o) & 0xff)) : (o)))
#else
#define O(o) ((uint32_t) ((((o) & 0xff00) == 0x0f00) ? ((((o) >> 8) & ~0xff) | ((o) & 0xff)) : (o)))
#endif
/* This constructs instr_type from opcode, type and group.  */
#define T(o,i,g) ((i) | ((g) << OPC_GROUP_SHIFT) | ((((o) & 0xff00) == 0x0f00) ? OPC_0F : 0))
#define DEF_ASM_OP0(name, opcode)
#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 0, { 0 } },
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 1, { op0 }},
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 2, { op0, op1 }},
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 3, { op0, op1, op2 }},
#ifdef TCC_TARGET_X86_64
# include "x86_64-asm.h"
#else
# include "i386-asm.h"
#endif
    /* last operation */
    { 0, },
};

static const uint16_t op0_codes[] = {
#define ALT(x)
#define DEF_ASM_OP0(x, opcode) opcode,
#define DEF_ASM_OP0L(name, opcode, group, instr_type)
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
#ifdef TCC_TARGET_X86_64
# include "x86_64-asm.h"
#else
# include "i386-asm.h"
#endif
};

static inline int get_reg_shift(TCCState *s1)
{
    int shift, v;
    v = asm_int_expr(s1);
    switch(v) {
    case 1:
        shift = 0;
        break;
    case 2:
        shift = 1;
        break;
    case 4:
        shift = 2;
        break;
    case 8:
        shift = 3;
        break;
    default:
        expect("1, 2, 4 or 8 constant");
        shift = 0;
        break;
    }
    return shift;
}

#ifdef TCC_TARGET_X86_64
static int asm_parse_numeric_reg(int t, unsigned int *type)
{
    int reg = -1;
    if (t >= TOK_IDENT && t < tok_ident) {
    const char *s = table_ident[t - TOK_IDENT]->str;
    char c;
    *type = OP_REG64;
    if (*s == 'c') {
        s++;
        *type = OP_CR;
    }
    if (*s++ != 'r')
      return -1;
    /* Don't allow leading '0'.  */
    if ((c = *s++) >= '1' && c <= '9')
      reg = c - '0';
    else
      return -1;
    if ((c = *s) >= '0' && c <= '5')
      s++, reg = reg * 10 + c - '0';
    if (reg > 15)
      return -1;
    if ((c = *s) == 0)
      ;
    else if (*type != OP_REG64)
      return -1;
    else if (c == 'b' && !s[1])
      *type = OP_REG8;
    else if (c == 'w' && !s[1])
      *type = OP_REG16;
    else if (c == 'd' && !s[1])
      *type = OP_REG32;
    else
      return -1;
    }
    return reg;
}
#endif

static int asm_parse_reg(unsigned int *type)
{
    int reg = 0;
    *type = 0;
    if (tok != '%')
        goto error_32;
    next();
    if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) {
        reg = tok - TOK_ASM_eax;
    *type = OP_REG32;
#ifdef TCC_TARGET_X86_64
    } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) {
        reg = tok - TOK_ASM_rax;
    *type = OP_REG64;
    } else if (tok == TOK_ASM_rip) {
        reg = -2; /* Probably should use different escape code. */
    *type = OP_REG64;
    } else if ((reg = asm_parse_numeric_reg(tok, type)) >= 0
           && (*type == OP_REG32 || *type == OP_REG64)) {
    ;
#endif
    } else {
    error_32:
        expect("register");
    }
    next();
    return reg;
}

static void parse_operand(TCCState *s1, Operand *op)
{
    ExprValue e;
    int reg, indir;
    const char *p;

    indir = 0;
    if (tok == '*') {
        next();
        indir = OP_INDIR;
    }

    if (tok == '%') {
        next();
        if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) {
            reg = tok - TOK_ASM_al;
            op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */
            op->reg = reg & 7;
            if ((op->type & OP_REG) && op->reg == TREG_XAX)
                op->type |= OP_EAX;
            else if (op->type == OP_REG8 && op->reg == TREG_XCX)
                op->type |= OP_CL;
            else if (op->type == OP_REG16 && op->reg == TREG_XDX)
                op->type |= OP_DX;
        } else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) {
            op->type = OP_DB;
            op->reg = tok - TOK_ASM_dr0;
        } else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) {
            op->type = OP_SEG;
            op->reg = tok - TOK_ASM_es;
        } else if (tok == TOK_ASM_st) {
            op->type = OP_ST;
            op->reg = 0;
            next();
            if (tok == '(') {
                next();
                if (tok != TOK_PPNUM)
                    goto reg_error;
                p = tokc.str.data;
                reg = p[0] - '0';
                if ((unsigned)reg >= 8 || p[1] != '\0')
                    goto reg_error;
                op->reg = reg;
                next();
                skip(')');
            }
            if (op->reg == 0)
                op->type |= OP_ST0;
            goto no_skip;
#ifdef TCC_TARGET_X86_64
    } else if (tok >= TOK_ASM_spl && tok <= TOK_ASM_dil) {
        op->type = OP_REG8 | OP_REG8_LOW;
        op->reg = 4 + tok - TOK_ASM_spl;
        } else if ((op->reg = asm_parse_numeric_reg(tok, &op->type)) >= 0) {
        ;
#endif
        } else {
        reg_error:
            tcc_error("unknown register %%%s", get_tok_str(tok, &tokc));
        }
        next();
    no_skip: ;
    } else if (tok == '$') {
        /* constant value */
        next();
        asm_expr(s1, &e);
        op->type = OP_IM32;
        op->e = e;
        if (!op->e.sym) {
            if (op->e.v == (uint8_t)op->e.v)
                op->type |= OP_IM8;
            if (op->e.v == (int8_t)op->e.v)
                op->type |= OP_IM8S;
            if (op->e.v == (uint16_t)op->e.v)
                op->type |= OP_IM16;
#ifdef TCC_TARGET_X86_64
            if (op->e.v != (int32_t)op->e.v && op->e.v != (uint32_t)op->e.v)
                op->type = OP_IM64;
#endif
        }
    } else {
        /* address(reg,reg2,shift) with all variants */
        op->type = OP_EA;
        op->reg = -1;
        op->reg2 = -1;
        op->shift = 0;
        if (tok != '(') {
            asm_expr(s1, &e);
            op->e = e;
        } else {
            next();
            if (tok == '%') {
                unget_tok('(');
                op->e.v = 0;
                op->e.sym = NULL;
            } else {
                /* bracketed offset expression */
                asm_expr(s1, &e);
                if (tok != ')')
                    expect(")");
                next();
                op->e.v = e.v;
                op->e.sym = e.sym;
            }
        op->e.pcrel = 0;
        }
        if (tok == '(') {
        unsigned int type = 0;
            next();
            if (tok != ',') {
                op->reg = asm_parse_reg(&type);
            }
            if (tok == ',') {
                next();
                if (tok != ',') {
                    op->reg2 = asm_parse_reg(&type);
                }
                if (tok == ',') {
                    next();
                    op->shift = get_reg_shift(s1);
                }
            }
        if (type & OP_REG32)
            op->type |= OP_EA32;
            skip(')');
        }
        if (op->reg == -1 && op->reg2 == -1)
            op->type |= OP_ADDR;
    }
    op->type |= indir;
}

/* XXX: unify with C code output ? */
ST_FUNC void gen_expr32(ExprValue *pe)
{
    if (pe->pcrel)
        /* If PC-relative, always set VT_SYM, even without symbol,
       so as to force a relocation to be emitted.  */
    gen_addrpc32(VT_SYM, pe->sym, pe->v);
    else
    gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
}

#ifdef TCC_TARGET_X86_64
ST_FUNC void gen_expr64(ExprValue *pe)
{
    gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
}
#endif

/* XXX: unify with C code output ? */
static void gen_disp32(ExprValue *pe)
{
    Sym *sym = pe->sym;
    if (sym && sym->r == cur_text_section->sh_num) {
        /* same section: we can output an absolute value. Note
           that the TCC compiler behaves differently here because
           it always outputs a relocation to ease (future) code
           elimination in the linker */
        gen_le32(pe->v + sym->jnext - ind - 4);
    } else {
        if (sym && sym->type.t == VT_VOID) {
            sym->type.t = VT_FUNC;
            sym->type.ref = NULL;
        }
        gen_addrpc32(VT_SYM, sym, pe->v);
    }
}

/* generate the modrm operand */
static inline int asm_modrm(int reg, Operand *op)
{
    int mod, reg1, reg2, sib_reg1;

    if (op->type & (OP_REG | OP_MMX | OP_SSE)) {
        g(0xc0 + (reg << 3) + op->reg);
    } else if (op->reg == -1 && op->reg2 == -1) {
        /* displacement only */
#ifdef TCC_TARGET_X86_64
    g(0x04 + (reg << 3));
    g(0x25);
#else
    g(0x05 + (reg << 3));
#endif
    gen_expr32(&op->e);
#ifdef TCC_TARGET_X86_64
    } else if (op->reg == -2) {
        ExprValue *pe = &op->e;
        g(0x05 + (reg << 3));
        gen_addrpc32(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
        return ind;
#endif
    } else {
        sib_reg1 = op->reg;
        /* fist compute displacement encoding */
        if (sib_reg1 == -1) {
            sib_reg1 = 5;
            mod = 0x00;
        } else if (op->e.v == 0 && !op->e.sym && op->reg != 5) {
            mod = 0x00;
        } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) {
            mod = 0x40;
        } else {
            mod = 0x80;
        }
        /* compute if sib byte needed */
        reg1 = op->reg;
        if (op->reg2 != -1)
            reg1 = 4;
        g(mod + (reg << 3) + reg1);
        if (reg1 == 4) {
            /* add sib byte */
            reg2 = op->reg2;
            if (reg2 == -1)
                reg2 = 4; /* indicate no index */
            g((op->shift << 6) + (reg2 << 3) + sib_reg1);
        }
        /* add offset */
        if (mod == 0x40) {
            g(op->e.v);
        } else if (mod == 0x80 || op->reg == -1) {
        gen_expr32(&op->e);
        }
    }
    return 0;
}

#ifdef TCC_TARGET_X86_64
#define REX_W 0x48
#define REX_R 0x44
#define REX_X 0x42
#define REX_B 0x41

static void asm_rex(int width64, Operand *ops, int nb_ops, int *op_type,
            int regi, int rmi)
{
  unsigned char rex = width64 ? 0x48 : 0;
  int saw_high_8bit = 0;
  int i;
  if (rmi == -1) {
      /* No mod/rm byte, but we might have a register op nevertheless
         (we will add it to the opcode later).  */
      for(i = 0; i < nb_ops; i++) {
      if (op_type[i] & (OP_REG | OP_ST)) {
          if (ops[i].reg >= 8) {
          rex |= REX_B;
          ops[i].reg -= 8;
          } else if (ops[i].type & OP_REG8_LOW)
          rex |= 0x40;
          else if (ops[i].type & OP_REG8 && ops[i].reg >= 4)
          /* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */
          saw_high_8bit = ops[i].reg;
          break;
      }
      }
  } else {
      if (regi != -1) {
      if (ops[regi].reg >= 8) {
          rex |= REX_R;
          ops[regi].reg -= 8;
      } else if (ops[regi].type & OP_REG8_LOW)
          rex |= 0x40;
      else if (ops[regi].type & OP_REG8 && ops[regi].reg >= 4)
          /* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */
          saw_high_8bit = ops[regi].reg;
      }
      if (ops[rmi].type & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_EA)) {
      if (ops[rmi].reg >= 8) {
          rex |= REX_B;
          ops[rmi].reg -= 8;
      } else if (ops[rmi].type & OP_REG8_LOW)
          rex |= 0x40;
      else if (ops[rmi].type & OP_REG8 && ops[rmi].reg >= 4)
          /* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */
          saw_high_8bit = ops[rmi].reg;
      }
      if (ops[rmi].type & OP_EA && ops[rmi].reg2 >= 8) {
      rex |= REX_X;
      ops[rmi].reg2 -= 8;
      }
  }
  if (rex) {
      if (saw_high_8bit)
      tcc_error("can't encode register %%%ch when REX prefix is required",
            "acdb"[saw_high_8bit-4]);
      g(rex);
  }
}
#endif

static void maybe_print_stats (void)
{
  static int already = 1;
  if (!already)
    /* print stats about opcodes */
    {
        const struct ASMInstr *pa;
        int freq[4];
        int op_vals[500];
        int nb_op_vals, i, j;

    already = 1;
        nb_op_vals = 0;
        memset(freq, 0, sizeof(freq));
        for(pa = asm_instrs; pa->sym != 0; pa++) {
            freq[pa->nb_ops]++;
            //for(i=0;i<pa->nb_ops;i++) {
                for(j=0;j<nb_op_vals;j++) {
                    //if (pa->op_type[i] == op_vals[j])
                    if (pa->instr_type == op_vals[j])
                        goto found;
                }
                //op_vals[nb_op_vals++] = pa->op_type[i];
                op_vals[nb_op_vals++] = pa->instr_type;
            found: ;
            //}
        }
        for(i=0;i<nb_op_vals;i++) {
            int v = op_vals[i];
            //if ((v & (v - 1)) != 0)
                printf("%3d: %08x\n", i, v);
        }
        printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
               (int)sizeof(asm_instrs),
           (int)sizeof(asm_instrs) / (int)sizeof(ASMInstr),
               freq[0], freq[1], freq[2], freq[3]);
    }
}

ST_FUNC void asm_opcode(TCCState *s1, int opcode)
{
    const ASMInstr *pa;
    int i, modrm_index, modreg_index, reg, v, op1, seg_prefix, pc;
    int nb_ops, s;
    Operand ops[MAX_OPERANDS], *pop;
    int op_type[3]; /* decoded op type */
    int alltypes;   /* OR of all operand types */
    int autosize;
    int p66;
#ifdef TCC_TARGET_X86_64
    int rex64;
#endif

    maybe_print_stats();
    /* force synthetic ';' after prefix instruction, so we can handle */
    /* one-line things like "rep stosb" instead of only "rep\nstosb" */
    if (opcode >= TOK_ASM_wait && opcode <= TOK_ASM_repnz)
        unget_tok(';');

    /* get operands */
    pop = ops;
    nb_ops = 0;
    seg_prefix = 0;
    alltypes = 0;
    for(;;) {
        if (tok == ';' || tok == TOK_LINEFEED)
            break;
        if (nb_ops >= MAX_OPERANDS) {
            tcc_error("incorrect number of operands");
        }
        parse_operand(s1, pop);
        if (tok == ':') {
           if (pop->type != OP_SEG || seg_prefix)
               tcc_error("incorrect prefix");
           seg_prefix = segment_prefixes[pop->reg];
           next();
           parse_operand(s1, pop);
           if (!(pop->type & OP_EA)) {
               tcc_error("segment prefix must be followed by memory reference");
           }
        }
        pop++;
        nb_ops++;
        if (tok != ',')
            break;
        next();
    }

    s = 0; /* avoid warning */

    /* optimize matching by using a lookup table (no hashing is needed
       !) */
    for(pa = asm_instrs; pa->sym != 0; pa++) {
    int it = pa->instr_type & OPCT_MASK;
        s = 0;
        if (it == OPC_FARITH) {
            v = opcode - pa->sym;
            if (!((unsigned)v < 8 * 6 && (v % 6) == 0))
                continue;
        } else if (it == OPC_ARITH) {
            if (!(opcode >= pa->sym && opcode < pa->sym + 8*NBWLX))
                continue;
            s = (opcode - pa->sym) % NBWLX;
        if ((pa->instr_type & OPC_BWLX) == OPC_WLX)
          {
        /* We need to reject the xxxb opcodes that we accepted above.
           Note that pa->sym for WLX opcodes is the 'w' token,
           to get the 'b' token subtract one.  */
        if (((opcode - pa->sym + 1) % NBWLX) == 0)
            continue;
            s++;
          }
        } else if (it == OPC_SHIFT) {
            if (!(opcode >= pa->sym && opcode < pa->sym + 7*NBWLX))
                continue;
            s = (opcode - pa->sym) % NBWLX;
        } else if (it == OPC_TEST) {
            if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES))
                continue;
        /* cmovxx is a test opcode but accepts multiple sizes.
           TCC doesn't accept the suffixed mnemonic, instead we 
           simply force size autodetection always.  */
        if (pa->instr_type & OPC_WLX)
            s = NBWLX - 1;
        } else if (pa->instr_type & OPC_B) {
#ifdef TCC_TARGET_X86_64
        /* Some instructions don't have the full size but only
           bwl form.  insb e.g. */
        if ((pa->instr_type & OPC_WLQ) != OPC_WLQ
        && !(opcode >= pa->sym && opcode < pa->sym + NBWLX-1))
            continue;
#endif
            if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX))
                continue;
            s = opcode - pa->sym;
        } else if (pa->instr_type & OPC_WLX) {
            if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX-1))
                continue;
            s = opcode - pa->sym + 1;
        } else {
            if (pa->sym != opcode)
                continue;
        }
        if (pa->nb_ops != nb_ops)
            continue;
#ifdef TCC_TARGET_X86_64
    /* Special case for moves.  Selecting the IM64->REG64 form
       should only be done if we really have an >32bit imm64, and that
       is hardcoded.  Ignore it here.  */
    if (pa->opcode == 0xb0 && ops[0].type != OP_IM64
        && (ops[1].type & OP_REG) == OP_REG64
        && !(pa->instr_type & OPC_0F))
        continue;
#endif
        /* now decode and check each operand */
    alltypes = 0;
        for(i = 0; i < nb_ops; i++) {
            int op1, op2;
            op1 = pa->op_type[i];
            op2 = op1 & 0x1f;
            switch(op2) {
            case OPT_IM:
                v = OP_IM8 | OP_IM16 | OP_IM32;
                break;
            case OPT_REG:
                v = OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64;
                break;
            case OPT_REGW:
                v = OP_REG16 | OP_REG32 | OP_REG64;
                break;
            case OPT_IMW:
                v = OP_IM16 | OP_IM32;
                break;
        case OPT_MMXSSE:
        v = OP_MMX | OP_SSE;
        break;
        case OPT_DISP:
        case OPT_DISP8:
        v = OP_ADDR;
        break;
            default:
                v = 1 << op2;
                break;
            }
            if (op1 & OPT_EA)
                v |= OP_EA;
        op_type[i] = v;
            if ((ops[i].type & v) == 0)
                goto next;
        alltypes |= ops[i].type;
        }
        /* all is matching ! */
        break;
    next: ;
    }
    if (pa->sym == 0) {
        if (opcode >= TOK_ASM_first && opcode <= TOK_ASM_last) {
            int b;
            b = op0_codes[opcode - TOK_ASM_first];
            if (b & 0xff00) 
                g(b >> 8);
            g(b);
            return;
        } else if (opcode <= TOK_ASM_alllast) {
            tcc_error("bad operand with opcode '%s'",
                  get_tok_str(opcode, NULL));
        } else {
            tcc_error("unknown opcode '%s'",
                  get_tok_str(opcode, NULL));
        }
    }
    /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */
    autosize = NBWLX-1;
#ifdef TCC_TARGET_X86_64
    /* XXX the autosize should rather be zero, to not have to adjust this
       all the time.  */
    if ((pa->instr_type & OPC_BWLQ) == OPC_B)
        autosize = NBWLX-2;
#endif
    if (s == autosize) {
    /* Check for register operands providing hints about the size.
       Start from the end, i.e. destination operands.  This matters
       only for opcodes accepting different sized registers, lar and lsl
       are such opcodes.  */
        for(i = nb_ops - 1; s == autosize && i >= 0; i--) {
            if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX)))
                s = reg_to_size[ops[i].type & OP_REG];
        }
        if (s == autosize) {
            if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) &&
                (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32)))
                s = 2;
        else if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) &&
             (ops[0].type & OP_EA))
            s = NBWLX - 2;
            else
                tcc_error("cannot infer opcode suffix");
        }
    }

#ifdef TCC_TARGET_X86_64
    /* Generate addr32 prefix if needed */
    for(i = 0; i < nb_ops; i++) {
        if (ops[i].type & OP_EA32) {
        g(0x67);
        break;
        }
    }
#endif
    /* generate data16 prefix if needed */
    p66 = 0;
    if (s == 1)
        p66 = 1;
    else {
    /* accepting mmx+sse in all operands --> needs 0x66 to
       switch to sse mode.  Accepting only sse in an operand --> is
       already SSE insn and needs 0x66/f2/f3 handling.  */
        for (i = 0; i < nb_ops; i++)
            if ((op_type[i] & (OP_MMX | OP_SSE)) == (OP_MMX | OP_SSE)
            && ops[i].type & OP_SSE)
            p66 = 1;
    }
    if (p66)
        g(0x66);
#ifdef TCC_TARGET_X86_64
    rex64 = 0;
    if (pa->instr_type & OPC_48)
        rex64 = 1;
    else if (s == 3 || (alltypes & OP_REG64)) {
        /* generate REX prefix */
    int default64 = 0;
    for(i = 0; i < nb_ops; i++) {
        if (op_type[i] == OP_REG64 && pa->opcode != 0xb8) {
        /* If only 64bit regs are accepted in one operand
           this is a default64 instruction without need for
           REX prefixes, except for movabs(0xb8).  */
        default64 = 1;
        break;
        }
    }
    /* XXX find better encoding for the default64 instructions.  */
        if (((opcode != TOK_ASM_push && opcode != TOK_ASM_pop
          && opcode != TOK_ASM_pushw && opcode != TOK_ASM_pushl
          && opcode != TOK_ASM_pushq && opcode != TOK_ASM_popw
          && opcode != TOK_ASM_popl && opcode != TOK_ASM_popq
          && opcode != TOK_ASM_call && opcode != TOK_ASM_jmp))
        && !default64)
            rex64 = 1;
    }
#endif

    /* now generates the operation */
    if (OPCT_IS(pa->instr_type, OPC_FWAIT))
        g(0x9b);
    if (seg_prefix)
        g(seg_prefix);

    v = pa->opcode;
    if (pa->instr_type & OPC_0F)
        v = ((v & ~0xff) << 8) | 0x0f00 | (v & 0xff);
    if ((v == 0x69 || v == 0x6b) && nb_ops == 2) {
        /* kludge for imul $im, %reg */
        nb_ops = 3;
        ops[2] = ops[1];
        op_type[2] = op_type[1];
    } else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) {
        v--; /* int $3 case */
        nb_ops = 0;
    } else if ((v == 0x06 || v == 0x07)) {
        if (ops[0].reg >= 4) {
            /* push/pop %fs or %gs */
            v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3);
        } else {
            v += ops[0].reg << 3;
        }
        nb_ops = 0;
    } else if (v <= 0x05) {
        /* arith case */
        v += ((opcode - TOK_ASM_addb) / NBWLX) << 3;
    } else if ((pa->instr_type & (OPCT_MASK | OPC_MODRM)) == OPC_FARITH) {
        /* fpu arith case */
        v += ((opcode - pa->sym) / 6) << 3;
    }

    /* search which operand will be used for modrm */
    modrm_index = -1;
    modreg_index = -1;
    if (pa->instr_type & OPC_MODRM) {
    if (!nb_ops) {
        /* A modrm opcode without operands is a special case (e.g. mfence).
           It has a group and acts as if there's an register operand 0
           (ax).  */
        i = 0;
        ops[i].type = OP_REG;
        ops[i].reg = 0;
        goto modrm_found;
    }
        /* first look for an ea operand */
        for(i = 0;i < nb_ops; i++) {
            if (op_type[i] & OP_EA)
                goto modrm_found;
        }
        /* then if not found, a register or indirection (shift instructions) */
        for(i = 0;i < nb_ops; i++) {
            if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR))
                goto modrm_found;
        }
#ifdef ASM_DEBUG
        tcc_error("bad op table");
#endif
    modrm_found:
        modrm_index = i;
        /* if a register is used in another operand then it is
           used instead of group */
        for(i = 0;i < nb_ops; i++) {
            int t = op_type[i];
            if (i != modrm_index &&
                (t & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) {
                modreg_index = i;
                break;
            }
        }
    }
#ifdef TCC_TARGET_X86_64
    asm_rex (rex64, ops, nb_ops, op_type, modreg_index, modrm_index);
#endif

    if (pa->instr_type & OPC_REG) {
        /* mov $im, %reg case */
        if (v == 0xb0 && s >= 1)
            v += 7;
        for(i = 0; i < nb_ops; i++) {
            if (op_type[i] & (OP_REG | OP_ST)) {
                v += ops[i].reg;
                break;
            }
        }
    }
    if (pa->instr_type & OPC_B)
        v += s >= 1;
    if (nb_ops == 1 && pa->op_type[0] == OPT_DISP8) {
        Sym *sym;
        int jmp_disp;

        /* see if we can really generate the jump with a byte offset */
        sym = ops[0].e.sym;
        if (!sym)
            goto no_short_jump;
        if (sym->r != cur_text_section->sh_num)
            goto no_short_jump;
        jmp_disp = ops[0].e.v + sym->jnext - ind - 2 - (v >= 0xff);
        if (jmp_disp == (int8_t)jmp_disp) {
            /* OK to generate jump */
        ops[0].e.sym = 0;
            ops[0].e.v = jmp_disp;
        op_type[0] = OP_IM8S;
        } else {
        no_short_jump:
        /* long jump will be allowed. need to modify the
           opcode slightly */
        if (v == 0xeb) /* jmp */
            v = 0xe9;
        else if (v == 0x70) /* jcc */
            v += 0x0f10;
        else
            tcc_error("invalid displacement");
        }
    }
    if (OPCT_IS(pa->instr_type, OPC_TEST))
        v += test_bits[opcode - pa->sym];
    op1 = v >> 16;
    if (op1)
        g(op1);
    op1 = (v >> 8) & 0xff;
    if (op1)
        g(op1);
    g(v);

    if (OPCT_IS(pa->instr_type, OPC_SHIFT)) {
        reg = (opcode - pa->sym) / NBWLX;
        if (reg == 6)
            reg = 7;
    } else if (OPCT_IS(pa->instr_type, OPC_ARITH)) {
        reg = (opcode - pa->sym) / NBWLX;
    } else if (OPCT_IS(pa->instr_type, OPC_FARITH)) {
        reg = (opcode - pa->sym) / 6;
    } else {
        reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7;
    }

    pc = 0;
    if (pa->instr_type & OPC_MODRM) {
        /* if a register is used in another operand then it is
           used instead of group */
    if (modreg_index >= 0)
        reg = ops[modreg_index].reg;
        pc = asm_modrm(reg, &ops[modrm_index]);
    }

    /* emit constants */
#ifndef TCC_TARGET_X86_64
    if (!(pa->instr_type & OPC_0F)
    && (pa->opcode == 0x9a || pa->opcode == 0xea)) {
        /* ljmp or lcall kludge */
    gen_expr32(&ops[1].e);
        if (ops[0].e.sym)
            tcc_error("cannot relocate");
        gen_le16(ops[0].e.v);
        return;
    }
#endif
    for(i = 0;i < nb_ops; i++) {
        v = op_type[i];
        if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64 | OP_IM8S | OP_ADDR)) {
            /* if multiple sizes are given it means we must look
               at the op size */
            if ((v | OP_IM8 | OP_IM64) == (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64)) {
                if (s == 0)
                    v = OP_IM8;
                else if (s == 1)
                    v = OP_IM16;
                else if (s == 2 || (v & OP_IM64) == 0)
                    v = OP_IM32;
                else
                    v = OP_IM64;
            }

            if ((v & (OP_IM8 | OP_IM8S | OP_IM16)) && ops[i].e.sym)
                tcc_error("cannot relocate");

            if (v & (OP_IM8 | OP_IM8S)) {
                g(ops[i].e.v);
            } else if (v & OP_IM16) {
                gen_le16(ops[i].e.v);
#ifdef TCC_TARGET_X86_64
            } else if (v & OP_IM64) {
                gen_expr64(&ops[i].e);
#endif
        } else if (pa->op_type[i] == OPT_DISP || pa->op_type[i] == OPT_DISP8) {
                gen_disp32(&ops[i].e);
            } else {
                gen_expr32(&ops[i].e);
            }
        }
    }

    /* after immediate operands, adjust pc-relative address */
    if (pc)
        add32le(cur_text_section->data + pc - 4, pc - ind);
}

/* return the constraint priority (we allocate first the lowest
   numbered constraints) */
static inline int constraint_priority(const char *str)
{
    int priority, c, pr;

    /* we take the lowest priority */
    priority = 0;
    for(;;) {
        c = *str;
        if (c == '\0')
            break;
        str++;
        switch(c) {
        case 'A':
            pr = 0;
            break;
        case 'a':
        case 'b':
        case 'c':
        case 'd':
        case 'S':
        case 'D':
            pr = 1;
            break;
        case 'q':
            pr = 2;
            break;
        case 'r':
    case 'R':
    case 'p':
            pr = 3;
            break;
        case 'N':
        case 'M':
        case 'I':
    case 'e':
        case 'i':
        case 'm':
        case 'g':
            pr = 4;
            break;
        default:
            tcc_error("unknown constraint '%c'", c);
            pr = 0;
        }
        if (pr > priority)
            priority = pr;
    }
    return priority;
}

static const char *skip_constraint_modifiers(const char *p)
{
    while (*p == '=' || *p == '&' || *p == '+' || *p == '%')
        p++;
    return p;
}

/* If T (a token) is of the form "%reg" returns the register
   number and type, otherwise return -1.  */
ST_FUNC int asm_parse_regvar (int t)
{
    const char *s;
    Operand op;
    if (t < TOK_IDENT)
        return -1;
    s = table_ident[t - TOK_IDENT]->str;
    if (s[0] != '%')
        return -1;
    t = tok_alloc(s+1, strlen(s)-1)->tok;
    unget_tok(t);
    unget_tok('%');
    parse_operand(tcc_state, &op);
    /* Accept only integer regs for now.  */
    if (op.type & OP_REG)
        return op.reg;
    else
        return -1;
}

#define REG_OUT_MASK 0x01
#define REG_IN_MASK  0x02

#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)

ST_FUNC void asm_compute_constraints(ASMOperand *operands,
                                    int nb_operands, int nb_outputs,
                                    const uint8_t *clobber_regs,
                                    int *pout_reg)
{
    ASMOperand *op;
    int sorted_op[MAX_ASM_OPERANDS];
    int i, j, k, p1, p2, tmp, reg, c, reg_mask;
    const char *str;
    uint8_t regs_allocated[NB_ASM_REGS];

    /* init fields */
    for(i=0;i<nb_operands;i++) {
        op = &operands[i];
        op->input_index = -1;
        op->ref_index = -1;
        op->reg = -1;
        op->is_memory = 0;
        op->is_rw = 0;
    }
    /* compute constraint priority and evaluate references to output
       constraints if input constraints */
    for(i=0;i<nb_operands;i++) {
        op = &operands[i];
        str = op->constraint;
        str = skip_constraint_modifiers(str);
        if (isnum(*str) || *str == '[') {
            /* this is a reference to another constraint */
            k = find_constraint(operands, nb_operands, str, NULL);
            if ((unsigned)k >= i || i < nb_outputs)
                tcc_error("invalid reference in constraint %d ('%s')",
                      i, str);
            op->ref_index = k;
            if (operands[k].input_index >= 0)
                tcc_error("cannot reference twice the same operand");
            operands[k].input_index = i;
            op->priority = 5;
    } else if ((op->vt->r & VT_VALMASK) == VT_LOCAL
           && op->vt->sym
           && (reg = op->vt->sym->r & VT_VALMASK) < VT_CONST) {
        op->priority = 1;
        op->reg = reg;
        } else {
            op->priority = constraint_priority(str);
        }
    }

    /* sort operands according to their priority */
    for(i=0;i<nb_operands;i++)
        sorted_op[i] = i;
    for(i=0;i<nb_operands - 1;i++) {
        for(j=i+1;j<nb_operands;j++) {
            p1 = operands[sorted_op[i]].priority;
            p2 = operands[sorted_op[j]].priority;
            if (p2 < p1) {
                tmp = sorted_op[i];
                sorted_op[i] = sorted_op[j];
                sorted_op[j] = tmp;
            }
        }
    }

    for(i = 0;i < NB_ASM_REGS; i++) {
        if (clobber_regs[i])
            regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK;
        else
            regs_allocated[i] = 0;
    }
    /* esp cannot be used */
    regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK;
    /* ebp cannot be used yet */
    regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK;

    /* allocate registers and generate corresponding asm moves */
    for(i=0;i<nb_operands;i++) {
        j = sorted_op[i];
        op = &operands[j];
        str = op->constraint;
        /* no need to allocate references */
        if (op->ref_index >= 0)
            continue;
        /* select if register is used for output, input or both */
        if (op->input_index >= 0) {
            reg_mask = REG_IN_MASK | REG_OUT_MASK;
        } else if (j < nb_outputs) {
            reg_mask = REG_OUT_MASK;
        } else {
            reg_mask = REG_IN_MASK;
        }
    if (op->reg >= 0) {
        if (is_reg_allocated(op->reg))
            tcc_error("asm regvar requests register that's taken already");
        reg = op->reg;
        goto reg_found;
    }
    try_next:
        c = *str++;
        switch(c) {
        case '=':
            goto try_next;
        case '+':
            op->is_rw = 1;
            /* FALL THRU */
        case '&':
            if (j >= nb_outputs)
                tcc_error("'%c' modifier can only be applied to outputs", c);
            reg_mask = REG_IN_MASK | REG_OUT_MASK;
            goto try_next;
        case 'A':
            /* allocate both eax and edx */
            if (is_reg_allocated(TREG_XAX) ||
                is_reg_allocated(TREG_XDX))
                goto try_next;
            op->is_llong = 1;
            op->reg = TREG_XAX;
            regs_allocated[TREG_XAX] |= reg_mask;
            regs_allocated[TREG_XDX] |= reg_mask;
            break;
        case 'a':
            reg = TREG_XAX;
            goto alloc_reg;
        case 'b':
            reg = 3;
            goto alloc_reg;
        case 'c':
            reg = TREG_XCX;
            goto alloc_reg;
        case 'd':
            reg = TREG_XDX;
            goto alloc_reg;
        case 'S':
            reg = 6;
            goto alloc_reg;
        case 'D':
            reg = 7;
        alloc_reg:
            if (is_reg_allocated(reg))
                goto try_next;
            goto reg_found;
        case 'q':
            /* eax, ebx, ecx or edx */
            for(reg = 0; reg < 4; reg++) {
                if (!is_reg_allocated(reg))
                    goto reg_found;
            }
            goto try_next;
        case 'r':
    case 'R':
    case 'p': /* A general address, for x86(64) any register is acceptable*/
            /* any general register */
            for(reg = 0; reg < 8; reg++) {
                if (!is_reg_allocated(reg))
                    goto reg_found;
            }
            goto try_next;
        reg_found:
            /* now we can reload in the register */
            op->is_llong = 0;
            op->reg = reg;
            regs_allocated[reg] |= reg_mask;
            break;
    case 'e':
        case 'i':
            if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST))
                goto try_next;
            break;
        case 'I':
        case 'N':
        case 'M':
            if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST))
                goto try_next;
            break;
        case 'm':
        case 'g':
            /* nothing special to do because the operand is already in
               memory, except if the pointer itself is stored in a
               memory variable (VT_LLOCAL case) */
            /* XXX: fix constant case */
            /* if it is a reference to a memory zone, it must lie
               in a register, so we reserve the register in the
               input registers and a load will be generated
               later */
            if (j < nb_outputs || c == 'm') {
                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
                    /* any general register */
                    for(reg = 0; reg < 8; reg++) {
                        if (!(regs_allocated[reg] & REG_IN_MASK))
                            goto reg_found1;
                    }
                    goto try_next;
                reg_found1:
                    /* now we can reload in the register */
                    regs_allocated[reg] |= REG_IN_MASK;
                    op->reg = reg;
                    op->is_memory = 1;
                }
            }
            break;
        default:
            tcc_error("asm constraint %d ('%s') could not be satisfied",
                  j, op->constraint);
            break;
        }
        /* if a reference is present for that operand, we assign it too */
        if (op->input_index >= 0) {
            operands[op->input_index].reg = op->reg;
            operands[op->input_index].is_llong = op->is_llong;
        }
    }

    /* compute out_reg. It is used to store outputs registers to memory
       locations references by pointers (VT_LLOCAL case) */
    *pout_reg = -1;
    for(i=0;i<nb_operands;i++) {
        op = &operands[i];
        if (op->reg >= 0 &&
            (op->vt->r & VT_VALMASK) == VT_LLOCAL  &&
            !op->is_memory) {
            for(reg = 0; reg < 8; reg++) {
                if (!(regs_allocated[reg] & REG_OUT_MASK))
                    goto reg_found2;
            }
            tcc_error("could not find free output register for reloading");
        reg_found2:
            *pout_reg = reg;
            break;
        }
    }

    /* print sorted constraints */
#ifdef ASM_DEBUG
    for(i=0;i<nb_operands;i++) {
        j = sorted_op[i];
        op = &operands[j];
        printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
               j,
               op->id ? get_tok_str(op->id, NULL) : "",
               op->constraint,
               op->vt->r,
               op->reg);
    }
    if (*pout_reg >= 0)
        printf("out_reg=%d\n", *pout_reg);
#endif
}

ST_FUNC void subst_asm_operand(CString *add_str,
                              SValue *sv, int modifier)
{
    int r, reg, size, val;
    char buf[64];

    r = sv->r;
    if ((r & VT_VALMASK) == VT_CONST) {
        if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n' &&
        modifier != 'P')
            cstr_ccat(add_str, '$');
        if (r & VT_SYM) {
        const char *name = get_tok_str(sv->sym->v, NULL);
        if (sv->sym->v >= SYM_FIRST_ANOM) {
        /* In case of anonymous symbols ("L.42", used
           for static data labels) we can't find them
           in the C symbol table when later looking up
           this name.  So enter them now into the asm label
           list when we still know the symbol.  */
        get_asm_sym(tok_alloc(name, strlen(name))->tok, sv->sym);
        }
            cstr_cat(add_str, name, -1);
            if ((uint32_t)sv->c.i == 0)
                goto no_offset;
        cstr_ccat(add_str, '+');
        }
        val = sv->c.i;
        if (modifier == 'n')
            val = -val;
        snprintf(buf, sizeof(buf), "%d", (int)sv->c.i);
        cstr_cat(add_str, buf, -1);
    no_offset:;
#ifdef TCC_TARGET_X86_64
        if (r & VT_LVAL)
            cstr_cat(add_str, "(%rip)", -1);
#endif
    } else if ((r & VT_VALMASK) == VT_LOCAL) {
#ifdef TCC_TARGET_X86_64
        snprintf(buf, sizeof(buf), "%d(%%rbp)", (int)sv->c.i);
#else
        snprintf(buf, sizeof(buf), "%d(%%ebp)", (int)sv->c.i);
#endif
        cstr_cat(add_str, buf, -1);
    } else if (r & VT_LVAL) {
        reg = r & VT_VALMASK;
        if (reg >= VT_CONST)
            tcc_error("internal compiler error");
        snprintf(buf, sizeof(buf), "(%%%s)",
#ifdef TCC_TARGET_X86_64
                 get_tok_str(TOK_ASM_rax + reg, NULL)
#else
                 get_tok_str(TOK_ASM_eax + reg, NULL)
#endif
         );
        cstr_cat(add_str, buf, -1);
    } else {
        /* register case */
        reg = r & VT_VALMASK;
        if (reg >= VT_CONST)
            tcc_error("internal compiler error");

        /* choose register operand size */
        if ((sv->type.t & VT_BTYPE) == VT_BYTE ||
        (sv->type.t & VT_BTYPE) == VT_BOOL)
            size = 1;
        else if ((sv->type.t & VT_BTYPE) == VT_SHORT)
            size = 2;
#ifdef TCC_TARGET_X86_64
        else if ((sv->type.t & VT_BTYPE) == VT_LLONG ||
         (sv->type.t & VT_BTYPE) == VT_PTR)
            size = 8;
#endif
        else
            size = 4;
        if (size == 1 && reg >= 4)
            size = 4;

        if (modifier == 'b') {
            if (reg >= 4)
                tcc_error("cannot use byte register");
            size = 1;
        } else if (modifier == 'h') {
            if (reg >= 4)
                tcc_error("cannot use byte register");
            size = -1;
        } else if (modifier == 'w') {
            size = 2;
        } else if (modifier == 'k') {
            size = 4;
#ifdef TCC_TARGET_X86_64
        } else if (modifier == 'q') {
            size = 8;
#endif
        }

        switch(size) {
        case -1:
            reg = TOK_ASM_ah + reg;
            break;
        case 1:
            reg = TOK_ASM_al + reg;
            break;
        case 2:
            reg = TOK_ASM_ax + reg;
            break;
        default:
            reg = TOK_ASM_eax + reg;
            break;
#ifdef TCC_TARGET_X86_64
        case 8:
            reg = TOK_ASM_rax + reg;
            break;
#endif
        }
        snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL));
        cstr_cat(add_str, buf, -1);
    }
}

/* generate prolog and epilog code for asm statement */
ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
                         int nb_outputs, int is_output,
                         uint8_t *clobber_regs,
                         int out_reg)
{
    uint8_t regs_allocated[NB_ASM_REGS];
    ASMOperand *op;
    int i, reg;

    /* Strictly speaking %Xbp and %Xsp should be included in the
       call-preserved registers, but currently it doesn't matter.  */
#ifdef TCC_TARGET_X86_64
#ifdef TCC_TARGET_PE
    static uint8_t reg_saved[] = { 3, 6, 7, 12, 13, 14, 15 };
#else
    static uint8_t reg_saved[] = { 3, 12, 13, 14, 15 };
#endif
#else
    static uint8_t reg_saved[] = { 3, 6, 7 };
#endif

    /* mark all used registers */
    memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated));
    for(i = 0; i < nb_operands;i++) {
        op = &operands[i];
        if (op->reg >= 0)
            regs_allocated[op->reg] = 1;
    }
    if (!is_output) {
        /* generate reg save code */
        for(i = 0; i < sizeof(reg_saved)/sizeof(reg_saved[0]); i++) {
            reg = reg_saved[i];
            if (regs_allocated[reg]) {
        if (reg >= 8)
          g(0x41), reg-=8;
                g(0x50 + reg);
            }
        }

        /* generate load code */
        for(i = 0; i < nb_operands; i++) {
            op = &operands[i];
            if (op->reg >= 0) {
                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL &&
                    op->is_memory) {
                    /* memory reference case (for both input and
                       output cases) */
                    SValue sv;
                    sv = *op->vt;
                    sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL | VT_LVAL;
                    sv.type.t = VT_PTR;
                    load(op->reg, &sv);
                } else if (i >= nb_outputs || op->is_rw) {
                    /* load value in register */
                    load(op->reg, op->vt);
                    if (op->is_llong) {
                        SValue sv;
                        sv = *op->vt;
                        sv.c.i += 4;
                        load(TREG_XDX, &sv);
                    }
                }
            }
        }
    } else {
        /* generate save code */
        for(i = 0 ; i < nb_outputs; i++) {
            op = &operands[i];
            if (op->reg >= 0) {
                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
                    if (!op->is_memory) {
                        SValue sv;
                        sv = *op->vt;
                        sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
            sv.type.t = VT_PTR;
                        load(out_reg, &sv);

            sv = *op->vt;
                        sv.r = (sv.r & ~VT_VALMASK) | out_reg;
                        store(op->reg, &sv);
                    }
                } else {
                    store(op->reg, op->vt);
                    if (op->is_llong) {
                        SValue sv;
                        sv = *op->vt;
                        sv.c.i += 4;
                        store(TREG_XDX, &sv);
                    }
                }
            }
        }
        /* generate reg restore code */
        for(i = sizeof(reg_saved)/sizeof(reg_saved[0]) - 1; i >= 0; i--) {
            reg = reg_saved[i];
            if (regs_allocated[reg]) {
        if (reg >= 8)
          g(0x41), reg-=8;
                g(0x58 + reg);
            }
        }
    }
}

ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str)
{
    int reg;
    TokenSym *ts;
#ifdef TCC_TARGET_X86_64
    unsigned int type;
#endif

    if (!strcmp(str, "memory") ||
        !strcmp(str, "cc") ||
    !strcmp(str, "flags"))
        return;
    ts = tok_alloc(str, strlen(str));
    reg = ts->tok;
    if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) {
        reg -= TOK_ASM_eax;
    } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) {
        reg -= TOK_ASM_ax;
#ifdef TCC_TARGET_X86_64
    } else if (reg >= TOK_ASM_rax && reg <= TOK_ASM_rdi) {
        reg -= TOK_ASM_rax;
    } else if ((reg = asm_parse_numeric_reg(reg, &type)) >= 0) {
    ;
#endif
    } else {
        tcc_error("invalid clobber register '%s'", str);
    }
    clobber_regs[reg] = 1;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isalnum.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>

int
isalnum (int c)
{
  return isdigit (c) || isalpha (c);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isalpha.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>

int
isalpha (int c)
{
  return islower (c) || isupper (c);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isascii.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>

int
isascii (int c)
{
  return c >= 0 && c <= 127;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/iscntrl.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>

int
iscntrl (int c)
{
  return c >= 0 && c < 32;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isgraph.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>

int
isgraph (int c)
{
  return c > 32 && c < 127;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/isprint.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>

int
isprint (int c)
{
  return c >= 32 && c < 127;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/ctype/ispunct.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <ctype.h>

int
ispunct (int c)
{
  return isprint (c) && !isspace (c) && !isalnum (c);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/dirent/closedir.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright (C) 1991, 1993, 1995, 1996, 1998 Free Software Foundation, Inc.
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

// Taken from GNU C Library 2.2.5

#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>

#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <dirent.h>
#include <unistd.h>
#include <dirstream.h>

/* Close the directory stream DIRP.
   Return 0 if successful, -1 if not.  */
int
closedir (DIR * dirp)
{
  int filedes;

  if (dirp == NULL)
    {
      errno = EINVAL;
      return -1;
    }

  filedes = dirp->fd;
  free (dirp);

  return close (filedes);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/dirent/__getdirentries.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright (C) 1993 Free Software Foundation, Inc.
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

// Taken from GNU C Library 1.06.4

#include <mes/lib.h>
#include <dirent.h>
#include <stdio.h>
#include <unistd.h>

int
__getdirentries (int filedes, char *buffer, size_t nbytes, off_t * basep)
{
  if (basep)
    *basep = lseek (filedes, (off_t) 0, SEEK_CUR);

  return read (filedes, buffer, nbytes);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/dirent/opendir.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright (C) 1991-1996,98,2000,2001 Free Software Foundation, Inc.
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

// Taken from GNU C Library 2.2.5

#include <mes/lib.h>
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>

#include <dirstream.h>

/* Open a directory stream on NAME.  */
DIR *
opendir (char const *name)
{
  DIR *dirp;
  struct stat statbuf;
  int fd;
  size_t allocation;
  int save_errno;

  if (name[0] == '\0')
    {
      /* POSIX.1-1990 says an empty name gets ENOENT;
         but `open' might like it fine.  */
      errno = ENOENT;
      return 0;
    }

  fd = open (name, O_RDONLY | O_DIRECTORY);
  if (fd < 0)
    return 0;

  if (fstat (fd, &statbuf) < 0)
    goto lose;

  if (fcntl (fd, F_SETFD, FD_CLOEXEC) < 0)
    goto lose;

  allocation = statbuf.st_blksize;

  dirp = (DIR *) calloc (1, sizeof (DIR) + allocation);
  if (!dirp)
  lose:
    {
      save_errno = errno;
      close (fd);
      errno = save_errno;
      return 0;
    }
  dirp->data = (char *) (dirp + 1);
  dirp->allocation = allocation;
  dirp->fd = fd;

  return dirp;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/dirent/readdir.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright (C) 1991,92,93,94,95,96,97,99,2000 Free Software Foundation, Inc.
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

// Taken from GNU C Library 2.2.5

#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#include <assert.h>

#include <dirstream.h>

int getdents (int filedes, char *buffer, size_t nbytes);

/* Read a directory entry from DIRP.  */
struct dirent *
readdir (DIR * dirp)
{
  struct dirent *dp;
  int saved_errno = errno;

  do
    {
      size_t reclen;

      if (dirp->offset >= dirp->size)
        {
          /* We've emptied out our buffer.  Refill it.  */

          size_t maxread;
          ssize_t bytes;

          maxread = dirp->allocation;

#if 0
          off_t base;
          bytes = __getdirentries (dirp->fd, dirp->data, maxread, &base);
#else
          bytes = getdents (dirp->fd, dirp->data, maxread);
#endif
          if (bytes <= 0)
            {
              /* Don't modifiy errno when reaching EOF.  */
              if (bytes == 0)
                errno = saved_errno;
              dp = 0;
              break;
            }
          dirp->size = (size_t) bytes;

          /* Reset the offset into the buffer.  */
          dirp->offset = 0;
        }

      dp = (struct dirent *) &dirp->data[dirp->offset];

      reclen = dp->d_reclen;
      dirp->offset += reclen;
      dirp->filepos = dp->d_off;

      /* Skip deleted files.  */
    }
  while (dp->d_ino == 0);

  return dp;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/chdir.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>

int
chdir (char const *file_name)
{
  return _sys_call1 (SYS_chdir, (long) file_name);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fcntl.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <stdarg.h>

int
fcntl (int filedes, int command, ...)
{
  va_list ap;
  va_start (ap, command);
  int data = va_arg (ap, int);
  int r = _sys_call3 (SYS_fcntl, (int) filedes, (int) command, (int) data);
  va_end (ap);
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/fstat.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>

int
fstat (int filedes, struct stat *statbuf)
{
  return _sys_call2 (SYS_fstat, (int) filedes, (long) statbuf);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getdents.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/types.h>

int
getdents (int filedes, char *buffer, size_t nbytes)
{
  return _sys_call3 (SYS_getdents, (int) filedes, (long) buffer, (long) nbytes);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getegid.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>

gid_t
getegid ()
{
  return _sys_call (SYS_getegid);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/geteuid.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>

uid_t
geteuid ()
{
  return _sys_call (SYS_geteuid);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getgid.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>

gid_t
getgid ()
{
  return _sys_call (SYS_getgid);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getppid.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>

pid_t
getppid ()
{
  return _sys_call (SYS_getppid);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getrusage.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/resource.h>

int
getrusage (int processes, struct rusage *rusage)
{
  return _sys_call2 (SYS_getrusage, (int) processes, (long) rusage);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/getuid.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>

uid_t
getuid ()
{
  return _sys_call (SYS_getuid);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/ioctl.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <stdarg.h>
#include <sys/ioctl.h>

int
ioctl (int filedes, unsigned long command, ...)
{
  va_list ap;
  va_start (ap, command);
  int data = va_arg (ap, int);
  int r = _sys_call3 (SYS_ioctl, (int) filedes, (long) command, (int) data);
  va_end (ap);
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/link.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>

int
link (char const *old_name, char const *new_name)
{
  return _sys_call2 (SYS_link, (long) old_name, (long) new_name);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/lstat.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>

int
lstat (char const *file_name, struct stat *statbuf)
{
  return _sys_call2 (SYS_lstat, (long) file_name, (long) statbuf);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/mkdir.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>

int
mkdir (char const *file_name, mode_t mode)
{
  return _sys_call2 (SYS_mkdir, (long) file_name, (long) mode);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/mknod.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>

int
mknod (char const *file_name, mode_t mode, dev_t dev)
{
  return _sys_call3 (SYS_mknod, (long) file_name, (long) mode, (long) dev);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/nanosleep.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <time.h>
#include <sys/time.h>

int
nanosleep (struct timespec const *requested_time, struct timespec const *remaining)
{
  return _sys_call2 (SYS_nanosleep, (long) requested_time, (long) remaining);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/pipe.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>

int
pipe (int filedes[2])
{
  return _sys_call1 (SYS_pipe, (long) filedes);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/readlink.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/stat.h>

ssize_t
readlink (char const *file_name, char *buffer, size_t size)
{
  return _sys_call3 (SYS_readlink, (long) file_name, (long) buffer, (long) size);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/rename.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>

int
rename (char const *old_name, char const *new_name)
{
  return _sys_call2 (SYS_rename, (long) old_name, (long) new_name);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/setgid.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>

int
setgid (gid_t newgid)
{
  return _sys_call1 (SYS_setgid, (long) newgid);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/settimer.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <sys/time.h>
#include <unistd.h>

int
setitimer (int which, struct itimerval const *new, struct itimerval *old)
{
  return _sys_call3 (SYS_setitimer, (long) which, (long) new, (long) old);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/setuid.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>

int
setuid (uid_t newuid)
{
  return _sys_call1 (SYS_setuid, (long) newuid);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/signal.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022,2023 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>
#include <signal.h>

#if __i386__
#else
void
_restorer_for_siginfo (void)
{
  _sys_call (SYS_rt_sigreturn);
}
#endif

sighandler_t
signal (int signum, sighandler_t action)
{
#if __i386__
  return (sighandler_t) _sys_call2 (SYS_signal, signum, (long) action);
#else
  static struct sigaction setup_action = { 0 };
  static struct sigaction old = { 0 };
  unsigned short bitindex;
  unsigned short itembitcount;

  setup_action.sa_handler = action;
  /* The sa_restorer is not used.  Reason:
     * The kernel uses its own restorer anyway by pretending we gave one on
     our side (no, really).
     glibc still has a custom restorer because they want to be able to
     backtrace.  gdb has a second part that detects a specific instruction
     sequence and then fixes up the backtrace.  Since we don't use that
     specific instruction sequence and also the non-siginfo restorer
     is complicated (pops foreign item off the stack--so needs inline
     assembly) and we don't support great debuggability in other places,
     I've decided to take the easy way out for now. */
  /*setup_action.sa_restorer = _restorer_for_siginfo;*/
  bitindex = signum - 1;
  itembitcount = 8 * sizeof (setup_action.sa_mask.items[0]);
  setup_action.sa_mask.items[bitindex / itembitcount] = 1UL << (bitindex % itembitcount);
  old.sa_handler = SIG_DFL;
  setup_action.sa_flags = SA_RESTART;
#if __x86_64__
  /* Tell kernel that we use a restorer, on penalty of segfault.  */
  setup_action.sa_flags |= SA_RESTORER;
#endif
  int r = _sys_call4 (SYS_rt_sigaction, signum, (long) &setup_action, (long) &old, sizeof (sigset_t));
  if (r)
    return 0;
  return old.sa_handler;
#endif
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/sigprogmask.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <signal.h>
#include <unistd.h>

int
sigprocmask (int how, sigset_t const *set, sigset_t * oldset)
{
#if __i386__
  return _sys_call3 (SYS_sigprocmask, (long) how, (long) set, (long) oldset);
#else
  return _sys_call3 (SYS_rt_sigprocmask, (long) how, (long) set, (long) oldset);
#endif
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/symlink.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019,2022 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <linux/syscall.h>
#include <arch/syscall.h>
#include <unistd.h>

int
symlink (char const *old_name, char const *new_name)
{
  return _sys_call2 (SYS_symlink, (long) old_name, (long) new_name);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/_exit.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib-mini.h"

#define SYS_exit   "0x01"

// *INDENT-OFF*
void
_exit (int code)
{
  asm (
       "mov    $"SYS_exit",%%eax\n\t"
       "mov    %0,%%ebx\n\t"
       "int    $0x80\n\t"
       : // no outputs "=" (r)
       : "rm" (code)
       : "eax", "ebx"
       );
  // not reached
  // _exit (0); // tcc has a problem with this
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/syscall.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <errno.h>
#include <linux/x86/syscall.h>

// *INDENT-OFF*
long
__sys_call (long sys_call)
{
  long r;
  asm (
       "mov    %1,%%eax\n\t"
       "int    $0x80\n\t"
       "mov    %%eax,%0\n\t"
       : "=r" (r)
       : "rm" (sys_call)
       : "eax"
       );
  return r;
}

long
__sys_call1 (long sys_call, long one)
{
  long r;
  asm (
       "mov    %1,%%eax\n\t"
       "mov    %2,%%ebx\n\t"
       "int    $0x80\n\t"
       "mov    %%eax,%0\n\t"
       : "=r" (r)
       : "rm" (sys_call), "rm" (one)
       : "eax", "ebx"
       );
  return r;
}

long
__sys_call2 (long sys_call, long one, long two)
{
  long r;
  asm (
       "mov    %1,%%eax\n\t"
       "mov    %2,%%ebx\n\t"
       "mov    %3,%%ecx\n\t"
       "int    $0x80\n\t"
       "mov    %%eax,%0\n\t"
       : "=r" (r)
       : "rm" (sys_call), "rm" (one), "rm" (two)
       : "eax", "ebx", "ecx"
       );
  return r;
}

long
__sys_call3 (long sys_call, long one, long two, long three)
{
  long r;
  asm (
       "mov    %2,%%ebx\n\t"
       "mov    %3,%%ecx\n\t"
       "mov    %4,%%edx\n\t"
       "mov    %1,%%eax\n\t"
       "int    $0x80\n\t"
       "mov    %%eax,%0\n\t"
       : "=r" (r)
       : "rm" (sys_call), "rm" (one), "rm" (two), "rm" (three)
       : "eax", "ebx", "ecx", "edx"
       );
  return r;
}

long
__sys_call4 (long sys_call, long one, long two, long three, long four)
{
  long r;
  asm (
       "mov    %2,%%ebx\n\t"
       "mov    %3,%%ecx\n\t"
       "mov    %4,%%edx\n\t"
       "mov    %5,%%esi\n\t"
       "mov    %1,%%eax\n\t"
       "int    $0x80\n\t"
       "mov    %%eax,%0\n\t"
       : "=r" (r)
       : "rm" (sys_call), "rm" (one), "rm" (two), "rm" (three), "rm" (four)
       : "eax", "ebx", "ecx", "edx", "esi"
       );
  return r;
}
// *INDENT-ON*

long
_sys_call (long sys_call)
{
  long r = __sys_call (sys_call);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

long
_sys_call1 (long sys_call, long one)
{
  long r = __sys_call1 (sys_call, one);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

long
_sys_call2 (long sys_call, long one, long two)
{
  long r = __sys_call2 (sys_call, one, two);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

long
_sys_call3 (long sys_call, long one, long two, long three)
{
  long r = __sys_call3 (sys_call, one, two, three);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

long
_sys_call4 (long sys_call, long one, long two, long three, long four)
{
  long r = __sys_call4 (sys_call, one, two, three, four);
  if (r < 0)
    {
      errno = -r;
      r = -1;
    }
  else
    errno = 0;
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/_write.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2016,2017,2019,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "mes/lib-mini.h"

#define SYS_write  "0x04"

// *INDENT-OFF*
ssize_t
_write (int filedes, void const *buffer, size_t size)
{
  long r;
  asm (
       "mov    $"SYS_write",%%eax\n\t"
       "mov    %1,%%ebx\n\t"
       "mov    %2,%%ecx\n\t"
       "mov    %3,%%edx\n\t"
       "int    $0x80\n\t"
       "mov    %%eax,%0\n\t"
       : "=r" (r)
       : "rm" (filedes), "rm" (buffer), "rm" (size)
       : "eax", "ebx", "ecx", "edx"
       );
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/math/ceil.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <math.h>

double
ceil (double number)
{
  long i = number + 0.9999;
  number = i;
  return number;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/math/fabs.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <math.h>

double
fabs (double number)
{
  if (number < 0)
    return -number;
  return number;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/math/floor.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <math.h>

double
floor (double number)
{
  long i = number;
  number = i;
  return number;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/mes/fdgets.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdio.h>

char *
fdgets (char *s, int count, int fd)
{
  int c = 0;
  char *p = s;
  while (--count > 0 && c != '\n')
    {
      c = fdgetc (fd);
      if (c == EOF)
        break;
      *p++ = c;
    }
  if (p == s && (c == EOF || count == -1))
    return 0;
  *p = 0;
  return s;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/alarm.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <unistd.h>
#include <sys/time.h>

unsigned int
alarm (unsigned int seconds)
{
#if !__MESC__
  struct itimerval old;
  struct itimerval new;
  new.it_interval.tv_usec = 0;
  new.it_interval.tv_sec = 0;
  new.it_value.tv_usec = 0;
  new.it_value.tv_sec = (long int) seconds;
  if (setitimer (ITIMER_REAL, &new, &old) < 0)
    return 0;
  return old.it_value.tv_sec;
#endif
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execl.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdarg.h>
#include <unistd.h>

int
vexec (char const *file_name, va_list ap)
{
  char *arg = va_arg (ap, char *);
  char *argv[1000];             // POSIX minimum 4096
  int i = 0;

  argv[i++] = (char *) file_name;
  while (arg)
    {
      argv[i++] = arg;
      arg = va_arg (ap, char *);
      if (__mes_debug () > 2)
        {
          eputs ("arg[");
          eputs (itoa (i));
          eputs ("]: ");
          eputs (argv[i - 1]);
          eputs ("\n");
        }
    }
  argv[i] = 0;
  int r = execv (file_name, argv);
  va_end (ap);
  return r;
}

int
execl (char const *file_name, char const *arg, ...)
{
  va_list ap;
  int r;
  va_start (ap, arg);
  if (__mes_debug () > 2)
    {
      eputs ("execl ");
      eputs (file_name);
      eputs ("\n");
    }
  r = vexec (file_name, ap);
  va_end (ap);
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/execlp.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>

int
execlp (char const *file_name, char const *arg, ...)
{
  va_list ap;
  int r;
  va_start (ap, arg);
  if (!strchr (file_name, '/'))
    file_name = search_path (file_name);
  if (__mes_debug () > 2)
    {
      eputs ("execlp ");
      eputs (file_name ? file_name : "0");
      eputs ("\n");
    }
  if (!file_name)
    {
      errno = ENOENT;
      return -1;
    }
  r = vexec (file_name, ap);
  va_end (ap);
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/mktemp.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>
#include <unistd.h>

char *
mktemp (char *template)
{
  char *p = strchr (template, '\0');
  int q = (long) template;
  *--p = ((unsigned char) (q >> 4)) % 26 + 'a';
  *--p = ((unsigned char) (q >> 8)) % 26 + 'a';
  *--p = ((unsigned char) (q >> 12)) % 26 + 'a';
  *--p = ((unsigned char) (q >> 16)) % 26 + 'a';
  *--p = ((unsigned char) (q >> 20)) % 26 + 'a';
  *--p = ((unsigned char) (q >> 24)) % 26 + 'a';
  return template;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/sbrk.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <unistd.h>
#include <stdlib.h>

#if __SBRK_CHAR_PTRDIFF
char *
sbrk (ptrdiff_t)
#else
void *
sbrk (intptr_t delta)
#endif
{
  if (delta >= 0)
    return malloc (delta);
  return __brk;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/sleep.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <sys/time.h>
#include <time.h>
#include <unistd.h>

unsigned int
sleep (unsigned int seconds)
{
  struct timespec requested_time;
  struct timespec remaining;
  requested_time.tv_sec = seconds;
  requested_time.tv_nsec = 0;
  return nanosleep (&requested_time, &remaining);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/unsetenv.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <string.h>
#include <unistd.h>

void
unsetenv (char const *name)
{
  int length = strlen (name);
  char **p = environ;
  while (*p)
    {
      if (!strncmp (name, *p, length) && *(*p + length) == '=')
        {
          char **q = p;
          q[0] = q[1];
          while (*q++)
            q[0] = q[1];
        }
      p++;
    }
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/clearerr.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <stdio.h>

void
clearerr (FILE * stream)
{
  errno = 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/feof.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

int
feof (FILE * stream)
{
  int c = fgetc (stream);
  if (c != EOF)
    ungetc (c, stream);
  return c == EOF;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fgets.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdio.h>

char *
fgets (char *s, int count, FILE * stream)
{
  return fdgets (s, count, (int) (long) stream);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fileno.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

int
fileno (FILE * stream)
{
  return (int) (long) stream;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/freopen.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>

FILE *
freopen (char const *file_name, char const *opentype, FILE * stream)
{
  fclose (stream);
  return fopen (file_name, opentype);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/fscanf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdarg.h>
#include <stdio.h>

int
fscanf (FILE * stream, char const *template, ...)
{
  va_list ap;
  va_start (ap, template);
  int r = vfscanf (stream, template, ap);
  va_end (ap);
  return r;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/perror.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

void
perror (char const *message)
{
  fprintf (stderr, "%s: %s\n", strerror (errno), message);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdio/vfscanf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019,2023 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>

int
vfscanf (FILE * stream, char const *template, va_list ap)
{
  char p = fgetc (stream);
  char const *t = template;
  int count = 0;
  while (*t && p != EOF)
    if (*t != '%')
      {
        t++;
        p = fgetc (stream);
      }
    else
      {
        t++;
        char c = *t;
        if (c == 'l')
          c = *++t;
        switch (c)
          {
          case '%':
            {
              p = fgetc (stream);
              break;
            }
          case 'c':
            {
              char *c = va_arg (ap, char *);
              *c = p;
              p = fgetc (stream);
              count++;
              break;
            }
          case 'd':
          case 'i':
          case 'u':
            {
              int *d = va_arg (ap, int *);
              char buf[20];
              char *q = buf;
              if (p == '+' || p == '-')
                {
                  *q++ = p;
                  p = fgetc (stream);
                }
              while (isdigit (p))
                {
                  *q++ = p;
                  p = fgetc (stream);
                }
              ungetc (p, stream);
              *q = 0;
              q = buf;
              *d = abtol ((char const**)&q, 10);
              count++;
              break;
            }
          case 'e':
          case 'f':
          case 'g':
          case 'E':
          case 'G':
            {
              float *f = va_arg (ap, float *);
              char buf[20];
              char *q = buf;
              if (p == '+' || p == '-')
                {
                  *q++ = p;
                  p = fgetc (stream);
                }
              while (isdigit (p))
                {
                  *q++ = p;
                  p = fgetc (stream);
                }
              ungetc (p, stream);
              *q = 0;
              q = buf;
              *f = strtod (q, &q);
              count++;
              break;
            }
          default:
            {
              eputs ("vsscanf: not supported: %:");
              eputc (c);
              eputs ("\n");
              t++;
              p = fgetc (stream);
            }
          }
        t++;
      }
  va_end (ap);
  return count;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/abort.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <signal.h>

void
abort (void)
{
  if (raise (SIGABRT) < 0) /* could not raise SIGABRT */
    {
      /* Fail in any way possible */
      unsigned char* x = (unsigned char*) 0;
      *x = 2;
    }
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/abs.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>

int
abs (int x)
{
  if (x < 0)
    return -x;
  return x;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/alloca.c?h=v0.24.2

Files:
/* alloca.c -- allocate automatically reclaimed memory
   (Mostly) portable public-domain implementation -- D A Gwyn

   Taken from GNU binutils 2.10.1.
   Minor changes
   Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>

   This implementation of the PWB library alloca function,
   which is used to allocate space off the run-time stack so
   that it is automatically reclaimed upon procedure exit,
   was inspired by discussions with J. Q. Johnson of Cornell.

   There are some preprocessor constants that can
   be defined when compiling for your specific system, for
   improved efficiency; however, the defaults should be okay.

   The general concept of this implementation is to keep
   track of all alloca-allocated blocks, and reclaim any
   that are found to be deeper in the stack than the current
   invocation.  This heuristic does not reclaim storage as
   soon as it becomes invalid, but it will do so eventually.

   As a special case, alloca(0) reclaims storage without
   allocating any.  It is a good idea to use alloca(0) in
   your main control loop, etc. to force garbage collection.  */

#include <stdlib.h>
#include <unistd.h>

#define ALIGN_SIZE 4
#define ADDRESS_FUNCTION(arg) &(arg)
#define STACK_DIR -1

union alloca_header
{
  char align[ALIGN_SIZE];       /* To force sizeof(union alloca_header).  */
  struct
  {
    union alloca_header *next;  /* For chaining headers.  */
    char *deep;                 /* For stack depth measure.  */
  } h;
};

static union alloca_header *last_alloca_header = NULL;  /* -> last alloca header.  */

/* Return a void * to at least SIZE bytes of storage,
   which will be automatically reclaimed upon exit from
   the procedure that called alloca.  Originally, this space
   was supposed to be taken from the current stack frame of the
   caller, but that method cannot be made to work for some
   implementations of C, for example under Gould's UTX/32.  */

void *
alloca (size_t size)
{
  char probe;                   /* Probes stack depth: */
  char *depth = ADDRESS_FUNCTION (probe);

  /* Reclaim garbage, defined as all alloca'd storage that
     was allocated from deeper in the stack than currently.  */

  {
    union alloca_header *hp;    /* Traverses linked list.  */

    for (hp = last_alloca_header; hp != NULL;)
      if ((STACK_DIR > 0 && hp->h.deep > depth) || (STACK_DIR < 0 && hp->h.deep < depth))
        {
          union alloca_header *np = hp->h.next;

          free ((void *) hp);   /* Collect garbage.  */

          hp = np;              /* -> next header.  */
        }
      else
        break;                  /* Rest are not deeper.  */

    last_alloca_header = hp;    /* -> last valid storage.  */

  }

  if (size == 0)
    return NULL;                /* No allocation required.  */

  /* Allocate combined header + user data storage.  */

  {
    void *new = malloc (sizeof (union alloca_header) + size);
    /* Address of header.  */

    if (new == 0)
      abort ();

    ((union alloca_header *) new)->h.next = last_alloca_header;
    ((union alloca_header *) new)->h.deep = depth;

    last_alloca_header = (union alloca_header *) new;

    /* User storage begins just after header.  */

    return (void *) ((char *) new + sizeof (union alloca_header));
  }
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/atexit.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdlib.h>

int
atexit (void (*function) (void))
{
  __call_at_exit = function;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/atof.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

double
atof (char const *string)
{
  char const *p = string;
  return abtod (&p, 0);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/atol.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>

int
atol (char const *s)
{
  return atoi (s);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/__exit.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>

int
__exit (int status)
{
  exit (status);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stdlib/mbstowcs.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2023 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

size_t
mbstowcs (wchar_t * wstring, char const *string, size_t size)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("mbstowcs stub\n");
  stub = 1;
  strcpy ((char*)wstring, string);
  return strlen (string);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/bcmp.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

int
bcmp (void const *s1, void const *s2, size_t size)
{
  return memcmp (s1, s2, size);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/bcopy.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

void
bcopy (void const *src, void *dest, size_t n)
{
  memmove (dest, src, n);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/bzero.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <string.h>

#if BZERO_INT
int
#else
void
#endif
bzero (void *block, size_t size)
{
#if BZERO_INT
  return (int) (long)
#endif
    memset (block, 0, size);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/index.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

#if INDEX_INT
int
#else
char *
#endif
index (char const *s, int c)
{
  return strchr (s, c);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/rindex.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

#if INDEX_INT
int
#else
char *
#endif
rindex (char const *s, int c)
{
  return strrchr (s, c);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strcspn.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

size_t
strcspn (char const *string, char const *stopset)
{
  char *p = (char *) string;
  while (*p)
    if (strchr (stopset, *p))
      break;
    else
      p++;
  return p - string;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strdup.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdlib.h>
#include <string.h>

char *
strdup (char const *s)
{
  size_t length = strlen (s) + 1;
  char *p = (char *) malloc (length);
  if (p)
    return memcpy (p, s, length);
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strerror.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <string.h>

char *sys_errlist[] = {
  "error 00",
  "error 01",
  "error 02",
  "error 03",
  "error 04",
  "error 05",
  "error 06",
  "error 07",
  "error 08",
  "error 09",
  "error 10",
  "error 11",
  "error 12",
  "error 13",
  "error 14",
  "error 15",
  "error 16",
  "error 17",
  "error 18",
  "error 19",
  "error 20",
  "error 21",
  "error 22",
  "error 23",
  "error 24",
  "error 25",
  "error 26",
  "error 27",
  "error 28",
  "error 29",
  "error 30",
  "error 31",
  "error 32",
  "error 33",
  "error 34",
  "error 35",
  "error 36",
  "error 37",
  "error 38",
  "error 39",
};

int sys_nerr = 39;

char *
strerror (int errnum)
{
  if (__mes_debug ())
    {
      eputs ("strerror errnum=");
      eputs (itoa (errnum));
      eputs ("\n");
    }
  if (errnum > 0 && errnum <= sys_nerr)
    return sys_errlist[errnum];
  return "sterror: unknown error";
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strncat.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

char *
strncat (char *to, char const *from, size_t size)
{
  if (size == 0)
    return to;
  char *p = strchr (to, '\0');
  while (*from && size-- > 0)
    *p++ = *from++;
  *p = 0;
  return to;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strpbrk.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

char *
strpbrk (char const *string, char const *stopset)
{
  char *p = (char *) string;
  while (*p)
    if (strchr (stopset, *p))
      break;
    else
      p++;
  return p;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/string/strspn.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>

size_t
strspn (char const *string, char const *skipset)
{
  char *p = (char *) string;
  while (*p)
    if (!strchr (skipset, *p))
      break;
    else
      p++;
  return p - string;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/atan2.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <math.h>

double
atan2 (double x, double y)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("atan2 stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/bsearch.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdlib.h>

void *
#if __MESC__
bsearch (void const *key, void const *array, size_t count, size_t size, void (*compare) ())
#else
bsearch (void const *key, void const *array, size_t count, size_t size, comparison_fn_t compare)
#endif
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("bsearch stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/chown.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <unistd.h>

int
chown (char const *file_name, uid_t owner, gid_t group)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("chown stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/__cleanup.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

int
__cleanup ()
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__cleanup stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/cos.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <math.h>

double
cos (double x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("cos stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/ctime.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <time.h>

char *
ctime (time_t const *time)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("ctime stub\n");
  stub = 1;
  errno = 0;
  return "now";
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/exp.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <math.h>

double
exp (double x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("exp stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/fpurge.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <stdio.h>

int
fpurge (FILE * stream)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("fpurge stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/freadahead.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <stdio.h>

size_t
freadahead (FILE * fp)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("freadahead stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/frexp.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

double
frexp (int x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("frexp stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getgrgid.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <grp.h>

struct group *
getgrgid (gid_t gid)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("getgrid stub\n");
  static char *groups[2] = { "root", 0 };
#if SYSTEM_LIBC
  static struct group root = { "root", 0, 0 };
  root.gr_mem = groups;
#else
  static struct group root = { "root", 0, groups };
#endif
  stub = 1;
  errno = 0;
  return &root;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getgrnam.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <grp.h>

struct group *
getgrnam (char const *name)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("getgrid stub\n");
  static char *groups[2] = { "root", 0 };
#if SYSTEM_LIBC
  static struct group root = { "root", 0, 0 };
  root.gr_mem = groups;
#else
  static struct group root = { "root", 0, groups };
#endif
  stub = 1;
  errno = 0;
  return &root;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getlogin.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <unistd.h>

char *
getlogin (void)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("getlogin stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getpgid.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <unistd.h>

int
getpgid (pid_t pid)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("getpgid stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getpgrp.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <unistd.h>

pid_t
getpgrp (void)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("getpgrp stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getpwnam.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <pwd.h>

struct passwd *
getpwnam (const char *name)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("getpwnam stub\n");
  stub = 1;
  errno = 0;
  static struct passwd root = { "root", "*", 0, 0, "", "/root", "/bin/sh" };
  return &root;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/getpwuid.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <pwd.h>

struct passwd *
getpwuid (uid_t uid)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("getpwuid stub\n");
  stub = 1;
  errno = 0;
  static struct passwd root = { "root", "*", 0, 0, "", "/root", "/bin/sh" };
  return &root;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/gmtime.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>

struct tm *
gmtime (time_t const *time)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("gmtime stub\n");
  stub = 1;
  errno = 0;
  return localtime (time);
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/log.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <math.h>

double
log (double x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("log stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/mktime.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <time.h>

time_t
mktime (struct tm *broken_time)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("mktime stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/modf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <math.h>

double
modf (double value, double *integer_part)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("modf stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/pclose.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>

int
pclose (int x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("pclose stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/popen.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>

int
popen (int x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("popen stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/pow.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <math.h>

double
pow (double base, double power)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("pow stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/rand.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>

int
rand (void)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("rand stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/rewind.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>

int
rewind (int x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("rewind stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/setbuf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>

int
setbuf (int x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("setbuf stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/setgrent.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>

void
setgrent (void)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("setgrent stub\n");
  stub = 1;
  errno = 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/setlocale.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <locale.h>

char *
setlocale (int category, char const *locale)
{
  return "C";
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/setvbuf.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <stdio.h>

int
setvbuf (FILE * stream, char *buf, int mode, size_t size)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("setvbuf stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigaddset.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <signal.h>

int
sigaddset (sigset_t * set, int signum)
{
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigblock.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <signal.h>

int
sigblock (int mask)
{
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigdelset.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <signal.h>

int
sigdelset (sigset_t * set, int signum)
{
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sigsetmask.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>

int
sigsetmask (int x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("sigsetmask stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sin.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <math.h>

double
sin (double x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("sin stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sys_siglist.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>

int
sys_siglist (int x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("sys_siglist stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/system.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>

int
system (int x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("system stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/sqrt.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <math.h>

double
sqrt (double x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("sqrt stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/strftime.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <time.h>

size_t
strftime (char *s, size_t size, char const *template, struct tm const *brokentime)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("strftime stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/times.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <sys/times.h>
#include <sys/time.h>

clock_t
times (struct tms *buffer)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("times stub\n");
  stub = 1;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/ttyname.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>
#include <unistd.h>

char *
ttyname (int filedes)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("ttyname stub\n");
  stub = 1;
  errno = 0;
  if (isatty (filedes))
    return "/dev/tty0";
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/umask.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>

int
umask (int x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("umask stub\n");
  stub = 1;
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/stub/utime.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>
#include <errno.h>

int
utime (int x)
{
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("utime stub\n");
  errno = 0;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/x86-mes-gcc/setjmp.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <setjmp.h>
#include <stdlib.h>

void
longjmp (jmp_buf env, int val)
{
  val = val == 0 ? 1 : val;
  // *INDENT-OFF*
  asm (
       "mov    0x8(%ebp),%ebp\n\t"     // env*

       "mov    0x4(%ebp),%ebx\n\t"     // env->__pc
       "mov    0x8(%ebp),%esp\n\t"     // env->__sp
       "mov    0x0(%ebp),%ebp\n\t"     // env->__bp
       "jmp    *%ebx\n\t"              // jmp *PC
       );
  // *INDENT-ON*
  // not reached
  exit (42);
}

int
setjmp (jmp_buf env)
{
  long *p = (long *) &env;
  env[0].__bp = p[-2];
  env[0].__pc = p[-1];
  env[0].__sp = (long) &env;
  return 0;
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/crt1.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2021 Paul Dersey <pdersey@gmail.com>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib-mini.h>
//int main (int argc, char *argv[], char *envp[]);

// *INDENT-OFF*
void
_start ()
{
  asm (
       "mov    $0,%%eax\n\t"
       "mov    %%eax,%0\n"
       : "=r" (__stdin)
       : //no inputs ""
       );

  asm (
       "mov    $1,%%eax\n\t"
       "mov    %%eax,%0\n"
       : "=r" (__stdout)
       : //no inputs ""
       );

  asm (
       "mov    $2,%%eax\n\t"
       "mov    %%eax,%0\n"
       : "=r" (__stderr)
       : //no inputs ""
       );
  asm (
       "mov     %%ebp,%%eax\n\t"
       "add     $4,%%eax\n\t"
       "mov     (%%eax),%%eax\n\t"
       "add     $3,%%eax\n\t"
       "shl     $2,%%eax\n\t"
       "add     %%ebp,%%eax\n\t"
       "mov     %%eax,%0\n\t"
       "push    %%eax\n\t"
       : "=r" (environ)
       : //no inputs ""
       );
  asm (
       "mov     %ebp,%eax\n\t"
       "add     $8,%eax\n\t"
       "push    %eax\n\t"

       "mov     %ebp,%eax\n\t"
       "add     $4,%eax\n\t"
       "mov     (%eax),%eax\n\t"
       "push    %eax\n\t"

       "call    main\n\t"

       "mov     %eax,%ebx\n\t"
       "mov     $1,%eax\n\t"
       "int     $0x80\n\t"
       "hlt     \n\t"
       );
}

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/crtn.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/linux/x86-mes-gcc/crti.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/libtcc1.c?h=v0.24.2

Files:
/* -*-comment-start: "//";comment-end:""-*-
 * GNU Mes --- Maxwell Equations of Software
 * Copyright © 2017,2018,2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
 * Copyright © 2021 Danny Milosavljevic <dannym@scratchpost.org>
 *
 * This file is part of GNU Mes.
 *
 * GNU Mes is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * GNU Mes 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 GNU Mes.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <mes/lib.h>

#if HAVE_LONG_LONG
long long
__divdi3 (long long a, long long b)
#else
long
__divdi3 (long a, long b)
#endif
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__divdi3 stub\n");
  stub = 1;
#endif
  // FIXME: Actually use long long
  long ai = a;
  long bi = b;
#if __arm__ && __TINYC__
  return __mesabi_idiv (ai, bi);
#else
  return ai / bi;
#endif
}

#if HAVE_LONG_LONG
long long
__moddi3 (long long a, long long b)
#else
long
__moddi3 (long a, long b)
#endif
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__moddi3 stub\n");
  stub = 1;
#endif
  // FIXME: Actually use long long
  long ai = a;
  long bi = b;
#if __arm__ && __TINYC__
  return __mesabi_imod (ai, bi);
#else
  return ai % bi;
#endif
}

#if HAVE_LONG_LONG
unsigned long long
__udivdi3 (unsigned long long a, unsigned long long b)
#else
unsigned long
__udivdi3 (unsigned long a, long ah, unsigned long b)
#endif
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__udivdi3 stub\n");
  stub = 1;
#endif
  // FIXME: Actually use long long
  unsigned long ai = a;
  unsigned long bi = b;
  if (!b)
    return 0;
#if __arm__ && __TINYC__
  return __mesabi_udiv (ai, bi);
#else
  return ai / bi;
#endif
}

#if HAVE_LONG_LONG
unsigned long long
__umoddi3 (unsigned long long a, unsigned long long b)
#else
unsigned long
__umoddi3 (unsigned long a, long ah, unsigned long b)
#endif
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__umoddi3 stub\n");
  stub = 1;
#endif
  // FIXME: Actually use long long
  unsigned long ai = a;
  unsigned long bi = b;
#if __arm__ && __TINYC__
  return __mesabi_umod (ai, bi);
#else
  return ai % bi;
#endif
}

#if HAVE_LONG_LONG
unsigned long long
__lshrdi3 (unsigned long long a, long b)
#else
unsigned long
__lshrdi3 (unsigned long a, long ah, long b)
#endif
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__lshrdi3 stub\n");
  stub = 1;
  return a >> b;
#else //  __TINYC__
  for (int i = 0; i < b; i++)
#if __arm__
    a = __mesabi_udiv (a, 2); // I sure hope that doesn't endless recurse because of the optimizations in __mesabi_udiv that shift
#else // !__arm__
    a /= 2;
#endif // !__arm__
  return a;
#endif // __TINYC__
}

#if HAVE_LONG_LONG
long long
__ashldi3 (long long a, long b)
#else
long
__ashldi3 (long a, long ah, long b)
#endif
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__ashldi3 stub\n");
  stub = 1;
  return a << b;
#else //  __TINYC__
  for (int i = 0; i < b; i++)
    a += a;
  return a;
#endif // __TINYC__
}

#if HAVE_LONG_LONG
long long
__ashrdi3 (long long a, long b)
#else
long
__ashrdi3 (long a, long ah, long b)
#endif
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__ashrdi3 stub\n");
  stub = 1;
 return a >> b;
#else //  __TINYC__
  for (int i = 0; i < b; i++)
#if __arm__
    a = __mesabi_idiv (a, 2); // I sure hope that doesn't endless recurse because of the optimizations in __mesabi_udiv that shift
#else // !__arm__
    a /= 2;
#endif // !__arm__
  return a;
#endif // __TINYC__
}

#if HAVE_FLOAT_STUB || HAVE_FLOAT
double
__attribute__((weak))
#if HAVE_LONG_LONG && HAVE_FLOAT
__floatundidf (unsigned long long a)
#else
__floatundidf (unsigned long a)
#endif
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__floatundidf stub\n");
  stub = 1;
#endif
  return 0;
}

#if HAVE_LONG_LONG && HAVE_FLOAT
long double
__attribute__((weak))
__floatundixf (unsigned long long a)
#else
double
__attribute__((weak))
__floatundixf (unsigned long a)
#endif
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__floatundixf stub\n");
  stub = 1;
#endif
  return 0;
}

#if HAVE_LONG_LONG
unsigned long long
#else
unsigned long
#endif
__attribute__((weak))
__fixunsxfdi (double a1)
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__fixunsxfdi stub\n");
  stub = 1;
#endif
  return 0;
}

#if __TINYC__ == 9226
long
#elif __TINYC__
int
#else // !__TINYCC__
long long
#endif // !__TINYCC__
__attribute__((weak))
__fixdfdi (double a1)
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__fixdfdi stub\n");
  stub = 1;
#endif
  return 0;
}

#if HAVE_LONG_LONG
unsigned long long
#else
unsigned long
#endif
__attribute__((weak))
__fixxfdi (double a1)
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__fixxfdi stub\n");
  stub = 1;
#endif
  return 0;
}

#if HAVE_LONG_LONG
long long
#else
long
#endif
__attribute__((weak))
__fixsfdi (double a1)
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__fixsfdi stub\n");
  stub = 1;
#endif
  return 0;
}

double
__attribute__((weak))
__fixunsdfdi (double num, double den)
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__fixunsdfdi stub\n");
  stub = 1;
#endif
  return 0;
}
#endif // HAVE_FLOAT_STUB || HAVE_FLOAT

int
__attribute__((weak))
__fixunsdfsi (int a, int b)
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__fixunsdfsi stub\n");
  stub = 1;
#endif
  return 0;
}

#if __arm__
int
__attribute__((weak))
__floatdisf (int a, int b)
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__floatdisf stub\n");
  stub = 1;
#endif
  return 0;
}

int
__attribute__((weak))
__floatdidf (int a, int b)
{
#if !__TINYC__
  static int stub = 0;
  if (__mes_debug () && !stub)
    eputs ("__floatdidf stub\n");
  stub = 1;
#endif
  return 0;
}

int
__divsi3 (int num, int den)
{
  return __mesabi_idiv (num, den);
}

int
__modsi3 (int num, int den)
{
  return __mesabi_imod (num, den);
}

int
__udivsi3 (int num, int den)
{
  return __mesabi_udiv (num, den);
}

int
__umodsi3 (int num, int den)
{
  return __mesabi_umod (num, den);
}
#endif //__arm__

https://git.savannah.gnu.org/cgit/mes.git/tree/lib/posix/getopt.c?h=v0.24.2

File: ../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/posix/getopt.c used in: Step 393, Step 471
/* Getopt for GNU.
   Copyright (C) 1987, 88, 89, 90, 91, 1992 Free Software Foundation, Inc.
   Copyright (C) 2017,2018 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>

   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, see <http://www.gnu.org/licenses/>. */



#include <stdio.h>
#include <string.h>
#include <getopt.h>

#if __MESC__
#define static
#endif

/* For communication from `getopt' to the caller.
   When `getopt' finds an option that takes an argument,
   the argument value is returned here. */

char *optarg = 0;

/* Index in ARGV of the next element to be scanned.
   This is used for communication to and from the caller
   and for communication between successive calls to `getopt'.

   On entry to `getopt', zero means this is the first call; initialize.

   When `getopt' returns EOF, this is the index of the first of the
   non-option elements that the caller should itself scan.

   Otherwise, `optind' communicates from one call to the next
   how much of ARGV has been scanned so far.  */

int optind = 0;

/* The next char to be scanned in the option-element
   in which the last option character we returned was found.
   This allows us to pick up the scan where we left off.

   If this is zero, or a null string, it means resume the scan
   by advancing to the next ARGV-element.  */

static char *nextchar;

/* Callers store zero here to inhibit the error message
   for unrecognized options.  */

int opterr = 1;



/* Handle permutation of arguments.  */

/* Describe the part of ARGV that contains non-options that have
   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
   `last_nonopt' is the index after the last of them.  */

static int first_nonopt;
static int last_nonopt;



/* Scan elements of ARGV (whose length is ARGC) for option characters
   given in OPTSTRING.

   If an element of ARGV starts with '-', and is not exactly "-" or "--",
   then it is an option element.  The characters of this element
   (aside from the initial '-') are option characters.  If `getopt'
   is called repeatedly, it returns successively each of the option characters
   from each of the option elements.

   If `getopt' finds another option character, it returns that character,
   updating `optind' and `nextchar' so that the next call to `getopt' can
   resume the scan with the following option character or ARGV-element.

   If there are no more option characters, `getopt' returns `EOF'.
   Then `optind' is the index in ARGV of the first ARGV-element
   that is not an option.  (The ARGV-elements have been permuted
   so that those that are not options now come last.)

   OPTSTRING is a string containing the legitimate option characters.
   If an option character is seen that is not listed in OPTSTRING,
   return '?' after printing an error message.  If you set `opterr' to
   zero, the error message is suppressed but we still return '?'.

   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
   so the following text in the same ARGV-element, or the text of the following
   ARGV-element, is returned in `optarg'.  Two colons mean an option that
   wants an optional arg; if there is text in the current ARGV-element,
   it is returned in `optarg', otherwise `optarg' is set to zero.

   If OPTSTRING starts with `-' or `+', it requests different methods of
   handling the non-option ARGV-elements.

   Long-named options begin with `--' instead of `-'.
   Their names may be abbreviated as long as the abbreviation is unique
   or is an exact match for some defined option.  If they have an
   argument, it follows the option name in the same ARGV-element, separated
   from the option name by a `=', or else the in next ARGV-element.
   When `getopt' finds a long-named option, it returns 0 if that option's
   `flag' field is nonzero, the value of the option's `val' field
   if the `flag' field is zero.

   The elements of ARGV aren't really const, because we permute them.
   But we pretend they're const in the prototype to be compatible
   with other systems.

   LONGOPTS is a vector of `struct option' terminated by an
   element containing a name which is zero.

   LONGIND returns the index in LONGOPT of the long-named option found.
   It is only valid when a long-named option has been found by the most
   recent call.

   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
   long-named options.  */

int
_getopt_internal (int argc, char *const
                  *argv, char const *optstring, struct option const *longopts, int *longind, int long_only)
{
  int option_index;

  optarg = 0;

  /* Initialize the internal data when the first call is made.
     Start processing options with ARGV-element 1 (since ARGV-element 0
     is the program name); the sequence of previously skipped
     non-option ARGV-elements is empty.  */

  if (optind == 0)
    {
      first_nonopt = last_nonopt = optind = 1;

      nextchar = NULL;
    }

  if (nextchar == NULL || *nextchar == '\0')
    {
      /* If we have done all the ARGV-elements, stop the scan
         and back over any non-options that we skipped and permuted.  */

      if (optind == argc)
        {
          /* Set the next-arg-index to point at the non-options
             that we previously skipped, so the caller will digest them.  */
          if (first_nonopt != last_nonopt)
            optind = first_nonopt;
          return EOF;
        }

      /* If we have come to a non-option and did not permute it,
         either stop the scan or describe it to the caller and pass it by.  */

      if ((argv[optind][0] != '-' || argv[optind][1] == '\0'))
        return EOF;

      /* We have found another option-ARGV-element.
         Start decoding its characters.  */

      nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-'));
    }

  if (longopts != NULL && ((argv[optind][0] == '-' && (argv[optind][1] == '-' || long_only))))
    {
      const struct option *p;
      char *s = nextchar;
      int exact = 0;
      int ambig = 0;
      const struct option *pfound = NULL;
      int indfound;

      while (*s && *s != '=')
        s++;

      /* Test all options for either exact match or abbreviated matches.  */
      for (p = longopts, option_index = 0; p->name; p++, option_index++)
        if (!strncmp (p->name, nextchar, s - nextchar))
          {
            if (s - nextchar == strlen (p->name))
              {
                /* Exact match found.  */
                pfound = p;
                indfound = option_index;
                exact = 1;
                break;
              }
            else if (pfound == NULL)
              {
                /* First nonexact match found.  */
                pfound = p;
                indfound = option_index;
              }
            else
              /* Second nonexact match found.  */
              ambig = 1;
          }

      if (ambig && !exact)
        {
          if (opterr)
            fprintf (stderr, "%s: option `%s' is ambiguous\n", argv[0], argv[optind]);
          nextchar += strlen (nextchar);
          optind++;
          return '?';
        }

      if (pfound != NULL)
        {
          option_index = indfound;
          optind++;
          if (*s)
            {
              /* Don't test has_arg with >, because some C compilers don't
                 allow it to be used on enums. */
              if (pfound->has_arg)
                optarg = s + 1;
              else
                {
                  if (opterr)
                    {
                      if (argv[optind - 1][1] == '-')
                        /* --option */
                        fprintf (stderr,
                                 "%s: option `--%s' doesn't allow an argument\n", argv[0], pfound->name);
                      else
                        /* +option or -option */
                        fprintf (stderr,
                                 "%s: option `%c%s' doesn't allow an argument\n",
                                 argv[0], argv[optind - 1][0], pfound->name);
                    }
                  nextchar += strlen (nextchar);
                  return '?';
                }
            }
          else if (pfound->has_arg == 1)
            {
              if (optind < argc)
                optarg = argv[optind++];
              else
                {
                  if (opterr)
                    fprintf (stderr, "%s: option `%s' requires an argument\n", argv[0], argv[optind - 1]);
                  nextchar += strlen (nextchar);
                  return '?';
                }
            }
          nextchar += strlen (nextchar);
          if (longind != NULL)
            *longind = option_index;
          if (pfound->flag)
            {
              *(pfound->flag) = pfound->val;
              return 0;
            }
          return pfound->val;
        }
      /* Can't find it as a long option.  If this is not getopt_long_only,
         or the option starts with '--' or is not a valid short
         option, then it's an error.
         Otherwise interpret it as a short option. */
      if (!long_only || argv[optind][1] == '-' || strchr (optstring, *nextchar) == NULL)
        {
          if (opterr)
            {
              if (argv[optind][1] == '-')
                /* --option */
                fprintf (stderr, "%s: unrecognized option `--%s'\n", argv[0], nextchar);
              else
                /* +option or -option */
                fprintf (stderr, "%s: unrecognized option `%c%s'\n", argv[0], argv[optind][0], nextchar);
            }
          nextchar += strlen (nextchar);
          optind++;
          return '?';
        }
    }

  /* Look at and handle the next option-character.  */

  {
    char c = *nextchar++;
    char *temp = strchr (optstring, c);

    /* Increment `optind' when we start to process its last character.  */
    if (*nextchar == '\0')
      optind++;

    if (temp == NULL || c == ':')
      {
        if (opterr)
          {
            if (c < 040 || c >= 0177)
              fprintf (stderr, "%s: unrecognized option, character code 0%o\n", argv[0], c);
            else
              fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
          }
        return '?';
      }
    if (temp[1] == ':')
      {
        if (temp[2] == ':')
          {
            /* This is an option that accepts an argument optionally.  */
            if (*nextchar != '\0')
              {
                optarg = nextchar;
                optind++;
              }
            else
              optarg = 0;
            nextchar = NULL;
          }
        else
          {
            /* This is an option that requires an argument.  */
            if (*nextchar != 0)
              {
                optarg = nextchar;
                /* If we end this ARGV-element by taking the rest as an arg,
                   we must advance to the next element now.  */
                optind++;
              }
            else if (optind == argc)
              {
                if (opterr)
                  fprintf (stderr, "%s: option `-%c' requires an argument\n", argv[0], c);
                c = '?';
              }
            else
              /* We already incremented `optind' once;
                 increment it again when taking next ARGV-elt as argument.  */
              optarg = argv[optind++];
            nextchar = NULL;
          }
      }
    return c;
  }
}

int
getopt (int argc, char *const *argv, char const *options)
{
  return _getopt_internal (argc, argv, options, (const struct option *) 0, (int *) 0, 0);
}

int
getopt_long (int argc, char *const *argv, char const *options,
             struct option const *long_options, int *opt_index)
{
  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
}

int
getopt_long_only (int argc, char *const *argv, char const *options,
                  struct option const *long_options, int *opt_index)
{
  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
}

https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/lib/libtcc1.c

File: ../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/lib/libtcc1.c used in: Step 402, Step 414, Step 426, Step 438, Step 450, Step 462
/* TCC runtime library. 
   Parts of this code are (c) 2002 Fabrice Bellard 

   Copyright (C) 1987, 1988, 1992, 1994, 1995 Free Software Foundation, Inc.

This file 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.

In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file.  (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)

This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  
*/

#define W_TYPE_SIZE   32
#define BITS_PER_UNIT 8

typedef int Wtype;
typedef unsigned int UWtype;
typedef unsigned int USItype;
typedef long long DWtype;
typedef unsigned long long UDWtype;

struct DWstruct {
    Wtype low, high;
};

typedef union
{
  struct DWstruct s;
  DWtype ll;
} DWunion;

typedef long double XFtype;
#define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT)
#define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE)

/* the following deal with IEEE single-precision numbers */
#define EXCESS      126
#define SIGNBIT     0x80000000
#define HIDDEN      (1 << 23)
#define SIGN(fp)    ((fp) & SIGNBIT)
#define EXP(fp)     (((fp) >> 23) & 0xFF)
#define MANT(fp)    (((fp) & 0x7FFFFF) | HIDDEN)
#define PACK(s,e,m) ((s) | ((e) << 23) | (m))

/* the following deal with IEEE double-precision numbers */
#define EXCESSD     1022
#define HIDDEND     (1 << 20)
#define EXPD(fp)    (((fp.l.upper) >> 20) & 0x7FF)
#define SIGND(fp)   ((fp.l.upper) & SIGNBIT)
#define MANTD(fp)   (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) | \
                (fp.l.lower >> 22))
#define HIDDEND_LL  ((long long)1 << 52)
#define MANTD_LL(fp)    ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL)
#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m))

/* the following deal with x86 long double-precision numbers */
#define EXCESSLD    16382
#define EXPLD(fp)   (fp.l.upper & 0x7fff)
#define SIGNLD(fp)  ((fp.l.upper) & 0x8000)

/* only for x86 */
union ldouble_long {
    long double ld;
    struct {
        unsigned long long lower;
        unsigned short upper;
    } l;
};

union double_long {
    double d;
#if 1
    struct {
        unsigned int lower;
        int upper;
    } l;
#else
    struct {
        int upper;
        unsigned int lower;
    } l;
#endif
    long long ll;
};

union float_long {
    float f;
    unsigned int l;
};

/* XXX: we don't support several builtin supports for now */
#if !defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_ARM)

/* XXX: use gcc/tcc intrinsic ? */
#if defined(TCC_TARGET_I386)
#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
  __asm__ ("subl %5,%1\n\tsbbl %3,%0"                   \
       : "=r" ((USItype) (sh)),                 \
         "=&r" ((USItype) (sl))                 \
       : "0" ((USItype) (ah)),                  \
         "g" ((USItype) (bh)),                  \
         "1" ((USItype) (al)),                  \
         "g" ((USItype) (bl)))
#define umul_ppmm(w1, w0, u, v) \
  __asm__ ("mull %3"                            \
       : "=a" ((USItype) (w0)),                 \
         "=d" ((USItype) (w1))                  \
       : "%0" ((USItype) (u)),                  \
         "rm" ((USItype) (v)))
#define udiv_qrnnd(q, r, n1, n0, dv) \
  __asm__ ("divl %4"                            \
       : "=a" ((USItype) (q)),                  \
         "=d" ((USItype) (r))                   \
       : "0" ((USItype) (n0)),                  \
         "1" ((USItype) (n1)),                  \
         "rm" ((USItype) (dv)))
#define count_leading_zeros(count, x) \
  do {                                  \
    USItype __cbtmp;                            \
    __asm__ ("bsrl %1,%0"                       \
         : "=r" (__cbtmp) : "rm" ((USItype) (x)));          \
    (count) = __cbtmp ^ 31;                     \
  } while (0)
#else
#error unsupported CPU type
#endif

/* most of this code is taken from libgcc2.c from gcc */

static UDWtype __udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp)
{
  DWunion ww;
  DWunion nn, dd;
  DWunion rr;
  UWtype d0, d1, n0, n1, n2;
  UWtype q0, q1;
  UWtype b, bm;

  nn.ll = n;
  dd.ll = d;

  d0 = dd.s.low;
  d1 = dd.s.high;
  n0 = nn.s.low;
  n1 = nn.s.high;

#if !defined(UDIV_NEEDS_NORMALIZATION)
  if (d1 == 0)
    {
      if (d0 > n1)
    {
      /* 0q = nn / 0D */

      udiv_qrnnd (q0, n0, n1, n0, d0);
      q1 = 0;

      /* Remainder in n0.  */
    }
      else
    {
      /* qq = NN / 0d */

      if (d0 == 0)
        d0 = 1 / d0;    /* Divide intentionally by zero.  */

      udiv_qrnnd (q1, n1, 0, n1, d0);
      udiv_qrnnd (q0, n0, n1, n0, d0);

      /* Remainder in n0.  */
    }

      if (rp != 0)
    {
      rr.s.low = n0;
      rr.s.high = 0;
      *rp = rr.ll;
    }
    }

#else /* UDIV_NEEDS_NORMALIZATION */

  if (d1 == 0)
    {
      if (d0 > n1)
    {
      /* 0q = nn / 0D */

      count_leading_zeros (bm, d0);

      if (bm != 0)
        {
          /* Normalize, i.e. make the most significant bit of the
         denominator set.  */

          d0 = d0 << bm;
          n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm));
          n0 = n0 << bm;
        }

      udiv_qrnnd (q0, n0, n1, n0, d0);
      q1 = 0;

      /* Remainder in n0 >> bm.  */
    }
      else
    {
      /* qq = NN / 0d */

      if (d0 == 0)
        d0 = 1 / d0;    /* Divide intentionally by zero.  */

      count_leading_zeros (bm, d0);

      if (bm == 0)
        {
          /* From (n1 >= d0) /\ (the most significant bit of d0 is set),
         conclude (the most significant bit of n1 is set) /\ (the
         leading quotient digit q1 = 1).

         This special case is necessary, not an optimization.
         (Shifts counts of W_TYPE_SIZE are undefined.)  */

          n1 -= d0;
          q1 = 1;
        }
      else
        {
          /* Normalize.  */

          b = W_TYPE_SIZE - bm;

          d0 = d0 << bm;
          n2 = n1 >> b;
          n1 = (n1 << bm) | (n0 >> b);
          n0 = n0 << bm;

          udiv_qrnnd (q1, n1, n2, n1, d0);
        }

      /* n1 != d0...  */

      udiv_qrnnd (q0, n0, n1, n0, d0);

      /* Remainder in n0 >> bm.  */
    }

      if (rp != 0)
    {
      rr.s.low = n0 >> bm;
      rr.s.high = 0;
      *rp = rr.ll;
    }
    }
#endif /* UDIV_NEEDS_NORMALIZATION */

  else
    {
      if (d1 > n1)
    {
      /* 00 = nn / DD */

      q0 = 0;
      q1 = 0;

      /* Remainder in n1n0.  */
      if (rp != 0)
        {
          rr.s.low = n0;
          rr.s.high = n1;
          *rp = rr.ll;
        }
    }
      else
    {
      /* 0q = NN / dd */

      count_leading_zeros (bm, d1);
      if (bm == 0)
        {
          /* From (n1 >= d1) /\ (the most significant bit of d1 is set),
         conclude (the most significant bit of n1 is set) /\ (the
         quotient digit q0 = 0 or 1).

         This special case is necessary, not an optimization.  */

          /* The condition on the next line takes advantage of that
         n1 >= d1 (true due to program flow).  */
          if (n1 > d1 || n0 >= d0)
        {
          q0 = 1;
          sub_ddmmss (n1, n0, n1, n0, d1, d0);
        }
          else
        q0 = 0;

          q1 = 0;

          if (rp != 0)
        {
          rr.s.low = n0;
          rr.s.high = n1;
          *rp = rr.ll;
        }
        }
      else
        {
          UWtype m1, m0;
          /* Normalize.  */

          b = W_TYPE_SIZE - bm;

          d1 = (d1 << bm) | (d0 >> b);
          d0 = d0 << bm;
          n2 = n1 >> b;
          n1 = (n1 << bm) | (n0 >> b);
          n0 = n0 << bm;

          udiv_qrnnd (q0, n1, n2, n1, d1);
          umul_ppmm (m1, m0, q0, d0);

          if (m1 > n1 || (m1 == n1 && m0 > n0))
        {
          q0--;
          sub_ddmmss (m1, m0, m1, m0, d1, d0);
        }

          q1 = 0;

          /* Remainder in (n1n0 - m1m0) >> bm.  */
          if (rp != 0)
        {
          sub_ddmmss (n1, n0, n1, n0, m1, m0);
          rr.s.low = (n1 << b) | (n0 >> bm);
          rr.s.high = n1 >> bm;
          *rp = rr.ll;
        }
        }
    }
    }

  ww.s.low = q0;
  ww.s.high = q1;
  return ww.ll;
}

#define __negdi2(a) (-(a))

long long __divdi3(long long u, long long v)
{
    int c = 0;
    DWunion uu, vv;
    DWtype w;
    
    uu.ll = u;
    vv.ll = v;
    
    if (uu.s.high < 0) {
        c = ~c;
        uu.ll = __negdi2 (uu.ll);
    }
    if (vv.s.high < 0) {
        c = ~c;
        vv.ll = __negdi2 (vv.ll);
    }
    w = __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) 0);
    if (c)
        w = __negdi2 (w);
    return w;
}

long long __moddi3(long long u, long long v)
{
    int c = 0;
    DWunion uu, vv;
    DWtype w;
    
    uu.ll = u;
    vv.ll = v;
    
    if (uu.s.high < 0) {
        c = ~c;
        uu.ll = __negdi2 (uu.ll);
    }
    if (vv.s.high < 0)
        vv.ll = __negdi2 (vv.ll);
    
    __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) &w);
    if (c)
        w = __negdi2 (w);
    return w;
}

unsigned long long __udivdi3(unsigned long long u, unsigned long long v)
{
    return __udivmoddi4 (u, v, (UDWtype *) 0);
}

unsigned long long __umoddi3(unsigned long long u, unsigned long long v)
{
    UDWtype w;
    
    __udivmoddi4 (u, v, &w);
    return w;
}

/* XXX: fix tcc's code generator to do this instead */
long long __ashrdi3(long long a, int b)
{
#ifdef __TINYC__
    DWunion u;
    u.ll = a;
    if (b >= 32) {
        u.s.low = u.s.high >> (b - 32);
        u.s.high = u.s.high >> 31;
    } else if (b != 0) {
        u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b));
        u.s.high = u.s.high >> b;
    }
    return u.ll;
#else
    return a >> b;
#endif
}

/* XXX: fix tcc's code generator to do this instead */
unsigned long long __lshrdi3(unsigned long long a, int b)
{
#ifdef __TINYC__
    DWunion u;
    u.ll = a;
    if (b >= 32) {
        u.s.low = (unsigned)u.s.high >> (b - 32);
        u.s.high = 0;
    } else if (b != 0) {
        u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b));
        u.s.high = (unsigned)u.s.high >> b;
    }
    return u.ll;
#else
    return a >> b;
#endif
}

/* XXX: fix tcc's code generator to do this instead */
long long __ashldi3(long long a, int b)
{
#ifdef __TINYC__
    DWunion u;
    u.ll = a;
    if (b >= 32) {
        u.s.high = (unsigned)u.s.low << (b - 32);
        u.s.low = 0;
    } else if (b != 0) {
        u.s.high = ((unsigned)u.s.high << b) | ((unsigned)u.s.low >> (32 - b));
        u.s.low = (unsigned)u.s.low << b;
    }
    return u.ll;
#else
    return a << b;
#endif
}

#endif /* !__x86_64__ */

/* XXX: fix tcc's code generator to do this instead */
float __floatundisf(unsigned long long a)
{
    DWunion uu; 
    XFtype r;

    uu.ll = a;
    if (uu.s.high >= 0) {
        return (float)uu.ll;
    } else {
        r = (XFtype)uu.ll;
        r += 18446744073709551616.0;
        return (float)r;
    }
}

double __floatundidf(unsigned long long a)
{
    DWunion uu; 
    XFtype r;

    uu.ll = a;
    if (uu.s.high >= 0) {
        return (double)uu.ll;
    } else {
        r = (XFtype)uu.ll;
        r += 18446744073709551616.0;
        return (double)r;
    }
}

long double __floatundixf(unsigned long long a)
{
    DWunion uu; 
    XFtype r;

    uu.ll = a;
    if (uu.s.high >= 0) {
        return (long double)uu.ll;
    } else {
        r = (XFtype)uu.ll;
        r += 18446744073709551616.0;
        return (long double)r;
    }
}

unsigned long long __fixunssfdi (float a1)
{
    register union float_long fl1;
    register int exp;
    register unsigned long l;

    fl1.f = a1;

    if (fl1.l == 0)
    return (0);

    exp = EXP (fl1.l) - EXCESS - 24;

    l = MANT(fl1.l);
    if (exp >= 41)
    return (unsigned long long)-1;
    else if (exp >= 0)
        return (unsigned long long)l << exp;
    else if (exp >= -23)
        return l >> -exp;
    else
        return 0;
}

long long __fixsfdi (float a1)
{
    long long ret; int s;
    ret = __fixunssfdi((s = a1 >= 0) ? a1 : -a1);
    return s ? ret : -ret;
}

unsigned long long __fixunsdfdi (double a1)
{
    register union double_long dl1;
    register int exp;
    register unsigned long long l;

    dl1.d = a1;

    if (dl1.ll == 0)
    return (0);

    exp = EXPD (dl1) - EXCESSD - 53;

    l = MANTD_LL(dl1);

    if (exp >= 12)
    return (unsigned long long)-1;
    else if (exp >= 0)
        return l << exp;
    else if (exp >= -52)
        return l >> -exp;
    else
        return 0;
}

long long __fixdfdi (double a1)
{
    long long ret; int s;
    ret = __fixunsdfdi((s = a1 >= 0) ? a1 : -a1);
    return s ? ret : -ret;
}

#ifndef TCC_TARGET_ARM
unsigned long long __fixunsxfdi (long double a1)
{
    register union ldouble_long dl1;
    register int exp;
    register unsigned long long l;

    dl1.ld = a1;

    if (dl1.l.lower == 0 && dl1.l.upper == 0)
    return (0);

    exp = EXPLD (dl1) - EXCESSLD - 64;

    l = dl1.l.lower;

    if (exp > 0)
    return (unsigned long long)-1;
    else if (exp >= -63) 
        return l >> -exp;
    else
        return 0;
}

long long __fixxfdi (long double a1)
{
    long long ret; int s;
    ret = __fixunsxfdi((s = a1 >= 0) ? a1 : -a1);
    return s ? ret : -ret;
}
#endif /* !ARM */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.26/tcc-0.9.26.checksums

File: ../git/live-bootstrap/sysa/tcc-0.9.26/tcc-0.9.26.checksums used in: Step 474
7897c2ba182e92765856a9e3ddd2bdfcd69988b3ddc45b3ef932dc3ec041041a  /usr/bin/mes-tcc
64bdb36f0048744957454b6ad186d37db4a7fcc392f67bf052b9bc7ec033b963  /usr/bin/boot0-tcc
18af79e0e3c4bfeaced2b0867561bc9fd370cbfcc78860de5a70926374ba7c53  /usr/bin/boot1-tcc
d6fa0c2006f6c8fefb4f3750fade14aa884670637098857d06aef3c7a9bd02d8  /usr/bin/boot2-tcc
93b2c1c567f5938435fbe6c0115276f6073bd46a0a92791b164ca2752180caf5  /usr/bin/boot3-tcc
a40305e6d2acb3dc64dbc80f64952431f4728eeff03f6e7c91c737cbf6e1bdda  /usr/bin/boot4-tcc
c56d99e284dfecae047e792b049c0cee022452f13262d6b3c869ec1c9935b276  /usr/bin/tcc
96f93f2d281ee6996767b25fbff4441e8335e7eeaeab060c1d4b698366b277c9  /usr/lib/mes/libc.a
12c07ae103e7e3b390150a79e5c600d88de14e9bb73a066f6342582729ef5a3f  /usr/lib/mes/libgetopt.a
d1168ee9b528e39f0b40e8d51fb7fa3619c4a5ee928137f7faf6d0879b0916b0  /usr/lib/mes/crt1.o
09d4f9821a2566f7e56381a19259c41bd97f3c5ed83f490705acbfd1139a7736  /usr/lib/mes/crti.o
461ca1494737fab86fe1c1d3addeaf9d0ece413e353abcdea8674db3f700cda3  /usr/lib/mes/crtn.o
ac11f09698f092ed76ae40ebcd56cf3f2b903ea1333ef7537a00673dd6f73da7  /usr/lib/mes/tcc/libtcc1.a

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/sources

File: ../git/live-bootstrap/sysa/tcc-0.9.27/sources used in: Step 476
https://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27.tar.bz2 de23af78fca90ce32dff2dd45b3432b2334740bb9bb7b05bf60fdbfc396ceb9c

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/remove-fileopen.before

File: /sysa/tcc-0.9.27/simple-patches/remove-fileopen.before used in: Step 482
    if (ret == 1)
        return ar_usage(ret);

    if ((fh = fopen(argv[i_lib], "wb")) == NULL)
    {
        fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
        goto the_end;
    }

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/remove-fileopen.after

File: /sysa/tcc-0.9.27/simple-patches/remove-fileopen.after used in: Step 482
    if (ret == 1)
        return ar_usage(ret);

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/addback-fileopen.before

File: /sysa/tcc-0.9.27/simple-patches/addback-fileopen.before used in: Step 483
    // write header

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/addback-fileopen.after

File: /sysa/tcc-0.9.27/simple-patches/addback-fileopen.after used in: Step 483
    if ((fh = fopen(argv[i_lib], "wb")) == NULL)
    {
        fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
        goto the_end;
    }

    // write header

https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tccelf.c

Files:
/*
 *  ELF file handling for TCC
 *
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "tcc.h"

/* Define this to get some debug output during relocation processing.  */
#undef DEBUG_RELOC

/********************************************************/
/* global variables */

ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */
ST_DATA Section *common_section;
ST_DATA Section *cur_text_section; /* current section where function code is generated */
#ifdef CONFIG_TCC_ASM
ST_DATA Section *last_text_section; /* to handle .previous asm directive */
#endif
#ifdef CONFIG_TCC_BCHECK
/* bound check related sections */
ST_DATA Section *bounds_section; /* contains global data bound description */
ST_DATA Section *lbounds_section; /* contains local data bound description */
#endif
/* symbol sections */
ST_DATA Section *symtab_section;
/* debug sections */
ST_DATA Section *stab_section, *stabstr_section;

/* XXX: avoid static variable */
static int new_undef_sym = 0; /* Is there a new undefined sym since last new_undef_sym() */

/* special flag to indicate that the section should not be linked to the other ones */
#define SHF_PRIVATE 0x80000000
/* section is dynsymtab_section */
#define SHF_DYNSYM 0x40000000

/* ------------------------------------------------------------------------- */

ST_FUNC void tccelf_new(TCCState *s)
{
    /* no section zero */
    dynarray_add(&s->sections, &s->nb_sections, NULL);

    /* create standard sections */
    text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
    data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
    bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
    common_section = new_section(s, ".common", SHT_NOBITS, SHF_PRIVATE);
    common_section->sh_num = SHN_COMMON;

    /* symbols are always generated for linking stage */
    symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0,
                                ".strtab",
                                ".hashtab", SHF_PRIVATE);
    s->symtab = symtab_section;

    /* private symbol table for dynamic symbols */
    s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE|SHF_DYNSYM,
                                      ".dynstrtab",
                                      ".dynhashtab", SHF_PRIVATE);
    get_sym_attr(s, 0, 1);
}

#ifdef CONFIG_TCC_BCHECK
ST_FUNC void tccelf_bounds_new(TCCState *s)
{
    /* create bounds sections */
    bounds_section = new_section(s, ".bounds",
                                 SHT_PROGBITS, SHF_ALLOC);
    lbounds_section = new_section(s, ".lbounds",
                                  SHT_PROGBITS, SHF_ALLOC);
}
#endif

ST_FUNC void tccelf_stab_new(TCCState *s)
{
    stab_section = new_section(s, ".stab", SHT_PROGBITS, 0);
    stab_section->sh_entsize = sizeof(Stab_Sym);
    stabstr_section = new_section(s, ".stabstr", SHT_STRTAB, 0);
    put_elf_str(stabstr_section, "");
    stab_section->link = stabstr_section;
    /* put first entry */
    put_stabs("", 0, 0, 0, 0);
}

static void free_section(Section *s)
{
    tcc_free(s->data);
}

ST_FUNC void tccelf_delete(TCCState *s1)
{
    int i;

    /* free all sections */
    for(i = 1; i < s1->nb_sections; i++)
        free_section(s1->sections[i]);
    dynarray_reset(&s1->sections, &s1->nb_sections);

    for(i = 0; i < s1->nb_priv_sections; i++)
        free_section(s1->priv_sections[i]);
    dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections);

    /* free any loaded DLLs */
#ifdef TCC_IS_NATIVE
    for ( i = 0; i < s1->nb_loaded_dlls; i++) {
        DLLReference *ref = s1->loaded_dlls[i];
        if ( ref->handle )
# ifdef _WIN32
            FreeLibrary((HMODULE)ref->handle);
# else
            dlclose(ref->handle);
# endif
    }
#endif
    /* free loaded dlls array */
    dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
    tcc_free(s1->sym_attrs);

    symtab_section = NULL; /* for tccrun.c:rt_printline() */
}

/* save section data state */
ST_FUNC void tccelf_begin_file(TCCState *s1)
{
    Section *s; int i;
    for (i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        s->sh_offset = s->data_offset;
    }
    /* disable symbol hashing during compilation */
    s = s1->symtab, s->reloc = s->hash, s->hash = NULL;
#if defined TCC_TARGET_X86_64 && defined TCC_TARGET_PE
    s1->uw_sym = 0;
#endif
}

/* At the end of compilation, convert any UNDEF syms to global, and merge
   with previously existing symbols */
ST_FUNC void tccelf_end_file(TCCState *s1)
{
    Section *s = s1->symtab;
    int first_sym, nb_syms, *tr, i;

    first_sym = s->sh_offset / sizeof (ElfSym);
    nb_syms = s->data_offset / sizeof (ElfSym) - first_sym;
    s->data_offset = s->sh_offset;
    s->link->data_offset = s->link->sh_offset;
    s->hash = s->reloc, s->reloc = NULL;
    tr = tcc_mallocz(nb_syms * sizeof *tr);

    for (i = 0; i < nb_syms; ++i) {
        ElfSym *sym = (ElfSym*)s->data + first_sym + i;
        if (sym->st_shndx == SHN_UNDEF
            && ELFW(ST_BIND)(sym->st_info) == STB_LOCAL)
            sym->st_info = ELFW(ST_INFO)(STB_GLOBAL, ELFW(ST_TYPE)(sym->st_info));
        tr[i] = set_elf_sym(s, sym->st_value, sym->st_size, sym->st_info,
            sym->st_other, sym->st_shndx, s->link->data + sym->st_name);
    }
    /* now update relocations */
    for (i = 1; i < s1->nb_sections; i++) {
        Section *sr = s1->sections[i];
        if (sr->sh_type == SHT_RELX && sr->link == s) {
            ElfW_Rel *rel = (ElfW_Rel*)(sr->data + sr->sh_offset);
            ElfW_Rel *rel_end = (ElfW_Rel*)(sr->data + sr->data_offset);
            for (; rel < rel_end; ++rel) {
                int n = ELFW(R_SYM)(rel->r_info) - first_sym;
                //if (n < 0) tcc_error("internal: invalid symbol index in relocation");
                rel->r_info = ELFW(R_INFO)(tr[n], ELFW(R_TYPE)(rel->r_info));
            }
        }
    }
    tcc_free(tr);
}

ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags)
{
    Section *sec;

    sec = tcc_mallocz(sizeof(Section) + strlen(name));
    strcpy(sec->name, name);
    sec->sh_type = sh_type;
    sec->sh_flags = sh_flags;
    switch(sh_type) {
    case SHT_HASH:
    case SHT_REL:
    case SHT_RELA:
    case SHT_DYNSYM:
    case SHT_SYMTAB:
    case SHT_DYNAMIC:
        sec->sh_addralign = 4;
        break;
    case SHT_STRTAB:
        sec->sh_addralign = 1;
        break;
    default:
        sec->sh_addralign =  PTR_SIZE; /* gcc/pcc default alignment */
        break;
    }

    if (sh_flags & SHF_PRIVATE) {
        dynarray_add(&s1->priv_sections, &s1->nb_priv_sections, sec);
    } else {
        sec->sh_num = s1->nb_sections;
        dynarray_add(&s1->sections, &s1->nb_sections, sec);
    }

    return sec;
}

ST_FUNC Section *new_symtab(TCCState *s1,
                           const char *symtab_name, int sh_type, int sh_flags,
                           const char *strtab_name,
                           const char *hash_name, int hash_sh_flags)
{
    Section *symtab, *strtab, *hash;
    int *ptr, nb_buckets;

    symtab = new_section(s1, symtab_name, sh_type, sh_flags);
    symtab->sh_entsize = sizeof(ElfW(Sym));
    strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags);
    put_elf_str(strtab, "");
    symtab->link = strtab;
    put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL);

    nb_buckets = 1;

    hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags);
    hash->sh_entsize = sizeof(int);
    symtab->hash = hash;
    hash->link = symtab;

    ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int));
    ptr[0] = nb_buckets;
    ptr[1] = 1;
    memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int));
    return symtab;
}

/* realloc section and set its content to zero */
ST_FUNC void section_realloc(Section *sec, unsigned long new_size)
{
    unsigned long size;
    unsigned char *data;

    size = sec->data_allocated;
    if (size == 0)
        size = 1;
    while (size < new_size)
        size = size * 2;
    data = tcc_realloc(sec->data, size);
    memset(data + sec->data_allocated, 0, size - sec->data_allocated);
    sec->data = data;
    sec->data_allocated = size;
}

/* reserve at least 'size' bytes aligned per 'align' in section
   'sec' from current offset, and return the aligned offset */
ST_FUNC size_t section_add(Section *sec, addr_t size, int align)
{
    size_t offset, offset1;

    offset = (sec->data_offset + align - 1) & -align;
    offset1 = offset + size;
    if (sec->sh_type != SHT_NOBITS && offset1 > sec->data_allocated)
        section_realloc(sec, offset1);
    sec->data_offset = offset1;
    if (align > sec->sh_addralign)
        sec->sh_addralign = align;
    return offset;
}

/* reserve at least 'size' bytes in section 'sec' from
   sec->data_offset. */
ST_FUNC void *section_ptr_add(Section *sec, addr_t size)
{
    size_t offset = section_add(sec, size, 1);
    return sec->data + offset;
}

/* reserve at least 'size' bytes from section start */
ST_FUNC void section_reserve(Section *sec, unsigned long size)
{
    if (size > sec->data_allocated)
        section_realloc(sec, size);
    if (size > sec->data_offset)
        sec->data_offset = size;
}

/* return a reference to a section, and create it if it does not
   exists */
ST_FUNC Section *find_section(TCCState *s1, const char *name)
{
    Section *sec;
    int i;
    for(i = 1; i < s1->nb_sections; i++) {
        sec = s1->sections[i];
        if (!strcmp(name, sec->name))
            return sec;
    }
    /* sections are created as PROGBITS */
    return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);
}

/* ------------------------------------------------------------------------- */

ST_FUNC int put_elf_str(Section *s, const char *sym)
{
    int offset, len;
    char *ptr;

    len = strlen(sym) + 1;
    offset = s->data_offset;
    ptr = section_ptr_add(s, len);
    memmove(ptr, sym, len);
    return offset;
}

/* elf symbol hashing function */
static unsigned long elf_hash(const unsigned char *name)
{
    unsigned long h = 0, g;

    while (*name) {
        h = (h << 4) + *name++;
        g = h & 0xf0000000;
        if (g)
            h ^= g >> 24;
        h &= ~g;
    }
    return h;
}

/* rebuild hash table of section s */
/* NOTE: we do factorize the hash table code to go faster */
static void rebuild_hash(Section *s, unsigned int nb_buckets)
{
    ElfW(Sym) *sym;
    int *ptr, *hash, nb_syms, sym_index, h;
    unsigned char *strtab;

    strtab = s->link->data;
    nb_syms = s->data_offset / sizeof(ElfW(Sym));

    if (!nb_buckets)
        nb_buckets = ((int*)s->hash->data)[0];

    s->hash->data_offset = 0;
    ptr = section_ptr_add(s->hash, (2 + nb_buckets + nb_syms) * sizeof(int));
    ptr[0] = nb_buckets;
    ptr[1] = nb_syms;
    ptr += 2;
    hash = ptr;
    memset(hash, 0, (nb_buckets + 1) * sizeof(int));
    ptr += nb_buckets + 1;

    sym = (ElfW(Sym) *)s->data + 1;
    for(sym_index = 1; sym_index < nb_syms; sym_index++) {
        if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
            h = elf_hash(strtab + sym->st_name) % nb_buckets;
            *ptr = hash[h];
            hash[h] = sym_index;
        } else {
            *ptr = 0;
        }
        ptr++;
        sym++;
    }
}

/* return the symbol number */
ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size,
    int info, int other, int shndx, const char *name)
{
    int name_offset, sym_index;
    int nbuckets, h;
    ElfW(Sym) *sym;
    Section *hs;

    sym = section_ptr_add(s, sizeof(ElfW(Sym)));
    if (name && name[0])
        name_offset = put_elf_str(s->link, name);
    else
        name_offset = 0;
    /* XXX: endianness */
    sym->st_name = name_offset;
    sym->st_value = value;
    sym->st_size = size;
    sym->st_info = info;
    sym->st_other = other;
    sym->st_shndx = shndx;
    sym_index = sym - (ElfW(Sym) *)s->data;
    hs = s->hash;
    if (hs) {
        int *ptr, *base;
        ptr = section_ptr_add(hs, sizeof(int));
        base = (int *)hs->data;
        /* only add global or weak symbols. */
        if (ELFW(ST_BIND)(info) != STB_LOCAL) {
            /* add another hashing entry */
            nbuckets = base[0];
            h = elf_hash((unsigned char *)s->link->data + name_offset) % nbuckets;
            *ptr = base[2 + h];
            base[2 + h] = sym_index;
            base[1]++;
            /* we resize the hash table */
            hs->nb_hashed_syms++;
            if (hs->nb_hashed_syms > 2 * nbuckets) {
                rebuild_hash(s, 2 * nbuckets);
            }
        } else {
            *ptr = 0;
            base[1]++;
        }
    }
    return sym_index;
}

ST_FUNC int find_elf_sym(Section *s, const char *name)
{
    ElfW(Sym) *sym;
    Section *hs;
    int nbuckets, sym_index, h;
    const char *name1;

    hs = s->hash;
    if (!hs)
        return 0;
    nbuckets = ((int *)hs->data)[0];
    h = elf_hash((unsigned char *) name) % nbuckets;
    sym_index = ((int *)hs->data)[2 + h];
    while (sym_index != 0) {
        sym = &((ElfW(Sym) *)s->data)[sym_index];
        name1 = (char *) s->link->data + sym->st_name;
        if (!strcmp(name, name1))
            return sym_index;
        sym_index = ((int *)hs->data)[2 + nbuckets + sym_index];
    }
    return 0;
}

/* return elf symbol value, signal error if 'err' is nonzero */
ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err)
{
    int sym_index;
    ElfW(Sym) *sym;

    sym_index = find_elf_sym(s->symtab, name);
    sym = &((ElfW(Sym) *)s->symtab->data)[sym_index];
    if (!sym_index || sym->st_shndx == SHN_UNDEF) {
        if (err)
            tcc_error("%s not defined", name);
        return 0;
    }
    return sym->st_value;
}

/* return elf symbol value */
LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name)
{
    return (void*)(uintptr_t)get_elf_sym_addr(s, name, 0);
}

#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE
/* return elf symbol value or error */
ST_FUNC void* tcc_get_symbol_err(TCCState *s, const char *name)
{
    return (void*)(uintptr_t)get_elf_sym_addr(s, name, 1);
}
#endif

/* add an elf symbol : check if it is already defined and patch
   it. Return symbol index. NOTE that sh_num can be SHN_UNDEF. */
ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size,
                       int info, int other, int shndx, const char *name)
{
    ElfW(Sym) *esym;
    int sym_bind, sym_index, sym_type, esym_bind;
    unsigned char sym_vis, esym_vis, new_vis;

    sym_bind = ELFW(ST_BIND)(info);
    sym_type = ELFW(ST_TYPE)(info);
    sym_vis = ELFW(ST_VISIBILITY)(other);

    if (sym_bind != STB_LOCAL) {
        /* we search global or weak symbols */
        sym_index = find_elf_sym(s, name);
        if (!sym_index)
            goto do_def;
        esym = &((ElfW(Sym) *)s->data)[sym_index];
        if (esym->st_value == value && esym->st_size == size && esym->st_info == info
            && esym->st_other == other && esym->st_shndx == shndx)
            return sym_index;
        if (esym->st_shndx != SHN_UNDEF) {
            esym_bind = ELFW(ST_BIND)(esym->st_info);
            /* propagate the most constraining visibility */
            /* STV_DEFAULT(0)<STV_PROTECTED(3)<STV_HIDDEN(2)<STV_INTERNAL(1) */
            esym_vis = ELFW(ST_VISIBILITY)(esym->st_other);
            if (esym_vis == STV_DEFAULT) {
                new_vis = sym_vis;
            } else if (sym_vis == STV_DEFAULT) {
                new_vis = esym_vis;
            } else {
                new_vis = (esym_vis < sym_vis) ? esym_vis : sym_vis;
            }
            esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
                             | new_vis;
            other = esym->st_other; /* in case we have to patch esym */
            if (shndx == SHN_UNDEF) {
                /* ignore adding of undefined symbol if the
                   corresponding symbol is already defined */
            } else if (sym_bind == STB_GLOBAL && esym_bind == STB_WEAK) {
                /* global overrides weak, so patch */
                goto do_patch;
            } else if (sym_bind == STB_WEAK && esym_bind == STB_GLOBAL) {
                /* weak is ignored if already global */
            } else if (sym_bind == STB_WEAK && esym_bind == STB_WEAK) {
                /* keep first-found weak definition, ignore subsequents */
            } else if (sym_vis == STV_HIDDEN || sym_vis == STV_INTERNAL) {
                /* ignore hidden symbols after */
            } else if ((esym->st_shndx == SHN_COMMON
                            || esym->st_shndx == bss_section->sh_num)
                        && (shndx < SHN_LORESERVE
                            && shndx != bss_section->sh_num)) {
                /* data symbol gets precedence over common/bss */
                goto do_patch;
            } else if (shndx == SHN_COMMON || shndx == bss_section->sh_num) {
                /* data symbol keeps precedence over common/bss */
            } else if (s->sh_flags & SHF_DYNSYM) {
                /* we accept that two DLL define the same symbol */
        } else if (esym->st_other & ST_ASM_SET) {
        /* If the existing symbol came from an asm .set
           we can override.  */
        goto do_patch;
            } else {
#if 0
                printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n",
                       sym_bind, shndx, new_vis, esym_bind, esym->st_shndx, esym_vis);
#endif
                tcc_error_noabort("'%s' defined twice", name);
            }
        } else {
        do_patch:
            esym->st_info = ELFW(ST_INFO)(sym_bind, sym_type);
            esym->st_shndx = shndx;
            new_undef_sym = 1;
            esym->st_value = value;
            esym->st_size = size;
            esym->st_other = other;
        }
    } else {
    do_def:
        sym_index = put_elf_sym(s, value, size,
                                ELFW(ST_INFO)(sym_bind, sym_type), other,
                                shndx, name);
    }
    return sym_index;
}

/* put relocation */
ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset,
                            int type, int symbol, addr_t addend)
{
    char buf[256];
    Section *sr;
    ElfW_Rel *rel;

    sr = s->reloc;
    if (!sr) {
        /* if no relocation section, create it */
        snprintf(buf, sizeof(buf), REL_SECTION_FMT, s->name);
        /* if the symtab is allocated, then we consider the relocation
           are also */
        sr = new_section(tcc_state, buf, SHT_RELX, symtab->sh_flags);
        sr->sh_entsize = sizeof(ElfW_Rel);
        sr->link = symtab;
        sr->sh_info = s->sh_num;
        s->reloc = sr;
    }
    rel = section_ptr_add(sr, sizeof(ElfW_Rel));
    rel->r_offset = offset;
    rel->r_info = ELFW(R_INFO)(symbol, type);
#if SHT_RELX == SHT_RELA
    rel->r_addend = addend;
#else
    if (addend)
        tcc_error("non-zero addend on REL architecture");
#endif
}

ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset,
                           int type, int symbol)
{
    put_elf_reloca(symtab, s, offset, type, symbol, 0);
}

/* Remove relocations for section S->reloc starting at oldrelocoffset
   that are to the same place, retaining the last of them.  As side effect
   the relocations are sorted.  Possibly reduces the number of relocs.  */
ST_FUNC void squeeze_multi_relocs(Section *s, size_t oldrelocoffset)
{
    Section *sr = s->reloc;
    ElfW_Rel *r, *dest;
    ssize_t a;
    ElfW(Addr) addr;

    if (oldrelocoffset + sizeof(*r) >= sr->data_offset)
      return;
    /* The relocs we're dealing with are the result of initializer parsing.
       So they will be mostly in order and there aren't many of them.
       Secondly we need a stable sort (which qsort isn't).  We use
       a simple insertion sort.  */
    for (a = oldrelocoffset + sizeof(*r); a < sr->data_offset; a += sizeof(*r)) {
    ssize_t i = a - sizeof(*r);
    addr = ((ElfW_Rel*)(sr->data + a))->r_offset;
    for (; i >= (ssize_t)oldrelocoffset &&
           ((ElfW_Rel*)(sr->data + i))->r_offset > addr; i -= sizeof(*r)) {
        ElfW_Rel tmp = *(ElfW_Rel*)(sr->data + a);
        *(ElfW_Rel*)(sr->data + a) = *(ElfW_Rel*)(sr->data + i);
        *(ElfW_Rel*)(sr->data + i) = tmp;
    }
    }

    r = (ElfW_Rel*)(sr->data + oldrelocoffset);
    dest = r;
    for (; r < (ElfW_Rel*)(sr->data + sr->data_offset); r++) {
    if (dest->r_offset != r->r_offset)
      dest++;
    *dest = *r;
    }
    sr->data_offset = (unsigned char*)dest - sr->data + sizeof(*r);
}

/* put stab debug information */

ST_FUNC void put_stabs(const char *str, int type, int other, int desc,
                      unsigned long value)
{
    Stab_Sym *sym;

    sym = section_ptr_add(stab_section, sizeof(Stab_Sym));
    if (str) {
        sym->n_strx = put_elf_str(stabstr_section, str);
    } else {
        sym->n_strx = 0;
    }
    sym->n_type = type;
    sym->n_other = other;
    sym->n_desc = desc;
    sym->n_value = value;
}

ST_FUNC void put_stabs_r(const char *str, int type, int other, int desc,
                        unsigned long value, Section *sec, int sym_index)
{
    put_stabs(str, type, other, desc, value);
    put_elf_reloc(symtab_section, stab_section,
                  stab_section->data_offset - sizeof(unsigned int),
                  R_DATA_32, sym_index);
}

ST_FUNC void put_stabn(int type, int other, int desc, int value)
{
    put_stabs(NULL, type, other, desc, value);
}

ST_FUNC void put_stabd(int type, int other, int desc)
{
    put_stabs(NULL, type, other, desc, 0);
}

ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc)
{
    int n;
    struct sym_attr *tab;

    if (index >= s1->nb_sym_attrs) {
        if (!alloc)
            return s1->sym_attrs;
        /* find immediately bigger power of 2 and reallocate array */
        n = 1;
        while (index >= n)
            n *= 2;
        tab = tcc_realloc(s1->sym_attrs, n * sizeof(*s1->sym_attrs));
        s1->sym_attrs = tab;
        memset(s1->sym_attrs + s1->nb_sym_attrs, 0,
               (n - s1->nb_sym_attrs) * sizeof(*s1->sym_attrs));
        s1->nb_sym_attrs = n;
    }
    return &s1->sym_attrs[index];
}

/* Browse each elem of type <type> in section <sec> starting at elem <startoff>
   using variable <elem> */
#define for_each_elem(sec, startoff, elem, type) \
    for (elem = (type *) sec->data + startoff; \
         elem < (type *) (sec->data + sec->data_offset); elem++)

/* In an ELF file symbol table, the local symbols must appear below
   the global and weak ones. Since TCC cannot sort it while generating
   the code, we must do it after. All the relocation tables are also
   modified to take into account the symbol table sorting */
static void sort_syms(TCCState *s1, Section *s)
{
    int *old_to_new_syms;
    ElfW(Sym) *new_syms;
    int nb_syms, i;
    ElfW(Sym) *p, *q;
    ElfW_Rel *rel;
    Section *sr;
    int type, sym_index;

    nb_syms = s->data_offset / sizeof(ElfW(Sym));
    new_syms = tcc_malloc(nb_syms * sizeof(ElfW(Sym)));
    old_to_new_syms = tcc_malloc(nb_syms * sizeof(int));

    /* first pass for local symbols */
    p = (ElfW(Sym) *)s->data;
    q = new_syms;
    for(i = 0; i < nb_syms; i++) {
        if (ELFW(ST_BIND)(p->st_info) == STB_LOCAL) {
            old_to_new_syms[i] = q - new_syms;
            *q++ = *p;
        }
        p++;
    }
    /* save the number of local symbols in section header */
    if( s->sh_size )    /* this 'if' makes IDA happy */
        s->sh_info = q - new_syms;

    /* then second pass for non local symbols */
    p = (ElfW(Sym) *)s->data;
    for(i = 0; i < nb_syms; i++) {
        if (ELFW(ST_BIND)(p->st_info) != STB_LOCAL) {
            old_to_new_syms[i] = q - new_syms;
            *q++ = *p;
        }
        p++;
    }

    /* we copy the new symbols to the old */
    memcpy(s->data, new_syms, nb_syms * sizeof(ElfW(Sym)));
    tcc_free(new_syms);

    /* now we modify all the relocations */
    for(i = 1; i < s1->nb_sections; i++) {
        sr = s1->sections[i];
        if (sr->sh_type == SHT_RELX && sr->link == s) {
            for_each_elem(sr, 0, rel, ElfW_Rel) {
                sym_index = ELFW(R_SYM)(rel->r_info);
                type = ELFW(R_TYPE)(rel->r_info);
                sym_index = old_to_new_syms[sym_index];
                rel->r_info = ELFW(R_INFO)(sym_index, type);
            }
        }
    }

    tcc_free(old_to_new_syms);
}

/* relocate symbol table, resolve undefined symbols if do_resolve is
   true and output error if undefined symbol. */
ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve)
{
    ElfW(Sym) *sym;
    int sym_bind, sh_num;
    const char *name;

    for_each_elem(symtab, 1, sym, ElfW(Sym)) {
        sh_num = sym->st_shndx;
        if (sh_num == SHN_UNDEF) {
            name = (char *) s1->symtab->link->data + sym->st_name;
            /* Use ld.so to resolve symbol for us (for tcc -run) */
            if (do_resolve) {
#if defined TCC_IS_NATIVE && !defined TCC_TARGET_PE
                void *addr = dlsym(RTLD_DEFAULT, name);
                if (addr) {
                    sym->st_value = (addr_t) addr;
#ifdef DEBUG_RELOC
            printf ("relocate_sym: %s -> 0x%lx\n", name, sym->st_value);
#endif
                    goto found;
                }
#endif
            /* if dynamic symbol exist, it will be used in relocate_section */
            } else if (s1->dynsym && find_elf_sym(s1->dynsym, name))
                goto found;
            /* XXX: _fp_hw seems to be part of the ABI, so we ignore
               it */
            if (!strcmp(name, "_fp_hw"))
                goto found;
            /* only weak symbols are accepted to be undefined. Their
               value is zero */
            sym_bind = ELFW(ST_BIND)(sym->st_info);
            if (sym_bind == STB_WEAK)
                sym->st_value = 0;
            else
                tcc_error_noabort("undefined symbol '%s'", name);
        } else if (sh_num < SHN_LORESERVE) {
            /* add section base */
            sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
        }
    found: ;
    }
}

/* relocate a given section (CPU dependent) by applying the relocations
   in the associated relocation section */
ST_FUNC void relocate_section(TCCState *s1, Section *s)
{
    Section *sr = s->reloc;
    ElfW_Rel *rel;
    ElfW(Sym) *sym;
    int type, sym_index;
    unsigned char *ptr;
    addr_t tgt, addr;

    relocate_init(sr);

    for_each_elem(sr, 0, rel, ElfW_Rel) {
        ptr = s->data + rel->r_offset;
        sym_index = ELFW(R_SYM)(rel->r_info);
        sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
        type = ELFW(R_TYPE)(rel->r_info);
        tgt = sym->st_value;
#if SHT_RELX == SHT_RELA
        tgt += rel->r_addend;
#endif
        addr = s->sh_addr + rel->r_offset;
        relocate(s1, rel, type, ptr, addr, tgt);
    }
    /* if the relocation is allocated, we change its symbol table */
    if (sr->sh_flags & SHF_ALLOC)
        sr->link = s1->dynsym;
}

/* relocate relocation table in 'sr' */
static void relocate_rel(TCCState *s1, Section *sr)
{
    Section *s;
    ElfW_Rel *rel;

    s = s1->sections[sr->sh_info];
    for_each_elem(sr, 0, rel, ElfW_Rel)
        rel->r_offset += s->sh_addr;
}

/* count the number of dynamic relocations so that we can reserve
   their space */
static int prepare_dynamic_rel(TCCState *s1, Section *sr)
{
    ElfW_Rel *rel;
    int sym_index, type, count;

    count = 0;
    for_each_elem(sr, 0, rel, ElfW_Rel) {
        sym_index = ELFW(R_SYM)(rel->r_info);
        type = ELFW(R_TYPE)(rel->r_info);
        switch(type) {
#if defined(TCC_TARGET_I386)
        case R_386_32:
            if (!get_sym_attr(s1, sym_index, 0)->dyn_index
                && ((ElfW(Sym)*)symtab_section->data + sym_index)->st_shndx == SHN_UNDEF) {
                /* don't fixup unresolved (weak) symbols */
                rel->r_info = ELFW(R_INFO)(sym_index, R_386_RELATIVE);
                break;
            }
#elif defined(TCC_TARGET_X86_64)
        case R_X86_64_32:
        case R_X86_64_32S:
        case R_X86_64_64:
#endif
            count++;
            break;
#if defined(TCC_TARGET_I386)
        case R_386_PC32:
#elif defined(TCC_TARGET_X86_64)
        case R_X86_64_PC32:
#endif
            if (get_sym_attr(s1, sym_index, 0)->dyn_index)
                count++;
            break;
        default:
            break;
        }
    }
    if (count) {
        /* allocate the section */
        sr->sh_flags |= SHF_ALLOC;
        sr->sh_size = count * sizeof(ElfW_Rel);
    }
    return count;
}

static void build_got(TCCState *s1)
{
    /* if no got, then create it */
    s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
    s1->got->sh_entsize = 4;
    set_elf_sym(symtab_section, 0, 4, ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT),
                0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_");
    /* keep space for _DYNAMIC pointer and two dummy got entries */
    section_ptr_add(s1->got, 3 * PTR_SIZE);
}

/* Create a GOT and (for function call) a PLT entry corresponding to a symbol
   in s1->symtab. When creating the dynamic symbol table entry for the GOT
   relocation, use 'size' and 'info' for the corresponding symbol metadata.
   Returns the offset of the GOT or (if any) PLT entry. */
static struct sym_attr * put_got_entry(TCCState *s1, int dyn_reloc_type,
                                       unsigned long size,
                                       int info, int sym_index)
{
    int need_plt_entry;
    const char *name;
    ElfW(Sym) *sym;
    struct sym_attr *attr;
    unsigned got_offset;
    char plt_name[100];
    int len;

    need_plt_entry = (dyn_reloc_type == R_JMP_SLOT);
    attr = get_sym_attr(s1, sym_index, 1);

    /* In case a function is both called and its address taken 2 GOT entries
       are created, one for taking the address (GOT) and the other for the PLT
       entry (PLTGOT).  */
    if (need_plt_entry ? attr->plt_offset : attr->got_offset)
        return attr;

    /* create the GOT entry */
    got_offset = s1->got->data_offset;
    section_ptr_add(s1->got, PTR_SIZE);

    /* Create the GOT relocation that will insert the address of the object or
       function of interest in the GOT entry. This is a static relocation for
       memory output (dlsym will give us the address of symbols) and dynamic
       relocation otherwise (executable and DLLs). The relocation should be
       done lazily for GOT entry with *_JUMP_SLOT relocation type (the one
       associated to a PLT entry) but is currently done at load time for an
       unknown reason. */

    sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
    name = (char *) symtab_section->link->data + sym->st_name;

    if (s1->dynsym) {
    if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL) {
        /* Hack alarm.  We don't want to emit dynamic symbols
           and symbol based relocs for STB_LOCAL symbols, but rather
           want to resolve them directly.  At this point the symbol
           values aren't final yet, so we must defer this.  We will later
           have to create a RELATIVE reloc anyway, so we misuse the
           relocation slot to smuggle the symbol reference until
           fill_local_got_entries.  Not that the sym_index is
           relative to symtab_section, not s1->dynsym!  Nevertheless
           we use s1->dyn_sym so that if this is the first call
           that got->reloc is correctly created.  Also note that
           RELATIVE relocs are not normally created for the .got,
           so the types serves as a marker for later (and is retained
           also for the final output, which is okay because then the
           got is just normal data).  */
        put_elf_reloc(s1->dynsym, s1->got, got_offset, R_RELATIVE,
              sym_index);
    } else {
        if (0 == attr->dyn_index)
                attr->dyn_index = set_elf_sym(s1->dynsym, sym->st_value, size,
                          info, 0, sym->st_shndx, name);
        put_elf_reloc(s1->dynsym, s1->got, got_offset, dyn_reloc_type,
              attr->dyn_index);
    }
    } else {
        put_elf_reloc(symtab_section, s1->got, got_offset, dyn_reloc_type,
                      sym_index);
    }

    if (need_plt_entry) {
        if (!s1->plt) {
            s1->plt = new_section(s1, ".plt", SHT_PROGBITS,
                          SHF_ALLOC | SHF_EXECINSTR);
            s1->plt->sh_entsize = 4;
        }

        attr->plt_offset = create_plt_entry(s1, got_offset, attr);

        /* create a symbol 'sym@plt' for the PLT jump vector */
        len = strlen(name);
        if (len > sizeof plt_name - 5)
            len = sizeof plt_name - 5;
        memcpy(plt_name, name, len);
        strcpy(plt_name + len, "@plt");
        attr->plt_sym = put_elf_sym(s1->symtab, attr->plt_offset, sym->st_size,
            ELFW(ST_INFO)(STB_GLOBAL, STT_FUNC), 0, s1->plt->sh_num, plt_name);

    } else {
        attr->got_offset = got_offset;
    }

    return attr;
}

/* build GOT and PLT entries */
ST_FUNC void build_got_entries(TCCState *s1)
{
    Section *s;
    ElfW_Rel *rel;
    ElfW(Sym) *sym;
    int i, type, gotplt_entry, reloc_type, sym_index;
    struct sym_attr *attr;

    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if (s->sh_type != SHT_RELX)
            continue;
        /* no need to handle got relocations */
        if (s->link != symtab_section)
            continue;
        for_each_elem(s, 0, rel, ElfW_Rel) {
            type = ELFW(R_TYPE)(rel->r_info);
            gotplt_entry = gotplt_entry_type(type);
            sym_index = ELFW(R_SYM)(rel->r_info);
            sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];

            if (gotplt_entry == NO_GOTPLT_ENTRY) {
                continue;
            }

            /* Automatically create PLT/GOT [entry] if it is an undefined
           reference (resolved at runtime), or the symbol is absolute,
           probably created by tcc_add_symbol, and thus on 64-bit
           targets might be too far from application code.  */
            if (gotplt_entry == AUTO_GOTPLT_ENTRY) {
                if (sym->st_shndx == SHN_UNDEF) {
                    ElfW(Sym) *esym;
            int dynindex;
                    if (s1->output_type == TCC_OUTPUT_DLL && ! PCRELATIVE_DLLPLT)
                        continue;
            /* Relocations for UNDEF symbols would normally need
               to be transferred into the executable or shared object.
               If that were done AUTO_GOTPLT_ENTRY wouldn't exist.
               But TCC doesn't do that (at least for exes), so we
               need to resolve all such relocs locally.  And that
               means PLT slots for functions in DLLs and COPY relocs for
               data symbols.  COPY relocs were generated in
               bind_exe_dynsyms (and the symbol adjusted to be defined),
               and for functions we were generated a dynamic symbol
               of function type.  */
            if (s1->dynsym) {
            /* dynsym isn't set for -run :-/  */
            dynindex = get_sym_attr(s1, sym_index, 0)->dyn_index;
            esym = (ElfW(Sym) *)s1->dynsym->data + dynindex;
            if (dynindex
                && (ELFW(ST_TYPE)(esym->st_info) == STT_FUNC
                || (ELFW(ST_TYPE)(esym->st_info) == STT_NOTYPE
                    && ELFW(ST_TYPE)(sym->st_info) == STT_FUNC)))
                goto jmp_slot;
            }
                } else if (!(sym->st_shndx == SHN_ABS
#ifndef TCC_TARGET_ARM
            && PTR_SIZE == 8
#endif
            ))
                    continue;
            }

#ifdef TCC_TARGET_X86_64
            if ((type == R_X86_64_PLT32 || type == R_X86_64_PC32) &&
                (ELFW(ST_VISIBILITY)(sym->st_other) != STV_DEFAULT ||
         ELFW(ST_BIND)(sym->st_info) == STB_LOCAL)) {
                rel->r_info = ELFW(R_INFO)(sym_index, R_X86_64_PC32);
                continue;
            }
#endif
            if (code_reloc(type)) {
            jmp_slot:
                reloc_type = R_JMP_SLOT;
            } else
                reloc_type = R_GLOB_DAT;

            if (!s1->got)
                build_got(s1);

            if (gotplt_entry == BUILD_GOT_ONLY)
                continue;

            attr = put_got_entry(s1, reloc_type, sym->st_size, sym->st_info,
                                 sym_index);

            if (reloc_type == R_JMP_SLOT)
                rel->r_info = ELFW(R_INFO)(attr->plt_sym, type);
        }
    }
}

/* put dynamic tag */
static void put_dt(Section *dynamic, int dt, addr_t val)
{
    ElfW(Dyn) *dyn;
    dyn = section_ptr_add(dynamic, sizeof(ElfW(Dyn)));
    dyn->d_tag = dt;
    dyn->d_un.d_val = val;
}

#ifndef TCC_TARGET_PE
static void add_init_array_defines(TCCState *s1, const char *section_name)
{
    Section *s;
    long end_offset;
    char sym_start[1024];
    char sym_end[1024];

    snprintf(sym_start, sizeof(sym_start), "__%s_start", section_name + 1);
    snprintf(sym_end, sizeof(sym_end), "__%s_end", section_name + 1);

    s = find_section(s1, section_name);
    if (!s) {
        end_offset = 0;
        s = data_section;
    } else {
        end_offset = s->data_offset;
    }

    set_elf_sym(symtab_section,
                0, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                s->sh_num, sym_start);
    set_elf_sym(symtab_section,
                end_offset, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                s->sh_num, sym_end);
}
#endif

static int tcc_add_support(TCCState *s1, const char *filename)
{
    char buf[1024];
    snprintf(buf, sizeof(buf), "%s/%s", s1->tcc_lib_path, filename);
    return tcc_add_file(s1, buf);
}

ST_FUNC void tcc_add_bcheck(TCCState *s1)
{
#ifdef CONFIG_TCC_BCHECK
    addr_t *ptr;
    int sym_index;

    if (0 == s1->do_bounds_check)
        return;
    /* XXX: add an object file to do that */
    ptr = section_ptr_add(bounds_section, sizeof(*ptr));
    *ptr = 0;
    set_elf_sym(symtab_section, 0, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                bounds_section->sh_num, "__bounds_start");
    /* pull bcheck.o from libtcc1.a */
    sym_index = set_elf_sym(symtab_section, 0, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                SHN_UNDEF, "__bound_init");
    if (s1->output_type != TCC_OUTPUT_MEMORY) {
        /* add 'call __bound_init()' in .init section */
        Section *init_section = find_section(s1, ".init");
        unsigned char *pinit = section_ptr_add(init_section, 5);
        pinit[0] = 0xe8;
        write32le(pinit + 1, -4);
        put_elf_reloc(symtab_section, init_section,
            init_section->data_offset - 4, R_386_PC32, sym_index);
            /* R_386_PC32 = R_X86_64_PC32 = 2 */
    }
#endif
}

/* add tcc runtime libraries */
ST_FUNC void tcc_add_runtime(TCCState *s1)
{
    tcc_add_bcheck(s1);
    tcc_add_pragma_libs(s1);
    /* add libc */
    if (!s1->nostdlib) {
        tcc_add_library_err(s1, "c");
#ifdef TCC_LIBGCC
        if (!s1->static_link) {
            if (TCC_LIBGCC[0] == '/')
                tcc_add_file(s1, TCC_LIBGCC);
            else
                tcc_add_dll(s1, TCC_LIBGCC, 0);
        }
#endif
        tcc_add_support(s1, TCC_LIBTCC1);
        /* add crt end if not memory output */
        if (s1->output_type != TCC_OUTPUT_MEMORY)
            tcc_add_crt(s1, "crtn.o");
    }
}

/* add various standard linker symbols (must be done after the
   sections are filled (for example after allocating common
   symbols)) */
static void tcc_add_linker_symbols(TCCState *s1)
{
    char buf[1024];
    int i;
    Section *s;

    set_elf_sym(symtab_section,
                text_section->data_offset, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                text_section->sh_num, "_etext");
    set_elf_sym(symtab_section,
                data_section->data_offset, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                data_section->sh_num, "_edata");
    set_elf_sym(symtab_section,
                bss_section->data_offset, 0,
                ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                bss_section->sh_num, "_end");
#ifndef TCC_TARGET_PE
    /* horrible new standard ldscript defines */
    add_init_array_defines(s1, ".preinit_array");
    add_init_array_defines(s1, ".init_array");
    add_init_array_defines(s1, ".fini_array");
#endif

    /* add start and stop symbols for sections whose name can be
       expressed in C */
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if (s->sh_type == SHT_PROGBITS &&
            (s->sh_flags & SHF_ALLOC)) {
            const char *p;
            int ch;

            /* check if section name can be expressed in C */
            p = s->name;
            for(;;) {
                ch = *p;
                if (!ch)
                    break;
                if (!isid(ch) && !isnum(ch))
                    goto next_sec;
                p++;
            }
            snprintf(buf, sizeof(buf), "__start_%s", s->name);
            set_elf_sym(symtab_section,
                        0, 0,
                        ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                        s->sh_num, buf);
            snprintf(buf, sizeof(buf), "__stop_%s", s->name);
            set_elf_sym(symtab_section,
                        s->data_offset, 0,
                        ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
                        s->sh_num, buf);
        }
    next_sec: ;
    }
}

ST_FUNC void resolve_common_syms(TCCState *s1)
{
    ElfW(Sym) *sym;

    /* Allocate common symbols in BSS.  */
    for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
        if (sym->st_shndx == SHN_COMMON) {
            /* symbol alignment is in st_value for SHN_COMMONs */
        sym->st_value = section_add(bss_section, sym->st_size,
                    sym->st_value);
            sym->st_shndx = bss_section->sh_num;
        }
    }

    /* Now assign linker provided symbols their value.  */
    tcc_add_linker_symbols(s1);
}

static void tcc_output_binary(TCCState *s1, FILE *f,
                              const int *sec_order)
{
    Section *s;
    int i, offset, size;

    offset = 0;
    for(i=1;i<s1->nb_sections;i++) {
        s = s1->sections[sec_order[i]];
        if (s->sh_type != SHT_NOBITS &&
            (s->sh_flags & SHF_ALLOC)) {
            while (offset < s->sh_offset) {
                fputc(0, f);
                offset++;
            }
            size = s->sh_size;
            fwrite(s->data, 1, size, f);
            offset += size;
        }
    }
}

ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel)
{
    int sym_index = ELFW(R_SYM) (rel->r_info);
    ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
    struct sym_attr *attr = get_sym_attr(s1, sym_index, 0);
    unsigned offset = attr->got_offset;

    if (0 == offset)
        return;
    section_reserve(s1->got, offset + PTR_SIZE);
#ifdef TCC_TARGET_X86_64
    write64le(s1->got->data + offset, sym->st_value);
#else
    write32le(s1->got->data + offset, sym->st_value);
#endif
}

/* Perform relocation to GOT or PLT entries */
ST_FUNC void fill_got(TCCState *s1)
{
    Section *s;
    ElfW_Rel *rel;
    int i;

    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if (s->sh_type != SHT_RELX)
            continue;
        /* no need to handle got relocations */
        if (s->link != symtab_section)
            continue;
        for_each_elem(s, 0, rel, ElfW_Rel) {
            switch (ELFW(R_TYPE) (rel->r_info)) {
                case R_X86_64_GOT32:
                case R_X86_64_GOTPCREL:
        case R_X86_64_GOTPCRELX:
        case R_X86_64_REX_GOTPCRELX:
                case R_X86_64_PLT32:
                    fill_got_entry(s1, rel);
                    break;
            }
        }
    }
}

/* See put_got_entry for a description.  This is the second stage
   where GOT references to local defined symbols are rewritten.  */
static void fill_local_got_entries(TCCState *s1)
{
    ElfW_Rel *rel;
    for_each_elem(s1->got->reloc, 0, rel, ElfW_Rel) {
    if (ELFW(R_TYPE)(rel->r_info) == R_RELATIVE) {
        int sym_index = ELFW(R_SYM) (rel->r_info);
        ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
        struct sym_attr *attr = get_sym_attr(s1, sym_index, 0);
        unsigned offset = attr->got_offset;
        if (offset != rel->r_offset - s1->got->sh_addr)
          tcc_error_noabort("huh");
        rel->r_info = ELFW(R_INFO)(0, R_RELATIVE);
#if SHT_RELX == SHT_RELA
        rel->r_addend = sym->st_value;
#else
        /* All our REL architectures also happen to be 32bit LE.  */
        write32le(s1->got->data + offset, sym->st_value);
#endif
    }
    }
}

/* Bind symbols of executable: resolve undefined symbols from exported symbols
   in shared libraries and export non local defined symbols to shared libraries
   if -rdynamic switch was given on command line */
static void bind_exe_dynsyms(TCCState *s1)
{
    const char *name;
    int sym_index, index;
    ElfW(Sym) *sym, *esym;
    int type;

    /* Resolve undefined symbols from dynamic symbols. When there is a match:
       - if STT_FUNC or STT_GNU_IFUNC symbol -> add it in PLT
       - if STT_OBJECT symbol -> add it in .bss section with suitable reloc */
    for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
        if (sym->st_shndx == SHN_UNDEF) {
            name = (char *) symtab_section->link->data + sym->st_name;
            sym_index = find_elf_sym(s1->dynsymtab_section, name);
            if (sym_index) {
                esym = &((ElfW(Sym) *)s1->dynsymtab_section->data)[sym_index];
                type = ELFW(ST_TYPE)(esym->st_info);
                if ((type == STT_FUNC) || (type == STT_GNU_IFUNC)) {
                    /* Indirect functions shall have STT_FUNC type in executable
                     * dynsym section. Indeed, a dlsym call following a lazy
                     * resolution would pick the symbol value from the
                     * executable dynsym entry which would contain the address
                     * of the function wanted by the caller of dlsym instead of
                     * the address of the function that would return that
                     * address */
                    int dynindex
              = put_elf_sym(s1->dynsym, 0, esym->st_size,
                    ELFW(ST_INFO)(STB_GLOBAL,STT_FUNC), 0, 0,
                    name);
            int index = sym - (ElfW(Sym) *) symtab_section->data;
            get_sym_attr(s1, index, 1)->dyn_index = dynindex;
                } else if (type == STT_OBJECT) {
                    unsigned long offset;
                    ElfW(Sym) *dynsym;
                    offset = bss_section->data_offset;
                    /* XXX: which alignment ? */
                    offset = (offset + 16 - 1) & -16;
                    set_elf_sym (s1->symtab, offset, esym->st_size,
                                 esym->st_info, 0, bss_section->sh_num, name);
                    index = put_elf_sym(s1->dynsym, offset, esym->st_size,
                                        esym->st_info, 0, bss_section->sh_num,
                                        name);

                    /* Ensure R_COPY works for weak symbol aliases */
                    if (ELFW(ST_BIND)(esym->st_info) == STB_WEAK) {
                        for_each_elem(s1->dynsymtab_section, 1, dynsym, ElfW(Sym)) {
                            if ((dynsym->st_value == esym->st_value)
                                && (ELFW(ST_BIND)(dynsym->st_info) == STB_GLOBAL)) {
                                char *dynname = (char *) s1->dynsymtab_section->link->data
                                                + dynsym->st_name;
                                put_elf_sym(s1->dynsym, offset, dynsym->st_size,
                                            dynsym->st_info, 0,
                                            bss_section->sh_num, dynname);
                                break;
                            }
                        }
                    }

                    put_elf_reloc(s1->dynsym, bss_section,
                                  offset, R_COPY, index);
                    offset += esym->st_size;
                    bss_section->data_offset = offset;
                }
            } else {
                /* STB_WEAK undefined symbols are accepted */
                /* XXX: _fp_hw seems to be part of the ABI, so we ignore it */
                if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK ||
                    !strcmp(name, "_fp_hw")) {
                } else {
                    tcc_error_noabort("undefined symbol '%s'", name);
                }
            }
        } else if (s1->rdynamic && ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
            /* if -rdynamic option, then export all non local symbols */
            name = (char *) symtab_section->link->data + sym->st_name;
            set_elf_sym(s1->dynsym, sym->st_value, sym->st_size, sym->st_info,
                        0, sym->st_shndx, name);
        }
    }
}

/* Bind symbols of libraries: export all non local symbols of executable that
   are referenced by shared libraries. The reason is that the dynamic loader
   search symbol first in executable and then in libraries. Therefore a
   reference to a symbol already defined by a library can still be resolved by
   a symbol in the executable. */
static void bind_libs_dynsyms(TCCState *s1)
{
    const char *name;
    int sym_index;
    ElfW(Sym) *sym, *esym;

    for_each_elem(s1->dynsymtab_section, 1, esym, ElfW(Sym)) {
        name = (char *) s1->dynsymtab_section->link->data + esym->st_name;
        sym_index = find_elf_sym(symtab_section, name);
        sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
        if (sym_index && sym->st_shndx != SHN_UNDEF
            && ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
            set_elf_sym(s1->dynsym, sym->st_value, sym->st_size,
                sym->st_info, 0, sym->st_shndx, name);
        } else if (esym->st_shndx == SHN_UNDEF) {
            /* weak symbols can stay undefined */
            if (ELFW(ST_BIND)(esym->st_info) != STB_WEAK)
                tcc_warning("undefined dynamic symbol '%s'", name);
        }
    }
}

/* Export all non local symbols. This is used by shared libraries so that the
   non local symbols they define can resolve a reference in another shared
   library or in the executable. Correspondingly, it allows undefined local
   symbols to be resolved by other shared libraries or by the executable. */
static void export_global_syms(TCCState *s1)
{
    int dynindex, index;
    const char *name;
    ElfW(Sym) *sym;

    for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
        if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
        name = (char *) symtab_section->link->data + sym->st_name;
        dynindex = put_elf_sym(s1->dynsym, sym->st_value, sym->st_size,
                   sym->st_info, 0, sym->st_shndx, name);
        index = sym - (ElfW(Sym) *) symtab_section->data;
            get_sym_attr(s1, index, 1)->dyn_index = dynindex;
        }
    }
}

/* Allocate strings for section names and decide if an unallocated section
   should be output.
   NOTE: the strsec section comes last, so its size is also correct ! */
static int alloc_sec_names(TCCState *s1, int file_type, Section *strsec)
{
    int i;
    Section *s;
    int textrel = 0;

    /* Allocate strings for section names */
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        /* when generating a DLL, we include relocations but we may
           patch them */
        if (file_type == TCC_OUTPUT_DLL &&
            s->sh_type == SHT_RELX &&
            !(s->sh_flags & SHF_ALLOC) &&
            (s1->sections[s->sh_info]->sh_flags & SHF_ALLOC) &&
            prepare_dynamic_rel(s1, s)) {
                if (s1->sections[s->sh_info]->sh_flags & SHF_EXECINSTR)
                    textrel = 1;
        } else if (s1->do_debug ||
            file_type == TCC_OUTPUT_OBJ ||
            (s->sh_flags & SHF_ALLOC) ||
        i == (s1->nb_sections - 1)) {
            /* we output all sections if debug or object file */
            s->sh_size = s->data_offset;
        }
    if (s->sh_size || (s->sh_flags & SHF_ALLOC))
            s->sh_name = put_elf_str(strsec, s->name);
    }
    strsec->sh_size = strsec->data_offset;
    return textrel;
}

/* Info to be copied in dynamic section */
struct dyn_inf {
    Section *dynamic;
    Section *dynstr;
    unsigned long data_offset;
    addr_t rel_addr;
    addr_t rel_size;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
    addr_t bss_addr;
    addr_t bss_size;
#endif
};

/* Assign sections to segments and decide how are sections laid out when loaded
   in memory. This function also fills corresponding program headers. */
static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum,
                           Section *interp, Section* strsec,
                           struct dyn_inf *dyninf, int *sec_order)
{
    int i, j, k, file_type, sh_order_index, file_offset;
    unsigned long s_align;
    long long tmp;
    addr_t addr;
    ElfW(Phdr) *ph;
    Section *s;

    file_type = s1->output_type;
    sh_order_index = 1;
    file_offset = 0;
    if (s1->output_format == TCC_OUTPUT_FORMAT_ELF)
        file_offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr));
    s_align = ELF_PAGE_SIZE;
    if (s1->section_align)
        s_align = s1->section_align;

    if (phnum > 0) {
        if (s1->has_text_addr) {
            int a_offset, p_offset;
            addr = s1->text_addr;
            /* we ensure that (addr % ELF_PAGE_SIZE) == file_offset %
               ELF_PAGE_SIZE */
            a_offset = (int) (addr & (s_align - 1));
            p_offset = file_offset & (s_align - 1);
            if (a_offset < p_offset)
                a_offset += s_align;
            file_offset += (a_offset - p_offset);
        } else {
            if (file_type == TCC_OUTPUT_DLL)
                addr = 0;
            else
                addr = ELF_START_ADDR;
            /* compute address after headers */
            addr += (file_offset & (s_align - 1));
        }

        ph = &phdr[0];
        /* Leave one program headers for the program interpreter and one for
           the program header table itself if needed. These are done later as
           they require section layout to be done first. */
        if (interp)
            ph += 2;

        /* dynamic relocation table information, for .dynamic section */
        dyninf->rel_addr = dyninf->rel_size = 0;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
        dyninf->bss_addr = dyninf->bss_size = 0;
#endif

        for(j = 0; j < 2; j++) {
            ph->p_type = PT_LOAD;
            if (j == 0)
                ph->p_flags = PF_R | PF_X;
            else
                ph->p_flags = PF_R | PF_W;
            ph->p_align = s_align;

            /* Decide the layout of sections loaded in memory. This must
               be done before program headers are filled since they contain
               info about the layout. We do the following ordering: interp,
               symbol tables, relocations, progbits, nobits */
            /* XXX: do faster and simpler sorting */
            for(k = 0; k < 5; k++) {
                for(i = 1; i < s1->nb_sections; i++) {
                    s = s1->sections[i];
                    /* compute if section should be included */
                    if (j == 0) {
                        if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
                            SHF_ALLOC)
                            continue;
                    } else {
                        if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
                            (SHF_ALLOC | SHF_WRITE))
                            continue;
                    }
                    if (s == interp) {
                        if (k != 0)
                            continue;
                    } else if (s->sh_type == SHT_DYNSYM ||
                               s->sh_type == SHT_STRTAB ||
                               s->sh_type == SHT_HASH) {
                        if (k != 1)
                            continue;
                    } else if (s->sh_type == SHT_RELX) {
                        if (k != 2)
                            continue;
                    } else if (s->sh_type == SHT_NOBITS) {
                        if (k != 4)
                            continue;
                    } else {
                        if (k != 3)
                            continue;
                    }
                    sec_order[sh_order_index++] = i;

                    /* section matches: we align it and add its size */
                    tmp = addr;
                    addr = (addr + s->sh_addralign - 1) &
                        ~(s->sh_addralign - 1);
                    file_offset += (int) ( addr - tmp );
                    s->sh_offset = file_offset;
                    s->sh_addr = addr;

                    /* update program header infos */
                    if (ph->p_offset == 0) {
                        ph->p_offset = file_offset;
                        ph->p_vaddr = addr;
                        ph->p_paddr = ph->p_vaddr;
                    }
                    /* update dynamic relocation infos */
                    if (s->sh_type == SHT_RELX) {
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
                        if (!strcmp(strsec->data + s->sh_name, ".rel.got")) {
                            dyninf->rel_addr = addr;
                            dyninf->rel_size += s->sh_size; /* XXX only first rel. */
                        }
                        if (!strcmp(strsec->data + s->sh_name, ".rel.bss")) {
                            dyninf->bss_addr = addr;
                            dyninf->bss_size = s->sh_size; /* XXX only first rel. */
                        }
#else
                        if (dyninf->rel_size == 0)
                            dyninf->rel_addr = addr;
                        dyninf->rel_size += s->sh_size;
#endif
                    }
                    addr += s->sh_size;
                    if (s->sh_type != SHT_NOBITS)
                        file_offset += s->sh_size;
                }
            }
        if (j == 0) {
        /* Make the first PT_LOAD segment include the program
           headers itself (and the ELF header as well), it'll
           come out with same memory use but will make various
           tools like binutils strip work better.  */
        ph->p_offset &= ~(ph->p_align - 1);
        ph->p_vaddr &= ~(ph->p_align - 1);
        ph->p_paddr &= ~(ph->p_align - 1);
        }
            ph->p_filesz = file_offset - ph->p_offset;
            ph->p_memsz = addr - ph->p_vaddr;
            ph++;
            if (j == 0) {
                if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) {
                    /* if in the middle of a page, we duplicate the page in
                       memory so that one copy is RX and the other is RW */
                    if ((addr & (s_align - 1)) != 0)
                        addr += s_align;
                } else {
                    addr = (addr + s_align - 1) & ~(s_align - 1);
                    file_offset = (file_offset + s_align - 1) & ~(s_align - 1);
                }
            }
        }
    }

    /* all other sections come after */
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if (phnum > 0 && (s->sh_flags & SHF_ALLOC))
            continue;
        sec_order[sh_order_index++] = i;

        file_offset = (file_offset + s->sh_addralign - 1) &
            ~(s->sh_addralign - 1);
        s->sh_offset = file_offset;
        if (s->sh_type != SHT_NOBITS)
            file_offset += s->sh_size;
    }

    return file_offset;
}

static void fill_unloadable_phdr(ElfW(Phdr) *phdr, int phnum, Section *interp,
                                 Section *dynamic)
{
    ElfW(Phdr) *ph;

    /* if interpreter, then add corresponding program header */
    if (interp) {
        ph = &phdr[0];

        ph->p_type = PT_PHDR;
        ph->p_offset = sizeof(ElfW(Ehdr));
        ph->p_filesz = ph->p_memsz = phnum * sizeof(ElfW(Phdr));
        ph->p_vaddr = interp->sh_addr - ph->p_filesz;
        ph->p_paddr = ph->p_vaddr;
        ph->p_flags = PF_R | PF_X;
        ph->p_align = 4; /* interp->sh_addralign; */
        ph++;

        ph->p_type = PT_INTERP;
        ph->p_offset = interp->sh_offset;
        ph->p_vaddr = interp->sh_addr;
        ph->p_paddr = ph->p_vaddr;
        ph->p_filesz = interp->sh_size;
        ph->p_memsz = interp->sh_size;
        ph->p_flags = PF_R;
        ph->p_align = interp->sh_addralign;
    }

    /* if dynamic section, then add corresponding program header */
    if (dynamic) {
        ph = &phdr[phnum - 1];

        ph->p_type = PT_DYNAMIC;
        ph->p_offset = dynamic->sh_offset;
        ph->p_vaddr = dynamic->sh_addr;
        ph->p_paddr = ph->p_vaddr;
        ph->p_filesz = dynamic->sh_size;
        ph->p_memsz = dynamic->sh_size;
        ph->p_flags = PF_R | PF_W;
        ph->p_align = dynamic->sh_addralign;
    }
}

/* Fill the dynamic section with tags describing the address and size of
   sections */
static void fill_dynamic(TCCState *s1, struct dyn_inf *dyninf)
{
    Section *dynamic = dyninf->dynamic;

    /* put dynamic section entries */
    put_dt(dynamic, DT_HASH, s1->dynsym->hash->sh_addr);
    put_dt(dynamic, DT_STRTAB, dyninf->dynstr->sh_addr);
    put_dt(dynamic, DT_SYMTAB, s1->dynsym->sh_addr);
    put_dt(dynamic, DT_STRSZ, dyninf->dynstr->data_offset);
    put_dt(dynamic, DT_SYMENT, sizeof(ElfW(Sym)));
#if PTR_SIZE == 8
    put_dt(dynamic, DT_RELA, dyninf->rel_addr);
    put_dt(dynamic, DT_RELASZ, dyninf->rel_size);
    put_dt(dynamic, DT_RELAENT, sizeof(ElfW_Rel));
#else
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
    put_dt(dynamic, DT_PLTGOT, s1->got->sh_addr);
    put_dt(dynamic, DT_PLTRELSZ, dyninf->rel_size);
    put_dt(dynamic, DT_JMPREL, dyninf->rel_addr);
    put_dt(dynamic, DT_PLTREL, DT_REL);
    put_dt(dynamic, DT_REL, dyninf->bss_addr);
    put_dt(dynamic, DT_RELSZ, dyninf->bss_size);
#else
    put_dt(dynamic, DT_REL, dyninf->rel_addr);
    put_dt(dynamic, DT_RELSZ, dyninf->rel_size);
    put_dt(dynamic, DT_RELENT, sizeof(ElfW_Rel));
#endif
#endif
    if (s1->do_debug)
        put_dt(dynamic, DT_DEBUG, 0);
    put_dt(dynamic, DT_NULL, 0);
}

/* Relocate remaining sections and symbols (that is those not related to
   dynamic linking) */
static int final_sections_reloc(TCCState *s1)
{
    int i;
    Section *s;

    relocate_syms(s1, s1->symtab, 0);

    if (s1->nb_errors != 0)
        return -1;

    /* relocate sections */
    /* XXX: ignore sections with allocated relocations ? */
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
#if defined(TCC_TARGET_I386) || defined(TCC_MUSL)
        if (s->reloc && s != s1->got && (s->sh_flags & SHF_ALLOC)) //gr
        /* On X86 gdb 7.3 works in any case but gdb 6.6 will crash if SHF_ALLOC
        checking is removed */
#else
        if (s->reloc && s != s1->got)
        /* On X86_64 gdb 7.3 will crash if SHF_ALLOC checking is present */
#endif
            relocate_section(s1, s);
    }

    /* relocate relocation entries if the relocation tables are
       allocated in the executable */
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if ((s->sh_flags & SHF_ALLOC) &&
            s->sh_type == SHT_RELX) {
            relocate_rel(s1, s);
        }
    }
    return 0;
}

/* Create an ELF file on disk.
   This function handle ELF specific layout requirements */
static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr,
                           int file_offset, int *sec_order)
{
    int i, shnum, offset, size, file_type;
    Section *s;
    ElfW(Ehdr) ehdr;
    ElfW(Shdr) shdr, *sh;

    file_type = s1->output_type;
    shnum = s1->nb_sections;

    memset(&ehdr, 0, sizeof(ehdr));

    if (phnum > 0) {
        ehdr.e_phentsize = sizeof(ElfW(Phdr));
        ehdr.e_phnum = phnum;
        ehdr.e_phoff = sizeof(ElfW(Ehdr));
    }

    /* align to 4 */
    file_offset = (file_offset + 3) & -4;

    /* fill header */
    ehdr.e_ident[0] = ELFMAG0;
    ehdr.e_ident[1] = ELFMAG1;
    ehdr.e_ident[2] = ELFMAG2;
    ehdr.e_ident[3] = ELFMAG3;
    ehdr.e_ident[4] = ELFCLASSW;
    ehdr.e_ident[5] = ELFDATA2LSB;
    ehdr.e_ident[6] = EV_CURRENT;
#if !defined(TCC_TARGET_PE) && (defined(__FreeBSD__) || defined(__FreeBSD_kernel__))
    /* FIXME: should set only for freebsd _target_, but we exclude only PE target */
    ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
#endif
#ifdef TCC_TARGET_ARM
#ifdef TCC_ARM_EABI
    ehdr.e_ident[EI_OSABI] = 0;
    ehdr.e_flags = EF_ARM_EABI_VER4;
    if (file_type == TCC_OUTPUT_EXE || file_type == TCC_OUTPUT_DLL)
        ehdr.e_flags |= EF_ARM_HASENTRY;
    if (s1->float_abi == ARM_HARD_FLOAT)
        ehdr.e_flags |= EF_ARM_VFP_FLOAT;
    else
        ehdr.e_flags |= EF_ARM_SOFT_FLOAT;
#else
    ehdr.e_ident[EI_OSABI] = ELFOSABI_ARM;
#endif
#endif
    switch(file_type) {
    default:
    case TCC_OUTPUT_EXE:
        ehdr.e_type = ET_EXEC;
        ehdr.e_entry = get_elf_sym_addr(s1, "_start", 1);
        break;
    case TCC_OUTPUT_DLL:
        ehdr.e_type = ET_DYN;
        ehdr.e_entry = text_section->sh_addr; /* XXX: is it correct ? */
        break;
    case TCC_OUTPUT_OBJ:
        ehdr.e_type = ET_REL;
        break;
    }
    ehdr.e_machine = EM_TCC_TARGET;
    ehdr.e_version = EV_CURRENT;
    ehdr.e_shoff = file_offset;
    ehdr.e_ehsize = sizeof(ElfW(Ehdr));
    ehdr.e_shentsize = sizeof(ElfW(Shdr));
    ehdr.e_shnum = shnum;
    ehdr.e_shstrndx = shnum - 1;

    fwrite(&ehdr, 1, sizeof(ElfW(Ehdr)), f);
    fwrite(phdr, 1, phnum * sizeof(ElfW(Phdr)), f);
    offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr));

    sort_syms(s1, symtab_section);
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[sec_order[i]];
        if (s->sh_type != SHT_NOBITS) {
            while (offset < s->sh_offset) {
                fputc(0, f);
                offset++;
            }
            size = s->sh_size;
            if (size)
                fwrite(s->data, 1, size, f);
            offset += size;
        }
    }

    /* output section headers */
    while (offset < ehdr.e_shoff) {
        fputc(0, f);
        offset++;
    }

    for(i = 0; i < s1->nb_sections; i++) {
        sh = &shdr;
        memset(sh, 0, sizeof(ElfW(Shdr)));
        s = s1->sections[i];
        if (s) {
            sh->sh_name = s->sh_name;
            sh->sh_type = s->sh_type;
            sh->sh_flags = s->sh_flags;
            sh->sh_entsize = s->sh_entsize;
            sh->sh_info = s->sh_info;
            if (s->link)
                sh->sh_link = s->link->sh_num;
            sh->sh_addralign = s->sh_addralign;
            sh->sh_addr = s->sh_addr;
            sh->sh_offset = s->sh_offset;
            sh->sh_size = s->sh_size;
        }
        fwrite(sh, 1, sizeof(ElfW(Shdr)), f);
    }
}

/* Write an elf, coff or "binary" file */
static int tcc_write_elf_file(TCCState *s1, const char *filename, int phnum,
                              ElfW(Phdr) *phdr, int file_offset, int *sec_order)
{
    int fd, mode, file_type;
    FILE *f;

    file_type = s1->output_type;
    if (file_type == TCC_OUTPUT_OBJ)
        mode = 0666;
    else
        mode = 0777;
    unlink(filename);
    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode);
    if (fd < 0) {
        tcc_error_noabort("could not write '%s'", filename);
        return -1;
    }
    f = fdopen(fd, "wb");
    if (s1->verbose)
        printf("<- %s\n", filename);

#ifdef TCC_TARGET_COFF
    if (s1->output_format == TCC_OUTPUT_FORMAT_COFF)
        tcc_output_coff(s1, f);
    else
#endif
    if (s1->output_format == TCC_OUTPUT_FORMAT_ELF)
        tcc_output_elf(s1, f, phnum, phdr, file_offset, sec_order);
    else
        tcc_output_binary(s1, f, sec_order);
    fclose(f);

    return 0;
}

/* Sort section headers by assigned sh_addr, remove sections
   that we aren't going to output.  */
static void tidy_section_headers(TCCState *s1, int *sec_order)
{
    int i, nnew, l, *backmap;
    Section **snew, *s;
    ElfW(Sym) *sym;

    snew = tcc_malloc(s1->nb_sections * sizeof(snew[0]));
    backmap = tcc_malloc(s1->nb_sections * sizeof(backmap[0]));
    for (i = 0, nnew = 0, l = s1->nb_sections; i < s1->nb_sections; i++) {
    s = s1->sections[sec_order[i]];
    if (!i || s->sh_name) {
        backmap[sec_order[i]] = nnew;
        snew[nnew] = s;
        ++nnew;
    } else {
        backmap[sec_order[i]] = 0;
        snew[--l] = s;
    }
    }
    for (i = 0; i < nnew; i++) {
    s = snew[i];
    if (s) {
        s->sh_num = i;
            if (s->sh_type == SHT_RELX)
        s->sh_info = backmap[s->sh_info];
    }
    }

    for_each_elem(symtab_section, 1, sym, ElfW(Sym))
    if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE)
        sym->st_shndx = backmap[sym->st_shndx];
    if( !s1->static_link ) {
        for_each_elem(s1->dynsym, 1, sym, ElfW(Sym))
        if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE)
            sym->st_shndx = backmap[sym->st_shndx];
    }
    for (i = 0; i < s1->nb_sections; i++)
    sec_order[i] = i;
    tcc_free(s1->sections);
    s1->sections = snew;
    s1->nb_sections = nnew;
    tcc_free(backmap);
}

/* Output an elf, coff or binary file */
/* XXX: suppress unneeded sections */
static int elf_output_file(TCCState *s1, const char *filename)
{
    int i, ret, phnum, shnum, file_type, file_offset, *sec_order;
    struct dyn_inf dyninf = {0};
    ElfW(Phdr) *phdr;
    ElfW(Sym) *sym;
    Section *strsec, *interp, *dynamic, *dynstr;
    int textrel;

    file_type = s1->output_type;
    s1->nb_errors = 0;
    ret = -1;
    phdr = NULL;
    sec_order = NULL;
    interp = dynamic = dynstr = NULL; /* avoid warning */
    textrel = 0;

    if (file_type != TCC_OUTPUT_OBJ) {
        /* if linking, also link in runtime libraries (libc, libgcc, etc.) */
        tcc_add_runtime(s1);
    resolve_common_syms(s1);

        if (!s1->static_link) {
            if (file_type == TCC_OUTPUT_EXE) {
                char *ptr;
                /* allow override the dynamic loader */
                const char *elfint = getenv("LD_SO");
                if (elfint == NULL)
                    elfint = DEFAULT_ELFINTERP(s1);
                /* add interpreter section only if executable */
                interp = new_section(s1, ".interp", SHT_PROGBITS, SHF_ALLOC);
                interp->sh_addralign = 1;
                ptr = section_ptr_add(interp, 1 + strlen(elfint));
                strcpy(ptr, elfint);
            }

            /* add dynamic symbol table */
            s1->dynsym = new_symtab(s1, ".dynsym", SHT_DYNSYM, SHF_ALLOC,
                                    ".dynstr",
                                    ".hash", SHF_ALLOC);
            dynstr = s1->dynsym->link;

            /* add dynamic section */
            dynamic = new_section(s1, ".dynamic", SHT_DYNAMIC,
                                  SHF_ALLOC | SHF_WRITE);
            dynamic->link = dynstr;
            dynamic->sh_entsize = sizeof(ElfW(Dyn));

            build_got(s1);

            if (file_type == TCC_OUTPUT_EXE) {
                bind_exe_dynsyms(s1);
                if (s1->nb_errors)
                    goto the_end;
                bind_libs_dynsyms(s1);
            } else {
                /* shared library case: simply export all global symbols */
                export_global_syms(s1);
            }
        }
        build_got_entries(s1);
    }

    /* we add a section for symbols */
    strsec = new_section(s1, ".shstrtab", SHT_STRTAB, 0);
    put_elf_str(strsec, "");

    /* Allocate strings for section names */
    textrel = alloc_sec_names(s1, file_type, strsec);

    if (dynamic) {
        /* add a list of needed dlls */
        for(i = 0; i < s1->nb_loaded_dlls; i++) {
            DLLReference *dllref = s1->loaded_dlls[i];
            if (dllref->level == 0)
                put_dt(dynamic, DT_NEEDED, put_elf_str(dynstr, dllref->name));
        }

        if (s1->rpath)
            put_dt(dynamic, s1->enable_new_dtags ? DT_RUNPATH : DT_RPATH,
                   put_elf_str(dynstr, s1->rpath));

        if (file_type == TCC_OUTPUT_DLL) {
            if (s1->soname)
                put_dt(dynamic, DT_SONAME, put_elf_str(dynstr, s1->soname));
            /* XXX: currently, since we do not handle PIC code, we
               must relocate the readonly segments */
            if (textrel)
                put_dt(dynamic, DT_TEXTREL, 0);
        }

        if (s1->symbolic)
            put_dt(dynamic, DT_SYMBOLIC, 0);

        dyninf.dynamic = dynamic;
        dyninf.dynstr = dynstr;
        /* remember offset and reserve space for 2nd call below */
        dyninf.data_offset = dynamic->data_offset;
        fill_dynamic(s1, &dyninf);
        dynamic->sh_size = dynamic->data_offset;
        dynstr->sh_size = dynstr->data_offset;
    }

    /* compute number of program headers */
    if (file_type == TCC_OUTPUT_OBJ)
        phnum = 0;
    else if (file_type == TCC_OUTPUT_DLL)
        phnum = 3;
    else if (s1->static_link)
        phnum = 2;
    else
        phnum = 5;

    /* allocate program segment headers */
    phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr)));

    /* compute number of sections */
    shnum = s1->nb_sections;

    /* this array is used to reorder sections in the output file */
    sec_order = tcc_malloc(sizeof(int) * shnum);
    sec_order[0] = 0;

    /* compute section to program header mapping */
    file_offset = layout_sections(s1, phdr, phnum, interp, strsec, &dyninf,
                                  sec_order);

    /* Fill remaining program header and finalize relocation related to dynamic
       linking. */
    if (file_type != TCC_OUTPUT_OBJ) {
        fill_unloadable_phdr(phdr, phnum, interp, dynamic);
        if (dynamic) {
            dynamic->data_offset = dyninf.data_offset;
            fill_dynamic(s1, &dyninf);

            /* put in GOT the dynamic section address and relocate PLT */
            write32le(s1->got->data, dynamic->sh_addr);
            if (file_type == TCC_OUTPUT_EXE
                || (RELOCATE_DLLPLT && file_type == TCC_OUTPUT_DLL))
                relocate_plt(s1);

            /* relocate symbols in .dynsym now that final addresses are known */
            for_each_elem(s1->dynsym, 1, sym, ElfW(Sym)) {
                if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE) {
                    /* do symbol relocation */
                    sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
                }
            }
        }

        /* if building executable or DLL, then relocate each section
           except the GOT which is already relocated */
        ret = final_sections_reloc(s1);
        if (ret)
            goto the_end;
    tidy_section_headers(s1, sec_order);

        /* Perform relocation to GOT or PLT entries */
        if (file_type == TCC_OUTPUT_EXE && s1->static_link)
            fill_got(s1);
        else if (s1->got)
            fill_local_got_entries(s1);
    }

    /* Create the ELF file with name 'filename' */
    ret = tcc_write_elf_file(s1, filename, phnum, phdr, file_offset, sec_order);
    s1->nb_sections = shnum;
 the_end:
    tcc_free(sec_order);
    tcc_free(phdr);
    return ret;
}

LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename)
{
    int ret;
#ifdef TCC_TARGET_PE
    if (s->output_type != TCC_OUTPUT_OBJ) {
        ret = pe_output_file(s, filename);
    } else
#endif
        ret = elf_output_file(s, filename);
    return ret;
}

static void *load_data(int fd, unsigned long file_offset, unsigned long size)
{
    void *data;

    data = tcc_malloc(size);
    lseek(fd, file_offset, SEEK_SET);
    read(fd, data, size);
    return data;
}

typedef struct SectionMergeInfo {
    Section *s;            /* corresponding existing section */
    unsigned long offset;  /* offset of the new section in the existing section */
    uint8_t new_section;       /* true if section 's' was added */
    uint8_t link_once;         /* true if link once section */
} SectionMergeInfo;

ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h)
{
    int size = read(fd, h, sizeof *h);
    if (size == sizeof *h && 0 == memcmp(h, ELFMAG, 4)) {
        if (h->e_type == ET_REL)
            return AFF_BINTYPE_REL;
        if (h->e_type == ET_DYN)
            return AFF_BINTYPE_DYN;
    } else if (size >= 8) {
        if (0 == memcmp(h, ARMAG, 8))
            return AFF_BINTYPE_AR;
#ifdef TCC_TARGET_COFF
        if (((struct filehdr*)h)->f_magic == COFF_C67_MAGIC)
            return AFF_BINTYPE_C67;
#endif
    }
    return 0;
}

/* load an object file and merge it with current files */
/* XXX: handle correctly stab (debug) info */
ST_FUNC int tcc_load_object_file(TCCState *s1,
                                int fd, unsigned long file_offset)
{
    ElfW(Ehdr) ehdr;
    ElfW(Shdr) *shdr, *sh;
    int size, i, j, offset, offseti, nb_syms, sym_index, ret, seencompressed;
    unsigned char *strsec, *strtab;
    int *old_to_new_syms;
    char *sh_name, *name;
    SectionMergeInfo *sm_table, *sm;
    ElfW(Sym) *sym, *symtab;
    ElfW_Rel *rel;
    Section *s;

    int stab_index;
    int stabstr_index;

    stab_index = stabstr_index = 0;

    lseek(fd, file_offset, SEEK_SET);
    if (tcc_object_type(fd, &ehdr) != AFF_BINTYPE_REL)
        goto fail1;
    /* test CPU specific stuff */
    if (ehdr.e_ident[5] != ELFDATA2LSB ||
        ehdr.e_machine != EM_TCC_TARGET) {
    fail1:
        tcc_error_noabort("invalid object file");
        return -1;
    }
    /* read sections */
    shdr = load_data(fd, file_offset + ehdr.e_shoff,
                     sizeof(ElfW(Shdr)) * ehdr.e_shnum);
    sm_table = tcc_mallocz(sizeof(SectionMergeInfo) * ehdr.e_shnum);

    /* load section names */
    sh = &shdr[ehdr.e_shstrndx];
    strsec = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);

    /* load symtab and strtab */
    old_to_new_syms = NULL;
    symtab = NULL;
    strtab = NULL;
    nb_syms = 0;
    seencompressed = 0;
    for(i = 1; i < ehdr.e_shnum; i++) {
        sh = &shdr[i];
        if (sh->sh_type == SHT_SYMTAB) {
            if (symtab) {
                tcc_error_noabort("object must contain only one symtab");
            fail:
                ret = -1;
                goto the_end;
            }
            nb_syms = sh->sh_size / sizeof(ElfW(Sym));
            symtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
            sm_table[i].s = symtab_section;

            /* now load strtab */
            sh = &shdr[sh->sh_link];
            strtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
        }
    if (sh->sh_flags & SHF_COMPRESSED)
        seencompressed = 1;
    }

    /* now examine each section and try to merge its content with the
       ones in memory */
    for(i = 1; i < ehdr.e_shnum; i++) {
        /* no need to examine section name strtab */
        if (i == ehdr.e_shstrndx)
            continue;
        sh = &shdr[i];
        sh_name = (char *) strsec + sh->sh_name;
        /* ignore sections types we do not handle */
        if (sh->sh_type != SHT_PROGBITS &&
            sh->sh_type != SHT_RELX &&
#ifdef TCC_ARM_EABI
            sh->sh_type != SHT_ARM_EXIDX &&
#endif
            sh->sh_type != SHT_NOBITS &&
            sh->sh_type != SHT_PREINIT_ARRAY &&
            sh->sh_type != SHT_INIT_ARRAY &&
            sh->sh_type != SHT_FINI_ARRAY &&
            strcmp(sh_name, ".stabstr")
            )
            continue;
    if (seencompressed
        && (!strncmp(sh_name, ".debug_", sizeof(".debug_")-1)
        || (sh->sh_type == SHT_RELX
            && !strncmp((char*)strsec + shdr[sh->sh_info].sh_name,
                    ".debug_", sizeof(".debug_")-1))))
      continue;
        if (sh->sh_addralign < 1)
            sh->sh_addralign = 1;
        /* find corresponding section, if any */
        for(j = 1; j < s1->nb_sections;j++) {
            s = s1->sections[j];
            if (!strcmp(s->name, sh_name)) {
                if (!strncmp(sh_name, ".gnu.linkonce",
                             sizeof(".gnu.linkonce") - 1)) {
                    /* if a 'linkonce' section is already present, we
                       do not add it again. It is a little tricky as
                       symbols can still be defined in
                       it. */
                    sm_table[i].link_once = 1;
                    goto next;
                } else {
                    goto found;
                }
            }
        }
        /* not found: create new section */
        s = new_section(s1, sh_name, sh->sh_type, sh->sh_flags & ~SHF_GROUP);
        /* take as much info as possible from the section. sh_link and
           sh_info will be updated later */
        s->sh_addralign = sh->sh_addralign;
        s->sh_entsize = sh->sh_entsize;
        sm_table[i].new_section = 1;
    found:
        if (sh->sh_type != s->sh_type) {
            tcc_error_noabort("invalid section type");
            goto fail;
        }

        /* align start of section */
        offset = s->data_offset;

        if (0 == strcmp(sh_name, ".stab")) {
            stab_index = i;
            goto no_align;
        }
        if (0 == strcmp(sh_name, ".stabstr")) {
            stabstr_index = i;
            goto no_align;
        }

        size = sh->sh_addralign - 1;
        offset = (offset + size) & ~size;
        if (sh->sh_addralign > s->sh_addralign)
            s->sh_addralign = sh->sh_addralign;
        s->data_offset = offset;
    no_align:
        sm_table[i].offset = offset;
        sm_table[i].s = s;
        /* concatenate sections */
        size = sh->sh_size;
        if (sh->sh_type != SHT_NOBITS) {
            unsigned char *ptr;
            lseek(fd, file_offset + sh->sh_offset, SEEK_SET);
            ptr = section_ptr_add(s, size);
            read(fd, ptr, size);
        } else {
            s->data_offset += size;
        }
    next: ;
    }

    /* gr relocate stab strings */
    if (stab_index && stabstr_index) {
        Stab_Sym *a, *b;
        unsigned o;
        s = sm_table[stab_index].s;
        a = (Stab_Sym *)(s->data + sm_table[stab_index].offset);
        b = (Stab_Sym *)(s->data + s->data_offset);
        o = sm_table[stabstr_index].offset;
        while (a < b)
            a->n_strx += o, a++;
    }

    /* second short pass to update sh_link and sh_info fields of new
       sections */
    for(i = 1; i < ehdr.e_shnum; i++) {
        s = sm_table[i].s;
        if (!s || !sm_table[i].new_section)
            continue;
        sh = &shdr[i];
        if (sh->sh_link > 0)
            s->link = sm_table[sh->sh_link].s;
        if (sh->sh_type == SHT_RELX) {
            s->sh_info = sm_table[sh->sh_info].s->sh_num;
            /* update backward link */
            s1->sections[s->sh_info]->reloc = s;
        }
    }
    sm = sm_table;

    /* resolve symbols */
    old_to_new_syms = tcc_mallocz(nb_syms * sizeof(int));

    sym = symtab + 1;
    for(i = 1; i < nb_syms; i++, sym++) {
        if (sym->st_shndx != SHN_UNDEF &&
            sym->st_shndx < SHN_LORESERVE) {
            sm = &sm_table[sym->st_shndx];
            if (sm->link_once) {
                /* if a symbol is in a link once section, we use the
                   already defined symbol. It is very important to get
                   correct relocations */
                if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
                    name = (char *) strtab + sym->st_name;
                    sym_index = find_elf_sym(symtab_section, name);
                    if (sym_index)
                        old_to_new_syms[i] = sym_index;
                }
                continue;
            }
            /* if no corresponding section added, no need to add symbol */
            if (!sm->s)
                continue;
            /* convert section number */
            sym->st_shndx = sm->s->sh_num;
            /* offset value */
            sym->st_value += sm->offset;
        }
        /* add symbol */
        name = (char *) strtab + sym->st_name;
        sym_index = set_elf_sym(symtab_section, sym->st_value, sym->st_size,
                                sym->st_info, sym->st_other,
                                sym->st_shndx, name);
        old_to_new_syms[i] = sym_index;
    }

    /* third pass to patch relocation entries */
    for(i = 1; i < ehdr.e_shnum; i++) {
        s = sm_table[i].s;
        if (!s)
            continue;
        sh = &shdr[i];
        offset = sm_table[i].offset;
        switch(s->sh_type) {
        case SHT_RELX:
            /* take relocation offset information */
            offseti = sm_table[sh->sh_info].offset;
            for_each_elem(s, (offset / sizeof(*rel)), rel, ElfW_Rel) {
                int type;
                unsigned sym_index;
                /* convert symbol index */
                type = ELFW(R_TYPE)(rel->r_info);
                sym_index = ELFW(R_SYM)(rel->r_info);
                /* NOTE: only one symtab assumed */
                if (sym_index >= nb_syms)
                    goto invalid_reloc;
                sym_index = old_to_new_syms[sym_index];
                /* ignore link_once in rel section. */
                if (!sym_index && !sm->link_once
#ifdef TCC_TARGET_ARM
                    && type != R_ARM_V4BX
#endif
                   ) {
                invalid_reloc:
                    tcc_error_noabort("Invalid relocation entry [%2d] '%s' @ %.8x",
                        i, strsec + sh->sh_name, rel->r_offset);
                    goto fail;
                }
                rel->r_info = ELFW(R_INFO)(sym_index, type);
                /* offset the relocation offset */
                rel->r_offset += offseti;
#ifdef TCC_TARGET_ARM
                /* Jumps and branches from a Thumb code to a PLT entry need
                   special handling since PLT entries are ARM code.
                   Unconditional bl instructions referencing PLT entries are
                   handled by converting these instructions into blx
                   instructions. Other case of instructions referencing a PLT
                   entry require to add a Thumb stub before the PLT entry to
                   switch to ARM mode. We set bit plt_thumb_stub of the
                   attribute of a symbol to indicate such a case. */
                if (type == R_ARM_THM_JUMP24)
                    get_sym_attr(s1, sym_index, 1)->plt_thumb_stub = 1;
#endif
            }
            break;
        default:
            break;
        }
    }

    ret = 0;
 the_end:
    tcc_free(symtab);
    tcc_free(strtab);
    tcc_free(old_to_new_syms);
    tcc_free(sm_table);
    tcc_free(strsec);
    tcc_free(shdr);
    return ret;
}

typedef struct ArchiveHeader {
    char ar_name[16];           /* name of this member */
    char ar_date[12];           /* file mtime */
    char ar_uid[6];             /* owner uid; printed as decimal */
    char ar_gid[6];             /* owner gid; printed as decimal */
    char ar_mode[8];            /* file mode, printed as octal   */
    char ar_size[10];           /* file size, printed as decimal */
    char ar_fmag[2];            /* should contain ARFMAG */
} ArchiveHeader;

static int get_be32(const uint8_t *b)
{
    return b[3] | (b[2] << 8) | (b[1] << 16) | (b[0] << 24);
}

static long get_be64(const uint8_t *b)
{
  long long ret = get_be32(b);
  ret = (ret << 32) | (unsigned)get_be32(b+4);
  return (long)ret;
}

/* load only the objects which resolve undefined symbols */
static int tcc_load_alacarte(TCCState *s1, int fd, int size, int entrysize)
{
    long i, bound, nsyms, sym_index, off, ret;
    uint8_t *data;
    const char *ar_names, *p;
    const uint8_t *ar_index;
    ElfW(Sym) *sym;

    data = tcc_malloc(size);
    if (read(fd, data, size) != size)
        goto fail;
    nsyms = entrysize == 4 ? get_be32(data) : get_be64(data);
    ar_index = data + entrysize;
    ar_names = (char *) ar_index + nsyms * entrysize;

    do {
        bound = 0;
        for(p = ar_names, i = 0; i < nsyms; i++, p += strlen(p)+1) {
            sym_index = find_elf_sym(symtab_section, p);
            if(sym_index) {
                sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
                if(sym->st_shndx == SHN_UNDEF) {
                    off = (entrysize == 4
               ? get_be32(ar_index + i * 4)
               : get_be64(ar_index + i * 8))
              + sizeof(ArchiveHeader);
                    ++bound;
                    if(tcc_load_object_file(s1, fd, off) < 0) {
                    fail:
                        ret = -1;
                        goto the_end;
                    }
                }
            }
        }
    } while(bound);
    ret = 0;
 the_end:
    tcc_free(data);
    return ret;
}

/* load a '.a' file */
ST_FUNC int tcc_load_archive(TCCState *s1, int fd)
{
    ArchiveHeader hdr;
    char ar_size[11];
    char ar_name[17];
    char magic[8];
    int size, len, i;
    unsigned long file_offset;

    /* skip magic which was already checked */
    read(fd, magic, sizeof(magic));

    for(;;) {
        len = read(fd, &hdr, sizeof(hdr));
        if (len == 0)
            break;
        if (len != sizeof(hdr)) {
            tcc_error_noabort("invalid archive");
            return -1;
        }
        memcpy(ar_size, hdr.ar_size, sizeof(hdr.ar_size));
        ar_size[sizeof(hdr.ar_size)] = '\0';
        size = strtol(ar_size, NULL, 0);
        memcpy(ar_name, hdr.ar_name, sizeof(hdr.ar_name));
        for(i = sizeof(hdr.ar_name) - 1; i >= 0; i--) {
            if (ar_name[i] != ' ')
                break;
        }
        ar_name[i + 1] = '\0';
        file_offset = lseek(fd, 0, SEEK_CUR);
        /* align to even */
        size = (size + 1) & ~1;
        if (!strcmp(ar_name, "/")) {
            /* coff symbol table : we handle it */
            if(s1->alacarte_link)
                return tcc_load_alacarte(s1, fd, size, 4);
    } else if (!strcmp(ar_name, "/SYM64/")) {
            if(s1->alacarte_link)
                return tcc_load_alacarte(s1, fd, size, 8);
        } else {
            ElfW(Ehdr) ehdr;
            if (tcc_object_type(fd, &ehdr) == AFF_BINTYPE_REL) {
                if (tcc_load_object_file(s1, fd, file_offset) < 0)
                    return -1;
            }
        }
        lseek(fd, file_offset + size, SEEK_SET);
    }
    return 0;
}

#ifndef TCC_TARGET_PE
/* load a DLL and all referenced DLLs. 'level = 0' means that the DLL
   is referenced by the user (so it should be added as DT_NEEDED in
   the generated ELF file) */
ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level)
{
    ElfW(Ehdr) ehdr;
    ElfW(Shdr) *shdr, *sh, *sh1;
    int i, j, nb_syms, nb_dts, sym_bind, ret;
    ElfW(Sym) *sym, *dynsym;
    ElfW(Dyn) *dt, *dynamic;
    unsigned char *dynstr;
    const char *name, *soname;
    DLLReference *dllref;

    read(fd, &ehdr, sizeof(ehdr));

    /* test CPU specific stuff */
    if (ehdr.e_ident[5] != ELFDATA2LSB ||
        ehdr.e_machine != EM_TCC_TARGET) {
        tcc_error_noabort("bad architecture");
        return -1;
    }

    /* read sections */
    shdr = load_data(fd, ehdr.e_shoff, sizeof(ElfW(Shdr)) * ehdr.e_shnum);

    /* load dynamic section and dynamic symbols */
    nb_syms = 0;
    nb_dts = 0;
    dynamic = NULL;
    dynsym = NULL; /* avoid warning */
    dynstr = NULL; /* avoid warning */
    for(i = 0, sh = shdr; i < ehdr.e_shnum; i++, sh++) {
        switch(sh->sh_type) {
        case SHT_DYNAMIC:
            nb_dts = sh->sh_size / sizeof(ElfW(Dyn));
            dynamic = load_data(fd, sh->sh_offset, sh->sh_size);
            break;
        case SHT_DYNSYM:
            nb_syms = sh->sh_size / sizeof(ElfW(Sym));
            dynsym = load_data(fd, sh->sh_offset, sh->sh_size);
            sh1 = &shdr[sh->sh_link];
            dynstr = load_data(fd, sh1->sh_offset, sh1->sh_size);
            break;
        default:
            break;
        }
    }

    /* compute the real library name */
    soname = tcc_basename(filename);

    for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
        if (dt->d_tag == DT_SONAME) {
            soname = (char *) dynstr + dt->d_un.d_val;
        }
    }

    /* if the dll is already loaded, do not load it */
    for(i = 0; i < s1->nb_loaded_dlls; i++) {
        dllref = s1->loaded_dlls[i];
        if (!strcmp(soname, dllref->name)) {
            /* but update level if needed */
            if (level < dllref->level)
                dllref->level = level;
            ret = 0;
            goto the_end;
        }
    }

    /* add the dll and its level */
    dllref = tcc_mallocz(sizeof(DLLReference) + strlen(soname));
    dllref->level = level;
    strcpy(dllref->name, soname);
    dynarray_add(&s1->loaded_dlls, &s1->nb_loaded_dlls, dllref);

    /* add dynamic symbols in dynsym_section */
    for(i = 1, sym = dynsym + 1; i < nb_syms; i++, sym++) {
        sym_bind = ELFW(ST_BIND)(sym->st_info);
        if (sym_bind == STB_LOCAL)
            continue;
        name = (char *) dynstr + sym->st_name;
        set_elf_sym(s1->dynsymtab_section, sym->st_value, sym->st_size,
                    sym->st_info, sym->st_other, sym->st_shndx, name);
    }

    /* load all referenced DLLs */
    for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
        switch(dt->d_tag) {
        case DT_NEEDED:
            name = (char *) dynstr + dt->d_un.d_val;
            for(j = 0; j < s1->nb_loaded_dlls; j++) {
                dllref = s1->loaded_dlls[j];
                if (!strcmp(name, dllref->name))
                    goto already_loaded;
            }
            if (tcc_add_dll(s1, name, AFF_REFERENCED_DLL) < 0) {
                tcc_error_noabort("referenced dll '%s' not found", name);
                ret = -1;
                goto the_end;
            }
        already_loaded:
            break;
        }
    }
    ret = 0;
 the_end:
    tcc_free(dynstr);
    tcc_free(dynsym);
    tcc_free(dynamic);
    tcc_free(shdr);
    return ret;
}

#define LD_TOK_NAME 256
#define LD_TOK_EOF  (-1)

/* return next ld script token */
static int ld_next(TCCState *s1, char *name, int name_size)
{
    int c;
    char *q;

 redo:
    switch(ch) {
    case ' ':
    case '\t':
    case '\f':
    case '\v':
    case '\r':
    case '\n':
        inp();
        goto redo;
    case '/':
        minp();
        if (ch == '*') {
            file->buf_ptr = parse_comment(file->buf_ptr);
            ch = file->buf_ptr[0];
            goto redo;
        } else {
            q = name;
            *q++ = '/';
            goto parse_name;
        }
        break;
    case '\\':
        ch = handle_eob();
        if (ch != '\\')
            goto redo;
        /* fall through */
    /* case 'a' ... 'z': */
    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 'A' ... 'z': */
    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 '.':
    case '$':
    case '~':
        q = name;
    parse_name:
        for(;;) {
            if (!((ch >= 'a' && ch <= 'z') ||
                  (ch >= 'A' && ch <= 'Z') ||
                  (ch >= '0' && ch <= '9') ||
                  strchr("/.-_+=$:\\,~", ch)))
                break;
            if ((q - name) < name_size - 1) {
                *q++ = ch;
            }
            minp();
        }
        *q = '\0';
        c = LD_TOK_NAME;
        break;
    case CH_EOF:
        c = LD_TOK_EOF;
        break;
    default:
        c = ch;
        inp();
        break;
    }
    return c;
}

static int ld_add_file(TCCState *s1, const char filename[])
{
    if (filename[0] == '/') {
        if (CONFIG_SYSROOT[0] == '\0'
            && tcc_add_file_internal(s1, filename, AFF_TYPE_BIN) == 0)
            return 0;
        filename = tcc_basename(filename);
    }
    return tcc_add_dll(s1, filename, 0);
}

static inline int new_undef_syms(void)
{
    int ret = 0;
    ret = new_undef_sym;
    new_undef_sym = 0;
    return ret;
}

static int ld_add_file_list(TCCState *s1, const char *cmd, int as_needed)
{
    char filename[1024], libname[1024];
    int t, group, nblibs = 0, ret = 0;
    char **libs = NULL;

    group = !strcmp(cmd, "GROUP");
    if (!as_needed)
        new_undef_syms();
    t = ld_next(s1, filename, sizeof(filename));
    if (t != '(')
        expect("(");
    t = ld_next(s1, filename, sizeof(filename));
    for(;;) {
        libname[0] = '\0';
        if (t == LD_TOK_EOF) {
            tcc_error_noabort("unexpected end of file");
            ret = -1;
            goto lib_parse_error;
        } else if (t == ')') {
            break;
        } else if (t == '-') {
            t = ld_next(s1, filename, sizeof(filename));
            if ((t != LD_TOK_NAME) || (filename[0] != 'l')) {
                tcc_error_noabort("library name expected");
                ret = -1;
                goto lib_parse_error;
            }
            pstrcpy(libname, sizeof libname, &filename[1]);
            if (s1->static_link) {
                snprintf(filename, sizeof filename, "lib%s.a", libname);
            } else {
                snprintf(filename, sizeof filename, "lib%s.so", libname);
            }
        } else if (t != LD_TOK_NAME) {
            tcc_error_noabort("filename expected");
            ret = -1;
            goto lib_parse_error;
        }
        if (!strcmp(filename, "AS_NEEDED")) {
            ret = ld_add_file_list(s1, cmd, 1);
            if (ret)
                goto lib_parse_error;
        } else {
            /* TODO: Implement AS_NEEDED support. Ignore it for now */
            if (!as_needed) {
                ret = ld_add_file(s1, filename);
                if (ret)
                    goto lib_parse_error;
                if (group) {
                    /* Add the filename *and* the libname to avoid future conversions */
                    dynarray_add(&libs, &nblibs, tcc_strdup(filename));
                    if (libname[0] != '\0')
                        dynarray_add(&libs, &nblibs, tcc_strdup(libname));
                }
            }
        }
        t = ld_next(s1, filename, sizeof(filename));
        if (t == ',') {
            t = ld_next(s1, filename, sizeof(filename));
        }
    }
    if (group && !as_needed) {
        while (new_undef_syms()) {
            int i;

            for (i = 0; i < nblibs; i ++)
                ld_add_file(s1, libs[i]);
        }
    }
lib_parse_error:
    dynarray_reset(&libs, &nblibs);
    return ret;
}

/* interpret a subset of GNU ldscripts to handle the dummy libc.so
   files */
ST_FUNC int tcc_load_ldscript(TCCState *s1)
{
    char cmd[64];
    char filename[1024];
    int t, ret;

    ch = handle_eob();
    for(;;) {
        t = ld_next(s1, cmd, sizeof(cmd));
        if (t == LD_TOK_EOF)
            return 0;
        else if (t != LD_TOK_NAME)
            return -1;
        if (!strcmp(cmd, "INPUT") ||
            !strcmp(cmd, "GROUP")) {
            ret = ld_add_file_list(s1, cmd, 0);
            if (ret)
                return ret;
        } else if (!strcmp(cmd, "OUTPUT_FORMAT") ||
                   !strcmp(cmd, "TARGET")) {
            /* ignore some commands */
            t = ld_next(s1, cmd, sizeof(cmd));
            if (t != '(')
                expect("(");
            for(;;) {
                t = ld_next(s1, filename, sizeof(filename));
                if (t == LD_TOK_EOF) {
                    tcc_error_noabort("unexpected end of file");
                    return -1;
                } else if (t == ')') {
                    break;
                }
            }
        } else {
            return -1;
        }
    }
    return 0;
}
#endif /* !TCC_TARGET_PE */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/fiwix-paddr.before

File: /sysa/tcc-0.9.27/simple-patches/fiwix-paddr.before used in: Step 484
                        ph->p_paddr = ph->p_vaddr;

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/fiwix-paddr.after

File: /sysa/tcc-0.9.27/simple-patches/fiwix-paddr.after used in: Step 484
                        ph->p_paddr = ph->p_vaddr;
                        if (ph->p_paddr >= 0xC0000000)
                            ph->p_paddr = ph->p_paddr - 0xC0000000;

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/check-reloc-null.before

File: /sysa/tcc-0.9.27/simple-patches/check-reloc-null.before used in: Step 485
static void fill_local_got_entries(TCCState *s1)
{
    ElfW_Rel *rel;
    for_each_elem(s1->got->reloc, 0, rel, ElfW_Rel) {

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/simple-patches/check-reloc-null.after

File: /sysa/tcc-0.9.27/simple-patches/check-reloc-null.after used in: Step 485
static void fill_local_got_entries(TCCState *s1)
{
    ElfW_Rel *rel;
    if (!s1->got->reloc)
    return;
    for_each_elem(s1->got->reloc, 0, rel, ElfW_Rel) {

https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tcc.c

File: ../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tcc.c used in: Step 488
/*
 *  TCC - Tiny C Compiler
 * 
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "tcc.h"
#if ONE_SOURCE
# include "libtcc.c"
#endif
#include "tcctools.c"

static const char help[] =
    "Tiny C Compiler "TCC_VERSION" - Copyright (C) 2001-2006 Fabrice Bellard\n"
    "Usage: tcc [options...] [-o outfile] [-c] infile(s)...\n"
    "       tcc [options...] -run infile [arguments...]\n"
    "General options:\n"
    "  -c          compile only - generate an object file\n"
    "  -o outfile  set output filename\n"
    "  -run        run compiled source\n"
    "  -fflag      set or reset (with 'no-' prefix) 'flag' (see tcc -hh)\n"
    "  -Wwarning   set or reset (with 'no-' prefix) 'warning' (see tcc -hh)\n"
    "  -w          disable all warnings\n"
    "  -v -vv      show version, show search paths or loaded files\n"
    "  -h -hh      show this, show more help\n"
    "  -bench      show compilation statistics\n"
    "  -           use stdin pipe as infile\n"
    "  @listfile   read arguments from listfile\n"
    "Preprocessor options:\n"
    "  -Idir       add include path 'dir'\n"
    "  -Dsym[=val] define 'sym' with value 'val'\n"
    "  -Usym       undefine 'sym'\n"
    "  -E          preprocess only\n"
    "Linker options:\n"
    "  -Ldir       add library path 'dir'\n"
    "  -llib       link with dynamic or static library 'lib'\n"
    "  -r          generate (relocatable) object file\n"
    "  -shared     generate a shared library/dll\n"
    "  -rdynamic   export all global symbols to dynamic linker\n"
    "  -soname     set name for shared library to be used at runtime\n"
    "  -Wl,-opt[=val]  set linker option (see tcc -hh)\n"
    "Debugger options:\n"
    "  -g          generate runtime debug info\n"
#ifdef CONFIG_TCC_BCHECK
    "  -b          compile with built-in memory and bounds checker (implies -g)\n"
#endif
#ifdef CONFIG_TCC_BACKTRACE
    "  -bt N       show N callers in stack traces\n"
#endif
    "Misc. options:\n"
    "  -x[c|a|n]   specify type of the next infile\n"
    "  -nostdinc   do not use standard system include paths\n"
    "  -nostdlib   do not link with standard crt and libraries\n"
    "  -Bdir       set tcc's private include/library dir\n"
    "  -MD         generate dependency file for make\n"
    "  -MF file    specify dependency file name\n"
    "  -m32/64     defer to i386/x86_64 cross compiler\n"
    "Tools:\n"
    "  create library  : tcc -ar [rcsv] lib.a files\n"
#ifdef TCC_TARGET_PE
    "  create def file : tcc -impdef lib.dll [-v] [-o lib.def]\n"
#endif
    ;

static const char help2[] =
    "Tiny C Compiler "TCC_VERSION" - More Options\n"
    "Special options:\n"
    "  -P -P1                        with -E: no/alternative #line output\n"
    "  -dD -dM                       with -E: output #define directives\n"
    "  -pthread                      same as -D_REENTRANT and -lpthread\n"
    "  -On                           same as -D__OPTIMIZE__ for n > 0\n"
    "  -Wp,-opt                      same as -opt\n"
    "  -include file                 include 'file' above each input file\n"
    "  -isystem dir                  add 'dir' to system include path\n"
    "  -static                       link to static libraries (not recommended)\n"
    "  -dumpversion                  print version\n"
    "  -print-search-dirs            print search paths\n"
    "  -dt                           with -run/-E: auto-define 'test_...' macros\n"
    "Ignored options:\n"
    "  --param  -pedantic  -pipe  -s  -std  -traditional\n"
    "-W... warnings:\n"
    "  all                           turn on some (*) warnings\n"
    "  error                         stop after first warning\n"
    "  unsupported                   warn about ignored options, pragmas, etc.\n"
    "  write-strings                 strings are const\n"
    "  implicit-function-declaration warn for missing prototype (*)\n"
    "-f[no-]... flags:\n"
    "  unsigned-char                 default char is unsigned\n"
    "  signed-char                   default char is signed\n"
    "  common                        use common section instead of bss\n"
    "  leading-underscore            decorate extern symbols\n"
    "  ms-extensions                 allow anonymous struct in struct\n"
    "  dollars-in-identifiers        allow '$' in C symbols\n"
    "-m... target specific options:\n"
    "  ms-bitfields                  use MSVC bitfield layout\n"
#ifdef TCC_TARGET_ARM
    "  float-abi                     hard/softfp on arm\n"
#endif
#ifdef TCC_TARGET_X86_64
    "  no-sse                        disable floats on x86_64\n"
#endif
    "-Wl,... linker options:\n"
    "  -nostdlib                     do not link with standard crt/libs\n"
    "  -[no-]whole-archive           load lib(s) fully/only as needed\n"
    "  -export-all-symbols           same as -rdynamic\n"
    "  -image-base= -Ttext=          set base address of executable\n"
    "  -section-alignment=           set section alignment in executable\n"
#ifdef TCC_TARGET_PE
    "  -file-alignment=              set PE file alignment\n"
    "  -stack=                       set PE stack reserve\n"
    "  -large-address-aware          set related PE option\n"
    "  -subsystem=[console/windows]  set PE subsystem\n"
    "  -oformat=[pe-* binary]        set executable output format\n"
    "Predefined macros:\n"
    "  tcc -E -dM - < nul\n"
#else
    "  -rpath=                       set dynamic library search path\n"
    "  -enable-new-dtags             set DT_RUNPATH instead of DT_RPATH\n"
    "  -soname=                      set DT_SONAME elf tag\n"
    "  -Bsymbolic                    set DT_SYMBOLIC elf tag\n"
    "  -oformat=[elf32/64-* binary]  set executable output format\n"
    "  -init= -fini= -as-needed -O   (ignored)\n"
    "Predefined macros:\n"
    "  tcc -E -dM - < /dev/null\n"
#endif
    "See also the manual for more details.\n"
    ;

static const char version[] =
    "tcc version "TCC_VERSION" ("
#ifdef TCC_TARGET_I386
        "i386"
#elif defined TCC_TARGET_X86_64
        "x86_64"
#elif defined TCC_TARGET_C67
        "C67"
#elif defined TCC_TARGET_ARM
        "ARM"
#elif defined TCC_TARGET_ARM64
        "AArch64"
#endif
#ifdef TCC_ARM_HARDFLOAT
        " Hard Float"
#endif
#ifdef TCC_TARGET_PE
        " Windows"
#elif defined(TCC_TARGET_MACHO)
        " Darwin"
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
        " FreeBSD"
#else
        " Linux"
#endif
    ")\n"
    ;

static void print_dirs(const char *msg, char **paths, int nb_paths)
{
    int i;
    printf("%s:\n%s", msg, nb_paths ? "" : "  -\n");
    for(i = 0; i < nb_paths; i++)
        printf("  %s\n", paths[i]);
}

static void print_search_dirs(TCCState *s)
{
    printf("install: %s\n", s->tcc_lib_path);
    /* print_dirs("programs", NULL, 0); */
    print_dirs("include", s->sysinclude_paths, s->nb_sysinclude_paths);
    print_dirs("libraries", s->library_paths, s->nb_library_paths);
    printf("libtcc1:\n  %s/"TCC_LIBTCC1"\n", s->tcc_lib_path);
#ifndef TCC_TARGET_PE
    print_dirs("crt", s->crt_paths, s->nb_crt_paths);
    printf("elfinterp:\n  %s\n",  DEFAULT_ELFINTERP(s));
#endif
}

static void set_environment(TCCState *s)
{
    char * path;

    path = getenv("C_INCLUDE_PATH");
    if(path != NULL) {
        tcc_add_sysinclude_path(s, path);
    }
    path = getenv("CPATH");
    if(path != NULL) {
        tcc_add_include_path(s, path);
    }
    path = getenv("LIBRARY_PATH");
    if(path != NULL) {
        tcc_add_library_path(s, path);
    }
}

static char *default_outputfile(TCCState *s, const char *first_file)
{
    char buf[1024];
    char *ext;
    const char *name = "a";

    if (first_file && strcmp(first_file, "-"))
        name = tcc_basename(first_file);
    snprintf(buf, sizeof(buf), "%s", name);
    ext = tcc_fileextension(buf);
#ifdef TCC_TARGET_PE
    if (s->output_type == TCC_OUTPUT_DLL)
        strcpy(ext, ".dll");
    else
    if (s->output_type == TCC_OUTPUT_EXE)
        strcpy(ext, ".exe");
    else
#endif
    if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r && *ext)
        strcpy(ext, ".o");
    else
        strcpy(buf, "a.out");
    return tcc_strdup(buf);
}

static unsigned getclock_ms(void)
{
#ifdef _WIN32
    return GetTickCount();
#else
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec*1000 + (tv.tv_usec+500)/1000;
#endif
}

int main(int argc0, char **argv0)
{
    TCCState *s;
    int ret, opt, n = 0, t = 0;
    unsigned start_time = 0;
    const char *first_file;
    int argc; char **argv;
    FILE *ppfp = stdout;

redo:
    argc = argc0, argv = argv0;
    s = tcc_new();
    opt = tcc_parse_args(s, &argc, &argv, 1);

    if ((n | t) == 0) {
        if (opt == OPT_HELP)
            return printf(help), 1;
        if (opt == OPT_HELP2)
            return printf(help2), 1;
        if (opt == OPT_M32 || opt == OPT_M64)
            tcc_tool_cross(s, argv, opt); /* never returns */
        if (s->verbose)
            printf(version);
        if (opt == OPT_AR)
            return tcc_tool_ar(s, argc, argv);
#ifdef TCC_TARGET_PE
        if (opt == OPT_IMPDEF)
            return tcc_tool_impdef(s, argc, argv);
#endif
        if (opt == OPT_V)
            return 0;
        if (opt == OPT_PRINT_DIRS) {
            /* initialize search dirs */
            set_environment(s);
            tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
            print_search_dirs(s);
            return 0;
        }

        n = s->nb_files;
        if (n == 0)
            tcc_error("no input files\n");

        if (s->output_type == TCC_OUTPUT_PREPROCESS) {
            if (s->outfile) {
                ppfp = fopen(s->outfile, "w");
                if (!ppfp)
                    tcc_error("could not write '%s'", s->outfile);
            }
        } else if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r) {
            if (s->nb_libraries)
                tcc_error("cannot specify libraries with -c");
            if (n > 1 && s->outfile)
                tcc_error("cannot specify output file with -c many files");
        } else {
            if (s->option_pthread)
                tcc_set_options(s, "-lpthread");
        }

        if (s->do_bench)
            start_time = getclock_ms();
    }

    set_environment(s);
    if (s->output_type == 0)
        s->output_type = TCC_OUTPUT_EXE;
    tcc_set_output_type(s, s->output_type);
    s->ppfp = ppfp;

    if ((s->output_type == TCC_OUTPUT_MEMORY
      || s->output_type == TCC_OUTPUT_PREPROCESS) && (s->dflag & 16))
        s->dflag |= t ? 32 : 0, s->run_test = ++t, n = s->nb_files;

    /* compile or add each files or library */
    for (first_file = NULL, ret = 0;;) {
        struct filespec *f = s->files[s->nb_files - n];
        s->filetype = f->type;
        s->alacarte_link = f->alacarte;
        if (f->type == AFF_TYPE_LIB) {
            if (tcc_add_library_err(s, f->name) < 0)
                ret = 1;
        } else {
            if (1 == s->verbose)
                printf("-> %s\n", f->name);
            if (!first_file)
                first_file = f->name;
            if (tcc_add_file(s, f->name) < 0)
                ret = 1;
        }
        s->filetype = 0;
        s->alacarte_link = 1;
        if (--n == 0 || ret
            || (s->output_type == TCC_OUTPUT_OBJ && !s->option_r))
            break;
    }

    if (s->run_test) {
        t = 0;
    } else if (s->output_type == TCC_OUTPUT_PREPROCESS) {
        ;
    } else if (0 == ret) {
        if (s->output_type == TCC_OUTPUT_MEMORY) {
#ifdef TCC_IS_NATIVE
            ret = tcc_run(s, argc, argv);
#endif
        } else {
            if (!s->outfile)
                s->outfile = default_outputfile(s, first_file);
            if (tcc_output_file(s, s->outfile))
                ret = 1;
            else if (s->gen_deps)
                gen_makedeps(s, s->outfile, s->deps_outfile);
        }
    }

    if (s->do_bench && (n | t | ret) == 0)
        tcc_print_stats(s, getclock_ms() - start_time);
    tcc_delete(s);
    if (ret == 0 && n)
        goto redo; /* compile more files with -c */
    if (t)
        goto redo; /* run more tests with -dt -run */
    if (ppfp && ppfp != stdout)
        fclose(ppfp);
    return ret;
}

https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tcc.h

File: ../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tcc.h used in: Step 488
/*
 *  TCC - Tiny C Compiler
 *
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef _TCC_H
#define _TCC_H

#define _GNU_SOURCE
#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <fcntl.h>
#include <setjmp.h>
#include <time.h>

#ifndef _WIN32
# include <unistd.h>
# include <sys/time.h>
# ifndef CONFIG_TCC_STATIC
#  include <dlfcn.h>
# endif
/* XXX: need to define this to use them in non ISOC99 context */
extern float strtof (const char *__nptr, char **__endptr);
extern long double strtold (const char *__nptr, char **__endptr);
#endif

#ifdef _WIN32
# include <windows.h>
# include <io.h> /* open, close etc. */
# include <direct.h> /* getcwd */
# ifdef __GNUC__
#  include <stdint.h>
# endif
# define inline __inline
# define snprintf _snprintf
# define vsnprintf _vsnprintf
# ifndef __GNUC__
#  define strtold (long double)strtod
#  define strtof (float)strtod
#  define strtoll _strtoi64
#  define strtoull _strtoui64
# endif
# ifdef LIBTCC_AS_DLL
#  define LIBTCCAPI __declspec(dllexport)
#  define PUB_FUNC LIBTCCAPI
# endif
# define inp next_inp /* inp is an intrinsic on msvc/mingw */
# ifdef _MSC_VER
#  pragma warning (disable : 4244)  // conversion from 'uint64_t' to 'int', possible loss of data
#  pragma warning (disable : 4267)  // conversion from 'size_t' to 'int', possible loss of data
#  pragma warning (disable : 4996)  // The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
#  pragma warning (disable : 4018)  // signed/unsigned mismatch
#  pragma warning (disable : 4146)  // unary minus operator applied to unsigned type, result still unsigned
#  define ssize_t intptr_t
# endif
# undef CONFIG_TCC_STATIC
#endif

#ifndef O_BINARY
# define O_BINARY 0
#endif

#ifndef offsetof
#define offsetof(type, field) ((size_t) &((type *)0)->field)
#endif

#ifndef countof
#define countof(tab) (sizeof(tab) / sizeof((tab)[0]))
#endif

#ifdef _MSC_VER
# define NORETURN __declspec(noreturn)
# define ALIGNED(x) __declspec(align(x))
#else
# define NORETURN __attribute__((noreturn))
# define ALIGNED(x) __attribute__((aligned(x)))
#endif

#ifdef _WIN32
# define IS_DIRSEP(c) (c == '/' || c == '\\')
# define IS_ABSPATH(p) (IS_DIRSEP(p[0]) || (p[0] && p[1] == ':' && IS_DIRSEP(p[2])))
# define PATHCMP stricmp
# define PATHSEP ";"
#else
# define IS_DIRSEP(c) (c == '/')
# define IS_ABSPATH(p) IS_DIRSEP(p[0])
# define PATHCMP strcmp
# define PATHSEP ":"
#endif

/* -------------------------------------------- */

/* parser debug */
/* #define PARSE_DEBUG */
/* preprocessor debug */
/* #define PP_DEBUG */
/* include file debug */
/* #define INC_DEBUG */
/* memory leak debug */
/* #define MEM_DEBUG */
/* assembler debug */
/* #define ASM_DEBUG */

/* target selection */
/* #define TCC_TARGET_I386   *//* i386 code generator */
/* #define TCC_TARGET_X86_64 *//* x86-64 code generator */
/* #define TCC_TARGET_ARM    *//* ARMv4 code generator */
/* #define TCC_TARGET_ARM64  *//* ARMv8 code generator */
/* #define TCC_TARGET_C67    *//* TMS320C67xx code generator */

/* default target is I386 */
#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_ARM) && \
    !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_C67) && \
    !defined(TCC_TARGET_X86_64)
# if defined __x86_64__ || defined _AMD64_
#  define TCC_TARGET_X86_64
# elif defined __arm__
#  define TCC_TARGET_ARM
#  define TCC_ARM_EABI
#  define TCC_ARM_HARDFLOAT
# elif defined __aarch64__
#  define TCC_TARGET_ARM64
# else
#  define TCC_TARGET_I386
# endif
# ifdef _WIN32
#  define TCC_TARGET_PE 1
# endif
#endif

/* only native compiler supports -run */
#if defined _WIN32 == defined TCC_TARGET_PE
# if (defined __i386__ || defined _X86_) && defined TCC_TARGET_I386
#  define TCC_IS_NATIVE
# elif (defined __x86_64__ || defined _AMD64_) && defined TCC_TARGET_X86_64
#  define TCC_IS_NATIVE
# elif defined __arm__ && defined TCC_TARGET_ARM
#  define TCC_IS_NATIVE
# elif defined __aarch64__ && defined TCC_TARGET_ARM64
#  define TCC_IS_NATIVE
# endif
#endif

#if defined TCC_IS_NATIVE && !defined CONFIG_TCCBOOT
# define CONFIG_TCC_BACKTRACE
# if (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64) \
  && !defined TCC_UCLIBC && !defined TCC_MUSL
# define CONFIG_TCC_BCHECK /* enable bound checking code */
# endif
#endif

/* ------------ path configuration ------------ */

#ifndef CONFIG_SYSROOT
# define CONFIG_SYSROOT ""
#endif
#ifndef CONFIG_TCCDIR
# define CONFIG_TCCDIR "/usr/local/lib/tcc"
#endif
#ifndef CONFIG_LDDIR
# define CONFIG_LDDIR "lib"
#endif
#ifdef CONFIG_TRIPLET
# define USE_TRIPLET(s) s "/" CONFIG_TRIPLET
# define ALSO_TRIPLET(s) USE_TRIPLET(s) ":" s
#else
# define USE_TRIPLET(s) s
# define ALSO_TRIPLET(s) s
#endif

/* path to find crt1.o, crti.o and crtn.o */
#ifndef CONFIG_TCC_CRTPREFIX
# define CONFIG_TCC_CRTPREFIX USE_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR)
#endif

/* Below: {B} is substituted by CONFIG_TCCDIR (rsp. -B option) */

/* system include paths */
#ifndef CONFIG_TCC_SYSINCLUDEPATHS
# ifdef TCC_TARGET_PE
#  define CONFIG_TCC_SYSINCLUDEPATHS "{B}/include"PATHSEP"{B}/include/winapi"
# else
#  define CONFIG_TCC_SYSINCLUDEPATHS \
        "{B}/include" \
    ":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/include") \
    ":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/include")
# endif
#endif

/* library search paths */
#ifndef CONFIG_TCC_LIBPATHS
# ifdef TCC_TARGET_PE
#  define CONFIG_TCC_LIBPATHS "{B}/lib"
# else
#  define CONFIG_TCC_LIBPATHS \
        ALSO_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) \
    ":" ALSO_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) \
    ":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/" CONFIG_LDDIR)
# endif
#endif

/* name of ELF interpreter */
#ifndef CONFIG_TCC_ELFINTERP
# if defined __FreeBSD__
#  define CONFIG_TCC_ELFINTERP "/libexec/ld-elf.so.1"
# elif defined __FreeBSD_kernel__
#  if defined(TCC_TARGET_X86_64)
#   define CONFIG_TCC_ELFINTERP "/lib/ld-kfreebsd-x86-64.so.1"
#  else
#   define CONFIG_TCC_ELFINTERP "/lib/ld.so.1"
#  endif
# elif defined __DragonFly__
#  define CONFIG_TCC_ELFINTERP "/usr/libexec/ld-elf.so.2"
# elif defined __NetBSD__
#  define CONFIG_TCC_ELFINTERP "/usr/libexec/ld.elf_so"
# elif defined __GNU__
#  define CONFIG_TCC_ELFINTERP "/lib/ld.so"
# elif defined(TCC_TARGET_PE)
#  define CONFIG_TCC_ELFINTERP "-"
# elif defined(TCC_UCLIBC)
#  define CONFIG_TCC_ELFINTERP "/lib/ld-uClibc.so.0" /* is there a uClibc for x86_64 ? */
# elif defined TCC_TARGET_ARM64
#  if defined(TCC_MUSL)
#   define CONFIG_TCC_ELFINTERP "/lib/ld-musl-aarch64.so.1"
#  else
#   define CONFIG_TCC_ELFINTERP "/lib/ld-linux-aarch64.so.1"
#  endif
# elif defined(TCC_TARGET_X86_64)
#  if defined(TCC_MUSL)
#   define CONFIG_TCC_ELFINTERP "/lib/ld-musl-x86_64.so.1"
#  else
#   define CONFIG_TCC_ELFINTERP "/lib64/ld-linux-x86-64.so.2"
#  endif
# elif !defined(TCC_ARM_EABI)
#  if defined(TCC_MUSL)
#   define CONFIG_TCC_ELFINTERP "/lib/ld-musl-arm.so.1"
#  else
#   define CONFIG_TCC_ELFINTERP "/lib/ld-linux.so.2"
#  endif
# endif
#endif

/* var elf_interp dans *-gen.c */
#ifdef CONFIG_TCC_ELFINTERP
# define DEFAULT_ELFINTERP(s) CONFIG_TCC_ELFINTERP
#else
# define DEFAULT_ELFINTERP(s) default_elfinterp(s)
#endif

/* (target specific) libtcc1.a */
#ifndef TCC_LIBTCC1
# define TCC_LIBTCC1 "libtcc1.a"
#endif

/* library to use with CONFIG_USE_LIBGCC instead of libtcc1.a */
#if defined CONFIG_USE_LIBGCC && !defined TCC_LIBGCC
#define TCC_LIBGCC USE_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) "/libgcc_s.so.1"
#endif

/* -------------------------------------------- */

#include "libtcc.h"
#include "elf.h"
#include "stab.h"

/* -------------------------------------------- */

#ifndef PUB_FUNC /* functions used by tcc.c but not in libtcc.h */
# define PUB_FUNC
#endif

#ifndef ONE_SOURCE
# define ONE_SOURCE 1
#endif

#if ONE_SOURCE
#define ST_INLN static inline
#define ST_FUNC static
#define ST_DATA static
#else
#define ST_INLN
#define ST_FUNC
#define ST_DATA extern
#endif

#ifdef TCC_PROFILE /* profile all functions */
# define static
#endif

/* -------------------------------------------- */
/* include the target specific definitions */

#define TARGET_DEFS_ONLY
#ifdef TCC_TARGET_I386
# include "i386-gen.c"
# include "i386-link.c"
#endif
#ifdef TCC_TARGET_X86_64
# include "x86_64-gen.c"
# include "x86_64-link.c"
#endif
#ifdef TCC_TARGET_ARM
# include "arm-gen.c"
# include "arm-link.c"
# include "arm-asm.c"
#endif
#ifdef TCC_TARGET_ARM64
# include "arm64-gen.c"
# include "arm64-link.c"
#endif
#ifdef TCC_TARGET_C67
# define TCC_TARGET_COFF
# include "coff.h"
# include "c67-gen.c"
# include "c67-link.c"
#endif
#undef TARGET_DEFS_ONLY

/* -------------------------------------------- */

#if PTR_SIZE == 8
# define ELFCLASSW ELFCLASS64
# define ElfW(type) Elf##64##_##type
# define ELFW(type) ELF##64##_##type
# define ElfW_Rel ElfW(Rela)
# define SHT_RELX SHT_RELA
# define REL_SECTION_FMT ".rela%s"
#else
# define ELFCLASSW ELFCLASS32
# define ElfW(type) Elf##32##_##type
# define ELFW(type) ELF##32##_##type
# define ElfW_Rel ElfW(Rel)
# define SHT_RELX SHT_REL
# define REL_SECTION_FMT ".rel%s"
#endif
/* target address type */
#define addr_t ElfW(Addr)
#define ElfSym ElfW(Sym)

#if PTR_SIZE == 8 && !defined TCC_TARGET_PE
# define LONG_SIZE 8
#else
# define LONG_SIZE 4
#endif

/* -------------------------------------------- */

#define INCLUDE_STACK_SIZE  32
#define IFDEF_STACK_SIZE    64
#define VSTACK_SIZE         256
#define STRING_MAX_SIZE     1024
#define TOKSTR_MAX_SIZE     256
#define PACK_STACK_SIZE     8

#define TOK_HASH_SIZE       16384 /* must be a power of two */
#define TOK_ALLOC_INCR      512  /* must be a power of two */
#define TOK_MAX_SIZE        4 /* token max size in int unit when stored in string */

/* token symbol management */
typedef struct TokenSym {
    struct TokenSym *hash_next;
    struct Sym *sym_define; /* direct pointer to define */
    struct Sym *sym_label; /* direct pointer to label */
    struct Sym *sym_struct; /* direct pointer to structure */
    struct Sym *sym_identifier; /* direct pointer to identifier */
    int tok; /* token number */
    int len;
    char str[1];
} TokenSym;

#ifdef TCC_TARGET_PE
typedef unsigned short nwchar_t;
#else
typedef int nwchar_t;
#endif

typedef struct CString {
    int size; /* size in bytes */
    void *data; /* either 'char *' or 'nwchar_t *' */
    int size_allocated;
} CString;

/* type definition */
typedef struct CType {
    int t;
    struct Sym *ref;
} CType;

/* constant value */
typedef union CValue {
    long double ld;
    double d;
    float f;
    uint64_t i;
    struct {
        int size;
        const void *data;
    } str;
    int tab[LDOUBLE_SIZE/4];
} CValue;

/* value on stack */
typedef struct SValue {
    CType type;      /* type */
    unsigned short r;      /* register + flags */
    unsigned short r2;     /* second register, used for 'long long'
                              type. If not used, set to VT_CONST */
    CValue c;              /* constant, if VT_CONST */
    struct Sym *sym;       /* symbol, if (VT_SYM | VT_CONST), or if
                      result of unary() for an identifier. */
} SValue;

/* symbol attributes */
struct SymAttr {
    unsigned short
    aligned     : 5, /* alignment as log2+1 (0 == unspecified) */
    packed      : 1,
    weak        : 1,
    visibility  : 2,
    dllexport   : 1,
    dllimport   : 1,
    unused      : 5;
};

/* function attributes or temporary attributes for parsing */
struct FuncAttr {
    unsigned
    func_call   : 3, /* calling convention (0..5), see below */
    func_type   : 2, /* FUNC_OLD/NEW/ELLIPSIS */
    func_args   : 8; /* PE __stdcall args */
};

/* GNUC attribute definition */
typedef struct AttributeDef {
    struct SymAttr a;
    struct FuncAttr f;
    struct Section *section;
    int alias_target; /* token */
    int asm_label; /* associated asm label */
    char attr_mode; /* __attribute__((__mode__(...))) */
} AttributeDef;

/* symbol management */
typedef struct Sym {
    int v; /* symbol token */
    unsigned short r; /* associated register or VT_CONST/VT_LOCAL and LVAL type */
    struct SymAttr a; /* symbol attributes */
    union {
        struct {
            int c; /* associated number or Elf symbol index */
            union {
                int sym_scope; /* scope level for locals */
                int jnext; /* next jump label */
                struct FuncAttr f; /* function attributes */
                int auxtype; /* bitfield access type */
            };
        };
        long long enum_val; /* enum constant if IS_ENUM_VAL */
        int *d; /* define token stream */
    };
    CType type; /* associated type */
    union {
        struct Sym *next; /* next related symbol (for fields and anoms) */
        int asm_label; /* associated asm label */
    };
    struct Sym *prev; /* prev symbol in stack */
    struct Sym *prev_tok; /* previous symbol for this token */
} Sym;

/* section definition */
typedef struct Section {
    unsigned long data_offset; /* current data offset */
    unsigned char *data;       /* section data */
    unsigned long data_allocated; /* used for realloc() handling */
    int sh_name;             /* elf section name (only used during output) */
    int sh_num;              /* elf section number */
    int sh_type;             /* elf section type */
    int sh_flags;            /* elf section flags */
    int sh_info;             /* elf section info */
    int sh_addralign;        /* elf section alignment */
    int sh_entsize;          /* elf entry size */
    unsigned long sh_size;   /* section size (only used during output) */
    addr_t sh_addr;          /* address at which the section is relocated */
    unsigned long sh_offset; /* file offset */
    int nb_hashed_syms;      /* used to resize the hash table */
    struct Section *link;    /* link to another section */
    struct Section *reloc;   /* corresponding section for relocation, if any */
    struct Section *hash;    /* hash table for symbols */
    struct Section *prev;    /* previous section on section stack */
    char name[1];           /* section name */
} Section;

typedef struct DLLReference {
    int level;
    void *handle;
    char name[1];
} DLLReference;

/* -------------------------------------------------- */

#define SYM_STRUCT     0x40000000 /* struct/union/enum symbol space */
#define SYM_FIELD      0x20000000 /* struct/union field symbol space */
#define SYM_FIRST_ANOM 0x10000000 /* first anonymous sym */

/* stored in 'Sym->f.func_type' field */
#define FUNC_NEW       1 /* ansi function prototype */
#define FUNC_OLD       2 /* old function prototype */
#define FUNC_ELLIPSIS  3 /* ansi function prototype with ... */

/* stored in 'Sym->f.func_call' field */
#define FUNC_CDECL     0 /* standard c call */
#define FUNC_STDCALL   1 /* pascal c call */
#define FUNC_FASTCALL1 2 /* first param in %eax */
#define FUNC_FASTCALL2 3 /* first parameters in %eax, %edx */
#define FUNC_FASTCALL3 4 /* first parameter in %eax, %edx, %ecx */
#define FUNC_FASTCALLW 5 /* first parameter in %ecx, %edx */

/* field 'Sym.t' for macros */
#define MACRO_OBJ      0 /* object like macro */
#define MACRO_FUNC     1 /* function like macro */

/* field 'Sym.r' for C labels */
#define LABEL_DEFINED  0 /* label is defined */
#define LABEL_FORWARD  1 /* label is forward defined */
#define LABEL_DECLARED 2 /* label is declared but never used */

/* type_decl() types */
#define TYPE_ABSTRACT  1 /* type without variable */
#define TYPE_DIRECT    2 /* type with variable */

#define IO_BUF_SIZE 8192

typedef struct BufferedFile {
    uint8_t *buf_ptr;
    uint8_t *buf_end;
    int fd;
    struct BufferedFile *prev;
    int line_num;    /* current line number - here to simplify code */
    int line_ref;    /* tcc -E: last printed line */
    int ifndef_macro;  /* #ifndef macro / #endif search */
    int ifndef_macro_saved; /* saved ifndef_macro */
    int *ifdef_stack_ptr; /* ifdef_stack value at the start of the file */
    int include_next_index; /* next search path */
    char filename[1024];    /* filename */
    char *true_filename; /* filename not modified by # line directive */
    unsigned char unget[4];
    unsigned char buffer[1]; /* extra size for CH_EOB char */
} BufferedFile;

#define CH_EOB   '\\'       /* end of buffer or '\0' char in file */
#define CH_EOF   (-1)   /* end of file */

/* used to record tokens */
typedef struct TokenString {
    int *str;
    int len;
    int lastlen;
    int allocated_len;
    int last_line_num;
    int save_line_num;
    /* used to chain token-strings with begin/end_macro() */
    struct TokenString *prev;
    const int *prev_ptr;
    char alloc;
} TokenString;

/* inline functions */
typedef struct InlineFunc {
    TokenString *func_str;
    Sym *sym;
    char filename[1];
} InlineFunc;

/* include file cache, used to find files faster and also to eliminate
   inclusion if the include file is protected by #ifndef ... #endif */
typedef struct CachedInclude {
    int ifndef_macro;
    int once;
    int hash_next; /* -1 if none */
    char filename[1]; /* path specified in #include */
} CachedInclude;

#define CACHED_INCLUDES_HASH_SIZE 32

#ifdef CONFIG_TCC_ASM
typedef struct ExprValue {
    uint64_t v;
    Sym *sym;
    int pcrel;
} ExprValue;

#define MAX_ASM_OPERANDS 30
typedef struct ASMOperand {
    int id; /* GCC 3 optional identifier (0 if number only supported */
    char *constraint;
    char asm_str[16]; /* computed asm string for operand */
    SValue *vt; /* C value of the expression */
    int ref_index; /* if >= 0, gives reference to a output constraint */
    int input_index; /* if >= 0, gives reference to an input constraint */
    int priority; /* priority, used to assign registers */
    int reg; /* if >= 0, register number used for this operand */
    int is_llong; /* true if double register value */
    int is_memory; /* true if memory operand */
    int is_rw;     /* for '+' modifier */
} ASMOperand;
#endif

/* extra symbol attributes (not in symbol table) */
struct sym_attr {
    unsigned got_offset;
    unsigned plt_offset;
    int plt_sym;
    int dyn_index;
#ifdef TCC_TARGET_ARM
    unsigned char plt_thumb_stub:1;
#endif
};

struct TCCState {

    int verbose; /* if true, display some information during compilation */
    int nostdinc; /* if true, no standard headers are added */
    int nostdlib; /* if true, no standard libraries are added */
    int nocommon; /* if true, do not use common symbols for .bss data */
    int static_link; /* if true, static linking is performed */
    int rdynamic; /* if true, all symbols are exported */
    int symbolic; /* if true, resolve symbols in the current module first */
    int alacarte_link; /* if true, only link in referenced objects from archive */

    char *tcc_lib_path; /* CONFIG_TCCDIR or -B option */
    char *soname; /* as specified on the command line (-soname) */
    char *rpath; /* as specified on the command line (-Wl,-rpath=) */
    int enable_new_dtags; /* ditto, (-Wl,--enable-new-dtags) */

    /* output type, see TCC_OUTPUT_XXX */
    int output_type;
    /* output format, see TCC_OUTPUT_FORMAT_xxx */
    int output_format;

    /* C language options */
    int char_is_unsigned;
    int leading_underscore;
    int ms_extensions;  /* allow nested named struct w/o identifier behave like unnamed */
    int dollars_in_identifiers; /* allows '$' char in identifiers */
    int ms_bitfields; /* if true, emulate MS algorithm for aligning bitfields */

    /* warning switches */
    int warn_write_strings;
    int warn_unsupported;
    int warn_error;
    int warn_none;
    int warn_implicit_function_declaration;
    int warn_gcc_compat;

    /* compile with debug symbol (and use them if error during execution) */
    int do_debug;
#ifdef CONFIG_TCC_BCHECK
    /* compile with built-in memory and bounds checker */
    int do_bounds_check;
#endif
#ifdef TCC_TARGET_ARM
    enum float_abi float_abi; /* float ABI of the generated code*/
#endif
    int run_test; /* nth test to run with -dt -run */

    addr_t text_addr; /* address of text section */
    int has_text_addr;

    unsigned section_align; /* section alignment */

    char *init_symbol; /* symbols to call at load-time (not used currently) */
    char *fini_symbol; /* symbols to call at unload-time (not used currently) */

#ifdef TCC_TARGET_I386
    int seg_size; /* 32. Can be 16 with i386 assembler (.code16) */
#endif
#ifdef TCC_TARGET_X86_64
    int nosse; /* For -mno-sse support. */
#endif

    /* array of all loaded dlls (including those referenced by loaded dlls) */
    DLLReference **loaded_dlls;
    int nb_loaded_dlls;

    /* include paths */
    char **include_paths;
    int nb_include_paths;

    char **sysinclude_paths;
    int nb_sysinclude_paths;

    /* library paths */
    char **library_paths;
    int nb_library_paths;

    /* crt?.o object path */
    char **crt_paths;
    int nb_crt_paths;

    /* -include files */
    char **cmd_include_files;
    int nb_cmd_include_files;

    /* error handling */
    void *error_opaque;
    void (*error_func)(void *opaque, const char *msg);
    int error_set_jmp_enabled;
    jmp_buf error_jmp_buf;
    int nb_errors;

    /* output file for preprocessing (-E) */
    FILE *ppfp;
    enum {
    LINE_MACRO_OUTPUT_FORMAT_GCC,
    LINE_MACRO_OUTPUT_FORMAT_NONE,
    LINE_MACRO_OUTPUT_FORMAT_STD,
    LINE_MACRO_OUTPUT_FORMAT_P10 = 11
    } Pflag; /* -P switch */
    char dflag; /* -dX value */

    /* for -MD/-MF: collected dependencies for this compilation */
    char **target_deps;
    int nb_target_deps;

    /* compilation */
    BufferedFile *include_stack[INCLUDE_STACK_SIZE];
    BufferedFile **include_stack_ptr;

    int ifdef_stack[IFDEF_STACK_SIZE];
    int *ifdef_stack_ptr;

    /* included files enclosed with #ifndef MACRO */
    int cached_includes_hash[CACHED_INCLUDES_HASH_SIZE];
    CachedInclude **cached_includes;
    int nb_cached_includes;

    /* #pragma pack stack */
    int pack_stack[PACK_STACK_SIZE];
    int *pack_stack_ptr;
    char **pragma_libs;
    int nb_pragma_libs;

    /* inline functions are stored as token lists and compiled last
       only if referenced */
    struct InlineFunc **inline_fns;
    int nb_inline_fns;

    /* sections */
    Section **sections;
    int nb_sections; /* number of sections, including first dummy section */

    Section **priv_sections;
    int nb_priv_sections; /* number of private sections */

    /* got & plt handling */
    Section *got;
    Section *plt;

    /* temporary dynamic symbol sections (for dll loading) */
    Section *dynsymtab_section;
    /* exported dynamic symbol section */
    Section *dynsym;
    /* copy of the global symtab_section variable */
    Section *symtab;
    /* extra attributes (eg. GOT/PLT value) for symtab symbols */
    struct sym_attr *sym_attrs;
    int nb_sym_attrs;

#ifdef TCC_TARGET_PE
    /* PE info */
    int pe_subsystem;
    unsigned pe_characteristics;
    unsigned pe_file_align;
    unsigned pe_stack_size;
    addr_t pe_imagebase;
# ifdef TCC_TARGET_X86_64
    Section *uw_pdata;
    int uw_sym;
    unsigned uw_offs;
# endif
#endif

#ifdef TCC_IS_NATIVE
    const char *runtime_main;
    void **runtime_mem;
    int nb_runtime_mem;
#endif

    /* used by main and tcc_parse_args only */
    struct filespec **files; /* files seen on command line */
    int nb_files; /* number thereof */
    int nb_libraries; /* number of libs thereof */
    int filetype;
    char *outfile; /* output filename */
    int option_r; /* option -r */
    int do_bench; /* option -bench */
    int gen_deps; /* option -MD  */
    char *deps_outfile; /* option -MF */
    int option_pthread; /* -pthread option */
    int argc;
    char **argv;
};

struct filespec {
    char type;
    char alacarte;
    char name[1];
};

/* The current value can be: */
#define VT_VALMASK   0x003f  /* mask for value location, register or: */
#define VT_CONST     0x0030  /* constant in vc (must be first non register value) */
#define VT_LLOCAL    0x0031  /* lvalue, offset on stack */
#define VT_LOCAL     0x0032  /* offset on stack */
#define VT_CMP       0x0033  /* the value is stored in processor flags (in vc) */
#define VT_JMP       0x0034  /* value is the consequence of jmp true (even) */
#define VT_JMPI      0x0035  /* value is the consequence of jmp false (odd) */
#define VT_LVAL      0x0100  /* var is an lvalue */
#define VT_SYM       0x0200  /* a symbol value is added */
#define VT_MUSTCAST  0x0400  /* value must be casted to be correct (used for
                                char/short stored in integer registers) */
#define VT_MUSTBOUND 0x0800  /* bound checking must be done before
                                dereferencing value */
#define VT_BOUNDED   0x8000  /* value is bounded. The address of the
                                bounding function call point is in vc */
#define VT_LVAL_BYTE     0x1000  /* lvalue is a byte */
#define VT_LVAL_SHORT    0x2000  /* lvalue is a short */
#define VT_LVAL_UNSIGNED 0x4000  /* lvalue is unsigned */
#define VT_LVAL_TYPE     (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED)

/* types */
#define VT_BTYPE       0x000f  /* mask for basic type */
#define VT_VOID             0  /* void type */
#define VT_BYTE             1  /* signed byte type */
#define VT_SHORT            2  /* short type */
#define VT_INT              3  /* integer type */
#define VT_LLONG            4  /* 64 bit integer */
#define VT_PTR              5  /* pointer */
#define VT_FUNC             6  /* function type */
#define VT_STRUCT           7  /* struct/union definition */
#define VT_FLOAT            8  /* IEEE float */
#define VT_DOUBLE           9  /* IEEE double */
#define VT_LDOUBLE         10  /* IEEE long double */
#define VT_BOOL            11  /* ISOC99 boolean type */
#define VT_QLONG           13  /* 128-bit integer. Only used for x86-64 ABI */
#define VT_QFLOAT          14  /* 128-bit float. Only used for x86-64 ABI */

#define VT_UNSIGNED    0x0010  /* unsigned type */
#define VT_DEFSIGN     0x0020  /* explicitly signed or unsigned */
#define VT_ARRAY       0x0040  /* array type (also has VT_PTR) */
#define VT_BITFIELD    0x0080  /* bitfield modifier */
#define VT_CONSTANT    0x0100  /* const modifier */
#define VT_VOLATILE    0x0200  /* volatile modifier */
#define VT_VLA         0x0400  /* VLA type (also has VT_PTR and VT_ARRAY) */
#define VT_LONG        0x0800  /* long type (also has VT_INT rsp. VT_LLONG) */

/* storage */
#define VT_EXTERN  0x00001000  /* extern definition */
#define VT_STATIC  0x00002000  /* static variable */
#define VT_TYPEDEF 0x00004000  /* typedef definition */
#define VT_INLINE  0x00008000  /* inline definition */
/* currently unused: 0x000[1248]0000  */

#define VT_STRUCT_SHIFT 20     /* shift for bitfield shift values (32 - 2*6) */
#define VT_STRUCT_MASK (((1 << (6+6)) - 1) << VT_STRUCT_SHIFT | VT_BITFIELD)
#define BIT_POS(t) (((t) >> VT_STRUCT_SHIFT) & 0x3f)
#define BIT_SIZE(t) (((t) >> (VT_STRUCT_SHIFT + 6)) & 0x3f)

#define VT_UNION    (1 << VT_STRUCT_SHIFT | VT_STRUCT)
#define VT_ENUM     (2 << VT_STRUCT_SHIFT) /* integral type is an enum really */
#define VT_ENUM_VAL (3 << VT_STRUCT_SHIFT) /* integral type is an enum constant really */

#define IS_ENUM(t) ((t & VT_STRUCT_MASK) == VT_ENUM)
#define IS_ENUM_VAL(t) ((t & VT_STRUCT_MASK) == VT_ENUM_VAL)
#define IS_UNION(t) ((t & (VT_STRUCT_MASK|VT_BTYPE)) == VT_UNION)

/* type mask (except storage) */
#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE)
#define VT_TYPE (~(VT_STORAGE|VT_STRUCT_MASK))

/* symbol was created by tccasm.c first */
#define VT_ASM (VT_VOID | VT_UNSIGNED)
#define IS_ASM_SYM(sym) (((sym)->type.t & (VT_BTYPE | VT_ASM)) == VT_ASM)

/* token values */

/* warning: the following compare tokens depend on i386 asm code */
#define TOK_ULT 0x92
#define TOK_UGE 0x93
#define TOK_EQ  0x94
#define TOK_NE  0x95
#define TOK_ULE 0x96
#define TOK_UGT 0x97
#define TOK_Nset 0x98
#define TOK_Nclear 0x99
#define TOK_LT  0x9c
#define TOK_GE  0x9d
#define TOK_LE  0x9e
#define TOK_GT  0x9f

#define TOK_LAND  0xa0
#define TOK_LOR   0xa1
#define TOK_DEC   0xa2
#define TOK_MID   0xa3 /* inc/dec, to void constant */
#define TOK_INC   0xa4
#define TOK_UDIV  0xb0 /* unsigned division */
#define TOK_UMOD  0xb1 /* unsigned modulo */
#define TOK_PDIV  0xb2 /* fast division with undefined rounding for pointers */

/* tokens that carry values (in additional token string space / tokc) --> */
#define TOK_CCHAR   0xb3 /* char constant in tokc */
#define TOK_LCHAR   0xb4
#define TOK_CINT    0xb5 /* number in tokc */
#define TOK_CUINT   0xb6 /* unsigned int constant */
#define TOK_CLLONG  0xb7 /* long long constant */
#define TOK_CULLONG 0xb8 /* unsigned long long constant */
#define TOK_STR     0xb9 /* pointer to string in tokc */
#define TOK_LSTR    0xba
#define TOK_CFLOAT  0xbb /* float constant */
#define TOK_CDOUBLE 0xbc /* double constant */
#define TOK_CLDOUBLE 0xbd /* long double constant */
#define TOK_PPNUM   0xbe /* preprocessor number */
#define TOK_PPSTR   0xbf /* preprocessor string */
#define TOK_LINENUM 0xc0 /* line number info */
#define TOK_TWODOTS 0xa8 /* C++ token ? */
/* <-- */

#define TOK_UMULL    0xc2 /* unsigned 32x32 -> 64 mul */
#define TOK_ADDC1    0xc3 /* add with carry generation */
#define TOK_ADDC2    0xc4 /* add with carry use */
#define TOK_SUBC1    0xc5 /* add with carry generation */
#define TOK_SUBC2    0xc6 /* add with carry use */
#define TOK_ARROW    0xc7
#define TOK_DOTS     0xc8 /* three dots */
#define TOK_SHR      0xc9 /* unsigned shift right */
#define TOK_TWOSHARPS 0xca /* ## preprocessing token */
#define TOK_PLCHLDR  0xcb /* placeholder token as defined in C99 */
#define TOK_NOSUBST  0xcc /* means following token has already been pp'd */
#define TOK_PPJOIN   0xcd /* A '##' in the right position to mean pasting */
#define TOK_CLONG    0xce /* long constant */
#define TOK_CULONG   0xcf /* unsigned long constant */

#define TOK_SHL   0x01 /* shift left */
#define TOK_SAR   0x02 /* signed shift right */

/* assignment operators : normal operator or 0x80 */
#define TOK_A_MOD 0xa5
#define TOK_A_AND 0xa6
#define TOK_A_MUL 0xaa
#define TOK_A_ADD 0xab
#define TOK_A_SUB 0xad
#define TOK_A_DIV 0xaf
#define TOK_A_XOR 0xde
#define TOK_A_OR  0xfc
#define TOK_A_SHL 0x81
#define TOK_A_SAR 0x82

#define TOK_EOF       (-1)  /* end of file */
#define TOK_LINEFEED  10    /* line feed */

/* all identifiers and strings have token above that */
#define TOK_IDENT 256

#define DEF_ASM(x) DEF(TOK_ASM_ ## x, #x)
#define TOK_ASM_int TOK_INT
#define DEF_ASMDIR(x) DEF(TOK_ASMDIR_ ## x, "." #x)
#define TOK_ASMDIR_FIRST TOK_ASMDIR_byte
#define TOK_ASMDIR_LAST TOK_ASMDIR_section

#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
/* only used for i386 asm opcodes definitions */
#define DEF_BWL(x) \
 DEF(TOK_ASM_ ## x ## b, #x "b") \
 DEF(TOK_ASM_ ## x ## w, #x "w") \
 DEF(TOK_ASM_ ## x ## l, #x "l") \
 DEF(TOK_ASM_ ## x, #x)
#define DEF_WL(x) \
 DEF(TOK_ASM_ ## x ## w, #x "w") \
 DEF(TOK_ASM_ ## x ## l, #x "l") \
 DEF(TOK_ASM_ ## x, #x)
#ifdef TCC_TARGET_X86_64
# define DEF_BWLQ(x) \
 DEF(TOK_ASM_ ## x ## b, #x "b") \
 DEF(TOK_ASM_ ## x ## w, #x "w") \
 DEF(TOK_ASM_ ## x ## l, #x "l") \
 DEF(TOK_ASM_ ## x ## q, #x "q") \
 DEF(TOK_ASM_ ## x, #x)
# define DEF_WLQ(x) \
 DEF(TOK_ASM_ ## x ## w, #x "w") \
 DEF(TOK_ASM_ ## x ## l, #x "l") \
 DEF(TOK_ASM_ ## x ## q, #x "q") \
 DEF(TOK_ASM_ ## x, #x)
# define DEF_BWLX DEF_BWLQ
# define DEF_WLX DEF_WLQ
/* number of sizes + 1 */
# define NBWLX 5
#else
# define DEF_BWLX DEF_BWL
# define DEF_WLX DEF_WL
/* number of sizes + 1 */
# define NBWLX 4
#endif

#define DEF_FP1(x) \
 DEF(TOK_ASM_ ## f ## x ## s, "f" #x "s") \
 DEF(TOK_ASM_ ## fi ## x ## l, "fi" #x "l") \
 DEF(TOK_ASM_ ## f ## x ## l, "f" #x "l") \
 DEF(TOK_ASM_ ## fi ## x ## s, "fi" #x "s")

#define DEF_FP(x) \
 DEF(TOK_ASM_ ## f ## x, "f" #x ) \
 DEF(TOK_ASM_ ## f ## x ## p, "f" #x "p") \
 DEF_FP1(x)

#define DEF_ASMTEST(x,suffix) \
 DEF_ASM(x ## o ## suffix) \
 DEF_ASM(x ## no ## suffix) \
 DEF_ASM(x ## b ## suffix) \
 DEF_ASM(x ## c ## suffix) \
 DEF_ASM(x ## nae ## suffix) \
 DEF_ASM(x ## nb ## suffix) \
 DEF_ASM(x ## nc ## suffix) \
 DEF_ASM(x ## ae ## suffix) \
 DEF_ASM(x ## e ## suffix) \
 DEF_ASM(x ## z ## suffix) \
 DEF_ASM(x ## ne ## suffix) \
 DEF_ASM(x ## nz ## suffix) \
 DEF_ASM(x ## be ## suffix) \
 DEF_ASM(x ## na ## suffix) \
 DEF_ASM(x ## nbe ## suffix) \
 DEF_ASM(x ## a ## suffix) \
 DEF_ASM(x ## s ## suffix) \
 DEF_ASM(x ## ns ## suffix) \
 DEF_ASM(x ## p ## suffix) \
 DEF_ASM(x ## pe ## suffix) \
 DEF_ASM(x ## np ## suffix) \
 DEF_ASM(x ## po ## suffix) \
 DEF_ASM(x ## l ## suffix) \
 DEF_ASM(x ## nge ## suffix) \
 DEF_ASM(x ## nl ## suffix) \
 DEF_ASM(x ## ge ## suffix) \
 DEF_ASM(x ## le ## suffix) \
 DEF_ASM(x ## ng ## suffix) \
 DEF_ASM(x ## nle ## suffix) \
 DEF_ASM(x ## g ## suffix)

#endif /* defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 */

enum tcc_token {
    TOK_LAST = TOK_IDENT - 1
#define DEF(id, str) ,id
#include "tcctok.h"
#undef DEF
};

/* keywords: tok >= TOK_IDENT && tok < TOK_UIDENT */
#define TOK_UIDENT TOK_DEFINE

/* ------------ libtcc.c ------------ */

/* use GNU C extensions */
ST_DATA int gnu_ext;
/* use Tiny C extensions */
ST_DATA int tcc_ext;
/* XXX: get rid of this ASAP */
ST_DATA struct TCCState *tcc_state;

/* public functions currently used by the tcc main function */
ST_FUNC char *pstrcpy(char *buf, int buf_size, const char *s);
ST_FUNC char *pstrcat(char *buf, int buf_size, const char *s);
ST_FUNC char *pstrncpy(char *out, const char *in, size_t num);
PUB_FUNC char *tcc_basename(const char *name);
PUB_FUNC char *tcc_fileextension (const char *name);

#ifndef MEM_DEBUG
PUB_FUNC void tcc_free(void *ptr);
PUB_FUNC void *tcc_malloc(unsigned long size);
PUB_FUNC void *tcc_mallocz(unsigned long size);
PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size);
PUB_FUNC char *tcc_strdup(const char *str);
#else
#define tcc_free(ptr)           tcc_free_debug(ptr)
#define tcc_malloc(size)        tcc_malloc_debug(size, __FILE__, __LINE__)
#define tcc_mallocz(size)       tcc_mallocz_debug(size, __FILE__, __LINE__)
#define tcc_realloc(ptr,size)   tcc_realloc_debug(ptr, size, __FILE__, __LINE__)
#define tcc_strdup(str)         tcc_strdup_debug(str, __FILE__, __LINE__)
PUB_FUNC void tcc_free_debug(void *ptr);
PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line);
PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line);
PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line);
PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line);
#endif

#define free(p) use_tcc_free(p)
#define malloc(s) use_tcc_malloc(s)
#define realloc(p, s) use_tcc_realloc(p, s)
#undef strdup
#define strdup(s) use_tcc_strdup(s)
PUB_FUNC void tcc_memcheck(void);
PUB_FUNC void tcc_error_noabort(const char *fmt, ...);
PUB_FUNC NORETURN void tcc_error(const char *fmt, ...);
PUB_FUNC void tcc_warning(const char *fmt, ...);

/* other utilities */
ST_FUNC void dynarray_add(void *ptab, int *nb_ptr, void *data);
ST_FUNC void dynarray_reset(void *pp, int *n);
ST_INLN void cstr_ccat(CString *cstr, int ch);
ST_FUNC void cstr_cat(CString *cstr, const char *str, int len);
ST_FUNC void cstr_wccat(CString *cstr, int ch);
ST_FUNC void cstr_new(CString *cstr);
ST_FUNC void cstr_free(CString *cstr);
ST_FUNC void cstr_reset(CString *cstr);

ST_INLN void sym_free(Sym *sym);
ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, int c);
ST_FUNC Sym *sym_find2(Sym *s, int v);
ST_FUNC Sym *sym_push(int v, CType *type, int r, int c);
ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep);
ST_INLN Sym *struct_find(int v);
ST_INLN Sym *sym_find(int v);
ST_FUNC Sym *global_identifier_push(int v, int t, int c);

ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen);
ST_FUNC int tcc_open(TCCState *s1, const char *filename);
ST_FUNC void tcc_close(void);

ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags);
/* flags: */
#define AFF_PRINT_ERROR     0x10 /* print error if file not found */
#define AFF_REFERENCED_DLL  0x20 /* load a referenced dll from another dll */
#define AFF_TYPE_BIN        0x40 /* file to add is binary */
/* s->filetype: */
#define AFF_TYPE_NONE   0
#define AFF_TYPE_C      1
#define AFF_TYPE_ASM    2
#define AFF_TYPE_ASMPP  3
#define AFF_TYPE_LIB    4
/* values from tcc_object_type(...) */
#define AFF_BINTYPE_REL 1
#define AFF_BINTYPE_DYN 2
#define AFF_BINTYPE_AR  3
#define AFF_BINTYPE_C67 4


ST_FUNC int tcc_add_crt(TCCState *s, const char *filename);
ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags);
ST_FUNC void tcc_add_pragma_libs(TCCState *s1);
PUB_FUNC int tcc_add_library_err(TCCState *s, const char *f);
PUB_FUNC void tcc_print_stats(TCCState *s, unsigned total_time);
PUB_FUNC int tcc_parse_args(TCCState *s, int *argc, char ***argv, int optind);
#ifdef _WIN32
ST_FUNC char *normalize_slashes(char *path);
#endif

/* tcc_parse_args return codes: */
#define OPT_HELP 1
#define OPT_HELP2 2
#define OPT_V 3
#define OPT_PRINT_DIRS 4
#define OPT_AR 5
#define OPT_IMPDEF 6
#define OPT_M32 32
#define OPT_M64 64

/* ------------ tccpp.c ------------ */

ST_DATA struct BufferedFile *file;
ST_DATA int ch, tok;
ST_DATA CValue tokc;
ST_DATA const int *macro_ptr;
ST_DATA int parse_flags;
ST_DATA int tok_flags;
ST_DATA CString tokcstr; /* current parsed string, if any */

/* display benchmark infos */
ST_DATA int total_lines;
ST_DATA int total_bytes;
ST_DATA int tok_ident;
ST_DATA TokenSym **table_ident;

#define TOK_FLAG_BOL   0x0001 /* beginning of line before */
#define TOK_FLAG_BOF   0x0002 /* beginning of file before */
#define TOK_FLAG_ENDIF 0x0004 /* a endif was found matching starting #ifdef */
#define TOK_FLAG_EOF   0x0008 /* end of file */

#define PARSE_FLAG_PREPROCESS 0x0001 /* activate preprocessing */
#define PARSE_FLAG_TOK_NUM    0x0002 /* return numbers instead of TOK_PPNUM */
#define PARSE_FLAG_LINEFEED   0x0004 /* line feed is returned as a
                                        token. line feed is also
                                        returned at eof */
#define PARSE_FLAG_ASM_FILE 0x0008 /* we processing an asm file: '#' can be used for line comment, etc. */
#define PARSE_FLAG_SPACES     0x0010 /* next() returns space tokens (for -E) */
#define PARSE_FLAG_ACCEPT_STRAYS 0x0020 /* next() returns '\\' token */
#define PARSE_FLAG_TOK_STR    0x0040 /* return parsed strings instead of TOK_PPSTR */

/* isidnum_table flags: */
#define IS_SPC 1
#define IS_ID  2
#define IS_NUM 4

ST_FUNC TokenSym *tok_alloc(const char *str, int len);
ST_FUNC const char *get_tok_str(int v, CValue *cv);
ST_FUNC void begin_macro(TokenString *str, int alloc);
ST_FUNC void end_macro(void);
ST_FUNC int set_idnum(int c, int val);
ST_INLN void tok_str_new(TokenString *s);
ST_FUNC TokenString *tok_str_alloc(void);
ST_FUNC void tok_str_free(TokenString *s);
ST_FUNC void tok_str_free_str(int *str);
ST_FUNC void tok_str_add(TokenString *s, int t);
ST_FUNC void tok_str_add_tok(TokenString *s);
ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg);
ST_FUNC void define_undef(Sym *s);
ST_INLN Sym *define_find(int v);
ST_FUNC void free_defines(Sym *b);
ST_FUNC Sym *label_find(int v);
ST_FUNC Sym *label_push(Sym **ptop, int v, int flags);
ST_FUNC void label_pop(Sym **ptop, Sym *slast, int keep);
ST_FUNC void parse_define(void);
ST_FUNC void preprocess(int is_bof);
ST_FUNC void next_nomacro(void);
ST_FUNC void next(void);
ST_INLN void unget_tok(int last_tok);
ST_FUNC void preprocess_start(TCCState *s1, int is_asm);
ST_FUNC void preprocess_end(TCCState *s1);
ST_FUNC void tccpp_new(TCCState *s);
ST_FUNC void tccpp_delete(TCCState *s);
ST_FUNC int tcc_preprocess(TCCState *s1);
ST_FUNC void skip(int c);
ST_FUNC NORETURN void expect(const char *msg);

/* space excluding newline */
static inline int is_space(int ch) {
    return ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f' || ch == '\r';
}
static inline int isid(int c) {
    return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
}
static inline int isnum(int c) {
    return c >= '0' && c <= '9';
}
static inline int isoct(int c) {
    return c >= '0' && c <= '7';
}
static inline int toup(int c) {
    return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c;
}

/* ------------ tccgen.c ------------ */

#define SYM_POOL_NB (8192 / sizeof(Sym))
ST_DATA Sym *sym_free_first;
ST_DATA void **sym_pools;
ST_DATA int nb_sym_pools;

ST_DATA Sym *global_stack;
ST_DATA Sym *local_stack;
ST_DATA Sym *local_label_stack;
ST_DATA Sym *global_label_stack;
ST_DATA Sym *define_stack;
ST_DATA CType char_pointer_type, func_old_type, int_type, size_type;
ST_DATA SValue __vstack[1+/*to make bcheck happy*/ VSTACK_SIZE], *vtop, *pvtop;
#define vstack  (__vstack + 1)
ST_DATA int rsym, anon_sym, ind, loc;

ST_DATA int const_wanted; /* true if constant wanted */
ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */
ST_DATA int global_expr;  /* true if compound literals must be allocated globally (used during initializers parsing */
ST_DATA CType func_vt; /* current function return type (used by return instruction) */
ST_DATA int func_var; /* true if current function is variadic */
ST_DATA int func_vc;
ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
ST_DATA const char *funcname;
ST_DATA int g_debug;

ST_FUNC void tcc_debug_start(TCCState *s1);
ST_FUNC void tcc_debug_end(TCCState *s1);
ST_FUNC void tcc_debug_funcstart(TCCState *s1, Sym *sym);
ST_FUNC void tcc_debug_funcend(TCCState *s1, int size);
ST_FUNC void tcc_debug_line(TCCState *s1);

ST_FUNC int tccgen_compile(TCCState *s1);
ST_FUNC void free_inline_functions(TCCState *s);
ST_FUNC void check_vstack(void);

ST_INLN int is_float(int t);
ST_FUNC int ieee_finite(double d);
ST_FUNC void test_lvalue(void);
ST_FUNC void vpushi(int v);
ST_FUNC ElfSym *elfsym(Sym *);
ST_FUNC void update_storage(Sym *sym);
ST_FUNC Sym *external_global_sym(int v, CType *type, int r);
ST_FUNC void vset(CType *type, int r, int v);
ST_FUNC void vswap(void);
ST_FUNC void vpush_global_sym(CType *type, int v);
ST_FUNC void vrote(SValue *e, int n);
ST_FUNC void vrott(int n);
ST_FUNC void vrotb(int n);
#ifdef TCC_TARGET_ARM
ST_FUNC int get_reg_ex(int rc, int rc2);
ST_FUNC void lexpand_nr(void);
#endif
ST_FUNC void vpushv(SValue *v);
ST_FUNC void save_reg(int r);
ST_FUNC void save_reg_upstack(int r, int n);
ST_FUNC int get_reg(int rc);
ST_FUNC void save_regs(int n);
ST_FUNC void gaddrof(void);
ST_FUNC int gv(int rc);
ST_FUNC void gv2(int rc1, int rc2);
ST_FUNC void vpop(void);
ST_FUNC void gen_op(int op);
ST_FUNC int type_size(CType *type, int *a);
ST_FUNC void mk_pointer(CType *type);
ST_FUNC void vstore(void);
ST_FUNC void inc(int post, int c);
ST_FUNC void parse_mult_str (CString *astr, const char *msg);
ST_FUNC void parse_asm_str(CString *astr);
ST_FUNC int lvalue_type(int t);
ST_FUNC void indir(void);
ST_FUNC void unary(void);
ST_FUNC void expr_prod(void);
ST_FUNC void expr_sum(void);
ST_FUNC void gexpr(void);
ST_FUNC int expr_const(void);
#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_C67
ST_FUNC Sym *get_sym_ref(CType *type, Section *sec, unsigned long offset, unsigned long size);
#endif
#if defined TCC_TARGET_X86_64 && !defined TCC_TARGET_PE
ST_FUNC int classify_x86_64_va_arg(CType *ty);
#endif

/* ------------ tccelf.c ------------ */

#define TCC_OUTPUT_FORMAT_ELF    0 /* default output format: ELF */
#define TCC_OUTPUT_FORMAT_BINARY 1 /* binary image output */
#define TCC_OUTPUT_FORMAT_COFF   2 /* COFF */

#define ARMAG  "!<arch>\012"    /* For COFF and a.out archives */

typedef struct {
    unsigned int n_strx;         /* index into string table of name */
    unsigned char n_type;         /* type of symbol */
    unsigned char n_other;        /* misc info (usually empty) */
    unsigned short n_desc;        /* description field */
    unsigned int n_value;        /* value of symbol */
} Stab_Sym;

ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */
ST_DATA Section *common_section;
ST_DATA Section *cur_text_section; /* current section where function code is generated */
#ifdef CONFIG_TCC_ASM
ST_DATA Section *last_text_section; /* to handle .previous asm directive */
#endif
#ifdef CONFIG_TCC_BCHECK
/* bound check related sections */
ST_DATA Section *bounds_section; /* contains global data bound description */
ST_DATA Section *lbounds_section; /* contains local data bound description */
ST_FUNC void tccelf_bounds_new(TCCState *s);
#endif
/* symbol sections */
ST_DATA Section *symtab_section;
/* debug sections */
ST_DATA Section *stab_section, *stabstr_section;

ST_FUNC void tccelf_new(TCCState *s);
ST_FUNC void tccelf_delete(TCCState *s);
ST_FUNC void tccelf_stab_new(TCCState *s);
ST_FUNC void tccelf_begin_file(TCCState *s1);
ST_FUNC void tccelf_end_file(TCCState *s1);

ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags);
ST_FUNC void section_realloc(Section *sec, unsigned long new_size);
ST_FUNC size_t section_add(Section *sec, addr_t size, int align);
ST_FUNC void *section_ptr_add(Section *sec, addr_t size);
ST_FUNC void section_reserve(Section *sec, unsigned long size);
ST_FUNC Section *find_section(TCCState *s1, const char *name);
ST_FUNC Section *new_symtab(TCCState *s1, const char *symtab_name, int sh_type, int sh_flags, const char *strtab_name, const char *hash_name, int hash_sh_flags);

ST_FUNC void put_extern_sym2(Sym *sym, int sh_num, addr_t value, unsigned long size, int can_add_underscore);
ST_FUNC void put_extern_sym(Sym *sym, Section *section, addr_t value, unsigned long size);
#if PTR_SIZE == 4
ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type);
#endif
ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type, addr_t addend);

ST_FUNC int put_elf_str(Section *s, const char *sym);
ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int shndx, const char *name);
ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int shndx, const char *name);
ST_FUNC int find_elf_sym(Section *s, const char *name);
ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, int type, int symbol);
ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset, int type, int symbol, addr_t addend);

ST_FUNC void put_stabs(const char *str, int type, int other, int desc, unsigned long value);
ST_FUNC void put_stabs_r(const char *str, int type, int other, int desc, unsigned long value, Section *sec, int sym_index);
ST_FUNC void put_stabn(int type, int other, int desc, int value);
ST_FUNC void put_stabd(int type, int other, int desc);

ST_FUNC void resolve_common_syms(TCCState *s1);
ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve);
ST_FUNC void relocate_section(TCCState *s1, Section *s);

ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h);
ST_FUNC int tcc_load_object_file(TCCState *s1, int fd, unsigned long file_offset);
ST_FUNC int tcc_load_archive(TCCState *s1, int fd);
ST_FUNC void tcc_add_bcheck(TCCState *s1);
ST_FUNC void tcc_add_runtime(TCCState *s1);

ST_FUNC void build_got_entries(TCCState *s1);
ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc);
ST_FUNC void squeeze_multi_relocs(Section *sec, size_t oldrelocoffset);

ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err);
#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE
ST_FUNC void *tcc_get_symbol_err(TCCState *s, const char *name);
#endif

#ifndef TCC_TARGET_PE
ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level);
ST_FUNC int tcc_load_ldscript(TCCState *s1);
ST_FUNC uint8_t *parse_comment(uint8_t *p);
ST_FUNC void minp(void);
ST_INLN void inp(void);
ST_FUNC int handle_eob(void);
#endif

/* ------------ xxx-link.c ------------ */

/* Whether to generate a GOT/PLT entry and when. NO_GOTPLT_ENTRY is first so
   that unknown relocation don't create a GOT or PLT entry */
enum gotplt_entry {
    NO_GOTPLT_ENTRY,    /* never generate (eg. GLOB_DAT & JMP_SLOT relocs) */
    BUILD_GOT_ONLY, /* only build GOT (eg. TPOFF relocs) */
    AUTO_GOTPLT_ENTRY,  /* generate if sym is UNDEF */
    ALWAYS_GOTPLT_ENTRY /* always generate (eg. PLTOFF relocs) */
};

ST_FUNC int code_reloc (int reloc_type);
ST_FUNC int gotplt_entry_type (int reloc_type);
ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr);
ST_FUNC void relocate_init(Section *sr);
ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val);
ST_FUNC void relocate_plt(TCCState *s1);

/* ------------ xxx-gen.c ------------ */

ST_DATA const int reg_classes[NB_REGS];

ST_FUNC void gsym_addr(int t, int a);
ST_FUNC void gsym(int t);
ST_FUNC void load(int r, SValue *sv);
ST_FUNC void store(int r, SValue *v);
ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize);
ST_FUNC void gfunc_call(int nb_args);
ST_FUNC void gfunc_prolog(CType *func_type);
ST_FUNC void gfunc_epilog(void);
ST_FUNC int gjmp(int t);
ST_FUNC void gjmp_addr(int a);
ST_FUNC int gtst(int inv, int t);
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
ST_FUNC void gtst_addr(int inv, int a);
#else
#define gtst_addr(inv, a) gsym_addr(gtst(inv, 0), a)
#endif
ST_FUNC void gen_opi(int op);
ST_FUNC void gen_opf(int op);
ST_FUNC void gen_cvt_ftoi(int t);
ST_FUNC void gen_cvt_ftof(int t);
ST_FUNC void ggoto(void);
#ifndef TCC_TARGET_C67
ST_FUNC void o(unsigned int c);
#endif
#ifndef TCC_TARGET_ARM
ST_FUNC void gen_cvt_itof(int t);
#endif
ST_FUNC void gen_vla_sp_save(int addr);
ST_FUNC void gen_vla_sp_restore(int addr);
ST_FUNC void gen_vla_alloc(CType *type, int align);

static inline uint16_t read16le(unsigned char *p) {
    return p[0] | (uint16_t)p[1] << 8;
}
static inline void write16le(unsigned char *p, uint16_t x) {
    p[0] = x & 255;  p[1] = x >> 8 & 255;
}
static inline uint32_t read32le(unsigned char *p) {
  return read16le(p) | (uint32_t)read16le(p + 2) << 16;
}
static inline void write32le(unsigned char *p, uint32_t x) {
    write16le(p, x);  write16le(p + 2, x >> 16);
}
static inline void add32le(unsigned char *p, int32_t x) {
    write32le(p, read32le(p) + x);
}
static inline uint64_t read64le(unsigned char *p) {
  return read32le(p) | (uint64_t)read32le(p + 4) << 32;
}
static inline void write64le(unsigned char *p, uint64_t x) {
    write32le(p, x);  write32le(p + 4, x >> 32);
}
static inline void add64le(unsigned char *p, int64_t x) {
    write64le(p, read64le(p) + x);
}

/* ------------ i386-gen.c ------------ */
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
ST_FUNC void g(int c);
ST_FUNC void gen_le16(int c);
ST_FUNC void gen_le32(int c);
ST_FUNC void gen_addr32(int r, Sym *sym, int c);
ST_FUNC void gen_addrpc32(int r, Sym *sym, int c);
#endif

#ifdef CONFIG_TCC_BCHECK
ST_FUNC void gen_bounded_ptr_add(void);
ST_FUNC void gen_bounded_ptr_deref(void);
#endif

/* ------------ x86_64-gen.c ------------ */
#ifdef TCC_TARGET_X86_64
ST_FUNC void gen_addr64(int r, Sym *sym, int64_t c);
ST_FUNC void gen_opl(int op);
#ifdef TCC_TARGET_PE
ST_FUNC void gen_vla_result(int addr);
#endif
#endif

/* ------------ arm-gen.c ------------ */
#ifdef TCC_TARGET_ARM
#if defined(TCC_ARM_EABI) && !defined(CONFIG_TCC_ELFINTERP)
PUB_FUNC const char *default_elfinterp(struct TCCState *s);
#endif
ST_FUNC void arm_init(struct TCCState *s);
ST_FUNC void gen_cvt_itof1(int t);
#endif

/* ------------ arm64-gen.c ------------ */
#ifdef TCC_TARGET_ARM64
ST_FUNC void gen_cvt_sxtw(void);
ST_FUNC void gen_opl(int op);
ST_FUNC void gfunc_return(CType *func_type);
ST_FUNC void gen_va_start(void);
ST_FUNC void gen_va_arg(CType *t);
ST_FUNC void gen_clear_cache(void);
#endif

/* ------------ c67-gen.c ------------ */
#ifdef TCC_TARGET_C67
#endif

/* ------------ tcccoff.c ------------ */

#ifdef TCC_TARGET_COFF
ST_FUNC int tcc_output_coff(TCCState *s1, FILE *f);
ST_FUNC int tcc_load_coff(TCCState * s1, int fd);
#endif

/* ------------ tccasm.c ------------ */
ST_FUNC void asm_instr(void);
ST_FUNC void asm_global_instr(void);
#ifdef CONFIG_TCC_ASM
ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands, const char *name, const char **pp);
ST_FUNC Sym* get_asm_sym(int name, Sym *csym);
ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
ST_FUNC int asm_int_expr(TCCState *s1);
ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess);
/* ------------ i386-asm.c ------------ */
ST_FUNC void gen_expr32(ExprValue *pe);
#ifdef TCC_TARGET_X86_64
ST_FUNC void gen_expr64(ExprValue *pe);
#endif
ST_FUNC void asm_opcode(TCCState *s1, int opcode);
ST_FUNC int asm_parse_regvar(int t);
ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg);
ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier);
ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg);
ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str);
#endif

/* ------------ tccpe.c -------------- */
#ifdef TCC_TARGET_PE
ST_FUNC int pe_load_file(struct TCCState *s1, const char *filename, int fd);
ST_FUNC int pe_output_file(TCCState * s1, const char *filename);
ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t value);
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
ST_FUNC SValue *pe_getimport(SValue *sv, SValue *v2);
#endif
#ifdef TCC_TARGET_X86_64
ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack);
#endif
PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp);
/* symbol properties stored in Elf32_Sym->st_other */
# define ST_PE_EXPORT 0x10
# define ST_PE_IMPORT 0x20
# define ST_PE_STDCALL 0x40
#endif
#define ST_ASM_SET 0x04

/* ------------ tccrun.c ----------------- */
#ifdef TCC_IS_NATIVE
#ifdef CONFIG_TCC_STATIC
#define RTLD_LAZY       0x001
#define RTLD_NOW        0x002
#define RTLD_GLOBAL     0x100
#define RTLD_DEFAULT    NULL
/* dummy function for profiling */
ST_FUNC void *dlopen(const char *filename, int flag);
ST_FUNC void dlclose(void *p);
ST_FUNC const char *dlerror(void);
ST_FUNC void *dlsym(void *handle, const char *symbol);
#endif
#ifdef CONFIG_TCC_BACKTRACE
ST_DATA int rt_num_callers;
ST_DATA const char **rt_bound_error_msg;
ST_DATA void *rt_prog_main;
ST_FUNC void tcc_set_num_callers(int n);
#endif
ST_FUNC void tcc_run_free(TCCState *s1);
#endif

/* ------------ tcctools.c ----------------- */
#if 0 /* included in tcc.c */
ST_FUNC int tcc_tool_ar(TCCState *s, int argc, char **argv);
#ifdef TCC_TARGET_PE
ST_FUNC int tcc_tool_impdef(TCCState *s, int argc, char **argv);
#endif
ST_FUNC void tcc_tool_cross(TCCState *s, char **argv, int option);
ST_FUNC void gen_makedeps(TCCState *s, const char *target, const char *filename);
#endif

/********************************************************/
#undef ST_DATA
#if ONE_SOURCE
#define ST_DATA static
#else
#define ST_DATA
#endif
/********************************************************/
#endif /* _TCC_H */

https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/libtcc.c

File: ../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/libtcc.c used in: Step 488
/*
 *  TCC - Tiny C Compiler
 *
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "tcc.h"

/********************************************************/
/* global variables */

/* use GNU C extensions */
ST_DATA int gnu_ext = 1;

/* use TinyCC extensions */
ST_DATA int tcc_ext = 1;

/* XXX: get rid of this ASAP */
ST_DATA struct TCCState *tcc_state;

static int nb_states;

/********************************************************/

#if ONE_SOURCE
#include "tccpp.c"
#include "tccgen.c"
#include "tccelf.c"
#include "tccrun.c"
#ifdef TCC_TARGET_I386
#include "i386-gen.c"
#include "i386-link.c"
#include "i386-asm.c"
#endif
#ifdef TCC_TARGET_ARM
#include "arm-gen.c"
#include "arm-link.c"
#include "arm-asm.c"
#endif
#ifdef TCC_TARGET_ARM64
#include "arm64-gen.c"
#include "arm64-link.c"
#endif
#ifdef TCC_TARGET_C67
#include "c67-gen.c"
#include "c67-link.c"
#include "tcccoff.c"
#endif
#ifdef TCC_TARGET_X86_64
#include "x86_64-gen.c"
#include "x86_64-link.c"
#include "i386-asm.c"
#endif
#ifdef CONFIG_TCC_ASM
#include "tccasm.c"
#endif
#ifdef TCC_TARGET_PE
#include "tccpe.c"
#endif
#endif /* ONE_SOURCE */

/********************************************************/
#ifndef CONFIG_TCC_ASM
ST_FUNC void asm_instr(void)
{
    tcc_error("inline asm() not supported");
}
ST_FUNC void asm_global_instr(void)
{
    tcc_error("inline asm() not supported");
}
#endif

/********************************************************/
#ifdef _WIN32
ST_FUNC char *normalize_slashes(char *path)
{
    char *p;
    for (p = path; *p; ++p)
        if (*p == '\\')
            *p = '/';
    return path;
}

static HMODULE tcc_module;

/* on win32, we suppose the lib and includes are at the location of 'tcc.exe' */
static void tcc_set_lib_path_w32(TCCState *s)
{
    char path[1024], *p;
    GetModuleFileNameA(tcc_module, path, sizeof path);
    p = tcc_basename(normalize_slashes(strlwr(path)));
    if (p > path)
        --p;
    *p = 0;
    tcc_set_lib_path(s, path);
}

#ifdef TCC_TARGET_PE
static void tcc_add_systemdir(TCCState *s)
{
    char buf[1000];
    GetSystemDirectory(buf, sizeof buf);
    tcc_add_library_path(s, normalize_slashes(buf));
}
#endif

#ifdef LIBTCC_AS_DLL
BOOL WINAPI DllMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved)
{
    if (DLL_PROCESS_ATTACH == dwReason)
        tcc_module = hDll;
    return TRUE;
}
#endif
#endif

/********************************************************/
/* copy a string and truncate it. */
ST_FUNC char *pstrcpy(char *buf, int buf_size, const char *s)
{
    char *q, *q_end;
    int c;

    if (buf_size > 0) {
        q = buf;
        q_end = buf + buf_size - 1;
        while (q < q_end) {
            c = *s++;
            if (c == '\0')
                break;
            *q++ = c;
        }
        *q = '\0';
    }
    return buf;
}

/* strcat and truncate. */
ST_FUNC char *pstrcat(char *buf, int buf_size, const char *s)
{
    int len;
    len = strlen(buf);
    if (len < buf_size)
        pstrcpy(buf + len, buf_size - len, s);
    return buf;
}

ST_FUNC char *pstrncpy(char *out, const char *in, size_t num)
{
    memcpy(out, in, num);
    out[num] = '\0';
    return out;
}

/* extract the basename of a file */
PUB_FUNC char *tcc_basename(const char *name)
{
    char *p = strchr(name, 0);
    while (p > name && !IS_DIRSEP(p[-1]))
        --p;
    return p;
}

/* extract extension part of a file
 *
 * (if no extension, return pointer to end-of-string)
 */
PUB_FUNC char *tcc_fileextension (const char *name)
{
    char *b = tcc_basename(name);
    char *e = strrchr(b, '.');
    return e ? e : strchr(b, 0);
}

/********************************************************/
/* memory management */

#undef free
#undef malloc
#undef realloc

#ifndef MEM_DEBUG

PUB_FUNC void tcc_free(void *ptr)
{
    free(ptr);
}

PUB_FUNC void *tcc_malloc(unsigned long size)
{
    void *ptr;
    ptr = malloc(size);
    if (!ptr && size)
        tcc_error("memory full (malloc)");
    return ptr;
}

PUB_FUNC void *tcc_mallocz(unsigned long size)
{
    void *ptr;
    ptr = tcc_malloc(size);
    memset(ptr, 0, size);
    return ptr;
}

PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size)
{
    void *ptr1;
    ptr1 = realloc(ptr, size);
    if (!ptr1 && size)
        tcc_error("memory full (realloc)");
    return ptr1;
}

PUB_FUNC char *tcc_strdup(const char *str)
{
    char *ptr;
    ptr = tcc_malloc(strlen(str) + 1);
    strcpy(ptr, str);
    return ptr;
}

PUB_FUNC void tcc_memcheck(void)
{
}

#else

#define MEM_DEBUG_MAGIC1 0xFEEDDEB1
#define MEM_DEBUG_MAGIC2 0xFEEDDEB2
#define MEM_DEBUG_MAGIC3 0xFEEDDEB3
#define MEM_DEBUG_FILE_LEN 40
#define MEM_DEBUG_CHECK3(header) \
    ((mem_debug_header_t*)((char*)header + header->size))->magic3
#define MEM_USER_PTR(header) \
    ((char *)header + offsetof(mem_debug_header_t, magic3))
#define MEM_HEADER_PTR(ptr) \
    (mem_debug_header_t *)((char*)ptr - offsetof(mem_debug_header_t, magic3))

struct mem_debug_header {
    unsigned magic1;
    unsigned size;
    struct mem_debug_header *prev;
    struct mem_debug_header *next;
    int line_num;
    char file_name[MEM_DEBUG_FILE_LEN + 1];
    unsigned magic2;
    ALIGNED(16) unsigned magic3;
};

typedef struct mem_debug_header mem_debug_header_t;

static mem_debug_header_t *mem_debug_chain;
static unsigned mem_cur_size;
static unsigned mem_max_size;

static mem_debug_header_t *malloc_check(void *ptr, const char *msg)
{
    mem_debug_header_t * header = MEM_HEADER_PTR(ptr);
    if (header->magic1 != MEM_DEBUG_MAGIC1 ||
        header->magic2 != MEM_DEBUG_MAGIC2 ||
        MEM_DEBUG_CHECK3(header) != MEM_DEBUG_MAGIC3 ||
        header->size == (unsigned)-1) {
        fprintf(stderr, "%s check failed\n", msg);
        if (header->magic1 == MEM_DEBUG_MAGIC1)
            fprintf(stderr, "%s:%u: block allocated here.\n",
                header->file_name, header->line_num);
        exit(1);
    }
    return header;
}

PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line)
{
    int ofs;
    mem_debug_header_t *header;

    header = malloc(sizeof(mem_debug_header_t) + size);
    if (!header)
        tcc_error("memory full (malloc)");

    header->magic1 = MEM_DEBUG_MAGIC1;
    header->magic2 = MEM_DEBUG_MAGIC2;
    header->size = size;
    MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3;
    header->line_num = line;
    ofs = strlen(file) - MEM_DEBUG_FILE_LEN;
    strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), MEM_DEBUG_FILE_LEN);
    header->file_name[MEM_DEBUG_FILE_LEN] = 0;

    header->next = mem_debug_chain;
    header->prev = NULL;
    if (header->next)
        header->next->prev = header;
    mem_debug_chain = header;

    mem_cur_size += size;
    if (mem_cur_size > mem_max_size)
        mem_max_size = mem_cur_size;

    return MEM_USER_PTR(header);
}

PUB_FUNC void tcc_free_debug(void *ptr)
{
    mem_debug_header_t *header;
    if (!ptr)
        return;
    header = malloc_check(ptr, "tcc_free");
    mem_cur_size -= header->size;
    header->size = (unsigned)-1;
    if (header->next)
        header->next->prev = header->prev;
    if (header->prev)
        header->prev->next = header->next;
    if (header == mem_debug_chain)
        mem_debug_chain = header->next;
    free(header);
}

PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line)
{
    void *ptr;
    ptr = tcc_malloc_debug(size,file,line);
    memset(ptr, 0, size);
    return ptr;
}

PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line)
{
    mem_debug_header_t *header;
    int mem_debug_chain_update = 0;
    if (!ptr)
        return tcc_malloc_debug(size, file, line);
    header = malloc_check(ptr, "tcc_realloc");
    mem_cur_size -= header->size;
    mem_debug_chain_update = (header == mem_debug_chain);
    header = realloc(header, sizeof(mem_debug_header_t) + size);
    if (!header)
        tcc_error("memory full (realloc)");
    header->size = size;
    MEM_DEBUG_CHECK3(header) = MEM_DEBUG_MAGIC3;
    if (header->next)
        header->next->prev = header;
    if (header->prev)
        header->prev->next = header;
    if (mem_debug_chain_update)
        mem_debug_chain = header;
    mem_cur_size += size;
    if (mem_cur_size > mem_max_size)
        mem_max_size = mem_cur_size;
    return MEM_USER_PTR(header);
}

PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line)
{
    char *ptr;
    ptr = tcc_malloc_debug(strlen(str) + 1, file, line);
    strcpy(ptr, str);
    return ptr;
}

PUB_FUNC void tcc_memcheck(void)
{
    if (mem_cur_size) {
        mem_debug_header_t *header = mem_debug_chain;
        fprintf(stderr, "MEM_DEBUG: mem_leak= %d bytes, mem_max_size= %d bytes\n",
            mem_cur_size, mem_max_size);
        while (header) {
            fprintf(stderr, "%s:%u: error: %u bytes leaked\n",
                header->file_name, header->line_num, header->size);
            header = header->next;
        }
#if MEM_DEBUG-0 == 2
        exit(2);
#endif
    }
}
#endif /* MEM_DEBUG */

#define free(p) use_tcc_free(p)
#define malloc(s) use_tcc_malloc(s)
#define realloc(p, s) use_tcc_realloc(p, s)

/********************************************************/
/* dynarrays */

ST_FUNC void dynarray_add(void *ptab, int *nb_ptr, void *data)
{
    int nb, nb_alloc;
    void **pp;

    nb = *nb_ptr;
    pp = *(void ***)ptab;
    /* every power of two we double array size */
    if ((nb & (nb - 1)) == 0) {
        if (!nb)
            nb_alloc = 1;
        else
            nb_alloc = nb * 2;
        pp = tcc_realloc(pp, nb_alloc * sizeof(void *));
        *(void***)ptab = pp;
    }
    pp[nb++] = data;
    *nb_ptr = nb;
}

ST_FUNC void dynarray_reset(void *pp, int *n)
{
    void **p;
    for (p = *(void***)pp; *n; ++p, --*n)
        if (*p)
            tcc_free(*p);
    tcc_free(*(void**)pp);
    *(void**)pp = NULL;
}

static void tcc_split_path(TCCState *s, void *p_ary, int *p_nb_ary, const char *in)
{
    const char *p;
    do {
        int c;
        CString str;

        cstr_new(&str);
        for (p = in; c = *p, c != '\0' && c != PATHSEP[0]; ++p) {
            if (c == '{' && p[1] && p[2] == '}') {
                c = p[1], p += 2;
                if (c == 'B')
                    cstr_cat(&str, s->tcc_lib_path, -1);
            } else {
                cstr_ccat(&str, c);
            }
        }
        if (str.size) {
            cstr_ccat(&str, '\0');
            dynarray_add(p_ary, p_nb_ary, tcc_strdup(str.data));
        }
        cstr_free(&str);
        in = p+1;
    } while (*p);
}

/********************************************************/

static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap)
{
    int len;
    len = strlen(buf);
    vsnprintf(buf + len, buf_size - len, fmt, ap);
}

static void strcat_printf(char *buf, int buf_size, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    strcat_vprintf(buf, buf_size, fmt, ap);
    va_end(ap);
}

static void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap)
{
    char buf[2048];
    BufferedFile **pf, *f;

    buf[0] = '\0';
    /* use upper file if inline ":asm:" or token ":paste:" */
    for (f = file; f && f->filename[0] == ':'; f = f->prev)
     ;
    if (f) {
        for(pf = s1->include_stack; pf < s1->include_stack_ptr; pf++)
            strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n",
                (*pf)->filename, (*pf)->line_num);
        if (s1->error_set_jmp_enabled) {
            strcat_printf(buf, sizeof(buf), "%s:%d: ",
                f->filename, f->line_num - !!(tok_flags & TOK_FLAG_BOL));
        } else {
            strcat_printf(buf, sizeof(buf), "%s: ",
                f->filename);
        }
    } else {
        strcat_printf(buf, sizeof(buf), "tcc: ");
    }
    if (is_warning)
        strcat_printf(buf, sizeof(buf), "warning: ");
    else
        strcat_printf(buf, sizeof(buf), "error: ");
    strcat_vprintf(buf, sizeof(buf), fmt, ap);

    if (!s1->error_func) {
        /* default case: stderr */
        if (s1->output_type == TCC_OUTPUT_PREPROCESS && s1->ppfp == stdout)
            /* print a newline during tcc -E */
            printf("\n"), fflush(stdout);
        fflush(stdout); /* flush -v output */
        fprintf(stderr, "%s\n", buf);
        fflush(stderr); /* print error/warning now (win32) */
    } else {
        s1->error_func(s1->error_opaque, buf);
    }
    if (!is_warning || s1->warn_error)
        s1->nb_errors++;
}

LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque,
                        void (*error_func)(void *opaque, const char *msg))
{
    s->error_opaque = error_opaque;
    s->error_func = error_func;
}

/* error without aborting current compilation */
PUB_FUNC void tcc_error_noabort(const char *fmt, ...)
{
    TCCState *s1 = tcc_state;
    va_list ap;

    va_start(ap, fmt);
    error1(s1, 0, fmt, ap);
    va_end(ap);
}

PUB_FUNC void tcc_error(const char *fmt, ...)
{
    TCCState *s1 = tcc_state;
    va_list ap;

    va_start(ap, fmt);
    error1(s1, 0, fmt, ap);
    va_end(ap);
    /* better than nothing: in some cases, we accept to handle errors */
    if (s1->error_set_jmp_enabled) {
        longjmp(s1->error_jmp_buf, 1);
    } else {
        /* XXX: eliminate this someday */
        exit(1);
    }
}

PUB_FUNC void tcc_warning(const char *fmt, ...)
{
    TCCState *s1 = tcc_state;
    va_list ap;

    if (s1->warn_none)
        return;

    va_start(ap, fmt);
    error1(s1, 1, fmt, ap);
    va_end(ap);
}

/********************************************************/
/* I/O layer */

ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen)
{
    BufferedFile *bf;
    int buflen = initlen ? initlen : IO_BUF_SIZE;

    bf = tcc_mallocz(sizeof(BufferedFile) + buflen);
    bf->buf_ptr = bf->buffer;
    bf->buf_end = bf->buffer + initlen;
    bf->buf_end[0] = CH_EOB; /* put eob symbol */
    pstrcpy(bf->filename, sizeof(bf->filename), filename);
    bf->true_filename = bf->filename;
    bf->line_num = 1;
    bf->ifdef_stack_ptr = s1->ifdef_stack_ptr;
    bf->fd = -1;
    bf->prev = file;
    file = bf;
    tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
}

ST_FUNC void tcc_close(void)
{
    BufferedFile *bf = file;
    if (bf->fd > 0) {
        close(bf->fd);
        total_lines += bf->line_num;
    }
    if (bf->true_filename != bf->filename)
        tcc_free(bf->true_filename);
    file = bf->prev;
    tcc_free(bf);
}

ST_FUNC int tcc_open(TCCState *s1, const char *filename)
{
    int fd;
    if (strcmp(filename, "-") == 0)
        fd = 0, filename = "<stdin>";
    else
        fd = open(filename, O_RDONLY | O_BINARY);
    if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3)
        printf("%s %*s%s\n", fd < 0 ? "nf":"->",
               (int)(s1->include_stack_ptr - s1->include_stack), "", filename);
    if (fd < 0)
        return -1;
    tcc_open_bf(s1, filename, 0);
#ifdef _WIN32
    normalize_slashes(file->filename);
#endif
    file->fd = fd;
    return fd;
}

/* compile the file opened in 'file'. Return non zero if errors. */
static int tcc_compile(TCCState *s1)
{
    Sym *define_start;
    int filetype, is_asm;

    define_start = define_stack;
    filetype = s1->filetype;
    is_asm = filetype == AFF_TYPE_ASM || filetype == AFF_TYPE_ASMPP;
    tccelf_begin_file(s1);

    if (setjmp(s1->error_jmp_buf) == 0) {
        s1->nb_errors = 0;
        s1->error_set_jmp_enabled = 1;

        preprocess_start(s1, is_asm);
        if (s1->output_type == TCC_OUTPUT_PREPROCESS) {
            tcc_preprocess(s1);
        } else if (is_asm) {
#ifdef CONFIG_TCC_ASM
            tcc_assemble(s1, filetype == AFF_TYPE_ASMPP);
#else
            tcc_error_noabort("asm not supported");
#endif
        } else {
            tccgen_compile(s1);
        }
    }
    s1->error_set_jmp_enabled = 0;

    preprocess_end(s1);
    free_inline_functions(s1);
    /* reset define stack, but keep -D and built-ins */
    free_defines(define_start);
    sym_pop(&global_stack, NULL, 0);
    sym_pop(&local_stack, NULL, 0);
    tccelf_end_file(s1);
    return s1->nb_errors != 0 ? -1 : 0;
}

LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str)
{
    int len, ret;

    len = strlen(str);
    tcc_open_bf(s, "<string>", len);
    memcpy(file->buffer, str, len);
    ret = tcc_compile(s);
    tcc_close();
    return ret;
}

/* define a preprocessor symbol. A value can also be provided with the '=' operator */
LIBTCCAPI void tcc_define_symbol(TCCState *s1, const char *sym, const char *value)
{
    int len1, len2;
    /* default value */
    if (!value)
        value = "1";
    len1 = strlen(sym);
    len2 = strlen(value);

    /* init file structure */
    tcc_open_bf(s1, "<define>", len1 + len2 + 1);
    memcpy(file->buffer, sym, len1);
    file->buffer[len1] = ' ';
    memcpy(file->buffer + len1 + 1, value, len2);

    /* parse with define parser */
    next_nomacro();
    parse_define();
    tcc_close();
}

/* undefine a preprocessor symbol */
LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym)
{
    TokenSym *ts;
    Sym *s;
    ts = tok_alloc(sym, strlen(sym));
    s = define_find(ts->tok);
    /* undefine symbol by putting an invalid name */
    if (s)
        define_undef(s);
}

/* cleanup all static data used during compilation */
static void tcc_cleanup(void)
{
    if (NULL == tcc_state)
        return;
    while (file)
        tcc_close();
    tccpp_delete(tcc_state);
    tcc_state = NULL;
    /* free sym_pools */
    dynarray_reset(&sym_pools, &nb_sym_pools);
    /* reset symbol stack */
    sym_free_first = NULL;
}

LIBTCCAPI TCCState *tcc_new(void)
{
    TCCState *s;

    tcc_cleanup();

    s = tcc_mallocz(sizeof(TCCState));
    if (!s)
        return NULL;
    tcc_state = s;
    ++nb_states;

    s->alacarte_link = 1;
    s->nocommon = 1;
    s->warn_implicit_function_declaration = 1;
    s->ms_extensions = 1;

#ifdef CHAR_IS_UNSIGNED
    s->char_is_unsigned = 1;
#endif
#ifdef TCC_TARGET_I386
    s->seg_size = 32;
#endif
    /* enable this if you want symbols with leading underscore on windows: */
#if 0 /* def TCC_TARGET_PE */
    s->leading_underscore = 1;
#endif
#ifdef _WIN32
    tcc_set_lib_path_w32(s);
#else
    tcc_set_lib_path(s, CONFIG_TCCDIR);
#endif
    tccelf_new(s);
    tccpp_new(s);

    /* we add dummy defines for some special macros to speed up tests
       and to have working defined() */
    define_push(TOK___LINE__, MACRO_OBJ, NULL, NULL);
    define_push(TOK___FILE__, MACRO_OBJ, NULL, NULL);
    define_push(TOK___DATE__, MACRO_OBJ, NULL, NULL);
    define_push(TOK___TIME__, MACRO_OBJ, NULL, NULL);
    define_push(TOK___COUNTER__, MACRO_OBJ, NULL, NULL);
    {
        /* define __TINYC__ 92X  */
        char buffer[32]; int a,b,c;
        sscanf(TCC_VERSION, "%d.%d.%d", &a, &b, &c);
        sprintf(buffer, "%d", a*10000 + b*100 + c);
        tcc_define_symbol(s, "__TINYC__", buffer);
    }

    /* standard defines */
    tcc_define_symbol(s, "__STDC__", NULL);
    tcc_define_symbol(s, "__STDC_VERSION__", "199901L");
    tcc_define_symbol(s, "__STDC_HOSTED__", NULL);

    /* target defines */
#if defined(TCC_TARGET_I386)
    tcc_define_symbol(s, "__i386__", NULL);
    tcc_define_symbol(s, "__i386", NULL);
    tcc_define_symbol(s, "i386", NULL);
#elif defined(TCC_TARGET_X86_64)
    tcc_define_symbol(s, "__x86_64__", NULL);
#elif defined(TCC_TARGET_ARM)
    tcc_define_symbol(s, "__ARM_ARCH_4__", NULL);
    tcc_define_symbol(s, "__arm_elf__", NULL);
    tcc_define_symbol(s, "__arm_elf", NULL);
    tcc_define_symbol(s, "arm_elf", NULL);
    tcc_define_symbol(s, "__arm__", NULL);
    tcc_define_symbol(s, "__arm", NULL);
    tcc_define_symbol(s, "arm", NULL);
    tcc_define_symbol(s, "__APCS_32__", NULL);
    tcc_define_symbol(s, "__ARMEL__", NULL);
#if defined(TCC_ARM_EABI)
    tcc_define_symbol(s, "__ARM_EABI__", NULL);
#endif
#if defined(TCC_ARM_HARDFLOAT)
    s->float_abi = ARM_HARD_FLOAT;
    tcc_define_symbol(s, "__ARM_PCS_VFP", NULL);
#else
    s->float_abi = ARM_SOFTFP_FLOAT;
#endif
#elif defined(TCC_TARGET_ARM64)
    tcc_define_symbol(s, "__aarch64__", NULL);
#elif defined TCC_TARGET_C67
    tcc_define_symbol(s, "__C67__", NULL);
#endif

#ifdef TCC_TARGET_PE
    tcc_define_symbol(s, "_WIN32", NULL);
# ifdef TCC_TARGET_X86_64
    tcc_define_symbol(s, "_WIN64", NULL);
# endif
#else
    tcc_define_symbol(s, "__unix__", NULL);
    tcc_define_symbol(s, "__unix", NULL);
    tcc_define_symbol(s, "unix", NULL);
# if defined(__linux__)
    tcc_define_symbol(s, "__linux__", NULL);
    tcc_define_symbol(s, "__linux", NULL);
# endif
# if defined(__FreeBSD__)
    tcc_define_symbol(s, "__FreeBSD__", "__FreeBSD__");
    /* No 'Thread Storage Local' on FreeBSD with tcc */
    tcc_define_symbol(s, "__NO_TLS", NULL);
# endif
# if defined(__FreeBSD_kernel__)
    tcc_define_symbol(s, "__FreeBSD_kernel__", NULL);
# endif
# if defined(__NetBSD__)
    tcc_define_symbol(s, "__NetBSD__", "__NetBSD__");
# endif
# if defined(__OpenBSD__)
    tcc_define_symbol(s, "__OpenBSD__", "__OpenBSD__");
# endif
#endif

    /* TinyCC & gcc defines */
#if PTR_SIZE == 4
    /* 32bit systems. */
    tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned int");
    tcc_define_symbol(s, "__PTRDIFF_TYPE__", "int");
    tcc_define_symbol(s, "__ILP32__", NULL);
#elif LONG_SIZE == 4
    /* 64bit Windows. */
    tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long long");
    tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long long");
    tcc_define_symbol(s, "__LLP64__", NULL);
#else
    /* Other 64bit systems. */
    tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long");
    tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long");
    tcc_define_symbol(s, "__LP64__", NULL);
#endif

#ifdef TCC_TARGET_PE
    tcc_define_symbol(s, "__WCHAR_TYPE__", "unsigned short");
    tcc_define_symbol(s, "__WINT_TYPE__", "unsigned short");
#else
    tcc_define_symbol(s, "__WCHAR_TYPE__", "int");
    /* wint_t is unsigned int by default, but (signed) int on BSDs
       and unsigned short on windows.  Other OSes might have still
       other conventions, sigh.  */
# if defined(__FreeBSD__) || defined (__FreeBSD_kernel__) \
  || defined(__NetBSD__) || defined(__OpenBSD__)
    tcc_define_symbol(s, "__WINT_TYPE__", "int");
#  ifdef __FreeBSD__
    /* define __GNUC__ to have some useful stuff from sys/cdefs.h
       that are unconditionally used in FreeBSDs other system headers :/ */
    tcc_define_symbol(s, "__GNUC__", "2");
    tcc_define_symbol(s, "__GNUC_MINOR__", "7");
    tcc_define_symbol(s, "__builtin_alloca", "alloca");
#  endif
# else
    tcc_define_symbol(s, "__WINT_TYPE__", "unsigned int");
    /* glibc defines */
    tcc_define_symbol(s, "__REDIRECT(name, proto, alias)",
        "name proto __asm__ (#alias)");
    tcc_define_symbol(s, "__REDIRECT_NTH(name, proto, alias)",
        "name proto __asm__ (#alias) __THROW");
# endif
# if defined(TCC_MUSL)
    tcc_define_symbol(s, "__DEFINED_va_list", "");
    tcc_define_symbol(s, "__DEFINED___isoc_va_list", "");
    tcc_define_symbol(s, "__isoc_va_list", "void *");
# endif /* TCC_MUSL */
    /* Some GCC builtins that are simple to express as macros.  */
    tcc_define_symbol(s, "__builtin_extract_return_addr(x)", "x");
#endif /* ndef TCC_TARGET_PE */
    return s;
}

LIBTCCAPI void tcc_delete(TCCState *s1)
{
    tcc_cleanup();

    /* free sections */
    tccelf_delete(s1);

    /* free library paths */
    dynarray_reset(&s1->library_paths, &s1->nb_library_paths);
    dynarray_reset(&s1->crt_paths, &s1->nb_crt_paths);

    /* free include paths */
    dynarray_reset(&s1->cached_includes, &s1->nb_cached_includes);
    dynarray_reset(&s1->include_paths, &s1->nb_include_paths);
    dynarray_reset(&s1->sysinclude_paths, &s1->nb_sysinclude_paths);
    dynarray_reset(&s1->cmd_include_files, &s1->nb_cmd_include_files);

    tcc_free(s1->tcc_lib_path);
    tcc_free(s1->soname);
    tcc_free(s1->rpath);
    tcc_free(s1->init_symbol);
    tcc_free(s1->fini_symbol);
    tcc_free(s1->outfile);
    tcc_free(s1->deps_outfile);
    dynarray_reset(&s1->files, &s1->nb_files);
    dynarray_reset(&s1->target_deps, &s1->nb_target_deps);
    dynarray_reset(&s1->pragma_libs, &s1->nb_pragma_libs);
    dynarray_reset(&s1->argv, &s1->argc);

#ifdef TCC_IS_NATIVE
    /* free runtime memory */
    tcc_run_free(s1);
#endif

    tcc_free(s1);
    if (0 == --nb_states)
        tcc_memcheck();
}

LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type)
{
    s->output_type = output_type;

    /* always elf for objects */
    if (output_type == TCC_OUTPUT_OBJ)
        s->output_format = TCC_OUTPUT_FORMAT_ELF;

    if (s->char_is_unsigned)
        tcc_define_symbol(s, "__CHAR_UNSIGNED__", NULL);

    if (!s->nostdinc) {
        /* default include paths */
        /* -isystem paths have already been handled */
        tcc_add_sysinclude_path(s, CONFIG_TCC_SYSINCLUDEPATHS);
    }

#ifdef CONFIG_TCC_BCHECK
    if (s->do_bounds_check) {
        /* if bound checking, then add corresponding sections */
        tccelf_bounds_new(s);
        /* define symbol */
        tcc_define_symbol(s, "__BOUNDS_CHECKING_ON", NULL);
    }
#endif
    if (s->do_debug) {
        /* add debug sections */
        tccelf_stab_new(s);
    }

    tcc_add_library_path(s, CONFIG_TCC_LIBPATHS);

#ifdef TCC_TARGET_PE
# ifdef _WIN32
    if (!s->nostdlib && output_type != TCC_OUTPUT_OBJ)
        tcc_add_systemdir(s);
# endif
#else
    /* paths for crt objects */
    tcc_split_path(s, &s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX);
    /* add libc crt1/crti objects */
    if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) &&
        !s->nostdlib) {
        if (output_type != TCC_OUTPUT_DLL)
            tcc_add_crt(s, "crt1.o");
        tcc_add_crt(s, "crti.o");
    }
#endif
    return 0;
}

LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname)
{
    tcc_split_path(s, &s->include_paths, &s->nb_include_paths, pathname);
    return 0;
}

LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname)
{
    tcc_split_path(s, &s->sysinclude_paths, &s->nb_sysinclude_paths, pathname);
    return 0;
}

ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags)
{
    int ret;

    /* open the file */
    ret = tcc_open(s1, filename);
    if (ret < 0) {
        if (flags & AFF_PRINT_ERROR)
            tcc_error_noabort("file '%s' not found", filename);
        return ret;
    }

    /* update target deps */
    dynarray_add(&s1->target_deps, &s1->nb_target_deps,
            tcc_strdup(filename));

    if (flags & AFF_TYPE_BIN) {
        ElfW(Ehdr) ehdr;
        int fd, obj_type;

        fd = file->fd;
        obj_type = tcc_object_type(fd, &ehdr);
        lseek(fd, 0, SEEK_SET);

#ifdef TCC_TARGET_MACHO
        if (0 == obj_type && 0 == strcmp(tcc_fileextension(filename), ".dylib"))
            obj_type = AFF_BINTYPE_DYN;
#endif

        switch (obj_type) {
        case AFF_BINTYPE_REL:
            ret = tcc_load_object_file(s1, fd, 0);
            break;
#ifndef TCC_TARGET_PE
        case AFF_BINTYPE_DYN:
            if (s1->output_type == TCC_OUTPUT_MEMORY) {
                ret = 0;
#ifdef TCC_IS_NATIVE
                if (NULL == dlopen(filename, RTLD_GLOBAL | RTLD_LAZY))
                    ret = -1;
#endif
            } else {
                ret = tcc_load_dll(s1, fd, filename,
                                   (flags & AFF_REFERENCED_DLL) != 0);
            }
            break;
#endif
        case AFF_BINTYPE_AR:
            ret = tcc_load_archive(s1, fd);
            break;
#ifdef TCC_TARGET_COFF
        case AFF_BINTYPE_C67:
            ret = tcc_load_coff(s1, fd);
            break;
#endif
        default:
#ifdef TCC_TARGET_PE
            ret = pe_load_file(s1, filename, fd);
#else
            /* as GNU ld, consider it is an ld script if not recognized */
            ret = tcc_load_ldscript(s1);
#endif
            if (ret < 0)
                tcc_error_noabort("unrecognized file type");
            break;
        }
    } else {
        ret = tcc_compile(s1);
    }
    tcc_close();
    return ret;
}

LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename)
{
    int filetype = s->filetype;
    int flags = AFF_PRINT_ERROR;
    if (filetype == 0) {
        /* use a file extension to detect a filetype */
        const char *ext = tcc_fileextension(filename);
        if (ext[0]) {
            ext++;
            if (!strcmp(ext, "S"))
                filetype = AFF_TYPE_ASMPP;
            else if (!strcmp(ext, "s"))
                filetype = AFF_TYPE_ASM;
            else if (!PATHCMP(ext, "c") || !PATHCMP(ext, "i"))
                filetype = AFF_TYPE_C;
            else
                flags |= AFF_TYPE_BIN;
        } else {
            filetype = AFF_TYPE_C;
        }
        s->filetype = filetype;
    }
    return tcc_add_file_internal(s, filename, flags);
}

LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname)
{
    tcc_split_path(s, &s->library_paths, &s->nb_library_paths, pathname);
    return 0;
}

static int tcc_add_library_internal(TCCState *s, const char *fmt,
    const char *filename, int flags, char **paths, int nb_paths)
{
    char buf[1024];
    int i;

    for(i = 0; i < nb_paths; i++) {
        snprintf(buf, sizeof(buf), fmt, paths[i], filename);
        if (tcc_add_file_internal(s, buf, flags | AFF_TYPE_BIN) == 0)
            return 0;
    }
    return -1;
}

/* find and load a dll. Return non zero if not found */
/* XXX: add '-rpath' option support ? */
ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags)
{
    return tcc_add_library_internal(s, "%s/%s", filename, flags,
        s->library_paths, s->nb_library_paths);
}

ST_FUNC int tcc_add_crt(TCCState *s, const char *filename)
{
    if (-1 == tcc_add_library_internal(s, "%s/%s",
        filename, 0, s->crt_paths, s->nb_crt_paths))
        tcc_error_noabort("file '%s' not found", filename);
    return 0;
}

/* the library name is the same as the argument of the '-l' option */
LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname)
{
#if defined TCC_TARGET_PE
    const char *libs[] = { "%s/%s.def", "%s/lib%s.def", "%s/%s.dll", "%s/lib%s.dll", "%s/lib%s.a", NULL };
    const char **pp = s->static_link ? libs + 4 : libs;
#elif defined TCC_TARGET_MACHO
    const char *libs[] = { "%s/lib%s.dylib", "%s/lib%s.a", NULL };
    const char **pp = s->static_link ? libs + 1 : libs;
#else
    const char *libs[] = { "%s/lib%s.so", "%s/lib%s.a", NULL };
    const char **pp = s->static_link ? libs + 1 : libs;
#endif
    while (*pp) {
        if (0 == tcc_add_library_internal(s, *pp,
            libraryname, 0, s->library_paths, s->nb_library_paths))
            return 0;
        ++pp;
    }
    return -1;
}

PUB_FUNC int tcc_add_library_err(TCCState *s, const char *libname)
{
    int ret = tcc_add_library(s, libname);
    if (ret < 0)
        tcc_error_noabort("library '%s' not found", libname);
    return ret;
}

/* handle #pragma comment(lib,) */
ST_FUNC void tcc_add_pragma_libs(TCCState *s1)
{
    int i;
    for (i = 0; i < s1->nb_pragma_libs; i++)
        tcc_add_library_err(s1, s1->pragma_libs[i]);
}

LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val)
{
#ifdef TCC_TARGET_PE
    /* On x86_64 'val' might not be reachable with a 32bit offset.
       So it is handled here as if it were in a DLL. */
    pe_putimport(s, 0, name, (uintptr_t)val);
#else
    set_elf_sym(symtab_section, (uintptr_t)val, 0,
        ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
        SHN_ABS, name);
#endif
    return 0;
}

LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path)
{
    tcc_free(s->tcc_lib_path);
    s->tcc_lib_path = tcc_strdup(path);
}

#define WD_ALL    0x0001 /* warning is activated when using -Wall */
#define FD_INVERT 0x0002 /* invert value before storing */

typedef struct FlagDef {
    uint16_t offset;
    uint16_t flags;
    const char *name;
} FlagDef;

static int no_flag(const char **pp)
{
    const char *p = *pp;
    if (*p != 'n' || *++p != 'o' || *++p != '-')
        return 0;
    *pp = p + 1;
    return 1;
}

ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, const char *name)
{
    int value, ret;
    const FlagDef *p;
    const char *r;

    value = 1;
    r = name;
    if (no_flag(&r))
        value = 0;

    for (ret = -1, p = flags; p->name; ++p) {
        if (ret) {
            if (strcmp(r, p->name))
                continue;
        } else {
            if (0 == (p->flags & WD_ALL))
                continue;
        }
        if (p->offset) {
            *(int*)((char *)s + p->offset) =
                p->flags & FD_INVERT ? !value : value;
            if (ret)
                return 0;
        } else {
            ret = 0;
        }
    }
    return ret;
}

static int strstart(const char *val, const char **str)
{
    const char *p, *q;
    p = *str;
    q = val;
    while (*q) {
        if (*p != *q)
            return 0;
        p++;
        q++;
    }
    *str = p;
    return 1;
}

/* Like strstart, but automatically takes into account that ld options can
 *
 * - start with double or single dash (e.g. '--soname' or '-soname')
 * - arguments can be given as separate or after '=' (e.g. '-Wl,-soname,x.so'
 *   or '-Wl,-soname=x.so')
 *
 * you provide `val` always in 'option[=]' form (no leading -)
 */
static int link_option(const char *str, const char *val, const char **ptr)
{
    const char *p, *q;
    int ret;

    /* there should be 1 or 2 dashes */
    if (*str++ != '-')
        return 0;
    if (*str == '-')
        str++;

    /* then str & val should match (potentially up to '=') */
    p = str;
    q = val;

    ret = 1;
    if (q[0] == '?') {
        ++q;
        if (no_flag(&p))
            ret = -1;
    }

    while (*q != '\0' && *q != '=') {
        if (*p != *q)
            return 0;
        p++;
        q++;
    }

    /* '=' near eos means ',' or '=' is ok */
    if (*q == '=') {
        if (*p == 0)
            *ptr = p;
        if (*p != ',' && *p != '=')
            return 0;
        p++;
    } else if (*p) {
        return 0;
    }
    *ptr = p;
    return ret;
}

static const char *skip_linker_arg(const char **str)
{
    const char *s1 = *str;
    const char *s2 = strchr(s1, ',');
    *str = s2 ? s2++ : (s2 = s1 + strlen(s1));
    return s2;
}

static void copy_linker_arg(char **pp, const char *s, int sep)
{
    const char *q = s;
    char *p = *pp;
    int l = 0;
    if (p && sep)
        p[l = strlen(p)] = sep, ++l;
    skip_linker_arg(&q);
    pstrncpy(l + (*pp = tcc_realloc(p, q - s + l + 1)), s, q - s);
}

/* set linker options */
static int tcc_set_linker(TCCState *s, const char *option)
{
    while (*option) {

        const char *p = NULL;
        char *end = NULL;
        int ignoring = 0;
        int ret;

        if (link_option(option, "Bsymbolic", &p)) {
            s->symbolic = 1;
        } else if (link_option(option, "nostdlib", &p)) {
            s->nostdlib = 1;
        } else if (link_option(option, "fini=", &p)) {
            copy_linker_arg(&s->fini_symbol, p, 0);
            ignoring = 1;
        } else if (link_option(option, "image-base=", &p)
                || link_option(option, "Ttext=", &p)) {
            s->text_addr = strtoull(p, &end, 16);
            s->has_text_addr = 1;
        } else if (link_option(option, "init=", &p)) {
            copy_linker_arg(&s->init_symbol, p, 0);
            ignoring = 1;
        } else if (link_option(option, "oformat=", &p)) {
#if defined(TCC_TARGET_PE)
            if (strstart("pe-", &p)) {
#elif PTR_SIZE == 8
            if (strstart("elf64-", &p)) {
#else
            if (strstart("elf32-", &p)) {
#endif
                s->output_format = TCC_OUTPUT_FORMAT_ELF;
            } else if (!strcmp(p, "binary")) {
                s->output_format = TCC_OUTPUT_FORMAT_BINARY;
#ifdef TCC_TARGET_COFF
            } else if (!strcmp(p, "coff")) {
                s->output_format = TCC_OUTPUT_FORMAT_COFF;
#endif
            } else
                goto err;

        } else if (link_option(option, "as-needed", &p)) {
            ignoring = 1;
        } else if (link_option(option, "O", &p)) {
            ignoring = 1;
        } else if (link_option(option, "export-all-symbols", &p)) {
            s->rdynamic = 1;
        } else if (link_option(option, "rpath=", &p)) {
            copy_linker_arg(&s->rpath, p, ':');
        } else if (link_option(option, "enable-new-dtags", &p)) {
            s->enable_new_dtags = 1;
        } else if (link_option(option, "section-alignment=", &p)) {
            s->section_align = strtoul(p, &end, 16);
        } else if (link_option(option, "soname=", &p)) {
            copy_linker_arg(&s->soname, p, 0);
#ifdef TCC_TARGET_PE
        } else if (link_option(option, "large-address-aware", &p)) {
            s->pe_characteristics |= 0x20;
        } else if (link_option(option, "file-alignment=", &p)) {
            s->pe_file_align = strtoul(p, &end, 16);
        } else if (link_option(option, "stack=", &p)) {
            s->pe_stack_size = strtoul(p, &end, 10);
        } else if (link_option(option, "subsystem=", &p)) {
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
            if (!strcmp(p, "native")) {
                s->pe_subsystem = 1;
            } else if (!strcmp(p, "console")) {
                s->pe_subsystem = 3;
            } else if (!strcmp(p, "gui") || !strcmp(p, "windows")) {
                s->pe_subsystem = 2;
            } else if (!strcmp(p, "posix")) {
                s->pe_subsystem = 7;
            } else if (!strcmp(p, "efiapp")) {
                s->pe_subsystem = 10;
            } else if (!strcmp(p, "efiboot")) {
                s->pe_subsystem = 11;
            } else if (!strcmp(p, "efiruntime")) {
                s->pe_subsystem = 12;
            } else if (!strcmp(p, "efirom")) {
                s->pe_subsystem = 13;
#elif defined(TCC_TARGET_ARM)
            if (!strcmp(p, "wince")) {
                s->pe_subsystem = 9;
#endif
            } else
                goto err;
#endif
        } else if (ret = link_option(option, "?whole-archive", &p), ret) {
            s->alacarte_link = ret < 0;
        } else if (p) {
            return 0;
        } else {
    err:
            tcc_error("unsupported linker option '%s'", option);
        }

        if (ignoring && s->warn_unsupported)
            tcc_warning("unsupported linker option '%s'", option);

        option = skip_linker_arg(&p);
    }
    return 1;
}

typedef struct TCCOption {
    const char *name;
    uint16_t index;
    uint16_t flags;
} TCCOption;

enum {
    TCC_OPTION_HELP,
    TCC_OPTION_HELP2,
    TCC_OPTION_v,
    TCC_OPTION_I,
    TCC_OPTION_D,
    TCC_OPTION_U,
    TCC_OPTION_P,
    TCC_OPTION_L,
    TCC_OPTION_B,
    TCC_OPTION_l,
    TCC_OPTION_bench,
    TCC_OPTION_bt,
    TCC_OPTION_b,
    TCC_OPTION_g,
    TCC_OPTION_c,
    TCC_OPTION_dumpversion,
    TCC_OPTION_d,
    TCC_OPTION_static,
    TCC_OPTION_std,
    TCC_OPTION_shared,
    TCC_OPTION_soname,
    TCC_OPTION_o,
    TCC_OPTION_r,
    TCC_OPTION_s,
    TCC_OPTION_traditional,
    TCC_OPTION_Wl,
    TCC_OPTION_Wp,
    TCC_OPTION_W,
    TCC_OPTION_O,
    TCC_OPTION_mfloat_abi,
    TCC_OPTION_m,
    TCC_OPTION_f,
    TCC_OPTION_isystem,
    TCC_OPTION_iwithprefix,
    TCC_OPTION_include,
    TCC_OPTION_nostdinc,
    TCC_OPTION_nostdlib,
    TCC_OPTION_print_search_dirs,
    TCC_OPTION_rdynamic,
    TCC_OPTION_param,
    TCC_OPTION_pedantic,
    TCC_OPTION_pthread,
    TCC_OPTION_run,
    TCC_OPTION_w,
    TCC_OPTION_pipe,
    TCC_OPTION_E,
    TCC_OPTION_MD,
    TCC_OPTION_MF,
    TCC_OPTION_x,
    TCC_OPTION_ar,
    TCC_OPTION_impdef
};

#define TCC_OPTION_HAS_ARG 0x0001
#define TCC_OPTION_NOSEP   0x0002 /* cannot have space before option and arg */

static const TCCOption tcc_options[] = {
    { "h", TCC_OPTION_HELP, 0 },
    { "-help", TCC_OPTION_HELP, 0 },
    { "?", TCC_OPTION_HELP, 0 },
    { "hh", TCC_OPTION_HELP2, 0 },
    { "v", TCC_OPTION_v, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "I", TCC_OPTION_I, TCC_OPTION_HAS_ARG },
    { "D", TCC_OPTION_D, TCC_OPTION_HAS_ARG },
    { "U", TCC_OPTION_U, TCC_OPTION_HAS_ARG },
    { "P", TCC_OPTION_P, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "L", TCC_OPTION_L, TCC_OPTION_HAS_ARG },
    { "B", TCC_OPTION_B, TCC_OPTION_HAS_ARG },
    { "l", TCC_OPTION_l, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "bench", TCC_OPTION_bench, 0 },
#ifdef CONFIG_TCC_BACKTRACE
    { "bt", TCC_OPTION_bt, TCC_OPTION_HAS_ARG },
#endif
#ifdef CONFIG_TCC_BCHECK
    { "b", TCC_OPTION_b, 0 },
#endif
    { "g", TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "c", TCC_OPTION_c, 0 },
    { "dumpversion", TCC_OPTION_dumpversion, 0},
    { "d", TCC_OPTION_d, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "static", TCC_OPTION_static, 0 },
    { "std", TCC_OPTION_std, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "shared", TCC_OPTION_shared, 0 },
    { "soname", TCC_OPTION_soname, TCC_OPTION_HAS_ARG },
    { "o", TCC_OPTION_o, TCC_OPTION_HAS_ARG },
    { "-param", TCC_OPTION_param, TCC_OPTION_HAS_ARG },
    { "pedantic", TCC_OPTION_pedantic, 0},
    { "pthread", TCC_OPTION_pthread, 0},
    { "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "rdynamic", TCC_OPTION_rdynamic, 0 },
    { "r", TCC_OPTION_r, 0 },
    { "s", TCC_OPTION_s, 0 },
    { "traditional", TCC_OPTION_traditional, 0 },
    { "Wl,", TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "Wp,", TCC_OPTION_Wp, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "W", TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "O", TCC_OPTION_O, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
#ifdef TCC_TARGET_ARM
    { "mfloat-abi", TCC_OPTION_mfloat_abi, TCC_OPTION_HAS_ARG },
#endif
    { "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
    { "isystem", TCC_OPTION_isystem, TCC_OPTION_HAS_ARG },
    { "include", TCC_OPTION_include, TCC_OPTION_HAS_ARG },
    { "nostdinc", TCC_OPTION_nostdinc, 0 },
    { "nostdlib", TCC_OPTION_nostdlib, 0 },
    { "print-search-dirs", TCC_OPTION_print_search_dirs, 0 },
    { "w", TCC_OPTION_w, 0 },
    { "pipe", TCC_OPTION_pipe, 0},
    { "E", TCC_OPTION_E, 0},
    { "MD", TCC_OPTION_MD, 0},
    { "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG },
    { "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG },
    { "ar", TCC_OPTION_ar, 0},
#ifdef TCC_TARGET_PE
    { "impdef", TCC_OPTION_impdef, 0},
#endif
    { NULL, 0, 0 },
};

static const FlagDef options_W[] = {
    { 0, 0, "all" },
    { offsetof(TCCState, warn_unsupported), 0, "unsupported" },
    { offsetof(TCCState, warn_write_strings), 0, "write-strings" },
    { offsetof(TCCState, warn_error), 0, "error" },
    { offsetof(TCCState, warn_gcc_compat), 0, "gcc-compat" },
    { offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
      "implicit-function-declaration" },
    { 0, 0, NULL }
};

static const FlagDef options_f[] = {
    { offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" },
    { offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" },
    { offsetof(TCCState, nocommon), FD_INVERT, "common" },
    { offsetof(TCCState, leading_underscore), 0, "leading-underscore" },
    { offsetof(TCCState, ms_extensions), 0, "ms-extensions" },
    { offsetof(TCCState, dollars_in_identifiers), 0, "dollars-in-identifiers" },
    { 0, 0, NULL }
};

static const FlagDef options_m[] = {
    { offsetof(TCCState, ms_bitfields), 0, "ms-bitfields" },
#ifdef TCC_TARGET_X86_64
    { offsetof(TCCState, nosse), FD_INVERT, "sse" },
#endif
    { 0, 0, NULL }
};

static void parse_option_D(TCCState *s1, const char *optarg)
{
    char *sym = tcc_strdup(optarg);
    char *value = strchr(sym, '=');
    if (value)
        *value++ = '\0';
    tcc_define_symbol(s1, sym, value);
    tcc_free(sym);
}

static void args_parser_add_file(TCCState *s, const char* filename, int filetype)
{
    struct filespec *f = tcc_malloc(sizeof *f + strlen(filename));
    f->type = filetype;
    f->alacarte = s->alacarte_link;
    strcpy(f->name, filename);
    dynarray_add(&s->files, &s->nb_files, f);
}

static int args_parser_make_argv(const char *r, int *argc, char ***argv)
{
    int ret = 0, q, c;
    CString str;
    for(;;) {
        while (c = (unsigned char)*r, c && c <= ' ')
        ++r;
        if (c == 0)
            break;
        q = 0;
        cstr_new(&str);
        while (c = (unsigned char)*r, c) {
            ++r;
            if (c == '\\' && (*r == '"' || *r == '\\')) {
                c = *r++;
            } else if (c == '"') {
                q = !q;
                continue;
            } else if (q == 0 && c <= ' ') {
                break;
            }
            cstr_ccat(&str, c);
        }
        cstr_ccat(&str, 0);
        //printf("<%s>\n", str.data), fflush(stdout);
        dynarray_add(argv, argc, tcc_strdup(str.data));
        cstr_free(&str);
        ++ret;
    }
    return ret;
}

/* read list file */
static void args_parser_listfile(TCCState *s,
    const char *filename, int optind, int *pargc, char ***pargv)
{
    int fd, i;
    size_t len;
    char *p;
    int argc = 0;
    char **argv = NULL;

    fd = open(filename, O_RDONLY | O_BINARY);
    if (fd < 0)
        tcc_error("listfile '%s' not found", filename);

    len = lseek(fd, 0, SEEK_END);
    p = tcc_malloc(len + 1), p[len] = 0;
    lseek(fd, 0, SEEK_SET), read(fd, p, len), close(fd);

    for (i = 0; i < *pargc; ++i)
        if (i == optind)
            args_parser_make_argv(p, &argc, &argv);
        else
            dynarray_add(&argv, &argc, tcc_strdup((*pargv)[i]));

    tcc_free(p);
    dynarray_reset(&s->argv, &s->argc);
    *pargc = s->argc = argc, *pargv = s->argv = argv;
}

PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv, int optind)
{
    const TCCOption *popt;
    const char *optarg, *r;
    const char *run = NULL;
    int last_o = -1;
    int x;
    CString linker_arg; /* collect -Wl options */
    int tool = 0, arg_start = 0, noaction = optind;
    char **argv = *pargv;
    int argc = *pargc;

    cstr_new(&linker_arg);

    while (optind < argc) {
        r = argv[optind];
        if (r[0] == '@' && r[1] != '\0') {
            args_parser_listfile(s, r + 1, optind, &argc, &argv);
        continue;
        }
        optind++;
        if (tool) {
            if (r[0] == '-' && r[1] == 'v' && r[2] == 0)
                ++s->verbose;
            continue;
        }
reparse:
        if (r[0] != '-' || r[1] == '\0') {
            if (r[0] != '@') /* allow "tcc file(s) -run @ args ..." */
                args_parser_add_file(s, r, s->filetype);
            if (run) {
                tcc_set_options(s, run);
                arg_start = optind - 1;
                break;
            }
            continue;
        }

        /* find option in table */
        for(popt = tcc_options; ; ++popt) {
            const char *p1 = popt->name;
            const char *r1 = r + 1;
            if (p1 == NULL)
                tcc_error("invalid option -- '%s'", r);
            if (!strstart(p1, &r1))
                continue;
            optarg = r1;
            if (popt->flags & TCC_OPTION_HAS_ARG) {
                if (*r1 == '\0' && !(popt->flags & TCC_OPTION_NOSEP)) {
                    if (optind >= argc)
                arg_err:
                        tcc_error("argument to '%s' is missing", r);
                    optarg = argv[optind++];
                }
            } else if (*r1 != '\0')
                continue;
            break;
        }

        switch(popt->index) {
        case TCC_OPTION_HELP:
            return OPT_HELP;
        case TCC_OPTION_HELP2:
            return OPT_HELP2;
        case TCC_OPTION_I:
            tcc_add_include_path(s, optarg);
            break;
        case TCC_OPTION_D:
            parse_option_D(s, optarg);
            break;
        case TCC_OPTION_U:
            tcc_undefine_symbol(s, optarg);
            break;
        case TCC_OPTION_L:
            tcc_add_library_path(s, optarg);
            break;
        case TCC_OPTION_B:
            /* set tcc utilities path (mainly for tcc development) */
            tcc_set_lib_path(s, optarg);
            break;
        case TCC_OPTION_l:
            args_parser_add_file(s, optarg, AFF_TYPE_LIB);
            s->nb_libraries++;
            break;
        case TCC_OPTION_pthread:
            parse_option_D(s, "_REENTRANT");
            s->option_pthread = 1;
            break;
        case TCC_OPTION_bench:
            s->do_bench = 1;
            break;
#ifdef CONFIG_TCC_BACKTRACE
        case TCC_OPTION_bt:
            tcc_set_num_callers(atoi(optarg));
            break;
#endif
#ifdef CONFIG_TCC_BCHECK
        case TCC_OPTION_b:
            s->do_bounds_check = 1;
            s->do_debug = 1;
            break;
#endif
        case TCC_OPTION_g:
            s->do_debug = 1;
            break;
        case TCC_OPTION_c:
            x = TCC_OUTPUT_OBJ;
        set_output_type:
            if (s->output_type)
                tcc_warning("-%s: overriding compiler action already specified", popt->name);
            s->output_type = x;
            break;
        case TCC_OPTION_d:
            if (*optarg == 'D')
                s->dflag = 3;
            else if (*optarg == 'M')
                s->dflag = 7;
            else if (*optarg == 't')
                s->dflag = 16;
            else if (isnum(*optarg))
                g_debug = atoi(optarg);
            else
                goto unsupported_option;
            break;
        case TCC_OPTION_static:
            s->static_link = 1;
            break;
        case TCC_OPTION_std:
            /* silently ignore, a current purpose:
               allow to use a tcc as a reference compiler for "make test" */
            break;
        case TCC_OPTION_shared:
            x = TCC_OUTPUT_DLL;
            goto set_output_type;
        case TCC_OPTION_soname:
            s->soname = tcc_strdup(optarg);
            break;
        case TCC_OPTION_o:
            if (s->outfile) {
                tcc_warning("multiple -o option");
                tcc_free(s->outfile);
            }
            s->outfile = tcc_strdup(optarg);
            break;
        case TCC_OPTION_r:
            /* generate a .o merging several output files */
            s->option_r = 1;
            x = TCC_OUTPUT_OBJ;
            goto set_output_type;
        case TCC_OPTION_isystem:
            tcc_add_sysinclude_path(s, optarg);
            break;
    case TCC_OPTION_include:
        dynarray_add(&s->cmd_include_files,
             &s->nb_cmd_include_files, tcc_strdup(optarg));
        break;
        case TCC_OPTION_nostdinc:
            s->nostdinc = 1;
            break;
        case TCC_OPTION_nostdlib:
            s->nostdlib = 1;
            break;
        case TCC_OPTION_run:
#ifndef TCC_IS_NATIVE
            tcc_error("-run is not available in a cross compiler");
#endif
            run = optarg;
            x = TCC_OUTPUT_MEMORY;
            goto set_output_type;
        case TCC_OPTION_v:
            do ++s->verbose; while (*optarg++ == 'v');
            ++noaction;
            break;
        case TCC_OPTION_f:
            if (set_flag(s, options_f, optarg) < 0)
                goto unsupported_option;
            break;
#ifdef TCC_TARGET_ARM
        case TCC_OPTION_mfloat_abi:
            /* tcc doesn't support soft float yet */
            if (!strcmp(optarg, "softfp")) {
                s->float_abi = ARM_SOFTFP_FLOAT;
                tcc_undefine_symbol(s, "__ARM_PCS_VFP");
            } else if (!strcmp(optarg, "hard"))
                s->float_abi = ARM_HARD_FLOAT;
            else
                tcc_error("unsupported float abi '%s'", optarg);
            break;
#endif
        case TCC_OPTION_m:
            if (set_flag(s, options_m, optarg) < 0) {
                if (x = atoi(optarg), x != 32 && x != 64)
                    goto unsupported_option;
                if (PTR_SIZE != x/8)
                    return x;
                ++noaction;
            }
            break;
        case TCC_OPTION_W:
            if (set_flag(s, options_W, optarg) < 0)
                goto unsupported_option;
            break;
        case TCC_OPTION_w:
            s->warn_none = 1;
            break;
        case TCC_OPTION_rdynamic:
            s->rdynamic = 1;
            break;
        case TCC_OPTION_Wl:
            if (linker_arg.size)
                --linker_arg.size, cstr_ccat(&linker_arg, ',');
            cstr_cat(&linker_arg, optarg, 0);
            if (tcc_set_linker(s, linker_arg.data))
                cstr_free(&linker_arg);
            break;
    case TCC_OPTION_Wp:
        r = optarg;
        goto reparse;
        case TCC_OPTION_E:
            x = TCC_OUTPUT_PREPROCESS;
            goto set_output_type;
        case TCC_OPTION_P:
            s->Pflag = atoi(optarg) + 1;
            break;
        case TCC_OPTION_MD:
            s->gen_deps = 1;
            break;
        case TCC_OPTION_MF:
            s->deps_outfile = tcc_strdup(optarg);
            break;
        case TCC_OPTION_dumpversion:
            printf ("%s\n", TCC_VERSION);
            exit(0);
            break;
        case TCC_OPTION_x:
            if (*optarg == 'c')
                s->filetype = AFF_TYPE_C;
            else if (*optarg == 'a')
                s->filetype = AFF_TYPE_ASMPP;
            else if (*optarg == 'n')
                s->filetype = AFF_TYPE_NONE;
            else
                tcc_warning("unsupported language '%s'", optarg);
            break;
        case TCC_OPTION_O:
            last_o = atoi(optarg);
            break;
        case TCC_OPTION_print_search_dirs:
            x = OPT_PRINT_DIRS;
            goto extra_action;
        case TCC_OPTION_impdef:
            x = OPT_IMPDEF;
            goto extra_action;
        case TCC_OPTION_ar:
            x = OPT_AR;
        extra_action:
            arg_start = optind - 1;
            if (arg_start != noaction)
                tcc_error("cannot parse %s here", r);
            tool = x;
            break;
        case TCC_OPTION_traditional:
        case TCC_OPTION_pedantic:
        case TCC_OPTION_pipe:
        case TCC_OPTION_s:
            /* ignored */
            break;
        default:
unsupported_option:
            if (s->warn_unsupported)
                tcc_warning("unsupported option '%s'", r);
            break;
        }
    }
    if (last_o > 0)
        tcc_define_symbol(s, "__OPTIMIZE__", NULL);
    if (linker_arg.size) {
        r = linker_arg.data;
        goto arg_err;
    }
    *pargc = argc - arg_start;
    *pargv = argv + arg_start;
    if (tool)
        return tool;
    if (optind != noaction)
        return 0;
    if (s->verbose == 2)
        return OPT_PRINT_DIRS;
    if (s->verbose)
        return OPT_V;
    return OPT_HELP;
}

LIBTCCAPI void tcc_set_options(TCCState *s, const char *r)
{
    char **argv = NULL;
    int argc = 0;
    args_parser_make_argv(r, &argc, &argv);
    tcc_parse_args(s, &argc, &argv, 0);
    dynarray_reset(&argv, &argc);
}

PUB_FUNC void tcc_print_stats(TCCState *s, unsigned total_time)
{
    if (total_time < 1)
        total_time = 1;
    if (total_bytes < 1)
        total_bytes = 1;
    fprintf(stderr, "* %d idents, %d lines, %d bytes\n"
                    "* %0.3f s, %u lines/s, %0.1f MB/s\n",
           tok_ident - TOK_IDENT, total_lines, total_bytes,
           (double)total_time/1000,
           (unsigned)total_lines*1000/total_time,
           (double)total_bytes/1000/total_time);
#ifdef MEM_DEBUG
    fprintf(stderr, "* %d bytes memory used\n", mem_max_size);
#endif
}

https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tccpp.c

File: ../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tccpp.c used in: Step 488
/*
 *  TCC - Tiny C Compiler
 * 
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "tcc.h"

/********************************************************/
/* global variables */

ST_DATA int tok_flags;
ST_DATA int parse_flags;

ST_DATA struct BufferedFile *file;
ST_DATA int ch, tok;
ST_DATA CValue tokc;
ST_DATA const int *macro_ptr;
ST_DATA CString tokcstr; /* current parsed string, if any */

/* display benchmark infos */
ST_DATA int total_lines;
ST_DATA int total_bytes;
ST_DATA int tok_ident;
ST_DATA TokenSym **table_ident;

/* ------------------------------------------------------------------------- */

static TokenSym *hash_ident[TOK_HASH_SIZE];
static char token_buf[STRING_MAX_SIZE + 1];
static CString cstr_buf;
static CString macro_equal_buf;
static TokenString tokstr_buf;
static unsigned char isidnum_table[256 - CH_EOF];
static int pp_debug_tok, pp_debug_symv;
static int pp_once;
static int pp_expr;
static int pp_counter;
static void tok_print(const char *msg, const int *str);

static struct TinyAlloc *toksym_alloc;
static struct TinyAlloc *tokstr_alloc;
static struct TinyAlloc *cstr_alloc;

static TokenString *macro_stack;

static const char tcc_keywords[] = 
#define DEF(id, str) str "\0"
#include "tcctok.h"
#undef DEF
;

/* WARNING: the content of this string encodes token numbers */
static const unsigned char tok_two_chars[] =
/* outdated -- gr
    "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253"
    "-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266";
*/{
    '<','=', TOK_LE,
    '>','=', TOK_GE,
    '!','=', TOK_NE,
    '&','&', TOK_LAND,
    '|','|', TOK_LOR,
    '+','+', TOK_INC,
    '-','-', TOK_DEC,
    '=','=', TOK_EQ,
    '<','<', TOK_SHL,
    '>','>', TOK_SAR,
    '+','=', TOK_A_ADD,
    '-','=', TOK_A_SUB,
    '*','=', TOK_A_MUL,
    '/','=', TOK_A_DIV,
    '%','=', TOK_A_MOD,
    '&','=', TOK_A_AND,
    '^','=', TOK_A_XOR,
    '|','=', TOK_A_OR,
    '-','>', TOK_ARROW,
    '.','.', TOK_TWODOTS,
    '#','#', TOK_TWOSHARPS,
    0
};

static void next_nomacro_spc(void);

ST_FUNC void skip(int c)
{
    if (tok != c)
        tcc_error("'%c' expected (got \"%s\")", c, get_tok_str(tok, &tokc));
    next();
}

ST_FUNC void expect(const char *msg)
{
    tcc_error("%s expected", msg);
}

/* ------------------------------------------------------------------------- */
/* Custom allocator for tiny objects */

#define USE_TAL

#ifndef USE_TAL
#define tal_free(al, p) tcc_free(p)
#define tal_realloc(al, p, size) tcc_realloc(p, size)
#define tal_new(a,b,c)
#define tal_delete(a)
#else
#if !defined(MEM_DEBUG)
#define tal_free(al, p) tal_free_impl(al, p)
#define tal_realloc(al, p, size) tal_realloc_impl(&al, p, size)
#define TAL_DEBUG_PARAMS
#else
#define TAL_DEBUG 1
//#define TAL_INFO 1 /* collect and dump allocators stats */
#define tal_free(al, p) tal_free_impl(al, p, __FILE__, __LINE__)
#define tal_realloc(al, p, size) tal_realloc_impl(&al, p, size, __FILE__, __LINE__)
#define TAL_DEBUG_PARAMS , const char *file, int line
#define TAL_DEBUG_FILE_LEN 40
#endif

#define TOKSYM_TAL_SIZE     (768 * 1024) /* allocator for tiny TokenSym in table_ident */
#define TOKSTR_TAL_SIZE     (768 * 1024) /* allocator for tiny TokenString instances */
#define CSTR_TAL_SIZE       (256 * 1024) /* allocator for tiny CString instances */
#define TOKSYM_TAL_LIMIT    256 /* prefer unique limits to distinguish allocators debug msgs */
#define TOKSTR_TAL_LIMIT    128 /* 32 * sizeof(int) */
#define CSTR_TAL_LIMIT      1024

typedef struct TinyAlloc {
    unsigned  limit;
    unsigned  size;
    uint8_t *buffer;
    uint8_t *p;
    unsigned  nb_allocs;
    struct TinyAlloc *next, *top;
#ifdef TAL_INFO
    unsigned  nb_peak;
    unsigned  nb_total;
    unsigned  nb_missed;
    uint8_t *peak_p;
#endif
} TinyAlloc;

typedef struct tal_header_t {
    unsigned  size;
#ifdef TAL_DEBUG
    int     line_num; /* negative line_num used for double free check */
    char    file_name[TAL_DEBUG_FILE_LEN + 1];
#endif
} tal_header_t;

/* ------------------------------------------------------------------------- */

static TinyAlloc *tal_new(TinyAlloc **pal, unsigned limit, unsigned size)
{
    TinyAlloc *al = tcc_mallocz(sizeof(TinyAlloc));
    al->p = al->buffer = tcc_malloc(size);
    al->limit = limit;
    al->size = size;
    if (pal) *pal = al;
    return al;
}

static void tal_delete(TinyAlloc *al)
{
    TinyAlloc *next;

tail_call:
    if (!al)
        return;
#ifdef TAL_INFO
    fprintf(stderr, "limit=%5d, size=%5g MB, nb_peak=%6d, nb_total=%8d, nb_missed=%6d, usage=%5.1f%%\n",
            al->limit, al->size / 1024.0 / 1024.0, al->nb_peak, al->nb_total, al->nb_missed,
            (al->peak_p - al->buffer) * 100.0 / al->size);
#endif
#ifdef TAL_DEBUG
    if (al->nb_allocs > 0) {
        uint8_t *p;
        fprintf(stderr, "TAL_DEBUG: memory leak %d chunk(s) (limit= %d)\n",
                al->nb_allocs, al->limit);
        p = al->buffer;
        while (p < al->p) {
            tal_header_t *header = (tal_header_t *)p;
            if (header->line_num > 0) {
                fprintf(stderr, "%s:%d: chunk of %d bytes leaked\n",
                        header->file_name, header->line_num, header->size);
            }
            p += header->size + sizeof(tal_header_t);
        }
#if MEM_DEBUG-0 == 2
        exit(2);
#endif
    }
#endif
    next = al->next;
    tcc_free(al->buffer);
    tcc_free(al);
    al = next;
    goto tail_call;
}

static void tal_free_impl(TinyAlloc *al, void *p TAL_DEBUG_PARAMS)
{
    if (!p)
        return;
tail_call:
    if (al->buffer <= (uint8_t *)p && (uint8_t *)p < al->buffer + al->size) {
#ifdef TAL_DEBUG
        tal_header_t *header = (((tal_header_t *)p) - 1);
        if (header->line_num < 0) {
            fprintf(stderr, "%s:%d: TAL_DEBUG: double frees chunk from\n",
                    file, line);
            fprintf(stderr, "%s:%d: %d bytes\n",
                    header->file_name, (int)-header->line_num, (int)header->size);
        } else
            header->line_num = -header->line_num;
#endif
        al->nb_allocs--;
        if (!al->nb_allocs)
            al->p = al->buffer;
    } else if (al->next) {
        al = al->next;
        goto tail_call;
    }
    else
        tcc_free(p);
}

static void *tal_realloc_impl(TinyAlloc **pal, void *p, unsigned size TAL_DEBUG_PARAMS)
{
    tal_header_t *header;
    void *ret;
    int is_own;
    unsigned adj_size = (size + 3) & -4;
    TinyAlloc *al = *pal;

tail_call:
    is_own = (al->buffer <= (uint8_t *)p && (uint8_t *)p < al->buffer + al->size);
    if ((!p || is_own) && size <= al->limit) {
        if (al->p + adj_size + sizeof(tal_header_t) < al->buffer + al->size) {
            header = (tal_header_t *)al->p;
            header->size = adj_size;
#ifdef TAL_DEBUG
            { int ofs = strlen(file) - TAL_DEBUG_FILE_LEN;
            strncpy(header->file_name, file + (ofs > 0 ? ofs : 0), TAL_DEBUG_FILE_LEN);
            header->file_name[TAL_DEBUG_FILE_LEN] = 0;
            header->line_num = line; }
#endif
            ret = al->p + sizeof(tal_header_t);
            al->p += adj_size + sizeof(tal_header_t);
            if (is_own) {
                header = (((tal_header_t *)p) - 1);
                memcpy(ret, p, header->size);
#ifdef TAL_DEBUG
                header->line_num = -header->line_num;
#endif
            } else {
                al->nb_allocs++;
            }
#ifdef TAL_INFO
            if (al->nb_peak < al->nb_allocs)
                al->nb_peak = al->nb_allocs;
            if (al->peak_p < al->p)
                al->peak_p = al->p;
            al->nb_total++;
#endif
            return ret;
        } else if (is_own) {
            al->nb_allocs--;
            ret = tal_realloc(*pal, 0, size);
            header = (((tal_header_t *)p) - 1);
            memcpy(ret, p, header->size);
#ifdef TAL_DEBUG
            header->line_num = -header->line_num;
#endif
            return ret;
        }
        if (al->next) {
            al = al->next;
        } else {
            TinyAlloc *bottom = al, *next = al->top ? al->top : al;

            al = tal_new(pal, next->limit, next->size * 2);
            al->next = next;
            bottom->top = al;
        }
        goto tail_call;
    }
    if (is_own) {
        al->nb_allocs--;
        ret = tcc_malloc(size);
        header = (((tal_header_t *)p) - 1);
        memcpy(ret, p, header->size);
#ifdef TAL_DEBUG
        header->line_num = -header->line_num;
#endif
    } else if (al->next) {
        al = al->next;
        goto tail_call;
    } else
        ret = tcc_realloc(p, size);
#ifdef TAL_INFO
    al->nb_missed++;
#endif
    return ret;
}

#endif /* USE_TAL */

/* ------------------------------------------------------------------------- */
/* CString handling */
static void cstr_realloc(CString *cstr, int new_size)
{
    int size;

    size = cstr->size_allocated;
    if (size < 8)
        size = 8; /* no need to allocate a too small first string */
    while (size < new_size)
        size = size * 2;
    cstr->data = tal_realloc(cstr_alloc, cstr->data, size);
    cstr->size_allocated = size;
}

/* add a byte */
ST_INLN void cstr_ccat(CString *cstr, int ch)
{
    int size;
    size = cstr->size + 1;
    if (size > cstr->size_allocated)
        cstr_realloc(cstr, size);
    ((unsigned char *)cstr->data)[size - 1] = ch;
    cstr->size = size;
}

ST_FUNC void cstr_cat(CString *cstr, const char *str, int len)
{
    int size;
    if (len <= 0)
        len = strlen(str) + 1 + len;
    size = cstr->size + len;
    if (size > cstr->size_allocated)
        cstr_realloc(cstr, size);
    memmove(((unsigned char *)cstr->data) + cstr->size, str, len);
    cstr->size = size;
}

/* add a wide char */
ST_FUNC void cstr_wccat(CString *cstr, int ch)
{
    int size;
    size = cstr->size + sizeof(nwchar_t);
    if (size > cstr->size_allocated)
        cstr_realloc(cstr, size);
    *(nwchar_t *)(((unsigned char *)cstr->data) + size - sizeof(nwchar_t)) = ch;
    cstr->size = size;
}

ST_FUNC void cstr_new(CString *cstr)
{
    memset(cstr, 0, sizeof(CString));
}

/* free string and reset it to NULL */
ST_FUNC void cstr_free(CString *cstr)
{
    tal_free(cstr_alloc, cstr->data);
    cstr_new(cstr);
}

/* reset string to empty */
ST_FUNC void cstr_reset(CString *cstr)
{
    cstr->size = 0;
}

/* XXX: unicode ? */
static void add_char(CString *cstr, int c)
{
    if (c == '\'' || c == '\"' || c == '\\') {
        /* XXX: could be more precise if char or string */
        cstr_ccat(cstr, '\\');
    }
    if (c >= 32 && c <= 126) {
        cstr_ccat(cstr, c);
    } else {
        cstr_ccat(cstr, '\\');
        if (c == '\n') {
            cstr_ccat(cstr, 'n');
        } else {
            cstr_ccat(cstr, '0' + ((c >> 6) & 7));
            cstr_ccat(cstr, '0' + ((c >> 3) & 7));
            cstr_ccat(cstr, '0' + (c & 7));
        }
    }
}

/* ------------------------------------------------------------------------- */
/* allocate a new token */
static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len)
{
    TokenSym *ts, **ptable;
    int i;

    if (tok_ident >= SYM_FIRST_ANOM) 
        tcc_error("memory full (symbols)");

    /* expand token table if needed */
    i = tok_ident - TOK_IDENT;
    if ((i % TOK_ALLOC_INCR) == 0) {
        ptable = tcc_realloc(table_ident, (i + TOK_ALLOC_INCR) * sizeof(TokenSym *));
        table_ident = ptable;
    }

    ts = tal_realloc(toksym_alloc, 0, sizeof(TokenSym) + len);
    table_ident[i] = ts;
    ts->tok = tok_ident++;
    ts->sym_define = NULL;
    ts->sym_label = NULL;
    ts->sym_struct = NULL;
    ts->sym_identifier = NULL;
    ts->len = len;
    ts->hash_next = NULL;
    memcpy(ts->str, str, len);
    ts->str[len] = '\0';
    *pts = ts;
    return ts;
}

#define TOK_HASH_INIT 1
#define TOK_HASH_FUNC(h, c) ((h) + ((h) << 5) + ((h) >> 27) + (c))


/* find a token and add it if not found */
ST_FUNC TokenSym *tok_alloc(const char *str, int len)
{
    TokenSym *ts, **pts;
    int i;
    unsigned int h;
    
    h = TOK_HASH_INIT;
    for(i=0;i<len;i++)
        h = TOK_HASH_FUNC(h, ((unsigned char *)str)[i]);
    h &= (TOK_HASH_SIZE - 1);

    pts = &hash_ident[h];
    for(;;) {
        ts = *pts;
        if (!ts)
            break;
        if (ts->len == len && !memcmp(ts->str, str, len))
            return ts;
        pts = &(ts->hash_next);
    }
    return tok_alloc_new(pts, str, len);
}

/* XXX: buffer overflow */
/* XXX: float tokens */
ST_FUNC const char *get_tok_str(int v, CValue *cv)
{
    char *p;
    int i, len;

    cstr_reset(&cstr_buf);
    p = cstr_buf.data;

    switch(v) {
    case TOK_CINT:
    case TOK_CUINT:
    case TOK_CLONG:
    case TOK_CULONG:
    case TOK_CLLONG:
    case TOK_CULLONG:
        /* XXX: not quite exact, but only useful for testing  */
#ifdef _WIN32
        sprintf(p, "%u", (unsigned)cv->i);
#else
        sprintf(p, "%llu", (unsigned long long)cv->i);
#endif
        break;
    case TOK_LCHAR:
        cstr_ccat(&cstr_buf, 'L');
    case TOK_CCHAR:
        cstr_ccat(&cstr_buf, '\'');
        add_char(&cstr_buf, cv->i);
        cstr_ccat(&cstr_buf, '\'');
        cstr_ccat(&cstr_buf, '\0');
        break;
    case TOK_PPNUM:
    case TOK_PPSTR:
        return (char*)cv->str.data;
    case TOK_LSTR:
        cstr_ccat(&cstr_buf, 'L');
    case TOK_STR:
        cstr_ccat(&cstr_buf, '\"');
        if (v == TOK_STR) {
            len = cv->str.size - 1;
            for(i=0;i<len;i++)
                add_char(&cstr_buf, ((unsigned char *)cv->str.data)[i]);
        } else {
            len = (cv->str.size / sizeof(nwchar_t)) - 1;
            for(i=0;i<len;i++)
                add_char(&cstr_buf, ((nwchar_t *)cv->str.data)[i]);
        }
        cstr_ccat(&cstr_buf, '\"');
        cstr_ccat(&cstr_buf, '\0');
        break;

    case TOK_CFLOAT:
        cstr_cat(&cstr_buf, "<float>", 0);
        break;
    case TOK_CDOUBLE:
    cstr_cat(&cstr_buf, "<double>", 0);
    break;
    case TOK_CLDOUBLE:
    cstr_cat(&cstr_buf, "<long double>", 0);
    break;
    case TOK_LINENUM:
    cstr_cat(&cstr_buf, "<linenumber>", 0);
    break;

    /* above tokens have value, the ones below don't */
    case TOK_LT:
        v = '<';
        goto addv;
    case TOK_GT:
        v = '>';
        goto addv;
    case TOK_DOTS:
        return strcpy(p, "...");
    case TOK_A_SHL:
        return strcpy(p, "<<=");
    case TOK_A_SAR:
        return strcpy(p, ">>=");
    case TOK_EOF:
        return strcpy(p, "<eof>");
    default:
        if (v < TOK_IDENT) {
            /* search in two bytes table */
            const unsigned char *q = tok_two_chars;
            while (*q) {
                if (q[2] == v) {
                    *p++ = q[0];
                    *p++ = q[1];
                    *p = '\0';
                    return cstr_buf.data;
                }
                q += 3;
            }
        if (v >= 127) {
            sprintf(cstr_buf.data, "<%02x>", v);
            return cstr_buf.data;
        }
        addv:
            *p++ = v;
            *p = '\0';
        } else if (v < tok_ident) {
            return table_ident[v - TOK_IDENT]->str;
        } else if (v >= SYM_FIRST_ANOM) {
            /* special name for anonymous symbol */
            sprintf(p, "L.%u", v - SYM_FIRST_ANOM);
        } else {
            /* should never happen */
            return NULL;
        }
        break;
    }
    return cstr_buf.data;
}

/* return the current character, handling end of block if necessary
   (but not stray) */
ST_FUNC int handle_eob(void)
{
    BufferedFile *bf = file;
    int len;

    /* only tries to read if really end of buffer */
    if (bf->buf_ptr >= bf->buf_end) {
        if (bf->fd >= 0) {
#if defined(PARSE_DEBUG)
            len = 1;
#else
            len = IO_BUF_SIZE;
#endif
            len = read(bf->fd, bf->buffer, len);
            if (len < 0)
                len = 0;
        } else {
            len = 0;
        }
        total_bytes += len;
        bf->buf_ptr = bf->buffer;
        bf->buf_end = bf->buffer + len;
        *bf->buf_end = CH_EOB;
    }
    if (bf->buf_ptr < bf->buf_end) {
        return bf->buf_ptr[0];
    } else {
        bf->buf_ptr = bf->buf_end;
        return CH_EOF;
    }
}

/* read next char from current input file and handle end of input buffer */
ST_INLN void inp(void)
{
    ch = *(++(file->buf_ptr));
    /* end of buffer/file handling */
    if (ch == CH_EOB)
        ch = handle_eob();
}

/* handle '\[\r]\n' */
static int handle_stray_noerror(void)
{
    while (ch == '\\') {
        inp();
        if (ch == '\n') {
            file->line_num++;
            inp();
        } else if (ch == '\r') {
            inp();
            if (ch != '\n')
                goto fail;
            file->line_num++;
            inp();
        } else {
        fail:
            return 1;
        }
    }
    return 0;
}

static void handle_stray(void)
{
    if (handle_stray_noerror())
        tcc_error("stray '\\' in program");
}

/* skip the stray and handle the \\n case. Output an error if
   incorrect char after the stray */
static int handle_stray1(uint8_t *p)
{
    int c;

    file->buf_ptr = p;
    if (p >= file->buf_end) {
        c = handle_eob();
        if (c != '\\')
            return c;
        p = file->buf_ptr;
    }
    ch = *p;
    if (handle_stray_noerror()) {
        if (!(parse_flags & PARSE_FLAG_ACCEPT_STRAYS))
            tcc_error("stray '\\' in program");
        *--file->buf_ptr = '\\';
    }
    p = file->buf_ptr;
    c = *p;
    return c;
}

/* handle just the EOB case, but not stray */
#define PEEKC_EOB(c, p)\
{\
    p++;\
    c = *p;\
    if (c == '\\') {\
        file->buf_ptr = p;\
        c = handle_eob();\
        p = file->buf_ptr;\
    }\
}

/* handle the complicated stray case */
#define PEEKC(c, p)\
{\
    p++;\
    c = *p;\
    if (c == '\\') {\
        c = handle_stray1(p);\
        p = file->buf_ptr;\
    }\
}

/* input with '\[\r]\n' handling. Note that this function cannot
   handle other characters after '\', so you cannot call it inside
   strings or comments */
ST_FUNC void minp(void)
{
    inp();
    if (ch == '\\') 
        handle_stray();
}

/* single line C++ comments */
static uint8_t *parse_line_comment(uint8_t *p)
{
    int c;

    p++;
    for(;;) {
        c = *p;
    redo:
        if (c == '\n' || c == CH_EOF) {
            break;
        } else if (c == '\\') {
            file->buf_ptr = p;
            c = handle_eob();
            p = file->buf_ptr;
            if (c == '\\') {
                PEEKC_EOB(c, p);
                if (c == '\n') {
                    file->line_num++;
                    PEEKC_EOB(c, p);
                } else if (c == '\r') {
                    PEEKC_EOB(c, p);
                    if (c == '\n') {
                        file->line_num++;
                        PEEKC_EOB(c, p);
                    }
                }
            } else {
                goto redo;
            }
        } else {
            p++;
        }
    }
    return p;
}

/* C comments */
ST_FUNC uint8_t *parse_comment(uint8_t *p)
{
    int c;

    p++;
    for(;;) {
        /* fast skip loop */
        for(;;) {
            c = *p;
            if (c == '\n' || c == '*' || c == '\\')
                break;
            p++;
            c = *p;
            if (c == '\n' || c == '*' || c == '\\')
                break;
            p++;
        }
        /* now we can handle all the cases */
        if (c == '\n') {
            file->line_num++;
            p++;
        } else if (c == '*') {
            p++;
            for(;;) {
                c = *p;
                if (c == '*') {
                    p++;
                } else if (c == '/') {
                    goto end_of_comment;
                } else if (c == '\\') {
                    file->buf_ptr = p;
                    c = handle_eob();
                    p = file->buf_ptr;
                    if (c == CH_EOF)
                        tcc_error("unexpected end of file in comment");
                    if (c == '\\') {
                        /* skip '\[\r]\n', otherwise just skip the stray */
                        while (c == '\\') {
                            PEEKC_EOB(c, p);
                            if (c == '\n') {
                                file->line_num++;
                                PEEKC_EOB(c, p);
                            } else if (c == '\r') {
                                PEEKC_EOB(c, p);
                                if (c == '\n') {
                                    file->line_num++;
                                    PEEKC_EOB(c, p);
                                }
                            } else {
                                goto after_star;
                            }
                        }
                    }
                } else {
                    break;
                }
            }
        after_star: ;
        } else {
            /* stray, eob or eof */
            file->buf_ptr = p;
            c = handle_eob();
            p = file->buf_ptr;
            if (c == CH_EOF) {
                tcc_error("unexpected end of file in comment");
            } else if (c == '\\') {
                p++;
            }
        }
    }
 end_of_comment:
    p++;
    return p;
}

ST_FUNC int set_idnum(int c, int val)
{
    int prev = isidnum_table[c - CH_EOF];
    isidnum_table[c - CH_EOF] = val;
    return prev;
}

#define cinp minp

static inline void skip_spaces(void)
{
    while (isidnum_table[ch - CH_EOF] & IS_SPC)
        cinp();
}

static inline int check_space(int t, int *spc) 
{
    if (t < 256 && (isidnum_table[t - CH_EOF] & IS_SPC)) {
        if (*spc) 
            return 1;
        *spc = 1;
    } else 
        *spc = 0;
    return 0;
}

/* parse a string without interpreting escapes */
static uint8_t *parse_pp_string(uint8_t *p,
                                int sep, CString *str)
{
    int c;
    p++;
    for(;;) {
        c = *p;
        if (c == sep) {
            break;
        } else if (c == '\\') {
            file->buf_ptr = p;
            c = handle_eob();
            p = file->buf_ptr;
            if (c == CH_EOF) {
            unterminated_string:
                /* XXX: indicate line number of start of string */
                tcc_error("missing terminating %c character", sep);
            } else if (c == '\\') {
                /* escape : just skip \[\r]\n */
                PEEKC_EOB(c, p);
                if (c == '\n') {
                    file->line_num++;
                    p++;
                } else if (c == '\r') {
                    PEEKC_EOB(c, p);
                    if (c != '\n')
                        expect("'\n' after '\r'");
                    file->line_num++;
                    p++;
                } else if (c == CH_EOF) {
                    goto unterminated_string;
                } else {
                    if (str) {
                        cstr_ccat(str, '\\');
                        cstr_ccat(str, c);
                    }
                    p++;
                }
            }
        } else if (c == '\n') {
            file->line_num++;
            goto add_char;
        } else if (c == '\r') {
            PEEKC_EOB(c, p);
            if (c != '\n') {
                if (str)
                    cstr_ccat(str, '\r');
            } else {
                file->line_num++;
                goto add_char;
            }
        } else {
        add_char:
            if (str)
                cstr_ccat(str, c);
            p++;
        }
    }
    p++;
    return p;
}

/* skip block of text until #else, #elif or #endif. skip also pairs of
   #if/#endif */
static void preprocess_skip(void)
{
    int a, start_of_line, c, in_warn_or_error;
    uint8_t *p;

    p = file->buf_ptr;
    a = 0;
redo_start:
    start_of_line = 1;
    in_warn_or_error = 0;
    for(;;) {
    redo_no_start:
        c = *p;
        switch(c) {
        case ' ':
        case '\t':
        case '\f':
        case '\v':
        case '\r':
            p++;
            goto redo_no_start;
        case '\n':
            file->line_num++;
            p++;
            goto redo_start;
        case '\\':
            file->buf_ptr = p;
            c = handle_eob();
            if (c == CH_EOF) {
                expect("#endif");
            } else if (c == '\\') {
                ch = file->buf_ptr[0];
                handle_stray_noerror();
            }
            p = file->buf_ptr;
            goto redo_no_start;
        /* skip strings */
        case '\"':
        case '\'':
            if (in_warn_or_error)
                goto _default;
            p = parse_pp_string(p, c, NULL);
            break;
        /* skip comments */
        case '/':
            if (in_warn_or_error)
                goto _default;
            file->buf_ptr = p;
            ch = *p;
            minp();
            p = file->buf_ptr;
            if (ch == '*') {
                p = parse_comment(p);
            } else if (ch == '/') {
                p = parse_line_comment(p);
            }
            break;
        case '#':
            p++;
            if (start_of_line) {
                file->buf_ptr = p;
                next_nomacro();
                p = file->buf_ptr;
                if (a == 0 && 
                    (tok == TOK_ELSE || tok == TOK_ELIF || tok == TOK_ENDIF))
                    goto the_end;
                if (tok == TOK_IF || tok == TOK_IFDEF || tok == TOK_IFNDEF)
                    a++;
                else if (tok == TOK_ENDIF)
                    a--;
                else if( tok == TOK_ERROR || tok == TOK_WARNING)
                    in_warn_or_error = 1;
                else if (tok == TOK_LINEFEED)
                    goto redo_start;
                else if (parse_flags & PARSE_FLAG_ASM_FILE)
                    p = parse_line_comment(p - 1);
            } else if (parse_flags & PARSE_FLAG_ASM_FILE)
                p = parse_line_comment(p - 1);
            break;
_default:
        default:
            p++;
            break;
        }
        start_of_line = 0;
    }
 the_end: ;
    file->buf_ptr = p;
}

#if 0
/* return the number of additional 'ints' necessary to store the
   token */
static inline int tok_size(const int *p)
{
    switch(*p) {
        /* 4 bytes */
    case TOK_CINT:
    case TOK_CUINT:
    case TOK_CCHAR:
    case TOK_LCHAR:
    case TOK_CFLOAT:
    case TOK_LINENUM:
        return 1 + 1;
    case TOK_STR:
    case TOK_LSTR:
    case TOK_PPNUM:
    case TOK_PPSTR:
        return 1 + ((sizeof(CString) + ((CString *)(p+1))->size + 3) >> 2);
    case TOK_CLONG:
    case TOK_CULONG:
    return 1 + LONG_SIZE / 4;
    case TOK_CDOUBLE:
    case TOK_CLLONG:
    case TOK_CULLONG:
        return 1 + 2;
    case TOK_CLDOUBLE:
        return 1 + LDOUBLE_SIZE / 4;
    default:
        return 1 + 0;
    }
}
#endif

/* token string handling */
ST_INLN void tok_str_new(TokenString *s)
{
    s->str = NULL;
    s->len = s->lastlen = 0;
    s->allocated_len = 0;
    s->last_line_num = -1;
}

ST_FUNC TokenString *tok_str_alloc(void)
{
    TokenString *str = tal_realloc(tokstr_alloc, 0, sizeof *str);
    tok_str_new(str);
    return str;
}

ST_FUNC int *tok_str_dup(TokenString *s)
{
    int *str;

    str = tal_realloc(tokstr_alloc, 0, s->len * sizeof(int));
    memcpy(str, s->str, s->len * sizeof(int));
    return str;
}

ST_FUNC void tok_str_free_str(int *str)
{
    tal_free(tokstr_alloc, str);
}

ST_FUNC void tok_str_free(TokenString *str)
{
    tok_str_free_str(str->str);
    tal_free(tokstr_alloc, str);
}

ST_FUNC int *tok_str_realloc(TokenString *s, int new_size)
{
    int *str, size;

    size = s->allocated_len;
    if (size < 16)
        size = 16;
    while (size < new_size)
        size = size * 2;
    if (size > s->allocated_len) {
        str = tal_realloc(tokstr_alloc, s->str, size * sizeof(int));
        s->allocated_len = size;
        s->str = str;
    }
    return s->str;
}

ST_FUNC void tok_str_add(TokenString *s, int t)
{
    int len, *str;

    len = s->len;
    str = s->str;
    if (len >= s->allocated_len)
        str = tok_str_realloc(s, len + 1);
    str[len++] = t;
    s->len = len;
}

ST_FUNC void begin_macro(TokenString *str, int alloc)
{
    str->alloc = alloc;
    str->prev = macro_stack;
    str->prev_ptr = macro_ptr;
    str->save_line_num = file->line_num;
    macro_ptr = str->str;
    macro_stack = str;
}

ST_FUNC void end_macro(void)
{
    TokenString *str = macro_stack;
    macro_stack = str->prev;
    macro_ptr = str->prev_ptr;
    file->line_num = str->save_line_num;
    if (str->alloc == 2) {
        str->alloc = 3; /* just mark as finished */
    } else {
        tok_str_free(str);
    }
}

static void tok_str_add2(TokenString *s, int t, CValue *cv)
{
    int len, *str;

    len = s->lastlen = s->len;
    str = s->str;

    /* allocate space for worst case */
    if (len + TOK_MAX_SIZE >= s->allocated_len)
        str = tok_str_realloc(s, len + TOK_MAX_SIZE + 1);
    str[len++] = t;
    switch(t) {
    case TOK_CINT:
    case TOK_CUINT:
    case TOK_CCHAR:
    case TOK_LCHAR:
    case TOK_CFLOAT:
    case TOK_LINENUM:
#if LONG_SIZE == 4
    case TOK_CLONG:
    case TOK_CULONG:
#endif
        str[len++] = cv->tab[0];
        break;
    case TOK_PPNUM:
    case TOK_PPSTR:
    case TOK_STR:
    case TOK_LSTR:
        {
            /* Insert the string into the int array. */
            size_t nb_words =
                1 + (cv->str.size + sizeof(int) - 1) / sizeof(int);
            if (len + nb_words >= s->allocated_len)
                str = tok_str_realloc(s, len + nb_words + 1);
            str[len] = cv->str.size;
            memcpy(&str[len + 1], cv->str.data, cv->str.size);
            len += nb_words;
        }
        break;
    case TOK_CDOUBLE:
    case TOK_CLLONG:
    case TOK_CULLONG:
#if LONG_SIZE == 8
    case TOK_CLONG:
    case TOK_CULONG:
#endif
#if LDOUBLE_SIZE == 8
    case TOK_CLDOUBLE:
#endif
        str[len++] = cv->tab[0];
        str[len++] = cv->tab[1];
        break;
#if LDOUBLE_SIZE == 12
    case TOK_CLDOUBLE:
        str[len++] = cv->tab[0];
        str[len++] = cv->tab[1];
        str[len++] = cv->tab[2];
#elif LDOUBLE_SIZE == 16
    case TOK_CLDOUBLE:
        str[len++] = cv->tab[0];
        str[len++] = cv->tab[1];
        str[len++] = cv->tab[2];
        str[len++] = cv->tab[3];
#elif LDOUBLE_SIZE != 8
#error add long double size support
#endif
        break;
    default:
        break;
    }
    s->len = len;
}

/* add the current parse token in token string 's' */
ST_FUNC void tok_str_add_tok(TokenString *s)
{
    CValue cval;

    /* save line number info */
    if (file->line_num != s->last_line_num) {
        s->last_line_num = file->line_num;
        cval.i = s->last_line_num;
        tok_str_add2(s, TOK_LINENUM, &cval);
    }
    tok_str_add2(s, tok, &tokc);
}

/* get a token from an integer array and increment pointer
   accordingly. we code it as a macro to avoid pointer aliasing. */
static inline void TOK_GET(int *t, const int **pp, CValue *cv)
{
    const int *p = *pp;
    int n, *tab;

    tab = cv->tab;
    switch(*t = *p++) {
#if LONG_SIZE == 4
    case TOK_CLONG:
#endif
    case TOK_CINT:
    case TOK_CCHAR:
    case TOK_LCHAR:
    case TOK_LINENUM:
        cv->i = *p++;
        break;
#if LONG_SIZE == 4
    case TOK_CULONG:
#endif
    case TOK_CUINT:
        cv->i = (unsigned)*p++;
        break;
    case TOK_CFLOAT:
    tab[0] = *p++;
    break;
    case TOK_STR:
    case TOK_LSTR:
    case TOK_PPNUM:
    case TOK_PPSTR:
        cv->str.size = *p++;
        cv->str.data = p;
        p += (cv->str.size + sizeof(int) - 1) / sizeof(int);
        break;
    case TOK_CDOUBLE:
    case TOK_CLLONG:
    case TOK_CULLONG:
#if LONG_SIZE == 8
    case TOK_CLONG:
    case TOK_CULONG:
#endif
        n = 2;
        goto copy;
    case TOK_CLDOUBLE:
#if LDOUBLE_SIZE == 16
        n = 4;
#elif LDOUBLE_SIZE == 12
        n = 3;
#elif LDOUBLE_SIZE == 8
        n = 2;
#else
# error add long double size support
#endif
    copy:
        do
            *tab++ = *p++;
        while (--n);
        break;
    default:
        break;
    }
    *pp = p;
}

static int macro_is_equal(const int *a, const int *b)
{
    CValue cv;
    int t;

    if (!a || !b)
        return 1;

    while (*a && *b) {
        /* first time preallocate macro_equal_buf, next time only reset position to start */
        cstr_reset(&macro_equal_buf);
        TOK_GET(&t, &a, &cv);
        cstr_cat(&macro_equal_buf, get_tok_str(t, &cv), 0);
        TOK_GET(&t, &b, &cv);
        if (strcmp(macro_equal_buf.data, get_tok_str(t, &cv)))
            return 0;
    }
    return !(*a || *b);
}

/* defines handling */
ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg)
{
    Sym *s, *o;

    o = define_find(v);
    s = sym_push2(&define_stack, v, macro_type, 0);
    s->d = str;
    s->next = first_arg;
    table_ident[v - TOK_IDENT]->sym_define = s;

    if (o && !macro_is_equal(o->d, s->d))
    tcc_warning("%s redefined", get_tok_str(v, NULL));
}

/* undefined a define symbol. Its name is just set to zero */
ST_FUNC void define_undef(Sym *s)
{
    int v = s->v;
    if (v >= TOK_IDENT && v < tok_ident)
        table_ident[v - TOK_IDENT]->sym_define = NULL;
}

ST_INLN Sym *define_find(int v)
{
    v -= TOK_IDENT;
    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
        return NULL;
    return table_ident[v]->sym_define;
}

/* free define stack until top reaches 'b' */
ST_FUNC void free_defines(Sym *b)
{
    while (define_stack != b) {
        Sym *top = define_stack;
        define_stack = top->prev;
        tok_str_free_str(top->d);
        define_undef(top);
        sym_free(top);
    }

    /* restore remaining (-D or predefined) symbols if they were
       #undef'd in the file */
    while (b) {
        int v = b->v;
        if (v >= TOK_IDENT && v < tok_ident) {
            Sym **d = &table_ident[v - TOK_IDENT]->sym_define;
            if (!*d)
                *d = b;
        }
        b = b->prev;
    }
}

/* label lookup */
ST_FUNC Sym *label_find(int v)
{
    v -= TOK_IDENT;
    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
        return NULL;
    return table_ident[v]->sym_label;
}

ST_FUNC Sym *label_push(Sym **ptop, int v, int flags)
{
    Sym *s, **ps;
    s = sym_push2(ptop, v, 0, 0);
    s->r = flags;
    ps = &table_ident[v - TOK_IDENT]->sym_label;
    if (ptop == &global_label_stack) {
        /* modify the top most local identifier, so that
           sym_identifier will point to 's' when popped */
        while (*ps != NULL)
            ps = &(*ps)->prev_tok;
    }
    s->prev_tok = *ps;
    *ps = s;
    return s;
}

/* pop labels until element last is reached. Look if any labels are
   undefined. Define symbols if '&&label' was used. */
ST_FUNC void label_pop(Sym **ptop, Sym *slast, int keep)
{
    Sym *s, *s1;
    for(s = *ptop; s != slast; s = s1) {
        s1 = s->prev;
        if (s->r == LABEL_DECLARED) {
            tcc_warning("label '%s' declared but not used", get_tok_str(s->v, NULL));
        } else if (s->r == LABEL_FORWARD) {
                tcc_error("label '%s' used but not defined",
                      get_tok_str(s->v, NULL));
        } else {
            if (s->c) {
                /* define corresponding symbol. A size of
                   1 is put. */
                put_extern_sym(s, cur_text_section, s->jnext, 1);
            }
        }
        /* remove label */
        table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok;
        if (!keep)
            sym_free(s);
    }
    if (!keep)
        *ptop = slast;
}

/* fake the nth "#if defined test_..." for tcc -dt -run */
static void maybe_run_test(TCCState *s)
{
    const char *p;
    if (s->include_stack_ptr != s->include_stack)
        return;
    p = get_tok_str(tok, NULL);
    if (0 != memcmp(p, "test_", 5))
        return;
    if (0 != --s->run_test)
        return;
    fprintf(s->ppfp, "\n[%s]\n" + !(s->dflag & 32), p), fflush(s->ppfp);
    define_push(tok, MACRO_OBJ, NULL, NULL);
}

/* eval an expression for #if/#elif */
static int expr_preprocess(void)
{
    int c, t;
    TokenString *str;
    
    str = tok_str_alloc();
    pp_expr = 1;
    while (tok != TOK_LINEFEED && tok != TOK_EOF) {
        next(); /* do macro subst */
        if (tok == TOK_DEFINED) {
            next_nomacro();
            t = tok;
            if (t == '(') 
                next_nomacro();
            if (tok < TOK_IDENT)
                expect("identifier");
            if (tcc_state->run_test)
                maybe_run_test(tcc_state);
            c = define_find(tok) != 0;
            if (t == '(') {
                next_nomacro();
                if (tok != ')')
                    expect("')'");
            }
            tok = TOK_CINT;
            tokc.i = c;
        } else if (tok >= TOK_IDENT) {
            /* if undefined macro */
            tok = TOK_CINT;
            tokc.i = 0;
        }
        tok_str_add_tok(str);
    }
    pp_expr = 0;
    tok_str_add(str, -1); /* simulate end of file */
    tok_str_add(str, 0);
    /* now evaluate C constant expression */
    begin_macro(str, 1);
    next();
    c = expr_const();
    end_macro();
    return c != 0;
}


/* parse after #define */
ST_FUNC void parse_define(void)
{
    Sym *s, *first, **ps;
    int v, t, varg, is_vaargs, spc;
    int saved_parse_flags = parse_flags;

    v = tok;
    if (v < TOK_IDENT || v == TOK_DEFINED)
        tcc_error("invalid macro name '%s'", get_tok_str(tok, &tokc));
    /* XXX: should check if same macro (ANSI) */
    first = NULL;
    t = MACRO_OBJ;
    /* We have to parse the whole define as if not in asm mode, in particular
       no line comment with '#' must be ignored.  Also for function
       macros the argument list must be parsed without '.' being an ID
       character.  */
    parse_flags = ((parse_flags & ~PARSE_FLAG_ASM_FILE) | PARSE_FLAG_SPACES);
    /* '(' must be just after macro definition for MACRO_FUNC */
    next_nomacro_spc();
    if (tok == '(') {
        int dotid = set_idnum('.', 0);
        next_nomacro();
        ps = &first;
        if (tok != ')') for (;;) {
            varg = tok;
            next_nomacro();
            is_vaargs = 0;
            if (varg == TOK_DOTS) {
                varg = TOK___VA_ARGS__;
                is_vaargs = 1;
            } else if (tok == TOK_DOTS && gnu_ext) {
                is_vaargs = 1;
                next_nomacro();
            }
            if (varg < TOK_IDENT)
        bad_list:
                tcc_error("bad macro parameter list");
            s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0);
            *ps = s;
            ps = &s->next;
            if (tok == ')')
                break;
            if (tok != ',' || is_vaargs)
                goto bad_list;
            next_nomacro();
        }
        next_nomacro_spc();
        t = MACRO_FUNC;
        set_idnum('.', dotid);
    }

    tokstr_buf.len = 0;
    spc = 2;
    parse_flags |= PARSE_FLAG_ACCEPT_STRAYS | PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED;
    /* The body of a macro definition should be parsed such that identifiers
       are parsed like the file mode determines (i.e. with '.' being an
       ID character in asm mode).  But '#' should be retained instead of
       regarded as line comment leader, so still don't set ASM_FILE
       in parse_flags. */
    while (tok != TOK_LINEFEED && tok != TOK_EOF) {
        /* remove spaces around ## and after '#' */
        if (TOK_TWOSHARPS == tok) {
            if (2 == spc)
                goto bad_twosharp;
            if (1 == spc)
                --tokstr_buf.len;
            spc = 3;
        tok = TOK_PPJOIN;
        } else if ('#' == tok) {
            spc = 4;
        } else if (check_space(tok, &spc)) {
            goto skip;
        }
        tok_str_add2(&tokstr_buf, tok, &tokc);
    skip:
        next_nomacro_spc();
    }

    parse_flags = saved_parse_flags;
    if (spc == 1)
        --tokstr_buf.len; /* remove trailing space */
    tok_str_add(&tokstr_buf, 0);
    if (3 == spc)
bad_twosharp:
        tcc_error("'##' cannot appear at either end of macro");
    define_push(v, t, tok_str_dup(&tokstr_buf), first);
}

static CachedInclude *search_cached_include(TCCState *s1, const char *filename, int add)
{
    const unsigned char *s;
    unsigned int h;
    CachedInclude *e;
    int i;

    h = TOK_HASH_INIT;
    s = (unsigned char *) filename;
    while (*s) {
#ifdef _WIN32
        h = TOK_HASH_FUNC(h, toup(*s));
#else
        h = TOK_HASH_FUNC(h, *s);
#endif
        s++;
    }
    h &= (CACHED_INCLUDES_HASH_SIZE - 1);

    i = s1->cached_includes_hash[h];
    for(;;) {
        if (i == 0)
            break;
        e = s1->cached_includes[i - 1];
        if (0 == PATHCMP(e->filename, filename))
            return e;
        i = e->hash_next;
    }
    if (!add)
        return NULL;

    e = tcc_malloc(sizeof(CachedInclude) + strlen(filename));
    strcpy(e->filename, filename);
    e->ifndef_macro = e->once = 0;
    dynarray_add(&s1->cached_includes, &s1->nb_cached_includes, e);
    /* add in hash table */
    e->hash_next = s1->cached_includes_hash[h];
    s1->cached_includes_hash[h] = s1->nb_cached_includes;
#ifdef INC_DEBUG
    printf("adding cached '%s'\n", filename);
#endif
    return e;
}

static void pragma_parse(TCCState *s1)
{
    next_nomacro();
    if (tok == TOK_push_macro || tok == TOK_pop_macro) {
        int t = tok, v;
        Sym *s;

        if (next(), tok != '(')
            goto pragma_err;
        if (next(), tok != TOK_STR)
            goto pragma_err;
        v = tok_alloc(tokc.str.data, tokc.str.size - 1)->tok;
        if (next(), tok != ')')
            goto pragma_err;
        if (t == TOK_push_macro) {
            while (NULL == (s = define_find(v)))
                define_push(v, 0, NULL, NULL);
            s->type.ref = s; /* set push boundary */
        } else {
            for (s = define_stack; s; s = s->prev)
                if (s->v == v && s->type.ref == s) {
                    s->type.ref = NULL;
                    break;
                }
        }
        if (s)
            table_ident[v - TOK_IDENT]->sym_define = s->d ? s : NULL;
        else
            tcc_warning("unbalanced #pragma pop_macro");
        pp_debug_tok = t, pp_debug_symv = v;

    } else if (tok == TOK_once) {
        search_cached_include(s1, file->filename, 1)->once = pp_once;

    } else if (s1->output_type == TCC_OUTPUT_PREPROCESS) {
        /* tcc -E: keep pragmas below unchanged */
        unget_tok(' ');
        unget_tok(TOK_PRAGMA);
        unget_tok('#');
        unget_tok(TOK_LINEFEED);

    } else if (tok == TOK_pack) {
        /* This may be:
           #pragma pack(1) // set
           #pragma pack() // reset to default
           #pragma pack(push,1) // push & set
           #pragma pack(pop) // restore previous */
        next();
        skip('(');
        if (tok == TOK_ASM_pop) {
            next();
            if (s1->pack_stack_ptr <= s1->pack_stack) {
            stk_error:
                tcc_error("out of pack stack");
            }
            s1->pack_stack_ptr--;
        } else {
            int val = 0;
            if (tok != ')') {
                if (tok == TOK_ASM_push) {
                    next();
                    if (s1->pack_stack_ptr >= s1->pack_stack + PACK_STACK_SIZE - 1)
                        goto stk_error;
                    s1->pack_stack_ptr++;
                    skip(',');
                }
                if (tok != TOK_CINT)
                    goto pragma_err;
                val = tokc.i;
                if (val < 1 || val > 16 || (val & (val - 1)) != 0)
                    goto pragma_err;
                next();
            }
            *s1->pack_stack_ptr = val;
        }
        if (tok != ')')
            goto pragma_err;

    } else if (tok == TOK_comment) {
        char *p; int t;
        next();
        skip('(');
        t = tok;
        next();
        skip(',');
        if (tok != TOK_STR)
            goto pragma_err;
        p = tcc_strdup((char *)tokc.str.data);
        next();
        if (tok != ')')
            goto pragma_err;
        if (t == TOK_lib) {
            dynarray_add(&s1->pragma_libs, &s1->nb_pragma_libs, p);
        } else {
            if (t == TOK_option)
                tcc_set_options(s1, p);
            tcc_free(p);
        }

    } else if (s1->warn_unsupported) {
        tcc_warning("#pragma %s is ignored", get_tok_str(tok, &tokc));
    }
    return;

pragma_err:
    tcc_error("malformed #pragma directive");
    return;
}

/* is_bof is true if first non space token at beginning of file */
ST_FUNC void preprocess(int is_bof)
{
    TCCState *s1 = tcc_state;
    int i, c, n, saved_parse_flags;
    char buf[1024], *q;
    Sym *s;

    saved_parse_flags = parse_flags;
    parse_flags = PARSE_FLAG_PREPROCESS
        | PARSE_FLAG_TOK_NUM
        | PARSE_FLAG_TOK_STR
        | PARSE_FLAG_LINEFEED
        | (parse_flags & PARSE_FLAG_ASM_FILE)
        ;

    next_nomacro();
 redo:
    switch(tok) {
    case TOK_DEFINE:
        pp_debug_tok = tok;
        next_nomacro();
        pp_debug_symv = tok;
        parse_define();
        break;
    case TOK_UNDEF:
        pp_debug_tok = tok;
        next_nomacro();
        pp_debug_symv = tok;
        s = define_find(tok);
        /* undefine symbol by putting an invalid name */
        if (s)
            define_undef(s);
        break;
    case TOK_INCLUDE:
    case TOK_INCLUDE_NEXT:
        ch = file->buf_ptr[0];
        /* XXX: incorrect if comments : use next_nomacro with a special mode */
        skip_spaces();
        if (ch == '<') {
            c = '>';
            goto read_name;
        } else if (ch == '\"') {
            c = ch;
        read_name:
            inp();
            q = buf;
            while (ch != c && ch != '\n' && ch != CH_EOF) {
                if ((q - buf) < sizeof(buf) - 1)
                    *q++ = ch;
                if (ch == '\\') {
                    if (handle_stray_noerror() == 0)
                        --q;
                } else
                    inp();
            }
            *q = '\0';
            minp();
#if 0
            /* eat all spaces and comments after include */
            /* XXX: slightly incorrect */
            while (ch1 != '\n' && ch1 != CH_EOF)
                inp();
#endif
        } else {
        int len;
            /* computed #include : concatenate everything up to linefeed,
           the result must be one of the two accepted forms.
           Don't convert pp-tokens to tokens here.  */
        parse_flags = (PARSE_FLAG_PREPROCESS
               | PARSE_FLAG_LINEFEED
               | (parse_flags & PARSE_FLAG_ASM_FILE));
            next();
            buf[0] = '\0';
        while (tok != TOK_LINEFEED) {
        pstrcat(buf, sizeof(buf), get_tok_str(tok, &tokc));
        next();
        }
        len = strlen(buf);
        /* check syntax and remove '<>|""' */
        if ((len < 2 || ((buf[0] != '"' || buf[len-1] != '"') &&
                 (buf[0] != '<' || buf[len-1] != '>'))))
            tcc_error("'#include' expects \"FILENAME\" or <FILENAME>");
        c = buf[len-1];
        memmove(buf, buf + 1, len - 2);
        buf[len - 2] = '\0';
        }

        if (s1->include_stack_ptr >= s1->include_stack + INCLUDE_STACK_SIZE)
            tcc_error("#include recursion too deep");
        /* store current file in stack, but increment stack later below */
        *s1->include_stack_ptr = file;
        i = tok == TOK_INCLUDE_NEXT ? file->include_next_index : 0;
        n = 2 + s1->nb_include_paths + s1->nb_sysinclude_paths;
        for (; i < n; ++i) {
            char buf1[sizeof file->filename];
            CachedInclude *e;
            const char *path;

            if (i == 0) {
                /* check absolute include path */
                if (!IS_ABSPATH(buf))
                    continue;
                buf1[0] = 0;

            } else if (i == 1) {
                /* search in file's dir if "header.h" */
                if (c != '\"')
                    continue;
                /* https://savannah.nongnu.org/bugs/index.php?50847 */
                path = file->true_filename;
                pstrncpy(buf1, path, tcc_basename(path) - path);

            } else {
                /* search in all the include paths */
                int j = i - 2, k = j - s1->nb_include_paths;
                path = k < 0 ? s1->include_paths[j] : s1->sysinclude_paths[k];
                pstrcpy(buf1, sizeof(buf1), path);
                pstrcat(buf1, sizeof(buf1), "/");
            }

            pstrcat(buf1, sizeof(buf1), buf);
            e = search_cached_include(s1, buf1, 0);
            if (e && (define_find(e->ifndef_macro) || e->once == pp_once)) {
                /* no need to parse the include because the 'ifndef macro'
                   is defined (or had #pragma once) */
#ifdef INC_DEBUG
                printf("%s: skipping cached %s\n", file->filename, buf1);
#endif
                goto include_done;
            }

            if (tcc_open(s1, buf1) < 0)
                continue;

            file->include_next_index = i + 1;
#ifdef INC_DEBUG
            printf("%s: including %s\n", file->prev->filename, file->filename);
#endif
            /* update target deps */
            dynarray_add(&s1->target_deps, &s1->nb_target_deps,
                    tcc_strdup(buf1));
            /* push current file in stack */
            ++s1->include_stack_ptr;
            /* add include file debug info */
            if (s1->do_debug)
                put_stabs(file->filename, N_BINCL, 0, 0, 0);
            tok_flags |= TOK_FLAG_BOF | TOK_FLAG_BOL;
            ch = file->buf_ptr[0];
            goto the_end;
        }
        tcc_error("include file '%s' not found", buf);
include_done:
        break;
    case TOK_IFNDEF:
        c = 1;
        goto do_ifdef;
    case TOK_IF:
        c = expr_preprocess();
        goto do_if;
    case TOK_IFDEF:
        c = 0;
    do_ifdef:
        next_nomacro();
        if (tok < TOK_IDENT)
            tcc_error("invalid argument for '#if%sdef'", c ? "n" : "");
        if (is_bof) {
            if (c) {
#ifdef INC_DEBUG
                printf("#ifndef %s\n", get_tok_str(tok, NULL));
#endif
                file->ifndef_macro = tok;
            }
        }
        c = (define_find(tok) != 0) ^ c;
    do_if:
        if (s1->ifdef_stack_ptr >= s1->ifdef_stack + IFDEF_STACK_SIZE)
            tcc_error("memory full (ifdef)");
        *s1->ifdef_stack_ptr++ = c;
        goto test_skip;
    case TOK_ELSE:
        if (s1->ifdef_stack_ptr == s1->ifdef_stack)
            tcc_error("#else without matching #if");
        if (s1->ifdef_stack_ptr[-1] & 2)
            tcc_error("#else after #else");
        c = (s1->ifdef_stack_ptr[-1] ^= 3);
        goto test_else;
    case TOK_ELIF:
        if (s1->ifdef_stack_ptr == s1->ifdef_stack)
            tcc_error("#elif without matching #if");
        c = s1->ifdef_stack_ptr[-1];
        if (c > 1)
            tcc_error("#elif after #else");
        /* last #if/#elif expression was true: we skip */
        if (c == 1) {
            c = 0;
        } else {
            c = expr_preprocess();
            s1->ifdef_stack_ptr[-1] = c;
        }
    test_else:
        if (s1->ifdef_stack_ptr == file->ifdef_stack_ptr + 1)
            file->ifndef_macro = 0;
    test_skip:
        if (!(c & 1)) {
            preprocess_skip();
            is_bof = 0;
            goto redo;
        }
        break;
    case TOK_ENDIF:
        if (s1->ifdef_stack_ptr <= file->ifdef_stack_ptr)
            tcc_error("#endif without matching #if");
        s1->ifdef_stack_ptr--;
        /* '#ifndef macro' was at the start of file. Now we check if
           an '#endif' is exactly at the end of file */
        if (file->ifndef_macro &&
            s1->ifdef_stack_ptr == file->ifdef_stack_ptr) {
            file->ifndef_macro_saved = file->ifndef_macro;
            /* need to set to zero to avoid false matches if another
               #ifndef at middle of file */
            file->ifndef_macro = 0;
            while (tok != TOK_LINEFEED)
                next_nomacro();
            tok_flags |= TOK_FLAG_ENDIF;
            goto the_end;
        }
        break;
    case TOK_PPNUM:
        n = strtoul((char*)tokc.str.data, &q, 10);
        goto _line_num;
    case TOK_LINE:
        next();
        if (tok != TOK_CINT)
    _line_err:
            tcc_error("wrong #line format");
        n = tokc.i;
    _line_num:
        next();
        if (tok != TOK_LINEFEED) {
            if (tok == TOK_STR) {
                if (file->true_filename == file->filename)
                    file->true_filename = tcc_strdup(file->filename);
                pstrcpy(file->filename, sizeof(file->filename), (char *)tokc.str.data);
            } else if (parse_flags & PARSE_FLAG_ASM_FILE)
                break;
            else
                goto _line_err;
            --n;
        }
        if (file->fd > 0)
            total_lines += file->line_num - n;
        file->line_num = n;
        if (s1->do_debug)
            put_stabs(file->filename, N_BINCL, 0, 0, 0);
        break;
    case TOK_ERROR:
    case TOK_WARNING:
        c = tok;
        ch = file->buf_ptr[0];
        skip_spaces();
        q = buf;
        while (ch != '\n' && ch != CH_EOF) {
            if ((q - buf) < sizeof(buf) - 1)
                *q++ = ch;
            if (ch == '\\') {
                if (handle_stray_noerror() == 0)
                    --q;
            } else
                inp();
        }
        *q = '\0';
        if (c == TOK_ERROR)
            tcc_error("#error %s", buf);
        else
            tcc_warning("#warning %s", buf);
        break;
    case TOK_PRAGMA:
        pragma_parse(s1);
        break;
    case TOK_LINEFEED:
        goto the_end;
    default:
        /* ignore gas line comment in an 'S' file. */
        if (saved_parse_flags & PARSE_FLAG_ASM_FILE)
            goto ignore;
        if (tok == '!' && is_bof)
            /* '!' is ignored at beginning to allow C scripts. */
            goto ignore;
        tcc_warning("Ignoring unknown preprocessing directive #%s", get_tok_str(tok, &tokc));
    ignore:
        file->buf_ptr = parse_line_comment(file->buf_ptr - 1);
        goto the_end;
    }
    /* ignore other preprocess commands or #! for C scripts */
    while (tok != TOK_LINEFEED)
        next_nomacro();
 the_end:
    parse_flags = saved_parse_flags;
}

/* evaluate escape codes in a string. */
static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long)
{
    int c, n;
    const uint8_t *p;

    p = buf;
    for(;;) {
        c = *p;
        if (c == '\0')
            break;
        if (c == '\\') {
            p++;
            /* escape */
            c = *p;
            switch(c) {
            case '0': case '1': case '2': case '3':
            case '4': case '5': case '6': case '7':
                /* at most three octal digits */
                n = c - '0';
                p++;
                c = *p;
                if (isoct(c)) {
                    n = n * 8 + c - '0';
                    p++;
                    c = *p;
                    if (isoct(c)) {
                        n = n * 8 + c - '0';
                        p++;
                    }
                }
                c = n;
                goto add_char_nonext;
            case 'x':
            case 'u':
            case 'U':
                p++;
                n = 0;
                for(;;) {
                    c = *p;
                    if (c >= 'a' && c <= 'f')
                        c = c - 'a' + 10;
                    else if (c >= 'A' && c <= 'F')
                        c = c - 'A' + 10;
                    else if (isnum(c))
                        c = c - '0';
                    else
                        break;
                    n = n * 16 + c;
                    p++;
                }
                c = n;
                goto add_char_nonext;
            case 'a':
                c = '\a';
                break;
            case 'b':
                c = '\b';
                break;
            case 'f':
                c = '\f';
                break;
            case 'n':
                c = '\n';
                break;
            case 'r':
                c = '\r';
                break;
            case 't':
                c = '\t';
                break;
            case 'v':
                c = '\v';
                break;
            case 'e':
                if (!gnu_ext)
                    goto invalid_escape;
                c = 27;
                break;
            case '\'':
            case '\"':
            case '\\': 
            case '?':
                break;
            default:
            invalid_escape:
                if (c >= '!' && c <= '~')
                    tcc_warning("unknown escape sequence: \'\\%c\'", c);
                else
                    tcc_warning("unknown escape sequence: \'\\x%x\'", c);
                break;
            }
        } else if (is_long && c >= 0x80) {
            /* assume we are processing UTF-8 sequence */
            /* reference: The Unicode Standard, Version 10.0, ch3.9 */

            int cont; /* count of continuation bytes */
            int skip; /* how many bytes should skip when error occurred */
            int i;

            /* decode leading byte */
            if (c < 0xC2) {
                skip = 1; goto invalid_utf8_sequence;
            } else if (c <= 0xDF) {
                cont = 1; n = c & 0x1f;
            } else if (c <= 0xEF) {
                cont = 2; n = c & 0xf;
            } else if (c <= 0xF4) {
                cont = 3; n = c & 0x7;
            } else {
                skip = 1; goto invalid_utf8_sequence;
            }

            /* decode continuation bytes */
            for (i = 1; i <= cont; i++) {
                int l = 0x80, h = 0xBF;

                /* adjust limit for second byte */
                if (i == 1) {
                    switch (c) {
                    case 0xE0: l = 0xA0; break;
                    case 0xED: h = 0x9F; break;
                    case 0xF0: l = 0x90; break;
                    case 0xF4: h = 0x8F; break;
                    }
                }

                if (p[i] < l || p[i] > h) {
                    skip = i; goto invalid_utf8_sequence;
                }

                n = (n << 6) | (p[i] & 0x3f);
            }

            /* advance pointer */
            p += 1 + cont;
            c = n;
            goto add_char_nonext;

            /* error handling */
        invalid_utf8_sequence:
            tcc_warning("ill-formed UTF-8 subsequence starting with: \'\\x%x\'", c);
            c = 0xFFFD;
            p += skip;
            goto add_char_nonext;

        }
        p++;
    add_char_nonext:
        if (!is_long)
            cstr_ccat(outstr, c);
        else {
#ifdef TCC_TARGET_PE
            /* store as UTF-16 */
            if (c < 0x10000) {
                cstr_wccat(outstr, c);
            } else {
                c -= 0x10000;
                cstr_wccat(outstr, (c >> 10) + 0xD800);
                cstr_wccat(outstr, (c & 0x3FF) + 0xDC00);
            }
#else
            cstr_wccat(outstr, c);
#endif
        }
    }
    /* add a trailing '\0' */
    if (!is_long)
        cstr_ccat(outstr, '\0');
    else
        cstr_wccat(outstr, '\0');
}

static void parse_string(const char *s, int len)
{
    uint8_t buf[1000], *p = buf;
    int is_long, sep;

    if ((is_long = *s == 'L'))
        ++s, --len;
    sep = *s++;
    len -= 2;
    if (len >= sizeof buf)
        p = tcc_malloc(len + 1);
    memcpy(p, s, len);
    p[len] = 0;

    cstr_reset(&tokcstr);
    parse_escape_string(&tokcstr, p, is_long);
    if (p != buf)
        tcc_free(p);

    if (sep == '\'') {
        int char_size, i, n, c;
        /* XXX: make it portable */
        if (!is_long)
            tok = TOK_CCHAR, char_size = 1;
        else
            tok = TOK_LCHAR, char_size = sizeof(nwchar_t);
        n = tokcstr.size / char_size - 1;
        if (n < 1)
            tcc_error("empty character constant");
        if (n > 1)
            tcc_warning("multi-character character constant");
        for (c = i = 0; i < n; ++i) {
            if (is_long)
                c = ((nwchar_t *)tokcstr.data)[i];
            else
                c = (c << 8) | ((char *)tokcstr.data)[i];
        }
        tokc.i = c;
    } else {
        tokc.str.size = tokcstr.size;
        tokc.str.data = tokcstr.data;
        if (!is_long)
            tok = TOK_STR;
        else
            tok = TOK_LSTR;
    }
}

/* we use 64 bit numbers */
#define BN_SIZE 2

/* bn = (bn << shift) | or_val */
static void bn_lshift(unsigned int *bn, int shift, int or_val)
{
    int i;
    unsigned int v;
    for(i=0;i<BN_SIZE;i++) {
        v = bn[i];
        bn[i] = (v << shift) | or_val;
        or_val = v >> (32 - shift);
    }
}

static void bn_zero(unsigned int *bn)
{
    int i;
    for(i=0;i<BN_SIZE;i++) {
        bn[i] = 0;
    }
}

/* parse number in null terminated string 'p' and return it in the
   current token */
static void parse_number(const char *p)
{
    int b, t, shift, frac_bits, s, exp_val, ch;
    char *q;
    unsigned int bn[BN_SIZE];
    double d;

    /* number */
    q = token_buf;
    ch = *p++;
    t = ch;
    ch = *p++;
    *q++ = t;
    b = 10;
    if (t == '.') {
        goto float_frac_parse;
    } else if (t == '0') {
        if (ch == 'x' || ch == 'X') {
            q--;
            ch = *p++;
            b = 16;
        } else if (tcc_ext && (ch == 'b' || ch == 'B')) {
            q--;
            ch = *p++;
            b = 2;
        }
    }
    /* parse all digits. cannot check octal numbers at this stage
       because of floating point constants */
    while (1) {
        if (ch >= 'a' && ch <= 'f')
            t = ch - 'a' + 10;
        else if (ch >= 'A' && ch <= 'F')
            t = ch - 'A' + 10;
        else if (isnum(ch))
            t = ch - '0';
        else
            break;
        if (t >= b)
            break;
        if (q >= token_buf + STRING_MAX_SIZE) {
        num_too_long:
            tcc_error("number too long");
        }
        *q++ = ch;
        ch = *p++;
    }
    if (ch == '.' ||
        ((ch == 'e' || ch == 'E') && b == 10) ||
        ((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) {
        if (b != 10) {
            /* NOTE: strtox should support that for hexa numbers, but
               non ISOC99 libcs do not support it, so we prefer to do
               it by hand */
            /* hexadecimal or binary floats */
            /* XXX: handle overflows */
            *q = '\0';
            if (b == 16)
                shift = 4;
            else 
                shift = 1;
            bn_zero(bn);
            q = token_buf;
            while (1) {
                t = *q++;
                if (t == '\0') {
                    break;
                } else if (t >= 'a') {
                    t = t - 'a' + 10;
                } else if (t >= 'A') {
                    t = t - 'A' + 10;
                } else {
                    t = t - '0';
                }
                bn_lshift(bn, shift, t);
            }
            frac_bits = 0;
            if (ch == '.') {
                ch = *p++;
                while (1) {
                    t = ch;
                    if (t >= 'a' && t <= 'f') {
                        t = t - 'a' + 10;
                    } else if (t >= 'A' && t <= 'F') {
                        t = t - 'A' + 10;
                    } else if (t >= '0' && t <= '9') {
                        t = t - '0';
                    } else {
                        break;
                    }
                    if (t >= b)
                        tcc_error("invalid digit");
                    bn_lshift(bn, shift, t);
                    frac_bits += shift;
                    ch = *p++;
                }
            }
            if (ch != 'p' && ch != 'P')
                expect("exponent");
            ch = *p++;
            s = 1;
            exp_val = 0;
            if (ch == '+') {
                ch = *p++;
            } else if (ch == '-') {
                s = -1;
                ch = *p++;
            }
            if (ch < '0' || ch > '9')
                expect("exponent digits");
            while (ch >= '0' && ch <= '9') {
                exp_val = exp_val * 10 + ch - '0';
                ch = *p++;
            }
            exp_val = exp_val * s;
            
            /* now we can generate the number */
            /* XXX: should patch directly float number */
            d = (double)bn[1] * 4294967296.0 + (double)bn[0];
            d = ldexp(d, exp_val - frac_bits);
            t = toup(ch);
            if (t == 'F') {
                ch = *p++;
                tok = TOK_CFLOAT;
                /* float : should handle overflow */
                tokc.f = (float)d;
            } else if (t == 'L') {
                ch = *p++;
#ifdef TCC_TARGET_PE
                tok = TOK_CDOUBLE;
                tokc.d = d;
#else
                tok = TOK_CLDOUBLE;
                /* XXX: not large enough */
                tokc.ld = (long double)d;
#endif
            } else {
                tok = TOK_CDOUBLE;
                tokc.d = d;
            }
        } else {
            /* decimal floats */
            if (ch == '.') {
                if (q >= token_buf + STRING_MAX_SIZE)
                    goto num_too_long;
                *q++ = ch;
                ch = *p++;
            float_frac_parse:
                while (ch >= '0' && ch <= '9') {
                    if (q >= token_buf + STRING_MAX_SIZE)
                        goto num_too_long;
                    *q++ = ch;
                    ch = *p++;
                }
            }
            if (ch == 'e' || ch == 'E') {
                if (q >= token_buf + STRING_MAX_SIZE)
                    goto num_too_long;
                *q++ = ch;
                ch = *p++;
                if (ch == '-' || ch == '+') {
                    if (q >= token_buf + STRING_MAX_SIZE)
                        goto num_too_long;
                    *q++ = ch;
                    ch = *p++;
                }
                if (ch < '0' || ch > '9')
                    expect("exponent digits");
                while (ch >= '0' && ch <= '9') {
                    if (q >= token_buf + STRING_MAX_SIZE)
                        goto num_too_long;
                    *q++ = ch;
                    ch = *p++;
                }
            }
            *q = '\0';
            t = toup(ch);
            errno = 0;
            if (t == 'F') {
                ch = *p++;
                tok = TOK_CFLOAT;
                tokc.f = strtof(token_buf, NULL);
            } else if (t == 'L') {
                ch = *p++;
#ifdef TCC_TARGET_PE
                tok = TOK_CDOUBLE;
                tokc.d = strtod(token_buf, NULL);
#else
                tok = TOK_CLDOUBLE;
                tokc.ld = strtold(token_buf, NULL);
#endif
            } else {
                tok = TOK_CDOUBLE;
                tokc.d = strtod(token_buf, NULL);
            }
        }
    } else {
        unsigned long long n, n1;
        int lcount, ucount, ov = 0;
        const char *p1;

        /* integer number */
        *q = '\0';
        q = token_buf;
        if (b == 10 && *q == '0') {
            b = 8;
            q++;
        }
        n = 0;
        while(1) {
            t = *q++;
            /* no need for checks except for base 10 / 8 errors */
            if (t == '\0')
                break;
            else if (t >= 'a')
                t = t - 'a' + 10;
            else if (t >= 'A')
                t = t - 'A' + 10;
            else
                t = t - '0';
            if (t >= b)
                tcc_error("invalid digit");
            n1 = n;
            n = n * b + t;
            /* detect overflow */
            if (n1 >= 0x1000000000000000ULL && n / b != n1)
                ov = 1;
        }

        /* Determine the characteristics (unsigned and/or 64bit) the type of
           the constant must have according to the constant suffix(es) */
        lcount = ucount = 0;
        p1 = p;
        for(;;) {
            t = toup(ch);
            if (t == 'L') {
                if (lcount >= 2)
                    tcc_error("three 'l's in integer constant");
                if (lcount && *(p - 1) != ch)
                    tcc_error("incorrect integer suffix: %s", p1);
                lcount++;
                ch = *p++;
            } else if (t == 'U') {
                if (ucount >= 1)
                    tcc_error("two 'u's in integer constant");
                ucount++;
                ch = *p++;
            } else {
                break;
            }
        }

        /* Determine if it needs 64 bits and/or unsigned in order to fit */
        if (ucount == 0 && b == 10) {
            if (lcount <= (LONG_SIZE == 4)) {
                if (n >= 0x80000000U)
                    lcount = (LONG_SIZE == 4) + 1;
            }
            if (n >= 0x8000000000000000ULL)
                ov = 1, ucount = 1;
        } else {
            if (lcount <= (LONG_SIZE == 4)) {
                if (n >= 0x100000000ULL)
                    lcount = (LONG_SIZE == 4) + 1;
                else if (n >= 0x80000000U)
                    ucount = 1;
            }
            if (n >= 0x8000000000000000ULL)
                ucount = 1;
        }

        if (ov)
            tcc_warning("integer constant overflow");

        tok = TOK_CINT;
    if (lcount) {
            tok = TOK_CLONG;
            if (lcount == 2)
                tok = TOK_CLLONG;
    }
    if (ucount)
        ++tok; /* TOK_CU... */
        tokc.i = n;
    }
    if (ch)
        tcc_error("invalid number\n");
}


#define PARSE2(c1, tok1, c2, tok2)              \
    case c1:                                    \
        PEEKC(c, p);                            \
        if (c == c2) {                          \
            p++;                                \
            tok = tok2;                         \
        } else {                                \
            tok = tok1;                         \
        }                                       \
        break;

/* return next token without macro substitution */
static inline void next_nomacro1(void)
{
    int t, c, is_long, len;
    TokenSym *ts;
    uint8_t *p, *p1;
    unsigned int h;

    p = file->buf_ptr;
 redo_no_start:
    c = *p;
    switch(c) {
    case ' ':
    case '\t':
        tok = c;
        p++;
        if (parse_flags & PARSE_FLAG_SPACES)
            goto keep_tok_flags;
        while (isidnum_table[*p - CH_EOF] & IS_SPC)
            ++p;
        goto redo_no_start;
    case '\f':
    case '\v':
    case '\r':
        p++;
        goto redo_no_start;
    case '\\':
        /* first look if it is in fact an end of buffer */
        c = handle_stray1(p);
        p = file->buf_ptr;
        if (c == '\\')
            goto parse_simple;
        if (c != CH_EOF)
            goto redo_no_start;
        {
            TCCState *s1 = tcc_state;
            if ((parse_flags & PARSE_FLAG_LINEFEED)
                && !(tok_flags & TOK_FLAG_EOF)) {
                tok_flags |= TOK_FLAG_EOF;
                tok = TOK_LINEFEED;
                goto keep_tok_flags;
            } else if (!(parse_flags & PARSE_FLAG_PREPROCESS)) {
                tok = TOK_EOF;
            } else if (s1->ifdef_stack_ptr != file->ifdef_stack_ptr) {
                tcc_error("missing #endif");
            } else if (s1->include_stack_ptr == s1->include_stack) {
                /* no include left : end of file. */
                tok = TOK_EOF;
            } else {
                tok_flags &= ~TOK_FLAG_EOF;
                /* pop include file */
                
                /* test if previous '#endif' was after a #ifdef at
                   start of file */
                if (tok_flags & TOK_FLAG_ENDIF) {
#ifdef INC_DEBUG
                    printf("#endif %s\n", get_tok_str(file->ifndef_macro_saved, NULL));
#endif
                    search_cached_include(s1, file->filename, 1)
                        ->ifndef_macro = file->ifndef_macro_saved;
                    tok_flags &= ~TOK_FLAG_ENDIF;
                }

                /* add end of include file debug info */
                if (tcc_state->do_debug) {
                    put_stabd(N_EINCL, 0, 0);
                }
                /* pop include stack */
                tcc_close();
                s1->include_stack_ptr--;
                p = file->buf_ptr;
                if (p == file->buffer)
                    tok_flags = TOK_FLAG_BOF|TOK_FLAG_BOL;
                goto redo_no_start;
            }
        }
        break;

    case '\n':
        file->line_num++;
        tok_flags |= TOK_FLAG_BOL;
        p++;
maybe_newline:
        if (0 == (parse_flags & PARSE_FLAG_LINEFEED))
            goto redo_no_start;
        tok = TOK_LINEFEED;
        goto keep_tok_flags;

    case '#':
        /* XXX: simplify */
        PEEKC(c, p);
        if ((tok_flags & TOK_FLAG_BOL) && 
            (parse_flags & PARSE_FLAG_PREPROCESS)) {
            file->buf_ptr = p;
            preprocess(tok_flags & TOK_FLAG_BOF);
            p = file->buf_ptr;
            goto maybe_newline;
        } else {
            if (c == '#') {
                p++;
                tok = TOK_TWOSHARPS;
            } else {
                if (parse_flags & PARSE_FLAG_ASM_FILE) {
                    p = parse_line_comment(p - 1);
                    goto redo_no_start;
                } else {
                    tok = '#';
                }
            }
        }
        break;
    
    /* dollar is allowed to start identifiers when not parsing asm */
    case '$':
        if (!(isidnum_table[c - CH_EOF] & IS_ID)
         || (parse_flags & PARSE_FLAG_ASM_FILE))
            goto parse_simple;

    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 'A': case 'B': case 'C': case 'D':
    case 'E': case 'F': case 'G': case 'H':
    case 'I': case 'J': case 'K': 
    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 '_':
    parse_ident_fast:
        p1 = p;
        h = TOK_HASH_INIT;
        h = TOK_HASH_FUNC(h, c);
        while (c = *++p, isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
            h = TOK_HASH_FUNC(h, c);
        len = p - p1;
        if (c != '\\') {
            TokenSym **pts;

            /* fast case : no stray found, so we have the full token
               and we have already hashed it */
            h &= (TOK_HASH_SIZE - 1);
            pts = &hash_ident[h];
            for(;;) {
                ts = *pts;
                if (!ts)
                    break;
                if (ts->len == len && !memcmp(ts->str, p1, len))
                    goto token_found;
                pts = &(ts->hash_next);
            }
            ts = tok_alloc_new(pts, (char *) p1, len);
        token_found: ;
        } else {
            /* slower case */
            cstr_reset(&tokcstr);
            cstr_cat(&tokcstr, (char *) p1, len);
            p--;
            PEEKC(c, p);
        parse_ident_slow:
            while (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
            {
                cstr_ccat(&tokcstr, c);
                PEEKC(c, p);
            }
            ts = tok_alloc(tokcstr.data, tokcstr.size);
        }
        tok = ts->tok;
        break;
    case 'L':
        t = p[1];
        if (t != '\\' && t != '\'' && t != '\"') {
            /* fast case */
            goto parse_ident_fast;
        } else {
            PEEKC(c, p);
            if (c == '\'' || c == '\"') {
                is_long = 1;
                goto str_const;
            } else {
                cstr_reset(&tokcstr);
                cstr_ccat(&tokcstr, 'L');
                goto parse_ident_slow;
            }
        }
        break;

    case '0': case '1': case '2': case '3':
    case '4': case '5': case '6': case '7':
    case '8': case '9':
        t = c;
        PEEKC(c, p);
        /* after the first digit, accept digits, alpha, '.' or sign if
           prefixed by 'eEpP' */
    parse_num:
        cstr_reset(&tokcstr);
        for(;;) {
            cstr_ccat(&tokcstr, t);
            if (!((isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
                  || c == '.'
                  || ((c == '+' || c == '-')
                      && (((t == 'e' || t == 'E')
                            && !(parse_flags & PARSE_FLAG_ASM_FILE
                                /* 0xe+1 is 3 tokens in asm */
                                && ((char*)tokcstr.data)[0] == '0'
                                && toup(((char*)tokcstr.data)[1]) == 'X'))
                          || t == 'p' || t == 'P'))))
                break;
            t = c;
            PEEKC(c, p);
        }
        /* We add a trailing '\0' to ease parsing */
        cstr_ccat(&tokcstr, '\0');
        tokc.str.size = tokcstr.size;
        tokc.str.data = tokcstr.data;
        tok = TOK_PPNUM;
        break;

    case '.':
        /* special dot handling because it can also start a number */
        PEEKC(c, p);
        if (isnum(c)) {
            t = '.';
            goto parse_num;
        } else if ((isidnum_table['.' - CH_EOF] & IS_ID)
                   && (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))) {
            *--p = c = '.';
            goto parse_ident_fast;
        } else if (c == '.') {
            PEEKC(c, p);
            if (c == '.') {
                p++;
                tok = TOK_DOTS;
            } else {
                *--p = '.'; /* may underflow into file->unget[] */
                tok = '.';
            }
        } else {
            tok = '.';
        }
        break;
    case '\'':
    case '\"':
        is_long = 0;
    str_const:
        cstr_reset(&tokcstr);
        if (is_long)
            cstr_ccat(&tokcstr, 'L');
        cstr_ccat(&tokcstr, c);
        p = parse_pp_string(p, c, &tokcstr);
        cstr_ccat(&tokcstr, c);
        cstr_ccat(&tokcstr, '\0');
        tokc.str.size = tokcstr.size;
        tokc.str.data = tokcstr.data;
        tok = TOK_PPSTR;
        break;

    case '<':
        PEEKC(c, p);
        if (c == '=') {
            p++;
            tok = TOK_LE;
        } else if (c == '<') {
            PEEKC(c, p);
            if (c == '=') {
                p++;
                tok = TOK_A_SHL;
            } else {
                tok = TOK_SHL;
            }
        } else {
            tok = TOK_LT;
        }
        break;
    case '>':
        PEEKC(c, p);
        if (c == '=') {
            p++;
            tok = TOK_GE;
        } else if (c == '>') {
            PEEKC(c, p);
            if (c == '=') {
                p++;
                tok = TOK_A_SAR;
            } else {
                tok = TOK_SAR;
            }
        } else {
            tok = TOK_GT;
        }
        break;
        
    case '&':
        PEEKC(c, p);
        if (c == '&') {
            p++;
            tok = TOK_LAND;
        } else if (c == '=') {
            p++;
            tok = TOK_A_AND;
        } else {
            tok = '&';
        }
        break;
        
    case '|':
        PEEKC(c, p);
        if (c == '|') {
            p++;
            tok = TOK_LOR;
        } else if (c == '=') {
            p++;
            tok = TOK_A_OR;
        } else {
            tok = '|';
        }
        break;

    case '+':
        PEEKC(c, p);
        if (c == '+') {
            p++;
            tok = TOK_INC;
        } else if (c == '=') {
            p++;
            tok = TOK_A_ADD;
        } else {
            tok = '+';
        }
        break;
        
    case '-':
        PEEKC(c, p);
        if (c == '-') {
            p++;
            tok = TOK_DEC;
        } else if (c == '=') {
            p++;
            tok = TOK_A_SUB;
        } else if (c == '>') {
            p++;
            tok = TOK_ARROW;
        } else {
            tok = '-';
        }
        break;

    PARSE2('!', '!', '=', TOK_NE)
    PARSE2('=', '=', '=', TOK_EQ)
    PARSE2('*', '*', '=', TOK_A_MUL)
    PARSE2('%', '%', '=', TOK_A_MOD)
    PARSE2('^', '^', '=', TOK_A_XOR)
        
        /* comments or operator */
    case '/':
        PEEKC(c, p);
        if (c == '*') {
            p = parse_comment(p);
            /* comments replaced by a blank */
            tok = ' ';
            goto keep_tok_flags;
        } else if (c == '/') {
            p = parse_line_comment(p);
            tok = ' ';
            goto keep_tok_flags;
        } else if (c == '=') {
            p++;
            tok = TOK_A_DIV;
        } else {
            tok = '/';
        }
        break;
        
        /* simple tokens */
    case '(':
    case ')':
    case '[':
    case ']':
    case '{':
    case '}':
    case ',':
    case ';':
    case ':':
    case '?':
    case '~':
    case '@': /* only used in assembler */
    parse_simple:
        tok = c;
        p++;
        break;
    default:
        if (c >= 0x80 && c <= 0xFF) /* utf8 identifiers */
        goto parse_ident_fast;
        if (parse_flags & PARSE_FLAG_ASM_FILE)
            goto parse_simple;
        tcc_error("unrecognized character \\x%02x", c);
        break;
    }
    tok_flags = 0;
keep_tok_flags:
    file->buf_ptr = p;
#if defined(PARSE_DEBUG)
    printf("token = %d %s\n", tok, get_tok_str(tok, &tokc));
#endif
}

/* return next token without macro substitution. Can read input from
   macro_ptr buffer */
static void next_nomacro_spc(void)
{
    if (macro_ptr) {
    redo:
        tok = *macro_ptr;
        if (tok) {
            TOK_GET(&tok, &macro_ptr, &tokc);
            if (tok == TOK_LINENUM) {
                file->line_num = tokc.i;
                goto redo;
            }
        }
    } else {
        next_nomacro1();
    }
    //printf("token = %s\n", get_tok_str(tok, &tokc));
}

ST_FUNC void next_nomacro(void)
{
    do {
        next_nomacro_spc();
    } while (tok < 256 && (isidnum_table[tok - CH_EOF] & IS_SPC));
}
 

static void macro_subst(
    TokenString *tok_str,
    Sym **nested_list,
    const int *macro_str
    );

/* substitute arguments in replacement lists in macro_str by the values in
   args (field d) and return allocated string */
static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args)
{
    int t, t0, t1, spc;
    const int *st;
    Sym *s;
    CValue cval;
    TokenString str;
    CString cstr;

    tok_str_new(&str);
    t0 = t1 = 0;
    while(1) {
        TOK_GET(&t, &macro_str, &cval);
        if (!t)
            break;
        if (t == '#') {
            /* stringize */
            TOK_GET(&t, &macro_str, &cval);
            if (!t)
                goto bad_stringy;
            s = sym_find2(args, t);
            if (s) {
                cstr_new(&cstr);
                cstr_ccat(&cstr, '\"');
                st = s->d;
                spc = 0;
                while (*st >= 0) {
                    TOK_GET(&t, &st, &cval);
                    if (t != TOK_PLCHLDR
                     && t != TOK_NOSUBST
                     && 0 == check_space(t, &spc)) {
                        const char *s = get_tok_str(t, &cval);
                        while (*s) {
                            if (t == TOK_PPSTR && *s != '\'')
                                add_char(&cstr, *s);
                            else
                                cstr_ccat(&cstr, *s);
                            ++s;
                        }
                    }
                }
                cstr.size -= spc;
                cstr_ccat(&cstr, '\"');
                cstr_ccat(&cstr, '\0');
#ifdef PP_DEBUG
                printf("\nstringize: <%s>\n", (char *)cstr.data);
#endif
                /* add string */
                cval.str.size = cstr.size;
                cval.str.data = cstr.data;
                tok_str_add2(&str, TOK_PPSTR, &cval);
                cstr_free(&cstr);
            } else {
        bad_stringy:
                expect("macro parameter after '#'");
            }
        } else if (t >= TOK_IDENT) {
            s = sym_find2(args, t);
            if (s) {
                int l0 = str.len;
                st = s->d;
                /* if '##' is present before or after, no arg substitution */
                if (*macro_str == TOK_PPJOIN || t1 == TOK_PPJOIN) {
                    /* special case for var arg macros : ## eats the ','
                       if empty VA_ARGS variable. */
                    if (t1 == TOK_PPJOIN && t0 == ',' && gnu_ext && s->type.t) {
                        if (*st <= 0) {
                            /* suppress ',' '##' */
                            str.len -= 2;
                        } else {
                            /* suppress '##' and add variable */
                            str.len--;
                            goto add_var;
                        }
                    }
                } else {
            add_var:
            if (!s->next) {
            /* Expand arguments tokens and store them.  In most
               cases we could also re-expand each argument if
               used multiple times, but not if the argument
               contains the __COUNTER__ macro.  */
            TokenString str2;
            sym_push2(&s->next, s->v, s->type.t, 0);
            tok_str_new(&str2);
            macro_subst(&str2, nested_list, st);
            tok_str_add(&str2, 0);
            s->next->d = str2.str;
            }
            st = s->next->d;
                }
                for(;;) {
                    int t2;
                    TOK_GET(&t2, &st, &cval);
                    if (t2 <= 0)
                        break;
                    tok_str_add2(&str, t2, &cval);
                }
                if (str.len == l0) /* expanded to empty string */
                    tok_str_add(&str, TOK_PLCHLDR);
            } else {
                tok_str_add(&str, t);
            }
        } else {
            tok_str_add2(&str, t, &cval);
        }
        t0 = t1, t1 = t;
    }
    tok_str_add(&str, 0);
    return str.str;
}

static char const ab_month_name[12][4] =
{
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

static int paste_tokens(int t1, CValue *v1, int t2, CValue *v2)
{
    CString cstr;
    int n, ret = 1;

    cstr_new(&cstr);
    if (t1 != TOK_PLCHLDR)
        cstr_cat(&cstr, get_tok_str(t1, v1), -1);
    n = cstr.size;
    if (t2 != TOK_PLCHLDR)
        cstr_cat(&cstr, get_tok_str(t2, v2), -1);
    cstr_ccat(&cstr, '\0');

    tcc_open_bf(tcc_state, ":paste:", cstr.size);
    memcpy(file->buffer, cstr.data, cstr.size);
    tok_flags = 0;
    for (;;) {
        next_nomacro1();
        if (0 == *file->buf_ptr)
            break;
        if (is_space(tok))
            continue;
        tcc_warning("pasting \"%.*s\" and \"%s\" does not give a valid"
            " preprocessing token", n, cstr.data, (char*)cstr.data + n);
        ret = 0;
        break;
    }
    tcc_close();
    //printf("paste <%s>\n", (char*)cstr.data);
    cstr_free(&cstr);
    return ret;
}

/* handle the '##' operator. Return NULL if no '##' seen. Otherwise
   return the resulting string (which must be freed). */
static inline int *macro_twosharps(const int *ptr0)
{
    int t;
    CValue cval;
    TokenString macro_str1;
    int start_of_nosubsts = -1;
    const int *ptr;

    /* we search the first '##' */
    for (ptr = ptr0;;) {
        TOK_GET(&t, &ptr, &cval);
        if (t == TOK_PPJOIN)
            break;
        if (t == 0)
            return NULL;
    }

    tok_str_new(&macro_str1);

    //tok_print(" $$$", ptr0);
    for (ptr = ptr0;;) {
        TOK_GET(&t, &ptr, &cval);
        if (t == 0)
            break;
        if (t == TOK_PPJOIN)
            continue;
        while (*ptr == TOK_PPJOIN) {
            int t1; CValue cv1;
            /* given 'a##b', remove nosubsts preceding 'a' */
            if (start_of_nosubsts >= 0)
                macro_str1.len = start_of_nosubsts;
            /* given 'a##b', remove nosubsts preceding 'b' */
            while ((t1 = *++ptr) == TOK_NOSUBST)
                ;
            if (t1 && t1 != TOK_PPJOIN) {
                TOK_GET(&t1, &ptr, &cv1);
                if (t != TOK_PLCHLDR || t1 != TOK_PLCHLDR) {
                    if (paste_tokens(t, &cval, t1, &cv1)) {
                        t = tok, cval = tokc;
                    } else {
                        tok_str_add2(&macro_str1, t, &cval);
                        t = t1, cval = cv1;
                    }
                }
            }
        }
        if (t == TOK_NOSUBST) {
            if (start_of_nosubsts < 0)
                start_of_nosubsts = macro_str1.len;
        } else {
            start_of_nosubsts = -1;
        }
        tok_str_add2(&macro_str1, t, &cval);
    }
    tok_str_add(&macro_str1, 0);
    //tok_print(" ###", macro_str1.str);
    return macro_str1.str;
}

/* peek or read [ws_str == NULL] next token from function macro call,
   walking up macro levels up to the file if necessary */
static int next_argstream(Sym **nested_list, TokenString *ws_str)
{
    int t;
    const int *p;
    Sym *sa;

    for (;;) {
        if (macro_ptr) {
            p = macro_ptr, t = *p;
            if (ws_str) {
                while (is_space(t) || TOK_LINEFEED == t || TOK_PLCHLDR == t)
                    tok_str_add(ws_str, t), t = *++p;
            }
            if (t == 0) {
                end_macro();
                /* also, end of scope for nested defined symbol */
                sa = *nested_list;
                while (sa && sa->v == 0)
                    sa = sa->prev;
                if (sa)
                    sa->v = 0;
                continue;
            }
        } else {
            ch = handle_eob();
            if (ws_str) {
                while (is_space(ch) || ch == '\n' || ch == '/') {
                    if (ch == '/') {
                        int c;
                        uint8_t *p = file->buf_ptr;
                        PEEKC(c, p);
                        if (c == '*') {
                            p = parse_comment(p);
                            file->buf_ptr = p - 1;
                        } else if (c == '/') {
                            p = parse_line_comment(p);
                            file->buf_ptr = p - 1;
                        } else
                            break;
                        ch = ' ';
                    }
                    if (ch == '\n')
                        file->line_num++;
                    if (!(ch == '\f' || ch == '\v' || ch == '\r'))
                        tok_str_add(ws_str, ch);
                    cinp();
                }
            }
            t = ch;
        }

        if (ws_str)
            return t;
        next_nomacro_spc();
        return tok;
    }
}

/* do macro substitution of current token with macro 's' and add
   result to (tok_str,tok_len). 'nested_list' is the list of all
   macros we got inside to avoid recursing. Return non zero if no
   substitution needs to be done */
static int macro_subst_tok(
    TokenString *tok_str,
    Sym **nested_list,
    Sym *s)
{
    Sym *args, *sa, *sa1;
    int parlevel, t, t1, spc;
    TokenString str;
    char *cstrval;
    CValue cval;
    CString cstr;
    char buf[32];

    /* if symbol is a macro, prepare substitution */
    /* special macros */
    if (tok == TOK___LINE__ || tok == TOK___COUNTER__) {
        t = tok == TOK___LINE__ ? file->line_num : pp_counter++;
        snprintf(buf, sizeof(buf), "%d", t);
        cstrval = buf;
        t1 = TOK_PPNUM;
        goto add_cstr1;
    } else if (tok == TOK___FILE__) {
        cstrval = file->filename;
        goto add_cstr;
    } else if (tok == TOK___DATE__ || tok == TOK___TIME__) {
        time_t ti;
        struct tm *tm;

        time(&ti);
        tm = localtime(&ti);
        if (tok == TOK___DATE__) {
            snprintf(buf, sizeof(buf), "%s %2d %d", 
                     ab_month_name[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900);
        } else {
            snprintf(buf, sizeof(buf), "%02d:%02d:%02d", 
                     tm->tm_hour, tm->tm_min, tm->tm_sec);
        }
        cstrval = buf;
    add_cstr:
        t1 = TOK_STR;
    add_cstr1:
        cstr_new(&cstr);
        cstr_cat(&cstr, cstrval, 0);
        cval.str.size = cstr.size;
        cval.str.data = cstr.data;
        tok_str_add2(tok_str, t1, &cval);
        cstr_free(&cstr);
    } else if (s->d) {
        int saved_parse_flags = parse_flags;
    int *joined_str = NULL;
        int *mstr = s->d;

        if (s->type.t == MACRO_FUNC) {
            /* whitespace between macro name and argument list */
            TokenString ws_str;
            tok_str_new(&ws_str);

            spc = 0;
            parse_flags |= PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED
                | PARSE_FLAG_ACCEPT_STRAYS;

            /* get next token from argument stream */
            t = next_argstream(nested_list, &ws_str);
            if (t != '(') {
                /* not a macro substitution after all, restore the
                 * macro token plus all whitespace we've read.
                 * whitespace is intentionally not merged to preserve
                 * newlines. */
                parse_flags = saved_parse_flags;
                tok_str_add(tok_str, tok);
                if (parse_flags & PARSE_FLAG_SPACES) {
                    int i;
                    for (i = 0; i < ws_str.len; i++)
                        tok_str_add(tok_str, ws_str.str[i]);
                }
                tok_str_free_str(ws_str.str);
                return 0;
            } else {
                tok_str_free_str(ws_str.str);
            }
        do {
        next_nomacro(); /* eat '(' */
        } while (tok == TOK_PLCHLDR);

            /* argument macro */
            args = NULL;
            sa = s->next;
            /* NOTE: empty args are allowed, except if no args */
            for(;;) {
                do {
                    next_argstream(nested_list, NULL);
                } while (is_space(tok) || TOK_LINEFEED == tok);
    empty_arg:
                /* handle '()' case */
                if (!args && !sa && tok == ')')
                    break;
                if (!sa)
                    tcc_error("macro '%s' used with too many args",
                          get_tok_str(s->v, 0));
                tok_str_new(&str);
                parlevel = spc = 0;
                /* NOTE: non zero sa->t indicates VA_ARGS */
                while ((parlevel > 0 || 
                        (tok != ')' && 
                         (tok != ',' || sa->type.t)))) {
                    if (tok == TOK_EOF || tok == 0)
                        break;
                    if (tok == '(')
                        parlevel++;
                    else if (tok == ')')
                        parlevel--;
                    if (tok == TOK_LINEFEED)
                        tok = ' ';
                    if (!check_space(tok, &spc))
                        tok_str_add2(&str, tok, &tokc);
                    next_argstream(nested_list, NULL);
                }
                if (parlevel)
                    expect(")");
                str.len -= spc;
                tok_str_add(&str, -1);
                tok_str_add(&str, 0);
                sa1 = sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, 0);
                sa1->d = str.str;
                sa = sa->next;
                if (tok == ')') {
                    /* special case for gcc var args: add an empty
                       var arg argument if it is omitted */
                    if (sa && sa->type.t && gnu_ext)
                        goto empty_arg;
                    break;
                }
                if (tok != ',')
                    expect(",");
            }
            if (sa) {
                tcc_error("macro '%s' used with too few args",
                      get_tok_str(s->v, 0));
            }

            parse_flags = saved_parse_flags;

            /* now subst each arg */
            mstr = macro_arg_subst(nested_list, mstr, args);
            /* free memory */
            sa = args;
            while (sa) {
                sa1 = sa->prev;
                tok_str_free_str(sa->d);
                if (sa->next) {
                    tok_str_free_str(sa->next->d);
                    sym_free(sa->next);
                }
                sym_free(sa);
                sa = sa1;
            }
        }

        sym_push2(nested_list, s->v, 0, 0);
        parse_flags = saved_parse_flags;
        joined_str = macro_twosharps(mstr);
        macro_subst(tok_str, nested_list, joined_str ? joined_str : mstr);

        /* pop nested defined symbol */
        sa1 = *nested_list;
        *nested_list = sa1->prev;
        sym_free(sa1);
    if (joined_str)
        tok_str_free_str(joined_str);
        if (mstr != s->d)
            tok_str_free_str(mstr);
    }
    return 0;
}

/* do macro substitution of macro_str and add result to
   (tok_str,tok_len). 'nested_list' is the list of all macros we got
   inside to avoid recursing. */
static void macro_subst(
    TokenString *tok_str,
    Sym **nested_list,
    const int *macro_str
    )
{
    Sym *s;
    int t, spc, nosubst;
    CValue cval;
    
    spc = nosubst = 0;

    while (1) {
        TOK_GET(&t, &macro_str, &cval);
        if (t <= 0)
            break;

        if (t >= TOK_IDENT && 0 == nosubst) {
            s = define_find(t);
            if (s == NULL)
                goto no_subst;

            /* if nested substitution, do nothing */
            if (sym_find2(*nested_list, t)) {
                /* and mark it as TOK_NOSUBST, so it doesn't get subst'd again */
                tok_str_add2(tok_str, TOK_NOSUBST, NULL);
                goto no_subst;
            }

            {
                TokenString str;
                str.str = (int*)macro_str;
                begin_macro(&str, 2);

                tok = t;
                macro_subst_tok(tok_str, nested_list, s);

                if (str.alloc == 3) {
                    /* already finished by reading function macro arguments */
                    break;
                }

                macro_str = macro_ptr;
                end_macro ();
            }
            if (tok_str->len)
                spc = is_space(t = tok_str->str[tok_str->lastlen]);
        } else {
            if (t == '\\' && !(parse_flags & PARSE_FLAG_ACCEPT_STRAYS))
                tcc_error("stray '\\' in program");
no_subst:
            if (!check_space(t, &spc))
                tok_str_add2(tok_str, t, &cval);

            if (nosubst) {
                if (nosubst > 1 && (spc || (++nosubst == 3 && t == '(')))
                    continue;
                nosubst = 0;
            }
            if (t == TOK_NOSUBST)
                nosubst = 1;
        }
        /* GCC supports 'defined' as result of a macro substitution */
        if (t == TOK_DEFINED && pp_expr)
            nosubst = 2;
    }
}

/* return next token with macro substitution */
ST_FUNC void next(void)
{
 redo:
    if (parse_flags & PARSE_FLAG_SPACES)
        next_nomacro_spc();
    else
        next_nomacro();

    if (macro_ptr) {
        if (tok == TOK_NOSUBST || tok == TOK_PLCHLDR) {
        /* discard preprocessor markers */
            goto redo;
        } else if (tok == 0) {
            /* end of macro or unget token string */
            end_macro();
            goto redo;
        }
    } else if (tok >= TOK_IDENT && (parse_flags & PARSE_FLAG_PREPROCESS)) {
        Sym *s;
        /* if reading from file, try to substitute macros */
        s = define_find(tok);
        if (s) {
            Sym *nested_list = NULL;
            tokstr_buf.len = 0;
            macro_subst_tok(&tokstr_buf, &nested_list, s);
            tok_str_add(&tokstr_buf, 0);
            begin_macro(&tokstr_buf, 2);
            goto redo;
        }
    }
    /* convert preprocessor tokens into C tokens */
    if (tok == TOK_PPNUM) {
        if  (parse_flags & PARSE_FLAG_TOK_NUM)
            parse_number((char *)tokc.str.data);
    } else if (tok == TOK_PPSTR) {
        if (parse_flags & PARSE_FLAG_TOK_STR)
            parse_string((char *)tokc.str.data, tokc.str.size - 1);
    }
}

/* push back current token and set current token to 'last_tok'. Only
   identifier case handled for labels. */
ST_INLN void unget_tok(int last_tok)
{

    TokenString *str = tok_str_alloc();
    tok_str_add2(str, tok, &tokc);
    tok_str_add(str, 0);
    begin_macro(str, 1);
    tok = last_tok;
}

ST_FUNC void preprocess_start(TCCState *s1, int is_asm)
{
    CString cstr;
    int i;

    s1->include_stack_ptr = s1->include_stack;
    s1->ifdef_stack_ptr = s1->ifdef_stack;
    file->ifdef_stack_ptr = s1->ifdef_stack_ptr;
    pp_expr = 0;
    pp_counter = 0;
    pp_debug_tok = pp_debug_symv = 0;
    pp_once++;
    pvtop = vtop = vstack - 1;
    s1->pack_stack[0] = 0;
    s1->pack_stack_ptr = s1->pack_stack;

    set_idnum('$', s1->dollars_in_identifiers ? IS_ID : 0);
    set_idnum('.', is_asm ? IS_ID : 0);

    cstr_new(&cstr);
    cstr_cat(&cstr, "\"", -1);
    cstr_cat(&cstr, file->filename, -1);
    cstr_cat(&cstr, "\"", 0);
    tcc_define_symbol(s1, "__BASE_FILE__", cstr.data);

    cstr_reset(&cstr);
    for (i = 0; i < s1->nb_cmd_include_files; i++) {
        cstr_cat(&cstr, "#include \"", -1);
        cstr_cat(&cstr, s1->cmd_include_files[i], -1);
        cstr_cat(&cstr, "\"\n", -1);
    }
    if (cstr.size) {
        *s1->include_stack_ptr++ = file;
    tcc_open_bf(s1, "<command line>", cstr.size);
    memcpy(file->buffer, cstr.data, cstr.size);
    }
    cstr_free(&cstr);

    if (is_asm)
        tcc_define_symbol(s1, "__ASSEMBLER__", NULL);

    parse_flags = is_asm ? PARSE_FLAG_ASM_FILE : 0;
    tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
}

/* cleanup from error/setjmp */
ST_FUNC void preprocess_end(TCCState *s1)
{
    while (macro_stack)
        end_macro();
    macro_ptr = NULL;
}

ST_FUNC void tccpp_new(TCCState *s)
{
    int i, c;
    const char *p, *r;

    /* might be used in error() before preprocess_start() */
    s->include_stack_ptr = s->include_stack;
    s->ppfp = stdout;

    /* init isid table */
    for(i = CH_EOF; i<128; i++)
        set_idnum(i,
            is_space(i) ? IS_SPC
            : isid(i) ? IS_ID
            : isnum(i) ? IS_NUM
            : 0);

    for(i = 128; i<256; i++)
        set_idnum(i, IS_ID);

    /* init allocators */
    tal_new(&toksym_alloc, TOKSYM_TAL_LIMIT, TOKSYM_TAL_SIZE);
    tal_new(&tokstr_alloc, TOKSTR_TAL_LIMIT, TOKSTR_TAL_SIZE);
    tal_new(&cstr_alloc, CSTR_TAL_LIMIT, CSTR_TAL_SIZE);

    memset(hash_ident, 0, TOK_HASH_SIZE * sizeof(TokenSym *));
    cstr_new(&cstr_buf);
    cstr_realloc(&cstr_buf, STRING_MAX_SIZE);
    tok_str_new(&tokstr_buf);
    tok_str_realloc(&tokstr_buf, TOKSTR_MAX_SIZE);
    
    tok_ident = TOK_IDENT;
    p = tcc_keywords;
    while (*p) {
        r = p;
        for(;;) {
            c = *r++;
            if (c == '\0')
                break;
        }
        tok_alloc(p, r - p - 1);
        p = r;
    }
}

ST_FUNC void tccpp_delete(TCCState *s)
{
    int i, n;

    /* free -D and compiler defines */
    free_defines(NULL);

    /* free tokens */
    n = tok_ident - TOK_IDENT;
    for(i = 0; i < n; i++)
        tal_free(toksym_alloc, table_ident[i]);
    tcc_free(table_ident);
    table_ident = NULL;

    /* free static buffers */
    cstr_free(&tokcstr);
    cstr_free(&cstr_buf);
    cstr_free(&macro_equal_buf);
    tok_str_free_str(tokstr_buf.str);

    /* free allocators */
    tal_delete(toksym_alloc);
    toksym_alloc = NULL;
    tal_delete(tokstr_alloc);
    tokstr_alloc = NULL;
    tal_delete(cstr_alloc);
    cstr_alloc = NULL;
}

/* ------------------------------------------------------------------------- */
/* tcc -E [-P[1]] [-dD} support */

static void tok_print(const char *msg, const int *str)
{
    FILE *fp;
    int t, s = 0;
    CValue cval;

    fp = tcc_state->ppfp;
    fprintf(fp, "%s", msg);
    while (str) {
    TOK_GET(&t, &str, &cval);
    if (!t)
        break;
    fprintf(fp, " %s" + s, get_tok_str(t, &cval)), s = 1;
    }
    fprintf(fp, "\n");
}

static void pp_line(TCCState *s1, BufferedFile *f, int level)
{
    int d = f->line_num - f->line_ref;

    if (s1->dflag & 4)
    return;

    if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_NONE) {
        ;
    } else if (level == 0 && f->line_ref && d < 8) {
    while (d > 0)
        fputs("\n", s1->ppfp), --d;
    } else if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_STD) {
    fprintf(s1->ppfp, "#line %d \"%s\"\n", f->line_num, f->filename);
    } else {
    fprintf(s1->ppfp, "# %d \"%s\"%s\n", f->line_num, f->filename,
        level > 0 ? " 1" : level < 0 ? " 2" : "");
    }
    f->line_ref = f->line_num;
}

static void define_print(TCCState *s1, int v)
{
    FILE *fp;
    Sym *s;

    s = define_find(v);
    if (NULL == s || NULL == s->d)
        return;

    fp = s1->ppfp;
    fprintf(fp, "#define %s", get_tok_str(v, NULL));
    if (s->type.t == MACRO_FUNC) {
        Sym *a = s->next;
        fprintf(fp,"(");
        if (a)
            for (;;) {
                fprintf(fp,"%s", get_tok_str(a->v & ~SYM_FIELD, NULL));
                if (!(a = a->next))
                    break;
                fprintf(fp,",");
            }
        fprintf(fp,")");
    }
    tok_print("", s->d);
}

static void pp_debug_defines(TCCState *s1)
{
    int v, t;
    const char *vs;
    FILE *fp;

    t = pp_debug_tok;
    if (t == 0)
        return;

    file->line_num--;
    pp_line(s1, file, 0);
    file->line_ref = ++file->line_num;

    fp = s1->ppfp;
    v = pp_debug_symv;
    vs = get_tok_str(v, NULL);
    if (t == TOK_DEFINE) {
        define_print(s1, v);
    } else if (t == TOK_UNDEF) {
        fprintf(fp, "#undef %s\n", vs);
    } else if (t == TOK_push_macro) {
        fprintf(fp, "#pragma push_macro(\"%s\")\n", vs);
    } else if (t == TOK_pop_macro) {
        fprintf(fp, "#pragma pop_macro(\"%s\")\n", vs);
    }
    pp_debug_tok = 0;
}

static void pp_debug_builtins(TCCState *s1)
{
    int v;
    for (v = TOK_IDENT; v < tok_ident; ++v)
        define_print(s1, v);
}

/* Add a space between tokens a and b to avoid unwanted textual pasting */
static int pp_need_space(int a, int b)
{
    return 'E' == a ? '+' == b || '-' == b
        : '+' == a ? TOK_INC == b || '+' == b
        : '-' == a ? TOK_DEC == b || '-' == b
        : a >= TOK_IDENT ? b >= TOK_IDENT
    : a == TOK_PPNUM ? b >= TOK_IDENT
        : 0;
}

/* maybe hex like 0x1e */
static int pp_check_he0xE(int t, const char *p)
{
    if (t == TOK_PPNUM && toup(strchr(p, 0)[-1]) == 'E')
        return 'E';
    return t;
}

/* Preprocess the current file */
ST_FUNC int tcc_preprocess(TCCState *s1)
{
    BufferedFile **iptr;
    int token_seen, spcs, level;
    const char *p;
    char white[400];

    parse_flags = PARSE_FLAG_PREPROCESS
                | (parse_flags & PARSE_FLAG_ASM_FILE)
                | PARSE_FLAG_LINEFEED
                | PARSE_FLAG_SPACES
                | PARSE_FLAG_ACCEPT_STRAYS
                ;
    /* Credits to Fabrice Bellard's initial revision to demonstrate its
       capability to compile and run itself, provided all numbers are
       given as decimals. tcc -E -P10 will do. */
    if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_P10)
        parse_flags |= PARSE_FLAG_TOK_NUM, s1->Pflag = 1;

#ifdef PP_BENCH
    /* for PP benchmarks */
    do next(); while (tok != TOK_EOF);
    return 0;
#endif

    if (s1->dflag & 1) {
        pp_debug_builtins(s1);
        s1->dflag &= ~1;
    }

    token_seen = TOK_LINEFEED, spcs = 0;
    pp_line(s1, file, 0);
    for (;;) {
        iptr = s1->include_stack_ptr;
        next();
        if (tok == TOK_EOF)
            break;

        level = s1->include_stack_ptr - iptr;
        if (level) {
            if (level > 0)
                pp_line(s1, *iptr, 0);
            pp_line(s1, file, level);
        }
        if (s1->dflag & 7) {
            pp_debug_defines(s1);
            if (s1->dflag & 4)
                continue;
        }

        if (is_space(tok)) {
            if (spcs < sizeof white - 1)
                white[spcs++] = tok;
            continue;
        } else if (tok == TOK_LINEFEED) {
            spcs = 0;
            if (token_seen == TOK_LINEFEED)
                continue;
            ++file->line_ref;
        } else if (token_seen == TOK_LINEFEED) {
            pp_line(s1, file, 0);
        } else if (spcs == 0 && pp_need_space(token_seen, tok)) {
            white[spcs++] = ' ';
        }

        white[spcs] = 0, fputs(white, s1->ppfp), spcs = 0;
        fputs(p = get_tok_str(tok, &tokc), s1->ppfp);
        token_seen = pp_check_he0xE(tok, p);
    }
    return 0;
}

/* ------------------------------------------------------------------------- */

https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tcctok.h

File: ../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tcctok.h used in: Step 488
/* keywords */
     DEF(TOK_INT, "int")
     DEF(TOK_VOID, "void")
     DEF(TOK_CHAR, "char")
     DEF(TOK_IF, "if")
     DEF(TOK_ELSE, "else")
     DEF(TOK_WHILE, "while")
     DEF(TOK_BREAK, "break")
     DEF(TOK_RETURN, "return")
     DEF(TOK_FOR, "for")
     DEF(TOK_EXTERN, "extern")
     DEF(TOK_STATIC, "static")
     DEF(TOK_UNSIGNED, "unsigned")
     DEF(TOK_GOTO, "goto")
     DEF(TOK_DO, "do")
     DEF(TOK_CONTINUE, "continue")
     DEF(TOK_SWITCH, "switch")
     DEF(TOK_CASE, "case")

     DEF(TOK_CONST1, "const")
     DEF(TOK_CONST2, "__const") /* gcc keyword */
     DEF(TOK_CONST3, "__const__") /* gcc keyword */
     DEF(TOK_VOLATILE1, "volatile")
     DEF(TOK_VOLATILE2, "__volatile") /* gcc keyword */
     DEF(TOK_VOLATILE3, "__volatile__") /* gcc keyword */
     DEF(TOK_LONG, "long")
     DEF(TOK_REGISTER, "register")
     DEF(TOK_SIGNED1, "signed")
     DEF(TOK_SIGNED2, "__signed") /* gcc keyword */
     DEF(TOK_SIGNED3, "__signed__") /* gcc keyword */
     DEF(TOK_AUTO, "auto")
     DEF(TOK_INLINE1, "inline")
     DEF(TOK_INLINE2, "__inline") /* gcc keyword */
     DEF(TOK_INLINE3, "__inline__") /* gcc keyword */
     DEF(TOK_RESTRICT1, "restrict")
     DEF(TOK_RESTRICT2, "__restrict")
     DEF(TOK_RESTRICT3, "__restrict__")
     DEF(TOK_EXTENSION, "__extension__") /* gcc keyword */

     DEF(TOK_GENERIC, "_Generic")

     DEF(TOK_FLOAT, "float")
     DEF(TOK_DOUBLE, "double")
     DEF(TOK_BOOL, "_Bool")
     DEF(TOK_SHORT, "short")
     DEF(TOK_STRUCT, "struct")
     DEF(TOK_UNION, "union")
     DEF(TOK_TYPEDEF, "typedef")
     DEF(TOK_DEFAULT, "default")
     DEF(TOK_ENUM, "enum")
     DEF(TOK_SIZEOF, "sizeof")
     DEF(TOK_ATTRIBUTE1, "__attribute")
     DEF(TOK_ATTRIBUTE2, "__attribute__")
     DEF(TOK_ALIGNOF1, "__alignof")
     DEF(TOK_ALIGNOF2, "__alignof__")
     DEF(TOK_TYPEOF1, "typeof")
     DEF(TOK_TYPEOF2, "__typeof")
     DEF(TOK_TYPEOF3, "__typeof__")
     DEF(TOK_LABEL, "__label__")
     DEF(TOK_ASM1, "asm")
     DEF(TOK_ASM2, "__asm")
     DEF(TOK_ASM3, "__asm__")

#ifdef TCC_TARGET_ARM64
     DEF(TOK_UINT128, "__uint128_t")
#endif

/*********************************************************************/
/* the following are not keywords. They are included to ease parsing */
/* preprocessor only */
     DEF(TOK_DEFINE, "define")
     DEF(TOK_INCLUDE, "include")
     DEF(TOK_INCLUDE_NEXT, "include_next")
     DEF(TOK_IFDEF, "ifdef")
     DEF(TOK_IFNDEF, "ifndef")
     DEF(TOK_ELIF, "elif")
     DEF(TOK_ENDIF, "endif")
     DEF(TOK_DEFINED, "defined")
     DEF(TOK_UNDEF, "undef")
     DEF(TOK_ERROR, "error")
     DEF(TOK_WARNING, "warning")
     DEF(TOK_LINE, "line")
     DEF(TOK_PRAGMA, "pragma")
     DEF(TOK___LINE__, "__LINE__")
     DEF(TOK___FILE__, "__FILE__")
     DEF(TOK___DATE__, "__DATE__")
     DEF(TOK___TIME__, "__TIME__")
     DEF(TOK___FUNCTION__, "__FUNCTION__")
     DEF(TOK___VA_ARGS__, "__VA_ARGS__")
     DEF(TOK___COUNTER__, "__COUNTER__")

/* special identifiers */
     DEF(TOK___FUNC__, "__func__")

/* special floating point values */
     DEF(TOK___NAN__, "__nan__")
     DEF(TOK___SNAN__, "__snan__")
     DEF(TOK___INF__, "__inf__")

/* attribute identifiers */
/* XXX: handle all tokens generically since speed is not critical */
     DEF(TOK_SECTION1, "section")
     DEF(TOK_SECTION2, "__section__")
     DEF(TOK_ALIGNED1, "aligned")
     DEF(TOK_ALIGNED2, "__aligned__")
     DEF(TOK_PACKED1, "packed")
     DEF(TOK_PACKED2, "__packed__")
     DEF(TOK_WEAK1, "weak")
     DEF(TOK_WEAK2, "__weak__")
     DEF(TOK_ALIAS1, "alias")
     DEF(TOK_ALIAS2, "__alias__")
     DEF(TOK_UNUSED1, "unused")
     DEF(TOK_UNUSED2, "__unused__")
     DEF(TOK_CDECL1, "cdecl")
     DEF(TOK_CDECL2, "__cdecl")
     DEF(TOK_CDECL3, "__cdecl__")
     DEF(TOK_STDCALL1, "stdcall")
     DEF(TOK_STDCALL2, "__stdcall")
     DEF(TOK_STDCALL3, "__stdcall__")
     DEF(TOK_FASTCALL1, "fastcall")
     DEF(TOK_FASTCALL2, "__fastcall")
     DEF(TOK_FASTCALL3, "__fastcall__")
     DEF(TOK_REGPARM1, "regparm")
     DEF(TOK_REGPARM2, "__regparm__")

     DEF(TOK_MODE, "__mode__")
     DEF(TOK_MODE_QI, "__QI__")
     DEF(TOK_MODE_DI, "__DI__")
     DEF(TOK_MODE_HI, "__HI__")
     DEF(TOK_MODE_SI, "__SI__")
     DEF(TOK_MODE_word, "__word__")

     DEF(TOK_DLLEXPORT, "dllexport")
     DEF(TOK_DLLIMPORT, "dllimport")
     DEF(TOK_NORETURN1, "noreturn")
     DEF(TOK_NORETURN2, "__noreturn__")
     DEF(TOK_VISIBILITY1, "visibility")
     DEF(TOK_VISIBILITY2, "__visibility__")

     DEF(TOK_builtin_types_compatible_p, "__builtin_types_compatible_p")
     DEF(TOK_builtin_choose_expr, "__builtin_choose_expr")
     DEF(TOK_builtin_constant_p, "__builtin_constant_p")
     DEF(TOK_builtin_frame_address, "__builtin_frame_address")
     DEF(TOK_builtin_return_address, "__builtin_return_address")
     DEF(TOK_builtin_expect, "__builtin_expect")
     /*DEF(TOK_builtin_va_list, "__builtin_va_list")*/
#if defined TCC_TARGET_PE && defined TCC_TARGET_X86_64
     DEF(TOK_builtin_va_start, "__builtin_va_start")
#elif defined TCC_TARGET_X86_64
     DEF(TOK_builtin_va_arg_types, "__builtin_va_arg_types")
#elif defined TCC_TARGET_ARM64
     DEF(TOK___va_start, "__va_start")
     DEF(TOK___va_arg, "__va_arg")
#endif

/* pragma */
     DEF(TOK_pack, "pack")
#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_X86_64)
     /* already defined for assembler */
     DEF(TOK_ASM_push, "push")
     DEF(TOK_ASM_pop, "pop")
#endif
     DEF(TOK_comment, "comment")
     DEF(TOK_lib, "lib")
     DEF(TOK_push_macro, "push_macro")
     DEF(TOK_pop_macro, "pop_macro")
     DEF(TOK_once, "once")
     DEF(TOK_option, "option")

/* builtin functions or variables */
#ifndef TCC_ARM_EABI
     DEF(TOK_memcpy, "memcpy")
     DEF(TOK_memmove, "memmove")
     DEF(TOK_memset, "memset")
     DEF(TOK___divdi3, "__divdi3")
     DEF(TOK___moddi3, "__moddi3")
     DEF(TOK___udivdi3, "__udivdi3")
     DEF(TOK___umoddi3, "__umoddi3")
     DEF(TOK___ashrdi3, "__ashrdi3")
     DEF(TOK___lshrdi3, "__lshrdi3")
     DEF(TOK___ashldi3, "__ashldi3")
     DEF(TOK___floatundisf, "__floatundisf")
     DEF(TOK___floatundidf, "__floatundidf")
# ifndef TCC_ARM_VFP
     DEF(TOK___floatundixf, "__floatundixf")
     DEF(TOK___fixunsxfdi, "__fixunsxfdi")
# endif
     DEF(TOK___fixunssfdi, "__fixunssfdi")
     DEF(TOK___fixunsdfdi, "__fixunsdfdi")
#endif

#if defined TCC_TARGET_ARM
# ifdef TCC_ARM_EABI
     DEF(TOK_memcpy, "__aeabi_memcpy")
     DEF(TOK_memcpy4, "__aeabi_memcpy4")
     DEF(TOK_memcpy8, "__aeabi_memcpy8")
     DEF(TOK_memmove, "__aeabi_memmove")
     DEF(TOK_memset, "__aeabi_memset")
     DEF(TOK___aeabi_ldivmod, "__aeabi_ldivmod")
     DEF(TOK___aeabi_uldivmod, "__aeabi_uldivmod")
     DEF(TOK___aeabi_idivmod, "__aeabi_idivmod")
     DEF(TOK___aeabi_uidivmod, "__aeabi_uidivmod")
     DEF(TOK___divsi3, "__aeabi_idiv")
     DEF(TOK___udivsi3, "__aeabi_uidiv")
     DEF(TOK___floatdisf, "__aeabi_l2f")
     DEF(TOK___floatdidf, "__aeabi_l2d")
     DEF(TOK___fixsfdi, "__aeabi_f2lz")
     DEF(TOK___fixdfdi, "__aeabi_d2lz")
     DEF(TOK___ashrdi3, "__aeabi_lasr")
     DEF(TOK___lshrdi3, "__aeabi_llsr")
     DEF(TOK___ashldi3, "__aeabi_llsl")
     DEF(TOK___floatundisf, "__aeabi_ul2f")
     DEF(TOK___floatundidf, "__aeabi_ul2d")
     DEF(TOK___fixunssfdi, "__aeabi_f2ulz")
     DEF(TOK___fixunsdfdi, "__aeabi_d2ulz")
# else
     DEF(TOK___modsi3, "__modsi3")
     DEF(TOK___umodsi3, "__umodsi3")
     DEF(TOK___divsi3, "__divsi3")
     DEF(TOK___udivsi3, "__udivsi3")
     DEF(TOK___floatdisf, "__floatdisf")
     DEF(TOK___floatdidf, "__floatdidf")
#  ifndef TCC_ARM_VFP
     DEF(TOK___floatdixf, "__floatdixf")
     DEF(TOK___fixunssfsi, "__fixunssfsi")
     DEF(TOK___fixunsdfsi, "__fixunsdfsi")
     DEF(TOK___fixunsxfsi, "__fixunsxfsi")
     DEF(TOK___fixxfdi, "__fixxfdi")
#  endif
     DEF(TOK___fixsfdi, "__fixsfdi")
     DEF(TOK___fixdfdi, "__fixdfdi")
# endif
#endif

#if defined TCC_TARGET_C67
     DEF(TOK__divi, "_divi")
     DEF(TOK__divu, "_divu")
     DEF(TOK__divf, "_divf")
     DEF(TOK__divd, "_divd")
     DEF(TOK__remi, "_remi")
     DEF(TOK__remu, "_remu")
#endif

#if defined TCC_TARGET_I386
     DEF(TOK___fixsfdi, "__fixsfdi")
     DEF(TOK___fixdfdi, "__fixdfdi")
     DEF(TOK___fixxfdi, "__fixxfdi")
#endif

#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
     DEF(TOK_alloca, "alloca")
#endif

#if defined TCC_TARGET_PE
     DEF(TOK___chkstk, "__chkstk")
#endif
#ifdef TCC_TARGET_ARM64
     DEF(TOK___arm64_clear_cache, "__arm64_clear_cache")
     DEF(TOK___addtf3, "__addtf3")
     DEF(TOK___subtf3, "__subtf3")
     DEF(TOK___multf3, "__multf3")
     DEF(TOK___divtf3, "__divtf3")
     DEF(TOK___extendsftf2, "__extendsftf2")
     DEF(TOK___extenddftf2, "__extenddftf2")
     DEF(TOK___trunctfsf2, "__trunctfsf2")
     DEF(TOK___trunctfdf2, "__trunctfdf2")
     DEF(TOK___fixtfsi, "__fixtfsi")
     DEF(TOK___fixtfdi, "__fixtfdi")
     DEF(TOK___fixunstfsi, "__fixunstfsi")
     DEF(TOK___fixunstfdi, "__fixunstfdi")
     DEF(TOK___floatsitf, "__floatsitf")
     DEF(TOK___floatditf, "__floatditf")
     DEF(TOK___floatunsitf, "__floatunsitf")
     DEF(TOK___floatunditf, "__floatunditf")
     DEF(TOK___eqtf2, "__eqtf2")
     DEF(TOK___netf2, "__netf2")
     DEF(TOK___lttf2, "__lttf2")
     DEF(TOK___letf2, "__letf2")
     DEF(TOK___gttf2, "__gttf2")
     DEF(TOK___getf2, "__getf2")
#endif

/* bound checking symbols */
#ifdef CONFIG_TCC_BCHECK
     DEF(TOK___bound_ptr_add, "__bound_ptr_add")
     DEF(TOK___bound_ptr_indir1, "__bound_ptr_indir1")
     DEF(TOK___bound_ptr_indir2, "__bound_ptr_indir2")
     DEF(TOK___bound_ptr_indir4, "__bound_ptr_indir4")
     DEF(TOK___bound_ptr_indir8, "__bound_ptr_indir8")
     DEF(TOK___bound_ptr_indir12, "__bound_ptr_indir12")
     DEF(TOK___bound_ptr_indir16, "__bound_ptr_indir16")
     DEF(TOK___bound_main_arg, "__bound_main_arg")
     DEF(TOK___bound_local_new, "__bound_local_new")
     DEF(TOK___bound_local_delete, "__bound_local_delete")
# ifdef TCC_TARGET_PE
     DEF(TOK_malloc, "malloc")
     DEF(TOK_free, "free")
     DEF(TOK_realloc, "realloc")
     DEF(TOK_memalign, "memalign")
     DEF(TOK_calloc, "calloc")
# endif
     DEF(TOK_strlen, "strlen")
     DEF(TOK_strcpy, "strcpy")
#endif

/* Tiny Assembler */
 DEF_ASMDIR(byte)              /* must be first directive */
 DEF_ASMDIR(word)
 DEF_ASMDIR(align)
 DEF_ASMDIR(balign)
 DEF_ASMDIR(p2align)
 DEF_ASMDIR(set)
 DEF_ASMDIR(skip)
 DEF_ASMDIR(space)
 DEF_ASMDIR(string)
 DEF_ASMDIR(asciz)
 DEF_ASMDIR(ascii)
 DEF_ASMDIR(file)
 DEF_ASMDIR(globl)
 DEF_ASMDIR(global)
 DEF_ASMDIR(weak)
 DEF_ASMDIR(hidden)
 DEF_ASMDIR(ident)
 DEF_ASMDIR(size)
 DEF_ASMDIR(type)
 DEF_ASMDIR(text)
 DEF_ASMDIR(data)
 DEF_ASMDIR(bss)
 DEF_ASMDIR(previous)
 DEF_ASMDIR(pushsection)
 DEF_ASMDIR(popsection)
 DEF_ASMDIR(fill)
 DEF_ASMDIR(rept)
 DEF_ASMDIR(endr)
 DEF_ASMDIR(org)
 DEF_ASMDIR(quad)
#if defined(TCC_TARGET_I386)
 DEF_ASMDIR(code16)
 DEF_ASMDIR(code32)
#elif defined(TCC_TARGET_X86_64)
 DEF_ASMDIR(code64)
#endif
 DEF_ASMDIR(short)
 DEF_ASMDIR(long)
 DEF_ASMDIR(int)
 DEF_ASMDIR(section)            /* must be last directive */

#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
#include "i386-tok.h"
#endif

https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tccgen.c

File: ../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tccgen.c used in: Step 488
/*
 *  TCC - Tiny C Compiler
 * 
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "tcc.h"

/********************************************************/
/* global variables */

/* loc : local variable index
   ind : output code index
   rsym: return symbol
   anon_sym: anonymous symbol index
*/
ST_DATA int rsym, anon_sym, ind, loc;

ST_DATA Sym *sym_free_first;
ST_DATA void **sym_pools;
ST_DATA int nb_sym_pools;

ST_DATA Sym *global_stack;
ST_DATA Sym *local_stack;
ST_DATA Sym *define_stack;
ST_DATA Sym *global_label_stack;
ST_DATA Sym *local_label_stack;
static int local_scope;
static int in_sizeof;
static int section_sym;

ST_DATA int vlas_in_scope; /* number of VLAs that are currently in scope */
ST_DATA int vla_sp_root_loc; /* vla_sp_loc for SP before any VLAs were pushed */
ST_DATA int vla_sp_loc; /* Pointer to variable holding location to store stack pointer on the stack when modifying stack pointer */

ST_DATA SValue __vstack[1+VSTACK_SIZE], *vtop, *pvtop;

ST_DATA int const_wanted; /* true if constant wanted */
ST_DATA int nocode_wanted; /* no code generation wanted */
#define NODATA_WANTED (nocode_wanted > 0) /* no static data output wanted either */
#define STATIC_DATA_WANTED (nocode_wanted & 0xC0000000) /* only static data output */
ST_DATA int global_expr;  /* true if compound literals must be allocated globally (used during initializers parsing */
ST_DATA CType func_vt; /* current function return type (used by return instruction) */
ST_DATA int func_var; /* true if current function is variadic (used by return instruction) */
ST_DATA int func_vc;
ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
ST_DATA const char *funcname;
ST_DATA int g_debug;

ST_DATA CType char_pointer_type, func_old_type, int_type, size_type, ptrdiff_type;

ST_DATA struct switch_t {
    struct case_t {
        int64_t v1, v2;
    int sym;
    } **p; int n; /* list of case ranges */
    int def_sym; /* default symbol */
} *cur_switch; /* current switch */

/* ------------------------------------------------------------------------- */

static void gen_cast(CType *type);
static void gen_cast_s(int t);
static inline CType *pointed_type(CType *type);
static int is_compatible_types(CType *type1, CType *type2);
static int parse_btype(CType *type, AttributeDef *ad);
static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td);
static void parse_expr_type(CType *type);
static void init_putv(CType *type, Section *sec, unsigned long c);
static void decl_initializer(CType *type, Section *sec, unsigned long c, int first, int size_only);
static void block(int *bsym, int *csym, int is_expr);
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, int scope);
static void decl(int l);
static int decl0(int l, int is_for_loop_init, Sym *);
static void expr_eq(void);
static void vla_runtime_type_size(CType *type, int *a);
static void vla_sp_restore(void);
static void vla_sp_restore_root(void);
static int is_compatible_unqualified_types(CType *type1, CType *type2);
static inline int64_t expr_const64(void);
static void vpush64(int ty, unsigned long long v);
static void vpush(CType *type);
static int gvtst(int inv, int t);
static void gen_inline_functions(TCCState *s);
static void skip_or_save_block(TokenString **str);
static void gv_dup(void);

ST_INLN int is_float(int t)
{
    int bt;
    bt = t & VT_BTYPE;
    return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT || bt == VT_QFLOAT;
}

/* we use our own 'finite' function to avoid potential problems with
   non standard math libs */
/* XXX: endianness dependent */
ST_FUNC int ieee_finite(double d)
{
    int p[4];
    memcpy(p, &d, sizeof(double));
    return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31;
}

/* compiling intel long double natively */
#if (defined __i386__ || defined __x86_64__) \
    && (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64)
# define TCC_IS_NATIVE_387
#endif

ST_FUNC void test_lvalue(void)
{
    if (!(vtop->r & VT_LVAL))
        expect("lvalue");
}

ST_FUNC void check_vstack(void)
{
    if (pvtop != vtop)
        tcc_error("internal compiler error: vstack leak (%d)", vtop - pvtop);
}

/* ------------------------------------------------------------------------- */
/* vstack debugging aid */

#if 0
void pv (const char *lbl, int a, int b)
{
    int i;
    for (i = a; i < a + b; ++i) {
        SValue *p = &vtop[-i];
        printf("%s vtop[-%d] : type.t:%04x  r:%04x  r2:%04x  c.i:%d\n",
            lbl, i, p->type.t, p->r, p->r2, (int)p->c.i);
    }
}
#endif

/* ------------------------------------------------------------------------- */
/* start of translation unit info */
ST_FUNC void tcc_debug_start(TCCState *s1)
{
    if (s1->do_debug) {
        char buf[512];

        /* file info: full path + filename */
        section_sym = put_elf_sym(symtab_section, 0, 0,
                                  ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0,
                                  text_section->sh_num, NULL);
        getcwd(buf, sizeof(buf));
#ifdef _WIN32
        normalize_slashes(buf);
#endif
        pstrcat(buf, sizeof(buf), "/");
        put_stabs_r(buf, N_SO, 0, 0,
                    text_section->data_offset, text_section, section_sym);
        put_stabs_r(file->filename, N_SO, 0, 0,
                    text_section->data_offset, text_section, section_sym);
        last_ind = 0;
        last_line_num = 0;
    }

    /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
       symbols can be safely used */
    put_elf_sym(symtab_section, 0, 0,
                ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
                SHN_ABS, file->filename);
}

/* put end of translation unit info */
ST_FUNC void tcc_debug_end(TCCState *s1)
{
    if (!s1->do_debug)
        return;
    put_stabs_r(NULL, N_SO, 0, 0,
        text_section->data_offset, text_section, section_sym);

}

/* generate line number info */
ST_FUNC void tcc_debug_line(TCCState *s1)
{
    if (!s1->do_debug)
        return;
    if ((last_line_num != file->line_num || last_ind != ind)) {
        put_stabn(N_SLINE, 0, file->line_num, ind - func_ind);
        last_ind = ind;
        last_line_num = file->line_num;
    }
}

/* put function symbol */
ST_FUNC void tcc_debug_funcstart(TCCState *s1, Sym *sym)
{
    char buf[512];

    if (!s1->do_debug)
        return;

    /* stabs info */
    /* XXX: we put here a dummy type */
    snprintf(buf, sizeof(buf), "%s:%c1",
             funcname, sym->type.t & VT_STATIC ? 'f' : 'F');
    put_stabs_r(buf, N_FUN, 0, file->line_num, 0,
                cur_text_section, sym->c);
    /* //gr gdb wants a line at the function */
    put_stabn(N_SLINE, 0, file->line_num, 0);

    last_ind = 0;
    last_line_num = 0;
}

/* put function size */
ST_FUNC void tcc_debug_funcend(TCCState *s1, int size)
{
    if (!s1->do_debug)
        return;
    put_stabn(N_FUN, 0, 0, size);
}

/* ------------------------------------------------------------------------- */
ST_FUNC int tccgen_compile(TCCState *s1)
{
    cur_text_section = NULL;
    funcname = "";
    anon_sym = SYM_FIRST_ANOM;
    section_sym = 0;
    const_wanted = 0;
    nocode_wanted = 0x80000000;

    /* define some often used types */
    int_type.t = VT_INT;
    char_pointer_type.t = VT_BYTE;
    mk_pointer(&char_pointer_type);
#if PTR_SIZE == 4
    size_type.t = VT_INT | VT_UNSIGNED;
    ptrdiff_type.t = VT_INT;
#elif LONG_SIZE == 4
    size_type.t = VT_LLONG | VT_UNSIGNED;
    ptrdiff_type.t = VT_LLONG;
#else
    size_type.t = VT_LONG | VT_LLONG | VT_UNSIGNED;
    ptrdiff_type.t = VT_LONG | VT_LLONG;
#endif
    func_old_type.t = VT_FUNC;
    func_old_type.ref = sym_push(SYM_FIELD, &int_type, 0, 0);
    func_old_type.ref->f.func_call = FUNC_CDECL;
    func_old_type.ref->f.func_type = FUNC_OLD;

    tcc_debug_start(s1);

#ifdef TCC_TARGET_ARM
    arm_init(s1);
#endif

#ifdef INC_DEBUG
    printf("%s: **** new file\n", file->filename);
#endif

    parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM | PARSE_FLAG_TOK_STR;
    next();
    decl(VT_CONST);
    gen_inline_functions(s1);
    check_vstack();
    /* end of translation unit info */
    tcc_debug_end(s1);
    return 0;
}

/* ------------------------------------------------------------------------- */
ST_FUNC ElfSym *elfsym(Sym *s)
{
  if (!s || !s->c)
    return NULL;
  return &((ElfSym *)symtab_section->data)[s->c];
}

/* apply storage attributes to Elf symbol */
ST_FUNC void update_storage(Sym *sym)
{
    ElfSym *esym;
    int sym_bind, old_sym_bind;

    esym = elfsym(sym);
    if (!esym)
        return;

    if (sym->a.visibility)
        esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
            | sym->a.visibility;

    if (sym->type.t & VT_STATIC)
        sym_bind = STB_LOCAL;
    else if (sym->a.weak)
        sym_bind = STB_WEAK;
    else
        sym_bind = STB_GLOBAL;
    old_sym_bind = ELFW(ST_BIND)(esym->st_info);
    if (sym_bind != old_sym_bind) {
        esym->st_info = ELFW(ST_INFO)(sym_bind, ELFW(ST_TYPE)(esym->st_info));
    }

#ifdef TCC_TARGET_PE
    if (sym->a.dllimport)
        esym->st_other |= ST_PE_IMPORT;
    if (sym->a.dllexport)
        esym->st_other |= ST_PE_EXPORT;
#endif

#if 0
    printf("storage %s: bind=%c vis=%d exp=%d imp=%d\n",
        get_tok_str(sym->v, NULL),
        sym_bind == STB_WEAK ? 'w' : sym_bind == STB_LOCAL ? 'l' : 'g',
        sym->a.visibility,
        sym->a.dllexport,
        sym->a.dllimport
        );
#endif
}

/* ------------------------------------------------------------------------- */
/* update sym->c so that it points to an external symbol in section
   'section' with value 'value' */

ST_FUNC void put_extern_sym2(Sym *sym, int sh_num,
                            addr_t value, unsigned long size,
                            int can_add_underscore)
{
    int sym_type, sym_bind, info, other, t;
    ElfSym *esym;
    const char *name;
    char buf1[256];
#ifdef CONFIG_TCC_BCHECK
    char buf[32];
#endif

    if (!sym->c) {
        name = get_tok_str(sym->v, NULL);
#ifdef CONFIG_TCC_BCHECK
        if (tcc_state->do_bounds_check) {
            /* XXX: avoid doing that for statics ? */
            /* if bound checking is activated, we change some function
               names by adding the "__bound" prefix */
            switch(sym->v) {
#ifdef TCC_TARGET_PE
            /* XXX: we rely only on malloc hooks */
            case TOK_malloc:
            case TOK_free:
            case TOK_realloc:
            case TOK_memalign:
            case TOK_calloc:
#endif
            case TOK_memcpy:
            case TOK_memmove:
            case TOK_memset:
            case TOK_strlen:
            case TOK_strcpy:
            case TOK_alloca:
                strcpy(buf, "__bound_");
                strcat(buf, name);
                name = buf;
                break;
            }
        }
#endif
        t = sym->type.t;
        if ((t & VT_BTYPE) == VT_FUNC) {
            sym_type = STT_FUNC;
        } else if ((t & VT_BTYPE) == VT_VOID) {
            sym_type = STT_NOTYPE;
        } else {
            sym_type = STT_OBJECT;
        }
        if (t & VT_STATIC)
            sym_bind = STB_LOCAL;
        else
            sym_bind = STB_GLOBAL;
        other = 0;
#ifdef TCC_TARGET_PE
        if (sym_type == STT_FUNC && sym->type.ref) {
            Sym *ref = sym->type.ref;
            if (ref->f.func_call == FUNC_STDCALL && can_add_underscore) {
                sprintf(buf1, "_%s@%d", name, ref->f.func_args * PTR_SIZE);
                name = buf1;
                other |= ST_PE_STDCALL;
                can_add_underscore = 0;
            }
        }
#endif
        if (tcc_state->leading_underscore && can_add_underscore) {
            buf1[0] = '_';
            pstrcpy(buf1 + 1, sizeof(buf1) - 1, name);
            name = buf1;
        }
        if (sym->asm_label)
            name = get_tok_str(sym->asm_label, NULL);
        info = ELFW(ST_INFO)(sym_bind, sym_type);
        sym->c = put_elf_sym(symtab_section, value, size, info, other, sh_num, name);
    } else {
        esym = elfsym(sym);
        esym->st_value = value;
        esym->st_size = size;
        esym->st_shndx = sh_num;
    }
    update_storage(sym);
}

ST_FUNC void put_extern_sym(Sym *sym, Section *section,
                           addr_t value, unsigned long size)
{
    int sh_num = section ? section->sh_num : SHN_UNDEF;
    put_extern_sym2(sym, sh_num, value, size, 1);
}

/* add a new relocation entry to symbol 'sym' in section 's' */
ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type,
                     addr_t addend)
{
    int c = 0;

    if (nocode_wanted && s == cur_text_section)
        return;

    if (sym) {
        if (0 == sym->c)
            put_extern_sym(sym, NULL, 0, 0);
        c = sym->c;
    }

    /* now we can add ELF relocation info */
    put_elf_reloca(symtab_section, s, offset, type, c, addend);
}

#if PTR_SIZE == 4
ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type)
{
    greloca(s, sym, offset, type, 0);
}
#endif

/* ------------------------------------------------------------------------- */
/* symbol allocator */
static Sym *__sym_malloc(void)
{
    Sym *sym_pool, *sym, *last_sym;
    int i;

    sym_pool = tcc_malloc(SYM_POOL_NB * sizeof(Sym));
    dynarray_add(&sym_pools, &nb_sym_pools, sym_pool);

    last_sym = sym_free_first;
    sym = sym_pool;
    for(i = 0; i < SYM_POOL_NB; i++) {
        sym->next = last_sym;
        last_sym = sym;
        sym++;
    }
    sym_free_first = last_sym;
    return last_sym;
}

static inline Sym *sym_malloc(void)
{
    Sym *sym;
#ifndef SYM_DEBUG
    sym = sym_free_first;
    if (!sym)
        sym = __sym_malloc();
    sym_free_first = sym->next;
    return sym;
#else
    sym = tcc_malloc(sizeof(Sym));
    return sym;
#endif
}

ST_INLN void sym_free(Sym *sym)
{
#ifndef SYM_DEBUG
    sym->next = sym_free_first;
    sym_free_first = sym;
#else
    tcc_free(sym);
#endif
}

/* push, without hashing */
ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, int c)
{
    Sym *s;

    s = sym_malloc();
    memset(s, 0, sizeof *s);
    s->v = v;
    s->type.t = t;
    s->c = c;
    /* add in stack */
    s->prev = *ps;
    *ps = s;
    return s;
}

/* find a symbol and return its associated structure. 's' is the top
   of the symbol stack */
ST_FUNC Sym *sym_find2(Sym *s, int v)
{
    while (s) {
        if (s->v == v)
            return s;
        else if (s->v == -1)
            return NULL;
        s = s->prev;
    }
    return NULL;
}

/* structure lookup */
ST_INLN Sym *struct_find(int v)
{
    v -= TOK_IDENT;
    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
        return NULL;
    return table_ident[v]->sym_struct;
}

/* find an identifier */
ST_INLN Sym *sym_find(int v)
{
    v -= TOK_IDENT;
    if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
        return NULL;
    return table_ident[v]->sym_identifier;
}

/* push a given symbol on the symbol stack */
ST_FUNC Sym *sym_push(int v, CType *type, int r, int c)
{
    Sym *s, **ps;
    TokenSym *ts;

    if (local_stack)
        ps = &local_stack;
    else
        ps = &global_stack;
    s = sym_push2(ps, v, type->t, c);
    s->type.ref = type->ref;
    s->r = r;
    /* don't record fields or anonymous symbols */
    /* XXX: simplify */
    if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
        /* record symbol in token array */
        ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
        if (v & SYM_STRUCT)
            ps = &ts->sym_struct;
        else
            ps = &ts->sym_identifier;
        s->prev_tok = *ps;
        *ps = s;
        s->sym_scope = local_scope;
        if (s->prev_tok && s->prev_tok->sym_scope == s->sym_scope)
            tcc_error("redeclaration of '%s'",
                get_tok_str(v & ~SYM_STRUCT, NULL));
    }
    return s;
}

/* push a global identifier */
ST_FUNC Sym *global_identifier_push(int v, int t, int c)
{
    Sym *s, **ps;
    s = sym_push2(&global_stack, v, t, c);
    /* don't record anonymous symbol */
    if (v < SYM_FIRST_ANOM) {
        ps = &table_ident[v - TOK_IDENT]->sym_identifier;
        /* modify the top most local identifier, so that
           sym_identifier will point to 's' when popped */
        while (*ps != NULL && (*ps)->sym_scope)
            ps = &(*ps)->prev_tok;
        s->prev_tok = *ps;
        *ps = s;
    }
    return s;
}

/* pop symbols until top reaches 'b'.  If KEEP is non-zero don't really
   pop them yet from the list, but do remove them from the token array.  */
ST_FUNC void sym_pop(Sym **ptop, Sym *b, int keep)
{
    Sym *s, *ss, **ps;
    TokenSym *ts;
    int v;

    s = *ptop;
    while(s != b) {
        ss = s->prev;
        v = s->v;
        /* remove symbol in token array */
        /* XXX: simplify */
        if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
            ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
            if (v & SYM_STRUCT)
                ps = &ts->sym_struct;
            else
                ps = &ts->sym_identifier;
            *ps = s->prev_tok;
        }
    if (!keep)
        sym_free(s);
        s = ss;
    }
    if (!keep)
    *ptop = b;
}

/* ------------------------------------------------------------------------- */

static void vsetc(CType *type, int r, CValue *vc)
{
    int v;

    if (vtop >= vstack + (VSTACK_SIZE - 1))
        tcc_error("memory full (vstack)");
    /* cannot let cpu flags if other instruction are generated. Also
       avoid leaving VT_JMP anywhere except on the top of the stack
       because it would complicate the code generator.

       Don't do this when nocode_wanted.  vtop might come from
       !nocode_wanted regions (see 88_codeopt.c) and transforming
       it to a register without actually generating code is wrong
       as their value might still be used for real.  All values
       we push under nocode_wanted will eventually be popped
       again, so that the VT_CMP/VT_JMP value will be in vtop
       when code is unsuppressed again.

       Same logic below in vswap(); */
    if (vtop >= vstack && !nocode_wanted) {
        v = vtop->r & VT_VALMASK;
        if (v == VT_CMP || (v & ~1) == VT_JMP)
            gv(RC_INT);
    }

    vtop++;
    vtop->type = *type;
    vtop->r = r;
    vtop->r2 = VT_CONST;
    vtop->c = *vc;
    vtop->sym = NULL;
}

ST_FUNC void vswap(void)
{
    SValue tmp;
    /* cannot vswap cpu flags. See comment at vsetc() above */
    if (vtop >= vstack && !nocode_wanted) {
        int v = vtop->r & VT_VALMASK;
        if (v == VT_CMP || (v & ~1) == VT_JMP)
            gv(RC_INT);
    }
    tmp = vtop[0];
    vtop[0] = vtop[-1];
    vtop[-1] = tmp;
}

/* pop stack value */
ST_FUNC void vpop(void)
{
    int v;
    v = vtop->r & VT_VALMASK;
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
    /* for x86, we need to pop the FP stack */
    if (v == TREG_ST0) {
        o(0xd8dd); /* fstp %st(0) */
    } else
#endif
    if (v == VT_JMP || v == VT_JMPI) {
        /* need to put correct jump if && or || without test */
        gsym(vtop->c.i);
    }
    vtop--;
}

/* push constant of type "type" with useless value */
ST_FUNC void vpush(CType *type)
{
    vset(type, VT_CONST, 0);
}

/* push integer constant */
ST_FUNC void vpushi(int v)
{
    CValue cval;
    cval.i = v;
    vsetc(&int_type, VT_CONST, &cval);
}

/* push a pointer sized constant */
static void vpushs(addr_t v)
{
  CValue cval;
  cval.i = v;
  vsetc(&size_type, VT_CONST, &cval);
}

/* push arbitrary 64bit constant */
ST_FUNC void vpush64(int ty, unsigned long long v)
{
    CValue cval;
    CType ctype;
    ctype.t = ty;
    ctype.ref = NULL;
    cval.i = v;
    vsetc(&ctype, VT_CONST, &cval);
}

/* push long long constant */
static inline void vpushll(long long v)
{
    vpush64(VT_LLONG, v);
}

ST_FUNC void vset(CType *type, int r, int v)
{
    CValue cval;

    cval.i = v;
    vsetc(type, r, &cval);
}

static void vseti(int r, int v)
{
    CType type;
    type.t = VT_INT;
    type.ref = NULL;
    vset(&type, r, v);
}

ST_FUNC void vpushv(SValue *v)
{
    if (vtop >= vstack + (VSTACK_SIZE - 1))
        tcc_error("memory full (vstack)");
    vtop++;
    *vtop = *v;
}

static void vdup(void)
{
    vpushv(vtop);
}

/* rotate n first stack elements to the bottom
   I1 ... In -> I2 ... In I1 [top is right]
*/
ST_FUNC void vrotb(int n)
{
    int i;
    SValue tmp;

    tmp = vtop[-n + 1];
    for(i=-n+1;i!=0;i++)
        vtop[i] = vtop[i+1];
    vtop[0] = tmp;
}

/* rotate the n elements before entry e towards the top
   I1 ... In ... -> In I1 ... I(n-1) ... [top is right]
 */
ST_FUNC void vrote(SValue *e, int n)
{
    int i;
    SValue tmp;

    tmp = *e;
    for(i = 0;i < n - 1; i++)
        e[-i] = e[-i - 1];
    e[-n + 1] = tmp;
}

/* rotate n first stack elements to the top
   I1 ... In -> In I1 ... I(n-1)  [top is right]
 */
ST_FUNC void vrott(int n)
{
    vrote(vtop, n);
}

/* push a symbol value of TYPE */
static inline void vpushsym(CType *type, Sym *sym)
{
    CValue cval;
    cval.i = 0;
    vsetc(type, VT_CONST | VT_SYM, &cval);
    vtop->sym = sym;
}

/* Return a static symbol pointing to a section */
ST_FUNC Sym *get_sym_ref(CType *type, Section *sec, unsigned long offset, unsigned long size)
{
    int v;
    Sym *sym;

    v = anon_sym++;
    sym = global_identifier_push(v, type->t | VT_STATIC, 0);
    sym->type.ref = type->ref;
    sym->r = VT_CONST | VT_SYM;
    put_extern_sym(sym, sec, offset, size);
    return sym;
}

/* push a reference to a section offset by adding a dummy symbol */
static void vpush_ref(CType *type, Section *sec, unsigned long offset, unsigned long size)
{
    vpushsym(type, get_sym_ref(type, sec, offset, size));  
}

/* define a new external reference to a symbol 'v' of type 'u' */
ST_FUNC Sym *external_global_sym(int v, CType *type, int r)
{
    Sym *s;

    s = sym_find(v);
    if (!s) {
        /* push forward reference */
        s = global_identifier_push(v, type->t | VT_EXTERN, 0);
        s->type.ref = type->ref;
        s->r = r | VT_CONST | VT_SYM;
    } else if (IS_ASM_SYM(s)) {
        s->type.t = type->t | (s->type.t & VT_EXTERN);
        s->type.ref = type->ref;
        update_storage(s);
    }
    return s;
}

/* Merge some type attributes.  */
static void patch_type(Sym *sym, CType *type)
{
    if (!(type->t & VT_EXTERN)) {
        if (!(sym->type.t & VT_EXTERN))
            tcc_error("redefinition of '%s'", get_tok_str(sym->v, NULL));
        sym->type.t &= ~VT_EXTERN;
    }

    if (IS_ASM_SYM(sym)) {
        /* stay static if both are static */
        sym->type.t = type->t & (sym->type.t | ~VT_STATIC);
        sym->type.ref = type->ref;
    }

    if (!is_compatible_types(&sym->type, type)) {
        tcc_error("incompatible types for redefinition of '%s'",
                  get_tok_str(sym->v, NULL));

    } else if ((sym->type.t & VT_BTYPE) == VT_FUNC) {
        int static_proto = sym->type.t & VT_STATIC;
        /* warn if static follows non-static function declaration */
        if ((type->t & VT_STATIC) && !static_proto && !(type->t & VT_INLINE))
            tcc_warning("static storage ignored for redefinition of '%s'",
                get_tok_str(sym->v, NULL));

        if (0 == (type->t & VT_EXTERN)) {
            /* put complete type, use static from prototype */
            sym->type.t = (type->t & ~VT_STATIC) | static_proto;
            if (type->t & VT_INLINE)
                sym->type.t = type->t;
            sym->type.ref = type->ref;
        }

    } else {
        if ((sym->type.t & VT_ARRAY) && type->ref->c >= 0) {
            /* set array size if it was omitted in extern declaration */
            if (sym->type.ref->c < 0)
                sym->type.ref->c = type->ref->c;
            else if (sym->type.ref->c != type->ref->c)
                tcc_error("conflicting type for '%s'", get_tok_str(sym->v, NULL));
        }
        if ((type->t ^ sym->type.t) & VT_STATIC)
            tcc_warning("storage mismatch for redefinition of '%s'",
                get_tok_str(sym->v, NULL));
    }
}


/* Merge some storage attributes.  */
static void patch_storage(Sym *sym, AttributeDef *ad, CType *type)
{
    if (type)
        patch_type(sym, type);

#ifdef TCC_TARGET_PE
    if (sym->a.dllimport != ad->a.dllimport)
        tcc_error("incompatible dll linkage for redefinition of '%s'",
            get_tok_str(sym->v, NULL));
    sym->a.dllexport |= ad->a.dllexport;
#endif
    sym->a.weak |= ad->a.weak;
    if (ad->a.visibility) {
        int vis = sym->a.visibility;
        int vis2 = ad->a.visibility;
        if (vis == STV_DEFAULT)
            vis = vis2;
        else if (vis2 != STV_DEFAULT)
            vis = (vis < vis2) ? vis : vis2;
        sym->a.visibility = vis;
    }
    if (ad->a.aligned)
        sym->a.aligned = ad->a.aligned;
    if (ad->asm_label)
        sym->asm_label = ad->asm_label;
    update_storage(sym);
}

/* define a new external reference to a symbol 'v' */
static Sym *external_sym(int v, CType *type, int r, AttributeDef *ad)
{
    Sym *s;
    s = sym_find(v);
    if (!s) {
        /* push forward reference */
        s = sym_push(v, type, r | VT_CONST | VT_SYM, 0);
        s->type.t |= VT_EXTERN;
        s->a = ad->a;
        s->sym_scope = 0;
    } else {
        if (s->type.ref == func_old_type.ref) {
            s->type.ref = type->ref;
            s->r = r | VT_CONST | VT_SYM;
            s->type.t |= VT_EXTERN;
        }
        patch_storage(s, ad, type);
    }
    return s;
}

/* push a reference to global symbol v */
ST_FUNC void vpush_global_sym(CType *type, int v)
{
    vpushsym(type, external_global_sym(v, type, 0));
}

/* save registers up to (vtop - n) stack entry */
ST_FUNC void save_regs(int n)
{
    SValue *p, *p1;
    for(p = vstack, p1 = vtop - n; p <= p1; p++)
        save_reg(p->r);
}

/* save r to the memory stack, and mark it as being free */
ST_FUNC void save_reg(int r)
{
    save_reg_upstack(r, 0);
}

/* save r to the memory stack, and mark it as being free,
   if seen up to (vtop - n) stack entry */
ST_FUNC void save_reg_upstack(int r, int n)
{
    int l, saved, size, align;
    SValue *p, *p1, sv;
    CType *type;

    if ((r &= VT_VALMASK) >= VT_CONST)
        return;
    if (nocode_wanted)
        return;

    /* modify all stack values */
    saved = 0;
    l = 0;
    for(p = vstack, p1 = vtop - n; p <= p1; p++) {
        if ((p->r & VT_VALMASK) == r ||
            ((p->type.t & VT_BTYPE) == VT_LLONG && (p->r2 & VT_VALMASK) == r)) {
            /* must save value on stack if not already done */
            if (!saved) {
                /* NOTE: must reload 'r' because r might be equal to r2 */
                r = p->r & VT_VALMASK;
                /* store register in the stack */
                type = &p->type;
                if ((p->r & VT_LVAL) ||
                    (!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG))
#if PTR_SIZE == 8
                    type = &char_pointer_type;
#else
                    type = &int_type;
#endif
                size = type_size(type, &align);
                loc = (loc - size) & -align;
                sv.type.t = type->t;
                sv.r = VT_LOCAL | VT_LVAL;
                sv.c.i = loc;
                store(r, &sv);
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
                /* x86 specific: need to pop fp register ST0 if saved */
                if (r == TREG_ST0) {
                    o(0xd8dd); /* fstp %st(0) */
                }
#endif
#if PTR_SIZE == 4
                /* special long long case */
                if ((type->t & VT_BTYPE) == VT_LLONG) {
                    sv.c.i += 4;
                    store(p->r2, &sv);
                }
#endif
                l = loc;
                saved = 1;
            }
            /* mark that stack entry as being saved on the stack */
            if (p->r & VT_LVAL) {
                /* also clear the bounded flag because the
                   relocation address of the function was stored in
                   p->c.i */
                p->r = (p->r & ~(VT_VALMASK | VT_BOUNDED)) | VT_LLOCAL;
            } else {
                p->r = lvalue_type(p->type.t) | VT_LOCAL;
            }
            p->r2 = VT_CONST;
            p->c.i = l;
        }
    }
}

#ifdef TCC_TARGET_ARM
/* find a register of class 'rc2' with at most one reference on stack.
 * If none, call get_reg(rc) */
ST_FUNC int get_reg_ex(int rc, int rc2)
{
    int r;
    SValue *p;
    
    for(r=0;r<NB_REGS;r++) {
        if (reg_classes[r] & rc2) {
            int n;
            n=0;
            for(p = vstack; p <= vtop; p++) {
                if ((p->r & VT_VALMASK) == r ||
                    (p->r2 & VT_VALMASK) == r)
                    n++;
            }
            if (n <= 1)
                return r;
        }
    }
    return get_reg(rc);
}
#endif

/* find a free register of class 'rc'. If none, save one register */
ST_FUNC int get_reg(int rc)
{
    int r;
    SValue *p;

    /* find a free register */
    for(r=0;r<NB_REGS;r++) {
        if (reg_classes[r] & rc) {
            if (nocode_wanted)
                return r;
            for(p=vstack;p<=vtop;p++) {
                if ((p->r & VT_VALMASK) == r ||
                    (p->r2 & VT_VALMASK) == r)
                    goto notfound;
            }
            return r;
        }
    notfound: ;
    }
    
    /* no register left : free the first one on the stack (VERY
       IMPORTANT to start from the bottom to ensure that we don't
       spill registers used in gen_opi()) */
    for(p=vstack;p<=vtop;p++) {
        /* look at second register (if long long) */
        r = p->r2 & VT_VALMASK;
        if (r < VT_CONST && (reg_classes[r] & rc))
            goto save_found;
        r = p->r & VT_VALMASK;
        if (r < VT_CONST && (reg_classes[r] & rc)) {
        save_found:
            save_reg(r);
            return r;
        }
    }
    /* Should never comes here */
    return -1;
}

/* move register 's' (of type 't') to 'r', and flush previous value of r to memory
   if needed */
static void move_reg(int r, int s, int t)
{
    SValue sv;

    if (r != s) {
        save_reg(r);
        sv.type.t = t;
        sv.type.ref = NULL;
        sv.r = s;
        sv.c.i = 0;
        load(r, &sv);
    }
}

/* get address of vtop (vtop MUST BE an lvalue) */
ST_FUNC void gaddrof(void)
{
    vtop->r &= ~VT_LVAL;
    /* tricky: if saved lvalue, then we can go back to lvalue */
    if ((vtop->r & VT_VALMASK) == VT_LLOCAL)
        vtop->r = (vtop->r & ~(VT_VALMASK | VT_LVAL_TYPE)) | VT_LOCAL | VT_LVAL;


}

#ifdef CONFIG_TCC_BCHECK
/* generate lvalue bound code */
static void gbound(void)
{
    int lval_type;
    CType type1;

    vtop->r &= ~VT_MUSTBOUND;
    /* if lvalue, then use checking code before dereferencing */
    if (vtop->r & VT_LVAL) {
        /* if not VT_BOUNDED value, then make one */
        if (!(vtop->r & VT_BOUNDED)) {
            lval_type = vtop->r & (VT_LVAL_TYPE | VT_LVAL);
            /* must save type because we must set it to int to get pointer */
            type1 = vtop->type;
            vtop->type.t = VT_PTR;
            gaddrof();
            vpushi(0);
            gen_bounded_ptr_add();
            vtop->r |= lval_type;
            vtop->type = type1;
        }
        /* then check for dereferencing */
        gen_bounded_ptr_deref();
    }
}
#endif

static void incr_bf_adr(int o)
{
    vtop->type = char_pointer_type;
    gaddrof();
    vpushi(o);
    gen_op('+');
    vtop->type.t = (vtop->type.t & ~(VT_BTYPE|VT_DEFSIGN))
        | (VT_BYTE|VT_UNSIGNED);
    vtop->r = (vtop->r & ~VT_LVAL_TYPE)
        | (VT_LVAL_BYTE|VT_LVAL_UNSIGNED|VT_LVAL);
}

/* single-byte load mode for packed or otherwise unaligned bitfields */
static void load_packed_bf(CType *type, int bit_pos, int bit_size)
{
    int n, o, bits;
    save_reg_upstack(vtop->r, 1);
    vpush64(type->t & VT_BTYPE, 0); // B X
    bits = 0, o = bit_pos >> 3, bit_pos &= 7;
    do {
        vswap(); // X B
        incr_bf_adr(o);
        vdup(); // X B B
        n = 8 - bit_pos;
        if (n > bit_size)
            n = bit_size;
        if (bit_pos)
            vpushi(bit_pos), gen_op(TOK_SHR), bit_pos = 0; // X B Y
        if (n < 8)
            vpushi((1 << n) - 1), gen_op('&');
        gen_cast(type);
        if (bits)
            vpushi(bits), gen_op(TOK_SHL);
        vrotb(3); // B Y X
        gen_op('|'); // B X
        bits += n, bit_size -= n, o = 1;
    } while (bit_size);
    vswap(), vpop();
    if (!(type->t & VT_UNSIGNED)) {
        n = ((type->t & VT_BTYPE) == VT_LLONG ? 64 : 32) - bits;
        vpushi(n), gen_op(TOK_SHL);
        vpushi(n), gen_op(TOK_SAR);
    }
}

/* single-byte store mode for packed or otherwise unaligned bitfields */
static void store_packed_bf(int bit_pos, int bit_size)
{
    int bits, n, o, m, c;

    c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
    vswap(); // X B
    save_reg_upstack(vtop->r, 1);
    bits = 0, o = bit_pos >> 3, bit_pos &= 7;
    do {
        incr_bf_adr(o); // X B
        vswap(); //B X
        c ? vdup() : gv_dup(); // B V X
        vrott(3); // X B V
        if (bits)
            vpushi(bits), gen_op(TOK_SHR);
        if (bit_pos)
            vpushi(bit_pos), gen_op(TOK_SHL);
        n = 8 - bit_pos;
        if (n > bit_size)
            n = bit_size;
        if (n < 8) {
            m = ((1 << n) - 1) << bit_pos;
            vpushi(m), gen_op('&'); // X B V1
            vpushv(vtop-1); // X B V1 B
            vpushi(m & 0x80 ? ~m & 0x7f : ~m);
            gen_op('&'); // X B V1 B1
            gen_op('|'); // X B V2
        }
        vdup(), vtop[-1] = vtop[-2]; // X B B V2
        vstore(), vpop(); // X B
        bits += n, bit_size -= n, bit_pos = 0, o = 1;
    } while (bit_size);
    vpop(), vpop();
}

static int adjust_bf(SValue *sv, int bit_pos, int bit_size)
{
    int t;
    if (0 == sv->type.ref)
        return 0;
    t = sv->type.ref->auxtype;
    if (t != -1 && t != VT_STRUCT) {
        sv->type.t = (sv->type.t & ~VT_BTYPE) | t;
        sv->r = (sv->r & ~VT_LVAL_TYPE) | lvalue_type(sv->type.t);
    }
    return t;
}

/* store vtop a register belonging to class 'rc'. lvalues are
   converted to values. Cannot be used if cannot be converted to
   register value (such as structures). */
ST_FUNC int gv(int rc)
{
    int r, bit_pos, bit_size, size, align, rc2;

    /* NOTE: get_reg can modify vstack[] */
    if (vtop->type.t & VT_BITFIELD) {
        CType type;

        bit_pos = BIT_POS(vtop->type.t);
        bit_size = BIT_SIZE(vtop->type.t);
        /* remove bit field info to avoid loops */
        vtop->type.t &= ~VT_STRUCT_MASK;

        type.ref = NULL;
        type.t = vtop->type.t & VT_UNSIGNED;
        if ((vtop->type.t & VT_BTYPE) == VT_BOOL)
            type.t |= VT_UNSIGNED;

        r = adjust_bf(vtop, bit_pos, bit_size);

        if ((vtop->type.t & VT_BTYPE) == VT_LLONG)
            type.t |= VT_LLONG;
        else
            type.t |= VT_INT;

        if (r == VT_STRUCT) {
            load_packed_bf(&type, bit_pos, bit_size);
        } else {
            int bits = (type.t & VT_BTYPE) == VT_LLONG ? 64 : 32;
            /* cast to int to propagate signedness in following ops */
            gen_cast(&type);
            /* generate shifts */
            vpushi(bits - (bit_pos + bit_size));
            gen_op(TOK_SHL);
            vpushi(bits - bit_size);
            /* NOTE: transformed to SHR if unsigned */
            gen_op(TOK_SAR);
        }
        r = gv(rc);
    } else {
        if (is_float(vtop->type.t) && 
            (vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
            unsigned long offset;
            /* CPUs usually cannot use float constants, so we store them
               generically in data segment */
            size = type_size(&vtop->type, &align);
            if (NODATA_WANTED)
                size = 0, align = 1;
            offset = section_add(data_section, size, align);
            vpush_ref(&vtop->type, data_section, offset, size);
        vswap();
        init_putv(&vtop->type, data_section, offset);
        vtop->r |= VT_LVAL;
        }
#ifdef CONFIG_TCC_BCHECK
        if (vtop->r & VT_MUSTBOUND) 
            gbound();
#endif

        r = vtop->r & VT_VALMASK;
        rc2 = (rc & RC_FLOAT) ? RC_FLOAT : RC_INT;
#ifndef TCC_TARGET_ARM64
        if (rc == RC_IRET)
            rc2 = RC_LRET;
#ifdef TCC_TARGET_X86_64
        else if (rc == RC_FRET)
            rc2 = RC_QRET;
#endif
#endif
        /* need to reload if:
           - constant
           - lvalue (need to dereference pointer)
           - already a register, but not in the right class */
        if (r >= VT_CONST
         || (vtop->r & VT_LVAL)
         || !(reg_classes[r] & rc)
#if PTR_SIZE == 8
         || ((vtop->type.t & VT_BTYPE) == VT_QLONG && !(reg_classes[vtop->r2] & rc2))
         || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT && !(reg_classes[vtop->r2] & rc2))
#else
         || ((vtop->type.t & VT_BTYPE) == VT_LLONG && !(reg_classes[vtop->r2] & rc2))
#endif
            )
        {
            r = get_reg(rc);
#if PTR_SIZE == 8
            if (((vtop->type.t & VT_BTYPE) == VT_QLONG) || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT)) {
                int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
#else
            if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
                int addr_type = VT_INT, load_size = 4, load_type = VT_INT;
                unsigned long long ll;
#endif
                int r2, original_type;
                original_type = vtop->type.t;
                /* two register type load : expand to two words
                   temporarily */
#if PTR_SIZE == 4
                if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
                    /* load constant */
                    ll = vtop->c.i;
                    vtop->c.i = ll; /* first word */
                    load(r, vtop);
                    vtop->r = r; /* save register value */
                    vpushi(ll >> 32); /* second word */
                } else
#endif
                if (vtop->r & VT_LVAL) {
                    /* We do not want to modifier the long long
                       pointer here, so the safest (and less
                       efficient) is to save all the other registers
                       in the stack. XXX: totally inefficient. */
               #if 0
                    save_regs(1);
               #else
                    /* lvalue_save: save only if used further down the stack */
                    save_reg_upstack(vtop->r, 1);
               #endif
                    /* load from memory */
                    vtop->type.t = load_type;
                    load(r, vtop);
                    vdup();
                    vtop[-1].r = r; /* save register value */
                    /* increment pointer to get second word */
                    vtop->type.t = addr_type;
                    gaddrof();
                    vpushi(load_size);
                    gen_op('+');
                    vtop->r |= VT_LVAL;
                    vtop->type.t = load_type;
                } else {
                    /* move registers */
                    load(r, vtop);
                    vdup();
                    vtop[-1].r = r; /* save register value */
                    vtop->r = vtop[-1].r2;
                }
                /* Allocate second register. Here we rely on the fact that
                   get_reg() tries first to free r2 of an SValue. */
                r2 = get_reg(rc2);
                load(r2, vtop);
                vpop();
                /* write second register */
                vtop->r2 = r2;
                vtop->type.t = original_type;
            } else if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) {
                int t1, t;
                /* lvalue of scalar type : need to use lvalue type
                   because of possible cast */
                t = vtop->type.t;
                t1 = t;
                /* compute memory access type */
                if (vtop->r & VT_LVAL_BYTE)
                    t = VT_BYTE;
                else if (vtop->r & VT_LVAL_SHORT)
                    t = VT_SHORT;
                if (vtop->r & VT_LVAL_UNSIGNED)
                    t |= VT_UNSIGNED;
                vtop->type.t = t;
                load(r, vtop);
                /* restore wanted type */
                vtop->type.t = t1;
            } else {
                /* one register type load */
                load(r, vtop);
            }
        }
        vtop->r = r;
#ifdef TCC_TARGET_C67
        /* uses register pairs for doubles */
        if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) 
            vtop->r2 = r+1;
#endif
    }
    return r;
}

/* generate vtop[-1] and vtop[0] in resp. classes rc1 and rc2 */
ST_FUNC void gv2(int rc1, int rc2)
{
    int v;

    /* generate more generic register first. But VT_JMP or VT_CMP
       values must be generated first in all cases to avoid possible
       reload errors */
    v = vtop[0].r & VT_VALMASK;
    if (v != VT_CMP && (v & ~1) != VT_JMP && rc1 <= rc2) {
        vswap();
        gv(rc1);
        vswap();
        gv(rc2);
        /* test if reload is needed for first register */
        if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) {
            vswap();
            gv(rc1);
            vswap();
        }
    } else {
        gv(rc2);
        vswap();
        gv(rc1);
        vswap();
        /* test if reload is needed for first register */
        if ((vtop[0].r & VT_VALMASK) >= VT_CONST) {
            gv(rc2);
        }
    }
}

#ifndef TCC_TARGET_ARM64
/* wrapper around RC_FRET to return a register by type */
static int rc_fret(int t)
{
#ifdef TCC_TARGET_X86_64
    if (t == VT_LDOUBLE) {
        return RC_ST0;
    }
#endif
    return RC_FRET;
}
#endif

/* wrapper around REG_FRET to return a register by type */
static int reg_fret(int t)
{
#ifdef TCC_TARGET_X86_64
    if (t == VT_LDOUBLE) {
        return TREG_ST0;
    }
#endif
    return REG_FRET;
}

#if PTR_SIZE == 4
/* expand 64bit on stack in two ints */
static void lexpand(void)
{
    int u, v;
    u = vtop->type.t & (VT_DEFSIGN | VT_UNSIGNED);
    v = vtop->r & (VT_VALMASK | VT_LVAL);
    if (v == VT_CONST) {
        vdup();
        vtop[0].c.i >>= 32;
    } else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) {
        vdup();
        vtop[0].c.i += 4;
    } else {
        gv(RC_INT);
        vdup();
        vtop[0].r = vtop[-1].r2;
        vtop[0].r2 = vtop[-1].r2 = VT_CONST;
    }
    vtop[0].type.t = vtop[-1].type.t = VT_INT | u;
}
#endif

#ifdef TCC_TARGET_ARM
/* expand long long on stack */
ST_FUNC void lexpand_nr(void)
{
    int u,v;

    u = vtop->type.t & (VT_DEFSIGN | VT_UNSIGNED);
    vdup();
    vtop->r2 = VT_CONST;
    vtop->type.t = VT_INT | u;
    v=vtop[-1].r & (VT_VALMASK | VT_LVAL);
    if (v == VT_CONST) {
      vtop[-1].c.i = vtop->c.i;
      vtop->c.i = vtop->c.i >> 32;
      vtop->r = VT_CONST;
    } else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) {
      vtop->c.i += 4;
      vtop->r = vtop[-1].r;
    } else if (v > VT_CONST) {
      vtop--;
      lexpand();
    } else
      vtop->r = vtop[-1].r2;
    vtop[-1].r2 = VT_CONST;
    vtop[-1].type.t = VT_INT | u;
}
#endif

#if PTR_SIZE == 4
/* build a long long from two ints */
static void lbuild(int t)
{
    gv2(RC_INT, RC_INT);
    vtop[-1].r2 = vtop[0].r;
    vtop[-1].type.t = t;
    vpop();
}
#endif

/* convert stack entry to register and duplicate its value in another
   register */
static void gv_dup(void)
{
    int rc, t, r, r1;
    SValue sv;

    t = vtop->type.t;
#if PTR_SIZE == 4
    if ((t & VT_BTYPE) == VT_LLONG) {
        if (t & VT_BITFIELD) {
            gv(RC_INT);
            t = vtop->type.t;
        }
        lexpand();
        gv_dup();
        vswap();
        vrotb(3);
        gv_dup();
        vrotb(4);
        /* stack: H L L1 H1 */
        lbuild(t);
        vrotb(3);
        vrotb(3);
        vswap();
        lbuild(t);
        vswap();
    } else
#endif
    {
        /* duplicate value */
        rc = RC_INT;
        sv.type.t = VT_INT;
        if (is_float(t)) {
            rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64
            if ((t & VT_BTYPE) == VT_LDOUBLE) {
                rc = RC_ST0;
            }
#endif
            sv.type.t = t;
        }
        r = gv(rc);
        r1 = get_reg(rc);
        sv.r = r;
        sv.c.i = 0;
        load(r1, &sv); /* move r to r1 */
        vdup();
        /* duplicates value */
        if (r != r1)
            vtop->r = r1;
    }
}

/* Generate value test
 *
 * Generate a test for any value (jump, comparison and integers) */
ST_FUNC int gvtst(int inv, int t)
{
    int v = vtop->r & VT_VALMASK;
    if (v != VT_CMP && v != VT_JMP && v != VT_JMPI) {
        vpushi(0);
        gen_op(TOK_NE);
    }
    if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
        /* constant jmp optimization */
        if ((vtop->c.i != 0) != inv)
            t = gjmp(t);
        vtop--;
        return t;
    }
    return gtst(inv, t);
}

#if PTR_SIZE == 4
/* generate CPU independent (unsigned) long long operations */
static void gen_opl(int op)
{
    int t, a, b, op1, c, i;
    int func;
    unsigned short reg_iret = REG_IRET;
    unsigned short reg_lret = REG_LRET;
    SValue tmp;

    switch(op) {
    case '/':
    case TOK_PDIV:
        func = TOK___divdi3;
        goto gen_func;
    case TOK_UDIV:
        func = TOK___udivdi3;
        goto gen_func;
    case '%':
        func = TOK___moddi3;
        goto gen_mod_func;
    case TOK_UMOD:
        func = TOK___umoddi3;
    gen_mod_func:
#ifdef TCC_ARM_EABI
        reg_iret = TREG_R2;
        reg_lret = TREG_R3;
#endif
    gen_func:
        /* call generic long long function */
        vpush_global_sym(&func_old_type, func);
        vrott(3);
        gfunc_call(2);
        vpushi(0);
        vtop->r = reg_iret;
        vtop->r2 = reg_lret;
        break;
    case '^':
    case '&':
    case '|':
    case '*':
    case '+':
    case '-':
        //pv("gen_opl A",0,2);
        t = vtop->type.t;
        vswap();
        lexpand();
        vrotb(3);
        lexpand();
        /* stack: L1 H1 L2 H2 */
        tmp = vtop[0];
        vtop[0] = vtop[-3];
        vtop[-3] = tmp;
        tmp = vtop[-2];
        vtop[-2] = vtop[-3];
        vtop[-3] = tmp;
        vswap();
        /* stack: H1 H2 L1 L2 */
        //pv("gen_opl B",0,4);
        if (op == '*') {
            vpushv(vtop - 1);
            vpushv(vtop - 1);
            gen_op(TOK_UMULL);
            lexpand();
            /* stack: H1 H2 L1 L2 ML MH */
            for(i=0;i<4;i++)
                vrotb(6);
            /* stack: ML MH H1 H2 L1 L2 */
            tmp = vtop[0];
            vtop[0] = vtop[-2];
            vtop[-2] = tmp;
            /* stack: ML MH H1 L2 H2 L1 */
            gen_op('*');
            vrotb(3);
            vrotb(3);
            gen_op('*');
            /* stack: ML MH M1 M2 */
            gen_op('+');
            gen_op('+');
        } else if (op == '+' || op == '-') {
            /* XXX: add non carry method too (for MIPS or alpha) */
            if (op == '+')
                op1 = TOK_ADDC1;
            else
                op1 = TOK_SUBC1;
            gen_op(op1);
            /* stack: H1 H2 (L1 op L2) */
            vrotb(3);
            vrotb(3);
            gen_op(op1 + 1); /* TOK_xxxC2 */
        } else {
            gen_op(op);
            /* stack: H1 H2 (L1 op L2) */
            vrotb(3);
            vrotb(3);
            /* stack: (L1 op L2) H1 H2 */
            gen_op(op);
            /* stack: (L1 op L2) (H1 op H2) */
        }
        /* stack: L H */
        lbuild(t);
        break;
    case TOK_SAR:
    case TOK_SHR:
    case TOK_SHL:
        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
            t = vtop[-1].type.t;
            vswap();
            lexpand();
            vrotb(3);
            /* stack: L H shift */
            c = (int)vtop->c.i;
            /* constant: simpler */
            /* NOTE: all comments are for SHL. the other cases are
               done by swapping words */
            vpop();
            if (op != TOK_SHL)
                vswap();
            if (c >= 32) {
                /* stack: L H */
                vpop();
                if (c > 32) {
                    vpushi(c - 32);
                    gen_op(op);
                }
                if (op != TOK_SAR) {
                    vpushi(0);
                } else {
                    gv_dup();
                    vpushi(31);
                    gen_op(TOK_SAR);
                }
                vswap();
            } else {
                vswap();
                gv_dup();
                /* stack: H L L */
                vpushi(c);
                gen_op(op);
                vswap();
                vpushi(32 - c);
                if (op == TOK_SHL)
                    gen_op(TOK_SHR);
                else
                    gen_op(TOK_SHL);
                vrotb(3);
                /* stack: L L H */
                vpushi(c);
                if (op == TOK_SHL)
                    gen_op(TOK_SHL);
                else
                    gen_op(TOK_SHR);
                gen_op('|');
            }
            if (op != TOK_SHL)
                vswap();
            lbuild(t);
        } else {
            /* XXX: should provide a faster fallback on x86 ? */
            switch(op) {
            case TOK_SAR:
                func = TOK___ashrdi3;
                goto gen_func;
            case TOK_SHR:
                func = TOK___lshrdi3;
                goto gen_func;
            case TOK_SHL:
                func = TOK___ashldi3;
                goto gen_func;
            }
        }
        break;
    default:
        /* compare operations */
        t = vtop->type.t;
        vswap();
        lexpand();
        vrotb(3);
        lexpand();
        /* stack: L1 H1 L2 H2 */
        tmp = vtop[-1];
        vtop[-1] = vtop[-2];
        vtop[-2] = tmp;
        /* stack: L1 L2 H1 H2 */
        /* compare high */
        op1 = op;
        /* when values are equal, we need to compare low words. since
           the jump is inverted, we invert the test too. */
        if (op1 == TOK_LT)
            op1 = TOK_LE;
        else if (op1 == TOK_GT)
            op1 = TOK_GE;
        else if (op1 == TOK_ULT)
            op1 = TOK_ULE;
        else if (op1 == TOK_UGT)
            op1 = TOK_UGE;
        a = 0;
        b = 0;
        gen_op(op1);
        if (op == TOK_NE) {
            b = gvtst(0, 0);
        } else {
            a = gvtst(1, 0);
            if (op != TOK_EQ) {
                /* generate non equal test */
                vpushi(TOK_NE);
                vtop->r = VT_CMP;
                b = gvtst(0, 0);
            }
        }
        /* compare low. Always unsigned */
        op1 = op;
        if (op1 == TOK_LT)
            op1 = TOK_ULT;
        else if (op1 == TOK_LE)
            op1 = TOK_ULE;
        else if (op1 == TOK_GT)
            op1 = TOK_UGT;
        else if (op1 == TOK_GE)
            op1 = TOK_UGE;
        gen_op(op1);
        a = gvtst(1, a);
        gsym(b);
        vseti(VT_JMPI, a);
        break;
    }
}
#endif

static uint64_t gen_opic_sdiv(uint64_t a, uint64_t b)
{
    uint64_t x = (a >> 63 ? -a : a) / (b >> 63 ? -b : b);
    return (a ^ b) >> 63 ? -x : x;
}

static int gen_opic_lt(uint64_t a, uint64_t b)
{
    return (a ^ (uint64_t)1 << 63) < (b ^ (uint64_t)1 << 63);
}

/* handle integer constant optimizations and various machine
   independent opt */
static void gen_opic(int op)
{
    SValue *v1 = vtop - 1;
    SValue *v2 = vtop;
    int t1 = v1->type.t & VT_BTYPE;
    int t2 = v2->type.t & VT_BTYPE;
    int c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
    int c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
    uint64_t l1 = c1 ? v1->c.i : 0;
    uint64_t l2 = c2 ? v2->c.i : 0;
    int shm = (t1 == VT_LLONG) ? 63 : 31;

    if (t1 != VT_LLONG && (PTR_SIZE != 8 || t1 != VT_PTR))
        l1 = ((uint32_t)l1 |
              (v1->type.t & VT_UNSIGNED ? 0 : -(l1 & 0x80000000)));
    if (t2 != VT_LLONG && (PTR_SIZE != 8 || t2 != VT_PTR))
        l2 = ((uint32_t)l2 |
              (v2->type.t & VT_UNSIGNED ? 0 : -(l2 & 0x80000000)));

    if (c1 && c2) {
        switch(op) {
        case '+': l1 += l2; break;
        case '-': l1 -= l2; break;
        case '&': l1 &= l2; break;
        case '^': l1 ^= l2; break;
        case '|': l1 |= l2; break;
        case '*': l1 *= l2; break;

        case TOK_PDIV:
        case '/':
        case '%':
        case TOK_UDIV:
        case TOK_UMOD:
            /* if division by zero, generate explicit division */
            if (l2 == 0) {
                if (const_wanted)
                    tcc_error("division by zero in constant");
                goto general_case;
            }
            switch(op) {
            default: l1 = gen_opic_sdiv(l1, l2); break;
            case '%': l1 = l1 - l2 * gen_opic_sdiv(l1, l2); break;
            case TOK_UDIV: l1 = l1 / l2; break;
            case TOK_UMOD: l1 = l1 % l2; break;
            }
            break;
        case TOK_SHL: l1 <<= (l2 & shm); break;
        case TOK_SHR: l1 >>= (l2 & shm); break;
        case TOK_SAR:
            l1 = (l1 >> 63) ? ~(~l1 >> (l2 & shm)) : l1 >> (l2 & shm);
            break;
            /* tests */
        case TOK_ULT: l1 = l1 < l2; break;
        case TOK_UGE: l1 = l1 >= l2; break;
        case TOK_EQ: l1 = l1 == l2; break;
        case TOK_NE: l1 = l1 != l2; break;
        case TOK_ULE: l1 = l1 <= l2; break;
        case TOK_UGT: l1 = l1 > l2; break;
        case TOK_LT: l1 = gen_opic_lt(l1, l2); break;
        case TOK_GE: l1 = !gen_opic_lt(l1, l2); break;
        case TOK_LE: l1 = !gen_opic_lt(l2, l1); break;
        case TOK_GT: l1 = gen_opic_lt(l2, l1); break;
            /* logical */
        case TOK_LAND: l1 = l1 && l2; break;
        case TOK_LOR: l1 = l1 || l2; break;
        default:
            goto general_case;
        }
    if (t1 != VT_LLONG && (PTR_SIZE != 8 || t1 != VT_PTR))
        l1 = ((uint32_t)l1 |
        (v1->type.t & VT_UNSIGNED ? 0 : -(l1 & 0x80000000)));
        v1->c.i = l1;
        vtop--;
    } else {
        /* if commutative ops, put c2 as constant */
        if (c1 && (op == '+' || op == '&' || op == '^' || 
                   op == '|' || op == '*')) {
            vswap();
            c2 = c1; //c = c1, c1 = c2, c2 = c;
            l2 = l1; //l = l1, l1 = l2, l2 = l;
        }
        if (!const_wanted &&
            c1 && ((l1 == 0 &&
                    (op == TOK_SHL || op == TOK_SHR || op == TOK_SAR)) ||
                   (l1 == -1 && op == TOK_SAR))) {
            /* treat (0 << x), (0 >> x) and (-1 >> x) as constant */
            vtop--;
        } else if (!const_wanted &&
                   c2 && ((l2 == 0 && (op == '&' || op == '*')) ||
                          (op == '|' &&
                            (l2 == -1 || (l2 == 0xFFFFFFFF && t2 != VT_LLONG))) ||
                          (l2 == 1 && (op == '%' || op == TOK_UMOD)))) {
            /* treat (x & 0), (x * 0), (x | -1) and (x % 1) as constant */
            if (l2 == 1)
                vtop->c.i = 0;
            vswap();
            vtop--;
        } else if (c2 && (((op == '*' || op == '/' || op == TOK_UDIV ||
                          op == TOK_PDIV) &&
                           l2 == 1) ||
                          ((op == '+' || op == '-' || op == '|' || op == '^' ||
                            op == TOK_SHL || op == TOK_SHR || op == TOK_SAR) &&
                           l2 == 0) ||
                          (op == '&' &&
                            (l2 == -1 || (l2 == 0xFFFFFFFF && t2 != VT_LLONG))))) {
            /* filter out NOP operations like x*1, x-0, x&-1... */
            vtop--;
        } else if (c2 && (op == '*' || op == TOK_PDIV || op == TOK_UDIV)) {
            /* try to use shifts instead of muls or divs */
            if (l2 > 0 && (l2 & (l2 - 1)) == 0) {
                int n = -1;
                while (l2) {
                    l2 >>= 1;
                    n++;
                }
                vtop->c.i = n;
                if (op == '*')
                    op = TOK_SHL;
                else if (op == TOK_PDIV)
                    op = TOK_SAR;
                else
                    op = TOK_SHR;
            }
            goto general_case;
        } else if (c2 && (op == '+' || op == '-') &&
                   (((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM))
                    || (vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_LOCAL)) {
            /* symbol + constant case */
            if (op == '-')
                l2 = -l2;
        l2 += vtop[-1].c.i;
        /* The backends can't always deal with addends to symbols
           larger than +-1<<31.  Don't construct such.  */
        if ((int)l2 != l2)
            goto general_case;
            vtop--;
            vtop->c.i = l2;
        } else {
        general_case:
                /* call low level op generator */
                if (t1 == VT_LLONG || t2 == VT_LLONG ||
                    (PTR_SIZE == 8 && (t1 == VT_PTR || t2 == VT_PTR)))
                    gen_opl(op);
                else
                    gen_opi(op);
        }
    }
}

/* generate a floating point operation with constant propagation */
static void gen_opif(int op)
{
    int c1, c2;
    SValue *v1, *v2;
#if defined _MSC_VER && defined _AMD64_
    /* avoid bad optimization with f1 -= f2 for f1:-0.0, f2:0.0 */
    volatile
#endif
    long double f1, f2;

    v1 = vtop - 1;
    v2 = vtop;
    /* currently, we cannot do computations with forward symbols */
    c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
    c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
    if (c1 && c2) {
        if (v1->type.t == VT_FLOAT) {
            f1 = v1->c.f;
            f2 = v2->c.f;
        } else if (v1->type.t == VT_DOUBLE) {
            f1 = v1->c.d;
            f2 = v2->c.d;
        } else {
            f1 = v1->c.ld;
            f2 = v2->c.ld;
        }

        /* NOTE: we only do constant propagation if finite number (not
           NaN or infinity) (ANSI spec) */
        if (!ieee_finite(f1) || !ieee_finite(f2))
            goto general_case;

        switch(op) {
        case '+': f1 += f2; break;
        case '-': f1 -= f2; break;
        case '*': f1 *= f2; break;
        case '/': 
            if (f2 == 0.0) {
                if (const_wanted)
                    tcc_error("division by zero in constant");
                goto general_case;
            }
            f1 /= f2; 
            break;
            /* XXX: also handles tests ? */
        default:
            goto general_case;
        }
        /* XXX: overflow test ? */
        if (v1->type.t == VT_FLOAT) {
            v1->c.f = f1;
        } else if (v1->type.t == VT_DOUBLE) {
            v1->c.d = f1;
        } else {
            v1->c.ld = f1;
        }
        vtop--;
    } else {
    general_case:
        gen_opf(op);
    }
}

static int pointed_size(CType *type)
{
    int align;
    return type_size(pointed_type(type), &align);
}

static void vla_runtime_pointed_size(CType *type)
{
    int align;
    vla_runtime_type_size(pointed_type(type), &align);
}

static inline int is_null_pointer(SValue *p)
{
    if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
        return 0;
    return ((p->type.t & VT_BTYPE) == VT_INT && (uint32_t)p->c.i == 0) ||
        ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.i == 0) ||
        ((p->type.t & VT_BTYPE) == VT_PTR &&
         (PTR_SIZE == 4 ? (uint32_t)p->c.i == 0 : p->c.i == 0));
}

static inline int is_integer_btype(int bt)
{
    return (bt == VT_BYTE || bt == VT_SHORT || 
            bt == VT_INT || bt == VT_LLONG);
}

/* check types for comparison or subtraction of pointers */
static void check_comparison_pointer_types(SValue *p1, SValue *p2, int op)
{
    CType *type1, *type2, tmp_type1, tmp_type2;
    int bt1, bt2;
    
    /* null pointers are accepted for all comparisons as gcc */
    if (is_null_pointer(p1) || is_null_pointer(p2))
        return;
    type1 = &p1->type;
    type2 = &p2->type;
    bt1 = type1->t & VT_BTYPE;
    bt2 = type2->t & VT_BTYPE;
    /* accept comparison between pointer and integer with a warning */
    if ((is_integer_btype(bt1) || is_integer_btype(bt2)) && op != '-') {
        if (op != TOK_LOR && op != TOK_LAND )
            tcc_warning("comparison between pointer and integer");
        return;
    }

    /* both must be pointers or implicit function pointers */
    if (bt1 == VT_PTR) {
        type1 = pointed_type(type1);
    } else if (bt1 != VT_FUNC) 
        goto invalid_operands;

    if (bt2 == VT_PTR) {
        type2 = pointed_type(type2);
    } else if (bt2 != VT_FUNC) { 
    invalid_operands:
        tcc_error("invalid operands to binary %s", get_tok_str(op, NULL));
    }
    if ((type1->t & VT_BTYPE) == VT_VOID || 
        (type2->t & VT_BTYPE) == VT_VOID)
        return;
    tmp_type1 = *type1;
    tmp_type2 = *type2;
    tmp_type1.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
    tmp_type2.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
    if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
        /* gcc-like error if '-' is used */
        if (op == '-')
            goto invalid_operands;
        else
            tcc_warning("comparison of distinct pointer types lacks a cast");
    }
}

/* generic gen_op: handles types problems */
ST_FUNC void gen_op(int op)
{
    int u, t1, t2, bt1, bt2, t;
    CType type1;

redo:
    t1 = vtop[-1].type.t;
    t2 = vtop[0].type.t;
    bt1 = t1 & VT_BTYPE;
    bt2 = t2 & VT_BTYPE;
        
    if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
        tcc_error("operation on a struct");
    } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) {
    if (bt2 == VT_FUNC) {
        mk_pointer(&vtop->type);
        gaddrof();
    }
    if (bt1 == VT_FUNC) {
        vswap();
        mk_pointer(&vtop->type);
        gaddrof();
        vswap();
    }
    goto redo;
    } else if (bt1 == VT_PTR || bt2 == VT_PTR) {
        /* at least one operand is a pointer */
        /* relational op: must be both pointers */
        if (op >= TOK_ULT && op <= TOK_LOR) {
            check_comparison_pointer_types(vtop - 1, vtop, op);
            /* pointers are handled are unsigned */
#if PTR_SIZE == 8
            t = VT_LLONG | VT_UNSIGNED;
#else
            t = VT_INT | VT_UNSIGNED;
#endif
            goto std_op;
        }
        /* if both pointers, then it must be the '-' op */
        if (bt1 == VT_PTR && bt2 == VT_PTR) {
            if (op != '-')
                tcc_error("cannot use pointers here");
            check_comparison_pointer_types(vtop - 1, vtop, op);
            /* XXX: check that types are compatible */
            if (vtop[-1].type.t & VT_VLA) {
                vla_runtime_pointed_size(&vtop[-1].type);
            } else {
                vpushi(pointed_size(&vtop[-1].type));
            }
            vrott(3);
            gen_opic(op);
            vtop->type.t = ptrdiff_type.t;
            vswap();
            gen_op(TOK_PDIV);
        } else {
            /* exactly one pointer : must be '+' or '-'. */
            if (op != '-' && op != '+')
                tcc_error("cannot use pointers here");
            /* Put pointer as first operand */
            if (bt2 == VT_PTR) {
                vswap();
                t = t1, t1 = t2, t2 = t;
            }
#if PTR_SIZE == 4
            if ((vtop[0].type.t & VT_BTYPE) == VT_LLONG)
                /* XXX: truncate here because gen_opl can't handle ptr + long long */
                gen_cast_s(VT_INT);
#endif
            type1 = vtop[-1].type;
            type1.t &= ~VT_ARRAY;
            if (vtop[-1].type.t & VT_VLA)
                vla_runtime_pointed_size(&vtop[-1].type);
            else {
                u = pointed_size(&vtop[-1].type);
                if (u < 0)
                    tcc_error("unknown array element size");
#if PTR_SIZE == 8
                vpushll(u);
#else
                /* XXX: cast to int ? (long long case) */
                vpushi(u);
#endif
            }
            gen_op('*');
#if 0
/* #ifdef CONFIG_TCC_BCHECK
    The main reason to removing this code:
    #include <stdio.h>
    int main ()
    {
        int v[10];
        int i = 10;
        int j = 9;
        fprintf(stderr, "v+i-j  = %p\n", v+i-j);
        fprintf(stderr, "v+(i-j)  = %p\n", v+(i-j));
    }
    When this code is on. then the output looks like 
    v+i-j = 0xfffffffe
    v+(i-j) = 0xbff84000
    */
            /* if evaluating constant expression, no code should be
               generated, so no bound check */
            if (tcc_state->do_bounds_check && !const_wanted) {
                /* if bounded pointers, we generate a special code to
                   test bounds */
                if (op == '-') {
                    vpushi(0);
                    vswap();
                    gen_op('-');
                }
                gen_bounded_ptr_add();
            } else
#endif
            {
                gen_opic(op);
            }
            /* put again type if gen_opic() swaped operands */
            vtop->type = type1;
        }
    } else if (is_float(bt1) || is_float(bt2)) {
        /* compute bigger type and do implicit casts */
        if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
            t = VT_LDOUBLE;
        } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
            t = VT_DOUBLE;
        } else {
            t = VT_FLOAT;
        }
        /* floats can only be used for a few operations */
        if (op != '+' && op != '-' && op != '*' && op != '/' &&
            (op < TOK_ULT || op > TOK_GT))
            tcc_error("invalid operands for binary operation");
        goto std_op;
    } else if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL) {
        t = bt1 == VT_LLONG ? VT_LLONG : VT_INT;
        if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (t | VT_UNSIGNED))
          t |= VT_UNSIGNED;
        t |= (VT_LONG & t1);
        goto std_op;
    } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
        /* cast to biggest op */
        t = VT_LLONG | VT_LONG;
        if (bt1 == VT_LLONG)
            t &= t1;
        if (bt2 == VT_LLONG)
            t &= t2;
        /* convert to unsigned if it does not fit in a long long */
        if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED) ||
            (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED))
            t |= VT_UNSIGNED;
        goto std_op;
    } else {
        /* integer operations */
        t = VT_INT | (VT_LONG & (t1 | t2));
        /* convert to unsigned if it does not fit in an integer */
        if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED) ||
            (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED))
            t |= VT_UNSIGNED;
    std_op:
        /* XXX: currently, some unsigned operations are explicit, so
           we modify them here */
        if (t & VT_UNSIGNED) {
            if (op == TOK_SAR)
                op = TOK_SHR;
            else if (op == '/')
                op = TOK_UDIV;
            else if (op == '%')
                op = TOK_UMOD;
            else if (op == TOK_LT)
                op = TOK_ULT;
            else if (op == TOK_GT)
                op = TOK_UGT;
            else if (op == TOK_LE)
                op = TOK_ULE;
            else if (op == TOK_GE)
                op = TOK_UGE;
        }
        vswap();
        type1.t = t;
        type1.ref = NULL;
        gen_cast(&type1);
        vswap();
        /* special case for shifts and long long: we keep the shift as
           an integer */
        if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL)
            type1.t = VT_INT;
        gen_cast(&type1);
        if (is_float(t))
            gen_opif(op);
        else
            gen_opic(op);
        if (op >= TOK_ULT && op <= TOK_GT) {
            /* relational op: the result is an int */
            vtop->type.t = VT_INT;
        } else {
            vtop->type.t = t;
        }
    }
    // Make sure that we have converted to an rvalue:
    if (vtop->r & VT_LVAL)
        gv(is_float(vtop->type.t & VT_BTYPE) ? RC_FLOAT : RC_INT);
}

#ifndef TCC_TARGET_ARM
/* generic itof for unsigned long long case */
static void gen_cvt_itof1(int t)
{
#ifdef TCC_TARGET_ARM64
    gen_cvt_itof(t);
#else
    if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == 
        (VT_LLONG | VT_UNSIGNED)) {

        if (t == VT_FLOAT)
            vpush_global_sym(&func_old_type, TOK___floatundisf);
#if LDOUBLE_SIZE != 8
        else if (t == VT_LDOUBLE)
            vpush_global_sym(&func_old_type, TOK___floatundixf);
#endif
        else
            vpush_global_sym(&func_old_type, TOK___floatundidf);
        vrott(2);
        gfunc_call(1);
        vpushi(0);
        vtop->r = reg_fret(t);
    } else {
        gen_cvt_itof(t);
    }
#endif
}
#endif

/* generic ftoi for unsigned long long case */
static void gen_cvt_ftoi1(int t)
{
#ifdef TCC_TARGET_ARM64
    gen_cvt_ftoi(t);
#else
    int st;

    if (t == (VT_LLONG | VT_UNSIGNED)) {
        /* not handled natively */
        st = vtop->type.t & VT_BTYPE;
        if (st == VT_FLOAT)
            vpush_global_sym(&func_old_type, TOK___fixunssfdi);
#if LDOUBLE_SIZE != 8
        else if (st == VT_LDOUBLE)
            vpush_global_sym(&func_old_type, TOK___fixunsxfdi);
#endif
        else
            vpush_global_sym(&func_old_type, TOK___fixunsdfdi);
        vrott(2);
        gfunc_call(1);
        vpushi(0);
        vtop->r = REG_IRET;
        vtop->r2 = REG_LRET;
    } else {
        gen_cvt_ftoi(t);
    }
#endif
}

/* force char or short cast */
static void force_charshort_cast(int t)
{
    int bits, dbt;

    /* cannot cast static initializers */
    if (STATIC_DATA_WANTED)
    return;

    dbt = t & VT_BTYPE;
    /* XXX: add optimization if lvalue : just change type and offset */
    if (dbt == VT_BYTE)
        bits = 8;
    else
        bits = 16;
    if (t & VT_UNSIGNED) {
        vpushi((1 << bits) - 1);
        gen_op('&');
    } else {
        if ((vtop->type.t & VT_BTYPE) == VT_LLONG)
            bits = 64 - bits;
        else
            bits = 32 - bits;
        vpushi(bits);
        gen_op(TOK_SHL);
        /* result must be signed or the SAR is converted to an SHL
           This was not the case when "t" was a signed short
           and the last value on the stack was an unsigned int */
        vtop->type.t &= ~VT_UNSIGNED;
        vpushi(bits);
        gen_op(TOK_SAR);
    }
}

/* cast 'vtop' to 'type'. Casting to bitfields is forbidden. */
static void gen_cast_s(int t)
{
    CType type;
    type.t = t;
    type.ref = NULL;
    gen_cast(&type);
}

static void gen_cast(CType *type)
{
    int sbt, dbt, sf, df, c, p;

    /* special delayed cast for char/short */
    /* XXX: in some cases (multiple cascaded casts), it may still
       be incorrect */
    if (vtop->r & VT_MUSTCAST) {
        vtop->r &= ~VT_MUSTCAST;
        force_charshort_cast(vtop->type.t);
    }

    /* bitfields first get cast to ints */
    if (vtop->type.t & VT_BITFIELD) {
        gv(RC_INT);
    }

    dbt = type->t & (VT_BTYPE | VT_UNSIGNED);
    sbt = vtop->type.t & (VT_BTYPE | VT_UNSIGNED);

    if (sbt != dbt) {
        sf = is_float(sbt);
        df = is_float(dbt);
        c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
        p = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM);
#if !defined TCC_IS_NATIVE && !defined TCC_IS_NATIVE_387
        c &= dbt != VT_LDOUBLE;
#endif
        if (c) {
            /* constant case: we can do it now */
            /* XXX: in ISOC, cannot do it if error in convert */
            if (sbt == VT_FLOAT)
                vtop->c.ld = vtop->c.f;
            else if (sbt == VT_DOUBLE)
                vtop->c.ld = vtop->c.d;

            if (df) {
                if ((sbt & VT_BTYPE) == VT_LLONG) {
                    if ((sbt & VT_UNSIGNED) || !(vtop->c.i >> 63))
                        vtop->c.ld = vtop->c.i;
                    else
                        vtop->c.ld = -(long double)-vtop->c.i;
                } else if(!sf) {
                    if ((sbt & VT_UNSIGNED) || !(vtop->c.i >> 31))
                        vtop->c.ld = (uint32_t)vtop->c.i;
                    else
                        vtop->c.ld = -(long double)-(uint32_t)vtop->c.i;
                }

                if (dbt == VT_FLOAT)
                    vtop->c.f = (float)vtop->c.ld;
                else if (dbt == VT_DOUBLE)
                    vtop->c.d = (double)vtop->c.ld;
            } else if (sf && dbt == (VT_LLONG|VT_UNSIGNED)) {
                vtop->c.i = vtop->c.ld;
            } else if (sf && dbt == VT_BOOL) {
                vtop->c.i = (vtop->c.ld != 0);
            } else {
                if(sf)
                    vtop->c.i = vtop->c.ld;
                else if (sbt == (VT_LLONG|VT_UNSIGNED))
                    ;
                else if (sbt & VT_UNSIGNED)
                    vtop->c.i = (uint32_t)vtop->c.i;
#if PTR_SIZE == 8
                else if (sbt == VT_PTR)
                    ;
#endif
                else if (sbt != VT_LLONG)
                    vtop->c.i = ((uint32_t)vtop->c.i |
                                  -(vtop->c.i & 0x80000000));

                if (dbt == (VT_LLONG|VT_UNSIGNED))
                    ;
                else if (dbt == VT_BOOL)
                    vtop->c.i = (vtop->c.i != 0);
#if PTR_SIZE == 8
                else if (dbt == VT_PTR)
                    ;
#endif
                else if (dbt != VT_LLONG) {
                    uint32_t m = ((dbt & VT_BTYPE) == VT_BYTE ? 0xff :
                                  (dbt & VT_BTYPE) == VT_SHORT ? 0xffff :
                                  0xffffffff);
                    vtop->c.i &= m;
                    if (!(dbt & VT_UNSIGNED))
                        vtop->c.i |= -(vtop->c.i & ((m >> 1) + 1));
                }
            }
        } else if (p && dbt == VT_BOOL) {
            vtop->r = VT_CONST;
            vtop->c.i = 1;
        } else {
            /* non constant case: generate code */
            if (sf && df) {
                /* convert from fp to fp */
                gen_cvt_ftof(dbt);
            } else if (df) {
                /* convert int to fp */
                gen_cvt_itof1(dbt);
            } else if (sf) {
                /* convert fp to int */
                if (dbt == VT_BOOL) {
                     vpushi(0);
                     gen_op(TOK_NE);
                } else {
                    /* we handle char/short/etc... with generic code */
                    if (dbt != (VT_INT | VT_UNSIGNED) &&
                        dbt != (VT_LLONG | VT_UNSIGNED) &&
                        dbt != VT_LLONG)
                        dbt = VT_INT;
                    gen_cvt_ftoi1(dbt);
                    if (dbt == VT_INT && (type->t & (VT_BTYPE | VT_UNSIGNED)) != dbt) {
                        /* additional cast for char/short... */
                        vtop->type.t = dbt;
                        gen_cast(type);
                    }
                }
#if PTR_SIZE == 4
            } else if ((dbt & VT_BTYPE) == VT_LLONG) {
                if ((sbt & VT_BTYPE) != VT_LLONG) {
                    /* scalar to long long */
                    /* machine independent conversion */
                    gv(RC_INT);
                    /* generate high word */
                    if (sbt == (VT_INT | VT_UNSIGNED)) {
                        vpushi(0);
                        gv(RC_INT);
                    } else {
                        if (sbt == VT_PTR) {
                            /* cast from pointer to int before we apply
                               shift operation, which pointers don't support*/
                            gen_cast_s(VT_INT);
                        }
                        gv_dup();
                        vpushi(31);
                        gen_op(TOK_SAR);
                    }
                    /* patch second register */
                    vtop[-1].r2 = vtop->r;
                    vpop();
                }
#else
            } else if ((dbt & VT_BTYPE) == VT_LLONG ||
                       (dbt & VT_BTYPE) == VT_PTR ||
                       (dbt & VT_BTYPE) == VT_FUNC) {
                if ((sbt & VT_BTYPE) != VT_LLONG &&
                    (sbt & VT_BTYPE) != VT_PTR &&
                    (sbt & VT_BTYPE) != VT_FUNC) {
                    /* need to convert from 32bit to 64bit */
                    gv(RC_INT);
                    if (sbt != (VT_INT | VT_UNSIGNED)) {
#if defined(TCC_TARGET_ARM64)
                        gen_cvt_sxtw();
#elif defined(TCC_TARGET_X86_64)
                        int r = gv(RC_INT);
                        /* x86_64 specific: movslq */
                        o(0x6348);
                        o(0xc0 + (REG_VALUE(r) << 3) + REG_VALUE(r));
#else
#error
#endif
                    }
                }
#endif
            } else if (dbt == VT_BOOL) {
                /* scalar to bool */
                vpushi(0);
                gen_op(TOK_NE);
            } else if ((dbt & VT_BTYPE) == VT_BYTE || 
                       (dbt & VT_BTYPE) == VT_SHORT) {
                if (sbt == VT_PTR) {
                    vtop->type.t = VT_INT;
                    tcc_warning("nonportable conversion from pointer to char/short");
                }
                force_charshort_cast(dbt);
#if PTR_SIZE == 4
            } else if ((dbt & VT_BTYPE) == VT_INT) {
                /* scalar to int */
                if ((sbt & VT_BTYPE) == VT_LLONG) {
                    /* from long long: just take low order word */
                    lexpand();
                    vpop();
                } 
                /* if lvalue and single word type, nothing to do because
                   the lvalue already contains the real type size (see
                   VT_LVAL_xxx constants) */
#endif
            }
        }
    } else if ((dbt & VT_BTYPE) == VT_PTR && !(vtop->r & VT_LVAL)) {
        /* if we are casting between pointer types,
           we must update the VT_LVAL_xxx size */
        vtop->r = (vtop->r & ~VT_LVAL_TYPE)
                  | (lvalue_type(type->ref->type.t) & VT_LVAL_TYPE);
    }
    vtop->type = *type;
}

/* return type size as known at compile time. Put alignment at 'a' */
ST_FUNC int type_size(CType *type, int *a)
{
    Sym *s;
    int bt;

    bt = type->t & VT_BTYPE;
    if (bt == VT_STRUCT) {
        /* struct/union */
        s = type->ref;
        *a = s->r;
        return s->c;
    } else if (bt == VT_PTR) {
        if (type->t & VT_ARRAY) {
            int ts;

            s = type->ref;
            ts = type_size(&s->type, a);

            if (ts < 0 && s->c < 0)
                ts = -ts;

            return ts * s->c;
        } else {
            *a = PTR_SIZE;
            return PTR_SIZE;
        }
    } else if (IS_ENUM(type->t) && type->ref->c == -1) {
        return -1; /* incomplete enum */
    } else if (bt == VT_LDOUBLE) {
        *a = LDOUBLE_ALIGN;
        return LDOUBLE_SIZE;
    } else if (bt == VT_DOUBLE || bt == VT_LLONG) {
#ifdef TCC_TARGET_I386
#ifdef TCC_TARGET_PE
        *a = 8;
#else
        *a = 4;
#endif
#elif defined(TCC_TARGET_ARM)
#ifdef TCC_ARM_EABI
        *a = 8; 
#else
        *a = 4;
#endif
#else
        *a = 8;
#endif
        return 8;
    } else if (bt == VT_INT || bt == VT_FLOAT) {
        *a = 4;
        return 4;
    } else if (bt == VT_SHORT) {
        *a = 2;
        return 2;
    } else if (bt == VT_QLONG || bt == VT_QFLOAT) {
        *a = 8;
        return 16;
    } else {
        /* char, void, function, _Bool */
        *a = 1;
        return 1;
    }
}

/* push type size as known at runtime time on top of value stack. Put
   alignment at 'a' */
ST_FUNC void vla_runtime_type_size(CType *type, int *a)
{
    if (type->t & VT_VLA) {
        type_size(&type->ref->type, a);
        vset(&int_type, VT_LOCAL|VT_LVAL, type->ref->c);
    } else {
        vpushi(type_size(type, a));
    }
}

static void vla_sp_restore(void) {
    if (vlas_in_scope) {
        gen_vla_sp_restore(vla_sp_loc);
    }
}

static void vla_sp_restore_root(void) {
    if (vlas_in_scope) {
        gen_vla_sp_restore(vla_sp_root_loc);
    }
}

/* return the pointed type of t */
static inline CType *pointed_type(CType *type)
{
    return &type->ref->type;
}

/* modify type so that its it is a pointer to type. */
ST_FUNC void mk_pointer(CType *type)
{
    Sym *s;
    s = sym_push(SYM_FIELD, type, 0, -1);
    type->t = VT_PTR | (type->t & VT_STORAGE);
    type->ref = s;
}

/* compare function types. OLD functions match any new functions */
static int is_compatible_func(CType *type1, CType *type2)
{
    Sym *s1, *s2;

    s1 = type1->ref;
    s2 = type2->ref;
    if (!is_compatible_types(&s1->type, &s2->type))
        return 0;
    /* check func_call */
    if (s1->f.func_call != s2->f.func_call)
        return 0;
    /* XXX: not complete */
    if (s1->f.func_type == FUNC_OLD || s2->f.func_type == FUNC_OLD)
        return 1;
    if (s1->f.func_type != s2->f.func_type)
        return 0;
    while (s1 != NULL) {
        if (s2 == NULL)
            return 0;
        if (!is_compatible_unqualified_types(&s1->type, &s2->type))
            return 0;
        s1 = s1->next;
        s2 = s2->next;
    }
    if (s2)
        return 0;
    return 1;
}

/* return true if type1 and type2 are the same.  If unqualified is
   true, qualifiers on the types are ignored.

   - enums are not checked as gcc __builtin_types_compatible_p () 
 */
static int compare_types(CType *type1, CType *type2, int unqualified)
{
    int bt1, t1, t2;

    t1 = type1->t & VT_TYPE;
    t2 = type2->t & VT_TYPE;
    if (unqualified) {
        /* strip qualifiers before comparing */
        t1 &= ~(VT_CONSTANT | VT_VOLATILE);
        t2 &= ~(VT_CONSTANT | VT_VOLATILE);
    }

    /* Default Vs explicit signedness only matters for char */
    if ((t1 & VT_BTYPE) != VT_BYTE) {
        t1 &= ~VT_DEFSIGN;
        t2 &= ~VT_DEFSIGN;
    }
    /* XXX: bitfields ? */
    if (t1 != t2)
        return 0;
    /* test more complicated cases */
    bt1 = t1 & VT_BTYPE;
    if (bt1 == VT_PTR) {
        type1 = pointed_type(type1);
        type2 = pointed_type(type2);
        return is_compatible_types(type1, type2);
    } else if (bt1 == VT_STRUCT) {
        return (type1->ref == type2->ref);
    } else if (bt1 == VT_FUNC) {
        return is_compatible_func(type1, type2);
    } else {
        return 1;
    }
}

/* return true if type1 and type2 are exactly the same (including
   qualifiers). 
*/
static int is_compatible_types(CType *type1, CType *type2)
{
    return compare_types(type1,type2,0);
}

/* return true if type1 and type2 are the same (ignoring qualifiers).
*/
static int is_compatible_unqualified_types(CType *type1, CType *type2)
{
    return compare_types(type1,type2,1);
}

/* print a type. If 'varstr' is not NULL, then the variable is also
   printed in the type */
/* XXX: union */
/* XXX: add array and function pointers */
static void type_to_str(char *buf, int buf_size, 
                 CType *type, const char *varstr)
{
    int bt, v, t;
    Sym *s, *sa;
    char buf1[256];
    const char *tstr;

    t = type->t;
    bt = t & VT_BTYPE;
    buf[0] = '\0';

    if (t & VT_EXTERN)
        pstrcat(buf, buf_size, "extern ");
    if (t & VT_STATIC)
        pstrcat(buf, buf_size, "static ");
    if (t & VT_TYPEDEF)
        pstrcat(buf, buf_size, "typedef ");
    if (t & VT_INLINE)
        pstrcat(buf, buf_size, "inline ");
    if (t & VT_VOLATILE)
        pstrcat(buf, buf_size, "volatile ");
    if (t & VT_CONSTANT)
        pstrcat(buf, buf_size, "const ");

    if (((t & VT_DEFSIGN) && bt == VT_BYTE)
        || ((t & VT_UNSIGNED)
            && (bt == VT_SHORT || bt == VT_INT || bt == VT_LLONG)
            && !IS_ENUM(t)
            ))
        pstrcat(buf, buf_size, (t & VT_UNSIGNED) ? "unsigned " : "signed ");

    buf_size -= strlen(buf);
    buf += strlen(buf);

    switch(bt) {
    case VT_VOID:
        tstr = "void";
        goto add_tstr;
    case VT_BOOL:
        tstr = "_Bool";
        goto add_tstr;
    case VT_BYTE:
        tstr = "char";
        goto add_tstr;
    case VT_SHORT:
        tstr = "short";
        goto add_tstr;
    case VT_INT:
        tstr = "int";
        goto maybe_long;
    case VT_LLONG:
        tstr = "long long";
    maybe_long:
        if (t & VT_LONG)
            tstr = "long";
        if (!IS_ENUM(t))
            goto add_tstr;
        tstr = "enum ";
        goto tstruct;
    case VT_FLOAT:
        tstr = "float";
        goto add_tstr;
    case VT_DOUBLE:
        tstr = "double";
        goto add_tstr;
    case VT_LDOUBLE:
        tstr = "long double";
    add_tstr:
        pstrcat(buf, buf_size, tstr);
        break;
    case VT_STRUCT:
        tstr = "struct ";
        if (IS_UNION(t))
            tstr = "union ";
    tstruct:
        pstrcat(buf, buf_size, tstr);
        v = type->ref->v & ~SYM_STRUCT;
        if (v >= SYM_FIRST_ANOM)
            pstrcat(buf, buf_size, "<anonymous>");
        else
            pstrcat(buf, buf_size, get_tok_str(v, NULL));
        break;
    case VT_FUNC:
        s = type->ref;
        type_to_str(buf, buf_size, &s->type, varstr);
        pstrcat(buf, buf_size, "(");
        sa = s->next;
        while (sa != NULL) {
            type_to_str(buf1, sizeof(buf1), &sa->type, NULL);
            pstrcat(buf, buf_size, buf1);
            sa = sa->next;
            if (sa)
                pstrcat(buf, buf_size, ", ");
        }
        pstrcat(buf, buf_size, ")");
        goto no_var;
    case VT_PTR:
        s = type->ref;
        if (t & VT_ARRAY) {
            snprintf(buf1, sizeof(buf1), "%s[%d]", varstr ? varstr : "", s->c);
            type_to_str(buf, buf_size, &s->type, buf1);
            goto no_var;
        }
        pstrcpy(buf1, sizeof(buf1), "*");
        if (t & VT_CONSTANT)
            pstrcat(buf1, buf_size, "const ");
        if (t & VT_VOLATILE)
            pstrcat(buf1, buf_size, "volatile ");
        if (varstr)
            pstrcat(buf1, sizeof(buf1), varstr);
        type_to_str(buf, buf_size, &s->type, buf1);
        goto no_var;
    }
    if (varstr) {
        pstrcat(buf, buf_size, " ");
        pstrcat(buf, buf_size, varstr);
    }
 no_var: ;
}

/* verify type compatibility to store vtop in 'dt' type, and generate
   casts if needed. */
static void gen_assign_cast(CType *dt)
{
    CType *st, *type1, *type2;
    char buf1[256], buf2[256];
    int dbt, sbt;

    st = &vtop->type; /* source type */
    dbt = dt->t & VT_BTYPE;
    sbt = st->t & VT_BTYPE;
    if (sbt == VT_VOID || dbt == VT_VOID) {
    if (sbt == VT_VOID && dbt == VT_VOID)
        ; /*
          It is Ok if both are void
          A test program:
            void func1() {}
        void func2() {
          return func1();
        }
          gcc accepts this program
          */
    else
            tcc_error("cannot cast from/to void");
    }
    if (dt->t & VT_CONSTANT)
        tcc_warning("assignment of read-only location");
    switch(dbt) {
    case VT_PTR:
        /* special cases for pointers */
        /* '0' can also be a pointer */
        if (is_null_pointer(vtop))
            goto type_ok;
        /* accept implicit pointer to integer cast with warning */
        if (is_integer_btype(sbt)) {
            tcc_warning("assignment makes pointer from integer without a cast");
            goto type_ok;
        }
        type1 = pointed_type(dt);
        /* a function is implicitly a function pointer */
        if (sbt == VT_FUNC) {
            if ((type1->t & VT_BTYPE) != VT_VOID &&
                !is_compatible_types(pointed_type(dt), st))
                tcc_warning("assignment from incompatible pointer type");
            goto type_ok;
        }
        if (sbt != VT_PTR)
            goto error;
        type2 = pointed_type(st);
        if ((type1->t & VT_BTYPE) == VT_VOID || 
            (type2->t & VT_BTYPE) == VT_VOID) {
            /* void * can match anything */
        } else {
            //printf("types %08x %08x\n", type1->t, type2->t);
            /* exact type match, except for qualifiers */
            if (!is_compatible_unqualified_types(type1, type2)) {
        /* Like GCC don't warn by default for merely changes
           in pointer target signedness.  Do warn for different
           base types, though, in particular for unsigned enums
           and signed int targets.  */
        if ((type1->t & (VT_BTYPE|VT_LONG)) != (type2->t & (VT_BTYPE|VT_LONG))
                    || IS_ENUM(type1->t) || IS_ENUM(type2->t)
                    )
            tcc_warning("assignment from incompatible pointer type");
        }
        }
        /* check const and volatile */
        if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) ||
            (!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE)))
            tcc_warning("assignment discards qualifiers from pointer target type");
        break;
    case VT_BYTE:
    case VT_SHORT:
    case VT_INT:
    case VT_LLONG:
        if (sbt == VT_PTR || sbt == VT_FUNC) {
            tcc_warning("assignment makes integer from pointer without a cast");
        } else if (sbt == VT_STRUCT) {
            goto case_VT_STRUCT;
        }
        /* XXX: more tests */
        break;
    case VT_STRUCT:
    case_VT_STRUCT:
        if (!is_compatible_unqualified_types(dt, st)) {
        error:
            type_to_str(buf1, sizeof(buf1), st, NULL);
            type_to_str(buf2, sizeof(buf2), dt, NULL);
            tcc_error("cannot cast '%s' to '%s'", buf1, buf2);
        }
        break;
    }
 type_ok:
    gen_cast(dt);
}

/* store vtop in lvalue pushed on stack */
ST_FUNC void vstore(void)
{
    int sbt, dbt, ft, r, t, size, align, bit_size, bit_pos, rc, delayed_cast;

    ft = vtop[-1].type.t;
    sbt = vtop->type.t & VT_BTYPE;
    dbt = ft & VT_BTYPE;
    if ((((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) ||
         (sbt == VT_INT && dbt == VT_SHORT))
    && !(vtop->type.t & VT_BITFIELD)) {
        /* optimize char/short casts */
        delayed_cast = VT_MUSTCAST;
        vtop->type.t = ft & VT_TYPE;
        /* XXX: factorize */
        if (ft & VT_CONSTANT)
            tcc_warning("assignment of read-only location");
    } else {
        delayed_cast = 0;
        if (!(ft & VT_BITFIELD))
            gen_assign_cast(&vtop[-1].type);
    }

    if (sbt == VT_STRUCT) {
        /* if structure, only generate pointer */
        /* structure assignment : generate memcpy */
        /* XXX: optimize if small size */
            size = type_size(&vtop->type, &align);

            /* destination */
            vswap();
            vtop->type.t = VT_PTR;
            gaddrof();

            /* address of memcpy() */
#ifdef TCC_ARM_EABI
            if(!(align & 7))
                vpush_global_sym(&func_old_type, TOK_memcpy8);
            else if(!(align & 3))
                vpush_global_sym(&func_old_type, TOK_memcpy4);
            else
#endif
            /* Use memmove, rather than memcpy, as dest and src may be same: */
            vpush_global_sym(&func_old_type, TOK_memmove);

            vswap();
            /* source */
            vpushv(vtop - 2);
            vtop->type.t = VT_PTR;
            gaddrof();
            /* type size */
            vpushi(size);
            gfunc_call(3);

        /* leave source on stack */
    } else if (ft & VT_BITFIELD) {
        /* bitfield store handling */

        /* save lvalue as expression result (example: s.b = s.a = n;) */
        vdup(), vtop[-1] = vtop[-2];

        bit_pos = BIT_POS(ft);
        bit_size = BIT_SIZE(ft);
        /* remove bit field info to avoid loops */
        vtop[-1].type.t = ft & ~VT_STRUCT_MASK;

        if ((ft & VT_BTYPE) == VT_BOOL) {
            gen_cast(&vtop[-1].type);
            vtop[-1].type.t = (vtop[-1].type.t & ~VT_BTYPE) | (VT_BYTE | VT_UNSIGNED);
        }

        r = adjust_bf(vtop - 1, bit_pos, bit_size);
        if (r == VT_STRUCT) {
            gen_cast_s((ft & VT_BTYPE) == VT_LLONG ? VT_LLONG : VT_INT);
            store_packed_bf(bit_pos, bit_size);
        } else {
            unsigned long long mask = (1ULL << bit_size) - 1;
            if ((ft & VT_BTYPE) != VT_BOOL) {
                /* mask source */
                if ((vtop[-1].type.t & VT_BTYPE) == VT_LLONG)
                    vpushll(mask);
                else
                    vpushi((unsigned)mask);
                gen_op('&');
            }
            /* shift source */
            vpushi(bit_pos);
            gen_op(TOK_SHL);
            vswap();
            /* duplicate destination */
            vdup();
            vrott(3);
            /* load destination, mask and or with source */
            if ((vtop->type.t & VT_BTYPE) == VT_LLONG)
                vpushll(~(mask << bit_pos));
            else
                vpushi(~((unsigned)mask << bit_pos));
            gen_op('&');
            gen_op('|');
            /* store result */
            vstore();
            /* ... and discard */
            vpop();
        }
    } else if (dbt == VT_VOID) {
        --vtop;
    } else {
#ifdef CONFIG_TCC_BCHECK
            /* bound check case */
            if (vtop[-1].r & VT_MUSTBOUND) {
                vswap();
                gbound();
                vswap();
            }
#endif
            rc = RC_INT;
            if (is_float(ft)) {
                rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64
                if ((ft & VT_BTYPE) == VT_LDOUBLE) {
                    rc = RC_ST0;
                } else if ((ft & VT_BTYPE) == VT_QFLOAT) {
                    rc = RC_FRET;
                }
#endif
            }
            r = gv(rc);  /* generate value */
            /* if lvalue was saved on stack, must read it */
            if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) {
                SValue sv;
                t = get_reg(RC_INT);
#if PTR_SIZE == 8
                sv.type.t = VT_PTR;
#else
                sv.type.t = VT_INT;
#endif
                sv.r = VT_LOCAL | VT_LVAL;
                sv.c.i = vtop[-1].c.i;
                load(t, &sv);
                vtop[-1].r = t | VT_LVAL;
            }
            /* two word case handling : store second register at word + 4 (or +8 for x86-64)  */
#if PTR_SIZE == 8
            if (((ft & VT_BTYPE) == VT_QLONG) || ((ft & VT_BTYPE) == VT_QFLOAT)) {
                int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
#else
            if ((ft & VT_BTYPE) == VT_LLONG) {
                int addr_type = VT_INT, load_size = 4, load_type = VT_INT;
#endif
                vtop[-1].type.t = load_type;
                store(r, vtop - 1);
                vswap();
                /* convert to int to increment easily */
                vtop->type.t = addr_type;
                gaddrof();
                vpushi(load_size);
                gen_op('+');
                vtop->r |= VT_LVAL;
                vswap();
                vtop[-1].type.t = load_type;
                /* XXX: it works because r2 is spilled last ! */
                store(vtop->r2, vtop - 1);
            } else {
                store(r, vtop - 1);
            }

        vswap();
        vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
        vtop->r |= delayed_cast;
    }
}

/* post defines POST/PRE add. c is the token ++ or -- */
ST_FUNC void inc(int post, int c)
{
    test_lvalue();
    vdup(); /* save lvalue */
    if (post) {
        gv_dup(); /* duplicate value */
        vrotb(3);
        vrotb(3);
    }
    /* add constant */
    vpushi(c - TOK_MID); 
    gen_op('+');
    vstore(); /* store value */
    if (post)
        vpop(); /* if post op, return saved value */
}

ST_FUNC void parse_mult_str (CString *astr, const char *msg)
{
    /* read the string */
    if (tok != TOK_STR)
        expect(msg);
    cstr_new(astr);
    while (tok == TOK_STR) {
        /* XXX: add \0 handling too ? */
        cstr_cat(astr, tokc.str.data, -1);
        next();
    }
    cstr_ccat(astr, '\0');
}

/* If I is >= 1 and a power of two, returns log2(i)+1.
   If I is 0 returns 0.  */
static int exact_log2p1(int i)
{
  int ret;
  if (!i)
    return 0;
  for (ret = 1; i >= 1 << 8; ret += 8)
    i >>= 8;
  if (i >= 1 << 4)
    ret += 4, i >>= 4;
  if (i >= 1 << 2)
    ret += 2, i >>= 2;
  if (i >= 1 << 1)
    ret++;
  return ret;
}

/* Parse __attribute__((...)) GNUC extension. */
static void parse_attribute(AttributeDef *ad)
{
    int t, n;
    CString astr;
    
redo:
    if (tok != TOK_ATTRIBUTE1 && tok != TOK_ATTRIBUTE2)
        return;
    next();
    skip('(');
    skip('(');
    while (tok != ')') {
        if (tok < TOK_IDENT)
            expect("attribute name");
        t = tok;
        next();
        switch(t) {
        case TOK_SECTION1:
        case TOK_SECTION2:
            skip('(');
        parse_mult_str(&astr, "section name");
            ad->section = find_section(tcc_state, (char *)astr.data);
            skip(')');
        cstr_free(&astr);
            break;
        case TOK_ALIAS1:
        case TOK_ALIAS2:
            skip('(');
        parse_mult_str(&astr, "alias(\"target\")");
            ad->alias_target = /* save string as token, for later */
              tok_alloc((char*)astr.data, astr.size-1)->tok;
            skip(')');
        cstr_free(&astr);
            break;
    case TOK_VISIBILITY1:
    case TOK_VISIBILITY2:
            skip('(');
        parse_mult_str(&astr,
               "visibility(\"default|hidden|internal|protected\")");
        if (!strcmp (astr.data, "default"))
            ad->a.visibility = STV_DEFAULT;
        else if (!strcmp (astr.data, "hidden"))
            ad->a.visibility = STV_HIDDEN;
        else if (!strcmp (astr.data, "internal"))
            ad->a.visibility = STV_INTERNAL;
        else if (!strcmp (astr.data, "protected"))
            ad->a.visibility = STV_PROTECTED;
        else
                expect("visibility(\"default|hidden|internal|protected\")");
            skip(')');
        cstr_free(&astr);
            break;
        case TOK_ALIGNED1:
        case TOK_ALIGNED2:
            if (tok == '(') {
                next();
                n = expr_const();
                if (n <= 0 || (n & (n - 1)) != 0) 
                    tcc_error("alignment must be a positive power of two");
                skip(')');
            } else {
                n = MAX_ALIGN;
            }
            ad->a.aligned = exact_log2p1(n);
        if (n != 1 << (ad->a.aligned - 1))
          tcc_error("alignment of %d is larger than implemented", n);
            break;
        case TOK_PACKED1:
        case TOK_PACKED2:
            ad->a.packed = 1;
            break;
        case TOK_WEAK1:
        case TOK_WEAK2:
            ad->a.weak = 1;
            break;
        case TOK_UNUSED1:
        case TOK_UNUSED2:
            /* currently, no need to handle it because tcc does not
               track unused objects */
            break;
        case TOK_NORETURN1:
        case TOK_NORETURN2:
            /* currently, no need to handle it because tcc does not
               track unused objects */
            break;
        case TOK_CDECL1:
        case TOK_CDECL2:
        case TOK_CDECL3:
            ad->f.func_call = FUNC_CDECL;
            break;
        case TOK_STDCALL1:
        case TOK_STDCALL2:
        case TOK_STDCALL3:
            ad->f.func_call = FUNC_STDCALL;
            break;
#ifdef TCC_TARGET_I386
        case TOK_REGPARM1:
        case TOK_REGPARM2:
            skip('(');
            n = expr_const();
            if (n > 3) 
                n = 3;
            else if (n < 0)
                n = 0;
            if (n > 0)
                ad->f.func_call = FUNC_FASTCALL1 + n - 1;
            skip(')');
            break;
        case TOK_FASTCALL1:
        case TOK_FASTCALL2:
        case TOK_FASTCALL3:
            ad->f.func_call = FUNC_FASTCALLW;
            break;            
#endif
        case TOK_MODE:
            skip('(');
            switch(tok) {
                case TOK_MODE_DI:
                    ad->attr_mode = VT_LLONG + 1;
                    break;
                case TOK_MODE_QI:
                    ad->attr_mode = VT_BYTE + 1;
                    break;
                case TOK_MODE_HI:
                    ad->attr_mode = VT_SHORT + 1;
                    break;
                case TOK_MODE_SI:
                case TOK_MODE_word:
                    ad->attr_mode = VT_INT + 1;
                    break;
                default:
                    tcc_warning("__mode__(%s) not supported\n", get_tok_str(tok, NULL));
                    break;
            }
            next();
            skip(')');
            break;
        case TOK_DLLEXPORT:
            ad->a.dllexport = 1;
            break;
        case TOK_DLLIMPORT:
            ad->a.dllimport = 1;
            break;
        default:
            if (tcc_state->warn_unsupported)
                tcc_warning("'%s' attribute ignored", get_tok_str(t, NULL));
            /* skip parameters */
            if (tok == '(') {
                int parenthesis = 0;
                do {
                    if (tok == '(') 
                        parenthesis++;
                    else if (tok == ')') 
                        parenthesis--;
                    next();
                } while (parenthesis && tok != -1);
            }
            break;
        }
        if (tok != ',')
            break;
        next();
    }
    skip(')');
    skip(')');
    goto redo;
}

static Sym * find_field (CType *type, int v)
{
    Sym *s = type->ref;
    v |= SYM_FIELD;
    while ((s = s->next) != NULL) {
    if ((s->v & SYM_FIELD) &&
        (s->type.t & VT_BTYPE) == VT_STRUCT &&
        (s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM) {
        Sym *ret = find_field (&s->type, v);
        if (ret)
            return ret;
    }
    if (s->v == v)
      break;
    }
    return s;
}

static void struct_add_offset (Sym *s, int offset)
{
    while ((s = s->next) != NULL) {
    if ((s->v & SYM_FIELD) &&
        (s->type.t & VT_BTYPE) == VT_STRUCT &&
        (s->v & ~SYM_FIELD) >= SYM_FIRST_ANOM) {
        struct_add_offset(s->type.ref, offset);
    } else
      s->c += offset;
    }
}

static void struct_layout(CType *type, AttributeDef *ad)
{
    int size, align, maxalign, offset, c, bit_pos, bit_size;
    int packed, a, bt, prevbt, prev_bit_size;
    int pcc = !tcc_state->ms_bitfields;
    int pragma_pack = *tcc_state->pack_stack_ptr;
    Sym *f;

    maxalign = 1;
    offset = 0;
    c = 0;
    bit_pos = 0;
    prevbt = VT_STRUCT; /* make it never match */
    prev_bit_size = 0;

//#define BF_DEBUG

    for (f = type->ref->next; f; f = f->next) {
        if (f->type.t & VT_BITFIELD)
            bit_size = BIT_SIZE(f->type.t);
        else
            bit_size = -1;
        size = type_size(&f->type, &align);
        a = f->a.aligned ? 1 << (f->a.aligned - 1) : 0;
        packed = 0;

        if (pcc && bit_size == 0) {
            /* in pcc mode, packing does not affect zero-width bitfields */

        } else {
            /* in pcc mode, attribute packed overrides if set. */
            if (pcc && (f->a.packed || ad->a.packed))
                align = packed = 1;

            /* pragma pack overrides align if lesser and packs bitfields always */
            if (pragma_pack) {
                packed = 1;
                if (pragma_pack < align)
                    align = pragma_pack;
                /* in pcc mode pragma pack also overrides individual align */
                if (pcc && pragma_pack < a)
                    a = 0;
            }
        }
        /* some individual align was specified */
        if (a)
            align = a;

        if (type->ref->type.t == VT_UNION) {
        if (pcc && bit_size >= 0)
            size = (bit_size + 7) >> 3;
        offset = 0;
        if (size > c)
            c = size;

    } else if (bit_size < 0) {
            if (pcc)
                c += (bit_pos + 7) >> 3;
        c = (c + align - 1) & -align;
        offset = c;
        if (size > 0)
            c += size;
        bit_pos = 0;
        prevbt = VT_STRUCT;
        prev_bit_size = 0;

    } else {
        /* A bit-field.  Layout is more complicated.  There are two
           options: PCC (GCC) compatible and MS compatible */
            if (pcc) {
        /* In PCC layout a bit-field is placed adjacent to the
                   preceding bit-fields, except if:
                   - it has zero-width
                   - an individual alignment was given
                   - it would overflow its base type container and
                     there is no packing */
                if (bit_size == 0) {
            new_field:
            c = (c + ((bit_pos + 7) >> 3) + align - 1) & -align;
            bit_pos = 0;
                } else if (f->a.aligned) {
                    goto new_field;
                } else if (!packed) {
                    int a8 = align * 8;
                int ofs = ((c * 8 + bit_pos) % a8 + bit_size + a8 - 1) / a8;
                    if (ofs > size / align)
                        goto new_field;
                }

                /* in pcc mode, long long bitfields have type int if they fit */
                if (size == 8 && bit_size <= 32)
                    f->type.t = (f->type.t & ~VT_BTYPE) | VT_INT, size = 4;

                while (bit_pos >= align * 8)
                    c += align, bit_pos -= align * 8;
                offset = c;

        /* In PCC layout named bit-fields influence the alignment
           of the containing struct using the base types alignment,
           except for packed fields (which here have correct align).  */
        if (f->v & SYM_FIRST_ANOM
                    // && bit_size // ??? gcc on ARM/rpi does that
                    )
            align = 1;

        } else {
        bt = f->type.t & VT_BTYPE;
        if ((bit_pos + bit_size > size * 8)
                    || (bit_size > 0) == (bt != prevbt)
                    ) {
            c = (c + align - 1) & -align;
            offset = c;
            bit_pos = 0;
            /* In MS bitfield mode a bit-field run always uses
               at least as many bits as the underlying type.
               To start a new run it's also required that this
               or the last bit-field had non-zero width.  */
            if (bit_size || prev_bit_size)
                c += size;
        }
        /* In MS layout the records alignment is normally
           influenced by the field, except for a zero-width
           field at the start of a run (but by further zero-width
           fields it is again).  */
        if (bit_size == 0 && prevbt != bt)
            align = 1;
        prevbt = bt;
                prev_bit_size = bit_size;
        }

        f->type.t = (f->type.t & ~(0x3f << VT_STRUCT_SHIFT))
                | (bit_pos << VT_STRUCT_SHIFT);
        bit_pos += bit_size;
    }
    if (align > maxalign)
        maxalign = align;

#ifdef BF_DEBUG
    printf("set field %s offset %-2d size %-2d align %-2d",
           get_tok_str(f->v & ~SYM_FIELD, NULL), offset, size, align);
    if (f->type.t & VT_BITFIELD) {
        printf(" pos %-2d bits %-2d",
                    BIT_POS(f->type.t),
                    BIT_SIZE(f->type.t)
                    );
    }
    printf("\n");
#endif

    if (f->v & SYM_FIRST_ANOM && (f->type.t & VT_BTYPE) == VT_STRUCT) {
        Sym *ass;
        /* An anonymous struct/union.  Adjust member offsets
           to reflect the real offset of our containing struct.
           Also set the offset of this anon member inside
           the outer struct to be zero.  Via this it
           works when accessing the field offset directly
           (from base object), as well as when recursing
           members in initializer handling.  */
        int v2 = f->type.ref->v;
        if (!(v2 & SYM_FIELD) &&
        (v2 & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
        Sym **pps;
        /* This happens only with MS extensions.  The
           anon member has a named struct type, so it
           potentially is shared with other references.
           We need to unshare members so we can modify
           them.  */
        ass = f->type.ref;
        f->type.ref = sym_push(anon_sym++ | SYM_FIELD,
                       &f->type.ref->type, 0,
                       f->type.ref->c);
        pps = &f->type.ref->next;
        while ((ass = ass->next) != NULL) {
            *pps = sym_push(ass->v, &ass->type, 0, ass->c);
            pps = &((*pps)->next);
        }
        *pps = NULL;
        }
        struct_add_offset(f->type.ref, offset);
        f->c = 0;
    } else {
        f->c = offset;
    }

    f->r = 0;
    }

    if (pcc)
        c += (bit_pos + 7) >> 3;

    /* store size and alignment */
    a = bt = ad->a.aligned ? 1 << (ad->a.aligned - 1) : 1;
    if (a < maxalign)
        a = maxalign;
    type->ref->r = a;
    if (pragma_pack && pragma_pack < maxalign && 0 == pcc) {
        /* can happen if individual align for some member was given.  In
           this case MSVC ignores maxalign when aligning the size */
        a = pragma_pack;
        if (a < bt)
            a = bt;
    }
    c = (c + a - 1) & -a;
    type->ref->c = c;

#ifdef BF_DEBUG
    printf("struct size %-2d align %-2d\n\n", c, a), fflush(stdout);
#endif

    /* check whether we can access bitfields by their type */
    for (f = type->ref->next; f; f = f->next) {
        int s, px, cx, c0;
        CType t;

        if (0 == (f->type.t & VT_BITFIELD))
            continue;
        f->type.ref = f;
        f->auxtype = -1;
        bit_size = BIT_SIZE(f->type.t);
        if (bit_size == 0)
            continue;
        bit_pos = BIT_POS(f->type.t);
        size = type_size(&f->type, &align);
        if (bit_pos + bit_size <= size * 8 && f->c + size <= c)
            continue;

        /* try to access the field using a different type */
        c0 = -1, s = align = 1;
        for (;;) {
            px = f->c * 8 + bit_pos;
            cx = (px >> 3) & -align;
            px = px - (cx << 3);
            if (c0 == cx)
                break;
            s = (px + bit_size + 7) >> 3;
            if (s > 4) {
                t.t = VT_LLONG;
            } else if (s > 2) {
                t.t = VT_INT;
            } else if (s > 1) {
                t.t = VT_SHORT;
            } else {
                t.t = VT_BYTE;
            }
            s = type_size(&t, &align);
            c0 = cx;
        }

        if (px + bit_size <= s * 8 && cx + s <= c) {
            /* update offset and bit position */
            f->c = cx;
            bit_pos = px;
        f->type.t = (f->type.t & ~(0x3f << VT_STRUCT_SHIFT))
                | (bit_pos << VT_STRUCT_SHIFT);
            if (s != size)
                f->auxtype = t.t;
#ifdef BF_DEBUG
            printf("FIX field %s offset %-2d size %-2d align %-2d "
                "pos %-2d bits %-2d\n",
                get_tok_str(f->v & ~SYM_FIELD, NULL),
                cx, s, align, px, bit_size);
#endif
        } else {
            /* fall back to load/store single-byte wise */
            f->auxtype = VT_STRUCT;
#ifdef BF_DEBUG
            printf("FIX field %s : load byte-wise\n",
                 get_tok_str(f->v & ~SYM_FIELD, NULL));
#endif
        }
    }
}

/* enum/struct/union declaration. u is VT_ENUM/VT_STRUCT/VT_UNION */
static void struct_decl(CType *type, int u)
{
    int v, c, size, align, flexible;
    int bit_size, bsize, bt;
    Sym *s, *ss, **ps;
    AttributeDef ad, ad1;
    CType type1, btype;

    memset(&ad, 0, sizeof ad);
    next();
    parse_attribute(&ad);
    if (tok != '{') {
        v = tok;
        next();
        /* struct already defined ? return it */
        if (v < TOK_IDENT)
            expect("struct/union/enum name");
        s = struct_find(v);
        if (s && (s->sym_scope == local_scope || tok != '{')) {
            if (u == s->type.t)
                goto do_decl;
            if (u == VT_ENUM && IS_ENUM(s->type.t))
                goto do_decl;
            tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
        }
    } else {
        v = anon_sym++;
    }
    /* Record the original enum/struct/union token.  */
    type1.t = u == VT_ENUM ? u | VT_INT | VT_UNSIGNED : u;
    type1.ref = NULL;
    /* we put an undefined size for struct/union */
    s = sym_push(v | SYM_STRUCT, &type1, 0, -1);
    s->r = 0; /* default alignment is zero as gcc */
do_decl:
    type->t = s->type.t;
    type->ref = s;

    if (tok == '{') {
        next();
        if (s->c != -1)
            tcc_error("struct/union/enum already defined");
        /* cannot be empty */
        /* non empty enums are not allowed */
        ps = &s->next;
        if (u == VT_ENUM) {
            long long ll = 0, pl = 0, nl = 0;
        CType t;
            t.ref = s;
            /* enum symbols have static storage */
            t.t = VT_INT|VT_STATIC|VT_ENUM_VAL;
            for(;;) {
                v = tok;
                if (v < TOK_UIDENT)
                    expect("identifier");
                ss = sym_find(v);
                if (ss && !local_stack)
                    tcc_error("redefinition of enumerator '%s'",
                              get_tok_str(v, NULL));
                next();
                if (tok == '=') {
                    next();
            ll = expr_const64();
                }
                ss = sym_push(v, &t, VT_CONST, 0);
                ss->enum_val = ll;
                *ps = ss, ps = &ss->next;
                if (ll < nl)
                    nl = ll;
                if (ll > pl)
                    pl = ll;
                if (tok != ',')
                    break;
                next();
                ll++;
                /* NOTE: we accept a trailing comma */
                if (tok == '}')
                    break;
            }
            skip('}');
            /* set integral type of the enum */
            t.t = VT_INT;
            if (nl >= 0) {
                if (pl != (unsigned)pl)
                    t.t = (LONG_SIZE==8 ? VT_LLONG|VT_LONG : VT_LLONG);
                t.t |= VT_UNSIGNED;
            } else if (pl != (int)pl || nl != (int)nl)
                t.t = (LONG_SIZE==8 ? VT_LLONG|VT_LONG : VT_LLONG);
            s->type.t = type->t = t.t | VT_ENUM;
            s->c = 0;
            /* set type for enum members */
            for (ss = s->next; ss; ss = ss->next) {
                ll = ss->enum_val;
                if (ll == (int)ll) /* default is int if it fits */
                    continue;
                if (t.t & VT_UNSIGNED) {
                    ss->type.t |= VT_UNSIGNED;
                    if (ll == (unsigned)ll)
                        continue;
                }
                ss->type.t = (ss->type.t & ~VT_BTYPE)
                    | (LONG_SIZE==8 ? VT_LLONG|VT_LONG : VT_LLONG);
            }
        } else {
            c = 0;
            flexible = 0;
            while (tok != '}') {
                if (!parse_btype(&btype, &ad1)) {
            skip(';');
            continue;
        }
                while (1) {
            if (flexible)
                tcc_error("flexible array member '%s' not at the end of struct",
                              get_tok_str(v, NULL));
                    bit_size = -1;
                    v = 0;
                    type1 = btype;
                    if (tok != ':') {
            if (tok != ';')
                            type_decl(&type1, &ad1, &v, TYPE_DIRECT);
                        if (v == 0) {
                            if ((type1.t & VT_BTYPE) != VT_STRUCT)
                            expect("identifier");
                            else {
                int v = btype.ref->v;
                if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
                    if (tcc_state->ms_extensions == 0)
                                expect("identifier");
                }
                            }
                        }
                        if (type_size(&type1, &align) < 0) {
                if ((u == VT_STRUCT) && (type1.t & VT_ARRAY) && c)
                    flexible = 1;
                else
                    tcc_error("field '%s' has incomplete type",
                                      get_tok_str(v, NULL));
                        }
                        if ((type1.t & VT_BTYPE) == VT_FUNC ||
                            (type1.t & VT_STORAGE))
                            tcc_error("invalid type for '%s'", 
                                  get_tok_str(v, NULL));
                    }
                    if (tok == ':') {
                        next();
                        bit_size = expr_const();
                        /* XXX: handle v = 0 case for messages */
                        if (bit_size < 0)
                            tcc_error("negative width in bit-field '%s'", 
                                  get_tok_str(v, NULL));
                        if (v && bit_size == 0)
                            tcc_error("zero width for bit-field '%s'", 
                                  get_tok_str(v, NULL));
            parse_attribute(&ad1);
                    }
                    size = type_size(&type1, &align);
                    if (bit_size >= 0) {
                        bt = type1.t & VT_BTYPE;
                        if (bt != VT_INT && 
                            bt != VT_BYTE && 
                            bt != VT_SHORT &&
                            bt != VT_BOOL &&
                            bt != VT_LLONG)
                            tcc_error("bitfields must have scalar type");
                        bsize = size * 8;
                        if (bit_size > bsize) {
                            tcc_error("width of '%s' exceeds its type",
                                  get_tok_str(v, NULL));
                        } else if (bit_size == bsize
                                    && !ad.a.packed && !ad1.a.packed) {
                            /* no need for bit fields */
                            ;
                        } else if (bit_size == 64) {
                            tcc_error("field width 64 not implemented");
                        } else {
                            type1.t = (type1.t & ~VT_STRUCT_MASK)
                                | VT_BITFIELD
                                | (bit_size << (VT_STRUCT_SHIFT + 6));
                        }
                    }
                    if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) {
                        /* Remember we've seen a real field to check
               for placement of flexible array member. */
            c = 1;
                    }
            /* If member is a struct or bit-field, enforce
               placing into the struct (as anonymous).  */
                    if (v == 0 &&
            ((type1.t & VT_BTYPE) == VT_STRUCT ||
             bit_size >= 0)) {
                v = anon_sym++;
            }
                    if (v) {
                        ss = sym_push(v | SYM_FIELD, &type1, 0, 0);
                        ss->a = ad1.a;
                        *ps = ss;
                        ps = &ss->next;
                    }
                    if (tok == ';' || tok == TOK_EOF)
                        break;
                    skip(',');
                }
                skip(';');
            }
            skip('}');
        parse_attribute(&ad);
        struct_layout(type, &ad);
        }
    }
}

static void sym_to_attr(AttributeDef *ad, Sym *s)
{
    if (s->a.aligned && 0 == ad->a.aligned)
        ad->a.aligned = s->a.aligned;
    if (s->f.func_call && 0 == ad->f.func_call)
        ad->f.func_call = s->f.func_call;
    if (s->f.func_type && 0 == ad->f.func_type)
        ad->f.func_type = s->f.func_type;
    if (s->a.packed)
        ad->a.packed = 1;
}

/* Add type qualifiers to a type. If the type is an array then the qualifiers
   are added to the element type, copied because it could be a typedef. */
static void parse_btype_qualify(CType *type, int qualifiers)
{
    while (type->t & VT_ARRAY) {
        type->ref = sym_push(SYM_FIELD, &type->ref->type, 0, type->ref->c);
        type = &type->ref->type;
    }
    type->t |= qualifiers;
}

/* return 0 if no type declaration. otherwise, return the basic type
   and skip it. 
 */
static int parse_btype(CType *type, AttributeDef *ad)
{
    int t, u, bt, st, type_found, typespec_found, g;
    Sym *s;
    CType type1;

    memset(ad, 0, sizeof(AttributeDef));
    type_found = 0;
    typespec_found = 0;
    t = VT_INT;
    bt = st = -1;
    type->ref = NULL;

    while(1) {
        switch(tok) {
        case TOK_EXTENSION:
            /* currently, we really ignore extension */
            next();
            continue;

            /* basic types */
        case TOK_CHAR:
            u = VT_BYTE;
        basic_type:
            next();
        basic_type1:
            if (u == VT_SHORT || u == VT_LONG) {
                if (st != -1 || (bt != -1 && bt != VT_INT))
                    tmbt: tcc_error("too many basic types");
                st = u;
            } else {
                if (bt != -1 || (st != -1 && u != VT_INT))
                    goto tmbt;
                bt = u;
            }
            if (u != VT_INT)
                t = (t & ~(VT_BTYPE|VT_LONG)) | u;
            typespec_found = 1;
            break;
        case TOK_VOID:
            u = VT_VOID;
            goto basic_type;
        case TOK_SHORT:
            u = VT_SHORT;
            goto basic_type;
        case TOK_INT:
            u = VT_INT;
            goto basic_type;
        case TOK_LONG:
            if ((t & VT_BTYPE) == VT_DOUBLE) {
                t = (t & ~(VT_BTYPE|VT_LONG)) | VT_LDOUBLE;
            } else if ((t & (VT_BTYPE|VT_LONG)) == VT_LONG) {
                t = (t & ~(VT_BTYPE|VT_LONG)) | VT_LLONG;
            } else {
                u = VT_LONG;
                goto basic_type;
            }
            next();
            break;
#ifdef TCC_TARGET_ARM64
        case TOK_UINT128:
            /* GCC's __uint128_t appears in some Linux header files. Make it a
               synonym for long double to get the size and alignment right. */
            u = VT_LDOUBLE;
            goto basic_type;
#endif
        case TOK_BOOL:
            u = VT_BOOL;
            goto basic_type;
        case TOK_FLOAT:
            u = VT_FLOAT;
            goto basic_type;
        case TOK_DOUBLE:
            if ((t & (VT_BTYPE|VT_LONG)) == VT_LONG) {
                t = (t & ~(VT_BTYPE|VT_LONG)) | VT_LDOUBLE;
            } else {
                u = VT_DOUBLE;
                goto basic_type;
            }
            next();
            break;
        case TOK_ENUM:
            struct_decl(&type1, VT_ENUM);
        basic_type2:
            u = type1.t;
            type->ref = type1.ref;
            goto basic_type1;
        case TOK_STRUCT:
            struct_decl(&type1, VT_STRUCT);
            goto basic_type2;
        case TOK_UNION:
            struct_decl(&type1, VT_UNION);
            goto basic_type2;

            /* type modifiers */
        case TOK_CONST1:
        case TOK_CONST2:
        case TOK_CONST3:
            type->t = t;
            parse_btype_qualify(type, VT_CONSTANT);
            t = type->t;
            next();
            break;
        case TOK_VOLATILE1:
        case TOK_VOLATILE2:
        case TOK_VOLATILE3:
            type->t = t;
            parse_btype_qualify(type, VT_VOLATILE);
            t = type->t;
            next();
            break;
        case TOK_SIGNED1:
        case TOK_SIGNED2:
        case TOK_SIGNED3:
            if ((t & (VT_DEFSIGN|VT_UNSIGNED)) == (VT_DEFSIGN|VT_UNSIGNED))
                tcc_error("signed and unsigned modifier");
            t |= VT_DEFSIGN;
            next();
            typespec_found = 1;
            break;
        case TOK_REGISTER:
        case TOK_AUTO:
        case TOK_RESTRICT1:
        case TOK_RESTRICT2:
        case TOK_RESTRICT3:
            next();
            break;
        case TOK_UNSIGNED:
            if ((t & (VT_DEFSIGN|VT_UNSIGNED)) == VT_DEFSIGN)
                tcc_error("signed and unsigned modifier");
            t |= VT_DEFSIGN | VT_UNSIGNED;
            next();
            typespec_found = 1;
            break;

            /* storage */
        case TOK_EXTERN:
            g = VT_EXTERN;
            goto storage;
        case TOK_STATIC:
            g = VT_STATIC;
            goto storage;
        case TOK_TYPEDEF:
            g = VT_TYPEDEF;
            goto storage;
       storage:
            if (t & (VT_EXTERN|VT_STATIC|VT_TYPEDEF) & ~g)
                tcc_error("multiple storage classes");
            t |= g;
            next();
            break;
        case TOK_INLINE1:
        case TOK_INLINE2:
        case TOK_INLINE3:
            t |= VT_INLINE;
            next();
            break;

            /* GNUC attribute */
        case TOK_ATTRIBUTE1:
        case TOK_ATTRIBUTE2:
            parse_attribute(ad);
            if (ad->attr_mode) {
                u = ad->attr_mode -1;
                t = (t & ~(VT_BTYPE|VT_LONG)) | u;
            }
            break;
            /* GNUC typeof */
        case TOK_TYPEOF1:
        case TOK_TYPEOF2:
        case TOK_TYPEOF3:
            next();
            parse_expr_type(&type1);
            /* remove all storage modifiers except typedef */
            type1.t &= ~(VT_STORAGE&~VT_TYPEDEF);
        if (type1.ref)
                sym_to_attr(ad, type1.ref);
            goto basic_type2;
        default:
            if (typespec_found)
                goto the_end;
            s = sym_find(tok);
            if (!s || !(s->type.t & VT_TYPEDEF))
                goto the_end;
            t &= ~(VT_BTYPE|VT_LONG);
            u = t & ~(VT_CONSTANT | VT_VOLATILE), t ^= u;
            type->t = (s->type.t & ~VT_TYPEDEF) | u;
            type->ref = s->type.ref;
            if (t)
                parse_btype_qualify(type, t);
            t = type->t;
            /* get attributes from typedef */
            sym_to_attr(ad, s);
            next();
            typespec_found = 1;
            st = bt = -2;
            break;
        }
        type_found = 1;
    }
the_end:
    if (tcc_state->char_is_unsigned) {
        if ((t & (VT_DEFSIGN|VT_BTYPE)) == VT_BYTE)
            t |= VT_UNSIGNED;
    }
    /* VT_LONG is used just as a modifier for VT_INT / VT_LLONG */
    bt = t & (VT_BTYPE|VT_LONG);
    if (bt == VT_LONG)
        t |= LONG_SIZE == 8 ? VT_LLONG : VT_INT;
#ifdef TCC_TARGET_PE
    if (bt == VT_LDOUBLE)
        t = (t & ~(VT_BTYPE|VT_LONG)) | VT_DOUBLE;
#endif
    type->t = t;
    return type_found;
}

/* convert a function parameter type (array to pointer and function to
   function pointer) */
static inline void convert_parameter_type(CType *pt)
{
    /* remove const and volatile qualifiers (XXX: const could be used
       to indicate a const function parameter */
    pt->t &= ~(VT_CONSTANT | VT_VOLATILE);
    /* array must be transformed to pointer according to ANSI C */
    pt->t &= ~VT_ARRAY;
    if ((pt->t & VT_BTYPE) == VT_FUNC) {
        mk_pointer(pt);
    }
}

ST_FUNC void parse_asm_str(CString *astr)
{
    skip('(');
    parse_mult_str(astr, "string constant");
}

/* Parse an asm label and return the token */
static int asm_label_instr(void)
{
    int v;
    CString astr;

    next();
    parse_asm_str(&astr);
    skip(')');
#ifdef ASM_DEBUG
    printf("asm_alias: \"%s\"\n", (char *)astr.data);
#endif
    v = tok_alloc(astr.data, astr.size - 1)->tok;
    cstr_free(&astr);
    return v;
}

static int post_type(CType *type, AttributeDef *ad, int storage, int td)
{
    int n, l, t1, arg_size, align;
    Sym **plast, *s, *first;
    AttributeDef ad1;
    CType pt;

    if (tok == '(') {
        /* function type, or recursive declarator (return if so) */
        next();
    if (td && !(td & TYPE_ABSTRACT))
      return 0;
    if (tok == ')')
      l = 0;
    else if (parse_btype(&pt, &ad1))
      l = FUNC_NEW;
    else if (td)
      return 0;
    else
      l = FUNC_OLD;
        first = NULL;
        plast = &first;
        arg_size = 0;
        if (l) {
            for(;;) {
                /* read param name and compute offset */
                if (l != FUNC_OLD) {
                    if ((pt.t & VT_BTYPE) == VT_VOID && tok == ')')
                        break;
                    type_decl(&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT);
                    if ((pt.t & VT_BTYPE) == VT_VOID)
                        tcc_error("parameter declared as void");
                    arg_size += (type_size(&pt, &align) + PTR_SIZE - 1) / PTR_SIZE;
                } else {
                    n = tok;
                    if (n < TOK_UIDENT)
                        expect("identifier");
                    pt.t = VT_VOID; /* invalid type */
                    next();
                }
                convert_parameter_type(&pt);
                s = sym_push(n | SYM_FIELD, &pt, 0, 0);
                *plast = s;
                plast = &s->next;
                if (tok == ')')
                    break;
                skip(',');
                if (l == FUNC_NEW && tok == TOK_DOTS) {
                    l = FUNC_ELLIPSIS;
                    next();
                    break;
                }
        if (l == FUNC_NEW && !parse_btype(&pt, &ad1))
            tcc_error("invalid type");
            }
        } else
            /* if no parameters, then old type prototype */
            l = FUNC_OLD;
        skip(')');
        /* NOTE: const is ignored in returned type as it has a special
           meaning in gcc / C++ */
        type->t &= ~VT_CONSTANT; 
        /* some ancient pre-K&R C allows a function to return an array
           and the array brackets to be put after the arguments, such 
           that "int c()[]" means something like "int[] c()" */
        if (tok == '[') {
            next();
            skip(']'); /* only handle simple "[]" */
            mk_pointer(type);
        }
        /* we push a anonymous symbol which will contain the function prototype */
        ad->f.func_args = arg_size;
        ad->f.func_type = l;
        s = sym_push(SYM_FIELD, type, 0, 0);
        s->a = ad->a;
        s->f = ad->f;
        s->next = first;
        type->t = VT_FUNC;
        type->ref = s;
    } else if (tok == '[') {
    int saved_nocode_wanted = nocode_wanted;
        /* array definition */
        next();
        if (tok == TOK_RESTRICT1)
            next();
        n = -1;
        t1 = 0;
        if (tok != ']') {
            if (!local_stack || (storage & VT_STATIC))
                vpushi(expr_const());
            else {
        /* VLAs (which can only happen with local_stack && !VT_STATIC)
           length must always be evaluated, even under nocode_wanted,
           so that its size slot is initialized (e.g. under sizeof
           or typeof).  */
        nocode_wanted = 0;
        gexpr();
        }
            if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
                n = vtop->c.i;
                if (n < 0)
                    tcc_error("invalid array size");
            } else {
                if (!is_integer_btype(vtop->type.t & VT_BTYPE))
                    tcc_error("size of variable length array should be an integer");
                t1 = VT_VLA;
            }
        }
        skip(']');
        /* parse next post type */
        post_type(type, ad, storage, 0);
        if (type->t == VT_FUNC)
            tcc_error("declaration of an array of functions");
        t1 |= type->t & VT_VLA;
        
        if (t1 & VT_VLA) {
            loc -= type_size(&int_type, &align);
            loc &= -align;
            n = loc;

            vla_runtime_type_size(type, &align);
            gen_op('*');
            vset(&int_type, VT_LOCAL|VT_LVAL, n);
            vswap();
            vstore();
        }
        if (n != -1)
            vpop();
    nocode_wanted = saved_nocode_wanted;
                
        /* we push an anonymous symbol which will contain the array
           element type */
        s = sym_push(SYM_FIELD, type, 0, n);
        type->t = (t1 ? VT_VLA : VT_ARRAY) | VT_PTR;
        type->ref = s;
    }
    return 1;
}

/* Parse a type declarator (except basic type), and return the type
   in 'type'. 'td' is a bitmask indicating which kind of type decl is
   expected. 'type' should contain the basic type. 'ad' is the
   attribute definition of the basic type. It can be modified by
   type_decl().  If this (possibly abstract) declarator is a pointer chain
   it returns the innermost pointed to type (equals *type, but is a different
   pointer), otherwise returns type itself, that's used for recursive calls.  */
static CType *type_decl(CType *type, AttributeDef *ad, int *v, int td)
{
    CType *post, *ret;
    int qualifiers, storage;

    /* recursive type, remove storage bits first, apply them later again */
    storage = type->t & VT_STORAGE;
    type->t &= ~VT_STORAGE;
    post = ret = type;

    while (tok == '*') {
        qualifiers = 0;
    redo:
        next();
        switch(tok) {
        case TOK_CONST1:
        case TOK_CONST2:
        case TOK_CONST3:
            qualifiers |= VT_CONSTANT;
            goto redo;
        case TOK_VOLATILE1:
        case TOK_VOLATILE2:
        case TOK_VOLATILE3:
            qualifiers |= VT_VOLATILE;
            goto redo;
        case TOK_RESTRICT1:
        case TOK_RESTRICT2:
        case TOK_RESTRICT3:
            goto redo;
    /* XXX: clarify attribute handling */
    case TOK_ATTRIBUTE1:
    case TOK_ATTRIBUTE2:
        parse_attribute(ad);
        break;
        }
        mk_pointer(type);
        type->t |= qualifiers;
    if (ret == type)
        /* innermost pointed to type is the one for the first derivation */
        ret = pointed_type(type);
    }

    if (tok == '(') {
    /* This is possibly a parameter type list for abstract declarators
       ('int ()'), use post_type for testing this.  */
    if (!post_type(type, ad, 0, td)) {
        /* It's not, so it's a nested declarator, and the post operations
           apply to the innermost pointed to type (if any).  */
        /* XXX: this is not correct to modify 'ad' at this point, but
           the syntax is not clear */
        parse_attribute(ad);
        post = type_decl(type, ad, v, td);
        skip(')');
    }
    } else if (tok >= TOK_IDENT && (td & TYPE_DIRECT)) {
    /* type identifier */
    *v = tok;
    next();
    } else {
    if (!(td & TYPE_ABSTRACT))
      expect("identifier");
    *v = 0;
    }
    post_type(post, ad, storage, 0);
    parse_attribute(ad);
    type->t |= storage;
    return ret;
}

/* compute the lvalue VT_LVAL_xxx needed to match type t. */
ST_FUNC int lvalue_type(int t)
{
    int bt, r;
    r = VT_LVAL;
    bt = t & VT_BTYPE;
    if (bt == VT_BYTE || bt == VT_BOOL)
        r |= VT_LVAL_BYTE;
    else if (bt == VT_SHORT)
        r |= VT_LVAL_SHORT;
    else
        return r;
    if (t & VT_UNSIGNED)
        r |= VT_LVAL_UNSIGNED;
    return r;
}

/* indirection with full error checking and bound check */
ST_FUNC void indir(void)
{
    if ((vtop->type.t & VT_BTYPE) != VT_PTR) {
        if ((vtop->type.t & VT_BTYPE) == VT_FUNC)
            return;
        expect("pointer");
    }
    if (vtop->r & VT_LVAL)
        gv(RC_INT);
    vtop->type = *pointed_type(&vtop->type);
    /* Arrays and functions are never lvalues */
    if (!(vtop->type.t & VT_ARRAY) && !(vtop->type.t & VT_VLA)
        && (vtop->type.t & VT_BTYPE) != VT_FUNC) {
        vtop->r |= lvalue_type(vtop->type.t);
        /* if bound checking, the referenced pointer must be checked */
#ifdef CONFIG_TCC_BCHECK
        if (tcc_state->do_bounds_check)
            vtop->r |= VT_MUSTBOUND;
#endif
    }
}

/* pass a parameter to a function and do type checking and casting */
static void gfunc_param_typed(Sym *func, Sym *arg)
{
    int func_type;
    CType type;

    func_type = func->f.func_type;
    if (func_type == FUNC_OLD ||
        (func_type == FUNC_ELLIPSIS && arg == NULL)) {
        /* default casting : only need to convert float to double */
        if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) {
            gen_cast_s(VT_DOUBLE);
        } else if (vtop->type.t & VT_BITFIELD) {
            type.t = vtop->type.t & (VT_BTYPE | VT_UNSIGNED);
        type.ref = vtop->type.ref;
            gen_cast(&type);
        }
    } else if (arg == NULL) {
        tcc_error("too many arguments to function");
    } else {
        type = arg->type;
        type.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
        gen_assign_cast(&type);
    }
}

/* parse an expression and return its type without any side effect. */
static void expr_type(CType *type, void (*expr_fn)(void))
{
    nocode_wanted++;
    expr_fn();
    *type = vtop->type;
    vpop();
    nocode_wanted--;
}

/* parse an expression of the form '(type)' or '(expr)' and return its
   type */
static void parse_expr_type(CType *type)
{
    int n;
    AttributeDef ad;

    skip('(');
    if (parse_btype(type, &ad)) {
        type_decl(type, &ad, &n, TYPE_ABSTRACT);
    } else {
        expr_type(type, gexpr);
    }
    skip(')');
}

static void parse_type(CType *type)
{
    AttributeDef ad;
    int n;

    if (!parse_btype(type, &ad)) {
        expect("type");
    }
    type_decl(type, &ad, &n, TYPE_ABSTRACT);
}

static void parse_builtin_params(int nc, const char *args)
{
    char c, sep = '(';
    CType t;
    if (nc)
        nocode_wanted++;
    next();
    while ((c = *args++)) {
    skip(sep);
    sep = ',';
    switch (c) {
        case 'e': expr_eq(); continue;
        case 't': parse_type(&t); vpush(&t); continue;
        default: tcc_error("internal error"); break;
    }
    }
    skip(')');
    if (nc)
        nocode_wanted--;
}

ST_FUNC void unary(void)
{
    int n, t, align, size, r, sizeof_caller;
    CType type;
    Sym *s;
    AttributeDef ad;

    sizeof_caller = in_sizeof;
    in_sizeof = 0;
    type.ref = NULL;
    /* XXX: GCC 2.95.3 does not generate a table although it should be
       better here */
 tok_next:
    switch(tok) {
    case TOK_EXTENSION:
        next();
        goto tok_next;
    case TOK_LCHAR:
#ifdef TCC_TARGET_PE
        t = VT_SHORT|VT_UNSIGNED;
        goto push_tokc;
#endif
    case TOK_CINT:
    case TOK_CCHAR: 
    t = VT_INT;
 push_tokc:
    type.t = t;
    vsetc(&type, VT_CONST, &tokc);
        next();
        break;
    case TOK_CUINT:
        t = VT_INT | VT_UNSIGNED;
        goto push_tokc;
    case TOK_CLLONG:
        t = VT_LLONG;
    goto push_tokc;
    case TOK_CULLONG:
        t = VT_LLONG | VT_UNSIGNED;
    goto push_tokc;
    case TOK_CFLOAT:
        t = VT_FLOAT;
    goto push_tokc;
    case TOK_CDOUBLE:
        t = VT_DOUBLE;
    goto push_tokc;
    case TOK_CLDOUBLE:
        t = VT_LDOUBLE;
    goto push_tokc;
    case TOK_CLONG:
        t = (LONG_SIZE == 8 ? VT_LLONG : VT_INT) | VT_LONG;
    goto push_tokc;
    case TOK_CULONG:
        t = (LONG_SIZE == 8 ? VT_LLONG : VT_INT) | VT_LONG | VT_UNSIGNED;
    goto push_tokc;
    case TOK___FUNCTION__:
        if (!gnu_ext)
            goto tok_identifier;
        /* fall thru */
    case TOK___FUNC__:
        {
            void *ptr;
            int len;
            /* special function name identifier */
            len = strlen(funcname) + 1;
            /* generate char[len] type */
            type.t = VT_BYTE;
            mk_pointer(&type);
            type.t |= VT_ARRAY;
            type.ref->c = len;
            vpush_ref(&type, data_section, data_section->data_offset, len);
            if (!NODATA_WANTED) {
                ptr = section_ptr_add(data_section, len);
                memcpy(ptr, funcname, len);
            }
            next();
        }
        break;
    case TOK_LSTR:
#ifdef TCC_TARGET_PE
        t = VT_SHORT | VT_UNSIGNED;
#else
        t = VT_INT;
#endif
        goto str_init;
    case TOK_STR:
        /* string parsing */
        t = VT_BYTE;
        if (tcc_state->char_is_unsigned)
            t = VT_BYTE | VT_UNSIGNED;
    str_init:
        if (tcc_state->warn_write_strings)
            t |= VT_CONSTANT;
        type.t = t;
        mk_pointer(&type);
        type.t |= VT_ARRAY;
        memset(&ad, 0, sizeof(AttributeDef));
        decl_initializer_alloc(&type, &ad, VT_CONST, 2, 0, 0);
        break;
    case '(':
        next();
        /* cast ? */
        if (parse_btype(&type, &ad)) {
            type_decl(&type, &ad, &n, TYPE_ABSTRACT);
            skip(')');
            /* check ISOC99 compound literal */
            if (tok == '{') {
                    /* data is allocated locally by default */
                if (global_expr)
                    r = VT_CONST;
                else
                    r = VT_LOCAL;
                /* all except arrays are lvalues */
                if (!(type.t & VT_ARRAY))
                    r |= lvalue_type(type.t);
                memset(&ad, 0, sizeof(AttributeDef));
                decl_initializer_alloc(&type, &ad, r, 1, 0, 0);
            } else {
                if (sizeof_caller) {
                    vpush(&type);
                    return;
                }
                unary();
                gen_cast(&type);
            }
        } else if (tok == '{') {
        int saved_nocode_wanted = nocode_wanted;
            if (const_wanted)
                tcc_error("expected constant");
            /* save all registers */
            save_regs(0);
            /* statement expression : we do not accept break/continue
               inside as GCC does.  We do retain the nocode_wanted state,
           as statement expressions can't ever be entered from the
           outside, so any reactivation of code emission (from labels
           or loop heads) can be disabled again after the end of it. */
            block(NULL, NULL, 1);
        nocode_wanted = saved_nocode_wanted;
            skip(')');
        } else {
            gexpr();
            skip(')');
        }
        break;
    case '*':
        next();
        unary();
        indir();
        break;
    case '&':
        next();
        unary();
        /* functions names must be treated as function pointers,
           except for unary '&' and sizeof. Since we consider that
           functions are not lvalues, we only have to handle it
           there and in function calls. */
        /* arrays can also be used although they are not lvalues */
        if ((vtop->type.t & VT_BTYPE) != VT_FUNC &&
            !(vtop->type.t & VT_ARRAY))
            test_lvalue();
        mk_pointer(&vtop->type);
        gaddrof();
        break;
    case '!':
        next();
        unary();
        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
            gen_cast_s(VT_BOOL);
            vtop->c.i = !vtop->c.i;
        } else if ((vtop->r & VT_VALMASK) == VT_CMP)
            vtop->c.i ^= 1;
        else {
            save_regs(1);
            vseti(VT_JMP, gvtst(1, 0));
        }
        break;
    case '~':
        next();
        unary();
        vpushi(-1);
        gen_op('^');
        break;
    case '+':
        next();
        unary();
        if ((vtop->type.t & VT_BTYPE) == VT_PTR)
            tcc_error("pointer not accepted for unary plus");
        /* In order to force cast, we add zero, except for floating point
       where we really need an noop (otherwise -0.0 will be transformed
       into +0.0).  */
    if (!is_float(vtop->type.t)) {
        vpushi(0);
        gen_op('+');
    }
        break;
    case TOK_SIZEOF:
    case TOK_ALIGNOF1:
    case TOK_ALIGNOF2:
        t = tok;
        next();
        in_sizeof++;
        expr_type(&type, unary); /* Perform a in_sizeof = 0; */
        s = vtop[1].sym; /* hack: accessing previous vtop */
        size = type_size(&type, &align);
        if (s && s->a.aligned)
            align = 1 << (s->a.aligned - 1);
        if (t == TOK_SIZEOF) {
            if (!(type.t & VT_VLA)) {
                if (size < 0)
                    tcc_error("sizeof applied to an incomplete type");
                vpushs(size);
            } else {
                vla_runtime_type_size(&type, &align);
            }
        } else {
            vpushs(align);
        }
        vtop->type.t |= VT_UNSIGNED;
        break;

    case TOK_builtin_expect:
    /* __builtin_expect is a no-op for now */
    parse_builtin_params(0, "ee");
    vpop();
        break;
    case TOK_builtin_types_compatible_p:
    parse_builtin_params(0, "tt");
    vtop[-1].type.t &= ~(VT_CONSTANT | VT_VOLATILE);
    vtop[0].type.t &= ~(VT_CONSTANT | VT_VOLATILE);
    n = is_compatible_types(&vtop[-1].type, &vtop[0].type);
    vtop -= 2;
    vpushi(n);
        break;
    case TOK_builtin_choose_expr:
    {
        int64_t c;
        next();
        skip('(');
        c = expr_const64();
        skip(',');
        if (!c) {
        nocode_wanted++;
        }
        expr_eq();
        if (!c) {
        vpop();
        nocode_wanted--;
        }
        skip(',');
        if (c) {
        nocode_wanted++;
        }
        expr_eq();
        if (c) {
        vpop();
        nocode_wanted--;
        }
        skip(')');
    }
        break;
    case TOK_builtin_constant_p:
    parse_builtin_params(1, "e");
    n = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
    vtop--;
    vpushi(n);
        break;
    case TOK_builtin_frame_address:
    case TOK_builtin_return_address:
        {
            int tok1 = tok;
            int level;
            next();
            skip('(');
            if (tok != TOK_CINT) {
                tcc_error("%s only takes positive integers",
                          tok1 == TOK_builtin_return_address ?
                          "__builtin_return_address" :
                          "__builtin_frame_address");
            }
            level = (uint32_t)tokc.i;
            next();
            skip(')');
            type.t = VT_VOID;
            mk_pointer(&type);
            vset(&type, VT_LOCAL, 0);       /* local frame */
            while (level--) {
                mk_pointer(&vtop->type);
                indir();                    /* -> parent frame */
            }
            if (tok1 == TOK_builtin_return_address) {
                // assume return address is just above frame pointer on stack
                vpushi(PTR_SIZE);
                gen_op('+');
                mk_pointer(&vtop->type);
                indir();
            }
        }
        break;
#ifdef TCC_TARGET_X86_64
#ifdef TCC_TARGET_PE
    case TOK_builtin_va_start:
    parse_builtin_params(0, "ee");
        r = vtop->r & VT_VALMASK;
        if (r == VT_LLOCAL)
            r = VT_LOCAL;
        if (r != VT_LOCAL)
            tcc_error("__builtin_va_start expects a local variable");
        vtop->r = r;
    vtop->type = char_pointer_type;
    vtop->c.i += 8;
    vstore();
        break;
#else
    case TOK_builtin_va_arg_types:
    parse_builtin_params(0, "t");
    vpushi(classify_x86_64_va_arg(&vtop->type));
    vswap();
    vpop();
        break;
#endif
#endif

#ifdef TCC_TARGET_ARM64
    case TOK___va_start: {
    parse_builtin_params(0, "ee");
        //xx check types
        gen_va_start();
        vpushi(0);
        vtop->type.t = VT_VOID;
        break;
    }
    case TOK___va_arg: {
    parse_builtin_params(0, "et");
    type = vtop->type;
    vpop();
        //xx check types
        gen_va_arg(&type);
        vtop->type = type;
        break;
    }
    case TOK___arm64_clear_cache: {
    parse_builtin_params(0, "ee");
        gen_clear_cache();
        vpushi(0);
        vtop->type.t = VT_VOID;
        break;
    }
#endif
    /* pre operations */
    case TOK_INC:
    case TOK_DEC:
        t = tok;
        next();
        unary();
        inc(0, t);
        break;
    case '-':
        next();
        unary();
        t = vtop->type.t & VT_BTYPE;
    if (is_float(t)) {
            /* In IEEE negate(x) isn't subtract(0,x), but rather
           subtract(-0, x).  */
        vpush(&vtop->type);
        if (t == VT_FLOAT)
            vtop->c.f = -1.0 * 0.0;
        else if (t == VT_DOUBLE)
            vtop->c.d = -1.0 * 0.0;
        else
            vtop->c.ld = -1.0 * 0.0;
    } else
        vpushi(0);
    vswap();
    gen_op('-');
        break;
    case TOK_LAND:
        if (!gnu_ext)
            goto tok_identifier;
        next();
        /* allow to take the address of a label */
        if (tok < TOK_UIDENT)
            expect("label identifier");
        s = label_find(tok);
        if (!s) {
            s = label_push(&global_label_stack, tok, LABEL_FORWARD);
        } else {
            if (s->r == LABEL_DECLARED)
                s->r = LABEL_FORWARD;
        }
        if (!s->type.t) {
            s->type.t = VT_VOID;
            mk_pointer(&s->type);
            s->type.t |= VT_STATIC;
        }
        vpushsym(&s->type, s);
        next();
        break;

    case TOK_GENERIC:
    {
    CType controlling_type;
    int has_default = 0;
    int has_match = 0;
    int learn = 0;
    TokenString *str = NULL;

    next();
    skip('(');
    expr_type(&controlling_type, expr_eq);
    controlling_type.t &= ~(VT_CONSTANT | VT_VOLATILE | VT_ARRAY);
    for (;;) {
        learn = 0;
        skip(',');
        if (tok == TOK_DEFAULT) {
        if (has_default)
            tcc_error("too many 'default'");
        has_default = 1;
        if (!has_match)
            learn = 1;
        next();
        } else {
            AttributeDef ad_tmp;
        int itmp;
            CType cur_type;
        parse_btype(&cur_type, &ad_tmp);
        type_decl(&cur_type, &ad_tmp, &itmp, TYPE_ABSTRACT);
        if (compare_types(&controlling_type, &cur_type, 0)) {
            if (has_match) {
              tcc_error("type match twice");
            }
            has_match = 1;
            learn = 1;
        }
        }
        skip(':');
        if (learn) {
        if (str)
            tok_str_free(str);
        skip_or_save_block(&str);
        } else {
        skip_or_save_block(NULL);
        }
        if (tok == ')')
        break;
    }
    if (!str) {
        char buf[60];
        type_to_str(buf, sizeof buf, &controlling_type, NULL);
        tcc_error("type '%s' does not match any association", buf);
    }
    begin_macro(str, 1);
    next();
    expr_eq();
    if (tok != TOK_EOF)
        expect(",");
    end_macro();
        next();
    break;
    }
    // special qnan , snan and infinity values
    case TOK___NAN__:
        vpush64(VT_DOUBLE, 0x7ff8000000000000ULL);
        next();
        break;
    case TOK___SNAN__:
        vpush64(VT_DOUBLE, 0x7ff0000000000001ULL);
        next();
        break;
    case TOK___INF__:
        vpush64(VT_DOUBLE, 0x7ff0000000000000ULL);
        next();
        break;

    default:
    tok_identifier:
        t = tok;
        next();
        if (t < TOK_UIDENT)
            expect("identifier");
        s = sym_find(t);
        if (!s || IS_ASM_SYM(s)) {
            const char *name = get_tok_str(t, NULL);
            if (tok != '(')
                tcc_error("'%s' undeclared", name);
            /* for simple function calls, we tolerate undeclared
               external reference to int() function */
            if (tcc_state->warn_implicit_function_declaration
#ifdef TCC_TARGET_PE
                /* people must be warned about using undeclared WINAPI functions
                   (which usually start with uppercase letter) */
                || (name[0] >= 'A' && name[0] <= 'Z')
#endif
            )
                tcc_warning("implicit declaration of function '%s'", name);
            s = external_global_sym(t, &func_old_type, 0); 
        }

        r = s->r;
        /* A symbol that has a register is a local register variable,
           which starts out as VT_LOCAL value.  */
        if ((r & VT_VALMASK) < VT_CONST)
            r = (r & ~VT_VALMASK) | VT_LOCAL;

        vset(&s->type, r, s->c);
        /* Point to s as backpointer (even without r&VT_SYM).
       Will be used by at least the x86 inline asm parser for
       regvars.  */
    vtop->sym = s;

        if (r & VT_SYM) {
            vtop->c.i = 0;
        } else if (r == VT_CONST && IS_ENUM_VAL(s->type.t)) {
            vtop->c.i = s->enum_val;
        }
        break;
    }
    
    /* post operations */
    while (1) {
        if (tok == TOK_INC || tok == TOK_DEC) {
            inc(1, tok);
            next();
        } else if (tok == '.' || tok == TOK_ARROW || tok == TOK_CDOUBLE) {
            int qualifiers;
            /* field */ 
            if (tok == TOK_ARROW) 
                indir();
            qualifiers = vtop->type.t & (VT_CONSTANT | VT_VOLATILE);
            test_lvalue();
            gaddrof();
            /* expect pointer on structure */
            if ((vtop->type.t & VT_BTYPE) != VT_STRUCT)
                expect("struct or union");
            if (tok == TOK_CDOUBLE)
                expect("field name");
            next();
            if (tok == TOK_CINT || tok == TOK_CUINT)
                expect("field name");
        s = find_field(&vtop->type, tok);
            if (!s)
                tcc_error("field not found: %s",  get_tok_str(tok & ~SYM_FIELD, &tokc));
            /* add field offset to pointer */
            vtop->type = char_pointer_type; /* change type to 'char *' */
            vpushi(s->c);
            gen_op('+');
            /* change type to field type, and set to lvalue */
            vtop->type = s->type;
            vtop->type.t |= qualifiers;
            /* an array is never an lvalue */
            if (!(vtop->type.t & VT_ARRAY)) {
                vtop->r |= lvalue_type(vtop->type.t);
#ifdef CONFIG_TCC_BCHECK
                /* if bound checking, the referenced pointer must be checked */
                if (tcc_state->do_bounds_check && (vtop->r & VT_VALMASK) != VT_LOCAL)
                    vtop->r |= VT_MUSTBOUND;
#endif
            }
            next();
        } else if (tok == '[') {
            next();
            gexpr();
            gen_op('+');
            indir();
            skip(']');
        } else if (tok == '(') {
            SValue ret;
            Sym *sa;
            int nb_args, ret_nregs, ret_align, regsize, variadic;

            /* function call  */
            if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
                /* pointer test (no array accepted) */
                if ((vtop->type.t & (VT_BTYPE | VT_ARRAY)) == VT_PTR) {
                    vtop->type = *pointed_type(&vtop->type);
                    if ((vtop->type.t & VT_BTYPE) != VT_FUNC)
                        goto error_func;
                } else {
                error_func:
                    expect("function pointer");
                }
            } else {
                vtop->r &= ~VT_LVAL; /* no lvalue */
            }
            /* get return type */
            s = vtop->type.ref;
            next();
            sa = s->next; /* first parameter */
            nb_args = regsize = 0;
            ret.r2 = VT_CONST;
            /* compute first implicit argument if a structure is returned */
            if ((s->type.t & VT_BTYPE) == VT_STRUCT) {
                variadic = (s->f.func_type == FUNC_ELLIPSIS);
                ret_nregs = gfunc_sret(&s->type, variadic, &ret.type,
                                       &ret_align, &regsize);
                if (!ret_nregs) {
                    /* get some space for the returned structure */
                    size = type_size(&s->type, &align);
#ifdef TCC_TARGET_ARM64
                /* On arm64, a small struct is return in registers.
                   It is much easier to write it to memory if we know
                   that we are allowed to write some extra bytes, so
                   round the allocated space up to a power of 2: */
                if (size < 16)
                    while (size & (size - 1))
                        size = (size | (size - 1)) + 1;
#endif
                    loc = (loc - size) & -align;
                    ret.type = s->type;
                    ret.r = VT_LOCAL | VT_LVAL;
                    /* pass it as 'int' to avoid structure arg passing
                       problems */
                    vseti(VT_LOCAL, loc);
                    ret.c = vtop->c;
                    nb_args++;
                }
            } else {
                ret_nregs = 1;
                ret.type = s->type;
            }

            if (ret_nregs) {
                /* return in register */
                if (is_float(ret.type.t)) {
                    ret.r = reg_fret(ret.type.t);
#ifdef TCC_TARGET_X86_64
                    if ((ret.type.t & VT_BTYPE) == VT_QFLOAT)
                      ret.r2 = REG_QRET;
#endif
                } else {
#ifndef TCC_TARGET_ARM64
#ifdef TCC_TARGET_X86_64
                    if ((ret.type.t & VT_BTYPE) == VT_QLONG)
#else
                    if ((ret.type.t & VT_BTYPE) == VT_LLONG)
#endif
                        ret.r2 = REG_LRET;
#endif
                    ret.r = REG_IRET;
                }
                ret.c.i = 0;
            }
            if (tok != ')') {
                for(;;) {
                    expr_eq();
                    gfunc_param_typed(s, sa);
                    nb_args++;
                    if (sa)
                        sa = sa->next;
                    if (tok == ')')
                        break;
                    skip(',');
                }
            }
            if (sa)
                tcc_error("too few arguments to function");
            skip(')');
            gfunc_call(nb_args);

            /* return value */
            for (r = ret.r + ret_nregs + !ret_nregs; r-- > ret.r;) {
                vsetc(&ret.type, r, &ret.c);
                vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */
            }

            /* handle packed struct return */
            if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) {
                int addr, offset;

                size = type_size(&s->type, &align);
        /* We're writing whole regs often, make sure there's enough
           space.  Assume register size is power of 2.  */
        if (regsize > align)
          align = regsize;
                loc = (loc - size) & -align;
                addr = loc;
                offset = 0;
                for (;;) {
                    vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset);
                    vswap();
                    vstore();
                    vtop--;
                    if (--ret_nregs == 0)
                        break;
                    offset += regsize;
                }
                vset(&s->type, VT_LOCAL | VT_LVAL, addr);
            }
        } else {
            break;
        }
    }
}

ST_FUNC void expr_prod(void)
{
    int t;

    unary();
    while (tok == '*' || tok == '/' || tok == '%') {
        t = tok;
        next();
        unary();
        gen_op(t);
    }
}

ST_FUNC void expr_sum(void)
{
    int t;

    expr_prod();
    while (tok == '+' || tok == '-') {
        t = tok;
        next();
        expr_prod();
        gen_op(t);
    }
}

static void expr_shift(void)
{
    int t;

    expr_sum();
    while (tok == TOK_SHL || tok == TOK_SAR) {
        t = tok;
        next();
        expr_sum();
        gen_op(t);
    }
}

static void expr_cmp(void)
{
    int t;

    expr_shift();
    while ((tok >= TOK_ULE && tok <= TOK_GT) ||
           tok == TOK_ULT || tok == TOK_UGE) {
        t = tok;
        next();
        expr_shift();
        gen_op(t);
    }
}

static void expr_cmpeq(void)
{
    int t;

    expr_cmp();
    while (tok == TOK_EQ || tok == TOK_NE) {
        t = tok;
        next();
        expr_cmp();
        gen_op(t);
    }
}

static void expr_and(void)
{
    expr_cmpeq();
    while (tok == '&') {
        next();
        expr_cmpeq();
        gen_op('&');
    }
}

static void expr_xor(void)
{
    expr_and();
    while (tok == '^') {
        next();
        expr_and();
        gen_op('^');
    }
}

static void expr_or(void)
{
    expr_xor();
    while (tok == '|') {
        next();
        expr_xor();
        gen_op('|');
    }
}

static void expr_land(void)
{
    expr_or();
    if (tok == TOK_LAND) {
    int t = 0;
    for(;;) {
        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
                gen_cast_s(VT_BOOL);
        if (vtop->c.i) {
            vpop();
        } else {
            nocode_wanted++;
            while (tok == TOK_LAND) {
            next();
            expr_or();
            vpop();
            }
            nocode_wanted--;
            if (t)
              gsym(t);
            gen_cast_s(VT_INT);
            break;
        }
        } else {
        if (!t)
          save_regs(1);
        t = gvtst(1, t);
        }
        if (tok != TOK_LAND) {
        if (t)
          vseti(VT_JMPI, t);
        else
          vpushi(1);
        break;
        }
        next();
        expr_or();
    }
    }
}

static void expr_lor(void)
{
    expr_land();
    if (tok == TOK_LOR) {
    int t = 0;
    for(;;) {
        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
                gen_cast_s(VT_BOOL);
        if (!vtop->c.i) {
            vpop();
        } else {
            nocode_wanted++;
            while (tok == TOK_LOR) {
            next();
            expr_land();
            vpop();
            }
            nocode_wanted--;
            if (t)
              gsym(t);
            gen_cast_s(VT_INT);
            break;
        }
        } else {
        if (!t)
          save_regs(1);
        t = gvtst(0, t);
        }
        if (tok != TOK_LOR) {
        if (t)
          vseti(VT_JMP, t);
        else
          vpushi(0);
        break;
        }
        next();
        expr_land();
    }
    }
}

/* Assuming vtop is a value used in a conditional context
   (i.e. compared with zero) return 0 if it's false, 1 if
   true and -1 if it can't be statically determined.  */
static int condition_3way(void)
{
    int c = -1;
    if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST &&
    (!(vtop->r & VT_SYM) || !vtop->sym->a.weak)) {
    vdup();
        gen_cast_s(VT_BOOL);
    c = vtop->c.i;
    vpop();
    }
    return c;
}

static void expr_cond(void)
{
    int tt, u, r1, r2, rc, t1, t2, bt1, bt2, islv, c, g;
    SValue sv;
    CType type, type1, type2;

    expr_lor();
    if (tok == '?') {
        next();
    c = condition_3way();
        g = (tok == ':' && gnu_ext);
        if (c < 0) {
            /* needed to avoid having different registers saved in
               each branch */
            if (is_float(vtop->type.t)) {
                rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64
                if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
                    rc = RC_ST0;
                }
#endif
            } else
                rc = RC_INT;
            gv(rc);
            save_regs(1);
            if (g)
                gv_dup();
            tt = gvtst(1, 0);

        } else {
            if (!g)
                vpop();
            tt = 0;
        }

        if (1) {
            if (c == 0)
                nocode_wanted++;
            if (!g)
                gexpr();

            type1 = vtop->type;
            sv = *vtop; /* save value to handle it later */
            vtop--; /* no vpop so that FP stack is not flushed */
            skip(':');

            u = 0;
            if (c < 0)
                u = gjmp(0);
            gsym(tt);

            if (c == 0)
                nocode_wanted--;
            if (c == 1)
                nocode_wanted++;
            expr_cond();
            if (c == 1)
                nocode_wanted--;

            type2 = vtop->type;
            t1 = type1.t;
            bt1 = t1 & VT_BTYPE;
            t2 = type2.t;
            bt2 = t2 & VT_BTYPE;
            type.ref = NULL;

            /* cast operands to correct type according to ISOC rules */
            if (is_float(bt1) || is_float(bt2)) {
                if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
                    type.t = VT_LDOUBLE;

                } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
                    type.t = VT_DOUBLE;
                } else {
                    type.t = VT_FLOAT;
                }
            } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
                /* cast to biggest op */
                type.t = VT_LLONG | VT_LONG;
                if (bt1 == VT_LLONG)
                    type.t &= t1;
                if (bt2 == VT_LLONG)
                    type.t &= t2;
                /* convert to unsigned if it does not fit in a long long */
                if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED) ||
                    (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED))
                    type.t |= VT_UNSIGNED;
            } else if (bt1 == VT_PTR || bt2 == VT_PTR) {
        /* If one is a null ptr constant the result type
           is the other.  */
        if (is_null_pointer (vtop))
          type = type1;
        else if (is_null_pointer (&sv))
          type = type2;
                /* XXX: test pointer compatibility, C99 has more elaborate
           rules here.  */
        else
          type = type1;
            } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) {
                /* XXX: test function pointer compatibility */
                type = bt1 == VT_FUNC ? type1 : type2;
            } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
                /* XXX: test structure compatibility */
                type = bt1 == VT_STRUCT ? type1 : type2;
            } else if (bt1 == VT_VOID || bt2 == VT_VOID) {
                /* NOTE: as an extension, we accept void on only one side */
                type.t = VT_VOID;
            } else {
                /* integer operations */
                type.t = VT_INT | (VT_LONG & (t1 | t2));
                /* convert to unsigned if it does not fit in an integer */
                if ((t1 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED) ||
                    (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_INT | VT_UNSIGNED))
                    type.t |= VT_UNSIGNED;
            }
            /* keep structs lvalue by transforming `(expr ? a : b)` to `*(expr ? &a : &b)` so
               that `(expr ? a : b).mem` does not error  with "lvalue expected" */
            islv = (vtop->r & VT_LVAL) && (sv.r & VT_LVAL) && VT_STRUCT == (type.t & VT_BTYPE);
            islv &= c < 0;

            /* now we convert second operand */
            if (c != 1) {
                gen_cast(&type);
                if (islv) {
                    mk_pointer(&vtop->type);
                    gaddrof();
                } else if (VT_STRUCT == (vtop->type.t & VT_BTYPE))
                    gaddrof();
            }

            rc = RC_INT;
            if (is_float(type.t)) {
                rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64
                if ((type.t & VT_BTYPE) == VT_LDOUBLE) {
                    rc = RC_ST0;
                }
#endif
            } else if ((type.t & VT_BTYPE) == VT_LLONG) {
                /* for long longs, we use fixed registers to avoid having
                   to handle a complicated move */
                rc = RC_IRET;
            }

            tt = r2 = 0;
            if (c < 0) {
                r2 = gv(rc);
                tt = gjmp(0);
            }
            gsym(u);

            /* this is horrible, but we must also convert first
               operand */
            if (c != 0) {
                *vtop = sv;
                gen_cast(&type);
                if (islv) {
                    mk_pointer(&vtop->type);
                    gaddrof();
                } else if (VT_STRUCT == (vtop->type.t & VT_BTYPE))
                    gaddrof();
            }

            if (c < 0) {
                r1 = gv(rc);
                move_reg(r2, r1, type.t);
                vtop->r = r2;
                gsym(tt);
                if (islv)
                    indir();
            }
        }
    }
}

static void expr_eq(void)
{
    int t;
    
    expr_cond();
    if (tok == '=' ||
        (tok >= TOK_A_MOD && tok <= TOK_A_DIV) ||
        tok == TOK_A_XOR || tok == TOK_A_OR ||
        tok == TOK_A_SHL || tok == TOK_A_SAR) {
        test_lvalue();
        t = tok;
        next();
        if (t == '=') {
            expr_eq();
        } else {
            vdup();
            expr_eq();
            gen_op(t & 0x7f);
        }
        vstore();
    }
}

ST_FUNC void gexpr(void)
{
    while (1) {
        expr_eq();
        if (tok != ',')
            break;
        vpop();
        next();
    }
}

/* parse a constant expression and return value in vtop.  */
static void expr_const1(void)
{
    const_wanted++;
    nocode_wanted++;
    expr_cond();
    nocode_wanted--;
    const_wanted--;
}

/* parse an integer constant and return its value. */
static inline int64_t expr_const64(void)
{
    int64_t c;
    expr_const1();
    if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
        expect("constant expression");
    c = vtop->c.i;
    vpop();
    return c;
}

/* parse an integer constant and return its value.
   Complain if it doesn't fit 32bit (signed or unsigned).  */
ST_FUNC int expr_const(void)
{
    int c;
    int64_t wc = expr_const64();
    c = wc;
    if (c != wc && (unsigned)c != wc)
        tcc_error("constant exceeds 32 bit");
    return c;
}

/* return the label token if current token is a label, otherwise
   return zero */
static int is_label(void)
{
    int last_tok;

    /* fast test first */
    if (tok < TOK_UIDENT)
        return 0;
    /* no need to save tokc because tok is an identifier */
    last_tok = tok;
    next();
    if (tok == ':') {
        return last_tok;
    } else {
        unget_tok(last_tok);
        return 0;
    }
}

#ifndef TCC_TARGET_ARM64
static void gfunc_return(CType *func_type)
{
    if ((func_type->t & VT_BTYPE) == VT_STRUCT) {
        CType type, ret_type;
        int ret_align, ret_nregs, regsize;
        ret_nregs = gfunc_sret(func_type, func_var, &ret_type,
                               &ret_align, &regsize);
        if (0 == ret_nregs) {
            /* if returning structure, must copy it to implicit
               first pointer arg location */
            type = *func_type;
            mk_pointer(&type);
            vset(&type, VT_LOCAL | VT_LVAL, func_vc);
            indir();
            vswap();
            /* copy structure value to pointer */
            vstore();
        } else {
            /* returning structure packed into registers */
            int r, size, addr, align;
            size = type_size(func_type,&align);
            if ((vtop->r != (VT_LOCAL | VT_LVAL) ||
                 (vtop->c.i & (ret_align-1)))
                && (align & (ret_align-1))) {
                loc = (loc - size) & -ret_align;
                addr = loc;
                type = *func_type;
                vset(&type, VT_LOCAL | VT_LVAL, addr);
                vswap();
                vstore();
                vpop();
                vset(&ret_type, VT_LOCAL | VT_LVAL, addr);
            }
            vtop->type = ret_type;
            if (is_float(ret_type.t))
                r = rc_fret(ret_type.t);
            else
                r = RC_IRET;

            if (ret_nregs == 1)
                gv(r);
            else {
                for (;;) {
                    vdup();
                    gv(r);
                    vpop();
                    if (--ret_nregs == 0)
                      break;
                    /* We assume that when a structure is returned in multiple
                       registers, their classes are consecutive values of the
                       suite s(n) = 2^n */
                    r <<= 1;
                    vtop->c.i += regsize;
                }
            }
        }
    } else if (is_float(func_type->t)) {
        gv(rc_fret(func_type->t));
    } else {
        gv(RC_IRET);
    }
    vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
}
#endif

static int case_cmp(const void *pa, const void *pb)
{
    int64_t a = (*(struct case_t**) pa)->v1;
    int64_t b = (*(struct case_t**) pb)->v1;
    return a < b ? -1 : a > b;
}

static void gcase(struct case_t **base, int len, int *bsym)
{
    struct case_t *p;
    int e;
    int ll = (vtop->type.t & VT_BTYPE) == VT_LLONG;
    gv(RC_INT);
    while (len > 4) {
        /* binary search */
        p = base[len/2];
        vdup();
    if (ll)
        vpushll(p->v2);
    else
        vpushi(p->v2);
        gen_op(TOK_LE);
        e = gtst(1, 0);
        vdup();
    if (ll)
        vpushll(p->v1);
    else
        vpushi(p->v1);
        gen_op(TOK_GE);
        gtst_addr(0, p->sym); /* v1 <= x <= v2 */
        /* x < v1 */
        gcase(base, len/2, bsym);
        if (cur_switch->def_sym)
            gjmp_addr(cur_switch->def_sym);
        else
            *bsym = gjmp(*bsym);
        /* x > v2 */
        gsym(e);
        e = len/2 + 1;
        base += e; len -= e;
    }
    /* linear scan */
    while (len--) {
        p = *base++;
        vdup();
    if (ll)
        vpushll(p->v2);
    else
        vpushi(p->v2);
        if (p->v1 == p->v2) {
            gen_op(TOK_EQ);
            gtst_addr(0, p->sym);
        } else {
            gen_op(TOK_LE);
            e = gtst(1, 0);
            vdup();
        if (ll)
            vpushll(p->v1);
        else
            vpushi(p->v1);
            gen_op(TOK_GE);
            gtst_addr(0, p->sym);
            gsym(e);
        }
    }
}

static void block(int *bsym, int *csym, int is_expr)
{
    int a, b, c, d, cond;
    Sym *s;

    /* generate line number info */
    if (tcc_state->do_debug)
        tcc_debug_line(tcc_state);

    if (is_expr) {
        /* default return value is (void) */
        vpushi(0);
        vtop->type.t = VT_VOID;
    }

    if (tok == TOK_IF) {
        /* if test */
    int saved_nocode_wanted = nocode_wanted;
        next();
        skip('(');
        gexpr();
        skip(')');
    cond = condition_3way();
        if (cond == 1)
            a = 0, vpop();
        else
            a = gvtst(1, 0);
        if (cond == 0)
        nocode_wanted |= 0x20000000;
        block(bsym, csym, 0);
    if (cond != 1)
        nocode_wanted = saved_nocode_wanted;
        c = tok;
        if (c == TOK_ELSE) {
            next();
            d = gjmp(0);
            gsym(a);
        if (cond == 1)
            nocode_wanted |= 0x20000000;
            block(bsym, csym, 0);
            gsym(d); /* patch else jmp */
        if (cond != 0)
        nocode_wanted = saved_nocode_wanted;
        } else
            gsym(a);
    } else if (tok == TOK_WHILE) {
    int saved_nocode_wanted;
    nocode_wanted &= ~0x20000000;
        next();
        d = ind;
        vla_sp_restore();
        skip('(');
        gexpr();
        skip(')');
        a = gvtst(1, 0);
        b = 0;
        ++local_scope;
    saved_nocode_wanted = nocode_wanted;
        block(&a, &b, 0);
    nocode_wanted = saved_nocode_wanted;
        --local_scope;
        gjmp_addr(d);
        gsym(a);
        gsym_addr(b, d);
    } else if (tok == '{') {
        Sym *llabel;
        int block_vla_sp_loc = vla_sp_loc, saved_vlas_in_scope = vlas_in_scope;

        next();
        /* record local declaration stack position */
        s = local_stack;
        llabel = local_label_stack;
        ++local_scope;
        
        /* handle local labels declarations */
        if (tok == TOK_LABEL) {
            next();
            for(;;) {
                if (tok < TOK_UIDENT)
                    expect("label identifier");
                label_push(&local_label_stack, tok, LABEL_DECLARED);
                next();
                if (tok == ',') {
                    next();
                } else {
                    skip(';');
                    break;
                }
            }
        }
        while (tok != '}') {
        if ((a = is_label()))
        unget_tok(a);
        else
            decl(VT_LOCAL);
            if (tok != '}') {
                if (is_expr)
                    vpop();
                block(bsym, csym, is_expr);
            }
        }
        /* pop locally defined labels */
        label_pop(&local_label_stack, llabel, is_expr);
        /* pop locally defined symbols */
        --local_scope;
    /* In the is_expr case (a statement expression is finished here),
       vtop might refer to symbols on the local_stack.  Either via the
       type or via vtop->sym.  We can't pop those nor any that in turn
       might be referred to.  To make it easier we don't roll back
       any symbols in that case; some upper level call to block() will
       do that.  We do have to remove such symbols from the lookup
       tables, though.  sym_pop will do that.  */
    sym_pop(&local_stack, s, is_expr);

        /* Pop VLA frames and restore stack pointer if required */
        if (vlas_in_scope > saved_vlas_in_scope) {
            vla_sp_loc = saved_vlas_in_scope ? block_vla_sp_loc : vla_sp_root_loc;
            vla_sp_restore();
        }
        vlas_in_scope = saved_vlas_in_scope;
        
        next();
    } else if (tok == TOK_RETURN) {
        next();
        if (tok != ';') {
            gexpr();
            gen_assign_cast(&func_vt);
            if ((func_vt.t & VT_BTYPE) == VT_VOID)
                vtop--;
            else
                gfunc_return(&func_vt);
        }
        skip(';');
        /* jump unless last stmt in top-level block */
        if (tok != '}' || local_scope != 1)
            rsym = gjmp(rsym);
    nocode_wanted |= 0x20000000;
    } else if (tok == TOK_BREAK) {
        /* compute jump */
        if (!bsym)
            tcc_error("cannot break");
        *bsym = gjmp(*bsym);
        next();
        skip(';');
    nocode_wanted |= 0x20000000;
    } else if (tok == TOK_CONTINUE) {
        /* compute jump */
        if (!csym)
            tcc_error("cannot continue");
        vla_sp_restore_root();
        *csym = gjmp(*csym);
        next();
        skip(';');
    } else if (tok == TOK_FOR) {
        int e;
    int saved_nocode_wanted;
    nocode_wanted &= ~0x20000000;
        next();
        skip('(');
        s = local_stack;
        ++local_scope;
        if (tok != ';') {
            /* c99 for-loop init decl? */
            if (!decl0(VT_LOCAL, 1, NULL)) {
                /* no, regular for-loop init expr */
                gexpr();
                vpop();
            }
        }
        skip(';');
        d = ind;
        c = ind;
        vla_sp_restore();
        a = 0;
        b = 0;
        if (tok != ';') {
            gexpr();
            a = gvtst(1, 0);
        }
        skip(';');
        if (tok != ')') {
            e = gjmp(0);
            c = ind;
            vla_sp_restore();
            gexpr();
            vpop();
            gjmp_addr(d);
            gsym(e);
        }
        skip(')');
    saved_nocode_wanted = nocode_wanted;
        block(&a, &b, 0);
    nocode_wanted = saved_nocode_wanted;
        gjmp_addr(c);
        gsym(a);
        gsym_addr(b, c);
        --local_scope;
        sym_pop(&local_stack, s, 0);

    } else 
    if (tok == TOK_DO) {
    int saved_nocode_wanted;
    nocode_wanted &= ~0x20000000;
        next();
        a = 0;
        b = 0;
        d = ind;
        vla_sp_restore();
    saved_nocode_wanted = nocode_wanted;
        block(&a, &b, 0);
        skip(TOK_WHILE);
        skip('(');
        gsym(b);
    gexpr();
    c = gvtst(0, 0);
    gsym_addr(c, d);
    nocode_wanted = saved_nocode_wanted;
        skip(')');
        gsym(a);
        skip(';');
    } else
    if (tok == TOK_SWITCH) {
        struct switch_t *saved, sw;
    int saved_nocode_wanted = nocode_wanted;
    SValue switchval;
        next();
        skip('(');
        gexpr();
        skip(')');
    switchval = *vtop--;
        a = 0;
        b = gjmp(0); /* jump to first case */
        sw.p = NULL; sw.n = 0; sw.def_sym = 0;
        saved = cur_switch;
        cur_switch = &sw;
        block(&a, csym, 0);
    nocode_wanted = saved_nocode_wanted;
        a = gjmp(a); /* add implicit break */
        /* case lookup */
        gsym(b);
        qsort(sw.p, sw.n, sizeof(void*), case_cmp);
        for (b = 1; b < sw.n; b++)
            if (sw.p[b - 1]->v2 >= sw.p[b]->v1)
                tcc_error("duplicate case value");
        /* Our switch table sorting is signed, so the compared
           value needs to be as well when it's 64bit.  */
        if ((switchval.type.t & VT_BTYPE) == VT_LLONG)
            switchval.type.t &= ~VT_UNSIGNED;
        vpushv(&switchval);
        gcase(sw.p, sw.n, &a);
        vpop();
        if (sw.def_sym)
          gjmp_addr(sw.def_sym);
        dynarray_reset(&sw.p, &sw.n);
        cur_switch = saved;
        /* break label */
        gsym(a);
    } else
    if (tok == TOK_CASE) {
        struct case_t *cr = tcc_malloc(sizeof(struct case_t));
        if (!cur_switch)
            expect("switch");
    nocode_wanted &= ~0x20000000;
        next();
        cr->v1 = cr->v2 = expr_const64();
        if (gnu_ext && tok == TOK_DOTS) {
            next();
            cr->v2 = expr_const64();
            if (cr->v2 < cr->v1)
                tcc_warning("empty case range");
        }
        cr->sym = ind;
        dynarray_add(&cur_switch->p, &cur_switch->n, cr);
        skip(':');
        is_expr = 0;
        goto block_after_label;
    } else 
    if (tok == TOK_DEFAULT) {
        next();
        skip(':');
        if (!cur_switch)
            expect("switch");
        if (cur_switch->def_sym)
            tcc_error("too many 'default'");
        cur_switch->def_sym = ind;
        is_expr = 0;
        goto block_after_label;
    } else
    if (tok == TOK_GOTO) {
        next();
        if (tok == '*' && gnu_ext) {
            /* computed goto */
            next();
            gexpr();
            if ((vtop->type.t & VT_BTYPE) != VT_PTR)
                expect("pointer");
            ggoto();
        } else if (tok >= TOK_UIDENT) {
            s = label_find(tok);
            /* put forward definition if needed */
            if (!s) {
                s = label_push(&global_label_stack, tok, LABEL_FORWARD);
            } else {
                if (s->r == LABEL_DECLARED)
                    s->r = LABEL_FORWARD;
            }
            vla_sp_restore_root();
        if (s->r & LABEL_FORWARD)
                s->jnext = gjmp(s->jnext);
            else
                gjmp_addr(s->jnext);
            next();
        } else {
            expect("label identifier");
        }
        skip(';');
    } else if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3) {
        asm_instr();
    } else {
        b = is_label();
        if (b) {
            /* label case */
        next();
            s = label_find(b);
            if (s) {
                if (s->r == LABEL_DEFINED)
                    tcc_error("duplicate label '%s'", get_tok_str(s->v, NULL));
                gsym(s->jnext);
                s->r = LABEL_DEFINED;
            } else {
                s = label_push(&global_label_stack, b, LABEL_DEFINED);
            }
            s->jnext = ind;
            vla_sp_restore();
            /* we accept this, but it is a mistake */
        block_after_label:
        nocode_wanted &= ~0x20000000;
            if (tok == '}') {
                tcc_warning("deprecated use of label at end of compound statement");
            } else {
                if (is_expr)
                    vpop();
                block(bsym, csym, is_expr);
            }
        } else {
            /* expression case */
            if (tok != ';') {
                if (is_expr) {
                    vpop();
                    gexpr();
                } else {
                    gexpr();
                    vpop();
                }
            }
            skip(';');
        }
    }
}

/* This skips over a stream of tokens containing balanced {} and ()
   pairs, stopping at outer ',' ';' and '}' (or matching '}' if we started
   with a '{').  If STR then allocates and stores the skipped tokens
   in *STR.  This doesn't check if () and {} are nested correctly,
   i.e. "({)}" is accepted.  */
static void skip_or_save_block(TokenString **str)
{
    int braces = tok == '{';
    int level = 0;
    if (str)
      *str = tok_str_alloc();

    while ((level > 0 || (tok != '}' && tok != ',' && tok != ';' && tok != ')'))) {
    int t;
    if (tok == TOK_EOF) {
         if (str || level > 0)
           tcc_error("unexpected end of file");
         else
           break;
    }
    if (str)
      tok_str_add_tok(*str);
    t = tok;
    next();
    if (t == '{' || t == '(') {
        level++;
    } else if (t == '}' || t == ')') {
        level--;
        if (level == 0 && braces && t == '}')
          break;
    }
    }
    if (str) {
    tok_str_add(*str, -1);
    tok_str_add(*str, 0);
    }
}

#define EXPR_CONST 1
#define EXPR_ANY   2

static void parse_init_elem(int expr_type)
{
    int saved_global_expr;
    switch(expr_type) {
    case EXPR_CONST:
        /* compound literals must be allocated globally in this case */
        saved_global_expr = global_expr;
        global_expr = 1;
        expr_const1();
        global_expr = saved_global_expr;
        /* NOTE: symbols are accepted, as well as lvalue for anon symbols
       (compound literals).  */
        if (((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST
         && ((vtop->r & (VT_SYM|VT_LVAL)) != (VT_SYM|VT_LVAL)
         || vtop->sym->v < SYM_FIRST_ANOM))
#ifdef TCC_TARGET_PE
                 || ((vtop->r & VT_SYM) && vtop->sym->a.dllimport)
#endif
            )
            tcc_error("initializer element is not constant");
        break;
    case EXPR_ANY:
        expr_eq();
        break;
    }
}

/* put zeros for variable based init */
static void init_putz(Section *sec, unsigned long c, int size)
{
    if (sec) {
        /* nothing to do because globals are already set to zero */
    } else {
        vpush_global_sym(&func_old_type, TOK_memset);
        vseti(VT_LOCAL, c);
#ifdef TCC_TARGET_ARM
        vpushs(size);
        vpushi(0);
#else
        vpushi(0);
        vpushs(size);
#endif
        gfunc_call(3);
    }
}

/* t is the array or struct type. c is the array or struct
   address. cur_field is the pointer to the current
   field, for arrays the 'c' member contains the current start
   index.  'size_only' is true if only size info is needed (only used
   in arrays).  al contains the already initialized length of the
   current container (starting at c).  This returns the new length of that.  */
static int decl_designator(CType *type, Section *sec, unsigned long c,
                           Sym **cur_field, int size_only, int al)
{
    Sym *s, *f;
    int index, index_last, align, l, nb_elems, elem_size;
    unsigned long corig = c;

    elem_size = 0;
    nb_elems = 1;
    if (gnu_ext && (l = is_label()) != 0)
        goto struct_field;
    /* NOTE: we only support ranges for last designator */
    while (nb_elems == 1 && (tok == '[' || tok == '.')) {
        if (tok == '[') {
            if (!(type->t & VT_ARRAY))
                expect("array type");
            next();
            index = index_last = expr_const();
            if (tok == TOK_DOTS && gnu_ext) {
                next();
                index_last = expr_const();
            }
            skip(']');
            s = type->ref;
        if (index < 0 || (s->c >= 0 && index_last >= s->c) ||
        index_last < index)
            tcc_error("invalid index");
            if (cur_field)
        (*cur_field)->c = index_last;
            type = pointed_type(type);
            elem_size = type_size(type, &align);
            c += index * elem_size;
            nb_elems = index_last - index + 1;
        } else {
            next();
            l = tok;
        struct_field:
            next();
            if ((type->t & VT_BTYPE) != VT_STRUCT)
                expect("struct/union type");
        f = find_field(type, l);
            if (!f)
                expect("field");
            if (cur_field)
                *cur_field = f;
        type = &f->type;
            c += f->c;
        }
        cur_field = NULL;
    }
    if (!cur_field) {
        if (tok == '=') {
            next();
        } else if (!gnu_ext) {
        expect("=");
        }
    } else {
        if (type->t & VT_ARRAY) {
        index = (*cur_field)->c;
        if (type->ref->c >= 0 && index >= type->ref->c)
            tcc_error("index too large");
            type = pointed_type(type);
            c += index * type_size(type, &align);
        } else {
            f = *cur_field;
        while (f && (f->v & SYM_FIRST_ANOM) && (f->type.t & VT_BITFIELD))
            *cur_field = f = f->next;
            if (!f)
                tcc_error("too many field init");
        type = &f->type;
            c += f->c;
        }
    }
    /* must put zero in holes (note that doing it that way
       ensures that it even works with designators) */
    if (!size_only && c - corig > al)
    init_putz(sec, corig + al, c - corig - al);
    decl_initializer(type, sec, c, 0, size_only);

    /* XXX: make it more general */
    if (!size_only && nb_elems > 1) {
        unsigned long c_end;
        uint8_t *src, *dst;
        int i;

        if (!sec) {
        vset(type, VT_LOCAL|VT_LVAL, c);
        for (i = 1; i < nb_elems; i++) {
        vset(type, VT_LOCAL|VT_LVAL, c + elem_size * i);
        vswap();
        vstore();
        }
        vpop();
        } else if (!NODATA_WANTED) {
        c_end = c + nb_elems * elem_size;
        if (c_end > sec->data_allocated)
            section_realloc(sec, c_end);
        src = sec->data + c;
        dst = src;
        for(i = 1; i < nb_elems; i++) {
        dst += elem_size;
        memcpy(dst, src, elem_size);
        }
    }
    }
    c += nb_elems * type_size(type, &align);
    if (c - corig > al)
      al = c - corig;
    return al;
}

/* store a value or an expression directly in global data or in local array */
static void init_putv(CType *type, Section *sec, unsigned long c)
{
    int bt;
    void *ptr;
    CType dtype;

    dtype = *type;
    dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */

    if (sec) {
    int size, align;
        /* XXX: not portable */
        /* XXX: generate error if incorrect relocation */
        gen_assign_cast(&dtype);
        bt = type->t & VT_BTYPE;

        if ((vtop->r & VT_SYM)
            && bt != VT_PTR
            && bt != VT_FUNC
            && (bt != (PTR_SIZE == 8 ? VT_LLONG : VT_INT)
                || (type->t & VT_BITFIELD))
            && !((vtop->r & VT_CONST) && vtop->sym->v >= SYM_FIRST_ANOM)
            )
            tcc_error("initializer element is not computable at load time");

        if (NODATA_WANTED) {
            vtop--;
            return;
        }

    size = type_size(type, &align);
    section_reserve(sec, c + size);
        ptr = sec->data + c;

        /* XXX: make code faster ? */
    if ((vtop->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST) &&
        vtop->sym->v >= SYM_FIRST_ANOM &&
        /* XXX This rejects compound literals like
           '(void *){ptr}'.  The problem is that '&sym' is
           represented the same way, which would be ruled out
           by the SYM_FIRST_ANOM check above, but also '"string"'
           in 'char *p = "string"' is represented the same
           with the type being VT_PTR and the symbol being an
           anonymous one.  That is, there's no difference in vtop
           between '(void *){x}' and '&(void *){x}'.  Ignore
           pointer typed entities here.  Hopefully no real code
           will every use compound literals with scalar type.  */
        (vtop->type.t & VT_BTYPE) != VT_PTR) {
        /* These come from compound literals, memcpy stuff over.  */
        Section *ssec;
        ElfSym *esym;
        ElfW_Rel *rel;
        esym = elfsym(vtop->sym);
        ssec = tcc_state->sections[esym->st_shndx];
        memmove (ptr, ssec->data + esym->st_value, size);
        if (ssec->reloc) {
        /* We need to copy over all memory contents, and that
           includes relocations.  Use the fact that relocs are
           created it order, so look from the end of relocs
           until we hit one before the copied region.  */
        int num_relocs = ssec->reloc->data_offset / sizeof(*rel);
        rel = (ElfW_Rel*)(ssec->reloc->data + ssec->reloc->data_offset);
        while (num_relocs--) {
            rel--;
            if (rel->r_offset >= esym->st_value + size)
              continue;
            if (rel->r_offset < esym->st_value)
              break;
            /* Note: if the same fields are initialized multiple
               times (possible with designators) then we possibly
               add multiple relocations for the same offset here.
               That would lead to wrong code, the last reloc needs
               to win.  We clean this up later after the whole
               initializer is parsed.  */
            put_elf_reloca(symtab_section, sec,
                   c + rel->r_offset - esym->st_value,
                   ELFW(R_TYPE)(rel->r_info),
                   ELFW(R_SYM)(rel->r_info),
#if PTR_SIZE == 8
                   rel->r_addend
#else
                   0
#endif
                  );
        }
        }
    } else {
            if (type->t & VT_BITFIELD) {
                int bit_pos, bit_size, bits, n;
                unsigned char *p, v, m;
                bit_pos = BIT_POS(vtop->type.t);
                bit_size = BIT_SIZE(vtop->type.t);
                p = (unsigned char*)ptr + (bit_pos >> 3);
                bit_pos &= 7, bits = 0;
                while (bit_size) {
                    n = 8 - bit_pos;
                    if (n > bit_size)
                        n = bit_size;
                    v = vtop->c.i >> bits << bit_pos;
                    m = ((1 << n) - 1) << bit_pos;
                    *p = (*p & ~m) | (v & m);
                    bits += n, bit_size -= n, bit_pos = 0, ++p;
                }
            } else
            switch(bt) {
        /* XXX: when cross-compiling we assume that each type has the
           same representation on host and target, which is likely to
           be wrong in the case of long double */
        case VT_BOOL:
        vtop->c.i = vtop->c.i != 0;
        case VT_BYTE:
        *(char *)ptr |= vtop->c.i;
        break;
        case VT_SHORT:
        *(short *)ptr |= vtop->c.i;
        break;
        case VT_FLOAT:
        *(float*)ptr = vtop->c.f;
        break;
        case VT_DOUBLE:
        *(double *)ptr = vtop->c.d;
        break;
        case VT_LDOUBLE:
#if defined TCC_IS_NATIVE_387
                if (sizeof (long double) >= 10) /* zero pad ten-byte LD */
                    memcpy(ptr, &vtop->c.ld, 10);
#ifdef __TINYC__
                else if (sizeof (long double) == sizeof (double))
                    __asm__("fldl %1\nfstpt %0\n" : "=m" (*ptr) : "m" (vtop->c.ld));
#endif
                else if (vtop->c.ld == 0.0)
                    ;
                else
#endif
                if (sizeof(long double) == LDOUBLE_SIZE)
            *(long double*)ptr = vtop->c.ld;
                else if (sizeof(double) == LDOUBLE_SIZE)
            *(double *)ptr = (double)vtop->c.ld;
                else
                    tcc_error("can't cross compile long double constants");
        break;
#if PTR_SIZE != 8
        case VT_LLONG:
        *(long long *)ptr |= vtop->c.i;
        break;
#else
        case VT_LLONG:
#endif
        case VT_PTR:
        {
            addr_t val = vtop->c.i;
#if PTR_SIZE == 8
            if (vtop->r & VT_SYM)
              greloca(sec, vtop->sym, c, R_DATA_PTR, val);
            else
              *(addr_t *)ptr |= val;
#else
            if (vtop->r & VT_SYM)
              greloc(sec, vtop->sym, c, R_DATA_PTR);
            *(addr_t *)ptr |= val;
#endif
            break;
        }
        default:
        {
            int val = vtop->c.i;
#if PTR_SIZE == 8
            if (vtop->r & VT_SYM)
              greloca(sec, vtop->sym, c, R_DATA_PTR, val);
            else
              *(int *)ptr |= val;
#else
            if (vtop->r & VT_SYM)
              greloc(sec, vtop->sym, c, R_DATA_PTR);
            *(int *)ptr |= val;
#endif
            break;
        }
        }
    }
        vtop--;
    } else {
        vset(&dtype, VT_LOCAL|VT_LVAL, c);
        vswap();
        vstore();
        vpop();
    }
}

/* 't' contains the type and storage info. 'c' is the offset of the
   object in section 'sec'. If 'sec' is NULL, it means stack based
   allocation. 'first' is true if array '{' must be read (multi
   dimension implicit array init handling). 'size_only' is true if
   size only evaluation is wanted (only for arrays). */
static void decl_initializer(CType *type, Section *sec, unsigned long c, 
                             int first, int size_only)
{
    int len, n, no_oblock, nb, i;
    int size1, align1;
    int have_elem;
    Sym *s, *f;
    Sym indexsym;
    CType *t1;

    /* If we currently are at an '}' or ',' we have read an initializer
       element in one of our callers, and not yet consumed it.  */
    have_elem = tok == '}' || tok == ',';
    if (!have_elem && tok != '{' &&
    /* In case of strings we have special handling for arrays, so
       don't consume them as initializer value (which would commit them
       to some anonymous symbol).  */
    tok != TOK_LSTR && tok != TOK_STR &&
    !size_only) {
    parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
    have_elem = 1;
    }

    if (have_elem &&
    !(type->t & VT_ARRAY) &&
    /* Use i_c_parameter_t, to strip toplevel qualifiers.
       The source type might have VT_CONSTANT set, which is
       of course assignable to non-const elements.  */
    is_compatible_unqualified_types(type, &vtop->type)) {
        init_putv(type, sec, c);
    } else if (type->t & VT_ARRAY) {
        s = type->ref;
        n = s->c;
        t1 = pointed_type(type);
        size1 = type_size(t1, &align1);

        no_oblock = 1;
        if ((first && tok != TOK_LSTR && tok != TOK_STR) || 
            tok == '{') {
            if (tok != '{')
                tcc_error("character array initializer must be a literal,"
                    " optionally enclosed in braces");
            skip('{');
            no_oblock = 0;
        }

        /* only parse strings here if correct type (otherwise: handle
           them as ((w)char *) expressions */
        if ((tok == TOK_LSTR && 
#ifdef TCC_TARGET_PE
             (t1->t & VT_BTYPE) == VT_SHORT && (t1->t & VT_UNSIGNED)
#else
             (t1->t & VT_BTYPE) == VT_INT
#endif
            ) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_BYTE)) {
        len = 0;
            while (tok == TOK_STR || tok == TOK_LSTR) {
                int cstr_len, ch;

                /* compute maximum number of chars wanted */
                if (tok == TOK_STR)
                    cstr_len = tokc.str.size;
                else
                    cstr_len = tokc.str.size / sizeof(nwchar_t);
                cstr_len--;
                nb = cstr_len;
                if (n >= 0 && nb > (n - len))
                    nb = n - len;
                if (!size_only) {
                    if (cstr_len > nb)
                        tcc_warning("initializer-string for array is too long");
                    /* in order to go faster for common case (char
                       string in global variable, we handle it
                       specifically */
                    if (sec && tok == TOK_STR && size1 == 1) {
                        if (!NODATA_WANTED)
                            memcpy(sec->data + c + len, tokc.str.data, nb);
                    } else {
                        for(i=0;i<nb;i++) {
                            if (tok == TOK_STR)
                                ch = ((unsigned char *)tokc.str.data)[i];
                            else
                                ch = ((nwchar_t *)tokc.str.data)[i];
                vpushi(ch);
                            init_putv(t1, sec, c + (len + i) * size1);
                        }
                    }
                }
                len += nb;
                next();
            }
            /* only add trailing zero if enough storage (no
               warning in this case since it is standard) */
            if (n < 0 || len < n) {
                if (!size_only) {
            vpushi(0);
                    init_putv(t1, sec, c + (len * size1));
                }
                len++;
            }
        len *= size1;
        } else {
        indexsym.c = 0;
        f = &indexsym;

          do_init_list:
        len = 0;
        while (tok != '}' || have_elem) {
        len = decl_designator(type, sec, c, &f, size_only, len);
        have_elem = 0;
        if (type->t & VT_ARRAY) {
            ++indexsym.c;
            /* special test for multi dimensional arrays (may not
               be strictly correct if designators are used at the
               same time) */
            if (no_oblock && len >= n*size1)
                break;
        } else {
            if (s->type.t == VT_UNION)
                f = NULL;
            else
                f = f->next;
            if (no_oblock && f == NULL)
                break;
        }

        if (tok == '}')
            break;
        skip(',');
        }
        }
        /* put zeros at the end */
    if (!size_only && len < n*size1)
        init_putz(sec, c + len, n*size1 - len);
        if (!no_oblock)
            skip('}');
        /* patch type size if needed, which happens only for array types */
        if (n < 0)
            s->c = size1 == 1 ? len : ((len + size1 - 1)/size1);
    } else if ((type->t & VT_BTYPE) == VT_STRUCT) {
    size1 = 1;
        no_oblock = 1;
        if (first || tok == '{') {
            skip('{');
            no_oblock = 0;
        }
        s = type->ref;
        f = s->next;
        n = s->c;
    goto do_init_list;
    } else if (tok == '{') {
        next();
        decl_initializer(type, sec, c, first, size_only);
        skip('}');
    } else if (size_only) {
    /* If we supported only ISO C we wouldn't have to accept calling
       this on anything than an array size_only==1 (and even then
       only on the outermost level, so no recursion would be needed),
       because initializing a flex array member isn't supported.
       But GNU C supports it, so we need to recurse even into
       subfields of structs and arrays when size_only is set.  */
        /* just skip expression */
        skip_or_save_block(NULL);
    } else {
    if (!have_elem) {
        /* This should happen only when we haven't parsed
           the init element above for fear of committing a
           string constant to memory too early.  */
        if (tok != TOK_STR && tok != TOK_LSTR)
          expect("string constant");
        parse_init_elem(!sec ? EXPR_ANY : EXPR_CONST);
    }
        init_putv(type, sec, c);
    }
}

/* parse an initializer for type 't' if 'has_init' is non zero, and
   allocate space in local or global data space ('r' is either
   VT_LOCAL or VT_CONST). If 'v' is non zero, then an associated
   variable 'v' of scope 'scope' is declared before initializers
   are parsed. If 'v' is zero, then a reference to the new object
   is put in the value stack. If 'has_init' is 2, a special parsing
   is done to handle string constants. */
static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, 
                                   int has_init, int v, int scope)
{
    int size, align, addr;
    TokenString *init_str = NULL;

    Section *sec;
    Sym *flexible_array;
    Sym *sym = NULL;
    int saved_nocode_wanted = nocode_wanted;
#ifdef CONFIG_TCC_BCHECK
    int bcheck = tcc_state->do_bounds_check && !NODATA_WANTED;
#endif

    if (type->t & VT_STATIC)
        nocode_wanted |= NODATA_WANTED ? 0x40000000 : 0x80000000;

    flexible_array = NULL;
    if ((type->t & VT_BTYPE) == VT_STRUCT) {
        Sym *field = type->ref->next;
        if (field) {
            while (field->next)
                field = field->next;
            if (field->type.t & VT_ARRAY && field->type.ref->c < 0)
                flexible_array = field;
        }
    }

    size = type_size(type, &align);
    /* If unknown size, we must evaluate it before
       evaluating initializers because
       initializers can generate global data too
       (e.g. string pointers or ISOC99 compound
       literals). It also simplifies local
       initializers handling */
    if (size < 0 || (flexible_array && has_init)) {
        if (!has_init) 
            tcc_error("unknown type size");
        /* get all init string */
        if (has_init == 2) {
        init_str = tok_str_alloc();
            /* only get strings */
            while (tok == TOK_STR || tok == TOK_LSTR) {
                tok_str_add_tok(init_str);
                next();
            }
        tok_str_add(init_str, -1);
        tok_str_add(init_str, 0);
        } else {
        skip_or_save_block(&init_str);
        }
        unget_tok(0);

        /* compute size */
        begin_macro(init_str, 1);
        next();
        decl_initializer(type, NULL, 0, 1, 1);
        /* prepare second initializer parsing */
        macro_ptr = init_str->str;
        next();
        
        /* if still unknown size, error */
        size = type_size(type, &align);
        if (size < 0) 
            tcc_error("unknown type size");
    }
    /* If there's a flex member and it was used in the initializer
       adjust size.  */
    if (flexible_array &&
    flexible_array->type.ref->c > 0)
        size += flexible_array->type.ref->c
            * pointed_size(&flexible_array->type);
    /* take into account specified alignment if bigger */
    if (ad->a.aligned) {
    int speca = 1 << (ad->a.aligned - 1);
        if (speca > align)
            align = speca;
    } else if (ad->a.packed) {
        align = 1;
    }

    if (NODATA_WANTED)
        size = 0, align = 1;

    if ((r & VT_VALMASK) == VT_LOCAL) {
        sec = NULL;
#ifdef CONFIG_TCC_BCHECK
        if (bcheck && (type->t & VT_ARRAY)) {
            loc--;
        }
#endif
        loc = (loc - size) & -align;
        addr = loc;
#ifdef CONFIG_TCC_BCHECK
        /* handles bounds */
        /* XXX: currently, since we do only one pass, we cannot track
           '&' operators, so we add only arrays */
        if (bcheck && (type->t & VT_ARRAY)) {
            addr_t *bounds_ptr;
            /* add padding between regions */
            loc--;
            /* then add local bound info */
            bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(addr_t));
            bounds_ptr[0] = addr;
            bounds_ptr[1] = size;
        }
#endif
        if (v) {
            /* local variable */
#ifdef CONFIG_TCC_ASM
        if (ad->asm_label) {
        int reg = asm_parse_regvar(ad->asm_label);
        if (reg >= 0)
            r = (r & ~VT_VALMASK) | reg;
        }
#endif
            sym = sym_push(v, type, r, addr);
            sym->a = ad->a;
        } else {
            /* push local reference */
            vset(type, r, addr);
        }
    } else {
        if (v && scope == VT_CONST) {
            /* see if the symbol was already defined */
            sym = sym_find(v);
            if (sym) {
                patch_storage(sym, ad, type);
                /* we accept several definitions of the same global variable. */
                if (!has_init && sym->c && elfsym(sym)->st_shndx != SHN_UNDEF)
                    goto no_alloc;
            }
        }

        /* allocate symbol in corresponding section */
        sec = ad->section;
        if (!sec) {
            if (has_init)
                sec = data_section;
            else if (tcc_state->nocommon)
                sec = bss_section;
        }

        if (sec) {
        addr = section_add(sec, size, align);
#ifdef CONFIG_TCC_BCHECK
            /* add padding if bound check */
            if (bcheck)
                section_add(sec, 1, 1);
#endif
        } else {
            addr = align; /* SHN_COMMON is special, symbol value is align */
        sec = common_section;
        }

        if (v) {
            if (!sym) {
                sym = sym_push(v, type, r | VT_SYM, 0);
                patch_storage(sym, ad, NULL);
            }
            /* Local statics have a scope until now (for
               warnings), remove it here.  */
            sym->sym_scope = 0;
            /* update symbol definition */
        put_extern_sym(sym, sec, addr, size);
        } else {
            /* push global reference */
            sym = get_sym_ref(type, sec, addr, size);
        vpushsym(type, sym);
        vtop->r |= r;
        }

#ifdef CONFIG_TCC_BCHECK
        /* handles bounds now because the symbol must be defined
           before for the relocation */
        if (bcheck) {
            addr_t *bounds_ptr;

            greloca(bounds_section, sym, bounds_section->data_offset, R_DATA_PTR, 0);
            /* then add global bound info */
            bounds_ptr = section_ptr_add(bounds_section, 2 * sizeof(addr_t));
            bounds_ptr[0] = 0; /* relocated */
            bounds_ptr[1] = size;
        }
#endif
    }

    if (type->t & VT_VLA) {
        int a;

        if (NODATA_WANTED)
            goto no_alloc;

        /* save current stack pointer */
        if (vlas_in_scope == 0) {
            if (vla_sp_root_loc == -1)
                vla_sp_root_loc = (loc -= PTR_SIZE);
            gen_vla_sp_save(vla_sp_root_loc);
        }

        vla_runtime_type_size(type, &a);
        gen_vla_alloc(type, a);
#if defined TCC_TARGET_PE && defined TCC_TARGET_X86_64
        /* on _WIN64, because of the function args scratch area, the
           result of alloca differs from RSP and is returned in RAX.  */
        gen_vla_result(addr), addr = (loc -= PTR_SIZE);
#endif
        gen_vla_sp_save(addr);
        vla_sp_loc = addr;
        vlas_in_scope++;

    } else if (has_init) {
    size_t oldreloc_offset = 0;
    if (sec && sec->reloc)
      oldreloc_offset = sec->reloc->data_offset;
        decl_initializer(type, sec, addr, 1, 0);
    if (sec && sec->reloc)
      squeeze_multi_relocs(sec, oldreloc_offset);
        /* patch flexible array member size back to -1, */
        /* for possible subsequent similar declarations */
        if (flexible_array)
            flexible_array->type.ref->c = -1;
    }

 no_alloc:
    /* restore parse state if needed */
    if (init_str) {
        end_macro();
        next();
    }

    nocode_wanted = saved_nocode_wanted;
}

/* parse a function defined by symbol 'sym' and generate its code in
   'cur_text_section' */
static void gen_function(Sym *sym)
{
    nocode_wanted = 0;
    ind = cur_text_section->data_offset;
    /* NOTE: we patch the symbol size later */
    put_extern_sym(sym, cur_text_section, ind, 0);
    funcname = get_tok_str(sym->v, NULL);
    func_ind = ind;
    /* Initialize VLA state */
    vla_sp_loc = -1;
    vla_sp_root_loc = -1;
    /* put debug symbol */
    tcc_debug_funcstart(tcc_state, sym);
    /* push a dummy symbol to enable local sym storage */
    sym_push2(&local_stack, SYM_FIELD, 0, 0);
    local_scope = 1; /* for function parameters */
    gfunc_prolog(&sym->type);
    local_scope = 0;
    rsym = 0;
    block(NULL, NULL, 0);
    nocode_wanted = 0;
    gsym(rsym);
    gfunc_epilog();
    cur_text_section->data_offset = ind;
    label_pop(&global_label_stack, NULL, 0);
    /* reset local stack */
    local_scope = 0;
    sym_pop(&local_stack, NULL, 0);
    /* end of function */
    /* patch symbol size */
    elfsym(sym)->st_size = ind - func_ind;
    tcc_debug_funcend(tcc_state, ind - func_ind);
    /* It's better to crash than to generate wrong code */
    cur_text_section = NULL;
    funcname = ""; /* for safety */
    func_vt.t = VT_VOID; /* for safety */
    func_var = 0; /* for safety */
    ind = 0; /* for safety */
    nocode_wanted = 0x80000000;
    check_vstack();
}

static void gen_inline_functions(TCCState *s)
{
    Sym *sym;
    int inline_generated, i, ln;
    struct InlineFunc *fn;

    ln = file->line_num;
    /* iterate while inline function are referenced */
    do {
        inline_generated = 0;
        for (i = 0; i < s->nb_inline_fns; ++i) {
            fn = s->inline_fns[i];
            sym = fn->sym;
            if (sym && sym->c) {
                /* the function was used: generate its code and
                   convert it to a normal function */
                fn->sym = NULL;
                if (file)
                    pstrcpy(file->filename, sizeof file->filename, fn->filename);
                sym->type.t &= ~VT_INLINE;

                begin_macro(fn->func_str, 1);
                next();
                cur_text_section = text_section;
                gen_function(sym);
                end_macro();

                inline_generated = 1;
            }
        }
    } while (inline_generated);
    file->line_num = ln;
}

ST_FUNC void free_inline_functions(TCCState *s)
{
    int i;
    /* free tokens of unused inline functions */
    for (i = 0; i < s->nb_inline_fns; ++i) {
        struct InlineFunc *fn = s->inline_fns[i];
        if (fn->sym)
            tok_str_free(fn->func_str);
    }
    dynarray_reset(&s->inline_fns, &s->nb_inline_fns);
}

/* 'l' is VT_LOCAL or VT_CONST to define default storage type, or VT_CMP
   if parsing old style parameter decl list (and FUNC_SYM is set then) */
static int decl0(int l, int is_for_loop_init, Sym *func_sym)
{
    int v, has_init, r;
    CType type, btype;
    Sym *sym;
    AttributeDef ad;

    while (1) {
        if (!parse_btype(&btype, &ad)) {
            if (is_for_loop_init)
                return 0;
            /* skip redundant ';' if not in old parameter decl scope */
            if (tok == ';' && l != VT_CMP) {
                next();
                continue;
            }
            if (l != VT_CONST)
                break;
            if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3) {
                /* global asm block */
                asm_global_instr();
                continue;
            }
            if (tok >= TOK_UIDENT) {
               /* special test for old K&R protos without explicit int
                  type. Only accepted when defining global data */
                btype.t = VT_INT;
            } else {
                if (tok != TOK_EOF)
                    expect("declaration");
                break;
            }
        }
        if (tok == ';') {
        if ((btype.t & VT_BTYPE) == VT_STRUCT) {
        int v = btype.ref->v;
        if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) >= SYM_FIRST_ANOM)
                tcc_warning("unnamed struct/union that defines no instances");
                next();
                continue;
        }
            if (IS_ENUM(btype.t)) {
                next();
                continue;
            }
        }
        while (1) { /* iterate thru each declaration */
            type = btype;
        /* If the base type itself was an array type of unspecified
           size (like in 'typedef int arr[]; arr x = {1};') then
           we will overwrite the unknown size by the real one for
           this decl.  We need to unshare the ref symbol holding
           that size.  */
        if ((type.t & VT_ARRAY) && type.ref->c < 0) {
        type.ref = sym_push(SYM_FIELD, &type.ref->type, 0, type.ref->c);
        }
            type_decl(&type, &ad, &v, TYPE_DIRECT);
#if 0
            {
                char buf[500];
                type_to_str(buf, sizeof(buf), &type, get_tok_str(v, NULL));
                printf("type = '%s'\n", buf);
            }
#endif
            if ((type.t & VT_BTYPE) == VT_FUNC) {
                if ((type.t & VT_STATIC) && (l == VT_LOCAL)) {
                    tcc_error("function without file scope cannot be static");
                }
                /* if old style function prototype, we accept a
                   declaration list */
                sym = type.ref;
                if (sym->f.func_type == FUNC_OLD && l == VT_CONST)
                    decl0(VT_CMP, 0, sym);
            }

            if (gnu_ext && (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
                ad.asm_label = asm_label_instr();
                /* parse one last attribute list, after asm label */
                parse_attribute(&ad);
                if (tok == '{')
                    expect(";");
            }

#ifdef TCC_TARGET_PE
            if (ad.a.dllimport || ad.a.dllexport) {
                if (type.t & (VT_STATIC|VT_TYPEDEF))
                    tcc_error("cannot have dll linkage with static or typedef");
                if (ad.a.dllimport) {
                    if ((type.t & VT_BTYPE) == VT_FUNC)
                        ad.a.dllimport = 0;
                    else
                        type.t |= VT_EXTERN;
                }
            }
#endif
            if (tok == '{') {
                if (l != VT_CONST)
                    tcc_error("cannot use local functions");
                if ((type.t & VT_BTYPE) != VT_FUNC)
                    expect("function definition");

                /* reject abstract declarators in function definition
           make old style params without decl have int type */
                sym = type.ref;
                while ((sym = sym->next) != NULL) {
                    if (!(sym->v & ~SYM_FIELD))
                        expect("identifier");
            if (sym->type.t == VT_VOID)
                sym->type = int_type;
        }
                
                /* XXX: cannot do better now: convert extern line to static inline */
                if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE))
                    type.t = (type.t & ~VT_EXTERN) | VT_STATIC;

                /* put function symbol */
                sym = external_global_sym(v, &type, 0);
                type.t &= ~VT_EXTERN;
                patch_storage(sym, &ad, &type);

                /* static inline functions are just recorded as a kind
                   of macro. Their code will be emitted at the end of
                   the compilation unit only if they are used */
                if ((type.t & (VT_INLINE | VT_STATIC)) == 
                    (VT_INLINE | VT_STATIC)) {
                    struct InlineFunc *fn;
                    const char *filename;
                           
                    filename = file ? file->filename : "";
                    fn = tcc_malloc(sizeof *fn + strlen(filename));
                    strcpy(fn->filename, filename);
                    fn->sym = sym;
            skip_or_save_block(&fn->func_str);
                    dynarray_add(&tcc_state->inline_fns,
                 &tcc_state->nb_inline_fns, fn);
                } else {
                    /* compute text section */
                    cur_text_section = ad.section;
                    if (!cur_text_section)
                        cur_text_section = text_section;
                    gen_function(sym);
                }
                break;
            } else {
        if (l == VT_CMP) {
            /* find parameter in function parameter list */
            for (sym = func_sym->next; sym; sym = sym->next)
            if ((sym->v & ~SYM_FIELD) == v)
                goto found;
            tcc_error("declaration for parameter '%s' but no such parameter",
                  get_tok_str(v, NULL));
found:
            if (type.t & VT_STORAGE) /* 'register' is okay */
                tcc_error("storage class specified for '%s'",
                  get_tok_str(v, NULL));
            if (sym->type.t != VT_VOID)
                tcc_error("redefinition of parameter '%s'",
                  get_tok_str(v, NULL));
            convert_parameter_type(&type);
            sym->type = type;
        } else if (type.t & VT_TYPEDEF) {
                    /* save typedefed type  */
                    /* XXX: test storage specifiers ? */
                    sym = sym_find(v);
                    if (sym && sym->sym_scope == local_scope) {
                        if (!is_compatible_types(&sym->type, &type)
                            || !(sym->type.t & VT_TYPEDEF))
                            tcc_error("incompatible redefinition of '%s'",
                                get_tok_str(v, NULL));
                        sym->type = type;
                    } else {
                        sym = sym_push(v, &type, 0, 0);
                    }
                    sym->a = ad.a;
                    sym->f = ad.f;
                } else {
                    r = 0;
                    if ((type.t & VT_BTYPE) == VT_FUNC) {
                        /* external function definition */
                        /* specific case for func_call attribute */
                        type.ref->f = ad.f;
                    } else if (!(type.t & VT_ARRAY)) {
                        /* not lvalue if array */
                        r |= lvalue_type(type.t);
                    }
                    has_init = (tok == '=');
                    if (has_init && (type.t & VT_VLA))
                        tcc_error("variable length array cannot be initialized");
                    if (((type.t & VT_EXTERN) && (!has_init || l != VT_CONST)) ||
            ((type.t & VT_BTYPE) == VT_FUNC) ||
                        ((type.t & VT_ARRAY) && (type.t & VT_STATIC) &&
                         !has_init && l == VT_CONST && type.ref->c < 0)) {
                        /* external variable or function */
                        /* NOTE: as GCC, uninitialized global static
                           arrays of null size are considered as
                           extern */
                        type.t |= VT_EXTERN;
                        sym = external_sym(v, &type, r, &ad);
                        if (ad.alias_target) {
                            ElfSym *esym;
                            Sym *alias_target;
                            alias_target = sym_find(ad.alias_target);
                            esym = elfsym(alias_target);
                            if (!esym)
                                tcc_error("unsupported forward __alias__ attribute");
                            /* Local statics have a scope until now (for
                               warnings), remove it here.  */
                            sym->sym_scope = 0;
                            put_extern_sym2(sym, esym->st_shndx, esym->st_value, esym->st_size, 0);
                        }
                    } else {
                        if (type.t & VT_STATIC)
                            r |= VT_CONST;
                        else
                            r |= l;
                        if (has_init)
                            next();
                        else if (l == VT_CONST)
                            /* uninitialized global variables may be overridden */
                            type.t |= VT_EXTERN;
                        decl_initializer_alloc(&type, &ad, r, has_init, v, l);
                    }
                }
                if (tok != ',') {
                    if (is_for_loop_init)
                        return 1;
                    skip(';');
                    break;
                }
                next();
            }
            ad.a.aligned = 0;
        }
    }
    return 0;
}

static void decl(int l)
{
    decl0(l, 0, NULL);
}

/* ------------------------------------------------------------------------- */

https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tccrun.c

File: ../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tccrun.c used in: Step 488
/*
 *  TCC - Tiny C Compiler - Support for -run switch
 *
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "tcc.h"

/* only native compiler supports -run */
#ifdef TCC_IS_NATIVE

#ifndef _WIN32
# include <sys/mman.h>
#endif

#ifdef CONFIG_TCC_BACKTRACE
# ifndef _WIN32
#  include <signal.h>
#  ifndef __OpenBSD__
#   include <sys/ucontext.h>
#  endif
# else
#  define ucontext_t CONTEXT
# endif
ST_DATA int rt_num_callers = 6;
ST_DATA const char **rt_bound_error_msg;
ST_DATA void *rt_prog_main;
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level);
static void rt_error(ucontext_t *uc, const char *fmt, ...);
static void set_exception_handler(void);
#endif

static void set_pages_executable(void *ptr, unsigned long length);
static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff);

#ifdef _WIN64
static void *win64_add_function_table(TCCState *s1);
static void win64_del_function_table(void *);
#endif

/* ------------------------------------------------------------- */
/* Do all relocations (needed before using tcc_get_symbol())
   Returns -1 on error. */

LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
{
    int size;
    addr_t ptr_diff = 0;

    if (TCC_RELOCATE_AUTO != ptr)
        return tcc_relocate_ex(s1, ptr, 0);

    size = tcc_relocate_ex(s1, NULL, 0);
    if (size < 0)
        return -1;

#ifdef HAVE_SELINUX
{
    /* Using mmap instead of malloc */
    void *prx;
    char tmpfname[] = "/tmp/.tccrunXXXXXX";
    int fd = mkstemp(tmpfname);
    unlink(tmpfname);
    ftruncate(fd, size);

    ptr = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    prx = mmap (NULL, size, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
    if (ptr == MAP_FAILED || prx == MAP_FAILED)
    tcc_error("tccrun: could not map memory");
    dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, (void*)(addr_t)size);
    dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, prx);
    ptr_diff = (char*)prx - (char*)ptr;
}
#else
    ptr = tcc_malloc(size);
#endif
    tcc_relocate_ex(s1, ptr, ptr_diff); /* no more errors expected */
    dynarray_add(&s1->runtime_mem, &s1->nb_runtime_mem, ptr);
    return 0;
}

ST_FUNC void tcc_run_free(TCCState *s1)
{
    int i;

    for (i = 0; i < s1->nb_runtime_mem; ++i) {
#ifdef HAVE_SELINUX
        unsigned size = (unsigned)(addr_t)s1->runtime_mem[i++];
        munmap(s1->runtime_mem[i++], size);
        munmap(s1->runtime_mem[i], size);
#else
#ifdef _WIN64
        win64_del_function_table(*(void**)s1->runtime_mem[i]);
#endif
        tcc_free(s1->runtime_mem[i]);
#endif
    }
    tcc_free(s1->runtime_mem);
}

/* launch the compiled program with the given arguments */
LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
{
    int (*prog_main)(int, char **);

    s1->runtime_main = "main";
    if ((s1->dflag & 16) && !find_elf_sym(s1->symtab, s1->runtime_main))
        return 0;
    if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
        return -1;
    prog_main = tcc_get_symbol_err(s1, s1->runtime_main);

#ifdef CONFIG_TCC_BACKTRACE
    if (s1->do_debug) {
        set_exception_handler();
        rt_prog_main = prog_main;
    }
#endif

    errno = 0; /* clean errno value */

#ifdef CONFIG_TCC_BCHECK
    if (s1->do_bounds_check) {
        void (*bound_init)(void);
        void (*bound_exit)(void);
        void (*bound_new_region)(void *p, addr_t size);
        int  (*bound_delete_region)(void *p);
        int i, ret;

        /* set error function */
        rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
        /* XXX: use .init section so that it also work in binary ? */
        bound_init = tcc_get_symbol_err(s1, "__bound_init");
        bound_exit = tcc_get_symbol_err(s1, "__bound_exit");
        bound_new_region = tcc_get_symbol_err(s1, "__bound_new_region");
        bound_delete_region = tcc_get_symbol_err(s1, "__bound_delete_region");

        bound_init();
        /* mark argv area as valid */
        bound_new_region(argv, argc*sizeof(argv[0]));
        for (i=0; i<argc; ++i)
            bound_new_region(argv[i], strlen(argv[i]) + 1);

        ret = (*prog_main)(argc, argv);

        /* unmark argv area */
        for (i=0; i<argc; ++i)
            bound_delete_region(argv[i]);
        bound_delete_region(argv);
        bound_exit();
        return ret;
    }
#endif
    return (*prog_main)(argc, argv);
}

#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
 #define RUN_SECTION_ALIGNMENT 63
#else
 #define RUN_SECTION_ALIGNMENT 15
#endif

/* relocate code. Return -1 on error, required size if ptr is NULL,
   otherwise copy code into buffer passed by the caller */
static int tcc_relocate_ex(TCCState *s1, void *ptr, addr_t ptr_diff)
{
    Section *s;
    unsigned offset, length, fill, i, k;
    addr_t mem;

    if (NULL == ptr) {
        s1->nb_errors = 0;
#ifdef TCC_TARGET_PE
        pe_output_file(s1, NULL);
#else
        tcc_add_runtime(s1);
    resolve_common_syms(s1);
        build_got_entries(s1);
#endif
        if (s1->nb_errors)
            return -1;
    }

    offset = 0, mem = (addr_t)ptr;
    fill = -mem & RUN_SECTION_ALIGNMENT;
#ifdef _WIN64
    offset += sizeof (void*);
#endif
    for (k = 0; k < 2; ++k) {
        for(i = 1; i < s1->nb_sections; i++) {
            s = s1->sections[i];
            if (0 == (s->sh_flags & SHF_ALLOC))
                continue;
            if (k != !(s->sh_flags & SHF_EXECINSTR))
                continue;
            offset += fill;
            if (!mem)
                s->sh_addr = 0;
            else if (s->sh_flags & SHF_EXECINSTR)
                s->sh_addr = mem + offset + ptr_diff;
            else
                s->sh_addr = mem + offset;
#if 0
            if (mem)
                printf("%-16s +%02lx %p %04x\n",
                    s->name, fill, (void*)s->sh_addr, (unsigned)s->data_offset);
#endif
            offset += s->data_offset;
            fill = -(mem + offset) & 15;
        }
#if RUN_SECTION_ALIGNMENT > 15
        /* To avoid that x86 processors would reload cached instructions each time
           when data is written in the near, we need to make sure that code and data
           do not share the same 64 byte unit */
        fill = -(mem + offset) & RUN_SECTION_ALIGNMENT;
#endif
    }

    /* relocate symbols */
    relocate_syms(s1, s1->symtab, 1);
    if (s1->nb_errors)
        return -1;

    if (0 == mem)
        return offset + RUN_SECTION_ALIGNMENT;

#ifdef TCC_TARGET_PE
    s1->pe_imagebase = mem;
#endif

    /* relocate each section */
    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if (s->reloc)
            relocate_section(s1, s);
    }
    relocate_plt(s1);

    for(i = 1; i < s1->nb_sections; i++) {
        s = s1->sections[i];
        if (0 == (s->sh_flags & SHF_ALLOC))
            continue;
        length = s->data_offset;
        ptr = (void*)s->sh_addr;
        if (s->sh_flags & SHF_EXECINSTR)
            ptr = (char*)ptr - ptr_diff;
        if (NULL == s->data || s->sh_type == SHT_NOBITS)
            memset(ptr, 0, length);
        else
            memcpy(ptr, s->data, length);
        /* mark executable sections as executable in memory */
        if (s->sh_flags & SHF_EXECINSTR)
            set_pages_executable((char*)ptr + ptr_diff, length);
    }

#ifdef _WIN64
    *(void**)mem = win64_add_function_table(s1);
#endif

    return 0;
}

/* ------------------------------------------------------------- */
/* allow to run code in memory */

static void set_pages_executable(void *ptr, unsigned long length)
{
#ifdef _WIN32
    unsigned long old_protect;
    VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
#else
    void __clear_cache(void *beginning, void *end);
# ifndef HAVE_SELINUX
    addr_t start, end;
#  ifndef PAGESIZE
#   define PAGESIZE 4096
#  endif
    start = (addr_t)ptr & ~(PAGESIZE - 1);
    end = (addr_t)ptr + length;
    end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
    if (mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC))
        tcc_error("mprotect failed: did you mean to configure --with-selinux?");
# endif
# if defined TCC_TARGET_ARM || defined TCC_TARGET_ARM64
    __clear_cache(ptr, (char *)ptr + length);
# endif
#endif
}

#ifdef _WIN64
static void *win64_add_function_table(TCCState *s1)
{
    void *p = NULL;
    if (s1->uw_pdata) {
        p = (void*)s1->uw_pdata->sh_addr;
        RtlAddFunctionTable(
            (RUNTIME_FUNCTION*)p,
            s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
            s1->pe_imagebase
            );
        s1->uw_pdata = NULL;
    }
    return p;
}

static void win64_del_function_table(void *p)
{
    if (p) {
        RtlDeleteFunctionTable((RUNTIME_FUNCTION*)p);
    }
}
#endif

/* ------------------------------------------------------------- */
#ifdef CONFIG_TCC_BACKTRACE

ST_FUNC void tcc_set_num_callers(int n)
{
    rt_num_callers = n;
}

/* print the position in the source file of PC value 'pc' by reading
   the stabs debug information */
static addr_t rt_printline(addr_t wanted_pc, const char *msg)
{
    char func_name[128], last_func_name[128];
    addr_t func_addr, last_pc, pc;
    const char *incl_files[INCLUDE_STACK_SIZE];
    int incl_index, len, last_line_num, i;
    const char *str, *p;

    Stab_Sym *stab_sym = NULL, *stab_sym_end, *sym;
    int stab_len = 0;
    char *stab_str = NULL;

    if (stab_section) {
        stab_len = stab_section->data_offset;
        stab_sym = (Stab_Sym *)stab_section->data;
        stab_str = (char *) stabstr_section->data;
    }

    func_name[0] = '\0';
    func_addr = 0;
    incl_index = 0;
    last_func_name[0] = '\0';
    last_pc = (addr_t)-1;
    last_line_num = 1;

    if (!stab_sym)
        goto no_stabs;

    stab_sym_end = (Stab_Sym*)((char*)stab_sym + stab_len);
    for (sym = stab_sym + 1; sym < stab_sym_end; ++sym) {
        switch(sym->n_type) {
            /* function start or end */
        case N_FUN:
            if (sym->n_strx == 0) {
                /* we test if between last line and end of function */
                pc = sym->n_value + func_addr;
                if (wanted_pc >= last_pc && wanted_pc < pc)
                    goto found;
                func_name[0] = '\0';
                func_addr = 0;
            } else {
                str = stab_str + sym->n_strx;
                p = strchr(str, ':');
                if (!p) {
                    pstrcpy(func_name, sizeof(func_name), str);
                } else {
                    len = p - str;
                    if (len > sizeof(func_name) - 1)
                        len = sizeof(func_name) - 1;
                    memcpy(func_name, str, len);
                    func_name[len] = '\0';
                }
                func_addr = sym->n_value;
            }
            break;
            /* line number info */
        case N_SLINE:
            pc = sym->n_value + func_addr;
            if (wanted_pc >= last_pc && wanted_pc < pc)
                goto found;
            last_pc = pc;
            last_line_num = sym->n_desc;
            /* XXX: slow! */
            strcpy(last_func_name, func_name);
            break;
            /* include files */
        case N_BINCL:
            str = stab_str + sym->n_strx;
        add_incl:
            if (incl_index < INCLUDE_STACK_SIZE) {
                incl_files[incl_index++] = str;
            }
            break;
        case N_EINCL:
            if (incl_index > 1)
                incl_index--;
            break;
        case N_SO:
            if (sym->n_strx == 0) {
                incl_index = 0; /* end of translation unit */
            } else {
                str = stab_str + sym->n_strx;
                /* do not add path */
                len = strlen(str);
                if (len > 0 && str[len - 1] != '/')
                    goto add_incl;
            }
            break;
        }
    }

no_stabs:
    /* second pass: we try symtab symbols (no line number info) */
    incl_index = 0;
    if (symtab_section)
    {
        ElfW(Sym) *sym, *sym_end;
        int type;

        sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
        for(sym = (ElfW(Sym) *)symtab_section->data + 1;
            sym < sym_end;
            sym++) {
            type = ELFW(ST_TYPE)(sym->st_info);
            if (type == STT_FUNC || type == STT_GNU_IFUNC) {
                if (wanted_pc >= sym->st_value &&
                    wanted_pc < sym->st_value + sym->st_size) {
                    pstrcpy(last_func_name, sizeof(last_func_name),
                            (char *) symtab_section->link->data + sym->st_name);
                    func_addr = sym->st_value;
                    goto found;
                }
            }
        }
    }
    /* did not find any info: */
    fprintf(stderr, "%s %p ???\n", msg, (void*)wanted_pc);
    fflush(stderr);
    return 0;
 found:
    i = incl_index;
    if (i > 0)
        fprintf(stderr, "%s:%d: ", incl_files[--i], last_line_num);
    fprintf(stderr, "%s %p", msg, (void*)wanted_pc);
    if (last_func_name[0] != '\0')
        fprintf(stderr, " %s()", last_func_name);
    if (--i >= 0) {
        fprintf(stderr, " (included from ");
        for (;;) {
            fprintf(stderr, "%s", incl_files[i]);
            if (--i < 0)
                break;
            fprintf(stderr, ", ");
        }
        fprintf(stderr, ")");
    }
    fprintf(stderr, "\n");
    fflush(stderr);
    return func_addr;
}

/* emit a run time error at position 'pc' */
static void rt_error(ucontext_t *uc, const char *fmt, ...)
{
    va_list ap;
    addr_t pc;
    int i;

    fprintf(stderr, "Runtime error: ");
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);
    fprintf(stderr, "\n");

    for(i=0;i<rt_num_callers;i++) {
        if (rt_get_caller_pc(&pc, uc, i) < 0)
            break;
        pc = rt_printline(pc, i ? "by" : "at");
        if (pc == (addr_t)rt_prog_main && pc)
            break;
    }
}

/* ------------------------------------------------------------- */
#ifndef _WIN32

/* signal handler for fatal errors */
static void sig_error(int signum, siginfo_t *siginf, void *puc)
{
    ucontext_t *uc = puc;

    switch(signum) {
    case SIGFPE:
        switch(siginf->si_code) {
        case FPE_INTDIV:
        case FPE_FLTDIV:
            rt_error(uc, "division by zero");
            break;
        default:
            rt_error(uc, "floating point exception");
            break;
        }
        break;
    case SIGBUS:
    case SIGSEGV:
        if (rt_bound_error_msg && *rt_bound_error_msg)
            rt_error(uc, *rt_bound_error_msg);
        else
            rt_error(uc, "dereferencing invalid pointer");
        break;
    case SIGILL:
        rt_error(uc, "illegal instruction");
        break;
    case SIGABRT:
        rt_error(uc, "abort() called");
        break;
    default:
        rt_error(uc, "caught signal %d", signum);
        break;
    }
    exit(255);
}

#ifndef SA_SIGINFO
# define SA_SIGINFO 0x00000004u
#endif

/* Generate a stack backtrace when a CPU exception occurs. */
static void set_exception_handler(void)
{
    struct sigaction sigact;
    /* install TCC signal handlers to print debug info on fatal
       runtime errors */
    sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
    sigact.sa_sigaction = sig_error;
    sigemptyset(&sigact.sa_mask);
    sigaction(SIGFPE, &sigact, NULL);
    sigaction(SIGILL, &sigact, NULL);
    sigaction(SIGSEGV, &sigact, NULL);
    sigaction(SIGBUS, &sigact, NULL);
    sigaction(SIGABRT, &sigact, NULL);
}

/* ------------------------------------------------------------- */
#ifdef __i386__

/* fix for glibc 2.1 */
#ifndef REG_EIP
#define REG_EIP EIP
#define REG_EBP EBP
#endif

/* return the PC at frame level 'level'. Return negative if not found */
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
    addr_t fp;
    int i;

    if (level == 0) {
#if defined(__APPLE__)
        *paddr = uc->uc_mcontext->__ss.__eip;
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
        *paddr = uc->uc_mcontext.mc_eip;
#elif defined(__dietlibc__)
        *paddr = uc->uc_mcontext.eip;
#elif defined(__NetBSD__)
        *paddr = uc->uc_mcontext.__gregs[_REG_EIP];
#elif defined(__OpenBSD__)
        *paddr = uc->sc_eip;
#else
        *paddr = uc->uc_mcontext.gregs[REG_EIP];
#endif
        return 0;
    } else {
#if defined(__APPLE__)
        fp = uc->uc_mcontext->__ss.__ebp;
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
        fp = uc->uc_mcontext.mc_ebp;
#elif defined(__dietlibc__)
        fp = uc->uc_mcontext.ebp;
#elif defined(__NetBSD__)
        fp = uc->uc_mcontext.__gregs[_REG_EBP];
#elif defined(__OpenBSD__)
        *paddr = uc->sc_ebp;
#else
        fp = uc->uc_mcontext.gregs[REG_EBP];
#endif
        for(i=1;i<level;i++) {
            /* XXX: check address validity with program info */
            if (fp <= 0x1000 || fp >= 0xc0000000)
                return -1;
            fp = ((addr_t *)fp)[0];
        }
        *paddr = ((addr_t *)fp)[1];
        return 0;
    }
}

/* ------------------------------------------------------------- */
#elif defined(__x86_64__)

/* return the PC at frame level 'level'. Return negative if not found */
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
    addr_t fp;
    int i;

    if (level == 0) {
        /* XXX: only support linux */
#if defined(__APPLE__)
        *paddr = uc->uc_mcontext->__ss.__rip;
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
        *paddr = uc->uc_mcontext.mc_rip;
#elif defined(__NetBSD__)
        *paddr = uc->uc_mcontext.__gregs[_REG_RIP];
#else
        *paddr = uc->uc_mcontext.gregs[REG_RIP];
#endif
        return 0;
    } else {
#if defined(__APPLE__)
        fp = uc->uc_mcontext->__ss.__rbp;
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
        fp = uc->uc_mcontext.mc_rbp;
#elif defined(__NetBSD__)
        fp = uc->uc_mcontext.__gregs[_REG_RBP];
#else
        fp = uc->uc_mcontext.gregs[REG_RBP];
#endif
        for(i=1;i<level;i++) {
            /* XXX: check address validity with program info */
            if (fp <= 0x1000)
                return -1;
            fp = ((addr_t *)fp)[0];
        }
        *paddr = ((addr_t *)fp)[1];
        return 0;
    }
}

/* ------------------------------------------------------------- */
#elif defined(__arm__)

/* return the PC at frame level 'level'. Return negative if not found */
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
    addr_t fp, sp;
    int i;

    if (level == 0) {
        /* XXX: only supports linux */
#if defined(__linux__)
        *paddr = uc->uc_mcontext.arm_pc;
#else
        return -1;
#endif
        return 0;
    } else {
#if defined(__linux__)
        fp = uc->uc_mcontext.arm_fp;
        sp = uc->uc_mcontext.arm_sp;
        if (sp < 0x1000)
            sp = 0x1000;
#else
        return -1;
#endif
        /* XXX: specific to tinycc stack frames */
        if (fp < sp + 12 || fp & 3)
            return -1;
        for(i = 1; i < level; i++) {
            sp = ((addr_t *)fp)[-2];
            if (sp < fp || sp - fp > 16 || sp & 3)
                return -1;
            fp = ((addr_t *)fp)[-3];
            if (fp <= sp || fp - sp < 12 || fp & 3)
                return -1;
        }
        /* XXX: check address validity with program info */
        *paddr = ((addr_t *)fp)[-1];
        return 0;
    }
}

/* ------------------------------------------------------------- */
#elif defined(__aarch64__)

static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
    if (level < 0)
        return -1;
    else if (level == 0) {
        *paddr = uc->uc_mcontext.pc;
        return 0;
    }
    else {
        addr_t *fp = (addr_t *)uc->uc_mcontext.regs[29];
        int i;
        for (i = 1; i < level; i++)
            fp = (addr_t *)fp[0];
        *paddr = fp[1];
        return 0;
    }
}

/* ------------------------------------------------------------- */
#else

#warning add arch specific rt_get_caller_pc()
static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
{
    return -1;
}

#endif /* !__i386__ */

/* ------------------------------------------------------------- */
#else /* WIN32 */

static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
{
    EXCEPTION_RECORD *er = ex_info->ExceptionRecord;
    CONTEXT *uc = ex_info->ContextRecord;
    switch (er->ExceptionCode) {
    case EXCEPTION_ACCESS_VIOLATION:
        if (rt_bound_error_msg && *rt_bound_error_msg)
            rt_error(uc, *rt_bound_error_msg);
        else
        rt_error(uc, "access violation");
        break;
    case EXCEPTION_STACK_OVERFLOW:
        rt_error(uc, "stack overflow");
        break;
    case EXCEPTION_INT_DIVIDE_BY_ZERO:
        rt_error(uc, "division by zero");
        break;
    default:
        rt_error(uc, "exception caught");
        break;
    }
    return EXCEPTION_EXECUTE_HANDLER;
}

/* Generate a stack backtrace when a CPU exception occurs. */
static void set_exception_handler(void)
{
    SetUnhandledExceptionFilter(cpu_exception_handler);
}

/* return the PC at frame level 'level'. Return non zero if not found */
static int rt_get_caller_pc(addr_t *paddr, CONTEXT *uc, int level)
{
    addr_t fp, pc;
    int i;
#ifdef _WIN64
    pc = uc->Rip;
    fp = uc->Rbp;
#else
    pc = uc->Eip;
    fp = uc->Ebp;
#endif
    if (level > 0) {
        for(i=1;i<level;i++) {
        /* XXX: check address validity with program info */
        if (fp <= 0x1000 || fp >= 0xc0000000)
        return -1;
        fp = ((addr_t*)fp)[0];
    }
        pc = ((addr_t*)fp)[1];
    }
    *paddr = pc;
    return 0;
}

#endif /* _WIN32 */
#endif /* CONFIG_TCC_BACKTRACE */
/* ------------------------------------------------------------- */
#ifdef CONFIG_TCC_STATIC

/* dummy function for profiling */
ST_FUNC void *dlopen(const char *filename, int flag)
{
    return NULL;
}

ST_FUNC void dlclose(void *p)
{
}

ST_FUNC const char *dlerror(void)
{
    return "error";
}

typedef struct TCCSyms {
    char *str;
    void *ptr;
} TCCSyms;


/* add the symbol you want here if no dynamic linking is done */
static TCCSyms tcc_syms[] = {
#if !defined(CONFIG_TCCBOOT)
#define TCCSYM(a) { #a, &a, },
    TCCSYM(printf)
    TCCSYM(fprintf)
    TCCSYM(fopen)
    TCCSYM(fclose)
#undef TCCSYM
#endif
    { NULL, NULL },
};

ST_FUNC void *dlsym(void *handle, const char *symbol)
{
    TCCSyms *p;
    p = tcc_syms;
    while (p->str != NULL) {
        if (!strcmp(p->str, symbol))
            return p->ptr;
        p++;
    }
    return NULL;
}

#endif /* CONFIG_TCC_STATIC */
#endif /* TCC_IS_NATIVE */
/* ------------------------------------------------------------- */

https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/i386-gen.c

File: ../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/i386-gen.c used in: Step 488
/*
 *  X86 code generator for TCC
 * 
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef TARGET_DEFS_ONLY

/* number of available registers */
#define NB_REGS         5
#define NB_ASM_REGS     8
#define CONFIG_TCC_ASM

/* a register can belong to several classes. The classes must be
   sorted from more general to more precise (see gv2() code which does
   assumptions on it). */
#define RC_INT     0x0001 /* generic integer register */
#define RC_FLOAT   0x0002 /* generic float register */
#define RC_EAX     0x0004
#define RC_ST0     0x0008 
#define RC_ECX     0x0010
#define RC_EDX     0x0020
#define RC_EBX     0x0040

#define RC_IRET    RC_EAX /* function return: integer register */
#define RC_LRET    RC_EDX /* function return: second integer register */
#define RC_FRET    RC_ST0 /* function return: float register */

/* pretty names for the registers */
enum {
    TREG_EAX = 0,
    TREG_ECX,
    TREG_EDX,
    TREG_EBX,
    TREG_ST0,
    TREG_ESP = 4
};

/* return registers for function */
#define REG_IRET TREG_EAX /* single word int return register */
#define REG_LRET TREG_EDX /* second word return register (for long long) */
#define REG_FRET TREG_ST0 /* float return register */

/* defined if function parameters must be evaluated in reverse order */
#define INVERT_FUNC_PARAMS

/* defined if structures are passed as pointers. Otherwise structures
   are directly pushed on stack. */
/* #define FUNC_STRUCT_PARAM_AS_PTR */

/* pointer size, in bytes */
#define PTR_SIZE 4

/* long double size and alignment, in bytes */
#define LDOUBLE_SIZE  12
#define LDOUBLE_ALIGN 4
/* maximum alignment (for aligned attribute support) */
#define MAX_ALIGN     8

/******************************************************/
#else /* ! TARGET_DEFS_ONLY */
/******************************************************/
#include "tcc.h"

/* define to 1/0 to [not] have EBX as 4th register */
#define USE_EBX 0

ST_DATA const int reg_classes[NB_REGS] = {
    /* eax */ RC_INT | RC_EAX,
    /* ecx */ RC_INT | RC_ECX,
    /* edx */ RC_INT | RC_EDX,
    /* ebx */ (RC_INT | RC_EBX) * USE_EBX,
    /* st0 */ RC_FLOAT | RC_ST0,
};

static unsigned long func_sub_sp_offset;
static int func_ret_sub;
#ifdef CONFIG_TCC_BCHECK
static addr_t func_bound_offset;
static unsigned long func_bound_ind;
#endif

/* XXX: make it faster ? */
ST_FUNC void g(int c)
{
    int ind1;
    if (nocode_wanted)
        return;
    ind1 = ind + 1;
    if (ind1 > cur_text_section->data_allocated)
        section_realloc(cur_text_section, ind1);
    cur_text_section->data[ind] = c;
    ind = ind1;
}

ST_FUNC void o(unsigned int c)
{
    while (c) {
        g(c);
        c = c >> 8;
    }
}

ST_FUNC void gen_le16(int v)
{
    g(v);
    g(v >> 8);
}

ST_FUNC void gen_le32(int c)
{
    g(c);
    g(c >> 8);
    g(c >> 16);
    g(c >> 24);
}

/* output a symbol and patch all calls to it */
ST_FUNC void gsym_addr(int t, int a)
{
    while (t) {
        unsigned char *ptr = cur_text_section->data + t;
        uint32_t n = read32le(ptr); /* next value */
        write32le(ptr, a - t - 4);
        t = n;
    }
}

ST_FUNC void gsym(int t)
{
    gsym_addr(t, ind);
}

/* instruction + 4 bytes data. Return the address of the data */
static int oad(int c, int s)
{
    int t;
    if (nocode_wanted)
        return s;
    o(c);
    t = ind;
    gen_le32(s);
    return t;
}

/* generate jmp to a label */
#define gjmp2(instr,lbl) oad(instr,lbl)

/* output constant with relocation if 'r & VT_SYM' is true */
ST_FUNC void gen_addr32(int r, Sym *sym, int c)
{
    if (r & VT_SYM)
        greloc(cur_text_section, sym, ind, R_386_32);
    gen_le32(c);
}

ST_FUNC void gen_addrpc32(int r, Sym *sym, int c)
{
    if (r & VT_SYM)
        greloc(cur_text_section, sym, ind, R_386_PC32);
    gen_le32(c - 4);
}

/* generate a modrm reference. 'op_reg' contains the additional 3
   opcode bits */
static void gen_modrm(int op_reg, int r, Sym *sym, int c)
{
    op_reg = op_reg << 3;
    if ((r & VT_VALMASK) == VT_CONST) {
        /* constant memory reference */
        o(0x05 | op_reg);
        gen_addr32(r, sym, c);
    } else if ((r & VT_VALMASK) == VT_LOCAL) {
        /* currently, we use only ebp as base */
        if (c == (char)c) {
            /* short reference */
            o(0x45 | op_reg);
            g(c);
        } else {
            oad(0x85 | op_reg, c);
        }
    } else {
        g(0x00 | op_reg | (r & VT_VALMASK));
    }
}

/* load 'r' from value 'sv' */
ST_FUNC void load(int r, SValue *sv)
{
    int v, t, ft, fc, fr;
    SValue v1;

#ifdef TCC_TARGET_PE
    SValue v2;
    sv = pe_getimport(sv, &v2);
#endif

    fr = sv->r;
    ft = sv->type.t & ~VT_DEFSIGN;
    fc = sv->c.i;

    ft &= ~(VT_VOLATILE | VT_CONSTANT);

    v = fr & VT_VALMASK;
    if (fr & VT_LVAL) {
        if (v == VT_LLOCAL) {
            v1.type.t = VT_INT;
            v1.r = VT_LOCAL | VT_LVAL;
            v1.c.i = fc;
            fr = r;
            if (!(reg_classes[fr] & RC_INT))
                fr = get_reg(RC_INT);
            load(fr, &v1);
        }
        if ((ft & VT_BTYPE) == VT_FLOAT) {
            o(0xd9); /* flds */
            r = 0;
        } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
            o(0xdd); /* fldl */
            r = 0;
        } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
            o(0xdb); /* fldt */
            r = 5;
        } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) {
            o(0xbe0f);   /* movsbl */
        } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
            o(0xb60f);   /* movzbl */
        } else if ((ft & VT_TYPE) == VT_SHORT) {
            o(0xbf0f);   /* movswl */
        } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
            o(0xb70f);   /* movzwl */
        } else {
            o(0x8b);     /* movl */
        }
        gen_modrm(r, fr, sv->sym, fc);
    } else {
        if (v == VT_CONST) {
            o(0xb8 + r); /* mov $xx, r */
            gen_addr32(fr, sv->sym, fc);
        } else if (v == VT_LOCAL) {
            if (fc) {
                o(0x8d); /* lea xxx(%ebp), r */
                gen_modrm(r, VT_LOCAL, sv->sym, fc);
            } else {
                o(0x89);
                o(0xe8 + r); /* mov %ebp, r */
            }
        } else if (v == VT_CMP) {
            oad(0xb8 + r, 0); /* mov $0, r */
            o(0x0f); /* setxx %br */
            o(fc);
            o(0xc0 + r);
        } else if (v == VT_JMP || v == VT_JMPI) {
            t = v & 1;
            oad(0xb8 + r, t); /* mov $1, r */
            o(0x05eb); /* jmp after */
            gsym(fc);
            oad(0xb8 + r, t ^ 1); /* mov $0, r */
        } else if (v != r) {
            o(0x89);
            o(0xc0 + r + v * 8); /* mov v, r */
        }
    }
}

/* store register 'r' in lvalue 'v' */
ST_FUNC void store(int r, SValue *v)
{
    int fr, bt, ft, fc;

#ifdef TCC_TARGET_PE
    SValue v2;
    v = pe_getimport(v, &v2);
#endif

    ft = v->type.t;
    fc = v->c.i;
    fr = v->r & VT_VALMASK;
    ft &= ~(VT_VOLATILE | VT_CONSTANT);
    bt = ft & VT_BTYPE;
    /* XXX: incorrect if float reg to reg */
    if (bt == VT_FLOAT) {
        o(0xd9); /* fsts */
        r = 2;
    } else if (bt == VT_DOUBLE) {
        o(0xdd); /* fstpl */
        r = 2;
    } else if (bt == VT_LDOUBLE) {
        o(0xc0d9); /* fld %st(0) */
        o(0xdb); /* fstpt */
        r = 7;
    } else {
        if (bt == VT_SHORT)
            o(0x66);
        if (bt == VT_BYTE || bt == VT_BOOL)
            o(0x88);
        else
            o(0x89);
    }
    if (fr == VT_CONST ||
        fr == VT_LOCAL ||
        (v->r & VT_LVAL)) {
        gen_modrm(r, v->r, v->sym, fc);
    } else if (fr != r) {
        o(0xc0 + fr + r * 8); /* mov r, fr */
    }
}

static void gadd_sp(int val)
{
    if (val == (char)val) {
        o(0xc483);
        g(val);
    } else {
        oad(0xc481, val); /* add $xxx, %esp */
    }
}

#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_PE
static void gen_static_call(int v)
{
    Sym *sym;

    sym = external_global_sym(v, &func_old_type, 0);
    oad(0xe8, -4);
    greloc(cur_text_section, sym, ind-4, R_386_PC32);
}
#endif

/* 'is_jmp' is '1' if it is a jump */
static void gcall_or_jmp(int is_jmp)
{
    int r;
    if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST && (vtop->r & VT_SYM)) {
        /* constant and relocation case */
        greloc(cur_text_section, vtop->sym, ind + 1, R_386_PC32);
        oad(0xe8 + is_jmp, vtop->c.i - 4); /* call/jmp im */
    } else {
        /* otherwise, indirect call */
        r = gv(RC_INT);
        o(0xff); /* call/jmp *r */
        o(0xd0 + r + (is_jmp << 4));
    }
    if (!is_jmp) {
        int rt;
        /* extend the return value to the whole register if necessary
           visual studio and gcc do not always set the whole eax register
           when assigning the return value of a function  */
        rt = vtop->type.ref->type.t;
        switch (rt & VT_BTYPE) {
            case VT_BYTE:
                if (rt & VT_UNSIGNED) {
                    o(0xc0b60f); /* movzx %al, %eax */
                }
                else {
                    o(0xc0be0f); /* movsx %al, %eax */
                }
                break;
            case VT_SHORT:
                if (rt & VT_UNSIGNED) {
                    o(0xc0b70f); /* movzx %ax, %eax */
                }
                else {
                    o(0xc0bf0f); /* movsx %ax, %eax */
                }
                break;
            default:
                break;
        }
    }
}

static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX };
static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX };

/* Return the number of registers needed to return the struct, or 0 if
   returning via struct pointer. */
ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize)
{
#ifdef TCC_TARGET_PE
    int size, align;
    *ret_align = 1; // Never have to re-align return values for x86
    *regsize = 4;
    size = type_size(vt, &align);
    if (size > 8 || (size & (size - 1)))
        return 0;
    if (size == 8)
        ret->t = VT_LLONG;
    else if (size == 4)
        ret->t = VT_INT;
    else if (size == 2)
        ret->t = VT_SHORT;
    else
        ret->t = VT_BYTE;
    ret->ref = NULL;
    return 1;
#else
    *ret_align = 1; // Never have to re-align return values for x86
    return 0;
#endif
}

/* Generate function call. The function address is pushed first, then
   all the parameters in call order. This functions pops all the
   parameters and the function address. */
ST_FUNC void gfunc_call(int nb_args)
{
    int size, align, r, args_size, i, func_call;
    Sym *func_sym;
    
    args_size = 0;
    for(i = 0;i < nb_args; i++) {
        if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
            size = type_size(&vtop->type, &align);
            /* align to stack align size */
            size = (size + 3) & ~3;
            /* allocate the necessary size on stack */
            oad(0xec81, size); /* sub $xxx, %esp */
            /* generate structure store */
            r = get_reg(RC_INT);
            o(0x89); /* mov %esp, r */
            o(0xe0 + r);
            vset(&vtop->type, r | VT_LVAL, 0);
            vswap();
            vstore();
            args_size += size;
        } else if (is_float(vtop->type.t)) {
            gv(RC_FLOAT); /* only one float register */
            if ((vtop->type.t & VT_BTYPE) == VT_FLOAT)
                size = 4;
            else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
                size = 8;
            else
                size = 12;
            oad(0xec81, size); /* sub $xxx, %esp */
            if (size == 12)
                o(0x7cdb);
            else
                o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */
            g(0x24);
            g(0x00);
            args_size += size;
        } else {
            /* simple type (currently always same size) */
            /* XXX: implicit cast ? */
            r = gv(RC_INT);
            if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
                size = 8;
                o(0x50 + vtop->r2); /* push r */
            } else {
                size = 4;
            }
            o(0x50 + r); /* push r */
            args_size += size;
        }
        vtop--;
    }
    save_regs(0); /* save used temporary registers */
    func_sym = vtop->type.ref;
    func_call = func_sym->f.func_call;
    /* fast call case */
    if ((func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) ||
        func_call == FUNC_FASTCALLW) {
        int fastcall_nb_regs;
        uint8_t *fastcall_regs_ptr;
        if (func_call == FUNC_FASTCALLW) {
            fastcall_regs_ptr = fastcallw_regs;
            fastcall_nb_regs = 2;
        } else {
            fastcall_regs_ptr = fastcall_regs;
            fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
        }
        for(i = 0;i < fastcall_nb_regs; i++) {
            if (args_size <= 0)
                break;
            o(0x58 + fastcall_regs_ptr[i]); /* pop r */
            /* XXX: incorrect for struct/floats */
            args_size -= 4;
        }
    }
#ifndef TCC_TARGET_PE
    else if ((vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT)
        args_size -= 4;
#endif
    gcall_or_jmp(0);

    if (args_size && func_call != FUNC_STDCALL && func_call != FUNC_FASTCALLW)
        gadd_sp(args_size);
    vtop--;
}

#ifdef TCC_TARGET_PE
#define FUNC_PROLOG_SIZE (10 + USE_EBX)
#else
#define FUNC_PROLOG_SIZE (9 + USE_EBX)
#endif

/* generate function prolog of type 't' */
ST_FUNC void gfunc_prolog(CType *func_type)
{
    int addr, align, size, func_call, fastcall_nb_regs;
    int param_index, param_addr;
    uint8_t *fastcall_regs_ptr;
    Sym *sym;
    CType *type;

    sym = func_type->ref;
    func_call = sym->f.func_call;
    addr = 8;
    loc = 0;
    func_vc = 0;

    if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) {
        fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
        fastcall_regs_ptr = fastcall_regs;
    } else if (func_call == FUNC_FASTCALLW) {
        fastcall_nb_regs = 2;
        fastcall_regs_ptr = fastcallw_regs;
    } else {
        fastcall_nb_regs = 0;
        fastcall_regs_ptr = NULL;
    }
    param_index = 0;

    ind += FUNC_PROLOG_SIZE;
    func_sub_sp_offset = ind;
    /* if the function returns a structure, then add an
       implicit pointer parameter */
    func_vt = sym->type;
    func_var = (sym->f.func_type == FUNC_ELLIPSIS);
#ifdef TCC_TARGET_PE
    size = type_size(&func_vt,&align);
    if (((func_vt.t & VT_BTYPE) == VT_STRUCT)
        && (size > 8 || (size & (size - 1)))) {
#else
    if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
#endif
        /* XXX: fastcall case ? */
        func_vc = addr;
        addr += 4;
        param_index++;
    }
    /* define parameters */
    while ((sym = sym->next) != NULL) {
        type = &sym->type;
        size = type_size(type, &align);
        size = (size + 3) & ~3;
#ifdef FUNC_STRUCT_PARAM_AS_PTR
        /* structs are passed as pointer */
        if ((type->t & VT_BTYPE) == VT_STRUCT) {
            size = 4;
        }
#endif
        if (param_index < fastcall_nb_regs) {
            /* save FASTCALL register */
            loc -= 4;
            o(0x89);     /* movl */
            gen_modrm(fastcall_regs_ptr[param_index], VT_LOCAL, NULL, loc);
            param_addr = loc;
        } else {
            param_addr = addr;
            addr += size;
        }
        sym_push(sym->v & ~SYM_FIELD, type,
                 VT_LOCAL | lvalue_type(type->t), param_addr);
        param_index++;
    }
    func_ret_sub = 0;
    /* pascal type call or fastcall ? */
    if (func_call == FUNC_STDCALL || func_call == FUNC_FASTCALLW)
        func_ret_sub = addr - 8;
#ifndef TCC_TARGET_PE
    else if (func_vc)
        func_ret_sub = 4;
#endif

#ifdef CONFIG_TCC_BCHECK
    /* leave some room for bound checking code */
    if (tcc_state->do_bounds_check) {
        func_bound_offset = lbounds_section->data_offset;
        func_bound_ind = ind;
        oad(0xb8, 0); /* lbound section pointer */
        oad(0xb8, 0); /* call to function */
    }
#endif
}

/* generate function epilog */
ST_FUNC void gfunc_epilog(void)
{
    addr_t v, saved_ind;

#ifdef CONFIG_TCC_BCHECK
    if (tcc_state->do_bounds_check
     && func_bound_offset != lbounds_section->data_offset) {
        addr_t saved_ind;
        addr_t *bounds_ptr;
        Sym *sym_data;

        /* add end of table info */
        bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t));
        *bounds_ptr = 0;

        /* generate bound local allocation */
        saved_ind = ind;
        ind = func_bound_ind;
        sym_data = get_sym_ref(&char_pointer_type, lbounds_section, 
                               func_bound_offset, lbounds_section->data_offset);
        greloc(cur_text_section, sym_data,
               ind + 1, R_386_32);
        oad(0xb8, 0); /* mov %eax, xxx */
        gen_static_call(TOK___bound_local_new);
        ind = saved_ind;

        /* generate bound check local freeing */
        o(0x5250); /* save returned value, if any */
        greloc(cur_text_section, sym_data, ind + 1, R_386_32);
        oad(0xb8, 0); /* mov %eax, xxx */
        gen_static_call(TOK___bound_local_delete);
        o(0x585a); /* restore returned value, if any */
    }
#endif

    /* align local size to word & save local variables */
    v = (-loc + 3) & -4;

#if USE_EBX
    o(0x8b);
    gen_modrm(TREG_EBX, VT_LOCAL, NULL, -(v+4));
#endif

    o(0xc9); /* leave */
    if (func_ret_sub == 0) {
        o(0xc3); /* ret */
    } else {
        o(0xc2); /* ret n */
        g(func_ret_sub);
        g(func_ret_sub >> 8);
    }
    saved_ind = ind;
    ind = func_sub_sp_offset - FUNC_PROLOG_SIZE;
#ifdef TCC_TARGET_PE
    if (v >= 4096) {
        oad(0xb8, v); /* mov stacksize, %eax */
        gen_static_call(TOK___chkstk); /* call __chkstk, (does the stackframe too) */
    } else
#endif
    {
        o(0xe58955);  /* push %ebp, mov %esp, %ebp */
        o(0xec81);  /* sub esp, stacksize */
        gen_le32(v);
#ifdef TCC_TARGET_PE
        o(0x90);  /* adjust to FUNC_PROLOG_SIZE */
#endif
    }
    o(0x53 * USE_EBX); /* push ebx */
    ind = saved_ind;
}

/* generate a jump to a label */
ST_FUNC int gjmp(int t)
{
    return gjmp2(0xe9, t);
}

/* generate a jump to a fixed address */
ST_FUNC void gjmp_addr(int a)
{
    int r;
    r = a - ind - 2;
    if (r == (char)r) {
        g(0xeb);
        g(r);
    } else {
        oad(0xe9, a - ind - 5);
    }
}

ST_FUNC void gtst_addr(int inv, int a)
{
    int v = vtop->r & VT_VALMASK;
    if (v == VT_CMP) {
    inv ^= (vtop--)->c.i;
    a -= ind + 2;
    if (a == (char)a) {
        g(inv - 32);
        g(a);
    } else {
        g(0x0f);
        oad(inv - 16, a - 4);
    }
    } else if ((v & ~1) == VT_JMP) {
    if ((v & 1) != inv) {
        gjmp_addr(a);
        gsym(vtop->c.i);
    } else {
        gsym(vtop->c.i);
        o(0x05eb);
        gjmp_addr(a);
    }
    vtop--;
    }
}

/* generate a test. set 'inv' to invert test. Stack entry is popped */
ST_FUNC int gtst(int inv, int t)
{
    int v = vtop->r & VT_VALMASK;
    if (nocode_wanted) {
        ;
    } else if (v == VT_CMP) {
        /* fast case : can jump directly since flags are set */
        g(0x0f);
        t = gjmp2((vtop->c.i - 16) ^ inv, t);
    } else if (v == VT_JMP || v == VT_JMPI) {
        /* && or || optimization */
        if ((v & 1) == inv) {
            /* insert vtop->c jump list in t */
            uint32_t n1, n = vtop->c.i;
            if (n) {
                while ((n1 = read32le(cur_text_section->data + n)))
                    n = n1;
                write32le(cur_text_section->data + n, t);
                t = vtop->c.i;
            }
        } else {
            t = gjmp(t);
            gsym(vtop->c.i);
        }
    }
    vtop--;
    return t;
}

/* generate an integer binary operation */
ST_FUNC void gen_opi(int op)
{
    int r, fr, opc, c;

    switch(op) {
    case '+':
    case TOK_ADDC1: /* add with carry generation */
        opc = 0;
    gen_op8:
        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
            /* constant case */
            vswap();
            r = gv(RC_INT);
            vswap();
            c = vtop->c.i;
            if (c == (char)c) {
                /* generate inc and dec for smaller code */
                if (c==1 && opc==0 && op != TOK_ADDC1) {
                    o (0x40 | r); // inc
                } else if (c==1 && opc==5 && op != TOK_SUBC1) {
                    o (0x48 | r); // dec
                } else {
                    o(0x83);
                    o(0xc0 | (opc << 3) | r);
                    g(c);
                }
            } else {
                o(0x81);
                oad(0xc0 | (opc << 3) | r, c);
            }
        } else {
            gv2(RC_INT, RC_INT);
            r = vtop[-1].r;
            fr = vtop[0].r;
            o((opc << 3) | 0x01);
            o(0xc0 + r + fr * 8); 
        }
        vtop--;
        if (op >= TOK_ULT && op <= TOK_GT) {
            vtop->r = VT_CMP;
            vtop->c.i = op;
        }
        break;
    case '-':
    case TOK_SUBC1: /* sub with carry generation */
        opc = 5;
        goto gen_op8;
    case TOK_ADDC2: /* add with carry use */
        opc = 2;
        goto gen_op8;
    case TOK_SUBC2: /* sub with carry use */
        opc = 3;
        goto gen_op8;
    case '&':
        opc = 4;
        goto gen_op8;
    case '^':
        opc = 6;
        goto gen_op8;
    case '|':
        opc = 1;
        goto gen_op8;
    case '*':
        gv2(RC_INT, RC_INT);
        r = vtop[-1].r;
        fr = vtop[0].r;
        vtop--;
        o(0xaf0f); /* imul fr, r */
        o(0xc0 + fr + r * 8);
        break;
    case TOK_SHL:
        opc = 4;
        goto gen_shift;
    case TOK_SHR:
        opc = 5;
        goto gen_shift;
    case TOK_SAR:
        opc = 7;
    gen_shift:
        opc = 0xc0 | (opc << 3);
        if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
            /* constant case */
            vswap();
            r = gv(RC_INT);
            vswap();
            c = vtop->c.i & 0x1f;
            o(0xc1); /* shl/shr/sar $xxx, r */
            o(opc | r);
            g(c);
        } else {
            /* we generate the shift in ecx */
            gv2(RC_INT, RC_ECX);
            r = vtop[-1].r;
            o(0xd3); /* shl/shr/sar %cl, r */
            o(opc | r);
        }
        vtop--;
        break;
    case '/':
    case TOK_UDIV:
    case TOK_PDIV:
    case '%':
    case TOK_UMOD:
    case TOK_UMULL:
        /* first operand must be in eax */
        /* XXX: need better constraint for second operand */
        gv2(RC_EAX, RC_ECX);
        r = vtop[-1].r;
        fr = vtop[0].r;
        vtop--;
        save_reg(TREG_EDX);
        /* save EAX too if used otherwise */
        save_reg_upstack(TREG_EAX, 1);
        if (op == TOK_UMULL) {
            o(0xf7); /* mul fr */
            o(0xe0 + fr);
            vtop->r2 = TREG_EDX;
            r = TREG_EAX;
        } else {
            if (op == TOK_UDIV || op == TOK_UMOD) {
                o(0xf7d231); /* xor %edx, %edx, div fr, %eax */
                o(0xf0 + fr);
            } else {
                o(0xf799); /* cltd, idiv fr, %eax */
                o(0xf8 + fr);
            }
            if (op == '%' || op == TOK_UMOD)
                r = TREG_EDX;
            else
                r = TREG_EAX;
        }
        vtop->r = r;
        break;
    default:
        opc = 7;
        goto gen_op8;
    }
}

/* generate a floating point operation 'v = t1 op t2' instruction. The
   two operands are guaranteed to have the same floating point type */
/* XXX: need to use ST1 too */
ST_FUNC void gen_opf(int op)
{
    int a, ft, fc, swapped, r;

    /* convert constants to memory references */
    if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
        vswap();
        gv(RC_FLOAT);
        vswap();
    }
    if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST)
        gv(RC_FLOAT);

    /* must put at least one value in the floating point register */
    if ((vtop[-1].r & VT_LVAL) &&
        (vtop[0].r & VT_LVAL)) {
        vswap();
        gv(RC_FLOAT);
        vswap();
    }
    swapped = 0;
    /* swap the stack if needed so that t1 is the register and t2 is
       the memory reference */
    if (vtop[-1].r & VT_LVAL) {
        vswap();
        swapped = 1;
    }
    if (op >= TOK_ULT && op <= TOK_GT) {
        /* load on stack second operand */
        load(TREG_ST0, vtop);
        save_reg(TREG_EAX); /* eax is used by FP comparison code */
        if (op == TOK_GE || op == TOK_GT)
            swapped = !swapped;
        else if (op == TOK_EQ || op == TOK_NE)
            swapped = 0;
        if (swapped)
            o(0xc9d9); /* fxch %st(1) */
        if (op == TOK_EQ || op == TOK_NE)
            o(0xe9da); /* fucompp */
        else
            o(0xd9de); /* fcompp */
        o(0xe0df); /* fnstsw %ax */
        if (op == TOK_EQ) {
            o(0x45e480); /* and $0x45, %ah */
            o(0x40fC80); /* cmp $0x40, %ah */
        } else if (op == TOK_NE) {
            o(0x45e480); /* and $0x45, %ah */
            o(0x40f480); /* xor $0x40, %ah */
            op = TOK_NE;
        } else if (op == TOK_GE || op == TOK_LE) {
            o(0x05c4f6); /* test $0x05, %ah */
            op = TOK_EQ;
        } else {
            o(0x45c4f6); /* test $0x45, %ah */
            op = TOK_EQ;
        }
        vtop--;
        vtop->r = VT_CMP;
        vtop->c.i = op;
    } else {
        /* no memory reference possible for long double operations */
        if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
            load(TREG_ST0, vtop);
            swapped = !swapped;
        }
        
        switch(op) {
        default:
        case '+':
            a = 0;
            break;
        case '-':
            a = 4;
            if (swapped)
                a++;
            break;
        case '*':
            a = 1;
            break;
        case '/':
            a = 6;
            if (swapped)
                a++;
            break;
        }
        ft = vtop->type.t;
        fc = vtop->c.i;
        if ((ft & VT_BTYPE) == VT_LDOUBLE) {
            o(0xde); /* fxxxp %st, %st(1) */
            o(0xc1 + (a << 3));
        } else {
            /* if saved lvalue, then we must reload it */
            r = vtop->r;
            if ((r & VT_VALMASK) == VT_LLOCAL) {
                SValue v1;
                r = get_reg(RC_INT);
                v1.type.t = VT_INT;
                v1.r = VT_LOCAL | VT_LVAL;
                v1.c.i = fc;
                load(r, &v1);
                fc = 0;
            }

            if ((ft & VT_BTYPE) == VT_DOUBLE)
                o(0xdc);
            else
                o(0xd8);
            gen_modrm(a, r, vtop->sym, fc);
        }
        vtop--;
    }
}

/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
   and 'long long' cases. */
ST_FUNC void gen_cvt_itof(int t)
{
    save_reg(TREG_ST0);
    gv(RC_INT);
    if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
        /* signed long long to float/double/long double (unsigned case
           is handled generically) */
        o(0x50 + vtop->r2); /* push r2 */
        o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
        o(0x242cdf); /* fildll (%esp) */
        o(0x08c483); /* add $8, %esp */
    } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == 
               (VT_INT | VT_UNSIGNED)) {
        /* unsigned int to float/double/long double */
        o(0x6a); /* push $0 */
        g(0x00);
        o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
        o(0x242cdf); /* fildll (%esp) */
        o(0x08c483); /* add $8, %esp */
    } else {
        /* int to float/double/long double */
        o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
        o(0x2404db); /* fildl (%esp) */
        o(0x04c483); /* add $4, %esp */
    }
    vtop->r = TREG_ST0;
}

/* convert fp to int 't' type */
ST_FUNC void gen_cvt_ftoi(int t)
{
    int bt = vtop->type.t & VT_BTYPE;
    if (bt == VT_FLOAT)
        vpush_global_sym(&func_old_type, TOK___fixsfdi);
    else if (bt == VT_LDOUBLE)
        vpush_global_sym(&func_old_type, TOK___fixxfdi);
    else
        vpush_global_sym(&func_old_type, TOK___fixdfdi);
    vswap();
    gfunc_call(1);
    vpushi(0);
    vtop->r = REG_IRET;
    vtop->r2 = REG_LRET;
}

/* convert from one floating point type to another */
ST_FUNC void gen_cvt_ftof(int t)
{
    /* all we have to do on i386 is to put the float in a register */
    gv(RC_FLOAT);
}

/* computed goto support */
ST_FUNC void ggoto(void)
{
    gcall_or_jmp(1);
    vtop--;
}

/* bound check support functions */
#ifdef CONFIG_TCC_BCHECK

/* generate a bounded pointer addition */
ST_FUNC void gen_bounded_ptr_add(void)
{
    /* prepare fast i386 function call (args in eax and edx) */
    gv2(RC_EAX, RC_EDX);
    /* save all temporary registers */
    vtop -= 2;
    save_regs(0);
    /* do a fast function call */
    gen_static_call(TOK___bound_ptr_add);
    /* returned pointer is in eax */
    vtop++;
    vtop->r = TREG_EAX | VT_BOUNDED;
    /* address of bounding function call point */
    vtop->c.i = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel));
}

/* patch pointer addition in vtop so that pointer dereferencing is
   also tested */
ST_FUNC void gen_bounded_ptr_deref(void)
{
    addr_t func;
    int  size, align;
    Elf32_Rel *rel;
    Sym *sym;

    size = 0;
    /* XXX: put that code in generic part of tcc */
    if (!is_float(vtop->type.t)) {
        if (vtop->r & VT_LVAL_BYTE)
            size = 1;
        else if (vtop->r & VT_LVAL_SHORT)
            size = 2;
    }
    if (!size)
        size = type_size(&vtop->type, &align);
    switch(size) {
    case  1: func = TOK___bound_ptr_indir1; break;
    case  2: func = TOK___bound_ptr_indir2; break;
    case  4: func = TOK___bound_ptr_indir4; break;
    case  8: func = TOK___bound_ptr_indir8; break;
    case 12: func = TOK___bound_ptr_indir12; break;
    case 16: func = TOK___bound_ptr_indir16; break;
    default:
        tcc_error("unhandled size when dereferencing bounded pointer");
        func = 0;
        break;
    }

    /* patch relocation */
    /* XXX: find a better solution ? */
    rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.i);
    sym = external_global_sym(func, &func_old_type, 0);
    if (!sym->c)
        put_extern_sym(sym, NULL, 0, 0);
    rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info));
}
#endif

/* Save the stack pointer onto the stack */
ST_FUNC void gen_vla_sp_save(int addr) {
    /* mov %esp,addr(%ebp)*/
    o(0x89);
    gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
}

/* Restore the SP from a location on the stack */
ST_FUNC void gen_vla_sp_restore(int addr) {
    o(0x8b);
    gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
}

/* Subtract from the stack pointer, and push the resulting value onto the stack */
ST_FUNC void gen_vla_alloc(CType *type, int align) {
#ifdef TCC_TARGET_PE
    /* alloca does more than just adjust %rsp on Windows */
    vpush_global_sym(&func_old_type, TOK_alloca);
    vswap(); /* Move alloca ref past allocation size */
    gfunc_call(1);
#else
    int r;
    r = gv(RC_INT); /* allocation size */
    /* sub r,%rsp */
    o(0x2b);
    o(0xe0 | r);
    /* We align to 16 bytes rather than align */
    /* and ~15, %esp */
    o(0xf0e483);
    vpop();
#endif
}

/* end of X86 code generator */
/*************************************************************/
#endif
/*************************************************************/

https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/i386-link.c

File: ../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/i386-link.c used in: Step 488
#ifdef TARGET_DEFS_ONLY

#define EM_TCC_TARGET EM_386

/* relocation type for 32 bit data relocation */
#define R_DATA_32   R_386_32
#define R_DATA_PTR  R_386_32
#define R_JMP_SLOT  R_386_JMP_SLOT
#define R_GLOB_DAT  R_386_GLOB_DAT
#define R_COPY      R_386_COPY
#define R_RELATIVE  R_386_RELATIVE

#define R_NUM       R_386_NUM

#define ELF_START_ADDR 0x08048000
#define ELF_PAGE_SIZE  0x1000

#define PCRELATIVE_DLLPLT 0
#define RELOCATE_DLLPLT 0

#else /* !TARGET_DEFS_ONLY */

#include "tcc.h"

/* Returns 1 for a code relocation, 0 for a data relocation. For unknown
   relocations, returns -1. */
int code_reloc (int reloc_type)
{
    switch (reloc_type) {
    case R_386_RELATIVE:
    case R_386_16:
        case R_386_32:
    case R_386_GOTPC:
    case R_386_GOTOFF:
    case R_386_GOT32:
    case R_386_GOT32X:
    case R_386_GLOB_DAT:
    case R_386_COPY:
            return 0;

    case R_386_PC16:
    case R_386_PC32:
    case R_386_PLT32:
    case R_386_JMP_SLOT:
            return 1;
    }

    tcc_error ("Unknown relocation type: %d", reloc_type);
    return -1;
}

/* Returns an enumerator to describe whether and when the relocation needs a
   GOT and/or PLT entry to be created. See tcc.h for a description of the
   different values. */
int gotplt_entry_type (int reloc_type)
{
    switch (reloc_type) {
    case R_386_RELATIVE:
    case R_386_16:
    case R_386_GLOB_DAT:
    case R_386_JMP_SLOT:
    case R_386_COPY:
            return NO_GOTPLT_ENTRY;

        case R_386_32:
        /* This relocations shouldn't normally need GOT or PLT
           slots if it weren't for simplicity in the code generator.
           See our caller for comments.  */
            return AUTO_GOTPLT_ENTRY;

    case R_386_PC16:
    case R_386_PC32:
            return AUTO_GOTPLT_ENTRY;

    case R_386_GOTPC:
    case R_386_GOTOFF:
            return BUILD_GOT_ONLY;

    case R_386_GOT32:
    case R_386_GOT32X:
    case R_386_PLT32:
            return ALWAYS_GOTPLT_ENTRY;
    }

    tcc_error ("Unknown relocation type: %d", reloc_type);
    return -1;
}

ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr)
{
    Section *plt = s1->plt;
    uint8_t *p;
    int modrm;
    unsigned plt_offset, relofs;

    /* on i386 if we build a DLL, we add a %ebx offset */
    if (s1->output_type == TCC_OUTPUT_DLL)
        modrm = 0xa3;
    else
        modrm = 0x25;

    /* empty PLT: create PLT0 entry that pushes the library identifier
       (GOT + PTR_SIZE) and jumps to ld.so resolution routine
       (GOT + 2 * PTR_SIZE) */
    if (plt->data_offset == 0) {
        p = section_ptr_add(plt, 16);
        p[0] = 0xff; /* pushl got + PTR_SIZE */
        p[1] = modrm + 0x10;
        write32le(p + 2, PTR_SIZE);
        p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */
        p[7] = modrm;
        write32le(p + 8, PTR_SIZE * 2);
    }
    plt_offset = plt->data_offset;

    /* The PLT slot refers to the relocation entry it needs via offset.
       The reloc entry is created below, so its offset is the current
       data_offset */
    relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0;

    /* Jump to GOT entry where ld.so initially put the address of ip + 4 */
    p = section_ptr_add(plt, 16);
    p[0] = 0xff; /* jmp *(got + x) */
    p[1] = modrm;
    write32le(p + 2, got_offset);
    p[6] = 0x68; /* push $xxx */
    write32le(p + 7, relofs);
    p[11] = 0xe9; /* jmp plt_start */
    write32le(p + 12, -(plt->data_offset));
    return plt_offset;
}

/* relocate the PLT: compute addresses and offsets in the PLT now that final
   address for PLT and GOT are known (see fill_program_header) */
ST_FUNC void relocate_plt(TCCState *s1)
{
    uint8_t *p, *p_end;

    if (!s1->plt)
      return;

    p = s1->plt->data;
    p_end = p + s1->plt->data_offset;

    if (p < p_end) {
        add32le(p + 2, s1->got->sh_addr);
        add32le(p + 8, s1->got->sh_addr);
        p += 16;
        while (p < p_end) {
            add32le(p + 2, s1->got->sh_addr);
            p += 16;
        }
    }
}

static ElfW_Rel *qrel; /* ptr to next reloc entry reused */

void relocate_init(Section *sr)
{
    qrel = (ElfW_Rel *) sr->data;
}

void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val)
{
    int sym_index, esym_index;

    sym_index = ELFW(R_SYM)(rel->r_info);

    switch (type) {
        case R_386_32:
            if (s1->output_type == TCC_OUTPUT_DLL) {
                esym_index = s1->sym_attrs[sym_index].dyn_index;
                qrel->r_offset = rel->r_offset;
                if (esym_index) {
                    qrel->r_info = ELFW(R_INFO)(esym_index, R_386_32);
                    qrel++;
                    return;
                } else {
                    qrel->r_info = ELFW(R_INFO)(0, R_386_RELATIVE);
                    qrel++;
                }
            }
            add32le(ptr, val);
            return;
        case R_386_PC32:
            if (s1->output_type == TCC_OUTPUT_DLL) {
                /* DLL relocation */
                esym_index = s1->sym_attrs[sym_index].dyn_index;
                if (esym_index) {
                    qrel->r_offset = rel->r_offset;
                    qrel->r_info = ELFW(R_INFO)(esym_index, R_386_PC32);
                    qrel++;
                    return;
                }
            }
            add32le(ptr, val - addr);
            return;
        case R_386_PLT32:
            add32le(ptr, val - addr);
            return;
        case R_386_GLOB_DAT:
        case R_386_JMP_SLOT:
            write32le(ptr, val);
            return;
        case R_386_GOTPC:
            add32le(ptr, s1->got->sh_addr - addr);
            return;
        case R_386_GOTOFF:
            add32le(ptr, val - s1->got->sh_addr);
            return;
        case R_386_GOT32:
        case R_386_GOT32X:
            /* we load the got offset */
            add32le(ptr, s1->sym_attrs[sym_index].got_offset);
            return;
        case R_386_16:
            if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) {
            output_file:
                tcc_error("can only produce 16-bit binary files");
            }
            write16le(ptr, read16le(ptr) + val);
            return;
        case R_386_PC16:
            if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY)
                goto output_file;
            write16le(ptr, read16le(ptr) + val - addr);
            return;
        case R_386_RELATIVE:
#ifdef TCC_TARGET_PE
            add32le(ptr, val - s1->pe_imagebase);
#endif
            /* do nothing */
            return;
        case R_386_COPY:
            /* This relocation must copy initialized data from the library
            to the program .bss segment. Currently made like for ARM
            (to remove noise of default case). Is this true?
            */
            return;
        default:
            fprintf(stderr,"FIXME: handle reloc type %d at %x [%p] to %x\n",
                type, (unsigned)addr, ptr, (unsigned)val);
            return;
    }
}

#endif /* !TARGET_DEFS_ONLY */

https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/i386-asm.c

File: ../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/i386-asm.c used in: Step 488
/*
 *  i386 specific functions for TCC assembler
 *
 *  Copyright (c) 2001, 2002 Fabrice Bellard
 *  Copyright (c) 2009 Frédéric Feret (x86_64 support)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "tcc.h"

#define MAX_OPERANDS 3

#define TOK_ASM_first TOK_ASM_clc
#define TOK_ASM_last TOK_ASM_emms
#define TOK_ASM_alllast TOK_ASM_subps

#define OPC_B          0x01  /* only used with OPC_WL */
#define OPC_WL         0x02  /* accepts w, l or no suffix */
#define OPC_BWL        (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */
#define OPC_REG        0x04 /* register is added to opcode */
#define OPC_MODRM      0x08 /* modrm encoding */

#define OPCT_MASK      0x70
#define OPC_FWAIT      0x10 /* add fwait opcode */
#define OPC_SHIFT      0x20 /* shift opcodes */
#define OPC_ARITH      0x30 /* arithmetic opcodes */
#define OPC_FARITH     0x40 /* FPU arithmetic opcodes */
#define OPC_TEST       0x50 /* test opcodes */
#define OPCT_IS(v,i) (((v) & OPCT_MASK) == (i))

#define OPC_0F        0x100 /* Is secondary map (0x0f prefix) */
#define OPC_48        0x200 /* Always has REX prefix */
#ifdef TCC_TARGET_X86_64
# define OPC_WLQ     0x1000  /* accepts w, l, q or no suffix */
# define OPC_BWLQ    (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */
# define OPC_WLX     OPC_WLQ
# define OPC_BWLX    OPC_BWLQ
#else
# define OPC_WLX     OPC_WL
# define OPC_BWLX    OPC_BWL
#endif

#define OPC_GROUP_SHIFT 13

/* in order to compress the operand type, we use specific operands and
   we or only with EA  */
enum {
    OPT_REG8=0, /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_REG16,  /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_REG32,  /* warning: value is hardcoded from TOK_ASM_xxx */
#ifdef TCC_TARGET_X86_64
    OPT_REG64,  /* warning: value is hardcoded from TOK_ASM_xxx */
#endif
    OPT_MMX,    /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_SSE,    /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_CR,     /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_TR,     /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_DB,     /* warning: value is hardcoded from TOK_ASM_xxx */
    OPT_SEG,
    OPT_ST,
#ifdef TCC_TARGET_X86_64
    OPT_REG8_LOW, /* %spl,%bpl,%sil,%dil, encoded like ah,ch,dh,bh, but
             with REX prefix, not used in insn templates */
#endif
    OPT_IM8,
    OPT_IM8S,
    OPT_IM16,
    OPT_IM32,
#ifdef TCC_TARGET_X86_64
    OPT_IM64,
#endif
    OPT_EAX,    /* %al, %ax, %eax or %rax register */
    OPT_ST0,    /* %st(0) register */
    OPT_CL,     /* %cl register */
    OPT_DX,     /* %dx register */
    OPT_ADDR,   /* OP_EA with only offset */
    OPT_INDIR,  /* *(expr) */
    /* composite types */
    OPT_COMPOSITE_FIRST,
    OPT_IM,     /* IM8 | IM16 | IM32 */
    OPT_REG,    /* REG8 | REG16 | REG32 | REG64 */
    OPT_REGW,   /* REG16 | REG32 | REG64 */
    OPT_IMW,    /* IM16 | IM32 */
    OPT_MMXSSE, /* MMX | SSE */
    OPT_DISP,   /* Like OPT_ADDR, but emitted as displacement (for jumps) */
    OPT_DISP8,  /* Like OPT_ADDR, but only 8bit (short jumps) */
    /* can be ored with any OPT_xxx */
    OPT_EA = 0x80
};

#define OP_REG8   (1 << OPT_REG8)
#define OP_REG16  (1 << OPT_REG16)
#define OP_REG32  (1 << OPT_REG32)
#define OP_MMX    (1 << OPT_MMX)
#define OP_SSE    (1 << OPT_SSE)
#define OP_CR     (1 << OPT_CR)
#define OP_TR     (1 << OPT_TR)
#define OP_DB     (1 << OPT_DB)
#define OP_SEG    (1 << OPT_SEG)
#define OP_ST     (1 << OPT_ST)
#define OP_IM8    (1 << OPT_IM8)
#define OP_IM8S   (1 << OPT_IM8S)
#define OP_IM16   (1 << OPT_IM16)
#define OP_IM32   (1 << OPT_IM32)
#define OP_EAX    (1 << OPT_EAX)
#define OP_ST0    (1 << OPT_ST0)
#define OP_CL     (1 << OPT_CL)
#define OP_DX     (1 << OPT_DX)
#define OP_ADDR   (1 << OPT_ADDR)
#define OP_INDIR  (1 << OPT_INDIR)
#ifdef TCC_TARGET_X86_64
# define OP_REG64 (1 << OPT_REG64)
# define OP_REG8_LOW (1 << OPT_REG8_LOW)
# define OP_IM64  (1 << OPT_IM64)
# define OP_EA32  (OP_EA << 1)
#else
# define OP_REG64 0
# define OP_REG8_LOW 0
# define OP_IM64  0
# define OP_EA32  0
#endif

#define OP_EA     0x40000000
#define OP_REG    (OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64)

#ifdef TCC_TARGET_X86_64
# define TREG_XAX   TREG_RAX
# define TREG_XCX   TREG_RCX
# define TREG_XDX   TREG_RDX
#else
# define TREG_XAX   TREG_EAX
# define TREG_XCX   TREG_ECX
# define TREG_XDX   TREG_EDX
#endif

typedef struct ASMInstr {
    uint16_t sym;
    uint16_t opcode;
    uint16_t instr_type;
    uint8_t nb_ops;
    uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */
} ASMInstr;

typedef struct Operand {
    uint32_t type;
    int8_t  reg; /* register, -1 if none */
    int8_t  reg2; /* second register, -1 if none */
    uint8_t shift;
    ExprValue e;
} Operand;

static const uint8_t reg_to_size[9] = {
/*
    [OP_REG8] = 0,
    [OP_REG16] = 1,
    [OP_REG32] = 2,
#ifdef TCC_TARGET_X86_64
    [OP_REG64] = 3,
#endif
*/
    0, 0, 1, 0, 2, 0, 0, 0, 3
};

#define NB_TEST_OPCODES 30

static const uint8_t test_bits[NB_TEST_OPCODES] = {
 0x00, /* o */
 0x01, /* no */
 0x02, /* b */
 0x02, /* c */
 0x02, /* nae */
 0x03, /* nb */
 0x03, /* nc */
 0x03, /* ae */
 0x04, /* e */
 0x04, /* z */
 0x05, /* ne */
 0x05, /* nz */
 0x06, /* be */
 0x06, /* na */
 0x07, /* nbe */
 0x07, /* a */
 0x08, /* s */
 0x09, /* ns */
 0x0a, /* p */
 0x0a, /* pe */
 0x0b, /* np */
 0x0b, /* po */
 0x0c, /* l */
 0x0c, /* nge */
 0x0d, /* nl */
 0x0d, /* ge */
 0x0e, /* le */
 0x0e, /* ng */
 0x0f, /* nle */
 0x0f, /* g */
};

static const uint8_t segment_prefixes[] = {
 0x26, /* es */
 0x2e, /* cs */
 0x36, /* ss */
 0x3e, /* ds */
 0x64, /* fs */
 0x65  /* gs */
};

static const ASMInstr asm_instrs[] = {
#define ALT(x) x
/* This removes a 0x0f in the second byte */
#define O(o) ((uint64_t) ((((o) & 0xff00) == 0x0f00) ? ((((o) >> 8) & ~0xff) | ((o) & 0xff)) : (o)))
/* This constructs instr_type from opcode, type and group.  */
#define T(o,i,g) ((i) | ((g) << OPC_GROUP_SHIFT) | ((((o) & 0xff00) == 0x0f00) ? OPC_0F : 0))
#define DEF_ASM_OP0(name, opcode)
#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 0, { 0 } },
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 1, { op0 }},
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 2, { op0, op1 }},
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, O(opcode), T(opcode, instr_type, group), 3, { op0, op1, op2 }},
#ifdef TCC_TARGET_X86_64
# include "x86_64-asm.h"
#else
# include "i386-asm.h"
#endif
    /* last operation */
    { 0, },
};

static const uint16_t op0_codes[] = {
#define ALT(x)
#define DEF_ASM_OP0(x, opcode) opcode,
#define DEF_ASM_OP0L(name, opcode, group, instr_type)
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
#ifdef TCC_TARGET_X86_64
# include "x86_64-asm.h"
#else
# include "i386-asm.h"
#endif
};

static inline int get_reg_shift(TCCState *s1)
{
    int shift, v;
    v = asm_int_expr(s1);
    switch(v) {
    case 1:
        shift = 0;
        break;
    case 2:
        shift = 1;
        break;
    case 4:
        shift = 2;
        break;
    case 8:
        shift = 3;
        break;
    default:
        expect("1, 2, 4 or 8 constant");
        shift = 0;
        break;
    }
    return shift;
}

#ifdef TCC_TARGET_X86_64
static int asm_parse_numeric_reg(int t, unsigned int *type)
{
    int reg = -1;
    if (t >= TOK_IDENT && t < tok_ident) {
    const char *s = table_ident[t - TOK_IDENT]->str;
    char c;
    *type = OP_REG64;
    if (*s == 'c') {
        s++;
        *type = OP_CR;
    }
    if (*s++ != 'r')
      return -1;
    /* Don't allow leading '0'.  */
    if ((c = *s++) >= '1' && c <= '9')
      reg = c - '0';
    else
      return -1;
    if ((c = *s) >= '0' && c <= '5')
      s++, reg = reg * 10 + c - '0';
    if (reg > 15)
      return -1;
    if ((c = *s) == 0)
      ;
    else if (*type != OP_REG64)
      return -1;
    else if (c == 'b' && !s[1])
      *type = OP_REG8;
    else if (c == 'w' && !s[1])
      *type = OP_REG16;
    else if (c == 'd' && !s[1])
      *type = OP_REG32;
    else
      return -1;
    }
    return reg;
}
#endif

static int asm_parse_reg(unsigned int *type)
{
    int reg = 0;
    *type = 0;
    if (tok != '%')
        goto error_32;
    next();
    if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) {
        reg = tok - TOK_ASM_eax;
    *type = OP_REG32;
#ifdef TCC_TARGET_X86_64
    } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) {
        reg = tok - TOK_ASM_rax;
    *type = OP_REG64;
    } else if (tok == TOK_ASM_rip) {
        reg = -2; /* Probably should use different escape code. */
    *type = OP_REG64;
    } else if ((reg = asm_parse_numeric_reg(tok, type)) >= 0
           && (*type == OP_REG32 || *type == OP_REG64)) {
    ;
#endif
    } else {
    error_32:
        expect("register");
    }
    next();
    return reg;
}

static void parse_operand(TCCState *s1, Operand *op)
{
    ExprValue e;
    int reg, indir;
    const char *p;

    indir = 0;
    if (tok == '*') {
        next();
        indir = OP_INDIR;
    }

    if (tok == '%') {
        next();
        if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) {
            reg = tok - TOK_ASM_al;
            op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */
            op->reg = reg & 7;
            if ((op->type & OP_REG) && op->reg == TREG_XAX)
                op->type |= OP_EAX;
            else if (op->type == OP_REG8 && op->reg == TREG_XCX)
                op->type |= OP_CL;
            else if (op->type == OP_REG16 && op->reg == TREG_XDX)
                op->type |= OP_DX;
        } else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) {
            op->type = OP_DB;
            op->reg = tok - TOK_ASM_dr0;
        } else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) {
            op->type = OP_SEG;
            op->reg = tok - TOK_ASM_es;
        } else if (tok == TOK_ASM_st) {
            op->type = OP_ST;
            op->reg = 0;
            next();
            if (tok == '(') {
                next();
                if (tok != TOK_PPNUM)
                    goto reg_error;
                p = tokc.str.data;
                reg = p[0] - '0';
                if ((unsigned)reg >= 8 || p[1] != '\0')
                    goto reg_error;
                op->reg = reg;
                next();
                skip(')');
            }
            if (op->reg == 0)
                op->type |= OP_ST0;
            goto no_skip;
#ifdef TCC_TARGET_X86_64
    } else if (tok >= TOK_ASM_spl && tok <= TOK_ASM_dil) {
        op->type = OP_REG8 | OP_REG8_LOW;
        op->reg = 4 + tok - TOK_ASM_spl;
        } else if ((op->reg = asm_parse_numeric_reg(tok, &op->type)) >= 0) {
        ;
#endif
        } else {
        reg_error:
            tcc_error("unknown register %%%s", get_tok_str(tok, &tokc));
        }
        next();
    no_skip: ;
    } else if (tok == '$') {
        /* constant value */
        next();
        asm_expr(s1, &e);
        op->type = OP_IM32;
        op->e = e;
        if (!op->e.sym) {
            if (op->e.v == (uint8_t)op->e.v)
                op->type |= OP_IM8;
            if (op->e.v == (int8_t)op->e.v)
                op->type |= OP_IM8S;
            if (op->e.v == (uint16_t)op->e.v)
                op->type |= OP_IM16;
#ifdef TCC_TARGET_X86_64
            if (op->e.v != (int32_t)op->e.v && op->e.v != (uint32_t)op->e.v)
                op->type = OP_IM64;
#endif
        }
    } else {
        /* address(reg,reg2,shift) with all variants */
        op->type = OP_EA;
        op->reg = -1;
        op->reg2 = -1;
        op->shift = 0;
        if (tok != '(') {
            asm_expr(s1, &e);
            op->e = e;
        } else {
            next();
            if (tok == '%') {
                unget_tok('(');
                op->e.v = 0;
                op->e.sym = NULL;
            } else {
                /* bracketed offset expression */
                asm_expr(s1, &e);
                if (tok != ')')
                    expect(")");
                next();
                op->e.v = e.v;
                op->e.sym = e.sym;
            }
        op->e.pcrel = 0;
        }
        if (tok == '(') {
        unsigned int type = 0;
            next();
            if (tok != ',') {
                op->reg = asm_parse_reg(&type);
            }
            if (tok == ',') {
                next();
                if (tok != ',') {
                    op->reg2 = asm_parse_reg(&type);
                }
                if (tok == ',') {
                    next();
                    op->shift = get_reg_shift(s1);
                }
            }
        if (type & OP_REG32)
            op->type |= OP_EA32;
            skip(')');
        }
        if (op->reg == -1 && op->reg2 == -1)
            op->type |= OP_ADDR;
    }
    op->type |= indir;
}

/* XXX: unify with C code output ? */
ST_FUNC void gen_expr32(ExprValue *pe)
{
    if (pe->pcrel)
        /* If PC-relative, always set VT_SYM, even without symbol,
       so as to force a relocation to be emitted.  */
    gen_addrpc32(VT_SYM, pe->sym, pe->v);
    else
    gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
}

#ifdef TCC_TARGET_X86_64
ST_FUNC void gen_expr64(ExprValue *pe)
{
    gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
}
#endif

/* XXX: unify with C code output ? */
static void gen_disp32(ExprValue *pe)
{
    Sym *sym = pe->sym;
    ElfSym *esym = elfsym(sym);
    if (esym && esym->st_shndx == cur_text_section->sh_num) {
        /* same section: we can output an absolute value. Note
           that the TCC compiler behaves differently here because
           it always outputs a relocation to ease (future) code
           elimination in the linker */
        gen_le32(pe->v + esym->st_value - ind - 4);
    } else {
        if (sym && sym->type.t == VT_VOID) {
            sym->type.t = VT_FUNC;
            sym->type.ref = NULL;
        }
        gen_addrpc32(VT_SYM, sym, pe->v);
    }
}

/* generate the modrm operand */
static inline int asm_modrm(int reg, Operand *op)
{
    int mod, reg1, reg2, sib_reg1;

    if (op->type & (OP_REG | OP_MMX | OP_SSE)) {
        g(0xc0 + (reg << 3) + op->reg);
    } else if (op->reg == -1 && op->reg2 == -1) {
        /* displacement only */
#ifdef TCC_TARGET_X86_64
    g(0x04 + (reg << 3));
    g(0x25);
#else
    g(0x05 + (reg << 3));
#endif
    gen_expr32(&op->e);
#ifdef TCC_TARGET_X86_64
    } else if (op->reg == -2) {
        ExprValue *pe = &op->e;
        g(0x05 + (reg << 3));
        gen_addrpc32(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
        return ind;
#endif
    } else {
        sib_reg1 = op->reg;
        /* fist compute displacement encoding */
        if (sib_reg1 == -1) {
            sib_reg1 = 5;
            mod = 0x00;
        } else if (op->e.v == 0 && !op->e.sym && op->reg != 5) {
            mod = 0x00;
        } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) {
            mod = 0x40;
        } else {
            mod = 0x80;
        }
        /* compute if sib byte needed */
        reg1 = op->reg;
        if (op->reg2 != -1)
            reg1 = 4;
        g(mod + (reg << 3) + reg1);
        if (reg1 == 4) {
            /* add sib byte */
            reg2 = op->reg2;
            if (reg2 == -1)
                reg2 = 4; /* indicate no index */
            g((op->shift << 6) + (reg2 << 3) + sib_reg1);
        }
        /* add offset */
        if (mod == 0x40) {
            g(op->e.v);
        } else if (mod == 0x80 || op->reg == -1) {
        gen_expr32(&op->e);
        }
    }
    return 0;
}

#ifdef TCC_TARGET_X86_64
#define REX_W 0x48
#define REX_R 0x44
#define REX_X 0x42
#define REX_B 0x41

static void asm_rex(int width64, Operand *ops, int nb_ops, int *op_type,
            int regi, int rmi)
{
  unsigned char rex = width64 ? 0x48 : 0;
  int saw_high_8bit = 0;
  int i;
  if (rmi == -1) {
      /* No mod/rm byte, but we might have a register op nevertheless
         (we will add it to the opcode later).  */
      for(i = 0; i < nb_ops; i++) {
      if (op_type[i] & (OP_REG | OP_ST)) {
          if (ops[i].reg >= 8) {
          rex |= REX_B;
          ops[i].reg -= 8;
          } else if (ops[i].type & OP_REG8_LOW)
          rex |= 0x40;
          else if (ops[i].type & OP_REG8 && ops[i].reg >= 4)
          /* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */
          saw_high_8bit = ops[i].reg;
          break;
      }
      }
  } else {
      if (regi != -1) {
      if (ops[regi].reg >= 8) {
          rex |= REX_R;
          ops[regi].reg -= 8;
      } else if (ops[regi].type & OP_REG8_LOW)
          rex |= 0x40;
      else if (ops[regi].type & OP_REG8 && ops[regi].reg >= 4)
          /* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */
          saw_high_8bit = ops[regi].reg;
      }
      if (ops[rmi].type & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_EA)) {
      if (ops[rmi].reg >= 8) {
          rex |= REX_B;
          ops[rmi].reg -= 8;
      } else if (ops[rmi].type & OP_REG8_LOW)
          rex |= 0x40;
      else if (ops[rmi].type & OP_REG8 && ops[rmi].reg >= 4)
          /* An 8 bit reg >= 4 without REG8 is ah/ch/dh/bh */
          saw_high_8bit = ops[rmi].reg;
      }
      if (ops[rmi].type & OP_EA && ops[rmi].reg2 >= 8) {
      rex |= REX_X;
      ops[rmi].reg2 -= 8;
      }
  }
  if (rex) {
      if (saw_high_8bit)
      tcc_error("can't encode register %%%ch when REX prefix is required",
            "acdb"[saw_high_8bit-4]);
      g(rex);
  }
}
#endif

static void maybe_print_stats (void)
{
  static int already = 1;
  if (!already)
    /* print stats about opcodes */
    {
        const struct ASMInstr *pa;
        int freq[4];
        int op_vals[500];
        int nb_op_vals, i, j;

    already = 1;
        nb_op_vals = 0;
        memset(freq, 0, sizeof(freq));
        for(pa = asm_instrs; pa->sym != 0; pa++) {
            freq[pa->nb_ops]++;
            //for(i=0;i<pa->nb_ops;i++) {
                for(j=0;j<nb_op_vals;j++) {
                    //if (pa->op_type[i] == op_vals[j])
                    if (pa->instr_type == op_vals[j])
                        goto found;
                }
                //op_vals[nb_op_vals++] = pa->op_type[i];
                op_vals[nb_op_vals++] = pa->instr_type;
            found: ;
            //}
        }
        for(i=0;i<nb_op_vals;i++) {
            int v = op_vals[i];
            //if ((v & (v - 1)) != 0)
                printf("%3d: %08x\n", i, v);
        }
        printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
               (int)sizeof(asm_instrs),
           (int)sizeof(asm_instrs) / (int)sizeof(ASMInstr),
               freq[0], freq[1], freq[2], freq[3]);
    }
}

ST_FUNC void asm_opcode(TCCState *s1, int opcode)
{
    const ASMInstr *pa;
    int i, modrm_index, modreg_index, reg, v, op1, seg_prefix, pc;
    int nb_ops, s;
    Operand ops[MAX_OPERANDS], *pop;
    int op_type[3]; /* decoded op type */
    int alltypes;   /* OR of all operand types */
    int autosize;
    int p66;
#ifdef TCC_TARGET_X86_64
    int rex64;
#endif

    maybe_print_stats();
    /* force synthetic ';' after prefix instruction, so we can handle */
    /* one-line things like "rep stosb" instead of only "rep\nstosb" */
    if (opcode >= TOK_ASM_wait && opcode <= TOK_ASM_repnz)
        unget_tok(';');

    /* get operands */
    pop = ops;
    nb_ops = 0;
    seg_prefix = 0;
    alltypes = 0;
    for(;;) {
        if (tok == ';' || tok == TOK_LINEFEED)
            break;
        if (nb_ops >= MAX_OPERANDS) {
            tcc_error("incorrect number of operands");
        }
        parse_operand(s1, pop);
        if (tok == ':') {
           if (pop->type != OP_SEG || seg_prefix)
               tcc_error("incorrect prefix");
           seg_prefix = segment_prefixes[pop->reg];
           next();
           parse_operand(s1, pop);
           if (!(pop->type & OP_EA)) {
               tcc_error("segment prefix must be followed by memory reference");
           }
        }
        pop++;
        nb_ops++;
        if (tok != ',')
            break;
        next();
    }

    s = 0; /* avoid warning */

again:
    /* optimize matching by using a lookup table (no hashing is needed
       !) */
    for(pa = asm_instrs; pa->sym != 0; pa++) {
    int it = pa->instr_type & OPCT_MASK;
        s = 0;
        if (it == OPC_FARITH) {
            v = opcode - pa->sym;
            if (!((unsigned)v < 8 * 6 && (v % 6) == 0))
                continue;
        } else if (it == OPC_ARITH) {
            if (!(opcode >= pa->sym && opcode < pa->sym + 8*NBWLX))
                continue;
            s = (opcode - pa->sym) % NBWLX;
        if ((pa->instr_type & OPC_BWLX) == OPC_WLX)
          {
        /* We need to reject the xxxb opcodes that we accepted above.
           Note that pa->sym for WLX opcodes is the 'w' token,
           to get the 'b' token subtract one.  */
        if (((opcode - pa->sym + 1) % NBWLX) == 0)
            continue;
            s++;
          }
        } else if (it == OPC_SHIFT) {
            if (!(opcode >= pa->sym && opcode < pa->sym + 7*NBWLX))
                continue;
            s = (opcode - pa->sym) % NBWLX;
        } else if (it == OPC_TEST) {
            if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES))
                continue;
        /* cmovxx is a test opcode but accepts multiple sizes.
           The suffixes aren't encoded in the table, instead we
           simply force size autodetection always and deal with suffixed
           variants below when we don't find e.g. "cmovzl".  */
        if (pa->instr_type & OPC_WLX)
            s = NBWLX - 1;
        } else if (pa->instr_type & OPC_B) {
#ifdef TCC_TARGET_X86_64
        /* Some instructions don't have the full size but only
           bwl form.  insb e.g. */
        if ((pa->instr_type & OPC_WLQ) != OPC_WLQ
        && !(opcode >= pa->sym && opcode < pa->sym + NBWLX-1))
            continue;
#endif
            if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX))
                continue;
            s = opcode - pa->sym;
        } else if (pa->instr_type & OPC_WLX) {
            if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX-1))
                continue;
            s = opcode - pa->sym + 1;
        } else {
            if (pa->sym != opcode)
                continue;
        }
        if (pa->nb_ops != nb_ops)
            continue;
#ifdef TCC_TARGET_X86_64
    /* Special case for moves.  Selecting the IM64->REG64 form
       should only be done if we really have an >32bit imm64, and that
       is hardcoded.  Ignore it here.  */
    if (pa->opcode == 0xb0 && ops[0].type != OP_IM64
        && (ops[1].type & OP_REG) == OP_REG64
        && !(pa->instr_type & OPC_0F))
        continue;
#endif
        /* now decode and check each operand */
    alltypes = 0;
        for(i = 0; i < nb_ops; i++) {
            int op1, op2;
            op1 = pa->op_type[i];
            op2 = op1 & 0x1f;
            switch(op2) {
            case OPT_IM:
                v = OP_IM8 | OP_IM16 | OP_IM32;
                break;
            case OPT_REG:
                v = OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64;
                break;
            case OPT_REGW:
                v = OP_REG16 | OP_REG32 | OP_REG64;
                break;
            case OPT_IMW:
                v = OP_IM16 | OP_IM32;
                break;
        case OPT_MMXSSE:
        v = OP_MMX | OP_SSE;
        break;
        case OPT_DISP:
        case OPT_DISP8:
        v = OP_ADDR;
        break;
            default:
                v = 1 << op2;
                break;
            }
            if (op1 & OPT_EA)
                v |= OP_EA;
        op_type[i] = v;
            if ((ops[i].type & v) == 0)
                goto next;
        alltypes |= ops[i].type;
        }
        /* all is matching ! */
        break;
    next: ;
    }
    if (pa->sym == 0) {
        if (opcode >= TOK_ASM_first && opcode <= TOK_ASM_last) {
            int b;
            b = op0_codes[opcode - TOK_ASM_first];
            if (b & 0xff00) 
                g(b >> 8);
            g(b);
            return;
        } else if (opcode <= TOK_ASM_alllast) {
            tcc_error("bad operand with opcode '%s'",
                  get_tok_str(opcode, NULL));
        } else {
        /* Special case for cmovcc, we accept size suffixes but ignore
           them, but we don't want them to blow up our tables.  */
        TokenSym *ts = table_ident[opcode - TOK_IDENT];
        if (ts->len >= 6
        && strchr("wlq", ts->str[ts->len-1])
        && !memcmp(ts->str, "cmov", 4)) {
        opcode = tok_alloc(ts->str, ts->len-1)->tok;
        goto again;
        }
            tcc_error("unknown opcode '%s'", ts->str);
        }
    }
    /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */
    autosize = NBWLX-1;
#ifdef TCC_TARGET_X86_64
    /* XXX the autosize should rather be zero, to not have to adjust this
       all the time.  */
    if ((pa->instr_type & OPC_BWLQ) == OPC_B)
        autosize = NBWLX-2;
#endif
    if (s == autosize) {
    /* Check for register operands providing hints about the size.
       Start from the end, i.e. destination operands.  This matters
       only for opcodes accepting different sized registers, lar and lsl
       are such opcodes.  */
        for(i = nb_ops - 1; s == autosize && i >= 0; i--) {
            if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX)))
                s = reg_to_size[ops[i].type & OP_REG];
        }
        if (s == autosize) {
            if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) &&
                (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32)))
                s = 2;
        else if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) &&
             (ops[0].type & OP_EA))
            s = NBWLX - 2;
            else
                tcc_error("cannot infer opcode suffix");
        }
    }

#ifdef TCC_TARGET_X86_64
    /* Generate addr32 prefix if needed */
    for(i = 0; i < nb_ops; i++) {
        if (ops[i].type & OP_EA32) {
        g(0x67);
        break;
        }
    }
#endif
    /* generate data16 prefix if needed */
    p66 = 0;
    if (s == 1)
        p66 = 1;
    else {
    /* accepting mmx+sse in all operands --> needs 0x66 to
       switch to sse mode.  Accepting only sse in an operand --> is
       already SSE insn and needs 0x66/f2/f3 handling.  */
        for (i = 0; i < nb_ops; i++)
            if ((op_type[i] & (OP_MMX | OP_SSE)) == (OP_MMX | OP_SSE)
            && ops[i].type & OP_SSE)
            p66 = 1;
    }
    if (p66)
        g(0x66);
#ifdef TCC_TARGET_X86_64
    rex64 = 0;
    if (pa->instr_type & OPC_48)
        rex64 = 1;
    else if (s == 3 || (alltypes & OP_REG64)) {
        /* generate REX prefix */
    int default64 = 0;
    for(i = 0; i < nb_ops; i++) {
        if (op_type[i] == OP_REG64 && pa->opcode != 0xb8) {
        /* If only 64bit regs are accepted in one operand
           this is a default64 instruction without need for
           REX prefixes, except for movabs(0xb8).  */
        default64 = 1;
        break;
        }
    }
    /* XXX find better encoding for the default64 instructions.  */
        if (((opcode != TOK_ASM_push && opcode != TOK_ASM_pop
          && opcode != TOK_ASM_pushw && opcode != TOK_ASM_pushl
          && opcode != TOK_ASM_pushq && opcode != TOK_ASM_popw
          && opcode != TOK_ASM_popl && opcode != TOK_ASM_popq
          && opcode != TOK_ASM_call && opcode != TOK_ASM_jmp))
        && !default64)
            rex64 = 1;
    }
#endif

    /* now generates the operation */
    if (OPCT_IS(pa->instr_type, OPC_FWAIT))
        g(0x9b);
    if (seg_prefix)
        g(seg_prefix);

    v = pa->opcode;
    if (pa->instr_type & OPC_0F)
        v = ((v & ~0xff) << 8) | 0x0f00 | (v & 0xff);
    if ((v == 0x69 || v == 0x6b) && nb_ops == 2) {
        /* kludge for imul $im, %reg */
        nb_ops = 3;
        ops[2] = ops[1];
        op_type[2] = op_type[1];
    } else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) {
        v--; /* int $3 case */
        nb_ops = 0;
    } else if ((v == 0x06 || v == 0x07)) {
        if (ops[0].reg >= 4) {
            /* push/pop %fs or %gs */
            v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3);
        } else {
            v += ops[0].reg << 3;
        }
        nb_ops = 0;
    } else if (v <= 0x05) {
        /* arith case */
        v += ((opcode - TOK_ASM_addb) / NBWLX) << 3;
    } else if ((pa->instr_type & (OPCT_MASK | OPC_MODRM)) == OPC_FARITH) {
        /* fpu arith case */
        v += ((opcode - pa->sym) / 6) << 3;
    }

    /* search which operand will be used for modrm */
    modrm_index = -1;
    modreg_index = -1;
    if (pa->instr_type & OPC_MODRM) {
    if (!nb_ops) {
        /* A modrm opcode without operands is a special case (e.g. mfence).
           It has a group and acts as if there's an register operand 0
           (ax).  */
        i = 0;
        ops[i].type = OP_REG;
        ops[i].reg = 0;
        goto modrm_found;
    }
        /* first look for an ea operand */
        for(i = 0;i < nb_ops; i++) {
            if (op_type[i] & OP_EA)
                goto modrm_found;
        }
        /* then if not found, a register or indirection (shift instructions) */
        for(i = 0;i < nb_ops; i++) {
            if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR))
                goto modrm_found;
        }
#ifdef ASM_DEBUG
        tcc_error("bad op table");
#endif
    modrm_found:
        modrm_index = i;
        /* if a register is used in another operand then it is
           used instead of group */
        for(i = 0;i < nb_ops; i++) {
            int t = op_type[i];
            if (i != modrm_index &&
                (t & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) {
                modreg_index = i;
                break;
            }
        }
    }
#ifdef TCC_TARGET_X86_64
    asm_rex (rex64, ops, nb_ops, op_type, modreg_index, modrm_index);
#endif

    if (pa->instr_type & OPC_REG) {
        /* mov $im, %reg case */
        if (v == 0xb0 && s >= 1)
            v += 7;
        for(i = 0; i < nb_ops; i++) {
            if (op_type[i] & (OP_REG | OP_ST)) {
                v += ops[i].reg;
                break;
            }
        }
    }
    if (pa->instr_type & OPC_B)
        v += s >= 1;
    if (nb_ops == 1 && pa->op_type[0] == OPT_DISP8) {
    ElfSym *esym;
        int jmp_disp;

        /* see if we can really generate the jump with a byte offset */
    esym = elfsym(ops[0].e.sym);
        if (!esym || esym->st_shndx != cur_text_section->sh_num)
            goto no_short_jump;
        jmp_disp = ops[0].e.v + esym->st_value - ind - 2 - (v >= 0xff);
        if (jmp_disp == (int8_t)jmp_disp) {
            /* OK to generate jump */
        ops[0].e.sym = 0;
            ops[0].e.v = jmp_disp;
        op_type[0] = OP_IM8S;
        } else {
        no_short_jump:
        /* long jump will be allowed. need to modify the
           opcode slightly */
        if (v == 0xeb) /* jmp */
            v = 0xe9;
        else if (v == 0x70) /* jcc */
            v += 0x0f10;
        else
            tcc_error("invalid displacement");
        }
    }
    if (OPCT_IS(pa->instr_type, OPC_TEST))
        v += test_bits[opcode - pa->sym];
    op1 = v >> 16;
    if (op1)
        g(op1);
    op1 = (v >> 8) & 0xff;
    if (op1)
        g(op1);
    g(v);

    if (OPCT_IS(pa->instr_type, OPC_SHIFT)) {
        reg = (opcode - pa->sym) / NBWLX;
        if (reg == 6)
            reg = 7;
    } else if (OPCT_IS(pa->instr_type, OPC_ARITH)) {
        reg = (opcode - pa->sym) / NBWLX;
    } else if (OPCT_IS(pa->instr_type, OPC_FARITH)) {
        reg = (opcode - pa->sym) / 6;
    } else {
        reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7;
    }

    pc = 0;
    if (pa->instr_type & OPC_MODRM) {
        /* if a register is used in another operand then it is
           used instead of group */
    if (modreg_index >= 0)
        reg = ops[modreg_index].reg;
        pc = asm_modrm(reg, &ops[modrm_index]);
    }

    /* emit constants */
#ifndef TCC_TARGET_X86_64
    if (!(pa->instr_type & OPC_0F)
    && (pa->opcode == 0x9a || pa->opcode == 0xea)) {
        /* ljmp or lcall kludge */
    gen_expr32(&ops[1].e);
        if (ops[0].e.sym)
            tcc_error("cannot relocate");
        gen_le16(ops[0].e.v);
        return;
    }
#endif
    for(i = 0;i < nb_ops; i++) {
        v = op_type[i];
        if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64 | OP_IM8S | OP_ADDR)) {
            /* if multiple sizes are given it means we must look
               at the op size */
            if ((v | OP_IM8 | OP_IM64) == (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64)) {
                if (s == 0)
                    v = OP_IM8;
                else if (s == 1)
                    v = OP_IM16;
                else if (s == 2 || (v & OP_IM64) == 0)
                    v = OP_IM32;
                else
                    v = OP_IM64;
            }

            if ((v & (OP_IM8 | OP_IM8S | OP_IM16)) && ops[i].e.sym)
                tcc_error("cannot relocate");

            if (v & (OP_IM8 | OP_IM8S)) {
                g(ops[i].e.v);
            } else if (v & OP_IM16) {
                gen_le16(ops[i].e.v);
#ifdef TCC_TARGET_X86_64
            } else if (v & OP_IM64) {
                gen_expr64(&ops[i].e);
#endif
        } else if (pa->op_type[i] == OPT_DISP || pa->op_type[i] == OPT_DISP8) {
                gen_disp32(&ops[i].e);
            } else {
                gen_expr32(&ops[i].e);
            }
        }
    }

    /* after immediate operands, adjust pc-relative address */
    if (pc)
        add32le(cur_text_section->data + pc - 4, pc - ind);
}

/* return the constraint priority (we allocate first the lowest
   numbered constraints) */
static inline int constraint_priority(const char *str)
{
    int priority, c, pr;

    /* we take the lowest priority */
    priority = 0;
    for(;;) {
        c = *str;
        if (c == '\0')
            break;
        str++;
        switch(c) {
        case 'A':
            pr = 0;
            break;
        case 'a':
        case 'b':
        case 'c':
        case 'd':
        case 'S':
        case 'D':
            pr = 1;
            break;
        case 'q':
            pr = 2;
            break;
        case 'r':
    case 'R':
    case 'p':
            pr = 3;
            break;
        case 'N':
        case 'M':
        case 'I':
    case 'e':
        case 'i':
        case 'm':
        case 'g':
            pr = 4;
            break;
        default:
            tcc_error("unknown constraint '%c'", c);
            pr = 0;
        }
        if (pr > priority)
            priority = pr;
    }
    return priority;
}

static const char *skip_constraint_modifiers(const char *p)
{
    while (*p == '=' || *p == '&' || *p == '+' || *p == '%')
        p++;
    return p;
}

/* If T (a token) is of the form "%reg" returns the register
   number and type, otherwise return -1.  */
ST_FUNC int asm_parse_regvar (int t)
{
    const char *s;
    Operand op;
    if (t < TOK_IDENT)
        return -1;
    s = table_ident[t - TOK_IDENT]->str;
    if (s[0] != '%')
        return -1;
    t = tok_alloc(s+1, strlen(s)-1)->tok;
    unget_tok(t);
    unget_tok('%');
    parse_operand(tcc_state, &op);
    /* Accept only integer regs for now.  */
    if (op.type & OP_REG)
        return op.reg;
    else
        return -1;
}

#define REG_OUT_MASK 0x01
#define REG_IN_MASK  0x02

#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)

ST_FUNC void asm_compute_constraints(ASMOperand *operands,
                                    int nb_operands, int nb_outputs,
                                    const uint8_t *clobber_regs,
                                    int *pout_reg)
{
    ASMOperand *op;
    int sorted_op[MAX_ASM_OPERANDS];
    int i, j, k, p1, p2, tmp, reg, c, reg_mask;
    const char *str;
    uint8_t regs_allocated[NB_ASM_REGS];

    /* init fields */
    for(i=0;i<nb_operands;i++) {
        op = &operands[i];
        op->input_index = -1;
        op->ref_index = -1;
        op->reg = -1;
        op->is_memory = 0;
        op->is_rw = 0;
    }
    /* compute constraint priority and evaluate references to output
       constraints if input constraints */
    for(i=0;i<nb_operands;i++) {
        op = &operands[i];
        str = op->constraint;
        str = skip_constraint_modifiers(str);
        if (isnum(*str) || *str == '[') {
            /* this is a reference to another constraint */
            k = find_constraint(operands, nb_operands, str, NULL);
            if ((unsigned)k >= i || i < nb_outputs)
                tcc_error("invalid reference in constraint %d ('%s')",
                      i, str);
            op->ref_index = k;
            if (operands[k].input_index >= 0)
                tcc_error("cannot reference twice the same operand");
            operands[k].input_index = i;
            op->priority = 5;
    } else if ((op->vt->r & VT_VALMASK) == VT_LOCAL
           && op->vt->sym
           && (reg = op->vt->sym->r & VT_VALMASK) < VT_CONST) {
        op->priority = 1;
        op->reg = reg;
        } else {
            op->priority = constraint_priority(str);
        }
    }

    /* sort operands according to their priority */
    for(i=0;i<nb_operands;i++)
        sorted_op[i] = i;
    for(i=0;i<nb_operands - 1;i++) {
        for(j=i+1;j<nb_operands;j++) {
            p1 = operands[sorted_op[i]].priority;
            p2 = operands[sorted_op[j]].priority;
            if (p2 < p1) {
                tmp = sorted_op[i];
                sorted_op[i] = sorted_op[j];
                sorted_op[j] = tmp;
            }
        }
    }

    for(i = 0;i < NB_ASM_REGS; i++) {
        if (clobber_regs[i])
            regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK;
        else
            regs_allocated[i] = 0;
    }
    /* esp cannot be used */
    regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK;
    /* ebp cannot be used yet */
    regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK;

    /* allocate registers and generate corresponding asm moves */
    for(i=0;i<nb_operands;i++) {
        j = sorted_op[i];
        op = &operands[j];
        str = op->constraint;
        /* no need to allocate references */
        if (op->ref_index >= 0)
            continue;
        /* select if register is used for output, input or both */
        if (op->input_index >= 0) {
            reg_mask = REG_IN_MASK | REG_OUT_MASK;
        } else if (j < nb_outputs) {
            reg_mask = REG_OUT_MASK;
        } else {
            reg_mask = REG_IN_MASK;
        }
    if (op->reg >= 0) {
        if (is_reg_allocated(op->reg))
            tcc_error("asm regvar requests register that's taken already");
        reg = op->reg;
        goto reg_found;
    }
    try_next:
        c = *str++;
        switch(c) {
        case '=':
            goto try_next;
        case '+':
            op->is_rw = 1;
            /* FALL THRU */
        case '&':
            if (j >= nb_outputs)
                tcc_error("'%c' modifier can only be applied to outputs", c);
            reg_mask = REG_IN_MASK | REG_OUT_MASK;
            goto try_next;
        case 'A':
            /* allocate both eax and edx */
            if (is_reg_allocated(TREG_XAX) ||
                is_reg_allocated(TREG_XDX))
                goto try_next;
            op->is_llong = 1;
            op->reg = TREG_XAX;
            regs_allocated[TREG_XAX] |= reg_mask;
            regs_allocated[TREG_XDX] |= reg_mask;
            break;
        case 'a':
            reg = TREG_XAX;
            goto alloc_reg;
        case 'b':
            reg = 3;
            goto alloc_reg;
        case 'c':
            reg = TREG_XCX;
            goto alloc_reg;
        case 'd':
            reg = TREG_XDX;
            goto alloc_reg;
        case 'S':
            reg = 6;
            goto alloc_reg;
        case 'D':
            reg = 7;
        alloc_reg:
            if (is_reg_allocated(reg))
                goto try_next;
            goto reg_found;
        case 'q':
            /* eax, ebx, ecx or edx */
            for(reg = 0; reg < 4; reg++) {
                if (!is_reg_allocated(reg))
                    goto reg_found;
            }
            goto try_next;
        case 'r':
    case 'R':
    case 'p': /* A general address, for x86(64) any register is acceptable*/
            /* any general register */
            for(reg = 0; reg < 8; reg++) {
                if (!is_reg_allocated(reg))
                    goto reg_found;
            }
            goto try_next;
        reg_found:
            /* now we can reload in the register */
            op->is_llong = 0;
            op->reg = reg;
            regs_allocated[reg] |= reg_mask;
            break;
    case 'e':
        case 'i':
            if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST))
                goto try_next;
            break;
        case 'I':
        case 'N':
        case 'M':
            if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST))
                goto try_next;
            break;
        case 'm':
        case 'g':
            /* nothing special to do because the operand is already in
               memory, except if the pointer itself is stored in a
               memory variable (VT_LLOCAL case) */
            /* XXX: fix constant case */
            /* if it is a reference to a memory zone, it must lie
               in a register, so we reserve the register in the
               input registers and a load will be generated
               later */
            if (j < nb_outputs || c == 'm') {
                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
                    /* any general register */
                    for(reg = 0; reg < 8; reg++) {
                        if (!(regs_allocated[reg] & REG_IN_MASK))
                            goto reg_found1;
                    }
                    goto try_next;
                reg_found1:
                    /* now we can reload in the register */
                    regs_allocated[reg] |= REG_IN_MASK;
                    op->reg = reg;
                    op->is_memory = 1;
                }
            }
            break;
        default:
            tcc_error("asm constraint %d ('%s') could not be satisfied",
                  j, op->constraint);
            break;
        }
        /* if a reference is present for that operand, we assign it too */
        if (op->input_index >= 0) {
            operands[op->input_index].reg = op->reg;
            operands[op->input_index].is_llong = op->is_llong;
        }
    }

    /* compute out_reg. It is used to store outputs registers to memory
       locations references by pointers (VT_LLOCAL case) */
    *pout_reg = -1;
    for(i=0;i<nb_operands;i++) {
        op = &operands[i];
        if (op->reg >= 0 &&
            (op->vt->r & VT_VALMASK) == VT_LLOCAL  &&
            !op->is_memory) {
            for(reg = 0; reg < 8; reg++) {
                if (!(regs_allocated[reg] & REG_OUT_MASK))
                    goto reg_found2;
            }
            tcc_error("could not find free output register for reloading");
        reg_found2:
            *pout_reg = reg;
            break;
        }
    }

    /* print sorted constraints */
#ifdef ASM_DEBUG
    for(i=0;i<nb_operands;i++) {
        j = sorted_op[i];
        op = &operands[j];
        printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
               j,
               op->id ? get_tok_str(op->id, NULL) : "",
               op->constraint,
               op->vt->r,
               op->reg);
    }
    if (*pout_reg >= 0)
        printf("out_reg=%d\n", *pout_reg);
#endif
}

ST_FUNC void subst_asm_operand(CString *add_str,
                              SValue *sv, int modifier)
{
    int r, reg, size, val;
    char buf[64];

    r = sv->r;
    if ((r & VT_VALMASK) == VT_CONST) {
        if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n' &&
        modifier != 'P')
            cstr_ccat(add_str, '$');
        if (r & VT_SYM) {
        const char *name = get_tok_str(sv->sym->v, NULL);
        if (sv->sym->v >= SYM_FIRST_ANOM) {
        /* In case of anonymous symbols ("L.42", used
           for static data labels) we can't find them
           in the C symbol table when later looking up
           this name.  So enter them now into the asm label
           list when we still know the symbol.  */
        get_asm_sym(tok_alloc(name, strlen(name))->tok, sv->sym);
        }
            cstr_cat(add_str, name, -1);
            if ((uint32_t)sv->c.i == 0)
                goto no_offset;
        cstr_ccat(add_str, '+');
        }
        val = sv->c.i;
        if (modifier == 'n')
            val = -val;
        snprintf(buf, sizeof(buf), "%d", (int)sv->c.i);
        cstr_cat(add_str, buf, -1);
    no_offset:;
#ifdef TCC_TARGET_X86_64
        if (r & VT_LVAL)
            cstr_cat(add_str, "(%rip)", -1);
#endif
    } else if ((r & VT_VALMASK) == VT_LOCAL) {
#ifdef TCC_TARGET_X86_64
        snprintf(buf, sizeof(buf), "%d(%%rbp)", (int)sv->c.i);
#else
        snprintf(buf, sizeof(buf), "%d(%%ebp)", (int)sv->c.i);
#endif
        cstr_cat(add_str, buf, -1);
    } else if (r & VT_LVAL) {
        reg = r & VT_VALMASK;
        if (reg >= VT_CONST)
            tcc_error("internal compiler error");
        snprintf(buf, sizeof(buf), "(%%%s)",
#ifdef TCC_TARGET_X86_64
                 get_tok_str(TOK_ASM_rax + reg, NULL)
#else
                 get_tok_str(TOK_ASM_eax + reg, NULL)
#endif
         );
        cstr_cat(add_str, buf, -1);
    } else {
        /* register case */
        reg = r & VT_VALMASK;
        if (reg >= VT_CONST)
            tcc_error("internal compiler error");

        /* choose register operand size */
        if ((sv->type.t & VT_BTYPE) == VT_BYTE ||
        (sv->type.t & VT_BTYPE) == VT_BOOL)
            size = 1;
        else if ((sv->type.t & VT_BTYPE) == VT_SHORT)
            size = 2;
#ifdef TCC_TARGET_X86_64
        else if ((sv->type.t & VT_BTYPE) == VT_LLONG ||
         (sv->type.t & VT_BTYPE) == VT_PTR)
            size = 8;
#endif
        else
            size = 4;
        if (size == 1 && reg >= 4)
            size = 4;

        if (modifier == 'b') {
            if (reg >= 4)
                tcc_error("cannot use byte register");
            size = 1;
        } else if (modifier == 'h') {
            if (reg >= 4)
                tcc_error("cannot use byte register");
            size = -1;
        } else if (modifier == 'w') {
            size = 2;
        } else if (modifier == 'k') {
            size = 4;
#ifdef TCC_TARGET_X86_64
        } else if (modifier == 'q') {
            size = 8;
#endif
        }

        switch(size) {
        case -1:
            reg = TOK_ASM_ah + reg;
            break;
        case 1:
            reg = TOK_ASM_al + reg;
            break;
        case 2:
            reg = TOK_ASM_ax + reg;
            break;
        default:
            reg = TOK_ASM_eax + reg;
            break;
#ifdef TCC_TARGET_X86_64
        case 8:
            reg = TOK_ASM_rax + reg;
            break;
#endif
        }
        snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL));
        cstr_cat(add_str, buf, -1);
    }
}

/* generate prolog and epilog code for asm statement */
ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
                         int nb_outputs, int is_output,
                         uint8_t *clobber_regs,
                         int out_reg)
{
    uint8_t regs_allocated[NB_ASM_REGS];
    ASMOperand *op;
    int i, reg;

    /* Strictly speaking %Xbp and %Xsp should be included in the
       call-preserved registers, but currently it doesn't matter.  */
#ifdef TCC_TARGET_X86_64
#ifdef TCC_TARGET_PE
    static uint8_t reg_saved[] = { 3, 6, 7, 12, 13, 14, 15 };
#else
    static uint8_t reg_saved[] = { 3, 12, 13, 14, 15 };
#endif
#else
    static uint8_t reg_saved[] = { 3, 6, 7 };
#endif

    /* mark all used registers */
    memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated));
    for(i = 0; i < nb_operands;i++) {
        op = &operands[i];
        if (op->reg >= 0)
            regs_allocated[op->reg] = 1;
    }
    if (!is_output) {
        /* generate reg save code */
        for(i = 0; i < sizeof(reg_saved)/sizeof(reg_saved[0]); i++) {
            reg = reg_saved[i];
            if (regs_allocated[reg]) {
        if (reg >= 8)
          g(0x41), reg-=8;
                g(0x50 + reg);
            }
        }

        /* generate load code */
        for(i = 0; i < nb_operands; i++) {
            op = &operands[i];
            if (op->reg >= 0) {
                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL &&
                    op->is_memory) {
                    /* memory reference case (for both input and
                       output cases) */
                    SValue sv;
                    sv = *op->vt;
                    sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL | VT_LVAL;
                    sv.type.t = VT_PTR;
                    load(op->reg, &sv);
                } else if (i >= nb_outputs || op->is_rw) {
                    /* load value in register */
                    load(op->reg, op->vt);
                    if (op->is_llong) {
                        SValue sv;
                        sv = *op->vt;
                        sv.c.i += 4;
                        load(TREG_XDX, &sv);
                    }
                }
            }
        }
    } else {
        /* generate save code */
        for(i = 0 ; i < nb_outputs; i++) {
            op = &operands[i];
            if (op->reg >= 0) {
                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
                    if (!op->is_memory) {
                        SValue sv;
                        sv = *op->vt;
                        sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
            sv.type.t = VT_PTR;
                        load(out_reg, &sv);

            sv = *op->vt;
                        sv.r = (sv.r & ~VT_VALMASK) | out_reg;
                        store(op->reg, &sv);
                    }
                } else {
                    store(op->reg, op->vt);
                    if (op->is_llong) {
                        SValue sv;
                        sv = *op->vt;
                        sv.c.i += 4;
                        store(TREG_XDX, &sv);
                    }
                }
            }
        }
        /* generate reg restore code */
        for(i = sizeof(reg_saved)/sizeof(reg_saved[0]) - 1; i >= 0; i--) {
            reg = reg_saved[i];
            if (regs_allocated[reg]) {
        if (reg >= 8)
          g(0x41), reg-=8;
                g(0x58 + reg);
            }
        }
    }
}

ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str)
{
    int reg;
    TokenSym *ts;
#ifdef TCC_TARGET_X86_64
    unsigned int type;
#endif

    if (!strcmp(str, "memory") ||
        !strcmp(str, "cc") ||
    !strcmp(str, "flags"))
        return;
    ts = tok_alloc(str, strlen(str));
    reg = ts->tok;
    if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) {
        reg -= TOK_ASM_eax;
    } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) {
        reg -= TOK_ASM_ax;
#ifdef TCC_TARGET_X86_64
    } else if (reg >= TOK_ASM_rax && reg <= TOK_ASM_rdi) {
        reg -= TOK_ASM_rax;
    } else if ((reg = asm_parse_numeric_reg(reg, &type)) >= 0) {
    ;
#endif
    } else {
        tcc_error("invalid clobber register '%s'", str);
    }
    clobber_regs[reg] = 1;
}

https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/tcctools.c

File: ../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/tcctools.c used in: Step 488
/* -------------------------------------------------------------- */
/*
 *  TCC - Tiny C Compiler
 *
 *  tcctools.c - extra tools and and -m32/64 support
 *
 */

/* -------------------------------------------------------------- */
/*
 * This program is for making libtcc1.a without ar
 * tiny_libmaker - tiny elf lib maker
 * usage: tiny_libmaker [lib] files...
 * Copyright (c) 2007 Timppa
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "tcc.h"

//#define ARMAG  "!<arch>\n"
#define ARFMAG "`\n"

typedef struct {
    char ar_name[16];
    char ar_date[12];
    char ar_uid[6];
    char ar_gid[6];
    char ar_mode[8];
    char ar_size[10];
    char ar_fmag[2];
} ArHdr;

static unsigned long le2belong(unsigned long ul) {
    return ((ul & 0xFF0000)>>8)+((ul & 0xFF000000)>>24) +
        ((ul & 0xFF)<<24)+((ul & 0xFF00)<<8);
}

/* Returns 1 if s contains any of the chars of list, else 0 */
static int contains_any(const char *s, const char *list) {
  const char *l;
  for (; *s; s++) {
      for (l = list; *l; l++) {
          if (*s == *l)
              return 1;
      }
  }
  return 0;
}

static int ar_usage(int ret) {
    fprintf(stderr, "usage: tcc -ar [rcsv] lib file...\n");
    fprintf(stderr, "create library ([abdioptxN] not supported).\n");
    return ret;
}

ST_FUNC int tcc_tool_ar(TCCState *s1, int argc, char **argv)
{
    static ArHdr arhdr = {
        "/               ",
        "            ",
        "0     ",
        "0     ",
        "0       ",
        "          ",
        ARFMAG
        };

    static ArHdr arhdro = {
        "                ",
        "            ",
        "0     ",
        "0     ",
        "0       ",
        "          ",
        ARFMAG
        };

    FILE *fi, *fh = NULL, *fo = NULL;
    ElfW(Ehdr) *ehdr;
    ElfW(Shdr) *shdr;
    ElfW(Sym) *sym;
    int i, fsize, i_lib, i_obj;
    char *buf, *shstr, *symtab = NULL, *strtab = NULL;
    int symtabsize = 0;//, strtabsize = 0;
    char *anames = NULL;
    int *afpos = NULL;
    int istrlen, strpos = 0, fpos = 0, funccnt = 0, funcmax, hofs;
    char tfile[260], stmp[20];
    char *file, *name;
    int ret = 2;
    const char *ops_conflict = "habdioptxN";  // unsupported but destructive if ignored.
    int verbose = 0;

    i_lib = 0; i_obj = 0;  // will hold the index of the lib and first obj
    for (i = 1; i < argc; i++) {
        const char *a = argv[i];
        if (*a == '-' && strstr(a, "."))
            ret = 1; // -x.y is always invalid (same as gnu ar)
        if ((*a == '-') || (i == 1 && !strstr(a, "."))) {  // options argument
            if (contains_any(a, ops_conflict))
                ret = 1;
            if (strstr(a, "v"))
                verbose = 1;
        } else {  // lib or obj files: don't abort - keep validating all args.
            if (!i_lib)  // first file is the lib
                i_lib = i;
            else if (!i_obj)  // second file is the first obj
                i_obj = i;
        }
    }

    if (!i_obj)  // i_obj implies also i_lib. we require both.
        ret = 1;

    if (ret == 1)
        return ar_usage(ret);

    if ((fh = fopen(argv[i_lib], "wb")) == NULL)
    {
        fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_lib]);
        goto the_end;
    }

    sprintf(tfile, "%s.tmp", argv[i_lib]);
    if ((fo = fopen(tfile, "wb+")) == NULL)
    {
        fprintf(stderr, "tcc: ar: can't create temporary file %s\n", tfile);
        goto the_end;
    }

    funcmax = 250;
    afpos = tcc_realloc(NULL, funcmax * sizeof *afpos); // 250 func
    memcpy(&arhdro.ar_mode, "100666", 6);

    // i_obj = first input object file
    while (i_obj < argc)
    {
        if (*argv[i_obj] == '-') {  // by now, all options start with '-'
            i_obj++;
            continue;
        }
        if ((fi = fopen(argv[i_obj], "rb")) == NULL) {
            fprintf(stderr, "tcc: ar: can't open file %s \n", argv[i_obj]);
            goto the_end;
        }
        if (verbose)
            printf("a - %s\n", argv[i_obj]);

        fseek(fi, 0, SEEK_END);
        fsize = ftell(fi);
        fseek(fi, 0, SEEK_SET);
        buf = tcc_malloc(fsize + 1);
        fread(buf, fsize, 1, fi);
        fclose(fi);

        // elf header
        ehdr = (ElfW(Ehdr) *)buf;
        if (ehdr->e_ident[4] != ELFCLASSW)
        {
            fprintf(stderr, "tcc: ar: Unsupported Elf Class: %s\n", argv[i_obj]);
            goto the_end;
        }

        shdr = (ElfW(Shdr) *) (buf + ehdr->e_shoff + ehdr->e_shstrndx * ehdr->e_shentsize);
        shstr = (char *)(buf + shdr->sh_offset);
        for (i = 0; i < ehdr->e_shnum; i++)
        {
            shdr = (ElfW(Shdr) *) (buf + ehdr->e_shoff + i * ehdr->e_shentsize);
            if (!shdr->sh_offset)
                continue;
            if (shdr->sh_type == SHT_SYMTAB)
            {
                symtab = (char *)(buf + shdr->sh_offset);
                symtabsize = shdr->sh_size;
            }
            if (shdr->sh_type == SHT_STRTAB)
            {
                if (!strcmp(shstr + shdr->sh_name, ".strtab"))
                {
                    strtab = (char *)(buf + shdr->sh_offset);
                    //strtabsize = shdr->sh_size;
                }
            }
        }

        if (symtab && symtabsize)
        {
            int nsym = symtabsize / sizeof(ElfW(Sym));
            //printf("symtab: info size shndx name\n");
            for (i = 1; i < nsym; i++)
            {
                sym = (ElfW(Sym) *) (symtab + i * sizeof(ElfW(Sym)));
                if (sym->st_shndx &&
                    (sym->st_info == 0x10
                    || sym->st_info == 0x11
                    || sym->st_info == 0x12
                    )) {
                    //printf("symtab: %2Xh %4Xh %2Xh %s\n", sym->st_info, sym->st_size, sym->st_shndx, strtab + sym->st_name);
                    istrlen = strlen(strtab + sym->st_name)+1;
                    anames = tcc_realloc(anames, strpos+istrlen);
                    strcpy(anames + strpos, strtab + sym->st_name);
                    strpos += istrlen;
                    if (++funccnt >= funcmax) {
                        funcmax += 250;
                        afpos = tcc_realloc(afpos, funcmax * sizeof *afpos); // 250 func more
                    }
                    afpos[funccnt] = fpos;
                }
            }
        }

        file = argv[i_obj];
        for (name = strchr(file, 0);
             name > file && name[-1] != '/' && name[-1] != '\\';
             --name);
        istrlen = strlen(name);
        if (istrlen >= sizeof(arhdro.ar_name))
            istrlen = sizeof(arhdro.ar_name) - 1;
        memset(arhdro.ar_name, ' ', sizeof(arhdro.ar_name));
        memcpy(arhdro.ar_name, name, istrlen);
        arhdro.ar_name[istrlen] = '/';
        sprintf(stmp, "%-10d", fsize);
        memcpy(&arhdro.ar_size, stmp, 10);
        fwrite(&arhdro, sizeof(arhdro), 1, fo);
        fwrite(buf, fsize, 1, fo);
        tcc_free(buf);
        i_obj++;
        fpos += (fsize + sizeof(arhdro));
    }
    hofs = 8 + sizeof(arhdr) + strpos + (funccnt+1) * sizeof(int);
    fpos = 0;
    if ((hofs & 1)) // align
        hofs++, fpos = 1;
    // write header
    fwrite("!<arch>\n", 8, 1, fh);
    sprintf(stmp, "%-10d", (int)(strpos + (funccnt+1) * sizeof(int)));
    memcpy(&arhdr.ar_size, stmp, 10);
    fwrite(&arhdr, sizeof(arhdr), 1, fh);
    afpos[0] = le2belong(funccnt);
    for (i=1; i<=funccnt; i++)
        afpos[i] = le2belong(afpos[i] + hofs);
    fwrite(afpos, (funccnt+1) * sizeof(int), 1, fh);
    fwrite(anames, strpos, 1, fh);
    if (fpos)
        fwrite("", 1, 1, fh);
    // write objects
    fseek(fo, 0, SEEK_END);
    fsize = ftell(fo);
    fseek(fo, 0, SEEK_SET);
    buf = tcc_malloc(fsize + 1);
    fread(buf, fsize, 1, fo);
    fwrite(buf, fsize, 1, fh);
    tcc_free(buf);
    ret = 0;
the_end:
    if (anames)
        tcc_free(anames);
    if (afpos)
        tcc_free(afpos);
    if (fh)
        fclose(fh);
    if (fo)
        fclose(fo), remove(tfile);
    return ret;
}

/* -------------------------------------------------------------- */
/*
 * tiny_impdef creates an export definition file (.def) from a dll
 * on MS-Windows. Usage: tiny_impdef library.dll [-o outputfile]"
 *
 *  Copyright (c) 2005,2007 grischka
 *
 * 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.
 */

#ifdef TCC_TARGET_PE

ST_FUNC int tcc_tool_impdef(TCCState *s1, int argc, char **argv)
{
    int ret, v, i;
    char infile[260];
    char outfile[260];

    const char *file;
    char *p, *q;
    FILE *fp, *op;

#ifdef _WIN32
    char path[260];
#endif

    infile[0] = outfile[0] = 0;
    fp = op = NULL;
    ret = 1;
    p = NULL;
    v = 0;

    for (i = 1; i < argc; ++i) {
        const char *a = argv[i];
        if ('-' == a[0]) {
            if (0 == strcmp(a, "-v")) {
                v = 1;
            } else if (0 == strcmp(a, "-o")) {
                if (++i == argc)
                    goto usage;
                strcpy(outfile, argv[i]);
            } else
                goto usage;
        } else if (0 == infile[0])
            strcpy(infile, a);
        else
            goto usage;
    }

    if (0 == infile[0]) {
usage:
        fprintf(stderr,
            "usage: tcc -impdef library.dll [-v] [-o outputfile]\n"
            "create export definition file (.def) from dll\n"
            );
        goto the_end;
    }

    if (0 == outfile[0]) {
        strcpy(outfile, tcc_basename(infile));
        q = strrchr(outfile, '.');
        if (NULL == q)
            q = strchr(outfile, 0);
        strcpy(q, ".def");
    }

    file = infile;
#ifdef _WIN32
    if (SearchPath(NULL, file, ".dll", sizeof path, path, NULL))
        file = path;
#endif
    ret = tcc_get_dllexports(file, &p);
    if (ret || !p) {
        fprintf(stderr, "tcc: impdef: %s '%s'\n",
            ret == -1 ? "can't find file" :
            ret ==  1 ? "can't read symbols" :
            ret ==  0 ? "no symbols found in" :
            "unknown file type", file);
        ret = 1;
        goto the_end;
    }

    if (v)
        printf("-> %s\n", file);

    op = fopen(outfile, "wb");
    if (NULL == op) {
        fprintf(stderr, "tcc: impdef: could not create output file: %s\n", outfile);
        goto the_end;
    }

    fprintf(op, "LIBRARY %s\n\nEXPORTS\n", tcc_basename(file));
    for (q = p, i = 0; *q; ++i) {
        fprintf(op, "%s\n", q);
        q += strlen(q) + 1;
    }

    if (v)
        printf("<- %s (%d symbol%s)\n", outfile, i, &"s"[i<2]);

    ret = 0;

the_end:
    /* cannot free memory received from tcc_get_dllexports
       if it came from a dll */
    /* if (p)
        tcc_free(p); */
    if (fp)
        fclose(fp);
    if (op)
        fclose(op);
    return ret;
}

#endif /* TCC_TARGET_PE */

/* -------------------------------------------------------------- */
/*
 *  TCC - Tiny C Compiler
 *
 *  Copyright (c) 2001-2004 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/* re-execute the i386/x86_64 cross-compilers with tcc -m32/-m64: */

#if !defined TCC_TARGET_I386 && !defined TCC_TARGET_X86_64

ST_FUNC void tcc_tool_cross(TCCState *s, char **argv, int option)
{
    tcc_error("-m%d not implemented.", option);
}

#else
#ifdef _WIN32
#include <process.h>

static char *str_replace(const char *str, const char *p, const char *r)
{
    const char *s, *s0;
    char *d, *d0;
    int sl, pl, rl;

    sl = strlen(str);
    pl = strlen(p);
    rl = strlen(r);
    for (d0 = NULL;; d0 = tcc_malloc(sl + 1)) {
        for (d = d0, s = str; s0 = s, s = strstr(s, p), s; s += pl) {
            if (d) {
                memcpy(d, s0, sl = s - s0), d += sl;
                memcpy(d, r, rl), d += rl;
            } else
                sl += rl - pl;
        }
        if (d) {
            strcpy(d, s0);
            return d0;
        }
    }
}

static int execvp_win32(const char *prog, char **argv)
{
    int ret; char **p;
    /* replace all " by \" */
    for (p = argv; *p; ++p)
        if (strchr(*p, '"'))
            *p = str_replace(*p, "\"", "\\\"");
    ret = _spawnvp(P_NOWAIT, prog, (const char *const*)argv);
    if (-1 == ret)
        return ret;
    _cwait(&ret, ret, WAIT_CHILD);
    exit(ret);
}
#define execvp execvp_win32
#endif /* _WIN32 */

ST_FUNC void tcc_tool_cross(TCCState *s, char **argv, int target)
{
    char program[4096];
    char *a0 = argv[0];
    int prefix = tcc_basename(a0) - a0;

    snprintf(program, sizeof program,
        "%.*s%s"
#ifdef TCC_TARGET_PE
        "-win32"
#endif
        "-tcc"
#ifdef _WIN32
        ".exe"
#endif
        , prefix, a0, target == 64 ? "x86_64" : "i386");

    if (strcmp(a0, program))
        execvp(argv[0] = program, argv);
    tcc_error("could not run '%s'", program);
}

#endif /* TCC_TARGET_I386 && TCC_TARGET_X86_64 */
/* -------------------------------------------------------------- */
/* enable commandline wildcard expansion (tcc -o x.exe *.c) */

#ifdef _WIN32
int _CRT_glob = 1;
#ifndef _CRT_glob
int _dowildcard = 1;
#endif
#endif

/* -------------------------------------------------------------- */
/* generate xxx.d file */

ST_FUNC void gen_makedeps(TCCState *s, const char *target, const char *filename)
{
    FILE *depout;
    char buf[1024];
    int i;

    if (!filename) {
        /* compute filename automatically: dir/file.o -> dir/file.d */
        snprintf(buf, sizeof buf, "%.*s.d",
            (int)(tcc_fileextension(target) - target), target);
        filename = buf;
    }

    if (s->verbose)
        printf("<- %s\n", filename);

    /* XXX return err codes instead of error() ? */
    depout = fopen(filename, "w");
    if (!depout)
        tcc_error("could not open '%s'", filename);

    fprintf(depout, "%s: \\\n", target);
    for (i=0; i<s->nb_target_deps; ++i)
        fprintf(depout, " %s \\\n", s->target_deps[i]);
    fprintf(depout, "\n");
    fclose(depout);
}

/* -------------------------------------------------------------- */

https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/lib/libtcc1.c

File: ../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/lib/libtcc1.c used in: Step 494
/* TCC runtime library. 
   Parts of this code are (c) 2002 Fabrice Bellard 

   Copyright (C) 1987, 1988, 1992, 1994, 1995 Free Software Foundation, Inc.

This file 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.

In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file.  (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)

This file is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  
*/

#define W_TYPE_SIZE   32
#define BITS_PER_UNIT 8

typedef int Wtype;
typedef unsigned int UWtype;
typedef unsigned int USItype;
typedef long long DWtype;
typedef unsigned long long UDWtype;

struct DWstruct {
    Wtype low, high;
};

typedef union
{
  struct DWstruct s;
  DWtype ll;
} DWunion;

typedef long double XFtype;
#define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT)
#define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE)

/* the following deal with IEEE single-precision numbers */
#define EXCESS      126
#define SIGNBIT     0x80000000
#define HIDDEN      (1 << 23)
#define SIGN(fp)    ((fp) & SIGNBIT)
#define EXP(fp)     (((fp) >> 23) & 0xFF)
#define MANT(fp)    (((fp) & 0x7FFFFF) | HIDDEN)
#define PACK(s,e,m) ((s) | ((e) << 23) | (m))

/* the following deal with IEEE double-precision numbers */
#define EXCESSD     1022
#define HIDDEND     (1 << 20)
#define EXPD(fp)    (((fp.l.upper) >> 20) & 0x7FF)
#define SIGND(fp)   ((fp.l.upper) & SIGNBIT)
#define MANTD(fp)   (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) | \
                (fp.l.lower >> 22))
#define HIDDEND_LL  ((long long)1 << 52)
#define MANTD_LL(fp)    ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL)
#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m))

/* the following deal with x86 long double-precision numbers */
#define EXCESSLD    16382
#define EXPLD(fp)   (fp.l.upper & 0x7fff)
#define SIGNLD(fp)  ((fp.l.upper) & 0x8000)

/* only for x86 */
union ldouble_long {
    long double ld;
    struct {
        unsigned long long lower;
        unsigned short upper;
    } l;
};

union double_long {
    double d;
#if 1
    struct {
        unsigned int lower;
        int upper;
    } l;
#else
    struct {
        int upper;
        unsigned int lower;
    } l;
#endif
    long long ll;
};

union float_long {
    float f;
    unsigned int l;
};

/* XXX: we don't support several builtin supports for now */
#if !defined __x86_64__ && !defined __arm__

/* XXX: use gcc/tcc intrinsic ? */
#if defined __i386__
#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
  __asm__ ("subl %5,%1\n\tsbbl %3,%0"                   \
       : "=r" ((USItype) (sh)),                 \
         "=&r" ((USItype) (sl))                 \
       : "0" ((USItype) (ah)),                  \
         "g" ((USItype) (bh)),                  \
         "1" ((USItype) (al)),                  \
         "g" ((USItype) (bl)))
#define umul_ppmm(w1, w0, u, v) \
  __asm__ ("mull %3"                            \
       : "=a" ((USItype) (w0)),                 \
         "=d" ((USItype) (w1))                  \
       : "%0" ((USItype) (u)),                  \
         "rm" ((USItype) (v)))
#define udiv_qrnnd(q, r, n1, n0, dv) \
  __asm__ ("divl %4"                            \
       : "=a" ((USItype) (q)),                  \
         "=d" ((USItype) (r))                   \
       : "0" ((USItype) (n0)),                  \
         "1" ((USItype) (n1)),                  \
         "rm" ((USItype) (dv)))
#define count_leading_zeros(count, x) \
  do {                                  \
    USItype __cbtmp;                            \
    __asm__ ("bsrl %1,%0"                       \
         : "=r" (__cbtmp) : "rm" ((USItype) (x)));          \
    (count) = __cbtmp ^ 31;                     \
  } while (0)
#else
#error unsupported CPU type
#endif

/* most of this code is taken from libgcc2.c from gcc */

static UDWtype __udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp)
{
  DWunion ww;
  DWunion nn, dd;
  DWunion rr;
  UWtype d0, d1, n0, n1, n2;
  UWtype q0, q1;
  UWtype b, bm;

  nn.ll = n;
  dd.ll = d;

  d0 = dd.s.low;
  d1 = dd.s.high;
  n0 = nn.s.low;
  n1 = nn.s.high;

#if !defined(UDIV_NEEDS_NORMALIZATION)
  if (d1 == 0)
    {
      if (d0 > n1)
    {
      /* 0q = nn / 0D */

      udiv_qrnnd (q0, n0, n1, n0, d0);
      q1 = 0;

      /* Remainder in n0.  */
    }
      else
    {
      /* qq = NN / 0d */

      if (d0 == 0)
        d0 = 1 / d0;    /* Divide intentionally by zero.  */

      udiv_qrnnd (q1, n1, 0, n1, d0);
      udiv_qrnnd (q0, n0, n1, n0, d0);

      /* Remainder in n0.  */
    }

      if (rp != 0)
    {
      rr.s.low = n0;
      rr.s.high = 0;
      *rp = rr.ll;
    }
    }

#else /* UDIV_NEEDS_NORMALIZATION */

  if (d1 == 0)
    {
      if (d0 > n1)
    {
      /* 0q = nn / 0D */

      count_leading_zeros (bm, d0);

      if (bm != 0)
        {
          /* Normalize, i.e. make the most significant bit of the
         denominator set.  */

          d0 = d0 << bm;
          n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm));
          n0 = n0 << bm;
        }

      udiv_qrnnd (q0, n0, n1, n0, d0);
      q1 = 0;

      /* Remainder in n0 >> bm.  */
    }
      else
    {
      /* qq = NN / 0d */

      if (d0 == 0)
        d0 = 1 / d0;    /* Divide intentionally by zero.  */

      count_leading_zeros (bm, d0);

      if (bm == 0)
        {
          /* From (n1 >= d0) /\ (the most significant bit of d0 is set),
         conclude (the most significant bit of n1 is set) /\ (the
         leading quotient digit q1 = 1).

         This special case is necessary, not an optimization.
         (Shifts counts of W_TYPE_SIZE are undefined.)  */

          n1 -= d0;
          q1 = 1;
        }
      else
        {
          /* Normalize.  */

          b = W_TYPE_SIZE - bm;

          d0 = d0 << bm;
          n2 = n1 >> b;
          n1 = (n1 << bm) | (n0 >> b);
          n0 = n0 << bm;

          udiv_qrnnd (q1, n1, n2, n1, d0);
        }

      /* n1 != d0...  */

      udiv_qrnnd (q0, n0, n1, n0, d0);

      /* Remainder in n0 >> bm.  */
    }

      if (rp != 0)
    {
      rr.s.low = n0 >> bm;
      rr.s.high = 0;
      *rp = rr.ll;
    }
    }
#endif /* UDIV_NEEDS_NORMALIZATION */

  else
    {
      if (d1 > n1)
    {
      /* 00 = nn / DD */

      q0 = 0;
      q1 = 0;

      /* Remainder in n1n0.  */
      if (rp != 0)
        {
          rr.s.low = n0;
          rr.s.high = n1;
          *rp = rr.ll;
        }
    }
      else
    {
      /* 0q = NN / dd */

      count_leading_zeros (bm, d1);
      if (bm == 0)
        {
          /* From (n1 >= d1) /\ (the most significant bit of d1 is set),
         conclude (the most significant bit of n1 is set) /\ (the
         quotient digit q0 = 0 or 1).

         This special case is necessary, not an optimization.  */

          /* The condition on the next line takes advantage of that
         n1 >= d1 (true due to program flow).  */
          if (n1 > d1 || n0 >= d0)
        {
          q0 = 1;
          sub_ddmmss (n1, n0, n1, n0, d1, d0);
        }
          else
        q0 = 0;

          q1 = 0;

          if (rp != 0)
        {
          rr.s.low = n0;
          rr.s.high = n1;
          *rp = rr.ll;
        }
        }
      else
        {
          UWtype m1, m0;
          /* Normalize.  */

          b = W_TYPE_SIZE - bm;

          d1 = (d1 << bm) | (d0 >> b);
          d0 = d0 << bm;
          n2 = n1 >> b;
          n1 = (n1 << bm) | (n0 >> b);
          n0 = n0 << bm;

          udiv_qrnnd (q0, n1, n2, n1, d1);
          umul_ppmm (m1, m0, q0, d0);

          if (m1 > n1 || (m1 == n1 && m0 > n0))
        {
          q0--;
          sub_ddmmss (m1, m0, m1, m0, d1, d0);
        }

          q1 = 0;

          /* Remainder in (n1n0 - m1m0) >> bm.  */
          if (rp != 0)
        {
          sub_ddmmss (n1, n0, n1, n0, m1, m0);
          rr.s.low = (n1 << b) | (n0 >> bm);
          rr.s.high = n1 >> bm;
          *rp = rr.ll;
        }
        }
    }
    }

  ww.s.low = q0;
  ww.s.high = q1;
  return ww.ll;
}

#define __negdi2(a) (-(a))

long long __divdi3(long long u, long long v)
{
    int c = 0;
    DWunion uu, vv;
    DWtype w;
    
    uu.ll = u;
    vv.ll = v;
    
    if (uu.s.high < 0) {
        c = ~c;
        uu.ll = __negdi2 (uu.ll);
    }
    if (vv.s.high < 0) {
        c = ~c;
        vv.ll = __negdi2 (vv.ll);
    }
    w = __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) 0);
    if (c)
        w = __negdi2 (w);
    return w;
}

long long __moddi3(long long u, long long v)
{
    int c = 0;
    DWunion uu, vv;
    DWtype w;
    
    uu.ll = u;
    vv.ll = v;
    
    if (uu.s.high < 0) {
        c = ~c;
        uu.ll = __negdi2 (uu.ll);
    }
    if (vv.s.high < 0)
        vv.ll = __negdi2 (vv.ll);
    
    __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) &w);
    if (c)
        w = __negdi2 (w);
    return w;
}

unsigned long long __udivdi3(unsigned long long u, unsigned long long v)
{
    return __udivmoddi4 (u, v, (UDWtype *) 0);
}

unsigned long long __umoddi3(unsigned long long u, unsigned long long v)
{
    UDWtype w;
    
    __udivmoddi4 (u, v, &w);
    return w;
}

/* XXX: fix tcc's code generator to do this instead */
long long __ashrdi3(long long a, int b)
{
#ifdef __TINYC__
    DWunion u;
    u.ll = a;
    if (b >= 32) {
        u.s.low = u.s.high >> (b - 32);
        u.s.high = u.s.high >> 31;
    } else if (b != 0) {
        u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b));
        u.s.high = u.s.high >> b;
    }
    return u.ll;
#else
    return a >> b;
#endif
}

/* XXX: fix tcc's code generator to do this instead */
unsigned long long __lshrdi3(unsigned long long a, int b)
{
#ifdef __TINYC__
    DWunion u;
    u.ll = a;
    if (b >= 32) {
        u.s.low = (unsigned)u.s.high >> (b - 32);
        u.s.high = 0;
    } else if (b != 0) {
        u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b));
        u.s.high = (unsigned)u.s.high >> b;
    }
    return u.ll;
#else
    return a >> b;
#endif
}

/* XXX: fix tcc's code generator to do this instead */
long long __ashldi3(long long a, int b)
{
#ifdef __TINYC__
    DWunion u;
    u.ll = a;
    if (b >= 32) {
        u.s.high = (unsigned)u.s.low << (b - 32);
        u.s.low = 0;
    } else if (b != 0) {
        u.s.high = ((unsigned)u.s.high << b) | ((unsigned)u.s.low >> (32 - b));
        u.s.low = (unsigned)u.s.low << b;
    }
    return u.ll;
#else
    return a << b;
#endif
}

#endif /* !__x86_64__ */

/* XXX: fix tcc's code generator to do this instead */
float __floatundisf(unsigned long long a)
{
    DWunion uu; 
    XFtype r;

    uu.ll = a;
    if (uu.s.high >= 0) {
        return (float)uu.ll;
    } else {
        r = (XFtype)uu.ll;
        r += 18446744073709551616.0;
        return (float)r;
    }
}

double __floatundidf(unsigned long long a)
{
    DWunion uu; 
    XFtype r;

    uu.ll = a;
    if (uu.s.high >= 0) {
        return (double)uu.ll;
    } else {
        r = (XFtype)uu.ll;
        r += 18446744073709551616.0;
        return (double)r;
    }
}

long double __floatundixf(unsigned long long a)
{
    DWunion uu; 
    XFtype r;

    uu.ll = a;
    if (uu.s.high >= 0) {
        return (long double)uu.ll;
    } else {
        r = (XFtype)uu.ll;
        r += 18446744073709551616.0;
        return (long double)r;
    }
}

unsigned long long __fixunssfdi (float a1)
{
    register union float_long fl1;
    register int exp;
    register unsigned long l;

    fl1.f = a1;

    if (fl1.l == 0)
    return (0);

    exp = EXP (fl1.l) - EXCESS - 24;

    l = MANT(fl1.l);
    if (exp >= 41)
    return (unsigned long long)-1;
    else if (exp >= 0)
        return (unsigned long long)l << exp;
    else if (exp >= -23)
        return l >> -exp;
    else
        return 0;
}

long long __fixsfdi (float a1)
{
    long long ret; int s;
    ret = __fixunssfdi((s = a1 >= 0) ? a1 : -a1);
    return s ? ret : -ret;
}

unsigned long long __fixunsdfdi (double a1)
{
    register union double_long dl1;
    register int exp;
    register unsigned long long l;

    dl1.d = a1;

    if (dl1.ll == 0)
    return (0);

    exp = EXPD (dl1) - EXCESSD - 53;

    l = MANTD_LL(dl1);

    if (exp >= 12)
    return (unsigned long long)-1;
    else if (exp >= 0)
        return l << exp;
    else if (exp >= -52)
        return l >> -exp;
    else
        return 0;
}

long long __fixdfdi (double a1)
{
    long long ret; int s;
    ret = __fixunsdfdi((s = a1 >= 0) ? a1 : -a1);
    return s ? ret : -ret;
}

#ifndef __arm__
unsigned long long __fixunsxfdi (long double a1)
{
    register union ldouble_long dl1;
    register int exp;
    register unsigned long long l;

    dl1.ld = a1;

    if (dl1.l.lower == 0 && dl1.l.upper == 0)
    return (0);

    exp = EXPLD (dl1) - EXCESSLD - 64;

    l = dl1.l.lower;

    if (exp > 0)
    return (unsigned long long)-1;
    else if (exp >= -63) 
        return l >> -exp;
    else
        return 0;
}

long long __fixxfdi (long double a1)
{
    long long ret; int s;
    ret = __fixunsxfdi((s = a1 >= 0) ? a1 : -a1);
    return s ? ret : -ret;
}
#endif /* !ARM */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tcc-0.9.27/tcc-0.9.27.checksums

File: ../git/live-bootstrap/sysa/tcc-0.9.27/tcc-0.9.27.checksums used in: Step 500
e5f00ef66c5796f436089d3f6e67af001182e6fbb439d13bb65eef8cf82f5973  /usr/bin/tcc

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/make-3.82/sources

File: ../git/live-bootstrap/sysa/make-3.82/sources used in: Step 507
https://mirrors.kernel.org/gnu/make/make-3.82.tar.bz2 e2c1a73f179c40c71e2fe8abf8a8a0688b8499538512984da4a76958d0402966

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/make-3.82/files/putenv_stub.c

File: ../git/live-bootstrap/sysa/make-3.82/files/putenv_stub.c used in: Step 514
/*
 * SPDX-FileCopyrightText: 2023 Paul Dersey <pdersey@gmail.com>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

int putenv(char *string)
{
    return 0;
}

https://git.savannah.gnu.org/cgit/make.git/tree/getopt.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/getopt.c used in: Step 515
/* Getopt for GNU.
NOTE: getopt is now part of the C library, so if you don't know what
"Keep this file name-space clean" means, talk to drepper@gnu.org
before changing it!

Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996,
1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.

NOTE: The canonical source of this file is maintained with the GNU C Library.
Bugs can be reported to bug-glibc@gnu.org.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
   Ditto for AIX 3.2 and <stdlib.h>.  */
#ifndef _NO_PROTO
# define _NO_PROTO
#endif

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#if !defined __STDC__ || !__STDC__
/* This is a separate conditional since some stdc systems
   reject `defined (const)'.  */
# ifndef const
#  define const
# endif
#endif

#include <stdio.h>

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#define GETOPT_INTERFACE_VERSION 2
#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
# include <gnu-versions.h>
# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
#  define ELIDE_CODE
# endif
#endif

#ifndef ELIDE_CODE


/* This needs to come after some library #include
   to get __GNU_LIBRARY__ defined.  */
#ifdef  __GNU_LIBRARY__
/* Don't include stdlib.h for non-GNU C libraries because some of them
   contain conflicting prototypes for getopt.  */
# include <stdlib.h>
# include <unistd.h>
#endif  /* GNU C library.  */

#ifdef VMS
# include <unixlib.h>
# if HAVE_STRING_H - 0
#  include <string.h>
# endif
#endif

/* This is for other GNU distributions with internationalized messages.
   When compiling libc, the _ macro is predefined.  */
#include "gettext.h"
#define _(msgid)    gettext (msgid)


/* This version of `getopt' appears to the caller like standard Unix `getopt'
   but it behaves differently for the user, since it allows the user
   to intersperse the options with the other arguments.

   As `getopt' works, it permutes the elements of ARGV so that,
   when it is done, all the options precede everything else.  Thus
   all application programs are extended to handle flexible argument order.

   Setting the environment variable POSIXLY_CORRECT disables permutation.
   Then the behavior is completely standard.

   GNU application programs can use a third alternative mode in which
   they can distinguish the relative order of options and other arguments.  */

#include "getopt.h"

/* For communication from `getopt' to the caller.
   When `getopt' finds an option that takes an argument,
   the argument value is returned here.
   Also, when `ordering' is RETURN_IN_ORDER,
   each non-option ARGV-element is returned here.  */

char *optarg = NULL;

/* Index in ARGV of the next element to be scanned.
   This is used for communication to and from the caller
   and for communication between successive calls to `getopt'.

   On entry to `getopt', zero means this is the first call; initialize.

   When `getopt' returns -1, this is the index of the first of the
   non-option elements that the caller should itself scan.

   Otherwise, `optind' communicates from one call to the next
   how much of ARGV has been scanned so far.  */

/* 1003.2 says this must be 1 before any call.  */
int optind = 1;

/* Formerly, initialization of getopt depended on optind==0, which
   causes problems with re-calling getopt as programs generally don't
   know that. */

int __getopt_initialized = 0;

/* The next char to be scanned in the option-element
   in which the last option character we returned was found.
   This allows us to pick up the scan where we left off.

   If this is zero, or a null string, it means resume the scan
   by advancing to the next ARGV-element.  */

static char *nextchar;

/* Callers store zero here to inhibit the error message
   for unrecognized options.  */

int opterr = 1;

/* Set to an option character which was unrecognized.
   This must be initialized on some systems to avoid linking in the
   system's own getopt implementation.  */

int optopt = '?';

/* Describe how to deal with options that follow non-option ARGV-elements.

   If the caller did not specify anything,
   the default is REQUIRE_ORDER if the environment variable
   POSIXLY_CORRECT is defined, PERMUTE otherwise.

   REQUIRE_ORDER means don't recognize them as options;
   stop option processing when the first non-option is seen.
   This is what Unix does.
   This mode of operation is selected by either setting the environment
   variable POSIXLY_CORRECT, or using `+' as the first character
   of the list of option characters.

   PERMUTE is the default.  We permute the contents of ARGV as we scan,
   so that eventually all the non-options are at the end.  This allows options
   to be given in any order, even with programs that were not written to
   expect this.

   RETURN_IN_ORDER is an option available to programs that were written
   to expect options and other ARGV-elements in any order and that care about
   the ordering of the two.  We describe each non-option ARGV-element
   as if it were the argument of an option with character code 1.
   Using `-' as the first character of the list of option characters
   selects this mode of operation.

   The special argument `--' forces an end of option-scanning regardless
   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */

static enum
{
  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
} ordering;

/* Value of POSIXLY_CORRECT environment variable.  */
static char *posixly_correct;


#ifdef  __GNU_LIBRARY__
/* We want to avoid inclusion of string.h with non-GNU libraries
   because there are many ways it can cause trouble.
   On some systems, it contains special magic macros that don't work
   in GCC.  */
# include <string.h>
# define my_index   strchr
#else

# if HAVE_STRING_H
#  include <string.h>
# else
#  include <strings.h>
# endif

/* Avoid depending on library functions or files
   whose names are inconsistent.  */

#ifndef getenv
extern char *getenv ();
#endif

static char *
my_index (const char *str, int chr)
{
  while (*str)
    {
      if (*str == chr)
    return (char *) str;
      str++;
    }
  return 0;
}

/* If using GCC, we can safely declare strlen this way.
   If not using GCC, it is ok not to declare it.  */
#ifdef __GNUC__
/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
   That was relevant to code that was here before.  */
# if (!defined __STDC__ || !__STDC__) && !defined strlen
/* gcc with -traditional declares the built-in strlen to return int,
   and has done so at least since version 2.4.5. -- rms.  */
extern int strlen (const char *);
# endif /* not __STDC__ */
#endif /* __GNUC__ */

#endif /* not __GNU_LIBRARY__ */


/* Handle permutation of arguments.  */

/* Describe the part of ARGV that contains non-options that have
   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
   `last_nonopt' is the index after the last of them.  */

static int first_nonopt;
static int last_nonopt;

#ifdef _LIBC
/* Bash 2.0 gives us an environment variable containing flags
   indicating ARGV elements that should not be considered arguments.  */

/* Defined in getopt_init.c  */
extern char *__getopt_nonoption_flags;

static int nonoption_flags_max_len;
static int nonoption_flags_len;

static int original_argc;
static char *const *original_argv;

/* Make sure the environment variable bash 2.0 puts in the environment
   is valid for the getopt call we must make sure that the ARGV passed
   to getopt is that one passed to the process.  */
static void __attribute__ ((unused))
store_args_and_env (int argc, char *const *argv)
{
  /* XXX This is no good solution.  We should rather copy the args so
     that we can compare them later.  But we must not use malloc(3).  */
  original_argc = argc;
  original_argv = argv;
}
# ifdef text_set_element
text_set_element (__libc_subinit, store_args_and_env);
# endif /* text_set_element */

# define SWAP_FLAGS(ch1, ch2) \
  if (nonoption_flags_len > 0)                            \
    {                                         \
      char __tmp = __getopt_nonoption_flags[ch1];                 \
      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];          \
      __getopt_nonoption_flags[ch2] = __tmp;                      \
    }
#else   /* !_LIBC */
# define SWAP_FLAGS(ch1, ch2)
#endif  /* _LIBC */

/* Exchange two adjacent subsequences of ARGV.
   One subsequence is elements [first_nonopt,last_nonopt)
   which contains all the non-options that have been skipped so far.
   The other is elements [last_nonopt,optind), which contains all
   the options processed since those non-options were skipped.

   `first_nonopt' and `last_nonopt' are relocated so that they describe
   the new indices of the non-options in ARGV after they are moved.  */

#if defined __STDC__ && __STDC__
static void exchange (char **);
#endif

static void
exchange (char **argv)
{
  int bottom = first_nonopt;
  int middle = last_nonopt;
  int top = optind;
  char *tem;

  /* Exchange the shorter segment with the far end of the longer segment.
     That puts the shorter segment into the right place.
     It leaves the longer segment in the right place overall,
     but it consists of two parts that need to be swapped next.  */

#ifdef _LIBC
  /* First make sure the handling of the `__getopt_nonoption_flags'
     string can work normally.  Our top argument must be in the range
     of the string.  */
  if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
    {
      /* We must extend the array.  The user plays games with us and
     presents new arguments.  */
      char *new_str = malloc (top + 1);
      if (new_str == NULL)
    nonoption_flags_len = nonoption_flags_max_len = 0;
      else
    {
      memset (__mempcpy (new_str, __getopt_nonoption_flags,
                 nonoption_flags_max_len),
          '\0', top + 1 - nonoption_flags_max_len);
      nonoption_flags_max_len = top + 1;
      __getopt_nonoption_flags = new_str;
    }
    }
#endif

  while (top > middle && middle > bottom)
    {
      if (top - middle > middle - bottom)
    {
      /* Bottom segment is the short one.  */
      int len = middle - bottom;
      register int i;

      /* Swap it with the top part of the top segment.  */
      for (i = 0; i < len; i++)
        {
          tem = argv[bottom + i];
          argv[bottom + i] = argv[top - (middle - bottom) + i];
          argv[top - (middle - bottom) + i] = tem;
          SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
        }
      /* Exclude the moved bottom segment from further swapping.  */
      top -= len;
    }
      else
    {
      /* Top segment is the short one.  */
      int len = top - middle;
      register int i;

      /* Swap it with the bottom part of the bottom segment.  */
      for (i = 0; i < len; i++)
        {
          tem = argv[bottom + i];
          argv[bottom + i] = argv[middle + i];
          argv[middle + i] = tem;
          SWAP_FLAGS (bottom + i, middle + i);
        }
      /* Exclude the moved top segment from further swapping.  */
      bottom += len;
    }
    }

  /* Update records for the slots the non-options now occupy.  */

  first_nonopt += (optind - last_nonopt);
  last_nonopt = optind;
}

/* Initialize the internal data when the first call is made.  */

#if defined __STDC__ && __STDC__
static const char *_getopt_initialize (int, char *const *, const char *);
#endif
static const char *
_getopt_initialize (int argc, char *const *argv, const char *optstring)
{
  /* Start processing options with ARGV-element 1 (since ARGV-element 0
     is the program name); the sequence of previously skipped
     non-option ARGV-elements is empty.  */

  first_nonopt = last_nonopt = optind;

  nextchar = NULL;

  posixly_correct = getenv ("POSIXLY_CORRECT");

  /* Determine how to handle the ordering of options and nonoptions.  */

  if (optstring[0] == '-')
    {
      ordering = RETURN_IN_ORDER;
      ++optstring;
    }
  else if (optstring[0] == '+')
    {
      ordering = REQUIRE_ORDER;
      ++optstring;
    }
  else if (posixly_correct != NULL)
    ordering = REQUIRE_ORDER;
  else
    ordering = PERMUTE;

#ifdef _LIBC
  if (posixly_correct == NULL
      && argc == original_argc && argv == original_argv)
    {
      if (nonoption_flags_max_len == 0)
    {
      if (__getopt_nonoption_flags == NULL
          || __getopt_nonoption_flags[0] == '\0')
        nonoption_flags_max_len = -1;
      else
        {
          const char *orig_str = __getopt_nonoption_flags;
          int len = nonoption_flags_max_len = strlen (orig_str);
          if (nonoption_flags_max_len < argc)
        nonoption_flags_max_len = argc;
          __getopt_nonoption_flags =
        (char *) malloc (nonoption_flags_max_len);
          if (__getopt_nonoption_flags == NULL)
        nonoption_flags_max_len = -1;
          else
        memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
            '\0', nonoption_flags_max_len - len);
        }
    }
      nonoption_flags_len = nonoption_flags_max_len;
    }
  else
    nonoption_flags_len = 0;
#endif

  return optstring;
}


/* Scan elements of ARGV (whose length is ARGC) for option characters
   given in OPTSTRING.

   If an element of ARGV starts with '-', and is not exactly "-" or "--",
   then it is an option element.  The characters of this element
   (aside from the initial '-') are option characters.  If `getopt'
   is called repeatedly, it returns successively each of the option characters
   from each of the option elements.

   If `getopt' finds another option character, it returns that character,
   updating `optind' and `nextchar' so that the next call to `getopt' can
   resume the scan with the following option character or ARGV-element.

   If there are no more option characters, `getopt' returns -1.
   Then `optind' is the index in ARGV of the first ARGV-element
   that is not an option.  (The ARGV-elements have been permuted
   so that those that are not options now come last.)

   OPTSTRING is a string containing the legitimate option characters.
   If an option character is seen that is not listed in OPTSTRING,
   return '?' after printing an error message.  If you set `opterr' to
   zero, the error message is suppressed but we still return '?'.

   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
   so the following text in the same ARGV-element, or the text of the following
   ARGV-element, is returned in `optarg'.  Two colons mean an option that
   wants an optional arg; if there is text in the current ARGV-element,
   it is returned in `optarg', otherwise `optarg' is set to zero.

   If OPTSTRING starts with `-' or `+', it requests different methods of
   handling the non-option ARGV-elements.
   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.

   Long-named options begin with `--' instead of `-'.
   Their names may be abbreviated as long as the abbreviation is unique
   or is an exact match for some defined option.  If they have an
   argument, it follows the option name in the same ARGV-element, separated
   from the option name by a `=', or else the in next ARGV-element.
   When `getopt' finds a long-named option, it returns 0 if that option's
   `flag' field is nonzero, the value of the option's `val' field
   if the `flag' field is zero.

   The elements of ARGV aren't really const, because we permute them.
   But we pretend they're const in the prototype to be compatible
   with other systems.

   LONGOPTS is a vector of `struct option' terminated by an
   element containing a name which is zero.

   LONGIND returns the index in LONGOPT of the long-named option found.
   It is only valid when a long-named option has been found by the most
   recent call.

   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
   long-named options.  */

int
_getopt_internal (int argc, char *const *argv, const char *optstring,
                  const struct option *longopts, int *longind, int long_only)
{
  optarg = NULL;

  if (optind == 0 || !__getopt_initialized)
    {
      if (optind == 0)
    optind = 1; /* Don't scan ARGV[0], the program name.  */
      optstring = _getopt_initialize (argc, argv, optstring);
      __getopt_initialized = 1;
    }

  /* Test whether ARGV[optind] points to a non-option argument.
     Either it does not have option syntax, or there is an environment flag
     from the shell indicating it is not an option.  The later information
     is only used when the used in the GNU libc.  */
#ifdef _LIBC
# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'       \
              || (optind < nonoption_flags_len                \
              && __getopt_nonoption_flags[optind] == '1'))
#else
# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
#endif

  if (nextchar == NULL || *nextchar == '\0')
    {
      /* Advance to the next ARGV-element.  */

      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
     moved back by the user (who may also have changed the arguments).  */
      if (last_nonopt > optind)
    last_nonopt = optind;
      if (first_nonopt > optind)
    first_nonopt = optind;

      if (ordering == PERMUTE)
    {
      /* If we have just processed some options following some non-options,
         exchange them so that the options come first.  */

      if (first_nonopt != last_nonopt && last_nonopt != optind)
        exchange ((char **) argv);
      else if (last_nonopt != optind)
        first_nonopt = optind;

      /* Skip any additional non-options
         and extend the range of non-options previously skipped.  */

      while (optind < argc && NONOPTION_P)
        optind++;
      last_nonopt = optind;
    }

      /* The special ARGV-element `--' means premature end of options.
     Skip it like a null option,
     then exchange with previous non-options as if it were an option,
     then skip everything else like a non-option.  */

      if (optind != argc && !strcmp (argv[optind], "--"))
    {
      optind++;

      if (first_nonopt != last_nonopt && last_nonopt != optind)
        exchange ((char **) argv);
      else if (first_nonopt == last_nonopt)
        first_nonopt = optind;
      last_nonopt = argc;

      optind = argc;
    }

      /* If we have done all the ARGV-elements, stop the scan
     and back over any non-options that we skipped and permuted.  */

      if (optind == argc)
    {
      /* Set the next-arg-index to point at the non-options
         that we previously skipped, so the caller will digest them.  */
      if (first_nonopt != last_nonopt)
        optind = first_nonopt;
      return -1;
    }

      /* If we have come to a non-option and did not permute it,
     either stop the scan or describe it to the caller and pass it by.  */

      if (NONOPTION_P)
    {
      if (ordering == REQUIRE_ORDER)
        return -1;
      optarg = argv[optind++];
      return 1;
    }

      /* We have found another option-ARGV-element.
     Skip the initial punctuation.  */

      nextchar = (argv[optind] + 1
          + (longopts != NULL && argv[optind][1] == '-'));
    }

  /* Decode the current option-ARGV-element.  */

  /* Check whether the ARGV-element is a long option.

     If long_only and the ARGV-element has the form "-f", where f is
     a valid short option, don't consider it an abbreviated form of
     a long option that starts with f.  Otherwise there would be no
     way to give the -f short option.

     On the other hand, if there's a long option "fubar" and
     the ARGV-element is "-fu", do consider that an abbreviation of
     the long option, just like "--fu", and not "-f" with arg "u".

     This distinction seems to be the most useful approach.  */

  if (longopts != NULL
      && (argv[optind][1] == '-'
      || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
    {
      char *nameend;
      const struct option *p;
      const struct option *pfound = NULL;
      int exact = 0;
      int ambig = 0;
      int indfound = -1;
      int option_index;

      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
    /* Do nothing.  */ ;

      /* Test all long options for either exact match
     or abbreviated matches.  */
      for (p = longopts, option_index = 0; p->name; p++, option_index++)
    if (!strncmp (p->name, nextchar, nameend - nextchar))
      {
        if ((unsigned int) (nameend - nextchar)
        == (unsigned int) strlen (p->name))
          {
        /* Exact match found.  */
        pfound = p;
        indfound = option_index;
        exact = 1;
        break;
          }
        else if (pfound == NULL)
          {
        /* First nonexact match found.  */
        pfound = p;
        indfound = option_index;
          }
        else
          /* Second or later nonexact match found.  */
          ambig = 1;
      }

      if (ambig && !exact)
    {
      if (opterr)
        fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
             argv[0], argv[optind]);
      nextchar += strlen (nextchar);
      optind++;
      optopt = 0;
      return '?';
    }

      if (pfound != NULL)
    {
      option_index = indfound;
      optind++;
      if (*nameend)
        {
          /* Don't test has_arg with >, because some C compilers don't
         allow it to be used on enums.  */
          if (pfound->has_arg)
        optarg = nameend + 1;
          else
        {
          if (opterr)
           if (argv[optind - 1][1] == '-')
            /* --option */
            fprintf (stderr,
             _("%s: option `--%s' doesn't allow an argument\n"),
             argv[0], pfound->name);
           else
            /* +option or -option */
            fprintf (stderr,
             _("%s: option `%c%s' doesn't allow an argument\n"),
             argv[0], argv[optind - 1][0], pfound->name);

          nextchar += strlen (nextchar);

          optopt = pfound->val;
          return '?';
        }
        }
      else if (pfound->has_arg == 1)
        {
          if (optind < argc)
        optarg = argv[optind++];
          else
        {
          if (opterr)
            fprintf (stderr,
               _("%s: option `%s' requires an argument\n"),
               argv[0], argv[optind - 1]);
          nextchar += strlen (nextchar);
          optopt = pfound->val;
          return optstring[0] == ':' ? ':' : '?';
        }
        }
      nextchar += strlen (nextchar);
      if (longind != NULL)
        *longind = option_index;
      if (pfound->flag)
        {
          *(pfound->flag) = pfound->val;
          return 0;
        }
      return pfound->val;
    }

      /* Can't find it as a long option.  If this is not getopt_long_only,
     or the option starts with '--' or is not a valid short
     option, then it's an error.
     Otherwise interpret it as a short option.  */
      if (!long_only || argv[optind][1] == '-'
      || my_index (optstring, *nextchar) == NULL)
    {
      if (opterr)
        {
          if (argv[optind][1] == '-')
        /* --option */
        fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
             argv[0], nextchar);
          else
        /* +option or -option */
        fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
             argv[0], argv[optind][0], nextchar);
        }
      nextchar = (char *) "";
      optind++;
      optopt = 0;
      return '?';
    }
    }

  /* Look at and handle the next short option-character.  */

  {
    char c = *nextchar++;
    char *temp = my_index (optstring, c);

    /* Increment `optind' when we start to process its last character.  */
    if (*nextchar == '\0')
      ++optind;

    if (temp == NULL || c == ':')
      {
    if (opterr)
      {
        if (posixly_correct)
          /* 1003.2 specifies the format of this message.  */
          fprintf (stderr, _("%s: illegal option -- %c\n"),
               argv[0], c);
        else
          fprintf (stderr, _("%s: invalid option -- %c\n"),
               argv[0], c);
      }
    optopt = c;
    return '?';
      }
    /* Convenience. Treat POSIX -W foo same as long option --foo */
    if (temp[0] == 'W' && temp[1] == ';')
      {
    char *nameend;
    const struct option *p;
    const struct option *pfound = NULL;
    int exact = 0;
    int ambig = 0;
    int indfound = 0;
    int option_index;

    /* This is an option that requires an argument.  */
    if (*nextchar != '\0')
      {
        optarg = nextchar;
        /* If we end this ARGV-element by taking the rest as an arg,
           we must advance to the next element now.  */
        optind++;
      }
    else if (optind == argc)
      {
        if (opterr)
          {
        /* 1003.2 specifies the format of this message.  */
        fprintf (stderr, _("%s: option requires an argument -- %c\n"),
             argv[0], c);
          }
        optopt = c;
        if (optstring[0] == ':')
          c = ':';
        else
          c = '?';
        return c;
      }
    else
      /* We already incremented `optind' once;
         increment it again when taking next ARGV-elt as argument.  */
      optarg = argv[optind++];

    /* optarg is now the argument, see if it's in the
       table of longopts.  */

    for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
      /* Do nothing.  */ ;

    /* Test all long options for either exact match
       or abbreviated matches.  */
    for (p = longopts, option_index = 0; p->name; p++, option_index++)
      if (!strncmp (p->name, nextchar, nameend - nextchar))
        {
          if ((unsigned int) (nameend - nextchar) == strlen (p->name))
        {
          /* Exact match found.  */
          pfound = p;
          indfound = option_index;
          exact = 1;
          break;
        }
          else if (pfound == NULL)
        {
          /* First nonexact match found.  */
          pfound = p;
          indfound = option_index;
        }
          else
        /* Second or later nonexact match found.  */
        ambig = 1;
        }
    if (ambig && !exact)
      {
        if (opterr)
          fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
               argv[0], argv[optind]);
        nextchar += strlen (nextchar);
        optind++;
        return '?';
      }
    if (pfound != NULL)
      {
        option_index = indfound;
        if (*nameend)
          {
        /* Don't test has_arg with >, because some C compilers don't
           allow it to be used on enums.  */
        if (pfound->has_arg)
          optarg = nameend + 1;
        else
          {
            if (opterr)
              fprintf (stderr, _("\
%s: option `-W %s' doesn't allow an argument\n"),
                   argv[0], pfound->name);

            nextchar += strlen (nextchar);
            return '?';
          }
          }
        else if (pfound->has_arg == 1)
          {
        if (optind < argc)
          optarg = argv[optind++];
        else
          {
            if (opterr)
              fprintf (stderr,
                   _("%s: option `%s' requires an argument\n"),
                   argv[0], argv[optind - 1]);
            nextchar += strlen (nextchar);
            return optstring[0] == ':' ? ':' : '?';
          }
          }
        nextchar += strlen (nextchar);
        if (longind != NULL)
          *longind = option_index;
        if (pfound->flag)
          {
        *(pfound->flag) = pfound->val;
        return 0;
          }
        return pfound->val;
      }
      nextchar = NULL;
      return 'W';   /* Let the application handle it.   */
      }
    if (temp[1] == ':')
      {
    if (temp[2] == ':')
      {
        /* This is an option that accepts an argument optionally.  */
        if (*nextchar != '\0')
          {
        optarg = nextchar;
        optind++;
          }
        else
          optarg = NULL;
        nextchar = NULL;
      }
    else
      {
        /* This is an option that requires an argument.  */
        if (*nextchar != '\0')
          {
        optarg = nextchar;
        /* If we end this ARGV-element by taking the rest as an arg,
           we must advance to the next element now.  */
        optind++;
          }
        else if (optind == argc)
          {
        if (opterr)
          {
            /* 1003.2 specifies the format of this message.  */
            fprintf (stderr,
               _("%s: option requires an argument -- %c\n"),
               argv[0], c);
          }
        optopt = c;
        if (optstring[0] == ':')
          c = ':';
        else
          c = '?';
          }
        else
          /* We already incremented `optind' once;
         increment it again when taking next ARGV-elt as argument.  */
          optarg = argv[optind++];
        nextchar = NULL;
      }
      }
    return c;
  }
}

int
getopt (int argc, char *const *argv, const char *optstring)
{
  return _getopt_internal (argc, argv, optstring,
               (const struct option *) 0,
               (int *) 0,
               0);
}

#endif  /* Not ELIDE_CODE.  */


#ifdef TEST

/* Compile with -DTEST to make an executable for use in testing
   the above definition of `getopt'.  */

int
main (int argc, char **argv)
{
  int c;
  int digit_optind = 0;

  while (1)
    {
      int this_option_optind = optind ? optind : 1;

      c = getopt (argc, argv, "abc:d:0123456789");
      if (c == -1)
    break;

      switch (c)
    {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (digit_optind != 0 && digit_optind != this_option_optind)
        printf ("digits occur in two different argv-elements.\n");
      digit_optind = this_option_optind;
      printf ("option %c\n", c);
      break;

    case 'a':
      printf ("option a\n");
      break;

    case 'b':
      printf ("option b\n");
      break;

    case 'c':
      printf ("option c with value `%s'\n", optarg);
      break;

    case '?':
      break;

    default:
      printf ("?? getopt returned character code 0%o ??\n", c);
    }
    }

  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
    printf ("%s ", argv[optind++]);
      printf ("\n");
    }

  exit (0);
}

#endif /* TEST */

https://git.savannah.gnu.org/cgit/make.git/tree/getopt1.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/getopt1.c used in: Step 516
/* getopt_long and getopt_long_only entry points for GNU getopt.
Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.

NOTE: The canonical source of this file is maintained with the GNU C Library.
Bugs can be reported to bug-glibc@gnu.org.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "getopt.h"

#if !defined __STDC__ || !__STDC__
/* This is a separate conditional since some stdc systems
   reject `defined (const)'.  */
#ifndef const
#define const
#endif
#endif

#include <stdio.h>

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#define GETOPT_INTERFACE_VERSION 2
#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
#include <gnu-versions.h>
#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
#define ELIDE_CODE
#endif
#endif

#ifndef ELIDE_CODE


/* This needs to come after some library #include
   to get __GNU_LIBRARY__ defined.  */
#ifdef __GNU_LIBRARY__
#include <stdlib.h>
#endif

#ifndef NULL
#define NULL 0
#endif

int
getopt_long (int argc, char *const *argv, const char *options,
             const struct option *long_options, int *opt_index)
{
  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
}

/* Like getopt_long, but '-' as well as '--' can indicate a long option.
   If an option that starts with '-' (not '--') doesn't match a long option,
   but does match a short option, it is parsed as a short option
   instead.  */

int
getopt_long_only (int argc, char *const *argv, const char *options,
                  const struct option *long_options, int *opt_index)
{
  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
}


#endif  /* Not ELIDE_CODE.  */


#ifdef TEST

#include <stdio.h>

int
main (int argc, char **argv)
{
  int c;
  int digit_optind = 0;

  while (1)
    {
      int this_option_optind = optind ? optind : 1;
      int option_index = 0;
      static struct option long_options[] =
      {
    {"add", 1, 0, 0},
    {"append", 0, 0, 0},
    {"delete", 1, 0, 0},
    {"verbose", 0, 0, 0},
    {"create", 0, 0, 0},
    {"file", 1, 0, 0},
    {0, 0, 0, 0}
      };

      c = getopt_long (argc, argv, "abc:d:0123456789",
               long_options, &option_index);
      if (c == -1)
    break;

      switch (c)
    {
    case 0:
      printf ("option %s", long_options[option_index].name);
      if (optarg)
        printf (" with arg %s", optarg);
      printf ("\n");
      break;

    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (digit_optind != 0 && digit_optind != this_option_optind)
        printf ("digits occur in two different argv-elements.\n");
      digit_optind = this_option_optind;
      printf ("option %c\n", c);
      break;

    case 'a':
      printf ("option a\n");
      break;

    case 'b':
      printf ("option b\n");
      break;

    case 'c':
      printf ("option c with value `%s'\n", optarg);
      break;

    case 'd':
      printf ("option d with value `%s'\n", optarg);
      break;

    case '?':
      break;

    default:
      printf ("?? getopt returned character code 0%o ??\n", c);
    }
    }

  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
    printf ("%s ", argv[optind++]);
      printf ("\n");
    }

  exit (0);
}

#endif /* TEST */

https://git.savannah.gnu.org/cgit/make.git/tree/ar.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/ar.c used in: Step 517
/* Interface to `ar' archives for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.

This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"

#ifndef NO_ARCHIVES

#include "filedef.h"
#include "dep.h"
#include <fnmatch.h>

/* Return nonzero if NAME is an archive-member reference, zero if not.  An
   archive-member reference is a name like `lib(member)' where member is a
   non-empty string.
   If a name like `lib((entry))' is used, a fatal error is signaled at
   the attempt to use this unsupported feature.  */

int
ar_name (const char *name)
{
  const char *p = strchr (name, '(');
  const char *end;

  if (p == 0 || p == name)
    return 0;

  end = p + strlen (p) - 1;
  if (*end != ')' || end == p + 1)
    return 0;

  if (p[1] == '(' && end[-1] == ')')
    fatal (NILF, _("attempt to use unsupported feature: `%s'"), name);

  return 1;
}


/* Parse the archive-member reference NAME into the archive and member names.
   Creates one allocated string containing both names, pointed to by ARNAME_P.
   MEMNAME_P points to the member.  */

void
ar_parse_name (const char *name, char **arname_p, char **memname_p)
{
  char *p;

  *arname_p = xstrdup (name);
  p = strchr (*arname_p, '(');
  *(p++) = '\0';
  p[strlen(p) - 1] = '\0';
  *memname_p = p;
}



/* This function is called by `ar_scan' to find which member to look at.  */

/* ARGSUSED */
static long int
ar_member_date_1 (int desc UNUSED, const char *mem, int truncated,
          long int hdrpos UNUSED, long int datapos UNUSED,
                  long int size UNUSED, long int date,
                  int uid UNUSED, int gid UNUSED, int mode UNUSED,
          const void *name)
{
  return ar_name_equal (name, mem, truncated) ? date : 0;
}

/* Return the modtime of NAME.  */

time_t
ar_member_date (const char *name)
{
  char *arname;
  char *memname;
  long int val;

  ar_parse_name (name, &arname, &memname);

  /* Make sure we know the modtime of the archive itself because we are
     likely to be called just before commands to remake a member are run,
     and they will change the archive itself.

     But we must be careful not to enter_file the archive itself if it does
     not exist, because pattern_search assumes that files found in the data
     base exist or can be made.  */
  {
    struct file *arfile;
    arfile = lookup_file (arname);
    if (arfile == 0 && file_exists_p (arname))
      arfile = enter_file (strcache_add (arname));

    if (arfile != 0)
      (void) f_mtime (arfile, 0);
  }

  val = ar_scan (arname, ar_member_date_1, memname);

  free (arname);

  return (val <= 0 ? (time_t) -1 : (time_t) val);
}


/* Set the archive-member NAME's modtime to now.  */

#ifdef VMS
int
ar_touch (const char *name)
{
  error (NILF, _("touch archive member is not available on VMS"));
  return -1;
}
#else
int
ar_touch (const char *name)
{
  char *arname, *memname;
  int val;

  ar_parse_name (name, &arname, &memname);

  /* Make sure we know the modtime of the archive itself before we
     touch the member, since this will change the archive modtime.  */
  {
    struct file *arfile;
    arfile = enter_file (strcache_add (arname));
    f_mtime (arfile, 0);
  }

  val = 1;
  switch (ar_member_touch (arname, memname))
    {
    case -1:
      error (NILF, _("touch: Archive `%s' does not exist"), arname);
      break;
    case -2:
      error (NILF, _("touch: `%s' is not a valid archive"), arname);
      break;
    case -3:
      perror_with_name ("touch: ", arname);
      break;
    case 1:
      error (NILF,
             _("touch: Member `%s' does not exist in `%s'"), memname, arname);
      break;
    case 0:
      val = 0;
      break;
    default:
      error (NILF,
             _("touch: Bad return code from ar_member_touch on `%s'"), name);
    }

  free (arname);

  return val;
}
#endif /* !VMS */


/* State of an `ar_glob' run, passed to `ar_glob_match'.  */

struct ar_glob_state
  {
    const char *arname;
    const char *pattern;
    unsigned int size;
    struct nameseq *chain;
    unsigned int n;
  };

/* This function is called by `ar_scan' to match one archive
   element against the pattern in STATE.  */

static long int
ar_glob_match (int desc UNUSED, const char *mem, int truncated UNUSED,
           long int hdrpos UNUSED, long int datapos UNUSED,
               long int size UNUSED, long int date UNUSED, int uid UNUSED,
               int gid UNUSED, int mode UNUSED, const void *arg)
{
  struct ar_glob_state *state = (struct ar_glob_state *)arg;

  if (fnmatch (state->pattern, mem, FNM_PATHNAME|FNM_PERIOD) == 0)
    {
      /* We have a match.  Add it to the chain.  */
      struct nameseq *new = xcalloc (state->size);
      new->name = strcache_add (concat (4, state->arname, "(", mem, ")"));
      new->next = state->chain;
      state->chain = new;
      ++state->n;
    }

  return 0L;
}

/* Return nonzero if PATTERN contains any metacharacters.
   Metacharacters can be quoted with backslashes if QUOTE is nonzero.  */
static int
glob_pattern_p (const char *pattern, int quote)
{
  const char *p;
  int opened = 0;

  for (p = pattern; *p != '\0'; ++p)
    switch (*p)
      {
      case '?':
      case '*':
    return 1;

      case '\\':
    if (quote)
      ++p;
    break;

      case '[':
    opened = 1;
    break;

      case ']':
    if (opened)
      return 1;
    break;
      }

  return 0;
}

/* Glob for MEMBER_PATTERN in archive ARNAME.
   Return a malloc'd chain of matching elements (or nil if none).  */

struct nameseq *
ar_glob (const char *arname, const char *member_pattern, unsigned int size)
{
  struct ar_glob_state state;
  struct nameseq *n;
  const char **names;
  unsigned int i;

  if (! glob_pattern_p (member_pattern, 1))
    return 0;

  /* Scan the archive for matches.
     ar_glob_match will accumulate them in STATE.chain.  */
  state.arname = arname;
  state.pattern = member_pattern;
  state.size = size;
  state.chain = 0;
  state.n = 0;
  ar_scan (arname, ar_glob_match, &state);

  if (state.chain == 0)
    return 0;

  /* Now put the names into a vector for sorting.  */
  names = alloca (state.n * sizeof (const char *));
  i = 0;
  for (n = state.chain; n != 0; n = n->next)
    names[i++] = n->name;

  /* Sort them alphabetically.  */
  /* MSVC erroneously warns without a cast here.  */
  qsort ((void *)names, i, sizeof (*names), alpha_compare);

  /* Put them back into the chain in the sorted order.  */
  i = 0;
  for (n = state.chain; n != 0; n = n->next)
    n->name = names[i++];

  return state.chain;
}

#endif  /* Not NO_ARCHIVES.  */

https://git.savannah.gnu.org/cgit/make.git/tree/make.h?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/make.h used in: Step 517, Step 518, Step 519, Step 520, Step 521, Step 522, Step 523, Step 524, Step 525, Step 526, Step 527, Step 528, Step 529, Step 530, Step 531, Step 532, Step 533, Step 534, Step 536, Step 537, Step 538
/* Miscellaneous global declarations and portability cruft for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

/* We use <config.h> instead of "config.h" so that a compilation
   using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
   (which it would do because make.h was found in $srcdir).  */
#include <config.h>
#undef  HAVE_CONFIG_H
#define HAVE_CONFIG_H 1

/* Specify we want GNU source code.  This must be defined before any
   system headers are included.  */

#define _GNU_SOURCE 1

/* AIX requires this to be the first thing in the file.  */
#if HAVE_ALLOCA_H
# include <alloca.h>
#else
# ifdef _AIX
 #pragma alloca
# else
#  if !defined(__GNUC__) && !defined(WINDOWS32)
#   ifndef alloca /* predefined by HP cc +Olibcalls */
char *alloca ();
#   endif
#  endif
# endif
#endif


#ifdef  CRAY
/* This must happen before #include <signal.h> so
   that the declaration therein is changed.  */
# define signal bsdsignal
#endif

/* If we're compiling for the dmalloc debugger, turn off string inlining.  */
#if defined(HAVE_DMALLOC_H) && defined(__GNUC__)
# define __NO_STRING_INLINES
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdio.h>
#include <ctype.h>
#ifdef HAVE_SYS_TIMEB_H
/* SCO 3.2 "devsys 4.2" has a prototype for `ftime' in <time.h> that bombs
   unless <sys/timeb.h> has been included first.  Does every system have a
   <sys/timeb.h>?  If any does not, configure should check for it.  */
# include <sys/timeb.h>
#endif

#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif

#include <errno.h>

#ifndef errno
extern int errno;
#endif

#ifndef isblank
# define isblank(c)     ((c) == ' ' || (c) == '\t')
#endif

#ifdef  HAVE_UNISTD_H
# include <unistd.h>
/* Ultrix's unistd.h always defines _POSIX_VERSION, but you only get
   POSIX.1 behavior with `cc -YPOSIX', which predefines POSIX itself!  */
# if defined (_POSIX_VERSION) && !defined (ultrix) && !defined (VMS)
#  define POSIX 1
# endif
#endif

/* Some systems define _POSIX_VERSION but are not really POSIX.1.  */
#if (defined (butterfly) || defined (__arm) || (defined (__mips) && defined (_SYSTYPE_SVR3)) || (defined (sequent) && defined (i386)))
# undef POSIX
#endif

#if !defined (POSIX) && defined (_AIX) && defined (_POSIX_SOURCE)
# define POSIX 1
#endif

#ifndef RETSIGTYPE
# define RETSIGTYPE     void
#endif

#ifndef sigmask
# define sigmask(sig)   (1 << ((sig) - 1))
#endif

#ifndef HAVE_SA_RESTART
# define SA_RESTART 0
#endif

#ifdef  HAVE_LIMITS_H
# include <limits.h>
#endif
#ifdef  HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif

#ifndef PATH_MAX
# ifndef POSIX
#  define PATH_MAX      MAXPATHLEN
# endif
#endif
#ifndef MAXPATHLEN
# define MAXPATHLEN 1024
#endif

#ifdef  PATH_MAX
# define GET_PATH_MAX   PATH_MAX
# define PATH_VAR(var)  char var[PATH_MAX]
#else
# define NEED_GET_PATH_MAX 1
# define GET_PATH_MAX   (get_path_max ())
# define PATH_VAR(var)  char *var = alloca (GET_PATH_MAX)
unsigned int get_path_max (void);
#endif

#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif

/* Nonzero if the integer type T is signed.  */
#define INTEGER_TYPE_SIGNED(t) ((t) -1 < 0)

/* The minimum and maximum values for the integer type T.
   Use ~ (t) 0, not -1, for portability to 1's complement hosts.  */
#define INTEGER_TYPE_MINIMUM(t) \
  (! INTEGER_TYPE_SIGNED (t) ? (t) 0 : ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1))
#define INTEGER_TYPE_MAXIMUM(t) (~ (t) 0 - INTEGER_TYPE_MINIMUM (t))

#ifndef CHAR_MAX
# define CHAR_MAX INTEGER_TYPE_MAXIMUM (char)
#endif

#ifdef STAT_MACROS_BROKEN
# ifdef S_ISREG
#  undef S_ISREG
# endif
# ifdef S_ISDIR
#  undef S_ISDIR
# endif
#endif  /* STAT_MACROS_BROKEN.  */

#ifndef S_ISREG
# define S_ISREG(mode)  (((mode) & S_IFMT) == S_IFREG)
#endif
#ifndef S_ISDIR
# define S_ISDIR(mode)  (((mode) & S_IFMT) == S_IFDIR)
#endif

#ifdef VMS
# include <types.h>
# include <unixlib.h>
# include <unixio.h>
# include <perror.h>
/* Needed to use alloca on VMS.  */
# include <builtins.h>
#endif

#ifndef __attribute__
/* This feature is available in gcc versions 2.5 and later.  */
# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || __STRICT_ANSI__
#  define __attribute__(x)
# endif
/* The __-protected variants of `format' and `printf' attributes
   are accepted by gcc versions 2.6.4 (effectively 2.7) and later.  */
# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
#  define __format__ format
#  define __printf__ printf
# endif
#endif
#define UNUSED  __attribute__ ((unused))

#if defined (STDC_HEADERS) || defined (__GNU_LIBRARY__)
# include <stdlib.h>
# include <string.h>
# define ANSI_STRING 1
#else   /* No standard headers.  */
# ifdef HAVE_STRING_H
#  include <string.h>
#  define ANSI_STRING 1
# else
#  include <strings.h>
# endif
# ifdef HAVE_MEMORY_H
#  include <memory.h>
# endif
# ifdef HAVE_STDLIB_H
#  include <stdlib.h>
# else
void *malloc (int);
void *realloc (void *, int);
void free (void *);

void abort (void) __attribute__ ((noreturn));
void exit (int) __attribute__ ((noreturn));
# endif /* HAVE_STDLIB_H.  */

#endif /* Standard headers.  */

/* These should be in stdlib.h.  Make sure we have them.  */
#ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 0
#endif
#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif

#ifndef  ANSI_STRING

/* SCO Xenix has a buggy macro definition in <string.h>.  */
#undef  strerror
#if !defined(__DECC)
char *strerror (int errnum);
#endif

#endif  /* !ANSI_STRING.  */
#undef  ANSI_STRING

#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#define FILE_TIMESTAMP uintmax_t

#if !defined(HAVE_STRSIGNAL)
char *strsignal (int signum);
#endif

/* ISDIGIT offers the following features:
   - Its arg may be any int or unsigned int; it need not be an unsigned char.
   - It's guaranteed to evaluate its argument exactly once.
      NOTE!  Make relies on this behavior, don't change it!
   - It's typically faster.
   POSIX 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
   only '0' through '9' are digits.  Prefer ISDIGIT to isdigit() unless
   it's important to use the locale's definition of `digit' even when the
   host does not conform to POSIX.  */
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)

/* Test if two strings are equal. Is this worthwhile?  Should be profiled.  */
#define streq(a, b) \
   ((a) == (b) || \
    (*(a) == *(b) && (*(a) == '\0' || !strcmp ((a) + 1, (b) + 1))))

/* Test if two strings are equal, but match case-insensitively on systems
   which have case-insensitive filesystems.  Should only be used for
   filenames!  */
#ifdef HAVE_CASE_INSENSITIVE_FS
# define patheq(a, b) \
    ((a) == (b) \
     || (tolower((unsigned char)*(a)) == tolower((unsigned char)*(b)) \
         && (*(a) == '\0' || !strcasecmp ((a) + 1, (b) + 1))))
#else
# define patheq(a, b) streq(a, b)
#endif

#define strneq(a, b, l) (strncmp ((a), (b), (l)) == 0)

#if defined(__GNUC__) || defined(ENUM_BITFIELDS)
# define ENUM_BITFIELD(bits)    :bits
#else
# define ENUM_BITFIELD(bits)
#endif

/* Handle gettext and locales.  */

#if HAVE_LOCALE_H
# include <locale.h>
#else
# define setlocale(category, locale)
#endif

#include <gettext.h>

#define _(msgid)            gettext (msgid)
#define N_(msgid)           gettext_noop (msgid)
#define S_(msg1,msg2,num)   ngettext (msg1,msg2,num)

/* Handle other OSs.  */
#ifndef PATH_SEPARATOR_CHAR
# if defined(HAVE_DOS_PATHS)
#  define PATH_SEPARATOR_CHAR ';'
# elif defined(VMS)
#  define PATH_SEPARATOR_CHAR ','
# else
#  define PATH_SEPARATOR_CHAR ':'
# endif
#endif

/* This is needed for getcwd() and chdir(), on some W32 systems.  */
#if defined(HAVE_DIRECT_H)
# include <direct.h>
#endif

#ifdef WINDOWS32
# include <fcntl.h>
# include <malloc.h>
# define pipe(_p)        _pipe((_p), 512, O_BINARY)
# define kill(_pid,_sig) w32_kill((_pid),(_sig))

void sync_Path_environment (void);
int w32_kill (pid_t pid, int sig);
char *end_of_token_w32 (const char *s, char stopchar);
int find_and_set_default_shell (const char *token);

/* indicates whether or not we have Bourne shell */
extern int no_default_sh_exe;

/* is default_shell unixy? */
extern int unixy_shell;
#endif  /* WINDOWS32 */

#if defined(HAVE_SYS_RESOURCE_H) && defined(HAVE_GETRLIMIT) && defined(HAVE_SETRLIMIT)
# define SET_STACK_SIZE
#endif
#ifdef SET_STACK_SIZE
# include <sys/resource.h>
struct rlimit stack_limit;
#endif

struct floc
  {
    const char *filenm;
    unsigned long lineno;
  };
#define NILF ((struct floc *)0)

#define STRING_SIZE_TUPLE(_s) (_s), (sizeof (_s)-1)



/* We have to have stdarg.h or varargs.h AND v*printf or doprnt to use
   variadic versions of these functions.  */

#if HAVE_STDARG_H || HAVE_VARARGS_H
# if HAVE_VPRINTF || HAVE_DOPRNT
#  define USE_VARIADIC 1
# endif
#endif

#if HAVE_ANSI_COMPILER && USE_VARIADIC && HAVE_STDARG_H
const char *concat (unsigned int, ...);
void message (int prefix, const char *fmt, ...)
              __attribute__ ((__format__ (__printf__, 2, 3)));
void error (const struct floc *flocp, const char *fmt, ...)
            __attribute__ ((__format__ (__printf__, 2, 3)));
void fatal (const struct floc *flocp, const char *fmt, ...)
                   __attribute__ ((noreturn, __format__ (__printf__, 2, 3)));
#else
const char *concat ();
void message ();
void error ();
void fatal ();
#endif

void die (int) __attribute__ ((noreturn));
void log_working_directory (int);
void pfatal_with_name (const char *) __attribute__ ((noreturn));
void perror_with_name (const char *, const char *);
void *xmalloc (unsigned int);
void *xcalloc (unsigned int);
void *xrealloc (void *, unsigned int);
char *xstrdup (const char *);
char *xstrndup (const char *, unsigned int);
char *find_next_token (const char **, unsigned int *);
char *next_token (const char *);
char *end_of_token (const char *);
void collapse_continuations (char *);
char *lindex (const char *, const char *, int);
int alpha_compare (const void *, const void *);
void print_spaces (unsigned int);
char *find_percent (char *);
const char *find_percent_cached (const char **);
FILE *open_tmpfile (char **, const char *);

#ifndef NO_ARCHIVES
int ar_name (const char *);
void ar_parse_name (const char *, char **, char **);
int ar_touch (const char *);
time_t ar_member_date (const char *);

typedef long int (*ar_member_func_t) (int desc, const char *mem, int truncated,
                      long int hdrpos, long int datapos,
                      long int size, long int date, int uid,
                      int gid, int mode, const void *arg);

long int ar_scan (const char *archive, ar_member_func_t function, const void *arg);
int ar_name_equal (const char *name, const char *mem, int truncated);
#ifndef VMS
int ar_member_touch (const char *arname, const char *memname);
#endif
#endif

int dir_file_exists_p (const char *, const char *);
int file_exists_p (const char *);
int file_impossible_p (const char *);
void file_impossible (const char *);
const char *dir_name (const char *);
void hash_init_directories (void);

void define_default_variables (void);
void set_default_suffixes (void);
void install_default_suffix_rules (void);
void install_default_implicit_rules (void);

void build_vpath_lists (void);
void construct_vpath_list (char *pattern, char *dirpath);
const char *vpath_search (const char *file, FILE_TIMESTAMP *mtime_ptr,
                          unsigned int* vpath_index, unsigned int* path_index);
int gpath_search (const char *file, unsigned int len);

void construct_include_path (const char **arg_dirs);

void user_access (void);
void make_access (void);
void child_access (void);

void close_stdout (void);

char *strip_whitespace (const char **begpp, const char **endpp);

/* String caching  */
void strcache_init (void);
void strcache_print_stats (const char *prefix);
int strcache_iscached (const char *str);
const char *strcache_add (const char *str);
const char *strcache_add_len (const char *str, int len);
int strcache_setbufsize (int size);

#ifdef  HAVE_VFORK_H
# include <vfork.h>
#endif

/* We omit these declarations on non-POSIX systems which define _POSIX_VERSION,
   because such systems often declare them in header files anyway.  */

#if !defined (__GNU_LIBRARY__) && !defined (POSIX) && !defined (_POSIX_VERSION) && !defined(WINDOWS32)

long int atol ();
# ifndef VMS
long int lseek ();
# endif

#endif  /* Not GNU C library or POSIX.  */

#ifdef  HAVE_GETCWD
# if !defined(VMS) && !defined(__DECC)
char *getcwd ();
# endif
#else
char *getwd ();
# define getcwd(buf, len)       getwd (buf)
#endif

#if !HAVE_STRCASECMP
# if HAVE_STRICMP
#  define strcasecmp stricmp
# elif HAVE_STRCMPI
#  define strcasecmp strcmpi
# else
/* Create our own, in misc.c */
int strcasecmp (const char *s1, const char *s2);
# endif
#endif

#if !HAVE_STRNCASECMP
# if HAVE_STRNICMP
#  define strncasecmp strnicmp
# elif HAVE_STRNCMPI
#  define strncasecmp strncmpi
# else
/* Create our own, in misc.c */
int strncasecmp (const char *s1, const char *s2, int n);
# endif
#endif

extern const struct floc *reading_file;
extern const struct floc **expanding_var;

extern char **environ;

extern int just_print_flag, silent_flag, ignore_errors_flag, keep_going_flag;
extern int print_data_base_flag, question_flag, touch_flag, always_make_flag;
extern int env_overrides, no_builtin_rules_flag, no_builtin_variables_flag;
extern int print_version_flag, print_directory_flag, check_symlink_flag;
extern int warn_undefined_variables_flag, posix_pedantic, not_parallel;
extern int second_expansion, clock_skew_detected, rebuilding_makefiles;
extern int one_shell;

/* can we run commands via 'sh -c xxx' or must we use batch files? */
extern int batch_mode_shell;

/* Resetting the command script introduction prefix character.  */
#define RECIPEPREFIX_NAME          ".RECIPEPREFIX"
#define RECIPEPREFIX_DEFAULT       '\t'
extern char cmd_prefix;

extern unsigned int job_slots;
extern int job_fds[2];
extern int job_rfd;
#ifndef NO_FLOAT
extern double max_load_average;
#else
extern int max_load_average;
#endif

extern char *program;
extern char *starting_directory;
extern unsigned int makelevel;
extern char *version_string, *remote_description, *make_host;

extern unsigned int commands_started;

extern int handling_fatal_signal;


#ifndef MIN
#define MIN(_a,_b) ((_a)<(_b)?(_a):(_b))
#endif
#ifndef MAX
#define MAX(_a,_b) ((_a)>(_b)?(_a):(_b))
#endif

#ifdef VMS
#  define MAKE_SUCCESS 1
#  define MAKE_TROUBLE 2
#  define MAKE_FAILURE 3
#else
#  define MAKE_SUCCESS 0
#  define MAKE_TROUBLE 1
#  define MAKE_FAILURE 2
#endif

/* Set up heap debugging library dmalloc.  */

#ifdef HAVE_DMALLOC_H
#include <dmalloc.h>
#endif

#ifndef initialize_main
# ifdef __EMX__
#  define initialize_main(pargc, pargv) \
                          { _wildcard(pargc, pargv); _response(pargc, pargv); }
# else
#  define initialize_main(pargc, pargv)
# endif
#endif

#ifdef __EMX__
# if !defined chdir
#  define chdir _chdir2
# endif
# if !defined getcwd
#  define getcwd _getcwd2
# endif

/* NO_CHDIR2 causes make not to use _chdir2() and _getcwd2() instead of
   chdir() and getcwd(). This avoids some error messages for the
   make testsuite but restricts the drive letter support. */
# ifdef NO_CHDIR2
#  warning NO_CHDIR2: usage of drive letters restricted
#  undef chdir
#  undef getcwd
# endif
#endif

#ifndef initialize_main
# define initialize_main(pargc, pargv)
#endif


/* Some systems (like Solaris, PTX, etc.) do not support the SA_RESTART flag
   properly according to POSIX.  So, we try to wrap common system calls with
   checks for EINTR.  Note that there are still plenty of system calls that
   can fail with EINTR but this, reportedly, gets the vast majority of
   failure cases.  If you still experience failures you'll need to either get
   a system where SA_RESTART works, or you need to avoid -j.  */

#define EINTRLOOP(_v,_c)   while (((_v)=_c)==-1 && errno==EINTR)

/* While system calls that return integers are pretty consistent about
   returning -1 on failure and setting errno in that case, functions that
   return pointers are not always so well behaved.  Sometimes they return
   NULL for expected behavior: one good example is readdir() which returns
   NULL at the end of the directory--and _doesn't_ reset errno.  So, we have
   to do it ourselves here.  */

#define ENULLLOOP(_v,_c)   do { errno = 0; (_v) = _c; } \
                           while((_v)==0 && errno==EINTR)

https://git.savannah.gnu.org/cgit/make.git/tree/filedef.h?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/filedef.h used in: Step 517, Step 519, Step 520, Step 522, Step 523, Step 524, Step 525, Step 526, Step 527, Step 529, Step 530, Step 531, Step 534, Step 536, Step 538
/* Definition of target file data structures for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */


/* Structure that represents the info on one file
   that the makefile says how to make.
   All of these are chained together through `next'.  */

#include "hash.h"

struct file
  {
    const char *name;
    const char *hname;          /* Hashed filename */
    const char *vpath;          /* VPATH/vpath pathname */
    struct dep *deps;       /* all dependencies, including duplicates */
    struct commands *cmds;  /* Commands to execute for this target.  */
    int command_flags;      /* Flags OR'd in for cmds; see commands.h.  */
    const char *stem;       /* Implicit stem, if an implicit
                                   rule has been used */
    struct dep *also_make;  /* Targets that are made by making this.  */
    FILE_TIMESTAMP last_mtime;  /* File's modtime, if already known.  */
    FILE_TIMESTAMP mtime_before_update; /* File's modtime before any updating
                                           has been performed.  */
    struct file *prev;      /* Previous entry for same file name;
                   used when there are multiple double-colon
                   entries for the same file.  */
    struct file *last;          /* Last entry for the same file name.  */

    /* File that this file was renamed to.  After any time that a
       file could be renamed, call `check_renamed' (below).  */
    struct file *renamed;

    /* List of variable sets used for this file.  */
    struct variable_set_list *variables;

    /* Pattern-specific variable reference for this target, or null if there
       isn't one.  Also see the pat_searched flag, below.  */
    struct variable_set_list *pat_variables;

    /* Immediate dependent that caused this target to be remade,
       or nil if there isn't one.  */
    struct file *parent;

    /* For a double-colon entry, this is the first double-colon entry for
       the same file.  Otherwise this is null.  */
    struct file *double_colon;

    short int update_status;    /* Status of the last attempt to update,
                   or -1 if none has been made.  */

    enum cmd_state      /* State of the commands.  */
      {     /* Note: It is important that cs_not_started be zero.  */
    cs_not_started,     /* Not yet started.  */
    cs_deps_running,    /* Dep commands running.  */
    cs_running,     /* Commands running.  */
    cs_finished     /* Commands finished.  */
      } command_state ENUM_BITFIELD (2);

    unsigned int precious:1;    /* Non-0 means don't delete file on quit */
    unsigned int low_resolution_time:1; /* Nonzero if this file's time stamp
                       has only one-second resolution.  */
    unsigned int tried_implicit:1; /* Nonzero if have searched
                      for implicit rule for making
                      this file; don't search again.  */
    unsigned int updating:1;    /* Nonzero while updating deps of this file */
    unsigned int updated:1; /* Nonzero if this file has been remade.  */
    unsigned int is_target:1;   /* Nonzero if file is described as target.  */
    unsigned int cmd_target:1;  /* Nonzero if file was given on cmd line.  */
    unsigned int phony:1;   /* Nonzero if this is a phony file
                   i.e., a prerequisite of .PHONY.  */
    unsigned int intermediate:1;/* Nonzero if this is an intermediate file.  */
    unsigned int secondary:1;   /* Nonzero means remove_intermediates should
                                   not delete it.  */
    unsigned int dontcare:1;    /* Nonzero if no complaint is to be made if
                   this target cannot be remade.  */
    unsigned int ignore_vpath:1;/* Nonzero if we threw out VPATH name.  */
    unsigned int pat_searched:1;/* Nonzero if we already searched for
                                   pattern-specific variables.  */
    unsigned int considered:1;  /* equal to 'considered' if file has been
                                   considered on current scan of goal chain */
    unsigned int no_diag:1;     /* True if the file failed to update and no
                                   diagnostics has been issued (dontcare). */
  };


extern struct file *suffix_file, *default_file;


struct file *lookup_file (const char *name);
struct file *enter_file (const char *name);
struct dep *split_prereqs (char *prereqstr);
struct dep *enter_prereqs (struct dep *prereqs, const char *stem);
void remove_intermediates (int sig);
void snap_deps (void);
void rename_file (struct file *file, const char *name);
void rehash_file (struct file *file, const char *name);
void set_command_state (struct file *file, enum cmd_state state);
void notice_finished_file (struct file *file);
void init_hash_files (void);
char *build_target_list (char *old_list);
void print_prereqs (const struct dep *deps);
void print_file_data_base (void);

#if FILE_TIMESTAMP_HI_RES
# define FILE_TIMESTAMP_STAT_MODTIME(fname, st) \
    file_timestamp_cons (fname, (st).st_mtime, (st).st_mtim.ST_MTIM_NSEC)
#else
# define FILE_TIMESTAMP_STAT_MODTIME(fname, st) \
    file_timestamp_cons (fname, (st).st_mtime, 0)
#endif

/* If FILE_TIMESTAMP is 64 bits (or more), use nanosecond resolution.
   (Multiply by 2**30 instead of by 10**9 to save time at the cost of
   slightly decreasing the number of available timestamps.)  With
   64-bit FILE_TIMESTAMP, this stops working on 2514-05-30 01:53:04
   UTC, but by then uintmax_t should be larger than 64 bits.  */
#define FILE_TIMESTAMPS_PER_S (FILE_TIMESTAMP_HI_RES ? 1000000000 : 1)
#define FILE_TIMESTAMP_LO_BITS (FILE_TIMESTAMP_HI_RES ? 30 : 0)

#define FILE_TIMESTAMP_S(ts) (((ts) - ORDINARY_MTIME_MIN) \
                  >> FILE_TIMESTAMP_LO_BITS)
#define FILE_TIMESTAMP_NS(ts) ((int) (((ts) - ORDINARY_MTIME_MIN) \
                      & ((1 << FILE_TIMESTAMP_LO_BITS) - 1)))

/* Upper bound on length of string "YYYY-MM-DD HH:MM:SS.NNNNNNNNN"
   representing a file timestamp.  The upper bound is not necessarily 19,
   since the year might be less than -999 or greater than 9999.

   Subtract one for the sign bit if in case file timestamps can be negative;
   subtract FLOOR_LOG2_SECONDS_PER_YEAR to yield an upper bound on how many
   file timestamp bits might affect the year;
   302 / 1000 is log10 (2) rounded up;
   add one for integer division truncation;
   add one more for a minus sign if file timestamps can be negative;
   add 4 to allow for any 4-digit epoch year (e.g. 1970);
   add 25 to allow for "-MM-DD HH:MM:SS.NNNNNNNNN".  */
#define FLOOR_LOG2_SECONDS_PER_YEAR 24
#define FILE_TIMESTAMP_PRINT_LEN_BOUND \
  (((sizeof (FILE_TIMESTAMP) * CHAR_BIT - 1 - FLOOR_LOG2_SECONDS_PER_YEAR) \
    * 302 / 1000) \
   + 1 + 1 + 4 + 25)

FILE_TIMESTAMP file_timestamp_cons (char const *, time_t, int);
FILE_TIMESTAMP file_timestamp_now (int *);
void file_timestamp_sprintf (char *p, FILE_TIMESTAMP ts);

/* Return the mtime of file F (a struct file *), caching it.
   The value is NONEXISTENT_MTIME if the file does not exist.  */
#define file_mtime(f) file_mtime_1 ((f), 1)
/* Return the mtime of file F (a struct file *), caching it.
   Don't search using vpath for the file--if it doesn't actually exist,
   we don't find it.
   The value is NONEXISTENT_MTIME if the file does not exist.  */
#define file_mtime_no_search(f) file_mtime_1 ((f), 0)
FILE_TIMESTAMP f_mtime (struct file *file, int search);
#define file_mtime_1(f, v) \
  ((f)->last_mtime == UNKNOWN_MTIME ? f_mtime ((f), v) : (f)->last_mtime)

/* Special timestamp values.  */

/* The file's timestamp is not yet known.  */
#define UNKNOWN_MTIME 0

/* The file does not exist.  */
#define NONEXISTENT_MTIME 1

/* The file does not exist, and we assume that it is older than any
   actual file.  */
#define OLD_MTIME 2

/* The smallest and largest ordinary timestamps.  */
#define ORDINARY_MTIME_MIN (OLD_MTIME + 1)
#define ORDINARY_MTIME_MAX ((FILE_TIMESTAMP_S (NEW_MTIME) \
                 << FILE_TIMESTAMP_LO_BITS) \
                + ORDINARY_MTIME_MIN + FILE_TIMESTAMPS_PER_S - 1)

/* Modtime value to use for `infinitely new'.  We used to get the current time
   from the system and use that whenever we wanted `new'.  But that causes
   trouble when the machine running make and the machine holding a file have
   different ideas about what time it is; and can also lose for `force'
   targets, which need to be considered newer than anything that depends on
   them, even if said dependents' modtimes are in the future.  */
#define NEW_MTIME INTEGER_TYPE_MAXIMUM (FILE_TIMESTAMP)

#define check_renamed(file) \
  while ((file)->renamed != 0) (file) = (file)->renamed /* No ; here.  */

/* Have we snapped deps yet?  */
extern int snapped_deps;

https://git.savannah.gnu.org/cgit/make.git/tree/hash.h?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/hash.h used in: Step 517, Step 519, Step 520, Step 521, Step 522, Step 523, Step 524, Step 525, Step 526, Step 527, Step 529, Step 530, Step 531, Step 533, Step 534, Step 536, Step 537, Step 538
/* hash.h -- decls for hash table
Copyright (C) 1995, 1999, 2002, 2010 Free Software Foundation, Inc.
Written by Greg McGary <gkm@gnu.org> <greg@mcgary.org>

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#ifndef _hash_h_
#define _hash_h_

#include <stdio.h>
#include <ctype.h>

#if defined __cplusplus || (defined __STDC__ && __STDC__) || defined WINDOWS32
# if !defined __GLIBC__ || !defined __P
#  undef    __P
#  define __P(protos)   protos
# endif
#else /* Not C++ or ANSI C.  */
# undef __P
# define __P(protos)    ()
/* We can get away without defining `const' here only because in this file
   it is used only inside the prototype for `fnmatch', which is elided in
   non-ANSI C where `const' is problematical.  */
#endif /* C++ or ANSI C.  */

typedef unsigned long (*hash_func_t) __P((void const *key));
typedef int (*hash_cmp_func_t) __P((void const *x, void const *y));
typedef void (*hash_map_func_t) __P((void const *item));
typedef void (*hash_map_arg_func_t) __P((void const *item, void *arg));

struct hash_table
{
  void **ht_vec;
  unsigned long ht_size;    /* total number of slots (power of 2) */
  unsigned long ht_capacity;    /* usable slots, limited by loading-factor */
  unsigned long ht_fill;    /* items in table */
  unsigned long ht_empty_slots; /* empty slots not including deleted slots */
  unsigned long ht_collisions;  /* # of failed calls to comparison function */
  unsigned long ht_lookups; /* # of queries */
  unsigned int ht_rehashes; /* # of times we've expanded table */
  hash_func_t ht_hash_1;    /* primary hash function */
  hash_func_t ht_hash_2;    /* secondary hash function */
  hash_cmp_func_t ht_compare;   /* comparison function */
};

typedef int (*qsort_cmp_t) __P((void const *, void const *));

void hash_init __P((struct hash_table *ht, unsigned long size,
            hash_func_t hash_1, hash_func_t hash_2, hash_cmp_func_t hash_cmp));
void hash_load __P((struct hash_table *ht, void *item_table,
            unsigned long cardinality, unsigned long size));
void **hash_find_slot __P((struct hash_table *ht, void const *key));
void *hash_find_item __P((struct hash_table *ht, void const *key));
void *hash_insert __P((struct hash_table *ht, const void *item));
void *hash_insert_at __P((struct hash_table *ht, const void *item, void const *slot));
void *hash_delete __P((struct hash_table *ht, void const *item));
void *hash_delete_at __P((struct hash_table *ht, void const *slot));
void hash_delete_items __P((struct hash_table *ht));
void hash_free_items __P((struct hash_table *ht));
void hash_free __P((struct hash_table *ht, int free_items));
void hash_map __P((struct hash_table *ht, hash_map_func_t map));
void hash_map_arg __P((struct hash_table *ht, hash_map_arg_func_t map, void *arg));
void hash_print_stats __P((struct hash_table *ht, FILE *out_FILE));
void **hash_dump __P((struct hash_table *ht, void **vector_0, qsort_cmp_t compare));

extern void *hash_deleted_item;
#define HASH_VACANT(item) ((item) == 0 || (void *) (item) == hash_deleted_item)



/* hash and comparison macros for case-sensitive string keys. */

/* Due to the strcache, it's not uncommon for the string pointers to
   be identical.  Take advantage of that to short-circuit string compares.  */

#define STRING_HASH_1(KEY, RESULT) do { \
  unsigned char const *_key_ = (unsigned char const *) (KEY) - 1; \
  while (*++_key_) \
    (RESULT) += (*_key_ << (_key_[1] & 0xf)); \
} while (0)
#define return_STRING_HASH_1(KEY) do { \
  unsigned long _result_ = 0; \
  STRING_HASH_1 ((KEY), _result_); \
  return _result_; \
} while (0)

#define STRING_HASH_2(KEY, RESULT) do { \
  unsigned char const *_key_ = (unsigned char const *) (KEY) - 1; \
  while (*++_key_) \
    (RESULT) += (*_key_ << (_key_[1] & 0x7)); \
} while (0)
#define return_STRING_HASH_2(KEY) do { \
  unsigned long _result_ = 0; \
  STRING_HASH_2 ((KEY), _result_); \
  return _result_; \
} while (0)

#define STRING_COMPARE(X, Y, RESULT) do { \
    RESULT = (X) == (Y) ? 0 : strcmp ((X), (Y)); \
} while (0)
#define return_STRING_COMPARE(X, Y) do { \
  return (X) == (Y) ? 0 : strcmp ((X), (Y)); \
} while (0)


#define STRING_N_HASH_1(KEY, N, RESULT) do { \
  unsigned char const *_key_ = (unsigned char const *) (KEY) - 1; \
  int _n_ = (N); \
  if (_n_) \
    while (--_n_ && *++_key_) \
      (RESULT) += (*_key_ << (_key_[1] & 0xf)); \
  (RESULT) += *++_key_; \
} while (0)
#define return_STRING_N_HASH_1(KEY, N) do { \
  unsigned long _result_ = 0; \
  STRING_N_HASH_1 ((KEY), (N), _result_); \
  return _result_; \
} while (0)

#define STRING_N_HASH_2(KEY, N, RESULT) do { \
  unsigned char const *_key_ = (unsigned char const *) (KEY) - 1; \
  int _n_ = (N); \
  if (_n_) \
    while (--_n_ && *++_key_) \
      (RESULT) += (*_key_ << (_key_[1] & 0x7)); \
  (RESULT) += *++_key_; \
} while (0)
#define return_STRING_N_HASH_2(KEY, N) do { \
  unsigned long _result_ = 0; \
  STRING_N_HASH_2 ((KEY), (N), _result_); \
  return _result_; \
} while (0)

#define STRING_N_COMPARE(X, Y, N, RESULT) do { \
  RESULT = (X) == (Y) ? 0 : strncmp ((X), (Y), (N)); \
} while (0)
#define return_STRING_N_COMPARE(X, Y, N) do { \
  return (X) == (Y) ? 0 : strncmp ((X), (Y), (N)); \
} while (0)

#ifdef HAVE_CASE_INSENSITIVE_FS

/* hash and comparison macros for case-insensitive string _key_s. */

#define ISTRING_HASH_1(KEY, RESULT) do { \
  unsigned char const *_key_ = (unsigned char const *) (KEY) - 1; \
  while (*++_key_) \
    (RESULT) += ((isupper (*_key_) ? tolower (*_key_) : *_key_) << (_key_[1] & 0xf)); \
} while (0)
#define return_ISTRING_HASH_1(KEY) do { \
  unsigned long _result_ = 0; \
  ISTRING_HASH_1 ((KEY), _result_); \
  return _result_; \
} while (0)

#define ISTRING_HASH_2(KEY, RESULT) do { \
  unsigned char const *_key_ = (unsigned char const *) (KEY) - 1; \
  while (*++_key_) \
    (RESULT) += ((isupper (*_key_) ? tolower (*_key_) : *_key_) << (_key_[1] & 0x7)); \
} while (0)
#define return_ISTRING_HASH_2(KEY) do { \
  unsigned long _result_ = 0; \
  ISTRING_HASH_2 ((KEY), _result_); \
  return _result_; \
} while (0)

#define ISTRING_COMPARE(X, Y, RESULT) do { \
  RESULT = (X) == (Y) ? 0 : strcasecmp ((X), (Y)); \
} while (0)
#define return_ISTRING_COMPARE(X, Y) do { \
  return (X) == (Y) ? 0 : strcasecmp ((X), (Y)); \
} while (0)

#else

#define ISTRING_HASH_1(KEY, RESULT) STRING_HASH_1 ((KEY), (RESULT))
#define return_ISTRING_HASH_1(KEY) return_STRING_HASH_1 (KEY)

#define ISTRING_HASH_2(KEY, RESULT) STRING_HASH_2 ((KEY), (RESULT))
#define return_ISTRING_HASH_2(KEY) return_STRING_HASH_2 (KEY)

#define ISTRING_COMPARE(X, Y, RESULT) STRING_COMPARE ((X), (Y), (RESULT))
#define return_ISTRING_COMPARE(X, Y) return_STRING_COMPARE ((X), (Y))

#endif

/* hash and comparison macros for integer _key_s. */

#define INTEGER_HASH_1(KEY, RESULT) do { \
  (RESULT) += ((unsigned long)(KEY)); \
} while (0)
#define return_INTEGER_HASH_1(KEY) do { \
  unsigned long _result_ = 0; \
  INTEGER_HASH_1 ((KEY), _result_); \
  return _result_; \
} while (0)

#define INTEGER_HASH_2(KEY, RESULT) do { \
  (RESULT) += ~((unsigned long)(KEY)); \
} while (0)
#define return_INTEGER_HASH_2(KEY) do { \
  unsigned long _result_ = 0; \
  INTEGER_HASH_2 ((KEY), _result_); \
  return _result_; \
} while (0)

#define INTEGER_COMPARE(X, Y, RESULT) do { \
  (RESULT) = X - Y; \
} while (0)
#define return_INTEGER_COMPARE(X, Y) do { \
  int _result_; \
  INTEGER_COMPARE (X, Y, _result_); \
  return _result_; \
} while (0)

/* hash and comparison macros for address keys. */

#define ADDRESS_HASH_1(KEY, RESULT) INTEGER_HASH_1 (((unsigned long)(KEY)) >> 3, (RESULT))
#define ADDRESS_HASH_2(KEY, RESULT) INTEGER_HASH_2 (((unsigned long)(KEY)) >> 3, (RESULT))
#define ADDRESS_COMPARE(X, Y, RESULT) INTEGER_COMPARE ((X), (Y), (RESULT))
#define return_ADDRESS_HASH_1(KEY) return_INTEGER_HASH_1 (((unsigned long)(KEY)) >> 3)
#define return_ADDRESS_HASH_2(KEY) return_INTEGER_HASH_2 (((unsigned long)(KEY)) >> 3)
#define return_ADDRESS_COMPARE(X, Y) return_INTEGER_COMPARE ((X), (Y))

#endif /* not _hash_h_ */

https://git.savannah.gnu.org/cgit/make.git/tree/dep.h?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/dep.h used in: Step 517, Step 519, Step 520, Step 523, Step 524, Step 525, Step 527, Step 528, Step 529, Step 530, Step 531, Step 534
/* Definitions of dependency data structures for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

/* Flag bits for the second argument to `read_makefile'.
   These flags are saved in the `changed' field of each
   `struct dep' in the chain returned by `read_all_makefiles'.  */

#define RM_NO_DEFAULT_GOAL  (1 << 0) /* Do not set default goal.  */
#define RM_INCLUDED     (1 << 1) /* Search makefile search path.  */
#define RM_DONTCARE     (1 << 2) /* No error if it doesn't exist.  */
#define RM_NO_TILDE     (1 << 3) /* Don't expand ~ in file name.  */
#define RM_NOFLAG       0

/* Structure representing one dependency of a file.
   Each struct file's `deps' points to a chain of these,
   chained through the `next'. `stem' is the stem for this
   dep line of static pattern rule or NULL.

   Note that the first two words of this match a struct nameseq.  */

struct dep
  {
    struct dep *next;
    const char *name;
    const char *stem;
    struct file *file;
    unsigned int changed : 8;
    unsigned int ignore_mtime : 1;
    unsigned int staticpattern : 1;
    unsigned int need_2nd_expansion : 1;
    unsigned int dontcare : 1;
  };


/* Structure used in chains of names, for parsing and globbing.  */

struct nameseq
  {
    struct nameseq *next;
    const char *name;
  };


#define PARSEFS_NONE    (0x0000)
#define PARSEFS_NOSTRIP (0x0001)
#define PARSEFS_NOAR    (0x0002)
#define PARSEFS_NOGLOB  (0x0004)
#define PARSEFS_EXISTS  (0x0008)
#define PARSEFS_NOCACHE (0x0010)

#define PARSE_FILE_SEQ(_s,_t,_c,_p,_f) \
            (_t *)parse_file_seq ((_s),sizeof (_t),(_c),(_p),(_f))

#ifdef VMS
void *parse_file_seq ();
#else
void *parse_file_seq (char **stringp, unsigned int size,
                      int stopchar, const char *prefix, int flags);
#endif

char *tilde_expand (const char *name);

#ifndef NO_ARCHIVES
struct nameseq *ar_glob (const char *arname, const char *member_pattern, unsigned int size);
#endif

#define dep_name(d)     ((d)->name == 0 ? (d)->file->name : (d)->name)

#define alloc_dep()     (xcalloc (sizeof (struct dep)))
#define free_ns(_n)     free (_n)
#define free_dep(_d)    free_ns (_d)

struct dep *copy_dep_chain (const struct dep *d);
void free_dep_chain (struct dep *d);
void free_ns_chain (struct nameseq *n);
struct dep *read_all_makefiles (const char **makefiles);
void eval_buffer (char *buffer);
int update_goal_chain (struct dep *goals);

https://git.savannah.gnu.org/cgit/make.git/tree/glob/fnmatch.h?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/glob/fnmatch.h used in: Step 517
/* Copyright (C) 1991, 1992, 1993, 1996, 1997, 1998, 1999 Free Software
Foundation, Inc.
This file is part of the GNU C Library.

The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

The GNU C Library 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.

You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB.  If not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
USA.  */

#ifndef _FNMATCH_H
#define _FNMATCH_H  1

#ifdef  __cplusplus
extern "C" {
#endif

#if defined __cplusplus || (defined __STDC__ && __STDC__) || defined WINDOWS32
# if !defined __GLIBC__
#  undef    __P
#  define __P(protos)   protos
# endif
#else /* Not C++ or ANSI C.  */
# undef __P
# define __P(protos)    ()
/* We can get away without defining `const' here only because in this file
   it is used only inside the prototype for `fnmatch', which is elided in
   non-ANSI C where `const' is problematical.  */
#endif /* C++ or ANSI C.  */

#ifndef const
# if (defined __STDC__ && __STDC__) || defined __cplusplus || defined WINDOWS32
#  define __const   const
# else
#  define __const
# endif
#endif

/* We #undef these before defining them because some losing systems
   (HP-UX A.08.07 for example) define these in <unistd.h>.  */
#undef  FNM_PATHNAME
#undef  FNM_NOESCAPE
#undef  FNM_PERIOD

/* Bits set in the FLAGS argument to `fnmatch'.  */
#define FNM_PATHNAME    (1 << 0) /* No wildcard can ever match `/'.  */
#define FNM_NOESCAPE    (1 << 1) /* Backslashes don't quote special chars.  */
#define FNM_PERIOD  (1 << 2) /* Leading `.' is matched only explicitly.  */

#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE
# define FNM_FILE_NAME   FNM_PATHNAME   /* Preferred GNU name.  */
# define FNM_LEADING_DIR (1 << 3)   /* Ignore `/...' after a match.  */
# define FNM_CASEFOLD    (1 << 4)   /* Compare without regard to case.  */
#endif

/* Value returned by `fnmatch' if STRING does not match PATTERN.  */
#define FNM_NOMATCH 1

/* This value is returned if the implementation does not support
   `fnmatch'.  Since this is not the case here it will never be
   returned but the conformance test suites still require the symbol
   to be defined.  */
#ifdef _XOPEN_SOURCE
# define FNM_NOSYS  (-1)
#endif

/* Match NAME against the filename pattern PATTERN,
   returning zero if it matches, FNM_NOMATCH if not.  */
extern int fnmatch __P ((__const char *__pattern, __const char *__name,
             int __flags));

#ifdef  __cplusplus
}
#endif

#endif /* fnmatch.h */

https://git.savannah.gnu.org/cgit/make.git/tree/arscan.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/arscan.c used in: Step 518
/* Library function for scanning an archive file.
Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#else
#include <sys/file.h>
#endif

#ifndef NO_ARCHIVES

#ifdef VMS
#include <lbrdef.h>
#include <mhddef.h>
#include <credef.h>
#include <descrip.h>
#include <ctype.h>
#if __DECC
#include <unixlib.h>
#include <lbr$routines.h>
#endif

static void *VMS_lib_idx;

static char *VMS_saved_memname;

static time_t VMS_member_date;

static long int (*VMS_function) ();

static int
VMS_get_member_info (struct dsc$descriptor_s *module, unsigned long *rfa)
{
  int status, i;
  long int fnval;

  time_t val;

  static struct dsc$descriptor_s bufdesc =
    { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };

  struct mhddef *mhd;
  char filename[128];

  bufdesc.dsc$a_pointer = filename;
  bufdesc.dsc$w_length = sizeof (filename);

  status = lbr$set_module (&VMS_lib_idx, rfa, &bufdesc,
               &bufdesc.dsc$w_length, 0);
  if (! (status & 1))
    {
      error (NILF, _("lbr$set_module() failed to extract module info, status = %d"),
         status);

      lbr$close (&VMS_lib_idx);

      return 0;
    }

  mhd = (struct mhddef *) filename;

#ifdef __DECC
  /* John Fowler <jfowler@nyx.net> writes this is needed in his environment,
   * but that decc$fix_time() isn't documented to work this way.  Let me
   * know if this causes problems in other VMS environments.
   */
  {
    /* Modified by M. Gehre at 11-JAN-2008 because old formula is wrong:
     * val = decc$fix_time (&mhd->mhd$l_datim) + timezone - daylight*3600;
     * a) daylight specifies, if the timezone has daylight saving enabled, not
     *    if it is active
     * b) what we need is the information, if daylight saving was active, if
     *    the library module was replaced. This information we get using the
     *    localtime function
     */

    struct tm *tmp;

    /* Conversion from VMS time to C time */
    val = decc$fix_time (&mhd->mhd$l_datim);

    /*
     * Conversion from local time (stored in library) to GMT (needed for gmake)
     * Note: The tm_gmtoff element is a VMS extension to the ANSI standard.
     */
    tmp = localtime (&val);
    val -= tmp->tm_gmtoff;
  }
#endif

  for (i = 0; i < module->dsc$w_length; i++)
    filename[i] = _tolower ((unsigned char)module->dsc$a_pointer[i]);

  filename[i] = '\0';

  VMS_member_date = (time_t) -1;

  fnval =
    (*VMS_function) (-1, filename, 0, 0, 0, 0, val, 0, 0, 0,
             VMS_saved_memname);

  if (fnval)
    {
      VMS_member_date = fnval;
      return 0;
    }
  else
    return 1;
}

/* Takes three arguments ARCHIVE, FUNCTION and ARG.

   Open the archive named ARCHIVE, find its members one by one,
   and for each one call FUNCTION with the following arguments:
     archive file descriptor for reading the data,
     member name,
     member name might be truncated flag,
     member header position in file,
     member data position in file,
     member data size,
     member date,
     member uid,
     member gid,
     member protection mode,
     ARG.

   NOTE: on VMS systems, only name, date, and arg are meaningful!

   The descriptor is poised to read the data of the member
   when FUNCTION is called.  It does not matter how much
   data FUNCTION reads.

   If FUNCTION returns nonzero, we immediately return
   what FUNCTION returned.

   Returns -1 if archive does not exist,
   Returns -2 if archive has invalid format.
   Returns 0 if have scanned successfully.  */

long int
ar_scan (const char *archive, ar_member_func_t function, const void *arg)
{
  char *p;

  static struct dsc$descriptor_s libdesc =
    { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, NULL };

  unsigned long func = LBR$C_READ;
  unsigned long type = LBR$C_TYP_UNK;
  unsigned long index = 1;

  int status;

  status = lbr$ini_control (&VMS_lib_idx, &func, &type, 0);

  if (! (status & 1))
    {
      error (NILF, _("lbr$ini_control() failed with status = %d"), status);
      return -2;
    }

  /* there is no such descriptor with "const char *dsc$a_pointer" */
  libdesc.dsc$a_pointer = (char *)archive;
  libdesc.dsc$w_length = strlen (archive);

  status = lbr$open (&VMS_lib_idx, &libdesc, 0, 0, 0, 0, 0);

  if (! (status & 1))
    {
      error (NILF, _("unable to open library `%s' to lookup member `%s'"),
         archive, (char *)arg);
      return -1;
    }

  VMS_saved_memname = (char *)arg;

  /* For comparison, delete .obj from arg name.  */

  p = strrchr (VMS_saved_memname, '.');
  if (p)
    *p = '\0';

  VMS_function = function;

  VMS_member_date = (time_t) -1;
  lbr$get_index (&VMS_lib_idx, &index, VMS_get_member_info, 0);

  /* Undo the damage.  */
  if (p)
    *p = '.';

  lbr$close (&VMS_lib_idx);

  return VMS_member_date > 0 ? VMS_member_date : 0;
}

#else /* !VMS */

/* SCO Unix's compiler defines both of these.  */
#ifdef  M_UNIX
#undef  M_XENIX
#endif

/* On the sun386i and in System V rel 3, ar.h defines two different archive
   formats depending upon whether you have defined PORTAR (normal) or PORT5AR
   (System V Release 1).  There is no default, one or the other must be defined
   to have a nonzero value.  */

#if (!defined (PORTAR) || PORTAR == 0) && (!defined (PORT5AR) || PORT5AR == 0)
#undef  PORTAR
#ifdef M_XENIX
/* According to Jim Sievert <jas1@rsvl.unisys.com>, for SCO XENIX defining
   PORTAR to 1 gets the wrong archive format, and defining it to 0 gets the
   right one.  */
#define PORTAR 0
#else
#define PORTAR 1
#endif
#endif

/* On AIX, define these symbols to be sure to get both archive formats.
   AIX 4.3 introduced the "big" archive format to support 64-bit object
   files, so on AIX 4.3 systems we need to support both the "normal" and
   "big" archive formats.  An archive's format is indicated in the
   "fl_magic" field of the "FL_HDR" structure.  For a normal archive,
   this field will be the string defined by the AIAMAG symbol.  For a
   "big" archive, it will be the string defined by the AIAMAGBIG symbol
   (at least on AIX it works this way).

   Note: we'll define these symbols regardless of which AIX version
   we're compiling on, but this is okay since we'll use the new symbols
   only if they're present.  */
#ifdef _AIX
# define __AR_SMALL__
# define __AR_BIG__
#endif

#ifndef WINDOWS32
# ifndef __BEOS__
#  include <ar.h>
# else
   /* BeOS 5 doesn't have <ar.h> but has archives in the same format
    * as many other Unices.  This was taken from GNU binutils for BeOS.
    */
#  define ARMAG "!<arch>\n" /* String that begins an archive file.  */
#  define SARMAG 8      /* Size of that string.  */
#  define ARFMAG "`\n"      /* String in ar_fmag at end of each header.  */
struct ar_hdr
  {
    char ar_name[16];       /* Member file name, sometimes / terminated. */
    char ar_date[12];       /* File date, decimal seconds since Epoch.  */
    char ar_uid[6], ar_gid[6];  /* User and group IDs, in ASCII decimal.  */
    char ar_mode[8];        /* File mode, in ASCII octal.  */
    char ar_size[10];       /* File size, in ASCII decimal.  */
    char ar_fmag[2];        /* Always contains ARFMAG.  */
  };
# endif
# define TOCHAR(_m)     (_m)
#else
/* These should allow us to read Windows (VC++) libraries (according to Frank
 * Libbrecht <frankl@abzx.belgium.hp.com>)
 */
# include <windows.h>
# include <windef.h>
# include <io.h>
# define ARMAG      IMAGE_ARCHIVE_START
# define SARMAG     IMAGE_ARCHIVE_START_SIZE
# define ar_hdr     _IMAGE_ARCHIVE_MEMBER_HEADER
# define ar_name    Name
# define ar_mode    Mode
# define ar_size    Size
# define ar_date    Date
# define ar_uid     UserID
# define ar_gid     GroupID
/* In Windows the member names have type BYTE so we must cast them.  */
# define TOCHAR(_m)     ((char *)(_m))
#endif

/* Cray's <ar.h> apparently defines this.  */
#ifndef AR_HDR_SIZE
# define   AR_HDR_SIZE  (sizeof (struct ar_hdr))
#endif


/* Takes three arguments ARCHIVE, FUNCTION and ARG.

   Open the archive named ARCHIVE, find its members one by one,
   and for each one call FUNCTION with the following arguments:
     archive file descriptor for reading the data,
     member name,
     member name might be truncated flag,
     member header position in file,
     member data position in file,
     member data size,
     member date,
     member uid,
     member gid,
     member protection mode,
     ARG.

   The descriptor is poised to read the data of the member
   when FUNCTION is called.  It does not matter how much
   data FUNCTION reads.

   If FUNCTION returns nonzero, we immediately return
   what FUNCTION returned.

   Returns -1 if archive does not exist,
   Returns -2 if archive has invalid format.
   Returns 0 if have scanned successfully.  */

long int
ar_scan (const char *archive, ar_member_func_t function, const void *arg)
{
#ifdef AIAMAG
  FL_HDR fl_header;
#ifdef AIAMAGBIG
  int big_archive = 0;
  FL_HDR_BIG fl_header_big;
#endif
#else
  int long_name = 0;
#endif
  char *namemap = 0;
  int desc = open (archive, O_RDONLY, 0);
  if (desc < 0)
    return -1;
#ifdef SARMAG
  {
    char buf[SARMAG];
    register int nread = read (desc, buf, SARMAG);
    if (nread != SARMAG || memcmp (buf, ARMAG, SARMAG))
      {
    (void) close (desc);
    return -2;
      }
  }
#else
#ifdef AIAMAG
  {
    register int nread = read (desc, &fl_header, FL_HSZ);

    if (nread != FL_HSZ)
      {
    (void) close (desc);
    return -2;
      }
#ifdef AIAMAGBIG
    /* If this is a "big" archive, then set the flag and
       re-read the header into the "big" structure. */
    if (!memcmp (fl_header.fl_magic, AIAMAGBIG, SAIAMAG))
      {
    big_archive = 1;

    /* seek back to beginning of archive */
    if (lseek (desc, 0, 0) < 0)
      {
        (void) close (desc);
        return -2;
      }

    /* re-read the header into the "big" structure */
    nread = read (desc, &fl_header_big, FL_HSZ_BIG);
    if (nread != FL_HSZ_BIG)
      {
        (void) close (desc);
        return -2;
      }
      }
    else
#endif
       /* Check to make sure this is a "normal" archive. */
      if (memcmp (fl_header.fl_magic, AIAMAG, SAIAMAG))
    {
          (void) close (desc);
          return -2;
    }
  }
#else
  {
#ifndef M_XENIX
    int buf;
#else
    unsigned short int buf;
#endif
    register int nread = read(desc, &buf, sizeof (buf));
    if (nread != sizeof (buf) || buf != ARMAG)
      {
    (void) close (desc);
    return -2;
      }
  }
#endif
#endif

  /* Now find the members one by one.  */
  {
#ifdef SARMAG
    register long int member_offset = SARMAG;
#else
#ifdef AIAMAG
    long int member_offset;
    long int last_member_offset;

#ifdef AIAMAGBIG
    if ( big_archive )
      {
    sscanf (fl_header_big.fl_fstmoff, "%20ld", &member_offset);
    sscanf (fl_header_big.fl_lstmoff, "%20ld", &last_member_offset);
      }
    else
#endif
      {
    sscanf (fl_header.fl_fstmoff, "%12ld", &member_offset);
    sscanf (fl_header.fl_lstmoff, "%12ld", &last_member_offset);
      }

    if (member_offset == 0)
      {
    /* Empty archive.  */
    close (desc);
    return 0;
      }
#else
#ifndef M_XENIX
    register long int member_offset = sizeof (int);
#else   /* Xenix.  */
    register long int member_offset = sizeof (unsigned short int);
#endif  /* Not Xenix.  */
#endif
#endif

    while (1)
      {
    register int nread;
    struct ar_hdr member_header;
#ifdef AIAMAGBIG
    struct ar_hdr_big member_header_big;
#endif
#ifdef AIAMAG
    char name[256];
    int name_len;
    long int dateval;
    int uidval, gidval;
    long int data_offset;
#else
    char namebuf[sizeof member_header.ar_name + 1];
    char *name;
    int is_namemap;     /* Nonzero if this entry maps long names.  */
#endif
    long int eltsize;
    int eltmode;
    long int fnval;

    if (lseek (desc, member_offset, 0) < 0)
      {
        (void) close (desc);
        return -2;
      }

#ifdef AIAMAG
#define       AR_MEMHDR_SZ(x) (sizeof(x) - sizeof (x._ar_name))

#ifdef AIAMAGBIG
    if (big_archive)
      {
        nread = read (desc, &member_header_big,
              AR_MEMHDR_SZ(member_header_big) );

        if (nread != AR_MEMHDR_SZ(member_header_big))
          {
        (void) close (desc);
        return -2;
          }

        sscanf (member_header_big.ar_namlen, "%4d", &name_len);
        nread = read (desc, name, name_len);

        if (nread != name_len)
          {
        (void) close (desc);
        return -2;
          }

        name[name_len] = 0;

        sscanf (member_header_big.ar_date, "%12ld", &dateval);
        sscanf (member_header_big.ar_uid, "%12d", &uidval);
        sscanf (member_header_big.ar_gid, "%12d", &gidval);
        sscanf (member_header_big.ar_mode, "%12o", &eltmode);
        sscanf (member_header_big.ar_size, "%20ld", &eltsize);

        data_offset = (member_offset + AR_MEMHDR_SZ(member_header_big)
               + name_len + 2);
      }
    else
#endif
      {
        nread = read (desc, &member_header,
              AR_MEMHDR_SZ(member_header) );

        if (nread != AR_MEMHDR_SZ(member_header))
          {
        (void) close (desc);
        return -2;
          }

        sscanf (member_header.ar_namlen, "%4d", &name_len);
        nread = read (desc, name, name_len);

        if (nread != name_len)
          {
        (void) close (desc);
        return -2;
          }

        name[name_len] = 0;

        sscanf (member_header.ar_date, "%12ld", &dateval);
        sscanf (member_header.ar_uid, "%12d", &uidval);
        sscanf (member_header.ar_gid, "%12d", &gidval);
        sscanf (member_header.ar_mode, "%12o", &eltmode);
        sscanf (member_header.ar_size, "%12ld", &eltsize);

        data_offset = (member_offset + AR_MEMHDR_SZ(member_header)
               + name_len + 2);
      }
    data_offset += data_offset % 2;

    fnval =
      (*function) (desc, name, 0,
               member_offset, data_offset, eltsize,
               dateval, uidval, gidval,
               eltmode, arg);

#else   /* Not AIAMAG.  */
    nread = read (desc, &member_header, AR_HDR_SIZE);
    if (nread == 0)
      /* No data left means end of file; that is OK.  */
      break;

    if (nread != AR_HDR_SIZE
#if defined(ARFMAG) || defined(ARFZMAG)
        || (
# ifdef ARFMAG
                memcmp (member_header.ar_fmag, ARFMAG, 2)
# else
                1
# endif
                &&
# ifdef ARFZMAG
                memcmp (member_header.ar_fmag, ARFZMAG, 2)
# else
                1
# endif
               )
#endif
        )
      {
        (void) close (desc);
        return -2;
      }

    name = namebuf;
    memcpy (name, member_header.ar_name, sizeof member_header.ar_name);
    {
      register char *p = name + sizeof member_header.ar_name;
      do
        *p = '\0';
      while (p > name && *--p == ' ');

#ifndef AIAMAG
      /* If the member name is "//" or "ARFILENAMES/" this may be
         a list of file name mappings.  The maximum file name
         length supported by the standard archive format is 14
         characters.  This member will actually always be the
         first or second entry in the archive, but we don't check
         that.  */
      is_namemap = (!strcmp (name, "//")
            || !strcmp (name, "ARFILENAMES/"));
#endif  /* Not AIAMAG. */
      /* On some systems, there is a slash after each member name.  */
      if (*p == '/')
        *p = '\0';

#ifndef AIAMAG
      /* If the member name starts with a space or a slash, this
         is an index into the file name mappings (used by GNU ar).
         Otherwise if the member name looks like #1/NUMBER the
         real member name appears in the element data (used by
         4.4BSD).  */
      if (! is_namemap
          && (name[0] == ' ' || name[0] == '/')
          && namemap != 0)
        {
          name = namemap + atoi (name + 1);
          long_name = 1;
        }
      else if (name[0] == '#'
           && name[1] == '1'
           && name[2] == '/')
        {
          int namesize = atoi (name + 3);

          name = alloca (namesize + 1);
          nread = read (desc, name, namesize);
          if (nread != namesize)
        {
          close (desc);
          return -2;
        }
          name[namesize] = '\0';

          long_name = 1;
        }
#endif /* Not AIAMAG. */
    }

#ifndef M_XENIX
    sscanf (TOCHAR (member_header.ar_mode), "%o", &eltmode);
    eltsize = atol (TOCHAR (member_header.ar_size));
#else   /* Xenix.  */
    eltmode = (unsigned short int) member_header.ar_mode;
    eltsize = member_header.ar_size;
#endif  /* Not Xenix.  */

    fnval =
      (*function) (desc, name, ! long_name, member_offset,
               member_offset + AR_HDR_SIZE, eltsize,
#ifndef M_XENIX
               atol (TOCHAR (member_header.ar_date)),
               atoi (TOCHAR (member_header.ar_uid)),
               atoi (TOCHAR (member_header.ar_gid)),
#else   /* Xenix.  */
               member_header.ar_date,
               member_header.ar_uid,
               member_header.ar_gid,
#endif  /* Not Xenix.  */
               eltmode, arg);

#endif  /* AIAMAG.  */

    if (fnval)
      {
        (void) close (desc);
        return fnval;
      }

#ifdef AIAMAG
    if (member_offset == last_member_offset)
      /* End of the chain.  */
      break;

#ifdef AIAMAGBIG
    if (big_archive)
         sscanf (member_header_big.ar_nxtmem, "%20ld", &member_offset);
    else
#endif
      sscanf (member_header.ar_nxtmem, "%12ld", &member_offset);

    if (lseek (desc, member_offset, 0) != member_offset)
      {
        (void) close (desc);
        return -2;
      }
#else

    /* If this member maps archive names, we must read it in.  The
       name map will always precede any members whose names must
       be mapped.  */
    if (is_namemap)
      {
        char *clear;
        char *limit;

        namemap = alloca (eltsize);
        nread = read (desc, namemap, eltsize);
        if (nread != eltsize)
          {
        (void) close (desc);
        return -2;
          }

        /* The names are separated by newlines.  Some formats have
           a trailing slash.  Null terminate the strings for
           convenience.  */
        limit = namemap + eltsize;
        for (clear = namemap; clear < limit; clear++)
          {
        if (*clear == '\n')
          {
            *clear = '\0';
            if (clear[-1] == '/')
              clear[-1] = '\0';
          }
          }

        is_namemap = 0;
      }

    member_offset += AR_HDR_SIZE + eltsize;
    if (member_offset % 2 != 0)
      member_offset++;
#endif
      }
  }

  close (desc);
  return 0;
}
#endif /* !VMS */


/* Return nonzero iff NAME matches MEM.
   If TRUNCATED is nonzero, MEM may be truncated to
   sizeof (struct ar_hdr.ar_name) - 1.  */

int
ar_name_equal (const char *name, const char *mem, int truncated)
{
  const char *p;

  p = strrchr (name, '/');
  if (p != 0)
    name = p + 1;

#ifndef VMS
  if (truncated)
    {
#ifdef AIAMAG
      /* TRUNCATED should never be set on this system.  */
      abort ();
#else
      struct ar_hdr hdr;
#if !defined (__hpux) && !defined (cray)
      return strneq (name, mem, sizeof(hdr.ar_name) - 1);
#else
      return strneq (name, mem, sizeof(hdr.ar_name) - 2);
#endif /* !__hpux && !cray */
#endif /* !AIAMAG */
    }
#endif /* !VMS */

  return !strcmp (name, mem);
}


#ifndef VMS
/* ARGSUSED */
static long int
ar_member_pos (int desc UNUSED, const char *mem, int truncated,
           long int hdrpos, long int datapos UNUSED, long int size UNUSED,
               long int date UNUSED, int uid UNUSED, int gid UNUSED,
               int mode UNUSED, const void *name)
{
  if (!ar_name_equal (name, mem, truncated))
    return 0;
  return hdrpos;
}

/* Set date of member MEMNAME in archive ARNAME to current time.
   Returns 0 if successful,
   -1 if file ARNAME does not exist,
   -2 if not a valid archive,
   -3 if other random system call error (including file read-only),
   1 if valid but member MEMNAME does not exist.  */

int
ar_member_touch (const char *arname, const char *memname)
{
  long int pos = ar_scan (arname, ar_member_pos, memname);
  int fd;
  struct ar_hdr ar_hdr;
  int i;
  unsigned int ui;
  struct stat statbuf;

  if (pos < 0)
    return (int) pos;
  if (!pos)
    return 1;

  fd = open (arname, O_RDWR, 0666);
  if (fd < 0)
    return -3;
  /* Read in this member's header */
  if (lseek (fd, pos, 0) < 0)
    goto lose;
  if (AR_HDR_SIZE != read (fd, &ar_hdr, AR_HDR_SIZE))
    goto lose;
  /* Write back the header, thus touching the archive file.  */
  if (lseek (fd, pos, 0) < 0)
    goto lose;
  if (AR_HDR_SIZE != write (fd, &ar_hdr, AR_HDR_SIZE))
    goto lose;
  /* The file's mtime is the time we we want.  */
  EINTRLOOP (i, fstat (fd, &statbuf));
  if (i < 0)
    goto lose;
#if defined(ARFMAG) || defined(ARFZMAG) || defined(AIAMAG) || defined(WINDOWS32)
  /* Advance member's time to that time */
  for (ui = 0; ui < sizeof ar_hdr.ar_date; ui++)
    ar_hdr.ar_date[ui] = ' ';
  sprintf (TOCHAR (ar_hdr.ar_date), "%ld", (long int) statbuf.st_mtime);
#ifdef AIAMAG
  ar_hdr.ar_date[strlen(ar_hdr.ar_date)] = ' ';
#endif
#else
  ar_hdr.ar_date = statbuf.st_mtime;
#endif
  /* Write back this member's header */
  if (lseek (fd, pos, 0) < 0)
    goto lose;
  if (AR_HDR_SIZE != write (fd, &ar_hdr, AR_HDR_SIZE))
    goto lose;
  close (fd);
  return 0;

 lose:
  i = errno;
  close (fd);
  errno = i;
  return -3;
}
#endif


#ifdef TEST

long int
describe_member (int desc, const char *name, int truncated,
         long int hdrpos, long int datapos, long int size,
                 long int date, int uid, int gid, int mode, const void *arg)
{
  extern char *ctime ();

  printf (_("Member `%s'%s: %ld bytes at %ld (%ld).\n"),
      name, truncated ? _(" (name might be truncated)") : "",
      size, hdrpos, datapos);
  printf (_("  Date %s"), ctime (&date));
  printf (_("  uid = %d, gid = %d, mode = 0%o.\n"), uid, gid, mode);

  return 0;
}

int
main (int argc, char **argv)
{
  ar_scan (argv[1], describe_member, NULL);
  return 0;
}

#endif  /* TEST.  */
#endif  /* NO_ARCHIVES.  */

https://git.savannah.gnu.org/cgit/make.git/tree/commands.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/commands.c used in: Step 519
/* Command processing for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"
#include "dep.h"
#include "filedef.h"
#include "variable.h"
#include "job.h"
#include "commands.h"
#ifdef WINDOWS32
#include <windows.h>
#include "w32err.h"
#endif

#if VMS
# define FILE_LIST_SEPARATOR ','
#else
# define FILE_LIST_SEPARATOR ' '
#endif

int remote_kill (int id, int sig);

#ifndef HAVE_UNISTD_H
int getpid ();
#endif



static unsigned long
dep_hash_1 (const void *key)
{
  const struct dep *d = key;
  return_STRING_HASH_1 (dep_name (d));
}

static unsigned long
dep_hash_2 (const void *key)
{
  const struct dep *d = key;
  return_STRING_HASH_2 (dep_name (d));
}

static int
dep_hash_cmp (const void *x, const void *y)
{
  const struct dep *dx = x;
  const struct dep *dy = y;
  return strcmp (dep_name (dx), dep_name (dy));
}

/* Set FILE's automatic variables up.  */

void
set_file_variables (struct file *file)
{
  struct dep *d;
  const char *at, *percent, *star, *less;

#ifndef NO_ARCHIVES
  /* If the target is an archive member `lib(member)',
     then $@ is `lib' and $% is `member'.  */

  if (ar_name (file->name))
    {
      unsigned int len;
      const char *cp;
      char *p;

      cp = strchr (file->name, '(');
      p = alloca (cp - file->name + 1);
      memcpy (p, file->name, cp - file->name);
      p[cp - file->name] = '\0';
      at = p;
      len = strlen (cp + 1);
      p = alloca (len);
      memcpy (p, cp + 1, len - 1);
      p[len - 1] = '\0';
      percent = p;
    }
  else
#endif  /* NO_ARCHIVES.  */
    {
      at = file->name;
      percent = "";
    }

  /* $* is the stem from an implicit or static pattern rule.  */
  if (file->stem == 0)
    {
      /* In Unix make, $* is set to the target name with
     any suffix in the .SUFFIXES list stripped off for
     explicit rules.  We store this in the `stem' member.  */
      const char *name;
      unsigned int len;

#ifndef NO_ARCHIVES
      if (ar_name (file->name))
    {
      name = strchr (file->name, '(') + 1;
      len = strlen (name) - 1;
    }
      else
#endif
    {
      name = file->name;
      len = strlen (name);
    }

      for (d = enter_file (strcache_add (".SUFFIXES"))->deps; d ; d = d->next)
    {
      unsigned int slen = strlen (dep_name (d));
      if (len > slen && strneq (dep_name (d), name + (len - slen), slen))
        {
          file->stem = strcache_add_len (name, len - slen);
          break;
        }
    }
      if (d == 0)
    file->stem = "";
    }
  star = file->stem;

  /* $< is the first not order-only dependency.  */
  less = "";
  for (d = file->deps; d != 0; d = d->next)
    if (!d->ignore_mtime)
      {
        if (!d->need_2nd_expansion)
          less = dep_name (d);
        break;
      }

  if (file->cmds == default_file->cmds)
    /* This file got its commands from .DEFAULT.
       In this case $< is the same as $@.  */
    less = at;

#define DEFINE_VARIABLE(name, len, value) \
  (void) define_variable_for_file (name,len,value,o_automatic,0,file)

  /* Define the variables.  */

  DEFINE_VARIABLE ("<", 1, less);
  DEFINE_VARIABLE ("*", 1, star);
  DEFINE_VARIABLE ("@", 1, at);
  DEFINE_VARIABLE ("%", 1, percent);

  /* Compute the values for $^, $+, $?, and $|.  */

  {
    static char *plus_value=0, *bar_value=0, *qmark_value=0;
    static unsigned int plus_max=0, bar_max=0, qmark_max=0;

    unsigned int qmark_len, plus_len, bar_len;
    char *cp;
    char *caret_value;
    char *qp;
    char *bp;
    unsigned int len;

    struct hash_table dep_hash;
    void **slot;

    /* Compute first the value for $+, which is supposed to contain
       duplicate dependencies as they were listed in the makefile.  */

    plus_len = 0;
    bar_len = 0;
    for (d = file->deps; d != 0; d = d->next)
      {
        if (!d->need_2nd_expansion)
          {
            if (d->ignore_mtime)
              bar_len += strlen (dep_name (d)) + 1;
            else
              plus_len += strlen (dep_name (d)) + 1;
          }
      }

    if (bar_len == 0)
      bar_len++;

    if (plus_len == 0)
      plus_len++;

    if (plus_len > plus_max)
      plus_value = xrealloc (plus_value, plus_max = plus_len);

    cp = plus_value;

    qmark_len = plus_len + 1;   /* Will be this or less.  */
    for (d = file->deps; d != 0; d = d->next)
      if (! d->ignore_mtime && ! d->need_2nd_expansion)
        {
          const char *c = dep_name (d);

#ifndef NO_ARCHIVES
          if (ar_name (c))
            {
              c = strchr (c, '(') + 1;
              len = strlen (c) - 1;
            }
          else
#endif
            len = strlen (c);

          memcpy (cp, c, len);
          cp += len;
          *cp++ = FILE_LIST_SEPARATOR;
          if (! (d->changed || always_make_flag))
            qmark_len -= len + 1;   /* Don't space in $? for this one.  */
        }

    /* Kill the last space and define the variable.  */

    cp[cp > plus_value ? -1 : 0] = '\0';
    DEFINE_VARIABLE ("+", 1, plus_value);

    /* Compute the values for $^, $?, and $|.  */

    cp = caret_value = plus_value; /* Reuse the buffer; it's big enough.  */

    if (qmark_len > qmark_max)
      qmark_value = xrealloc (qmark_value, qmark_max = qmark_len);
    qp = qmark_value;

    if (bar_len > bar_max)
      bar_value = xrealloc (bar_value, bar_max = bar_len);
    bp = bar_value;

    /* Make sure that no dependencies are repeated in $^, $?, and $|.  It
       would be natural to combine the next two loops but we can't do it
       because of a situation where we have two dep entries, the first
       is order-only and the second is normal (see below).  */

    hash_init (&dep_hash, 500, dep_hash_1, dep_hash_2, dep_hash_cmp);

    for (d = file->deps; d != 0; d = d->next)
      {
        if (d->need_2nd_expansion)
          continue;

        slot = hash_find_slot (&dep_hash, d);
        if (HASH_VACANT (*slot))
          hash_insert_at (&dep_hash, d, slot);
        else
          {
            /* Check if the two prerequisites have different ignore_mtime.
               If so then we need to "upgrade" one that is order-only.  */

            struct dep* hd = (struct dep*) *slot;

            if (d->ignore_mtime != hd->ignore_mtime)
              d->ignore_mtime = hd->ignore_mtime = 0;
          }
      }

    for (d = file->deps; d != 0; d = d->next)
      {
        const char *c;

        if (d->need_2nd_expansion || hash_find_item (&dep_hash, d) != d)
          continue;

        c = dep_name (d);
#ifndef NO_ARCHIVES
        if (ar_name (c))
      {
        c = strchr (c, '(') + 1;
        len = strlen (c) - 1;
      }
    else
#endif
      len = strlen (c);

        if (d->ignore_mtime)
          {
            memcpy (bp, c, len);
        bp += len;
        *bp++ = FILE_LIST_SEPARATOR;
      }
    else
          {
            memcpy (cp, c, len);
            cp += len;
            *cp++ = FILE_LIST_SEPARATOR;
            if (d->changed || always_make_flag)
              {
                memcpy (qp, c, len);
                qp += len;
                *qp++ = FILE_LIST_SEPARATOR;
              }
          }
      }

    hash_free (&dep_hash, 0);

    /* Kill the last spaces and define the variables.  */

    cp[cp > caret_value ? -1 : 0] = '\0';
    DEFINE_VARIABLE ("^", 1, caret_value);

    qp[qp > qmark_value ? -1 : 0] = '\0';
    DEFINE_VARIABLE ("?", 1, qmark_value);

    bp[bp > bar_value ? -1 : 0] = '\0';
    DEFINE_VARIABLE ("|", 1, bar_value);
  }

#undef  DEFINE_VARIABLE
}


/* Chop CMDS up into individual command lines if necessary.
   Also set the `lines_flags' and `any_recurse' members.  */

void
chop_commands (struct commands *cmds)
{
  unsigned int nlines, idx;
  char **lines;

  /* If we don't have any commands,
     or we already parsed them, never mind.  */

  if (!cmds || cmds->command_lines != 0)
    return;

  /* Chop CMDS->commands up into lines in CMDS->command_lines.  */

  if (one_shell)
    {
      int l = strlen (cmds->commands);

      nlines = 1;
      lines = xmalloc (nlines * sizeof (char *));
      lines[0] = xstrdup (cmds->commands);

      /* Strip the trailing newline.  */
      if (l > 0 && lines[0][l-1] == '\n')
        lines[0][l-1] = '\0';
    }
  else
    {
      const char *p;

      nlines = 5;
      lines = xmalloc (nlines * sizeof (char *));
      idx = 0;
      p = cmds->commands;
      while (*p != '\0')
        {
          const char *end = p;
        find_end:;
          end = strchr (end, '\n');
          if (end == 0)
            end = p + strlen (p);
          else if (end > p && end[-1] == '\\')
            {
              int backslash = 1;
              const char *b;
              for (b = end - 2; b >= p && *b == '\\'; --b)
                backslash = !backslash;
              if (backslash)
                {
                  ++end;
                  goto find_end;
                }
            }

          if (idx == nlines)
            {
              nlines += 2;
              lines = xrealloc (lines, nlines * sizeof (char *));
            }
          lines[idx++] = xstrndup (p, end - p);
          p = end;
          if (*p != '\0')
            ++p;
        }

      if (idx != nlines)
        {
          nlines = idx;
          lines = xrealloc (lines, nlines * sizeof (char *));
        }
    }

  /* Finally, set the corresponding CMDS->lines_flags elements and the
     CMDS->any_recurse flag.  */

  cmds->ncommand_lines = nlines;
  cmds->command_lines = lines;

  cmds->any_recurse = 0;
  cmds->lines_flags = xmalloc (nlines);

  for (idx = 0; idx < nlines; ++idx)
    {
      int flags = 0;
      const char *p = lines[idx];

      while (isblank (*p) || *p == '-' || *p == '@' || *p == '+')
        switch (*(p++))
          {
          case '+':
            flags |= COMMANDS_RECURSE;
            break;
          case '@':
            flags |= COMMANDS_SILENT;
            break;
          case '-':
            flags |= COMMANDS_NOERROR;
            break;
          }

      /* If no explicit '+' was given, look for MAKE variable references.  */
      if (!(flags & COMMANDS_RECURSE)
          && (strstr (p, "$(MAKE)") != 0 || strstr (p, "${MAKE}") != 0))
        flags |= COMMANDS_RECURSE;

      cmds->lines_flags[idx] = flags;
      cmds->any_recurse |= flags & COMMANDS_RECURSE;
    }
}


/* Execute the commands to remake FILE.  If they are currently executing,
   return or have already finished executing, just return.  Otherwise,
   fork off a child process to run the first command line in the sequence.  */

void
execute_file_commands (struct file *file)
{
  const char *p;

  /* Don't go through all the preparations if
     the commands are nothing but whitespace.  */

  for (p = file->cmds->commands; *p != '\0'; ++p)
    if (!isspace ((unsigned char)*p) && *p != '-' && *p != '@')
      break;
  if (*p == '\0')
    {
      /* If there are no commands, assume everything worked.  */
      set_command_state (file, cs_running);
      file->update_status = 0;
      notice_finished_file (file);
      return;
    }

  /* First set the automatic variables according to this file.  */

  initialize_file_variables (file, 0);

  set_file_variables (file);

  /* Start the commands running.  */
  new_job (file);
}


/* This is set while we are inside fatal_error_signal,
   so things can avoid nonreentrant operations.  */

int handling_fatal_signal = 0;

/* Handle fatal signals.  */

RETSIGTYPE
fatal_error_signal (int sig)
{
#ifdef __MSDOS__
  extern int dos_status, dos_command_running;

  if (dos_command_running)
    {
      /* That was the child who got the signal, not us.  */
      dos_status |= (sig << 8);
      return;
    }
  remove_intermediates (1);
  exit (EXIT_FAILURE);
#else /* not __MSDOS__ */
#ifdef _AMIGA
  remove_intermediates (1);
  if (sig == SIGINT)
     fputs (_("*** Break.\n"), stderr);

  exit (10);
#else /* not Amiga */
#ifdef WINDOWS32
  extern HANDLE main_thread;

  /* Windows creates a sperate thread for handling Ctrl+C, so we need
     to suspend the main thread, or else we will have race conditions
     when both threads call reap_children.  */
  if (main_thread)
    {
      DWORD susp_count = SuspendThread (main_thread);

      if (susp_count != 0)
    fprintf (stderr, "SuspendThread: suspend count = %ld\n", susp_count);
      else if (susp_count == (DWORD)-1)
    {
      DWORD ierr = GetLastError ();

      fprintf (stderr, "SuspendThread: error %ld: %s\n",
           ierr, map_windows32_error_to_string (ierr));
    }
    }
#endif
  handling_fatal_signal = 1;

  /* Set the handling for this signal to the default.
     It is blocked now while we run this handler.  */
  signal (sig, SIG_DFL);

  /* A termination signal won't be sent to the entire
     process group, but it means we want to kill the children.  */

  if (sig == SIGTERM)
    {
      struct child *c;
      for (c = children; c != 0; c = c->next)
    if (!c->remote)
      (void) kill (c->pid, SIGTERM);
    }

  /* If we got a signal that means the user
     wanted to kill make, remove pending targets.  */

  if (sig == SIGTERM || sig == SIGINT
#ifdef SIGHUP
    || sig == SIGHUP
#endif
#ifdef SIGQUIT
    || sig == SIGQUIT
#endif
    )
    {
      struct child *c;

      /* Remote children won't automatically get signals sent
     to the process group, so we must send them.  */
      for (c = children; c != 0; c = c->next)
    if (c->remote)
      (void) remote_kill (c->pid, sig);

      for (c = children; c != 0; c = c->next)
    delete_child_targets (c);

      /* Clean up the children.  We don't just use the call below because
     we don't want to print the "Waiting for children" message.  */
      while (job_slots_used > 0)
    reap_children (1, 0);
    }
  else
    /* Wait for our children to die.  */
    while (job_slots_used > 0)
      reap_children (1, 1);

  /* Delete any non-precious intermediate files that were made.  */

  remove_intermediates (1);

#ifdef SIGQUIT
  if (sig == SIGQUIT)
    /* We don't want to send ourselves SIGQUIT, because it will
       cause a core dump.  Just exit instead.  */
    exit (EXIT_FAILURE);
#endif

#ifdef WINDOWS32
  if (main_thread)
    CloseHandle (main_thread);
  /* Cannot call W32_kill with a pid (it needs a handle).  The exit
     status of 130 emulates what happens in Bash.  */
  exit (130);
#else
  /* Signal the same code; this time it will really be fatal.  The signal
     will be unblocked when we return and arrive then to kill us.  */
  if (kill (getpid (), sig) < 0)
    pfatal_with_name ("kill");
#endif /* not WINDOWS32 */
#endif /* not Amiga */
#endif /* not __MSDOS__  */
}


/* Delete FILE unless it's precious or not actually a file (phony),
   and it has changed on disk since we last stat'd it.  */

static void
delete_target (struct file *file, const char *on_behalf_of)
{
  struct stat st;
  int e;

  if (file->precious || file->phony)
    return;

#ifndef NO_ARCHIVES
  if (ar_name (file->name))
    {
      time_t file_date = (file->last_mtime == NONEXISTENT_MTIME
              ? (time_t) -1
              : (time_t) FILE_TIMESTAMP_S (file->last_mtime));
      if (ar_member_date (file->name) != file_date)
    {
      if (on_behalf_of)
        error (NILF, _("*** [%s] Archive member `%s' may be bogus; not deleted"),
           on_behalf_of, file->name);
      else
        error (NILF, _("*** Archive member `%s' may be bogus; not deleted"),
           file->name);
    }
      return;
    }
#endif

  EINTRLOOP (e, stat (file->name, &st));
  if (e == 0
      && S_ISREG (st.st_mode)
      && FILE_TIMESTAMP_STAT_MODTIME (file->name, st) != file->last_mtime)
    {
      if (on_behalf_of)
    error (NILF, _("*** [%s] Deleting file `%s'"), on_behalf_of, file->name);
      else
    error (NILF, _("*** Deleting file `%s'"), file->name);
      if (unlink (file->name) < 0
      && errno != ENOENT)   /* It disappeared; so what.  */
    perror_with_name ("unlink: ", file->name);
    }
}


/* Delete all non-precious targets of CHILD unless they were already deleted.
   Set the flag in CHILD to say they've been deleted.  */

void
delete_child_targets (struct child *child)
{
  struct dep *d;

  if (child->deleted)
    return;

  /* Delete the target file if it changed.  */
  delete_target (child->file, NULL);

  /* Also remove any non-precious targets listed in the `also_make' member.  */
  for (d = child->file->also_make; d != 0; d = d->next)
    delete_target (d->file, child->file->name);

  child->deleted = 1;
}


/* Print out the commands in CMDS.  */

void
print_commands (const struct commands *cmds)
{
  const char *s;

  fputs (_("#  recipe to execute"), stdout);

  if (cmds->fileinfo.filenm == 0)
    puts (_(" (built-in):"));
  else
    printf (_(" (from `%s', line %lu):\n"),
            cmds->fileinfo.filenm, cmds->fileinfo.lineno);

  s = cmds->commands;
  while (*s != '\0')
    {
      const char *end;

      end = strchr (s, '\n');
      if (end == 0)
    end = s + strlen (s);

      printf ("%c%.*s\n", cmd_prefix, (int) (end - s), s);

      s = end + (end[0] == '\n');
    }
}

https://git.savannah.gnu.org/cgit/make.git/tree/variable.h?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/variable.h used in: Step 519, Step 520, Step 522, Step 523, Step 524, Step 525, Step 526, Step 527, Step 529, Step 530, Step 531, Step 534, Step 536
/* Definitions for using variables in GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "hash.h"

/* Codes in a variable definition saying where the definition came from.
   Increasing numeric values signify less-overridable definitions.  */
enum variable_origin
  {
    o_default,      /* Variable from the default set.  */
    o_env,      /* Variable from environment.  */
    o_file,     /* Variable given in a makefile.  */
    o_env_override, /* Variable from environment, if -e.  */
    o_command,      /* Variable given by user.  */
    o_override,     /* Variable from an `override' directive.  */
    o_automatic,    /* Automatic variable -- cannot be set.  */
    o_invalid       /* Core dump time.  */
  };

enum variable_flavor
  {
    f_bogus,            /* Bogus (error) */
    f_simple,           /* Simple definition (:=) */
    f_recursive,        /* Recursive definition (=) */
    f_append,           /* Appending definition (+=) */
    f_conditional       /* Conditional definition (?=) */
  };

/* Structure that represents one variable definition.
   Each bucket of the hash table is a chain of these,
   chained through `next'.  */

#define EXP_COUNT_BITS  15      /* This gets all the bitfields into 32 bits */
#define EXP_COUNT_MAX   ((1<<EXP_COUNT_BITS)-1)

struct variable
  {
    char *name;         /* Variable name.  */
    int length;         /* strlen (name) */
    char *value;        /* Variable value.  */
    struct floc fileinfo;       /* Where the variable was defined.  */
    unsigned int recursive:1;   /* Gets recursively re-evaluated.  */
    unsigned int append:1;  /* Nonzero if an appending target-specific
                                   variable.  */
    unsigned int conditional:1; /* Nonzero if set with a ?=. */
    unsigned int per_target:1;  /* Nonzero if a target-specific variable.  */
    unsigned int special:1;     /* Nonzero if this is a special variable.  */
    unsigned int exportable:1;  /* Nonzero if the variable _could_ be
                                   exported.  */
    unsigned int expanding:1;   /* Nonzero if currently being expanded.  */
    unsigned int private_var:1; /* Nonzero avoids inheritance of this
                                   target-specific variable.  */
    unsigned int exp_count:EXP_COUNT_BITS;
                                /* If >1, allow this many self-referential
                                   expansions.  */
    enum variable_flavor
      flavor ENUM_BITFIELD (3); /* Variable flavor.  */
    enum variable_origin
      origin ENUM_BITFIELD (3); /* Variable origin.  */
    enum variable_export
      {
    v_export,       /* Export this variable.  */
    v_noexport,     /* Don't export this variable.  */
    v_ifset,        /* Export it if it has a non-default value.  */
    v_default       /* Decide in target_environment.  */
      } export ENUM_BITFIELD (2);
  };

/* Structure that represents a variable set.  */

struct variable_set
  {
    struct hash_table table;    /* Hash table of variables.  */
  };

/* Structure that represents a list of variable sets.  */

struct variable_set_list
  {
    struct variable_set_list *next; /* Link in the chain.  */
    struct variable_set *set;       /* Variable set.  */
    int next_is_parent;                 /* True if next is a parent target.  */
  };

/* Structure used for pattern-specific variables.  */

struct pattern_var
  {
    struct pattern_var *next;
    const char *suffix;
    const char *target;
    unsigned int len;
    struct variable variable;
  };

extern char *variable_buffer;
extern struct variable_set_list *current_variable_set_list;
extern struct variable *default_goal_var;

/* expand.c */
char *variable_buffer_output (char *ptr, const char *string, unsigned int length);
char *variable_expand (const char *line);
char *variable_expand_for_file (const char *line, struct file *file);
char *allocated_variable_expand_for_file (const char *line, struct file *file);
#define allocated_variable_expand(line) \
  allocated_variable_expand_for_file (line, (struct file *) 0)
char *expand_argument (const char *str, const char *end);
char *variable_expand_string (char *line, const char *string, long length);
void install_variable_buffer (char **bufp, unsigned int *lenp);
void restore_variable_buffer (char *buf, unsigned int len);

/* function.c */
int handle_function (char **op, const char **stringp);
int pattern_matches (const char *pattern, const char *percent, const char *str);
char *subst_expand (char *o, const char *text, const char *subst,
                    const char *replace, unsigned int slen, unsigned int rlen,
                    int by_word);
char *patsubst_expand_pat (char *o, const char *text, const char *pattern,
                           const char *replace, const char *pattern_percent,
                           const char *replace_percent);
char *patsubst_expand (char *o, const char *text, char *pattern, char *replace);

/* expand.c */
char *recursively_expand_for_file (struct variable *v, struct file *file);
#define recursively_expand(v)   recursively_expand_for_file (v, NULL)

/* variable.c */
struct variable_set_list *create_new_variable_set (void);
void free_variable_set (struct variable_set_list *);
struct variable_set_list *push_new_variable_scope (void);
void pop_variable_scope (void);
void define_automatic_variables (void);
void initialize_file_variables (struct file *file, int reading);
void print_file_variables (const struct file *file);
void print_variable_set (struct variable_set *set, char *prefix);
void merge_variable_set_lists (struct variable_set_list **to_list,
                               struct variable_set_list *from_list);
struct variable *do_variable_definition (const struct floc *flocp,
                                         const char *name, const char *value,
                                         enum variable_origin origin,
                                         enum variable_flavor flavor,
                                         int target_var);
char *parse_variable_definition (const char *line,
                                 enum variable_flavor *flavor);
struct variable *assign_variable_definition (struct variable *v, char *line);
struct variable *try_variable_definition (const struct floc *flocp, char *line,
                                          enum variable_origin origin,
                                          int target_var);
void init_hash_global_variable_set (void);
void hash_init_function_table (void);
struct variable *lookup_variable (const char *name, unsigned int length);
struct variable *lookup_variable_in_set (const char *name, unsigned int length,
                                         const struct variable_set *set);

struct variable *define_variable_in_set (const char *name, unsigned int length,
                                         const char *value,
                                         enum variable_origin origin,
                                         int recursive,
                                         struct variable_set *set,
                                         const struct floc *flocp);

/* Define a variable in the current variable set.  */

#define define_variable(n,l,v,o,r) \
          define_variable_in_set((n),(l),(v),(o),(r),\
                                 current_variable_set_list->set,NILF)

/* Define a variable with a constant name in the current variable set.  */

#define define_variable_cname(n,v,o,r) \
          define_variable_in_set((n),(sizeof (n) - 1),(v),(o),(r),\
                                 current_variable_set_list->set,NILF)

/* Define a variable with a location in the current variable set.  */

#define define_variable_loc(n,l,v,o,r,f) \
          define_variable_in_set((n),(l),(v),(o),(r),\
                                 current_variable_set_list->set,(f))

/* Define a variable with a location in the global variable set.  */

#define define_variable_global(n,l,v,o,r,f) \
          define_variable_in_set((n),(l),(v),(o),(r),NULL,(f))

/* Define a variable in FILE's variable set.  */

#define define_variable_for_file(n,l,v,o,r,f) \
          define_variable_in_set((n),(l),(v),(o),(r),(f)->variables->set,NILF)

void undefine_variable_in_set (const char *name, unsigned int length,
                               enum variable_origin origin,
                               struct variable_set *set);

/* Remove variable from the current variable set. */

#define undefine_variable_global(n,l,o) \
          undefine_variable_in_set((n),(l),(o),NULL)

/* Warn that NAME is an undefined variable.  */

#define warn_undefined(n,l) do{\
                              if (warn_undefined_variables_flag) \
                                error (reading_file, \
                                       _("warning: undefined variable `%.*s'"), \
                                (int)(l), (n)); \
                              }while(0)

char **target_environment (struct file *file);

struct pattern_var *create_pattern_var (const char *target,
                                        const char *suffix);

extern int export_all_variables;

#define MAKELEVEL_NAME "MAKELEVEL"
#define MAKELEVEL_LENGTH (sizeof (MAKELEVEL_NAME) - 1)

https://git.savannah.gnu.org/cgit/make.git/tree/job.h?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/job.h used in: Step 519, Step 520, Step 522, Step 523, Step 524, Step 525, Step 526, Step 527, Step 529, Step 530, Step 531, Step 534, Step 538
/* Definitions for managing subprocesses in GNU Make.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#ifndef SEEN_JOB_H
#define SEEN_JOB_H

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#else
# include <sys/file.h>
#endif

/* How to set close-on-exec for a file descriptor.  */

#if !defined F_SETFD
# define CLOSE_ON_EXEC(_d)
#else
# ifndef FD_CLOEXEC
#  define FD_CLOEXEC 1
# endif
# define CLOSE_ON_EXEC(_d) (void) fcntl ((_d), F_SETFD, FD_CLOEXEC)
#endif

/* Structure describing a running or dead child process.  */

struct child
  {
    struct child *next;     /* Link in the chain.  */

    struct file *file;      /* File being remade.  */

    char **environment;     /* Environment for commands.  */

    char **command_lines;   /* Array of variable-expanded cmd lines.  */
    unsigned int command_line;  /* Index into above.  */
    char *command_ptr;      /* Ptr into command_lines[command_line].  */

    pid_t pid;          /* Child process's ID number.  */
#ifdef VMS
    int efn;            /* Completion event flag number */
    int cstatus;        /* Completion status */
    char *comname;              /* Temporary command file name */
#endif
    char *sh_batch_file;        /* Script file for shell commands */
    unsigned int remote:1;  /* Nonzero if executing remotely.  */

    unsigned int noerror:1; /* Nonzero if commands contained a `-'.  */

    unsigned int good_stdin:1;  /* Nonzero if this child has a good stdin.  */
    unsigned int deleted:1; /* Nonzero if targets have been deleted.  */
    unsigned int dontcare:1;    /* Saved dontcare flag.  */
  };

extern struct child *children;

int is_bourne_compatible_shell(const char *path);
void new_job (struct file *file);
void reap_children (int block, int err);
void start_waiting_jobs (void);

char **construct_command_argv (char *line, char **restp, struct file *file,
                               int cmd_flags, char** batch_file);
#ifdef VMS
int child_execute_job (char *argv, struct child *child);
#elif defined(__EMX__)
int child_execute_job (int stdin_fd, int stdout_fd, char **argv, char **envp);
#else
void child_execute_job (int stdin_fd, int stdout_fd, char **argv, char **envp);
#endif
#ifdef _AMIGA
void exec_command (char **argv);
#elif defined(__EMX__)
int exec_command (char **argv, char **envp);
#else
void exec_command (char **argv, char **envp);
#endif

extern unsigned int job_slots_used;

void block_sigs (void);
#ifdef POSIX
void unblock_sigs (void);
#else
#ifdef  HAVE_SIGSETMASK
extern int fatal_signal_mask;
#define unblock_sigs()  sigsetmask (0)
#else
#define unblock_sigs()
#endif
#endif

extern unsigned int jobserver_tokens;

#endif /* SEEN_JOB_H */

https://git.savannah.gnu.org/cgit/make.git/tree/commands.h?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/commands.h used in: Step 519, Step 520, Step 522, Step 523, Step 524, Step 525, Step 526, Step 527, Step 529, Step 530, Step 531, Step 534, Step 538
/* Definition of data structures describing shell commands for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

/* Structure that gives the commands to make a file
   and information about where these commands came from.  */

struct commands
  {
    struct floc fileinfo;   /* Where commands were defined.  */
    char *commands;     /* Commands text.  */
    unsigned int ncommand_lines;/* Number of command lines.  */
    char **command_lines;   /* Commands chopped up into lines.  */
    char *lines_flags;      /* One set of flag bits for each line.  */
    int any_recurse;        /* Nonzero if any `lines_recurse' elt has */
                /* the COMMANDS_RECURSE bit set.  */
  };

/* Bits in `lines_flags'.  */
#define COMMANDS_RECURSE    1 /* Recurses: + or $(MAKE).  */
#define COMMANDS_SILENT     2 /* Silent: @.  */
#define COMMANDS_NOERROR    4 /* No errors: -.  */

void execute_file_commands (struct file *file);
void print_commands (const struct commands *cmds);
void delete_child_targets (struct child *child);
void chop_commands (struct commands *cmds);
void set_file_variables (struct file *file);

https://git.savannah.gnu.org/cgit/make.git/tree/default.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/default.c used in: Step 520
/* Data base of default implicit rules for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"
#include "filedef.h"
#include "variable.h"
#include "rule.h"
#include "dep.h"
#include "job.h"
#include "commands.h"

/* Define GCC_IS_NATIVE if gcc is the native development environment on
   your system (gcc/bison/flex vs cc/yacc/lex).  */
#if defined(__MSDOS__) || defined(__EMX__)
# define GCC_IS_NATIVE
#endif


/* This is the default list of suffixes for suffix rules.
   `.s' must come last, so that a `.o' file will be made from
   a `.c' or `.p' or ... file rather than from a .s file.  */

static char default_suffixes[]
#ifdef VMS
  = ".exe .olb .ln .obj .c .cxx .cc .pas .p .for .f .r .y .l .mar \
.s .ss .i .ii .mod .sym .def .h .info .dvi .tex .texinfo .texi .txinfo \
.w .ch .cweb .web .com .sh .elc .el";
#elif defined(__EMX__)
  = ".out .a .ln .o .c .cc .C .cpp .p .f .F .m .r .y .l .ym .yl .s .S \
.mod .sym .def .h .info .dvi .tex .texinfo .texi .txinfo \
.w .ch .web .sh .elc .el .obj .exe .dll .lib";
#else
  = ".out .a .ln .o .c .cc .C .cpp .p .f .F .m .r .y .l .ym .yl .s .S \
.mod .sym .def .h .info .dvi .tex .texinfo .texi .txinfo \
.w .ch .web .sh .elc .el";
#endif

static struct pspec default_pattern_rules[] =
  {
    { "(%)", "%",
    "$(AR) $(ARFLAGS) $@ $<" },

    /* The X.out rules are only in BSD's default set because
       BSD Make has no null-suffix rules, so `foo.out' and
       `foo' are the same thing.  */
#ifdef VMS
    { "%.exe", "%",
        "copy $< $@" },
#else
    { "%.out", "%",
    "@rm -f $@ \n cp $< $@" },
#endif
    /* Syntax is "ctangle foo.w foo.ch foo.c".  */
    { "%.c", "%.w %.ch",
    "$(CTANGLE) $^ $@" },
    { "%.tex", "%.w %.ch",
    "$(CWEAVE) $^ $@" },

    { 0, 0, 0 }
  };

static struct pspec default_terminal_rules[] =
  {
#ifdef VMS
    /* RCS.  */
    { "%", "%$$5lv", /* Multinet style */
        "if f$$search($@) .nes. \"\" then +$(CHECKOUT,v)" },
    { "%", "[.$$rcs]%$$5lv", /* Multinet style */
        "if f$$search($@) .nes. \"\" then +$(CHECKOUT,v)" },
    { "%", "%_v", /* Normal style */
        "if f$$search($@) .nes. \"\" then +$(CHECKOUT,v)" },
    { "%", "[.rcs]%_v", /* Normal style */
        "if f$$search($@) .nes. \"\" then +$(CHECKOUT,v)" },

    /* SCCS.  */
    /* ain't no SCCS on vms */
#else
    /* RCS.  */
    { "%", "%,v",
    "$(CHECKOUT,v)" },
    { "%", "RCS/%,v",
    "$(CHECKOUT,v)" },
    { "%", "RCS/%",
    "$(CHECKOUT,v)" },

    /* SCCS.  */
    { "%", "s.%",
    "$(GET) $(GFLAGS) $(SCCS_OUTPUT_OPTION) $<" },
    { "%", "SCCS/s.%",
    "$(GET) $(GFLAGS) $(SCCS_OUTPUT_OPTION) $<" },
#endif /* !VMS */
    { 0, 0, 0 }
  };

static char *default_suffix_rules[] =
  {
#ifdef VMS
    ".obj.exe",
    "$(LINK.obj) $^ $(LOADLIBES) $(LDLIBS) $(CRT0) /exe=$@",
    ".mar.exe",
    "$(COMPILE.mar) $^ \n $(LINK.obj) $(subst .mar,.obj,$^) $(LOADLIBES) $(LDLIBS) $(CRT0) /exe=$@",
    ".s.exe",
    "$(COMPILE.s) $^ \n $(LINK.obj) $(subst .s,.obj,$^) $(LOADLIBES) $(LDLIBS) $(CRT0) /exe=$@",
    ".c.exe",
    "$(COMPILE.c) $^ \n $(LINK.obj) $(subst .c,.obj,$^) $(LOADLIBES) $(LDLIBS) $(CRT0) /exe=$@",
    ".cc.exe",
#ifdef GCC_IS_NATIVE
    "$(COMPILE.cc) $^ \n $(LINK.obj) $(CXXSTARTUP),sys$$disk:[]$(subst .cc,.obj,$^) $(LOADLIBES) $(LXLIBS) $(LDLIBS) $(CXXRT0) /exe=$@",
#else
    "$(COMPILE.cc) $^ \n $(CXXLINK.obj) $(subst .cc,.obj,$^) $(LOADLIBES) $(LXLIBS) $(LDLIBS) $(CXXRT0) /exe=$@",
    ".cxx.exe",
    "$(COMPILE.cxx) $^ \n $(CXXLINK.obj) $(subst .cxx,.obj,$^) $(LOADLIBES) $(LXLIBS) $(LDLIBS) $(CXXRT0) /exe=$@",
#endif
    ".for.exe",
    "$(COMPILE.for) $^ \n $(LINK.obj) $(subst .for,.obj,$^) $(LOADLIBES) $(LDLIBS) /exe=$@",
    ".pas.exe",
    "$(COMPILE.pas) $^ \n $(LINK.obj) $(subst .pas,.obj,$^) $(LOADLIBES) $(LDLIBS) /exe=$@",

    ".com",
    "copy $< >$@",

    ".mar.obj",
    "$(COMPILE.mar) /obj=$@ $<",
    ".s.obj",
    "$(COMPILE.s) /obj=$@ $<",
    ".ss.obj",
    "$(COMPILE.s) /obj=$@ $<",
    ".c.i",
    "$(COMPILE.c)/prep /list=$@ $<",
    ".c.s",
    "$(COMPILE.c)/noobj/machine /list=$@ $<",
    ".i.s",
    "$(COMPILE.c)/noprep/noobj/machine /list=$@ $<",
    ".c.obj",
    "$(COMPILE.c) /obj=$@ $<",
    ".cc.ii",
    "$(COMPILE.cc)/prep /list=$@ $<",
    ".cc.ss",
    "$(COMPILE.cc)/noobj/machine /list=$@ $<",
    ".ii.ss",
    "$(COMPILE.cc)/noprep/noobj/machine /list=$@ $<",
    ".cc.obj",
    "$(COMPILE.cc) /obj=$@ $<",
    ".cxx.obj",
    "$(COMPILE.cxx) /obj=$@ $<",
    ".for.obj",
    "$(COMPILE.for) /obj=$@ $<",
    ".pas.obj",
    "$(COMPILE.pas) /obj=$@ $<",

    ".y.c",
    "$(YACC.y) $< \n rename y_tab.c $@",
    ".l.c",
    "$(LEX.l) $< \n rename lexyy.c $@",

    ".texinfo.info",
    "$(MAKEINFO) $<",

    ".tex.dvi",
    "$(TEX) $<",

#else /* ! VMS */

    ".o",
    "$(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@",
    ".s",
    "$(LINK.s) $^ $(LOADLIBES) $(LDLIBS) -o $@",
    ".S",
    "$(LINK.S) $^ $(LOADLIBES) $(LDLIBS) -o $@",
    ".c",
    "$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@",
    ".cc",
    "$(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@",
    ".C",
    "$(LINK.C) $^ $(LOADLIBES) $(LDLIBS) -o $@",
    ".cpp",
    "$(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@",
    ".f",
    "$(LINK.f) $^ $(LOADLIBES) $(LDLIBS) -o $@",
    ".m",
    "$(LINK.m) $^ $(LOADLIBES) $(LDLIBS) -o $@",
    ".p",
    "$(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@",
    ".F",
    "$(LINK.F) $^ $(LOADLIBES) $(LDLIBS) -o $@",
    ".r",
    "$(LINK.r) $^ $(LOADLIBES) $(LDLIBS) -o $@",
    ".mod",
    "$(COMPILE.mod) -o $@ -e $@ $^",

    ".def.sym",
    "$(COMPILE.def) -o $@ $<",

    ".sh",
    "cat $< >$@ \n chmod a+x $@",

    ".s.o",
    "$(COMPILE.s) -o $@ $<",
    ".S.o",
    "$(COMPILE.S) -o $@ $<",
    ".c.o",
    "$(COMPILE.c) $(OUTPUT_OPTION) $<",
    ".cc.o",
    "$(COMPILE.cc) $(OUTPUT_OPTION) $<",
    ".C.o",
    "$(COMPILE.C) $(OUTPUT_OPTION) $<",
    ".cpp.o",
    "$(COMPILE.cpp) $(OUTPUT_OPTION) $<",
    ".f.o",
    "$(COMPILE.f) $(OUTPUT_OPTION) $<",
    ".m.o",
    "$(COMPILE.m) $(OUTPUT_OPTION) $<",
    ".p.o",
    "$(COMPILE.p) $(OUTPUT_OPTION) $<",
    ".F.o",
    "$(COMPILE.F) $(OUTPUT_OPTION) $<",
    ".r.o",
    "$(COMPILE.r) $(OUTPUT_OPTION) $<",
    ".mod.o",
    "$(COMPILE.mod) -o $@ $<",

    ".c.ln",
    "$(LINT.c) -C$* $<",
    ".y.ln",
#ifndef __MSDOS__
    "$(YACC.y) $< \n $(LINT.c) -C$* y.tab.c \n $(RM) y.tab.c",
#else
    "$(YACC.y) $< \n $(LINT.c) -C$* y_tab.c \n $(RM) y_tab.c",
#endif
    ".l.ln",
    "@$(RM) $*.c\n $(LEX.l) $< > $*.c\n$(LINT.c) -i $*.c -o $@\n $(RM) $*.c",

    ".y.c",
#ifndef __MSDOS__
    "$(YACC.y) $< \n mv -f y.tab.c $@",
#else
    "$(YACC.y) $< \n mv -f y_tab.c $@",
#endif
    ".l.c",
    "@$(RM) $@ \n $(LEX.l) $< > $@",
    ".ym.m",
    "$(YACC.m) $< \n mv -f y.tab.c $@",
    ".lm.m",
    "@$(RM) $@ \n $(LEX.m) $< > $@",

    ".F.f",
    "$(PREPROCESS.F) $(OUTPUT_OPTION) $<",
    ".r.f",
    "$(PREPROCESS.r) $(OUTPUT_OPTION) $<",

    /* This might actually make lex.yy.c if there's no %R% directive in $*.l,
       but in that case why were you trying to make $*.r anyway?  */
    ".l.r",
    "$(LEX.l) $< > $@ \n mv -f lex.yy.r $@",

    ".S.s",
    "$(PREPROCESS.S) $< > $@",

    ".texinfo.info",
    "$(MAKEINFO) $(MAKEINFO_FLAGS) $< -o $@",

    ".texi.info",
    "$(MAKEINFO) $(MAKEINFO_FLAGS) $< -o $@",

    ".txinfo.info",
    "$(MAKEINFO) $(MAKEINFO_FLAGS) $< -o $@",

    ".tex.dvi",
    "$(TEX) $<",

    ".texinfo.dvi",
    "$(TEXI2DVI) $(TEXI2DVI_FLAGS) $<",

    ".texi.dvi",
    "$(TEXI2DVI) $(TEXI2DVI_FLAGS) $<",

    ".txinfo.dvi",
    "$(TEXI2DVI) $(TEXI2DVI_FLAGS) $<",

    ".w.c",
    "$(CTANGLE) $< - $@",   /* The `-' says there is no `.ch' file.  */

    ".web.p",
    "$(TANGLE) $<",

    ".w.tex",
    "$(CWEAVE) $< - $@",    /* The `-' says there is no `.ch' file.  */

    ".web.tex",
    "$(WEAVE) $<",

#endif /* !VMS */

    0, 0,
  };

static const char *default_variables[] =
  {
#ifdef VMS
#ifdef __ALPHA
    "ARCH", "ALPHA",
#endif
#ifdef __ia64
    "ARCH", "IA64",
#endif
#ifdef __VAX
    "ARCH", "VAX",
#endif
    "AR", "library/obj",
    "ARFLAGS", "/replace",
    "AS", "macro",
    "MACRO", "macro",
#ifdef GCC_IS_NATIVE
    "CC", "gcc",
#else
    "CC", "cc",
#endif
    "CD", "builtin_cd",
    "MAKE", "make",
    "ECHO", "write sys$$output \"",
#ifdef GCC_IS_NATIVE
    "C++", "gcc/plus",
    "CXX", "gcc/plus",
#else
    "C++", "cxx",
    "CXX", "cxx",
    "CXXLD", "cxxlink",
#endif
    "CO", "co",
    "CPP", "$(CC) /preprocess_only",
    "FC", "fortran",
    /* System V uses these, so explicit rules using them should work.
       However, there is no way to make implicit rules use them and FC.  */
    "F77", "$(FC)",
    "F77FLAGS", "$(FFLAGS)",
    "LD", "link",
    "LEX", "lex",
    "PC", "pascal",
    "YACC", "bison/yacc",
    "YFLAGS", "/Define/Verbose",
    "BISON", "bison",
    "MAKEINFO", "makeinfo",
    "TEX", "tex",
    "TEXINDEX", "texindex",

    "RM", "delete/nolog",

    "CSTARTUP", "",
#ifdef GCC_IS_NATIVE
    "CRT0", ",sys$$library:vaxcrtl.olb/lib,gnu_cc_library:crt0.obj",
    "CXXSTARTUP", "gnu_cc_library:crtbegin.obj",
    "CXXRT0", ",sys$$library:vaxcrtl.olb/lib,gnu_cc_library:crtend.obj,gnu_cc_library:gxx_main.obj",
    "LXLIBS", ",gnu_cc_library:libstdcxx.olb/lib,gnu_cc_library:libgccplus.olb/lib",
    "LDLIBS", ",gnu_cc_library:libgcc.olb/lib",
#else
    "CRT0", "",
    "CXXSTARTUP", "",
    "CXXRT0", "",
    "LXLIBS", "",
    "LDLIBS", "",
#endif

    "LINK.obj", "$(LD) $(LDFLAGS)",
#ifndef GCC_IS_NATIVE
    "CXXLINK.obj", "$(CXXLD) $(LDFLAGS)",
    "COMPILE.cxx", "$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH)",
#endif
    "COMPILE.c", "$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH)",
    "COMPILE.cc", "$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH)",
    "YACC.y", "$(YACC) $(YFLAGS)",
    "LEX.l", "$(LEX) $(LFLAGS)",
    "COMPILE.for", "$(FC) $(FFLAGS) $(TARGET_ARCH)",
    "COMPILE.pas", "$(PC) $(PFLAGS) $(CPPFLAGS) $(TARGET_ARCH)",
    "COMPILE.mar", "$(MACRO) $(MACROFLAGS)",
    "COMPILE.s", "$(AS) $(ASFLAGS) $(TARGET_MACH)",
    "LINT.c", "$(LINT) $(LINTFLAGS) $(CPPFLAGS) $(TARGET_ARCH)",

    "MV", "rename/new_version",
    "CP", "copy",

#else /* !VMS */

    "AR", "ar",
    "ARFLAGS", "rv",
    "AS", "as",
#ifdef GCC_IS_NATIVE
    "CC", "gcc",
# ifdef __MSDOS__
    "CXX", "gpp",   /* g++ is an invalid name on MSDOS */
# else
    "CXX", "gcc",
# endif /* __MSDOS__ */
    "OBJC", "gcc",
#else
    "CC", "cc",
    "CXX", "g++",
    "OBJC", "cc",
#endif

    /* This expands to $(CO) $(COFLAGS) $< $@ if $@ does not exist,
       and to the empty string if $@ does exist.  */
    "CHECKOUT,v", "+$(if $(wildcard $@),,$(CO) $(COFLAGS) $< $@)",
    "CO", "co",
    "COFLAGS", "",

    "CPP", "$(CC) -E",
#ifdef  CRAY
    "CF77PPFLAGS", "-P",
    "CF77PP", "/lib/cpp",
    "CFT", "cft77",
    "CF", "cf77",
    "FC", "$(CF)",
#else   /* Not CRAY.  */
#ifdef  _IBMR2
    "FC", "xlf",
#else
#ifdef  __convex__
    "FC", "fc",
#else
    "FC", "f77",
#endif /* __convex__ */
#endif /* _IBMR2 */
    /* System V uses these, so explicit rules using them should work.
       However, there is no way to make implicit rules use them and FC.  */
    "F77", "$(FC)",
    "F77FLAGS", "$(FFLAGS)",
#endif  /* Cray.  */
    "GET", SCCS_GET,
    "LD", "ld",
#ifdef GCC_IS_NATIVE
    "LEX", "flex",
#else
    "LEX", "lex",
#endif
    "LINT", "lint",
    "M2C", "m2c",
#ifdef  pyr
    "PC", "pascal",
#else
#ifdef  CRAY
    "PC", "PASCAL",
    "SEGLDR", "segldr",
#else
    "PC", "pc",
#endif  /* CRAY.  */
#endif  /* pyr.  */
#ifdef GCC_IS_NATIVE
    "YACC", "bison -y",
#else
    "YACC", "yacc", /* Or "bison -y"  */
#endif
    "MAKEINFO", "makeinfo",
    "TEX", "tex",
    "TEXI2DVI", "texi2dvi",
    "WEAVE", "weave",
    "CWEAVE", "cweave",
    "TANGLE", "tangle",
    "CTANGLE", "ctangle",

    "RM", "rm -f",

    "LINK.o", "$(CC) $(LDFLAGS) $(TARGET_ARCH)",
    "COMPILE.c", "$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c",
    "LINK.c", "$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
    "COMPILE.m", "$(OBJC) $(OBJCFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c",
    "LINK.m", "$(OBJC) $(OBJCFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
    "COMPILE.cc", "$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c",
    "COMPILE.C", "$(COMPILE.cc)",
    "COMPILE.cpp", "$(COMPILE.cc)",
    "LINK.cc", "$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
    "LINK.C", "$(LINK.cc)",
    "LINK.cpp", "$(LINK.cc)",
    "YACC.y", "$(YACC) $(YFLAGS)",
    "LEX.l", "$(LEX) $(LFLAGS) -t",
    "YACC.m", "$(YACC) $(YFLAGS)",
    "LEX.m", "$(LEX) $(LFLAGS) -t",
    "COMPILE.f", "$(FC) $(FFLAGS) $(TARGET_ARCH) -c",
    "LINK.f", "$(FC) $(FFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
    "COMPILE.F", "$(FC) $(FFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c",
    "LINK.F", "$(FC) $(FFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
    "COMPILE.r", "$(FC) $(FFLAGS) $(RFLAGS) $(TARGET_ARCH) -c",
    "LINK.r", "$(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
    "COMPILE.def", "$(M2C) $(M2FLAGS) $(DEFFLAGS) $(TARGET_ARCH)",
    "COMPILE.mod", "$(M2C) $(M2FLAGS) $(MODFLAGS) $(TARGET_ARCH)",
    "COMPILE.p", "$(PC) $(PFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c",
    "LINK.p", "$(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)",
    "LINK.s", "$(CC) $(ASFLAGS) $(LDFLAGS) $(TARGET_MACH)",
    "COMPILE.s", "$(AS) $(ASFLAGS) $(TARGET_MACH)",
    "LINK.S", "$(CC) $(ASFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_MACH)",
    "COMPILE.S", "$(CC) $(ASFLAGS) $(CPPFLAGS) $(TARGET_MACH) -c",
    "PREPROCESS.S", "$(CC) -E $(CPPFLAGS)",
    "PREPROCESS.F", "$(FC) $(FFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -F",
    "PREPROCESS.r", "$(FC) $(FFLAGS) $(RFLAGS) $(TARGET_ARCH) -F",
    "LINT.c", "$(LINT) $(LINTFLAGS) $(CPPFLAGS) $(TARGET_ARCH)",

#ifndef NO_MINUS_C_MINUS_O
    "OUTPUT_OPTION", "-o $@",
#endif

#ifdef  SCCS_GET_MINUS_G
    "SCCS_OUTPUT_OPTION", "-G$@",
#endif

#ifdef _AMIGA
    ".LIBPATTERNS", "%.lib",
#else
#ifdef __MSDOS__
    ".LIBPATTERNS", "lib%.a $(DJDIR)/lib/lib%.a",
#else
    ".LIBPATTERNS", "lib%.so lib%.a",
#endif
#endif

#endif /* !VMS */
    0, 0
  };


/* Set up the default .SUFFIXES list.  */

void
set_default_suffixes (void)
{
  suffix_file = enter_file (strcache_add (".SUFFIXES"));

  if (no_builtin_rules_flag)
    define_variable_cname ("SUFFIXES", "", o_default, 0);
  else
    {
      char *p = default_suffixes;
      suffix_file->deps = enter_prereqs(PARSE_FILE_SEQ (&p, struct dep, '\0',
                                                        NULL, 0),
                                        NULL);
      define_variable_cname ("SUFFIXES", default_suffixes, o_default, 0);
    }
}

/* Enter the default suffix rules as file rules.  This used to be done in
   install_default_implicit_rules, but that loses because we want the
   suffix rules installed before reading makefiles, and the pattern rules
   installed after.  */

void
install_default_suffix_rules (void)
{
  char **s;

  if (no_builtin_rules_flag)
    return;

  for (s = default_suffix_rules; *s != 0; s += 2)
    {
      struct file *f = enter_file (strcache_add (s[0]));
      /* Don't clobber cmds given in a makefile if there were any.  */
      if (f->cmds == 0)
    {
      f->cmds = xmalloc (sizeof (struct commands));
      f->cmds->fileinfo.filenm = 0;
      f->cmds->commands = s[1];
      f->cmds->command_lines = 0;
    }
    }
}


/* Install the default pattern rules.  */

void
install_default_implicit_rules (void)
{
  struct pspec *p;

  if (no_builtin_rules_flag)
    return;

  for (p = default_pattern_rules; p->target != 0; ++p)
    install_pattern_rule (p, 0);

  for (p = default_terminal_rules; p->target != 0; ++p)
    install_pattern_rule (p, 1);
}

void
define_default_variables (void)
{
  const char **s;

  if (no_builtin_variables_flag)
    return;

  for (s = default_variables; *s != 0; s += 2)
    define_variable (s[0], strlen (s[0]), s[1], o_default, 1);
}

https://git.savannah.gnu.org/cgit/make.git/tree/rule.h?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/rule.h used in: Step 520, Step 522, Step 525, Step 527, Step 529, Step 531, Step 534
/* Definitions for using pattern rules in GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */


/* Structure used for pattern (implicit) rules.  */

struct rule
  {
    struct rule *next;
    const char **targets;   /* Targets of the rule.  */
    unsigned int *lens;     /* Lengths of each target.  */
    const char **suffixes;  /* Suffixes (after `%') of each target.  */
    struct dep *deps;       /* Dependencies of the rule.  */
    struct commands *cmds;  /* Commands to execute.  */
    unsigned short num;         /* Number of targets.  */
    char terminal;      /* If terminal (double-colon).  */
    char in_use;        /* If in use by a parent pattern_search.  */
  };

/* For calling install_pattern_rule.  */
struct pspec
  {
    char *target, *dep, *commands;
  };


extern struct rule *pattern_rules;
extern struct rule *last_pattern_rule;
extern unsigned int num_pattern_rules;

extern unsigned int max_pattern_deps;
extern unsigned int max_pattern_targets;
extern unsigned int max_pattern_dep_length;

extern struct file *suffix_file;
extern unsigned int maxsuffix;


void count_implicit_rule_limits (void);
void convert_to_pattern (void);
void install_pattern_rule (struct pspec *p, int terminal);
void create_pattern_rule (const char **targets, const char **target_percents,
                          unsigned int num, int terminal, struct dep *deps,
                          struct commands *commands, int override);

https://git.savannah.gnu.org/cgit/make.git/tree/dir.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/dir.c used in: Step 521
/* Directory hashing for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"
#include "hash.h"

#ifdef  HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
# ifdef VMS
/* its prototype is in vmsdir.h, which is not needed for HAVE_DIRENT_H */
const char *vmsify (const char *name, int type);
# endif
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# ifdef HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
# ifdef HAVE_NDIR_H
#  include <ndir.h>
# endif
# ifdef HAVE_VMSDIR_H
#  include "vmsdir.h"
# endif /* HAVE_VMSDIR_H */
#endif

/* In GNU systems, <dirent.h> defines this macro for us.  */
#ifdef _D_NAMLEN
# undef NAMLEN
# define NAMLEN(d) _D_NAMLEN(d)
#endif

#if (defined (POSIX) || defined (VMS) || defined (WINDOWS32)) && !defined (__GNU_LIBRARY__)
/* Posix does not require that the d_ino field be present, and some
   systems do not provide it. */
# define REAL_DIR_ENTRY(dp) 1
# define FAKE_DIR_ENTRY(dp)
#else
# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
# define FAKE_DIR_ENTRY(dp) (dp->d_ino = 1)
#endif /* POSIX */


#ifdef __MSDOS__
#include <ctype.h>
#include <fcntl.h>

/* If it's MSDOS that doesn't have _USE_LFN, disable LFN support.  */
#ifndef _USE_LFN
#define _USE_LFN 0
#endif

static const char *
dosify (const char *filename)
{
  static char dos_filename[14];
  char *df;
  int i;

  if (filename == 0 || _USE_LFN)
    return filename;

  /* FIXME: what about filenames which violate
     8+3 constraints, like "config.h.in", or ".emacs"?  */
  if (strpbrk (filename, "\"*+,;<=>?[\\]|") != 0)
    return filename;

  df = dos_filename;

  /* First, transform the name part.  */
  for (i = 0; *filename != '\0' && i < 8 && *filename != '.'; ++i)
    *df++ = tolower ((unsigned char)*filename++);

  /* Now skip to the next dot.  */
  while (*filename != '\0' && *filename != '.')
    ++filename;
  if (*filename != '\0')
    {
      *df++ = *filename++;
      for (i = 0; *filename != '\0' && i < 3 && *filename != '.'; ++i)
    *df++ = tolower ((unsigned char)*filename++);
    }

  /* Look for more dots.  */
  while (*filename != '\0' && *filename != '.')
    ++filename;
  if (*filename == '.')
    return filename;
  *df = 0;
  return dos_filename;
}
#endif /* __MSDOS__ */

#ifdef WINDOWS32
#include "pathstuff.h"
#endif

#ifdef _AMIGA
#include <ctype.h>
#endif

#ifdef HAVE_CASE_INSENSITIVE_FS
static const char *
downcase (const char *filename)
{
  static PATH_VAR (new_filename);
  char *df;

  if (filename == 0)
    return 0;

  df = new_filename;
  while (*filename != '\0')
    {
      *df++ = tolower ((unsigned char)*filename);
      ++filename;
    }

  *df = 0;

  return new_filename;
}
#endif /* HAVE_CASE_INSENSITIVE_FS */

#ifdef VMS

static int
vms_hash (const char *name)
{
  int h = 0;
  int g;

  while (*name)
    {
      unsigned char uc = *name;
#ifdef HAVE_CASE_INSENSITIVE_FS
      h = (h << 4) + (isupper (uc) ? tolower (uc) : uc);
#else
      h = (h << 4) + uc;
#endif
      name++;
      g = h & 0xf0000000;
      if (g)
    {
      h = h ^ (g >> 24);
      h = h ^ g;
    }
    }
  return h;
}

/* fake stat entry for a directory */
static int
vmsstat_dir (const char *name, struct stat *st)
{
  char *s;
  int h;
  DIR *dir;

  dir = opendir (name);
  if (dir == 0)
    return -1;
  closedir (dir);
  s = strchr (name, ':');   /* find device */
  if (s)
    {
      /* to keep the compiler happy we said "const char *name", now we cheat */
      *s++ = 0;
      st->st_dev = (char *)vms_hash (name);
      h = vms_hash (s);
      *(s-1) = ':';
    }
  else
    {
      st->st_dev = 0;
      h = vms_hash (name);
    }

  st->st_ino[0] = h & 0xff;
  st->st_ino[1] = h & 0xff00;
  st->st_ino[2] = h >> 16;

  return 0;
}
#endif /* VMS */


/* Hash table of directories.  */

#ifndef DIRECTORY_BUCKETS
#define DIRECTORY_BUCKETS 199
#endif

struct directory_contents
  {
    dev_t dev;          /* Device and inode numbers of this dir.  */
#ifdef WINDOWS32
    /* Inode means nothing on WINDOWS32. Even file key information is
     * unreliable because it is random per file open and undefined for remote
     * filesystems. The most unique attribute I can come up with is the fully
     * qualified name of the directory. Beware though, this is also
     * unreliable. I'm open to suggestion on a better way to emulate inode.  */
    char *path_key;
    int   ctime;
    int   mtime;        /* controls check for stale directory cache */
    int   fs_flags;     /* FS_FAT, FS_NTFS, ... */
# define FS_FAT      0x1
# define FS_NTFS     0x2
# define FS_UNKNOWN  0x4
#else
# ifdef VMS
    ino_t ino[3];
# else
    ino_t ino;
# endif
#endif /* WINDOWS32 */
    struct hash_table dirfiles; /* Files in this directory.  */
    DIR *dirstream;     /* Stream reading this directory.  */
  };

static unsigned long
directory_contents_hash_1 (const void *key_0)
{
  const struct directory_contents *key = key_0;
  unsigned long hash;

#ifdef WINDOWS32
  hash = 0;
  ISTRING_HASH_1 (key->path_key, hash);
  hash ^= ((unsigned int) key->dev << 4) ^ (unsigned int) key->ctime;
#else
# ifdef VMS
  hash = (((unsigned int) key->dev << 4)
      ^ ((unsigned int) key->ino[0]
         + (unsigned int) key->ino[1]
         + (unsigned int) key->ino[2]));
# else
  hash = ((unsigned int) key->dev << 4) ^ (unsigned int) key->ino;
# endif
#endif /* WINDOWS32 */
  return hash;
}

static unsigned long
directory_contents_hash_2 (const void *key_0)
{
  const struct directory_contents *key = key_0;
  unsigned long hash;

#ifdef WINDOWS32
  hash = 0;
  ISTRING_HASH_2 (key->path_key, hash);
  hash ^= ((unsigned int) key->dev << 4) ^ (unsigned int) ~key->ctime;
#else
# ifdef VMS
  hash = (((unsigned int) key->dev << 4)
      ^ ~((unsigned int) key->ino[0]
          + (unsigned int) key->ino[1]
          + (unsigned int) key->ino[2]));
# else
  hash = ((unsigned int) key->dev << 4) ^ (unsigned int) ~key->ino;
# endif
#endif /* WINDOWS32 */

  return hash;
}

/* Sometimes it's OK to use subtraction to get this value:
     result = X - Y;
   But, if we're not sure of the type of X and Y they may be too large for an
   int (on a 64-bit system for example).  So, use ?: instead.
   See Savannah bug #15534.

   NOTE!  This macro has side-effects!
*/

#define MAKECMP(_x,_y)  ((_x)<(_y)?-1:((_x)==(_y)?0:1))

static int
directory_contents_hash_cmp (const void *xv, const void *yv)
{
  const struct directory_contents *x = xv;
  const struct directory_contents *y = yv;
  int result;

#ifdef WINDOWS32
  ISTRING_COMPARE (x->path_key, y->path_key, result);
  if (result)
    return result;
  result = MAKECMP(x->ctime, y->ctime);
  if (result)
    return result;
#else
# ifdef VMS
  result = MAKECMP(x->ino[0], y->ino[0]);
  if (result)
    return result;
  result = MAKECMP(x->ino[1], y->ino[1]);
  if (result)
    return result;
  result = MAKECMP(x->ino[2], y->ino[2]);
  if (result)
    return result;
# else
  result = MAKECMP(x->ino, y->ino);
  if (result)
    return result;
# endif
#endif /* WINDOWS32 */

  return MAKECMP(x->dev, y->dev);
}

/* Table of directory contents hashed by device and inode number.  */
static struct hash_table directory_contents;

struct directory
  {
    const char *name;           /* Name of the directory.  */

    /* The directory's contents.  This data may be shared by several
       entries in the hash table, which refer to the same directory
       (identified uniquely by `dev' and `ino') under different names.  */
    struct directory_contents *contents;
  };

static unsigned long
directory_hash_1 (const void *key)
{
  return_ISTRING_HASH_1 (((const struct directory *) key)->name);
}

static unsigned long
directory_hash_2 (const void *key)
{
  return_ISTRING_HASH_2 (((const struct directory *) key)->name);
}

static int
directory_hash_cmp (const void *x, const void *y)
{
  return_ISTRING_COMPARE (((const struct directory *) x)->name,
              ((const struct directory *) y)->name);
}

/* Table of directories hashed by name.  */
static struct hash_table directories;

/* Never have more than this many directories open at once.  */

#define MAX_OPEN_DIRECTORIES 10

static unsigned int open_directories = 0;


/* Hash table of files in each directory.  */

struct dirfile
  {
    const char *name;       /* Name of the file.  */
    short length;
    short impossible;       /* This file is impossible.  */
  };

static unsigned long
dirfile_hash_1 (const void *key)
{
  return_ISTRING_HASH_1 (((struct dirfile const *) key)->name);
}

static unsigned long
dirfile_hash_2 (const void *key)
{
  return_ISTRING_HASH_2 (((struct dirfile const *) key)->name);
}

static int
dirfile_hash_cmp (const void *xv, const void *yv)
{
  const struct dirfile *x = xv;
  const struct dirfile *y = yv;
  int result = x->length - y->length;
  if (result)
    return result;
  return_ISTRING_COMPARE (x->name, y->name);
}

#ifndef DIRFILE_BUCKETS
#define DIRFILE_BUCKETS 107
#endif


static int dir_contents_file_exists_p (struct directory_contents *dir,
                                       const char *filename);
static struct directory *find_directory (const char *name);

/* Find the directory named NAME and return its `struct directory'.  */

static struct directory *
find_directory (const char *name)
{
  const char *p;
  struct directory *dir;
  struct directory **dir_slot;
  struct directory dir_key;
  int r;
#ifdef WINDOWS32
  char* w32_path;
  char  fs_label[BUFSIZ];
  char  fs_type[BUFSIZ];
  unsigned long  fs_serno;
  unsigned long  fs_flags;
  unsigned long  fs_len;
#endif
#ifdef VMS
  if ((*name == '.') && (*(name+1) == 0))
    name = "[]";
  else
    name = vmsify (name,1);
#endif

  dir_key.name = name;
  dir_slot = (struct directory **) hash_find_slot (&directories, &dir_key);
  dir = *dir_slot;

  if (HASH_VACANT (dir))
    {
      struct stat st;

      /* The directory was not found.  Create a new entry for it.  */

      p = name + strlen (name);
      dir = xmalloc (sizeof (struct directory));
#if defined(HAVE_CASE_INSENSITIVE_FS) && defined(VMS)
      dir->name = strcache_add_len (downcase(name), p - name);
#else
      dir->name = strcache_add_len (name, p - name);
#endif
      hash_insert_at (&directories, dir, dir_slot);
      /* The directory is not in the name hash table.
     Find its device and inode numbers, and look it up by them.  */

#ifdef VMS
      r = vmsstat_dir (name, &st);
#elif defined(WINDOWS32)
      {
        char tem[MAXPATHLEN], *tstart, *tend;

        /* Remove any trailing slashes.  Windows32 stat fails even on
           valid directories if they end in a slash. */
        memcpy (tem, name, p - name + 1);
        tstart = tem;
        if (tstart[1] == ':')
          tstart += 2;
        for (tend = tem + (p - name - 1);
             tend > tstart && (*tend == '/' || *tend == '\\');
             tend--)
          *tend = '\0';

        r = stat (tem, &st);
      }
#else
      EINTRLOOP (r, stat (name, &st));
#endif

      if (r < 0)
        {
    /* Couldn't stat the directory.  Mark this by
       setting the `contents' member to a nil pointer.  */
      dir->contents = 0;
    }
      else
    {
      /* Search the contents hash table; device and inode are the key.  */

      struct directory_contents *dc;
      struct directory_contents **dc_slot;
      struct directory_contents dc_key;

      dc_key.dev = st.st_dev;
#ifdef WINDOWS32
      dc_key.path_key = w32_path = w32ify (name, 1);
      dc_key.ctime = st.st_ctime;
#else
# ifdef VMS
      dc_key.ino[0] = st.st_ino[0];
      dc_key.ino[1] = st.st_ino[1];
      dc_key.ino[2] = st.st_ino[2];
# else
      dc_key.ino = st.st_ino;
# endif
#endif
      dc_slot = (struct directory_contents **) hash_find_slot (&directory_contents, &dc_key);
      dc = *dc_slot;

      if (HASH_VACANT (dc))
        {
          /* Nope; this really is a directory we haven't seen before.  */

          dc = (struct directory_contents *)
        xmalloc (sizeof (struct directory_contents));

          /* Enter it in the contents hash table.  */
          dc->dev = st.st_dev;
#ifdef WINDOWS32
              dc->path_key = xstrdup (w32_path);
          dc->ctime = st.st_ctime;
              dc->mtime = st.st_mtime;

              /*
               * NTFS is the only WINDOWS32 filesystem that bumps mtime
               * on a directory when files are added/deleted from
               * a directory.
               */
              w32_path[3] = '\0';
              if (GetVolumeInformation(w32_path,
                     fs_label, sizeof (fs_label),
                     &fs_serno, &fs_len,
                     &fs_flags, fs_type, sizeof (fs_type)) == FALSE)
                dc->fs_flags = FS_UNKNOWN;
              else if (!strcmp(fs_type, "FAT"))
                dc->fs_flags = FS_FAT;
              else if (!strcmp(fs_type, "NTFS"))
                dc->fs_flags = FS_NTFS;
              else
                dc->fs_flags = FS_UNKNOWN;
#else
# ifdef VMS
          dc->ino[0] = st.st_ino[0];
          dc->ino[1] = st.st_ino[1];
          dc->ino[2] = st.st_ino[2];
# else
          dc->ino = st.st_ino;
# endif
#endif /* WINDOWS32 */
          hash_insert_at (&directory_contents, dc, dc_slot);
          ENULLLOOP (dc->dirstream, opendir (name));
          if (dc->dirstream == 0)
                /* Couldn't open the directory.  Mark this by setting the
                   `files' member to a nil pointer.  */
                dc->dirfiles.ht_vec = 0;
          else
        {
          hash_init (&dc->dirfiles, DIRFILE_BUCKETS,
                 dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
          /* Keep track of how many directories are open.  */
          ++open_directories;
          if (open_directories == MAX_OPEN_DIRECTORIES)
            /* We have too many directories open already.
               Read the entire directory and then close it.  */
            dir_contents_file_exists_p (dc, 0);
        }
        }

      /* Point the name-hashed entry for DIR at its contents data.  */
      dir->contents = dc;
    }
    }

  return dir;
}


/* Return 1 if the name FILENAME is entered in DIR's hash table.
   FILENAME must contain no slashes.  */

static int
dir_contents_file_exists_p (struct directory_contents *dir,
                            const char *filename)
{
  unsigned int hash;
  struct dirfile *df;
  struct dirent *d;
#ifdef WINDOWS32
  struct stat st;
  int rehash = 0;
#endif

  if (dir == 0 || dir->dirfiles.ht_vec == 0)
    /* The directory could not be stat'd or opened.  */
    return 0;

#ifdef __MSDOS__
  filename = dosify (filename);
#endif

#ifdef HAVE_CASE_INSENSITIVE_FS
  filename = downcase (filename);
#endif

#ifdef __EMX__
  if (filename != 0)
    _fnlwr (filename); /* lower case for FAT drives */
#endif

#ifdef VMS
  filename = vmsify (filename,0);
#endif

  hash = 0;
  if (filename != 0)
    {
      struct dirfile dirfile_key;

      if (*filename == '\0')
    {
      /* Checking if the directory exists.  */
      return 1;
    }
      dirfile_key.name = filename;
      dirfile_key.length = strlen (filename);
      df = hash_find_item (&dir->dirfiles, &dirfile_key);
      if (df)
        return !df->impossible;
    }

  /* The file was not found in the hashed list.
     Try to read the directory further.  */

  if (dir->dirstream == 0)
    {
#ifdef WINDOWS32
      /*
       * Check to see if directory has changed since last read. FAT
       * filesystems force a rehash always as mtime does not change
       * on directories (ugh!).
       */
      if (dir->path_key)
    {
          if ((dir->fs_flags & FS_FAT) != 0)
        {
          dir->mtime = time ((time_t *) 0);
          rehash = 1;
        }
      else if (stat (dir->path_key, &st) == 0 && st.st_mtime > dir->mtime)
        {
          /* reset date stamp to show most recent re-process.  */
          dir->mtime = st.st_mtime;
          rehash = 1;
        }

          /* If it has been already read in, all done.  */
      if (!rehash)
        return 0;

          /* make sure directory can still be opened; if not return.  */
          dir->dirstream = opendir (dir->path_key);
          if (!dir->dirstream)
            return 0;
    }
      else
#endif
    /* The directory has been all read in.  */
    return 0;
    }

  while (1)
    {
      /* Enter the file in the hash table.  */
      unsigned int len;
      struct dirfile dirfile_key;
      struct dirfile **dirfile_slot;

      ENULLLOOP (d, readdir (dir->dirstream));
      if (d == 0)
        {
          if (errno)
            fatal (NILF, "INTERNAL: readdir: %s\n", strerror (errno));
          break;
        }

#if defined(VMS) && defined(HAVE_DIRENT_H)
      /* In VMS we get file versions too, which have to be stripped off */
      {
        char *p = strrchr (d->d_name, ';');
        if (p)
          *p = '\0';
      }
#endif
      if (!REAL_DIR_ENTRY (d))
    continue;

      len = NAMLEN (d);
      dirfile_key.name = d->d_name;
      dirfile_key.length = len;
      dirfile_slot = (struct dirfile **) hash_find_slot (&dir->dirfiles, &dirfile_key);
#ifdef WINDOWS32
      /*
       * If re-reading a directory, don't cache files that have
       * already been discovered.
       */
      if (! rehash || HASH_VACANT (*dirfile_slot))
#endif
    {
      df = xmalloc (sizeof (struct dirfile));
#if defined(HAVE_CASE_INSENSITIVE_FS) && defined(VMS)
          df->name = strcache_add_len (downcase(d->d_name), len);
#else
      df->name = strcache_add_len (d->d_name, len);
#endif
      df->length = len;
      df->impossible = 0;
      hash_insert_at (&dir->dirfiles, df, dirfile_slot);
    }
      /* Check if the name matches the one we're searching for.  */
      if (filename != 0 && patheq (d->d_name, filename))
        return 1;
    }

  /* If the directory has been completely read in,
     close the stream and reset the pointer to nil.  */
  if (d == 0)
    {
      --open_directories;
      closedir (dir->dirstream);
      dir->dirstream = 0;
    }
  return 0;
}

/* Return 1 if the name FILENAME in directory DIRNAME
   is entered in the dir hash table.
   FILENAME must contain no slashes.  */

int
dir_file_exists_p (const char *dirname, const char *filename)
{
  return dir_contents_file_exists_p (find_directory (dirname)->contents,
                     filename);
}


/* Return 1 if the file named NAME exists.  */

int
file_exists_p (const char *name)
{
  const char *dirend;
  const char *dirname;
  const char *slash;

#ifndef NO_ARCHIVES
  if (ar_name (name))
    return ar_member_date (name) != (time_t) -1;
#endif

#ifdef VMS
  dirend = strrchr (name, ']');
  if (dirend == 0)
    dirend = strrchr (name, ':');
  if (dirend == 0)
    return dir_file_exists_p ("[]", name);
#else /* !VMS */
  dirend = strrchr (name, '/');
#ifdef HAVE_DOS_PATHS
  /* Forward and backslashes might be mixed.  We need the rightmost one.  */
  {
    const char *bslash = strrchr(name, '\\');
    if (!dirend || bslash > dirend)
      dirend = bslash;
    /* The case of "d:file".  */
    if (!dirend && name[0] && name[1] == ':')
      dirend = name + 1;
  }
#endif /* HAVE_DOS_PATHS */
  if (dirend == 0)
#ifndef _AMIGA
    return dir_file_exists_p (".", name);
#else /* !VMS && !AMIGA */
    return dir_file_exists_p ("", name);
#endif /* AMIGA */
#endif /* VMS */

  slash = dirend;
  if (dirend == name)
    dirname = "/";
  else
    {
      char *p;
#ifdef HAVE_DOS_PATHS
  /* d:/ and d: are *very* different...  */
      if (dirend < name + 3 && name[1] == ':' &&
      (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
    dirend++;
#endif
      p = alloca (dirend - name + 1);
      memcpy (p, name, dirend - name);
      p[dirend - name] = '\0';
      dirname = p;
    }
  return dir_file_exists_p (dirname, slash + 1);
}


/* Mark FILENAME as `impossible' for `file_impossible_p'.
   This means an attempt has been made to search for FILENAME
   as an intermediate file, and it has failed.  */

void
file_impossible (const char *filename)
{
  const char *dirend;
  const char *p = filename;
  struct directory *dir;
  struct dirfile *new;

#ifdef VMS
  dirend = strrchr (p, ']');
  if (dirend == 0)
    dirend = strrchr (p, ':');
  dirend++;
  if (dirend == (char *)1)
    dir = find_directory ("[]");
#else
  dirend = strrchr (p, '/');
# ifdef HAVE_DOS_PATHS
  /* Forward and backslashes might be mixed.  We need the rightmost one.  */
  {
    const char *bslash = strrchr(p, '\\');
    if (!dirend || bslash > dirend)
      dirend = bslash;
    /* The case of "d:file".  */
    if (!dirend && p[0] && p[1] == ':')
      dirend = p + 1;
  }
# endif /* HAVE_DOS_PATHS */
  if (dirend == 0)
# ifdef _AMIGA
    dir = find_directory ("");
# else /* !VMS && !AMIGA */
    dir = find_directory (".");
# endif /* AMIGA */
#endif /* VMS */
  else
    {
      const char *dirname;
      const char *slash = dirend;
      if (dirend == p)
    dirname = "/";
      else
    {
          char *cp;
#ifdef HAVE_DOS_PATHS
      /* d:/ and d: are *very* different...  */
      if (dirend < p + 3 && p[1] == ':' &&
          (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
        dirend++;
#endif
      cp = alloca (dirend - p + 1);
      memcpy (cp, p, dirend - p);
      cp[dirend - p] = '\0';
          dirname = cp;
    }
      dir = find_directory (dirname);
      filename = p = slash + 1;
    }

  if (dir->contents == 0)
    /* The directory could not be stat'd.  We allocate a contents
       structure for it, but leave it out of the contents hash table.  */
    dir->contents = xcalloc (sizeof (struct directory_contents));

  if (dir->contents->dirfiles.ht_vec == 0)
    {
      hash_init (&dir->contents->dirfiles, DIRFILE_BUCKETS,
         dirfile_hash_1, dirfile_hash_2, dirfile_hash_cmp);
    }

  /* Make a new entry and put it in the table.  */

  new = xmalloc (sizeof (struct dirfile));
  new->length = strlen (filename);
#if defined(HAVE_CASE_INSENSITIVE_FS) && defined(VMS)
  new->name = strcache_add_len (downcase(filename), new->length);
#else
  new->name = strcache_add_len (filename, new->length);
#endif
  new->impossible = 1;
  hash_insert (&dir->contents->dirfiles, new);
}


/* Return nonzero if FILENAME has been marked impossible.  */

int
file_impossible_p (const char *filename)
{
  const char *dirend;
  const char *p = filename;
  struct directory_contents *dir;
  struct dirfile *dirfile;
  struct dirfile dirfile_key;

#ifdef VMS
  dirend = strrchr (filename, ']');
  if (dirend == 0)
    dir = find_directory ("[]")->contents;
#else
  dirend = strrchr (filename, '/');
#ifdef HAVE_DOS_PATHS
  /* Forward and backslashes might be mixed.  We need the rightmost one.  */
  {
    const char *bslash = strrchr(filename, '\\');
    if (!dirend || bslash > dirend)
      dirend = bslash;
    /* The case of "d:file".  */
    if (!dirend && filename[0] && filename[1] == ':')
      dirend = filename + 1;
  }
#endif /* HAVE_DOS_PATHS */
  if (dirend == 0)
#ifdef _AMIGA
    dir = find_directory ("")->contents;
#else /* !VMS && !AMIGA */
    dir = find_directory (".")->contents;
#endif /* AMIGA */
#endif /* VMS */
  else
    {
      const char *dirname;
      const char *slash = dirend;
      if (dirend == filename)
    dirname = "/";
      else
    {
          char *cp;
#ifdef HAVE_DOS_PATHS
      /* d:/ and d: are *very* different...  */
      if (dirend < filename + 3 && filename[1] == ':' &&
          (*dirend == '/' || *dirend == '\\' || *dirend == ':'))
        dirend++;
#endif
      cp = alloca (dirend - filename + 1);
      memcpy (cp, p, dirend - p);
      cp[dirend - p] = '\0';
          dirname = cp;
    }
      dir = find_directory (dirname)->contents;
      p = filename = slash + 1;
    }

  if (dir == 0 || dir->dirfiles.ht_vec == 0)
    /* There are no files entered for this directory.  */
    return 0;

#ifdef __MSDOS__
  filename = dosify (p);
#endif
#ifdef HAVE_CASE_INSENSITIVE_FS
  filename = downcase (p);
#endif
#ifdef VMS
  filename = vmsify (p, 1);
#endif

  dirfile_key.name = filename;
  dirfile_key.length = strlen (filename);
  dirfile = hash_find_item (&dir->dirfiles, &dirfile_key);
  if (dirfile)
    return dirfile->impossible;

  return 0;
}


/* Return the already allocated name in the
   directory hash table that matches DIR.  */

const char *
dir_name (const char *dir)
{
  return find_directory (dir)->name;
}


/* Print the data base of directories.  */

void
print_dir_data_base (void)
{
  unsigned int files;
  unsigned int impossible;
  struct directory **dir_slot;
  struct directory **dir_end;

  puts (_("\n# Directories\n"));

  files = impossible = 0;

  dir_slot = (struct directory **) directories.ht_vec;
  dir_end = dir_slot + directories.ht_size;
  for ( ; dir_slot < dir_end; dir_slot++)
    {
      struct directory *dir = *dir_slot;
      if (! HASH_VACANT (dir))
    {
      if (dir->contents == 0)
        printf (_("# %s: could not be stat'd.\n"), dir->name);
      else if (dir->contents->dirfiles.ht_vec == 0)
        {
#ifdef WINDOWS32
          printf (_("# %s (key %s, mtime %d): could not be opened.\n"),
              dir->name, dir->contents->path_key,dir->contents->mtime);
#else  /* WINDOWS32 */
#ifdef VMS
          printf (_("# %s (device %d, inode [%d,%d,%d]): could not be opened.\n"),
              dir->name, dir->contents->dev,
              dir->contents->ino[0], dir->contents->ino[1],
              dir->contents->ino[2]);
#else
          printf (_("# %s (device %ld, inode %ld): could not be opened.\n"),
              dir->name, (long int) dir->contents->dev,
              (long int) dir->contents->ino);
#endif
#endif /* WINDOWS32 */
        }
      else
        {
          unsigned int f = 0;
          unsigned int im = 0;
          struct dirfile **files_slot;
          struct dirfile **files_end;

          files_slot = (struct dirfile **) dir->contents->dirfiles.ht_vec;
          files_end = files_slot + dir->contents->dirfiles.ht_size;
          for ( ; files_slot < files_end; files_slot++)
        {
          struct dirfile *df = *files_slot;
          if (! HASH_VACANT (df))
            {
              if (df->impossible)
            ++im;
              else
            ++f;
            }
        }
#ifdef WINDOWS32
          printf (_("# %s (key %s, mtime %d): "),
              dir->name, dir->contents->path_key, dir->contents->mtime);
#else  /* WINDOWS32 */
#ifdef VMS
          printf (_("# %s (device %d, inode [%d,%d,%d]): "),
              dir->name, dir->contents->dev,
              dir->contents->ino[0], dir->contents->ino[1],
              dir->contents->ino[2]);
#else
          printf (_("# %s (device %ld, inode %ld): "),
              dir->name,
              (long)dir->contents->dev, (long)dir->contents->ino);
#endif
#endif /* WINDOWS32 */
          if (f == 0)
        fputs (_("No"), stdout);
          else
        printf ("%u", f);
          fputs (_(" files, "), stdout);
          if (im == 0)
        fputs (_("no"), stdout);
          else
        printf ("%u", im);
          fputs (_(" impossibilities"), stdout);
          if (dir->contents->dirstream == 0)
        puts (".");
          else
        puts (_(" so far."));
          files += f;
          impossible += im;
        }
    }
    }

  fputs ("\n# ", stdout);
  if (files == 0)
    fputs (_("No"), stdout);
  else
    printf ("%u", files);
  fputs (_(" files, "), stdout);
  if (impossible == 0)
    fputs (_("no"), stdout);
  else
    printf ("%u", impossible);
  printf (_(" impossibilities in %lu directories.\n"), directories.ht_fill);
}


/* Hooks for globbing.  */

#include <glob.h>

/* Structure describing state of iterating through a directory hash table.  */

struct dirstream
  {
    struct directory_contents *contents; /* The directory being read.  */
    struct dirfile **dirfile_slot; /* Current slot in table.  */
  };

/* Forward declarations.  */
static __ptr_t open_dirstream (const char *);
static struct dirent *read_dirstream (__ptr_t);

static __ptr_t
open_dirstream (const char *directory)
{
  struct dirstream *new;
  struct directory *dir = find_directory (directory);

  if (dir->contents == 0 || dir->contents->dirfiles.ht_vec == 0)
    /* DIR->contents is nil if the directory could not be stat'd.
       DIR->contents->dirfiles is nil if it could not be opened.  */
    return 0;

  /* Read all the contents of the directory now.  There is no benefit
     in being lazy, since glob will want to see every file anyway.  */

  dir_contents_file_exists_p (dir->contents, 0);

  new = xmalloc (sizeof (struct dirstream));
  new->contents = dir->contents;
  new->dirfile_slot = (struct dirfile **) new->contents->dirfiles.ht_vec;

  return (__ptr_t) new;
}

static struct dirent *
read_dirstream (__ptr_t stream)
{
  static char *buf;
  static unsigned int bufsz;

  struct dirstream *const ds = (struct dirstream *) stream;
  struct directory_contents *dc = ds->contents;
  struct dirfile **dirfile_end = (struct dirfile **) dc->dirfiles.ht_vec + dc->dirfiles.ht_size;

  while (ds->dirfile_slot < dirfile_end)
    {
      struct dirfile *df = *ds->dirfile_slot++;
      if (! HASH_VACANT (df) && !df->impossible)
    {
      /* The glob interface wants a `struct dirent', so mock one up.  */
      struct dirent *d;
      unsigned int len = df->length + 1;
          unsigned int sz = sizeof (*d) - sizeof (d->d_name) + len;
      if (sz > bufsz)
        {
          bufsz *= 2;
          if (sz > bufsz)
        bufsz = sz;
          buf = xrealloc (buf, bufsz);
        }
      d = (struct dirent *) buf;
#ifdef __MINGW32__
# if __MINGW32_MAJOR_VERSION < 3 || (__MINGW32_MAJOR_VERSION == 3 && \
                     __MINGW32_MINOR_VERSION == 0)
      d->d_name = xmalloc(len);
# endif
#endif
      FAKE_DIR_ENTRY (d);
#ifdef _DIRENT_HAVE_D_NAMLEN
      d->d_namlen = len - 1;
#endif
#ifdef _DIRENT_HAVE_D_TYPE
      d->d_type = DT_UNKNOWN;
#endif
      memcpy (d->d_name, df->name, len);
      return d;
    }
    }

  return 0;
}

static void
ansi_free (void *p)
{
  if (p)
    free(p);
}

/* On 64 bit ReliantUNIX (5.44 and above) in LFS mode, stat() is actually a
 * macro for stat64().  If stat is a macro, make a local wrapper function to
 * invoke it.
 */
#ifndef stat
# ifndef VMS
int stat (const char *path, struct stat *sbuf);
# endif
# define local_stat stat
#else
static int
local_stat (const char *path, struct stat *buf)
{
  int e;

  EINTRLOOP (e, stat (path, buf));
  return e;
}
#endif

void
dir_setup_glob (glob_t *gl)
{
  gl->gl_opendir = open_dirstream;
  gl->gl_readdir = read_dirstream;
  gl->gl_closedir = ansi_free;
  gl->gl_stat = local_stat;
  /* We don't bother setting gl_lstat, since glob never calls it.
     The slot is only there for compatibility with 4.4 BSD.  */
}

void
hash_init_directories (void)
{
  hash_init (&directories, DIRECTORY_BUCKETS,
         directory_hash_1, directory_hash_2, directory_hash_cmp);
  hash_init (&directory_contents, DIRECTORY_BUCKETS,
         directory_contents_hash_1, directory_contents_hash_2,
             directory_contents_hash_cmp);
}

https://git.savannah.gnu.org/cgit/make.git/tree/expand.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/expand.c used in: Step 522
/* Variable expansion functions for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"

#include <assert.h>

#include "filedef.h"
#include "job.h"
#include "commands.h"
#include "variable.h"
#include "rule.h"

/* Initially, any errors reported when expanding strings will be reported
   against the file where the error appears.  */
const struct floc **expanding_var = &reading_file;

/* The next two describe the variable output buffer.
   This buffer is used to hold the variable-expansion of a line of the
   makefile.  It is made bigger with realloc whenever it is too small.
   variable_buffer_length is the size currently allocated.
   variable_buffer is the address of the buffer.

   For efficiency, it's guaranteed that the buffer will always have
   VARIABLE_BUFFER_ZONE extra bytes allocated.  This allows you to add a few
   extra chars without having to call a function.  Note you should never use
   these bytes unless you're _sure_ you have room (you know when the buffer
   length was last checked.  */

#define VARIABLE_BUFFER_ZONE    5

static unsigned int variable_buffer_length;
char *variable_buffer;

/* Subroutine of variable_expand and friends:
   The text to add is LENGTH chars starting at STRING to the variable_buffer.
   The text is added to the buffer at PTR, and the updated pointer into
   the buffer is returned as the value.  Thus, the value returned by
   each call to variable_buffer_output should be the first argument to
   the following call.  */

char *
variable_buffer_output (char *ptr, const char *string, unsigned int length)
{
  register unsigned int newlen = length + (ptr - variable_buffer);

  if ((newlen + VARIABLE_BUFFER_ZONE) > variable_buffer_length)
    {
      unsigned int offset = ptr - variable_buffer;
      variable_buffer_length = (newlen + 100 > 2 * variable_buffer_length
                ? newlen + 100
                : 2 * variable_buffer_length);
      variable_buffer = xrealloc (variable_buffer, variable_buffer_length);
      ptr = variable_buffer + offset;
    }

  memcpy (ptr, string, length);
  return ptr + length;
}

/* Return a pointer to the beginning of the variable buffer.  */

static char *
initialize_variable_output (void)
{
  /* If we don't have a variable output buffer yet, get one.  */

  if (variable_buffer == 0)
    {
      variable_buffer_length = 200;
      variable_buffer = xmalloc (variable_buffer_length);
      variable_buffer[0] = '\0';
    }

  return variable_buffer;
}


/* Recursively expand V.  The returned string is malloc'd.  */

static char *allocated_variable_append (const struct variable *v);

char *
recursively_expand_for_file (struct variable *v, struct file *file)
{
  char *value;
  const struct floc *this_var;
  const struct floc **saved_varp;
  struct variable_set_list *save = 0;
  int set_reading = 0;

  /* Don't install a new location if this location is empty.
     This can happen for command-line variables, builtin variables, etc.  */
  saved_varp = expanding_var;
  if (v->fileinfo.filenm)
    {
      this_var = &v->fileinfo;
      expanding_var = &this_var;
    }

  /* If we have no other file-reading context, use the variable's context. */
  if (!reading_file)
    {
      set_reading = 1;
      reading_file = &v->fileinfo;
    }

  if (v->expanding)
    {
      if (!v->exp_count)
        /* Expanding V causes infinite recursion.  Lose.  */
        fatal (*expanding_var,
               _("Recursive variable `%s' references itself (eventually)"),
               v->name);
      --v->exp_count;
    }

  if (file)
    {
      save = current_variable_set_list;
      current_variable_set_list = file->variables;
    }

  v->expanding = 1;
  if (v->append)
    value = allocated_variable_append (v);
  else
    value = allocated_variable_expand (v->value);
  v->expanding = 0;

  if (set_reading)
    reading_file = 0;

  if (file)
    current_variable_set_list = save;

  expanding_var = saved_varp;

  return value;
}

/* Expand a simple reference to variable NAME, which is LENGTH chars long.  */

#ifdef __GNUC__
__inline
#endif
static char *
reference_variable (char *o, const char *name, unsigned int length)
{
  struct variable *v;
  char *value;

  v = lookup_variable (name, length);

  if (v == 0)
    warn_undefined (name, length);

  /* If there's no variable by that name or it has no value, stop now.  */
  if (v == 0 || (*v->value == '\0' && !v->append))
    return o;

  value = (v->recursive ? recursively_expand (v) : v->value);

  o = variable_buffer_output (o, value, strlen (value));

  if (v->recursive)
    free (value);

  return o;
}


/* Scan STRING for variable references and expansion-function calls.  Only
   LENGTH bytes of STRING are actually scanned.  If LENGTH is -1, scan until
   a null byte is found.

   Write the results to LINE, which must point into `variable_buffer'.  If
   LINE is NULL, start at the beginning of the buffer.
   Return a pointer to LINE, or to the beginning of the buffer if LINE is
   NULL.
 */
char *
variable_expand_string (char *line, const char *string, long length)
{
  struct variable *v;
  const char *p, *p1;
  char *abuf = NULL;
  char *o;
  unsigned int line_offset;

  if (!line)
    line = initialize_variable_output();
  o = line;
  line_offset = line - variable_buffer;

  if (length == 0)
    {
      variable_buffer_output (o, "", 1);
      return (variable_buffer);
    }

  /* If we want a subset of the string, allocate a temporary buffer for it.
     Most of the functions we use here don't work with length limits.  */
  if (length > 0 && string[length] != '\0')
    {
      abuf = xmalloc(length+1);
      memcpy(abuf, string, length);
      abuf[length] = '\0';
      string = abuf;
    }
  p = string;

  while (1)
    {
      /* Copy all following uninteresting chars all at once to the
         variable output buffer, and skip them.  Uninteresting chars end
     at the next $ or the end of the input.  */

      p1 = strchr (p, '$');

      o = variable_buffer_output (o, p, p1 != 0 ? (unsigned int)(p1 - p) : strlen (p) + 1);

      if (p1 == 0)
    break;
      p = p1 + 1;

      /* Dispatch on the char that follows the $.  */

      switch (*p)
    {
    case '$':
      /* $$ seen means output one $ to the variable output buffer.  */
      o = variable_buffer_output (o, p, 1);
      break;

    case '(':
    case '{':
      /* $(...) or ${...} is the general case of substitution.  */
      {
        char openparen = *p;
        char closeparen = (openparen == '(') ? ')' : '}';
            const char *begp;
        const char *beg = p + 1;
        char *op;
            char *abeg = NULL;
        const char *end, *colon;

        op = o;
        begp = p;
        if (handle_function (&op, &begp))
          {
        o = op;
        p = begp;
        break;
          }

        /* Is there a variable reference inside the parens or braces?
           If so, expand it before expanding the entire reference.  */

        end = strchr (beg, closeparen);
        if (end == 0)
              /* Unterminated variable reference.  */
              fatal (*expanding_var, _("unterminated variable reference"));
        p1 = lindex (beg, end, '$');
        if (p1 != 0)
          {
        /* BEG now points past the opening paren or brace.
           Count parens or braces until it is matched.  */
        int count = 0;
        for (p = beg; *p != '\0'; ++p)
          {
            if (*p == openparen)
              ++count;
            else if (*p == closeparen && --count < 0)
              break;
          }
        /* If COUNT is >= 0, there were unmatched opening parens
           or braces, so we go to the simple case of a variable name
           such as `$($(a)'.  */
        if (count < 0)
          {
            abeg = expand_argument (beg, p); /* Expand the name.  */
            beg = abeg;
            end = strchr (beg, '\0');
          }
          }
        else
          /* Advance P to the end of this reference.  After we are
                 finished expanding this one, P will be incremented to
                 continue the scan.  */
          p = end;

        /* This is not a reference to a built-in function and
           any variable references inside are now expanded.
           Is the resultant text a substitution reference?  */

        colon = lindex (beg, end, ':');
        if (colon)
          {
        /* This looks like a substitution reference: $(FOO:A=B).  */
        const char *subst_beg, *subst_end, *replace_beg, *replace_end;

        subst_beg = colon + 1;
        subst_end = lindex (subst_beg, end, '=');
        if (subst_end == 0)
          /* There is no = in sight.  Punt on the substitution
             reference and treat this as a variable name containing
             a colon, in the code below.  */
          colon = 0;
        else
          {
            replace_beg = subst_end + 1;
            replace_end = end;

            /* Extract the variable name before the colon
               and look up that variable.  */
            v = lookup_variable (beg, colon - beg);
            if (v == 0)
              warn_undefined (beg, colon - beg);

                    /* If the variable is not empty, perform the
                       substitution.  */
            if (v != 0 && *v->value != '\0')
              {
            char *pattern, *replace, *ppercent, *rpercent;
            char *value = (v->recursive
                                       ? recursively_expand (v)
                       : v->value);

                        /* Copy the pattern and the replacement.  Add in an
                           extra % at the beginning to use in case there
                           isn't one in the pattern.  */
                        pattern = alloca (subst_end - subst_beg + 2);
                        *(pattern++) = '%';
                        memcpy (pattern, subst_beg, subst_end - subst_beg);
                        pattern[subst_end - subst_beg] = '\0';

                        replace = alloca (replace_end - replace_beg + 2);
                        *(replace++) = '%';
                        memcpy (replace, replace_beg,
                               replace_end - replace_beg);
                        replace[replace_end - replace_beg] = '\0';

                        /* Look for %.  Set the percent pointers properly
                           based on whether we find one or not.  */
            ppercent = find_percent (pattern);
            if (ppercent)
                          {
                            ++ppercent;
                            rpercent = find_percent (replace);
                            if (rpercent)
                              ++rpercent;
                          }
            else
                          {
                            ppercent = pattern;
                            rpercent = replace;
                            --pattern;
                            --replace;
                          }

                        o = patsubst_expand_pat (o, value, pattern, replace,
                                                 ppercent, rpercent);

            if (v->recursive)
              free (value);
              }
          }
          }

        if (colon == 0)
          /* This is an ordinary variable reference.
         Look up the value of the variable.  */
        o = reference_variable (o, beg, end - beg);

      if (abeg)
        free (abeg);
      }
      break;

    case '\0':
      break;

    default:
      if (isblank ((unsigned char)p[-1]))
        break;

      /* A $ followed by a random char is a variable reference:
         $a is equivalent to $(a).  */
          o = reference_variable (o, p, 1);

      break;
    }

      if (*p == '\0')
    break;

      ++p;
    }

  if (abuf)
    free (abuf);

  variable_buffer_output (o, "", 1);
  return (variable_buffer + line_offset);
}


/* Scan LINE for variable references and expansion-function calls.
   Build in `variable_buffer' the result of expanding the references and calls.
   Return the address of the resulting string, which is null-terminated
   and is valid only until the next time this function is called.  */

char *
variable_expand (const char *line)
{
  return variable_expand_string(NULL, line, (long)-1);
}


/* Expand an argument for an expansion function.
   The text starting at STR and ending at END is variable-expanded
   into a null-terminated string that is returned as the value.
   This is done without clobbering `variable_buffer' or the current
   variable-expansion that is in progress.  */

char *
expand_argument (const char *str, const char *end)
{
  char *tmp, *alloc = NULL;
  char *r;

  if (str == end)
    return xstrdup("");

  if (!end || *end == '\0')
    return allocated_variable_expand (str);

  if (end - str + 1 > 1000)
    tmp = alloc = xmalloc (end - str + 1);
  else
    tmp = alloca (end - str + 1);

  memcpy (tmp, str, end - str);
  tmp[end - str] = '\0';

  r = allocated_variable_expand (tmp);

  if (alloc)
    free (alloc);

  return r;
}


/* Expand LINE for FILE.  Error messages refer to the file and line where
   FILE's commands were found.  Expansion uses FILE's variable set list.  */

char *
variable_expand_for_file (const char *line, struct file *file)
{
  char *result;
  struct variable_set_list *savev;
  const struct floc *savef;

  if (file == 0)
    return variable_expand (line);

  savev = current_variable_set_list;
  current_variable_set_list = file->variables;

  savef = reading_file;
  if (file->cmds && file->cmds->fileinfo.filenm)
    reading_file = &file->cmds->fileinfo;
  else
    reading_file = 0;

  result = variable_expand (line);

  current_variable_set_list = savev;
  reading_file = savef;

  return result;
}


/* Like allocated_variable_expand, but for += target-specific variables.
   First recursively construct the variable value from its appended parts in
   any upper variable sets.  Then expand the resulting value.  */

static char *
variable_append (const char *name, unsigned int length,
                 const struct variable_set_list *set)
{
  const struct variable *v;
  char *buf = 0;

  /* If there's nothing left to check, return the empty buffer.  */
  if (!set)
    return initialize_variable_output ();

  /* Try to find the variable in this variable set.  */
  v = lookup_variable_in_set (name, length, set->set);

  /* If there isn't one, look to see if there's one in a set above us.  */
  if (!v)
    return variable_append (name, length, set->next);

  /* If this variable type is append, first get any upper values.
     If not, initialize the buffer.  */
  if (v->append)
    buf = variable_append (name, length, set->next);
  else
    buf = initialize_variable_output ();

  /* Append this value to the buffer, and return it.
     If we already have a value, first add a space.  */
  if (buf > variable_buffer)
    buf = variable_buffer_output (buf, " ", 1);

  /* Either expand it or copy it, depending.  */
  if (! v->recursive)
    return variable_buffer_output (buf, v->value, strlen (v->value));

  buf = variable_expand_string (buf, v->value, strlen (v->value));
  return (buf + strlen (buf));
}


static char *
allocated_variable_append (const struct variable *v)
{
  char *val;

  /* Construct the appended variable value.  */

  char *obuf = variable_buffer;
  unsigned int olen = variable_buffer_length;

  variable_buffer = 0;

  val = variable_append (v->name, strlen (v->name), current_variable_set_list);
  variable_buffer_output (val, "", 1);
  val = variable_buffer;

  variable_buffer = obuf;
  variable_buffer_length = olen;

  return val;
}

/* Like variable_expand_for_file, but the returned string is malloc'd.
   This function is called a lot.  It wants to be efficient.  */

char *
allocated_variable_expand_for_file (const char *line, struct file *file)
{
  char *value;

  char *obuf = variable_buffer;
  unsigned int olen = variable_buffer_length;

  variable_buffer = 0;

  value = variable_expand_for_file (line, file);

  variable_buffer = obuf;
  variable_buffer_length = olen;

  return value;
}

/* Install a new variable_buffer context, returning the current one for
   safe-keeping.  */

void
install_variable_buffer (char **bufp, unsigned int *lenp)
{
  *bufp = variable_buffer;
  *lenp = variable_buffer_length;

  variable_buffer = 0;
  initialize_variable_output ();
}

/* Restore a previously-saved variable_buffer setting (free the current one).
 */

void
restore_variable_buffer (char *buf, unsigned int len)
{
  free (variable_buffer);

  variable_buffer = buf;
  variable_buffer_length = len;
}

https://git.savannah.gnu.org/cgit/make.git/tree/file.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/file.c used in: Step 523
/* Target file management for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"

#include <assert.h>

#include "dep.h"
#include "filedef.h"
#include "job.h"
#include "commands.h"
#include "variable.h"
#include "debug.h"
#include "hash.h"


/* Remember whether snap_deps has been invoked: we need this to be sure we
   don't add new rules (via $(eval ...)) afterwards.  In the future it would
   be nice to support this, but it means we'd need to re-run snap_deps() or
   at least its functionality... it might mean changing snap_deps() to be run
   per-file, so we can invoke it after the eval... or remembering which files
   in the hash have been snapped (a new boolean flag?) and having snap_deps()
   only work on files which have not yet been snapped. */
int snapped_deps = 0;

/* Hash table of files the makefile knows how to make.  */

static unsigned long
file_hash_1 (const void *key)
{
  return_ISTRING_HASH_1 (((struct file const *) key)->hname);
}

static unsigned long
file_hash_2 (const void *key)
{
  return_ISTRING_HASH_2 (((struct file const *) key)->hname);
}

static int
file_hash_cmp (const void *x, const void *y)
{
  return_ISTRING_COMPARE (((struct file const *) x)->hname,
              ((struct file const *) y)->hname);
}

#ifndef FILE_BUCKETS
#define FILE_BUCKETS    1007
#endif
static struct hash_table files;

/* Whether or not .SECONDARY with no prerequisites was given.  */
static int all_secondary = 0;

/* Access the hash table of all file records.
   lookup_file  given a name, return the struct file * for that name,
                or nil if there is none.
*/

struct file *
lookup_file (const char *name)
{
  struct file *f;
  struct file file_key;
#if defined(VMS) && !defined(WANT_CASE_SENSITIVE_TARGETS)
  char *lname;
#endif

  assert (*name != '\0');

  /* This is also done in parse_file_seq, so this is redundant
     for names read from makefiles.  It is here for names passed
     on the command line.  */
#ifdef VMS
# ifndef WANT_CASE_SENSITIVE_TARGETS
  if (*name != '.')
    {
      const char *n;
      char *ln;
      lname = xstrdup (name);
      for (n = name, ln = lname; *n != '\0'; ++n, ++ln)
        *ln = isupper ((unsigned char)*n) ? tolower ((unsigned char)*n) : *n;
      *ln = '\0';
      name = lname;
    }
# endif

  while (name[0] == '[' && name[1] == ']' && name[2] != '\0')
      name += 2;
#endif
  while (name[0] == '.'
#ifdef HAVE_DOS_PATHS
     && (name[1] == '/' || name[1] == '\\')
#else
     && name[1] == '/'
#endif
     && name[2] != '\0')
    {
      name += 2;
      while (*name == '/'
#ifdef HAVE_DOS_PATHS
         || *name == '\\'
#endif
         )
    /* Skip following slashes: ".//foo" is "foo", not "/foo".  */
    ++name;
    }

  if (*name == '\0')
    /* It was all slashes after a dot.  */
#if defined(VMS)
    name = "[]";
#elif defined(_AMIGA)
    name = "";
#else
    name = "./";
#endif

  file_key.hname = name;
  f = hash_find_item (&files, &file_key);
#if defined(VMS) && !defined(WANT_CASE_SENSITIVE_TARGETS)
  if (*name != '.')
    free (lname);
#endif

  return f;
}

/* Look up a file record for file NAME and return it.
   Create a new record if one doesn't exist.  NAME will be stored in the
   new record so it should be constant or in the strcache etc.
 */

struct file *
enter_file (const char *name)
{
  struct file *f;
  struct file *new;
  struct file **file_slot;
  struct file file_key;

  assert (*name != '\0');
  assert (strcache_iscached (name));

#if defined(VMS) && !defined(WANT_CASE_SENSITIVE_TARGETS)
  if (*name != '.')
    {
      const char *n;
      char *lname, *ln;
      lname = xstrdup (name);
      for (n = name, ln = lname; *n != '\0'; ++n, ++ln)
        if (isupper ((unsigned char)*n))
          *ln = tolower ((unsigned char)*n);
        else
          *ln = *n;

      *ln = '\0';
      name = strcache_add (lname);
      free (lname);
    }
#endif

  file_key.hname = name;
  file_slot = (struct file **) hash_find_slot (&files, &file_key);
  f = *file_slot;
  if (! HASH_VACANT (f) && !f->double_colon)
    return f;

  new = xcalloc (sizeof (struct file));
  new->name = new->hname = name;
  new->update_status = -1;

  if (HASH_VACANT (f))
    {
      new->last = new;
      hash_insert_at (&files, new, file_slot);
    }
  else
    {
      /* There is already a double-colon entry for this file.  */
      new->double_colon = f;
      f->last->prev = new;
      f->last = new;
    }

  return new;
}


/* Rehash FILE to NAME.  This is not as simple as resetting
   the `hname' member, since it must be put in a new hash bucket,
   and possibly merged with an existing file called NAME.  */

void
rehash_file (struct file *from_file, const char *to_hname)
{
  struct file file_key;
  struct file **file_slot;
  struct file *to_file;
  struct file *deleted_file;
  struct file *f;

  /* If it's already that name, we're done.  */
  file_key.hname = to_hname;
  if (! file_hash_cmp (from_file, &file_key))
    return;

  /* Find the end of the renamed list for the "from" file.  */
  file_key.hname = from_file->hname;
  while (from_file->renamed != 0)
    from_file = from_file->renamed;
  if (file_hash_cmp (from_file, &file_key))
    /* hname changed unexpectedly!! */
    abort ();

  /* Remove the "from" file from the hash.  */
  deleted_file = hash_delete (&files, from_file);
  if (deleted_file != from_file)
    /* from_file isn't the one stored in files */
    abort ();

  /* Find where the newly renamed file will go in the hash.  */
  file_key.hname = to_hname;
  file_slot = (struct file **) hash_find_slot (&files, &file_key);
  to_file = *file_slot;

  /* Change the hash name for this file.  */
  from_file->hname = to_hname;
  for (f = from_file->double_colon; f != 0; f = f->prev)
    f->hname = to_hname;

  /* If the new name doesn't exist yet just set it to the renamed file.  */
  if (HASH_VACANT (to_file))
    {
      hash_insert_at (&files, from_file, file_slot);
      return;
    }

  /* TO_FILE already exists under TO_HNAME.
     We must retain TO_FILE and merge FROM_FILE into it.  */

  if (from_file->cmds != 0)
    {
      if (to_file->cmds == 0)
        to_file->cmds = from_file->cmds;
      else if (from_file->cmds != to_file->cmds)
        {
          /* We have two sets of commands.  We will go with the
             one given in the rule explicitly mentioning this name,
             but give a message to let the user know what's going on.  */
          if (to_file->cmds->fileinfo.filenm != 0)
            error (&from_file->cmds->fileinfo,
                   _("Recipe was specified for file `%s' at %s:%lu,"),
                   from_file->name, to_file->cmds->fileinfo.filenm,
                   to_file->cmds->fileinfo.lineno);
          else
            error (&from_file->cmds->fileinfo,
                   _("Recipe for file `%s' was found by implicit rule search,"),
                   from_file->name);
          error (&from_file->cmds->fileinfo,
                 _("but `%s' is now considered the same file as `%s'."),
                 from_file->name, to_hname);
          error (&from_file->cmds->fileinfo,
                 _("Recipe for `%s' will be ignored in favor of the one for `%s'."),
                 to_hname, from_file->name);
        }
    }

  /* Merge the dependencies of the two files.  */

  if (to_file->deps == 0)
    to_file->deps = from_file->deps;
  else
    {
      struct dep *deps = to_file->deps;
      while (deps->next != 0)
        deps = deps->next;
      deps->next = from_file->deps;
    }

  merge_variable_set_lists (&to_file->variables, from_file->variables);

  if (to_file->double_colon && from_file->is_target && !from_file->double_colon)
    fatal (NILF, _("can't rename single-colon `%s' to double-colon `%s'"),
           from_file->name, to_hname);
  if (!to_file->double_colon  && from_file->double_colon)
    {
      if (to_file->is_target)
        fatal (NILF, _("can't rename double-colon `%s' to single-colon `%s'"),
               from_file->name, to_hname);
      else
        to_file->double_colon = from_file->double_colon;
    }

  if (from_file->last_mtime > to_file->last_mtime)
    /* %%% Kludge so -W wins on a file that gets vpathized.  */
    to_file->last_mtime = from_file->last_mtime;

  to_file->mtime_before_update = from_file->mtime_before_update;

#define MERGE(field) to_file->field |= from_file->field
  MERGE (precious);
  MERGE (tried_implicit);
  MERGE (updating);
  MERGE (updated);
  MERGE (is_target);
  MERGE (cmd_target);
  MERGE (phony);
  MERGE (ignore_vpath);
#undef MERGE

  from_file->renamed = to_file;
}

/* Rename FILE to NAME.  This is not as simple as resetting
   the `name' member, since it must be put in a new hash bucket,
   and possibly merged with an existing file called NAME.  */

void
rename_file (struct file *from_file, const char *to_hname)
{
  rehash_file (from_file, to_hname);
  while (from_file)
    {
      from_file->name = from_file->hname;
      from_file = from_file->prev;
    }
}


/* Remove all nonprecious intermediate files.
   If SIG is nonzero, this was caused by a fatal signal,
   meaning that a different message will be printed, and
   the message will go to stderr rather than stdout.  */

void
remove_intermediates (int sig)
{
  struct file **file_slot;
  struct file **file_end;
  int doneany = 0;

  /* If there's no way we will ever remove anything anyway, punt early.  */
  if (question_flag || touch_flag || all_secondary)
    return;

  if (sig && just_print_flag)
    return;

  file_slot = (struct file **) files.ht_vec;
  file_end = file_slot + files.ht_size;
  for ( ; file_slot < file_end; file_slot++)
    if (! HASH_VACANT (*file_slot))
      {
    struct file *f = *file_slot;
        /* Is this file eligible for automatic deletion?
           Yes, IFF: it's marked intermediate, it's not secondary, it wasn't
           given on the command line, and it's either a -include makefile or
           it's not precious.  */
    if (f->intermediate && (f->dontcare || !f->precious)
        && !f->secondary && !f->cmd_target)
      {
        int status;
        if (f->update_status == -1)
          /* If nothing would have created this file yet,
         don't print an "rm" command for it.  */
          continue;
        if (just_print_flag)
          status = 0;
        else
          {
        status = unlink (f->name);
        if (status < 0 && errno == ENOENT)
          continue;
          }
        if (!f->dontcare)
          {
        if (sig)
          error (NILF, _("*** Deleting intermediate file `%s'"), f->name);
        else
          {
            if (! doneany)
              DB (DB_BASIC, (_("Removing intermediate files...\n")));
            if (!silent_flag)
              {
            if (! doneany)
              {
                fputs ("rm ", stdout);
                doneany = 1;
              }
            else
              putchar (' ');
            fputs (f->name, stdout);
            fflush (stdout);
              }
          }
        if (status < 0)
          perror_with_name ("unlink: ", f->name);
          }
      }
      }

  if (doneany && !sig)
    {
      putchar ('\n');
      fflush (stdout);
    }
}


/* Given a string containing prerequisites (fully expanded), break it up into
   a struct dep list.  Enter each of these prereqs into the file database.
 */
struct dep *
split_prereqs (char *p)
{
  struct dep *new = PARSE_FILE_SEQ (&p, struct dep, '|', NULL, 0);

  if (*p)
    {
      /* Files that follow '|' are "order-only" prerequisites that satisfy the
         dependency by existing: their modification times are irrelevant.  */
      struct dep *ood;

      ++p;
      ood = PARSE_FILE_SEQ (&p, struct dep, '\0', NULL, 0);

      if (! new)
        new = ood;
      else
        {
          struct dep *dp;
          for (dp = new; dp->next != NULL; dp = dp->next)
            ;
          dp->next = ood;
        }

      for (; ood != NULL; ood = ood->next)
        ood->ignore_mtime = 1;
    }

  return new;
}

/* Given a list of prerequisites, enter them into the file database.
   If STEM is set then first expand patterns using STEM.  */
struct dep *
enter_prereqs (struct dep *deps, const char *stem)
{
  struct dep *d1;

  if (deps == 0)
    return 0;

  /* If we have a stem, expand the %'s.  We use patsubst_expand to translate
     the prerequisites' patterns into plain prerequisite names.  */
  if (stem)
    {
      const char *pattern = "%";
      char *buffer = variable_expand ("");
      struct dep *dp = deps, *dl = 0;

      while (dp != 0)
        {
          char *percent;
          int nl = strlen (dp->name) + 1;
          char *nm = alloca (nl);
          memcpy (nm, dp->name, nl);
          percent = find_percent (nm);
          if (percent)
            {
              char *o;

              /* We have to handle empty stems specially, because that
                 would be equivalent to $(patsubst %,dp->name,) which
                 will always be empty.  */
              if (stem[0] == '\0')
                {
                  memmove (percent, percent+1, strlen (percent));
                  o = variable_buffer_output (buffer, nm, strlen (nm) + 1);
                }
              else
                o = patsubst_expand_pat (buffer, stem, pattern, nm,
                                         pattern+1, percent+1);

              /* If the name expanded to the empty string, ignore it.  */
              if (buffer[0] == '\0')
                {
                  struct dep *df = dp;
                  if (dp == deps)
                    dp = deps = deps->next;
                  else
                    dp = dl->next = dp->next;
                  free_dep (df);
                  continue;
                }

              /* Save the name.  */
              dp->name = strcache_add_len (buffer, o - buffer);
            }
          dp->stem = stem;
          dp->staticpattern = 1;
          dl = dp;
          dp = dp->next;
        }
    }

  /* Enter them as files, unless they need a 2nd expansion.  */
  for (d1 = deps; d1 != 0; d1 = d1->next)
    {
      if (d1->need_2nd_expansion)
        continue;

      d1->file = lookup_file (d1->name);
      if (d1->file == 0)
        d1->file = enter_file (d1->name);
      d1->staticpattern = 0;
      d1->name = 0;
    }

  return deps;
}

/* Set the intermediate flag.  */

static void
set_intermediate (const void *item)
{
  struct file *f = (struct file *) item;
  f->intermediate = 1;
}

/* Expand and parse each dependency line. */
static void
expand_deps (struct file *f)
{
  struct dep *d;
  struct dep **dp;
  const char *file_stem = f->stem;
  int initialized = 0;

  f->updating = 0;

  /* Walk through the dependencies.  For any dependency that needs 2nd
     expansion, expand it then insert the result into the list.  */
  dp = &f->deps;
  d = f->deps;
  while (d != 0)
    {
      char *p;
      struct dep *new, *next;
      char *name = (char *)d->name;

      if (! d->name || ! d->need_2nd_expansion)
        {
          /* This one is all set already.  */
          dp = &d->next;
          d = d->next;
          continue;
        }

      /* If it's from a static pattern rule, convert the patterns into
         "$*" so they'll expand properly.  */
      if (d->staticpattern)
        {
          char *o;
          d->name = o = variable_expand ("");
          o = subst_expand (o, name, "%", "$*", 1, 2, 0);
          *o = '\0';
          free (name);
          d->name = name = xstrdup (d->name);
          d->staticpattern = 0;
        }

      /* We're going to do second expansion so initialize file variables for
         the file. Since the stem for static pattern rules comes from
         individual dep lines, we will temporarily set f->stem to d->stem.  */
      if (!initialized)
        {
          initialize_file_variables (f, 0);
          initialized = 1;
        }

      if (d->stem != 0)
        f->stem = d->stem;

      set_file_variables (f);

      p = variable_expand_for_file (d->name, f);

      if (d->stem != 0)
        f->stem = file_stem;

      /* At this point we don't need the name anymore: free it.  */
      free (name);

      /* Parse the prerequisites and enter them into the file database.  */
      new = enter_prereqs (split_prereqs (p), d->stem);

      /* If there were no prereqs here (blank!) then throw this one out.  */
      if (new == 0)
        {
          *dp = d->next;
          free_dep (d);
          d = *dp;
          continue;
        }

      /* Add newly parsed prerequisites.  */
      next = d->next;
      *dp = new;
      for (dp = &new->next, d = new->next; d != 0; dp = &d->next, d = d->next)
        ;
      *dp = next;
      d = *dp;
    }
}

/* Reset the updating flag.  */

static void
reset_updating (const void *item)
{
  struct file *f = (struct file *) item;
  f->updating = 0;
}

/* For each dependency of each file, make the `struct dep' point
   at the appropriate `struct file' (which may have to be created).

   Also mark the files depended on by .PRECIOUS, .PHONY, .SILENT,
   and various other special targets.  */

void
snap_deps (void)
{
  struct file *f;
  struct file *f2;
  struct dep *d;

  /* Remember that we've done this.  Once we start snapping deps we can no
     longer define new targets.  */
  snapped_deps = 1;

  /* Perform second expansion and enter each dependency name as a file.  We
     must use hash_dump() here because within these loops we likely add new
     files to the table, possibly causing an in-situ table expansion.

     We only need to do this if second_expansion has been defined; if it
     hasn't then all deps were expanded as the makefile was read in.  If we
     ever change make to be able to unset .SECONDARY_EXPANSION this will have
     to change.  */

  if (second_expansion)
    {
      struct file **file_slot_0 = (struct file **) hash_dump (&files, 0, 0);
      struct file **file_end = file_slot_0 + files.ht_fill;
      struct file **file_slot;
      const char *suffixes;

      /* Expand .SUFFIXES: its prerequisites are used for $$* calc.  */
      f = lookup_file (".SUFFIXES");
      suffixes = f ? f->name : 0;
      for (; f != 0; f = f->prev)
        expand_deps (f);

      /* For every target that's not .SUFFIXES, expand its prerequisites.  */

      for (file_slot = file_slot_0; file_slot < file_end; file_slot++)
        for (f = *file_slot; f != 0; f = f->prev)
          if (f->name != suffixes)
            expand_deps (f);
      free (file_slot_0);
    }
  else
    /* We're not doing second expansion, so reset updating.  */
    hash_map (&files, reset_updating);

  /* Now manage all the special targets.  */

  for (f = lookup_file (".PRECIOUS"); f != 0; f = f->prev)
    for (d = f->deps; d != 0; d = d->next)
      for (f2 = d->file; f2 != 0; f2 = f2->prev)
    f2->precious = 1;

  for (f = lookup_file (".LOW_RESOLUTION_TIME"); f != 0; f = f->prev)
    for (d = f->deps; d != 0; d = d->next)
      for (f2 = d->file; f2 != 0; f2 = f2->prev)
    f2->low_resolution_time = 1;

  for (f = lookup_file (".PHONY"); f != 0; f = f->prev)
    for (d = f->deps; d != 0; d = d->next)
      for (f2 = d->file; f2 != 0; f2 = f2->prev)
    {
      /* Mark this file as phony nonexistent target.  */
      f2->phony = 1;
          f2->is_target = 1;
      f2->last_mtime = NONEXISTENT_MTIME;
      f2->mtime_before_update = NONEXISTENT_MTIME;
    }

  for (f = lookup_file (".INTERMEDIATE"); f != 0; f = f->prev)
    /* Mark .INTERMEDIATE deps as intermediate files.  */
    for (d = f->deps; d != 0; d = d->next)
      for (f2 = d->file; f2 != 0; f2 = f2->prev)
        f2->intermediate = 1;
    /* .INTERMEDIATE with no deps does nothing.
       Marking all files as intermediates is useless since the goal targets
       would be deleted after they are built.  */

  for (f = lookup_file (".SECONDARY"); f != 0; f = f->prev)
    /* Mark .SECONDARY deps as both intermediate and secondary.  */
    if (f->deps)
      for (d = f->deps; d != 0; d = d->next)
        for (f2 = d->file; f2 != 0; f2 = f2->prev)
          f2->intermediate = f2->secondary = 1;
    /* .SECONDARY with no deps listed marks *all* files that way.  */
    else
      {
        all_secondary = 1;
        hash_map (&files, set_intermediate);
      }

  f = lookup_file (".EXPORT_ALL_VARIABLES");
  if (f != 0 && f->is_target)
    export_all_variables = 1;

  f = lookup_file (".IGNORE");
  if (f != 0 && f->is_target)
    {
      if (f->deps == 0)
    ignore_errors_flag = 1;
      else
    for (d = f->deps; d != 0; d = d->next)
      for (f2 = d->file; f2 != 0; f2 = f2->prev)
        f2->command_flags |= COMMANDS_NOERROR;
    }

  f = lookup_file (".SILENT");
  if (f != 0 && f->is_target)
    {
      if (f->deps == 0)
    silent_flag = 1;
      else
    for (d = f->deps; d != 0; d = d->next)
      for (f2 = d->file; f2 != 0; f2 = f2->prev)
        f2->command_flags |= COMMANDS_SILENT;
    }

  f = lookup_file (".NOTPARALLEL");
  if (f != 0 && f->is_target)
    not_parallel = 1;

#ifndef NO_MINUS_C_MINUS_O
  /* If .POSIX was defined, remove OUTPUT_OPTION to comply.  */
  /* This needs more work: what if the user sets this in the makefile?
  if (posix_pedantic)
    define_variable_cname ("OUTPUT_OPTION", "", o_default, 1);
  */
#endif
}


/* Set the `command_state' member of FILE and all its `also_make's.  */

void
set_command_state (struct file *file, enum cmd_state state)
{
  struct dep *d;

  file->command_state = state;

  for (d = file->also_make; d != 0; d = d->next)
    d->file->command_state = state;
}


/* Convert an external file timestamp to internal form.  */

FILE_TIMESTAMP
file_timestamp_cons (const char *fname, time_t s, int ns)
{
  int offset = ORDINARY_MTIME_MIN + (FILE_TIMESTAMP_HI_RES ? ns : 0);
  FILE_TIMESTAMP product = (FILE_TIMESTAMP) s << FILE_TIMESTAMP_LO_BITS;
  FILE_TIMESTAMP ts = product + offset;

  if (! (s <= FILE_TIMESTAMP_S (ORDINARY_MTIME_MAX)
     && product <= ts && ts <= ORDINARY_MTIME_MAX))
    {
      char buf[FILE_TIMESTAMP_PRINT_LEN_BOUND + 1];
      ts = s <= OLD_MTIME ? ORDINARY_MTIME_MIN : ORDINARY_MTIME_MAX;
      file_timestamp_sprintf (buf, ts);
      error (NILF, _("%s: Timestamp out of range; substituting %s"),
         fname ? fname : _("Current time"), buf);
    }

  return ts;
}


/* Return the current time as a file timestamp, setting *RESOLUTION to
   its resolution.  */
FILE_TIMESTAMP
file_timestamp_now (int *resolution)
{
  int r;
  time_t s;
  int ns;

  /* Don't bother with high-resolution clocks if file timestamps have
     only one-second resolution.  The code below should work, but it's
     not worth the hassle of debugging it on hosts where it fails.  */
#if FILE_TIMESTAMP_HI_RES
# if HAVE_CLOCK_GETTIME && defined CLOCK_REALTIME
  {
    struct timespec timespec;
    if (clock_gettime (CLOCK_REALTIME, &timespec) == 0)
      {
    r = 1;
    s = timespec.tv_sec;
    ns = timespec.tv_nsec;
    goto got_time;
      }
  }
# endif
# if HAVE_GETTIMEOFDAY
  {
    struct timeval timeval;
    if (gettimeofday (&timeval, 0) == 0)
      {
    r = 1000;
    s = timeval.tv_sec;
    ns = timeval.tv_usec * 1000;
    goto got_time;
      }
  }
# endif
#endif

  r = 1000000000;
  s = time ((time_t *) 0);
  ns = 0;

#if FILE_TIMESTAMP_HI_RES
 got_time:
#endif
  *resolution = r;
  return file_timestamp_cons (0, s, ns);
}

/* Place into the buffer P a printable representation of the file
   timestamp TS.  */
void
file_timestamp_sprintf (char *p, FILE_TIMESTAMP ts)
{
  time_t t = FILE_TIMESTAMP_S (ts);
  struct tm *tm = localtime (&t);

  if (tm)
    sprintf (p, "%04d-%02d-%02d %02d:%02d:%02d",
         tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
         tm->tm_hour, tm->tm_min, tm->tm_sec);
  else if (t < 0)
    sprintf (p, "%ld", (long) t);
  else
    sprintf (p, "%lu", (unsigned long) t);
  p += strlen (p);

  /* Append nanoseconds as a fraction, but remove trailing zeros.  We don't
     know the actual timestamp resolution, since clock_getres applies only to
     local times, whereas this timestamp might come from a remote filesystem.
     So removing trailing zeros is the best guess that we can do.  */
  sprintf (p, ".%09d", FILE_TIMESTAMP_NS (ts));
  p += strlen (p) - 1;
  while (*p == '0')
    p--;
  p += *p != '.';

  *p = '\0';
}


/* Print the data base of files.  */

void
print_prereqs (const struct dep *deps)
{
  const struct dep *ood = 0;

  /* Print all normal dependencies; note any order-only deps.  */
  for (; deps != 0; deps = deps->next)
    if (! deps->ignore_mtime)
      printf (" %s", dep_name (deps));
    else if (! ood)
      ood = deps;

  /* Print order-only deps, if we have any.  */
  if (ood)
    {
      printf (" | %s", dep_name (ood));
      for (ood = ood->next; ood != 0; ood = ood->next)
        if (ood->ignore_mtime)
          printf (" %s", dep_name (ood));
    }

  putchar ('\n');
}

static void
print_file (const void *item)
{
  const struct file *f = item;

  putchar ('\n');
  if (!f->is_target)
    puts (_("# Not a target:"));
  printf ("%s:%s", f->name, f->double_colon ? ":" : "");
  print_prereqs (f->deps);

  if (f->precious)
    puts (_("#  Precious file (prerequisite of .PRECIOUS)."));
  if (f->phony)
    puts (_("#  Phony target (prerequisite of .PHONY)."));
  if (f->cmd_target)
    puts (_("#  Command line target."));
  if (f->dontcare)
    puts (_("#  A default, MAKEFILES, or -include/sinclude makefile."));
  puts (f->tried_implicit
        ? _("#  Implicit rule search has been done.")
        : _("#  Implicit rule search has not been done."));
  if (f->stem != 0)
    printf (_("#  Implicit/static pattern stem: `%s'\n"), f->stem);
  if (f->intermediate)
    puts (_("#  File is an intermediate prerequisite."));
  if (f->also_make != 0)
    {
      const struct dep *d;
      fputs (_("#  Also makes:"), stdout);
      for (d = f->also_make; d != 0; d = d->next)
    printf (" %s", dep_name (d));
      putchar ('\n');
    }
  if (f->last_mtime == UNKNOWN_MTIME)
    puts (_("#  Modification time never checked."));
  else if (f->last_mtime == NONEXISTENT_MTIME)
    puts (_("#  File does not exist."));
  else if (f->last_mtime == OLD_MTIME)
    puts (_("#  File is very old."));
  else
    {
      char buf[FILE_TIMESTAMP_PRINT_LEN_BOUND + 1];
      file_timestamp_sprintf (buf, f->last_mtime);
      printf (_("#  Last modified %s\n"), buf);
    }
  puts (f->updated
        ? _("#  File has been updated.") : _("#  File has not been updated."));
  switch (f->command_state)
    {
    case cs_running:
      puts (_("#  Recipe currently running (THIS IS A BUG)."));
      break;
    case cs_deps_running:
      puts (_("#  Dependencies recipe running (THIS IS A BUG)."));
      break;
    case cs_not_started:
    case cs_finished:
      switch (f->update_status)
    {
    case -1:
      break;
    case 0:
      puts (_("#  Successfully updated."));
      break;
    case 1:
      assert (question_flag);
      puts (_("#  Needs to be updated (-q is set)."));
      break;
    case 2:
      puts (_("#  Failed to be updated."));
      break;
    default:
      puts (_("#  Invalid value in `update_status' member!"));
      fflush (stdout);
      fflush (stderr);
      abort ();
    }
      break;
    default:
      puts (_("#  Invalid value in `command_state' member!"));
      fflush (stdout);
      fflush (stderr);
      abort ();
    }

  if (f->variables != 0)
    print_file_variables (f);

  if (f->cmds != 0)
    print_commands (f->cmds);

  if (f->prev)
    print_file ((const void *) f->prev);
}

void
print_file_data_base (void)
{
  puts (_("\n# Files"));

  hash_map (&files, print_file);

  fputs (_("\n# files hash-table stats:\n# "), stdout);
  hash_print_stats (&files, stdout);
}


/* Verify the integrity of the data base of files.  */

#define VERIFY_CACHED(_p,_n) \
    do{\
        if (_p->_n && _p->_n[0] && !strcache_iscached (_p->_n)) \
          error (NULL, "%s: Field '%s' not cached: %s\n", _p->name, # _n, _p->_n); \
    }while(0)

static void
verify_file (const void *item)
{
  const struct file *f = item;
  const struct dep *d;

  VERIFY_CACHED (f, name);
  VERIFY_CACHED (f, hname);
  VERIFY_CACHED (f, vpath);
  VERIFY_CACHED (f, stem);

  /* Check the deps.  */
  for (d = f->deps; d != 0; d = d->next)
    {
      if (! d->need_2nd_expansion)
        VERIFY_CACHED (d, name);
      VERIFY_CACHED (d, stem);
    }
}

void
verify_file_data_base (void)
{
  hash_map (&files, verify_file);
}

#define EXPANSION_INCREMENT(_l)  ((((_l) / 500) + 1) * 500)

char *
build_target_list (char *value)
{
  static unsigned long last_targ_count = 0;

  if (files.ht_fill != last_targ_count)
    {
      unsigned long max = EXPANSION_INCREMENT (strlen (value));
      unsigned long len;
      char *p;
      struct file **fp = (struct file **) files.ht_vec;
      struct file **end = &fp[files.ht_size];

      /* Make sure we have at least MAX bytes in the allocated buffer.  */
      value = xrealloc (value, max);

      p = value;
      len = 0;
      for (; fp < end; ++fp)
        if (!HASH_VACANT (*fp) && (*fp)->is_target)
          {
            struct file *f = *fp;
            int l = strlen (f->name);

            len += l + 1;
            if (len > max)
              {
                unsigned long off = p - value;

                max += EXPANSION_INCREMENT (l + 1);
                value = xrealloc (value, max);
                p = &value[off];
              }

            memcpy (p, f->name, l);
            p += l;
            *(p++) = ' ';
          }
      *(p-1) = '\0';

      last_targ_count = files.ht_fill;
    }

  return value;
}

void
init_hash_files (void)
{
  hash_init (&files, 1000, file_hash_1, file_hash_2, file_hash_cmp);
}

/* EOF */

https://git.savannah.gnu.org/cgit/make.git/tree/debug.h?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/debug.h used in: Step 523, Step 524, Step 525, Step 526, Step 527, Step 528, Step 529, Step 530
/* Debugging macros and interface.
Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
2009, 2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#define DB_NONE         (0x000)
#define DB_BASIC        (0x001)
#define DB_VERBOSE      (0x002)
#define DB_JOBS         (0x004)
#define DB_IMPLICIT     (0x008)
#define DB_MAKEFILES    (0x100)

#define DB_ALL          (0xfff)

extern int db_level;

#define ISDB(_l)    ((_l)&db_level)

#define DBS(_l,_x)  do{ if(ISDB(_l)) {print_spaces (depth); \
                                      printf _x; fflush (stdout);} }while(0)

#define DBF(_l,_x)  do{ if(ISDB(_l)) {print_spaces (depth); \
                                      printf (_x, file->name); \
                                      fflush (stdout);} }while(0)

#define DB(_l,_x)   do{ if(ISDB(_l)) {printf _x; fflush (stdout);} }while(0)

https://git.savannah.gnu.org/cgit/make.git/tree/function.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/function.c used in: Step 524
/* Builtin function expansion for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"
#include "filedef.h"
#include "variable.h"
#include "dep.h"
#include "job.h"
#include "commands.h"
#include "debug.h"

#ifdef _AMIGA
#include "amiga.h"
#endif


struct function_table_entry
  {
    const char *name;
    unsigned char len;
    unsigned char minimum_args;
    unsigned char maximum_args;
    char expand_args;
    char *(*func_ptr) (char *output, char **argv, const char *fname);
  };

static unsigned long
function_table_entry_hash_1 (const void *keyv)
{
  const struct function_table_entry *key = keyv;
  return_STRING_N_HASH_1 (key->name, key->len);
}

static unsigned long
function_table_entry_hash_2 (const void *keyv)
{
  const struct function_table_entry *key = keyv;
  return_STRING_N_HASH_2 (key->name, key->len);
}

static int
function_table_entry_hash_cmp (const void *xv, const void *yv)
{
  const struct function_table_entry *x = xv;
  const struct function_table_entry *y = yv;
  int result = x->len - y->len;
  if (result)
    return result;
  return_STRING_N_COMPARE (x->name, y->name, x->len);
}

static struct hash_table function_table;



/* Store into VARIABLE_BUFFER at O the result of scanning TEXT and replacing
   each occurrence of SUBST with REPLACE. TEXT is null-terminated.  SLEN is
   the length of SUBST and RLEN is the length of REPLACE.  If BY_WORD is
   nonzero, substitutions are done only on matches which are complete
   whitespace-delimited words.  */

char *
subst_expand (char *o, const char *text, const char *subst, const char *replace,
              unsigned int slen, unsigned int rlen, int by_word)
{
  const char *t = text;
  const char *p;

  if (slen == 0 && !by_word)
    {
      /* The first occurrence of "" in any string is its end.  */
      o = variable_buffer_output (o, t, strlen (t));
      if (rlen > 0)
    o = variable_buffer_output (o, replace, rlen);
      return o;
    }

  do
    {
      if (by_word && slen == 0)
    /* When matching by words, the empty string should match
       the end of each word, rather than the end of the whole text.  */
    p = end_of_token (next_token (t));
      else
    {
      p = strstr (t, subst);
      if (p == 0)
        {
          /* No more matches.  Output everything left on the end.  */
          o = variable_buffer_output (o, t, strlen (t));
          return o;
        }
    }

      /* Output everything before this occurrence of the string to replace.  */
      if (p > t)
    o = variable_buffer_output (o, t, p - t);

      /* If we're substituting only by fully matched words,
     or only at the ends of words, check that this case qualifies.  */
      if (by_word
          && ((p > text && !isblank ((unsigned char)p[-1]))
              || (p[slen] != '\0' && !isblank ((unsigned char)p[slen]))))
    /* Struck out.  Output the rest of the string that is
       no longer to be replaced.  */
    o = variable_buffer_output (o, subst, slen);
      else if (rlen > 0)
    /* Output the replacement string.  */
    o = variable_buffer_output (o, replace, rlen);

      /* Advance T past the string to be replaced.  */
      t = p + slen;
    } while (*t != '\0');

  return o;
}



/* Store into VARIABLE_BUFFER at O the result of scanning TEXT
   and replacing strings matching PATTERN with REPLACE.
   If PATTERN_PERCENT is not nil, PATTERN has already been
   run through find_percent, and PATTERN_PERCENT is the result.
   If REPLACE_PERCENT is not nil, REPLACE has already been
   run through find_percent, and REPLACE_PERCENT is the result.
   Note that we expect PATTERN_PERCENT and REPLACE_PERCENT to point to the
   character _AFTER_ the %, not to the % itself.
*/

char *
patsubst_expand_pat (char *o, const char *text,
                     const char *pattern, const char *replace,
                     const char *pattern_percent, const char *replace_percent)
{
  unsigned int pattern_prepercent_len, pattern_postpercent_len;
  unsigned int replace_prepercent_len, replace_postpercent_len;
  const char *t;
  unsigned int len;
  int doneany = 0;

  /* Record the length of REPLACE before and after the % so we don't have to
     compute these lengths more than once.  */
  if (replace_percent)
    {
      replace_prepercent_len = replace_percent - replace - 1;
      replace_postpercent_len = strlen (replace_percent);
    }
  else
    {
      replace_prepercent_len = strlen (replace);
      replace_postpercent_len = 0;
    }

  if (!pattern_percent)
    /* With no % in the pattern, this is just a simple substitution.  */
    return subst_expand (o, text, pattern, replace,
             strlen (pattern), strlen (replace), 1);

  /* Record the length of PATTERN before and after the %
     so we don't have to compute it more than once.  */
  pattern_prepercent_len = pattern_percent - pattern - 1;
  pattern_postpercent_len = strlen (pattern_percent);

  while ((t = find_next_token (&text, &len)) != 0)
    {
      int fail = 0;

      /* Is it big enough to match?  */
      if (len < pattern_prepercent_len + pattern_postpercent_len)
    fail = 1;

      /* Does the prefix match? */
      if (!fail && pattern_prepercent_len > 0
      && (*t != *pattern
          || t[pattern_prepercent_len - 1] != pattern_percent[-2]
          || !strneq (t + 1, pattern + 1, pattern_prepercent_len - 1)))
    fail = 1;

      /* Does the suffix match? */
      if (!fail && pattern_postpercent_len > 0
      && (t[len - 1] != pattern_percent[pattern_postpercent_len - 1]
          || t[len - pattern_postpercent_len] != *pattern_percent
          || !strneq (&t[len - pattern_postpercent_len],
              pattern_percent, pattern_postpercent_len - 1)))
    fail = 1;

      if (fail)
    /* It didn't match.  Output the string.  */
    o = variable_buffer_output (o, t, len);
      else
    {
      /* It matched.  Output the replacement.  */

      /* Output the part of the replacement before the %.  */
      o = variable_buffer_output (o, replace, replace_prepercent_len);

      if (replace_percent != 0)
        {
          /* Output the part of the matched string that
         matched the % in the pattern.  */
          o = variable_buffer_output (o, t + pattern_prepercent_len,
                      len - (pattern_prepercent_len
                         + pattern_postpercent_len));
          /* Output the part of the replacement after the %.  */
          o = variable_buffer_output (o, replace_percent,
                      replace_postpercent_len);
        }
    }

      /* Output a space, but not if the replacement is "".  */
      if (fail || replace_prepercent_len > 0
      || (replace_percent != 0 && len + replace_postpercent_len > 0))
    {
      o = variable_buffer_output (o, " ", 1);
      doneany = 1;
    }
    }
  if (doneany)
    /* Kill the last space.  */
    --o;

  return o;
}

/* Store into VARIABLE_BUFFER at O the result of scanning TEXT
   and replacing strings matching PATTERN with REPLACE.
   If PATTERN_PERCENT is not nil, PATTERN has already been
   run through find_percent, and PATTERN_PERCENT is the result.
   If REPLACE_PERCENT is not nil, REPLACE has already been
   run through find_percent, and REPLACE_PERCENT is the result.
   Note that we expect PATTERN_PERCENT and REPLACE_PERCENT to point to the
   character _AFTER_ the %, not to the % itself.
*/

char *
patsubst_expand (char *o, const char *text, char *pattern, char *replace)
{
  const char *pattern_percent = find_percent (pattern);
  const char *replace_percent = find_percent (replace);

  /* If there's a percent in the pattern or replacement skip it.  */
  if (replace_percent)
    ++replace_percent;
  if (pattern_percent)
    ++pattern_percent;

  return patsubst_expand_pat (o, text, pattern, replace,
                              pattern_percent, replace_percent);
}



/* Look up a function by name.  */

static const struct function_table_entry *
lookup_function (const char *s)
{
  const char *e = s;

  while (*e && ( (*e >= 'a' && *e <= 'z') || *e == '-'))
    e++;
  if (*e == '\0' || isblank ((unsigned char) *e))
    {
      struct function_table_entry function_table_entry_key;
      function_table_entry_key.name = s;
      function_table_entry_key.len = e - s;

      return hash_find_item (&function_table, &function_table_entry_key);
    }
  return 0;
}



/* Return 1 if PATTERN matches STR, 0 if not.  */

int
pattern_matches (const char *pattern, const char *percent, const char *str)
{
  unsigned int sfxlen, strlength;

  if (percent == 0)
    {
      unsigned int len = strlen (pattern) + 1;
      char *new_chars = alloca (len);
      memcpy (new_chars, pattern, len);
      percent = find_percent (new_chars);
      if (percent == 0)
    return streq (new_chars, str);
      pattern = new_chars;
    }

  sfxlen = strlen (percent + 1);
  strlength = strlen (str);

  if (strlength < (percent - pattern) + sfxlen
      || !strneq (pattern, str, percent - pattern))
    return 0;

  return !strcmp (percent + 1, str + (strlength - sfxlen));
}



/* Find the next comma or ENDPAREN (counting nested STARTPAREN and
   ENDPARENtheses), starting at PTR before END.  Return a pointer to
   next character.

   If no next argument is found, return NULL.
*/

static char *
find_next_argument (char startparen, char endparen,
                    const char *ptr, const char *end)
{
  int count = 0;

  for (; ptr < end; ++ptr)
    if (*ptr == startparen)
      ++count;

    else if (*ptr == endparen)
      {
    --count;
    if (count < 0)
      return NULL;
      }

    else if (*ptr == ',' && !count)
      return (char *)ptr;

  /* We didn't find anything.  */
  return NULL;
}



/* Glob-expand LINE.  The returned pointer is
   only good until the next call to string_glob.  */

static char *
string_glob (char *line)
{
  static char *result = 0;
  static unsigned int length;
  struct nameseq *chain;
  unsigned int idx;

  chain = PARSE_FILE_SEQ (&line, struct nameseq, '\0', NULL,
                          /* We do not want parse_file_seq to strip `./'s.
                             That would break examples like:
                             $(patsubst ./%.c,obj/%.o,$(wildcard ./?*.c)).  */
                          PARSEFS_NOSTRIP|PARSEFS_NOCACHE|PARSEFS_EXISTS);

  if (result == 0)
    {
      length = 100;
      result = xmalloc (100);
    }

  idx = 0;
  while (chain != 0)
    {
      struct nameseq *next = chain->next;
      unsigned int len = strlen (chain->name);

      if (idx + len + 1 > length)
        {
          length += (len + 1) * 2;
          result = xrealloc (result, length);
        }
      memcpy (&result[idx], chain->name, len);
      idx += len;
      result[idx++] = ' ';

      /* Because we used PARSEFS_NOCACHE above, we have to free() NAME.  */
      free ((char *)chain->name);
      free (chain);
      chain = next;
    }

  /* Kill the last space and terminate the string.  */
  if (idx == 0)
    result[0] = '\0';
  else
    result[idx - 1] = '\0';

  return result;
}


/*
  Builtin functions
 */

static char *
func_patsubst (char *o, char **argv, const char *funcname UNUSED)
{
  o = patsubst_expand (o, argv[2], argv[0], argv[1]);
  return o;
}


static char *
func_join (char *o, char **argv, const char *funcname UNUSED)
{
  int doneany = 0;

  /* Write each word of the first argument directly followed
     by the corresponding word of the second argument.
     If the two arguments have a different number of words,
     the excess words are just output separated by blanks.  */
  const char *tp;
  const char *pp;
  const char *list1_iterator = argv[0];
  const char *list2_iterator = argv[1];
  do
    {
      unsigned int len1, len2;

      tp = find_next_token (&list1_iterator, &len1);
      if (tp != 0)
    o = variable_buffer_output (o, tp, len1);

      pp = find_next_token (&list2_iterator, &len2);
      if (pp != 0)
    o = variable_buffer_output (o, pp, len2);

      if (tp != 0 || pp != 0)
    {
      o = variable_buffer_output (o, " ", 1);
      doneany = 1;
    }
    }
  while (tp != 0 || pp != 0);
  if (doneany)
    /* Kill the last blank.  */
    --o;

  return o;
}


static char *
func_origin (char *o, char **argv, const char *funcname UNUSED)
{
  /* Expand the argument.  */
  struct variable *v = lookup_variable (argv[0], strlen (argv[0]));
  if (v == 0)
    o = variable_buffer_output (o, "undefined", 9);
  else
    switch (v->origin)
      {
      default:
      case o_invalid:
    abort ();
    break;
      case o_default:
    o = variable_buffer_output (o, "default", 7);
    break;
      case o_env:
    o = variable_buffer_output (o, "environment", 11);
    break;
      case o_file:
    o = variable_buffer_output (o, "file", 4);
    break;
      case o_env_override:
    o = variable_buffer_output (o, "environment override", 20);
    break;
      case o_command:
    o = variable_buffer_output (o, "command line", 12);
    break;
      case o_override:
    o = variable_buffer_output (o, "override", 8);
    break;
      case o_automatic:
    o = variable_buffer_output (o, "automatic", 9);
    break;
      }

  return o;
}

static char *
func_flavor (char *o, char **argv, const char *funcname UNUSED)
{
  struct variable *v = lookup_variable (argv[0], strlen (argv[0]));

  if (v == 0)
    o = variable_buffer_output (o, "undefined", 9);
  else
    if (v->recursive)
      o = variable_buffer_output (o, "recursive", 9);
    else
      o = variable_buffer_output (o, "simple", 6);

  return o;
}

#ifdef VMS
# define IS_PATHSEP(c) ((c) == ']')
#else
# ifdef HAVE_DOS_PATHS
#  define IS_PATHSEP(c) ((c) == '/' || (c) == '\\')
# else
#  define IS_PATHSEP(c) ((c) == '/')
# endif
#endif


static char *
func_notdir_suffix (char *o, char **argv, const char *funcname)
{
  /* Expand the argument.  */
  const char *list_iterator = argv[0];
  const char *p2;
  int doneany =0;
  unsigned int len=0;

  int is_suffix = streq (funcname, "suffix");
  int is_notdir = !is_suffix;
  while ((p2 = find_next_token (&list_iterator, &len)) != 0)
    {
      const char *p = p2 + len;


      while (p >= p2 && (!is_suffix || *p != '.'))
    {
      if (IS_PATHSEP (*p))
        break;
      --p;
    }

      if (p >= p2)
    {
      if (is_notdir)
        ++p;
      else if (*p != '.')
        continue;
      o = variable_buffer_output (o, p, len - (p - p2));
    }
#ifdef HAVE_DOS_PATHS
      /* Handle the case of "d:foo/bar".  */
      else if (streq (funcname, "notdir") && p2[0] && p2[1] == ':')
    {
      p = p2 + 2;
      o = variable_buffer_output (o, p, len - (p - p2));
    }
#endif
      else if (is_notdir)
    o = variable_buffer_output (o, p2, len);

      if (is_notdir || p >= p2)
    {
      o = variable_buffer_output (o, " ", 1);
      doneany = 1;
    }
    }

  if (doneany)
    /* Kill last space.  */
    --o;

  return o;
}


static char *
func_basename_dir (char *o, char **argv, const char *funcname)
{
  /* Expand the argument.  */
  const char *p3 = argv[0];
  const char *p2;
  int doneany=0;
  unsigned int len=0;

  int is_basename= streq (funcname, "basename");
  int is_dir= !is_basename;

  while ((p2 = find_next_token (&p3, &len)) != 0)
    {
      const char *p = p2 + len;
      while (p >= p2 && (!is_basename  || *p != '.'))
        {
          if (IS_PATHSEP (*p))
            break;
          --p;
        }

      if (p >= p2 && (is_dir))
        o = variable_buffer_output (o, p2, ++p - p2);
      else if (p >= p2 && (*p == '.'))
        o = variable_buffer_output (o, p2, p - p2);
#ifdef HAVE_DOS_PATHS
      /* Handle the "d:foobar" case */
      else if (p2[0] && p2[1] == ':' && is_dir)
        o = variable_buffer_output (o, p2, 2);
#endif
      else if (is_dir)
#ifdef VMS
        o = variable_buffer_output (o, "[]", 2);
#else
#ifndef _AMIGA
      o = variable_buffer_output (o, "./", 2);
#else
      ; /* Just a nop...  */
#endif /* AMIGA */
#endif /* !VMS */
      else
        /* The entire name is the basename.  */
        o = variable_buffer_output (o, p2, len);

      o = variable_buffer_output (o, " ", 1);
      doneany = 1;
    }

  if (doneany)
    /* Kill last space.  */
    --o;

  return o;
}

static char *
func_addsuffix_addprefix (char *o, char **argv, const char *funcname)
{
  int fixlen = strlen (argv[0]);
  const char *list_iterator = argv[1];
  int is_addprefix = streq (funcname, "addprefix");
  int is_addsuffix = !is_addprefix;

  int doneany = 0;
  const char *p;
  unsigned int len;

  while ((p = find_next_token (&list_iterator, &len)) != 0)
    {
      if (is_addprefix)
    o = variable_buffer_output (o, argv[0], fixlen);
      o = variable_buffer_output (o, p, len);
      if (is_addsuffix)
    o = variable_buffer_output (o, argv[0], fixlen);
      o = variable_buffer_output (o, " ", 1);
      doneany = 1;
    }

  if (doneany)
    /* Kill last space.  */
    --o;

  return o;
}

static char *
func_subst (char *o, char **argv, const char *funcname UNUSED)
{
  o = subst_expand (o, argv[2], argv[0], argv[1], strlen (argv[0]),
            strlen (argv[1]), 0);

  return o;
}


static char *
func_firstword (char *o, char **argv, const char *funcname UNUSED)
{
  unsigned int i;
  const char *words = argv[0];    /* Use a temp variable for find_next_token */
  const char *p = find_next_token (&words, &i);

  if (p != 0)
    o = variable_buffer_output (o, p, i);

  return o;
}

static char *
func_lastword (char *o, char **argv, const char *funcname UNUSED)
{
  unsigned int i;
  const char *words = argv[0];    /* Use a temp variable for find_next_token */
  const char *p = NULL;
  const char *t;

  while ((t = find_next_token (&words, &i)))
    p = t;

  if (p != 0)
    o = variable_buffer_output (o, p, i);

  return o;
}

static char *
func_words (char *o, char **argv, const char *funcname UNUSED)
{
  int i = 0;
  const char *word_iterator = argv[0];
  char buf[20];

  while (find_next_token (&word_iterator, (unsigned int *) 0) != 0)
    ++i;

  sprintf (buf, "%d", i);
  o = variable_buffer_output (o, buf, strlen (buf));

  return o;
}

/* Set begpp to point to the first non-whitespace character of the string,
 * and endpp to point to the last non-whitespace character of the string.
 * If the string is empty or contains nothing but whitespace, endpp will be
 * begpp-1.
 */
char *
strip_whitespace (const char **begpp, const char **endpp)
{
  while (*begpp <= *endpp && isspace ((unsigned char)**begpp))
    (*begpp) ++;
  while (*endpp >= *begpp && isspace ((unsigned char)**endpp))
    (*endpp) --;
  return (char *)*begpp;
}

static void
check_numeric (const char *s, const char *msg)
{
  const char *end = s + strlen (s) - 1;
  const char *beg = s;
  strip_whitespace (&s, &end);

  for (; s <= end; ++s)
    if (!ISDIGIT (*s))  /* ISDIGIT only evals its arg once: see make.h.  */
      break;

  if (s <= end || end - beg < 0)
    fatal (*expanding_var, "%s: '%s'", msg, beg);
}



static char *
func_word (char *o, char **argv, const char *funcname UNUSED)
{
  const char *end_p;
  const char *p;
  int i;

  /* Check the first argument.  */
  check_numeric (argv[0], _("non-numeric first argument to `word' function"));
  i = atoi (argv[0]);

  if (i == 0)
    fatal (*expanding_var,
           _("first argument to `word' function must be greater than 0"));

  end_p = argv[1];
  while ((p = find_next_token (&end_p, 0)) != 0)
    if (--i == 0)
      break;

  if (i == 0)
    o = variable_buffer_output (o, p, end_p - p);

  return o;
}

static char *
func_wordlist (char *o, char **argv, const char *funcname UNUSED)
{
  int start, count;

  /* Check the arguments.  */
  check_numeric (argv[0],
         _("non-numeric first argument to `wordlist' function"));
  check_numeric (argv[1],
         _("non-numeric second argument to `wordlist' function"));

  start = atoi (argv[0]);
  if (start < 1)
    fatal (*expanding_var,
           "invalid first argument to `wordlist' function: `%d'", start);

  count = atoi (argv[1]) - start + 1;

  if (count > 0)
    {
      const char *p;
      const char *end_p = argv[2];

      /* Find the beginning of the "start"th word.  */
      while (((p = find_next_token (&end_p, 0)) != 0) && --start)
        ;

      if (p)
        {
          /* Find the end of the "count"th word from start.  */
          while (--count && (find_next_token (&end_p, 0) != 0))
            ;

          /* Return the stuff in the middle.  */
          o = variable_buffer_output (o, p, end_p - p);
        }
    }

  return o;
}

static char *
func_findstring (char *o, char **argv, const char *funcname UNUSED)
{
  /* Find the first occurrence of the first string in the second.  */
  if (strstr (argv[1], argv[0]) != 0)
    o = variable_buffer_output (o, argv[0], strlen (argv[0]));

  return o;
}

static char *
func_foreach (char *o, char **argv, const char *funcname UNUSED)
{
  /* expand only the first two.  */
  char *varname = expand_argument (argv[0], NULL);
  char *list = expand_argument (argv[1], NULL);
  const char *body = argv[2];

  int doneany = 0;
  const char *list_iterator = list;
  const char *p;
  unsigned int len;
  struct variable *var;

  push_new_variable_scope ();
  var = define_variable (varname, strlen (varname), "", o_automatic, 0);

  /* loop through LIST,  put the value in VAR and expand BODY */
  while ((p = find_next_token (&list_iterator, &len)) != 0)
    {
      char *result = 0;

      free (var->value);
      var->value = xstrndup (p, len);

      result = allocated_variable_expand (body);

      o = variable_buffer_output (o, result, strlen (result));
      o = variable_buffer_output (o, " ", 1);
      doneany = 1;
      free (result);
    }

  if (doneany)
    /* Kill the last space.  */
    --o;

  pop_variable_scope ();
  free (varname);
  free (list);

  return o;
}

struct a_word
{
  struct a_word *next;
  struct a_word *chain;
  char *str;
  int length;
  int matched;
};

static unsigned long
a_word_hash_1 (const void *key)
{
  return_STRING_HASH_1 (((struct a_word const *) key)->str);
}

static unsigned long
a_word_hash_2 (const void *key)
{
  return_STRING_HASH_2 (((struct a_word const *) key)->str);
}

static int
a_word_hash_cmp (const void *x, const void *y)
{
  int result = ((struct a_word const *) x)->length - ((struct a_word const *) y)->length;
  if (result)
    return result;
  return_STRING_COMPARE (((struct a_word const *) x)->str,
             ((struct a_word const *) y)->str);
}

struct a_pattern
{
  struct a_pattern *next;
  char *str;
  char *percent;
  int length;
  int save_c;
};

static char *
func_filter_filterout (char *o, char **argv, const char *funcname)
{
  struct a_word *wordhead;
  struct a_word **wordtail;
  struct a_word *wp;
  struct a_pattern *pathead;
  struct a_pattern **pattail;
  struct a_pattern *pp;

  struct hash_table a_word_table;
  int is_filter = streq (funcname, "filter");
  const char *pat_iterator = argv[0];
  const char *word_iterator = argv[1];
  int literals = 0;
  int words = 0;
  int hashing = 0;
  char *p;
  unsigned int len;

  /* Chop ARGV[0] up into patterns to match against the words.  */

  pattail = &pathead;
  while ((p = find_next_token (&pat_iterator, &len)) != 0)
    {
      struct a_pattern *pat = alloca (sizeof (struct a_pattern));

      *pattail = pat;
      pattail = &pat->next;

      if (*pat_iterator != '\0')
    ++pat_iterator;

      pat->str = p;
      pat->length = len;
      pat->save_c = p[len];
      p[len] = '\0';
      pat->percent = find_percent (p);
      if (pat->percent == 0)
    literals++;
    }
  *pattail = 0;

  /* Chop ARGV[1] up into words to match against the patterns.  */

  wordtail = &wordhead;
  while ((p = find_next_token (&word_iterator, &len)) != 0)
    {
      struct a_word *word = alloca (sizeof (struct a_word));

      *wordtail = word;
      wordtail = &word->next;

      if (*word_iterator != '\0')
    ++word_iterator;

      p[len] = '\0';
      word->str = p;
      word->length = len;
      word->matched = 0;
      word->chain = 0;
      words++;
    }
  *wordtail = 0;

  /* Only use a hash table if arg list lengths justifies the cost.  */
  hashing = (literals >= 2 && (literals * words) >= 10);
  if (hashing)
    {
      hash_init (&a_word_table, words, a_word_hash_1, a_word_hash_2,
                 a_word_hash_cmp);
      for (wp = wordhead; wp != 0; wp = wp->next)
    {
      struct a_word *owp = hash_insert (&a_word_table, wp);
      if (owp)
        wp->chain = owp;
    }
    }

  if (words)
    {
      int doneany = 0;

      /* Run each pattern through the words, killing words.  */
      for (pp = pathead; pp != 0; pp = pp->next)
    {
      if (pp->percent)
        for (wp = wordhead; wp != 0; wp = wp->next)
          wp->matched |= pattern_matches (pp->str, pp->percent, wp->str);
      else if (hashing)
        {
          struct a_word a_word_key;
          a_word_key.str = pp->str;
          a_word_key.length = pp->length;
          wp = hash_find_item (&a_word_table, &a_word_key);
          while (wp)
        {
          wp->matched |= 1;
          wp = wp->chain;
        }
        }
      else
        for (wp = wordhead; wp != 0; wp = wp->next)
          wp->matched |= (wp->length == pp->length
                  && strneq (pp->str, wp->str, wp->length));
    }

      /* Output the words that matched (or didn't, for filter-out).  */
      for (wp = wordhead; wp != 0; wp = wp->next)
    if (is_filter ? wp->matched : !wp->matched)
      {
        o = variable_buffer_output (o, wp->str, strlen (wp->str));
        o = variable_buffer_output (o, " ", 1);
        doneany = 1;
      }

      if (doneany)
    /* Kill the last space.  */
    --o;
    }

  for (pp = pathead; pp != 0; pp = pp->next)
    pp->str[pp->length] = pp->save_c;

  if (hashing)
    hash_free (&a_word_table, 0);

  return o;
}


static char *
func_strip (char *o, char **argv, const char *funcname UNUSED)
{
  const char *p = argv[0];
  int doneany = 0;

  while (*p != '\0')
    {
      int i=0;
      const char *word_start;

      while (isspace ((unsigned char)*p))
    ++p;
      word_start = p;
      for (i=0; *p != '\0' && !isspace ((unsigned char)*p); ++p, ++i)
    {}
      if (!i)
    break;
      o = variable_buffer_output (o, word_start, i);
      o = variable_buffer_output (o, " ", 1);
      doneany = 1;
    }

  if (doneany)
    /* Kill the last space.  */
    --o;

  return o;
}

/*
  Print a warning or fatal message.
*/
static char *
func_error (char *o, char **argv, const char *funcname)
{
  char **argvp;
  char *msg, *p;
  int len;

  /* The arguments will be broken on commas.  Rather than create yet
     another special case where function arguments aren't broken up,
     just create a format string that puts them back together.  */
  for (len=0, argvp=argv; *argvp != 0; ++argvp)
    len += strlen (*argvp) + 2;

  p = msg = alloca (len + 1);

  for (argvp=argv; argvp[1] != 0; ++argvp)
    {
      strcpy (p, *argvp);
      p += strlen (*argvp);
      *(p++) = ',';
      *(p++) = ' ';
    }
  strcpy (p, *argvp);

  switch (*funcname) {
    case 'e':
      fatal (reading_file, "%s", msg);

    case 'w':
      error (reading_file, "%s", msg);
      break;

    case 'i':
      printf ("%s\n", msg);
      fflush(stdout);
      break;

    default:
      fatal (*expanding_var, "Internal error: func_error: '%s'", funcname);
  }

  /* The warning function expands to the empty string.  */
  return o;
}


/*
  chop argv[0] into words, and sort them.
 */
static char *
func_sort (char *o, char **argv, const char *funcname UNUSED)
{
  const char *t;
  char **words;
  int wordi;
  char *p;
  unsigned int len;
  int i;

  /* Find the maximum number of words we'll have.  */
  t = argv[0];
  wordi = 1;
  while (*t != '\0')
    {
      char c = *(t++);

      if (! isspace ((unsigned char)c))
        continue;

      ++wordi;

      while (isspace ((unsigned char)*t))
        ++t;
    }

  words = xmalloc (wordi * sizeof (char *));

  /* Now assign pointers to each string in the array.  */
  t = argv[0];
  wordi = 0;
  while ((p = find_next_token (&t, &len)) != 0)
    {
      ++t;
      p[len] = '\0';
      words[wordi++] = p;
    }

  if (wordi)
    {
      /* Now sort the list of words.  */
      qsort (words, wordi, sizeof (char *), alpha_compare);

      /* Now write the sorted list, uniquified.  */
      for (i = 0; i < wordi; ++i)
        {
          len = strlen (words[i]);
          if (i == wordi - 1 || strlen (words[i + 1]) != len
              || strcmp (words[i], words[i + 1]))
            {
              o = variable_buffer_output (o, words[i], len);
              o = variable_buffer_output (o, " ", 1);
            }
        }

      /* Kill the last space.  */
      --o;
    }

  free (words);

  return o;
}

/*
  $(if condition,true-part[,false-part])

  CONDITION is false iff it evaluates to an empty string.  White
  space before and after condition are stripped before evaluation.

  If CONDITION is true, then TRUE-PART is evaluated, otherwise FALSE-PART is
  evaluated (if it exists).  Because only one of the two PARTs is evaluated,
  you can use $(if ...) to create side-effects (with $(shell ...), for
  example).
*/

static char *
func_if (char *o, char **argv, const char *funcname UNUSED)
{
  const char *begp = argv[0];
  const char *endp = begp + strlen (argv[0]) - 1;
  int result = 0;

  /* Find the result of the condition: if we have a value, and it's not
     empty, the condition is true.  If we don't have a value, or it's the
     empty string, then it's false.  */

  strip_whitespace (&begp, &endp);

  if (begp <= endp)
    {
      char *expansion = expand_argument (begp, endp+1);

      result = strlen (expansion);
      free (expansion);
    }

  /* If the result is true (1) we want to eval the first argument, and if
     it's false (0) we want to eval the second.  If the argument doesn't
     exist we do nothing, otherwise expand it and add to the buffer.  */

  argv += 1 + !result;

  if (*argv)
    {
      char *expansion = expand_argument (*argv, NULL);

      o = variable_buffer_output (o, expansion, strlen (expansion));

      free (expansion);
    }

  return o;
}

/*
  $(or condition1[,condition2[,condition3[...]]])

  A CONDITION is false iff it evaluates to an empty string.  White
  space before and after CONDITION are stripped before evaluation.

  CONDITION1 is evaluated.  If it's true, then this is the result of
  expansion.  If it's false, CONDITION2 is evaluated, and so on.  If none of
  the conditions are true, the expansion is the empty string.

  Once a CONDITION is true no further conditions are evaluated
  (short-circuiting).
*/

static char *
func_or (char *o, char **argv, const char *funcname UNUSED)
{
  for ( ; *argv ; ++argv)
    {
      const char *begp = *argv;
      const char *endp = begp + strlen (*argv) - 1;
      char *expansion;
      int result = 0;

      /* Find the result of the condition: if it's false keep going.  */

      strip_whitespace (&begp, &endp);

      if (begp > endp)
        continue;

      expansion = expand_argument (begp, endp+1);
      result = strlen (expansion);

      /* If the result is false keep going.  */
      if (!result)
        {
          free (expansion);
          continue;
        }

      /* It's true!  Keep this result and return.  */
      o = variable_buffer_output (o, expansion, result);
      free (expansion);
      break;
    }

  return o;
}

/*
  $(and condition1[,condition2[,condition3[...]]])

  A CONDITION is false iff it evaluates to an empty string.  White
  space before and after CONDITION are stripped before evaluation.

  CONDITION1 is evaluated.  If it's false, then this is the result of
  expansion.  If it's true, CONDITION2 is evaluated, and so on.  If all of
  the conditions are true, the expansion is the result of the last condition.

  Once a CONDITION is false no further conditions are evaluated
  (short-circuiting).
*/

static char *
func_and (char *o, char **argv, const char *funcname UNUSED)
{
  char *expansion;
  int result;

  while (1)
    {
      const char *begp = *argv;
      const char *endp = begp + strlen (*argv) - 1;

      /* An empty condition is always false.  */
      strip_whitespace (&begp, &endp);
      if (begp > endp)
        return o;

      expansion = expand_argument (begp, endp+1);
      result = strlen (expansion);

      /* If the result is false, stop here: we're done.  */
      if (!result)
        break;

      /* Otherwise the result is true.  If this is the last one, keep this
         result and quit.  Otherwise go on to the next one!  */

      if (*(++argv))
        free (expansion);
      else
        {
          o = variable_buffer_output (o, expansion, result);
          break;
        }
    }

  free (expansion);

  return o;
}

static char *
func_wildcard (char *o, char **argv, const char *funcname UNUSED)
{
#ifdef _AMIGA
   o = wildcard_expansion (argv[0], o);
#else
   char *p = string_glob (argv[0]);
   o = variable_buffer_output (o, p, strlen (p));
#endif
   return o;
}

/*
  $(eval <makefile string>)

  Always resolves to the empty string.

  Treat the arguments as a segment of makefile, and parse them.
*/

static char *
func_eval (char *o, char **argv, const char *funcname UNUSED)
{
  char *buf;
  unsigned int len;

  /* Eval the buffer.  Pop the current variable buffer setting so that the
     eval'd code can use its own without conflicting.  */

  install_variable_buffer (&buf, &len);

  eval_buffer (argv[0]);

  restore_variable_buffer (buf, len);

  return o;
}


static char *
func_value (char *o, char **argv, const char *funcname UNUSED)
{
  /* Look up the variable.  */
  struct variable *v = lookup_variable (argv[0], strlen (argv[0]));

  /* Copy its value into the output buffer without expanding it.  */
  if (v)
    o = variable_buffer_output (o, v->value, strlen(v->value));

  return o;
}

/*
  \r  is replaced on UNIX as well. Is this desirable?
 */
static void
fold_newlines (char *buffer, unsigned int *length)
{
  char *dst = buffer;
  char *src = buffer;
  char *last_nonnl = buffer -1;
  src[*length] = 0;
  for (; *src != '\0'; ++src)
    {
      if (src[0] == '\r' && src[1] == '\n')
    continue;
      if (*src == '\n')
    {
      *dst++ = ' ';
    }
      else
    {
      last_nonnl = dst;
      *dst++ = *src;
    }
    }
  *(++last_nonnl) = '\0';
  *length = last_nonnl - buffer;
}



int shell_function_pid = 0, shell_function_completed;


#ifdef WINDOWS32
/*untested*/

#include <windows.h>
#include <io.h>
#include "sub_proc.h"


void
windows32_openpipe (int *pipedes, pid_t *pid_p, char **command_argv, char **envp)
{
  SECURITY_ATTRIBUTES saAttr;
  HANDLE hIn;
  HANDLE hErr;
  HANDLE hChildOutRd;
  HANDLE hChildOutWr;
  HANDLE hProcess;


  saAttr.nLength = sizeof (SECURITY_ATTRIBUTES);
  saAttr.bInheritHandle = TRUE;
  saAttr.lpSecurityDescriptor = NULL;

  if (DuplicateHandle (GetCurrentProcess(),
              GetStdHandle(STD_INPUT_HANDLE),
              GetCurrentProcess(),
              &hIn,
              0,
              TRUE,
              DUPLICATE_SAME_ACCESS) == FALSE) {
    fatal (NILF, _("windows32_openpipe(): DuplicateHandle(In) failed (e=%ld)\n"),
       GetLastError());

  }
  if (DuplicateHandle(GetCurrentProcess(),
              GetStdHandle(STD_ERROR_HANDLE),
              GetCurrentProcess(),
              &hErr,
              0,
              TRUE,
              DUPLICATE_SAME_ACCESS) == FALSE) {
    fatal (NILF, _("windows32_open_pipe(): DuplicateHandle(Err) failed (e=%ld)\n"),
       GetLastError());
  }

  if (!CreatePipe(&hChildOutRd, &hChildOutWr, &saAttr, 0))
    fatal (NILF, _("CreatePipe() failed (e=%ld)\n"), GetLastError());

  hProcess = process_init_fd(hIn, hChildOutWr, hErr);

  if (!hProcess)
    fatal (NILF, _("windows32_openpipe(): process_init_fd() failed\n"));

  /* make sure that CreateProcess() has Path it needs */
  sync_Path_environment();
  /* `sync_Path_environment' may realloc `environ', so take note of
     the new value.  */
  envp = environ;

  if (!process_begin(hProcess, command_argv, envp, command_argv[0], NULL)) {
    /* register process for wait */
    process_register(hProcess);

    /* set the pid for returning to caller */
    *pid_p = (pid_t) hProcess;

  /* set up to read data from child */
  pipedes[0] = _open_osfhandle((intptr_t) hChildOutRd, O_RDONLY);

  /* this will be closed almost right away */
  pipedes[1] = _open_osfhandle((intptr_t) hChildOutWr, O_APPEND);
  } else {
    /* reap/cleanup the failed process */
    process_cleanup(hProcess);

    /* close handles which were duplicated, they weren't used */
    CloseHandle(hIn);
    CloseHandle(hErr);

    /* close pipe handles, they won't be used */
    CloseHandle(hChildOutRd);
    CloseHandle(hChildOutWr);

    /* set status for return */
    pipedes[0] = pipedes[1] = -1;
    *pid_p = (pid_t)-1;
  }
}
#endif


#ifdef __MSDOS__
FILE *
msdos_openpipe (int* pipedes, int *pidp, char *text)
{
  FILE *fpipe=0;
  /* MSDOS can't fork, but it has `popen'.  */
  struct variable *sh = lookup_variable ("SHELL", 5);
  int e;
  extern int dos_command_running, dos_status;

  /* Make sure not to bother processing an empty line.  */
  while (isblank ((unsigned char)*text))
    ++text;
  if (*text == '\0')
    return 0;

  if (sh)
    {
      char buf[PATH_MAX + 7];
      /* This makes sure $SHELL value is used by $(shell), even
     though the target environment is not passed to it.  */
      sprintf (buf, "SHELL=%s", sh->value);
      putenv (buf);
    }

  e = errno;
  errno = 0;
  dos_command_running = 1;
  dos_status = 0;
  /* If dos_status becomes non-zero, it means the child process
     was interrupted by a signal, like SIGINT or SIGQUIT.  See
     fatal_error_signal in commands.c.  */
  fpipe = popen (text, "rt");
  dos_command_running = 0;
  if (!fpipe || dos_status)
    {
      pipedes[0] = -1;
      *pidp = -1;
      if (dos_status)
    errno = EINTR;
      else if (errno == 0)
    errno = ENOMEM;
      shell_function_completed = -1;
    }
  else
    {
      pipedes[0] = fileno (fpipe);
      *pidp = 42; /* Yes, the Meaning of Life, the Universe, and Everything! */
      errno = e;
      shell_function_completed = 1;
    }
  return fpipe;
}
#endif

/*
  Do shell spawning, with the naughty bits for different OSes.
 */

#ifdef VMS

/* VMS can't do $(shell ...)  */
#define func_shell 0

#else
#ifndef _AMIGA
static char *
func_shell (char *o, char **argv, const char *funcname UNUSED)
{
  char *batch_filename = NULL;

#ifdef __MSDOS__
  FILE *fpipe;
#endif
  char **command_argv;
  const char *error_prefix;
  char **envp;
  int pipedes[2];
  pid_t pid;

#ifndef __MSDOS__
  /* Construct the argument list.  */
  command_argv = construct_command_argv (argv[0], NULL, NULL, 0,
                                         &batch_filename);
  if (command_argv == 0)
    return o;
#endif

  /* Using a target environment for `shell' loses in cases like:
     export var = $(shell echo foobie)
     because target_environment hits a loop trying to expand $(var)
     to put it in the environment.  This is even more confusing when
     var was not explicitly exported, but just appeared in the
     calling environment.

     See Savannah bug #10593.

  envp = target_environment (NILF);
  */

  envp = environ;

  /* For error messages.  */
  if (reading_file && reading_file->filenm)
    {
      char *p = alloca (strlen (reading_file->filenm)+11+4);
      sprintf (p, "%s:%lu: ", reading_file->filenm, reading_file->lineno);
      error_prefix = p;
    }
  else
    error_prefix = "";

#if defined(__MSDOS__)
  fpipe = msdos_openpipe (pipedes, &pid, argv[0]);
  if (pipedes[0] < 0)
    {
      perror_with_name (error_prefix, "pipe");
      return o;
    }
#elif defined(WINDOWS32)
  windows32_openpipe (pipedes, &pid, command_argv, envp);
  if (pipedes[0] < 0)
    {
      /* open of the pipe failed, mark as failed execution */
      shell_function_completed = -1;

      return o;
    }
  else
#else
  if (pipe (pipedes) < 0)
    {
      perror_with_name (error_prefix, "pipe");
      return o;
    }

# ifdef __EMX__
  /* close some handles that are unnecessary for the child process */
  CLOSE_ON_EXEC(pipedes[1]);
  CLOSE_ON_EXEC(pipedes[0]);
  /* Never use fork()/exec() here! Use spawn() instead in exec_command() */
  pid = child_execute_job (0, pipedes[1], command_argv, envp);
  if (pid < 0)
    perror_with_name (error_prefix, "spawn");
# else /* ! __EMX__ */
  pid = vfork ();
  if (pid < 0)
    perror_with_name (error_prefix, "fork");
  else if (pid == 0)
    child_execute_job (0, pipedes[1], command_argv, envp);
  else
# endif
#endif
    {
      /* We are the parent.  */
      char *buffer;
      unsigned int maxlen, i;
      int cc;

      /* Record the PID for reap_children.  */
      shell_function_pid = pid;
#ifndef  __MSDOS__
      shell_function_completed = 0;

      /* Free the storage only the child needed.  */
      free (command_argv[0]);
      free (command_argv);

      /* Close the write side of the pipe.  We test for -1, since
     pipedes[1] is -1 on MS-Windows, and some versions of MS
     libraries barf when `close' is called with -1.  */
      if (pipedes[1] >= 0)
    close (pipedes[1]);
#endif

      /* Set up and read from the pipe.  */

      maxlen = 200;
      buffer = xmalloc (maxlen + 1);

      /* Read from the pipe until it gets EOF.  */
      for (i = 0; ; i += cc)
    {
      if (i == maxlen)
        {
          maxlen += 512;
          buffer = xrealloc (buffer, maxlen + 1);
        }

      EINTRLOOP (cc, read (pipedes[0], &buffer[i], maxlen - i));
      if (cc <= 0)
        break;
    }
      buffer[i] = '\0';

      /* Close the read side of the pipe.  */
#ifdef  __MSDOS__
      if (fpipe)
    (void) pclose (fpipe);
#else
      (void) close (pipedes[0]);
#endif

      /* Loop until child_handler or reap_children()  sets
         shell_function_completed to the status of our child shell.  */
      while (shell_function_completed == 0)
    reap_children (1, 0);

      if (batch_filename) {
    DB (DB_VERBOSE, (_("Cleaning up temporary batch file %s\n"),
                       batch_filename));
    remove (batch_filename);
    free (batch_filename);
      }
      shell_function_pid = 0;

      /* The child_handler function will set shell_function_completed
     to 1 when the child dies normally, or to -1 if it
     dies with status 127, which is most likely an exec fail.  */

      if (shell_function_completed == -1)
    {
      /* This likely means that the execvp failed, so we should just
         write the error message in the pipe from the child.  */
      fputs (buffer, stderr);
      fflush (stderr);
    }
      else
    {
      /* The child finished normally.  Replace all newlines in its output
         with spaces, and put that in the variable output buffer.  */
      fold_newlines (buffer, &i);
      o = variable_buffer_output (o, buffer, i);
    }

      free (buffer);
    }

  return o;
}

#else   /* _AMIGA */

/* Do the Amiga version of func_shell.  */

static char *
func_shell (char *o, char **argv, const char *funcname)
{
  /* Amiga can't fork nor spawn, but I can start a program with
     redirection of my choice.  However, this means that we
     don't have an opportunity to reopen stdout to trap it.  Thus,
     we save our own stdout onto a new descriptor and dup a temp
     file's descriptor onto our stdout temporarily.  After we
     spawn the shell program, we dup our own stdout back to the
     stdout descriptor.  The buffer reading is the same as above,
     except that we're now reading from a file.  */

#include <dos/dos.h>
#include <proto/dos.h>

  BPTR child_stdout;
  char tmp_output[FILENAME_MAX];
  unsigned int maxlen = 200, i;
  int cc;
  char * buffer, * ptr;
  char ** aptr;
  int len = 0;
  char* batch_filename = NULL;

  /* Construct the argument list.  */
  command_argv = construct_command_argv (argv[0], NULL, NULL, 0,
                                         &batch_filename);
  if (command_argv == 0)
    return o;

  /* Note the mktemp() is a security hole, but this only runs on Amiga.
     Ideally we would use main.c:open_tmpfile(), but this uses a special
     Open(), not fopen(), and I'm not familiar enough with the code to mess
     with it.  */
  strcpy (tmp_output, "t:MakeshXXXXXXXX");
  mktemp (tmp_output);
  child_stdout = Open (tmp_output, MODE_NEWFILE);

  for (aptr=command_argv; *aptr; aptr++)
    len += strlen (*aptr) + 1;

  buffer = xmalloc (len + 1);
  ptr = buffer;

  for (aptr=command_argv; *aptr; aptr++)
    {
      strcpy (ptr, *aptr);
      ptr += strlen (ptr) + 1;
      *ptr ++ = ' ';
      *ptr = 0;
    }

  ptr[-1] = '\n';

  Execute (buffer, NULL, child_stdout);
  free (buffer);

  Close (child_stdout);

  child_stdout = Open (tmp_output, MODE_OLDFILE);

  buffer = xmalloc (maxlen);
  i = 0;
  do
    {
      if (i == maxlen)
    {
      maxlen += 512;
      buffer = xrealloc (buffer, maxlen + 1);
    }

      cc = Read (child_stdout, &buffer[i], maxlen - i);
      if (cc > 0)
    i += cc;
    } while (cc > 0);

  Close (child_stdout);

  fold_newlines (buffer, &i);
  o = variable_buffer_output (o, buffer, i);
  free (buffer);
  return o;
}
#endif  /* _AMIGA */
#endif  /* !VMS */

#ifdef EXPERIMENTAL

/*
  equality. Return is string-boolean, ie, the empty string is false.
 */
static char *
func_eq (char *o, char **argv, char *funcname)
{
  int result = ! strcmp (argv[0], argv[1]);
  o = variable_buffer_output (o,  result ? "1" : "", result);
  return o;
}


/*
  string-boolean not operator.
 */
static char *
func_not (char *o, char **argv, char *funcname)
{
  const char *s = argv[0];
  int result = 0;
  while (isspace ((unsigned char)*s))
    s++;
  result = ! (*s);
  o = variable_buffer_output (o,  result ? "1" : "", result);
  return o;
}
#endif



#ifdef HAVE_DOS_PATHS
#define IS_ABSOLUTE(n) (n[0] && n[1] == ':')
#define ROOT_LEN 3
#else
#define IS_ABSOLUTE(n) (n[0] == '/')
#define ROOT_LEN 1
#endif

/* Return the absolute name of file NAME which does not contain any `.',
   `..' components nor any repeated path separators ('/').   */

static char *
abspath (const char *name, char *apath)
{
  char *dest;
  const char *start, *end, *apath_limit;
  unsigned long root_len = ROOT_LEN;

  if (name[0] == '\0' || apath == NULL)
    return NULL;

  apath_limit = apath + GET_PATH_MAX;

  if (!IS_ABSOLUTE(name))
    {
      /* It is unlikely we would make it until here but just to make sure. */
      if (!starting_directory)
    return NULL;

      strcpy (apath, starting_directory);

#ifdef HAVE_DOS_PATHS
      if (IS_PATHSEP(name[0]))
    {
      if (IS_PATHSEP(name[1]))
        {
          /* A UNC.  Don't prepend a drive letter.  */
          apath[0] = name[0];
          apath[1] = name[1];
          root_len = 2;
        }
      /* We have /foo, an absolute file name except for the drive
         letter.  Assume the missing drive letter is the current
         drive, which we can get if we remove from starting_directory
         everything past the root directory.  */
      apath[root_len] = '\0';
    }
#endif

      dest = strchr (apath, '\0');
    }
  else
    {
      strncpy (apath, name, root_len);
      apath[root_len] = '\0';
      dest = apath + root_len;
      /* Get past the root, since we already copied it.  */
      name += root_len;
#ifdef HAVE_DOS_PATHS
      if (!IS_PATHSEP(apath[2]))
    {
      /* Convert d:foo into d:./foo and increase root_len.  */
      apath[2] = '.';
      apath[3] = '/';
      dest++;
      root_len++;
      /* strncpy above copied one character too many.  */
      name--;
    }
      else
    apath[2] = '/'; /* make sure it's a forward slash */
#endif
    }

  for (start = end = name; *start != '\0'; start = end)
    {
      unsigned long len;

      /* Skip sequence of multiple path-separators.  */
      while (IS_PATHSEP(*start))
    ++start;

      /* Find end of path component.  */
      for (end = start; *end != '\0' && !IS_PATHSEP(*end); ++end)
        ;

      len = end - start;

      if (len == 0)
    break;
      else if (len == 1 && start[0] == '.')
    /* nothing */;
      else if (len == 2 && start[0] == '.' && start[1] == '.')
    {
      /* Back up to previous component, ignore if at root already.  */
      if (dest > apath + root_len)
        for (--dest; !IS_PATHSEP(dest[-1]); --dest);
    }
      else
    {
      if (!IS_PATHSEP(dest[-1]))
            *dest++ = '/';

      if (dest + len >= apath_limit)
            return NULL;

      dest = memcpy (dest, start, len);
          dest += len;
      *dest = '\0';
    }
    }

  /* Unless it is root strip trailing separator.  */
  if (dest > apath + root_len && IS_PATHSEP(dest[-1]))
    --dest;

  *dest = '\0';

  return apath;
}


static char *
func_realpath (char *o, char **argv, const char *funcname UNUSED)
{
  /* Expand the argument.  */
  const char *p = argv[0];
  const char *path = 0;
  int doneany = 0;
  unsigned int len = 0;
#ifndef HAVE_REALPATH
  struct stat st;
#endif
  PATH_VAR (in);
  PATH_VAR (out);

  while ((path = find_next_token (&p, &len)) != 0)
    {
      if (len < GET_PATH_MAX)
        {
          strncpy (in, path, len);
          in[len] = '\0';

          if (
#ifdef HAVE_REALPATH
              realpath (in, out)
#else
              abspath (in, out) && stat (out, &st) == 0
#endif
             )
            {
              o = variable_buffer_output (o, out, strlen (out));
              o = variable_buffer_output (o, " ", 1);
              doneany = 1;
            }
        }
    }

  /* Kill last space.  */
  if (doneany)
    --o;

  return o;
}

static char *
func_abspath (char *o, char **argv, const char *funcname UNUSED)
{
  /* Expand the argument.  */
  const char *p = argv[0];
  const char *path = 0;
  int doneany = 0;
  unsigned int len = 0;
  PATH_VAR (in);
  PATH_VAR (out);

  while ((path = find_next_token (&p, &len)) != 0)
    {
      if (len < GET_PATH_MAX)
        {
          strncpy (in, path, len);
          in[len] = '\0';

          if (abspath (in, out))
            {
              o = variable_buffer_output (o, out, strlen (out));
              o = variable_buffer_output (o, " ", 1);
              doneany = 1;
            }
        }
    }

  /* Kill last space.  */
  if (doneany)
    --o;

  return o;
}

/* Lookup table for builtin functions.

   This doesn't have to be sorted; we use a straight lookup.  We might gain
   some efficiency by moving most often used functions to the start of the
   table.

   If MAXIMUM_ARGS is 0, that means there is no maximum and all
   comma-separated values are treated as arguments.

   EXPAND_ARGS means that all arguments should be expanded before invocation.
   Functions that do namespace tricks (foreach) don't automatically expand.  */

static char *func_call (char *o, char **argv, const char *funcname);


static struct function_table_entry function_table_init[] =
{
 /* Name/size */                    /* MIN MAX EXP? Function */
  { STRING_SIZE_TUPLE("abspath"),       0,  1,  1,  func_abspath},
  { STRING_SIZE_TUPLE("addprefix"),     2,  2,  1,  func_addsuffix_addprefix},
  { STRING_SIZE_TUPLE("addsuffix"),     2,  2,  1,  func_addsuffix_addprefix},
  { STRING_SIZE_TUPLE("basename"),      0,  1,  1,  func_basename_dir},
  { STRING_SIZE_TUPLE("dir"),           0,  1,  1,  func_basename_dir},
  { STRING_SIZE_TUPLE("notdir"),        0,  1,  1,  func_notdir_suffix},
  { STRING_SIZE_TUPLE("subst"),         3,  3,  1,  func_subst},
  { STRING_SIZE_TUPLE("suffix"),        0,  1,  1,  func_notdir_suffix},
  { STRING_SIZE_TUPLE("filter"),        2,  2,  1,  func_filter_filterout},
  { STRING_SIZE_TUPLE("filter-out"),    2,  2,  1,  func_filter_filterout},
  { STRING_SIZE_TUPLE("findstring"),    2,  2,  1,  func_findstring},
  { STRING_SIZE_TUPLE("firstword"),     0,  1,  1,  func_firstword},
  { STRING_SIZE_TUPLE("flavor"),        0,  1,  1,  func_flavor},
  { STRING_SIZE_TUPLE("join"),          2,  2,  1,  func_join},
  { STRING_SIZE_TUPLE("lastword"),      0,  1,  1,  func_lastword},
  { STRING_SIZE_TUPLE("patsubst"),      3,  3,  1,  func_patsubst},
  { STRING_SIZE_TUPLE("realpath"),      0,  1,  1,  func_realpath},
  { STRING_SIZE_TUPLE("shell"),         0,  1,  1,  func_shell},
  { STRING_SIZE_TUPLE("sort"),          0,  1,  1,  func_sort},
  { STRING_SIZE_TUPLE("strip"),         0,  1,  1,  func_strip},
  { STRING_SIZE_TUPLE("wildcard"),      0,  1,  1,  func_wildcard},
  { STRING_SIZE_TUPLE("word"),          2,  2,  1,  func_word},
  { STRING_SIZE_TUPLE("wordlist"),      3,  3,  1,  func_wordlist},
  { STRING_SIZE_TUPLE("words"),         0,  1,  1,  func_words},
  { STRING_SIZE_TUPLE("origin"),        0,  1,  1,  func_origin},
  { STRING_SIZE_TUPLE("foreach"),       3,  3,  0,  func_foreach},
  { STRING_SIZE_TUPLE("call"),          1,  0,  1,  func_call},
  { STRING_SIZE_TUPLE("info"),          0,  1,  1,  func_error},
  { STRING_SIZE_TUPLE("error"),         0,  1,  1,  func_error},
  { STRING_SIZE_TUPLE("warning"),       0,  1,  1,  func_error},
  { STRING_SIZE_TUPLE("if"),            2,  3,  0,  func_if},
  { STRING_SIZE_TUPLE("or"),            1,  0,  0,  func_or},
  { STRING_SIZE_TUPLE("and"),           1,  0,  0,  func_and},
  { STRING_SIZE_TUPLE("value"),         0,  1,  1,  func_value},
  { STRING_SIZE_TUPLE("eval"),          0,  1,  1,  func_eval},
#ifdef EXPERIMENTAL
  { STRING_SIZE_TUPLE("eq"),            2,  2,  1,  func_eq},
  { STRING_SIZE_TUPLE("not"),           0,  1,  1,  func_not},
#endif
};

#define FUNCTION_TABLE_ENTRIES (sizeof (function_table_init) / sizeof (struct function_table_entry))



/* These must come after the definition of function_table.  */

static char *
expand_builtin_function (char *o, int argc, char **argv,
                         const struct function_table_entry *entry_p)
{
  if (argc < (int)entry_p->minimum_args)
    fatal (*expanding_var,
           _("insufficient number of arguments (%d) to function `%s'"),
           argc, entry_p->name);

  /* I suppose technically some function could do something with no
     arguments, but so far none do, so just test it for all functions here
     rather than in each one.  We can change it later if necessary.  */

  if (!argc)
    return o;

  if (!entry_p->func_ptr)
    fatal (*expanding_var,
           _("unimplemented on this platform: function `%s'"), entry_p->name);

  return entry_p->func_ptr (o, argv, entry_p->name);
}

/* Check for a function invocation in *STRINGP.  *STRINGP points at the
   opening ( or { and is not null-terminated.  If a function invocation
   is found, expand it into the buffer at *OP, updating *OP, incrementing
   *STRINGP past the reference and returning nonzero.  If not, return zero.  */

int
handle_function (char **op, const char **stringp)
{
  const struct function_table_entry *entry_p;
  char openparen = (*stringp)[0];
  char closeparen = openparen == '(' ? ')' : '}';
  const char *beg;
  const char *end;
  int count = 0;
  char *abeg = NULL;
  char **argv, **argvp;
  int nargs;

  beg = *stringp + 1;

  entry_p = lookup_function (beg);

  if (!entry_p)
    return 0;

  /* We found a builtin function.  Find the beginning of its arguments (skip
     whitespace after the name).  */

  beg = next_token (beg + entry_p->len);

  /* Find the end of the function invocation, counting nested use of
     whichever kind of parens we use.  Since we're looking, count commas
     to get a rough estimate of how many arguments we might have.  The
     count might be high, but it'll never be low.  */

  for (nargs=1, end=beg; *end != '\0'; ++end)
    if (*end == ',')
      ++nargs;
    else if (*end == openparen)
      ++count;
    else if (*end == closeparen && --count < 0)
      break;

  if (count >= 0)
    fatal (*expanding_var,
       _("unterminated call to function `%s': missing `%c'"),
       entry_p->name, closeparen);

  *stringp = end;

  /* Get some memory to store the arg pointers.  */
  argvp = argv = alloca (sizeof (char *) * (nargs + 2));

  /* Chop the string into arguments, then a nul.  As soon as we hit
     MAXIMUM_ARGS (if it's >0) assume the rest of the string is part of the
     last argument.

     If we're expanding, store pointers to the expansion of each one.  If
     not, make a duplicate of the string and point into that, nul-terminating
     each argument.  */

  if (entry_p->expand_args)
    {
      const char *p;
      for (p=beg, nargs=0; p <= end; ++argvp)
        {
          const char *next;

          ++nargs;

          if (nargs == entry_p->maximum_args
              || (! (next = find_next_argument (openparen, closeparen, p, end))))
            next = end;

          *argvp = expand_argument (p, next);
          p = next + 1;
        }
    }
  else
    {
      int len = end - beg;
      char *p, *aend;

      abeg = xmalloc (len+1);
      memcpy (abeg, beg, len);
      abeg[len] = '\0';
      aend = abeg + len;

      for (p=abeg, nargs=0; p <= aend; ++argvp)
        {
          char *next;

          ++nargs;

          if (nargs == entry_p->maximum_args
              || (! (next = find_next_argument (openparen, closeparen, p, aend))))
            next = aend;

          *argvp = p;
          *next = '\0';
          p = next + 1;
        }
    }
  *argvp = NULL;

  /* Finally!  Run the function...  */
  *op = expand_builtin_function (*op, nargs, argv, entry_p);

  /* Free memory.  */
  if (entry_p->expand_args)
    for (argvp=argv; *argvp != 0; ++argvp)
      free (*argvp);
  if (abeg)
    free (abeg);

  return 1;
}



/* User-defined functions.  Expand the first argument as either a builtin
   function or a make variable, in the context of the rest of the arguments
   assigned to $1, $2, ... $N.  $0 is the name of the function.  */

static char *
func_call (char *o, char **argv, const char *funcname UNUSED)
{
  static int max_args = 0;
  char *fname;
  char *cp;
  char *body;
  int flen;
  int i;
  int saved_args;
  const struct function_table_entry *entry_p;
  struct variable *v;

  /* There is no way to define a variable with a space in the name, so strip
     leading and trailing whitespace as a favor to the user.  */
  fname = argv[0];
  while (*fname != '\0' && isspace ((unsigned char)*fname))
    ++fname;

  cp = fname + strlen (fname) - 1;
  while (cp > fname && isspace ((unsigned char)*cp))
    --cp;
  cp[1] = '\0';

  /* Calling nothing is a no-op */
  if (*fname == '\0')
    return o;

  /* Are we invoking a builtin function?  */

  entry_p = lookup_function (fname);
  if (entry_p)
    {
      /* How many arguments do we have?  */
      for (i=0; argv[i+1]; ++i)
        ;
      return expand_builtin_function (o, i, argv+1, entry_p);
    }

  /* Not a builtin, so the first argument is the name of a variable to be
     expanded and interpreted as a function.  Find it.  */
  flen = strlen (fname);

  v = lookup_variable (fname, flen);

  if (v == 0)
    warn_undefined (fname, flen);

  if (v == 0 || *v->value == '\0')
    return o;

  body = alloca (flen + 4);
  body[0] = '$';
  body[1] = '(';
  memcpy (body + 2, fname, flen);
  body[flen+2] = ')';
  body[flen+3] = '\0';

  /* Set up arguments $(1) .. $(N).  $(0) is the function name.  */

  push_new_variable_scope ();

  for (i=0; *argv; ++i, ++argv)
    {
      char num[11];

      sprintf (num, "%d", i);
      define_variable (num, strlen (num), *argv, o_automatic, 0);
    }

  /* If the number of arguments we have is < max_args, it means we're inside
     a recursive invocation of $(call ...).  Fill in the remaining arguments
     in the new scope with the empty value, to hide them from this
     invocation.  */

  for (; i < max_args; ++i)
    {
      char num[11];

      sprintf (num, "%d", i);
      define_variable (num, strlen (num), "", o_automatic, 0);
    }

  /* Expand the body in the context of the arguments, adding the result to
     the variable buffer.  */

  v->exp_count = EXP_COUNT_MAX;

  saved_args = max_args;
  max_args = i;
  o = variable_expand_string (o, body, flen+3);
  max_args = saved_args;

  v->exp_count = 0;

  pop_variable_scope ();

  return o + strlen (o);
}

void
hash_init_function_table (void)
{
  hash_init (&function_table, FUNCTION_TABLE_ENTRIES * 2,
         function_table_entry_hash_1, function_table_entry_hash_2,
         function_table_entry_hash_cmp);
  hash_load (&function_table, function_table_init,
         FUNCTION_TABLE_ENTRIES, sizeof (struct function_table_entry));
}

https://git.savannah.gnu.org/cgit/make.git/tree/implicit.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/implicit.c used in: Step 525
/* Implicit rule searching for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"
#include "filedef.h"
#include "rule.h"
#include "dep.h"
#include "debug.h"
#include "variable.h"
#include "job.h"      /* struct child, used inside commands.h */
#include "commands.h" /* set_file_variables */

static int pattern_search (struct file *file, int archive,
                           unsigned int depth, unsigned int recursions);


/* For a FILE which has no commands specified, try to figure out some
   from the implicit pattern rules.
   Returns 1 if a suitable implicit rule was found,
   after modifying FILE to contain the appropriate commands and deps,
   or returns 0 if no implicit rule was found.  */

int
try_implicit_rule (struct file *file, unsigned int depth)
{
  DBF (DB_IMPLICIT, _("Looking for an implicit rule for `%s'.\n"));

  /* The order of these searches was previously reversed.  My logic now is
     that since the non-archive search uses more information in the target
     (the archive search omits the archive name), it is more specific and
     should come first.  */

  if (pattern_search (file, 0, depth, 0))
    return 1;

#ifndef NO_ARCHIVES
  /* If this is an archive member reference, use just the
     archive member name to search for implicit rules.  */
  if (ar_name (file->name))
    {
      DBF (DB_IMPLICIT,
           _("Looking for archive-member implicit rule for `%s'.\n"));
      if (pattern_search (file, 1, depth, 0))
        return 1;
    }
#endif

  return 0;
}



/* Scans the BUFFER for the next word with whitespace as a separator.
   Returns the pointer to the beginning of the word. LENGTH hold the
   length of the word.  */

static const char *
get_next_word (const char *buffer, unsigned int *length)
{
  const char *p = buffer, *beg;
  char c;

  /* Skip any leading whitespace.  */
  while (isblank ((unsigned char)*p))
    ++p;

  beg = p;
  c = *(p++);

  if (c == '\0')
    return 0;


  /* We already found the first value of "c", above.  */
  while (1)
    {
      char closeparen;
      int count;

      switch (c)
        {
        case '\0':
        case ' ':
        case '\t':
          goto done_word;

        case '$':
          c = *(p++);
          if (c == '$')
            break;

          /* This is a variable reference, so read it to the matching
             close paren.  */

          if (c == '(')
            closeparen = ')';
          else if (c == '{')
            closeparen = '}';
          else
            /* This is a single-letter variable reference.  */
            break;

          for (count = 0; *p != '\0'; ++p)
            {
              if (*p == c)
                ++count;
              else if (*p == closeparen && --count < 0)
                {
                  ++p;
                  break;
                }
            }
          break;

        case '|':
          goto done;

        default:
          break;
        }

      c = *(p++);
    }
 done_word:
  --p;

 done:
  if (length)
    *length = p - beg;

  return beg;
}

/* This structure stores information about the expanded prerequisites for a
   pattern rule.  NAME is always set to the strcache'd name of the prereq.
   FILE and PATTERN will be set for intermediate files only.  IGNORE_MTIME is
   copied from the prerequisite we expanded.
 */
struct patdeps
  {
    const char *name;
    const char *pattern;
    struct file *file;
    unsigned int ignore_mtime : 1;
  };

/* This structure stores information about pattern rules that we need
   to try.
*/
struct tryrule
  {
    struct rule *rule;

    /* Index of the target in this rule that matched the file. */
    unsigned int matches;

    /* Stem length for this match. */
    unsigned int stemlen;

    /* Definition order of this rule. Used to implement stable sort.*/
    unsigned int order;

    /* Nonzero if the LASTSLASH logic was used in matching this rule. */
    char checked_lastslash;
  };

int
stemlen_compare (const void *v1, const void *v2)
{
  const struct tryrule *r1 = v1;
  const struct tryrule *r2 = v2;
  int r = r1->stemlen - r2->stemlen;
  return r != 0 ? r : (int)(r1->order - r2->order);
}

/* Search the pattern rules for a rule with an existing dependency to make
   FILE.  If a rule is found, the appropriate commands and deps are put in FILE
   and 1 is returned.  If not, 0 is returned.

   If ARCHIVE is nonzero, FILE->name is of the form "LIB(MEMBER)".  A rule for
   "(MEMBER)" will be searched for, and "(MEMBER)" will not be chopped up into
   directory and filename parts.

   If an intermediate file is found by pattern search, the intermediate file
   is set up as a target by the recursive call and is also made a dependency
   of FILE.

   DEPTH is used for debugging messages.  */

static int
pattern_search (struct file *file, int archive,
                unsigned int depth, unsigned int recursions)
{
  /* Filename we are searching for a rule for.  */
  const char *filename = archive ? strchr (file->name, '(') : file->name;

  /* Length of FILENAME.  */
  unsigned int namelen = strlen (filename);

  /* The last slash in FILENAME (or nil if there is none).  */
  const char *lastslash;

  /* This is a file-object used as an argument in
     recursive calls.  It never contains any data
     except during a recursive call.  */
  struct file *int_file = 0;

  /* List of dependencies found recursively.  */
  struct patdeps *deplist
    = xmalloc (max_pattern_deps * sizeof (struct patdeps));
  struct patdeps *pat = deplist;

  /* All the prerequisites actually found for a rule, after expansion.  */
  struct dep *deps;

  /* Names of possible dependencies are constructed in this buffer.  */
  char *depname = alloca (namelen + max_pattern_dep_length);

  /* The start and length of the stem of FILENAME for the current rule.  */
  const char *stem = 0;
  unsigned int stemlen = 0;
  unsigned int fullstemlen = 0;

  /* Buffer in which we store all the rules that are possibly applicable.  */
  struct tryrule *tryrules = xmalloc (num_pattern_rules * max_pattern_targets
                                      * sizeof (struct tryrule));

  /* Number of valid elements in TRYRULES.  */
  unsigned int nrules;

  /* The index in TRYRULES of the rule we found.  */
  unsigned int foundrule;

  /* Nonzero if should consider intermediate files as dependencies.  */
  int intermed_ok;

  /* Nonzero if we have initialized file variables for this target.  */
  int file_vars_initialized = 0;

  /* Nonzero if we have matched a pattern-rule target
     that is not just `%'.  */
  int specific_rule_matched = 0;

  struct dep dep_simple;

  unsigned int ri;  /* uninit checks OK */
  struct rule *rule;

  char *pathdir = NULL;
  unsigned long pathlen;

  PATH_VAR (stem_str); /* @@ Need to get rid of stem, stemlen, etc. */

#ifndef NO_ARCHIVES
  if (archive || ar_name (filename))
    lastslash = 0;
  else
#endif
    {
      /* Set LASTSLASH to point at the last slash in FILENAME
         but not counting any slash at the end.  (foo/bar/ counts as
         bar/ in directory foo/, not empty in directory foo/bar/.)  */
#ifdef VMS
      lastslash = strrchr (filename, ']');
      if (lastslash == 0)
        lastslash = strrchr (filename, ':');
#else
      lastslash = strrchr (filename, '/');
#ifdef HAVE_DOS_PATHS
      /* Handle backslashes (possibly mixed with forward slashes)
         and the case of "d:file".  */
      {
        char *bslash = strrchr (filename, '\\');
        if (lastslash == 0 || bslash > lastslash)
          lastslash = bslash;
        if (lastslash == 0 && filename[0] && filename[1] == ':')
          lastslash = filename + 1;
      }
#endif
#endif
      if (lastslash != 0 && lastslash[1] == '\0')
        lastslash = 0;
    }

  pathlen = lastslash - filename + 1;

  /* First see which pattern rules match this target and may be considered.
     Put them in TRYRULES.  */

  nrules = 0;
  for (rule = pattern_rules; rule != 0; rule = rule->next)
    {
      unsigned int ti;

      /* If the pattern rule has deps but no commands, ignore it.
         Users cancel built-in rules by redefining them without commands.  */
      if (rule->deps != 0 && rule->cmds == 0)
        continue;

      /* If this rule is in use by a parent pattern_search,
         don't use it here.  */
      if (rule->in_use)
        {
          DBS (DB_IMPLICIT, (_("Avoiding implicit rule recursion.\n")));
          continue;
        }

      for (ti = 0; ti < rule->num; ++ti)
        {
          const char *target = rule->targets[ti];
          const char *suffix = rule->suffixes[ti];
          int check_lastslash;

          /* Rules that can match any filename and are not terminal
             are ignored if we're recursing, so that they cannot be
             intermediate files.  */
          if (recursions > 0 && target[1] == '\0' && !rule->terminal)
            continue;

          if (rule->lens[ti] > namelen)
            /* It can't possibly match.  */
            continue;

          /* From the lengths of the filename and the pattern parts,
             find the stem: the part of the filename that matches the %.  */
          stem = filename + (suffix - target - 1);
          stemlen = namelen - rule->lens[ti] + 1;

          /* Set CHECK_LASTSLASH if FILENAME contains a directory
             prefix and the target pattern does not contain a slash.  */

          check_lastslash = 0;
          if (lastslash)
            {
#ifdef VMS
              check_lastslash = (strchr (target, ']') == 0
                                 && strchr (target, ':') == 0);
#else
              check_lastslash = strchr (target, '/') == 0;
#ifdef HAVE_DOS_PATHS
              /* Didn't find it yet: check for DOS-type directories.  */
              if (check_lastslash)
                {
                  char *b = strchr (target, '\\');
                  check_lastslash = !(b || (target[0] && target[1] == ':'));
                }
#endif
#endif
            }
          if (check_lastslash)
            {
              /* If so, don't include the directory prefix in STEM here.  */
              if (pathlen > stemlen)
                continue;
              stemlen -= pathlen;
              stem += pathlen;
            }

          /* Check that the rule pattern matches the text before the stem.  */
          if (check_lastslash)
            {
              if (stem > (lastslash + 1)
                  && !strneq (target, lastslash + 1, stem - lastslash - 1))
                continue;
            }
          else if (stem > filename
                   && !strneq (target, filename, stem - filename))
            continue;

          /* Check that the rule pattern matches the text after the stem.
             We could test simply use streq, but this way we compare the
             first two characters immediately.  This saves time in the very
             common case where the first character matches because it is a
             period.  */
          if (*suffix != stem[stemlen]
              || (*suffix != '\0' && !streq (&suffix[1], &stem[stemlen + 1])))
            continue;

          /* Record if we match a rule that not all filenames will match.  */
          if (target[1] != '\0')
            specific_rule_matched = 1;

          /* A rule with no dependencies and no commands exists solely to set
             specific_rule_matched when it matches.  Don't try to use it.  */
          if (rule->deps == 0 && rule->cmds == 0)
            continue;

          /* Record this rule in TRYRULES and the index of the matching
             target in MATCHES.  If several targets of the same rule match,
             that rule will be in TRYRULES more than once.  */
          tryrules[nrules].rule = rule;
      tryrules[nrules].matches = ti;
          tryrules[nrules].stemlen = stemlen + (check_lastslash ? pathlen : 0);
          tryrules[nrules].order = nrules;
      tryrules[nrules].checked_lastslash = check_lastslash;
          ++nrules;
        }
    }

  /* Bail out early if we haven't found any rules. */
  if (nrules == 0)
    goto done;

  /* Sort the rules to place matches with the shortest stem first. This
     way the most specific rules will be tried first. */
  if (nrules > 1)
    qsort (tryrules, nrules, sizeof (struct tryrule), stemlen_compare);

  /* If we have found a matching rule that won't match all filenames,
     retroactively reject any non-"terminal" rules that do always match.  */
  if (specific_rule_matched)
    for (ri = 0; ri < nrules; ++ri)
      if (!tryrules[ri].rule->terminal)
        {
          unsigned int j;
          for (j = 0; j < tryrules[ri].rule->num; ++j)
            if (tryrules[ri].rule->targets[j][1] == '\0')
              {
                tryrules[ri].rule = 0;
                break;
              }
        }

  /* Try each rule once without intermediate files, then once with them.  */
  for (intermed_ok = 0; intermed_ok < 2; ++intermed_ok)
    {
      pat = deplist;

      /* Try each pattern rule till we find one that applies.  If it does,
         expand its dependencies (as substituted) and chain them in DEPS.  */
      for (ri = 0; ri < nrules; ri++)
        {
          struct dep *dep;
          int check_lastslash;
          unsigned int failed = 0;
          int file_variables_set = 0;
          unsigned int deps_found = 0;
          /* NPTR points to the part of the prereq we haven't processed.  */
          const char *nptr = 0;
          const char *dir = NULL;
          int order_only = 0;
          unsigned int matches;

          rule = tryrules[ri].rule;

          /* RULE is nil when we discover that a rule, already placed in
             TRYRULES, should not be applied.  */
          if (rule == 0)
            continue;

          /* Reject any terminal rules if we're looking to make intermediate
             files.  */
          if (intermed_ok && rule->terminal)
            continue;

          /* From the lengths of the filename and the matching pattern parts,
             find the stem: the part of the filename that matches the %.  */
          matches = tryrules[ri].matches;
          stem = filename + (rule->suffixes[matches]
                             - rule->targets[matches]) - 1;
          stemlen = (namelen - rule->lens[matches]) + 1;
          check_lastslash = tryrules[ri].checked_lastslash;
          if (check_lastslash)
            {
              stem += pathlen;
              stemlen -= pathlen;

              /* We need to add the directory prefix, so set it up.  */
              if (! pathdir)
                {
                  pathdir = alloca (pathlen + 1);
                  memcpy (pathdir, filename, pathlen);
                  pathdir[pathlen] = '\0';
                }
              dir = pathdir;
            }

          DBS (DB_IMPLICIT, (_("Trying pattern rule with stem `%.*s'.\n"),
                             (int) stemlen, stem));

          strncpy (stem_str, stem, stemlen);
          stem_str[stemlen] = '\0';

          /* If there are no prerequisites, then this rule matches.  */
          if (rule->deps == 0)
            break;

          /* Temporary assign STEM to file->stem (needed to set file
             variables below).   */
          file->stem = stem_str;

          /* Mark this rule as in use so a recursive pattern_search won't try
             to use it.  */
          rule->in_use = 1;

          /* Try each prerequisite; see if it exists or can be created.  We'll
             build a list of prereq info in DEPLIST.  Due to 2nd expansion we
             may have to process multiple prereqs for a single dep entry.  */

          pat = deplist;
          dep = rule->deps;
          nptr = dep_name (dep);
          while (1)
            {
              struct dep *dl, *d;
              char *p;

              /* If we're out of name to parse, start the next prereq.  */
              if (! nptr)
                {
                  dep = dep->next;
                  if (dep == 0)
                    break;
                  nptr = dep_name (dep);
                }

              /* If we don't need a second expansion, just replace the %.  */
              if (! dep->need_2nd_expansion)
                {
                  dep_simple = *dep;
                  dep_simple.next = 0;
                  p = strchr (nptr, '%');
                  if (p == 0)
                    dep_simple.name = nptr;
                  else
                    {
                      char *o = depname;
                      if (check_lastslash)
                        {
                          memcpy (o, filename, pathlen);
                          o += pathlen;
                        }
                      memcpy (o, nptr, p - nptr);
                      o += p - nptr;
                      memcpy (o, stem_str, stemlen);
                      o += stemlen;
                      strcpy (o, p + 1);
                      dep_simple.name = strcache_add (depname);
                    }
                  dl = &dep_simple;

                  /* We've used up this dep, so next time get a new one.  */
                  nptr = 0;
                  ++deps_found;
                }

              /* We have to perform second expansion on this prereq.  In an
                 ideal world we would take the dependency line, substitute the
                 stem, re-expand the whole line and chop it into individual
                 prerequisites.  Unfortunately this won't work because of the
                 "check_lastslash" twist.  Instead, we will have to go word by
                 word, taking $()'s into account.  For each word we will
                 substitute the stem, re-expand, chop it up, and, if
                 check_lastslash != 0, add the directory part to each
                 resulting prerequisite.  */
              else
                {
                  int add_dir = 0;
                  unsigned int len;

                  nptr = get_next_word (nptr, &len);
                  if (nptr == 0)
                    continue;

                  /* See this is a transition to order-only prereqs.  */
                  if (! order_only && len == 1 && nptr[0] == '|')
                    {
                      order_only = 1;
                      nptr += len;
                      continue;
                    }

                  /* If the dependency name has %, substitute the stem.  If we
                     just replace % with the stem value then later, when we do
                     the 2nd expansion, we will re-expand this stem value
                     again.  This is not good if you have certain characters
                     in your stem (like $).

                     Instead, we will replace % with $* and allow the second
                     expansion to take care of it for us.  This way (since $*
                     is a simple variable) there won't be additional
                     re-expansion of the stem.  */

                  p = lindex (nptr, nptr + len, '%');
                  if (p == 0)
                    {
                      memcpy (depname, nptr, len);
                      depname[len] = '\0';
                    }
                  else
                    {
                      unsigned int i = p - nptr;
                      memcpy (depname, nptr, i);
                      memcpy (depname + i, "$*", 2);
                      memcpy (depname + i + 2, p + 1, len - i - 1);
                      depname[len + 2 - 1] = '\0';

                      if (check_lastslash)
                        add_dir = 1;
                    }

                  /* Initialize and set file variables if we haven't already
                     done so. */
                  if (!file_vars_initialized)
                    {
                      initialize_file_variables (file, 0);
                      set_file_variables (file);
                      file_vars_initialized = 1;
                    }
                  /* Update the stem value in $* for this rule.  */
                  else if (!file_variables_set)
                    {
                      define_variable_for_file (
                        "*", 1, file->stem, o_automatic, 0, file);
                      file_variables_set = 1;
                    }

                  /* Perform the 2nd expansion.  */
                  p = variable_expand_for_file (depname, file);

                  /* Parse the expanded string. */
                  dl = PARSE_FILE_SEQ (&p, struct dep, order_only ? '\0' : '|',
                                       add_dir ? dir : NULL, 0);

                  for (d = dl; d != NULL; d = d->next)
                    {
                      ++deps_found;
                      if (order_only)
                        d->ignore_mtime = 1;
                    }

                  /* Set up for the next word.  */
                  nptr += len;
                }

              /* If there are more than max_pattern_deps prerequisites (due to
                 2nd expansion), reset it and realloc the arrays.  */

              if (deps_found > max_pattern_deps)
                {
                  unsigned int l = pat - deplist;
                  deplist = xrealloc (deplist,
                                      deps_found * sizeof (struct patdeps));
                  pat = deplist + l;
                  max_pattern_deps = deps_found;
                }

              /* Go through the nameseq and handle each as a prereq name.  */
              for (d = dl; d != 0; d = d->next)
                {
                  struct dep *expl_d;
                  int is_rule = d->name == dep_name (dep);

                  if (file_impossible_p (d->name))
                    {
                      /* If this prereq has already been ruled "impossible",
                         then the rule fails.  Don't bother trying it on the
                         second pass either since we know that will fail.  */
                      DBS (DB_IMPLICIT,
                           (is_rule
                            ? _("Rejecting impossible rule prerequisite `%s'.\n")
                            : _("Rejecting impossible implicit prerequisite `%s'.\n"),
                            d->name));
                      tryrules[ri].rule = 0;

                      failed = 1;
                      break;
                    }

                  memset (pat, '\0', sizeof (struct patdeps));
                  pat->ignore_mtime = d->ignore_mtime;

                  DBS (DB_IMPLICIT,
                       (is_rule
                        ? _("Trying rule prerequisite `%s'.\n")
                        : _("Trying implicit prerequisite `%s'.\n"), d->name));

                  /* If this prereq is also explicitly mentioned for FILE,
                     skip all tests below since it must be built no matter
                     which implicit rule we choose. */

                  for (expl_d = file->deps; expl_d != 0; expl_d = expl_d->next)
                    if (streq (dep_name (expl_d), d->name))
                      break;
                  if (expl_d != 0)
                    {
                      (pat++)->name = d->name;
                      continue;
                    }

                  /* The DEP->changed flag says that this dependency resides
                     in a nonexistent directory.  So we normally can skip
                     looking for the file.  However, if CHECK_LASTSLASH is
                     set, then the dependency file we are actually looking for
                     is in a different directory (the one gotten by prepending
                     FILENAME's directory), so it might actually exist.  */

                  /* @@ dep->changed check is disabled. */
                  if (lookup_file (d->name) != 0
                      /*|| ((!dep->changed || check_lastslash) && */
                      || file_exists_p (d->name))
                    {
                      (pat++)->name = d->name;
                      continue;
                    }

                  /* This code, given FILENAME = "lib/foo.o", dependency name
                     "lib/foo.c", and VPATH=src, searches for
                     "src/lib/foo.c".  */
                  {
                    const char *vname = vpath_search (d->name, 0, NULL, NULL);
                    if (vname)
                      {
                        DBS (DB_IMPLICIT,
                             (_("Found prerequisite `%s' as VPATH `%s'\n"),
                              d->name, vname));
                        (pat++)->name = d->name;
                        continue;
                      }
                  }

                  /* We could not find the file in any place we should look.
                     Try to make this dependency as an intermediate file, but
                     only on the second pass.  */

                  if (intermed_ok)
                    {
                      DBS (DB_IMPLICIT,
                           (_("Looking for a rule with intermediate file `%s'.\n"),
                            d->name));

                      if (int_file == 0)
                        int_file = alloca (sizeof (struct file));
                      memset (int_file, '\0', sizeof (struct file));
                      int_file->name = d->name;

                      if (pattern_search (int_file,
                                          0,
                                          depth + 1,
                                          recursions + 1))
                        {
                          pat->pattern = int_file->name;
                          int_file->name = d->name;
                          pat->file = int_file;
                          (pat++)->name = d->name;
                          int_file = 0;
                          continue;
                        }

                      /* If we have tried to find P as an intermediate file
                         and failed, mark that name as impossible so we won't
                         go through the search again later.  */
                      if (int_file->variables)
                        free_variable_set (int_file->variables);
                      if (int_file->pat_variables)
                        free_variable_set (int_file->pat_variables);
                      file_impossible (d->name);
                    }

                  /* A dependency of this rule does not exist. Therefore, this
                     rule fails.  */
                  failed = 1;
                  break;
                }

              /* Free the ns chain.  */
              if (dl != &dep_simple)
                free_dep_chain (dl);

              if (failed)
                break;
            }

          /* Reset the stem in FILE. */

          file->stem = 0;

          /* This rule is no longer `in use' for recursive searches.  */
          rule->in_use = 0;

          if (! failed)
            /* This pattern rule does apply.  Stop looking for one.  */
            break;

          /* This pattern rule does not apply. If some of its dependencies
             succeeded, free the data structure describing them.  */
          /* free_idep_chain (deps); */
          deps = 0;
        }

      /* If we found an applicable rule without intermediate files, don't try
         with them.  */
      if (ri < nrules)
        break;

      rule = 0;
    }

  /* RULE is nil if the loop went through the list but everything failed.  */
  if (rule == 0)
    goto done;

  foundrule = ri;

  /* If we are recursing, store the pattern that matched FILENAME in
     FILE->name for use in upper levels.  */

  if (recursions > 0)
    /* Kludge-o-matic */
    file->name = rule->targets[tryrules[foundrule].matches];

  /* DEPLIST lists the prerequisites for the rule we found.  This includes the
     intermediate files, if any.  Convert them into entries on the deps-chain
     of FILE.  */

  while (pat-- > deplist)
    {
      struct dep *dep;
      const char *s;

      if (pat->file != 0)
        {
          /* If we need to use an intermediate file, make sure it is entered
             as a target, with the info that was found for it in the recursive
             pattern_search call.  We know that the intermediate file did not
             already exist as a target; therefore we can assume that the deps
             and cmds of F below are null before we change them.  */

          struct file *imf = pat->file;
          struct file *f = lookup_file (imf->name);

          /* We don't want to delete an intermediate file that happened
             to be a prerequisite of some (other) target. Mark it as
             precious.  */
          if (f != 0)
            f->precious = 1;
          else
            f = enter_file (imf->name);

          f->deps = imf->deps;
          f->cmds = imf->cmds;
          f->stem = imf->stem;
          f->variables = imf->variables;
          f->pat_variables = imf->pat_variables;
          f->pat_searched = imf->pat_searched;
          f->also_make = imf->also_make;
          f->is_target = 1;
          f->intermediate = 1;
          f->tried_implicit = 1;

          imf = lookup_file (pat->pattern);
          if (imf != 0 && imf->precious)
            f->precious = 1;

          for (dep = f->deps; dep != 0; dep = dep->next)
            {
              dep->file = enter_file (dep->name);
              dep->name = 0;
              dep->file->tried_implicit |= dep->changed;
            }
        }

      dep = alloc_dep ();
      dep->ignore_mtime = pat->ignore_mtime;
      s = strcache_add (pat->name);
      if (recursions)
        dep->name = s;
      else
        {
          dep->file = lookup_file (s);
          if (dep->file == 0)
            dep->file = enter_file (s);
        }

      if (pat->file == 0 && tryrules[foundrule].rule->terminal)
        {
          /* If the file actually existed (was not an intermediate file), and
             the rule that found it was a terminal one, then we want to mark
             the found file so that it will not have implicit rule search done
             for it.  If we are not entering a `struct file' for it now, we
             indicate this with the `changed' flag.  */
          if (dep->file == 0)
            dep->changed = 1;
          else
            dep->file->tried_implicit = 1;
        }

      dep->next = file->deps;
      file->deps = dep;
    }

  if (!tryrules[foundrule].checked_lastslash)
    {
      /* Always allocate new storage, since STEM might be on the stack for an
         intermediate file.  */
      file->stem = strcache_add_len (stem, stemlen);
      fullstemlen = stemlen;
    }
  else
    {
      int dirlen = (lastslash + 1) - filename;
      char *sp;

      /* We want to prepend the directory from
         the original FILENAME onto the stem.  */
      fullstemlen = dirlen + stemlen;
      sp = alloca (fullstemlen + 1);
      memcpy (sp, filename, dirlen);
      memcpy (sp + dirlen, stem, stemlen);
      sp[fullstemlen] = '\0';
      file->stem = strcache_add (sp);
    }

  file->cmds = rule->cmds;
  file->is_target = 1;

  /* Set precious flag. */
  {
    struct file *f = lookup_file (rule->targets[tryrules[foundrule].matches]);
    if (f && f->precious)
      file->precious = 1;
  }

  /* If this rule builds other targets, too, put the others into FILE's
     `also_make' member.  */

  if (rule->num > 1)
    for (ri = 0; ri < rule->num; ++ri)
      if (ri != tryrules[foundrule].matches)
        {
          char *nm = alloca (rule->lens[ri] + fullstemlen + 1);
          char *p = nm;
          struct file *f;
          struct dep *new = alloc_dep ();

          /* GKM FIMXE: handle '|' here too */
          memcpy (p, rule->targets[ri],
                  rule->suffixes[ri] - rule->targets[ri] - 1);
          p += rule->suffixes[ri] - rule->targets[ri] - 1;
          memcpy (p, file->stem, fullstemlen);
          p += fullstemlen;
          memcpy (p, rule->suffixes[ri],
                  rule->lens[ri] - (rule->suffixes[ri] - rule->targets[ri])+1);
          new->name = strcache_add (nm);
          new->file = enter_file (new->name);
          new->next = file->also_make;

          /* Set precious flag. */
          f = lookup_file (rule->targets[ri]);
          if (f && f->precious)
            new->file->precious = 1;

          /* Set the is_target flag so that this file is not treated as
             intermediate by the pattern rule search algorithm and
             file_exists_p cannot pick it up yet.  */
          new->file->is_target = 1;

          file->also_make = new;
        }

 done:
  free (tryrules);
  free (deplist);

  return rule != 0;
}

https://git.savannah.gnu.org/cgit/make.git/tree/job.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/job.c used in: Step 526
/* Job execution and handling for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"

#include <assert.h>

#include "job.h"
#include "debug.h"
#include "filedef.h"
#include "commands.h"
#include "variable.h"
#include "debug.h"

#include <string.h>

/* Default shell to use.  */
#ifdef WINDOWS32
#include <windows.h>

char *default_shell = "sh.exe";
int no_default_sh_exe = 1;
int batch_mode_shell = 1;
HANDLE main_thread;

#elif defined (_AMIGA)

char default_shell[] = "";
extern int MyExecute (char **);
int batch_mode_shell = 0;

#elif defined (__MSDOS__)

/* The default shell is a pointer so we can change it if Makefile
   says so.  It is without an explicit path so we get a chance
   to search the $PATH for it (since MSDOS doesn't have standard
   directories we could trust).  */
char *default_shell = "command.com";
int batch_mode_shell = 0;

#elif defined (__EMX__)

char *default_shell = "/bin/sh";
int batch_mode_shell = 0;

#elif defined (VMS)

# include <descrip.h>
char default_shell[] = "";
int batch_mode_shell = 0;

#elif defined (__riscos__)

char default_shell[] = "";
int batch_mode_shell = 0;

#else

char default_shell[] = "/bin/sh";
int batch_mode_shell = 0;

#endif

#ifdef __MSDOS__
# include <process.h>
static int execute_by_shell;
static int dos_pid = 123;
int dos_status;
int dos_command_running;
#endif /* __MSDOS__ */

#ifdef _AMIGA
# include <proto/dos.h>
static int amiga_pid = 123;
static int amiga_status;
static char amiga_bname[32];
static int amiga_batch_file;
#endif /* Amiga.  */

#ifdef VMS
# ifndef __GNUC__
#   include <processes.h>
# endif
# include <starlet.h>
# include <lib$routines.h>
static void vmsWaitForChildren (int *);
#endif

#ifdef WINDOWS32
# include <windows.h>
# include <io.h>
# include <process.h>
# include "sub_proc.h"
# include "w32err.h"
# include "pathstuff.h"
#endif /* WINDOWS32 */

#ifdef __EMX__
# include <process.h>
#endif

#if defined (HAVE_SYS_WAIT_H) || defined (HAVE_UNION_WAIT)
# include <sys/wait.h>
#endif

#ifdef HAVE_WAITPID
# define WAIT_NOHANG(status)    waitpid (-1, (status), WNOHANG)
#else   /* Don't have waitpid.  */
# ifdef HAVE_WAIT3
#  ifndef wait3
extern int wait3 ();
#  endif
#  define WAIT_NOHANG(status)   wait3 ((status), WNOHANG, (struct rusage *) 0)
# endif /* Have wait3.  */
#endif /* Have waitpid.  */

#if !defined (wait) && !defined (POSIX)
int wait ();
#endif

#ifndef HAVE_UNION_WAIT

# define WAIT_T int

# ifndef WTERMSIG
#  define WTERMSIG(x) ((x) & 0x7f)
# endif
# ifndef WCOREDUMP
#  define WCOREDUMP(x) ((x) & 0x80)
# endif
# ifndef WEXITSTATUS
#  define WEXITSTATUS(x) (((x) >> 8) & 0xff)
# endif
# ifndef WIFSIGNALED
#  define WIFSIGNALED(x) (WTERMSIG (x) != 0)
# endif
# ifndef WIFEXITED
#  define WIFEXITED(x) (WTERMSIG (x) == 0)
# endif

#else   /* Have `union wait'.  */

# define WAIT_T union wait
# ifndef WTERMSIG
#  define WTERMSIG(x) ((x).w_termsig)
# endif
# ifndef WCOREDUMP
#  define WCOREDUMP(x) ((x).w_coredump)
# endif
# ifndef WEXITSTATUS
#  define WEXITSTATUS(x) ((x).w_retcode)
# endif
# ifndef WIFSIGNALED
#  define WIFSIGNALED(x) (WTERMSIG(x) != 0)
# endif
# ifndef WIFEXITED
#  define WIFEXITED(x) (WTERMSIG(x) == 0)
# endif

#endif  /* Don't have `union wait'.  */

#if !defined(HAVE_UNISTD_H) && !defined(WINDOWS32)
int dup2 ();
int execve ();
void _exit ();
# ifndef VMS
int geteuid ();
int getegid ();
int setgid ();
int getgid ();
# endif
#endif

/* Different systems have different requirements for pid_t.
   Plus we have to support gettext string translation... Argh.  */
static const char *
pid2str (pid_t pid)
{
  static char pidstring[100];
#if defined(WINDOWS32) && (__GNUC__ > 3 || _MSC_VER > 1300)
  /* %Id is only needed for 64-builds, which were not supported by
      older versions of Windows compilers.  */
  sprintf (pidstring, "%Id", pid);
#else
  sprintf (pidstring, "%lu", (unsigned long) pid);
#endif
  return pidstring;
}

int getloadavg (double loadavg[], int nelem);
int start_remote_job (char **argv, char **envp, int stdin_fd, int *is_remote,
                      int *id_ptr, int *used_stdin);
int start_remote_job_p (int);
int remote_status (int *exit_code_ptr, int *signal_ptr, int *coredump_ptr,
                   int block);

RETSIGTYPE child_handler (int);
static void free_child (struct child *);
static void start_job_command (struct child *child);
static int load_too_high (void);
static int job_next_command (struct child *);
static int start_waiting_job (struct child *);


/* Chain of all live (or recently deceased) children.  */

struct child *children = 0;

/* Number of children currently running.  */

unsigned int job_slots_used = 0;

/* Nonzero if the `good' standard input is in use.  */

static int good_stdin_used = 0;

/* Chain of children waiting to run until the load average goes down.  */

static struct child *waiting_jobs = 0;

/* Non-zero if we use a *real* shell (always so on Unix).  */

int unixy_shell = 1;

/* Number of jobs started in the current second.  */

unsigned long job_counter = 0;

/* Number of jobserver tokens this instance is currently using.  */

unsigned int jobserver_tokens = 0;


#ifdef WINDOWS32
/*
 * The macro which references this function is defined in make.h.
 */
int
w32_kill(pid_t pid, int sig)
{
  return ((process_kill((HANDLE)pid, sig) == TRUE) ? 0 : -1);
}

/* This function creates a temporary file name with an extension specified
 * by the unixy arg.
 * Return an xmalloc'ed string of a newly created temp file and its
 * file descriptor, or die.  */
static char *
create_batch_file (char const *base, int unixy, int *fd)
{
  const char *const ext = unixy ? "sh" : "bat";
  const char *error_string = NULL;
  char temp_path[MAXPATHLEN]; /* need to know its length */
  unsigned path_size = GetTempPath(sizeof temp_path, temp_path);
  int path_is_dot = 0;
  unsigned uniq = 1;
  const unsigned sizemax = strlen (base) + strlen (ext) + 10;

  if (path_size == 0)
    {
      path_size = GetCurrentDirectory (sizeof temp_path, temp_path);
      path_is_dot = 1;
    }

  while (path_size > 0 &&
         path_size + sizemax < sizeof temp_path &&
         uniq < 0x10000)
    {
      unsigned size = sprintf (temp_path + path_size,
                               "%s%s-%x.%s",
                               temp_path[path_size - 1] == '\\' ? "" : "\\",
                               base, uniq, ext);
      HANDLE h = CreateFile (temp_path,  /* file name */
                             GENERIC_READ | GENERIC_WRITE, /* desired access */
                             0,                            /* no share mode */
                             NULL,                         /* default security attributes */
                             CREATE_NEW,                   /* creation disposition */
                             FILE_ATTRIBUTE_NORMAL |       /* flags and attributes */
                             FILE_ATTRIBUTE_TEMPORARY,     /* we'll delete it */
                             NULL);                        /* no template file */

      if (h == INVALID_HANDLE_VALUE)
        {
          const DWORD er = GetLastError();

          if (er == ERROR_FILE_EXISTS || er == ERROR_ALREADY_EXISTS)
            ++uniq;

          /* the temporary path is not guaranteed to exist */
          else if (path_is_dot == 0)
            {
              path_size = GetCurrentDirectory (sizeof temp_path, temp_path);
              path_is_dot = 1;
            }

          else
            {
              error_string = map_windows32_error_to_string (er);
              break;
            }
        }
      else
        {
          const unsigned final_size = path_size + size + 1;
          char *const path = xmalloc (final_size);
          memcpy (path, temp_path, final_size);
          *fd = _open_osfhandle ((intptr_t)h, 0);
          if (unixy)
            {
              char *p;
              int ch;
              for (p = path; (ch = *p) != 0; ++p)
                if (ch == '\\')
                  *p = '/';
            }
          return path; /* good return */
        }
    }

  *fd = -1;
  if (error_string == NULL)
    error_string = _("Cannot create a temporary file\n");
  fatal (NILF, error_string);

  /* not reached */
  return NULL;
}
#endif /* WINDOWS32 */

#ifdef __EMX__
/* returns whether path is assumed to be a unix like shell. */
int
_is_unixy_shell (const char *path)
{
  /* list of non unix shells */
  const char *known_os2shells[] = {
    "cmd.exe",
    "cmd",
    "4os2.exe",
    "4os2",
    "4dos.exe",
    "4dos",
    "command.com",
    "command",
    NULL
  };

  /* find the rightmost '/' or '\\' */
  const char *name = strrchr (path, '/');
  const char *p = strrchr (path, '\\');
  unsigned i;

  if (name && p)    /* take the max */
    name = (name > p) ? name : p;
  else if (p)       /* name must be 0 */
    name = p;
  else if (!name)   /* name and p must be 0 */
    name = path;

  if (*name == '/' || *name == '\\') name++;

  i = 0;
  while (known_os2shells[i] != NULL) {
    if (strcasecmp (name, known_os2shells[i]) == 0)
      return 0; /* not a unix shell */
    i++;
  }

  /* in doubt assume a unix like shell */
  return 1;
}
#endif /* __EMX__ */

/* determines whether path looks to be a Bourne-like shell. */
int
is_bourne_compatible_shell (const char *path)
{
  /* list of known unix (Bourne-like) shells */
  const char *unix_shells[] = {
    "sh",
    "bash",
    "ksh",
    "rksh",
    "zsh",
    "ash",
    "dash",
    NULL
  };
  unsigned i, len;

  /* find the rightmost '/' or '\\' */
  const char *name = strrchr (path, '/');
  char *p = strrchr (path, '\\');

  if (name && p)    /* take the max */
    name = (name > p) ? name : p;
  else if (p)       /* name must be 0 */
    name = p;
  else if (!name)   /* name and p must be 0 */
    name = path;

  if (*name == '/' || *name == '\\') name++;

  /* this should be able to deal with extensions on Windows-like systems */
  for (i = 0; unix_shells[i] != NULL; i++) {
    len = strlen(unix_shells[i]);
#if defined(WINDOWS32) || defined(__MSDOS__)
    if ((strncasecmp (name, unix_shells[i], len) == 0) &&
      (strlen(name) >= len && (name[len] == '\0' || name[len] == '.')))
#else
    if ((strncmp (name, unix_shells[i], len) == 0) &&
      (strlen(name) >= len && name[len] == '\0'))
#endif
    return 1; /* a known unix-style shell */
  }

  /* if not on the list, assume it's not a Bourne-like shell */
  return 0;
}



/* Write an error message describing the exit status given in
   EXIT_CODE, EXIT_SIG, and COREDUMP, for the target TARGET_NAME.
   Append "(ignored)" if IGNORED is nonzero.  */

static void
child_error (const char *target_name,
             int exit_code, int exit_sig, int coredump, int ignored)
{
  if (ignored && silent_flag)
    return;

#ifdef VMS
  if (!(exit_code & 1))
      error (NILF,
             (ignored ? _("*** [%s] Error 0x%x (ignored)")
              : _("*** [%s] Error 0x%x")),
             target_name, exit_code);
#else
  if (exit_sig == 0)
    error (NILF, ignored ? _("[%s] Error %d (ignored)") :
       _("*** [%s] Error %d"),
       target_name, exit_code);
  else
    error (NILF, "*** [%s] %s%s",
       target_name, strsignal (exit_sig),
       coredump ? _(" (core dumped)") : "");
#endif /* VMS */
}



/* Handle a dead child.  This handler may or may not ever be installed.

   If we're using the jobserver feature, we need it.  First, installing it
   ensures the read will interrupt on SIGCHLD.  Second, we close the dup'd
   read FD to ensure we don't enter another blocking read without reaping all
   the dead children.  In this case we don't need the dead_children count.

   If we don't have either waitpid or wait3, then make is unreliable, but we
   use the dead_children count to reap children as best we can.  */

static unsigned int dead_children = 0;

RETSIGTYPE
child_handler (int sig UNUSED)
{
  ++dead_children;

  if (job_rfd >= 0)
    {
      close (job_rfd);
      job_rfd = -1;
    }

#ifdef __EMX__
  /* The signal handler must called only once! */
  signal (SIGCHLD, SIG_DFL);
#endif

  /* This causes problems if the SIGCHLD interrupts a printf().
  DB (DB_JOBS, (_("Got a SIGCHLD; %u unreaped children.\n"), dead_children));
  */
}

extern int shell_function_pid, shell_function_completed;

/* Reap all dead children, storing the returned status and the new command
   state (`cs_finished') in the `file' member of the `struct child' for the
   dead child, and removing the child from the chain.  In addition, if BLOCK
   nonzero, we block in this function until we've reaped at least one
   complete child, waiting for it to die if necessary.  If ERR is nonzero,
   print an error message first.  */

void
reap_children (int block, int err)
{
#ifndef WINDOWS32
  WAIT_T status;
  /* Initially, assume we have some.  */
  int reap_more = 1;
#endif

#ifdef WAIT_NOHANG
# define REAP_MORE reap_more
#else
# define REAP_MORE dead_children
#endif

  /* As long as:

       We have at least one child outstanding OR a shell function in progress,
         AND
       We're blocking for a complete child OR there are more children to reap

     we'll keep reaping children.  */

  while ((children != 0 || shell_function_pid != 0)
         && (block || REAP_MORE))
    {
      int remote = 0;
      pid_t pid;
      int exit_code, exit_sig, coredump;
      register struct child *lastc, *c;
      int child_failed;
      int any_remote, any_local;
      int dontcare;

      if (err && block)
    {
          static int printed = 0;

      /* We might block for a while, so let the user know why.
             Only print this message once no matter how many jobs are left.  */
      fflush (stdout);
          if (!printed)
            error (NILF, _("*** Waiting for unfinished jobs...."));
          printed = 1;
    }

      /* We have one less dead child to reap.  As noted in
     child_handler() above, this count is completely unimportant for
     all modern, POSIX-y systems that support wait3() or waitpid().
     The rest of this comment below applies only to early, broken
     pre-POSIX systems.  We keep the count only because... it's there...

     The test and decrement are not atomic; if it is compiled into:
        register = dead_children - 1;
        dead_children = register;
     a SIGCHLD could come between the two instructions.
     child_handler increments dead_children.
     The second instruction here would lose that increment.  But the
     only effect of dead_children being wrong is that we might wait
     longer than necessary to reap a child, and lose some parallelism;
     and we might print the "Waiting for unfinished jobs" message above
     when not necessary.  */

      if (dead_children > 0)
    --dead_children;

      any_remote = 0;
      any_local = shell_function_pid != 0;
      for (c = children; c != 0; c = c->next)
    {
      any_remote |= c->remote;
      any_local |= ! c->remote;
      DB (DB_JOBS, (_("Live child %p (%s) PID %s %s\n"),
                        c, c->file->name, pid2str (c->pid),
                        c->remote ? _(" (remote)") : ""));
#ifdef VMS
      break;
#endif
    }

      /* First, check for remote children.  */
      if (any_remote)
    pid = remote_status (&exit_code, &exit_sig, &coredump, 0);
      else
    pid = 0;

      if (pid > 0)
    /* We got a remote child.  */
    remote = 1;
      else if (pid < 0)
    {
          /* A remote status command failed miserably.  Punt.  */
    remote_status_lose:
      pfatal_with_name ("remote_status");
    }
      else
    {
      /* No remote children.  Check for local children.  */
#if !defined(__MSDOS__) && !defined(_AMIGA) && !defined(WINDOWS32)
      if (any_local)
        {
#ifdef VMS
          vmsWaitForChildren (&status);
          pid = c->pid;
#else
#ifdef WAIT_NOHANG
          if (!block)
        pid = WAIT_NOHANG (&status);
          else
#endif
        EINTRLOOP(pid, wait (&status));
#endif /* !VMS */
        }
      else
        pid = 0;

      if (pid < 0)
        {
              /* The wait*() failed miserably.  Punt.  */
          pfatal_with_name ("wait");
        }
      else if (pid > 0)
        {
          /* We got a child exit; chop the status word up.  */
          exit_code = WEXITSTATUS (status);
          exit_sig = WIFSIGNALED (status) ? WTERMSIG (status) : 0;
          coredump = WCOREDUMP (status);

              /* If we have started jobs in this second, remove one.  */
              if (job_counter)
                --job_counter;
        }
      else
        {
          /* No local children are dead.  */
              reap_more = 0;

          if (!block || !any_remote)
                break;

              /* Now try a blocking wait for a remote child.  */
              pid = remote_status (&exit_code, &exit_sig, &coredump, 1);
              if (pid < 0)
                goto remote_status_lose;
              else if (pid == 0)
                /* No remote children either.  Finally give up.  */
                break;

              /* We got a remote child.  */
              remote = 1;
        }
#endif /* !__MSDOS__, !Amiga, !WINDOWS32.  */

#ifdef __MSDOS__
      /* Life is very different on MSDOS.  */
      pid = dos_pid - 1;
      status = dos_status;
      exit_code = WEXITSTATUS (status);
      if (exit_code == 0xff)
        exit_code = -1;
      exit_sig = WIFSIGNALED (status) ? WTERMSIG (status) : 0;
      coredump = 0;
#endif /* __MSDOS__ */
#ifdef _AMIGA
      /* Same on Amiga */
      pid = amiga_pid - 1;
      status = amiga_status;
      exit_code = amiga_status;
      exit_sig = 0;
      coredump = 0;
#endif /* _AMIGA */
#ifdef WINDOWS32
          {
            HANDLE hPID;
            int werr;
            HANDLE hcTID, hcPID;
            exit_code = 0;
            exit_sig = 0;
            coredump = 0;

            /* Record the thread ID of the main process, so that we
               could suspend it in the signal handler.  */
            if (!main_thread)
              {
                hcTID = GetCurrentThread ();
                hcPID = GetCurrentProcess ();
                if (!DuplicateHandle (hcPID, hcTID, hcPID, &main_thread, 0,
                                      FALSE, DUPLICATE_SAME_ACCESS))
                  {
                    DWORD e = GetLastError ();
                    fprintf (stderr,
                             "Determine main thread ID (Error %ld: %s)\n",
                             e, map_windows32_error_to_string(e));
                  }
                else
                  DB (DB_VERBOSE, ("Main thread handle = %p\n", main_thread));
              }

            /* wait for anything to finish */
            hPID = process_wait_for_any();
            if (hPID)
              {

                /* was an error found on this process? */
                werr = process_last_err(hPID);

                /* get exit data */
                exit_code = process_exit_code(hPID);

                if (werr)
                  fprintf(stderr, "make (e=%d): %s",
                          exit_code, map_windows32_error_to_string(exit_code));

                /* signal */
                exit_sig = process_signal(hPID);

                /* cleanup process */
                process_cleanup(hPID);

                coredump = 0;
              }
            pid = (pid_t) hPID;
          }
#endif /* WINDOWS32 */
    }

      /* Check if this is the child of the `shell' function.  */
      if (!remote && pid == shell_function_pid)
    {
      /* It is.  Leave an indicator for the `shell' function.  */
      if (exit_sig == 0 && exit_code == 127)
        shell_function_completed = -1;
      else
        shell_function_completed = 1;
      break;
    }

      child_failed = exit_sig != 0 || exit_code != 0;

      /* Search for a child matching the deceased one.  */
      lastc = 0;
      for (c = children; c != 0; lastc = c, c = c->next)
    if (c->remote == remote && c->pid == pid)
      break;

      if (c == 0)
        /* An unknown child died.
           Ignore it; it was inherited from our invoker.  */
        continue;

      DB (DB_JOBS, (child_failed
                    ? _("Reaping losing child %p PID %s %s\n")
                    : _("Reaping winning child %p PID %s %s\n"),
                    c, pid2str (c->pid), c->remote ? _(" (remote)") : ""));

      if (c->sh_batch_file) {
        DB (DB_JOBS, (_("Cleaning up temp batch file %s\n"),
                      c->sh_batch_file));

        /* just try and remove, don't care if this fails */
        remove (c->sh_batch_file);

        /* all done with memory */
        free (c->sh_batch_file);
        c->sh_batch_file = NULL;
      }

      /* If this child had the good stdin, say it is now free.  */
      if (c->good_stdin)
        good_stdin_used = 0;

      dontcare = c->dontcare;

      if (child_failed && !c->noerror && !ignore_errors_flag)
        {
          /* The commands failed.  Write an error message,
             delete non-precious targets, and abort.  */
          static int delete_on_error = -1;

          if (!dontcare)
            child_error (c->file->name, exit_code, exit_sig, coredump, 0);

          c->file->update_status = 2;
          if (delete_on_error == -1)
            {
              struct file *f = lookup_file (".DELETE_ON_ERROR");
              delete_on_error = f != 0 && f->is_target;
            }
          if (exit_sig != 0 || delete_on_error)
            delete_child_targets (c);
        }
      else
        {
          if (child_failed)
            {
              /* The commands failed, but we don't care.  */
              child_error (c->file->name,
                           exit_code, exit_sig, coredump, 1);
              child_failed = 0;
            }

          /* If there are more commands to run, try to start them.  */
          if (job_next_command (c))
            {
              if (handling_fatal_signal)
                {
                  /* Never start new commands while we are dying.
                     Since there are more commands that wanted to be run,
                     the target was not completely remade.  So we treat
                     this as if a command had failed.  */
                  c->file->update_status = 2;
                }
              else
                {
                  /* Check again whether to start remotely.
                     Whether or not we want to changes over time.
                     Also, start_remote_job may need state set up
                     by start_remote_job_p.  */
                  c->remote = start_remote_job_p (0);
                  start_job_command (c);
                  /* Fatal signals are left blocked in case we were
                     about to put that child on the chain.  But it is
                     already there, so it is safe for a fatal signal to
                     arrive now; it will clean up this child's targets.  */
                  unblock_sigs ();
                  if (c->file->command_state == cs_running)
                    /* We successfully started the new command.
                       Loop to reap more children.  */
                    continue;
                }

              if (c->file->update_status != 0)
                /* We failed to start the commands.  */
                delete_child_targets (c);
            }
          else
            /* There are no more commands.  We got through them all
               without an unignored error.  Now the target has been
               successfully updated.  */
            c->file->update_status = 0;
        }

      /* When we get here, all the commands for C->file are finished
         (or aborted) and C->file->update_status contains 0 or 2.  But
         C->file->command_state is still cs_running if all the commands
         ran; notice_finish_file looks for cs_running to tell it that
         it's interesting to check the file's modtime again now.  */

      if (! handling_fatal_signal)
        /* Notice if the target of the commands has been changed.
           This also propagates its values for command_state and
           update_status to its also_make files.  */
        notice_finished_file (c->file);

      DB (DB_JOBS, (_("Removing child %p PID %s%s from chain.\n"),
                    c, pid2str (c->pid), c->remote ? _(" (remote)") : ""));

      /* Block fatal signals while frobnicating the list, so that
         children and job_slots_used are always consistent.  Otherwise
         a fatal signal arriving after the child is off the chain and
         before job_slots_used is decremented would believe a child was
         live and call reap_children again.  */
      block_sigs ();

      /* There is now another slot open.  */
      if (job_slots_used > 0)
        --job_slots_used;

      /* Remove the child from the chain and free it.  */
      if (lastc == 0)
        children = c->next;
      else
        lastc->next = c->next;

      free_child (c);

      unblock_sigs ();

      /* If the job failed, and the -k flag was not given, die,
         unless we are already in the process of dying.  */
      if (!err && child_failed && !dontcare && !keep_going_flag &&
          /* fatal_error_signal will die with the right signal.  */
          !handling_fatal_signal)
        die (2);

      /* Only block for one child.  */
      block = 0;
    }

  return;
}


/* Free the storage allocated for CHILD.  */

static void
free_child (struct child *child)
{
  if (!jobserver_tokens)
    fatal (NILF, "INTERNAL: Freeing child %p (%s) but no tokens left!\n",
           child, child->file->name);

  /* If we're using the jobserver and this child is not the only outstanding
     job, put a token back into the pipe for it.  */

  if (job_fds[1] >= 0 && jobserver_tokens > 1)
    {
      char token = '+';
      int r;

      /* Write a job token back to the pipe.  */

      EINTRLOOP (r, write (job_fds[1], &token, 1));
      if (r != 1)
    pfatal_with_name (_("write jobserver"));

      DB (DB_JOBS, (_("Released token for child %p (%s).\n"),
                    child, child->file->name));
    }

  --jobserver_tokens;

  if (handling_fatal_signal) /* Don't bother free'ing if about to die.  */
    return;

  if (child->command_lines != 0)
    {
      register unsigned int i;
      for (i = 0; i < child->file->cmds->ncommand_lines; ++i)
    free (child->command_lines[i]);
      free (child->command_lines);
    }

  if (child->environment != 0)
    {
      register char **ep = child->environment;
      while (*ep != 0)
    free (*ep++);
      free (child->environment);
    }

  free (child);
}


#ifdef POSIX
extern sigset_t fatal_signal_set;
#endif

void
block_sigs (void)
{
#ifdef POSIX
  (void) sigprocmask (SIG_BLOCK, &fatal_signal_set, (sigset_t *) 0);
#else
# ifdef HAVE_SIGSETMASK
  (void) sigblock (fatal_signal_mask);
# endif
#endif
}

#ifdef POSIX
void
unblock_sigs (void)
{
  sigset_t empty;
  sigemptyset (&empty);
  sigprocmask (SIG_SETMASK, &empty, (sigset_t *) 0);
}
#endif

#ifdef MAKE_JOBSERVER
RETSIGTYPE
job_noop (int sig UNUSED)
{
}
/* Set the child handler action flags to FLAGS.  */
static void
set_child_handler_action_flags (int set_handler, int set_alarm)
{
  struct sigaction sa;

#ifdef __EMX__
  /* The child handler must be turned off here.  */
  signal (SIGCHLD, SIG_DFL);
#endif

  memset (&sa, '\0', sizeof sa);
  sa.sa_handler = child_handler;
  sa.sa_flags = set_handler ? 0 : SA_RESTART;
#if defined SIGCHLD
  sigaction (SIGCHLD, &sa, NULL);
#endif
#if defined SIGCLD && SIGCLD != SIGCHLD
  sigaction (SIGCLD, &sa, NULL);
#endif
#if defined SIGALRM
  if (set_alarm)
    {
      /* If we're about to enter the read(), set an alarm to wake up in a
         second so we can check if the load has dropped and we can start more
         work.  On the way out, turn off the alarm and set SIG_DFL.  */
      alarm (set_handler ? 1 : 0);
      sa.sa_handler = set_handler ? job_noop : SIG_DFL;
      sa.sa_flags = 0;
      sigaction (SIGALRM, &sa, NULL);
    }
#endif
}
#endif


/* Start a job to run the commands specified in CHILD.
   CHILD is updated to reflect the commands and ID of the child process.

   NOTE: On return fatal signals are blocked!  The caller is responsible
   for calling `unblock_sigs', once the new child is safely on the chain so
   it can be cleaned up in the event of a fatal signal.  */

static void
start_job_command (struct child *child)
{
#if !defined(_AMIGA) && !defined(WINDOWS32)
  static int bad_stdin = -1;
#endif
  char *p;
  /* Must be volatile to silence bogus GCC warning about longjmp/vfork.  */
  volatile int flags;
#ifdef VMS
  char *argv;
#else
  char **argv;
#endif

  /* If we have a completely empty commandset, stop now.  */
  if (!child->command_ptr)
    goto next_command;

  /* Combine the flags parsed for the line itself with
     the flags specified globally for this target.  */
  flags = (child->file->command_flags
       | child->file->cmds->lines_flags[child->command_line - 1]);

  p = child->command_ptr;
  child->noerror = ((flags & COMMANDS_NOERROR) != 0);

  while (*p != '\0')
    {
      if (*p == '@')
    flags |= COMMANDS_SILENT;
      else if (*p == '+')
    flags |= COMMANDS_RECURSE;
      else if (*p == '-')
    child->noerror = 1;
      else if (!isblank ((unsigned char)*p))
    break;
      ++p;
    }

  /* Update the file's command flags with any new ones we found.  We only
     keep the COMMANDS_RECURSE setting.  Even this isn't 100% correct; we are
     now marking more commands recursive than should be in the case of
     multiline define/endef scripts where only one line is marked "+".  In
     order to really fix this, we'll have to keep a lines_flags for every
     actual line, after expansion.  */
  child->file->cmds->lines_flags[child->command_line - 1]
    |= flags & COMMANDS_RECURSE;

  /* Figure out an argument list from this command line.  */

  {
    char *end = 0;
#ifdef VMS
    argv = p;
#else
    argv = construct_command_argv (p, &end, child->file,
                   child->file->cmds->lines_flags[child->command_line - 1],
                   &child->sh_batch_file);
#endif
    if (end == NULL)
      child->command_ptr = NULL;
    else
      {
    *end++ = '\0';
    child->command_ptr = end;
      }
  }

  /* If -q was given, say that updating `failed' if there was any text on the
     command line, or `succeeded' otherwise.  The exit status of 1 tells the
     user that -q is saying `something to do'; the exit status for a random
     error is 2.  */
  if (argv != 0 && question_flag && !(flags & COMMANDS_RECURSE))
    {
#ifndef VMS
      free (argv[0]);
      free (argv);
#endif
      child->file->update_status = 1;
      notice_finished_file (child->file);
      return;
    }

  if (touch_flag && !(flags & COMMANDS_RECURSE))
    {
      /* Go on to the next command.  It might be the recursive one.
     We construct ARGV only to find the end of the command line.  */
#ifndef VMS
      if (argv)
        {
          free (argv[0]);
          free (argv);
        }
#endif
      argv = 0;
    }

  if (argv == 0)
    {
    next_command:
#ifdef __MSDOS__
      execute_by_shell = 0;   /* in case construct_command_argv sets it */
#endif
      /* This line has no commands.  Go to the next.  */
      if (job_next_command (child))
    start_job_command (child);
      else
    {
      /* No more commands.  Make sure we're "running"; we might not be if
             (e.g.) all commands were skipped due to -n.  */
          set_command_state (child->file, cs_running);
      child->file->update_status = 0;
      notice_finished_file (child->file);
    }
      return;
    }

  /* Print out the command.  If silent, we call `message' with null so it
     can log the working directory before the command's own error messages
     appear.  */

  message (0, (just_print_flag || (!(flags & COMMANDS_SILENT) && !silent_flag))
       ? "%s" : (char *) 0, p);

  /* Tell update_goal_chain that a command has been started on behalf of
     this target.  It is important that this happens here and not in
     reap_children (where we used to do it), because reap_children might be
     reaping children from a different target.  We want this increment to
     guaranteedly indicate that a command was started for the dependency
     chain (i.e., update_file recursion chain) we are processing.  */

  ++commands_started;

  /* Optimize an empty command.  People use this for timestamp rules,
     so avoid forking a useless shell.  Do this after we increment
     commands_started so make still treats this special case as if it
     performed some action (makes a difference as to what messages are
     printed, etc.  */

#if !defined(VMS) && !defined(_AMIGA)
  if (
#if defined __MSDOS__ || defined (__EMX__)
      unixy_shell   /* the test is complicated and we already did it */
#else
      (argv[0] && is_bourne_compatible_shell(argv[0]))
#endif
      && (argv[1] && argv[1][0] == '-'
    &&
        ((argv[1][1] == 'c' && argv[1][2] == '\0')
      ||
         (argv[1][1] == 'e' && argv[1][2] == 'c' && argv[1][3] == '\0')))
      && (argv[2] && argv[2][0] == ':' && argv[2][1] == '\0')
      && argv[3] == NULL)
    {
      free (argv[0]);
      free (argv);
      goto next_command;
    }
#endif  /* !VMS && !_AMIGA */

  /* If -n was given, recurse to get the next line in the sequence.  */

  if (just_print_flag && !(flags & COMMANDS_RECURSE))
    {
#ifndef VMS
      free (argv[0]);
      free (argv);
#endif
      goto next_command;
    }

  /* Flush the output streams so they won't have things written twice.  */

  fflush (stdout);
  fflush (stderr);

#ifndef VMS
#if !defined(WINDOWS32) && !defined(_AMIGA) && !defined(__MSDOS__)

  /* Set up a bad standard input that reads from a broken pipe.  */

  if (bad_stdin == -1)
    {
      /* Make a file descriptor that is the read end of a broken pipe.
     This will be used for some children's standard inputs.  */
      int pd[2];
      if (pipe (pd) == 0)
    {
      /* Close the write side.  */
      (void) close (pd[1]);
      /* Save the read side.  */
      bad_stdin = pd[0];

      /* Set the descriptor to close on exec, so it does not litter any
         child's descriptor table.  When it is dup2'd onto descriptor 0,
         that descriptor will not close on exec.  */
      CLOSE_ON_EXEC (bad_stdin);
    }
    }

#endif /* !WINDOWS32 && !_AMIGA && !__MSDOS__ */

  /* Decide whether to give this child the `good' standard input
     (one that points to the terminal or whatever), or the `bad' one
     that points to the read side of a broken pipe.  */

  child->good_stdin = !good_stdin_used;
  if (child->good_stdin)
    good_stdin_used = 1;

#endif /* !VMS */

  child->deleted = 0;

#ifndef _AMIGA
  /* Set up the environment for the child.  */
  if (child->environment == 0)
    child->environment = target_environment (child->file);
#endif

#if !defined(__MSDOS__) && !defined(_AMIGA) && !defined(WINDOWS32)

#ifndef VMS
  /* start_waiting_job has set CHILD->remote if we can start a remote job.  */
  if (child->remote)
    {
      int is_remote, id, used_stdin;
      if (start_remote_job (argv, child->environment,
                child->good_stdin ? 0 : bad_stdin,
                &is_remote, &id, &used_stdin))
        /* Don't give up; remote execution may fail for various reasons.  If
           so, simply run the job locally.  */
    goto run_local;
      else
    {
      if (child->good_stdin && !used_stdin)
        {
          child->good_stdin = 0;
          good_stdin_used = 0;
        }
      child->remote = is_remote;
      child->pid = id;
    }
    }
  else
#endif /* !VMS */
    {
      /* Fork the child process.  */

      char **parent_environ;

    run_local:
      block_sigs ();

      child->remote = 0;

#ifdef VMS
      if (!child_execute_job (argv, child)) {
        /* Fork failed!  */
        perror_with_name ("vfork", "");
        goto error;
      }

#else

      parent_environ = environ;

# ifdef __EMX__
      /* If we aren't running a recursive command and we have a jobserver
         pipe, close it before exec'ing.  */
      if (!(flags & COMMANDS_RECURSE) && job_fds[0] >= 0)
    {
      CLOSE_ON_EXEC (job_fds[0]);
      CLOSE_ON_EXEC (job_fds[1]);
    }
      if (job_rfd >= 0)
    CLOSE_ON_EXEC (job_rfd);

      /* Never use fork()/exec() here! Use spawn() instead in exec_command() */
      child->pid = child_execute_job (child->good_stdin ? 0 : bad_stdin, 1,
                                      argv, child->environment);
      if (child->pid < 0)
    {
      /* spawn failed!  */
      unblock_sigs ();
      perror_with_name ("spawn", "");
      goto error;
    }

      /* undo CLOSE_ON_EXEC() after the child process has been started */
      if (!(flags & COMMANDS_RECURSE) && job_fds[0] >= 0)
    {
      fcntl (job_fds[0], F_SETFD, 0);
      fcntl (job_fds[1], F_SETFD, 0);
    }
      if (job_rfd >= 0)
    fcntl (job_rfd, F_SETFD, 0);

#else  /* !__EMX__ */

      child->pid = vfork ();
      environ = parent_environ; /* Restore value child may have clobbered.  */
      if (child->pid == 0)
    {
      /* We are the child side.  */
      unblock_sigs ();

          /* If we aren't running a recursive command and we have a jobserver
             pipe, close it before exec'ing.  */
          if (!(flags & COMMANDS_RECURSE) && job_fds[0] >= 0)
            {
              close (job_fds[0]);
              close (job_fds[1]);
            }
          if (job_rfd >= 0)
            close (job_rfd);

#ifdef SET_STACK_SIZE
          /* Reset limits, if necessary.  */
          if (stack_limit.rlim_cur)
            setrlimit (RLIMIT_STACK, &stack_limit);
#endif

      child_execute_job (child->good_stdin ? 0 : bad_stdin, 1,
                             argv, child->environment);
    }
      else if (child->pid < 0)
    {
      /* Fork failed!  */
      unblock_sigs ();
      perror_with_name ("vfork", "");
      goto error;
    }
# endif  /* !__EMX__ */
#endif /* !VMS */
    }

#else   /* __MSDOS__ or Amiga or WINDOWS32 */
#ifdef __MSDOS__
  {
    int proc_return;

    block_sigs ();
    dos_status = 0;

    /* We call `system' to do the job of the SHELL, since stock DOS
       shell is too dumb.  Our `system' knows how to handle long
       command lines even if pipes/redirection is needed; it will only
       call COMMAND.COM when its internal commands are used.  */
    if (execute_by_shell)
      {
    char *cmdline = argv[0];
    /* We don't have a way to pass environment to `system',
       so we need to save and restore ours, sigh...  */
    char **parent_environ = environ;

    environ = child->environment;

    /* If we have a *real* shell, tell `system' to call
       it to do everything for us.  */
    if (unixy_shell)
      {
        /* A *real* shell on MSDOS may not support long
           command lines the DJGPP way, so we must use `system'.  */
        cmdline = argv[2];  /* get past "shell -c" */
      }

    dos_command_running = 1;
    proc_return = system (cmdline);
    environ = parent_environ;
    execute_by_shell = 0;   /* for the next time */
      }
    else
      {
    dos_command_running = 1;
    proc_return = spawnvpe (P_WAIT, argv[0], argv, child->environment);
      }

    /* Need to unblock signals before turning off
       dos_command_running, so that child's signals
       will be treated as such (see fatal_error_signal).  */
    unblock_sigs ();
    dos_command_running = 0;

    /* If the child got a signal, dos_status has its
       high 8 bits set, so be careful not to alter them.  */
    if (proc_return == -1)
      dos_status |= 0xff;
    else
      dos_status |= (proc_return & 0xff);
    ++dead_children;
    child->pid = dos_pid++;
  }
#endif /* __MSDOS__ */
#ifdef _AMIGA
  amiga_status = MyExecute (argv);

  ++dead_children;
  child->pid = amiga_pid++;
  if (amiga_batch_file)
  {
     amiga_batch_file = 0;
     DeleteFile (amiga_bname);        /* Ignore errors.  */
  }
#endif  /* Amiga */
#ifdef WINDOWS32
  {
      HANDLE hPID;
      char* arg0;

      /* make UNC paths safe for CreateProcess -- backslash format */
      arg0 = argv[0];
      if (arg0 && arg0[0] == '/' && arg0[1] == '/')
        for ( ; arg0 && *arg0; arg0++)
          if (*arg0 == '/')
            *arg0 = '\\';

      /* make sure CreateProcess() has Path it needs */
      sync_Path_environment();

      hPID = process_easy(argv, child->environment);

      if (hPID != INVALID_HANDLE_VALUE)
        child->pid = (pid_t) hPID;
      else {
        int i;
        unblock_sigs();
        fprintf(stderr,
                _("process_easy() failed to launch process (e=%ld)\n"),
                process_last_err(hPID));
        for (i = 0; argv[i]; i++)
          fprintf(stderr, "%s ", argv[i]);
        fprintf(stderr, _("\nCounted %d args in failed launch\n"), i);
        goto error;
      }
  }
#endif /* WINDOWS32 */
#endif  /* __MSDOS__ or Amiga or WINDOWS32 */

  /* Bump the number of jobs started in this second.  */
  ++job_counter;

  /* We are the parent side.  Set the state to
     say the commands are running and return.  */

  set_command_state (child->file, cs_running);

  /* Free the storage used by the child's argument list.  */
#ifndef VMS
  free (argv[0]);
  free (argv);
#endif

  return;

 error:
  child->file->update_status = 2;
  notice_finished_file (child->file);
  return;
}

/* Try to start a child running.
   Returns nonzero if the child was started (and maybe finished), or zero if
   the load was too high and the child was put on the `waiting_jobs' chain.  */

static int
start_waiting_job (struct child *c)
{
  struct file *f = c->file;

  /* If we can start a job remotely, we always want to, and don't care about
     the local load average.  We record that the job should be started
     remotely in C->remote for start_job_command to test.  */

  c->remote = start_remote_job_p (1);

  /* If we are running at least one job already and the load average
     is too high, make this one wait.  */
  if (!c->remote
      && ((job_slots_used > 0 && load_too_high ())
#ifdef WINDOWS32
      || (process_used_slots () >= MAXIMUM_WAIT_OBJECTS)
#endif
      ))
    {
      /* Put this child on the chain of children waiting for the load average
         to go down.  */
      set_command_state (f, cs_running);
      c->next = waiting_jobs;
      waiting_jobs = c;
      return 0;
    }

  /* Start the first command; reap_children will run later command lines.  */
  start_job_command (c);

  switch (f->command_state)
    {
    case cs_running:
      c->next = children;
      DB (DB_JOBS, (_("Putting child %p (%s) PID %s%s on the chain.\n"),
                    c, c->file->name, pid2str (c->pid),
                    c->remote ? _(" (remote)") : ""));
      children = c;
      /* One more job slot is in use.  */
      ++job_slots_used;
      unblock_sigs ();
      break;

    case cs_not_started:
      /* All the command lines turned out to be empty.  */
      f->update_status = 0;
      /* FALLTHROUGH */

    case cs_finished:
      notice_finished_file (f);
      free_child (c);
      break;

    default:
      assert (f->command_state == cs_finished);
      break;
    }

  return 1;
}

/* Create a `struct child' for FILE and start its commands running.  */

void
new_job (struct file *file)
{
  struct commands *cmds = file->cmds;
  struct child *c;
  char **lines;
  unsigned int i;

  /* Let any previously decided-upon jobs that are waiting
     for the load to go down start before this new one.  */
  start_waiting_jobs ();

  /* Reap any children that might have finished recently.  */
  reap_children (0, 0);

  /* Chop the commands up into lines if they aren't already.  */
  chop_commands (cmds);

  /* Expand the command lines and store the results in LINES.  */
  lines = xmalloc (cmds->ncommand_lines * sizeof (char *));
  for (i = 0; i < cmds->ncommand_lines; ++i)
    {
      /* Collapse backslash-newline combinations that are inside variable
     or function references.  These are left alone by the parser so
     that they will appear in the echoing of commands (where they look
     nice); and collapsed by construct_command_argv when it tokenizes.
     But letting them survive inside function invocations loses because
     we don't want the functions to see them as part of the text.  */

      char *in, *out, *ref;

      /* IN points to where in the line we are scanning.
     OUT points to where in the line we are writing.
     When we collapse a backslash-newline combination,
     IN gets ahead of OUT.  */

      in = out = cmds->command_lines[i];
      while ((ref = strchr (in, '$')) != 0)
    {
      ++ref;        /* Move past the $.  */

      if (out != in)
        /* Copy the text between the end of the last chunk
           we processed (where IN points) and the new chunk
           we are about to process (where REF points).  */
        memmove (out, in, ref - in);

      /* Move both pointers past the boring stuff.  */
      out += ref - in;
      in = ref;

      if (*ref == '(' || *ref == '{')
        {
          char openparen = *ref;
          char closeparen = openparen == '(' ? ')' : '}';
          int count;
          char *p;

          *out++ = *in++;   /* Copy OPENPAREN.  */
          /* IN now points past the opening paren or brace.
         Count parens or braces until it is matched.  */
          count = 0;
          while (*in != '\0')
        {
          if (*in == closeparen && --count < 0)
            break;
          else if (*in == '\\' && in[1] == '\n')
            {
              /* We have found a backslash-newline inside a
             variable or function reference.  Eat it and
             any following whitespace.  */

              int quoted = 0;
              for (p = in - 1; p > ref && *p == '\\'; --p)
            quoted = !quoted;

              if (quoted)
            /* There were two or more backslashes, so this is
               not really a continuation line.  We don't collapse
               the quoting backslashes here as is done in
               collapse_continuations, because the line will
               be collapsed again after expansion.  */
            *out++ = *in++;
              else
            {
              /* Skip the backslash, newline and
                 any following whitespace.  */
              in = next_token (in + 2);

              /* Discard any preceding whitespace that has
                 already been written to the output.  */
              while (out > ref
                 && isblank ((unsigned char)out[-1]))
                --out;

              /* Replace it all with a single space.  */
              *out++ = ' ';
            }
            }
          else
            {
              if (*in == openparen)
            ++count;

              *out++ = *in++;
            }
        }
        }
    }

      /* There are no more references in this line to worry about.
     Copy the remaining uninteresting text to the output.  */
      if (out != in)
    memmove (out, in, strlen (in) + 1);

      /* Finally, expand the line.  */
      lines[i] = allocated_variable_expand_for_file (cmds->command_lines[i],
                             file);
    }

  /* Start the command sequence, record it in a new
     `struct child', and add that to the chain.  */

  c = xcalloc (sizeof (struct child));
  c->file = file;
  c->command_lines = lines;
  c->sh_batch_file = NULL;

  /* Cache dontcare flag because file->dontcare can be changed once we
     return. Check dontcare inheritance mechanism for details.  */
  c->dontcare = file->dontcare;

  /* Fetch the first command line to be run.  */
  job_next_command (c);

  /* Wait for a job slot to be freed up.  If we allow an infinite number
     don't bother; also job_slots will == 0 if we're using the jobserver.  */

  if (job_slots != 0)
    while (job_slots_used == job_slots)
      reap_children (1, 0);

#ifdef MAKE_JOBSERVER
  /* If we are controlling multiple jobs make sure we have a token before
     starting the child. */

  /* This can be inefficient.  There's a decent chance that this job won't
     actually have to run any subprocesses: the command script may be empty
     or otherwise optimized away.  It would be nice if we could defer
     obtaining a token until just before we need it, in start_job_command.
     To do that we'd need to keep track of whether we'd already obtained a
     token (since start_job_command is called for each line of the job, not
     just once).  Also more thought needs to go into the entire algorithm;
     this is where the old parallel job code waits, so...  */

  else if (job_fds[0] >= 0)
    while (1)
      {
        char token;
    int got_token;
    int saved_errno;

        DB (DB_JOBS, ("Need a job token; we %shave children\n",
                      children ? "" : "don't "));

        /* If we don't already have a job started, use our "free" token.  */
        if (!jobserver_tokens)
          break;

        /* Read a token.  As long as there's no token available we'll block.
           We enable interruptible system calls before the read(2) so that if
           we get a SIGCHLD while we're waiting, we'll return with EINTR and
           we can process the death(s) and return tokens to the free pool.

           Once we return from the read, we immediately reinstate restartable
           system calls.  This allows us to not worry about checking for
           EINTR on all the other system calls in the program.

           There is one other twist: there is a span between the time
           reap_children() does its last check for dead children and the time
           the read(2) call is entered, below, where if a child dies we won't
           notice.  This is extremely serious as it could cause us to
           deadlock, given the right set of events.

           To avoid this, we do the following: before we reap_children(), we
           dup(2) the read FD on the jobserver pipe.  The read(2) call below
           uses that new FD.  In the signal handler, we close that FD.  That
           way, if a child dies during the section mentioned above, the
           read(2) will be invoked with an invalid FD and will return
           immediately with EBADF.  */

        /* Make sure we have a dup'd FD.  */
        if (job_rfd < 0)
          {
            DB (DB_JOBS, ("Duplicate the job FD\n"));
            job_rfd = dup (job_fds[0]);
          }

        /* Reap anything that's currently waiting.  */
        reap_children (0, 0);

        /* Kick off any jobs we have waiting for an opportunity that
           can run now (ie waiting for load). */
        start_waiting_jobs ();

        /* If our "free" slot has become available, use it; we don't need an
           actual token.  */
        if (!jobserver_tokens)
          break;

        /* There must be at least one child already, or we have no business
           waiting for a token. */
        if (!children)
          fatal (NILF, "INTERNAL: no children as we go to sleep on read\n");

        /* Set interruptible system calls, and read() for a job token.  */
    set_child_handler_action_flags (1, waiting_jobs != NULL);
    got_token = read (job_rfd, &token, 1);
    saved_errno = errno;
    set_child_handler_action_flags (0, waiting_jobs != NULL);

        /* If we got one, we're done here.  */
    if (got_token == 1)
          {
            DB (DB_JOBS, (_("Obtained token for child %p (%s).\n"),
                          c, c->file->name));
            break;
          }

        /* If the error _wasn't_ expected (EINTR or EBADF), punt.  Otherwise,
           go back and reap_children(), and try again.  */
    errno = saved_errno;
        if (errno != EINTR && errno != EBADF)
          pfatal_with_name (_("read jobs pipe"));
        if (errno == EBADF)
          DB (DB_JOBS, ("Read returned EBADF.\n"));
      }
#endif

  ++jobserver_tokens;

  /* The job is now primed.  Start it running.
     (This will notice if there is in fact no recipe.)  */
  if (cmds->fileinfo.filenm)
    DB (DB_BASIC, (_("Invoking recipe from %s:%lu to update target `%s'.\n"),
                   cmds->fileinfo.filenm, cmds->fileinfo.lineno,
                   c->file->name));
  else
    DB (DB_BASIC, (_("Invoking builtin recipe to update target `%s'.\n"),
                   c->file->name));


  start_waiting_job (c);

  if (job_slots == 1 || not_parallel)
    /* Since there is only one job slot, make things run linearly.
       Wait for the child to die, setting the state to `cs_finished'.  */
    while (file->command_state == cs_running)
      reap_children (1, 0);

  return;
}


/* Move CHILD's pointers to the next command for it to execute.
   Returns nonzero if there is another command.  */

static int
job_next_command (struct child *child)
{
  while (child->command_ptr == 0 || *child->command_ptr == '\0')
    {
      /* There are no more lines in the expansion of this line.  */
      if (child->command_line == child->file->cmds->ncommand_lines)
    {
      /* There are no more lines to be expanded.  */
      child->command_ptr = 0;
      return 0;
    }
      else
    /* Get the next line to run.  */
    child->command_ptr = child->command_lines[child->command_line++];
    }
  return 1;
}

/* Determine if the load average on the system is too high to start a new job.
   The real system load average is only recomputed once a second.  However, a
   very parallel make can easily start tens or even hundreds of jobs in a
   second, which brings the system to its knees for a while until that first
   batch of jobs clears out.

   To avoid this we use a weighted algorithm to try to account for jobs which
   have been started since the last second, and guess what the load average
   would be now if it were computed.

   This algorithm was provided by Thomas Riedl <thomas.riedl@siemens.com>,
   who writes:

!      calculate something load-oid and add to the observed sys.load,
!      so that latter can catch up:
!      - every job started increases jobctr;
!      - every dying job decreases a positive jobctr;
!      - the jobctr value gets zeroed every change of seconds,
!        after its value*weight_b is stored into the 'backlog' value last_sec
!      - weight_a times the sum of jobctr and last_sec gets
!        added to the observed sys.load.
!
!      The two weights have been tried out on 24 and 48 proc. Sun Solaris-9
!      machines, using a several-thousand-jobs-mix of cpp, cc, cxx and smallish
!      sub-shelled commands (rm, echo, sed...) for tests.
!      lowering the 'direct influence' factor weight_a (e.g. to 0.1)
!      resulted in significant excession of the load limit, raising it
!      (e.g. to 0.5) took bad to small, fast-executing jobs and didn't
!      reach the limit in most test cases.
!
!      lowering the 'history influence' weight_b (e.g. to 0.1) resulted in
!      exceeding the limit for longer-running stuff (compile jobs in
!      the .5 to 1.5 sec. range),raising it (e.g. to 0.5) overrepresented
!      small jobs' effects.

 */

#define LOAD_WEIGHT_A           0.25
#define LOAD_WEIGHT_B           0.25

static int
load_too_high (void)
{
#if defined(__MSDOS__) || defined(VMS) || defined(_AMIGA) || defined(__riscos__)
  return 1;
#else
  static double last_sec;
  static time_t last_now;
  double load, guess;
  time_t now;

#ifdef WINDOWS32
  /* sub_proc.c cannot wait for more than MAXIMUM_WAIT_OBJECTS children */
  if (process_used_slots () >= MAXIMUM_WAIT_OBJECTS)
    return 1;
#endif

  if (max_load_average < 0)
    return 0;

  /* Find the real system load average.  */
  make_access ();
  if (getloadavg (&load, 1) != 1)
    {
      static int lossage = -1;
      /* Complain only once for the same error.  */
      if (lossage == -1 || errno != lossage)
    {
      if (errno == 0)
        /* An errno value of zero means getloadavg is just unsupported.  */
        error (NILF,
                   _("cannot enforce load limits on this operating system"));
      else
        perror_with_name (_("cannot enforce load limit: "), "getloadavg");
    }
      lossage = errno;
      load = 0;
    }
  user_access ();

  /* If we're in a new second zero the counter and correct the backlog
     value.  Only keep the backlog for one extra second; after that it's 0.  */
  now = time (NULL);
  if (last_now < now)
    {
      if (last_now == now - 1)
        last_sec = LOAD_WEIGHT_B * job_counter;
      else
        last_sec = 0.0;

      job_counter = 0;
      last_now = now;
    }

  /* Try to guess what the load would be right now.  */
  guess = load + (LOAD_WEIGHT_A * (job_counter + last_sec));

  DB (DB_JOBS, ("Estimated system load = %f (actual = %f) (max requested = %f)\n",
                guess, load, max_load_average));

  return guess >= max_load_average;
#endif
}

/* Start jobs that are waiting for the load to be lower.  */

void
start_waiting_jobs (void)
{
  struct child *job;

  if (waiting_jobs == 0)
    return;

  do
    {
      /* Check for recently deceased descendants.  */
      reap_children (0, 0);

      /* Take a job off the waiting list.  */
      job = waiting_jobs;
      waiting_jobs = job->next;

      /* Try to start that job.  We break out of the loop as soon
     as start_waiting_job puts one back on the waiting list.  */
    }
  while (start_waiting_job (job) && waiting_jobs != 0);

  return;
}


#ifndef WINDOWS32

/* EMX: Start a child process. This function returns the new pid.  */
# if defined __EMX__
int
child_execute_job (int stdin_fd, int stdout_fd, char **argv, char **envp)
{
  int pid;
  /* stdin_fd == 0 means: nothing to do for stdin;
     stdout_fd == 1 means: nothing to do for stdout */
  int save_stdin = (stdin_fd != 0) ? dup (0) : 0;
  int save_stdout = (stdout_fd != 1) ? dup (1): 1;

  /* < 0 only if dup() failed */
  if (save_stdin < 0)
    fatal (NILF, _("no more file handles: could not duplicate stdin\n"));
  if (save_stdout < 0)
    fatal (NILF, _("no more file handles: could not duplicate stdout\n"));

  /* Close unnecessary file handles for the child.  */
  if (save_stdin != 0)
    CLOSE_ON_EXEC (save_stdin);
  if (save_stdout != 1)
    CLOSE_ON_EXEC (save_stdout);

  /* Connect the pipes to the child process.  */
  if (stdin_fd != 0)
    (void) dup2 (stdin_fd, 0);
  if (stdout_fd != 1)
    (void) dup2 (stdout_fd, 1);

  /* stdin_fd and stdout_fd must be closed on exit because we are
     still in the parent process */
  if (stdin_fd != 0)
    CLOSE_ON_EXEC (stdin_fd);
  if (stdout_fd != 1)
    CLOSE_ON_EXEC (stdout_fd);

  /* Run the command.  */
  pid = exec_command (argv, envp);

  /* Restore stdout/stdin of the parent and close temporary FDs.  */
  if (stdin_fd != 0)
    {
      if (dup2 (save_stdin, 0) != 0)
        fatal (NILF, _("Could not restore stdin\n"));
      else
        close (save_stdin);
    }

  if (stdout_fd != 1)
    {
      if (dup2 (save_stdout, 1) != 1)
        fatal (NILF, _("Could not restore stdout\n"));
      else
        close (save_stdout);
    }

  return pid;
}

#elif !defined (_AMIGA) && !defined (__MSDOS__) && !defined (VMS)

/* UNIX:
   Replace the current process with one executing the command in ARGV.
   STDIN_FD and STDOUT_FD are used as the process's stdin and stdout; ENVP is
   the environment of the new program.  This function does not return.  */
void
child_execute_job (int stdin_fd, int stdout_fd, char **argv, char **envp)
{
  if (stdin_fd != 0)
    (void) dup2 (stdin_fd, 0);
  if (stdout_fd != 1)
    (void) dup2 (stdout_fd, 1);
  if (stdin_fd != 0)
    (void) close (stdin_fd);
  if (stdout_fd != 1)
    (void) close (stdout_fd);

  /* Run the command.  */
  exec_command (argv, envp);
}
#endif /* !AMIGA && !__MSDOS__ && !VMS */
#endif /* !WINDOWS32 */


#ifndef _AMIGA
/* Replace the current process with one running the command in ARGV,
   with environment ENVP.  This function does not return.  */

/* EMX: This function returns the pid of the child process.  */
# ifdef __EMX__
int
# else
void
# endif
exec_command (char **argv, char **envp)
{
#ifdef VMS
  /* to work around a problem with signals and execve: ignore them */
#ifdef SIGCHLD
  signal (SIGCHLD,SIG_IGN);
#endif
  /* Run the program.  */
  execve (argv[0], argv, envp);
  perror_with_name ("execve: ", argv[0]);
  _exit (EXIT_FAILURE);
#else
#ifdef WINDOWS32
  HANDLE hPID;
  HANDLE hWaitPID;
  int err = 0;
  int exit_code = EXIT_FAILURE;

  /* make sure CreateProcess() has Path it needs */
  sync_Path_environment();

  /* launch command */
  hPID = process_easy(argv, envp);

  /* make sure launch ok */
  if (hPID == INVALID_HANDLE_VALUE)
    {
      int i;
      fprintf(stderr,
              _("process_easy() failed to launch process (e=%ld)\n"),
              process_last_err(hPID));
      for (i = 0; argv[i]; i++)
          fprintf(stderr, "%s ", argv[i]);
      fprintf(stderr, _("\nCounted %d args in failed launch\n"), i);
      exit(EXIT_FAILURE);
    }

  /* wait and reap last child */
  hWaitPID = process_wait_for_any();
  while (hWaitPID)
    {
      /* was an error found on this process? */
      err = process_last_err(hWaitPID);

      /* get exit data */
      exit_code = process_exit_code(hWaitPID);

      if (err)
          fprintf(stderr, "make (e=%d, rc=%d): %s",
                  err, exit_code, map_windows32_error_to_string(err));

      /* cleanup process */
      process_cleanup(hWaitPID);

      /* expect to find only last pid, warn about other pids reaped */
      if (hWaitPID == hPID)
          break;
      else
    {
      char *pidstr = xstrdup (pid2str ((pid_t)hWaitPID));

          fprintf(stderr,
                  _("make reaped child pid %s, still waiting for pid %s\n"),
          pidstr, pid2str ((pid_t)hPID));
      free (pidstr);
    }
    }

  /* return child's exit code as our exit code */
  exit(exit_code);

#else  /* !WINDOWS32 */

# ifdef __EMX__
  int pid;
# endif

  /* Be the user, permanently.  */
  child_access ();

# ifdef __EMX__

  /* Run the program.  */
  pid = spawnvpe (P_NOWAIT, argv[0], argv, envp);

  if (pid >= 0)
    return pid;

  /* the file might have a strange shell extension */
  if (errno == ENOENT)
    errno = ENOEXEC;

# else

  /* Run the program.  */
  environ = envp;
  execvp (argv[0], argv);

# endif /* !__EMX__ */

  switch (errno)
    {
    case ENOENT:
      error (NILF, _("%s: Command not found"), argv[0]);
      break;
    case ENOEXEC:
      {
    /* The file is not executable.  Try it as a shell script.  */
    extern char *getenv ();
    char *shell;
    char **new_argv;
    int argc;
        int i=1;

# ifdef __EMX__
        /* Do not use $SHELL from the environment */
    struct variable *p = lookup_variable ("SHELL", 5);
    if (p)
      shell = p->value;
        else
          shell = 0;
# else
    shell = getenv ("SHELL");
# endif
    if (shell == 0)
      shell = default_shell;

    argc = 1;
    while (argv[argc] != 0)
      ++argc;

# ifdef __EMX__
        if (!unixy_shell)
          ++argc;
# endif

    new_argv = alloca ((1 + argc + 1) * sizeof (char *));
    new_argv[0] = shell;

# ifdef __EMX__
        if (!unixy_shell)
          {
            new_argv[1] = "/c";
            ++i;
            --argc;
          }
# endif

        new_argv[i] = argv[0];
    while (argc > 0)
      {
        new_argv[i + argc] = argv[argc];
        --argc;
      }

# ifdef __EMX__
    pid = spawnvpe (P_NOWAIT, shell, new_argv, envp);
    if (pid >= 0)
          break;
# else
    execvp (shell, new_argv);
# endif
    if (errno == ENOENT)
      error (NILF, _("%s: Shell program not found"), shell);
    else
      perror_with_name ("execvp: ", shell);
    break;
      }

# ifdef __EMX__
    case EINVAL:
      /* this nasty error was driving me nuts :-( */
      error (NILF, _("spawnvpe: environment space might be exhausted"));
      /* FALLTHROUGH */
# endif

    default:
      perror_with_name ("execvp: ", argv[0]);
      break;
    }

# ifdef __EMX__
  return pid;
# else
  _exit (127);
# endif
#endif /* !WINDOWS32 */
#endif /* !VMS */
}
#else /* On Amiga */
void exec_command (char **argv)
{
  MyExecute (argv);
}

void clean_tmp (void)
{
  DeleteFile (amiga_bname);
}

#endif /* On Amiga */


#ifndef VMS
/* Figure out the argument list necessary to run LINE as a command.  Try to
   avoid using a shell.  This routine handles only ' quoting, and " quoting
   when no backslash, $ or ` characters are seen in the quotes.  Starting
   quotes may be escaped with a backslash.  If any of the characters in
   sh_chars[] is seen, or any of the builtin commands listed in sh_cmds[]
   is the first word of a line, the shell is used.

   If RESTP is not NULL, *RESTP is set to point to the first newline in LINE.
   If *RESTP is NULL, newlines will be ignored.

   SHELL is the shell to use, or nil to use the default shell.
   IFS is the value of $IFS, or nil (meaning the default).

   FLAGS is the value of lines_flags for this command line.  It is
   used in the WINDOWS32 port to check whether + or $(MAKE) were found
   in this command line, in which case the effect of just_print_flag
   is overridden.  */

static char **
construct_command_argv_internal (char *line, char **restp, char *shell,
                                 char *shellflags, char *ifs, int flags,
                 char **batch_filename_ptr)
{
#ifdef __MSDOS__
  /* MSDOS supports both the stock DOS shell and ports of Unixy shells.
     We call `system' for anything that requires ``slow'' processing,
     because DOS shells are too dumb.  When $SHELL points to a real
     (unix-style) shell, `system' just calls it to do everything.  When
     $SHELL points to a DOS shell, `system' does most of the work
     internally, calling the shell only for its internal commands.
     However, it looks on the $PATH first, so you can e.g. have an
     external command named `mkdir'.

     Since we call `system', certain characters and commands below are
     actually not specific to COMMAND.COM, but to the DJGPP implementation
     of `system'.  In particular:

       The shell wildcard characters are in DOS_CHARS because they will
       not be expanded if we call the child via `spawnXX'.

       The `;' is in DOS_CHARS, because our `system' knows how to run
       multiple commands on a single line.

       DOS_CHARS also include characters special to 4DOS/NDOS, so we
       won't have to tell one from another and have one more set of
       commands and special characters.  */
  static char sh_chars_dos[] = "*?[];|<>%^&()";
  static char *sh_cmds_dos[] = { "break", "call", "cd", "chcp", "chdir", "cls",
                 "copy", "ctty", "date", "del", "dir", "echo",
                 "erase", "exit", "for", "goto", "if", "md",
                 "mkdir", "path", "pause", "prompt", "rd",
                 "rmdir", "rem", "ren", "rename", "set",
                 "shift", "time", "type", "ver", "verify",
                 "vol", ":", 0 };

  static char sh_chars_sh[]  = "#;\"*?[]&|<>(){}$`^";
  static char *sh_cmds_sh[]  = { "cd", "echo", "eval", "exec", "exit", "login",
                 "logout", "set", "umask", "wait", "while",
                 "for", "case", "if", ":", ".", "break",
                 "continue", "export", "read", "readonly",
                 "shift", "times", "trap", "switch", "unset",
                                 "ulimit", 0 };

  char *sh_chars;
  char **sh_cmds;
#elif defined (__EMX__)
  static char sh_chars_dos[] = "*?[];|<>%^&()";
  static char *sh_cmds_dos[] = { "break", "call", "cd", "chcp", "chdir", "cls",
                 "copy", "ctty", "date", "del", "dir", "echo",
                 "erase", "exit", "for", "goto", "if", "md",
                 "mkdir", "path", "pause", "prompt", "rd",
                 "rmdir", "rem", "ren", "rename", "set",
                 "shift", "time", "type", "ver", "verify",
                 "vol", ":", 0 };

  static char sh_chars_os2[] = "*?[];|<>%^()\"'&";
  static char *sh_cmds_os2[] = { "call", "cd", "chcp", "chdir", "cls", "copy",
                 "date", "del", "detach", "dir", "echo",
                 "endlocal", "erase", "exit", "for", "goto", "if",
                 "keys", "md", "mkdir", "move", "path", "pause",
                 "prompt", "rd", "rem", "ren", "rename", "rmdir",
                 "set", "setlocal", "shift", "start", "time",
                             "type", "ver", "verify", "vol", ":", 0 };

  static char sh_chars_sh[]  = "#;\"*?[]&|<>(){}$`^~'";
  static char *sh_cmds_sh[]  = { "echo", "cd", "eval", "exec", "exit", "login",
                 "logout", "set", "umask", "wait", "while",
                 "for", "case", "if", ":", ".", "break",
                 "continue", "export", "read", "readonly",
                 "shift", "times", "trap", "switch", "unset",
                                 0 };
  char *sh_chars;
  char **sh_cmds;

#elif defined (_AMIGA)
  static char sh_chars[] = "#;\"|<>()?*$`";
  static char *sh_cmds[] = { "cd", "eval", "if", "delete", "echo", "copy",
                 "rename", "set", "setenv", "date", "makedir",
                 "skip", "else", "endif", "path", "prompt",
                 "unset", "unsetenv", "version",
                 0 };
#elif defined (WINDOWS32)
  static char sh_chars_dos[] = "\"|&<>";
  static char *sh_cmds_dos[] = { "assoc", "break", "call", "cd", "chcp",
                 "chdir", "cls", "color", "copy", "ctty",
                 "date", "del", "dir", "echo", "echo.",
                 "endlocal", "erase", "exit", "for", "ftype",
                 "goto", "if", "if", "md", "mkdir", "path",
                 "pause", "prompt", "rd", "rem", "ren",
                 "rename", "rmdir", "set", "setlocal",
                 "shift", "time", "title", "type", "ver",
                 "verify", "vol", ":", 0 };
  static char sh_chars_sh[] = "#;\"*?[]&|<>(){}$`^";
  static char *sh_cmds_sh[] = { "cd", "eval", "exec", "exit", "login",
                 "logout", "set", "umask", "wait", "while", "for",
                 "case", "if", ":", ".", "break", "continue",
                 "export", "read", "readonly", "shift", "times",
                 "trap", "switch", "test",
#ifdef BATCH_MODE_ONLY_SHELL
                 "echo",
#endif
                 0 };
  char*  sh_chars;
  char** sh_cmds;
#elif defined(__riscos__)
  static char sh_chars[] = "";
  static char *sh_cmds[] = { 0 };
#else  /* must be UNIX-ish */
  static char sh_chars[] = "#;\"*?[]&|<>(){}$`^~!";
  static char *sh_cmds[] = { ".", ":", "break", "case", "cd", "continue",
                             "eval", "exec", "exit", "export", "for", "if",
                             "login", "logout", "read", "readonly", "set",
                             "shift", "switch", "test", "times", "trap",
                             "ulimit", "umask", "unset", "wait", "while", 0 };
# ifdef HAVE_DOS_PATHS
  /* This is required if the MSYS/Cygwin ports (which do not define
     WINDOWS32) are compiled with HAVE_DOS_PATHS defined, which uses
     sh_chars_sh[] directly (see below).  */
  static char *sh_chars_sh = sh_chars;
# endif  /* HAVE_DOS_PATHS */
#endif
  int i;
  char *p;
  char *ap;
  char *end;
  int instring, word_has_equals, seen_nonequals, last_argument_was_empty;
  char **new_argv = 0;
  char *argstr = 0;
#ifdef WINDOWS32
  int slow_flag = 0;

  if (!unixy_shell) {
    sh_cmds = sh_cmds_dos;
    sh_chars = sh_chars_dos;
  } else {
    sh_cmds = sh_cmds_sh;
    sh_chars = sh_chars_sh;
  }
#endif /* WINDOWS32 */

  if (restp != NULL)
    *restp = NULL;

  /* Make sure not to bother processing an empty line.  */
  while (isblank ((unsigned char)*line))
    ++line;
  if (*line == '\0')
    return 0;

  /* See if it is safe to parse commands internally.  */
  if (shell == 0)
    shell = default_shell;
#ifdef WINDOWS32
  else if (strcmp (shell, default_shell))
  {
    char *s1 = _fullpath (NULL, shell, 0);
    char *s2 = _fullpath (NULL, default_shell, 0);

    slow_flag = strcmp ((s1 ? s1 : ""), (s2 ? s2 : ""));

    if (s1)
      free (s1);
    if (s2)
      free (s2);
  }
  if (slow_flag)
    goto slow;
#else  /* not WINDOWS32 */
#if defined (__MSDOS__) || defined (__EMX__)
  else if (strcasecmp (shell, default_shell))
    {
      extern int _is_unixy_shell (const char *_path);

      DB (DB_BASIC, (_("$SHELL changed (was `%s', now `%s')\n"),
                     default_shell, shell));
      unixy_shell = _is_unixy_shell (shell);
      /* we must allocate a copy of shell: construct_command_argv() will free
       * shell after this function returns.  */
      default_shell = xstrdup (shell);
    }
  if (unixy_shell)
    {
      sh_chars = sh_chars_sh;
      sh_cmds  = sh_cmds_sh;
    }
  else
    {
      sh_chars = sh_chars_dos;
      sh_cmds  = sh_cmds_dos;
# ifdef __EMX__
      if (_osmode == OS2_MODE)
        {
          sh_chars = sh_chars_os2;
          sh_cmds = sh_cmds_os2;
        }
# endif
    }
#else  /* !__MSDOS__ */
  else if (strcmp (shell, default_shell))
    goto slow;
#endif /* !__MSDOS__ && !__EMX__ */
#endif /* not WINDOWS32 */

  if (ifs != 0)
    for (ap = ifs; *ap != '\0'; ++ap)
      if (*ap != ' ' && *ap != '\t' && *ap != '\n')
    goto slow;

  if (shellflags != 0)
    if (shellflags[0] != '-'
        || ((shellflags[1] != 'c' || shellflags[2] != '\0')
            && (shellflags[1] != 'e' || shellflags[2] != 'c' || shellflags[3] != '\0')))
      goto slow;

  i = strlen (line) + 1;

  /* More than 1 arg per character is impossible.  */
  new_argv = xmalloc (i * sizeof (char *));

  /* All the args can fit in a buffer as big as LINE is.   */
  ap = new_argv[0] = argstr = xmalloc (i);
  end = ap + i;

  /* I is how many complete arguments have been found.  */
  i = 0;
  instring = word_has_equals = seen_nonequals = last_argument_was_empty = 0;
  for (p = line; *p != '\0'; ++p)
    {
      assert (ap <= end);

      if (instring)
    {
      /* Inside a string, just copy any char except a closing quote
         or a backslash-newline combination.  */
      if (*p == instring)
        {
          instring = 0;
          if (ap == new_argv[0] || *(ap-1) == '\0')
        last_argument_was_empty = 1;
        }
      else if (*p == '\\' && p[1] == '\n')
            {
              /* Backslash-newline is handled differently depending on what
                 kind of string we're in: inside single-quoted strings you
                 keep them; in double-quoted strings they disappear.
             For DOS/Windows/OS2, if we don't have a POSIX shell,
         we keep the pre-POSIX behavior of removing the
         backslash-newline.  */
              if (instring == '"'
#if defined (__MSDOS__) || defined (__EMX__) || defined (WINDOWS32)
          || !unixy_shell
#endif
          )
                ++p;
              else
                {
                  *(ap++) = *(p++);
                  *(ap++) = *p;
                }
            }
      else if (*p == '\n' && restp != NULL)
        {
          /* End of the command line.  */
          *restp = p;
          goto end_of_line;
        }
      /* Backslash, $, and ` are special inside double quotes.
         If we see any of those, punt.
         But on MSDOS, if we use COMMAND.COM, double and single
         quotes have the same effect.  */
      else if (instring == '"' && strchr ("\\$`", *p) != 0 && unixy_shell)
        goto slow;
      else
        *ap++ = *p;
    }
      else if (strchr (sh_chars, *p) != 0)
    /* Not inside a string, but it's a special char.  */
    goto slow;
      else if (one_shell && *p == '\n')
    /* In .ONESHELL mode \n is a separator like ; or && */
    goto slow;
#ifdef  __MSDOS__
      else if (*p == '.' && p[1] == '.' && p[2] == '.' && p[3] != '.')
    /* `...' is a wildcard in DJGPP.  */
    goto slow;
#endif
      else
    /* Not a special char.  */
    switch (*p)
      {
      case '=':
        /* Equals is a special character in leading words before the
           first word with no equals sign in it.  This is not the case
           with sh -k, but we never get here when using nonstandard
           shell flags.  */
        if (! seen_nonequals && unixy_shell)
          goto slow;
        word_has_equals = 1;
        *ap++ = '=';
        break;

      case '\\':
        /* Backslash-newline has special case handling, ref POSIX.
               We're in the fastpath, so emulate what the shell would do.  */
        if (p[1] == '\n')
          {
        /* Throw out the backslash and newline.  */
                ++p;

                /* If there's nothing in this argument yet, skip any
                   whitespace before the start of the next word.  */
                if (ap == new_argv[i])
                  p = next_token (p + 1) - 1;
          }
        else if (p[1] != '\0')
              {
#ifdef HAVE_DOS_PATHS
                /* Only remove backslashes before characters special to Unixy
                   shells.  All other backslashes are copied verbatim, since
                   they are probably DOS-style directory separators.  This
                   still leaves a small window for problems, but at least it
                   should work for the vast majority of naive users.  */

#ifdef __MSDOS__
                /* A dot is only special as part of the "..."
                   wildcard.  */
                if (strneq (p + 1, ".\\.\\.", 5))
                  {
                    *ap++ = '.';
                    *ap++ = '.';
                    p += 4;
                  }
                else
#endif
                  if (p[1] != '\\' && p[1] != '\''
                      && !isspace ((unsigned char)p[1])
                      && strchr (sh_chars_sh, p[1]) == 0)
                    /* back up one notch, to copy the backslash */
                    --p;
#endif  /* HAVE_DOS_PATHS */

                /* Copy and skip the following char.  */
                *ap++ = *++p;
              }
        break;

      case '\'':
      case '"':
        instring = *p;
        break;

      case '\n':
        if (restp != NULL)
          {
        /* End of the command line.  */
        *restp = p;
        goto end_of_line;
          }
        else
          /* Newlines are not special.  */
          *ap++ = '\n';
        break;

      case ' ':
      case '\t':
        /* We have the end of an argument.
           Terminate the text of the argument.  */
        *ap++ = '\0';
        new_argv[++i] = ap;
        last_argument_was_empty = 0;

        /* Update SEEN_NONEQUALS, which tells us if every word
           heretofore has contained an `='.  */
        seen_nonequals |= ! word_has_equals;
        if (word_has_equals && ! seen_nonequals)
          /* An `=' in a word before the first
         word without one is magical.  */
          goto slow;
        word_has_equals = 0; /* Prepare for the next word.  */

        /* If this argument is the command name,
           see if it is a built-in shell command.
           If so, have the shell handle it.  */
        if (i == 1)
          {
        register int j;
        for (j = 0; sh_cmds[j] != 0; ++j)
                  {
                    if (streq (sh_cmds[j], new_argv[0]))
                      goto slow;
# ifdef __EMX__
                    /* Non-Unix shells are case insensitive.  */
                    if (!unixy_shell
                        && strcasecmp (sh_cmds[j], new_argv[0]) == 0)
                      goto slow;
# endif
                  }
          }

        /* Ignore multiple whitespace chars.  */
        p = next_token (p) - 1;
        break;

      default:
        *ap++ = *p;
        break;
      }
    }
 end_of_line:

  if (instring)
    /* Let the shell deal with an unterminated quote.  */
    goto slow;

  /* Terminate the last argument and the argument list.  */

  *ap = '\0';
  if (new_argv[i][0] != '\0' || last_argument_was_empty)
    ++i;
  new_argv[i] = 0;

  if (i == 1)
    {
      register int j;
      for (j = 0; sh_cmds[j] != 0; ++j)
    if (streq (sh_cmds[j], new_argv[0]))
      goto slow;
    }

  if (new_argv[0] == 0)
    {
      /* Line was empty.  */
      free (argstr);
      free (new_argv);
      return 0;
    }

  return new_argv;

 slow:;
  /* We must use the shell.  */

  if (new_argv != 0)
    {
      /* Free the old argument list we were working on.  */
      free (argstr);
      free (new_argv);
    }

#ifdef __MSDOS__
  execute_by_shell = 1; /* actually, call `system' if shell isn't unixy */
#endif

#ifdef _AMIGA
  {
    char *ptr;
    char *buffer;
    char *dptr;

    buffer = xmalloc (strlen (line)+1);

    ptr = line;
    for (dptr=buffer; *ptr; )
    {
      if (*ptr == '\\' && ptr[1] == '\n')
    ptr += 2;
      else if (*ptr == '@') /* Kludge: multiline commands */
      {
    ptr += 2;
    *dptr++ = '\n';
      }
      else
    *dptr++ = *ptr++;
    }
    *dptr = 0;

    new_argv = xmalloc (2 * sizeof (char *));
    new_argv[0] = buffer;
    new_argv[1] = 0;
  }
#else   /* Not Amiga  */
#ifdef WINDOWS32
  /*
   * Not eating this whitespace caused things like
   *
   *    sh -c "\n"
   *
   * which gave the shell fits. I think we have to eat
   * whitespace here, but this code should be considered
   * suspicious if things start failing....
   */

  /* Make sure not to bother processing an empty line.  */
  while (isspace ((unsigned char)*line))
    ++line;
  if (*line == '\0')
    return 0;
#endif /* WINDOWS32 */

  {
    /* SHELL may be a multi-word command.  Construct a command line
       "$(SHELL) $(.SHELLFLAGS) LINE", with all special chars in LINE escaped.
       Then recurse, expanding this command line to get the final
       argument list.  */

    unsigned int shell_len = strlen (shell);
    unsigned int line_len = strlen (line);
    unsigned int sflags_len = strlen (shellflags);
    char *command_ptr = NULL; /* used for batch_mode_shell mode */
    char *new_line;

# ifdef __EMX__ /* is this necessary? */
    if (!unixy_shell)
      shellflags[0] = '/'; /* "/c" */
# endif

    /* In .ONESHELL mode we are allowed to throw the entire current
    recipe string at a single shell and trust that the user
    has configured the shell and shell flags, and formatted
    the string, appropriately. */
    if (one_shell)
      {
    /* If the shell is Bourne compatible, we must remove and ignore
       interior special chars [@+-] because they're meaningless to
       the shell itself. If, however, we're in .ONESHELL mode and
       have changed SHELL to something non-standard, we should
       leave those alone because they could be part of the
       script. In this case we must also leave in place
       any leading [@+-] for the same reason.  */

    /* Remove and ignore interior prefix chars [@+-] because they're
         meaningless given a single shell. */
#if defined __MSDOS__ || defined (__EMX__)
    if (unixy_shell)     /* the test is complicated and we already did it */
#else
    if (is_bourne_compatible_shell(shell))
#endif
          {
            const char *f = line;
            char *t = line;

            /* Copy the recipe, removing and ignoring interior prefix chars
               [@+-]: they're meaningless in .ONESHELL mode.  */
            while (f[0] != '\0')
              {
                int esc = 0;

                /* This is the start of a new recipe line.
                   Skip whitespace and prefix characters.  */
                while (isblank (*f) || *f == '-' || *f == '@' || *f == '+')
                  ++f;

                /* Copy until we get to the next logical recipe line.  */
                while (*f != '\0')
                  {
                    *(t++) = *(f++);
                    if (f[-1] == '\\')
                      esc = !esc;
                    else
                      {
                        /* On unescaped newline, we're done with this line.  */
                        if (f[-1] == '\n' && ! esc)
                          break;

                        /* Something else: reset the escape sequence.  */
                        esc = 0;
                      }
                  }
              }
            *t = '\0';
          }

    new_argv = xmalloc (4 * sizeof (char *));
    new_argv[0] = xstrdup(shell);
    new_argv[1] = xstrdup(shellflags);
    new_argv[2] = line;
    new_argv[3] = NULL;
    return new_argv;
      }

    new_line = alloca (shell_len + 1 + sflags_len + 1
                             + (line_len*2) + 1);
    ap = new_line;
    memcpy (ap, shell, shell_len);
    ap += shell_len;
    *(ap++) = ' ';
    memcpy (ap, shellflags, sflags_len);
    ap += sflags_len;
    *(ap++) = ' ';
    command_ptr = ap;
    for (p = line; *p != '\0'; ++p)
      {
    if (restp != NULL && *p == '\n')
      {
        *restp = p;
        break;
      }
    else if (*p == '\\' && p[1] == '\n')
      {
        /* POSIX says we keep the backslash-newline.  If we don't have a
               POSIX shell on DOS/Windows/OS2, mimic the pre-POSIX behavior
               and remove the backslash/newline.  */
#if defined (__MSDOS__) || defined (__EMX__) || defined (WINDOWS32)
# define PRESERVE_BSNL  unixy_shell
#else
# define PRESERVE_BSNL  1
#endif
        if (PRESERVE_BSNL)
          {
        *(ap++) = '\\';
        /* Only non-batch execution needs another backslash,
           because it will be passed through a recursive
           invocation of this function.  */
        if (!batch_mode_shell)
          *(ap++) = '\\';
        *(ap++) = '\n';
          }
        ++p;
        continue;
      }

        /* DOS shells don't know about backslash-escaping.  */
    if (unixy_shell && !batch_mode_shell &&
            (*p == '\\' || *p == '\'' || *p == '"'
             || isspace ((unsigned char)*p)
             || strchr (sh_chars, *p) != 0))
      *ap++ = '\\';
#ifdef __MSDOS__
        else if (unixy_shell && strneq (p, "...", 3))
          {
            /* The case of `...' wildcard again.  */
            strcpy (ap, "\\.\\.\\");
            ap += 5;
            p  += 2;
          }
#endif
    *ap++ = *p;
      }
    if (ap == new_line + shell_len + sflags_len + 2)
      /* Line was empty.  */
      return 0;
    *ap = '\0';

#ifdef WINDOWS32
    /* Some shells do not work well when invoked as 'sh -c xxx' to run a
       command line (e.g. Cygnus GNUWIN32 sh.exe on WIN32 systems).  In these
       cases, run commands via a script file.  */
    if (just_print_flag && !(flags & COMMANDS_RECURSE)) {
      /* Need to allocate new_argv, although it's unused, because
        start_job_command will want to free it and its 0'th element.  */
      new_argv = xmalloc(2 * sizeof (char *));
      new_argv[0] = xstrdup ("");
      new_argv[1] = NULL;
    } else if ((no_default_sh_exe || batch_mode_shell) && batch_filename_ptr) {
      int temp_fd;
      FILE* batch = NULL;
      int id = GetCurrentProcessId();
      PATH_VAR(fbuf);

      /* create a file name */
      sprintf(fbuf, "make%d", id);
      *batch_filename_ptr = create_batch_file (fbuf, unixy_shell, &temp_fd);

      DB (DB_JOBS, (_("Creating temporary batch file %s\n"),
                    *batch_filename_ptr));

      /* Create a FILE object for the batch file, and write to it the
     commands to be executed.  Put the batch file in TEXT mode.  */
      _setmode (temp_fd, _O_TEXT);
      batch = _fdopen (temp_fd, "wt");
      if (!unixy_shell)
        fputs ("@echo off\n", batch);
      fputs (command_ptr, batch);
      fputc ('\n', batch);
      fclose (batch);
      DB (DB_JOBS, (_("Batch file contents:%s\n\t%s\n"),
            !unixy_shell ? "\n\t@echo off" : "", command_ptr));

      /* create argv */
      new_argv = xmalloc(3 * sizeof (char *));
      if (unixy_shell) {
        new_argv[0] = xstrdup (shell);
        new_argv[1] = *batch_filename_ptr; /* only argv[0] gets freed later */
      } else {
        new_argv[0] = xstrdup (*batch_filename_ptr);
        new_argv[1] = NULL;
      }
      new_argv[2] = NULL;
    } else
#endif /* WINDOWS32 */

    if (unixy_shell)
      new_argv = construct_command_argv_internal (new_line, 0, 0, 0, 0, flags, 0);

#ifdef __EMX__
    else if (!unixy_shell)
      {
    /* new_line is local, must not be freed therefore
           We use line here instead of new_line because we run the shell
           manually.  */
        size_t line_len = strlen (line);
        char *p = new_line;
        char *q = new_line;
        memcpy (new_line, line, line_len + 1);
        /* Replace all backslash-newline combination and also following tabs.
           Important: stop at the first '\n' because that's what the loop above
           did. The next line starting at restp[0] will be executed during the
           next call of this function. */
        while (*q != '\0' && *q != '\n')
          {
            if (q[0] == '\\' && q[1] == '\n')
              q += 2; /* remove '\\' and '\n' */
            else
              *p++ = *q++;
          }
        *p = '\0';

# ifndef NO_CMD_DEFAULT
        if (strnicmp (new_line, "echo", 4) == 0
            && (new_line[4] == ' ' || new_line[4] == '\t'))
          {
            /* the builtin echo command: handle it separately */
            size_t echo_len = line_len - 5;
            char *echo_line = new_line + 5;

            /* special case: echo 'x="y"'
               cmd works this way: a string is printed as is, i.e., no quotes
               are removed. But autoconf uses a command like echo 'x="y"' to
               determine whether make works. autoconf expects the output x="y"
               so we will do exactly that.
               Note: if we do not allow cmd to be the default shell
               we do not need this kind of voodoo */
            if (echo_line[0] == '\''
                && echo_line[echo_len - 1] == '\''
                && strncmp (echo_line + 1, "ac_maketemp=",
                            strlen ("ac_maketemp=")) == 0)
              {
                /* remove the enclosing quotes */
                memmove (echo_line, echo_line + 1, echo_len - 2);
                echo_line[echo_len - 2] = '\0';
              }
          }
# endif

        {
          /* Let the shell decide what to do. Put the command line into the
             2nd command line argument and hope for the best ;-)  */
          size_t sh_len = strlen (shell);

          /* exactly 3 arguments + NULL */
          new_argv = xmalloc (4 * sizeof (char *));
          /* Exactly strlen(shell) + strlen("/c") + strlen(line) + 3 times
             the trailing '\0' */
          new_argv[0] = xmalloc (sh_len + line_len + 5);
          memcpy (new_argv[0], shell, sh_len + 1);
          new_argv[1] = new_argv[0] + sh_len + 1;
          memcpy (new_argv[1], "/c", 3);
          new_argv[2] = new_argv[1] + 3;
          memcpy (new_argv[2], new_line, line_len + 1);
          new_argv[3] = NULL;
        }
      }
#elif defined(__MSDOS__)
    else
      {
        /* With MSDOS shells, we must construct the command line here
           instead of recursively calling ourselves, because we
           cannot backslash-escape the special characters (see above).  */
        new_argv = xmalloc (sizeof (char *));
        line_len = strlen (new_line) - shell_len - sflags_len - 2;
        new_argv[0] = xmalloc (line_len + 1);
        strncpy (new_argv[0],
                 new_line + shell_len + sflags_len + 2, line_len);
        new_argv[0][line_len] = '\0';
      }
#else
    else
      fatal (NILF, _("%s (line %d) Bad shell context (!unixy && !batch_mode_shell)\n"),
            __FILE__, __LINE__);
#endif
  }
#endif  /* ! AMIGA */

  return new_argv;
}
#endif /* !VMS */

/* Figure out the argument list necessary to run LINE as a command.  Try to
   avoid using a shell.  This routine handles only ' quoting, and " quoting
   when no backslash, $ or ` characters are seen in the quotes.  Starting
   quotes may be escaped with a backslash.  If any of the characters in
   sh_chars[] is seen, or any of the builtin commands listed in sh_cmds[]
   is the first word of a line, the shell is used.

   If RESTP is not NULL, *RESTP is set to point to the first newline in LINE.
   If *RESTP is NULL, newlines will be ignored.

   FILE is the target whose commands these are.  It is used for
   variable expansion for $(SHELL) and $(IFS).  */

char **
construct_command_argv (char *line, char **restp, struct file *file,
                        int cmd_flags, char **batch_filename_ptr)
{
  char *shell, *ifs, *shellflags;
  char **argv;

#ifdef VMS
  char *cptr;
  int argc;

  argc = 0;
  cptr = line;
  for (;;)
    {
      while ((*cptr != 0)
         && (isspace ((unsigned char)*cptr)))
    cptr++;
      if (*cptr == 0)
    break;
      while ((*cptr != 0)
         && (!isspace((unsigned char)*cptr)))
    cptr++;
      argc++;
    }

  argv = xmalloc (argc * sizeof (char *));
  if (argv == 0)
    abort ();

  cptr = line;
  argc = 0;
  for (;;)
    {
      while ((*cptr != 0)
         && (isspace ((unsigned char)*cptr)))
    cptr++;
      if (*cptr == 0)
    break;
      DB (DB_JOBS, ("argv[%d] = [%s]\n", argc, cptr));
      argv[argc++] = cptr;
      while ((*cptr != 0)
         && (!isspace((unsigned char)*cptr)))
    cptr++;
      if (*cptr != 0)
    *cptr++ = 0;
    }
#else
  {
    /* Turn off --warn-undefined-variables while we expand SHELL and IFS.  */
    int save = warn_undefined_variables_flag;
    warn_undefined_variables_flag = 0;

    shell = allocated_variable_expand_for_file ("$(SHELL)", file);
#ifdef WINDOWS32
    /*
     * Convert to forward slashes so that construct_command_argv_internal()
     * is not confused.
     */
    if (shell) {
      char *p = w32ify (shell, 0);
      strcpy (shell, p);
    }
#endif
#ifdef __EMX__
    {
      static const char *unixroot = NULL;
      static const char *last_shell = "";
      static int init = 0;
      if (init == 0)
    {
      unixroot = getenv ("UNIXROOT");
      /* unixroot must be NULL or not empty */
      if (unixroot && unixroot[0] == '\0') unixroot = NULL;
      init = 1;
    }

      /* if we have an unixroot drive and if shell is not default_shell
         (which means it's either cmd.exe or the test has already been
         performed) and if shell is an absolute path without drive letter,
         try whether it exists e.g.: if "/bin/sh" does not exist use
         "$UNIXROOT/bin/sh" instead.  */
      if (unixroot && shell && strcmp (shell, last_shell) != 0
      && (shell[0] == '/' || shell[0] == '\\'))
    {
      /* trying a new shell, check whether it exists */
      size_t size = strlen (shell);
      char *buf = xmalloc (size + 7);
      memcpy (buf, shell, size);
      memcpy (buf + size, ".exe", 5); /* including the trailing '\0' */
          if (access (shell, F_OK) != 0 && access (buf, F_OK) != 0)
        {
          /* try the same for the unixroot drive */
          memmove (buf + 2, buf, size + 5);
          buf[0] = unixroot[0];
          buf[1] = unixroot[1];
          if (access (buf, F_OK) == 0)
        /* we have found a shell! */
        /* free(shell); */
        shell = buf;
          else
        free (buf);
        }
      else
            free (buf);
    }
    }
#endif /* __EMX__ */

    shellflags = allocated_variable_expand_for_file ("$(.SHELLFLAGS)", file);
    ifs = allocated_variable_expand_for_file ("$(IFS)", file);

    warn_undefined_variables_flag = save;
  }

  argv = construct_command_argv_internal (line, restp, shell, shellflags, ifs,
                                          cmd_flags, batch_filename_ptr);

  free (shell);
  free (shellflags);
  free (ifs);
#endif /* !VMS */
  return argv;
}


#if !defined(HAVE_DUP2) && !defined(_AMIGA)
int
dup2 (int old, int new)
{
  int fd;

  (void) close (new);
  fd = dup (old);
  if (fd != new)
    {
      (void) close (fd);
      errno = EMFILE;
      return -1;
    }

  return fd;
}
#endif /* !HAVE_DUP2 && !_AMIGA */

/* On VMS systems, include special VMS functions.  */

#ifdef VMS
#include "vmsjobs.c"
#endif

https://git.savannah.gnu.org/cgit/make.git/tree/main.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/main.c used in: Step 527
/* Argument parsing and main program of GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"
#include "dep.h"
#include "filedef.h"
#include "variable.h"
#include "job.h"
#include "commands.h"
#include "rule.h"
#include "debug.h"
#include "getopt.h"

#include <assert.h>
#ifdef _AMIGA
# include <dos/dos.h>
# include <proto/dos.h>
#endif
#ifdef WINDOWS32
#include <windows.h>
#include <io.h>
#include "pathstuff.h"
#endif
#ifdef __EMX__
# include <sys/types.h>
# include <sys/wait.h>
#endif
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#ifdef _AMIGA
int __stack = 20000; /* Make sure we have 20K of stack space */
#endif

void init_dir (void);
void remote_setup (void);
void remote_cleanup (void);
RETSIGTYPE fatal_error_signal (int sig);

void print_variable_data_base (void);
void print_dir_data_base (void);
void print_rule_data_base (void);
void print_vpath_data_base (void);

void verify_file_data_base (void);

#if defined HAVE_WAITPID || defined HAVE_WAIT3
# define HAVE_WAIT_NOHANG
#endif

#ifndef HAVE_UNISTD_H
int chdir ();
#endif
#ifndef STDC_HEADERS
# ifndef sun            /* Sun has an incorrect decl in a header.  */
void exit (int) __attribute__ ((noreturn));
# endif
double atof ();
#endif

static void clean_jobserver (int status);
static void print_data_base (void);
static void print_version (void);
static void decode_switches (int argc, char **argv, int env);
static void decode_env_switches (char *envar, unsigned int len);
static const char *define_makeflags (int all, int makefile);
static char *quote_for_env (char *out, const char *in);
static void initialize_global_hash_tables (void);



/* The structure that describes an accepted command switch.  */

struct command_switch
  {
    int c;          /* The switch character.  */

    enum            /* Type of the value.  */
      {
    flag,           /* Turn int flag on.  */
    flag_off,       /* Turn int flag off.  */
    string,         /* One string per switch.  */
    filename,       /* A string containing a file name.  */
    positive_int,       /* A positive integer.  */
    floating,       /* A floating-point number (double).  */
    ignore          /* Ignored.  */
      } type;

    void *value_ptr;    /* Pointer to the value-holding variable.  */

    unsigned int env:1;     /* Can come from MAKEFLAGS.  */
    unsigned int toenv:1;   /* Should be put in MAKEFLAGS.  */
    unsigned int no_makefile:1; /* Don't propagate when remaking makefiles.  */

    const void *noarg_value;    /* Pointer to value used if no arg given.  */
    const void *default_value;  /* Pointer to default value.  */

    char *long_name;        /* Long option name.  */
  };

/* True if C is a switch value that corresponds to a short option.  */

#define short_option(c) ((c) <= CHAR_MAX)

/* The structure used to hold the list of strings given
   in command switches of a type that takes string arguments.  */

struct stringlist
  {
    const char **list;  /* Nil-terminated list of strings.  */
    unsigned int idx;   /* Index into above.  */
    unsigned int max;   /* Number of pointers allocated.  */
  };


/* The recognized command switches.  */

/* Nonzero means do not print commands to be executed (-s).  */

int silent_flag;

/* Nonzero means just touch the files
   that would appear to need remaking (-t)  */

int touch_flag;

/* Nonzero means just print what commands would need to be executed,
   don't actually execute them (-n).  */

int just_print_flag;

/* Print debugging info (--debug).  */

static struct stringlist *db_flags;
static int debug_flag = 0;

int db_level = 0;

/* Output level (--verbosity).  */

static struct stringlist *verbosity_flags;

#ifdef WINDOWS32
/* Suspend make in main for a short time to allow debugger to attach */

int suspend_flag = 0;
#endif

/* Environment variables override makefile definitions.  */

int env_overrides = 0;

/* Nonzero means ignore status codes returned by commands
   executed to remake files.  Just treat them all as successful (-i).  */

int ignore_errors_flag = 0;

/* Nonzero means don't remake anything, just print the data base
   that results from reading the makefile (-p).  */

int print_data_base_flag = 0;

/* Nonzero means don't remake anything; just return a nonzero status
   if the specified targets are not up to date (-q).  */

int question_flag = 0;

/* Nonzero means do not use any of the builtin rules (-r) / variables (-R).  */

int no_builtin_rules_flag = 0;
int no_builtin_variables_flag = 0;

/* Nonzero means keep going even if remaking some file fails (-k).  */

int keep_going_flag;
int default_keep_going_flag = 0;

/* Nonzero means check symlink mtimes.  */

int check_symlink_flag = 0;

/* Nonzero means print directory before starting and when done (-w).  */

int print_directory_flag = 0;

/* Nonzero means ignore print_directory_flag and never print the directory.
   This is necessary because print_directory_flag is set implicitly.  */

int inhibit_print_directory_flag = 0;

/* Nonzero means print version information.  */

int print_version_flag = 0;

/* List of makefiles given with -f switches.  */

static struct stringlist *makefiles = 0;

/* Size of the stack when we started.  */

#ifdef SET_STACK_SIZE
struct rlimit stack_limit;
#endif


/* Number of job slots (commands that can be run at once).  */

unsigned int job_slots = 1;
unsigned int default_job_slots = 1;
static unsigned int master_job_slots = 0;

/* Value of job_slots that means no limit.  */

static unsigned int inf_jobs = 0;

/* File descriptors for the jobs pipe.  */

static struct stringlist *jobserver_fds = 0;

int job_fds[2] = { -1, -1 };
int job_rfd = -1;

/* Maximum load average at which multiple jobs will be run.
   Negative values mean unlimited, while zero means limit to
   zero load (which could be useful to start infinite jobs remotely
   but one at a time locally).  */
#ifndef NO_FLOAT
double max_load_average = -1.0;
double default_load_average = -1.0;
#else
int max_load_average = -1;
int default_load_average = -1;
#endif

/* List of directories given with -C switches.  */

static struct stringlist *directories = 0;

/* List of include directories given with -I switches.  */

static struct stringlist *include_directories = 0;

/* List of files given with -o switches.  */

static struct stringlist *old_files = 0;

/* List of files given with -W switches.  */

static struct stringlist *new_files = 0;

/* List of strings to be eval'd.  */
static struct stringlist *eval_strings = 0;

/* If nonzero, we should just print usage and exit.  */

static int print_usage_flag = 0;

/* If nonzero, we should print a warning message
   for each reference to an undefined variable.  */

int warn_undefined_variables_flag;

/* If nonzero, always build all targets, regardless of whether
   they appear out of date or not.  */

static int always_make_set = 0;
int always_make_flag = 0;

/* If nonzero, we're in the "try to rebuild makefiles" phase.  */

int rebuilding_makefiles = 0;

/* Remember the original value of the SHELL variable, from the environment.  */

struct variable shell_var;

/* This character introduces a command: it's the first char on the line.  */

char cmd_prefix = '\t';



/* The usage output.  We write it this way to make life easier for the
   translators, especially those trying to translate to right-to-left
   languages like Hebrew.  */

static const char *const usage[] =
  {
    N_("Options:\n"),
    N_("\
  -b, -m                      Ignored for compatibility.\n"),
    N_("\
  -B, --always-make           Unconditionally make all targets.\n"),
    N_("\
  -C DIRECTORY, --directory=DIRECTORY\n\
                              Change to DIRECTORY before doing anything.\n"),
    N_("\
  -d                          Print lots of debugging information.\n"),
    N_("\
  --debug[=FLAGS]             Print various types of debugging information.\n"),
    N_("\
  -e, --environment-overrides\n\
                              Environment variables override makefiles.\n"),
    N_("\
  --eval=STRING               Evaluate STRING as a makefile statement.\n"),
    N_("\
  -f FILE, --file=FILE, --makefile=FILE\n\
                              Read FILE as a makefile.\n"),
    N_("\
  -h, --help                  Print this message and exit.\n"),
    N_("\
  -i, --ignore-errors         Ignore errors from recipes.\n"),
    N_("\
  -I DIRECTORY, --include-dir=DIRECTORY\n\
                              Search DIRECTORY for included makefiles.\n"),
    N_("\
  -j [N], --jobs[=N]          Allow N jobs at once; infinite jobs with no arg.\n"),
    N_("\
  -k, --keep-going            Keep going when some targets can't be made.\n"),
    N_("\
  -l [N], --load-average[=N], --max-load[=N]\n\
                              Don't start multiple jobs unless load is below N.\n"),
    N_("\
  -L, --check-symlink-times   Use the latest mtime between symlinks and target.\n"),
    N_("\
  -n, --just-print, --dry-run, --recon\n\
                              Don't actually run any recipe; just print them.\n"),
    N_("\
  -o FILE, --old-file=FILE, --assume-old=FILE\n\
                              Consider FILE to be very old and don't remake it.\n"),
    N_("\
  -p, --print-data-base       Print make's internal database.\n"),
    N_("\
  -q, --question              Run no recipe; exit status says if up to date.\n"),
    N_("\
  -r, --no-builtin-rules      Disable the built-in implicit rules.\n"),
    N_("\
  -R, --no-builtin-variables  Disable the built-in variable settings.\n"),
    N_("\
  -s, --silent, --quiet       Don't echo recipes.\n"),
    N_("\
  -S, --no-keep-going, --stop\n\
                              Turns off -k.\n"),
    N_("\
  -t, --touch                 Touch targets instead of remaking them.\n"),
    N_("\
  -v, --version               Print the version number of make and exit.\n"),
    N_("\
  -w, --print-directory       Print the current directory.\n"),
    N_("\
  --no-print-directory        Turn off -w, even if it was turned on implicitly.\n"),
    N_("\
  -W FILE, --what-if=FILE, --new-file=FILE, --assume-new=FILE\n\
                              Consider FILE to be infinitely new.\n"),
    N_("\
  --warn-undefined-variables  Warn when an undefined variable is referenced.\n"),
    NULL
  };

/* The table of command switches.  */

static const struct command_switch switches[] =
  {
    { 'b', ignore, 0, 0, 0, 0, 0, 0, 0 },
    { 'B', flag, &always_make_set, 1, 1, 0, 0, 0, "always-make" },
    { 'C', filename, &directories, 0, 0, 0, 0, 0, "directory" },
    { 'd', flag, &debug_flag, 1, 1, 0, 0, 0, 0 },
    { CHAR_MAX+1, string, &db_flags, 1, 1, 0, "basic", 0, "debug" },
#ifdef WINDOWS32
    { 'D', flag, &suspend_flag, 1, 1, 0, 0, 0, "suspend-for-debug" },
#endif
    { 'e', flag, &env_overrides, 1, 1, 0, 0, 0, "environment-overrides", },
    { 'f', filename, &makefiles, 0, 0, 0, 0, 0, "file" },
    { 'h', flag, &print_usage_flag, 0, 0, 0, 0, 0, "help" },
    { 'i', flag, &ignore_errors_flag, 1, 1, 0, 0, 0, "ignore-errors" },
    { 'I', filename, &include_directories, 1, 1, 0, 0, 0,
      "include-dir" },
    { 'j', positive_int, &job_slots, 1, 1, 0, &inf_jobs, &default_job_slots,
      "jobs" },
    { CHAR_MAX+2, string, &jobserver_fds, 1, 1, 0, 0, 0, "jobserver-fds" },
    { 'k', flag, &keep_going_flag, 1, 1, 0, 0, &default_keep_going_flag,
      "keep-going" },
#ifndef NO_FLOAT
    { 'l', floating, &max_load_average, 1, 1, 0, &default_load_average,
      &default_load_average, "load-average" },
#else
    { 'l', positive_int, &max_load_average, 1, 1, 0, &default_load_average,
      &default_load_average, "load-average" },
#endif
    { 'L', flag, &check_symlink_flag, 1, 1, 0, 0, 0, "check-symlink-times" },
    { 'm', ignore, 0, 0, 0, 0, 0, 0, 0 },
    { 'n', flag, &just_print_flag, 1, 1, 1, 0, 0, "just-print" },
    { 'o', filename, &old_files, 0, 0, 0, 0, 0, "old-file" },
    { 'p', flag, &print_data_base_flag, 1, 1, 0, 0, 0, "print-data-base" },
    { 'q', flag, &question_flag, 1, 1, 1, 0, 0, "question" },
    { 'r', flag, &no_builtin_rules_flag, 1, 1, 0, 0, 0, "no-builtin-rules" },
    { 'R', flag, &no_builtin_variables_flag, 1, 1, 0, 0, 0,
      "no-builtin-variables" },
    { 's', flag, &silent_flag, 1, 1, 0, 0, 0, "silent" },
    { 'S', flag_off, &keep_going_flag, 1, 1, 0, 0, &default_keep_going_flag,
      "no-keep-going" },
    { 't', flag, &touch_flag, 1, 1, 1, 0, 0, "touch" },
    { 'v', flag, &print_version_flag, 1, 1, 0, 0, 0, "version" },
    { CHAR_MAX+3, string, &verbosity_flags, 1, 1, 0, 0, 0,
      "verbosity" },
    { 'w', flag, &print_directory_flag, 1, 1, 0, 0, 0, "print-directory" },
    { CHAR_MAX+4, flag, &inhibit_print_directory_flag, 1, 1, 0, 0, 0,
      "no-print-directory" },
    { 'W', filename, &new_files, 0, 0, 0, 0, 0, "what-if" },
    { CHAR_MAX+5, flag, &warn_undefined_variables_flag, 1, 1, 0, 0, 0,
      "warn-undefined-variables" },
    { CHAR_MAX+6, string, &eval_strings, 1, 0, 0, 0, 0, "eval" },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
  };

/* Secondary long names for options.  */

static struct option long_option_aliases[] =
  {
    { "quiet",      no_argument,        0, 's' },
    { "stop",       no_argument,        0, 'S' },
    { "new-file",   required_argument,  0, 'W' },
    { "assume-new", required_argument,  0, 'W' },
    { "assume-old", required_argument,  0, 'o' },
    { "max-load",   optional_argument,  0, 'l' },
    { "dry-run",    no_argument,        0, 'n' },
    { "recon",      no_argument,        0, 'n' },
    { "makefile",   required_argument,  0, 'f' },
  };

/* List of goal targets.  */

static struct dep *goals, *lastgoal;

/* List of variables which were defined on the command line
   (or, equivalently, in MAKEFLAGS).  */

struct command_variable
  {
    struct command_variable *next;
    struct variable *variable;
  };
static struct command_variable *command_variables;


/* The name we were invoked with.  */

char *program;

/* Our current directory before processing any -C options.  */

char *directory_before_chdir;

/* Our current directory after processing all -C options.  */

char *starting_directory;

/* Value of the MAKELEVEL variable at startup (or 0).  */

unsigned int makelevel;

/* Pointer to the value of the .DEFAULT_GOAL special variable.
   The value will be the name of the goal to remake if the command line
   does not override it.  It can be set by the makefile, or else it's
   the first target defined in the makefile whose name does not start
   with '.'.  */

struct variable * default_goal_var;

/* Pointer to structure for the file .DEFAULT
   whose commands are used for any file that has none of its own.
   This is zero if the makefiles do not define .DEFAULT.  */

struct file *default_file;

/* Nonzero if we have seen the magic `.POSIX' target.
   This turns on pedantic compliance with POSIX.2.  */

int posix_pedantic;

/* Nonzero if we have seen the '.SECONDEXPANSION' target.
   This turns on secondary expansion of prerequisites.  */

int second_expansion;

/* Nonzero if we have seen the '.ONESHELL' target.
   This causes the entire recipe to be handed to SHELL
   as a single string, potentially containing newlines.  */

int one_shell;

/* Nonzero if we have seen the `.NOTPARALLEL' target.
   This turns off parallel builds for this invocation of make.  */

int not_parallel;

/* Nonzero if some rule detected clock skew; we keep track so (a) we only
   print one warning about it during the run, and (b) we can print a final
   warning at the end of the run. */

int clock_skew_detected;


/* Mask of signals that are being caught with fatal_error_signal.  */

#ifdef  POSIX
sigset_t fatal_signal_set;
#else
# ifdef HAVE_SIGSETMASK
int fatal_signal_mask;
# endif
#endif

#if !HAVE_DECL_BSD_SIGNAL && !defined bsd_signal
# if !defined HAVE_SIGACTION
#  define bsd_signal signal
# else
typedef RETSIGTYPE (*bsd_signal_ret_t) (int);

static bsd_signal_ret_t
bsd_signal (int sig, bsd_signal_ret_t func)
{
  struct sigaction act, oact;
  act.sa_handler = func;
  act.sa_flags = SA_RESTART;
  sigemptyset (&act.sa_mask);
  sigaddset (&act.sa_mask, sig);
  if (sigaction (sig, &act, &oact) != 0)
    return SIG_ERR;
  return oact.sa_handler;
}
# endif
#endif

static void
initialize_global_hash_tables (void)
{
  init_hash_global_variable_set ();
  strcache_init ();
  init_hash_files ();
  hash_init_directories ();
  hash_init_function_table ();
}

static const char *
expand_command_line_file (char *name)
{
  const char *cp;
  char *expanded = 0;

  if (name[0] == '\0')
    fatal (NILF, _("empty string invalid as file name"));

  if (name[0] == '~')
    {
      expanded = tilde_expand (name);
      if (expanded != 0)
    name = expanded;
    }

  /* This is also done in parse_file_seq, so this is redundant
     for names read from makefiles.  It is here for names passed
     on the command line.  */
  while (name[0] == '.' && name[1] == '/' && name[2] != '\0')
    {
      name += 2;
      while (*name == '/')
    /* Skip following slashes: ".//foo" is "foo", not "/foo".  */
    ++name;
    }

  if (*name == '\0')
    {
      /* It was all slashes!  Move back to the dot and truncate
     it after the first slash, so it becomes just "./".  */
      do
    --name;
      while (name[0] != '.');
      name[2] = '\0';
    }

  cp = strcache_add (name);

  if (expanded)
    free (expanded);

  return cp;
}

/* Toggle -d on receipt of SIGUSR1.  */

#ifdef SIGUSR1
static RETSIGTYPE
debug_signal_handler (int sig UNUSED)
{
  db_level = db_level ? DB_NONE : DB_BASIC;
}
#endif

static void
decode_debug_flags (void)
{
  const char **pp;

  if (debug_flag)
    db_level = DB_ALL;

  if (!db_flags)
    return;

  for (pp=db_flags->list; *pp; ++pp)
    {
      const char *p = *pp;

      while (1)
        {
          switch (tolower (p[0]))
            {
            case 'a':
              db_level |= DB_ALL;
              break;
            case 'b':
              db_level |= DB_BASIC;
              break;
            case 'i':
              db_level |= DB_BASIC | DB_IMPLICIT;
              break;
            case 'j':
              db_level |= DB_JOBS;
              break;
            case 'm':
              db_level |= DB_BASIC | DB_MAKEFILES;
              break;
            case 'v':
              db_level |= DB_BASIC | DB_VERBOSE;
              break;
            default:
              fatal (NILF, _("unknown debug level specification `%s'"), p);
            }

          while (*(++p) != '\0')
            if (*p == ',' || *p == ' ')
              break;

          if (*p == '\0')
            break;

          ++p;
        }
    }
}

#ifdef WINDOWS32
/*
 * HANDLE runtime exceptions by avoiding a requestor on the GUI. Capture
 * exception and print it to stderr instead.
 *
 * If ! DB_VERBOSE, just print a simple message and exit.
 * If DB_VERBOSE, print a more verbose message.
 * If compiled for DEBUG, let exception pass through to GUI so that
 *   debuggers can attach.
 */
LONG WINAPI
handle_runtime_exceptions( struct _EXCEPTION_POINTERS *exinfo )
{
  PEXCEPTION_RECORD exrec = exinfo->ExceptionRecord;
  LPSTR cmdline = GetCommandLine();
  LPSTR prg = strtok(cmdline, " ");
  CHAR errmsg[1024];
#ifdef USE_EVENT_LOG
  HANDLE hEventSource;
  LPTSTR lpszStrings[1];
#endif

  if (! ISDB (DB_VERBOSE))
    {
      sprintf(errmsg,
              _("%s: Interrupt/Exception caught (code = 0x%lx, addr = 0x%p)\n"),
              prg, exrec->ExceptionCode, exrec->ExceptionAddress);
      fprintf(stderr, errmsg);
      exit(255);
    }

  sprintf(errmsg,
          _("\nUnhandled exception filter called from program %s\nExceptionCode = %lx\nExceptionFlags = %lx\nExceptionAddress = 0x%p\n"),
          prg, exrec->ExceptionCode, exrec->ExceptionFlags,
          exrec->ExceptionAddress);

  if (exrec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION
      && exrec->NumberParameters >= 2)
    sprintf(&errmsg[strlen(errmsg)],
            (exrec->ExceptionInformation[0]
             ? _("Access violation: write operation at address 0x%p\n")
             : _("Access violation: read operation at address 0x%p\n")),
            (PVOID)exrec->ExceptionInformation[1]);

  /* turn this on if we want to put stuff in the event log too */
#ifdef USE_EVENT_LOG
  hEventSource = RegisterEventSource(NULL, "GNU Make");
  lpszStrings[0] = errmsg;

  if (hEventSource != NULL)
    {
      ReportEvent(hEventSource,         /* handle of event source */
                  EVENTLOG_ERROR_TYPE,  /* event type */
                  0,                    /* event category */
                  0,                    /* event ID */
                  NULL,                 /* current user's SID */
                  1,                    /* strings in lpszStrings */
                  0,                    /* no bytes of raw data */
                  lpszStrings,          /* array of error strings */
                  NULL);                /* no raw data */

      (VOID) DeregisterEventSource(hEventSource);
    }
#endif

  /* Write the error to stderr too */
  fprintf(stderr, errmsg);

#ifdef DEBUG
  return EXCEPTION_CONTINUE_SEARCH;
#else
  exit(255);
  return (255); /* not reached */
#endif
}

/*
 * On WIN32 systems we don't have the luxury of a /bin directory that
 * is mapped globally to every drive mounted to the system. Since make could
 * be invoked from any drive, and we don't want to propogate /bin/sh
 * to every single drive. Allow ourselves a chance to search for
 * a value for default shell here (if the default path does not exist).
 */

int
find_and_set_default_shell (const char *token)
{
  int sh_found = 0;
  char *atoken = 0;
  char *search_token;
  char *tokend;
  PATH_VAR(sh_path);
  extern char *default_shell;

  if (!token)
    search_token = default_shell;
  else
    atoken = search_token = xstrdup (token);

  /* If the user explicitly requests the DOS cmd shell, obey that request.
     However, make sure that's what they really want by requiring the value
     of SHELL either equal, or have a final path element of, "cmd" or
     "cmd.exe" case-insensitive.  */
  tokend = search_token + strlen (search_token) - 3;
  if (((tokend == search_token
        || (tokend > search_token
            && (tokend[-1] == '/' || tokend[-1] == '\\')))
       && !strcasecmp (tokend, "cmd"))
      || ((tokend - 4 == search_token
           || (tokend - 4 > search_token
               && (tokend[-5] == '/' || tokend[-5] == '\\')))
          && !strcasecmp (tokend - 4, "cmd.exe"))) {
    batch_mode_shell = 1;
    unixy_shell = 0;
    sprintf (sh_path, "%s", search_token);
    default_shell = xstrdup (w32ify (sh_path, 0));
    DB (DB_VERBOSE, (_("find_and_set_shell() setting default_shell = %s\n"),
                     default_shell));
    sh_found = 1;
  } else if (!no_default_sh_exe &&
             (token == NULL || !strcmp (search_token, default_shell))) {
    /* no new information, path already set or known */
    sh_found = 1;
  } else if (file_exists_p (search_token)) {
    /* search token path was found */
    sprintf (sh_path, "%s", search_token);
    default_shell = xstrdup (w32ify (sh_path, 0));
    DB (DB_VERBOSE, (_("find_and_set_shell() setting default_shell = %s\n"),
                     default_shell));
    sh_found = 1;
  } else {
    char *p;
    struct variable *v = lookup_variable (STRING_SIZE_TUPLE ("PATH"));

    /* Search Path for shell */
    if (v && v->value) {
      char *ep;

      p  = v->value;
      ep = strchr (p, PATH_SEPARATOR_CHAR);

      while (ep && *ep) {
        *ep = '\0';

        if (dir_file_exists_p (p, search_token)) {
          sprintf (sh_path, "%s/%s", p, search_token);
          default_shell = xstrdup (w32ify (sh_path, 0));
          sh_found = 1;
          *ep = PATH_SEPARATOR_CHAR;

          /* terminate loop */
          p += strlen (p);
        } else {
          *ep = PATH_SEPARATOR_CHAR;
           p = ++ep;
        }

        ep = strchr (p, PATH_SEPARATOR_CHAR);
      }

      /* be sure to check last element of Path */
      if (p && *p && dir_file_exists_p (p, search_token)) {
          sprintf (sh_path, "%s/%s", p, search_token);
          default_shell = xstrdup (w32ify (sh_path, 0));
          sh_found = 1;
      }

      if (sh_found)
        DB (DB_VERBOSE,
            (_("find_and_set_shell() path search set default_shell = %s\n"),
             default_shell));
    }
  }

  /* naive test */
  if (!unixy_shell && sh_found &&
      (strstr (default_shell, "sh") || strstr (default_shell, "SH"))) {
    unixy_shell = 1;
    batch_mode_shell = 0;
  }

#ifdef BATCH_MODE_ONLY_SHELL
  batch_mode_shell = 1;
#endif

  if (atoken)
    free (atoken);

  return (sh_found);
}
#endif  /* WINDOWS32 */

#ifdef __MSDOS__
static void
msdos_return_to_initial_directory (void)
{
  if (directory_before_chdir)
    chdir (directory_before_chdir);
}
#endif  /* __MSDOS__ */

char *mktemp (char *template);
int mkstemp (char *template);

FILE *
open_tmpfile(char **name, const char *template)
{
#ifdef HAVE_FDOPEN
  int fd;
#endif

#if defined HAVE_MKSTEMP || defined HAVE_MKTEMP
# define TEMPLATE_LEN   strlen (template)
#else
# define TEMPLATE_LEN   L_tmpnam
#endif
  *name = xmalloc (TEMPLATE_LEN + 1);
  strcpy (*name, template);

#if defined HAVE_MKSTEMP && defined HAVE_FDOPEN
  /* It's safest to use mkstemp(), if we can.  */
  fd = mkstemp (*name);
  if (fd == -1)
    return 0;
  return fdopen (fd, "w");
#else
# ifdef HAVE_MKTEMP
  (void) mktemp (*name);
# else
  (void) tmpnam (*name);
# endif

# ifdef HAVE_FDOPEN
  /* Can't use mkstemp(), but guard against a race condition.  */
  fd = open (*name, O_CREAT|O_EXCL|O_WRONLY, 0600);
  if (fd == -1)
    return 0;
  return fdopen (fd, "w");
# else
  /* Not secure, but what can we do?  */
  return fopen (*name, "w");
# endif
#endif
}


#ifdef _AMIGA
int
main (int argc, char **argv)
#else
int
main (int argc, char **argv, char **envp)
#endif
{
  static char *stdin_nm = 0;
  int makefile_status = MAKE_SUCCESS;
  struct dep *read_makefiles;
  PATH_VAR (current_directory);
  unsigned int restarts = 0;
#ifdef WINDOWS32
  char *unix_path = NULL;
  char *windows32_path = NULL;

  SetUnhandledExceptionFilter(handle_runtime_exceptions);

  /* start off assuming we have no shell */
  unixy_shell = 0;
  no_default_sh_exe = 1;
#endif

#ifdef SET_STACK_SIZE
 /* Get rid of any avoidable limit on stack size.  */
  {
    struct rlimit rlim;

    /* Set the stack limit huge so that alloca does not fail.  */
    if (getrlimit (RLIMIT_STACK, &rlim) == 0
        && rlim.rlim_cur > 0 && rlim.rlim_cur < rlim.rlim_max)
      {
        stack_limit = rlim;
        rlim.rlim_cur = rlim.rlim_max;
        setrlimit (RLIMIT_STACK, &rlim);
      }
    else
      stack_limit.rlim_cur = 0;
  }
#endif

#ifdef HAVE_ATEXIT
  atexit (close_stdout);
#endif

  /* Needed for OS/2 */
  initialize_main(&argc, &argv);

  reading_file = 0;

#if defined (__MSDOS__) && !defined (_POSIX_SOURCE)
  /* Request the most powerful version of `system', to
     make up for the dumb default shell.  */
  __system_flags = (__system_redirect
            | __system_use_shell
            | __system_allow_multiple_cmds
            | __system_allow_long_cmds
            | __system_handle_null_commands
            | __system_emulate_chdir);

#endif

  /* Set up gettext/internationalization support.  */
  setlocale (LC_ALL, "");
  /* The cast to void shuts up compiler warnings on systems that
     disable NLS.  */
  (void)bindtextdomain (PACKAGE, LOCALEDIR);
  (void)textdomain (PACKAGE);

#ifdef  POSIX
  sigemptyset (&fatal_signal_set);
#define ADD_SIG(sig)    sigaddset (&fatal_signal_set, sig)
#else
#ifdef  HAVE_SIGSETMASK
  fatal_signal_mask = 0;
#define ADD_SIG(sig)    fatal_signal_mask |= sigmask (sig)
#else
#define ADD_SIG(sig)    (void)sig      /* Needed to avoid warnings in MSVC.  */
#endif
#endif

#define FATAL_SIG(sig)                                \
  if (bsd_signal (sig, fatal_error_signal) == SIG_IGN)                \
    bsd_signal (sig, SIG_IGN);                            \
  else                                        \
    ADD_SIG (sig);

#ifdef SIGHUP
  FATAL_SIG (SIGHUP);
#endif
#ifdef SIGQUIT
  FATAL_SIG (SIGQUIT);
#endif
  FATAL_SIG (SIGINT);
  FATAL_SIG (SIGTERM);

#ifdef __MSDOS__
  /* Windows 9X delivers FP exceptions in child programs to their
     parent!  We don't want Make to die when a child divides by zero,
     so we work around that lossage by catching SIGFPE.  */
  FATAL_SIG (SIGFPE);
#endif

#ifdef  SIGDANGER
  FATAL_SIG (SIGDANGER);
#endif
#ifdef SIGXCPU
  FATAL_SIG (SIGXCPU);
#endif
#ifdef SIGXFSZ
  FATAL_SIG (SIGXFSZ);
#endif

#undef  FATAL_SIG

  /* Do not ignore the child-death signal.  This must be done before
     any children could possibly be created; otherwise, the wait
     functions won't work on systems with the SVR4 ECHILD brain
     damage, if our invoker is ignoring this signal.  */

#ifdef HAVE_WAIT_NOHANG
# if defined SIGCHLD
  (void) bsd_signal (SIGCHLD, SIG_DFL);
# endif
# if defined SIGCLD && SIGCLD != SIGCHLD
  (void) bsd_signal (SIGCLD, SIG_DFL);
# endif
#endif

  /* Make sure stdout is line-buffered.  */

#ifdef HAVE_SETVBUF
# ifdef SETVBUF_REVERSED
  setvbuf (stdout, _IOLBF, xmalloc (BUFSIZ), BUFSIZ);
# else  /* setvbuf not reversed.  */
  /* Some buggy systems lose if we pass 0 instead of allocating ourselves.  */
  setvbuf (stdout, 0, _IOLBF, BUFSIZ);
# endif /* setvbuf reversed.  */
#elif HAVE_SETLINEBUF
  setlinebuf (stdout);
#endif  /* setlinebuf missing.  */

  /* Figure out where this program lives.  */

  if (argv[0] == 0)
    argv[0] = "";
  if (argv[0][0] == '\0')
    program = "make";
  else
    {
#ifdef VMS
      program = strrchr (argv[0], ']');
#else
      program = strrchr (argv[0], '/');
#endif
#if defined(__MSDOS__) || defined(__EMX__)
      if (program == 0)
    program = strrchr (argv[0], '\\');
      else
    {
      /* Some weird environments might pass us argv[0] with
         both kinds of slashes; we must find the rightmost.  */
      char *p = strrchr (argv[0], '\\');
      if (p && p > program)
        program = p;
    }
      if (program == 0 && argv[0][1] == ':')
    program = argv[0] + 1;
#endif
#ifdef WINDOWS32
      if (program == 0)
        {
          /* Extract program from full path */
          int argv0_len;
          program = strrchr (argv[0], '\\');
          if (program)
            {
              argv0_len = strlen(program);
              if (argv0_len > 4 && streq (&program[argv0_len - 4], ".exe"))
                /* Remove .exe extension */
                program[argv0_len - 4] = '\0';
            }
        }
#endif
      if (program == 0)
    program = argv[0];
      else
    ++program;
    }

  /* Set up to access user data (files).  */
  user_access ();

  initialize_global_hash_tables ();

  /* Figure out where we are.  */

#ifdef WINDOWS32
  if (getcwd_fs (current_directory, GET_PATH_MAX) == 0)
#else
  if (getcwd (current_directory, GET_PATH_MAX) == 0)
#endif
    {
#ifdef  HAVE_GETCWD
      perror_with_name ("getcwd", "");
#else
      error (NILF, "getwd: %s", current_directory);
#endif
      current_directory[0] = '\0';
      directory_before_chdir = 0;
    }
  else
    directory_before_chdir = xstrdup (current_directory);
#ifdef  __MSDOS__
  /* Make sure we will return to the initial directory, come what may.  */
  atexit (msdos_return_to_initial_directory);
#endif

  /* Initialize the special variables.  */
  define_variable_cname (".VARIABLES", "", o_default, 0)->special = 1;
  /* define_variable_cname (".TARGETS", "", o_default, 0)->special = 1; */
  define_variable_cname (".RECIPEPREFIX", "", o_default, 0)->special = 1;
  define_variable_cname (".SHELLFLAGS", "-c", o_default, 0);

  /* Set up .FEATURES
     We must do this in multiple calls because define_variable_cname() is
     a macro and some compilers (MSVC) don't like conditionals in macros.  */
  {
    const char *features = "target-specific order-only second-expansion"
                           " else-if shortest-stem undefine"
#ifndef NO_ARCHIVES
                           " archives"
#endif
#ifdef MAKE_JOBSERVER
                           " jobserver"
#endif
#ifdef MAKE_SYMLINKS
                           " check-symlink"
#endif
                           ;

    define_variable_cname (".FEATURES", features, o_default, 0);
  }

  /* Read in variables from the environment.  It is important that this be
     done before $(MAKE) is figured out so its definitions will not be
     from the environment.  */

#ifndef _AMIGA
  {
    unsigned int i;

    for (i = 0; envp[i] != 0; ++i)
      {
        int do_not_define = 0;
        char *ep = envp[i];

        while (*ep != '\0' && *ep != '=')
          ++ep;
#ifdef WINDOWS32
        if (!unix_path && strneq(envp[i], "PATH=", 5))
          unix_path = ep+1;
        else if (!strnicmp(envp[i], "Path=", 5)) {
          do_not_define = 1; /* it gets defined after loop exits */
          if (!windows32_path)
            windows32_path = ep+1;
        }
#endif
        /* The result of pointer arithmetic is cast to unsigned int for
           machines where ptrdiff_t is a different size that doesn't widen
           the same.  */
        if (!do_not_define)
          {
            struct variable *v;

            v = define_variable (envp[i], (unsigned int) (ep - envp[i]),
                                 ep + 1, o_env, 1);
            /* Force exportation of every variable culled from the
               environment.  We used to rely on target_environment's
               v_default code to do this.  But that does not work for the
               case where an environment variable is redefined in a makefile
               with `override'; it should then still be exported, because it
               was originally in the environment.  */
            v->export = v_export;

            /* Another wrinkle is that POSIX says the value of SHELL set in
               the makefile won't change the value of SHELL given to
               subprocesses.  */
            if (streq (v->name, "SHELL"))
              {
#ifndef __MSDOS__
                v->export = v_noexport;
#endif
                shell_var.name = "SHELL";
                shell_var.length = 5;
                shell_var.value = xstrdup (ep + 1);
              }

            /* If MAKE_RESTARTS is set, remember it but don't export it.  */
            if (streq (v->name, "MAKE_RESTARTS"))
              {
                v->export = v_noexport;
                restarts = (unsigned int) atoi (ep + 1);
              }
          }
      }
  }
#ifdef WINDOWS32
    /* If we didn't find a correctly spelled PATH we define PATH as
     * either the first mispelled value or an empty string
     */
    if (!unix_path)
      define_variable_cname ("PATH", windows32_path ? windows32_path : "",
                             o_env, 1)->export = v_export;
#endif
#else /* For Amiga, read the ENV: device, ignoring all dirs */
    {
    BPTR env, file, old;
    char buffer[1024];
    int len;
    __aligned struct FileInfoBlock fib;

    env = Lock ("ENV:", ACCESS_READ);
    if (env)
    {
        old = CurrentDir (DupLock(env));
        Examine (env, &fib);

        while (ExNext (env, &fib))
        {
        if (fib.fib_DirEntryType < 0) /* File */
        {
            /* Define an empty variable. It will be filled in
            variable_lookup(). Makes startup quite a bit
            faster. */
            define_variable (fib.fib_FileName,
                strlen (fib.fib_FileName),
            "", o_env, 1)->export = v_export;
        }
        }
        UnLock (env);
        UnLock(CurrentDir(old));
    }
    }
#endif

  /* Decode the switches.  */

  decode_env_switches (STRING_SIZE_TUPLE ("MAKEFLAGS"));
#if 0
  /* People write things like:
        MFLAGS="CC=gcc -pipe" "CFLAGS=-g"
     and we set the -p, -i and -e switches.  Doesn't seem quite right.  */
  decode_env_switches (STRING_SIZE_TUPLE ("MFLAGS"));
#endif

  decode_switches (argc, argv, 0);

#ifdef WINDOWS32
  if (suspend_flag) {
        fprintf(stderr, "%s (pid = %ld)\n", argv[0], GetCurrentProcessId());
        fprintf(stderr, _("%s is suspending for 30 seconds..."), argv[0]);
        Sleep(30 * 1000);
        fprintf(stderr, _("done sleep(30). Continuing.\n"));
  }
#endif

  decode_debug_flags ();

  /* Set always_make_flag if -B was given and we've not restarted already.  */
  always_make_flag = always_make_set && (restarts == 0);

  /* Print version information.  */
  if (print_version_flag || print_data_base_flag || db_level)
    {
      print_version ();

      /* `make --version' is supposed to just print the version and exit.  */
      if (print_version_flag)
        die (0);
    }

#ifndef VMS
  /* Set the "MAKE_COMMAND" variable to the name we were invoked with.
     (If it is a relative pathname with a slash, prepend our directory name
     so the result will run the same program regardless of the current dir.
     If it is a name with no slash, we can only hope that PATH did not
     find it in the current directory.)  */
#ifdef WINDOWS32
  /*
   * Convert from backslashes to forward slashes for
   * programs like sh which don't like them. Shouldn't
   * matter if the path is one way or the other for
   * CreateProcess().
   */
  if (strpbrk(argv[0], "/:\\") ||
      strstr(argv[0], "..") ||
      strneq(argv[0], "//", 2))
    argv[0] = xstrdup(w32ify(argv[0],1));
#else /* WINDOWS32 */
#if defined (__MSDOS__) || defined (__EMX__)
  if (strchr (argv[0], '\\'))
    {
      char *p;

      argv[0] = xstrdup (argv[0]);
      for (p = argv[0]; *p; p++)
    if (*p == '\\')
      *p = '/';
    }
  /* If argv[0] is not in absolute form, prepend the current
     directory.  This can happen when Make is invoked by another DJGPP
     program that uses a non-absolute name.  */
  if (current_directory[0] != '\0'
      && argv[0] != 0
      && (argv[0][0] != '/' && (argv[0][0] == '\0' || argv[0][1] != ':'))
# ifdef __EMX__
      /* do not prepend cwd if argv[0] contains no '/', e.g. "make" */
      && (strchr (argv[0], '/') != 0 || strchr (argv[0], '\\') != 0)
# endif
      )
    argv[0] = xstrdup (concat (3, current_directory, "/", argv[0]));
#else  /* !__MSDOS__ */
  if (current_directory[0] != '\0'
      && argv[0] != 0 && argv[0][0] != '/' && strchr (argv[0], '/') != 0
#ifdef HAVE_DOS_PATHS
      && (argv[0][0] != '\\' && (!argv[0][0] || argv[0][1] != ':'))
      && strchr (argv[0], '\\') != 0
#endif
      )
    argv[0] = xstrdup (concat (3, current_directory, "/", argv[0]));
#endif /* !__MSDOS__ */
#endif /* WINDOWS32 */
#endif

  /* The extra indirection through $(MAKE_COMMAND) is done
     for hysterical raisins.  */
  define_variable_cname ("MAKE_COMMAND", argv[0], o_default, 0);
  define_variable_cname ("MAKE", "$(MAKE_COMMAND)", o_default, 1);

  if (command_variables != 0)
    {
      struct command_variable *cv;
      struct variable *v;
      unsigned int len = 0;
      char *value, *p;

      /* Figure out how much space will be taken up by the command-line
     variable definitions.  */
      for (cv = command_variables; cv != 0; cv = cv->next)
    {
      v = cv->variable;
      len += 2 * strlen (v->name);
      if (! v->recursive)
        ++len;
      ++len;
      len += 2 * strlen (v->value);
      ++len;
    }

      /* Now allocate a buffer big enough and fill it.  */
      p = value = alloca (len);
      for (cv = command_variables; cv != 0; cv = cv->next)
    {
      v = cv->variable;
      p = quote_for_env (p, v->name);
      if (! v->recursive)
        *p++ = ':';
      *p++ = '=';
      p = quote_for_env (p, v->value);
      *p++ = ' ';
    }
      p[-1] = '\0';     /* Kill the final space and terminate.  */

      /* Define an unchangeable variable with a name that no POSIX.2
     makefile could validly use for its own variable.  */
      define_variable_cname ("-*-command-variables-*-", value, o_automatic, 0);

      /* Define the variable; this will not override any user definition.
         Normally a reference to this variable is written into the value of
         MAKEFLAGS, allowing the user to override this value to affect the
         exported value of MAKEFLAGS.  In POSIX-pedantic mode, we cannot
         allow the user's setting of MAKEOVERRIDES to affect MAKEFLAGS, so
         a reference to this hidden variable is written instead. */
      define_variable_cname ("MAKEOVERRIDES", "${-*-command-variables-*-}",
                             o_env, 1);
    }

  /* If there were -C flags, move ourselves about.  */
  if (directories != 0)
    {
      unsigned int i;
      for (i = 0; directories->list[i] != 0; ++i)
        {
          const char *dir = directories->list[i];
#ifdef WINDOWS32
          /* WINDOWS32 chdir() doesn't work if the directory has a trailing '/'
             But allow -C/ just in case someone wants that.  */
          {
            char *p = (char *)dir + strlen (dir) - 1;
            while (p > dir && (p[0] == '/' || p[0] == '\\'))
              --p;
            p[1] = '\0';
          }
#endif
          if (chdir (dir) < 0)
            pfatal_with_name (dir);
        }
    }

#ifdef WINDOWS32
  /*
   * THIS BLOCK OF CODE MUST COME AFTER chdir() CALL ABOVE IN ORDER
   * TO NOT CONFUSE THE DEPENDENCY CHECKING CODE IN implicit.c.
   *
   * The functions in dir.c can incorrectly cache information for "."
   * before we have changed directory and this can cause file
   * lookups to fail because the current directory (.) was pointing
   * at the wrong place when it was first evaluated.
   */
   no_default_sh_exe = !find_and_set_default_shell(NULL);

#endif /* WINDOWS32 */
  /* Figure out the level of recursion.  */
  {
    struct variable *v = lookup_variable (STRING_SIZE_TUPLE (MAKELEVEL_NAME));
    if (v != 0 && v->value[0] != '\0' && v->value[0] != '-')
      makelevel = (unsigned int) atoi (v->value);
    else
      makelevel = 0;
  }

  /* Except under -s, always do -w in sub-makes and under -C.  */
  if (!silent_flag && (directories != 0 || makelevel > 0))
    print_directory_flag = 1;

  /* Let the user disable that with --no-print-directory.  */
  if (inhibit_print_directory_flag)
    print_directory_flag = 0;

  /* If -R was given, set -r too (doesn't make sense otherwise!)  */
  if (no_builtin_variables_flag)
    no_builtin_rules_flag = 1;

  /* Construct the list of include directories to search.  */

  construct_include_path (include_directories == 0
                          ? 0 : include_directories->list);

  /* Figure out where we are now, after chdir'ing.  */
  if (directories == 0)
    /* We didn't move, so we're still in the same place.  */
    starting_directory = current_directory;
  else
    {
#ifdef WINDOWS32
      if (getcwd_fs (current_directory, GET_PATH_MAX) == 0)
#else
      if (getcwd (current_directory, GET_PATH_MAX) == 0)
#endif
    {
#ifdef  HAVE_GETCWD
      perror_with_name ("getcwd", "");
#else
      error (NILF, "getwd: %s", current_directory);
#endif
      starting_directory = 0;
    }
      else
    starting_directory = current_directory;
    }

  define_variable_cname ("CURDIR", current_directory, o_file, 0);

  /* Read any stdin makefiles into temporary files.  */

  if (makefiles != 0)
    {
      unsigned int i;
      for (i = 0; i < makefiles->idx; ++i)
    if (makefiles->list[i][0] == '-' && makefiles->list[i][1] == '\0')
      {
        /* This makefile is standard input.  Since we may re-exec
           and thus re-read the makefiles, we read standard input
           into a temporary file and read from that.  */
        FILE *outfile;
            char *template, *tmpdir;

            if (stdin_nm)
              fatal (NILF, _("Makefile from standard input specified twice."));

#ifdef VMS
# define DEFAULT_TMPDIR     "sys$scratch:"
#else
# ifdef P_tmpdir
#  define DEFAULT_TMPDIR    P_tmpdir
# else
#  define DEFAULT_TMPDIR    "/tmp"
# endif
#endif
#define DEFAULT_TMPFILE     "GmXXXXXX"

        if (((tmpdir = getenv ("TMPDIR")) == NULL || *tmpdir == '\0')
#if defined (__MSDOS__) || defined (WINDOWS32) || defined (__EMX__)
                /* These are also used commonly on these platforms.  */
                && ((tmpdir = getenv ("TEMP")) == NULL || *tmpdir == '\0')
                && ((tmpdir = getenv ("TMP")) == NULL || *tmpdir == '\0')
#endif
               )
          tmpdir = DEFAULT_TMPDIR;

            template = alloca (strlen (tmpdir) + sizeof (DEFAULT_TMPFILE) + 1);
        strcpy (template, tmpdir);

#ifdef HAVE_DOS_PATHS
        if (strchr ("/\\", template[strlen (template) - 1]) == NULL)
          strcat (template, "/");
#else
# ifndef VMS
        if (template[strlen (template) - 1] != '/')
          strcat (template, "/");
# endif /* !VMS */
#endif /* !HAVE_DOS_PATHS */

        strcat (template, DEFAULT_TMPFILE);
        outfile = open_tmpfile (&stdin_nm, template);
        if (outfile == 0)
          pfatal_with_name (_("fopen (temporary file)"));
        while (!feof (stdin) && ! ferror (stdin))
          {
        char buf[2048];
        unsigned int n = fread (buf, 1, sizeof (buf), stdin);
        if (n > 0 && fwrite (buf, 1, n, outfile) != n)
          pfatal_with_name (_("fwrite (temporary file)"));
          }
        fclose (outfile);

        /* Replace the name that read_all_makefiles will
           see with the name of the temporary file.  */
            makefiles->list[i] = strcache_add (stdin_nm);

        /* Make sure the temporary file will not be remade.  */
            {
              struct file *f = enter_file (strcache_add (stdin_nm));
              f->updated = 1;
              f->update_status = 0;
              f->command_state = cs_finished;
              /* Can't be intermediate, or it'll be removed too early for
                 make re-exec.  */
              f->intermediate = 0;
              f->dontcare = 0;
            }
      }
    }

#ifndef __EMX__ /* Don't use a SIGCHLD handler for OS/2 */
#if defined(MAKE_JOBSERVER) || !defined(HAVE_WAIT_NOHANG)
  /* Set up to handle children dying.  This must be done before
     reading in the makefiles so that `shell' function calls will work.

     If we don't have a hanging wait we have to fall back to old, broken
     functionality here and rely on the signal handler and counting
     children.

     If we're using the jobs pipe we need a signal handler so that
     SIGCHLD is not ignored; we need it to interrupt the read(2) of the
     jobserver pipe in job.c if we're waiting for a token.

     If none of these are true, we don't need a signal handler at all.  */
  {
    RETSIGTYPE child_handler (int sig);
# if defined SIGCHLD
    bsd_signal (SIGCHLD, child_handler);
# endif
# if defined SIGCLD && SIGCLD != SIGCHLD
    bsd_signal (SIGCLD, child_handler);
# endif
  }
#endif
#endif

  /* Let the user send us SIGUSR1 to toggle the -d flag during the run.  */
#ifdef SIGUSR1
  bsd_signal (SIGUSR1, debug_signal_handler);
#endif

  /* Define the initial list of suffixes for old-style rules.  */

  set_default_suffixes ();

  /* Define the file rules for the built-in suffix rules.  These will later
     be converted into pattern rules.  We used to do this in
     install_default_implicit_rules, but since that happens after reading
     makefiles, it results in the built-in pattern rules taking precedence
     over makefile-specified suffix rules, which is wrong.  */

  install_default_suffix_rules ();

  /* Define some internal and special variables.  */

  define_automatic_variables ();

  /* Set up the MAKEFLAGS and MFLAGS variables
     so makefiles can look at them.  */

  define_makeflags (0, 0);

  /* Define the default variables.  */
  define_default_variables ();

  default_file = enter_file (strcache_add (".DEFAULT"));

  default_goal_var = define_variable_cname (".DEFAULT_GOAL", "", o_file, 0);

  /* Evaluate all strings provided with --eval.
     Also set up the $(-*-eval-flags-*-) variable.  */

  if (eval_strings)
    {
      char *p, *value;
      unsigned int i;
      unsigned int len = sizeof ("--eval=") * eval_strings->idx;

      for (i = 0; i < eval_strings->idx; ++i)
        {
          p = xstrdup (eval_strings->list[i]);
          len += 2 * strlen (p);
          eval_buffer (p);
          free (p);
        }

      p = value = alloca (len);
      for (i = 0; i < eval_strings->idx; ++i)
        {
          strcpy (p, "--eval=");
          p += strlen (p);
          p = quote_for_env (p, eval_strings->list[i]);
          *(p++) = ' ';
        }
      p[-1] = '\0';

      define_variable_cname ("-*-eval-flags-*-", value, o_automatic, 0);
    }

  /* Read all the makefiles.  */

  read_makefiles
    = read_all_makefiles (makefiles == 0 ? 0 : makefiles->list);

#ifdef WINDOWS32
  /* look one last time after reading all Makefiles */
  if (no_default_sh_exe)
    no_default_sh_exe = !find_and_set_default_shell(NULL);
#endif /* WINDOWS32 */

#if defined (__MSDOS__) || defined (__EMX__)
  /* We need to know what kind of shell we will be using.  */
  {
    extern int _is_unixy_shell (const char *_path);
    struct variable *shv = lookup_variable (STRING_SIZE_TUPLE ("SHELL"));
    extern int unixy_shell;
    extern char *default_shell;

    if (shv && *shv->value)
      {
    char *shell_path = recursively_expand(shv);

    if (shell_path && _is_unixy_shell (shell_path))
      unixy_shell = 1;
    else
      unixy_shell = 0;
    if (shell_path)
      default_shell = shell_path;
      }
  }
#endif /* __MSDOS__ || __EMX__ */

  /* Decode switches again, in case the variables were set by the makefile.  */
  decode_env_switches (STRING_SIZE_TUPLE ("MAKEFLAGS"));
#if 0
  decode_env_switches (STRING_SIZE_TUPLE ("MFLAGS"));
#endif

#if defined (__MSDOS__) || defined (__EMX__)
  if (job_slots != 1
# ifdef __EMX__
      && _osmode != OS2_MODE /* turn off -j if we are in DOS mode */
# endif
      )
    {
      error (NILF,
             _("Parallel jobs (-j) are not supported on this platform."));
      error (NILF, _("Resetting to single job (-j1) mode."));
      job_slots = 1;
    }
#endif

#ifdef MAKE_JOBSERVER
  /* If the jobserver-fds option is seen, make sure that -j is reasonable.  */

  if (jobserver_fds)
    {
      const char *cp;
      unsigned int ui;

      for (ui=1; ui < jobserver_fds->idx; ++ui)
        if (!streq (jobserver_fds->list[0], jobserver_fds->list[ui]))
          fatal (NILF, _("internal error: multiple --jobserver-fds options"));

      /* Now parse the fds string and make sure it has the proper format.  */

      cp = jobserver_fds->list[0];

      if (sscanf (cp, "%d,%d", &job_fds[0], &job_fds[1]) != 2)
        fatal (NILF,
               _("internal error: invalid --jobserver-fds string `%s'"), cp);

      DB (DB_JOBS,
          (_("Jobserver client (fds %d,%d)\n"), job_fds[0], job_fds[1]));

      /* The combination of a pipe + !job_slots means we're using the
         jobserver.  If !job_slots and we don't have a pipe, we can start
         infinite jobs.  If we see both a pipe and job_slots >0 that means the
         user set -j explicitly.  This is broken; in this case obey the user
         (ignore the jobserver pipe for this make) but print a message.  */

      if (job_slots > 0)
        error (NILF,
               _("warning: -jN forced in submake: disabling jobserver mode."));

      /* Create a duplicate pipe, that will be closed in the SIGCHLD
         handler.  If this fails with EBADF, the parent has closed the pipe
         on us because it didn't think we were a submake.  If so, print a
         warning then default to -j1.  */

      else if ((job_rfd = dup (job_fds[0])) < 0)
        {
          if (errno != EBADF)
            pfatal_with_name (_("dup jobserver"));

          error (NILF,
                 _("warning: jobserver unavailable: using -j1.  Add `+' to parent make rule."));
          job_slots = 1;
        }

      if (job_slots > 0)
        {
          close (job_fds[0]);
          close (job_fds[1]);
          job_fds[0] = job_fds[1] = -1;
          free (jobserver_fds->list);
          free (jobserver_fds);
          jobserver_fds = 0;
        }
    }

  /* If we have >1 slot but no jobserver-fds, then we're a top-level make.
     Set up the pipe and install the fds option for our children.  */

  if (job_slots > 1)
    {
      char *cp;
      char c = '+';

      if (pipe (job_fds) < 0 || (job_rfd = dup (job_fds[0])) < 0)
    pfatal_with_name (_("creating jobs pipe"));

      /* Every make assumes that it always has one job it can run.  For the
         submakes it's the token they were given by their parent.  For the
         top make, we just subtract one from the number the user wants.  We
         want job_slots to be 0 to indicate we're using the jobserver.  */

      master_job_slots = job_slots;

      while (--job_slots)
        {
          int r;

          EINTRLOOP (r, write (job_fds[1], &c, 1));
          if (r != 1)
            pfatal_with_name (_("init jobserver pipe"));
        }

      /* Fill in the jobserver_fds struct for our children.  */

      cp = xmalloc ((sizeof ("1024")*2)+1);
      sprintf (cp, "%d,%d", job_fds[0], job_fds[1]);

      jobserver_fds = (struct stringlist *)
                        xmalloc (sizeof (struct stringlist));
      jobserver_fds->list = xmalloc (sizeof (char *));
      jobserver_fds->list[0] = cp;
      jobserver_fds->idx = 1;
      jobserver_fds->max = 1;
    }
#endif

#ifndef MAKE_SYMLINKS
  if (check_symlink_flag)
    {
      error (NILF, _("Symbolic links not supported: disabling -L."));
      check_symlink_flag = 0;
    }
#endif

  /* Set up MAKEFLAGS and MFLAGS again, so they will be right.  */

  define_makeflags (1, 0);

  /* Make each `struct dep' point at the `struct file' for the file
     depended on.  Also do magic for special targets.  */

  snap_deps ();

  /* Convert old-style suffix rules to pattern rules.  It is important to
     do this before installing the built-in pattern rules below, so that
     makefile-specified suffix rules take precedence over built-in pattern
     rules.  */

  convert_to_pattern ();

  /* Install the default implicit pattern rules.
     This used to be done before reading the makefiles.
     But in that case, built-in pattern rules were in the chain
     before user-defined ones, so they matched first.  */

  install_default_implicit_rules ();

  /* Compute implicit rule limits.  */

  count_implicit_rule_limits ();

  /* Construct the listings of directories in VPATH lists.  */

  build_vpath_lists ();

  /* Mark files given with -o flags as very old and as having been updated
     already, and files given with -W flags as brand new (time-stamp as far
     as possible into the future).  If restarts is set we'll do -W later.  */

  if (old_files != 0)
    {
      const char **p;
      for (p = old_files->list; *p != 0; ++p)
        {
          struct file *f = enter_file (*p);
          f->last_mtime = f->mtime_before_update = OLD_MTIME;
          f->updated = 1;
          f->update_status = 0;
          f->command_state = cs_finished;
        }
    }

  if (!restarts && new_files != 0)
    {
      const char **p;
      for (p = new_files->list; *p != 0; ++p)
    {
      struct file *f = enter_file (*p);
      f->last_mtime = f->mtime_before_update = NEW_MTIME;
    }
    }

  /* Initialize the remote job module.  */
  remote_setup ();

  if (read_makefiles != 0)
    {
      /* Update any makefiles if necessary.  */

      FILE_TIMESTAMP *makefile_mtimes = 0;
      unsigned int mm_idx = 0;
      char **nargv;
      int nargc;
      int orig_db_level = db_level;
      int status;

      if (! ISDB (DB_MAKEFILES))
        db_level = DB_NONE;

      DB (DB_BASIC, (_("Updating makefiles....\n")));

      /* Remove any makefiles we don't want to try to update.
     Also record the current modtimes so we can compare them later.  */
      {
    register struct dep *d, *last;
    last = 0;
    d = read_makefiles;
    while (d != 0)
      {
        struct file *f = d->file;
        if (f->double_colon)
          for (f = f->double_colon; f != NULL; f = f->prev)
        {
          if (f->deps == 0 && f->cmds != 0)
            {
              /* This makefile is a :: target with commands, but
             no dependencies.  So, it will always be remade.
             This might well cause an infinite loop, so don't
             try to remake it.  (This will only happen if
             your makefiles are written exceptionally
             stupidly; but if you work for Athena, that's how
             you write your makefiles.)  */

              DB (DB_VERBOSE,
                          (_("Makefile `%s' might loop; not remaking it.\n"),
                           f->name));

              if (last == 0)
            read_makefiles = d->next;
              else
            last->next = d->next;

              /* Free the storage.  */
                      free_dep (d);

              d = last == 0 ? read_makefiles : last->next;

              break;
            }
        }
        if (f == NULL || !f->double_colon)
          {
                makefile_mtimes = xrealloc (makefile_mtimes,
                                            (mm_idx+1)
                                            * sizeof (FILE_TIMESTAMP));
        makefile_mtimes[mm_idx++] = file_mtime_no_search (d->file);
        last = d;
        d = d->next;
          }
      }
      }

      /* Set up `MAKEFLAGS' specially while remaking makefiles.  */
      define_makeflags (1, 1);

      rebuilding_makefiles = 1;
      status = update_goal_chain (read_makefiles);
      rebuilding_makefiles = 0;

      switch (status)
    {
    case 1:
          /* The only way this can happen is if the user specified -q and asked
           * for one of the makefiles to be remade as a target on the command
           * line.  Since we're not actually updating anything with -q we can
           * treat this as "did nothing".
           */

    case -1:
      /* Did nothing.  */
      break;

    case 2:
      /* Failed to update.  Figure out if we care.  */
      {
        /* Nonzero if any makefile was successfully remade.  */
        int any_remade = 0;
        /* Nonzero if any makefile we care about failed
           in updating or could not be found at all.  */
        int any_failed = 0;
        unsigned int i;
            struct dep *d;

        for (i = 0, d = read_makefiles; d != 0; ++i, d = d->next)
              {
                /* Reset the considered flag; we may need to look at the file
                   again to print an error.  */
                d->file->considered = 0;

                if (d->file->updated)
                  {
                    /* This makefile was updated.  */
                    if (d->file->update_status == 0)
                      {
                        /* It was successfully updated.  */
                        any_remade |= (file_mtime_no_search (d->file)
                                       != makefile_mtimes[i]);
                      }
                    else if (! (d->changed & RM_DONTCARE))
                      {
                        FILE_TIMESTAMP mtime;
                        /* The update failed and this makefile was not
                           from the MAKEFILES variable, so we care.  */
                        error (NILF, _("Failed to remake makefile `%s'."),
                               d->file->name);
                        mtime = file_mtime_no_search (d->file);
                        any_remade |= (mtime != NONEXISTENT_MTIME
                                       && mtime != makefile_mtimes[i]);
                        makefile_status = MAKE_FAILURE;
                      }
                  }
                else
                  /* This makefile was not found at all.  */
                  if (! (d->changed & RM_DONTCARE))
                    {
                      /* This is a makefile we care about.  See how much.  */
                      if (d->changed & RM_INCLUDED)
                        /* An included makefile.  We don't need
                           to die, but we do want to complain.  */
                        error (NILF,
                               _("Included makefile `%s' was not found."),
                               dep_name (d));
                      else
                        {
                          /* A normal makefile.  We must die later.  */
                          error (NILF, _("Makefile `%s' was not found"),
                                 dep_name (d));
                          any_failed = 1;
                        }
                    }
              }
            /* Reset this to empty so we get the right error message below.  */
            read_makefiles = 0;

        if (any_remade)
          goto re_exec;
        if (any_failed)
          die (2);
            break;
      }

    case 0:
    re_exec:
      /* Updated successfully.  Re-exec ourselves.  */

      remove_intermediates (0);

      if (print_data_base_flag)
        print_data_base ();

      log_working_directory (0);

          clean_jobserver (0);

      if (makefiles != 0)
        {
          /* These names might have changed.  */
          int i, j = 0;
          for (i = 1; i < argc; ++i)
        if (strneq (argv[i], "-f", 2)) /* XXX */
          {
            if (argv[i][2] == '\0')
                      /* This cast is OK since we never modify argv.  */
              argv[++i] = (char *) makefiles->list[j];
            else
              argv[i] = xstrdup (concat (2, "-f", makefiles->list[j]));
            ++j;
          }
        }

          /* Add -o option for the stdin temporary file, if necessary.  */
          nargc = argc;
          if (stdin_nm)
            {
              nargv = xmalloc ((nargc + 2) * sizeof (char *));
              memcpy (nargv, argv, argc * sizeof (char *));
              nargv[nargc++] = xstrdup (concat (2, "-o", stdin_nm));
              nargv[nargc] = 0;
            }
          else
            nargv = argv;

      if (directories != 0 && directories->idx > 0)
        {
          int bad = 1;
          if (directory_before_chdir != 0)
        {
          if (chdir (directory_before_chdir) < 0)
              perror_with_name ("chdir", "");
          else
            bad = 0;
        }
          if (bad)
        fatal (NILF, _("Couldn't change back to original directory."));
        }

          ++restarts;

          /* Reset makeflags in case they were changed.  */
          {
            const char *pv = define_makeflags (1, 1);
            char *p = alloca (sizeof ("MAKEFLAGS=") + strlen (pv) + 1);
            sprintf (p, "MAKEFLAGS=%s", pv);
            putenv (p);
          }

      if (ISDB (DB_BASIC))
        {
          char **p;
          printf (_("Re-executing[%u]:"), restarts);
          for (p = nargv; *p != 0; ++p)
        printf (" %s", *p);
          putchar ('\n');
        }

#ifndef _AMIGA
          {
            char **p;
            for (p = environ; *p != 0; ++p)
              {
                if (strneq (*p, MAKELEVEL_NAME, MAKELEVEL_LENGTH)
                    && (*p)[MAKELEVEL_LENGTH] == '=')
                  {
                    *p = alloca (40);
                    sprintf (*p, "%s=%u", MAKELEVEL_NAME, makelevel);
                  }
                if (strneq (*p, "MAKE_RESTARTS=", 14))
                  {
                    *p = alloca (40);
                    sprintf (*p, "MAKE_RESTARTS=%u", restarts);
                    restarts = 0;
                  }
              }
          }
#else /* AMIGA */
      {
        char buffer[256];

            sprintf (buffer, "%u", makelevel);
            SetVar (MAKELEVEL_NAME, buffer, -1, GVF_GLOBAL_ONLY);

            sprintf (buffer, "%u", restarts);
            SetVar ("MAKE_RESTARTS", buffer, -1, GVF_GLOBAL_ONLY);
            restarts = 0;
      }
#endif

          /* If we didn't set the restarts variable yet, add it.  */
          if (restarts)
            {
              char *b = alloca (40);
              sprintf (b, "MAKE_RESTARTS=%u", restarts);
              putenv (b);
            }

      fflush (stdout);
      fflush (stderr);

          /* Close the dup'd jobserver pipe if we opened one.  */
          if (job_rfd >= 0)
            close (job_rfd);

#ifdef _AMIGA
      exec_command (nargv);
      exit (0);
#elif defined (__EMX__)
      {
        /* It is not possible to use execve() here because this
           would cause the parent process to be terminated with
           exit code 0 before the child process has been terminated.
           Therefore it may be the best solution simply to spawn the
           child process including all file handles and to wait for its
           termination. */
        int pid;
        int status;
        pid = child_execute_job (0, 1, nargv, environ);

        /* is this loop really necessary? */
        do {
          pid = wait (&status);
        } while (pid <= 0);
        /* use the exit code of the child process */
        exit (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE);
      }
#else
      exec_command (nargv, environ);
#endif
      /* NOTREACHED */

    default:
#define BOGUS_UPDATE_STATUS 0
      assert (BOGUS_UPDATE_STATUS);
      break;
    }

      db_level = orig_db_level;

      /* Free the makefile mtimes (if we allocated any).  */
      if (makefile_mtimes)
        free (makefile_mtimes);
    }

  /* Set up `MAKEFLAGS' again for the normal targets.  */
  define_makeflags (1, 0);

  /* Set always_make_flag if -B was given.  */
  always_make_flag = always_make_set;

  /* If restarts is set we haven't set up -W files yet, so do that now.  */
  if (restarts && new_files != 0)
    {
      const char **p;
      for (p = new_files->list; *p != 0; ++p)
    {
      struct file *f = enter_file (*p);
      f->last_mtime = f->mtime_before_update = NEW_MTIME;
    }
    }

  /* If there is a temp file from reading a makefile from stdin, get rid of
     it now.  */
  if (stdin_nm && unlink (stdin_nm) < 0 && errno != ENOENT)
    perror_with_name (_("unlink (temporary file): "), stdin_nm);

  /* If there were no command-line goals, use the default.  */
  if (goals == 0)
    {
      char *p;

      if (default_goal_var->recursive)
        p = variable_expand (default_goal_var->value);
      else
        {
          p = variable_buffer_output (variable_buffer, default_goal_var->value,
                                      strlen (default_goal_var->value));
          *p = '\0';
          p = variable_buffer;
        }

      if (*p != '\0')
        {
          struct file *f = lookup_file (p);

          /* If .DEFAULT_GOAL is a non-existent target, enter it into the
             table and let the standard logic sort it out. */
          if (f == 0)
            {
              struct nameseq *ns;

              ns = PARSE_FILE_SEQ (&p, struct nameseq, '\0', NULL, 0);
              if (ns)
                {
                  /* .DEFAULT_GOAL should contain one target. */
                  if (ns->next != 0)
                    fatal (NILF, _(".DEFAULT_GOAL contains more than one target"));

                  f = enter_file (strcache_add (ns->name));

                  ns->name = 0; /* It was reused by enter_file(). */
                  free_ns_chain (ns);
                }
            }

          if (f)
            {
              goals = alloc_dep ();
              goals->file = f;
            }
        }
    }
  else
    lastgoal->next = 0;


  if (!goals)
    {
      if (read_makefiles == 0)
        fatal (NILF, _("No targets specified and no makefile found"));

      fatal (NILF, _("No targets"));
    }

  /* Update the goals.  */

  DB (DB_BASIC, (_("Updating goal targets....\n")));

  {
    int status;

    switch (update_goal_chain (goals))
    {
      case -1:
        /* Nothing happened.  */
      case 0:
        /* Updated successfully.  */
        status = makefile_status;
        break;
      case 1:
        /* We are under -q and would run some commands.  */
        status = MAKE_TROUBLE;
        break;
      case 2:
        /* Updating failed.  POSIX.2 specifies exit status >1 for this;
           but in VMS, there is only success and failure.  */
        status = MAKE_FAILURE;
        break;
      default:
        abort ();
    }

    /* If we detected some clock skew, generate one last warning */
    if (clock_skew_detected)
      error (NILF,
             _("warning:  Clock skew detected.  Your build may be incomplete."));

    /* Exit.  */
    die (status);
  }

  /* NOTREACHED */
  return 0;
}


/* Parsing of arguments, decoding of switches.  */

static char options[1 + sizeof (switches) / sizeof (switches[0]) * 3];
static struct option long_options[(sizeof (switches) / sizeof (switches[0])) +
                  (sizeof (long_option_aliases) /
                   sizeof (long_option_aliases[0]))];

/* Fill in the string and vector for getopt.  */
static void
init_switches (void)
{
  char *p;
  unsigned int c;
  unsigned int i;

  if (options[0] != '\0')
    /* Already done.  */
    return;

  p = options;

  /* Return switch and non-switch args in order, regardless of
     POSIXLY_CORRECT.  Non-switch args are returned as option 1.  */
  *p++ = '-';

  for (i = 0; switches[i].c != '\0'; ++i)
    {
      long_options[i].name = (switches[i].long_name == 0 ? "" :
                  switches[i].long_name);
      long_options[i].flag = 0;
      long_options[i].val = switches[i].c;
      if (short_option (switches[i].c))
    *p++ = switches[i].c;
      switch (switches[i].type)
    {
    case flag:
    case flag_off:
    case ignore:
      long_options[i].has_arg = no_argument;
      break;

    case string:
        case filename:
    case positive_int:
    case floating:
      if (short_option (switches[i].c))
        *p++ = ':';
      if (switches[i].noarg_value != 0)
        {
          if (short_option (switches[i].c))
        *p++ = ':';
          long_options[i].has_arg = optional_argument;
        }
      else
        long_options[i].has_arg = required_argument;
      break;
    }
    }
  *p = '\0';
  for (c = 0; c < (sizeof (long_option_aliases) /
           sizeof (long_option_aliases[0]));
       ++c)
    long_options[i++] = long_option_aliases[c];
  long_options[i].name = 0;
}

static void
handle_non_switch_argument (char *arg, int env)
{
  /* Non-option argument.  It might be a variable definition.  */
  struct variable *v;
  if (arg[0] == '-' && arg[1] == '\0')
    /* Ignore plain `-' for compatibility.  */
    return;
  v = try_variable_definition (0, arg, o_command, 0);
  if (v != 0)
    {
      /* It is indeed a variable definition.  If we don't already have this
     one, record a pointer to the variable for later use in
     define_makeflags.  */
      struct command_variable *cv;

      for (cv = command_variables; cv != 0; cv = cv->next)
        if (cv->variable == v)
          break;

      if (! cv) {
        cv = xmalloc (sizeof (*cv));
        cv->variable = v;
        cv->next = command_variables;
        command_variables = cv;
      }
    }
  else if (! env)
    {
      /* Not an option or variable definition; it must be a goal
     target!  Enter it as a file and add it to the dep chain of
     goals.  */
      struct file *f = enter_file (strcache_add (expand_command_line_file (arg)));
      f->cmd_target = 1;

      if (goals == 0)
    {
      goals = alloc_dep ();
      lastgoal = goals;
    }
      else
    {
      lastgoal->next = alloc_dep ();
      lastgoal = lastgoal->next;
    }

      lastgoal->file = f;

      {
        /* Add this target name to the MAKECMDGOALS variable. */
        struct variable *gv;
        const char *value;

        gv = lookup_variable (STRING_SIZE_TUPLE ("MAKECMDGOALS"));
        if (gv == 0)
          value = f->name;
        else
          {
            /* Paste the old and new values together */
            unsigned int oldlen, newlen;
            char *vp;

            oldlen = strlen (gv->value);
            newlen = strlen (f->name);
            vp = alloca (oldlen + 1 + newlen + 1);
            memcpy (vp, gv->value, oldlen);
            vp[oldlen] = ' ';
            memcpy (&vp[oldlen + 1], f->name, newlen + 1);
            value = vp;
          }
        define_variable_cname ("MAKECMDGOALS", value, o_default, 0);
      }
    }
}

/* Print a nice usage method.  */

static void
print_usage (int bad)
{
  const char *const *cpp;
  FILE *usageto;

  if (print_version_flag)
    print_version ();

  usageto = bad ? stderr : stdout;

  fprintf (usageto, _("Usage: %s [options] [target] ...\n"), program);

  for (cpp = usage; *cpp; ++cpp)
    fputs (_(*cpp), usageto);

  if (!remote_description || *remote_description == '\0')
    fprintf (usageto, _("\nThis program built for %s\n"), make_host);
  else
    fprintf (usageto, _("\nThis program built for %s (%s)\n"),
             make_host, remote_description);

  fprintf (usageto, _("Report bugs to <bug-make@gnu.org>\n"));
}

/* Decode switches from ARGC and ARGV.
   They came from the environment if ENV is nonzero.  */

static void
decode_switches (int argc, char **argv, int env)
{
  int bad = 0;
  register const struct command_switch *cs;
  register struct stringlist *sl;
  register int c;

  /* getopt does most of the parsing for us.
     First, get its vectors set up.  */

  init_switches ();

  /* Let getopt produce error messages for the command line,
     but not for options from the environment.  */
  opterr = !env;
  /* Reset getopt's state.  */
  optind = 0;

  while (optind < argc)
    {
      /* Parse the next argument.  */
      c = getopt_long (argc, argv, options, long_options, (int *) 0);
      if (c == EOF)
    /* End of arguments, or "--" marker seen.  */
    break;
      else if (c == 1)
    /* An argument not starting with a dash.  */
    handle_non_switch_argument (optarg, env);
      else if (c == '?')
    /* Bad option.  We will print a usage message and die later.
       But continue to parse the other options so the user can
       see all he did wrong.  */
    bad = 1;
      else
    for (cs = switches; cs->c != '\0'; ++cs)
      if (cs->c == c)
        {
          /* Whether or not we will actually do anything with
         this switch.  We test this individually inside the
         switch below rather than just once outside it, so that
         options which are to be ignored still consume args.  */
          int doit = !env || cs->env;

          switch (cs->type)
        {
        default:
          abort ();

        case ignore:
          break;

        case flag:
        case flag_off:
          if (doit)
            *(int *) cs->value_ptr = cs->type == flag;
          break;

        case string:
        case filename:
          if (!doit)
            break;

          if (optarg == 0)
            optarg = xstrdup (cs->noarg_value);
                  else if (*optarg == '\0')
                    {
                      char opt[2] = "c";
                      const char *op = opt;

                      if (short_option (cs->c))
                        opt[0] = cs->c;
                      else
                        op = cs->long_name;

                      error (NILF, _("the `%s%s' option requires a non-empty string argument"),
                             short_option (cs->c) ? "-" : "--", op);
                      bad = 1;
                    }

          sl = *(struct stringlist **) cs->value_ptr;
          if (sl == 0)
            {
              sl = (struct stringlist *)
            xmalloc (sizeof (struct stringlist));
              sl->max = 5;
              sl->idx = 0;
              sl->list = xmalloc (5 * sizeof (char *));
              *(struct stringlist **) cs->value_ptr = sl;
            }
          else if (sl->idx == sl->max - 1)
            {
              sl->max += 5;
                      /* MSVC erroneously warns without a cast here.  */
              sl->list = xrealloc ((void *)sl->list,
                                           sl->max * sizeof (char *));
            }
                  if (cs->type == filename)
                    sl->list[sl->idx++] = expand_command_line_file (optarg);
                  else
                    sl->list[sl->idx++] = optarg;
          sl->list[sl->idx] = 0;
          break;

        case positive_int:
                  /* See if we have an option argument; if we do require that
                     it's all digits, not something like "10foo".  */
          if (optarg == 0 && argc > optind)
                    {
                      const char *cp;
                      for (cp=argv[optind]; ISDIGIT (cp[0]); ++cp)
                        ;
                      if (cp[0] == '\0')
                        optarg = argv[optind++];
                    }

          if (!doit)
            break;

          if (optarg != 0)
            {
              int i = atoi (optarg);
                      const char *cp;

                      /* Yes, I realize we're repeating this in some cases.  */
                      for (cp = optarg; ISDIGIT (cp[0]); ++cp)
                        ;

              if (i < 1 || cp[0] != '\0')
            {
                          error (NILF, _("the `-%c' option requires a positive integral argument"),
                                 cs->c);
              bad = 1;
            }
              else
            *(unsigned int *) cs->value_ptr = i;
            }
          else
            *(unsigned int *) cs->value_ptr
              = *(unsigned int *) cs->noarg_value;
          break;

#ifndef NO_FLOAT
        case floating:
          if (optarg == 0 && optind < argc
              && (ISDIGIT (argv[optind][0]) || argv[optind][0] == '.'))
            optarg = argv[optind++];

          if (doit)
            *(double *) cs->value_ptr
              = (optarg != 0 ? atof (optarg)
             : *(double *) cs->noarg_value);

          break;
#endif
        }

          /* We've found the switch.  Stop looking.  */
          break;
        }
    }

  /* There are no more options according to getting getopt, but there may
     be some arguments left.  Since we have asked for non-option arguments
     to be returned in order, this only happens when there is a "--"
     argument to prevent later arguments from being options.  */
  while (optind < argc)
    handle_non_switch_argument (argv[optind++], env);


  if (!env && (bad || print_usage_flag))
    {
      print_usage (bad);
      die (bad ? 2 : 0);
    }
}

/* Decode switches from environment variable ENVAR (which is LEN chars long).
   We do this by chopping the value into a vector of words, prepending a
   dash to the first word if it lacks one, and passing the vector to
   decode_switches.  */

static void
decode_env_switches (char *envar, unsigned int len)
{
  char *varref = alloca (2 + len + 2);
  char *value, *p;
  int argc;
  char **argv;

  /* Get the variable's value.  */
  varref[0] = '$';
  varref[1] = '(';
  memcpy (&varref[2], envar, len);
  varref[2 + len] = ')';
  varref[2 + len + 1] = '\0';
  value = variable_expand (varref);

  /* Skip whitespace, and check for an empty value.  */
  value = next_token (value);
  len = strlen (value);
  if (len == 0)
    return;

  /* Allocate a vector that is definitely big enough.  */
  argv = alloca ((1 + len + 1) * sizeof (char *));

  /* Allocate a buffer to copy the value into while we split it into words
     and unquote it.  We must use permanent storage for this because
     decode_switches may store pointers into the passed argument words.  */
  p = xmalloc (2 * len);

  /* getopt will look at the arguments starting at ARGV[1].
     Prepend a spacer word.  */
  argv[0] = 0;
  argc = 1;
  argv[argc] = p;
  while (*value != '\0')
    {
      if (*value == '\\' && value[1] != '\0')
    ++value;        /* Skip the backslash.  */
      else if (isblank ((unsigned char)*value))
    {
      /* End of the word.  */
      *p++ = '\0';
      argv[++argc] = p;
      do
        ++value;
      while (isblank ((unsigned char)*value));
      continue;
    }
      *p++ = *value++;
    }
  *p = '\0';
  argv[++argc] = 0;

  if (argv[1][0] != '-' && strchr (argv[1], '=') == 0)
    /* The first word doesn't start with a dash and isn't a variable
       definition.  Add a dash and pass it along to decode_switches.  We
       need permanent storage for this in case decode_switches saves
       pointers into the value.  */
    argv[1] = xstrdup (concat (2, "-", argv[1]));

  /* Parse those words.  */
  decode_switches (argc, argv, 1);
}


/* Quote the string IN so that it will be interpreted as a single word with
   no magic by decode_env_switches; also double dollar signs to avoid
   variable expansion in make itself.  Write the result into OUT, returning
   the address of the next character to be written.
   Allocating space for OUT twice the length of IN is always sufficient.  */

static char *
quote_for_env (char *out, const char *in)
{
  while (*in != '\0')
    {
      if (*in == '$')
    *out++ = '$';
      else if (isblank ((unsigned char)*in) || *in == '\\')
        *out++ = '\\';
      *out++ = *in++;
    }

  return out;
}

/* Define the MAKEFLAGS and MFLAGS variables to reflect the settings of the
   command switches.  Include options with args if ALL is nonzero.
   Don't include options with the `no_makefile' flag set if MAKEFILE.  */

static const char *
define_makeflags (int all, int makefile)
{
  const char ref[] = "$(MAKEOVERRIDES)";
  const char posixref[] = "$(-*-command-variables-*-)";
  const char evalref[] = "$(-*-eval-flags-*-)";
  const struct command_switch *cs;
  char *flagstring;
  register char *p;
  unsigned int words;
  struct variable *v;

  /* We will construct a linked list of `struct flag's describing
     all the flags which need to go in MAKEFLAGS.  Then, once we
     know how many there are and their lengths, we can put them all
     together in a string.  */

  struct flag
    {
      struct flag *next;
      const struct command_switch *cs;
      const char *arg;
    };
  struct flag *flags = 0;
  unsigned int flagslen = 0;
#define ADD_FLAG(ARG, LEN) \
  do {                                        \
    struct flag *new = alloca (sizeof (struct flag));                 \
    new->cs = cs;                                 \
    new->arg = (ARG);                                 \
    new->next = flags;                                \
    flags = new;                                  \
    if (new->arg == 0)                                \
      ++flagslen;       /* Just a single flag letter.  */         \
    else                                      \
      /* " -x foo", plus space to expand "foo".  */               \
      flagslen += 1 + 1 + 1 + 1 + (3 * (LEN));                    \
    if (!short_option (cs->c))                            \
      /* This switch has no single-letter version, so we use the long.  */    \
      flagslen += 2 + strlen (cs->long_name);                     \
  } while (0)

  for (cs = switches; cs->c != '\0'; ++cs)
    if (cs->toenv && (!makefile || !cs->no_makefile))
      switch (cs->type)
    {
    case ignore:
      break;

    case flag:
    case flag_off:
      if (!*(int *) cs->value_ptr == (cs->type == flag_off)
          && (cs->default_value == 0
          || *(int *) cs->value_ptr != *(int *) cs->default_value))
        ADD_FLAG (0, 0);
      break;

    case positive_int:
      if (all)
        {
          if ((cs->default_value != 0
           && (*(unsigned int *) cs->value_ptr
               == *(unsigned int *) cs->default_value)))
        break;
          else if (cs->noarg_value != 0
               && (*(unsigned int *) cs->value_ptr ==
               *(unsigned int *) cs->noarg_value))
        ADD_FLAG ("", 0); /* Optional value omitted; see below.  */
          else if (cs->c == 'j')
        /* Special case for `-j'.  */
        ADD_FLAG ("1", 1);
          else
        {
          char *buf = alloca (30);
          sprintf (buf, "%u", *(unsigned int *) cs->value_ptr);
          ADD_FLAG (buf, strlen (buf));
        }
        }
      break;

#ifndef NO_FLOAT
    case floating:
      if (all)
        {
          if (cs->default_value != 0
          && (*(double *) cs->value_ptr
              == *(double *) cs->default_value))
        break;
          else if (cs->noarg_value != 0
               && (*(double *) cs->value_ptr
               == *(double *) cs->noarg_value))
        ADD_FLAG ("", 0); /* Optional value omitted; see below.  */
          else
        {
          char *buf = alloca (100);
          sprintf (buf, "%g", *(double *) cs->value_ptr);
          ADD_FLAG (buf, strlen (buf));
        }
        }
      break;
#endif

    case filename:
    case string:
      if (all)
        {
          struct stringlist *sl = *(struct stringlist **) cs->value_ptr;
          if (sl != 0)
        {
          /* Add the elements in reverse order, because all the flags
             get reversed below; and the order matters for some
             switches (like -I).  */
          unsigned int i = sl->idx;
          while (i-- > 0)
            ADD_FLAG (sl->list[i], strlen (sl->list[i]));
        }
        }
      break;

    default:
      abort ();
    }

  /* Four more for the possible " -- ".  */
  flagslen += 4 + sizeof (posixref) + sizeof (evalref);

#undef  ADD_FLAG

  /* Construct the value in FLAGSTRING.
     We allocate enough space for a preceding dash and trailing null.  */
  flagstring = alloca (1 + flagslen + 1);
  memset (flagstring, '\0', 1 + flagslen + 1);
  p = flagstring;
  words = 1;
  *p++ = '-';
  while (flags != 0)
    {
      /* Add the flag letter or name to the string.  */
      if (short_option (flags->cs->c))
    *p++ = flags->cs->c;
      else
    {
          if (*p != '-')
            {
              *p++ = ' ';
              *p++ = '-';
            }
      *p++ = '-';
      strcpy (p, flags->cs->long_name);
      p += strlen (p);
    }
      if (flags->arg != 0)
    {
      /* A flag that takes an optional argument which in this case is
         omitted is specified by ARG being "".  We must distinguish
         because a following flag appended without an intervening " -"
         is considered the arg for the first.  */
      if (flags->arg[0] != '\0')
        {
          /* Add its argument too.  */
          *p++ = !short_option (flags->cs->c) ? '=' : ' ';
          p = quote_for_env (p, flags->arg);
        }
      ++words;
      /* Write a following space and dash, for the next flag.  */
      *p++ = ' ';
      *p++ = '-';
    }
      else if (!short_option (flags->cs->c))
    {
      ++words;
      /* Long options must each go in their own word,
         so we write the following space and dash.  */
      *p++ = ' ';
      *p++ = '-';
    }
      flags = flags->next;
    }

  /* Define MFLAGS before appending variable definitions.  */

  if (p == &flagstring[1])
    /* No flags.  */
    flagstring[0] = '\0';
  else if (p[-1] == '-')
    {
      /* Kill the final space and dash.  */
      p -= 2;
      *p = '\0';
    }
  else
    /* Terminate the string.  */
    *p = '\0';

  /* Since MFLAGS is not parsed for flags, there is no reason to
     override any makefile redefinition.  */
  define_variable_cname ("MFLAGS", flagstring, o_env, 1);

  /* Write a reference to -*-eval-flags-*-, which contains all the --eval
     flag options.  */
  if (eval_strings)
    {
      if (p == &flagstring[1])
    /* No flags written, so elide the leading dash already written.  */
    p = flagstring;
      else
        *p++ = ' ';
      memcpy (p, evalref, sizeof (evalref) - 1);
      p += sizeof (evalref) - 1;
    }

  if (all && command_variables != 0)
    {
      /* Now write a reference to $(MAKEOVERRIDES), which contains all the
     command-line variable definitions.  */

      if (p == &flagstring[1])
    /* No flags written, so elide the leading dash already written.  */
    p = flagstring;
      else
    {
      /* Separate the variables from the switches with a "--" arg.  */
      if (p[-1] != '-')
        {
          /* We did not already write a trailing " -".  */
          *p++ = ' ';
          *p++ = '-';
        }
      /* There is a trailing " -"; fill it out to " -- ".  */
      *p++ = '-';
      *p++ = ' ';
    }

      /* Copy in the string.  */
      if (posix_pedantic)
    {
      memcpy (p, posixref, sizeof (posixref) - 1);
      p += sizeof (posixref) - 1;
    }
      else
    {
      memcpy (p, ref, sizeof (ref) - 1);
      p += sizeof (ref) - 1;
    }
    }
  else if (p == &flagstring[1])
    {
      words = 0;
      --p;
    }
  else if (p[-1] == '-')
    /* Kill the final space and dash.  */
    p -= 2;
  /* Terminate the string.  */
  *p = '\0';

  /* If there are switches, omit the leading dash unless it is a single long
     option with two leading dashes.  */
  if (flagstring[0] == '-' && flagstring[1] != '-')
    ++flagstring;

  v = define_variable_cname ("MAKEFLAGS", flagstring,
                             /* This used to use o_env, but that lost when a
                                makefile defined MAKEFLAGS.  Makefiles set
                                MAKEFLAGS to add switches, but we still want
                                to redefine its value with the full set of
                                switches.  Of course, an override or command
                                definition will still take precedence.  */
                             o_file, 1);

  if (! all)
    /* The first time we are called, set MAKEFLAGS to always be exported.
       We should not do this again on the second call, because that is
       after reading makefiles which might have done `unexport MAKEFLAGS'. */
    v->export = v_export;

  return v->value;
}


/* Print version information.  */

static void
print_version (void)
{
  static int printed_version = 0;

  char *precede = print_data_base_flag ? "# " : "";

  if (printed_version)
    /* Do it only once.  */
    return;

  printf ("%sGNU Make %s\n", precede, version_string);

  if (!remote_description || *remote_description == '\0')
    printf (_("%sBuilt for %s\n"), precede, make_host);
  else
    printf (_("%sBuilt for %s (%s)\n"),
            precede, make_host, remote_description);

  /* Print this untranslated.  The coding standards recommend translating the
     (C) to the copyright symbol, but this string is going to change every
     year, and none of the rest of it should be translated (including the
     word "Copyright", so it hardly seems worth it.  */

  printf ("%sCopyright (C) 2010  Free Software Foundation, Inc.\n", precede);

  printf (_("%sLicense GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
%sThis is free software: you are free to change and redistribute it.\n\
%sThere is NO WARRANTY, to the extent permitted by law.\n"),
            precede, precede, precede);

  printed_version = 1;

  /* Flush stdout so the user doesn't have to wait to see the
     version information while things are thought about.  */
  fflush (stdout);
}

/* Print a bunch of information about this and that.  */

static void
print_data_base ()
{
  time_t when;

  when = time ((time_t *) 0);
  printf (_("\n# Make data base, printed on %s"), ctime (&when));

  print_variable_data_base ();
  print_dir_data_base ();
  print_rule_data_base ();
  print_file_data_base ();
  print_vpath_data_base ();
  strcache_print_stats ("#");

  when = time ((time_t *) 0);
  printf (_("\n# Finished Make data base on %s\n"), ctime (&when));
}

static void
clean_jobserver (int status)
{
  char token = '+';

  /* Sanity: have we written all our jobserver tokens back?  If our
     exit status is 2 that means some kind of syntax error; we might not
     have written all our tokens so do that now.  If tokens are left
     after any other error code, that's bad.  */

  if (job_fds[0] != -1 && jobserver_tokens)
    {
      if (status != 2)
        error (NILF,
               "INTERNAL: Exiting with %u jobserver tokens (should be 0)!",
               jobserver_tokens);
      else
        while (jobserver_tokens--)
          {
            int r;

            EINTRLOOP (r, write (job_fds[1], &token, 1));
            if (r != 1)
              perror_with_name ("write", "");
          }
    }


  /* Sanity: If we're the master, were all the tokens written back?  */

  if (master_job_slots)
    {
      /* We didn't write one for ourself, so start at 1.  */
      unsigned int tcnt = 1;

      /* Close the write side, so the read() won't hang.  */
      close (job_fds[1]);

      while (read (job_fds[0], &token, 1) == 1)
        ++tcnt;

      if (tcnt != master_job_slots)
        error (NILF,
               "INTERNAL: Exiting with %u jobserver tokens available; should be %u!",
               tcnt, master_job_slots);

      close (job_fds[0]);

      /* Clean out jobserver_fds so we don't pass this information to any
         sub-makes.  Also reset job_slots since it will be put on the command
         line, not in MAKEFLAGS.  */
      job_slots = default_job_slots;
      if (jobserver_fds)
        {
          /* MSVC erroneously warns without a cast here.  */
          free ((void *)jobserver_fds->list);
          free (jobserver_fds);
          jobserver_fds = 0;
        }
    }
}


/* Exit with STATUS, cleaning up as necessary.  */

void
die (int status)
{
  static char dying = 0;

  if (!dying)
    {
      int err;

      dying = 1;

      if (print_version_flag)
    print_version ();

      /* Wait for children to die.  */
      err = (status != 0);
      while (job_slots_used > 0)
    reap_children (1, err);

      /* Let the remote job module clean up its state.  */
      remote_cleanup ();

      /* Remove the intermediate files.  */
      remove_intermediates (0);

      if (print_data_base_flag)
    print_data_base ();

      verify_file_data_base ();

      clean_jobserver (status);

      /* Try to move back to the original directory.  This is essential on
     MS-DOS (where there is really only one process), and on Unix it
     puts core files in the original directory instead of the -C
     directory.  Must wait until after remove_intermediates(), or unlinks
         of relative pathnames fail.  */
      if (directory_before_chdir != 0)
        {
          /* If it fails we don't care: shut up GCC.  */
          int _x;
          _x = chdir (directory_before_chdir);
        }

      log_working_directory (0);
    }

  exit (status);
}


/* Write a message indicating that we've just entered or
   left (according to ENTERING) the current directory.  */

void
log_working_directory (int entering)
{
  static int entered = 0;

  /* Print nothing without the flag.  Don't print the entering message
     again if we already have.  Don't print the leaving message if we
     haven't printed the entering message.  */
  if (! print_directory_flag || entering == entered)
    return;

  entered = entering;

  if (print_data_base_flag)
    fputs ("# ", stdout);

  /* Use entire sentences to give the translators a fighting chance.  */

  if (makelevel == 0)
    if (starting_directory == 0)
      if (entering)
        printf (_("%s: Entering an unknown directory\n"), program);
      else
        printf (_("%s: Leaving an unknown directory\n"), program);
    else
      if (entering)
        printf (_("%s: Entering directory `%s'\n"),
                program, starting_directory);
      else
        printf (_("%s: Leaving directory `%s'\n"),
                program, starting_directory);
  else
    if (starting_directory == 0)
      if (entering)
        printf (_("%s[%u]: Entering an unknown directory\n"),
                program, makelevel);
      else
        printf (_("%s[%u]: Leaving an unknown directory\n"),
                program, makelevel);
    else
      if (entering)
        printf (_("%s[%u]: Entering directory `%s'\n"),
                program, makelevel, starting_directory);
      else
        printf (_("%s[%u]: Leaving directory `%s'\n"),
                program, makelevel, starting_directory);

  /* Flush stdout to be sure this comes before any stderr output.  */
  fflush (stdout);
}

https://git.savannah.gnu.org/cgit/make.git/tree/getopt.h?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/getopt.h used in: Step 527
/* Declarations for getopt.
Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free
Software Foundation, Inc.

NOTE: The canonical source of this file is maintained with the GNU C Library.
Bugs can be reported to bug-glibc@gnu.org.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#ifndef _GETOPT_H
#define _GETOPT_H 1

#ifdef  __cplusplus
extern "C" {
#endif

/* For communication from `getopt' to the caller.
   When `getopt' finds an option that takes an argument,
   the argument value is returned here.
   Also, when `ordering' is RETURN_IN_ORDER,
   each non-option ARGV-element is returned here.  */

extern char *optarg;

/* Index in ARGV of the next element to be scanned.
   This is used for communication to and from the caller
   and for communication between successive calls to `getopt'.

   On entry to `getopt', zero means this is the first call; initialize.

   When `getopt' returns -1, this is the index of the first of the
   non-option elements that the caller should itself scan.

   Otherwise, `optind' communicates from one call to the next
   how much of ARGV has been scanned so far.  */

extern int optind;

/* Callers store zero here to inhibit the error message `getopt' prints
   for unrecognized options.  */

extern int opterr;

/* Set to an option character which was unrecognized.  */

extern int optopt;

/* Describe the long-named options requested by the application.
   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
   of `struct option' terminated by an element containing a name which is
   zero.

   The field `has_arg' is:
   no_argument      (or 0) if the option does not take an argument,
   required_argument    (or 1) if the option requires an argument,
   optional_argument    (or 2) if the option takes an optional argument.

   If the field `flag' is not NULL, it points to a variable that is set
   to the value given in the field `val' when the option is found, but
   left unchanged if the option is not found.

   To have a long-named option do something other than set an `int' to
   a compiled-in constant, such as set a value from `optarg', set the
   option's `flag' field to zero and its `val' field to a nonzero
   value (the equivalent single-letter option character, if there is
   one).  For long options that have a zero `flag' field, `getopt'
   returns the contents of the `val' field.  */

struct option
{
#if defined (__STDC__) && __STDC__
  const char *name;
#else
  char *name;
#endif
  /* has_arg can't be an enum because some compilers complain about
     type mismatches in all the code that assumes it is an int.  */
  int has_arg;
  int *flag;
  int val;
};

/* Names for the values of the `has_arg' field of `struct option'.  */

#define no_argument     0
#define required_argument   1
#define optional_argument   2

#if defined (__STDC__) && __STDC__
#ifdef __GNU_LIBRARY__
/* Many other libraries have conflicting prototypes for getopt, with
   differences in the consts, in stdlib.h.  To avoid compilation
   errors, only prototype getopt for the GNU C library.  */
extern int getopt (int argc, char *const *argv, const char *shortopts);
#else /* not __GNU_LIBRARY__ */
extern int getopt ();
#endif /* __GNU_LIBRARY__ */
extern int getopt_long (int argc, char *const *argv, const char *shortopts,
                const struct option *longopts, int *longind);
extern int getopt_long_only (int argc, char *const *argv,
                 const char *shortopts,
                     const struct option *longopts, int *longind);

/* Internal only.  Users should not call this directly.  */
extern int _getopt_internal (int argc, char *const *argv,
                 const char *shortopts,
                     const struct option *longopts, int *longind,
                 int long_only);
#else /* not __STDC__ */
extern int getopt ();
extern int getopt_long ();
extern int getopt_long_only ();

extern int _getopt_internal ();
#endif /* __STDC__ */

#ifdef  __cplusplus
}
#endif

#endif /* getopt.h */

https://git.savannah.gnu.org/cgit/make.git/tree/misc.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/misc.c used in: Step 528
/* Miscellaneous generic support functions for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"
#include "dep.h"
#include "debug.h"

/* Variadic functions.  We go through contortions to allow proper function
   prototypes for both ANSI and pre-ANSI C compilers, and also for those
   which support stdarg.h vs. varargs.h, and finally those which have
   vfprintf(), etc. and those who have _doprnt... or nothing.

   This fancy stuff all came from GNU fileutils, except for the VA_PRINTF and
   VA_END macros used here since we have multiple print functions.  */

#if USE_VARIADIC
# if HAVE_STDARG_H
#  include <stdarg.h>
#  define VA_START(args, lastarg) va_start(args, lastarg)
# else
#  include <varargs.h>
#  define VA_START(args, lastarg) va_start(args)
# endif
# if HAVE_VPRINTF
#  define VA_PRINTF(fp, lastarg, args) vfprintf((fp), (lastarg), (args))
# else
#  define VA_PRINTF(fp, lastarg, args) _doprnt((lastarg), (args), (fp))
# endif
# define VA_END(args) va_end(args)
#else
/* We can't use any variadic interface! */
# define va_alist a1, a2, a3, a4, a5, a6, a7, a8
# define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
# define VA_START(args, lastarg)
# define VA_PRINTF(fp, lastarg, args) fprintf((fp), (lastarg), va_alist)
# define VA_END(args)
#endif


/* Compare strings *S1 and *S2.
   Return negative if the first is less, positive if it is greater,
   zero if they are equal.  */

int
alpha_compare (const void *v1, const void *v2)
{
  const char *s1 = *((char **)v1);
  const char *s2 = *((char **)v2);

  if (*s1 != *s2)
    return *s1 - *s2;
  return strcmp (s1, s2);
}


/* Discard each backslash-newline combination from LINE.
   Backslash-backslash-newline combinations become backslash-newlines.
   This is done by copying the text at LINE into itself.  */

void
collapse_continuations (char *line)
{
  register char *in, *out, *p;
  register int backslash;
  register unsigned int bs_write;

  in = strchr (line, '\n');
  if (in == 0)
    return;

  out = in;
  while (out > line && out[-1] == '\\')
    --out;

  while (*in != '\0')
    {
      /* BS_WRITE gets the number of quoted backslashes at
     the end just before IN, and BACKSLASH gets nonzero
     if the next character is quoted.  */
      backslash = 0;
      bs_write = 0;
      for (p = in - 1; p >= line && *p == '\\'; --p)
    {
      if (backslash)
        ++bs_write;
      backslash = !backslash;

      /* It should be impossible to go back this far without exiting,
         but if we do, we can't get the right answer.  */
      if (in == out - 1)
        abort ();
    }

      /* Output the appropriate number of backslashes.  */
      while (bs_write-- > 0)
    *out++ = '\\';

      /* Skip the newline.  */
      ++in;

      /* If the newline is escaped, discard following whitespace leaving just
     one space.  POSIX requires that each backslash/newline/following
     whitespace sequence be reduced to a single space.  */
      if (backslash)
    {
      in = next_token (in);
          /* Removing this loop will fix Savannah bug #16670: do we want to? */
      while (out > line && isblank ((unsigned char)out[-1]))
        --out;
      *out++ = ' ';
    }
      else
    /* If the newline isn't quoted, put it in the output.  */
    *out++ = '\n';

      /* Now copy the following line to the output.
     Stop when we find backslashes followed by a newline.  */
      while (*in != '\0')
    if (*in == '\\')
      {
        p = in + 1;
        while (*p == '\\')
          ++p;
        if (*p == '\n')
          {
        in = p;
        break;
          }
        while (in < p)
          *out++ = *in++;
      }
    else
      *out++ = *in++;
    }

  *out = '\0';
}


/* Print N spaces (used in debug for target-depth).  */

void
print_spaces (unsigned int n)
{
  while (n-- > 0)
    putchar (' ');
}



/* Return a string whose contents concatenate the NUM strings provided
   This string lives in static, re-used memory.  */

const char *
#if HAVE_ANSI_COMPILER && USE_VARIADIC && HAVE_STDARG_H
concat (unsigned int num, ...)
#else
concat (num, va_alist)
     unsigned int num;
     va_dcl
#endif
{
  static unsigned int rlen = 0;
  static char *result = NULL;
  int ri = 0;

#if USE_VARIADIC
  va_list args;
#endif

  VA_START (args, num);

  while (num-- > 0)
    {
      const char *s = va_arg (args, const char *);
      unsigned int l = s ? strlen (s) : 0;

      if (l == 0)
        continue;

      if (ri + l > rlen)
        {
          rlen = ((rlen ? rlen : 60) + l) * 2;
          result = xrealloc (result, rlen);
        }

      memcpy (result + ri, s, l);
      ri += l;
    }

  VA_END (args);

  /* Get some more memory if we don't have enough space for the
     terminating '\0'.   */
  if (ri == rlen)
    {
      rlen = (rlen ? rlen : 60) * 2;
      result = xrealloc (result, rlen);
    }

  result[ri] = '\0';

  return result;
}


/* Print a message on stdout.  */

void
#if HAVE_ANSI_COMPILER && USE_VARIADIC && HAVE_STDARG_H
message (int prefix, const char *fmt, ...)
#else
message (prefix, fmt, va_alist)
     int prefix;
     const char *fmt;
     va_dcl
#endif
{
#if USE_VARIADIC
  va_list args;
#endif

  log_working_directory (1);

  if (fmt != 0)
    {
      if (prefix)
    {
      if (makelevel == 0)
        printf ("%s: ", program);
      else
        printf ("%s[%u]: ", program, makelevel);
    }
      VA_START (args, fmt);
      VA_PRINTF (stdout, fmt, args);
      VA_END (args);
      putchar ('\n');
    }

  fflush (stdout);
}

/* Print an error message.  */

void
#if HAVE_ANSI_COMPILER && USE_VARIADIC && HAVE_STDARG_H
error (const struct floc *flocp, const char *fmt, ...)
#else
error (flocp, fmt, va_alist)
     const struct floc *flocp;
     const char *fmt;
     va_dcl
#endif
{
#if USE_VARIADIC
  va_list args;
#endif

  log_working_directory (1);

  if (flocp && flocp->filenm)
    fprintf (stderr, "%s:%lu: ", flocp->filenm, flocp->lineno);
  else if (makelevel == 0)
    fprintf (stderr, "%s: ", program);
  else
    fprintf (stderr, "%s[%u]: ", program, makelevel);

  VA_START(args, fmt);
  VA_PRINTF (stderr, fmt, args);
  VA_END (args);

  putc ('\n', stderr);
  fflush (stderr);
}

/* Print an error message and exit.  */

void
#if HAVE_ANSI_COMPILER && USE_VARIADIC && HAVE_STDARG_H
fatal (const struct floc *flocp, const char *fmt, ...)
#else
fatal (flocp, fmt, va_alist)
     const struct floc *flocp;
     const char *fmt;
     va_dcl
#endif
{
#if USE_VARIADIC
  va_list args;
#endif

  log_working_directory (1);

  if (flocp && flocp->filenm)
    fprintf (stderr, "%s:%lu: *** ", flocp->filenm, flocp->lineno);
  else if (makelevel == 0)
    fprintf (stderr, "%s: *** ", program);
  else
    fprintf (stderr, "%s[%u]: *** ", program, makelevel);

  VA_START(args, fmt);
  VA_PRINTF (stderr, fmt, args);
  VA_END (args);

  fputs (_(".  Stop.\n"), stderr);

  die (2);
}

#ifndef HAVE_STRERROR

#undef  strerror

char *
strerror (int errnum)
{
  extern int errno, sys_nerr;
#ifndef __DECC
  extern char *sys_errlist[];
#endif
  static char buf[] = "Unknown error 12345678901234567890";

  if (errno < sys_nerr)
    return sys_errlist[errnum];

  sprintf (buf, _("Unknown error %d"), errnum);
  return buf;
}
#endif

/* Print an error message from errno.  */

void
perror_with_name (const char *str, const char *name)
{
  error (NILF, _("%s%s: %s"), str, name, strerror (errno));
}

/* Print an error message from errno and exit.  */

void
pfatal_with_name (const char *name)
{
  fatal (NILF, _("%s: %s"), name, strerror (errno));

  /* NOTREACHED */
}


/* Like malloc but get fatal error if memory is exhausted.  */
/* Don't bother if we're using dmalloc; it provides these for us.  */

#ifndef HAVE_DMALLOC_H

#undef xmalloc
#undef xcalloc
#undef xrealloc
#undef xstrdup

void *
xmalloc (unsigned int size)
{
  /* Make sure we don't allocate 0, for pre-ISO implementations.  */
  void *result = malloc (size ? size : 1);
  if (result == 0)
    fatal (NILF, _("virtual memory exhausted"));
  return result;
}


void *
xcalloc (unsigned int size)
{
  /* Make sure we don't allocate 0, for pre-ISO implementations.  */
  void *result = calloc (size ? size : 1, 1);
  if (result == 0)
    fatal (NILF, _("virtual memory exhausted"));
  return result;
}


void *
xrealloc (void *ptr, unsigned int size)
{
  void *result;

  /* Some older implementations of realloc() don't conform to ISO.  */
  if (! size)
    size = 1;
  result = ptr ? realloc (ptr, size) : malloc (size);
  if (result == 0)
    fatal (NILF, _("virtual memory exhausted"));
  return result;
}


char *
xstrdup (const char *ptr)
{
  char *result;

#ifdef HAVE_STRDUP
  result = strdup (ptr);
#else
  result = malloc (strlen (ptr) + 1);
#endif

  if (result == 0)
    fatal (NILF, _("virtual memory exhausted"));

#ifdef HAVE_STRDUP
  return result;
#else
  return strcpy (result, ptr);
#endif
}

#endif  /* HAVE_DMALLOC_H */

char *
xstrndup (const char *str, unsigned int length)
{
  char *result;

#ifdef HAVE_STRNDUP
  result = strndup (str, length);
  if (result == 0)
    fatal (NILF, _("virtual memory exhausted"));
#else
  result = xmalloc (length + 1);
  if (length > 0)
    strncpy (result, str, length);
  result[length] = '\0';
#endif

  return result;
}



/* Limited INDEX:
   Search through the string STRING, which ends at LIMIT, for the character C.
   Returns a pointer to the first occurrence, or nil if none is found.
   Like INDEX except that the string searched ends where specified
   instead of at the first null.  */

char *
lindex (const char *s, const char *limit, int c)
{
  while (s < limit)
    if (*s++ == c)
      return (char *)(s - 1);

  return 0;
}


/* Return the address of the first whitespace or null in the string S.  */

char *
end_of_token (const char *s)
{
  while (*s != '\0' && !isblank ((unsigned char)*s))
    ++s;
  return (char *)s;
}

#ifdef WINDOWS32
/*
 * Same as end_of_token, but take into account a stop character
 */
char *
end_of_token_w32 (const char *s, char stopchar)
{
  const char *p = s;
  int backslash = 0;

  while (*p != '\0' && *p != stopchar
     && (backslash || !isblank ((unsigned char)*p)))
    {
      if (*p++ == '\\')
        {
          backslash = !backslash;
          while (*p == '\\')
            {
              backslash = !backslash;
              ++p;
            }
        }
      else
        backslash = 0;
    }

  return (char *)p;
}
#endif

/* Return the address of the first nonwhitespace or null in the string S.  */

char *
next_token (const char *s)
{
  while (isblank ((unsigned char)*s))
    ++s;
  return (char *)s;
}

/* Find the next token in PTR; return the address of it, and store the length
   of the token into *LENGTHPTR if LENGTHPTR is not nil.  Set *PTR to the end
   of the token, so this function can be called repeatedly in a loop.  */

char *
find_next_token (const char **ptr, unsigned int *lengthptr)
{
  const char *p = next_token (*ptr);

  if (*p == '\0')
    return 0;

  *ptr = end_of_token (p);
  if (lengthptr != 0)
    *lengthptr = *ptr - p;

  return (char *)p;
}



/* Copy a chain of `struct dep'.  For 2nd expansion deps, dup the name.  */

struct dep *
copy_dep_chain (const struct dep *d)
{
  struct dep *firstnew = 0;
  struct dep *lastnew = 0;

  while (d != 0)
    {
      struct dep *c = xmalloc (sizeof (struct dep));
      memcpy (c, d, sizeof (struct dep));

      if (c->need_2nd_expansion)
        c->name = xstrdup (c->name);

      c->next = 0;
      if (firstnew == 0)
    firstnew = lastnew = c;
      else
    lastnew = lastnew->next = c;

      d = d->next;
    }

  return firstnew;
}

/* Free a chain of 'struct dep'.  */

void
free_dep_chain (struct dep *d)
{
  while (d != 0)
    {
      struct dep *df = d;
      d = d->next;
      free_dep (df);
    }
}

/* Free a chain of struct nameseq.
   For struct dep chains use free_dep_chain.  */

void
free_ns_chain (struct nameseq *ns)
{
  while (ns != 0)
    {
      struct nameseq *t = ns;
      ns = ns->next;
      free (t);
    }
}



#if !HAVE_STRCASECMP && !HAVE_STRICMP && !HAVE_STRCMPI

/* If we don't have strcasecmp() (from POSIX), or anything that can substitute
   for it, define our own version.  */

int
strcasecmp (const char *s1, const char *s2)
{
  while (1)
    {
      int c1 = (int) *(s1++);
      int c2 = (int) *(s2++);

      if (isalpha (c1))
        c1 = tolower (c1);
      if (isalpha (c2))
        c2 = tolower (c2);

      if (c1 != '\0' && c1 == c2)
        continue;

      return (c1 - c2);
    }
}
#endif

#if !HAVE_STRNCASECMP && !HAVE_STRNICMP && !HAVE_STRNCMPI

/* If we don't have strncasecmp() (from POSIX), or anything that can
   substitute for it, define our own version.  */

int
strncasecmp (const char *s1, const char *s2, int n)
{
  while (n-- > 0)
    {
      int c1 = (int) *(s1++);
      int c2 = (int) *(s2++);

      if (isalpha (c1))
        c1 = tolower (c1);
      if (isalpha (c2))
        c2 = tolower (c2);

      if (c1 != '\0' && c1 == c2)
        continue;

      return (c1 - c2);
    }

  return 0;
}
#endif


#ifdef  GETLOADAVG_PRIVILEGED

#ifdef POSIX

/* Hopefully if a system says it's POSIX.1 and has the setuid and setgid
   functions, they work as POSIX.1 says.  Some systems (Alpha OSF/1 1.2,
   for example) which claim to be POSIX.1 also have the BSD setreuid and
   setregid functions, but they don't work as in BSD and only the POSIX.1
   way works.  */

#undef HAVE_SETREUID
#undef HAVE_SETREGID

#else   /* Not POSIX.  */

/* Some POSIX.1 systems have the seteuid and setegid functions.  In a
   POSIX-like system, they are the best thing to use.  However, some
   non-POSIX systems have them too but they do not work in the POSIX style
   and we must use setreuid and setregid instead.  */

#undef HAVE_SETEUID
#undef HAVE_SETEGID

#endif  /* POSIX.  */

#ifndef HAVE_UNISTD_H
extern int getuid (), getgid (), geteuid (), getegid ();
extern int setuid (), setgid ();
#ifdef HAVE_SETEUID
extern int seteuid ();
#else
#ifdef  HAVE_SETREUID
extern int setreuid ();
#endif  /* Have setreuid.  */
#endif  /* Have seteuid.  */
#ifdef HAVE_SETEGID
extern int setegid ();
#else
#ifdef  HAVE_SETREGID
extern int setregid ();
#endif  /* Have setregid.  */
#endif  /* Have setegid.  */
#endif  /* No <unistd.h>.  */

/* Keep track of the user and group IDs for user- and make- access.  */
static int user_uid = -1, user_gid = -1, make_uid = -1, make_gid = -1;
#define access_inited   (user_uid != -1)
static enum { make, user } current_access;


/* Under -d, write a message describing the current IDs.  */

static void
log_access (const char *flavor)
{
  if (! ISDB (DB_JOBS))
    return;

  /* All the other debugging messages go to stdout,
     but we write this one to stderr because it might be
     run in a child fork whose stdout is piped.  */

  fprintf (stderr, _("%s: user %lu (real %lu), group %lu (real %lu)\n"),
       flavor, (unsigned long) geteuid (), (unsigned long) getuid (),
           (unsigned long) getegid (), (unsigned long) getgid ());
  fflush (stderr);
}


static void
init_access (void)
{
#ifndef VMS
  user_uid = getuid ();
  user_gid = getgid ();

  make_uid = geteuid ();
  make_gid = getegid ();

  /* Do these ever fail?  */
  if (user_uid == -1 || user_gid == -1 || make_uid == -1 || make_gid == -1)
    pfatal_with_name ("get{e}[gu]id");

  log_access (_("Initialized access"));

  current_access = make;
#endif
}

#endif  /* GETLOADAVG_PRIVILEGED */

/* Give the process appropriate permissions for access to
   user data (i.e., to stat files, or to spawn a child process).  */
void
user_access (void)
{
#ifdef  GETLOADAVG_PRIVILEGED

  if (!access_inited)
    init_access ();

  if (current_access == user)
    return;

  /* We are in "make access" mode.  This means that the effective user and
     group IDs are those of make (if it was installed setuid or setgid).
     We now want to set the effective user and group IDs to the real IDs,
     which are the IDs of the process that exec'd make.  */

#ifdef  HAVE_SETEUID

  /* Modern systems have the seteuid/setegid calls which set only the
     effective IDs, which is ideal.  */

  if (seteuid (user_uid) < 0)
    pfatal_with_name ("user_access: seteuid");

#else   /* Not HAVE_SETEUID.  */

#ifndef HAVE_SETREUID

  /* System V has only the setuid/setgid calls to set user/group IDs.
     There is an effective ID, which can be set by setuid/setgid.
     It can be set (unless you are root) only to either what it already is
     (returned by geteuid/getegid, now in make_uid/make_gid),
     the real ID (return by getuid/getgid, now in user_uid/user_gid),
     or the saved set ID (what the effective ID was before this set-ID
     executable (make) was exec'd).  */

  if (setuid (user_uid) < 0)
    pfatal_with_name ("user_access: setuid");

#else   /* HAVE_SETREUID.  */

  /* In 4BSD, the setreuid/setregid calls set both the real and effective IDs.
     They may be set to themselves or each other.  So you have two alternatives
     at any one time.  If you use setuid/setgid, the effective will be set to
     the real, leaving only one alternative.  Using setreuid/setregid, however,
     you can toggle between your two alternatives by swapping the values in a
     single setreuid or setregid call.  */

  if (setreuid (make_uid, user_uid) < 0)
    pfatal_with_name ("user_access: setreuid");

#endif  /* Not HAVE_SETREUID.  */
#endif  /* HAVE_SETEUID.  */

#ifdef  HAVE_SETEGID
  if (setegid (user_gid) < 0)
    pfatal_with_name ("user_access: setegid");
#else
#ifndef HAVE_SETREGID
  if (setgid (user_gid) < 0)
    pfatal_with_name ("user_access: setgid");
#else
  if (setregid (make_gid, user_gid) < 0)
    pfatal_with_name ("user_access: setregid");
#endif
#endif

  current_access = user;

  log_access (_("User access"));

#endif  /* GETLOADAVG_PRIVILEGED */
}

/* Give the process appropriate permissions for access to
   make data (i.e., the load average).  */
void
make_access (void)
{
#ifdef  GETLOADAVG_PRIVILEGED

  if (!access_inited)
    init_access ();

  if (current_access == make)
    return;

  /* See comments in user_access, above.  */

#ifdef  HAVE_SETEUID
  if (seteuid (make_uid) < 0)
    pfatal_with_name ("make_access: seteuid");
#else
#ifndef HAVE_SETREUID
  if (setuid (make_uid) < 0)
    pfatal_with_name ("make_access: setuid");
#else
  if (setreuid (user_uid, make_uid) < 0)
    pfatal_with_name ("make_access: setreuid");
#endif
#endif

#ifdef  HAVE_SETEGID
  if (setegid (make_gid) < 0)
    pfatal_with_name ("make_access: setegid");
#else
#ifndef HAVE_SETREGID
  if (setgid (make_gid) < 0)
    pfatal_with_name ("make_access: setgid");
#else
  if (setregid (user_gid, make_gid) < 0)
    pfatal_with_name ("make_access: setregid");
#endif
#endif

  current_access = make;

  log_access (_("Make access"));

#endif  /* GETLOADAVG_PRIVILEGED */
}

/* Give the process appropriate permissions for a child process.
   This is like user_access, but you can't get back to make_access.  */
void
child_access (void)
{
#ifdef  GETLOADAVG_PRIVILEGED

  if (!access_inited)
    abort ();

  /* Set both the real and effective UID and GID to the user's.
     They cannot be changed back to make's.  */

#ifndef HAVE_SETREUID
  if (setuid (user_uid) < 0)
    pfatal_with_name ("child_access: setuid");
#else
  if (setreuid (user_uid, user_uid) < 0)
    pfatal_with_name ("child_access: setreuid");
#endif

#ifndef HAVE_SETREGID
  if (setgid (user_gid) < 0)
    pfatal_with_name ("child_access: setgid");
#else
  if (setregid (user_gid, user_gid) < 0)
    pfatal_with_name ("child_access: setregid");
#endif

  log_access (_("Child access"));

#endif  /* GETLOADAVG_PRIVILEGED */
}


#ifdef NEED_GET_PATH_MAX
unsigned int
get_path_max (void)
{
  static unsigned int value;

  if (value == 0)
    {
      long int x = pathconf ("/", _PC_PATH_MAX);
      if (x > 0)
    value = x;
      else
    return MAXPATHLEN;
    }

  return value;
}
#endif



/* This code is stolen from gnulib.
   If/when we abandon the requirement to work with K&R compilers, we can
   remove this (and perhaps other parts of GNU make!) and migrate to using
   gnulib directly.

   This is called only through atexit(), which means die() has already been
   invoked.  So, call exit() here directly.  Apparently that works...?
*/

/* Close standard output, exiting with status 'exit_failure' on failure.
   If a program writes *anything* to stdout, that program should close
   stdout and make sure that it succeeds before exiting.  Otherwise,
   suppose that you go to the extreme of checking the return status
   of every function that does an explicit write to stdout.  The last
   printf can succeed in writing to the internal stream buffer, and yet
   the fclose(stdout) could still fail (due e.g., to a disk full error)
   when it tries to write out that buffered data.  Thus, you would be
   left with an incomplete output file and the offending program would
   exit successfully.  Even calling fflush is not always sufficient,
   since some file systems (NFS and CODA) buffer written/flushed data
   until an actual close call.

   Besides, it's wasteful to check the return value from every call
   that writes to stdout -- just let the internal stream state record
   the failure.  That's what the ferror test is checking below.

   It's important to detect such failures and exit nonzero because many
   tools (most notably `make' and other build-management systems) depend
   on being able to detect failure in other tools via their exit status.  */

void
close_stdout (void)
{
  int prev_fail = ferror (stdout);
  int fclose_fail = fclose (stdout);

  if (prev_fail || fclose_fail)
    {
      if (fclose_fail)
        error (NILF, _("write error: %s"), strerror (errno));
      else
        error (NILF, _("write error"));
      exit (EXIT_FAILURE);
    }
}

https://git.savannah.gnu.org/cgit/make.git/tree/read.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/read.c used in: Step 529
/* Reading and parsing of makefiles for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"

#include <assert.h>

#include <glob.h>

#include "dep.h"
#include "filedef.h"
#include "job.h"
#include "commands.h"
#include "variable.h"
#include "rule.h"
#include "debug.h"
#include "hash.h"


#ifndef WINDOWS32
#ifndef _AMIGA
#ifndef VMS
#include <pwd.h>
#else
struct passwd *getpwnam (char *name);
#endif
#endif
#endif /* !WINDOWS32 */

/* A 'struct ebuffer' controls the origin of the makefile we are currently
   eval'ing.
*/

struct ebuffer
  {
    char *buffer;       /* Start of the current line in the buffer.  */
    char *bufnext;      /* Start of the next line in the buffer.  */
    char *bufstart;     /* Start of the entire buffer.  */
    unsigned int size;  /* Malloc'd size of buffer. */
    FILE *fp;           /* File, or NULL if this is an internal buffer.  */
    struct floc floc;   /* Info on the file in fp (if any).  */
  };

/* Track the modifiers we can have on variable assignments */

struct vmodifiers
  {
    unsigned int assign_v:1;
    unsigned int define_v:1;
    unsigned int undefine_v:1;
    unsigned int export_v:1;
    unsigned int override_v:1;
    unsigned int private_v:1;
  };

/* Types of "words" that can be read in a makefile.  */
enum make_word_type
  {
     w_bogus, w_eol, w_static, w_variable, w_colon, w_dcolon, w_semicolon,
     w_varassign
  };


/* A `struct conditionals' contains the information describing
   all the active conditionals in a makefile.

   The global variable `conditionals' contains the conditionals
   information for the current makefile.  It is initialized from
   the static structure `toplevel_conditionals' and is later changed
   to new structures for included makefiles.  */

struct conditionals
  {
    unsigned int if_cmds;   /* Depth of conditional nesting.  */
    unsigned int allocated; /* Elts allocated in following arrays.  */
    char *ignoring;     /* Are we ignoring or interpreting?
                                   0=interpreting, 1=not yet interpreted,
                                   2=already interpreted */
    char *seen_else;        /* Have we already seen an `else'?  */
  };

static struct conditionals toplevel_conditionals;
static struct conditionals *conditionals = &toplevel_conditionals;


/* Default directories to search for include files in  */

static const char *default_include_directories[] =
  {
#if defined(WINDOWS32) && !defined(INCLUDEDIR)
/* This completely up to the user when they install MSVC or other packages.
   This is defined as a placeholder.  */
# define INCLUDEDIR "."
#endif
    INCLUDEDIR,
#ifndef _AMIGA
    "/usr/gnu/include",
    "/usr/local/include",
    "/usr/include",
#endif
    0
  };

/* List of directories to search for include files in  */

static const char **include_directories;

/* Maximum length of an element of the above.  */

static unsigned int max_incl_len;

/* The filename and pointer to line number of the
   makefile currently being read in.  */

const struct floc *reading_file = 0;

/* The chain of makefiles read by read_makefile.  */

static struct dep *read_makefiles = 0;

static int eval_makefile (const char *filename, int flags);
static void eval (struct ebuffer *buffer, int flags);

static long readline (struct ebuffer *ebuf);
static void do_undefine (char *name, enum variable_origin origin,
                         struct ebuffer *ebuf);
static struct variable *do_define (char *name, enum variable_origin origin,
                                   struct ebuffer *ebuf);
static int conditional_line (char *line, int len, const struct floc *flocp);
static void record_files (struct nameseq *filenames, const char *pattern,
                          const char *pattern_percent, char *depstr,
                          unsigned int cmds_started, char *commands,
                          unsigned int commands_idx, int two_colon,
                          const struct floc *flocp);
static void record_target_var (struct nameseq *filenames, char *defn,
                               enum variable_origin origin,
                               struct vmodifiers *vmod,
                               const struct floc *flocp);
static enum make_word_type get_next_mword (char *buffer, char *delim,
                                           char **startp, unsigned int *length);
static void remove_comments (char *line);
static char *find_char_unquote (char *string, int stop1, int stop2,
                                int blank, int ignorevars);


/* Compare a word, both length and contents.
   P must point to the word to be tested, and WLEN must be the length.
*/
#define word1eq(s)  (wlen == sizeof(s)-1 && strneq (s, p, sizeof(s)-1))



/* Read in all the makefiles and return the chain of their names.  */

struct dep *
read_all_makefiles (const char **makefiles)
{
  unsigned int num_makefiles = 0;

  /* Create *_LIST variables, to hold the makefiles, targets, and variables
     we will be reading. */

  define_variable_cname ("MAKEFILE_LIST", "", o_file, 0);

  DB (DB_BASIC, (_("Reading makefiles...\n")));

  /* If there's a non-null variable MAKEFILES, its value is a list of
     files to read first thing.  But don't let it prevent reading the
     default makefiles and don't let the default goal come from there.  */

  {
    char *value;
    char *name, *p;
    unsigned int length;

    {
      /* Turn off --warn-undefined-variables while we expand MAKEFILES.  */
      int save = warn_undefined_variables_flag;
      warn_undefined_variables_flag = 0;

      value = allocated_variable_expand ("$(MAKEFILES)");

      warn_undefined_variables_flag = save;
    }

    /* Set NAME to the start of next token and LENGTH to its length.
       MAKEFILES is updated for finding remaining tokens.  */
    p = value;

    while ((name = find_next_token ((const char **)&p, &length)) != 0)
      {
    if (*p != '\0')
      *p++ = '\0';
    eval_makefile (name, RM_NO_DEFAULT_GOAL|RM_INCLUDED|RM_DONTCARE);
      }

    free (value);
  }

  /* Read makefiles specified with -f switches.  */

  if (makefiles != 0)
    while (*makefiles != 0)
      {
    struct dep *tail = read_makefiles;
    register struct dep *d;

    if (! eval_makefile (*makefiles, 0))
      perror_with_name ("", *makefiles);

    /* Find the right element of read_makefiles.  */
    d = read_makefiles;
    while (d->next != tail)
      d = d->next;

    /* Use the storage read_makefile allocates.  */
    *makefiles = dep_name (d);
    ++num_makefiles;
    ++makefiles;
      }

  /* If there were no -f switches, try the default names.  */

  if (num_makefiles == 0)
    {
      static char *default_makefiles[] =
#ifdef VMS
    /* all lower case since readdir() (the vms version) 'lowercasifies' */
    { "makefile.vms", "gnumakefile.", "makefile.", 0 };
#else
#ifdef _AMIGA
    { "GNUmakefile", "Makefile", "SMakefile", 0 };
#else /* !Amiga && !VMS */
    { "GNUmakefile", "makefile", "Makefile", 0 };
#endif /* AMIGA */
#endif /* VMS */
      register char **p = default_makefiles;
      while (*p != 0 && !file_exists_p (*p))
    ++p;

      if (*p != 0)
    {
      if (! eval_makefile (*p, 0))
        perror_with_name ("", *p);
    }
      else
    {
      /* No default makefile was found.  Add the default makefiles to the
         `read_makefiles' chain so they will be updated if possible.  */
      struct dep *tail = read_makefiles;
      /* Add them to the tail, after any MAKEFILES variable makefiles.  */
      while (tail != 0 && tail->next != 0)
        tail = tail->next;
      for (p = default_makefiles; *p != 0; ++p)
        {
          struct dep *d = alloc_dep ();
          d->file = enter_file (strcache_add (*p));
          d->dontcare = 1;
          /* Tell update_goal_chain to bail out as soon as this file is
         made, and main not to die if we can't make this file.  */
          d->changed = RM_DONTCARE;
          if (tail == 0)
        read_makefiles = d;
          else
        tail->next = d;
          tail = d;
        }
      if (tail != 0)
        tail->next = 0;
    }
    }

  return read_makefiles;
}


/* Install a new conditional and return the previous one.  */

static struct conditionals *
install_conditionals (struct conditionals *new)
{
  struct conditionals *save = conditionals;

  memset (new, '\0', sizeof (*new));
  conditionals = new;

  return save;
}

/* Free the current conditionals and reinstate a saved one.  */

static void
restore_conditionals (struct conditionals *saved)
{
  /* Free any space allocated by conditional_line.  */
  if (conditionals->ignoring)
    free (conditionals->ignoring);
  if (conditionals->seen_else)
    free (conditionals->seen_else);

  /* Restore state.  */
  conditionals = saved;
}


static int
eval_makefile (const char *filename, int flags)
{
  struct dep *deps;
  struct ebuffer ebuf;
  const struct floc *curfile;
  char *expanded = 0;
  int makefile_errno;

  filename = strcache_add (filename);
  ebuf.floc.filenm = filename;
  ebuf.floc.lineno = 1;

  if (ISDB (DB_VERBOSE))
    {
      printf (_("Reading makefile `%s'"), filename);
      if (flags & RM_NO_DEFAULT_GOAL)
    printf (_(" (no default goal)"));
      if (flags & RM_INCLUDED)
    printf (_(" (search path)"));
      if (flags & RM_DONTCARE)
    printf (_(" (don't care)"));
      if (flags & RM_NO_TILDE)
    printf (_(" (no ~ expansion)"));
      puts ("...");
    }

  /* First, get a stream to read.  */

  /* Expand ~ in FILENAME unless it came from `include',
     in which case it was already done.  */
  if (!(flags & RM_NO_TILDE) && filename[0] == '~')
    {
      expanded = tilde_expand (filename);
      if (expanded != 0)
    filename = expanded;
    }

  ebuf.fp = fopen (filename, "r");
  /* Save the error code so we print the right message later.  */
  makefile_errno = errno;

  /* If the makefile wasn't found and it's either a makefile from
     the `MAKEFILES' variable or an included makefile,
     search the included makefile search path for this makefile.  */
  if (ebuf.fp == 0 && (flags & RM_INCLUDED) && *filename != '/')
    {
      unsigned int i;
      for (i = 0; include_directories[i] != 0; ++i)
    {
      const char *included = concat (3, include_directories[i],
                                         "/", filename);
      ebuf.fp = fopen (included, "r");
      if (ebuf.fp)
        {
          filename = strcache_add (included);
          break;
        }
    }
    }

  /* Add FILENAME to the chain of read makefiles.  */
  deps = alloc_dep ();
  deps->next = read_makefiles;
  read_makefiles = deps;
  deps->file = lookup_file (filename);
  if (deps->file == 0)
    deps->file = enter_file (filename);
  filename = deps->file->name;
  deps->changed = flags;
  if (flags & RM_DONTCARE)
    deps->dontcare = 1;

  if (expanded)
    free (expanded);

  /* If the makefile can't be found at all, give up entirely.  */

  if (ebuf.fp == 0)
    {
      /* If we did some searching, errno has the error from the last
     attempt, rather from FILENAME itself.  Restore it in case the
     caller wants to use it in a message.  */
      errno = makefile_errno;
      return 0;
    }

  /* Set close-on-exec to avoid leaking the makefile to children, such as
     $(shell ...).  */
#ifdef HAVE_FILENO
  CLOSE_ON_EXEC (fileno (ebuf.fp));
#endif

  /* Add this makefile to the list. */
  do_variable_definition (&ebuf.floc, "MAKEFILE_LIST", filename, o_file,
                          f_append, 0);

  /* Evaluate the makefile */

  ebuf.size = 200;
  ebuf.buffer = ebuf.bufnext = ebuf.bufstart = xmalloc (ebuf.size);

  curfile = reading_file;
  reading_file = &ebuf.floc;

  eval (&ebuf, !(flags & RM_NO_DEFAULT_GOAL));

  reading_file = curfile;

  fclose (ebuf.fp);

  free (ebuf.bufstart);
  alloca (0);

  return 1;
}

void
eval_buffer (char *buffer)
{
  struct ebuffer ebuf;
  struct conditionals *saved;
  struct conditionals new;
  const struct floc *curfile;

  /* Evaluate the buffer */

  ebuf.size = strlen (buffer);
  ebuf.buffer = ebuf.bufnext = ebuf.bufstart = buffer;
  ebuf.fp = NULL;

  if (reading_file)
    ebuf.floc = *reading_file;
  else
    ebuf.floc.filenm = NULL;

  curfile = reading_file;
  reading_file = &ebuf.floc;

  saved = install_conditionals (&new);

  eval (&ebuf, 1);

  restore_conditionals (saved);

  reading_file = curfile;

  alloca (0);
}


/* Check LINE to see if it's a variable assignment or undefine.

   It might use one of the modifiers "export", "override", "private", or it
   might be one of the conditional tokens like "ifdef", "include", etc.

   If it's not a variable assignment or undefine, VMOD.V_ASSIGN is 0.
   Returns LINE.

   Returns a pointer to the first non-modifier character, and sets VMOD
   based on the modifiers found if any, plus V_ASSIGN is 1.
 */
static char *
parse_var_assignment (const char *line, struct vmodifiers *vmod)
{
  const char *p;
  memset (vmod, '\0', sizeof (*vmod));

  /* Find the start of the next token.  If there isn't one we're done.  */
  line = next_token (line);
  if (*line == '\0')
    return (char *)line;

  p = line;
  while (1)
    {
      int wlen;
      const char *p2;
      enum variable_flavor flavor;

      p2 = parse_variable_definition (p, &flavor);

      /* If this is a variable assignment, we're done.  */
      if (p2)
        break;

      /* It's not a variable; see if it's a modifier.  */
      p2 = end_of_token (p);
      wlen = p2 - p;

      if (word1eq ("export"))
        vmod->export_v = 1;
      else if (word1eq ("override"))
        vmod->override_v = 1;
      else if (word1eq ("private"))
        vmod->private_v = 1;
      else if (word1eq ("define"))
        {
          /* We can't have modifiers after 'define' */
          vmod->define_v = 1;
          p = next_token (p2);
          break;
        }
      else if (word1eq ("undefine"))
        {
          /* We can't have modifiers after 'undefine' */
          vmod->undefine_v = 1;
          p = next_token (p2);
          break;
        }
      else
        /* Not a variable or modifier: this is not a variable assignment.  */
        return (char *)line;

      /* It was a modifier.  Try the next word.  */
      p = next_token (p2);
      if (*p == '\0')
        return (char *)line;
    }

  /* Found a variable assignment or undefine.  */
  vmod->assign_v = 1;
  return (char *)p;
}



/* Read file FILENAME as a makefile and add its contents to the data base.

   SET_DEFAULT is true if we are allowed to set the default goal.  */

static void
eval (struct ebuffer *ebuf, int set_default)
{
  char *collapsed = 0;
  unsigned int collapsed_length = 0;
  unsigned int commands_len = 200;
  char *commands;
  unsigned int commands_idx = 0;
  unsigned int cmds_started, tgts_started;
  int ignoring = 0, in_ignored_define = 0;
  int no_targets = 0;       /* Set when reading a rule without targets.  */
  struct nameseq *filenames = 0;
  char *depstr = 0;
  long nlines = 0;
  int two_colon = 0;
  const char *pattern = 0;
  const char *pattern_percent;
  struct floc *fstart;
  struct floc fi;

#define record_waiting_files()                            \
  do                                          \
    {                                         \
      if (filenames != 0)                             \
        {                                                                     \
      fi.lineno = tgts_started;                                           \
      record_files (filenames, pattern, pattern_percent, depstr,          \
                        cmds_started, commands, commands_idx, two_colon,      \
                        &fi);                                                 \
          filenames = 0;                              \
        }                                                                     \
      commands_idx = 0;                               \
      no_targets = 0;                                                         \
      pattern = 0;                                                            \
    } while (0)

  pattern_percent = 0;
  cmds_started = tgts_started = 1;

  fstart = &ebuf->floc;
  fi.filenm = ebuf->floc.filenm;

  /* Loop over lines in the file.
     The strategy is to accumulate target names in FILENAMES, dependencies
     in DEPS and commands in COMMANDS.  These are used to define a rule
     when the start of the next rule (or eof) is encountered.

     When you see a "continue" in the loop below, that means we are moving on
     to the next line _without_ ending any rule that we happen to be working
     with at the moment.  If you see a "goto rule_complete", then the
     statement we just parsed also finishes the previous rule.  */

  commands = xmalloc (200);

  while (1)
    {
      unsigned int linelen;
      char *line;
      unsigned int wlen;
      char *p;
      char *p2;
      struct vmodifiers vmod;

      /* At the top of this loop, we are starting a brand new line.  */
      /* Grab the next line to be evaluated */
      ebuf->floc.lineno += nlines;
      nlines = readline (ebuf);

      /* If there is nothing left to eval, we're done.  */
      if (nlines < 0)
        break;

      /* If this line is empty, skip it.  */
      line = ebuf->buffer;
      if (line[0] == '\0')
        continue;

      linelen = strlen (line);

      /* Check for a shell command line first.
     If it is not one, we can stop treating tab specially.  */
      if (line[0] == cmd_prefix)
    {
      if (no_targets)
        /* Ignore the commands in a rule with no targets.  */
        continue;

      /* If there is no preceding rule line, don't treat this line
         as a command, even though it begins with a recipe prefix.
         SunOS 4 make appears to behave this way.  */

      if (filenames != 0)
        {
          if (ignoring)
        /* Yep, this is a shell command, and we don't care.  */
        continue;

          /* Append this command line to the line being accumulated.
                 Strip command prefix chars that appear after newlines.  */
          if (commands_idx == 0)
        cmds_started = ebuf->floc.lineno;

          if (linelen + commands_idx > commands_len)
        {
          commands_len = (linelen + commands_idx) * 2;
          commands = xrealloc (commands, commands_len);
        }
              p = &commands[commands_idx];
              p2 = line + 1;
              while (--linelen)
                {
                  ++commands_idx;
                  *(p++) = *p2;
                  if (p2[0] == '\n' && p2[1] == cmd_prefix)
                    {
                      ++p2;
                      --linelen;
                    }
                  ++p2;
                }
              *p = '\n';
              ++commands_idx;

          continue;
        }
    }

      /* This line is not a shell command line.  Don't worry about whitespace.
         Get more space if we need it; we don't need to preserve the current
         contents of the buffer.  */

      if (collapsed_length < linelen+1)
    {
      collapsed_length = linelen+1;
          if (collapsed)
            free (collapsed);
          /* Don't need xrealloc: we don't need to preserve the content.  */
      collapsed = xmalloc (collapsed_length);
    }
      strcpy (collapsed, line);
      /* Collapse continuation lines.  */
      collapse_continuations (collapsed);
      remove_comments (collapsed);

      /* Get rid if starting space (including formfeed, vtab, etc.)  */
      p = collapsed;
      while (isspace ((unsigned char)*p))
        ++p;

      /* See if this is a variable assignment.  We need to do this early, to
         allow variables with names like 'ifdef', 'export', 'private', etc.  */
      p = parse_var_assignment(p, &vmod);
      if (vmod.assign_v)
        {
          struct variable *v;
          enum variable_origin origin = vmod.override_v ? o_override : o_file;

          /* If we're ignoring then we're done now.  */
      if (ignoring)
            {
              if (vmod.define_v)
                in_ignored_define = 1;
              continue;
            }

          if (vmod.undefine_v)
          {
            do_undefine (p, origin, ebuf);

            /* This line has been dealt with.  */
            goto rule_complete;
          }
          else if (vmod.define_v)
            v = do_define (p, origin, ebuf);
          else
            v = try_variable_definition (fstart, p, origin, 0);

          assert (v != NULL);

          if (vmod.export_v)
            v->export = v_export;
          if (vmod.private_v)
            v->private_var = 1;

          /* This line has been dealt with.  */
          goto rule_complete;
        }

      /* If this line is completely empty, ignore it.  */
      if (*p == '\0')
    continue;

      p2 = end_of_token (p);
      wlen = p2 - p;
      p2 = next_token (p2);

      /* If we're in an ignored define, skip this line (but maybe get out).  */
      if (in_ignored_define)
    {
          /* See if this is an endef line (plus optional comment).  */
          if (word1eq ("endef") && (*p2 == '\0' || *p2 == '#'))
            in_ignored_define = 0;

      continue;
    }

      /* Check for conditional state changes.  */
      {
        int i = conditional_line (p, wlen, fstart);
        if (i != -2)
          {
            if (i == -1)
              fatal (fstart, _("invalid syntax in conditional"));

            ignoring = i;
            continue;
          }
      }

      /* Nothing to see here... move along.  */
      if (ignoring)
    continue;

      /* Manage the "export" keyword used outside of variable assignment
         as well as "unexport".  */
      if (word1eq ("export") || word1eq ("unexport"))
    {
          int exporting = *p == 'u' ? 0 : 1;

          /* (un)export by itself causes everything to be (un)exported. */
      if (*p2 == '\0')
            export_all_variables = exporting;
          else
            {
              unsigned int l;
              const char *cp;
              char *ap;

              /* Expand the line so we can use indirect and constructed
                 variable names in an (un)export command.  */
              cp = ap = allocated_variable_expand (p2);

              for (p = find_next_token (&cp, &l); p != 0;
                   p = find_next_token (&cp, &l))
                {
                  struct variable *v = lookup_variable (p, l);
                  if (v == 0)
                    v = define_variable_loc (p, l, "", o_file, 0, fstart);
                  v->export = exporting ? v_export : v_noexport;
                }

              free (ap);
            }
          goto rule_complete;
    }

      /* Handle the special syntax for vpath.  */
      if (word1eq ("vpath"))
    {
          const char *cp;
      char *vpat;
      unsigned int l;
      cp = variable_expand (p2);
      p = find_next_token (&cp, &l);
      if (p != 0)
        {
          vpat = xstrndup (p, l);
          p = find_next_token (&cp, &l);
          /* No searchpath means remove all previous
         selective VPATH's with the same pattern.  */
        }
      else
        /* No pattern means remove all previous selective VPATH's.  */
        vpat = 0;
      construct_vpath_list (vpat, p);
      if (vpat != 0)
        free (vpat);

          goto rule_complete;
    }

      /* Handle include and variants.  */
      if (word1eq ("include") || word1eq ("-include") || word1eq ("sinclude"))
    {
      /* We have found an `include' line specifying a nested
         makefile to be read at this point.  */
      struct conditionals *save;
          struct conditionals new_conditionals;
      struct nameseq *files;
      /* "-include" (vs "include") says no error if the file does not
         exist.  "sinclude" is an alias for this from SGI.  */
      int noerror = (p[0] != 'i');

      p = allocated_variable_expand (p2);

          /* If no filenames, it's a no-op.  */
      if (*p == '\0')
            {
              free (p);
              continue;
            }

      /* Parse the list of file names.  Don't expand archive references!  */
      p2 = p;
      files = PARSE_FILE_SEQ (&p2, struct nameseq, '\0', NULL,
                                  PARSEFS_NOAR);
      free (p);

      /* Save the state of conditionals and start
         the included makefile with a clean slate.  */
      save = install_conditionals (&new_conditionals);

      /* Record the rules that are waiting so they will determine
         the default goal before those in the included makefile.  */
      record_waiting_files ();

      /* Read each included makefile.  */
      while (files != 0)
        {
          struct nameseq *next = files->next;
          const char *name = files->name;
              int r;

          free_ns (files);
          files = next;

              r = eval_makefile (name,
                                 (RM_INCLUDED | RM_NO_TILDE
                                  | (noerror ? RM_DONTCARE : 0)
                                  | (set_default ? 0 : RM_NO_DEFAULT_GOAL)));
          if (!r && !noerror)
                error (fstart, "%s: %s", name, strerror (errno));
        }

      /* Restore conditional state.  */
      restore_conditionals (save);

          goto rule_complete;
    }

      /* This line starts with a tab but was not caught above because there
         was no preceding target, and the line might have been usable as a
         variable definition.  But now we know it is definitely lossage.  */
      if (line[0] == cmd_prefix)
        fatal(fstart, _("recipe commences before first target"));

      /* This line describes some target files.  This is complicated by
         the existence of target-specific variables, because we can't
         expand the entire line until we know if we have one or not.  So
         we expand the line word by word until we find the first `:',
         then check to see if it's a target-specific variable.

         In this algorithm, `lb_next' will point to the beginning of the
         unexpanded parts of the input buffer, while `p2' points to the
         parts of the expanded buffer we haven't searched yet. */

      {
        enum make_word_type wtype;
        char *cmdleft, *semip, *lb_next;
        unsigned int plen = 0;
        char *colonp;
        const char *end, *beg; /* Helpers for whitespace stripping. */

        /* Record the previous rule.  */

        record_waiting_files ();
        tgts_started = fstart->lineno;

        /* Search the line for an unquoted ; that is not after an
           unquoted #.  */
        cmdleft = find_char_unquote (line, ';', '#', 0, 1);
        if (cmdleft != 0 && *cmdleft == '#')
          {
            /* We found a comment before a semicolon.  */
            *cmdleft = '\0';
            cmdleft = 0;
          }
        else if (cmdleft != 0)
          /* Found one.  Cut the line short there before expanding it.  */
          *(cmdleft++) = '\0';
        semip = cmdleft;

        collapse_continuations (line);

        /* We can't expand the entire line, since if it's a per-target
           variable we don't want to expand it.  So, walk from the
           beginning, expanding as we go, and looking for "interesting"
           chars.  The first word is always expandable.  */
        wtype = get_next_mword(line, NULL, &lb_next, &wlen);
        switch (wtype)
          {
          case w_eol:
            if (cmdleft != 0)
              fatal(fstart, _("missing rule before recipe"));
            /* This line contained something but turned out to be nothing
               but whitespace (a comment?).  */
            continue;

          case w_colon:
          case w_dcolon:
            /* We accept and ignore rules without targets for
               compatibility with SunOS 4 make.  */
            no_targets = 1;
            continue;

          default:
            break;
          }

        p2 = variable_expand_string(NULL, lb_next, wlen);

        while (1)
          {
            lb_next += wlen;
            if (cmdleft == 0)
              {
                /* Look for a semicolon in the expanded line.  */
                cmdleft = find_char_unquote (p2, ';', 0, 0, 0);

                if (cmdleft != 0)
                  {
                    unsigned long p2_off = p2 - variable_buffer;
                    unsigned long cmd_off = cmdleft - variable_buffer;
                    char *pend = p2 + strlen(p2);

                    /* Append any remnants of lb, then cut the line short
                       at the semicolon.  */
                    *cmdleft = '\0';

                    /* One school of thought says that you shouldn't expand
                       here, but merely copy, since now you're beyond a ";"
                       and into a command script.  However, the old parser
                       expanded the whole line, so we continue that for
                       backwards-compatiblity.  Also, it wouldn't be
                       entirely consistent, since we do an unconditional
                       expand below once we know we don't have a
                       target-specific variable. */
                    (void)variable_expand_string(pend, lb_next, (long)-1);
                    lb_next += strlen(lb_next);
                    p2 = variable_buffer + p2_off;
                    cmdleft = variable_buffer + cmd_off + 1;
                  }
              }

            colonp = find_char_unquote(p2, ':', 0, 0, 0);
#ifdef HAVE_DOS_PATHS
            /* The drive spec brain-damage strikes again...  */
            /* Note that the only separators of targets in this context
               are whitespace and a left paren.  If others are possible,
               they should be added to the string in the call to index.  */
            while (colonp && (colonp[1] == '/' || colonp[1] == '\\') &&
                   colonp > p2 && isalpha ((unsigned char)colonp[-1]) &&
                   (colonp == p2 + 1 || strchr (" \t(", colonp[-2]) != 0))
              colonp = find_char_unquote(colonp + 1, ':', 0, 0, 0);
#endif
            if (colonp != 0)
              break;

            wtype = get_next_mword(lb_next, NULL, &lb_next, &wlen);
            if (wtype == w_eol)
              break;

            p2 += strlen(p2);
            *(p2++) = ' ';
            p2 = variable_expand_string(p2, lb_next, wlen);
            /* We don't need to worry about cmdleft here, because if it was
               found in the variable_buffer the entire buffer has already
               been expanded... we'll never get here.  */
          }

        p2 = next_token (variable_buffer);

        /* If the word we're looking at is EOL, see if there's _anything_
           on the line.  If not, a variable expanded to nothing, so ignore
           it.  If so, we can't parse this line so punt.  */
        if (wtype == w_eol)
          {
            if (*p2 != '\0')
              /* There's no need to be ivory-tower about this: check for
                 one of the most common bugs found in makefiles...  */
              fatal (fstart, _("missing separator%s"),
                     (cmd_prefix == '\t' && !strneq(line, "        ", 8))
                     ? "" : _(" (did you mean TAB instead of 8 spaces?)"));
            continue;
          }

        /* Make the colon the end-of-string so we know where to stop
           looking for targets.  */
        *colonp = '\0';
        filenames = PARSE_FILE_SEQ (&p2, struct nameseq, '\0', NULL, 0);
        *p2 = ':';

        if (!filenames)
          {
            /* We accept and ignore rules without targets for
               compatibility with SunOS 4 make.  */
            no_targets = 1;
            continue;
          }
        /* This should never be possible; we handled it above.  */
        assert (*p2 != '\0');
        ++p2;

        /* Is this a one-colon or two-colon entry?  */
        two_colon = *p2 == ':';
        if (two_colon)
          p2++;

        /* Test to see if it's a target-specific variable.  Copy the rest
           of the buffer over, possibly temporarily (we'll expand it later
           if it's not a target-specific variable).  PLEN saves the length
           of the unparsed section of p2, for later.  */
        if (*lb_next != '\0')
          {
            unsigned int l = p2 - variable_buffer;
            plen = strlen (p2);
            variable_buffer_output (p2+plen, lb_next, strlen (lb_next)+1);
            p2 = variable_buffer + l;
          }

        p2 = parse_var_assignment (p2, &vmod);
        if (vmod.assign_v)
          {
            /* If there was a semicolon found, add it back, plus anything
               after it.  */
            if (semip)
              {
                unsigned int l = p - variable_buffer;
                *(--semip) = ';';
                collapse_continuations (semip);
                variable_buffer_output (p2 + strlen (p2),
                                        semip, strlen (semip)+1);
                p = variable_buffer + l;
              }
            record_target_var (filenames, p2,
                               vmod.override_v ? o_override : o_file,
                               &vmod, fstart);
            filenames = 0;
            continue;
          }

        /* This is a normal target, _not_ a target-specific variable.
           Unquote any = in the dependency list.  */
        find_char_unquote (lb_next, '=', 0, 0, 0);

        /* We have some targets, so don't ignore the following commands.  */
        no_targets = 0;

        /* Expand the dependencies, etc.  */
        if (*lb_next != '\0')
          {
            unsigned int l = p2 - variable_buffer;
            (void) variable_expand_string (p2 + plen, lb_next, (long)-1);
            p2 = variable_buffer + l;

            /* Look for a semicolon in the expanded line.  */
            if (cmdleft == 0)
              {
                cmdleft = find_char_unquote (p2, ';', 0, 0, 0);
                if (cmdleft != 0)
                  *(cmdleft++) = '\0';
              }
          }

        /* Is this a static pattern rule: `target: %targ: %dep; ...'?  */
        p = strchr (p2, ':');
        while (p != 0 && p[-1] == '\\')
          {
            char *q = &p[-1];
            int backslash = 0;
            while (*q-- == '\\')
              backslash = !backslash;
            if (backslash)
              p = strchr (p + 1, ':');
            else
              break;
          }
#ifdef _AMIGA
        /* Here, the situation is quite complicated. Let's have a look
           at a couple of targets:

           install: dev:make

           dev:make: make

           dev:make:: xyz

           The rule is that it's only a target, if there are TWO :'s
           OR a space around the :.
        */
        if (p && !(isspace ((unsigned char)p[1]) || !p[1]
                   || isspace ((unsigned char)p[-1])))
          p = 0;
#endif
#ifdef HAVE_DOS_PATHS
        {
          int check_again;
          do {
            check_again = 0;
            /* For DOS-style paths, skip a "C:\..." or a "C:/..." */
            if (p != 0 && (p[1] == '\\' || p[1] == '/') &&
                isalpha ((unsigned char)p[-1]) &&
                (p == p2 + 1 || strchr (" \t:(", p[-2]) != 0)) {
              p = strchr (p + 1, ':');
              check_again = 1;
            }
          } while (check_again);
        }
#endif
        if (p != 0)
          {
            struct nameseq *target;
            target = PARSE_FILE_SEQ (&p2, struct nameseq, ':', NULL,
                                     PARSEFS_NOGLOB);
            ++p2;
            if (target == 0)
              fatal (fstart, _("missing target pattern"));
            else if (target->next != 0)
              fatal (fstart, _("multiple target patterns"));
            pattern_percent = find_percent_cached (&target->name);
            pattern = target->name;
            if (pattern_percent == 0)
              fatal (fstart, _("target pattern contains no `%%'"));
            free_ns (target);
          }
        else
          pattern = 0;

        /* Strip leading and trailing whitespaces. */
        beg = p2;
        end = beg + strlen (beg) - 1;
        strip_whitespace (&beg, &end);

        /* Put all the prerequisites here; they'll be parsed later.  */
        if (beg <= end && *beg != '\0')
          depstr = xstrndup (beg, end - beg + 1);
        else
          depstr = 0;

        commands_idx = 0;
        if (cmdleft != 0)
          {
            /* Semicolon means rest of line is a command.  */
            unsigned int l = strlen (cmdleft);

            cmds_started = fstart->lineno;

            /* Add this command line to the buffer.  */
            if (l + 2 > commands_len)
              {
                commands_len = (l + 2) * 2;
                commands = xrealloc (commands, commands_len);
              }
            memcpy (commands, cmdleft, l);
            commands_idx += l;
            commands[commands_idx++] = '\n';
          }

        /* Determine if this target should be made default. We used to do
           this in record_files() but because of the delayed target recording
           and because preprocessor directives are legal in target's commands
           it is too late. Consider this fragment for example:

           foo:

           ifeq ($(.DEFAULT_GOAL),foo)
              ...
           endif

           Because the target is not recorded until after ifeq directive is
           evaluated the .DEFAULT_GOAL does not contain foo yet as one
           would expect. Because of this we have to move the logic here.  */

        if (set_default && default_goal_var->value[0] == '\0')
          {
            const char *name;
            struct dep *d;
            struct nameseq *t = filenames;

            for (; t != 0; t = t->next)
              {
                int reject = 0;
                name = t->name;

                /* We have nothing to do if this is an implicit rule. */
                if (strchr (name, '%') != 0)
                  break;

                /* See if this target's name does not start with a `.',
                   unless it contains a slash.  */
                if (*name == '.' && strchr (name, '/') == 0
#ifdef HAVE_DOS_PATHS
                    && strchr (name, '\\') == 0
#endif
                    )
                  continue;


                /* If this file is a suffix, don't let it be
                   the default goal file.  */
                for (d = suffix_file->deps; d != 0; d = d->next)
                  {
                    register struct dep *d2;
                    if (*dep_name (d) != '.' && streq (name, dep_name (d)))
                      {
                        reject = 1;
                        break;
                      }
                    for (d2 = suffix_file->deps; d2 != 0; d2 = d2->next)
                      {
                        unsigned int l = strlen (dep_name (d2));
                        if (!strneq (name, dep_name (d2), l))
                          continue;
                        if (streq (name + l, dep_name (d)))
                          {
                            reject = 1;
                            break;
                          }
                      }

                    if (reject)
                      break;
                  }

                if (!reject)
                  {
                    define_variable_global (".DEFAULT_GOAL", 13, t->name,
                                            o_file, 0, NILF);
                    break;
                  }
              }
          }

        continue;
      }

      /* We get here except in the case that we just read a rule line.
     Record now the last rule we read, so following spurious
     commands are properly diagnosed.  */
 rule_complete:
      record_waiting_files ();
    }

#undef  word1eq

  if (conditionals->if_cmds)
    fatal (fstart, _("missing `endif'"));

  /* At eof, record the last rule.  */
  record_waiting_files ();

  if (collapsed)
    free (collapsed);
  free (commands);
}



/* Remove comments from LINE.
   This is done by copying the text at LINE onto itself.  */

static void
remove_comments (char *line)
{
  char *comment;

  comment = find_char_unquote (line, '#', 0, 0, 0);

  if (comment != 0)
    /* Cut off the line at the #.  */
    *comment = '\0';
}

/* Execute a `undefine' directive.
   The undefine line has already been read, and NAME is the name of
   the variable to be undefined. */

static void
do_undefine (char *name, enum variable_origin origin, struct ebuffer *ebuf)
{
  char *p, *var;

  /* Expand the variable name and find the beginning (NAME) and end.  */
  var = allocated_variable_expand (name);
  name = next_token (var);
  if (*name == '\0')
    fatal (&ebuf->floc, _("empty variable name"));
  p = name + strlen (name) - 1;
  while (p > name && isblank ((unsigned char)*p))
    --p;
  p[1] = '\0';

  undefine_variable_global (name, p - name + 1, origin);
  free (var);
}

/* Execute a `define' directive.
   The first line has already been read, and NAME is the name of
   the variable to be defined.  The following lines remain to be read.  */

static struct variable *
do_define (char *name, enum variable_origin origin, struct ebuffer *ebuf)
{
  struct variable *v;
  enum variable_flavor flavor;
  struct floc defstart;
  int nlevels = 1;
  unsigned int length = 100;
  char *definition = xmalloc (length);
  unsigned int idx = 0;
  char *p, *var;

  defstart = ebuf->floc;

  p = parse_variable_definition (name, &flavor);
  if (p == NULL)
    /* No assignment token, so assume recursive.  */
    flavor = f_recursive;
  else
    {
      if (*(next_token (p)) != '\0')
        error (&defstart, _("extraneous text after `define' directive"));

      /* Chop the string before the assignment token to get the name.  */
      p[flavor == f_recursive ? -1 : -2] = '\0';
    }

  /* Expand the variable name and find the beginning (NAME) and end.  */
  var = allocated_variable_expand (name);
  name = next_token (var);
  if (*name == '\0')
    fatal (&defstart, _("empty variable name"));
  p = name + strlen (name) - 1;
  while (p > name && isblank ((unsigned char)*p))
    --p;
  p[1] = '\0';

  /* Now read the value of the variable.  */
  while (1)
    {
      unsigned int len;
      char *line;
      long nlines = readline (ebuf);

      /* If there is nothing left to be eval'd, there's no 'endef'!!  */
      if (nlines < 0)
        fatal (&defstart, _("missing `endef', unterminated `define'"));

      ebuf->floc.lineno += nlines;
      line = ebuf->buffer;

      collapse_continuations (line);

      /* If the line doesn't begin with a tab, test to see if it introduces
         another define, or ends one.  Stop if we find an 'endef' */
      if (line[0] != cmd_prefix)
        {
          p = next_token (line);
          len = strlen (p);

          /* If this is another 'define', increment the level count.  */
          if ((len == 6 || (len > 6 && isblank ((unsigned char)p[6])))
              && strneq (p, "define", 6))
            ++nlevels;

          /* If this is an 'endef', decrement the count.  If it's now 0,
             we've found the last one.  */
          else if ((len == 5 || (len > 5 && isblank ((unsigned char)p[5])))
                   && strneq (p, "endef", 5))
            {
              p += 5;
              remove_comments (p);
              if (*(next_token (p)) != '\0')
                error (&ebuf->floc,
                       _("extraneous text after `endef' directive"));

              if (--nlevels == 0)
                break;
            }
        }

      /* Add this line to the variable definition.  */
      len = strlen (line);
      if (idx + len + 1 > length)
        {
          length = (idx + len) * 2;
          definition = xrealloc (definition, length + 1);
        }

      memcpy (&definition[idx], line, len);
      idx += len;
      /* Separate lines with a newline.  */
      definition[idx++] = '\n';
    }

  /* We've got what we need; define the variable.  */
  if (idx == 0)
    definition[0] = '\0';
  else
    definition[idx - 1] = '\0';

  v = do_variable_definition (&defstart, name, definition, origin, flavor, 0);
  free (definition);
  free (var);
  return (v);
}


/* Interpret conditional commands "ifdef", "ifndef", "ifeq",
   "ifneq", "else" and "endif".
   LINE is the input line, with the command as its first word.

   FILENAME and LINENO are the filename and line number in the
   current makefile.  They are used for error messages.

   Value is -2 if the line is not a conditional at all,
   -1 if the line is an invalid conditional,
   0 if following text should be interpreted,
   1 if following text should be ignored.  */

static int
conditional_line (char *line, int len, const struct floc *flocp)
{
  char *cmdname;
  enum { c_ifdef, c_ifndef, c_ifeq, c_ifneq, c_else, c_endif } cmdtype;
  unsigned int i;
  unsigned int o;

  /* Compare a word, both length and contents. */
#define word1eq(s)      (len == sizeof(s)-1 && strneq (s, line, sizeof(s)-1))
#define chkword(s, t)   if (word1eq (s)) { cmdtype = (t); cmdname = (s); }

  /* Make sure this line is a conditional.  */
  chkword ("ifdef", c_ifdef)
  else chkword ("ifndef", c_ifndef)
  else chkword ("ifeq", c_ifeq)
  else chkword ("ifneq", c_ifneq)
  else chkword ("else", c_else)
  else chkword ("endif", c_endif)
  else
    return -2;

  /* Found one: skip past it and any whitespace after it.  */
  line = next_token (line + len);

#define EXTRANEOUS() error (flocp, _("Extraneous text after `%s' directive"), cmdname)

  /* An 'endif' cannot contain extra text, and reduces the if-depth by 1  */
  if (cmdtype == c_endif)
    {
      if (*line != '\0')
    EXTRANEOUS ();

      if (!conditionals->if_cmds)
    fatal (flocp, _("extraneous `%s'"), cmdname);

      --conditionals->if_cmds;

      goto DONE;
    }

  /* An 'else' statement can either be simple, or it can have another
     conditional after it.  */
  if (cmdtype == c_else)
    {
      const char *p;

      if (!conditionals->if_cmds)
    fatal (flocp, _("extraneous `%s'"), cmdname);

      o = conditionals->if_cmds - 1;

      if (conditionals->seen_else[o])
        fatal (flocp, _("only one `else' per conditional"));

      /* Change the state of ignorance.  */
      switch (conditionals->ignoring[o])
        {
          case 0:
            /* We've just been interpreting.  Never do it again.  */
            conditionals->ignoring[o] = 2;
            break;
          case 1:
            /* We've never interpreted yet.  Maybe this time!  */
            conditionals->ignoring[o] = 0;
            break;
        }

      /* It's a simple 'else'.  */
      if (*line == '\0')
        {
          conditionals->seen_else[o] = 1;
          goto DONE;
        }

      /* The 'else' has extra text.  That text must be another conditional
         and cannot be an 'else' or 'endif'.  */

      /* Find the length of the next word.  */
      for (p = line+1; *p != '\0' && !isspace ((unsigned char)*p); ++p)
        ;
      len = p - line;

      /* If it's 'else' or 'endif' or an illegal conditional, fail.  */
      if (word1eq("else") || word1eq("endif")
          || conditional_line (line, len, flocp) < 0)
    EXTRANEOUS ();
      else
        {
          /* conditional_line() created a new level of conditional.
             Raise it back to this level.  */
          if (conditionals->ignoring[o] < 2)
            conditionals->ignoring[o] = conditionals->ignoring[o+1];
          --conditionals->if_cmds;
        }

      goto DONE;
    }

  if (conditionals->allocated == 0)
    {
      conditionals->allocated = 5;
      conditionals->ignoring = xmalloc (conditionals->allocated);
      conditionals->seen_else = xmalloc (conditionals->allocated);
    }

  o = conditionals->if_cmds++;
  if (conditionals->if_cmds > conditionals->allocated)
    {
      conditionals->allocated += 5;
      conditionals->ignoring = xrealloc (conditionals->ignoring,
                                         conditionals->allocated);
      conditionals->seen_else = xrealloc (conditionals->seen_else,
                                          conditionals->allocated);
    }

  /* Record that we have seen an `if...' but no `else' so far.  */
  conditionals->seen_else[o] = 0;

  /* Search through the stack to see if we're already ignoring.  */
  for (i = 0; i < o; ++i)
    if (conditionals->ignoring[i])
      {
    /* We are already ignoring, so just push a level to match the next
       "else" or "endif", and keep ignoring.  We don't want to expand
       variables in the condition.  */
    conditionals->ignoring[o] = 1;
    return 1;
      }

  if (cmdtype == c_ifdef || cmdtype == c_ifndef)
    {
      char *var;
      struct variable *v;
      char *p;

      /* Expand the thing we're looking up, so we can use indirect and
         constructed variable names.  */
      var = allocated_variable_expand (line);

      /* Make sure there's only one variable name to test.  */
      p = end_of_token (var);
      i = p - var;
      p = next_token (p);
      if (*p != '\0')
    return -1;

      var[i] = '\0';
      v = lookup_variable (var, i);

      conditionals->ignoring[o] =
        ((v != 0 && *v->value != '\0') == (cmdtype == c_ifndef));

      free (var);
    }
  else
    {
      /* "ifeq" or "ifneq".  */
      char *s1, *s2;
      unsigned int l;
      char termin = *line == '(' ? ',' : *line;

      if (termin != ',' && termin != '"' && termin != '\'')
    return -1;

      s1 = ++line;
      /* Find the end of the first string.  */
      if (termin == ',')
    {
      int count = 0;
      for (; *line != '\0'; ++line)
        if (*line == '(')
          ++count;
        else if (*line == ')')
          --count;
        else if (*line == ',' && count <= 0)
          break;
    }
      else
    while (*line != '\0' && *line != termin)
      ++line;

      if (*line == '\0')
    return -1;

      if (termin == ',')
    {
      /* Strip blanks after the first string.  */
      char *p = line++;
      while (isblank ((unsigned char)p[-1]))
        --p;
      *p = '\0';
    }
      else
    *line++ = '\0';

      s2 = variable_expand (s1);
      /* We must allocate a new copy of the expanded string because
     variable_expand re-uses the same buffer.  */
      l = strlen (s2);
      s1 = alloca (l + 1);
      memcpy (s1, s2, l + 1);

      if (termin != ',')
    /* Find the start of the second string.  */
    line = next_token (line);

      termin = termin == ',' ? ')' : *line;
      if (termin != ')' && termin != '"' && termin != '\'')
    return -1;

      /* Find the end of the second string.  */
      if (termin == ')')
    {
      int count = 0;
      s2 = next_token (line);
      for (line = s2; *line != '\0'; ++line)
        {
          if (*line == '(')
        ++count;
          else if (*line == ')')
        {
          if (count <= 0)
            break;
          else
            --count;
        }
        }
    }
      else
    {
      ++line;
      s2 = line;
      while (*line != '\0' && *line != termin)
        ++line;
    }

      if (*line == '\0')
    return -1;

      *line = '\0';
      line = next_token (++line);
      if (*line != '\0')
    EXTRANEOUS ();

      s2 = variable_expand (s2);
      conditionals->ignoring[o] = (streq (s1, s2) == (cmdtype == c_ifneq));
    }

 DONE:
  /* Search through the stack to see if we're ignoring.  */
  for (i = 0; i < conditionals->if_cmds; ++i)
    if (conditionals->ignoring[i])
      return 1;
  return 0;
}



/* Record target-specific variable values for files FILENAMES.
   TWO_COLON is nonzero if a double colon was used.

   The links of FILENAMES are freed, and so are any names in it
   that are not incorporated into other data structures.

   If the target is a pattern, add the variable to the pattern-specific
   variable value list.  */

static void
record_target_var (struct nameseq *filenames, char *defn,
                   enum variable_origin origin, struct vmodifiers *vmod,
                   const struct floc *flocp)
{
  struct nameseq *nextf;
  struct variable_set_list *global;

  global = current_variable_set_list;

  /* If the variable is an append version, store that but treat it as a
     normal recursive variable.  */

  for (; filenames != 0; filenames = nextf)
    {
      struct variable *v;
      const char *name = filenames->name;
      const char *fname;
      const char *percent;
      struct pattern_var *p;

      nextf = filenames->next;
      free_ns (filenames);

      /* If it's a pattern target, then add it to the pattern-specific
         variable list.  */
      percent = find_percent_cached (&name);
      if (percent)
        {
          /* Get a reference for this pattern-specific variable struct.  */
          p = create_pattern_var (name, percent);
          p->variable.fileinfo = *flocp;
          /* I don't think this can fail since we already determined it was a
             variable definition.  */
          v = assign_variable_definition (&p->variable, defn);
          assert (v != 0);

          v->origin = origin;
          if (v->flavor == f_simple)
            v->value = allocated_variable_expand (v->value);
          else
            v->value = xstrdup (v->value);

          fname = p->target;
        }
      else
        {
          struct file *f;

          /* Get a file reference for this file, and initialize it.
             We don't want to just call enter_file() because that allocates a
             new entry if the file is a double-colon, which we don't want in
             this situation.  */
          f = lookup_file (name);
          if (!f)
            f = enter_file (strcache_add (name));
          else if (f->double_colon)
            f = f->double_colon;

          initialize_file_variables (f, 1);
          fname = f->name;

          current_variable_set_list = f->variables;
          v = try_variable_definition (flocp, defn, origin, 1);
          if (!v)
            fatal (flocp, _("Malformed target-specific variable definition"));
          current_variable_set_list = global;
        }

      /* Set up the variable to be *-specific.  */
      v->per_target = 1;
      v->private_var = vmod->private_v;
      v->export = vmod->export_v ? v_export : v_default;

      /* If it's not an override, check to see if there was a command-line
         setting.  If so, reset the value.  */
      if (v->origin != o_override)
        {
          struct variable *gv;
          int len = strlen(v->name);

          gv = lookup_variable (v->name, len);
          if (gv && (gv->origin == o_env_override || gv->origin == o_command))
            {
              if (v->value != 0)
                free (v->value);
              v->value = xstrdup (gv->value);
              v->origin = gv->origin;
              v->recursive = gv->recursive;
              v->append = 0;
            }
        }
    }
}


/* Record a description line for files FILENAMES,
   with dependencies DEPS, commands to execute described
   by COMMANDS and COMMANDS_IDX, coming from FILENAME:COMMANDS_STARTED.
   TWO_COLON is nonzero if a double colon was used.
   If not nil, PATTERN is the `%' pattern to make this
   a static pattern rule, and PATTERN_PERCENT is a pointer
   to the `%' within it.

   The links of FILENAMES are freed, and so are any names in it
   that are not incorporated into other data structures.  */

static void
record_files (struct nameseq *filenames, const char *pattern,
              const char *pattern_percent, char *depstr,
              unsigned int cmds_started, char *commands,
              unsigned int commands_idx, int two_colon,
              const struct floc *flocp)
{
  struct commands *cmds;
  struct dep *deps;
  const char *implicit_percent;
  const char *name;

  /* If we've already snapped deps, that means we're in an eval being
     resolved after the makefiles have been read in.  We can't add more rules
     at this time, since they won't get snapped and we'll get core dumps.
     See Savannah bug # 12124.  */
  if (snapped_deps)
    fatal (flocp, _("prerequisites cannot be defined in recipes"));

  /* Determine if this is a pattern rule or not.  */
  name = filenames->name;
  implicit_percent = find_percent_cached (&name);

  /* If there's a recipe, set up a struct for it.  */
  if (commands_idx > 0)
    {
      cmds = xmalloc (sizeof (struct commands));
      cmds->fileinfo.filenm = flocp->filenm;
      cmds->fileinfo.lineno = cmds_started;
      cmds->commands = xstrndup (commands, commands_idx);
      cmds->command_lines = 0;
    }
  else
     cmds = 0;

  /* If there's a prereq string then parse it--unless it's eligible for 2nd
     expansion: if so, snap_deps() will do it.  */
  if (depstr == 0)
    deps = 0;
  else if (second_expansion && strchr (depstr, '$'))
    {
      deps = alloc_dep ();
      deps->name = depstr;
      deps->need_2nd_expansion = 1;
      deps->staticpattern = pattern != 0;
    }
  else
    {
      deps = split_prereqs (depstr);
      free (depstr);

      /* We'll enter static pattern prereqs later when we have the stem.  We
         don't want to enter pattern rules at all so that we don't think that
         they ought to exist (make manual "Implicit Rule Search Algorithm",
         item 5c).  */
      if (! pattern && ! implicit_percent)
        deps = enter_prereqs (deps, NULL);
    }

  /* For implicit rules, _all_ the targets must have a pattern.  That means we
     can test the first one to see if we're working with an implicit rule; if
     so we handle it specially. */

  if (implicit_percent)
    {
      struct nameseq *nextf;
      const char **targets, **target_pats;
      unsigned int c;

      if (pattern != 0)
        fatal (flocp, _("mixed implicit and static pattern rules"));

      /* Count the targets to create an array of target names.
         We already have the first one.  */
      nextf = filenames->next;
      free_ns (filenames);
      filenames = nextf;

      for (c = 1; nextf; ++c, nextf = nextf->next)
        ;
      targets = xmalloc (c * sizeof (const char *));
      target_pats = xmalloc (c * sizeof (const char *));

      targets[0] = name;
      target_pats[0] = implicit_percent;

      c = 1;
      while (filenames)
        {
          name = filenames->name;
          implicit_percent = find_percent_cached (&name);

          if (implicit_percent == 0)
            fatal (flocp, _("mixed implicit and normal rules"));

      targets[c] = name;
      target_pats[c] = implicit_percent;
          ++c;

          nextf = filenames->next;
          free_ns (filenames);
          filenames = nextf;
        }

      create_pattern_rule (targets, target_pats, c, two_colon, deps, cmds, 1);

      return;
    }


  /* Walk through each target and create it in the database.
     We already set up the first target, above.  */
  while (1)
    {
      struct nameseq *nextf = filenames->next;
      struct file *f;
      struct dep *this = 0;

      free_ns (filenames);

      /* Check for special targets.  Do it here instead of, say, snap_deps()
         so that we can immediately use the value.  */
      if (streq (name, ".POSIX"))
        {
          posix_pedantic = 1;
          define_variable_cname (".SHELLFLAGS", "-ec", o_default, 0);
        }
      else if (streq (name, ".SECONDEXPANSION"))
        second_expansion = 1;
#if !defined(WINDOWS32) && !defined (__MSDOS__) && !defined (__EMX__)
      else if (streq (name, ".ONESHELL"))
        one_shell = 1;
#endif

      /* If this is a static pattern rule:
         `targets: target%pattern: prereq%pattern; recipe',
         make sure the pattern matches this target name.  */
      if (pattern && !pattern_matches (pattern, pattern_percent, name))
        error (flocp, _("target `%s' doesn't match the target pattern"), name);
      else if (deps)
        /* If there are multiple targets, copy the chain DEPS for all but the
           last one.  It is not safe for the same deps to go in more than one
           place in the database.  */
        this = nextf != 0 ? copy_dep_chain (deps) : deps;

      /* Find or create an entry in the file database for this target.  */
      if (!two_colon)
    {
      /* Single-colon.  Combine this rule with the file's existing record,
         if any.  */
      f = enter_file (strcache_add (name));
      if (f->double_colon)
        fatal (flocp,
                   _("target file `%s' has both : and :: entries"), f->name);

      /* If CMDS == F->CMDS, this target was listed in this rule
         more than once.  Just give a warning since this is harmless.  */
      if (cmds != 0 && cmds == f->cmds)
        error (flocp,
                   _("target `%s' given more than once in the same rule."),
                   f->name);

      /* Check for two single-colon entries both with commands.
         Check is_target so that we don't lose on files such as .c.o
         whose commands were preinitialized.  */
      else if (cmds != 0 && f->cmds != 0 && f->is_target)
        {
          error (&cmds->fileinfo,
                     _("warning: overriding recipe for target `%s'"),
                     f->name);
          error (&f->cmds->fileinfo,
                     _("warning: ignoring old recipe for target `%s'"),
                     f->name);
        }

      /* Defining .DEFAULT with no deps or cmds clears it.  */
      if (f == default_file && this == 0 && cmds == 0)
        f->cmds = 0;
      if (cmds != 0)
        f->cmds = cmds;

      /* Defining .SUFFIXES with no dependencies clears out the list of
         suffixes.  */
      if (f == suffix_file && this == 0)
        {
              free_dep_chain (f->deps);
          f->deps = 0;
        }
    }
      else
    {
      /* Double-colon.  Make a new record even if there already is one.  */
      f = lookup_file (name);

      /* Check for both : and :: rules.  Check is_target so we don't lose
         on default suffix rules or makefiles.  */
      if (f != 0 && f->is_target && !f->double_colon)
        fatal (flocp,
                   _("target file `%s' has both : and :: entries"), f->name);

      f = enter_file (strcache_add (name));
      /* If there was an existing entry and it was a double-colon entry,
         enter_file will have returned a new one, making it the prev
         pointer of the old one, and setting its double_colon pointer to
         the first one.  */
      if (f->double_colon == 0)
        /* This is the first entry for this name, so we must set its
           double_colon pointer to itself.  */
        f->double_colon = f;

      f->cmds = cmds;
    }

      f->is_target = 1;

      /* If this is a static pattern rule, set the stem to the part of its
         name that matched the `%' in the pattern, so you can use $* in the
         commands.  If we didn't do it before, enter the prereqs now.  */
      if (pattern)
        {
          static const char *percent = "%";
          char *buffer = variable_expand ("");
          char *o = patsubst_expand_pat (buffer, name, pattern, percent,
                                         pattern_percent+1, percent+1);
          f->stem = strcache_add_len (buffer, o - buffer);
          if (this)
            {
              if (! this->need_2nd_expansion)
                this = enter_prereqs (this, f->stem);
              else
                this->stem = f->stem;
            }
        }

      /* Add the dependencies to this file entry.  */
      if (this != 0)
        {
          /* Add the file's old deps and the new ones in THIS together.  */
          if (f->deps == 0)
            f->deps = this;
          else if (cmds != 0)
            {
              struct dep *d = this;

              /* If this rule has commands, put these deps first.  */
              while (d->next != 0)
                d = d->next;

              d->next = f->deps;
              f->deps = this;
            }
          else
            {
              struct dep *d = f->deps;

              /* A rule without commands: put its prereqs at the end.  */
              while (d->next != 0)
                d = d->next;

              d->next = this;
            }
        }

      name = f->name;

      /* All done!  Set up for the next one.  */
      if (nextf == 0)
        break;

      filenames = nextf;

      /* Reduce escaped percents.  If there are any unescaped it's an error  */
      name = filenames->name;
      if (find_percent_cached (&name))
        fatal (flocp, _("mixed implicit and normal rules"));
    }
}


/* Search STRING for an unquoted STOPCHAR or blank (if BLANK is nonzero).
   Backslashes quote STOPCHAR, blanks if BLANK is nonzero, and backslash.
   Quoting backslashes are removed from STRING by compacting it into
   itself.  Returns a pointer to the first unquoted STOPCHAR if there is
   one, or nil if there are none.  STOPCHARs inside variable references are
   ignored if IGNOREVARS is true.

   STOPCHAR _cannot_ be '$' if IGNOREVARS is true.  */

static char *
find_char_unquote (char *string, int stop1, int stop2, int blank,
                   int ignorevars)
{
  unsigned int string_len = 0;
  char *p = string;

  if (ignorevars)
    ignorevars = '$';

  while (1)
    {
      if (stop2 && blank)
    while (*p != '\0' && *p != ignorevars && *p != stop1 && *p != stop2
           && ! isblank ((unsigned char) *p))
      ++p;
      else if (stop2)
    while (*p != '\0' && *p != ignorevars && *p != stop1 && *p != stop2)
      ++p;
      else if (blank)
    while (*p != '\0' && *p != ignorevars && *p != stop1
           && ! isblank ((unsigned char) *p))
      ++p;
      else
    while (*p != '\0' && *p != ignorevars && *p != stop1)
      ++p;

      if (*p == '\0')
    break;

      /* If we stopped due to a variable reference, skip over its contents.  */
      if (*p == ignorevars)
        {
          char openparen = p[1];

          p += 2;

          /* Skip the contents of a non-quoted, multi-char variable ref.  */
          if (openparen == '(' || openparen == '{')
            {
              unsigned int pcount = 1;
              char closeparen = (openparen == '(' ? ')' : '}');

              while (*p)
                {
                  if (*p == openparen)
                    ++pcount;
                  else if (*p == closeparen)
                    if (--pcount == 0)
                      {
                        ++p;
                        break;
                      }
                  ++p;
                }
            }

          /* Skipped the variable reference: look for STOPCHARS again.  */
          continue;
        }

      if (p > string && p[-1] == '\\')
    {
      /* Search for more backslashes.  */
      int i = -2;
      while (&p[i] >= string && p[i] == '\\')
        --i;
      ++i;
      /* Only compute the length if really needed.  */
      if (string_len == 0)
        string_len = strlen (string);
      /* The number of backslashes is now -I.
         Copy P over itself to swallow half of them.  */
      memmove (&p[i], &p[i/2], (string_len - (p - string)) - (i/2) + 1);
      p += i/2;
      if (i % 2 == 0)
        /* All the backslashes quoted each other; the STOPCHAR was
           unquoted.  */
        return p;

      /* The STOPCHAR was quoted by a backslash.  Look for another.  */
    }
      else
    /* No backslash in sight.  */
    return p;
    }

  /* Never hit a STOPCHAR or blank (with BLANK nonzero).  */
  return 0;
}

/* Search PATTERN for an unquoted % and handle quoting.  */

char *
find_percent (char *pattern)
{
  return find_char_unquote (pattern, '%', 0, 0, 0);
}

/* Search STRING for an unquoted % and handle quoting.  Returns a pointer to
   the % or NULL if no % was found.
   This version is used with strings in the string cache: if there's a need to
   modify the string a new version will be added to the string cache and
   *STRING will be set to that.  */

const char *
find_percent_cached (const char **string)
{
  const char *p = *string;
  char *new = 0;
  int slen = 0;

  /* If the first char is a % return now.  This lets us avoid extra tests
     inside the loop.  */
  if (*p == '%')
    return p;

  while (1)
    {
      while (*p != '\0' && *p != '%')
        ++p;

      if (*p == '\0')
        break;

      /* See if this % is escaped with a backslash; if not we're done.  */
      if (p[-1] != '\\')
        break;

      {
        /* Search for more backslashes.  */
        char *pv;
        int i = -2;

        while (&p[i] >= *string && p[i] == '\\')
          --i;
        ++i;

        /* At this point we know we'll need to allocate a new string.
           Make a copy if we haven't yet done so.  */
        if (! new)
          {
            slen = strlen (*string);
            new = alloca (slen + 1);
            memcpy (new, *string, slen + 1);
            p = new + (p - *string);
            *string = new;
          }

        /* At this point *string, p, and new all point into the same string.
           Get a non-const version of p so we can modify new.  */
        pv = new + (p - *string);

        /* The number of backslashes is now -I.
           Copy P over itself to swallow half of them.  */
        memmove (&pv[i], &pv[i/2], (slen - (pv - new)) - (i/2) + 1);
        p += i/2;

        /* If the backslashes quoted each other; the % was unquoted.  */
        if (i % 2 == 0)
          break;
      }
    }

  /* If we had to change STRING, add it to the strcache.  */
  if (new)
    {
      *string = strcache_add (*string);
      p = *string + (p - new);
    }

  /* If we didn't find a %, return NULL.  Otherwise return a ptr to it.  */
  return (*p == '\0') ? NULL : p;
}


/* Find the next line of text in an eval buffer, combining continuation lines
   into one line.
   Return the number of actual lines read (> 1 if continuation lines).
   Returns -1 if there's nothing left in the buffer.

   After this function, ebuf->buffer points to the first character of the
   line we just found.
 */

/* Read a line of text from a STRING.
   Since we aren't really reading from a file, don't bother with linenumbers.
 */

static unsigned long
readstring (struct ebuffer *ebuf)
{
  char *eol;

  /* If there is nothing left in this buffer, return 0.  */
  if (ebuf->bufnext >= ebuf->bufstart + ebuf->size)
    return -1;

  /* Set up a new starting point for the buffer, and find the end of the
     next logical line (taking into account backslash/newline pairs).  */

  eol = ebuf->buffer = ebuf->bufnext;

  while (1)
    {
      int backslash = 0;
      const char *bol = eol;
      const char *p;

      /* Find the next newline.  At EOS, stop.  */
      p = eol = strchr (eol , '\n');
      if (!eol)
        {
          ebuf->bufnext = ebuf->bufstart + ebuf->size + 1;
          return 0;
        }

      /* Found a newline; if it's escaped continue; else we're done.  */
      while (p > bol && *(--p) == '\\')
        backslash = !backslash;
      if (!backslash)
        break;
      ++eol;
    }

  /* Overwrite the newline char.  */
  *eol = '\0';
  ebuf->bufnext = eol+1;

  return 0;
}

static long
readline (struct ebuffer *ebuf)
{
  char *p;
  char *end;
  char *start;
  long nlines = 0;

  /* The behaviors between string and stream buffers are different enough to
     warrant different functions.  Do the Right Thing.  */

  if (!ebuf->fp)
    return readstring (ebuf);

  /* When reading from a file, we always start over at the beginning of the
     buffer for each new line.  */

  p = start = ebuf->bufstart;
  end = p + ebuf->size;
  *p = '\0';

  while (fgets (p, end - p, ebuf->fp) != 0)
    {
      char *p2;
      unsigned long len;
      int backslash;

      len = strlen (p);
      if (len == 0)
    {
      /* This only happens when the first thing on the line is a '\0'.
         It is a pretty hopeless case, but (wonder of wonders) Athena
         lossage strikes again!  (xmkmf puts NULs in its makefiles.)
         There is nothing really to be done; we synthesize a newline so
         the following line doesn't appear to be part of this line.  */
      error (&ebuf->floc,
                 _("warning: NUL character seen; rest of line ignored"));
      p[0] = '\n';
      len = 1;
    }

      /* Jump past the text we just read.  */
      p += len;

      /* If the last char isn't a newline, the whole line didn't fit into the
         buffer.  Get some more buffer and try again.  */
      if (p[-1] != '\n')
        goto more_buffer;

      /* We got a newline, so add one to the count of lines.  */
      ++nlines;

#if !defined(WINDOWS32) && !defined(__MSDOS__) && !defined(__EMX__)
      /* Check to see if the line was really ended with CRLF; if so ignore
         the CR.  */
      if ((p - start) > 1 && p[-2] == '\r')
        {
          --p;
          p[-1] = '\n';
        }
#endif

      backslash = 0;
      for (p2 = p - 2; p2 >= start; --p2)
    {
      if (*p2 != '\\')
        break;
          backslash = !backslash;
    }

      if (!backslash)
    {
      p[-1] = '\0';
      break;
    }

      /* It was a backslash/newline combo.  If we have more space, read
         another line.  */
      if (end - p >= 80)
        continue;

      /* We need more space at the end of our buffer, so realloc it.
         Make sure to preserve the current offset of p.  */
    more_buffer:
      {
        unsigned long off = p - start;
        ebuf->size *= 2;
        start = ebuf->buffer = ebuf->bufstart = xrealloc (start, ebuf->size);
        p = start + off;
        end = start + ebuf->size;
        *p = '\0';
      }
    }

  if (ferror (ebuf->fp))
    pfatal_with_name (ebuf->floc.filenm);

  /* If we found some lines, return how many.
     If we didn't, but we did find _something_, that indicates we read the last
     line of a file with no final newline; return 1.
     If we read nothing, we're at EOF; return -1.  */

  return nlines ? nlines : p == ebuf->bufstart ? -1 : 1;
}


/* Parse the next "makefile word" from the input buffer, and return info
   about it.

   A "makefile word" is one of:

     w_bogus        Should never happen
     w_eol          End of input
     w_static       A static word; cannot be expanded
     w_variable     A word containing one or more variables/functions
     w_colon        A colon
     w_dcolon       A double-colon
     w_semicolon    A semicolon
     w_varassign    A variable assignment operator (=, :=, +=, or ?=)

   Note that this function is only used when reading certain parts of the
   makefile.  Don't use it where special rules hold sway (RHS of a variable,
   in a command list, etc.)  */

static enum make_word_type
get_next_mword (char *buffer, char *delim, char **startp, unsigned int *length)
{
  enum make_word_type wtype = w_bogus;
  char *p = buffer, *beg;
  char c;

  /* Skip any leading whitespace.  */
  while (isblank ((unsigned char)*p))
    ++p;

  beg = p;
  c = *(p++);
  switch (c)
    {
    case '\0':
      wtype = w_eol;
      break;

    case ';':
      wtype = w_semicolon;
      break;

    case '=':
      wtype = w_varassign;
      break;

    case ':':
      wtype = w_colon;
      switch (*p)
        {
        case ':':
          ++p;
          wtype = w_dcolon;
          break;

        case '=':
          ++p;
          wtype = w_varassign;
          break;
        }
      break;

    case '+':
    case '?':
      if (*p == '=')
        {
          ++p;
          wtype = w_varassign;
          break;
        }

    default:
      if (delim && strchr (delim, c))
        wtype = w_static;
      break;
    }

  /* Did we find something?  If so, return now.  */
  if (wtype != w_bogus)
    goto done;

  /* This is some non-operator word.  A word consists of the longest
     string of characters that doesn't contain whitespace, one of [:=#],
     or [?+]=, or one of the chars in the DELIM string.  */

  /* We start out assuming a static word; if we see a variable we'll
     adjust our assumptions then.  */
  wtype = w_static;

  /* We already found the first value of "c", above.  */
  while (1)
    {
      char closeparen;
      int count;

      switch (c)
        {
        case '\0':
        case ' ':
        case '\t':
        case '=':
          goto done_word;

        case ':':
#ifdef HAVE_DOS_PATHS
      /* A word CAN include a colon in its drive spec.  The drive
         spec is allowed either at the beginning of a word, or as part
         of the archive member name, like in "libfoo.a(d:/foo/bar.o)".  */
      if (!(p - beg >= 2
        && (*p == '/' || *p == '\\') && isalpha ((unsigned char)p[-2])
        && (p - beg == 2 || p[-3] == '(')))
#endif
      goto done_word;

        case '$':
          c = *(p++);
          if (c == '$')
            break;

          /* This is a variable reference, so note that it's expandable.
             Then read it to the matching close paren.  */
          wtype = w_variable;

          if (c == '(')
            closeparen = ')';
          else if (c == '{')
            closeparen = '}';
          else
            /* This is a single-letter variable reference.  */
            break;

          for (count=0; *p != '\0'; ++p)
            {
              if (*p == c)
                ++count;
              else if (*p == closeparen && --count < 0)
                {
                  ++p;
                  break;
                }
            }
          break;

        case '?':
        case '+':
          if (*p == '=')
            goto done_word;
          break;

        case '\\':
          switch (*p)
            {
            case ':':
            case ';':
            case '=':
            case '\\':
              ++p;
              break;
            }
          break;

        default:
          if (delim && strchr (delim, c))
            goto done_word;
          break;
        }

      c = *(p++);
    }
 done_word:
  --p;

 done:
  if (startp)
    *startp = beg;
  if (length)
    *length = p - beg;
  return wtype;
}


/* Construct the list of include directories
   from the arguments and the default list.  */

void
construct_include_path (const char **arg_dirs)
{
#ifdef VAXC     /* just don't ask ... */
  stat_t stbuf;
#else
  struct stat stbuf;
#endif
  const char **dirs;
  const char **cpp;
  unsigned int idx;

  /* Compute the number of pointers we need in the table.  */
  idx = sizeof (default_include_directories) / sizeof (const char *);
  if (arg_dirs)
    for (cpp = arg_dirs; *cpp != 0; ++cpp)
      ++idx;

#ifdef  __MSDOS__
  /* Add one for $DJDIR.  */
  ++idx;
#endif

  dirs = xmalloc (idx * sizeof (const char *));

  idx = 0;
  max_incl_len = 0;

  /* First consider any dirs specified with -I switches.
     Ignore any that don't exist.  Remember the maximum string length.  */

  if (arg_dirs)
    while (*arg_dirs != 0)
      {
    const char *dir = *(arg_dirs++);
        char *expanded = 0;
        int e;

    if (dir[0] == '~')
      {
        expanded = tilde_expand (dir);
        if (expanded != 0)
          dir = expanded;
      }

        EINTRLOOP (e, stat (dir, &stbuf));
    if (e == 0 && S_ISDIR (stbuf.st_mode))
          {
            unsigned int len = strlen (dir);
            /* If dir name is written with trailing slashes, discard them.  */
            while (len > 1 && dir[len - 1] == '/')
              --len;
            if (len > max_incl_len)
              max_incl_len = len;
            dirs[idx++] = strcache_add_len (dir, len);
          }

    if (expanded)
      free (expanded);
      }

  /* Now add the standard default dirs at the end.  */

#ifdef  __MSDOS__
  {
    /* The environment variable $DJDIR holds the root of the DJGPP directory
       tree; add ${DJDIR}/include.  */
    struct variable *djdir = lookup_variable ("DJDIR", 5);

    if (djdir)
      {
        unsigned int len = strlen (djdir->value) + 8;
    char *defdir = alloca (len + 1);

    strcat (strcpy (defdir, djdir->value), "/include");
    dirs[idx++] = strcache_add (defdir);

        if (len > max_incl_len)
          max_incl_len = len;
      }
  }
#endif

  for (cpp = default_include_directories; *cpp != 0; ++cpp)
    {
      int e;

      EINTRLOOP (e, stat (*cpp, &stbuf));
      if (e == 0 && S_ISDIR (stbuf.st_mode))
        {
          unsigned int len = strlen (*cpp);
          /* If dir name is written with trailing slashes, discard them.  */
          while (len > 1 && (*cpp)[len - 1] == '/')
            --len;
          if (len > max_incl_len)
            max_incl_len = len;
          dirs[idx++] = strcache_add_len (*cpp, len);
        }
    }

  dirs[idx] = 0;

  /* Now add each dir to the .INCLUDE_DIRS variable.  */

  for (cpp = dirs; *cpp != 0; ++cpp)
    do_variable_definition (NILF, ".INCLUDE_DIRS", *cpp,
                            o_default, f_append, 0);

  include_directories = dirs;
}


/* Expand ~ or ~USER at the beginning of NAME.
   Return a newly malloc'd string or 0.  */

char *
tilde_expand (const char *name)
{
#ifndef VMS
  if (name[1] == '/' || name[1] == '\0')
    {
      extern char *getenv ();
      char *home_dir;
      int is_variable;

      {
    /* Turn off --warn-undefined-variables while we expand HOME.  */
    int save = warn_undefined_variables_flag;
    warn_undefined_variables_flag = 0;

    home_dir = allocated_variable_expand ("$(HOME)");

    warn_undefined_variables_flag = save;
      }

      is_variable = home_dir[0] != '\0';
      if (!is_variable)
    {
      free (home_dir);
      home_dir = getenv ("HOME");
    }
# if !defined(_AMIGA) && !defined(WINDOWS32)
      if (home_dir == 0 || home_dir[0] == '\0')
    {
      extern char *getlogin ();
      char *logname = getlogin ();
      home_dir = 0;
      if (logname != 0)
        {
          struct passwd *p = getpwnam (logname);
          if (p != 0)
        home_dir = p->pw_dir;
        }
    }
# endif /* !AMIGA && !WINDOWS32 */
      if (home_dir != 0)
    {
      char *new = xstrdup (concat (2, home_dir, name + 1));
      if (is_variable)
        free (home_dir);
      return new;
    }
    }
# if !defined(_AMIGA) && !defined(WINDOWS32)
  else
    {
      struct passwd *pwent;
      char *userend = strchr (name + 1, '/');
      if (userend != 0)
    *userend = '\0';
      pwent = getpwnam (name + 1);
      if (pwent != 0)
    {
      if (userend == 0)
        return xstrdup (pwent->pw_dir);
      else
        return xstrdup (concat (3, pwent->pw_dir, "/", userend + 1));
    }
      else if (userend != 0)
    *userend = '/';
    }
# endif /* !AMIGA && !WINDOWS32 */
#endif /* !VMS */
  return 0;
}


/* Parse a string into a sequence of filenames represented as a chain of
   struct nameseq's and return that chain.  Optionally expand the strings via
   glob().

   The string is passed as STRINGP, the address of a string pointer.
   The string pointer is updated to point at the first character
   not parsed, which either is a null char or equals STOPCHAR.

   SIZE is how big to construct chain elements.
   This is useful if we want them actually to be other structures
   that have room for additional info.

   PREFIX, if non-null, is added to the beginning of each filename.

   FLAGS allows one or more of the following bitflags to be set:
        PARSEFS_NOSTRIP - Do no strip './'s off the beginning
        PARSEFS_NOAR    - Do not check filenames for archive references
        PARSEFS_NOGLOB  - Do not expand globbing characters
        PARSEFS_EXISTS  - Only return globbed files that actually exist
                          (cannot also set NOGLOB)
        PARSEFS_NOCACHE - Do not add filenames to the strcache (caller frees)
  */

void *
parse_file_seq (char **stringp, unsigned int size, int stopchar,
                const char *prefix, int flags)
{
  extern void dir_setup_glob (glob_t *glob);

  /* tmp points to tmpbuf after the prefix, if any.
     tp is the end of the buffer. */
  static char *tmpbuf = NULL;
  static int tmpbuf_len = 0;

  int cachep = (! (flags & PARSEFS_NOCACHE));

  struct nameseq *new = 0;
  struct nameseq **newp = &new;
#define NEWELT(_n)  do { \
                        const char *__n = (_n); \
                        *newp = xcalloc (size); \
                        (*newp)->name = (cachep ? strcache_add (__n) : xstrdup (__n)); \
                        newp = &(*newp)->next; \
                    } while(0)

  char *p;
  glob_t gl;
  char *tp;

#ifdef VMS
# define VMS_COMMA ','
#else
# define VMS_COMMA 0
#endif

  if (size < sizeof (struct nameseq))
    size = sizeof (struct nameseq);

  if (! (flags & PARSEFS_NOGLOB))
    dir_setup_glob (&gl);

  /* Get enough temporary space to construct the largest possible target.  */
  {
    int l = strlen (*stringp) + 1;
    if (l > tmpbuf_len)
      {
        tmpbuf = xrealloc (tmpbuf, l);
        tmpbuf_len = l;
      }
  }
  tp = tmpbuf;

  /* Parse STRING.  P will always point to the end of the parsed content.  */
  p = *stringp;
  while (1)
    {
      const char *name;
      const char **nlist = 0;
      char *tildep = 0;
#ifndef NO_ARCHIVES
      char *arname = 0;
      char *memname = 0;
#endif
      char *s;
      int nlen;
      int i;

      /* Skip whitespace; at the end of the string or STOPCHAR we're done.  */
      p = next_token (p);
      if (*p == '\0' || *p == stopchar)
    break;

      /* There are names left, so find the end of the next name.
         Throughout this iteration S points to the start.  */
      s = p;
      p = find_char_unquote (p, stopchar, VMS_COMMA, 1, 0);
#ifdef VMS
    /* convert comma separated list to space separated */
      if (p && *p == ',')
    *p =' ';
#endif
#ifdef _AMIGA
      if (stopchar == ':' && p && *p == ':'
          && !(isspace ((unsigned char)p[1]) || !p[1]
               || isspace ((unsigned char)p[-1])))
    p = find_char_unquote (p+1, stopchar, VMS_COMMA, 1, 0);
#endif
#ifdef HAVE_DOS_PATHS
    /* For DOS paths, skip a "C:\..." or a "C:/..." until we find the
       first colon which isn't followed by a slash or a backslash.
       Note that tokens separated by spaces should be treated as separate
       tokens since make doesn't allow path names with spaces */
    if (stopchar == ':')
      while (p != 0 && !isspace ((unsigned char)*p) &&
             (p[1] == '\\' || p[1] == '/') && isalpha ((unsigned char)p[-1]))
        p = find_char_unquote (p + 1, stopchar, VMS_COMMA, 1, 0);
#endif
      if (p == 0)
    p = s + strlen (s);

      /* Strip leading "this directory" references.  */
      if (! (flags & PARSEFS_NOSTRIP))
#ifdef VMS
    /* Skip leading `[]'s.  */
    while (p - s > 2 && s[0] == '[' && s[1] == ']')
#else
    /* Skip leading `./'s.  */
    while (p - s > 2 && s[0] == '.' && s[1] == '/')
#endif
      {
            /* Skip "./" and all following slashes.  */
        s += 2;
        while (*s == '/')
          ++s;
      }

      /* Extract the filename just found, and skip it.
         Set NAME to the string, and NLEN to its length.  */

      if (s == p)
        {
    /* The name was stripped to empty ("./"). */
#if defined(VMS)
          continue;
#elif defined(_AMIGA)
          /* PDS-- This cannot be right!! */
          tp[0] = '\0';
          nlen = 0;
#else
          tp[0] = '.';
          tp[1] = '/';
          tp[2] = '\0';
          nlen = 2;
#endif
        }
      else
    {
#ifdef VMS
/* VMS filenames can have a ':' in them but they have to be '\'ed but we need
 *  to remove this '\' before we can use the filename.
 * xstrdup called because S may be read-only string constant.
 */
      char *n = tp;
      while (s < p)
        {
          if (s[0] == '\\' && s[1] == ':')
                ++s;
          *(n++) = *(s++);
        }
          n[0] = '\0';
          nlen = strlen (tp);
#else
          nlen = p - s;
          memcpy (tp, s, nlen);
          tp[nlen] = '\0';
#endif
        }

      /* At this point, TP points to the element and NLEN is its length.  */

#ifndef NO_ARCHIVES
      /* If this is the start of an archive group that isn't complete, set up
         to add the archive prefix for future files.  A file list like:
         "libf.a(x.o y.o z.o)" needs to be expanded as:
         "libf.a(x.o) libf.a(y.o) libf.a(z.o)"

         TP == TMP means we're not already in an archive group.  Ignore
         something starting with `(', as that cannot actually be an
         archive-member reference (and treating it as such results in an empty
         file name, which causes much lossage).  Also if it ends in ")" then
         it's a complete reference so we don't need to treat it specially.

         Finally, note that archive groups must end with ')' as the last
         character, so ensure there's some word ending like that before
         considering this an archive group.  */
      if (! (flags & PARSEFS_NOAR)
          && tp == tmpbuf && tp[0] != '(' && tp[nlen-1] != ')')
        {
          char *n = strchr (tp, '(');
          if (n)
            {
              /* This looks like the first element in an open archive group.
                 A valid group MUST have ')' as the last character.  */
              const char *e = p + nlen;
              do
                {
                  e = next_token (e);
                  /* Find the end of this word.  We don't want to unquote and
                     we don't care about quoting since we're looking for the
                     last char in the word. */
                  while (*e != '\0' && *e != stopchar && *e != VMS_COMMA
                         && ! isblank ((unsigned char) *e))
                    ++e;
                  if (e[-1] == ')')
                    {
                      /* Found the end, so this is the first element in an
                         open archive group.  It looks like "lib(mem".
                         Reset TP past the open paren.  */
                      nlen -= (n + 1) - tp;
                      tp = n + 1;

                      /* If we have just "lib(", part of something like
                         "lib( a b)", go to the next item.  */
                      if (! nlen)
                        continue;

                      /* We can stop looking now.  */
                      break;
                    }
                }
              while (*e != '\0');
            }
        }

      /* If we are inside an archive group, make sure it has an end.  */
      if (tp > tmpbuf)
        {
          if (tp[nlen-1] == ')')
            {
              /* This is the natural end; reset TP.  */
              tp = tmpbuf;

              /* This is just ")", something like "lib(a b )": skip it.  */
              if (nlen == 1)
                continue;
            }
          else
            {
              /* Not the end, so add a "fake" end.  */
              tp[nlen++] = ')';
              tp[nlen] = '\0';
            }
        }
#endif

      /* If we're not globbing we're done: add it to the end of the chain.
         Go to the next item in the string.  */
      if (flags & PARSEFS_NOGLOB)
        {
          NEWELT (concat (2, prefix, tp));
          continue;
        }

      /* If we get here we know we're doing glob expansion.
         TP is a string in tmpbuf.  NLEN is no longer used.
         We may need to do more work: after this NAME will be set.  */
      name = tp;

      /* Expand tilde if applicable.  */
      if (tp[0] == '~')
    {
      tildep = tilde_expand (tp);
      if (tildep != 0)
            name = tildep;
    }

#ifndef NO_ARCHIVES
      /* If NAME is an archive member reference replace it with the archive
         file name, and save the member name in MEMNAME.  We will glob on the
         archive name and then reattach MEMNAME later.  */
      if (! (flags & PARSEFS_NOAR) && ar_name (name))
    {
      ar_parse_name (name, &arname, &memname);
      name = arname;
    }
#endif /* !NO_ARCHIVES */

      switch (glob (name, GLOB_NOSORT|GLOB_ALTDIRFUNC, NULL, &gl))
    {
    case GLOB_NOSPACE:
      fatal (NILF, _("virtual memory exhausted"));

    case 0:
          /* Success.  */
          i = gl.gl_pathc;
          nlist = (const char **)gl.gl_pathv;
          break;

        case GLOB_NOMATCH:
          /* If we want only existing items, skip this one.  */
          if (flags & PARSEFS_EXISTS)
            {
              i = 0;
              break;
            }
          /* FALLTHROUGH */

    default:
          /* By default keep this name.  */
          i = 1;
          nlist = &name;
          break;
    }

      /* For each matched element, add it to the list.  */
      while (i-- > 0)
#ifndef NO_ARCHIVES
        if (memname != 0)
          {
            /* Try to glob on MEMNAME within the archive.  */
            struct nameseq *found = ar_glob (nlist[i], memname, size);
            if (! found)
              /* No matches.  Use MEMNAME as-is.  */
              NEWELT (concat (5, prefix, nlist[i], "(", memname, ")"));
            else
              {
                /* We got a chain of items.  Attach them.  */
                (*newp)->next = found;

                /* Find and set the new end.  Massage names if necessary.  */
                while (1)
                  {
                    if (! cachep)
                      found->name = xstrdup (concat (2, prefix, name));
                    else if (prefix)
                      found->name = strcache_add (concat (2, prefix, name));

                    if (found->next == 0)
                      break;

                    found = found->next;
                  }
                newp = &found->next;
              }
          }
        else
#endif /* !NO_ARCHIVES */
          NEWELT (concat (2, prefix, nlist[i]));

      globfree (&gl);

#ifndef NO_ARCHIVES
      if (arname)
        free (arname);
#endif

      if (tildep)
        free (tildep);
    }

  *stringp = p;
  return new;
}

https://git.savannah.gnu.org/cgit/make.git/tree/glob/glob.h?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/glob/glob.h used in: Step 529
/* Copyright (C) 1991, 1992, 1995, 1996, 1997, 1998 Free Software Foundation,
Inc.

The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

The GNU C Library 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.

You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB.  If not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
USA.  */

#ifndef _GLOB_H
#define _GLOB_H 1

#ifdef  __cplusplus
extern "C" {
#endif

#undef  __ptr_t
#if defined __cplusplus || (defined __STDC__ && __STDC__) || defined WINDOWS32
# if !defined __GLIBC__
#  undef __P
#  undef __PMT
#  define __P(protos)   protos
#  define __PMT(protos) protos
#  if !defined __GNUC__ || __GNUC__ < 2
#   undef __const
#   define __const const
#  endif
# endif
# define __ptr_t    void *
#else /* Not C++ or ANSI C.  */
# undef __P
# undef __PMT
# define __P(protos)    ()
# define __PMT(protos)  ()
# undef __const
# define __const
# define __ptr_t    char *
#endif /* C++ or ANSI C.  */

/* We need `size_t' for the following definitions.  */
#ifndef __size_t
# if defined __FreeBSD__
#  define __size_t size_t
# else
#  if defined __GNUC__ && __GNUC__ >= 2
typedef __SIZE_TYPE__ __size_t;
#  else
/* This is a guess.  */
/*hb
 *  Conflicts with DECCs aready defined type __size_t.
 *  Defining an own type with a name beginning with '__' is no good.
 *  Anyway if DECC is used and __SIZE_T is defined then __size_t is
 *  already defined (and I hope it's exactly the one we need here).
 */
#   if !(defined __DECC && defined __SIZE_T)
typedef unsigned long int __size_t;
#   endif
#  endif
# endif
#else
/* The GNU CC stddef.h version defines __size_t as empty.  We need a real
   definition.  */
# undef __size_t
# define __size_t size_t
#endif

/* Bits set in the FLAGS argument to `glob'.  */
#define GLOB_ERR    (1 << 0)/* Return on read errors.  */
#define GLOB_MARK   (1 << 1)/* Append a slash to each name.  */
#define GLOB_NOSORT (1 << 2)/* Don't sort the names.  */
#define GLOB_DOOFFS (1 << 3)/* Insert PGLOB->gl_offs NULLs.  */
#define GLOB_NOCHECK    (1 << 4)/* If nothing matches, return the pattern.  */
#define GLOB_APPEND (1 << 5)/* Append to results of a previous call.  */
#define GLOB_NOESCAPE   (1 << 6)/* Backslashes don't quote metacharacters.  */
#define GLOB_PERIOD (1 << 7)/* Leading `.' can be matched by metachars.  */

#if (!defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _BSD_SOURCE \
     || defined _GNU_SOURCE)
# define GLOB_MAGCHAR    (1 << 8)/* Set in gl_flags if any metachars seen.  */
# define GLOB_ALTDIRFUNC (1 << 9)/* Use gl_opendir et al functions.  */
# define GLOB_BRACE  (1 << 10)/* Expand "{a,b}" to "a" "b".  */
# define GLOB_NOMAGIC    (1 << 11)/* If no magic chars, return the pattern.  */
# define GLOB_TILDE  (1 << 12)/* Expand ~user and ~ to home directories. */
# define GLOB_ONLYDIR    (1 << 13)/* Match only directories.  */
# define GLOB_TILDE_CHECK (1 << 14)/* Like GLOB_TILDE but return an error
                      if the user name is not available.  */
# define __GLOB_FLAGS   (GLOB_ERR|GLOB_MARK|GLOB_NOSORT|GLOB_DOOFFS| \
             GLOB_NOESCAPE|GLOB_NOCHECK|GLOB_APPEND|     \
             GLOB_PERIOD|GLOB_ALTDIRFUNC|GLOB_BRACE|     \
             GLOB_NOMAGIC|GLOB_TILDE|GLOB_ONLYDIR|GLOB_TILDE_CHECK)
#else
# define __GLOB_FLAGS   (GLOB_ERR|GLOB_MARK|GLOB_NOSORT|GLOB_DOOFFS| \
             GLOB_NOESCAPE|GLOB_NOCHECK|GLOB_APPEND|     \
             GLOB_PERIOD)
#endif

/* Error returns from `glob'.  */
#define GLOB_NOSPACE    1   /* Ran out of memory.  */
#define GLOB_ABORTED    2   /* Read error.  */
#define GLOB_NOMATCH    3   /* No matches found.  */
#define GLOB_NOSYS  4   /* Not implemented.  */
#ifdef _GNU_SOURCE
/* Previous versions of this file defined GLOB_ABEND instead of
   GLOB_ABORTED.  Provide a compatibility definition here.  */
# define GLOB_ABEND GLOB_ABORTED
#endif

/* Structure describing a globbing run.  */
#if !defined _AMIGA && !defined VMS /* Buggy compiler.   */
struct stat;
#endif
typedef struct
  {
    __size_t gl_pathc;      /* Count of paths matched by the pattern.  */
    char **gl_pathv;        /* List of matched pathnames.  */
    __size_t gl_offs;       /* Slots to reserve in `gl_pathv'.  */
    int gl_flags;       /* Set to FLAGS, maybe | GLOB_MAGCHAR.  */

    /* If the GLOB_ALTDIRFUNC flag is set, the following functions
       are used instead of the normal file access functions.  */
    void (*gl_closedir) __PMT ((void *));
    struct dirent *(*gl_readdir) __PMT ((void *));
    __ptr_t (*gl_opendir) __PMT ((__const char *));
    int (*gl_lstat) __PMT ((__const char *, struct stat *));
#if defined(VMS) && defined(__DECC) && !defined(_POSIX_C_SOURCE)
    int (*gl_stat) __PMT ((__const char *, struct stat *, ...));
#else
    int (*gl_stat) __PMT ((__const char *, struct stat *));
#endif
  } glob_t;

#ifdef _LARGEFILE64_SOURCE
struct stat64;
typedef struct
  {
    __size_t gl_pathc;
    char **gl_pathv;
    __size_t gl_offs;
    int gl_flags;

    /* If the GLOB_ALTDIRFUNC flag is set, the following functions
       are used instead of the normal file access functions.  */
    void (*gl_closedir) __PMT ((void *));
    struct dirent64 *(*gl_readdir) __PMT ((void *));
    __ptr_t (*gl_opendir) __PMT ((__const char *));
    int (*gl_lstat) __PMT ((__const char *, struct stat64 *));
    int (*gl_stat) __PMT ((__const char *, struct stat64 *));
  } glob64_t;
#endif

#if _FILE_OFFSET_BITS == 64 && __GNUC__ < 2
# define glob glob64
# define globfree globfree64
#else
# ifdef _LARGEFILE64_SOURCE
extern int glob64 __P ((__const char *__pattern, int __flags,
            int (*__errfunc) (__const char *, int),
            glob64_t *__pglob));

extern void globfree64 __P ((glob64_t *__pglob));
# endif
#endif

/* Do glob searching for PATTERN, placing results in PGLOB.
   The bits defined above may be set in FLAGS.
   If a directory cannot be opened or read and ERRFUNC is not nil,
   it is called with the pathname that caused the error, and the
   `errno' value from the failing call; if it returns non-zero
   `glob' returns GLOB_ABEND; if it returns zero, the error is ignored.
   If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned.
   Otherwise, `glob' returns zero.  */
#if _FILE_OFFSET_BITS != 64 || __GNUC__ < 2
extern int glob __P ((__const char *__pattern, int __flags,
              int (*__errfunc) (__const char *, int),
              glob_t *__pglob));

/* Free storage allocated in PGLOB by a previous `glob' call.  */
extern void globfree __P ((glob_t *__pglob));
#else
extern int glob __P ((__const char *__pattern, int __flags,
              int (*__errfunc) (__const char *, int),
              glob_t *__pglob)) __asm__ ("glob64");

extern void globfree __P ((glob_t *__pglob)) __asm__ ("globfree64");
#endif


#ifdef _GNU_SOURCE
/* Return nonzero if PATTERN contains any metacharacters.
   Metacharacters can be quoted with backslashes if QUOTE is nonzero.

   This function is not part of the interface specified by POSIX.2
   but several programs want to use it.  */
extern int glob_pattern_p __P ((__const char *__pattern, int __quote));
#endif

#ifdef  __cplusplus
}
#endif

#endif /* glob.h  */

https://git.savannah.gnu.org/cgit/make.git/tree/remake.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/remake.c used in: Step 530
/* Basic dependency engine for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"
#include "filedef.h"
#include "job.h"
#include "commands.h"
#include "dep.h"
#include "variable.h"
#include "debug.h"

#include <assert.h>

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#else
#include <sys/file.h>
#endif

#ifdef VMS
#include <starlet.h>
#endif
#ifdef WINDOWS32
#include <io.h>
#endif

extern int try_implicit_rule (struct file *file, unsigned int depth);


/* The test for circular dependencies is based on the 'updating' bit in
   `struct file'.  However, double colon targets have seperate `struct
   file's; make sure we always use the base of the double colon chain. */

#define start_updating(_f)  (((_f)->double_colon ? (_f)->double_colon : (_f))\
                             ->updating = 1)
#define finish_updating(_f) (((_f)->double_colon ? (_f)->double_colon : (_f))\
                             ->updating = 0)
#define is_updating(_f)     (((_f)->double_colon ? (_f)->double_colon : (_f))\
                             ->updating)


/* Incremented when a command is started (under -n, when one would be).  */
unsigned int commands_started = 0;

/* Current value for pruning the scan of the goal chain (toggle 0/1).  */
static unsigned int considered;

static int update_file (struct file *file, unsigned int depth);
static int update_file_1 (struct file *file, unsigned int depth);
static int check_dep (struct file *file, unsigned int depth,
                      FILE_TIMESTAMP this_mtime, int *must_make_ptr);
static int touch_file (struct file *file);
static void remake_file (struct file *file);
static FILE_TIMESTAMP name_mtime (const char *name);
static const char *library_search (const char *lib, FILE_TIMESTAMP *mtime_ptr);



/* Remake all the goals in the `struct dep' chain GOALS.  Return -1 if nothing
   was done, 0 if all goals were updated successfully, or 1 if a goal failed.

   If rebuilding_makefiles is nonzero, these goals are makefiles, so -t, -q,
   and -n should be disabled for them unless they were also command-line
   targets, and we should only make one goal at a time and return as soon as
   one goal whose `changed' member is nonzero is successfully made.  */

int
update_goal_chain (struct dep *goals)
{
  int t = touch_flag, q = question_flag, n = just_print_flag;
  int status = -1;

#define MTIME(file) (rebuilding_makefiles ? file_mtime_no_search (file) \
             : file_mtime (file))

  /* Duplicate the chain so we can remove things from it.  */

  goals = copy_dep_chain (goals);

  {
    /* Clear the `changed' flag of each goal in the chain.
       We will use the flag below to notice when any commands
       have actually been run for a target.  When no commands
       have been run, we give an "up to date" diagnostic.  */

    struct dep *g;
    for (g = goals; g != 0; g = g->next)
      g->changed = 0;
  }

  /* All files start with the considered bit 0, so the global value is 1.  */
  considered = 1;

  /* Update all the goals until they are all finished.  */

  while (goals != 0)
    {
      register struct dep *g, *lastgoal;

      /* Start jobs that are waiting for the load to go down.  */

      start_waiting_jobs ();

      /* Wait for a child to die.  */

      reap_children (1, 0);

      lastgoal = 0;
      g = goals;
      while (g != 0)
    {
      /* Iterate over all double-colon entries for this file.  */
      struct file *file;
      int stop = 0, any_not_updated = 0;

      for (file = g->file->double_colon ? g->file->double_colon : g->file;
           file != NULL;
           file = file->prev)
        {
          unsigned int ocommands_started;
          int x;

              file->dontcare = g->dontcare;

          check_renamed (file);
          if (rebuilding_makefiles)
        {
          if (file->cmd_target)
            {
              touch_flag = t;
              question_flag = q;
              just_print_flag = n;
            }
          else
            touch_flag = question_flag = just_print_flag = 0;
        }

          /* Save the old value of `commands_started' so we can compare
         later.  It will be incremented when any commands are
         actually run.  */
          ocommands_started = commands_started;

          x = update_file (file, rebuilding_makefiles ? 1 : 0);
          check_renamed (file);

          /* Set the goal's `changed' flag if any commands were started
         by calling update_file above.  We check this flag below to
         decide when to give an "up to date" diagnostic.  */
              if (commands_started > ocommands_started)
                g->changed = 1;

              /* If we updated a file and STATUS was not already 1, set it to
                 1 if updating failed, or to 0 if updating succeeded.  Leave
                 STATUS as it is if no updating was done.  */

          stop = 0;
          if ((x != 0 || file->updated) && status < 1)
                {
                  if (file->update_status != 0)
                    {
                      /* Updating failed, or -q triggered.  The STATUS value
                         tells our caller which.  */
                      status = file->update_status;
                      /* If -q just triggered, stop immediately.  It doesn't
                         matter how much more we run, since we already know
                         the answer to return.  */
                      stop = (question_flag && !keep_going_flag
                              && !rebuilding_makefiles);
                    }
                  else
                    {
                      FILE_TIMESTAMP mtime = MTIME (file);
                      check_renamed (file);

                      if (file->updated && g->changed &&
                           mtime != file->mtime_before_update)
                        {
                          /* Updating was done.  If this is a makefile and
                             just_print_flag or question_flag is set (meaning
                             -n or -q was given and this file was specified
                             as a command-line target), don't change STATUS.
                             If STATUS is changed, we will get re-exec'd, and
                             enter an infinite loop.  */
                          if (!rebuilding_makefiles
                              || (!just_print_flag && !question_flag))
                            status = 0;
                          if (rebuilding_makefiles && file->dontcare)
                            /* This is a default makefile; stop remaking.  */
                            stop = 1;
                        }
                    }
                }

          /* Keep track if any double-colon entry is not finished.
                 When they are all finished, the goal is finished.  */
          any_not_updated |= !file->updated;

              file->dontcare = 0;

          if (stop)
        break;
        }

      /* Reset FILE since it is null at the end of the loop.  */
      file = g->file;

      if (stop || !any_not_updated)
        {
          /* If we have found nothing whatever to do for the goal,
         print a message saying nothing needs doing.  */

          if (!rebuilding_makefiles
          /* If the update_status is zero, we updated successfully
             or not at all.  G->changed will have been set above if
             any commands were actually started for this goal.  */
          && file->update_status == 0 && !g->changed
          /* Never give a message under -s or -q.  */
          && !silent_flag && !question_flag)
        message (1, ((file->phony || file->cmds == 0)
                 ? _("Nothing to be done for `%s'.")
                 : _("`%s' is up to date.")),
             file->name);

          /* This goal is finished.  Remove it from the chain.  */
          if (lastgoal == 0)
        goals = g->next;
          else
        lastgoal->next = g->next;

          /* Free the storage.  */
          free (g);

          g = lastgoal == 0 ? goals : lastgoal->next;

          if (stop)
        break;
        }
      else
        {
          lastgoal = g;
          g = g->next;
        }
    }

      /* If we reached the end of the dependency graph toggle the considered
         flag for the next pass.  */
      if (g == 0)
        considered = !considered;
    }

  if (rebuilding_makefiles)
    {
      touch_flag = t;
      question_flag = q;
      just_print_flag = n;
    }

  return status;
}


/* If FILE is not up to date, execute the commands for it.
   Return 0 if successful, 1 if unsuccessful;
   but with some flag settings, just call `exit' if unsuccessful.

   DEPTH is the depth in recursions of this function.
   We increment it during the consideration of our dependencies,
   then decrement it again after finding out whether this file
   is out of date.

   If there are multiple double-colon entries for FILE,
   each is considered in turn.  */

static int
update_file (struct file *file, unsigned int depth)
{
  register int status = 0;
  register struct file *f;

  f = file->double_colon ? file->double_colon : file;

  /* Prune the dependency graph: if we've already been here on _this_
     pass through the dependency graph, we don't have to go any further.
     We won't reap_children until we start the next pass, so no state
     change is possible below here until then.  */
  if (f->considered == considered)
    {
      /* Check for the case where a target has been tried and failed but
         the diagnostics hasn't been issued. If we need the diagnostics
         then we will have to continue. */
      if (!(f->updated && f->update_status > 0 && !f->dontcare && f->no_diag))
        {
          DBF (DB_VERBOSE, _("Pruning file `%s'.\n"));
          return f->command_state == cs_finished ? f->update_status : 0;
        }
    }

  /* This loop runs until we start commands for a double colon rule, or until
     the chain is exhausted. */
  for (; f != 0; f = f->prev)
    {
      f->considered = considered;

      status |= update_file_1 (f, depth);
      check_renamed (f);

      /* Clean up any alloca() used during the update.  */
      alloca (0);

      /* If we got an error, don't bother with double_colon etc.  */
      if (status != 0 && !keep_going_flag)
        return status;

      if (f->command_state == cs_running
          || f->command_state == cs_deps_running)
        {
      /* Don't run the other :: rules for this
         file until this rule is finished.  */
          status = 0;
          break;
        }
    }

  /* Process the remaining rules in the double colon chain so they're marked
     considered.  Start their prerequisites, too.  */
  if (file->double_colon)
    for (; f != 0 ; f = f->prev)
      {
        struct dep *d;

        f->considered = considered;

        for (d = f->deps; d != 0; d = d->next)
          status |= update_file (d->file, depth + 1);
      }

  return status;
}


/* Show a message stating the target failed to build.  */

static void
complain (struct file *file)
{
  const char *msg_noparent
    = _("%sNo rule to make target `%s'%s");
  const char *msg_parent
    = _("%sNo rule to make target `%s', needed by `%s'%s");

  /* If this file has no_diag set then it means we tried to update it
     before in the dontcare mode and failed. The target that actually
     failed is not necessarily this file but could be one of its direct
     or indirect dependencies. So traverse this file's dependencies and
     find the one that actually caused the failure. */

  struct dep *d;

  for (d = file->deps; d != 0; d = d->next)
    {
      if (d->file->updated && d->file->update_status > 0 && file->no_diag)
        {
          complain (d->file);
          break;
        }
    }

  if (d == 0)
    {
      /* Didn't find any dependencies to complain about. */
      if (!keep_going_flag)
        {
          if (file->parent == 0)
            fatal (NILF, msg_noparent, "", file->name, "");

          fatal (NILF, msg_parent, "", file->name, file->parent->name, "");
        }

      if (file->parent == 0)
        error (NILF, msg_noparent, "*** ", file->name, ".");
      else
        error (NILF, msg_parent, "*** ", file->name, file->parent->name, ".");

      file->no_diag = 0;
    }
}

/* Consider a single `struct file' and update it as appropriate.  */

static int
update_file_1 (struct file *file, unsigned int depth)
{
  FILE_TIMESTAMP this_mtime;
  int noexist, must_make, deps_changed;
  int dep_status = 0;
  struct file *ofile;
  struct dep *d, *ad;
  struct dep amake;
  int running = 0;

  DBF (DB_VERBOSE, _("Considering target file `%s'.\n"));

  if (file->updated)
    {
      if (file->update_status > 0)
    {
      DBF (DB_VERBOSE,
               _("Recently tried and failed to update file `%s'.\n"));

          /* If the file we tried to make is marked no_diag then no message
             was printed about it when it failed during the makefile rebuild.
             If we're trying to build it again in the normal rebuild, print a
             message now.  */
          if (file->no_diag && !file->dontcare)
              complain (file);

      return file->update_status;
    }

      DBF (DB_VERBOSE, _("File `%s' was considered already.\n"));
      return 0;
    }

  switch (file->command_state)
    {
    case cs_not_started:
    case cs_deps_running:
      break;
    case cs_running:
      DBF (DB_VERBOSE, _("Still updating file `%s'.\n"));
      return 0;
    case cs_finished:
      DBF (DB_VERBOSE, _("Finished updating file `%s'.\n"));
      return file->update_status;
    default:
      abort ();
    }

  /* Determine whether the diagnostics will be issued should this update
     fail. */
  file->no_diag = file->dontcare;

  ++depth;

  /* Notice recursive update of the same file.  */
  start_updating (file);

  /* We might change file if we find a different one via vpath;
     remember this one to turn off updating.  */
  ofile = file;

  /* Looking at the file's modtime beforehand allows the possibility
     that its name may be changed by a VPATH search, and thus it may
     not need an implicit rule.  If this were not done, the file
     might get implicit commands that apply to its initial name, only
     to have that name replaced with another found by VPATH search.  */

  this_mtime = file_mtime (file);
  check_renamed (file);
  noexist = this_mtime == NONEXISTENT_MTIME;
  if (noexist)
    DBF (DB_BASIC, _("File `%s' does not exist.\n"));
  else if (ORDINARY_MTIME_MIN <= this_mtime && this_mtime <= ORDINARY_MTIME_MAX
       && file->low_resolution_time)
    {
      /* Avoid spurious rebuilds due to low resolution time stamps.  */
      int ns = FILE_TIMESTAMP_NS (this_mtime);
      if (ns != 0)
    error (NILF, _("*** Warning: .LOW_RESOLUTION_TIME file `%s' has a high resolution time stamp"),
           file->name);
      this_mtime += FILE_TIMESTAMPS_PER_S - 1 - ns;
    }

  must_make = noexist;

  /* If file was specified as a target with no commands,
     come up with some default commands.  */

  if (!file->phony && file->cmds == 0 && !file->tried_implicit)
    {
      if (try_implicit_rule (file, depth))
    DBF (DB_IMPLICIT, _("Found an implicit rule for `%s'.\n"));
      else
    DBF (DB_IMPLICIT, _("No implicit rule found for `%s'.\n"));
      file->tried_implicit = 1;
    }
  if (file->cmds == 0 && !file->is_target
      && default_file != 0 && default_file->cmds != 0)
    {
      DBF (DB_IMPLICIT, _("Using default recipe for `%s'.\n"));
      file->cmds = default_file->cmds;
    }

  /* Update all non-intermediate files we depend on, if necessary, and see
     whether any of them is more recent than this file.  We need to walk our
     deps, AND the deps of any also_make targets to ensure everything happens
     in the correct order.  */

  amake.file = file;
  amake.next = file->also_make;
  ad = &amake;
  while (ad)
    {
      struct dep *lastd = 0;

      /* Find the deps we're scanning */
      d = ad->file->deps;
      ad = ad->next;

      while (d)
        {
          FILE_TIMESTAMP mtime;
          int maybe_make;
          int dontcare = 0;

          check_renamed (d->file);

          mtime = file_mtime (d->file);
          check_renamed (d->file);

          if (is_updating (d->file))
            {
              error (NILF, _("Circular %s <- %s dependency dropped."),
                     file->name, d->file->name);
              /* We cannot free D here because our the caller will still have
                 a reference to it when we were called recursively via
                 check_dep below.  */
              if (lastd == 0)
                file->deps = d->next;
              else
                lastd->next = d->next;
              d = d->next;
              continue;
            }

          d->file->parent = file;
          maybe_make = must_make;

          /* Inherit dontcare flag from our parent. */
          if (rebuilding_makefiles)
            {
              dontcare = d->file->dontcare;
              d->file->dontcare = file->dontcare;
            }

          dep_status |= check_dep (d->file, depth, this_mtime, &maybe_make);

          /* Restore original dontcare flag. */
          if (rebuilding_makefiles)
            d->file->dontcare = dontcare;

          if (! d->ignore_mtime)
            must_make = maybe_make;

          check_renamed (d->file);

          {
            register struct file *f = d->file;
            if (f->double_colon)
              f = f->double_colon;
            do
              {
                running |= (f->command_state == cs_running
                            || f->command_state == cs_deps_running);
                f = f->prev;
              }
            while (f != 0);
          }

          if (dep_status != 0 && !keep_going_flag)
            break;

          if (!running)
            /* The prereq is considered changed if the timestamp has changed while
               it was built, OR it doesn't exist.  */
            d->changed = ((file_mtime (d->file) != mtime)
                          || (mtime == NONEXISTENT_MTIME));

          lastd = d;
          d = d->next;
        }
    }

  /* Now we know whether this target needs updating.
     If it does, update all the intermediate files we depend on.  */

  if (must_make || always_make_flag)
    {
      for (d = file->deps; d != 0; d = d->next)
    if (d->file->intermediate)
      {
            int dontcare = 0;

        FILE_TIMESTAMP mtime = file_mtime (d->file);
        check_renamed (d->file);
        d->file->parent = file;

            /* Inherit dontcare flag from our parent. */
            if (rebuilding_makefiles)
              {
                dontcare = d->file->dontcare;
                d->file->dontcare = file->dontcare;
              }


        dep_status |= update_file (d->file, depth);

            /* Restore original dontcare flag. */
            if (rebuilding_makefiles)
              d->file->dontcare = dontcare;

        check_renamed (d->file);

        {
          register struct file *f = d->file;
          if (f->double_colon)
        f = f->double_colon;
          do
        {
          running |= (f->command_state == cs_running
                  || f->command_state == cs_deps_running);
          f = f->prev;
        }
          while (f != 0);
        }

        if (dep_status != 0 && !keep_going_flag)
          break;

        if (!running)
          d->changed = ((file->phony && file->cmds != 0)
                || file_mtime (d->file) != mtime);
      }
    }

  finish_updating (file);
  finish_updating (ofile);

  DBF (DB_VERBOSE, _("Finished prerequisites of target file `%s'.\n"));

  if (running)
    {
      set_command_state (file, cs_deps_running);
      --depth;
      DBF (DB_VERBOSE, _("The prerequisites of `%s' are being made.\n"));
      return 0;
    }

  /* If any dependency failed, give up now.  */

  if (dep_status != 0)
    {
      file->update_status = dep_status;
      notice_finished_file (file);

      --depth;

      DBF (DB_VERBOSE, _("Giving up on target file `%s'.\n"));

      if (depth == 0 && keep_going_flag
      && !just_print_flag && !question_flag)
    error (NILF,
               _("Target `%s' not remade because of errors."), file->name);

      return dep_status;
    }

  if (file->command_state == cs_deps_running)
    /* The commands for some deps were running on the last iteration, but
       they have finished now.  Reset the command_state to not_started to
       simplify later bookkeeping.  It is important that we do this only
       when the prior state was cs_deps_running, because that prior state
       was definitely propagated to FILE's also_make's by set_command_state
       (called above), but in another state an also_make may have
       independently changed to finished state, and we would confuse that
       file's bookkeeping (updated, but not_started is bogus state).  */
    set_command_state (file, cs_not_started);

  /* Now record which prerequisites are more
     recent than this file, so we can define $?.  */

  deps_changed = 0;
  for (d = file->deps; d != 0; d = d->next)
    {
      FILE_TIMESTAMP d_mtime = file_mtime (d->file);
      check_renamed (d->file);

      if (! d->ignore_mtime)
        {
#if 1
          /* %%% In version 4, remove this code completely to
       implement not remaking deps if their deps are newer
       than their parents.  */
          if (d_mtime == NONEXISTENT_MTIME && !d->file->intermediate)
            /* We must remake if this dep does not
               exist and is not intermediate.  */
            must_make = 1;
#endif

          /* Set DEPS_CHANGED if this dep actually changed.  */
          deps_changed |= d->changed;
        }

      /* Set D->changed if either this dep actually changed,
     or its dependent, FILE, is older or does not exist.  */
      d->changed |= noexist || d_mtime > this_mtime;

      if (!noexist && ISDB (DB_BASIC|DB_VERBOSE))
    {
          const char *fmt = 0;

          if (d->ignore_mtime)
            {
              if (ISDB (DB_VERBOSE))
                fmt = _("Prerequisite `%s' is order-only for target `%s'.\n");
            }
          else if (d_mtime == NONEXISTENT_MTIME)
            {
              if (ISDB (DB_BASIC))
                fmt = _("Prerequisite `%s' of target `%s' does not exist.\n");
            }
      else if (d->changed)
            {
              if (ISDB (DB_BASIC))
                fmt = _("Prerequisite `%s' is newer than target `%s'.\n");
            }
          else if (ISDB (DB_VERBOSE))
            fmt = _("Prerequisite `%s' is older than target `%s'.\n");

          if (fmt)
            {
              print_spaces (depth);
              printf (fmt, dep_name (d), file->name);
              fflush (stdout);
            }
    }
    }

  /* Here depth returns to the value it had when we were called.  */
  depth--;

  if (file->double_colon && file->deps == 0)
    {
      must_make = 1;
      DBF (DB_BASIC,
           _("Target `%s' is double-colon and has no prerequisites.\n"));
    }
  else if (!noexist && file->is_target && !deps_changed && file->cmds == 0
           && !always_make_flag)
    {
      must_make = 0;
      DBF (DB_VERBOSE,
           _("No recipe for `%s' and no prerequisites actually changed.\n"));
    }
  else if (!must_make && file->cmds != 0 && always_make_flag)
    {
      must_make = 1;
      DBF (DB_VERBOSE, _("Making `%s' due to always-make flag.\n"));
    }

  if (!must_make)
    {
      if (ISDB (DB_VERBOSE))
        {
          print_spaces (depth);
          printf (_("No need to remake target `%s'"), file->name);
          if (!streq (file->name, file->hname))
              printf (_("; using VPATH name `%s'"), file->hname);
          puts (".");
          fflush (stdout);
        }

      notice_finished_file (file);

      /* Since we don't need to remake the file, convert it to use the
         VPATH filename if we found one.  hfile will be either the
         local name if no VPATH or the VPATH name if one was found.  */

      while (file)
        {
          file->name = file->hname;
          file = file->prev;
        }

      return 0;
    }

  DBF (DB_BASIC, _("Must remake target `%s'.\n"));

  /* It needs to be remade.  If it's VPATH and not reset via GPATH, toss the
     VPATH.  */
  if (!streq(file->name, file->hname))
    {
      DB (DB_BASIC, (_("  Ignoring VPATH name `%s'.\n"), file->hname));
      file->ignore_vpath = 1;
    }

  /* Now, take appropriate actions to remake the file.  */
  remake_file (file);

  if (file->command_state != cs_finished)
    {
      DBF (DB_VERBOSE, _("Recipe of `%s' is being run.\n"));
      return 0;
    }

  switch (file->update_status)
    {
    case 2:
      DBF (DB_BASIC, _("Failed to remake target file `%s'.\n"));
      break;
    case 0:
      DBF (DB_BASIC, _("Successfully remade target file `%s'.\n"));
      break;
    case 1:
      DBF (DB_BASIC, _("Target file `%s' needs remade under -q.\n"));
      break;
    default:
      assert (file->update_status >= 0 && file->update_status <= 2);
      break;
    }

  file->updated = 1;
  return file->update_status;
}


/* Set FILE's `updated' flag and re-check its mtime and the mtime's of all
   files listed in its `also_make' member.  Under -t, this function also
   touches FILE.

   On return, FILE->update_status will no longer be -1 if it was.  */

void
notice_finished_file (struct file *file)
{
  struct dep *d;
  int ran = file->command_state == cs_running;
  int touched = 0;

  file->command_state = cs_finished;
  file->updated = 1;

  if (touch_flag
      /* The update status will be:
        -1  if this target was not remade;
        0   if 0 or more commands (+ or ${MAKE}) were run and won;
        1   if some commands were run and lost.
     We touch the target if it has commands which either were not run
     or won when they ran (i.e. status is 0).  */
      && file->update_status == 0)
    {
      if (file->cmds != 0 && file->cmds->any_recurse)
    {
      /* If all the command lines were recursive,
         we don't want to do the touching.  */
      unsigned int i;
      for (i = 0; i < file->cmds->ncommand_lines; ++i)
        if (!(file->cmds->lines_flags[i] & COMMANDS_RECURSE))
          goto have_nonrecursing;
    }
      else
    {
    have_nonrecursing:
      if (file->phony)
        file->update_status = 0;
          /* According to POSIX, -t doesn't affect targets with no cmds.  */
      else if (file->cmds != 0)
            {
              /* Should set file's modification date and do nothing else.  */
              file->update_status = touch_file (file);

              /* Pretend we ran a real touch command, to suppress the
                 "`foo' is up to date" message.  */
              commands_started++;

              /* Request for the timestamp to be updated (and distributed
                 to the double-colon entries). Simply setting ran=1 would
                 almost have done the trick, but messes up with the also_make
                 updating logic below.  */
              touched = 1;
            }
    }
    }

  if (file->mtime_before_update == UNKNOWN_MTIME)
    file->mtime_before_update = file->last_mtime;

  if ((ran && !file->phony) || touched)
    {
      int i = 0;

      /* If -n, -t, or -q and all the commands are recursive, we ran them so
         really check the target's mtime again.  Otherwise, assume the target
         would have been updated. */

      if ((question_flag || just_print_flag || touch_flag) && file->cmds)
        {
          for (i = file->cmds->ncommand_lines; i > 0; --i)
            if (! (file->cmds->lines_flags[i-1] & COMMANDS_RECURSE))
              break;
        }

      /* If there were no commands at all, it's always new. */

      else if (file->is_target && file->cmds == 0)
    i = 1;

      file->last_mtime = i == 0 ? UNKNOWN_MTIME : NEW_MTIME;
    }

  if (file->double_colon)
    {
      /* If this is a double colon rule and it is the last one to be
         updated, propagate the change of modification time to all the
         double-colon entries for this file.

         We do it on the last update because it is important to handle
         individual entries as separate rules with separate timestamps
         while they are treated as targets and then as one rule with the
         unified timestamp when they are considered as a prerequisite
         of some target.  */

      struct file *f;
      FILE_TIMESTAMP max_mtime = file->last_mtime;

      /* Check that all rules were updated and at the same time find
         the max timestamp.  We assume UNKNOWN_MTIME is newer then
         any other value.  */
      for (f = file->double_colon; f != 0 && f->updated; f = f->prev)
        if (max_mtime != UNKNOWN_MTIME
            && (f->last_mtime == UNKNOWN_MTIME || f->last_mtime > max_mtime))
          max_mtime = f->last_mtime;

      if (f == 0)
        for (f = file->double_colon; f != 0; f = f->prev)
          f->last_mtime = max_mtime;
    }

  if (ran && file->update_status != -1)
    /* We actually tried to update FILE, which has
       updated its also_make's as well (if it worked).
       If it didn't work, it wouldn't work again for them.
       So mark them as updated with the same status.  */
    for (d = file->also_make; d != 0; d = d->next)
      {
    d->file->command_state = cs_finished;
    d->file->updated = 1;
    d->file->update_status = file->update_status;

    if (ran && !d->file->phony)
      /* Fetch the new modification time.
         We do this instead of just invalidating the cached time
         so that a vpath_search can happen.  Otherwise, it would
         never be done because the target is already updated.  */
      f_mtime (d->file, 0);
      }
  else if (file->update_status == -1)
    /* Nothing was done for FILE, but it needed nothing done.
       So mark it now as "succeeded".  */
    file->update_status = 0;
}


/* Check whether another file (whose mtime is THIS_MTIME) needs updating on
   account of a dependency which is file FILE.  If it does, store 1 in
   *MUST_MAKE_PTR.  In the process, update any non-intermediate files that
   FILE depends on (including FILE itself).  Return nonzero if any updating
   failed.  */

static int
check_dep (struct file *file, unsigned int depth,
           FILE_TIMESTAMP this_mtime, int *must_make_ptr)
{
  struct file *ofile;
  struct dep *d;
  int dep_status = 0;

  ++depth;
  start_updating (file);

  /* We might change file if we find a different one via vpath;
     remember this one to turn off updating.  */
  ofile = file;

  if (file->phony || !file->intermediate)
    {
      /* If this is a non-intermediate file, update it and record whether it
         is newer than THIS_MTIME.  */
      FILE_TIMESTAMP mtime;
      dep_status = update_file (file, depth);
      check_renamed (file);
      mtime = file_mtime (file);
      check_renamed (file);
      if (mtime == NONEXISTENT_MTIME || mtime > this_mtime)
    *must_make_ptr = 1;
    }
  else
    {
      /* FILE is an intermediate file.  */
      FILE_TIMESTAMP mtime;

      if (!file->phony && file->cmds == 0 && !file->tried_implicit)
    {
      if (try_implicit_rule (file, depth))
        DBF (DB_IMPLICIT, _("Found an implicit rule for `%s'.\n"));
      else
        DBF (DB_IMPLICIT, _("No implicit rule found for `%s'.\n"));
      file->tried_implicit = 1;
    }
      if (file->cmds == 0 && !file->is_target
      && default_file != 0 && default_file->cmds != 0)
    {
      DBF (DB_IMPLICIT, _("Using default commands for `%s'.\n"));
      file->cmds = default_file->cmds;
    }

      check_renamed (file);
      mtime = file_mtime (file);
      check_renamed (file);
      if (mtime != NONEXISTENT_MTIME && mtime > this_mtime)
        /* If the intermediate file actually exists and is newer, then we
           should remake from it.  */
    *must_make_ptr = 1;
      else
    {
          /* Otherwise, update all non-intermediate files we depend on, if
             necessary, and see whether any of them is more recent than the
             file on whose behalf we are checking.  */
      struct dep *ld;
          int deps_running = 0;

          /* If this target is not running, set it's state so that we check it
             fresh.  It could be it was checked as part of an order-only
             prerequisite and so wasn't rebuilt then, but should be now.  */
          if (file->command_state != cs_running)
            set_command_state (file, cs_not_started);

      ld = 0;
      d = file->deps;
      while (d != 0)
        {
              int maybe_make;

          if (is_updating (d->file))
        {
          error (NILF, _("Circular %s <- %s dependency dropped."),
             file->name, d->file->name);
          if (ld == 0)
            {
              file->deps = d->next;
                      free_dep (d);
              d = file->deps;
            }
          else
            {
              ld->next = d->next;
                      free_dep (d);
              d = ld->next;
            }
          continue;
        }

          d->file->parent = file;
              maybe_make = *must_make_ptr;
          dep_status |= check_dep (d->file, depth, this_mtime,
                                       &maybe_make);
              if (! d->ignore_mtime)
                *must_make_ptr = maybe_make;
          check_renamed (d->file);
          if (dep_status != 0 && !keep_going_flag)
        break;

          if (d->file->command_state == cs_running
          || d->file->command_state == cs_deps_running)
        deps_running = 1;

          ld = d;
          d = d->next;
        }

          if (deps_running)
            /* Record that some of FILE's deps are still being made.
               This tells the upper levels to wait on processing it until the
               commands are finished.  */
            set_command_state (file, cs_deps_running);
    }
    }

  finish_updating (file);
  finish_updating (ofile);

  return dep_status;
}


/* Touch FILE.  Return zero if successful, one if not.  */

#define TOUCH_ERROR(call) return (perror_with_name (call, file->name), 1)

static int
touch_file (struct file *file)
{
  if (!silent_flag)
    message (0, "touch %s", file->name);

#ifndef NO_ARCHIVES
  if (ar_name (file->name))
    return ar_touch (file->name);
  else
#endif
    {
      int fd = open (file->name, O_RDWR | O_CREAT, 0666);

      if (fd < 0)
    TOUCH_ERROR ("touch: open: ");
      else
    {
      struct stat statbuf;
      char buf = 'x';
          int e;

          EINTRLOOP (e, fstat (fd, &statbuf));
      if (e < 0)
        TOUCH_ERROR ("touch: fstat: ");
      /* Rewrite character 0 same as it already is.  */
      if (read (fd, &buf, 1) < 0)
        TOUCH_ERROR ("touch: read: ");
      if (lseek (fd, 0L, 0) < 0L)
        TOUCH_ERROR ("touch: lseek: ");
      if (write (fd, &buf, 1) < 0)
        TOUCH_ERROR ("touch: write: ");
      /* If file length was 0, we just
         changed it, so change it back.  */
      if (statbuf.st_size == 0)
        {
          (void) close (fd);
          fd = open (file->name, O_RDWR | O_TRUNC, 0666);
          if (fd < 0)
        TOUCH_ERROR ("touch: open: ");
        }
      (void) close (fd);
    }
    }

  return 0;
}


/* Having checked and updated the dependencies of FILE,
   do whatever is appropriate to remake FILE itself.
   Return the status from executing FILE's commands.  */

static void
remake_file (struct file *file)
{
  if (file->cmds == 0)
    {
      if (file->phony)
    /* Phony target.  Pretend it succeeded.  */
    file->update_status = 0;
      else if (file->is_target)
    /* This is a nonexistent target file we cannot make.
       Pretend it was successfully remade.  */
    file->update_status = 0;
      else
        {
          /* This is a dependency file we cannot remake.  Fail.  */
          if (!rebuilding_makefiles || !file->dontcare)
            complain (file);
          file->update_status = 2;
        }
    }
  else
    {
      chop_commands (file->cmds);

      /* The normal case: start some commands.  */
      if (!touch_flag || file->cmds->any_recurse)
    {
      execute_file_commands (file);
      return;
    }

      /* This tells notice_finished_file it is ok to touch the file.  */
      file->update_status = 0;
    }

  /* This does the touching under -t.  */
  notice_finished_file (file);
}


/* Return the mtime of a file, given a `struct file'.
   Caches the time in the struct file to avoid excess stat calls.

   If the file is not found, and SEARCH is nonzero, VPATH searching and
   replacement is done.  If that fails, a library (-lLIBNAME) is tried and
   the library's actual name (/lib/libLIBNAME.a, etc.) is substituted into
   FILE.  */

FILE_TIMESTAMP
f_mtime (struct file *file, int search)
{
  FILE_TIMESTAMP mtime;

  /* File's mtime is not known; must get it from the system.  */

#ifndef NO_ARCHIVES
  if (ar_name (file->name))
    {
      /* This file is an archive-member reference.  */

      char *arname, *memname;
      struct file *arfile;
      time_t member_date;

      /* Find the archive's name.  */
      ar_parse_name (file->name, &arname, &memname);

      /* Find the modification time of the archive itself.
     Also allow for its name to be changed via VPATH search.  */
      arfile = lookup_file (arname);
      if (arfile == 0)
        arfile = enter_file (strcache_add (arname));
      mtime = f_mtime (arfile, search);
      check_renamed (arfile);
      if (search && strcmp (arfile->hname, arname))
    {
      /* The archive's name has changed.
         Change the archive-member reference accordingly.  */

          char *name;
      unsigned int arlen, memlen;

      arlen = strlen (arfile->hname);
      memlen = strlen (memname);

      name = xmalloc (arlen + 1 + memlen + 2);
      memcpy (name, arfile->hname, arlen);
      name[arlen] = '(';
      memcpy (name + arlen + 1, memname, memlen);
      name[arlen + 1 + memlen] = ')';
      name[arlen + 1 + memlen + 1] = '\0';

          /* If the archive was found with GPATH, make the change permanent;
             otherwise defer it until later.  */
          if (arfile->name == arfile->hname)
            rename_file (file, name);
          else
            rehash_file (file, name);
          check_renamed (file);
    }

      free (arname);

      file->low_resolution_time = 1;

      if (mtime == NONEXISTENT_MTIME)
    /* The archive doesn't exist, so its members don't exist either.  */
    return NONEXISTENT_MTIME;

      member_date = ar_member_date (file->hname);
      mtime = (member_date == (time_t) -1
               ? NONEXISTENT_MTIME
               : file_timestamp_cons (file->hname, member_date, 0));
    }
  else
#endif
    {
      mtime = name_mtime (file->name);

      if (mtime == NONEXISTENT_MTIME && search && !file->ignore_vpath)
    {
      /* If name_mtime failed, search VPATH.  */
      const char *name = vpath_search (file->name, &mtime, NULL, NULL);
      if (name
          /* Last resort, is it a library (-lxxx)?  */
          || (file->name[0] == '-' && file->name[1] == 'l'
          && (name = library_search (file->name, &mtime)) != 0))
        {
          if (mtime != UNKNOWN_MTIME)
        /* vpath_search and library_search store UNKNOWN_MTIME
           if they didn't need to do a stat call for their work.  */
        file->last_mtime = mtime;

              /* If we found it in VPATH, see if it's in GPATH too; if so,
                 change the name right now; if not, defer until after the
                 dependencies are updated. */
              if (gpath_search (name, strlen(name) - strlen(file->name) - 1))
                {
                  rename_file (file, name);
                  check_renamed (file);
                  return file_mtime (file);
                }

          rehash_file (file, name);
          check_renamed (file);
              /* If the result of a vpath search is -o or -W, preserve it.
                 Otherwise, find the mtime of the resulting file.  */
              if (mtime != OLD_MTIME && mtime != NEW_MTIME)
                mtime = name_mtime (name);
        }
    }
    }

  /* Files can have bogus timestamps that nothing newly made will be
     "newer" than.  Updating their dependents could just result in loops.
     So notify the user of the anomaly with a warning.

     We only need to do this once, for now. */

  if (!clock_skew_detected
      && mtime != NONEXISTENT_MTIME && mtime != NEW_MTIME
      && !file->updated)
    {
      static FILE_TIMESTAMP adjusted_now;

      FILE_TIMESTAMP adjusted_mtime = mtime;

#if defined(WINDOWS32) || defined(__MSDOS__)
      /* Experimentation has shown that FAT filesystems can set file times
         up to 3 seconds into the future!  Play it safe.  */

#define FAT_ADJ_OFFSET  (FILE_TIMESTAMP) 3

      FILE_TIMESTAMP adjustment = FAT_ADJ_OFFSET << FILE_TIMESTAMP_LO_BITS;
      if (ORDINARY_MTIME_MIN + adjustment <= adjusted_mtime)
        adjusted_mtime -= adjustment;
#elif defined(__EMX__)
      /* FAT filesystems round time to the nearest even second!
         Allow for any file (NTFS or FAT) to perhaps suffer from this
         brain damage.  */
      FILE_TIMESTAMP adjustment = (((FILE_TIMESTAMP_S (adjusted_mtime) & 1) == 0
                     && FILE_TIMESTAMP_NS (adjusted_mtime) == 0)
                    ? (FILE_TIMESTAMP) 1 << FILE_TIMESTAMP_LO_BITS
                    : 0);
#endif

      /* If the file's time appears to be in the future, update our
         concept of the present and try once more.  */
      if (adjusted_now < adjusted_mtime)
        {
          int resolution;
          FILE_TIMESTAMP now = file_timestamp_now (&resolution);
          adjusted_now = now + (resolution - 1);
          if (adjusted_now < adjusted_mtime)
            {
#ifdef NO_FLOAT
              error (NILF, _("Warning: File `%s' has modification time in the future"),
                     file->name);
#else
              double from_now =
                (FILE_TIMESTAMP_S (mtime) - FILE_TIMESTAMP_S (now)
                 + ((FILE_TIMESTAMP_NS (mtime) - FILE_TIMESTAMP_NS (now))
                    / 1e9));
              char from_now_string[100];

              if (from_now >= 99 && from_now <= ULONG_MAX)
                sprintf (from_now_string, "%lu", (unsigned long) from_now);
              else
                sprintf (from_now_string, "%.2g", from_now);
              error (NILF, _("Warning: File `%s' has modification time %s s in the future"),
                     file->name, from_now_string);
#endif
              clock_skew_detected = 1;
            }
        }
    }

  /* Store the mtime into all the entries for this file.  */
  if (file->double_colon)
    file = file->double_colon;

  do
    {
      /* If this file is not implicit but it is intermediate then it was
     made so by the .INTERMEDIATE target.  If this file has never
     been built by us but was found now, it existed before make
     started.  So, turn off the intermediate bit so make doesn't
     delete it, since it didn't create it.  */
      if (mtime != NONEXISTENT_MTIME && file->command_state == cs_not_started
      && file->command_state == cs_not_started
      && !file->tried_implicit && file->intermediate)
    file->intermediate = 0;

      file->last_mtime = mtime;
      file = file->prev;
    }
  while (file != 0);

  return mtime;
}


/* Return the mtime of the file or archive-member reference NAME.  */

/* First, we check with stat().  If the file does not exist, then we return
   NONEXISTENT_MTIME.  If it does, and the symlink check flag is set, then
   examine each indirection of the symlink and find the newest mtime.
   This causes one duplicate stat() when -L is being used, but the code is
   much cleaner.  */

static FILE_TIMESTAMP
name_mtime (const char *name)
{
  FILE_TIMESTAMP mtime;
  struct stat st;
  int e;

  EINTRLOOP (e, stat (name, &st));
  if (e == 0)
    mtime = FILE_TIMESTAMP_STAT_MODTIME (name, st);
  else if (errno == ENOENT || errno == ENOTDIR)
    mtime = NONEXISTENT_MTIME;
  else
    {
      perror_with_name ("stat: ", name);
      return NONEXISTENT_MTIME;
    }

  /* If we get here we either found it, or it doesn't exist.
     If it doesn't exist see if we can use a symlink mtime instead.  */

#ifdef MAKE_SYMLINKS
#ifndef S_ISLNK
# define S_ISLNK(_m)     (((_m)&S_IFMT)==S_IFLNK)
#endif
  if (check_symlink_flag)
    {
      PATH_VAR (lpath);

      /* Check each symbolic link segment (if any).  Find the latest mtime
         amongst all of them (and the target file of course).
         Note that we have already successfully dereferenced all the links
         above.  So, if we run into any error trying to lstat(), or
         readlink(), or whatever, something bizarre-o happened.  Just give up
         and use whatever mtime we've already computed at that point.  */
      strcpy (lpath, name);
      while (1)
        {
          FILE_TIMESTAMP ltime;
          PATH_VAR (lbuf);
          long llen;
          char *p;

          EINTRLOOP (e, lstat (lpath, &st));
          if (e)
            {
              /* Just take what we have so far.  */
              if (errno != ENOENT && errno != ENOTDIR)
                perror_with_name ("lstat: ", lpath);
              break;
            }

          /* If this is not a symlink, we're done (we started with the real
             file's mtime so we don't need to test it again).  */
          if (!S_ISLNK (st.st_mode))
            break;

          /* If this mtime is newer than what we had, keep the new one.  */
          ltime = FILE_TIMESTAMP_STAT_MODTIME (lpath, st);
          if (ltime > mtime)
            mtime = ltime;

          /* Set up to check the file pointed to by this link.  */
          EINTRLOOP (llen, readlink (lpath, lbuf, GET_PATH_MAX));
          if (llen < 0)
            {
              /* Eh?  Just take what we have.  */
              perror_with_name ("readlink: ", lpath);
              break;
            }
          lbuf[llen] = '\0';

          /* If the target is fully-qualified or the source is just a
             filename, then the new path is the target.  Otherwise it's the
             source directory plus the target.  */
          if (lbuf[0] == '/' || (p = strrchr (lpath, '/')) == NULL)
            strcpy (lpath, lbuf);
          else if ((p - lpath) + llen + 2 > GET_PATH_MAX)
            /* Eh?  Path too long!  Again, just go with what we have.  */
            break;
          else
            /* Create the next step in the symlink chain.  */
            strcpy (p+1, lbuf);
        }
    }
#endif

  return mtime;
}


/* Search for a library file specified as -lLIBNAME, searching for a
   suitable library file in the system library directories and the VPATH
   directories.  */

static const char *
library_search (const char *lib, FILE_TIMESTAMP *mtime_ptr)
{
  static char *dirs[] =
    {
#ifndef _AMIGA
      "/lib",
      "/usr/lib",
#endif
#if defined(WINDOWS32) && !defined(LIBDIR)
/*
 * This is completely up to the user at product install time. Just define
 * a placeholder.
 */
#define LIBDIR "."
#endif
      LIBDIR,           /* Defined by configuration.  */
      0
    };

  const char *file = 0;
  char *libpatterns;
  FILE_TIMESTAMP mtime;

  /* Loop variables for the libpatterns value.  */
  char *p;
  const char *p2;
  unsigned int len;
  unsigned int liblen;

  /* Information about the earliest (in the vpath sequence) match.  */
  unsigned int best_vpath, best_path;
  unsigned int std_dirs = 0;

  char **dp;

  libpatterns = xstrdup (variable_expand ("$(.LIBPATTERNS)"));

  /* Skip the '-l'.  */
  lib += 2;
  liblen = strlen (lib);

  /* Loop through all the patterns in .LIBPATTERNS, and search on each one.
     To implement the linker-compatible behavior we have to search through
     all entries in .LIBPATTERNS and choose the "earliest" one.  */
  p2 = libpatterns;
  while ((p = find_next_token (&p2, &len)) != 0)
    {
      static char *buf = NULL;
      static unsigned int buflen = 0;
      static int libdir_maxlen = -1;
      char *libbuf = variable_expand ("");

      /* Expand the pattern using LIB as a replacement.  */
      {
    char c = p[len];
    char *p3, *p4;

    p[len] = '\0';
    p3 = find_percent (p);
    if (!p3)
      {
        /* Give a warning if there is no pattern.  */
        error (NILF, _(".LIBPATTERNS element `%s' is not a pattern"), p);
            p[len] = c;
        continue;
      }
    p4 = variable_buffer_output (libbuf, p, p3-p);
    p4 = variable_buffer_output (p4, lib, liblen);
    p4 = variable_buffer_output (p4, p3+1, len - (p3-p));
    p[len] = c;
      }

      /* Look first for `libNAME.a' in the current directory.  */
      mtime = name_mtime (libbuf);
      if (mtime != NONEXISTENT_MTIME)
    {
      if (mtime_ptr != 0)
        *mtime_ptr = mtime;
      file = strcache_add (libbuf);
          /* This by definition will have the best index, so stop now.  */
          break;
    }

      /* Now try VPATH search on that.  */

      {
        unsigned int vpath_index, path_index;
        const char* f = vpath_search (libbuf, mtime_ptr ? &mtime : NULL,
                                      &vpath_index, &path_index);
        if (f)
          {
            /* If we have a better match, record it.  */
            if (file == 0 ||
                vpath_index < best_vpath ||
                (vpath_index == best_vpath && path_index < best_path))
              {
                file = f;
                best_vpath = vpath_index;
                best_path = path_index;

                if (mtime_ptr != 0)
                  *mtime_ptr = mtime;
              }
          }
      }

      /* Now try the standard set of directories.  */

      if (!buflen)
        {
          for (dp = dirs; *dp != 0; ++dp)
            {
              int l = strlen (*dp);
              if (l > libdir_maxlen)
                libdir_maxlen = l;
              std_dirs++;
            }
          buflen = strlen (libbuf);
          buf = xmalloc(libdir_maxlen + buflen + 2);
        }
      else if (buflen < strlen (libbuf))
        {
          buflen = strlen (libbuf);
          buf = xrealloc (buf, libdir_maxlen + buflen + 2);
        }

      {
        /* Use the last std_dirs index for standard directories. This
           was it will always be greater than the VPATH index.  */
        unsigned int vpath_index = ~((unsigned int)0) - std_dirs;

        for (dp = dirs; *dp != 0; ++dp)
      {
            sprintf (buf, "%s/%s", *dp, libbuf);
            mtime = name_mtime (buf);
            if (mtime != NONEXISTENT_MTIME)
          {
                if (file == 0 || vpath_index < best_vpath)
                  {
                    file = strcache_add (buf);
                    best_vpath = vpath_index;

                    if (mtime_ptr != 0)
                      *mtime_ptr = mtime;
                  }
              }

            vpath_index++;
          }
      }

    }

  free (libpatterns);
  return file;
}

https://git.savannah.gnu.org/cgit/make.git/tree/rule.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/rule.c used in: Step 531
/* Pattern and suffix rule internals for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"

#include <assert.h>

#include "dep.h"
#include "filedef.h"
#include "job.h"
#include "commands.h"
#include "variable.h"
#include "rule.h"

static void freerule (struct rule *rule, struct rule *lastrule);


/* Chain of all pattern rules.  */

struct rule *pattern_rules;

/* Pointer to last rule in the chain, so we can add onto the end.  */

struct rule *last_pattern_rule;

/* Number of rules in the chain.  */

unsigned int num_pattern_rules;

/* Maximum number of target patterns of any pattern rule.  */

unsigned int max_pattern_targets;

/* Maximum number of dependencies of any pattern rule.  */

unsigned int max_pattern_deps;

/* Maximum length of the name of a dependencies of any pattern rule.  */

unsigned int max_pattern_dep_length;

/* Pointer to structure for the file .SUFFIXES
   whose dependencies are the suffixes to be searched.  */

struct file *suffix_file;

/* Maximum length of a suffix.  */

unsigned int maxsuffix;


/* Compute the maximum dependency length and maximum number of
   dependencies of all implicit rules.  Also sets the subdir
   flag for a rule when appropriate, possibly removing the rule
   completely when appropriate.  */

void
count_implicit_rule_limits (void)
{
  char *name;
  int namelen;
  struct rule *rule, *lastrule;

  num_pattern_rules = max_pattern_targets = max_pattern_deps = 0;
  max_pattern_dep_length = 0;

  name = 0;
  namelen = 0;
  rule = pattern_rules;
  lastrule = 0;
  while (rule != 0)
    {
      unsigned int ndeps = 0;
      struct dep *dep;
      struct rule *next = rule->next;

      ++num_pattern_rules;

      if (rule->num > max_pattern_targets)
    max_pattern_targets = rule->num;

      for (dep = rule->deps; dep != 0; dep = dep->next)
    {
          const char *dname = dep_name (dep);
          unsigned int len = strlen (dname);

#ifdef VMS
          const char *p = strrchr (dname, ']');
          const char *p2;
          if (p == 0)
            p = strrchr (dname, ':');
          p2 = p != 0 ? strchr (dname, '%') : 0;
#else
          const char *p = strrchr (dname, '/');
          const char *p2 = p != 0 ? strchr (dname, '%') : 0;
#endif
          ndeps++;

      if (len > max_pattern_dep_length)
        max_pattern_dep_length = len;

      if (p != 0 && p2 > p)
        {
          /* There is a slash before the % in the dep name.
         Extract the directory name.  */
          if (p == dname)
        ++p;
          if (p - dname > namelen)
        {
          namelen = p - dname;
          name = xrealloc (name, namelen + 1);
        }
          memcpy (name, dname, p - dname);
          name[p - dname] = '\0';

          /* In the deps of an implicit rule the `changed' flag
         actually indicates that the dependency is in a
         nonexistent subdirectory.  */

          dep->changed = !dir_file_exists_p (name, "");
        }
      else
        /* This dependency does not reside in a subdirectory.  */
        dep->changed = 0;
    }

      if (ndeps > max_pattern_deps)
    max_pattern_deps = ndeps;

      lastrule = rule;
      rule = next;
    }

  if (name != 0)
    free (name);
}


/* Create a pattern rule from a suffix rule.
   TARGET is the target suffix; SOURCE is the source suffix.
   CMDS are the commands.
   If TARGET is nil, it means the target pattern should be `(%.o)'.
   If SOURCE is nil, it means there should be no deps.  */

static void
convert_suffix_rule (const char *target, const char *source,
                     struct commands *cmds)
{
  const char **names, **percents;
  struct dep *deps;

  names = xmalloc (sizeof (const char *));
  percents = xmalloc (sizeof (const char *));

  if (target == 0)
    {
      /* Special case: TARGET being nil means we are defining a `.X.a' suffix
         rule; the target pattern is always `(%.o)'.  */
#ifdef VMS
      *names = strcache_add_len ("(%.obj)", 7);
#else
      *names = strcache_add_len ("(%.o)", 5);
#endif
      *percents = *names + 1;
    }
  else
    {
      /* Construct the target name.  */
      unsigned int len = strlen (target);
      char *p = alloca (1 + len + 1);
      p[0] = '%';
      memcpy (p + 1, target, len + 1);
      *names = strcache_add_len (p, len + 1);
      *percents = *names;
    }

  if (source == 0)
    deps = 0;
  else
    {
      /* Construct the dependency name.  */
      unsigned int len = strlen (source);
      char *p = alloca (1 + len + 1);
      p[0] = '%';
      memcpy (p + 1, source, len + 1);
      deps = alloc_dep ();
      deps->name = strcache_add_len (p, len + 1);
    }

  create_pattern_rule (names, percents, 1, 0, deps, cmds, 0);
}

/* Convert old-style suffix rules to pattern rules.
   All rules for the suffixes on the .SUFFIXES list are converted and added to
   the chain of pattern rules.  */

void
convert_to_pattern (void)
{
  struct dep *d, *d2;
  char *rulename;

  /* We will compute every potential suffix rule (.x.y) from the list of
     suffixes in the .SUFFIXES target's dependencies and see if it exists.
     First find the longest of the suffixes.  */

  maxsuffix = 0;
  for (d = suffix_file->deps; d != 0; d = d->next)
    {
      unsigned int l = strlen (dep_name (d));
      if (l > maxsuffix)
    maxsuffix = l;
    }

  /* Space to construct the suffix rule target name.  */
  rulename = alloca ((maxsuffix * 2) + 1);

  for (d = suffix_file->deps; d != 0; d = d->next)
    {
      unsigned int slen;

      /* Make a rule that is just the suffix, with no deps or commands.
     This rule exists solely to disqualify match-anything rules.  */
      convert_suffix_rule (dep_name (d), 0, 0);

      if (d->file->cmds != 0)
    /* Record a pattern for this suffix's null-suffix rule.  */
    convert_suffix_rule ("", dep_name (d), d->file->cmds);

      /* Add every other suffix to this one and see if it exists as a
         two-suffix rule.  */
      slen = strlen (dep_name (d));
      memcpy (rulename, dep_name (d), slen);

      for (d2 = suffix_file->deps; d2 != 0; d2 = d2->next)
    {
          struct file *f;
          unsigned int s2len;

      s2len = strlen (dep_name (d2));

          /* Can't build something from itself.  */
      if (slen == s2len && streq (dep_name (d), dep_name (d2)))
        continue;

      memcpy (rulename + slen, dep_name (d2), s2len + 1);
      f = lookup_file (rulename);
      if (f == 0 || f->cmds == 0)
        continue;

      if (s2len == 2 && rulename[slen] == '.' && rulename[slen + 1] == 'a')
        /* A suffix rule `.X.a:' generates the pattern rule `(%.o): %.X'.
           It also generates a normal `%.a: %.X' rule below.  */
        convert_suffix_rule (NULL, /* Indicates `(%.o)'.  */
                 dep_name (d),
                 f->cmds);

      /* The suffix rule `.X.Y:' is converted
         to the pattern rule `%.Y: %.X'.  */
      convert_suffix_rule (dep_name (d2), dep_name (d), f->cmds);
    }
    }
}


/* Install the pattern rule RULE (whose fields have been filled in) at the end
   of the list (so that any rules previously defined will take precedence).
   If this rule duplicates a previous one (identical target and dependencies),
   the old one is replaced if OVERRIDE is nonzero, otherwise this new one is
   thrown out.  When an old rule is replaced, the new one is put at the end of
   the list.  Return nonzero if RULE is used; zero if not.  */

static int
new_pattern_rule (struct rule *rule, int override)
{
  struct rule *r, *lastrule;
  unsigned int i, j;

  rule->in_use = 0;
  rule->terminal = 0;

  rule->next = 0;

  /* Search for an identical rule.  */
  lastrule = 0;
  for (r = pattern_rules; r != 0; lastrule = r, r = r->next)
    for (i = 0; i < rule->num; ++i)
      {
    for (j = 0; j < r->num; ++j)
      if (!streq (rule->targets[i], r->targets[j]))
        break;
        /* If all the targets matched...  */
    if (j == r->num)
      {
        struct dep *d, *d2;
        for (d = rule->deps, d2 = r->deps;
         d != 0 && d2 != 0; d = d->next, d2 = d2->next)
          if (!streq (dep_name (d), dep_name (d2)))
        break;
        if (d == 0 && d2 == 0)
          {
        /* All the dependencies matched.  */
        if (override)
          {
            /* Remove the old rule.  */
            freerule (r, lastrule);
            /* Install the new one.  */
            if (pattern_rules == 0)
              pattern_rules = rule;
            else
              last_pattern_rule->next = rule;
            last_pattern_rule = rule;

            /* We got one.  Stop looking.  */
            goto matched;
          }
        else
          {
            /* The old rule stays intact.  Destroy the new one.  */
            freerule (rule, (struct rule *) 0);
            return 0;
          }
          }
      }
      }

 matched:;

  if (r == 0)
    {
      /* There was no rule to replace.  */
      if (pattern_rules == 0)
    pattern_rules = rule;
      else
    last_pattern_rule->next = rule;
      last_pattern_rule = rule;
    }

  return 1;
}


/* Install an implicit pattern rule based on the three text strings
   in the structure P points to.  These strings come from one of
   the arrays of default implicit pattern rules.
   TERMINAL specifies what the `terminal' field of the rule should be.  */

void
install_pattern_rule (struct pspec *p, int terminal)
{
  struct rule *r;
  char *ptr;

  r = xmalloc (sizeof (struct rule));

  r->num = 1;
  r->targets = xmalloc (sizeof (const char *));
  r->suffixes = xmalloc (sizeof (const char *));
  r->lens = xmalloc (sizeof (unsigned int));

  r->lens[0] = strlen (p->target);
  r->targets[0] = p->target;
  r->suffixes[0] = find_percent_cached (&r->targets[0]);
  assert (r->suffixes[0] != NULL);
  ++r->suffixes[0];

  ptr = p->dep;
  r->deps = PARSE_FILE_SEQ (&ptr, struct dep, '\0', NULL, 0);

  if (new_pattern_rule (r, 0))
    {
      r->terminal = terminal;
      r->cmds = xmalloc (sizeof (struct commands));
      r->cmds->fileinfo.filenm = 0;
      r->cmds->fileinfo.lineno = 0;
      /* These will all be string literals, but we malloc space for them
     anyway because somebody might want to free them later.  */
      r->cmds->commands = xstrdup (p->commands);
      r->cmds->command_lines = 0;
    }
}


/* Free all the storage used in RULE and take it out of the
   pattern_rules chain.  LASTRULE is the rule whose next pointer
   points to RULE.  */

static void
freerule (struct rule *rule, struct rule *lastrule)
{
  struct rule *next = rule->next;

  free_dep_chain (rule->deps);

  /* MSVC erroneously warns without a cast here.  */
  free ((void *)rule->targets);
  free ((void *)rule->suffixes);
  free (rule->lens);

  /* We can't free the storage for the commands because there
     are ways that they could be in more than one place:
       * If the commands came from a suffix rule, they could also be in
       the `struct file's for other suffix rules or plain targets given
       on the same makefile line.
       * If two suffixes that together make a two-suffix rule were each
       given twice in the .SUFFIXES list, and in the proper order, two
       identical pattern rules would be created and the second one would
       be discarded here, but both would contain the same `struct commands'
       pointer from the `struct file' for the suffix rule.  */

  free (rule);

  if (pattern_rules == rule)
    if (lastrule != 0)
      abort ();
    else
      pattern_rules = next;
  else if (lastrule != 0)
    lastrule->next = next;
  if (last_pattern_rule == rule)
    last_pattern_rule = lastrule;
}


/* Create a new pattern rule with the targets in the nil-terminated array
   TARGETS.  TARGET_PERCENTS is an array of pointers to the % in each element
   of TARGETS.  N is the number of items in the array (not counting the nil
   element).  The new rule has dependencies DEPS and commands from COMMANDS.
   It is a terminal rule if TERMINAL is nonzero.  This rule overrides
   identical rules with different commands if OVERRIDE is nonzero.

   The storage for TARGETS and its elements and TARGET_PERCENTS is used and
   must not be freed until the rule is destroyed.  */

void
create_pattern_rule (const char **targets, const char **target_percents,
                     unsigned int n, int terminal, struct dep *deps,
                     struct commands *commands, int override)
{
  unsigned int i;
  struct rule *r = xmalloc (sizeof (struct rule));

  r->num = n;
  r->cmds = commands;
  r->deps = deps;
  r->targets = targets;
  r->suffixes = target_percents;
  r->lens = xmalloc (n * sizeof (unsigned int));

  for (i = 0; i < n; ++i)
    {
      r->lens[i] = strlen (targets[i]);
      assert (r->suffixes[i] != NULL);
      ++r->suffixes[i];
    }

  if (new_pattern_rule (r, override))
    r->terminal = terminal;
}


/* Print the data base of rules.  */

static void         /* Useful to call from gdb.  */
print_rule (struct rule *r)
{
  unsigned int i;

  for (i = 0; i < r->num; ++i)
    {
      fputs (r->targets[i], stdout);
      putchar ((i + 1 == r->num) ? ':' : ' ');
    }
  if (r->terminal)
    putchar (':');

  print_prereqs (r->deps);

  if (r->cmds != 0)
    print_commands (r->cmds);
}

void
print_rule_data_base (void)
{
  unsigned int rules, terminal;
  struct rule *r;

  puts (_("\n# Implicit Rules"));

  rules = terminal = 0;
  for (r = pattern_rules; r != 0; r = r->next)
    {
      ++rules;

      putchar ('\n');
      print_rule (r);

      if (r->terminal)
    ++terminal;
    }

  if (rules == 0)
    puts (_("\n# No implicit rules."));
  else
    {
      printf (_("\n# %u implicit rules, %u"), rules, terminal);
#ifndef NO_FLOAT
      printf (" (%.1f%%)", (double) terminal / (double) rules * 100.0);
#else
      {
    int f = (terminal * 1000 + 5) / rules;
    printf (" (%d.%d%%)", f/10, f%10);
      }
#endif
      puts (_(" terminal."));
    }

  if (num_pattern_rules != rules)
    {
      /* This can happen if a fatal error was detected while reading the
         makefiles and thus count_implicit_rule_limits wasn't called yet.  */
      if (num_pattern_rules != 0)
        fatal (NILF, _("BUG: num_pattern_rules is wrong!  %u != %u"),
               num_pattern_rules, rules);
    }
}

https://git.savannah.gnu.org/cgit/make.git/tree/signame.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/signame.c used in: Step 532
/* Convert between signal names and numbers.
Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software
Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"

/* If the system provides strsignal, we don't need it. */

#if !HAVE_STRSIGNAL

/* If the system provides sys_siglist, we'll use that.
   Otherwise create our own.
 */

#if !HAVE_DECL_SYS_SIGLIST

/* Some systems do not define NSIG in <signal.h>.  */
#ifndef NSIG
#ifdef  _NSIG
#define NSIG    _NSIG
#else
#define NSIG    32
#endif
#endif

/* There is too much variation in Sys V signal numbers and names, so
   we must initialize them at runtime.  */

static const char *undoc;

static const char *sys_siglist[NSIG];

/* Table of abbreviations for signals.  Note:  A given number can
   appear more than once with different abbreviations.  */
#define SIG_TABLE_SIZE  (NSIG*2)

typedef struct
  {
    int number;
    const char *abbrev;
  } num_abbrev;

static num_abbrev sig_table[SIG_TABLE_SIZE];

/* Number of elements of sig_table used.  */
static int sig_table_nelts = 0;

/* Enter signal number NUMBER into the tables with ABBREV and NAME.  */

static void
init_sig (int number, const char *abbrev, const char *name)
{
  /* If this value is ever greater than NSIG it seems like it'd be a bug in
     the system headers, but... better safe than sorry.  We know, for
     example, that this isn't always true on VMS.  */

  if (number >= 0 && number < NSIG)
    sys_siglist[number] = name;

  if (sig_table_nelts < SIG_TABLE_SIZE)
    {
      sig_table[sig_table_nelts].number = number;
      sig_table[sig_table_nelts++].abbrev = abbrev;
    }
}

static int
signame_init (void)
{
  int i;

  undoc = xstrdup (_("unknown signal"));

  /* Initialize signal names.  */
  for (i = 0; i < NSIG; i++)
    sys_siglist[i] = undoc;

  /* Initialize signal names.  */
#if defined (SIGHUP)
  init_sig (SIGHUP, "HUP", _("Hangup"));
#endif
#if defined (SIGINT)
  init_sig (SIGINT, "INT", _("Interrupt"));
#endif
#if defined (SIGQUIT)
  init_sig (SIGQUIT, "QUIT", _("Quit"));
#endif
#if defined (SIGILL)
  init_sig (SIGILL, "ILL", _("Illegal Instruction"));
#endif
#if defined (SIGTRAP)
  init_sig (SIGTRAP, "TRAP", _("Trace/breakpoint trap"));
#endif
  /* If SIGIOT == SIGABRT, we want to print it as SIGABRT because
     SIGABRT is in ANSI and POSIX.1 and SIGIOT isn't.  */
#if defined (SIGABRT)
  init_sig (SIGABRT, "ABRT", _("Aborted"));
#endif
#if defined (SIGIOT)
  init_sig (SIGIOT, "IOT", _("IOT trap"));
#endif
#if defined (SIGEMT)
  init_sig (SIGEMT, "EMT", _("EMT trap"));
#endif
#if defined (SIGFPE)
  init_sig (SIGFPE, "FPE", _("Floating point exception"));
#endif
#if defined (SIGKILL)
  init_sig (SIGKILL, "KILL", _("Killed"));
#endif
#if defined (SIGBUS)
  init_sig (SIGBUS, "BUS", _("Bus error"));
#endif
#if defined (SIGSEGV)
  init_sig (SIGSEGV, "SEGV", _("Segmentation fault"));
#endif
#if defined (SIGSYS)
  init_sig (SIGSYS, "SYS", _("Bad system call"));
#endif
#if defined (SIGPIPE)
  init_sig (SIGPIPE, "PIPE", _("Broken pipe"));
#endif
#if defined (SIGALRM)
  init_sig (SIGALRM, "ALRM", _("Alarm clock"));
#endif
#if defined (SIGTERM)
  init_sig (SIGTERM, "TERM", _("Terminated"));
#endif
#if defined (SIGUSR1)
  init_sig (SIGUSR1, "USR1", _("User defined signal 1"));
#endif
#if defined (SIGUSR2)
  init_sig (SIGUSR2, "USR2", _("User defined signal 2"));
#endif
  /* If SIGCLD == SIGCHLD, we want to print it as SIGCHLD because that
     is what is in POSIX.1.  */
#if defined (SIGCHLD)
  init_sig (SIGCHLD, "CHLD", _("Child exited"));
#endif
#if defined (SIGCLD)
  init_sig (SIGCLD, "CLD", _("Child exited"));
#endif
#if defined (SIGPWR)
  init_sig (SIGPWR, "PWR", _("Power failure"));
#endif
#if defined (SIGTSTP)
  init_sig (SIGTSTP, "TSTP", _("Stopped"));
#endif
#if defined (SIGTTIN)
  init_sig (SIGTTIN, "TTIN", _("Stopped (tty input)"));
#endif
#if defined (SIGTTOU)
  init_sig (SIGTTOU, "TTOU", _("Stopped (tty output)"));
#endif
#if defined (SIGSTOP)
  init_sig (SIGSTOP, "STOP", _("Stopped (signal)"));
#endif
#if defined (SIGXCPU)
  init_sig (SIGXCPU, "XCPU", _("CPU time limit exceeded"));
#endif
#if defined (SIGXFSZ)
  init_sig (SIGXFSZ, "XFSZ", _("File size limit exceeded"));
#endif
#if defined (SIGVTALRM)
  init_sig (SIGVTALRM, "VTALRM", _("Virtual timer expired"));
#endif
#if defined (SIGPROF)
  init_sig (SIGPROF, "PROF", _("Profiling timer expired"));
#endif
#if defined (SIGWINCH)
  /* "Window size changed" might be more accurate, but even if that
     is all that it means now, perhaps in the future it will be
     extended to cover other kinds of window changes.  */
  init_sig (SIGWINCH, "WINCH", _("Window changed"));
#endif
#if defined (SIGCONT)
  init_sig (SIGCONT, "CONT", _("Continued"));
#endif
#if defined (SIGURG)
  init_sig (SIGURG, "URG", _("Urgent I/O condition"));
#endif
#if defined (SIGIO)
  /* "I/O pending" has also been suggested.  A disadvantage is
     that signal only happens when the process has
     asked for it, not everytime I/O is pending.  Another disadvantage
     is the confusion from giving it a different name than under Unix.  */
  init_sig (SIGIO, "IO", _("I/O possible"));
#endif
#if defined (SIGWIND)
  init_sig (SIGWIND, "WIND", _("SIGWIND"));
#endif
#if defined (SIGPHONE)
  init_sig (SIGPHONE, "PHONE", _("SIGPHONE"));
#endif
#if defined (SIGPOLL)
  init_sig (SIGPOLL, "POLL", _("I/O possible"));
#endif
#if defined (SIGLOST)
  init_sig (SIGLOST, "LOST", _("Resource lost"));
#endif
#if defined (SIGDANGER)
  init_sig (SIGDANGER, "DANGER", _("Danger signal"));
#endif
#if defined (SIGINFO)
  init_sig (SIGINFO, "INFO", _("Information request"));
#endif
#if defined (SIGNOFP)
  init_sig (SIGNOFP, "NOFP", _("Floating point co-processor not available"));
#endif

  return 1;
}

#endif  /* HAVE_DECL_SYS_SIGLIST */


char *
strsignal (int sig)
{
  static char buf[] = "Signal 12345678901234567890";

#if ! HAVE_DECL_SYS_SIGLIST
# if HAVE_DECL__SYS_SIGLIST
#  define sys_siglist _sys_siglist
# elif HAVE_DECL___SYS_SIGLIST
#  define sys_siglist __sys_siglist
# else
  static char sig_initted = 0;

  if (!sig_initted)
    sig_initted = signame_init ();
# endif
#endif

  if (sig > 0 || sig < NSIG)
    return (char *) sys_siglist[sig];

  sprintf (buf, "Signal %d", sig);
  return buf;
}

#endif  /* HAVE_STRSIGNAL */

https://git.savannah.gnu.org/cgit/make.git/tree/strcache.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/strcache.c used in: Step 533
/* Constant string caching for GNU Make.
Copyright (C) 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"

#include <assert.h>

#include "hash.h"

/* The size (in bytes) of each cache buffer.
   Try to pick something that will map well into the heap.  */
#define CACHE_BUFFER_SIZE   (8192 - 16)


/* A string cached here will never be freed, so we don't need to worry about
   reference counting.  We just store the string, and then remember it in a
   hash so it can be looked up again. */

struct strcache {
  struct strcache *next;    /* The next block of strings.  */
  char *end;                /* Pointer to the beginning of the free space.  */
  int count;                /* # of strings in this buffer (for stats).  */
  int bytesfree;            /* The amount of the buffer that is free.  */
  char buffer[1];           /* The buffer comes after this.  */
};

static int bufsize = CACHE_BUFFER_SIZE;
static struct strcache *strcache = NULL;

/* Add a new buffer to the cache.  Add it at the front to reduce search time.
   This can also increase the overhead, since it's less likely that older
   buffers will be filled in.  However, GNU make has so many smaller strings
   that this doesn't seem to be much of an issue in practice.
 */
static struct strcache *
new_cache()
{
  struct strcache *new;
  new = xmalloc (sizeof (*new) + bufsize);
  new->end = new->buffer;
  new->count = 0;
  new->bytesfree = bufsize;

  new->next = strcache;
  strcache = new;

  return new;
}

static const char *
add_string(const char *str, int len)
{
  struct strcache *best = NULL;
  struct strcache *sp;
  const char *res;

  /* If the string we want is too large to fit into a single buffer, then
     we're screwed; nothing will ever fit!  Change the maximum size of the
     cache to be big enough.  */
  if (len > bufsize)
    bufsize = len * 2;

  /* First, find a cache with enough free space.  We always look through all
     the blocks and choose the one with the best fit (the one that leaves the
     least amount of space free).  */
  for (sp = strcache; sp != NULL; sp = sp->next)
    if (sp->bytesfree > len && (!best || best->bytesfree > sp->bytesfree))
      best = sp;

  /* If nothing is big enough, make a new cache.  */
  if (!best)
    best = new_cache();

  assert (best->bytesfree > len);

  /* Add the string to the best cache.  */
  res = best->end;
  memcpy (best->end, str, len);
  best->end += len;
  *(best->end++) = '\0';
  best->bytesfree -= len + 1;
  ++best->count;

  return res;
}


/* Hash table of strings in the cache.  */

static unsigned long
str_hash_1 (const void *key)
{
  return_ISTRING_HASH_1 ((const char *) key);
}

static unsigned long
str_hash_2 (const void *key)
{
  return_ISTRING_HASH_2 ((const char *) key);
}

static int
str_hash_cmp (const void *x, const void *y)
{
  return_ISTRING_COMPARE ((const char *) x, (const char *) y);
}

static struct hash_table strings;
static unsigned long total_adds = 0;

static const char *
add_hash (const char *str, int len)
{
  /* Look up the string in the hash.  If it's there, return it.  */
  char *const *slot = (char *const *) hash_find_slot (&strings, str);
  const char *key = *slot;

  /* Count the total number of adds we performed.  */
  ++total_adds;

  if (!HASH_VACANT (key))
    return key;

  /* Not there yet so add it to a buffer, then into the hash table.  */
  key = add_string (str, len);
  hash_insert_at (&strings, key, slot);
  return key;
}

/* Returns true if the string is in the cache; false if not.  */
int
strcache_iscached (const char *str)
{
  struct strcache *sp;

  for (sp = strcache; sp != 0; sp = sp->next)
    if (str >= sp->buffer && str < sp->end)
      return 1;

  return 0;
}

/* If the string is already in the cache, return a pointer to the cached
   version.  If not, add it then return a pointer to the cached version.
   Note we do NOT take control of the string passed in.  */
const char *
strcache_add (const char *str)
{
  return add_hash (str, strlen (str));
}

const char *
strcache_add_len (const char *str, int len)
{
  /* If we're not given a nul-terminated string we have to create one, because
     the hashing functions expect it.  */
  if (str[len] != '\0')
    {
      char *key = alloca (len + 1);
      memcpy (key, str, len);
      key[len] = '\0';
      str = key;
    }

  return add_hash (str, len);
}

int
strcache_setbufsize(int size)
{
  if (size > bufsize)
    bufsize = size;
  return bufsize;
}

void
strcache_init (void)
{
  hash_init (&strings, 8000, str_hash_1, str_hash_2, str_hash_cmp);
}


/* Generate some stats output.  */

void
strcache_print_stats (const char *prefix)
{
  int numbuffs = 0, numstrs = 0;
  int totsize = 0, avgsize, maxsize = 0, minsize = bufsize;
  int totfree = 0, avgfree, maxfree = 0, minfree = bufsize;
  int lastused = 0, lastfree = 0;

  if (strcache)
    {
      const struct strcache *sp;

      /* Count the first buffer separately since it's not full.  */
      lastused = strcache->end - strcache->buffer;
      lastfree = strcache->bytesfree;

      for (sp = strcache->next; sp != NULL; sp = sp->next)
        {
          int bf = sp->bytesfree;
          int sz = sp->end - sp->buffer;

          ++numbuffs;
          numstrs += sp->count;

          totsize += sz;
          maxsize = (sz > maxsize ? sz : maxsize);
          minsize = (sz < minsize ? sz : minsize);

          totfree += bf;
          maxfree = (bf > maxfree ? bf : maxfree);
          minfree = (bf < minfree ? bf : minfree);
        }
    }

  avgsize = numbuffs ? (int)(totsize / numbuffs) : 0;
  avgfree = numbuffs ? (int)(totfree / numbuffs) : 0;

  printf (_("\n%s # of strings in strcache: %d / lookups = %lu / hits = %lu\n"),
          prefix, numstrs, total_adds, (total_adds - numstrs));
  printf (_("%s # of strcache buffers: %d (* %d B/buffer = %d B)\n"),
          prefix, (numbuffs + 1), bufsize, ((numbuffs + 1) * bufsize));
  printf (_("%s strcache used: total = %d (%d) / max = %d / min = %d / avg = %d\n"),
          prefix, totsize, lastused, maxsize, minsize, avgsize);
  printf (_("%s strcache free: total = %d (%d) / max = %d / min = %d / avg = %d\n"),
          prefix, totfree, lastfree, maxfree, minfree, avgfree);

  fputs (_("\n# strcache hash-table stats:\n# "), stdout);
  hash_print_stats (&strings, stdout);
}

https://git.savannah.gnu.org/cgit/make.git/tree/variable.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/variable.c used in: Step 534
/* Internals of variables for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"

#include <assert.h>

#include "dep.h"
#include "filedef.h"
#include "job.h"
#include "commands.h"
#include "variable.h"
#include "rule.h"
#ifdef WINDOWS32
#include "pathstuff.h"
#endif
#include "hash.h"

/* Chain of all pattern-specific variables.  */

static struct pattern_var *pattern_vars;

/* Pointer to the last struct in the pack of a specific size, from 1 to 255.*/

static struct pattern_var *last_pattern_vars[256];

/* Create a new pattern-specific variable struct. The new variable is
   inserted into the PATTERN_VARS list in the shortest patterns first
   order to support the shortest stem matching (the variables are
   matched in the reverse order so the ones with the longest pattern
   will be considered first). Variables with the same pattern length
   are inserted in the definition order. */

struct pattern_var *
create_pattern_var (const char *target, const char *suffix)
{
  register unsigned int len = strlen (target);
  register struct pattern_var *p = xmalloc (sizeof (struct pattern_var));

  if (pattern_vars != 0)
    {
      if (len < 256 && last_pattern_vars[len] != 0)
        {
          p->next = last_pattern_vars[len]->next;
          last_pattern_vars[len]->next = p;
        }
      else
        {
          /* Find the position where we can insert this variable. */
          register struct pattern_var **v;

          for (v = &pattern_vars; ; v = &(*v)->next)
            {
              /* Insert at the end of the pack so that patterns with the
                 same length appear in the order they were defined .*/

              if (*v == 0 || (*v)->len > len)
                {
                  p->next = *v;
                  *v = p;
                  break;
                }
            }
        }
    }
  else
    {
      pattern_vars = p;
      p->next = 0;
    }

  p->target = target;
  p->len = len;
  p->suffix = suffix + 1;

  if (len < 256)
    last_pattern_vars[len] = p;

  return p;
}

/* Look up a target in the pattern-specific variable list.  */

static struct pattern_var *
lookup_pattern_var (struct pattern_var *start, const char *target)
{
  struct pattern_var *p;
  unsigned int targlen = strlen(target);

  for (p = start ? start->next : pattern_vars; p != 0; p = p->next)
    {
      const char *stem;
      unsigned int stemlen;

      if (p->len > targlen)
        /* It can't possibly match.  */
        continue;

      /* From the lengths of the filename and the pattern parts,
         find the stem: the part of the filename that matches the %.  */
      stem = target + (p->suffix - p->target - 1);
      stemlen = targlen - p->len + 1;

      /* Compare the text in the pattern before the stem, if any.  */
      if (stem > target && !strneq (p->target, target, stem - target))
        continue;

      /* Compare the text in the pattern after the stem, if any.
         We could test simply using streq, but this way we compare the
         first two characters immediately.  This saves time in the very
         common case where the first character matches because it is a
         period.  */
      if (*p->suffix == stem[stemlen]
          && (*p->suffix == '\0' || streq (&p->suffix[1], &stem[stemlen+1])))
        break;
    }

  return p;
}


/* Hash table of all global variable definitions.  */

static unsigned long
variable_hash_1 (const void *keyv)
{
  struct variable const *key = (struct variable const *) keyv;
  return_STRING_N_HASH_1 (key->name, key->length);
}

static unsigned long
variable_hash_2 (const void *keyv)
{
  struct variable const *key = (struct variable const *) keyv;
  return_STRING_N_HASH_2 (key->name, key->length);
}

static int
variable_hash_cmp (const void *xv, const void *yv)
{
  struct variable const *x = (struct variable const *) xv;
  struct variable const *y = (struct variable const *) yv;
  int result = x->length - y->length;
  if (result)
    return result;
  return_STRING_N_COMPARE (x->name, y->name, x->length);
}

#ifndef VARIABLE_BUCKETS
#define VARIABLE_BUCKETS        523
#endif
#ifndef PERFILE_VARIABLE_BUCKETS
#define PERFILE_VARIABLE_BUCKETS    23
#endif
#ifndef SMALL_SCOPE_VARIABLE_BUCKETS
#define SMALL_SCOPE_VARIABLE_BUCKETS    13
#endif

static struct variable_set global_variable_set;
static struct variable_set_list global_setlist
  = { 0, &global_variable_set, 0 };
struct variable_set_list *current_variable_set_list = &global_setlist;


/* Implement variables.  */

void
init_hash_global_variable_set (void)
{
  hash_init (&global_variable_set.table, VARIABLE_BUCKETS,
         variable_hash_1, variable_hash_2, variable_hash_cmp);
}

/* Define variable named NAME with value VALUE in SET.  VALUE is copied.
   LENGTH is the length of NAME, which does not need to be null-terminated.
   ORIGIN specifies the origin of the variable (makefile, command line
   or environment).
   If RECURSIVE is nonzero a flag is set in the variable saying
   that it should be recursively re-expanded.  */

struct variable *
define_variable_in_set (const char *name, unsigned int length,
                        const char *value, enum variable_origin origin,
                        int recursive, struct variable_set *set,
                        const struct floc *flocp)
{
  struct variable *v;
  struct variable **var_slot;
  struct variable var_key;

  if (set == NULL)
    set = &global_variable_set;

  var_key.name = (char *) name;
  var_key.length = length;
  var_slot = (struct variable **) hash_find_slot (&set->table, &var_key);

  if (env_overrides && origin == o_env)
    origin = o_env_override;

  v = *var_slot;
  if (! HASH_VACANT (v))
    {
      if (env_overrides && v->origin == o_env)
    /* V came from in the environment.  Since it was defined
       before the switches were parsed, it wasn't affected by -e.  */
    v->origin = o_env_override;

      /* A variable of this name is already defined.
     If the old definition is from a stronger source
     than this one, don't redefine it.  */
      if ((int) origin >= (int) v->origin)
    {
      if (v->value != 0)
        free (v->value);
      v->value = xstrdup (value);
          if (flocp != 0)
            v->fileinfo = *flocp;
          else
            v->fileinfo.filenm = 0;
      v->origin = origin;
      v->recursive = recursive;
    }
      return v;
    }

  /* Create a new variable definition and add it to the hash table.  */

  v = xmalloc (sizeof (struct variable));
  v->name = xstrndup (name, length);
  v->length = length;
  hash_insert_at (&set->table, v, var_slot);
  v->value = xstrdup (value);
  if (flocp != 0)
    v->fileinfo = *flocp;
  else
    v->fileinfo.filenm = 0;
  v->origin = origin;
  v->recursive = recursive;
  v->special = 0;
  v->expanding = 0;
  v->exp_count = 0;
  v->per_target = 0;
  v->append = 0;
  v->private_var = 0;
  v->export = v_default;

  v->exportable = 1;
  if (*name != '_' && (*name < 'A' || *name > 'Z')
      && (*name < 'a' || *name > 'z'))
    v->exportable = 0;
  else
    {
      for (++name; *name != '\0'; ++name)
        if (*name != '_' && (*name < 'a' || *name > 'z')
            && (*name < 'A' || *name > 'Z') && !ISDIGIT(*name))
          break;

      if (*name != '\0')
        v->exportable = 0;
    }

  return v;
}



/* Undefine variable named NAME in SET. LENGTH is the length of NAME, which
   does not need to be null-terminated. ORIGIN specifies the origin of the
   variable (makefile, command line or environment). */

static void
free_variable_name_and_value (const void *item);

void
undefine_variable_in_set (const char *name, unsigned int length,
                          enum variable_origin origin,
                          struct variable_set *set)
{
  struct variable *v;
  struct variable **var_slot;
  struct variable var_key;

  if (set == NULL)
    set = &global_variable_set;

  var_key.name = (char *) name;
  var_key.length = length;
  var_slot = (struct variable **) hash_find_slot (&set->table, &var_key);

  if (env_overrides && origin == o_env)
    origin = o_env_override;

  v = *var_slot;
  if (! HASH_VACANT (v))
    {
      if (env_overrides && v->origin == o_env)
    /* V came from in the environment.  Since it was defined
       before the switches were parsed, it wasn't affected by -e.  */
    v->origin = o_env_override;

      /* If the definition is from a stronger source than this one, don't
         undefine it.  */
      if ((int) origin >= (int) v->origin)
    {
          hash_delete_at (&set->table, var_slot);
          free_variable_name_and_value (v);
    }
    }
}

/* If the variable passed in is "special", handle its special nature.
   Currently there are two such variables, both used for introspection:
   .VARIABLES expands to a list of all the variables defined in this instance
   of make.
   .TARGETS expands to a list of all the targets defined in this
   instance of make.
   Returns the variable reference passed in.  */

#define EXPANSION_INCREMENT(_l)  ((((_l) / 500) + 1) * 500)

static struct variable *
lookup_special_var (struct variable *var)
{
  static unsigned long last_var_count = 0;


  /* This one actually turns out to be very hard, due to the way the parser
     records targets.  The way it works is that target information is collected
     internally until make knows the target is completely specified.  It unitl
     it sees that some new construct (a new target or variable) is defined that
     it knows the previous one is done.  In short, this means that if you do
     this:

       all:

       TARGS := $(.TARGETS)

     then $(TARGS) won't contain "all", because it's not until after the
     variable is created that the previous target is completed.

     Changing this would be a major pain.  I think a less complex way to do it
     would be to pre-define the target files as soon as the first line is
     parsed, then come back and do the rest of the definition as now.  That
     would allow $(.TARGETS) to be correct without a major change to the way
     the parser works.

  if (streq (var->name, ".TARGETS"))
    var->value = build_target_list (var->value);
  else
  */

  if (streq (var->name, ".VARIABLES")
      && global_variable_set.table.ht_fill != last_var_count)
    {
      unsigned long max = EXPANSION_INCREMENT (strlen (var->value));
      unsigned long len;
      char *p;
      struct variable **vp = (struct variable **) global_variable_set.table.ht_vec;
      struct variable **end = &vp[global_variable_set.table.ht_size];

      /* Make sure we have at least MAX bytes in the allocated buffer.  */
      var->value = xrealloc (var->value, max);

      /* Walk through the hash of variables, constructing a list of names.  */
      p = var->value;
      len = 0;
      for (; vp < end; ++vp)
        if (!HASH_VACANT (*vp))
          {
            struct variable *v = *vp;
            int l = v->length;

            len += l + 1;
            if (len > max)
              {
                unsigned long off = p - var->value;

                max += EXPANSION_INCREMENT (l + 1);
                var->value = xrealloc (var->value, max);
                p = &var->value[off];
              }

            memcpy (p, v->name, l);
            p += l;
            *(p++) = ' ';
          }
      *(p-1) = '\0';

      /* Remember how many variables are in our current count.  Since we never
         remove variables from the list, this is a reliable way to know whether
         the list is up to date or needs to be recomputed.  */

      last_var_count = global_variable_set.table.ht_fill;
    }

  return var;
}



/* Lookup a variable whose name is a string starting at NAME
   and with LENGTH chars.  NAME need not be null-terminated.
   Returns address of the `struct variable' containing all info
   on the variable, or nil if no such variable is defined.  */

struct variable *
lookup_variable (const char *name, unsigned int length)
{
  const struct variable_set_list *setlist;
  struct variable var_key;
  int is_parent = 0;

  var_key.name = (char *) name;
  var_key.length = length;

  for (setlist = current_variable_set_list;
       setlist != 0; setlist = setlist->next)
    {
      const struct variable_set *set = setlist->set;
      struct variable *v;

      v = (struct variable *) hash_find_item ((struct hash_table *) &set->table, &var_key);
      if (v && (!is_parent || !v->private_var))
    return v->special ? lookup_special_var (v) : v;

      is_parent |= setlist->next_is_parent;
    }

#ifdef VMS
  /* since we don't read envp[] on startup, try to get the
     variable via getenv() here.  */
  {
    char *vname = alloca (length + 1);
    char *value;
    strncpy (vname, name, length);
    vname[length] = 0;
    value = getenv (vname);
    if (value != 0)
      {
        char *sptr;
        int scnt;

        sptr = value;
        scnt = 0;

        while ((sptr = strchr (sptr, '$')))
          {
            scnt++;
            sptr++;
          }

        if (scnt > 0)
          {
            char *nvalue;
            char *nptr;

            nvalue = alloca (strlen (value) + scnt + 1);
            sptr = value;
            nptr = nvalue;

            while (*sptr)
              {
                if (*sptr == '$')
                  {
                    *nptr++ = '$';
                    *nptr++ = '$';
                  }
                else
                  {
                    *nptr++ = *sptr;
                  }
                sptr++;
              }

            *nptr = '\0';
            return define_variable (vname, length, nvalue, o_env, 1);

          }

        return define_variable (vname, length, value, o_env, 1);
      }
  }
#endif /* VMS */

  return 0;
}


/* Lookup a variable whose name is a string starting at NAME
   and with LENGTH chars in set SET.  NAME need not be null-terminated.
   Returns address of the `struct variable' containing all info
   on the variable, or nil if no such variable is defined.  */

struct variable *
lookup_variable_in_set (const char *name, unsigned int length,
                        const struct variable_set *set)
{
  struct variable var_key;

  var_key.name = (char *) name;
  var_key.length = length;

  return (struct variable *) hash_find_item ((struct hash_table *) &set->table, &var_key);
}


/* Initialize FILE's variable set list.  If FILE already has a variable set
   list, the topmost variable set is left intact, but the the rest of the
   chain is replaced with FILE->parent's setlist.  If FILE is a double-colon
   rule, then we will use the "root" double-colon target's variable set as the
   parent of FILE's variable set.

   If we're READING a makefile, don't do the pattern variable search now,
   since the pattern variable might not have been defined yet.  */

void
initialize_file_variables (struct file *file, int reading)
{
  struct variable_set_list *l = file->variables;

  if (l == 0)
    {
      l = (struct variable_set_list *)
    xmalloc (sizeof (struct variable_set_list));
      l->set = xmalloc (sizeof (struct variable_set));
      hash_init (&l->set->table, PERFILE_VARIABLE_BUCKETS,
                 variable_hash_1, variable_hash_2, variable_hash_cmp);
      file->variables = l;
    }

  /* If this is a double-colon, then our "parent" is the "root" target for
     this double-colon rule.  Since that rule has the same name, parent,
     etc. we can just use its variables as the "next" for ours.  */

  if (file->double_colon && file->double_colon != file)
    {
      initialize_file_variables (file->double_colon, reading);
      l->next = file->double_colon->variables;
      l->next_is_parent = 0;
      return;
    }

  if (file->parent == 0)
    l->next = &global_setlist;
  else
    {
      initialize_file_variables (file->parent, reading);
      l->next = file->parent->variables;
    }
  l->next_is_parent = 1;

  /* If we're not reading makefiles and we haven't looked yet, see if
     we can find pattern variables for this target.  */

  if (!reading && !file->pat_searched)
    {
      struct pattern_var *p;

      p = lookup_pattern_var (0, file->name);
      if (p != 0)
        {
          struct variable_set_list *global = current_variable_set_list;

          /* We found at least one.  Set up a new variable set to accumulate
             all the pattern variables that match this target.  */

          file->pat_variables = create_new_variable_set ();
          current_variable_set_list = file->pat_variables;

          do
            {
              /* We found one, so insert it into the set.  */

              struct variable *v;

              if (p->variable.flavor == f_simple)
                {
                  v = define_variable_loc (
                    p->variable.name, strlen (p->variable.name),
                    p->variable.value, p->variable.origin,
                    0, &p->variable.fileinfo);

                  v->flavor = f_simple;
                }
              else
                {
                  v = do_variable_definition (
                    &p->variable.fileinfo, p->variable.name,
                    p->variable.value, p->variable.origin,
                    p->variable.flavor, 1);
                }

              /* Also mark it as a per-target and copy export status. */
              v->per_target = p->variable.per_target;
              v->export = p->variable.export;
              v->private_var = p->variable.private_var;
            }
          while ((p = lookup_pattern_var (p, file->name)) != 0);

          current_variable_set_list = global;
        }
      file->pat_searched = 1;
    }

  /* If we have a pattern variable match, set it up.  */

  if (file->pat_variables != 0)
    {
      file->pat_variables->next = l->next;
      file->pat_variables->next_is_parent = l->next_is_parent;
      l->next = file->pat_variables;
      l->next_is_parent = 0;
    }
}


/* Pop the top set off the current variable set list,
   and free all its storage.  */

struct variable_set_list *
create_new_variable_set (void)
{
  register struct variable_set_list *setlist;
  register struct variable_set *set;

  set = xmalloc (sizeof (struct variable_set));
  hash_init (&set->table, SMALL_SCOPE_VARIABLE_BUCKETS,
         variable_hash_1, variable_hash_2, variable_hash_cmp);

  setlist = (struct variable_set_list *)
    xmalloc (sizeof (struct variable_set_list));
  setlist->set = set;
  setlist->next = current_variable_set_list;
  setlist->next_is_parent = 0;

  return setlist;
}

static void
free_variable_name_and_value (const void *item)
{
  struct variable *v = (struct variable *) item;
  free (v->name);
  free (v->value);
}

void
free_variable_set (struct variable_set_list *list)
{
  hash_map (&list->set->table, free_variable_name_and_value);
  hash_free (&list->set->table, 1);
  free (list->set);
  free (list);
}

/* Create a new variable set and push it on the current setlist.
   If we're pushing a global scope (that is, the current scope is the global
   scope) then we need to "push" it the other way: file variable sets point
   directly to the global_setlist so we need to replace that with the new one.
 */

struct variable_set_list *
push_new_variable_scope (void)
{
  current_variable_set_list = create_new_variable_set();
  if (current_variable_set_list->next == &global_setlist)
    {
      /* It was the global, so instead of new -> &global we want to replace
         &global with the new one and have &global -> new, with current still
         pointing to &global  */
      struct variable_set *set = current_variable_set_list->set;
      current_variable_set_list->set = global_setlist.set;
      global_setlist.set = set;
      current_variable_set_list->next = global_setlist.next;
      global_setlist.next = current_variable_set_list;
      current_variable_set_list = &global_setlist;
    }
  return (current_variable_set_list);
}

void
pop_variable_scope (void)
{
  struct variable_set_list *setlist;
  struct variable_set *set;

  /* Can't call this if there's no scope to pop!  */
  assert(current_variable_set_list->next != NULL);

  if (current_variable_set_list != &global_setlist)
    {
      /* We're not pointing to the global setlist, so pop this one.  */
      setlist = current_variable_set_list;
      set = setlist->set;
      current_variable_set_list = setlist->next;
    }
  else
    {
      /* This set is the one in the global_setlist, but there is another global
         set beyond that.  We want to copy that set to global_setlist, then
         delete what used to be in global_setlist.  */
      setlist = global_setlist.next;
      set = global_setlist.set;
      global_setlist.set = setlist->set;
      global_setlist.next = setlist->next;
      global_setlist.next_is_parent = setlist->next_is_parent;
    }

  /* Free the one we no longer need.  */
  free (setlist);
  hash_map (&set->table, free_variable_name_and_value);
  hash_free (&set->table, 1);
  free (set);
}


/* Merge FROM_SET into TO_SET, freeing unused storage in FROM_SET.  */

static void
merge_variable_sets (struct variable_set *to_set,
                     struct variable_set *from_set)
{
  struct variable **from_var_slot = (struct variable **) from_set->table.ht_vec;
  struct variable **from_var_end = from_var_slot + from_set->table.ht_size;

  for ( ; from_var_slot < from_var_end; from_var_slot++)
    if (! HASH_VACANT (*from_var_slot))
      {
    struct variable *from_var = *from_var_slot;
    struct variable **to_var_slot
      = (struct variable **) hash_find_slot (&to_set->table, *from_var_slot);
    if (HASH_VACANT (*to_var_slot))
      hash_insert_at (&to_set->table, from_var, to_var_slot);
    else
      {
        /* GKM FIXME: delete in from_set->table */
        free (from_var->value);
        free (from_var);
      }
      }
}

/* Merge SETLIST1 into SETLIST0, freeing unused storage in SETLIST1.  */

void
merge_variable_set_lists (struct variable_set_list **setlist0,
                          struct variable_set_list *setlist1)
{
  struct variable_set_list *to = *setlist0;
  struct variable_set_list *last0 = 0;

  /* If there's nothing to merge, stop now.  */
  if (!setlist1)
    return;

  /* This loop relies on the fact that all setlists terminate with the global
     setlist (before NULL).  If that's not true, arguably we SHOULD die.  */
  if (to)
    while (setlist1 != &global_setlist && to != &global_setlist)
      {
        struct variable_set_list *from = setlist1;
        setlist1 = setlist1->next;

        merge_variable_sets (to->set, from->set);

        last0 = to;
        to = to->next;
      }

  if (setlist1 != &global_setlist)
    {
      if (last0 == 0)
    *setlist0 = setlist1;
      else
    last0->next = setlist1;
    }
}


/* Define the automatic variables, and record the addresses
   of their structures so we can change their values quickly.  */

void
define_automatic_variables (void)
{
#if defined(WINDOWS32) || defined(__EMX__)
  extern char* default_shell;
#else
  extern char default_shell[];
#endif
  register struct variable *v;
  char buf[200];

  sprintf (buf, "%u", makelevel);
  define_variable_cname (MAKELEVEL_NAME, buf, o_env, 0);

  sprintf (buf, "%s%s%s",
       version_string,
       (remote_description == 0 || remote_description[0] == '\0')
       ? "" : "-",
       (remote_description == 0 || remote_description[0] == '\0')
       ? "" : remote_description);
  define_variable_cname ("MAKE_VERSION", buf, o_default, 0);

#ifdef  __MSDOS__
  /* Allow to specify a special shell just for Make,
     and use $COMSPEC as the default $SHELL when appropriate.  */
  {
    static char shell_str[] = "SHELL";
    const int shlen = sizeof (shell_str) - 1;
    struct variable *mshp = lookup_variable ("MAKESHELL", 9);
    struct variable *comp = lookup_variable ("COMSPEC", 7);

    /* $(MAKESHELL) overrides $(SHELL) even if -e is in effect.  */
    if (mshp)
      (void) define_variable (shell_str, shlen,
                  mshp->value, o_env_override, 0);
    else if (comp)
      {
    /* $(COMSPEC) shouldn't override $(SHELL).  */
    struct variable *shp = lookup_variable (shell_str, shlen);

    if (!shp)
      (void) define_variable (shell_str, shlen, comp->value, o_env, 0);
      }
  }
#elif defined(__EMX__)
  {
    static char shell_str[] = "SHELL";
    const int shlen = sizeof (shell_str) - 1;
    struct variable *shell = lookup_variable (shell_str, shlen);
    struct variable *replace = lookup_variable ("MAKESHELL", 9);

    /* if $MAKESHELL is defined in the environment assume o_env_override */
    if (replace && *replace->value && replace->origin == o_env)
      replace->origin = o_env_override;

    /* if $MAKESHELL is not defined use $SHELL but only if the variable
       did not come from the environment */
    if (!replace || !*replace->value)
      if (shell && *shell->value && (shell->origin == o_env
      || shell->origin == o_env_override))
    {
      /* overwrite whatever we got from the environment */
      free(shell->value);
      shell->value = xstrdup (default_shell);
      shell->origin = o_default;
    }

    /* Some people do not like cmd to be used as the default
       if $SHELL is not defined in the Makefile.
       With -DNO_CMD_DEFAULT you can turn off this behaviour */
# ifndef NO_CMD_DEFAULT
    /* otherwise use $COMSPEC */
    if (!replace || !*replace->value)
      replace = lookup_variable ("COMSPEC", 7);

    /* otherwise use $OS2_SHELL */
    if (!replace || !*replace->value)
      replace = lookup_variable ("OS2_SHELL", 9);
# else
#   warning NO_CMD_DEFAULT: GNU make will not use CMD.EXE as default shell
# endif

    if (replace && *replace->value)
      /* overwrite $SHELL */
      (void) define_variable (shell_str, shlen, replace->value,
                  replace->origin, 0);
    else
      /* provide a definition if there is none */
      (void) define_variable (shell_str, shlen, default_shell,
                  o_default, 0);
  }

#endif

  /* This won't override any definition, but it will provide one if there
     isn't one there.  */
  v = define_variable_cname ("SHELL", default_shell, o_default, 0);
#ifdef __MSDOS__
  v->export = v_export;  /*  Export always SHELL.  */
#endif

  /* On MSDOS we do use SHELL from environment, since it isn't a standard
     environment variable on MSDOS, so whoever sets it, does that on purpose.
     On OS/2 we do not use SHELL from environment but we have already handled
     that problem above. */
#if !defined(__MSDOS__) && !defined(__EMX__)
  /* Don't let SHELL come from the environment.  */
  if (*v->value == '\0' || v->origin == o_env || v->origin == o_env_override)
    {
      free (v->value);
      v->origin = o_file;
      v->value = xstrdup (default_shell);
    }
#endif

  /* Make sure MAKEFILES gets exported if it is set.  */
  v = define_variable_cname ("MAKEFILES", "", o_default, 0);
  v->export = v_ifset;

  /* Define the magic D and F variables in terms of
     the automatic variables they are variations of.  */

#ifdef VMS
  define_variable_cname ("@D", "$(dir $@)", o_automatic, 1);
  define_variable_cname ("%D", "$(dir $%)", o_automatic, 1);
  define_variable_cname ("*D", "$(dir $*)", o_automatic, 1);
  define_variable_cname ("<D", "$(dir $<)", o_automatic, 1);
  define_variable_cname ("?D", "$(dir $?)", o_automatic, 1);
  define_variable_cname ("^D", "$(dir $^)", o_automatic, 1);
  define_variable_cname ("+D", "$(dir $+)", o_automatic, 1);
#else
  define_variable_cname ("@D", "$(patsubst %/,%,$(dir $@))", o_automatic, 1);
  define_variable_cname ("%D", "$(patsubst %/,%,$(dir $%))", o_automatic, 1);
  define_variable_cname ("*D", "$(patsubst %/,%,$(dir $*))", o_automatic, 1);
  define_variable_cname ("<D", "$(patsubst %/,%,$(dir $<))", o_automatic, 1);
  define_variable_cname ("?D", "$(patsubst %/,%,$(dir $?))", o_automatic, 1);
  define_variable_cname ("^D", "$(patsubst %/,%,$(dir $^))", o_automatic, 1);
  define_variable_cname ("+D", "$(patsubst %/,%,$(dir $+))", o_automatic, 1);
#endif
  define_variable_cname ("@F", "$(notdir $@)", o_automatic, 1);
  define_variable_cname ("%F", "$(notdir $%)", o_automatic, 1);
  define_variable_cname ("*F", "$(notdir $*)", o_automatic, 1);
  define_variable_cname ("<F", "$(notdir $<)", o_automatic, 1);
  define_variable_cname ("?F", "$(notdir $?)", o_automatic, 1);
  define_variable_cname ("^F", "$(notdir $^)", o_automatic, 1);
  define_variable_cname ("+F", "$(notdir $+)", o_automatic, 1);
}


int export_all_variables;

/* Create a new environment for FILE's commands.
   If FILE is nil, this is for the `shell' function.
   The child's MAKELEVEL variable is incremented.  */

char **
target_environment (struct file *file)
{
  struct variable_set_list *set_list;
  register struct variable_set_list *s;
  struct hash_table table;
  struct variable **v_slot;
  struct variable **v_end;
  struct variable makelevel_key;
  char **result_0;
  char **result;

  if (file == 0)
    set_list = current_variable_set_list;
  else
    set_list = file->variables;

  hash_init (&table, VARIABLE_BUCKETS,
         variable_hash_1, variable_hash_2, variable_hash_cmp);

  /* Run through all the variable sets in the list,
     accumulating variables in TABLE.  */
  for (s = set_list; s != 0; s = s->next)
    {
      struct variable_set *set = s->set;
      v_slot = (struct variable **) set->table.ht_vec;
      v_end = v_slot + set->table.ht_size;
      for ( ; v_slot < v_end; v_slot++)
    if (! HASH_VACANT (*v_slot))
      {
        struct variable **new_slot;
        struct variable *v = *v_slot;

        /* If this is a per-target variable and it hasn't been touched
           already then look up the global version and take its export
           value.  */
        if (v->per_target && v->export == v_default)
          {
        struct variable *gv;

        gv = lookup_variable_in_set (v->name, strlen(v->name),
                                             &global_variable_set);
        if (gv)
          v->export = gv->export;
          }

        switch (v->export)
          {
          case v_default:
        if (v->origin == o_default || v->origin == o_automatic)
          /* Only export default variables by explicit request.  */
          continue;

                /* The variable doesn't have a name that can be exported.  */
                if (! v->exportable)
                  continue;

        if (! export_all_variables
            && v->origin != o_command
            && v->origin != o_env && v->origin != o_env_override)
          continue;
        break;

          case v_export:
        break;

          case v_noexport:
        {
          /* If this is the SHELL variable and it's not exported,
             then add the value from our original environment, if
             the original environment defined a value for SHELL.  */
          extern struct variable shell_var;
          if (streq (v->name, "SHELL") && shell_var.value)
            {
              v = &shell_var;
              break;
            }
          continue;
        }

          case v_ifset:
        if (v->origin == o_default)
          continue;
        break;
          }

        new_slot = (struct variable **) hash_find_slot (&table, v);
        if (HASH_VACANT (*new_slot))
          hash_insert_at (&table, v, new_slot);
      }
    }

  makelevel_key.name = MAKELEVEL_NAME;
  makelevel_key.length = MAKELEVEL_LENGTH;
  hash_delete (&table, &makelevel_key);

  result = result_0 = xmalloc ((table.ht_fill + 2) * sizeof (char *));

  v_slot = (struct variable **) table.ht_vec;
  v_end = v_slot + table.ht_size;
  for ( ; v_slot < v_end; v_slot++)
    if (! HASH_VACANT (*v_slot))
      {
    struct variable *v = *v_slot;

    /* If V is recursively expanded and didn't come from the environment,
       expand its value.  If it came from the environment, it should
       go back into the environment unchanged.  */
    if (v->recursive
        && v->origin != o_env && v->origin != o_env_override)
      {
        char *value = recursively_expand_for_file (v, file);
#ifdef WINDOWS32
        if (strcmp(v->name, "Path") == 0 ||
        strcmp(v->name, "PATH") == 0)
          convert_Path_to_windows32(value, ';');
#endif
        *result++ = xstrdup (concat (3, v->name, "=", value));
        free (value);
      }
    else
      {
#ifdef WINDOWS32
            if (strcmp(v->name, "Path") == 0 ||
                strcmp(v->name, "PATH") == 0)
              convert_Path_to_windows32(v->value, ';');
#endif
        *result++ = xstrdup (concat (3, v->name, "=", v->value));
      }
      }

  *result = xmalloc (100);
  sprintf (*result, "%s=%u", MAKELEVEL_NAME, makelevel + 1);
  *++result = 0;

  hash_free (&table, 0);

  return result_0;
}


static struct variable *
set_special_var (struct variable *var)
{
  if (streq (var->name, RECIPEPREFIX_NAME))
    {
      /* The user is resetting the command introduction prefix.  This has to
         happen immediately, so that subsequent rules are interpreted
         properly.  */
      cmd_prefix = var->value[0]=='\0' ? RECIPEPREFIX_DEFAULT : var->value[0];
    }

  return var;
}


/* Given a variable, a value, and a flavor, define the variable.
   See the try_variable_definition() function for details on the parameters. */

struct variable *
do_variable_definition (const struct floc *flocp, const char *varname,
                        const char *value, enum variable_origin origin,
                        enum variable_flavor flavor, int target_var)
{
  const char *p;
  char *alloc_value = NULL;
  struct variable *v;
  int append = 0;
  int conditional = 0;

  /* Calculate the variable's new value in VALUE.  */

  switch (flavor)
    {
    default:
    case f_bogus:
      /* Should not be possible.  */
      abort ();
    case f_simple:
      /* A simple variable definition "var := value".  Expand the value.
         We have to allocate memory since otherwise it'll clobber the
     variable buffer, and we may still need that if we're looking at a
         target-specific variable.  */
      p = alloc_value = allocated_variable_expand (value);
      break;
    case f_conditional:
      /* A conditional variable definition "var ?= value".
         The value is set IFF the variable is not defined yet. */
      v = lookup_variable (varname, strlen (varname));
      if (v)
        return v->special ? set_special_var (v) : v;

      conditional = 1;
      flavor = f_recursive;
      /* FALLTHROUGH */
    case f_recursive:
      /* A recursive variable definition "var = value".
     The value is used verbatim.  */
      p = value;
      break;
    case f_append:
      {
        /* If we have += but we're in a target variable context, we want to
           append only with other variables in the context of this target.  */
        if (target_var)
          {
            append = 1;
            v = lookup_variable_in_set (varname, strlen (varname),
                                        current_variable_set_list->set);

            /* Don't append from the global set if a previous non-appending
               target-specific variable definition exists. */
            if (v && !v->append)
              append = 0;
          }
        else
          v = lookup_variable (varname, strlen (varname));

        if (v == 0)
          {
            /* There was no old value.
               This becomes a normal recursive definition.  */
            p = value;
            flavor = f_recursive;
          }
        else
          {
            /* Paste the old and new values together in VALUE.  */

            unsigned int oldlen, vallen;
            const char *val;
            char *tp = NULL;

            val = value;
            if (v->recursive)
              /* The previous definition of the variable was recursive.
                 The new value is the unexpanded old and new values. */
              flavor = f_recursive;
            else
              /* The previous definition of the variable was simple.
                 The new value comes from the old value, which was expanded
                 when it was set; and from the expanded new value.  Allocate
                 memory for the expansion as we may still need the rest of the
                 buffer if we're looking at a target-specific variable.  */
              val = tp = allocated_variable_expand (val);

            oldlen = strlen (v->value);
            vallen = strlen (val);
            p = alloc_value = xmalloc (oldlen + 1 + vallen + 1);
            memcpy (alloc_value, v->value, oldlen);
            alloc_value[oldlen] = ' ';
            memcpy (&alloc_value[oldlen + 1], val, vallen + 1);

            if (tp)
              free (tp);
          }
      }
    }

#ifdef __MSDOS__
  /* Many Unix Makefiles include a line saying "SHELL=/bin/sh", but
     non-Unix systems don't conform to this default configuration (in
     fact, most of them don't even have `/bin').  On the other hand,
     $SHELL in the environment, if set, points to the real pathname of
     the shell.
     Therefore, we generally won't let lines like "SHELL=/bin/sh" from
     the Makefile override $SHELL from the environment.  But first, we
     look for the basename of the shell in the directory where SHELL=
     points, and along the $PATH; if it is found in any of these places,
     we define $SHELL to be the actual pathname of the shell.  Thus, if
     you have bash.exe installed as d:/unix/bash.exe, and d:/unix is on
     your $PATH, then SHELL=/usr/local/bin/bash will have the effect of
     defining SHELL to be "d:/unix/bash.exe".  */
  if ((origin == o_file || origin == o_override)
      && strcmp (varname, "SHELL") == 0)
    {
      PATH_VAR (shellpath);
      extern char * __dosexec_find_on_path (const char *, char *[], char *);

      /* See if we can find "/bin/sh.exe", "/bin/sh.com", etc.  */
      if (__dosexec_find_on_path (p, NULL, shellpath))
    {
      char *tp;

      for (tp = shellpath; *tp; tp++)
            if (*tp == '\\')
              *tp = '/';

      v = define_variable_loc (varname, strlen (varname),
                                   shellpath, origin, flavor == f_recursive,
                                   flocp);
    }
      else
    {
      const char *shellbase, *bslash;
      struct variable *pathv = lookup_variable ("PATH", 4);
      char *path_string;
      char *fake_env[2];
      size_t pathlen = 0;

      shellbase = strrchr (p, '/');
      bslash = strrchr (p, '\\');
      if (!shellbase || bslash > shellbase)
        shellbase = bslash;
      if (!shellbase && p[1] == ':')
        shellbase = p + 1;
      if (shellbase)
        shellbase++;
      else
        shellbase = p;

      /* Search for the basename of the shell (with standard
         executable extensions) along the $PATH.  */
      if (pathv)
        pathlen = strlen (pathv->value);
      path_string = xmalloc (5 + pathlen + 2 + 1);
      /* On MSDOS, current directory is considered as part of $PATH.  */
      sprintf (path_string, "PATH=.;%s", pathv ? pathv->value : "");
      fake_env[0] = path_string;
      fake_env[1] = 0;
      if (__dosexec_find_on_path (shellbase, fake_env, shellpath))
        {
          char *tp;

          for (tp = shellpath; *tp; tp++)
                if (*tp == '\\')
                  *tp = '/';

          v = define_variable_loc (varname, strlen (varname),
                                       shellpath, origin,
                                       flavor == f_recursive, flocp);
        }
      else
        v = lookup_variable (varname, strlen (varname));

      free (path_string);
    }
    }
  else
#endif /* __MSDOS__ */
#ifdef WINDOWS32
  if ((origin == o_file || origin == o_override || origin == o_command)
      && streq (varname, "SHELL"))
    {
      extern char *default_shell;

      /* Call shell locator function. If it returns TRUE, then
     set no_default_sh_exe to indicate sh was found and
         set new value for SHELL variable.  */

      if (find_and_set_default_shell (p))
        {
          v = define_variable_in_set (varname, strlen (varname), default_shell,
                                      origin, flavor == f_recursive,
                                      (target_var
                                       ? current_variable_set_list->set
                                       : NULL),
                                      flocp);
          no_default_sh_exe = 0;
        }
      else
        {
          char *tp = alloc_value;

          alloc_value = allocated_variable_expand (p);

          if (find_and_set_default_shell (alloc_value))
            {
              v = define_variable_in_set (varname, strlen (varname), p,
                                          origin, flavor == f_recursive,
                                          (target_var
                                           ? current_variable_set_list->set
                                           : NULL),
                                          flocp);
              no_default_sh_exe = 0;
            }
          else
            v = lookup_variable (varname, strlen (varname));

          if (tp)
            free (tp);
        }
    }
  else
#endif

  /* If we are defining variables inside an $(eval ...), we might have a
     different variable context pushed, not the global context (maybe we're
     inside a $(call ...) or something.  Since this function is only ever
     invoked in places where we want to define globally visible variables,
     make sure we define this variable in the global set.  */

  v = define_variable_in_set (varname, strlen (varname), p,
                              origin, flavor == f_recursive,
                              (target_var
                               ? current_variable_set_list->set : NULL),
                              flocp);
  v->append = append;
  v->conditional = conditional;

  if (alloc_value)
    free (alloc_value);

  return v->special ? set_special_var (v) : v;
}


/* Parse P (a null-terminated string) as a variable definition.

   If it is not a variable definition, return NULL.

   If it is a variable definition, return a pointer to the char after the
   assignment token and set *FLAVOR to the type of variable assignment.  */

char *
parse_variable_definition (const char *p, enum variable_flavor *flavor)
{
  int wspace = 0;

  p = next_token (p);

  while (1)
    {
      int c = *p++;

      /* If we find a comment or EOS, it's not a variable definition.  */
      if (c == '\0' || c == '#')
    return NULL;

      if (c == '$')
    {
      /* This begins a variable expansion reference.  Make sure we don't
         treat chars inside the reference as assignment tokens.  */
      char closeparen;
      int count;
      c = *p++;
      if (c == '(')
        closeparen = ')';
      else if (c == '{')
        closeparen = '}';
      else
            /* '$$' or '$X'.  Either way, nothing special to do here.  */
        continue;

      /* P now points past the opening paren or brace.
         Count parens or braces until it is matched.  */
      count = 0;
      for (; *p != '\0'; ++p)
        {
          if (*p == c)
        ++count;
          else if (*p == closeparen && --count < 0)
        {
          ++p;
          break;
        }
        }
          continue;
    }

      /* If we find whitespace skip it, and remember we found it.  */
      if (isblank ((unsigned char)c))
        {
          wspace = 1;
          p = next_token (p);
          c = *p;
          if (c == '\0')
            return NULL;
          ++p;
        }


      if (c == '=')
    {
      *flavor = f_recursive;
      return (char *)p;
    }

      /* Match assignment variants (:=, +=, ?=)  */
      if (*p == '=')
        {
          switch (c)
            {
              case ':':
                *flavor = f_simple;
                break;
              case '+':
                *flavor = f_append;
                break;
              case '?':
                *flavor = f_conditional;
                break;
              default:
                /* If we skipped whitespace, non-assignments means no var.  */
                if (wspace)
                  return NULL;

                /* Might be assignment, or might be $= or #=.  Check.  */
                continue;
            }
          return (char *)++p;
        }
      else if (c == ':')
        /* A colon other than := is a rule line, not a variable defn.  */
        return NULL;

      /* If we skipped whitespace, non-assignments means no var.  */
      if (wspace)
        return NULL;
    }

  return (char *)p;
}


/* Try to interpret LINE (a null-terminated string) as a variable definition.

   If LINE was recognized as a variable definition, a pointer to its `struct
   variable' is returned.  If LINE is not a variable definition, NULL is
   returned.  */

struct variable *
assign_variable_definition (struct variable *v, char *line)
{
  char *beg;
  char *end;
  enum variable_flavor flavor;
  char *name;

  beg = next_token (line);
  line = parse_variable_definition (beg, &flavor);
  if (!line)
    return NULL;

  end = line - (flavor == f_recursive ? 1 : 2);
  while (end > beg && isblank ((unsigned char)end[-1]))
    --end;
  line = next_token (line);
  v->value = line;
  v->flavor = flavor;

  /* Expand the name, so "$(foo)bar = baz" works.  */
  name = alloca (end - beg + 1);
  memcpy (name, beg, end - beg);
  name[end - beg] = '\0';
  v->name = allocated_variable_expand (name);

  if (v->name[0] == '\0')
    fatal (&v->fileinfo, _("empty variable name"));

  return v;
}


/* Try to interpret LINE (a null-terminated string) as a variable definition.

   ORIGIN may be o_file, o_override, o_env, o_env_override,
   or o_command specifying that the variable definition comes
   from a makefile, an override directive, the environment with
   or without the -e switch, or the command line.

   See the comments for assign_variable_definition().

   If LINE was recognized as a variable definition, a pointer to its `struct
   variable' is returned.  If LINE is not a variable definition, NULL is
   returned.  */

struct variable *
try_variable_definition (const struct floc *flocp, char *line,
                         enum variable_origin origin, int target_var)
{
  struct variable v;
  struct variable *vp;

  if (flocp != 0)
    v.fileinfo = *flocp;
  else
    v.fileinfo.filenm = 0;

  if (!assign_variable_definition (&v, line))
    return 0;

  vp = do_variable_definition (flocp, v.name, v.value,
                               origin, v.flavor, target_var);

  free (v.name);

  return vp;
}


/* Print information for variable V, prefixing it with PREFIX.  */

static void
print_variable (const void *item, void *arg)
{
  const struct variable *v = item;
  const char *prefix = arg;
  const char *origin;

  switch (v->origin)
    {
    case o_default:
      origin = _("default");
      break;
    case o_env:
      origin = _("environment");
      break;
    case o_file:
      origin = _("makefile");
      break;
    case o_env_override:
      origin = _("environment under -e");
      break;
    case o_command:
      origin = _("command line");
      break;
    case o_override:
      origin = _("`override' directive");
      break;
    case o_automatic:
      origin = _("automatic");
      break;
    case o_invalid:
    default:
      abort ();
    }
  fputs ("# ", stdout);
  fputs (origin, stdout);
  if (v->private_var)
    fputs (" private", stdout);
  if (v->fileinfo.filenm)
    printf (_(" (from `%s', line %lu)"),
            v->fileinfo.filenm, v->fileinfo.lineno);
  putchar ('\n');
  fputs (prefix, stdout);

  /* Is this a `define'?  */
  if (v->recursive && strchr (v->value, '\n') != 0)
    printf ("define %s\n%s\nendef\n", v->name, v->value);
  else
    {
      char *p;

      printf ("%s %s= ", v->name, v->recursive ? v->append ? "+" : "" : ":");

      /* Check if the value is just whitespace.  */
      p = next_token (v->value);
      if (p != v->value && *p == '\0')
    /* All whitespace.  */
    printf ("$(subst ,,%s)", v->value);
      else if (v->recursive)
    fputs (v->value, stdout);
      else
    /* Double up dollar signs.  */
    for (p = v->value; *p != '\0'; ++p)
      {
        if (*p == '$')
          putchar ('$');
        putchar (*p);
      }
      putchar ('\n');
    }
}


/* Print all the variables in SET.  PREFIX is printed before
   the actual variable definitions (everything else is comments).  */

void
print_variable_set (struct variable_set *set, char *prefix)
{
  hash_map_arg (&set->table, print_variable, prefix);

  fputs (_("# variable set hash-table stats:\n"), stdout);
  fputs ("# ", stdout);
  hash_print_stats (&set->table, stdout);
  putc ('\n', stdout);
}

/* Print the data base of variables.  */

void
print_variable_data_base (void)
{
  puts (_("\n# Variables\n"));

  print_variable_set (&global_variable_set, "");

  puts (_("\n# Pattern-specific Variable Values"));

  {
    struct pattern_var *p;
    int rules = 0;

    for (p = pattern_vars; p != 0; p = p->next)
      {
        ++rules;
        printf ("\n%s :\n", p->target);
        print_variable (&p->variable, "# ");
      }

    if (rules == 0)
      puts (_("\n# No pattern-specific variable values."));
    else
      printf (_("\n# %u pattern-specific variable values"), rules);
  }
}


/* Print all the local variables of FILE.  */

void
print_file_variables (const struct file *file)
{
  if (file->variables != 0)
    print_variable_set (file->variables->set, "# ");
}

#ifdef WINDOWS32
void
sync_Path_environment (void)
{
  char *path = allocated_variable_expand ("$(PATH)");
  static char *environ_path = NULL;

  if (!path)
    return;

  /*
   * If done this before, don't leak memory unnecessarily.
   * Free the previous entry before allocating new one.
   */
  if (environ_path)
    free (environ_path);

  /*
   * Create something WINDOWS32 world can grok
   */
  convert_Path_to_windows32 (path, ';');
  environ_path = xstrdup (concat (3, "PATH", "=", path));
  putenv (environ_path);
  free (path);
}
#endif

https://git.savannah.gnu.org/cgit/make.git/tree/version.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/version.c used in: Step 535
/* Record version and build host architecture for GNU make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

/* We use <config.h> instead of "config.h" so that a compilation
   using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
   (which it would do because make.h was found in $srcdir).  */
#include <config.h>

#ifndef MAKE_HOST
# define MAKE_HOST "unknown"
#endif

char *version_string = VERSION;
char *make_host = MAKE_HOST;


/*
  Local variables:
  version-control: never
  End:
 */

https://git.savannah.gnu.org/cgit/make.git/tree/vpath.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/vpath.c used in: Step 536
/* Implementation of pattern-matching file search paths for GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"
#include "filedef.h"
#include "variable.h"
#ifdef WINDOWS32
#include "pathstuff.h"
#endif


/* Structure used to represent a selective VPATH searchpath.  */

struct vpath
  {
    struct vpath *next; /* Pointer to next struct in the linked list.  */
    const char *pattern;/* The pattern to match.  */
    const char *percent;/* Pointer into `pattern' where the `%' is.  */
    unsigned int patlen;/* Length of the pattern.  */
    const char **searchpath; /* Null-terminated list of directories.  */
    unsigned int maxlen;/* Maximum length of any entry in the list.  */
  };

/* Linked-list of all selective VPATHs.  */

static struct vpath *vpaths;

/* Structure for the general VPATH given in the variable.  */

static struct vpath *general_vpath;

/* Structure for GPATH given in the variable.  */

static struct vpath *gpaths;



/* Reverse the chain of selective VPATH lists so they will be searched in the
   order given in the makefiles and construct the list from the VPATH
   variable.  */

void
build_vpath_lists ()
{
  register struct vpath *new = 0;
  register struct vpath *old, *nexto;
  register char *p;

  /* Reverse the chain.  */
  for (old = vpaths; old != 0; old = nexto)
    {
      nexto = old->next;
      old->next = new;
      new = old;
    }

  vpaths = new;

  /* If there is a VPATH variable with a nonnull value, construct the
     general VPATH list from it.  We use variable_expand rather than just
     calling lookup_variable so that it will be recursively expanded.  */

  {
    /* Turn off --warn-undefined-variables while we expand SHELL and IFS.  */
    int save = warn_undefined_variables_flag;
    warn_undefined_variables_flag = 0;

    p = variable_expand ("$(strip $(VPATH))");

    warn_undefined_variables_flag = save;
  }

  if (*p != '\0')
    {
      /* Save the list of vpaths.  */
      struct vpath *save_vpaths = vpaths;
      char gp[] = "%";

      /* Empty `vpaths' so the new one will have no next, and `vpaths'
     will still be nil if P contains no existing directories.  */
      vpaths = 0;

      /* Parse P.  */
      construct_vpath_list (gp, p);

      /* Store the created path as the general path,
     and restore the old list of vpaths.  */
      general_vpath = vpaths;
      vpaths = save_vpaths;
    }

  /* If there is a GPATH variable with a nonnull value, construct the
     GPATH list from it.  We use variable_expand rather than just
     calling lookup_variable so that it will be recursively expanded.  */

  {
    /* Turn off --warn-undefined-variables while we expand SHELL and IFS.  */
    int save = warn_undefined_variables_flag;
    warn_undefined_variables_flag = 0;

    p = variable_expand ("$(strip $(GPATH))");

    warn_undefined_variables_flag = save;
  }

  if (*p != '\0')
    {
      /* Save the list of vpaths.  */
      struct vpath *save_vpaths = vpaths;
      char gp[] = "%";

      /* Empty `vpaths' so the new one will have no next, and `vpaths'
     will still be nil if P contains no existing directories.  */
      vpaths = 0;

      /* Parse P.  */
      construct_vpath_list (gp, p);

      /* Store the created path as the GPATH,
     and restore the old list of vpaths.  */
      gpaths = vpaths;
      vpaths = save_vpaths;
    }
}


/* Construct the VPATH listing for the PATTERN and DIRPATH given.

   This function is called to generate selective VPATH lists and also for
   the general VPATH list (which is in fact just a selective VPATH that
   is applied to everything).  The returned pointer is either put in the
   linked list of all selective VPATH lists or in the GENERAL_VPATH
   variable.

   If DIRPATH is nil, remove all previous listings with the same
   pattern.  If PATTERN is nil, remove all VPATH listings.  Existing
   and readable directories that are not "." given in the DIRPATH
   separated by the path element separator (defined in make.h) are
   loaded into the directory hash table if they are not there already
   and put in the VPATH searchpath for the given pattern with trailing
   slashes stripped off if present (and if the directory is not the
   root, "/").  The length of the longest entry in the list is put in
   the structure as well.  The new entry will be at the head of the
   VPATHS chain.  */

void
construct_vpath_list (char *pattern, char *dirpath)
{
  unsigned int elem;
  char *p;
  const char **vpath;
  unsigned int maxvpath;
  unsigned int maxelem;
  const char *percent = NULL;

  if (pattern != 0)
    percent = find_percent (pattern);

  if (dirpath == 0)
    {
      /* Remove matching listings.  */
      struct vpath *path, *lastpath;

      lastpath = 0;
      path = vpaths;
      while (path != 0)
    {
      struct vpath *next = path->next;

      if (pattern == 0
          || (((percent == 0 && path->percent == 0)
           || (percent - pattern == path->percent - path->pattern))
          && streq (pattern, path->pattern)))
        {
          /* Remove it from the linked list.  */
          if (lastpath == 0)
        vpaths = path->next;
          else
        lastpath->next = next;

          /* Free its unused storage.  */
              /* MSVC erroneously warns without a cast here.  */
          free ((void *)path->searchpath);
          free (path);
        }
      else
        lastpath = path;

      path = next;
    }

      return;
    }

#ifdef WINDOWS32
    convert_vpath_to_windows32(dirpath, ';');
#endif

  /* Skip over any initial separators and blanks.  */
  while (*dirpath == PATH_SEPARATOR_CHAR || isblank ((unsigned char)*dirpath))
    ++dirpath;

  /* Figure out the maximum number of VPATH entries and put it in
     MAXELEM.  We start with 2, one before the first separator and one
     nil (the list terminator) and increment our estimated number for
     each separator or blank we find.  */
  maxelem = 2;
  p = dirpath;
  while (*p != '\0')
    if (*p++ == PATH_SEPARATOR_CHAR || isblank ((unsigned char)*p))
      ++maxelem;

  vpath = xmalloc (maxelem * sizeof (const char *));
  maxvpath = 0;

  elem = 0;
  p = dirpath;
  while (*p != '\0')
    {
      char *v;
      unsigned int len;

      /* Find the end of this entry.  */
      v = p;
      while (*p != '\0'
#if defined(HAVE_DOS_PATHS) && (PATH_SEPARATOR_CHAR == ':')
         /* Platforms whose PATH_SEPARATOR_CHAR is ':' and which
        also define HAVE_DOS_PATHS would like us to recognize
        colons after the drive letter in the likes of
        "D:/foo/bar:C:/xyzzy".  */
         && (*p != PATH_SEPARATOR_CHAR
         || (p == v + 1 && (p[1] == '/' || p[1] == '\\')))
#else
         && *p != PATH_SEPARATOR_CHAR
#endif
         && !isblank ((unsigned char)*p))
    ++p;

      len = p - v;
      /* Make sure there's no trailing slash,
     but still allow "/" as a directory.  */
#if defined(__MSDOS__) || defined(__EMX__) || defined(HAVE_DOS_PATHS)
      /* We need also to leave alone a trailing slash in "d:/".  */
      if (len > 3 || (len > 1 && v[1] != ':'))
#endif
      if (len > 1 && p[-1] == '/')
    --len;

      /* Put the directory on the vpath list.  */
      if (len > 1 || *v != '.')
    {
          vpath[elem++] = dir_name (strcache_add_len (v, len));
          if (len > maxvpath)
            maxvpath = len;
    }

      /* Skip over separators and blanks between entries.  */
      while (*p == PATH_SEPARATOR_CHAR || isblank ((unsigned char)*p))
    ++p;
    }

  if (elem > 0)
    {
      struct vpath *path;
      /* ELEM is now incremented one element past the last
     entry, to where the nil-pointer terminator goes.
     Usually this is maxelem - 1.  If not, shrink down.  */
      if (elem < (maxelem - 1))
    vpath = xrealloc (vpath, (elem+1) * sizeof (const char *));

      /* Put the nil-pointer terminator on the end of the VPATH list.  */
      vpath[elem] = NULL;

      /* Construct the vpath structure and put it into the linked list.  */
      path = xmalloc (sizeof (struct vpath));
      path->searchpath = vpath;
      path->maxlen = maxvpath;
      path->next = vpaths;
      vpaths = path;

      /* Set up the members.  */
      path->pattern = strcache_add (pattern);
      path->patlen = strlen (pattern);
      path->percent = percent ? path->pattern + (percent - pattern) : 0;
    }
  else
    /* There were no entries, so free whatever space we allocated.  */
    /* MSVC erroneously warns without a cast here.  */
    free ((void *)vpath);
}


/* Search the GPATH list for a pathname string that matches the one passed
   in.  If it is found, return 1.  Otherwise we return 0.  */

int
gpath_search (const char *file, unsigned int len)
{
  const char **gp;

  if (gpaths && (len <= gpaths->maxlen))
    for (gp = gpaths->searchpath; *gp != NULL; ++gp)
      if (strneq (*gp, file, len) && (*gp)[len] == '\0')
        return 1;

  return 0;
}



/* Search the given VPATH list for a directory where the name pointed to by
   FILE exists.  If it is found, we return a cached name of the existing file
   and set *MTIME_PTR (if MTIME_PTR is not NULL) to its modtime (or zero if no
   stat call was done). Also set the matching directory index in PATH_INDEX
   if it is not NULL. Otherwise we return NULL.  */

static const char *
selective_vpath_search (struct vpath *path, const char *file,
                        FILE_TIMESTAMP *mtime_ptr, unsigned int* path_index)
{
  int not_target;
  char *name;
  const char *n;
  const char *filename;
  const char **vpath = path->searchpath;
  unsigned int maxvpath = path->maxlen;
  unsigned int i;
  unsigned int flen, vlen, name_dplen;
  int exists = 0;

  /* Find out if *FILE is a target.
     If and only if it is NOT a target, we will accept prospective
     files that don't exist but are mentioned in a makefile.  */
  {
    struct file *f = lookup_file (file);
    not_target = f == 0 || !f->is_target;
  }

  flen = strlen (file);

  /* Split *FILE into a directory prefix and a name-within-directory.
     NAME_DPLEN gets the length of the prefix; FILENAME gets the pointer to
     the name-within-directory and FLEN is its length.  */

  n = strrchr (file, '/');
#ifdef HAVE_DOS_PATHS
  /* We need the rightmost slash or backslash.  */
  {
    const char *bslash = strrchr(file, '\\');
    if (!n || bslash > n)
      n = bslash;
  }
#endif
  name_dplen = n != 0 ? n - file : 0;
  filename = name_dplen > 0 ? n + 1 : file;
  if (name_dplen > 0)
    flen -= name_dplen + 1;

  /* Get enough space for the biggest VPATH entry, a slash, the directory
     prefix that came with FILE, another slash (although this one may not
     always be necessary), the filename, and a null terminator.  */
  name = alloca (maxvpath + 1 + name_dplen + 1 + flen + 1);

  /* Try each VPATH entry.  */
  for (i = 0; vpath[i] != 0; ++i)
    {
      int exists_in_cache = 0;
      char *p;

      p = name;

      /* Put the next VPATH entry into NAME at P and increment P past it.  */
      vlen = strlen (vpath[i]);
      memcpy (p, vpath[i], vlen);
      p += vlen;

      /* Add the directory prefix already in *FILE.  */
      if (name_dplen > 0)
    {
#ifndef VMS
      *p++ = '/';
#endif
      memcpy (p, file, name_dplen);
      p += name_dplen;
    }

#ifdef HAVE_DOS_PATHS
      /* Cause the next if to treat backslash and slash alike.  */
      if (p != name && p[-1] == '\\' )
    p[-1] = '/';
#endif
      /* Now add the name-within-directory at the end of NAME.  */
#ifndef VMS
      if (p != name && p[-1] != '/')
    {
      *p = '/';
      memcpy (p + 1, filename, flen + 1);
    }
      else
#endif
    memcpy (p, filename, flen + 1);

      /* Check if the file is mentioned in a makefile.  If *FILE is not
     a target, that is enough for us to decide this file exists.
     If *FILE is a target, then the file must be mentioned in the
     makefile also as a target to be chosen.

     The restriction that *FILE must not be a target for a
     makefile-mentioned file to be chosen was added by an
     inadequately commented change in July 1990; I am not sure off
     hand what problem it fixes.

     In December 1993 I loosened this restriction to allow a file
     to be chosen if it is mentioned as a target in a makefile.  This
     seem logical.

         Special handling for -W / -o: make sure we preserve the special
         values here.  Actually this whole thing is a little bogus: I think
         we should ditch the name/hname thing and look into the renamed
         capability that already exists for files: that is, have a new struct
         file* entry for the VPATH-found file, and set the renamed field if
         we use it.
      */
      {
    struct file *f = lookup_file (name);
    if (f != 0)
          {
            exists = not_target || f->is_target;
            if (exists && mtime_ptr
                && (f->last_mtime == OLD_MTIME || f->last_mtime == NEW_MTIME))
              {
                *mtime_ptr = f->last_mtime;
                mtime_ptr = 0;
              }
          }
      }

      if (!exists)
    {
      /* That file wasn't mentioned in the makefile.
         See if it actually exists.  */

#ifdef VMS
      exists_in_cache = exists = dir_file_exists_p (vpath[i], filename);
#else
      /* Clobber a null into the name at the last slash.
         Now NAME is the name of the directory to look in.  */
      *p = '\0';

      /* We know the directory is in the hash table now because either
         construct_vpath_list or the code just above put it there.
         Does the file we seek exist in it?  */
      exists_in_cache = exists = dir_file_exists_p (name, filename);
#endif
    }

      if (exists)
    {
      /* The file is in the directory cache.
         Now check that it actually exists in the filesystem.
         The cache may be out of date.  When vpath thinks a file
         exists, but stat fails for it, confusion results in the
         higher levels.  */

      struct stat st;

#ifndef VMS
      /* Put the slash back in NAME.  */
      *p = '/';
#endif

      if (exists_in_cache)  /* Makefile-mentioned file need not exist.  */
        {
              int e;

              EINTRLOOP (e, stat (name, &st)); /* Does it really exist?  */
              if (e != 0)
                {
                  exists = 0;
                  continue;
                }

              /* Store the modtime into *MTIME_PTR for the caller.  */
              if (mtime_ptr != 0)
                {
                  *mtime_ptr = FILE_TIMESTAMP_STAT_MODTIME (name, st);
                  mtime_ptr = 0;
                }
            }

          /* We have found a file.
             If we get here and mtime_ptr hasn't been set, record
             UNKNOWN_MTIME to indicate this.  */
          if (mtime_ptr != 0)
            *mtime_ptr = UNKNOWN_MTIME;

          /* Store the name we found and return it.  */

          if (path_index)
            *path_index = i;

          return strcache_add_len (name, (p + 1 - name) + flen);
    }
    }

  return 0;
}


/* Search the VPATH list whose pattern matches FILE for a directory where FILE
   exists.  If it is found, return the cached name of an existing file, and
   set *MTIME_PTR (if MTIME_PTR is not NULL) to its modtime (or zero if no
   stat call was done). Also set the matching directory index in VPATH_INDEX
   and PATH_INDEX if they are not NULL.  Otherwise we return 0.  */

const char *
vpath_search (const char *file, FILE_TIMESTAMP *mtime_ptr,
              unsigned int* vpath_index, unsigned int* path_index)
{
  struct vpath *v;

  /* If there are no VPATH entries or FILENAME starts at the root,
     there is nothing we can do.  */

  if (file[0] == '/'
#ifdef HAVE_DOS_PATHS
      || file[0] == '\\' || file[1] == ':'
#endif
      || (vpaths == 0 && general_vpath == 0))
    return 0;

  if (vpath_index)
    {
      *vpath_index = 0;
      *path_index = 0;
    }

  for (v = vpaths; v != 0; v = v->next)
    {
      if (pattern_matches (v->pattern, v->percent, file))
        {
          const char *p = selective_vpath_search (
            v, file, mtime_ptr, path_index);
          if (p)
            return p;
        }

      if (vpath_index)
        ++*vpath_index;
    }


  if (general_vpath != 0)
    {
      const char *p = selective_vpath_search (
        general_vpath, file, mtime_ptr, path_index);
      if (p)
        return p;
    }

  return 0;
}





/* Print the data base of VPATH search paths.  */

void
print_vpath_data_base (void)
{
  unsigned int nvpaths;
  struct vpath *v;

  puts (_("\n# VPATH Search Paths\n"));

  nvpaths = 0;
  for (v = vpaths; v != 0; v = v->next)
    {
      register unsigned int i;

      ++nvpaths;

      printf ("vpath %s ", v->pattern);

      for (i = 0; v->searchpath[i] != 0; ++i)
    printf ("%s%c", v->searchpath[i],
        v->searchpath[i + 1] == 0 ? '\n' : PATH_SEPARATOR_CHAR);
    }

  if (vpaths == 0)
    puts (_("# No `vpath' search paths."));
  else
    printf (_("\n# %u `vpath' search paths.\n"), nvpaths);

  if (general_vpath == 0)
    puts (_("\n# No general (`VPATH' variable) search path."));
  else
    {
      const char **path = general_vpath->searchpath;
      unsigned int i;

      fputs (_("\n# General (`VPATH' variable) search path:\n# "), stdout);

      for (i = 0; path[i] != 0; ++i)
    printf ("%s%c", path[i],
        path[i + 1] == 0 ? '\n' : PATH_SEPARATOR_CHAR);
    }
}

https://git.savannah.gnu.org/cgit/make.git/tree/hash.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/hash.c used in: Step 537
/* hash.c -- hash table maintenance
Copyright (C) 1995, 1999, 2002, 2010 Free Software Foundation, Inc.
Written by Greg McGary <gkm@gnu.org> <greg@mcgary.org>

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"
#include "hash.h"

#define CALLOC(t, n) ((t *) calloc (sizeof (t), (n)))
#define MALLOC(t, n) ((t *) xmalloc (sizeof (t) * (n)))
#define REALLOC(o, t, n) ((t *) xrealloc ((o), sizeof (t) * (n)))
#define CLONE(o, t, n) ((t *) memcpy (MALLOC (t, (n)), (o), sizeof (t) * (n)))

static void hash_rehash __P((struct hash_table* ht));
static unsigned long round_up_2 __P((unsigned long rough));

/* Implement double hashing with open addressing.  The table size is
   always a power of two.  The secondary (`increment') hash function
   is forced to return an odd-value, in order to be relatively prime
   to the table size.  This guarantees that the increment can
   potentially hit every slot in the table during collision
   resolution.  */

void *hash_deleted_item = &hash_deleted_item;

/* Force the table size to be a power of two, possibly rounding up the
   given size.  */

void
hash_init (struct hash_table *ht, unsigned long size,
           hash_func_t hash_1, hash_func_t hash_2, hash_cmp_func_t hash_cmp)
{
  ht->ht_size = round_up_2 (size);
  ht->ht_empty_slots = ht->ht_size;
  ht->ht_vec = (void**) CALLOC (struct token *, ht->ht_size);
  if (ht->ht_vec == 0)
    {
      fprintf (stderr, _("can't allocate %lu bytes for hash table: memory exhausted"),
           ht->ht_size * (unsigned long) sizeof (struct token *));
      exit (1);
    }

  ht->ht_capacity = ht->ht_size - (ht->ht_size / 16); /* 93.75% loading factor */
  ht->ht_fill = 0;
  ht->ht_collisions = 0;
  ht->ht_lookups = 0;
  ht->ht_rehashes = 0;
  ht->ht_hash_1 = hash_1;
  ht->ht_hash_2 = hash_2;
  ht->ht_compare = hash_cmp;
}

/* Load an array of items into `ht'.  */

void
hash_load (struct hash_table *ht, void *item_table,
           unsigned long cardinality, unsigned long size)
{
  char *items = (char *) item_table;
  while (cardinality--)
    {
      hash_insert (ht, items);
      items += size;
    }
}

/* Returns the address of the table slot matching `key'.  If `key' is
   not found, return the address of an empty slot suitable for
   inserting `key'.  The caller is responsible for incrementing
   ht_fill on insertion.  */

void **
hash_find_slot (struct hash_table *ht, const void *key)
{
  void **slot;
  void **deleted_slot = 0;
  unsigned int hash_2 = 0;
  unsigned int hash_1 = (*ht->ht_hash_1) (key);

  ht->ht_lookups++;
  for (;;)
    {
      hash_1 &= (ht->ht_size - 1);
      slot = &ht->ht_vec[hash_1];

      if (*slot == 0)
    return (deleted_slot ? deleted_slot : slot);
      if (*slot == hash_deleted_item)
    {
      if (deleted_slot == 0)
        deleted_slot = slot;
    }
      else
    {
      if (key == *slot)
        return slot;
      if ((*ht->ht_compare) (key, *slot) == 0)
        return slot;
      ht->ht_collisions++;
    }
      if (!hash_2)
      hash_2 = (*ht->ht_hash_2) (key) | 1;
      hash_1 += hash_2;
    }
}

void *
hash_find_item (struct hash_table *ht, const void *key)
{
  void **slot = hash_find_slot (ht, key);
  return ((HASH_VACANT (*slot)) ? 0 : *slot);
}

void *
hash_insert (struct hash_table *ht, const void *item)
{
  void **slot = hash_find_slot (ht, item);
  const void *old_item = *slot;
  hash_insert_at (ht, item, slot);
  return (void *)((HASH_VACANT (old_item)) ? 0 : old_item);
}

void *
hash_insert_at (struct hash_table *ht, const void *item, const void *slot)
{
  const void *old_item = *(void **) slot;
  if (HASH_VACANT (old_item))
    {
      ht->ht_fill++;
      if (old_item == 0)
    ht->ht_empty_slots--;
      old_item = item;
    }
  *(void const **) slot = item;
  if (ht->ht_empty_slots < ht->ht_size - ht->ht_capacity)
    {
      hash_rehash (ht);
      return (void *) hash_find_slot (ht, item);
    }
  else
    return (void *) slot;
}

void *
hash_delete (struct hash_table *ht, const void *item)
{
  void **slot = hash_find_slot (ht, item);
  return hash_delete_at (ht, slot);
}

void *
hash_delete_at (struct hash_table *ht, const void *slot)
{
  void *item = *(void **) slot;
  if (!HASH_VACANT (item))
    {
      *(void const **) slot = hash_deleted_item;
      ht->ht_fill--;
      return item;
    }
  else
    return 0;
}

void
hash_free_items (struct hash_table *ht)
{
  void **vec = ht->ht_vec;
  void **end = &vec[ht->ht_size];
  for (; vec < end; vec++)
    {
      void *item = *vec;
      if (!HASH_VACANT (item))
    free (item);
      *vec = 0;
    }
  ht->ht_fill = 0;
  ht->ht_empty_slots = ht->ht_size;
}

void
hash_delete_items (struct hash_table *ht)
{
  void **vec = ht->ht_vec;
  void **end = &vec[ht->ht_size];
  for (; vec < end; vec++)
    *vec = 0;
  ht->ht_fill = 0;
  ht->ht_collisions = 0;
  ht->ht_lookups = 0;
  ht->ht_rehashes = 0;
  ht->ht_empty_slots = ht->ht_size;
}

void
hash_free (struct hash_table *ht, int free_items)
{
  if (free_items)
    hash_free_items (ht);
  else
    {
      ht->ht_fill = 0;
      ht->ht_empty_slots = ht->ht_size;
    }
  free (ht->ht_vec);
  ht->ht_vec = 0;
  ht->ht_capacity = 0;
}

void
hash_map (struct hash_table *ht, hash_map_func_t map)
{
  void **slot;
  void **end = &ht->ht_vec[ht->ht_size];

  for (slot = ht->ht_vec; slot < end; slot++)
    {
      if (!HASH_VACANT (*slot))
    (*map) (*slot);
    }
}

void
hash_map_arg (struct hash_table *ht, hash_map_arg_func_t map, void *arg)
{
  void **slot;
  void **end = &ht->ht_vec[ht->ht_size];

  for (slot = ht->ht_vec; slot < end; slot++)
    {
      if (!HASH_VACANT (*slot))
    (*map) (*slot, arg);
    }
}

/* Double the size of the hash table in the event of overflow... */

static void
hash_rehash (struct hash_table *ht)
{
  unsigned long old_ht_size = ht->ht_size;
  void **old_vec = ht->ht_vec;
  void **ovp;

  if (ht->ht_fill >= ht->ht_capacity)
    {
      ht->ht_size *= 2;
      ht->ht_capacity = ht->ht_size - (ht->ht_size >> 4);
    }
  ht->ht_rehashes++;
  ht->ht_vec = (void **) CALLOC (struct token *, ht->ht_size);

  for (ovp = old_vec; ovp < &old_vec[old_ht_size]; ovp++)
    {
      if (! HASH_VACANT (*ovp))
    {
      void **slot = hash_find_slot (ht, *ovp);
      *slot = *ovp;
    }
    }
  ht->ht_empty_slots = ht->ht_size - ht->ht_fill;
  free (old_vec);
}

void
hash_print_stats (struct hash_table *ht, FILE *out_FILE)
{
  /* GKM FIXME: honor NO_FLOAT */
  fprintf (out_FILE, _("Load=%ld/%ld=%.0f%%, "), ht->ht_fill, ht->ht_size,
       100.0 * (double) ht->ht_fill / (double) ht->ht_size);
  fprintf (out_FILE, _("Rehash=%d, "), ht->ht_rehashes);
  fprintf (out_FILE, _("Collisions=%ld/%ld=%.0f%%"), ht->ht_collisions, ht->ht_lookups,
       (ht->ht_lookups
        ? (100.0 * (double) ht->ht_collisions / (double) ht->ht_lookups)
        : 0));
}

/* Dump all items into a NULL-terminated vector.  Use the
   user-supplied vector, or malloc one.  */

void **
hash_dump (struct hash_table *ht, void **vector_0, qsort_cmp_t compare)
{
  void **vector;
  void **slot;
  void **end = &ht->ht_vec[ht->ht_size];

  if (vector_0 == 0)
    vector_0 = MALLOC (void *, ht->ht_fill + 1);
  vector = vector_0;

  for (slot = ht->ht_vec; slot < end; slot++)
    if (!HASH_VACANT (*slot))
      *vector++ = *slot;
  *vector = 0;

  if (compare)
    qsort (vector_0, ht->ht_fill, sizeof (void *), compare);
  return vector_0;
}

/* Round a given number up to the nearest power of 2. */

static unsigned long
round_up_2 (unsigned long n)
{
  n |= (n >> 1);
  n |= (n >> 2);
  n |= (n >> 4);
  n |= (n >> 8);
  n |= (n >> 16);

#if !defined(HAVE_LIMITS_H) || ULONG_MAX > 4294967295
  /* We only need this on systems where unsigned long is >32 bits.  */
  n |= (n >> 32);
#endif

  return n + 1;
}

https://git.savannah.gnu.org/cgit/make.git/tree/remote-stub.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/remote-stub.c used in: Step 538
/* Template for the remote job exportation interface to GNU Make.
Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
2010 Free Software Foundation, Inc.
This file is part of GNU Make.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "make.h"
#include "filedef.h"
#include "job.h"
#include "commands.h"


char *remote_description = 0;

/* Call once at startup even if no commands are run.  */

void
remote_setup (void)
{
}

/* Called before exit.  */

void
remote_cleanup (void)
{
}


/* Return nonzero if the next job should be done remotely.  */

int
start_remote_job_p (int first_p UNUSED)
{
  return 0;
}


/* Start a remote job running the command in ARGV,
   with environment from ENVP.  It gets standard input from STDIN_FD.  On
   failure, return nonzero.  On success, return zero, and set *USED_STDIN
   to nonzero if it will actually use STDIN_FD, zero if not, set *ID_PTR to
   a unique identification, and set *IS_REMOTE to zero if the job is local,
   nonzero if it is remote (meaning *ID_PTR is a process ID).  */

int
start_remote_job (char **argv UNUSED, char **envp UNUSED, int stdin_fd UNUSED,
                  int *is_remote UNUSED, int *id_ptr UNUSED,
                  int *used_stdin UNUSED)
{
  return -1;
}


/* Get the status of a dead remote child.  Block waiting for one to die
   if BLOCK is nonzero.  Set *EXIT_CODE_PTR to the exit status, *SIGNAL_PTR
   to the termination signal or zero if it exited normally, and *COREDUMP_PTR
   nonzero if it dumped core.  Return the ID of the child that died,
   0 if we would have to block and !BLOCK, or < 0 if there were none.  */

int
remote_status (int *exit_code_ptr UNUSED, int *signal_ptr UNUSED,
               int *coredump_ptr UNUSED, int block UNUSED)
{
  errno = ECHILD;
  return -1;
}

/* Block asynchronous notification of remote child death.
   If this notification is done by raising the child termination
   signal, do not block that signal.  */
void
block_remote_children (void)
{
  return;
}

/* Restore asynchronous notification of remote child death.
   If this is done by raising the child termination signal,
   do not unblock that signal.  */
void
unblock_remote_children (void)
{
  return;
}

/* Send signal SIG to child ID.  Return 0 if successful, -1 if not.  */
int
remote_kill (int id UNUSED, int sig UNUSED)
{
  return -1;
}

https://git.savannah.gnu.org/cgit/make.git/tree/getloadavg.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/getloadavg.c used in: Step 539
/* Get the system load averages.
Copyright (C) 1985, 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994,
1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
2008, 2009, 2010 Free Software Foundation, Inc.

GNU Make is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see <http://www.gnu.org/licenses/>.  */

/* Compile-time symbols that this file uses:

   HAVE_PSTAT_GETDYNAMIC    Define this if your system has the
                                pstat_getdynamic function.  I think it
                is unique to HPUX9.  The best way to get the
                definition is through the AC_FUNC_GETLOADAVG
                macro that comes with autoconf 2.13 or newer.
                If that isn't an option, then just put
                AC_CHECK_FUNCS(pstat_getdynamic) in your
                configure.in file.
   FIXUP_KERNEL_SYMBOL_ADDR()   Adjust address in returned struct nlist.
   KERNEL_FILE          Pathname of the kernel to nlist.
   LDAV_CVT()           Scale the load average from the kernel.
                Returns a double.
   LDAV_SYMBOL          Name of kernel symbol giving load average.
   LOAD_AVE_TYPE        Type of the load average array in the kernel.
                Must be defined unless one of
                apollo, DGUX, NeXT, or UMAX is defined;
                                or we have libkstat;
                otherwise, no load average is available.
   NLIST_STRUCT         Include nlist.h, not a.out.h, and
                the nlist n_name element is a pointer,
                not an array.
   HAVE_STRUCT_NLIST_N_UN_N_NAME struct nlist has an n_un member, not n_name.
   LINUX_LDAV_FILE      [__linux__]: File containing load averages.

   Specific system predefines this file uses, aside from setting
   default values if not emacs:

   apollo
   BSD              Real BSD, not just BSD-like.
   convex
   DGUX
   eunice           UNIX emulator under VMS.
   hpux
   __MSDOS__            No-op for MSDOS.
   NeXT
   sgi
   sequent          Sequent Dynix 3.x.x (BSD)
   _SEQUENT_            Sequent DYNIX/ptx 1.x.x (SYSV)
   sony_news                    NEWS-OS (works at least for 4.1C)
   UMAX
   UMAX4_3
   VMS
   WINDOWS32            No-op for Windows95/NT.
   __linux__            Linux: assumes /proc filesystem mounted.
                Support from Michael K. Johnson.
   __NetBSD__           NetBSD: assumes /kern filesystem mounted.

   In addition, to avoid nesting many #ifdefs, we internally set
   LDAV_DONE to indicate that the load average has been computed.

   We also #define LDAV_PRIVILEGED if a program will require
   special installation to be able to call getloadavg.  */

/* This should always be first.  */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>

/* Both the Emacs and non-Emacs sections want this.  Some
   configuration files' definitions for the LOAD_AVE_CVT macro (like
   sparc.h's) use macros like FSCALE, defined here.  */
#if defined (unix) || defined (__unix)
# include <sys/param.h>
#endif


/* Exclude all the code except the test program at the end
   if the system has its own `getloadavg' function.

   The declaration of `errno' is needed by the test program
   as well as the function itself, so it comes first.  */

#include <errno.h>

#ifndef errno
extern int errno;
#endif

#if HAVE_LOCALE_H
# include <locale.h>
#endif
#if !HAVE_SETLOCALE
# define setlocale(Category, Locale) /* empty */
#endif

#ifndef HAVE_GETLOADAVG


/* The existing Emacs configuration files define a macro called
   LOAD_AVE_CVT, which accepts a value of type LOAD_AVE_TYPE, and
   returns the load average multiplied by 100.  What we actually want
   is a macro called LDAV_CVT, which returns the load average as an
   unmultiplied double.

   For backwards compatibility, we'll define LDAV_CVT in terms of
   LOAD_AVE_CVT, but future machine config files should just define
   LDAV_CVT directly.  */

# if !defined(LDAV_CVT) && defined(LOAD_AVE_CVT)
#  define LDAV_CVT(n) (LOAD_AVE_CVT (n) / 100.0)
# endif

# if !defined (BSD) && defined (ultrix)
/* Ultrix behaves like BSD on Vaxen.  */
#  define BSD
# endif

# ifdef NeXT
/* NeXT in the 2.{0,1,2} releases defines BSD in <sys/param.h>, which
   conflicts with the definition understood in this file, that this
   really is BSD. */
#  undef BSD

/* NeXT defines FSCALE in <sys/param.h>.  However, we take FSCALE being
   defined to mean that the nlist method should be used, which is not true.  */
#  undef FSCALE
# endif

/* Same issues as for NeXT apply to the HURD-based GNU system.  */
# ifdef __GNU__
#  undef BSD
#  undef FSCALE
# endif /* __GNU__ */

/* Set values that are different from the defaults, which are
   set a little farther down with #ifndef.  */


/* Some shorthands.  */

# if defined (HPUX) && !defined (hpux)
#  define hpux
# endif

# if defined (__hpux) && !defined (hpux)
#  define hpux
# endif

# if defined (__sun) && !defined (sun)
#  define sun
# endif

# if defined(hp300) && !defined(hpux)
#  define MORE_BSD
# endif

# if defined(ultrix) && defined(mips)
#  define decstation
# endif

# if defined (__SVR4) && !defined (SVR4)
#  define SVR4
# endif

# if (defined(sun) && defined(SVR4)) || defined (SOLARIS2)
#  define SUNOS_5
# endif

# if defined (__osf__) && (defined (__alpha) || defined (__alpha__))
#  define OSF_ALPHA
#  include <sys/mbuf.h>
#  include <sys/socket.h>
#  include <net/route.h>
#  include <sys/table.h>
# endif

# if defined (__osf__) && (defined (mips) || defined (__mips__))
#  define OSF_MIPS
#  include <sys/table.h>
# endif

/* UTek's /bin/cc on the 4300 has no architecture specific cpp define by
   default, but _MACH_IND_SYS_TYPES is defined in <sys/types.h>.  Combine
   that with a couple of other things and we'll have a unique match.  */
# if !defined (tek4300) && defined (unix) && defined (m68k) && defined (mc68000) && defined (mc68020) && defined (_MACH_IND_SYS_TYPES)
#  define tek4300           /* Define by emacs, but not by other users.  */
# endif

/* AC_FUNC_GETLOADAVG thinks QNX is SVR4, but it isn't. */
# if defined(__QNX__)
#  undef SVR4
# endif

/* VAX C can't handle multi-line #ifs, or lines longer than 256 chars.  */
# ifndef LOAD_AVE_TYPE

#  ifdef MORE_BSD
#   define LOAD_AVE_TYPE long
#  endif

#  ifdef sun
#   define LOAD_AVE_TYPE long
#  endif

#  ifdef decstation
#   define LOAD_AVE_TYPE long
#  endif

#  ifdef _SEQUENT_
#   define LOAD_AVE_TYPE long
#  endif

#  ifdef sgi
#   define LOAD_AVE_TYPE long
#  endif

#  ifdef SVR4
#   define LOAD_AVE_TYPE long
#  endif

#  ifdef sony_news
#   define LOAD_AVE_TYPE long
#  endif

#  ifdef sequent
#   define LOAD_AVE_TYPE long
#  endif

#  ifdef OSF_ALPHA
#   define LOAD_AVE_TYPE long
#  endif

#  if defined (ardent) && defined (titan)
#   define LOAD_AVE_TYPE long
#  endif

#  ifdef tek4300
#   define LOAD_AVE_TYPE long
#  endif

#  if defined(alliant) && defined(i860) /* Alliant FX/2800 */
#   define LOAD_AVE_TYPE long
#  endif

#  ifdef _AIX
#   define LOAD_AVE_TYPE long
#  endif

#  ifdef convex
#   define LOAD_AVE_TYPE double
#   ifndef LDAV_CVT
#    define LDAV_CVT(n) (n)
#   endif
#  endif

# endif /* No LOAD_AVE_TYPE.  */

# ifdef OSF_ALPHA
/* <sys/param.h> defines an incorrect value for FSCALE on Alpha OSF/1,
   according to ghazi@noc.rutgers.edu.  */
#  undef FSCALE
#  define FSCALE 1024.0
# endif

# if defined(alliant) && defined(i860) /* Alliant FX/2800 */
/* <sys/param.h> defines an incorrect value for FSCALE on an
   Alliant FX/2800 Concentrix 2.2, according to ghazi@noc.rutgers.edu.  */
#  undef FSCALE
#  define FSCALE 100.0
# endif


# ifndef    FSCALE

/* SunOS and some others define FSCALE in sys/param.h.  */

#  ifdef MORE_BSD
#   define FSCALE 2048.0
#  endif

#  if defined(MIPS) || defined(SVR4) || defined(decstation)
#   define FSCALE 256
#  endif

#  if defined (sgi) || defined (sequent)
/* Sometimes both MIPS and sgi are defined, so FSCALE was just defined
   above under #ifdef MIPS.  But we want the sgi value.  */
#   undef FSCALE
#   define  FSCALE 1000.0
#  endif

#  if defined (ardent) && defined (titan)
#   define FSCALE 65536.0
#  endif

#  ifdef tek4300
#   define FSCALE 100.0
#  endif

#  ifdef _AIX
#   define FSCALE 65536.0
#  endif

# endif /* Not FSCALE.  */

# if !defined (LDAV_CVT) && defined (FSCALE)
#  define   LDAV_CVT(n) (((double) (n)) / FSCALE)
# endif


# if defined(sgi) || (defined(mips) && !defined(BSD))
#  define FIXUP_KERNEL_SYMBOL_ADDR(nl) ((nl)[0].n_value &= ~(1 << 31))
# endif


# if !defined (KERNEL_FILE) && defined (sequent)
#  define KERNEL_FILE "/dynix"
# endif

# if !defined (KERNEL_FILE) && defined (hpux)
#  define KERNEL_FILE "/hp-ux"
# endif

# if !defined(KERNEL_FILE) && (defined(_SEQUENT_) || defined(MIPS) || defined(SVR4) || defined(ISC) || defined (sgi) || (defined (ardent) && defined (titan)))
#  define KERNEL_FILE "/unix"
# endif


# if !defined (LDAV_SYMBOL) && defined (alliant)
#  define LDAV_SYMBOL "_Loadavg"
# endif

# if !defined(LDAV_SYMBOL) && ((defined(hpux) && !defined(hp9000s300)) || defined(_SEQUENT_) || defined(SVR4) || defined(ISC) || defined(sgi) || (defined (ardent) && defined (titan)) || defined (_AIX))
#  define LDAV_SYMBOL "avenrun"
# endif

# ifdef HAVE_UNISTD_H
#  include <unistd.h>
# endif

# include <stdio.h>

/* LOAD_AVE_TYPE should only get defined if we're going to use the
   nlist method.  */
# if !defined(LOAD_AVE_TYPE) && (defined(BSD) || defined(LDAV_CVT) || defined(KERNEL_FILE) || defined(LDAV_SYMBOL)) && !defined(__riscos__)
#  define LOAD_AVE_TYPE double
# endif

# ifdef LOAD_AVE_TYPE

#  ifndef VMS
#   ifndef __linux__
#    ifdef HAVE_NLIST_H
#     include <nlist.h>
#    else
#     include <a.out.h>
#    endif

#    ifdef SUNOS_5
#     include <fcntl.h>
#     include <kvm.h>
#     include <kstat.h>
#    endif

#    if defined (hpux) && defined (HAVE_PSTAT_GETDYNAMIC)
#     include <sys/pstat.h>
#    endif

#    ifndef KERNEL_FILE
#     define KERNEL_FILE "/vmunix"
#    endif /* KERNEL_FILE */

#    ifndef LDAV_SYMBOL
#     define LDAV_SYMBOL "_avenrun"
#    endif /* LDAV_SYMBOL */
#   endif /* __linux__ */

#  else /* VMS */

#   ifndef eunice
#    include <iodef.h>
#    include <descrip.h>
#   else /* eunice */
#    include <vms/iodef.h>
#   endif /* eunice */
#  endif /* VMS */

#  ifndef LDAV_CVT
#   define LDAV_CVT(n) ((double) (n))
#  endif /* !LDAV_CVT */

# endif /* LOAD_AVE_TYPE */

# if defined(__GNU__) && !defined (NeXT)
/* Note that NeXT Openstep defines __GNU__ even though it should not.  */
/* GNU system acts much like NeXT, for load average purposes,
   but not exactly.  */
#  define NeXT
#  define host_self mach_host_self
# endif

# ifdef NeXT
#  ifdef HAVE_MACH_MACH_H
#   include <mach/mach.h>
#  else
#   include <mach.h>
#  endif
# endif /* NeXT */

# ifdef sgi
#  include <sys/sysmp.h>
# endif /* sgi */

# ifdef UMAX
#  include <stdio.h>
#  include <signal.h>
#  include <sys/time.h>
#  include <sys/wait.h>
#  include <sys/syscall.h>

#  ifdef UMAX_43
#   include <machine/cpu.h>
#   include <inq_stats/statistics.h>
#   include <inq_stats/sysstats.h>
#   include <inq_stats/cpustats.h>
#   include <inq_stats/procstats.h>
#  else /* Not UMAX_43.  */
#   include <sys/sysdefs.h>
#   include <sys/statistics.h>
#   include <sys/sysstats.h>
#   include <sys/cpudefs.h>
#   include <sys/cpustats.h>
#   include <sys/procstats.h>
#  endif /* Not UMAX_43.  */
# endif /* UMAX */

# ifdef DGUX
#  include <sys/dg_sys_info.h>
# endif

# if defined(HAVE_FCNTL_H) || defined(_POSIX_VERSION)
#  include <fcntl.h>
# else
#  include <sys/file.h>
# endif



/* Avoid static vars inside a function since in HPUX they dump as pure.  */

# ifdef NeXT
static processor_set_t default_set;
static int getloadavg_initialized;
# endif /* NeXT */

# ifdef UMAX
static unsigned int cpus = 0;
static unsigned int samples;
# endif /* UMAX */

# ifdef DGUX
static struct dg_sys_info_load_info load_info;  /* what-a-mouthful! */
# endif /* DGUX */

#if !defined(HAVE_LIBKSTAT) && defined(LOAD_AVE_TYPE)
/* File descriptor open to /dev/kmem or VMS load ave driver.  */
static int channel;
/* Nonzero iff channel is valid.  */
static int getloadavg_initialized;
/* Offset in kmem to seek to read load average, or 0 means invalid.  */
static long offset;

#if !defined(VMS) && !defined(sgi) && !defined(__linux__)
static struct nlist nl[2];
#endif /* Not VMS or sgi */

#ifdef SUNOS_5
static kvm_t *kd;
#endif /* SUNOS_5 */

#endif /* LOAD_AVE_TYPE && !HAVE_LIBKSTAT */


/* Put the 1 minute, 5 minute and 15 minute load averages
   into the first NELEM elements of LOADAVG.
   Return the number written (never more than 3, but may be less than NELEM),
   or -1 if an error occurred.  */

int
getloadavg (double loadavg[], int nelem)
{
  int elem = 0;         /* Return value.  */

# ifdef NO_GET_LOAD_AVG
#  define LDAV_DONE
  /* Set errno to zero to indicate that there was no particular error;
     this function just can't work at all on this system.  */
  errno = 0;
  elem = -1;
# endif

# if !defined (LDAV_DONE) && defined (HAVE_LIBKSTAT)
/* Use libkstat because we don't have to be root.  */
#  define LDAV_DONE
  kstat_ctl_t *kc;
  kstat_t *ksp;
  kstat_named_t *kn;

  kc = kstat_open ();
  if (kc == 0)
    return -1;
  ksp = kstat_lookup (kc, "unix", 0, "system_misc");
  if (ksp == 0 )
    return -1;
  if (kstat_read (kc, ksp, 0) == -1)
    return -1;


  kn = kstat_data_lookup (ksp, "avenrun_1min");
  if (kn == 0)
    {
      /* Return -1 if no load average information is available.  */
      nelem = 0;
      elem = -1;
    }

  if (nelem >= 1)
    loadavg[elem++] = (double) kn->value.ul/FSCALE;

  if (nelem >= 2)
    {
      kn = kstat_data_lookup (ksp, "avenrun_5min");
      if (kn != 0)
    {
      loadavg[elem++] = (double) kn->value.ul/FSCALE;

      if (nelem >= 3)
        {
          kn = kstat_data_lookup (ksp, "avenrun_15min");
          if (kn != 0)
        loadavg[elem++] = (double) kn->value.ul/FSCALE;
        }
    }
    }

  kstat_close (kc);
# endif /* HAVE_LIBKSTAT */

# if !defined (LDAV_DONE) && defined (hpux) && defined (HAVE_PSTAT_GETDYNAMIC)
/* Use pstat_getdynamic() because we don't have to be root.  */
#  define LDAV_DONE
#  undef LOAD_AVE_TYPE

  struct pst_dynamic dyn_info;
  if (pstat_getdynamic (&dyn_info, sizeof (dyn_info), 0, 0) < 0)
    return -1;
  if (nelem > 0)
    loadavg[elem++] = dyn_info.psd_avg_1_min;
  if (nelem > 1)
    loadavg[elem++] = dyn_info.psd_avg_5_min;
  if (nelem > 2)
    loadavg[elem++] = dyn_info.psd_avg_15_min;

# endif /* hpux && HAVE_PSTAT_GETDYNAMIC */

# if !defined (LDAV_DONE) && defined (__linux__)
#  define LDAV_DONE
#  undef LOAD_AVE_TYPE

#  ifndef LINUX_LDAV_FILE
#   define LINUX_LDAV_FILE "/proc/loadavg"
#  endif

  char ldavgbuf[40];
  double load_ave[3];
  int fd, count;

  fd = open (LINUX_LDAV_FILE, O_RDONLY);
  if (fd == -1)
    return -1;
  count = read (fd, ldavgbuf, 40);
  (void) close (fd);
  if (count <= 0)
    return -1;

  /* The following sscanf must use the C locale.  */
  setlocale (LC_NUMERIC, "C");
  count = sscanf (ldavgbuf, "%lf %lf %lf",
          &load_ave[0], &load_ave[1], &load_ave[2]);
  setlocale (LC_NUMERIC, "");
  if (count < 1)
    return -1;

  for (elem = 0; elem < nelem && elem < count; elem++)
    loadavg[elem] = load_ave[elem];

  return elem;

# endif /* __linux__ */

# if !defined (LDAV_DONE) && defined (__NetBSD__)
#  define LDAV_DONE
#  undef LOAD_AVE_TYPE

#  ifndef NETBSD_LDAV_FILE
#   define NETBSD_LDAV_FILE "/kern/loadavg"
#  endif

  unsigned long int load_ave[3], scale;
  int count;
  FILE *fp;

  fp = fopen (NETBSD_LDAV_FILE, "r");
  if (fp == NULL)
    return -1;
  count = fscanf (fp, "%lu %lu %lu %lu\n",
          &load_ave[0], &load_ave[1], &load_ave[2],
          &scale);
  (void) fclose (fp);
  if (count != 4)
    return -1;

  for (elem = 0; elem < nelem; elem++)
    loadavg[elem] = (double) load_ave[elem] / (double) scale;

  return elem;

# endif /* __NetBSD__ */

# if !defined (LDAV_DONE) && defined (NeXT)
#  define LDAV_DONE
  /* The NeXT code was adapted from iscreen 3.2.  */

  host_t host;
  struct processor_set_basic_info info;
  unsigned info_count;

  /* We only know how to get the 1-minute average for this system,
     so even if the caller asks for more than 1, we only return 1.  */

  if (!getloadavg_initialized)
    {
      if (processor_set_default (host_self (), &default_set) == KERN_SUCCESS)
    getloadavg_initialized = 1;
    }

  if (getloadavg_initialized)
    {
      info_count = PROCESSOR_SET_BASIC_INFO_COUNT;
      if (processor_set_info (default_set, PROCESSOR_SET_BASIC_INFO, &host,
                  (processor_set_info_t) &info, &info_count)
      != KERN_SUCCESS)
    getloadavg_initialized = 0;
      else
    {
      if (nelem > 0)
        loadavg[elem++] = (double) info.load_average / LOAD_SCALE;
    }
    }

  if (!getloadavg_initialized)
    return -1;
# endif /* NeXT */

# if !defined (LDAV_DONE) && defined (UMAX)
#  define LDAV_DONE
/* UMAX 4.2, which runs on the Encore Multimax multiprocessor, does not
   have a /dev/kmem.  Information about the workings of the running kernel
   can be gathered with inq_stats system calls.
   We only know how to get the 1-minute average for this system.  */

  struct proc_summary proc_sum_data;
  struct stat_descr proc_info;
  double load;
  register unsigned int i, j;

  if (cpus == 0)
    {
      register unsigned int c, i;
      struct cpu_config conf;
      struct stat_descr desc;

      desc.sd_next = 0;
      desc.sd_subsys = SUBSYS_CPU;
      desc.sd_type = CPUTYPE_CONFIG;
      desc.sd_addr = (char *) &conf;
      desc.sd_size = sizeof conf;

      if (inq_stats (1, &desc))
    return -1;

      c = 0;
      for (i = 0; i < conf.config_maxclass; ++i)
    {
      struct class_stats stats;
      memset (&stats, '\0', sizeof stats);

      desc.sd_type = CPUTYPE_CLASS;
      desc.sd_objid = i;
      desc.sd_addr = (char *) &stats;
      desc.sd_size = sizeof stats;

      if (inq_stats (1, &desc))
        return -1;

      c += stats.class_numcpus;
    }
      cpus = c;
      samples = cpus < 2 ? 3 : (2 * cpus / 3);
    }

  proc_info.sd_next = 0;
  proc_info.sd_subsys = SUBSYS_PROC;
  proc_info.sd_type = PROCTYPE_SUMMARY;
  proc_info.sd_addr = (char *) &proc_sum_data;
  proc_info.sd_size = sizeof (struct proc_summary);
  proc_info.sd_sizeused = 0;

  if (inq_stats (1, &proc_info) != 0)
    return -1;

  load = proc_sum_data.ps_nrunnable;
  j = 0;
  for (i = samples - 1; i > 0; --i)
    {
      load += proc_sum_data.ps_nrun[j];
      if (j++ == PS_NRUNSIZE)
    j = 0;
    }

  if (nelem > 0)
    loadavg[elem++] = load / samples / cpus;
# endif /* UMAX */

# if !defined (LDAV_DONE) && defined (DGUX)
#  define LDAV_DONE
  /* This call can return -1 for an error, but with good args
     it's not supposed to fail.  The first argument is for no
     apparent reason of type `long int *'.  */
  dg_sys_info ((long int *) &load_info,
           DG_SYS_INFO_LOAD_INFO_TYPE,
           DG_SYS_INFO_LOAD_VERSION_0);

  if (nelem > 0)
    loadavg[elem++] = load_info.one_minute;
  if (nelem > 1)
    loadavg[elem++] = load_info.five_minute;
  if (nelem > 2)
    loadavg[elem++] = load_info.fifteen_minute;
# endif /* DGUX */

# if !defined (LDAV_DONE) && defined (apollo)
#  define LDAV_DONE
/* Apollo code from lisch@mentorg.com (Ray Lischner).

   This system call is not documented.  The load average is obtained as
   three long integers, for the load average over the past minute,
   five minutes, and fifteen minutes.  Each value is a scaled integer,
   with 16 bits of integer part and 16 bits of fraction part.

   I'm not sure which operating system first supported this system call,
   but I know that SR10.2 supports it.  */

  extern void proc1_$get_loadav ();
  unsigned long load_ave[3];

  proc1_$get_loadav (load_ave);

  if (nelem > 0)
    loadavg[elem++] = load_ave[0] / 65536.0;
  if (nelem > 1)
    loadavg[elem++] = load_ave[1] / 65536.0;
  if (nelem > 2)
    loadavg[elem++] = load_ave[2] / 65536.0;
# endif /* apollo */

# if !defined (LDAV_DONE) && defined (OSF_MIPS)
#  define LDAV_DONE

  struct tbl_loadavg load_ave;
  table (TBL_LOADAVG, 0, &load_ave, 1, sizeof (load_ave));
  loadavg[elem++]
    = (load_ave.tl_lscale == 0
       ? load_ave.tl_avenrun.d[0]
       : (load_ave.tl_avenrun.l[0] / (double) load_ave.tl_lscale));
# endif /* OSF_MIPS */

# if !defined (LDAV_DONE) && (defined (__MSDOS__) || defined (WINDOWS32))
#  define LDAV_DONE

  /* A faithful emulation is going to have to be saved for a rainy day.  */
  for ( ; elem < nelem; elem++)
    {
      loadavg[elem] = 0.0;
    }
# endif  /* __MSDOS__ || WINDOWS32 */

# if !defined (LDAV_DONE) && defined (OSF_ALPHA)
#  define LDAV_DONE

  struct tbl_loadavg load_ave;
  table (TBL_LOADAVG, 0, &load_ave, 1, sizeof (load_ave));
  for (elem = 0; elem < nelem; elem++)
    loadavg[elem]
      = (load_ave.tl_lscale == 0
       ? load_ave.tl_avenrun.d[elem]
       : (load_ave.tl_avenrun.l[elem] / (double) load_ave.tl_lscale));
# endif /* OSF_ALPHA */

# if !defined (LDAV_DONE) && defined (VMS)
  /* VMS specific code -- read from the Load Ave driver.  */

  LOAD_AVE_TYPE load_ave[3];
  static int getloadavg_initialized = 0;
#  ifdef eunice
  struct
  {
    int dsc$w_length;
    char *dsc$a_pointer;
  } descriptor;
#  endif

  /* Ensure that there is a channel open to the load ave device.  */
  if (!getloadavg_initialized)
    {
      /* Attempt to open the channel.  */
#  ifdef eunice
      descriptor.dsc$w_length = 18;
      descriptor.dsc$a_pointer = "$$VMS_LOAD_AVERAGE";
#  else
      $DESCRIPTOR (descriptor, "LAV0:");
#  endif
      if (sys$assign (&descriptor, &channel, 0, 0) & 1)
    getloadavg_initialized = 1;
    }

  /* Read the load average vector.  */
  if (getloadavg_initialized
      && !(sys$qiow (0, channel, IO$_READVBLK, 0, 0, 0,
             load_ave, 12, 0, 0, 0, 0) & 1))
    {
      sys$dassgn (channel);
      getloadavg_initialized = 0;
    }

  if (!getloadavg_initialized)
    return -1;
# endif /* VMS */

# if !defined (LDAV_DONE) && defined(LOAD_AVE_TYPE) && !defined(VMS)

  /* UNIX-specific code -- read the average from /dev/kmem.  */

#  define LDAV_PRIVILEGED       /* This code requires special installation.  */

  LOAD_AVE_TYPE load_ave[3];

  /* Get the address of LDAV_SYMBOL.  */
  if (offset == 0)
    {
#  ifndef sgi
#   ifndef NLIST_STRUCT
      strcpy (nl[0].n_name, LDAV_SYMBOL);
      strcpy (nl[1].n_name, "");
#   else /* NLIST_STRUCT */
#    ifdef HAVE_STRUCT_NLIST_N_UN_N_NAME
      nl[0].n_un.n_name = LDAV_SYMBOL;
      nl[1].n_un.n_name = 0;
#    else /* not HAVE_STRUCT_NLIST_N_UN_N_NAME */
      nl[0].n_name = LDAV_SYMBOL;
      nl[1].n_name = 0;
#    endif /* not HAVE_STRUCT_NLIST_N_UN_N_NAME */
#   endif /* NLIST_STRUCT */

#   ifndef SUNOS_5
      if (
#    if !(defined (_AIX) && !defined (ps2))
      nlist (KERNEL_FILE, nl)
#    else  /* _AIX */
      knlist (nl, 1, sizeof (nl[0]))
#    endif
      >= 0)
      /* Omit "&& nl[0].n_type != 0 " -- it breaks on Sun386i.  */
      {
#    ifdef FIXUP_KERNEL_SYMBOL_ADDR
        FIXUP_KERNEL_SYMBOL_ADDR (nl);
#    endif
        offset = nl[0].n_value;
      }
#   endif /* !SUNOS_5 */
#  else  /* sgi */
      int ldav_off;

      ldav_off = sysmp (MP_KERNADDR, MPKA_AVENRUN);
      if (ldav_off != -1)
    offset = (long) ldav_off & 0x7fffffff;
#  endif /* sgi */
    }

  /* Make sure we have /dev/kmem open.  */
  if (!getloadavg_initialized)
    {
#  ifndef SUNOS_5
      channel = open ("/dev/kmem", 0);
      if (channel >= 0)
    {
      /* Set the channel to close on exec, so it does not
         litter any child's descriptor table.  */
#   ifdef F_SETFD
#    ifndef FD_CLOEXEC
#     define FD_CLOEXEC 1
#    endif
      (void) fcntl (channel, F_SETFD, FD_CLOEXEC);
#   endif
      getloadavg_initialized = 1;
    }
#  else /* SUNOS_5 */
      /* We pass 0 for the kernel, corefile, and swapfile names
     to use the currently running kernel.  */
      kd = kvm_open (0, 0, 0, O_RDONLY, 0);
      if (kd != 0)
    {
      /* nlist the currently running kernel.  */
      kvm_nlist (kd, nl);
      offset = nl[0].n_value;
      getloadavg_initialized = 1;
    }
#  endif /* SUNOS_5 */
    }

  /* If we can, get the load average values.  */
  if (offset && getloadavg_initialized)
    {
      /* Try to read the load.  */
#  ifndef SUNOS_5
      if (lseek (channel, offset, 0) == -1L
      || read (channel, (char *) load_ave, sizeof (load_ave))
      != sizeof (load_ave))
    {
      close (channel);
      getloadavg_initialized = 0;
    }
#  else  /* SUNOS_5 */
      if (kvm_read (kd, offset, (char *) load_ave, sizeof (load_ave))
      != sizeof (load_ave))
        {
          kvm_close (kd);
          getloadavg_initialized = 0;
    }
#  endif /* SUNOS_5 */
    }

  if (offset == 0 || !getloadavg_initialized)
    return -1;
# endif /* LOAD_AVE_TYPE and not VMS */

# if !defined (LDAV_DONE) && defined (LOAD_AVE_TYPE) /* Including VMS.  */
  if (nelem > 0)
    loadavg[elem++] = LDAV_CVT (load_ave[0]);
  if (nelem > 1)
    loadavg[elem++] = LDAV_CVT (load_ave[1]);
  if (nelem > 2)
    loadavg[elem++] = LDAV_CVT (load_ave[2]);

#  define LDAV_DONE
# endif /* !LDAV_DONE && LOAD_AVE_TYPE */

# ifdef LDAV_DONE
  return elem;
# else
  /* Set errno to zero to indicate that there was no particular error;
     this function just can't work at all on this system.  */
  errno = 0;
  return -1;
# endif
}

#endif /* ! HAVE_GETLOADAVG */


#ifdef TEST
#include "make.h"

int
main (int argc, char **argv)
{
  int naptime = 0;

  if (argc > 1)
    naptime = atoi (argv[1]);

  while (1)
    {
      double avg[3];
      int loads;

      errno = 0;        /* Don't be misled if it doesn't set errno.  */
      loads = getloadavg (avg, 3);
      if (loads == -1)
    {
      perror ("Error getting load average");
      exit (1);
    }
      if (loads > 0)
    printf ("1-minute: %f  ", avg[0]);
      if (loads > 1)
    printf ("5-minute: %f  ", avg[1]);
      if (loads > 2)
    printf ("15-minute: %f  ", avg[2]);
      if (loads > 0)
    putchar ('\n');

      if (naptime == 0)
    break;
      sleep (naptime);
    }

  exit (0);
}
#endif /* TEST */

https://git.savannah.gnu.org/cgit/make.git/tree/glob/fnmatch.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/glob/fnmatch.c used in: Step 540
/* Copyright (C) 1991, 1992, 1993, 1996, 1997, 1998, 1999 Free Software
Foundation, Inc.
This file is part of the GNU C Library.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This library 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.

You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB.  If not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
USA.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

/* Enable GNU extensions in fnmatch.h.  */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE    1
#endif

#include <errno.h>
#include <fnmatch.h>
#include <ctype.h>

#if HAVE_STRING_H || defined _LIBC
# include <string.h>
#else
# include <strings.h>
#endif

#if defined STDC_HEADERS || defined _LIBC
# include <stdlib.h>
#endif

/* For platform which support the ISO C amendement 1 functionality we
   support user defined character classes.  */
#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>.  */
# include <wchar.h>
# include <wctype.h>
#endif

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#if defined _LIBC || !defined __GNU_LIBRARY__


# if defined STDC_HEADERS || !defined isascii
#  define ISASCII(c) 1
# else
#  define ISASCII(c) isascii(c)
# endif

# ifdef isblank
#  define ISBLANK(c) (ISASCII (c) && isblank (c))
# else
#  define ISBLANK(c) ((c) == ' ' || (c) == '\t')
# endif
# ifdef isgraph
#  define ISGRAPH(c) (ISASCII (c) && isgraph (c))
# else
#  define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
# endif

# define ISPRINT(c) (ISASCII (c) && isprint (c))
# define ISDIGIT(c) (ISASCII (c) && isdigit (c))
# define ISALNUM(c) (ISASCII (c) && isalnum (c))
# define ISALPHA(c) (ISASCII (c) && isalpha (c))
# define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
# define ISLOWER(c) (ISASCII (c) && islower (c))
# define ISPUNCT(c) (ISASCII (c) && ispunct (c))
# define ISSPACE(c) (ISASCII (c) && isspace (c))
# define ISUPPER(c) (ISASCII (c) && isupper (c))
# define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))

# define STREQ(s1, s2) ((strcmp (s1, s2) == 0))

# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
/* The GNU C library provides support for user-defined character classes
   and the functions from ISO C amendement 1.  */
#  ifdef CHARCLASS_NAME_MAX
#   define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
#  else
/* This shouldn't happen but some implementation might still have this
   problem.  Use a reasonable default value.  */
#   define CHAR_CLASS_MAX_LENGTH 256
#  endif

#  ifdef _LIBC
#   define IS_CHAR_CLASS(string) __wctype (string)
#  else
#   define IS_CHAR_CLASS(string) wctype (string)
#  endif
# else
#  define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */

#  define IS_CHAR_CLASS(string)                           \
   (STREQ (string, "alpha") || STREQ (string, "upper")                \
    || STREQ (string, "lower") || STREQ (string, "digit")             \
    || STREQ (string, "alnum") || STREQ (string, "xdigit")            \
    || STREQ (string, "space") || STREQ (string, "print")             \
    || STREQ (string, "punct") || STREQ (string, "graph")             \
    || STREQ (string, "cntrl") || STREQ (string, "blank"))
# endif

/* Avoid depending on library functions or files
   whose names are inconsistent.  */

# if !defined _LIBC && !defined getenv
extern char *getenv ();
# endif

# ifndef errno
extern int errno;
# endif

/* This function doesn't exist on most systems.  */

# if !defined HAVE___STRCHRNUL && !defined _LIBC
static char *
__strchrnul (s, c)
     const char *s;
     int c;
{
  char *result = strchr (s, c);
  if (result == NULL)
    result = strchr (s, '\0');
  return result;
}
# endif

# ifndef internal_function
/* Inside GNU libc we mark some function in a special way.  In other
   environments simply ignore the marking.  */
#  define internal_function
# endif

/* Match STRING against the filename pattern PATTERN, returning zero if
   it matches, nonzero if not.  */
static int internal_fnmatch __P ((const char *pattern, const char *string,
                  int no_leading_period, int flags))
     internal_function;
static int
internal_function
internal_fnmatch (pattern, string, no_leading_period, flags)
     const char *pattern;
     const char *string;
     int no_leading_period;
     int flags;
{
  register const char *p = pattern, *n = string;
  register unsigned char c;

/* Note that this evaluates C many times.  */
# ifdef _LIBC
#  define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c))
# else
#  define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
# endif

  while ((c = *p++) != '\0')
    {
      c = FOLD (c);

      switch (c)
    {
    case '?':
      if (*n == '\0')
        return FNM_NOMATCH;
      else if (*n == '/' && (flags & FNM_FILE_NAME))
        return FNM_NOMATCH;
      else if (*n == '.' && no_leading_period
           && (n == string
               || (n[-1] == '/' && (flags & FNM_FILE_NAME))))
        return FNM_NOMATCH;
      break;

    case '\\':
      if (!(flags & FNM_NOESCAPE))
        {
          c = *p++;
          if (c == '\0')
        /* Trailing \ loses.  */
        return FNM_NOMATCH;
          c = FOLD (c);
        }
      if (FOLD ((unsigned char) *n) != c)
        return FNM_NOMATCH;
      break;

    case '*':
      if (*n == '.' && no_leading_period
          && (n == string
          || (n[-1] == '/' && (flags & FNM_FILE_NAME))))
        return FNM_NOMATCH;

      for (c = *p++; c == '?' || c == '*'; c = *p++)
        {
          if (*n == '/' && (flags & FNM_FILE_NAME))
        /* A slash does not match a wildcard under FNM_FILE_NAME.  */
        return FNM_NOMATCH;
          else if (c == '?')
        {
          /* A ? needs to match one character.  */
          if (*n == '\0')
            /* There isn't another character; no match.  */
            return FNM_NOMATCH;
          else
            /* One character of the string is consumed in matching
               this ? wildcard, so *??? won't match if there are
               less than three characters.  */
            ++n;
        }
        }

      if (c == '\0')
        /* The wildcard(s) is/are the last element of the pattern.
           If the name is a file name and contains another slash
           this does mean it cannot match.  */
        return ((flags & FNM_FILE_NAME) && strchr (n, '/') != NULL
            ? FNM_NOMATCH : 0);
      else
        {
          const char *endp;

          endp = __strchrnul (n, (flags & FNM_FILE_NAME) ? '/' : '\0');

          if (c == '[')
        {
          int flags2 = ((flags & FNM_FILE_NAME)
                ? flags : (flags & ~FNM_PERIOD));

          for (--p; n < endp; ++n)
            if (internal_fnmatch (p, n,
                      (no_leading_period
                       && (n == string
                           || (n[-1] == '/'
                           && (flags
                               & FNM_FILE_NAME)))),
                      flags2)
            == 0)
              return 0;
        }
          else if (c == '/' && (flags & FNM_FILE_NAME))
        {
          while (*n != '\0' && *n != '/')
            ++n;
          if (*n == '/'
              && (internal_fnmatch (p, n + 1, flags & FNM_PERIOD,
                        flags) == 0))
            return 0;
        }
          else
        {
          int flags2 = ((flags & FNM_FILE_NAME)
                ? flags : (flags & ~FNM_PERIOD));

          if (c == '\\' && !(flags & FNM_NOESCAPE))
            c = *p;
          c = FOLD (c);
          for (--p; n < endp; ++n)
            if (FOLD ((unsigned char) *n) == c
            && (internal_fnmatch (p, n,
                          (no_leading_period
                           && (n == string
                           || (n[-1] == '/'
                               && (flags
                               & FNM_FILE_NAME)))),
                          flags2) == 0))
              return 0;
        }
        }

      /* If we come here no match is possible with the wildcard.  */
      return FNM_NOMATCH;

    case '[':
      {
        /* Nonzero if the sense of the character class is inverted.  */
        static int posixly_correct;
        register int not;
        char cold;

        if (posixly_correct == 0)
          posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;

        if (*n == '\0')
          return FNM_NOMATCH;

        if (*n == '.' && no_leading_period && (n == string
                           || (n[-1] == '/'
                               && (flags
                               & FNM_FILE_NAME))))
          return FNM_NOMATCH;

        if (*n == '/' && (flags & FNM_FILE_NAME))
          /* `/' cannot be matched.  */
          return FNM_NOMATCH;

        not = (*p == '!' || (posixly_correct < 0 && *p == '^'));
        if (not)
          ++p;

        c = *p++;
        for (;;)
          {
        unsigned char fn = FOLD ((unsigned char) *n);

        if (!(flags & FNM_NOESCAPE) && c == '\\')
          {
            if (*p == '\0')
              return FNM_NOMATCH;
            c = FOLD ((unsigned char) *p);
            ++p;

            if (c == fn)
              goto matched;
          }
        else if (c == '[' && *p == ':')
          {
            /* Leave room for the null.  */
            char str[CHAR_CLASS_MAX_LENGTH + 1];
            size_t c1 = 0;
# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
            wctype_t wt;
# endif
            const char *startp = p;

            for (;;)
              {
            if (c1 == CHAR_CLASS_MAX_LENGTH)
              /* The name is too long and therefore the pattern
                 is ill-formed.  */
              return FNM_NOMATCH;

            c = *++p;
            if (c == ':' && p[1] == ']')
              {
                p += 2;
                break;
              }
            if (c < 'a' || c >= 'z')
              {
                /* This cannot possibly be a character class name.
                   Match it as a normal range.  */
                p = startp;
                c = '[';
                goto normal_bracket;
              }
            str[c1++] = c;
              }
            str[c1] = '\0';

# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
            wt = IS_CHAR_CLASS (str);
            if (wt == 0)
              /* Invalid character class name.  */
              return FNM_NOMATCH;

            if (__iswctype (__btowc ((unsigned char) *n), wt))
              goto matched;
# else
            if ((STREQ (str, "alnum") && ISALNUM ((unsigned char) *n))
            || (STREQ (str, "alpha") && ISALPHA ((unsigned char) *n))
            || (STREQ (str, "blank") && ISBLANK ((unsigned char) *n))
            || (STREQ (str, "cntrl") && ISCNTRL ((unsigned char) *n))
            || (STREQ (str, "digit") && ISDIGIT ((unsigned char) *n))
            || (STREQ (str, "graph") && ISGRAPH ((unsigned char) *n))
            || (STREQ (str, "lower") && ISLOWER ((unsigned char) *n))
            || (STREQ (str, "print") && ISPRINT ((unsigned char) *n))
            || (STREQ (str, "punct") && ISPUNCT ((unsigned char) *n))
            || (STREQ (str, "space") && ISSPACE ((unsigned char) *n))
            || (STREQ (str, "upper") && ISUPPER ((unsigned char) *n))
            || (STREQ (str, "xdigit") && ISXDIGIT ((unsigned char) *n)))
              goto matched;
# endif
          }
        else if (c == '\0')
          /* [ (unterminated) loses.  */
          return FNM_NOMATCH;
        else
          {
          normal_bracket:
            if (FOLD (c) == fn)
              goto matched;

            cold = c;
            c = *p++;

            if (c == '-' && *p != ']')
              {
            /* It is a range.  */
            unsigned char cend = *p++;
            if (!(flags & FNM_NOESCAPE) && cend == '\\')
              cend = *p++;
            if (cend == '\0')
              return FNM_NOMATCH;

            if (cold <= fn && fn <= FOLD (cend))
              goto matched;

            c = *p++;
              }
          }

        if (c == ']')
          break;
          }

        if (!not)
          return FNM_NOMATCH;
        break;

      matched:
        /* Skip the rest of the [...] that already matched.  */
        while (c != ']')
          {
        if (c == '\0')
          /* [... (unterminated) loses.  */
          return FNM_NOMATCH;

        c = *p++;
        if (!(flags & FNM_NOESCAPE) && c == '\\')
          {
            if (*p == '\0')
              return FNM_NOMATCH;
            /* XXX 1003.2d11 is unclear if this is right.  */
            ++p;
          }
        else if (c == '[' && *p == ':')
          {
            do
              if (*++p == '\0')
            return FNM_NOMATCH;
            while (*p != ':' || p[1] == ']');
            p += 2;
            c = *p;
          }
          }
        if (not)
          return FNM_NOMATCH;
      }
      break;

    default:
      if (c != FOLD ((unsigned char) *n))
        return FNM_NOMATCH;
    }

      ++n;
    }

  if (*n == '\0')
    return 0;

  if ((flags & FNM_LEADING_DIR) && *n == '/')
    /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */
    return 0;

  return FNM_NOMATCH;

# undef FOLD
}


int
fnmatch (pattern, string, flags)
     const char *pattern;
     const char *string;
     int flags;
{
  return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags);
}

#endif  /* _LIBC or not __GNU_LIBRARY__.  */

https://git.savannah.gnu.org/cgit/make.git/tree/glob/glob.c?h=3.82

File: ../git/live-bootstrap/sysa/make-3.82/build/make-3.82/glob/glob.c used in: Step 541
/* Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999 Free
Software Foundation, Inc.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This library 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.

You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB.  If not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
USA.  */

/* AIX requires this to be the first thing in the file.  */
#if defined _AIX && !defined __GNUC__
 #pragma alloca
#endif

#ifdef  HAVE_CONFIG_H
# include <config.h>
#endif

/* Enable GNU extensions in glob.h.  */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE    1
#endif

#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

/* Outcomment the following line for production quality code.  */
/* #define NDEBUG 1 */
#include <assert.h>

#include <stdio.h>      /* Needed on stupid SunOS for assert.  */


/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#define GLOB_INTERFACE_VERSION 1
#if !defined _LIBC && defined __GNU_LIBRARY__ && __GNU_LIBRARY__ > 1
# include <gnu-versions.h>
# if _GNU_GLOB_INTERFACE_VERSION == GLOB_INTERFACE_VERSION
#  define ELIDE_CODE
# endif
#endif

#ifndef ELIDE_CODE

#if defined STDC_HEADERS || defined __GNU_LIBRARY__
# include <stddef.h>
#endif

#if defined HAVE_UNISTD_H || defined _LIBC
# include <unistd.h>
# ifndef POSIX
#  ifdef _POSIX_VERSION
#   define POSIX
#  endif
# endif
#endif

#if !defined _AMIGA && !defined VMS && !defined WINDOWS32
# include <pwd.h>
#endif

#if !defined __GNU_LIBRARY__ && !defined STDC_HEADERS
extern int errno;
#endif
#ifndef __set_errno
# define __set_errno(val) errno = (val)
#endif

#ifndef NULL
# define NULL   0
#endif


#if defined HAVE_DIRENT_H || defined __GNU_LIBRARY__
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# ifdef HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# ifdef HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
# ifdef HAVE_NDIR_H
#  include <ndir.h>
# endif
# ifdef HAVE_VMSDIR_H
#  include "vmsdir.h"
# endif /* HAVE_VMSDIR_H */
#endif


/* In GNU systems, <dirent.h> defines this macro for us.  */
#ifdef _D_NAMLEN
# undef NAMLEN
# define NAMLEN(d) _D_NAMLEN(d)
#endif

/* When used in the GNU libc the symbol _DIRENT_HAVE_D_TYPE is available
   if the `d_type' member for `struct dirent' is available.  */
#ifdef _DIRENT_HAVE_D_TYPE
# define HAVE_D_TYPE    1
#endif


#if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__
/* Posix does not require that the d_ino field be present, and some
   systems do not provide it. */
# define REAL_DIR_ENTRY(dp) 1
#else
# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
#endif /* POSIX */

#if defined STDC_HEADERS || defined __GNU_LIBRARY__
# include <stdlib.h>
# include <string.h>
# define    ANSI_STRING
#else   /* No standard headers.  */

extern char *getenv ();

# ifdef HAVE_STRING_H
#  include <string.h>
#  define ANSI_STRING
# else
#  include <strings.h>
# endif
# ifdef HAVE_MEMORY_H
#  include <memory.h>
# endif

extern char *malloc (), *realloc ();
extern void free ();

extern void qsort ();
extern void abort (), exit ();

#endif  /* Standard headers.  */

#ifndef ANSI_STRING

# ifndef bzero
extern void bzero ();
# endif
# ifndef bcopy
extern void bcopy ();
# endif

# define memcpy(d, s, n)    bcopy ((s), (d), (n))
# define strrchr    rindex
/* memset is only used for zero here, but let's be paranoid.  */
# define memset(s, better_be_zero, n) \
  ((void) ((better_be_zero) == 0 ? (bzero((s), (n)), 0) : (abort(), 0)))
#endif  /* Not ANSI_STRING.  */

#if !defined HAVE_STRCOLL && !defined _LIBC
# define strcoll    strcmp
#endif

#if !defined HAVE_MEMPCPY && __GLIBC__ - 0 == 2 && __GLIBC_MINOR__ >= 1
# define HAVE_MEMPCPY   1
# undef  mempcpy
# define mempcpy(Dest, Src, Len) __mempcpy (Dest, Src, Len)
#endif

#if !defined __GNU_LIBRARY__ && !defined __DJGPP__
# ifdef __GNUC__
__inline
# endif
# ifndef __SASC
#  ifdef WINDOWS32
static void *
my_realloc (void *p, unsigned int n)
#  else
static char *
my_realloc (p, n)
     char *p;
     unsigned int n;
#  endif
{
  /* These casts are the for sake of the broken Ultrix compiler,
     which warns of illegal pointer combinations otherwise.  */
  if (p == NULL)
    return (char *) malloc (n);
  return (char *) realloc (p, n);
}
# define    realloc my_realloc
# endif /* __SASC */
#endif /* __GNU_LIBRARY__ || __DJGPP__ */


#if !defined __alloca && !defined __GNU_LIBRARY__

# ifdef __GNUC__
#  undef alloca
#  define alloca(n) __builtin_alloca (n)
# else  /* Not GCC.  */
#  ifdef HAVE_ALLOCA_H
#   include <alloca.h>
#  else /* Not HAVE_ALLOCA_H.  */
#   ifndef _AIX
#    ifdef WINDOWS32
#     include <malloc.h>
#    else
extern char *alloca ();
#    endif /* WINDOWS32 */
#   endif /* Not _AIX.  */
#  endif /* sparc or HAVE_ALLOCA_H.  */
# endif /* GCC.  */

# define __alloca   alloca

#endif

#ifndef __GNU_LIBRARY__
# define __stat stat
# ifdef STAT_MACROS_BROKEN
#  undef S_ISDIR
# endif
# ifndef S_ISDIR
#  define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
# endif
#endif

#ifdef _LIBC
# undef strdup
# define strdup(str) __strdup (str)
# define sysconf(id) __sysconf (id)
# define closedir(dir) __closedir (dir)
# define opendir(name) __opendir (name)
# define readdir(str) __readdir (str)
# define getpwnam_r(name, bufp, buf, len, res) \
   __getpwnam_r (name, bufp, buf, len, res)
# ifndef __stat
#  define __stat(fname, buf) __xstat (_STAT_VER, fname, buf)
# endif
#endif

#if !(defined STDC_HEADERS || defined __GNU_LIBRARY__)
# undef size_t
# define size_t unsigned int
#endif

/* Some system header files erroneously define these.
   We want our own definitions from <fnmatch.h> to take precedence.  */
#ifndef __GNU_LIBRARY__
# undef FNM_PATHNAME
# undef FNM_NOESCAPE
# undef FNM_PERIOD
#endif
#include <fnmatch.h>

/* Some system header files erroneously define these.
   We want our own definitions from <glob.h> to take precedence.  */
#ifndef __GNU_LIBRARY__
# undef GLOB_ERR
# undef GLOB_MARK
# undef GLOB_NOSORT
# undef GLOB_DOOFFS
# undef GLOB_NOCHECK
# undef GLOB_APPEND
# undef GLOB_NOESCAPE
# undef GLOB_PERIOD
#endif
#include <glob.h>

#ifdef HAVE_GETLOGIN_R
extern int getlogin_r __P ((char *, size_t));
#else
extern char *getlogin __P ((void));
#endif


static
#if __GNUC__ - 0 >= 2
inline
#endif
const char *next_brace_sub __P ((const char *begin));
static int glob_in_dir __P ((const char *pattern, const char *directory,
                 int flags,
                 int (*errfunc) (const char *, int),
                 glob_t *pglob));
static int prefix_array __P ((const char *prefix, char **array, size_t n));
static int collated_compare __P ((const __ptr_t, const __ptr_t));

#if !defined _LIBC || !defined NO_GLOB_PATTERN_P
int __glob_pattern_p __P ((const char *pattern, int quote));
#endif

/* Find the end of the sub-pattern in a brace expression.  We define
   this as an inline function if the compiler permits.  */
static
#if __GNUC__ - 0 >= 2
inline
#endif
const char *
next_brace_sub (begin)
     const char *begin;
{
  unsigned int depth = 0;
  const char *cp = begin;

  while (1)
    {
      if (depth == 0)
    {
      if (*cp != ',' && *cp != '}' && *cp != '\0')
        {
          if (*cp == '{')
        ++depth;
          ++cp;
          continue;
        }
    }
      else
    {
      while (*cp != '\0' && (*cp != '}' || depth > 0))
        {
          if (*cp == '}')
        --depth;
          ++cp;
        }
      if (*cp == '\0')
        /* An incorrectly terminated brace expression.  */
        return NULL;

      continue;
    }
      break;
    }

  return cp;
}

/* Do glob searching for PATTERN, placing results in PGLOB.
   The bits defined above may be set in FLAGS.
   If a directory cannot be opened or read and ERRFUNC is not nil,
   it is called with the pathname that caused the error, and the
   `errno' value from the failing call; if it returns non-zero
   `glob' returns GLOB_ABORTED; if it returns zero, the error is ignored.
   If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned.
   Otherwise, `glob' returns zero.  */
int
glob (pattern, flags, errfunc, pglob)
     const char *pattern;
     int flags;
     int (*errfunc) __P ((const char *, int));
     glob_t *pglob;
{
  const char *filename;
  const char *dirname;
  size_t dirlen;
  int status;
  int oldcount;

  if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0)
    {
      __set_errno (EINVAL);
      return -1;
    }

  if (flags & GLOB_BRACE)
    {
      const char *begin = strchr (pattern, '{');
      if (begin != NULL)
    {
      /* Allocate working buffer large enough for our work.  Note that
        we have at least an opening and closing brace.  */
      int firstc;
      char *alt_start;
      const char *p;
      const char *next;
      const char *rest;
      size_t rest_len;
#ifdef __GNUC__
      char onealt[strlen (pattern) - 1];
#else
      char *onealt = (char *) malloc (strlen (pattern) - 1);
      if (onealt == NULL)
        {
          if (!(flags & GLOB_APPEND))
        globfree (pglob);
          return GLOB_NOSPACE;
        }
#endif

      /* We know the prefix for all sub-patterns.  */
#ifdef HAVE_MEMPCPY
      alt_start = mempcpy (onealt, pattern, begin - pattern);
#else
      memcpy (onealt, pattern, begin - pattern);
      alt_start = &onealt[begin - pattern];
#endif

      /* Find the first sub-pattern and at the same time find the
         rest after the closing brace.  */
      next = next_brace_sub (begin + 1);
      if (next == NULL)
        {
          /* It is an illegal expression.  */
#ifndef __GNUC__
          free (onealt);
#endif
          return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob);
        }

      /* Now find the end of the whole brace expression.  */
      rest = next;
      while (*rest != '}')
        {
          rest = next_brace_sub (rest + 1);
          if (rest == NULL)
        {
          /* It is an illegal expression.  */
#ifndef __GNUC__
          free (onealt);
#endif
          return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob);
        }
        }
      /* Please note that we now can be sure the brace expression
         is well-formed.  */
      rest_len = strlen (++rest) + 1;

      /* We have a brace expression.  BEGIN points to the opening {,
         NEXT points past the terminator of the first element, and END
         points past the final }.  We will accumulate result names from
         recursive runs for each brace alternative in the buffer using
         GLOB_APPEND.  */

      if (!(flags & GLOB_APPEND))
        {
          /* This call is to set a new vector, so clear out the
         vector so we can append to it.  */
          pglob->gl_pathc = 0;
          pglob->gl_pathv = NULL;
        }
      firstc = pglob->gl_pathc;

      p = begin + 1;
      while (1)
        {
          int result;

          /* Construct the new glob expression.  */
#ifdef HAVE_MEMPCPY
          mempcpy (mempcpy (alt_start, p, next - p), rest, rest_len);
#else
          memcpy (alt_start, p, next - p);
          memcpy (&alt_start[next - p], rest, rest_len);
#endif

          result = glob (onealt,
                 ((flags & ~(GLOB_NOCHECK|GLOB_NOMAGIC))
                  | GLOB_APPEND), errfunc, pglob);

          /* If we got an error, return it.  */
          if (result && result != GLOB_NOMATCH)
        {
#ifndef __GNUC__
          free (onealt);
#endif
          if (!(flags & GLOB_APPEND))
            globfree (pglob);
          return result;
        }

          if (*next == '}')
        /* We saw the last entry.  */
        break;

          p = next + 1;
          next = next_brace_sub (p);
          assert (next != NULL);
        }

#ifndef __GNUC__
      free (onealt);
#endif

      if (pglob->gl_pathc != firstc)
        /* We found some entries.  */
        return 0;
      else if (!(flags & (GLOB_NOCHECK|GLOB_NOMAGIC)))
        return GLOB_NOMATCH;
    }
    }

  /* Find the filename.  */
  filename = strrchr (pattern, '/');
#if defined __MSDOS__ || defined WINDOWS32
  /* The case of "d:pattern".  Since `:' is not allowed in
     file names, we can safely assume that wherever it
     happens in pattern, it signals the filename part.  This
     is so we could some day support patterns like "[a-z]:foo".  */
  if (filename == NULL)
    filename = strchr (pattern, ':');
#endif /* __MSDOS__ || WINDOWS32 */
  if (filename == NULL)
    {
      /* This can mean two things: a simple name or "~name".  The later
     case is nothing but a notation for a directory.  */
      if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && pattern[0] == '~')
    {
      dirname = pattern;
      dirlen = strlen (pattern);

      /* Set FILENAME to NULL as a special flag.  This is ugly but
         other solutions would require much more code.  We test for
         this special case below.  */
      filename = NULL;
    }
      else
    {
      filename = pattern;
#ifdef _AMIGA
      dirname = "";
#else
      dirname = ".";
#endif
      dirlen = 0;
    }
    }
  else if (filename == pattern)
    {
      /* "/pattern".  */
      dirname = "/";
      dirlen = 1;
      ++filename;
    }
  else
    {
      char *newp;
      dirlen = filename - pattern;
#if defined __MSDOS__ || defined WINDOWS32
      if (*filename == ':'
      || (filename > pattern + 1 && filename[-1] == ':'))
    {
      char *drive_spec;

      ++dirlen;
      drive_spec = (char *) __alloca (dirlen + 1);
#ifdef HAVE_MEMPCPY
      *((char *) mempcpy (drive_spec, pattern, dirlen)) = '\0';
#else
      memcpy (drive_spec, pattern, dirlen);
      drive_spec[dirlen] = '\0';
#endif
      /* For now, disallow wildcards in the drive spec, to
         prevent infinite recursion in glob.  */
      if (__glob_pattern_p (drive_spec, !(flags & GLOB_NOESCAPE)))
        return GLOB_NOMATCH;
      /* If this is "d:pattern", we need to copy `:' to DIRNAME
         as well.  If it's "d:/pattern", don't remove the slash
         from "d:/", since "d:" and "d:/" are not the same.*/
    }
#endif
      newp = (char *) __alloca (dirlen + 1);
#ifdef HAVE_MEMPCPY
      *((char *) mempcpy (newp, pattern, dirlen)) = '\0';
#else
      memcpy (newp, pattern, dirlen);
      newp[dirlen] = '\0';
#endif
      dirname = newp;
      ++filename;

      if (filename[0] == '\0'
#if defined __MSDOS__ || defined WINDOWS32
          && dirname[dirlen - 1] != ':'
      && (dirlen < 3 || dirname[dirlen - 2] != ':'
          || dirname[dirlen - 1] != '/')
#endif
      && dirlen > 1)
    /* "pattern/".  Expand "pattern", appending slashes.  */
    {
      int val = glob (dirname, flags | GLOB_MARK, errfunc, pglob);
      if (val == 0)
        pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK)
                   | (flags & GLOB_MARK));
      return val;
    }
    }

  if (!(flags & GLOB_APPEND))
    {
      pglob->gl_pathc = 0;
      pglob->gl_pathv = NULL;
    }

  oldcount = pglob->gl_pathc;

#ifndef VMS
  if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~')
    {
      if (dirname[1] == '\0' || dirname[1] == '/')
    {
      /* Look up home directory.  */
#ifdef VMS
/* This isn't obvious, RTLs of DECC and VAXC know about "HOME" */
          const char *home_dir = getenv ("SYS$LOGIN");
#else
          const char *home_dir = getenv ("HOME");
#endif
# ifdef _AMIGA
      if (home_dir == NULL || home_dir[0] == '\0')
        home_dir = "SYS:";
# else
#  ifdef WINDOWS32
      if (home_dir == NULL || home_dir[0] == '\0')
            home_dir = "c:/users/default"; /* poor default */
#  else
#   ifdef VMS
/* Again, this isn't obvious, if "HOME" isn't known "SYS$LOGIN" should be set */
      if (home_dir == NULL || home_dir[0] == '\0')
        home_dir = "SYS$DISK:[]";
#   else
      if (home_dir == NULL || home_dir[0] == '\0')
        {
          int success;
          char *name;
#   if defined HAVE_GETLOGIN_R || defined _LIBC
          size_t buflen = sysconf (_SC_LOGIN_NAME_MAX) + 1;

          if (buflen == 0)
        /* `sysconf' does not support _SC_LOGIN_NAME_MAX.  Try
           a moderate value.  */
        buflen = 20;
          name = (char *) __alloca (buflen);

          success = getlogin_r (name, buflen) >= 0;
#   else
          success = (name = getlogin ()) != NULL;
#   endif
          if (success)
        {
          struct passwd *p;
#   if defined HAVE_GETPWNAM_R || defined _LIBC
          size_t pwbuflen = sysconf (_SC_GETPW_R_SIZE_MAX);
          char *pwtmpbuf;
          struct passwd pwbuf;
          int save = errno;

          if (pwbuflen == -1)
            /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX.
               Try a moderate value.  */
            pwbuflen = 1024;
          pwtmpbuf = (char *) __alloca (pwbuflen);

          while (getpwnam_r (name, &pwbuf, pwtmpbuf, pwbuflen, &p)
             != 0)
            {
              if (errno != ERANGE)
            {
              p = NULL;
              break;
            }
              pwbuflen *= 2;
              pwtmpbuf = (char *) __alloca (pwbuflen);
              __set_errno (save);
            }
#   else
          p = getpwnam (name);
#   endif
          if (p != NULL)
            home_dir = p->pw_dir;
        }
        }
      if (home_dir == NULL || home_dir[0] == '\0')
        {
          if (flags & GLOB_TILDE_CHECK)
        return GLOB_NOMATCH;
          else
        home_dir = "~"; /* No luck.  */
        }
#   endif /* VMS */
#  endif /* WINDOWS32 */
# endif
      /* Now construct the full directory.  */
      if (dirname[1] == '\0')
        dirname = home_dir;
      else
        {
          char *newp;
          size_t home_len = strlen (home_dir);
          newp = (char *) __alloca (home_len + dirlen);
# ifdef HAVE_MEMPCPY
          mempcpy (mempcpy (newp, home_dir, home_len),
               &dirname[1], dirlen);
# else
          memcpy (newp, home_dir, home_len);
          memcpy (&newp[home_len], &dirname[1], dirlen);
# endif
          dirname = newp;
        }
    }
# if !defined _AMIGA && !defined WINDOWS32 && !defined VMS
      else
    {
      char *end_name = strchr (dirname, '/');
      const char *user_name;
      const char *home_dir;

      if (end_name == NULL)
        user_name = dirname + 1;
      else
        {
          char *newp;
          newp = (char *) __alloca (end_name - dirname);
# ifdef HAVE_MEMPCPY
          *((char *) mempcpy (newp, dirname + 1, end_name - dirname))
        = '\0';
# else
          memcpy (newp, dirname + 1, end_name - dirname);
          newp[end_name - dirname - 1] = '\0';
# endif
          user_name = newp;
        }

      /* Look up specific user's home directory.  */
      {
        struct passwd *p;
#  if defined HAVE_GETPWNAM_R || defined _LIBC
        size_t buflen = sysconf (_SC_GETPW_R_SIZE_MAX);
        char *pwtmpbuf;
        struct passwd pwbuf;
        int save = errno;

        if (buflen == -1)
          /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX.  Try a
         moderate value.  */
          buflen = 1024;
        pwtmpbuf = (char *) __alloca (buflen);

        while (getpwnam_r (user_name, &pwbuf, pwtmpbuf, buflen, &p) != 0)
          {
        if (errno != ERANGE)
          {
            p = NULL;
            break;
          }
        buflen *= 2;
        pwtmpbuf = __alloca (buflen);
        __set_errno (save);
          }
#  else
        p = getpwnam (user_name);
#  endif
        if (p != NULL)
          home_dir = p->pw_dir;
        else
          home_dir = NULL;
      }
      /* If we found a home directory use this.  */
      if (home_dir != NULL)
        {
          char *newp;
          size_t home_len = strlen (home_dir);
          size_t rest_len = end_name == NULL ? 0 : strlen (end_name);
          newp = (char *) __alloca (home_len + rest_len + 1);
#  ifdef HAVE_MEMPCPY
          *((char *) mempcpy (mempcpy (newp, home_dir, home_len),
                  end_name, rest_len)) = '\0';
#  else
          memcpy (newp, home_dir, home_len);
          memcpy (&newp[home_len], end_name, rest_len);
          newp[home_len + rest_len] = '\0';
#  endif
          dirname = newp;
        }
      else
        if (flags & GLOB_TILDE_CHECK)
          /* We have to regard it as an error if we cannot find the
         home directory.  */
          return GLOB_NOMATCH;
    }
# endif /* Not Amiga && not WINDOWS32 && not VMS.  */
    }
#endif  /* Not VMS.  */

  /* Now test whether we looked for "~" or "~NAME".  In this case we
     can give the answer now.  */
  if (filename == NULL)
    {
      struct stat st;

      /* Return the directory if we don't check for error or if it exists.  */
      if ((flags & GLOB_NOCHECK)
      || (((flags & GLOB_ALTDIRFUNC)
           ? (*pglob->gl_stat) (dirname, &st)
           : __stat (dirname, &st)) == 0
          && S_ISDIR (st.st_mode)))
    {
      pglob->gl_pathv
        = (char **) realloc (pglob->gl_pathv,
                 (pglob->gl_pathc +
                  ((flags & GLOB_DOOFFS) ?
                   pglob->gl_offs : 0) +
                  1 + 1) *
                 sizeof (char *));
      if (pglob->gl_pathv == NULL)
        return GLOB_NOSPACE;

      if (flags & GLOB_DOOFFS)
        while (pglob->gl_pathc < pglob->gl_offs)
          pglob->gl_pathv[pglob->gl_pathc++] = NULL;

#if defined HAVE_STRDUP || defined _LIBC
      pglob->gl_pathv[pglob->gl_pathc] = strdup (dirname);
#else
      {
        size_t len = strlen (dirname) + 1;
        char *dircopy = malloc (len);
        if (dircopy != NULL)
          pglob->gl_pathv[pglob->gl_pathc] = memcpy (dircopy, dirname,
                             len);
      }
#endif
      if (pglob->gl_pathv[pglob->gl_pathc] == NULL)
        {
          free (pglob->gl_pathv);
          return GLOB_NOSPACE;
        }
      pglob->gl_pathv[++pglob->gl_pathc] = NULL;
      pglob->gl_flags = flags;

      return 0;
    }

      /* Not found.  */
      return GLOB_NOMATCH;
    }

  if (__glob_pattern_p (dirname, !(flags & GLOB_NOESCAPE)))
    {
      /* The directory name contains metacharacters, so we
     have to glob for the directory, and then glob for
     the pattern in each directory found.  */
      glob_t dirs;
      register int i;

      status = glob (dirname,
             ((flags & (GLOB_ERR | GLOB_NOCHECK | GLOB_NOESCAPE))
              | GLOB_NOSORT | GLOB_ONLYDIR),
             errfunc, &dirs);
      if (status != 0)
    return status;

      /* We have successfully globbed the preceding directory name.
     For each name we found, call glob_in_dir on it and FILENAME,
     appending the results to PGLOB.  */
      for (i = 0; i < dirs.gl_pathc; ++i)
    {
      int old_pathc;

#ifdef  SHELL
      {
        /* Make globbing interruptible in the bash shell. */
        extern int interrupt_state;

        if (interrupt_state)
          {
        globfree (&dirs);
        globfree (&files);
        return GLOB_ABORTED;
          }
      }
#endif /* SHELL.  */

      old_pathc = pglob->gl_pathc;
      status = glob_in_dir (filename, dirs.gl_pathv[i],
                ((flags | GLOB_APPEND)
                 & ~(GLOB_NOCHECK | GLOB_ERR)),
                errfunc, pglob);
      if (status == GLOB_NOMATCH)
        /* No matches in this directory.  Try the next.  */
        continue;

      if (status != 0)
        {
          globfree (&dirs);
          globfree (pglob);
          return status;
        }

      /* Stick the directory on the front of each name.  */
      if (prefix_array (dirs.gl_pathv[i],
                &pglob->gl_pathv[old_pathc],
                pglob->gl_pathc - old_pathc))
        {
          globfree (&dirs);
          globfree (pglob);
          return GLOB_NOSPACE;
        }
    }

      flags |= GLOB_MAGCHAR;

      /* We have ignored the GLOB_NOCHECK flag in the `glob_in_dir' calls.
     But if we have not found any matching entry and thie GLOB_NOCHECK
     flag was set we must return the list consisting of the disrectory
     names followed by the filename.  */
      if (pglob->gl_pathc == oldcount)
    {
      /* No matches.  */
      if (flags & GLOB_NOCHECK)
        {
          size_t filename_len = strlen (filename) + 1;
          char **new_pathv;
          struct stat st;

          /* This is an pessimistic guess about the size.  */
          pglob->gl_pathv
        = (char **) realloc (pglob->gl_pathv,
                     (pglob->gl_pathc +
                      ((flags & GLOB_DOOFFS) ?
                       pglob->gl_offs : 0) +
                      dirs.gl_pathc + 1) *
                     sizeof (char *));
          if (pglob->gl_pathv == NULL)
        {
          globfree (&dirs);
          return GLOB_NOSPACE;
        }

          if (flags & GLOB_DOOFFS)
        while (pglob->gl_pathc < pglob->gl_offs)
          pglob->gl_pathv[pglob->gl_pathc++] = NULL;

          for (i = 0; i < dirs.gl_pathc; ++i)
        {
          const char *dir = dirs.gl_pathv[i];
          size_t dir_len = strlen (dir);

          /* First check whether this really is a directory.  */
          if (((flags & GLOB_ALTDIRFUNC)
               ? (*pglob->gl_stat) (dir, &st) : __stat (dir, &st)) != 0
              || !S_ISDIR (st.st_mode))
            /* No directory, ignore this entry.  */
            continue;

          pglob->gl_pathv[pglob->gl_pathc] = malloc (dir_len + 1
                                 + filename_len);
          if (pglob->gl_pathv[pglob->gl_pathc] == NULL)
            {
              globfree (&dirs);
              globfree (pglob);
              return GLOB_NOSPACE;
            }

#ifdef HAVE_MEMPCPY
          mempcpy (mempcpy (mempcpy (pglob->gl_pathv[pglob->gl_pathc],
                         dir, dir_len),
                    "/", 1),
               filename, filename_len);
#else
          memcpy (pglob->gl_pathv[pglob->gl_pathc], dir, dir_len);
          pglob->gl_pathv[pglob->gl_pathc][dir_len] = '/';
          memcpy (&pglob->gl_pathv[pglob->gl_pathc][dir_len + 1],
              filename, filename_len);
#endif
          ++pglob->gl_pathc;
        }

          pglob->gl_pathv[pglob->gl_pathc] = NULL;
          pglob->gl_flags = flags;

          /* Now we know how large the gl_pathv vector must be.  */
          new_pathv = (char **) realloc (pglob->gl_pathv,
                         ((pglob->gl_pathc + 1)
                          * sizeof (char *)));
          if (new_pathv != NULL)
        pglob->gl_pathv = new_pathv;
        }
      else
        return GLOB_NOMATCH;
    }

      globfree (&dirs);
    }
  else
    {
      status = glob_in_dir (filename, dirname, flags, errfunc, pglob);
      if (status != 0)
    return status;

      if (dirlen > 0)
    {
      /* Stick the directory on the front of each name.  */
      int ignore = oldcount;

      if ((flags & GLOB_DOOFFS) && ignore < pglob->gl_offs)
        ignore = pglob->gl_offs;

      if (prefix_array (dirname,
                &pglob->gl_pathv[ignore],
                pglob->gl_pathc - ignore))
        {
          globfree (pglob);
          return GLOB_NOSPACE;
        }
    }
    }

  if (flags & GLOB_MARK)
    {
      /* Append slashes to directory names.  */
      int i;
      struct stat st;
      for (i = oldcount; i < pglob->gl_pathc; ++i)
    if (((flags & GLOB_ALTDIRFUNC)
         ? (*pglob->gl_stat) (pglob->gl_pathv[i], &st)
         : __stat (pglob->gl_pathv[i], &st)) == 0
        && S_ISDIR (st.st_mode))
      {
        size_t len = strlen (pglob->gl_pathv[i]) + 2;
        char *new = realloc (pglob->gl_pathv[i], len);
        if (new == NULL)
          {
        globfree (pglob);
        return GLOB_NOSPACE;
          }
        strcpy (&new[len - 2], "/");
        pglob->gl_pathv[i] = new;
      }
    }

  if (!(flags & GLOB_NOSORT))
    {
      /* Sort the vector.  */
      int non_sort = oldcount;

      if ((flags & GLOB_DOOFFS) && pglob->gl_offs > oldcount)
    non_sort = pglob->gl_offs;

      qsort ((__ptr_t) &pglob->gl_pathv[non_sort],
         pglob->gl_pathc - non_sort,
         sizeof (char *), collated_compare);
    }

  return 0;
}


/* Free storage allocated in PGLOB by a previous `glob' call.  */
void
globfree (pglob)
     register glob_t *pglob;
{
  if (pglob->gl_pathv != NULL)
    {
      register int i;
      for (i = 0; i < pglob->gl_pathc; ++i)
    if (pglob->gl_pathv[i] != NULL)
      free ((__ptr_t) pglob->gl_pathv[i]);
      free ((__ptr_t) pglob->gl_pathv);
    }
}


/* Do a collated comparison of A and B.  */
static int
collated_compare (a, b)
     const __ptr_t a;
     const __ptr_t b;
{
  const char *const s1 = *(const char *const * const) a;
  const char *const s2 = *(const char *const * const) b;

  if (s1 == s2)
    return 0;
  if (s1 == NULL)
    return 1;
  if (s2 == NULL)
    return -1;
  return strcoll (s1, s2);
}


/* Prepend DIRNAME to each of N members of ARRAY, replacing ARRAY's
   elements in place.  Return nonzero if out of memory, zero if successful.
   A slash is inserted between DIRNAME and each elt of ARRAY,
   unless DIRNAME is just "/".  Each old element of ARRAY is freed.  */
static int
prefix_array (dirname, array, n)
     const char *dirname;
     char **array;
     size_t n;
{
  register size_t i;
  size_t dirlen = strlen (dirname);
#if defined __MSDOS__ || defined WINDOWS32
  int sep_char = '/';
# define DIRSEP_CHAR sep_char
#else
# define DIRSEP_CHAR '/'
#endif

  if (dirlen == 1 && dirname[0] == '/')
    /* DIRNAME is just "/", so normal prepending would get us "//foo".
       We want "/foo" instead, so don't prepend any chars from DIRNAME.  */
    dirlen = 0;
#if defined __MSDOS__ || defined WINDOWS32
  else if (dirlen > 1)
    {
      if (dirname[dirlen - 1] == '/' && dirname[dirlen - 2] == ':')
    /* DIRNAME is "d:/".  Don't prepend the slash from DIRNAME.  */
    --dirlen;
      else if (dirname[dirlen - 1] == ':')
    {
      /* DIRNAME is "d:".  Use `:' instead of `/'.  */
      --dirlen;
      sep_char = ':';
    }
    }
#endif

  for (i = 0; i < n; ++i)
    {
      size_t eltlen = strlen (array[i]) + 1;
      char *new = (char *) malloc (dirlen + 1 + eltlen);
      if (new == NULL)
    {
      while (i > 0)
        free ((__ptr_t) array[--i]);
      return 1;
    }

#ifdef HAVE_MEMPCPY
      {
    char *endp = (char *) mempcpy (new, dirname, dirlen);
    *endp++ = DIRSEP_CHAR;
    mempcpy (endp, array[i], eltlen);
      }
#else
      memcpy (new, dirname, dirlen);
      new[dirlen] = DIRSEP_CHAR;
      memcpy (&new[dirlen + 1], array[i], eltlen);
#endif
      free ((__ptr_t) array[i]);
      array[i] = new;
    }

  return 0;
}


/* We must not compile this function twice.  */
#if !defined _LIBC || !defined NO_GLOB_PATTERN_P
/* Return nonzero if PATTERN contains any metacharacters.
   Metacharacters can be quoted with backslashes if QUOTE is nonzero.  */
int
__glob_pattern_p (pattern, quote)
     const char *pattern;
     int quote;
{
  register const char *p;
  int open = 0;

  for (p = pattern; *p != '\0'; ++p)
    switch (*p)
      {
      case '?':
      case '*':
    return 1;

      case '\\':
    if (quote && p[1] != '\0')
      ++p;
    break;

      case '[':
    open = 1;
    break;

      case ']':
    if (open)
      return 1;
    break;
      }

  return 0;
}
# ifdef _LIBC
weak_alias (__glob_pattern_p, glob_pattern_p)
# endif
#endif


/* Like `glob', but PATTERN is a final pathname component,
   and matches are searched for in DIRECTORY.
   The GLOB_NOSORT bit in FLAGS is ignored.  No sorting is ever done.
   The GLOB_APPEND flag is assumed to be set (always appends).  */
static int
glob_in_dir (pattern, directory, flags, errfunc, pglob)
     const char *pattern;
     const char *directory;
     int flags;
     int (*errfunc) __P ((const char *, int));
     glob_t *pglob;
{
  __ptr_t stream = NULL;

  struct globlink
    {
      struct globlink *next;
      char *name;
    };
  struct globlink *names = NULL;
  size_t nfound;
  int meta;
  int save;

#ifdef VMS
  if (*directory == 0)
    directory = "[]";
#endif
  meta = __glob_pattern_p (pattern, !(flags & GLOB_NOESCAPE));
  if (meta == 0)
    {
      if (flags & (GLOB_NOCHECK|GLOB_NOMAGIC))
    /* We need not do any tests.  The PATTERN contains no meta
       characters and we must not return an error therefore the
       result will always contain exactly one name.  */
    flags |= GLOB_NOCHECK;
      else
    {
      /* Since we use the normal file functions we can also use stat()
         to verify the file is there.  */
      struct stat st;
      size_t patlen = strlen (pattern);
      size_t dirlen = strlen (directory);
      char *fullname = (char *) __alloca (dirlen + 1 + patlen + 1);

# ifdef HAVE_MEMPCPY
      mempcpy (mempcpy (mempcpy (fullname, directory, dirlen),
                "/", 1),
           pattern, patlen + 1);
# else
      memcpy (fullname, directory, dirlen);
      fullname[dirlen] = '/';
      memcpy (&fullname[dirlen + 1], pattern, patlen + 1);
# endif
      if (((flags & GLOB_ALTDIRFUNC)
           ? (*pglob->gl_stat) (fullname, &st)
           : __stat (fullname, &st)) == 0)
        /* We found this file to be existing.  Now tell the rest
           of the function to copy this name into the result.  */
        flags |= GLOB_NOCHECK;
    }

      nfound = 0;
    }
  else
    {
      if (pattern[0] == '\0')
    {
      /* This is a special case for matching directories like in
         "*a/".  */
      names = (struct globlink *) __alloca (sizeof (struct globlink));
      names->name = (char *) malloc (1);
      if (names->name == NULL)
        goto memory_error;
      names->name[0] = '\0';
      names->next = NULL;
      nfound = 1;
      meta = 0;
    }
      else
    {
      stream = ((flags & GLOB_ALTDIRFUNC)
            ? (*pglob->gl_opendir) (directory)
            : (__ptr_t) opendir (directory));
      if (stream == NULL)
        {
          if (errno != ENOTDIR
          && ((errfunc != NULL && (*errfunc) (directory, errno))
              || (flags & GLOB_ERR)))
        return GLOB_ABORTED;
          nfound = 0;
          meta = 0;
        }
      else
        {
          int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0)
                   | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)
#if defined HAVE_CASE_INSENSITIVE_FS
                   | FNM_CASEFOLD
#endif
                   );
          nfound = 0;
          flags |= GLOB_MAGCHAR;

          while (1)
        {
          const char *name;
          size_t len;
          struct dirent *d = ((flags & GLOB_ALTDIRFUNC)
                      ? (*pglob->gl_readdir) (stream)
                      : readdir ((DIR *) stream));
          if (d == NULL)
            break;
          if (! REAL_DIR_ENTRY (d))
            continue;

#ifdef HAVE_D_TYPE
          /* If we shall match only directories use the information
             provided by the dirent call if possible.  */
          if ((flags & GLOB_ONLYDIR)
              && d->d_type != DT_UNKNOWN && d->d_type != DT_DIR)
            continue;
#endif

          name = d->d_name;

          if (fnmatch (pattern, name, fnm_flags) == 0)
            {
              struct globlink *new = (struct globlink *)
            __alloca (sizeof (struct globlink));
              len = NAMLEN (d);
              new->name = (char *) malloc (len + 1);
              if (new->name == NULL)
            goto memory_error;
#ifdef HAVE_MEMPCPY
              *((char *) mempcpy ((__ptr_t) new->name, name, len))
            = '\0';
#else
              memcpy ((__ptr_t) new->name, name, len);
              new->name[len] = '\0';
#endif
              new->next = names;
              names = new;
              ++nfound;
            }
        }
        }
    }
    }

  if (nfound == 0 && (flags & GLOB_NOCHECK))
    {
      size_t len = strlen (pattern);
      nfound = 1;
      names = (struct globlink *) __alloca (sizeof (struct globlink));
      names->next = NULL;
      names->name = (char *) malloc (len + 1);
      if (names->name == NULL)
    goto memory_error;
#ifdef HAVE_MEMPCPY
      *((char *) mempcpy (names->name, pattern, len)) = '\0';
#else
      memcpy (names->name, pattern, len);
      names->name[len] = '\0';
#endif
    }

  if (nfound != 0)
    {
      pglob->gl_pathv
    = (char **) realloc (pglob->gl_pathv,
                 (pglob->gl_pathc +
                  ((flags & GLOB_DOOFFS) ? pglob->gl_offs : 0) +
                  nfound + 1) *
                 sizeof (char *));
      if (pglob->gl_pathv == NULL)
    goto memory_error;

      if (flags & GLOB_DOOFFS)
    while (pglob->gl_pathc < pglob->gl_offs)
      pglob->gl_pathv[pglob->gl_pathc++] = NULL;

      for (; names != NULL; names = names->next)
    pglob->gl_pathv[pglob->gl_pathc++] = names->name;
      pglob->gl_pathv[pglob->gl_pathc] = NULL;

      pglob->gl_flags = flags;
    }

  save = errno;
  if (stream != NULL)
    {
      if (flags & GLOB_ALTDIRFUNC)
    (*pglob->gl_closedir) (stream);
      else
    closedir ((DIR *) stream);
    }
  __set_errno (save);

  return nfound == 0 ? GLOB_NOMATCH : 0;

 memory_error:
  {
    int save = errno;
    if (flags & GLOB_ALTDIRFUNC)
      (*pglob->gl_closedir) (stream);
    else
      closedir ((DIR *) stream);
    __set_errno (save);
  }
  while (names != NULL)
    {
      if (names->name != NULL)
    free ((__ptr_t) names->name);
      names = names->next;
    }
  return GLOB_NOSPACE;
}

#endif  /* Not ELIDE_CODE.  */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/make-3.82/make-3.82.checksums

File: ../git/live-bootstrap/sysa/make-3.82/make-3.82.checksums used in: Step 546
aa57178c15d44020d30cc893a5cf75278a38a801361f0373cf17600cf534c73c  /usr/bin/make

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/sources

File: ../git/live-bootstrap/sysa/patch-2.5.9/sources used in: Step 548
https://mirrors.kernel.org/gnu/patch/patch-2.5.9.tar.gz ecb5c6469d732bcf01d6ec1afe9e64f1668caba5bfdb103c28d7f537ba3cdb8a

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/mk/main.mk

File: ../git/live-bootstrap/sysa/patch-2.5.9/mk/main.mk used in: Step 554
# SPDX-FileCopyrightText: 2022 Andrius Å tikonas <andrius@stikonas.eu>
#
# SPDX-License-Identifier: GPL-3.0-or-later

CC      = tcc

CFLAGS  = -I .
CPPFLAGS = -DHAVE_DECL_GETENV -DHAVE_DECL_MALLOC -DHAVE_DIRENT_H -DHAVE_LIMITS_H -DHAVE_GETEUID -DHAVE_MKTEMP -DPACKAGE_BUGREPORT= -Ded_PROGRAM=\"/nullop\" -Dmbstate_t=void -DRETSIGTYPE=int -DHAVE_MKDIR -DHAVE_RMDIR -DHAVE_FCNTL_H -DPACKAGE_NAME=\"patch\" -DPACKAGE_VERSION=\"2.5.9\" -DHAVE_MALLOC -DHAVE_REALLOC -DSTDC_HEADERS -DHAVE_STRING_H -DHAVE_STDLIB_H
LDFLAGS = -static

.PHONY: all
all: patch

patch: error.o getopt.o getopt1.o addext.o argmatch.o backupfile.o basename.o dirname.o inp.o maketime.o partime.o patch.o pch.o quote.o quotearg.o quotesys.o util.o version.o xmalloc.o
    $(CC) $^ $(LDFLAGS) -o $@

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/error.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/error.c used in: Step 558
/* Error handler for noninteractive utilities
   Copyright (C) 1990-1998, 2000, 2001, 2002 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.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>

#ifdef _LIBC
# include <libintl.h>
#else
# include "gettext.h"
#endif

#ifdef _LIBC
# include <wchar.h>
# define mbsrtowcs __mbsrtowcs
#endif

#if HAVE_VPRINTF || HAVE_DOPRNT || _LIBC
# if __STDC__
#  include <stdarg.h>
#  define VA_START(args, lastarg) va_start(args, lastarg)
# else
#  include <varargs.h>
#  define VA_START(args, lastarg) va_start(args)
# endif
#else
# define va_alist a1, a2, a3, a4, a5, a6, a7, a8
# define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
#endif

#if STDC_HEADERS || _LIBC
# include <stdlib.h>
# include <string.h>
#else
void exit ();
#endif

#include "error.h"

#if !_LIBC
# include "unlocked-io.h"
#endif

#ifndef _
# define _(String) String
#endif

/* If NULL, error will flush stdout, then print on stderr the program
   name, a colon and a space.  Otherwise, error will call this
   function without parameters instead.  */
void (*error_print_progname) (
#if __STDC__ - 0
                  void
#endif
                  );

/* This variable is incremented each time `error' is called.  */
unsigned int error_message_count;

#ifdef _LIBC
/* In the GNU C library, there is a predefined variable for this.  */

# define program_name program_invocation_name
# include <errno.h>
# include <libio/libioP.h>

/* In GNU libc we want do not want to use the common name `error' directly.
   Instead make it a weak alias.  */
extern void __error (int status, int errnum, const char *message, ...)
     __attribute__ ((__format__ (__printf__, 3, 4)));
extern void __error_at_line (int status, int errnum, const char *file_name,
                 unsigned int line_number, const char *message,
                 ...)
     __attribute__ ((__format__ (__printf__, 5, 6)));;
# define error __error
# define error_at_line __error_at_line

# ifdef USE_IN_LIBIO
#  include <libio/iolibio.h>
#  define fflush(s) INTUSE(_IO_fflush) (s)
#  undef putc
#  define putc(c, fp) INTUSE(_IO_putc) (c, fp)
# endif

#else /* not _LIBC */

# if !HAVE_DECL_STRERROR_R && STRERROR_R_CHAR_P
#  ifndef HAVE_DECL_STRERROR_R
"this configure-time declaration test was not run"
#  endif
char *strerror_r ();
# endif

/* The calling program should define program_name and set it to the
   name of the executing program.  */
extern char *program_name;

# if HAVE_STRERROR_R || defined strerror_r
#  define __strerror_r strerror_r
# else
#  if HAVE_STRERROR
#   ifndef HAVE_DECL_STRERROR
"this configure-time declaration test was not run"
#   endif
#   if !HAVE_DECL_STRERROR
char *strerror ();
#   endif
#  else
static char *
private_strerror (int errnum)
{
  extern char *sys_errlist[];
  extern int sys_nerr;

  if (errnum > 0 && errnum <= sys_nerr)
    return _(sys_errlist[errnum]);
  return _("Unknown system error");
}
#   define strerror private_strerror
#  endif /* HAVE_STRERROR */
# endif /* HAVE_STRERROR_R || defined strerror_r */
#endif  /* not _LIBC */

static void
print_errno_message (int errnum)
{
  char const *s;

#if defined HAVE_STRERROR_R || _LIBC
  char errbuf[1024];
# if STRERROR_R_CHAR_P || _LIBC
  s = __strerror_r (errnum, errbuf, sizeof errbuf);
# else
  if (__strerror_r (errnum, errbuf, sizeof errbuf) == 0)
    s = errbuf;
  else
    s = 0;
# endif
#else
  s = strerror (errnum);
#endif

#if !_LIBC
  if (! s)
    s = _("Unknown system error");
#endif

#if _LIBC && USE_IN_LIBIO
  if (_IO_fwide (stderr, 0) > 0)
    {
      __fwprintf (stderr, L": %s", s);
      return;
    }
#endif

  fprintf (stderr, ": %s", s);
}

#ifdef VA_START
static void
error_tail (int status, int errnum, const char *message, va_list args)
{
# if HAVE_VPRINTF || _LIBC
#  if _LIBC && USE_IN_LIBIO
  if (_IO_fwide (stderr, 0) > 0)
    {
#   define ALLOCA_LIMIT 2000
      size_t len = strlen (message) + 1;
      wchar_t *wmessage = NULL;
      mbstate_t st;
      size_t res;
      const char *tmp;

      do
    {
      if (len < ALLOCA_LIMIT)
        wmessage = (wchar_t *) alloca (len * sizeof (wchar_t));
      else
        {
          if (wmessage != NULL && len / 2 < ALLOCA_LIMIT)
        wmessage = NULL;

          wmessage = (wchar_t *) realloc (wmessage,
                          len * sizeof (wchar_t));

          if (wmessage == NULL)
        {
          fputws_unlocked (L"out of memory\n", stderr);
          return;
        }
        }

      memset (&st, '\0', sizeof (st));
      tmp =message;
    }
      while ((res = mbsrtowcs (wmessage, &tmp, len, &st)) == len);

      if (res == (size_t) -1)
    /* The string cannot be converted.  */
    wmessage = (wchar_t *) L"???";

      __vfwprintf (stderr, wmessage, args);
    }
  else
#  endif
    vfprintf (stderr, message, args);
# else
  _doprnt (message, args, stderr);
# endif
  va_end (args);

  ++error_message_count;
  if (errnum)
    print_errno_message (errnum);
# if _LIBC && USE_IN_LIBIO
  if (_IO_fwide (stderr, 0) > 0)
    putwc (L'\n', stderr);
  else
# endif
    putc ('\n', stderr);
  fflush (stderr);
  if (status)
    exit (status);
}
#endif


/* Print the program name and error message MESSAGE, which is a printf-style
   format string with optional args.
   If ERRNUM is nonzero, print its corresponding system error message.
   Exit with status STATUS if it is nonzero.  */
/* VARARGS */
void
#if defined VA_START && __STDC__
error (int status, int errnum, const char *message, ...)
#else
error (status, errnum, message, va_alist)
     int status;
     int errnum;
     char *message;
     va_dcl
#endif
{
#ifdef VA_START
  va_list args;
#endif

  fflush (stdout);
#ifdef _LIBC
# ifdef USE_IN_LIBIO
  _IO_flockfile (stderr);
# else
  __flockfile (stderr);
# endif
#endif
  if (error_print_progname)
    (*error_print_progname) ();
  else
    {
#if _LIBC && USE_IN_LIBIO
      if (_IO_fwide (stderr, 0) > 0)
    __fwprintf (stderr, L"%s: ", program_name);
      else
#endif
    fprintf (stderr, "%s: ", program_name);
    }

#ifdef VA_START
  VA_START (args, message);
  error_tail (status, errnum, message, args);
#else
  fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);

  ++error_message_count;
  if (errnum)
    print_errno_message (errnum);
  putc ('\n', stderr);
  fflush (stderr);
  if (status)
    exit (status);
#endif

#ifdef _LIBC
# ifdef USE_IN_LIBIO
  _IO_funlockfile (stderr);
# else
  __funlockfile (stderr);
# endif
#endif
}


/* Sometimes we want to have at most one error per line.  This
   variable controls whether this mode is selected or not.  */
int error_one_per_line;

void
#if defined VA_START && __STDC__
error_at_line (int status, int errnum, const char *file_name,
           unsigned int line_number, const char *message, ...)
#else
error_at_line (status, errnum, file_name, line_number, message, va_alist)
     int status;
     int errnum;
     const char *file_name;
     unsigned int line_number;
     char *message;
     va_dcl
#endif
{
#ifdef VA_START
  va_list args;
#endif

  if (error_one_per_line)
    {
      static const char *old_file_name;
      static unsigned int old_line_number;

      if (old_line_number == line_number
      && (file_name == old_file_name
          || strcmp (old_file_name, file_name) == 0))
    /* Simply return and print nothing.  */
    return;

      old_file_name = file_name;
      old_line_number = line_number;
    }

  fflush (stdout);
#ifdef _LIBC
# ifdef USE_IN_LIBIO
  _IO_flockfile (stderr);
# else
  __flockfile (stderr);
# endif
#endif
  if (error_print_progname)
    (*error_print_progname) ();
  else
    {
#if _LIBC && USE_IN_LIBIO
      if (_IO_fwide (stderr, 0) > 0)
    __fwprintf (stderr, L"%s: ", program_name);
      else
#endif
    fprintf (stderr, "%s:", program_name);
    }

  if (file_name != NULL)
    {
#if _LIBC && USE_IN_LIBIO
      if (_IO_fwide (stderr, 0) > 0)
    __fwprintf (stderr, L"%s:%d: ", file_name, line_number);
      else
#endif
    fprintf (stderr, "%s:%d: ", file_name, line_number);
    }

#ifdef VA_START
  VA_START (args, message);
  error_tail (status, errnum, message, args);
#else
  fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);

  ++error_message_count;
  if (errnum)
    print_errno_message (errnum);
  putc ('\n', stderr);
  fflush (stderr);
  if (status)
    exit (status);
#endif

#ifdef _LIBC
# ifdef USE_IN_LIBIO
  _IO_funlockfile (stderr);
# else
  __funlockfile (stderr);
# endif
#endif
}

#ifdef _LIBC
/* Make the weak alias.  */
# undef error
# undef error_at_line
weak_alias (__error, error)
weak_alias (__error_at_line, error_at_line)
#endif

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/getopt.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/getopt.c used in: Step 559
/* Getopt for GNU.
   NOTE: getopt is now part of the C library, so if you don't know what
   "Keep this file name-space clean" means, talk to drepper@gnu.org
   before changing it!
   Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001,2002
    Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */


/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
   Ditto for AIX 3.2 and <stdlib.h>.  */
#ifndef _NO_PROTO
# define _NO_PROTO
#endif

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#if !defined __STDC__ || !__STDC__
/* This is a separate conditional since some stdc systems
   reject `defined (const)'.  */
# ifndef const
#  define const
# endif
#endif

#include <stdio.h>

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#define GETOPT_INTERFACE_VERSION 2
#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
# include <gnu-versions.h>
# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
#  define ELIDE_CODE
# endif
#endif

#ifndef ELIDE_CODE


/* This needs to come after some library #include
   to get __GNU_LIBRARY__ defined.  */
#ifdef  __GNU_LIBRARY__
/* Don't include stdlib.h for non-GNU C libraries because some of them
   contain conflicting prototypes for getopt.  */
# include <stdlib.h>
# include <unistd.h>
#endif  /* GNU C library.  */

#ifdef VMS
# include <unixlib.h>
# if HAVE_STRING_H - 0
#  include <string.h>
# endif
#endif

#ifdef _LIBC
# include <libintl.h>
#else
/* This is for other GNU distributions with internationalized messages.  */
# include "gettext.h"
#endif
#define _(msgid) gettext (msgid)

#if defined _LIBC && defined USE_IN_LIBIO
# include <wchar.h>
#endif

#ifndef attribute_hidden
# define attribute_hidden
#endif

/* This version of `getopt' appears to the caller like standard Unix `getopt'
   but it behaves differently for the user, since it allows the user
   to intersperse the options with the other arguments.

   As `getopt' works, it permutes the elements of ARGV so that,
   when it is done, all the options precede everything else.  Thus
   all application programs are extended to handle flexible argument order.

   Setting the environment variable POSIXLY_CORRECT disables permutation.
   Then the behavior is completely standard.

   GNU application programs can use a third alternative mode in which
   they can distinguish the relative order of options and other arguments.  */

#include "getopt.h"

/* For communication from `getopt' to the caller.
   When `getopt' finds an option that takes an argument,
   the argument value is returned here.
   Also, when `ordering' is RETURN_IN_ORDER,
   each non-option ARGV-element is returned here.  */

char *optarg;

/* Index in ARGV of the next element to be scanned.
   This is used for communication to and from the caller
   and for communication between successive calls to `getopt'.

   On entry to `getopt', zero means this is the first call; initialize.

   When `getopt' returns -1, this is the index of the first of the
   non-option elements that the caller should itself scan.

   Otherwise, `optind' communicates from one call to the next
   how much of ARGV has been scanned so far.  */

/* 1003.2 says this must be 1 before any call.  */
int optind = 1;

/* Formerly, initialization of getopt depended on optind==0, which
   causes problems with re-calling getopt as programs generally don't
   know that. */

int __getopt_initialized attribute_hidden;

/* The next char to be scanned in the option-element
   in which the last option character we returned was found.
   This allows us to pick up the scan where we left off.

   If this is zero, or a null string, it means resume the scan
   by advancing to the next ARGV-element.  */

static char *nextchar;

/* Callers store zero here to inhibit the error message
   for unrecognized options.  */

int opterr = 1;

/* Set to an option character which was unrecognized.
   This must be initialized on some systems to avoid linking in the
   system's own getopt implementation.  */

int optopt = '?';

/* Describe how to deal with options that follow non-option ARGV-elements.

   If the caller did not specify anything,
   the default is REQUIRE_ORDER if the environment variable
   POSIXLY_CORRECT is defined, PERMUTE otherwise.

   REQUIRE_ORDER means don't recognize them as options;
   stop option processing when the first non-option is seen.
   This is what Unix does.
   This mode of operation is selected by either setting the environment
   variable POSIXLY_CORRECT, or using `+' as the first character
   of the list of option characters.

   PERMUTE is the default.  We permute the contents of ARGV as we scan,
   so that eventually all the non-options are at the end.  This allows options
   to be given in any order, even with programs that were not written to
   expect this.

   RETURN_IN_ORDER is an option available to programs that were written
   to expect options and other ARGV-elements in any order and that care about
   the ordering of the two.  We describe each non-option ARGV-element
   as if it were the argument of an option with character code 1.
   Using `-' as the first character of the list of option characters
   selects this mode of operation.

   The special argument `--' forces an end of option-scanning regardless
   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */

static enum
{
  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
} ordering;

/* Value of POSIXLY_CORRECT environment variable.  */
static char *posixly_correct;


#ifdef  __GNU_LIBRARY__
/* We want to avoid inclusion of string.h with non-GNU libraries
   because there are many ways it can cause trouble.
   On some systems, it contains special magic macros that don't work
   in GCC.  */
# include <string.h>
# define my_index   strchr
#else

# if HAVE_STRING_H
#  include <string.h>
# else
#  include <strings.h>
# endif

/* Avoid depending on library functions or files
   whose names are inconsistent.  */

#ifndef getenv
extern char *getenv ();
#endif

static char *
my_index (str, chr)
     const char *str;
     int chr;
{
  while (*str)
    {
      if (*str == chr)
    return (char *) str;
      str++;
    }
  return 0;
}

/* If using GCC, we can safely declare strlen this way.
   If not using GCC, it is ok not to declare it.  */
#ifdef __GNUC__
/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
   That was relevant to code that was here before.  */
# if (!defined __STDC__ || !__STDC__) && !defined strlen
/* gcc with -traditional declares the built-in strlen to return int,
   and has done so at least since version 2.4.5. -- rms.  */
extern int strlen (const char *);
# endif /* not __STDC__ */
#endif /* __GNUC__ */

#endif /* not __GNU_LIBRARY__ */


/* Handle permutation of arguments.  */

/* Describe the part of ARGV that contains non-options that have
   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
   `last_nonopt' is the index after the last of them.  */

static int first_nonopt;
static int last_nonopt;

#ifdef _LIBC
/* Stored original parameters.
   XXX This is no good solution.  We should rather copy the args so
   that we can compare them later.  But we must not use malloc(3).  */
extern int __libc_argc;
extern char **__libc_argv;

/* Bash 2.0 gives us an environment variable containing flags
   indicating ARGV elements that should not be considered arguments.  */

# ifdef USE_NONOPTION_FLAGS
/* Defined in getopt_init.c  */
extern char *__getopt_nonoption_flags;

static int nonoption_flags_max_len;
static int nonoption_flags_len;
# endif

# ifdef USE_NONOPTION_FLAGS
#  define SWAP_FLAGS(ch1, ch2) \
  if (nonoption_flags_len > 0)                            \
    {                                         \
      char __tmp = __getopt_nonoption_flags[ch1];                 \
      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];          \
      __getopt_nonoption_flags[ch2] = __tmp;                      \
    }
# else
#  define SWAP_FLAGS(ch1, ch2)
# endif
#else   /* !_LIBC */
# define SWAP_FLAGS(ch1, ch2)
#endif  /* _LIBC */

/* Exchange two adjacent subsequences of ARGV.
   One subsequence is elements [first_nonopt,last_nonopt)
   which contains all the non-options that have been skipped so far.
   The other is elements [last_nonopt,optind), which contains all
   the options processed since those non-options were skipped.

   `first_nonopt' and `last_nonopt' are relocated so that they describe
   the new indices of the non-options in ARGV after they are moved.  */

#if defined __STDC__ && __STDC__
static void exchange (char **);
#endif

static void
exchange (argv)
     char **argv;
{
  int bottom = first_nonopt;
  int middle = last_nonopt;
  int top = optind;
  char *tem;

  /* Exchange the shorter segment with the far end of the longer segment.
     That puts the shorter segment into the right place.
     It leaves the longer segment in the right place overall,
     but it consists of two parts that need to be swapped next.  */

#if defined _LIBC && defined USE_NONOPTION_FLAGS
  /* First make sure the handling of the `__getopt_nonoption_flags'
     string can work normally.  Our top argument must be in the range
     of the string.  */
  if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
    {
      /* We must extend the array.  The user plays games with us and
     presents new arguments.  */
      char *new_str = malloc (top + 1);
      if (new_str == NULL)
    nonoption_flags_len = nonoption_flags_max_len = 0;
      else
    {
      memset (__mempcpy (new_str, __getopt_nonoption_flags,
                 nonoption_flags_max_len),
          '\0', top + 1 - nonoption_flags_max_len);
      nonoption_flags_max_len = top + 1;
      __getopt_nonoption_flags = new_str;
    }
    }
#endif

  while (top > middle && middle > bottom)
    {
      if (top - middle > middle - bottom)
    {
      /* Bottom segment is the short one.  */
      int len = middle - bottom;
      register int i;

      /* Swap it with the top part of the top segment.  */
      for (i = 0; i < len; i++)
        {
          tem = argv[bottom + i];
          argv[bottom + i] = argv[top - (middle - bottom) + i];
          argv[top - (middle - bottom) + i] = tem;
          SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
        }
      /* Exclude the moved bottom segment from further swapping.  */
      top -= len;
    }
      else
    {
      /* Top segment is the short one.  */
      int len = top - middle;
      register int i;

      /* Swap it with the bottom part of the bottom segment.  */
      for (i = 0; i < len; i++)
        {
          tem = argv[bottom + i];
          argv[bottom + i] = argv[middle + i];
          argv[middle + i] = tem;
          SWAP_FLAGS (bottom + i, middle + i);
        }
      /* Exclude the moved top segment from further swapping.  */
      bottom += len;
    }
    }

  /* Update records for the slots the non-options now occupy.  */

  first_nonopt += (optind - last_nonopt);
  last_nonopt = optind;
}

/* Initialize the internal data when the first call is made.  */

#if defined __STDC__ && __STDC__
static const char *_getopt_initialize (int, char *const *, const char *);
#endif
static const char *
_getopt_initialize (argc, argv, optstring)
     int argc;
     char *const *argv;
     const char *optstring;
{
  /* Start processing options with ARGV-element 1 (since ARGV-element 0
     is the program name); the sequence of previously skipped
     non-option ARGV-elements is empty.  */

  first_nonopt = last_nonopt = optind;

  nextchar = NULL;

  posixly_correct = getenv ("POSIXLY_CORRECT");

  /* Determine how to handle the ordering of options and nonoptions.  */

  if (optstring[0] == '-')
    {
      ordering = RETURN_IN_ORDER;
      ++optstring;
    }
  else if (optstring[0] == '+')
    {
      ordering = REQUIRE_ORDER;
      ++optstring;
    }
  else if (posixly_correct != NULL)
    ordering = REQUIRE_ORDER;
  else
    ordering = PERMUTE;

#if defined _LIBC && defined USE_NONOPTION_FLAGS
  if (posixly_correct == NULL
      && argc == __libc_argc && argv == __libc_argv)
    {
      if (nonoption_flags_max_len == 0)
    {
      if (__getopt_nonoption_flags == NULL
          || __getopt_nonoption_flags[0] == '\0')
        nonoption_flags_max_len = -1;
      else
        {
          const char *orig_str = __getopt_nonoption_flags;
          int len = nonoption_flags_max_len = strlen (orig_str);
          if (nonoption_flags_max_len < argc)
        nonoption_flags_max_len = argc;
          __getopt_nonoption_flags =
        (char *) malloc (nonoption_flags_max_len);
          if (__getopt_nonoption_flags == NULL)
        nonoption_flags_max_len = -1;
          else
        memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
            '\0', nonoption_flags_max_len - len);
        }
    }
      nonoption_flags_len = nonoption_flags_max_len;
    }
  else
    nonoption_flags_len = 0;
#endif

  return optstring;
}


/* Scan elements of ARGV (whose length is ARGC) for option characters
   given in OPTSTRING.

   If an element of ARGV starts with '-', and is not exactly "-" or "--",
   then it is an option element.  The characters of this element
   (aside from the initial '-') are option characters.  If `getopt'
   is called repeatedly, it returns successively each of the option characters
   from each of the option elements.

   If `getopt' finds another option character, it returns that character,
   updating `optind' and `nextchar' so that the next call to `getopt' can
   resume the scan with the following option character or ARGV-element.

   If there are no more option characters, `getopt' returns -1.
   Then `optind' is the index in ARGV of the first ARGV-element
   that is not an option.  (The ARGV-elements have been permuted
   so that those that are not options now come last.)

   OPTSTRING is a string containing the legitimate option characters.
   If an option character is seen that is not listed in OPTSTRING,
   return '?' after printing an error message.  If you set `opterr' to
   zero, the error message is suppressed but we still return '?'.

   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
   so the following text in the same ARGV-element, or the text of the following
   ARGV-element, is returned in `optarg'.  Two colons mean an option that
   wants an optional arg; if there is text in the current ARGV-element,
   it is returned in `optarg', otherwise `optarg' is set to zero.

   If OPTSTRING starts with `-' or `+', it requests different methods of
   handling the non-option ARGV-elements.
   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.

   Long-named options begin with `--' instead of `-'.
   Their names may be abbreviated as long as the abbreviation is unique
   or is an exact match for some defined option.  If they have an
   argument, it follows the option name in the same ARGV-element, separated
   from the option name by a `=', or else the in next ARGV-element.
   When `getopt' finds a long-named option, it returns 0 if that option's
   `flag' field is nonzero, the value of the option's `val' field
   if the `flag' field is zero.

   The elements of ARGV aren't really const, because we permute them.
   But we pretend they're const in the prototype to be compatible
   with other systems.

   LONGOPTS is a vector of `struct option' terminated by an
   element containing a name which is zero.

   LONGIND returns the index in LONGOPT of the long-named option found.
   It is only valid when a long-named option has been found by the most
   recent call.

   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
   long-named options.  */

int
_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
     int argc;
     char *const *argv;
     const char *optstring;
     const struct option *longopts;
     int *longind;
     int long_only;
{
  int print_errors = opterr;
  if (optstring[0] == ':')
    print_errors = 0;

  if (argc < 1)
    return -1;

  optarg = NULL;

  if (optind == 0 || !__getopt_initialized)
    {
      if (optind == 0)
    optind = 1; /* Don't scan ARGV[0], the program name.  */
      optstring = _getopt_initialize (argc, argv, optstring);
      __getopt_initialized = 1;
    }

  /* Test whether ARGV[optind] points to a non-option argument.
     Either it does not have option syntax, or there is an environment flag
     from the shell indicating it is not an option.  The later information
     is only used when the used in the GNU libc.  */
#if defined _LIBC && defined USE_NONOPTION_FLAGS
# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'       \
              || (optind < nonoption_flags_len                \
              && __getopt_nonoption_flags[optind] == '1'))
#else
# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
#endif

  if (nextchar == NULL || *nextchar == '\0')
    {
      /* Advance to the next ARGV-element.  */

      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
     moved back by the user (who may also have changed the arguments).  */
      if (last_nonopt > optind)
    last_nonopt = optind;
      if (first_nonopt > optind)
    first_nonopt = optind;

      if (ordering == PERMUTE)
    {
      /* If we have just processed some options following some non-options,
         exchange them so that the options come first.  */

      if (first_nonopt != last_nonopt && last_nonopt != optind)
        exchange ((char **) argv);
      else if (last_nonopt != optind)
        first_nonopt = optind;

      /* Skip any additional non-options
         and extend the range of non-options previously skipped.  */

      while (optind < argc && NONOPTION_P)
        optind++;
      last_nonopt = optind;
    }

      /* The special ARGV-element `--' means premature end of options.
     Skip it like a null option,
     then exchange with previous non-options as if it were an option,
     then skip everything else like a non-option.  */

      if (optind != argc && !strcmp (argv[optind], "--"))
    {
      optind++;

      if (first_nonopt != last_nonopt && last_nonopt != optind)
        exchange ((char **) argv);
      else if (first_nonopt == last_nonopt)
        first_nonopt = optind;
      last_nonopt = argc;

      optind = argc;
    }

      /* If we have done all the ARGV-elements, stop the scan
     and back over any non-options that we skipped and permuted.  */

      if (optind == argc)
    {
      /* Set the next-arg-index to point at the non-options
         that we previously skipped, so the caller will digest them.  */
      if (first_nonopt != last_nonopt)
        optind = first_nonopt;
      return -1;
    }

      /* If we have come to a non-option and did not permute it,
     either stop the scan or describe it to the caller and pass it by.  */

      if (NONOPTION_P)
    {
      if (ordering == REQUIRE_ORDER)
        return -1;
      optarg = argv[optind++];
      return 1;
    }

      /* We have found another option-ARGV-element.
     Skip the initial punctuation.  */

      nextchar = (argv[optind] + 1
          + (longopts != NULL && argv[optind][1] == '-'));
    }

  /* Decode the current option-ARGV-element.  */

  /* Check whether the ARGV-element is a long option.

     If long_only and the ARGV-element has the form "-f", where f is
     a valid short option, don't consider it an abbreviated form of
     a long option that starts with f.  Otherwise there would be no
     way to give the -f short option.

     On the other hand, if there's a long option "fubar" and
     the ARGV-element is "-fu", do consider that an abbreviation of
     the long option, just like "--fu", and not "-f" with arg "u".

     This distinction seems to be the most useful approach.  */

  if (longopts != NULL
      && (argv[optind][1] == '-'
      || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
    {
      char *nameend;
      const struct option *p;
      const struct option *pfound = NULL;
      int exact = 0;
      int ambig = 0;
      int indfound = -1;
      int option_index;

      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
    /* Do nothing.  */ ;

      /* Test all long options for either exact match
     or abbreviated matches.  */
      for (p = longopts, option_index = 0; p->name; p++, option_index++)
    if (!strncmp (p->name, nextchar, nameend - nextchar))
      {
        if ((unsigned int) (nameend - nextchar)
        == (unsigned int) strlen (p->name))
          {
        /* Exact match found.  */
        pfound = p;
        indfound = option_index;
        exact = 1;
        break;
          }
        else if (pfound == NULL)
          {
        /* First nonexact match found.  */
        pfound = p;
        indfound = option_index;
          }
        else if (long_only
             || pfound->has_arg != p->has_arg
             || pfound->flag != p->flag
             || pfound->val != p->val)
          /* Second or later nonexact match found.  */
          ambig = 1;
      }

      if (ambig && !exact)
    {
      if (print_errors)
        {
#if defined _LIBC && defined USE_IN_LIBIO
          char *buf;

          if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"),
                  argv[0], argv[optind]) >= 0)
        {

          if (_IO_fwide (stderr, 0) > 0)
            __fwprintf (stderr, L"%s", buf);
          else
            fputs (buf, stderr);

          free (buf);
        }
#else
          fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
               argv[0], argv[optind]);
#endif
        }
      nextchar += strlen (nextchar);
      optind++;
      optopt = 0;
      return '?';
    }

      if (pfound != NULL)
    {
      option_index = indfound;
      optind++;
      if (*nameend)
        {
          /* Don't test has_arg with >, because some C compilers don't
         allow it to be used on enums.  */
          if (pfound->has_arg)
        optarg = nameend + 1;
          else
        {
          if (print_errors)
            {
#if defined _LIBC && defined USE_IN_LIBIO
              char *buf;
              int n;
#endif

              if (argv[optind - 1][1] == '-')
            {
              /* --option */
#if defined _LIBC && defined USE_IN_LIBIO
              n = __asprintf (&buf, _("\
%s: option `--%s' doesn't allow an argument\n"),
                      argv[0], pfound->name);
#else
              fprintf (stderr, _("\
%s: option `--%s' doesn't allow an argument\n"),
                   argv[0], pfound->name);
#endif
            }
              else
            {
              /* +option or -option */
#if defined _LIBC && defined USE_IN_LIBIO
              n = __asprintf (&buf, _("\
%s: option `%c%s' doesn't allow an argument\n"),
                      argv[0], argv[optind - 1][0],
                      pfound->name);
#else
              fprintf (stderr, _("\
%s: option `%c%s' doesn't allow an argument\n"),
                   argv[0], argv[optind - 1][0], pfound->name);
#endif
            }

#if defined _LIBC && defined USE_IN_LIBIO
              if (n >= 0)
            {
              if (_IO_fwide (stderr, 0) > 0)
                __fwprintf (stderr, L"%s", buf);
              else
                fputs (buf, stderr);

              free (buf);
            }
#endif
            }

          nextchar += strlen (nextchar);

          optopt = pfound->val;
          return '?';
        }
        }
      else if (pfound->has_arg == 1)
        {
          if (optind < argc)
        optarg = argv[optind++];
          else
        {
          if (print_errors)
            {
#if defined _LIBC && defined USE_IN_LIBIO
              char *buf;

              if (__asprintf (&buf, _("\
%s: option `%s' requires an argument\n"),
                      argv[0], argv[optind - 1]) >= 0)
            {
              if (_IO_fwide (stderr, 0) > 0)
                __fwprintf (stderr, L"%s", buf);
              else
                fputs (buf, stderr);

              free (buf);
            }
#else
              fprintf (stderr,
                   _("%s: option `%s' requires an argument\n"),
                   argv[0], argv[optind - 1]);
#endif
            }
          nextchar += strlen (nextchar);
          optopt = pfound->val;
          return optstring[0] == ':' ? ':' : '?';
        }
        }
      nextchar += strlen (nextchar);
      if (longind != NULL)
        *longind = option_index;
      if (pfound->flag)
        {
          *(pfound->flag) = pfound->val;
          return 0;
        }
      return pfound->val;
    }

      /* Can't find it as a long option.  If this is not getopt_long_only,
     or the option starts with '--' or is not a valid short
     option, then it's an error.
     Otherwise interpret it as a short option.  */
      if (!long_only || argv[optind][1] == '-'
      || my_index (optstring, *nextchar) == NULL)
    {
      if (print_errors)
        {
#if defined _LIBC && defined USE_IN_LIBIO
          char *buf;
          int n;
#endif

          if (argv[optind][1] == '-')
        {
          /* --option */
#if defined _LIBC && defined USE_IN_LIBIO
          n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"),
                  argv[0], nextchar);
#else
          fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
               argv[0], nextchar);
#endif
        }
          else
        {
          /* +option or -option */
#if defined _LIBC && defined USE_IN_LIBIO
          n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"),
                  argv[0], argv[optind][0], nextchar);
#else
          fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
               argv[0], argv[optind][0], nextchar);
#endif
        }

#if defined _LIBC && defined USE_IN_LIBIO
          if (n >= 0)
        {
          if (_IO_fwide (stderr, 0) > 0)
            __fwprintf (stderr, L"%s", buf);
          else
            fputs (buf, stderr);

          free (buf);
        }
#endif
        }
      nextchar = (char *) "";
      optind++;
      optopt = 0;
      return '?';
    }
    }

  /* Look at and handle the next short option-character.  */

  {
    char c = *nextchar++;
    char *temp = my_index (optstring, c);

    /* Increment `optind' when we start to process its last character.  */
    if (*nextchar == '\0')
      ++optind;

    if (temp == NULL || c == ':')
      {
    if (print_errors)
      {
#if defined _LIBC && defined USE_IN_LIBIO
          char *buf;
          int n;
#endif

        if (posixly_correct)
          {
        /* 1003.2 specifies the format of this message.  */
#if defined _LIBC && defined USE_IN_LIBIO
        n = __asprintf (&buf, _("%s: illegal option -- %c\n"),
                argv[0], c);
#else
        fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c);
#endif
          }
        else
          {
#if defined _LIBC && defined USE_IN_LIBIO
        n = __asprintf (&buf, _("%s: invalid option -- %c\n"),
                argv[0], c);
#else
        fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c);
#endif
          }

#if defined _LIBC && defined USE_IN_LIBIO
        if (n >= 0)
          {
        if (_IO_fwide (stderr, 0) > 0)
          __fwprintf (stderr, L"%s", buf);
        else
          fputs (buf, stderr);

        free (buf);
          }
#endif
      }
    optopt = c;
    return '?';
      }
    /* Convenience. Treat POSIX -W foo same as long option --foo */
    if (temp[0] == 'W' && temp[1] == ';')
      {
    char *nameend;
    const struct option *p;
    const struct option *pfound = NULL;
    int exact = 0;
    int ambig = 0;
    int indfound = 0;
    int option_index;

    /* This is an option that requires an argument.  */
    if (*nextchar != '\0')
      {
        optarg = nextchar;
        /* If we end this ARGV-element by taking the rest as an arg,
           we must advance to the next element now.  */
        optind++;
      }
    else if (optind == argc)
      {
        if (print_errors)
          {
        /* 1003.2 specifies the format of this message.  */
#if defined _LIBC && defined USE_IN_LIBIO
        char *buf;

        if (__asprintf (&buf,
                _("%s: option requires an argument -- %c\n"),
                argv[0], c) >= 0)
          {
            if (_IO_fwide (stderr, 0) > 0)
              __fwprintf (stderr, L"%s", buf);
            else
              fputs (buf, stderr);

            free (buf);
          }
#else
        fprintf (stderr, _("%s: option requires an argument -- %c\n"),
             argv[0], c);
#endif
          }
        optopt = c;
        if (optstring[0] == ':')
          c = ':';
        else
          c = '?';
        return c;
      }
    else
      /* We already incremented `optind' once;
         increment it again when taking next ARGV-elt as argument.  */
      optarg = argv[optind++];

    /* optarg is now the argument, see if it's in the
       table of longopts.  */

    for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
      /* Do nothing.  */ ;

    /* Test all long options for either exact match
       or abbreviated matches.  */
    for (p = longopts, option_index = 0; p->name; p++, option_index++)
      if (!strncmp (p->name, nextchar, nameend - nextchar))
        {
          if ((unsigned int) (nameend - nextchar) == strlen (p->name))
        {
          /* Exact match found.  */
          pfound = p;
          indfound = option_index;
          exact = 1;
          break;
        }
          else if (pfound == NULL)
        {
          /* First nonexact match found.  */
          pfound = p;
          indfound = option_index;
        }
          else
        /* Second or later nonexact match found.  */
        ambig = 1;
        }
    if (ambig && !exact)
      {
        if (print_errors)
          {
#if defined _LIBC && defined USE_IN_LIBIO
        char *buf;

        if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"),
                argv[0], argv[optind]) >= 0)
          {
            if (_IO_fwide (stderr, 0) > 0)
              __fwprintf (stderr, L"%s", buf);
            else
              fputs (buf, stderr);

            free (buf);
          }
#else
        fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
             argv[0], argv[optind]);
#endif
          }
        nextchar += strlen (nextchar);
        optind++;
        return '?';
      }
    if (pfound != NULL)
      {
        option_index = indfound;
        if (*nameend)
          {
        /* Don't test has_arg with >, because some C compilers don't
           allow it to be used on enums.  */
        if (pfound->has_arg)
          optarg = nameend + 1;
        else
          {
            if (print_errors)
              {
#if defined _LIBC && defined USE_IN_LIBIO
            char *buf;

            if (__asprintf (&buf, _("\
%s: option `-W %s' doesn't allow an argument\n"),
                    argv[0], pfound->name) >= 0)
              {
                if (_IO_fwide (stderr, 0) > 0)
                  __fwprintf (stderr, L"%s", buf);
                else
                  fputs (buf, stderr);

                free (buf);
              }
#else
            fprintf (stderr, _("\
%s: option `-W %s' doesn't allow an argument\n"),
                 argv[0], pfound->name);
#endif
              }

            nextchar += strlen (nextchar);
            return '?';
          }
          }
        else if (pfound->has_arg == 1)
          {
        if (optind < argc)
          optarg = argv[optind++];
        else
          {
            if (print_errors)
              {
#if defined _LIBC && defined USE_IN_LIBIO
            char *buf;

            if (__asprintf (&buf, _("\
%s: option `%s' requires an argument\n"),
                    argv[0], argv[optind - 1]) >= 0)
              {
                if (_IO_fwide (stderr, 0) > 0)
                  __fwprintf (stderr, L"%s", buf);
                else
                  fputs (buf, stderr);

                free (buf);
              }
#else
            fprintf (stderr,
                 _("%s: option `%s' requires an argument\n"),
                 argv[0], argv[optind - 1]);
#endif
              }
            nextchar += strlen (nextchar);
            return optstring[0] == ':' ? ':' : '?';
          }
          }
        nextchar += strlen (nextchar);
        if (longind != NULL)
          *longind = option_index;
        if (pfound->flag)
          {
        *(pfound->flag) = pfound->val;
        return 0;
          }
        return pfound->val;
      }
      nextchar = NULL;
      return 'W';   /* Let the application handle it.   */
      }
    if (temp[1] == ':')
      {
    if (temp[2] == ':')
      {
        /* This is an option that accepts an argument optionally.  */
        if (*nextchar != '\0')
          {
        optarg = nextchar;
        optind++;
          }
        else
          optarg = NULL;
        nextchar = NULL;
      }
    else
      {
        /* This is an option that requires an argument.  */
        if (*nextchar != '\0')
          {
        optarg = nextchar;
        /* If we end this ARGV-element by taking the rest as an arg,
           we must advance to the next element now.  */
        optind++;
          }
        else if (optind == argc)
          {
        if (print_errors)
          {
            /* 1003.2 specifies the format of this message.  */
#if defined _LIBC && defined USE_IN_LIBIO
            char *buf;

            if (__asprintf (&buf, _("\
%s: option requires an argument -- %c\n"),
                    argv[0], c) >= 0)
              {
            if (_IO_fwide (stderr, 0) > 0)
              __fwprintf (stderr, L"%s", buf);
            else
              fputs (buf, stderr);

            free (buf);
              }
#else
            fprintf (stderr,
                 _("%s: option requires an argument -- %c\n"),
                 argv[0], c);
#endif
          }
        optopt = c;
        if (optstring[0] == ':')
          c = ':';
        else
          c = '?';
          }
        else
          /* We already incremented `optind' once;
         increment it again when taking next ARGV-elt as argument.  */
          optarg = argv[optind++];
        nextchar = NULL;
      }
      }
    return c;
  }
}

int
getopt (argc, argv, optstring)
     int argc;
     char *const *argv;
     const char *optstring;
{
  return _getopt_internal (argc, argv, optstring,
               (const struct option *) 0,
               (int *) 0,
               0);
}

#endif  /* Not ELIDE_CODE.  */


#ifdef TEST

/* Compile with -DTEST to make an executable for use in testing
   the above definition of `getopt'.  */

int
main (argc, argv)
     int argc;
     char **argv;
{
  int c;
  int digit_optind = 0;

  while (1)
    {
      int this_option_optind = optind ? optind : 1;

      c = getopt (argc, argv, "abc:d:0123456789");
      if (c == -1)
    break;

      switch (c)
    {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (digit_optind != 0 && digit_optind != this_option_optind)
        printf ("digits occur in two different argv-elements.\n");
      digit_optind = this_option_optind;
      printf ("option %c\n", c);
      break;

    case 'a':
      printf ("option a\n");
      break;

    case 'b':
      printf ("option b\n");
      break;

    case 'c':
      printf ("option c with value `%s'\n", optarg);
      break;

    case '?':
      break;

    default:
      printf ("?? getopt returned character code 0%o ??\n", c);
    }
    }

  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
    printf ("%s ", argv[optind++]);
      printf ("\n");
    }

  exit (0);
}

#endif /* TEST */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/getopt1.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/getopt1.c used in: Step 560
/* getopt_long and getopt_long_only entry points for GNU getopt.
   Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
     Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef _LIBC
# include <getopt.h>
#else
# include "getopt.h"
#endif

#if !defined __STDC__ || !__STDC__
/* This is a separate conditional since some stdc systems
   reject `defined (const)'.  */
#ifndef const
#define const
#endif
#endif

#include <stdio.h>

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#define GETOPT_INTERFACE_VERSION 2
#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
#include <gnu-versions.h>
#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
#define ELIDE_CODE
#endif
#endif

#ifndef ELIDE_CODE


/* This needs to come after some library #include
   to get __GNU_LIBRARY__ defined.  */
#ifdef __GNU_LIBRARY__
#include <stdlib.h>
#endif

#ifndef NULL
#define NULL 0
#endif

int
getopt_long (argc, argv, options, long_options, opt_index)
     int argc;
     char *const *argv;
     const char *options;
     const struct option *long_options;
     int *opt_index;
{
  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
}

/* Like getopt_long, but '-' as well as '--' can indicate a long option.
   If an option that starts with '-' (not '--') doesn't match a long option,
   but does match a short option, it is parsed as a short option
   instead.  */

int
getopt_long_only (argc, argv, options, long_options, opt_index)
     int argc;
     char *const *argv;
     const char *options;
     const struct option *long_options;
     int *opt_index;
{
  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
}

# ifdef _LIBC
libc_hidden_def (getopt_long)
libc_hidden_def (getopt_long_only)
# endif

#endif  /* Not ELIDE_CODE.  */


#ifdef TEST

#include <stdio.h>

int
main (argc, argv)
     int argc;
     char **argv;
{
  int c;
  int digit_optind = 0;

  while (1)
    {
      int this_option_optind = optind ? optind : 1;
      int option_index = 0;
      static struct option long_options[] =
      {
    {"add", 1, 0, 0},
    {"append", 0, 0, 0},
    {"delete", 1, 0, 0},
    {"verbose", 0, 0, 0},
    {"create", 0, 0, 0},
    {"file", 1, 0, 0},
    {0, 0, 0, 0}
      };

      c = getopt_long (argc, argv, "abc:d:0123456789",
               long_options, &option_index);
      if (c == -1)
    break;

      switch (c)
    {
    case 0:
      printf ("option %s", long_options[option_index].name);
      if (optarg)
        printf (" with arg %s", optarg);
      printf ("\n");
      break;

    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (digit_optind != 0 && digit_optind != this_option_optind)
        printf ("digits occur in two different argv-elements.\n");
      digit_optind = this_option_optind;
      printf ("option %c\n", c);
      break;

    case 'a':
      printf ("option a\n");
      break;

    case 'b':
      printf ("option b\n");
      break;

    case 'c':
      printf ("option c with value `%s'\n", optarg);
      break;

    case 'd':
      printf ("option d with value `%s'\n", optarg);
      break;

    case '?':
      break;

    default:
      printf ("?? getopt returned character code 0%o ??\n", c);
    }
    }

  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
    printf ("%s ", argv[optind++]);
      printf ("\n");
    }

  exit (0);
}

#endif /* TEST */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/addext.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/addext.c used in: Step 561
/* addext.c -- add an extension to a file name

   Copyright (C) 1990, 1997, 1998, 1999, 2001, 2003 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu> and Paul Eggert */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#ifndef HAVE_DOS_FILE_NAMES
# define HAVE_DOS_FILE_NAMES 0
#endif
#ifndef HAVE_LONG_FILE_NAMES
# define HAVE_LONG_FILE_NAMES 0
#endif

#if HAVE_LIMITS_H
# include <limits.h>
#endif
#ifndef _POSIX_NAME_MAX
# define _POSIX_NAME_MAX 14
#endif

#include <sys/types.h>
#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif

#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#include "backupfile.h"
#include "dirname.h"

/* Append to FILENAME the extension EXT, unless the result would be too long,
   in which case just append the character E.  */

void
addext (char *filename, char const *ext, int e)
{
  char *s = base_name (filename);
  size_t slen = base_len (s);
  size_t extlen = strlen (ext);
  size_t slen_max = HAVE_LONG_FILE_NAMES ? 255 : _POSIX_NAME_MAX;

#if HAVE_PATHCONF && defined _PC_NAME_MAX
  if (_POSIX_NAME_MAX < slen + extlen || HAVE_DOS_FILE_NAMES)
    {
      /* The new base name is long enough to require a pathconf check.  */
      long name_max;
      errno = 0;
      if (s == filename)
    name_max = pathconf (".", _PC_NAME_MAX);
      else
    {
      char c = *s;
      if (! ISSLASH (c))
        *s = 0;
      name_max = pathconf (filename, _PC_NAME_MAX);
      *s = c;
    }
      if (0 <= name_max || errno == 0)
    {
      long size = slen_max = name_max;
      if (name_max != size)
        slen_max = -1;
    }
    }
#endif

  if (HAVE_DOS_FILE_NAMES && slen_max <= 12)
    {
      /* Live within DOS's 8.3 limit.  */
      char *dot = strchr (s, '.');
      if (dot)
    {
      slen -= dot + 1 - s;
      s = dot + 1;
      slen_max = 3;
    }
      else
    slen_max = 8;
      extlen = 9; /* Don't use EXT.  */
    }

  if (slen + extlen <= slen_max)
    strcpy (s + slen, ext);
  else
    {
      if (slen_max <= slen)
    slen = slen_max - 1;
      s[slen] = e;
      s[slen + 1] = 0;
    }
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/argmatch.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/argmatch.c used in: Step 562
/* argmatch.c -- find a match for a string in an array

   Copyright (C) 1990, 1998, 1999, 2001, 2002, 2003 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.  */

/* Written by David MacKenzie <djm@ai.mit.edu>
   Modified by Akim Demaille <demaille@inf.enst.fr> */

#if HAVE_CONFIG_H
# include <config.h>
#endif

/* Specification.  */
#include "argmatch.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "gettext.h"
#define _(msgid) gettext (msgid)

#include "error.h"
#include "quotearg.h"
#include "quote.h"
#include "unlocked-io.h"

/* When reporting an invalid argument, show nonprinting characters
   by using the quoting style ARGMATCH_QUOTING_STYLE.  Do not use
   literal_quoting_style.  */
#ifndef ARGMATCH_QUOTING_STYLE
# define ARGMATCH_QUOTING_STYLE locale_quoting_style
#endif

#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif

/* Non failing version of argmatch call this function after failing. */
#ifndef ARGMATCH_DIE
# define ARGMATCH_DIE exit (EXIT_FAILURE)
#endif

#ifdef ARGMATCH_DIE_DECL
ARGMATCH_DIE_DECL;
#endif

static void
__argmatch_die (void)
{
  ARGMATCH_DIE;
}

/* Used by XARGMATCH and XARGCASEMATCH.  See description in argmatch.h.
   Default to __argmatch_die, but allow caller to change this at run-time. */
argmatch_exit_fn argmatch_die = __argmatch_die;



/* If ARG is an unambiguous match for an element of the
   null-terminated array ARGLIST, return the index in ARGLIST
   of the matched element, else -1 if it does not match any element
   or -2 if it is ambiguous (is a prefix of more than one element).

   If VALLIST is none null, use it to resolve ambiguities limited to
   synonyms, i.e., for
     "yes", "yop" -> 0
     "no", "nope" -> 1
   "y" is a valid argument, for `0', and "n" for `1'.  */

int
argmatch (const char *arg, const char *const *arglist,
      const char *vallist, size_t valsize)
{
  int i;            /* Temporary index in ARGLIST.  */
  size_t arglen;        /* Length of ARG.  */
  int matchind = -1;        /* Index of first nonexact match.  */
  int ambiguous = 0;        /* If nonzero, multiple nonexact match(es).  */

  arglen = strlen (arg);

  /* Test all elements for either exact match or abbreviated matches.  */
  for (i = 0; arglist[i]; i++)
    {
      if (!strncmp (arglist[i], arg, arglen))
    {
      if (strlen (arglist[i]) == arglen)
        /* Exact match found.  */
        return i;
      else if (matchind == -1)
        /* First nonexact match found.  */
        matchind = i;
      else
        {
          /* Second nonexact match found.  */
          if (vallist == NULL
          || memcmp (vallist + valsize * matchind,
                 vallist + valsize * i, valsize))
        {
          /* There is a real ambiguity, or we could not
             disambiguate. */
          ambiguous = 1;
        }
        }
    }
    }
  if (ambiguous)
    return -2;
  else
    return matchind;
}

/* Error reporting for argmatch.
   CONTEXT is a description of the type of entity that was being matched.
   VALUE is the invalid value that was given.
   PROBLEM is the return value from argmatch.  */

void
argmatch_invalid (const char *context, const char *value, int problem)
{
  char const *format = (problem == -1
            ? _("invalid argument %s for %s")
            : _("ambiguous argument %s for %s"));

  error (0, 0, format, quotearg_n_style (0, ARGMATCH_QUOTING_STYLE, value),
     quote_n (1, context));
}

/* List the valid arguments for argmatch.
   ARGLIST is the same as in argmatch.
   VALLIST is a pointer to an array of values.
   VALSIZE is the size of the elements of VALLIST */
void
argmatch_valid (const char *const *arglist,
        const char *vallist, size_t valsize)
{
  int i;
  const char *last_val = NULL;

  /* We try to put synonyms on the same line.  The assumption is that
     synonyms follow each other */
  fprintf (stderr, _("Valid arguments are:"));
  for (i = 0; arglist[i]; i++)
    if ((i == 0)
    || memcmp (last_val, vallist + valsize * i, valsize))
      {
    fprintf (stderr, "\n  - `%s'", arglist[i]);
    last_val = vallist + valsize * i;
      }
    else
      {
    fprintf (stderr, ", `%s'", arglist[i]);
      }
  putc ('\n', stderr);
}

/* Never failing versions of the previous functions.

   CONTEXT is the context for which argmatch is called (e.g.,
   "--version-control", or "$VERSION_CONTROL" etc.).  Upon failure,
   calls the (supposed never to return) function EXIT_FN. */

int
__xargmatch_internal (const char *context,
              const char *arg, const char *const *arglist,
              const char *vallist, size_t valsize,
              argmatch_exit_fn exit_fn)
{
  int res = argmatch (arg, arglist, vallist, valsize);
  if (res >= 0)
    /* Success. */
    return res;

  /* We failed.  Explain why. */
  argmatch_invalid (context, arg, res);
  argmatch_valid (arglist, vallist, valsize);
  (*exit_fn) ();

  return -1; /* To please the compilers. */
}

/* Look for VALUE in VALLIST, an array of objects of size VALSIZE and
   return the first corresponding argument in ARGLIST */
const char *
argmatch_to_argument (const char *value,
              const char *const *arglist,
              const char *vallist, size_t valsize)
{
  int i;

  for (i = 0; arglist[i]; i++)
    if (!memcmp (value, vallist + valsize * i, valsize))
      return arglist[i];
  return NULL;
}

#ifdef TEST
/*
 * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu>
 */
char *program_name;
extern const char *getenv ();

/* When to make backup files.  */
enum backup_type
{
  /* Never make backups.  */
  none,

  /* Make simple backups of every file.  */
  simple,

  /* Make numbered backups of files that already have numbered backups,
     and simple backups of the others.  */
  numbered_existing,

  /* Make numbered backups of every file.  */
  numbered
};

/* Two tables describing arguments (keys) and their corresponding
   values */
static const char *const backup_args[] =
{
  "no", "none", "off",
  "simple", "never",
  "existing", "nil",
  "numbered", "t",
  0
};

static const enum backup_type backup_vals[] =
{
  none, none, none,
  simple, simple,
  numbered_existing, numbered_existing,
  numbered, numbered
};

int
main (int argc, const char *const *argv)
{
  const char *cp;
  enum backup_type backup_type = none;

  program_name = (char *) argv[0];

  if (argc > 2)
    {
      fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name);
      exit (1);
    }

  if ((cp = getenv ("VERSION_CONTROL")))
    backup_type = XARGMATCH ("$VERSION_CONTROL", cp,
                 backup_args, backup_vals);

  if (argc == 2)
    backup_type = XARGMATCH (program_name, argv[1],
                 backup_args, backup_vals);

  printf ("The version control is `%s'\n",
      ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals));

  return 0;
}
#endif

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/backupfile.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/backupfile.c used in: Step 563
/* backupfile.c -- make Emacs style backup file names
   Copyright (C) 1990,91,92,93,94,95,96,97,98,99,2000, 2001, 2002 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu>.
   Some algorithms adapted from GNU Emacs. */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <sys/types.h>
#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif

#if HAVE_DIRENT_H
# include <dirent.h>
# define NLENGTH(direct) strlen ((direct)->d_name)
#else
# define dirent direct
# define NLENGTH(direct) ((size_t) (direct)->d_namlen)
# if HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
# if HAVE_NDIR_H
#  include <ndir.h>
# endif
#endif

#if CLOSEDIR_VOID
/* Fake a return value. */
# define CLOSEDIR(d) (closedir (d), 0)
#else
# define CLOSEDIR(d) closedir (d)
#endif

#if HAVE_STDLIB_H
# include <stdlib.h>
#endif

#ifndef HAVE_DECL_GETENV
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_GETENV
char *getenv ();
#endif

#ifndef HAVE_DECL_MALLOC
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_MALLOC
char *malloc ();
#endif

#if HAVE_DIRENT_H || HAVE_NDIR_H || HAVE_SYS_DIR_H || HAVE_SYS_NDIR_H
# define HAVE_DIR 1
#else
# define HAVE_DIR 0
#endif

#if HAVE_LIMITS_H
# include <limits.h>
#endif
#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif
/* Upper bound on the string length of an integer converted to string.
   302 / 1000 is ceil (log10 (2.0)).  Subtract 1 for the sign bit;
   add 1 for integer division truncation; add 1 more for a minus sign.  */
#define INT_STRLEN_BOUND(t) ((sizeof (t) * CHAR_BIT - 1) * 302 / 1000 + 2)

/* ISDIGIT differs from isdigit, as follows:
   - Its arg may be any int or unsigned int; it need not be an unsigned char.
   - It's guaranteed to evaluate its argument exactly once.
   - It's typically faster.
   POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
   ISDIGIT_LOCALE unless it's important to use the locale's definition
   of `digit' even when the host does not conform to POSIX.  */
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)

#if D_INO_IN_DIRENT
# define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0)
#else
# define REAL_DIR_ENTRY(dp) 1
#endif

#include "argmatch.h"
#include "backupfile.h"
#include "dirname.h"

/* The extension added to file names to produce a simple (as opposed
   to numbered) backup file name. */
const char *simple_backup_suffix = "~";

static int max_backup_version PARAMS ((const char *, const char *));
static int version_number PARAMS ((const char *, const char *, size_t));

/* Return the name of the new backup file for file FILE,
   allocated with malloc.  Return 0 if out of memory.
   FILE must not end with a '/' unless it is the root directory.
   Do not call this function if backup_type == none. */

char *
find_backup_file_name (const char *file, enum backup_type backup_type)
{
  size_t backup_suffix_size_max;
  size_t file_len = strlen (file);
  size_t numbered_suffix_size_max = INT_STRLEN_BOUND (int) + 4;
  char *s;
  const char *suffix = simple_backup_suffix;

  /* Allow room for simple or `.~N~' backups.  */
  backup_suffix_size_max = strlen (simple_backup_suffix) + 1;
  if (HAVE_DIR && backup_suffix_size_max < numbered_suffix_size_max)
    backup_suffix_size_max = numbered_suffix_size_max;

  s = malloc (file_len + 1
          + backup_suffix_size_max + numbered_suffix_size_max);
  if (s)
    {
#if HAVE_DIR
      if (backup_type != simple)
    {
      int highest_backup;
      size_t dirlen = dir_len (file);

      memcpy (s, file, dirlen);
      if (dirlen == FILESYSTEM_PREFIX_LEN (file))
        s[dirlen++] = '.';
      s[dirlen] = '\0';
      highest_backup = max_backup_version (base_name (file), s);
      if (! (backup_type == numbered_existing && highest_backup == 0))
        {
          char *numbered_suffix = s + (file_len + backup_suffix_size_max);
          sprintf (numbered_suffix, ".~%d~", highest_backup + 1);
          suffix = numbered_suffix;
        }
    }
#endif /* HAVE_DIR */

      strcpy (s, file);
      addext (s, suffix, '~');
    }
  return s;
}

#if HAVE_DIR

/* Return the number of the highest-numbered backup file for file
   FILE in directory DIR.  If there are no numbered backups
   of FILE in DIR, or an error occurs reading DIR, return 0.
   */

static int
max_backup_version (const char *file, const char *dir)
{
  DIR *dirp;
  struct dirent *dp;
  int highest_version;
  int this_version;
  size_t file_name_length;

  dirp = opendir (dir);
  if (!dirp)
    return 0;

  highest_version = 0;
  file_name_length = base_len (file);

  while ((dp = readdir (dirp)) != 0)
    {
      if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) < file_name_length + 4)
    continue;

      this_version = version_number (file, dp->d_name, file_name_length);
      if (this_version > highest_version)
    highest_version = this_version;
    }
  if (CLOSEDIR (dirp))
    return 0;
  return highest_version;
}

/* If BACKUP is a numbered backup of BASE, return its version number;
   otherwise return 0.  BASE_LENGTH is the length of BASE.
   */

static int
version_number (const char *base, const char *backup, size_t base_length)
{
  int version;
  const char *p;

  version = 0;
  if (strncmp (base, backup, base_length) == 0
      && backup[base_length] == '.'
      && backup[base_length + 1] == '~')
    {
      for (p = &backup[base_length + 2]; ISDIGIT (*p); ++p)
    version = version * 10 + *p - '0';
      if (p[0] != '~' || p[1])
    version = 0;
    }
  return version;
}
#endif /* HAVE_DIR */

static const char * const backup_args[] =
{
  /* In a series of synonyms, present the most meaning full first, so
     that argmatch_valid be more readable. */
  "none", "off",
  "simple", "never",
  "existing", "nil",
  "numbered", "t",
  0
};

static const enum backup_type backup_types[] =
{
  none, none,
  simple, simple,
  numbered_existing, numbered_existing,
  numbered, numbered
};

/* Return the type of backup specified by VERSION.
   If VERSION is NULL or the empty string, return numbered_existing.
   If VERSION is invalid or ambiguous, fail with a diagnostic appropriate
   for the specified CONTEXT.  Unambiguous abbreviations are accepted.  */

enum backup_type
get_version (const char *context, const char *version)
{
  if (version == 0 || *version == 0)
    return numbered_existing;
  else
    return XARGMATCH (context, version, backup_args, backup_types);
}


/* Return the type of backup specified by VERSION.
   If VERSION is NULL, use the value of the envvar VERSION_CONTROL.
   If the specified string is invalid or ambiguous, fail with a diagnostic
   appropriate for the specified CONTEXT.
   Unambiguous abbreviations are accepted.  */

enum backup_type
xget_version (const char *context, const char *version)
{
  if (version && *version)
    return get_version (context, version);
  else
    return get_version ("$VERSION_CONTROL", getenv ("VERSION_CONTROL"));
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/basename.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/basename.c used in: Step 564
/* basename.c -- return the last element in a path
   Copyright (C) 1990, 1998, 1999, 2000, 2001 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if STDC_HEADERS || HAVE_STRING_H
# include <string.h>
#endif
#include "dirname.h"

/* 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.  */

char *
base_name (char const *name)
{
  char const *base = name + FILESYSTEM_PREFIX_LEN (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.  */

size_t
base_len (char const *name)
{
  size_t len;

  for (len = strlen (name);  1 < len && ISSLASH (name[len - 1]);  len--)
    continue;

  return len;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/dirname.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/dirname.c used in: Step 565
/* dirname.c -- return all but the last element in a path
   Copyright 1990, 1998, 2000, 2001, 2003 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if STDC_HEADERS || HAVE_STRING_H
# include <string.h>
#endif

#include "dirname.h"
#include "xalloc.h"

/* Return the length of `dirname (PATH)', or zero if PATH is
   in the working directory.  Works properly even if
   there are trailing slashes (by effectively ignoring them).  */
size_t
dir_len (char const *path)
{
  size_t prefix_length = FILESYSTEM_PREFIX_LEN (path);
  size_t length;

  /* Strip the basename and any redundant slashes before it.  */
  for (length = base_name (path) - path;  prefix_length < length;  length--)
    if (! ISSLASH (path[length - 1]))
      return length;

  /* But don't strip the only slash from "/".  */
  return prefix_length + ISSLASH (path[prefix_length]);
}

/* Return the leading directories part of PATH,
   allocated with xmalloc.
   Works properly even if there are trailing slashes
   (by effectively ignoring them).  */

char *
dir_name (char const *path)
{
  size_t length = dir_len (path);
  int append_dot = (length == FILESYSTEM_PREFIX_LEN (path));
  char *newpath = xmalloc (length + append_dot + 1);
  memcpy (newpath, path, length);
  if (append_dot)
    newpath[length++] = '.';
  newpath[length] = 0;
  return newpath;
}

#ifdef TEST_DIRNAME
/*

Run the test like this (expect no output):
  gcc -DHAVE_CONFIG_H -DTEST_DIRNAME -I.. -O -Wall \
     basename.c dirname.c xmalloc.c error.c
  sed -n '/^BEGIN-DATA$/,/^END-DATA$/p' dirname.c|grep -v DATA|./a.out

If it's been built on a DOS or Windows platforms, run another test like
this (again, expect no output):
  sed -n '/^BEGIN-DOS-DATA$/,/^END-DOS-DATA$/p' dirname.c|grep -v DATA|./a.out

BEGIN-DATA
foo//// .
bar/foo//// bar
foo/ .
/ /
. .
a .
END-DATA

BEGIN-DOS-DATA
c:///// c:/
c:/ c:/
c:/. c:/
c:foo c:.
c:foo/bar c:foo
END-DOS-DATA

*/

# define MAX_BUFF_LEN 1024
# include <stdio.h>

char *program_name;

int
main (int argc, char *argv[])
{
  char buff[MAX_BUFF_LEN + 1];

  program_name = argv[0];

  buff[MAX_BUFF_LEN] = 0;
  while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
    {
      char path[MAX_BUFF_LEN];
      char expected_result[MAX_BUFF_LEN];
      char const *result;
      sscanf (buff, "%s %s", path, expected_result);
      result = dir_name (path);
      if (strcmp (result, expected_result))
    printf ("%s: got %s, expected %s\n", path, result, expected_result);
    }
  return 0;
}
#endif

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/inp.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/inp.c used in: Step 566
/* inputting files to be patched */

/* $Id: inp.c,v 1.25 2003/05/20 13:58:02 eggert Exp $ */

/* Copyright (C) 1986, 1988 Larry Wall
   Copyright (C) 1991, 1992, 1993, 1997, 1998, 1999, 2002, 2003 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#define XTERN extern
#include <common.h>
#include <backupfile.h>
#include <pch.h>
#include <quotearg.h>
#include <util.h>
#include <xalloc.h>
#undef XTERN
#define XTERN
#include <inp.h>

/* Input-file-with-indexable-lines abstract type */

static char *i_buffer;          /* plan A buffer */
static char const **i_ptr;      /* pointers to lines in plan A buffer */

static size_t tibufsize;        /* size of plan b buffers */
#ifndef TIBUFSIZE_MINIMUM
#define TIBUFSIZE_MINIMUM (8 * 1024)    /* minimum value for tibufsize */
#endif
static int tifd = -1;           /* plan b virtual string array */
static char *tibuf[2];          /* plan b buffers */
static LINENUM tiline[2] = {-1, -1};    /* 1st line in each buffer */
static LINENUM lines_per_buf;       /* how many lines per buffer */
static size_t tireclen;         /* length of records in tmp file */
static size_t last_line_size;       /* size of last input line */

static bool plan_a (char const *);  /* yield false if memory runs out */
static void plan_b (char const *);
static void report_revision (bool);
static void too_many_lines (char const *) __attribute__((noreturn));

/* New patch--prepare to edit another file. */

void
re_input (void)
{
    if (using_plan_a) {
      if (i_buffer)
    {
      free (i_buffer);
      i_buffer = 0;
      free (i_ptr);
    }
    }
    else {
    close (tifd);
    tifd = -1;
    if (tibuf[0])
      {
        free (tibuf[0]);
        tibuf[0] = 0;
      }
    tiline[0] = tiline[1] = -1;
    tireclen = 0;
    }
}

/* Construct the line index, somehow or other. */

void
scan_input (char *filename)
{
    using_plan_a = ! (debug & 16) && plan_a (filename);
    if (!using_plan_a)
    plan_b(filename);

    if (verbosity != SILENT)
      {
    filename = quotearg (filename);

    if (verbosity == VERBOSE)
      say ("Patching file %s using Plan %s...\n",
           filename, using_plan_a ? "A" : "B");
    else
      say ("patching file %s\n", filename);
      }
}

/* Report whether a desired revision was found.  */

static void
report_revision (bool found_revision)
{
  char const *rev = quotearg (revision);

  if (found_revision)
    {
      if (verbosity == VERBOSE)
    say ("Good.  This file appears to be the %s version.\n", rev);
    }
  else if (force)
    {
      if (verbosity != SILENT)
    say ("Warning: this file doesn't appear to be the %s version -- patching anyway.\n",
         rev);
    }
  else if (batch)
    fatal ("This file doesn't appear to be the %s version -- aborting.",
       rev);
  else
    {
      ask ("This file doesn't appear to be the %s version -- patch anyway? [n] ",
       rev);
      if (*buf != 'y')
    fatal ("aborted");
    }
}


static void
too_many_lines (char const *filename)
{
  fatal ("File %s has too many lines", quotearg (filename));
}


void
get_input_file (char const *filename, char const *outname)
{
    bool elsewhere = strcmp (filename, outname) != 0;
    char const *cs;
    char *diffbuf;
    char *getbuf;

    if (inerrno == -1)
      inerrno = stat (inname, &instat) == 0 ? 0 : errno;

    /* Perhaps look for RCS or SCCS versions.  */
    if (patch_get
    && invc != 0
    && (inerrno
        || (! elsewhere
        && (/* No one can write to it.  */
            (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) == 0
            /* Only the owner (who's not me) can write to it.  */
            || ((instat.st_mode & (S_IWGRP|S_IWOTH)) == 0
            && instat.st_uid != geteuid ()))))
    && (invc = !! (cs = (version_controller
                 (filename, elsewhere,
                  inerrno ? (struct stat *) 0 : &instat,
                  &getbuf, &diffbuf))))) {

        if (!inerrno) {
        if (!elsewhere
            && (instat.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) != 0)
            /* Somebody can write to it.  */
          fatal ("File %s seems to be locked by somebody else under %s",
             quotearg (filename), cs);
        if (diffbuf)
          {
            /* It might be checked out unlocked.  See if it's safe to
               check out the default version locked.  */

            if (verbosity == VERBOSE)
              say ("Comparing file %s to default %s version...\n",
               quotearg (filename), cs);

            if (systemic (diffbuf) != 0)
              {
            say ("warning: Patching file %s, which does not match default %s version\n",
                 quotearg (filename), cs);
            cs = 0;
              }
          }
        }

        if (cs && version_get (filename, cs, ! inerrno, elsewhere, getbuf,
                   &instat))
          inerrno = 0;

        free (getbuf);
        if (diffbuf)
          free (diffbuf);

    } else if (inerrno && !pch_says_nonexistent (reverse))
      {
    errno = inerrno;
    pfatal ("Can't find file %s", quotearg (filename));
      }

    if (inerrno)
      {
    instat.st_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
    instat.st_size = 0;
      }
    else if (! S_ISREG (instat.st_mode))
      fatal ("File %s is not a regular file -- can't patch",
         quotearg (filename));
}


/* Try keeping everything in memory. */

static bool
plan_a (char const *filename)
{
  register char const *s;
  register char const *lim;
  register char const **ptr;
  register char *buffer;
  register LINENUM iline;
  size_t size = instat.st_size;

  /* Fail if the file size doesn't fit in a size_t,
     or if storage isn't available.  */
  if (! (size == instat.st_size
     && (buffer = malloc (size ? size : (size_t) 1))))
    return false;

  /* Read the input file, but don't bother reading it if it's empty.
     When creating files, the files do not actually exist.  */
  if (size)
    {
      int ifd = open (filename, O_RDONLY|binary_transput);
      size_t buffered = 0, n;
      if (ifd < 0)
    pfatal ("can't open file %s", quotearg (filename));

      while (size - buffered != 0)
    {
      n = read (ifd, buffer + buffered, size - buffered);
      if (n == 0)
        {
          /* Some non-POSIX hosts exaggerate st_size in text mode;
         or the file may have shrunk!  */
          size = buffered;
          break;
        }
      if (n == (size_t) -1)
        {
          /* Perhaps size is too large for this host.  */
          close (ifd);
          free (buffer);
          return false;
        }
      buffered += n;
    }

      if (close (ifd) != 0)
    read_fatal ();
    }

  /* Scan the buffer and build array of pointers to lines.  */
  lim = buffer + size;
  iline = 3; /* 1 unused, 1 for SOF, 1 for EOF if last line is incomplete */
  for (s = buffer;  (s = (char *) memchr (s, '\n', lim - s));  s++)
    if (++iline < 0)
      too_many_lines (filename);
  if (! (iline == (size_t) iline
     && (size_t) iline * sizeof *ptr / sizeof *ptr == (size_t) iline
     && (ptr = (char const **) malloc ((size_t) iline * sizeof *ptr))))
    {
      free (buffer);
      return false;
    }
  iline = 0;
  for (s = buffer;  ;  s++)
    {
      ptr[++iline] = s;
      if (! (s = (char *) memchr (s, '\n', lim - s)))
    break;
    }
  if (size && lim[-1] != '\n')
    ptr[++iline] = lim;
  input_lines = iline - 1;

  if (revision)
    {
      char const *rev = revision;
      int rev0 = rev[0];
      bool found_revision = false;
      size_t revlen = strlen (rev);

      if (revlen <= size)
    {
      char const *limrev = lim - revlen;

      for (s = buffer;  (s = (char *) memchr (s, rev0, limrev - s));  s++)
        if (memcmp (s, rev, revlen) == 0
        && (s == buffer || ISSPACE ((unsigned char) s[-1]))
        && (s + 1 == limrev || ISSPACE ((unsigned char) s[revlen])))
          {
        found_revision = true;
        break;
          }
    }

      report_revision (found_revision);
    }

  /* Plan A will work.  */
  i_buffer = buffer;
  i_ptr = ptr;
  return true;
}

/* Keep (virtually) nothing in memory. */

static void
plan_b (char const *filename)
{
  register FILE *ifp;
  register int c;
  register size_t len;
  register size_t maxlen;
  register bool found_revision;
  register size_t i;
  register char const *rev;
  register size_t revlen;
  register LINENUM line = 1;
  int exclusive;

  if (instat.st_size == 0)
    filename = NULL_DEVICE;
  if (! (ifp = fopen (filename, binary_transput ? "rb" : "r")))
    pfatal ("Can't open file %s", quotearg (filename));
  exclusive = TMPINNAME_needs_removal ? 0 : O_EXCL;
  TMPINNAME_needs_removal = 1;
  tifd = create_file (TMPINNAME, O_RDWR | O_BINARY | exclusive, (mode_t) 0);
  i = 0;
  len = 0;
  maxlen = 1;
  rev = revision;
  found_revision = !rev;
  revlen = rev ? strlen (rev) : 0;

  while ((c = getc (ifp)) != EOF)
    {
      len++;

      if (c == '\n')
    {
      if (++line < 0)
        too_many_lines (filename);
      if (maxlen < len)
          maxlen = len;
      len = 0;
    }

      if (!found_revision)
    {
      if (i == revlen)
        {
          found_revision = ISSPACE ((unsigned char) c);
          i = (size_t) -1;
        }
      else if (i != (size_t) -1)
        i = rev[i]==c ? i + 1 : (size_t) -1;

      if (i == (size_t) -1  &&  ISSPACE ((unsigned char) c))
        i = 0;
    }
    }

  if (revision)
    report_revision (found_revision);
  Fseek (ifp, (off_t) 0, SEEK_SET);     /* rewind file */
  for (tibufsize = TIBUFSIZE_MINIMUM;  tibufsize < maxlen;  tibufsize <<= 1)
    continue;
  lines_per_buf = tibufsize / maxlen;
  tireclen = maxlen;
  tibuf[0] = xmalloc (2 * tibufsize);
  tibuf[1] = tibuf[0] + tibufsize;

  for (line = 1; ; line++)
    {
      char *p = tibuf[0] + maxlen * (line % lines_per_buf);
      char const *p0 = p;
      if (! (line % lines_per_buf)) /* new block */
    if (write (tifd, tibuf[0], tibufsize) != tibufsize)
      write_fatal ();
      if ((c = getc (ifp)) == EOF)
    break;

      for (;;)
    {
      *p++ = c;
      if (c == '\n')
        {
          last_line_size = p - p0;
          break;
        }

      if ((c = getc (ifp)) == EOF)
        {
          last_line_size = p - p0;
          line++;
          goto EOF_reached;
        }
    }
    }
 EOF_reached:
  if (ferror (ifp)  ||  fclose (ifp) != 0)
    read_fatal ();

  if (line % lines_per_buf  !=  0)
    if (write (tifd, tibuf[0], tibufsize) != tibufsize)
      write_fatal ();
  input_lines = line - 1;
}

/* Fetch a line from the input file.
   WHICHBUF is ignored when the file is in memory.  */

char const *
ifetch (LINENUM line, bool whichbuf, size_t *psize)
{
    register char const *q;
    register char const *p;

    if (line < 1 || line > input_lines) {
    *psize = 0;
    return "";
    }
    if (using_plan_a) {
    p = i_ptr[line];
    *psize = i_ptr[line + 1] - p;
    return p;
    } else {
    LINENUM offline = line % lines_per_buf;
    LINENUM baseline = line - offline;

    if (tiline[0] == baseline)
        whichbuf = false;
    else if (tiline[1] == baseline)
        whichbuf = true;
    else {
        tiline[whichbuf] = baseline;
        if (lseek (tifd, (off_t) (baseline/lines_per_buf * tibufsize),
               SEEK_SET) == -1
        || read (tifd, tibuf[whichbuf], tibufsize) < 0)
          read_fatal ();
    }
    p = tibuf[whichbuf] + (tireclen*offline);
    if (line == input_lines)
        *psize = last_line_size;
    else {
        for (q = p;  *q++ != '\n';  )
        continue;
        *psize = q - p;
    }
    return p;
    }
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/common.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/common.h used in: Step 566, Step 569, Step 570, Step 574, Step 575
/* common definitions for `patch' */

/* $Id: common.h,v 1.34 2003/05/19 06:57:36 eggert Exp $ */

/* Copyright (C) 1986, 1988 Larry Wall

   Copyright (C) 1990, 1991, 1992, 1993, 1997, 1998, 1999, 2002, 2003
   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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#ifndef DEBUGGING
#define DEBUGGING 1
#endif

#include <config.h>

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <sys/types.h>
#include <time.h>

#include <sys/stat.h>
#if ! defined S_ISDIR && defined S_IFDIR
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#if ! defined S_ISREG && defined S_IFREG
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
#ifndef S_IXOTH
#define S_IXOTH 1
#endif
#ifndef S_IWOTH
#define S_IWOTH 2
#endif
#ifndef S_IROTH
#define S_IROTH 4
#endif
#ifndef S_IXGRP
#define S_IXGRP (S_IXOTH << 3)
#endif
#ifndef S_IWGRP
#define S_IWGRP (S_IWOTH << 3)
#endif
#ifndef S_IRGRP
#define S_IRGRP (S_IROTH << 3)
#endif
#ifndef S_IXUSR
#define S_IXUSR (S_IXOTH << 6)
#endif
#ifndef S_IWUSR
#define S_IWUSR (S_IWOTH << 6)
#endif
#ifndef S_IRUSR
#define S_IRUSR (S_IROTH << 6)
#endif
#ifdef MKDIR_TAKES_ONE_ARG
# define mkdir(name, mode) ((mkdir) (name))
#endif

#if HAVE_LIMITS_H
# include <limits.h>
#endif
#ifndef CHAR_BIT
#define CHAR_BIT 8
#endif
/* The extra casts work around common compiler bugs,
   e.g. Cray C 5.0.3.0 time_t.  */
#define TYPE_SIGNED(t) ((t) -1 < (t) 0)
#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
                  ? (t) (~ (t) 0 << (sizeof (t) * CHAR_BIT - 1)) \
                  : (t) 0))
#define TYPE_MAXIMUM(t) ((t) ((t) ~ (t) 0 - TYPE_MINIMUM (t)))
#ifndef CHAR_MAX
#define CHAR_MAX TYPE_MAXIMUM (char)
#endif
#ifndef INT_MAX
#define INT_MAX TYPE_MAXIMUM (int)
#endif
#ifndef LONG_MIN
#define LONG_MIN TYPE_MINIMUM (long)
#endif

#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#ifndef SIZE_MAX
/* On some nonstandard hosts, size_t is signed,
   so SIZE_MAX != (size_t) -1.  */
#define SIZE_MAX TYPE_MAXIMUM (size_t)
#endif

#include <ctype.h>
/* CTYPE_DOMAIN (C) is nonzero if the unsigned char C can safely be given
   as an argument to <ctype.h> macros like `isspace'.  */
#if STDC_HEADERS
#define CTYPE_DOMAIN(c) 1
#else
#define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177)
#endif
#ifndef ISSPACE
#define ISSPACE(c) (CTYPE_DOMAIN (c) && isspace (c))
#endif

#ifndef ISDIGIT
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
#endif


/* handy definitions */

#define strEQ(s1,s2) (!strcmp(s1, s2))
#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l))

/* typedefs */

typedef off_t LINENUM;          /* must be signed */

/* globals */

XTERN char *program_name;   /* The name this program was run with. */

XTERN char *buf;            /* general purpose buffer */
XTERN size_t bufsize;           /* allocated size of buf */

XTERN bool using_plan_a;        /* try to keep everything in memory */

XTERN char *inname;
XTERN char *outfile;
XTERN int inerrno;
XTERN int invc;
XTERN struct stat instat;
XTERN bool dry_run;
XTERN bool posixly_correct;

XTERN char const *origprae;
XTERN char const *origbase;

XTERN char const * volatile TMPINNAME;
XTERN char const * volatile TMPOUTNAME;
XTERN char const * volatile TMPPATNAME;

XTERN int volatile TMPINNAME_needs_removal;
XTERN int volatile TMPOUTNAME_needs_removal;
XTERN int volatile TMPPATNAME_needs_removal;

#ifdef DEBUGGING
XTERN int debug;
#else
# define debug 0
#endif
XTERN bool force;
XTERN bool batch;
XTERN bool noreverse;
XTERN bool reverse;
XTERN enum { DEFAULT_VERBOSITY, SILENT, VERBOSE } verbosity;
XTERN bool skip_rest_of_patch;
XTERN int strippath;
XTERN bool canonicalize;
XTERN int patch_get;
XTERN bool set_time;
XTERN bool set_utc;

enum diff
  {
    NO_DIFF,
    CONTEXT_DIFF,
    NORMAL_DIFF,
    ED_DIFF,
    NEW_CONTEXT_DIFF,
    UNI_DIFF
  };

XTERN enum diff diff_type;

XTERN char *revision;           /* prerequisite revision, if any */

#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 6) || __STRICT_ANSI__
# define __attribute__(x)
#endif

void fatal_exit (int) __attribute__ ((noreturn));

#include <errno.h>
#if !STDC_HEADERS && !defined errno
extern int errno;
#endif

#if STDC_HEADERS || HAVE_STRING_H
# include <string.h>
#else
# if !HAVE_MEMCHR
#  define memcmp(s1, s2, n) bcmp (s1, s2, n)
#  define memcpy(d, s, n) bcopy (s, d, n)
void *memchr ();
# endif
#endif

#if STDC_HEADERS
# include <stdlib.h>
#else
char *getenv ();
void *malloc ();
void *realloc ();
#endif

#if HAVE_UNISTD_H
# include <unistd.h>
#else
# ifndef lseek
   off_t lseek ();
# endif
#endif
#ifndef SEEK_SET
#define SEEK_SET 0
#endif
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
#define STDOUT_FILENO 1
#endif
#ifndef STDERR_FILENO
#define STDERR_FILENO 2
#endif
#if HAVE_FSEEKO
  typedef off_t file_offset;
# define file_seek fseeko
# define file_tell ftello
#else
  typedef long file_offset;
# define file_seek fseek
# define file_tell ftell
#endif
#if ! (HAVE_GETEUID || defined geteuid)
# if ! (HAVE_GETUID || defined getuid)
#  define geteuid() (-1)
# else
#  define geteuid() getuid ()
# endif
#endif

#if HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifndef O_RDONLY
#define O_RDONLY 0
#endif
#ifndef O_WRONLY
#define O_WRONLY 1
#endif
#ifndef O_RDWR
#define O_RDWR 2
#endif
#ifndef _O_BINARY
#define _O_BINARY 0
#endif
#ifndef O_BINARY
#define O_BINARY _O_BINARY
#endif
#ifndef O_CREAT
#define O_CREAT 0
#endif
#ifndef O_EXCL
#define O_EXCL 0
#endif
#ifndef O_TRUNC
#define O_TRUNC 0
#endif

#if HAVE_SETMODE_DOS
  XTERN int binary_transput;    /* O_BINARY if binary i/o is desired */
#else
# define binary_transput 0
#endif

#ifndef NULL_DEVICE
#define NULL_DEVICE "/dev/null"
#endif

#ifndef TTY_DEVICE
#define TTY_DEVICE "/dev/tty"
#endif

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/backupfile.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/backupfile.h used in: Step 566, Step 569, Step 570, Step 574
/* backupfile.h -- declarations for making Emacs style backup file names
   Copyright (C) 1990-1992, 1997-1999 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#ifndef BACKUPFILE_H_
# define BACKUPFILE_H_

/* When to make backup files. */
enum backup_type
{
  /* Never make backups. */
  none,

  /* Make simple backups of every file. */
  simple,

  /* Make numbered backups of files that already have numbered backups,
     and simple backups of the others. */
  numbered_existing,

  /* Make numbered backups of every file. */
  numbered
};

# define VALID_BACKUP_TYPE(Type)    \
  ((Type) == none           \
   || (Type) == simple          \
   || (Type) == numbered_existing   \
   || (Type) == numbered)

extern char const *simple_backup_suffix;

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

char *find_backup_file_name PARAMS ((char const *, enum backup_type));
enum backup_type get_version PARAMS ((char const *context, char const *arg));
enum backup_type xget_version PARAMS ((char const *context, char const *arg));
void addext PARAMS ((char *, char const *, int));

#endif /* ! BACKUPFILE_H_ */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/pch.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/pch.h used in: Step 566, Step 569, Step 570
/* reading patches */

/* $Id: pch.h,v 1.11 2003/05/20 13:56:03 eggert Exp $ */

/* Copyright (C) 1986, 1987, 1988 Larry Wall

   Copyright (C) 1990, 1991, 1992, 1993, 1997, 1998, 1999, 2000, 2001,
   2002, 2003 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

LINENUM pch_end (void);
LINENUM pch_first (void);
LINENUM pch_hunk_beg (void);
LINENUM pch_newfirst (void);
LINENUM pch_prefix_context (void);
LINENUM pch_ptrn_lines (void);
LINENUM pch_repl_lines (void);
LINENUM pch_suffix_context (void);
bool pch_swap (void);
bool pch_write_line (LINENUM, FILE *);
bool there_is_another_patch (void);
char *pfetch (LINENUM);
char pch_char (LINENUM);
int another_hunk (enum diff, bool);
int pch_says_nonexistent (bool);
size_t pch_line_len (LINENUM);
time_t pch_timestamp (bool);
void do_ed_script (FILE *);
void open_patch_file (char const *);
void re_patch (void);
void set_hunkmax (void);

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/quotearg.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/quotearg.h used in: Step 566, Step 569, Step 570, Step 574
/* quotearg.h - quote arguments for output

   Copyright (C) 1998, 1999, 2000, 2001, 2002 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.  */

/* Written by Paul Eggert <eggert@twinsun.com> */

#ifndef QUOTEARG_H_
# define QUOTEARG_H_ 1

# include <stddef.h>

/* Basic quoting styles.  */
enum quoting_style
  {
    literal_quoting_style,  /* --quoting-style=literal */
    shell_quoting_style,    /* --quoting-style=shell */
    shell_always_quoting_style, /* --quoting-style=shell-always */
    c_quoting_style,        /* --quoting-style=c */
    escape_quoting_style,   /* --quoting-style=escape */
    locale_quoting_style,   /* --quoting-style=locale */
    clocale_quoting_style   /* --quoting-style=clocale */
  };

/* For now, --quoting-style=literal is the default, but this may change.  */
# ifndef DEFAULT_QUOTING_STYLE
#  define DEFAULT_QUOTING_STYLE literal_quoting_style
# endif

/* Names of quoting styles and their corresponding values.  */
extern char const *const quoting_style_args[];
extern enum quoting_style const quoting_style_vals[];

struct quoting_options;

/* The functions listed below set and use a hidden variable
   that contains the default quoting style options.  */

/* Allocate a new set of quoting options, with contents initially identical
   to O if O is not null, or to the default if O is null.
   It is the caller's responsibility to free the result.  */
struct quoting_options *clone_quoting_options (struct quoting_options *o);

/* Get the value of O's quoting style.  If O is null, use the default.  */
enum quoting_style get_quoting_style (struct quoting_options *o);

/* In O (or in the default if O is null),
   set the value of the quoting style to S.  */
void set_quoting_style (struct quoting_options *o, enum quoting_style s);

/* In O (or in the default if O is null),
   set the value of the quoting options for character C to I.
   Return the old value.  Currently, the only values defined for I are
   0 (the default) and 1 (which means to quote the character even if
   it would not otherwise be quoted).  */
int set_char_quoting (struct quoting_options *o, char c, int i);

/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
   argument ARG (of size ARGSIZE), using O to control quoting.
   If O is null, use the default.
   Terminate the output with a null character, and return the written
   size of the output, not counting the terminating null.
   If BUFFERSIZE is too small to store the output string, return the
   value that would have been returned had BUFFERSIZE been large enough.
   If ARGSIZE is -1, use the string length of the argument for ARGSIZE.  */
size_t quotearg_buffer (char *buffer, size_t buffersize,
            char const *arg, size_t argsize,
            struct quoting_options const *o);

/* Use storage slot N to return a quoted version of the string ARG.
   Use the default quoting options.
   The returned value points to static storage that can be
   reused by the next call to this function with the same value of N.
   N must be nonnegative.  */
char *quotearg_n (int n, char const *arg);

/* Equivalent to quotearg_n (0, ARG).  */
char *quotearg (char const *arg);

/* Use style S and storage slot N to return a quoted version of the string ARG.
   This is like quotearg_n (N, ARG), except that it uses S with no other
   options to specify the quoting method.  */
char *quotearg_n_style (int n, enum quoting_style s, char const *arg);

/* Use style S and storage slot N to return a quoted version of the
   argument ARG of size ARGSIZE.  This is like quotearg_n_style
   (N, S, ARG), except it can quote null bytes.  */
char *quotearg_n_style_mem (int n, enum quoting_style s,
                char const *arg, size_t argsize);

/* Equivalent to quotearg_n_style (0, S, ARG).  */
char *quotearg_style (enum quoting_style s, char const *arg);

/* Like quotearg (ARG), except also quote any instances of CH.  */
char *quotearg_char (char const *arg, char ch);

/* Equivalent to quotearg_char (ARG, ':').  */
char *quotearg_colon (char const *arg);

#endif /* !QUOTEARG_H_ */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/util.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/util.h used in: Step 566, Step 569, Step 570, Step 574
/* utility functions for `patch' */

/* $Id: util.h,v 1.20 2003/05/20 13:56:48 eggert Exp $ */

/* Copyright (C) 1986 Larry Wall

   Copyright (C) 1992, 1993, 1997, 1998, 1999, 2001, 2002, 2003 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* An upper bound on the print length of a signed decimal line number.
   Add one for the sign.  */
#define LINENUM_LENGTH_BOUND (sizeof (LINENUM) * CHAR_BIT / 3 + 1)

XTERN enum backup_type backup_type;

bool ok_to_reverse (char const *, ...) __attribute__ ((format (printf, 1, 2)));
void ask (char const *, ...) __attribute__ ((format (printf, 1, 2)));
void say (char const *, ...) __attribute__ ((format (printf, 1, 2)));

void fatal (char const *, ...)
    __attribute__ ((noreturn, format (printf, 1, 2)));
void pfatal (char const *, ...)
    __attribute__ ((noreturn, format (printf, 1, 2)));

char *fetchname (char *, int, time_t *);
char *savebuf (char const *, size_t);
char *savestr (char const *);
char const *version_controller (char const *, bool, struct stat const *, char **, char **);
bool version_get (char const *, char const *, bool, bool, char const *, struct stat *);
int create_file (char const *, int, mode_t);
int systemic (char const *);
char *format_linenum (char[LINENUM_LENGTH_BOUND + 1], LINENUM);
void Fseek (FILE *, file_offset, int);
void copy_file (char const *, char const *, int, mode_t);
void exit_with_signal (int) __attribute__ ((noreturn));
void ignore_signals (void);
void init_time (void);
void memory_fatal (void) __attribute__ ((noreturn));
void move_file (char const *, int volatile *, char *, mode_t, bool);
void read_fatal (void) __attribute__ ((noreturn));
void remove_prefix (char *, size_t);
void removedirs (char *);
void set_signals (bool);
void write_fatal (void) __attribute__ ((noreturn));

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/xalloc.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/xalloc.h used in: Step 566, Step 569, Step 574
/* xalloc.h -- malloc with out-of-memory checking
   Copyright (C) 1990-1998, 1999, 2000 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.  */

#ifndef XALLOC_H_
# define XALLOC_H_

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

# ifndef __attribute__
#  if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
#   define __attribute__(x)
#  endif
# endif

# ifndef ATTRIBUTE_NORETURN
#  define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__))
# endif

/* Exit value when the requested amount of memory is not available.
   It is initialized to EXIT_FAILURE, but the caller may set it to
   some other value.  */
extern int xalloc_exit_failure;

/* If this pointer is non-zero, run the specified function upon each
   allocation failure.  It is initialized to zero. */
extern void (*xalloc_fail_func) PARAMS ((void));

/* If XALLOC_FAIL_FUNC is undefined or a function that returns, this
   message is output.  It is translated via gettext.
   Its value is "memory exhausted".  */
extern char const xalloc_msg_memory_exhausted[];

/* This function is always triggered when memory is exhausted.  It is
   in charge of honoring the three previous items.  This is the
   function to call when one wants the program to die because of a
   memory allocation failure.  */
extern void xalloc_die PARAMS ((void)) ATTRIBUTE_NORETURN;

void *xmalloc PARAMS ((size_t n));
void *xcalloc PARAMS ((size_t n, size_t s));
void *xrealloc PARAMS ((void *p, size_t n));
char *xstrdup PARAMS ((const char *str));

# define XMALLOC(Type, N_items) ((Type *) xmalloc (sizeof (Type) * (N_items)))
# define XCALLOC(Type, N_items) ((Type *) xcalloc (sizeof (Type), (N_items)))
# define XREALLOC(Ptr, Type, N_items) \
  ((Type *) xrealloc ((void *) (Ptr), sizeof (Type) * (N_items)))

/* Declare and alloc memory for VAR of type TYPE. */
# define NEW(Type, Var)  Type *(Var) = XMALLOC (Type, 1)

/* Free VAR only if non NULL. */
# define XFREE(Var) \
   do {                 \
      if (Var)          \
        free (Var);     \
   } while (0)

/* Return a pointer to a malloc'ed copy of the array SRC of NUM elements. */
# define CCLONE(Src, Num) \
  (memcpy (xmalloc (sizeof (*Src) * (Num)), (Src), sizeof (*Src) * (Num)))

/* Return a malloc'ed copy of SRC. */
# define CLONE(Src) CCLONE (Src, 1)


#endif /* !XALLOC_H_ */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/inp.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/inp.h used in: Step 566, Step 569, Step 570
/* inputting files to be patched */

/* $Id: inp.h,v 1.7 2003/05/20 14:05:22 eggert Exp $ */

/* Copyright (C) 1986, 1988 Larry Wall
   Copyright (C) 1991, 1992, 1993, 1997, 1998, 1999, 2002, 2003
   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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

XTERN LINENUM input_lines;      /* how long is input file in lines */

char const *ifetch (LINENUM, bool, size_t *);
void get_input_file (char const *, char const *);
void re_input (void);
void scan_input (char *);

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/maketime.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/maketime.c used in: Step 567
/* Convert struct partime into time_t.  */

/* Copyright 1992, 1993, 1994, 1995, 1997 Paul Eggert
   Distributed under license by the Free Software Foundation, Inc.

   This file is part of RCS.

   RCS 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.

   RCS 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 RCS; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   Report problems and direct all questions to:

    rcs-bugs@cs.purdue.edu

 */

/* For maximum portability, use only localtime and gmtime.
   Make no assumptions about the time_t epoch or the range of time_t values.
   Avoid mktime because it's not universal and because there's no easy,
   portable way for mktime to yield the inverse of gmtime.  */

#if has_conf_h
# include <conf.h>
#else
# if HAVE_CONFIG_H
#  include <config.h>
# else
#  ifndef __STDC__
#   define const
#  endif
# endif
  /* MIPS RISCOS4.52 defines time_t in <sys/types.h> not <time.h>.  */
# include <sys/types.h>
# if HAVE_LIMITS_H
#  include <limits.h>
# endif
# ifndef LONG_MIN
# define LONG_MIN (-1-2147483647L)
# endif
# if STDC_HEADERS
#  include <stdlib.h>
# endif
# include <time.h>
# ifdef __STDC__
#  define P(x) x
# else
#  define P(x) ()
# endif
#endif

#include <partime.h>
#include <maketime.h>

char const maket_id[] =
  "$Id: maketime.c,v 5.17 1999/08/29 11:12:37 eggert Exp $";

static int isleap P ((int));
static int month_days P ((struct tm const *));
static time_t maketime P ((struct partime const *, time_t));

/* Suppose A1 + B1 = SUM1, using 2's complement arithmetic ignoring overflow.
   Suppose A, B and SUM have the same respective signs as A1, B1, and SUM1.
   Then this yields nonzero if overflow occurred during the addition.
   Overflow occurs if A and B have the same sign, but A and SUM differ in sign.
   Use `^' to test whether signs differ, and `< 0' to isolate the sign.  */
#define overflow_sum_sign(a, b, sum) ((~((a) ^ (b)) & ((a) ^ (sum))) < 0)

/* Quotient and remainder when dividing A by B,
   truncating towards minus infinity, where B is positive.  */
#define DIV(a, b) ((a) / (b) - ((a) % (b) < 0))
#define MOD(a, b) ((a) % (b) + (b) * ((a) % (b) < 0))

/* Number of days in 400 consecutive Gregorian years.  */
#define Y400_DAYS (365 * 400L + 100 - 4 + 1)

/* Number of years to add to tm_year to get Gregorian year.  */
#define TM_YEAR_ORIGIN 1900

static int
isleap (y)
     int y;
{
  return (y & 3) == 0 && (y % 100 != 0 || y % 400 == 0);
}

/* days in year before start of months 0-12 */
static int const month_yday[] =
{
  0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
};

/* Yield the number of days in TM's month.  */
static int
month_days (tm)
     struct tm const *tm;
{
  int m = tm->tm_mon;
  return (month_yday[m + 1] - month_yday[m]
      + (m == 1 && isleap (tm->tm_year + TM_YEAR_ORIGIN)));
}

/* Convert UNIXTIME to struct tm form.
   Use gmtime if available and if !LOCALZONE, localtime otherwise.  */
struct tm *
time2tm (unixtime, localzone)
     time_t unixtime;
     int localzone;
{
  struct tm *tm;
#ifdef TZ_is_unset
  static char const *TZ;
  if (!TZ && !(TZ = getenv ("TZ")))
    TZ_is_unset ("The TZ environment variable is not set; please set it to your timezone");
#endif
  if (localzone || !(tm = gmtime (&unixtime)))
    tm = localtime (&unixtime);
  return tm;
}

/* Yield A - B, measured in seconds.  */
time_t
difftm (a, b)
     struct tm const *a;
     struct tm const *b;
{
  int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
  int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
  int ac = DIV (ay, 100);
  int bc = DIV (by, 100);
  int difference_in_day_of_year = a->tm_yday - b->tm_yday;
  int intervening_leap_days = (((ay >> 2) - (by >> 2))
                   - (ac - bc)
                   + ((ac >> 2) - (bc >> 2)));
  time_t difference_in_years = ay - by;
  time_t difference_in_days
    = (difference_in_years * 365
       + (intervening_leap_days + difference_in_day_of_year));
  return (((((difference_in_days * 24
          + (a->tm_hour - b->tm_hour))
         * 60)
        + (a->tm_min - b->tm_min))
       * 60)
      + (a->tm_sec - b->tm_sec));
}

/* Adjust time T by adding SECONDS.
   The absolute value of SECONDS cannot exceed 59 * INT_MAX,
   and also cannot exceed one month's worth of seconds;
   this is enough to handle any POSIX or real-life daylight-saving offset.
   Adjust only T's year, mon, mday, hour, min and sec members;
   plus adjust wday if it is defined.  */
void
adjzone (t, seconds)
     register struct tm *t;
     long seconds;
{
  int days = 0;

  /* This code can be off by a second if SECONDS is not a multiple of 60,
     if T is local time, and if a leap second happens during this minute.
     But this bug has never occurred, and most likely will not ever occur.
     Liberia, the last country for which SECONDS % 60 was nonzero,
     switched to UTC in May 1972; the first leap second was in June 1972.  */
  int leap_second = t->tm_sec == 60;
  long sec = seconds + (t->tm_sec - leap_second);
  if (sec < 0)
    {
      if ((t->tm_min -= (59 - sec) / 60) < 0
      && (t->tm_hour -= (59 - t->tm_min) / 60) < 0)
    {
      days = - ((23 - t->tm_hour) / 24);
      if ((t->tm_mday += days) <= 0)
        {
          if (--t->tm_mon < 0)
        {
          --t->tm_year;
          t->tm_mon = 11;
        }
          t->tm_mday += month_days (t);
        }
    }
    }
  else
    {
      if (60 <= (t->tm_min += sec / 60)
      && (24 <= (t->tm_hour += t->tm_min / 60)))
    {
      days = t->tm_hour / 24;
      if (month_days (t) < (t->tm_mday += days))
        {
          if (11 < ++t->tm_mon)
        {
          ++t->tm_year;
          t->tm_mon = 0;
        }
          t->tm_mday = 1;
        }
    }
    }
  if (TM_DEFINED (t->tm_wday))
    t->tm_wday = MOD (t->tm_wday + days, 7);
  t->tm_hour = MOD (t->tm_hour, 24);
  t->tm_min = MOD (t->tm_min, 60);
  t->tm_sec = (int) MOD (sec, 60) + leap_second;
}

/* Convert TM to time_t, using localtime if LOCALZONE and gmtime otherwise.
   Use only TM's year, mon, mday, hour, min, and sec members.
   Ignore TM's old tm_yday and tm_wday, but fill in their correct values.
   Yield -1 on failure (e.g. a member out of range).
   POSIX 1003.1 doesn't allow leap seconds, but some implementations
   have them anyway, so allow them if localtime/gmtime does.  */
time_t
tm2time (tm, localzone)
     struct tm *tm;
     int localzone;
{
  /* Cache the most recent t,tm pairs; 1 for gmtime, 1 for localtime.  */
  static time_t t_cache[2];
  static struct tm tm_cache[2];

  time_t d, gt;
  struct tm const *gtm;
  /* The maximum number of iterations should be enough to handle any
     combinations of leap seconds, time zone rule changes, and solar time.
     4 is probably enough; we use a bigger number just to be safe.  */
  int remaining_tries = 8;

  /* Avoid subscript errors.  */
  if (12 <= (unsigned) tm->tm_mon)
    return -1;

  tm->tm_yday = month_yday[tm->tm_mon] + tm->tm_mday
    - (tm->tm_mon < 2 || !isleap (tm->tm_year + TM_YEAR_ORIGIN));

  /* Make a first guess.  */
  gt = t_cache[localzone];
  gtm = gt ? &tm_cache[localzone] : time2tm (gt, localzone);

  /* Repeatedly use the error from the guess to improve the guess.  */
  while ((d = difftm (tm, gtm)) != 0)
    {
      if (--remaining_tries == 0)
    return -1;
      gt += d;
      gtm = time2tm (gt, localzone);
    }

  /* Check that the guess actually matches;
     overflow can cause difftm to yield 0 even on differing times,
     or tm may have members out of range (e.g. bad leap seconds).  */
#define TM_DIFFER(a,b) \
        ( \
            ((a)->tm_year ^ (b)->tm_year) | \
            ((a)->tm_mon ^ (b)->tm_mon) | \
            ((a)->tm_mday ^ (b)->tm_mday) | \
            ((a)->tm_hour ^ (b)->tm_hour) | \
            ((a)->tm_min ^ (b)->tm_min) | \
            ((a)->tm_sec ^ (b)->tm_sec) \
        )
  if (TM_DIFFER (tm, gtm))
    {
      /* If gt is a leap second, try gt+1; if it is one greater than
         a leap second, try gt-1; otherwise, it doesn't matter.
         Leap seconds always fall at month end.  */
      int yd = tm->tm_year - gtm->tm_year;
      gt += yd + (yd ? 0 : tm->tm_mon - gtm->tm_mon);
      gtm = time2tm (gt, localzone);
      if (TM_DIFFER (tm, gtm))
    return -1;
    }
  t_cache[localzone] = gt;
  tm_cache[localzone] = *gtm;

  tm->tm_wday = gtm->tm_wday;
  return gt;
}

/* Check *PT and convert it to time_t.
   If it is incompletely specified, use DEFAULT_TIME to fill it out.
   Use localtime if PT->zone is the special value TM_LOCAL_ZONE.
   Yield -1 on failure.
   ISO 8601 day-of-year and week numbers are not yet supported.  */
static time_t
maketime (pt, default_time)
     struct partime const *pt;
     time_t default_time;
{
  int localzone, wday, year;
  struct tm tm;
  struct tm *tm0 = 0;
  time_t r;
  int use_ordinal_day;

  tm0 = 0;          /* Keep gcc -Wall happy.  */
  localzone = pt->zone == TM_LOCAL_ZONE;

  tm = pt->tm;
  year = tm.tm_year;
  wday = tm.tm_wday;
  use_ordinal_day = (!TM_DEFINED (tm.tm_mday)
             && TM_DEFINED (wday) && TM_DEFINED (pt->wday_ordinal));

  if (use_ordinal_day || TM_DEFINED (pt->ymodulus) || !TM_DEFINED (year))
    {
      /* Get tm corresponding to default time.  */
      tm0 = time2tm (default_time, localzone);
      if (!localzone)
    adjzone (tm0, pt->zone);
    }

  if (use_ordinal_day)
    tm.tm_mday = (tm0->tm_mday
          + ((wday - tm0->tm_wday + 7) % 7
             + 7 * (pt->wday_ordinal - (pt->wday_ordinal != 0))));

  if (TM_DEFINED (pt->ymodulus))
    {
      /* Yield a year closest to the default that has the given modulus.  */
      int year0 = tm0->tm_year + TM_YEAR_ORIGIN;
      int y0 = MOD (year0, pt->ymodulus);
      int d = 2 * (year - y0);
      year += (((year0 - y0) / pt->ymodulus
            + (pt->ymodulus < d ? -1 : d < -pt->ymodulus))
           * pt->ymodulus);
    }
  else if (!TM_DEFINED (year))
    {
      /* Set default year, month, day from current time.  */
      year = tm0->tm_year + TM_YEAR_ORIGIN;
      if (!TM_DEFINED (tm.tm_mon))
    {
      tm.tm_mon = tm0->tm_mon;
      if (!TM_DEFINED (tm.tm_mday))
        tm.tm_mday = tm0->tm_mday;
    }
    }

  /* Set remaining default fields to be their minimum values.  */
  if (!TM_DEFINED (tm.tm_mon))
    tm.tm_mon = 0;
  if (!TM_DEFINED (tm.tm_mday))
    tm.tm_mday = 1;
  if (!TM_DEFINED (tm.tm_hour))
    tm.tm_hour = 0;
  if (!TM_DEFINED (tm.tm_min))
    tm.tm_min = 0;
  if (!TM_DEFINED (tm.tm_sec))
    tm.tm_sec = 0;

  tm.tm_year = year - TM_YEAR_ORIGIN;
  if ((year < tm.tm_year) != (TM_YEAR_ORIGIN < 0))
    return -1;

  if (!localzone)
    {
      adjzone (&tm, -pt->zone);
      wday = tm.tm_wday;
    }

  /* Convert and fill in the rest of the tm.  */
  r = tm2time (&tm, localzone);
  if (r == -1)
    return r;

  /* Check weekday.  */
  if (TM_DEFINED (wday) && wday != tm.tm_wday)
    return -1;

  /* Add relative time, except for seconds.
     We handle seconds separately, at the end,
     so that leap seconds are handled properly.  */
  if (pt->tmr.tm_year | pt->tmr.tm_mon | pt->tmr.tm_mday
      | pt->tmr.tm_hour | pt->tmr.tm_min)
    {
      int years = tm.tm_year + pt->tmr.tm_year;
      int mons = tm.tm_mon + pt->tmr.tm_mon;
      int mdays = tm.tm_mday + pt->tmr.tm_mday;
      int hours = tm.tm_hour + pt->tmr.tm_hour;
      int mins = tm.tm_min + pt->tmr.tm_min;

      int carried_hours = DIV (mins, 60);
      int hours1 = hours + carried_hours;
      int carried_days = DIV (hours1, 24);
      int mdays1 = mdays + carried_days;

      int mon0 = MOD (mons, 12);
      int carried_years0 = DIV (mons, 12);
      int year0 = years + carried_years0;
      int yday0 = (month_yday[mon0]
           - (mon0 < 2 || !isleap (year0 + TM_YEAR_ORIGIN)));

      int yday1 = yday0 + mdays1;
      int carried_years1 = DIV (yday1, Y400_DAYS) * 400;
      int year1 = year0 + carried_years1;
      int yday2 = MOD (yday1, Y400_DAYS);
      int leap;

      if (overflow_sum_sign (tm.tm_year, pt->tmr.tm_year, years)
      | overflow_sum_sign (tm.tm_mon, pt->tmr.tm_mon, mons)
      | overflow_sum_sign (tm.tm_mday, pt->tmr.tm_mday, mdays)
      | overflow_sum_sign (tm.tm_hour, pt->tmr.tm_hour, hours)
      | overflow_sum_sign (tm.tm_min, pt->tmr.tm_min, mins)
      | overflow_sum_sign (hours, carried_hours, hours1)
      | overflow_sum_sign (mdays, carried_days, mdays1)
      | overflow_sum_sign (years, carried_years0, year0)
      | overflow_sum_sign (yday0, mdays1, yday1)
      | overflow_sum_sign (year0, carried_years1, year1))
    return -1;

      for (;;)
    {
      int days_per_year = 365 + (leap = isleap (year1 + TM_YEAR_ORIGIN));
      if (yday2 < days_per_year)
        break;
      yday2 -= days_per_year;
      year1++;
    }

      tm.tm_year = year1;

      {
    int mon;
    for (mon = 11;
         (tm.tm_mday = (yday2 - month_yday[mon] + (mon < 2 || !leap))) <= 0;
         mon--)
      continue;
    tm.tm_mon = mon;
      }

      tm.tm_hour = MOD (hours1, 24);
      tm.tm_min = MOD (mins, 60);

      r = tm2time (&tm, localzone);
      if (r == -1)
    return r;
    }

  /* Add the seconds' part of relative time.  */
  {
    time_t rs = r + pt->tmr.tm_sec;
    if ((pt->tmr.tm_sec < 0) != (rs < r))
      return -1;
    return rs;
  }
}

/* Parse a free-format date in *SOURCE, yielding a Unix format time.
   Update *SOURCE to point to the first character after the date.
   If *SOURCE is missing some information, take defaults from
   DEFAULT_TIME and DEFAULT_ZONE.  *SOURCE may even be the empty
   string or an immediately invalid string, in which case the default
   time and zone is used.
   Return (time_t) -1 if the time is invalid or cannot be represented.  */
time_t
str2time (source, default_time, default_zone)
     char const **source;
     time_t default_time;
     long default_zone;
{
  struct partime pt;

  *source = partime (*source, &pt);
  if (pt.zone == TM_UNDEFINED_ZONE)
    pt.zone = default_zone;
  return maketime (&pt, default_time);
}

#ifdef TEST
#include <stdio.h>
int
main (argc, argv)
     int argc;
     char **argv;
{
  time_t default_time = time ((time_t *) 0);
  long default_zone = argv[1] ? atol (argv[1]) : TM_LOCAL_ZONE;
  char buf[1000];
  while (fgets (buf, sizeof (buf), stdin))
    {
      char const *p = buf;
      time_t t = str2time (&p, default_time, default_zone);
      printf ("`%.*s' -> %s",
          (int) (p - buf - (p[0] == '\0' && p[-1] == '\n')), buf,
          asctime ((argv[1] ? gmtime : localtime) (&t)));
    }
  return 0;
}
#endif

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/partime.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/partime.c used in: Step 568
/* Parse a string, yielding a struct partime that describes it.  */

/* Copyright (C) 1993, 1994, 1995, 1997, 2002 Paul Eggert
   Distributed under license by the Free Software Foundation, Inc.

   This file is part of RCS.

   RCS 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.

   RCS 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 RCS; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   Report problems and direct all questions to:

    rcs-bugs@cs.purdue.edu

 */

#if has_conf_h
# include <conf.h>
#else
# if HAVE_CONFIG_H
#  include <config.h>
# else
#  ifndef __STDC__
#   define const
#  endif
# endif
# if HAVE_LIMITS_H
#  include <limits.h>
# endif
# ifndef LONG_MIN
# define LONG_MIN (-1-2147483647L)
# endif
# if HAVE_STDDEF_H
#  include <stddef.h>
# endif
# if STDC_HEADERS
#  include <stdlib.h>
# endif
# include <time.h>
# ifdef __STDC__
#  define P(x) x
# else
#  define P(x) ()
# endif
#endif

#ifndef offsetof
#define offsetof(aggregate, member) ((size_t) &((aggregate *) 0)->member)
#endif

#include <ctype.h>
#if STDC_HEADERS
# define CTYPE_DOMAIN(c) 1
#else
# define CTYPE_DOMAIN(c) ((unsigned) (c) <= 0177)
#endif
#define ISALNUM(c)  (CTYPE_DOMAIN (c) && isalnum (c))
#define ISALPHA(c)  (CTYPE_DOMAIN (c) && isalpha (c))
#define ISSPACE(c)  (CTYPE_DOMAIN (c) && isspace (c))
#define ISUPPER(c)  (CTYPE_DOMAIN (c) && isupper (c))
#define ISDIGIT(c)  ((unsigned) (c) - '0' <= 9)

#include <partime.h>

char const partime_id[] =
  "$Id: partime.c,v 1.2 2002/02/18 07:42:58 eggert Exp $";


/* Lookup tables for names of months, weekdays, time zones.  */

#define NAME_LENGTH_MAXIMUM 4

struct name_val
  {
    char name[NAME_LENGTH_MAXIMUM];
    int val;
  };


static char const *parse_decimal P ((char const *, int, int, int, int, int *, int *));
static char const *parse_fixed P ((char const *, int, int *));
static char const *parse_pattern_letter P ((char const *, int, struct partime *));
static char const *parse_prefix P ((char const *, char const **, struct partime *));
static char const *parse_ranged P ((char const *, int, int, int, int *));
static char const *parse_varying P ((char const *, int *));
static int lookup P ((char const *, struct name_val const[]));
static int merge_partime P ((struct partime *, struct partime const *));
static void undefine P ((struct partime *));


static struct name_val const month_names[] =
{
  {"jan", 0},
  {"feb", 1},
  {"mar", 2},
  {"apr", 3},
  {"may", 4},
  {"jun", 5},
  {"jul", 6},
  {"aug", 7},
  {"sep", 8},
  {"oct", 9},
  {"nov", 10},
  {"dec", 11},
  {"", TM_UNDEFINED}
};

static struct name_val const weekday_names[] =
{
  {"sun", 0},
  {"mon", 1},
  {"tue", 2},
  {"wed", 3},
  {"thu", 4},
  {"fri", 5},
  {"sat", 6},
  {"", TM_UNDEFINED}
};

#define RELATIVE_CONS(member, multiplier)   \
    (offsetof (struct tm, member) + (multiplier) * sizeof (struct tm))
#define RELATIVE_OFFSET(c)  ((c) % sizeof (struct tm))
#define RELATIVE_MULTIPLIER(c)  ((c) / sizeof (struct tm))
static struct name_val const relative_units[] =
{
  {"year", RELATIVE_CONS (tm_year,  1) },
  {"mont", RELATIVE_CONS (tm_mon ,  1) },
  {"fort", RELATIVE_CONS (tm_mday, 14) },
  {"week", RELATIVE_CONS (tm_mday,  7) },
  {"day" , RELATIVE_CONS (tm_mday,  1) },
  {"hour", RELATIVE_CONS (tm_hour,  1) },
  {"min" , RELATIVE_CONS (tm_min ,  1) },
  {"sec" , RELATIVE_CONS (tm_sec ,  1) },
  {"", TM_UNDEFINED}
};

static struct name_val const ago[] =
{
  {"ago", 0},
  {"", TM_UNDEFINED}
};

static struct name_val const dst_names[] =
{
  {"dst", 1},
  {"", 0}
};

#define hr60nonnegative(t)  ((t)/100 * 60  +  (t)%100)
#define hr60(t) ((t) < 0 ? - hr60nonnegative (-(t)) : hr60nonnegative (t))
#define zs(t, s)    {s, hr60 (t)}
#define zd(t, s, d) zs (t, s),  zs ((t) + 100, d)

static struct name_val const zone_names[] =
{
  zs (-1000, "hst"),        /* Hawaii */
  zd (-1000, "hast", "hadt"),   /* Hawaii-Aleutian */
  zd (- 900, "akst", "akdt"),   /* Alaska */
  zd (- 800, "pst" , "pdt" ),   /* Pacific */
  zd (- 700, "mst" , "mdt" ),   /* Mountain */
  zd (- 600, "cst" , "cdt" ),   /* Central */
  zd (- 500, "est" , "edt" ),   /* Eastern */
  zd (- 400, "ast" , "adt" ),   /* Atlantic */
  zd (- 330, "nst" , "ndt" ),   /* Newfoundland */
  zs (  000, "utc" ),       /* Coordinated Universal */
  zs (  000, "uct" ),       /* " */
  zs (  000, "cut" ),       /* " */
  zs (  000, "ut"),     /* Universal */
  zs (  000, "z"),      /* Zulu (required by ISO 8601) */
  zd (  000, "gmt" , "bst" ),   /* Greenwich Mean, British Summer */
  zd (  000, "wet" , "west"),   /* Western European */
  zd (  100, "cet" , "cest"),   /* Central European */
  zd (  100, "met" , "mest"),   /* Middle European (bug in old tz versions) */
  zd (  100, "mez" , "mesz"),   /* Mittel-Europaeische Zeit */
  zd (  200, "eet" , "eest"),   /* Eastern European */
  zs (  530, "ist" ),       /* India */
  zd (  900, "jst" , "jdt" ),   /* Japan */
  zd (  900, "kst" , "kdt" ),   /* Korea */
  zd ( 1200, "nzst", "nzdt"),   /* New Zealand */
  {"lt", 1},
#if 0
  /* The following names are duplicates or are not well attested.
     It's not worth keeping a complete list, since alphabetic time zone names
     are deprecated and there are lots more where these came from.  */
  zs (-1100, "sst" ),       /* Samoan */
  zd (- 900, "yst" , "ydt" ),   /* Yukon - name is no longer used */
  zd (- 500, "ast" , "adt" ),   /* Acre */
  zd (- 400, "wst" , "wdt" ),   /* Western Brazil */
  zd (- 400, "cst" , "cdt" ),   /* Chile */
  zd (- 200, "fst" , "fdt" ),   /* Fernando de Noronha */
  zs (  000, "wat" ),       /* West African */
  zs (  100, "cat" ),       /* Central African */
  zs (  200, "sat" ),       /* South African */
  zd (  200, "ist" , "idt" ),   /* Israel */
  zs (  300, "eat" ),       /* East African */
  zd (  300, "msk" , "msd" ),   /* Moscow */
  zd (  330, "ist" , "idt" ),   /* Iran */
  zs (  800, "hkt" ),       /* Hong Kong */
  zs (  800, "sgt" ),       /* Singapore */
  zd (  800, "cst" , "cdt" ),   /* China */
  zd (  800, "wst" , "wst" ),   /* Western Australia */
  zd (  930, "cst" , "cst" ),   /* Central Australia */
  zs ( 1000, "gst" ),       /* Guam */
  zd ( 1000, "est" , "est" ),   /* Eastern Australia */
#endif
  {"", -1}
};

/* Look for a prefix of S in TABLE, returning val for first matching entry.  */
static int
lookup (s, table)
     char const *s;
     struct name_val const table[];
{
  int j;
  char buf[NAME_LENGTH_MAXIMUM];

  for (j = 0; j < NAME_LENGTH_MAXIMUM; j++)
    {
      unsigned char c = *s;
      if (! ISALPHA (c))
    {
      buf[j] = '\0';
      break;
    }
      buf[j] = ISUPPER (c) ? tolower (c) : c;
      s++;
      s += *s == '.';
    }

  for (;; table++)
    for (j = 0; ; j++)
      if (j == NAME_LENGTH_MAXIMUM  ||  ! table[0].name[j])
    return table[0].val;
      else if (buf[j] != table[0].name[j])
    break;
}


/* Set *T to ``undefined'' values.  */
static void
undefine (t)
     struct partime *t;
{
  t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon
    = t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday
    = t->wday_ordinal = t->ymodulus = t->yweek
    = TM_UNDEFINED;
  t->tmr.tm_sec = t->tmr.tm_min = t->tmr.tm_hour =
    t->tmr.tm_mday = t->tmr.tm_mon = t->tmr.tm_year = 0;
  t->zone = TM_UNDEFINED_ZONE;
}

/* Patterns to look for in a time string.
   Order is important: we look for the first matching pattern
   whose values do not contradict values that we already know about.
   See `parse_pattern_letter' below for the meaning of the pattern codes.  */
static char const time_patterns[] =
{
  /* Traditional patterns come first,
     to prevent an ISO 8601 format from misinterpreting their prefixes.  */

  /* RFC 822, extended */
  'E', '_', 'N', '_', 'y', '$', 0,
  'x', 0,

  /* traditional */
  '4', '_', 'M', '_', 'D', '_', 'h', '_', 'm', '_', 's', '$', 0,
  'R', '_', 'M', '_', 'D', '_', 'h', '_', 'm', '_', 's', '$', 0,
  'E', '_', 'N', 0,
  'N', '_', 'E', '_', 'y', ';', 0,
  'N', '_', 'E', ';', 0,
  'N', 0,
  't', ':', 'm', ':', 's', '_', 'A', 0,
  't', ':', 'm', '_', 'A', 0,
  't', '_', 'A', 0,

  /* traditional get_date */
  'i', '_', 'x', 0,
  'Y', '/', 'n', '/', 'E', ';', 0,
  'n', '/', 'E', '/', 'y', ';', 0,
  'n', '/', 'E', ';', 0,
  'u', 0,

  /* ISO 8601:1988 formats, generalized a bit.  */
  'y', '-', 'M', '-', 'D', '$', 0,
  '4', 'M', 'D', '$', 0,
  'Y', '-', 'M', '$', 0,
  'R', 'M', 'D', '$', 0,
  '-', 'R', '=', 'M', '$', 0,
  '-', 'R', '$', 0,
  '-', '-', 'M', '=', 'D', '$', 0,
  'M', '=', 'D', 'T', 0,
  '-', '-', 'M', '$', 0,
  '-', '-', '-', 'D', '$', 0,
  'D', 'T', 0,
  'Y', '-', 'd', '$', 0,
  '4', 'd', '$', 0,
  'R', '=', 'd', '$', 0,
  '-', 'd', '$', 0,
  'd', 'T', 0,
  'y', '-', 'W', '-', 'X', 0,
  'y', 'W', 'X', 0,
  'y', '=', 'W', 0,
  '-', 'r', '-', 'W', '-', 'X', 0,
  'r', '-', 'W', '-', 'X', 'T', 0,
  '-', 'r', 'W', 'X', 0,
  'r', 'W', 'X', 'T', 0,
  '-', 'W', '=', 'X', 0,
  'W', '=', 'X', 'T', 0,
  '-', 'W', 0,
  '-', 'w', '-', 'X', 0,
  'w', '-', 'X', 'T', 0,
  '-', '-', '-', 'X', '$', 0,
  'X', 'T', 0,
  '4', '$', 0,
  'T', 0,
  'h', ':', 'm', ':', 's', '$', 0,
  'h', 'm', 's', '$', 0,
  'h', ':', 'L', '$', 0,
  'h', 'L', '$', 0,
  'H', '$', 0,
  '-', 'm', ':', 's', '$', 0,
  '-', 'm', 's', '$', 0,
  '-', 'L', '$', 0,
  '-', '-', 's', '$', 0,
  'Y', 0,
  'Z', 0,

  0
};

/* Parse an initial prefix of STR according to *PATTERNS, setting *T.
   Return the first character after the prefix, or 0 if it couldn't be parsed.
   *PATTERNS is a character array containing one pattern string after another;
   it is terminated by an empty string.
   If success, set *PATTERNS to the next pattern to try.
   Set *PATTERNS to 0 if we know there are no more patterns to try;
   if *PATTERNS is initially 0, give up immediately.  */
static char const *
parse_prefix (str, patterns, t)
     char const *str;
     char const **patterns;
     struct partime *t;
{
  char const *pat = *patterns;
  unsigned char c;

  if (! pat)
    return 0;

  /* Remove initial noise.  */
  while (! ISALNUM (c = *str) && c != '-' && c != '+')
    {
      if (! c)
    {
      undefine (t);
      *patterns = 0;
      return str;
    }

      str++;
    }

  /* Try a pattern until one succeeds.  */
  while (*pat)
    {
      char const *s = str;
      undefine (t);

      do
    {
      if (! (c = *pat++))
        {
          *patterns = pat;
          return s;
        }
    }
      while ((s = parse_pattern_letter (s, c, t)) != 0);

      while (*pat++)
    continue;
    }

  return 0;
}

/* Parse an initial prefix of S of length DIGITS; it must be a number.
   Store the parsed number into *RES.
   Return the first character after the prefix, or 0 if it wasn't parsed.  */
static char const *
parse_fixed (s, digits, res)
     char const *s;
     int digits, *res;
{
  int n = 0;
  char const *lim = s + digits;
  while (s < lim)
    {
      unsigned d = *s++ - '0';
      if (9 < d)
    return 0;
      n = 10 * n + d;
    }
  *res = n;
  return s;
}

/* Parse a possibly empty initial prefix of S.
   Store the parsed number into *RES.
   Return the first character after the prefix.  */
static char const *
parse_varying (s, res)
     char const *s;
     int *res;
{
  int n = 0;
  for (;;)
    {
      unsigned d = *s - '0';
      if (9 < d)
    break;
      s++;
      n = 10 * n + d;
    }
  *res = n;
  return s;
}

/* Parse an initial prefix of S of length DIGITS;
   it must be a number in the range LO through HI.
   Store the parsed number into *RES.
   Return the first character after the prefix, or 0 if it wasn't parsed.  */
static char const *
parse_ranged (s, digits, lo, hi, res)
     char const *s;
     int digits, lo, hi, *res;
{
  s = parse_fixed (s, digits, res);
  return s && lo <= *res && *res <= hi ? s : 0;
}

/* Parse an initial prefix of S of length DIGITS;
   it must be a number in the range LO through HI
   and it may be followed by a fraction to be computed using RESOLUTION.
   Store the parsed number into *RES; store the fraction times RESOLUTION,
   rounded to the nearest integer, into *FRES.
   Return the first character after the prefix, or 0 if it wasn't parsed.  */
static char const *
parse_decimal (s, digits, lo, hi, resolution, res, fres)
     char const *s;
     int digits, lo, hi, resolution, *res, *fres;
{
  s = parse_fixed (s, digits, res);
  if (s && lo <= *res && *res <= hi)
    {
      int f = 0;
      if ((s[0] == ',' || s[0] == '.') && ISDIGIT (s[1]))
    {
      char const *s1 = ++s;
      int num10 = 0, denom10 = 10, product;
      while (ISDIGIT (*++s))
        {
          int d = denom10 * 10;
          if (d / 10  !=  denom10)
        return 0; /* overflow */
          denom10 = d;
        }
      s = parse_fixed (s1, (int) (s - s1), &num10);
      product = num10 * resolution;
      f = (product + (denom10 >> 1)) / denom10;
      f -= f & (product % denom10  ==  denom10 >> 1); /* round to even */
      if (f < 0  ||  product/resolution != num10)
        return 0; /* overflow */
    }
      *fres = f;
      return s;
    }
  return 0;
}

/* Parse an initial prefix of S; it must denote a time zone.
   Set *ZONE to the number of seconds east of GMT,
   or to TM_LOCAL_ZONE if it is the local time zone.
   Return the first character after the prefix, or 0 if it wasn't parsed.  */
char *
parzone (s, zone)
     char const *s;
     long *zone;
{
  char const *s1;
  char sign;
  int hh, mm, ss;
  int minutes_east_of_UTC;
  int trailing_DST;
  long offset, z;

  /* The formats are LT, n, n DST, nDST, no, o
     where n is a time zone name
     and o is a time zone offset of the form [-+]hh[:mm[:ss]].  */
  switch (*s)
    {
    case '-':
    case '+':
      z = 0;
      break;

    default:
      minutes_east_of_UTC = lookup (s, zone_names);
      if (minutes_east_of_UTC == -1)
    return 0;

      /* Don't bother to check rest of spelling,
     but look for an embedded "DST".  */
      trailing_DST = 0;
      while (ISALPHA ((unsigned char) *s))
    {
      if ((*s == 'D' || *s == 'd') && lookup (s, dst_names))
        trailing_DST = 1;
      s++;
      s += *s == '.';
    }

      /* Don't modify LT.  */
      if (minutes_east_of_UTC == 1)
    {
      *zone = TM_LOCAL_ZONE;
      return (char *) s;
    }

      z = minutes_east_of_UTC * 60L;
      s1 = s;

      /* Look for trailing "DST" or " DST".  */
      while (ISSPACE ((unsigned char) *s))
    s++;
      if (lookup (s, dst_names))
    {
      while (ISALPHA ((unsigned char) *s))
        {
          s++;
          s += *s == '.';
        }
      trailing_DST = 1;
    }

      if (trailing_DST)
    {
      *zone = z + 60*60;
      return (char *) s;
    }

      s = s1;

      switch (*s)
    {
    case '-':
    case '+':
      break;

    default:
      *zone = z;
      return (char *) s;
    }

      break;
    }

  sign = *s++;

  if (! (s = parse_ranged (s, 2, 0, 23, &hh)))
    return 0;
  mm = ss = 0;
  if (*s == ':')
    s++;
  if (ISDIGIT (*s))
    {
      if (! (s = parse_ranged (s, 2, 0, 59, &mm)))
    return 0;
      if (*s == ':' && s[-3] == ':' && ISDIGIT (s[1])
      && ! (s = parse_ranged (s + 1, 2, 0, 59, &ss)))
    return 0;
    }
  if (ISDIGIT (*s))
    return 0;
  offset = (hh * 60 + mm) * 60L + ss;
  *zone = z + (sign == '-' ? -offset : offset);
  /* ?? Are fractions allowed here?  If so, they're not implemented.  */
  return (char *) s;
}

/* Parse an initial prefix of S, matching the pattern whose code is C.
   Set *T accordingly.
   Return the first character after the prefix, or 0 if it wasn't parsed.  */
static char const *
parse_pattern_letter (s, c, t)
     char const *s;
     int c;
     struct partime *t;
{
  char const *s0 = s;

  switch (c)
    {
    case '$': /* The next character must be a non-digit.  */
      if (ISDIGIT (*s))
    return 0;
      break;

    case '-':
    case '/':
    case ':':
      /* These characters stand for themselves.  */
      if (*s++ != c)
    return 0;
      break;

    case '4': /* 4-digit year */
      s = parse_fixed (s, 4, &t->tm.tm_year);
      break;

    case ';': /* The next character must be a non-digit, and cannot be ':'.  */
      if (ISDIGIT (*s) || *s == ':')
    return 0;
      break;

    case '=': /* optional '-' */
      s += *s == '-';
      break;

    case 'A': /* AM or PM */
      /* This matches the regular expression [AaPp]\.?([Mm]\.?)?.
         It must not be followed by a letter or digit;
         otherwise it would match prefixes of strings like "PST".  */
      switch (*s)
    {
    case 'A':
    case 'a':
      if (t->tm.tm_hour == 12)
        t->tm.tm_hour = 0;
      break;

    case 'P':
    case 'p':
      if (t->tm.tm_hour != 12)
        t->tm.tm_hour += 12;
      break;

    default:
      return 0;
    }
      s++;
      s += *s == '.';
      switch (*s)
    {
    case 'M':
    case 'm':
      s++;
      s += *s == '.';
      break;
    }
      if (ISALNUM ((unsigned char) *s))
    return 0;
      break;

    case 'D': /* day of month [01-31] */
      s = parse_ranged (s, 2, 1, 31, &t->tm.tm_mday);
      break;

    case 'd': /* day of year [001-366] */
      s = parse_ranged (s, 3, 1, 366, &t->tm.tm_yday);
      t->tm.tm_yday--;
      break;

    case 'E': /* traditional day of month [1-9, 01-31] */
      s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 31,
            &t->tm.tm_mday);
      break;

    case 'h': /* hour [00-23] */
      s = parse_ranged (s, 2, 0, 23, &t->tm.tm_hour);
      break;

    case 'H': /* hour [00-23 followed by optional fraction] */
      {
    int frac;
    s = parse_decimal (s, 2, 0, 23, 60 * 60, &t->tm.tm_hour, &frac);
    t->tm.tm_min = frac / 60;
    t->tm.tm_sec = frac % 60;
      }
      break;

    case 'i': /* ordinal day number, e.g. "3rd" */
      s = parse_varying (s, &t->wday_ordinal);
      if (s == s0)
    return 0;
      while (ISALPHA ((unsigned char) *s))
    s++;
      break;

    case 'L': /* minute [00-59 followed by optional fraction] */
      s = parse_decimal (s, 2, 0, 59, 60, &t->tm.tm_min, &t->tm.tm_sec);
      break;

    case 'm': /* minute [00-59] */
      s = parse_ranged (s, 2, 0, 59, &t->tm.tm_min);
      break;

    case 'M': /* month [01-12] */
      s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon);
      t->tm.tm_mon--;
      break;

    case 'n': /* traditional month [1-9, 01-12] */
      s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 12,
            &t->tm.tm_mon);
      t->tm.tm_mon--;
      break;

    case 'N': /* month name [e.g. "Jan"] */
      if (! TM_DEFINED (t->tm.tm_mon = lookup (s, month_names)))
    return 0;
      /* Don't bother to check rest of spelling.  */
      while (ISALPHA ((unsigned char) *s))
    s++;
      break;

    case 'r': /* year % 10 (remainder in origin-0 decade) [0-9] */
      s = parse_fixed (s, 1, &t->tm.tm_year);
      t->ymodulus = 10;
      break;

    case_R:
    case 'R': /* year % 100 (remainder in origin-0 century) [00-99] */
      s = parse_fixed (s, 2, &t->tm.tm_year);
      t->ymodulus = 100;
      break;

    case 's': /* second [00-60 followed by optional fraction] */
      {
    int frac;
    s = parse_decimal (s, 2, 0, 60, 1, &t->tm.tm_sec, &frac);
    t->tm.tm_sec += frac;
      }
      break;

    case 'T': /* 'T' or 't' */
      switch (*s++)
    {
    case 'T':
    case 't':
      break;
    default:
      return 0;
    }
      break;

    case 't': /* traditional hour [1-9 or 01-12] */
      s = parse_ranged (s, (ISDIGIT (s[0]) && ISDIGIT (s[1])) + 1, 1, 12,
            &t->tm.tm_hour);
      break;

    case 'u': /* relative unit */
      {
    int i;
    int n;
    int negative = 0;
    switch (*s)
      {
        case '-': negative = 1;
        /* Fall through.  */
        case '+': s++;
      }
    if (ISDIGIT (*s))
      s = parse_varying (s, &n);
    else if (s == s0)
      n = 1;
    else
      return 0;
    if (negative)
      n = -n;
    while (! ISALNUM ((unsigned char) *s) && *s)
      s++;
    i = lookup (s, relative_units);
    if (!TM_DEFINED (i))
      return 0;
    * (int *) ((char *) &t->tmr + RELATIVE_OFFSET (i))
      += n * RELATIVE_MULTIPLIER (i);
    while (ISALPHA ((unsigned char) *s))
      s++;
    while (! ISALNUM ((unsigned char) *s) && *s)
      s++;
    if (TM_DEFINED (lookup (s, ago)))
      {
        t->tmr.tm_sec  = - t->tmr.tm_sec;
        t->tmr.tm_min  = - t->tmr.tm_min;
        t->tmr.tm_hour = - t->tmr.tm_hour;
        t->tmr.tm_mday = - t->tmr.tm_mday;
        t->tmr.tm_mon  = - t->tmr.tm_mon;
        t->tmr.tm_year = - t->tmr.tm_year;
        while (ISALPHA ((unsigned char) *s))
          s++;
      }
    break;
      }

    case 'w': /* 'W' or 'w' only (stands for current week) */
      switch (*s++)
    {
    case 'W':
    case 'w':
      break;
    default:
      return 0;
    }
      break;

    case 'W': /* 'W' or 'w', followed by a week of year [00-53] */
      switch (*s++)
    {
    case 'W':
    case 'w':
      break;
    default:
      return 0;
    }
      s = parse_ranged (s, 2, 0, 53, &t->yweek);
      break;

    case 'X': /* weekday (1=Mon ... 7=Sun) [1-7] */
      s = parse_ranged (s, 1, 1, 7, &t->tm.tm_wday);
      t->tm.tm_wday--;
      break;

    case 'x': /* weekday name [e.g. "Sun"] */
      if (! TM_DEFINED (t->tm.tm_wday = lookup (s, weekday_names)))
    return 0;
      /* Don't bother to check rest of spelling.  */
      while (ISALPHA ((unsigned char) *s))
    s++;
      break;

    case 'y': /* either R or Y */
      if (ISDIGIT (s[0]) && ISDIGIT (s[1]) && ! ISDIGIT (s[2]))
    goto case_R;
      /* fall into */
    case 'Y': /* year in full [4 or more digits] */
      s = parse_varying (s, &t->tm.tm_year);
      if (s - s0 < 4)
    return 0;
      break;

    case 'Z': /* time zone */
      s = parzone (s, &t->zone);
      break;

    case '_': /* possibly empty sequence of non-alphanumerics */
      while (! ISALNUM ((unsigned char) *s) && *s)
    s++;
      break;

    default: /* bad pattern */
      return 0;
    }

  return s;
}

/* If there is no conflict, merge into *T the additional information in *U
   and return 0.  Otherwise do nothing and return -1.  */
static int
merge_partime (t, u)
     struct partime *t;
     struct partime const *u;
{
# define conflict(a,b) ((a) != (b)  &&  TM_DEFINED (a)  &&  TM_DEFINED (b))
  if (conflict (t->tm.tm_sec, u->tm.tm_sec)
      || conflict (t->tm.tm_min, u->tm.tm_min)
      || conflict (t->tm.tm_hour, u->tm.tm_hour)
      || conflict (t->tm.tm_mday, u->tm.tm_mday)
      || conflict (t->tm.tm_mon, u->tm.tm_mon)
      || conflict (t->tm.tm_year, u->tm.tm_year)
      || conflict (t->tm.tm_wday, u->tm.tm_wday)
      || conflict (t->tm.tm_yday, u->tm.tm_yday)
      || conflict (t->ymodulus, u->ymodulus)
      || conflict (t->yweek, u->yweek)
      || (t->zone != u->zone
      && t->zone != TM_UNDEFINED_ZONE
      && u->zone != TM_UNDEFINED_ZONE))
    return -1;
# undef conflict
# define merge_(a,b) if (TM_DEFINED (b)) (a) = (b);
  merge_ (t->tm.tm_sec, u->tm.tm_sec)
  merge_ (t->tm.tm_min, u->tm.tm_min)
  merge_ (t->tm.tm_hour, u->tm.tm_hour)
  merge_ (t->tm.tm_mday, u->tm.tm_mday)
  merge_ (t->tm.tm_mon, u->tm.tm_mon)
  merge_ (t->tm.tm_year, u->tm.tm_year)
  merge_ (t->tm.tm_wday, u->tm.tm_wday)
  merge_ (t->tm.tm_yday, u->tm.tm_yday)
  merge_ (t->ymodulus, u->ymodulus)
  merge_ (t->yweek, u->yweek)
# undef merge_
  t->tmr.tm_sec += u->tmr.tm_sec;
  t->tmr.tm_min += u->tmr.tm_min;
  t->tmr.tm_hour += u->tmr.tm_hour;
  t->tmr.tm_mday += u->tmr.tm_mday;
  t->tmr.tm_mon += u->tmr.tm_mon;
  t->tmr.tm_year += u->tmr.tm_year;
  if (u->zone != TM_UNDEFINED_ZONE)
    t->zone = u->zone;
  return 0;
}

/* Parse a date/time prefix of S, putting the parsed result into *T.
   Return the first character after the prefix.
   The prefix may contain no useful information;
   in that case, *T will contain only undefined values.  */
char *
partime (s, t)
     char const *s;
     struct partime *t;
{
  struct partime p;

  undefine (t);

  while (*s)
    {
      char const *patterns = time_patterns;
      char const *s1;

      do
    {
      if (! (s1 = parse_prefix (s, &patterns, &p)))
        return (char *) s;
    }
      while (merge_partime (t, &p) != 0);

      s = s1;
    }

  return (char *) s;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/patch.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/patch.c used in: Step 569
/* patch - a program to apply diffs to original files */

/* $Id: patch.c,v 1.44 2003/05/20 13:55:03 eggert Exp $ */

/* Copyright (C) 1984, 1985, 1986, 1987, 1988 Larry Wall

   Copyright (C) 1989, 1990, 1991, 1992, 1993, 1997, 1998, 1999, 2002,
   2003 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#define XTERN
#include <common.h>
#undef XTERN
#define XTERN extern
#include <argmatch.h>
#include <backupfile.h>
#include <getopt.h>
#include <inp.h>
#include <pch.h>
#include <quotearg.h>
#include <util.h>
#include <version.h>
#include <xalloc.h>

#if HAVE_UTIME_H
# include <utime.h>
#endif
/* Some nonstandard hosts don't declare this structure even in <utime.h>.  */
#if ! HAVE_STRUCT_UTIMBUF
struct utimbuf
{
  time_t actime;
  time_t modtime;
};
#endif

/* Output stream state.  */
struct outstate
{
  FILE *ofp;
  bool after_newline;
  bool zero_output;
};

/* procedures */

static FILE *create_output_file (char const *, int);
static LINENUM locate_hunk (LINENUM);
static bool apply_hunk (struct outstate *, LINENUM);
static bool copy_till (struct outstate *, LINENUM);
static bool patch_match (LINENUM, LINENUM, LINENUM, LINENUM);
static bool similar (char const *, size_t, char const *, size_t);
static bool spew_output (struct outstate *);
static char const *make_temp (char);
static int numeric_string (char const *, bool, char const *);
static void abort_hunk (void);
static void cleanup (void);
static void get_some_switches (void);
static void init_output (char const *, int, struct outstate *);
static void init_reject (void);
static void reinitialize_almost_everything (void);
static void remove_if_needed (char const *, int volatile *);
static void usage (FILE *, int) __attribute__((noreturn));

static bool make_backups;
static bool backup_if_mismatch;
static char const *version_control;
static char const *version_control_context;
static bool remove_empty_files;

/* true if -R was specified on command line.  */
static bool reverse_flag_specified;

/* how many input lines have been irretractably output */
static LINENUM last_frozen_line;

static char const *do_defines; /* symbol to patch using ifdef, ifndef, etc. */
static char const if_defined[] = "\n#ifdef %s\n";
static char const not_defined[] = "\n#ifndef %s\n";
static char const else_defined[] = "\n#else\n";
static char const end_defined[] = "\n#endif\n";

static int Argc;
static char * const *Argv;

static FILE *rejfp;  /* reject file pointer */

static char const *patchname;
static char *rejname;
static char const * volatile TMPREJNAME;
static int volatile TMPREJNAME_needs_removal;

static LINENUM last_offset;
static LINENUM maxfuzz = 2;

static char serrbuf[BUFSIZ];

/* Apply a set of diffs as appropriate. */

int
main (int argc, char **argv)
{
    char const *val;
    bool somefailed = false;
    struct outstate outstate;
    char numbuf[LINENUM_LENGTH_BOUND + 1];

    xalloc_exit_failure = 2;
    program_name = argv[0];
    init_time ();

    setbuf(stderr, serrbuf);

    xalloc_fail_func = memory_fatal;
    bufsize = 8 * 1024;
    buf = xmalloc (bufsize);

    strippath = -1;

    val = getenv ("QUOTING_STYLE");
    {
      int i = val ? argmatch (val, quoting_style_args, 0, 0) : -1;
      set_quoting_style ((struct quoting_options *) 0,
             i < 0 ? shell_quoting_style : (enum quoting_style) i);
    }

    posixly_correct = getenv ("POSIXLY_CORRECT") != 0;
    backup_if_mismatch = ! posixly_correct;
    patch_get = ((val = getenv ("PATCH_GET"))
         ? numeric_string (val, true, "PATCH_GET value")
         : posixly_correct - 1);

    val = getenv ("SIMPLE_BACKUP_SUFFIX");
    simple_backup_suffix = val && *val ? val : ".orig";

    if ((version_control = getenv ("PATCH_VERSION_CONTROL")))
      version_control_context = "$PATCH_VERSION_CONTROL";
    else if ((version_control = getenv ("VERSION_CONTROL")))
      version_control_context = "$VERSION_CONTROL";

    /* Cons up the names of the global temporary files.
       Do this before `cleanup' can possibly be called (e.g. by `pfatal').  */
    TMPOUTNAME = make_temp ('o');
    TMPINNAME = make_temp ('i');
    TMPREJNAME = make_temp ('r');
    TMPPATNAME = make_temp ('p');

    /* parse switches */
    Argc = argc;
    Argv = argv;
    get_some_switches();

    if (make_backups | backup_if_mismatch)
      backup_type = get_version (version_control_context, version_control);

    init_output (outfile, 0, &outstate);

    /* Make sure we clean up in case of disaster.  */
    set_signals (false);

    for (
    open_patch_file (patchname);
    there_is_another_patch();
    reinitialize_almost_everything()
    ) {                 /* for each patch in patch file */
      int hunk = 0;
      int failed = 0;
      bool mismatch = false;
      char *outname = outfile ? outfile : inname;

      if (!skip_rest_of_patch)
    get_input_file (inname, outname);

      if (diff_type == ED_DIFF) {
    outstate.zero_output = false;
    somefailed |= skip_rest_of_patch;
    do_ed_script (outstate.ofp);
    if (! dry_run && ! outfile && ! skip_rest_of_patch)
      {
        struct stat statbuf;
        if (stat (TMPOUTNAME, &statbuf) != 0)
          pfatal ("%s", TMPOUTNAME);
        outstate.zero_output = statbuf.st_size == 0;
      }
      } else {
    int got_hunk;
    bool apply_anyway = false;

    /* initialize the patched file */
    if (! skip_rest_of_patch && ! outfile)
      {
        int exclusive = TMPOUTNAME_needs_removal ? 0 : O_EXCL;
        TMPOUTNAME_needs_removal = 1;
        init_output (TMPOUTNAME, exclusive, &outstate);
      }

    /* initialize reject file */
    init_reject ();

    /* find out where all the lines are */
    if (!skip_rest_of_patch)
        scan_input (inname);

    /* from here on, open no standard i/o files, because malloc */
    /* might misfire and we can't catch it easily */

    /* apply each hunk of patch */
    while (0 < (got_hunk = another_hunk (diff_type, reverse))) {
        LINENUM where = 0; /* Pacify `gcc -Wall'.  */
        LINENUM newwhere;
        LINENUM fuzz = 0;
        LINENUM prefix_context = pch_prefix_context ();
        LINENUM suffix_context = pch_suffix_context ();
        LINENUM context = (prefix_context < suffix_context
                   ? suffix_context : prefix_context);
        LINENUM mymaxfuzz = (maxfuzz < context ? maxfuzz : context);
        hunk++;
        if (!skip_rest_of_patch) {
        do {
            where = locate_hunk(fuzz);
            if (! where || fuzz || last_offset)
              mismatch = true;
            if (hunk == 1 && ! where && ! (force | apply_anyway)
            && reverse == reverse_flag_specified) {
                        /* dwim for reversed patch? */
            if (!pch_swap()) {
                say (
"Not enough memory to try swapped hunk!  Assuming unswapped.\n");
                continue;
            }
            /* Try again.  */
            where = locate_hunk (fuzz);
            if (where
                && (ok_to_reverse
                ("%s patch detected!",
                 (reverse
                  ? "Unreversed"
                  : "Reversed (or previously applied)"))))
              reverse = ! reverse;
            else
              {
                /* Put it back to normal.  */
                if (! pch_swap ())
                  fatal ("lost hunk on alloc error!");
                if (where)
                  {
                apply_anyway = true;
                fuzz--; /* Undo `++fuzz' below.  */
                where = 0;
                  }
              }
            }
        } while (!skip_rest_of_patch && !where
             && ++fuzz <= mymaxfuzz);

        if (skip_rest_of_patch) {       /* just got decided */
          if (outstate.ofp && ! outfile)
            {
              fclose (outstate.ofp);
              outstate.ofp = 0;
            }
        }
        }

        newwhere = pch_newfirst() + last_offset;
        if (skip_rest_of_patch) {
        abort_hunk();
        failed++;
        if (verbosity == VERBOSE)
          say ("Hunk #%d ignored at %s.\n", hunk,
               format_linenum (numbuf, newwhere));
        }
        else if (!where
             || (where == 1 && pch_says_nonexistent (reverse) == 2
             && instat.st_size)) {

        if (where)
          say ("Patch attempted to create file %s, which already exists.\n",
               quotearg (inname));

        abort_hunk();
        failed++;
        if (verbosity != SILENT)
          say ("Hunk #%d FAILED at %s.\n", hunk,
               format_linenum (numbuf, newwhere));
        }
        else if (! apply_hunk (&outstate, where)) {
        abort_hunk ();
        failed++;
        if (verbosity != SILENT)
          say ("Hunk #%d FAILED at %s.\n", hunk,
               format_linenum (numbuf, newwhere));
        } else {
        if (verbosity == VERBOSE
            || (verbosity != SILENT && (fuzz || last_offset))) {
            say ("Hunk #%d succeeded at %s", hunk,
             format_linenum (numbuf, newwhere));
            if (fuzz)
              say (" with fuzz %s", format_linenum (numbuf, fuzz));
            if (last_offset)
              say (" (offset %s line%s)",
               format_linenum (numbuf, last_offset),
               "s" + (last_offset == 1));
            say (".\n");
        }
        }
    }

    if (!skip_rest_of_patch)
      {
        if (got_hunk < 0  &&  using_plan_a)
          {
        if (outfile)
          fatal ("out of memory using Plan A");
        say ("\n\nRan out of memory using Plan A -- trying again...\n\n");
        if (outstate.ofp)
          {
            fclose (outstate.ofp);
            outstate.ofp = 0;
          }
        fclose (rejfp);
        continue;
          }

        /* Finish spewing out the new file.  */
        assert (hunk);
        if (! spew_output (&outstate))
          {
        say ("Skipping patch.\n");
        skip_rest_of_patch = true;
          }
      }
      }

      /* and put the output where desired */
      ignore_signals ();
      if (! skip_rest_of_patch && ! outfile) {
      if (outstate.zero_output
          && (remove_empty_files
          || (pch_says_nonexistent (! reverse) == 2
              && ! posixly_correct)))
        {
          if (verbosity == VERBOSE)
        say ("Removing file %s%s\n", quotearg (outname),
             dry_run ? " and any empty ancestor directories" : "");
          if (! dry_run)
        {
          move_file ((char *) 0, (int *) 0, outname, (mode_t) 0,
                 (make_backups
                  || (backup_if_mismatch && (mismatch | failed))));
          removedirs (outname);
        }
        }
      else
        {
          if (! outstate.zero_output
          && pch_says_nonexistent (! reverse))
        {
          mismatch = true;
          if (verbosity != SILENT)
            say ("File %s is not empty after patch, as expected\n",
             quotearg (outname));
        }

          if (! dry_run)
        {
          time_t t;

          move_file (TMPOUTNAME, &TMPOUTNAME_needs_removal,
                 outname, instat.st_mode,
                 (make_backups
                  || (backup_if_mismatch && (mismatch | failed))));

          if ((set_time | set_utc)
              && (t = pch_timestamp (! reverse)) != (time_t) -1)
            {
              struct utimbuf utimbuf;
              utimbuf.actime = utimbuf.modtime = t;

              if (! force && ! inerrno
              && pch_says_nonexistent (reverse) != 2
              && (t = pch_timestamp (reverse)) != (time_t) -1
              && t != instat.st_mtime)
            say ("Not setting time of file %s (time mismatch)\n",
                 quotearg (outname));
              else if (! force && (mismatch | failed))
            say ("Not setting time of file %s (contents mismatch)\n",
                 quotearg (outname));
              else if (utime (outname, &utimbuf) != 0)
            pfatal ("Can't set timestamp on file %s",
                quotearg (outname));
            }

          if (! inerrno && chmod (outname, instat.st_mode) != 0)
            pfatal ("Can't set permissions on file %s",
                quotearg (outname));
        }
        }
      }
      if (diff_type != ED_DIFF) {
    if (fclose (rejfp) != 0)
        write_fatal ();
    if (failed) {
        somefailed = true;
        say ("%d out of %d hunk%s %s", failed, hunk, "s" + (hunk == 1),
         skip_rest_of_patch ? "ignored" : "FAILED");
        if (outname) {
        char *rej = rejname;
        if (!rejname) {
            rej = xmalloc (strlen (outname) + 5);
            strcpy (rej, outname);
            addext (rej, ".rej", '#');
        }
        say (" -- saving rejects to file %s", quotearg (rej));
        if (! dry_run)
          {
            move_file (TMPREJNAME, &TMPREJNAME_needs_removal,
                   rej, instat.st_mode, false);
            if (! inerrno
            && (chmod (rej, (instat.st_mode
                     & ~(S_IXUSR|S_IXGRP|S_IXOTH)))
                != 0))
              pfatal ("can't set permissions on file %s",
                  quotearg (rej));
          }
        if (!rejname)
            free (rej);
        }
        say ("\n");
    }
      }
      set_signals (true);
    }
    if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0))
      write_fatal ();
    cleanup ();
    if (somefailed)
      exit (1);
    return 0;
}

/* Prepare to find the next patch to do in the patch file. */

static void
reinitialize_almost_everything (void)
{
    re_patch();
    re_input();

    input_lines = 0;
    last_frozen_line = 0;

    if (inname) {
    free (inname);
    inname = 0;
    }

    last_offset = 0;

    diff_type = NO_DIFF;

    if (revision) {
    free(revision);
    revision = 0;
    }

    reverse = reverse_flag_specified;
    skip_rest_of_patch = false;
}

static char const shortopts[] = "bB:cd:D:eEfF:g:i:lnNo:p:r:RstTuvV:x:Y:z:Z";
static struct option const longopts[] =
{
  {"backup", no_argument, NULL, 'b'},
  {"prefix", required_argument, NULL, 'B'},
  {"context", no_argument, NULL, 'c'},
  {"directory", required_argument, NULL, 'd'},
  {"ifdef", required_argument, NULL, 'D'},
  {"ed", no_argument, NULL, 'e'},
  {"remove-empty-files", no_argument, NULL, 'E'},
  {"force", no_argument, NULL, 'f'},
  {"fuzz", required_argument, NULL, 'F'},
  {"get", no_argument, NULL, 'g'},
  {"input", required_argument, NULL, 'i'},
  {"ignore-whitespace", no_argument, NULL, 'l'},
  {"normal", no_argument, NULL, 'n'},
  {"forward", no_argument, NULL, 'N'},
  {"output", required_argument, NULL, 'o'},
  {"strip", required_argument, NULL, 'p'},
  {"reject-file", required_argument, NULL, 'r'},
  {"reverse", no_argument, NULL, 'R'},
  {"quiet", no_argument, NULL, 's'},
  {"silent", no_argument, NULL, 's'},
  {"batch", no_argument, NULL, 't'},
  {"set-time", no_argument, NULL, 'T'},
  {"unified", no_argument, NULL, 'u'},
  {"version", no_argument, NULL, 'v'},
  {"version-control", required_argument, NULL, 'V'},
  {"debug", required_argument, NULL, 'x'},
  {"basename-prefix", required_argument, NULL, 'Y'},
  {"suffix", required_argument, NULL, 'z'},
  {"set-utc", no_argument, NULL, 'Z'},
  {"dry-run", no_argument, NULL, CHAR_MAX + 1},
  {"verbose", no_argument, NULL, CHAR_MAX + 2},
  {"binary", no_argument, NULL, CHAR_MAX + 3},
  {"help", no_argument, NULL, CHAR_MAX + 4},
  {"backup-if-mismatch", no_argument, NULL, CHAR_MAX + 5},
  {"no-backup-if-mismatch", no_argument, NULL, CHAR_MAX + 6},
  {"posix", no_argument, NULL, CHAR_MAX + 7},
  {"quoting-style", required_argument, NULL, CHAR_MAX + 8},
  {NULL, no_argument, NULL, 0}
};

static char const *const option_help[] =
{
"Input options:",
"",
"  -p NUM  --strip=NUM  Strip NUM leading components from file names.",
"  -F LINES  --fuzz LINES  Set the fuzz factor to LINES for inexact matching.",
"  -l  --ignore-whitespace  Ignore white space changes between patch and input.",
"",
"  -c  --context  Interpret the patch as a context difference.",
"  -e  --ed  Interpret the patch as an ed script.",
"  -n  --normal  Interpret the patch as a normal difference.",
"  -u  --unified  Interpret the patch as a unified difference.",
"",
"  -N  --forward  Ignore patches that appear to be reversed or already applied.",
"  -R  --reverse  Assume patches were created with old and new files swapped.",
"",
"  -i PATCHFILE  --input=PATCHFILE  Read patch from PATCHFILE instead of stdin.",
"",
"Output options:",
"",
"  -o FILE  --output=FILE  Output patched files to FILE.",
"  -r FILE  --reject-file=FILE  Output rejects to FILE.",
"",
"  -D NAME  --ifdef=NAME  Make merged if-then-else output using NAME.",
"  -E  --remove-empty-files  Remove output files that are empty after patching.",
"",
"  -Z  --set-utc  Set times of patched files, assuming diff uses UTC (GMT).",
"  -T  --set-time  Likewise, assuming local time.",
"",
"  --quoting-style=WORD   output file names using quoting style WORD.",
"    Valid WORDs are: literal, shell, shell-always, c, escape.",
"    Default is taken from QUOTING_STYLE env variable, or 'shell' if unset.",
"",
"Backup and version control options:",
"",
"  -b  --backup  Back up the original contents of each file.",
"  --backup-if-mismatch  Back up if the patch does not match exactly.",
"  --no-backup-if-mismatch  Back up mismatches only if otherwise requested.",
"",
"  -V STYLE  --version-control=STYLE  Use STYLE version control.",
"   STYLE is either 'simple', 'numbered', or 'existing'.",
"  -B PREFIX  --prefix=PREFIX  Prepend PREFIX to backup file names.",
"  -Y PREFIX  --basename-prefix=PREFIX  Prepend PREFIX to backup file basenames.",
"  -z SUFFIX  --suffix=SUFFIX  Append SUFFIX to backup file names.",
"",
"  -g NUM  --get=NUM  Get files from RCS etc. if positive; ask if negative.",
"",
"Miscellaneous options:",
"",
"  -t  --batch  Ask no questions; skip bad-Prereq patches; assume reversed.",
"  -f  --force  Like -t, but ignore bad-Prereq patches, and assume unreversed.",
"  -s  --quiet  --silent  Work silently unless an error occurs.",
"  --verbose  Output extra information about the work being done.",
"  --dry-run  Do not actually change any files; just print what would happen.",
"  --posix  Conform to the POSIX standard.",
"",
"  -d DIR  --directory=DIR  Change the working directory to DIR first.",
#if HAVE_SETMODE_DOS
"  --binary  Read and write data in binary mode.",
#else
"  --binary  Read and write data in binary mode (no effect on this platform).",
#endif
"",
"  -v  --version  Output version info.",
"  --help  Output this help.",
"",
"Report bugs to <" PACKAGE_BUGREPORT ">.",
0
};

static void
usage (FILE *stream, int status)
{
  char const * const *p;

  if (status != 0)
    {
      fprintf (stream, "%s: Try `%s --help' for more information.\n",
           program_name, Argv[0]);
    }
  else
    {
      fprintf (stream, "Usage: %s [OPTION]... [ORIGFILE [PATCHFILE]]\n\n",
           Argv[0]);
      for (p = option_help;  *p;  p++)
    fprintf (stream, "%s\n", *p);
    }

  exit (status);
}

/* Process switches and filenames.  */

static void
get_some_switches (void)
{
    register int optc;

    if (rejname)
    free (rejname);
    rejname = 0;
    if (optind == Argc)
    return;
    while ((optc = getopt_long (Argc, Argv, shortopts, longopts, (int *) 0))
       != -1) {
    switch (optc) {
        case 'b':
        make_backups = true;
         /* Special hack for backward compatibility with CVS 1.9.
            If the last 4 args are `-b SUFFIX ORIGFILE PATCHFILE',
            treat `-b' as if it were `-b -z'.  */
        if (Argc - optind == 3
            && strcmp (Argv[optind - 1], "-b") == 0
            && ! (Argv[optind + 0][0] == '-' && Argv[optind + 0][1])
            && ! (Argv[optind + 1][0] == '-' && Argv[optind + 1][1])
            && ! (Argv[optind + 2][0] == '-' && Argv[optind + 2][1]))
          {
            optarg = Argv[optind++];
            if (verbosity != SILENT)
              say ("warning: the `-b %s' option is obsolete; use `-b -z %s' instead\n",
               optarg, optarg);
            goto case_z;
          }
        break;
        case 'B':
        if (!*optarg)
          fatal ("backup prefix is empty");
        origprae = savestr (optarg);
        break;
        case 'c':
        diff_type = CONTEXT_DIFF;
        break;
        case 'd':
        if (chdir(optarg) < 0)
          pfatal ("Can't change to directory %s", quotearg (optarg));
        break;
        case 'D':
        do_defines = savestr (optarg);
        break;
        case 'e':
        diff_type = ED_DIFF;
        break;
        case 'E':
        remove_empty_files = true;
        break;
        case 'f':
        force = true;
        break;
        case 'F':
        maxfuzz = numeric_string (optarg, false, "fuzz factor");
        break;
        case 'g':
        patch_get = numeric_string (optarg, true, "get option value");
        break;
        case 'i':
        patchname = savestr (optarg);
        break;
        case 'l':
        canonicalize = true;
        break;
        case 'n':
        diff_type = NORMAL_DIFF;
        break;
        case 'N':
        noreverse = true;
        break;
        case 'o':
        if (strcmp (optarg, "-") == 0)
          fatal ("can't output patches to standard output");
        outfile = savestr (optarg);
        break;
        case 'p':
        strippath = numeric_string (optarg, false, "strip count");
        break;
        case 'r':
        rejname = savestr (optarg);
        break;
        case 'R':
        reverse = true;
        reverse_flag_specified = true;
        break;
        case 's':
        verbosity = SILENT;
        break;
        case 't':
        batch = true;
        break;
        case 'T':
        set_time = true;
        break;
        case 'u':
        diff_type = UNI_DIFF;
        break;
        case 'v':
        version();
        exit (0);
        break;
        case 'V':
        version_control = optarg;
        version_control_context = "--version-control or -V option";
        break;
#if DEBUGGING
        case 'x':
        debug = numeric_string (optarg, true, "debugging option");
        break;
#endif
        case 'Y':
        if (!*optarg)
          fatal ("backup basename prefix is empty");
        origbase = savestr (optarg);
        break;
        case 'z':
        case_z:
        if (!*optarg)
          fatal ("backup suffix is empty");
        simple_backup_suffix = savestr (optarg);
        break;
        case 'Z':
        set_utc = true;
        break;
        case CHAR_MAX + 1:
        dry_run = true;
        break;
        case CHAR_MAX + 2:
        verbosity = VERBOSE;
        break;
        case CHAR_MAX + 3:
#if HAVE_SETMODE_DOS
        binary_transput = O_BINARY;
#endif
        break;
        case CHAR_MAX + 4:
        usage (stdout, 0);
        case CHAR_MAX + 5:
        backup_if_mismatch = true;
        break;
        case CHAR_MAX + 6:
        backup_if_mismatch = false;
        break;
        case CHAR_MAX + 7:
        posixly_correct = true;
        break;
        case CHAR_MAX + 8:
        {
          int i = argmatch (optarg, quoting_style_args, 0, 0);
          if (i < 0)
            {
              invalid_arg ("quoting style", optarg, i);
              usage (stderr, 2);
            }
          set_quoting_style ((struct quoting_options *) 0,
                     (enum quoting_style) i);
        }
        break;
        default:
        usage (stderr, 2);
    }
    }

    /* Process any filename args.  */
    if (optind < Argc)
      {
    inname = savestr (Argv[optind++]);
    invc = -1;
    if (optind < Argc)
      {
        patchname = savestr (Argv[optind++]);
        if (optind < Argc)
          {
        fprintf (stderr, "%s: %s: extra operand\n",
             program_name, quotearg (Argv[optind]));
        usage (stderr, 2);
          }
      }
      }
}

/* Handle STRING (possibly negative if NEGATIVE_ALLOWED is nonzero)
   of type ARGTYPE_MSGID by converting it to an integer,
   returning the result.  */
static int
numeric_string (char const *string,
        bool negative_allowed,
        char const *argtype_msgid)
{
  int value = 0;
  char const *p = string;
  int sign = *p == '-' ? -1 : 1;

  p += *p == '-' || *p == '+';

  do
    {
      int v10 = value * 10;
      int digit = *p - '0';
      int signed_digit = sign * digit;
      int next_value = v10 + signed_digit;

      if (9 < (unsigned) digit)
    fatal ("%s %s is not a number", argtype_msgid, quotearg (string));

      if (v10 / 10 != value || (next_value < v10) != (signed_digit < 0))
    fatal ("%s %s is too large", argtype_msgid, quotearg (string));

      value = next_value;
    }
  while (*++p);

  if (value < 0 && ! negative_allowed)
    fatal ("%s %s is negative", argtype_msgid, quotearg (string));

  return value;
}

/* Attempt to find the right place to apply this hunk of patch. */

static LINENUM
locate_hunk (LINENUM fuzz)
{
    register LINENUM first_guess = pch_first () + last_offset;
    register LINENUM offset;
    LINENUM pat_lines = pch_ptrn_lines();
    LINENUM prefix_context = pch_prefix_context ();
    LINENUM suffix_context = pch_suffix_context ();
    LINENUM context = (prefix_context < suffix_context
               ? suffix_context : prefix_context);
    LINENUM prefix_fuzz = fuzz + prefix_context - context;
    LINENUM suffix_fuzz = fuzz + suffix_context - context;
    LINENUM max_where = input_lines - (pat_lines - suffix_fuzz) + 1;
    LINENUM min_where = last_frozen_line + 1 - (prefix_context - prefix_fuzz);
    LINENUM max_pos_offset = max_where - first_guess;
    LINENUM max_neg_offset = first_guess - min_where;
    LINENUM max_offset = (max_pos_offset < max_neg_offset
              ? max_neg_offset : max_pos_offset);

    if (!pat_lines)         /* null range matches always */
    return first_guess;

    /* Do not try lines <= 0.  */
    if (first_guess <= max_neg_offset)
    max_neg_offset = first_guess - 1;

    if (prefix_fuzz < 0)
      {
    /* Can only match start of file.  */

    if (suffix_fuzz < 0)
      /* Can only match entire file.  */
      if (pat_lines != input_lines || prefix_context < last_frozen_line)
        return 0;

    offset = 1 - first_guess;
    if (last_frozen_line <= prefix_context
        && offset <= max_pos_offset
        && patch_match (first_guess, offset, (LINENUM) 0, suffix_fuzz))
      {
        last_offset += offset;
        return first_guess + offset;
      }
    else
      return 0;
      }

    if (suffix_fuzz < 0)
      {
    /* Can only match end of file.  */
    offset = first_guess - (input_lines - pat_lines + 1);
    if (offset <= max_neg_offset
        && patch_match (first_guess, -offset, prefix_fuzz, (LINENUM) 0))
      {
        last_offset -= offset;
        return first_guess - offset;
      }
    else
      return 0;
      }

    for (offset = 0;  offset <= max_offset;  offset++) {
    char numbuf0[LINENUM_LENGTH_BOUND + 1];
    char numbuf1[LINENUM_LENGTH_BOUND + 1];
    if (offset <= max_pos_offset
        && patch_match (first_guess, offset, prefix_fuzz, suffix_fuzz)) {
        if (debug & 1)
          say ("Offset changing from %s to %s\n",
           format_linenum (numbuf0, last_offset),
           format_linenum (numbuf1, last_offset + offset));
        last_offset += offset;
        return first_guess+offset;
    }
    if (0 < offset && offset <= max_neg_offset
        && patch_match (first_guess, -offset, prefix_fuzz, suffix_fuzz)) {
        if (debug & 1)
          say ("Offset changing from %s to %s\n",
           format_linenum (numbuf0, last_offset),
           format_linenum (numbuf1, last_offset - offset));
        last_offset -= offset;
        return first_guess-offset;
    }
    }
    return 0;
}

/* We did not find the pattern, dump out the hunk so they can handle it. */

static void
abort_hunk (void)
{
    register LINENUM i;
    register LINENUM pat_end = pch_end ();
    /* add in last_offset to guess the same as the previous successful hunk */
    LINENUM oldfirst = pch_first() + last_offset;
    LINENUM newfirst = pch_newfirst() + last_offset;
    LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1;
    LINENUM newlast = newfirst + pch_repl_lines() - 1;
    char const *stars =
      (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ****" : "";
    char const *minuses =
      (int) NEW_CONTEXT_DIFF <= (int) diff_type ? " ----" : " -----";

    fprintf(rejfp, "***************\n");
    for (i=0; i<=pat_end; i++) {
    char numbuf0[LINENUM_LENGTH_BOUND + 1];
    char numbuf1[LINENUM_LENGTH_BOUND + 1];
    switch (pch_char(i)) {
    case '*':
        if (oldlast < oldfirst)
        fprintf(rejfp, "*** 0%s\n", stars);
        else if (oldlast == oldfirst)
        fprintf (rejfp, "*** %s%s\n",
             format_linenum (numbuf0, oldfirst), stars);
        else
        fprintf (rejfp, "*** %s,%s%s\n",
             format_linenum (numbuf0, oldfirst),
             format_linenum (numbuf1, oldlast), stars);
        break;
    case '=':
        if (newlast < newfirst)
        fprintf(rejfp, "--- 0%s\n", minuses);
        else if (newlast == newfirst)
        fprintf (rejfp, "--- %s%s\n",
             format_linenum (numbuf0, newfirst), minuses);
        else
        fprintf (rejfp, "--- %s,%s%s\n",
             format_linenum (numbuf0, newfirst),
             format_linenum (numbuf1, newlast), minuses);
        break;
    case ' ': case '-': case '+': case '!':
        fprintf (rejfp, "%c ", pch_char (i));
        /* fall into */
    case '\n':
        pch_write_line (i, rejfp);
        break;
    default:
        fatal ("fatal internal error in abort_hunk");
    }
    if (ferror (rejfp))
      write_fatal ();
    }
}

/* We found where to apply it (we hope), so do it. */

static bool
apply_hunk (struct outstate *outstate, LINENUM where)
{
    register LINENUM old = 1;
    register LINENUM lastline = pch_ptrn_lines ();
    register LINENUM new = lastline+1;
    register enum {OUTSIDE, IN_IFNDEF, IN_IFDEF, IN_ELSE} def_state = OUTSIDE;
    register char const *R_do_defines = do_defines;
    register LINENUM pat_end = pch_end ();
    register FILE *fp = outstate->ofp;

    where--;
    while (pch_char(new) == '=' || pch_char(new) == '\n')
    new++;

    while (old <= lastline) {
    if (pch_char(old) == '-') {
        assert (outstate->after_newline);
        if (! copy_till (outstate, where + old - 1))
        return false;
        if (R_do_defines) {
        if (def_state == OUTSIDE) {
            fprintf (fp, outstate->after_newline + not_defined,
                 R_do_defines);
            def_state = IN_IFNDEF;
        }
        else if (def_state == IN_IFDEF) {
            fprintf (fp, outstate->after_newline + else_defined);
            def_state = IN_ELSE;
        }
        if (ferror (fp))
          write_fatal ();
        outstate->after_newline = pch_write_line (old, fp);
        outstate->zero_output = false;
        }
        last_frozen_line++;
        old++;
    }
    else if (new > pat_end) {
        break;
    }
    else if (pch_char(new) == '+') {
        if (! copy_till (outstate, where + old - 1))
        return false;
        if (R_do_defines) {
        if (def_state == IN_IFNDEF) {
            fprintf (fp, outstate->after_newline + else_defined);
            def_state = IN_ELSE;
        }
        else if (def_state == OUTSIDE) {
            fprintf (fp, outstate->after_newline + if_defined,
                 R_do_defines);
            def_state = IN_IFDEF;
        }
        if (ferror (fp))
          write_fatal ();
        }
        outstate->after_newline = pch_write_line (new, fp);
        outstate->zero_output = false;
        new++;
    }
    else if (pch_char(new) != pch_char(old)) {
        char numbuf0[LINENUM_LENGTH_BOUND + 1];
        char numbuf1[LINENUM_LENGTH_BOUND + 1];
        if (debug & 1)
          say ("oldchar = '%c', newchar = '%c'\n",
           pch_char (old), pch_char (new));
        fatal ("Out-of-sync patch, lines %s,%s -- mangled text or line numbers, maybe?",
           format_linenum (numbuf0, pch_hunk_beg() + old),
           format_linenum (numbuf1, pch_hunk_beg() + new));
    }
    else if (pch_char(new) == '!') {
        assert (outstate->after_newline);
        if (! copy_till (outstate, where + old - 1))
        return false;
        assert (outstate->after_newline);
        if (R_do_defines) {
           fprintf (fp, 1 + not_defined, R_do_defines);
           if (ferror (fp))
        write_fatal ();
           def_state = IN_IFNDEF;
        }

        do
          {
        if (R_do_defines) {
            outstate->after_newline = pch_write_line (old, fp);
        }
        last_frozen_line++;
        old++;
          }
        while (pch_char (old) == '!');

        if (R_do_defines) {
        fprintf (fp, outstate->after_newline + else_defined);
        if (ferror (fp))
          write_fatal ();
        def_state = IN_ELSE;
        }

        do
          {
        outstate->after_newline = pch_write_line (new, fp);
        new++;
          }
        while (pch_char (new) == '!');
        outstate->zero_output = false;
    }
    else {
        assert(pch_char(new) == ' ');
        old++;
        new++;
        if (R_do_defines && def_state != OUTSIDE) {
        fprintf (fp, outstate->after_newline + end_defined);
        if (ferror (fp))
          write_fatal ();
        outstate->after_newline = true;
        def_state = OUTSIDE;
        }
    }
    }
    if (new <= pat_end && pch_char(new) == '+') {
    if (! copy_till (outstate, where + old - 1))
        return false;
    if (R_do_defines) {
        if (def_state == OUTSIDE) {
        fprintf (fp, outstate->after_newline + if_defined,
             R_do_defines);
        def_state = IN_IFDEF;
        }
        else if (def_state == IN_IFNDEF) {
        fprintf (fp, outstate->after_newline + else_defined);
        def_state = IN_ELSE;
        }
        if (ferror (fp))
          write_fatal ();
        outstate->zero_output = false;
    }

    do
      {
        if (! outstate->after_newline  &&  putc ('\n', fp) == EOF)
          write_fatal ();
        outstate->after_newline = pch_write_line (new, fp);
        outstate->zero_output = false;
        new++;
      }
    while (new <= pat_end && pch_char (new) == '+');
    }
    if (R_do_defines && def_state != OUTSIDE) {
    fprintf (fp, outstate->after_newline + end_defined);
    if (ferror (fp))
      write_fatal ();
    outstate->after_newline = true;
    }
    return true;
}

/* Create an output file.  */

static FILE *
create_output_file (char const *name, int open_flags)
{
  int fd = create_file (name, O_WRONLY | binary_transput | open_flags,
            instat.st_mode);
  FILE *f = fdopen (fd, binary_transput ? "wb" : "w");
  if (! f)
    pfatal ("Can't create file %s", quotearg (name));
  return f;
}

/* Open the new file. */

static void
init_output (char const *name, int open_flags, struct outstate *outstate)
{
  outstate->ofp = name ? create_output_file (name, open_flags) : (FILE *) 0;
  outstate->after_newline = true;
  outstate->zero_output = true;
}

/* Open a file to put hunks we can't locate. */

static void
init_reject (void)
{
  int exclusive = TMPREJNAME_needs_removal ? 0 : O_EXCL;
  TMPREJNAME_needs_removal = 1;
  rejfp = create_output_file (TMPREJNAME, exclusive);
}

/* Copy input file to output, up to wherever hunk is to be applied. */

static bool
copy_till (register struct outstate *outstate, register LINENUM lastline)
{
    register LINENUM R_last_frozen_line = last_frozen_line;
    register FILE *fp = outstate->ofp;
    register char const *s;
    size_t size;

    if (R_last_frozen_line > lastline)
      {
    say ("misordered hunks! output would be garbled\n");
    return false;
      }
    while (R_last_frozen_line < lastline)
      {
    s = ifetch (++R_last_frozen_line, false, &size);
    if (size)
      {
        if ((! outstate->after_newline  &&  putc ('\n', fp) == EOF)
        || ! fwrite (s, sizeof *s, size, fp))
          write_fatal ();
        outstate->after_newline = s[size - 1] == '\n';
        outstate->zero_output = false;
      }
      }
    last_frozen_line = R_last_frozen_line;
    return true;
}

/* Finish copying the input file to the output file. */

static bool
spew_output (struct outstate *outstate)
{
    if (debug & 256)
      {
    char numbuf0[LINENUM_LENGTH_BOUND + 1];
    char numbuf1[LINENUM_LENGTH_BOUND + 1];
    say ("il=%s lfl=%s\n",
         format_linenum (numbuf0, input_lines),
         format_linenum (numbuf1, last_frozen_line));
      }

    if (last_frozen_line < input_lines)
      if (! copy_till (outstate, input_lines))
    return false;

    if (outstate->ofp && ! outfile)
      {
    if (fclose (outstate->ofp) != 0)
      write_fatal ();
    outstate->ofp = 0;
      }

    return true;
}

/* Does the patch pattern match at line base+offset? */

static bool
patch_match (LINENUM base, LINENUM offset,
         LINENUM prefix_fuzz, LINENUM suffix_fuzz)
{
    register LINENUM pline = 1 + prefix_fuzz;
    register LINENUM iline;
    register LINENUM pat_lines = pch_ptrn_lines () - suffix_fuzz;
    size_t size;
    register char const *p;

    for (iline=base+offset+prefix_fuzz; pline <= pat_lines; pline++,iline++) {
    p = ifetch (iline, offset >= 0, &size);
    if (canonicalize) {
        if (!similar(p, size,
             pfetch(pline),
             pch_line_len(pline) ))
        return false;
    }
    else if (size != pch_line_len (pline)
         || memcmp (p, pfetch (pline), size) != 0)
        return false;
    }
    return true;
}

/* Do two lines match with canonicalized white space? */

static bool
similar (register char const *a, register size_t alen,
     register char const *b, register size_t blen)
{
  /* Ignore presence or absence of trailing newlines.  */
  alen  -=  alen && a[alen - 1] == '\n';
  blen  -=  blen && b[blen - 1] == '\n';

  for (;;)
    {
      if (!blen || (*b == ' ' || *b == '\t'))
    {
      while (blen && (*b == ' ' || *b == '\t'))
        b++, blen--;
      if (alen)
        {
          if (!(*a == ' ' || *a == '\t'))
        return false;
          do a++, alen--;
          while (alen && (*a == ' ' || *a == '\t'));
        }
      if (!alen || !blen)
        return alen == blen;
    }
      else if (!alen || *a++ != *b++)
    return false;
      else
    alen--, blen--;
    }
}

/* Make a temporary file.  */

#if HAVE_MKTEMP && ! HAVE_DECL_MKTEMP && ! defined mktemp
char *mktemp (char *);
#endif

#ifndef TMPDIR
#define TMPDIR "/tmp"
#endif

static char const *
make_temp (char letter)
{
  char *r;
#if HAVE_MKTEMP
  char const *tmpdir = getenv ("TMPDIR");   /* Unix tradition */
  if (!tmpdir) tmpdir = getenv ("TMP");     /* DOS tradition */
  if (!tmpdir) tmpdir = getenv ("TEMP");    /* another DOS tradition */
  if (!tmpdir) tmpdir = TMPDIR;
  r = xmalloc (strlen (tmpdir) + 10);
  sprintf (r, "%s/p%cXXXXXX", tmpdir, letter);

  /* It is OK to use mktemp here, since the rest of the code always
     opens temp files with O_EXCL.  It might be better to use mkstemp
     to avoid some DoS problems, but simply substituting mkstemp for
     mktemp here will not fix the DoS problems; a more extensive
     change would be needed.  */
  mktemp (r);

  if (!*r)
    pfatal ("mktemp");
#else
  r = xmalloc (L_tmpnam);
  if (! (tmpnam (r) == r && *r))
    pfatal ("tmpnam");
#endif
  return r;
}

/* Fatal exit with cleanup. */

void
fatal_exit (int sig)
{
  cleanup ();

  if (sig)
    exit_with_signal (sig);

  exit (2);
}

static void
remove_if_needed (char const *name, int volatile *needs_removal)
{
  if (*needs_removal)
    {
      unlink (name);
      *needs_removal = 0;
    }
}

static void
cleanup (void)
{
  remove_if_needed (TMPINNAME, &TMPINNAME_needs_removal);
  remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal);
  remove_if_needed (TMPPATNAME, &TMPPATNAME_needs_removal);
  remove_if_needed (TMPREJNAME, &TMPREJNAME_needs_removal);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/argmatch.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/argmatch.h used in: Step 569
/* argmatch.h -- definitions and prototypes for argmatch.c
   Copyright (C) 1990, 1998, 1999, 2001, 2002 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.  */

/* Written by David MacKenzie <djm@ai.mit.edu>
   Modified by Akim Demaille <demaille@inf.enst.fr> */

#ifndef ARGMATCH_H_
# define ARGMATCH_H_ 1

# include <stddef.h>

# define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))

# define ARGMATCH_CONSTRAINT(Arglist, Vallist) \
  (ARRAY_CARDINALITY (Arglist) == ARRAY_CARDINALITY (Vallist) + 1)

/* Assert there are as many real arguments as there are values
   (argument list ends with a NULL guard).  ARGMATCH_VERIFY is
   preferred, since it is guaranteed to be checked at compile-time.
   ARGMATCH_ASSERT is for backward compatibility only.  */

# define ARGMATCH_VERIFY(Arglist, Vallist)                \
  struct argmatch_verify                          \
  {                                   \
    char argmatch_verify[ARGMATCH_CONSTRAINT(Arglist, Vallist) ? 1 : -1]; \
  }

# define ARGMATCH_ASSERT(Arglist, Vallist) \
  assert (ARGMATCH_CONSTRAINT (Arglist, Vallist))

/* Return the index of the element of ARGLIST (NULL terminated) that
   matches with ARG.  If VALLIST is not NULL, then use it to resolve
   false ambiguities (i.e., different matches of ARG but corresponding
   to the same values in VALLIST).  */

int argmatch (char const *arg, char const *const *arglist,
          char const *vallist, size_t valsize);

# define ARGMATCH(Arg, Arglist, Vallist) \
  argmatch (Arg, Arglist, (char const *) (Vallist), sizeof *(Vallist))

/* xargmatch calls this function when it fails.  This function should not
   return.  By default, this is a function that calls ARGMATCH_DIE which
   in turn defaults to `exit (EXIT_FAILURE)'.  */
typedef void (*argmatch_exit_fn) (void);
extern argmatch_exit_fn argmatch_die;

/* Report on stderr why argmatch failed.  Report correct values. */

void argmatch_invalid (char const *context, char const *value, int problem);

/* Left for compatibility with the old name invalid_arg */

# define invalid_arg(Context, Value, Problem) \
  argmatch_invalid (Context, Value, Problem)



/* Report on stderr the list of possible arguments.  */

void argmatch_valid (char const *const *arglist,
             char const *vallist, size_t valsize);

# define ARGMATCH_VALID(Arglist, Vallist) \
  argmatch_valid (Arglist, (char const *) (Vallist), sizeof *(Vallist))



/* Same as argmatch, but upon failure, reports a explanation on the
   failure, and exits using the function EXIT_FN. */

int __xargmatch_internal (char const *context,
              char const *arg, char const *const *arglist,
              char const *vallist, size_t valsize,
              argmatch_exit_fn exit_fn);

/* Programmer friendly interface to __xargmatch_internal. */

# define XARGMATCH(Context, Arg, Arglist, Vallist)      \
  ((Vallist) [__xargmatch_internal (Context, Arg, Arglist,  \
                    (char const *) (Vallist),   \
                    sizeof *(Vallist),      \
                    argmatch_die)])

/* Convert a value into a corresponding argument. */

char const *argmatch_to_argument (char const *value,
                  char const *const *arglist,
                  char const *vallist, size_t valsize);

# define ARGMATCH_TO_ARGUMENT(Value, Arglist, Vallist)          \
  argmatch_to_argument (Value, Arglist,                 \
            (char const *) (Vallist), sizeof *(Vallist))

#endif /* ARGMATCH_H_ */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/getopt.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/getopt.h used in: Step 569
/* Declarations for getopt.
   Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#ifndef _GETOPT_H

#ifndef __need_getopt
# define _GETOPT_H 1
#endif

/* If __GNU_LIBRARY__ is not already defined, either we are being used
   standalone, or this is the first header included in the source file.
   If we are being used with glibc, we need to include <features.h>, but
   that does not exist if we are standalone.  So: if __GNU_LIBRARY__ is
   not defined, include <ctype.h>, which will pull in <features.h> for us
   if it's from glibc.  (Why ctype.h?  It's guaranteed to exist and it
   doesn't flood the namespace with stuff the way some other headers do.)  */
#if !defined __GNU_LIBRARY__
# include <ctype.h>
#endif

#ifdef  __cplusplus
extern "C" {
#endif

/* For communication from `getopt' to the caller.
   When `getopt' finds an option that takes an argument,
   the argument value is returned here.
   Also, when `ordering' is RETURN_IN_ORDER,
   each non-option ARGV-element is returned here.  */

extern char *optarg;

/* Index in ARGV of the next element to be scanned.
   This is used for communication to and from the caller
   and for communication between successive calls to `getopt'.

   On entry to `getopt', zero means this is the first call; initialize.

   When `getopt' returns -1, this is the index of the first of the
   non-option elements that the caller should itself scan.

   Otherwise, `optind' communicates from one call to the next
   how much of ARGV has been scanned so far.  */

extern int optind;

/* Callers store zero here to inhibit the error message `getopt' prints
   for unrecognized options.  */

extern int opterr;

/* Set to an option character which was unrecognized.  */

extern int optopt;

#ifndef __need_getopt
/* Describe the long-named options requested by the application.
   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
   of `struct option' terminated by an element containing a name which is
   zero.

   The field `has_arg' is:
   no_argument      (or 0) if the option does not take an argument,
   required_argument    (or 1) if the option requires an argument,
   optional_argument    (or 2) if the option takes an optional argument.

   If the field `flag' is not NULL, it points to a variable that is set
   to the value given in the field `val' when the option is found, but
   left unchanged if the option is not found.

   To have a long-named option do something other than set an `int' to
   a compiled-in constant, such as set a value from `optarg', set the
   option's `flag' field to zero and its `val' field to a nonzero
   value (the equivalent single-letter option character, if there is
   one).  For long options that have a zero `flag' field, `getopt'
   returns the contents of the `val' field.  */

struct option
{
# if (defined __STDC__ && __STDC__) || defined __cplusplus
  const char *name;
# else
  char *name;
# endif
  /* has_arg can't be an enum because some compilers complain about
     type mismatches in all the code that assumes it is an int.  */
  int has_arg;
  int *flag;
  int val;
};

/* Names for the values of the `has_arg' field of `struct option'.  */

# define no_argument        0
# define required_argument  1
# define optional_argument  2
#endif  /* need getopt */


/* Get definitions and prototypes for functions to process the
   arguments in ARGV (ARGC of them, minus the program name) for
   options given in OPTS.

   Return the option character from OPTS just read.  Return -1 when
   there are no more options.  For unrecognized options, or options
   missing arguments, `optopt' is set to the option letter, and '?' is
   returned.

   The OPTS string is a list of characters which are recognized option
   letters, optionally followed by colons, specifying that that letter
   takes an argument, to be placed in `optarg'.

   If a letter in OPTS is followed by two colons, its argument is
   optional.  This behavior is specific to the GNU `getopt'.

   The argument `--' causes premature termination of argument
   scanning, explicitly telling `getopt' that there are no more
   options.

   If OPTS begins with `--', then non-option arguments are treated as
   arguments to the option '\0'.  This behavior is specific to the GNU
   `getopt'.  */

#if (defined __STDC__ && __STDC__) || defined __cplusplus
# ifdef __GNU_LIBRARY__
/* Many other libraries have conflicting prototypes for getopt, with
   differences in the consts, in stdlib.h.  To avoid compilation
   errors, only prototype getopt for the GNU C library.  */
extern int getopt (int ___argc, char *const *___argv, const char *__shortopts);
# else /* not __GNU_LIBRARY__ */
extern int getopt ();
# endif /* __GNU_LIBRARY__ */

# ifndef __need_getopt
extern int getopt_long (int ___argc, char *const *___argv,
            const char *__shortopts,
                const struct option *__longopts, int *__longind);
extern int getopt_long_only (int ___argc, char *const *___argv,
                 const char *__shortopts,
                     const struct option *__longopts, int *__longind);

/* Internal only.  Users should not call this directly.  */
extern int _getopt_internal (int ___argc, char *const *___argv,
                 const char *__shortopts,
                     const struct option *__longopts, int *__longind,
                 int __long_only);
# endif
#else /* not __STDC__ */
extern int getopt ();
# ifndef __need_getopt
extern int getopt_long ();
extern int getopt_long_only ();

extern int _getopt_internal ();
# endif
#endif /* __STDC__ */

#ifdef  __cplusplus
}
#endif

/* Make sure we later can get all the definitions and declarations.  */
#undef __need_getopt

#endif /* getopt.h */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/version.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/version.h used in: Step 569, Step 574, Step 575
/* Print the version number.  */

/* $Id: version.h,v 1.5 2002/05/28 07:24:05 eggert Exp $ */

void version (void);

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/pch.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/pch.c used in: Step 570
/* reading patches */

/* $Id: pch.c,v 1.44 2003/05/20 14:03:17 eggert Exp $ */

/* Copyright (C) 1986, 1987, 1988 Larry Wall

   Copyright (C) 1990, 1991, 1992, 1993, 1997, 1998, 1999, 2000, 2001,
   2002, 2003 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#define XTERN extern
#include <common.h>
#include <backupfile.h>
#include <dirname.h>
#include <inp.h>
#include <quotearg.h>
#include <util.h>
#undef XTERN
#define XTERN
#include <pch.h>

#define INITHUNKMAX 125         /* initial dynamic allocation size */

/* Patch (diff listing) abstract type. */

static FILE *pfp;           /* patch file pointer */
static int p_says_nonexistent[2];   /* [0] for old file, [1] for new:
        0 for existent and nonempty,
        1 for existent and probably (but not necessarily) empty,
        2 for nonexistent */
static int p_rfc934_nesting;        /* RFC 934 nesting level */
static time_t p_timestamp[2];       /* timestamps in patch headers */
static off_t p_filesize;        /* size of the patch file */
static LINENUM p_first;         /* 1st line number */
static LINENUM p_newfirst;      /* 1st line number of replacement */
static LINENUM p_ptrn_lines;        /* # lines in pattern */
static LINENUM p_repl_lines;        /* # lines in replacement text */
static LINENUM p_end = -1;      /* last line in hunk */
static LINENUM p_max;           /* max allowed value of p_end */
static LINENUM p_prefix_context;    /* # of prefix context lines */
static LINENUM p_suffix_context;    /* # of suffix context lines */
static LINENUM p_input_line;        /* current line # from patch file */
static char **p_line;           /* the text of the hunk */
static size_t *p_len;           /* line length including \n if any */
static char *p_Char;            /* +, -, and ! */
static LINENUM hunkmax = INITHUNKMAX;   /* size of above arrays */
static int p_indent;            /* indent to patch */
static bool p_strip_trailing_cr;    /* true if stripping trailing \r */
static bool p_pass_comments_through;    /* true if not ignoring # lines */
static file_offset p_base;      /* where to intuit this time */
static LINENUM p_bline;         /* line # of p_base */
static file_offset p_start;     /* where intuit found a patch */
static LINENUM p_sline;         /* and the line number for it */
static LINENUM p_hunk_beg;      /* line number of current hunk */
static LINENUM p_efake = -1;        /* end of faked up lines--don't free */
static LINENUM p_bfake = -1;        /* beg of faked up lines */

enum nametype { OLD, NEW, INDEX, NONE };

static char *scan_linenum (char *, LINENUM *);
static enum diff intuit_diff_type (void);
static enum nametype best_name (char * const *, int const *);
static int prefix_components (char *, bool);
static size_t pget_line (int, int, bool, bool);
static size_t get_line (void);
static bool incomplete_line (void);
static bool grow_hunkmax (void);
static void malformed (void) __attribute__ ((noreturn));
static void next_intuit_at (file_offset, LINENUM);
static void skip_to (file_offset, LINENUM);
static char get_ed_command_letter (char const *);

/* Prepare to look for the next patch in the patch file. */

void
re_patch (void)
{
    p_first = 0;
    p_newfirst = 0;
    p_ptrn_lines = 0;
    p_repl_lines = 0;
    p_end = -1;
    p_max = 0;
    p_indent = 0;
    p_strip_trailing_cr = false;
}

/* Open the patch file at the beginning of time. */

void
open_patch_file (char const *filename)
{
    file_offset file_pos = 0;
    struct stat st;
    if (!filename || !*filename || strEQ (filename, "-"))
      {
    file_offset stdin_pos;
#if HAVE_SETMODE_DOS
    if (binary_transput)
      {
        if (isatty (STDIN_FILENO))
          fatal ("cannot read binary data from tty on this platform");
        setmode (STDIN_FILENO, O_BINARY);
      }
#endif
    if (fstat (STDIN_FILENO, &st) != 0)
      pfatal ("fstat");
    if (S_ISREG (st.st_mode) && (stdin_pos = file_tell (stdin)) != -1)
      {
        pfp = stdin;
        file_pos = stdin_pos;
      }
    else
      {
        size_t charsread;
        int exclusive = TMPPATNAME_needs_removal ? 0 : O_EXCL;
        TMPPATNAME_needs_removal = 1;
        pfp = fdopen (create_file (TMPPATNAME,
                       O_RDWR | O_BINARY | exclusive,
                       (mode_t) 0),
              "w+b");
        if (!pfp)
          pfatal ("Can't open stream for file %s", quotearg (TMPPATNAME));
        for (st.st_size = 0;
         (charsread = fread (buf, 1, bufsize, stdin)) != 0;
         st.st_size += charsread)
          if (fwrite (buf, 1, charsread, pfp) != charsread)
        write_fatal ();
        if (ferror (stdin) || fclose (stdin) != 0)
          read_fatal ();
        if (fflush (pfp) != 0
        || file_seek (pfp, (file_offset) 0, SEEK_SET) != 0)
          write_fatal ();
      }
      }
    else
      {
    pfp = fopen (filename, binary_transput ? "rb" : "r");
    if (!pfp)
      pfatal ("Can't open patch file %s", quotearg (filename));
    if (fstat (fileno (pfp), &st) != 0)
      pfatal ("fstat");
      }
    p_filesize = st.st_size;
    if (p_filesize != (file_offset) p_filesize)
      fatal ("patch file is too long");
    next_intuit_at (file_pos, (LINENUM) 1);
    set_hunkmax();
}

/* Make sure our dynamically realloced tables are malloced to begin with. */

void
set_hunkmax (void)
{
    if (!p_line)
    p_line = (char **) malloc (hunkmax * sizeof *p_line);
    if (!p_len)
    p_len = (size_t *) malloc (hunkmax * sizeof *p_len);
    if (!p_Char)
    p_Char = malloc (hunkmax * sizeof *p_Char);
}

/* Enlarge the arrays containing the current hunk of patch. */

static bool
grow_hunkmax (void)
{
    hunkmax *= 2;
    assert (p_line && p_len && p_Char);
    if ((p_line = (char **) realloc (p_line, hunkmax * sizeof (*p_line)))
    && (p_len = (size_t *) realloc (p_len, hunkmax * sizeof (*p_len)))
    && (p_Char = realloc (p_Char, hunkmax * sizeof (*p_Char))))
      return true;
    if (!using_plan_a)
      memory_fatal ();
    /* Don't free previous values of p_line etc.,
       since some broken implementations free them for us.
       Whatever is null will be allocated again from within plan_a (),
       of all places.  */
    return false;
}

/* True if the remainder of the patch file contains a diff of some sort. */

bool
there_is_another_patch (void)
{
    if (p_base != 0 && p_base >= p_filesize) {
    if (verbosity == VERBOSE)
        say ("done\n");
    return false;
    }
    if (verbosity == VERBOSE)
    say ("Hmm...");
    diff_type = intuit_diff_type();
    if (diff_type == NO_DIFF) {
    if (verbosity == VERBOSE)
      say (p_base
           ? "  Ignoring the trailing garbage.\ndone\n"
           : "  I can't seem to find a patch in there anywhere.\n");
    if (! p_base && p_filesize)
      fatal ("Only garbage was found in the patch input.");
    return false;
    }
    if (skip_rest_of_patch)
      {
    Fseek (pfp, p_start, SEEK_SET);
    p_input_line = p_sline - 1;
    return true;
      }
    if (verbosity == VERBOSE)
    say ("  %sooks like %s to me...\n",
        (p_base == 0 ? "L" : "The next patch l"),
        diff_type == UNI_DIFF ? "a unified diff" :
        diff_type == CONTEXT_DIFF ? "a context diff" :
        diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" :
        diff_type == NORMAL_DIFF ? "a normal diff" :
        "an ed script" );

    if (verbosity != SILENT)
      {
    if (p_indent)
      say ("(Patch is indented %d space%s.)\n", p_indent, p_indent==1?"":"s");
    if (p_strip_trailing_cr)
      say ("(Stripping trailing CRs from patch.)\n");
    if (! inname)
      {
        char numbuf[LINENUM_LENGTH_BOUND + 1];
        say ("can't find file to patch at input line %s\n",
         format_linenum (numbuf, p_sline));
        if (diff_type != ED_DIFF)
          say (strippath == -1
           ? "Perhaps you should have used the -p or --strip option?\n"
           : "Perhaps you used the wrong -p or --strip option?\n");
      }
      }

    skip_to(p_start,p_sline);
    while (!inname) {
    if (force | batch) {
        say ("No file to patch.  Skipping patch.\n");
        skip_rest_of_patch = true;
        return true;
    }
    ask ("File to patch: ");
    inname = fetchname (buf, 0, (time_t *) 0);
    if (inname)
      {
        if (stat (inname, &instat) == 0)
          {
        inerrno = 0;
        invc = -1;
          }
        else
          {
        perror (inname);
        fflush (stderr);
        free (inname);
        inname = 0;
          }
      }
    if (!inname) {
        ask ("Skip this patch? [y] ");
        if (*buf != 'n') {
        if (verbosity != SILENT)
            say ("Skipping patch.\n");
        skip_rest_of_patch = true;
        return true;
        }
    }
    }
    return true;
}

/* Determine what kind of diff is in the remaining part of the patch file. */

static enum diff
intuit_diff_type (void)
{
    register file_offset this_line = 0;
    register file_offset first_command_line = -1;
    char first_ed_command_letter = 0;
    LINENUM fcl_line = 0; /* Pacify `gcc -W'.  */
    register bool this_is_a_command = false;
    register bool stars_this_line = false;
    enum nametype i;
    char *name[3];
    struct stat st[3];
    int stat_errno[3];
    int version_controlled[3];
    register enum diff retval;

    name[OLD] = name[NEW] = name[INDEX] = 0;
    version_controlled[OLD] = -1;
    version_controlled[NEW] = -1;
    version_controlled[INDEX] = -1;
    p_rfc934_nesting = 0;
    p_timestamp[OLD] = p_timestamp[NEW] = (time_t) -1;
    p_says_nonexistent[OLD] = p_says_nonexistent[NEW] = 0;
    Fseek (pfp, p_base, SEEK_SET);
    p_input_line = p_bline - 1;
    for (;;) {
    register char *s;
    register char *t;
    register file_offset previous_line = this_line;
    register bool last_line_was_command = this_is_a_command;
    register bool stars_last_line = stars_this_line;
    register int indent = 0;
    char ed_command_letter;
    bool strip_trailing_cr;
    size_t chars_read;

    this_line = file_tell (pfp);
    chars_read = pget_line (0, 0, false, false);
    if (chars_read == (size_t) -1)
      memory_fatal ();
    if (! chars_read) {
        if (first_ed_command_letter) {
                    /* nothing but deletes!? */
        p_start = first_command_line;
        p_sline = fcl_line;
        retval = ED_DIFF;
        goto scan_exit;
        }
        else {
        p_start = this_line;
        p_sline = p_input_line;
        return NO_DIFF;
        }
    }
    strip_trailing_cr = 2 <= chars_read && buf[chars_read - 2] == '\r';
    for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) {
        if (*s == '\t')
        indent = (indent + 8) & ~7;
        else
        indent++;
    }
    for (t = s;  ISDIGIT (*t) || *t == ',';  t++)
      continue;
    this_is_a_command = (ISDIGIT (*s) &&
      (*t == 'd' || *t == 'c' || *t == 'a') );
    if (first_command_line < 0
        && ((ed_command_letter = get_ed_command_letter (s))
        || this_is_a_command)) {
        first_command_line = this_line;
        first_ed_command_letter = ed_command_letter;
        fcl_line = p_input_line;
        p_indent = indent;      /* assume this for now */
        p_strip_trailing_cr = strip_trailing_cr;
    }
    if (!stars_last_line && strnEQ(s, "*** ", 4))
        name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]);
    else if (strnEQ(s, "+++ ", 4))
        /* Swap with NEW below.  */
        name[OLD] = fetchname (s+4, strippath, &p_timestamp[OLD]);
    else if (strnEQ(s, "Index:", 6))
        name[INDEX] = fetchname (s+6, strippath, (time_t *) 0);
    else if (strnEQ(s, "Prereq:", 7)) {
        for (t = s + 7;  ISSPACE ((unsigned char) *t);  t++)
          continue;
        revision = t;
        for (t = revision;  *t;  t++)
          if (ISSPACE ((unsigned char) *t))
        {
          char const *u;
          for (u = t + 1;  ISSPACE ((unsigned char) *u);  u++)
            continue;
          if (*u)
            {
              char numbuf[LINENUM_LENGTH_BOUND + 1];
              say ("Prereq: with multiple words at line %s of patch\n",
               format_linenum (numbuf, this_line));
            }
          break;
        }
        if (t == revision)
        revision = 0;
        else {
        char oldc = *t;
        *t = '\0';
        revision = savestr (revision);
        *t = oldc;
        }
    } else
      {
        for (t = s;  t[0] == '-' && t[1] == ' ';  t += 2)
          continue;
        if (strnEQ(t, "--- ", 4))
          {
        time_t timestamp = (time_t) -1;
        name[NEW] = fetchname (t+4, strippath, &timestamp);
        if (timestamp != (time_t) -1)
          {
            p_timestamp[NEW] = timestamp;
            p_rfc934_nesting = (t - s) >> 1;
          }
          }
      }
    if ((diff_type == NO_DIFF || diff_type == ED_DIFF) &&
      first_command_line >= 0 &&
      strEQ(s, ".\n") ) {
        p_start = first_command_line;
        p_sline = fcl_line;
        retval = ED_DIFF;
        goto scan_exit;
    }
    if ((diff_type == NO_DIFF || diff_type == UNI_DIFF)
        && strnEQ(s, "@@ -", 4)) {

        /* `name' and `p_timestamp' are backwards; swap them.  */
        time_t ti = p_timestamp[OLD];
        p_timestamp[OLD] = p_timestamp[NEW];
        p_timestamp[NEW] = ti;
        t = name[OLD];
        name[OLD] = name[NEW];
        name[NEW] = t;

        s += 4;
        if (s[0] == '0' && !ISDIGIT (s[1]))
          p_says_nonexistent[OLD] = 1 + ! p_timestamp[OLD];
        while (*s != ' ' && *s != '\n')
          s++;
        while (*s == ' ')
          s++;
        if (s[0] == '+' && s[1] == '0' && !ISDIGIT (s[2]))
          p_says_nonexistent[NEW] = 1 + ! p_timestamp[NEW];
        p_indent = indent;
        p_start = this_line;
        p_sline = p_input_line;
        retval = UNI_DIFF;
        if (! ((name[OLD] || ! p_timestamp[OLD])
           && (name[NEW] || ! p_timestamp[NEW]))
        && ! name[INDEX])
          {
        char numbuf[LINENUM_LENGTH_BOUND + 1];
        say ("missing header for unified diff at line %s of patch\n",
             format_linenum (numbuf, p_sline));
          }
        goto scan_exit;
    }
    stars_this_line = strnEQ(s, "********", 8);
    if ((diff_type == NO_DIFF
         || diff_type == CONTEXT_DIFF
         || diff_type == NEW_CONTEXT_DIFF)
        && stars_last_line && strnEQ (s, "*** ", 4)) {
        s += 4;
        if (s[0] == '0' && !ISDIGIT (s[1]))
          p_says_nonexistent[OLD] = 1 + ! p_timestamp[OLD];
        /* if this is a new context diff the character just before */
        /* the newline is a '*'. */
        while (*s != '\n')
        s++;
        p_indent = indent;
        p_strip_trailing_cr = strip_trailing_cr;
        p_start = previous_line;
        p_sline = p_input_line - 1;
        retval = (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF);

        {
          /* Scan the first hunk to see whether the file contents
         appear to have been deleted.  */
          file_offset saved_p_base = p_base;
          LINENUM saved_p_bline = p_bline;
          Fseek (pfp, previous_line, SEEK_SET);
          p_input_line -= 2;
          if (another_hunk (retval, false)
          && ! p_repl_lines && p_newfirst == 1)
        p_says_nonexistent[NEW] = 1 + ! p_timestamp[NEW];
          next_intuit_at (saved_p_base, saved_p_bline);
        }

        if (! ((name[OLD] || ! p_timestamp[OLD])
           && (name[NEW] || ! p_timestamp[NEW]))
        && ! name[INDEX])
          {
        char numbuf[LINENUM_LENGTH_BOUND + 1];
        say ("missing header for context diff at line %s of patch\n",
             format_linenum (numbuf, p_sline));
          }
        goto scan_exit;
    }
    if ((diff_type == NO_DIFF || diff_type == NORMAL_DIFF) &&
      last_line_was_command &&
      (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2)) ) {
        p_start = previous_line;
        p_sline = p_input_line - 1;
        p_indent = indent;
        p_strip_trailing_cr = strip_trailing_cr;
        retval = NORMAL_DIFF;
        goto scan_exit;
    }
    }

  scan_exit:

    /* To intuit `inname', the name of the file to patch,
       use the algorithm specified by POSIX 1003.1-2001 XCU lines 25680-26599
       (with some modifications if posixly_correct is zero):

       - Take the old and new names from the context header if present,
     and take the index name from the `Index:' line if present and
     if either the old and new names are both absent
     or posixly_correct is nonzero.
     Consider the file names to be in the order (old, new, index).
       - If some named files exist, use the first one if posixly_correct
     is nonzero, the best one otherwise.
       - If patch_get is nonzero, and no named files exist,
     but an RCS or SCCS master file exists,
     use the first named file with an RCS or SCCS master.
       - If no named files exist, no RCS or SCCS master was found,
     some names are given, posixly_correct is zero,
     and the patch appears to create a file, then use the best name
     requiring the creation of the fewest directories.
       - Otherwise, report failure by setting `inname' to 0;
     this causes our invoker to ask the user for a file name.  */

    i = NONE;

    if (!inname)
      {
    enum nametype i0 = NONE;

    if (! posixly_correct && (name[OLD] || name[NEW]) && name[INDEX])
      {
        free (name[INDEX]);
        name[INDEX] = 0;
      }

    for (i = OLD;  i <= INDEX;  i++)
      if (name[i])
        {
          if (i0 != NONE && strcmp (name[i0], name[i]) == 0)
        {
          /* It's the same name as before; reuse stat results.  */
          stat_errno[i] = stat_errno[i0];
          if (! stat_errno[i])
            st[i] = st[i0];
        }
          else if (stat (name[i], &st[i]) != 0)
        stat_errno[i] = errno;
          else
        {
          stat_errno[i] = 0;
          if (posixly_correct)
            break;
        }
          i0 = i;
        }

    if (! posixly_correct)
      {
        bool is_empty;

        i = best_name (name, stat_errno);

        if (i == NONE && patch_get)
          {
        enum nametype nope = NONE;

        for (i = OLD;  i <= INDEX;  i++)
          if (name[i])
            {
              char const *cs;
              char *getbuf;
              char *diffbuf;
              bool readonly = (outfile
                       && strcmp (outfile, name[i]) != 0);

              if (nope == NONE || strcmp (name[nope], name[i]) != 0)
            {
              cs = (version_controller
                    (name[i], readonly, (struct stat *) 0,
                 &getbuf, &diffbuf));
              version_controlled[i] = !! cs;
              if (cs)
                {
                  if (version_get (name[i], cs, false, readonly,
                           getbuf, &st[i]))
                stat_errno[i] = 0;
                  else
                version_controlled[i] = 0;

                  free (getbuf);
                  if (diffbuf)
                free (diffbuf);

                  if (! stat_errno[i])
                break;
                }
            }

              nope = i;
            }
          }

        is_empty = i == NONE || st[i].st_size == 0;
        if ((! is_empty) < p_says_nonexistent[reverse ^ is_empty])
          {
        assert (i0 != NONE);
        reverse ^=
          ok_to_reverse
            ("The next patch%s would %s the file %s,\nwhich %s!",
             reverse ? ", when reversed," : "",
             (i == NONE ? "delete"
              : st[i].st_size == 0 ? "empty out"
              : "create"),
             quotearg (name[i == NONE || st[i].st_size == 0 ? i0 : i]),
             (i == NONE ? "does not exist"
              : st[i].st_size == 0 ? "is already empty"
              : "already exists"));
          }

        if (i == NONE && p_says_nonexistent[reverse])
          {
        int newdirs[3];
        int newdirs_min = INT_MAX;
        int distance_from_minimum[3];

        for (i = OLD;  i <= INDEX;  i++)
          if (name[i])
            {
              newdirs[i] = (prefix_components (name[i], false)
                    - prefix_components (name[i], true));
              if (newdirs[i] < newdirs_min)
            newdirs_min = newdirs[i];
            }

        for (i = OLD;  i <= INDEX;  i++)
          if (name[i])
            distance_from_minimum[i] = newdirs[i] - newdirs_min;

        i = best_name (name, distance_from_minimum);
          }
      }
      }

    if (i == NONE)
      inerrno = -1;
    else
      {
    inname = name[i];
    name[i] = 0;
    inerrno = stat_errno[i];
    invc = version_controlled[i];
    instat = st[i];
      }

    for (i = OLD;  i <= INDEX;  i++)
      if (name[i])
    free (name[i]);

    return retval;
}

/* Count the path name components in FILENAME's prefix.
   If CHECKDIRS is true, count only existing directories.  */
static int
prefix_components (char *filename, bool checkdirs)
{
  int count = 0;
  struct stat stat_buf;
  int stat_result;
  char *f = filename + FILESYSTEM_PREFIX_LEN (filename);

  if (*f)
    while (*++f)
      if (ISSLASH (f[0]) && ! ISSLASH (f[-1]))
    {
      if (checkdirs)
        {
          *f = '\0';
          stat_result = stat (filename, &stat_buf);
          *f = '/';
          if (! (stat_result == 0 && S_ISDIR (stat_buf.st_mode)))
        break;
        }

      count++;
    }

  return count;
}

/* Return the index of the best of NAME[OLD], NAME[NEW], and NAME[INDEX].
   Ignore null names, and ignore NAME[i] if IGNORE[i] is nonzero.
   Return NONE if all names are ignored.  */
static enum nametype
best_name (char *const *name, int const *ignore)
{
  enum nametype i;
  int components[3];
  int components_min = INT_MAX;
  size_t basename_len[3];
  size_t basename_len_min = SIZE_MAX;
  size_t len[3];
  size_t len_min = SIZE_MAX;

  for (i = OLD;  i <= INDEX;  i++)
    if (name[i] && !ignore[i])
      {
    /* Take the names with the fewest prefix components.  */
    components[i] = prefix_components (name[i], false);
    if (components_min < components[i])
      continue;
    components_min = components[i];

    /* Of those, take the names with the shortest basename.  */
    basename_len[i] = strlen (base_name (name[i]));
    if (basename_len_min < basename_len[i])
      continue;
    basename_len_min = basename_len[i];

    /* Of those, take the shortest names.  */
    len[i] = strlen (name[i]);
    if (len_min < len[i])
      continue;
    len_min = len[i];
      }

  /* Of those, take the first name.  */
  for (i = OLD;  i <= INDEX;  i++)
    if (name[i] && !ignore[i]
    && components[i] == components_min
    && basename_len[i] == basename_len_min
    && len[i] == len_min)
      break;

  return i;
}

/* Remember where this patch ends so we know where to start up again. */

static void
next_intuit_at (file_offset file_pos, LINENUM file_line)
{
    p_base = file_pos;
    p_bline = file_line;
}

/* Basically a verbose fseek() to the actual diff listing. */

static void
skip_to (file_offset file_pos, LINENUM file_line)
{
    register FILE *i = pfp;
    register FILE *o = stdout;
    register int c;

    assert(p_base <= file_pos);
    if ((verbosity == VERBOSE || !inname) && p_base < file_pos) {
    Fseek (i, p_base, SEEK_SET);
    say ("The text leading up to this was:\n--------------------------\n");

    while (file_tell (i) < file_pos)
      {
        putc ('|', o);
        do
          {
        if ((c = getc (i)) == EOF)
          read_fatal ();
        putc (c, o);
          }
        while (c != '\n');
      }

    say ("--------------------------\n");
    }
    else
    Fseek (i, file_pos, SEEK_SET);
    p_input_line = file_line - 1;
}

/* Make this a function for better debugging.  */
static void
malformed (void)
{
    char numbuf[LINENUM_LENGTH_BOUND + 1];
    fatal ("malformed patch at line %s: %s",
       format_linenum (numbuf, p_input_line), buf);
        /* about as informative as "Syntax error" in C */
}

/* Parse a line number from a string.
   Return the address of the first char after the number.  */
static char *
scan_linenum (char *s0, LINENUM *linenum)
{
  char *s;
  LINENUM n = 0;
  bool overflow = false;
  char numbuf[LINENUM_LENGTH_BOUND + 1];

  for (s = s0;  ISDIGIT (*s);  s++)
    {
      LINENUM new_n = 10 * n + (*s - '0');
      overflow |= new_n / 10 != n;
      n = new_n;
    }

  if (s == s0)
    fatal ("missing line number at line %s: %s",
       format_linenum (numbuf, p_input_line), buf);

  if (overflow)
    fatal ("line number %.*s is too large at line %s: %s",
       (int) (s - s0), s0, format_linenum (numbuf, p_input_line), buf);

  *linenum = n;
  return s;
}

/* 1 if there is more of the current diff listing to process;
   0 if not; -1 if ran out of memory. */

int
another_hunk (enum diff difftype, bool rev)
{
    register char *s;
    register LINENUM context = 0;
    register size_t chars_read;
    char numbuf0[LINENUM_LENGTH_BOUND + 1];
    char numbuf1[LINENUM_LENGTH_BOUND + 1];
    char numbuf2[LINENUM_LENGTH_BOUND + 1];
    char numbuf3[LINENUM_LENGTH_BOUND + 1];

    while (p_end >= 0) {
    if (p_end == p_efake)
        p_end = p_bfake;        /* don't free twice */
    else
        free(p_line[p_end]);
    p_end--;
    }
    assert(p_end == -1);
    p_efake = -1;

    p_max = hunkmax;            /* gets reduced when --- found */
    if (difftype == CONTEXT_DIFF || difftype == NEW_CONTEXT_DIFF) {
    file_offset line_beginning = file_tell (pfp);
                    /* file pos of the current line */
    LINENUM repl_beginning = 0; /* index of --- line */
    register LINENUM fillcnt = 0;   /* #lines of missing ptrn or repl */
    register LINENUM fillsrc;   /* index of first line to copy */
    register LINENUM filldst;   /* index of first missing line */
    bool ptrn_spaces_eaten = false; /* ptrn was slightly misformed */
    bool some_context = false;  /* (perhaps internal) context seen */
    register bool repl_could_be_missing = true;
    bool ptrn_missing = false;  /* The pattern was missing.  */
    bool repl_missing = false;  /* Likewise for replacement.  */
    file_offset repl_backtrack_position = 0;
                    /* file pos of first repl line */
    LINENUM repl_patch_line;    /* input line number for same */
    LINENUM repl_context;       /* context for same */
    LINENUM ptrn_prefix_context = -1; /* lines in pattern prefix context */
    LINENUM ptrn_suffix_context = -1; /* lines in pattern suffix context */
    LINENUM repl_prefix_context = -1; /* lines in replac. prefix context */
    LINENUM ptrn_copiable = 0;  /* # of copiable lines in ptrn */
    LINENUM repl_copiable = 0;  /* Likewise for replacement.  */

    /* Pacify `gcc -Wall'.  */
    fillsrc = filldst = repl_patch_line = repl_context = 0;

    chars_read = get_line ();
    if (chars_read == (size_t) -1
        || chars_read <= 8
        || strncmp (buf, "********", 8) != 0) {
        next_intuit_at(line_beginning,p_input_line);
        return chars_read == (size_t) -1 ? -1 : 0;
    }
    p_hunk_beg = p_input_line + 1;
    while (p_end < p_max) {
        chars_read = get_line ();
        if (chars_read == (size_t) -1)
          return -1;
        if (!chars_read) {
        if (repl_beginning && repl_could_be_missing) {
            repl_missing = true;
            goto hunk_done;
        }
        if (p_max - p_end < 4) {
            strcpy (buf, "  \n");  /* assume blank lines got chopped */
            chars_read = 3;
        } else {
            fatal ("unexpected end of file in patch");
        }
        }
        p_end++;
        if (p_end == hunkmax)
          fatal ("unterminated hunk starting at line %s; giving up at line %s: %s",
             format_linenum (numbuf0, pch_hunk_beg ()),
             format_linenum (numbuf1, p_input_line), buf);
        assert(p_end < hunkmax);
        p_Char[p_end] = *buf;
        p_len[p_end] = 0;
        p_line[p_end] = 0;
        switch (*buf) {
        case '*':
        if (strnEQ(buf, "********", 8)) {
            if (repl_beginning && repl_could_be_missing) {
            repl_missing = true;
            goto hunk_done;
            }
            else
              fatal ("unexpected end of hunk at line %s",
                 format_linenum (numbuf0, p_input_line));
        }
        if (p_end != 0) {
            if (repl_beginning && repl_could_be_missing) {
            repl_missing = true;
            goto hunk_done;
            }
            fatal ("unexpected `***' at line %s: %s",
               format_linenum (numbuf0, p_input_line), buf);
        }
        context = 0;
        p_len[p_end] = strlen (buf);
        if (! (p_line[p_end] = savestr (buf))) {
            p_end--;
            return -1;
        }
        for (s = buf;  *s && !ISDIGIT (*s);  s++)
          continue;
        if (strnEQ(s,"0,0",3))
            remove_prefix (s, 2);
        s = scan_linenum (s, &p_first);
        if (*s == ',') {
            while (*s && !ISDIGIT (*s))
              s++;
            scan_linenum (s, &p_ptrn_lines);
            p_ptrn_lines += 1 - p_first;
        }
        else if (p_first)
            p_ptrn_lines = 1;
        else {
            p_ptrn_lines = 0;
            p_first = 1;
        }
        p_max = p_ptrn_lines + 6;   /* we need this much at least */
        while (p_max >= hunkmax)
            if (! grow_hunkmax ())
            return -1;
        p_max = hunkmax;
        break;
        case '-':
        if (buf[1] != '-')
          goto change_line;
        if (ptrn_prefix_context == -1)
          ptrn_prefix_context = context;
        ptrn_suffix_context = context;
        if (repl_beginning
            || (p_end
            != p_ptrn_lines + 1 + (p_Char[p_end - 1] == '\n')))
          {
            if (p_end == 1)
              {
            /* `Old' lines were omitted.  Set up to fill
               them in from `new' context lines.  */
            ptrn_missing = true;
            p_end = p_ptrn_lines + 1;
            ptrn_prefix_context = ptrn_suffix_context = -1;
            fillsrc = p_end + 1;
            filldst = 1;
            fillcnt = p_ptrn_lines;
              }
            else if (! repl_beginning)
              fatal ("%s `---' at line %s; check line numbers at line %s",
                 (p_end <= p_ptrn_lines
                  ? "Premature"
                  : "Overdue"),
                 format_linenum (numbuf0, p_input_line),
                 format_linenum (numbuf1, p_hunk_beg));
            else if (! repl_could_be_missing)
              fatal ("duplicate `---' at line %s; check line numbers at line %s",
                 format_linenum (numbuf0, p_input_line),
                 format_linenum (numbuf1,
                         p_hunk_beg + repl_beginning));
            else
              {
            repl_missing = true;
            goto hunk_done;
              }
          }
        repl_beginning = p_end;
        repl_backtrack_position = file_tell (pfp);
        repl_patch_line = p_input_line;
        repl_context = context;
        p_len[p_end] = strlen (buf);
        if (! (p_line[p_end] = savestr (buf)))
          {
            p_end--;
            return -1;
          }
        p_Char[p_end] = '=';
        for (s = buf;  *s && ! ISDIGIT (*s);  s++)
          continue;
        s = scan_linenum (s, &p_newfirst);
        if (*s == ',')
          {
            do
              {
            if (!*++s)
              malformed ();
              }
            while (! ISDIGIT (*s));
            scan_linenum (s, &p_repl_lines);
            p_repl_lines += 1 - p_newfirst;
          }
        else if (p_newfirst)
          p_repl_lines = 1;
        else
          {
            p_repl_lines = 0;
            p_newfirst = 1;
          }
        p_max = p_repl_lines + p_end;
        while (p_max >= hunkmax)
          if (! grow_hunkmax ())
            return -1;
        if (p_repl_lines != ptrn_copiable
            && (p_prefix_context != 0
            || context != 0
            || p_repl_lines != 1))
          repl_could_be_missing = false;
        context = 0;
        break;
        case '+':  case '!':
        repl_could_be_missing = false;
          change_line:
        s = buf + 1;
        chars_read--;
        if (*s == '\n' && canonicalize) {
            strcpy (s, " \n");
            chars_read = 2;
        }
        if (*s == ' ' || *s == '\t') {
            s++;
            chars_read--;
        } else if (repl_beginning && repl_could_be_missing) {
            repl_missing = true;
            goto hunk_done;
        }
        if (! repl_beginning)
          {
            if (ptrn_prefix_context == -1)
              ptrn_prefix_context = context;
          }
        else
          {
            if (repl_prefix_context == -1)
              repl_prefix_context = context;
          }
        chars_read -=
          (1 < chars_read
           && p_end == (repl_beginning ? p_max : p_ptrn_lines)
           && incomplete_line ());
        p_len[p_end] = chars_read;
        if (! (p_line[p_end] = savebuf (s, chars_read))) {
            p_end--;
            return -1;
        }
        context = 0;
        break;
        case '\t': case '\n':   /* assume spaces got eaten */
        s = buf;
        if (*buf == '\t') {
            s++;
            chars_read--;
        }
        if (repl_beginning && repl_could_be_missing &&
            (!ptrn_spaces_eaten || difftype == NEW_CONTEXT_DIFF) ) {
            repl_missing = true;
            goto hunk_done;
        }
        chars_read -=
          (1 < chars_read
           && p_end == (repl_beginning ? p_max : p_ptrn_lines)
           && incomplete_line ());
        p_len[p_end] = chars_read;
        if (! (p_line[p_end] = savebuf (buf, chars_read))) {
            p_end--;
            return -1;
        }
        if (p_end != p_ptrn_lines + 1) {
            ptrn_spaces_eaten |= (repl_beginning != 0);
            some_context = true;
            context++;
            if (repl_beginning)
            repl_copiable++;
            else
            ptrn_copiable++;
            p_Char[p_end] = ' ';
        }
        break;
        case ' ':
        s = buf + 1;
        chars_read--;
        if (*s == '\n' && canonicalize) {
            strcpy (s, "\n");
            chars_read = 2;
        }
        if (*s == ' ' || *s == '\t') {
            s++;
            chars_read--;
        } else if (repl_beginning && repl_could_be_missing) {
            repl_missing = true;
            goto hunk_done;
        }
        some_context = true;
        context++;
        if (repl_beginning)
            repl_copiable++;
        else
            ptrn_copiable++;
        chars_read -=
          (1 < chars_read
           && p_end == (repl_beginning ? p_max : p_ptrn_lines)
           && incomplete_line ());
        p_len[p_end] = chars_read;
        if (! (p_line[p_end] = savebuf (buf + 2, chars_read))) {
            p_end--;
            return -1;
        }
        break;
        default:
        if (repl_beginning && repl_could_be_missing) {
            repl_missing = true;
            goto hunk_done;
        }
        malformed ();
        }
    }

    hunk_done:
    if (p_end >=0 && !repl_beginning)
      fatal ("no `---' found in patch at line %s",
         format_linenum (numbuf0, pch_hunk_beg ()));

    if (repl_missing) {

        /* reset state back to just after --- */
        p_input_line = repl_patch_line;
        context = repl_context;
        for (p_end--; p_end > repl_beginning; p_end--)
        free(p_line[p_end]);
        Fseek (pfp, repl_backtrack_position, SEEK_SET);

        /* redundant 'new' context lines were omitted - set */
        /* up to fill them in from the old file context */
        fillsrc = 1;
        filldst = repl_beginning+1;
        fillcnt = p_repl_lines;
        p_end = p_max;
    }
    else if (! ptrn_missing && ptrn_copiable != repl_copiable)
      fatal ("context mangled in hunk at line %s",
         format_linenum (numbuf0, p_hunk_beg));
    else if (!some_context && fillcnt == 1) {
        /* the first hunk was a null hunk with no context */
        /* and we were expecting one line -- fix it up. */
        while (filldst < p_end) {
        p_line[filldst] = p_line[filldst+1];
        p_Char[filldst] = p_Char[filldst+1];
        p_len[filldst] = p_len[filldst+1];
        filldst++;
        }
#if 0
        repl_beginning--;       /* this doesn't need to be fixed */
#endif
        p_end--;
        p_first++;          /* do append rather than insert */
        fillcnt = 0;
        p_ptrn_lines = 0;
    }

    p_prefix_context = ((repl_prefix_context == -1
                 || (ptrn_prefix_context != -1
                 && ptrn_prefix_context < repl_prefix_context))
                ? ptrn_prefix_context : repl_prefix_context);
    p_suffix_context = ((ptrn_suffix_context != -1
                 && ptrn_suffix_context < context)
                ? ptrn_suffix_context : context);
    assert (p_prefix_context != -1 && p_suffix_context != -1);

    if (difftype == CONTEXT_DIFF
        && (fillcnt
        || (p_first > 1
            && p_prefix_context + p_suffix_context < ptrn_copiable))) {
        if (verbosity == VERBOSE)
        say ("%s\n%s\n%s\n",
"(Fascinating -- this is really a new-style context diff but without",
"the telltale extra asterisks on the *** line that usually indicate",
"the new style...)");
        diff_type = difftype = NEW_CONTEXT_DIFF;
    }

    /* if there were omitted context lines, fill them in now */
    if (fillcnt) {
        p_bfake = filldst;      /* remember where not to free() */
        p_efake = filldst + fillcnt - 1;
        while (fillcnt-- > 0) {
        while (fillsrc <= p_end && fillsrc != repl_beginning
               && p_Char[fillsrc] != ' ')
            fillsrc++;
        if (p_end < fillsrc || fillsrc == repl_beginning)
          {
            fatal ("replacement text or line numbers mangled in hunk at line %s",
               format_linenum (numbuf0, p_hunk_beg));
          }
        p_line[filldst] = p_line[fillsrc];
        p_Char[filldst] = p_Char[fillsrc];
        p_len[filldst] = p_len[fillsrc];
        fillsrc++; filldst++;
        }
        while (fillsrc <= p_end && fillsrc != repl_beginning)
          {
        if (p_Char[fillsrc] == ' ')
          fatal ("replacement text or line numbers mangled in hunk at line %s",
             format_linenum (numbuf0, p_hunk_beg));
        fillsrc++;
          }
        if (debug & 64)
          printf ("fillsrc %s, filldst %s, rb %s, e+1 %s\n",
              format_linenum (numbuf0, fillsrc),
              format_linenum (numbuf1, filldst),
              format_linenum (numbuf2, repl_beginning),
              format_linenum (numbuf3, p_end + 1));
        assert(fillsrc==p_end+1 || fillsrc==repl_beginning);
        assert(filldst==p_end+1 || filldst==repl_beginning);
    }
    }
    else if (difftype == UNI_DIFF) {
    file_offset line_beginning = file_tell (pfp);
                    /* file pos of the current line */
    register LINENUM fillsrc;   /* index of old lines */
    register LINENUM filldst;   /* index of new lines */
    char ch = '\0';

    chars_read = get_line ();
    if (chars_read == (size_t) -1
        || chars_read <= 4
        || strncmp (buf, "@@ -", 4) != 0) {
        next_intuit_at(line_beginning,p_input_line);
        return chars_read == (size_t) -1 ? -1 : 0;
    }
    s = scan_linenum (buf + 4, &p_first);
    if (*s == ',')
        s = scan_linenum (s + 1, &p_ptrn_lines);
    else
        p_ptrn_lines = 1;
    if (*s == ' ') s++;
    if (*s != '+')
        malformed ();
    s = scan_linenum (s + 1, &p_newfirst);
    if (*s == ',')
        s = scan_linenum (s + 1, &p_repl_lines);
    else
        p_repl_lines = 1;
    if (*s == ' ') s++;
    if (*s != '@')
        malformed ();
    if (!p_ptrn_lines)
        p_first++;          /* do append rather than insert */
    if (!p_repl_lines)
        p_newfirst++;
    p_max = p_ptrn_lines + p_repl_lines + 1;
    while (p_max >= hunkmax)
        if (! grow_hunkmax ())
        return -1;
    fillsrc = 1;
    filldst = fillsrc + p_ptrn_lines;
    p_end = filldst + p_repl_lines;
    sprintf (buf, "*** %s,%s ****\n",
         format_linenum (numbuf0, p_first),
         format_linenum (numbuf1, p_first + p_ptrn_lines - 1));
    p_len[0] = strlen (buf);
    if (! (p_line[0] = savestr (buf))) {
        p_end = -1;
        return -1;
    }
    p_Char[0] = '*';
    sprintf (buf, "--- %s,%s ----\n",
         format_linenum (numbuf0, p_newfirst),
         format_linenum (numbuf1, p_newfirst + p_repl_lines - 1));
    p_len[filldst] = strlen (buf);
    if (! (p_line[filldst] = savestr (buf))) {
        p_end = 0;
        return -1;
    }
    p_Char[filldst++] = '=';
    p_prefix_context = -1;
    p_hunk_beg = p_input_line + 1;
    while (fillsrc <= p_ptrn_lines || filldst <= p_end) {
        chars_read = get_line ();
        if (!chars_read) {
        if (p_max - filldst < 3) {
            strcpy (buf, " \n");  /* assume blank lines got chopped */
            chars_read = 2;
        } else {
            fatal ("unexpected end of file in patch");
        }
        }
        if (chars_read == (size_t) -1)
        s = 0;
        else if (*buf == '\t' || *buf == '\n') {
        ch = ' ';       /* assume the space got eaten */
        s = savebuf (buf, chars_read);
        }
        else {
        ch = *buf;
        s = savebuf (buf+1, --chars_read);
        }
        if (!s) {
        while (--filldst > p_ptrn_lines)
            free(p_line[filldst]);
        p_end = fillsrc-1;
        return -1;
        }
        switch (ch) {
        case '-':
        if (fillsrc > p_ptrn_lines) {
            free(s);
            p_end = filldst-1;
            malformed ();
        }
        chars_read -= fillsrc == p_ptrn_lines && incomplete_line ();
        p_Char[fillsrc] = ch;
        p_line[fillsrc] = s;
        p_len[fillsrc++] = chars_read;
        break;
        case '=':
        ch = ' ';
        /* FALL THROUGH */
        case ' ':
        if (fillsrc > p_ptrn_lines) {
            free(s);
            while (--filldst > p_ptrn_lines)
            free(p_line[filldst]);
            p_end = fillsrc-1;
            malformed ();
        }
        context++;
        chars_read -= fillsrc == p_ptrn_lines && incomplete_line ();
        p_Char[fillsrc] = ch;
        p_line[fillsrc] = s;
        p_len[fillsrc++] = chars_read;
        s = savebuf (s, chars_read);
        if (!s) {
            while (--filldst > p_ptrn_lines)
            free(p_line[filldst]);
            p_end = fillsrc-1;
            return -1;
        }
        /* FALL THROUGH */
        case '+':
        if (filldst > p_end) {
            free(s);
            while (--filldst > p_ptrn_lines)
            free(p_line[filldst]);
            p_end = fillsrc-1;
            malformed ();
        }
        chars_read -= filldst == p_end && incomplete_line ();
        p_Char[filldst] = ch;
        p_line[filldst] = s;
        p_len[filldst++] = chars_read;
        break;
        default:
        p_end = filldst;
        malformed ();
        }
        if (ch != ' ') {
        if (p_prefix_context == -1)
            p_prefix_context = context;
        context = 0;
        }
    }/* while */
    if (p_prefix_context == -1)
      malformed ();
    p_suffix_context = context;
    }
    else {              /* normal diff--fake it up */
    char hunk_type;
    register int i;
    LINENUM min, max;
    file_offset line_beginning = file_tell (pfp);

    p_prefix_context = p_suffix_context = 0;
    chars_read = get_line ();
    if (chars_read == (size_t) -1 || !chars_read || !ISDIGIT (*buf)) {
        next_intuit_at(line_beginning,p_input_line);
        return chars_read == (size_t) -1 ? -1 : 0;
    }
    s = scan_linenum (buf, &p_first);
    if (*s == ',') {
        s = scan_linenum (s + 1, &p_ptrn_lines);
        p_ptrn_lines += 1 - p_first;
    }
    else
        p_ptrn_lines = (*s != 'a');
    hunk_type = *s;
    if (hunk_type == 'a')
        p_first++;          /* do append rather than insert */
    s = scan_linenum (s + 1, &min);
    if (*s == ',')
        scan_linenum (s + 1, &max);
    else
        max = min;
    if (hunk_type == 'd')
        min++;
    p_end = p_ptrn_lines + 1 + max - min + 1;
    while (p_end >= hunkmax)
      if (! grow_hunkmax ())
        {
          p_end = -1;
          return -1;
        }
    p_newfirst = min;
    p_repl_lines = max - min + 1;
    sprintf (buf, "*** %s,%s\n",
         format_linenum (numbuf0, p_first),
         format_linenum (numbuf1, p_first + p_ptrn_lines - 1));
    p_len[0] = strlen (buf);
    if (! (p_line[0] = savestr (buf))) {
        p_end = -1;
        return -1;
    }
    p_Char[0] = '*';
    for (i=1; i<=p_ptrn_lines; i++) {
        chars_read = get_line ();
        if (chars_read == (size_t) -1)
          {
        p_end = i - 1;
        return -1;
          }
        if (!chars_read)
          fatal ("unexpected end of file in patch at line %s",
             format_linenum (numbuf0, p_input_line));
        if (buf[0] != '<' || (buf[1] != ' ' && buf[1] != '\t'))
          fatal ("`<' expected at line %s of patch",
             format_linenum (numbuf0, p_input_line));
        chars_read -= 2 + (i == p_ptrn_lines && incomplete_line ());
        p_len[i] = chars_read;
        if (! (p_line[i] = savebuf (buf + 2, chars_read))) {
        p_end = i-1;
        return -1;
        }
        p_Char[i] = '-';
    }
    if (hunk_type == 'c') {
        chars_read = get_line ();
        if (chars_read == (size_t) -1)
          {
        p_end = i - 1;
        return -1;
          }
        if (! chars_read)
          fatal ("unexpected end of file in patch at line %s",
             format_linenum (numbuf0, p_input_line));
        if (*buf != '-')
          fatal ("`---' expected at line %s of patch",
             format_linenum (numbuf0, p_input_line));
    }
    sprintf (buf, "--- %s,%s\n",
         format_linenum (numbuf0, min),
         format_linenum (numbuf1, max));
    p_len[i] = strlen (buf);
    if (! (p_line[i] = savestr (buf))) {
        p_end = i-1;
        return -1;
    }
    p_Char[i] = '=';
    for (i++; i<=p_end; i++) {
        chars_read = get_line ();
        if (chars_read == (size_t) -1)
          {
        p_end = i - 1;
        return -1;
          }
        if (!chars_read)
          fatal ("unexpected end of file in patch at line %s",
             format_linenum (numbuf0, p_input_line));
        if (buf[0] != '>' || (buf[1] != ' ' && buf[1] != '\t'))
          fatal ("`>' expected at line %s of patch",
             format_linenum (numbuf0, p_input_line));
        chars_read -= 2 + (i == p_end && incomplete_line ());
        p_len[i] = chars_read;
        if (! (p_line[i] = savebuf (buf + 2, chars_read))) {
        p_end = i-1;
        return -1;
        }
        p_Char[i] = '+';
    }
    }
    if (rev)                /* backwards patch? */
    if (!pch_swap())
        say ("Not enough memory to swap next hunk!\n");
    if (debug & 2) {
    LINENUM i;
    char special;

    for (i=0; i <= p_end; i++) {
        if (i == p_ptrn_lines)
        special = '^';
        else
        special = ' ';
        fprintf (stderr, "%s %c %c ", format_linenum (numbuf0, i),
             p_Char[i], special);
        pch_write_line (i, stderr);
        fflush (stderr);
    }
    }
    if (p_end+1 < hunkmax)  /* paranoia reigns supreme... */
    p_Char[p_end+1] = '^';  /* add a stopper for apply_hunk */
    return 1;
}

static size_t
get_line (void)
{
   return pget_line (p_indent, p_rfc934_nesting, p_strip_trailing_cr,
             p_pass_comments_through);
}

/* Input a line from the patch file, worrying about indentation.
   Strip up to INDENT characters' worth of leading indentation.
   Then remove up to RFC934_NESTING instances of leading "- ".
   If STRIP_TRAILING_CR is true, remove any trailing carriage-return.
   Unless PASS_COMMENTS_THROUGH is true, ignore any resulting lines
   that begin with '#'; they're comments.
   Ignore any partial lines at end of input, but warn about them.
   Succeed if a line was read; it is terminated by "\n\0" for convenience.
   Return the number of characters read, including '\n' but not '\0'.
   Return -1 if we ran out of memory.  */

static size_t
pget_line (int indent, int rfc934_nesting, bool strip_trailing_cr,
       bool pass_comments_through)
{
  register FILE *fp = pfp;
  register int c;
  register int i;
  register char *b;
  register size_t s;

  do
    {
      i = 0;
      for (;;)
    {
      c = getc (fp);
      if (c == EOF)
        {
          if (ferror (fp))
        read_fatal ();
          return 0;
        }
      if (indent <= i)
        break;
      if (c == ' ' || c == 'X')
        i++;
      else if (c == '\t')
        i = (i + 8) & ~7;
      else
        break;
    }

      i = 0;
      b = buf;

      while (c == '-' && 0 <= --rfc934_nesting)
    {
      c = getc (fp);
      if (c == EOF)
        goto patch_ends_in_middle_of_line;
      if (c != ' ')
        {
          i = 1;
          b[0] = '-';
          break;
        }
      c = getc (fp);
      if (c == EOF)
        goto patch_ends_in_middle_of_line;
    }

      s = bufsize;

      for (;;)
    {
      if (i == s - 1)
        {
          s *= 2;
          b = realloc (b, s);
          if (!b)
        {
          if (!using_plan_a)
            memory_fatal ();
          return (size_t) -1;
        }
          buf = b;
          bufsize = s;
        }
      b[i++] = c;
      if (c == '\n')
        break;
      c = getc (fp);
      if (c == EOF)
        goto patch_ends_in_middle_of_line;
    }

      p_input_line++;
    }
  while (*b == '#' && !pass_comments_through);

  if (strip_trailing_cr && 2 <= i && b[i - 2] == '\r')
    b[i-- - 2] = '\n';
  b[i] = '\0';
  return i;

 patch_ends_in_middle_of_line:
  if (ferror (fp))
    read_fatal ();
  say ("patch unexpectedly ends in middle of line\n");
  return 0;
}

static bool
incomplete_line (void)
{
  register FILE *fp = pfp;
  register int c;
  register file_offset line_beginning = file_tell (fp);

  if (getc (fp) == '\\')
    {
      while ((c = getc (fp)) != '\n'  &&  c != EOF)
    continue;
      return true;
    }
  else
    {
      /* We don't trust ungetc.  */
      Fseek (pfp, line_beginning, SEEK_SET);
      return false;
    }
}

/* Reverse the old and new portions of the current hunk. */

bool
pch_swap (void)
{
    char **tp_line;     /* the text of the hunk */
    size_t *tp_len;     /* length of each line */
    char *tp_char;      /* +, -, and ! */
    register LINENUM i;
    register LINENUM n;
    bool blankline = false;
    register char *s;

    i = p_first;
    p_first = p_newfirst;
    p_newfirst = i;

    /* make a scratch copy */

    tp_line = p_line;
    tp_len = p_len;
    tp_char = p_Char;
    p_line = 0; /* force set_hunkmax to allocate again */
    p_len = 0;
    p_Char = 0;
    set_hunkmax();
    if (!p_line || !p_len || !p_Char) {
    if (p_line)
      free (p_line);
    p_line = tp_line;
    if (p_len)
      free (p_len);
    p_len = tp_len;
    if (p_Char)
      free (p_Char);
    p_Char = tp_char;
    return false;       /* not enough memory to swap hunk! */
    }

    /* now turn the new into the old */

    i = p_ptrn_lines + 1;
    if (tp_char[i] == '\n') {       /* account for possible blank line */
    blankline = true;
    i++;
    }
    if (p_efake >= 0) {         /* fix non-freeable ptr range */
    if (p_efake <= i)
        n = p_end - i + 1;
    else
        n = -i;
    p_efake += n;
    p_bfake += n;
    }
    for (n=0; i <= p_end; i++,n++) {
    p_line[n] = tp_line[i];
    p_Char[n] = tp_char[i];
    if (p_Char[n] == '+')
        p_Char[n] = '-';
    p_len[n] = tp_len[i];
    }
    if (blankline) {
    i = p_ptrn_lines + 1;
    p_line[n] = tp_line[i];
    p_Char[n] = tp_char[i];
    p_len[n] = tp_len[i];
    n++;
    }
    assert(p_Char[0] == '=');
    p_Char[0] = '*';
    for (s=p_line[0]; *s; s++)
    if (*s == '-')
        *s = '*';

    /* now turn the old into the new */

    assert(tp_char[0] == '*');
    tp_char[0] = '=';
    for (s=tp_line[0]; *s; s++)
    if (*s == '*')
        *s = '-';
    for (i=0; n <= p_end; i++,n++) {
    p_line[n] = tp_line[i];
    p_Char[n] = tp_char[i];
    if (p_Char[n] == '-')
        p_Char[n] = '+';
    p_len[n] = tp_len[i];
    }
    assert(i == p_ptrn_lines + 1);
    i = p_ptrn_lines;
    p_ptrn_lines = p_repl_lines;
    p_repl_lines = i;
    if (tp_line)
      free (tp_line);
    if (tp_len)
      free (tp_len);
    if (tp_char)
      free (tp_char);
    return true;
}

/* Return whether file WHICH (false = old, true = new) appears to nonexistent.
   Return 1 for empty, 2 for nonexistent.  */

int
pch_says_nonexistent (bool which)
{
  return p_says_nonexistent[which];
}

/* Return timestamp of patch header for file WHICH (false = old, true = new),
   or -1 if there was no timestamp or an error in the timestamp.  */

time_t
pch_timestamp (bool which)
{
  return p_timestamp[which];
}

/* Return the specified line position in the old file of the old context. */

LINENUM
pch_first (void)
{
    return p_first;
}

/* Return the number of lines of old context. */

LINENUM
pch_ptrn_lines (void)
{
    return p_ptrn_lines;
}

/* Return the probable line position in the new file of the first line. */

LINENUM
pch_newfirst (void)
{
    return p_newfirst;
}

/* Return the number of lines in the replacement text including context. */

LINENUM
pch_repl_lines (void)
{
    return p_repl_lines;
}

/* Return the number of lines in the whole hunk. */

LINENUM
pch_end (void)
{
    return p_end;
}

/* Return the number of context lines before the first changed line. */

LINENUM
pch_prefix_context (void)
{
    return p_prefix_context;
}

/* Return the number of context lines after the last changed line. */

LINENUM
pch_suffix_context (void)
{
    return p_suffix_context;
}

/* Return the length of a particular patch line. */

size_t
pch_line_len (LINENUM line)
{
    return p_len[line];
}

/* Return the control character (+, -, *, !, etc) for a patch line. */

char
pch_char (LINENUM line)
{
    return p_Char[line];
}

/* Return a pointer to a particular patch line. */

char *
pfetch (LINENUM line)
{
    return p_line[line];
}

/* Output a patch line.  */

bool
pch_write_line (LINENUM line, FILE *file)
{
  bool after_newline = p_line[line][p_len[line] - 1] == '\n';
  if (! fwrite (p_line[line], sizeof (*p_line[line]), p_len[line], file))
    write_fatal ();
  return after_newline;
}

/* Return where in the patch file this hunk began, for error messages. */

LINENUM
pch_hunk_beg (void)
{
    return p_hunk_beg;
}

/* Is the newline-terminated line a valid `ed' command for patch
   input?  If so, return the command character; if not, return 0.
   This accepts accepts just a subset of the valid commands, but it's
   good enough in practice.  */

static char
get_ed_command_letter (char const *line)
{
  char const *p = line;
  char letter;
  bool pair = false;
  if (! ISDIGIT (*p))
    return 0;
  while (ISDIGIT (*++p))
    continue;
  if (*p == ',')
    {
      if (! ISDIGIT (*++p))
    return 0;
      while (ISDIGIT (*++p))
    continue;
      pair = true;
    }
  letter = *p++;

  switch (letter)
    {
    case 'a':
    case 'i':
      if (pair)
    return 0;
      break;

    case 'c':
    case 'd':
      break;

    case 's':
      if (strncmp (p, "/.//", 4) != 0)
    return 0;
      p += 4;
      break;

    default:
      return 0;
    }

  while (*p == ' ' || *p == '\t')
    p++;
  if (*p == '\n')
    return letter;
  return 0;
}

/* Apply an ed script by feeding ed itself. */

void
do_ed_script (FILE *ofp)
{
    static char const ed_program[] = ed_PROGRAM;

    register file_offset beginning_of_this_line;
    register FILE *pipefp = 0;
    register size_t chars_read;

    if (! dry_run && ! skip_rest_of_patch) {
    int exclusive = TMPOUTNAME_needs_removal ? 0 : O_EXCL;
    assert (! inerrno);
    TMPOUTNAME_needs_removal = 1;
    copy_file (inname, TMPOUTNAME, exclusive, instat.st_mode);
    sprintf (buf, "%s %s%s", ed_program, verbosity == VERBOSE ? "" : "- ",
         TMPOUTNAME);
    fflush (stdout);
    pipefp = popen(buf, binary_transput ? "wb" : "w");
    if (!pipefp)
      pfatal ("Can't open pipe to %s", quotearg (buf));
    }
    for (;;) {
    char ed_command_letter;
    beginning_of_this_line = file_tell (pfp);
    chars_read = get_line ();
    if (! chars_read) {
        next_intuit_at(beginning_of_this_line,p_input_line);
        break;
    }
    ed_command_letter = get_ed_command_letter (buf);
    if (ed_command_letter) {
        if (pipefp)
        if (! fwrite (buf, sizeof *buf, chars_read, pipefp))
            write_fatal ();
        if (ed_command_letter != 'd' && ed_command_letter != 's') {
            p_pass_comments_through = true;
        while ((chars_read = get_line ()) != 0) {
            if (pipefp)
            if (! fwrite (buf, sizeof *buf, chars_read, pipefp))
                write_fatal ();
            if (chars_read == 2  &&  strEQ (buf, ".\n"))
            break;
        }
        p_pass_comments_through = false;
        }
    }
    else {
        next_intuit_at(beginning_of_this_line,p_input_line);
        break;
    }
    }
    if (!pipefp)
      return;
    if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, pipefp) == 0
    || fflush (pipefp) != 0)
      write_fatal ();
    if (pclose (pipefp) != 0)
      fatal ("%s FAILED", ed_program);

    if (ofp)
      {
    FILE *ifp = fopen (TMPOUTNAME, binary_transput ? "rb" : "r");
    int c;
    if (!ifp)
      pfatal ("can't open `%s'", TMPOUTNAME);
    while ((c = getc (ifp)) != EOF)
      if (putc (c, ofp) == EOF)
        write_fatal ();
    if (ferror (ifp) || fclose (ifp) != 0)
      read_fatal ();
      }
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/dirname.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/dirname.h used in: Step 570, Step 574
/*  Copyright (C) 1998, 2001 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.  */

#ifndef DIRNAME_H_
# define DIRNAME_H_ 1

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

# ifndef DIRECTORY_SEPARATOR
#  define DIRECTORY_SEPARATOR '/'
# endif

# ifndef ISSLASH
#  define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR)
# endif

# ifndef FILESYSTEM_PREFIX_LEN
#  define FILESYSTEM_PREFIX_LEN(Filename) 0
# endif

char *base_name PARAMS ((char const *path));
char *dir_name PARAMS ((char const *path));
size_t base_len PARAMS ((char const *path));
size_t dir_len PARAMS ((char const *path));

int strip_trailing_slashes PARAMS ((char *path));

#endif /* not DIRNAME_H_ */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/quote.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/quote.c used in: Step 571
/* quote.c - quote arguments for output
   Copyright (C) 1998, 1999, 2000, 2001, 2003 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.  */

/* Written by Paul Eggert <eggert@twinsun.com> */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_STDDEF_H
# include <stddef.h>  /* For the definition of size_t on windows w/MSVC.  */
#endif
#include <sys/types.h>
#include "quotearg.h"
#include "quote.h"

/* Return an unambiguous printable representation of NAME,
   allocated in slot N, suitable for diagnostics.  */
char const *
quote_n (int n, char const *name)
{
  return quotearg_n_style (n, locale_quoting_style, name);
}

/* Return an unambiguous printable representation of NAME,
   suitable for diagnostics.  */
char const *
quote (char const *name)
{
  return quote_n (0, name);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/quotearg.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/quotearg.c used in: Step 572
/* quotearg.c - quote arguments for output
   Copyright (C) 1998, 1999, 2000, 2001, 2002 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.  */

/* Written by Paul Eggert <eggert@twinsun.com> */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include "quotearg.h"

#include "xalloc.h"

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>

#include "gettext.h"
#define _(msgid) gettext (msgid)
#define N_(msgid) msgid

#if HAVE_WCHAR_H

/* BSD/OS 4.1 wchar.h requires FILE and struct tm to be declared.  */
# include <stdio.h>
# include <time.h>

# include <wchar.h>
#endif

#if !HAVE_MBRTOWC
/* Disable multibyte processing entirely.  Since MB_CUR_MAX is 1, the
   other macros are defined only for documentation and to satisfy C
   syntax.  */
# undef MB_CUR_MAX
# define MB_CUR_MAX 1
# define mbrtowc(pwc, s, n, ps) ((*(pwc) = *(s)) != 0)
# define iswprint(wc) isprint ((unsigned char) (wc))
# undef HAVE_MBSINIT
#endif

#if !defined mbsinit && !HAVE_MBSINIT
# define mbsinit(ps) 1
#endif

#ifndef iswprint
# if HAVE_WCTYPE_H
#  include <wctype.h>
# endif
# if !defined iswprint && !HAVE_ISWPRINT
#  define iswprint(wc) 1
# endif
#endif

#ifndef SIZE_MAX
# define SIZE_MAX ((size_t) -1)
#endif

#define INT_BITS (sizeof (int) * CHAR_BIT)

struct quoting_options
{
  /* Basic quoting style.  */
  enum quoting_style style;

  /* Quote the characters indicated by this bit vector even if the
     quoting style would not normally require them to be quoted.  */
  int quote_these_too[(UCHAR_MAX / INT_BITS) + 1];
};

/* Names of quoting styles.  */
char const *const quoting_style_args[] =
{
  "literal",
  "shell",
  "shell-always",
  "c",
  "escape",
  "locale",
  "clocale",
  0
};

/* Correspondences to quoting style names.  */
enum quoting_style const quoting_style_vals[] =
{
  literal_quoting_style,
  shell_quoting_style,
  shell_always_quoting_style,
  c_quoting_style,
  escape_quoting_style,
  locale_quoting_style,
  clocale_quoting_style
};

/* The default quoting options.  */
static struct quoting_options default_quoting_options;

/* Allocate a new set of quoting options, with contents initially identical
   to O if O is not null, or to the default if O is null.
   It is the caller's responsibility to free the result.  */
struct quoting_options *
clone_quoting_options (struct quoting_options *o)
{
  int e = errno;
  struct quoting_options *p = xmalloc (sizeof *p);
  *p = *(o ? o : &default_quoting_options);
  errno = e;
  return p;
}

/* Get the value of O's quoting style.  If O is null, use the default.  */
enum quoting_style
get_quoting_style (struct quoting_options *o)
{
  return (o ? o : &default_quoting_options)->style;
}

/* In O (or in the default if O is null),
   set the value of the quoting style to S.  */
void
set_quoting_style (struct quoting_options *o, enum quoting_style s)
{
  (o ? o : &default_quoting_options)->style = s;
}

/* In O (or in the default if O is null),
   set the value of the quoting options for character C to I.
   Return the old value.  Currently, the only values defined for I are
   0 (the default) and 1 (which means to quote the character even if
   it would not otherwise be quoted).  */
int
set_char_quoting (struct quoting_options *o, char c, int i)
{
  unsigned char uc = c;
  int *p = (o ? o : &default_quoting_options)->quote_these_too + uc / INT_BITS;
  int shift = uc % INT_BITS;
  int r = (*p >> shift) & 1;
  *p ^= ((i & 1) ^ r) << shift;
  return r;
}

/* MSGID approximates a quotation mark.  Return its translation if it
   has one; otherwise, return either it or "\"", depending on S.  */
static char const *
gettext_quote (char const *msgid, enum quoting_style s)
{
  char const *translation = _(msgid);
  if (translation == msgid && s == clocale_quoting_style)
    translation = "\"";
  return translation;
}

/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
   argument ARG (of size ARGSIZE), using QUOTING_STYLE and the
   non-quoting-style part of O to control quoting.
   Terminate the output with a null character, and return the written
   size of the output, not counting the terminating null.
   If BUFFERSIZE is too small to store the output string, return the
   value that would have been returned had BUFFERSIZE been large enough.
   If ARGSIZE is -1, use the string length of the argument for ARGSIZE.

   This function acts like quotearg_buffer (BUFFER, BUFFERSIZE, ARG,
   ARGSIZE, O), except it uses QUOTING_STYLE instead of the quoting
   style specified by O, and O may not be null.  */

static size_t
quotearg_buffer_restyled (char *buffer, size_t buffersize,
              char const *arg, size_t argsize,
              enum quoting_style quoting_style,
              struct quoting_options const *o)
{
  size_t i;
  size_t len = 0;
  char const *quote_string = 0;
  size_t quote_string_len = 0;
  int backslash_escapes = 0;
  int unibyte_locale = MB_CUR_MAX == 1;

#define STORE(c) \
    do \
      { \
    if (len < buffersize) \
      buffer[len] = (c); \
    len++; \
      } \
    while (0)

  switch (quoting_style)
    {
    case c_quoting_style:
      STORE ('"');
      backslash_escapes = 1;
      quote_string = "\"";
      quote_string_len = 1;
      break;

    case escape_quoting_style:
      backslash_escapes = 1;
      break;

    case locale_quoting_style:
    case clocale_quoting_style:
      {
    /* Get translations for open and closing quotation marks.

       The message catalog should translate "`" to a left
       quotation mark suitable for the locale, and similarly for
       "'".  If the catalog has no translation,
       locale_quoting_style quotes `like this', and
       clocale_quoting_style quotes "like this".

       For example, an American English Unicode locale should
       translate "`" to U+201C (LEFT DOUBLE QUOTATION MARK), and
       should translate "'" to U+201D (RIGHT DOUBLE QUOTATION
       MARK).  A British English Unicode locale should instead
       translate these to U+2018 (LEFT SINGLE QUOTATION MARK) and
       U+2019 (RIGHT SINGLE QUOTATION MARK), respectively.  */

    char const *left = gettext_quote (N_("`"), quoting_style);
    char const *right = gettext_quote (N_("'"), quoting_style);
    for (quote_string = left; *quote_string; quote_string++)
      STORE (*quote_string);
    backslash_escapes = 1;
    quote_string = right;
    quote_string_len = strlen (quote_string);
      }
      break;

    case shell_always_quoting_style:
      STORE ('\'');
      quote_string = "'";
      quote_string_len = 1;
      break;

    default:
      break;
    }

  for (i = 0;  ! (argsize == SIZE_MAX ? arg[i] == '\0' : i == argsize);  i++)
    {
      unsigned char c;
      unsigned char esc;

      if (backslash_escapes
      && quote_string_len
      && i + quote_string_len <= argsize
      && memcmp (arg + i, quote_string, quote_string_len) == 0)
    STORE ('\\');

      c = arg[i];
      switch (c)
    {
    case '\0':
      if (backslash_escapes)
        {
          STORE ('\\');
          STORE ('0');
          STORE ('0');
          c = '0';
        }
      break;

    case '?':
      switch (quoting_style)
        {
        case shell_quoting_style:
          goto use_shell_always_quoting_style;

        case c_quoting_style:
          if (i + 2 < argsize && arg[i + 1] == '?')
        switch (arg[i + 2])
          {
          case '!': case '\'':
          case '(': case ')': case '-': case '/':
          case '<': case '=': case '>':
            /* Escape the second '?' in what would otherwise be
               a trigraph.  */
            c = arg[i + 2];
            i += 2;
            STORE ('?');
            STORE ('\\');
            STORE ('?');
            break;
          }
          break;

        default:
          break;
        }
      break;

    case '\a': esc = 'a'; goto c_escape;
    case '\b': esc = 'b'; goto c_escape;
    case '\f': esc = 'f'; goto c_escape;
    case '\n': esc = 'n'; goto c_and_shell_escape;
    case '\r': esc = 'r'; goto c_and_shell_escape;
    case '\t': esc = 't'; goto c_and_shell_escape;
    case '\v': esc = 'v'; goto c_escape;
    case '\\': esc = c; goto c_and_shell_escape;

    c_and_shell_escape:
      if (quoting_style == shell_quoting_style)
        goto use_shell_always_quoting_style;
    c_escape:
      if (backslash_escapes)
        {
          c = esc;
          goto store_escape;
        }
      break;

    case '#': case '~':
      if (i != 0)
        break;
      /* Fall through.  */
    case ' ':
    case '!': /* special in bash */
    case '"': case '$': case '&':
    case '(': case ')': case '*': case ';':
    case '<': case '>': case '[':
    case '^': /* special in old /bin/sh, e.g. SunOS 4.1.4 */
    case '`': case '|':
      /* A shell special character.  In theory, '$' and '`' could
         be the first bytes of multibyte characters, which means
         we should check them with mbrtowc, but in practice this
         doesn't happen so it's not worth worrying about.  */
      if (quoting_style == shell_quoting_style)
        goto use_shell_always_quoting_style;
      break;

    case '\'':
      switch (quoting_style)
        {
        case shell_quoting_style:
          goto use_shell_always_quoting_style;

        case shell_always_quoting_style:
          STORE ('\'');
          STORE ('\\');
          STORE ('\'');
          break;

        default:
          break;
        }
      break;

    case '%': case '+': case ',': case '-': case '.': case '/':
    case '0': case '1': case '2': case '3': case '4': case '5':
    case '6': case '7': case '8': case '9': case ':': 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': case ']': 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':
    case '{': case '}':
      /* These characters don't cause problems, no matter what the
         quoting style is.  They cannot start multibyte sequences.  */
      break;

    default:
      /* If we have a multibyte sequence, copy it until we reach
         its end, find an error, or come back to the initial shift
         state.  For C-like styles, if the sequence has
         unprintable characters, escape the whole sequence, since
         we can't easily escape single characters within it.  */
      {
        /* Length of multibyte sequence found so far.  */
        size_t m;

        int printable;

        if (unibyte_locale)
          {
        m = 1;
        printable = isprint (c);
          }
        else
          {
        mbstate_t mbstate;
        memset (&mbstate, 0, sizeof mbstate);

        m = 0;
        printable = 1;
        if (argsize == SIZE_MAX)
          argsize = strlen (arg);

        do
          {
            wchar_t w;
            size_t bytes = mbrtowc (&w, &arg[i + m],
                        argsize - (i + m), &mbstate);
            if (bytes == 0)
              break;
            else if (bytes == (size_t) -1)
              {
            printable = 0;
            break;
              }
            else if (bytes == (size_t) -2)
              {
            printable = 0;
            while (i + m < argsize && arg[i + m])
              m++;
            break;
              }
            else
              {
            if (! iswprint (w))
              printable = 0;
            m += bytes;
              }
          }
        while (! mbsinit (&mbstate));
          }

        if (1 < m || (backslash_escapes && ! printable))
          {
        /* Output a multibyte sequence, or an escaped
           unprintable unibyte character.  */
        size_t ilim = i + m;

        for (;;)
          {
            if (backslash_escapes && ! printable)
              {
            STORE ('\\');
            STORE ('0' + (c >> 6));
            STORE ('0' + ((c >> 3) & 7));
            c = '0' + (c & 7);
              }
            if (ilim <= i + 1)
              break;
            STORE (c);
            c = arg[++i];
          }

        goto store_c;
          }
      }
    }

      if (! (backslash_escapes
         && o->quote_these_too[c / INT_BITS] & (1 << (c % INT_BITS))))
    goto store_c;

    store_escape:
      STORE ('\\');

    store_c:
      STORE (c);
    }

  if (quote_string)
    for (; *quote_string; quote_string++)
      STORE (*quote_string);

  if (len < buffersize)
    buffer[len] = '\0';
  return len;

 use_shell_always_quoting_style:
  return quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
                   shell_always_quoting_style, o);
}

/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
   argument ARG (of size ARGSIZE), using O to control quoting.
   If O is null, use the default.
   Terminate the output with a null character, and return the written
   size of the output, not counting the terminating null.
   If BUFFERSIZE is too small to store the output string, return the
   value that would have been returned had BUFFERSIZE been large enough.
   If ARGSIZE is -1, use the string length of the argument for ARGSIZE.  */
size_t
quotearg_buffer (char *buffer, size_t buffersize,
         char const *arg, size_t argsize,
         struct quoting_options const *o)
{
  struct quoting_options const *p = o ? o : &default_quoting_options;
  int e = errno;
  size_t r = quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
                       p->style, p);
  errno = e;
  return r;
}

/* Use storage slot N to return a quoted version of argument ARG.
   ARG is of size ARGSIZE, but if that is -1, ARG is a null-terminated string.
   OPTIONS specifies the quoting options.
   The returned value points to static storage that can be
   reused by the next call to this function with the same value of N.
   N must be nonnegative.  N is deliberately declared with type "int"
   to allow for future extensions (using negative values).  */
static char *
quotearg_n_options (int n, char const *arg, size_t argsize,
            struct quoting_options const *options)
{
  int e = errno;

  /* Preallocate a slot 0 buffer, so that the caller can always quote
     one small component of a "memory exhausted" message in slot 0.  */
  static char slot0[256];
  static unsigned int nslots = 1;
  unsigned int n0 = n;
  struct slotvec
    {
      size_t size;
      char *val;
    };
  static struct slotvec slotvec0 = {sizeof slot0, slot0};
  static struct slotvec *slotvec = &slotvec0;

  if (n < 0)
    abort ();

  if (nslots <= n0)
    {
      unsigned int n1 = n0 + 1;
      size_t s = n1 * sizeof *slotvec;

      if (SIZE_MAX / UINT_MAX <= sizeof *slotvec
      && n1 != s / sizeof *slotvec)
    xalloc_die ();

      if (slotvec == &slotvec0)
    {
      slotvec = xmalloc (sizeof *slotvec);
      *slotvec = slotvec0;
    }
      slotvec = xrealloc (slotvec, s);
      memset (slotvec + nslots, 0, (n1 - nslots) * sizeof *slotvec);
      nslots = n1;
    }

  {
    size_t size = slotvec[n].size;
    char *val = slotvec[n].val;
    size_t qsize = quotearg_buffer (val, size, arg, argsize, options);

    if (size <= qsize)
      {
    slotvec[n].size = size = qsize + 1;
    slotvec[n].val = val = xrealloc (val == slot0 ? 0 : val, size);
    quotearg_buffer (val, size, arg, argsize, options);
      }

    errno = e;
    return val;
  }
}

char *
quotearg_n (int n, char const *arg)
{
  return quotearg_n_options (n, arg, SIZE_MAX, &default_quoting_options);
}

char *
quotearg (char const *arg)
{
  return quotearg_n (0, arg);
}

/* Return quoting options for STYLE, with no extra quoting.  */
static struct quoting_options
quoting_options_from_style (enum quoting_style style)
{
  struct quoting_options o;
  o.style = style;
  memset (o.quote_these_too, 0, sizeof o.quote_these_too);
  return o;
}

char *
quotearg_n_style (int n, enum quoting_style s, char const *arg)
{
  struct quoting_options const o = quoting_options_from_style (s);
  return quotearg_n_options (n, arg, SIZE_MAX, &o);
}

char *
quotearg_n_style_mem (int n, enum quoting_style s,
              char const *arg, size_t argsize)
{
  struct quoting_options const o = quoting_options_from_style (s);
  return quotearg_n_options (n, arg, argsize, &o);
}

char *
quotearg_style (enum quoting_style s, char const *arg)
{
  return quotearg_n_style (0, s, arg);
}

char *
quotearg_char (char const *arg, char ch)
{
  struct quoting_options options;
  options = default_quoting_options;
  set_char_quoting (&options, ch, 1);
  return quotearg_n_options (0, arg, SIZE_MAX, &options);
}

char *
quotearg_colon (char const *arg)
{
  return quotearg_char (arg, ':');
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/quotesys.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/quotesys.c used in: Step 573
/* Shell command argument quoting.
   Copyright (C) 1994, 1995, 1997 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Written by Paul Eggert <eggert@twinsun.com> */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>
#include <quotesys.h>

/* Place into QUOTED a quoted version of ARG suitable for `system'.
   Return the length of the resulting string (which is not null-terminated).
   If QUOTED is null, return the length without any side effects.  */

size_t
quote_system_arg (quoted, arg)
     char *quoted;
     char const *arg;
{
  char const *a;
  size_t len = 0;

  /* Scan ARG, copying it to QUOTED if QUOTED is not null,
     looking for shell metacharacters.  */

  for (a = arg; ; a++)
    {
      char c = *a;
      switch (c)
    {
    case 0:
      /* ARG has no shell metacharacters.  */
      return len;

    case '=':
      if (*arg == '-')
        break;
      /* Fall through.  */
    case '\t': case '\n': case ' ':
    case '!': case '"': case '#': case '$': case '%': case '&': case '\'':
    case '(': case ')': case '*': case ';':
    case '<': case '>': case '?': case '[': case '\\':
    case '^': case '`': case '|': case '~':
      {
        /* ARG has a shell metacharacter.
           Start over, quoting it this time.  */

        len = 0;
        c = *arg++;

        /* If ARG is an option, quote just its argument.
           This is not necessary, but it looks nicer.  */
        if (c == '-'  &&  arg < a)
          {
        c = *arg++;

        if (quoted)
          {
            quoted[len] = '-';
            quoted[len + 1] = c;
          }
        len += 2;

        if (c == '-')
          while (arg < a)
            {
              c = *arg++;
              if (quoted)
            quoted[len] = c;
              len++;
              if (c == '=')
            break;
            }
        c = *arg++;
          }

        if (quoted)
          quoted[len] = '\'';
        len++;

        for (;  c;  c = *arg++)
          {
        if (c == '\'')
          {
            if (quoted)
              {
            quoted[len] = '\'';
            quoted[len + 1] = '\\';
            quoted[len + 2] = '\'';
              }
            len += 3;
          }
        if (quoted)
          quoted[len] = c;
        len++;
          }

        if (quoted)
          quoted[len] = '\'';
        return len + 1;
      }
    }

      if (quoted)
    quoted[len] = c;
      len++;
    }
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/util.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/util.c used in: Step 574
/* utility functions for `patch' */

/* $Id: util.c,v 1.36 2003/05/20 14:04:53 eggert Exp $ */

/* Copyright (C) 1986 Larry Wall

   Copyright (C) 1992, 1993, 1997, 1998, 1999, 2001, 2002, 2003 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#define XTERN extern
#include <common.h>
#include <backupfile.h>
#include <dirname.h>
#include <quotearg.h>
#include <quotesys.h>
#include <version.h>
#undef XTERN
#define XTERN
#include <util.h>
#include <xalloc.h>

#include <maketime.h>
#include <partime.h>

#include <signal.h>
#if !defined SIGCHLD && defined SIGCLD
#define SIGCHLD SIGCLD
#endif
#if ! HAVE_RAISE && ! defined raise
# define raise(sig) kill (getpid (), sig)
#endif

#include <stdarg.h>

static void makedirs (char *);

/* Move a file FROM (where *FROM_NEEDS_REMOVAL is nonzero if FROM
   needs removal when cleaning up at the end of execution)
   to TO, renaming it if possible and copying it if necessary.
   If we must create TO, use MODE to create it.
   If FROM is null, remove TO (ignoring FROMSTAT).
   FROM_NEEDS_REMOVAL must be nonnull if FROM is nonnull.
   Back up TO if BACKUP is true.  */

void
move_file (char const *from, int volatile *from_needs_removal,
       char *to, mode_t mode, bool backup)
{
  struct stat to_st;
  int to_errno = ! backup ? -1 : stat (to, &to_st) == 0 ? 0 : errno;

  if (backup)
    {
      int try_makedirs_errno = 0;
      char *bakname;

      if (origprae || origbase)
    {
      char const *p = origprae ? origprae : "";
      char const *b = origbase ? origbase : "";
      char const *o = base_name (to);
      size_t plen = strlen (p);
      size_t tlen = o - to;
      size_t blen = strlen (b);
      size_t osize = strlen (o) + 1;
      bakname = xmalloc (plen + tlen + blen + osize);
      memcpy (bakname, p, plen);
      memcpy (bakname + plen, to, tlen);
      memcpy (bakname + plen + tlen, b, blen);
      memcpy (bakname + plen + tlen + blen, o, osize);
      for (p += FILESYSTEM_PREFIX_LEN (p);  *p;  p++)
        if (ISSLASH (*p))
          {
        try_makedirs_errno = ENOENT;
        break;
          }
    }
      else
    {
      bakname = find_backup_file_name (to, backup_type);
      if (!bakname)
        memory_fatal ();
    }

      if (to_errno)
    {
      int fd;

      if (debug & 4)
        say ("Creating empty unreadable file %s\n", quotearg (bakname));

      try_makedirs_errno = ENOENT;
      unlink (bakname);
      while ((fd = creat (bakname, 0)) < 0)
        {
          if (errno != try_makedirs_errno)
        pfatal ("Can't create file %s", quotearg (bakname));
          makedirs (bakname);
          try_makedirs_errno = 0;
        }
      if (close (fd) != 0)
        pfatal ("Can't close file %s", quotearg (bakname));
    }
      else
    {
      if (debug & 4)
        say ("Renaming file %s to %s\n",
         quotearg_n (0, to), quotearg_n (1, bakname));
      while (rename (to, bakname) != 0)
        {
          if (errno != try_makedirs_errno)
        pfatal ("Can't rename file %s to %s",
            quotearg_n (0, to), quotearg_n (1, bakname));
          makedirs (bakname);
          try_makedirs_errno = 0;
        }
    }

      free (bakname);
    }

  if (from)
    {
      if (debug & 4)
    say ("Renaming file %s to %s\n",
         quotearg_n (0, from), quotearg_n (1, to));

      if (rename (from, to) != 0)
    {
      bool to_dir_known_to_exist = false;

      if (errno == ENOENT
          && (to_errno == -1 || to_errno == ENOENT))
        {
          makedirs (to);
          to_dir_known_to_exist = 1;
          if (rename (from, to) == 0)
        goto rename_succeeded;
        }

      if (errno == EXDEV)
        {
          if (! backup)
        {
          if (unlink (to) == 0)
            to_dir_known_to_exist = true;
          else if (errno != ENOENT)
            pfatal ("Can't remove file %s", quotearg (to));
        }
          if (! to_dir_known_to_exist)
        makedirs (to);
          copy_file (from, to, 0, mode);
          return;
        }

      pfatal ("Can't rename file %s to %s",
          quotearg_n (0, from), quotearg_n (1, to));
    }

    rename_succeeded:
      /* Do not clear *FROM_NEEDS_REMOVAL if it's possible that the
     rename returned zero because FROM and TO are hard links to
     the same file.  */
      if (0 < to_errno
      || (to_errno == 0 && to_st.st_nlink <= 1))
    *from_needs_removal = 0;
    }
  else if (! backup)
    {
      if (debug & 4)
    say ("Removing file %s\n", quotearg (to));
      if (unlink (to) != 0)
    pfatal ("Can't remove file %s", quotearg (to));
    }
}

/* Create FILE with OPEN_FLAGS, and with MODE adjusted so that
   we can read and write the file and that the file is not executable.
   Return the file descriptor.  */
int
create_file (char const *file, int open_flags, mode_t mode)
{
  int fd;
  mode |= S_IRUSR | S_IWUSR;
  mode &= ~ (S_IXUSR | S_IXGRP | S_IXOTH);
  if (! (O_CREAT && O_TRUNC))
    close (creat (file, mode));
  fd = open (file, O_CREAT | O_TRUNC | open_flags, mode);
  if (fd < 0)
    pfatal ("Can't create file %s", quotearg (file));
  return fd;
}

/* Copy a file. */

void
copy_file (char const *from, char const *to, int to_flags, mode_t mode)
{
  int tofd;
  int fromfd;
  size_t i;

  if ((fromfd = open (from, O_RDONLY | O_BINARY)) < 0)
    pfatal ("Can't reopen file %s", quotearg (from));
  tofd = create_file (to, O_WRONLY | O_BINARY | to_flags, mode);
  while ((i = read (fromfd, buf, bufsize)) != 0)
    {
      if (i == (size_t) -1)
    read_fatal ();
      if (write (tofd, buf, i) != i)
    write_fatal ();
    }
  if (close (fromfd) != 0)
    read_fatal ();
  if (close (tofd) != 0)
    write_fatal ();
}

static char const DEV_NULL[] = NULL_DEVICE;

static char const RCSSUFFIX[] = ",v";
static char const CHECKOUT[] = "co %s";
static char const CHECKOUT_LOCKED[] = "co -l %s";
static char const RCSDIFF1[] = "rcsdiff %s";

static char const SCCSPREFIX[] = "s.";
static char const GET[] = "get ";
static char const GET_LOCKED[] = "get -e ";
static char const SCCSDIFF1[] = "get -p ";
static char const SCCSDIFF2[] = "|diff - %s";

static char const CLEARTOOL_CO[] = "cleartool co -unr -nc ";

static char const PERFORCE_CO[] = "p4 edit ";

/* Return "RCS" if FILENAME is controlled by RCS,
   "SCCS" if it is controlled by SCCS,
   "ClearCase" if it is controlled by Clearcase,
   "Perforce" if it is controlled by Perforce,
   and 0 otherwise.
   READONLY is true if we desire only readonly access to FILENAME.
   FILESTAT describes FILENAME's status or is 0 if FILENAME does not exist.
   If successful and if GETBUF is nonzero, set *GETBUF to a command
   that gets the file; similarly for DIFFBUF and a command to diff the file
   (but set *DIFFBUF to 0 if the diff operation is meaningless).
   *GETBUF and *DIFFBUF must be freed by the caller.  */
char const *
version_controller (char const *filename, bool readonly,
            struct stat const *filestat, char **getbuf, char **diffbuf)
{
  struct stat cstat;
  char const *filebase = base_name (filename);
  char const *dotslash = *filename == '-' ? "./" : "";
  size_t dirlen = filebase - filename;
  size_t filenamelen = strlen (filename);
  size_t maxfixlen = sizeof "SCCS/" - 1 + sizeof SCCSPREFIX - 1;
  size_t maxtrysize = filenamelen + maxfixlen + 1;
  size_t quotelen = quote_system_arg (0, filename);
  size_t maxgetsize = sizeof CLEARTOOL_CO + quotelen + maxfixlen;
  size_t maxdiffsize =
    (sizeof SCCSDIFF1 + sizeof SCCSDIFF2 + sizeof DEV_NULL - 1
     + 2 * quotelen + maxfixlen);
  char *trybuf = xmalloc (maxtrysize);
  char const *r = 0;

  strcpy (trybuf, filename);

#define try1(f,a1)    (sprintf (trybuf + dirlen, f, a1),    stat (trybuf, &cstat) == 0)
#define try2(f,a1,a2) (sprintf (trybuf + dirlen, f, a1,a2), stat (trybuf, &cstat) == 0)

  /* Check that RCS file is not working file.
     Some hosts don't report file name length errors.  */

  if ((try2 ("RCS/%s%s", filebase, RCSSUFFIX)
       || try1 ("RCS/%s", filebase)
       || try2 ("%s%s", filebase, RCSSUFFIX))
      && ! (filestat
        && filestat->st_dev == cstat.st_dev
        && filestat->st_ino == cstat.st_ino))
    {
      if (getbuf)
    {
      char *p = *getbuf = xmalloc (maxgetsize);
      sprintf (p, readonly ? CHECKOUT : CHECKOUT_LOCKED, dotslash);
      p += strlen (p);
      p += quote_system_arg (p, filename);
      *p = '\0';
    }

      if (diffbuf)
    {
      char *p = *diffbuf = xmalloc (maxdiffsize);
      sprintf (p, RCSDIFF1, dotslash);
      p += strlen (p);
      p += quote_system_arg (p, filename);
      *p++ = '>';
      strcpy (p, DEV_NULL);
    }

      r = "RCS";
    }
  else if (try2 ("SCCS/%s%s", SCCSPREFIX, filebase)
       || try2 ("%s%s", SCCSPREFIX, filebase))
    {
      if (getbuf)
    {
      char *p = *getbuf = xmalloc (maxgetsize);
      sprintf (p, readonly ? GET : GET_LOCKED);
      p += strlen (p);
      p += quote_system_arg (p, trybuf);
      *p = '\0';
    }

      if (diffbuf)
    {
      char *p = *diffbuf = xmalloc (maxdiffsize);
      strcpy (p, SCCSDIFF1);
      p += sizeof SCCSDIFF1 - 1;
      p += quote_system_arg (p, trybuf);
      sprintf (p, SCCSDIFF2, dotslash);
      p += strlen (p);
      p += quote_system_arg (p, filename);
      *p++ = '>';
      strcpy (p, DEV_NULL);
    }

      r = "SCCS";
    }
  else if (!readonly && filestat
       && try1 ("%s@@", filebase) && S_ISDIR (cstat.st_mode))
    {
      if (getbuf)
    {
      char *p = *getbuf = xmalloc (maxgetsize);
      strcpy (p, CLEARTOOL_CO);
      p += sizeof CLEARTOOL_CO - 1;
      p += quote_system_arg (p, filename);
      *p = '\0';
    }

      if (diffbuf)
    *diffbuf = 0;

      r = "ClearCase";
     }
  else if (!readonly && filestat &&
           (getenv("P4PORT") || getenv("P4USER") || getenv("P4CONFIG")))
    {
      if (getbuf)
    {
      char *p = *getbuf = xmalloc (maxgetsize);
      strcpy (p, PERFORCE_CO);
      p += sizeof PERFORCE_CO - 1;
      p += quote_system_arg (p, filename);
      *p = '\0';
    }

      if (diffbuf)
    *diffbuf = 0;

      r = "Perforce";
    }

  free (trybuf);
  return r;
}

/* Get FILENAME from version control system CS.  The file already exists if
   EXISTS.  Only readonly access is needed if READONLY.
   Use the command GETBUF to actually get the named file.
   Store the resulting file status into *FILESTAT.
   Return true if successful.  */
bool
version_get (char const *filename, char const *cs, bool exists, bool readonly,
         char const *getbuf, struct stat *filestat)
{
  if (patch_get < 0)
    {
      ask ("Get file %s from %s%s? [y] ",
       quotearg (filename), cs, readonly ? "" : " with lock");
      if (*buf == 'n')
    return 0;
    }

  if (dry_run)
    {
      if (! exists)
    fatal ("can't do dry run on nonexistent version-controlled file %s; invoke `%s' and try again",
           quotearg (filename), getbuf);
    }
  else
    {
      if (verbosity == VERBOSE)
    say ("Getting file %s from %s%s...\n", quotearg (filename),
         cs, readonly ? "" : " with lock");
      if (systemic (getbuf) != 0)
    fatal ("Can't get file %s from %s", quotearg (filename), cs);
      if (stat (filename, filestat) != 0)
    pfatal ("%s", quotearg (filename));
    }

  return 1;
}

/* Allocate a unique area for a string. */

char *
savebuf (register char const *s, register size_t size)
{
  register char *rv;

  assert (s && size);
  rv = malloc (size);

  if (! rv)
    {
      if (! using_plan_a)
    memory_fatal ();
    }
  else
    memcpy (rv, s, size);

  return rv;
}

char *
savestr (char const *s)
{
  return savebuf (s, strlen (s) + 1);
}

void
remove_prefix (char *p, size_t prefixlen)
{
  char const *s = p + prefixlen;
  while ((*p++ = *s++))
    continue;
}

char *
format_linenum (char numbuf[LINENUM_LENGTH_BOUND + 1], LINENUM n)
{
  char *p = numbuf + LINENUM_LENGTH_BOUND;
  *p = '\0';

  if (n < 0)
    {
      do
    *--p = '0' - (int) (n % 10);
      while ((n /= 10) != 0);

      *--p = '-';
    }
  else
    {
      do
    *--p = '0' + (int) (n % 10);
      while ((n /= 10) != 0);
    }

  return p;
}

#if !HAVE_VPRINTF
#define vfprintf my_vfprintf
static int
vfprintf (FILE *stream, char const *format, va_list args)
{
#if !HAVE_DOPRNT && HAVE__DOPRINTF
# define _doprnt _doprintf
#endif
#if HAVE_DOPRNT || HAVE__DOPRINTF
  _doprnt (format, args, stream);
  return ferror (stream) ? -1 : 0;
#else
  int *a = (int *) args;
  return fprintf (stream, format,
          a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]);
#endif
}
#endif /* !HAVE_VPRINTF */

/* Terminal output, pun intended. */

void
fatal (char const *format, ...)
{
  va_list args;
  fprintf (stderr, "%s: **** ", program_name);
  va_start (args, format);
  vfprintf (stderr, format, args);
  va_end (args);
  putc ('\n', stderr);
  fflush (stderr);
  fatal_exit (0);
}

void
memory_fatal (void)
{
  fatal ("out of memory");
}

void
read_fatal (void)
{
  pfatal ("read error");
}

void
write_fatal (void)
{
  pfatal ("write error");
}

/* Say something from patch, something from the system, then silence . . . */

void
pfatal (char const *format, ...)
{
  int errnum = errno;
  va_list args;
  fprintf (stderr, "%s: **** ", program_name);
  va_start (args, format);
  vfprintf (stderr, format, args);
  va_end (args);
  fflush (stderr); /* perror bypasses stdio on some hosts.  */
  errno = errnum;
  perror (" ");
  fflush (stderr);
  fatal_exit (0);
}

/* Tell the user something.  */

void
say (char const *format, ...)
{
  va_list args;
  va_start (args, format);
  vfprintf (stdout, format, args);
  va_end (args);
  fflush (stdout);
}

/* Get a response from the user, somehow or other. */

void
ask (char const *format, ...)
{
  static int ttyfd = -2;
  int r;
  va_list args;

  va_start (args, format);
  vfprintf (stdout, format, args);
  va_end (args);
  fflush (stdout);

  if (ttyfd == -2)
    {
      /* If standard output is not a tty, don't bother opening /dev/tty,
     since it's unlikely that stdout will be seen by the tty user.
     The isatty test also works around a bug in GNU Emacs 19.34 under Linux
     which makes a call-process `patch' hang when it reads from /dev/tty.
     POSIX.1-2001 XCU line 26599 requires that we read /dev/tty,
     though.  */
      ttyfd = (posixly_correct || isatty (STDOUT_FILENO)
           ? open (TTY_DEVICE, O_RDONLY)
           : -1);
    }

  if (ttyfd < 0)
    {
      /* No terminal at all -- default it.  */
      printf ("\n");
      buf[0] = '\n';
      buf[1] = '\0';
    }
  else
    {
      size_t s = 0;
      while ((r = read (ttyfd, buf + s, bufsize - 1 - s)) == bufsize - 1 - s
         && buf[bufsize - 2] != '\n')
    {
      s = bufsize - 1;
      bufsize *= 2;
      buf = realloc (buf, bufsize);
      if (!buf)
        memory_fatal ();
    }
      if (r == 0)
    printf ("EOF\n");
      else if (r < 0)
    {
      perror ("tty read");
      fflush (stderr);
      close (ttyfd);
      ttyfd = -1;
      r = 0;
    }
      buf[s + r] = '\0';
    }
}

/* Return nonzero if it OK to reverse a patch.  */

bool
ok_to_reverse (char const *format, ...)
{
  bool r = false;

  if (noreverse || ! (force && verbosity == SILENT))
    {
      va_list args;
      va_start (args, format);
      vfprintf (stdout, format, args);
      va_end (args);
    }

  if (noreverse)
    {
      printf ("  Skipping patch.\n");
      skip_rest_of_patch = true;
    }
  else if (force)
    {
      if (verbosity != SILENT)
    printf ("  Applying it anyway.\n");
    }
  else if (batch)
    {
      say (reverse ? "  Ignoring -R.\n" : "  Assuming -R.\n");
      r = true;
    }
  else
    {
      ask (reverse ? "  Ignore -R? [n] " : "  Assume -R? [n] ");
      r = *buf == 'y';
      if (! r)
    {
      ask ("Apply anyway? [n] ");
      if (*buf != 'y')
        {
          if (verbosity != SILENT)
        say ("Skipping patch.\n");
          skip_rest_of_patch = true;
        }
    }
    }

  return r;
}

/* How to handle certain events when not in a critical region. */

#define NUM_SIGS (sizeof (sigs) / sizeof (*sigs))
static int const sigs[] = {
#ifdef SIGHUP
       SIGHUP,
#endif
#ifdef SIGPIPE
       SIGPIPE,
#endif
#ifdef SIGTERM
       SIGTERM,
#endif
#ifdef SIGXCPU
       SIGXCPU,
#endif
#ifdef SIGXFSZ
       SIGXFSZ,
#endif
       SIGINT
};

#if !HAVE_SIGPROCMASK
#define sigset_t int
#define sigemptyset(s) (*(s) = 0)
#ifndef sigmask
#define sigmask(sig) (1 << ((sig) - 1))
#endif
#define sigaddset(s, sig) (*(s) |= sigmask (sig))
#define sigismember(s, sig) ((*(s) & sigmask (sig)) != 0)
#ifndef SIG_BLOCK
#define SIG_BLOCK 0
#endif
#ifndef SIG_UNBLOCK
#define SIG_UNBLOCK (SIG_BLOCK + 1)
#endif
#ifndef SIG_SETMASK
#define SIG_SETMASK (SIG_BLOCK + 2)
#endif
#define sigprocmask(how, n, o) \
  ((how) == SIG_BLOCK \
   ? ((o) ? *(o) = sigblock (*(n)) : sigblock (*(n))) \
   : (how) == SIG_UNBLOCK \
   ? sigsetmask (((o) ? *(o) = sigblock (0) : sigblock (0)) & ~*(n)) \
   : (o ? *(o) = sigsetmask (*(n)) : sigsetmask (*(n))))
#if !HAVE_SIGSETMASK
#define sigblock(mask) 0
#define sigsetmask(mask) 0
#endif
#endif

static sigset_t initial_signal_mask;
static sigset_t signals_to_block;

#if ! HAVE_SIGACTION
static RETSIGTYPE fatal_exit_handler (int) __attribute__ ((noreturn));
static RETSIGTYPE
fatal_exit_handler (int sig)
{
  signal (sig, SIG_IGN);
  fatal_exit (sig);
}
#endif

void
set_signals (bool reset)
{
  int i;
#if HAVE_SIGACTION
  struct sigaction initial_act, fatal_act;
  fatal_act.sa_handler = fatal_exit;
  sigemptyset (&fatal_act.sa_mask);
  fatal_act.sa_flags = 0;
#define setup_handler(sig) sigaction (sig, &fatal_act, (struct sigaction *) 0)
#else
#define setup_handler(sig) signal (sig, fatal_exit_handler)
#endif

  if (!reset)
    {
#ifdef SIGCHLD
      /* System V fork+wait does not work if SIGCHLD is ignored.  */
      signal (SIGCHLD, SIG_DFL);
#endif
      sigemptyset (&signals_to_block);
      for (i = 0;  i < NUM_SIGS;  i++)
    {
      bool ignoring_signal;
#if HAVE_SIGACTION
      if (sigaction (sigs[i], (struct sigaction *) 0, &initial_act) != 0)
        continue;
      ignoring_signal = initial_act.sa_handler == SIG_IGN;
#else
      ignoring_signal = signal (sigs[i], SIG_IGN) == SIG_IGN;
#endif
      if (! ignoring_signal)
        {
          sigaddset (&signals_to_block, sigs[i]);
          setup_handler (sigs[i]);
        }
    }
    }
  else
    {
      /* Undo the effect of ignore_signals.  */
#if HAVE_SIGPROCMASK || HAVE_SIGSETMASK
      sigprocmask (SIG_SETMASK, &initial_signal_mask, (sigset_t *) 0);
#else
      for (i = 0;  i < NUM_SIGS;  i++)
    if (sigismember (&signals_to_block, sigs[i]))
      setup_handler (sigs[i]);
#endif
    }
}

/* How to handle certain events when in a critical region. */

void
ignore_signals (void)
{
#if HAVE_SIGPROCMASK || HAVE_SIGSETMASK
  sigprocmask (SIG_BLOCK, &signals_to_block, &initial_signal_mask);
#else
  int i;
  for (i = 0;  i < NUM_SIGS;  i++)
    if (sigismember (&signals_to_block, sigs[i]))
      signal (sigs[i], SIG_IGN);
#endif
}

void
exit_with_signal (int sig)
{
  sigset_t s;
  signal (sig, SIG_DFL);
  sigemptyset (&s);
  sigaddset (&s, sig);
  sigprocmask (SIG_UNBLOCK, &s, (sigset_t *) 0);
  raise (sig);
  exit (2);
}

int
systemic (char const *command)
{
  if (debug & 8)
    say ("+ %s\n", command);
  fflush (stdout);
  return system (command);
}

/* Replace '/' with '\0' in FILENAME if it marks a place that
   needs testing for the existence of directory.  Return the address
   of the last location replaced, or 0 if none were replaced.  */
static char *
replace_slashes (char *filename)
{
  char *f;
  char *last_location_replaced = 0;
  char const *component_start;

  for (f = filename + FILESYSTEM_PREFIX_LEN (filename);  ISSLASH (*f);  f++)
    continue;

  component_start = f;

  for (; *f; f++)
    if (ISSLASH (*f))
      {
    char *slash = f;

    /* Treat multiple slashes as if they were one slash.  */
    while (ISSLASH (f[1]))
      f++;

    /* Ignore slashes at the end of the path.  */
    if (! f[1])
      break;

    /* "." and ".." need not be tested.  */
    if (! (slash - component_start <= 2
           && component_start[0] == '.' && slash[-1] == '.'))
      {
        *slash = '\0';
        last_location_replaced = slash;
      }

    component_start = f + 1;
      }

  return last_location_replaced;
}

/* Make sure we'll have the directories to create a file.
   Ignore the last element of `filename'.  */

static void
makedirs (register char *filename)
{
  register char *f;
  register char *flim = replace_slashes (filename);

  if (flim)
    {
      /* Create any missing directories, replacing NULs by '/'s.
     Ignore errors.  We may have to keep going even after an EEXIST,
     since the path may contain ".."s; and when there is an EEXIST
     failure the system may return some other error number.
     Any problems will eventually be reported when we create the file.  */
      for (f = filename;  f <= flim;  f++)
    if (!*f)
      {
        mkdir (filename,
           S_IRUSR|S_IWUSR|S_IXUSR
           |S_IRGRP|S_IWGRP|S_IXGRP
           |S_IROTH|S_IWOTH|S_IXOTH);
        *f = '/';
      }
    }
}

/* Remove empty ancestor directories of FILENAME.
   Ignore errors, since the path may contain ".."s, and when there
   is an EEXIST failure the system may return some other error number.  */
void
removedirs (char *filename)
{
  size_t i;

  for (i = strlen (filename);  i != 0;  i--)
    if (ISSLASH (filename[i])
    && ! (ISSLASH (filename[i - 1])
          || (filename[i - 1] == '.'
          && (i == 1
              || ISSLASH (filename[i - 2])
              || (filename[i - 2] == '.'
              && (i == 2
                  || ISSLASH (filename[i - 3])))))))
      {
    filename[i] = '\0';
    if (rmdir (filename) == 0 && verbosity == VERBOSE)
      say ("Removed empty directory %s\n", quotearg (filename));
    filename[i] = '/';
      }
}

static time_t initial_time;

void
init_time (void)
{
  time (&initial_time);
}

/* Make filenames more reasonable. */

char *
fetchname (char *at, int strip_leading, time_t *pstamp)
{
    char *name;
    register char *t;
    int sleading = strip_leading;
    time_t stamp = (time_t) -1;

    while (ISSPACE ((unsigned char) *at))
    at++;
    if (debug & 128)
    say ("fetchname %s %d\n", at, strip_leading);

    name = at;
    /* Strip up to `strip_leading' leading slashes and null terminate.
       If `strip_leading' is negative, strip all leading slashes.  */
    for (t = at;  *t;  t++)
      {
    if (ISSLASH (*t))
      {
        while (ISSLASH (t[1]))
          t++;
        if (strip_leading < 0 || --sleading >= 0)
        name = t+1;
      }
    else if (ISSPACE ((unsigned char) *t))
      {
        /* Allow file names with internal spaces,
           but only if a tab separates the file name from the date.  */
        char const *u = t;
        while (*u != '\t' && ISSPACE ((unsigned char) u[1]))
          u++;
        if (*u != '\t' && strchr (u + 1, '\t'))
          continue;

        if (set_time | set_utc)
          stamp = str2time (&u, initial_time,
                set_utc ? 0L : TM_LOCAL_ZONE);
        else
          {
        /* The head says the file is nonexistent if the timestamp
           is the epoch; but the listed time is local time, not UTC,
           and POSIX.1 allows local time offset anywhere in the range
           -25:00 < offset < +26:00.  Match any time in that
           range by assuming local time is -25:00 and then matching
           any ``local'' time T in the range 0 < T < 25+26 hours.  */
        stamp = str2time (&u, initial_time, -25L * 60 * 60);
        if (0 < stamp && stamp < (25 + 26) * 60L * 60)
          stamp = 0;
          }

        if (*u && ! ISSPACE ((unsigned char) *u))
          stamp = (time_t) -1;

        *t = '\0';
        break;
      }
      }

    if (!*name)
      return 0;

    /* If the name is "/dev/null", ignore the name and mark the file
       as being nonexistent.  The name "/dev/null" appears in patches
       regardless of how NULL_DEVICE is spelled.  */
    if (strcmp (at, "/dev/null") == 0)
      {
    if (pstamp)
      *pstamp = 0;
    return 0;
      }

    /* Ignore the name if it doesn't have enough slashes to strip off.  */
    if (0 < sleading)
      return 0;

    if (pstamp)
      *pstamp = stamp;

    return savestr (name);
}

void
Fseek (FILE *stream, file_offset offset, int ptrname)
{
  if (file_seek (stream, offset, ptrname) != 0)
    pfatal ("fseek");
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/quotesys.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/quotesys.h used in: Step 574
/* quotesys.h -- declarations for quoting system arguments */

#if defined __STDC__ || __GNUC__
# define __QUOTESYS_P(args) args
#else
# define __QUOTESYS_P(args) ()
#endif

size_t quote_system_arg __QUOTESYS_P ((char *, char const *));

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/maketime.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/maketime.h used in: Step 574
/* Yield time_t from struct partime yielded by partime.  */

/* Copyright 1993, 1994, 1995 Paul Eggert
   Distributed under license by the Free Software Foundation, Inc.

   This file is part of RCS.

   RCS 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.

   RCS 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 RCS; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   Report problems and direct all questions to:

   rcs-bugs@cs.purdue.edu

 */

#if defined __STDC__ || has_prototypes
# define __MAKETIME_P(x) x
#else
# define __MAKETIME_P(x) ()
#endif

struct tm *time2tm __MAKETIME_P ((time_t, int));
time_t difftm __MAKETIME_P ((struct tm const *, struct tm const *));
time_t str2time __MAKETIME_P ((char const **, time_t, long));
time_t tm2time __MAKETIME_P ((struct tm *, int));
void adjzone __MAKETIME_P ((struct tm *, long));

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/partime.h

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/partime.h used in: Step 574
/* Parse a string, yielding a struct partime that describes it.  */

/* Copyright 1993, 1994, 1995, 1997 Paul Eggert
   Distributed under license by the Free Software Foundation, Inc.

   This file is part of RCS.

   RCS 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.

   RCS 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 RCS; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   Report problems and direct all questions to:

   rcs-bugs@cs.purdue.edu

 */

#define TM_UNDEFINED (-1)
#define TM_DEFINED(x) (0 <= (x))

/* #include <limits.h> if you want to use these symbols.  */
#define TM_LOCAL_ZONE LONG_MIN
#define TM_UNDEFINED_ZONE (LONG_MIN + 1)

struct partime
  {
    /* This structure describes the parsed time.
       Only the following tm_* members are used:
        sec, min, hour, mday, mon, year, wday, yday.
       If ! TM_DEFINED (value), the parser never found the value.
       The tm_year field is the actual year, not the year - 1900;
       but see ymodulus below.  */
    struct tm tm;

    /* Like tm, but values are relative to the value in tm,
       and values are initialized to 0 rather than to TM_UNDEFINED.
       Only the following tm_* members are used:
        sec, min, hour, mday, mon, year.  */
    struct tm tmr;

    /* If TM_DEFINED (wday_ordinal),
       then day number (e.g. 3 in "3rd Sunday").  */
    int wday_ordinal;

    /* If TM_DEFINED (ymodulus),
       then tm.tm_year is actually modulo ymodulus.  */
    int ymodulus;

    /* Week of year, ISO 8601 style.
       If ! TM_DEFINED (yweek), the parser never found yweek.
       Weeks start on Mondays.
       Week 1 includes Jan 4.  */
    int yweek;

    /* Seconds east of UTC; or TM_LOCAL_ZONE or TM_UNDEFINED_ZONE.  */
    long zone;
  };

#if defined __STDC__ || has_prototypes
# define __PARTIME_P(x) x
#else
# define __PARTIME_P(x) ()
#endif

char *partime __PARTIME_P ((char const *, struct partime *));
char *parzone __PARTIME_P ((char const *, long *));

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/version.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/version.c used in: Step 575
/* Print the version number.  */

/* $Id: version.c,v 1.13 2003/05/18 08:25:17 eggert Exp $ */

#define XTERN extern
#include <common.h>
#undef XTERN
#define XTERN
#include <version.h>

static char const copyright_string[] = "\
Copyright (C) 1988 Larry Wall\n\
Copyright (C) 2003 Free Software Foundation, Inc.";

static char const free_software_msgid[] = "\
This program comes with NO WARRANTY, to the extent permitted by law.\n\
You may redistribute copies of this program\n\
under the terms of the GNU General Public License.\n\
For more information about these matters, see the file named COPYING.";

static char const authorship_msgid[] = "\
written by Larry Wall and Paul Eggert";

void
version (void)
{
  printf ("%s %s\n%s\n\n%s\n\n%s\n", PACKAGE_NAME, PACKAGE_VERSION,
      copyright_string, free_software_msgid, authorship_msgid);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/build/patch-2.5.9/xmalloc.c

File: ../git/live-bootstrap/sysa/patch-2.5.9/build/patch-2.5.9/xmalloc.c used in: Step 576
/* xmalloc.c -- malloc with out of memory checking
   Copyright (C) 1990-1999, 2000, 2002 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>

#if STDC_HEADERS
# include <stdlib.h>
#else
void *calloc ();
void *malloc ();
void *realloc ();
void free ();
#endif

#include "gettext.h"
#define _(msgid) gettext (msgid)
#define N_(msgid) msgid

#include "error.h"
#include "xalloc.h"

#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif

/* The following tests require AC_PREREQ(2.54).  */

#ifndef HAVE_MALLOC
"you must run the autoconf test for a GNU libc compatible malloc"
#endif

#ifndef HAVE_REALLOC
"you must run the autoconf test for a GNU libc compatible realloc"
#endif

/* Exit value when the requested amount of memory is not available.
   The caller may set it to some other value.  */
int xalloc_exit_failure = EXIT_FAILURE;

/* If non NULL, call this function when memory is exhausted. */
void (*xalloc_fail_func) PARAMS ((void)) = 0;

/* If XALLOC_FAIL_FUNC is NULL, or does return, display this message
   before exiting when memory is exhausted.  Goes through gettext. */
char const xalloc_msg_memory_exhausted[] = N_("memory exhausted");

void
xalloc_die (void)
{
  if (xalloc_fail_func)
    (*xalloc_fail_func) ();
  error (xalloc_exit_failure, 0, "%s", _(xalloc_msg_memory_exhausted));
  /* The `noreturn' cannot be given to error, since it may return if
     its first argument is 0.  To help compilers understand the
     xalloc_die does terminate, call exit. */
  exit (EXIT_FAILURE);
}

/* Allocate N bytes of memory dynamically, with error checking.  */

void *
xmalloc (size_t n)
{
  void *p;

  p = malloc (n);
  if (p == 0)
    xalloc_die ();
  return p;
}

/* Change the size of an allocated block of memory P to N bytes,
   with error checking.  */

void *
xrealloc (void *p, size_t n)
{
  p = realloc (p, n);
  if (p == 0)
    xalloc_die ();
  return p;
}

/* Allocate memory for N elements of S bytes, with error checking.  */

void *
xcalloc (size_t n, size_t s)
{
  void *p;

  p = calloc (n, s);
  if (p == 0)
    xalloc_die ();
  return p;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/patch-2.5.9/patch-2.5.9.checksums

File: ../git/live-bootstrap/sysa/patch-2.5.9/patch-2.5.9.checksums used in: Step 581
0ad4338d5aaf61d126984508172e731da3162fea3d46b9fb34b2dc71beb96a1c  /usr/bin/patch

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/sources

File: ../git/live-bootstrap/sysa/gzip-1.2.4/sources used in: Step 583
https://mirrors.kernel.org/gnu/gzip/gzip-1.2.4.tar.gz 1ca41818a23c9c59ef1d5e1d00c0d5eaa2285d931c0fb059637d7c0cc02ad967

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/mk/main.mk

File: ../git/live-bootstrap/sysa/gzip-1.2.4/mk/main.mk used in: Step 589
# SPDX-FileCopyrightText: 2022 Andrius Å tikonas <andrius@stikonas.eu>
#
# SPDX-License-Identifier: GPL-3.0-or-later

CC = tcc
AR = tcc -ar

CPPFLAGS = -DNO_UTIME \
         -Dstrlwr=unused

CFLAGS = -I .
LDFLAGS = -static

.PHONY: all

GZIP_SRC = gzip bits crypt deflate getopt inflate lzw trees unlzh unlzw unpack unzip util zip
GZIP_OBJ = $(addsuffix .o, $(GZIP_SRC))

all: gzip

gzip: $(GZIP_OBJ)
    $(CC) $(LDFLAGS) $^ -o $@

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/files/stat_override.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/files/stat_override.c used in: Step 590
/*
 * SPDX-FileCopyrightText: 2022 fosslinux <fosslinux@aussies.space>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include <sys/stat.h>
#include <linux/syscall.h>
#include <linux/x86/syscall.h>

int _stat(const char *path, struct stat *buf) {
    int rc = stat(path, buf);
    if (rc == 0) {
        buf->st_atime = 0;
        buf->st_mtime = 0;
    }
    return rc;
}

int _lstat(const char *path, struct stat *buf) {
    int rc = lstat(path, buf);
    if (rc == 0) {
        buf->st_atime = 0;
        buf->st_mtime = 0;
    }
    return rc;
}

#define stat(a,b) _stat(a,b)
#define lstat(a,b) _lstat(a,b)

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/patches/removecrc.patch

File: ../git/live-bootstrap/sysa/gzip-1.2.4/patches/removecrc.patch used in: Step 592
SPDX-FileCopyrightText: 2023 Paul Dersey <pdersey@gmail.com>

SPDX-License-Identifier: GPL-2.0-or-later

Remove generated CRC table

--- util.c
+++ util.c
@@ -406,57 +406,3 @@ voidp xmalloc (size)
 /* ========================================================================
  * 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
-};

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/patches/makecrc-write-to-file.patch

File: ../git/live-bootstrap/sysa/gzip-1.2.4/patches/makecrc-write-to-file.patch used in: Step 593
SPDX-FileCopyrightText: 2023 Paul Dersey <pdersey@gmail.com>

SPDX-License-Identifier: GPL-2.0-or-later

Modify makecrc so that it outputs C code to a file that
can be appended to util.c

--- sample/makecrc.c
+++ sample/makecrc.c
@@ -47,7 +47,9 @@ main()
     e |= 1L << (31 - p[i]);
 
   /* Compute and print table of CRC's, five per line */
-  printf("  0x00000000L");
+  FILE *output = fopen("crc.c", "w");
+  fprintf(output, "ulg crc_32_tab[] = {\n");
+  fprintf(output,"  0x00000000L");
   for (i = 1; i < 256; i++)
   {
     c = i;
@@ -56,8 +58,8 @@ main()
      */
     for (k = 8; k; k--)
       c = c & 1 ? (c >> 1) ^ e : c >> 1;
-    printf(i % 5 ? ", 0x%08lxL" : ",\n  0x%08lxL", c);
+    fprintf(output, i % 5 ? ", 0x%08lxL" : ",\n  0x%08lxL", c);
   }
-  putchar('\n');
+  fprintf(output,"\n};\n");
   return 0;
 }

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/sample/makecrc.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/sample/makecrc.c used in: Step 593, Step 594
/* Not copyrighted 1990 Mark Adler */

#ifndef lint
static char rcsid[] = "$Id: makecrc.c,v 0.6 1993/05/28 07:42:59 jloup Exp $";
#endif

#include <stdio.h>

main()
/*
  Generate a table 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 table is simply the CRC of all possible eight bit values.  This is all
  the information needed to generate CRC's on data a byte at a time for all
  combinations of CRC register values and incoming bytes.  The table is
  written to stdout as 256 long hexadecimal values in C language format.
*/
{
  unsigned long c;      /* crc shift register */
  unsigned long e;      /* polynomial exclusive-or pattern */
  int i;                /* counter for all possible eight bit values */
  int k;                /* byte being shifted into crc apparatus */

  /* terms of polynomial defining this crc (except x^32): */
  static int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};

  /* Make exclusive-or pattern from polynomial (0xedb88320) */
  e = 0;
  for (i = 0; i < sizeof(p)/sizeof(int); i++)
    e |= 1L << (31 - p[i]);

  /* Compute and print table of CRC's, five per line */
  printf("  0x00000000L");
  for (i = 1; i < 256; i++)
  {
    c = i;
    /* The idea to initialize the register with the byte instead of
     * zero was stolen from Haruhiko Okumura's ar002
     */
    for (k = 8; k; k--)
      c = c & 1 ? (c >> 1) ^ e : c >> 1;
    printf(i % 5 ? ", 0x%08lxL" : ",\n  0x%08lxL", c);
  }
  putchar('\n');
  return 0;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/crc.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/crc.c used in: Step 596

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/bits.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/bits.c used in: Step 600
/* 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"
#include "crypt.h"

#ifdef DEBUG
#  include <stdio.h>
#endif

#ifdef RCSID
static char rcsid[] = "$Id: bits.c,v 0.9 1993/06/11 10:16:58 jloup 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--) {
#ifdef CRYPT
        int t;
    if (key) zencode(*buf, t);
#endif
    put_byte(*buf++);
    }
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/tailor.h

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/tailor.h used in: Step 600, Step 602, Step 606
/* 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 0.18 1993/06/14 19:32:20 jloup Exp $ */

#if defined(__MSDOS__) && !defined(MSDOS)
#  define MSDOS
#endif

#if defined(__OS2__) && !defined(OS2)
#  define OS2
#endif

#if defined(OS2) && defined(MSDOS) /* MS C under OS/2 */
#  undef MSDOS
#endif

#ifdef MSDOS
#  ifdef __GNUC__
     /* DJGPP version 1.09+ on MS-DOS.
      * The DJGPP 1.09 stat() function must be upgraded before gzip will
      * fully work.
      * No need for DIRENT, since <unistd.h> defines POSIX_SOURCE which
      * implies DIRENT.
      */
#    define near
#  else
#    define MAXSEG_64K
#    ifdef __TURBOC__
#      define NO_OFF_T
#      ifdef __BORLANDC__
#        define DIRENT
#      else
#        define NO_UTIME
#      endif
#    else /* MSC */
#      define HAVE_SYS_UTIME_H
#      define NO_UTIME_H
#    endif
#  endif
#  define PATH_SEP2 '\\'
#  define PATH_SEP3 ':'
#  define MAX_PATH_LEN  128
#  define NO_MULTIPLE_DOTS
#  define MAX_EXT_CHARS 3
#  define Z_SUFFIX "z"
#  define NO_CHOWN
#  define PROTO
#  define STDC_HEADERS
#  define NO_SIZE_CHECK
#  define casemap(c) tolow(c) /* Force file names to lower case */
#  include <io.h>
#  define OS_CODE  0x00
#  define SET_BINARY_MODE(fd) setmode(fd, O_BINARY)
#  if !defined(NO_ASM) && !defined(ASMV)
#    define ASMV
#  endif
#else
#  define near
#endif

#ifdef OS2
#  define PATH_SEP2 '\\'
#  define PATH_SEP3 ':'
#  define MAX_PATH_LEN  260
#  ifdef OS2FAT
#    define NO_MULTIPLE_DOTS
#    define MAX_EXT_CHARS 3
#    define Z_SUFFIX "z"
#    define casemap(c) tolow(c)
#  endif
#  define NO_CHOWN
#  define PROTO
#  define STDC_HEADERS
#  include <io.h>
#  define OS_CODE  0x06
#  define SET_BINARY_MODE(fd) setmode(fd, O_BINARY)
#  ifdef _MSC_VER
#    define HAVE_SYS_UTIME_H
#    define NO_UTIME_H
#    define MAXSEG_64K
#    undef near
#    define near _near
#  endif
#  ifdef __EMX__
#    define HAVE_SYS_UTIME_H
#    define NO_UTIME_H
#    define DIRENT
#    define EXPAND(argc,argv) \
       {_response(&argc, &argv); _wildcard(&argc, &argv);}
#  endif
#  ifdef __BORLANDC__
#    define DIRENT
#  endif
#  ifdef __ZTC__
#    define NO_DIR
#    define NO_UTIME_H
#    include <dos.h>
#    define EXPAND(argc,argv) \
       {response_expand(&argc, &argv);}
#  endif
#endif

#ifdef WIN32 /* Windows NT */
#  define HAVE_SYS_UTIME_H
#  define NO_UTIME_H
#  define PATH_SEP2 '\\'
#  define PATH_SEP3 ':'
#  define MAX_PATH_LEN  260
#  define NO_CHOWN
#  define PROTO
#  define STDC_HEADERS
#  define SET_BINARY_MODE(fd) setmode(fd, O_BINARY)
#  include <io.h>
#  include <malloc.h>
#  ifdef NTFAT
#    define NO_MULTIPLE_DOTS
#    define MAX_EXT_CHARS 3
#    define Z_SUFFIX "z"
#    define casemap(c) tolow(c) /* Force file names to lower case */
#  endif
#  define OS_CODE  0x0b
#endif

#ifdef MSDOS
#  ifdef __TURBOC__
#    include <alloc.h>
#    define DYN_ALLOC
     /* Turbo C 2.0 does not accept static allocations of large arrays */
     void * fcalloc (unsigned items, unsigned size);
     void fcfree (void *ptr);
#  else /* MSC */
#    include <malloc.h>
#    define fcalloc(nitems,itemsize) halloc((long)(nitems),(itemsize))
#    define fcfree(ptr) hfree(ptr)
#  endif
#else
#  ifdef MAXSEG_64K
#    define fcalloc(items,size) calloc((items),(size))
#  else
#    define fcalloc(items,size) malloc((size_t)(items)*(size_t)(size))
#  endif
#  define fcfree(ptr) free(ptr)
#endif

#if defined(VAXC) || defined(VMS)
#  define PATH_SEP ']'
#  define PATH_SEP2 ':'
#  define SUFFIX_SEP ';'
#  define NO_MULTIPLE_DOTS
#  define Z_SUFFIX "-gz"
#  define RECORD_IO 1
#  define casemap(c) tolow(c)
#  define OS_CODE  0x02
#  define OPTIONS_VAR "GZIP_OPT"
#  define STDC_HEADERS
#  define NO_UTIME
#  define EXPAND(argc,argv) vms_expand_args(&argc,&argv);
#  include <file.h>
#  define unlink delete
#  ifdef VAXC
#    define NO_FCNTL_H
#    include <unixio.h>
#  endif
#endif

#ifdef AMIGA
#  define PATH_SEP2 ':'
#  define STDC_HEADERS
#  define OS_CODE  0x01
#  define ASMV
#  ifdef __GNUC__
#    define DIRENT
#    define HAVE_UNISTD_H
#  else /* SASC */
#    define NO_STDIN_FSTAT
#    define SYSDIR
#    define NO_SYMLINK
#    define NO_CHOWN
#    define NO_FCNTL_H
#    include <fcntl.h> /* for read() and write() */
#    define direct dirent
     extern void _expand_args(int *argc, char ***argv);
#    define EXPAND(argc,argv) _expand_args(&argc,&argv);
#    undef  O_BINARY /* disable useless --ascii option */
#  endif
#endif

#if defined(ATARI) || defined(atarist)
#  ifndef STDC_HEADERS
#    define STDC_HEADERS
#    define HAVE_UNISTD_H
#    define DIRENT
#  endif
#  define ASMV
#  define OS_CODE  0x05
#  ifdef TOSFS
#    define PATH_SEP2 '\\'
#    define PATH_SEP3 ':'
#    define MAX_PATH_LEN  128
#    define NO_MULTIPLE_DOTS
#    define MAX_EXT_CHARS 3
#    define Z_SUFFIX "z"
#    define NO_CHOWN
#    define casemap(c) tolow(c) /* Force file names to lower case */
#    define NO_SYMLINK
#  endif
#endif

#ifdef MACOS
#  define PATH_SEP ':'
#  define DYN_ALLOC
#  define PROTO
#  define NO_STDIN_FSTAT
#  define NO_CHOWN
#  define NO_UTIME
#  define chmod(file, mode) (0)
#  define OPEN(name, flags, mode) open(name, flags)
#  define OS_CODE  0x07
#  ifdef MPW
#    define isatty(fd) ((fd) <= 2)
#  endif
#endif

#ifdef __50SERIES /* Prime/PRIMOS */
#  define PATH_SEP '>'
#  define STDC_HEADERS
#  define NO_MEMORY_H
#  define NO_UTIME_H
#  define NO_UTIME
#  define NO_CHOWN 
#  define NO_STDIN_FSTAT 
#  define NO_SIZE_CHECK 
#  define NO_SYMLINK
#  define RECORD_IO  1
#  define casemap(c)  tolow(c) /* Force file names to lower case */
#  define put_char(c) put_byte((c) & 0x7F)
#  define get_char(c) ascii2pascii(get_byte())
#  define OS_CODE  0x0F    /* temporary, subject to change */
#  ifdef SIGTERM
#    undef SIGTERM         /* We don't want a signal handler for SIGTERM */
#  endif
#endif

#if defined(pyr) && !defined(NOMEMCPY) /* Pyramid */
#  define NOMEMCPY /* problem with overlapping copies */
#endif

#ifdef TOPS20
#  define OS_CODE  0x0a
#endif

#ifndef unix
#  define NO_ST_INO /* don't rely on inode numbers */
#endif


    /* Common defaults */

#ifndef OS_CODE
#  define OS_CODE  0x03  /* assume Unix */
#endif

#ifndef PATH_SEP
#  define PATH_SEP '/'
#endif

#ifndef casemap
#  define casemap(c) (c)
#endif

#ifndef OPTIONS_VAR
#  define OPTIONS_VAR "GZIP"
#endif

#ifndef Z_SUFFIX
#  define Z_SUFFIX ".gz"
#endif

#ifdef MAX_EXT_CHARS
#  define MAX_SUFFIX  MAX_EXT_CHARS
#else
#  define MAX_SUFFIX  30
#endif

#ifndef MAKE_LEGAL_NAME
#  ifdef NO_MULTIPLE_DOTS
#    define MAKE_LEGAL_NAME(name)   make_simple_name(name)
#  else
#    define MAKE_LEGAL_NAME(name)
#  endif
#endif

#ifndef MIN_PART
#  define MIN_PART 3
   /* keep at least MIN_PART chars between dots in a file name. */
#endif

#ifndef EXPAND
#  define EXPAND(argc,argv)
#endif

#ifndef RECORD_IO
#  define RECORD_IO 0
#endif

#ifndef SET_BINARY_MODE
#  define SET_BINARY_MODE(fd)
#endif

#ifndef OPEN
#  define OPEN(name, flags, mode) open(name, flags, mode)
#endif

#ifndef get_char
#  define get_char() get_byte()
#endif

#ifndef put_char
#  define put_char(c) put_byte(c)
#endif

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/gzip.h

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/gzip.h used in: Step 600, Step 602, Step 606
/* 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>
#if !defined(NO_STRING_H) || defined(STDC_HEADERS)
#  include <string.h>
#  if !defined(STDC_HEADERS) && !defined(NO_MEMORY_H) && !defined(__GNUC__)
#    include <memory.h>
#  endif
#  define memzero(s, n)     memset ((voidp)(s), 0, (n))
#else
#  include <strings.h>
#  define strchr            index 
#  define strrchr           rindex
#  define memcpy(d, s, n)   bcopy((s), (d), (n)) 
#  define memcmp(s1, s2, n) bcmp((s1), (s2), (n)) 
#  define memzero(s, n)     bzero((s), (n))
#endif

#ifndef RETSIGTYPE
#  define RETSIGTYPE void
#endif

#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 long 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 unpack.c */
extern int unpack     OF((int in, int out));

    /* in unlzh.c */
extern int unlzh      OF((int in, int out));

    /* in gzip.c */
RETSIGTYPE 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));

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/crypt.h

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/crypt.h used in: Step 600
/* crypt.h (dummy version) -- do not perform encryption
 * Hardly worth copyrighting :-)
 */

#ifdef CRYPT
#  undef CRYPT      /* dummy version */
#endif

#define RAND_HEAD_LEN  12  /* length of encryption random header */

#define zencode
#define zdecode

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/crypt.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/crypt.c used in: Step 601
/* crypt.c (dummy version) -- do not perform encryption
 * Hardly worth copyrighting :-)
 */
#ifdef RCSID
static char rcsid[] = "$Id: crypt.c,v 0.6 1993/03/22 09:48:47 jloup Exp $";
#endif

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/deflate.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/deflate.c used in: Step 602
/* 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"
#include "lzw.h" /* just for consistency checking */

#ifdef RCSID
static char rcsid[] = "$Id: deflate.c,v 0.15 1993/06/24 10:53:53 jloup 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 near 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 near strstart;      /* start of string to insert */
      unsigned near 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 near 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 near 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 near 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. */
#if defined(MAXSEG_64K) && HASH_BITS == 15
    for (j = 0;  j < HASH_SIZE; j++) head[j] = NIL;
#else
    memzero((char*)head, HASH_SIZE*sizeof(*head));
#endif
    /* 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 */
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/lzw.h

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/lzw.h used in: Step 602
/* lzw.h -- define the lzw functions.
 * 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(OF) && defined(lint)
#  include "gzip.h"
#endif

#ifndef BITS
#  define BITS 16
#endif
#define INIT_BITS 9              /* Initial number of bits per code */

#define LZW_MAGIC  "\037\235"   /* Magic header for lzw files, 1F 9D */

#define BIT_MASK    0x1f /* Mask for 'number of compression bits' */
/* Mask 0x20 is reserved to mean a fourth header byte, and 0x40 is free.
 * It's a pity that old uncompress does not check bit 0x20. That makes
 * extension of the format actually undesirable because old compress
 * would just crash on the new format instead of giving a meaningful
 * error message. It does check the number of bits, but it's more
 * helpful to say "unsupported format, get a new version" than
 * "can only handle 16 bits".
 */

#define BLOCK_MODE  0x80
/* Block compression: if table is full and compression rate is dropping,
 * clear the dictionary.
 */

#define LZW_RESERVED 0x60 /* reserved bits */

#define CLEAR  256       /* flush the dictionary */
#define FIRST  (CLEAR+1) /* first free entry */

extern int maxbits;      /* max bits per code for LZW */
extern int block_mode;   /* block compress mode -C compatible with 2.0 */

extern int lzw    OF((int in, int out));
extern int unlzw  OF((int in, int out));

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/getopt.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/getopt.c used in: Step 603
/* Getopt for GNU.
   NOTE: getopt is now part of the C library, so if you don't know what
   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
   before changing it!

   Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
    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, 675 Mass Ave, Cambridge, MA 02139, USA.  */


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifndef __STDC__
#  ifndef const
#    define const
#  endif
#endif

/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.  */
#ifndef _NO_PROTO
#define _NO_PROTO
#endif

#include <stdio.h>
#include "tailor.h"

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#if defined (_LIBC) || !defined (__GNU_LIBRARY__)


/* This needs to come after some library #include
   to get __GNU_LIBRARY__ defined.  */
#ifdef  __GNU_LIBRARY__
/* Don't include stdlib.h for non-GNU C libraries because some of them
   contain conflicting prototypes for getopt.  */
#include <stdlib.h>
#endif  /* GNU C library.  */

/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
   long-named option.  Because this is not POSIX.2 compliant, it is
   being phased out.  */
/* #define GETOPT_COMPAT */

/* This version of `getopt' appears to the caller like standard Unix `getopt'
   but it behaves differently for the user, since it allows the user
   to intersperse the options with the other arguments.

   As `getopt' works, it permutes the elements of ARGV so that,
   when it is done, all the options precede everything else.  Thus
   all application programs are extended to handle flexible argument order.

   Setting the environment variable POSIXLY_CORRECT disables permutation.
   Then the behavior is completely standard.

   GNU application programs can use a third alternative mode in which
   they can distinguish the relative order of options and other arguments.  */

#include "getopt.h"

/* For communication from `getopt' to the caller.
   When `getopt' finds an option that takes an argument,
   the argument value is returned here.
   Also, when `ordering' is RETURN_IN_ORDER,
   each non-option ARGV-element is returned here.  */

char *optarg = 0;

/* Index in ARGV of the next element to be scanned.
   This is used for communication to and from the caller
   and for communication between successive calls to `getopt'.

   On entry to `getopt', zero means this is the first call; initialize.

   When `getopt' returns EOF, this is the index of the first of the
   non-option elements that the caller should itself scan.

   Otherwise, `optind' communicates from one call to the next
   how much of ARGV has been scanned so far.  */

/* XXX 1003.2 says this must be 1 before any call.  */
int optind = 0;

/* The next char to be scanned in the option-element
   in which the last option character we returned was found.
   This allows us to pick up the scan where we left off.

   If this is zero, or a null string, it means resume the scan
   by advancing to the next ARGV-element.  */

static char *nextchar;

/* Callers store zero here to inhibit the error message
   for unrecognized options.  */

int opterr = 1;

/* Set to an option character which was unrecognized.
   This must be initialized on some systems to avoid linking in the
   system's own getopt implementation.  */

#define BAD_OPTION '\0'
int optopt = BAD_OPTION;

/* Describe how to deal with options that follow non-option ARGV-elements.

   If the caller did not specify anything,
   the default is REQUIRE_ORDER if the environment variable
   POSIXLY_CORRECT is defined, PERMUTE otherwise.

   REQUIRE_ORDER means don't recognize them as options;
   stop option processing when the first non-option is seen.
   This is what Unix does.
   This mode of operation is selected by either setting the environment
   variable POSIXLY_CORRECT, or using `+' as the first character
   of the list of option characters.

   PERMUTE is the default.  We permute the contents of ARGV as we scan,
   so that eventually all the non-options are at the end.  This allows options
   to be given in any order, even with programs that were not written to
   expect this.

   RETURN_IN_ORDER is an option available to programs that were written
   to expect options and other ARGV-elements in any order and that care about
   the ordering of the two.  We describe each non-option ARGV-element
   as if it were the argument of an option with character code 1.
   Using `-' as the first character of the list of option characters
   selects this mode of operation.

   The special argument `--' forces an end of option-scanning regardless
   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
   `--' can cause `getopt' to return EOF with `optind' != ARGC.  */

static enum
{
  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
} ordering;


#ifdef  __GNU_LIBRARY__
/* We want to avoid inclusion of string.h with non-GNU libraries
   because there are many ways it can cause trouble.
   On some systems, it contains special magic macros that don't work
   in GCC.  */
#include <string.h>
#define my_index    strchr
#define my_strlen   strlen
#else

/* Avoid depending on library functions or files
   whose names are inconsistent.  */

#if __STDC__ || defined(PROTO)
extern char *getenv(const char *name);
extern int  strcmp (const char *s1, const char *s2);
extern int  strncmp(const char *s1, const char *s2, int n);

static int my_strlen(const char *s);
static char *my_index (const char *str, int chr);
#else
extern char *getenv ();
#endif

static int
my_strlen (str)
     const char *str;
{
  int n = 0;
  while (*str++)
    n++;
  return n;
}

static char *
my_index (str, chr)
     const char *str;
     int chr;
{
  while (*str)
    {
      if (*str == chr)
    return (char *) str;
      str++;
    }
  return 0;
}

#endif              /* GNU C library.  */


/* Handle permutation of arguments.  */

/* Describe the part of ARGV that contains non-options that have
   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
   `last_nonopt' is the index after the last of them.  */

static int first_nonopt;
static int last_nonopt;

/* Exchange two adjacent subsequences of ARGV.
   One subsequence is elements [first_nonopt,last_nonopt)
   which contains all the non-options that have been skipped so far.
   The other is elements [last_nonopt,optind), which contains all
   the options processed since those non-options were skipped.

   `first_nonopt' and `last_nonopt' are relocated so that they describe
   the new indices of the non-options in ARGV after they are moved.

   To perform the swap, we first reverse the order of all elements. So
   all options now come before all non options, but they are in the
   wrong order. So we put back the options and non options in original
   order by reversing them again. For example:
       original input:      a b c -x -y
       reverse all:         -y -x c b a
       reverse options:     -x -y c b a
       reverse non options: -x -y a b c
*/

#if __STDC__ || defined(PROTO)
static void exchange (char **argv);
#endif

static void
exchange (argv)
     char **argv;
{
  char *temp, **first, **last;

  /* Reverse all the elements [first_nonopt, optind) */
  first = &argv[first_nonopt];
  last  = &argv[optind-1];
  while (first < last) {
    temp = *first; *first = *last; *last = temp; first++; last--;
  }
  /* Put back the options in order */
  first = &argv[first_nonopt];
  first_nonopt += (optind - last_nonopt);
  last  = &argv[first_nonopt - 1];
  while (first < last) {
    temp = *first; *first = *last; *last = temp; first++; last--;
  }

  /* Put back the non options in order */
  first = &argv[first_nonopt];
  last_nonopt = optind;
  last  = &argv[last_nonopt-1];
  while (first < last) {
    temp = *first; *first = *last; *last = temp; first++; last--;
  }
}


/* Scan elements of ARGV (whose length is ARGC) for option characters
   given in OPTSTRING.

   If an element of ARGV starts with '-', and is not exactly "-" or "--",
   then it is an option element.  The characters of this element
   (aside from the initial '-') are option characters.  If `getopt'
   is called repeatedly, it returns successively each of the option characters
   from each of the option elements.

   If `getopt' finds another option character, it returns that character,
   updating `optind' and `nextchar' so that the next call to `getopt' can
   resume the scan with the following option character or ARGV-element.

   If there are no more option characters, `getopt' returns `EOF'.
   Then `optind' is the index in ARGV of the first ARGV-element
   that is not an option.  (The ARGV-elements have been permuted
   so that those that are not options now come last.)

   OPTSTRING is a string containing the legitimate option characters.
   If an option character is seen that is not listed in OPTSTRING,
   return BAD_OPTION after printing an error message.  If you set `opterr' to
   zero, the error message is suppressed but we still return BAD_OPTION.

   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
   so the following text in the same ARGV-element, or the text of the following
   ARGV-element, is returned in `optarg'.  Two colons mean an option that
   wants an optional arg; if there is text in the current ARGV-element,
   it is returned in `optarg', otherwise `optarg' is set to zero.

   If OPTSTRING starts with `-' or `+', it requests different methods of
   handling the non-option ARGV-elements.
   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.

   Long-named options begin with `--' instead of `-'.
   Their names may be abbreviated as long as the abbreviation is unique
   or is an exact match for some defined option.  If they have an
   argument, it follows the option name in the same ARGV-element, separated
   from the option name by a `=', or else the in next ARGV-element.
   When `getopt' finds a long-named option, it returns 0 if that option's
   `flag' field is nonzero, the value of the option's `val' field
   if the `flag' field is zero.

   The elements of ARGV aren't really const, because we permute them.
   But we pretend they're const in the prototype to be compatible
   with other systems.

   LONGOPTS is a vector of `struct option' terminated by an
   element containing a name which is zero.

   LONGIND returns the index in LONGOPT of the long-named option found.
   It is only valid when a long-named option has been found by the most
   recent call.

   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
   long-named options.  */

int
_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
     int argc;
     char *const *argv;
     const char *optstring;
     const struct option *longopts;
     int *longind;
     int long_only;
{
  int option_index;

  optarg = 0;

  /* Initialize the internal data when the first call is made.
     Start processing options with ARGV-element 1 (since ARGV-element 0
     is the program name); the sequence of previously skipped
     non-option ARGV-elements is empty.  */

  if (optind == 0)
    {
      first_nonopt = last_nonopt = optind = 1;

      nextchar = NULL;

      /* Determine how to handle the ordering of options and nonoptions.  */

      if (optstring[0] == '-')
    {
      ordering = RETURN_IN_ORDER;
      ++optstring;
    }
      else if (optstring[0] == '+')
    {
      ordering = REQUIRE_ORDER;
      ++optstring;
    }
      else if (getenv ("POSIXLY_CORRECT") != NULL)
    ordering = REQUIRE_ORDER;
      else
    ordering = PERMUTE;
    }

  if (nextchar == NULL || *nextchar == '\0')
    {
      if (ordering == PERMUTE)
    {
      /* If we have just processed some options following some non-options,
         exchange them so that the options come first.  */

      if (first_nonopt != last_nonopt && last_nonopt != optind)
        exchange ((char **) argv);
      else if (last_nonopt != optind)
        first_nonopt = optind;

      /* Now skip any additional non-options
         and extend the range of non-options previously skipped.  */

      while (optind < argc
         && (argv[optind][0] != '-' || argv[optind][1] == '\0')
#ifdef GETOPT_COMPAT
         && (longopts == NULL
             || argv[optind][0] != '+' || argv[optind][1] == '\0')
#endif              /* GETOPT_COMPAT */
         )
        optind++;
      last_nonopt = optind;
    }

      /* Special ARGV-element `--' means premature end of options.
     Skip it like a null option,
     then exchange with previous non-options as if it were an option,
     then skip everything else like a non-option.  */

      if (optind != argc && !strcmp (argv[optind], "--"))
    {
      optind++;

      if (first_nonopt != last_nonopt && last_nonopt != optind)
        exchange ((char **) argv);
      else if (first_nonopt == last_nonopt)
        first_nonopt = optind;
      last_nonopt = argc;

      optind = argc;
    }

      /* If we have done all the ARGV-elements, stop the scan
     and back over any non-options that we skipped and permuted.  */

      if (optind == argc)
    {
      /* Set the next-arg-index to point at the non-options
         that we previously skipped, so the caller will digest them.  */
      if (first_nonopt != last_nonopt)
        optind = first_nonopt;
      return EOF;
    }

      /* If we have come to a non-option and did not permute it,
     either stop the scan or describe it to the caller and pass it by.  */

      if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
#ifdef GETOPT_COMPAT
      && (longopts == NULL
          || argv[optind][0] != '+' || argv[optind][1] == '\0')
#endif              /* GETOPT_COMPAT */
      )
    {
      if (ordering == REQUIRE_ORDER)
        return EOF;
      optarg = argv[optind++];
      return 1;
    }

      /* We have found another option-ARGV-element.
     Start decoding its characters.  */

      nextchar = (argv[optind] + 1
          + (longopts != NULL && argv[optind][1] == '-'));
    }

  if (longopts != NULL
      && ((argv[optind][0] == '-'
       && (argv[optind][1] == '-' || long_only))
#ifdef GETOPT_COMPAT
      || argv[optind][0] == '+'
#endif              /* GETOPT_COMPAT */
      ))
    {
      const struct option *p;
      char *s = nextchar;
      int exact = 0;
      int ambig = 0;
      const struct option *pfound = NULL;
      int indfound = 0;

      while (*s && *s != '=')
    s++;

      /* Test all options for either exact match or abbreviated matches.  */
      for (p = longopts, option_index = 0; p->name;
       p++, option_index++)
    if (!strncmp (p->name, nextchar, s - nextchar))
      {
        if (s - nextchar == my_strlen (p->name))
          {
        /* Exact match found.  */
        pfound = p;
        indfound = option_index;
        exact = 1;
        break;
          }
        else if (pfound == NULL)
          {
        /* First nonexact match found.  */
        pfound = p;
        indfound = option_index;
          }
        else
          /* Second nonexact match found.  */
          ambig = 1;
      }

      if (ambig && !exact)
    {
      if (opterr)
        fprintf (stderr, "%s: option `%s' is ambiguous\n",
             argv[0], argv[optind]);
      nextchar += my_strlen (nextchar);
      optind++;
      return BAD_OPTION;
    }

      if (pfound != NULL)
    {
      option_index = indfound;
      optind++;
      if (*s)
        {
          /* Don't test has_arg with >, because some C compilers don't
         allow it to be used on enums.  */
          if (pfound->has_arg)
        optarg = s + 1;
          else
        {
          if (opterr)
            {
              if (argv[optind - 1][1] == '-')
            /* --option */
            fprintf (stderr,
                 "%s: option `--%s' doesn't allow an argument\n",
                 argv[0], pfound->name);
              else
            /* +option or -option */
            fprintf (stderr,
                 "%s: option `%c%s' doesn't allow an argument\n",
                 argv[0], argv[optind - 1][0], pfound->name);
            }
          nextchar += my_strlen (nextchar);
          return BAD_OPTION;
        }
        }
      else if (pfound->has_arg == 1)
        {
          if (optind < argc)
        optarg = argv[optind++];
          else
        {
          if (opterr)
            fprintf (stderr, "%s: option `%s' requires an argument\n",
                 argv[0], argv[optind - 1]);
          nextchar += my_strlen (nextchar);
          return optstring[0] == ':' ? ':' : BAD_OPTION;
        }
        }
      nextchar += my_strlen (nextchar);
      if (longind != NULL)
        *longind = option_index;
      if (pfound->flag)
        {
          *(pfound->flag) = pfound->val;
          return 0;
        }
      return pfound->val;
    }
      /* Can't find it as a long option.  If this is not getopt_long_only,
     or the option starts with '--' or is not a valid short
     option, then it's an error.
     Otherwise interpret it as a short option.  */
      if (!long_only || argv[optind][1] == '-'
#ifdef GETOPT_COMPAT
      || argv[optind][0] == '+'
#endif              /* GETOPT_COMPAT */
      || my_index (optstring, *nextchar) == NULL)
    {
      if (opterr)
        {
          if (argv[optind][1] == '-')
        /* --option */
        fprintf (stderr, "%s: unrecognized option `--%s'\n",
             argv[0], nextchar);
          else
        /* +option or -option */
        fprintf (stderr, "%s: unrecognized option `%c%s'\n",
             argv[0], argv[optind][0], nextchar);
        }
      nextchar = (char *) "";
      optind++;
      return BAD_OPTION;
    }
    }

  /* Look at and handle the next option-character.  */

  {
    char c = *nextchar++;
    char *temp = my_index (optstring, c);

    /* Increment `optind' when we start to process its last character.  */
    if (*nextchar == '\0')
      ++optind;

    if (temp == NULL || c == ':')
      {
    if (opterr)
      {
#if 0
        if (c < 040 || c >= 0177)
          fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
               argv[0], c);
        else
          fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
#else
        /* 1003.2 specifies the format of this message.  */
        fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
#endif
      }
    optopt = c;
    return BAD_OPTION;
      }
    if (temp[1] == ':')
      {
    if (temp[2] == ':')
      {
        /* This is an option that accepts an argument optionally.  */
        if (*nextchar != '\0')
          {
        optarg = nextchar;
        optind++;
          }
        else
          optarg = 0;
        nextchar = NULL;
      }
    else
      {
        /* This is an option that requires an argument.  */
        if (*nextchar != '\0')
          {
        optarg = nextchar;
        /* If we end this ARGV-element by taking the rest as an arg,
           we must advance to the next element now.  */
        optind++;
          }
        else if (optind == argc)
          {
        if (opterr)
          {
#if 0
            fprintf (stderr, "%s: option `-%c' requires an argument\n",
                 argv[0], c);
#else
            /* 1003.2 specifies the format of this message.  */
            fprintf (stderr, "%s: option requires an argument -- %c\n",
                 argv[0], c);
#endif
          }
        optopt = c;
        if (optstring[0] == ':')
          c = ':';
        else
          c = BAD_OPTION;
          }
        else
          /* We already incremented `optind' once;
         increment it again when taking next ARGV-elt as argument.  */
          optarg = argv[optind++];
        nextchar = NULL;
      }
      }
    return c;
  }
}

int
getopt (argc, argv, optstring)
     int argc;
     char *const *argv;
     const char *optstring;
{
  return _getopt_internal (argc, argv, optstring,
               (const struct option *) 0,
               (int *) 0,
               0);
}

int
getopt_long (argc, argv, options, long_options, opt_index)
     int argc;
     char *const *argv;
     const char *options;
     const struct option *long_options;
     int *opt_index;
{
  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
}

#endif  /* _LIBC or not __GNU_LIBRARY__.  */


#ifdef TEST

/* Compile with -DTEST to make an executable for use in testing
   the above definition of `getopt'.  */

int
main (argc, argv)
     int argc;
     char **argv;
{
  int c;
  int digit_optind = 0;

  while (1)
    {
      int this_option_optind = optind ? optind : 1;

      c = getopt (argc, argv, "abc:d:0123456789");
      if (c == EOF)
    break;

      switch (c)
    {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (digit_optind != 0 && digit_optind != this_option_optind)
        printf ("digits occur in two different argv-elements.\n");
      digit_optind = this_option_optind;
      printf ("option %c\n", c);
      break;

    case 'a':
      printf ("option a\n");
      break;

    case 'b':
      printf ("option b\n");
      break;

    case 'c':
      printf ("option c with value `%s'\n", optarg);
      break;

    case BAD_OPTION:
      break;

    default:
      printf ("?? getopt returned character code 0%o ??\n", c);
    }
    }

  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
    printf ("%s ", argv[optind++]);
      printf ("\n");
    }

  exit (0);
}

#endif /* TEST */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/inflate.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/inflate.c used in: Step 604
/* 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 0.14 1993/06/10 13:27:04 jloup Exp $";
#endif

#include <sys/types.h>

#include "tailor.h"

#if defined(STDC_HEADERS) || !defined(NO_STDLIB_H)
#  include <stdlib.h>
#endif

#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(), zdecode(cc), 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 ? 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;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/lzw.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/lzw.c used in: Step 605
/* lzw.c -- compress files in LZW format.
 * This is a dummy version avoiding patent problems.
 */

#ifdef RCSID
static char rcsid[] = "$Id: lzw.c,v 0.9 1993/06/10 13:27:31 jloup Exp $";
#endif

#include "tailor.h"
#include "gzip.h"
#include "lzw.h"

static int msg_done = 0;

/* Compress in to out with lzw method. */
int lzw(in, out)
    int in, out;
{
    if (msg_done) return ERROR;
    msg_done = 1;
    fprintf(stderr,"output in compress .Z format not supported\n");
    if (in != out) { /* avoid warnings on unused variables */
        exit_code = ERROR;
    }
    return ERROR;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/trees.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/trees.c used in: Step 606
/* 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 0.12 1993/06/10 13:27:54 jloup 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 near 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 near 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 near 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 near dyn_ltree[HEAP_SIZE];   /* literal and length tree */
local ct_data near dyn_dtree[2*D_CODES+1]; /* distance tree */

local ct_data near 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 near static_dtree[D_CODES];
/* The static distance tree. (Actually a trivial tree since all codes use
 * 5 bits.)
 */

local ct_data near bl_tree[2*BL_CODES+1];
/* Huffman tree for the bit lengths */

typedef struct tree_desc {
    ct_data near *dyn_tree;      /* the dynamic tree */
    ct_data near *static_tree;   /* corresponding static tree or NULL */
    int     near *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 near l_desc =
{dyn_ltree, static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS, 0};

local tree_desc near d_desc =
{dyn_dtree, static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS, 0};

local tree_desc near bl_desc =
{bl_tree, (ct_data near *)0, extra_blbits, 0,      BL_CODES, MAX_BL_BITS, 0};


local ush near bl_count[MAX_BITS+1];
/* number of codes at each bit length for an optimal tree */

local uch near 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 near 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 near 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 near base_length[LENGTH_CODES];
/* First normalized length for each code (0 = MIN_MATCH) */

local int near 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 near 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 near strstart; /* window offset of current string */

/* ===========================================================================
 * Local (static) routines in this file.
 */

local void init_block     OF((void));
local void pqdownheap     OF((ct_data near *tree, int k));
local void gen_bitlen     OF((tree_desc near *desc));
local void gen_codes      OF((ct_data near *tree, int max_code));
local void build_tree     OF((tree_desc near *desc));
local void scan_tree      OF((ct_data near *tree, int max_code));
local void send_tree      OF((ct_data near *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 near *ltree, ct_data near *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 near *)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 near *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 near *desc; /* the tree descriptor */
{
    ct_data near *tree  = desc->dyn_tree;
    int near *extra     = desc->extra_bits;
    int base            = desc->extra_base;
    int max_code        = desc->max_code;
    int max_length      = desc->max_length;
    ct_data near *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 near *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 near *desc; /* the tree descriptor */
{
    ct_data near *tree   = desc->dyn_tree;
    ct_data near *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 near *)desc);

    /* The field len is now set, we can generate the bit codes */
    gen_codes ((ct_data near *)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 near *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 near *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 near *)dyn_ltree, l_desc.max_code);
    scan_tree((ct_data near *)dyn_dtree, d_desc.max_code);

    /* Build the bit length tree: */
    build_tree((tree_desc near *)(&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 near *)dyn_ltree, lcodes-1); /* send the literal tree */
    Tracev((stderr, "\nlit tree: sent %ld", bits_sent));

    send_tree((ct_data near *)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 near *)(&l_desc));
    Tracev((stderr, "\nlit data: dyn %ld, stat %ld", opt_len, static_len));

    build_tree((tree_desc near *)(&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 near *)static_ltree, (ct_data near *)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 near *)dyn_ltree, (ct_data near *)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 near *ltree; /* literal tree */
    ct_data near *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", "");
    }
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/unlzh.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/unlzh.c used in: Step 607
/* unlzh.c -- decompress files in SCO compress -H (LZH) format.
 * The code in this file is directly derived from the public domain 'ar002'
 * written by Haruhiko Okumura.
 */

#ifdef RCSID
static char rcsid[] = "$Id: unlzh.c,v 1.2 1993/06/24 10:59:01 jloup Exp $";
#endif

#include <stdio.h>

#include "tailor.h"
#include "gzip.h"
#include "lzw.h" /* just for consistency checking */

/* decode.c */

local unsigned  decode  OF((unsigned count, uch buffer[]));
local void decode_start OF((void));

/* huf.c */
local void huf_decode_start OF((void));
local unsigned decode_c     OF((void));
local unsigned decode_p     OF((void));
local void read_pt_len      OF((int nn, int nbit, int i_special));
local void read_c_len       OF((void));

/* io.c */
local void fillbuf      OF((int n));
local unsigned getbits  OF((int n));
local void init_getbits OF((void));

/* maketbl.c */

local void make_table OF((int nchar, uch bitlen[],
              int tablebits, ush table[]));


#define DICBIT    13    /* 12(-lh4-) or 13(-lh5-) */
#define DICSIZ ((unsigned) 1 << DICBIT)

#ifndef CHAR_BIT
#  define CHAR_BIT 8
#endif

#ifndef UCHAR_MAX
#  define UCHAR_MAX 255
#endif

#define BITBUFSIZ (CHAR_BIT * 2 * sizeof(char))
/* Do not use CHAR_BIT * sizeof(bitbuf), does not work on machines
 * for which short is not on 16 bits (Cray).
 */

/* encode.c and decode.c */

#define MAXMATCH 256    /* formerly F (not more than UCHAR_MAX + 1) */
#define THRESHOLD  3    /* choose optimal value */

/* huf.c */

#define NC (UCHAR_MAX + MAXMATCH + 2 - THRESHOLD)
    /* alphabet = {0, 1, 2, ..., NC - 1} */
#define CBIT 9  /* $\lfloor \log_2 NC \rfloor + 1$ */
#define CODE_BIT  16  /* codeword length */

#define NP (DICBIT + 1)
#define NT (CODE_BIT + 3)
#define PBIT 4  /* smallest integer such that (1U << PBIT) > NP */
#define TBIT 5  /* smallest integer such that (1U << TBIT) > NT */
#if NT > NP
# define NPT NT
#else
# define NPT NP
#endif

/* local ush left[2 * NC - 1]; */
/* local ush right[2 * NC - 1]; */
#define left  prev
#define right head
#if NC > (1<<(BITS-2))
    error cannot overlay left+right and prev
#endif

/* local uch c_len[NC]; */
#define c_len outbuf
#if NC > OUTBUFSIZ
    error cannot overlay c_len and outbuf
#endif

local uch pt_len[NPT];
local unsigned blocksize;
local ush pt_table[256];

/* local ush c_table[4096]; */
#define c_table d_buf
#if (DIST_BUFSIZE-1) < 4095
    error cannot overlay c_table and d_buf
#endif

/***********************************************************
        io.c -- input/output
***********************************************************/

local ush       bitbuf;
local unsigned  subbitbuf;
local int       bitcount;

local void fillbuf(n)  /* Shift bitbuf n bits left, read n bits */
    int n;
{
    bitbuf <<= n;
    while (n > bitcount) {
    bitbuf |= subbitbuf << (n -= bitcount);
    subbitbuf = (unsigned)try_byte();
    if ((int)subbitbuf == EOF) subbitbuf = 0;
    bitcount = CHAR_BIT;
    }
    bitbuf |= subbitbuf >> (bitcount -= n);
}

local unsigned getbits(n)
    int n;
{
    unsigned x;

    x = bitbuf >> (BITBUFSIZ - n);  fillbuf(n);
    return x;
}

local void init_getbits()
{
    bitbuf = 0;  subbitbuf = 0;  bitcount = 0;
    fillbuf(BITBUFSIZ);
}

/***********************************************************
    maketbl.c -- make table for decoding
***********************************************************/

local void make_table(nchar, bitlen, tablebits, table)
    int nchar;
    uch bitlen[];
    int tablebits;
    ush table[];
{
    ush count[17], weight[17], start[18], *p;
    unsigned i, k, len, ch, jutbits, avail, nextcode, mask;

    for (i = 1; i <= 16; i++) count[i] = 0;
    for (i = 0; i < (unsigned)nchar; i++) count[bitlen[i]]++;

    start[1] = 0;
    for (i = 1; i <= 16; i++)
    start[i + 1] = start[i] + (count[i] << (16 - i));
    if ((start[17] & 0xffff) != 0)
    error("Bad table\n");

    jutbits = 16 - tablebits;
    for (i = 1; i <= (unsigned)tablebits; i++) {
    start[i] >>= jutbits;
    weight[i] = (unsigned) 1 << (tablebits - i);
    }
    while (i <= 16) {
    weight[i] = (unsigned) 1 << (16 - i);
    i++;
    }

    i = start[tablebits + 1] >> jutbits;
    if (i != 0) {
    k = 1 << tablebits;
    while (i != k) table[i++] = 0;
    }

    avail = nchar;
    mask = (unsigned) 1 << (15 - tablebits);
    for (ch = 0; ch < (unsigned)nchar; ch++) {
    if ((len = bitlen[ch]) == 0) continue;
    nextcode = start[len] + weight[len];
    if (len <= (unsigned)tablebits) {
        for (i = start[len]; i < nextcode; i++) table[i] = ch;
    } else {
        k = start[len];
        p = &table[k >> jutbits];
        i = len - tablebits;
        while (i != 0) {
        if (*p == 0) {
            right[avail] = left[avail] = 0;
            *p = avail++;
        }
        if (k & mask) p = &right[*p];
        else          p = &left[*p];
        k <<= 1;  i--;
        }
        *p = ch;
    }
    start[len] = nextcode;
    }
}

/***********************************************************
        huf.c -- static Huffman
***********************************************************/

local void read_pt_len(nn, nbit, i_special)
    int nn;
    int nbit;
    int i_special;
{
    int i, c, n;
    unsigned mask;

    n = getbits(nbit);
    if (n == 0) {
    c = getbits(nbit);
    for (i = 0; i < nn; i++) pt_len[i] = 0;
    for (i = 0; i < 256; i++) pt_table[i] = c;
    } else {
    i = 0;
    while (i < n) {
        c = bitbuf >> (BITBUFSIZ - 3);
        if (c == 7) {
        mask = (unsigned) 1 << (BITBUFSIZ - 1 - 3);
        while (mask & bitbuf) {  mask >>= 1;  c++;  }
        }
        fillbuf((c < 7) ? 3 : c - 3);
        pt_len[i++] = c;
        if (i == i_special) {
        c = getbits(2);
        while (--c >= 0) pt_len[i++] = 0;
        }
    }
    while (i < nn) pt_len[i++] = 0;
    make_table(nn, pt_len, 8, pt_table);
    }
}

local void read_c_len()
{
    int i, c, n;
    unsigned mask;

    n = getbits(CBIT);
    if (n == 0) {
    c = getbits(CBIT);
    for (i = 0; i < NC; i++) c_len[i] = 0;
    for (i = 0; i < 4096; i++) c_table[i] = c;
    } else {
    i = 0;
    while (i < n) {
        c = pt_table[bitbuf >> (BITBUFSIZ - 8)];
        if (c >= NT) {
        mask = (unsigned) 1 << (BITBUFSIZ - 1 - 8);
        do {
            if (bitbuf & mask) c = right[c];
            else               c = left [c];
            mask >>= 1;
        } while (c >= NT);
        }
        fillbuf((int) pt_len[c]);
        if (c <= 2) {
        if      (c == 0) c = 1;
        else if (c == 1) c = getbits(4) + 3;
        else             c = getbits(CBIT) + 20;
        while (--c >= 0) c_len[i++] = 0;
        } else c_len[i++] = c - 2;
    }
    while (i < NC) c_len[i++] = 0;
    make_table(NC, c_len, 12, c_table);
    }
}

local unsigned decode_c()
{
    unsigned j, mask;

    if (blocksize == 0) {
    blocksize = getbits(16);
    if (blocksize == 0) {
        return NC; /* end of file */
    }
    read_pt_len(NT, TBIT, 3);
    read_c_len();
    read_pt_len(NP, PBIT, -1);
    }
    blocksize--;
    j = c_table[bitbuf >> (BITBUFSIZ - 12)];
    if (j >= NC) {
    mask = (unsigned) 1 << (BITBUFSIZ - 1 - 12);
    do {
        if (bitbuf & mask) j = right[j];
        else               j = left [j];
        mask >>= 1;
    } while (j >= NC);
    }
    fillbuf((int) c_len[j]);
    return j;
}

local unsigned decode_p()
{
    unsigned j, mask;

    j = pt_table[bitbuf >> (BITBUFSIZ - 8)];
    if (j >= NP) {
    mask = (unsigned) 1 << (BITBUFSIZ - 1 - 8);
    do {
        if (bitbuf & mask) j = right[j];
        else               j = left [j];
        mask >>= 1;
    } while (j >= NP);
    }
    fillbuf((int) pt_len[j]);
    if (j != 0) j = ((unsigned) 1 << (j - 1)) + getbits((int) (j - 1));
    return j;
}

local void huf_decode_start()
{
    init_getbits();  blocksize = 0;
}

/***********************************************************
        decode.c
***********************************************************/

local int j;    /* remaining bytes to copy */
local int done; /* set at end of input */

local void decode_start()
{
    huf_decode_start();
    j = 0;
    done = 0;
}

/* Decode the input and return the number of decoded bytes put in buffer
 */
local unsigned decode(count, buffer)
    unsigned count;
    uch buffer[];
    /* The calling function must keep the number of
       bytes to be processed.  This function decodes
       either 'count' bytes or 'DICSIZ' bytes, whichever
       is smaller, into the array 'buffer[]' of size
       'DICSIZ' or more.
       Call decode_start() once for each new file
       before calling this function.
     */
{
    local unsigned i;
    unsigned r, c;

    r = 0;
    while (--j >= 0) {
    buffer[r] = buffer[i];
    i = (i + 1) & (DICSIZ - 1);
    if (++r == count) return r;
    }
    for ( ; ; ) {
    c = decode_c();
    if (c == NC) {
        done = 1;
        return r;
    }
    if (c <= UCHAR_MAX) {
        buffer[r] = c;
        if (++r == count) return r;
    } else {
        j = c - (UCHAR_MAX + 1 - THRESHOLD);
        i = (r - decode_p() - 1) & (DICSIZ - 1);
        while (--j >= 0) {
        buffer[r] = buffer[i];
        i = (i + 1) & (DICSIZ - 1);
        if (++r == count) return r;
        }
    }
    }
}


/* ===========================================================================
 * Unlzh in to out. Return OK or ERROR.
 */
int unlzh(in, out)
    int in;
    int out;
{
    unsigned n;
    ifd = in;
    ofd = out;

    decode_start();
    while (!done) {
    n = decode((unsigned) DICSIZ, window);
    if (!test && n > 0) {
        write_buf(out, (char*)window, n);
    }
    }
    return OK;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/unlzw.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/unlzw.c used in: Step 608
/* unlzw.c -- decompress files in LZW format.
 * The code in this file is directly 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.
 *
 * This is a temporary version which will be rewritten in some future version
 * to accommodate in-memory decompression.
 */

#ifdef RCSID
static char rcsid[] = "$Id: unlzw.c,v 0.15 1993/06/10 13:28:35 jloup Exp $";
#endif

#include <sys/types.h>

#include "tailor.h"

#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif
#ifndef NO_FCNTL_H
#  include <fcntl.h>
#endif

#include "gzip.h"
#include "lzw.h"

typedef unsigned char   char_type;
typedef          long   code_int;
typedef unsigned long   count_int;
typedef unsigned short  count_short;
typedef unsigned long   cmp_code_int;

#define MAXCODE(n)  (1L << (n))
    
#ifndef REGISTERS
#   define  REGISTERS   2
#endif
#define REG1    
#define REG2    
#define REG3    
#define REG4    
#define REG5    
#define REG6    
#define REG7    
#define REG8    
#define REG9    
#define REG10
#define REG11   
#define REG12   
#define REG13
#define REG14
#define REG15
#define REG16
#if REGISTERS >= 1
#   undef   REG1
#   define  REG1    register
#endif
#if REGISTERS >= 2
#   undef   REG2
#   define  REG2    register
#endif
#if REGISTERS >= 3
#   undef   REG3
#   define  REG3    register
#endif
#if REGISTERS >= 4
#   undef   REG4
#   define  REG4    register
#endif
#if REGISTERS >= 5
#   undef   REG5
#   define  REG5    register
#endif
#if REGISTERS >= 6
#   undef   REG6
#   define  REG6    register
#endif
#if REGISTERS >= 7
#   undef   REG7
#   define  REG7    register
#endif
#if REGISTERS >= 8
#   undef   REG8
#   define  REG8    register
#endif
#if REGISTERS >= 9
#   undef   REG9
#   define  REG9    register
#endif
#if REGISTERS >= 10
#   undef   REG10
#   define  REG10   register
#endif
#if REGISTERS >= 11
#   undef   REG11
#   define  REG11   register
#endif
#if REGISTERS >= 12
#   undef   REG12
#   define  REG12   register
#endif
#if REGISTERS >= 13
#   undef   REG13
#   define  REG13   register
#endif
#if REGISTERS >= 14
#   undef   REG14
#   define  REG14   register
#endif
#if REGISTERS >= 15
#   undef   REG15
#   define  REG15   register
#endif
#if REGISTERS >= 16
#   undef   REG16
#   define  REG16   register
#endif
    
#ifndef BYTEORDER
#   define  BYTEORDER   0000
#endif
    
#ifndef NOALLIGN
#   define  NOALLIGN    0
#endif


union   bytes {
    long  word;
    struct {
#if BYTEORDER == 4321
    char_type   b1;
    char_type   b2;
    char_type   b3;
    char_type   b4;
#else
#if BYTEORDER == 1234
    char_type   b4;
    char_type   b3;
    char_type   b2;
    char_type   b1;
#else
#   undef   BYTEORDER
    int  dummy;
#endif
#endif
    } bytes;
};

#if BYTEORDER == 4321 && NOALLIGN == 1
#  define input(b,o,c,n,m){ \
     (c) = (*(long *)(&(b)[(o)>>3])>>((o)&0x7))&(m); \
     (o) += (n); \
   }
#else
#  define input(b,o,c,n,m){ \
     REG1 char_type *p = &(b)[(o)>>3]; \
     (c) = ((((long)(p[0]))|((long)(p[1])<<8)| \
     ((long)(p[2])<<16))>>((o)&0x7))&(m); \
     (o) += (n); \
   }
#endif

#ifndef MAXSEG_64K
   /* DECLARE(ush, tab_prefix, (1<<BITS)); -- prefix code */
#  define tab_prefixof(i) tab_prefix[i]
#  define clear_tab_prefixof()  memzero(tab_prefix, 256);
#else
   /* DECLARE(ush, tab_prefix0, (1<<(BITS-1)); -- prefix for even codes */
   /* DECLARE(ush, tab_prefix1, (1<<(BITS-1)); -- prefix for odd  codes */
   ush *tab_prefix[2];
#  define tab_prefixof(i) tab_prefix[(i)&1][(i)>>1]
#  define clear_tab_prefixof()  \
      memzero(tab_prefix0, 128), \
      memzero(tab_prefix1, 128);
#endif
#define de_stack        ((char_type *)(&d_buf[DIST_BUFSIZE-1]))
#define tab_suffixof(i) tab_suffix[i]

int block_mode = BLOCK_MODE; /* block compress mode -C compatible with 2.0 */

/* ============================================================================
 * Decompress in to out.  This routine adapts to the codes in the
 * file building the "string" table on-the-fly; requiring no table to
 * be stored in the compressed file.
 * IN assertions: the buffer inbuf contains already the beginning of
 *   the compressed data, from offsets iptr to insize-1 included.
 *   The magic header has already been checked and skipped.
 *   bytes_in and bytes_out have been initialized.
 */
int unlzw(in, out) 
    int in, out;    /* input and output file descriptors */
{
    REG2   char_type  *stackp;
    REG3   code_int   code;
    REG4   int        finchar;
    REG5   code_int   oldcode;
    REG6   code_int   incode;
    REG7   long       inbits;
    REG8   long       posbits;
    REG9   int        outpos;
/*  REG10  int        insize; (global) */
    REG11  unsigned   bitmask;
    REG12  code_int   free_ent;
    REG13  code_int   maxcode;
    REG14  code_int   maxmaxcode;
    REG15  int        n_bits;
    REG16  int        rsize;
    
#ifdef MAXSEG_64K
    tab_prefix[0] = tab_prefix0;
    tab_prefix[1] = tab_prefix1;
#endif
    maxbits = get_byte();
    block_mode = maxbits & BLOCK_MODE;
    if ((maxbits & LZW_RESERVED) != 0) {
    WARN((stderr, "\n%s: %s: warning, unknown flags 0x%x\n",
          progname, ifname, maxbits & LZW_RESERVED));
    }
    maxbits &= BIT_MASK;
    maxmaxcode = MAXCODE(maxbits);
    
    if (maxbits > BITS) {
    fprintf(stderr,
        "\n%s: %s: compressed with %d bits, can only handle %d bits\n",
        progname, ifname, maxbits, BITS);
    exit_code = ERROR;
    return ERROR;
    }
    rsize = insize;
    maxcode = MAXCODE(n_bits = INIT_BITS)-1;
    bitmask = (1<<n_bits)-1;
    oldcode = -1;
    finchar = 0;
    outpos = 0;
    posbits = inptr<<3;

    free_ent = ((block_mode) ? FIRST : 256);
    
    clear_tab_prefixof(); /* Initialize the first 256 entries in the table. */
    
    for (code = 255 ; code >= 0 ; --code) {
    tab_suffixof(code) = (char_type)code;
    }
    do {
    REG1 int i;
    int  e;
    int  o;
    
    resetbuf:
    e = insize-(o = (posbits>>3));
    
    for (i = 0 ; i < e ; ++i) {
        inbuf[i] = inbuf[i+o];
    }
    insize = e;
    posbits = 0;
    
    if (insize < INBUF_EXTRA) {
        if ((rsize = read(in, (char*)inbuf+insize, INBUFSIZ)) == EOF) {
        read_error();
        }
        insize += rsize;
        bytes_in += (ulg)rsize;
    }
    inbits = ((rsize != 0) ? ((long)insize - insize%n_bits)<<3 : 
          ((long)insize<<3)-(n_bits-1));
    
    while (inbits > posbits) {
        if (free_ent > maxcode) {
        posbits = ((posbits-1) +
               ((n_bits<<3)-(posbits-1+(n_bits<<3))%(n_bits<<3)));
        ++n_bits;
        if (n_bits == maxbits) {
            maxcode = maxmaxcode;
        } else {
            maxcode = MAXCODE(n_bits)-1;
        }
        bitmask = (1<<n_bits)-1;
        goto resetbuf;
        }
        input(inbuf,posbits,code,n_bits,bitmask);
        Tracev((stderr, "%d ", code));

        if (oldcode == -1) {
        if (code >= 256) error("corrupt input.");
        outbuf[outpos++] = (char_type)(finchar = (int)(oldcode=code));
        continue;
        }
        if (code == CLEAR && block_mode) {
        clear_tab_prefixof();
        free_ent = FIRST - 1;
        posbits = ((posbits-1) +
               ((n_bits<<3)-(posbits-1+(n_bits<<3))%(n_bits<<3)));
        maxcode = MAXCODE(n_bits = INIT_BITS)-1;
        bitmask = (1<<n_bits)-1;
        goto resetbuf;
        }
        incode = code;
        stackp = de_stack;
        
        if (code >= free_ent) { /* Special case for KwKwK string. */
        if (code > free_ent) {
#ifdef DEBUG            
            char_type *p;

            posbits -= n_bits;
            p = &inbuf[posbits>>3];
            fprintf(stderr,
                "code:%ld free_ent:%ld n_bits:%d insize:%u\n",
                code, free_ent, n_bits, insize);
            fprintf(stderr,
                "posbits:%ld inbuf:%02X %02X %02X %02X %02X\n",
                posbits, p[-1],p[0],p[1],p[2],p[3]);
#endif
            if (!test && outpos > 0) {
            write_buf(out, (char*)outbuf, outpos);
            bytes_out += (ulg)outpos;
            }
            error(to_stdout ? "corrupt input." :
              "corrupt input. Use zcat to recover some data.");
        }
        *--stackp = (char_type)finchar;
        code = oldcode;
        }

        while ((cmp_code_int)code >= (cmp_code_int)256) {
        /* Generate output characters in reverse order */
        *--stackp = tab_suffixof(code);
        code = tab_prefixof(code);
        }
        *--stackp = (char_type)(finchar = tab_suffixof(code));
        
        /* And put them out in forward order */
        {
        REG1 int    i;
        
        if (outpos+(i = (de_stack-stackp)) >= OUTBUFSIZ) {
            do {
            if (i > OUTBUFSIZ-outpos) i = OUTBUFSIZ-outpos;

            if (i > 0) {
                memcpy(outbuf+outpos, stackp, i);
                outpos += i;
            }
            if (outpos >= OUTBUFSIZ) {
                if (!test) {
                write_buf(out, (char*)outbuf, outpos);
                bytes_out += (ulg)outpos;
                }
                outpos = 0;
            }
            stackp+= i;
            } while ((i = (de_stack-stackp)) > 0);
        } else {
            memcpy(outbuf+outpos, stackp, i);
            outpos += i;
        }
        }

        if ((code = free_ent) < maxmaxcode) { /* Generate the new entry. */

        tab_prefixof(code) = (unsigned short)oldcode;
        tab_suffixof(code) = (char_type)finchar;
        free_ent = code+1;
        } 
        oldcode = incode;   /* Remember previous code.  */
    }
    } while (rsize != 0);
    
    if (!test && outpos > 0) {
    write_buf(out, (char*)outbuf, outpos);
    bytes_out += (ulg)outpos;
    }
    return OK;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/unpack.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/unpack.c used in: Step 609
/* unpack.c -- decompress files in pack 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: unpack.c,v 1.4 1993/06/11 19:25:36 jloup Exp $";
#endif

#include "tailor.h"
#include "gzip.h"
#include "crypt.h"

#define MIN(a,b) ((a) <= (b) ? (a) : (b))
/* The arguments must not have side effects. */

#define MAX_BITLEN 25
/* Maximum length of Huffman codes. (Minor modifications to the code
 * would be needed to support 32 bits codes, but pack never generates
 * more than 24 bits anyway.)
 */

#define LITERALS 256
/* Number of literals, excluding the End of Block (EOB) code */

#define MAX_PEEK 12
/* Maximum number of 'peek' bits used to optimize traversal of the
 * Huffman tree.
 */

local ulg orig_len;       /* original uncompressed length */
local int max_len;        /* maximum bit length of Huffman codes */

local uch literal[LITERALS];
/* The literal bytes present in the Huffman tree. The EOB code is not
 * represented.
 */

local int lit_base[MAX_BITLEN+1];
/* All literals of a given bit length are contiguous in literal[] and
 * have contiguous codes. literal[code+lit_base[len]] is the literal
 * for a code of len bits.
 */

local int leaves [MAX_BITLEN+1]; /* Number of leaves for each bit length */
local int parents[MAX_BITLEN+1]; /* Number of parents for each bit length */

local int peek_bits; /* Number of peek bits currently used */

/* local uch prefix_len[1 << MAX_PEEK]; */
#define prefix_len outbuf
/* For each bit pattern b of peek_bits bits, prefix_len[b] is the length
 * of the Huffman code starting with a prefix of b (upper bits), or 0
 * if all codes of prefix b have more than peek_bits bits. It is not
 * necessary to have a huge table (large MAX_PEEK) because most of the
 * codes encountered in the input stream are short codes (by construction).
 * So for most codes a single lookup will be necessary.
 */
#if (1<<MAX_PEEK) > OUTBUFSIZ
    error cannot overlay prefix_len and outbuf
#endif

local ulg bitbuf;
/* Bits are added on the low part of bitbuf and read from the high part. */

local int valid;                  /* number of valid bits in bitbuf */
/* all bits above the last valid bit are always zero */

/* Set code to the next 'bits' input bits without skipping them. code
 * must be the name of a simple variable and bits must not have side effects.
 * IN assertions: bits <= 25 (so that we still have room for an extra byte
 * when valid is only 24), and mask = (1<<bits)-1.
 */
#define look_bits(code,bits,mask) \
{ \
  while (valid < (bits)) bitbuf = (bitbuf<<8) | (ulg)get_byte(), valid += 8; \
  code = (bitbuf >> (valid-(bits))) & (mask); \
}

/* Skip the given number of bits (after having peeked at them): */
#define skip_bits(bits)  (valid -= (bits))

#define clear_bitbuf() (valid = 0, bitbuf = 0)

/* Local functions */

local void read_tree  OF((void));
local void build_tree OF((void));

/* ===========================================================================
 * Read the Huffman tree.
 */
local void read_tree()
{
    int len;  /* bit length */
    int base; /* base offset for a sequence of leaves */
    int n;

    /* Read the original input size, MSB first */
    orig_len = 0;
    for (n = 1; n <= 4; n++) orig_len = (orig_len << 8) | (ulg)get_byte();

    max_len = (int)get_byte(); /* maximum bit length of Huffman codes */
    if (max_len > MAX_BITLEN) {
    error("invalid compressed data -- Huffman code > 32 bits");
    }

    /* Get the number of leaves at each bit length */
    n = 0;
    for (len = 1; len <= max_len; len++) {
    leaves[len] = (int)get_byte();
    n += leaves[len];
    }
    if (n > LITERALS) {
    error("too many leaves in Huffman tree");
    }
    Trace((stderr, "orig_len %ld, max_len %d, leaves %d\n",
       orig_len, max_len, n));
    /* There are at least 2 and at most 256 leaves of length max_len.
     * (Pack arbitrarily rejects empty files and files consisting of
     * a single byte even repeated.) To fit the last leaf count in a
     * byte, it is offset by 2. However, the last literal is the EOB
     * code, and is not transmitted explicitly in the tree, so we must
     * adjust here by one only.
     */
    leaves[max_len]++;

    /* Now read the leaves themselves */
    base = 0;
    for (len = 1; len <= max_len; len++) {
    /* Remember where the literals of this length start in literal[] : */
    lit_base[len] = base;
    /* And read the literals: */
    for (n = leaves[len]; n > 0; n--) {
        literal[base++] = (uch)get_byte();
    }
    }
    leaves[max_len]++; /* Now include the EOB code in the Huffman tree */
}

/* ===========================================================================
 * Build the Huffman tree and the prefix table.
 */
local void build_tree()
{
    int nodes = 0; /* number of nodes (parents+leaves) at current bit length */
    int len;       /* current bit length */
    uch *prefixp;  /* pointer in prefix_len */

    for (len = max_len; len >= 1; len--) {
    /* The number of parent nodes at this level is half the total
     * number of nodes at parent level:
     */
    nodes >>= 1;
    parents[len] = nodes;
    /* Update lit_base by the appropriate bias to skip the parent nodes
     * (which are not represented in the literal array):
     */
    lit_base[len] -= nodes;
    /* Restore nodes to be parents+leaves: */
    nodes += leaves[len];
    }
    /* Construct the prefix table, from shortest leaves to longest ones.
     * The shortest code is all ones, so we start at the end of the table.
     */
    peek_bits = MIN(max_len, MAX_PEEK);
    prefixp = &prefix_len[1<<peek_bits];
    for (len = 1; len <= peek_bits; len++) {
    int prefixes = leaves[len] << (peek_bits-len); /* may be 0 */
    while (prefixes--) *--prefixp = (uch)len;
    }
    /* The length of all other codes is unknown: */
    while (prefixp > prefix_len) *--prefixp = 0;
}

/* ===========================================================================
 * Unpack in to out.  This routine does not support the old pack format
 * with magic header \037\037.
 *
 * 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 unpack(in, out)
    int in, out;            /* input and output file descriptors */
{
    int len;                /* Bit length of current code */
    unsigned eob;           /* End Of Block code */
    register unsigned peek; /* lookahead bits */
    unsigned peek_mask;     /* Mask for peek_bits bits */

    ifd = in;
    ofd = out;

    read_tree();     /* Read the Huffman tree */
    build_tree();    /* Build the prefix table */
    clear_bitbuf();  /* Initialize bit input */
    peek_mask = (1<<peek_bits)-1;

    /* The eob code is the largest code among all leaves of maximal length: */
    eob = leaves[max_len]-1;
    Trace((stderr, "eob %d %x\n", max_len, eob));

    /* Decode the input data: */
    for (;;) {
    /* Since eob is the longest code and not shorter than max_len,
         * we can peek at max_len bits without having the risk of reading
         * beyond the end of file.
     */
    look_bits(peek, peek_bits, peek_mask);
    len = prefix_len[peek];
    if (len > 0) {
        peek >>= peek_bits - len; /* discard the extra bits */
    } else {
        /* Code of more than peek_bits bits, we must traverse the tree */
        ulg mask = peek_mask;
        len = peek_bits;
        do {
                len++, mask = (mask<<1)+1;
        look_bits(peek, len, mask);
        } while (peek < (unsigned)parents[len]);
        /* loop as long as peek is a parent node */
    }
    /* At this point, peek is the next complete code, of len bits */
    if (peek == eob && len == max_len) break; /* end of file? */
    put_ubyte(literal[peek+lit_base[len]]);
    Tracev((stderr,"%02d %04x %c\n", len, peek,
        literal[peek+lit_base[len]]));
    skip_bits(len);
    } /* for (;;) */

    flush_window();
    Trace((stderr, "bytes_out %ld\n", bytes_out));
    if (orig_len != (ulg)bytes_out) {
    error("invalid compressed data--length error");
    }
    return OK;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/unzip.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/unzip.c used in: Step 610
/* 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 or pkzip format.
   For the latter, only the first entry is extracted, and it has to be
   either deflated or stored.
 */

#ifdef RCSID
static char rcsid[] = "$Id: unzip.c,v 0.13 1993/06/10 13:29:00 jloup Exp $";
#endif

#include "tailor.h"
#include "gzip.h"
#include "crypt.h"

/* PKZIP header definitions */
#define LOCSIG 0x04034b50L      /* four-byte lead-in (lsb first) */
#define LOCFLG 6                /* offset of bit flag */
#define  CRPFLG 1               /*  bit for encrypted entry */
#define  EXTFLG 8               /*  bit for extended local header */
#define LOCHOW 8                /* offset of compression method */
#define LOCTIM 10               /* file mod time (for decryption) */
#define LOCCRC 14               /* offset of crc */
#define LOCSIZ 18               /* offset of compressed size */
#define LOCLEN 22               /* offset of uncompressed length */
#define LOCFIL 26               /* offset of file name field length */
#define LOCEXT 28               /* offset of extra field length */
#define LOCHDR 30               /* size of local header, including sig */
#define EXTHDR 16               /* size of extended local header, inc sig */


/* Globals */

int decrypt;        /* flag to turn on decryption */
char *key;          /* not used--needed to link crypt.c */
int pkzip = 0;      /* set for a pkzip file */
int ext_header = 0; /* set if extended local header */

/* ===========================================================================
 * Check zip file and advance inptr to the start of the compressed data.
 * Get ofname from the local header if necessary.
 */
int check_zipfile(in)
    int in;   /* input file descriptors */
{
    uch *h = inbuf + inptr; /* first local header */

    ifd = in;

    /* Check validity of local header, and skip name and extra fields */
    inptr += LOCHDR + SH(h + LOCFIL) + SH(h + LOCEXT);

    if (inptr > insize || LG(h) != LOCSIG) {
    fprintf(stderr, "\n%s: %s: not a valid zip file\n",
        progname, ifname);
    exit_code = ERROR;
    return ERROR;
    }
    method = h[LOCHOW];
    if (method != STORED && method != DEFLATED) {
    fprintf(stderr,
        "\n%s: %s: first entry not deflated or stored -- use unzip\n",
        progname, ifname);
    exit_code = ERROR;
    return ERROR;
    }

    /* If entry encrypted, decrypt and validate encryption header */
    if ((decrypt = h[LOCFLG] & CRPFLG) != 0) {
    fprintf(stderr, "\n%s: %s: encrypted file -- use unzip\n",
        progname, ifname);
    exit_code = ERROR;
    return ERROR;
    }

    /* Save flags for unzip() */
    ext_header = (h[LOCFLG] & EXTFLG) != 0;
    pkzip = 1;

    /* Get ofname and time stamp from local header (to be done) */
    return OK;
}

/* ===========================================================================
 * Unzip in to out.  This routine works on both gzip and pkzip files.
 *
 * 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[EXTHDR];        /* extended local header */

    ifd = in;
    ofd = out;

    updcrc(NULL, 0);           /* initialize crc */

    if (pkzip && !ext_header) {  /* crc and length at the end otherwise */
    orig_crc = LG(inbuf + LOCCRC);
    orig_len = LG(inbuf + LOCLEN);
    }

    /* 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 if (pkzip && method == STORED) {

    register ulg n = LG(inbuf + LOCLEN);

    if (n != LG(inbuf + LOCSIZ) - (decrypt ? RAND_HEAD_LEN : 0)) {

        fprintf(stderr, "len %ld, siz %ld\n", n, LG(inbuf + LOCSIZ));
        error("invalid compressed data--length mismatch");
    }
    while (n--) {
        uch c = (uch)get_byte();
#ifdef CRYPT
        if (decrypt) zdecode(c);
#endif
        put_ubyte(c);
    }
    flush_window();
    } else {
    error("internal error, invalid method");
    }

    /* Get the crc and original length */
    if (!pkzip) {
        /* 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);

    } else if (ext_header) {  /* If extended header, check it */
    /* signature - 4bytes: 0x50 0x4b 0x07 0x08
     * CRC-32 value
         * compressed size 4-bytes
         * uncompressed size 4-bytes
     */
    for (n = 0; n < EXTHDR; n++) {
        buf[n] = (uch)get_byte(); /* may cause an error if EOF */
    }
    orig_crc = LG(buf+4);
    orig_len = LG(buf+12);
    }

    /* 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");
    }

    /* Check if there are more entries in a pkzip file */
    if (pkzip && inptr + 4 < insize && LG(inbuf+inptr) == LOCSIG) {
    if (to_stdout) {
        WARN((stderr,
          "%s: %s has more than one entry--rest ignored\n",
          progname, ifname));
    } else {
        /* Don't destroy the input zip file */
        fprintf(stderr,
            "%s: %s has more than one entry -- unchanged\n",
            progname, ifname);
        exit_code = ERROR;
        ext_header = pkzip = 0;
        return ERROR;
    }
    }
    ext_header = pkzip = 0; /* for next file */
    return OK;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/build/gzip-1.2.4/zip.c

File: ../git/live-bootstrap/sysa/gzip-1.2.4/build/gzip-1.2.4/zip.c used in: Step 612
/* 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 0.17 1993/06/10 13:29:25 jloup Exp $";
#endif

#include <ctype.h>
#include <sys/types.h>

#include "tailor.h"
#include "gzip.h"
#include "crypt.h"

#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif
#ifndef NO_FCNTL_H
#  include <fcntl.h>
#endif

local ulg crc;       /* crc on uncompressed file data */
long header_bytes;   /* number of bytes in gzip header */

/* ===========================================================================
 * 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;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/gzip-1.2.4/gzip-1.2.4.checksums

File: ../git/live-bootstrap/sysa/gzip-1.2.4/gzip-1.2.4.checksums used in: Step 619
6608aee8c4a8fdb0945e8b7b0a6f9fe3ca5336394bbcbc564879971e0129a6f1  /usr/bin/gzip

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tar-1.12/sources

File: ../git/live-bootstrap/sysa/tar-1.12/sources used in: Step 621
https://mirrors.kernel.org/gnu/tar/tar-1.12.tar.gz c6c37e888b136ccefab903c51149f4b7bd659d69d4aea21245f61053a57aa60a

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tar-1.12/mk/main.mk

File: ../git/live-bootstrap/sysa/tar-1.12/mk/main.mk used in: Step 628
# SPDX-FileCopyrightText: 2022 Andrius Å tikonas <andrius@stikonas.eu>
#
# SPDX-License-Identifier: GPL-3.0-or-later

CC = tcc
AR = tcc -ar

# -DSIZEOF_UNSIGNED_LONG=4 forces use of simulated arithmetic
# This is to avoid running configure test to determine sizeof(long long)
CPPFLAGS = -DHAVE_FCNTL_H \
         -DHAVE_DIRENT_H \
         -DHAVE_GETCWD_H \
         -DHAVE_GETCWD \
         -DSIZEOF_UNSIGNED_LONG=4 \
         -DVERSION=\"1.12\" \
         -DPACKAGE=\"tar\"

CFLAGS = -I . -I lib
LDFLAGS = -L . -ltar -static

.PHONY: all

LIB_SRC = argmatch backupfile error fnmatch ftruncate getdate_stub getopt getopt1 getversion modechange msleep xgetcwd xmalloc xstrdup

LIB_OBJ = $(addprefix lib/, $(addsuffix .o, $(LIB_SRC)))

TAR_SRC = arith buffer compare create delete extract incremen list mangle misc names open3 rtapelib tar update
TAR_OBJ = $(addprefix src/, $(addsuffix .o, $(TAR_SRC)))

all: tar

libtar.a: $(LIB_OBJ)
    $(AR) cr $@ $^

tar: libtar.a $(TAR_OBJ)
    $(CC) $^ $(LDFLAGS) -o $@

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tar-1.12/files/getdate_stub.c

File: ../git/live-bootstrap/sysa/tar-1.12/files/getdate_stub.c used in: Step 629
/*
 * SPDX-FileCopyrightText: 2021 Paul Dersey <pdersey@gmail.com>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "getdate.h"

time_t get_date (const char *p, const time_t *now)
{
    return 0;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tar-1.12/files/stat_override.c

File: ../git/live-bootstrap/sysa/tar-1.12/files/stat_override.c used in: Step 630
/*
 * SPDX-FileCopyrightText: 2022 fosslinux <fosslinux@aussies.space>
 * SPDX-FileCopyrightText: 2022 Andrius Å tikonas <andrius@stikonas.eu>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include <sys/stat.h>
#include <linux/syscall.h>
#include <linux/x86/syscall.h>

int _lstat(const char *path, struct stat *buf) {
    int rc = lstat(path, buf);
    if (rc == 0) {
        buf->st_atime = 0;
        buf->st_mtime = 0;
    }
    return rc;
}

/* stat is deliberately hacked to be lstat.
   In src/system.h tar already defines lstat to be stat
   since S_ISLNK is not defined in mes C library
   Hence, we can't use something like #define lstat(a,b) _lstat(a,b)
   to have separate stat and lstat functions.
   Thus here we break tar with --dereference option but we don't use
   this option in live-bootstrap.
 */
#define stat(a,b) _lstat(a,b)

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/argmatch.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/argmatch.c used in: Step 633
/* argmatch.c -- find a match for a string in an array
   Copyright (C) 1990 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.  */

/* Written by David MacKenzie <djm@ai.mit.edu> */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>

#include <stdio.h>
#ifdef STDC_HEADERS
# include <string.h>
#endif

extern char *program_name;

/* If ARG is an unambiguous match for an element of the
   null-terminated array OPTLIST, return the index in OPTLIST
   of the matched element, else -1 if it does not match any element
   or -2 if it is ambiguous (is a prefix of more than one element).  */

int
argmatch (arg, optlist)
     const char *arg;
     const char *const *optlist;
{
  int i;            /* Temporary index in OPTLIST.  */
  size_t arglen;        /* Length of ARG.  */
  int matchind = -1;        /* Index of first nonexact match.  */
  int ambiguous = 0;        /* If nonzero, multiple nonexact match(es).  */

  arglen = strlen (arg);

  /* Test all elements for either exact match or abbreviated matches.  */
  for (i = 0; optlist[i]; i++)
    {
      if (!strncmp (optlist[i], arg, arglen))
    {
      if (strlen (optlist[i]) == arglen)
        /* Exact match found.  */
        return i;
      else if (matchind == -1)
        /* First nonexact match found.  */
        matchind = i;
      else
        /* Second nonexact match found.  */
        ambiguous = 1;
    }
    }
  if (ambiguous)
    return -2;
  else
    return matchind;
}

/* Error reporting for argmatch.
   KIND is a description of the type of entity that was being matched.
   VALUE is the invalid value that was given.
   PROBLEM is the return value from argmatch.  */

void
invalid_arg (kind, value, problem)
     const char *kind;
     const char *value;
     int problem;
{
  fprintf (stderr, "%s: ", program_name);
  if (problem == -1)
    fprintf (stderr, "invalid");
  else              /* Assume -2.  */
    fprintf (stderr, "ambiguous");
  fprintf (stderr, " %s `%s'\n", kind, value);
}

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/backupfile.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/backupfile.c used in: Step 634
/* backupfile.c -- make Emacs style backup file names
   Copyright (C) 1990, 1997 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.  */

/* David MacKenzie <djm@gnu.ai.mit.edu>.
   Some algorithms adapted from GNU Emacs.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include "backupfile.h"

#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
# ifndef strrchr
#  define strrchr rindex
# endif
#endif

#if HAVE_DIRENT_H
# include <dirent.h>
# define NLENGTH(Direct) (strlen((Direct)->d_name))
#else
# define dirent direct
# define NLENGTH(Direct) ((Direct)->d_namlen)
# if HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
# if HAVE_NDIR_H
#  include <ndir.h>
# endif
#endif

#if CLOSEDIR_VOID
/* Fake a return value.  */
# define CLOSEDIR(d) (closedir (d), 0)
#else
# define CLOSEDIR(d) closedir (d)
#endif

#if STDC_HEADERS
# include <stdlib.h>
#else
char *malloc ();
#endif

#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
# define IN_CTYPE_DOMAIN(Char) 1
#else
# define IN_CTYPE_DOMAIN(Char) isascii(Char)
#endif

#define ISDIGIT(Char) (IN_CTYPE_DOMAIN ((unsigned char) (Char)) \
               && isdigit ((unsigned char) (Char)))

#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#ifdef _POSIX_VERSION
/* POSIX does not require that the d_ino field be present, and some
   systems do not provide it.  */
# define REAL_DIR_ENTRY(Dp) 1
#else
# define REAL_DIR_ENTRY(Dp) ((Dp)->d_ino != 0)
#endif

/* Which type of backup file names are generated.  */
enum backup_type backup_type = none;

/* The extension added to file names to produce a simple (as opposed
   to numbered) backup file name.  */
char *simple_backup_suffix = "~";

static char *concat ();
char *find_backup_file_name ();
static char *make_version_name ();
static int max_backup_version ();
static int version_number ();

/* Return the name of the new backup file for file FILE,
   allocated with malloc.  Return 0 if out of memory.
   FILE must not end with a '/' unless it is the root directory.
   Do not call this function if backup_type == none.  */

char *
find_backup_file_name (file)
     const char *file;
{
  char *copy;
  char *base;
  const char *dir;
  char *base_versions;
  int highest_backup;

  if (backup_type == simple)
    return concat (file, simple_backup_suffix);

  copy = malloc (strlen (file) + 1);
  if (copy == 0)
    return 0;
  strcpy (copy, file);

  base = strrchr (copy, '/');
  if (base == 0)
    {
      base = copy;
      dir = ".";
    }
  else
    {
      *base++ = '\0';
      dir = copy;
    }

  base_versions = concat (base, ".~");
  if (base_versions == 0)
    {
      free (copy);
      return 0;
    }

  highest_backup = max_backup_version (base_versions, dir);
  free (copy);
  free (base_versions);

  if (backup_type == numbered_existing && highest_backup == 0)
    return concat (file, simple_backup_suffix);

  return make_version_name (file, highest_backup + 1);
}

/* Return the number of the highest-numbered backup file for file
   FILE in directory DIR.  If there are no numbered backups
   of FILE in DIR, or an error occurs reading DIR, return 0.
   FILE should already have ".~" appended to it.  */

static int
max_backup_version (file, dir)
     const char *file;
     const char *dir;
{
  DIR *dirp;
  struct dirent *dp;
  int highest_version;
  int this_version;
  size_t file_name_length;

  dirp = opendir (dir);
  if (!dirp)
    return 0;

  highest_version = 0;
  file_name_length = strlen (file);

  while ((dp = readdir (dirp)) != 0)
    {
      if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) <= file_name_length)
    continue;

      this_version = version_number (file, dp->d_name, file_name_length);
      if (this_version > highest_version)
    highest_version = this_version;
    }
  if (CLOSEDIR (dirp))
    return 0;
  return highest_version;
}

/* Return a string, allocated with malloc, containing
   "FILE.~VERSION~".  Return 0 if out of memory.  */

static char *
make_version_name (file, version)
     const char *file;
     int version;
{
  char *backup_name;

  backup_name = malloc (strlen (file) + 16);
  if (backup_name == 0)
    return 0;
  sprintf (backup_name, "%s.~%d~", file, version);
  return backup_name;
}

/* If BACKUP is a numbered backup of BASE, return its version number;
   otherwise return 0.  BASE_LENGTH is the length of BASE.
   BASE should already have ".~" appended to it.  */

static int
version_number (base, backup, base_length)
     const char *base;
     const char *backup;
     int base_length;
{
  int version;
  const char *p;

  version = 0;
  if (!strncmp (base, backup, base_length) && ISDIGIT (backup[base_length]))
    {
      for (p = &backup[base_length]; ISDIGIT (*p); ++p)
    version = version * 10 + *p - '0';
      if (p[0] != '~' || p[1])
    version = 0;
    }
  return version;
}

/* Return the newly-allocated concatenation of STR1 and STR2.
   If out of memory, return 0.  */

static char *
concat (str1, str2)
     const char *str1;
     const char *str2;
{
  char *newstr;
  int str1_length = strlen (str1);

  newstr = malloc (str1_length + strlen (str2) + 1);
  if (newstr == 0)
    return 0;
  strcpy (newstr, str1);
  strcpy (newstr + str1_length, str2);
  return newstr;
}

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/error.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/error.c used in: Step 635
/* error.c -- error handler for noninteractive utilities
   Copyright (C) 1990, 91, 92, 93, 94, 95, 96 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.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>

#if HAVE_VPRINTF || HAVE_DOPRNT || _LIBC
# if __STDC__
#  include <stdarg.h>
#  define VA_START(args, lastarg) va_start(args, lastarg)
# else
#  include <varargs.h>
#  define VA_START(args, lastarg) va_start(args)
# endif
#else
# define va_alist a1, a2, a3, a4, a5, a6, a7, a8
# define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
#endif

#if STDC_HEADERS || _LIBC
# include <stdlib.h>
# include <string.h>
#else
void exit ();
#endif

#ifndef _
# define _(String) String
#endif

/* Get prototypes for the functions defined here.  */
#include <error.h>

/* If NULL, error will flush stdout, then print on stderr the program
   name, a colon and a space.  Otherwise, error will call this
   function without parameters instead.  */
void (*error_print_progname) (
#if __STDC__ - 0
                  void
#endif
                  );

/* This variable is incremented each time `error' is called.  */
unsigned int error_message_count;

#ifdef _LIBC
/* In the GNU C library, there is a predefined variable for this.  */

# define program_name program_invocation_name
# include <errno.h>

#else /* not _LIBC */

/* The calling program should define program_name and set it to the
   name of the executing program.  */
extern char *program_name;

# if HAVE_STRERROR
#  ifndef strerror      /* On some systems, strerror is a macro */
char *strerror ();
#  endif
# else
static char *
private_strerror (errnum)
     int errnum;
{
  extern char *sys_errlist[];
  extern int sys_nerr;

  if (errnum > 0 && errnum <= sys_nerr)
    return sys_errlist[errnum];
  return _("Unknown system error");
}
#  define strerror private_strerror
# endif /* HAVE_STRERROR */

#endif  /* not _LIBC */

/* Print the program name and error message MESSAGE, which is a printf-style
   format string with optional args.
   If ERRNUM is nonzero, print its corresponding system error message.
   Exit with status STATUS if it is nonzero.  */
/* VARARGS */

void
#if defined(VA_START) && __STDC__
error (int status, int errnum, const char *message, ...)
#else
error (status, errnum, message, va_alist)
     int status;
     int errnum;
     char *message;
     va_dcl
#endif
{
#ifdef VA_START
  va_list args;
#endif

  if (error_print_progname)
    (*error_print_progname) ();
  else
    {
      fflush (stdout);
      fprintf (stderr, "%s: ", program_name);
    }

#ifdef VA_START
  VA_START (args, message);
# if HAVE_VPRINTF || _LIBC
  vfprintf (stderr, message, args);
# else
  _doprnt (message, args, stderr);
# endif
  va_end (args);
#else
  fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
#endif

  ++error_message_count;
  if (errnum)
    fprintf (stderr, ": %s", strerror (errnum));
  putc ('\n', stderr);
  fflush (stderr);
  if (status)
    exit (status);
}


/* Sometimes we want to have at most one error per line.  This
   variable controls whether this mode is selected or not.  */
int error_one_per_line;

void
#if defined(VA_START) && __STDC__
error_at_line (int status, int errnum, const char *file_name,
           unsigned int line_number, const char *message, ...)
#else
error_at_line (status, errnum, file_name, line_number, message, va_alist)
     int status;
     int errnum;
     const char *file_name;
     unsigned int line_number;
     char *message;
     va_dcl
#endif
{
#ifdef VA_START
  va_list args;
#endif

  if (error_one_per_line)
    {
      static const char *old_file_name;
      static unsigned int old_line_number;

      if (old_line_number == line_number &&
      (file_name == old_file_name || !strcmp (old_file_name, file_name)))
    /* Simply return and print nothing.  */
    return;

      old_file_name = file_name;
      old_line_number = line_number;
    }

  if (error_print_progname)
    (*error_print_progname) ();
  else
    {
      fflush (stdout);
      fprintf (stderr, "%s:", program_name);
    }

  if (file_name != NULL)
    fprintf (stderr, "%s:%d: ", file_name, line_number);

#ifdef VA_START
  VA_START (args, message);
# if HAVE_VPRINTF || _LIBC
  vfprintf (stderr, message, args);
# else
  _doprnt (message, args, stderr);
# endif
  va_end (args);
#else
  fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);
#endif

  ++error_message_count;
  if (errnum)
    fprintf (stderr, ": %s", strerror (errnum));
  putc ('\n', stderr);
  fflush (stderr);
  if (status)
    exit (status);
}

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/fnmatch.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/fnmatch.c used in: Step 636
/* Copyright (C) 1991, 1992, 1993, 1996, 1997 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

/* Enable GNU extensions in fnmatch.h.  */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE    1
#endif

#include <errno.h>
#include <fnmatch.h>
#include <ctype.h>


/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#if defined _LIBC || !defined __GNU_LIBRARY__


# if defined STDC_HEADERS || !defined isascii
#  define ISASCII(c) 1
# else
#  define ISASCII(c) isascii(c)
# endif

# define ISUPPER(c) (ISASCII (c) && isupper (c))


# ifndef errno
extern int errno;
# endif

/* Match STRING against the filename pattern PATTERN, returning zero if
   it matches, nonzero if not.  */
int
fnmatch (pattern, string, flags)
     const char *pattern;
     const char *string;
     int flags;
{
  register const char *p = pattern, *n = string;
  register char c;

/* Note that this evaluates C many times.  */
# define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))

  while ((c = *p++) != '\0')
    {
      c = FOLD (c);

      switch (c)
    {
    case '?':
      if (*n == '\0')
        return FNM_NOMATCH;
      else if ((flags & FNM_FILE_NAME) && *n == '/')
        return FNM_NOMATCH;
      else if ((flags & FNM_PERIOD) && *n == '.' &&
           (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
        return FNM_NOMATCH;
      break;

    case '\\':
      if (!(flags & FNM_NOESCAPE))
        {
          c = *p++;
          if (c == '\0')
        /* Trailing \ loses.  */
        return FNM_NOMATCH;
          c = FOLD (c);
        }
      if (FOLD (*n) != c)
        return FNM_NOMATCH;
      break;

    case '*':
      if ((flags & FNM_PERIOD) && *n == '.' &&
          (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
        return FNM_NOMATCH;

      for (c = *p++; c == '?' || c == '*'; c = *p++)
        {
          if ((flags & FNM_FILE_NAME) && *n == '/')
        /* A slash does not match a wildcard under FNM_FILE_NAME.  */
        return FNM_NOMATCH;
          else if (c == '?')
        {
          /* A ? needs to match one character.  */
          if (*n == '\0')
            /* There isn't another character; no match.  */
            return FNM_NOMATCH;
          else
            /* One character of the string is consumed in matching
               this ? wildcard, so *??? won't match if there are
               less than three characters.  */
            ++n;
        }
        }

      if (c == '\0')
        return 0;

      {
        char c1 = (!(flags & FNM_NOESCAPE) && c == '\\') ? *p : c;
        c1 = FOLD (c1);
        for (--p; *n != '\0'; ++n)
          if ((c == '[' || FOLD (*n) == c1) &&
          fnmatch (p, n, flags & ~FNM_PERIOD) == 0)
        return 0;
        return FNM_NOMATCH;
      }

    case '[':
      {
        /* Nonzero if the sense of the character class is inverted.  */
        register int not;

        if (*n == '\0')
          return FNM_NOMATCH;

        if ((flags & FNM_PERIOD) && *n == '.' &&
        (n == string || ((flags & FNM_FILE_NAME) && n[-1] == '/')))
          return FNM_NOMATCH;

        not = (*p == '!' || *p == '^');
        if (not)
          ++p;

        c = *p++;
        for (;;)
          {
        register char cstart = c, cend = c;

        if (!(flags & FNM_NOESCAPE) && c == '\\')
          {
            if (*p == '\0')
              return FNM_NOMATCH;
            cstart = cend = *p++;
          }

        cstart = cend = FOLD (cstart);

        if (c == '\0')
          /* [ (unterminated) loses.  */
          return FNM_NOMATCH;

        c = *p++;
        c = FOLD (c);

        if ((flags & FNM_FILE_NAME) && c == '/')
          /* [/] can never match.  */
          return FNM_NOMATCH;

        if (c == '-' && *p != ']')
          {
            cend = *p++;
            if (!(flags & FNM_NOESCAPE) && cend == '\\')
              cend = *p++;
            if (cend == '\0')
              return FNM_NOMATCH;
            cend = FOLD (cend);

            c = *p++;
          }

        if (FOLD (*n) >= cstart && FOLD (*n) <= cend)
          goto matched;

        if (c == ']')
          break;
          }
        if (!not)
          return FNM_NOMATCH;
        break;

      matched:;
        /* Skip the rest of the [...] that already matched.  */
        while (c != ']')
          {
        if (c == '\0')
          /* [... (unterminated) loses.  */
          return FNM_NOMATCH;

        c = *p++;
        if (!(flags & FNM_NOESCAPE) && c == '\\')
          {
            if (*p == '\0')
              return FNM_NOMATCH;
            /* XXX 1003.2d11 is unclear if this is right.  */
            ++p;
          }
          }
        if (not)
          return FNM_NOMATCH;
      }
      break;

    default:
      if (c != FOLD (*n))
        return FNM_NOMATCH;
    }

      ++n;
    }

  if (*n == '\0')
    return 0;

  if ((flags & FNM_LEADING_DIR) && *n == '/')
    /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */
    return 0;

  return FNM_NOMATCH;

# undef FOLD
}

#endif  /* _LIBC or not __GNU_LIBRARY__.  */

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/ftruncate.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/ftruncate.c used in: Step 637
/* ftruncate emulations that work on some System V's.
   This file is in the public domain.  */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <sys/types.h>
#include <fcntl.h>

#ifdef F_CHSIZE

int
ftruncate (fd, length)
     int fd;
     off_t length;
{
  return fcntl (fd, F_CHSIZE, length);
}

#else /* not F_CHSIZE */
#ifdef F_FREESP

/* By William Kucharski <kucharsk@netcom.com>.  */

#include <sys/stat.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

int
ftruncate (fd, length)
     int fd;
     off_t length;
{
  struct flock fl;
  struct stat filebuf;

  if (fstat (fd, &filebuf) < 0)
    return -1;

  if (filebuf.st_size < length)
    {
      /* Extend file length. */
      if (lseek (fd, (length - 1), SEEK_SET) < 0)
    return -1;

      /* Write a "0" byte. */
      if (write (fd, "", 1) != 1)
    return -1;
    }
  else
    {

      /* Truncate length. */

      fl.l_whence = 0;
      fl.l_len = 0;
      fl.l_start = length;
      fl.l_type = F_WRLCK;  /* write lock on file space */

      /* This relies on the *undocumented* F_FREESP argument to fcntl,
     which truncates the file so that it ends at the position
     indicated by fl.l_start.  Will minor miracles never cease?  */

      if (fcntl (fd, F_FREESP, &fl) < 0)
    return -1;
    }

  return 0;
}

#else /* not F_CHSIZE nor F_FREESP */
#ifdef HAVE_CHSIZE

int
ftruncate (fd, length)
     int fd;
     off_t length;
{
  return chsize (fd, length);
}

#else /* not F_CHSIZE nor F_FREESP nor HAVE_CHSIZE */

#include <errno.h>
#ifndef errno
extern int errno;
#endif

int
ftruncate (fd, length)
     int fd;
     off_t length;
{
  errno = EIO;
  return -1;
}

#endif /* not HAVE_CHSIZE */
#endif /* not F_FREESP */
#endif /* not F_CHSIZE */

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/getdate.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/getdate.h used in: Step 638
/*  Copyright (C) 1995 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.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

# ifndef PARAMS
#  if defined (__GNUC__) || __STDC__
#   define PARAMS(args) args
#  else
#   define PARAMS(args) ()
#  endif
# endif

#if defined (vms)
# include <types.h>
# include <time.h>
#else
# include <sys/types.h>
# ifdef TIME_WITH_SYS_TIME
#  include <sys/time.h>
#  include <time.h>
# else
#  ifdef HAVE_SYS_TIME_H
#   include <sys/time.h>
#  else
#   include <time.h>
#  endif
# endif
#endif /* defined (vms) */

time_t get_date PARAMS ((const char *p, const time_t *now));

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/getopt.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/getopt.c used in: Step 639
/* Getopt for GNU.
   NOTE: getopt is now part of the C library, so if you don't know what
   "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
   before changing it!

   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
   Free Software Foundation, Inc.

   This file is part of the GNU C Library.  Its master source is NOT part of
   the C library, however.  The master source lives in /gd/gnu/lib.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */


/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
   Ditto for AIX 3.2 and <stdlib.h>.  */
#ifndef _NO_PROTO
#define _NO_PROTO
#endif

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#if !defined (__STDC__) || !__STDC__
/* This is a separate conditional since some stdc systems
   reject `defined (const)'.  */
#ifndef const
#define const
#endif
#endif

#include <stdio.h>

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#define GETOPT_INTERFACE_VERSION 2
#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
#include <gnu-versions.h>
#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
#define ELIDE_CODE
#endif
#endif

#ifndef ELIDE_CODE


/* This needs to come after some library #include
   to get __GNU_LIBRARY__ defined.  */
#ifdef  __GNU_LIBRARY__
/* Don't include stdlib.h for non-GNU C libraries because some of them
   contain conflicting prototypes for getopt.  */
#include <stdlib.h>
#include <unistd.h>
#endif  /* GNU C library.  */

#ifdef VMS
#include <unixlib.h>
#if HAVE_STRING_H - 0
#include <string.h>
#endif
#endif

#if defined (WIN32) && !defined (__CYGWIN32__)
/* It's not Unix, really.  See?  Capital letters.  */
#include <windows.h>
#define getpid() GetCurrentProcessId()
#endif

#ifndef _
/* This is for other GNU distributions with internationalized messages.
   When compiling libc, the _ macro is predefined.  */
#ifdef HAVE_LIBINTL_H
# include <libintl.h>
# define _(msgid)   gettext (msgid)
#else
# define _(msgid)   (msgid)
#endif
#endif

/* This version of `getopt' appears to the caller like standard Unix `getopt'
   but it behaves differently for the user, since it allows the user
   to intersperse the options with the other arguments.

   As `getopt' works, it permutes the elements of ARGV so that,
   when it is done, all the options precede everything else.  Thus
   all application programs are extended to handle flexible argument order.

   Setting the environment variable POSIXLY_CORRECT disables permutation.
   Then the behavior is completely standard.

   GNU application programs can use a third alternative mode in which
   they can distinguish the relative order of options and other arguments.  */

#include "getopt.h"

/* For communication from `getopt' to the caller.
   When `getopt' finds an option that takes an argument,
   the argument value is returned here.
   Also, when `ordering' is RETURN_IN_ORDER,
   each non-option ARGV-element is returned here.  */

char *optarg = NULL;

/* Index in ARGV of the next element to be scanned.
   This is used for communication to and from the caller
   and for communication between successive calls to `getopt'.

   On entry to `getopt', zero means this is the first call; initialize.

   When `getopt' returns -1, this is the index of the first of the
   non-option elements that the caller should itself scan.

   Otherwise, `optind' communicates from one call to the next
   how much of ARGV has been scanned so far.  */

/* 1003.2 says this must be 1 before any call.  */
int optind = 1;

/* Formerly, initialization of getopt depended on optind==0, which
   causes problems with re-calling getopt as programs generally don't
   know that. */

int __getopt_initialized = 0;

/* The next char to be scanned in the option-element
   in which the last option character we returned was found.
   This allows us to pick up the scan where we left off.

   If this is zero, or a null string, it means resume the scan
   by advancing to the next ARGV-element.  */

static char *nextchar;

/* Callers store zero here to inhibit the error message
   for unrecognized options.  */

int opterr = 1;

/* Set to an option character which was unrecognized.
   This must be initialized on some systems to avoid linking in the
   system's own getopt implementation.  */

int optopt = '?';

/* Describe how to deal with options that follow non-option ARGV-elements.

   If the caller did not specify anything,
   the default is REQUIRE_ORDER if the environment variable
   POSIXLY_CORRECT is defined, PERMUTE otherwise.

   REQUIRE_ORDER means don't recognize them as options;
   stop option processing when the first non-option is seen.
   This is what Unix does.
   This mode of operation is selected by either setting the environment
   variable POSIXLY_CORRECT, or using `+' as the first character
   of the list of option characters.

   PERMUTE is the default.  We permute the contents of ARGV as we scan,
   so that eventually all the non-options are at the end.  This allows options
   to be given in any order, even with programs that were not written to
   expect this.

   RETURN_IN_ORDER is an option available to programs that were written
   to expect options and other ARGV-elements in any order and that care about
   the ordering of the two.  We describe each non-option ARGV-element
   as if it were the argument of an option with character code 1.
   Using `-' as the first character of the list of option characters
   selects this mode of operation.

   The special argument `--' forces an end of option-scanning regardless
   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */

static enum
{
  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
} ordering;

/* Value of POSIXLY_CORRECT environment variable.  */
static char *posixly_correct;


#ifdef  __GNU_LIBRARY__
/* We want to avoid inclusion of string.h with non-GNU libraries
   because there are many ways it can cause trouble.
   On some systems, it contains special magic macros that don't work
   in GCC.  */
#include <string.h>
#define my_index    strchr
#else

/* Avoid depending on library functions or files
   whose names are inconsistent.  */

char *getenv ();

static char *
my_index (str, chr)
     const char *str;
     int chr;
{
  while (*str)
    {
      if (*str == chr)
    return (char *) str;
      str++;
    }
  return 0;
}

/* If using GCC, we can safely declare strlen this way.
   If not using GCC, it is ok not to declare it.  */
#ifdef __GNUC__
/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
   That was relevant to code that was here before.  */
#if !defined (__STDC__) || !__STDC__
/* gcc with -traditional declares the built-in strlen to return int,
   and has done so at least since version 2.4.5. -- rms.  */
extern int strlen (const char *);
#endif /* not __STDC__ */
#endif /* __GNUC__ */

#endif /* not __GNU_LIBRARY__ */


/* Handle permutation of arguments.  */

/* Describe the part of ARGV that contains non-options that have
   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
   `last_nonopt' is the index after the last of them.  */

static int first_nonopt;
static int last_nonopt;

#ifdef _LIBC
/* Bash 2.0 gives us an environment variable containing flags
   indicating ARGV elements that should not be considered arguments.  */

static const char *nonoption_flags;
static int nonoption_flags_len;

static int original_argc;
static char *const *original_argv;

/* Make sure the environment variable bash 2.0 puts in the environment
   is valid for the getopt call we must make sure that the ARGV passed
   to getopt is that one passed to the process.  */
static void store_args (int argc, char *const *argv) __attribute__ ((unused));
static void
store_args (int argc, char *const *argv)
{
  /* XXX This is no good solution.  We should rather copy the args so
     that we can compare them later.  But we must not use malloc(3).  */
  original_argc = argc;
  original_argv = argv;
}
text_set_element (__libc_subinit, store_args);
#endif

/* Exchange two adjacent subsequences of ARGV.
   One subsequence is elements [first_nonopt,last_nonopt)
   which contains all the non-options that have been skipped so far.
   The other is elements [last_nonopt,optind), which contains all
   the options processed since those non-options were skipped.

   `first_nonopt' and `last_nonopt' are relocated so that they describe
   the new indices of the non-options in ARGV after they are moved.  */

#if defined (__STDC__) && __STDC__
static void exchange (char **);
#endif

static void
exchange (argv)
     char **argv;
{
  int bottom = first_nonopt;
  int middle = last_nonopt;
  int top = optind;
  char *tem;

  /* Exchange the shorter segment with the far end of the longer segment.
     That puts the shorter segment into the right place.
     It leaves the longer segment in the right place overall,
     but it consists of two parts that need to be swapped next.  */

  while (top > middle && middle > bottom)
    {
      if (top - middle > middle - bottom)
    {
      /* Bottom segment is the short one.  */
      int len = middle - bottom;
      register int i;

      /* Swap it with the top part of the top segment.  */
      for (i = 0; i < len; i++)
        {
          tem = argv[bottom + i];
          argv[bottom + i] = argv[top - (middle - bottom) + i];
          argv[top - (middle - bottom) + i] = tem;
        }
      /* Exclude the moved bottom segment from further swapping.  */
      top -= len;
    }
      else
    {
      /* Top segment is the short one.  */
      int len = top - middle;
      register int i;

      /* Swap it with the bottom part of the bottom segment.  */
      for (i = 0; i < len; i++)
        {
          tem = argv[bottom + i];
          argv[bottom + i] = argv[middle + i];
          argv[middle + i] = tem;
        }
      /* Exclude the moved top segment from further swapping.  */
      bottom += len;
    }
    }

  /* Update records for the slots the non-options now occupy.  */

  first_nonopt += (optind - last_nonopt);
  last_nonopt = optind;
}

/* Initialize the internal data when the first call is made.  */

#if defined (__STDC__) && __STDC__
static const char *_getopt_initialize (int, char *const *, const char *);
#endif
static const char *
_getopt_initialize (argc, argv, optstring)
     int argc;
     char *const *argv;
     const char *optstring;
{
  /* Start processing options with ARGV-element 1 (since ARGV-element 0
     is the program name); the sequence of previously skipped
     non-option ARGV-elements is empty.  */

  first_nonopt = last_nonopt = optind = 1;

  nextchar = NULL;

  posixly_correct = getenv ("POSIXLY_CORRECT");

  /* Determine how to handle the ordering of options and nonoptions.  */

  if (optstring[0] == '-')
    {
      ordering = RETURN_IN_ORDER;
      ++optstring;
    }
  else if (optstring[0] == '+')
    {
      ordering = REQUIRE_ORDER;
      ++optstring;
    }
  else if (posixly_correct != NULL)
    ordering = REQUIRE_ORDER;
  else
    ordering = PERMUTE;

#ifdef _LIBC
  if (posixly_correct == NULL
      && argc == original_argc && argv == original_argv)
    {
      /* Bash 2.0 puts a special variable in the environment for each
     command it runs, specifying which ARGV elements are the results of
     file name wildcard expansion and therefore should not be
     considered as options.  */
      char var[100];
      sprintf (var, "_%d_GNU_nonoption_argv_flags_", getpid ());
      nonoption_flags = getenv (var);
      if (nonoption_flags == NULL)
    nonoption_flags_len = 0;
      else
    nonoption_flags_len = strlen (nonoption_flags);
    }
  else
    nonoption_flags_len = 0;
#endif

  return optstring;
}


/* Scan elements of ARGV (whose length is ARGC) for option characters
   given in OPTSTRING.

   If an element of ARGV starts with '-', and is not exactly "-" or "--",
   then it is an option element.  The characters of this element
   (aside from the initial '-') are option characters.  If `getopt'
   is called repeatedly, it returns successively each of the option characters
   from each of the option elements.

   If `getopt' finds another option character, it returns that character,
   updating `optind' and `nextchar' so that the next call to `getopt' can
   resume the scan with the following option character or ARGV-element.

   If there are no more option characters, `getopt' returns -1.
   Then `optind' is the index in ARGV of the first ARGV-element
   that is not an option.  (The ARGV-elements have been permuted
   so that those that are not options now come last.)

   OPTSTRING is a string containing the legitimate option characters.
   If an option character is seen that is not listed in OPTSTRING,
   return '?' after printing an error message.  If you set `opterr' to
   zero, the error message is suppressed but we still return '?'.

   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
   so the following text in the same ARGV-element, or the text of the following
   ARGV-element, is returned in `optarg'.  Two colons mean an option that
   wants an optional arg; if there is text in the current ARGV-element,
   it is returned in `optarg', otherwise `optarg' is set to zero.

   If OPTSTRING starts with `-' or `+', it requests different methods of
   handling the non-option ARGV-elements.
   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.

   Long-named options begin with `--' instead of `-'.
   Their names may be abbreviated as long as the abbreviation is unique
   or is an exact match for some defined option.  If they have an
   argument, it follows the option name in the same ARGV-element, separated
   from the option name by a `=', or else the in next ARGV-element.
   When `getopt' finds a long-named option, it returns 0 if that option's
   `flag' field is nonzero, the value of the option's `val' field
   if the `flag' field is zero.

   The elements of ARGV aren't really const, because we permute them.
   But we pretend they're const in the prototype to be compatible
   with other systems.

   LONGOPTS is a vector of `struct option' terminated by an
   element containing a name which is zero.

   LONGIND returns the index in LONGOPT of the long-named option found.
   It is only valid when a long-named option has been found by the most
   recent call.

   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
   long-named options.  */

int
_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
     int argc;
     char *const *argv;
     const char *optstring;
     const struct option *longopts;
     int *longind;
     int long_only;
{
  optarg = NULL;

  if (!__getopt_initialized || optind == 0)
    {
      optstring = _getopt_initialize (argc, argv, optstring);
      optind = 1;       /* Don't scan ARGV[0], the program name.  */
      __getopt_initialized = 1;
    }

  /* Test whether ARGV[optind] points to a non-option argument.
     Either it does not have option syntax, or there is an environment flag
     from the shell indicating it is not an option.  The later information
     is only used when the used in the GNU libc.  */
#ifdef _LIBC
#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'        \
             || (optind < nonoption_flags_len                 \
             && nonoption_flags[optind] == '1'))
#else
#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
#endif

  if (nextchar == NULL || *nextchar == '\0')
    {
      /* Advance to the next ARGV-element.  */

      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
     moved back by the user (who may also have changed the arguments).  */
      if (last_nonopt > optind)
    last_nonopt = optind;
      if (first_nonopt > optind)
    first_nonopt = optind;

      if (ordering == PERMUTE)
    {
      /* If we have just processed some options following some non-options,
         exchange them so that the options come first.  */

      if (first_nonopt != last_nonopt && last_nonopt != optind)
        exchange ((char **) argv);
      else if (last_nonopt != optind)
        first_nonopt = optind;

      /* Skip any additional non-options
         and extend the range of non-options previously skipped.  */

      while (optind < argc && NONOPTION_P)
        optind++;
      last_nonopt = optind;
    }

      /* The special ARGV-element `--' means premature end of options.
     Skip it like a null option,
     then exchange with previous non-options as if it were an option,
     then skip everything else like a non-option.  */

      if (optind != argc && !strcmp (argv[optind], "--"))
    {
      optind++;

      if (first_nonopt != last_nonopt && last_nonopt != optind)
        exchange ((char **) argv);
      else if (first_nonopt == last_nonopt)
        first_nonopt = optind;
      last_nonopt = argc;

      optind = argc;
    }

      /* If we have done all the ARGV-elements, stop the scan
     and back over any non-options that we skipped and permuted.  */

      if (optind == argc)
    {
      /* Set the next-arg-index to point at the non-options
         that we previously skipped, so the caller will digest them.  */
      if (first_nonopt != last_nonopt)
        optind = first_nonopt;
      return -1;
    }

      /* If we have come to a non-option and did not permute it,
     either stop the scan or describe it to the caller and pass it by.  */

      if (NONOPTION_P)
    {
      if (ordering == REQUIRE_ORDER)
        return -1;
      optarg = argv[optind++];
      return 1;
    }

      /* We have found another option-ARGV-element.
     Skip the initial punctuation.  */

      nextchar = (argv[optind] + 1
          + (longopts != NULL && argv[optind][1] == '-'));
    }

  /* Decode the current option-ARGV-element.  */

  /* Check whether the ARGV-element is a long option.

     If long_only and the ARGV-element has the form "-f", where f is
     a valid short option, don't consider it an abbreviated form of
     a long option that starts with f.  Otherwise there would be no
     way to give the -f short option.

     On the other hand, if there's a long option "fubar" and
     the ARGV-element is "-fu", do consider that an abbreviation of
     the long option, just like "--fu", and not "-f" with arg "u".

     This distinction seems to be the most useful approach.  */

  if (longopts != NULL
      && (argv[optind][1] == '-'
      || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
    {
      char *nameend;
      const struct option *p;
      const struct option *pfound = NULL;
      int exact = 0;
      int ambig = 0;
      int indfound = -1;
      int option_index;

      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
    /* Do nothing.  */ ;

      /* Test all long options for either exact match
     or abbreviated matches.  */
      for (p = longopts, option_index = 0; p->name; p++, option_index++)
    if (!strncmp (p->name, nextchar, nameend - nextchar))
      {
        if ((unsigned int) (nameend - nextchar)
        == (unsigned int) strlen (p->name))
          {
        /* Exact match found.  */
        pfound = p;
        indfound = option_index;
        exact = 1;
        break;
          }
        else if (pfound == NULL)
          {
        /* First nonexact match found.  */
        pfound = p;
        indfound = option_index;
          }
        else
          /* Second or later nonexact match found.  */
          ambig = 1;
      }

      if (ambig && !exact)
    {
      if (opterr)
        fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
             argv[0], argv[optind]);
      nextchar += strlen (nextchar);
      optind++;
      optopt = 0;
      return '?';
    }

      if (pfound != NULL)
    {
      option_index = indfound;
      optind++;
      if (*nameend)
        {
          /* Don't test has_arg with >, because some C compilers don't
         allow it to be used on enums.  */
          if (pfound->has_arg)
        optarg = nameend + 1;
          else
        {
          if (opterr)
           if (argv[optind - 1][1] == '-')
            /* --option */
            fprintf (stderr,
             _("%s: option `--%s' doesn't allow an argument\n"),
             argv[0], pfound->name);
           else
            /* +option or -option */
            fprintf (stderr,
             _("%s: option `%c%s' doesn't allow an argument\n"),
             argv[0], argv[optind - 1][0], pfound->name);

          nextchar += strlen (nextchar);

          optopt = pfound->val;
          return '?';
        }
        }
      else if (pfound->has_arg == 1)
        {
          if (optind < argc)
        optarg = argv[optind++];
          else
        {
          if (opterr)
            fprintf (stderr,
               _("%s: option `%s' requires an argument\n"),
               argv[0], argv[optind - 1]);
          nextchar += strlen (nextchar);
          optopt = pfound->val;
          return optstring[0] == ':' ? ':' : '?';
        }
        }
      nextchar += strlen (nextchar);
      if (longind != NULL)
        *longind = option_index;
      if (pfound->flag)
        {
          *(pfound->flag) = pfound->val;
          return 0;
        }
      return pfound->val;
    }

      /* Can't find it as a long option.  If this is not getopt_long_only,
     or the option starts with '--' or is not a valid short
     option, then it's an error.
     Otherwise interpret it as a short option.  */
      if (!long_only || argv[optind][1] == '-'
      || my_index (optstring, *nextchar) == NULL)
    {
      if (opterr)
        {
          if (argv[optind][1] == '-')
        /* --option */
        fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
             argv[0], nextchar);
          else
        /* +option or -option */
        fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
             argv[0], argv[optind][0], nextchar);
        }
      nextchar = (char *) "";
      optind++;
      optopt = 0;
      return '?';
    }
    }

  /* Look at and handle the next short option-character.  */

  {
    char c = *nextchar++;
    char *temp = my_index (optstring, c);

    /* Increment `optind' when we start to process its last character.  */
    if (*nextchar == '\0')
      ++optind;

    if (temp == NULL || c == ':')
      {
    if (opterr)
      {
        if (posixly_correct)
          /* 1003.2 specifies the format of this message.  */
          fprintf (stderr, _("%s: illegal option -- %c\n"),
               argv[0], c);
        else
          fprintf (stderr, _("%s: invalid option -- %c\n"),
               argv[0], c);
      }
    optopt = c;
    return '?';
      }
    /* Convenience. Treat POSIX -W foo same as long option --foo */
    if (temp[0] == 'W' && temp[1] == ';')
      {
    char *nameend;
    const struct option *p;
    const struct option *pfound = NULL;
    int exact = 0;
    int ambig = 0;
    int indfound = 0;
    int option_index;

    /* This is an option that requires an argument.  */
    if (*nextchar != '\0')
      {
        optarg = nextchar;
        /* If we end this ARGV-element by taking the rest as an arg,
           we must advance to the next element now.  */
        optind++;
      }
    else if (optind == argc)
      {
        if (opterr)
          {
        /* 1003.2 specifies the format of this message.  */
        fprintf (stderr, _("%s: option requires an argument -- %c\n"),
             argv[0], c);
          }
        optopt = c;
        if (optstring[0] == ':')
          c = ':';
        else
          c = '?';
        return c;
      }
    else
      /* We already incremented `optind' once;
         increment it again when taking next ARGV-elt as argument.  */
      optarg = argv[optind++];

    /* optarg is now the argument, see if it's in the
       table of longopts.  */

    for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
      /* Do nothing.  */ ;

    /* Test all long options for either exact match
       or abbreviated matches.  */
    for (p = longopts, option_index = 0; p->name; p++, option_index++)
      if (!strncmp (p->name, nextchar, nameend - nextchar))
        {
          if ((unsigned int) (nameend - nextchar) == strlen (p->name))
        {
          /* Exact match found.  */
          pfound = p;
          indfound = option_index;
          exact = 1;
          break;
        }
          else if (pfound == NULL)
        {
          /* First nonexact match found.  */
          pfound = p;
          indfound = option_index;
        }
          else
        /* Second or later nonexact match found.  */
        ambig = 1;
        }
    if (ambig && !exact)
      {
        if (opterr)
          fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
               argv[0], argv[optind]);
        nextchar += strlen (nextchar);
        optind++;
        return '?';
      }
    if (pfound != NULL)
      {
        option_index = indfound;
        if (*nameend)
          {
        /* Don't test has_arg with >, because some C compilers don't
           allow it to be used on enums.  */
        if (pfound->has_arg)
          optarg = nameend + 1;
        else
          {
            if (opterr)
              fprintf (stderr, _("\
%s: option `-W %s' doesn't allow an argument\n"),
                   argv[0], pfound->name);

            nextchar += strlen (nextchar);
            return '?';
          }
          }
        else if (pfound->has_arg == 1)
          {
        if (optind < argc)
          optarg = argv[optind++];
        else
          {
            if (opterr)
              fprintf (stderr,
                   _("%s: option `%s' requires an argument\n"),
                   argv[0], argv[optind - 1]);
            nextchar += strlen (nextchar);
            return optstring[0] == ':' ? ':' : '?';
          }
          }
        nextchar += strlen (nextchar);
        if (longind != NULL)
          *longind = option_index;
        if (pfound->flag)
          {
        *(pfound->flag) = pfound->val;
        return 0;
          }
        return pfound->val;
      }
      nextchar = NULL;
      return 'W';   /* Let the application handle it.   */
      }
    if (temp[1] == ':')
      {
    if (temp[2] == ':')
      {
        /* This is an option that accepts an argument optionally.  */
        if (*nextchar != '\0')
          {
        optarg = nextchar;
        optind++;
          }
        else
          optarg = NULL;
        nextchar = NULL;
      }
    else
      {
        /* This is an option that requires an argument.  */
        if (*nextchar != '\0')
          {
        optarg = nextchar;
        /* If we end this ARGV-element by taking the rest as an arg,
           we must advance to the next element now.  */
        optind++;
          }
        else if (optind == argc)
          {
        if (opterr)
          {
            /* 1003.2 specifies the format of this message.  */
            fprintf (stderr,
               _("%s: option requires an argument -- %c\n"),
               argv[0], c);
          }
        optopt = c;
        if (optstring[0] == ':')
          c = ':';
        else
          c = '?';
          }
        else
          /* We already incremented `optind' once;
         increment it again when taking next ARGV-elt as argument.  */
          optarg = argv[optind++];
        nextchar = NULL;
      }
      }
    return c;
  }
}

int
getopt (argc, argv, optstring)
     int argc;
     char *const *argv;
     const char *optstring;
{
  return _getopt_internal (argc, argv, optstring,
               (const struct option *) 0,
               (int *) 0,
               0);
}

#endif  /* Not ELIDE_CODE.  */


#ifdef TEST

/* Compile with -DTEST to make an executable for use in testing
   the above definition of `getopt'.  */

int
main (argc, argv)
     int argc;
     char **argv;
{
  int c;
  int digit_optind = 0;

  while (1)
    {
      int this_option_optind = optind ? optind : 1;

      c = getopt (argc, argv, "abc:d:0123456789");
      if (c == -1)
    break;

      switch (c)
    {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (digit_optind != 0 && digit_optind != this_option_optind)
        printf ("digits occur in two different argv-elements.\n");
      digit_optind = this_option_optind;
      printf ("option %c\n", c);
      break;

    case 'a':
      printf ("option a\n");
      break;

    case 'b':
      printf ("option b\n");
      break;

    case 'c':
      printf ("option c with value `%s'\n", optarg);
      break;

    case '?':
      break;

    default:
      printf ("?? getopt returned character code 0%o ??\n", c);
    }
    }

  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
    printf ("%s ", argv[optind++]);
      printf ("\n");
    }

  exit (0);
}

#endif /* TEST */

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/getopt1.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/getopt1.c used in: Step 640
/* getopt_long and getopt_long_only entry points for GNU getopt.
   Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.

   This file is part of the GNU C Library.  Its master source is NOT part of
   the C library, however.  The master source lives in /gd/gnu/lib.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "getopt.h"

#if !defined (__STDC__) || !__STDC__
/* This is a separate conditional since some stdc systems
   reject `defined (const)'.  */
#ifndef const
#define const
#endif
#endif

#include <stdio.h>

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#define GETOPT_INTERFACE_VERSION 2
#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
#include <gnu-versions.h>
#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
#define ELIDE_CODE
#endif
#endif

#ifndef ELIDE_CODE


/* This needs to come after some library #include
   to get __GNU_LIBRARY__ defined.  */
#ifdef __GNU_LIBRARY__
#include <stdlib.h>
#endif

#ifndef NULL
#define NULL 0
#endif

int
getopt_long (argc, argv, options, long_options, opt_index)
     int argc;
     char *const *argv;
     const char *options;
     const struct option *long_options;
     int *opt_index;
{
  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
}

/* Like getopt_long, but '-' as well as '--' can indicate a long option.
   If an option that starts with '-' (not '--') doesn't match a long option,
   but does match a short option, it is parsed as a short option
   instead.  */

int
getopt_long_only (argc, argv, options, long_options, opt_index)
     int argc;
     char *const *argv;
     const char *options;
     const struct option *long_options;
     int *opt_index;
{
  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
}


#endif  /* Not ELIDE_CODE.  */


#ifdef TEST

#include <stdio.h>

int
main (argc, argv)
     int argc;
     char **argv;
{
  int c;
  int digit_optind = 0;

  while (1)
    {
      int this_option_optind = optind ? optind : 1;
      int option_index = 0;
      static struct option long_options[] =
      {
    {"add", 1, 0, 0},
    {"append", 0, 0, 0},
    {"delete", 1, 0, 0},
    {"verbose", 0, 0, 0},
    {"create", 0, 0, 0},
    {"file", 1, 0, 0},
    {0, 0, 0, 0}
      };

      c = getopt_long (argc, argv, "abc:d:0123456789",
               long_options, &option_index);
      if (c == -1)
    break;

      switch (c)
    {
    case 0:
      printf ("option %s", long_options[option_index].name);
      if (optarg)
        printf (" with arg %s", optarg);
      printf ("\n");
      break;

    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (digit_optind != 0 && digit_optind != this_option_optind)
        printf ("digits occur in two different argv-elements.\n");
      digit_optind = this_option_optind;
      printf ("option %c\n", c);
      break;

    case 'a':
      printf ("option a\n");
      break;

    case 'b':
      printf ("option b\n");
      break;

    case 'c':
      printf ("option c with value `%s'\n", optarg);
      break;

    case 'd':
      printf ("option d with value `%s'\n", optarg);
      break;

    case '?':
      break;

    default:
      printf ("?? getopt returned character code 0%o ??\n", c);
    }
    }

  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
    printf ("%s ", argv[optind++]);
      printf ("\n");
    }

  exit (0);
}

#endif /* TEST */

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/getversion.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/getversion.c used in: Step 641
/* getversion.c -- select backup filename type
   Copyright (C) 1990 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.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu> */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include "backupfile.h"

#if STDC_HEADERS
# include <stdlib.h>
#endif

int argmatch ();
void invalid_arg ();

extern char *program_name;

static const char *const backup_args[] =
{
  "never", "simple", "nil", "existing", "t", "numbered", 0
};

static const enum backup_type backup_types[] =
{
  simple, simple, numbered_existing, numbered_existing, numbered, numbered
};

/* Return the type of backup indicated by VERSION.
   Unique abbreviations are accepted. */

enum backup_type
get_version (version)
     char *version;
{
  int i;

  if (version == 0 || *version == 0)
    return numbered_existing;
  i = argmatch (version, backup_args);
  if (i >= 0)
    return backup_types[i];
  invalid_arg ("version control type", version, i);
  exit (1);
}

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/modechange.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/modechange.c used in: Step 642
/* modechange.c -- file mode manipulation
   Copyright (C) 1989, 1990 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.  */

/* Written by David MacKenzie <djm@ai.mit.edu> */

/* The ASCII mode string is compiled into a linked list of `struct
   modechange', which can then be applied to each file to be changed.
   We do this instead of re-parsing the ASCII string for each file
   because the compiled form requires less computation to use; when
   changing the mode of many files, this probably results in a
   performance gain. */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include "modechange.h"

#if STDC_HEADERS
# include <stdlib.h>
#else
char *malloc ();
#endif

#ifndef NULL
# define NULL 0
#endif

#if STAT_MACROS_BROKEN
# undef S_ISDIR
#endif

#if !defined(S_ISDIR) && defined(S_IFDIR)
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif

/* Return newly allocated memory to hold one element of type TYPE. */
#define talloc(type) ((type *) malloc (sizeof (type)))

#define isodigit(c) ((c) >= '0' && (c) <= '7')

static int oatoi ();

/* Return a linked list of file mode change operations created from
   MODE_STRING, an ASCII string that contains either an octal number
   specifying an absolute mode, or symbolic mode change operations with
   the form:
   [ugoa...][[+-=][rwxXstugo...]...][,...]
   MASKED_OPS is a bitmask indicating which symbolic mode operators (=+-)
   should not affect bits set in the umask when no users are given.
   Operators not selected in MASKED_OPS ignore the umask.

   Return MODE_INVALID if `mode_string' does not contain a valid
   representation of file mode change operations;
   return MODE_MEMORY_EXHAUSTED if there is insufficient memory. */

struct mode_change *
mode_compile (mode_string, masked_ops)
     const char *mode_string;
     unsigned masked_ops;
{
  struct mode_change *head; /* First element of the linked list. */
  struct mode_change *change;   /* An element of the linked list. */
  int i;            /* General purpose temporary. */
  int umask_value;      /* The umask value (surprise). */
  unsigned short affected_bits; /* Which bits in the mode are operated on. */
  unsigned short affected_masked; /* `affected_bits' modified by umask. */
  unsigned ops_to_mask;     /* Operators to actually use umask on. */

  i = oatoi (mode_string);
  if (i >= 0)
    {
      if (i > 07777)
    return MODE_INVALID;
      head = talloc (struct mode_change);
      if (head == NULL)
    return MODE_MEMORY_EXHAUSTED;
      head->next = NULL;
      head->op = '=';
      head->flags = 0;
      head->value = i;
      head->affected = 07777;   /* Affect all permissions. */
      return head;
    }

  umask_value = umask (0);
  umask (umask_value);      /* Restore the old value. */

  head = NULL;
#ifdef lint
  change = NULL;
#endif
  --mode_string;

  /* One loop iteration for each "ugoa...=+-rwxXstugo...[=+-rwxXstugo...]". */
  do
    {
      affected_bits = 0;
      ops_to_mask = 0;
      /* Turn on all the bits in `affected_bits' for each group given. */
      for (++mode_string;; ++mode_string)
    switch (*mode_string)
      {
      case 'u':
        affected_bits |= 04700;
        break;
      case 'g':
        affected_bits |= 02070;
        break;
      case 'o':
        affected_bits |= 01007;
        break;
      case 'a':
        affected_bits |= 07777;
        break;
      default:
        goto no_more_affected;
      }

    no_more_affected:
      /* If none specified, affect all bits, except perhaps those
     set in the umask. */
      if (affected_bits == 0)
    {
      affected_bits = 07777;
      ops_to_mask = masked_ops;
    }

      while (*mode_string == '=' || *mode_string == '+' || *mode_string == '-')
    {
      /* Add the element to the tail of the list, so the operations
         are performed in the correct order. */
      if (head == NULL)
        {
          head = talloc (struct mode_change);
          if (head == NULL)
        return MODE_MEMORY_EXHAUSTED;
          change = head;
        }
      else
        {
          change->next = talloc (struct mode_change);
          if (change->next == NULL)
        {
          mode_free (change);
          return MODE_MEMORY_EXHAUSTED;
        }
          change = change->next;
        }

      change->next = NULL;
      change->op = *mode_string;    /* One of "=+-". */
      affected_masked = affected_bits;
      if (ops_to_mask & (*mode_string == '=' ? MODE_MASK_EQUALS
                 : *mode_string == '+' ? MODE_MASK_PLUS
                 : MODE_MASK_MINUS))
        affected_masked &= ~umask_value;
      change->affected = affected_masked;
      change->value = 0;
      change->flags = 0;

      /* Set `value' according to the bits set in `affected_masked'. */
      for (++mode_string;; ++mode_string)
        switch (*mode_string)
          {
          case 'r':
        change->value |= 00444 & affected_masked;
        break;
          case 'w':
        change->value |= 00222 & affected_masked;
        break;
          case 'X':
        change->flags |= MODE_X_IF_ANY_X;
        /* Fall through. */
          case 'x':
        change->value |= 00111 & affected_masked;
        break;
          case 's':
        /* Set the setuid/gid bits if `u' or `g' is selected. */
        change->value |= 06000 & affected_masked;
        break;
          case 't':
        /* Set the "save text image" bit if `o' is selected. */
        change->value |= 01000 & affected_masked;
        break;
          case 'u':
        /* Set the affected bits to the value of the `u' bits
           on the same file.  */
        if (change->value)
          goto invalid;
        change->value = 00700;
        change->flags |= MODE_COPY_EXISTING;
        break;
          case 'g':
        /* Set the affected bits to the value of the `g' bits
           on the same file.  */
        if (change->value)
          goto invalid;
        change->value = 00070;
        change->flags |= MODE_COPY_EXISTING;
        break;
          case 'o':
        /* Set the affected bits to the value of the `o' bits
           on the same file.  */
        if (change->value)
          goto invalid;
        change->value = 00007;
        change->flags |= MODE_COPY_EXISTING;
        break;
          default:
        goto no_more_values;
          }
    no_more_values:;
    }
  } while (*mode_string == ',');
  if (*mode_string == 0)
    return head;
invalid:
  mode_free (head);
  return MODE_INVALID;
}

/* Return file mode OLDMODE, adjusted as indicated by the list of change
   operations CHANGES.  If OLDMODE is a directory, the type `X'
   change affects it even if no execute bits were set in OLDMODE.
   The returned value has the S_IFMT bits cleared. */

unsigned short
mode_adjust (oldmode, changes)
     unsigned oldmode;
     const struct mode_change *changes;
{
  unsigned short newmode;   /* The adjusted mode and one operand. */
  unsigned short value;     /* The other operand. */

  newmode = oldmode & 07777;

  for (; changes; changes = changes->next)
    {
      if (changes->flags & MODE_COPY_EXISTING)
    {
      /* Isolate in `value' the bits in `newmode' to copy, given in
         the mask `changes->value'. */
      value = newmode & changes->value;

      if (changes->value & 00700)
        /* Copy `u' permissions onto `g' and `o'. */
        value |= (value >> 3) | (value >> 6);
      else if (changes->value & 00070)
        /* Copy `g' permissions onto `u' and `o'. */
        value |= (value << 3) | (value >> 3);
      else
        /* Copy `o' permissions onto `u' and `g'. */
        value |= (value << 3) | (value << 6);

      /* In order to change only `u', `g', or `o' permissions,
         or some combination thereof, clear unselected bits.
         This can not be done in mode_compile because the value
         to which the `changes->affected' mask is applied depends
         on the old mode of each file. */
      value &= changes->affected;
    }
      else
    {
      value = changes->value;
      /* If `X', do not affect the execute bits if the file is not a
         directory and no execute bits are already set. */
      if ((changes->flags & MODE_X_IF_ANY_X)
          && !S_ISDIR (oldmode)
          && (newmode & 00111) == 0)
        value &= ~00111;    /* Clear the execute bits. */
    }

      switch (changes->op)
    {
    case '=':
      /* Preserve the previous values in `newmode' of bits that are
         not affected by this change operation. */
      newmode = (newmode & ~changes->affected) | value;
      break;
    case '+':
      newmode |= value;
      break;
    case '-':
      newmode &= ~value;
      break;
    }
    }
  return newmode;
}

/* Free the memory used by the list of file mode change operations
   CHANGES. */

void
mode_free (changes)
     register struct mode_change *changes;
{
  register struct mode_change *next;

  while (changes)
    {
      next = changes->next;
      free (changes);
      changes = next;
    }
}

/* Return a positive integer containing the value of the ASCII
   octal number S.  If S is not an octal number, return -1.  */

static int
oatoi (s)
     char *s;
{
  register int i;

  if (*s == 0)
    return -1;
  for (i = 0; isodigit (*s); ++s)
    i = i * 8 + *s - '0';
  if (*s)
    return -1;
  return i;
}

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/msleep.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/msleep.c used in: Step 643
/* Sleep a given number of milliseconds.
   Copyright (C) 1992, 1993, 1994, 1997 Free Software Foundation, Inc.
   François Pinard <pinard@iro.umontreal.ca>, 1992.

   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.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

/* This code is heavily borrowed from Taylor UUCP 1.03.  Ian picks one of
   usleep, nap, napms, poll, select and sleep, in decreasing order of
   preference.  The sleep function is always available.  */

/* In many cases, we will sleep if the wanted number of milliseconds
   is higher than this value.  */
#define THRESHOLD_FOR_SLEEP 30000

/* Include some header files.  */

#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#if HAVE_POLL
# if HAVE_STROPTS_H
#  include <stropts.h>
# endif
# if HAVE_POLL_H
#  include <sys/types.h>
#  include <poll.h>
# endif
# if !HAVE_STROPTS_H && !HAVE_POLL_H
/* We need a definition for struct pollfd, although it doesn't matter
   what it contains.  */
struct pollfd
{
  int idummy;
};
# endif
#else
# if HAVE_SELECT
#  include <sys/time.h>
# endif
#endif

/*---------------------------------------.
| Sleep a given number of milliseconds.  |
`---------------------------------------*/

void
msleep (milliseconds)
     int milliseconds;
{
#if HAVE_USLEEP

  if (milliseconds > 0)
    usleep (milliseconds * (long) 1000);

#else
# if HAVE_NAP

  if (milliseconds > 0)
    nap ((long) milliseconds);

# else
#  if HAVE_NAPMS

  if (milliseconds >= THRESHOLD_FOR_SLEEP)
    {
      sleep (milliseconds / 1000);
      milliseconds %= 1000;
    }
  if (milliseconds > 0)
    napms (milliseconds);

#  else
#   if HAVE_POLL

  struct pollfd sdummy;     /* poll(2) checks this address */

  if (milliseconds >= THRESHOLD_FOR_SLEEP)
    {
      sleep (milliseconds / 1000);
      milliseconds %= 1000;
    }
  if (milliseconds > 0)
    poll (&sdummy, 0, milliseconds);

#   else
#    if HAVE_SELECT

  struct timeval s;

  if (milliseconds >= THRESHOLD_FOR_SLEEP)
    {
      sleep (milliseconds / 1000);
      milliseconds %= 1000;
    }
  if (milliseconds > 0)
    {
      s.tv_sec = milliseconds / 1000;
      s.tv_usec = (milliseconds % 1000) * (long) 1000;
      select (0, NULL, NULL, NULL, &s);
    }

#    else

  /* Round the time up to the next full second.  */

  if (milliseconds > 0)
    sleep ((milliseconds + 999) / 1000);

#    endif
#   endif
#  endif
# endif
#endif
}

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/xgetcwd.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/xgetcwd.c used in: Step 644
/* xgetcwd.c -- return current directory with unlimited length
   Copyright (C) 1992, 1996 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.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <errno.h>
#ifndef errno
extern int errno;
#endif

#include <sys/types.h>
#include "pathmax.h"

#if HAVE_GETCWD
char *getcwd ();
#else
char *getwd ();
# define getcwd(Buf, Max) getwd (Buf)
#endif

/* Amount to increase buffer size by in each try. */
#define PATH_INCR 32

char *xmalloc ();
char *xrealloc ();
void free ();

/* Return the current directory, newly allocated, arbitrarily long.
   Return NULL and set errno on error. */

char *
xgetcwd ()
{
  char *cwd;
  char *ret;
  unsigned path_max;

  errno = 0;
  path_max = (unsigned) PATH_MAX;
  path_max += 2;        /* The getcwd docs say to do this. */

  cwd = xmalloc (path_max);

  errno = 0;
  while ((ret = getcwd (cwd, path_max)) == NULL && errno == ERANGE)
    {
      path_max += PATH_INCR;
      cwd = xrealloc (cwd, path_max);
      errno = 0;
    }

  if (ret == NULL)
    {
      int save_errno = errno;
      free (cwd);
      errno = save_errno;
      return NULL;
    }
  return cwd;
}

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/xmalloc.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/xmalloc.c used in: Step 645
/* xmalloc.c -- malloc with out of memory checking
   Copyright (C) 1990, 91, 92, 93, 94, 95, 96 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if __STDC__
# define VOID void
#else
# define VOID char
#endif

#include <sys/types.h>

#if STDC_HEADERS
# include <stdlib.h>
#else
VOID *calloc ();
VOID *malloc ();
VOID *realloc ();
void free ();
#endif

#if ENABLE_NLS
# include <libintl.h>
# define _(Text) gettext (Text)
#else
# define textdomain(Domain)
# define _(Text) Text
#endif

#include "error.h"

#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif

/* Prototypes for functions defined here.  */
#if defined (__STDC__) && __STDC__
static VOID *fixup_null_alloc (size_t n);
VOID *xmalloc (size_t n);
VOID *xcalloc (size_t n, size_t s);
VOID *xrealloc (VOID *p, size_t n);
#endif


/* Exit value when the requested amount of memory is not available.
   The caller may set it to some other value.  */
int xmalloc_exit_failure = EXIT_FAILURE;

#if __STDC__ && (HAVE_VPRINTF || HAVE_DOPRNT)
void error (int, int, const char *, ...);
#else
void error ();
#endif

static VOID *
fixup_null_alloc (n)
     size_t n;
{
  VOID *p;

  p = 0;
  if (n == 0)
    p = malloc ((size_t) 1);
  if (p == 0)
    error (xmalloc_exit_failure, 0, _("Memory exhausted"));
  return p;
}

/* Allocate N bytes of memory dynamically, with error checking.  */

VOID *
xmalloc (n)
     size_t n;
{
  VOID *p;

  p = malloc (n);
  if (p == 0)
    p = fixup_null_alloc (n);
  return p;
}

/* Allocate memory for N elements of S bytes, with error checking.  */

VOID *
xcalloc (n, s)
     size_t n, s;
{
  VOID *p;

  p = calloc (n, s);
  if (p == 0)
    p = fixup_null_alloc (n);
  return p;
}

/* Change the size of an allocated block of memory P to N bytes,
   with error checking.
   If P is NULL, run xmalloc.  */

VOID *
xrealloc (p, n)
     VOID *p;
     size_t n;
{
  if (p == 0)
    return xmalloc (n);
  p = realloc (p, n);
  if (p == 0)
    p = fixup_null_alloc (n);
  return p;
}

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/xstrdup.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/xstrdup.c used in: Step 646
/* xstrdup.c -- copy a string with out of memory checking
   Copyright (C) 1990, 1996 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if defined(STDC_HEADERS) || defined(HAVE_STRING_H)
# include <string.h>
#else
# include <strings.h>
#endif

#if defined (__STDC__) && __STDC__
char *xmalloc (size_t);
char *xstrdup (char *string);
#else
char *xmalloc ();
#endif

/* Return a newly allocated copy of STRING.  */

char *
xstrdup (string)
     char *string;
{
  return strcpy (xmalloc (strlen (string) + 1), string);
}

https://git.savannah.gnu.org/cgit/tar.git/tree//src/arith.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/arith.c used in: Step 648
/* Arithmetic for numbers greater than a long int, for GNU tar.
   Copyright (C) 1996, 1997 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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include "system.h"

/* common.h is needed to define FATAL_ERROR.  It also includes arith.h.  */
#include "common.h"

/* GNU tar needs handling numbers exceeding 32 bits, which is the size of
   unsigned long ints for many C compilers.  This module should provide
   machinery for handling at least BITS_PER_TARLONG bits per number.  If
   `long long' ints are available and are sufficient for the task, they will
   be used preferrably.

   Russell Cattelan reports 165 Gb single tapes (digital video D2 tapes on
   Ampex drives), so requiring 38 bits for the tape length in bytes.  He
   also reports breaking the terabyte limit with a single file (using SGI
   xFS file system over 37 28GB disk arrays attached to a Power Challenge
   XL; check out http://www.lcse.umn.edu/ for a picture), so requiring a
   little more than 40 bits for the file size in bytes.  The POSIX header
   structure allows for 12 octal digits to represent file lengths, that is,
   up to 36 bits for the byte size of files.

   If `long long' is not supported by the compiler, SIZEOF_LONG_LONG will be
   set to zero by configure.  In this case, or if `long long' ints does not
   have enough bits, then huge numbers are rather represented by an array of
   longs, with the least significant super-digit at position 0.  For making
   multiplication and decimal input/output easy, the base of a super-digit
   is an exact exponent of 10, and is such that base*base fits in a long.  */

#if SUPERDIGIT

/*-------------------------------.
| Check if ACCUMULATOR is zero.  |
`-------------------------------*/

int
zerop_tarlong_helper (unsigned long *accumulator)
{
  int counter;

  for (counter = LONGS_PER_TARLONG - 1; counter >= 0; counter--)
    if (accumulator[counter])
      return 0;

  return 1;
}

/*----------------------------------------------.
| Check if FIRST is strictly less than SECOND.  |
`----------------------------------------------*/

int
lessp_tarlong_helper (unsigned long *first, unsigned long *second)
{
  int counter;

  for (counter = LONGS_PER_TARLONG - 1; counter >= 0; counter--)
    if (first[counter] != second[counter])
      return first[counter] < second[counter];

  return 0;
}

/*----------------------------.
| Reset ACCUMULATOR to zero.  |
`----------------------------*/

void
clear_tarlong_helper (unsigned long *accumulator)
{
  int counter;

  for (counter = 0; counter < LONGS_PER_TARLONG; counter++)
    accumulator[counter] = 0;
}

/*----------------------------.
| To ACCUMULATOR, add VALUE.  |
`----------------------------*/

void
add_to_tarlong_helper (unsigned long *accumulator, int value)
{
  int counter;

  if (value < 0)
    for (counter = 0; counter < LONGS_PER_TARLONG; counter++)
      {
    if (accumulator[counter] >= -value)
      {
        accumulator[counter] += value;
        return;
      }
    accumulator[counter] += value + SUPERDIGIT;
    value = -1;
      }
  else
    for (counter = 0; counter < LONGS_PER_TARLONG; counter++)
      {
    if (accumulator[counter] + value < SUPERDIGIT)
      {
        accumulator[counter] += value;
        return;
      }
    accumulator[counter] += value - SUPERDIGIT;
    value = 1;
      }
  FATAL_ERROR ((0, 0, _("Arithmetic overflow")));
}

/*--------------------------------.
| Multiply ACCUMULATOR by VALUE.  |
`--------------------------------*/

void
mult_tarlong_helper (unsigned long *accumulator, int value)
{
  int carry = 0;
  int counter;

  for (counter = 0; counter < LONGS_PER_TARLONG; counter++)
    {
      carry += accumulator[counter] * value;
      accumulator[counter] = carry % SUPERDIGIT;
      carry /= SUPERDIGIT;
    }
  if (carry)
    FATAL_ERROR ((0, 0, _("Arithmetic overflow")));
}

/*----------------------------------------------------------.
| Print the decimal representation of ACCUMULATOR on FILE.  |
`----------------------------------------------------------*/

void
print_tarlong_helper (unsigned long *accumulator, FILE *file)
{
  int counter = LONGS_PER_TARLONG - 1;

  while (counter > 0 && accumulator[counter] == 0)
    counter--;

  fprintf (file, "%uld", accumulator[counter]);
  while (counter > 0)
    fprintf (file, TARLONG_FORMAT, accumulator[--counter]);
}

#endif /* SUPERDIGIT */

https://git.savannah.gnu.org/cgit/tar.git/tree//src/system.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/system.h used in: Step 648, Step 649, Step 650, Step 651, Step 652, Step 653, Step 654, Step 655, Step 656, Step 657, Step 658, Step 659, Step 660, Step 661, Step 662
/* System dependent definitions for GNU tar.
   Copyright (C) 1994, 1995, 1996, 1997 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.
*/

#if HAVE_CONFIG_H
# include <config.h>
#endif

/* Declare alloca.  AIX requires this to be the first thing in the file.  */

#if __GNUC__
# define alloca __builtin_alloca
#else
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
 #pragma alloca
#  else
#   ifndef alloca
char *alloca ();
#   endif
#  endif
# endif
#endif

#include <sys/types.h>

/* Declare a generic pointer type.  */
#if __STDC__ || defined(__TURBOC__)
# define voidstar void *
#else
# define voidstar char *
#endif

/* Declare ISASCII.  */

#include <ctype.h>

#if STDC_HEADERS
# define ISASCII(Char) 1
#else
# ifdef isascii
#  define ISASCII(Char) isascii (Char)
# else
#  if HAVE_ISASCII
#   define ISASCII(Char) isascii (Char)
#  else
#   define ISASCII(Char) 1
#  endif
# endif
#endif

/* Declare string and memory handling routines.  Take care that an ANSI
   string.h and pre-ANSI memory.h might conflict, and that memory.h and
   strings.h conflict on some systems.  */

#if STDC_HEADERS || HAVE_STRING_H
# include <string.h>
# if !STDC_HEADERS && HAVE_MEMORY_H
#  include <memory.h>
# endif
#else
# include <strings.h>
# ifndef strchr
#  define strchr index
# endif
# ifndef strrchr
#  define strrchr rindex
# endif
# ifndef memcpy
#  define memcpy(Dst, Src, Num) bcopy (Src, Dst, Num)
# endif
# ifndef memcmp
#  define memcmp(Src1, Src2, Num) bcmp (Src1, Src2, Num)
# endif
#endif

/* Declare errno.  */

#include <errno.h>
#ifndef errno
extern int errno;
#endif

/* Declare open parameters.  */

#if HAVE_FCNTL_H
# include <fcntl.h>
#else
# include <sys/file.h>
#endif
                /* Pick only one of the next three: */
#ifndef O_RDONLY
# define O_RDONLY   0   /* only allow read */
#endif
#ifndef O_WRONLY
# define O_WRONLY   1   /* only allow write */
#endif
#ifndef O_RDWR
# define O_RDWR     2   /* both are allowed */
#endif
                /* The rest can be OR-ed in to the above: */
#ifndef O_NDELAY
# define O_NDELAY   4   /* don't block on opening devices */
#endif
#ifndef O_CREAT
# define O_CREAT    8   /* create file if needed */
#endif
#ifndef O_EXCL
# define O_EXCL     16  /* file cannot already exist */
#endif
#ifndef O_TRUNC
# define O_TRUNC    32  /* truncate file on open */
#endif
#ifndef O_APPEND
# define O_APPEND   64  /* always write at end of file */
#endif
                /* MS-DOG forever, with my love! */
#ifndef O_BINARY
# define O_BINARY 0
#endif
                /* Emulate System V 3-argument open call */
#if EMUL_OPEN3
# define open open3
#endif

/* Declare file status routines and bits.  */

#include <sys/stat.h>

#ifndef S_ISLNK
# define lstat stat
#endif

#if STAT_MACROS_BROKEN
# undef S_ISBLK
# undef S_ISCHR
# undef S_ISDIR
# undef S_ISFIFO
# undef S_ISLNK
# undef S_ISMPB
# undef S_ISMPC
# undef S_ISNWK
# undef S_ISREG
# undef S_ISSOCK
#endif

/* On MSDOS, there are missing things from <sys/stat.h>.  */
#if MSDOS
# define S_ISUID 0
# define S_ISGID 0
# define S_ISVTX 0
#endif

#ifndef S_ISREG         /* POSIX.1 stat stuff missing */
# define mode_t unsigned short
#endif
#if !defined(S_ISBLK) && defined(S_IFBLK)
# define S_ISBLK(Mode) (((Mode) & S_IFMT) == S_IFBLK)
#endif
#if !defined(S_ISCHR) && defined(S_IFCHR)
# define S_ISCHR(Mode) (((Mode) & S_IFMT) == S_IFCHR)
#endif
#if !defined(S_ISDIR) && defined(S_IFDIR)
# define S_ISDIR(Mode) (((Mode) & S_IFMT) == S_IFDIR)
#endif
#if !defined(S_ISREG) && defined(S_IFREG)
# define S_ISREG(Mode) (((Mode) & S_IFMT) == S_IFREG)
#endif
#if !defined(S_ISFIFO) && defined(S_IFIFO)
# define S_ISFIFO(Mode) (((Mode) & S_IFMT) == S_IFIFO)
#endif
#if !defined(S_ISLNK) && defined(S_IFLNK)
# define S_ISLNK(Mode) (((Mode) & S_IFMT) == S_IFLNK)
#endif
#if !defined(S_ISSOCK) && defined(S_IFSOCK)
# define S_ISSOCK(Mode) (((Mode) & S_IFMT) == S_IFSOCK)
#endif
#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */
# define S_ISMPB(Mode) (((Mode) & S_IFMT) == S_IFMPB)
# define S_ISMPC(Mode) (((Mode) & S_IFMT) == S_IFMPC)
#endif
#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */
# define S_ISNWK(Mode) (((Mode) & S_IFMT) == S_IFNWK)
#endif

#if !HAVE_MKFIFO
# define mkfifo(Path, Mode) (mknod (Path, (Mode) | S_IFIFO, 0))
#endif

#if !defined(S_ISCTG) && defined(S_IFCTG) /* contiguous file */
# define S_ISCTG(Mode) (((Mode) & S_IFMT) == S_IFCTG)
#endif
#if !defined(S_ISVTX)
# define S_ISVTX 0001000
#endif

#ifndef _POSIX_SOURCE
# include <sys/param.h>
#endif

/* Include <unistd.h> before any preprocessor test of _POSIX_VERSION.  */
#if HAVE_UNISTD_H
# include <unistd.h>
#endif

/* Declare make device, major and minor.  Since major is a function on
   SVR4, we have to resort to GOT_MAJOR instead of just testing if
   major is #define'd.  */

#if MAJOR_IN_MKDEV
# include <sys/mkdev.h>
# define GOT_MAJOR
#endif

#if MAJOR_IN_SYSMACROS
# include <sys/sysmacros.h>
# define GOT_MAJOR
#endif

/* Some <sys/types.h> defines the macros. */
#ifdef major
# define GOT_MAJOR
#endif

#ifndef GOT_MAJOR
# if MSDOS
#  define major(Device)     (Device)
#  define minor(Device)     (Device)
#  define makedev(Major, Minor) (((Major) << 8) | (Minor))
#  define GOT_MAJOR
# endif
#endif

/* For HP-UX before HP-UX 8, major/minor are not in <sys/sysmacros.h>.  */
#ifndef GOT_MAJOR
# if defined(hpux) || defined(__hpux__) || defined(__hpux)
#  include <sys/mknod.h>
#  define GOT_MAJOR
# endif
#endif

#ifndef GOT_MAJOR
# define major(Device)      (((Device) >> 8) & 0xff)
# define minor(Device)      ((Device) & 0xff)
# define makedev(Major, Minor)  (((Major) << 8) | (Minor))
#endif

#undef GOT_MAJOR

/* Declare directory reading routines and structures.  */

#if __TURBOC__
# include "msd_dir.h"
# define NAMLEN(dirent) ((dirent)->d_namlen)
#else
# if HAVE_DIRENT_H
#  include <dirent.h>
#  define NAMLEN(dirent) (strlen((dirent)->d_name))
# else
#  define dirent direct
#  define NAMLEN(dirent) ((dirent)->d_namlen)
#  if HAVE_SYS_NDIR_H
#   include <sys/ndir.h>
#  endif
#  if HAVE_SYS_DIR_H
#   include <sys/dir.h>
#  endif
#  if HAVE_NDIR_H
#   include <ndir.h>
#  endif
# endif
#endif

/* Declare wait status.  */

#if HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif

#if HAVE_UNION_WAIT
# define WAIT_T union wait
# ifndef WTERMSIG
#  define WTERMSIG(Status)     ((Status).w_termsig)
# endif
# ifndef WCOREDUMP
#  define WCOREDUMP(Status)    ((Status).w_coredump)
# endif
# ifndef WEXITSTATUS
#  define WEXITSTATUS(Status)  ((Status).w_retcode)
# endif
#else
# define WAIT_T int
# ifndef WTERMSIG
#  define WTERMSIG(Status)     ((Status) & 0x7f)
# endif
# ifndef WCOREDUMP
#  define WCOREDUMP(Status)    ((Status) & 0x80)
# endif
# ifndef WEXITSTATUS
#  define WEXITSTATUS(Status)  (((Status) >> 8) & 0xff)
# endif
#endif

#ifndef WIFSTOPPED
# define WIFSTOPPED(Status)    (WTERMSIG(Status) == 0x7f)
#endif
#ifndef WIFSIGNALED
# define WIFSIGNALED(Status)   (WTERMSIG(Status) != 0)
#endif
#ifndef WIFEXITED
# define WIFEXITED(Status)     (WTERMSIG(Status) == 0)
#endif

/* FIXME: It is wrong to use BLOCKSIZE for buffers when the logical block
   size is greater than 512 bytes; so ST_BLKSIZE code below, in preparation
   for some cleanup in this area, later.  */

/* Get or fake the disk device blocksize.  Usually defined by sys/param.h
   (if at all).  */

#if !defined(DEV_BSIZE) && defined(BSIZE)
# define DEV_BSIZE BSIZE
#endif
#if !defined(DEV_BSIZE) && defined(BBSIZE) /* SGI */
# define DEV_BSIZE BBSIZE
#endif
#ifndef DEV_BSIZE
# define DEV_BSIZE 4096
#endif

/* Extract or fake data from a `struct stat'.  ST_BLKSIZE gives the
   optimal I/O blocksize for the file, in bytes.  Some systems, like
   Sequents, return st_blksize of 0 on pipes.  */

#if !HAVE_ST_BLKSIZE
# define ST_BLKSIZE(Statbuf) DEV_BSIZE
#else
# define ST_BLKSIZE(Statbuf) \
    ((Statbuf).st_blksize > 0 ? (Statbuf).st_blksize : DEV_BSIZE)
#endif

/* Extract or fake data from a `struct stat'.  ST_NBLOCKS gives the
   number of 512-byte blocks in the file (including indirect blocks).
   fileblocks.c uses BSIZE.  HP-UX counts st_blocks in 1024-byte units,
   this loses when mixing HP-UX and BSD filesystems with NFS.  AIX PS/2
   counts st_blocks in 4K units.  */

#if !HAVE_ST_BLOCKS
# if defined(_POSIX_SOURCE) || !defined(BSIZE)
#  define ST_NBLOCKS(Statbuf) (((Statbuf).st_size + 512 - 1) / 512)
# else
#  define ST_NBLOCKS(Statbuf) (st_blocks ((Statbuf).st_size))
# endif
#else
# if defined(hpux) || defined(__hpux__) || defined(__hpux)
#  define ST_NBLOCKS(Statbuf) ((Statbuf).st_blocks * 2)
# else
#  if defined(_AIX) && defined(_I386)
#   define ST_NBLOCKS(Statbuf) ((Statbuf).st_blocks * 8)
#  else
#   define ST_NBLOCKS(Statbuf) ((Statbuf).st_blocks)
#  endif
# endif
#endif

/* This is a real challenge to properly get MTIO* symbols :-(.  ISC uses
   <sys/gentape.h>.  SCO and BSDi uses <sys/tape.h>; BSDi also requires
   <sys/tprintf.h> and <sys/device.h> for defining tp_dev and tpr_t.  It
   seems that the rest use <sys/mtio.h>, which itself requires other files,
   depending on systems.  Pyramid defines _IOW in <sgtty.h>, for example.  */

#if HAVE_SYS_GENTAPE_H
# include <sys/gentape.h>
#else
# if HAVE_SYS_TAPE_H
#  if HAVE_SYS_DEVICE_H
#   include <sys/device.h>
#  endif
#  if HAVE_SYS_BUF_H
#   include <sys/buf.h>
#  endif
#  if HAVE_SYS_TPRINTF_H
#   include <sys/tprintf.h>
#  endif
#  include <sys/tape.h>
# else
#  if HAVE_SYS_MTIO_H
#   include <sys/ioctl.h>
#   if HAVE_SGTTY_H
#    include <sgtty.h>
#   endif
#   if HAVE_SYS_IO_TRIOCTL_H
#    include <sys/io/trioctl.h>
#   endif
#   include <sys/mtio.h>
#  endif
# endif
#endif

/* Declare standard functions.  */

#if STDC_HEADERS
# include <stdlib.h>
#else
voidstar malloc ();
voidstar realloc ();
# if HAVE_GETCWD
char *getcwd ();
# endif
char *getenv ();
#endif

#include <stdio.h>

#ifndef _POSIX_VERSION
# if MSDOS
#  include <io.h>
# else
off_t lseek ();
# endif
#endif

#include <pathmax.h>

#if WITH_DMALLOC
# undef HAVE_VALLOC
# define DMALLOC_FUNC_CHECK
# include <dmalloc.h>
#endif


/* Prototypes for external functions.  */

#ifndef PARAMS
# if PROTOTYPES
#  define PARAMS(Args) Args
# else
#  define PARAMS(Args) ()
# endif
#endif

#if HAVE_LOCALE_H
# include <locale.h>
#endif
#if !HAVE_SETLOCALE
# define setlocale(Category, Locale)
#endif

#if ENABLE_NLS
# include <libintl.h>
# define _(Text) gettext (Text)
#else
# define bindtextdomain(Domain, Directory)
# define textdomain(Domain)
# define _(Text) Text
#endif
#define N_(Text) Text

/* Library modules.  */

#include "error.h"

#if !HAVE_STRSTR
char *strstr PARAMS ((const char *, const char *));
#endif

#if HAVE_VALLOC
# ifndef valloc
voidstar valloc PARAMS ((size_t));
# endif
#else
# define valloc(Size) malloc (Size)
#endif

voidstar xmalloc PARAMS ((size_t));
voidstar xrealloc PARAMS ((voidstar, size_t));
char *xstrdup PARAMS ((const char *));

https://git.savannah.gnu.org/cgit/tar.git/tree//src/common.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/common.h used in: Step 648, Step 652, Step 654, Step 655, Step 656, Step 657, Step 658, Step 661, Step 662
/* Common declarations for the tar program.
   Copyright (C) 1988, 92, 93, 94, 96, 97 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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Declare the GNU tar archive format.  */
#include "tar.h"

/* The checksum field is filled with this while the checksum is computed.  */
#define CHKBLANKS   "        "  /* 8 blanks, no null */

/* Some constants from POSIX are given names.  */
#define NAME_FIELD_SIZE   100
#define PREFIX_FIELD_SIZE 155
#define UNAME_FIELD_SIZE   32
#define GNAME_FIELD_SIZE   32

/* POSIX specified symbols currently unused are undefined here.  */
#undef TSUID
#undef TSGID
#undef TSVTX
#undef TUREAD
#undef TUWRITE
#undef TUEXEC
#undef TGREAD
#undef TGWRITE
#undef TGEXEC
#undef TOREAD
#undef TOWRITE
#undef TOEXEC


/* Some various global definitions.  */

/* Name of file to use for interacting with user.  */
#if MSDOS
# define TTY_NAME "con"
#else
# define TTY_NAME "/dev/tty"
#endif

/* GLOBAL is defined to empty in `tar.c' only, and left alone in other `*.c'
   modules.  Here, we merely set it to "extern" if it is not already set.
   GNU tar does depend on the system loader to preset all GLOBAL variables to
   neutral (or zero) values, explicit initialisation is usually not done.  */
#ifndef GLOBAL
# define GLOBAL extern
#endif

/* Exit status for GNU tar.  Let's try to keep this list as simple as
   possible.  -d option strongly invites a status different for unequal
   comparison and other errors.  */
GLOBAL int exit_status;

#define TAREXIT_SUCCESS 0
#define TAREXIT_DIFFERS 1
#define TAREXIT_FAILURE 2

/* Both WARN and ERROR write a message on stderr and continue processing,
   however ERROR manages so tar will exit unsuccessfully.  FATAL_ERROR
   writes a message on stderr and aborts immediately, with another message
   line telling so.  USAGE_ERROR works like FATAL_ERROR except that the
   other message line suggests trying --help.  All four macros accept a
   single argument of the form ((0, errno, _("FORMAT"), Args...)).  `errno'
   is `0' when the error is not being detected by the system.  */

#define WARN(Args) \
  error Args
#define ERROR(Args) \
  (error Args, exit_status = TAREXIT_FAILURE)
#define FATAL_ERROR(Args) \
  (error Args, error (TAREXIT_FAILURE, 0, \
              _("Error is not recoverable: exiting now")), 0)
#define USAGE_ERROR(Args) \
  (error Args, usage (TAREXIT_FAILURE), 0)


/* Information gleaned from the command line.  */

#include "arith.h"
#include "modechange.h"

/* Name of this program.  */
GLOBAL const char *program_name;

/* Main command option.  */

enum subcommand
{
  UNKNOWN_SUBCOMMAND,       /* none of the following */
  APPEND_SUBCOMMAND,        /* -r */
  CAT_SUBCOMMAND,       /* -A */
  CREATE_SUBCOMMAND,        /* -c */
  DELETE_SUBCOMMAND,        /* -D */
  DIFF_SUBCOMMAND,      /* -d */
  EXTRACT_SUBCOMMAND,       /* -x */
  LIST_SUBCOMMAND,      /* -t */
  UPDATE_SUBCOMMAND     /* -u */
};

GLOBAL enum subcommand subcommand_option;

/* Selected format for output archive.  */
GLOBAL enum archive_format archive_format;

/* Either NL or NUL, as decided by the --null option.  */
GLOBAL char filename_terminator;

/* Size of each record, once in blocks, once in bytes.  Those two variables
   are always related, the second being BLOCKSIZE times the first.  They do
   not have _option in their name, even if their values is derived from
   option decoding, as these are especially important in tar.  */
GLOBAL int blocking_factor;
GLOBAL int record_size;

/* Boolean value.  */
GLOBAL int absolute_names_option;

/* This variable tells how to interpret newer_mtime_option, below.  If zero,
   files get archived if their mtime is not less than newer_mtime_option.
   If nonzero, files get archived if *either* their ctime or mtime is not less
   than newer_mtime_option.  */
GLOBAL int after_date_option;

/* Boolean value.  */
GLOBAL int atime_preserve_option;

/* Boolean value.  */
GLOBAL int backup_option;

/* Boolean value.  */
GLOBAL int block_number_option;

/* Boolean value.  */
GLOBAL int checkpoint_option;

/* Specified name of compression program, or "gzip" as implied by -z.  */
GLOBAL const char *use_compress_program_option;

/* Boolean value.  */
GLOBAL int dereference_option;

/* Boolean value.  */
GLOBAL int exclude_option;

/* Specified file containing names to work on.  */
GLOBAL const char *files_from_option;

/* Boolean value.  */
GLOBAL int force_local_option;

/* Specified value to be put into tar file in place of stat () results, or
   just -1 if such an override should not take place.  */
GLOBAL gid_t group_option;

/* Boolean value.  */
GLOBAL int ignore_failed_read_option;

/* Boolean value.  */
GLOBAL int ignore_zeros_option;

/* Boolean value.  */
GLOBAL int incremental_option;

/* Specified name of script to run at end of each tape change.  */
GLOBAL const char *info_script_option;

/* Boolean value.  */
GLOBAL int interactive_option;

/* Boolean value.  */
GLOBAL int keep_old_files_option;

/* Specified file name for incremental list.  */
GLOBAL const char *listed_incremental_option;

/* Specified mode change string.  */
GLOBAL struct mode_change *mode_option;

/* Boolean value.  */
GLOBAL int multi_volume_option;

/* The same variable hold the time, whether mtime or ctime.  Just fake a
   non-existing option, for making the code clearer, elsewhere.  */
#define newer_ctime_option newer_mtime_option

/* Specified threshold date and time.  Files having a more recent timestamp
   get archived (also see after_date_option above).  If left to zero, it may
   be interpreted as very low threshold, just usable as such.  */
GLOBAL time_t newer_mtime_option;

/* Boolean value.  */
GLOBAL int no_recurse_option;

/* Boolean value.  */
GLOBAL int numeric_owner_option;

/* Boolean value.  */
GLOBAL int one_file_system_option;

/* Specified value to be put into tar file in place of stat () results, or
   just -1 if such an override should not take place.  */
GLOBAL uid_t owner_option;

/* Boolean value.  */
GLOBAL int recursive_unlink_option;

/* Boolean value.  */
GLOBAL int read_full_records_option;

/* Boolean value.  */
GLOBAL int remove_files_option;

/* Specified remote shell command.  */
GLOBAL const char *rsh_command_option;

/* Boolean value.  */
GLOBAL int same_order_option;

/* Boolean value.  */
GLOBAL int same_owner_option;

/* Boolean value.  */
GLOBAL int same_permissions_option;

/* Boolean value.  */
GLOBAL int show_omitted_dirs_option;

/* Boolean value.  */
GLOBAL int sparse_option;

/* Boolean value.  */
GLOBAL int starting_file_option;

/* Specified maximum byte length of each tape volume (multiple of 1024).  */
GLOBAL tarlong tape_length_option;

/* Boolean value.  */
GLOBAL int to_stdout_option;

/* Boolean value.  */
GLOBAL int totals_option;

/* Boolean value.  */
GLOBAL int touch_option;

/* Boolean value.  */
GLOBAL int unlink_first_option;

/* Count how many times the option has been set, multiple setting yields
   more verbose behavior.  Value 0 means no verbosity, 1 means file name
   only, 2 means file name and all attributes.  More than 2 is just like 2.  */
GLOBAL int verbose_option;

/* Boolean value.  */
GLOBAL int verify_option;

/* Specified name of file containing the volume number.  */
GLOBAL const char *volno_file_option;

/* Specified value or pattern.  */
GLOBAL const char *volume_label_option;


/* Other global variables.  */

/* File descriptor for archive file.  */
GLOBAL int archive;

/* Nonzero when outputting to /dev/null.  */
GLOBAL int dev_null_output;

/* Name of file for the current archive entry.  */
GLOBAL char *current_file_name;

/* Name of link for the current archive entry.  */
GLOBAL char *current_link_name;

/* List of tape drive names, number of such tape drives, allocated number,
   and current cursor in list.  */
GLOBAL const char **archive_name_array;
GLOBAL int archive_names;
GLOBAL int allocated_archive_names;
GLOBAL const char **archive_name_cursor;

/* Structure for keeping track of filenames and lists thereof.  */
struct name
  {
    struct name *next;
    short length;       /* cached strlen(name) */
    char found;         /* a matching file has been found */
    char firstch;       /* first char is literally matched */
    char regexp;        /* this name is a regexp, not literal */
    char *change_dir;       /* set with the -C option */
    char *dir_contents;     /* for incremental_option */
    char fake;          /* dummy entry */
    char name[1];
  };
GLOBAL struct name *namelist;   /* points to first name in list */
GLOBAL struct name *namelast;   /* points to last name in list */

/* Pointer to the start of the scratch space.  */
struct sp_array
  {
    off_t offset;
    int numbytes;
  };
GLOBAL struct sp_array *sparsearray;

/* Initial size of the sparsearray.  */
GLOBAL int sp_array_size;


/* Declarations for each module.  */

/* FIXME: compare.c should not directly handle the following variable,
   instead, this should be done in buffer.c only.  */

enum access_mode
{
  ACCESS_READ,
  ACCESS_WRITE,
  ACCESS_UPDATE
};
extern enum access_mode access_mode;

/* Module buffer.c.  */

extern FILE *stdlis;
extern char *save_name;
extern long save_sizeleft;
extern long save_totsize;
extern int write_archive_to_stdout;

int available_space_after PARAMS ((union block *));
long current_block_ordinal PARAMS ((void));
void close_archive PARAMS ((void));
void closeout_volume_number PARAMS ((void));
union block *find_next_block PARAMS ((void));
void flush_read PARAMS ((void));
void flush_write PARAMS ((void));
void flush_archive PARAMS ((void));
void init_total_written PARAMS ((void));
void init_volume_number PARAMS ((void));
void open_archive PARAMS ((enum access_mode));
void print_total_written PARAMS ((void));
void reset_eof PARAMS ((void));
void set_next_block_after PARAMS ((union block *));

/* Module create.c.  */

void create_archive PARAMS ((void));
void dump_file PARAMS ((char *, int, int));
void finish_header PARAMS ((union block *));
void to_oct PARAMS ((long, int, char *));
void write_eot PARAMS ((void));

/* Module diffarch.c.  */

extern int now_verifying;

void diff_archive PARAMS ((void));
void diff_init PARAMS ((void));
void verify_volume PARAMS ((void));

/* Module extract.c.  */

void extr_init PARAMS ((void));
void extract_archive PARAMS ((void));
void apply_delayed_set_stat PARAMS ((void));

/* Module delete.c.  */

void delete_archive_members PARAMS ((void));

/* Module incremen.c.  */

void collect_and_sort_names PARAMS ((void));
char *get_directory_contents PARAMS ((char *, int));
void write_dir_file PARAMS ((void));
void gnu_restore PARAMS ((int));
void write_directory_file PARAMS ((void));

/* Module list.c.  */

enum read_header
{
  HEADER_STILL_UNREAD,      /* for when read_header has not been called */
  HEADER_SUCCESS,       /* header successfully read and checksummed */
  HEADER_ZERO_BLOCK,        /* zero block where header expected */
  HEADER_END_OF_FILE,       /* true end of file while header expected */
  HEADER_FAILURE        /* ill-formed header, or bad checksum */
};

extern union block *current_header;
extern struct stat current_stat;
extern enum archive_format current_format;

void decode_header PARAMS ((union block *, struct stat *,
                enum archive_format *, int));
long from_oct PARAMS ((int, char *));
void list_archive PARAMS ((void));
void print_for_mkdir PARAMS ((char *, int, int));
void print_header PARAMS ((void));
void read_and PARAMS ((void (*do_) ()));
enum read_header read_header PARAMS ((void));
void skip_extended_headers PARAMS ((void));
void skip_file PARAMS ((long));

/* Module mangle.c.  */

void extract_mangle PARAMS ((void));

/* Module misc.c.  */

void assign_string PARAMS ((char **, const char *));
char *quote_copy_string PARAMS ((const char *));
int unquote_string PARAMS ((char *));

char *merge_sort PARAMS ((char *, int, int, int (*) (char *, char *)));

int is_dot_or_dotdot PARAMS ((const char *));
int remove_any_file PARAMS ((const char *, int));
int maybe_backup_file PARAMS ((const char *, int));
void undo_last_backup PARAMS ((void));

/* Module names.c.  */

void gid_to_gname PARAMS ((gid_t, char gname[GNAME_FIELD_SIZE]));
int gname_to_gid PARAMS ((char gname[GNAME_FIELD_SIZE], gid_t *));
void uid_to_uname PARAMS ((uid_t, char uname[UNAME_FIELD_SIZE]));
int uname_to_uid PARAMS ((char uname[UNAME_FIELD_SIZE], uid_t *));

void init_names PARAMS ((void));
void name_add PARAMS ((const char *));
void name_init PARAMS ((int, char *const *));
void name_term PARAMS ((void));
char *name_next PARAMS ((int change_));
void name_close PARAMS ((void));
void name_gather PARAMS ((void));
void addname PARAMS ((const char *));
int name_match PARAMS ((const char *));
void names_notfound PARAMS ((void));
void name_expand PARAMS ((void));
struct name *name_scan PARAMS ((const char *));
char *name_from_list PARAMS ((void));
void blank_name_list PARAMS ((void));
char *new_name PARAMS ((const char *, const char *));

void add_exclude PARAMS ((char *));
void add_exclude_file PARAMS ((const char *));
int check_exclude PARAMS ((const char *));

/* Module tar.c.  */

int confirm PARAMS ((const char *, const char *));
void request_stdin PARAMS ((const char *));

/* Module update.c.  */

extern char *output_start;

void update_archive PARAMS ((void));

https://git.savannah.gnu.org/cgit/tar.git/tree//src/tar.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/tar.h used in: Step 648, Step 652, Step 654, Step 655, Step 656, Step 657, Step 658, Step 661, Step 662
/* Format of tar archives.
   Copyright (C) 1988, 92, 93, 94, 96, 97 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.  */

/* GNU tar Archive Format description.  */

/* If OLDGNU_COMPATIBILITY is not zero, tar produces archives which, by
   default, are readable by older versions of GNU tar.  This can be
   overriden by using --posix; in this case, POSIXLY_CORRECT in environment
   may be set for enforcing stricter conformance.  If OLDGNU_COMPATIBILITY
   is zero or undefined, tar will eventually produces archives which, by
   default, POSIX compatible; then either using --posix or defining
   POSIXLY_CORRECT enforces stricter conformance.

   This #define will disappear in a few years.  FP, June 1995.  */
#define OLDGNU_COMPATIBILITY 1

/*---------------------------------------------.
| `tar' Header Block, from POSIX 1003.1-1990.  |
`---------------------------------------------*/

/* POSIX header.  */

struct posix_header
{               /* byte offset */
  char name[100];       /*   0 */
  char mode[8];         /* 100 */
  char uid[8];          /* 108 */
  char gid[8];          /* 116 */
  char size[12];        /* 124 */
  char mtime[12];       /* 136 */
  char chksum[8];       /* 148 */
  char typeflag;        /* 156 */
  char linkname[100];       /* 157 */
  char magic[6];        /* 257 */
  char version[2];      /* 263 */
  char uname[32];       /* 265 */
  char gname[32];       /* 297 */
  char devmajor[8];     /* 329 */
  char devminor[8];     /* 337 */
  char prefix[155];     /* 345 */
                /* 500 */
};

#define TMAGIC   "ustar"    /* ustar and a null */
#define TMAGLEN  6
#define TVERSION "00"       /* 00 and no null */
#define TVERSLEN 2

/* Values used in typeflag field.  */
#define REGTYPE  '0'        /* regular file */
#define AREGTYPE '\0'       /* regular file */
#define LNKTYPE  '1'        /* link */
#define SYMTYPE  '2'        /* reserved */
#define CHRTYPE  '3'        /* character special */
#define BLKTYPE  '4'        /* block special */
#define DIRTYPE  '5'        /* directory */
#define FIFOTYPE '6'        /* FIFO special */
#define CONTTYPE '7'        /* reserved */

/* Bits used in the mode field, values in octal.  */
#define TSUID    04000      /* set UID on execution */
#define TSGID    02000      /* set GID on execution */
#define TSVTX    01000      /* reserved */
                /* file permissions */
#define TUREAD   00400      /* read by owner */
#define TUWRITE  00200      /* write by owner */
#define TUEXEC   00100      /* execute/search by owner */
#define TGREAD   00040      /* read by group */
#define TGWRITE  00020      /* write by group */
#define TGEXEC   00010      /* execute/search by group */
#define TOREAD   00004      /* read by other */
#define TOWRITE  00002      /* write by other */
#define TOEXEC   00001      /* execute/search by other */

/*-------------------------------------.
| `tar' Header Block, GNU extensions.  |
`-------------------------------------*/

/* In GNU tar, SYMTYPE is for to symbolic links, and CONTTYPE is for
   contiguous files, so maybe disobeying the `reserved' comment in POSIX
   header description.  I suspect these were meant to be used this way, and
   should not have really been `reserved' in the published standards.  */

/* *BEWARE* *BEWARE* *BEWARE* that the following information is still
   boiling, and may change.  Even if the OLDGNU format description should be
   accurate, the so-called GNU format is not yet fully decided.  It is
   surely meant to use only extensions allowed by POSIX, but the sketch
   below repeats some ugliness from the OLDGNU format, which should rather
   go away.  Sparse files should be saved in such a way that they do *not*
   require two passes at archive creation time.  Huge files get some POSIX
   fields to overflow, alternate solutions have to be sought for this.  */

/* Descriptor for a single file hole.  */

struct sparse
{               /* byte offset */
  char offset[12];      /*   0 */
  char numbytes[12];        /*  12 */
                /*  24 */
};

/* Sparse files are not supported in POSIX ustar format.  For sparse files
   with a POSIX header, a GNU extra header is provided which holds overall
   sparse information and a few sparse descriptors.  When an old GNU header
   replaces both the POSIX header and the GNU extra header, it holds some
   sparse descriptors too.  Whether POSIX or not, if more sparse descriptors
   are still needed, they are put into as many successive sparse headers as
   necessary.  The following constants tell how many sparse descriptors fit
   in each kind of header able to hold them.  */

#define SPARSES_IN_EXTRA_HEADER  16
#define SPARSES_IN_OLDGNU_HEADER 4
#define SPARSES_IN_SPARSE_HEADER 21

/* The GNU extra header contains some information GNU tar needs, but not
   foreseen in POSIX header format.  It is only used after a POSIX header
   (and never with old GNU headers), and immediately follows this POSIX
   header, when typeflag is a letter rather than a digit, so signaling a GNU
   extension.  */

struct extra_header
{               /* byte offset */
  char atime[12];       /*   0 */
  char ctime[12];       /*  12 */
  char offset[12];      /*  24 */
  char realsize[12];        /*  36 */
  char longnames[4];        /*  48 */
  char unused_pad1[68];     /*  52 */
  struct sparse sp[SPARSES_IN_EXTRA_HEADER];
                /* 120 */
  char isextended;      /* 504 */
                /* 505 */
};

/* Extension header for sparse files, used immediately after the GNU extra
   header, and used only if all sparse information cannot fit into that
   extra header.  There might even be many such extension headers, one after
   the other, until all sparse information has been recorded.  */

struct sparse_header
{               /* byte offset */
  struct sparse sp[SPARSES_IN_SPARSE_HEADER];
                /*   0 */
  char isextended;      /* 504 */
                /* 505 */
};

/* The old GNU format header conflicts with POSIX format in such a way that
   POSIX archives may fool old GNU tar's, and POSIX tar's might well be
   fooled by old GNU tar archives.  An old GNU format header uses the space
   used by the prefix field in a POSIX header, and cumulates information
   normally found in a GNU extra header.  With an old GNU tar header, we
   never see any POSIX header nor GNU extra header.  Supplementary sparse
   headers are allowed, however.  */

struct oldgnu_header
{               /* byte offset */
  char unused_pad1[345];    /*   0 */
  char atime[12];       /* 345 */
  char ctime[12];       /* 357 */
  char offset[12];      /* 369 */
  char longnames[4];        /* 381 */
  char unused_pad2;     /* 385 */
  struct sparse sp[SPARSES_IN_OLDGNU_HEADER];
                /* 386 */
  char isextended;      /* 482 */
  char realsize[12];        /* 483 */
                /* 495 */
};

/* OLDGNU_MAGIC uses both magic and version fields, which are contiguous.
   Found in an archive, it indicates an old GNU header format, which will be
   hopefully become obsolescent.  With OLDGNU_MAGIC, uname and gname are
   valid, though the header is not truly POSIX conforming.  */
#define OLDGNU_MAGIC "ustar  "  /* 7 chars and a null */

/* The standards committee allows only capital A through capital Z for
   user-defined expansion.  */

/* This is a dir entry that contains the names of files that were in the
   dir at the time the dump was made.  */
#define GNUTYPE_DUMPDIR 'D'

/* Identifies the *next* file on the tape as having a long linkname.  */
#define GNUTYPE_LONGLINK 'K'

/* Identifies the *next* file on the tape as having a long name.  */
#define GNUTYPE_LONGNAME 'L'

/* This is the continuation of a file that began on another volume.  */
#define GNUTYPE_MULTIVOL 'M'

/* For storing filenames that do not fit into the main header.  */
#define GNUTYPE_NAMES 'N'

/* This is for sparse files.  */
#define GNUTYPE_SPARSE 'S'

/* This file is a tape/volume header.  Ignore it on extraction.  */
#define GNUTYPE_VOLHDR 'V'

/*--------------------------------------.
| tar Header Block, overall structure.  |
`--------------------------------------*/

/* tar files are made in basic blocks of this size.  */
#define BLOCKSIZE 512

enum archive_format
{
  DEFAULT_FORMAT,       /* format to be decided later */
  V7_FORMAT,            /* old V7 tar format */
  OLDGNU_FORMAT,        /* GNU format as per before tar 1.12 */
  POSIX_FORMAT,         /* restricted, pure POSIX format */
  GNU_FORMAT            /* POSIX format with GNU extensions */
};

union block
{
  char buffer[BLOCKSIZE];
  struct posix_header header;
  struct extra_header extra_header;
  struct oldgnu_header oldgnu_header;
  struct sparse_header sparse_header;
};

/* End of Format description.  */

https://git.savannah.gnu.org/cgit/tar.git/tree//src/buffer.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/buffer.c used in: Step 649
/* Buffer management for tar.
   Copyright (C) 1988, 92, 93, 94, 96, 97 Free Software Foundation, Inc.
   Written by John Gilmore, on 1985-08-25.

   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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include "system.h"

#include <signal.h>
#include <time.h>
time_t time ();

#if MSDOS
# include <process.h>
#endif

#if XENIX
# include <sys/inode.h>
#endif

#ifndef FNM_LEADING_DIR
# include <fnmatch.h>
#endif

#include "common.h"
#include "rmt.h"

#define DEBUG_FORK 0        /* if nonzero, childs are born stopped */

#define STDIN 0         /* standard input  file descriptor */
#define STDOUT 1        /* standard output file descriptor */

#define PREAD 0         /* read file descriptor from pipe() */
#define PWRITE 1        /* write file descriptor from pipe() */

/* Number of retries before giving up on read.  */
#define READ_ERROR_MAX 10

/* Globbing pattern to append to volume label if initial match failed.  */
#define VOLUME_LABEL_APPEND " Volume [1-9]*"


/* Variables.  */

static tarlong total_written;   /* bytes written on all volumes */
static tarlong bytes_written;   /* bytes written on this volume */

/* FIXME: The following four variables should ideally be static to this
   module.  However, this cannot be done yet, as update.c uses the first
   three a lot, and compare.c uses the fourth.  The cleanup continues!  */

union block *record_start;  /* start of record of archive */
union block *record_end;    /* last+1 block of archive record */
union block *current_block; /* current block of archive */
enum access_mode access_mode;   /* how do we handle the archive */
static struct stat archive_stat; /* stat block for archive file */

static long record_start_block; /* block ordinal at record_start */

/* Where we write list messages (not errors, not interactions) to.  Stdout
   unless we're writing a pipe, in which case stderr.  */
FILE *stdlis;

static void backspace_output PARAMS ((void));
static int new_volume PARAMS ((enum access_mode));
static void write_error PARAMS ((int));
static void read_error PARAMS ((void));

#if !MSDOS
/* Obnoxious test to see if dimwit is trying to dump the archive.  */
dev_t ar_dev;
ino_t ar_ino;
#endif

/* PID of child program, if compress_option or remote archive access.  */
static int child_pid = 0;

/* Error recovery stuff  */
static int read_error_count;

/* Have we hit EOF yet?  */
static int hit_eof;

/* Checkpointing counter */
static int checkpoint;

/* We're reading, but we just read the last block and its time to update.  */
/* As least EXTERN like this one as possible.  FIXME!  */
extern int time_to_start_writing;

int file_to_switch_to = -1; /* if remote update, close archive, and use
                   this descriptor to write to */

static int volno = 1;       /* which volume of a multi-volume tape we're
                   on */
static int global_volno = 1;    /* volume number to print in external
                   messages */

/* The pointer save_name, which is set in function dump_file() of module
   create.c, points to the original long filename instead of the new,
   shorter mangled name that is set in start_header() of module create.c.
   The pointer save_name is only used in multi-volume mode when the file
   being processed is non-sparse; if a file is split between volumes, the
   save_name is used in generating the LF_MULTIVOL record on the second
   volume.  (From Pierce Cantrell, 1991-08-13.)  */

char *save_name;        /* name of the file we are currently writing */
long save_totsize;      /* total size of file we are writing, only
                   valid if save_name is non NULL */
long save_sizeleft;     /* where we are in the file we are writing,
                   only valid if save_name is nonzero */

int write_archive_to_stdout = 0;

/* Used by flush_read and flush_write to store the real info about saved
   names.  */
static char *real_s_name = NULL;
static long real_s_totsize;
static long real_s_sizeleft;


/* Functions.  */

#if DEBUG_FORK

static pid_t
myfork (void)
{
  pid_t result = fork();

  if (result == 0)
    kill (getpid (), SIGSTOP);
  return result;
}

# define fork myfork

#endif /* DEBUG FORK */

void
init_total_written (void)
{
  clear_tarlong (total_written);
  clear_tarlong (bytes_written);
}

void
print_total_written (void)
{
  fprintf (stderr, _("Total bytes written: "));
  print_tarlong (total_written, stderr);
  fprintf (stderr, "\n");
}

/*--------------------------------------------------------.
| Compute and return the block ordinal at current_block.  |
`--------------------------------------------------------*/

long
current_block_ordinal (void)
{
  return record_start_block + (current_block - record_start);
}

/*------------------------------------------------------------------.
| If the EOF flag is set, reset it, as well as current_block, etc.  |
`------------------------------------------------------------------*/

void
reset_eof (void)
{
  if (hit_eof)
    {
      hit_eof = 0;
      current_block = record_start;
      record_end = record_start + blocking_factor;
      access_mode = ACCESS_WRITE;
    }
}

/*-------------------------------------------------------------------------.
| Return the location of the next available input or output block.     |
| Return NULL for EOF.  Once we have returned NULL, we just keep returning |
| it, to avoid accidentally going on to the next file on the tape.     |
`-------------------------------------------------------------------------*/

union block *
find_next_block (void)
{
  if (current_block == record_end)
    {
      if (hit_eof)
    return NULL;
      flush_archive ();
      if (current_block == record_end)
    {
      hit_eof = 1;
      return NULL;
    }
    }
  return current_block;
}

/*------------------------------------------------------.
| Indicate that we have used all blocks up thru BLOCK.  |
|                               |
| FIXME: should the arg have an off-by-1?           |
`------------------------------------------------------*/

void
set_next_block_after (union block *block)
{
  while (block >= current_block)
    current_block++;

  /* Do *not* flush the archive here.  If we do, the same argument to
     set_next_block_after could mean the next block (if the input record
     is exactly one block long), which is not what is intended.  */

  if (current_block > record_end)
    abort ();
}

/*------------------------------------------------------------------------.
| Return the number of bytes comprising the space between POINTER through |
| the end of the current buffer of blocks.  This space is available for   |
| filling with data, or taking data from.  POINTER is usually (but not    |
| always) the result previous find_next_block call.           |
`------------------------------------------------------------------------*/

int
available_space_after (union block *pointer)
{
  return (int) (record_end->buffer - pointer->buffer);
}

/*------------------------------------------------------------------.
| Close file having descriptor FD, and abort if close unsucessful.  |
`------------------------------------------------------------------*/

static void
xclose (int fd)
{
  if (close (fd) < 0)
    FATAL_ERROR ((0, errno, _("Cannot close file #%d"), fd));
}

/*-----------------------------------------------------------------------.
| Duplicate file descriptor FROM into becoming INTO, or else, issue  |
| MESSAGE.  INTO is closed first and has to be the next available slot.  |
`-----------------------------------------------------------------------*/

static void
xdup2 (int from, int into, const char *message)
{
  if (from != into)
    {
      int status = close (into);

      if (status < 0 && errno != EBADF)
    FATAL_ERROR ((0, errno, _("Cannot close descriptor %d"), into));
      status = dup (from);
      if (status != into)
    FATAL_ERROR ((0, errno, _("Cannot properly duplicate %s"), message));
      xclose (from);
    }
}

#if MSDOS

/*-------------------------------------------------------.
| Set ARCHIVE for writing, then compressing an archive.  |
`-------------------------------------------------------*/

static void
child_open_for_compress (void)
{
  FATAL_ERROR ((0, 0, _("Cannot use compressed or remote archives")));
}

/*---------------------------------------------------------.
| Set ARCHIVE for uncompressing, then reading an archive.  |
`---------------------------------------------------------*/

static void
child_open_for_uncompress (void)
{
  FATAL_ERROR ((0, 0, _("Cannot use compressed or remote archives")));
}

#else /* not MSDOS */

/*---------------------------------------------------------------------.
| Return nonzero if NAME is the name of a regular file, or if the file |
| does not exist (so it would be created as a regular file).           |
`---------------------------------------------------------------------*/

static int
is_regular_file (const char *name)
{
  struct stat stbuf;

  if (stat (name, &stbuf) < 0)
    return 1;

  if (S_ISREG (stbuf.st_mode))
    return 1;

  return 0;
}

/*-------------------------------------------------------.
| Set ARCHIVE for writing, then compressing an archive.  |
`-------------------------------------------------------*/

static void
child_open_for_compress (void)
{
  int parent_pipe[2];
  int child_pipe[2];
  int grandchild_pid;

  if (pipe (parent_pipe) < 0)
    FATAL_ERROR ((0, errno, _("Cannot open pipe")));

  child_pid = fork ();
  if (child_pid < 0)
    FATAL_ERROR ((0, errno, _("Cannot fork")));

  if (child_pid > 0)
    {
      /* The parent tar is still here!  Just clean up.  */

      archive = parent_pipe[PWRITE];
      xclose (parent_pipe[PREAD]);
      return;
    }

  /* The new born child tar is here!  */

  program_name = _("tar (child)");

  xdup2 (parent_pipe[PREAD], STDIN, _("(child) Pipe to stdin"));
  xclose (parent_pipe[PWRITE]);

  /* Check if we need a grandchild tar.  This happens only if either:
     a) we are writing stdout: to force reblocking;
     b) the file is to be accessed by rmt: compressor doesn't know how;
     c) the file is not a plain file.  */

  if (strcmp (archive_name_array[0], "-") != 0
      && !_remdev (archive_name_array[0])
      && is_regular_file (archive_name_array[0]))
    {
      if (backup_option)
    maybe_backup_file (archive_name_array[0], 1);

      /* We don't need a grandchild tar.  Open the archive and launch the
     compressor.  */

      archive = creat (archive_name_array[0], 0666);
      if (archive < 0)
    {
      int saved_errno = errno;

      if (backup_option)
        undo_last_backup ();
      FATAL_ERROR ((0, saved_errno, _("Cannot open archive %s"),
            archive_name_array[0]));
    }
      xdup2 (archive, STDOUT, _("Archive to stdout"));
      execlp (use_compress_program_option, use_compress_program_option,
          (char *) 0);
      FATAL_ERROR ((0, errno, _("Cannot exec %s"),
            use_compress_program_option));
    }

  /* We do need a grandchild tar.  */

  if (pipe (child_pipe) < 0)
    FATAL_ERROR ((0, errno, _("Cannot open pipe")));

  grandchild_pid = fork ();
  if (grandchild_pid < 0)
    FATAL_ERROR ((0, errno, _("Child cannot fork")));

  if (grandchild_pid > 0)
    {
      /* The child tar is still here!  Launch the compressor.  */

      xdup2 (child_pipe[PWRITE], STDOUT, _("((child)) Pipe to stdout"));
      xclose (child_pipe[PREAD]);
      execlp (use_compress_program_option, use_compress_program_option,
          (char *) 0);
      FATAL_ERROR ((0, errno, _("Cannot exec %s"),
            use_compress_program_option));
    }

  /* The new born grandchild tar is here!  */

  program_name = _("tar (grandchild)");

  /* Prepare for reblocking the data from the compressor into the archive.  */

  xdup2 (child_pipe[PREAD], STDIN, _("(grandchild) Pipe to stdin"));
  xclose (child_pipe[PWRITE]);

  if (strcmp (archive_name_array[0], "-") == 0)
    archive = STDOUT;
  else
    archive = rmtcreat (archive_name_array[0], 0666, rsh_command_option);
  if (archive < 0)
    FATAL_ERROR ((0, errno, _("Cannot open archive %s"),
          archive_name_array[0]));

  /* Let's read out of the stdin pipe and write an archive.  */

  while (1)
    {
      int status = 0;
      char *cursor;
      int length;

      /* Assemble a record.  */

      for (length = 0, cursor = record_start->buffer;
       length < record_size;
       length += status, cursor += status)
    {
      int size = record_size - length;

      if (size < BLOCKSIZE)
        size = BLOCKSIZE;
      status = read (STDIN, cursor, (size_t) size);
      if (status <= 0)
        break;
    }

      if (status < 0)
    FATAL_ERROR ((0, errno, _("Cannot read from compression program")));

      /* Copy the record.  */

      if (status == 0)
    {
      /* We hit the end of the file.  Write last record at
         full length, as the only role of the grandchild is
         doing proper reblocking.  */

      if (length > 0)
        {
          memset (record_start->buffer + length, 0,
              (size_t) record_size - length);
          status = rmtwrite (archive, record_start->buffer,
                 (unsigned int) record_size);
          if (status != record_size)
        write_error (status);
        }

      /* There is nothing else to read, break out.  */
      break;
    }

      status = rmtwrite (archive, record_start->buffer,
             (unsigned int) record_size);
      if (status != record_size)
    write_error (status);
    }

#if 0
  close_archive ();
#endif
  exit (exit_status);
}

/*---------------------------------------------------------.
| Set ARCHIVE for uncompressing, then reading an archive.  |
`---------------------------------------------------------*/

static void
child_open_for_uncompress (void)
{
  int parent_pipe[2];
  int child_pipe[2];
  int grandchild_pid;

  if (pipe (parent_pipe) < 0)
    FATAL_ERROR ((0, errno, _("Cannot open pipe")));

  child_pid = fork ();
  if (child_pid < 0)
    FATAL_ERROR ((0, errno, _("Cannot fork")));

  if (child_pid > 0)
    {
      /* The parent tar is still here!  Just clean up.  */

      read_full_records_option = 1;
      archive = parent_pipe[PREAD];
      xclose (parent_pipe[PWRITE]);
      return;
    }

  /* The new born child tar is here!  */

  program_name = _("tar (child)");

  xdup2 (parent_pipe[PWRITE], STDOUT, _("(child) Pipe to stdout"));
  xclose (parent_pipe[PREAD]);

  /* Check if we need a grandchild tar.  This happens only if either:
     a) we're reading stdin: to force unblocking;
     b) the file is to be accessed by rmt: compressor doesn't know how;
     c) the file is not a plain file.  */

  if (strcmp (archive_name_array[0], "-") != 0
      && !_remdev (archive_name_array[0])
      && is_regular_file (archive_name_array[0]))
    {
      /* We don't need a grandchild tar.  Open the archive and lauch the
     uncompressor.  */

      archive = open (archive_name_array[0], O_RDONLY | O_BINARY, 0666);
      if (archive < 0)
    FATAL_ERROR ((0, errno, _("Cannot open archive %s"),
              archive_name_array[0]));
      xdup2 (archive, STDIN, _("Archive to stdin"));
      execlp (use_compress_program_option, use_compress_program_option,
          "-d", (char *) 0);
      FATAL_ERROR ((0, errno, _("Cannot exec %s"),
            use_compress_program_option));
    }

  /* We do need a grandchild tar.  */

  if (pipe (child_pipe) < 0)
    FATAL_ERROR ((0, errno, _("Cannot open pipe")));

  grandchild_pid = fork ();
  if (grandchild_pid < 0)
    FATAL_ERROR ((0, errno, _("Child cannot fork")));

  if (grandchild_pid > 0)
    {
      /* The child tar is still here!  Launch the uncompressor.  */

      xdup2 (child_pipe[PREAD], STDIN, _("((child)) Pipe to stdin"));
      xclose (child_pipe[PWRITE]);
      execlp (use_compress_program_option, use_compress_program_option,
          "-d", (char *) 0);
      FATAL_ERROR ((0, errno, _("Cannot exec %s"),
            use_compress_program_option));
    }

  /* The new born grandchild tar is here!  */

  program_name = _("tar (grandchild)");

  /* Prepare for unblocking the data from the archive into the uncompressor.  */

  xdup2 (child_pipe[PWRITE], STDOUT, _("(grandchild) Pipe to stdout"));
  xclose (child_pipe[PREAD]);

  if (strcmp (archive_name_array[0], "-") == 0)
    archive = STDIN;
  else
    archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY,
               0666, rsh_command_option);
  if (archive < 0)
    FATAL_ERROR ((0, errno, _("Cannot open archive %s"),
          archive_name_array[0]));

  /* Let's read the archive and pipe it into stdout.  */

  while (1)
    {
      char *cursor;
      int maximum;
      int count;
      int status;

      read_error_count = 0;

    error_loop:
      status = rmtread (archive, record_start->buffer,
            (unsigned int) (record_size));
      if (status < 0)
    {
      read_error ();
      goto error_loop;
    }
      if (status == 0)
    break;
      cursor = record_start->buffer;
      maximum = status;
      while (maximum)
    {
      count = maximum < BLOCKSIZE ? maximum : BLOCKSIZE;
      status = write (STDOUT, cursor, (size_t) count);
      if (status < 0)
        FATAL_ERROR ((0, errno, _("\
Cannot write to compression program")));

      if (status != count)
        {
          ERROR ((0, 0, _("\
Write to compression program short %d bytes"),
              count - status));
          count = status;
        }

      cursor += count;
      maximum -= count;
    }
    }

#if 0
  close_archive ();
#endif
  exit (exit_status);
}

#endif /* not MSDOS */

/*--------------------------------------------------------------------------.
| Check the LABEL block against the volume label, seen as a globbing        |
| pattern.  Return true if the pattern matches.  In case of failure, retry  |
| matching a volume sequence number before giving up in multi-volume mode.  |
`--------------------------------------------------------------------------*/

static int
check_label_pattern (union block *label)
{
  char *string;
  int result;

  if (fnmatch (volume_label_option, label->header.name, 0) == 0)
    return 1;

  if (!multi_volume_option)
    return 0;

  string = xmalloc (strlen (volume_label_option)
            + sizeof VOLUME_LABEL_APPEND + 1);
  strcpy (string, volume_label_option);
  strcat (string, VOLUME_LABEL_APPEND);
  result = fnmatch (string, label->header.name, 0) == 0;
  free (string);
  return result;
}

/*------------------------------------------------------------------------.
| Open an archive file.  The argument specifies whether we are reading or |
| writing, or both.                           |
`------------------------------------------------------------------------*/

void
open_archive (enum access_mode access)
{
  int backed_up_flag = 0;

  stdlis = to_stdout_option ? stderr : stdout;

  if (record_size == 0)
    FATAL_ERROR ((0, 0, _("Invalid value for record_size")));

  if (archive_names == 0)
    FATAL_ERROR ((0, 0, _("No archive name given")));

  current_file_name = NULL;
  current_link_name = NULL;

  /* FIXME: According to POSIX.1, PATH_MAX may well not be a compile-time
     constant, and the value from sysconf (_SC_PATH_MAX) may well not be any
     size that is reasonable to allocate a buffer.  In the GNU system, there
     is no fixed limit.  The only correct thing to do is to use dynamic
     allocation.  (Roland McGrath)  */

  if (!real_s_name)
    real_s_name = (char *) xmalloc (PATH_MAX);
  /* FIXME: real_s_name is never freed.  */

  save_name = NULL;

  if (multi_volume_option)
    {
      record_start
    = (union block *) valloc ((unsigned) (record_size + (2 * BLOCKSIZE)));
      if (record_start)
    record_start += 2;
    }
  else
    record_start = (union block *) valloc ((unsigned) record_size);
  if (!record_start)
    FATAL_ERROR ((0, 0, _("Could not allocate memory for blocking factor %d"),
          blocking_factor));

  current_block = record_start;
  record_end = record_start + blocking_factor;
  /* When updating the archive, we start with reading.  */
  access_mode = access == ACCESS_UPDATE ? ACCESS_READ : access;

  if (multi_volume_option && verify_option)
    FATAL_ERROR ((0, 0, _("Cannot verify multi-volume archives")));

  if (use_compress_program_option)
    {
      if (multi_volume_option)
    FATAL_ERROR ((0, 0, _("Cannot use multi-volume compressed archives")));
      if (verify_option)
    FATAL_ERROR ((0, 0, _("Cannot verify compressed archives")));

      switch (access)
    {
    case ACCESS_READ:
      child_open_for_uncompress ();
      break;

    case ACCESS_WRITE:
      child_open_for_compress ();
      break;

    case ACCESS_UPDATE:
      FATAL_ERROR ((0, 0, _("Cannot update compressed archives")));
      break;
    }

      if (access == ACCESS_WRITE && strcmp (archive_name_array[0], "-") == 0)
    stdlis = stderr;
    }
  else if (strcmp (archive_name_array[0], "-") == 0)
    {
      read_full_records_option = 1; /* could be a pipe, be safe */
      if (verify_option)
    FATAL_ERROR ((0, 0, _("Cannot verify stdin/stdout archive")));

      switch (access)
    {
    case ACCESS_READ:
      archive = STDIN;
      break;

    case ACCESS_WRITE:
      archive = STDOUT;
      stdlis = stderr;
      break;

    case ACCESS_UPDATE:
      archive = STDIN;
      stdlis = stderr;
      write_archive_to_stdout = 1;
      break;
    }
    }
  else if (verify_option)
    archive = rmtopen (archive_name_array[0], O_RDWR | O_CREAT | O_BINARY,
               0666, rsh_command_option);
  else
    switch (access)
      {
      case ACCESS_READ:
    archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY, 0666,
               rsh_command_option);
    break;

      case ACCESS_WRITE:
    if (backup_option)
      {
        maybe_backup_file (archive_name_array[0], 1);
        backed_up_flag = 1;
      }
    archive = rmtcreat (archive_name_array[0], 0666, rsh_command_option);
    break;

      case ACCESS_UPDATE:
    archive = rmtopen (archive_name_array[0], O_RDWR | O_CREAT | O_BINARY,
               0666, rsh_command_option);
    break;
      }

  if (archive < 0)
    {
      int saved_errno = errno;

      if (backed_up_flag)
    undo_last_backup ();
      FATAL_ERROR ((0, saved_errno, _("Cannot open %s"),
            archive_name_array[0]));
    }

#if !MSDOS

  fstat (archive, &archive_stat);

  /* Detect if outputting to "/dev/null".  */
  {
    struct stat dev_null_stat;

    stat ("/dev/null", &dev_null_stat);
    dev_null_output = (S_ISCHR (archive_stat.st_mode)
               && archive_stat.st_rdev == dev_null_stat.st_rdev);
  }

  if (!_isrmt (archive) && S_ISREG (archive_stat.st_mode))
    {
      ar_dev = archive_stat.st_dev;
      ar_ino = archive_stat.st_ino;
    }

#endif /* not MSDOS */

#if MSDOS
  setmode (archive, O_BINARY);
#endif

  switch (access)
    {
    case ACCESS_READ:
    case ACCESS_UPDATE:
      record_end = record_start; /* set up for 1st record = # 0 */
      find_next_block ();   /* read it in, check for EOF */

      if (volume_label_option)
    {
      union block *label = find_next_block ();

      if (!label)
        FATAL_ERROR ((0, 0, _("Archive not labelled to match `%s'"),
              volume_label_option));
      if (!check_label_pattern (label))
        FATAL_ERROR ((0, 0, _("Volume `%s' does not match `%s'"),
              label->header.name, volume_label_option));
    }
      break;

    case ACCESS_WRITE:
      if (volume_label_option)
    {
      memset ((void *) record_start, 0, BLOCKSIZE);
      if (multi_volume_option)
        sprintf (record_start->header.name, "%s Volume 1",
             volume_label_option);
      else
        strcpy (record_start->header.name, volume_label_option);

      assign_string (&current_file_name, record_start->header.name);

      record_start->header.typeflag = GNUTYPE_VOLHDR;
      to_oct (time (0), 1 + 12, record_start->header.mtime);
      finish_header (record_start);
#if 0
      current_block++;
#endif
    }
      break;
    }
}

/*--------------------------------------.
| Perform a write to flush the buffer.  |
`--------------------------------------*/

void
flush_write (void)
{
  int copy_back;
  int status;

  if (checkpoint_option && !(++checkpoint % 10))
    WARN ((0, 0, _("Write checkpoint %d"), checkpoint));

  if (!zerop_tarlong (tape_length_option)
      && !lessp_tarlong (bytes_written, tape_length_option))
    {
      errno = ENOSPC;       /* FIXME: errno should be read-only */
      status = 0;
    }
  else if (dev_null_output)
    status = record_size;
  else
    status = rmtwrite (archive, record_start->buffer,
               (unsigned int) record_size);
  if (status != record_size && !multi_volume_option)
    write_error (status);
  else if (totals_option)
    add_to_tarlong (total_written, record_size);

  if (status > 0)
    add_to_tarlong (bytes_written, status);

  if (status == record_size)
    {
      if (multi_volume_option)
    {
      char *cursor;

      if (!save_name)
        {
          real_s_name[0] = '\0';
          real_s_totsize = 0;
          real_s_sizeleft = 0;
          return;
        }

      cursor = save_name;
#if MSDOS
      if (cursor[1] == ':')
        cursor += 2;
#endif
      while (*cursor == '/')
        cursor++;

      strcpy (real_s_name, cursor);
      real_s_totsize = save_totsize;
      real_s_sizeleft = save_sizeleft;
    }
      return;
    }

  /* We're multivol.  Panic if we didn't get the right kind of response.  */

  /* ENXIO is for the UNIX PC.  */
  if (status < 0 && errno != ENOSPC && errno != EIO && errno != ENXIO)
    write_error (status);

  /* If error indicates a short write, we just move to the next tape.  */

  if (!new_volume (ACCESS_WRITE))
    return;

  clear_tarlong (bytes_written);

  if (volume_label_option && real_s_name[0])
    {
      copy_back = 2;
      record_start -= 2;
    }
  else if (volume_label_option || real_s_name[0])
    {
      copy_back = 1;
      record_start--;
    }
  else
    copy_back = 0;

  if (volume_label_option)
    {
      memset ((void *) record_start, 0, BLOCKSIZE);
      sprintf (record_start->header.name, "%s Volume %d", volume_label_option, volno);
      to_oct (time (0), 1 + 12, record_start->header.mtime);
      record_start->header.typeflag = GNUTYPE_VOLHDR;
      finish_header (record_start);
    }

  if (real_s_name[0])
    {
      int tmp;

      if (volume_label_option)
    record_start++;

      memset ((void *) record_start, 0, BLOCKSIZE);

      /* FIXME: Michael P Urban writes: [a long name file] is being written
     when a new volume rolls around [...]  Looks like the wrong value is
     being preserved in real_s_name, though.  */

      strcpy (record_start->header.name, real_s_name);
      record_start->header.typeflag = GNUTYPE_MULTIVOL;
      to_oct ((long) real_s_sizeleft, 1 + 12,
          record_start->header.size);
      to_oct ((long) real_s_totsize - real_s_sizeleft,
          1 + 12, record_start->oldgnu_header.offset);
      tmp = verbose_option;
      verbose_option = 0;
      finish_header (record_start);
      verbose_option = tmp;

      if (volume_label_option)
    record_start--;
    }

  status = rmtwrite (archive, record_start->buffer,
             (unsigned int) record_size);
  if (status != record_size)
    write_error (status);
  else if (totals_option)
    add_to_tarlong (total_written, record_size);

  add_to_tarlong (bytes_written, record_size);
  if (copy_back)
    {
      record_start += copy_back;
      memcpy ((void *) current_block,
          (void *) (record_start + blocking_factor - copy_back),
          (size_t) (copy_back * BLOCKSIZE));
      current_block += copy_back;

      if (real_s_sizeleft >= copy_back * BLOCKSIZE)
    real_s_sizeleft -= copy_back * BLOCKSIZE;
      else if ((real_s_sizeleft + BLOCKSIZE - 1) / BLOCKSIZE <= copy_back)
    real_s_name[0] = '\0';
      else
    {
      char *cursor = save_name;

#if MSDOS
      if (cursor[1] == ':')
        cursor += 2;
#endif
      while (*cursor == '/')
        cursor++;

      strcpy (real_s_name, cursor);
      real_s_sizeleft = save_sizeleft;
      real_s_totsize = save_totsize;
    }
      copy_back = 0;
    }
}

/*---------------------------------------------------------------------.
| Handle write errors on the archive.  Write errors are always fatal.  |
| Hitting the end of a volume does not cause a write error unless the  |
| write was the first record of the volume.                |
`---------------------------------------------------------------------*/

static void
write_error (int status)
{
  int saved_errno = errno;

  /* It might be useful to know how much was written before the error
     occured.  Beware that mere printing maybe change errno value.  */
  if (totals_option)
    print_total_written ();

  if (status < 0)
    FATAL_ERROR ((0, saved_errno, _("Cannot write to %s"),
          *archive_name_cursor));
  else
    FATAL_ERROR ((0, 0, _("Only wrote %u of %u bytes to %s"),
          status, record_size, *archive_name_cursor));
}

/*-------------------------------------------------------------------.
| Handle read errors on the archive.  If the read should be retried, |
| returns to the caller.                         |
`-------------------------------------------------------------------*/

static void
read_error (void)
{
  WARN ((0, errno, _("Read error on %s"), *archive_name_cursor));

  if (record_start_block == 0)
    FATAL_ERROR ((0, 0, _("At beginning of tape, quitting now")));

  /* Read error in mid archive.  We retry up to READ_ERROR_MAX times and
     then give up on reading the archive.  */

  if (read_error_count++ > READ_ERROR_MAX)
    FATAL_ERROR ((0, 0, _("Too many errors, quitting")));
  return;
}

/*-------------------------------------.
| Perform a read to flush the buffer.  |
`-------------------------------------*/

void
flush_read (void)
{
  int status;           /* result from system call */
  int left;         /* bytes left */
  char *more;           /* pointer to next byte to read */

  if (checkpoint_option && !(++checkpoint % 10))
    WARN ((0, 0, _("Read checkpoint %d"), checkpoint));

  /* Clear the count of errors.  This only applies to a single call to
     flush_read.  */

  read_error_count = 0;     /* clear error count */

  if (write_archive_to_stdout && record_start_block != 0)
    {
      status = rmtwrite (1, record_start->buffer, (unsigned int) record_size);
      if (status != record_size)
    write_error (status);
    }
  if (multi_volume_option)
    if (save_name)
      {
    char *cursor = save_name;

#if MSDOS
    if (cursor[1] == ':')
      cursor += 2;
#endif
    while (*cursor == '/')
      cursor++;

    strcpy (real_s_name, cursor);
    real_s_sizeleft = save_sizeleft;
    real_s_totsize = save_totsize;
      }
    else
      {
    real_s_name[0] = '\0';
    real_s_totsize = 0;
    real_s_sizeleft = 0;
      }

error_loop:
  status = rmtread (archive, record_start->buffer, (unsigned int) record_size);
  if (status == record_size)
    return;

  if ((status == 0
       || (status < 0 && errno == ENOSPC)
       || (status > 0 && !read_full_records_option))
      && multi_volume_option)
    {
      union block *cursor;

    try_volume:
      switch (subcommand_option)
    {
    case APPEND_SUBCOMMAND:
    case CAT_SUBCOMMAND:
    case UPDATE_SUBCOMMAND:
      if (!new_volume (ACCESS_UPDATE))
        return;
      break;

    default:
      if (!new_volume (ACCESS_READ))
        return;
      break;
    }

    vol_error:
      status = rmtread (archive, record_start->buffer,
            (unsigned int) record_size);
      if (status < 0)
    {
      read_error ();
      goto vol_error;
    }
      if (status != record_size)
    goto short_read;

      cursor = record_start;

      if (cursor->header.typeflag == GNUTYPE_VOLHDR)
    {
      if (volume_label_option)
        {
          if (!check_label_pattern (cursor))
        {
          WARN ((0, 0, _("Volume `%s' does not match `%s'"),
             cursor->header.name, volume_label_option));
          volno--;
          global_volno--;
          goto try_volume;
        }
        }
      if (verbose_option)
        fprintf (stdlis, _("Reading %s\n"), cursor->header.name);
      cursor++;
    }
      else if (volume_label_option)
    WARN ((0, 0, _("WARNING: No volume header")));

      if (real_s_name[0])
    {
      if (cursor->header.typeflag != GNUTYPE_MULTIVOL
          || strcmp (cursor->header.name, real_s_name))
        {
          WARN ((0, 0, _("%s is not continued on this volume"),
             real_s_name));
          volno--;
          global_volno--;
          goto try_volume;
        }
      if (real_s_totsize
          != (from_oct (1 + 12, cursor->header.size)
          + from_oct (1 + 12, cursor->oldgnu_header.offset)))
        {
          WARN ((0, 0, _("%s is the wrong size (%ld != %ld + %ld)"),
             cursor->header.name, save_totsize,
             from_oct (1 + 12, cursor->header.size),
             from_oct (1 + 12, cursor->oldgnu_header.offset)));
          volno--;
          global_volno--;
          goto try_volume;
        }
      if (real_s_totsize - real_s_sizeleft
          != from_oct (1 + 12, cursor->oldgnu_header.offset))
        {
          WARN ((0, 0, _("This volume is out of sequence")));
          volno--;
          global_volno--;
          goto try_volume;
        }
      cursor++;
    }
      current_block = cursor;
      return;
    }
  else if (status < 0)
    {
      read_error ();
      goto error_loop;      /* try again */
    }

short_read:
  more = record_start->buffer + status;
  left = record_size - status;

again:
  if ((unsigned) left % BLOCKSIZE == 0)
    {
      /* FIXME: for size=0, multi-volume support.  On the first record, warn
     about the problem.  */

      if (!read_full_records_option && verbose_option
      && record_start_block == 0 && status > 0)
    WARN ((0, 0, _("Record size = %d blocks"), status / BLOCKSIZE));

      record_end
    = record_start + ((unsigned) (record_size - left)) / BLOCKSIZE;

      return;
    }
  if (read_full_records_option)
    {
      /* User warned us about this.  Fix up.  */

      if (left > 0)
    {
    error2loop:
      status = rmtread (archive, more, (unsigned int) left);
      if (status < 0)
        {
          read_error ();
          goto error2loop;  /* try again */
        }
      if (status == 0)
        FATAL_ERROR ((0, 0, _("Archive %s EOF not on block boundary"),
              *archive_name_cursor));
      left -= status;
      more += status;
      goto again;
    }
    }
  else
    FATAL_ERROR ((0, 0, _("Only read %d bytes from archive %s"),
          status, *archive_name_cursor));
}

/*-----------------------------------------------.
| Flush the current buffer to/from the archive.  |
`-----------------------------------------------*/

void
flush_archive (void)
{
  record_start_block += record_end - record_start;
  current_block = record_start;
  record_end = record_start + blocking_factor;

  if (access_mode == ACCESS_READ && time_to_start_writing)
    {
      access_mode = ACCESS_WRITE;
      time_to_start_writing = 0;

      if (file_to_switch_to >= 0)
    {
      int status = rmtclose (archive);

      if (status < 0)
        WARN ((0, errno, _("WARNING: Cannot close %s (%d, %d)"),
           *archive_name_cursor, archive, status));

      archive = file_to_switch_to;
    }
      else
    backspace_output ();
    }

  switch (access_mode)
    {
    case ACCESS_READ:
      flush_read ();
      break;

    case ACCESS_WRITE:
      flush_write ();
      break;

    case ACCESS_UPDATE:
      abort ();
    }
}

/*-------------------------------------------------------------------------.
| Backspace the archive descriptor by one record worth.  If its a tape,    |
| MTIOCTOP will work.  If its something else, we try to seek on it.  If we |
| can't seek, we lose!                             |
`-------------------------------------------------------------------------*/

static void
backspace_output (void)
{
#ifdef MTIOCTOP
  {
    struct mtop operation;

    operation.mt_op = MTBSR;
    operation.mt_count = 1;
    if (rmtioctl (archive, MTIOCTOP, (char *) &operation) >= 0)
      return;
    if (errno == EIO && rmtioctl (archive, MTIOCTOP, (char *) &operation) >= 0)
      return;
  }
#endif

  {
    off_t position = rmtlseek (archive, 0L, 1);

    /* Seek back to the beginning of this record and start writing there.  */

    position -= record_size;
    if (rmtlseek (archive, position, 0) != position)
      {
    /* Lseek failed.  Try a different method.  */

    WARN ((0, 0, _("\
Could not backspace archive file; it may be unreadable without -i")));

    /* Replace the first part of the record with NULs.  */

    if (record_start->buffer != output_start)
      memset (record_start->buffer, 0,
          (size_t) (output_start - record_start->buffer));
      }
  }
}

/*-------------------------.
| Close the archive file.  |
`-------------------------*/

void
close_archive (void)
{
  if (time_to_start_writing || access_mode == ACCESS_WRITE)
    flush_archive ();

#if !MSDOS

  /* Manage to fully drain a pipe we might be reading, so to not break it on
     the producer after the EOF block.  FIXME: one of these days, GNU tar
     might become clever enough to just stop working, once there is no more
     work to do, we might have to revise this area in such time.  */

  if (access_mode == ACCESS_READ && S_ISFIFO (archive_stat.st_mode))
    while (rmtread (archive, record_start->buffer, (unsigned int) record_size)
       > 0)
      continue;
#endif

  if (subcommand_option == DELETE_SUBCOMMAND)
    {
      off_t pos;

      pos = rmtlseek (archive, 0L, 1);
#if MSDOS
      rmtwrite (archive, "", 0);
#else
      ftruncate (archive, (size_t) pos);
#endif
    }
  if (verify_option)
    verify_volume ();

  {
    int status = rmtclose (archive);

    if (status < 0)
      WARN ((0, errno, _("WARNING: Cannot close %s (%d, %d)"),
         *archive_name_cursor, archive, status));
  }

#if !MSDOS

  if (child_pid)
    {
      WAIT_T wait_status;
      int child;

      /* Loop waiting for the right child to die, or for no more kids.  */

      while ((child = wait (&wait_status), child != child_pid)
         && child != -1)
    continue;

      if (child != -1)
    if (WIFSIGNALED (wait_status)
#if 0
        && !WIFSTOPPED (wait_status)
#endif
        )
      {
        /* SIGPIPE is OK, everything else is a problem.  */

        if (WTERMSIG (wait_status) != SIGPIPE)
          ERROR ((0, 0, _("Child died with signal %d%s"),
              WTERMSIG (wait_status),
              WCOREDUMP (wait_status) ? _(" (core dumped)") : ""));
      }
    else
      {
        /* Child voluntarily terminated -- but why?  /bin/sh returns
           SIGPIPE + 128 if its child, then do nothing.  */

        if (WEXITSTATUS (wait_status) != (SIGPIPE + 128)
        && WEXITSTATUS (wait_status))
          ERROR ((0, 0, _("Child returned status %d"),
              WEXITSTATUS (wait_status)));
      }
    }
#endif /* !MSDOS */

  if (current_file_name)
    free (current_file_name);
  if (current_link_name)
    free (current_link_name);
  if (save_name)
    free (save_name);
  free (multi_volume_option ? record_start - 2 : record_start);
}

/*------------------------------------------------.
| Called to initialize the global volume number.  |
`------------------------------------------------*/

void
init_volume_number (void)
{
  FILE *file = fopen (volno_file_option, "r");

  if (file)
    {
      fscanf (file, "%d", &global_volno);
      if (fclose (file) == EOF)
    ERROR ((0, errno, "%s", volno_file_option));
    }
  else if (errno != ENOENT)
    ERROR ((0, errno, "%s", volno_file_option));
}

/*-------------------------------------------------------.
| Called to write out the closing global volume number.  |
`-------------------------------------------------------*/

void
closeout_volume_number (void)
{
  FILE *file = fopen (volno_file_option, "w");

  if (file)
    {
      fprintf (file, "%d\n", global_volno);
      if (fclose (file) == EOF)
    ERROR ((0, errno, "%s", volno_file_option));
    }
  else
    ERROR ((0, errno, "%s", volno_file_option));
}

/*-----------------------------------------------------------------------.
| We've hit the end of the old volume.  Close it and open the next one.  |
| Return nonzero on success.                         |
`-----------------------------------------------------------------------*/

static int
new_volume (enum access_mode access)
{
  static FILE *read_file = NULL;
  static int looped = 0;

  int status;

  if (!read_file && !info_script_option)
    /* FIXME: if fopen is used, it will never be closed.  */
    read_file = archive == STDIN ? fopen (TTY_NAME, "r") : stdin;

  if (now_verifying)
    return 0;
  if (verify_option)
    verify_volume ();

  if (status = rmtclose (archive), status < 0)
    WARN ((0, errno, _("WARNING: Cannot close %s (%d, %d)"),
       *archive_name_cursor, archive, status));

  global_volno++;
  volno++;
  archive_name_cursor++;
  if (archive_name_cursor == archive_name_array + archive_names)
    {
      archive_name_cursor = archive_name_array;
      looped = 1;
    }

tryagain:
  if (looped)
    {
      /* We have to prompt from now on.  */

      if (info_script_option)
    {
      if (volno_file_option)
        closeout_volume_number ();
      system (info_script_option);
    }
      else
    while (1)
      {
        char input_buffer[80];

        fprintf (stderr,
             _("\007Prepare volume #%d for %s and hit return: "),
             global_volno, *archive_name_cursor);
        fflush (stderr);

        if (fgets (input_buffer, sizeof (input_buffer), read_file) == 0)
          {
        fprintf (stderr, _("EOF where user reply was expected"));

        if (subcommand_option != EXTRACT_SUBCOMMAND
            && subcommand_option != LIST_SUBCOMMAND
            && subcommand_option != DIFF_SUBCOMMAND)
          WARN ((0, 0, _("WARNING: Archive is incomplete")));

        exit (TAREXIT_FAILURE);
          }
        if (input_buffer[0] == '\n'
        || input_buffer[0] == 'y'
        || input_buffer[0] == 'Y')
          break;

        switch (input_buffer[0])
          {
          case '?':
        {
          fprintf (stderr, _("\
 n [name]   Give a new file name for the next (and subsequent) volume(s)\n\
 q          Abort tar\n\
 !          Spawn a subshell\n\
 ?          Print this list\n"));
        }
        break;

          case 'q':
        /* Quit.  */

        fprintf (stdlis, _("No new volume; exiting.\n"));

        if (subcommand_option != EXTRACT_SUBCOMMAND
            && subcommand_option != LIST_SUBCOMMAND
            && subcommand_option != DIFF_SUBCOMMAND)
          WARN ((0, 0, _("WARNING: Archive is incomplete")));

        exit (TAREXIT_FAILURE);

          case 'n':
        /* Get new file name.  */

        {
          char *name = &input_buffer[1];
          char *cursor;

          while (*name == ' ' || *name == '\t')
            name++;
          cursor = name;
          while (*cursor && *cursor != '\n')
            cursor++;
          *cursor = '\0';

          /* FIXME: the following allocation is never reclaimed.  */
          *archive_name_cursor = xstrdup (name);
        }
        break;

          case '!':
#if MSDOS
        spawnl (P_WAIT, getenv ("COMSPEC"), "-", 0);
#else /* not MSDOS */
        switch (fork ())
          {
          case -1:
            WARN ((0, errno, _("Cannot fork!")));
            break;

          case 0:
            {
              const char *shell = getenv ("SHELL");

              if (shell == NULL)
            shell = "/bin/sh";
              execlp (shell, "-sh", "-i", 0);
              FATAL_ERROR ((0, errno, _("Cannot exec a shell %s"),
                    shell));
            }

          default:
            {
              WAIT_T wait_status;

              wait (&wait_status);
            }
            break;
          }

        /* FIXME: I'm not sure if that's all that has to be done
           here.  (jk)  */

#endif /* not MSDOS */
        break;
          }
      }
    }

  if (verify_option)
    archive = rmtopen (*archive_name_cursor, O_RDWR | O_CREAT, 0666,
               rsh_command_option);
  else
    switch (access)
      {
      case ACCESS_READ:
    archive = rmtopen (*archive_name_cursor, O_RDONLY, 0666,
               rsh_command_option);
    break;

      case ACCESS_WRITE:
    if (backup_option)
      maybe_backup_file (*archive_name_cursor, 1);
    archive = rmtcreat (*archive_name_cursor, 0666, rsh_command_option);
    break;

      case ACCESS_UPDATE:
    archive = rmtopen (*archive_name_cursor, O_RDWR | O_CREAT, 0666,
               rsh_command_option);
    break;
      }

  if (archive < 0)
    {
      WARN ((0, errno, _("Cannot open %s"), *archive_name_cursor));
      if (!verify_option && access == ACCESS_WRITE && backup_option)
    undo_last_backup ();
      goto tryagain;
    }

#if MSDOS
  setmode (archive, O_BINARY);
#endif

  return 1;
}

https://git.savannah.gnu.org/cgit/tar.git/tree//src/compare.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/compare.c used in: Step 650
/* Diff files from a tar archive.
   Copyright (C) 1988, 92, 93, 94, 96, 97 Free Software Foundation, Inc.
   Written by John Gilmore, on 1987-04-30.

   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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include "system.h"

#if HAVE_LINUX_FD_H
# include <linux/fd.h>
#endif

#include "common.h"
#include "rmt.h"

/* Spare space for messages, hopefully safe even after gettext.  */
#define MESSAGE_BUFFER_SIZE 100

/* Nonzero if we are verifying at the moment.  */
int now_verifying = 0;

/* File descriptor for the file we are diffing.  */
static int diff_handle;

/* Area for reading file contents into.  */
static char *diff_buffer = NULL;

/*--------------------------------.
| Initialize for a diff operation |
`--------------------------------*/

void
diff_init (void)
{
  diff_buffer = (char *) valloc ((unsigned) record_size);
  if (!diff_buffer)
    FATAL_ERROR ((0, 0,
          _("Could not allocate memory for diff buffer of %d bytes"),
          record_size));
}

/*------------------------------------------------------------------------.
| Sigh about something that differs by writing a MESSAGE to stdlis, given |
| MESSAGE is not NULL.  Also set the exit status if not already.          |
`------------------------------------------------------------------------*/

static void
report_difference (const char *message)
{
  if (message)
    fprintf (stdlis, "%s: %s\n", current_file_name, message);

  if (exit_status == TAREXIT_SUCCESS)
    exit_status = TAREXIT_DIFFERS;
}

/*-----------------------------------------------------------------------.
| Takes a buffer returned by read_and_process and does nothing with it.  |
`-----------------------------------------------------------------------*/

/* Yes, I know.  SIZE and DATA are unused in this function.  Some compilers
   may even report it.  That's OK, just relax!  */

static int
process_noop (long size, char *data)
{
  return 1;
}

/*---.
| ?  |
`---*/

static int
process_rawdata (long bytes, char *buffer)
{
  int status = read (diff_handle, diff_buffer, (size_t) bytes);
  char message[MESSAGE_BUFFER_SIZE];

  if (status != bytes)
    {
      if (status < 0)
    {
      WARN ((0, errno, _("Cannot read %s"), current_file_name));
      report_difference (NULL);
    }
      else
    {
      sprintf (message, _("Could only read %d of %ld bytes"),
           status, bytes);
      report_difference (message);
    }
      return 0;
    }

  if (memcmp (buffer, diff_buffer, (size_t) bytes))
    {
      report_difference (_("Data differs"));
      return 0;
    }

  return 1;
}

/*---.
| ?  |
`---*/

/* Directory contents, only for GNUTYPE_DUMPDIR.  */

static char *dumpdir_cursor;

static int
process_dumpdir (long bytes, char *buffer)
{
  if (memcmp (buffer, dumpdir_cursor, (size_t) bytes))
    {
      report_difference (_("Data differs"));
      return 0;
    }

  dumpdir_cursor += bytes;
  return 1;
}

/*------------------------------------------------------------------------.
| Some other routine wants SIZE bytes in the archive.  For each chunk of  |
| the archive, call PROCESSOR with the size of the chunk, and the address |
| of the chunk it can work with.  The PROCESSOR should return nonzero for |
| success.  It it return error once, continue skipping without calling    |
| PROCESSOR anymore.                                                      |
`------------------------------------------------------------------------*/

static void
read_and_process (long size, int (*processor) (long, char *))
{
  union block *data_block;
  long data_size;

  if (multi_volume_option)
    save_sizeleft = size;
  while (size)
    {
      data_block = find_next_block ();
      if (data_block == NULL)
    {
      ERROR ((0, 0, _("Unexpected EOF on archive file")));
      return;
    }

      data_size = available_space_after (data_block);
      if (data_size > size)
    data_size = size;
      if (!(*processor) (data_size, data_block->buffer))
    processor = process_noop;
      set_next_block_after ((union block *)
                (data_block->buffer + data_size - 1));
      size -= data_size;
      if (multi_volume_option)
    save_sizeleft -= data_size;
    }
}

/*---.
| ?  |
`---*/

/* JK This routine should be used more often than it is ... look into
   that.  Anyhow, what it does is translate the sparse information on the
   header, and in any subsequent extended headers, into an array of
   structures with true numbers, as opposed to character strings.  It
   simply makes our life much easier, doing so many comparisong and such.
   */

static void
fill_in_sparse_array (void)
{
  int counter;

  /* Allocate space for our scratch space; it's initially 10 elements
     long, but can change in this routine if necessary.  */

  sp_array_size = 10;
  sparsearray = (struct sp_array *) xmalloc (sp_array_size * sizeof (struct sp_array));

  /* There are at most five of these structures in the header itself;
     read these in first.  */

  for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
    {
      /* Compare to 0, or use !(int)..., for Pyramid's dumb compiler.  */
      if (current_header->oldgnu_header.sp[counter].numbytes == 0)
    break;

      sparsearray[counter].offset =
    from_oct (1 + 12, current_header->oldgnu_header.sp[counter].offset);
      sparsearray[counter].numbytes =
    from_oct (1 + 12, current_header->oldgnu_header.sp[counter].numbytes);
    }

  /* If the header's extended, we gotta read in exhdr's till we're done.  */

  if (current_header->oldgnu_header.isextended)
    {
      /* How far into the sparsearray we are `so far'.  */
      static int so_far_ind = SPARSES_IN_OLDGNU_HEADER;
      union block *exhdr;

      while (1)
    {
      exhdr = find_next_block ();
      for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
        {
          if (counter + so_far_ind > sp_array_size - 1)
        {
          /* We just ran out of room in our scratch area -
             realloc it.  */

          sp_array_size *= 2;
          sparsearray = (struct sp_array *)
            xrealloc (sparsearray,
                  sp_array_size * sizeof (struct sp_array));
        }

          /* Convert the character strings into longs.  */

          sparsearray[counter + so_far_ind].offset =
        from_oct (1 + 12, exhdr->sparse_header.sp[counter].offset);
          sparsearray[counter + so_far_ind].numbytes =
        from_oct (1 + 12, exhdr->sparse_header.sp[counter].numbytes);
        }

      /* If this is the last extended header for this file, we can
         stop.  */

      if (!exhdr->sparse_header.isextended)
        break;

      so_far_ind += SPARSES_IN_SPARSE_HEADER;
      set_next_block_after (exhdr);
    }

      /* Be sure to skip past the last one.  */

      set_next_block_after (exhdr);
    }
}

/*---.
| ?  |
`---*/

/* JK Diff'ing a sparse file with its counterpart on the tar file is a
   bit of a different story than a normal file.  First, we must know what
   areas of the file to skip through, i.e., we need to contruct a
   sparsearray, which will hold all the information we need.  We must
   compare small amounts of data at a time as we find it.  */

/* FIXME: This does not look very solid to me, at first glance.  Zero areas
   are not checked, spurious sparse entries seemingly goes undetected, and
   I'm not sure overall identical sparsity is verified.  */

static void
diff_sparse_files (int size_of_file)
{
  int remaining_size = size_of_file;
  char *buffer = (char *) xmalloc (BLOCKSIZE * sizeof (char));
  int buffer_size = BLOCKSIZE;
  union block *data_block = NULL;
  int counter = 0;
  int different = 0;

  fill_in_sparse_array ();

  while (remaining_size > 0)
    {
      int status;
      long chunk_size;
#if 0
      int amount_read = 0;
#endif

      data_block = find_next_block ();
      chunk_size = sparsearray[counter].numbytes;
      if (!chunk_size)
    break;

      lseek (diff_handle, sparsearray[counter].offset, 0);

      /* Take care to not run out of room in our buffer.  */

      while (buffer_size < chunk_size)
    {
      buffer_size *= 2;
      buffer = (char *) xrealloc (buffer, buffer_size * sizeof (char));
    }

      while (chunk_size > BLOCKSIZE)
    {
      if (status = read (diff_handle, buffer, BLOCKSIZE),
          status != BLOCKSIZE)
        {
          if (status < 0)
        {
          WARN ((0, errno, _("Cannot read %s"), current_file_name));
          report_difference (NULL);
        }
          else
        {
          char message[MESSAGE_BUFFER_SIZE];

          sprintf (message, _("Could only read %d of %ld bytes"),
               status, chunk_size);
          report_difference (message);
        }
          break;
        }

      if (memcmp (buffer, data_block->buffer, BLOCKSIZE))
        {
          different = 1;
          break;
        }

      chunk_size -= status;
      remaining_size -= status;
      set_next_block_after (data_block);
      data_block = find_next_block ();
    }
      if (status = read (diff_handle, buffer, (size_t) chunk_size),
      status != chunk_size)
    {
      if (status < 0)
        {
          WARN ((0, errno, _("Cannot read %s"), current_file_name));
          report_difference (NULL);
        }
      else
        {
          char message[MESSAGE_BUFFER_SIZE];

          sprintf (message, _("Could only read %d of %ld bytes"),
               status, chunk_size);
          report_difference (message);
        }
      break;
    }

      if (memcmp (buffer, data_block->buffer, (size_t) chunk_size))
    {
      different = 1;
      break;
    }
#if 0
      amount_read += chunk_size;
      if (amount_read >= BLOCKSIZE)
    {
      amount_read = 0;
      set_next_block_after (data_block);
      data_block = find_next_block ();
    }
#endif
      set_next_block_after (data_block);
      counter++;
      remaining_size -= chunk_size;
    }

#if 0
  /* If the number of bytes read isn't the number of bytes supposedly in
     the file, they're different.  */

  if (amount_read != size_of_file)
    different = 1;
#endif

  set_next_block_after (data_block);
  free (sparsearray);

  if (different)
    report_difference (_("Data differs"));
}

/*---------------------------------------------------------------------.
| Call either stat or lstat over STAT_DATA, depending on --dereference |
| (-h), for a file which should exist.  Diagnose any problem.  Return  |
| nonzero for success, zero otherwise.                     |
`---------------------------------------------------------------------*/

static int
get_stat_data (struct stat *stat_data)
{
  int status = (dereference_option
        ? stat (current_file_name, stat_data)
        : lstat (current_file_name, stat_data));

  if (status < 0)
    {
      if (errno == ENOENT)
    report_difference (_("File does not exist"));
      else
    {
      ERROR ((0, errno, _("Cannot stat file %s"), current_file_name));
      report_difference (NULL);
    }
#if 0
      skip_file ((long) current_stat.st_size);
#endif
      return 0;
    }

  return 1;
}

/*----------------------------------.
| Diff a file against the archive.  |
`----------------------------------*/

void
diff_archive (void)
{
  struct stat stat_data;
  int name_length;
  int status;

  errno = EPIPE;        /* FIXME: errno should be read-only */
                /* FIXME: remove perrors */

  set_next_block_after (current_header);
  decode_header (current_header, &current_stat, &current_format, 1);

  /* Print the block from `current_header' and `current_stat'.  */

  if (verbose_option)
    {
      if (now_verifying)
    fprintf (stdlis, _("Verify "));
      print_header ();
    }

  switch (current_header->header.typeflag)
    {
    default:
      WARN ((0, 0, _("Unknown file type '%c' for %s, diffed as normal file"),
         current_header->header.typeflag, current_file_name));
      /* Fall through.  */

    case AREGTYPE:
    case REGTYPE:
    case GNUTYPE_SPARSE:
    case CONTTYPE:

      /* Appears to be a file.  See if it's really a directory.  */

      name_length = strlen (current_file_name) - 1;
      if (current_file_name[name_length] == '/')
    goto really_dir;

      if (!get_stat_data (&stat_data))
    {
      if (current_header->oldgnu_header.isextended)
        skip_extended_headers ();
      skip_file ((long) current_stat.st_size);
      goto quit;
    }

      if (!S_ISREG (stat_data.st_mode))
    {
      report_difference (_("Not a regular file"));
      skip_file ((long) current_stat.st_size);
      goto quit;
    }

      stat_data.st_mode &= 07777;
      if (stat_data.st_mode != current_stat.st_mode)
    report_difference (_("Mode differs"));

#if !MSDOS
      /* stat() in djgpp's C library gives a constant number of 42 as the
     uid and gid of a file.  So, comparing an FTP'ed archive just after
     unpack would fail on MSDOS.  */
      if (stat_data.st_uid != current_stat.st_uid)
    report_difference (_("Uid differs"));
      if (stat_data.st_gid != current_stat.st_gid)
    report_difference (_("Gid differs"));
#endif

      if (stat_data.st_mtime != current_stat.st_mtime)
    report_difference (_("Mod time differs"));
      if (current_header->header.typeflag != GNUTYPE_SPARSE &&
      stat_data.st_size != current_stat.st_size)
    {
      report_difference (_("Size differs"));
      skip_file ((long) current_stat.st_size);
      goto quit;
    }

      diff_handle = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);

      if (diff_handle < 0 && !absolute_names_option)
    {
      char *tmpbuf = xmalloc (strlen (current_file_name) + 2);

      *tmpbuf = '/';
      strcpy (tmpbuf + 1, current_file_name);
      diff_handle = open (tmpbuf, O_NDELAY | O_RDONLY);
      free (tmpbuf);
    }
      if (diff_handle < 0)
    {
      ERROR ((0, errno, _("Cannot open %s"), current_file_name));
      if (current_header->oldgnu_header.isextended)
        skip_extended_headers ();
      skip_file ((long) current_stat.st_size);
      report_difference (NULL);
      goto quit;
    }

      /* Need to treat sparse files completely differently here.  */

      if (current_header->header.typeflag == GNUTYPE_SPARSE)
    diff_sparse_files (current_stat.st_size);
      else
    {
      if (multi_volume_option)
        {
          assign_string (&save_name, current_file_name);
          save_totsize = current_stat.st_size;
          /* save_sizeleft is set in read_and_process.  */
        }

      read_and_process ((long) (current_stat.st_size), process_rawdata);

      if (multi_volume_option)
        assign_string (&save_name, NULL);
    }

      status = close (diff_handle);
      if (status < 0)
    ERROR ((0, errno, _("Error while closing %s"), current_file_name));

    quit:
      break;

#if !MSDOS
    case LNKTYPE:
      {
    dev_t dev;
    ino_t ino;

    if (!get_stat_data (&stat_data))
      break;

    dev = stat_data.st_dev;
    ino = stat_data.st_ino;
    status = stat (current_link_name, &stat_data);
    if (status < 0)
      {
        if (errno == ENOENT)
          report_difference (_("Does not exist"));
        else
          {
        WARN ((0, errno, _("Cannot stat file %s"), current_file_name));
        report_difference (NULL);
          }
        break;
      }

    if (stat_data.st_dev != dev || stat_data.st_ino != ino)
      {
        char *message = (char *)
          xmalloc (MESSAGE_BUFFER_SIZE + strlen (current_link_name));

        sprintf (message, _("Not linked to %s"), current_link_name);
        report_difference (message);
        free (message);
        break;
      }

    break;
      }
#endif /* not MSDOS */

#ifdef S_ISLNK
    case SYMTYPE:
      {
    char linkbuf[NAME_FIELD_SIZE + 3]; /* FIXME: may be too short.  */

    status = readlink (current_file_name, linkbuf, (sizeof linkbuf) - 1);

    if (status < 0)
      {
        if (errno == ENOENT)
          report_difference (_("No such file or directory"));
        else
          {
        WARN ((0, errno, _("Cannot read link %s"), current_file_name));
        report_difference (NULL);
          }
        break;
      }

    linkbuf[status] = '\0'; /* null-terminate it */
    if (strncmp (current_link_name, linkbuf, (size_t) status) != 0)
      report_difference (_("Symlink differs"));

    break;
      }
#endif /* not S_ISLNK */

#ifdef S_IFCHR
    case CHRTYPE:
      current_stat.st_mode |= S_IFCHR;
      goto check_node;
#endif /* not S_IFCHR */

#ifdef S_IFBLK
      /* If local system doesn't support block devices, use default case.  */

    case BLKTYPE:
      current_stat.st_mode |= S_IFBLK;
      goto check_node;
#endif /* not S_IFBLK */

#ifdef S_ISFIFO
      /* If local system doesn't support FIFOs, use default case.  */

    case FIFOTYPE:
# ifdef S_IFIFO
      current_stat.st_mode |= S_IFIFO;
# endif
      current_stat.st_rdev = 0; /* FIXME: do we need this? */
      goto check_node;
#endif /* S_ISFIFO */

    check_node:
      /* FIXME: deal with umask.  */

      if (!get_stat_data (&stat_data))
    break;

      if (current_stat.st_rdev != stat_data.st_rdev)
    {
      report_difference (_("Device numbers changed"));
      break;
    }

      if (
#ifdef S_IFMT
      current_stat.st_mode != stat_data.st_mode
#else
      /* POSIX lossage.  */
      (current_stat.st_mode & 07777) != (stat_data.st_mode & 07777)
#endif
      )
    {
      report_difference (_("Mode or device-type changed"));
      break;
    }

      break;

    case GNUTYPE_DUMPDIR:
      {
    char *dumpdir_buffer = get_directory_contents (current_file_name, 0);

    if (multi_volume_option)
      {
        assign_string (&save_name, current_file_name);
        save_totsize = current_stat.st_size;
        /* save_sizeleft is set in read_and_process.  */
      }

    if (dumpdir_buffer)
      {
        dumpdir_cursor = dumpdir_buffer;
        read_and_process ((long) (current_stat.st_size), process_dumpdir);
        free (dumpdir_buffer);
      }
    else
      read_and_process ((long) (current_stat.st_size), process_noop);

    if (multi_volume_option)
      assign_string (&save_name, NULL);
    /* Fall through.  */
      }

    case DIRTYPE:
      /* Check for trailing /.  */

      name_length = strlen (current_file_name) - 1;

    really_dir:
      while (name_length && current_file_name[name_length] == '/')
    current_file_name[name_length--] = '\0';    /* zap / */

      if (!get_stat_data (&stat_data))
    break;

      if (!S_ISDIR (stat_data.st_mode))
    {
      report_difference (_("No longer a directory"));
      break;
    }

      if ((stat_data.st_mode & 07777) != (current_stat.st_mode & 07777))
    report_difference (_("Mode differs"));
      break;

    case GNUTYPE_VOLHDR:
      break;

    case GNUTYPE_MULTIVOL:
      {
    off_t offset;

    name_length = strlen (current_file_name) - 1;
    if (current_file_name[name_length] == '/')
      goto really_dir;

    if (!get_stat_data (&stat_data))
      break;

    if (!S_ISREG (stat_data.st_mode))
      {
        report_difference (_("Not a regular file"));
        skip_file ((long) current_stat.st_size);
        break;
      }

    stat_data.st_mode &= 07777;
    offset = from_oct (1 + 12, current_header->oldgnu_header.offset);
    if (stat_data.st_size != current_stat.st_size + offset)
      {
        report_difference (_("Size differs"));
        skip_file ((long) current_stat.st_size);
        break;
      }

    diff_handle = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);

    if (diff_handle < 0)
      {
        WARN ((0, errno, _("Cannot open file %s"), current_file_name));
        report_difference (NULL);
        skip_file ((long) current_stat.st_size);
        break;
      }

    status = lseek (diff_handle, offset, 0);
    if (status != offset)
      {
        WARN ((0, errno, _("Cannot seek to %ld in file %s"),
           offset, current_file_name));
        report_difference (NULL);
        break;
      }

    if (multi_volume_option)
      {
        assign_string (&save_name, current_file_name);
        save_totsize = stat_data.st_size;
        /* save_sizeleft is set in read_and_process.  */
      }

    read_and_process ((long) (current_stat.st_size), process_rawdata);

    if (multi_volume_option)
      assign_string (&save_name, NULL);

    status = close (diff_handle);
    if (status < 0)
      ERROR ((0, errno, _("Error while closing %s"), current_file_name));

    break;
      }
    }
}

/*---.
| ?  |
`---*/

void
verify_volume (void)
{
  if (!diff_buffer)
    diff_init ();

  /* Verifying an archive is meant to check if the physical media got it
     correctly, so try to defeat clever in-memory buffering pertaining to
     this particular media.  On Linux, for example, the floppy drive would
     not even be accessed for the whole verification.

     The code was using fsync only when the ioctl is unavailable, but
     Marty Leisner says that the ioctl does not work when not preceded by
     fsync.  So, until we know better, or maybe to please Marty, let's do it
     the unbelievable way :-).  */

#if HAVE_FSYNC
  fsync (archive);
#endif
#ifdef FDFLUSH
  ioctl (archive, FDFLUSH);
#endif

#ifdef MTIOCTOP
  {
    struct mtop operation;
    int status;

    operation.mt_op = MTBSF;
    operation.mt_count = 1;
    if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation), status < 0)
      {
    if (errno != EIO
        || (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
        status < 0))
      {
#endif
        if (rmtlseek (archive, 0L, 0) != 0)
          {
        /* Lseek failed.  Try a different method.  */

        WARN ((0, errno,
               _("Could not rewind archive file for verify")));
        return;
          }
#ifdef MTIOCTOP
      }
      }
  }
#endif

  access_mode = ACCESS_READ;
  now_verifying = 1;

  flush_read ();
  while (1)
    {
      enum read_header status = read_header ();

      if (status == HEADER_FAILURE)
    {
      int counter = 0;

      while (status == HEADER_FAILURE);
        {
          counter++;
          status = read_header ();
        }
      ERROR ((0, 0,
          _("VERIFY FAILURE: %d invalid header(s) detected"), counter));
    }
      if (status == HEADER_ZERO_BLOCK || status == HEADER_END_OF_FILE)
    break;

      diff_archive ();
    }

  access_mode = ACCESS_WRITE;
  now_verifying = 0;
}

https://git.savannah.gnu.org/cgit/tar.git/tree//src/delete.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/delete.c used in: Step 652
/* Delete entries from a tar archive.
   Copyright (C) 1988, 1992, 1994, 1996, 1997 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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include "system.h"

#define STDIN 0
#define STDOUT 1

#include "common.h"
#include "rmt.h"

static union block *new_record = NULL;
static union block *save_record = NULL;
static int records_read = 0;
static int new_blocks = 0;
static int blocks_needed = 0;

/* FIXME: This module should not directly handle the following three
   variables, instead, this should be done in buffer.c only.  */
extern union block *record_start;
extern union block *record_end;
extern union block *current_block;

/*-------------------------------------------------------------------------.
| Move archive descriptor by COUNT records worth.  If COUNT is positive we |
| move forward, else we move negative.  If its a tape, MTIOCTOP had better |
| work.  If its something else, we try to seek on it.  If we can't seek,   |
| we loose!                                |
`-------------------------------------------------------------------------*/

static void
move_archive (int count)
{
#ifdef MTIOCTOP
  {
    struct mtop operation;
    int status;

    if (count > 0)
      {
    operation.mt_op = MTFSR;
    operation.mt_count = count;
      }
    else
      {
    operation.mt_op = MTBSR;
    operation.mt_count = -count;
      }

    if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
    status >= 0)
      return;

    if (errno == EIO)
      if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
      status >= 0)
      return;
  }
#endif /* MTIOCTOP */

  {
    off_t position = rmtlseek (archive, 0L, 1);

    position += record_size * count;

    if (rmtlseek (archive, position, 0) != position)
      FATAL_ERROR ((0, 0, _("Could not re-position archive file")));

    return;
  }
}

/*----------------------------------------------------------------.
| Write out the record which has been filled.  If MOVE_BACK_FLAG, |
| backspace to where we started.                                  |
`----------------------------------------------------------------*/

static void
write_record (int move_back_flag)
{
  save_record = record_start;
  record_start = new_record;

  if (archive == STDIN)
    {
      archive = STDOUT;
      flush_write ();
      archive = STDIN;
    }
  else
    {
      move_archive (-(records_read + 1));
      flush_write ();
    }

  record_start = save_record;

  if (move_back_flag)
    {
      /* Move the tape head back to where we were.  */

      if (archive != STDIN)
    move_archive (records_read);

      records_read--;
    }

  blocks_needed = blocking_factor;
  new_blocks = 0;
}

/*---.
| ?  |
`---*/

void
delete_archive_members (void)
{
  enum read_header logical_status = HEADER_STILL_UNREAD;
  enum read_header previous_status = HEADER_STILL_UNREAD;

  /* FIXME: Should clean the routine before cleaning these variables :-( */
  struct name *name;
  int blocks_to_skip = 0;
  int blocks_to_keep = 0;
  int kept_blocks_in_record;

  name_gather ();
  open_archive (ACCESS_UPDATE);

  while (logical_status == HEADER_STILL_UNREAD)
    {
      enum read_header status = read_header ();

      switch (status)
    {
    case HEADER_STILL_UNREAD:
      abort ();

    case HEADER_SUCCESS:
      if (name = name_scan (current_file_name), !name)
        {
          set_next_block_after (current_header);
          if (current_header->oldgnu_header.isextended)
        skip_extended_headers ();
          skip_file ((long) (current_stat.st_size));
          break;
        }
      name->found = 1;
      logical_status = HEADER_SUCCESS;
      break;

    case HEADER_ZERO_BLOCK:
    case HEADER_END_OF_FILE:
      logical_status = HEADER_END_OF_FILE;
      break;

    case HEADER_FAILURE:
      set_next_block_after (current_header);
      switch (previous_status)
        {
        case HEADER_STILL_UNREAD:
          WARN ((0, 0, _("This does not look like a tar archive")));
          /* Fall through.  */

        case HEADER_SUCCESS:
        case HEADER_ZERO_BLOCK:
          ERROR ((0, 0, _("Skipping to next header")));
          /* Fall through.  */

        case HEADER_FAILURE:
          break;

        case HEADER_END_OF_FILE:
          abort ();
        }
      break;
    }

      previous_status = status;
    }

  if (logical_status != HEADER_SUCCESS)
    {
      write_eot ();
      close_archive ();
      names_notfound ();
      return;
    }

  write_archive_to_stdout = 0;
  new_record = (union block *) xmalloc ((size_t) record_size);

  /* Save away blocks before this one in this record.  */

  new_blocks = current_block - record_start;
  blocks_needed = blocking_factor - new_blocks;
  if (new_blocks)
    memcpy ((void *) new_record, (void *) record_start,
       (size_t) (new_blocks * BLOCKSIZE));

#if 0
  /* FIXME: Old code, before the goto was inserted.  To be redesigned.  */
  set_next_block_after (current_header);
  if (current_header->oldgnu_header.isextended)
    skip_extended_headers ();
  skip_file ((long) (current_stat.st_size));
#endif
  logical_status = HEADER_STILL_UNREAD;
  goto flush_file;

  /* FIXME: Solaris 2.4 Sun cc (the ANSI one, not the old K&R) says:
       "delete.c", line 223: warning: loop not entered at top
     Reported by Bruno Haible.  */
  while (1)
    {
      enum read_header status;

      /* Fill in a record.  */

      if (current_block == record_end)
    {
      flush_archive ();
      records_read++;
    }
      status = read_header ();

      if (status == HEADER_ZERO_BLOCK && ignore_zeros_option)
    {
      set_next_block_after (current_header);
      continue;
    }
      if (status == HEADER_END_OF_FILE || status == HEADER_ZERO_BLOCK)
    {
      logical_status = HEADER_END_OF_FILE;
      memset (new_record[new_blocks].buffer, 0,
         (size_t) (BLOCKSIZE * blocks_needed));
      new_blocks += blocks_needed;
      blocks_needed = 0;
      write_record (0);
      break;
    }

      if (status == HEADER_FAILURE)
    {
      ERROR ((0, 0, _("Deleting non-header from archive")));
      set_next_block_after (current_header);
      continue;
    }

      /* Found another header.  */

      if (name = name_scan (current_file_name), name)
    {
      name->found = 1;
    flush_file:
      set_next_block_after (current_header);
      blocks_to_skip = (current_stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;

      while (record_end - current_block <= blocks_to_skip)
        {
          blocks_to_skip -= (record_end - current_block);
          flush_archive ();
          records_read++;
        }
      current_block += blocks_to_skip;
      blocks_to_skip = 0;
      continue;
    }

      /* Copy header.  */

      new_record[new_blocks] = *current_header;
      new_blocks++;
      blocks_needed--;
      blocks_to_keep
    = (current_stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
      set_next_block_after (current_header);
      if (blocks_needed == 0)
    write_record (1);

      /* Copy data.  */

      kept_blocks_in_record = record_end - current_block;
      if (kept_blocks_in_record > blocks_to_keep)
    kept_blocks_in_record = blocks_to_keep;

      while (blocks_to_keep)
    {
      int count;

      if (current_block == record_end)
        {
          flush_read ();
          records_read++;
          current_block = record_start;
          kept_blocks_in_record = blocking_factor;
          if (kept_blocks_in_record > blocks_to_keep)
        kept_blocks_in_record = blocks_to_keep;
        }
      count = kept_blocks_in_record;
      if (count > blocks_needed)
        count = blocks_needed;

      memcpy ((void *) (new_record + new_blocks),
          (void *) current_block,
          (size_t) (count * BLOCKSIZE));
      new_blocks += count;
      blocks_needed -= count;
      current_block += count;
      blocks_to_keep -= count;
      kept_blocks_in_record -= count;

      if (blocks_needed == 0)
        write_record (1);
    }
    }

  write_eot ();
  close_archive ();
  names_notfound ();
}

https://git.savannah.gnu.org/cgit/tar.git/tree//src/rmt.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/rmt.h used in: Step 652, Step 657
/* Definitions for communicating with a remote tape drive.
   Copyright (C) 1988, 1992, 1996, 1997 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.  */

extern char *rmt_path__;

int rmt_open__ PARAMS ((const char *, int, int, const char *));
int rmt_close__ PARAMS ((int));
int rmt_read__ PARAMS ((int, char *, unsigned int));
int rmt_write__ PARAMS ((int, char *, unsigned int));
long rmt_lseek__ PARAMS ((int, off_t, int));
int rmt_ioctl__ PARAMS ((int, int, char *));

/* A filename is remote if it contains a colon not preceeded by a slash,
   to take care of `/:/' which is a shorthand for `/.../<CELL-NAME>/fs'
   on machines running OSF's Distributing Computing Environment (DCE) and
   Distributed File System (DFS).  However, when --force-local, a
   filename is never remote.  */

#define _remdev(Path) \
  (!force_local_option && (rmt_path__ = strchr (Path, ':')) \
   && rmt_path__ > (Path) && rmt_path__[-1] != '/')

#define _isrmt(Fd) \
  ((Fd) >= __REM_BIAS)

#define __REM_BIAS 128

#ifndef O_CREAT
# define O_CREAT 01000
#endif

#define rmtopen(Path, Oflag, Mode, Command) \
  (_remdev (Path) ? rmt_open__ (Path, Oflag, __REM_BIAS, Command) \
   : open (Path, Oflag, Mode))

#define rmtaccess(Path, Amode) \
  (_remdev (Path) ? 0 : access (Path, Amode))

#define rmtstat(Path, Buffer) \
  (_remdev (Path) ? (errno = EOPNOTSUPP), -1 : stat (Path, Buffer))
                /* FIXME: errno should be read-only */

#define rmtcreat(Path, Mode, Command) \
   (_remdev (Path) \
    ? rmt_open__ (Path, 1 | O_CREAT, __REM_BIAS, Command) \
    : creat (Path, Mode))

#define rmtlstat(Path, Buffer) \
  (_remdev (Path) ? (errno = EOPNOTSUPP), -1 : lstat (Path, Buffer))
                /* FIXME: errno should be read-only */

#define rmtread(Fd, Buffer, Length) \
  (_isrmt (Fd) ? rmt_read__ (Fd - __REM_BIAS, Buffer, Length) \
   : read (Fd, Buffer, Length))

#define rmtwrite(Fd, Buffer, Length) \
  (_isrmt (Fd) ? rmt_write__ (Fd - __REM_BIAS, Buffer, Length) \
   : write (Fd, Buffer, Length))

#define rmtlseek(Fd, Offset, Where) \
  (_isrmt (Fd) ? rmt_lseek__ (Fd - __REM_BIAS, Offset, Where) \
   : lseek (Fd, Offset, Where))

#define rmtclose(Fd) \
  (_isrmt (Fd) ? rmt_close__ (Fd - __REM_BIAS) : close (Fd))

#define rmtioctl(Fd, Request, Argument) \
  (_isrmt (Fd) ? rmt_ioctl__ (Fd - __REM_BIAS, Request, Argument) \
   : ioctl (Fd, Request, Argument))

#define rmtdup(Fd) \
  (_isrmt (Fd) ? (errno = EOPNOTSUPP), -1 : dup (Fd))
                /* FIXME: errno should be read-only */

#define rmtfstat(Fd, Buffer) \
  (_isrmt (Fd) ? (errno = EOPNOTSUPP), -1 : fstat (Fd, Buffer))
                /* FIXME: errno should be read-only */

#define rmtfcntl(Fd, Command, Argument) \
  (_isrmt (Fd) ? (errno = EOPNOTSUPP), -1 : fcntl (Fd, Command, Argument))
                /* FIXME: errno should be read-only */

#define rmtisatty(Fd) \
  (_isrmt (Fd) ? 0 : isatty (Fd))

https://git.savannah.gnu.org/cgit/tar.git/tree//src/extract.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/extract.c used in: Step 653
/* Extract files from a tar archive.
   Copyright (C) 1988, 92, 93, 94, 96, 97 Free Software Foundation, Inc.
   Written by John Gilmore, on 1985-11-19.

   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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include "system.h"

#include <time.h>
time_t time ();

#if HAVE_UTIME_H
# include <utime.h>
#else
struct utimbuf
  {
    long actime;
    long modtime;
  };
#endif

#include "common.h"

static time_t now;      /* current time */
static int we_are_root;     /* true if our effective uid == 0 */
static int newdir_umask;    /* umask when creating new directories */
static int current_umask;   /* current umask (which is set to 0 if -p) */

#if 0
/* "Scratch" space to store the information about a sparse file before
   writing the info into the header or extended header.  */
struct sp_array *sparsearray;

/* Number of elts storable in the sparsearray.  */
int   sp_array_size = 10;
#endif

struct delayed_set_stat
  {
    struct delayed_set_stat *next;
    char *file_name;
    struct stat stat_info;
  };

static struct delayed_set_stat *delayed_set_stat_head;

/*--------------------------.
| Set up to extract files.  |
`--------------------------*/

void
extr_init (void)
{
  now = time ((time_t *) 0);
  we_are_root = geteuid () == 0;

  /* Option -p clears the kernel umask, so it does not affect proper
     restoration of file permissions.  New intermediate directories will
     comply with umask at start of program.  */

  newdir_umask = umask (0);
  if (same_permissions_option)
    current_umask = 0;
  else
    {
      umask (newdir_umask); /* restore the kernel umask */
      current_umask = newdir_umask;
    }

  /* FIXME: Just make sure we can add files in directories we create.  Maybe
     should we later remove permissions we are adding, here?  */
  newdir_umask &= ~0300;
}

/*------------------------------------------------------------------.
| Restore mode for FILE_NAME, from information given in STAT_INFO.  |
`------------------------------------------------------------------*/

static void
set_mode (char *file_name, struct stat *stat_info)
{
  /* We ought to force permission when -k is not selected, because if the
     file already existed, open or creat would save the permission bits from
     the previously created file, ignoring the ones we specified.

     But with -k selected, we know *we* created this file, so the mode
     bits were set by our open.  If the file has abnormal mode bits, we must
     chmod since writing or chown has probably reset them.  If the file is
     normal, we merely skip the chmod.  This works because we did umask (0)
     when -p, so umask will have left the specified mode alone.  */

  if (!keep_old_files_option
      || (stat_info->st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
    if (chmod (file_name, ~current_umask & (int) stat_info->st_mode) < 0)
      ERROR ((0, errno, _("%s: Cannot change mode to %0.4o"),
          file_name, ~current_umask & (int) stat_info->st_mode));
}

/*----------------------------------------------------------------------.
| Restore stat attributes (owner, group, mode and times) for FILE_NAME, |
| using information given in STAT_INFO.  SYMLINK_FLAG is non-zero for a |
| freshly restored symbolic link.                       |
`----------------------------------------------------------------------*/

/* FIXME: About proper restoration of symbolic link attributes, we still do
   not have it right.  Pretesters' reports tell us we need further study and
   probably more configuration.  For now, just use lchown if it exists, and
   punt for the rest.  Sigh!  */

static void
set_stat (char *file_name, struct stat *stat_info, int symlink_flag)
{
  struct utimbuf utimbuf;

  if (!symlink_flag)
    {
      /* We do the utime before the chmod because some versions of utime are
     broken and trash the modes of the file.  */

      if (!touch_option)
    {
      /* We set the accessed time to `now', which is really the time we
         started extracting files, unless incremental_option is used, in
         which case .st_atime is used.  */

      /* FIXME: incremental_option should set ctime too, but how?  */

      if (incremental_option)
        utimbuf.actime = stat_info->st_atime;
      else
        utimbuf.actime = now;

      utimbuf.modtime = stat_info->st_mtime;

      if (utime (file_name, &utimbuf) < 0)
        ERROR ((0, errno,
            _("%s: Could not change access and modification times"),
            file_name));
    }

      /* Some systems allow non-root users to give files away.  Once this
     done, it is not possible anymore to change file permissions, so we
     have to set permissions prior to possibly giving files away.  */

      set_mode (file_name, stat_info);
    }

  /* If we are root, set the owner and group of the extracted file, so we
     extract as the original owner.  Or else, if we are running as a user,
     leave the owner and group as they are, so we extract as that user.  */

  if (we_are_root || same_owner_option)
    {
#if HAVE_LCHOWN

      /* When lchown exists, it should be used to change the attributes of
     the symbolic link itself.  In this case, a mere chown would change
     the attributes of the file the symbolic link is pointing to, and
     should be avoided.  */

      if (symlink_flag)
    {
      if (lchown (file_name, stat_info->st_uid, stat_info->st_gid) < 0)
        ERROR ((0, errno, _("%s: Cannot lchown to uid %d gid %d"),
            file_name, stat_info->st_uid, stat_info->st_gid));
    }
      else
    {
      if (chown (file_name, stat_info->st_uid, stat_info->st_gid) < 0)
        ERROR ((0, errno, _("%s: Cannot chown to uid %d gid %d"),
            file_name, stat_info->st_uid, stat_info->st_gid));
    }

#else /* not HAVE_LCHOWN */

      if (!symlink_flag)

    if (chown (file_name, stat_info->st_uid, stat_info->st_gid) < 0)
      ERROR ((0, errno, _("%s: Cannot chown to uid %d gid %d"),
          file_name, stat_info->st_uid, stat_info->st_gid));

#endif/* not HAVE_LCHOWN */

      if (!symlink_flag)

    /* On a few systems, and in particular, those allowing to give files
       away, changing the owner or group destroys the suid or sgid bits.
       So, when root, let's attempt setting these bits once more.  */

    if (we_are_root && (stat_info->st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
      set_mode (file_name, stat_info);
    }
}

/*-----------------------------------------------------------------------.
| After a file/link/symlink/directory creation has failed, see if it's   |
| because some required directory was not present, and if so, create all |
| required directories.  Return non-zero if a directory was created.     |
`-----------------------------------------------------------------------*/

static int
make_directories (char *file_name)
{
  char *cursor;         /* points into path */
  int did_something = 0;    /* did we do anything yet? */
  int saved_errno = errno;  /* remember caller's errno */
  int status;

  for (cursor = strchr (file_name, '/');
       cursor != NULL;
       cursor = strchr (cursor + 1, '/'))
    {
      /* Avoid mkdir of empty string, if leading or double '/'.  */

      if (cursor == file_name || cursor[-1] == '/')
    continue;

      /* Avoid mkdir where last part of path is '.'.  */

      if (cursor[-1] == '.' && (cursor == file_name + 1 || cursor[-2] == '/'))
    continue;

      *cursor = '\0';       /* truncate the path there */
      status = mkdir (file_name, ~newdir_umask & 0777);

      if (status == 0)
    {
      /* Fix ownership.  */

      if (we_are_root)
        if (chown (file_name, current_stat.st_uid, current_stat.st_gid) < 0)
          ERROR ((0, errno,
              _("%s: Cannot change owner to uid %d, gid %d"),
              file_name, current_stat.st_uid, current_stat.st_gid));

      print_for_mkdir (file_name, cursor - file_name,
               ~newdir_umask & 0777);
      did_something = 1;

      *cursor = '/';
      continue;
    }

      *cursor = '/';

      if (errno == EEXIST
#if MSDOS
      /* Turbo C mkdir gives a funny errno.  */
      || errno == EACCES
#endif
      )
    /* Directory already exists.  */
    continue;

      /* Some other error in the mkdir.  We return to the caller.  */
      break;
    }

  errno = saved_errno;      /* FIXME: errno should be read-only */
  return did_something;     /* tell them to retry if we made one */
}

/*--------------------------------------------------------------------.
| Attempt repairing what went wrong with the extraction.  Delete an   |
| already existing file or create missing intermediate directories.   |
| Return nonzero if we somewhat increased our chances at a successful |
| extraction.  errno is properly restored on zero return.         |
`--------------------------------------------------------------------*/

static int
maybe_recoverable (char *file_name)
{
  switch (errno)
    {
    case EEXIST:
      /* Attempt deleting an existing file.  However, with -k, just stay
     quiet.  */

      if (keep_old_files_option)
    return 0;

      return remove_any_file (file_name, 0);

    case ENOENT:
      /* Attempt creating missing intermediate directories.  */

      return make_directories (file_name);

    default:
      /* Just say we can't do anything about it...  */

      return 0;
    }
}

/*---.
| ?  |
`---*/

static void
extract_sparse_file (int fd, long *sizeleft, long totalsize, char *name)
{
  union block *data_block;
  int sparse_ind = 0;
  int written, count;

  /* FIXME: `data_block' might be used uninitialized in this function.
     Reported by Bruno Haible.  */

  /* assuming sizeleft is initially totalsize */

  while (*sizeleft > 0)
    {
      data_block = find_next_block ();
      if (data_block == NULL)
    {
      ERROR ((0, 0, _("Unexpected EOF on archive file")));
      return;
    }
      lseek (fd, sparsearray[sparse_ind].offset, 0);
      written = sparsearray[sparse_ind++].numbytes;
      while (written > BLOCKSIZE)
    {
      count = write (fd, data_block->buffer, BLOCKSIZE);
      if (count < 0)
        ERROR ((0, errno, _("%s: Could not write to file"), name));
      written -= count;
      *sizeleft -= count;
      set_next_block_after (data_block);
      data_block = find_next_block ();
    }

      count = write (fd, data_block->buffer, (size_t) written);

      if (count < 0)
    ERROR ((0, errno, _("%s: Could not write to file"), name));
      else if (count != written)
    {
      ERROR ((0, 0, _("%s: Could only write %d of %d bytes"),
             name, count, totalsize));
      skip_file ((long) (*sizeleft));
    }

      written -= count;
      *sizeleft -= count;
      set_next_block_after (data_block);
    }
  free (sparsearray);
  set_next_block_after (data_block);
}

/*----------------------------------.
| Extract a file from the archive.  |
`----------------------------------*/

void
extract_archive (void)
{
  union block *data_block;
  int fd;
  int status;
  int name_length;
  int written;
  int openflag;
  long size;
  int skipcrud;
  int counter;
#if 0
  int sparse_ind = 0;
#endif
  union block *exhdr;
  struct delayed_set_stat *data;

#define CURRENT_FILE_NAME (skipcrud + current_file_name)

  set_next_block_after (current_header);
  decode_header (current_header, &current_stat, &current_format, 1);

  if (interactive_option && !confirm ("extract", current_file_name))
    {
      if (current_header->oldgnu_header.isextended)
    skip_extended_headers ();
      skip_file ((long) current_stat.st_size);
      return;
    }

  /* Print the block from `current_header' and `current_stat'.  */

  if (verbose_option)
    print_header ();

  /* Check for fully specified file names and other atrocities.  */

  skipcrud = 0;
  while (!absolute_names_option && CURRENT_FILE_NAME[0] == '/')
    {
      static int warned_once = 0;

      skipcrud++;       /* force relative path */
      if (!warned_once)
    {
      warned_once = 1;
      WARN ((0, 0, _("\
Removing leading `/' from absolute path names in the archive")));
    }
    }

  /* Take a safety backup of a previously existing file.  */

  if (backup_option && !to_stdout_option)
    if (!maybe_backup_file (CURRENT_FILE_NAME, 0))
      {
    ERROR ((0, errno, _("%s: Was unable to backup this file"),
        CURRENT_FILE_NAME));
    if (current_header->oldgnu_header.isextended)
      skip_extended_headers ();
    skip_file ((long) current_stat.st_size);
    return;
      }

  /* Extract the archive entry according to its type.  */

  switch (current_header->header.typeflag)
    {
      /* JK - What we want to do if the file is sparse is loop through
     the array of sparse structures in the header and read in and
     translate the character strings representing 1) the offset at
     which to write and 2) how many bytes to write into numbers,
     which we store into the scratch array, "sparsearray".  This
     array makes our life easier the same way it did in creating the
     tar file that had to deal with a sparse file.

     After we read in the first five (at most) sparse structures, we
     check to see if the file has an extended header, i.e., if more
     sparse structures are needed to describe the contents of the new
     file.  If so, we read in the extended headers and continue to
     store their contents into the sparsearray.  */

    case GNUTYPE_SPARSE:
      sp_array_size = 10;
      sparsearray = (struct sp_array *)
    xmalloc (sp_array_size * sizeof (struct sp_array));

      for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
    {
      sparsearray[counter].offset =
        from_oct (1 + 12,
              current_header->oldgnu_header.sp[counter].offset);
      sparsearray[counter].numbytes =
        from_oct (1 + 12,
              current_header->oldgnu_header.sp[counter].numbytes);
      if (!sparsearray[counter].numbytes)
        break;
    }

      if (current_header->oldgnu_header.isextended)
    {
      /* Read in the list of extended headers and translate them into
         the sparsearray as before.  */

      /* static */ int ind = SPARSES_IN_OLDGNU_HEADER;

      while (1)
        {
          exhdr = find_next_block ();
          for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
        {
          if (counter + ind > sp_array_size - 1)
            {
              /* Realloc the scratch area since we've run out of
             room.  */

              sp_array_size *= 2;
              sparsearray = (struct sp_array *)
            xrealloc (sparsearray,
                  sp_array_size * (sizeof (struct sp_array)));
            }
          /* Compare to 0, or use !(int)..., for Pyramid's dumb
             compiler.  */
          if (exhdr->sparse_header.sp[counter].numbytes == 0)
            break;
          sparsearray[counter + ind].offset =
            from_oct (1 + 12,
                  exhdr->sparse_header.sp[counter].offset);
          sparsearray[counter + ind].numbytes =
            from_oct (1 + 12,
                  exhdr->sparse_header.sp[counter].numbytes);
        }
          if (!exhdr->sparse_header.isextended)
        break;
          else
        {
          ind += SPARSES_IN_SPARSE_HEADER;
          set_next_block_after (exhdr);
        }
        }
      set_next_block_after (exhdr);
    }
      /* Fall through.  */

    case AREGTYPE:
    case REGTYPE:
    case CONTTYPE:

      /* Appears to be a file.  But BSD tar uses the convention that a slash
     suffix means a directory.  */

      name_length = strlen (CURRENT_FILE_NAME) - 1;
      if (CURRENT_FILE_NAME[name_length] == '/')
    goto really_dir;

      /* FIXME: deal with protection issues.  */

    again_file:
      openflag = (keep_old_files_option ?
          O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_EXCL :
          O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_TRUNC)
    | ((current_header->header.typeflag == GNUTYPE_SPARSE) ? 0 : O_APPEND);

      /* JK - The last | is a kludge to solve the problem the O_APPEND
     flag causes with files we are trying to make sparse: when a file
     is opened with O_APPEND, it writes to the last place that
     something was written, thereby ignoring any lseeks that we have
     done.  We add this extra condition to make it able to lseek when
     a file is sparse, i.e., we don't open the new file with this
     flag.  (Grump -- this bug caused me to waste a good deal of
     time, I might add)  */

      if (to_stdout_option)
    {
      fd = 1;
      goto extract_file;
    }

      if (unlink_first_option)
    remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);

#if O_CTG
      /* Contiguous files (on the Masscomp) have to specify the size in
     the open call that creates them.  */

      if (current_header->header.typeflag == CONTTYPE)
    fd = open (CURRENT_FILE_NAME, openflag | O_CTG,
           current_stat.st_mode, current_stat.st_size);
      else
    fd = open (CURRENT_FILE_NAME, openflag, current_stat.st_mode);

#else /* not O_CTG */
      if (current_header->header.typeflag == CONTTYPE)
    {
      static int conttype_diagnosed = 0;

      if (!conttype_diagnosed)
        {
          conttype_diagnosed = 1;
          WARN ((0, 0, _("Extracting contiguous files as regular files")));
        }
    }
      fd = open (CURRENT_FILE_NAME, openflag, current_stat.st_mode);

#endif /* not O_CTG */

      if (fd < 0)
    {
      if (maybe_recoverable (CURRENT_FILE_NAME))
        goto again_file;

      ERROR ((0, errno, _("%s: Could not create file"),
          CURRENT_FILE_NAME));
      if (current_header->oldgnu_header.isextended)
        skip_extended_headers ();
      skip_file ((long) current_stat.st_size);
      if (backup_option)
        undo_last_backup ();
      break;
    }

    extract_file:
      if (current_header->header.typeflag == GNUTYPE_SPARSE)
    {
      char *name;
      int name_length_bis;

      /* Kludge alert.  NAME is assigned to header.name because
         during the extraction, the space that contains the header
         will get scribbled on, and the name will get munged, so any
         error messages that happen to contain the filename will look
         REAL interesting unless we do this.  */

      name_length_bis = strlen (CURRENT_FILE_NAME) + 1;
      name = (char *) xmalloc ((sizeof (char)) * name_length_bis);
      memcpy (name, CURRENT_FILE_NAME, (size_t) name_length_bis);
      size = current_stat.st_size;
      extract_sparse_file (fd, &size, current_stat.st_size, name);
    }
      else
    for (size = current_stat.st_size;
         size > 0;
         size -= written)
      {
#if 0
        long offset, numbytes;
#endif
        if (multi_volume_option)
          {
        assign_string (&save_name, current_file_name);
        save_totsize = current_stat.st_size;
        save_sizeleft = size;
          }

        /* Locate data, determine max length writeable, write it,
           block that we have used the data, then check if the write
           worked.  */

        data_block = find_next_block ();
        if (data_block == NULL)
          {
        ERROR ((0, 0, _("Unexpected EOF on archive file")));
        break;      /* FIXME: What happens, then?  */
          }

        /* If the file is sparse, use the sparsearray that we created
           before to lseek into the new file the proper amount, and to
           see how many bytes we want to write at that position.  */

#if 0
        if (current_header->header.typeflag == GNUTYPE_SPARSE)
          {
        off_t pos;

        pos = lseek (fd, (off_t) sparsearray[sparse_ind].offset, 0);
        fprintf (msg_file, _("%d at %d\n"), (int) pos, sparse_ind);
        written = sparsearray[sparse_ind++].numbytes;
          }
        else
#endif
          written = available_space_after (data_block);

        if (written > size)
          written = size;
        errno = 0;      /* FIXME: errno should be read-only */
        status = write (fd, data_block->buffer, (size_t) written);

        set_next_block_after ((union block *)
                  (data_block->buffer + written - 1));
        if (status == written)
          continue;

        /* Error in writing to file.  Print it, skip to next file in
           archive.  */

        if (status < 0)
          ERROR ((0, errno, _("%s: Could not write to file"),
              CURRENT_FILE_NAME));
        else
          ERROR ((0, 0, _("%s: Could only write %d of %d bytes"),
              CURRENT_FILE_NAME, status, written));
        skip_file ((long) (size - written));
        break;      /* still do the close, mod time, chmod, etc */
      }

      if (multi_volume_option)
    assign_string (&save_name, NULL);

      /* If writing to stdout, don't try to do anything to the filename;
     it doesn't exist, or we don't want to touch it anyway.  */

      if (to_stdout_option)
    break;

#if 0
      if (current_header->header.isextended)
    {
      union block *exhdr;
      int counter;

      for (counter = 0; counter < 21; counter++)
        {
          off_t offset;

          if (!exhdr->sparse_header.sp[counter].numbytes)
        break;
          offset = from_oct (1 + 12, exhdr->sparse_header.sp[counter].offset);
          written = from_oct (1 + 12, exhdr->sparse_header.sp[counter].numbytes);
          lseek (fd, offset, 0);
          status = write (fd, data_block->buffer, written);
          if (status == written)
        continue;
        }
    }
#endif
      status = close (fd);
      if (status < 0)
    {
      ERROR ((0, errno, _("%s: Error while closing"), CURRENT_FILE_NAME));
      if (backup_option)
        undo_last_backup ();
    }

      set_stat (CURRENT_FILE_NAME, &current_stat, 0);
      break;

    case SYMTYPE:
      if (to_stdout_option)
    break;

#ifdef S_ISLNK
      if (unlink_first_option)
    remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);

      while (status = symlink (current_link_name, CURRENT_FILE_NAME),
         status != 0)
    if (!maybe_recoverable (CURRENT_FILE_NAME))
      break;

      if (status == 0)

    /* Setting the attributes of symbolic links might, on some systems,
       change the pointed to file, instead of the symbolic link itself.
       At least some of these systems have a lchown call, and the
       set_stat routine knows about this.    */

    set_stat (CURRENT_FILE_NAME, &current_stat, 1);

      else
    {
      ERROR ((0, errno, _("%s: Could not create symlink to `%s'"),
          CURRENT_FILE_NAME, current_link_name));
      if (backup_option)
        undo_last_backup ();
    }
      break;

#else /* not S_ISLNK */
      {
    static int warned_once = 0;

    if (!warned_once)
      {
        warned_once = 1;
        WARN ((0, 0, _("\
Attempting extraction of symbolic links as hard links")));
      }
      }
      /* Fall through.  */

#endif /* not S_ISLNK */

    case LNKTYPE:
      if (to_stdout_option)
    break;

      if (unlink_first_option)
    remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);

    again_link:
      {
    struct stat st1, st2;

    /* MSDOS does not implement links.  However, djgpp's link() actually
       copies the file.  */
    status = link (current_link_name, CURRENT_FILE_NAME);

    if (status == 0)
      break;
    if (maybe_recoverable (CURRENT_FILE_NAME))
      goto again_link;

    if (incremental_option && errno == EEXIST)
      break;
    if (stat (current_link_name, &st1) == 0
        && stat (CURRENT_FILE_NAME, &st2) == 0
        && st1.st_dev == st2.st_dev
        && st1.st_ino == st2.st_ino)
      break;

    ERROR ((0, errno, _("%s: Could not link to `%s'"),
        CURRENT_FILE_NAME, current_link_name));
    if (backup_option)
      undo_last_backup ();
      }
      break;

#if S_IFCHR
    case CHRTYPE:
      current_stat.st_mode |= S_IFCHR;
      goto make_node;
#endif

#if S_IFBLK
    case BLKTYPE:
      current_stat.st_mode |= S_IFBLK;
#endif

#if defined(S_IFCHR) || defined(S_IFBLK)
    make_node:
      if (to_stdout_option)
    break;

      if (unlink_first_option)
    remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);

      status = mknod (CURRENT_FILE_NAME, (int) current_stat.st_mode,
             (int) current_stat.st_rdev);
      if (status != 0)
    {
      if (maybe_recoverable (CURRENT_FILE_NAME))
        goto make_node;

      ERROR ((0, errno, _("%s: Could not make node"), CURRENT_FILE_NAME));
      if (backup_option)
        undo_last_backup ();
      break;
    };
      set_stat (CURRENT_FILE_NAME, &current_stat, 0);
      break;
#endif

#ifdef S_ISFIFO
    case FIFOTYPE:
      if (to_stdout_option)
    break;

      if (unlink_first_option)
    remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);

      while (status = mkfifo (CURRENT_FILE_NAME, (int) current_stat.st_mode),
         status != 0)
    if (!maybe_recoverable (CURRENT_FILE_NAME))
      break;

      if (status == 0)
    set_stat (CURRENT_FILE_NAME, &current_stat, 0);
      else
    {
      ERROR ((0, errno, _("%s: Could not make fifo"), CURRENT_FILE_NAME));
      if (backup_option)
        undo_last_backup ();
    }
      break;
#endif

    case DIRTYPE:
    case GNUTYPE_DUMPDIR:
      name_length = strlen (CURRENT_FILE_NAME) - 1;

    really_dir:
      /* Check for trailing /, and zap as many as we find.  */
      while (name_length && CURRENT_FILE_NAME[name_length] == '/')
    CURRENT_FILE_NAME[name_length--] = '\0';

      if (incremental_option)
    {
      /* Read the entry and delete files that aren't listed in the
         archive.  */

      gnu_restore (skipcrud);
    }
      else if (current_header->header.typeflag == GNUTYPE_DUMPDIR)
    skip_file ((long) (current_stat.st_size));

      if (to_stdout_option)
    break;

    again_dir:
      status = mkdir (CURRENT_FILE_NAME,
             (we_are_root ? 0 : 0300) | (int) current_stat.st_mode);
      if (status != 0)
    {
      /* If the directory creation fails, let's consider immediately the
         case where the directory already exists.  We have three good
         reasons for clearing out this case before attempting recovery.

         1) It would not be efficient recovering the error by deleting
         the directory in maybe_recoverable, then recreating it right
         away.  We only hope we will be able to adjust its permissions
         adequately, later.

         2) Removing the directory might fail if it is not empty.  By
         exception, this real error is traditionally not reported.

         3) Let's suppose `DIR' already exists and we are about to
         extract `DIR/../DIR'.  This would fail because the directory
         already exists, and maybe_recoverable would react by removing
         `DIR'.  This then would fail again because there are missing
         intermediate directories, and maybe_recoverable would react by
         creating `DIR'.  We would then have an extraction loop.  */

      if (errno == EEXIST)
        {
          struct stat st1;
          int saved_errno = errno;

          if (stat (CURRENT_FILE_NAME, &st1) == 0 && S_ISDIR (st1.st_mode))
        goto check_perms;

          errno = saved_errno; /* FIXME: errno should be read-only */
        }

      if (maybe_recoverable (CURRENT_FILE_NAME))
        goto again_dir;

      /* If we're trying to create '.', let it be.  */

      /* FIXME: Strange style...  */

      if (CURRENT_FILE_NAME[name_length] == '.'
          && (name_length == 0
          || CURRENT_FILE_NAME[name_length - 1] == '/'))
        goto check_perms;

      ERROR ((0, errno, _("%s: Could not create directory"),
          CURRENT_FILE_NAME));
      if (backup_option)
        undo_last_backup ();
      break;
    }

    check_perms:
      if (!we_are_root && 0300 != (0300 & (int) current_stat.st_mode))
    {
      current_stat.st_mode |= 0300;
      WARN ((0, 0, _("Added write and execute permission to directory %s"),
         CURRENT_FILE_NAME));
    }

#if !MSDOS
      /* MSDOS does not associate timestamps with directories.   In this
     case, no need to try delaying their restoration.  */

      if (touch_option)

    /* FIXME: I do not believe in this.  Ignoring time stamps does not
       alleviate the need of delaying the restoration of directories'
       mode.  Let's ponder this for a little while.  */

    set_mode (CURRENT_FILE_NAME, &current_stat);

      else
    {
      data = ((struct delayed_set_stat *)
              xmalloc (sizeof (struct delayed_set_stat)));
      data->file_name = xstrdup (CURRENT_FILE_NAME);
      data->stat_info = current_stat;
      data->next = delayed_set_stat_head;
      delayed_set_stat_head = data;
    }
#endif /* !MSDOS */
      break;

    case GNUTYPE_VOLHDR:
      if (verbose_option)
    fprintf (stdlis, _("Reading %s\n"), current_file_name);
      break;

    case GNUTYPE_NAMES:
      extract_mangle ();
      break;

    case GNUTYPE_MULTIVOL:
      ERROR ((0, 0, _("\
Cannot extract `%s' -- file is continued from another volume"),
          current_file_name));
      skip_file ((long) current_stat.st_size);
      if (backup_option)
    undo_last_backup ();
      break;

    case GNUTYPE_LONGNAME:
    case GNUTYPE_LONGLINK:
      ERROR ((0, 0, _("Visible long name error")));
      skip_file ((long) current_stat.st_size);
      if (backup_option)
    undo_last_backup ();
      break;

    default:
      WARN ((0, 0,
         _("Unknown file type '%c' for %s, extracted as normal file"),
         current_header->header.typeflag, CURRENT_FILE_NAME));
      goto again_file;
    }

#undef CURRENT_FILE_NAME
}

/*----------------------------------------------------------------.
| Set back the utime and mode for all the extracted directories.  |
`----------------------------------------------------------------*/

void
apply_delayed_set_stat (void)
{
  struct delayed_set_stat *data;

  while (delayed_set_stat_head != NULL)
    {
      data = delayed_set_stat_head;
      delayed_set_stat_head = delayed_set_stat_head->next;
      set_stat (data->file_name, &data->stat_info, 0);
      free (data->file_name);
      free (data);
    }
}

https://git.savannah.gnu.org/cgit/tar.git/tree//src/incremen.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/incremen.c used in: Step 654
/* GNU dump extensions to tar.
   Copyright (C) 1988, 92, 93, 94, 96, 97 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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include "system.h"

#include <time.h>
time_t time ();

#define ISDIGIT(Char) (ISASCII (Char) && isdigit (Char))
#define ISSPACE(Char) (ISASCII (Char) && isspace (Char))

#include "common.h"


/* Variable sized generic character buffers.  */

struct accumulator
{
  int allocated;
  int length;
  char *pointer;
};

/* Amount of space guaranteed just after a reallocation.  */
#define ACCUMULATOR_SLACK 50

/*---------------------------------------------------------.
| Return the accumulated data from an ACCUMULATOR buffer.  |
`---------------------------------------------------------*/

static char *
get_accumulator (struct accumulator *accumulator)
{
  return accumulator->pointer;
}

/*-----------------------------------------------.
| Allocate and return a new accumulator buffer.  |
`-----------------------------------------------*/

static struct accumulator *
new_accumulator (void)
{
  struct accumulator *accumulator
    = (struct accumulator *) xmalloc (sizeof (struct accumulator));

  accumulator->allocated = ACCUMULATOR_SLACK;
  accumulator->pointer = (char *) xmalloc (ACCUMULATOR_SLACK);
  accumulator->length = 0;
  return accumulator;
}

/*-----------------------------------.
| Deallocate an ACCUMULATOR buffer.  |
`-----------------------------------*/

static void
delete_accumulator (struct accumulator *accumulator)
{
  free (accumulator->pointer);
  free (accumulator);
}

/*----------------------------------------------------------------------.
| At the end of an ACCUMULATOR buffer, add a DATA block of SIZE bytes.  |
`----------------------------------------------------------------------*/

static void
add_to_accumulator (struct accumulator *accumulator,
            const char *data, int size)
{
  if (accumulator->length + size > accumulator->allocated)
    {
      accumulator->allocated = accumulator->length + size + ACCUMULATOR_SLACK;
      accumulator->pointer = (char *)
    xrealloc (accumulator->pointer, (size_t) accumulator->allocated);
    }
  memcpy (accumulator->pointer + accumulator->length, data, (size_t) size);
  accumulator->length += size;
}


/* Incremental dump specialities.  */

/* Current time.  */
static time_t time_now;

/* List of directory names.  */
struct directory
  {
    struct directory *next; /* next entry in list */
    const char *name;       /* path name of directory */
    int device_number;      /* device number for directory */
    int inode_number;       /* inode number for directory */
    int allnew;
    const char *dir_text;
  };
static struct directory *directory_list = NULL;

/*-------------------------------------------------------------------.
| Create and link a new directory entry for directory NAME, having a |
| DEVICE_NUMBER and a INODE_NUMBER, with some TEXT.          |
`-------------------------------------------------------------------*/

static void
note_directory (char *name, dev_t device_number, ino_t inode_number,
        const char *text)
{
  struct directory *directory
    = (struct directory *) xmalloc (sizeof (struct directory));

  directory->next = directory_list;
  directory_list = directory;

  directory->device_number = device_number;
  directory->inode_number = inode_number;
  directory->name = xstrdup (name);
  directory->dir_text = text;
  directory->allnew = 0;
}

/*------------------------------------------------------------------------.
| Return a directory entry for a given path NAME, or NULL if none found.  |
`------------------------------------------------------------------------*/

static struct directory *
find_directory (char *name)
{
  struct directory *directory;

  for (directory = directory_list;
       directory;
       directory = directory->next)
    {
      if (!strcmp (directory->name, name))
    return directory;
    }
  return NULL;
}

/*---.
| ?  |
`---*/

static int
compare_dirents (const voidstar first, const voidstar second)
{
  return strcmp ((*(char *const *) first) + 1,
         (*(char *const *) second) + 1);
}

/*---.
| ?  |
`---*/

char *
get_directory_contents (char *path, int device)
{
  struct accumulator *accumulator;

  /* Recursively scan the given PATH.  */

  {
    DIR *dirp = opendir (path); /* for scanning directory */
    struct dirent *entry;   /* directory entry being scanned */
    char *name_buffer;      /* directory, `/', and directory member */
    int name_buffer_size;   /* allocated size of name_buffer, minus 2 */
    int name_length;        /* used length in name_buffer */
    struct directory *directory; /* for checking if already already seen */
    int all_children;

    if (dirp == NULL)
      {
    ERROR ((0, errno, _("Cannot open directory %s"), path));
    return NULL;
      }
    errno = 0;          /* FIXME: errno should be read-only */

    name_buffer_size = strlen (path) + NAME_FIELD_SIZE;
    name_buffer = xmalloc ((size_t) (name_buffer_size + 2));
    strcpy (name_buffer, path);
    if (path[strlen (path) - 1] != '/')
      strcat (name_buffer, "/");
    name_length = strlen (name_buffer);

    directory = find_directory (path);
    all_children = directory ? directory->allnew : 0;

    accumulator = new_accumulator ();

    while (entry = readdir (dirp), entry)
      {
    struct stat stat_data;

    /* Skip `.' and `..'.  */

    if (is_dot_or_dotdot (entry->d_name))
      continue;

    if ((int) NAMLEN (entry) + name_length >= name_buffer_size)
      {
        while ((int) NAMLEN (entry) + name_length >= name_buffer_size)
          name_buffer_size += NAME_FIELD_SIZE;
        name_buffer = (char *)
          xrealloc (name_buffer, (size_t) (name_buffer_size + 2));
      }
    strcpy (name_buffer + name_length, entry->d_name);

    if (dereference_option
#ifdef AIX
        ? statx (name_buffer, &stat_data, STATSIZE, STX_HIDDEN)
        : statx (name_buffer, &stat_data, STATSIZE, STX_HIDDEN | STX_LINK)
#else
        ? stat (name_buffer, &stat_data)
        : lstat (name_buffer, &stat_data)
#endif
        )
      {
        ERROR ((0, errno, _("Cannot stat %s"), name_buffer));
        continue;
      }

    if ((one_file_system_option && device != stat_data.st_dev)
        || (exclude_option && check_exclude (name_buffer)))
      add_to_accumulator (accumulator, "N", 1);

#ifdef AIX
    else if (S_ISHIDDEN (stat_data.st_mode))
      {
        add_to_accumulator (accumulator, "D", 1);
        strcat (entry->d_name, "A");
        entry->d_namlen++;
      }
#endif

    else if (S_ISDIR (stat_data.st_mode))
      {
        if (directory = find_directory (name_buffer), directory)
          {
        /* Devices having the high bit set are NFS devices, which are
           attributed somewhat randomly in automounting situations.
           For avoiding spurious incremental redumping of directories,
           we have to plainly consider all NFS devices as equal,
           relying on the i-node only to establish differences.  */

        /* FIXME: Göran Uddeborg <goeran@uddeborg.pp.se> says, on
           1996-09-20, that SunOS 5/Solaris 2 uses unsigned long for
           the device number type.  */

        if ((((short) directory->device_number >= 0
              || (short) stat_data.st_dev >= 0)
             && directory->device_number != stat_data.st_dev)
            || directory->inode_number != stat_data.st_ino)
          {
            if (verbose_option)
              WARN ((0, 0, _("Directory %s has been renamed"),
                 name_buffer));
            directory->allnew = 1;
            directory->device_number = stat_data.st_dev;
            directory->inode_number = stat_data.st_ino;
          }
        directory->dir_text = "";
          }
        else
          {
        if (verbose_option)
          WARN ((0, 0, _("Directory %s is new"), name_buffer));
        note_directory (name_buffer, stat_data.st_dev, stat_data.st_ino,
                "");
        directory = find_directory (name_buffer);
        directory->allnew = 1;
          }
        if (all_children && directory)
          directory->allnew = 1;

        add_to_accumulator (accumulator, "D", 1);
      }

    else
      if (!all_children
          && stat_data.st_mtime < newer_mtime_option
          && (!after_date_option
          || stat_data.st_ctime < newer_ctime_option))
        add_to_accumulator (accumulator, "N", 1);
      else
        add_to_accumulator (accumulator, "Y", 1);

    add_to_accumulator (accumulator,
                entry->d_name, (int) (NAMLEN (entry) + 1));
      }
    add_to_accumulator (accumulator, "\000\000", 2);

    free (name_buffer);
    closedir (dirp);
  }

  /* Sort the contents of the directory, now that we have it all.  */

  {
    char *pointer = get_accumulator (accumulator);
    size_t counter;
    char *cursor;
    char *buffer;
    char **array;
    char **array_cursor;

    counter = 0;
    for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
      counter++;

    if (counter == 0)
      {
    delete_accumulator (accumulator);
    return NULL;
      }

    array = (char **) xmalloc (sizeof (char *) * (counter + 1));

    array_cursor = array;
    for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
      *array_cursor++ = cursor;
    *array_cursor = NULL;

    qsort ((voidstar) array, counter, sizeof (char *), compare_dirents);

    buffer = (char *) xmalloc ((size_t) (cursor - pointer + 2));

    cursor = buffer;
    for (array_cursor = array; *array_cursor; array_cursor++)
      {
    char *string = *array_cursor;

    while ((*cursor++ = *string++))
      continue;
      }
    *cursor = '\0';

    delete_accumulator (accumulator);
    free (array);
    return buffer;
  }
}

/*----------------------------------------------------------------------.
| Add all the files in PATH, which is a directory, to the namelist.  If |
| any of the files is a directory, recurse on the subdirectory.         |
`----------------------------------------------------------------------*/

static void
add_hierarchy_to_namelist (char *path, int device)
{
  char *buffer = get_directory_contents (path, device);

  {
    struct name *name;

    for (name = namelist; name; name = name->next)
      if (strcmp (name->name, path) == 0)
      break;
    if (name)
      name->dir_contents = buffer ? buffer : "\0\0\0\0";
  }

  if (buffer)
    {
      int name_length = strlen (path);
      int allocated_length = (name_length >= NAME_FIELD_SIZE
                  ? name_length + NAME_FIELD_SIZE
                  : NAME_FIELD_SIZE);
      char *name_buffer = xmalloc ((size_t) (allocated_length + 1));
                /* FIXME: + 2 above?  */
      char *string;
      int string_length;

      strcpy (name_buffer, path);
      if (name_buffer[name_length - 1] != '/')
    {
      name_buffer[name_length++] = '/';
      name_buffer[name_length] = '\0';
    }

      for (string = buffer; *string; string += string_length + 1)
    {
      string_length = strlen (string);
      if (*string == 'D')
        {
          if (name_length + string_length >= allocated_length)
        {
          while (name_length + string_length >= allocated_length)
            allocated_length += NAME_FIELD_SIZE;
          name_buffer = (char *)
            xrealloc (name_buffer, (size_t) (allocated_length + 1));
        }
          strcpy (name_buffer + name_length, string + 1);
          addname (name_buffer);
          add_hierarchy_to_namelist (name_buffer, device);
        }
    }

      free (name_buffer);
    }
}


/*---.
| ?  |
`---*/

static void
read_directory_file (void)
{
  dev_t device_number;
  ino_t inode_number;
  char *strp;
  FILE *fp;
  char buf[512];
  static char *path = NULL;

  if (path == NULL)
    path = xmalloc (PATH_MAX);
  time (&time_now);
  if (listed_incremental_option[0] != '/')
    {
#if HAVE_GETCWD
      if (!getcwd (path, PATH_MAX))
    FATAL_ERROR ((0, 0, _("Could not get current directory")));
#else
      char *getwd ();

      if (!getwd (path))
    FATAL_ERROR ((0, 0, _("Could not get current directory: %s"), path));
#endif

      if (strlen (path) + 1 + strlen (listed_incremental_option) + 1 > PATH_MAX)
    ERROR ((TAREXIT_FAILURE, 0, _("File name %s/%s too long"),
        path, listed_incremental_option));

      strcat (path, "/");
      strcat (path, listed_incremental_option);
      listed_incremental_option = path;
    }
  fp = fopen (listed_incremental_option, "r");
  if (fp == 0 && errno != ENOENT)
    {
      ERROR ((0, errno, _("Cannot open %s"), listed_incremental_option));
      return;
    }
  if (!fp)
    return;
  fgets (buf, sizeof (buf), fp);

  /* FIXME: Using after_date_option as a first time flag looks fairly
     dubious to me!  So, using -N with incremental might be buggy just
     because of the next few lines.  I saw a few unexplained, almost harsh
     advices, from other GNU people, about *not* using -N with incremental
     dumps, and here might lie (part of) the reason.  */
  if (!after_date_option)
    {
      newer_mtime_option = atol (buf);
      after_date_option = 1;
    }

  while (fgets (buf, sizeof (buf), fp))
    {
      strp = &buf[strlen (buf)];
      if (strp[-1] == '\n')
    strp[-1] = '\0';
      /* FIXME: For files ending with an incomplete line, maybe a NUL might
     be missing, here...  */

      strp = buf;
      device_number = atol (strp);
      while (ISDIGIT (*strp))
    strp++;
      inode_number = atol (strp);
      while (ISSPACE (*strp))
    strp++;
      while (ISDIGIT (*strp))
    strp++;
      strp++;
      unquote_string (strp);
      note_directory (strp, device_number, inode_number, NULL);
    }
  if (fclose (fp) == EOF)
    ERROR ((0, errno, "%s", listed_incremental_option));
}

/*---.
| ?  |
`---*/

void
write_dir_file (void)
{
  FILE *fp;
  struct directory *directory;
  char *str;

  fp = fopen (listed_incremental_option, "w");
  if (fp == 0)
    {
      ERROR ((0, errno, _("Cannot write to %s"), listed_incremental_option));
      return;
    }
  fprintf (fp, "%lu\n", time_now);
  for (directory = directory_list; directory; directory = directory->next)
    {
      if (!directory->dir_text)
    continue;
      str = quote_copy_string (directory->name);
      if (str)
    {
      fprintf (fp, "%u %u %s\n", directory->device_number,
           directory->inode_number, str);
      free (str);
    }
      else
    fprintf (fp, "%u %u %s\n", directory->device_number,
         directory->inode_number, directory->name);
    }
  if (fclose (fp) == EOF)
    ERROR ((0, errno, "%s", listed_incremental_option));
}

/*---.
| ?  |
`---*/

static int
compare_names (char *param1, char *param2)
{
  struct name *n1 = (struct name *) param1;
  struct name *n2 = (struct name *) param2;

  if (n1->found)
    return n2->found ? strcmp (n1->name, n2->name) : -1;

  if (n2->found)
    return 1;

  return strcmp (n1->name, n2->name);
}

/*-------------------------------------------------------------------------.
| Collect all the names from argv[] (or whatever), then expand them into a |
| directory tree, and put all the directories at the beginning.        |
`-------------------------------------------------------------------------*/

void
collect_and_sort_names (void)
{
  struct name *name;
  struct name *next_name;
  int num_names;
  struct stat statbuf;

  name_gather ();

  if (listed_incremental_option)
    read_directory_file ();

  if (!namelist)
    addname (".");

  for (name = namelist; name; name = next_name)
    {
      next_name = name->next;
      if (name->found || name->dir_contents)
    continue;
      if (name->regexp)     /* FIXME: just skip regexps for now */
    continue;
      if (name->change_dir)
    if (chdir (name->change_dir) < 0)
      {
        ERROR ((0, errno, _("Cannot chdir to %s"), name->change_dir));
        continue;
      }

      if (
#ifdef AIX
      statx (name->name, &statbuf, STATSIZE, STX_HIDDEN | STX_LINK)
#else
      lstat (name->name, &statbuf) < 0
#endif
      )
    {
      ERROR ((0, errno, _("Cannot stat %s"), name->name));
      continue;
    }
      if (S_ISDIR (statbuf.st_mode))
    {
      name->found = 1;
      add_hierarchy_to_namelist (name->name, statbuf.st_dev);
    }
    }

  num_names = 0;
  for (name = namelist; name; name = name->next)
    num_names++;
  namelist = (struct name *)
    merge_sort ((voidstar) namelist, num_names,
        (char *) (&(namelist->next)) - (char *) namelist,
        compare_names);

  for (name = namelist; name; name = name->next)
    name->found = 0;

  if (listed_incremental_option)
    write_dir_file ();
}


/* Restoration of incremental dumps.  */

/*---.
| ?  |
`---*/

void
gnu_restore (int skipcrud)
{
  char *current_dir;
  char *archive_dir;
  struct accumulator *accumulator;
  char *p;
  DIR *dirp;
  struct dirent *d;
  char *cur, *arc;
  long size, copied;
  union block *data_block;
  char *to;

#define CURRENT_FILE_NAME (skipcrud + current_file_name)

  dirp = opendir (CURRENT_FILE_NAME);

  if (!dirp)
    {
      /* The directory doesn't exist now.  It'll be created.  In any
     case, we don't have to delete any files out of it.  */

      skip_file ((long) current_stat.st_size);
      return;
    }

  accumulator = new_accumulator ();
  while (d = readdir (dirp), d)
    {
      if (is_dot_or_dotdot (d->d_name))
    continue;

      add_to_accumulator (accumulator, d->d_name, (int) (NAMLEN (d) + 1));
    }
  closedir (dirp);
  add_to_accumulator (accumulator, "", 1);

  current_dir = get_accumulator (accumulator);
  archive_dir = (char *) xmalloc ((size_t) current_stat.st_size);
  to = archive_dir;
  for (size = current_stat.st_size; size > 0; size -= copied)
    {
      data_block = find_next_block ();
      if (!data_block)
    {
      ERROR ((0, 0, _("Unexpected EOF in archive")));
      break;        /* FIXME: What happens then?  */
    }
      copied = available_space_after (data_block);
      if (copied > size)
    copied = size;
      memcpy (to, data_block->buffer, (size_t) copied);
      to += copied;
      set_next_block_after ((union block *)
                (data_block->buffer + copied - 1));
    }

  for (cur = current_dir; *cur; cur += strlen (cur) + 1)
    {
      for (arc = archive_dir; *arc; arc += strlen (arc) + 1)
    {
      arc++;
      if (!strcmp (arc, cur))
        break;
    }
      if (*arc == '\0')
    {
      p = new_name (CURRENT_FILE_NAME, cur);
      if (interactive_option && !confirm ("delete", p))
        {
          free (p);
          continue;
        }
      if (verbose_option)
        fprintf (stdlis, _("%s: Deleting %s\n"), program_name, p);
      if (!remove_any_file (p, 1))
        ERROR ((0, errno, _("Error while deleting %s"), p));
      free (p);
    }

    }
  delete_accumulator (accumulator);
  free (archive_dir);

#undef CURRENT_FILE_NAME
}

https://git.savannah.gnu.org/cgit/tar.git/tree//src/list.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/list.c used in: Step 655
/* List a tar archive, with support routines for reading a tar archive.
   Copyright (C) 1988, 92, 93, 94, 96, 97 Free Software Foundation, Inc.
   Written by John Gilmore, on 1985-08-26.

   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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Define to non-zero for forcing old ctime() instead of isotime().  */
#undef USE_OLD_CTIME

#include "system.h"

#include <ctype.h>
#include <time.h>

#define ISODIGIT(Char) \
  ((unsigned char) (Char) >= '0' && (unsigned char) (Char) <= '7')
#define ISSPACE(Char) (ISASCII (Char) && isspace (Char))

#include "common.h"

union block *current_header;    /* points to current archive header */
struct stat current_stat;   /* stat struct corresponding */
enum archive_format current_format; /* recognized format */

/*-----------------------------------.
| Main loop for reading an archive.  |
`-----------------------------------*/

void
read_and (void (*do_something) ())
{
  enum read_header status = HEADER_STILL_UNREAD;
  enum read_header prev_status;
  char save_typeflag;

  name_gather ();
  open_archive (ACCESS_READ);

  while (1)
    {
      prev_status = status;
      status = read_header ();
      switch (status)
    {
    case HEADER_STILL_UNREAD:
      abort ();

    case HEADER_SUCCESS:

      /* Valid header.  We should decode next field (mode) first.
         Ensure incoming names are null terminated.  */

      /* FIXME: This is a quick kludge before 1.12 goes out.  */
      current_stat.st_mtime
        = from_oct (1 + 12, current_header->header.mtime);

      if (!name_match (current_file_name)
          || current_stat.st_mtime < newer_mtime_option
          || (exclude_option && check_exclude (current_file_name)))
        {
          int isextended = 0;

          if (current_header->header.typeflag == GNUTYPE_VOLHDR
          || current_header->header.typeflag == GNUTYPE_MULTIVOL
          || current_header->header.typeflag == GNUTYPE_NAMES)
        {
          (*do_something) ();
          continue;
        }
          if (show_omitted_dirs_option
          && current_header->header.typeflag == DIRTYPE)
        WARN ((0, 0, _("Omitting %s"), current_file_name));

          /* Skip past it in the archive.  */

          if (current_header->oldgnu_header.isextended)
        isextended = 1;
          save_typeflag = current_header->header.typeflag;
          set_next_block_after (current_header);
          if (isextended)
        {
#if 0
          union block *exhdr;

          while (1)
            {
              exhdr = find_next_block ();
              if (!exhdr->sparse_header.isextended)
            {
              set_next_block_after (exhdr);
              break;
            }
            }
          set_next_block_after (exhdr);
#endif
          skip_extended_headers ();
        }

          /* Skip to the next header on the archive.  */

          if (save_typeflag != DIRTYPE)
        skip_file ((long) current_stat.st_size);
          continue;
        }

      (*do_something) ();
      continue;

    case HEADER_ZERO_BLOCK:
      if (block_number_option)
        fprintf (stdlis, _("block %10ld: ** Block of NULs **\n"),
             current_block_ordinal ());

      set_next_block_after (current_header);
      status = prev_status;
      if (ignore_zeros_option)
        continue;
      break;

    case HEADER_END_OF_FILE:
      if (block_number_option)
        fprintf (stdlis, _("block %10ld: ** End of File **\n"),
             current_block_ordinal ());
      break;

    case HEADER_FAILURE:
      /* If the previous header was good, tell them that we are
         skipping bad ones.  */
      set_next_block_after (current_header);
      switch (prev_status)
        {
        case HEADER_STILL_UNREAD:
          WARN ((0, 0, _("Hmm, this doesn't look like a tar archive")));
          /* Fall through.  */

        case HEADER_ZERO_BLOCK:
        case HEADER_SUCCESS:
          WARN ((0, 0, _("Skipping to next file header")));
          break;

        case HEADER_END_OF_FILE:
        case HEADER_FAILURE:
          /* We are in the middle of a cascade of errors.  */
          break;
        }
      continue;
    }
      break;
    }

  apply_delayed_set_stat ();
  close_archive ();
  names_notfound ();        /* print names not found */
}

/*---------------------------------------------.
| Print a header block, based on tar options.  |
`---------------------------------------------*/

void
list_archive (void)
{
  int isextended = 0;       /* to remember if current_header is extended */

  /* Print the header block.  */

  if (verbose_option)
    {
      if (verbose_option > 1)
    decode_header (current_header, &current_stat, &current_format, 0);
      print_header ();
    }

  if (incremental_option && current_header->header.typeflag == GNUTYPE_DUMPDIR)
    {
      size_t size, written, check;
      union block *data_block;

      set_next_block_after (current_header);
      if (multi_volume_option)
    {
      assign_string (&save_name, current_file_name);
      save_totsize = current_stat.st_size;
    }
      for (size = current_stat.st_size; size > 0; size -= written)
    {
      if (multi_volume_option)
        save_sizeleft = size;
      data_block = find_next_block ();
      if (!data_block)
        {
          ERROR ((0, 0, _("EOF in archive file")));
          break;        /* FIXME: What happens, then?  */
        }
      written = available_space_after (data_block);
      if (written > size)
        written = size;
      errno = 0;        /* FIXME: errno should be read-only */
      check = fwrite (data_block->buffer, sizeof (char), written, stdlis);
      set_next_block_after ((union block *)
                (data_block->buffer + written - 1));
      if (check != written)
        {
          ERROR ((0, errno, _("Only wrote %ld of %ld bytes to file %s"),
              check, written, current_file_name));
          skip_file ((long) (size - written));
          break;
        }
    }
      if (multi_volume_option)
    assign_string (&save_name, NULL);
      fputc ('\n', stdlis);
      fflush (stdlis);
      return;

    }

  /* Check to see if we have an extended header to skip over also.  */

  if (current_header->oldgnu_header.isextended)
    isextended = 1;

  /* Skip past the header in the archive.  */

  set_next_block_after (current_header);

  /* If we needed to skip any extended headers, do so now, by reading
     extended headers and skipping past them in the archive.  */

  if (isextended)
    {
#if 0
      union block *exhdr;

      while (1)
    {
      exhdr = find_next_block ();

      if (!exhdr->sparse_header.isextended)
        {
          set_next_block_after (exhdr);
          break;
        }
      set_next_block_after (exhdr);
    }
#endif
      skip_extended_headers ();
    }

  if (multi_volume_option)
    assign_string (&save_name, current_file_name);

  /* Skip to the next header on the archive.  */

  skip_file ((long) current_stat.st_size);

  if (multi_volume_option)
    assign_string (&save_name, NULL);
}

/*-----------------------------------------------------------------------.
| Read a block that's supposed to be a header block.  Return its address |
| in "current_header", and if it is good, the file's size in             |
| current_stat.st_size.                                                  |
|                                                                        |
| Return 1 for success, 0 if the checksum is bad, EOF on eof, 2 for a    |
| block full of zeros (EOF marker).                                      |
|                                                                        |
| You must always set_next_block_after(current_header) to skip past the  |
| header which this routine reads.                                       |
`-----------------------------------------------------------------------*/

/* The standard BSD tar sources create the checksum by adding up the
   bytes in the header as type char.  I think the type char was unsigned
   on the PDP-11, but it's signed on the Next and Sun.  It looks like the
   sources to BSD tar were never changed to compute the checksum
   currectly, so both the Sun and Next add the bytes of the header as
   signed chars.  This doesn't cause a problem until you get a file with
   a name containing characters with the high bit set.  So read_header
   computes two checksums -- signed and unsigned.  */

/* FIXME: The signed checksum computation is broken on machines where char's
   are unsigned.  It's uneasy to handle all cases correctly...  */

enum read_header
read_header (void)
{
  int i;
  long unsigned_sum;        /* the POSIX one :-) */
  long signed_sum;      /* the Sun one :-( */
  long recorded_sum;
  char *p;
  union block *header;
  char **longp;
  char *bp;
  union block *data_block;
  int size, written;
  static char *next_long_name, *next_long_link;

  while (1)
    {
      header = find_next_block ();
      current_header = header;
      if (!header)
    return HEADER_END_OF_FILE;

      recorded_sum
    = from_oct (sizeof header->header.chksum, header->header.chksum);

      unsigned_sum = 0;
      signed_sum = 0;
      p = header->buffer;
      for (i = sizeof (*header); --i >= 0;)
    {
      /* We can't use unsigned char here because of old compilers,
         e.g. V7.  */

      unsigned_sum += 0xFF & *p;
      signed_sum += *p++;
    }

      /* Adjust checksum to count the "chksum" field as blanks.  */

      for (i = sizeof (header->header.chksum); --i >= 0;)
    {
      unsigned_sum -= 0xFF & header->header.chksum[i];
      signed_sum -= header->header.chksum[i];
    }
      unsigned_sum += ' ' * sizeof header->header.chksum;
      signed_sum += ' ' * sizeof header->header.chksum;

      if (unsigned_sum == sizeof header->header.chksum * ' ')
    {
      /* This is a zeroed block...whole block is 0's except for the
         blanks we faked for the checksum field.  */

      return HEADER_ZERO_BLOCK;
    }

      if (unsigned_sum != recorded_sum && signed_sum != recorded_sum)
    return HEADER_FAILURE;

      /* Good block.  Decode file size and return.  */

      if (header->header.typeflag == LNKTYPE)
    current_stat.st_size = 0;   /* links 0 size on tape */
      else
    current_stat.st_size = from_oct (1 + 12, header->header.size);

      header->header.name[NAME_FIELD_SIZE - 1] = '\0';
      if (header->header.typeflag == GNUTYPE_LONGNAME
      || header->header.typeflag == GNUTYPE_LONGLINK)
    {
      longp = ((header->header.typeflag == GNUTYPE_LONGNAME)
           ? &next_long_name
           : &next_long_link);

      set_next_block_after (header);
      if (*longp)
        free (*longp);
      bp = *longp = (char *) xmalloc ((size_t) current_stat.st_size);

      for (size = current_stat.st_size; size > 0; size -= written)
        {
          data_block = find_next_block ();
          if (data_block == NULL)
        {
          ERROR ((0, 0, _("Unexpected EOF on archive file")));
          break;
        }
          written = available_space_after (data_block);
          if (written > size)
        written = size;

          memcpy (bp, data_block->buffer, (size_t) written);
          bp += written;
          set_next_block_after ((union block *)
                    (data_block->buffer + written - 1));
        }

      /* Loop!  */

    }
      else
    {
      assign_string (&current_file_name,
             (next_long_name ? next_long_name
              : current_header->header.name));
      assign_string (&current_link_name,
             (next_long_link ? next_long_link
              : current_header->header.linkname));
      next_long_link = next_long_name = 0;
      return HEADER_SUCCESS;
    }
    }
}

/*-------------------------------------------------------------------------.
| Decode things from a file HEADER block into STAT_INFO, also setting      |
| *FORMAT_POINTER depending on the header block format.  If DO_USER_GROUP, |
| decode the user/group information (this is useful for extraction, but    |
| waste time when merely listing).                     |
|                                      |
| read_header() has already decoded the checksum and length, so we don't.  |
|                                      |
| This routine should *not* be called twice for the same block, since the  |
| two calls might use different DO_USER_GROUP values and thus might end up |
| with different uid/gid for the two calls.  If anybody wants the uid/gid  |
| they should decode it first, and other callers should decode it without  |
| uid/gid before calling a routine, e.g. print_header, that assumes    |
| decoded data.                                |
`-------------------------------------------------------------------------*/

void
decode_header (union block *header, struct stat *stat_info,
           enum archive_format *format_pointer, int do_user_group)
{
  enum archive_format format;

  if (strcmp (header->header.magic, TMAGIC) == 0)
    format = POSIX_FORMAT;
  else if (strcmp (header->header.magic, OLDGNU_MAGIC) == 0)
    format = OLDGNU_FORMAT;
  else
    format = V7_FORMAT;
  *format_pointer = format;

  stat_info->st_mode = from_oct (8, header->header.mode);
  stat_info->st_mode &= 07777;
  stat_info->st_mtime = from_oct (1 + 12, header->header.mtime);

  if (format == OLDGNU_FORMAT && incremental_option)
    {
      stat_info->st_atime = from_oct (1 + 12, header->oldgnu_header.atime);
      stat_info->st_ctime = from_oct (1 + 12, header->oldgnu_header.ctime);
    }

  if (format == V7_FORMAT)
    {
      stat_info->st_uid = from_oct (8, header->header.uid);
      stat_info->st_gid = from_oct (8, header->header.gid);
      stat_info->st_rdev = 0;
    }
  else
    {
      if (do_user_group)
    {
      /* FIXME: Decide if this should somewhat depend on -p.  */

      if (numeric_owner_option
          || !*header->header.uname
          || !uname_to_uid (header->header.uname, &stat_info->st_uid))
        stat_info->st_uid = from_oct (8, header->header.uid);

      if (numeric_owner_option
          || !*header->header.gname
          || !gname_to_gid (header->header.gname, &stat_info->st_gid))
        stat_info->st_gid = from_oct (8, header->header.gid);
    }
      switch (header->header.typeflag)
    {
#ifdef S_IFBLK
    case BLKTYPE:
      stat_info->st_rdev = makedev (from_oct (8, header->header.devmajor),
                    from_oct (8, header->header.devminor));
      break;
#endif

#ifdef S_IFCHR
    case CHRTYPE:
      stat_info->st_rdev = makedev (from_oct (8, header->header.devmajor),
                    from_oct (8, header->header.devminor));
      break;
#endif

    default:
      stat_info->st_rdev = 0;
    }
    }
}

/*------------------------------------------------------------------------.
| Quick and dirty octal conversion.  Result is -1 if the field is invalid |
| (all blank, or nonoctal).                       |
`------------------------------------------------------------------------*/

long
from_oct (int digs, char *where)
{
  long value;

  while (ISSPACE (*where))
    {               /* skip spaces */
      where++;
      if (--digs <= 0)
    return -1;      /* all blank field */
    }
  value = 0;
  while (digs > 0 && ISODIGIT (*where))
    {
      /* Scan til nonoctal.  */

      value = (value << 3) | (*where++ - '0');
      --digs;
    }

  if (digs > 0 && *where && !ISSPACE (*where))
    return -1;          /* ended on non-space/nul */

  return value;
}

#if !USE_OLD_CTIME

/*-------------------------------------------.
| Return the time formatted along ISO 8601.  |
`-------------------------------------------*/

/* Also, see http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html.  */

static char *
isotime (const time_t *time)
{
  static char buffer[21];
  struct tm *tm;

  tm = localtime (time);
  sprintf (buffer, "%4d-%02d-%02d %02d:%02d:%02d\n",
       tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
       tm->tm_hour, tm->tm_min, tm->tm_sec);
  return buffer;
}

#endif /* not USE_OLD_CTIME */

/*-------------------------------------------------------------------------.
| Decode MODE from its binary form in a stat structure, and encode it into |
| a 9 characters string STRING, terminated with a NUL.                     |
`-------------------------------------------------------------------------*/

static void
decode_mode (unsigned mode, char *string)
{
  unsigned mask;
  const char *rwx = "rwxrwxrwx";

  for (mask = 0400; mask != 0; mask >>= 1)
    if (mode & mask)
      *string++ = *rwx++;
    else
      {
    *string++ = '-';
    rwx++;
      }

  if (mode & S_ISUID)
    string[-7] = string[-7] == 'x' ? 's' : 'S';
  if (mode & S_ISGID)
    string[-4] = string[-4] == 'x' ? 's' : 'S';
  if (mode & S_ISVTX)
    string[-1] = string[-1] == 'x' ? 't' : 'T';

  *string = '\0';
}

/*-------------------------------------------------------------------------.
| Actually print it.                               |
|                                      |
| Plain and fancy file header block logging.  Non-verbose just prints the  |
| name, e.g. for "tar t" or "tar x".  This should just contain file names, |
| so it can be fed back into tar with xargs or the "-T" option.  The       |
| verbose option can give a bunch of info, one line per file.  I doubt     |
| anybody tries to parse its format, or if they do, they shouldn't.  Unix  |
| tar is pretty random here anyway.                    |
`-------------------------------------------------------------------------*/

/* FIXME: Note that print_header uses the globals HEAD, HSTAT, and
   HEAD_STANDARD, which must be set up in advance.  Not very clean...  */

/* UGSWIDTH starts with 18, so with user and group names <= 8 chars, the
   columns never shift during the listing.  */
#define UGSWIDTH 18
static int ugswidth = UGSWIDTH; /* maximum width encountered so far */

/* DATEWIDTH is the number of columns taken by the date and time fields.  */
#if USE_OLD_CDATE
# define DATEWIDTH 19
#else
# define DATEWIDTH 18
#endif

void
print_header (void)
{
  char modes[11];
  char *timestamp;
  char uform[11], gform[11];    /* these hold formatted ints */
  char *user, *group;
  char size[24];        /* holds a formatted long or maj, min */
  time_t longie;        /* to make ctime() call portable */
  int pad;
  char *name;

  if (block_number_option)
    fprintf (stdlis, _("block %10ld: "), current_block_ordinal ());

  if (verbose_option <= 1)
    {
      /* Just the fax, mam.  */

      char *quoted_name = quote_copy_string (current_file_name);

      if (quoted_name)
    {
      fprintf (stdlis, "%s\n", quoted_name);
      free (quoted_name);
    }
      else
    fprintf (stdlis, "%s\n", current_file_name);
    }
  else
    {
      /* File type and modes.  */

      modes[0] = '?';
      switch (current_header->header.typeflag)
    {
    case GNUTYPE_VOLHDR:
      modes[0] = 'V';
      break;

    case GNUTYPE_MULTIVOL:
      modes[0] = 'M';
      break;

    case GNUTYPE_NAMES:
      modes[0] = 'N';
      break;

    case GNUTYPE_LONGNAME:
    case GNUTYPE_LONGLINK:
      ERROR ((0, 0, _("Visible longname error")));
      break;

    case GNUTYPE_SPARSE:
    case REGTYPE:
    case AREGTYPE:
    case LNKTYPE:
      modes[0] = '-';
      if (current_file_name[strlen (current_file_name) - 1] == '/')
        modes[0] = 'd';
      break;
    case GNUTYPE_DUMPDIR:
      modes[0] = 'd';
      break;
    case DIRTYPE:
      modes[0] = 'd';
      break;
    case SYMTYPE:
      modes[0] = 'l';
      break;
    case BLKTYPE:
      modes[0] = 'b';
      break;
    case CHRTYPE:
      modes[0] = 'c';
      break;
    case FIFOTYPE:
      modes[0] = 'p';
      break;
    case CONTTYPE:
      modes[0] = 'C';
      break;
    }

      decode_mode ((unsigned) current_stat.st_mode, modes + 1);

      /* Timestamp.  */

      longie = current_stat.st_mtime;
#if USE_OLD_CTIME
      timestamp = ctime (&longie);
      timestamp[16] = '\0';
      timestamp[24] = '\0';
#else
      timestamp = isotime (&longie);
      timestamp[16] = '\0';
#endif

      /* User and group names.  */

      if (*current_header->header.uname && current_format != V7_FORMAT)
    user = current_header->header.uname;
      else
    {
      user = uform;
      sprintf (uform, "%ld", from_oct (8, current_header->header.uid));
    }

      if (*current_header->header.gname && current_format != V7_FORMAT)
    group = current_header->header.gname;
      else
    {
      group = gform;
      sprintf (gform, "%ld", from_oct (8, current_header->header.gid));
    }

      /* Format the file size or major/minor device numbers.  */

      switch (current_header->header.typeflag)
    {
#if defined(S_IFBLK) || defined(S_IFCHR)
    case CHRTYPE:
    case BLKTYPE:
      sprintf (size, "%d,%d",
           major (current_stat.st_rdev), minor (current_stat.st_rdev));
      break;
#endif
    case GNUTYPE_SPARSE:
      sprintf (size, "%ld",
           from_oct (1 + 12, current_header->oldgnu_header.realsize));
      break;
    default:
      sprintf (size, "%ld", (long) current_stat.st_size);
    }

      /* Figure out padding and print the whole line.  */

      pad = strlen (user) + strlen (group) + strlen (size) + 1;
      if (pad > ugswidth)
    ugswidth = pad;

#if USE_OLD_CTIME
      fprintf (stdlis, "%s %s/%s %*s%s %s %s",
           modes, user, group, ugswidth - pad, "",
           size, timestamp + 4, timestamp + 20);
#else
      fprintf (stdlis, "%s %s/%s %*s%s %s",
           modes, user, group, ugswidth - pad, "", size, timestamp);
#endif

      name = quote_copy_string (current_file_name);
      if (name)
    {
      fprintf (stdlis, " %s", name);
      free (name);
    }
      else
    fprintf (stdlis, " %s", current_file_name);

      switch (current_header->header.typeflag)
    {
    case SYMTYPE:
      name = quote_copy_string (current_link_name);
      if (name)
        {
          fprintf (stdlis, " -> %s\n", name);
          free (name);
        }
      else
        fprintf (stdlis, " -> %s\n", current_link_name);
      break;

    case LNKTYPE:
      name = quote_copy_string (current_link_name);
      if (name)
        {
          fprintf (stdlis, _(" link to %s\n"), name);
          free (name);
        }
      else
        fprintf (stdlis, _(" link to %s\n"), current_link_name);
      break;

    default:
      fprintf (stdlis, _(" unknown file type `%c'\n"),
           current_header->header.typeflag);
      break;

    case AREGTYPE:
    case REGTYPE:
    case GNUTYPE_SPARSE:
    case CHRTYPE:
    case BLKTYPE:
    case DIRTYPE:
    case FIFOTYPE:
    case CONTTYPE:
    case GNUTYPE_DUMPDIR:
      putc ('\n', stdlis);
      break;

    case GNUTYPE_VOLHDR:
      fprintf (stdlis, _("--Volume Header--\n"));
      break;

    case GNUTYPE_MULTIVOL:
      fprintf (stdlis, _("--Continued at byte %ld--\n"),
           from_oct (1 + 12, current_header->oldgnu_header.offset));
      break;

    case GNUTYPE_NAMES:
      fprintf (stdlis, _("--Mangled file names--\n"));
      break;
    }
    }
  fflush (stdlis);
}

/*--------------------------------------------------------------.
| Print a similar line when we make a directory automatically.  |
`--------------------------------------------------------------*/

void
print_for_mkdir (char *pathname, int length, int mode)
{
  char modes[11];
  char *name;

  if (verbose_option > 1)
    {
      /* File type and modes.  */

      modes[0] = 'd';
      decode_mode ((unsigned) mode, modes + 1);

      if (block_number_option)
    fprintf (stdlis, _("block %10ld: "), current_block_ordinal ());
      name = quote_copy_string (pathname);
      if (name)
    {
      fprintf (stdlis, "%s %*s %.*s\n", modes, ugswidth + DATEWIDTH,
           _("Creating directory:"), length, name);
      free (name);
    }
      else
    fprintf (stdlis, "%s %*s %.*s\n", modes, ugswidth + DATEWIDTH,
         _("Creating directory:"), length, pathname);
    }
}

/*--------------------------------------------------------.
| Skip over SIZE bytes of data in blocks in the archive.  |
`--------------------------------------------------------*/

void
skip_file (long size)
{
  union block *x;

  if (multi_volume_option)
    {
      save_totsize = size;
      save_sizeleft = size;
    }

  while (size > 0)
    {
      x = find_next_block ();
      if (x == NULL)
    FATAL_ERROR ((0, 0, _("Unexpected EOF on archive file")));

      set_next_block_after (x);
      size -= BLOCKSIZE;
      if (multi_volume_option)
    save_sizeleft -= BLOCKSIZE;
    }
}

/*---.
| ?  |
`---*/

void
skip_extended_headers (void)
{
  union block *exhdr;

  while (1)
    {
      exhdr = find_next_block ();
      if (!exhdr->sparse_header.isextended)
    {
      set_next_block_after (exhdr);
      break;
    }
      set_next_block_after (exhdr);
    }
}

https://git.savannah.gnu.org/cgit/tar.git/tree//src/mangle.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/mangle.c used in: Step 656
/* Encode long filenames for GNU tar.
   Copyright (C) 1988, 1992, 1994, 1996, 1997 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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include "system.h"

#include <time.h>
time_t time ();

#include "common.h"

struct mangled
  {
    struct mangled *next;
    int type;
    char mangled[NAME_FIELD_SIZE];
    char *linked_to;
    char normal[1];
  };

/* Should use a hash table, etc. .  */
struct mangled *first_mangle;
int mangled_num = 0;

/*---------------------------------------------------------------------.
| Extract a GNUTYPE_NAMES record contents.  It seems that such are not |
| produced anymore by GNU tar, but we leave the reading code around    |
| nevertheless, for salvaging old tapes.                   |
`---------------------------------------------------------------------*/

void
extract_mangle (void)
{
  int size = current_stat.st_size;
  char *buffer = xmalloc ((size_t) (size + 1));
  char *copy = buffer;
  char *cursor = buffer;

  buffer[size] = '\0';

  while (size > 0)
    {
      union block *block = find_next_block ();
      int available;

      if (!block)
    {
      ERROR ((0, 0, _("Unexpected EOF in mangled names")));
      return;
    }
      available = available_space_after (block);
      if (available > size)
    available = size;
      memcpy (copy, block->buffer, (size_t) available);
      copy += available;
      size -= available;
      set_next_block_after ((union block *) (block->buffer + available - 1));
    }

  while (*cursor)
    {
      char *next_cursor;
      char *name;
      char *name_end;

      next_cursor = strchr (cursor, '\n');
      *next_cursor++ = '\0';

      if (!strncmp (cursor, "Rename ", 7))
    {

      name = cursor + 7;
      name_end = strchr (name, ' ');
      while (strncmp (name_end, " to ", 4))
        {
          name_end++;
          name_end = strchr (name_end, ' ');
        }
      *name_end = '\0';
      if (next_cursor[-2] == '/')
        next_cursor[-2] = '\0';
      unquote_string (name_end + 4);
      if (rename (name, name_end + 4))
        ERROR ((0, errno, _("Cannot rename %s to %s"), name, name_end + 4));
      else if (verbose_option)
        WARN ((0, 0, _("Renamed %s to %s"), name, name_end + 4));
    }
#ifdef S_ISLNK
      else if (!strncmp (cursor, "Symlink ", 8))
    {
      name = cursor + 8;
      name_end = strchr (name, ' ');
      while (strncmp (name_end, " to ", 4))
        {
          name_end++;
          name_end = strchr (name_end, ' ');
        }
      *name_end = '\0';
      unquote_string (name);
      unquote_string (name_end + 4);
      if (symlink (name, name_end + 4)
          && (unlink (name_end + 4) || symlink (name, name_end + 4)))
        ERROR ((0, errno, _("Cannot symlink %s to %s"),
            name, name_end + 4));
      else if (verbose_option)
        WARN ((0, 0, _("Symlinked %s to %s"), name, name_end + 4));
    }
#endif
      else
    ERROR ((0, 0, _("Unknown demangling command %s"), cursor));

      cursor = next_cursor;
    }
}

https://git.savannah.gnu.org/cgit/tar.git/tree//src/misc.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/misc.c used in: Step 657
/* Miscellaneous functions, not really specific to GNU tar.
   Copyright (C) 1988, 92, 94, 95, 96, 97 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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include "system.h"

#include "backupfile.h"
#include "rmt.h"

/* The following inclusion for crosschecking prototypes, only.  */
#include "common.h"


/* Handling strings.  */

#define ISPRINT(Char) (ISASCII (Char) && isprint (Char))

/*-------------------------------------------------------------------------.
| Assign STRING to a copy of VALUE if not NULL, or to NULL.  If STRING was |
| not NULL, it is freed first.                         |
`-------------------------------------------------------------------------*/

void
assign_string (char **string, const char *value)
{
  if (*string)
    free (*string);
  *string = value ? xstrdup (value) : NULL;
}

/*------------------------------------------------------------------------.
| Allocate a copy of the string quoted as in C, and returns that.  If the |
| string does not have to be quoted, it returns the NULL string.  The     |
| allocated copy should normally be freed with free() after the caller is |
| done with it.                               |
|                                     |
| This is used in two contexts only: either listing a tar file for the    |
| --list (-t) option, or generating the directory file in incremental     |
| dumps.                                  |
`------------------------------------------------------------------------*/

char *
quote_copy_string (const char *string)
{
  const char *source = string;
  char *destination = NULL;
  char *buffer = NULL;
  int copying = 0;

  while (*source)
    {
      int character = (unsigned char) *source++;

      if (character == '\\')
    {
      if (!copying)
        {
          int length = (source - string) - 1;

          copying = 1;
          buffer = (char *) xmalloc (length + 5 + strlen (source) * 4);
          memcpy (buffer, string, (size_t) length);
          destination = buffer + length;
        }
      *destination++ = '\\';
      *destination++ = '\\';
    }
      else if (ISPRINT (character))
    {
      if (copying)
        *destination++ = character;
    }
      else
    {
      if (!copying)
        {
          int length = (source - string) - 1;

          copying = 1;
          buffer = (char *) xmalloc (length + 5 + strlen (source) * 4);
          memcpy (buffer, string, (size_t) length);
          destination = buffer + length;
        }
      *destination++ = '\\';
      switch (character)
        {
        case '\n':
          *destination++ = 'n';
          break;

        case '\t':
          *destination++ = 't';
          break;

        case '\f':
          *destination++ = 'f';
          break;

        case '\b':
          *destination++ = 'b';
          break;

        case '\r':
          *destination++ = 'r';
          break;

        case '\177':
          *destination++ = '?';
          break;

        default:
          *destination++ = (character >> 6) + '0';
          *destination++ = ((character >> 3) & 07) + '0';
          *destination++ = (character & 07) + '0';
          break;
        }
    }
    }
  if (copying)
    {
      *destination = '\0';
      return buffer;
    }
  return NULL;
}

/*-------------------------------------------------------------------------.
| Takes a quoted C string (like those produced by quote_copy_string) and   |
| turns it back into the un-quoted original.  This is done in place.       |
| Returns 0 only if the string was not properly quoted, but completes the  |
| unquoting anyway.                            |
|                                      |
| This is used for reading the saved directory file in incremental dumps.  |
| It is used for decoding old `N' records (demangling names).  But also,   |
| it is used for decoding file arguments, would they come from the shell   |
| or a -T file, and for decoding the --exclude argument.           |
`-------------------------------------------------------------------------*/

int
unquote_string (char *string)
{
  int result = 1;
  char *source = string;
  char *destination = string;

  while (*source)
    if (*source == '\\')
      switch (*++source)
    {
    case '\\':
      *destination++ = '\\';
      source++;
      break;

    case 'n':
      *destination++ = '\n';
      source++;
      break;

    case 't':
      *destination++ = '\t';
      source++;
      break;

    case 'f':
      *destination++ = '\f';
      source++;
      break;

    case 'b':
      *destination++ = '\b';
      source++;
      break;

    case 'r':
      *destination++ = '\r';
      source++;
      break;

    case '?':
      *destination++ = 0177;
      source++;
      break;

    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
      {
        int value = *source++ - '0';

        if (*source < '0' || *source > '7')
          {
        *destination++ = value;
        break;
          }
        value = value * 8 + *source++ - '0';
        if (*source < '0' || *source > '7')
          {
        *destination++ = value;
        break;
          }
        value = value * 8 + *source++ - '0';
        *destination++ = value;
        break;
      }

    default:
      result = 0;
      *destination++ = '\\';
      if (*source)
        *destination++ = *source++;
      break;
    }
    else if (source != destination)
      *destination++ = *source++;
    else
      source++, destination++;

  if (source != destination)
    *destination = '\0';
  return result;
}


/* Sorting lists.  */

/*---.
| ?  |
`---*/

char *
merge_sort (char *list, int length, int offset, int (*compare) (char *, char *))
{
  char *first_list;
  char *second_list;
  int first_length;
  int second_length;
  char *result;
  char **merge_point;
  char *cursor;
  int counter;

#define SUCCESSOR(Pointer) \
  (*((char **) (((char *) (Pointer)) + offset)))

  if (length == 1)
    return list;

  if (length == 2)
    {
      if ((*compare) (list, SUCCESSOR (list)) > 0)
    {
      result = SUCCESSOR (list);
      SUCCESSOR (result) = list;
      SUCCESSOR (list) = NULL;
      return result;
    }
      return list;
    }

  first_list = list;
  first_length = (length + 1) / 2;
  second_length = length / 2;
  for (cursor = list, counter = first_length - 1;
       counter;
       cursor = SUCCESSOR (cursor), counter--)
    continue;
  second_list = SUCCESSOR (cursor);
  SUCCESSOR (cursor) = NULL;

  first_list = merge_sort (first_list, first_length, offset, compare);
  second_list = merge_sort (second_list, second_length, offset, compare);

  merge_point = &result;
  while (first_list && second_list)
    if ((*compare) (first_list, second_list) < 0)
      {
    cursor = SUCCESSOR (first_list);
    *merge_point = first_list;
    merge_point = &SUCCESSOR (first_list);
    first_list = cursor;
      }
    else
      {
    cursor = SUCCESSOR (second_list);
    *merge_point = second_list;
    merge_point = &SUCCESSOR (second_list);
    second_list = cursor;
      }
  if (first_list)
    *merge_point = first_list;
  else
    *merge_point = second_list;

  return result;

#undef SUCCESSOR
}


/* File handling.  */

/* Saved names in case backup needs to be undone.  */
static char *before_backup_name = NULL;
static char *after_backup_name = NULL;

/*------------------------------------------------------------------------.
| Returns nonzero if p is `.' or `..'.  This could be a macro for speed.  |
`------------------------------------------------------------------------*/

/* Early Solaris 2.4 readdir may return d->d_name as `' in NFS-mounted
   directories.  The workaround here skips `' just like `.'.  Without it,
   GNU tar would then treat `' much like `.' and loop endlessly.  */

int
is_dot_or_dotdot (const char *p)
{
  return (p[0] == '\0'
      || (p[0] == '.' && (p[1] == '\0'
                  || (p[1] == '.' && p[2] == '\0'))));
}

/*-------------------------------------------------------------------------.
| Delete PATH, whatever it might be.  If RECURSE, first recursively delete |
| the contents of PATH when it is a directory.  Return zero on any error,  |
| with errno set.  As a special case, if we fail to delete a directory     |
| when not RECURSE, do not set errno (just be tolerant to this error).     |
`-------------------------------------------------------------------------*/

int
remove_any_file (const char *path, int recurse)
{
  struct stat stat_buffer;

  if (lstat (path, &stat_buffer) < 0)
    return 0;

  if (S_ISDIR (stat_buffer.st_mode))
    if (recurse)
      {
    DIR *dirp = opendir (path);
    struct dirent *dp;

    if (dirp == NULL)
      return 0;

    while (dp = readdir (dirp), dp && !is_dot_or_dotdot (dp->d_name))
      {
        char *path_buffer = new_name (path, dp->d_name);

        if (!remove_any_file (path_buffer, 1))
          {
        int saved_errno = errno;

        free (path_buffer);
        closedir (dirp);
        errno = saved_errno; /* FIXME: errno should be read-only */
        return 0;
          }
        free (path_buffer);
      }
    closedir (dirp);
    return rmdir (path) >= 0;
      }
    else
      {
    /* FIXME: Saving errno might not be needed anymore, now that
       extract_archive tests for the special case before recovery.  */
    int saved_errno = errno;

    if (rmdir (path) >= 0)
      return 1;
    errno = saved_errno;    /* FIXME: errno should be read-only */
    return 0;
      }

  return unlink (path) >= 0;
}

/*-------------------------------------------------------------------------.
| Check if PATH already exists and make a backup of it right now.  Return  |
| success (nonzero) only if the backup in either unneeded, or successful.  |
|                                      |
| For now, directories are considered to never need backup.  If ARCHIVE is |
| nonzero, this is the archive and so, we do not have to backup block or   |
| character devices, nor remote entities.                  |
`-------------------------------------------------------------------------*/

int
maybe_backup_file (const char *path, int archive)
{
  struct stat file_stat;

  /* Check if we really need to backup the file.  */

  if (archive && _remdev (path))
    return 1;

  if (stat (path, &file_stat))
    {
      if (errno == ENOENT)
    return 1;

      ERROR ((0, errno, "%s", path));
      return 0;
    }

  if (S_ISDIR (file_stat.st_mode))
    return 1;

#ifdef S_ISBLK
  if (archive && S_ISBLK (file_stat.st_mode))
    return 1;
#endif

#ifdef S_ISCHR
  if (archive && S_ISCHR (file_stat.st_mode))
    return 1;
#endif

  assign_string (&before_backup_name, path);

  /* A run situation may exist between Emacs or other GNU programs trying to
     make a backup for the same file simultaneously.  If theoretically
     possible, real problems are unlikely.  Doing any better would require a
     convention, GNU-wide, for all programs doing backups.  */

  assign_string (&after_backup_name, NULL);
  after_backup_name = find_backup_file_name (path);
  if (after_backup_name == NULL)
    FATAL_ERROR ((0, 0, "Virtual memory exhausted"));

  if (rename (before_backup_name, after_backup_name) == 0)
    {
      if (verbose_option)
    fprintf (stdlis, _("Renaming previous `%s' to `%s'\n"),
         before_backup_name, after_backup_name);
      return 1;
    }

  /* The backup operation failed.  */

  ERROR ((0, errno, _("%s: Cannot rename for backup"), before_backup_name));
  assign_string (&after_backup_name, NULL);
  return 0;
}

/*-----------------------------------------------------------------------.
| Try to restore the recently backed up file to its original name.  This |
| is usually only needed after a failed extraction.          |
`-----------------------------------------------------------------------*/

void
undo_last_backup (void)
{
  if (after_backup_name)
    {
      if (rename (after_backup_name, before_backup_name) != 0)
    ERROR ((0, errno, _("%s: Cannot rename from backup"),
        before_backup_name));
      if (verbose_option)
    fprintf (stdlis, _("Renaming `%s' back to `%s'\n"),
         after_backup_name, before_backup_name);
      assign_string (&after_backup_name, NULL);
    }
}

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/backupfile.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/backupfile.h used in: Step 657, Step 661
/* backupfile.h -- declarations for making Emacs style backup file names
   Copyright (C) 1990 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.  */

/* When to make backup files.  */
enum backup_type
{
  /* Never make backups.  */
  none,

  /* Make simple backups of every file.  */
  simple,

  /* Make numbered backups of files that already have numbered backups,
     and simple backups of the others.  */
  numbered_existing,

  /* Make numbered backups of every file.  */
  numbered
};

extern enum backup_type backup_type;
extern char *simple_backup_suffix;

#ifdef __STDC__
char *find_backup_file_name (const char *file);
#else
char *find_backup_file_name ();
#endif

https://git.savannah.gnu.org/cgit/tar.git/tree//src/names.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/names.c used in: Step 658
/* Various processing of names.
   Copyright (C) 1988, 1992, 1994, 1996, 1997 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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include "system.h"

#include <pwd.h>
#include <grp.h>

#ifndef FNM_LEADING_DIR
# include <fnmatch.h>
#endif

#include "common.h"


/* User and group names.  */

extern struct group *getgrnam ();
extern struct passwd *getpwnam ();
#if !HAVE_GETPWUID
extern struct passwd *getpwuid ();
#endif
#if !HAVE_GETGRGID
extern struct group *getgrgid ();
#endif

/* Make sure you link with the proper libraries if you are running the
   Yellow Peril (thanks for the good laugh, Ian J.!), or, euh... NIS.
   This code should also be modified for non-UNIX systems to do something
   reasonable.  */

static char cached_uname[UNAME_FIELD_SIZE] = "";
static char cached_gname[GNAME_FIELD_SIZE] = "";

static uid_t cached_uid;    /* valid only if cached_uname is not empty */
static gid_t cached_gid;    /* valid only if cached_gname is not empty */

/*------------------------------------------.
| Given UID, find the corresponding UNAME.  |
`------------------------------------------*/

void
uid_to_uname (uid_t uid, char uname[UNAME_FIELD_SIZE])
{
  struct passwd *passwd;

  if (!cached_uname[0] || uid != cached_uid)
    {
      passwd = getpwuid (uid);
      if (passwd)
    {
      cached_uid = uid;
      strncpy (cached_uname, passwd->pw_name, UNAME_FIELD_SIZE);
    }
      else
    *uname = '\0';
    }
  strncpy (uname, cached_uname, UNAME_FIELD_SIZE);
}

/*------------------------------------------.
| Given GID, find the corresponding GNAME.  |
`------------------------------------------*/

void
gid_to_gname (gid_t gid, char gname[GNAME_FIELD_SIZE])
{
  struct group *group;

  if (!cached_gname[0] || gid != cached_gid)
    {
      setgrent ();      /* FIXME: why?! */
      group = getgrgid (gid);
      if (group)
    {
      cached_gid = gid;
      strncpy (cached_gname, group->gr_name, GNAME_FIELD_SIZE);
    }
      else
    *gname = '\0';
    }
  strncpy (gname, cached_gname, GNAME_FIELD_SIZE);
}

/*-------------------------------------------------------------------------.
| Given UNAME, set the corresponding UID and return 1, or else, return 0.  |
`-------------------------------------------------------------------------*/

int
uname_to_uid (char uname[UNAME_FIELD_SIZE], uid_t *uidp)
{
  struct passwd *passwd;

  if (!cached_uname[0]
      || uname[0] != cached_uname[0]
      || strncmp (uname, cached_uname, UNAME_FIELD_SIZE) != 0)
    {
      passwd = getpwnam (uname);
      if (passwd)
    {
      cached_uid = passwd->pw_uid;
      strncpy (cached_uname, uname, UNAME_FIELD_SIZE);
    }
      else
    return 0;
    }
  *uidp = cached_uid;
  return 1;
}

/*-------------------------------------------------------------------------.
| Given GNAME, set the corresponding GID and return 1, or else, return 0.  |
`-------------------------------------------------------------------------*/

int
gname_to_gid (char gname[GNAME_FIELD_SIZE], gid_t *gidp)
{
  struct group *group;

  if (!cached_gname[0]
      || gname[0] != cached_gname[0]
      || strncmp (gname, cached_gname, GNAME_FIELD_SIZE) != 0)
    {
      group = getgrnam (gname);
      if (group)
    {
      cached_gid = group->gr_gid;
      strncpy (cached_gname, gname, GNAME_FIELD_SIZE);
    }
      else
    return 0;
    }
  *gidp = cached_gid;
  return 1;
}


/* Names from the command call.  */

static const char **name_array; /* store an array of names */
static int allocated_names; /* how big is the array? */
static int names;       /* how many entries does it have? */
static int name_index = 0;  /* how many of the entries have we scanned? */

/*------------------------.
| Initialize structures.  |
`------------------------*/

void
init_names (void)
{
  allocated_names = 10;
  name_array = (const char **)
    xmalloc (sizeof (const char *) * allocated_names);
  names = 0;
}

/*--------------------------------------------------------------.
| Add NAME at end of name_array, reallocating it as necessary.  |
`--------------------------------------------------------------*/

void
name_add (const char *name)
{
  if (names == allocated_names)
    {
      allocated_names *= 2;
      name_array = (const char **)
    xrealloc (name_array, sizeof (const char *) * allocated_names);
    }
  name_array[names++] = name;
}


/* Names from external name file.  */

static FILE *name_file;     /* file to read names from */
static char *name_buffer;   /* buffer to hold the current file name */
static size_t name_buffer_length; /* allocated length of name_buffer */

/*---.
| ?  |
`---*/

/* FIXME: I should better check more closely.  It seems at first glance that
   is_pattern is only used when reading a file, and ignored for all
   command line arguments.  */

static inline int
is_pattern (const char *string)
{
  return strchr (string, '*') || strchr (string, '[') || strchr (string, '?');
}

/*-----------------------------------------------------------------------.
| Set up to gather file names for tar.  They can either come from a file |
| or were saved from decoding arguments.                 |
`-----------------------------------------------------------------------*/

void
name_init (int argc, char *const *argv)
{
  name_buffer = xmalloc (NAME_FIELD_SIZE + 2);
  name_buffer_length = NAME_FIELD_SIZE;

  if (files_from_option)
    {
      if (!strcmp (files_from_option, "-"))
    {
      request_stdin ("-T");
      name_file = stdin;
    }
      else if (name_file = fopen (files_from_option, "r"), !name_file)
    FATAL_ERROR ((0, errno, _("Cannot open file %s"), name_file));
    }
}

/*---.
| ?  |
`---*/

void
name_term (void)
{
  free (name_buffer);
  free (name_array);
}

/*---------------------------------------------------------------------.
| Read the next filename from name_file and null-terminate it.  Put it |
| into name_buffer, reallocating and adjusting name_buffer_length if   |
| necessary.  Return 0 at end of file, 1 otherwise.            |
`---------------------------------------------------------------------*/

static int
read_name_from_file (void)
{
  int character;
  int counter = 0;

  /* FIXME: getc may be called even if character was EOF the last time here.  */

  /* FIXME: This + 2 allocation might serve no purpose.  */

  while (character = getc (name_file),
     character != EOF && character != filename_terminator)
    {
      if (counter == name_buffer_length)
    {
      name_buffer_length += NAME_FIELD_SIZE;
      name_buffer = xrealloc (name_buffer, name_buffer_length + 2);
    }
      name_buffer[counter++] = character;
    }

  if (counter == 0 && character == EOF)
    return 0;

  if (counter == name_buffer_length)
    {
      name_buffer_length += NAME_FIELD_SIZE;
      name_buffer = xrealloc (name_buffer, name_buffer_length + 2);
    }
  name_buffer[counter] = '\0';

  return 1;
}

/*------------------------------------------------------------------------.
| Get the next name from ARGV or the file of names.  Result is in static  |
| storage and can't be relied upon across two calls.              |
|                                     |
| If CHANGE_DIRS is true, treat a filename of the form "-C" as meaning    |
| that the next filename is the name of a directory to change to.  If     |
| `filename_terminator' is NUL, CHANGE_DIRS is effectively always false.  |
`------------------------------------------------------------------------*/

char *
name_next (int change_dirs)
{
  const char *source;
  char *cursor;
  int chdir_flag = 0;

  if (filename_terminator == '\0')
    change_dirs = 0;

  while (1)
    {
      /* Get a name, either from file or from saved arguments.  */

      if (name_file)
    {
      if (!read_name_from_file ())
        break;
    }
      else
    {
      if (name_index == names)
        break;

      source = name_array[name_index++];
      if (strlen (source) > name_buffer_length)
        {
          free (name_buffer);
          name_buffer_length = strlen (source);
          name_buffer = xmalloc (name_buffer_length + 2);
        }
      strcpy (name_buffer, source);
    }

      /* Zap trailing slashes.  */

      cursor = name_buffer + strlen (name_buffer) - 1;
      while (cursor > name_buffer && *cursor == '/')
    *cursor-- = '\0';

      if (chdir_flag)
    {
      if (chdir (name_buffer) < 0)
        FATAL_ERROR ((0, errno, _("Cannot change to directory %s"),
              name_buffer));
      chdir_flag = 0;
    }
      else if (change_dirs && strcmp (name_buffer, "-C") == 0)
    chdir_flag = 1;
      else
#if 0
    if (!exclude_option || !check_exclude (name_buffer))
#endif
      {
        unquote_string (name_buffer);
        return name_buffer;
      }
    }

  /* No more names in file.  */

  if (name_file && chdir_flag)
    FATAL_ERROR ((0, 0, _("Missing file name after -C")));

  return NULL;
}

/*------------------------------.
| Close the name file, if any.  |
`------------------------------*/

void
name_close (void)
{
  if (name_file != NULL && name_file != stdin)
    if (fclose (name_file) == EOF)
      ERROR ((0, errno, "%s", name_buffer));
}

/*-------------------------------------------------------------------------.
| Gather names in a list for scanning.  Could hash them later if we really |
| care.                                    |
|                                      |
| If the names are already sorted to match the archive, we just read them  |
| one by one.  name_gather reads the first one, and it is called by    |
| name_match as appropriate to read the next ones.  At EOF, the last name  |
| read is just left in the buffer.  This option lets users of small    |
| machines extract an arbitrary number of files by doing "tar t" and       |
| editing down the list of files.                      |
`-------------------------------------------------------------------------*/

void
name_gather (void)
{
  /* Buffer able to hold a single name.  */
  static struct name *buffer;
  static int allocated_length = 0;

  char *name;

  if (same_order_option)
    {
      if (allocated_length == 0)
    {
      allocated_length = sizeof (struct name) + NAME_FIELD_SIZE;
      buffer = (struct name *) xmalloc (allocated_length);
      /* FIXME: This memset is overkill, and ugly...  */
      memset (buffer, 0, allocated_length);
    }
      name = name_next (0);
      if (name)
    {
      if (strcmp (name, "-C") == 0)
        {
          char *copy = xstrdup (name_next (0));

          name = name_next (0);
          if (!name)
        FATAL_ERROR ((0, 0, _("Missing file name after -C")));
          buffer->change_dir = copy;
        }
      buffer->length = strlen (name);
      if (sizeof (struct name) + buffer->length >= allocated_length)
        {
          allocated_length = sizeof (struct name) + buffer->length;
          buffer = (struct name *) xrealloc (buffer, allocated_length);
        }
      strncpy (buffer->name, name, (size_t) buffer->length);
      buffer->name[buffer->length] = 0;
      buffer->next = NULL;
      buffer->found = 0;

      /* FIXME: Poorly named globals, indeed...  */
      namelist = buffer;
      namelast = namelist;
    }
      return;
    }

  /* Non sorted names -- read them all in.  */

  while (name = name_next (0), name)
    addname (name);
}

/*-----------------------------.
| Add a name to the namelist.  |
`-----------------------------*/

void
addname (const char *string)
{
  /* FIXME: This is ugly.  How is memory managed?  */
  static char *chdir_name = NULL;

  struct name *name;
  int length;

  if (strcmp (string, "-C") == 0)
    {
      chdir_name = xstrdup (name_next (0));
      string = name_next (0);
      if (!chdir_name)
    FATAL_ERROR ((0, 0, _("Missing file name after -C")));

      if (chdir_name[0] != '/')
    {
      char *path = xmalloc (PATH_MAX);

      /* FIXME: Shouldn't we use xgetcwd?  */
#if HAVE_GETCWD
      if (!getcwd (path, PATH_MAX))
        FATAL_ERROR ((0, 0, _("Could not get current directory")));
#else
      char *getwd ();

      if (!getwd (path))
        FATAL_ERROR ((0, 0, _("Could not get current directory: %s"),
              path));
#endif
      chdir_name = new_name (path, chdir_name);
      free (path);
    }
    }

  length = string ? strlen (string) : 0;
  name = (struct name *) xmalloc ((size_t) (sizeof (struct name) + length));
  memset (name, 0, sizeof (struct name) + length);
  name->next = NULL;

  if (string)
    {
      name->fake = 0;
      name->length = length;
      /* FIXME: Possibly truncating a string, here?  Tss, tss, tss!  */
      strncpy (name->name, string, (size_t) length);
      name->name[length] = '\0';
    }
  else
    name->fake = 1;

  name->found = 0;
  name->regexp = 0;     /* assume not a regular expression */
  name->firstch = 1;        /* assume first char is literal */
  name->change_dir = chdir_name;
  name->dir_contents = 0;

  if (string && is_pattern (string))
    {
      name->regexp = 1;
      if (string[0] == '*' || string[0] == '[' || string[0] == '?')
    name->firstch = 0;
    }

  if (namelast)
    namelast->next = name;
  namelast = name;
  if (!namelist)
    namelist = name;
}

/*------------------------------------------------------------------------.
| Return true if and only if name PATH (from an archive) matches any name |
| from the namelist.                              |
`------------------------------------------------------------------------*/

int
name_match (const char *path)
{
  int length = strlen (path);

  while (1)
    {
      struct name *cursor = namelist;

      if (!cursor)
    return 1;       /* empty namelist is easy */

      if (cursor->fake)
    {
      if (cursor->change_dir && chdir (cursor->change_dir))
        FATAL_ERROR ((0, errno, _("Cannot change to directory %s"),
              cursor->change_dir));
      namelist = 0;
      return 1;
    }

      for (; cursor; cursor = cursor->next)
    {
      /* If first chars don't match, quick skip.  */

      if (cursor->firstch && cursor->name[0] != path[0])
        continue;

      /* Regular expressions (shell globbing, actually).  */

      if (cursor->regexp)
        {
          if (fnmatch (cursor->name, path, FNM_LEADING_DIR) == 0)
        {
          cursor->found = 1; /* remember it matched */
          if (starting_file_option)
            {
              free (namelist);
              namelist = NULL;
            }
          if (cursor->change_dir && chdir (cursor->change_dir))
            FATAL_ERROR ((0, errno, _("Cannot change to directory %s"),
                  cursor->change_dir));

          /* We got a match.  */
          return 1;
        }
          continue;
        }

      /* Plain Old Strings.  */

      if (cursor->length <= length
                /* archive length >= specified */
          && (path[cursor->length] == '\0'
          || path[cursor->length] == '/')
                /* full match on file/dirname */
          && strncmp (path, cursor->name, (size_t) cursor->length) == 0)
                /* name compare */
        {
          cursor->found = 1;    /* remember it matched */
          if (starting_file_option)
        {
          free ((void *) namelist);
          namelist = 0;
        }
          if (cursor->change_dir && chdir (cursor->change_dir))
        FATAL_ERROR ((0, errno, _("Cannot change to directory %s"),
                  cursor->change_dir));

          /* We got a match.  */
          return 1;
        }
    }

      /* Filename from archive not found in namelist.  If we have the whole
     namelist here, just return 0.  Otherwise, read the next name in and
     compare it.  If this was the last name, namelist->found will remain
     on.  If not, we loop to compare the newly read name.  */

      if (same_order_option && namelist->found)
    {
      name_gather ();   /* read one more */
      if (namelist->found)
        return 0;
    }
      else
    return 0;
    }
}

/*------------------------------------------------------------------.
| Print the names of things in the namelist that were not matched.  |
`------------------------------------------------------------------*/

void
names_notfound (void)
{
  struct name *cursor;
  struct name *next;

  for (cursor = namelist; cursor; cursor = next)
    {
      next = cursor->next;
      if (!cursor->found && !cursor->fake)
    ERROR ((0, 0, _("%s: Not found in archive"), cursor->name));

      /* We could free the list, but the process is about to die anyway, so
     save some CPU time.  Amigas and other similarly broken software
     will need to waste the time, though.  */

#ifdef amiga
      if (!same_order_option)
    free (cursor);
#endif
    }
  namelist = (struct name *) NULL;
  namelast = (struct name *) NULL;

  if (same_order_option)
    {
      char *name;

      while (name = name_next (1), name)
    ERROR ((0, 0, _("%s: Not found in archive"), name));
    }
}

/*---.
| ?  |
`---*/

void
name_expand (void)
{
}

/*-------------------------------------------------------------------------.
| This is like name_match, except that it returns a pointer to the name it |
| matched, and doesn't set FOUND in structure.  The caller will have to do |
| that if it wants to.  Oh, and if the namelist is empty, it returns NULL, |
| unlike name_match, which returns TRUE.                                   |
`-------------------------------------------------------------------------*/

struct name *
name_scan (const char *path)
{
  int length = strlen (path);

  while (1)
    {
      struct name *cursor = namelist;

      if (!cursor)
    return NULL;        /* empty namelist is easy */

      for (; cursor; cursor = cursor->next)
    {
      /* If first chars don't match, quick skip.  */

      if (cursor->firstch && cursor->name[0] != path[0])
        continue;

      /* Regular expressions.  */

      if (cursor->regexp)
        {
          if (fnmatch (cursor->name, path, FNM_LEADING_DIR) == 0)
        return cursor;  /* we got a match */
          continue;
        }

      /* Plain Old Strings.  */

      if (cursor->length <= length
                /* archive length >= specified */
          && (path[cursor->length] == '\0'
          || path[cursor->length] == '/')
                /* full match on file/dirname */
          && strncmp (path, cursor->name, (size_t) cursor->length) == 0)
                /* name compare */
        return cursor;  /* we got a match */
    }

      /* Filename from archive not found in namelist.  If we have the whole
     namelist here, just return 0.  Otherwise, read the next name in and
     compare it.  If this was the last name, namelist->found will remain
     on.  If not, we loop to compare the newly read name.  */

      if (same_order_option && namelist->found)
    {
      name_gather ();   /* read one more */
      if (namelist->found)
        return NULL;
    }
      else
    return NULL;
    }
}

/*-----------------------------------------------------------------------.
| This returns a name from the namelist which doesn't have ->found set.  |
| It sets ->found before returning, so successive calls will find and    |
| return all the non-found names in the namelist             |
`-----------------------------------------------------------------------*/

struct name *gnu_list_name = NULL;

char *
name_from_list (void)
{
  if (!gnu_list_name)
    gnu_list_name = namelist;
  while (gnu_list_name && gnu_list_name->found)
    gnu_list_name = gnu_list_name->next;
  if (gnu_list_name)
    {
      gnu_list_name->found = 1;
      if (gnu_list_name->change_dir)
    if (chdir (gnu_list_name->change_dir) < 0)
      FATAL_ERROR ((0, errno, _("Cannot change to directory %s"),
            gnu_list_name->change_dir));
      return gnu_list_name->name;
    }
  return NULL;
}

/*---.
| ?  |
`---*/

void
blank_name_list (void)
{
  struct name *name;

  gnu_list_name = 0;
  for (name = namelist; name; name = name->next)
    name->found = 0;
}

/*---.
| ?  |
`---*/

char *
new_name (const char *path, const char *name)
{
  char *buffer = (char *) xmalloc (strlen (path) + strlen (name) + 2);

  sprintf (buffer, "%s/%s", path, name);
  return buffer;
}


/* Excludes names.  */

static char *exclude_pool = NULL;
static int exclude_pool_size = 0;
static int allocated_exclude_pool_size = 0;

static char **simple_exclude_array = NULL;
static int simple_excludes = 0;
static int allocated_simple_excludes = 0;

static char **pattern_exclude_array = NULL;
static int pattern_excludes = 0;
static int allocated_pattern_excludes = 0;

/*---.
| ?  |
`---*/

void
add_exclude (char *name)
{
  int name_size;

  unquote_string (name);    /* FIXME: unquote in all cases?  If ever? */
  name_size = strlen (name) + 1;

  if (exclude_pool_size + name_size > allocated_exclude_pool_size)
    {
      char *previous_exclude_pool = exclude_pool;
      char **cursor;

      allocated_exclude_pool_size = exclude_pool_size + name_size + 1024;
      exclude_pool = (char *)
    xrealloc (exclude_pool, (size_t) allocated_exclude_pool_size);

      for (cursor = simple_exclude_array;
       cursor < simple_exclude_array + simple_excludes;
       cursor++)
    *cursor = exclude_pool + (*cursor - previous_exclude_pool);
      for (cursor = pattern_exclude_array;
       cursor < pattern_exclude_array + pattern_excludes;
       cursor++)
    *cursor = exclude_pool + (*cursor - previous_exclude_pool);
    }

  if (is_pattern (name))
    {
      if (pattern_excludes == allocated_pattern_excludes)
    {
      allocated_pattern_excludes += 32;
      pattern_exclude_array = (char **)
        xrealloc (pattern_exclude_array,
              allocated_pattern_excludes * sizeof (char *));
    }
      pattern_exclude_array[pattern_excludes++]
    = exclude_pool + exclude_pool_size;
    }
  else
    {
      if (simple_excludes == allocated_simple_excludes)
    {
      allocated_simple_excludes += 32;
      simple_exclude_array = (char **)
        xrealloc (simple_exclude_array,
              allocated_simple_excludes * sizeof (char *));
    }
      simple_exclude_array[simple_excludes++]
    = exclude_pool + exclude_pool_size;
    }

  strcpy (exclude_pool + exclude_pool_size, name);
  exclude_pool_size += name_size;
}

/*---.
| ?  |
`---*/

void
add_exclude_file (const char *name)
{
  FILE *file;
  char buffer[1024];

  if (strcmp (name, "-"))
    file = fopen (name, "r");
  else
    {
      request_stdin ("-X");
      file = stdin;
    }
  if (!file)
    FATAL_ERROR ((0, errno, _("Cannot open %s"), name));

  while (fgets (buffer, 1024, file))
    {
      char *end_of_line = strrchr (buffer, '\n');

      if (end_of_line)
    *end_of_line = '\0';
      add_exclude (buffer);
    }
  if (fclose (file) == EOF)
    ERROR ((0, errno, "%s", name));
}

/*------------------------------------------------------------------.
| Returns true if the file NAME should not be added nor extracted.  |
`------------------------------------------------------------------*/

int
check_exclude (const char *name)
{
  int counter;

  for (counter = 0; counter < pattern_excludes; counter++)
    if (fnmatch (pattern_exclude_array[counter], name, FNM_LEADING_DIR) == 0)
      return 1;

  for (counter = 0; counter < simple_excludes; counter++)
    {
      /* Accept the output from strstr only if it is the last part of the
     string.  FIXME: Find a faster way to do this.  */

      char *string = strstr (name, simple_exclude_array[counter]);

      if (string
      && (string == name || string[-1] == '/')
      && string[strlen (simple_exclude_array[counter])] == '\0')
    return 1;
    }
  return 0;
}

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/fnmatch.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/fnmatch.h used in: Step 658
/* Copyright (C) 1991, 1992, 1993, 1996 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.  */

#ifndef _FNMATCH_H

#define _FNMATCH_H  1

#ifdef  __cplusplus
extern "C" {
#endif

#if (defined (__cplusplus) || (defined (__STDC__) && __STDC__) \
     || defined (WIN32))
#undef  __P
#define __P(protos) protos
#else /* Not C++ or ANSI C.  */
#undef  __P
#define __P(protos) ()
/* We can get away without defining `const' here only because in this file
   it is used only inside the prototype for `fnmatch', which is elided in
   non-ANSI C where `const' is problematical.  */
#endif /* C++ or ANSI C.  */


/* We #undef these before defining them because some losing systems
   (HP-UX A.08.07 for example) define these in <unistd.h>.  */
#undef  FNM_PATHNAME
#undef  FNM_NOESCAPE
#undef  FNM_PERIOD

/* Bits set in the FLAGS argument to `fnmatch'.  */
#define FNM_PATHNAME    (1 << 0) /* No wildcard can ever match `/'.  */
#define FNM_NOESCAPE    (1 << 1) /* Backslashes don't quote special chars.  */
#define FNM_PERIOD  (1 << 2) /* Leading `.' is matched only explicitly.  */

#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE)
#define FNM_FILE_NAME   FNM_PATHNAME /* Preferred GNU name.  */
#define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match.  */
#define FNM_CASEFOLD    (1 << 4) /* Compare without regard to case.  */
#endif

/* Value returned by `fnmatch' if STRING does not match PATTERN.  */
#define FNM_NOMATCH 1

/* Match STRING against the filename pattern PATTERN,
   returning zero if it matches, FNM_NOMATCH if not.  */
extern int fnmatch __P ((const char *__pattern, const char *__string,
             int __flags));

#ifdef  __cplusplus
}
#endif

#endif /* fnmatch.h */

https://git.savannah.gnu.org/cgit/tar.git/tree//src/open3.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/open3.c used in: Step 659
/* Defines for Sys V style 3-argument open call.
   Copyright (C) 1988, 1994, 1995, 1996 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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include "system.h"

#if EMUL_OPEN3

/* open3.h -- #defines for the various flags for the Sys V style 3-argument
   open() call.  On BSD or System 5, the system already has this in an
   include file.  This file is needed for V7 and MINIX systems for the
   benefit of open3() in port.c, a routine that emulates the 3-argument call
   using system calls available on V7/MINIX.

   Written 1987-06-10 by Richard Todd.

   The names have been changed by John Gilmore, 1987-07-31, since Richard
   called it "bsdopen", and really this change was introduced in AT&T Unix
   systems before BSD picked it up.  */

/*-----------------------------------------------------------------------.
| open3 -- routine to emulate the 3-argument open system.        |
|                                    |
| open3 (path, flag, mode);                      |
|                                    |
| Attempts to open the file specified by the given pathname.  The    |
| following flag bits specify options to the routine.  Needless to say,  |
| you should only specify one of the first three.  Function returns file |
| descriptor if successful, -1 and errno if not.             |
`-----------------------------------------------------------------------*/

/* The routine obeys the following mode arguments:

   O_RDONLY file open for read only
   O_WRONLY file open for write only
   O_RDWR   file open for both read & write

   O_CREAT  file is created with specified mode if it needs to be
   O_TRUNC  if file exists, it is truncated to 0 bytes
   O_EXCL   used with O_CREAT--routine returns error if file exists  */

/* Call that if present in most modern Unix systems.  This version attempts
   to support all the flag bits except for O_NDELAY and O_APPEND, which are
   silently ignored.  The emulation is not as efficient as the real thing
   (at worst, 4 system calls instead of one), but there's not much I can do
   about that.  */

/* Array to give arguments to access for various modes FIXME, this table
   depends on the specific integer values of O_*, and also contains
   integers (args to 'access') that should be #define's.  */

static int modes[] =
  {
    04,             /* O_RDONLY */
    02,             /* O_WRONLY */
    06,             /* O_RDWR */
    06,             /* invalid, just cope: O_WRONLY+O_RDWR */
  };

/* Shut off the automatic emulation of open(), we'll need it. */
#undef open

int
open3 (char *path, int flags, int mode)
{
  int exists = 1;
  int call_creat = 0;

  /* We actually do the work by calling the open() or creat() system
     call, depending on the flags.  Call_creat is true if we will use
     creat(), false if we will use open().  */

  /* See if the file exists and is accessible in the requested mode.

     Strictly speaking we shouldn't be using access, since access checks
     against real uid, and the open call should check against euid.  Most
     cases real uid == euid, so it won't matter.  FIXME.  FIXME, the
     construction "flags & 3" and the modes table depends on the specific
     integer values of the O_* #define's.  Foo!  */

  if (access (path, modes[flags & 3]) < 0)
    {
      if (errno == ENOENT)
    {
      /* The file does not exist.  */

      exists = 0;
    }
      else
    {
      /* Probably permission violation.  */

      if (flags & O_EXCL)
        {
          /* Oops, the file exists, we didn't want it.  No matter
         what the error, claim EEXIST.  */

          errno = EEXIST;   /* FIXME: errno should be read-only */
        }
      return -1;
    }
    }

  /* If we have the O_CREAT bit set, check for O_EXCL.  */

  if (flags & O_CREAT)
    {
      if ((flags & O_EXCL) && exists)
    {
      /* Oops, the file exists and we didn't want it to.  */

      errno = EEXIST;   /* FIXME: errno should be read-only */
      return -1;
    }

      /* If the file doesn't exist, be sure to call creat() so that it
     will be created with the proper mode.  */

      if (!exists)
    call_creat = 1;
    }
  else
    {
      /* If O_CREAT isn't set and the file doesn't exist, error.  */

      if (!exists)
    {
      errno = ENOENT;   /* FIXME: errno should be read-only */
      return -1;
    }
    }

  /* If the O_TRUNC flag is set and the file exists, we want to call
     creat() anyway, since creat() guarantees that the file will be
     truncated and open()-for-writing doesn't.  (If the file doesn't
     exist, we're calling creat() anyway and the file will be created
     with zero length.)  */

  if ((flags & O_TRUNC) && exists)
    call_creat = 1;

  /* Actually do the call.  */

  if (call_creat)

    /* Call creat.  May have to close and reopen the file if we want
       O_RDONLY or O_RDWR access -- creat() only gives O_WRONLY.  */

    {
      int fd = creat (path, mode);

      if (fd < 0 || (flags & O_WRONLY))
    return fd;
      if (close (fd) < 0)
    return -1;

      /* Fall out to reopen the file we've created.  */
    }

  /* Calling old open, we strip most of the new flags just in case.  */

  return open (path, flags & (O_RDONLY | O_WRONLY | O_RDWR | O_BINARY));
}

#endif /* EMUL_OPEN3 */

https://git.savannah.gnu.org/cgit/tar.git/tree//src/rtapelib.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/rtapelib.c used in: Step 660
/* Functions for communicating with a remote tape drive.
   Copyright (C) 1988, 1992, 1994, 1996 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.  */

/* The man page rmt(8) for /etc/rmt documents the remote mag tape protocol
   which rdump and rrestore use.  Unfortunately, the man page is *WRONG*.
   The author of the routines I'm including originally wrote his code just
   based on the man page, and it didn't work, so he went to the rdump source
   to figure out why.  The only thing he had to change was to check for the
   'F' return code in addition to the 'E', and to separate the various
   arguments with \n instead of a space.  I personally don't think that this
   is much of a problem, but I wanted to point it out. -- Arnold Robbins

   Originally written by Jeff Lee, modified some by Arnold Robbins.  Redone
   as a library that can replace open, read, write, etc., by Fred Fish, with
   some additional work by Arnold Robbins.  Modified to make all rmt* calls
   into macros for speed by Jay Fenlason.  Use -DHAVE_NETDB_H for rexec
   code, courtesy of Dan Kegel.  */

#include "system.h"

/* Try hard to get EOPNOTSUPP defined.  486/ISC has it in net/errno.h,
   3B2/SVR3 has it in sys/inet.h.  Otherwise, like on MSDOS, use EINVAL.  */

#ifndef EOPNOTSUPP
# if HAVE_NET_ERRNO_H
#  include <net/errno.h>
# endif
# if HAVE_SYS_INET_H
#  include <sys/inet.h>
# endif
# ifndef EOPNOTSUPP
#  define EOPNOTSUPP EINVAL
# endif
#endif

#include <signal.h>

#if HAVE_NETDB_H
# include <netdb.h>
#endif

#include "rmt.h"

/* FIXME: Just to shut up -Wall.  */
int rexec ();

/* Exit status if exec errors.  */
#define EXIT_ON_EXEC_ERROR 128

/* FIXME: Size of buffers for reading and writing commands to rmt.  */
#define COMMAND_BUFFER_SIZE 64

#ifndef RETSIGTYPE
# define RETSIGTYPE void
#endif

/* FIXME: Maximum number of simultaneous remote tape connections.  */
#define MAXUNIT 4

#define PREAD 0         /* read  file descriptor from pipe() */
#define PWRITE 1        /* write file descriptor from pipe() */

/* Return the parent's read side of remote tape connection Fd.  */
#define READ_SIDE(Fd) (from_remote[Fd][PREAD])

/* Return the parent's write side of remote tape connection Fd.  */
#define WRITE_SIDE(Fd) (to_remote[Fd][PWRITE])

/* The pipes for receiving data from remote tape drives.  */
static int from_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}};

/* The pipes for sending data to remote tape drives.  */
static int to_remote[MAXUNIT][2] = {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}};

/* Temporary variable used by macros in rmt.h.  */
char *rmt_path__;



/*----------------------------------------------------------------------.
| Close remote tape connection HANDLE, and reset errno to ERRNO_VALUE.  |
`----------------------------------------------------------------------*/

static void
_rmt_shutdown (int handle, int errno_value)
{
  close (READ_SIDE (handle));
  close (WRITE_SIDE (handle));
  READ_SIDE (handle) = -1;
  WRITE_SIDE (handle) = -1;
  errno = errno_value;      /* FIXME: errno should be read-only */
}

/*-------------------------------------------------------------------------.
| Attempt to perform the remote tape command specified in BUFFER on remote |
| tape connection HANDLE.  Return 0 if successful, -1 on error.        |
`-------------------------------------------------------------------------*/

static int
do_command (int handle, const char *buffer)
{
  int length;
  RETSIGTYPE (*pipe_handler) ();

  /* Save the current pipe handler and try to make the request.  */

  pipe_handler = signal (SIGPIPE, SIG_IGN);
  length = strlen (buffer);
  if (write (WRITE_SIDE (handle), buffer, (size_t) length) == length)
    {
      signal (SIGPIPE, pipe_handler);
      return 0;
    }

  /* Something went wrong.  Close down and go home.  */

  signal (SIGPIPE, pipe_handler);
  _rmt_shutdown (handle, EIO);
  return -1;
}

/*----------------------------------------------------------------------.
| Read and return the status from remote tape connection HANDLE.  If an |
| error occurred, return -1 and set errno.                  |
`----------------------------------------------------------------------*/

static int
get_status (int handle)
{
  char command_buffer[COMMAND_BUFFER_SIZE];
  char *cursor;
  int counter;

  /* Read the reply command line.  */

  for (counter = 0, cursor = command_buffer;
       counter < COMMAND_BUFFER_SIZE;
       counter++, cursor++)
    {
      if (read (READ_SIDE (handle), cursor, 1) != 1)
    {
      _rmt_shutdown (handle, EIO);
      return -1;
    }
      if (*cursor == '\n')
    {
      *cursor = '\0';
      break;
    }
    }

  if (counter == COMMAND_BUFFER_SIZE)
    {
      _rmt_shutdown (handle, EIO);
      return -1;
    }

  /* Check the return status.  */

  for (cursor = command_buffer; *cursor; cursor++)
    if (*cursor != ' ')
      break;

  if (*cursor == 'E' || *cursor == 'F')
    {
      errno = atoi (cursor + 1); /* FIXME: errno should be read-only */

      /* Skip the error message line.  */

      /* FIXME: there is better to do than merely ignoring error messages
     coming from the remote end.  Translate them, too...  */

      {
    char character;

    while (read (READ_SIDE (handle), &character, 1) == 1)
      if (character == '\n')
        break;
      }

      if (*cursor == 'F')
    _rmt_shutdown (handle, errno);

      return -1;
    }

  /* Check for mis-synced pipes.  */

  if (*cursor != 'A')
    {
      _rmt_shutdown (handle, EIO);
      return -1;
    }

  /* Got an `A' (success) response.  */

  return atoi (cursor + 1);
}

#if HAVE_NETDB_H

/*-------------------------------------------------------------------------.
| Execute /etc/rmt as user USER on remote system HOST using rexec.  Return |
| a file descriptor of a bidirectional socket for stdin and stdout.  If    |
| USER is NULL, use the current username.                  |
|                                      |
| By default, this code is not used, since it requires that the user have  |
| a .netrc file in his/her home directory, or that the application     |
| designer be willing to have rexec prompt for login and password info.    |
| This may be unacceptable, and .rhosts files for use with rsh are much    |
| more common on BSD systems.                          |
`-------------------------------------------------------------------------*/

static int
_rmt_rexec (char *host, char *user)
{
  int saved_stdin = dup (fileno (stdin));
  int saved_stdout = dup (fileno (stdout));
  struct servent *rexecserv;
  int result;

  /* When using cpio -o < filename, stdin is no longer the tty.  But the
     rexec subroutine reads the login and the passwd on stdin, to allow
     remote execution of the command.  So, reopen stdin and stdout on
     /dev/tty before the rexec and give them back their original value
     after.  */

  if (freopen ("/dev/tty", "r", stdin) == NULL)
    freopen ("/dev/null", "r", stdin);
  if (freopen ("/dev/tty", "w", stdout) == NULL)
    freopen ("/dev/null", "w", stdout);

  if (rexecserv = getservbyname ("exec", "tcp"), !rexecserv)
    error (EXIT_ON_EXEC_ERROR, 0, _("exec/tcp: Service not available"));

  result = rexec (&host, rexecserv->s_port, user, NULL,
           "/etc/rmt", (int *) NULL);
  if (fclose (stdin) == EOF)
    error (0, errno, _("stdin"));
  fdopen (saved_stdin, "r");
  if (fclose (stdout) == EOF)
    error (0, errno, _("stdout"));
  fdopen (saved_stdout, "w");

  return result;
}

#endif /* HAVE_NETDB_H */

/*------------------------------------------------------------------------.
| Open a file (a magnetic tape device?) on the system specified in PATH,  |
| as the given user.  PATH has the form `[USER@]HOST:FILE'.  OPEN_MODE is |
| O_RDONLY, O_WRONLY, etc.  If successful, return the remote pipe number  |
| plus BIAS.  REMOTE_SHELL may be overriden.  On error, return -1.    |
`------------------------------------------------------------------------*/

int
rmt_open__ (const char *path, int open_mode, int bias, const char *remote_shell)
{
  int remote_pipe_number;   /* pseudo, biased file descriptor */
  char *path_copy ;     /* copy of path string */
  char *remote_host;        /* remote host name */
  char *remote_file;        /* remote file name (often a device) */
  char *remote_user;        /* remote user name */

  /* Find an unused pair of file descriptors.  */

  for (remote_pipe_number = 0;
       remote_pipe_number < MAXUNIT;
       remote_pipe_number++)
    if (READ_SIDE (remote_pipe_number) == -1
    && WRITE_SIDE (remote_pipe_number) == -1)
      break;

  if (remote_pipe_number == MAXUNIT)
    {
      errno = EMFILE;       /* FIXME: errno should be read-only */
      return -1;
    }

  /* Pull apart the system and device, and optional user.  */

  {
    char *cursor;

    path_copy = xstrdup (path);
    remote_host = path_copy;
    remote_user = NULL;
    remote_file = NULL;

    for (cursor = path_copy; *cursor; cursor++)
      switch (*cursor)
    {
    default:
      break;

    case '@':
      if (!remote_user)
        {
          remote_user = remote_host;
          *cursor = '\0';
          remote_host = cursor + 1;
        }
      break;

    case ':':
      if (!remote_file)
        {
          *cursor = '\0';
          remote_file = cursor + 1;
        }
      break;
    }
  }

  /* FIXME: Should somewhat validate the decoding, here.  */

  if (remote_user && *remote_user == '\0')
    remote_user = NULL;

#if HAVE_NETDB_H

  /* Execute the remote command using rexec.  */

  READ_SIDE (remote_pipe_number) = _rmt_rexec (remote_host, remote_user);
  if (READ_SIDE (remote_pipe_number) < 0)
    {
      free (path_copy);
      return -1;
    }

  WRITE_SIDE (remote_pipe_number) = READ_SIDE (remote_pipe_number);

#else /* not HAVE_NETDB_H */
  {
    const char *remote_shell_basename;
    int status;

    /* Identify the remote command to be executed.  */

    if (!remote_shell)
      {
#ifdef REMOTE_SHELL
    remote_shell = REMOTE_SHELL;
#else
    errno = EIO;        /* FIXME: errno should be read-only */
    free (path_copy);
    return -1;
#endif
      }
    remote_shell_basename = strrchr (remote_shell, '/');
    if (remote_shell_basename)
      remote_shell_basename++;
    else
      remote_shell_basename = remote_shell;

    /* Set up the pipes for the `rsh' command, and fork.  */

    if (pipe (to_remote[remote_pipe_number]) == -1
    || pipe (from_remote[remote_pipe_number]) == -1)
      {
    free (path_copy);
    return -1;
      }

    status = fork ();
    if (status == -1)
      {
    free (path_copy);
    return -1;
      }

    if (status == 0)
      {
    /* Child.  */

    close (0);
    dup (to_remote[remote_pipe_number][PREAD]);
    close (to_remote[remote_pipe_number][PREAD]);
    close (to_remote[remote_pipe_number][PWRITE]);

    close (1);
    dup (from_remote[remote_pipe_number][PWRITE]);
    close (from_remote[remote_pipe_number][PREAD]);
    close (from_remote[remote_pipe_number][PWRITE]);

#if !MSDOS
    setuid (getuid ());
    setgid (getgid ());
#endif

    if (remote_user)
      execl (remote_shell, remote_shell_basename, remote_host,
         "-l", remote_user, "/etc/rmt", (char *) 0);
    else
      execl (remote_shell, remote_shell_basename, remote_host,
         "/etc/rmt", (char *) 0);

    /* Bad problems if we get here.  */

    /* In a previous version, _exit was used here instead of exit.  */
    error (EXIT_ON_EXEC_ERROR, errno, _("Cannot execute remote shell"));
      }

    /* Parent.  */

    close (from_remote[remote_pipe_number][PWRITE]);
    close (to_remote[remote_pipe_number][PREAD]);
  }
#endif /* not HAVE_NETDB_H */

  /* Attempt to open the tape device.  */

  {
    char command_buffer[COMMAND_BUFFER_SIZE];

    sprintf (command_buffer, "O%s\n%d\n", remote_file, open_mode);
    if (do_command (remote_pipe_number, command_buffer) == -1
    || get_status (remote_pipe_number) == -1)
      {
    _rmt_shutdown (remote_pipe_number, errno);
    free (path_copy);
    return -1;
      }
  }

  free (path_copy);
  return remote_pipe_number + bias;
}

/*----------------------------------------------------------------.
| Close remote tape connection HANDLE and shut down.  Return 0 if |
| successful, -1 on error.                    |
`----------------------------------------------------------------*/

int
rmt_close__ (int handle)
{
  int status;

  if (do_command (handle, "C\n") == -1)
    return -1;

  status = get_status (handle);
  _rmt_shutdown (handle, errno);
  return status;
}

/*-------------------------------------------------------------------------.
| Read up to LENGTH bytes into BUFFER from remote tape connection HANDLE.  |
| Return the number of bytes read on success, -1 on error.         |
`-------------------------------------------------------------------------*/

int
rmt_read__ (int handle, char *buffer, unsigned int length)
{
  char command_buffer[COMMAND_BUFFER_SIZE];
  int status;
  int counter;

  sprintf (command_buffer, "R%d\n", length);
  if (do_command (handle, command_buffer) == -1
      || (status = get_status (handle)) == -1)
    return -1;

  for (counter = 0; counter < status; counter += length, buffer += length)
    {
      length = read (READ_SIDE (handle), buffer, (size_t) (status - counter));
      if (length <= 0)
    {
      _rmt_shutdown (handle, EIO);
      return -1;
    }
    }

  return status;
}

/*-------------------------------------------------------------------------.
| Write LENGTH bytes from BUFFER to remote tape connection HANDLE.  Return |
| the number of bytes written on success, -1 on error.             |
`-------------------------------------------------------------------------*/

int
rmt_write__ (int handle, char *buffer, unsigned int length)
{
  char command_buffer[COMMAND_BUFFER_SIZE];
  RETSIGTYPE (*pipe_handler) ();

  sprintf (command_buffer, "W%d\n", length);
  if (do_command (handle, command_buffer) == -1)
    return -1;

  pipe_handler = signal (SIGPIPE, SIG_IGN);
  if (write (WRITE_SIDE (handle), buffer, length) == length)
    {
      signal (SIGPIPE, pipe_handler);
      return get_status (handle);
    }

  /* Write error.  */

  signal (SIGPIPE, pipe_handler);
  _rmt_shutdown (handle, EIO);
  return -1;
}

/*------------------------------------------------------------------------.
| Perform an imitation lseek operation on remote tape connection HANDLE.  |
| Return the new file offset if successful, -1 if on error.       |
`------------------------------------------------------------------------*/

long
rmt_lseek__ (int handle, off_t offset, int whence)
{
  char command_buffer[COMMAND_BUFFER_SIZE];

  sprintf (command_buffer, "L%ld\n%d\n", offset, whence);
  if (do_command (handle, command_buffer) == -1)
    return -1;

  return get_status (handle);
}

/*-----------------------------------------------------------------------.
| Perform a raw tape operation on remote tape connection HANDLE.  Return |
| the results of the ioctl, or -1 on error.              |
`-----------------------------------------------------------------------*/

int
rmt_ioctl__ (int handle, int operation, char *argument)
{
  switch (operation)
    {
    default:
      errno = EOPNOTSUPP;   /* FIXME: errno should be read-only */
      return -1;

#ifdef MTIOCTOP
    case MTIOCTOP:
      {
    char command_buffer[COMMAND_BUFFER_SIZE];

    /* MTIOCTOP is the easy one.  Nothing is transfered in binary.  */

    sprintf (command_buffer, "I%d\n%d\n", ((struct mtop *) argument)->mt_op,
         ((struct mtop *) argument)->mt_count);
    if (do_command (handle, command_buffer) == -1)
      return -1;

    /* Return the count.  */

    return get_status (handle);
      }
#endif /* MTIOCTOP */

#ifdef MTIOCGET
    case MTIOCGET:
      {
    int status;
    int counter;

    /* Grab the status and read it directly into the structure.  This
       assumes that the status buffer is not padded and that 2 shorts
       fit in a long without any word alignment problems; i.e., the
       whole struct is contiguous.  NOTE - this is probably NOT a good
       assumption.  */

    if (do_command (handle, "S") == -1
        || (status = get_status (handle), status == -1))
      return -1;

    for (; status > 0; status -= counter, argument += counter)
      {
        counter = read (READ_SIDE (handle), argument, (size_t) status);
        if (counter <= 0)
          {
        _rmt_shutdown (handle, EIO);
        return -1;
          }
      }

    /* Check for byte position.  mt_type (or mt_model) is a small integer
       field (normally) so we will check its magnitude.  If it is larger
       than 256, we will assume that the bytes are swapped and go through
       and reverse all the bytes.  */

    if (((struct mtget *) argument)->MTIO_CHECK_FIELD < 256)
      return 0;

    for (counter = 0; counter < status; counter += 2)
      {
        char copy = argument[counter];

        argument[counter] = argument[counter + 1];
        argument[counter + 1] = copy;
      }

    return 0;
      }
#endif /* MTIOCGET */

    }
}

https://git.savannah.gnu.org/cgit/tar.git/tree//src/tar.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/tar.c used in: Step 661
/* A tar (tape archiver) program.
   Copyright (C) 1988, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
   Written by John Gilmore, starting 1985-08-25.

   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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include "system.h"

#include <getopt.h>

/* The following causes "common.h" to produce definitions of all the global
   variables, rather than just "extern" declarations of them.  GNU tar does
   depend on the system loader to preset all GLOBAL variables to neutral (or
   zero) values, explicit initialisation is usually not done.  */
#define GLOBAL
#include "common.h"

#include "backupfile.h"
enum backup_type get_version ();

/* FIXME: We should use a conversion routine that does reasonable error
   checking -- atoi doesn't.  For now, punt.  */
#define intconv atoi

time_t get_date ();

/* Local declarations.  */

#ifndef DEFAULT_ARCHIVE
# define DEFAULT_ARCHIVE "tar.out"
#endif

#ifndef DEFAULT_BLOCKING
# define DEFAULT_BLOCKING 20
#endif

static void usage PARAMS ((int));


/* Miscellaneous.  */

/*------------------------------------------------------------------------.
| Check if STRING is the decimal representation of number, and return its |
| value.  If not a decimal number, return -1.                 |
`------------------------------------------------------------------------*/

static int
check_decimal (const char *string)
{
  int value = -1;

  while (*string)
    switch (*string)
      {
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
    value = value < 0 ? *string - '0' : 10 * value + *string - '0';
    string++;
    break;

      default:
    return -1;
      }
    return value;
}

/*----------------------------------------------.
| Doesn't return if stdin already requested.    |
`----------------------------------------------*/

/* Name of option using stdin.  */
static const char *stdin_used_by = NULL;

void
request_stdin (const char *option)
{
  if (stdin_used_by)
    USAGE_ERROR ((0, 0, _("Options `-%s' and `-%s' both want standard input"),
          stdin_used_by, option));

  stdin_used_by = option;
}

/*--------------------------------------------------------.
| Returns true if and only if the user typed 'y' or 'Y'.  |
`--------------------------------------------------------*/

int
confirm (const char *message_action, const char *message_name)
{
  static FILE *confirm_file = NULL;

  if (!confirm_file)
    {
      if (archive == 0 || stdin_used_by)
    confirm_file = fopen (TTY_NAME, "r");
      else
    {
      request_stdin ("-w");
      confirm_file = stdin;
    }

      if (!confirm_file)
    FATAL_ERROR ((0, 0, _("Cannot read confirmation from user")));
    }

  fprintf (stdlis, "%s %s?", message_action, message_name);
  fflush (stdlis);

  {
    int reply = getc (confirm_file);
    int character;

    for (character = reply;
     character != '\n' && character != EOF;
     character = getc (confirm_file))
      continue;
    return reply == 'y' || reply == 'Y';
  }
}


/* Options.  */

/* For long options that unconditionally set a single flag, we have getopt
   do it.  For the others, we share the code for the equivalent short
   named option, the name of which is stored in the otherwise-unused `val'
   field of the `struct option'; for long options that have no equivalent
   short option, we use nongraphic characters as pseudo short option
   characters, starting at 2 and going upwards.  */

#define BACKUP_OPTION           2
#define DELETE_OPTION           3
#define EXCLUDE_OPTION          4
#define GROUP_OPTION            5
#define MODE_OPTION         6
#define NEWER_MTIME_OPTION      7
#define NO_RECURSE_OPTION       8
#define NULL_OPTION         9
#define OWNER_OPTION            10
#define POSIX_OPTION            11
#define PRESERVE_OPTION         12
#define RECORD_SIZE_OPTION      13
#define RSH_COMMAND_OPTION      14
#define SUFFIX_OPTION           15
#define USE_COMPRESS_PROGRAM_OPTION 16
#define VOLNO_FILE_OPTION       17

/* Some cleanup is being made in GNU tar long options.  Using old names is
   allowed for a while, but will also send a warning to stderr.  Take old
   names out in 1.14, or in summer 1997, whichever happens last.  We use
   nongraphic characters as pseudo short option characters, starting at 31
   and going downwards.  */

#define OBSOLETE_ABSOLUTE_NAMES     31
#define OBSOLETE_BLOCK_COMPRESS     30
#define OBSOLETE_BLOCKING_FACTOR    29
#define OBSOLETE_BLOCK_NUMBER       28
#define OBSOLETE_READ_FULL_RECORDS  27
#define OBSOLETE_TOUCH          26
#define OBSOLETE_VERSION_CONTROL    25

/* If nonzero, display usage information and exit.  */
static int show_help = 0;

/* If nonzero, print the version on standard output and exit.  */
static int show_version = 0;

struct option long_options[] =
{
  {"absolute-names", no_argument, NULL, 'P'},
  {"absolute-paths", no_argument, NULL, OBSOLETE_ABSOLUTE_NAMES},
  {"after-date", required_argument, NULL, 'N'},
  {"append", no_argument, NULL, 'r'},
  {"atime-preserve", no_argument, &atime_preserve_option, 1},
  {"backup", optional_argument, NULL, BACKUP_OPTION},
  {"block-compress", no_argument, NULL, OBSOLETE_BLOCK_COMPRESS},
  {"block-number", no_argument, NULL, 'R'},
  {"block-size", required_argument, NULL, OBSOLETE_BLOCKING_FACTOR},
  {"blocking-factor", required_argument, NULL, 'b'},
  {"catenate", no_argument, NULL, 'A'},
  {"checkpoint", no_argument, &checkpoint_option, 1},
  {"compare", no_argument, NULL, 'd'},
  {"compress", no_argument, NULL, 'Z'},
  {"concatenate", no_argument, NULL, 'A'},
  {"confirmation", no_argument, NULL, 'w'},
  /* FIXME: --selective as a synonym for --confirmation?  */
  {"create", no_argument, NULL, 'c'},
  {"delete", no_argument, NULL, DELETE_OPTION},
  {"dereference", no_argument, NULL, 'h'},
  {"diff", no_argument, NULL, 'd'},
  {"directory", required_argument, NULL, 'C'},
  {"exclude", required_argument, NULL, EXCLUDE_OPTION},
  {"exclude-from", required_argument, NULL, 'X'},
  {"extract", no_argument, NULL, 'x'},
  {"file", required_argument, NULL, 'f'},
  {"files-from", required_argument, NULL, 'T'},
  {"force-local", no_argument, &force_local_option, 1},
  {"get", no_argument, NULL, 'x'},
  {"group", required_argument, NULL, GROUP_OPTION},
  {"gunzip", no_argument, NULL, 'z'},
  {"gzip", no_argument, NULL, 'z'},
  {"help", no_argument, &show_help, 1},
  {"ignore-failed-read", no_argument, &ignore_failed_read_option, 1},
  {"ignore-zeros", no_argument, NULL, 'i'},
  /* FIXME: --ignore-end as a new name for --ignore-zeros?  */
  {"incremental", no_argument, NULL, 'G'},
  {"info-script", required_argument, NULL, 'F'},
  {"interactive", no_argument, NULL, 'w'},
  {"keep-old-files", no_argument, NULL, 'k'},
  {"label", required_argument, NULL, 'V'},
  {"list", no_argument, NULL, 't'},
  {"listed-incremental", required_argument, NULL, 'g'},
  {"mode", required_argument, NULL, MODE_OPTION},
  {"modification-time", no_argument, NULL, OBSOLETE_TOUCH},
  {"multi-volume", no_argument, NULL, 'M'},
  {"new-volume-script", required_argument, NULL, 'F'},
  {"newer", required_argument, NULL, 'N'},
  {"newer-mtime", required_argument, NULL, NEWER_MTIME_OPTION},
  {"null", no_argument, NULL, NULL_OPTION},
  {"no-recursion", no_argument, NULL, NO_RECURSE_OPTION},
  {"numeric-owner", no_argument, &numeric_owner_option, 1},
  {"old-archive", no_argument, NULL, 'o'},
  {"one-file-system", no_argument, NULL, 'l'},
  {"owner", required_argument, NULL, OWNER_OPTION},
  {"portability", no_argument, NULL, 'o'},
  {"posix", no_argument, NULL, POSIX_OPTION},
  {"preserve", no_argument, NULL, PRESERVE_OPTION},
  {"preserve-order", no_argument, NULL, 's'},
  {"preserve-permissions", no_argument, NULL, 'p'},
  {"recursive-unlink", no_argument, &recursive_unlink_option, 1},
  {"read-full-blocks", no_argument, NULL, OBSOLETE_READ_FULL_RECORDS},
  {"read-full-records", no_argument, NULL, 'B'},
  /* FIXME: --partial-blocks might be a synonym for --read-full-records?  */
  {"record-number", no_argument, NULL, OBSOLETE_BLOCK_NUMBER},
  {"record-size", required_argument, NULL, RECORD_SIZE_OPTION},
  {"remove-files", no_argument, &remove_files_option, 1},
  {"rsh-command", required_argument, NULL, RSH_COMMAND_OPTION},
  {"same-order", no_argument, NULL, 's'},
  {"same-owner", no_argument, &same_owner_option, 1},
  {"same-permissions", no_argument, NULL, 'p'},
  {"show-omitted-dirs", no_argument, &show_omitted_dirs_option, 1},
  {"sparse", no_argument, NULL, 'S'},
  {"starting-file", required_argument, NULL, 'K'},
  {"suffix", required_argument, NULL, SUFFIX_OPTION},
  {"tape-length", required_argument, NULL, 'L'},
  {"to-stdout", no_argument, NULL, 'O'},
  {"totals", no_argument, &totals_option, 1},
  {"touch", no_argument, NULL, 'm'},
  {"uncompress", no_argument, NULL, 'Z'},
  {"ungzip", no_argument, NULL, 'z'},
  {"unlink-first", no_argument, NULL, 'U'},
  {"update", no_argument, NULL, 'u'},
  {"use-compress-program", required_argument, NULL, USE_COMPRESS_PROGRAM_OPTION},
  {"verbose", no_argument, NULL, 'v'},
  {"verify", no_argument, NULL, 'W'},
  {"version", no_argument, &show_version, 1},
  {"version-control", required_argument, NULL, OBSOLETE_VERSION_CONTROL},
  {"volno-file", required_argument, NULL, VOLNO_FILE_OPTION},

  {0, 0, 0, 0}
};

/*---------------------------------------------.
| Print a usage message and exit with STATUS.  |
`---------------------------------------------*/

static void
usage (int status)
{
  if (status != TAREXIT_SUCCESS)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      fputs (_("\
GNU `tar' saves many files together into a single tape or disk archive, and\n\
can restore individual files from the archive.\n"),
         stdout);
      printf (_("\nUsage: %s [OPTION]... [FILE]...\n"), program_name);
      fputs (_("\
\n\
If a long option shows an argument as mandatory, then it is mandatory\n\
for the equivalent short option also.  Similarly for optional arguments.\n"),
         stdout);
      fputs(_("\
\n\
Main operation mode:\n\
  -t, --list              list the contents of an archive\n\
  -x, --extract, --get    extract files from an archive\n\
  -c, --create            create a new archive\n\
  -d, --diff, --compare   find differences between archive and file system\n\
  -r, --append            append files to the end of an archive\n\
  -u, --update            only append files newer than copy in archive\n\
  -A, --catenate          append tar files to an archive\n\
      --concatenate       same as -A\n\
      --delete            delete from the archive (not on mag tapes!)\n"),
        stdout);
      fputs (_("\
\n\
Operation modifiers:\n\
  -W, --verify               attempt to verify the archive after writing it\n\
      --remove-files         remove files after adding them to the archive\n\
  -k, --keep-old-files       don't overwrite existing files when extracting\n\
  -U, --unlink-first         remove each file prior to extracting over it\n\
      --recursive-unlink     empty hierarchies prior to extracting directory\n\
  -S, --sparse               handle sparse files efficiently\n\
  -O, --to-stdout            extract files to standard output\n\
  -G, --incremental          handle old GNU-format incremental backup\n\
  -g, --listed-incremental   handle new GNU-format incremental backup\n\
      --ignore-failed-read   do not exit with nonzero on unreadable files\n"),
         stdout);
      fputs (_("\
\n\
Handling of file attributes:\n\
      --owner=NAME             force NAME as owner for added files\n\
      --group=NAME             force NAME as group for added files\n\
      --mode=CHANGES           force (symbolic) mode CHANGES for added files\n\
      --atime-preserve         don't change access times on dumped files\n\
  -m, --modification-time      don't extract file modified time\n\
      --same-owner             try extracting files with the same ownership\n\
      --numeric-owner          always use numbers for user/group names\n\
  -p, --same-permissions       extract all protection information\n\
      --preserve-permissions   same as -p\n\
  -s, --same-order             sort names to extract to match archive\n\
      --preserve-order         same as -s\n\
      --preserve               same as both -p and -s\n"),
         stdout);
      fputs (_("\
\n\
Device selection and switching:\n\
  -f, --file=ARCHIVE             use archive file or device ARCHIVE\n\
      --force-local              archive file is local even if has a colon\n\
      --rsh-command=COMMAND      use remote COMMAND instead of rsh\n\
  -[0-7][lmh]                    specify drive and density\n\
  -M, --multi-volume             create/list/extract multi-volume archive\n\
  -L, --tape-length=NUM          change tape after writing NUM x 1024 bytes\n\
  -F, --info-script=FILE         run script at end of each tape (implies -M)\n\
      --new-volume-script=FILE   same as -F FILE\n\
      --volno-file=FILE          use/update the volume number in FILE\n"),
         stdout);
      fputs (_("\
\n\
Device blocking:\n\
  -b, --blocking-factor=BLOCKS   BLOCKS x 512 bytes per record\n\
      --record-size=SIZE         SIZE bytes per record, multiple of 512\n\
  -i, --ignore-zeros             ignore zeroed blocks in archive (means EOF)\n\
  -B, --read-full-records        reblock as we read (for 4.2BSD pipes)\n"),
         stdout);
      fputs (_("\
\n\
Archive format selection:\n\
  -V, --label=NAME                   create archive with volume name NAME\n\
              PATTERN                at list/extract time, a globbing PATTERN\n\
  -o, --old-archive, --portability   write a V7 format archive\n\
      --posix                        write a POSIX conformant archive\n\
  -z, --gzip, --ungzip               filter the archive through gzip\n\
  -Z, --compress, --uncompress       filter the archive through compress\n\
      --use-compress-program=PROG    filter through PROG (must accept -d)\n"),
         stdout);
      fputs (_("\
\n\
Local file selection:\n\
  -C, --directory=DIR          change to directory DIR\n\
  -T, --files-from=NAME        get names to extract or create from file NAME\n\
      --null                   -T reads null-terminated names, disable -C\n\
      --exclude=PATTERN        exclude files, given as a globbing PATTERN\n\
  -X, --exclude-from=FILE      exclude globbing patterns listed in FILE\n\
  -P, --absolute-names         don't strip leading `/'s from file names\n\
  -h, --dereference            dump instead the files symlinks point to\n\
      --no-recursion           avoid descending automatically in directories\n\
  -l, --one-file-system        stay in local file system when creating archive\n\
  -K, --starting-file=NAME     begin at file NAME in the archive\n"),
         stdout);
#if !MSDOS
      fputs (_("\
  -N, --newer=DATE             only store files newer than DATE\n\
      --newer-mtime            compare date and time when data changed only\n\
      --after-date=DATE        same as -N\n"),
         stdout);
#endif
      fputs (_("\
      --backup[=CONTROL]       backup before removal, choose version control\n\
      --suffix=SUFFIX          backup before removel, override usual suffix\n"),
         stdout);
      fputs (_("\
\n\
Informative output:\n\
      --help            print this help, then exit\n\
      --version         print tar program version number, then exit\n\
  -v, --verbose         verbosely list files processed\n\
      --checkpoint      print directory names while reading the archive\n\
      --totals          print total bytes written while creating archive\n\
  -R, --block-number    show block number within archive with each message\n\
  -w, --interactive     ask for confirmation for every action\n\
      --confirmation    same as -w\n"),
         stdout);
      fputs (_("\
\n\
The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
The version control may be set with --backup or VERSION_CONTROL, values are:\n\
\n\
  t, numbered     make numbered backups\n\
  nil, existing   numbered if numbered backups exist, simple otherwise\n\
  never, simple   always make simple backups\n"),
         stdout);
      printf (_("\
\n\
GNU tar cannot read nor produce `--posix' archives.  If POSIXLY_CORRECT\n\
is set in the environment, GNU extensions are disallowed with `--posix'.\n\
Support for POSIX is only partially implemented, don't count on it yet.\n\
ARCHIVE may be FILE, HOST:FILE or USER@HOST:FILE; and FILE may be a file\n\
or a device.  *This* `tar' defaults to `-f%s -b%d'.\n"),
          DEFAULT_ARCHIVE, DEFAULT_BLOCKING);
      fputs (_("\
\n\
Report bugs to <tar-bugs@gnu.ai.mit.edu>.\n"),
           stdout);
    }
  exit (status);
}

/*----------------------------.
| Parse the options for tar.  |
`----------------------------*/

/* Available option letters are DEHIJQY and aejnqy.  Some are reserved:

   y  per-file gzip compression
   Y  per-block gzip compression */

#define OPTION_STRING \
  "-01234567ABC:F:GK:L:MN:OPRST:UV:WX:Zb:cdf:g:hiklmoprstuvwxz"

static void
set_subcommand_option (enum subcommand subcommand)
{
  if (subcommand_option != UNKNOWN_SUBCOMMAND
      && subcommand_option != subcommand)
    USAGE_ERROR ((0, 0,
          _("You may not specify more than one `-Acdtrux' option")));

  subcommand_option = subcommand;
}

static void
set_use_compress_program_option (const char *string)
{
  if (use_compress_program_option && strcmp (use_compress_program_option, string) != 0)
    USAGE_ERROR ((0, 0, _("Conflicting compression options")));

  use_compress_program_option = string;
}

static void
decode_options (int argc, char *const *argv)
{
  int optchar;          /* option letter */
  int input_files;      /* number of input files */
  const char *backup_suffix_string;
  const char *version_control_string;

  /* Set some default option values.  */

  subcommand_option = UNKNOWN_SUBCOMMAND;
  archive_format = DEFAULT_FORMAT;
  blocking_factor = DEFAULT_BLOCKING;
  record_size = DEFAULT_BLOCKING * BLOCKSIZE;

  owner_option = -1;
  group_option = -1;

  backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
  version_control_string = getenv ("VERSION_CONTROL");

  /* Convert old-style tar call by exploding option element and rearranging
     options accordingly.  */

  if (argc > 1 && argv[1][0] != '-')
    {
      int new_argc;     /* argc value for rearranged arguments */
      char **new_argv;      /* argv value for rearranged arguments */
      char *const *in;      /* cursor into original argv */
      char **out;       /* cursor into rearranged argv */
      const char *letter;   /* cursor into old option letters */
      char buffer[3];       /* constructed option buffer */
      const char *cursor;   /* cursor in OPTION_STRING */

      /* Initialize a constructed option.  */

      buffer[0] = '-';
      buffer[2] = '\0';

      /* Allocate a new argument array, and copy program name in it.  */

      new_argc = argc - 1 + strlen (argv[1]);
      new_argv = (char **) xmalloc (new_argc * sizeof (char *));
      in = argv;
      out = new_argv;
      *out++ = *in++;

      /* Copy each old letter option as a separate option, and have the
     corresponding argument moved next to it.  */

      for (letter = *in++; *letter; letter++)
    {
      buffer[1] = *letter;
      *out++ = xstrdup (buffer);
      cursor = strchr (OPTION_STRING, *letter);
      if (cursor && cursor[1] == ':')
        if (in < argv + argc)
          *out++ = *in++;
        else
          USAGE_ERROR ((0, 0, _("Old option `%c' requires an argument."),
                *letter));
    }

      /* Copy all remaining options.  */

      while (in < argv + argc)
    *out++ = *in++;

      /* Replace the old option list by the new one.  */

      argc = new_argc;
      argv = new_argv;
    }

  /* Parse all options and non-options as they appear.  */

  input_files = 0;

  while (optchar = getopt_long (argc, argv, OPTION_STRING, long_options, NULL),
     optchar != EOF)
    switch (optchar)
      {
      case '?':
    usage (TAREXIT_FAILURE);

      case 0:
    break;

      case 1:
    /* File name or non-parsed option, because of RETURN_IN_ORDER
       ordering triggerred by the leading dash in OPTION_STRING.  */

    name_add (optarg);
    input_files++;
    break;

      case 'A':
    set_subcommand_option (CAT_SUBCOMMAND);
    break;

      case OBSOLETE_BLOCK_COMPRESS:
    WARN ((0, 0, _("Obsolete option, now implied by --blocking-factor")));
    break;

      case OBSOLETE_BLOCKING_FACTOR:
    WARN ((0, 0, _("Obsolete option name replaced by --blocking-factor")));
    /* Fall through.  */

      case 'b':
    blocking_factor = intconv (optarg);
    record_size = blocking_factor * BLOCKSIZE;
    break;

      case OBSOLETE_READ_FULL_RECORDS:
    WARN ((0, 0,
           _("Obsolete option name replaced by --read-full-records")));
    /* Fall through.  */

      case 'B':
    /* Try to reblock input records.  For reading 4.2BSD pipes.  */

    /* It would surely make sense to exchange -B and -R, but it seems
       that -B has been used for a long while in Sun tar ans most
       BSD-derived systems.  This is a consequence of the block/record
       terminology confusion.  */

    read_full_records_option = 1;
    break;

      case 'c':
    set_subcommand_option (CREATE_SUBCOMMAND);
    break;

      case 'C':
    name_add ("-C");
    name_add (optarg);
    break;

      case 'd':
    set_subcommand_option (DIFF_SUBCOMMAND);
    break;

      case 'f':
    if (archive_names == allocated_archive_names)
      {
        allocated_archive_names *= 2;
        archive_name_array = (const char **)
          xrealloc (archive_name_array,
            sizeof (const char *) * allocated_archive_names);
      }
    archive_name_array[archive_names++] = optarg;
    break;

      case 'F':
    /* Since -F is only useful with -M, make it implied.  Run this
       script at the end of each tape.  */

    info_script_option = optarg;
    multi_volume_option = 1;
    break;

      case 'g':
    listed_incremental_option = optarg;
    /* Fall through.  */

      case 'G':
    /* We are making an incremental dump (FIXME: are we?); save
       directories at the beginning of the archive, and include in each
       directory its contents.  */

    incremental_option = 1;
    break;

      case 'h':
    /* Follow symbolic links.  */

    dereference_option = 1;
    break;

      case 'i':
    /* Ignore zero blocks (eofs).  This can't be the default,
       because Unix tar writes two blocks of zeros, then pads out
       the record with garbage.  */

    ignore_zeros_option = 1;
    break;

      case 'k':
    /* Don't overwrite existing files.  */

    keep_old_files_option = 1;
    break;

      case 'K':
    starting_file_option = 1;
    addname (optarg);
    break;

      case 'l':
    /* When dumping directories, don't dump files/subdirectories
       that are on other filesystems.  */

    one_file_system_option = 1;
    break;

      case 'L':
    clear_tarlong (tape_length_option);
    add_to_tarlong (tape_length_option, intconv (optarg));
    mult_tarlong (tape_length_option, 1024);
    multi_volume_option = 1;
    break;

      case OBSOLETE_TOUCH:
    WARN ((0, 0, _("Obsolete option name replaced by --touch")));
    /* Fall through.  */

      case 'm':
    touch_option = 1;
    break;

      case 'M':
    /* Make multivolume archive: when we can't write any more into
       the archive, re-open it, and continue writing.  */

    multi_volume_option = 1;
    break;

#if !MSDOS
      case 'N':
    after_date_option = 1;
    /* Fall through.  */

      case NEWER_MTIME_OPTION:
    if (newer_mtime_option)
      USAGE_ERROR ((0, 0, _("More than one threshold date")));

    newer_mtime_option = get_date (optarg, (voidstar) 0);
    if (newer_mtime_option == (time_t) -1)
      USAGE_ERROR ((0, 0, _("Invalid date format `%s'"), optarg));

    break;
#endif /* not MSDOS */

      case 'o':
    if (archive_format == DEFAULT_FORMAT)
      archive_format = V7_FORMAT;
    else if (archive_format != V7_FORMAT)
      USAGE_ERROR ((0, 0, _("Conflicting archive format options")));
    break;

      case 'O':
    to_stdout_option = 1;
    break;

      case 'p':
    same_permissions_option = 1;
    break;

      case OBSOLETE_ABSOLUTE_NAMES:
    WARN ((0, 0, _("Obsolete option name replaced by --absolute-names")));
    /* Fall through.  */

      case 'P':
    absolute_names_option = 1;
    break;

      case 'r':
    set_subcommand_option (APPEND_SUBCOMMAND);
    break;

      case OBSOLETE_BLOCK_NUMBER:
    WARN ((0, 0, _("Obsolete option name replaced by --block-number")));
    /* Fall through.  */

      case 'R':
    /* Print block numbers for debugging bad tar archives.  */

    /* It would surely make sense to exchange -B and -R, but it seems
       that -B has been used for a long while in Sun tar ans most
       BSD-derived systems.  This is a consequence of the block/record
       terminology confusion.  */

    block_number_option = 1;
    break;

      case 's':
    /* Names to extr are sorted.  */

    same_order_option = 1;
    break;

      case 'S':
    sparse_option = 1;
    break;

      case 't':
    set_subcommand_option (LIST_SUBCOMMAND);
    verbose_option++;
    break;

      case 'T':
    files_from_option = optarg;
    break;

      case 'u':
    set_subcommand_option (UPDATE_SUBCOMMAND);
    break;

      case 'U':
    unlink_first_option = 1;
    break;

      case 'v':
    verbose_option++;
    break;

      case 'V':
    volume_label_option = optarg;
    break;

      case 'w':
    interactive_option = 1;
    break;

      case 'W':
    verify_option = 1;
    break;

      case 'x':
    set_subcommand_option (EXTRACT_SUBCOMMAND);
    break;

      case 'X':
    exclude_option = 1;
    add_exclude_file (optarg);
    break;

      case 'z':
    set_use_compress_program_option ("gzip");
    break;

      case 'Z':
    set_use_compress_program_option ("compress");
    break;

      case OBSOLETE_VERSION_CONTROL:
    WARN ((0, 0, _("Obsolete option name replaced by --backup")));
    /* Fall through.  */

      case BACKUP_OPTION:
    backup_option = 1;
    if (optarg)
      version_control_string = optarg;
    break;

      case DELETE_OPTION:
    set_subcommand_option (DELETE_SUBCOMMAND);
    break;

      case EXCLUDE_OPTION:
    exclude_option = 1;
    add_exclude (optarg);
    break;

      case GROUP_OPTION:
    if (!gname_to_gid (optarg, &group_option))
      if (!check_decimal (optarg) >= 0)
        ERROR ((TAREXIT_FAILURE, 0, _("Invalid group given on option")));
      else
        group_option = check_decimal (optarg);
    break;

      case MODE_OPTION:
    mode_option
      = mode_compile (optarg,
              MODE_MASK_EQUALS | MODE_MASK_PLUS | MODE_MASK_MINUS);
    if (mode_option == MODE_INVALID)
      ERROR ((TAREXIT_FAILURE, 0, _("Invalid mode given on option")));
    if (mode_option == MODE_MEMORY_EXHAUSTED)
      ERROR ((TAREXIT_FAILURE, 0, _("Memory exhausted")));
    break;

      case NO_RECURSE_OPTION:
    no_recurse_option = 1;
    break;

      case NULL_OPTION:
    filename_terminator = '\0';
    break;

      case OWNER_OPTION:
    if (!uname_to_uid (optarg, &owner_option))
      if (!check_decimal (optarg) >= 0)
        ERROR ((TAREXIT_FAILURE, 0, _("Invalid owner given on option")));
      else
        owner_option = check_decimal (optarg);
    break;

      case POSIX_OPTION:
#if OLDGNU_COMPATIBILITY
    if (archive_format == DEFAULT_FORMAT)
      archive_format = GNU_FORMAT;
    else if (archive_format != GNU_FORMAT)
      USAGE_ERROR ((0, 0, _("Conflicting archive format options")));
#else
    if (archive_format == DEFAULT_FORMAT)
      archive_format = POSIX_FORMAT;
    else if (archive_format != POSIX_FORMAT)
      USAGE_ERROR ((0, 0, _("Conflicting archive format options")));
#endif
    break;

      case PRESERVE_OPTION:
    same_permissions_option = 1;
    same_order_option = 1;
    break;

      case RECORD_SIZE_OPTION:
    record_size = intconv (optarg);
    if (record_size % BLOCKSIZE != 0)
      USAGE_ERROR ((0, 0, _("Record size must be a multiple of %d."),
            BLOCKSIZE));
    blocking_factor = record_size / BLOCKSIZE;
    break;

      case RSH_COMMAND_OPTION:
    rsh_command_option = optarg;
    break;

      case SUFFIX_OPTION:
    backup_option = 1;
    backup_suffix_string = optarg;
    break;

      case VOLNO_FILE_OPTION:
    volno_file_option = optarg;
    break;

      case USE_COMPRESS_PROGRAM_OPTION:
    set_use_compress_program_option (optarg);
    break;

      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':

#ifdef DEVICE_PREFIX
    {
      int device = optchar - '0';
      int density;
      static char buf[sizeof DEVICE_PREFIX + 10];
      char *cursor;

      density = getopt_long (argc, argv, "lmh", NULL, NULL);
      strcpy (buf, DEVICE_PREFIX);
      cursor = buf + strlen (buf);

#ifdef DENSITY_LETTER

      sprintf (cursor, "%d%c", device, density);

#else /* not DENSITY_LETTER */

      switch (density)
        {
        case 'l':
#ifdef LOW_NUM
          device += LOW_NUM;
#endif
          break;

        case 'm':
#ifdef MID_NUM
          device += MID_NUM;
#else
          device += 8;
#endif
          break;

        case 'h':
#ifdef HGH_NUM
          device += HGH_NUM;
#else
          device += 16;
#endif
          break;

        default:
          usage (TAREXIT_FAILURE);
        }
      sprintf (cursor, "%d", device);

#endif /* not DENSITY_LETTER */

      if (archive_names == allocated_archive_names)
        {
          allocated_archive_names *= 2;
          archive_name_array = (const char **)
        xrealloc (archive_name_array,
              sizeof (const char *) * allocated_archive_names);
        }
      archive_name_array[archive_names++] = buf;

      /* FIXME: How comes this works for many archives when buf is
         not xstrdup'ed?  */
    }
    break;

#else /* not DEVICE_PREFIX */

    USAGE_ERROR ((0, 0,
              _("Options `-[0-7][lmh]' not supported by *this* tar")));

#endif /* not DEVICE_PREFIX */
      }

  /* Process trivial options.  */

  if (show_version)
    {
      printf ("tar (GNU %s) %s\n", PACKAGE, VERSION);
      fputs (_("\
\n\
Copyright (C) 1988, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc.\n"),
         stdout);
      fputs (_("\
This is free software; see the source for copying conditions.  There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"),
         stdout);
      fputs (_("\
\n\
Written by John Gilmore and Jay Fenlason.\n"),
         stdout);
      exit (TAREXIT_SUCCESS);
    }

  if (show_help)
    usage (TAREXIT_SUCCESS);

  /* Derive option values and check option consistency.  */

  if (archive_format == DEFAULT_FORMAT)
    {
#if OLDGNU_COMPATIBILITY
      archive_format = OLDGNU_FORMAT;
#else
      archive_format = GNU_FORMAT;
#endif
    }

  if (archive_format == GNU_FORMAT && getenv ("POSIXLY_CORRECT"))
    archive_format = POSIX_FORMAT;

  if ((volume_label_option != NULL
       || incremental_option || multi_volume_option || sparse_option)
      && archive_format != OLDGNU_FORMAT && archive_format != GNU_FORMAT)
    USAGE_ERROR ((0, 0,
          _("GNU features wanted on incompatible archive format")));

  if (archive_names == 0)
    {
      /* If no archive file name given, try TAPE from the environment, or
     else, DEFAULT_ARCHIVE from the configuration process.  */

      archive_names = 1;
      archive_name_array[0] = getenv ("TAPE");
      if (archive_name_array[0] == NULL)
    archive_name_array[0] = DEFAULT_ARCHIVE;
    }

  /* Allow multiple archives only with `-M'.  */

  if (archive_names > 1 && !multi_volume_option)
    USAGE_ERROR ((0, 0,
          _("Multiple archive files requires `-M' option")));

  /* If ready to unlink hierarchies, so we are for simpler files.  */
  if (recursive_unlink_option)
    unlink_first_option = 1;

  /* Forbid using -c with no input files whatsoever.  Check that `-f -',
     explicit or implied, is used correctly.  */

  switch (subcommand_option)
    {
    case CREATE_SUBCOMMAND:
      if (input_files == 0 && !files_from_option)
    USAGE_ERROR ((0, 0,
              _("Cowardly refusing to create an empty archive")));
      break;

    case EXTRACT_SUBCOMMAND:
    case LIST_SUBCOMMAND:
    case DIFF_SUBCOMMAND:
      for (archive_name_cursor = archive_name_array;
       archive_name_cursor < archive_name_array + archive_names;
       archive_name_cursor++)
    if (!strcmp (*archive_name_cursor, "-"))
      request_stdin ("-f");
      break;

    case CAT_SUBCOMMAND:
    case UPDATE_SUBCOMMAND:
    case APPEND_SUBCOMMAND:
      for (archive_name_cursor = archive_name_array;
       archive_name_cursor < archive_name_array + archive_names;
       archive_name_cursor++)
    if (!strcmp (*archive_name_cursor, "-"))
      USAGE_ERROR ((0, 0,
            _("Options `-Aru' are incompatible with `-f -'")));

    default:
      break;
    }

  archive_name_cursor = archive_name_array;

  /* Prepare for generating backup names.  */

  if (backup_suffix_string)
    simple_backup_suffix = xstrdup (backup_suffix_string);

  if (backup_option)
    backup_type = get_version (version_control_string);
}


/* Tar proper.  */

/*-----------------------.
| Main routine for tar.  |
`-----------------------*/

int
main (int argc, char *const *argv)
{
  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  exit_status = TAREXIT_SUCCESS;
  filename_terminator = '\n';

  /* Pre-allocate a few structures.  */

  allocated_archive_names = 10;
  archive_name_array = (const char **)
    xmalloc (sizeof (const char *) * allocated_archive_names);
  archive_names = 0;

  init_names ();

  /* Decode options.  */

  decode_options (argc, argv);
  name_init (argc, argv);

  /* Main command execution.  */

  if (volno_file_option)
    init_volume_number ();

  switch (subcommand_option)
    {
    case UNKNOWN_SUBCOMMAND:
      USAGE_ERROR ((0, 0,
            _("You must specify one of the `-Acdtrux' options")));

    case CAT_SUBCOMMAND:
    case UPDATE_SUBCOMMAND:
    case APPEND_SUBCOMMAND:
      update_archive ();
      break;

    case DELETE_SUBCOMMAND:
      delete_archive_members ();
      break;

    case CREATE_SUBCOMMAND:
      if (totals_option)
    init_total_written ();

      create_archive ();
      name_close ();

      if (totals_option)
    print_total_written ();
      break;

    case EXTRACT_SUBCOMMAND:
      extr_init ();
      read_and (extract_archive);
      break;

    case LIST_SUBCOMMAND:
      read_and (list_archive);
      break;

    case DIFF_SUBCOMMAND:
      diff_init ();
      read_and (diff_archive);
      break;
    }

  if (volno_file_option)
    closeout_volume_number ();

  /* Dispose of allocated memory, and return.  */

  free (archive_name_array);
  name_term ();

  if (exit_status == TAREXIT_FAILURE)
    error (0, 0, _("Error exit delayed from previous errors"));
  exit (exit_status);
}

https://git.savannah.gnu.org/cgit/tar.git/tree//lib/getopt.h?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/lib/getopt.h used in: Step 661
/* Declarations for getopt.
   Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.

   This file is part of the GNU C Library.  Its master source is NOT part of
   the C library, however.  The master source lives in /gd/gnu/lib.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#ifndef _GETOPT_H
#define _GETOPT_H 1

#ifdef  __cplusplus
extern "C" {
#endif

/* For communication from `getopt' to the caller.
   When `getopt' finds an option that takes an argument,
   the argument value is returned here.
   Also, when `ordering' is RETURN_IN_ORDER,
   each non-option ARGV-element is returned here.  */

extern char *optarg;

/* Index in ARGV of the next element to be scanned.
   This is used for communication to and from the caller
   and for communication between successive calls to `getopt'.

   On entry to `getopt', zero means this is the first call; initialize.

   When `getopt' returns -1, this is the index of the first of the
   non-option elements that the caller should itself scan.

   Otherwise, `optind' communicates from one call to the next
   how much of ARGV has been scanned so far.  */

extern int optind;

/* Callers store zero here to inhibit the error message `getopt' prints
   for unrecognized options.  */

extern int opterr;

/* Set to an option character which was unrecognized.  */

extern int optopt;

/* Describe the long-named options requested by the application.
   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
   of `struct option' terminated by an element containing a name which is
   zero.

   The field `has_arg' is:
   no_argument      (or 0) if the option does not take an argument,
   required_argument    (or 1) if the option requires an argument,
   optional_argument    (or 2) if the option takes an optional argument.

   If the field `flag' is not NULL, it points to a variable that is set
   to the value given in the field `val' when the option is found, but
   left unchanged if the option is not found.

   To have a long-named option do something other than set an `int' to
   a compiled-in constant, such as set a value from `optarg', set the
   option's `flag' field to zero and its `val' field to a nonzero
   value (the equivalent single-letter option character, if there is
   one).  For long options that have a zero `flag' field, `getopt'
   returns the contents of the `val' field.  */

struct option
{
#if defined (__STDC__) && __STDC__
  const char *name;
#else
  char *name;
#endif
  /* has_arg can't be an enum because some compilers complain about
     type mismatches in all the code that assumes it is an int.  */
  int has_arg;
  int *flag;
  int val;
};

/* Names for the values of the `has_arg' field of `struct option'.  */

#define no_argument     0
#define required_argument   1
#define optional_argument   2

#if defined (__STDC__) && __STDC__
#ifdef __GNU_LIBRARY__
/* Many other libraries have conflicting prototypes for getopt, with
   differences in the consts, in stdlib.h.  To avoid compilation
   errors, only prototype getopt for the GNU C library.  */
extern int getopt (int argc, char *const *argv, const char *shortopts);
#else /* not __GNU_LIBRARY__ */
extern int getopt ();
#endif /* __GNU_LIBRARY__ */
extern int getopt_long (int argc, char *const *argv, const char *shortopts,
                const struct option *longopts, int *longind);
extern int getopt_long_only (int argc, char *const *argv,
                 const char *shortopts,
                     const struct option *longopts, int *longind);

/* Internal only.  Users should not call this directly.  */
extern int _getopt_internal (int argc, char *const *argv,
                 const char *shortopts,
                     const struct option *longopts, int *longind,
                 int long_only);
#else /* not __STDC__ */
extern int getopt ();
extern int getopt_long ();
extern int getopt_long_only ();

extern int _getopt_internal ();
#endif /* __STDC__ */

#ifdef  __cplusplus
}
#endif

#endif /* _GETOPT_H */

https://git.savannah.gnu.org/cgit/tar.git/tree//src/update.c?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e

File: ../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12/src/update.c used in: Step 662
/* Update a tar archive.
   Copyright (C) 1988, 1992, 1994, 1996, 1997 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 Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Implement the 'r', 'u' and 'A' options for tar.  'A' means that the
   file names are tar files, and they should simply be appended to the end
   of the archive.  No attempt is made to record the reads from the args; if
   they're on raw tape or something like that, it'll probably lose...  */

#include "system.h"
#include "common.h"

/* FIXME: This module should not directly handle the following variable,
   instead, this should be done in buffer.c only.  */
extern union block *current_block;

/* We've hit the end of the old stuff, and its time to start writing new
   stuff to the tape.  This involves seeking back one record and
   re-writing the current record (which has been changed).  */
int time_to_start_writing = 0;

/* Pointer to where we started to write in the first record we write out.
   This is used if we can't backspace the output and have to null out the
   first part of the record.  */
char *output_start;

/*------------------------------------------------------------------------.
| Catenate file PATH to the archive without creating a header for it.  It |
| had better be a tar file or the archive is screwed.             |
`------------------------------------------------------------------------*/

static void
append_file (char *path)
{
  int handle;
  struct stat stat_data;
  long bytes_left;

  if (stat (path, &stat_data) != 0
      || (handle = open (path, O_RDONLY | O_BINARY), handle < 0))
    {
      ERROR ((0, errno, _("Cannot open file %s"), path));
      return;
    }

  bytes_left = stat_data.st_size;

  while (bytes_left > 0)
    {
      union block *start = find_next_block ();
      long buffer_size = available_space_after (start);
      int status;

      if (bytes_left < buffer_size)
    {
      buffer_size = bytes_left;
      status = buffer_size % BLOCKSIZE;
      if (status)
        memset (start->buffer + bytes_left, 0,
            (size_t) (BLOCKSIZE - status));
    }

      status = read (handle, start->buffer, (size_t) buffer_size);
      if (status < 0)
    FATAL_ERROR ((0, errno,
        _("Read error at byte %ld reading %d bytes in file %s"),
        stat_data.st_size - bytes_left, buffer_size, path));
      bytes_left -= status;

      set_next_block_after (start + (status - 1) / BLOCKSIZE);

      if (status != buffer_size)
    FATAL_ERROR ((0, 0, _("%s: File shrunk by %d bytes, (yark!)"),
              path, bytes_left));
    }

  close (handle);
}

/*-----------------------------------------------------------------------.
| Implement the 'r' (add files to end of archive), and 'u' (add files to |
| end of archive if they arent there, or are more up to date than the    |
| version in the archive.) commands.                     |
`-----------------------------------------------------------------------*/

void
update_archive (void)
{
  enum read_header previous_status = HEADER_STILL_UNREAD;
  int found_end = 0;

  name_gather ();
  if (subcommand_option == UPDATE_SUBCOMMAND)
    name_expand ();
  open_archive (ACCESS_UPDATE);

  while (!found_end)
    {
      enum read_header status = read_header ();

      switch (status)
    {
    case HEADER_STILL_UNREAD:
      abort ();

    case HEADER_SUCCESS:
      {
        struct name *name;

        if (subcommand_option == UPDATE_SUBCOMMAND
        && (name = name_scan (current_file_name), name))
          {
        struct stat stat_data;
        enum archive_format unused;

        decode_header (current_header, &current_stat, &unused, 0);
        if (stat (current_file_name, &stat_data) < 0)
          ERROR ((0, errno, _("Cannot stat %s"), current_file_name));
        else if (current_stat.st_mtime >= stat_data.st_mtime)
          name->found = 1;
          }
        set_next_block_after (current_header);
        if (current_header->oldgnu_header.isextended)
          skip_extended_headers ();
        skip_file ((long) current_stat.st_size);
        break;
      }

    case HEADER_ZERO_BLOCK:
      current_block = current_header;
      found_end = 1;
      break;

    case HEADER_END_OF_FILE:
      found_end = 1;
      break;

    case HEADER_FAILURE:
      set_next_block_after (current_header);
      switch (previous_status)
        {
        case HEADER_STILL_UNREAD:
          WARN ((0, 0, _("This does not look like a tar archive")));
          /* Fall through.  */

        case HEADER_SUCCESS:
        case HEADER_ZERO_BLOCK:
          ERROR ((0, 0, _("Skipping to next header")));
          /* Fall through.  */

        case HEADER_FAILURE:
          break;

        case HEADER_END_OF_FILE:
          abort ();
        }
      break;
    }

      previous_status = status;
    }

  reset_eof ();
  time_to_start_writing = 1;
  output_start = current_block->buffer;

  {
    char *path;

    while (path = name_from_list (), path)
      {
    if (interactive_option && !confirm ("add", path))
      continue;
    if (subcommand_option == CAT_SUBCOMMAND)
      append_file (path);
    else
      dump_file (path, -1, 1);
      }
  }

  write_eot ();
  close_archive ();
  names_notfound ();
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/tar-1.12/tar-1.12.checksums

File: ../git/live-bootstrap/sysa/tar-1.12/tar-1.12.checksums used in: Step 667
2186b01ab8d953222f15cab0d257f80215f4a17c53b30a5a7467b80e49c2cc34  /usr/bin/tar

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/sed-4.0.9/sources

File: ../git/live-bootstrap/sysa/sed-4.0.9/sources used in: Step 669
https://mirrors.kernel.org/gnu/sed/sed-4.0.9.tar.gz c365874794187f8444e5d22998cd5888ffa47f36def4b77517a808dec27c0600

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/sed-4.0.9/mk/main.mk

File: ../git/live-bootstrap/sysa/sed-4.0.9/mk/main.mk used in: Step 675
# SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
# SPDX-FileCopyrightText: 2022 fosslinux <fosslinux@aussies.space>
#
# SPDX-License-Identifier: GPL-3.0-or-later

CC = tcc
AR = tcc -ar

CPPFLAGS = -DENABLE_NLS=0 \
         -DHAVE_FCNTL_H \
         -DHAVE_ALLOCA_H \
         -DSED_FEATURE_VERSION=\"4.0\" \
         -DVERSION=\"4.0.9\" \
         -DPACKAGE=\"sed\"
CFLAGS = -I . -I lib
LDFLAGS = -L . -lsed -static

.PHONY: all

ifeq ($(LIBC),mes)
    LIB_SRC = getline
else
    LIB_SRC = alloca
endif

LIB_SRC += getopt1 getopt utils regex obstack strverscmp mkstemp

LIB_OBJ = $(addprefix lib/, $(addsuffix .o, $(LIB_SRC)))

SED_SRC = compile execute regexp fmt sed
SED_OBJ = $(addprefix sed/, $(addsuffix .o, $(SED_SRC)))

all: sed/sed

lib/regex.h: lib/regex_.h
    cp $< $@

lib/regex.o: lib/regex.h

libsed.a: $(LIB_OBJ)
    $(AR) cr $@ $^

sed/sed: libsed.a $(SED_OBJ)
    $(CC) $^ $(LDFLAGS) -o $@

install:
    install -D sed/sed $(DESTDIR)$(PREFIX)/bin/sed

https://git.savannah.gnu.org/cgit/sed.git/tree/lib/alloca.c?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/alloca.c used in: Step 678
/* alloca.c -- allocate automatically reclaimed memory
   (Mostly) portable public-domain implementation -- D A Gwyn

   This implementation of the PWB library alloca function,
   which is used to allocate space off the run-time stack so
   that it is automatically reclaimed upon procedure exit,
   was inspired by discussions with J. Q. Johnson of Cornell.
   J.Otto Tennant <jot@cray.com> contributed the Cray support.

   There are some preprocessor constants that can
   be defined when compiling for your specific system, for
   improved efficiency; however, the defaults should be okay.

   The general concept of this implementation is to keep
   track of all alloca-allocated blocks, and reclaim any
   that are found to be deeper in the stack than the current
   invocation.  This heuristic does not reclaim storage as
   soon as it becomes invalid, but it will do so eventually.

   As a special case, alloca(0) reclaims storage without
   allocating any.  It is a good idea to use alloca(0) in
   your main control loop, etc. to force garbage collection.  */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif

#ifdef emacs
#include "blockinput.h"
#endif

/* If compiling with GCC 2, this file's not needed.  */
#if !defined (__GNUC__) || __GNUC__ < 2

/* If someone has defined alloca as a macro,
   there must be some other way alloca is supposed to work.  */
#ifndef alloca

#ifdef emacs
#ifdef static
/* actually, only want this if static is defined as ""
   -- this is for usg, in which emacs must undefine static
   in order to make unexec workable
   */
#ifndef STACK_DIRECTION
you
lose
-- must know STACK_DIRECTION at compile-time
#endif /* STACK_DIRECTION undefined */
#endif /* static */
#endif /* emacs */

/* If your stack is a linked list of frames, you have to
   provide an "address metric" ADDRESS_FUNCTION macro.  */

#if defined (CRAY) && defined (CRAY_STACKSEG_END)
long i00afunc ();
#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
#else
#define ADDRESS_FUNCTION(arg) &(arg)
#endif

#if __STDC__
typedef void *pointer;
#else
typedef char *pointer;
#endif

#ifndef NULL
#define NULL    0
#endif

/* Different portions of Emacs need to call different versions of
   malloc.  The Emacs executable needs alloca to call xmalloc, because
   ordinary malloc isn't protected from input signals.  On the other
   hand, the utilities in lib-src need alloca to call malloc; some of
   them are very simple, and don't have an xmalloc routine.

   Non-Emacs programs expect this to call xmalloc.

   Callers below should use malloc.  */

#ifndef emacs
#define malloc xmalloc
#endif
extern pointer malloc ();

/* Define STACK_DIRECTION if you know the direction of stack
   growth for your system; otherwise it will be automatically
   deduced at run-time.

   STACK_DIRECTION > 0 => grows toward higher addresses
   STACK_DIRECTION < 0 => grows toward lower addresses
   STACK_DIRECTION = 0 => direction of growth unknown  */

#ifndef STACK_DIRECTION
#define STACK_DIRECTION 0   /* Direction unknown.  */
#endif

#if STACK_DIRECTION != 0

#define STACK_DIR   STACK_DIRECTION /* Known at compile-time.  */

#else /* STACK_DIRECTION == 0; need run-time code.  */

static int stack_dir;       /* 1 or -1 once known.  */
#define STACK_DIR   stack_dir

static void
find_stack_direction ()
{
  static char *addr = NULL; /* Address of first `dummy', once known.  */
  auto char dummy;      /* To get stack address.  */

  if (addr == NULL)
    {               /* Initial entry.  */
      addr = ADDRESS_FUNCTION (dummy);

      find_stack_direction ();  /* Recurse once.  */
    }
  else
    {
      /* Second entry.  */
      if (ADDRESS_FUNCTION (dummy) > addr)
    stack_dir = 1;      /* Stack grew upward.  */
      else
    stack_dir = -1;     /* Stack grew downward.  */
    }
}

#endif /* STACK_DIRECTION == 0 */

/* An "alloca header" is used to:
   (a) chain together all alloca'ed blocks;
   (b) keep track of stack depth.

   It is very important that sizeof(header) agree with malloc
   alignment chunk size.  The following default should work okay.  */

#ifndef ALIGN_SIZE
#define ALIGN_SIZE  sizeof(double)
#endif

typedef union hdr
{
  char align[ALIGN_SIZE];   /* To force sizeof(header).  */
  struct
    {
      union hdr *next;      /* For chaining headers.  */
      char *deep;       /* For stack depth measure.  */
    } h;
} header;

static header *last_alloca_header = NULL;   /* -> last alloca header.  */

/* Return a pointer to at least SIZE bytes of storage,
   which will be automatically reclaimed upon exit from
   the procedure that called alloca.  Originally, this space
   was supposed to be taken from the current stack frame of the
   caller, but that method cannot be made to work for some
   implementations of C, for example under Gould's UTX/32.  */

pointer
alloca (size)
     unsigned size;
{
  auto char probe;      /* Probes stack depth: */
  register char *depth = ADDRESS_FUNCTION (probe);

#if STACK_DIRECTION == 0
  if (STACK_DIR == 0)       /* Unknown growth direction.  */
    find_stack_direction ();
#endif

  /* Reclaim garbage, defined as all alloca'd storage that
     was allocated from deeper in the stack than currently.  */

  {
    register header *hp;    /* Traverses linked list.  */

#ifdef emacs
    BLOCK_INPUT;
#endif

    for (hp = last_alloca_header; hp != NULL;)
      if ((STACK_DIR > 0 && hp->h.deep > depth)
      || (STACK_DIR < 0 && hp->h.deep < depth))
    {
      register header *np = hp->h.next;

      free ((pointer) hp);  /* Collect garbage.  */

      hp = np;      /* -> next header.  */
    }
      else
    break;          /* Rest are not deeper.  */

    last_alloca_header = hp;    /* -> last valid storage.  */

#ifdef emacs
    UNBLOCK_INPUT;
#endif
  }

  if (size == 0)
    return NULL;        /* No allocation required.  */

  /* Allocate combined header + user data storage.  */

  {
    register pointer new = malloc (sizeof (header) + size);
    /* Address of header.  */

    if (new == 0)
      abort();

    ((header *) new)->h.next = last_alloca_header;
    ((header *) new)->h.deep = depth;

    last_alloca_header = (header *) new;

    /* User storage begins just after header.  */

    return (pointer) ((char *) new + sizeof (header));
  }
}

#if defined (CRAY) && defined (CRAY_STACKSEG_END)

#ifdef DEBUG_I00AFUNC
#include <stdio.h>
#endif

#ifndef CRAY_STACK
#define CRAY_STACK
#ifndef CRAY2
/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
struct stack_control_header
  {
    long shgrow:32;     /* Number of times stack has grown.  */
    long shaseg:32;     /* Size of increments to stack.  */
    long shhwm:32;      /* High water mark of stack.  */
    long shsize:32;     /* Current size of stack (all segments).  */
  };

/* The stack segment linkage control information occurs at
   the high-address end of a stack segment.  (The stack
   grows from low addresses to high addresses.)  The initial
   part of the stack segment linkage control information is
   0200 (octal) words.  This provides for register storage
   for the routine which overflows the stack.  */

struct stack_segment_linkage
  {
    long ss[0200];      /* 0200 overflow words.  */
    long sssize:32;     /* Number of words in this segment.  */
    long ssbase:32;     /* Offset to stack base.  */
    long:32;
    long sspseg:32;     /* Offset to linkage control of previous
                   segment of stack.  */
    long:32;
    long sstcpt:32;     /* Pointer to task common address block.  */
    long sscsnm;        /* Private control structure number for
                   microtasking.  */
    long ssusr1;        /* Reserved for user.  */
    long ssusr2;        /* Reserved for user.  */
    long sstpid;        /* Process ID for pid based multi-tasking.  */
    long ssgvup;        /* Pointer to multitasking thread giveup.  */
    long sscray[7];     /* Reserved for Cray Research.  */
    long ssa0;
    long ssa1;
    long ssa2;
    long ssa3;
    long ssa4;
    long ssa5;
    long ssa6;
    long ssa7;
    long sss0;
    long sss1;
    long sss2;
    long sss3;
    long sss4;
    long sss5;
    long sss6;
    long sss7;
  };

#else /* CRAY2 */
/* The following structure defines the vector of words
   returned by the STKSTAT library routine.  */
struct stk_stat
  {
    long now;           /* Current total stack size.  */
    long maxc;          /* Amount of contiguous space which would
                   be required to satisfy the maximum
                   stack demand to date.  */
    long high_water;        /* Stack high-water mark.  */
    long overflows;     /* Number of stack overflow ($STKOFEN) calls.  */
    long hits;          /* Number of internal buffer hits.  */
    long extends;       /* Number of block extensions.  */
    long stko_mallocs;      /* Block allocations by $STKOFEN.  */
    long underflows;        /* Number of stack underflow calls ($STKRETN).  */
    long stko_free;     /* Number of deallocations by $STKRETN.  */
    long stkm_free;     /* Number of deallocations by $STKMRET.  */
    long segments;      /* Current number of stack segments.  */
    long maxs;          /* Maximum number of stack segments so far.  */
    long pad_size;      /* Stack pad size.  */
    long current_address;   /* Current stack segment address.  */
    long current_size;      /* Current stack segment size.  This
                   number is actually corrupted by STKSTAT to
                   include the fifteen word trailer area.  */
    long initial_address;   /* Address of initial segment.  */
    long initial_size;      /* Size of initial segment.  */
  };

/* The following structure describes the data structure which trails
   any stack segment.  I think that the description in 'asdef' is
   out of date.  I only describe the parts that I am sure about.  */

struct stk_trailer
  {
    long this_address;      /* Address of this block.  */
    long this_size;     /* Size of this block (does not include
                   this trailer).  */
    long unknown2;
    long unknown3;
    long link;          /* Address of trailer block of previous
                   segment.  */
    long unknown5;
    long unknown6;
    long unknown7;
    long unknown8;
    long unknown9;
    long unknown10;
    long unknown11;
    long unknown12;
    long unknown13;
    long unknown14;
  };

#endif /* CRAY2 */
#endif /* not CRAY_STACK */

#ifdef CRAY2
/* Determine a "stack measure" for an arbitrary ADDRESS.
   I doubt that "lint" will like this much.  */

static long
i00afunc (long *address)
{
  struct stk_stat status;
  struct stk_trailer *trailer;
  long *block, size;
  long result = 0;

  /* We want to iterate through all of the segments.  The first
     step is to get the stack status structure.  We could do this
     more quickly and more directly, perhaps, by referencing the
     $LM00 common block, but I know that this works.  */

  STKSTAT (&status);

  /* Set up the iteration.  */

  trailer = (struct stk_trailer *) (status.current_address
                    + status.current_size
                    - 15);

  /* There must be at least one stack segment.  Therefore it is
     a fatal error if "trailer" is null.  */

  if (trailer == 0)
    abort ();

  /* Discard segments that do not contain our argument address.  */

  while (trailer != 0)
    {
      block = (long *) trailer->this_address;
      size = trailer->this_size;
      if (block == 0 || size == 0)
    abort ();
      trailer = (struct stk_trailer *) trailer->link;
      if ((block <= address) && (address < (block + size)))
    break;
    }

  /* Set the result to the offset in this segment and add the sizes
     of all predecessor segments.  */

  result = address - block;

  if (trailer == 0)
    {
      return result;
    }

  do
    {
      if (trailer->this_size <= 0)
    abort ();
      result += trailer->this_size;
      trailer = (struct stk_trailer *) trailer->link;
    }
  while (trailer != 0);

  /* We are done.  Note that if you present a bogus address (one
     not in any segment), you will get a different number back, formed
     from subtracting the address of the first block.  This is probably
     not what you want.  */

  return (result);
}

#else /* not CRAY2 */
/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
   Determine the number of the cell within the stack,
   given the address of the cell.  The purpose of this
   routine is to linearize, in some sense, stack addresses
   for alloca.  */

static long
i00afunc (long address)
{
  long stkl = 0;

  long size, pseg, this_segment, stack;
  long result = 0;

  struct stack_segment_linkage *ssptr;

  /* Register B67 contains the address of the end of the
     current stack segment.  If you (as a subprogram) store
     your registers on the stack and find that you are past
     the contents of B67, you have overflowed the segment.

     B67 also points to the stack segment linkage control
     area, which is what we are really interested in.  */

  stkl = CRAY_STACKSEG_END ();
  ssptr = (struct stack_segment_linkage *) stkl;

  /* If one subtracts 'size' from the end of the segment,
     one has the address of the first word of the segment.

     If this is not the first segment, 'pseg' will be
     nonzero.  */

  pseg = ssptr->sspseg;
  size = ssptr->sssize;

  this_segment = stkl - size;

  /* It is possible that calling this routine itself caused
     a stack overflow.  Discard stack segments which do not
     contain the target address.  */

  while (!(this_segment <= address && address <= stkl))
    {
#ifdef DEBUG_I00AFUNC
      fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl);
#endif
      if (pseg == 0)
    break;
      stkl = stkl - pseg;
      ssptr = (struct stack_segment_linkage *) stkl;
      size = ssptr->sssize;
      pseg = ssptr->sspseg;
      this_segment = stkl - size;
    }

  result = address - this_segment;

  /* If you subtract pseg from the current end of the stack,
     you get the address of the previous stack segment's end.
     This seems a little convoluted to me, but I'll bet you save
     a cycle somewhere.  */

  while (pseg != 0)
    {
#ifdef DEBUG_I00AFUNC
      fprintf (stderr, "%011o %011o\n", pseg, size);
#endif
      stkl = stkl - pseg;
      ssptr = (struct stack_segment_linkage *) stkl;
      size = ssptr->sssize;
      pseg = ssptr->sspseg;
      result += size;
    }
  return (result);
}

#endif /* not CRAY2 */
#endif /* CRAY */

#endif /* no alloca */
#endif /* not GCC version 2 */

https://git.savannah.gnu.org/cgit/sed.git/tree/lib/getopt1.c?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/getopt1.c used in: Step 679
/* getopt_long and getopt_long_only entry points for GNU getopt.
   Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
     Free Software Foundation, Inc.

   NOTE: The canonical source of this file is maintained with the GNU C Library.
   Bugs can be reported to bug-glibc@gnu.org.

   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.  */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "getopt.h"

#if !defined __STDC__ || !__STDC__
/* This is a separate conditional since some stdc systems
   reject `defined (const)'.  */
#ifndef const
#define const
#endif
#endif

#include <stdio.h>

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#define GETOPT_INTERFACE_VERSION 2
#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
#include <gnu-versions.h>
#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
#define ELIDE_CODE
#endif
#endif

#ifndef ELIDE_CODE


/* This needs to come after some library #include
   to get __GNU_LIBRARY__ defined.  */
#ifdef __GNU_LIBRARY__
#include <stdlib.h>
#endif

#ifndef NULL
#define NULL 0
#endif

int
getopt_long (argc, argv, options, long_options, opt_index)
     int argc;
     char *const *argv;
     const char *options;
     const struct option *long_options;
     int *opt_index;
{
  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
}

/* Like getopt_long, but '-' as well as '--' can indicate a long option.
   If an option that starts with '-' (not '--') doesn't match a long option,
   but does match a short option, it is parsed as a short option
   instead.  */

int
getopt_long_only (argc, argv, options, long_options, opt_index)
     int argc;
     char *const *argv;
     const char *options;
     const struct option *long_options;
     int *opt_index;
{
  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
}


#endif  /* Not ELIDE_CODE.  */


#ifdef TEST

#include <stdio.h>

int
main (argc, argv)
     int argc;
     char **argv;
{
  int c;
  int digit_optind = 0;

  while (1)
    {
      int this_option_optind = optind ? optind : 1;
      int option_index = 0;
      static struct option long_options[] =
      {
    {"add", 1, 0, 0},
    {"append", 0, 0, 0},
    {"delete", 1, 0, 0},
    {"verbose", 0, 0, 0},
    {"create", 0, 0, 0},
    {"file", 1, 0, 0},
    {0, 0, 0, 0}
      };

      c = getopt_long (argc, argv, "abc:d:0123456789",
               long_options, &option_index);
      if (c == -1)
    break;

      switch (c)
    {
    case 0:
      printf ("option %s", long_options[option_index].name);
      if (optarg)
        printf (" with arg %s", optarg);
      printf ("\n");
      break;

    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (digit_optind != 0 && digit_optind != this_option_optind)
        printf ("digits occur in two different argv-elements.\n");
      digit_optind = this_option_optind;
      printf ("option %c\n", c);
      break;

    case 'a':
      printf ("option a\n");
      break;

    case 'b':
      printf ("option b\n");
      break;

    case 'c':
      printf ("option c with value `%s'\n", optarg);
      break;

    case 'd':
      printf ("option d with value `%s'\n", optarg);
      break;

    case '?':
      break;

    default:
      printf ("?? getopt returned character code 0%o ??\n", c);
    }
    }

  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
    printf ("%s ", argv[optind++]);
      printf ("\n");
    }

  exit (0);
}

#endif /* TEST */

https://git.savannah.gnu.org/cgit/sed.git/tree/lib/getopt.c?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/getopt.c used in: Step 680
/* Getopt for GNU.
   NOTE: getopt is now part of the C library, so if you don't know what
   "Keep this file name-space clean" means, talk to drepper@gnu.org
   before changing it!

   Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98
    Free Software Foundation, Inc.

   NOTE: The canonical source of this file is maintained with the GNU C Library.
   Bugs can be reported to bug-glibc@gnu.org.

   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.  */


/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
   Ditto for AIX 3.2 and <stdlib.h>.  */
#ifndef _NO_PROTO
# define _NO_PROTO
#endif

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#if !defined __STDC__ || !__STDC__
/* This is a separate conditional since some stdc systems
   reject `defined (const)'.  */
# ifndef const
#  define const
# endif
#endif

#include <stdio.h>

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#define GETOPT_INTERFACE_VERSION 2
#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
# include <gnu-versions.h>
# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
#  define ELIDE_CODE
# endif
#endif

#ifndef ELIDE_CODE


/* This needs to come after some library #include
   to get __GNU_LIBRARY__ defined.  */
#ifdef  __GNU_LIBRARY__
/* Don't include stdlib.h for non-GNU C libraries because some of them
   contain conflicting prototypes for getopt.  */
# include <stdlib.h>
# include <unistd.h>
#endif  /* GNU C library.  */

#ifdef VMS
# include <unixlib.h>
# if HAVE_STRING_H - 0
#  include <string.h>
# endif
#endif

#ifndef _
/* This is for other GNU distributions with internationalized messages.
   When compiling libc, the _ macro is predefined.  */
# ifdef HAVE_LIBINTL_H
#  include <libintl.h>
#  define _(msgid)  gettext (msgid)
# else
#  define _(msgid)  (msgid)
# endif
#endif

/* This version of `getopt' appears to the caller like standard Unix `getopt'
   but it behaves differently for the user, since it allows the user
   to intersperse the options with the other arguments.

   As `getopt' works, it permutes the elements of ARGV so that,
   when it is done, all the options precede everything else.  Thus
   all application programs are extended to handle flexible argument order.

   Setting the environment variable POSIXLY_CORRECT disables permutation.
   Then the behavior is completely standard.

   GNU application programs can use a third alternative mode in which
   they can distinguish the relative order of options and other arguments.  */

#include "getopt.h"

/* For communication from `getopt' to the caller.
   When `getopt' finds an option that takes an argument,
   the argument value is returned here.
   Also, when `ordering' is RETURN_IN_ORDER,
   each non-option ARGV-element is returned here.  */

char *optarg = NULL;

/* Index in ARGV of the next element to be scanned.
   This is used for communication to and from the caller
   and for communication between successive calls to `getopt'.

   On entry to `getopt', zero means this is the first call; initialize.

   When `getopt' returns -1, this is the index of the first of the
   non-option elements that the caller should itself scan.

   Otherwise, `optind' communicates from one call to the next
   how much of ARGV has been scanned so far.  */

/* 1003.2 says this must be 1 before any call.  */
int optind = 1;

/* Formerly, initialization of getopt depended on optind==0, which
   causes problems with re-calling getopt as programs generally don't
   know that. */

int __getopt_initialized = 0;

/* The next char to be scanned in the option-element
   in which the last option character we returned was found.
   This allows us to pick up the scan where we left off.

   If this is zero, or a null string, it means resume the scan
   by advancing to the next ARGV-element.  */

static char *nextchar;

/* Callers store zero here to inhibit the error message
   for unrecognized options.  */

int opterr = 1;

/* Set to an option character which was unrecognized.
   This must be initialized on some systems to avoid linking in the
   system's own getopt implementation.  */

int optopt = '?';

/* Describe how to deal with options that follow non-option ARGV-elements.

   If the caller did not specify anything,
   the default is REQUIRE_ORDER if the environment variable
   POSIXLY_CORRECT is defined, PERMUTE otherwise.

   REQUIRE_ORDER means don't recognize them as options;
   stop option processing when the first non-option is seen.
   This is what Unix does.
   This mode of operation is selected by either setting the environment
   variable POSIXLY_CORRECT, or using `+' as the first character
   of the list of option characters.

   PERMUTE is the default.  We permute the contents of ARGV as we scan,
   so that eventually all the non-options are at the end.  This allows options
   to be given in any order, even with programs that were not written to
   expect this.

   RETURN_IN_ORDER is an option available to programs that were written
   to expect options and other ARGV-elements in any order and that care about
   the ordering of the two.  We describe each non-option ARGV-element
   as if it were the argument of an option with character code 1.
   Using `-' as the first character of the list of option characters
   selects this mode of operation.

   The special argument `--' forces an end of option-scanning regardless
   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */

static enum
{
  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
} ordering;

/* Value of POSIXLY_CORRECT environment variable.  */
static char *posixly_correct;


#ifdef  __GNU_LIBRARY__
/* We want to avoid inclusion of string.h with non-GNU libraries
   because there are many ways it can cause trouble.
   On some systems, it contains special magic macros that don't work
   in GCC.  */
# include <string.h>
# define my_index   strchr
#else

/* Avoid depending on library functions or files
   whose names are inconsistent.  */

#ifndef getenv
extern char *getenv ();
#endif
#ifndef strncmp
extern int strncmp ();
#endif

static char *
my_index (str, chr)
     const char *str;
     int chr;
{
  while (*str)
    {
      if (*str == chr)
    return (char *) str;
      str++;
    }
  return 0;
}

/* If using GCC, we can safely declare strlen this way.
   If not using GCC, it is ok not to declare it.  */
#ifdef __GNUC__
/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
   That was relevant to code that was here before.  */
# if (!defined __STDC__ || !__STDC__) && !defined strlen
/* gcc with -traditional declares the built-in strlen to return int,
   and has done so at least since version 2.4.5. -- rms.  */
extern int strlen (const char *);
# endif /* not __STDC__ */
#endif /* __GNUC__ */

#endif /* not __GNU_LIBRARY__ */


/* Handle permutation of arguments.  */

/* Describe the part of ARGV that contains non-options that have
   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
   `last_nonopt' is the index after the last of them.  */

static int first_nonopt;
static int last_nonopt;

#ifdef _LIBC
/* Bash 2.0 gives us an environment variable containing flags
   indicating ARGV elements that should not be considered arguments.  */

/* Defined in getopt_init.c  */
extern char *__getopt_nonoption_flags;

static int nonoption_flags_max_len;
static int nonoption_flags_len;

static int original_argc;
static char *const *original_argv;

/* Make sure the environment variable bash 2.0 puts in the environment
   is valid for the getopt call we must make sure that the ARGV passed
   to getopt is that one passed to the process.  */
static void
__attribute__ ((unused))
store_args_and_env (int argc, char *const *argv)
{
  /* XXX This is no good solution.  We should rather copy the args so
     that we can compare them later.  But we must not use malloc(3).  */
  original_argc = argc;
  original_argv = argv;
}
# ifdef text_set_element
text_set_element (__libc_subinit, store_args_and_env);
# endif /* text_set_element */

# define SWAP_FLAGS(ch1, ch2) \
  if (nonoption_flags_len > 0)                            \
    {                                         \
      char __tmp = __getopt_nonoption_flags[ch1];                 \
      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];          \
      __getopt_nonoption_flags[ch2] = __tmp;                      \
    }
#else   /* !_LIBC */
# define SWAP_FLAGS(ch1, ch2)
#endif  /* _LIBC */

/* Exchange two adjacent subsequences of ARGV.
   One subsequence is elements [first_nonopt,last_nonopt)
   which contains all the non-options that have been skipped so far.
   The other is elements [last_nonopt,optind), which contains all
   the options processed since those non-options were skipped.

   `first_nonopt' and `last_nonopt' are relocated so that they describe
   the new indices of the non-options in ARGV after they are moved.  */

#if defined __STDC__ && __STDC__
static void exchange (char **);
#endif

static void
exchange (argv)
     char **argv;
{
  int bottom = first_nonopt;
  int middle = last_nonopt;
  int top = optind;
  char *tem;

  /* Exchange the shorter segment with the far end of the longer segment.
     That puts the shorter segment into the right place.
     It leaves the longer segment in the right place overall,
     but it consists of two parts that need to be swapped next.  */

#ifdef _LIBC
  /* First make sure the handling of the `__getopt_nonoption_flags'
     string can work normally.  Our top argument must be in the range
     of the string.  */
  if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
    {
      /* We must extend the array.  The user plays games with us and
     presents new arguments.  */
      char *new_str = malloc (top + 1);
      if (new_str == NULL)
    nonoption_flags_len = nonoption_flags_max_len = 0;
      else
    {
      memset (__mempcpy (new_str, __getopt_nonoption_flags,
                 nonoption_flags_max_len),
          '\0', top + 1 - nonoption_flags_max_len);
      nonoption_flags_max_len = top + 1;
      __getopt_nonoption_flags = new_str;
    }
    }
#endif

  while (top > middle && middle > bottom)
    {
      if (top - middle > middle - bottom)
    {
      /* Bottom segment is the short one.  */
      int len = middle - bottom;
      register int i;

      /* Swap it with the top part of the top segment.  */
      for (i = 0; i < len; i++)
        {
          tem = argv[bottom + i];
          argv[bottom + i] = argv[top - (middle - bottom) + i];
          argv[top - (middle - bottom) + i] = tem;
          SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
        }
      /* Exclude the moved bottom segment from further swapping.  */
      top -= len;
    }
      else
    {
      /* Top segment is the short one.  */
      int len = top - middle;
      register int i;

      /* Swap it with the bottom part of the bottom segment.  */
      for (i = 0; i < len; i++)
        {
          tem = argv[bottom + i];
          argv[bottom + i] = argv[middle + i];
          argv[middle + i] = tem;
          SWAP_FLAGS (bottom + i, middle + i);
        }
      /* Exclude the moved top segment from further swapping.  */
      bottom += len;
    }
    }

  /* Update records for the slots the non-options now occupy.  */

  first_nonopt += (optind - last_nonopt);
  last_nonopt = optind;
}

/* Initialize the internal data when the first call is made.  */

#if defined __STDC__ && __STDC__
static const char *_getopt_initialize (int, char *const *, const char *);
#endif
static const char *
_getopt_initialize (argc, argv, optstring)
     int argc;
     char *const *argv;
     const char *optstring;
{
  /* Start processing options with ARGV-element 1 (since ARGV-element 0
     is the program name); the sequence of previously skipped
     non-option ARGV-elements is empty.  */

  first_nonopt = last_nonopt = optind;

  nextchar = NULL;

  posixly_correct = getenv ("POSIXLY_CORRECT");

  /* Determine how to handle the ordering of options and nonoptions.  */

  if (optstring[0] == '-')
    {
      ordering = RETURN_IN_ORDER;
      ++optstring;
    }
  else if (optstring[0] == '+')
    {
      ordering = REQUIRE_ORDER;
      ++optstring;
    }
  else if (posixly_correct != NULL)
    ordering = REQUIRE_ORDER;
  else
    ordering = PERMUTE;

#ifdef _LIBC
  if (posixly_correct == NULL
      && argc == original_argc && argv == original_argv)
    {
      if (nonoption_flags_max_len == 0)
    {
      if (__getopt_nonoption_flags == NULL
          || __getopt_nonoption_flags[0] == '\0')
        nonoption_flags_max_len = -1;
      else
        {
          const char *orig_str = __getopt_nonoption_flags;
          int len = nonoption_flags_max_len = strlen (orig_str);
          if (nonoption_flags_max_len < argc)
        nonoption_flags_max_len = argc;
          __getopt_nonoption_flags =
        (char *) malloc (nonoption_flags_max_len);
          if (__getopt_nonoption_flags == NULL)
        nonoption_flags_max_len = -1;
          else
        memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
            '\0', nonoption_flags_max_len - len);
        }
    }
      nonoption_flags_len = nonoption_flags_max_len;
    }
  else
    nonoption_flags_len = 0;
#endif

  return optstring;
}


/* Scan elements of ARGV (whose length is ARGC) for option characters
   given in OPTSTRING.

   If an element of ARGV starts with '-', and is not exactly "-" or "--",
   then it is an option element.  The characters of this element
   (aside from the initial '-') are option characters.  If `getopt'
   is called repeatedly, it returns successively each of the option characters
   from each of the option elements.

   If `getopt' finds another option character, it returns that character,
   updating `optind' and `nextchar' so that the next call to `getopt' can
   resume the scan with the following option character or ARGV-element.

   If there are no more option characters, `getopt' returns -1.
   Then `optind' is the index in ARGV of the first ARGV-element
   that is not an option.  (The ARGV-elements have been permuted
   so that those that are not options now come last.)

   OPTSTRING is a string containing the legitimate option characters.
   If an option character is seen that is not listed in OPTSTRING,
   return '?' after printing an error message.  If you set `opterr' to
   zero, the error message is suppressed but we still return '?'.

   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
   so the following text in the same ARGV-element, or the text of the following
   ARGV-element, is returned in `optarg'.  Two colons mean an option that
   wants an optional arg; if there is text in the current ARGV-element,
   it is returned in `optarg', otherwise `optarg' is set to zero.

   If OPTSTRING starts with `-' or `+', it requests different methods of
   handling the non-option ARGV-elements.
   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.

   Long-named options begin with `--' instead of `-'.
   Their names may be abbreviated as long as the abbreviation is unique
   or is an exact match for some defined option.  If they have an
   argument, it follows the option name in the same ARGV-element, separated
   from the option name by a `=', or else the in next ARGV-element.
   When `getopt' finds a long-named option, it returns 0 if that option's
   `flag' field is nonzero, the value of the option's `val' field
   if the `flag' field is zero.

   The elements of ARGV aren't really const, because we permute them.
   But we pretend they're const in the prototype to be compatible
   with other systems.

   LONGOPTS is a vector of `struct option' terminated by an
   element containing a name which is zero.

   LONGIND returns the index in LONGOPT of the long-named option found.
   It is only valid when a long-named option has been found by the most
   recent call.

   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
   long-named options.  */

int
_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
     int argc;
     char *const *argv;
     const char *optstring;
     const struct option *longopts;
     int *longind;
     int long_only;
{
  optarg = NULL;

  if (optind == 0 || !__getopt_initialized)
    {
      if (optind == 0)
    optind = 1; /* Don't scan ARGV[0], the program name.  */
      optstring = _getopt_initialize (argc, argv, optstring);
      __getopt_initialized = 1;
    }

  /* Test whether ARGV[optind] points to a non-option argument.
     Either it does not have option syntax, or there is an environment flag
     from the shell indicating it is not an option.  The later information
     is only used when the used in the GNU libc.  */
#ifdef _LIBC
# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'       \
              || (optind < nonoption_flags_len                \
              && __getopt_nonoption_flags[optind] == '1'))
#else
# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
#endif

  if (nextchar == NULL || *nextchar == '\0')
    {
      /* Advance to the next ARGV-element.  */

      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
     moved back by the user (who may also have changed the arguments).  */
      if (last_nonopt > optind)
    last_nonopt = optind;
      if (first_nonopt > optind)
    first_nonopt = optind;

      if (ordering == PERMUTE)
    {
      /* If we have just processed some options following some non-options,
         exchange them so that the options come first.  */

      if (first_nonopt != last_nonopt && last_nonopt != optind)
        exchange ((char **) argv);
      else if (last_nonopt != optind)
        first_nonopt = optind;

      /* Skip any additional non-options
         and extend the range of non-options previously skipped.  */

      while (optind < argc && NONOPTION_P)
        optind++;
      last_nonopt = optind;
    }

      /* The special ARGV-element `--' means premature end of options.
     Skip it like a null option,
     then exchange with previous non-options as if it were an option,
     then skip everything else like a non-option.  */

      if (optind != argc && !strcmp (argv[optind], "--"))
    {
      optind++;

      if (first_nonopt != last_nonopt && last_nonopt != optind)
        exchange ((char **) argv);
      else if (first_nonopt == last_nonopt)
        first_nonopt = optind;
      last_nonopt = argc;

      optind = argc;
    }

      /* If we have done all the ARGV-elements, stop the scan
     and back over any non-options that we skipped and permuted.  */

      if (optind == argc)
    {
      /* Set the next-arg-index to point at the non-options
         that we previously skipped, so the caller will digest them.  */
      if (first_nonopt != last_nonopt)
        optind = first_nonopt;
      return -1;
    }

      /* If we have come to a non-option and did not permute it,
     either stop the scan or describe it to the caller and pass it by.  */

      if (NONOPTION_P)
    {
      if (ordering == REQUIRE_ORDER)
        return -1;
      optarg = argv[optind++];
      return 1;
    }

      /* We have found another option-ARGV-element.
     Skip the initial punctuation.  */

      nextchar = (argv[optind] + 1
          + (longopts != NULL && argv[optind][1] == '-'));
    }

  /* Decode the current option-ARGV-element.  */

  /* Check whether the ARGV-element is a long option.

     If long_only and the ARGV-element has the form "-f", where f is
     a valid short option, don't consider it an abbreviated form of
     a long option that starts with f.  Otherwise there would be no
     way to give the -f short option.

     On the other hand, if there's a long option "fubar" and
     the ARGV-element is "-fu", do consider that an abbreviation of
     the long option, just like "--fu", and not "-f" with arg "u".

     This distinction seems to be the most useful approach.  */

  if (longopts != NULL
      && (argv[optind][1] == '-'
      || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
    {
      char *nameend;
      const struct option *p;
      const struct option *pfound = NULL;
      int exact = 0;
      int ambig = 0;
      int indfound = -1;
      int option_index;

      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
    /* Do nothing.  */ ;

      /* Test all long options for either exact match
     or abbreviated matches.  */
      for (p = longopts, option_index = 0; p->name; p++, option_index++)
    if (!strncmp (p->name, nextchar, nameend - nextchar))
      {
        if ((unsigned int) (nameend - nextchar)
        == (unsigned int) strlen (p->name))
          {
        /* Exact match found.  */
        pfound = p;
        indfound = option_index;
        exact = 1;
        break;
          }
        else if (pfound == NULL)
          {
        /* First nonexact match found.  */
        pfound = p;
        indfound = option_index;
          }
        else
          /* Second or later nonexact match found.  */
          ambig = 1;
      }

      if (ambig && !exact)
    {
      if (opterr)
        fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
             argv[0], argv[optind]);
      nextchar += strlen (nextchar);
      optind++;
      optopt = 0;
      return '?';
    }

      if (pfound != NULL)
    {
      option_index = indfound;
      optind++;
      if (*nameend)
        {
          /* Don't test has_arg with >, because some C compilers don't
         allow it to be used on enums.  */
          if (pfound->has_arg)
        optarg = nameend + 1;
          else
        {
          if (opterr)
           if (argv[optind - 1][1] == '-')
            /* --option */
            fprintf (stderr,
             _("%s: option `--%s' doesn't allow an argument\n"),
             argv[0], pfound->name);
           else
            /* +option or -option */
            fprintf (stderr,
             _("%s: option `%c%s' doesn't allow an argument\n"),
             argv[0], argv[optind - 1][0], pfound->name);

          nextchar += strlen (nextchar);

          optopt = pfound->val;
          return '?';
        }
        }
      else if (pfound->has_arg == 1)
        {
          if (optind < argc)
        optarg = argv[optind++];
          else
        {
          if (opterr)
            fprintf (stderr,
               _("%s: option `%s' requires an argument\n"),
               argv[0], argv[optind - 1]);
          nextchar += strlen (nextchar);
          optopt = pfound->val;
          return optstring[0] == ':' ? ':' : '?';
        }
        }
      nextchar += strlen (nextchar);
      if (longind != NULL)
        *longind = option_index;
      if (pfound->flag)
        {
          *(pfound->flag) = pfound->val;
          return 0;
        }
      return pfound->val;
    }

      /* Can't find it as a long option.  If this is not getopt_long_only,
     or the option starts with '--' or is not a valid short
     option, then it's an error.
     Otherwise interpret it as a short option.  */
      if (!long_only || argv[optind][1] == '-'
      || my_index (optstring, *nextchar) == NULL)
    {
      if (opterr)
        {
          if (argv[optind][1] == '-')
        /* --option */
        fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
             argv[0], nextchar);
          else
        /* +option or -option */
        fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
             argv[0], argv[optind][0], nextchar);
        }
      nextchar = (char *) "";
      optind++;
      optopt = 0;
      return '?';
    }
    }

  /* Look at and handle the next short option-character.  */

  {
    char c = *nextchar++;
    char *temp = my_index (optstring, c);

    /* Increment `optind' when we start to process its last character.  */
    if (*nextchar == '\0')
      ++optind;

    if (temp == NULL || c == ':')
      {
    if (opterr)
      {
        if (posixly_correct)
          /* 1003.2 specifies the format of this message.  */
          fprintf (stderr, _("%s: illegal option -- %c\n"),
               argv[0], c);
        else
          fprintf (stderr, _("%s: invalid option -- %c\n"),
               argv[0], c);
      }
    optopt = c;
    return '?';
      }
    /* Convenience. Treat POSIX -W foo same as long option --foo */
    if (temp[0] == 'W' && temp[1] == ';')
      {
    char *nameend;
    const struct option *p;
    const struct option *pfound = NULL;
    int exact = 0;
    int ambig = 0;
    int indfound = 0;
    int option_index;

    /* This is an option that requires an argument.  */
    if (*nextchar != '\0')
      {
        optarg = nextchar;
        /* If we end this ARGV-element by taking the rest as an arg,
           we must advance to the next element now.  */
        optind++;
      }
    else if (optind == argc)
      {
        if (opterr)
          {
        /* 1003.2 specifies the format of this message.  */
        fprintf (stderr, _("%s: option requires an argument -- %c\n"),
             argv[0], c);
          }
        optopt = c;
        if (optstring[0] == ':')
          c = ':';
        else
          c = '?';
        return c;
      }
    else
      /* We already incremented `optind' once;
         increment it again when taking next ARGV-elt as argument.  */
      optarg = argv[optind++];

    /* optarg is now the argument, see if it's in the
       table of longopts.  */

    for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
      /* Do nothing.  */ ;

    /* Test all long options for either exact match
       or abbreviated matches.  */
    for (p = longopts, option_index = 0; p->name; p++, option_index++)
      if (!strncmp (p->name, nextchar, nameend - nextchar))
        {
          if ((unsigned int) (nameend - nextchar) == strlen (p->name))
        {
          /* Exact match found.  */
          pfound = p;
          indfound = option_index;
          exact = 1;
          break;
        }
          else if (pfound == NULL)
        {
          /* First nonexact match found.  */
          pfound = p;
          indfound = option_index;
        }
          else
        /* Second or later nonexact match found.  */
        ambig = 1;
        }
    if (ambig && !exact)
      {
        if (opterr)
          fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
               argv[0], argv[optind]);
        nextchar += strlen (nextchar);
        optind++;
        return '?';
      }
    if (pfound != NULL)
      {
        option_index = indfound;
        if (*nameend)
          {
        /* Don't test has_arg with >, because some C compilers don't
           allow it to be used on enums.  */
        if (pfound->has_arg)
          optarg = nameend + 1;
        else
          {
            if (opterr)
              fprintf (stderr, _("\
%s: option `-W %s' doesn't allow an argument\n"),
                   argv[0], pfound->name);

            nextchar += strlen (nextchar);
            return '?';
          }
          }
        else if (pfound->has_arg == 1)
          {
        if (optind < argc)
          optarg = argv[optind++];
        else
          {
            if (opterr)
              fprintf (stderr,
                   _("%s: option `%s' requires an argument\n"),
                   argv[0], argv[optind - 1]);
            nextchar += strlen (nextchar);
            return optstring[0] == ':' ? ':' : '?';
          }
          }
        nextchar += strlen (nextchar);
        if (longind != NULL)
          *longind = option_index;
        if (pfound->flag)
          {
        *(pfound->flag) = pfound->val;
        return 0;
          }
        return pfound->val;
      }
      nextchar = NULL;
      return 'W';   /* Let the application handle it.   */
      }
    if (temp[1] == ':')
      {
    if (temp[2] == ':')
      {
        /* This is an option that accepts an argument optionally.  */
        if (*nextchar != '\0')
          {
        optarg = nextchar;
        optind++;
          }
        else
          optarg = NULL;
        nextchar = NULL;
      }
    else
      {
        /* This is an option that requires an argument.  */
        if (*nextchar != '\0')
          {
        optarg = nextchar;
        /* If we end this ARGV-element by taking the rest as an arg,
           we must advance to the next element now.  */
        optind++;
          }
        else if (optind == argc)
          {
        if (opterr)
          {
            /* 1003.2 specifies the format of this message.  */
            fprintf (stderr,
               _("%s: option requires an argument -- %c\n"),
               argv[0], c);
          }
        optopt = c;
        if (optstring[0] == ':')
          c = ':';
        else
          c = '?';
          }
        else
          /* We already incremented `optind' once;
         increment it again when taking next ARGV-elt as argument.  */
          optarg = argv[optind++];
        nextchar = NULL;
      }
      }
    return c;
  }
}

int
getopt (argc, argv, optstring)
     int argc;
     char *const *argv;
     const char *optstring;
{
  return _getopt_internal (argc, argv, optstring,
               (const struct option *) 0,
               (int *) 0,
               0);
}

#endif  /* Not ELIDE_CODE.  */


#ifdef TEST

/* Compile with -DTEST to make an executable for use in testing
   the above definition of `getopt'.  */

int
main (argc, argv)
     int argc;
     char **argv;
{
  int c;
  int digit_optind = 0;

  while (1)
    {
      int this_option_optind = optind ? optind : 1;

      c = getopt (argc, argv, "abc:d:0123456789");
      if (c == -1)
    break;

      switch (c)
    {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (digit_optind != 0 && digit_optind != this_option_optind)
        printf ("digits occur in two different argv-elements.\n");
      digit_optind = this_option_optind;
      printf ("option %c\n", c);
      break;

    case 'a':
      printf ("option a\n");
      break;

    case 'b':
      printf ("option b\n");
      break;

    case 'c':
      printf ("option c with value `%s'\n", optarg);
      break;

    case '?':
      break;

    default:
      printf ("?? getopt returned character code 0%o ??\n", c);
    }
    }

  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
    printf ("%s ", argv[optind++]);
      printf ("\n");
    }

  exit (0);
}

#endif /* TEST */

https://git.savannah.gnu.org/cgit/sed.git/tree/lib/utils.c?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/utils.c used in: Step 681
/*  Functions from hack's utils library.
    Copyright (C) 1989, 1990, 1991, 1998, 1999, 2003
    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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */

#include "config.h"

#include <stdio.h>

#include <errno.h>
#ifndef errno
  extern int errno;
#endif

#ifdef HAVE_STRINGS_H
# include <strings.h>
#else
# include <string.h>
#endif /* HAVE_STRINGS_H */

#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif /* HAVE_STDLIB_H */

#include "utils.h"

const char *myname;

void do_ck_fclose P_((FILE *stream));

/* Print an error message and exit */
#if !defined __STDC__ || !(__STDC__-0)
# include <varargs.h>
# define VSTART(l,a)    va_start(l)
void
panic(str, va_alist)
  char *str;
  va_dcl
#else /*__STDC__*/
# include <stdarg.h>
# define VSTART(l,a)    va_start(l, a)
void
panic(const char *str, ...)
#endif /* __STDC__ */
{
  va_list iggy;

  fprintf(stderr, "%s: ", myname);
  VSTART(iggy, str);
#ifndef HAVE_VPRINTF
# ifndef HAVE_DOPRNT
  fputs(str, stderr);   /* not great, but perhaps better than nothing... */
# else /* HAVE_DOPRNT */
  _doprnt(str, &iggy, stderr);
# endif /* HAVE_DOPRNT */
#else /* HAVE_VFPRINTF */
  vfprintf(stderr, str, iggy);
#endif /* HAVE_VFPRINTF */
  va_end(iggy);
  putc('\n', stderr);
  exit(4);
}



/* Store information about files opened with ck_fopen
   so that error messages from ck_fread, ck_fwrite, etc. can print the
   name of the file that had the error */

struct id
  {
    FILE *fp;
    char *name;
    struct id *link;
  };

static struct id *utils_id_s = NULL;

/* Internal routine to get a filename from utils_id_s */
static const char *utils_fp_name P_((FILE *fp));
static const char *
utils_fp_name(fp)
  FILE *fp;
{
  struct id *p;

  for (p=utils_id_s; p; p=p->link)
    if (p->fp == fp)
      return p->name;
  if (fp == stdin)
    return "stdin";
  else if (fp == stdout)
    return "stdout";
  else if (fp == stderr)
    return "stderr";

  return "<unknown>";
}

/* Panic on failing fopen */
FILE *
ck_fopen(name, mode, fail)
  const char *name;
  const char *mode;
  flagT fail;
{
  FILE *fp;
  struct id *p;

  fp = fopen (name, mode);
  if (!fp)
    {
      if (fail)
        panic(_("Couldn't open file %s: %s"), name, strerror(errno));

      return NULL;
    }

  for (p=utils_id_s; p; p=p->link)
    {
      if (fp == p->fp)
    {
      FREE(p->name);
      break;
    }
    }
  if (!p)
    {
      p = MALLOC(1, struct id);
      p->link = utils_id_s;
      utils_id_s = p;
    }
  p->name = ck_strdup(name);
  p->fp = fp;
  return fp;
}

/* Panic on failing fwrite */
void
ck_fwrite(ptr, size, nmemb, stream)
  const VOID *ptr;
  size_t size;
  size_t nmemb;
  FILE *stream;
{
  clearerr(stream);
  if (size && fwrite(ptr, size, nmemb, stream) != nmemb)
    panic(ngettext("couldn't write %d item to %s: %s",
           "couldn't write %d items to %s: %s", nmemb), 
        nmemb, utils_fp_name(stream), strerror(errno));
}

/* Panic on failing fread */
size_t
ck_fread(ptr, size, nmemb, stream)
  VOID *ptr;
  size_t size;
  size_t nmemb;
  FILE *stream;
{
  clearerr(stream);
  if (size && (nmemb=fread(ptr, size, nmemb, stream)) <= 0 && ferror(stream))
    panic(_("read error on %s: %s"), utils_fp_name(stream), strerror(errno));

  return nmemb;
}

/* Panic on failing fflush */
void
ck_fflush(stream)
  FILE *stream;
{
  clearerr(stream);
  if (fflush(stream) == EOF && errno != EBADF)
    panic("Couldn't flush %s: %s", utils_fp_name(stream), strerror(errno));
}

/* Panic on failing fclose */
void
ck_fclose(stream)
  FILE *stream;
{
  struct id r;
  struct id *prev;
  struct id *cur;

  /* a NULL stream means to close all files */
  r.link = utils_id_s;
  prev = &r;
  while ( (cur = prev->link) )
    {
      if (!stream || stream == cur->fp)
    {
      do_ck_fclose (cur->fp);
      prev->link = cur->link;
      FREE(cur->name);
      FREE(cur);
    }
      else
    prev = cur;
    }

  utils_id_s = r.link;

  /* Also care about stdout, because if it is redirected the
     last output operations might fail and it is important
     to signal this as an error (perhaps to make). */
  if (!stream)
    {
      do_ck_fclose (stdout);
      do_ck_fclose (stderr);
    }
}

/* Close a single file and update a count of closed files. */
void
do_ck_fclose(stream)
  FILE *stream;
{
  ck_fflush(stream);
  clearerr(stream);
  if (fclose(stream) == EOF)
    panic("Couldn't close %s: %s", utils_fp_name(stream), strerror(errno));
}




char *
temp_file_template(tmpdir, program)
  const char *tmpdir;    
  char *program;
{
  char *template;
  if (tmpdir == NULL)
    tmpdir = getenv("TMPDIR");
  if (tmpdir == NULL)
    {
      tmpdir = getenv("TMP");
      if (tmpdir == NULL)
#ifdef P_tmpdir
    tmpdir = P_tmpdir;
#else
    tmpdir = "/tmp";
#endif
    }

  template = xmalloc (strlen (tmpdir) + strlen (program) + 8);
  sprintf (template, "%s/%sXXXXXX", tmpdir, program);
  return (template);
}



/* Panic on failing malloc */
VOID *
ck_malloc(size)
  size_t size;
{
  VOID *ret = calloc(1, size ? size : 1);
  if (!ret)
    panic("Couldn't allocate memory");
  return ret;
}

/* Panic on failing malloc */
VOID *
xmalloc(size)
  size_t size;
{
  return ck_malloc(size);
}

/* Panic on failing realloc */
VOID *
ck_realloc(ptr, size)
  VOID *ptr;
  size_t size;
{
  VOID *ret;

  if (size == 0)
    {
      FREE(ptr);
      return NULL;
    }
  if (!ptr)
    return ck_malloc(size);
  ret = realloc(ptr, size);
  if (!ret)
    panic("Couldn't re-allocate memory");
  return ret;
}

/* Return a malloc()'d copy of a string */
char *
ck_strdup(str)
  const char *str;
{
  char *ret = MALLOC(strlen(str)+1, char);
  return strcpy(ret, str);
}

/* Return a malloc()'d copy of a block of memory */
VOID *
ck_memdup(buf, len)
  const VOID *buf;
  size_t len;
{
  VOID *ret = ck_malloc(len);
  return memcpy(ret, buf, len);
}

/* Release a malloc'd block of memory */
void
ck_free(ptr)
  VOID *ptr;
{
  if (ptr)
    free(ptr);
}



/* Implement a variable sized buffer of `stuff'.  We don't know what it is,
nor do we care, as long as it doesn't mind being aligned by malloc. */

struct buffer
  {
    size_t allocated;
    size_t length;
    char *b;
  };

#define MIN_ALLOCATE 50

struct buffer *
init_buffer()
{
  struct buffer *b = MALLOC(1, struct buffer);
  b->b = MALLOC(MIN_ALLOCATE, char);
  b->allocated = MIN_ALLOCATE;
  b->length = 0;
  return b;
}

char *
get_buffer(b)
  struct buffer *b;
{
  return b->b;
}

size_t
size_buffer(b)
  struct buffer *b;
{
  return b->length;
}

static void resize_buffer P_((struct buffer *b, size_t newlen));
static void
resize_buffer(b, newlen)
  struct buffer *b;
  size_t newlen;
{
  char *try = NULL;
  size_t alen = b->allocated;

  if (newlen <= alen)
    return;
  alen *= 2;
  if (newlen < alen)
    try = realloc(b->b, alen);  /* Note: *not* the REALLOC() macro! */
  if (!try)
    {
      alen = newlen;
      try = REALLOC(b->b, alen, char);
    }
  b->allocated = alen;
  b->b = try;
}

void
add_buffer(b, p, n)
  struct buffer *b;
  const char *p;
  size_t n;
{
  if (b->allocated - b->length < n)
    resize_buffer(b, b->length+n);
  memcpy(b->b + b->length, p, n);
  b->length += n;
}

void
add1_buffer(b, c)
  struct buffer *b;
  int c;
{
  /* This special case should be kept cheap;
   *  don't make it just a mere convenience
   *  wrapper for add_buffer() -- even "builtin"
   *  versions of memcpy(a, b, 1) can become
   *  expensive when called too often.
   */
  if (c != EOF)
    {
      if (b->allocated - b->length < 1)
    resize_buffer(b, b->length+1);
      b->b[b->length++] = c;
    }
}

void
free_buffer(b)
  struct buffer *b;
{
  if (b)
    FREE(b->b);
  FREE(b);
}

https://git.savannah.gnu.org/cgit/sed.git/tree/lib/regex_.h?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/regex_.h used in: Step 682
/* Definitions for data structures and routines for the regular
   expression library.
   Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003
   Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#ifndef _REGEX_H
#define _REGEX_H 1

#include <sys/types.h>

/* Allow the use in C++ code.  */
#ifdef __cplusplus
extern "C" {
#endif

/* POSIX says that <sys/types.h> must be included (by the caller) before
   <regex.h>.  */

#if !defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE && defined VMS
/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
   should be there.  */
# include <stddef.h>
#endif

/* The following two types have to be signed and unsigned integer type
   wide enough to hold a value of a pointer.  For most ANSI compilers
   ptrdiff_t and size_t should be likely OK.  Still size of these two
   types is 2 for Microsoft C.  Ugh... */
typedef long int s_reg_t;
typedef unsigned long int active_reg_t;

/* The following bits are used to determine the regexp syntax we
   recognize.  The set/not-set meanings are chosen so that Emacs syntax
   remains the value 0.  The bits are given in alphabetical order, and
   the definitions shifted by one from the previous bit; thus, when we
   add or remove a bit, only one other definition need change.  */
typedef unsigned long int reg_syntax_t;

/* If this bit is not set, then \ inside a bracket expression is literal.
   If set, then such a \ quotes the following character.  */
#define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)

/* If this bit is not set, then + and ? are operators, and \+ and \? are
     literals.
   If set, then \+ and \? are operators and + and ? are literals.  */
#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)

/* If this bit is set, then character classes are supported.  They are:
     [:alpha:], [:upper:], [:lower:],  [:digit:], [:alnum:], [:xdigit:],
     [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
   If not set, then character classes are not supported.  */
#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)

/* If this bit is set, then ^ and $ are always anchors (outside bracket
     expressions, of course).
   If this bit is not set, then it depends:
        ^  is an anchor if it is at the beginning of a regular
           expression or after an open-group or an alternation operator;
        $  is an anchor if it is at the end of a regular expression, or
           before a close-group or an alternation operator.

   This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
   POSIX draft 11.2 says that * etc. in leading positions is undefined.
   We already implemented a previous draft which made those constructs
   invalid, though, so we haven't changed the code back.  */
#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)

/* If this bit is set, then special characters are always special
     regardless of where they are in the pattern.
   If this bit is not set, then special characters are special only in
     some contexts; otherwise they are ordinary.  Specifically,
     * + ? and intervals are only special when not after the beginning,
     open-group, or alternation operator.  */
#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)

/* If this bit is set, then *, +, ?, and { cannot be first in an re or
     immediately after an alternation or begin-group operator.  */
#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)

/* If this bit is set, then . matches newline.
   If not set, then it doesn't.  */
#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)

/* If this bit is set, then . doesn't match NUL.
   If not set, then it does.  */
#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)

/* If this bit is set, nonmatching lists [^...] do not match newline.
   If not set, they do.  */
#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)

/* If this bit is set, either \{...\} or {...} defines an
     interval, depending on RE_NO_BK_BRACES.
   If not set, \{, \}, {, and } are literals.  */
#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)

/* If this bit is set, +, ? and | aren't recognized as operators.
   If not set, they are.  */
#define RE_LIMITED_OPS (RE_INTERVALS << 1)

/* If this bit is set, newline is an alternation operator.
   If not set, newline is literal.  */
#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)

/* If this bit is set, then `{...}' defines an interval, and \{ and \}
     are literals.
  If not set, then `\{...\}' defines an interval.  */
#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)

/* If this bit is set, (...) defines a group, and \( and \) are literals.
   If not set, \(...\) defines a group, and ( and ) are literals.  */
#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)

/* If this bit is set, then \<digit> matches <digit>.
   If not set, then \<digit> is a back-reference.  */
#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)

/* If this bit is set, then | is an alternation operator, and \| is literal.
   If not set, then \| is an alternation operator, and | is literal.  */
#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)

/* If this bit is set, then an ending range point collating higher
     than the starting range point, as in [z-a], is invalid.
   If not set, then when ending range point collates higher than the
     starting range point, the range is ignored.  */
#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)

/* If this bit is set, then an unmatched ) is ordinary.
   If not set, then an unmatched ) is invalid.  */
#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)

/* If this bit is set, succeed as soon as we match the whole pattern,
   without further backtracking.  */
#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)

/* If this bit is set, do not process the GNU regex operators.
   If not set, then the GNU regex operators are recognized. */
#define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)

/* If this bit is set, turn on internal regex debugging.
   If not set, and debugging was on, turn it off.
   This only works if regex.c is compiled -DDEBUG.
   We define this bit always, so that all that's needed to turn on
   debugging is to recompile regex.c; the calling code can always have
   this bit set, and it won't affect anything in the normal case. */
#define RE_DEBUG (RE_NO_GNU_OPS << 1)

/* If this bit is set, a syntactically invalid interval is treated as
   a string of ordinary characters.  For example, the ERE 'a{1' is
   treated as 'a\{1'.  */
#define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1)

/* If this bit is set, then ignore case when matching.
   If not set, then case is significant.  */
#define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)

/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only
   for ^, because it is difficult to scan the regex backwards to find
   whether ^ should be special.  */
#define RE_CARET_ANCHORS_HERE (RE_ICASE << 1)

/* If this bit is set, then \{ cannot be first in an bre or
   immediately after an alternation or begin-group operator.  */
#define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1)

/* This global variable defines the particular regexp syntax to use (for
   some interfaces).  When a regexp is compiled, the syntax used is
   stored in the pattern buffer, so changing this does not affect
   already-compiled regexps.  */
extern reg_syntax_t re_syntax_options;


/* Define combinations of the above bits for the standard possibilities.
   (The [[[ comments delimit what gets put into the Texinfo file, so
   don't delete them!)  */
/* [[[begin syntaxes]]] */
#define RE_SYNTAX_EMACS 0

#define RE_SYNTAX_AWK                           \
  (RE_BACKSLASH_ESCAPE_IN_LISTS   | RE_DOT_NOT_NULL         \
   | RE_NO_BK_PARENS              | RE_NO_BK_REFS           \
   | RE_NO_BK_VBAR                | RE_NO_EMPTY_RANGES          \
   | RE_DOT_NEWLINE       | RE_CONTEXT_INDEP_ANCHORS        \
   | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)

#define RE_SYNTAX_GNU_AWK                       \
  ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG) \
   & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS        \
       | RE_CONTEXT_INVALID_OPS ))

#define RE_SYNTAX_POSIX_AWK                         \
  (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS      \
   | RE_INTERVALS       | RE_NO_GNU_OPS)

#define RE_SYNTAX_GREP                          \
  (RE_BK_PLUS_QM              | RE_CHAR_CLASSES             \
   | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS                \
   | RE_NEWLINE_ALT)

#define RE_SYNTAX_EGREP                         \
  (RE_CHAR_CLASSES        | RE_CONTEXT_INDEP_ANCHORS            \
   | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE            \
   | RE_NEWLINE_ALT       | RE_NO_BK_PARENS             \
   | RE_NO_BK_VBAR)

#define RE_SYNTAX_POSIX_EGREP                       \
  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES         \
   | RE_INVALID_INTERVAL_ORD)

/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff.  */
#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC

#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC

/* Syntax bits common to both basic and extended POSIX regex syntax.  */
#define _RE_SYNTAX_POSIX_COMMON                     \
  (RE_CHAR_CLASSES | RE_DOT_NEWLINE      | RE_DOT_NOT_NULL      \
   | RE_INTERVALS  | RE_NO_EMPTY_RANGES)

#define RE_SYNTAX_POSIX_BASIC                       \
  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP)

/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
   RE_LIMITED_OPS, i.e., \? \+ \| are not recognized.  Actually, this
   isn't minimal, since other operators, such as \`, aren't disabled.  */
#define RE_SYNTAX_POSIX_MINIMAL_BASIC                   \
  (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)

#define RE_SYNTAX_POSIX_EXTENDED                    \
  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS          \
   | RE_CONTEXT_INDEP_OPS   | RE_NO_BK_BRACES               \
   | RE_NO_BK_PARENS        | RE_NO_BK_VBAR             \
   | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD)

/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is
   removed and RE_NO_BK_REFS is added.  */
#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED                \
  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS          \
   | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES               \
   | RE_NO_BK_PARENS        | RE_NO_BK_REFS             \
   | RE_NO_BK_VBAR      | RE_UNMATCHED_RIGHT_PAREN_ORD)
/* [[[end syntaxes]]] */


/* Maximum number of duplicates an interval can allow.  Some systems
   (erroneously) define this in other header files, but we want our
   value, so remove any previous define.  */
#ifdef RE_DUP_MAX
# undef RE_DUP_MAX
#endif
/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows.  */
#define RE_DUP_MAX (0x7fff)


/* POSIX `cflags' bits (i.e., information for `regcomp').  */

/* If this bit is set, then use extended regular expression syntax.
   If not set, then use basic regular expression syntax.  */
#define REG_EXTENDED 1

/* If this bit is set, then ignore case when matching.
   If not set, then case is significant.  */
#define REG_ICASE (REG_EXTENDED << 1)

/* If this bit is set, then anchors do not match at newline
     characters in the string.
   If not set, then anchors do match at newlines.  */
#define REG_NEWLINE (REG_ICASE << 1)

/* If this bit is set, then report only success or fail in regexec.
   If not set, then returns differ between not matching and errors.  */
#define REG_NOSUB (REG_NEWLINE << 1)


/* POSIX `eflags' bits (i.e., information for regexec).  */

/* If this bit is set, then the beginning-of-line operator doesn't match
     the beginning of the string (presumably because it's not the
     beginning of a line).
   If not set, then the beginning-of-line operator does match the
     beginning of the string.  */
#define REG_NOTBOL 1

/* Like REG_NOTBOL, except for the end-of-line.  */
#define REG_NOTEOL (1 << 1)


/* If any error codes are removed, changed, or added, update the
   `re_error_msg' table in regex.c.  */
typedef enum
{
#ifdef _XOPEN_SOURCE
  REG_ENOSYS = -1,  /* This will never happen for this implementation.  */
#endif

  REG_NOERROR = 0,  /* Success.  */
  REG_NOMATCH,      /* Didn't find a match (for regexec).  */

  /* POSIX regcomp return error codes.  (In the order listed in the
     standard.)  */
  REG_BADPAT,       /* Invalid pattern.  */
  REG_ECOLLATE,     /* Not implemented.  */
  REG_ECTYPE,       /* Invalid character class name.  */
  REG_EESCAPE,      /* Trailing backslash.  */
  REG_ESUBREG,      /* Invalid back reference.  */
  REG_EBRACK,       /* Unmatched left bracket.  */
  REG_EPAREN,       /* Parenthesis imbalance.  */
  REG_EBRACE,       /* Unmatched \{.  */
  REG_BADBR,        /* Invalid contents of \{\}.  */
  REG_ERANGE,       /* Invalid range end.  */
  REG_ESPACE,       /* Ran out of memory.  */
  REG_BADRPT,       /* No preceding re for repetition op.  */

  /* Error codes we've added.  */
  REG_EEND,     /* Premature end.  */
  REG_ESIZE,        /* Compiled pattern bigger than 2^16 bytes.  */
  REG_ERPAREN       /* Unmatched ) or \); not returned from regcomp.  */
} reg_errcode_t;


/* This data structure represents a compiled pattern.  Before calling
   the pattern compiler, the fields `buffer', `allocated', `fastmap',
   `translate', and `no_sub' can be set.  After the pattern has been
   compiled, the `re_nsub' field is available.  All other fields are
   private to the regex routines.  */

#ifndef RE_TRANSLATE_TYPE
# define RE_TRANSLATE_TYPE char *
#endif

struct re_pattern_buffer
{
/* [[[begin pattern_buffer]]] */
    /* Space that holds the compiled pattern.  It is declared as
          `unsigned char *' because its elements are
           sometimes used as array indexes.  */
  unsigned char *buffer;

    /* Number of bytes to which `buffer' points.  */
  unsigned long int allocated;

    /* Number of bytes actually used in `buffer'.  */
  unsigned long int used;

        /* Syntax setting with which the pattern was compiled.  */
  reg_syntax_t syntax;

        /* Pointer to a fastmap, if any, otherwise zero.  re_search uses
           the fastmap, if there is one, to skip over impossible
           starting points for matches.  */
  char *fastmap;

        /* Either a translate table to apply to all characters before
           comparing them, or zero for no translation.  The translation
           is applied to a pattern when it is compiled and to a string
           when it is matched.  */
  RE_TRANSLATE_TYPE translate;

    /* Number of subexpressions found by the compiler.  */
  size_t re_nsub;

        /* Zero if this pattern cannot match the empty string, one else.
           Well, in truth it's used only in `re_search_2', to see
           whether or not we should use the fastmap, so we don't set
           this absolutely perfectly; see `re_compile_fastmap' (the
           `duplicate' case).  */
  unsigned can_be_null : 1;

        /* If REGS_UNALLOCATED, allocate space in the `regs' structure
             for `max (RE_NREGS, re_nsub + 1)' groups.
           If REGS_REALLOCATE, reallocate space if necessary.
           If REGS_FIXED, use what's there.  */
#define REGS_UNALLOCATED 0
#define REGS_REALLOCATE 1
#define REGS_FIXED 2
  unsigned regs_allocated : 2;

        /* Set to zero when `regex_compile' compiles a pattern; set to one
           by `re_compile_fastmap' if it updates the fastmap.  */
  unsigned fastmap_accurate : 1;

        /* If set, `re_match_2' does not return information about
           subexpressions.  */
  unsigned no_sub : 1;

        /* If set, a beginning-of-line anchor doesn't match at the
           beginning of the string.  */
  unsigned not_bol : 1;

        /* Similarly for an end-of-line anchor.  */
  unsigned not_eol : 1;

        /* If true, an anchor at a newline matches.  */
  unsigned newline_anchor : 1;

/* [[[end pattern_buffer]]] */
};

typedef struct re_pattern_buffer regex_t;


/* Type for byte offsets within the string.  POSIX mandates this.  */
typedef int regoff_t;


/* This is the structure we store register match data in.  See
   regex.texinfo for a full description of what registers match.  */
struct re_registers
{
  unsigned num_regs;
  regoff_t *start;
  regoff_t *end;
};


/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
   `re_match_2' returns information about at least this many registers
   the first time a `regs' structure is passed.  */
#ifndef RE_NREGS
# define RE_NREGS 30
#endif


/* POSIX specification for registers.  Aside from the different names than
   `re_registers', POSIX uses an array of structures, instead of a
   structure of arrays.  */
typedef struct
{
  regoff_t rm_so;  /* Byte offset from string's start to substring's start.  */
  regoff_t rm_eo;  /* Byte offset from string's start to substring's end.  */
} regmatch_t;


/* Declarations for routines.  */

/* To avoid duplicating every routine declaration -- once with a
   prototype (if we are ANSI), and once without (if we aren't) -- we
   use the following macro to declare argument types.  This
   unfortunately clutters up the declarations a bit, but I think it's
   worth it.  */

#if __STDC__

# define _RE_ARGS(args) args

#else /* not __STDC__ */

# define _RE_ARGS(args) ()

#endif /* not __STDC__ */

/* Sets the current default syntax to SYNTAX, and return the old syntax.
   You can also simply assign to the `re_syntax_options' variable.  */
extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));

/* Compile the regular expression PATTERN, with length LENGTH
   and syntax given by the global `re_syntax_options', into the buffer
   BUFFER.  Return NULL if successful, and an error string if not.  */
extern const char *re_compile_pattern
  _RE_ARGS ((const char *pattern, size_t length,
             struct re_pattern_buffer *buffer));


/* Compile a fastmap for the compiled pattern in BUFFER; used to
   accelerate searches.  Return 0 if successful and -2 if was an
   internal error.  */
extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));


/* Search in the string STRING (with length LENGTH) for the pattern
   compiled into BUFFER.  Start searching at position START, for RANGE
   characters.  Return the starting position of the match, -1 for no
   match, or -2 for an internal error.  Also return register
   information in REGS (if REGS and BUFFER->no_sub are nonzero).  */
extern int re_search
  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
            int length, int start, int range, struct re_registers *regs));


/* Like `re_search', but search in the concatenation of STRING1 and
   STRING2.  Also, stop searching at index START + STOP.  */
extern int re_search_2
  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
             int length1, const char *string2, int length2,
             int start, int range, struct re_registers *regs, int stop));


/* Like `re_search', but return how many characters in STRING the regexp
   in BUFFER matched, starting at position START.  */
extern int re_match
  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
             int length, int start, struct re_registers *regs));


/* Relates to `re_match' as `re_search_2' relates to `re_search'.  */
extern int re_match_2
  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
             int length1, const char *string2, int length2,
             int start, struct re_registers *regs, int stop));


/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
   ENDS.  Subsequent matches using BUFFER and REGS will use this memory
   for recording register information.  STARTS and ENDS must be
   allocated with malloc, and must each be at least `NUM_REGS * sizeof
   (regoff_t)' bytes long.

   If NUM_REGS == 0, then subsequent matches should allocate their own
   register data.

   Unless this function is called, the first search or match using
   PATTERN_BUFFER will allocate its own register data, without
   freeing the old data.  */
extern void re_set_registers
  _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
             unsigned num_regs, regoff_t *starts, regoff_t *ends));

#if defined _REGEX_RE_COMP || defined _LIBC
# ifndef _CRAY
/* 4.2 bsd compatibility.  */
extern char *re_comp _RE_ARGS ((const char *));
extern int re_exec _RE_ARGS ((const char *));
# endif
#endif

/* GCC 2.95 and later have "__restrict"; C99 compilers have
   "restrict", and "configure" may have defined "restrict".  */
#ifndef __restrict
# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__))
#  if defined restrict || 199901L <= __STDC_VERSION__
#   define __restrict restrict
#  else
#   define __restrict
#  endif
# endif
#endif
/* gcc 3.1 and up support the [restrict] syntax.  */
#ifndef __restrict_arr
# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
#  define __restrict_arr __restrict
# else
#  define __restrict_arr
# endif
#endif

/* POSIX compatibility.  */
extern int regcomp _RE_ARGS ((regex_t *__restrict __preg,
                  const char *__restrict __pattern,
                  int __cflags));

extern int regexec _RE_ARGS ((const regex_t *__restrict __preg,
                  const char *__restrict __string, size_t __nmatch,
                  regmatch_t __pmatch[__restrict_arr],
                  int __eflags));

extern size_t regerror _RE_ARGS ((int __errcode, const regex_t *__preg,
                  char *__errbuf, size_t __errbuf_size));

extern void regfree _RE_ARGS ((regex_t *__preg));


#ifdef __cplusplus
}
#endif  /* C++ */

#endif /* regex.h */


/*
Local variables:
make-backup-files: t
version-control: t
trim-versions-without-asking: nil
End:
*/

https://git.savannah.gnu.org/cgit/sed.git/tree/lib/regex.c?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/regex.c used in: Step 683
/* Extended regular expression matching and search library.
   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#if defined(_AIX)
#pragma alloca
#else
# if !defined(alloca)           /* predefined by HP cc +Olibcalls */
#  ifdef __GNUC__
#   define alloca(size) __builtin_alloca(size)
#  else
#   if HAVE_ALLOCA_H
#    include <alloca.h>
#   else
#    if defined(__hpux)
    void *alloca ();
#    else
#     if !defined(__OS2__) && !defined(WIN32)
    char *alloca ();
#     else
#      include <malloc.h>       /* OS/2 defines alloca in here */
#     endif
#    endif
#   endif
#  endif
# endif
#endif

#ifdef _LIBC
/* We have to keep the namespace clean.  */
# define regfree(preg) __regfree (preg)
# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
# define regerror(errcode, preg, errbuf, errbuf_size) \
    __regerror(errcode, preg, errbuf, errbuf_size)
# define re_set_registers(bu, re, nu, st, en) \
    __re_set_registers (bu, re, nu, st, en)
# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
    __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
# define re_match(bufp, string, size, pos, regs) \
    __re_match (bufp, string, size, pos, regs)
# define re_search(bufp, string, size, startpos, range, regs) \
    __re_search (bufp, string, size, startpos, range, regs)
# define re_compile_pattern(pattern, length, bufp) \
    __re_compile_pattern (pattern, length, bufp)
# define re_set_syntax(syntax) __re_set_syntax (syntax)
# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
    __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)

# include "../locale/localeinfo.h"
#endif

/* POSIX says that <sys/types.h> must be included (by the caller) before
   <regex.h>.  */
#include <sys/types.h>
#include <regex.h>
#include "regex_internal.h"

#include "regex_internal.c"
#include "regcomp.c"
#include "regexec.c"

/* Binary backward compatibility.  */
#if _LIBC
# include <shlib-compat.h>
# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3)
link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.")
int re_max_failures = 2000;
# endif
#endif

https://git.savannah.gnu.org/cgit/sed.git/tree/lib/obstack.c?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/obstack.c used in: Step 684
/* obstack.c - subroutines used implicitly by object stack macros -*- C -*-
   Copyright (C) 1988,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.

   This file is part of the GNU C Library.  Its master source is NOT part of
   the C library, however.  The master source lives in /gd/gnu/lib.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include "obstack.h"

/* NOTE BEFORE MODIFYING THIS FILE: This version number must be
   incremented whenever callers compiled using an old obstack.h can no
   longer properly call the functions in this obstack.c.  */
#define OBSTACK_INTERFACE_VERSION 1

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself, and the installed library
   supports the same library interface we do.  This code is part of the GNU
   C Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object
   files, it is simpler to just do this in the source for each such file.  */

#include <stdio.h>      /* Random thing to get __GNU_LIBRARY__.  */
#if !defined (_LIBC) && defined (__GNU_LIBRARY__) && __GNU_LIBRARY__ > 1
#include <gnu-versions.h>
#if _GNU_OBSTACK_INTERFACE_VERSION == OBSTACK_INTERFACE_VERSION
#define ELIDE_CODE
#endif
#endif


#ifndef ELIDE_CODE


#if defined (__STDC__) && __STDC__
#define POINTER void *
#else
#define POINTER char *
#endif

/* Determine default alignment.  */
struct fooalign {char x; double d;};
#define DEFAULT_ALIGNMENT  \
  ((PTR_INT_TYPE) ((char *) &((struct fooalign *) 0)->d - (char *) 0))
/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
   But in fact it might be less smart and round addresses to as much as
   DEFAULT_ROUNDING.  So we prepare for it to do that.  */
union fooround {long x; double d;};
#define DEFAULT_ROUNDING (sizeof (union fooround))

#ifdef original_glibc_code
/**//* When we copy a long block of data, this is the unit to do it with. */
/**//* On some machines, copying successive ints does not work; */
/**//* in such a case, redefine COPYING_UNIT to `long' (if that works) */
/**//* or `char' as a last resort.  */
/**/#ifndef COPYING_UNIT
/**/#define COPYING_UNIT int
/**/#endif
#endif

/* The functions allocating more room by calling `obstack_chunk_alloc'
   jump to the handler pointed to by `obstack_alloc_failed_handler'.
   This variable by default points to the internal function
   `print_and_abort'.  */
#if defined (__STDC__) && __STDC__
static void print_and_abort (void);
void (*obstack_alloc_failed_handler) (void) = print_and_abort;
#else
static void print_and_abort ();
void (*obstack_alloc_failed_handler) () = print_and_abort;
#endif

/* Exit value used when `print_and_abort' is used.  */
#if defined __GNU_LIBRARY__ || defined HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
int obstack_exit_failure = EXIT_FAILURE;

/* The non-GNU-C macros copy the obstack into this global variable
   to avoid multiple evaluation.  */

struct obstack *_obstack;

/* Define a macro that either calls functions with the traditional malloc/free
   calling interface, or calls functions with the mmalloc/mfree interface
   (that adds an extra first argument), based on the state of use_extra_arg.
   For free, do not use ?:, since some compilers, like the MIPS compilers,
   do not allow (expr) ? void : void.  */

#if defined (__STDC__) && __STDC__
#define CALL_CHUNKFUN(h, size) \
  (((h) -> use_extra_arg) \
   ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
   : (*(struct _obstack_chunk *(*) (long)) (h)->chunkfun) ((size)))

#define CALL_FREEFUN(h, old_chunk) \
  do { \
    if ((h) -> use_extra_arg) \
      (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
    else \
      (*(void (*) (void *)) (h)->freefun) ((old_chunk)); \
  } while (0)
#else
#define CALL_CHUNKFUN(h, size) \
  (((h) -> use_extra_arg) \
   ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
   : (*(struct _obstack_chunk *(*) ()) (h)->chunkfun) ((size)))

#define CALL_FREEFUN(h, old_chunk) \
  do { \
    if ((h) -> use_extra_arg) \
      (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
    else \
      (*(void (*) ()) (h)->freefun) ((old_chunk)); \
  } while (0)
#endif



/* Initialize an obstack H for use.  Specify chunk size SIZE (0 means default).
   Objects start on multiples of ALIGNMENT (0 means use default).
   CHUNKFUN is the function to use to allocate chunks,
   and FREEFUN the function to free them.

   Return nonzero if successful, zero if out of memory.
   To recover from an out of memory error,
   free up some memory, then call this again.  */

int
_obstack_begin (h, size, alignment, chunkfun, freefun)
     struct obstack *h;
     int size;
     int alignment;
#if defined (__STDC__) && __STDC__
     POINTER (*chunkfun) (long);
     void (*freefun) (void *);
#else
     POINTER (*chunkfun) ();
     void (*freefun) ();
#endif
{
  register struct _obstack_chunk *chunk; /* points to new chunk */

  if (alignment == 0)
    alignment = DEFAULT_ALIGNMENT;
  if (size == 0)
    /* Default size is what GNU malloc can fit in a 4096-byte block.  */
    {
      /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
     Use the values for range checking, because if range checking is off,
     the extra bytes won't be missed terribly, but if range checking is on
     and we used a larger request, a whole extra 4096 bytes would be
     allocated.

     These number are irrelevant to the new GNU malloc.  I suspect it is
     less sensitive to the size of the request.  */
      int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
            + 4 + DEFAULT_ROUNDING - 1)
           & ~(DEFAULT_ROUNDING - 1));
      size = 4096 - extra;
    }

#if defined (__STDC__) && __STDC__
  h->chunkfun = (struct _obstack_chunk * (*)(void *, long)) chunkfun;
  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
#else
  h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
  h->freefun = freefun;
#endif
  h->chunk_size = size;
  h->alignment_mask = alignment - 1;
  h->use_extra_arg = 0;

  chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
  if (!chunk)
    (*obstack_alloc_failed_handler) ();
  h->next_free = h->object_base = chunk->contents;
  h->chunk_limit = chunk->limit
    = (char *) chunk + h->chunk_size;
  chunk->prev = 0;
  /* The initial chunk now contains no empty object.  */
  h->maybe_empty_object = 0;
  h->alloc_failed = 0;
  return 1;
}

int
_obstack_begin_1 (h, size, alignment, chunkfun, freefun, arg)
     struct obstack *h;
     int size;
     int alignment;
#if defined (__STDC__) && __STDC__
     POINTER (*chunkfun) (POINTER, long);
     void (*freefun) (POINTER, POINTER);
#else
     POINTER (*chunkfun) ();
     void (*freefun) ();
#endif
     POINTER arg;
{
  register struct _obstack_chunk *chunk; /* points to new chunk */

  if (alignment == 0)
    alignment = DEFAULT_ALIGNMENT;
  if (size == 0)
    /* Default size is what GNU malloc can fit in a 4096-byte block.  */
    {
      /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
     Use the values for range checking, because if range checking is off,
     the extra bytes won't be missed terribly, but if range checking is on
     and we used a larger request, a whole extra 4096 bytes would be
     allocated.

     These number are irrelevant to the new GNU malloc.  I suspect it is
     less sensitive to the size of the request.  */
      int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
            + 4 + DEFAULT_ROUNDING - 1)
           & ~(DEFAULT_ROUNDING - 1));
      size = 4096 - extra;
    }

#if defined(__STDC__) && __STDC__
  h->chunkfun = (struct _obstack_chunk * (*)(void *,long)) chunkfun;
  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
#else
  h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
  h->freefun = freefun;
#endif
  h->chunk_size = size;
  h->alignment_mask = alignment - 1;
  h->extra_arg = arg;
  h->use_extra_arg = 1;

  chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
  if (!chunk)
    (*obstack_alloc_failed_handler) ();
  h->next_free = h->object_base = chunk->contents;
  h->chunk_limit = chunk->limit
    = (char *) chunk + h->chunk_size;
  chunk->prev = 0;
  /* The initial chunk now contains no empty object.  */
  h->maybe_empty_object = 0;
  h->alloc_failed = 0;
  return 1;
}

/* Allocate a new current chunk for the obstack *H
   on the assumption that LENGTH bytes need to be added
   to the current object, or a new object of length LENGTH allocated.
   Copies any partial object from the end of the old chunk
   to the beginning of the new one.  */

void
_obstack_newchunk (h, length)
     struct obstack *h;
     int length;
{
  register struct _obstack_chunk *old_chunk = h->chunk;
  register struct _obstack_chunk *new_chunk;
  register long new_size;
  register int obj_size = h->next_free - h->object_base;

  /* Compute size for new chunk.  */
  new_size = (obj_size + length) + (obj_size >> 3) + 100;
  if (new_size < h->chunk_size)
    new_size = h->chunk_size;

  /* Allocate and initialize the new chunk.  */
  new_chunk = CALL_CHUNKFUN (h, new_size);
  if (!new_chunk)
    (*obstack_alloc_failed_handler) ();
  h->chunk = new_chunk;
  new_chunk->prev = old_chunk;
  new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;

  _obstack_memcpy(new_chunk->contents, h->object_base, obj_size);

  /* If the object just copied was the only data in OLD_CHUNK, */
  /* free that chunk and remove it from the chain. */
  /* But not if that chunk might contain an empty object.  */
  if (h->object_base == old_chunk->contents && ! h->maybe_empty_object)
    {
      new_chunk->prev = old_chunk->prev;
      CALL_FREEFUN (h, old_chunk);
    }

  h->object_base = new_chunk->contents;
  h->next_free = h->object_base + obj_size;
  /* The new chunk certainly contains no empty object yet.  */
  h->maybe_empty_object = 0;
}

/* Return nonzero if object OBJ has been allocated from obstack H.
   This is here for debugging.
   If you use it in a program, you are probably losing.  */

#if defined (__STDC__) && __STDC__
/* Suppress -Wmissing-prototypes warning.  We don't want to declare this in
   obstack.h because it is just for debugging.  */
int _obstack_allocated_p (struct obstack *h, POINTER obj);
#endif

int
_obstack_allocated_p (h, obj)
     struct obstack *h;
     POINTER obj;
{
  register struct _obstack_chunk *lp;   /* below addr of any objects in this chunk */
  register struct _obstack_chunk *plp;  /* point to previous chunk if any */

  lp = (h)->chunk;
  /* We use >= rather than > since the object cannot be exactly at
     the beginning of the chunk but might be an empty object exactly
     at the end of an adjacent chunk.  */
  while (lp != 0 && ((POINTER) lp >= obj || (POINTER) (lp)->limit < obj))
    {
      plp = lp->prev;
      lp = plp;
    }
  return lp != 0;
}


/* Free objects in obstack H, including OBJ and everything allocate
   more recently than OBJ.  If OBJ is zero, free everything in H.  */

#undef obstack_free

/* This function has two names with identical definitions.
   This is the first one, called from non-ANSI code.  */

void
_obstack_free (h, obj)
     struct obstack *h;
     POINTER obj;
{
  register struct _obstack_chunk *lp;   /* below addr of any objects in this chunk */
  register struct _obstack_chunk *plp;  /* point to previous chunk if any */

  lp = h->chunk;
  /* We use >= because there cannot be an object at the beginning of a chunk.
     But there can be an empty object at that address
     at the end of another chunk.  */
  while (lp != 0 && ((POINTER) lp >= obj || (POINTER) (lp)->limit < obj))
    {
      plp = lp->prev;
      CALL_FREEFUN (h, lp);
      lp = plp;
      /* If we switch chunks, we can't tell whether the new current
     chunk contains an empty object, so assume that it may.  */
      h->maybe_empty_object = 1;
    }
  if (lp)
    {
      h->object_base = h->next_free = (char *) (obj);
      h->chunk_limit = lp->limit;
      h->chunk = lp;
    }
  else if (obj != 0)
    /* obj is not in any of the chunks! */
    abort ();
}

/* This function is used from ANSI code.  */

void
obstack_free (h, obj)
     struct obstack *h;
     POINTER obj;
{
  register struct _obstack_chunk *lp;   /* below addr of any objects in this chunk */
  register struct _obstack_chunk *plp;  /* point to previous chunk if any */

  lp = h->chunk;
  /* We use >= because there cannot be an object at the beginning of a chunk.
     But there can be an empty object at that address
     at the end of another chunk.  */
  while (lp != 0 && ((POINTER) lp >= obj || (POINTER) (lp)->limit < obj))
    {
      plp = lp->prev;
      CALL_FREEFUN (h, lp);
      lp = plp;
      /* If we switch chunks, we can't tell whether the new current
     chunk contains an empty object, so assume that it may.  */
      h->maybe_empty_object = 1;
    }
  if (lp)
    {
      h->object_base = h->next_free = (char *) (obj);
      h->chunk_limit = lp->limit;
      h->chunk = lp;
    }
  else if (obj != 0)
    /* obj is not in any of the chunks! */
    abort ();
}


int
_obstack_memory_used (h)
     struct obstack *h;
{
  register struct _obstack_chunk* lp;
  register int nbytes = 0;

  for (lp = h->chunk; lp != 0; lp = lp->prev)
    {
      nbytes += lp->limit - (char *) lp;
    }
  return nbytes;
}


/* Define the error handler.  */
#ifndef _
# ifdef HAVE_LIBINTL_H
#  include <libintl.h>
#  ifndef _
#   define _(Str) gettext (Str)
#  endif
# else
#  define _(Str) (Str)
# endif
#endif

static void
print_and_abort ()
{
  fputs (_("memory exhausted\n"), stderr);
  exit (obstack_exit_failure);
}


#if 0
/* These are now turned off because the applications do not use it
   and it uses bcopy via obstack_grow, which causes trouble on sysV.  */

/* Now define the functional versions of the obstack macros.
   Define them to simply use the corresponding macros to do the job.  */

#if defined (__STDC__) && __STDC__
/* These function definitions do not work with non-ANSI preprocessors;
   they won't pass through the macro names in parentheses.  */

/* The function names appear in parentheses in order to prevent
   the macro-definitions of the names from being expanded there.  */

POINTER (obstack_base) (obstack)
     struct obstack *obstack;
{
  return obstack_base (obstack);
}

POINTER (obstack_next_free) (obstack)
     struct obstack *obstack;
{
  return obstack_next_free (obstack);
}

int (obstack_object_size) (obstack)
     struct obstack *obstack;
{
  return obstack_object_size (obstack);
}

int (obstack_room) (obstack)
     struct obstack *obstack;
{
  return obstack_room (obstack);
}

int (obstack_make_room) (obstack, length)
     struct obstack *obstack;
     int length;
{
  return obstack_make_room (obstack, length);
}

void (obstack_grow) (obstack, pointer, length)
     struct obstack *obstack;
     POINTER pointer;
     int length;
{
  obstack_grow (obstack, pointer, length);
}

void (obstack_grow0) (obstack, pointer, length)
     struct obstack *obstack;
     POINTER pointer;
     int length;
{
  obstack_grow0 (obstack, pointer, length);
}

void (obstack_1grow) (obstack, character)
     struct obstack *obstack;
     int character;
{
  obstack_1grow (obstack, character);
}

void (obstack_blank) (obstack, length)
     struct obstack *obstack;
     int length;
{
  obstack_blank (obstack, length);
}

void (obstack_1grow_fast) (obstack, character)
     struct obstack *obstack;
     int character;
{
  obstack_1grow_fast (obstack, character);
}

void (obstack_blank_fast) (obstack, length)
     struct obstack *obstack;
     int length;
{
  obstack_blank_fast (obstack, length);
}

POINTER (obstack_finish) (obstack)
     struct obstack *obstack;
{
  return obstack_finish (obstack);
}

POINTER (obstack_alloc) (obstack, length)
     struct obstack *obstack;
     int length;
{
  return obstack_alloc (obstack, length);
}

POINTER (obstack_copy) (obstack, pointer, length)
     struct obstack *obstack;
     POINTER pointer;
     int length;
{
  return obstack_copy (obstack, pointer, length);
}

POINTER (obstack_copy0) (obstack, pointer, length)
     struct obstack *obstack;
     POINTER pointer;
     int length;
{
  return obstack_copy0 (obstack, pointer, length);
}

#endif /* __STDC__ */

#endif /* 0 */

#endif  /* !ELIDE_CODE */

https://git.savannah.gnu.org/cgit/sed.git/tree/lib/obstack.h?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/obstack.h used in: Step 684
/* obstack.h - object stack macros
   Copyright (C) 1988,89,90,91,92,93,94,96,97,98,99 Free Software Foundation, Inc.

   This file is part of the GNU C Library.  Its master source is NOT part of
   the C library, however.  The master source lives in /gd/gnu/lib.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

/* Summary:

All the apparent functions defined here are macros. The idea
is that you would use these pre-tested macros to solve a
very specific set of problems, and they would run fast.
Caution: no side-effects in arguments please!! They may be
evaluated MANY times!!

These macros operate a stack of objects.  Each object starts life
small, and may grow to maturity.  (Consider building a word syllable
by syllable.)  An object can move while it is growing.  Once it has
been "finished" it never changes address again.  So the "top of the
stack" is typically an immature growing object, while the rest of the
stack is of mature, fixed size and fixed address objects.

These routines grab large chunks of memory, using a function you
supply, called `obstack_chunk_alloc'.  On occasion, they free chunks,
by calling `obstack_chunk_free'.  You must define them and declare
them before using any obstack macros.

Each independent stack is represented by a `struct obstack'.
Each of the obstack macros expects a pointer to such a structure
as the first argument.

One motivation for this package is the problem of growing char strings
in symbol tables.  Unless you are "fascist pig with a read-only mind"
--Gosper's immortal quote from HAKMEM item 154, out of context--you
would not like to put any arbitrary upper limit on the length of your
symbols.

In practice this often means you will build many short symbols and a
few long symbols.  At the time you are reading a symbol you don't know
how long it is.  One traditional method is to read a symbol into a
buffer, realloc()ating the buffer every time you try to read a symbol
that is longer than the buffer.  This is beaut, but you still will
want to copy the symbol from the buffer to a more permanent
symbol-table entry say about half the time.

With obstacks, you can work differently.  Use one obstack for all symbol
names.  As you read a symbol, grow the name in the obstack gradually.
When the name is complete, finalize it.  Then, if the symbol exists already,
free the newly read name.

The way we do this is to take a large chunk, allocating memory from
low addresses.  When you want to build a symbol in the chunk you just
add chars above the current "high water mark" in the chunk.  When you
have finished adding chars, because you got to the end of the symbol,
you know how long the chars are, and you can create a new object.
Mostly the chars will not burst over the highest address of the chunk,
because you would typically expect a chunk to be (say) 100 times as
long as an average object.

In case that isn't clear, when we have enough chars to make up
the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed)
so we just point to it where it lies.  No moving of chars is
needed and this is the second win: potentially long strings need
never be explicitly shuffled. Once an object is formed, it does not
change its address during its lifetime.

When the chars burst over a chunk boundary, we allocate a larger
chunk, and then copy the partly formed object from the end of the old
chunk to the beginning of the new larger chunk.  We then carry on
accreting characters to the end of the object as we normally would.

A special macro is provided to add a single char at a time to a
growing object.  This allows the use of register variables, which
break the ordinary 'growth' macro.

Summary:
    We allocate large chunks.
    We carve out one object at a time from the current chunk.
    Once carved, an object never moves.
    We are free to append data of any size to the currently
      growing object.
    Exactly one object is growing in an obstack at any one time.
    You can run one obstack per control block.
    You may have as many control blocks as you dare.
    Because of the way we do it, you can `unwind' an obstack
      back to a previous state. (You may remove objects much
      as you would with a stack.)
*/


/* Don't do the contents of this file more than once.  */

#ifndef _OBSTACK_H
#define _OBSTACK_H 1

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif


/* We use subtraction of (char *) 0 instead of casting to int
   because on word-addressable machines a simple cast to int
   may ignore the byte-within-word field of the pointer.  */

#ifndef __PTR_TO_INT
# define __PTR_TO_INT(P) ((P) - (char *) 0)
#endif

#ifndef __INT_TO_PTR
# define __INT_TO_PTR(P) ((P) + (char *) 0)
#endif

/* We need the type of the resulting object.  If __PTRDIFF_TYPE__ is
   defined, as with GNU C, use that; that way we don't pollute the
   namespace with <stddef.h>'s symbols.  Otherwise, if <stddef.h> is
   available, include it and use ptrdiff_t.  In traditional C, long is
   the best that we can do.  */

#ifdef __PTRDIFF_TYPE__
# define PTR_INT_TYPE __PTRDIFF_TYPE__
#else
# ifdef HAVE_STDDEF_H
#  include <stddef.h>
#  define PTR_INT_TYPE ptrdiff_t
# else
#  define PTR_INT_TYPE long
# endif
#endif

#if defined _LIBC || defined HAVE_STRING_H || defined HAVE_STRINGS_H
# ifdef HAVE_STRINGS_H
#  include <strings.h>
# else
# include <string.h>
# endif
# define _obstack_memcpy(To, From, N) memcpy ((To), (From), (N))
#else
# ifdef memcpy
#  define _obstack_memcpy(To, From, N) memcpy ((To), (From), (N))
# else
#  define _obstack_memcpy(To, From, N) bcopy ((From), (To), (N))
# endif
#endif

struct _obstack_chunk       /* Lives at front of each chunk. */
{
  char  *limit;         /* 1 past end of this chunk */
  struct _obstack_chunk *prev;  /* address of prior chunk or NULL */
  char  contents[4];        /* objects begin here */
};

struct obstack      /* control current object in current chunk */
{
  long  chunk_size;     /* preferred size to allocate chunks in */
  struct _obstack_chunk *chunk; /* address of current struct obstack_chunk */
  char  *object_base;       /* address of object we are building */
  char  *next_free;     /* where to add next char to current object */
  char  *chunk_limit;       /* address of char after current chunk */
  PTR_INT_TYPE temp;        /* Temporary for some macros.  */
  int   alignment_mask;     /* Mask of alignment for each object. */
#if defined __STDC__ && __STDC__
  /* These prototypes vary based on `use_extra_arg', and we use
     casts to the prototypeless function type in all assignments,
     but having prototypes here quiets -Wstrict-prototypes.  */
  struct _obstack_chunk *(*chunkfun) (void *, long);
  void (*freefun) (void *, struct _obstack_chunk *);
  void *extra_arg;      /* first arg for chunk alloc/dealloc funcs */
#else
  struct _obstack_chunk *(*chunkfun) (); /* User's fcn to allocate a chunk.  */
  void (*freefun) ();       /* User's function to free a chunk.  */
  char *extra_arg;      /* first arg for chunk alloc/dealloc funcs */
#endif
  unsigned use_extra_arg:1; /* chunk alloc/dealloc funcs take extra arg */
  unsigned maybe_empty_object:1;/* There is a possibility that the current
                   chunk contains a zero-length object.  This
                   prevents freeing the chunk if we allocate
                   a bigger chunk to replace it. */
  unsigned alloc_failed:1;  /* No longer used, as we now call the failed
                   handler on error, but retained for binary
                   compatibility.  */
};

/* Declare the external functions we use; they are in obstack.c.  */

#if defined __STDC__ && __STDC__
extern void _obstack_newchunk (struct obstack *, int);
extern void _obstack_free (struct obstack *, void *);
extern int _obstack_begin (struct obstack *, int, int,
                void *(*) (long), void (*) (void *));
extern int _obstack_begin_1 (struct obstack *, int, int,
                 void *(*) (void *, long),
                 void (*) (void *, void *), void *);
extern int _obstack_memory_used (struct obstack *);
#else
extern void _obstack_newchunk ();
extern void _obstack_free ();
extern int _obstack_begin ();
extern int _obstack_begin_1 ();
extern int _obstack_memory_used ();
#endif


#if defined __STDC__ && __STDC__

/* Do the function-declarations after the structs
   but before defining the macros.  */

void obstack_init (struct obstack *obstack);

void * obstack_alloc (struct obstack *obstack, int size);

void * obstack_copy (struct obstack *obstack, const void *address, int size);
void * obstack_copy0 (struct obstack *obstack, const void *address, int size);

void obstack_free (struct obstack *obstack, void *block);

void obstack_blank (struct obstack *obstack, int size);

void obstack_grow (struct obstack *obstack, const void *data, int size);
void obstack_grow0 (struct obstack *obstack, const void *data, int size);

void obstack_1grow (struct obstack *obstack, int data_char);
void obstack_ptr_grow (struct obstack *obstack, const void *data);
void obstack_int_grow (struct obstack *obstack, int data);

void * obstack_finish (struct obstack *obstack);

int obstack_object_size (struct obstack *obstack);

int obstack_room (struct obstack *obstack);
void obstack_make_room (struct obstack *obstack, int size);
void obstack_1grow_fast (struct obstack *obstack, int data_char);
void obstack_ptr_grow_fast (struct obstack *obstack, const void *data);
void obstack_int_grow_fast (struct obstack *obstack, int data);
void obstack_blank_fast (struct obstack *obstack, int size);

void * obstack_base (struct obstack *obstack);
void * obstack_next_free (struct obstack *obstack);
int obstack_alignment_mask (struct obstack *obstack);
int obstack_chunk_size (struct obstack *obstack);
int obstack_memory_used (struct obstack *obstack);

#endif /* __STDC__ */

/* Non-ANSI C cannot really support alternative functions for these macros,
   so we do not declare them.  */

/* Error handler called when `obstack_chunk_alloc' failed to allocate
   more memory.  This can be set to a user defined function which
   should either abort gracefully or use longjump - but shouldn't
   return.  The default action is to print a message and abort.  */
#if defined __STDC__ && __STDC__
extern void (*obstack_alloc_failed_handler) (void);
#else
extern void (*obstack_alloc_failed_handler) ();
#endif

/* Exit value used when `print_and_abort' is used.  */
extern int obstack_exit_failure;


/* Pointer to beginning of object being allocated or to be allocated next.
   Note that this might not be the final address of the object
   because a new chunk might be needed to hold the final size.  */

#define obstack_base(h) ((h)->object_base)

/* Size for allocating ordinary chunks.  */

#define obstack_chunk_size(h) ((h)->chunk_size)

/* Pointer to next byte not yet allocated in current chunk.  */

#define obstack_next_free(h)    ((h)->next_free)

/* Mask specifying low bits that should be clear in address of an object.  */

#define obstack_alignment_mask(h) ((h)->alignment_mask)

/* To prevent prototype warnings provide complete argument list in
   standard C version.  */
#if defined __STDC__ && __STDC__

# define obstack_init(h)                    \
  _obstack_begin ((h), 0, 0,                    \
          (void *(*) (long)) obstack_chunk_alloc,   \
          (void (*) (void *)) obstack_chunk_free)

# define obstack_begin(h, size)                 \
  _obstack_begin ((h), (size), 0,               \
          (void *(*) (long)) obstack_chunk_alloc,   \
          (void (*) (void *)) obstack_chunk_free)

# define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \
  _obstack_begin ((h), (size), (alignment),                \
          (void *(*) (long)) (chunkfun),               \
          (void (*) (void *)) (freefun))

# define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \
  _obstack_begin_1 ((h), (size), (alignment),               \
            (void *(*) (void *, long)) (chunkfun),      \
            (void (*) (void *, void *)) (freefun), (arg))

# define obstack_chunkfun(h, newchunkfun) \
  ((h) -> chunkfun = (struct _obstack_chunk *(*)(void *, long)) (newchunkfun))

# define obstack_freefun(h, newfreefun) \
  ((h) -> freefun = (void (*)(void *, struct _obstack_chunk *)) (newfreefun))

#else

# define obstack_init(h)                        \
  _obstack_begin ((h), 0, 0,                        \
          (void *(*) ()) obstack_chunk_alloc,           \
          (void (*) ()) obstack_chunk_free)

# define obstack_begin(h, size)                     \
  _obstack_begin ((h), (size), 0,                   \
          (void *(*) ()) obstack_chunk_alloc,           \
          (void (*) ()) obstack_chunk_free)

# define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \
  _obstack_begin ((h), (size), (alignment),                \
          (void *(*) ()) (chunkfun),                   \
          (void (*) ()) (freefun))

# define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \
  _obstack_begin_1 ((h), (size), (alignment),               \
            (void *(*) ()) (chunkfun),              \
            (void (*) ()) (freefun), (arg))

# define obstack_chunkfun(h, newchunkfun) \
  ((h) -> chunkfun = (struct _obstack_chunk *(*)()) (newchunkfun))

# define obstack_freefun(h, newfreefun) \
  ((h) -> freefun = (void (*)()) (newfreefun))

#endif

#define obstack_1grow_fast(h,achar) (*((h)->next_free)++ = achar)

#define obstack_blank_fast(h,n) ((h)->next_free += (n))

#define obstack_memory_used(h) _obstack_memory_used (h)


#if defined __GNUC__ && defined __STDC__ && __STDC__
/* NextStep 2.0 cc is really gcc 1.93 but it defines __GNUC__ = 2 and
   does not implement __extension__.  But that compiler doesn't define
   __GNUC_MINOR__.  */
# if __GNUC__ < 2 || (__NeXT__ && !__GNUC_MINOR__)
#  define __extension__
# endif

/* For GNU C, if not -traditional,
   we can define these macros to compute all args only once
   without using a global variable.
   Also, we can avoid using the `temp' slot, to make faster code.  */

# define obstack_object_size(OBSTACK)                   \
  __extension__                             \
  ({ struct obstack *__o = (OBSTACK);                   \
     (unsigned) (__o->next_free - __o->object_base); })

# define obstack_room(OBSTACK)                      \
  __extension__                             \
  ({ struct obstack *__o = (OBSTACK);                   \
     (unsigned) (__o->chunk_limit - __o->next_free); })

# define obstack_make_room(OBSTACK,length)              \
__extension__                               \
({ struct obstack *__o = (OBSTACK);                 \
   int __len = (length);                        \
   if (__o->chunk_limit - __o->next_free < __len)           \
     _obstack_newchunk (__o, __len);                    \
   (void) 0; })

# define obstack_empty_p(OBSTACK)                   \
  __extension__                             \
  ({ struct obstack *__o = (OBSTACK);                   \
     (__o->chunk->prev == 0 && __o->next_free - __o->chunk->contents == 0); })

# define obstack_grow(OBSTACK,where,length)             \
__extension__                               \
({ struct obstack *__o = (OBSTACK);                 \
   int __len = (length);                        \
   if (__o->next_free + __len > __o->chunk_limit)           \
     _obstack_newchunk (__o, __len);                    \
   _obstack_memcpy (__o->next_free, (where), __len);            \
   __o->next_free += __len;                     \
   (void) 0; })

# define obstack_grow0(OBSTACK,where,length)                \
__extension__                               \
({ struct obstack *__o = (OBSTACK);                 \
   int __len = (length);                        \
   if (__o->next_free + __len + 1 > __o->chunk_limit)           \
     _obstack_newchunk (__o, __len + 1);                \
   _obstack_memcpy (__o->next_free, (where), __len);            \
   __o->next_free += __len;                     \
   *(__o->next_free)++ = 0;                     \
   (void) 0; })

# define obstack_1grow(OBSTACK,datum)                   \
__extension__                               \
({ struct obstack *__o = (OBSTACK);                 \
   if (__o->next_free + 1 > __o->chunk_limit)               \
     _obstack_newchunk (__o, 1);                    \
   *(__o->next_free)++ = (datum);                   \
   (void) 0; })

/* These assume that the obstack alignment is good enough for pointers
   or ints, and that the data added so far to the current object
   shares that much alignment.  */

# define obstack_ptr_grow(OBSTACK,datum)                \
__extension__                               \
({ struct obstack *__o = (OBSTACK);                 \
   if (__o->next_free + sizeof (void *) > __o->chunk_limit)     \
     _obstack_newchunk (__o, sizeof (void *));              \
   *((void **)__o->next_free)++ = (datum);              \
   (void) 0; })

# define obstack_int_grow(OBSTACK,datum)                \
__extension__                               \
({ struct obstack *__o = (OBSTACK);                 \
   if (__o->next_free + sizeof (int) > __o->chunk_limit)        \
     _obstack_newchunk (__o, sizeof (int));             \
   *((int *)__o->next_free)++ = (datum);                \
   (void) 0; })

# define obstack_ptr_grow_fast(h,aptr)                  \
  (*((void **) (h)->next_free)++ = (aptr))

# define obstack_int_grow_fast(h,aint)                  \
  (*((int *) (h)->next_free)++ = (aint))

# define obstack_blank(OBSTACK,length)                  \
__extension__                               \
({ struct obstack *__o = (OBSTACK);                 \
   int __len = (length);                        \
   if (__o->chunk_limit - __o->next_free < __len)           \
     _obstack_newchunk (__o, __len);                    \
   __o->next_free += __len;                     \
   (void) 0; })

# define obstack_alloc(OBSTACK,length)                  \
__extension__                               \
({ struct obstack *__h = (OBSTACK);                 \
   obstack_blank (__h, (length));                   \
   obstack_finish (__h); })

# define obstack_copy(OBSTACK,where,length)             \
__extension__                               \
({ struct obstack *__h = (OBSTACK);                 \
   obstack_grow (__h, (where), (length));               \
   obstack_finish (__h); })

# define obstack_copy0(OBSTACK,where,length)                \
__extension__                               \
({ struct obstack *__h = (OBSTACK);                 \
   obstack_grow0 (__h, (where), (length));              \
   obstack_finish (__h); })

/* The local variable is named __o1 to avoid a name conflict
   when obstack_blank is called.  */
# define obstack_finish(OBSTACK)                    \
__extension__                               \
({ struct obstack *__o1 = (OBSTACK);                    \
   void *value;                             \
   value = (void *) __o1->object_base;                  \
   if (__o1->next_free == value)                    \
     __o1->maybe_empty_object = 1;                  \
   __o1->next_free                          \
     = __INT_TO_PTR ((__PTR_TO_INT (__o1->next_free)+__o1->alignment_mask)\
             & ~ (__o1->alignment_mask));           \
   if (__o1->next_free - (char *)__o1->chunk                \
       > __o1->chunk_limit - (char *)__o1->chunk)           \
     __o1->next_free = __o1->chunk_limit;               \
   __o1->object_base = __o1->next_free;                 \
   value; })

# define obstack_free(OBSTACK, OBJ)                 \
__extension__                               \
({ struct obstack *__o = (OBSTACK);                 \
   void *__obj = (OBJ);                         \
   if (__obj > (void *)__o->chunk && __obj < (void *)__o->chunk_limit)  \
     __o->next_free = __o->object_base = (char *)__obj;         \
   else (obstack_free) (__o, __obj); })


#else /* not __GNUC__ or not __STDC__ */

# define obstack_object_size(h) \
 (unsigned) ((h)->next_free - (h)->object_base)

# define obstack_room(h)        \
 (unsigned) ((h)->chunk_limit - (h)->next_free)

# define obstack_empty_p(h) \
 ((h)->chunk->prev == 0 && (h)->next_free - (h)->chunk->contents == 0)

/* Note that the call to _obstack_newchunk is enclosed in (..., 0)
   so that we can avoid having void expressions
   in the arms of the conditional expression.
   Casting the third operand to void was tried before,
   but some compilers won't accept it.  */

# define obstack_make_room(h,length)                    \
( (h)->temp = (length),                         \
  (((h)->next_free + (h)->temp > (h)->chunk_limit)          \
   ? (_obstack_newchunk ((h), (h)->temp), 0) : 0))

# define obstack_grow(h,where,length)                   \
( (h)->temp = (length),                         \
  (((h)->next_free + (h)->temp > (h)->chunk_limit)          \
   ? (_obstack_newchunk ((h), (h)->temp), 0) : 0),          \
  _obstack_memcpy ((h)->next_free, (where), (h)->temp),         \
  (h)->next_free += (h)->temp)

# define obstack_grow0(h,where,length)                  \
( (h)->temp = (length),                         \
  (((h)->next_free + (h)->temp + 1 > (h)->chunk_limit)          \
   ? (_obstack_newchunk ((h), (h)->temp + 1), 0) : 0),          \
  _obstack_memcpy ((h)->next_free, (where), (h)->temp),         \
  (h)->next_free += (h)->temp,                      \
  *((h)->next_free)++ = 0)

# define obstack_1grow(h,datum)                     \
( (((h)->next_free + 1 > (h)->chunk_limit)              \
   ? (_obstack_newchunk ((h), 1), 0) : 0),              \
  (*((h)->next_free)++ = (datum)))

# define obstack_ptr_grow(h,datum)                  \
( (((h)->next_free + sizeof (char *) > (h)->chunk_limit)        \
   ? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0),        \
  (*((const char **) (((h)->next_free+=sizeof(char *))-sizeof(char *))) = (datum)))

# define obstack_int_grow(h,datum)                  \
( (((h)->next_free + sizeof (int) > (h)->chunk_limit)           \
   ? (_obstack_newchunk ((h), sizeof (int)), 0) : 0),           \
  (*((int *) (((h)->next_free+=sizeof(int))-sizeof(int))) = (datum)))

# define obstack_ptr_grow_fast(h,aptr)                  \
  (*((const char **) (h)->next_free)++ = (aptr))

# define obstack_int_grow_fast(h,aint)                  \
  (*((int *) (h)->next_free)++ = (aint))

# define obstack_blank(h,length)                    \
( (h)->temp = (length),                         \
  (((h)->chunk_limit - (h)->next_free < (h)->temp)          \
   ? (_obstack_newchunk ((h), (h)->temp), 0) : 0),          \
  ((h)->next_free += (h)->temp))

# define obstack_alloc(h,length)                    \
 (obstack_blank ((h), (length)), obstack_finish ((h)))

# define obstack_copy(h,where,length)                   \
 (obstack_grow ((h), (where), (length)), obstack_finish ((h)))

# define obstack_copy0(h,where,length)                  \
 (obstack_grow0 ((h), (where), (length)), obstack_finish ((h)))

# define obstack_finish(h)                          \
( ((h)->next_free == (h)->object_base                   \
   ? (((h)->maybe_empty_object = 1), 0)                 \
   : 0),                                \
  (h)->temp = __PTR_TO_INT ((h)->object_base),              \
  (h)->next_free                            \
    = __INT_TO_PTR ((__PTR_TO_INT ((h)->next_free)+(h)->alignment_mask) \
            & ~ ((h)->alignment_mask)),             \
  (((h)->next_free - (char *) (h)->chunk                \
    > (h)->chunk_limit - (char *) (h)->chunk)               \
   ? ((h)->next_free = (h)->chunk_limit) : 0),              \
  (h)->object_base = (h)->next_free,                    \
  __INT_TO_PTR ((h)->temp))

# if defined __STDC__ && __STDC__
#  define obstack_free(h,obj)                       \
( (h)->temp = (char *) (obj) - (char *) (h)->chunk,         \
  (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\
   ? (int) ((h)->next_free = (h)->object_base               \
        = (h)->temp + (char *) (h)->chunk)              \
   : (((obstack_free) ((h), (h)->temp + (char *) (h)->chunk), 0), 0)))
# else
#  define obstack_free(h,obj)                       \
( (h)->temp = (char *) (obj) - (char *) (h)->chunk,         \
  (((h)->temp > 0 && (h)->temp < (h)->chunk_limit - (char *) (h)->chunk)\
   ? (int) ((h)->next_free = (h)->object_base               \
        = (h)->temp + (char *) (h)->chunk)              \
   : (_obstack_free ((h), (h)->temp + (char *) (h)->chunk), 0)))
# endif

#endif /* not __GNUC__ or not __STDC__ */

#ifdef __cplusplus
}   /* C++ */
#endif

#endif /* obstack.h */

https://git.savannah.gnu.org/cgit/sed.git/tree/lib/strverscmp.c?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/strverscmp.c used in: Step 685
/* Compare strings while treating digits characters numerically.
   Copyright (C) 1997, 2000, 2002 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   Contributed by Jean-François Bignolles <bignolle@ecoledoc.ibp.fr>, 1997.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <string.h>
#include <ctype.h>

/* states: S_N: normal, S_I: comparing integral part, S_F: comparing
           fractional parts, S_Z: idem but with leading Zeroes only */
#define S_N    0x0
#define S_I    0x4
#define S_F    0x8
#define S_Z    0xC

/* result_type: CMP: return diff; LEN: compare using len_diff/diff */
#define CMP    2
#define LEN    3


/* ISDIGIT differs from isdigit, as follows:
   - Its arg may be any int or unsigned int; it need not be an unsigned char.
   - It's guaranteed to evaluate its argument exactly once.
   - It's typically faster.
   POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
   ISDIGIT_LOCALE unless it's important to use the locale's definition
   of `digit' even when the host does not conform to POSIX.  */
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)

#undef __strverscmp
#undef strverscmp

#ifndef weak_alias
# define __strverscmp strverscmp
#endif

/* Compare S1 and S2 as strings holding indices/version numbers,
   returning less than, equal to or greater than zero if S1 is less than,
   equal to or greater than S2 (for more info, see the texinfo doc).
*/

int
__strverscmp (const char *s1, const char *s2)
{
  const unsigned char *p1 = (const unsigned char *) s1;
  const unsigned char *p2 = (const unsigned char *) s2;
  unsigned char c1, c2;
  int state;
  int diff;

  /* Symbol(s)    0       [1-9]   others  (padding)
     Transition   (10) 0  (01) d  (00) x  (11) -   */
  static const unsigned int next_state[] =
  {
      /* state    x    d    0    - */
      /* S_N */  S_N, S_I, S_Z, S_N,
      /* S_I */  S_N, S_I, S_I, S_I,
      /* S_F */  S_N, S_F, S_F, S_F,
      /* S_Z */  S_N, S_F, S_Z, S_Z
  };

  static const int result_type[] =
  {
      /* state   x/x  x/d  x/0  x/-  d/x  d/d  d/0  d/-
                 0/x  0/d  0/0  0/-  -/x  -/d  -/0  -/- */

      /* S_N */  CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
                 CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
      /* S_I */  CMP, -1,  -1,  CMP,  1,  LEN, LEN, CMP,
                  1,  LEN, LEN, CMP, CMP, CMP, CMP, CMP,
      /* S_F */  CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
                 CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
      /* S_Z */  CMP,  1,   1,  CMP, -1,  CMP, CMP, CMP,
                 -1,  CMP, CMP, CMP
  };

  if (p1 == p2)
    return 0;

  c1 = *p1++;
  c2 = *p2++;
  /* Hint: '0' is a digit too.  */
  state = S_N | ((c1 == '0') + (ISDIGIT (c1) != 0));

  while ((diff = c1 - c2) == 0 && c1 != '\0')
    {
      state = next_state[state];
      c1 = *p1++;
      c2 = *p2++;
      state |= (c1 == '0') + (ISDIGIT (c1) != 0);
    }

  state = result_type[state << 2 | ((c2 == '0') + (ISDIGIT (c2) != 0))];

  switch (state)
    {
    case CMP:
      return diff;

    case LEN:
      while (ISDIGIT (*p1++))
    if (!ISDIGIT (*p2++))
      return 1;

      return ISDIGIT (*p2) ? -1 : diff;

    default:
      return state;
    }
}
#ifdef weak_alias
weak_alias (__strverscmp, strverscmp)
#endif

https://git.savannah.gnu.org/cgit/sed.git/tree/lib/mkstemp.c?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/mkstemp.c used in: Step 686
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_STRINGS_H
# include <strings.h>
#else
# include <string.h>
#endif /* HAVE_STRINGS_H */

#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif /* HAVE_STDLIB_H */

#ifdef HAVE_SYS_FILE_H
# include <sys/file.h>
#endif /* HAVE_SYS_FILE_H */

#ifdef HAVE_IO_H
# include <io.h>
#endif /* HAVE_IO_H */

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif /* HAVE_FCNTL_H */

#include <limits.h>
#include <errno.h>

/* Generate a unique temporary file name from template.  The last six characters of
   template must be XXXXXX and these are replaced with a string that makes the
   filename unique. */

int
mkstemp (template)
     char *template;
{
  int i, j, n, fd;
  char *data = template + strlen(template) - 6;

  if (data < template) {
    errno = EINVAL;
    return -1;
  }

  for (n = 0; n <= 5; n++)
    if (data[n] != 'X') {
      errno = EINVAL;
      return -1;
    }

  for (i = 0; i < INT_MAX; i++) {
    j = i ^ 827714841;             /* Base 36 DOSSUX :-) */
    for (n = 5; n >= 0; n--) {
      data[n] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" [j % 36];
      j /= 36;
    }

    fd = open (template, O_CREAT|O_EXCL|O_RDWR, 0600);
    if (fd != -1)
      return fd;
  }
    
  errno = EEXIST;
  return -1;
}

https://git.savannah.gnu.org/cgit/sed.git/tree/sed/compile.c?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/sed/compile.c used in: Step 688
/*  GNU SED, a batch stream editor.
    Copyright (C) 1989,90,91,92,93,94,95,98,99,2002,2003
    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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */

/* compile.c: translate sed source into internal form */

#include "sed.h"
#include "strverscmp.h"
#include <stdio.h>
#include <ctype.h>

#ifdef HAVE_STRINGS_H
# include <strings.h>
# ifdef HAVE_MEMORY_H
#  include <memory.h>
# endif
#else
# include <string.h>
#endif /* HAVE_STRINGS_H */

#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#include <obstack.h>

extern flagT no_default_output;
extern flagT POSIXLY_CORRECT;



#define YMAP_LENGTH     256 /*XXX shouldn't this be (UCHAR_MAX+1)?*/
#define VECTOR_ALLOC_INCREMENT  40

/* let's not confuse text editors that have only dumb bracket-matching... */
#define OPEN_BRACKET    '['
#define CLOSE_BRACKET   ']'
#define OPEN_BRACE  '{'
#define CLOSE_BRACE '}'

struct prog_info {
  /* When we're reading a script command from a string, `prog.base'
     points to the first character in the string, 'prog.cur' points
     to the current character in the string, and 'prog.end' points
     to the end of the string.  This allows us to compile script
     strings that contain nulls. */
  const unsigned char *base;
  const unsigned char *cur;
  const unsigned char *end;

  /* This is the current script file.  If it is NULL, we are reading
     from a string stored at `prog.cur' instead.  If both `prog.file'
     and `prog.cur' are NULL, we're in trouble! */
  FILE *file;
};

/* Information used to give out useful and informative error messages. */
struct error_info {
  /* This is the name of the current script file. */
  const char *name;

  /* This is the number of the current script line that we're compiling. */
  countT line;

  /* This is the index of the "-e" expressions on the command line. */
  countT string_expr_count;
};


/* Label structure used to resolve GOTO's, labels, and block beginnings. */
struct sed_label {
  countT v_index;       /* index of vector element being referenced */
  char *name;           /* NUL-terminated name of the label */
  struct error_info err_info;   /* track where `{}' blocks start */
  struct sed_label *next;   /* linked list (stack) */
};

struct special_files {
  char *name;
  FILE **pfp;
};

FILE *my_stdin, *my_stdout, *my_stderr;
struct special_files special_files[] = {
  { "/dev/stdin", &my_stdin },
  { "/dev/stdout", &my_stdout },
  { "/dev/stderr", &my_stderr },
  { NULL, NULL }
};

/* This structure tracks files opened by the `w' and `s///w' commands
   so that they may all be closed cleanly at normal program termination.
   Those marked as `special' are not closed. */
struct fp_list {
    char *name;
    int special;
    FILE *fp;
    struct fp_list *link;
  };



/* Where we are in the processing of the input. */
static struct prog_info prog;
static struct error_info cur_input;

/* Information about labels and jumps-to-labels.  This is used to do
   the required backpatching after we have compiled all the scripts. */
static struct sed_label *jumps = NULL;
static struct sed_label *labels = NULL;

/* We wish to detect #n magic only in the first input argument;
   this flag tracks when we have consumed the first file of input. */
static flagT first_script = TRUE;

/* Allow for scripts like "sed -e 'i\' -e foo": */
static struct buffer *pending_text = NULL;
static struct text_buf *old_text_buf = NULL;

/* Information about block start positions.  This is used to backpatch
   block end positions. */
static struct sed_label *blocks = NULL;

/* Use an obstack for compilation. */
static struct obstack obs;

/* Various error messages we may want to print */
static const char errors[] =
  "Multiple `!'s\0"
  "Unexpected `,'\0"
  "Cannot use +N or ~N as first address\0"
  "Unmatched `{'\0"
  "Unexpected `}'\0"
  "Extra characters after command\0"
  "Expected \\ after `a', `c' or `i'\0"
  "`}' doesn't want any addresses\0"
  ": doesn't want any addresses\0"
  "Comments don't accept any addresses\0"
  "Missing command\0"
  "Command only uses one address\0"
  "Unterminated address regex\0"
  "Unterminated `s' command\0"
  "Unterminated `y' command\0"
  "Unknown option to `s'\0"
  "multiple `p' options to `s' command\0"
  "multiple `g' options to `s' command\0"
  "multiple number options to `s' command\0"
  "number option to `s' command may not be zero\0"
  "strings for y command are different lengths\0"
  "expected newer version of sed";

#define BAD_BANG (errors)
#define BAD_COMMA (BAD_BANG + sizeof(N_("Multiple `!'s")))
#define BAD_PLUS (BAD_COMMA + sizeof(N_("Unexpected `,'")))
#define EXCESS_OPEN_BRACE (BAD_PLUS + sizeof(N_("Cannot use +N or ~N as first address")))
#define EXCESS_CLOSE_BRACE (EXCESS_OPEN_BRACE + sizeof(N_("Unmatched `{'")))
#define EXCESS_JUNK (EXCESS_CLOSE_BRACE + sizeof(N_("Unexpected `}'")))
#define EXPECTED_SLASH (EXCESS_JUNK + sizeof(N_("Extra characters after command")))
#define NO_CLOSE_BRACE_ADDR (EXPECTED_SLASH + sizeof(N_("Expected \\ after `a', `c' or `i'")))
#define NO_COLON_ADDR (NO_CLOSE_BRACE_ADDR + sizeof(N_("`}' doesn't want any addresses")))
#define NO_SHARP_ADDR (NO_COLON_ADDR + sizeof(N_(": doesn't want any addresses")))
#define NO_COMMAND (NO_SHARP_ADDR + sizeof(N_("Comments don't accept any addresses")))
#define ONE_ADDR (NO_COMMAND + sizeof(N_("Missing command")))
#define UNTERM_ADDR_RE (ONE_ADDR + sizeof(N_("Command only uses one address")))
#define UNTERM_S_CMD (UNTERM_ADDR_RE + sizeof(N_("Unterminated address regex")))
#define UNTERM_Y_CMD (UNTERM_S_CMD + sizeof(N_("Unterminated `s' command")))
#define UNKNOWN_S_OPT (UNTERM_Y_CMD + sizeof(N_("Unterminated `y' command")))
#define EXCESS_P_OPT (UNKNOWN_S_OPT + sizeof(N_("Unknown option to `s'")))
#define EXCESS_G_OPT (EXCESS_P_OPT + sizeof(N_("multiple `p' options to `s' command")))
#define EXCESS_N_OPT (EXCESS_G_OPT + sizeof(N_("multiple `g' options to `s' command")))
#define ZERO_N_OPT (EXCESS_N_OPT + sizeof(N_("multiple number options to `s' command")))
#define Y_CMD_LEN (ZERO_N_OPT + sizeof(N_("number option to `s' command may not be zero")))
#define ANCIENT_VERSION (Y_CMD_LEN + sizeof(N_("strings for y command are different lengths")))
#define END_ERRORS (ANCIENT_VERSION + sizeof(N_("expected newer version of sed")))

static struct fp_list *file_read = NULL;
static struct fp_list *file_write = NULL;



/* Read the next character from the program.  Return EOF if there isn't
   anything to read.  Keep cur_input.line up to date, so error messages
   can be meaningful. */
static int inchar P_((void));
static int
inchar()
{
  int ch = EOF;

  if (prog.cur)
    {
      if (prog.cur < prog.end)
    ch = *prog.cur++;
    }
  else if (prog.file)
    {
      if (!feof(prog.file))
    ch = getc(prog.file);
    }
  if (ch == '\n')
    ++cur_input.line;
  return ch;
}

/* unget `ch' so the next call to inchar will return it.   */
static void savchar P_((int ch));
static void
savchar(ch)
  int ch;
{
  if (ch == EOF)
    return;
  if (ch == '\n' && cur_input.line > 0)
    --cur_input.line;
  if (prog.cur)
    {
      if (prog.cur <= prog.base || *--prog.cur != ch)
    panic(_("Called savchar() with unexpected pushback (%x)"),
          CAST(unsigned char)ch);
    }
  else
    ungetc(ch, prog.file);
}

/* Read the next non-blank character from the program.  */
static int in_nonblank P_((void));
static int
in_nonblank()
{
  int ch;
  do
    ch = inchar();
    while (ISBLANK(ch));
  return ch;
}

/* Read an integer value from the program.  */
static countT in_integer P_((int ch));
static countT
in_integer(ch)
  int ch;
{
  countT num = 0;

  while (ISDIGIT(ch))
    {
      num = num * 10 + ch - '0';
      ch = inchar();
    }
  savchar(ch);
  return num;
}

static int add_then_next P_((struct buffer *b, int ch));
static int
add_then_next(b, ch)
  struct buffer *b;
  int ch;
{
  add1_buffer(b, ch);
  return inchar();
}

static char * convert_number P_((char *, char *, const char *, int, int, int));
static char *
convert_number(result, buf, bufend, base, maxdigits, default_char)
  char *result;
  char *buf;
  const char *bufend;
  int base;
  int maxdigits;
  int default_char;
{
  int n = 0;
  char *p;

  for (p=buf; p < bufend && maxdigits-- > 0; ++p)
    {
      int d = -1;
      switch (*p)
    {
    case '0': d = 0x0; break;
    case '1': d = 0x1; break;
    case '2': d = 0x2; break;
    case '3': d = 0x3; break;
    case '4': d = 0x4; break;
    case '5': d = 0x5; break;
    case '6': d = 0x6; break;
    case '7': d = 0x7; break;
    case '8': d = 0x8; break;
    case '9': d = 0x9; break;
    case 'A': case 'a': d = 0xa; break;
    case 'B': case 'b': d = 0xb; break;
    case 'C': case 'c': d = 0xc; break;
    case 'D': case 'd': d = 0xd; break;
    case 'E': case 'e': d = 0xe; break;
    case 'F': case 'f': d = 0xf; break;
    }
      if (d < 0 || base <= d)
    break;
      n = n * base + d;
    }
  if (p == buf)
    *result = default_char;
  else
    *result = n;
  return p;
}



/* Read in a filename for a `r', `w', or `s///w' command. */
static struct buffer *read_filename P_((void));
static struct buffer *
read_filename()
{
  struct buffer *b;
  int ch;

  b = init_buffer();
  ch = in_nonblank();
  while (ch != EOF && ch != '\n')
    {
#if 0 /*XXX ZZZ 1998-09-12 kpp: added, then had second thoughts*/
      if (!POSIXLY_CORRECT)
    if (ch == ';' || ch == CLOSE_BRACE || ch == '#')
      {
        savchar(ch);
        break;
      }
#endif
      add1_buffer(b, ch);
      ch = inchar();
    }
  add1_buffer(b, '\0');
  return b;
}

static FILE *get_openfile P_((struct fp_list **file_ptrs, char *mode, flagT fail));
static FILE *
get_openfile(file_ptrs, mode, fail)
     struct fp_list **file_ptrs;
     char *mode;
     flagT fail;
{
  struct buffer *b;
  char *file_name;
  struct fp_list *p;
  int is_stderr;

  b = read_filename();
  file_name = get_buffer(b);
  for (p=*file_ptrs; p; p=p->link)
    if (strcmp(p->name, file_name) == 0)
      break;

  if (!p)
    {
      FILE *fp = NULL;
      if (!POSIXLY_CORRECT)
    {
      /* Check whether it is a special file (stdin, stdout or stderr) */
      struct special_files *special = special_files;
          
      /* std* sometimes are not constants, so they
         cannot be used in the initializer for special_files */
      my_stdin = stdin; my_stdout = stdout; my_stderr = stderr;
      for (special = special_files; special->name; special++)
        if (strcmp(special->name, file_name) == 0)
          {
        fp = *special->pfp;
        break;
          }
    }

      p = OB_MALLOC(&obs, 1, struct fp_list);
      p->name = ck_strdup(file_name);
      p->special = fp != NULL;
      if (!fp)
    fp = ck_fopen(p->name, mode, fail);
      p->fp = fp;
      p->link = *file_ptrs;
      *file_ptrs = p;
    }
  free_buffer(b);
  return p->fp;
}



static struct sed_cmd *next_cmd_entry P_((struct vector **vectorp));
static struct sed_cmd *
next_cmd_entry(vectorp)
  struct vector **vectorp;
{
  struct sed_cmd *cmd;
  struct vector *v;

  v = *vectorp;
  if (v->v_length == v->v_allocated)
    {
      v->v_allocated += VECTOR_ALLOC_INCREMENT;
      v->v = REALLOC(v->v, v->v_allocated, struct sed_cmd);
    }

  cmd = v->v + v->v_length;
  cmd->a1 = NULL;
  cmd->a2 = NULL;
  cmd->a1_matched = FALSE;
  cmd->addr_bang = FALSE;
  cmd->cmd = '\0';  /* something invalid, to catch bugs early */

  *vectorp  = v;
  return cmd;
}

static int snarf_char_class P_((struct buffer *b));
static int
snarf_char_class(b)
  struct buffer *b;
{
  int ch;

  ch = inchar();
  if (ch == '^')
    ch = add_then_next(b, ch);
  if (ch == CLOSE_BRACKET)
    ch = add_then_next(b, ch);
  while (ch != EOF && ch != '\n' && ch != CLOSE_BRACKET)
    {
      if (ch == OPEN_BRACKET)
    {
      int prev;
      int delim = ch = add_then_next(b, ch);

      if (delim != '.'  &&  delim != ':'  &&  delim != '=')
        continue; /* bypass the add_then_next() call at bottom of loop */
      for (prev=ch=add_then_next(b, ch);
          !(ch==CLOSE_BRACKET && prev==delim); ch=add_then_next(b, ch))
        {
          if (ch == EOF || ch == '\n')
        return ch;
          prev = ch;
        }
    }
#ifndef REG_PERL
      else if (ch == '\\')
    {
      ch = inchar();
      if (ch == EOF)
        break;
      if (ch != 'n' && ch != '\n')
        {
          add1_buffer(b, '\\');
          continue; /* bypass the add_then_next() call at bottom of loop */
        }
      ch = '\n';
    }
#endif
      ch = add_then_next(b, ch);
    }

  return ch;
}

static struct buffer *match_slash P_((int slash, flagT regex, flagT keep_back));
static struct buffer *
match_slash(slash, regex, keep_backwhack)
  int slash;
  flagT regex;
  flagT keep_backwhack;
{
  struct buffer *b;
  int ch;

  b = init_buffer();
  while ((ch = inchar()) != EOF && ch != '\n' && ch != slash)
    {
      if (ch == '\\')
    {
      ch = inchar();
      if (ch == EOF)
        break;
#ifndef REG_PERL
      else if (ch == 'n' && regex)
        ch = '\n';
#endif
      else if (ch != '\n' && (ch != slash || keep_backwhack))
        add1_buffer(b, '\\');
    }
      else if (ch == OPEN_BRACKET && regex)
    {
      add1_buffer(b, ch);
      ch = snarf_char_class(b);
      if (ch != CLOSE_BRACKET)
        break;
    }
      add1_buffer(b, ch);
    }
  if (ch == slash)
    return b;

  if (ch == '\n')
    savchar(ch);    /* for proper line number in error report */
  free_buffer(b);
  return NULL;
}

static flagT mark_subst_opts P_((struct subst *cmd));
static flagT
mark_subst_opts(cmd)
  struct subst *cmd;
{
  int flags = 0;
  int ch;

  cmd->global = FALSE;
  cmd->print = FALSE;
  cmd->eval = FALSE;
  cmd->numb = 0;
  cmd->fp = NULL;

  for (;;)
    switch ( (ch = in_nonblank()) )
      {
      case 'i': /* GNU extension */
      case 'I': /* GNU extension */
    flags |= REG_ICASE;
    break;

#ifdef REG_PERL
      case 's': /* GNU extension */
      case 'S': /* GNU extension */
    if (extended_regexp_flags & REG_PERL)
      flags |= REG_DOTALL;
    break;

      case 'x': /* GNU extension */
      case 'X': /* GNU extension */
    if (extended_regexp_flags & REG_PERL)
      flags |= REG_EXTENDED;
    break;
#endif

      case 'm': /* GNU extension */
      case 'M': /* GNU extension */
    flags |= REG_NEWLINE;
    break;

      case 'e':
    cmd->eval = TRUE;
    break;

      case 'p':
    if (cmd->print)
      bad_prog(_(EXCESS_P_OPT));
    cmd->print |= (TRUE << cmd->eval); /* 1=before eval, 2=after */
    break;

      case 'g':
    if (cmd->global)
      bad_prog(_(EXCESS_G_OPT));
    cmd->global = TRUE;
    break;

      case 'w':
    cmd->fp = get_openfile(&file_write, "w", TRUE);
    return flags;

      case '0': case '1': case '2': case '3': case '4':
      case '5': case '6': case '7': case '8': case '9':
    if (cmd->numb)
      bad_prog(_(EXCESS_N_OPT));
    cmd->numb = in_integer(ch);
    if (!cmd->numb)
      bad_prog(_(ZERO_N_OPT));
    break;

      case CLOSE_BRACE:
      case '#':
    savchar(ch);
    /* Fall Through */
      case EOF:
      case '\n':
      case ';':
    return flags;

      case '\r':
    if (inchar() == '\n')
      return flags;
    /* FALLTHROUGH */

      default:
    bad_prog(_(UNKNOWN_S_OPT));
    /*NOTREACHED*/
      }
}



/* read in a label for a `:', `b', or `t' command */
static char *read_label P_((void));
static char *
read_label()
{
  struct buffer *b;
  int ch;
  char *ret;

  b = init_buffer();
  ch = in_nonblank();

  while (ch != EOF && ch != '\n'
     && !ISBLANK(ch) && ch != ';' && ch != CLOSE_BRACE && ch != '#')
    {
      add1_buffer(b, ch);
      ch = inchar();
    }
  savchar(ch);
  add1_buffer(b, '\0');
  ret = ck_strdup(get_buffer(b));
  free_buffer(b);
  return ret;
}

/* Store a label (or label reference) created by a `:', `b', or `t'
   command so that the jump to/from the label can be backpatched after
   compilation is complete, or a reference created by a `{' to be
   backpatched when the corresponding `}' is found.  */
static struct sed_label *setup_label
  P_((struct sed_label *, countT, char *, const struct error_info *));
static struct sed_label *
setup_label(list, idx, name, err_info)
  struct sed_label *list;
  countT idx;
  char *name;
  const struct error_info *err_info;
{
  struct sed_label *ret = OB_MALLOC(&obs, 1, struct sed_label);
  ret->v_index = idx;
  ret->name = name;
  if (err_info)
    MEMCPY(&ret->err_info, err_info, sizeof ret->err_info);
  ret->next = list;
  return ret;
}

static struct sed_label *release_label P_((struct sed_label *list_head));
static struct sed_label *
release_label(list_head)
  struct sed_label *list_head;
{
  struct sed_label *ret;

  if (!list_head)
    return NULL;
  ret = list_head->next;

  FREE(list_head->name);

#if 0
  /* We use obstacks */
  FREE(list_head);
#endif
  return ret;
}

static struct replacement *new_replacement P_((char *, size_t,
                           enum replacement_types));
static struct replacement *
new_replacement(text, length, type)
  char *text;
  size_t length;
  enum replacement_types type;
{
  struct replacement *r = OB_MALLOC(&obs, 1, struct replacement);

  r->prefix = text;
  r->prefix_length = length;
  r->subst_id = -1;
  r->repl_type = type;

  /* r-> next = NULL; */
  return r;
}

static void setup_replacement P_((struct subst *, const char *, size_t));
static void
setup_replacement(sub, text, length)
     struct subst *sub;
     const char *text;
     size_t length;
{
  char *base;
  char *p;
  char *text_end;
  enum replacement_types repl_type = repl_asis, save_type = repl_asis;
  struct replacement root;
  struct replacement *tail;

  sub->max_id = 0;
  base = MEMDUP(text, length, char);
  length = normalize_text(base, length);

  text_end = base + length;
  tail = &root;

  for (p=base; p<text_end; ++p)
    {
      if (*p == '\\')
    {
      /* Preceding the backslash may be some literal text: */
      tail = tail->next =
        new_replacement(base, CAST(size_t)(p - base), repl_type);

      repl_type = save_type;

      /* Skip the backslash and look for a numeric back-reference: */
      ++p;
      if (p<text_end)
        switch (*p)
          {
          case '0': case '1': case '2': case '3': case '4': 
          case '5': case '6': case '7': case '8': case '9': 
        tail->subst_id = *p - '0';
        if (sub->max_id < tail->subst_id)
          sub->max_id = tail->subst_id;
        break;

          case 'L':
        repl_type = repl_lowercase;
        save_type = repl_lowercase;
        break;

          case 'U':
        repl_type = repl_uppercase;
        save_type = repl_uppercase;
        break;
        
          case 'E':
        repl_type = repl_asis;
        save_type = repl_asis;
        break;

          case 'l':
        save_type = repl_type;
        repl_type |= repl_lowercase_first;
        break;

          case 'u':
        save_type = repl_type;
        repl_type |= repl_uppercase_first;
        break;
        
          default:
        p[-1] = *p;
        ++tail->prefix_length;
          }

      base = p + 1;
    }
      else if (*p == '&')
    {
      /* Preceding the ampersand may be some literal text: */
      tail = tail->next =
        new_replacement(base, CAST(size_t)(p - base), repl_type);

      repl_type = save_type;
      tail->subst_id = 0;
      base = p + 1;
    }
  }
  /* There may be some trailing literal text: */
  if (base < text_end)
    tail = tail->next =
      new_replacement(base, CAST(size_t)(text_end - base), repl_type);

  tail->next = NULL;
  sub->replacement = root.next;
}

static void read_text P_((struct text_buf *buf, int leadin_ch));
static void
read_text(buf, leadin_ch)
  struct text_buf *buf;
  int leadin_ch;
{
  int ch;

  /* Should we start afresh (as opposed to continue a partial text)? */
  if (buf)
    {
      if (pending_text)
    free_buffer(pending_text);
      pending_text = init_buffer();
      buf->text = NULL;
      buf->text_length = 0;
      old_text_buf = buf;
    }
  /* assert(old_text_buf != NULL); */

  if (leadin_ch == EOF)
    return;
#ifndef NO_INPUT_INDENT
  if (leadin_ch != '\n')
    add1_buffer(pending_text, leadin_ch);
  ch = inchar();
#else /*NO_INPUT_INDENT*/
  if (leadin_ch == '\n')
    ch = in_nonblank();
  else
    {
      add1_buffer(pending_text, leadin_ch);
      ch = inchar();
    }
#endif /*NO_INPUT_INDENT*/

  while (ch!=EOF && ch!='\n')
    {
      if (ch == '\\')
    ch = inchar();
      if (ch == EOF)
    {
      add1_buffer(pending_text, '\n');
      return;
    }
      add1_buffer(pending_text, ch);
#ifdef NO_INPUT_INDENT
      if (ch == '\n')
    ch = in_nonblank();
      else
#endif /*NO_INPUT_INDENT*/
    ch = inchar();
    }
  add1_buffer(pending_text, '\n');

  if (!buf)
    buf = old_text_buf;
  buf->text_length = size_buffer(pending_text);
  buf->text = MEMDUP(get_buffer(pending_text), buf->text_length, char);
  free_buffer(pending_text);
  pending_text = NULL;
}



/* Try to read an address for a sed command.  If it succeeds,
   return non-zero and store the resulting address in `*addr'.
   If the input doesn't look like an address read nothing
   and return zero.  */
static flagT compile_address P_((struct addr *addr, int ch));
static flagT
compile_address(addr, ch)
  struct addr *addr;
  int ch;
{
  addr->addr_type = addr_is_null;
  addr->addr_step = 0;
  addr->addr_number = ~(countT)0;  /* extremely unlikely to ever match */
  addr->addr_regex = NULL;

  if (ch == '/' || ch == '\\')
    {
      int flags = 0;
      struct buffer *b;
      addr->addr_type = addr_is_regex;
      if (ch == '\\')
    ch = inchar();
      if ( !(b = match_slash(ch, TRUE, TRUE)) )
    bad_prog(_(UNTERM_ADDR_RE));

      for(;;)
    {
      ch = in_nonblank();
          switch(ch)
        {
        case 'I':   /* GNU extension */
          flags |= REG_ICASE;
          break;

#ifdef REG_PERL
        case 'S':   /* GNU extension */
          if (extended_regexp_flags & REG_PERL)
        flags |= REG_DOTALL;
          break;

        case 'X':   /* GNU extension */
          if (extended_regexp_flags & REG_PERL)
        flags |= REG_EXTENDED;
          break;
#endif

        case 'M':   /* GNU extension */
          flags |= REG_NEWLINE;
          break;

        default:
          savchar (ch);
          addr->addr_regex = compile_regex (b, flags, 0);
          free_buffer(b);
          return TRUE;
        }
    }
    }
  else if (ISDIGIT(ch))
    {
      addr->addr_number = in_integer(ch);
      addr->addr_type = addr_is_num;
      ch = in_nonblank();
      if (ch != '~')
    {
      savchar(ch);
    }
      else
    {
      countT step = in_integer(in_nonblank());
      if (step > 0)
        {
          addr->addr_step = step;
          addr->addr_type = addr_is_num_mod;
        }
    }
    }
  else if (ch == '+' || ch == '~')
    {
      addr->addr_step = in_integer(in_nonblank());
      if (addr->addr_step==0)
    ; /* default to addr_is_null; forces matching to stop on next line */
      else if (ch == '+')
    addr->addr_type = addr_is_step;
      else
    addr->addr_type = addr_is_step_mod;
    }
  else if (ch == '$')
    {
      addr->addr_type = addr_is_last;
    }
  else
    return FALSE;

  return TRUE;
}

/* Read a program (or a subprogram within `{' `}' pairs) in and store
   the compiled form in `*vector'.  Return a pointer to the new vector.  */
static struct vector *compile_program P_((struct vector *));
static struct vector *
compile_program(vector)
  struct vector *vector;
{
  struct sed_cmd *cur_cmd;
  struct buffer *b;
  int ch;

  if (!vector)
    {
      vector = MALLOC(1, struct vector);
      vector->v = NULL;
      vector->v_allocated = 0;
      vector->v_length = 0;

      obstack_init (&obs);
    }
  if (pending_text)
    read_text(NULL, '\n');

  for (;;)
    {
      struct addr a;

      while ((ch=inchar()) == ';' || ISSPACE(ch))
    ;
      if (ch == EOF)
    break;

      cur_cmd = next_cmd_entry(&vector);
      if (compile_address(&a, ch))
    {
      if (a.addr_type == addr_is_step
          || a.addr_type == addr_is_step_mod)
        bad_prog(_(BAD_PLUS));

      cur_cmd->a1 = MEMDUP(&a, 1, struct addr);
      ch = in_nonblank();
      if (ch == ',')
        {
          if (!compile_address(&a, in_nonblank()))
        bad_prog(_(BAD_COMMA));
          if (a.addr_type == addr_is_num)
        a.addr_type = addr_is_num2;
          cur_cmd->a2 = MEMDUP(&a, 1, struct addr);
          ch = in_nonblank();
          if ((cur_cmd->a1->addr_type == addr_is_num
           || cur_cmd->a1->addr_type == addr_is_num_mod)
              && cur_cmd->a1->addr_number == 0)
        cur_cmd->a1_matched = TRUE;
        }
    }
      if (ch == '!')
    {
      cur_cmd->addr_bang = TRUE;
      ch = in_nonblank();
      if (ch == '!')
        bad_prog(_(BAD_BANG));
    }

      cur_cmd->cmd = ch;
      switch (ch)
    {
    case '#':
      if (cur_cmd->a1)
        bad_prog(_(NO_SHARP_ADDR));
      ch = inchar();
      if (ch=='n' && first_script && cur_input.line < 2)
        if (   (prog.base && prog.cur==2+prog.base)
        || (prog.file && !prog.base && 2==ftell(prog.file)))
          no_default_output = TRUE;
      while (ch != EOF && ch != '\n')
        ch = inchar();
      continue; /* restart the for (;;) loop */

    case 'v':
          /* This is an extension.  Programs needing GNU sed might start
           * with a `v' command so that other seds will stop.
           * We compare the version ignore it.
           */
          {
            char *version = read_label ();
            char *compared_version;
            compared_version = (*version == '\0') ? "4.0" : version;
            if (strverscmp (compared_version, SED_FEATURE_VERSION) > 0)
              bad_prog(_(ANCIENT_VERSION));

            free (version);
        POSIXLY_CORRECT = 0;
          }
      continue;

    case '{':
      blocks = setup_label(blocks, vector->v_length, NULL, &cur_input);
      cur_cmd->addr_bang = !cur_cmd->addr_bang;
      break;

    case '}':
      if (!blocks)
        bad_prog(_(EXCESS_CLOSE_BRACE));
      if (cur_cmd->a1)
        bad_prog(_(NO_CLOSE_BRACE_ADDR));
      ch = in_nonblank();
      if (ch == CLOSE_BRACE || ch == '#')
        savchar(ch);
      else if (ch != EOF && ch != '\n' && ch != ';')
        bad_prog(_(EXCESS_JUNK));

      vector->v[blocks->v_index].x.jump_index = vector->v_length;
      blocks = release_label(blocks);   /* done with this entry */
      break;

    case 'e':
      ch = in_nonblank();
      if (ch == EOF || ch == '\n')
        {
          cur_cmd->x.cmd_txt.text_length = 0;
          break;
        }
      else
        goto read_text_to_slash;

    case 'a':
    case 'i':
      if (POSIXLY_CORRECT && cur_cmd->a2)
        bad_prog(_(ONE_ADDR));
      /* Fall Through */

    case 'c':
      ch = in_nonblank();

    read_text_to_slash:
      if (ch == EOF)
        bad_prog(_(EXPECTED_SLASH));
          
      if (ch == '\\')
        ch = inchar();
      else
        {
          savchar(ch);
          ch = '\n';
        }

      read_text(&cur_cmd->x.cmd_txt, ch);
      break;

    case ':':
      if (cur_cmd->a1)
        bad_prog(_(NO_COLON_ADDR));
      labels = setup_label(labels, vector->v_length, read_label(), NULL);
      break;
    
    case 'b':
    case 't':
    case 'T':
      jumps = setup_label(jumps, vector->v_length, read_label(), NULL);
      break;

    case 'q':
    case 'Q':
      if (cur_cmd->a2)
        bad_prog(_(ONE_ADDR));
      /* Fall through */

    case 'l':
    case 'L':
      if (POSIXLY_CORRECT && cur_cmd->a2)
        bad_prog(_(ONE_ADDR));

      ch = in_nonblank();
      if (ISDIGIT(ch)) 
        {
          cur_cmd->x.int_arg = in_integer(ch);
          ch = in_nonblank();
        }
      else
        cur_cmd->x.int_arg = -1;

      if (ch == CLOSE_BRACE || ch == '#')
        savchar(ch);
      else if (ch != EOF && ch != '\n' && ch != ';')
        bad_prog(_(EXCESS_JUNK));

      break;

    case '=':
      if (POSIXLY_CORRECT && cur_cmd->a2)
        bad_prog(_(ONE_ADDR));
      /* Fall Through */
    case 'd':
    case 'D':
    case 'g':
    case 'G':
    case 'h':
    case 'H':
    case 'n':
    case 'N':
    case 'p':
    case 'P':
    case 'x':
      ch = in_nonblank();
      if (ch == CLOSE_BRACE || ch == '#')
        savchar(ch);
      else if (ch != EOF && ch != '\n' && ch != ';')
        bad_prog(_(EXCESS_JUNK));
      break;

    case 'r':
      if (POSIXLY_CORRECT && cur_cmd->a2)
        bad_prog(_(ONE_ADDR));
      b = read_filename();
      cur_cmd->x.fname = ck_strdup(get_buffer(b));
      free_buffer(b);
      break;

        case 'R':
      cur_cmd->x.fp = get_openfile(&file_read, "r", FALSE);
      break;

    case 'w':
        case 'W':
      cur_cmd->x.fp = get_openfile(&file_write, "w", TRUE);
      break;

    case 's':
      {
        struct buffer *b2;
        int flags;
        int slash;

        slash = inchar();
        if ( !(b  = match_slash(slash, TRUE, TRUE)) )
          bad_prog(_(UNTERM_S_CMD));
        if ( !(b2 = match_slash(slash, FALSE, TRUE)) )
          bad_prog(_(UNTERM_S_CMD));

        cur_cmd->x.cmd_subst = OB_MALLOC(&obs, 1, struct subst);
        setup_replacement(cur_cmd->x.cmd_subst,
                  get_buffer(b2), size_buffer(b2));
        free_buffer(b2);

        flags = mark_subst_opts(cur_cmd->x.cmd_subst);
        cur_cmd->x.cmd_subst->regx =
          compile_regex(b, flags, cur_cmd->x.cmd_subst->max_id);
        free_buffer(b);
      }
      break;

    case 'y':
      {
        unsigned char *ustring;
        size_t len;
        int slash;

#if defined HAVE_MBRTOWC && !defined (REG_PERL)
        if (MB_CUR_MAX == 1)
#endif
          {
            ustring = OB_MALLOC(&obs, YMAP_LENGTH, unsigned char);
            for (len = 0; len < YMAP_LENGTH; len++)
              ustring[len] = len;
            cur_cmd->x.translate = ustring;
          }

        slash = inchar();
        if ( !(b = match_slash(slash, FALSE, FALSE)) )
          bad_prog(_(UNTERM_Y_CMD));

#if defined HAVE_MBRTOWC && !defined (REG_PERL)
            if (MB_CUR_MAX > 1)
          {
                int i, j, idx, src_char_num, len = size_buffer(b);
                size_t *src_lens = MALLOC(len, size_t);
                char *src_buf, *dest_buf, **trans_pairs;
                size_t mbclen;
                mbstate_t cur_stat;
            struct buffer *b2;

                /* Enumerate how many character the source buffer has.  */
                memset(&cur_stat, 0, sizeof(mbstate_t));
                src_buf = get_buffer(b);
                for (i = 0, j = 0; i < len;)
                  {
                    mbclen = mbrlen(src_buf + i, len - i, &cur_stat);
                    /* An invalid sequence, or a truncated multibyte character.
                       We treat it as a singlebyte character.  */
                    if (mbclen == (size_t) -1 || mbclen == (size_t) -2
                        || mbclen == 0)
                      mbclen = 1;
                    src_lens[j++] = mbclen;
                    i += mbclen;
                  }
                src_char_num = j;

                memset(&cur_stat, 0, sizeof(mbstate_t));
                if ( !(b2 = match_slash(slash, FALSE, FALSE)) )
          bad_prog(_(UNTERM_Y_CMD));
                dest_buf = get_buffer(b2);
                idx = 0;
                len = size_buffer(b2);

                /* trans_pairs = {src(0), dest(0), src(1), dest(1), ..., NULL}
                     src(i) : pointer to i-th source character.
                     dest(i) : pointer to i-th destination character.
                     NULL : terminator */
                trans_pairs = MALLOC(2 * src_char_num + 1, char*);
                cur_cmd->x.translatemb = trans_pairs;
                for (i = 0; i < src_char_num; i++)
                  {
                    if (idx >= len)
                      bad_prog(_(Y_CMD_LEN));

                    /* Set the i-th source character.  */
                    trans_pairs[2 * i] = MALLOC(src_lens[i] + 1, char);
                    strncpy(trans_pairs[2 * i], src_buf, src_lens[i]);
                    trans_pairs[2 * i][src_lens[i]] = '\0';
                    src_buf += src_lens[i]; /* Forward to next character.  */

                    /* Fetch the i-th destination character.  */
                    mbclen = mbrlen(dest_buf + idx, len - idx, &cur_stat);
                    /* An invalid sequence, or a truncated multibyte character.
                       We treat it as a singlebyte character.  */
                    if (mbclen == (size_t) -1 || mbclen == (size_t) -2
                        || mbclen == 0)
                      mbclen = 1;

                    /* Set the i-th destination character.  */
                    trans_pairs[2 * i + 1] = MALLOC(mbclen + 1, char);
                    strncpy(trans_pairs[2 * i + 1], dest_buf + idx, mbclen);
                    trans_pairs[2 * i + 1][mbclen] = '\0';
                    idx += mbclen; /* Forward to next character.  */
                  }
                trans_pairs[2 * i] = NULL;
                if (idx != len)
                  bad_prog(_(Y_CMD_LEN));
                free_buffer(b);
                free_buffer(b2);
              }
            else
#endif
              {
                ustring = CAST(unsigned char *)get_buffer(b);
                for (len = size_buffer(b); len; --len)
                  {
                    ch = inchar();
                    if (ch == slash)
                      bad_prog(_(Y_CMD_LEN));
                    if (ch == '\n')
                      bad_prog(UNTERM_Y_CMD);
                    if (ch == '\\')
                      ch = inchar();
                    if (ch == EOF)
                      bad_prog(UNTERM_Y_CMD);
                    cur_cmd->x.translate[*ustring++] = ch;
                  }
                free_buffer(b);

                if (inchar() != slash)
          bad_prog(_(Y_CMD_LEN));
                else if ((ch = in_nonblank()) != EOF && ch != '\n' && ch != ';')
                  bad_prog(_(EXCESS_JUNK));
          }
      }
    break;

    case EOF:
      bad_prog(_(NO_COMMAND));
      /*NOTREACHED*/
    default:
      {
        const char *msg = _("Unknown command:");
        char *unknown_cmd = xmalloc(strlen(msg) + 5);
        sprintf(unknown_cmd, "%s `%c'", msg, ch);
        bad_prog(unknown_cmd);
        /*NOTREACHED*/
      }
    }

      /* this is buried down here so that "continue" statements will miss it */
      ++vector->v_length;
    }
  return vector;
}



/* Complain about a programming error and exit. */
void
bad_prog(why)
  const char *why;
{
  if (cur_input.name)
    fprintf(stderr, _("%s: file %s line %lu: %s\n"),
        myname, cur_input.name, CAST(unsigned long)cur_input.line, why);
  else
    fprintf(stderr, _("%s: -e expression #%lu, char %lu: %s\n"),
        myname,
        CAST(unsigned long)cur_input.string_expr_count,
        CAST(unsigned long)(prog.cur-prog.base),
        why);
  exit(EXIT_FAILURE);
}


/* deal with \X escapes */
size_t
normalize_text(buf, len)
  char *buf;
  size_t len;
{
  const char *bufend = buf + len;
  char *p = buf;
  char *q = buf;

  /* I'm not certain whether POSIX.2 allows these escapes.
     Play it safe for now... */
  if (POSIXLY_CORRECT && !(extended_regexp_flags))
    return len;

  while (p < bufend)
    {
      int c;

      *q = *p++;
      if (*q == '\\' && p < bufend)
    switch ( (c = *p++) )
      {
#if defined __STDC__ && __STDC__-0
      case 'a': *q = '\a'; break;
#else /* Not STDC; we'll just assume ASCII */
      case 'a': *q = '\007'; break;
#endif
      /* case 'b': *q = '\b'; break; --- conflicts with \b RE */
      case 'f': *q = '\f'; break;
      case '\n': /*fall through */
      case 'n': *q = '\n'; break;
      case 'r': *q = '\r'; break;
      case 't': *q = '\t'; break;
      case 'v': *q = '\v'; break;

      case 'd': /* decimal byte */
        p = convert_number(q, p, bufend, 10, 3, 'd');
        break;

      case 'x': /* hexadecimal byte */
        p = convert_number(q, p, bufend, 16, 2, 'x');
        break;

#ifdef REG_PERL
      case '0': case '1': case '2': case '3':
      case '4': case '5': case '6': case '7':
        if ((extended_regexp_flags & REG_PERL) &&
        p < bufend && isdigit(*p))
          {
        p--;
        p = convert_number(q, p, bufend, 8, 3, *p);
          }
        else
          /* we just pass the \ up one level for interpretation */
          *++q = p[-1];

        break;

      case 'o': /* octal byte */
        if (!(extended_regexp_flags & REG_PERL))
          p = convert_number(q, p, bufend,  8, 3, 'o');
        else
          /* we just pass the \ up one level for interpretation */
          *++q = p[-1];
        
        break;

#else
      case 'o': /* octal byte */
        p = convert_number(q, p, bufend,  8, 3, 'o');
        break;
#endif

      case 'c':
        if (p < bufend)
          {
        *q = toupper(*p) ^ 0x40;
        p++;
        break;
          }
        /* else FALLTHROUGH */

      default:
        /* we just pass the \ up one level for interpretation */
        *++q = p[-1];
        break;
      }
      ++q;
    }
    return (size_t)(q - buf);
}


/* `str' is a string (from the command line) that contains a sed command.
   Compile the command, and add it to the end of `cur_program'. */
struct vector *
compile_string(cur_program, str, len)
  struct vector *cur_program;
  char *str;
  size_t len;
{
  static countT string_expr_count = 0;
  struct vector *ret;

  prog.file = NULL;
  prog.base = CAST(unsigned char *)str;
  prog.cur = prog.base;
  prog.end = prog.cur + len;

  cur_input.line = 0;
  cur_input.name = NULL;
  cur_input.string_expr_count = ++string_expr_count;

  ret = compile_program(cur_program);
  prog.base = NULL;
  prog.cur = NULL;
  prog.end = NULL;

  first_script = FALSE;
  return ret;
}

/* `cmdfile' is the name of a file containing sed commands.
   Read them in and add them to the end of `cur_program'.
 */
struct vector *
compile_file(cur_program, cmdfile)
  struct vector *cur_program;
  const char *cmdfile;
{
  size_t len;
  struct vector *ret;

  prog.file = stdin;
  if (cmdfile[0] != '-' || cmdfile[1] != '\0')
    prog.file = ck_fopen(cmdfile, "rt", TRUE);

  cur_input.line = 1;
  cur_input.name = cmdfile;
  cur_input.string_expr_count = 0;

  ret = compile_program(cur_program);
  if (prog.file != stdin)
    ck_fclose(prog.file);
  prog.file = NULL;

  first_script = FALSE;
  return ret;
}

/* Make any checks which require the whole program to have been read.
   In particular: this backpatches the jump targets.
   Any cleanup which can be done after these checks is done here also.  */
void
check_final_program(program)
  struct vector *program;
{
  struct sed_label *go;
  struct sed_label *lbl;

  /* do all "{"s have a corresponding "}"? */
  if (blocks)
    {
      /* update info for error reporting: */
      MEMCPY(&cur_input, &blocks->err_info, sizeof cur_input);
      bad_prog(_(EXCESS_OPEN_BRACE));
    }

  /* was the final command an unterminated a/c/i command? */
  if (pending_text)
    {
      old_text_buf->text_length = size_buffer(pending_text);
      old_text_buf->text = MEMDUP(get_buffer(pending_text),
                  old_text_buf->text_length, char);
      free_buffer(pending_text);
      pending_text = NULL;
    }

  for (go = jumps; go; go = release_label(go))
    {
      for (lbl = labels; lbl; lbl = lbl->next)
    if (strcmp(lbl->name, go->name) == 0)
      break;
      if (lbl)
    {
      program->v[go->v_index].x.jump_index = lbl->v_index;
    }
      else
    {
      if (*go->name)
        panic(_("Can't find label for jump to `%s'"), go->name);
      program->v[go->v_index].x.jump_index = program->v_length;
    }
    }
  jumps = NULL;

  for (lbl = labels; lbl; lbl = release_label(lbl))
    ;
  labels = NULL;

  /* There is no longer a need to track file names: */
  {
    struct fp_list *p;

    for (p=file_read; p; p=p->link)
      if (p->name)
    {
      FREE(p->name);
      p->name = NULL;
    }

    for (p=file_write; p; p=p->link)
      if (p->name)
    {
      FREE(p->name);
      p->name = NULL;
    }
  }
}

/* Release all resources which were allocated in this module. */
void
rewind_read_files()
{
  struct fp_list *p, *q;

  for (p=file_read; p; p=p->link)
    if (p->fp && !p->special)
      rewind(p->fp);
}

/* Release all resources which were allocated in this module. */
void
finish_program(program)
  struct vector *program;
{
  /* close all files... */
  {
    struct fp_list *p, *q;

    for (p=file_read; p; p=q)
      {
    if (p->fp && !p->special)
      ck_fclose(p->fp);
    q = p->link;
#if 0
    /* We use obstacks. */
    FREE(p);
#endif
      }

    for (p=file_write; p; p=q)
      {
    if (p->fp && !p->special)
      ck_fclose(p->fp);
    q = p->link;
#if 0
    /* We use obstacks. */
    FREE(p);
#endif
      }
    file_read = file_write = NULL;
  }

#ifdef DEBUG_LEAKS
  obstack_free (&obs, NULL);
#endif /*DEBUG_LEAKS*/
}

https://git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.h?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/sed/sed.h used in: Step 688, Step 689, Step 690, Step 691, Step 692
/*  GNU SED, a batch stream editor.
    Copyright (C) 1989,90,91,92,93,94,95,98,99,2002,2003
    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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#if defined(_AIX)
#pragma alloca
#else
# if !defined(alloca)           /* predefined by HP cc +Olibcalls */
#  ifdef __GNUC__
#   define alloca(size) __builtin_alloca(size)
#  else
#   if HAVE_ALLOCA_H
#    include <alloca.h>
#   else
#    if defined(__hpux)
        void *alloca ();
#    else
#     if !defined(__OS2__) && !defined(WIN32)
    char *alloca ();
#     else
#      include <malloc.h>       /* OS/2 defines alloca in here */
#     endif
#    endif
#   endif
#  endif
# endif
#endif

#ifndef BOOTSTRAP
#include <stdio.h>
#endif
#include "basicdefs.h"
#include "regex.h"
#include "utils.h"

#ifdef HAVE_WCHAR_H
# include <wchar.h>
#endif
#ifdef HAVE_WCTYPE_H
# include <wctype.h>
#endif


/* Struct vector is used to describe a compiled sed program. */
struct vector {
  struct sed_cmd *v;    /* a dynamically allocated array */
  size_t v_allocated;   /* ... number slots allocated */
  size_t v_length;  /* ... number of slots in use */
};


struct text_buf {
  char *text;
  size_t text_length;
};

enum replacement_types {
  repl_asis = 0,
  repl_uppercase = 1,
  repl_lowercase = 2,
  repl_uppercase_first = 4,
  repl_lowercase_first = 8,
  repl_modifiers = repl_uppercase_first | repl_lowercase_first,

  /* These are given to aid in debugging */
  repl_uppercase_uppercase = repl_uppercase_first | repl_uppercase,
  repl_uppercase_lowercase = repl_uppercase_first | repl_lowercase,
  repl_lowercase_uppercase = repl_lowercase_first | repl_uppercase,
  repl_lowercase_lowercase = repl_lowercase_first | repl_lowercase
};

enum addr_types {
  addr_is_null,     /* null address */
  addr_is_regex,    /* a.addr_regex is valid */
  addr_is_num,      /* a.addr_number is valid */
  addr_is_num_mod,  /* a.addr_number is valid, addr_step is modulo */
  addr_is_num2,     /* a.addr_number is valid (only valid for addr2) */
  addr_is_step,     /* address is +N (only valid for addr2) */
  addr_is_step_mod, /* address is ~N (only valid for addr2) */
  addr_is_last      /* address is $ */
};

struct addr {
  enum addr_types addr_type;
  countT addr_number;
  countT addr_step;
  regex_t *addr_regex;
};


struct replacement {
  char *prefix;
  size_t prefix_length;
  int subst_id;
  enum replacement_types repl_type;
  struct replacement *next;
};

struct subst {
  regex_t *regx;
  struct replacement *replacement;
  countT numb;      /* if >0, only substitute for match number "numb" */
  FILE *fp;     /* 'w' option given */
  unsigned global : 1;  /* 'g' option given */
  unsigned print : 2;   /* 'p' option given (before/after eval) */
  unsigned eval : 1;    /* 'e' option given */
  unsigned max_id : 4;  /* maximum backreference on the RHS */
};

#ifdef REG_PERL
/* This is the structure we store register match data in.  See
   regex.texinfo for a full description of what registers match.  */
struct re_registers
{
  unsigned num_regs;
  regoff_t *start;
  regoff_t *end;
};
#endif



struct sed_cmd {
  struct addr *a1;  /* save space: usually is NULL */
  struct addr *a2;

  /* non-zero if a1 has been matched; apply command until a2 matches */
  char a1_matched;

  /* non-zero: only apply command to non-matches */
  char addr_bang;

  /* the actual command */
  char cmd;

  /* auxiliary data for various commands */
  union {
    /* This structure is used for a, i, and c commands */
    struct text_buf cmd_txt;

    /* This is used for the l, q and Q commands */
    int int_arg;

    /* This is used for {}, b, and t commands */
    countT jump_index;

    /* This for r command */
    char *fname;

    /* This for the hairy s command */
    struct subst *cmd_subst;

    /* This for R and w command */
    FILE *fp;

    /* This for the y command */
    unsigned char *translate;
    char **translatemb;
  } x;
};




void bad_prog P_((const char *why));
size_t normalize_text P_((char *, size_t));
struct vector *compile_string P_((struct vector *, char *str, size_t len));
struct vector *compile_file P_((struct vector *, const char *cmdfile));
void check_final_program P_((struct vector *));
void rewind_read_files P_((void));
void finish_program P_((struct vector *));

regex_t *compile_regex P_((struct buffer *b, int flags, int needed_sub));
int match_regex P_((regex_t *regex,
            char *buf, size_t buflen, size_t buf_start_offset,
            struct re_registers *regarray, int regsize));
#ifdef DEBUG_LEAKS
void release_regex P_((regex_t *));
#endif

int process_files P_((struct vector *, char **argv));

int main P_((int, char **));

extern void fmt P_ ((char *line, char *line_end, int max_length, FILE *output_file));

extern int extended_regexp_flags;

/* If set, fflush(stdout) on every line output. */
extern flagT unbuffered_output;

/* If set, don't write out the line unless explicitly told to. */
extern flagT no_default_output;

/* If set, reset line counts on every new file. */
extern flagT separate_files;

/* Do we need to be pedantically POSIX compliant? */
extern flagT POSIXLY_CORRECT;

/* How long should the `l' command's output line be? */
extern countT lcmd_out_line_len;

/* How do we edit files in-place? (we don't if NULL) */
extern char *in_place_extension;

https://git.savannah.gnu.org/cgit/sed.git/tree/lib/strverscmp.h?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/lib/strverscmp.h used in: Step 688
/* strverscmp.h -- compare strings holding indices/version numbers */

#ifndef STRVERSCMP_H_
# define STRVERSCMP_H_

# if HAVE_CONFIG_H
#  include <config.h>
# endif

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

int strverscmp PARAMS ((const char*, const char*));

#endif /* not STRVERSCMP_H_ */

https://git.savannah.gnu.org/cgit/sed.git/tree/sed/execute.c?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/sed/execute.c used in: Step 689
/*  GNU SED, a batch stream editor.
    Copyright (C) 1989,90,91,92,93,94,95,98,99,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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */

#undef EXPERIMENTAL_DASH_N_OPTIMIZATION /*don't use -- is very buggy*/
#define INITIAL_BUFFER_SIZE 50
#define FREAD_BUFFER_SIZE   8192

#include "sed.h"

#include <stdio.h>
#include <ctype.h>

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#ifdef __GNUC__
# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__-0 >= 7)
   /* silence warning about unused parameter even for "gcc -W -Wunused" */
#  define UNUSED    __attribute__((unused))
# endif
#endif
#ifndef UNUSED
# define UNUSED
#endif

#ifdef HAVE_STRINGS_H
# include <strings.h>
#else
# include <string.h>
#endif /*HAVE_STRINGS_H*/
#ifdef HAVE_MEMORY_H
# include <memory.h>
#endif

#ifndef HAVE_STRCHR
# define strchr index
# define strrchr rindex
#endif

#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 0
#endif

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

#include <sys/stat.h>



/* Sed operates a line at a time. */
struct line {
  char *text;       /* Pointer to line allocated by malloc. */
  char *active;     /* Pointer to non-consumed part of text. */
  size_t length;    /* Length of text (or active, if used). */
  size_t alloc;     /* Allocated space for active. */
  flagT chomped;    /* Was a trailing newline dropped? */
};

/* A queue of text to write out at the end of a cycle
   (filled by the "a", "r" and "R" commands.) */
struct append_queue {
  const char *fname;
  char *text;
  size_t textlen;
  struct append_queue *next;
  flagT free;
};

/* State information for the input stream. */
struct input {
  char **file_list; /* The list of yet-to-be-opened files.
               It is invalid for file_list to be NULL.
               When *file_list is NULL we are
               currently processing the last file. */
  countT bad_count; /* count of files we failed to open */
  countT line_number;   /* current input line number (over all files) */

  flagT (*read_fn) P_((struct input *));    /* read one line */
  /* If fp is NULL, read_fn better not be one which uses fp;
     in particular, read_always_fail() is recommended. */

  char *out_file_name;
  const char *in_file_name;

  FILE *fp;     /* if NULL, none of the following are valid */
  flagT no_buffering;
};


/* Have we done any replacements lately?  This is used by the `t' command. */
static flagT replaced = FALSE;

/* The current output file (stdout if -i is not being used. */
static FILE *output_file;

/* The `current' input line. */
static struct line line;

/* An input line used to accumulate the result of the s and e commands. */
static struct line s_accum;

/* An input line that's been stored by later use by the program */
static struct line hold;

/* The buffered input look-ahead.  The only field that should be
   used outside of read_mem_line() or line_init() is buffer.length. */
static struct line buffer;

static struct append_queue *append_head = NULL;
static struct append_queue *append_tail = NULL;



#ifdef BOOTSTRAP
/* We can't be sure that the system we're boostrapping on has
   memchr(), and ../lib/memchr.c requires configuration knowledge
   about how many bits are in a `long'.  This implementation
   is far from ideal, but it should get us up-and-limping well
   enough to run the configure script, which is all that matters.
*/
# ifdef memchr
#  undef memchr
# endif
# define memchr bootstrap_memchr

static VOID *bootstrap_memchr P_((const VOID *s, int c, size_t n));
static VOID *
bootstrap_memchr(s, c, n)
  const VOID *s;
  int c;
  size_t n;
{
  char *p;

  for (p=(char *)s; n-- > 0; ++p)
    if (*p == c)
      return p;
  return CAST(VOID *)0;
}
#endif /*BOOTSTRAP*/

/* increase a struct line's length, making some attempt at
   keeping realloc() calls under control by padding for future growth.  */
static void resize_line P_((struct line *, size_t));
static void
resize_line(lb, len)
  struct line *lb;
  size_t len;
{
  int inactive;
  inactive = lb->active - lb->text;

  /* If the inactive part has got to more than two thirds of the buffer,
   * remove it. */
  if (inactive > lb->alloc * 2)
    {
      MEMMOVE(lb->text, lb->active, lb->length);
      lb->alloc += lb->active - lb->text;
      lb->active = lb->text;
      inactive = 0;

      if (lb->alloc > len)
    return;
    }

  lb->alloc *= 2;
  if (lb->alloc < len)
    lb->alloc = len;
  if (lb->alloc < INITIAL_BUFFER_SIZE)
    lb->alloc = INITIAL_BUFFER_SIZE;
    
  lb->text = REALLOC(lb->text, inactive + lb->alloc, char);
  lb->active = lb->text + inactive;
}

/* Append `length' bytes from `string' to the line `to'. */
static void str_append P_((struct line *, const char *, size_t));
static void
str_append(to, string, length)
  struct line *to;
  const char *string;
  size_t length;
{
  size_t new_length = to->length + length;

  if (to->alloc < new_length)
    resize_line(to, new_length);
  MEMCPY(to->active + to->length, string, length);
  to->length = new_length;
}

static void str_append_modified P_((struct line *, const char *, size_t,
                    enum replacement_types));
static void
str_append_modified(to, string, length, type)
  struct line *to;
  const char *string;
  size_t length;
  enum replacement_types type;
{
  size_t old_length = to->length;
  char *start, *end;

  if (length == 0)
    return;

  str_append(to, string, length);
  start = to->active + old_length;
  end = start + length;

  /* Now do the required modifications.  First \[lu]... */
  if (type & repl_uppercase_first)
    {
      *start = toupper(*start);
      start++;
      type &= ~repl_uppercase_first;
    }
  else if (type & repl_lowercase_first)
    {
      *start = tolower(*start);
      start++;
      type &= ~repl_lowercase_first;
    }

  if (type == repl_asis)
    return;

  /* ...and then \[LU] */
  if (type == repl_uppercase)
    for (; start != end; start++)
      *start = toupper(*start);
  else
    for (; start != end; start++)
      *start = tolower(*start);
}

/* initialize a "struct line" buffer */
static void line_init P_((struct line *, size_t initial_size));
static void
line_init(buf, initial_size)
  struct line *buf;
  size_t initial_size;
{
  buf->text = MALLOC(initial_size, char);
  buf->active = buf->text;
  buf->alloc = initial_size;
  buf->length = 0;
  buf->chomped = TRUE;
}

/* Copy the contents of the line `from' into the line `to'.
   This destroys the old contents of `to'. */
static void line_copy P_((struct line *from, struct line *to));
static void
line_copy(from, to)
  struct line *from;
  struct line *to;
{
  /* Remove the inactive portion in the destination buffer. */
  to->alloc += to->active - to->text;

  if (to->alloc < from->length)
    {
      to->alloc *= 2;
      if (to->alloc < from->length)
    to->alloc = from->length;
      if (to->alloc < INITIAL_BUFFER_SIZE)
    to->alloc = INITIAL_BUFFER_SIZE;
      /* Use FREE()+MALLOC() instead of REALLOC() to
     avoid unnecessary copying of old text. */
      FREE(to->text);
      to->text = MALLOC(to->alloc, char);
    }

  to->active = to->text;
  to->length = from->length;
  to->chomped = from->chomped;
  MEMCPY(to->active, from->active, from->length);
}

/* Append the contents of the line `from' to the line `to'. */
static void line_append P_((struct line *from, struct line *to));
static void
line_append(from, to)
  struct line *from;
  struct line *to;
{
  str_append(to, "\n", 1);
  str_append(to, from->active, from->length);
  to->chomped = from->chomped;
}

/* Exchange the contents of two "struct line" buffers. */
static void line_exchange P_((struct line *, struct line *));
static void
line_exchange(a, b)
  struct line *a;
  struct line *b;
{
  struct line t;

  MEMCPY(&t,  a, sizeof(struct line));
  MEMCPY( a,  b, sizeof(struct line));
  MEMCPY( b, &t, sizeof(struct line));
}



/* dummy function to simplify read_pattern_space() */
static flagT read_always_fail P_((struct input *));
static flagT
read_always_fail(input)
  struct input *input UNUSED;
{
  return FALSE;
}

static flagT read_file_line P_((struct input *));
static flagT
read_file_line(input)
  struct input *input;
{
  static char *b;
  static size_t blen;

  long result = getline (&b, &blen, input->fp);

  /* Remove the trailing new-line that is left by getline. */
  if (result > 0 && b[result - 1] == '\n')
    --result;
  else
    {
      /* No trailing new line found. */
      if (!*input->file_list && !POSIXLY_CORRECT)
    line.chomped = FALSE;

      if (result <= 0)
    return FALSE;
    }

  str_append(&line, b, result);
  return TRUE;
}



static void output_line P_((const char *, size_t, flagT, FILE *));
static void
output_line(text, length, nl, fp)
  const char *text;
  size_t length;
  flagT nl;
  FILE *fp;
{
  if (length)
    ck_fwrite(text, 1, length, fp);
  if (nl)
    ck_fwrite("\n", 1, 1, fp);
  if (fp != stdout || unbuffered_output)
    ck_fflush(fp);
}

static struct append_queue *next_append_slot P_((void));
static struct append_queue *
next_append_slot()
{
  struct append_queue *n = MALLOC(1, struct append_queue);

  n->fname = NULL;
  n->text = NULL;
  n->textlen = 0;
  n->next = NULL;
  n->free = FALSE;

  if (append_tail)
      append_tail->next = n;
  else
      append_head = n;
  return append_tail = n;
}

static void release_append_queue P_((void));
static void
release_append_queue()
{
  struct append_queue *p, *q;

  for (p=append_head; p; p=q)
    {
      if (p->free)
    FREE(p->text);

      q = p->next;
      FREE(p);
    }
  append_head = append_tail = NULL;
}

static void dump_append_queue P_((void));
static void
dump_append_queue()
{
  struct append_queue *p;

  for (p=append_head; p; p=p->next)
    {
      if (p->text)
      output_line(p->text, p->textlen, FALSE, output_file);
      if (p->fname)
    {
      char buf[FREAD_BUFFER_SIZE];
      size_t cnt;
      FILE *fp;

      /* "If _fname_ does not exist or cannot be read, it shall
         be treated as if it were an empty file, causing no error
         condition."  IEEE Std 1003.2-1992
         So, don't fail. */
      fp = ck_fopen(p->fname, "r", FALSE);
      if (fp)
        {
          while ((cnt = ck_fread(buf, 1, sizeof buf, fp)) > 0)
        ck_fwrite(buf, 1, cnt, output_file);
          ck_fclose(fp);
        }
    }
    }
  release_append_queue();
}



/* Compute the name of the backup file for in-place editing */
static char *get_backup_file_name P_((const char *));
static char *
get_backup_file_name(name)
  const char *name;
{
  char *old_asterisk, *asterisk, *backup, *p;
  int name_length = strlen(name), backup_length = strlen(in_place_extension);

  /* Compute the length of the backup file */
  for (asterisk = in_place_extension - 1, old_asterisk = asterisk + 1;
       asterisk = strchr(old_asterisk, '*');
       old_asterisk = asterisk + 1)
    backup_length += name_length - 1;

  p = backup = xmalloc(backup_length + 1);

  /* Each iteration gobbles up to an asterisk */
  for (asterisk = in_place_extension - 1, old_asterisk = asterisk + 1;
       asterisk = strchr(old_asterisk, '*');
       old_asterisk = asterisk + 1)
    {
      memcpy (p, old_asterisk, asterisk - old_asterisk);
      p += asterisk - old_asterisk;
      strcpy (p, name);
      p += name_length;
    }

  /* Tack on what's after the last asterisk */
  strcpy (p, old_asterisk);
  return backup;
}

/* Initialize a struct input for the named file. */
static void open_next_file P_((const char *name, struct input *));
static void
open_next_file(name, input)
  const char *name;
  struct input *input;
{
  buffer.length = 0;

  if (name[0] == '-' && name[1] == '\0')
    {
      clearerr(stdin);  /* clear any stale EOF indication */
      input->fp = stdin;
    }
  else if ( ! (input->fp = ck_fopen(name, "r", FALSE)) )
    {
      const char *ptr = strerror(errno);
      fprintf(stderr, _("%s: can't read %s: %s\n"), myname, name, ptr);
      input->read_fn = read_always_fail; /* a redundancy */
      ++input->bad_count;
      return;
    }

  input->read_fn = read_file_line;

  if (in_place_extension)
    {
      int output_fd;
      char *tmpdir = ck_strdup(name), *p;

      /* get the base name */
      if (p = strrchr(tmpdir, '/'))
    *(p + 1) = 0;
      else
    strcpy(tmpdir, ".");
      
      input->in_file_name = name;
      input->out_file_name = temp_file_template (tmpdir, "sed");
      output_fd = mkstemp (input->out_file_name);
      free (tmpdir);

      if (output_fd == -1)
        panic(_("Couldn't open temporary file %s: %s"), input->out_file_name, strerror(errno));

#ifdef HAVE_FCHMOD
      {
    struct stat st;
    fstat (fileno (input->fp), &st);
    fchmod (output_fd, st.st_mode);
      }
#endif

      output_file = fdopen (output_fd, "w");
      if (!output_file)
        panic(_("Couldn't open temporary file %s: %s"), input->out_file_name, strerror(errno));
    }
  else
    output_file = stdout;
}


/* Clean up an input stream that we are done with. */
static void closedown P_((struct input *));
static void
closedown(input)
  struct input *input;
{
  input->read_fn = read_always_fail;
  if (!input->fp)
    return;
  if (input->fp != stdin) /* stdin can be reused on tty and tape devices */
    ck_fclose(input->fp);

  if (in_place_extension && output_file != NULL)
    {
      int fd = fileno (output_file);
      if (strcmp(in_place_extension, "*") != 0)
        {
          char *backup_file_name = get_backup_file_name(input->in_file_name);
          rename (input->in_file_name, backup_file_name);
          free (backup_file_name);
        }

      ck_fclose (output_file);
      close (fd);
      rename (input->out_file_name, input->in_file_name);
      free (input->out_file_name);
    }

  input->fp = NULL;

  if (separate_files)
    rewind_read_files ();
}

/* Reset range commands so that they are marked as non-matching */
static void reset_addresses P_((struct vector *));
static void
reset_addresses(vec)
     struct vector *vec;
{
  struct sed_cmd *cur_cmd;
  int n;

  for (cur_cmd = vec->v, n = vec->v_length; n--; cur_cmd++)
    if (cur_cmd->a1)
      {
    enum addr_types type = cur_cmd->a1->addr_type;
        cur_cmd->a1_matched = (type == addr_is_num || type == addr_is_num_mod)
                              && cur_cmd->a1->addr_number == 0;
      }
    else
      cur_cmd->a1_matched = FALSE;
}

/* Read in the next line of input, and store it in the pattern space.
   Return zero if there is nothing left to input. */
static flagT read_pattern_space P_((struct input *, struct vector *, flagT));
static flagT
read_pattern_space(input, the_program, append)
  struct input *input;
  struct vector *the_program;
  flagT append;
{
  if (append_head) /* redundant test to optimize for common case */
    dump_append_queue();
  replaced = FALSE;
  if (!append)
    line.length = 0;
  line.chomped = TRUE;  /* default, until proved otherwise */

  while ( ! (*input->read_fn)(input) )
    {
      closedown(input);

      if (!*input->file_list)
    {
      line.chomped = FALSE;
      return FALSE;
    }

      if (separate_files)
    {
      input->line_number = 0;
      reset_addresses (the_program);
    }

      open_next_file (*input->file_list++, input);
    }

  ++input->line_number;
  return TRUE;
}



static flagT last_file_with_data_p P_((struct input *));
static flagT
last_file_with_data_p(input)
  struct input *input;
{
  for (;;)
    {
      int ch;

      closedown(input);
      if (!*input->file_list)
    return TRUE;
      open_next_file(*input->file_list++, input);
      if (input->fp)
    {
      if ((ch = getc(input->fp)) != EOF)
        {
          ungetc(ch, input->fp);
          return FALSE;
        }
    }
    }
}

/* Determine if we match the `$' address. */
static flagT test_eof P_((struct input *));
static flagT
test_eof(input)
  struct input *input;
{
  int ch;

  if (buffer.length)
    return FALSE;
  if (!input->fp)
    return separate_files || last_file_with_data_p(input);
  if (feof(input->fp))
    return separate_files || last_file_with_data_p(input);
  if ((ch = getc(input->fp)) == EOF)
    return separate_files || last_file_with_data_p(input);
  ungetc(ch, input->fp);
  return FALSE;
}

/* Return non-zero if the current line matches the address
   pointed to by `addr'. */
static flagT match_an_address_p P_((struct addr *, struct input *));
static flagT
match_an_address_p(addr, input)
  struct addr *addr;
  struct input *input;
{
  switch (addr->addr_type)
    {
    case addr_is_null:
      return TRUE;

    case addr_is_regex:
      return match_regex(addr->addr_regex, line.active, line.length, 0, NULL, 0);

    case addr_is_num:
      return (addr->addr_number == input->line_number);

    case addr_is_num_mod:
      if (addr->addr_number < addr->addr_step)
    return (addr->addr_number == input->line_number%addr->addr_step);
      /* addr_number >= step implies we have an extra initial skip */
      if (input->line_number < addr->addr_number)
    return FALSE;
      /* normalize */
      addr->addr_number %= addr->addr_step;
      return (addr->addr_number == 0);

    case addr_is_num2:
    case addr_is_step:
    case addr_is_step_mod:
      /* reminder: these are only meaningful for a2 addresses */
      /* a2->addr_number needs to be recomputed each time a1 address
     matches for the step and step_mod types */
      return (addr->addr_number <= input->line_number);

    case addr_is_last:
      return test_eof(input);

    default:
      panic(_("INTERNAL ERROR: bad address type"));
    }
  /*NOTREACHED*/
  return FALSE;
}

/* return non-zero if current address is valid for cmd */
static flagT match_address_p P_((struct sed_cmd *, struct input *));
static flagT
match_address_p(cmd, input)
  struct sed_cmd *cmd;
  struct input *input;
{
  flagT addr_matched = cmd->a1_matched;

  if (addr_matched)
    {
      if (match_an_address_p(cmd->a2, input))
    cmd->a1_matched = FALSE;
    }
  else if (!cmd->a1 || match_an_address_p(cmd->a1, input))
    {
      addr_matched = TRUE;
      if (cmd->a2)
    {
      cmd->a1_matched = TRUE;
      switch (cmd->a2->addr_type)
        {
        case addr_is_regex:
          break;
        case addr_is_step:
          cmd->a2->addr_number = input->line_number + cmd->a2->addr_step;
          break;
        case addr_is_step_mod:
          cmd->a2->addr_number = input->line_number + cmd->a2->addr_step
                     - (input->line_number%cmd->a2->addr_step);
          break;
        default:
          if (match_an_address_p(cmd->a2, input))
        cmd->a1_matched = FALSE;
          break;
        }
    }
    }
  if (cmd->addr_bang)
    return !addr_matched;
  return addr_matched;
}



static void do_list P_((int line_len));
static void
do_list(line_len)
     int line_len;
{
  unsigned char *p = CAST(unsigned char *)line.active;
  countT len = line.length;
  countT width = 0;
  char obuf[180];   /* just in case we encounter a 512-bit char (;-) */
  char *o;
  size_t olen;

  for (; len--; ++p) {
      o = obuf;
      
      /* Some locales define 8-bit characters as printable.  This makes the
     testsuite fail at 8to7.sed because the `l' command in fact will not
     convert the 8-bit characters. */
#if defined isascii || defined HAVE_ISASCII
      if (isascii(*p) && ISPRINT(*p)) {
#else
      if (ISPRINT(*p)) {
#endif
      *o++ = *p;
      if (*p == '\\')
        *o++ = '\\';
      } else {
      *o++ = '\\';
      switch (*p) {
#if defined __STDC__ && __STDC__-0
        case '\a': *o++ = 'a'; break;
#else /* Not STDC; we'll just assume ASCII */
        case 007:  *o++ = 'a'; break;
#endif
        case '\b': *o++ = 'b'; break;
        case '\f': *o++ = 'f'; break;
        case '\n': *o++ = 'n'; break;
        case '\r': *o++ = 'r'; break;
        case '\t': *o++ = 't'; break;
        case '\v': *o++ = 'v'; break;
        default:
          sprintf(o, "%03o", *p);
          o += strlen(o);
          break;
        }
      }
      olen = o - obuf;
      if (width+olen >= line_len && line_len > 0) {
      ck_fwrite("\\\n", 1, 2, output_file);
      width = 0;
      }
      ck_fwrite(obuf, 1, olen, output_file);
      width += olen;
  }
  ck_fwrite("$\n", 1, 2, output_file);
}

static enum replacement_types append_replacement P_((struct line *, struct replacement *,
                                     struct re_registers *,
                                     enum replacement_types));
static enum replacement_types
append_replacement (buf, p, regs, repl_mod)
  struct line *buf;
  struct replacement *p;
  struct re_registers *regs;
  enum replacement_types repl_mod;
{
  for (; p; p=p->next)
    {
      int i = p->subst_id;
      enum replacement_types curr_type;

      /* Apply a \[lu] modifier that was given earlier, but which we
     have not had yet the occasion to apply.  But don't do it
     if this replacement has a modifier of its own. */
      curr_type = (p->repl_type & repl_modifiers)
    ? p->repl_type
    : p->repl_type | repl_mod;

      repl_mod = 0;
      if (p->prefix_length)
    {
      str_append_modified(buf, p->prefix, p->prefix_length,
                  curr_type);
      curr_type &= ~repl_modifiers;
    }

      if (0 <= i)
    if (regs->end[i] == regs->start[i] && p->repl_type & repl_modifiers)
      /* Save this modifier, we shall apply it later.
         e.g. in s/()([a-z])/\u\1\2/
         the \u modifier is applied to \2, not \1 */
      repl_mod = curr_type & repl_modifiers;

    else
      str_append_modified(buf, line.active + regs->start[i],
                  CAST(size_t)(regs->end[i] - regs->start[i]),
                  curr_type);
    }

  return repl_mod;
}

static void do_subst P_((struct subst *));
static void
do_subst(sub)
  struct subst *sub;
{
  size_t start = 0;     /* where to start scan for (next) match in LINE */
  size_t last_end = 0;  /* where did the last successful match end in LINE */
  countT count = 0;     /* number of matches found */
  flagT again = TRUE;

#define MAX_BACKREFERENCES 10
  static struct re_registers regs;

  if (s_accum.alloc == 0)
    line_init(&s_accum, INITIAL_BUFFER_SIZE);
  s_accum.length = 0;

  /* The first part of the loop optimizes s/xxx// when xxx is at the
     start, and s/xxx$// */
  if (!match_regex(sub->regx, line.active, line.length, start,
           &regs, MAX_BACKREFERENCES))
    return;

  if (!sub->replacement && sub->numb <= 1)
    if (regs.start[0] == 0 && !sub->global)
      {
    /* We found a match, set the `replaced' flag. */
    replaced = TRUE;

    line.active += regs.end[0];
    line.length -= regs.end[0];
    line.alloc -= regs.end[0];
    goto post_subst;
      }
    else if (regs.end[0] == line.length)
      {
    /* We found a match, set the `replaced' flag. */
    replaced = TRUE;

    line.length = regs.start[0];
    goto post_subst;
      }

  do
    {
      enum replacement_types repl_mod = 0;

      size_t offset = regs.start[0];
      size_t matched = regs.end[0] - regs.start[0];

      /* Copy stuff to the left of this match into the output string. */
      if (start < offset)
    str_append(&s_accum, line.active + start, offset - start);

      /* If we're counting up to the Nth match, are we there yet?
     And even if we are there, there is another case we have to
     skip: are we matching an empty string immediately following
     another match?

     This latter case avoids that baaaac, when passed through
     s,a*,x,g, gives `xbxxcx' instead of xbxcx.  This behavior is
     unacceptable because it is not consistently applied (for
     example, `baaaa' gives `xbx', not `xbxx'). */
      if ((matched > 0 || count == 0 || offset > last_end)
      && (last_end = regs.end[0], ++count >= sub->numb))
    {
      /* We found a match, set the `replaced' flag. */
      replaced = TRUE;

      /* Now expand the replacement string into the output string. */
      repl_mod = append_replacement (&s_accum, sub->replacement, &regs, repl_mod);
      again = sub->global;
    }
      else
    {
      /* The match was not replaced.  Copy the text until its
         end; if it was vacuous, skip over one character and
         add that character to the output.  */
      if (matched == 0)
        {
          if (start < line.length)
        matched = 1;
          else
        break;
        }

      str_append(&s_accum, line.active + offset, matched);
    }

      /* Start after the match.  */
      start = offset + matched;
    }
  while (again
     && start <= line.length
     && match_regex(sub->regx, line.active, line.length, start,
            &regs, MAX_BACKREFERENCES));

  /* Copy stuff to the right of the last match into the output string. */
  if (start < line.length)
    str_append(&s_accum, line.active + start, line.length-start);
  s_accum.chomped = line.chomped;

  /* Exchange line and s_accum.  This can be much cheaper
     than copying s_accum.active into line.text (for huge lines). */
  line_exchange(&line, &s_accum);

  /* Finish up. */
  if (count < sub->numb)
    return;

 post_subst:
  if (sub->print & 1)
    output_line(line.active, line.length, line.chomped, output_file);

  if (sub->eval)
    {
#ifdef HAVE_POPEN
      FILE *pipe;
      s_accum.length = 0;

      str_append (&line, "", 1);
      pipe = popen(line.active, "r");
      
      if (pipe != NULL) 
    {
      while (!feof (pipe)) 
        {
          char buf[4096];
          int n = fread (buf, sizeof(char), 4096, pipe);
          if (n > 0)
        str_append(&s_accum, buf, n);
        }
      
      pclose (pipe);

      line_exchange(&line, &s_accum);
      if (line.length &&
          line.active[line.length - 1] == '\n')
        line.length--;
    }
      else
    panic(_("error in subprocess"));
#else
      panic(_("option `e' not supported"));
#endif
    } 
  
  if (sub->print & 2)
    output_line(line.active, line.length, line.chomped, output_file);
  if (sub->fp)
    output_line(line.active, line.length, line.chomped, sub->fp);
}

#ifdef EXPERIMENTAL_DASH_N_OPTIMIZATION
/* Used to attempt a simple-minded optimization. */

static countT branches;

static countT count_branches P_((struct vector *));
static countT
count_branches(program)
  struct vector *program;
{
  struct sed_cmd *cur_cmd = program->v;
  countT isn_cnt = program->v_length;
  countT cnt = 0;

  while (isn_cnt-- > 0)
    {
      switch (cur_cmd->cmd)
    {
    case 'b':
    case 't':
    case 'T':
    case '{':
      ++cnt;
    }
    }
  return cnt;
}

static struct sed_cmd *shrink_program P_((struct vector *, struct sed_cmd *));
static struct sed_cmd *
shrink_program(vec, cur_cmd)
  struct vector *vec;
  struct sed_cmd *cur_cmd;
{
  struct sed_cmd *v = vec->v;
  struct sed_cmd *last_cmd = v + vec->v_length;
  struct sed_cmd *p;
  countT cmd_cnt;

  for (p=v; p < cur_cmd; ++p)
    if (p->cmd != ':')
      MEMCPY(v++, p, sizeof *v);
  cmd_cnt = v - vec->v;

  for (; p < last_cmd; ++p)
    if (p->cmd != ':')
      MEMCPY(v++, p, sizeof *v);
  vec->v_length = v - vec->v;

  return (0 < vec->v_length) ? (vec->v + cmd_cnt) : CAST(struct sed_cmd *)0;
}
#endif /*EXPERIMENTAL_DASH_N_OPTIMIZATION*/

/* Execute the program `vec' on the current input line.
   Return exit status if caller should quit, -1 otherwise. */
static int execute_program P_((struct vector *, struct input *));
static int
execute_program(vec, input)
  struct vector *vec;
  struct input *input;
{
  struct sed_cmd *cur_cmd;
  struct sed_cmd *end_cmd;

  cur_cmd = vec->v;
  end_cmd = vec->v + vec->v_length;
  while (cur_cmd < end_cmd)
    {
      if (match_address_p(cur_cmd, input))
    {
      switch (cur_cmd->cmd)
        {
        case 'a':
          {
        struct append_queue *aq = next_append_slot();
        aq->text = cur_cmd->x.cmd_txt.text;
        aq->textlen = cur_cmd->x.cmd_txt.text_length;
          }
          break;

        case '{':
        case 'b':
          cur_cmd = vec->v + cur_cmd->x.jump_index;
          continue;

        case '}':
        case ':':
          /* Executing labels and block-ends are easy. */
          break;

        case 'c':
          if (!cur_cmd->a1_matched)
        output_line(cur_cmd->x.cmd_txt.text,
                cur_cmd->x.cmd_txt.text_length, FALSE, output_file);
          /* POSIX.2 is silent about c starting a new cycle,
         but it seems to be expected (and make sense). */
          /* Fall Through */
        case 'd':
          return -1;

        case 'D':
          {
        char *p = memchr(line.active, '\n', line.length);
        if (!p)
          {
            line.length = 0;
            line.chomped = FALSE;
            return -1;
          }
        ++p;
        line.alloc -= p - line.active;
        line.length -= p - line.active;
        line.active += p - line.active;

        /* reset to start next cycle without reading a new line: */
        cur_cmd = vec->v;
        continue;
          }

        case 'e': {
#ifdef HAVE_POPEN
          FILE *pipe;
          int cmd_length = cur_cmd->x.cmd_txt.text_length;
          if (s_accum.alloc == 0)
        line_init(&s_accum, INITIAL_BUFFER_SIZE);
          s_accum.length = 0;

          if (!cmd_length)
        {
          str_append (&line, "", 1);
          pipe = popen(line.active, "r");
        } 
          else
        {
          cur_cmd->x.cmd_txt.text[cmd_length - 1] = 0;
          pipe = popen(cur_cmd->x.cmd_txt.text, "r");
        }

          if (pipe != NULL) 
        {
          while (!feof (pipe)) 
            {
              char buf[4096];
              int n = fread (buf, sizeof(char), 4096, pipe);
              if (n > 0)
            if (!cmd_length)
              str_append(&s_accum, buf, n);
            else
              output_line(buf, n, FALSE, output_file);
            }
          
          pclose (pipe);
          if (!cmd_length)
            {
              /* Store into pattern space for plain `e' commands */
              if (s_accum.length &&
              s_accum.active[s_accum.length - 1] == '\n')
            s_accum.length--;

              /* Exchange line and s_accum.  This can be much
             cheaper than copying s_accum.active into line.text
             (for huge lines). */
              line_exchange(&line, &s_accum);
            }
        }
          else
        panic(_("error in subprocess"));
#else
          panic(_("`e' command not supported"));
#endif
          break;
        }

        case 'g':
          line_copy(&hold, &line);
          break;

        case 'G':
          line_append(&hold, &line);
          break;

        case 'h':
          line_copy(&line, &hold);
          break;

        case 'H':
          line_append(&line, &hold);
          break;

        case 'i':
          output_line(cur_cmd->x.cmd_txt.text,
              cur_cmd->x.cmd_txt.text_length, FALSE, output_file);
          break;

        case 'l':
          do_list(cur_cmd->x.int_arg == -1
              ? lcmd_out_line_len
              : cur_cmd->x.int_arg);
          break;

        case 'L':
          fmt(line.active, line.active + line.length,
          cur_cmd->x.int_arg == -1
          ? lcmd_out_line_len
          : cur_cmd->x.int_arg,
          output_file);
          break;

        case 'n':
          if (!no_default_output)
        output_line(line.active, line.length, line.chomped, output_file);
          if (test_eof(input) || !read_pattern_space(input, vec, FALSE))
        return -1;
          break;

        case 'N':
          str_append(&line, "\n", 1);

          if (test_eof(input) || !read_pattern_space(input, vec, TRUE))
        {
          line.length--;
                  if (!POSIXLY_CORRECT && !no_default_output)
                    output_line(line.active, line.length, line.chomped,
                output_file);
          return -1;
        }
          break;

        case 'p':
          output_line(line.active, line.length, line.chomped, output_file);
          break;

        case 'P':
          {
        char *p = memchr(line.active, '\n', line.length);
        output_line(line.active, p ? p - line.active : line.length,
                p ? 1 : line.chomped, output_file);
          }
          break;

            case 'q':
              if (!no_default_output)
                output_line(line.active, line.length, line.chomped, output_file);

            case 'Q':
              return cur_cmd->x.int_arg == -1 ? 0 : cur_cmd->x.int_arg;

        case 'r':
          if (cur_cmd->x.fname)
        {
          struct append_queue *aq = next_append_slot();
          aq->fname = cur_cmd->x.fname;
        }
          break;

        case 'R':
          if (cur_cmd->x.fp && !feof (cur_cmd->x.fp))
        {
          struct append_queue *aq;
          size_t buflen;
          char *text = NULL;
          int result;

          result = getline (&text, &buflen, cur_cmd->x.fp);

          if (result != EOF)
            {
              aq = next_append_slot();
              aq->free = TRUE;
              aq->text = text;
              aq->textlen = result;
            }
        }
          break;

        case 's':
          do_subst(cur_cmd->x.cmd_subst);
          break;

        case 't':
          if (replaced)
        {
          replaced = FALSE;
          cur_cmd = vec->v + cur_cmd->x.jump_index;
          continue;
        }
          break;

        case 'T':
          if (!replaced)
        {
          cur_cmd = vec->v + cur_cmd->x.jump_index;
          continue;
        }
          else
        replaced = FALSE;
          break;

        case 'w':
          if (cur_cmd->x.fp)
        output_line(line.active, line.length,
                line.chomped, cur_cmd->x.fp);
          break;

        case 'W':
          if (cur_cmd->x.fp)
        {
          char *p = memchr(line.active, '\n', line.length);
          output_line(line.active, p ? p - line.active : line.length,
                  p ? 1 : line.chomped, cur_cmd->x.fp);
        }
          break;

        case 'x':
          line_exchange(&line, &hold);
          break;

        case 'y':
          {
#if defined HAVE_MBRTOWC && !defined REG_PERL
           if (MB_CUR_MAX > 1)
         {
           int idx, prev_idx; /* index in the input line.  */
           char **trans;
           mbstate_t cur_stat;
           memset(&cur_stat, 0, sizeof(mbstate_t));
           for (idx = 0; idx < line.length;)
             {
               int mbclen, i;
               mbclen = mbrlen(line.active + idx, line.length - idx,
                       &cur_stat);
               /* An invalid sequence, or a truncated multibyte
              character.  We treat it as a singlebyte character.
               */
               if (mbclen == (size_t) -1 || mbclen == (size_t) -2
               || mbclen == 0)
             mbclen = 1;

               trans = cur_cmd->x.translatemb;
               /* `i' indicate i-th translate pair.  */
               for (i = 0; trans[2*i] != NULL; i++)
             {
               if (strncmp(line.active + idx, trans[2*i], mbclen)
                   == 0)
                 {
                   flagT move_remain_buffer = FALSE;
                   int trans_len = strlen(trans[2*i+1]);

                   if (mbclen < trans_len)
                 {
                   int new_len;
                   new_len = line.length + 1 + trans_len - mbclen;
                   /* We must extend the line buffer.  */
                   if (line.alloc < new_len)
                     {
                       /* And we must resize the buffer.  */
                       resize_line(&line, new_len);
                     }
                   move_remain_buffer = TRUE;
                 }
                   else if (mbclen > trans_len)
                 {
                   /* We must truncate the line buffer.  */
                   move_remain_buffer = TRUE;
                 }
                   prev_idx = idx;
                   if (move_remain_buffer)
                 {
                   int move_len, move_offset;
                   char *move_from, *move_to;
                   /* Move the remaining with \0.  */
                   move_from = line.active + idx + mbclen;
                   move_to = line.active + idx + trans_len;
                   move_len = line.length + 1 - idx - mbclen;
                   move_offset = trans_len - mbclen;
                   memmove(move_to, move_from, move_len);
                   line.length += move_offset;
                   idx += move_offset;
                 }
                   strncpy(line.active + prev_idx, trans[2*i+1],
                       trans_len);
                   break;
                 }
             }
               idx += mbclen;
             }
         }
           else
#endif /* HAVE_MBRTOWC */
         {
           unsigned char *p, *e;
           p = CAST(unsigned char *)line.active;
           for (e=p+line.length; p<e; ++p)
             *p = cur_cmd->x.translate[*p];
         }
          }
          break;

        case '=':
          fprintf(output_file, "%lu\n",
              CAST(unsigned long)input->line_number);
          break;

        default:
          panic(_("INTERNAL ERROR: Bad cmd %c"), cur_cmd->cmd);
        }
    }

#ifdef EXPERIMENTAL_DASH_N_OPTIMIZATION
      /* If our top-level program consists solely of commands with
       * addr_is_num addresses then once we past the last mentioned
       * line we should be able to quit if no_default_output is true,
       * or otherwise quickly copy input to output.  Now whether this
       * optimization is a win or not depends on how cheaply we can
       * implement this for the cases where it doesn't help, as
       * compared against how much time is saved.  One semantic
       * difference (which I think is an improvement) is that *this*
       * version will terminate after printing line two in the script
       * "yes | sed -n 2p". 
       *
       * Don't use this when in-place editing is active, because line
       * numbers restart each time then. */
      else if (output_file == stdout)
    {
      /* can we ever match again? */
      if (cur_cmd->a1->addr_type == addr_is_num &&
          ((input->line_number < cur_cmd->a1->addr_number)
           != !cur_cmd->addr_bang))
        {
          /* skip all this next time */
          cur_cmd->a1->addr_type = addr_is_null;
          cur_cmd->addr_bang = TRUE;

          /* can we make an optimization? */
          if (cur_cmd->cmd == 'b' || cur_cmd->cmd == 't'
          || cur_cmd->cmd == '{')
        --branches;
          cur_cmd->cmd = ':';   /* replace with no-op */
          if (branches == 0)
        {
          /* whew!  all that just so that we can get to here! */
          cur_cmd = shrink_program(vec, cur_cmd);
          if (!cur_cmd && no_default_output)
            return 0;
          end_cmd = vec->v + vec->v_length;
          if (!cur_cmd)
            cur_cmd = end_cmd;
          continue;
        }
        }
    }
#endif /*EXPERIMENTAL_DASH_N_OPTIMIZATION*/

      /* this is buried down here so that a "continue" statement can skip it */
      ++cur_cmd;
    }

    if (!no_default_output)
      output_line(line.active, line.length, line.chomped, output_file);
    return -1;
}




/* Apply the compiled script to all the named files. */
int
process_files(the_program, argv)
  struct vector *the_program;
  char **argv;
{
  static char dash[] = "-";
  static char *stdin_argv[2] = { dash, NULL };
  struct input input;
  int status;

  line_init(&line, INITIAL_BUFFER_SIZE);
  line_init(&hold, 0);
  line_init(&buffer, 0);

#ifdef EXPERIMENTAL_DASH_N_OPTIMIZATION
  branches = count_branches(the_program);
#endif /*EXPERIMENTAL_DASH_N_OPTIMIZATION*/
  input.file_list = stdin_argv;
  if (argv && *argv)
    input.file_list = argv;
  input.bad_count = 0;
  input.line_number = 0;
  input.read_fn = read_always_fail;
  input.fp = NULL;

  status = EXIT_SUCCESS;
  while (read_pattern_space(&input, the_program, FALSE))
    {
      status = execute_program(the_program, &input);
      if (status == -1)
    status = EXIT_SUCCESS;
      else
    break;
    }
  closedown(&input);

#ifdef DEBUG_LEAKS
  /* We're about to exit, so these free()s are redundant.
     But if we're running under a memory-leak detecting
     implementation of malloc(), we want to explicitly
     deallocate in order to avoid extraneous noise from
     the allocator. */
  release_append_queue();
  FREE(buffer.text);
  FREE(hold.text);
  FREE(line.text);
  FREE(s_accum.text);
#endif /*DEBUG_LEAKS*/

  if (input.bad_count)
    status = 2;

  return status;
}

https://git.savannah.gnu.org/cgit/sed.git/tree/sed/regexp.c?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/sed/regexp.c used in: Step 690
/*  GNU SED, a batch stream editor.
    Copyright (C) 1999, 2002, 2003 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */

#include "sed.h"

#include <ctype.h>
#include <stdio.h>
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif

#ifdef gettext_noop
# define N_(String) gettext_noop(String)
#else
# define N_(String) (String)
#endif

extern flagT use_extended_syntax_p;

static const char errors[] =
  "No previous regular expression\0"
  "Cannot specify modifiers on empty regexp";

#define NO_REGEX (errors)
#define BAD_MODIF (NO_REGEX + sizeof(N_("No previous regular expression")))
#define END_ERRORS (BAD_MODIF + sizeof(N_("Cannot specify modifiers on empty regexp")))




regex_t *
compile_regex(b, flags, needed_sub)
  struct buffer *b;
  int flags;
  int needed_sub;
{
  regex_t *new_regex;

  char *last_re = NULL;
  size_t last_re_len;

  /* // matches the last RE */
  if (size_buffer(b) == 0)
    {
      if (flags > 0)
    bad_prog(_(BAD_MODIF));
      return NULL;
    }

  last_re_len = size_buffer(b);
  last_re = ck_memdup(get_buffer(b), last_re_len);

  new_regex = MALLOC(1, regex_t);

#ifdef REG_PERL
  {
    int errcode;
    errcode = regncomp(new_regex, last_re, last_re_len,
               (needed_sub ? 0 : REG_NOSUB)
               | flags
               | extended_regexp_flags);

    if (errcode)
      {
        char errorbuf[200];
        regerror(errcode, NULL, errorbuf, 200);
        bad_prog(gettext(errorbuf));
      }
  }
#else
  new_regex->fastmap = malloc (1 << (sizeof (char) * 8));
  {
    const char *error;
    int syntax = ((extended_regexp_flags & REG_EXTENDED)
           ? RE_SYNTAX_POSIX_EXTENDED
                   : RE_SYNTAX_POSIX_BASIC)
           & ~RE_UNMATCHED_RIGHT_PAREN_ORD;

    syntax |= RE_NO_POSIX_BACKTRACKING;
#ifdef RE_ICASE
    syntax |= (flags & REG_ICASE) ? RE_ICASE : 0;
#endif

    /* If REG_NEWLINE is set, newlines are treated differently.  */
    if (flags & REG_NEWLINE)
      { /* REG_NEWLINE implies neither . nor [^...] match newline.  */
        syntax &= ~RE_DOT_NEWLINE;
        syntax |= RE_HAT_LISTS_NOT_NEWLINE;
      }

    /* GNU regex does not process \t & co. */
    last_re_len = normalize_text(last_re, last_re_len);
    re_set_syntax (syntax);
    error = re_compile_pattern (last_re, last_re_len, new_regex);
    new_regex->newline_anchor = (flags & REG_NEWLINE) != 0;

    new_regex->translate = NULL;
#ifndef RE_ICASE
    if (flags & REG_ICASE)
      {
        static char translate[1 << (sizeof(char) * 8)];
    int i;
    for (i = 0; i < sizeof(translate) / sizeof(char); i++)
      translate[i] = tolower (i);

        new_regex->translate = translate;
      }
#endif

    if (error)
      bad_prog(error);
  }
#endif

  FREE(last_re);

  /* Just to be sure, I mark this as not POSIXLY_CORRECT behavior */
  if (new_regex->re_nsub < needed_sub && !POSIXLY_CORRECT)
    {
      char buf[200];
      sprintf(buf, _("Invalid reference \\%d on `s' command's RHS"),
          needed_sub);
      bad_prog(buf);
    }

  return new_regex;
}

#ifdef REG_PERL
static void
copy_regs (regs, pmatch, nregs)
     struct re_registers *regs;
     regmatch_t *pmatch;
     int nregs;
{
  int i;
  int need_regs = nregs + 1;
  /* We need one extra element beyond `num_regs' for the `-1' marker GNU code
     uses.  */

  /* Have the register data arrays been allocated?  */
  if (!regs->start)
    { /* No.  So allocate them with malloc.  */
      regs->start = MALLOC (need_regs, regoff_t);
      regs->end = MALLOC (need_regs, regoff_t);
      regs->num_regs = need_regs;
    }
  else if (need_regs > regs->num_regs)
    { /* Yes.  We also need more elements than were already
         allocated, so reallocate them.  */
      regs->start = REALLOC (regs->start, need_regs, regoff_t);
      regs->end = REALLOC (regs->end, need_regs, regoff_t);
      regs->num_regs = need_regs;
    }

  /* Copy the regs.  */
  for (i = 0; i < nregs; ++i)
    {
      regs->start[i] = pmatch[i].rm_so;
      regs->end[i] = pmatch[i].rm_eo;
    }
  for ( ; i < regs->num_regs; ++i)
    regs->start[i] = regs->end[i] = -1;
}
#endif

int
match_regex(regex, buf, buflen, buf_start_offset, regarray, regsize)
  regex_t *regex;
  char *buf;
  size_t buflen;
  size_t buf_start_offset;
  struct re_registers *regarray;
  int regsize;
{
  int ret;
  static regex_t *regex_last;
#ifdef REG_PERL
  regmatch_t *regmatch;
  if (regsize)
    regmatch = (regmatch_t *) alloca (sizeof (regmatch_t) * regsize);
  else
    regmatch = NULL;
#endif

  /* printf ("Matching from %d/%d\n", buf_start_offset, buflen); */

  /* Keep track of the last regexp matched. */
  if (!regex)
    {
      regex = regex_last;
      if (!regex_last)
    bad_prog(_(NO_REGEX));
    }
  else
    regex_last = regex;

#ifdef REG_PERL
  ret = regexec2(regex, 
         buf, CAST(int)buflen, CAST(int)buf_start_offset,
         regsize, regmatch, 0);

  if (regsize)
    copy_regs (regarray, regmatch, regsize);

  return (ret == 0);
#else
  regex->regs_allocated = REGS_REALLOCATE;
  ret = re_search(regex, buf, buflen, buf_start_offset,
          buflen - buf_start_offset,
          regsize ? regarray : NULL);

  return (ret > -1);
#endif
}


#ifdef DEBUG_LEAKS
void
release_regex(regex)
  regex_t *regex;
{
  regfree(regex);
  FREE(regex);
}
#endif /*DEBUG_LEAKS*/

https://git.savannah.gnu.org/cgit/sed.git/tree/sed/fmt.c?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/sed/fmt.c used in: Step 691
/* `L' command implementation for GNU sed, based on GNU fmt 1.22.
   Copyright (C) 1994, 1995, 1996, 2002, 2003 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.  */

/* GNU fmt was written by Ross Paterson <rap@doc.ic.ac.uk>.  */

#include "sed.h"

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>

#if HAVE_LIMITS_H
# include <limits.h>
#endif

#ifndef UINT_MAX
# define UINT_MAX ((unsigned int) ~(unsigned int) 0)
#endif

#ifndef INT_MAX
# define INT_MAX ((int) (UINT_MAX >> 1))
#endif

/* The following parameters represent the program's idea of what is
   "best".  Adjust to taste, subject to the caveats given.  */

/* Prefer lines to be LEEWAY % shorter than the maximum width, giving
   room for optimization.  */
#define LEEWAY  7

/* Costs and bonuses are expressed as the equivalent departure from the
   optimal line length, multiplied by 10.  e.g. assigning something a
   cost of 50 means that it is as bad as a line 5 characters too short
   or too long.  The definition of SHORT_COST(n) should not be changed.
   However, EQUIV(n) may need tuning.  */

typedef long COST;

#define MAXCOST (~(((unsigned long) 1) << (8 * sizeof (COST) -1)))

#define SQR(n)      ((n) * (n))
#define EQUIV(n)    SQR ((COST) (n))

/* Cost of a filled line n chars longer or shorter than best_width.  */
#define SHORT_COST(n)   EQUIV ((n) * 10)

/* Cost of the difference between adjacent filled lines.  */
#define RAGGED_COST(n)  (SHORT_COST (n) / 2)

/* Basic cost per line.  */
#define LINE_COST   EQUIV (70)

/* Cost of breaking a line after the first word of a sentence, where
   the length of the word is N.  */
#define WIDOW_COST(n)   (EQUIV (200) / ((n) + 2))

/* Cost of breaking a line before the last word of a sentence, where
   the length of the word is N.  */
#define ORPHAN_COST(n)  (EQUIV (150) / ((n) + 2))

/* Bonus for breaking a line at the end of a sentence.  */
#define SENTENCE_BONUS  EQUIV (50)

/* Cost of breaking a line after a period not marking end of a sentence.
   With the definition of sentence we are using (borrowed from emacs, see
   get_line()) such a break would then look like a sentence break.  Hence
   we assign a very high cost -- it should be avoided unless things are
   really bad.  */
#define NOBREAK_COST    EQUIV (600)

/* Bonus for breaking a line before open parenthesis.  */
#define PAREN_BONUS EQUIV (40)

/* Bonus for breaking a line after other punctuation.  */
#define PUNCT_BONUS EQUIV(40)

/* Credit for breaking a long paragraph one line later.  */
#define LINE_CREDIT EQUIV(3)

/* Size of paragraph buffer in words.  Longer paragraphs are handled
   neatly (cf. flush_paragraph()), so there's little to gain by making
   these larger.  */
#define MAXWORDS    1000

#define GETC()          (parabuf == end_of_parabuf ? EOF : *parabuf++)

/* Extra ctype(3)-style macros.  */

#define isopen(c)   (strchr ("([`'\"", (c)) != NULL)
#define isclose(c)  (strchr (")]'\"", (c)) != NULL)
#define isperiod(c) (strchr (".?!", (c)) != NULL)

/* Size of a tab stop, for expansion on input and re-introduction on
   output.  */
#define TABWIDTH    8

/* Word descriptor structure.  */

typedef struct Word WORD;

struct Word
  {

    /* Static attributes determined during input.  */

    const char *text;       /* the text of the word */
    short length;       /* length of this word */
    short space;        /* the size of the following space */
    unsigned paren:1;       /* starts with open paren */
    unsigned period:1;      /* ends in [.?!])* */
    unsigned punct:1;       /* ends in punctuation */
    unsigned final:1;       /* end of sentence */

    /* The remaining fields are computed during the optimization.  */

    short line_length;      /* length of the best line starting here */
    COST best_cost;     /* cost of best paragraph starting here */
    WORD *next_break;       /* break which achieves best_cost */
  };

/* Forward declarations.  */

extern void fmt P_ ((char *line, char *line_end, int max_length, FILE *output_file));
static flagT get_paragraph P_ ((void));
static int get_line P_ ((int c));
static int get_space P_ ((int c));
static int copy_rest P_ ((int c));
static flagT same_para P_ ((int c));
static void flush_paragraph P_ ((void));
static void fmt_paragraph P_ ((void));
static void check_punctuation P_ ((WORD *w));
static COST base_cost P_ ((WORD *this));
static COST line_cost P_ ((WORD *next, int len));
static void put_paragraph P_ ((WORD *finish));
static void put_line P_ ((WORD *w, int indent));
static void put_word P_ ((WORD *w));
static void put_space P_ ((int space));

/* Option values.  */

/* User-supplied maximum line width (default WIDTH).  The only output
   lines
   longer than this will each comprise a single word.  */
static int max_width;

/* Space for the paragraph text.  */
static char *parabuf;

/* End of space for the paragraph text.  */
static char *end_of_parabuf;

/* The file on which we output */
static FILE *outfile;

/* Values derived from the option values.  */

/* The preferred width of text lines, set to LEEWAY % less than max_width.  */
static int best_width;

/* Dynamic variables.  */

/* Start column of the character most recently read from the input file.  */
static int in_column;

/* Start column of the next character to be written to stdout.  */
static int out_column;

/* The words of a paragraph -- longer paragraphs are handled neatly
   (cf. flush_paragraph()).  */
static WORD words[MAXWORDS];

/* A pointer into the above word array, indicating the first position
   after the last complete word.  Sometimes it will point at an incomplete
   word.  */
static WORD *word_limit;

/* Indentation of the first line of the current paragraph.  */
static int first_indent;

/* Indentation of other lines of the current paragraph */
static int other_indent;

/* The last character read from the input file.  */
static int next_char;

/* If nonzero, the length of the last line output in the current
   paragraph, used to charge for raggedness at the split point for long
   paragraphs chosen by fmt_paragraph().  */
static int last_line_length;

/* read file F and send formatted output to stdout.  */

void
fmt (char *line, char *line_end, int max_length, FILE *output_file)
{
  parabuf = line;
  end_of_parabuf = line_end;
  outfile = output_file;

  max_width = max_length;
  best_width = max_width * (201 - 2 * LEEWAY) / 200;

  in_column = 0;
  other_indent = 0;
  next_char = GETC();
  while (get_paragraph ())
    {
      fmt_paragraph ();
      put_paragraph (word_limit);
    }
}

/* Read a paragraph from input file F.  A paragraph consists of a
   maximal number of non-blank (excluding any prefix) lines
   with the same indent.

   Return FALSE if end-of-file was encountered before the start of a
   paragraph, else TRUE.  */

static flagT
get_paragraph ()
{
  register int c;

  last_line_length = 0;
  c = next_char;

  /* Scan (and copy) blank lines, and lines not introduced by the prefix.  */

  while (c == '\n' || c == EOF)
    {
      c = copy_rest (c);
      if (c == EOF)
    {
      next_char = EOF;
      return FALSE;
    }
      putc ('\n', outfile);
      c = GETC();
    }

  /* Got a suitable first line for a paragraph.  */

  first_indent = in_column;
  word_limit = words;
  c = get_line (c);

  /* Read rest of paragraph.  */

  other_indent = in_column;
  while (same_para (c) && in_column == other_indent)
    c = get_line (c);

  (word_limit - 1)->period = (word_limit - 1)->final = TRUE;
  next_char = c;
  return TRUE;
}

/* Copy to the output a blank line.  In the latter, C is \n or EOF.
   Return the character (\n or EOF) ending the line.  */

static int
copy_rest (register int c)
{
  out_column = 0;
  while (c != '\n' && c != EOF)
    {
      putc (c, outfile);
      c = GETC();
    }
  return c;
}

/* Return TRUE if a line whose first non-blank character after the
   prefix (if any) is C could belong to the current paragraph,
   otherwise FALSE.  */

static flagT
same_para (register int c)
{
  return (c != '\n' && c != EOF);
}

/* Read a line from the input data given first non-blank character C
   after the prefix, and the following indent, and break it into words.
   A word is a maximal non-empty string of non-white characters.  A word
   ending in [.?!]["')\]]* and followed by end-of-line or at least two
   spaces ends a sentence, as in emacs.

   Return the first non-blank character of the next line.  */

static int
get_line (register int c)
{
  int start;
  register WORD *end_of_word;

  end_of_word = &words[MAXWORDS - 2];

  do
    {               /* for each word in a line */

      /* Scan word.  */

      word_limit->text = parabuf - 1;
      do
    c = GETC();
      while (c != EOF && !ISSPACE (c));
      word_limit->length = parabuf - word_limit->text - (c != EOF);
      in_column += word_limit->length;

      check_punctuation (word_limit);

      /* Scan inter-word space.  */

      start = in_column;
      c = get_space (c);
      word_limit->space = in_column - start;
      word_limit->final = (c == EOF
               || (word_limit->period
                   && (c == '\n' || word_limit->space > 1)));
      if (c == '\n' || c == EOF)
    word_limit->space = word_limit->final ? 2 : 1;
      if (word_limit == end_of_word)
    flush_paragraph ();
      word_limit++;
      if (c == EOF)
    {
      in_column = first_indent;
      return EOF;
    }
    }
  while (c != '\n');

  in_column = 0;
  c = GETC();
  return get_space (c);
}

/* Read blank characters from the input data, starting with C, and keeping
   in_column up-to-date.  Return first non-blank character.  */

static int
get_space (register int c)
{
  for (;;)
    {
      if (c == ' ')
    in_column++;
      else if (c == '\t')
    in_column = (in_column / TABWIDTH + 1) * TABWIDTH;
      else
    return c;
      c = GETC();
    }
}

/* Set extra fields in word W describing any attached punctuation.  */

static void
check_punctuation (register WORD *w)
{
  register const char *start, *finish;

  start = w->text;
  finish = start + (w->length - 1);
  w->paren = isopen (*start);
  w->punct = ISPUNCT (*finish);
  while (isclose (*finish) && finish > start)
    finish--;
  w->period = isperiod (*finish);
}

/* Flush part of the paragraph to make room.  This function is called on
   hitting the limit on the number of words or characters.  */

static void
flush_paragraph (void)
{
  WORD *split_point;
  register WORD *w;
  COST best_break;

  /* - format what you have so far as a paragraph,
     - find a low-cost line break near the end,
     - output to there,
     - make that the start of the paragraph.  */

  fmt_paragraph ();

  /* Choose a good split point.  */

  split_point = word_limit;
  best_break = MAXCOST;
  for (w = words->next_break; w != word_limit; w = w->next_break)
    {
      if (w->best_cost - w->next_break->best_cost < best_break)
    {
      split_point = w;
      best_break = w->best_cost - w->next_break->best_cost;
    }
      if (best_break <= MAXCOST - LINE_CREDIT)
    best_break += LINE_CREDIT;
    }
  put_paragraph (split_point);

  /* Copy words from split_point down to word -- we use memmove because
     the source and target may overlap.  */

  memmove ((char *) words, (char *) split_point,
     (word_limit - split_point + 1) * sizeof (WORD));
  word_limit -= split_point - words;
}

/* Compute the optimal formatting for the whole paragraph by computing
   and remembering the optimal formatting for each suffix from the empty
   one to the whole paragraph.  */

static void
fmt_paragraph (void)
{
  register WORD *start, *w;
  register int len;
  register COST wcost, best;
  int saved_length;

  word_limit->best_cost = 0;
  saved_length = word_limit->length;
  word_limit->length = max_width;   /* sentinel */

  for (start = word_limit - 1; start >= words; start--)
    {
      best = MAXCOST;
      len = start == words ? first_indent : other_indent;

      /* At least one word, however long, in the line.  */

      w = start;
      len += w->length;
      do
    {
      w++;

      /* Consider breaking before w.  */

      wcost = line_cost (w, len) + w->best_cost;
      if (start == words && last_line_length > 0)
        wcost += RAGGED_COST (len - last_line_length);
      if (wcost < best)
        {
          best = wcost;
          start->next_break = w;
          start->line_length = len;
        }
      len += (w - 1)->space + w->length;    /* w > start >= words */
    }
      while (len < max_width);
      start->best_cost = best + base_cost (start);
    }

  word_limit->length = saved_length;
}

/* Return the constant component of the cost of breaking before the
   word THIS.  */

static COST
base_cost (register WORD *this)
{
  register COST cost;

  cost = LINE_COST;

  if (this > words)
    {
      if ((this - 1)->period)
    {
      if ((this - 1)->final)
        cost -= SENTENCE_BONUS;
      else
        cost += NOBREAK_COST;
    }
      else if ((this - 1)->punct)
    cost -= PUNCT_BONUS;
      else if (this > words + 1 && (this - 2)->final)
    cost += WIDOW_COST ((this - 1)->length);
    }

  if (this->paren)
    cost -= PAREN_BONUS;
  else if (this->final)
    cost += ORPHAN_COST (this->length);

  return cost;
}

/* Return the component of the cost of breaking before word NEXT that
   depends on LEN, the length of the line beginning there.  */

static COST
line_cost (register WORD *next, register int len)
{
  register int n;
  register COST cost;

  if (next == word_limit)
    return 0;
  n = best_width - len;
  cost = SHORT_COST (n);
  if (next->next_break != word_limit)
    {
      n = len - next->line_length;
      cost += RAGGED_COST (n);
    }
  return cost;
}

/* Output to stdout a paragraph from word up to (but not including)
   FINISH, which must be in the next_break chain from word.  */

static void
put_paragraph (register WORD *finish)
{
  register WORD *w;

  put_line (words, first_indent);
  for (w = words->next_break; w != finish; w = w->next_break)
    put_line (w, other_indent);
}

/* Output to stdout the line beginning with word W, beginning in column
   INDENT, including the prefix (if any).  */

static void
put_line (register WORD *w, int indent)
{
  register WORD *endline;
  out_column = 0;
  put_space (indent);

  endline = w->next_break - 1;
  for (; w != endline; w++)
    {
      put_word (w);
      put_space (w->space);
    }
  put_word (w);
  last_line_length = out_column;
  putc ('\n', outfile);
}

/* Output to stdout the word W.  */

static void
put_word (register WORD *w)
{
  register const char *s;
  register int n;

  s = w->text;
  for (n = w->length; n != 0; n--)
    putc (*s++, outfile);
  out_column += w->length;
}

/* Output to stdout SPACE spaces, or equivalent tabs.  */

static void
put_space (int space)
{
  out_column += space;
  while (space--)
    putc (' ', outfile);
}

https://git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c?id=9c9919efe2166efd32409054005619062624226c

File: ../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/sed/sed.c used in: Step 692
#define COPYRIGHT_NOTICE "Copyright (C) 2003 Free Software Foundation, Inc."
#define BUG_ADDRESS "bonzini@gnu.org"

/*  GNU SED, a batch stream editor.
    Copyright (C) 1989,90,91,92,93,94,95,98,99,2002,2003
    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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */


#include "sed.h"

#include <stdio.h>
#ifdef HAVE_STRINGS_H
# include <strings.h>
#else
# include <string.h>
#endif /*HAVE_STRINGS_H*/
#ifdef HAVE_MEMORY_H
# include <memory.h>
#endif

#ifndef HAVE_STRCHR
# define strchr index
# define strrchr rindex
#endif

#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif

#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#ifdef HAVE_LOCALE_H
# include <locale.h>
#endif
#include "getopt.h"

#ifndef BOOTSTRAP
#ifndef HAVE_STDLIB_H
 extern char *getenv P_((const char *));
#endif
#endif

#ifndef HAVE_STRTOUL
# define ATOI(x)    atoi(x)
#else
# define ATOI(x)    strtoul(x, NULL, 0)
#endif

int extended_regexp_flags = 0;

/* If set, fflush(stdout) on every line output. */
flagT unbuffered_output = FALSE;

/* If set, don't write out the line unless explicitly told to */
flagT no_default_output = FALSE;

/* If set, reset line counts on every new file. */
flagT separate_files = FALSE;

/* How do we edit files in-place? (we don't if NULL) */
char *in_place_extension = NULL;

/* Do we need to be pedantically POSIX compliant? */
flagT POSIXLY_CORRECT;

/* How long should the `l' command's output line be? */
countT lcmd_out_line_len = 70;

/* The complete compiled SED program that we are going to run: */
static struct vector *the_program = NULL;

static void usage P_((int));
static void
usage(status)
  int status;
{
  FILE *out = status ? stderr : stdout;

#ifdef REG_PERL
#define PERL_HELP _("  -R, --regexp-perl\n                 use Perl 5's regular expressions syntax in the script.\n")
#else
#define PERL_HELP ""
#endif

  fprintf(out, _("\
Usage: %s [OPTION]... {script-only-if-no-other-script} [input-file]...\n\
\n\
  -n, --quiet, --silent\n\
                 suppress automatic printing of pattern space\n\
  -e script, --expression=script\n\
                 add the script to the commands to be executed\n\
  -f script-file, --file=script-file\n\
                 add the contents of script-file to the commands to be executed\n\
  -i[suffix], --in-place[=suffix]\n\
                 edit files in place (makes backup if extension supplied)\n\
  -l N, --line-length=N\n\
                 specify the desired line-wrap length for the `l' command\n\
  -r, --regexp-extended\n\
                 use extended regular expressions in the script.\n%s\
  -s, --separate\n\
                 consider files as separate rather than as a single continuous\n\
                 long stream.\n\
  -u, --unbuffered\n\
                 load minimal amounts of data from the input files and flush\n\
                 the output buffers more often\n\
      --help     display this help and exit\n\
  -V, --version  output version information and exit\n\
\n\
If no -e, --expression, -f, or --file option is given, then the first\n\
non-option argument is taken as the sed script to interpret.  All\n\
remaining arguments are names of input files; if no input files are\n\
specified, then the standard input is read.\n\
\n"), myname, PERL_HELP);
  fprintf(out, _("E-mail bug reports to: %s .\n\
Be sure to include the word ``%s'' somewhere in the ``Subject:'' field.\n"),
      BUG_ADDRESS, PACKAGE);

  ck_fclose (NULL);
  exit (status);
}

int
main(argc, argv)
  int argc;
  char **argv;
{
#ifdef REG_PERL
#define SHORTOPTS "shnrRuVe:f:l:i::"
#else
#define SHORTOPTS "shnruVe:f:l:i::"
#endif

  static struct option longopts[] = {
    {"regexp-extended", 0, NULL, 'r'},
#ifdef REG_PERL
    {"regexp-perl", 0, NULL, 'R'},
#endif
    {"expression", 1, NULL, 'e'},
    {"file", 1, NULL, 'f'},
    {"in-place", 2, NULL, 'i'},
    {"line-length", 1, NULL, 'l'},
    {"quiet", 0, NULL, 'n'},
    {"silent", 0, NULL, 'n'},
    {"separate", 0, NULL, 's'},
    {"unbuffered", 0, NULL, 'u'},
    {"version", 0, NULL, 'V'},
    {"help", 0, NULL, 'h'},
    {NULL, 0, NULL, 0}
  };

  int opt;
  int return_code;

  initialize_main(&argc, &argv);

#if ENABLE_NLS
#if HAVE_SETLOCALE
  /* Set locale according to user's wishes.  */
  setlocale (LC_ALL, "");
#endif

  /* Tell program which translations to use and where to find.  */
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);
#endif

  POSIXLY_CORRECT = (getenv("POSIXLY_CORRECT") != NULL);

  /* If environment variable `COLS' is set, use its value for
     the baseline setting of `lcmd_out_line_len'.  The "-1"
     is to avoid gratuitous auto-line-wrap on ttys.
   */
  {
    const char *p = getenv("COLS");
    if (p)
      {
    countT t = ATOI(p);
    if (1 < t)
      lcmd_out_line_len = t-1;
      }
  }

  myname = *argv;
  while ((opt = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != EOF)
    {
      switch (opt)
    {
    case 'n':
      no_default_output = TRUE;
      break;
    case 'e':
      the_program = compile_string(the_program, optarg, strlen(optarg));
      break;
    case 'f':
      the_program = compile_file(the_program, optarg);
      break;

    case 'i':
      separate_files = TRUE;
      if (optarg == NULL)
        in_place_extension = "*";
      else if (strchr(optarg, '*') == NULL)
        {
          in_place_extension = xmalloc (strlen(optarg) + 2);
          in_place_extension[0] = '*';
          strcpy (in_place_extension + 1, optarg);
        }
      else
        in_place_extension = ck_strdup(optarg);
      break;

    case 'l':
      lcmd_out_line_len = ATOI(optarg);
      break;
    case 'r':
      if (extended_regexp_flags)
        usage(4);
      extended_regexp_flags = REG_EXTENDED;
      break;

#ifdef REG_PERL
    case 'R':
      if (extended_regexp_flags)
        usage(4);
      extended_regexp_flags = REG_PERL;
      break;
#endif

    case 's':
      separate_files = TRUE;
      break;

    case 'u':
      unbuffered_output = TRUE;
      break;

    case 'V':
#ifdef REG_PERL
      fprintf(stdout, _("super-sed version %s\n"), VERSION);
      fprintf(stdout, _("based on GNU sed version 3.02.80\n\n"));
#else
      fprintf(stdout, _("GNU sed version %s\n"), VERSION);
#endif
      fprintf(stdout, _("%s\n\
This is free software; see the source for copying conditions.  There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE,\n\
to the extent permitted by law.\n\
"), COPYRIGHT_NOTICE);
      ck_fclose (NULL);
      exit (0);
    case 'h':
      usage(0);
    default:
      usage(4);
    }
    }

  if (!the_program)
    {
      if (optind < argc)
    {
      char *arg = argv[optind++];
      the_program = compile_string(the_program, arg, strlen(arg));
    }
      else
    usage(4);
    }
  check_final_program(the_program);

  return_code = process_files(the_program, argv+optind);

  finish_program(the_program);
  ck_fclose(NULL);

  return return_code;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/sed-4.0.9/sed-4.0.9.checksums

File: ../git/live-bootstrap/sysa/sed-4.0.9/sed-4.0.9.checksums used in: Step 697
bf0d4ef807c706a2b201c7d132c70314ad96aa24353d444bcaed4eca6c95a528  /usr/bin/sed

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bzip2-1.0.8/sources

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/sources used in: Step 699
https://sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bzip2-1.0.8/patches/mes-libc.patch

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/patches/mes-libc.patch used in: Step 705
SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>

SPDX-License-Identifier: bzip2-1.0.6

mes libc has no time support, so we remove that.

It also does not have fch{own,mod}, which we don't care about in the bootstrap
anyway, so we can null-op those calls.

diff -r -N -U3 bzip2.c bzip2.c
--- bzip2.c 2019-07-13 18:50:05.000000000 +0100
+++ bzip2.c 2021-01-14 14:11:40.160213521 +0000
@@ -1051,12 +1051,9 @@
 {
 #  if BZ_UNIX
    IntNative      retVal;
-   struct utimbuf uTimBuf;
 
-   uTimBuf.actime = fileMetaInfo.st_atime;
-   uTimBuf.modtime = fileMetaInfo.st_mtime;
 
-   retVal = utime ( dstName, &uTimBuf );
+   retVal = 0;
    ERROR_IF_NOT_ZERO ( retVal );
 #  endif
 }
diff -r -N -U3 utime.h utime.h
--- utime.h 1970-01-01 01:00:00.000000000 +0100
+++ utime.h 2021-01-14 18:11:11.253825037 +0000
@@ -0,0 +1,2 @@
+#define fchown(filedes, owner, group) 0
+#define fchmod(filedes, mode) 0

https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/bzip2.c

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/bzip2.c used in: Step 705, Step 717
/*-----------------------------------------------------------*/
/*--- A block-sorting, lossless compressor        bzip2.c ---*/
/*-----------------------------------------------------------*/

/* ------------------------------------------------------------------
   This file is part of bzip2/libbzip2, a program and library for
   lossless, block-sorting data compression.

   bzip2/libbzip2 version 1.0.8 of 13 July 2019
   Copyright (C) 1996-2019 Julian Seward <jseward@acm.org>

   Please read the WARNING, DISCLAIMER and PATENTS sections in the 
   README file.

   This program is released under the terms of the license contained
   in the file LICENSE.
   ------------------------------------------------------------------ */


/* Place a 1 beside your platform, and 0 elsewhere.
   Generic 32-bit Unix.
   Also works on 64-bit Unix boxes.
   This is the default.
*/
#define BZ_UNIX      1

/*--
  Win32, as seen by Jacob Navia's excellent
  port of (Chris Fraser & David Hanson)'s excellent
  lcc compiler.  Or with MS Visual C.
  This is selected automatically if compiled by a compiler which
  defines _WIN32, not including the Cygwin GCC.
--*/
#define BZ_LCCWIN32  0

#if defined(_WIN32) && !defined(__CYGWIN__)
#undef  BZ_LCCWIN32
#define BZ_LCCWIN32 1
#undef  BZ_UNIX
#define BZ_UNIX 0
#endif


/*---------------------------------------------*/
/*--
  Some stuff for all platforms.
--*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <math.h>
#include <errno.h>
#include <ctype.h>
#include "bzlib.h"

#define ERROR_IF_EOF(i)       { if ((i) == EOF)  ioError(); }
#define ERROR_IF_NOT_ZERO(i)  { if ((i) != 0)    ioError(); }
#define ERROR_IF_MINUS_ONE(i) { if ((i) == (-1)) ioError(); }


/*---------------------------------------------*/
/*--
   Platform-specific stuff.
--*/

#if BZ_UNIX
#   include <fcntl.h>
#   include <sys/types.h>
#   include <utime.h>
#   include <unistd.h>
#   include <sys/stat.h>
#   include <sys/times.h>

#   define PATH_SEP    '/'
#   define MY_LSTAT    lstat
#   define MY_STAT     stat
#   define MY_S_ISREG  S_ISREG
#   define MY_S_ISDIR  S_ISDIR

#   define APPEND_FILESPEC(root, name) \
      root=snocString((root), (name))

#   define APPEND_FLAG(root, name) \
      root=snocString((root), (name))

#   define SET_BINARY_MODE(fd) /**/

#   ifdef __GNUC__
#      define NORETURN __attribute__ ((noreturn))
#   else
#      define NORETURN /**/
#   endif

#   ifdef __DJGPP__
#     include <io.h>
#     include <fcntl.h>
#     undef MY_LSTAT
#     undef MY_STAT
#     define MY_LSTAT stat
#     define MY_STAT stat
#     undef SET_BINARY_MODE
#     define SET_BINARY_MODE(fd)                        \
        do {                                            \
           int retVal = setmode ( fileno ( fd ),        \
                                  O_BINARY );           \
           ERROR_IF_MINUS_ONE ( retVal );               \
        } while ( 0 )
#   endif

#   ifdef __CYGWIN__
#     include <io.h>
#     include <fcntl.h>
#     undef SET_BINARY_MODE
#     define SET_BINARY_MODE(fd)                        \
        do {                                            \
           int retVal = setmode ( fileno ( fd ),        \
                                  O_BINARY );           \
           ERROR_IF_MINUS_ONE ( retVal );               \
        } while ( 0 )
#   endif
#endif /* BZ_UNIX */



#if BZ_LCCWIN32
#   include <io.h>
#   include <fcntl.h>
#   include <sys/stat.h>

#   define NORETURN       /**/
#   define PATH_SEP       '\\'
#   define MY_LSTAT       _stati64
#   define MY_STAT        _stati64
#   define MY_S_ISREG(x)  ((x) & _S_IFREG)
#   define MY_S_ISDIR(x)  ((x) & _S_IFDIR)

#   define APPEND_FLAG(root, name) \
      root=snocString((root), (name))

#   define APPEND_FILESPEC(root, name)                \
      root = snocString ((root), (name))

#   define SET_BINARY_MODE(fd)                        \
      do {                                            \
         int retVal = setmode ( fileno ( fd ),        \
                                O_BINARY );           \
         ERROR_IF_MINUS_ONE ( retVal );               \
      } while ( 0 )

#endif /* BZ_LCCWIN32 */


/*---------------------------------------------*/
/*--
  Some more stuff for all platforms :-)
--*/

typedef char            Char;
typedef unsigned char   Bool;
typedef unsigned char   UChar;
typedef int             Int32;
typedef unsigned int    UInt32;
typedef short           Int16;
typedef unsigned short  UInt16;
                                       
#define True  ((Bool)1)
#define False ((Bool)0)

/*--
  IntNative is your platform's `native' int size.
  Only here to avoid probs with 64-bit platforms.
--*/
typedef int IntNative;


/*---------------------------------------------------*/
/*--- Misc (file handling) data decls             ---*/
/*---------------------------------------------------*/

Int32   verbosity;
Bool    keepInputFiles, smallMode, deleteOutputOnInterrupt;
Bool    forceOverwrite, testFailsExist, unzFailsExist, noisy;
Int32   numFileNames, numFilesProcessed, blockSize100k;
Int32   exitValue;

/*-- source modes; F==file, I==stdin, O==stdout --*/
#define SM_I2O           1
#define SM_F2O           2
#define SM_F2F           3

/*-- operation modes --*/
#define OM_Z             1
#define OM_UNZ           2
#define OM_TEST          3

Int32   opMode;
Int32   srcMode;

#define FILE_NAME_LEN 1034

Int32   longestFileName;
Char    inName [FILE_NAME_LEN];
Char    outName[FILE_NAME_LEN];
Char    tmpName[FILE_NAME_LEN];
Char    *progName;
Char    progNameReally[FILE_NAME_LEN];
FILE    *outputHandleJustInCase;
Int32   workFactor;

static void    panic                 ( const Char* ) NORETURN;
static void    ioError               ( void )        NORETURN;
static void    outOfMemory           ( void )        NORETURN;
static void    configError           ( void )        NORETURN;
static void    crcError              ( void )        NORETURN;
static void    cleanUpAndFail        ( Int32 )       NORETURN;
static void    compressedStreamEOF   ( void )        NORETURN;

static void    copyFileName ( Char*, Char* );
static void*   myMalloc     ( Int32 );
static void    applySavedFileAttrToOutputFile ( IntNative fd );



/*---------------------------------------------------*/
/*--- An implementation of 64-bit ints.  Sigh.    ---*/
/*--- Roll on widespread deployment of ANSI C9X ! ---*/
/*---------------------------------------------------*/

typedef
   struct { UChar b[8]; } 
   UInt64;


static
void uInt64_from_UInt32s ( UInt64* n, UInt32 lo32, UInt32 hi32 )
{
   n->b[7] = (UChar)((hi32 >> 24) & 0xFF);
   n->b[6] = (UChar)((hi32 >> 16) & 0xFF);
   n->b[5] = (UChar)((hi32 >> 8)  & 0xFF);
   n->b[4] = (UChar) (hi32        & 0xFF);
   n->b[3] = (UChar)((lo32 >> 24) & 0xFF);
   n->b[2] = (UChar)((lo32 >> 16) & 0xFF);
   n->b[1] = (UChar)((lo32 >> 8)  & 0xFF);
   n->b[0] = (UChar) (lo32        & 0xFF);
}


static
double uInt64_to_double ( UInt64* n )
{
   Int32  i;
   double base = 1.0;
   double sum  = 0.0;
   for (i = 0; i < 8; i++) {
      sum  += base * (double)(n->b[i]);
      base *= 256.0;
   }
   return sum;
}


static
Bool uInt64_isZero ( UInt64* n )
{
   Int32 i;
   for (i = 0; i < 8; i++)
      if (n->b[i] != 0) return 0;
   return 1;
}


/* Divide *n by 10, and return the remainder.  */
static 
Int32 uInt64_qrm10 ( UInt64* n )
{
   UInt32 rem, tmp;
   Int32  i;
   rem = 0;
   for (i = 7; i >= 0; i--) {
      tmp = rem * 256 + n->b[i];
      n->b[i] = tmp / 10;
      rem = tmp % 10;
   }
   return rem;
}


/* ... and the Whole Entire Point of all this UInt64 stuff is
   so that we can supply the following function.
*/
static
void uInt64_toAscii ( char* outbuf, UInt64* n )
{
   Int32  i, q;
   UChar  buf[32];
   Int32  nBuf   = 0;
   UInt64 n_copy = *n;
   do {
      q = uInt64_qrm10 ( &n_copy );
      buf[nBuf] = q + '0';
      nBuf++;
   } while (!uInt64_isZero(&n_copy));
   outbuf[nBuf] = 0;
   for (i = 0; i < nBuf; i++) 
      outbuf[i] = buf[nBuf-i-1];
}


/*---------------------------------------------------*/
/*--- Processing of complete files and streams    ---*/
/*---------------------------------------------------*/

/*---------------------------------------------*/
static 
Bool myfeof ( FILE* f )
{
   Int32 c = fgetc ( f );
   if (c == EOF) return True;
   ungetc ( c, f );
   return False;
}


/*---------------------------------------------*/
static 
void compressStream ( FILE *stream, FILE *zStream )
{
   BZFILE* bzf = NULL;
   UChar   ibuf[5000];
   Int32   nIbuf;
   UInt32  nbytes_in_lo32, nbytes_in_hi32;
   UInt32  nbytes_out_lo32, nbytes_out_hi32;
   Int32   bzerr, bzerr_dummy, ret;

   SET_BINARY_MODE(stream);
   SET_BINARY_MODE(zStream);

   if (ferror(stream)) goto errhandler_io;
   if (ferror(zStream)) goto errhandler_io;

   bzf = BZ2_bzWriteOpen ( &bzerr, zStream, 
                           blockSize100k, verbosity, workFactor );   
   if (bzerr != BZ_OK) goto errhandler;

   if (verbosity >= 2) fprintf ( stderr, "\n" );

   while (True) {

      if (myfeof(stream)) break;
      nIbuf = fread ( ibuf, sizeof(UChar), 5000, stream );
      if (ferror(stream)) goto errhandler_io;
      if (nIbuf > 0) BZ2_bzWrite ( &bzerr, bzf, (void*)ibuf, nIbuf );
      if (bzerr != BZ_OK) goto errhandler;

   }

   BZ2_bzWriteClose64 ( &bzerr, bzf, 0, 
                        &nbytes_in_lo32, &nbytes_in_hi32,
                        &nbytes_out_lo32, &nbytes_out_hi32 );
   if (bzerr != BZ_OK) goto errhandler;

   if (ferror(zStream)) goto errhandler_io;
   ret = fflush ( zStream );
   if (ret == EOF) goto errhandler_io;
   if (zStream != stdout) {
      Int32 fd = fileno ( zStream );
      if (fd < 0) goto errhandler_io;
      applySavedFileAttrToOutputFile ( fd );
      ret = fclose ( zStream );
      outputHandleJustInCase = NULL;
      if (ret == EOF) goto errhandler_io;
   }
   outputHandleJustInCase = NULL;
   if (ferror(stream)) goto errhandler_io;
   ret = fclose ( stream );
   if (ret == EOF) goto errhandler_io;

   if (verbosity >= 1) {
      if (nbytes_in_lo32 == 0 && nbytes_in_hi32 == 0) {
     fprintf ( stderr, " no data compressed.\n");
      } else {
     Char   buf_nin[32], buf_nout[32];
     UInt64 nbytes_in,   nbytes_out;
     double nbytes_in_d, nbytes_out_d;
     uInt64_from_UInt32s ( &nbytes_in, 
                   nbytes_in_lo32, nbytes_in_hi32 );
     uInt64_from_UInt32s ( &nbytes_out, 
                   nbytes_out_lo32, nbytes_out_hi32 );
     nbytes_in_d  = uInt64_to_double ( &nbytes_in );
     nbytes_out_d = uInt64_to_double ( &nbytes_out );
     uInt64_toAscii ( buf_nin, &nbytes_in );
     uInt64_toAscii ( buf_nout, &nbytes_out );
     fprintf ( stderr, "%6.3f:1, %6.3f bits/byte, "
           "%5.2f%% saved, %s in, %s out.\n",
           nbytes_in_d / nbytes_out_d,
           (8.0 * nbytes_out_d) / nbytes_in_d,
           100.0 * (1.0 - nbytes_out_d / nbytes_in_d),
           buf_nin,
           buf_nout
         );
      }
   }

   return;

   errhandler:
   BZ2_bzWriteClose64 ( &bzerr_dummy, bzf, 1, 
                        &nbytes_in_lo32, &nbytes_in_hi32,
                        &nbytes_out_lo32, &nbytes_out_hi32 );
   switch (bzerr) {
      case BZ_CONFIG_ERROR:
         configError(); break;
      case BZ_MEM_ERROR:
         outOfMemory (); break;
      case BZ_IO_ERROR:
         errhandler_io:
         ioError(); break;
      default:
         panic ( "compress:unexpected error" );
   }

   panic ( "compress:end" );
   /*notreached*/
}



/*---------------------------------------------*/
static 
Bool uncompressStream ( FILE *zStream, FILE *stream )
{
   BZFILE* bzf = NULL;
   Int32   bzerr, bzerr_dummy, ret, nread, streamNo, i;
   UChar   obuf[5000];
   UChar   unused[BZ_MAX_UNUSED];
   Int32   nUnused;
   void*   unusedTmpV;
   UChar*  unusedTmp;

   nUnused = 0;
   streamNo = 0;

   SET_BINARY_MODE(stream);
   SET_BINARY_MODE(zStream);

   if (ferror(stream)) goto errhandler_io;
   if (ferror(zStream)) goto errhandler_io;

   while (True) {

      bzf = BZ2_bzReadOpen ( 
               &bzerr, zStream, verbosity, 
               (int)smallMode, unused, nUnused
            );
      if (bzf == NULL || bzerr != BZ_OK) goto errhandler;
      streamNo++;

      while (bzerr == BZ_OK) {
         nread = BZ2_bzRead ( &bzerr, bzf, obuf, 5000 );
         if (bzerr == BZ_DATA_ERROR_MAGIC) goto trycat;
         if ((bzerr == BZ_OK || bzerr == BZ_STREAM_END) && nread > 0)
            fwrite ( obuf, sizeof(UChar), nread, stream );
         if (ferror(stream)) goto errhandler_io;
      }
      if (bzerr != BZ_STREAM_END) goto errhandler;

      BZ2_bzReadGetUnused ( &bzerr, bzf, &unusedTmpV, &nUnused );
      if (bzerr != BZ_OK) panic ( "decompress:bzReadGetUnused" );

      unusedTmp = (UChar*)unusedTmpV;
      for (i = 0; i < nUnused; i++) unused[i] = unusedTmp[i];

      BZ2_bzReadClose ( &bzerr, bzf );
      if (bzerr != BZ_OK) panic ( "decompress:bzReadGetUnused" );

      if (nUnused == 0 && myfeof(zStream)) break;
   }

   closeok:
   if (ferror(zStream)) goto errhandler_io;
   if (stream != stdout) {
      Int32 fd = fileno ( stream );
      if (fd < 0) goto errhandler_io;
      applySavedFileAttrToOutputFile ( fd );
   }
   ret = fclose ( zStream );
   if (ret == EOF) goto errhandler_io;

   if (ferror(stream)) goto errhandler_io;
   ret = fflush ( stream );
   if (ret != 0) goto errhandler_io;
   if (stream != stdout) {
      ret = fclose ( stream );
      outputHandleJustInCase = NULL;
      if (ret == EOF) goto errhandler_io;
   }
   outputHandleJustInCase = NULL;
   if (verbosity >= 2) fprintf ( stderr, "\n    " );
   return True;

   trycat: 
   if (forceOverwrite) {
      rewind(zStream);
      while (True) {
         if (myfeof(zStream)) break;
         nread = fread ( obuf, sizeof(UChar), 5000, zStream );
         if (ferror(zStream)) goto errhandler_io;
         if (nread > 0) fwrite ( obuf, sizeof(UChar), nread, stream );
         if (ferror(stream)) goto errhandler_io;
      }
      goto closeok;
   }
  
   errhandler:
   BZ2_bzReadClose ( &bzerr_dummy, bzf );
   switch (bzerr) {
      case BZ_CONFIG_ERROR:
         configError(); break;
      case BZ_IO_ERROR:
         errhandler_io:
         ioError(); break;
      case BZ_DATA_ERROR:
         crcError();
      case BZ_MEM_ERROR:
         outOfMemory();
      case BZ_UNEXPECTED_EOF:
         compressedStreamEOF();
      case BZ_DATA_ERROR_MAGIC:
         if (zStream != stdin) fclose(zStream);
         if (stream != stdout) fclose(stream);
         if (streamNo == 1) {
            return False;
         } else {
            if (noisy)
            fprintf ( stderr, 
                      "\n%s: %s: trailing garbage after EOF ignored\n",
                      progName, inName );
            return True;       
         }
      default:
         panic ( "decompress:unexpected error" );
   }

   panic ( "decompress:end" );
   return True; /*notreached*/
}


/*---------------------------------------------*/
static 
Bool testStream ( FILE *zStream )
{
   BZFILE* bzf = NULL;
   Int32   bzerr, bzerr_dummy, ret, streamNo, i;
   UChar   obuf[5000];
   UChar   unused[BZ_MAX_UNUSED];
   Int32   nUnused;
   void*   unusedTmpV;
   UChar*  unusedTmp;

   nUnused = 0;
   streamNo = 0;

   SET_BINARY_MODE(zStream);
   if (ferror(zStream)) goto errhandler_io;

   while (True) {

      bzf = BZ2_bzReadOpen ( 
               &bzerr, zStream, verbosity, 
               (int)smallMode, unused, nUnused
            );
      if (bzf == NULL || bzerr != BZ_OK) goto errhandler;
      streamNo++;

      while (bzerr == BZ_OK) {
         BZ2_bzRead ( &bzerr, bzf, obuf, 5000 );
         if (bzerr == BZ_DATA_ERROR_MAGIC) goto errhandler;
      }
      if (bzerr != BZ_STREAM_END) goto errhandler;

      BZ2_bzReadGetUnused ( &bzerr, bzf, &unusedTmpV, &nUnused );
      if (bzerr != BZ_OK) panic ( "test:bzReadGetUnused" );

      unusedTmp = (UChar*)unusedTmpV;
      for (i = 0; i < nUnused; i++) unused[i] = unusedTmp[i];

      BZ2_bzReadClose ( &bzerr, bzf );
      if (bzerr != BZ_OK) panic ( "test:bzReadGetUnused" );
      if (nUnused == 0 && myfeof(zStream)) break;

   }

   if (ferror(zStream)) goto errhandler_io;
   ret = fclose ( zStream );
   if (ret == EOF) goto errhandler_io;

   if (verbosity >= 2) fprintf ( stderr, "\n    " );
   return True;

   errhandler:
   BZ2_bzReadClose ( &bzerr_dummy, bzf );
   if (verbosity == 0) 
      fprintf ( stderr, "%s: %s: ", progName, inName );
   switch (bzerr) {
      case BZ_CONFIG_ERROR:
         configError(); break;
      case BZ_IO_ERROR:
         errhandler_io:
         ioError(); break;
      case BZ_DATA_ERROR:
         fprintf ( stderr,
                   "data integrity (CRC) error in data\n" );
         return False;
      case BZ_MEM_ERROR:
         outOfMemory();
      case BZ_UNEXPECTED_EOF:
         fprintf ( stderr,
                   "file ends unexpectedly\n" );
         return False;
      case BZ_DATA_ERROR_MAGIC:
         if (zStream != stdin) fclose(zStream);
         if (streamNo == 1) {
          fprintf ( stderr, 
                    "bad magic number (file not created by bzip2)\n" );
            return False;
         } else {
            if (noisy)
            fprintf ( stderr, 
                      "trailing garbage after EOF ignored\n" );
            return True;       
         }
      default:
         panic ( "test:unexpected error" );
   }

   panic ( "test:end" );
   return True; /*notreached*/
}


/*---------------------------------------------------*/
/*--- Error [non-] handling grunge                ---*/
/*---------------------------------------------------*/

/*---------------------------------------------*/
static
void setExit ( Int32 v )
{
   if (v > exitValue) exitValue = v;
}


/*---------------------------------------------*/
static 
void cadvise ( void )
{
   if (noisy)
   fprintf (
      stderr,
      "\nIt is possible that the compressed file(s) have become corrupted.\n"
        "You can use the -tvv option to test integrity of such files.\n\n"
        "You can use the `bzip2recover' program to attempt to recover\n"
        "data from undamaged sections of corrupted files.\n\n"
    );
}


/*---------------------------------------------*/
static 
void showFileNames ( void )
{
   if (noisy)
   fprintf (
      stderr,
      "\tInput file = %s, output file = %s\n",
      inName, outName 
   );
}


/*---------------------------------------------*/
static 
void cleanUpAndFail ( Int32 ec )
{
   IntNative      retVal;
   struct MY_STAT statBuf;

   if ( srcMode == SM_F2F 
        && opMode != OM_TEST
        && deleteOutputOnInterrupt ) {

      /* Check whether input file still exists.  Delete output file
         only if input exists to avoid loss of data.  Joerg Prante, 5
         January 2002.  (JRS 06-Jan-2002: other changes in 1.0.2 mean
         this is less likely to happen.  But to be ultra-paranoid, we
         do the check anyway.)  */
      retVal = MY_STAT ( inName, &statBuf );
      if (retVal == 0) {
         if (noisy)
            fprintf ( stderr, 
                      "%s: Deleting output file %s, if it exists.\n",
                      progName, outName );
         if (outputHandleJustInCase != NULL)
            fclose ( outputHandleJustInCase );
         retVal = remove ( outName );
         if (retVal != 0)
            fprintf ( stderr,
                      "%s: WARNING: deletion of output file "
                      "(apparently) failed.\n",
                      progName );
      } else {
         fprintf ( stderr,
                   "%s: WARNING: deletion of output file suppressed\n",
                    progName );
         fprintf ( stderr,
                   "%s:    since input file no longer exists.  Output file\n",
                   progName );
         fprintf ( stderr,
                   "%s:    `%s' may be incomplete.\n",
                   progName, outName );
         fprintf ( stderr, 
                   "%s:    I suggest doing an integrity test (bzip2 -tv)"
                   " of it.\n",
                   progName );
      }
   }

   if (noisy && numFileNames > 0 && numFilesProcessed < numFileNames) {
      fprintf ( stderr, 
                "%s: WARNING: some files have not been processed:\n"
                "%s:    %d specified on command line, %d not processed yet.\n\n",
                progName, progName,
                numFileNames, numFileNames - numFilesProcessed );
   }
   setExit(ec);
   exit(exitValue);
}


/*---------------------------------------------*/
static 
void panic ( const Char* s )
{
   fprintf ( stderr,
             "\n%s: PANIC -- internal consistency error:\n"
             "\t%s\n"
             "\tThis is a BUG.  Please report it to:\n"
             "\tbzip2-devel@sourceware.org\n",
             progName, s );
   showFileNames();
   cleanUpAndFail( 3 );
}


/*---------------------------------------------*/
static 
void crcError ( void )
{
   fprintf ( stderr,
             "\n%s: Data integrity error when decompressing.\n",
             progName );
   showFileNames();
   cadvise();
   cleanUpAndFail( 2 );
}


/*---------------------------------------------*/
static 
void compressedStreamEOF ( void )
{
  if (noisy) {
    fprintf ( stderr,
          "\n%s: Compressed file ends unexpectedly;\n\t"
          "perhaps it is corrupted?  *Possible* reason follows.\n",
          progName );
    perror ( progName );
    showFileNames();
    cadvise();
  }
  cleanUpAndFail( 2 );
}


/*---------------------------------------------*/
static 
void ioError ( void )
{
   fprintf ( stderr,
             "\n%s: I/O or other error, bailing out.  "
             "Possible reason follows.\n",
             progName );
   perror ( progName );
   showFileNames();
   cleanUpAndFail( 1 );
}


/*---------------------------------------------*/
static 
void mySignalCatcher ( IntNative n )
{
   fprintf ( stderr,
             "\n%s: Control-C or similar caught, quitting.\n",
             progName );
   cleanUpAndFail(1);
}


/*---------------------------------------------*/
static 
void mySIGSEGVorSIGBUScatcher ( IntNative n )
{
   if (opMode == OM_Z)
      fprintf ( 
      stderr,
      "\n%s: Caught a SIGSEGV or SIGBUS whilst compressing.\n"
      "\n"
      "   Possible causes are (most likely first):\n"
      "   (1) This computer has unreliable memory or cache hardware\n"
      "       (a surprisingly common problem; try a different machine.)\n"
      "   (2) A bug in the compiler used to create this executable\n"
      "       (unlikely, if you didn't compile bzip2 yourself.)\n"
      "   (3) A real bug in bzip2 -- I hope this should never be the case.\n"
      "   The user's manual, Section 4.3, has more info on (1) and (2).\n"
      "   \n"
      "   If you suspect this is a bug in bzip2, or are unsure about (1)\n"
      "   or (2), feel free to report it to: bzip2-devel@sourceware.org.\n"
      "   Section 4.3 of the user's manual describes the info a useful\n"
      "   bug report should have.  If the manual is available on your\n"
      "   system, please try and read it before mailing me.  If you don't\n"
      "   have the manual or can't be bothered to read it, mail me anyway.\n"
      "\n",
      progName );
      else
      fprintf ( 
      stderr,
      "\n%s: Caught a SIGSEGV or SIGBUS whilst decompressing.\n"
      "\n"
      "   Possible causes are (most likely first):\n"
      "   (1) The compressed data is corrupted, and bzip2's usual checks\n"
      "       failed to detect this.  Try bzip2 -tvv my_file.bz2.\n"
      "   (2) This computer has unreliable memory or cache hardware\n"
      "       (a surprisingly common problem; try a different machine.)\n"
      "   (3) A bug in the compiler used to create this executable\n"
      "       (unlikely, if you didn't compile bzip2 yourself.)\n"
      "   (4) A real bug in bzip2 -- I hope this should never be the case.\n"
      "   The user's manual, Section 4.3, has more info on (2) and (3).\n"
      "   \n"
      "   If you suspect this is a bug in bzip2, or are unsure about (2)\n"
      "   or (3), feel free to report it to: bzip2-devel@sourceware.org.\n"
      "   Section 4.3 of the user's manual describes the info a useful\n"
      "   bug report should have.  If the manual is available on your\n"
      "   system, please try and read it before mailing me.  If you don't\n"
      "   have the manual or can't be bothered to read it, mail me anyway.\n"
      "\n",
      progName );

   showFileNames();
   if (opMode == OM_Z)
      cleanUpAndFail( 3 ); else
      { cadvise(); cleanUpAndFail( 2 ); }
}


/*---------------------------------------------*/
static 
void outOfMemory ( void )
{
   fprintf ( stderr,
             "\n%s: couldn't allocate enough memory\n",
             progName );
   showFileNames();
   cleanUpAndFail(1);
}


/*---------------------------------------------*/
static 
void configError ( void )
{
   fprintf ( stderr,
             "bzip2: I'm not configured correctly for this platform!\n"
             "\tI require Int32, Int16 and Char to have sizes\n"
             "\tof 4, 2 and 1 bytes to run properly, and they don't.\n"
             "\tProbably you can fix this by defining them correctly,\n"
             "\tand recompiling.  Bye!\n" );
   setExit(3);
   exit(exitValue);
}


/*---------------------------------------------------*/
/*--- The main driver machinery                   ---*/
/*---------------------------------------------------*/

/* All rather crufty.  The main problem is that input files
   are stat()d multiple times before use.  This should be
   cleaned up. 
*/

/*---------------------------------------------*/
static 
void pad ( Char *s )
{
   Int32 i;
   if ( (Int32)strlen(s) >= longestFileName ) return;
   for (i = 1; i <= longestFileName - (Int32)strlen(s); i++)
      fprintf ( stderr, " " );
}


/*---------------------------------------------*/
static 
void copyFileName ( Char* to, Char* from ) 
{
   if ( strlen(from) > FILE_NAME_LEN-10 )  {
      fprintf (
         stderr,
         "bzip2: file name\n`%s'\n"
         "is suspiciously (more than %d chars) long.\n"
         "Try using a reasonable file name instead.  Sorry! :-)\n",
         from, FILE_NAME_LEN-10
      );
      setExit(1);
      exit(exitValue);
   }

  strncpy(to,from,FILE_NAME_LEN-10);
  to[FILE_NAME_LEN-10]='\0';
}


/*---------------------------------------------*/
static 
Bool fileExists ( Char* name )
{
   FILE *tmp   = fopen ( name, "rb" );
   Bool exists = (tmp != NULL);
   if (tmp != NULL) fclose ( tmp );
   return exists;
}


/*---------------------------------------------*/
/* Open an output file safely with O_EXCL and good permissions.
   This avoids a race condition in versions < 1.0.2, in which
   the file was first opened and then had its interim permissions
   set safely.  We instead use open() to create the file with
   the interim permissions required. (--- --- rw-).

   For non-Unix platforms, if we are not worrying about
   security issues, simple this simply behaves like fopen.
*/
static
FILE* fopen_output_safely ( Char* name, const char* mode )
{
#  if BZ_UNIX
   FILE*     fp;
   IntNative fh;
   fh = open(name, O_WRONLY|O_CREAT|O_EXCL, S_IWUSR|S_IRUSR);
   if (fh == -1) return NULL;
   fp = fdopen(fh, mode);
   if (fp == NULL) close(fh);
   return fp;
#  else
   return fopen(name, mode);
#  endif
}


/*---------------------------------------------*/
/*--
  if in doubt, return True
--*/
static 
Bool notAStandardFile ( Char* name )
{
   IntNative      i;
   struct MY_STAT statBuf;

   i = MY_LSTAT ( name, &statBuf );
   if (i != 0) return True;
   if (MY_S_ISREG(statBuf.st_mode)) return False;
   return True;
}


/*---------------------------------------------*/
/*--
  rac 11/21/98 see if file has hard links to it
--*/
static 
Int32 countHardLinks ( Char* name )
{  
   IntNative      i;
   struct MY_STAT statBuf;

   i = MY_LSTAT ( name, &statBuf );
   if (i != 0) return 0;
   return (statBuf.st_nlink - 1);
}


/*---------------------------------------------*/
/* Copy modification date, access date, permissions and owner from the
   source to destination file.  We have to copy this meta-info off
   into fileMetaInfo before starting to compress / decompress it,
   because doing it afterwards means we get the wrong access time.

   To complicate matters, in compress() and decompress() below, the
   sequence of tests preceding the call to saveInputFileMetaInfo()
   involves calling fileExists(), which in turn establishes its result
   by attempting to fopen() the file, and if successful, immediately
   fclose()ing it again.  So we have to assume that the fopen() call
   does not cause the access time field to be updated.

   Reading of the man page for stat() (man 2 stat) on RedHat 7.2 seems
   to imply that merely doing open() will not affect the access time.
   Therefore we merely need to hope that the C library only does
   open() as a result of fopen(), and not any kind of read()-ahead
   cleverness.

   It sounds pretty fragile to me.  Whether this carries across
   robustly to arbitrary Unix-like platforms (or even works robustly
   on this one, RedHat 7.2) is unknown to me.  Nevertheless ...  
*/
#if BZ_UNIX
static 
struct MY_STAT fileMetaInfo;
#endif

static 
void saveInputFileMetaInfo ( Char *srcName )
{
#  if BZ_UNIX
   IntNative retVal;
   /* Note use of stat here, not lstat. */
   retVal = MY_STAT( srcName, &fileMetaInfo );
   ERROR_IF_NOT_ZERO ( retVal );
#  endif
}


static 
void applySavedTimeInfoToOutputFile ( Char *dstName )
{
#  if BZ_UNIX
   IntNative      retVal;
   struct utimbuf uTimBuf;

   uTimBuf.actime = fileMetaInfo.st_atime;
   uTimBuf.modtime = fileMetaInfo.st_mtime;

   retVal = utime ( dstName, &uTimBuf );
   ERROR_IF_NOT_ZERO ( retVal );
#  endif
}

static 
void applySavedFileAttrToOutputFile ( IntNative fd )
{
#  if BZ_UNIX
   IntNative retVal;

   retVal = fchmod ( fd, fileMetaInfo.st_mode );
   ERROR_IF_NOT_ZERO ( retVal );

   (void) fchown ( fd, fileMetaInfo.st_uid, fileMetaInfo.st_gid );
   /* chown() will in many cases return with EPERM, which can
      be safely ignored.
   */
#  endif
}


/*---------------------------------------------*/
static 
Bool containsDubiousChars ( Char* name )
{
#  if BZ_UNIX
   /* On unix, files can contain any characters and the file expansion
    * is performed by the shell.
    */
   return False;
#  else /* ! BZ_UNIX */
   /* On non-unix (Win* platforms), wildcard characters are not allowed in 
    * filenames.
    */
   for (; *name != '\0'; name++)
      if (*name == '?' || *name == '*') return True;
   return False;
#  endif /* BZ_UNIX */
}


/*---------------------------------------------*/
#define BZ_N_SUFFIX_PAIRS 4

const Char* zSuffix[BZ_N_SUFFIX_PAIRS] 
   = { ".bz2", ".bz", ".tbz2", ".tbz" };
const Char* unzSuffix[BZ_N_SUFFIX_PAIRS] 
   = { "", "", ".tar", ".tar" };

static 
Bool hasSuffix ( Char* s, const Char* suffix )
{
   Int32 ns = strlen(s);
   Int32 nx = strlen(suffix);
   if (ns < nx) return False;
   if (strcmp(s + ns - nx, suffix) == 0) return True;
   return False;
}

static 
Bool mapSuffix ( Char* name, 
                 const Char* oldSuffix, 
                 const Char* newSuffix )
{
   if (!hasSuffix(name,oldSuffix)) return False;
   name[strlen(name)-strlen(oldSuffix)] = 0;
   strcat ( name, newSuffix );
   return True;
}


/*---------------------------------------------*/
static 
void compress ( Char *name )
{
   FILE  *inStr;
   FILE  *outStr;
   Int32 n, i;
   struct MY_STAT statBuf;

   deleteOutputOnInterrupt = False;

   if (name == NULL && srcMode != SM_I2O)
      panic ( "compress: bad modes\n" );

   switch (srcMode) {
      case SM_I2O: 
         copyFileName ( inName, (Char*)"(stdin)" );
         copyFileName ( outName, (Char*)"(stdout)" ); 
         break;
      case SM_F2F: 
         copyFileName ( inName, name );
         copyFileName ( outName, name );
         strcat ( outName, ".bz2" ); 
         break;
      case SM_F2O: 
         copyFileName ( inName, name );
         copyFileName ( outName, (Char*)"(stdout)" ); 
         break;
   }

   if ( srcMode != SM_I2O && containsDubiousChars ( inName ) ) {
      if (noisy)
      fprintf ( stderr, "%s: There are no files matching `%s'.\n",
                progName, inName );
      setExit(1);
      return;
   }
   if ( srcMode != SM_I2O && !fileExists ( inName ) ) {
      fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
                progName, inName, strerror(errno) );
      setExit(1);
      return;
   }
   for (i = 0; i < BZ_N_SUFFIX_PAIRS; i++) {
      if (hasSuffix(inName, zSuffix[i])) {
         if (noisy)
         fprintf ( stderr, 
                   "%s: Input file %s already has %s suffix.\n",
                   progName, inName, zSuffix[i] );
         setExit(1);
         return;
      }
   }
   if ( srcMode == SM_F2F || srcMode == SM_F2O ) {
      MY_STAT(inName, &statBuf);
      if ( MY_S_ISDIR(statBuf.st_mode) ) {
         fprintf( stderr,
                  "%s: Input file %s is a directory.\n",
                  progName,inName);
         setExit(1);
         return;
      }
   }
   if ( srcMode == SM_F2F && !forceOverwrite && notAStandardFile ( inName )) {
      if (noisy)
      fprintf ( stderr, "%s: Input file %s is not a normal file.\n",
                progName, inName );
      setExit(1);
      return;
   }
   if ( srcMode == SM_F2F && fileExists ( outName ) ) {
      if (forceOverwrite) {
     remove(outName);
      } else {
     fprintf ( stderr, "%s: Output file %s already exists.\n",
           progName, outName );
     setExit(1);
     return;
      }
   }
   if ( srcMode == SM_F2F && !forceOverwrite &&
        (n=countHardLinks ( inName )) > 0) {
      fprintf ( stderr, "%s: Input file %s has %d other link%s.\n",
                progName, inName, n, n > 1 ? "s" : "" );
      setExit(1);
      return;
   }

   if ( srcMode == SM_F2F ) {
      /* Save the file's meta-info before we open it.  Doing it later
         means we mess up the access times. */
      saveInputFileMetaInfo ( inName );
   }

   switch ( srcMode ) {

      case SM_I2O:
         inStr = stdin;
         outStr = stdout;
         if ( isatty ( fileno ( stdout ) ) ) {
            fprintf ( stderr,
                      "%s: I won't write compressed data to a terminal.\n",
                      progName );
            fprintf ( stderr, "%s: For help, type: `%s --help'.\n",
                              progName, progName );
            setExit(1);
            return;
         };
         break;

      case SM_F2O:
         inStr = fopen ( inName, "rb" );
         outStr = stdout;
         if ( isatty ( fileno ( stdout ) ) ) {
            fprintf ( stderr,
                      "%s: I won't write compressed data to a terminal.\n",
                      progName );
            fprintf ( stderr, "%s: For help, type: `%s --help'.\n",
                              progName, progName );
            if ( inStr != NULL ) fclose ( inStr );
            setExit(1);
            return;
         };
         if ( inStr == NULL ) {
            fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
                      progName, inName, strerror(errno) );
            setExit(1);
            return;
         };
         break;

      case SM_F2F:
         inStr = fopen ( inName, "rb" );
         outStr = fopen_output_safely ( outName, "wb" );
         if ( outStr == NULL) {
            fprintf ( stderr, "%s: Can't create output file %s: %s.\n",
                      progName, outName, strerror(errno) );
            if ( inStr != NULL ) fclose ( inStr );
            setExit(1);
            return;
         }
         if ( inStr == NULL ) {
            fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
                      progName, inName, strerror(errno) );
            if ( outStr != NULL ) fclose ( outStr );
            setExit(1);
            return;
         };
         break;

      default:
         panic ( "compress: bad srcMode" );
         break;
   }

   if (verbosity >= 1) {
      fprintf ( stderr,  "  %s: ", inName );
      pad ( inName );
      fflush ( stderr );
   }

   /*--- Now the input and output handles are sane.  Do the Biz. ---*/
   outputHandleJustInCase = outStr;
   deleteOutputOnInterrupt = True;
   compressStream ( inStr, outStr );
   outputHandleJustInCase = NULL;

   /*--- If there was an I/O error, we won't get here. ---*/
   if ( srcMode == SM_F2F ) {
      applySavedTimeInfoToOutputFile ( outName );
      deleteOutputOnInterrupt = False;
      if ( !keepInputFiles ) {
         IntNative retVal = remove ( inName );
         ERROR_IF_NOT_ZERO ( retVal );
      }
   }

   deleteOutputOnInterrupt = False;
}


/*---------------------------------------------*/
static 
void uncompress ( Char *name )
{
   FILE  *inStr;
   FILE  *outStr;
   Int32 n, i;
   Bool  magicNumberOK;
   Bool  cantGuess;
   struct MY_STAT statBuf;

   deleteOutputOnInterrupt = False;

   if (name == NULL && srcMode != SM_I2O)
      panic ( "uncompress: bad modes\n" );

   cantGuess = False;
   switch (srcMode) {
      case SM_I2O: 
         copyFileName ( inName, (Char*)"(stdin)" );
         copyFileName ( outName, (Char*)"(stdout)" ); 
         break;
      case SM_F2F: 
         copyFileName ( inName, name );
         copyFileName ( outName, name );
         for (i = 0; i < BZ_N_SUFFIX_PAIRS; i++)
            if (mapSuffix(outName,zSuffix[i],unzSuffix[i]))
               goto zzz; 
         cantGuess = True;
         strcat ( outName, ".out" );
         break;
      case SM_F2O: 
         copyFileName ( inName, name );
         copyFileName ( outName, (Char*)"(stdout)" ); 
         break;
   }

   zzz:
   if ( srcMode != SM_I2O && containsDubiousChars ( inName ) ) {
      if (noisy)
      fprintf ( stderr, "%s: There are no files matching `%s'.\n",
                progName, inName );
      setExit(1);
      return;
   }
   if ( srcMode != SM_I2O && !fileExists ( inName ) ) {
      fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
                progName, inName, strerror(errno) );
      setExit(1);
      return;
   }
   if ( srcMode == SM_F2F || srcMode == SM_F2O ) {
      MY_STAT(inName, &statBuf);
      if ( MY_S_ISDIR(statBuf.st_mode) ) {
         fprintf( stderr,
                  "%s: Input file %s is a directory.\n",
                  progName,inName);
         setExit(1);
         return;
      }
   }
   if ( srcMode == SM_F2F && !forceOverwrite && notAStandardFile ( inName )) {
      if (noisy)
      fprintf ( stderr, "%s: Input file %s is not a normal file.\n",
                progName, inName );
      setExit(1);
      return;
   }
   if ( /* srcMode == SM_F2F implied && */ cantGuess ) {
      if (noisy)
      fprintf ( stderr, 
                "%s: Can't guess original name for %s -- using %s\n",
                progName, inName, outName );
      /* just a warning, no return */
   }   
   if ( srcMode == SM_F2F && fileExists ( outName ) ) {
      if (forceOverwrite) {
    remove(outName);
      } else {
        fprintf ( stderr, "%s: Output file %s already exists.\n",
                  progName, outName );
        setExit(1);
        return;
      }
   }
   if ( srcMode == SM_F2F && !forceOverwrite &&
        (n=countHardLinks ( inName ) ) > 0) {
      fprintf ( stderr, "%s: Input file %s has %d other link%s.\n",
                progName, inName, n, n > 1 ? "s" : "" );
      setExit(1);
      return;
   }

   if ( srcMode == SM_F2F ) {
      /* Save the file's meta-info before we open it.  Doing it later
         means we mess up the access times. */
      saveInputFileMetaInfo ( inName );
   }

   switch ( srcMode ) {

      case SM_I2O:
         inStr = stdin;
         outStr = stdout;
         if ( isatty ( fileno ( stdin ) ) ) {
            fprintf ( stderr,
                      "%s: I won't read compressed data from a terminal.\n",
                      progName );
            fprintf ( stderr, "%s: For help, type: `%s --help'.\n",
                              progName, progName );
            setExit(1);
            return;
         };
         break;

      case SM_F2O:
         inStr = fopen ( inName, "rb" );
         outStr = stdout;
         if ( inStr == NULL ) {
            fprintf ( stderr, "%s: Can't open input file %s:%s.\n",
                      progName, inName, strerror(errno) );
            if ( inStr != NULL ) fclose ( inStr );
            setExit(1);
            return;
         };
         break;

      case SM_F2F:
         inStr = fopen ( inName, "rb" );
         outStr = fopen_output_safely ( outName, "wb" );
         if ( outStr == NULL) {
            fprintf ( stderr, "%s: Can't create output file %s: %s.\n",
                      progName, outName, strerror(errno) );
            if ( inStr != NULL ) fclose ( inStr );
            setExit(1);
            return;
         }
         if ( inStr == NULL ) {
            fprintf ( stderr, "%s: Can't open input file %s: %s.\n",
                      progName, inName, strerror(errno) );
            if ( outStr != NULL ) fclose ( outStr );
            setExit(1);
            return;
         };
         break;

      default:
         panic ( "uncompress: bad srcMode" );
         break;
   }

   if (verbosity >= 1) {
      fprintf ( stderr, "  %s: ", inName );
      pad ( inName );
      fflush ( stderr );
   }

   /*--- Now the input and output handles are sane.  Do the Biz. ---*/
   outputHandleJustInCase = outStr;
   deleteOutputOnInterrupt = True;
   magicNumberOK = uncompressStream ( inStr, outStr );
   outputHandleJustInCase = NULL;

   /*--- If there was an I/O error, we won't get here. ---*/
   if ( magicNumberOK ) {
      if ( srcMode == SM_F2F ) {
         applySavedTimeInfoToOutputFile ( outName );
         deleteOutputOnInterrupt = False;
         if ( !keepInputFiles ) {
            IntNative retVal = remove ( inName );
            ERROR_IF_NOT_ZERO ( retVal );
         }
      }
   } else {
      unzFailsExist = True;
      deleteOutputOnInterrupt = False;
      if ( srcMode == SM_F2F ) {
         IntNative retVal = remove ( outName );
         ERROR_IF_NOT_ZERO ( retVal );
      }
   }
   deleteOutputOnInterrupt = False;

   if ( magicNumberOK ) {
      if (verbosity >= 1)
         fprintf ( stderr, "done\n" );
   } else {
      setExit(2);
      if (verbosity >= 1)
         fprintf ( stderr, "not a bzip2 file.\n" ); else
         fprintf ( stderr,
                   "%s: %s is not a bzip2 file.\n",
                   progName, inName );
   }

}


/*---------------------------------------------*/
static 
void testf ( Char *name )
{
   FILE *inStr;
   Bool allOK;
   struct MY_STAT statBuf;

   deleteOutputOnInterrupt = False;

   if (name == NULL && srcMode != SM_I2O)
      panic ( "testf: bad modes\n" );

   copyFileName ( outName, (Char*)"(none)" );
   switch (srcMode) {
      case SM_I2O: copyFileName ( inName, (Char*)"(stdin)" ); break;
      case SM_F2F: copyFileName ( inName, name ); break;
      case SM_F2O: copyFileName ( inName, name ); break;
   }

   if ( srcMode != SM_I2O && containsDubiousChars ( inName ) ) {
      if (noisy)
      fprintf ( stderr, "%s: There are no files matching `%s'.\n",
                progName, inName );
      setExit(1);
      return;
   }
   if ( srcMode != SM_I2O && !fileExists ( inName ) ) {
      fprintf ( stderr, "%s: Can't open input %s: %s.\n",
                progName, inName, strerror(errno) );
      setExit(1);
      return;
   }
   if ( srcMode != SM_I2O ) {
      MY_STAT(inName, &statBuf);
      if ( MY_S_ISDIR(statBuf.st_mode) ) {
         fprintf( stderr,
                  "%s: Input file %s is a directory.\n",
                  progName,inName);
         setExit(1);
         return;
      }
   }

   switch ( srcMode ) {

      case SM_I2O:
         if ( isatty ( fileno ( stdin ) ) ) {
            fprintf ( stderr,
                      "%s: I won't read compressed data from a terminal.\n",
                      progName );
            fprintf ( stderr, "%s: For help, type: `%s --help'.\n",
                              progName, progName );
            setExit(1);
            return;
         };
         inStr = stdin;
         break;

      case SM_F2O: case SM_F2F:
         inStr = fopen ( inName, "rb" );
         if ( inStr == NULL ) {
            fprintf ( stderr, "%s: Can't open input file %s:%s.\n",
                      progName, inName, strerror(errno) );
            setExit(1);
            return;
         };
         break;

      default:
         panic ( "testf: bad srcMode" );
         break;
   }

   if (verbosity >= 1) {
      fprintf ( stderr, "  %s: ", inName );
      pad ( inName );
      fflush ( stderr );
   }

   /*--- Now the input handle is sane.  Do the Biz. ---*/
   outputHandleJustInCase = NULL;
   allOK = testStream ( inStr );

   if (allOK && verbosity >= 1) fprintf ( stderr, "ok\n" );
   if (!allOK) testFailsExist = True;
}


/*---------------------------------------------*/
static 
void license ( void )
{
   fprintf ( stderr,

    "bzip2, a block-sorting file compressor.  "
    "Version %s.\n"
    "   \n"
    "   Copyright (C) 1996-2019 by Julian Seward.\n"
    "   \n"
    "   This program is free software; you can redistribute it and/or modify\n"
    "   it under the terms set out in the LICENSE file, which is included\n"
    "   in the bzip2 source distribution.\n"
    "   \n"
    "   This program is distributed in the hope that it will be useful,\n"
    "   but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
    "   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
    "   LICENSE file for more details.\n"
    "   \n",
    BZ2_bzlibVersion()
   );
}


/*---------------------------------------------*/
static 
void usage ( Char *fullProgName )
{
   fprintf (
      stderr,
      "bzip2, a block-sorting file compressor.  "
      "Version %s.\n"
      "\n   usage: %s [flags and input files in any order]\n"
      "\n"
      "   -h --help           print this message\n"
      "   -d --decompress     force decompression\n"
      "   -z --compress       force compression\n"
      "   -k --keep           keep (don't delete) input files\n"
      "   -f --force          overwrite existing output files\n"
      "   -t --test           test compressed file integrity\n"
      "   -c --stdout         output to standard out\n"
      "   -q --quiet          suppress noncritical error messages\n"
      "   -v --verbose        be verbose (a 2nd -v gives more)\n"
      "   -L --license        display software version & license\n"
      "   -V --version        display software version & license\n"
      "   -s --small          use less memory (at most 2500k)\n"
      "   -1 .. -9            set block size to 100k .. 900k\n"
      "   --fast              alias for -1\n"
      "   --best              alias for -9\n"
      "\n"
      "   If invoked as `bzip2', default action is to compress.\n"
      "              as `bunzip2',  default action is to decompress.\n"
      "              as `bzcat', default action is to decompress to stdout.\n"
      "\n"
      "   If no file names are given, bzip2 compresses or decompresses\n"
      "   from standard input to standard output.  You can combine\n"
      "   short flags, so `-v -4' means the same as -v4 or -4v, &c.\n"
#     if BZ_UNIX
      "\n"
#     endif
      ,

      BZ2_bzlibVersion(),
      fullProgName
   );
}


/*---------------------------------------------*/
static 
void redundant ( Char* flag )
{
   fprintf ( 
      stderr, 
      "%s: %s is redundant in versions 0.9.5 and above\n",
      progName, flag );
}


/*---------------------------------------------*/
/*--
  All the garbage from here to main() is purely to
  implement a linked list of command-line arguments,
  into which main() copies argv[1 .. argc-1].

  The purpose of this exercise is to facilitate 
  the expansion of wildcard characters * and ? in 
  filenames for OSs which don't know how to do it
  themselves, like MSDOS, Windows 95 and NT.

  The actual Dirty Work is done by the platform-
  specific macro APPEND_FILESPEC.
--*/

typedef
   struct zzzz {
      Char        *name;
      struct zzzz *link;
   }
   Cell;


/*---------------------------------------------*/
static 
void *myMalloc ( Int32 n )
{
   void* p;

   p = malloc ( (size_t)n );
   if (p == NULL) outOfMemory ();
   return p;
}


/*---------------------------------------------*/
static 
Cell *mkCell ( void )
{
   Cell *c;

   c = (Cell*) myMalloc ( sizeof ( Cell ) );
   c->name = NULL;
   c->link = NULL;
   return c;
}


/*---------------------------------------------*/
static 
Cell *snocString ( Cell *root, Char *name )
{
   if (root == NULL) {
      Cell *tmp = mkCell();
      tmp->name = (Char*) myMalloc ( 5 + strlen(name) );
      strcpy ( tmp->name, name );
      return tmp;
   } else {
      Cell *tmp = root;
      while (tmp->link != NULL) tmp = tmp->link;
      tmp->link = snocString ( tmp->link, name );
      return root;
   }
}


/*---------------------------------------------*/
static 
void addFlagsFromEnvVar ( Cell** argList, Char* varName ) 
{
   Int32 i, j, k;
   Char *envbase, *p;

   envbase = getenv(varName);
   if (envbase != NULL) {
      p = envbase;
      i = 0;
      while (True) {
         if (p[i] == 0) break;
         p += i;
         i = 0;
         while (isspace((Int32)(p[0]))) p++;
         while (p[i] != 0 && !isspace((Int32)(p[i]))) i++;
         if (i > 0) {
            k = i; if (k > FILE_NAME_LEN-10) k = FILE_NAME_LEN-10;
            for (j = 0; j < k; j++) tmpName[j] = p[j];
            tmpName[k] = 0;
            APPEND_FLAG(*argList, tmpName);
         }
      }
   }
}


/*---------------------------------------------*/
#define ISFLAG(s) (strcmp(aa->name, (s))==0)

IntNative main ( IntNative argc, Char *argv[] )
{
   Int32  i, j;
   Char   *tmp;
   Cell   *argList;
   Cell   *aa;
   Bool   decode;

   /*-- Be really really really paranoid :-) --*/
   if (sizeof(Int32) != 4 || sizeof(UInt32) != 4  ||
       sizeof(Int16) != 2 || sizeof(UInt16) != 2  ||
       sizeof(Char)  != 1 || sizeof(UChar)  != 1)
      configError();

   /*-- Initialise --*/
   outputHandleJustInCase  = NULL;
   smallMode               = False;
   keepInputFiles          = False;
   forceOverwrite          = False;
   noisy                   = True;
   verbosity               = 0;
   blockSize100k           = 9;
   testFailsExist          = False;
   unzFailsExist           = False;
   numFileNames            = 0;
   numFilesProcessed       = 0;
   workFactor              = 30;
   deleteOutputOnInterrupt = False;
   exitValue               = 0;
   i = j = 0; /* avoid bogus warning from egcs-1.1.X */

   /*-- Set up signal handlers for mem access errors --*/
   signal (SIGSEGV, mySIGSEGVorSIGBUScatcher);
#  if BZ_UNIX
#  ifndef __DJGPP__
   signal (SIGBUS,  mySIGSEGVorSIGBUScatcher);
#  endif
#  endif

   copyFileName ( inName,  (Char*)"(none)" );
   copyFileName ( outName, (Char*)"(none)" );

   copyFileName ( progNameReally, argv[0] );
   progName = &progNameReally[0];
   for (tmp = &progNameReally[0]; *tmp != '\0'; tmp++)
      if (*tmp == PATH_SEP) progName = tmp + 1;


   /*-- Copy flags from env var BZIP2, and 
        expand filename wildcards in arg list.
   --*/
   argList = NULL;
   addFlagsFromEnvVar ( &argList,  (Char*)"BZIP2" );
   addFlagsFromEnvVar ( &argList,  (Char*)"BZIP" );
   for (i = 1; i <= argc-1; i++)
      APPEND_FILESPEC(argList, argv[i]);


   /*-- Find the length of the longest filename --*/
   longestFileName = 7;
   numFileNames    = 0;
   decode          = True;
   for (aa = argList; aa != NULL; aa = aa->link) {
      if (ISFLAG("--")) { decode = False; continue; }
      if (aa->name[0] == '-' && decode) continue;
      numFileNames++;
      if (longestFileName < (Int32)strlen(aa->name) )
         longestFileName = (Int32)strlen(aa->name);
   }


   /*-- Determine source modes; flag handling may change this too. --*/
   if (numFileNames == 0)
      srcMode = SM_I2O; else srcMode = SM_F2F;


   /*-- Determine what to do (compress/uncompress/test/cat). --*/
   /*-- Note that subsequent flag handling may change this. --*/
   opMode = OM_Z;

   if ( (strstr ( progName, "unzip" ) != 0) ||
        (strstr ( progName, "UNZIP" ) != 0) )
      opMode = OM_UNZ;

   if ( (strstr ( progName, "z2cat" ) != 0) ||
        (strstr ( progName, "Z2CAT" ) != 0) ||
        (strstr ( progName, "zcat" ) != 0)  ||
        (strstr ( progName, "ZCAT" ) != 0) )  {
      opMode = OM_UNZ;
      srcMode = (numFileNames == 0) ? SM_I2O : SM_F2O;
   }


   /*-- Look at the flags. --*/
   for (aa = argList; aa != NULL; aa = aa->link) {
      if (ISFLAG("--")) break;
      if (aa->name[0] == '-' && aa->name[1] != '-') {
         for (j = 1; aa->name[j] != '\0'; j++) {
            switch (aa->name[j]) {
               case 'c': srcMode          = SM_F2O; break;
               case 'd': opMode           = OM_UNZ; break;
               case 'z': opMode           = OM_Z; break;
               case 'f': forceOverwrite   = True; break;
               case 't': opMode           = OM_TEST; break;
               case 'k': keepInputFiles   = True; break;
               case 's': smallMode        = True; break;
               case 'q': noisy            = False; break;
               case '1': blockSize100k    = 1; break;
               case '2': blockSize100k    = 2; break;
               case '3': blockSize100k    = 3; break;
               case '4': blockSize100k    = 4; break;
               case '5': blockSize100k    = 5; break;
               case '6': blockSize100k    = 6; break;
               case '7': blockSize100k    = 7; break;
               case '8': blockSize100k    = 8; break;
               case '9': blockSize100k    = 9; break;
               case 'V':
               case 'L': license();            break;
               case 'v': verbosity++; break;
               case 'h': usage ( progName );
                         exit ( 0 );
                         break;
               default:  fprintf ( stderr, "%s: Bad flag `%s'\n",
                                   progName, aa->name );
                         usage ( progName );
                         exit ( 1 );
                         break;
            }
         }
      }
   }
   
   /*-- And again ... --*/
   for (aa = argList; aa != NULL; aa = aa->link) {
      if (ISFLAG("--")) break;
      if (ISFLAG("--stdout"))            srcMode          = SM_F2O;  else
      if (ISFLAG("--decompress"))        opMode           = OM_UNZ;  else
      if (ISFLAG("--compress"))          opMode           = OM_Z;    else
      if (ISFLAG("--force"))             forceOverwrite   = True;    else
      if (ISFLAG("--test"))              opMode           = OM_TEST; else
      if (ISFLAG("--keep"))              keepInputFiles   = True;    else
      if (ISFLAG("--small"))             smallMode        = True;    else
      if (ISFLAG("--quiet"))             noisy            = False;   else
      if (ISFLAG("--version"))           license();                  else
      if (ISFLAG("--license"))           license();                  else
      if (ISFLAG("--exponential"))       workFactor = 1;             else 
      if (ISFLAG("--repetitive-best"))   redundant(aa->name);        else
      if (ISFLAG("--repetitive-fast"))   redundant(aa->name);        else
      if (ISFLAG("--fast"))              blockSize100k = 1;          else
      if (ISFLAG("--best"))              blockSize100k = 9;          else
      if (ISFLAG("--verbose"))           verbosity++;                else
      if (ISFLAG("--help"))              { usage ( progName ); exit ( 0 ); }
         else
         if (strncmp ( aa->name, "--", 2) == 0) {
            fprintf ( stderr, "%s: Bad flag `%s'\n", progName, aa->name );
            usage ( progName );
            exit ( 1 );
         }
   }

   if (verbosity > 4) verbosity = 4;
   if (opMode == OM_Z && smallMode && blockSize100k > 2) 
      blockSize100k = 2;

   if (opMode == OM_TEST && srcMode == SM_F2O) {
      fprintf ( stderr, "%s: -c and -t cannot be used together.\n",
                progName );
      exit ( 1 );
   }

   if (srcMode == SM_F2O && numFileNames == 0)
      srcMode = SM_I2O;

   if (opMode != OM_Z) blockSize100k = 0;

   if (srcMode == SM_F2F) {
      signal (SIGINT,  mySignalCatcher);
      signal (SIGTERM, mySignalCatcher);
#     if BZ_UNIX
      signal (SIGHUP,  mySignalCatcher);
#     endif
   }

   if (opMode == OM_Z) {
     if (srcMode == SM_I2O) {
        compress ( NULL );
     } else {
        decode = True;
        for (aa = argList; aa != NULL; aa = aa->link) {
           if (ISFLAG("--")) { decode = False; continue; }
           if (aa->name[0] == '-' && decode) continue;
           numFilesProcessed++;
           compress ( aa->name );
        }
     }
   } 
   else

   if (opMode == OM_UNZ) {
      unzFailsExist = False;
      if (srcMode == SM_I2O) {
         uncompress ( NULL );
      } else {
         decode = True;
         for (aa = argList; aa != NULL; aa = aa->link) {
            if (ISFLAG("--")) { decode = False; continue; }
            if (aa->name[0] == '-' && decode) continue;
            numFilesProcessed++;
            uncompress ( aa->name );
         }      
      }
      if (unzFailsExist) { 
         setExit(2); 
         exit(exitValue);
      }
   } 

   else {
      testFailsExist = False;
      if (srcMode == SM_I2O) {
         testf ( NULL );
      } else {
         decode = True;
         for (aa = argList; aa != NULL; aa = aa->link) {
        if (ISFLAG("--")) { decode = False; continue; }
            if (aa->name[0] == '-' && decode) continue;
            numFilesProcessed++;
            testf ( aa->name );
     }
      }
      if (testFailsExist) {
     if (noisy) {
            fprintf ( stderr,
               "\n"
               "You can use the `bzip2recover' program to attempt to recover\n"
               "data from undamaged sections of corrupted files.\n\n"
            );
     }
         setExit(2);
         exit(exitValue);
      }
   }

   /* Free the argument list memory to mollify leak detectors 
      (eg) Purify, Checker.  Serves no other useful purpose.
   */
   aa = argList;
   while (aa != NULL) {
      Cell* aa2 = aa->link;
      if (aa->name != NULL) free(aa->name);
      free(aa);
      aa = aa2;
   }

   return exitValue;
}


/*-----------------------------------------------------------*/
/*--- end                                         bzip2.c ---*/
/*-----------------------------------------------------------*/

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bzip2-1.0.8/patches/coreutils.patch

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/patches/coreutils.patch used in: Step 706
SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>

SPDX-License-Identifier: bzip2-1.0.6

Disable all coreutils calls in the Makefile which we do not have at this
point of the bootstrap. ranlib can be unconditionally disabled as we do not
have it either.

diff -r -N -U3 Makefile Makefile
--- Makefile    2019-07-13 18:50:05.000000000 +0100
+++ Makefile    2021-01-14 15:45:17.558516299 +0000
@@ -21,7 +21,7 @@
 LDFLAGS=
 
 BIGFILES=-D_FILE_OFFSET_BITS=64
-CFLAGS=-Wall -Winline -O2 -g $(BIGFILES)
+CFLAGS=-Wall -Winline -O2 -g $(BIGFILES) -I .
 
 # Where you want it installed when you do 'make install'
 PREFIX=/usr/local
@@ -44,11 +44,6 @@
 
 libbz2.a: $(OBJS)
    $(AR) cq libbz2.a $(OBJS)
-   @if ( test -f $(RANLIB) -o -f /usr/bin/ranlib -o \
-       -f /bin/ranlib -o -f /usr/ccs/bin/ranlib ) ; then \
-       echo $(RANLIB) libbz2.a ; \
-       $(RANLIB) libbz2.a ; \
-   fi
 
 check: test
 test: bzip2
@@ -114,7 +108,6 @@
    sample1.tst sample2.tst sample3.tst
 
 blocksort.o: blocksort.c
-   @cat words0
    $(CC) $(CFLAGS) -c blocksort.c
 huffman.o: huffman.c
    $(CC) $(CFLAGS) -c huffman.c

https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/Makefile

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/Makefile used in: Step 706, Step 707
# ------------------------------------------------------------------
# This file is part of bzip2/libbzip2, a program and library for
# lossless, block-sorting data compression.
#
# bzip2/libbzip2 version 1.0.8 of 13 July 2019
# Copyright (C) 1996-2019 Julian Seward <jseward@acm.org>
#
# Please read the WARNING, DISCLAIMER and PATENTS sections in the 
# README file.
#
# This program is released under the terms of the license contained
# in the file LICENSE.
# ------------------------------------------------------------------

SHELL=/bin/sh

# To assist in cross-compiling
CC=gcc
AR=ar
RANLIB=ranlib
LDFLAGS=

BIGFILES=-D_FILE_OFFSET_BITS=64
CFLAGS=-Wall -Winline -O2 -g $(BIGFILES)

# Where you want it installed when you do 'make install'
PREFIX=/usr/local


OBJS= blocksort.o  \
      huffman.o    \
      crctable.o   \
      randtable.o  \
      compress.o   \
      decompress.o \
      bzlib.o

all: libbz2.a bzip2 bzip2recover test

bzip2: libbz2.a bzip2.o
    $(CC) $(CFLAGS) $(LDFLAGS) -o bzip2 bzip2.o -L. -lbz2

bzip2recover: bzip2recover.o
    $(CC) $(CFLAGS) $(LDFLAGS) -o bzip2recover bzip2recover.o

libbz2.a: $(OBJS)
    rm -f libbz2.a
    $(AR) cq libbz2.a $(OBJS)
    @if ( test -f $(RANLIB) -o -f /usr/bin/ranlib -o \
        -f /bin/ranlib -o -f /usr/ccs/bin/ranlib ) ; then \
        echo $(RANLIB) libbz2.a ; \
        $(RANLIB) libbz2.a ; \
    fi

check: test
test: bzip2
    @cat words1
    ./bzip2 -1  < sample1.ref > sample1.rb2
    ./bzip2 -2  < sample2.ref > sample2.rb2
    ./bzip2 -3  < sample3.ref > sample3.rb2
    ./bzip2 -d  < sample1.bz2 > sample1.tst
    ./bzip2 -d  < sample2.bz2 > sample2.tst
    ./bzip2 -ds < sample3.bz2 > sample3.tst
    cmp sample1.bz2 sample1.rb2 
    cmp sample2.bz2 sample2.rb2
    cmp sample3.bz2 sample3.rb2
    cmp sample1.tst sample1.ref
    cmp sample2.tst sample2.ref
    cmp sample3.tst sample3.ref
    @cat words3

install: bzip2 bzip2recover
    if ( test ! -d $(PREFIX)/bin ) ; then mkdir -p $(PREFIX)/bin ; fi
    if ( test ! -d $(PREFIX)/lib ) ; then mkdir -p $(PREFIX)/lib ; fi
    if ( test ! -d $(PREFIX)/man ) ; then mkdir -p $(PREFIX)/man ; fi
    if ( test ! -d $(PREFIX)/man/man1 ) ; then mkdir -p $(PREFIX)/man/man1 ; fi
    if ( test ! -d $(PREFIX)/include ) ; then mkdir -p $(PREFIX)/include ; fi
    cp -f bzip2 $(PREFIX)/bin/bzip2
    cp -f bzip2 $(PREFIX)/bin/bunzip2
    cp -f bzip2 $(PREFIX)/bin/bzcat
    cp -f bzip2recover $(PREFIX)/bin/bzip2recover
    chmod a+x $(PREFIX)/bin/bzip2
    chmod a+x $(PREFIX)/bin/bunzip2
    chmod a+x $(PREFIX)/bin/bzcat
    chmod a+x $(PREFIX)/bin/bzip2recover
    cp -f bzip2.1 $(PREFIX)/man/man1
    chmod a+r $(PREFIX)/man/man1/bzip2.1
    cp -f bzlib.h $(PREFIX)/include
    chmod a+r $(PREFIX)/include/bzlib.h
    cp -f libbz2.a $(PREFIX)/lib
    chmod a+r $(PREFIX)/lib/libbz2.a
    cp -f bzgrep $(PREFIX)/bin/bzgrep
    ln -s -f $(PREFIX)/bin/bzgrep $(PREFIX)/bin/bzegrep
    ln -s -f $(PREFIX)/bin/bzgrep $(PREFIX)/bin/bzfgrep
    chmod a+x $(PREFIX)/bin/bzgrep
    cp -f bzmore $(PREFIX)/bin/bzmore
    ln -s -f $(PREFIX)/bin/bzmore $(PREFIX)/bin/bzless
    chmod a+x $(PREFIX)/bin/bzmore
    cp -f bzdiff $(PREFIX)/bin/bzdiff
    ln -s -f $(PREFIX)/bin/bzdiff $(PREFIX)/bin/bzcmp
    chmod a+x $(PREFIX)/bin/bzdiff
    cp -f bzgrep.1 bzmore.1 bzdiff.1 $(PREFIX)/man/man1
    chmod a+r $(PREFIX)/man/man1/bzgrep.1
    chmod a+r $(PREFIX)/man/man1/bzmore.1
    chmod a+r $(PREFIX)/man/man1/bzdiff.1
    echo ".so man1/bzgrep.1" > $(PREFIX)/man/man1/bzegrep.1
    echo ".so man1/bzgrep.1" > $(PREFIX)/man/man1/bzfgrep.1
    echo ".so man1/bzmore.1" > $(PREFIX)/man/man1/bzless.1
    echo ".so man1/bzdiff.1" > $(PREFIX)/man/man1/bzcmp.1

clean: 
    rm -f *.o libbz2.a bzip2 bzip2recover \
    sample1.rb2 sample2.rb2 sample3.rb2 \
    sample1.tst sample2.tst sample3.tst

blocksort.o: blocksort.c
    @cat words0
    $(CC) $(CFLAGS) -c blocksort.c
huffman.o: huffman.c
    $(CC) $(CFLAGS) -c huffman.c
crctable.o: crctable.c
    $(CC) $(CFLAGS) -c crctable.c
randtable.o: randtable.c
    $(CC) $(CFLAGS) -c randtable.c
compress.o: compress.c
    $(CC) $(CFLAGS) -c compress.c
decompress.o: decompress.c
    $(CC) $(CFLAGS) -c decompress.c
bzlib.o: bzlib.c
    $(CC) $(CFLAGS) -c bzlib.c
bzip2.o: bzip2.c
    $(CC) $(CFLAGS) -c bzip2.c
bzip2recover.o: bzip2recover.c
    $(CC) $(CFLAGS) -c bzip2recover.c


distclean: clean
    rm -f manual.ps manual.html manual.pdf

DISTNAME=bzip2-1.0.8
dist: check manual
    rm -f $(DISTNAME)
    ln -s -f . $(DISTNAME)
    tar cvf $(DISTNAME).tar \
       $(DISTNAME)/blocksort.c \
       $(DISTNAME)/huffman.c \
       $(DISTNAME)/crctable.c \
       $(DISTNAME)/randtable.c \
       $(DISTNAME)/compress.c \
       $(DISTNAME)/decompress.c \
       $(DISTNAME)/bzlib.c \
       $(DISTNAME)/bzip2.c \
       $(DISTNAME)/bzip2recover.c \
       $(DISTNAME)/bzlib.h \
       $(DISTNAME)/bzlib_private.h \
       $(DISTNAME)/Makefile \
       $(DISTNAME)/LICENSE \
       $(DISTNAME)/bzip2.1 \
       $(DISTNAME)/bzip2.1.preformatted \
       $(DISTNAME)/bzip2.txt \
       $(DISTNAME)/words0 \
       $(DISTNAME)/words1 \
       $(DISTNAME)/words2 \
       $(DISTNAME)/words3 \
       $(DISTNAME)/sample1.ref \
       $(DISTNAME)/sample2.ref \
       $(DISTNAME)/sample3.ref \
       $(DISTNAME)/sample1.bz2 \
       $(DISTNAME)/sample2.bz2 \
       $(DISTNAME)/sample3.bz2 \
       $(DISTNAME)/dlltest.c \
       $(DISTNAME)/manual.html \
       $(DISTNAME)/manual.pdf \
       $(DISTNAME)/manual.ps \
       $(DISTNAME)/README \
       $(DISTNAME)/README.COMPILATION.PROBLEMS \
       $(DISTNAME)/README.XML.STUFF \
       $(DISTNAME)/CHANGES \
       $(DISTNAME)/libbz2.def \
       $(DISTNAME)/libbz2.dsp \
       $(DISTNAME)/dlltest.dsp \
       $(DISTNAME)/makefile.msc \
       $(DISTNAME)/unzcrash.c \
       $(DISTNAME)/spewG.c \
       $(DISTNAME)/mk251.c \
       $(DISTNAME)/bzdiff \
       $(DISTNAME)/bzdiff.1 \
       $(DISTNAME)/bzmore \
       $(DISTNAME)/bzmore.1 \
       $(DISTNAME)/bzgrep \
       $(DISTNAME)/bzgrep.1 \
       $(DISTNAME)/Makefile-libbz2_so \
       $(DISTNAME)/bz-common.xsl \
       $(DISTNAME)/bz-fo.xsl \
       $(DISTNAME)/bz-html.xsl \
       $(DISTNAME)/bzip.css \
       $(DISTNAME)/entities.xml \
       $(DISTNAME)/manual.xml \
       $(DISTNAME)/format.pl \
       $(DISTNAME)/xmlproc.sh
    gzip -v $(DISTNAME).tar

# For rebuilding the manual from sources on my SuSE 9.1 box

MANUAL_SRCS=    bz-common.xsl bz-fo.xsl bz-html.xsl bzip.css \
        entities.xml manual.xml 

manual: manual.html manual.ps manual.pdf

manual.ps: $(MANUAL_SRCS)
    ./xmlproc.sh -ps manual.xml

manual.pdf: $(MANUAL_SRCS)
    ./xmlproc.sh -pdf manual.xml

manual.html: $(MANUAL_SRCS)
    ./xmlproc.sh -html manual.xml

https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/blocksort.c

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/blocksort.c used in: Step 708
/*-------------------------------------------------------------*/
/*--- Block sorting machinery                               ---*/
/*---                                           blocksort.c ---*/
/*-------------------------------------------------------------*/

/* ------------------------------------------------------------------
   This file is part of bzip2/libbzip2, a program and library for
   lossless, block-sorting data compression.

   bzip2/libbzip2 version 1.0.8 of 13 July 2019
   Copyright (C) 1996-2019 Julian Seward <jseward@acm.org>

   Please read the WARNING, DISCLAIMER and PATENTS sections in the 
   README file.

   This program is released under the terms of the license contained
   in the file LICENSE.
   ------------------------------------------------------------------ */


#include "bzlib_private.h"

/*---------------------------------------------*/
/*--- Fallback O(N log(N)^2) sorting        ---*/
/*--- algorithm, for repetitive blocks      ---*/
/*---------------------------------------------*/

/*---------------------------------------------*/
static 
__inline__
void fallbackSimpleSort ( UInt32* fmap, 
                          UInt32* eclass, 
                          Int32   lo, 
                          Int32   hi )
{
   Int32 i, j, tmp;
   UInt32 ec_tmp;

   if (lo == hi) return;

   if (hi - lo > 3) {
      for ( i = hi-4; i >= lo; i-- ) {
         tmp = fmap[i];
         ec_tmp = eclass[tmp];
         for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 )
            fmap[j-4] = fmap[j];
         fmap[j-4] = tmp;
      }
   }

   for ( i = hi-1; i >= lo; i-- ) {
      tmp = fmap[i];
      ec_tmp = eclass[tmp];
      for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ )
         fmap[j-1] = fmap[j];
      fmap[j-1] = tmp;
   }
}


/*---------------------------------------------*/
#define fswap(zz1, zz2) \
   { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; }

#define fvswap(zzp1, zzp2, zzn)       \
{                                     \
   Int32 yyp1 = (zzp1);               \
   Int32 yyp2 = (zzp2);               \
   Int32 yyn  = (zzn);                \
   while (yyn > 0) {                  \
      fswap(fmap[yyp1], fmap[yyp2]);  \
      yyp1++; yyp2++; yyn--;          \
   }                                  \
}


#define fmin(a,b) ((a) < (b)) ? (a) : (b)

#define fpush(lz,hz) { stackLo[sp] = lz; \
                       stackHi[sp] = hz; \
                       sp++; }

#define fpop(lz,hz) { sp--;              \
                      lz = stackLo[sp];  \
                      hz = stackHi[sp]; }

#define FALLBACK_QSORT_SMALL_THRESH 10
#define FALLBACK_QSORT_STACK_SIZE   100


static
void fallbackQSort3 ( UInt32* fmap, 
                      UInt32* eclass,
                      Int32   loSt, 
                      Int32   hiSt )
{
   Int32 unLo, unHi, ltLo, gtHi, n, m;
   Int32 sp, lo, hi;
   UInt32 med, r, r3;
   Int32 stackLo[FALLBACK_QSORT_STACK_SIZE];
   Int32 stackHi[FALLBACK_QSORT_STACK_SIZE];

   r = 0;

   sp = 0;
   fpush ( loSt, hiSt );

   while (sp > 0) {

      AssertH ( sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004 );

      fpop ( lo, hi );
      if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) {
         fallbackSimpleSort ( fmap, eclass, lo, hi );
         continue;
      }

      /* Random partitioning.  Median of 3 sometimes fails to
         avoid bad cases.  Median of 9 seems to help but 
         looks rather expensive.  This too seems to work but
         is cheaper.  Guidance for the magic constants 
         7621 and 32768 is taken from Sedgewick's algorithms
         book, chapter 35.
      */
      r = ((r * 7621) + 1) % 32768;
      r3 = r % 3;
      if (r3 == 0) med = eclass[fmap[lo]]; else
      if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else
                   med = eclass[fmap[hi]];

      unLo = ltLo = lo;
      unHi = gtHi = hi;

      while (1) {
         while (1) {
            if (unLo > unHi) break;
            n = (Int32)eclass[fmap[unLo]] - (Int32)med;
            if (n == 0) { 
               fswap(fmap[unLo], fmap[ltLo]); 
               ltLo++; unLo++; 
               continue; 
            };
            if (n > 0) break;
            unLo++;
         }
         while (1) {
            if (unLo > unHi) break;
            n = (Int32)eclass[fmap[unHi]] - (Int32)med;
            if (n == 0) { 
               fswap(fmap[unHi], fmap[gtHi]); 
               gtHi--; unHi--; 
               continue; 
            };
            if (n < 0) break;
            unHi--;
         }
         if (unLo > unHi) break;
         fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--;
      }

      AssertD ( unHi == unLo-1, "fallbackQSort3(2)" );

      if (gtHi < ltLo) continue;

      n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n);
      m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m);

      n = lo + unLo - ltLo - 1;
      m = hi - (gtHi - unHi) + 1;

      if (n - lo > hi - m) {
         fpush ( lo, n );
         fpush ( m, hi );
      } else {
         fpush ( m, hi );
         fpush ( lo, n );
      }
   }
}

#undef fmin
#undef fpush
#undef fpop
#undef fswap
#undef fvswap
#undef FALLBACK_QSORT_SMALL_THRESH
#undef FALLBACK_QSORT_STACK_SIZE


/*---------------------------------------------*/
/* Pre:
      nblock > 0
      eclass exists for [0 .. nblock-1]
      ((UChar*)eclass) [0 .. nblock-1] holds block
      ptr exists for [0 .. nblock-1]

   Post:
      ((UChar*)eclass) [0 .. nblock-1] holds block
      All other areas of eclass destroyed
      fmap [0 .. nblock-1] holds sorted order
      bhtab [ 0 .. 2+(nblock/32) ] destroyed
*/

#define       SET_BH(zz)  bhtab[(zz) >> 5] |= ((UInt32)1 << ((zz) & 31))
#define     CLEAR_BH(zz)  bhtab[(zz) >> 5] &= ~((UInt32)1 << ((zz) & 31))
#define     ISSET_BH(zz)  (bhtab[(zz) >> 5] & ((UInt32)1 << ((zz) & 31)))
#define      WORD_BH(zz)  bhtab[(zz) >> 5]
#define UNALIGNED_BH(zz)  ((zz) & 0x01f)

static
void fallbackSort ( UInt32* fmap, 
                    UInt32* eclass, 
                    UInt32* bhtab,
                    Int32   nblock,
                    Int32   verb )
{
   Int32 ftab[257];
   Int32 ftabCopy[256];
   Int32 H, i, j, k, l, r, cc, cc1;
   Int32 nNotDone;
   Int32 nBhtab;
   UChar* eclass8 = (UChar*)eclass;

   /*--
      Initial 1-char radix sort to generate
      initial fmap and initial BH bits.
   --*/
   if (verb >= 4)
      VPrintf0 ( "        bucket sorting ...\n" );
   for (i = 0; i < 257;    i++) ftab[i] = 0;
   for (i = 0; i < nblock; i++) ftab[eclass8[i]]++;
   for (i = 0; i < 256;    i++) ftabCopy[i] = ftab[i];
   for (i = 1; i < 257;    i++) ftab[i] += ftab[i-1];

   for (i = 0; i < nblock; i++) {
      j = eclass8[i];
      k = ftab[j] - 1;
      ftab[j] = k;
      fmap[k] = i;
   }

   nBhtab = 2 + (nblock / 32);
   for (i = 0; i < nBhtab; i++) bhtab[i] = 0;
   for (i = 0; i < 256; i++) SET_BH(ftab[i]);

   /*--
      Inductively refine the buckets.  Kind-of an
      "exponential radix sort" (!), inspired by the
      Manber-Myers suffix array construction algorithm.
   --*/

   /*-- set sentinel bits for block-end detection --*/
   for (i = 0; i < 32; i++) { 
      SET_BH(nblock + 2*i);
      CLEAR_BH(nblock + 2*i + 1);
   }

   /*-- the log(N) loop --*/
   H = 1;
   while (1) {

      if (verb >= 4) 
         VPrintf1 ( "        depth %6d has ", H );

      j = 0;
      for (i = 0; i < nblock; i++) {
         if (ISSET_BH(i)) j = i;
         k = fmap[i] - H; if (k < 0) k += nblock;
         eclass[k] = j;
      }

      nNotDone = 0;
      r = -1;
      while (1) {

     /*-- find the next non-singleton bucket --*/
         k = r + 1;
         while (ISSET_BH(k) && UNALIGNED_BH(k)) k++;
         if (ISSET_BH(k)) {
            while (WORD_BH(k) == 0xffffffff) k += 32;
            while (ISSET_BH(k)) k++;
         }
         l = k - 1;
         if (l >= nblock) break;
         while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++;
         if (!ISSET_BH(k)) {
            while (WORD_BH(k) == 0x00000000) k += 32;
            while (!ISSET_BH(k)) k++;
         }
         r = k - 1;
         if (r >= nblock) break;

         /*-- now [l, r] bracket current bucket --*/
         if (r > l) {
            nNotDone += (r - l + 1);
            fallbackQSort3 ( fmap, eclass, l, r );

            /*-- scan bucket and generate header bits-- */
            cc = -1;
            for (i = l; i <= r; i++) {
               cc1 = eclass[fmap[i]];
               if (cc != cc1) { SET_BH(i); cc = cc1; };
            }
         }
      }

      if (verb >= 4) 
         VPrintf1 ( "%6d unresolved strings\n", nNotDone );

      H *= 2;
      if (H > nblock || nNotDone == 0) break;
   }

   /*-- 
      Reconstruct the original block in
      eclass8 [0 .. nblock-1], since the
      previous phase destroyed it.
   --*/
   if (verb >= 4)
      VPrintf0 ( "        reconstructing block ...\n" );
   j = 0;
   for (i = 0; i < nblock; i++) {
      while (ftabCopy[j] == 0) j++;
      ftabCopy[j]--;
      eclass8[fmap[i]] = (UChar)j;
   }
   AssertH ( j < 256, 1005 );
}

#undef       SET_BH
#undef     CLEAR_BH
#undef     ISSET_BH
#undef      WORD_BH
#undef UNALIGNED_BH


/*---------------------------------------------*/
/*--- The main, O(N^2 log(N)) sorting       ---*/
/*--- algorithm.  Faster for "normal"       ---*/
/*--- non-repetitive blocks.                ---*/
/*---------------------------------------------*/

/*---------------------------------------------*/
static
__inline__
Bool mainGtU ( UInt32  i1, 
               UInt32  i2,
               UChar*  block, 
               UInt16* quadrant,
               UInt32  nblock,
               Int32*  budget )
{
   Int32  k;
   UChar  c1, c2;
   UInt16 s1, s2;

   AssertD ( i1 != i2, "mainGtU" );
   /* 1 */
   c1 = block[i1]; c2 = block[i2];
   if (c1 != c2) return (c1 > c2);
   i1++; i2++;
   /* 2 */
   c1 = block[i1]; c2 = block[i2];
   if (c1 != c2) return (c1 > c2);
   i1++; i2++;
   /* 3 */
   c1 = block[i1]; c2 = block[i2];
   if (c1 != c2) return (c1 > c2);
   i1++; i2++;
   /* 4 */
   c1 = block[i1]; c2 = block[i2];
   if (c1 != c2) return (c1 > c2);
   i1++; i2++;
   /* 5 */
   c1 = block[i1]; c2 = block[i2];
   if (c1 != c2) return (c1 > c2);
   i1++; i2++;
   /* 6 */
   c1 = block[i1]; c2 = block[i2];
   if (c1 != c2) return (c1 > c2);
   i1++; i2++;
   /* 7 */
   c1 = block[i1]; c2 = block[i2];
   if (c1 != c2) return (c1 > c2);
   i1++; i2++;
   /* 8 */
   c1 = block[i1]; c2 = block[i2];
   if (c1 != c2) return (c1 > c2);
   i1++; i2++;
   /* 9 */
   c1 = block[i1]; c2 = block[i2];
   if (c1 != c2) return (c1 > c2);
   i1++; i2++;
   /* 10 */
   c1 = block[i1]; c2 = block[i2];
   if (c1 != c2) return (c1 > c2);
   i1++; i2++;
   /* 11 */
   c1 = block[i1]; c2 = block[i2];
   if (c1 != c2) return (c1 > c2);
   i1++; i2++;
   /* 12 */
   c1 = block[i1]; c2 = block[i2];
   if (c1 != c2) return (c1 > c2);
   i1++; i2++;

   k = nblock + 8;

   do {
      /* 1 */
      c1 = block[i1]; c2 = block[i2];
      if (c1 != c2) return (c1 > c2);
      s1 = quadrant[i1]; s2 = quadrant[i2];
      if (s1 != s2) return (s1 > s2);
      i1++; i2++;
      /* 2 */
      c1 = block[i1]; c2 = block[i2];
      if (c1 != c2) return (c1 > c2);
      s1 = quadrant[i1]; s2 = quadrant[i2];
      if (s1 != s2) return (s1 > s2);
      i1++; i2++;
      /* 3 */
      c1 = block[i1]; c2 = block[i2];
      if (c1 != c2) return (c1 > c2);
      s1 = quadrant[i1]; s2 = quadrant[i2];
      if (s1 != s2) return (s1 > s2);
      i1++; i2++;
      /* 4 */
      c1 = block[i1]; c2 = block[i2];
      if (c1 != c2) return (c1 > c2);
      s1 = quadrant[i1]; s2 = quadrant[i2];
      if (s1 != s2) return (s1 > s2);
      i1++; i2++;
      /* 5 */
      c1 = block[i1]; c2 = block[i2];
      if (c1 != c2) return (c1 > c2);
      s1 = quadrant[i1]; s2 = quadrant[i2];
      if (s1 != s2) return (s1 > s2);
      i1++; i2++;
      /* 6 */
      c1 = block[i1]; c2 = block[i2];
      if (c1 != c2) return (c1 > c2);
      s1 = quadrant[i1]; s2 = quadrant[i2];
      if (s1 != s2) return (s1 > s2);
      i1++; i2++;
      /* 7 */
      c1 = block[i1]; c2 = block[i2];
      if (c1 != c2) return (c1 > c2);
      s1 = quadrant[i1]; s2 = quadrant[i2];
      if (s1 != s2) return (s1 > s2);
      i1++; i2++;
      /* 8 */
      c1 = block[i1]; c2 = block[i2];
      if (c1 != c2) return (c1 > c2);
      s1 = quadrant[i1]; s2 = quadrant[i2];
      if (s1 != s2) return (s1 > s2);
      i1++; i2++;

      if (i1 >= nblock) i1 -= nblock;
      if (i2 >= nblock) i2 -= nblock;

      k -= 8;
      (*budget)--;
   }
      while (k >= 0);

   return False;
}


/*---------------------------------------------*/
/*--
   Knuth's increments seem to work better
   than Incerpi-Sedgewick here.  Possibly
   because the number of elems to sort is
   usually small, typically <= 20.
--*/
static
Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280,
                   9841, 29524, 88573, 265720,
                   797161, 2391484 };

static
void mainSimpleSort ( UInt32* ptr,
                      UChar*  block,
                      UInt16* quadrant,
                      Int32   nblock,
                      Int32   lo, 
                      Int32   hi, 
                      Int32   d,
                      Int32*  budget )
{
   Int32 i, j, h, bigN, hp;
   UInt32 v;

   bigN = hi - lo + 1;
   if (bigN < 2) return;

   hp = 0;
   while (incs[hp] < bigN) hp++;
   hp--;

   for (; hp >= 0; hp--) {
      h = incs[hp];

      i = lo + h;
      while (True) {

         /*-- copy 1 --*/
         if (i > hi) break;
         v = ptr[i];
         j = i;
         while ( mainGtU ( 
                    ptr[j-h]+d, v+d, block, quadrant, nblock, budget 
                 ) ) {
            ptr[j] = ptr[j-h];
            j = j - h;
            if (j <= (lo + h - 1)) break;
         }
         ptr[j] = v;
         i++;

         /*-- copy 2 --*/
         if (i > hi) break;
         v = ptr[i];
         j = i;
         while ( mainGtU ( 
                    ptr[j-h]+d, v+d, block, quadrant, nblock, budget 
                 ) ) {
            ptr[j] = ptr[j-h];
            j = j - h;
            if (j <= (lo + h - 1)) break;
         }
         ptr[j] = v;
         i++;

         /*-- copy 3 --*/
         if (i > hi) break;
         v = ptr[i];
         j = i;
         while ( mainGtU ( 
                    ptr[j-h]+d, v+d, block, quadrant, nblock, budget 
                 ) ) {
            ptr[j] = ptr[j-h];
            j = j - h;
            if (j <= (lo + h - 1)) break;
         }
         ptr[j] = v;
         i++;

         if (*budget < 0) return;
      }
   }
}


/*---------------------------------------------*/
/*--
   The following is an implementation of
   an elegant 3-way quicksort for strings,
   described in a paper "Fast Algorithms for
   Sorting and Searching Strings", by Robert
   Sedgewick and Jon L. Bentley.
--*/

#define mswap(zz1, zz2) \
   { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; }

#define mvswap(zzp1, zzp2, zzn)       \
{                                     \
   Int32 yyp1 = (zzp1);               \
   Int32 yyp2 = (zzp2);               \
   Int32 yyn  = (zzn);                \
   while (yyn > 0) {                  \
      mswap(ptr[yyp1], ptr[yyp2]);    \
      yyp1++; yyp2++; yyn--;          \
   }                                  \
}

static 
__inline__
UChar mmed3 ( UChar a, UChar b, UChar c )
{
   UChar t;
   if (a > b) { t = a; a = b; b = t; };
   if (b > c) { 
      b = c;
      if (a > b) b = a;
   }
   return b;
}

#define mmin(a,b) ((a) < (b)) ? (a) : (b)

#define mpush(lz,hz,dz) { stackLo[sp] = lz; \
                          stackHi[sp] = hz; \
                          stackD [sp] = dz; \
                          sp++; }

#define mpop(lz,hz,dz) { sp--;             \
                         lz = stackLo[sp]; \
                         hz = stackHi[sp]; \
                         dz = stackD [sp]; }


#define mnextsize(az) (nextHi[az]-nextLo[az])

#define mnextswap(az,bz)                                        \
   { Int32 tz;                                                  \
     tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \
     tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \
     tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; }


#define MAIN_QSORT_SMALL_THRESH 20
#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT)
#define MAIN_QSORT_STACK_SIZE 100

static
void mainQSort3 ( UInt32* ptr,
                  UChar*  block,
                  UInt16* quadrant,
                  Int32   nblock,
                  Int32   loSt, 
                  Int32   hiSt, 
                  Int32   dSt,
                  Int32*  budget )
{
   Int32 unLo, unHi, ltLo, gtHi, n, m, med;
   Int32 sp, lo, hi, d;

   Int32 stackLo[MAIN_QSORT_STACK_SIZE];
   Int32 stackHi[MAIN_QSORT_STACK_SIZE];
   Int32 stackD [MAIN_QSORT_STACK_SIZE];

   Int32 nextLo[3];
   Int32 nextHi[3];
   Int32 nextD [3];

   sp = 0;
   mpush ( loSt, hiSt, dSt );

   while (sp > 0) {

      AssertH ( sp < MAIN_QSORT_STACK_SIZE - 2, 1001 );

      mpop ( lo, hi, d );
      if (hi - lo < MAIN_QSORT_SMALL_THRESH || 
          d > MAIN_QSORT_DEPTH_THRESH) {
         mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget );
         if (*budget < 0) return;
         continue;
      }

      med = (Int32) 
            mmed3 ( block[ptr[ lo         ]+d],
                    block[ptr[ hi         ]+d],
                    block[ptr[ (lo+hi)>>1 ]+d] );

      unLo = ltLo = lo;
      unHi = gtHi = hi;

      while (True) {
         while (True) {
            if (unLo > unHi) break;
            n = ((Int32)block[ptr[unLo]+d]) - med;
            if (n == 0) { 
               mswap(ptr[unLo], ptr[ltLo]); 
               ltLo++; unLo++; continue; 
            };
            if (n >  0) break;
            unLo++;
         }
         while (True) {
            if (unLo > unHi) break;
            n = ((Int32)block[ptr[unHi]+d]) - med;
            if (n == 0) { 
               mswap(ptr[unHi], ptr[gtHi]); 
               gtHi--; unHi--; continue; 
            };
            if (n <  0) break;
            unHi--;
         }
         if (unLo > unHi) break;
         mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--;
      }

      AssertD ( unHi == unLo-1, "mainQSort3(2)" );

      if (gtHi < ltLo) {
         mpush(lo, hi, d+1 );
         continue;
      }

      n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n);
      m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m);

      n = lo + unLo - ltLo - 1;
      m = hi - (gtHi - unHi) + 1;

      nextLo[0] = lo;  nextHi[0] = n;   nextD[0] = d;
      nextLo[1] = m;   nextHi[1] = hi;  nextD[1] = d;
      nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1;

      if (mnextsize(0) < mnextsize(1)) mnextswap(0,1);
      if (mnextsize(1) < mnextsize(2)) mnextswap(1,2);
      if (mnextsize(0) < mnextsize(1)) mnextswap(0,1);

      AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" );
      AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" );

      mpush (nextLo[0], nextHi[0], nextD[0]);
      mpush (nextLo[1], nextHi[1], nextD[1]);
      mpush (nextLo[2], nextHi[2], nextD[2]);
   }
}

#undef mswap
#undef mvswap
#undef mpush
#undef mpop
#undef mmin
#undef mnextsize
#undef mnextswap
#undef MAIN_QSORT_SMALL_THRESH
#undef MAIN_QSORT_DEPTH_THRESH
#undef MAIN_QSORT_STACK_SIZE


/*---------------------------------------------*/
/* Pre:
      nblock > N_OVERSHOOT
      block32 exists for [0 .. nblock-1 +N_OVERSHOOT]
      ((UChar*)block32) [0 .. nblock-1] holds block
      ptr exists for [0 .. nblock-1]

   Post:
      ((UChar*)block32) [0 .. nblock-1] holds block
      All other areas of block32 destroyed
      ftab [0 .. 65536 ] destroyed
      ptr [0 .. nblock-1] holds sorted order
      if (*budget < 0), sorting was abandoned
*/

#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8])
#define SETMASK (1 << 21)
#define CLEARMASK (~(SETMASK))

static
void mainSort ( UInt32* ptr, 
                UChar*  block,
                UInt16* quadrant, 
                UInt32* ftab,
                Int32   nblock,
                Int32   verb,
                Int32*  budget )
{
   Int32  i, j, k, ss, sb;
   Int32  runningOrder[256];
   Bool   bigDone[256];
   Int32  copyStart[256];
   Int32  copyEnd  [256];
   UChar  c1;
   Int32  numQSorted;
   UInt16 s;
   if (verb >= 4) VPrintf0 ( "        main sort initialise ...\n" );

   /*-- set up the 2-byte frequency table --*/
   for (i = 65536; i >= 0; i--) ftab[i] = 0;

   j = block[0] << 8;
   i = nblock-1;
   for (; i >= 3; i -= 4) {
      quadrant[i] = 0;
      j = (j >> 8) | ( ((UInt16)block[i]) << 8);
      ftab[j]++;
      quadrant[i-1] = 0;
      j = (j >> 8) | ( ((UInt16)block[i-1]) << 8);
      ftab[j]++;
      quadrant[i-2] = 0;
      j = (j >> 8) | ( ((UInt16)block[i-2]) << 8);
      ftab[j]++;
      quadrant[i-3] = 0;
      j = (j >> 8) | ( ((UInt16)block[i-3]) << 8);
      ftab[j]++;
   }
   for (; i >= 0; i--) {
      quadrant[i] = 0;
      j = (j >> 8) | ( ((UInt16)block[i]) << 8);
      ftab[j]++;
   }

   /*-- (emphasises close relationship of block & quadrant) --*/
   for (i = 0; i < BZ_N_OVERSHOOT; i++) {
      block   [nblock+i] = block[i];
      quadrant[nblock+i] = 0;
   }

   if (verb >= 4) VPrintf0 ( "        bucket sorting ...\n" );

   /*-- Complete the initial radix sort --*/
   for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1];

   s = block[0] << 8;
   i = nblock-1;
   for (; i >= 3; i -= 4) {
      s = (s >> 8) | (block[i] << 8);
      j = ftab[s] -1;
      ftab[s] = j;
      ptr[j] = i;
      s = (s >> 8) | (block[i-1] << 8);
      j = ftab[s] -1;
      ftab[s] = j;
      ptr[j] = i-1;
      s = (s >> 8) | (block[i-2] << 8);
      j = ftab[s] -1;
      ftab[s] = j;
      ptr[j] = i-2;
      s = (s >> 8) | (block[i-3] << 8);
      j = ftab[s] -1;
      ftab[s] = j;
      ptr[j] = i-3;
   }
   for (; i >= 0; i--) {
      s = (s >> 8) | (block[i] << 8);
      j = ftab[s] -1;
      ftab[s] = j;
      ptr[j] = i;
   }

   /*--
      Now ftab contains the first loc of every small bucket.
      Calculate the running order, from smallest to largest
      big bucket.
   --*/
   for (i = 0; i <= 255; i++) {
      bigDone     [i] = False;
      runningOrder[i] = i;
   }

   {
      Int32 vv;
      Int32 h = 1;
      do h = 3 * h + 1; while (h <= 256);
      do {
         h = h / 3;
         for (i = h; i <= 255; i++) {
            vv = runningOrder[i];
            j = i;
            while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) {
               runningOrder[j] = runningOrder[j-h];
               j = j - h;
               if (j <= (h - 1)) goto zero;
            }
            zero:
            runningOrder[j] = vv;
         }
      } while (h != 1);
   }

   /*--
      The main sorting loop.
   --*/

   numQSorted = 0;

   for (i = 0; i <= 255; i++) {

      /*--
         Process big buckets, starting with the least full.
         Basically this is a 3-step process in which we call
         mainQSort3 to sort the small buckets [ss, j], but
         also make a big effort to avoid the calls if we can.
      --*/
      ss = runningOrder[i];

      /*--
         Step 1:
         Complete the big bucket [ss] by quicksorting
         any unsorted small buckets [ss, j], for j != ss.  
         Hopefully previous pointer-scanning phases have already
         completed many of the small buckets [ss, j], so
         we don't have to sort them at all.
      --*/
      for (j = 0; j <= 255; j++) {
         if (j != ss) {
            sb = (ss << 8) + j;
            if ( ! (ftab[sb] & SETMASK) ) {
               Int32 lo = ftab[sb]   & CLEARMASK;
               Int32 hi = (ftab[sb+1] & CLEARMASK) - 1;
               if (hi > lo) {
                  if (verb >= 4)
                     VPrintf4 ( "        qsort [0x%x, 0x%x]   "
                                "done %d   this %d\n",
                                ss, j, numQSorted, hi - lo + 1 );
                  mainQSort3 ( 
                     ptr, block, quadrant, nblock, 
                     lo, hi, BZ_N_RADIX, budget 
                  );   
                  numQSorted += (hi - lo + 1);
                  if (*budget < 0) return;
               }
            }
            ftab[sb] |= SETMASK;
         }
      }

      AssertH ( !bigDone[ss], 1006 );

      /*--
         Step 2:
         Now scan this big bucket [ss] so as to synthesise the
         sorted order for small buckets [t, ss] for all t,
         including, magically, the bucket [ss,ss] too.
         This will avoid doing Real Work in subsequent Step 1's.
      --*/
      {
         for (j = 0; j <= 255; j++) {
            copyStart[j] =  ftab[(j << 8) + ss]     & CLEARMASK;
            copyEnd  [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1;
         }
         for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) {
            k = ptr[j]-1; if (k < 0) k += nblock;
            c1 = block[k];
            if (!bigDone[c1])
               ptr[ copyStart[c1]++ ] = k;
         }
         for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) {
            k = ptr[j]-1; if (k < 0) k += nblock;
            c1 = block[k];
            if (!bigDone[c1]) 
               ptr[ copyEnd[c1]-- ] = k;
         }
      }

      AssertH ( (copyStart[ss]-1 == copyEnd[ss])
                || 
                /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1.
                   Necessity for this case is demonstrated by compressing 
                   a sequence of approximately 48.5 million of character 
                   251; 1.0.0/1.0.1 will then die here. */
                (copyStart[ss] == 0 && copyEnd[ss] == nblock-1),
                1007 )

      for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK;

      /*--
         Step 3:
         The [ss] big bucket is now done.  Record this fact,
         and update the quadrant descriptors.  Remember to
         update quadrants in the overshoot area too, if
         necessary.  The "if (i < 255)" test merely skips
         this updating for the last bucket processed, since
         updating for the last bucket is pointless.

         The quadrant array provides a way to incrementally
         cache sort orderings, as they appear, so as to 
         make subsequent comparisons in fullGtU() complete
         faster.  For repetitive blocks this makes a big
         difference (but not big enough to be able to avoid
         the fallback sorting mechanism, exponential radix sort).

         The precise meaning is: at all times:

            for 0 <= i < nblock and 0 <= j <= nblock

            if block[i] != block[j], 

               then the relative values of quadrant[i] and 
                    quadrant[j] are meaningless.

               else {
                  if quadrant[i] < quadrant[j]
                     then the string starting at i lexicographically
                     precedes the string starting at j

                  else if quadrant[i] > quadrant[j]
                     then the string starting at j lexicographically
                     precedes the string starting at i

                  else
                     the relative ordering of the strings starting
                     at i and j has not yet been determined.
               }
      --*/
      bigDone[ss] = True;

      if (i < 255) {
         Int32 bbStart  = ftab[ss << 8] & CLEARMASK;
         Int32 bbSize   = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart;
         Int32 shifts   = 0;

         while ((bbSize >> shifts) > 65534) shifts++;

         for (j = bbSize-1; j >= 0; j--) {
            Int32 a2update     = ptr[bbStart + j];
            UInt16 qVal        = (UInt16)(j >> shifts);
            quadrant[a2update] = qVal;
            if (a2update < BZ_N_OVERSHOOT)
               quadrant[a2update + nblock] = qVal;
         }
         AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 );
      }

   }

   if (verb >= 4)
      VPrintf3 ( "        %d pointers, %d sorted, %d scanned\n",
                 nblock, numQSorted, nblock - numQSorted );
}

#undef BIGFREQ
#undef SETMASK
#undef CLEARMASK


/*---------------------------------------------*/
/* Pre:
      nblock > 0
      arr2 exists for [0 .. nblock-1 +N_OVERSHOOT]
      ((UChar*)arr2)  [0 .. nblock-1] holds block
      arr1 exists for [0 .. nblock-1]

   Post:
      ((UChar*)arr2) [0 .. nblock-1] holds block
      All other areas of block destroyed
      ftab [ 0 .. 65536 ] destroyed
      arr1 [0 .. nblock-1] holds sorted order
*/
void BZ2_blockSort ( EState* s )
{
   UInt32* ptr    = s->ptr; 
   UChar*  block  = s->block;
   UInt32* ftab   = s->ftab;
   Int32   nblock = s->nblock;
   Int32   verb   = s->verbosity;
   Int32   wfact  = s->workFactor;
   UInt16* quadrant;
   Int32   budget;
   Int32   budgetInit;
   Int32   i;

   if (nblock < 10000) {
      fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb );
   } else {
      /* Calculate the location for quadrant, remembering to get
         the alignment right.  Assumes that &(block[0]) is at least
         2-byte aligned -- this should be ok since block is really
         the first section of arr2.
      */
      i = nblock+BZ_N_OVERSHOOT;
      if (i & 1) i++;
      quadrant = (UInt16*)(&(block[i]));

      /* (wfact-1) / 3 puts the default-factor-30
         transition point at very roughly the same place as 
         with v0.1 and v0.9.0.  
         Not that it particularly matters any more, since the
         resulting compressed stream is now the same regardless
         of whether or not we use the main sort or fallback sort.
      */
      if (wfact < 1  ) wfact = 1;
      if (wfact > 100) wfact = 100;
      budgetInit = nblock * ((wfact-1) / 3);
      budget = budgetInit;

      mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget );
      if (verb >= 3) 
         VPrintf3 ( "      %d work, %d block, ratio %5.2f\n",
                    budgetInit - budget,
                    nblock, 
                    (float)(budgetInit - budget) /
                    (float)(nblock==0 ? 1 : nblock) ); 
      if (budget < 0) {
         if (verb >= 2) 
            VPrintf0 ( "    too repetitive; using fallback"
                       " sorting algorithm\n" );
         fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb );
      }
   }

   s->origPtr = -1;
   for (i = 0; i < s->nblock; i++)
      if (ptr[i] == 0)
         { s->origPtr = i; break; };

   AssertH( s->origPtr != -1, 1003 );
}


/*-------------------------------------------------------------*/
/*--- end                                       blocksort.c ---*/
/*-------------------------------------------------------------*/

https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/bzlib_private.h

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/bzlib_private.h used in: Step 708, Step 709, Step 710, Step 711, Step 712, Step 713, Step 714
/*-------------------------------------------------------------*/
/*--- Private header file for the library.                  ---*/
/*---                                       bzlib_private.h ---*/
/*-------------------------------------------------------------*/

/* ------------------------------------------------------------------
   This file is part of bzip2/libbzip2, a program and library for
   lossless, block-sorting data compression.

   bzip2/libbzip2 version 1.0.8 of 13 July 2019
   Copyright (C) 1996-2019 Julian Seward <jseward@acm.org>

   Please read the WARNING, DISCLAIMER and PATENTS sections in the 
   README file.

   This program is released under the terms of the license contained
   in the file LICENSE.
   ------------------------------------------------------------------ */


#ifndef _BZLIB_PRIVATE_H
#define _BZLIB_PRIVATE_H

#include <stdlib.h>

#ifndef BZ_NO_STDIO
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#endif

#include "bzlib.h"



/*-- General stuff. --*/

#define BZ_VERSION  "1.0.8, 13-Jul-2019"

typedef char            Char;
typedef unsigned char   Bool;
typedef unsigned char   UChar;
typedef int             Int32;
typedef unsigned int    UInt32;
typedef short           Int16;
typedef unsigned short  UInt16;

#define True  ((Bool)1)
#define False ((Bool)0)

#ifndef __GNUC__
#define __inline__  /* */
#endif 

#ifndef BZ_NO_STDIO

extern void BZ2_bz__AssertH__fail ( int errcode );
#define AssertH(cond,errcode) \
   { if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); }

#if BZ_DEBUG
#define AssertD(cond,msg) \
   { if (!(cond)) {       \
      fprintf ( stderr,   \
        "\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\
      exit(1); \
   }}
#else
#define AssertD(cond,msg) /* */
#endif

#define VPrintf0(zf) \
   fprintf(stderr,zf)
#define VPrintf1(zf,za1) \
   fprintf(stderr,zf,za1)
#define VPrintf2(zf,za1,za2) \
   fprintf(stderr,zf,za1,za2)
#define VPrintf3(zf,za1,za2,za3) \
   fprintf(stderr,zf,za1,za2,za3)
#define VPrintf4(zf,za1,za2,za3,za4) \
   fprintf(stderr,zf,za1,za2,za3,za4)
#define VPrintf5(zf,za1,za2,za3,za4,za5) \
   fprintf(stderr,zf,za1,za2,za3,za4,za5)

#else

extern void bz_internal_error ( int errcode );
#define AssertH(cond,errcode) \
   { if (!(cond)) bz_internal_error ( errcode ); }
#define AssertD(cond,msg)                do { } while (0)
#define VPrintf0(zf)                     do { } while (0)
#define VPrintf1(zf,za1)                 do { } while (0)
#define VPrintf2(zf,za1,za2)             do { } while (0)
#define VPrintf3(zf,za1,za2,za3)         do { } while (0)
#define VPrintf4(zf,za1,za2,za3,za4)     do { } while (0)
#define VPrintf5(zf,za1,za2,za3,za4,za5) do { } while (0)

#endif


#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1)
#define BZFREE(ppp)  (strm->bzfree)(strm->opaque,(ppp))


/*-- Header bytes. --*/

#define BZ_HDR_B 0x42   /* 'B' */
#define BZ_HDR_Z 0x5a   /* 'Z' */
#define BZ_HDR_h 0x68   /* 'h' */
#define BZ_HDR_0 0x30   /* '0' */
  
/*-- Constants for the back end. --*/

#define BZ_MAX_ALPHA_SIZE 258
#define BZ_MAX_CODE_LEN    23

#define BZ_RUNA 0
#define BZ_RUNB 1

#define BZ_N_GROUPS 6
#define BZ_G_SIZE   50
#define BZ_N_ITERS  4

#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE))



/*-- Stuff for randomising repetitive blocks. --*/

extern Int32 BZ2_rNums[512];

#define BZ_RAND_DECLS                          \
   Int32 rNToGo;                               \
   Int32 rTPos                                 \

#define BZ_RAND_INIT_MASK                      \
   s->rNToGo = 0;                              \
   s->rTPos  = 0                               \

#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0)

#define BZ_RAND_UPD_MASK                       \
   if (s->rNToGo == 0) {                       \
      s->rNToGo = BZ2_rNums[s->rTPos];         \
      s->rTPos++;                              \
      if (s->rTPos == 512) s->rTPos = 0;       \
   }                                           \
   s->rNToGo--;



/*-- Stuff for doing CRCs. --*/

extern UInt32 BZ2_crc32Table[256];

#define BZ_INITIALISE_CRC(crcVar)              \
{                                              \
   crcVar = 0xffffffffL;                       \
}

#define BZ_FINALISE_CRC(crcVar)                \
{                                              \
   crcVar = ~(crcVar);                         \
}

#define BZ_UPDATE_CRC(crcVar,cha)              \
{                                              \
   crcVar = (crcVar << 8) ^                    \
            BZ2_crc32Table[(crcVar >> 24) ^    \
                           ((UChar)cha)];      \
}



/*-- States and modes for compression. --*/

#define BZ_M_IDLE      1
#define BZ_M_RUNNING   2
#define BZ_M_FLUSHING  3
#define BZ_M_FINISHING 4

#define BZ_S_OUTPUT    1
#define BZ_S_INPUT     2

#define BZ_N_RADIX 2
#define BZ_N_QSORT 12
#define BZ_N_SHELL 18
#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2)




/*-- Structure holding all the compression-side stuff. --*/

typedef
   struct {
      /* pointer back to the struct bz_stream */
      bz_stream* strm;

      /* mode this stream is in, and whether inputting */
      /* or outputting data */
      Int32    mode;
      Int32    state;

      /* remembers avail_in when flush/finish requested */
      UInt32   avail_in_expect;

      /* for doing the block sorting */
      UInt32*  arr1;
      UInt32*  arr2;
      UInt32*  ftab;
      Int32    origPtr;

      /* aliases for arr1 and arr2 */
      UInt32*  ptr;
      UChar*   block;
      UInt16*  mtfv;
      UChar*   zbits;

      /* for deciding when to use the fallback sorting algorithm */
      Int32    workFactor;

      /* run-length-encoding of the input */
      UInt32   state_in_ch;
      Int32    state_in_len;
      BZ_RAND_DECLS;

      /* input and output limits and current posns */
      Int32    nblock;
      Int32    nblockMAX;
      Int32    numZ;
      Int32    state_out_pos;

      /* map of bytes used in block */
      Int32    nInUse;
      Bool     inUse[256];
      UChar    unseqToSeq[256];

      /* the buffer for bit stream creation */
      UInt32   bsBuff;
      Int32    bsLive;

      /* block and combined CRCs */
      UInt32   blockCRC;
      UInt32   combinedCRC;

      /* misc administratium */
      Int32    verbosity;
      Int32    blockNo;
      Int32    blockSize100k;

      /* stuff for coding the MTF values */
      Int32    nMTF;
      Int32    mtfFreq    [BZ_MAX_ALPHA_SIZE];
      UChar    selector   [BZ_MAX_SELECTORS];
      UChar    selectorMtf[BZ_MAX_SELECTORS];

      UChar    len     [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
      Int32    code    [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
      Int32    rfreq   [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
      /* second dimension: only 3 needed; 4 makes index calculations faster */
      UInt32   len_pack[BZ_MAX_ALPHA_SIZE][4];

   }
   EState;



/*-- externs for compression. --*/

extern void 
BZ2_blockSort ( EState* );

extern void 
BZ2_compressBlock ( EState*, Bool );

extern void 
BZ2_bsInitWrite ( EState* );

extern void 
BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 );

extern void 
BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 );



/*-- states for decompression. --*/

#define BZ_X_IDLE        1
#define BZ_X_OUTPUT      2

#define BZ_X_MAGIC_1     10
#define BZ_X_MAGIC_2     11
#define BZ_X_MAGIC_3     12
#define BZ_X_MAGIC_4     13
#define BZ_X_BLKHDR_1    14
#define BZ_X_BLKHDR_2    15
#define BZ_X_BLKHDR_3    16
#define BZ_X_BLKHDR_4    17
#define BZ_X_BLKHDR_5    18
#define BZ_X_BLKHDR_6    19
#define BZ_X_BCRC_1      20
#define BZ_X_BCRC_2      21
#define BZ_X_BCRC_3      22
#define BZ_X_BCRC_4      23
#define BZ_X_RANDBIT     24
#define BZ_X_ORIGPTR_1   25
#define BZ_X_ORIGPTR_2   26
#define BZ_X_ORIGPTR_3   27
#define BZ_X_MAPPING_1   28
#define BZ_X_MAPPING_2   29
#define BZ_X_SELECTOR_1  30
#define BZ_X_SELECTOR_2  31
#define BZ_X_SELECTOR_3  32
#define BZ_X_CODING_1    33
#define BZ_X_CODING_2    34
#define BZ_X_CODING_3    35
#define BZ_X_MTF_1       36
#define BZ_X_MTF_2       37
#define BZ_X_MTF_3       38
#define BZ_X_MTF_4       39
#define BZ_X_MTF_5       40
#define BZ_X_MTF_6       41
#define BZ_X_ENDHDR_2    42
#define BZ_X_ENDHDR_3    43
#define BZ_X_ENDHDR_4    44
#define BZ_X_ENDHDR_5    45
#define BZ_X_ENDHDR_6    46
#define BZ_X_CCRC_1      47
#define BZ_X_CCRC_2      48
#define BZ_X_CCRC_3      49
#define BZ_X_CCRC_4      50



/*-- Constants for the fast MTF decoder. --*/

#define MTFA_SIZE 4096
#define MTFL_SIZE 16



/*-- Structure holding all the decompression-side stuff. --*/

typedef
   struct {
      /* pointer back to the struct bz_stream */
      bz_stream* strm;

      /* state indicator for this stream */
      Int32    state;

      /* for doing the final run-length decoding */
      UChar    state_out_ch;
      Int32    state_out_len;
      Bool     blockRandomised;
      BZ_RAND_DECLS;

      /* the buffer for bit stream reading */
      UInt32   bsBuff;
      Int32    bsLive;

      /* misc administratium */
      Int32    blockSize100k;
      Bool     smallDecompress;
      Int32    currBlockNo;
      Int32    verbosity;

      /* for undoing the Burrows-Wheeler transform */
      Int32    origPtr;
      UInt32   tPos;
      Int32    k0;
      Int32    unzftab[256];
      Int32    nblock_used;
      Int32    cftab[257];
      Int32    cftabCopy[257];

      /* for undoing the Burrows-Wheeler transform (FAST) */
      UInt32   *tt;

      /* for undoing the Burrows-Wheeler transform (SMALL) */
      UInt16   *ll16;
      UChar    *ll4;

      /* stored and calculated CRCs */
      UInt32   storedBlockCRC;
      UInt32   storedCombinedCRC;
      UInt32   calculatedBlockCRC;
      UInt32   calculatedCombinedCRC;

      /* map of bytes used in block */
      Int32    nInUse;
      Bool     inUse[256];
      Bool     inUse16[16];
      UChar    seqToUnseq[256];

      /* for decoding the MTF values */
      UChar    mtfa   [MTFA_SIZE];
      Int32    mtfbase[256 / MTFL_SIZE];
      UChar    selector   [BZ_MAX_SELECTORS];
      UChar    selectorMtf[BZ_MAX_SELECTORS];
      UChar    len  [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];

      Int32    limit  [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
      Int32    base   [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
      Int32    perm   [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
      Int32    minLens[BZ_N_GROUPS];

      /* save area for scalars in the main decompress code */
      Int32    save_i;
      Int32    save_j;
      Int32    save_t;
      Int32    save_alphaSize;
      Int32    save_nGroups;
      Int32    save_nSelectors;
      Int32    save_EOB;
      Int32    save_groupNo;
      Int32    save_groupPos;
      Int32    save_nextSym;
      Int32    save_nblockMAX;
      Int32    save_nblock;
      Int32    save_es;
      Int32    save_N;
      Int32    save_curr;
      Int32    save_zt;
      Int32    save_zn; 
      Int32    save_zvec;
      Int32    save_zj;
      Int32    save_gSel;
      Int32    save_gMinlen;
      Int32*   save_gLimit;
      Int32*   save_gBase;
      Int32*   save_gPerm;

   }
   DState;



/*-- Macros for decompression. --*/

#define BZ_GET_FAST(cccc)                     \
    /* c_tPos is unsigned, hence test < 0 is pointless. */ \
    if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \
    s->tPos = s->tt[s->tPos];                 \
    cccc = (UChar)(s->tPos & 0xff);           \
    s->tPos >>= 8;

#define BZ_GET_FAST_C(cccc)                   \
    /* c_tPos is unsigned, hence test < 0 is pointless. */ \
    if (c_tPos >= (UInt32)100000 * (UInt32)ro_blockSize100k) return True; \
    c_tPos = c_tt[c_tPos];                    \
    cccc = (UChar)(c_tPos & 0xff);            \
    c_tPos >>= 8;

#define SET_LL4(i,n)                                          \
   { if (((i) & 0x1) == 0)                                    \
        s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else    \
        s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4);  \
   }

#define GET_LL4(i)                             \
   ((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF)

#define SET_LL(i,n)                          \
   { s->ll16[i] = (UInt16)(n & 0x0000ffff);  \
     SET_LL4(i, n >> 16);                    \
   }

#define GET_LL(i) \
   (((UInt32)s->ll16[i]) | (GET_LL4(i) << 16))

#define BZ_GET_SMALL(cccc)                            \
    /* c_tPos is unsigned, hence test < 0 is pointless. */ \
    if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \
    cccc = BZ2_indexIntoF ( s->tPos, s->cftab );    \
    s->tPos = GET_LL(s->tPos);


/*-- externs for decompression. --*/

extern Int32 
BZ2_indexIntoF ( Int32, Int32* );

extern Int32 
BZ2_decompress ( DState* );

extern void 
BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*,
                           Int32,  Int32, Int32 );


#endif


/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/

#ifdef BZ_NO_STDIO
#ifndef NULL
#define NULL 0
#endif
#endif


/*-------------------------------------------------------------*/
/*--- end                                   bzlib_private.h ---*/
/*-------------------------------------------------------------*/

https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/bzlib.h

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/bzlib.h used in: Step 708, Step 709, Step 710, Step 711, Step 712, Step 713, Step 714
/*-------------------------------------------------------------*/
/*--- Public header file for the library.                   ---*/
/*---                                               bzlib.h ---*/
/*-------------------------------------------------------------*/

/* ------------------------------------------------------------------
   This file is part of bzip2/libbzip2, a program and library for
   lossless, block-sorting data compression.

   bzip2/libbzip2 version 1.0.8 of 13 July 2019
   Copyright (C) 1996-2019 Julian Seward <jseward@acm.org>

   Please read the WARNING, DISCLAIMER and PATENTS sections in the 
   README file.

   This program is released under the terms of the license contained
   in the file LICENSE.
   ------------------------------------------------------------------ */


#ifndef _BZLIB_H
#define _BZLIB_H

#ifdef __cplusplus
extern "C" {
#endif

#define BZ_RUN               0
#define BZ_FLUSH             1
#define BZ_FINISH            2

#define BZ_OK                0
#define BZ_RUN_OK            1
#define BZ_FLUSH_OK          2
#define BZ_FINISH_OK         3
#define BZ_STREAM_END        4
#define BZ_SEQUENCE_ERROR    (-1)
#define BZ_PARAM_ERROR       (-2)
#define BZ_MEM_ERROR         (-3)
#define BZ_DATA_ERROR        (-4)
#define BZ_DATA_ERROR_MAGIC  (-5)
#define BZ_IO_ERROR          (-6)
#define BZ_UNEXPECTED_EOF    (-7)
#define BZ_OUTBUFF_FULL      (-8)
#define BZ_CONFIG_ERROR      (-9)

typedef 
   struct {
      char *next_in;
      unsigned int avail_in;
      unsigned int total_in_lo32;
      unsigned int total_in_hi32;

      char *next_out;
      unsigned int avail_out;
      unsigned int total_out_lo32;
      unsigned int total_out_hi32;

      void *state;

      void *(*bzalloc)(void *,int,int);
      void (*bzfree)(void *,void *);
      void *opaque;
   } 
   bz_stream;


#ifndef BZ_IMPORT
#define BZ_EXPORT
#endif

#ifndef BZ_NO_STDIO
/* Need a definitition for FILE */
#include <stdio.h>
#endif

#ifdef _WIN32
#   include <windows.h>
#   ifdef small
      /* windows.h define small to char */
#      undef small
#   endif
#   ifdef BZ_EXPORT
#   define BZ_API(func) WINAPI func
#   define BZ_EXTERN extern
#   else
   /* import windows dll dynamically */
#   define BZ_API(func) (WINAPI * func)
#   define BZ_EXTERN
#   endif
#else
#   define BZ_API(func) func
#   define BZ_EXTERN extern
#endif


/*-- Core (low-level) library functions --*/

BZ_EXTERN int BZ_API(BZ2_bzCompressInit) ( 
      bz_stream* strm, 
      int        blockSize100k, 
      int        verbosity, 
      int        workFactor 
   );

BZ_EXTERN int BZ_API(BZ2_bzCompress) ( 
      bz_stream* strm, 
      int action 
   );

BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) ( 
      bz_stream* strm 
   );

BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) ( 
      bz_stream *strm, 
      int       verbosity, 
      int       small
   );

BZ_EXTERN int BZ_API(BZ2_bzDecompress) ( 
      bz_stream* strm 
   );

BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) ( 
      bz_stream *strm 
   );



/*-- High(er) level library functions --*/

#ifndef BZ_NO_STDIO
#define BZ_MAX_UNUSED 5000

typedef void BZFILE;

BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) ( 
      int*  bzerror,   
      FILE* f, 
      int   verbosity, 
      int   small,
      void* unused,    
      int   nUnused 
   );

BZ_EXTERN void BZ_API(BZ2_bzReadClose) ( 
      int*    bzerror, 
      BZFILE* b 
   );

BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) ( 
      int*    bzerror, 
      BZFILE* b, 
      void**  unused,  
      int*    nUnused 
   );

BZ_EXTERN int BZ_API(BZ2_bzRead) ( 
      int*    bzerror, 
      BZFILE* b, 
      void*   buf, 
      int     len 
   );

BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) ( 
      int*  bzerror,      
      FILE* f, 
      int   blockSize100k, 
      int   verbosity, 
      int   workFactor 
   );

BZ_EXTERN void BZ_API(BZ2_bzWrite) ( 
      int*    bzerror, 
      BZFILE* b, 
      void*   buf, 
      int     len 
   );

BZ_EXTERN void BZ_API(BZ2_bzWriteClose) ( 
      int*          bzerror, 
      BZFILE*       b, 
      int           abandon, 
      unsigned int* nbytes_in, 
      unsigned int* nbytes_out 
   );

BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) ( 
      int*          bzerror, 
      BZFILE*       b, 
      int           abandon, 
      unsigned int* nbytes_in_lo32, 
      unsigned int* nbytes_in_hi32, 
      unsigned int* nbytes_out_lo32, 
      unsigned int* nbytes_out_hi32
   );
#endif


/*-- Utility functions --*/

BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) ( 
      char*         dest, 
      unsigned int* destLen,
      char*         source, 
      unsigned int  sourceLen,
      int           blockSize100k, 
      int           verbosity, 
      int           workFactor 
   );

BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) ( 
      char*         dest, 
      unsigned int* destLen,
      char*         source, 
      unsigned int  sourceLen,
      int           small, 
      int           verbosity 
   );


/*--
   Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp)
   to support better zlib compatibility.
   This code is not _officially_ part of libbzip2 (yet);
   I haven't tested it, documented it, or considered the
   threading-safeness of it.
   If this code breaks, please contact both Yoshioka and me.
--*/

BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) (
      void
   );

#ifndef BZ_NO_STDIO
BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) (
      const char *path,
      const char *mode
   );

BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) (
      int        fd,
      const char *mode
   );
         
BZ_EXTERN int BZ_API(BZ2_bzread) (
      BZFILE* b, 
      void* buf, 
      int len 
   );

BZ_EXTERN int BZ_API(BZ2_bzwrite) (
      BZFILE* b, 
      void*   buf, 
      int     len 
   );

BZ_EXTERN int BZ_API(BZ2_bzflush) (
      BZFILE* b
   );

BZ_EXTERN void BZ_API(BZ2_bzclose) (
      BZFILE* b
   );

BZ_EXTERN const char * BZ_API(BZ2_bzerror) (
      BZFILE *b, 
      int    *errnum
   );
#endif

#ifdef __cplusplus
}
#endif

#endif

/*-------------------------------------------------------------*/
/*--- end                                           bzlib.h ---*/
/*-------------------------------------------------------------*/

https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/huffman.c

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/huffman.c used in: Step 709
/*-------------------------------------------------------------*/
/*--- Huffman coding low-level stuff                        ---*/
/*---                                             huffman.c ---*/
/*-------------------------------------------------------------*/

/* ------------------------------------------------------------------
   This file is part of bzip2/libbzip2, a program and library for
   lossless, block-sorting data compression.

   bzip2/libbzip2 version 1.0.8 of 13 July 2019
   Copyright (C) 1996-2019 Julian Seward <jseward@acm.org>

   Please read the WARNING, DISCLAIMER and PATENTS sections in the 
   README file.

   This program is released under the terms of the license contained
   in the file LICENSE.
   ------------------------------------------------------------------ */


#include "bzlib_private.h"

/*---------------------------------------------------*/
#define WEIGHTOF(zz0)  ((zz0) & 0xffffff00)
#define DEPTHOF(zz1)   ((zz1) & 0x000000ff)
#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3))

#define ADDWEIGHTS(zw1,zw2)                           \
   (WEIGHTOF(zw1)+WEIGHTOF(zw2)) |                    \
   (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2)))

#define UPHEAP(z)                                     \
{                                                     \
   Int32 zz, tmp;                                     \
   zz = z; tmp = heap[zz];                            \
   while (weight[tmp] < weight[heap[zz >> 1]]) {      \
      heap[zz] = heap[zz >> 1];                       \
      zz >>= 1;                                       \
   }                                                  \
   heap[zz] = tmp;                                    \
}

#define DOWNHEAP(z)                                   \
{                                                     \
   Int32 zz, yy, tmp;                                 \
   zz = z; tmp = heap[zz];                            \
   while (True) {                                     \
      yy = zz << 1;                                   \
      if (yy > nHeap) break;                          \
      if (yy < nHeap &&                               \
          weight[heap[yy+1]] < weight[heap[yy]])      \
         yy++;                                        \
      if (weight[tmp] < weight[heap[yy]]) break;      \
      heap[zz] = heap[yy];                            \
      zz = yy;                                        \
   }                                                  \
   heap[zz] = tmp;                                    \
}


/*---------------------------------------------------*/
void BZ2_hbMakeCodeLengths ( UChar *len, 
                             Int32 *freq,
                             Int32 alphaSize,
                             Int32 maxLen )
{
   /*--
      Nodes and heap entries run from 1.  Entry 0
      for both the heap and nodes is a sentinel.
   --*/
   Int32 nNodes, nHeap, n1, n2, i, j, k;
   Bool  tooLong;

   Int32 heap   [ BZ_MAX_ALPHA_SIZE + 2 ];
   Int32 weight [ BZ_MAX_ALPHA_SIZE * 2 ];
   Int32 parent [ BZ_MAX_ALPHA_SIZE * 2 ]; 

   for (i = 0; i < alphaSize; i++)
      weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8;

   while (True) {

      nNodes = alphaSize;
      nHeap = 0;

      heap[0] = 0;
      weight[0] = 0;
      parent[0] = -2;

      for (i = 1; i <= alphaSize; i++) {
         parent[i] = -1;
         nHeap++;
         heap[nHeap] = i;
         UPHEAP(nHeap);
      }

      AssertH( nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001 );
   
      while (nHeap > 1) {
         n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1);
         n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1);
         nNodes++;
         parent[n1] = parent[n2] = nNodes;
         weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]);
         parent[nNodes] = -1;
         nHeap++;
         heap[nHeap] = nNodes;
         UPHEAP(nHeap);
      }

      AssertH( nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002 );

      tooLong = False;
      for (i = 1; i <= alphaSize; i++) {
         j = 0;
         k = i;
         while (parent[k] >= 0) { k = parent[k]; j++; }
         len[i-1] = j;
         if (j > maxLen) tooLong = True;
      }
      
      if (! tooLong) break;

      /* 17 Oct 04: keep-going condition for the following loop used
         to be 'i < alphaSize', which missed the last element,
         theoretically leading to the possibility of the compressor
         looping.  However, this count-scaling step is only needed if
         one of the generated Huffman code words is longer than
         maxLen, which up to and including version 1.0.2 was 20 bits,
         which is extremely unlikely.  In version 1.0.3 maxLen was
         changed to 17 bits, which has minimal effect on compression
         ratio, but does mean this scaling step is used from time to
         time, enough to verify that it works.

         This means that bzip2-1.0.3 and later will only produce
         Huffman codes with a maximum length of 17 bits.  However, in
         order to preserve backwards compatibility with bitstreams
         produced by versions pre-1.0.3, the decompressor must still
         handle lengths of up to 20. */

      for (i = 1; i <= alphaSize; i++) {
         j = weight[i] >> 8;
         j = 1 + (j / 2);
         weight[i] = j << 8;
      }
   }
}


/*---------------------------------------------------*/
void BZ2_hbAssignCodes ( Int32 *code,
                         UChar *length,
                         Int32 minLen,
                         Int32 maxLen,
                         Int32 alphaSize )
{
   Int32 n, vec, i;

   vec = 0;
   for (n = minLen; n <= maxLen; n++) {
      for (i = 0; i < alphaSize; i++)
         if (length[i] == n) { code[i] = vec; vec++; };
      vec <<= 1;
   }
}


/*---------------------------------------------------*/
void BZ2_hbCreateDecodeTables ( Int32 *limit,
                                Int32 *base,
                                Int32 *perm,
                                UChar *length,
                                Int32 minLen,
                                Int32 maxLen,
                                Int32 alphaSize )
{
   Int32 pp, i, j, vec;

   pp = 0;
   for (i = minLen; i <= maxLen; i++)
      for (j = 0; j < alphaSize; j++)
         if (length[j] == i) { perm[pp] = j; pp++; };

   for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0;
   for (i = 0; i < alphaSize; i++) base[length[i]+1]++;

   for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1];

   for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0;
   vec = 0;

   for (i = minLen; i <= maxLen; i++) {
      vec += (base[i+1] - base[i]);
      limit[i] = vec-1;
      vec <<= 1;
   }
   for (i = minLen + 1; i <= maxLen; i++)
      base[i] = ((limit[i-1] + 1) << 1) - base[i];
}


/*-------------------------------------------------------------*/
/*--- end                                         huffman.c ---*/
/*-------------------------------------------------------------*/

https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/crctable.c

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/crctable.c used in: Step 710
/*-------------------------------------------------------------*/
/*--- Table for doing CRCs                                  ---*/
/*---                                            crctable.c ---*/
/*-------------------------------------------------------------*/

/* ------------------------------------------------------------------
   This file is part of bzip2/libbzip2, a program and library for
   lossless, block-sorting data compression.

   bzip2/libbzip2 version 1.0.8 of 13 July 2019
   Copyright (C) 1996-2019 Julian Seward <jseward@acm.org>

   Please read the WARNING, DISCLAIMER and PATENTS sections in the 
   README file.

   This program is released under the terms of the license contained
   in the file LICENSE.
   ------------------------------------------------------------------ */


#include "bzlib_private.h"

/*--
  I think this is an implementation of the AUTODIN-II,
  Ethernet & FDDI 32-bit CRC standard.  Vaguely derived
  from code by Rob Warnock, in Section 51 of the
  comp.compression FAQ.
--*/

UInt32 BZ2_crc32Table[256] = {

   /*-- Ugly, innit? --*/

   0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L,
   0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L,
   0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L,
   0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL,
   0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L,
   0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L,
   0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L,
   0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL,
   0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L,
   0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L,
   0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L,
   0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL,
   0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L,
   0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L,
   0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L,
   0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL,
   0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL,
   0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L,
   0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L,
   0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL,
   0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL,
   0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L,
   0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L,
   0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL,
   0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL,
   0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L,
   0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L,
   0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL,
   0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL,
   0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L,
   0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L,
   0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL,
   0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L,
   0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL,
   0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL,
   0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L,
   0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L,
   0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL,
   0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL,
   0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L,
   0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L,
   0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL,
   0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL,
   0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L,
   0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L,
   0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL,
   0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL,
   0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L,
   0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L,
   0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL,
   0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L,
   0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L,
   0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L,
   0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL,
   0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L,
   0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L,
   0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L,
   0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL,
   0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L,
   0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L,
   0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L,
   0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL,
   0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L,
   0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L
};


/*-------------------------------------------------------------*/
/*--- end                                        crctable.c ---*/
/*-------------------------------------------------------------*/

https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/randtable.c

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/randtable.c used in: Step 711
/*-------------------------------------------------------------*/
/*--- Table for randomising repetitive blocks               ---*/
/*---                                           randtable.c ---*/
/*-------------------------------------------------------------*/

/* ------------------------------------------------------------------
   This file is part of bzip2/libbzip2, a program and library for
   lossless, block-sorting data compression.

   bzip2/libbzip2 version 1.0.8 of 13 July 2019
   Copyright (C) 1996-2019 Julian Seward <jseward@acm.org>

   Please read the WARNING, DISCLAIMER and PATENTS sections in the 
   README file.

   This program is released under the terms of the license contained
   in the file LICENSE.
   ------------------------------------------------------------------ */


#include "bzlib_private.h"


/*---------------------------------------------*/
Int32 BZ2_rNums[512] = { 
   619, 720, 127, 481, 931, 816, 813, 233, 566, 247, 
   985, 724, 205, 454, 863, 491, 741, 242, 949, 214, 
   733, 859, 335, 708, 621, 574, 73, 654, 730, 472, 
   419, 436, 278, 496, 867, 210, 399, 680, 480, 51, 
   878, 465, 811, 169, 869, 675, 611, 697, 867, 561, 
   862, 687, 507, 283, 482, 129, 807, 591, 733, 623, 
   150, 238, 59, 379, 684, 877, 625, 169, 643, 105, 
   170, 607, 520, 932, 727, 476, 693, 425, 174, 647, 
   73, 122, 335, 530, 442, 853, 695, 249, 445, 515, 
   909, 545, 703, 919, 874, 474, 882, 500, 594, 612, 
   641, 801, 220, 162, 819, 984, 589, 513, 495, 799, 
   161, 604, 958, 533, 221, 400, 386, 867, 600, 782, 
   382, 596, 414, 171, 516, 375, 682, 485, 911, 276, 
   98, 553, 163, 354, 666, 933, 424, 341, 533, 870, 
   227, 730, 475, 186, 263, 647, 537, 686, 600, 224, 
   469, 68, 770, 919, 190, 373, 294, 822, 808, 206, 
   184, 943, 795, 384, 383, 461, 404, 758, 839, 887, 
   715, 67, 618, 276, 204, 918, 873, 777, 604, 560, 
   951, 160, 578, 722, 79, 804, 96, 409, 713, 940, 
   652, 934, 970, 447, 318, 353, 859, 672, 112, 785, 
   645, 863, 803, 350, 139, 93, 354, 99, 820, 908, 
   609, 772, 154, 274, 580, 184, 79, 626, 630, 742, 
   653, 282, 762, 623, 680, 81, 927, 626, 789, 125, 
   411, 521, 938, 300, 821, 78, 343, 175, 128, 250, 
   170, 774, 972, 275, 999, 639, 495, 78, 352, 126, 
   857, 956, 358, 619, 580, 124, 737, 594, 701, 612, 
   669, 112, 134, 694, 363, 992, 809, 743, 168, 974, 
   944, 375, 748, 52, 600, 747, 642, 182, 862, 81, 
   344, 805, 988, 739, 511, 655, 814, 334, 249, 515, 
   897, 955, 664, 981, 649, 113, 974, 459, 893, 228, 
   433, 837, 553, 268, 926, 240, 102, 654, 459, 51, 
   686, 754, 806, 760, 493, 403, 415, 394, 687, 700, 
   946, 670, 656, 610, 738, 392, 760, 799, 887, 653, 
   978, 321, 576, 617, 626, 502, 894, 679, 243, 440, 
   680, 879, 194, 572, 640, 724, 926, 56, 204, 700, 
   707, 151, 457, 449, 797, 195, 791, 558, 945, 679, 
   297, 59, 87, 824, 713, 663, 412, 693, 342, 606, 
   134, 108, 571, 364, 631, 212, 174, 643, 304, 329, 
   343, 97, 430, 751, 497, 314, 983, 374, 822, 928, 
   140, 206, 73, 263, 980, 736, 876, 478, 430, 305, 
   170, 514, 364, 692, 829, 82, 855, 953, 676, 246, 
   369, 970, 294, 750, 807, 827, 150, 790, 288, 923, 
   804, 378, 215, 828, 592, 281, 565, 555, 710, 82, 
   896, 831, 547, 261, 524, 462, 293, 465, 502, 56, 
   661, 821, 976, 991, 658, 869, 905, 758, 745, 193, 
   768, 550, 608, 933, 378, 286, 215, 979, 792, 961, 
   61, 688, 793, 644, 986, 403, 106, 366, 905, 644, 
   372, 567, 466, 434, 645, 210, 389, 550, 919, 135, 
   780, 773, 635, 389, 707, 100, 626, 958, 165, 504, 
   920, 176, 193, 713, 857, 265, 203, 50, 668, 108, 
   645, 990, 626, 197, 510, 357, 358, 850, 858, 364, 
   936, 638
};


/*-------------------------------------------------------------*/
/*--- end                                       randtable.c ---*/
/*-------------------------------------------------------------*/

https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/compress.c

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/compress.c used in: Step 712
/*-------------------------------------------------------------*/
/*--- Compression machinery (not incl block sorting)        ---*/
/*---                                            compress.c ---*/
/*-------------------------------------------------------------*/

/* ------------------------------------------------------------------
   This file is part of bzip2/libbzip2, a program and library for
   lossless, block-sorting data compression.

   bzip2/libbzip2 version 1.0.8 of 13 July 2019
   Copyright (C) 1996-2019 Julian Seward <jseward@acm.org>

   Please read the WARNING, DISCLAIMER and PATENTS sections in the 
   README file.

   This program is released under the terms of the license contained
   in the file LICENSE.
   ------------------------------------------------------------------ */


/* CHANGES
    0.9.0    -- original version.
    0.9.0a/b -- no changes in this file.
    0.9.0c   -- changed setting of nGroups in sendMTFValues() 
                so as to do a bit better on small files
*/

#include "bzlib_private.h"


/*---------------------------------------------------*/
/*--- Bit stream I/O                              ---*/
/*---------------------------------------------------*/

/*---------------------------------------------------*/
void BZ2_bsInitWrite ( EState* s )
{
   s->bsLive = 0;
   s->bsBuff = 0;
}


/*---------------------------------------------------*/
static
void bsFinishWrite ( EState* s )
{
   while (s->bsLive > 0) {
      s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24);
      s->numZ++;
      s->bsBuff <<= 8;
      s->bsLive -= 8;
   }
}


/*---------------------------------------------------*/
#define bsNEEDW(nz)                           \
{                                             \
   while (s->bsLive >= 8) {                   \
      s->zbits[s->numZ]                       \
         = (UChar)(s->bsBuff >> 24);          \
      s->numZ++;                              \
      s->bsBuff <<= 8;                        \
      s->bsLive -= 8;                         \
   }                                          \
}


/*---------------------------------------------------*/
static
__inline__
void bsW ( EState* s, Int32 n, UInt32 v )
{
   bsNEEDW ( n );
   s->bsBuff |= (v << (32 - s->bsLive - n));
   s->bsLive += n;
}


/*---------------------------------------------------*/
static
void bsPutUInt32 ( EState* s, UInt32 u )
{
   bsW ( s, 8, (u >> 24) & 0xffL );
   bsW ( s, 8, (u >> 16) & 0xffL );
   bsW ( s, 8, (u >>  8) & 0xffL );
   bsW ( s, 8,  u        & 0xffL );
}


/*---------------------------------------------------*/
static
void bsPutUChar ( EState* s, UChar c )
{
   bsW( s, 8, (UInt32)c );
}


/*---------------------------------------------------*/
/*--- The back end proper                         ---*/
/*---------------------------------------------------*/

/*---------------------------------------------------*/
static
void makeMaps_e ( EState* s )
{
   Int32 i;
   s->nInUse = 0;
   for (i = 0; i < 256; i++)
      if (s->inUse[i]) {
         s->unseqToSeq[i] = s->nInUse;
         s->nInUse++;
      }
}


/*---------------------------------------------------*/
static
void generateMTFValues ( EState* s )
{
   UChar   yy[256];
   Int32   i, j;
   Int32   zPend;
   Int32   wr;
   Int32   EOB;

   /* 
      After sorting (eg, here),
         s->arr1 [ 0 .. s->nblock-1 ] holds sorted order,
         and
         ((UChar*)s->arr2) [ 0 .. s->nblock-1 ] 
         holds the original block data.

      The first thing to do is generate the MTF values,
      and put them in
         ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ].
      Because there are strictly fewer or equal MTF values
      than block values, ptr values in this area are overwritten
      with MTF values only when they are no longer needed.

      The final compressed bitstream is generated into the
      area starting at
         (UChar*) (&((UChar*)s->arr2)[s->nblock])

      These storage aliases are set up in bzCompressInit(),
      except for the last one, which is arranged in 
      compressBlock().
   */
   UInt32* ptr   = s->ptr;
   UChar* block  = s->block;
   UInt16* mtfv  = s->mtfv;

   makeMaps_e ( s );
   EOB = s->nInUse+1;

   for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0;

   wr = 0;
   zPend = 0;
   for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i;

   for (i = 0; i < s->nblock; i++) {
      UChar ll_i;
      AssertD ( wr <= i, "generateMTFValues(1)" );
      j = ptr[i]-1; if (j < 0) j += s->nblock;
      ll_i = s->unseqToSeq[block[j]];
      AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" );

      if (yy[0] == ll_i) { 
         zPend++;
      } else {

         if (zPend > 0) {
            zPend--;
            while (True) {
               if (zPend & 1) {
                  mtfv[wr] = BZ_RUNB; wr++; 
                  s->mtfFreq[BZ_RUNB]++; 
               } else {
                  mtfv[wr] = BZ_RUNA; wr++; 
                  s->mtfFreq[BZ_RUNA]++; 
               }
               if (zPend < 2) break;
               zPend = (zPend - 2) / 2;
            };
            zPend = 0;
         }
         {
            register UChar  rtmp;
            register UChar* ryy_j;
            register UChar  rll_i;
            rtmp  = yy[1];
            yy[1] = yy[0];
            ryy_j = &(yy[1]);
            rll_i = ll_i;
            while ( rll_i != rtmp ) {
               register UChar rtmp2;
               ryy_j++;
               rtmp2  = rtmp;
               rtmp   = *ryy_j;
               *ryy_j = rtmp2;
            };
            yy[0] = rtmp;
            j = ryy_j - &(yy[0]);
            mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++;
         }

      }
   }

   if (zPend > 0) {
      zPend--;
      while (True) {
         if (zPend & 1) {
            mtfv[wr] = BZ_RUNB; wr++; 
            s->mtfFreq[BZ_RUNB]++; 
         } else {
            mtfv[wr] = BZ_RUNA; wr++; 
            s->mtfFreq[BZ_RUNA]++; 
         }
         if (zPend < 2) break;
         zPend = (zPend - 2) / 2;
      };
      zPend = 0;
   }

   mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++;

   s->nMTF = wr;
}


/*---------------------------------------------------*/
#define BZ_LESSER_ICOST  0
#define BZ_GREATER_ICOST 15

static
void sendMTFValues ( EState* s )
{
   Int32 v, t, i, j, gs, ge, totc, bt, bc, iter;
   Int32 nSelectors, alphaSize, minLen, maxLen, selCtr;
   Int32 nGroups, nBytes;

   /*--
   UChar  len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
   is a global since the decoder also needs it.

   Int32  code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
   Int32  rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
   are also globals only used in this proc.
   Made global to keep stack frame size small.
   --*/


   UInt16 cost[BZ_N_GROUPS];
   Int32  fave[BZ_N_GROUPS];

   UInt16* mtfv = s->mtfv;

   if (s->verbosity >= 3)
      VPrintf3( "      %d in block, %d after MTF & 1-2 coding, "
                "%d+2 syms in use\n", 
                s->nblock, s->nMTF, s->nInUse );

   alphaSize = s->nInUse+2;
   for (t = 0; t < BZ_N_GROUPS; t++)
      for (v = 0; v < alphaSize; v++)
         s->len[t][v] = BZ_GREATER_ICOST;

   /*--- Decide how many coding tables to use ---*/
   AssertH ( s->nMTF > 0, 3001 );
   if (s->nMTF < 200)  nGroups = 2; else
   if (s->nMTF < 600)  nGroups = 3; else
   if (s->nMTF < 1200) nGroups = 4; else
   if (s->nMTF < 2400) nGroups = 5; else
                       nGroups = 6;

   /*--- Generate an initial set of coding tables ---*/
   { 
      Int32 nPart, remF, tFreq, aFreq;

      nPart = nGroups;
      remF  = s->nMTF;
      gs = 0;
      while (nPart > 0) {
         tFreq = remF / nPart;
         ge = gs-1;
         aFreq = 0;
         while (aFreq < tFreq && ge < alphaSize-1) {
            ge++;
            aFreq += s->mtfFreq[ge];
         }

         if (ge > gs 
             && nPart != nGroups && nPart != 1 
             && ((nGroups-nPart) % 2 == 1)) {
            aFreq -= s->mtfFreq[ge];
            ge--;
         }

         if (s->verbosity >= 3)
            VPrintf5( "      initial group %d, [%d .. %d], "
                      "has %d syms (%4.1f%%)\n",
                      nPart, gs, ge, aFreq, 
                      (100.0 * (float)aFreq) / (float)(s->nMTF) );
 
         for (v = 0; v < alphaSize; v++)
            if (v >= gs && v <= ge) 
               s->len[nPart-1][v] = BZ_LESSER_ICOST; else
               s->len[nPart-1][v] = BZ_GREATER_ICOST;
 
         nPart--;
         gs = ge+1;
         remF -= aFreq;
      }
   }

   /*--- 
      Iterate up to BZ_N_ITERS times to improve the tables.
   ---*/
   for (iter = 0; iter < BZ_N_ITERS; iter++) {

      for (t = 0; t < nGroups; t++) fave[t] = 0;

      for (t = 0; t < nGroups; t++)
         for (v = 0; v < alphaSize; v++)
            s->rfreq[t][v] = 0;

      /*---
        Set up an auxiliary length table which is used to fast-track
    the common case (nGroups == 6). 
      ---*/
      if (nGroups == 6) {
         for (v = 0; v < alphaSize; v++) {
            s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v];
            s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v];
            s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v];
     }
      }

      nSelectors = 0;
      totc = 0;
      gs = 0;
      while (True) {

         /*--- Set group start & end marks. --*/
         if (gs >= s->nMTF) break;
         ge = gs + BZ_G_SIZE - 1; 
         if (ge >= s->nMTF) ge = s->nMTF-1;

         /*-- 
            Calculate the cost of this group as coded
            by each of the coding tables.
         --*/
         for (t = 0; t < nGroups; t++) cost[t] = 0;

         if (nGroups == 6 && 50 == ge-gs+1) {
            /*--- fast track the common case ---*/
            register UInt32 cost01, cost23, cost45;
            register UInt16 icv;
            cost01 = cost23 = cost45 = 0;

#           define BZ_ITER(nn)                \
               icv = mtfv[gs+(nn)];           \
               cost01 += s->len_pack[icv][0]; \
               cost23 += s->len_pack[icv][1]; \
               cost45 += s->len_pack[icv][2]; \

            BZ_ITER(0);  BZ_ITER(1);  BZ_ITER(2);  BZ_ITER(3);  BZ_ITER(4);
            BZ_ITER(5);  BZ_ITER(6);  BZ_ITER(7);  BZ_ITER(8);  BZ_ITER(9);
            BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14);
            BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19);
            BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24);
            BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29);
            BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34);
            BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39);
            BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44);
            BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49);

#           undef BZ_ITER

            cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16;
            cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16;
            cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16;

         } else {
        /*--- slow version which correctly handles all situations ---*/
            for (i = gs; i <= ge; i++) { 
               UInt16 icv = mtfv[i];
               for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv];
            }
         }
 
         /*-- 
            Find the coding table which is best for this group,
            and record its identity in the selector table.
         --*/
         bc = 999999999; bt = -1;
         for (t = 0; t < nGroups; t++)
            if (cost[t] < bc) { bc = cost[t]; bt = t; };
         totc += bc;
         fave[bt]++;
         s->selector[nSelectors] = bt;
         nSelectors++;

         /*-- 
            Increment the symbol frequencies for the selected table.
          --*/
         if (nGroups == 6 && 50 == ge-gs+1) {
            /*--- fast track the common case ---*/

#           define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++

            BZ_ITUR(0);  BZ_ITUR(1);  BZ_ITUR(2);  BZ_ITUR(3);  BZ_ITUR(4);
            BZ_ITUR(5);  BZ_ITUR(6);  BZ_ITUR(7);  BZ_ITUR(8);  BZ_ITUR(9);
            BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14);
            BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19);
            BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24);
            BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29);
            BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34);
            BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39);
            BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44);
            BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49);

#           undef BZ_ITUR

         } else {
        /*--- slow version which correctly handles all situations ---*/
            for (i = gs; i <= ge; i++)
               s->rfreq[bt][ mtfv[i] ]++;
         }

         gs = ge+1;
      }
      if (s->verbosity >= 3) {
         VPrintf2 ( "      pass %d: size is %d, grp uses are ", 
                   iter+1, totc/8 );
         for (t = 0; t < nGroups; t++)
            VPrintf1 ( "%d ", fave[t] );
         VPrintf0 ( "\n" );
      }

      /*--
        Recompute the tables based on the accumulated frequencies.
      --*/
      /* maxLen was changed from 20 to 17 in bzip2-1.0.3.  See 
         comment in huffman.c for details. */
      for (t = 0; t < nGroups; t++)
         BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]), 
                                 alphaSize, 17 /*20*/ );
   }


   AssertH( nGroups < 8, 3002 );
   AssertH( nSelectors < 32768 &&
            nSelectors <= BZ_MAX_SELECTORS,
            3003 );


   /*--- Compute MTF values for the selectors. ---*/
   {
      UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp;
      for (i = 0; i < nGroups; i++) pos[i] = i;
      for (i = 0; i < nSelectors; i++) {
         ll_i = s->selector[i];
         j = 0;
         tmp = pos[j];
         while ( ll_i != tmp ) {
            j++;
            tmp2 = tmp;
            tmp = pos[j];
            pos[j] = tmp2;
         };
         pos[0] = tmp;
         s->selectorMtf[i] = j;
      }
   };

   /*--- Assign actual codes for the tables. --*/
   for (t = 0; t < nGroups; t++) {
      minLen = 32;
      maxLen = 0;
      for (i = 0; i < alphaSize; i++) {
         if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
         if (s->len[t][i] < minLen) minLen = s->len[t][i];
      }
      AssertH ( !(maxLen > 17 /*20*/ ), 3004 );
      AssertH ( !(minLen < 1),  3005 );
      BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]), 
                          minLen, maxLen, alphaSize );
   }

   /*--- Transmit the mapping table. ---*/
   { 
      Bool inUse16[16];
      for (i = 0; i < 16; i++) {
          inUse16[i] = False;
          for (j = 0; j < 16; j++)
             if (s->inUse[i * 16 + j]) inUse16[i] = True;
      }
     
      nBytes = s->numZ;
      for (i = 0; i < 16; i++)
         if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0);

      for (i = 0; i < 16; i++)
         if (inUse16[i])
            for (j = 0; j < 16; j++) {
               if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0);
            }

      if (s->verbosity >= 3) 
         VPrintf1( "      bytes: mapping %d, ", s->numZ-nBytes );
   }

   /*--- Now the selectors. ---*/
   nBytes = s->numZ;
   bsW ( s, 3, nGroups );
   bsW ( s, 15, nSelectors );
   for (i = 0; i < nSelectors; i++) { 
      for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1);
      bsW(s,1,0);
   }
   if (s->verbosity >= 3)
      VPrintf1( "selectors %d, ", s->numZ-nBytes );

   /*--- Now the coding tables. ---*/
   nBytes = s->numZ;

   for (t = 0; t < nGroups; t++) {
      Int32 curr = s->len[t][0];
      bsW ( s, 5, curr );
      for (i = 0; i < alphaSize; i++) {
         while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ };
         while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ };
         bsW ( s, 1, 0 );
      }
   }

   if (s->verbosity >= 3)
      VPrintf1 ( "code lengths %d, ", s->numZ-nBytes );

   /*--- And finally, the block data proper ---*/
   nBytes = s->numZ;
   selCtr = 0;
   gs = 0;
   while (True) {
      if (gs >= s->nMTF) break;
      ge = gs + BZ_G_SIZE - 1; 
      if (ge >= s->nMTF) ge = s->nMTF-1;
      AssertH ( s->selector[selCtr] < nGroups, 3006 );

      if (nGroups == 6 && 50 == ge-gs+1) {
            /*--- fast track the common case ---*/
            UInt16 mtfv_i;
            UChar* s_len_sel_selCtr 
               = &(s->len[s->selector[selCtr]][0]);
            Int32* s_code_sel_selCtr
               = &(s->code[s->selector[selCtr]][0]);

#           define BZ_ITAH(nn)                      \
               mtfv_i = mtfv[gs+(nn)];              \
               bsW ( s,                             \
                     s_len_sel_selCtr[mtfv_i],      \
                     s_code_sel_selCtr[mtfv_i] )

            BZ_ITAH(0);  BZ_ITAH(1);  BZ_ITAH(2);  BZ_ITAH(3);  BZ_ITAH(4);
            BZ_ITAH(5);  BZ_ITAH(6);  BZ_ITAH(7);  BZ_ITAH(8);  BZ_ITAH(9);
            BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14);
            BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19);
            BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24);
            BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29);
            BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34);
            BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39);
            BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44);
            BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49);

#           undef BZ_ITAH

      } else {
     /*--- slow version which correctly handles all situations ---*/
         for (i = gs; i <= ge; i++) {
            bsW ( s, 
                  s->len  [s->selector[selCtr]] [mtfv[i]],
                  s->code [s->selector[selCtr]] [mtfv[i]] );
         }
      }


      gs = ge+1;
      selCtr++;
   }
   AssertH( selCtr == nSelectors, 3007 );

   if (s->verbosity >= 3)
      VPrintf1( "codes %d\n", s->numZ-nBytes );
}


/*---------------------------------------------------*/
void BZ2_compressBlock ( EState* s, Bool is_last_block )
{
   if (s->nblock > 0) {

      BZ_FINALISE_CRC ( s->blockCRC );
      s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31);
      s->combinedCRC ^= s->blockCRC;
      if (s->blockNo > 1) s->numZ = 0;

      if (s->verbosity >= 2)
         VPrintf4( "    block %d: crc = 0x%08x, "
                   "combined CRC = 0x%08x, size = %d\n",
                   s->blockNo, s->blockCRC, s->combinedCRC, s->nblock );

      BZ2_blockSort ( s );
   }

   s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]);

   /*-- If this is the first block, create the stream header. --*/
   if (s->blockNo == 1) {
      BZ2_bsInitWrite ( s );
      bsPutUChar ( s, BZ_HDR_B );
      bsPutUChar ( s, BZ_HDR_Z );
      bsPutUChar ( s, BZ_HDR_h );
      bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) );
   }

   if (s->nblock > 0) {

      bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 );
      bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 );
      bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 );

      /*-- Now the block's CRC, so it is in a known place. --*/
      bsPutUInt32 ( s, s->blockCRC );

      /*-- 
         Now a single bit indicating (non-)randomisation. 
         As of version 0.9.5, we use a better sorting algorithm
         which makes randomisation unnecessary.  So always set
         the randomised bit to 'no'.  Of course, the decoder
         still needs to be able to handle randomised blocks
         so as to maintain backwards compatibility with
         older versions of bzip2.
      --*/
      bsW(s,1,0);

      bsW ( s, 24, s->origPtr );
      generateMTFValues ( s );
      sendMTFValues ( s );
   }


   /*-- If this is the last block, add the stream trailer. --*/
   if (is_last_block) {

      bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 );
      bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 );
      bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 );
      bsPutUInt32 ( s, s->combinedCRC );
      if (s->verbosity >= 2)
         VPrintf1( "    final combined CRC = 0x%08x\n   ", s->combinedCRC );
      bsFinishWrite ( s );
   }
}


/*-------------------------------------------------------------*/
/*--- end                                        compress.c ---*/
/*-------------------------------------------------------------*/

https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/decompress.c

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/decompress.c used in: Step 713
/*-------------------------------------------------------------*/
/*--- Decompression machinery                               ---*/
/*---                                          decompress.c ---*/
/*-------------------------------------------------------------*/

/* ------------------------------------------------------------------
   This file is part of bzip2/libbzip2, a program and library for
   lossless, block-sorting data compression.

   bzip2/libbzip2 version 1.0.8 of 13 July 2019
   Copyright (C) 1996-2019 Julian Seward <jseward@acm.org>

   Please read the WARNING, DISCLAIMER and PATENTS sections in the 
   README file.

   This program is released under the terms of the license contained
   in the file LICENSE.
   ------------------------------------------------------------------ */


#include "bzlib_private.h"


/*---------------------------------------------------*/
static
void makeMaps_d ( DState* s )
{
   Int32 i;
   s->nInUse = 0;
   for (i = 0; i < 256; i++)
      if (s->inUse[i]) {
         s->seqToUnseq[s->nInUse] = i;
         s->nInUse++;
      }
}


/*---------------------------------------------------*/
#define RETURN(rrr)                               \
   { retVal = rrr; goto save_state_and_return; };

#define GET_BITS(lll,vvv,nnn)                     \
   case lll: s->state = lll;                      \
   while (True) {                                 \
      if (s->bsLive >= nnn) {                     \
         UInt32 v;                                \
         v = (s->bsBuff >>                        \
             (s->bsLive-nnn)) & ((1 << nnn)-1);   \
         s->bsLive -= nnn;                        \
         vvv = v;                                 \
         break;                                   \
      }                                           \
      if (s->strm->avail_in == 0) RETURN(BZ_OK);  \
      s->bsBuff                                   \
         = (s->bsBuff << 8) |                     \
           ((UInt32)                              \
              (*((UChar*)(s->strm->next_in))));   \
      s->bsLive += 8;                             \
      s->strm->next_in++;                         \
      s->strm->avail_in--;                        \
      s->strm->total_in_lo32++;                   \
      if (s->strm->total_in_lo32 == 0)            \
         s->strm->total_in_hi32++;                \
   }

#define GET_UCHAR(lll,uuu)                        \
   GET_BITS(lll,uuu,8)

#define GET_BIT(lll,uuu)                          \
   GET_BITS(lll,uuu,1)

/*---------------------------------------------------*/
#define GET_MTF_VAL(label1,label2,lval)           \
{                                                 \
   if (groupPos == 0) {                           \
      groupNo++;                                  \
      if (groupNo >= nSelectors)                  \
         RETURN(BZ_DATA_ERROR);                   \
      groupPos = BZ_G_SIZE;                       \
      gSel = s->selector[groupNo];                \
      gMinlen = s->minLens[gSel];                 \
      gLimit = &(s->limit[gSel][0]);              \
      gPerm = &(s->perm[gSel][0]);                \
      gBase = &(s->base[gSel][0]);                \
   }                                              \
   groupPos--;                                    \
   zn = gMinlen;                                  \
   GET_BITS(label1, zvec, zn);                    \
   while (1) {                                    \
      if (zn > 20 /* the longest code */)         \
         RETURN(BZ_DATA_ERROR);                   \
      if (zvec <= gLimit[zn]) break;              \
      zn++;                                       \
      GET_BIT(label2, zj);                        \
      zvec = (zvec << 1) | zj;                    \
   };                                             \
   if (zvec - gBase[zn] < 0                       \
       || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE)  \
      RETURN(BZ_DATA_ERROR);                      \
   lval = gPerm[zvec - gBase[zn]];                \
}


/*---------------------------------------------------*/
Int32 BZ2_decompress ( DState* s )
{
   UChar      uc;
   Int32      retVal;
   Int32      minLen, maxLen;
   bz_stream* strm = s->strm;

   /* stuff that needs to be saved/restored */
   Int32  i;
   Int32  j;
   Int32  t;
   Int32  alphaSize;
   Int32  nGroups;
   Int32  nSelectors;
   Int32  EOB;
   Int32  groupNo;
   Int32  groupPos;
   Int32  nextSym;
   Int32  nblockMAX;
   Int32  nblock;
   Int32  es;
   Int32  N;
   Int32  curr;
   Int32  zt;
   Int32  zn; 
   Int32  zvec;
   Int32  zj;
   Int32  gSel;
   Int32  gMinlen;
   Int32* gLimit;
   Int32* gBase;
   Int32* gPerm;

   if (s->state == BZ_X_MAGIC_1) {
      /*initialise the save area*/
      s->save_i           = 0;
      s->save_j           = 0;
      s->save_t           = 0;
      s->save_alphaSize   = 0;
      s->save_nGroups     = 0;
      s->save_nSelectors  = 0;
      s->save_EOB         = 0;
      s->save_groupNo     = 0;
      s->save_groupPos    = 0;
      s->save_nextSym     = 0;
      s->save_nblockMAX   = 0;
      s->save_nblock      = 0;
      s->save_es          = 0;
      s->save_N           = 0;
      s->save_curr        = 0;
      s->save_zt          = 0;
      s->save_zn          = 0;
      s->save_zvec        = 0;
      s->save_zj          = 0;
      s->save_gSel        = 0;
      s->save_gMinlen     = 0;
      s->save_gLimit      = NULL;
      s->save_gBase       = NULL;
      s->save_gPerm       = NULL;
   }

   /*restore from the save area*/
   i           = s->save_i;
   j           = s->save_j;
   t           = s->save_t;
   alphaSize   = s->save_alphaSize;
   nGroups     = s->save_nGroups;
   nSelectors  = s->save_nSelectors;
   EOB         = s->save_EOB;
   groupNo     = s->save_groupNo;
   groupPos    = s->save_groupPos;
   nextSym     = s->save_nextSym;
   nblockMAX   = s->save_nblockMAX;
   nblock      = s->save_nblock;
   es          = s->save_es;
   N           = s->save_N;
   curr        = s->save_curr;
   zt          = s->save_zt;
   zn          = s->save_zn; 
   zvec        = s->save_zvec;
   zj          = s->save_zj;
   gSel        = s->save_gSel;
   gMinlen     = s->save_gMinlen;
   gLimit      = s->save_gLimit;
   gBase       = s->save_gBase;
   gPerm       = s->save_gPerm;

   retVal = BZ_OK;

   switch (s->state) {

      GET_UCHAR(BZ_X_MAGIC_1, uc);
      if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC);

      GET_UCHAR(BZ_X_MAGIC_2, uc);
      if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC);

      GET_UCHAR(BZ_X_MAGIC_3, uc)
      if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC);

      GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8)
      if (s->blockSize100k < (BZ_HDR_0 + 1) || 
          s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC);
      s->blockSize100k -= BZ_HDR_0;

      if (s->smallDecompress) {
         s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) );
         s->ll4  = BZALLOC( 
                      ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar) 
                   );
         if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR);
      } else {
         s->tt  = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) );
         if (s->tt == NULL) RETURN(BZ_MEM_ERROR);
      }

      GET_UCHAR(BZ_X_BLKHDR_1, uc);

      if (uc == 0x17) goto endhdr_2;
      if (uc != 0x31) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_2, uc);
      if (uc != 0x41) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_3, uc);
      if (uc != 0x59) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_4, uc);
      if (uc != 0x26) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_5, uc);
      if (uc != 0x53) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_BLKHDR_6, uc);
      if (uc != 0x59) RETURN(BZ_DATA_ERROR);

      s->currBlockNo++;
      if (s->verbosity >= 2)
         VPrintf1 ( "\n    [%d: huff+mtf ", s->currBlockNo );
 
      s->storedBlockCRC = 0;
      GET_UCHAR(BZ_X_BCRC_1, uc);
      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_BCRC_2, uc);
      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_BCRC_3, uc);
      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_BCRC_4, uc);
      s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);

      GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1);

      s->origPtr = 0;
      GET_UCHAR(BZ_X_ORIGPTR_1, uc);
      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
      GET_UCHAR(BZ_X_ORIGPTR_2, uc);
      s->origPtr = (s->origPtr << 8) | ((Int32)uc);
      GET_UCHAR(BZ_X_ORIGPTR_3, uc);
      s->origPtr = (s->origPtr << 8) | ((Int32)uc);

      if (s->origPtr < 0)
         RETURN(BZ_DATA_ERROR);
      if (s->origPtr > 10 + 100000*s->blockSize100k) 
         RETURN(BZ_DATA_ERROR);

      /*--- Receive the mapping table ---*/
      for (i = 0; i < 16; i++) {
         GET_BIT(BZ_X_MAPPING_1, uc);
         if (uc == 1) 
            s->inUse16[i] = True; else 
            s->inUse16[i] = False;
      }

      for (i = 0; i < 256; i++) s->inUse[i] = False;

      for (i = 0; i < 16; i++)
         if (s->inUse16[i])
            for (j = 0; j < 16; j++) {
               GET_BIT(BZ_X_MAPPING_2, uc);
               if (uc == 1) s->inUse[i * 16 + j] = True;
            }
      makeMaps_d ( s );
      if (s->nInUse == 0) RETURN(BZ_DATA_ERROR);
      alphaSize = s->nInUse+2;

      /*--- Now the selectors ---*/
      GET_BITS(BZ_X_SELECTOR_1, nGroups, 3);
      if (nGroups < 2 || nGroups > BZ_N_GROUPS) RETURN(BZ_DATA_ERROR);
      GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15);
      if (nSelectors < 1) RETURN(BZ_DATA_ERROR);
      for (i = 0; i < nSelectors; i++) {
         j = 0;
         while (True) {
            GET_BIT(BZ_X_SELECTOR_3, uc);
            if (uc == 0) break;
            j++;
            if (j >= nGroups) RETURN(BZ_DATA_ERROR);
         }
         /* Having more than BZ_MAX_SELECTORS doesn't make much sense
            since they will never be used, but some implementations might
            "round up" the number of selectors, so just ignore those. */
         if (i < BZ_MAX_SELECTORS)
           s->selectorMtf[i] = j;
      }
      if (nSelectors > BZ_MAX_SELECTORS)
        nSelectors = BZ_MAX_SELECTORS;

      /*--- Undo the MTF values for the selectors. ---*/
      {
         UChar pos[BZ_N_GROUPS], tmp, v;
         for (v = 0; v < nGroups; v++) pos[v] = v;
   
         for (i = 0; i < nSelectors; i++) {
            v = s->selectorMtf[i];
            tmp = pos[v];
            while (v > 0) { pos[v] = pos[v-1]; v--; }
            pos[0] = tmp;
            s->selector[i] = tmp;
         }
      }

      /*--- Now the coding tables ---*/
      for (t = 0; t < nGroups; t++) {
         GET_BITS(BZ_X_CODING_1, curr, 5);
         for (i = 0; i < alphaSize; i++) {
            while (True) {
               if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR);
               GET_BIT(BZ_X_CODING_2, uc);
               if (uc == 0) break;
               GET_BIT(BZ_X_CODING_3, uc);
               if (uc == 0) curr++; else curr--;
            }
            s->len[t][i] = curr;
         }
      }

      /*--- Create the Huffman decoding tables ---*/
      for (t = 0; t < nGroups; t++) {
         minLen = 32;
         maxLen = 0;
         for (i = 0; i < alphaSize; i++) {
            if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
            if (s->len[t][i] < minLen) minLen = s->len[t][i];
         }
         BZ2_hbCreateDecodeTables ( 
            &(s->limit[t][0]), 
            &(s->base[t][0]), 
            &(s->perm[t][0]), 
            &(s->len[t][0]),
            minLen, maxLen, alphaSize
         );
         s->minLens[t] = minLen;
      }

      /*--- Now the MTF values ---*/

      EOB      = s->nInUse+1;
      nblockMAX = 100000 * s->blockSize100k;
      groupNo  = -1;
      groupPos = 0;

      for (i = 0; i <= 255; i++) s->unzftab[i] = 0;

      /*-- MTF init --*/
      {
         Int32 ii, jj, kk;
         kk = MTFA_SIZE-1;
         for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) {
            for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
               s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj);
               kk--;
            }
            s->mtfbase[ii] = kk + 1;
         }
      }
      /*-- end MTF init --*/

      nblock = 0;
      GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym);

      while (True) {

         if (nextSym == EOB) break;

         if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) {

            es = -1;
            N = 1;
            do {
               /* Check that N doesn't get too big, so that es doesn't
                  go negative.  The maximum value that can be
                  RUNA/RUNB encoded is equal to the block size (post
                  the initial RLE), viz, 900k, so bounding N at 2
                  million should guard against overflow without
                  rejecting any legitimate inputs. */
               if (N >= 2*1024*1024) RETURN(BZ_DATA_ERROR);
               if (nextSym == BZ_RUNA) es = es + (0+1) * N; else
               if (nextSym == BZ_RUNB) es = es + (1+1) * N;
               N = N * 2;
               GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym);
            }
               while (nextSym == BZ_RUNA || nextSym == BZ_RUNB);

            es++;
            uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ];
            s->unzftab[uc] += es;

            if (s->smallDecompress)
               while (es > 0) {
                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
                  s->ll16[nblock] = (UInt16)uc;
                  nblock++;
                  es--;
               }
            else
               while (es > 0) {
                  if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
                  s->tt[nblock] = (UInt32)uc;
                  nblock++;
                  es--;
               };

            continue;

         } else {

            if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);

            /*-- uc = MTF ( nextSym-1 ) --*/
            {
               Int32 ii, jj, kk, pp, lno, off;
               UInt32 nn;
               nn = (UInt32)(nextSym - 1);

               if (nn < MTFL_SIZE) {
                  /* avoid general-case expense */
                  pp = s->mtfbase[0];
                  uc = s->mtfa[pp+nn];
                  while (nn > 3) {
                     Int32 z = pp+nn;
                     s->mtfa[(z)  ] = s->mtfa[(z)-1];
                     s->mtfa[(z)-1] = s->mtfa[(z)-2];
                     s->mtfa[(z)-2] = s->mtfa[(z)-3];
                     s->mtfa[(z)-3] = s->mtfa[(z)-4];
                     nn -= 4;
                  }
                  while (nn > 0) { 
                     s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--; 
                  };
                  s->mtfa[pp] = uc;
               } else { 
                  /* general case */
                  lno = nn / MTFL_SIZE;
                  off = nn % MTFL_SIZE;
                  pp = s->mtfbase[lno] + off;
                  uc = s->mtfa[pp];
                  while (pp > s->mtfbase[lno]) { 
                     s->mtfa[pp] = s->mtfa[pp-1]; pp--; 
                  };
                  s->mtfbase[lno]++;
                  while (lno > 0) {
                     s->mtfbase[lno]--;
                     s->mtfa[s->mtfbase[lno]] 
                        = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1];
                     lno--;
                  }
                  s->mtfbase[0]--;
                  s->mtfa[s->mtfbase[0]] = uc;
                  if (s->mtfbase[0] == 0) {
                     kk = MTFA_SIZE-1;
                     for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) {
                        for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
                           s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj];
                           kk--;
                        }
                        s->mtfbase[ii] = kk + 1;
                     }
                  }
               }
            }
            /*-- end uc = MTF ( nextSym-1 ) --*/

            s->unzftab[s->seqToUnseq[uc]]++;
            if (s->smallDecompress)
               s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else
               s->tt[nblock]   = (UInt32)(s->seqToUnseq[uc]);
            nblock++;

            GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym);
            continue;
         }
      }

      /* Now we know what nblock is, we can do a better sanity
         check on s->origPtr.
      */
      if (s->origPtr < 0 || s->origPtr >= nblock)
         RETURN(BZ_DATA_ERROR);

      /*-- Set up cftab to facilitate generation of T^(-1) --*/
      /* Check: unzftab entries in range. */
      for (i = 0; i <= 255; i++) {
         if (s->unzftab[i] < 0 || s->unzftab[i] > nblock)
            RETURN(BZ_DATA_ERROR);
      }
      /* Actually generate cftab. */
      s->cftab[0] = 0;
      for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1];
      for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1];
      /* Check: cftab entries in range. */
      for (i = 0; i <= 256; i++) {
         if (s->cftab[i] < 0 || s->cftab[i] > nblock) {
            /* s->cftab[i] can legitimately be == nblock */
            RETURN(BZ_DATA_ERROR);
         }
      }
      /* Check: cftab entries non-descending. */
      for (i = 1; i <= 256; i++) {
         if (s->cftab[i-1] > s->cftab[i]) {
            RETURN(BZ_DATA_ERROR);
         }
      }

      s->state_out_len = 0;
      s->state_out_ch  = 0;
      BZ_INITIALISE_CRC ( s->calculatedBlockCRC );
      s->state = BZ_X_OUTPUT;
      if (s->verbosity >= 2) VPrintf0 ( "rt+rld" );

      if (s->smallDecompress) {

         /*-- Make a copy of cftab, used in generation of T --*/
         for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i];

         /*-- compute the T vector --*/
         for (i = 0; i < nblock; i++) {
            uc = (UChar)(s->ll16[i]);
            SET_LL(i, s->cftabCopy[uc]);
            s->cftabCopy[uc]++;
         }

         /*-- Compute T^(-1) by pointer reversal on T --*/
         i = s->origPtr;
         j = GET_LL(i);
         do {
            Int32 tmp = GET_LL(j);
            SET_LL(j, i);
            i = j;
            j = tmp;
         }
            while (i != s->origPtr);

         s->tPos = s->origPtr;
         s->nblock_used = 0;
         if (s->blockRandomised) {
            BZ_RAND_INIT_MASK;
            BZ_GET_SMALL(s->k0); s->nblock_used++;
            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; 
         } else {
            BZ_GET_SMALL(s->k0); s->nblock_used++;
         }

      } else {

         /*-- compute the T^(-1) vector --*/
         for (i = 0; i < nblock; i++) {
            uc = (UChar)(s->tt[i] & 0xff);
            s->tt[s->cftab[uc]] |= (i << 8);
            s->cftab[uc]++;
         }

         s->tPos = s->tt[s->origPtr] >> 8;
         s->nblock_used = 0;
         if (s->blockRandomised) {
            BZ_RAND_INIT_MASK;
            BZ_GET_FAST(s->k0); s->nblock_used++;
            BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; 
         } else {
            BZ_GET_FAST(s->k0); s->nblock_used++;
         }

      }

      RETURN(BZ_OK);



    endhdr_2:

      GET_UCHAR(BZ_X_ENDHDR_2, uc);
      if (uc != 0x72) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_ENDHDR_3, uc);
      if (uc != 0x45) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_ENDHDR_4, uc);
      if (uc != 0x38) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_ENDHDR_5, uc);
      if (uc != 0x50) RETURN(BZ_DATA_ERROR);
      GET_UCHAR(BZ_X_ENDHDR_6, uc);
      if (uc != 0x90) RETURN(BZ_DATA_ERROR);

      s->storedCombinedCRC = 0;
      GET_UCHAR(BZ_X_CCRC_1, uc);
      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_CCRC_2, uc);
      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_CCRC_3, uc);
      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
      GET_UCHAR(BZ_X_CCRC_4, uc);
      s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);

      s->state = BZ_X_IDLE;
      RETURN(BZ_STREAM_END);

      default: AssertH ( False, 4001 );
   }

   AssertH ( False, 4002 );

   save_state_and_return:

   s->save_i           = i;
   s->save_j           = j;
   s->save_t           = t;
   s->save_alphaSize   = alphaSize;
   s->save_nGroups     = nGroups;
   s->save_nSelectors  = nSelectors;
   s->save_EOB         = EOB;
   s->save_groupNo     = groupNo;
   s->save_groupPos    = groupPos;
   s->save_nextSym     = nextSym;
   s->save_nblockMAX   = nblockMAX;
   s->save_nblock      = nblock;
   s->save_es          = es;
   s->save_N           = N;
   s->save_curr        = curr;
   s->save_zt          = zt;
   s->save_zn          = zn;
   s->save_zvec        = zvec;
   s->save_zj          = zj;
   s->save_gSel        = gSel;
   s->save_gMinlen     = gMinlen;
   s->save_gLimit      = gLimit;
   s->save_gBase       = gBase;
   s->save_gPerm       = gPerm;

   return retVal;   
}


/*-------------------------------------------------------------*/
/*--- end                                      decompress.c ---*/
/*-------------------------------------------------------------*/

https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/bzlib.c

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/bzlib.c used in: Step 714
/*-------------------------------------------------------------*/
/*--- Library top-level functions.                          ---*/
/*---                                               bzlib.c ---*/
/*-------------------------------------------------------------*/

/* ------------------------------------------------------------------
   This file is part of bzip2/libbzip2, a program and library for
   lossless, block-sorting data compression.

   bzip2/libbzip2 version 1.0.8 of 13 July 2019
   Copyright (C) 1996-2019 Julian Seward <jseward@acm.org>

   Please read the WARNING, DISCLAIMER and PATENTS sections in the 
   README file.

   This program is released under the terms of the license contained
   in the file LICENSE.
   ------------------------------------------------------------------ */

/* CHANGES
   0.9.0    -- original version.
   0.9.0a/b -- no changes in this file.
   0.9.0c   -- made zero-length BZ_FLUSH work correctly in bzCompress().
     fixed bzWrite/bzRead to ignore zero-length requests.
     fixed bzread to correctly handle read requests after EOF.
     wrong parameter order in call to bzDecompressInit in
     bzBuffToBuffDecompress.  Fixed.
*/

#include "bzlib_private.h"


/*---------------------------------------------------*/
/*--- Compression stuff                           ---*/
/*---------------------------------------------------*/


/*---------------------------------------------------*/
#ifndef BZ_NO_STDIO
void BZ2_bz__AssertH__fail ( int errcode )
{
   fprintf(stderr, 
      "\n\nbzip2/libbzip2: internal error number %d.\n"
      "This is a bug in bzip2/libbzip2, %s.\n"
      "Please report it to: bzip2-devel@sourceware.org.  If this happened\n"
      "when you were using some program which uses libbzip2 as a\n"
      "component, you should also report this bug to the author(s)\n"
      "of that program.  Please make an effort to report this bug;\n"
      "timely and accurate bug reports eventually lead to higher\n"
      "quality software.  Thanks.\n\n",
      errcode,
      BZ2_bzlibVersion()
   );

   if (errcode == 1007) {
   fprintf(stderr,
      "\n*** A special note about internal error number 1007 ***\n"
      "\n"
      "Experience suggests that a common cause of i.e. 1007\n"
      "is unreliable memory or other hardware.  The 1007 assertion\n"
      "just happens to cross-check the results of huge numbers of\n"
      "memory reads/writes, and so acts (unintendedly) as a stress\n"
      "test of your memory system.\n"
      "\n"
      "I suggest the following: try compressing the file again,\n"
      "possibly monitoring progress in detail with the -vv flag.\n"
      "\n"
      "* If the error cannot be reproduced, and/or happens at different\n"
      "  points in compression, you may have a flaky memory system.\n"
      "  Try a memory-test program.  I have used Memtest86\n"
      "  (www.memtest86.com).  At the time of writing it is free (GPLd).\n"
      "  Memtest86 tests memory much more thorougly than your BIOSs\n"
      "  power-on test, and may find failures that the BIOS doesn't.\n"
      "\n"
      "* If the error can be repeatably reproduced, this is a bug in\n"
      "  bzip2, and I would very much like to hear about it.  Please\n"
      "  let me know, and, ideally, save a copy of the file causing the\n"
      "  problem -- without which I will be unable to investigate it.\n"
      "\n"
   );
   }

   exit(3);
}
#endif


/*---------------------------------------------------*/
static
int bz_config_ok ( void )
{
   if (sizeof(int)   != 4) return 0;
   if (sizeof(short) != 2) return 0;
   if (sizeof(char)  != 1) return 0;
   return 1;
}


/*---------------------------------------------------*/
static
void* default_bzalloc ( void* opaque, Int32 items, Int32 size )
{
   void* v = malloc ( items * size );
   return v;
}

static
void default_bzfree ( void* opaque, void* addr )
{
   if (addr != NULL) free ( addr );
}


/*---------------------------------------------------*/
static
void prepare_new_block ( EState* s )
{
   Int32 i;
   s->nblock = 0;
   s->numZ = 0;
   s->state_out_pos = 0;
   BZ_INITIALISE_CRC ( s->blockCRC );
   for (i = 0; i < 256; i++) s->inUse[i] = False;
   s->blockNo++;
}


/*---------------------------------------------------*/
static
void init_RL ( EState* s )
{
   s->state_in_ch  = 256;
   s->state_in_len = 0;
}


static
Bool isempty_RL ( EState* s )
{
   if (s->state_in_ch < 256 && s->state_in_len > 0)
      return False; else
      return True;
}


/*---------------------------------------------------*/
int BZ_API(BZ2_bzCompressInit) 
                    ( bz_stream* strm, 
                     int        blockSize100k,
                     int        verbosity,
                     int        workFactor )
{
   Int32   n;
   EState* s;

   if (!bz_config_ok()) return BZ_CONFIG_ERROR;

   if (strm == NULL || 
       blockSize100k < 1 || blockSize100k > 9 ||
       workFactor < 0 || workFactor > 250)
     return BZ_PARAM_ERROR;

   if (workFactor == 0) workFactor = 30;
   if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc;
   if (strm->bzfree == NULL) strm->bzfree = default_bzfree;

   s = BZALLOC( sizeof(EState) );
   if (s == NULL) return BZ_MEM_ERROR;
   s->strm = strm;

   s->arr1 = NULL;
   s->arr2 = NULL;
   s->ftab = NULL;

   n       = 100000 * blockSize100k;
   s->arr1 = BZALLOC( n                  * sizeof(UInt32) );
   s->arr2 = BZALLOC( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) );
   s->ftab = BZALLOC( 65537              * sizeof(UInt32) );

   if (s->arr1 == NULL || s->arr2 == NULL || s->ftab == NULL) {
      if (s->arr1 != NULL) BZFREE(s->arr1);
      if (s->arr2 != NULL) BZFREE(s->arr2);
      if (s->ftab != NULL) BZFREE(s->ftab);
      if (s       != NULL) BZFREE(s);
      return BZ_MEM_ERROR;
   }

   s->blockNo           = 0;
   s->state             = BZ_S_INPUT;
   s->mode              = BZ_M_RUNNING;
   s->combinedCRC       = 0;
   s->blockSize100k     = blockSize100k;
   s->nblockMAX         = 100000 * blockSize100k - 19;
   s->verbosity         = verbosity;
   s->workFactor        = workFactor;

   s->block             = (UChar*)s->arr2;
   s->mtfv              = (UInt16*)s->arr1;
   s->zbits             = NULL;
   s->ptr               = (UInt32*)s->arr1;

   strm->state          = s;
   strm->total_in_lo32  = 0;
   strm->total_in_hi32  = 0;
   strm->total_out_lo32 = 0;
   strm->total_out_hi32 = 0;
   init_RL ( s );
   prepare_new_block ( s );
   return BZ_OK;
}


/*---------------------------------------------------*/
static
void add_pair_to_block ( EState* s )
{
   Int32 i;
   UChar ch = (UChar)(s->state_in_ch);
   for (i = 0; i < s->state_in_len; i++) {
      BZ_UPDATE_CRC( s->blockCRC, ch );
   }
   s->inUse[s->state_in_ch] = True;
   switch (s->state_in_len) {
      case 1:
         s->block[s->nblock] = (UChar)ch; s->nblock++;
         break;
      case 2:
         s->block[s->nblock] = (UChar)ch; s->nblock++;
         s->block[s->nblock] = (UChar)ch; s->nblock++;
         break;
      case 3:
         s->block[s->nblock] = (UChar)ch; s->nblock++;
         s->block[s->nblock] = (UChar)ch; s->nblock++;
         s->block[s->nblock] = (UChar)ch; s->nblock++;
         break;
      default:
         s->inUse[s->state_in_len-4] = True;
         s->block[s->nblock] = (UChar)ch; s->nblock++;
         s->block[s->nblock] = (UChar)ch; s->nblock++;
         s->block[s->nblock] = (UChar)ch; s->nblock++;
         s->block[s->nblock] = (UChar)ch; s->nblock++;
         s->block[s->nblock] = ((UChar)(s->state_in_len-4));
         s->nblock++;
         break;
   }
}


/*---------------------------------------------------*/
static
void flush_RL ( EState* s )
{
   if (s->state_in_ch < 256) add_pair_to_block ( s );
   init_RL ( s );
}


/*---------------------------------------------------*/
#define ADD_CHAR_TO_BLOCK(zs,zchh0)               \
{                                                 \
   UInt32 zchh = (UInt32)(zchh0);                 \
   /*-- fast track the common case --*/           \
   if (zchh != zs->state_in_ch &&                 \
       zs->state_in_len == 1) {                   \
      UChar ch = (UChar)(zs->state_in_ch);        \
      BZ_UPDATE_CRC( zs->blockCRC, ch );          \
      zs->inUse[zs->state_in_ch] = True;          \
      zs->block[zs->nblock] = (UChar)ch;          \
      zs->nblock++;                               \
      zs->state_in_ch = zchh;                     \
   }                                              \
   else                                           \
   /*-- general, uncommon cases --*/              \
   if (zchh != zs->state_in_ch ||                 \
      zs->state_in_len == 255) {                  \
      if (zs->state_in_ch < 256)                  \
         add_pair_to_block ( zs );                \
      zs->state_in_ch = zchh;                     \
      zs->state_in_len = 1;                       \
   } else {                                       \
      zs->state_in_len++;                         \
   }                                              \
}


/*---------------------------------------------------*/
static
Bool copy_input_until_stop ( EState* s )
{
   Bool progress_in = False;

   if (s->mode == BZ_M_RUNNING) {

      /*-- fast track the common case --*/
      while (True) {
         /*-- block full? --*/
         if (s->nblock >= s->nblockMAX) break;
         /*-- no input? --*/
         if (s->strm->avail_in == 0) break;
         progress_in = True;
         ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); 
         s->strm->next_in++;
         s->strm->avail_in--;
         s->strm->total_in_lo32++;
         if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++;
      }

   } else {

      /*-- general, uncommon case --*/
      while (True) {
         /*-- block full? --*/
         if (s->nblock >= s->nblockMAX) break;
         /*-- no input? --*/
         if (s->strm->avail_in == 0) break;
         /*-- flush/finish end? --*/
         if (s->avail_in_expect == 0) break;
         progress_in = True;
         ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); 
         s->strm->next_in++;
         s->strm->avail_in--;
         s->strm->total_in_lo32++;
         if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++;
         s->avail_in_expect--;
      }
   }
   return progress_in;
}


/*---------------------------------------------------*/
static
Bool copy_output_until_stop ( EState* s )
{
   Bool progress_out = False;

   while (True) {

      /*-- no output space? --*/
      if (s->strm->avail_out == 0) break;

      /*-- block done? --*/
      if (s->state_out_pos >= s->numZ) break;

      progress_out = True;
      *(s->strm->next_out) = s->zbits[s->state_out_pos];
      s->state_out_pos++;
      s->strm->avail_out--;
      s->strm->next_out++;
      s->strm->total_out_lo32++;
      if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
   }

   return progress_out;
}


/*---------------------------------------------------*/
static
Bool handle_compress ( bz_stream* strm )
{
   Bool progress_in  = False;
   Bool progress_out = False;
   EState* s = strm->state;
   
   while (True) {

      if (s->state == BZ_S_OUTPUT) {
         progress_out |= copy_output_until_stop ( s );
         if (s->state_out_pos < s->numZ) break;
         if (s->mode == BZ_M_FINISHING && 
             s->avail_in_expect == 0 &&
             isempty_RL(s)) break;
         prepare_new_block ( s );
         s->state = BZ_S_INPUT;
         if (s->mode == BZ_M_FLUSHING && 
             s->avail_in_expect == 0 &&
             isempty_RL(s)) break;
      }

      if (s->state == BZ_S_INPUT) {
         progress_in |= copy_input_until_stop ( s );
         if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) {
            flush_RL ( s );
            BZ2_compressBlock ( s, (Bool)(s->mode == BZ_M_FINISHING) );
            s->state = BZ_S_OUTPUT;
         }
         else
         if (s->nblock >= s->nblockMAX) {
            BZ2_compressBlock ( s, False );
            s->state = BZ_S_OUTPUT;
         }
         else
         if (s->strm->avail_in == 0) {
            break;
         }
      }

   }

   return progress_in || progress_out;
}


/*---------------------------------------------------*/
int BZ_API(BZ2_bzCompress) ( bz_stream *strm, int action )
{
   Bool progress;
   EState* s;
   if (strm == NULL) return BZ_PARAM_ERROR;
   s = strm->state;
   if (s == NULL) return BZ_PARAM_ERROR;
   if (s->strm != strm) return BZ_PARAM_ERROR;

   preswitch:
   switch (s->mode) {

      case BZ_M_IDLE:
         return BZ_SEQUENCE_ERROR;

      case BZ_M_RUNNING:
         if (action == BZ_RUN) {
            progress = handle_compress ( strm );
            return progress ? BZ_RUN_OK : BZ_PARAM_ERROR;
         } 
         else
     if (action == BZ_FLUSH) {
            s->avail_in_expect = strm->avail_in;
            s->mode = BZ_M_FLUSHING;
            goto preswitch;
         }
         else
         if (action == BZ_FINISH) {
            s->avail_in_expect = strm->avail_in;
            s->mode = BZ_M_FINISHING;
            goto preswitch;
         }
         else 
            return BZ_PARAM_ERROR;

      case BZ_M_FLUSHING:
         if (action != BZ_FLUSH) return BZ_SEQUENCE_ERROR;
         if (s->avail_in_expect != s->strm->avail_in) 
            return BZ_SEQUENCE_ERROR;
         progress = handle_compress ( strm );
         if (s->avail_in_expect > 0 || !isempty_RL(s) ||
             s->state_out_pos < s->numZ) return BZ_FLUSH_OK;
         s->mode = BZ_M_RUNNING;
         return BZ_RUN_OK;

      case BZ_M_FINISHING:
         if (action != BZ_FINISH) return BZ_SEQUENCE_ERROR;
         if (s->avail_in_expect != s->strm->avail_in) 
            return BZ_SEQUENCE_ERROR;
         progress = handle_compress ( strm );
         if (!progress) return BZ_SEQUENCE_ERROR;
         if (s->avail_in_expect > 0 || !isempty_RL(s) ||
             s->state_out_pos < s->numZ) return BZ_FINISH_OK;
         s->mode = BZ_M_IDLE;
         return BZ_STREAM_END;
   }
   return BZ_OK; /*--not reached--*/
}


/*---------------------------------------------------*/
int BZ_API(BZ2_bzCompressEnd)  ( bz_stream *strm )
{
   EState* s;
   if (strm == NULL) return BZ_PARAM_ERROR;
   s = strm->state;
   if (s == NULL) return BZ_PARAM_ERROR;
   if (s->strm != strm) return BZ_PARAM_ERROR;

   if (s->arr1 != NULL) BZFREE(s->arr1);
   if (s->arr2 != NULL) BZFREE(s->arr2);
   if (s->ftab != NULL) BZFREE(s->ftab);
   BZFREE(strm->state);

   strm->state = NULL;   

   return BZ_OK;
}


/*---------------------------------------------------*/
/*--- Decompression stuff                         ---*/
/*---------------------------------------------------*/

/*---------------------------------------------------*/
int BZ_API(BZ2_bzDecompressInit) 
                     ( bz_stream* strm, 
                       int        verbosity,
                       int        small )
{
   DState* s;

   if (!bz_config_ok()) return BZ_CONFIG_ERROR;

   if (strm == NULL) return BZ_PARAM_ERROR;
   if (small != 0 && small != 1) return BZ_PARAM_ERROR;
   if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR;

   if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc;
   if (strm->bzfree == NULL) strm->bzfree = default_bzfree;

   s = BZALLOC( sizeof(DState) );
   if (s == NULL) return BZ_MEM_ERROR;
   s->strm                  = strm;
   strm->state              = s;
   s->state                 = BZ_X_MAGIC_1;
   s->bsLive                = 0;
   s->bsBuff                = 0;
   s->calculatedCombinedCRC = 0;
   strm->total_in_lo32      = 0;
   strm->total_in_hi32      = 0;
   strm->total_out_lo32     = 0;
   strm->total_out_hi32     = 0;
   s->smallDecompress       = (Bool)small;
   s->ll4                   = NULL;
   s->ll16                  = NULL;
   s->tt                    = NULL;
   s->currBlockNo           = 0;
   s->verbosity             = verbosity;

   return BZ_OK;
}


/*---------------------------------------------------*/
/* Return  True iff data corruption is discovered.
   Returns False if there is no problem.
*/
static
Bool unRLE_obuf_to_output_FAST ( DState* s )
{
   UChar k1;

   if (s->blockRandomised) {

      while (True) {
         /* try to finish existing run */
         while (True) {
            if (s->strm->avail_out == 0) return False;
            if (s->state_out_len == 0) break;
            *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
            BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
            s->state_out_len--;
            s->strm->next_out++;
            s->strm->avail_out--;
            s->strm->total_out_lo32++;
            if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
         }

         /* can a new run be started? */
         if (s->nblock_used == s->save_nblock+1) return False;
               
         /* Only caused by corrupt data stream? */
         if (s->nblock_used > s->save_nblock+1)
            return True;
   
         s->state_out_len = 1;
         s->state_out_ch = s->k0;
         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; 
         k1 ^= BZ_RAND_MASK; s->nblock_used++;
         if (s->nblock_used == s->save_nblock+1) continue;
         if (k1 != s->k0) { s->k0 = k1; continue; };
   
         s->state_out_len = 2;
         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; 
         k1 ^= BZ_RAND_MASK; s->nblock_used++;
         if (s->nblock_used == s->save_nblock+1) continue;
         if (k1 != s->k0) { s->k0 = k1; continue; };
   
         s->state_out_len = 3;
         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; 
         k1 ^= BZ_RAND_MASK; s->nblock_used++;
         if (s->nblock_used == s->save_nblock+1) continue;
         if (k1 != s->k0) { s->k0 = k1; continue; };
   
         BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; 
         k1 ^= BZ_RAND_MASK; s->nblock_used++;
         s->state_out_len = ((Int32)k1) + 4;
         BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK; 
         s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
      }

   } else {

      /* restore */
      UInt32        c_calculatedBlockCRC = s->calculatedBlockCRC;
      UChar         c_state_out_ch       = s->state_out_ch;
      Int32         c_state_out_len      = s->state_out_len;
      Int32         c_nblock_used        = s->nblock_used;
      Int32         c_k0                 = s->k0;
      UInt32*       c_tt                 = s->tt;
      UInt32        c_tPos               = s->tPos;
      char*         cs_next_out          = s->strm->next_out;
      unsigned int  cs_avail_out         = s->strm->avail_out;
      Int32         ro_blockSize100k     = s->blockSize100k;
      /* end restore */

      UInt32       avail_out_INIT = cs_avail_out;
      Int32        s_save_nblockPP = s->save_nblock+1;
      unsigned int total_out_lo32_old;

      while (True) {

         /* try to finish existing run */
         if (c_state_out_len > 0) {
            while (True) {
               if (cs_avail_out == 0) goto return_notr;
               if (c_state_out_len == 1) break;
               *( (UChar*)(cs_next_out) ) = c_state_out_ch;
               BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
               c_state_out_len--;
               cs_next_out++;
               cs_avail_out--;
            }
            s_state_out_len_eq_one:
            {
               if (cs_avail_out == 0) { 
                  c_state_out_len = 1; goto return_notr;
               };
               *( (UChar*)(cs_next_out) ) = c_state_out_ch;
               BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
               cs_next_out++;
               cs_avail_out--;
            }
         }   
         /* Only caused by corrupt data stream? */
         if (c_nblock_used > s_save_nblockPP)
            return True;

         /* can a new run be started? */
         if (c_nblock_used == s_save_nblockPP) {
            c_state_out_len = 0; goto return_notr;
         };   
         c_state_out_ch = c_k0;
         BZ_GET_FAST_C(k1); c_nblock_used++;
         if (k1 != c_k0) { 
            c_k0 = k1; goto s_state_out_len_eq_one; 
         };
         if (c_nblock_used == s_save_nblockPP) 
            goto s_state_out_len_eq_one;
   
         c_state_out_len = 2;
         BZ_GET_FAST_C(k1); c_nblock_used++;
         if (c_nblock_used == s_save_nblockPP) continue;
         if (k1 != c_k0) { c_k0 = k1; continue; };
   
         c_state_out_len = 3;
         BZ_GET_FAST_C(k1); c_nblock_used++;
         if (c_nblock_used == s_save_nblockPP) continue;
         if (k1 != c_k0) { c_k0 = k1; continue; };
   
         BZ_GET_FAST_C(k1); c_nblock_used++;
         c_state_out_len = ((Int32)k1) + 4;
         BZ_GET_FAST_C(c_k0); c_nblock_used++;
      }

      return_notr:
      total_out_lo32_old = s->strm->total_out_lo32;
      s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out);
      if (s->strm->total_out_lo32 < total_out_lo32_old)
         s->strm->total_out_hi32++;

      /* save */
      s->calculatedBlockCRC = c_calculatedBlockCRC;
      s->state_out_ch       = c_state_out_ch;
      s->state_out_len      = c_state_out_len;
      s->nblock_used        = c_nblock_used;
      s->k0                 = c_k0;
      s->tt                 = c_tt;
      s->tPos               = c_tPos;
      s->strm->next_out     = cs_next_out;
      s->strm->avail_out    = cs_avail_out;
      /* end save */
   }
   return False;
}



/*---------------------------------------------------*/
__inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab )
{
   Int32 nb, na, mid;
   nb = 0;
   na = 256;
   do {
      mid = (nb + na) >> 1;
      if (indx >= cftab[mid]) nb = mid; else na = mid;
   }
   while (na - nb != 1);
   return nb;
}


/*---------------------------------------------------*/
/* Return  True iff data corruption is discovered.
   Returns False if there is no problem.
*/
static
Bool unRLE_obuf_to_output_SMALL ( DState* s )
{
   UChar k1;

   if (s->blockRandomised) {

      while (True) {
         /* try to finish existing run */
         while (True) {
            if (s->strm->avail_out == 0) return False;
            if (s->state_out_len == 0) break;
            *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
            BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
            s->state_out_len--;
            s->strm->next_out++;
            s->strm->avail_out--;
            s->strm->total_out_lo32++;
            if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
         }
   
         /* can a new run be started? */
         if (s->nblock_used == s->save_nblock+1) return False;

         /* Only caused by corrupt data stream? */
         if (s->nblock_used > s->save_nblock+1)
            return True;
   
         s->state_out_len = 1;
         s->state_out_ch = s->k0;
         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; 
         k1 ^= BZ_RAND_MASK; s->nblock_used++;
         if (s->nblock_used == s->save_nblock+1) continue;
         if (k1 != s->k0) { s->k0 = k1; continue; };
   
         s->state_out_len = 2;
         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; 
         k1 ^= BZ_RAND_MASK; s->nblock_used++;
         if (s->nblock_used == s->save_nblock+1) continue;
         if (k1 != s->k0) { s->k0 = k1; continue; };
   
         s->state_out_len = 3;
         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; 
         k1 ^= BZ_RAND_MASK; s->nblock_used++;
         if (s->nblock_used == s->save_nblock+1) continue;
         if (k1 != s->k0) { s->k0 = k1; continue; };
   
         BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; 
         k1 ^= BZ_RAND_MASK; s->nblock_used++;
         s->state_out_len = ((Int32)k1) + 4;
         BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK; 
         s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
      }

   } else {

      while (True) {
         /* try to finish existing run */
         while (True) {
            if (s->strm->avail_out == 0) return False;
            if (s->state_out_len == 0) break;
            *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
            BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
            s->state_out_len--;
            s->strm->next_out++;
            s->strm->avail_out--;
            s->strm->total_out_lo32++;
            if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
         }
   
         /* can a new run be started? */
         if (s->nblock_used == s->save_nblock+1) return False;

         /* Only caused by corrupt data stream? */
         if (s->nblock_used > s->save_nblock+1)
            return True;
   
         s->state_out_len = 1;
         s->state_out_ch = s->k0;
         BZ_GET_SMALL(k1); s->nblock_used++;
         if (s->nblock_used == s->save_nblock+1) continue;
         if (k1 != s->k0) { s->k0 = k1; continue; };
   
         s->state_out_len = 2;
         BZ_GET_SMALL(k1); s->nblock_used++;
         if (s->nblock_used == s->save_nblock+1) continue;
         if (k1 != s->k0) { s->k0 = k1; continue; };
   
         s->state_out_len = 3;
         BZ_GET_SMALL(k1); s->nblock_used++;
         if (s->nblock_used == s->save_nblock+1) continue;
         if (k1 != s->k0) { s->k0 = k1; continue; };
   
         BZ_GET_SMALL(k1); s->nblock_used++;
         s->state_out_len = ((Int32)k1) + 4;
         BZ_GET_SMALL(s->k0); s->nblock_used++;
      }

   }
}


/*---------------------------------------------------*/
int BZ_API(BZ2_bzDecompress) ( bz_stream *strm )
{
   Bool    corrupt;
   DState* s;
   if (strm == NULL) return BZ_PARAM_ERROR;
   s = strm->state;
   if (s == NULL) return BZ_PARAM_ERROR;
   if (s->strm != strm) return BZ_PARAM_ERROR;

   while (True) {
      if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR;
      if (s->state == BZ_X_OUTPUT) {
         if (s->smallDecompress)
            corrupt = unRLE_obuf_to_output_SMALL ( s ); else
            corrupt = unRLE_obuf_to_output_FAST  ( s );
         if (corrupt) return BZ_DATA_ERROR;
         if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) {
            BZ_FINALISE_CRC ( s->calculatedBlockCRC );
            if (s->verbosity >= 3) 
               VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC, 
                          s->calculatedBlockCRC );
            if (s->verbosity >= 2) VPrintf0 ( "]" );
            if (s->calculatedBlockCRC != s->storedBlockCRC)
               return BZ_DATA_ERROR;
            s->calculatedCombinedCRC 
               = (s->calculatedCombinedCRC << 1) | 
                    (s->calculatedCombinedCRC >> 31);
            s->calculatedCombinedCRC ^= s->calculatedBlockCRC;
            s->state = BZ_X_BLKHDR_1;
         } else {
            return BZ_OK;
         }
      }
      if (s->state >= BZ_X_MAGIC_1) {
         Int32 r = BZ2_decompress ( s );
         if (r == BZ_STREAM_END) {
            if (s->verbosity >= 3)
               VPrintf2 ( "\n    combined CRCs: stored = 0x%08x, computed = 0x%08x", 
                          s->storedCombinedCRC, s->calculatedCombinedCRC );
            if (s->calculatedCombinedCRC != s->storedCombinedCRC)
               return BZ_DATA_ERROR;
            return r;
         }
         if (s->state != BZ_X_OUTPUT) return r;
      }
   }

   AssertH ( 0, 6001 );

   return 0;  /*NOTREACHED*/
}


/*---------------------------------------------------*/
int BZ_API(BZ2_bzDecompressEnd)  ( bz_stream *strm )
{
   DState* s;
   if (strm == NULL) return BZ_PARAM_ERROR;
   s = strm->state;
   if (s == NULL) return BZ_PARAM_ERROR;
   if (s->strm != strm) return BZ_PARAM_ERROR;

   if (s->tt   != NULL) BZFREE(s->tt);
   if (s->ll16 != NULL) BZFREE(s->ll16);
   if (s->ll4  != NULL) BZFREE(s->ll4);

   BZFREE(strm->state);
   strm->state = NULL;

   return BZ_OK;
}


#ifndef BZ_NO_STDIO
/*---------------------------------------------------*/
/*--- File I/O stuff                              ---*/
/*---------------------------------------------------*/

#define BZ_SETERR(eee)                    \
{                                         \
   if (bzerror != NULL) *bzerror = eee;   \
   if (bzf != NULL) bzf->lastErr = eee;   \
}

typedef 
   struct {
      FILE*     handle;
      Char      buf[BZ_MAX_UNUSED];
      Int32     bufN;
      Bool      writing;
      bz_stream strm;
      Int32     lastErr;
      Bool      initialisedOk;
   }
   bzFile;


/*---------------------------------------------*/
static Bool myfeof ( FILE* f )
{
   Int32 c = fgetc ( f );
   if (c == EOF) return True;
   ungetc ( c, f );
   return False;
}


/*---------------------------------------------------*/
BZFILE* BZ_API(BZ2_bzWriteOpen) 
                    ( int*  bzerror,      
                      FILE* f, 
                      int   blockSize100k, 
                      int   verbosity,
                      int   workFactor )
{
   Int32   ret;
   bzFile* bzf = NULL;

   BZ_SETERR(BZ_OK);

   if (f == NULL ||
       (blockSize100k < 1 || blockSize100k > 9) ||
       (workFactor < 0 || workFactor > 250) ||
       (verbosity < 0 || verbosity > 4))
      { BZ_SETERR(BZ_PARAM_ERROR); return NULL; };

   if (ferror(f))
      { BZ_SETERR(BZ_IO_ERROR); return NULL; };

   bzf = malloc ( sizeof(bzFile) );
   if (bzf == NULL)
      { BZ_SETERR(BZ_MEM_ERROR); return NULL; };

   BZ_SETERR(BZ_OK);
   bzf->initialisedOk = False;
   bzf->bufN          = 0;
   bzf->handle        = f;
   bzf->writing       = True;
   bzf->strm.bzalloc  = NULL;
   bzf->strm.bzfree   = NULL;
   bzf->strm.opaque   = NULL;

   if (workFactor == 0) workFactor = 30;
   ret = BZ2_bzCompressInit ( &(bzf->strm), blockSize100k, 
                              verbosity, workFactor );
   if (ret != BZ_OK)
      { BZ_SETERR(ret); free(bzf); return NULL; };

   bzf->strm.avail_in = 0;
   bzf->initialisedOk = True;
   return bzf;   
}



/*---------------------------------------------------*/
void BZ_API(BZ2_bzWrite)
             ( int*    bzerror, 
               BZFILE* b, 
               void*   buf, 
               int     len )
{
   Int32 n, n2, ret;
   bzFile* bzf = (bzFile*)b;

   BZ_SETERR(BZ_OK);
   if (bzf == NULL || buf == NULL || len < 0)
      { BZ_SETERR(BZ_PARAM_ERROR); return; };
   if (!(bzf->writing))
      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
   if (ferror(bzf->handle))
      { BZ_SETERR(BZ_IO_ERROR); return; };

   if (len == 0)
      { BZ_SETERR(BZ_OK); return; };

   bzf->strm.avail_in = len;
   bzf->strm.next_in  = buf;

   while (True) {
      bzf->strm.avail_out = BZ_MAX_UNUSED;
      bzf->strm.next_out = bzf->buf;
      ret = BZ2_bzCompress ( &(bzf->strm), BZ_RUN );
      if (ret != BZ_RUN_OK)
         { BZ_SETERR(ret); return; };

      if (bzf->strm.avail_out < BZ_MAX_UNUSED) {
         n = BZ_MAX_UNUSED - bzf->strm.avail_out;
         n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), 
                       n, bzf->handle );
         if (n != n2 || ferror(bzf->handle))
            { BZ_SETERR(BZ_IO_ERROR); return; };
      }

      if (bzf->strm.avail_in == 0)
         { BZ_SETERR(BZ_OK); return; };
   }
}


/*---------------------------------------------------*/
void BZ_API(BZ2_bzWriteClose)
                  ( int*          bzerror, 
                    BZFILE*       b, 
                    int           abandon,
                    unsigned int* nbytes_in,
                    unsigned int* nbytes_out )
{
   BZ2_bzWriteClose64 ( bzerror, b, abandon, 
                        nbytes_in, NULL, nbytes_out, NULL );
}


void BZ_API(BZ2_bzWriteClose64)
                  ( int*          bzerror, 
                    BZFILE*       b, 
                    int           abandon,
                    unsigned int* nbytes_in_lo32,
                    unsigned int* nbytes_in_hi32,
                    unsigned int* nbytes_out_lo32,
                    unsigned int* nbytes_out_hi32 )
{
   Int32   n, n2, ret;
   bzFile* bzf = (bzFile*)b;

   if (bzf == NULL)
      { BZ_SETERR(BZ_OK); return; };
   if (!(bzf->writing))
      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
   if (ferror(bzf->handle))
      { BZ_SETERR(BZ_IO_ERROR); return; };

   if (nbytes_in_lo32 != NULL) *nbytes_in_lo32 = 0;
   if (nbytes_in_hi32 != NULL) *nbytes_in_hi32 = 0;
   if (nbytes_out_lo32 != NULL) *nbytes_out_lo32 = 0;
   if (nbytes_out_hi32 != NULL) *nbytes_out_hi32 = 0;

   if ((!abandon) && bzf->lastErr == BZ_OK) {
      while (True) {
         bzf->strm.avail_out = BZ_MAX_UNUSED;
         bzf->strm.next_out = bzf->buf;
         ret = BZ2_bzCompress ( &(bzf->strm), BZ_FINISH );
         if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END)
            { BZ_SETERR(ret); return; };

         if (bzf->strm.avail_out < BZ_MAX_UNUSED) {
            n = BZ_MAX_UNUSED - bzf->strm.avail_out;
            n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), 
                          n, bzf->handle );
            if (n != n2 || ferror(bzf->handle))
               { BZ_SETERR(BZ_IO_ERROR); return; };
         }

         if (ret == BZ_STREAM_END) break;
      }
   }

   if ( !abandon && !ferror ( bzf->handle ) ) {
      fflush ( bzf->handle );
      if (ferror(bzf->handle))
         { BZ_SETERR(BZ_IO_ERROR); return; };
   }

   if (nbytes_in_lo32 != NULL)
      *nbytes_in_lo32 = bzf->strm.total_in_lo32;
   if (nbytes_in_hi32 != NULL)
      *nbytes_in_hi32 = bzf->strm.total_in_hi32;
   if (nbytes_out_lo32 != NULL)
      *nbytes_out_lo32 = bzf->strm.total_out_lo32;
   if (nbytes_out_hi32 != NULL)
      *nbytes_out_hi32 = bzf->strm.total_out_hi32;

   BZ_SETERR(BZ_OK);
   BZ2_bzCompressEnd ( &(bzf->strm) );
   free ( bzf );
}


/*---------------------------------------------------*/
BZFILE* BZ_API(BZ2_bzReadOpen) 
                   ( int*  bzerror, 
                     FILE* f, 
                     int   verbosity,
                     int   small,
                     void* unused,
                     int   nUnused )
{
   bzFile* bzf = NULL;
   int     ret;

   BZ_SETERR(BZ_OK);

   if (f == NULL || 
       (small != 0 && small != 1) ||
       (verbosity < 0 || verbosity > 4) ||
       (unused == NULL && nUnused != 0) ||
       (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED)))
      { BZ_SETERR(BZ_PARAM_ERROR); return NULL; };

   if (ferror(f))
      { BZ_SETERR(BZ_IO_ERROR); return NULL; };

   bzf = malloc ( sizeof(bzFile) );
   if (bzf == NULL) 
      { BZ_SETERR(BZ_MEM_ERROR); return NULL; };

   BZ_SETERR(BZ_OK);

   bzf->initialisedOk = False;
   bzf->handle        = f;
   bzf->bufN          = 0;
   bzf->writing       = False;
   bzf->strm.bzalloc  = NULL;
   bzf->strm.bzfree   = NULL;
   bzf->strm.opaque   = NULL;
   
   while (nUnused > 0) {
      bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++;
      unused = ((void*)( 1 + ((UChar*)(unused))  ));
      nUnused--;
   }

   ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small );
   if (ret != BZ_OK)
      { BZ_SETERR(ret); free(bzf); return NULL; };

   bzf->strm.avail_in = bzf->bufN;
   bzf->strm.next_in  = bzf->buf;

   bzf->initialisedOk = True;
   return bzf;   
}


/*---------------------------------------------------*/
void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b )
{
   bzFile* bzf = (bzFile*)b;

   BZ_SETERR(BZ_OK);
   if (bzf == NULL)
      { BZ_SETERR(BZ_OK); return; };

   if (bzf->writing)
      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };

   if (bzf->initialisedOk)
      (void)BZ2_bzDecompressEnd ( &(bzf->strm) );
   free ( bzf );
}


/*---------------------------------------------------*/
int BZ_API(BZ2_bzRead) 
           ( int*    bzerror, 
             BZFILE* b, 
             void*   buf, 
             int     len )
{
   Int32   n, ret;
   bzFile* bzf = (bzFile*)b;

   BZ_SETERR(BZ_OK);

   if (bzf == NULL || buf == NULL || len < 0)
      { BZ_SETERR(BZ_PARAM_ERROR); return 0; };

   if (bzf->writing)
      { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; };

   if (len == 0)
      { BZ_SETERR(BZ_OK); return 0; };

   bzf->strm.avail_out = len;
   bzf->strm.next_out = buf;

   while (True) {

      if (ferror(bzf->handle)) 
         { BZ_SETERR(BZ_IO_ERROR); return 0; };

      if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) {
         n = fread ( bzf->buf, sizeof(UChar), 
                     BZ_MAX_UNUSED, bzf->handle );
         if (ferror(bzf->handle))
            { BZ_SETERR(BZ_IO_ERROR); return 0; };
         bzf->bufN = n;
         bzf->strm.avail_in = bzf->bufN;
         bzf->strm.next_in = bzf->buf;
      }

      ret = BZ2_bzDecompress ( &(bzf->strm) );

      if (ret != BZ_OK && ret != BZ_STREAM_END)
         { BZ_SETERR(ret); return 0; };

      if (ret == BZ_OK && myfeof(bzf->handle) && 
          bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0)
         { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; };

      if (ret == BZ_STREAM_END)
         { BZ_SETERR(BZ_STREAM_END);
           return len - bzf->strm.avail_out; };
      if (bzf->strm.avail_out == 0)
         { BZ_SETERR(BZ_OK); return len; };
      
   }

   return 0; /*not reached*/
}


/*---------------------------------------------------*/
void BZ_API(BZ2_bzReadGetUnused) 
                     ( int*    bzerror, 
                       BZFILE* b, 
                       void**  unused, 
                       int*    nUnused )
{
   bzFile* bzf = (bzFile*)b;
   if (bzf == NULL)
      { BZ_SETERR(BZ_PARAM_ERROR); return; };
   if (bzf->lastErr != BZ_STREAM_END)
      { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
   if (unused == NULL || nUnused == NULL)
      { BZ_SETERR(BZ_PARAM_ERROR); return; };

   BZ_SETERR(BZ_OK);
   *nUnused = bzf->strm.avail_in;
   *unused = bzf->strm.next_in;
}
#endif


/*---------------------------------------------------*/
/*--- Misc convenience stuff                      ---*/
/*---------------------------------------------------*/

/*---------------------------------------------------*/
int BZ_API(BZ2_bzBuffToBuffCompress) 
                         ( char*         dest, 
                           unsigned int* destLen,
                           char*         source, 
                           unsigned int  sourceLen,
                           int           blockSize100k, 
                           int           verbosity, 
                           int           workFactor )
{
   bz_stream strm;
   int ret;

   if (dest == NULL || destLen == NULL || 
       source == NULL ||
       blockSize100k < 1 || blockSize100k > 9 ||
       verbosity < 0 || verbosity > 4 ||
       workFactor < 0 || workFactor > 250) 
      return BZ_PARAM_ERROR;

   if (workFactor == 0) workFactor = 30;
   strm.bzalloc = NULL;
   strm.bzfree = NULL;
   strm.opaque = NULL;
   ret = BZ2_bzCompressInit ( &strm, blockSize100k, 
                              verbosity, workFactor );
   if (ret != BZ_OK) return ret;

   strm.next_in = source;
   strm.next_out = dest;
   strm.avail_in = sourceLen;
   strm.avail_out = *destLen;

   ret = BZ2_bzCompress ( &strm, BZ_FINISH );
   if (ret == BZ_FINISH_OK) goto output_overflow;
   if (ret != BZ_STREAM_END) goto errhandler;

   /* normal termination */
   *destLen -= strm.avail_out;   
   BZ2_bzCompressEnd ( &strm );
   return BZ_OK;

   output_overflow:
   BZ2_bzCompressEnd ( &strm );
   return BZ_OUTBUFF_FULL;

   errhandler:
   BZ2_bzCompressEnd ( &strm );
   return ret;
}


/*---------------------------------------------------*/
int BZ_API(BZ2_bzBuffToBuffDecompress) 
                           ( char*         dest, 
                             unsigned int* destLen,
                             char*         source, 
                             unsigned int  sourceLen,
                             int           small,
                             int           verbosity )
{
   bz_stream strm;
   int ret;

   if (dest == NULL || destLen == NULL || 
       source == NULL ||
       (small != 0 && small != 1) ||
       verbosity < 0 || verbosity > 4) 
          return BZ_PARAM_ERROR;

   strm.bzalloc = NULL;
   strm.bzfree = NULL;
   strm.opaque = NULL;
   ret = BZ2_bzDecompressInit ( &strm, verbosity, small );
   if (ret != BZ_OK) return ret;

   strm.next_in = source;
   strm.next_out = dest;
   strm.avail_in = sourceLen;
   strm.avail_out = *destLen;

   ret = BZ2_bzDecompress ( &strm );
   if (ret == BZ_OK) goto output_overflow_or_eof;
   if (ret != BZ_STREAM_END) goto errhandler;

   /* normal termination */
   *destLen -= strm.avail_out;
   BZ2_bzDecompressEnd ( &strm );
   return BZ_OK;

   output_overflow_or_eof:
   if (strm.avail_out > 0) {
      BZ2_bzDecompressEnd ( &strm );
      return BZ_UNEXPECTED_EOF;
   } else {
      BZ2_bzDecompressEnd ( &strm );
      return BZ_OUTBUFF_FULL;
   };      

   errhandler:
   BZ2_bzDecompressEnd ( &strm );
   return ret; 
}


/*---------------------------------------------------*/
/*--
   Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp)
   to support better zlib compatibility.
   This code is not _officially_ part of libbzip2 (yet);
   I haven't tested it, documented it, or considered the
   threading-safeness of it.
   If this code breaks, please contact both Yoshioka and me.
--*/
/*---------------------------------------------------*/

/*---------------------------------------------------*/
/*--
   return version like "0.9.5d, 4-Sept-1999".
--*/
const char * BZ_API(BZ2_bzlibVersion)(void)
{
   return BZ_VERSION;
}


#ifndef BZ_NO_STDIO
/*---------------------------------------------------*/

#if defined(_WIN32) || defined(OS2) || defined(MSDOS)
#   include <fcntl.h>
#   include <io.h>
#   define SET_BINARY_MODE(file) setmode(fileno(file),O_BINARY)
#else
#   define SET_BINARY_MODE(file)
#endif
static
BZFILE * bzopen_or_bzdopen
               ( const char *path,   /* no use when bzdopen */
                 int fd,             /* no use when bzdopen */
                 const char *mode,
                 int open_mode)      /* bzopen: 0, bzdopen:1 */
{
   int    bzerr;
   char   unused[BZ_MAX_UNUSED];
   int    blockSize100k = 9;
   int    writing       = 0;
   char   mode2[10]     = "";
   FILE   *fp           = NULL;
   BZFILE *bzfp         = NULL;
   int    verbosity     = 0;
   int    workFactor    = 30;
   int    smallMode     = 0;
   int    nUnused       = 0; 

   if (mode == NULL) return NULL;
   while (*mode) {
      switch (*mode) {
      case 'r':
         writing = 0; break;
      case 'w':
         writing = 1; break;
      case 's':
         smallMode = 1; break;
      default:
         if (isdigit((int)(*mode))) {
            blockSize100k = *mode-BZ_HDR_0;
         }
      }
      mode++;
   }
   strcat(mode2, writing ? "w" : "r" );
   strcat(mode2,"b");   /* binary mode */

   if (open_mode==0) {
      if (path==NULL || strcmp(path,"")==0) {
        fp = (writing ? stdout : stdin);
        SET_BINARY_MODE(fp);
      } else {
        fp = fopen(path,mode2);
      }
   } else {
#ifdef BZ_STRICT_ANSI
      fp = NULL;
#else
      fp = fdopen(fd,mode2);
#endif
   }
   if (fp == NULL) return NULL;

   if (writing) {
      /* Guard against total chaos and anarchy -- JRS */
      if (blockSize100k < 1) blockSize100k = 1;
      if (blockSize100k > 9) blockSize100k = 9; 
      bzfp = BZ2_bzWriteOpen(&bzerr,fp,blockSize100k,
                             verbosity,workFactor);
   } else {
      bzfp = BZ2_bzReadOpen(&bzerr,fp,verbosity,smallMode,
                            unused,nUnused);
   }
   if (bzfp == NULL) {
      if (fp != stdin && fp != stdout) fclose(fp);
      return NULL;
   }
   return bzfp;
}


/*---------------------------------------------------*/
/*--
   open file for read or write.
      ex) bzopen("file","w9")
      case path="" or NULL => use stdin or stdout.
--*/
BZFILE * BZ_API(BZ2_bzopen)
               ( const char *path,
                 const char *mode )
{
   return bzopen_or_bzdopen(path,-1,mode,/*bzopen*/0);
}


/*---------------------------------------------------*/
BZFILE * BZ_API(BZ2_bzdopen)
               ( int fd,
                 const char *mode )
{
   return bzopen_or_bzdopen(NULL,fd,mode,/*bzdopen*/1);
}


/*---------------------------------------------------*/
int BZ_API(BZ2_bzread) (BZFILE* b, void* buf, int len )
{
   int bzerr, nread;
   if (((bzFile*)b)->lastErr == BZ_STREAM_END) return 0;
   nread = BZ2_bzRead(&bzerr,b,buf,len);
   if (bzerr == BZ_OK || bzerr == BZ_STREAM_END) {
      return nread;
   } else {
      return -1;
   }
}


/*---------------------------------------------------*/
int BZ_API(BZ2_bzwrite) (BZFILE* b, void* buf, int len )
{
   int bzerr;

   BZ2_bzWrite(&bzerr,b,buf,len);
   if(bzerr == BZ_OK){
      return len;
   }else{
      return -1;
   }
}


/*---------------------------------------------------*/
int BZ_API(BZ2_bzflush) (BZFILE *b)
{
   /* do nothing now... */
   return 0;
}


/*---------------------------------------------------*/
void BZ_API(BZ2_bzclose) (BZFILE* b)
{
   int bzerr;
   FILE *fp;
   
   if (b==NULL) {return;}
   fp = ((bzFile *)b)->handle;
   if(((bzFile*)b)->writing){
      BZ2_bzWriteClose(&bzerr,b,0,NULL,NULL);
      if(bzerr != BZ_OK){
         BZ2_bzWriteClose(NULL,b,1,NULL,NULL);
      }
   }else{
      BZ2_bzReadClose(&bzerr,b);
   }
   if(fp!=stdin && fp!=stdout){
      fclose(fp);
   }
}


/*---------------------------------------------------*/
/*--
   return last error code 
--*/
static const char *bzerrorstrings[] = {
       "OK"
      ,"SEQUENCE_ERROR"
      ,"PARAM_ERROR"
      ,"MEM_ERROR"
      ,"DATA_ERROR"
      ,"DATA_ERROR_MAGIC"
      ,"IO_ERROR"
      ,"UNEXPECTED_EOF"
      ,"OUTBUFF_FULL"
      ,"CONFIG_ERROR"
      ,"???"   /* for future */
      ,"???"   /* for future */
      ,"???"   /* for future */
      ,"???"   /* for future */
      ,"???"   /* for future */
      ,"???"   /* for future */
};


const char * BZ_API(BZ2_bzerror) (BZFILE *b, int *errnum)
{
   int err = ((bzFile *)b)->lastErr;

   if(err>0) err = 0;
   *errnum = err;
   return bzerrorstrings[err*-1];
}
#endif


/*-------------------------------------------------------------*/
/*--- end                                           bzlib.c ---*/
/*-------------------------------------------------------------*/

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bzip2-1.0.8/bzip2-1.0.8.checksums

File: ../git/live-bootstrap/sysa/bzip2-1.0.8/bzip2-1.0.8.checksums used in: Step 725
532b2455e0b0c6fd3ef067776a61ebd0620437d1f55145ce56ce1ddad23844c5  /usr/bin/bzip2

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/sources

File: ../git/live-bootstrap/sysa/coreutils-5.0/sources used in: Step 727
https://mirrors.kernel.org/gnu/coreutils/coreutils-5.0.tar.bz2 c25b36b8af6e0ad2a875daf4d6196bd0df28a62be7dd252e5f99a4d5d7288d95

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/mk/main.mk

File: ../git/live-bootstrap/sysa/coreutils-5.0/mk/main.mk used in: Step 733
# SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
# SPDX-FileCopyrightText: 2021 Paul Dersey <pdersey@gmail.com>
# SPDX-FileCopyrightText: 2023 Emily Trau <emily@downunderctf.com>
#
# SPDX-License-Identifier: GPL-3.0-or-later

PACKAGE=coreutils
PACKAGE_NAME=GNU\ coreutils
PACKAGE_BUGREPORT=bug-coreutils@gnu.org
PACKAGE_VERSION=5.0
VERSION=5.0

CC      = tcc
LD      = tcc
AR      = tcc -ar
LDFLAGS = -static

bindir=$(PREFIX)/bin

CFLAGS  = -I . -I lib \
          -DPACKAGE=\"$(PACKAGE)\" \
          -DPACKAGE_NAME=\"$(PACKAGE_NAME)\" \
          -DGNU_PACKAGE=\"$(PACKAGE_NAME)\" \
          -DPACKAGE_BUGREPORT=\"$(PACKAGE_BUGREPORT)\" \
          -DPACKAGE_VERSION=\"$(PACKAGE_VERSION)\" \
          -DVERSION=\"$(VERSION)\" \
          -DHAVE_LIMITS_H=1 \
          -DHAVE_DECL_FREE=1 \
          -DHAVE_DECL_MALLOC=1 \
          -DHAVE_MALLOC=1 \
          -DHAVE_STDLIB_H=1 \
          -DHAVE_REALLOC=1 \
          -DHAVE_DECL_REALLOC=1 \
          -DHAVE_DECL_GETENV=1 \
          -DHAVE_DIRENT_H=1 \
          -DHAVE_DECL___FPENDING=0 \
          -DSTDC_HEADERS=1 \
          -DHAVE_ALLOCA_H=1 \
          -DHAVE_STRUCT_TIMESPEC=1 \
          -DHAVE_STRING_H=1 \
          -DHAVE_SYS_TIME_H=1 \
          -DTIME_WITH_SYS_TIME=1 \
          -DHAVE_STDINT_H=1 \
          -DMB_LEN_MAX=16 \
          -DLIBDIR=\"$(PREFIX)/lib/mes\" \
          -DHAVE_DECL_WCWIDTH=0 \
          -DHAVE_SYS_STAT_H=1 \
          -DHAVE_INTTYPES_H=1 \
          -DHAVE_DECL_MEMCHR=1 \
          -DHAVE_MEMORY_H=1 \
          -DPENDING_OUTPUT_N_BYTES=1 \
          -DCHAR_MIN=0 \
          -DLOCALEDIR=NULL \
          -DHAVE_FCNTL_H=1 \
          -DEPERM=1 \
          -DHAVE_DECL_STRTOUL=1 \
          -DHAVE_DECL_STRTOULL=1 \
          -DHAVE_DECL_STRTOL=1 \
          -DHAVE_DECL_STRTOLL=1 \
          -DHAVE_RMDIR=1 \
          -DRMDIR_ERRNO_NOT_EMPTY=39 \
          -DHAVE_DECL_FREE=1 \
          -DENOTEMPTY=1 \
          -DLSTAT_FOLLOWS_SLASHED_SYMLINK=1 \
          -DHAVE_DECL_DIRFD=0 \
          -DLC_TIME=\"C\" \
          -DLC_COLLATE=\"C\" \
          -DHAVE_GETCWD=1 \
          -Dmy_strftime=nstrftime \
          -Dmkstemp=rpl_mkstemp \
          -DDIR_TO_FD\(Dir_p\)=-1 \
          -DUTILS_OPEN_MAX=1000 \
          -Dmajor_t=unsigned \
          -Dminor_t=unsigned

.PHONY: all install

SRC_DIR=src

COREUTILS = basename cat chmod cksum csplit cut dirname echo expand expr factor false fmt fold head hostname id join kill link ln logname mkfifo mkdir mknod nl od paste pathchk pr printf ptx pwd readlink rmdir seq sleep sort split sum tail tee tr tsort unexpand uniq unlink wc whoami tac test touch true yes

BINARIES = $(addprefix $(SRC_DIR)/, $(COREUTILS))

ALL=$(BINARIES) $(SRC_DIR)/cp $(SRC_DIR)/ls $(SRC_DIR)/install $(SRC_DIR)/md5sum $(SRC_DIR)/mv $(SRC_DIR)/rm $(SRC_DIR)/sha1sum
all: $(BINARIES) $(SRC_DIR)/cp $(SRC_DIR)/ls $(SRC_DIR)/install $(SRC_DIR)/md5sum $(SRC_DIR)/mv $(SRC_DIR)/rm $(SRC_DIR)/sha1sum

LIB_DIR = lib
LIB_SRC = acl posixtm posixver strftime getopt getopt1 hash hash-pjw addext argmatch backupfile basename canon-host closeout cycle-check diacrit dirname dup-safer error exclude exitfail filemode __fpending file-type fnmatch fopen-safer full-read full-write gethostname getline getstr gettime hard-locale human idcache isdir imaxtostr linebuffer localcharset long-options makepath mbswidth md5 memcasecmp memcoll modechange offtostr path-concat physmem quote quotearg readtokens rpmatch safe-read safe-write same save-cwd savedir settime sha stpcpy stripslash strtoimax strtoumax umaxtostr unicodeio userspec version-etc xgetcwd xgethostname xmalloc xmemcoll xnanosleep xreadlink xstrdup xstrtod xstrtol xstrtoul xstrtoimax xstrtoumax yesno strnlen getcwd sig2str mountlist regex canonicalize mkstemp memrchr euidaccess ftw dirfd obstack strverscmp strftime tempname tsearch

LIB_OBJECTS = $(addprefix $(LIB_DIR)/, $(addsuffix .o, $(LIB_SRC)))

$(SRC_DIR)/false.c: $(SRC_DIR)/true.c
    cp $< $@
    sed -i -e s/true/false/g \
          -e s/success/failure/g \
          -e 's/(EXIT_SUCCESS)/(EXIT_FAILURE)/g' \
          $@

$(LIB_DIR)/libfettish.a: $(LIB_OBJECTS)
    $(AR) cr $@ $^

$(BINARIES) : % : %.o $(LIB_DIR)/libfettish.a
    $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@

$(SRC_DIR)/cp: $(SRC_DIR)/cp.o $(SRC_DIR)/copy.o $(SRC_DIR)/cp-hash.c $(LIB_DIR)/libfettish.a
    $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@

$(SRC_DIR)/install: $(SRC_DIR)/install.o $(SRC_DIR)/copy.o $(SRC_DIR)/cp-hash.c $(LIB_DIR)/libfettish.a
    $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@

$(SRC_DIR)/ls: $(SRC_DIR)/ls.o $(SRC_DIR)/ls-ls.o $(LIB_DIR)/libfettish.a
    $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@

$(SRC_DIR)/md5sum: $(SRC_DIR)/md5.o $(SRC_DIR)/md5sum.o $(LIB_DIR)/libfettish.a
    $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@

$(SRC_DIR)/mv: $(SRC_DIR)/mv.o $(SRC_DIR)/copy.o $(SRC_DIR)/remove.o $(SRC_DIR)/cp-hash.o $(LIB_DIR)/libfettish.a
    $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@

$(SRC_DIR)/rm: $(SRC_DIR)/rm.o $(SRC_DIR)/remove.o $(LIB_DIR)/libfettish.a
    $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@

$(SRC_DIR)/sha1sum: $(SRC_DIR)/sha1sum.o $(SRC_DIR)/md5sum.o $(LIB_DIR)/libfettish.a
    $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@

install: $(ALL)
    $(SRC_DIR)/install $^ $(bindir)

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/fnmatch_.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/fnmatch_.h used in: Step 734
/* Copyright (C) 1991, 1992, 1993, 1996, 1997, 1998, 1999, 2001, 2002, 2003
   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.  */

#ifndef _FNMATCH_H
# define _FNMATCH_H 1

# ifdef __cplusplus
extern "C" {
# endif

# if defined __cplusplus || (defined __STDC__ && __STDC__) || defined WINDOWS32
#  if !defined __GLIBC__ || !defined __P
#   undef   __P
#   define __P(protos)  protos
#  endif
# else /* Not C++ or ANSI C.  */
#  undef    __P
#  define __P(protos)   ()
/* We can get away without defining `const' here only because in this file
   it is used only inside the prototype for `fnmatch', which is elided in
   non-ANSI C where `const' is problematical.  */
# endif /* C++ or ANSI C.  */

/* We #undef these before defining them because some losing systems
   (HP-UX A.08.07 for example) define these in <unistd.h>.  */
# undef FNM_PATHNAME
# undef FNM_NOESCAPE
# undef FNM_PERIOD

/* Bits set in the FLAGS argument to `fnmatch'.  */
# define FNM_PATHNAME   (1 << 0) /* No wildcard can ever match `/'.  */
# define FNM_NOESCAPE   (1 << 1) /* Backslashes don't quote special chars.  */
# define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly.  */

# if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE
#  define FNM_FILE_NAME  FNM_PATHNAME   /* Preferred GNU name.  */
#  define FNM_LEADING_DIR (1 << 3)  /* Ignore `/...' after a match.  */
#  define FNM_CASEFOLD   (1 << 4)   /* Compare without regard to case.  */
#  define FNM_EXTMATCH   (1 << 5)   /* Use ksh-like extended matching. */
# endif

/* Value returned by `fnmatch' if STRING does not match PATTERN.  */
# define FNM_NOMATCH    1

/* This value is returned if the implementation does not support
   `fnmatch'.  Since this is not the case here it will never be
   returned but the conformance test suites still require the symbol
   to be defined.  */
# ifdef _XOPEN_SOURCE
#  define FNM_NOSYS (-1)
# endif

/* Match NAME against the filename pattern PATTERN,
   returning zero if it matches, FNM_NOMATCH if not.  */
extern int fnmatch __P ((const char *__pattern, const char *__name,
             int __flags));

# ifdef __cplusplus
}
# endif

#endif /* fnmatch.h */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/ftw_.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/ftw_.h used in: Step 735
/* Copyright (C) 1992, 1996, 1997, 1998, 1999, 2003
   Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

/*
 *  X/Open Portability Guide 4.2: ftw.h
 */

#ifndef _FTW_H
# define _FTW_H 1

# ifdef _LIBC
#  include <features.h>
# else
#  undef __THROW
#  define __THROW
#  undef __BEGIN_DECLS
#  define __BEGIN_DECLS
#  undef __END_DECLS
#  define __END_DECLS
# endif

# include <sys/types.h>
# include <sys/stat.h>

/* When compiling stand-alone on a system that does not define
   __USE_XOPEN_EXTENDED, define that symbol so that all the
   required declarations appear.  */
# if ! defined _LIBC && ! defined __USE_XOPEN_EXTENDED
#  define FTW_H_STANDALONE 1
#  define __USE_XOPEN_EXTENDED 1
# endif

__BEGIN_DECLS

/* Values for the FLAG argument to the user function passed to `ftw'
   and 'nftw'.  */
enum
{
  FTW_F,        /* Regular file.  */
# define FTW_F   FTW_F
  FTW_D,        /* Directory.  */
# define FTW_D   FTW_D
  FTW_DNR,      /* Unreadable directory.  */
# define FTW_DNR     FTW_DNR
  FTW_NS,       /* Unstatable file.  */
# define FTW_NS  FTW_NS

  /* Can't chdir to named directory.  This can happen only when using
     FTW_CHDIR.  Note that although we can't chdir into that directory,
     we were able to stat it, so SB (2nd argument to callback) is valid.  */
  FTW_DCH,
# define FTW_DCH FTW_DCH

  /* Can't chdir to parent of named directory.  This can happen only when
     using FTW_CHDIR.  Unlike for FTW_DCH, in this case, SB is not valid.
     In fact, it is NULL.  */
  FTW_DCHP,
# define FTW_DCHP FTW_DCHP

  /* nftw calls the user-supplied function at most twice for each directory
     it encounters.  When calling it the first time, it passes this value
     as the `type'.  */
  FTW_DPRE,
# define FTW_DPRE FTW_DPRE

# if defined __USE_BSD || defined __USE_XOPEN_EXTENDED

  FTW_SL,       /* Symbolic link.  */
#  define FTW_SL     FTW_SL
# endif

# ifdef __USE_XOPEN_EXTENDED
/* These flags are only passed from the `nftw' function.  */
  FTW_DP,       /* Directory, all subdirs have been visited. */
#  define FTW_DP     FTW_DP
  FTW_SLN       /* Symbolic link naming non-existing file.  */
#  define FTW_SLN FTW_SLN

# endif /* extended X/Open */
};


# ifdef __USE_XOPEN_EXTENDED
/* Flags for fourth argument of `nftw'.  */
enum
{
  FTW_PHYS = 1,     /* Perform physical walk, ignore symlinks.  */
#  define FTW_PHYS  FTW_PHYS
  FTW_MOUNT = 2,    /* Report only files on same file system as the
               argument.  */
#  define FTW_MOUNT FTW_MOUNT
  FTW_CHDIR = 4,    /* Change to current directory while processing it.  */
#  define FTW_CHDIR FTW_CHDIR
  FTW_DEPTH = 8     /* Report files in directory before directory itself.*/
#  define FTW_DEPTH FTW_DEPTH
};

/* Structure used for fourth argument to callback function for `nftw'.  */
struct FTW
  {
    int base;
    int level;
    int skip;
  };
# endif /* extended X/Open */


/* Convenient types for callback functions.  */
typedef int (*__ftw_func_t) (const char *__filename,
                 const struct stat *__status, int __flag);
# ifdef __USE_LARGEFILE64
typedef int (*__ftw64_func_t) (const char *__filename,
                   const struct stat64 *__status, int __flag);
# endif
# ifdef __USE_XOPEN_EXTENDED
typedef int (*__nftw_func_t) (const char *__filename,
                  const struct stat *__status, int __flag,
                  struct FTW *__info);
#  ifdef __USE_LARGEFILE64
typedef int (*__nftw64_func_t) (const char *__filename,
                const struct stat64 *__status,
                int __flag, struct FTW *__info);
#  endif
# endif

/* Call a function on every element in a directory tree.  */
# ifndef __USE_FILE_OFFSET64
extern int ftw (const char *__dir, __ftw_func_t __func, int __descriptors)
     __THROW;
# else
#  ifdef __REDIRECT
extern int __REDIRECT (ftw, (const char *__dir, __ftw_func_t __func,
                 int __descriptors) __THROW, ftw64);
#  else
#   define ftw ftw64
#  endif
# endif
# ifdef __USE_LARGEFILE64
extern int ftw64 (const char *__dir, __ftw64_func_t __func,
          int __descriptors) __THROW;
# endif

# ifdef __USE_XOPEN_EXTENDED
/* Call a function on every element in a directory tree.  FLAG allows
   to specify the behaviour more detailed.  */
#  ifndef __USE_FILE_OFFSET64
extern int nftw (const char *__dir, __nftw_func_t __func, int __descriptors,
         int __flag) __THROW;
#  else
#   ifdef __REDIRECT
extern int __REDIRECT (nftw, (const char *__dir, __nftw_func_t __func,
                  int __descriptors, int __flag) __THROW, nftw64);
#   else
#    define nftw nftw64
#   endif
#  endif
#  ifdef __USE_LARGEFILE64
extern int nftw64 (const char *__dir, __nftw64_func_t __func,
           int __descriptors, int __flag) __THROW;
#  endif
# endif

/* If we defined __USE_XOPEN_EXTENDED above, undefine it here.  */
# ifdef FTW_H_STANDALONE
#  undef __USE_XOPEN_EXTENDED
# endif

__END_DECLS

#endif  /* ftw.h */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/search_.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/search_.h used in: Step 736
/* For use with hsearch(3).  */
#ifndef __COMPAR_FN_T
# define __COMPAR_FN_T
typedef int (*__compar_fn_t) (const void *, const void *);

# ifdef __USE_GNU
typedef __compar_fn_t comparison_fn_t;
# endif
#endif

/* The tsearch routines are very interesting. They make many
   assumptions about the compiler.  It assumes that the first field
   in node must be the "key" field, which points to the datum.
   Everything depends on that.  */
/* For tsearch */
typedef enum
{
  preorder,
  postorder,
  endorder,
  leaf
}
VISIT;

/* GCC 2.95 and later have "__restrict"; C99 compilers have
   "restrict", and "configure" may have defined "restrict".  */
#ifndef __restrict
# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__))
#  if defined restrict || 199901L <= __STDC_VERSION__
#   define __restrict restrict
#  else
#   define __restrict
#  endif
# endif
#endif

/* Search for an entry matching the given KEY in the tree pointed to
   by *ROOTP and insert a new element if not found.  */
extern void *tsearch (const void *__key, void **__rootp,
              __compar_fn_t __compar);

/* Search for an entry matching the given KEY in the tree pointed to
   by *ROOTP.  If no matching entry is available return NULL.  */
extern void *tfind (const void *__key, void *const *__rootp,
            __compar_fn_t __compar);

/* Remove the element matching KEY from the tree pointed to by *ROOTP.  */
extern void *tdelete (const void *__restrict __key,
              void **__restrict __rootp,
              __compar_fn_t __compar);

#ifndef __ACTION_FN_T
# define __ACTION_FN_T
typedef void (*__action_fn_t) (const void *__nodep, VISIT __value,
                   int __level);
#endif

/* Walk through the whole tree and call the ACTION callback for every node
   or leaf.  */
extern void twalk (const void *__root, __action_fn_t __action);

#ifdef __USE_GNU
/* Callback type for function to free a tree node.  If the keys are atomic
   data this function should do nothing.  */
typedef void (*__free_fn_t) (void *__nodep);

/* Destroy the whole tree, call FREEFCT for each node or leaf.  */
extern void tdestroy (void *__root, __free_fn_t __freefct);
#endif

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/modechange.patch

File: ../git/live-bootstrap/sysa/coreutils-5.0/patches/modechange.patch used in: Step 740
SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>

SPDX-License-Identifier: GPL-2.0-or-later

modechange.h uses functions defined in sys/stat.h, so we need to move it to
after sys/stat.h include.

--- lib/modechange.c    2001-12-09 22:54:19.000000000 +0000
+++ lib/modechange.c    2021-01-17 18:34:22.016427148 +0000
@@ -28,8 +28,8 @@
 # include <config.h>
 #endif
 
-#include "modechange.h"
 #include <sys/stat.h>
+#include "modechange.h"
 #include "xstrtol.h"
 
 #if STDC_HEADERS

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/modechange.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/modechange.c used in: Step 740, Step 796
/* modechange.c -- file mode manipulation
   Copyright (C) 1989, 1990, 1997, 1998, 1999, 2001 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.  */

/* Written by David MacKenzie <djm@ai.mit.edu> */

/* The ASCII mode string is compiled into a linked list of `struct
   modechange', which can then be applied to each file to be changed.
   We do this instead of re-parsing the ASCII string for each file
   because the compiled form requires less computation to use; when
   changing the mode of many files, this probably results in a
   performance gain. */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include "modechange.h"
#include <sys/stat.h>
#include "xstrtol.h"

#if STDC_HEADERS
# include <stdlib.h>
#else
char *malloc ();
#endif

#ifndef NULL
# define NULL 0
#endif

#if STAT_MACROS_BROKEN
# undef S_ISDIR
#endif

#if !defined(S_ISDIR) && defined(S_IFDIR)
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif

/* The traditional octal values corresponding to each mode bit.  */
#define SUID 04000
#define SGID 02000
#define SVTX 01000
#define RUSR 00400
#define WUSR 00200
#define XUSR 00100
#define RGRP 00040
#define WGRP 00020
#define XGRP 00010
#define ROTH 00004
#define WOTH 00002
#define XOTH 00001
#define ALLM 07777 /* all octal mode bits */

#ifndef S_ISUID
# define S_ISUID SUID
#endif
#ifndef S_ISGID
# define S_ISGID SGID
#endif
#ifndef S_ISVTX
# define S_ISVTX SVTX
#endif
#ifndef S_IRUSR
# define S_IRUSR RUSR
#endif
#ifndef S_IWUSR
# define S_IWUSR WUSR
#endif
#ifndef S_IXUSR
# define S_IXUSR XUSR
#endif
#ifndef S_IRGRP
# define S_IRGRP RGRP
#endif
#ifndef S_IWGRP
# define S_IWGRP WGRP
#endif
#ifndef S_IXGRP
# define S_IXGRP XGRP
#endif
#ifndef S_IROTH
# define S_IROTH ROTH
#endif
#ifndef S_IWOTH
# define S_IWOTH WOTH
#endif
#ifndef S_IXOTH
# define S_IXOTH XOTH
#endif
#ifndef S_IRWXU
# define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
#endif
#ifndef S_IRWXG
# define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
#endif
#ifndef S_IRWXO
# define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH)
#endif

/* All the mode bits that can be affected by chmod.  */
#define CHMOD_MODE_BITS \
  (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)

/* Return newly allocated memory to hold one element of type TYPE. */
#define talloc(type) ((type *) malloc (sizeof (type)))

/* Create a mode_change entry with the specified `=ddd'-style
   mode change operation, where NEW_MODE is `ddd'.  Return the
   new entry, or NULL upon failure.  */

static struct mode_change *
make_node_op_equals (mode_t new_mode)
{
  struct mode_change *p;
  p = talloc (struct mode_change);
  if (p == NULL)
    return p;
  p->next = NULL;
  p->op = '=';
  p->flags = 0;
  p->value = new_mode;
  p->affected = CHMOD_MODE_BITS;    /* Affect all permissions. */
  return p;
}

/* Append entry E to the end of the link list with the specified
   HEAD and TAIL.  */

static void
mode_append_entry (struct mode_change **head,
           struct mode_change **tail,
           struct mode_change *e)
{
  if (*head == NULL)
    *head = *tail = e;
  else
    {
      (*tail)->next = e;
      *tail = e;
    }
}

/* Return a linked list of file mode change operations created from
   MODE_STRING, an ASCII string that contains either an octal number
   specifying an absolute mode, or symbolic mode change operations with
   the form:
   [ugoa...][[+-=][rwxXstugo...]...][,...]
   MASKED_OPS is a bitmask indicating which symbolic mode operators (=+-)
   should not affect bits set in the umask when no users are given.
   Operators not selected in MASKED_OPS ignore the umask.

   Return MODE_INVALID if `mode_string' does not contain a valid
   representation of file mode change operations;
   return MODE_MEMORY_EXHAUSTED if there is insufficient memory. */

struct mode_change *
mode_compile (const char *mode_string, unsigned int masked_ops)
{
  struct mode_change *head; /* First element of the linked list. */
  struct mode_change *tail; /* An element of the linked list. */
  unsigned long octal_value;    /* The mode value, if octal.  */
  mode_t umask_value;       /* The umask value (surprise). */

  head = NULL;
#ifdef lint
  tail = NULL;
#endif

  if (xstrtoul (mode_string, NULL, 8, &octal_value, "") == LONGINT_OK)
    {
      struct mode_change *p;
      mode_t mode;
      if (octal_value != (octal_value & ALLM))
    return MODE_INVALID;

      /* Help the compiler optimize the usual case where mode_t uses
     the traditional octal representation.  */
      mode = ((S_ISUID == SUID && S_ISGID == SGID && S_ISVTX == SVTX
           && S_IRUSR == RUSR && S_IWUSR == WUSR && S_IXUSR == XUSR
           && S_IRGRP == RGRP && S_IWGRP == WGRP && S_IXGRP == XGRP
           && S_IROTH == ROTH && S_IWOTH == WOTH && S_IXOTH == XOTH)
          ? octal_value
          : (mode_t) ((octal_value & SUID ? S_ISUID : 0)
              | (octal_value & SGID ? S_ISGID : 0)
              | (octal_value & SVTX ? S_ISVTX : 0)
              | (octal_value & RUSR ? S_IRUSR : 0)
              | (octal_value & WUSR ? S_IWUSR : 0)
              | (octal_value & XUSR ? S_IXUSR : 0)
              | (octal_value & RGRP ? S_IRGRP : 0)
              | (octal_value & WGRP ? S_IWGRP : 0)
              | (octal_value & XGRP ? S_IXGRP : 0)
              | (octal_value & ROTH ? S_IROTH : 0)
              | (octal_value & WOTH ? S_IWOTH : 0)
              | (octal_value & XOTH ? S_IXOTH : 0)));

      p = make_node_op_equals (mode);
      if (p == NULL)
    return MODE_MEMORY_EXHAUSTED;
      mode_append_entry (&head, &tail, p);
      return head;
    }

  umask_value = umask (0);
  umask (umask_value);      /* Restore the old value. */
  --mode_string;

  /* One loop iteration for each "ugoa...=+-rwxXstugo...[=+-rwxXstugo...]". */
  do
    {
      /* Which bits in the mode are operated on. */
      mode_t affected_bits = 0;
      /* `affected_bits' modified by umask. */
      mode_t affected_masked;
      /* Operators to actually use umask on. */
      unsigned ops_to_mask = 0;

      int who_specified_p;

      affected_bits = 0;
      ops_to_mask = 0;
      /* Turn on all the bits in `affected_bits' for each group given. */
      for (++mode_string;; ++mode_string)
    switch (*mode_string)
      {
      case 'u':
        affected_bits |= S_ISUID | S_IRWXU;
        break;
      case 'g':
        affected_bits |= S_ISGID | S_IRWXG;
        break;
      case 'o':
        affected_bits |= S_ISVTX | S_IRWXO;
        break;
      case 'a':
        affected_bits |= CHMOD_MODE_BITS;
        break;
      default:
        goto no_more_affected;
      }

    no_more_affected:
      /* If none specified, affect all bits, except perhaps those
     set in the umask. */
      if (affected_bits)
    who_specified_p = 1;
      else
    {
      who_specified_p = 0;
      affected_bits = CHMOD_MODE_BITS;
      ops_to_mask = masked_ops;
    }

      while (*mode_string == '=' || *mode_string == '+' || *mode_string == '-')
    {
      struct mode_change *change = talloc (struct mode_change);
      if (change == NULL)
        {
          mode_free (head);
          return MODE_MEMORY_EXHAUSTED;
        }

      change->next = NULL;
      change->op = *mode_string;    /* One of "=+-". */
      affected_masked = affected_bits;

      /* Per the Single Unix Spec, if `who' is not specified and the
         `=' operator is used, then clear all the bits first.  */
      if (!who_specified_p &&
          ops_to_mask & (*mode_string == '=' ? MODE_MASK_EQUALS : 0))
        {
          struct mode_change *p = make_node_op_equals (0);
          if (p == NULL)
        return MODE_MEMORY_EXHAUSTED;
          mode_append_entry (&head, &tail, p);
        }

      if (ops_to_mask & (*mode_string == '=' ? MODE_MASK_EQUALS
                 : *mode_string == '+' ? MODE_MASK_PLUS
                 : MODE_MASK_MINUS))
        affected_masked &= ~umask_value;
      change->affected = affected_masked;
      change->value = 0;
      change->flags = 0;

      /* Add the element to the tail of the list, so the operations
         are performed in the correct order. */
      mode_append_entry (&head, &tail, change);

      /* Set `value' according to the bits set in `affected_masked'. */
      for (++mode_string;; ++mode_string)
        switch (*mode_string)
          {
          case 'r':
        change->value |= ((S_IRUSR | S_IRGRP | S_IROTH)
                  & affected_masked);
        break;
          case 'w':
        change->value |= ((S_IWUSR | S_IWGRP | S_IWOTH)
                  & affected_masked);
        break;
          case 'X':
        change->flags |= MODE_X_IF_ANY_X;
        /* Fall through. */
          case 'x':
        change->value |= ((S_IXUSR | S_IXGRP | S_IXOTH)
                  & affected_masked);
        break;
          case 's':
        /* Set the setuid/gid bits if `u' or `g' is selected. */
        change->value |= (S_ISUID | S_ISGID) & affected_masked;
        break;
          case 't':
        /* Set the "save text image" bit if `o' is selected. */
        change->value |= S_ISVTX & affected_masked;
        break;
          case 'u':
        /* Set the affected bits to the value of the `u' bits
           on the same file.  */
        if (change->value)
          goto invalid;
        change->value = S_IRWXU;
        change->flags |= MODE_COPY_EXISTING;
        break;
          case 'g':
        /* Set the affected bits to the value of the `g' bits
           on the same file.  */
        if (change->value)
          goto invalid;
        change->value = S_IRWXG;
        change->flags |= MODE_COPY_EXISTING;
        break;
          case 'o':
        /* Set the affected bits to the value of the `o' bits
           on the same file.  */
        if (change->value)
          goto invalid;
        change->value = S_IRWXO;
        change->flags |= MODE_COPY_EXISTING;
        break;
          default:
        goto no_more_values;
          }
    no_more_values:;
    }
  } while (*mode_string == ',');
  if (*mode_string == 0)
    return head;
invalid:
  mode_free (head);
  return MODE_INVALID;
}

/* Return a file mode change operation that sets permissions to match those
   of REF_FILE.  Return MODE_BAD_REFERENCE if REF_FILE can't be accessed.  */

struct mode_change *
mode_create_from_ref (const char *ref_file)
{
  struct mode_change *change;   /* the only change element */
  struct stat ref_stats;

  if (stat (ref_file, &ref_stats))
    return MODE_BAD_REFERENCE;

  change = talloc (struct mode_change);

  if (change == NULL)
    return MODE_MEMORY_EXHAUSTED;

  change->op = '=';
  change->flags = 0;
  change->affected = CHMOD_MODE_BITS;
  change->value = ref_stats.st_mode;
  change->next = NULL;

  return change;
}

/* Return file mode OLDMODE, adjusted as indicated by the list of change
   operations CHANGES.  If OLDMODE is a directory, the type `X'
   change affects it even if no execute bits were set in OLDMODE.
   The returned value has the S_IFMT bits cleared. */

mode_t
mode_adjust (mode_t oldmode, const struct mode_change *changes)
{
  mode_t newmode;   /* The adjusted mode and one operand. */
  mode_t value;     /* The other operand. */

  newmode = oldmode & CHMOD_MODE_BITS;

  for (; changes; changes = changes->next)
    {
      if (changes->flags & MODE_COPY_EXISTING)
    {
      /* Isolate in `value' the bits in `newmode' to copy, given in
         the mask `changes->value'. */
      value = newmode & changes->value;

      if (changes->value & S_IRWXU)
        /* Copy `u' permissions onto `g' and `o'. */
        value |= (  (value & S_IRUSR ? S_IRGRP | S_IROTH : 0)
              | (value & S_IWUSR ? S_IWGRP | S_IWOTH : 0)
              | (value & S_IXUSR ? S_IXGRP | S_IXOTH : 0));
      else if (changes->value & S_IRWXG)
        /* Copy `g' permissions onto `u' and `o'. */
        value |= (  (value & S_IRGRP ? S_IRUSR | S_IROTH : 0)
              | (value & S_IWGRP ? S_IWUSR | S_IWOTH : 0)
              | (value & S_IXGRP ? S_IXUSR | S_IXOTH : 0));
      else
        /* Copy `o' permissions onto `u' and `g'. */
        value |= (  (value & S_IROTH ? S_IRUSR | S_IRGRP : 0)
              | (value & S_IWOTH ? S_IWUSR | S_IWGRP : 0)
              | (value & S_IXOTH ? S_IXUSR | S_IXGRP : 0));

      /* In order to change only `u', `g', or `o' permissions,
         or some combination thereof, clear unselected bits.
         This cannot be done in mode_compile because the value
         to which the `changes->affected' mask is applied depends
         on the old mode of each file. */
      value &= changes->affected;
    }
      else
    {
      value = changes->value;
      /* If `X', do not affect the execute bits if the file is not a
         directory and no execute bits are already set. */
      if ((changes->flags & MODE_X_IF_ANY_X)
          && !S_ISDIR (oldmode)
          && (newmode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
        /* Clear the execute bits. */
        value &= ~ (S_IXUSR | S_IXGRP | S_IXOTH);
    }

      switch (changes->op)
    {
    case '=':
      /* Preserve the previous values in `newmode' of bits that are
         not affected by this change operation. */
      newmode = (newmode & ~changes->affected) | value;
      break;
    case '+':
      newmode |= value;
      break;
    case '-':
      newmode &= ~value;
      break;
    }
    }
  return newmode;
}

/* Free the memory used by the list of file mode change operations
   CHANGES. */

void
mode_free (register struct mode_change *changes)
{
  register struct mode_change *next;

  while (changes)
    {
      next = changes->next;
      free (changes);
      changes = next;
    }
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/mbstate.patch

File: ../git/live-bootstrap/sysa/coreutils-5.0/patches/mbstate.patch used in: Step 741
SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>

SPDX-License-Identifier: GPL-2.0-or-later

mbstate_t is a struct that is required. However, it is not defined by mes libc.
This implementation was taken from glibc 2.32. 

--- lib/quotearg.c  2002-11-23 07:08:10.000000000 +0000
+++ lib/quotearg.c  2021-01-17 19:41:59.461095532 +0000
@@ -21,6 +21,7 @@
 # include <config.h>
 #endif
 
+#include "mbstate_t.h"
 #include "quotearg.h"
 
 #include "xalloc.h"
--- lib/mbstate_t.h 1970-01-01 01:00:00.000000000 +0100
+++ lib/mbstate_t.h 2021-01-17 19:42:21.341658668 +0000
@@ -0,0 +1,23 @@
+#ifndef ____mbstate_t_defined
+#define ____mbstate_t_defined 1
+
+/* Integral type unchanged by default argument promotions that can
+   hold any value corresponding to members of the extended character
+   set, as well as at least one value that does not correspond to any
+   member of the extended character set.  */
+#ifndef __WINT_TYPE__
+# define __WINT_TYPE__ unsigned int
+#endif
+
+/* Conversion state information.  */
+typedef struct
+{
+  int __count;
+  union
+  {
+    __WINT_TYPE__ __wch;
+    char __wchb[4];
+  } __value;       /* Value so far.  */
+} mbstate_t;
+
+#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/quotearg.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/quotearg.c used in: Step 741, Step 801
/* quotearg.c - quote arguments for output
   Copyright (C) 1998, 1999, 2000, 2001, 2002 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.  */

/* Written by Paul Eggert <eggert@twinsun.com> */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include "quotearg.h"

#include "xalloc.h"

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>

#include "gettext.h"
#define _(msgid) gettext (msgid)
#define N_(msgid) msgid

#if HAVE_WCHAR_H

/* BSD/OS 4.1 wchar.h requires FILE and struct tm to be declared.  */
# include <stdio.h>
# include <time.h>

# include <wchar.h>
#endif

#if !HAVE_MBRTOWC
/* Disable multibyte processing entirely.  Since MB_CUR_MAX is 1, the
   other macros are defined only for documentation and to satisfy C
   syntax.  */
# undef MB_CUR_MAX
# define MB_CUR_MAX 1
# define mbrtowc(pwc, s, n, ps) ((*(pwc) = *(s)) != 0)
# define iswprint(wc) isprint ((unsigned char) (wc))
# undef HAVE_MBSINIT
#endif

#if !defined mbsinit && !HAVE_MBSINIT
# define mbsinit(ps) 1
#endif

#ifndef iswprint
# if HAVE_WCTYPE_H
#  include <wctype.h>
# endif
# if !defined iswprint && !HAVE_ISWPRINT
#  define iswprint(wc) 1
# endif
#endif

#ifndef SIZE_MAX
# define SIZE_MAX ((size_t) -1)
#endif

#define INT_BITS (sizeof (int) * CHAR_BIT)

struct quoting_options
{
  /* Basic quoting style.  */
  enum quoting_style style;

  /* Quote the characters indicated by this bit vector even if the
     quoting style would not normally require them to be quoted.  */
  int quote_these_too[(UCHAR_MAX / INT_BITS) + 1];
};

/* Names of quoting styles.  */
char const *const quoting_style_args[] =
{
  "literal",
  "shell",
  "shell-always",
  "c",
  "escape",
  "locale",
  "clocale",
  0
};

/* Correspondences to quoting style names.  */
enum quoting_style const quoting_style_vals[] =
{
  literal_quoting_style,
  shell_quoting_style,
  shell_always_quoting_style,
  c_quoting_style,
  escape_quoting_style,
  locale_quoting_style,
  clocale_quoting_style
};

/* The default quoting options.  */
static struct quoting_options default_quoting_options;

/* Allocate a new set of quoting options, with contents initially identical
   to O if O is not null, or to the default if O is null.
   It is the caller's responsibility to free the result.  */
struct quoting_options *
clone_quoting_options (struct quoting_options *o)
{
  int e = errno;
  struct quoting_options *p = xmalloc (sizeof *p);
  *p = *(o ? o : &default_quoting_options);
  errno = e;
  return p;
}

/* Get the value of O's quoting style.  If O is null, use the default.  */
enum quoting_style
get_quoting_style (struct quoting_options *o)
{
  return (o ? o : &default_quoting_options)->style;
}

/* In O (or in the default if O is null),
   set the value of the quoting style to S.  */
void
set_quoting_style (struct quoting_options *o, enum quoting_style s)
{
  (o ? o : &default_quoting_options)->style = s;
}

/* In O (or in the default if O is null),
   set the value of the quoting options for character C to I.
   Return the old value.  Currently, the only values defined for I are
   0 (the default) and 1 (which means to quote the character even if
   it would not otherwise be quoted).  */
int
set_char_quoting (struct quoting_options *o, char c, int i)
{
  unsigned char uc = c;
  int *p = (o ? o : &default_quoting_options)->quote_these_too + uc / INT_BITS;
  int shift = uc % INT_BITS;
  int r = (*p >> shift) & 1;
  *p ^= ((i & 1) ^ r) << shift;
  return r;
}

/* MSGID approximates a quotation mark.  Return its translation if it
   has one; otherwise, return either it or "\"", depending on S.  */
static char const *
gettext_quote (char const *msgid, enum quoting_style s)
{
  char const *translation = _(msgid);
  if (translation == msgid && s == clocale_quoting_style)
    translation = "\"";
  return translation;
}

/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
   argument ARG (of size ARGSIZE), using QUOTING_STYLE and the
   non-quoting-style part of O to control quoting.
   Terminate the output with a null character, and return the written
   size of the output, not counting the terminating null.
   If BUFFERSIZE is too small to store the output string, return the
   value that would have been returned had BUFFERSIZE been large enough.
   If ARGSIZE is -1, use the string length of the argument for ARGSIZE.

   This function acts like quotearg_buffer (BUFFER, BUFFERSIZE, ARG,
   ARGSIZE, O), except it uses QUOTING_STYLE instead of the quoting
   style specified by O, and O may not be null.  */

static size_t
quotearg_buffer_restyled (char *buffer, size_t buffersize,
              char const *arg, size_t argsize,
              enum quoting_style quoting_style,
              struct quoting_options const *o)
{
  size_t i;
  size_t len = 0;
  char const *quote_string = 0;
  size_t quote_string_len = 0;
  int backslash_escapes = 0;
  int unibyte_locale = MB_CUR_MAX == 1;

#define STORE(c) \
    do \
      { \
    if (len < buffersize) \
      buffer[len] = (c); \
    len++; \
      } \
    while (0)

  switch (quoting_style)
    {
    case c_quoting_style:
      STORE ('"');
      backslash_escapes = 1;
      quote_string = "\"";
      quote_string_len = 1;
      break;

    case escape_quoting_style:
      backslash_escapes = 1;
      break;

    case locale_quoting_style:
    case clocale_quoting_style:
      {
    /* Get translations for open and closing quotation marks.

       The message catalog should translate "`" to a left
       quotation mark suitable for the locale, and similarly for
       "'".  If the catalog has no translation,
       locale_quoting_style quotes `like this', and
       clocale_quoting_style quotes "like this".

       For example, an American English Unicode locale should
       translate "`" to U+201C (LEFT DOUBLE QUOTATION MARK), and
       should translate "'" to U+201D (RIGHT DOUBLE QUOTATION
       MARK).  A British English Unicode locale should instead
       translate these to U+2018 (LEFT SINGLE QUOTATION MARK) and
       U+2019 (RIGHT SINGLE QUOTATION MARK), respectively.  */

    char const *left = gettext_quote (N_("`"), quoting_style);
    char const *right = gettext_quote (N_("'"), quoting_style);
    for (quote_string = left; *quote_string; quote_string++)
      STORE (*quote_string);
    backslash_escapes = 1;
    quote_string = right;
    quote_string_len = strlen (quote_string);
      }
      break;

    case shell_always_quoting_style:
      STORE ('\'');
      quote_string = "'";
      quote_string_len = 1;
      break;

    default:
      break;
    }

  for (i = 0;  ! (argsize == SIZE_MAX ? arg[i] == '\0' : i == argsize);  i++)
    {
      unsigned char c;
      unsigned char esc;

      if (backslash_escapes
      && quote_string_len
      && i + quote_string_len <= argsize
      && memcmp (arg + i, quote_string, quote_string_len) == 0)
    STORE ('\\');

      c = arg[i];
      switch (c)
    {
    case '\0':
      if (backslash_escapes)
        {
          STORE ('\\');
          STORE ('0');
          STORE ('0');
          c = '0';
        }
      break;

    case '?':
      switch (quoting_style)
        {
        case shell_quoting_style:
          goto use_shell_always_quoting_style;

        case c_quoting_style:
          if (i + 2 < argsize && arg[i + 1] == '?')
        switch (arg[i + 2])
          {
          case '!': case '\'':
          case '(': case ')': case '-': case '/':
          case '<': case '=': case '>':
            /* Escape the second '?' in what would otherwise be
               a trigraph.  */
            c = arg[i + 2];
            i += 2;
            STORE ('?');
            STORE ('\\');
            STORE ('?');
            break;
          }
          break;

        default:
          break;
        }
      break;

    case '\a': esc = 'a'; goto c_escape;
    case '\b': esc = 'b'; goto c_escape;
    case '\f': esc = 'f'; goto c_escape;
    case '\n': esc = 'n'; goto c_and_shell_escape;
    case '\r': esc = 'r'; goto c_and_shell_escape;
    case '\t': esc = 't'; goto c_and_shell_escape;
    case '\v': esc = 'v'; goto c_escape;
    case '\\': esc = c; goto c_and_shell_escape;

    c_and_shell_escape:
      if (quoting_style == shell_quoting_style)
        goto use_shell_always_quoting_style;
    c_escape:
      if (backslash_escapes)
        {
          c = esc;
          goto store_escape;
        }
      break;

    case '#': case '~':
      if (i != 0)
        break;
      /* Fall through.  */
    case ' ':
    case '!': /* special in bash */
    case '"': case '$': case '&':
    case '(': case ')': case '*': case ';':
    case '<': case '>': case '[':
    case '^': /* special in old /bin/sh, e.g. SunOS 4.1.4 */
    case '`': case '|':
      /* A shell special character.  In theory, '$' and '`' could
         be the first bytes of multibyte characters, which means
         we should check them with mbrtowc, but in practice this
         doesn't happen so it's not worth worrying about.  */
      if (quoting_style == shell_quoting_style)
        goto use_shell_always_quoting_style;
      break;

    case '\'':
      switch (quoting_style)
        {
        case shell_quoting_style:
          goto use_shell_always_quoting_style;

        case shell_always_quoting_style:
          STORE ('\'');
          STORE ('\\');
          STORE ('\'');
          break;

        default:
          break;
        }
      break;

    case '%': case '+': case ',': case '-': case '.': case '/':
    case '0': case '1': case '2': case '3': case '4': case '5':
    case '6': case '7': case '8': case '9': case ':': 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': case ']': 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':
    case '{': case '}':
      /* These characters don't cause problems, no matter what the
         quoting style is.  They cannot start multibyte sequences.  */
      break;

    default:
      /* If we have a multibyte sequence, copy it until we reach
         its end, find an error, or come back to the initial shift
         state.  For C-like styles, if the sequence has
         unprintable characters, escape the whole sequence, since
         we can't easily escape single characters within it.  */
      {
        /* Length of multibyte sequence found so far.  */
        size_t m;

        int printable;

        if (unibyte_locale)
          {
        m = 1;
        printable = isprint (c);
          }
        else
          {
        mbstate_t mbstate;
        memset (&mbstate, 0, sizeof mbstate);

        m = 0;
        printable = 1;
        if (argsize == SIZE_MAX)
          argsize = strlen (arg);

        do
          {
            wchar_t w;
            size_t bytes = mbrtowc (&w, &arg[i + m],
                        argsize - (i + m), &mbstate);
            if (bytes == 0)
              break;
            else if (bytes == (size_t) -1)
              {
            printable = 0;
            break;
              }
            else if (bytes == (size_t) -2)
              {
            printable = 0;
            while (i + m < argsize && arg[i + m])
              m++;
            break;
              }
            else
              {
            if (! iswprint (w))
              printable = 0;
            m += bytes;
              }
          }
        while (! mbsinit (&mbstate));
          }

        if (1 < m || (backslash_escapes && ! printable))
          {
        /* Output a multibyte sequence, or an escaped
           unprintable unibyte character.  */
        size_t ilim = i + m;

        for (;;)
          {
            if (backslash_escapes && ! printable)
              {
            STORE ('\\');
            STORE ('0' + (c >> 6));
            STORE ('0' + ((c >> 3) & 7));
            c = '0' + (c & 7);
              }
            if (ilim <= i + 1)
              break;
            STORE (c);
            c = arg[++i];
          }

        goto store_c;
          }
      }
    }

      if (! (backslash_escapes
         && o->quote_these_too[c / INT_BITS] & (1 << (c % INT_BITS))))
    goto store_c;

    store_escape:
      STORE ('\\');

    store_c:
      STORE (c);
    }

  if (quote_string)
    for (; *quote_string; quote_string++)
      STORE (*quote_string);

  if (len < buffersize)
    buffer[len] = '\0';
  return len;

 use_shell_always_quoting_style:
  return quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
                   shell_always_quoting_style, o);
}

/* Place into buffer BUFFER (of size BUFFERSIZE) a quoted version of
   argument ARG (of size ARGSIZE), using O to control quoting.
   If O is null, use the default.
   Terminate the output with a null character, and return the written
   size of the output, not counting the terminating null.
   If BUFFERSIZE is too small to store the output string, return the
   value that would have been returned had BUFFERSIZE been large enough.
   If ARGSIZE is -1, use the string length of the argument for ARGSIZE.  */
size_t
quotearg_buffer (char *buffer, size_t buffersize,
         char const *arg, size_t argsize,
         struct quoting_options const *o)
{
  struct quoting_options const *p = o ? o : &default_quoting_options;
  int e = errno;
  size_t r = quotearg_buffer_restyled (buffer, buffersize, arg, argsize,
                       p->style, p);
  errno = e;
  return r;
}

/* Use storage slot N to return a quoted version of argument ARG.
   ARG is of size ARGSIZE, but if that is -1, ARG is a null-terminated string.
   OPTIONS specifies the quoting options.
   The returned value points to static storage that can be
   reused by the next call to this function with the same value of N.
   N must be nonnegative.  N is deliberately declared with type "int"
   to allow for future extensions (using negative values).  */
static char *
quotearg_n_options (int n, char const *arg, size_t argsize,
            struct quoting_options const *options)
{
  int e = errno;

  /* Preallocate a slot 0 buffer, so that the caller can always quote
     one small component of a "memory exhausted" message in slot 0.  */
  static char slot0[256];
  static unsigned int nslots = 1;
  unsigned int n0 = n;
  struct slotvec
    {
      size_t size;
      char *val;
    };
  static struct slotvec slotvec0 = {sizeof slot0, slot0};
  static struct slotvec *slotvec = &slotvec0;

  if (n < 0)
    abort ();

  if (nslots <= n0)
    {
      unsigned int n1 = n0 + 1;
      size_t s = n1 * sizeof *slotvec;

      if (SIZE_MAX / UINT_MAX <= sizeof *slotvec
      && n1 != s / sizeof *slotvec)
    xalloc_die ();

      if (slotvec == &slotvec0)
    {
      slotvec = xmalloc (sizeof *slotvec);
      *slotvec = slotvec0;
    }
      slotvec = xrealloc (slotvec, s);
      memset (slotvec + nslots, 0, (n1 - nslots) * sizeof *slotvec);
      nslots = n1;
    }

  {
    size_t size = slotvec[n].size;
    char *val = slotvec[n].val;
    size_t qsize = quotearg_buffer (val, size, arg, argsize, options);

    if (size <= qsize)
      {
    slotvec[n].size = size = qsize + 1;
    slotvec[n].val = val = xrealloc (val == slot0 ? 0 : val, size);
    quotearg_buffer (val, size, arg, argsize, options);
      }

    errno = e;
    return val;
  }
}

char *
quotearg_n (int n, char const *arg)
{
  return quotearg_n_options (n, arg, SIZE_MAX, &default_quoting_options);
}

char *
quotearg (char const *arg)
{
  return quotearg_n (0, arg);
}

/* Return quoting options for STYLE, with no extra quoting.  */
static struct quoting_options
quoting_options_from_style (enum quoting_style style)
{
  struct quoting_options o;
  o.style = style;
  memset (o.quote_these_too, 0, sizeof o.quote_these_too);
  return o;
}

char *
quotearg_n_style (int n, enum quoting_style s, char const *arg)
{
  struct quoting_options const o = quoting_options_from_style (s);
  return quotearg_n_options (n, arg, SIZE_MAX, &o);
}

char *
quotearg_n_style_mem (int n, enum quoting_style s,
              char const *arg, size_t argsize)
{
  struct quoting_options const o = quoting_options_from_style (s);
  return quotearg_n_options (n, arg, argsize, &o);
}

char *
quotearg_style (enum quoting_style s, char const *arg)
{
  return quotearg_n_style (0, s, arg);
}

char *
quotearg_char (char const *arg, char ch)
{
  struct quoting_options options;
  options = default_quoting_options;
  set_char_quoting (&options, ch, 1);
  return quotearg_n_options (0, arg, SIZE_MAX, &options);
}

char *
quotearg_colon (char const *arg)
{
  return quotearg_char (arg, ':');
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/ls-strcmp.patch

File: ../git/live-bootstrap/sysa/coreutils-5.0/patches/ls-strcmp.patch used in: Step 742
SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>

SPDX-License-Identifier: GPL-2.0-or-later

strcoll() does not exist in mes libc, change it to strcmp.

--- src/ls.c
+++ src/ls.c
@@ -2597,7 +2597,7 @@ xstrcoll (char const *a, char const *b)
 {
   int diff;
   errno = 0;
-  diff = strcoll (a, b);
+  diff = strcmp (a, b);
   if (errno)
     {
       error (0, errno, _("cannot compare file names %s and %s"),

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/ls.c used in: Step 742, Step 960
/* `dir', `vdir' and `ls' directory listing programs for GNU.
   Copyright (C) 85, 88, 90, 91, 1995-2003 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.  */

/* If ls_mode is LS_MULTI_COL,
   the multi-column format is the default regardless
   of the type of output device.
   This is for the `dir' program.

   If ls_mode is LS_LONG_FORMAT,
   the long format is the default regardless of the
   type of output device.
   This is for the `vdir' program.

   If ls_mode is LS_LS,
   the output format depends on whether the output
   device is a terminal.
   This is for the `ls' program. */

/* Written by Richard Stallman and David MacKenzie.  */

/* Color support by Peter Anvin <Peter.Anvin@linux.org> and Dennis
   Flaherty <dennisf@denix.elk.miles.com> based on original patches by
   Greg Lee <lee@uhunix.uhcc.hawaii.edu>.  */

#ifdef _AIX
 #pragma alloca
#endif

#include <config.h>
#include <sys/types.h>

#if HAVE_TERMIOS_H
# include <termios.h>
#endif

#ifdef GWINSZ_IN_SYS_IOCTL
# include <sys/ioctl.h>
#endif

#ifdef WINSIZE_IN_PTEM
# include <sys/stream.h>
# include <sys/ptem.h>
#endif

#include <stdio.h>
#include <assert.h>
#include <setjmp.h>
#include <grp.h>
#include <pwd.h>
#include <getopt.h>
#include <signal.h>

/* Get MB_CUR_MAX.  */
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif

/* Get mbstate_t, mbrtowc(), mbsinit(), wcwidth().  */
#if HAVE_WCHAR_H
# include <wchar.h>
#endif

/* Get iswprint().  */
#if HAVE_WCTYPE_H
# include <wctype.h>
#endif
#if !defined iswprint && !HAVE_ISWPRINT
# define iswprint(wc) 1
#endif

#ifndef HAVE_DECL_WCWIDTH
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_WCWIDTH
int wcwidth ();
#endif

/* If wcwidth() doesn't exist, assume all printable characters have
   width 1.  */
#ifndef wcwidth
# if !HAVE_WCWIDTH
#  define wcwidth(wc) ((wc) == 0 ? 0 : iswprint (wc) ? 1 : -1)
# endif
#endif

#include "system.h"
#include <fnmatch.h>

#include "acl.h"
#include "argmatch.h"
#include "dev-ino.h"
#include "dirname.h"
#include "dirfd.h"
#include "error.h"
#include "full-write.h"
#include "hard-locale.h"
#include "hash.h"
#include "human.h"
#include "filemode.h"
#include "inttostr.h"
#include "ls.h"
#include "mbswidth.h"
#include "obstack.h"
#include "path-concat.h"
#include "quote.h"
#include "quotearg.h"
#include "same.h"
#include "strftime.h"
#include "strverscmp.h"
#include "xstrtol.h"
#include "xreadlink.h"

#define PROGRAM_NAME (ls_mode == LS_LS ? "ls" \
              : (ls_mode == LS_MULTI_COL \
             ? "dir" : "vdir"))

#define AUTHORS N_ ("Richard Stallman and David MacKenzie")

#define obstack_chunk_alloc malloc
#define obstack_chunk_free free

/* Return an int indicating the result of comparing two integers.
   Subtracting doesn't always work, due to overflow.  */
#define longdiff(a, b) ((a) < (b) ? -1 : (a) > (b))

/* The field width for inode numbers.  On some hosts inode numbers are
   64 bits, so columns won't line up exactly when a huge inode number
   is encountered, but in practice 7 digits is usually enough.  */
#ifndef INODE_DIGITS
# define INODE_DIGITS 7
#endif

/* Arrange to make lstat calls go through the wrapper function
   on systems with an lstat function that does not dereference symlinks
   that are specified with a trailing slash.  */
#if ! LSTAT_FOLLOWS_SLASHED_SYMLINK
int rpl_lstat (const char *, struct stat *);
# undef lstat
# define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf)
#endif

#if HAVE_STRUCT_DIRENT_D_TYPE && defined DTTOIF
# define DT_INIT(Val) = Val
#else
# define DT_INIT(Val) /* empty */
#endif

#ifdef ST_MTIM_NSEC
# define TIMESPEC_NS(timespec) ((timespec).ST_MTIM_NSEC)
#else
# define TIMESPEC_NS(timespec) 0
#endif

#if ! HAVE_STRUCT_STAT_ST_AUTHOR
# define st_author st_uid
#endif

/* Cray/Unicos DMF: use the file's migrated, not real, status */
#if HAVE_ST_DM_MODE
# define ST_DM_MODE(Stat_buf) ((Stat_buf).st_dm_mode)
#else
# define ST_DM_MODE(Stat_buf) ((Stat_buf).st_mode)
#endif

#ifndef LOGIN_NAME_MAX
# if _POSIX_LOGIN_NAME_MAX
#  define LOGIN_NAME_MAX _POSIX_LOGIN_NAME_MAX
# else
#  define LOGIN_NAME_MAX 17
# endif
#endif

/* The maximum length of a string representation of a user or group ID,
   not counting any terminating NUL byte.  */
#define ID_LENGTH_MAX \
  MAX (LOGIN_NAME_MAX - 1, LONGEST_HUMAN_READABLE)

enum filetype
  {
    unknown DT_INIT (DT_UNKNOWN),
    fifo DT_INIT (DT_FIFO),
    chardev DT_INIT (DT_CHR),
    directory DT_INIT (DT_DIR),
    blockdev DT_INIT (DT_BLK),
    normal DT_INIT (DT_REG),
    symbolic_link DT_INIT (DT_LNK),
    sock DT_INIT (DT_SOCK),
    arg_directory DT_INIT (2 * (DT_UNKNOWN | DT_FIFO | DT_CHR | DT_DIR | DT_BLK
                | DT_REG | DT_LNK | DT_SOCK))
  };

struct fileinfo
  {
    /* The file name. */
    char *name;

    struct stat stat;

    /* For symbolic link, name of the file linked to, otherwise zero. */
    char *linkname;

    /* For symbolic link and long listing, st_mode of file linked to, otherwise
       zero. */
    mode_t linkmode;

    /* For symbolic link and color printing, 1 if linked-to file
       exists, otherwise 0.  */
    int linkok;

    enum filetype filetype;

#if HAVE_ACL
    /* For long listings, true if the file has an access control list.  */
    bool have_acl;
#endif
  };

#if HAVE_ACL
# define FILE_HAS_ACL(F) ((F)->have_acl)
#else
# define FILE_HAS_ACL(F) 0
#endif

#define LEN_STR_PAIR(s) sizeof (s) - 1, s

/* Null is a valid character in a color indicator (think about Epson
   printers, for example) so we have to use a length/buffer string
   type.  */

struct bin_str
  {
    int len;            /* Number of bytes */
    const char *string;     /* Pointer to the same */
  };

#ifndef STDC_HEADERS
time_t time ();
#endif

char *getgroup ();
char *getuser ();

static size_t quote_name (FILE *out, const char *name,
              struct quoting_options const *options,
              size_t *width);
static char *make_link_path (const char *path, const char *linkname);
static int decode_switches (int argc, char **argv);
static int file_interesting (const struct dirent *next);
static uintmax_t gobble_file (const char *name, enum filetype type,
                  int explicit_arg, const char *dirname);
static void print_color_indicator (const char *name, mode_t mode, int linkok);
static void put_indicator (const struct bin_str *ind);
static int put_indicator_direct (const struct bin_str *ind);
static int length_of_file_name_and_frills (const struct fileinfo *f);
static void add_ignore_pattern (const char *pattern);
static void attach (char *dest, const char *dirname, const char *name);
static void clear_files (void);
static void extract_dirs_from_files (const char *dirname,
                     int ignore_dot_and_dot_dot);
static void get_link_name (const char *filename, struct fileinfo *f);
static void indent (int from, int to);
static void init_column_info (void);
static void print_current_files (void);
static void print_dir (const char *name, const char *realname);
static void print_file_name_and_frills (const struct fileinfo *f);
static void print_horizontal (void);
static void print_long_format (const struct fileinfo *f);
static void print_many_per_line (void);
static void print_name_with_quoting (const char *p, mode_t mode,
                     int linkok,
                     struct obstack *stack);
static void prep_non_filename_text (void);
static void print_type_indicator (mode_t mode);
static void print_with_commas (void);
static void queue_directory (const char *name, const char *realname);
static void sort_files (void);
static void parse_ls_color (void);
void usage (int status);

/* The name the program was run with, stripped of any leading path. */
char *program_name;

/* Initial size of hash table.
   Most hierarchies are likely to be shallower than this.  */
#define INITIAL_TABLE_SIZE 30

/* The set of `active' directories, from the current command-line argument
   to the level in the hierarchy at which files are being listed.
   A directory is represented by its device and inode numbers (struct dev_ino).
   A directory is added to this set when ls begins listing it or its
   entries, and it is removed from the set just after ls has finished
   processing it.  This set is used solely to detect loops, e.g., with
   mkdir loop; cd loop; ln -s ../loop sub; ls -RL  */
static Hash_table *active_dir_set;

#define LOOP_DETECT (!!active_dir_set)

/* The table of files in the current directory:

   `files' points to a vector of `struct fileinfo', one per file.
   `nfiles' is the number of elements space has been allocated for.
   `files_index' is the number actually in use.  */

/* Address of block containing the files that are described.  */
static struct fileinfo *files;  /* FIXME: rename this to e.g. cwd_file */

/* Length of block that `files' points to, measured in files.  */
static int nfiles;  /* FIXME: rename this to e.g. cwd_n_alloc */

/* Index of first unused in `files'.  */
static int files_index;  /* FIXME: rename this to e.g. cwd_n_used */

/* When nonzero, in a color listing, color each symlink name according to the
   type of file it points to.  Otherwise, color them according to the `ln'
   directive in LS_COLORS.  Dangling (orphan) symlinks are treated specially,
   regardless.  This is set when `ln=target' appears in LS_COLORS.  */

static int color_symlink_as_referent;

/* mode of appropriate file for colorization */
#define FILE_OR_LINK_MODE(File) \
    ((color_symlink_as_referent && (File)->linkok) \
     ? (File)->linkmode : (File)->stat.st_mode)


/* Record of one pending directory waiting to be listed.  */

struct pending
  {
    char *name;
    /* If the directory is actually the file pointed to by a symbolic link we
       were told to list, `realname' will contain the name of the symbolic
       link, otherwise zero. */
    char *realname;
    struct pending *next;
  };

static struct pending *pending_dirs;

/* Current time in seconds and nanoseconds since 1970, updated as
   needed when deciding whether a file is recent.  */

static time_t current_time = TYPE_MINIMUM (time_t);
static int current_time_ns = -1;

/* The number of digits to use for block sizes.
   4, or more if needed for bigger numbers.  */

static int block_size_size;

/* Option flags */

/* long_format for lots of info, one per line.
   one_per_line for just names, one per line.
   many_per_line for just names, many per line, sorted vertically.
   horizontal for just names, many per line, sorted horizontally.
   with_commas for just names, many per line, separated by commas.

   -l (and other options that imply -l), -1, -C, -x and -m control
   this parameter.  */

enum format
  {
    long_format,        /* -l and other options that imply -l */
    one_per_line,       /* -1 */
    many_per_line,      /* -C */
    horizontal,         /* -x */
    with_commas         /* -m */
  };

static enum format format;

/* `full-iso' uses full ISO-style dates and times.  `long-iso' uses longer
   ISO-style time stamps, though shorter than `full-iso'.  `iso' uses shorter
   ISO-style time stamps.  `locale' uses locale-dependent time stamps.  */
enum time_style
  {
    full_iso_time_style,    /* --time-style=full-iso */
    long_iso_time_style,    /* --time-style=long-iso */
    iso_time_style,     /* --time-style=iso */
    locale_time_style       /* --time-style=locale */
  };

static char const *const time_style_args[] =
{
  "full-iso", "long-iso", "iso", "locale", 0
};

static enum time_style const time_style_types[] =
{
  full_iso_time_style, long_iso_time_style, iso_time_style,
  locale_time_style, 0
};

/* Type of time to print or sort by.  Controlled by -c and -u.  */

enum time_type
  {
    time_mtime,         /* default */
    time_ctime,         /* -c */
    time_atime          /* -u */
  };

static enum time_type time_type;

/* The file characteristic to sort by.  Controlled by -t, -S, -U, -X, -v. */

enum sort_type
  {
    sort_none,          /* -U */
    sort_name,          /* default */
    sort_extension,     /* -X */
    sort_time,          /* -t */
    sort_size,          /* -S */
    sort_version        /* -v */
  };

static enum sort_type sort_type;

/* Direction of sort.
   0 means highest first if numeric,
   lowest first if alphabetic;
   these are the defaults.
   1 means the opposite order in each case.  -r  */

static int sort_reverse;

/* Nonzero means to display owner information.  -g turns this off.  */

static int print_owner = 1;

/* Nonzero means to display author information.  */

static bool print_author;

/* Nonzero means to display group information.  -G and -o turn this off.  */

static int print_group = 1;

/* Nonzero means print the user and group id's as numbers rather
   than as names.  -n  */

static int numeric_ids;

/* Nonzero means mention the size in blocks of each file.  -s  */

static int print_block_size;

/* Human-readable options for output.  */
static int human_output_opts;

/* The units to use when printing sizes other than file sizes.  */
static uintmax_t output_block_size;

/* Likewise, but for file sizes.  */
static uintmax_t file_output_block_size = 1;

/* Precede each line of long output (per file) with a string like `m,n:'
   where M is the number of characters after the `:' and before the
   filename and N is the length of the filename.  Using this format,
   Emacs' dired mode starts up twice as fast, and can handle all
   strange characters in file names.  */
static int dired;

/* `none' means don't mention the type of files.
   `classify' means mention file types and mark executables.
   `file_type' means mention only file types.

   Controlled by -F, -p, and --indicator-style.  */

enum indicator_style
  {
    none,   /*     --indicator-style=none */
    classify,   /* -F, --indicator-style=classify */
    file_type   /* -p, --indicator-style=file-type */
  };

static enum indicator_style indicator_style;

/* Names of indicator styles.  */
static char const *const indicator_style_args[] =
{
  "none", "classify", "file-type", 0
};

static enum indicator_style const indicator_style_types[]=
{
  none, classify, file_type
};

/* Nonzero means use colors to mark types.  Also define the different
   colors as well as the stuff for the LS_COLORS environment variable.
   The LS_COLORS variable is now in a termcap-like format.  */

static int print_with_color;

enum color_type
  {
    color_never,        /* 0: default or --color=never */
    color_always,       /* 1: --color=always */
    color_if_tty        /* 2: --color=tty */
  };

enum Dereference_symlink
  {
    DEREF_UNDEFINED = 1,
    DEREF_NEVER,
    DEREF_COMMAND_LINE_ARGUMENTS,   /* -H */
    DEREF_COMMAND_LINE_SYMLINK_TO_DIR,  /* the default, in certain cases */
    DEREF_ALWAYS            /* -L */
  };

enum indicator_no
  {
    C_LEFT, C_RIGHT, C_END, C_NORM, C_FILE, C_DIR, C_LINK, C_FIFO, C_SOCK,
    C_BLK, C_CHR, C_MISSING, C_ORPHAN, C_EXEC, C_DOOR
  };

static const char *const indicator_name[]=
  {
    "lc", "rc", "ec", "no", "fi", "di", "ln", "pi", "so",
    "bd", "cd", "mi", "or", "ex", "do", NULL
  };

struct color_ext_type
  {
    struct bin_str ext;     /* The extension we're looking for */
    struct bin_str seq;     /* The sequence to output when we do */
    struct color_ext_type *next;    /* Next in list */
  };

static struct bin_str color_indicator[] =
  {
    { LEN_STR_PAIR ("\033[") },     /* lc: Left of color sequence */
    { LEN_STR_PAIR ("m") },     /* rc: Right of color sequence */
    { 0, NULL },            /* ec: End color (replaces lc+no+rc) */
    { LEN_STR_PAIR ("0") },     /* no: Normal */
    { LEN_STR_PAIR ("0") },     /* fi: File: default */
    { LEN_STR_PAIR ("01;34") },     /* di: Directory: bright blue */
    { LEN_STR_PAIR ("01;36") },     /* ln: Symlink: bright cyan */
    { LEN_STR_PAIR ("33") },        /* pi: Pipe: yellow/brown */
    { LEN_STR_PAIR ("01;35") },     /* so: Socket: bright magenta */
    { LEN_STR_PAIR ("01;33") },     /* bd: Block device: bright yellow */
    { LEN_STR_PAIR ("01;33") },     /* cd: Char device: bright yellow */
    { 0, NULL },            /* mi: Missing file: undefined */
    { 0, NULL },            /* or: Orphanned symlink: undefined */
    { LEN_STR_PAIR ("01;32") },     /* ex: Executable: bright green */
    { LEN_STR_PAIR ("01;35") }      /* do: Door: bright magenta */
  };

/* FIXME: comment  */
static struct color_ext_type *color_ext_list = NULL;

/* Buffer for color sequences */
static char *color_buf;

/* Nonzero means to check for orphaned symbolic link, for displaying
   colors.  */

static int check_symlink_color;

/* Nonzero means mention the inode number of each file.  -i  */

static int print_inode;

/* What to do with symbolic links.  Affected by -d, -F, -H, -l (and
   other options that imply -l), and -L.  */

static enum Dereference_symlink dereference;

/* Nonzero means when a directory is found, display info on its
   contents.  -R  */

static int recursive;

/* Nonzero means when an argument is a directory name, display info
   on it itself.  -d  */

static int immediate_dirs;

/* Nonzero means don't omit files whose names start with `.'.  -A */

static int all_files;

/* Nonzero means don't omit files `.' and `..'
   This flag implies `all_files'.  -a  */

static int really_all_files;

/* A linked list of shell-style globbing patterns.  If a non-argument
   file name matches any of these patterns, it is omitted.
   Controlled by -I.  Multiple -I options accumulate.
   The -B option adds `*~' and `.*~' to this list.  */

struct ignore_pattern
  {
    const char *pattern;
    struct ignore_pattern *next;
  };

static struct ignore_pattern *ignore_patterns;

/* Nonzero means output nongraphic chars in file names as `?'.
   (-q, --hide-control-chars)
   qmark_funny_chars and the quoting style (-Q, --quoting-style=WORD) are
   independent.  The algorithm is: first, obey the quoting style to get a
   string representing the file name;  then, if qmark_funny_chars is set,
   replace all nonprintable chars in that string with `?'.  It's necessary
   to replace nonprintable chars even in quoted strings, because we don't
   want to mess up the terminal if control chars get sent to it, and some
   quoting methods pass through control chars as-is.  */
static int qmark_funny_chars;

/* Quoting options for file and dir name output.  */

static struct quoting_options *filename_quoting_options;
static struct quoting_options *dirname_quoting_options;

/* The number of chars per hardware tab stop.  Setting this to zero
   inhibits the use of TAB characters for separating columns.  -T */
static int tabsize;

/* Nonzero means we are listing the working directory because no
   non-option arguments were given. */

static int dir_defaulted;

/* Nonzero means print each directory name before listing it. */

static int print_dir_name;

/* The line length to use for breaking lines in many-per-line format.
   Can be set with -w.  */

static int line_length;

/* If nonzero, the file listing format requires that stat be called on
   each file. */

static int format_needs_stat;

/* Similar to `format_needs_stat', but set if only the file type is
   needed.  */

static int format_needs_type;

/* strftime formats for non-recent and recent files, respectively, in
   -l output.  */

static char const *long_time_format[2] =
  {
    /* strftime format for non-recent files (older than 6 months), in
       -l output when --time-style=locale is specified.  This should
       contain the year, month and day (at least), in an order that is
       understood by people in your locale's territory.
       Please try to keep the number of used screen columns small,
       because many people work in windows with only 80 columns.  But
       make this as wide as the other string below, for recent files.  */
    N_("%b %e  %Y"),
    /* strftime format for recent files (younger than 6 months), in
       -l output when --time-style=locale is specified.  This should
       contain the month, day and time (at least), in an order that is
       understood by people in your locale's territory.
       Please try to keep the number of used screen columns small,
       because many people work in windows with only 80 columns.  But
       make this as wide as the other string above, for non-recent files.  */
    N_("%b %e %H:%M")
  };

/* The exit status to use if we don't get any fatal errors. */

static int exit_status;

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  AUTHOR_OPTION = CHAR_MAX + 1,
  BLOCK_SIZE_OPTION,
  COLOR_OPTION,
  DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR_OPTION,
  FORMAT_OPTION,
  FULL_TIME_OPTION,
  INDICATOR_STYLE_OPTION,
  QUOTING_STYLE_OPTION,
  SHOW_CONTROL_CHARS_OPTION,
  SI_OPTION,
  SORT_OPTION,
  TIME_OPTION,
  TIME_STYLE_OPTION
};

static struct option const long_options[] =
{
  {"all", no_argument, 0, 'a'},
  {"escape", no_argument, 0, 'b'},
  {"directory", no_argument, 0, 'd'},
  {"dired", no_argument, 0, 'D'},
  {"full-time", no_argument, 0, FULL_TIME_OPTION},
  {"human-readable", no_argument, 0, 'h'},
  {"inode", no_argument, 0, 'i'},
  {"kilobytes", no_argument, 0, 'k'}, /* long form is obsolescent */
  {"numeric-uid-gid", no_argument, 0, 'n'},
  {"no-group", no_argument, 0, 'G'},
  {"hide-control-chars", no_argument, 0, 'q'},
  {"reverse", no_argument, 0, 'r'},
  {"size", no_argument, 0, 's'},
  {"width", required_argument, 0, 'w'},
  {"almost-all", no_argument, 0, 'A'},
  {"ignore-backups", no_argument, 0, 'B'},
  {"classify", no_argument, 0, 'F'},
  {"file-type", no_argument, 0, 'p'},
  {"si", no_argument, 0, SI_OPTION},
  {"dereference-command-line", no_argument, 0, 'H'},
  {"dereference-command-line-symlink-to-dir", no_argument, 0,
   DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR_OPTION},
  {"ignore", required_argument, 0, 'I'},
  {"indicator-style", required_argument, 0, INDICATOR_STYLE_OPTION},
  {"dereference", no_argument, 0, 'L'},
  {"literal", no_argument, 0, 'N'},
  {"quote-name", no_argument, 0, 'Q'},
  {"quoting-style", required_argument, 0, QUOTING_STYLE_OPTION},
  {"recursive", no_argument, 0, 'R'},
  {"format", required_argument, 0, FORMAT_OPTION},
  {"show-control-chars", no_argument, 0, SHOW_CONTROL_CHARS_OPTION},
  {"sort", required_argument, 0, SORT_OPTION},
  {"tabsize", required_argument, 0, 'T'},
  {"time", required_argument, 0, TIME_OPTION},
  {"time-style", required_argument, 0, TIME_STYLE_OPTION},
  {"color", optional_argument, 0, COLOR_OPTION},
  {"block-size", required_argument, 0, BLOCK_SIZE_OPTION},
  {"author", no_argument, 0, AUTHOR_OPTION},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

static char const *const format_args[] =
{
  "verbose", "long", "commas", "horizontal", "across",
  "vertical", "single-column", 0
};

static enum format const format_types[] =
{
  long_format, long_format, with_commas, horizontal, horizontal,
  many_per_line, one_per_line
};

static char const *const sort_args[] =
{
  "none", "time", "size", "extension", "version", 0
};

static enum sort_type const sort_types[] =
{
  sort_none, sort_time, sort_size, sort_extension, sort_version
};

static char const *const time_args[] =
{
  "atime", "access", "use", "ctime", "status", 0
};

static enum time_type const time_types[] =
{
  time_atime, time_atime, time_atime, time_ctime, time_ctime
};

static char const *const color_args[] =
{
  /* force and none are for compatibility with another color-ls version */
  "always", "yes", "force",
  "never", "no", "none",
  "auto", "tty", "if-tty", 0
};

static enum color_type const color_types[] =
{
  color_always, color_always, color_always,
  color_never, color_never, color_never,
  color_if_tty, color_if_tty, color_if_tty
};

/* Information about filling a column.  */
struct column_info
{
  int valid_len;
  int line_len;
  int *col_arr;
};

/* Array with information about column filledness.  */
static struct column_info *column_info;

/* Maximum number of columns ever possible for this display.  */
static int max_idx;

/* The minimum width of a colum is 3: 1 character for the name and 2
   for the separating white space.  */
#define MIN_COLUMN_WIDTH    3


/* This zero-based index is used solely with the --dired option.
   When that option is in effect, this counter is incremented for each
   character of output generated by this program so that the beginning
   and ending indices (in that output) of every file name can be recorded
   and later output themselves.  */
static size_t dired_pos;

#define DIRED_PUTCHAR(c) do {putchar ((c)); ++dired_pos;} while (0)

/* Write S to STREAM and increment DIRED_POS by S_LEN.  */
#define DIRED_FPUTS(s, stream, s_len) \
    do {fputs ((s), (stream)); dired_pos += s_len;} while (0)

/* Like DIRED_FPUTS, but for use when S is a literal string.  */
#define DIRED_FPUTS_LITERAL(s, stream) \
    do {fputs ((s), (stream)); dired_pos += sizeof((s)) - 1;} while (0)

#define DIRED_INDENT()                          \
    do                                  \
      {                                 \
    if (dired)                          \
      DIRED_FPUTS_LITERAL ("  ", stdout);               \
      }                                 \
    while (0)

/* With --dired, store pairs of beginning and ending indices of filenames.  */
static struct obstack dired_obstack;

/* With --dired, store pairs of beginning and ending indices of any
   directory names that appear as headers (just before `total' line)
   for lists of directory entries.  Such directory names are seen when
   listing hierarchies using -R and when a directory is listed with at
   least one other command line argument.  */
static struct obstack subdired_obstack;

/* Save the current index on the specified obstack, OBS.  */
#define PUSH_CURRENT_DIRED_POS(obs)                 \
  do                                    \
    {                                   \
      if (dired)                            \
    obstack_grow ((obs), &dired_pos, sizeof (dired_pos));       \
    }                                   \
  while (0)

/* With -R, this stack is used to help detect directory cycles.
   The device/inode pairs on this stack mirror the pairs in the
   active_dir_set hash table.  */
static struct obstack dev_ino_obstack;

/* Push a pair onto the device/inode stack.  */
#define DEV_INO_PUSH(Dev, Ino)                      \
  do                                    \
    {                                   \
      struct dev_ino *di;                       \
      obstack_blank (&dev_ino_obstack, sizeof (struct dev_ino));    \
      di = -1 + (struct dev_ino *) obstack_next_free (&dev_ino_obstack); \
      di->st_dev = (Dev);                       \
      di->st_ino = (Ino);                       \
    }                                   \
  while (0)

/* Pop a dev/ino struct off the global dev_ino_obstack
   and return that struct.  */
static struct dev_ino
dev_ino_pop (void)
{
  assert (sizeof (struct dev_ino) <= obstack_object_size (&dev_ino_obstack));
  obstack_blank (&dev_ino_obstack, -(int) (sizeof (struct dev_ino)));
  return *(struct dev_ino*) obstack_next_free (&dev_ino_obstack);
}

#define ASSERT_MATCHING_DEV_INO(Name, Di)   \
  do                        \
    {                       \
      struct stat sb;               \
      assert (Name);                \
      assert (0 <= stat (Name, &sb));       \
      assert (sb.st_dev == Di.st_dev);      \
      assert (sb.st_ino == Di.st_ino);      \
    }                       \
  while (0)


/* Write to standard output PREFIX, followed by the quoting style and
   a space-separated list of the integers stored in OS all on one line.  */

static void
dired_dump_obstack (const char *prefix, struct obstack *os)
{
  int n_pos;

  n_pos = obstack_object_size (os) / sizeof (dired_pos);
  if (n_pos > 0)
    {
      int i;
      size_t *pos;

      pos = (size_t *) obstack_finish (os);
      fputs (prefix, stdout);
      for (i = 0; i < n_pos; i++)
    printf (" %lu", (unsigned long) pos[i]);
      putchar ('\n');
    }
}

static unsigned int
dev_ino_hash (void const *x, unsigned int table_size)
{
  struct dev_ino const *p = x;
  return (uintmax_t) p->st_ino % table_size;
}

static bool
dev_ino_compare (void const *x, void const *y)
{
  struct dev_ino const *a = x;
  struct dev_ino const *b = y;
  return SAME_INODE (*a, *b) ? true : false;
}

static void
dev_ino_free (void *x)
{
  free (x);
}

/* Add the device/inode pair (P->st_dev/P->st_ino) to the set of
   active directories.  Return nonzero if there is already a matching
   entry in the table.  Otherwise, return zero.  */

static int
visit_dir (dev_t dev, ino_t ino)
{
  struct dev_ino *ent;
  struct dev_ino *ent_from_table;
  int found_match;

  ent = XMALLOC (struct dev_ino, 1);
  ent->st_ino = ino;
  ent->st_dev = dev;

  /* Attempt to insert this entry into the table.  */
  ent_from_table = hash_insert (active_dir_set, ent);

  if (ent_from_table == NULL)
    {
      /* Insertion failed due to lack of memory.  */
      xalloc_die ();
    }

  found_match = (ent_from_table != ent);

  if (found_match)
    {
      /* ent was not inserted, so free it.  */
      free (ent);
    }

  return found_match;
}

static void
free_pending_ent (struct pending *p)
{
  if (p->name)
    free (p->name);
  if (p->realname)
    free (p->realname);
  free (p);
}

static void
restore_default_color (void)
{
  if (put_indicator_direct (&color_indicator[C_LEFT]) == 0)
    put_indicator_direct (&color_indicator[C_RIGHT]);
}

/* Upon interrupt, suspend, hangup, etc. ensure that the
   terminal text color is restored to the default.  */
static void
sighandler (int sig)
{
#ifndef SA_NOCLDSTOP
  signal (sig, SIG_IGN);
#endif

  restore_default_color ();

  /* SIGTSTP is special, since the application can receive that signal more
     than once.  In this case, don't set the signal handler to the default.
     Instead, just raise the uncatchable SIGSTOP.  */
  if (sig == SIGTSTP)
    {
      sig = SIGSTOP;
    }
  else
    {
#ifdef SA_NOCLDSTOP
      struct sigaction sigact;

      sigact.sa_handler = SIG_DFL;
      sigemptyset (&sigact.sa_mask);
      sigact.sa_flags = 0;
      sigaction (sig, &sigact, NULL);
#else
      signal (sig, SIG_DFL);
#endif
    }

  raise (sig);
}

int
main (int argc, char **argv)
{
  register int i;
  register struct pending *thispend;
  unsigned int n_files;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

#define N_ENTRIES(Array) (sizeof Array / sizeof *(Array))
  assert (N_ENTRIES (color_indicator) + 1 == N_ENTRIES (indicator_name));

  exit_status = 0;
  dir_defaulted = 1;
  print_dir_name = 1;
  pending_dirs = 0;

  i = decode_switches (argc, argv);

  if (print_with_color)
    parse_ls_color ();

  /* Test print_with_color again, because the call to parse_ls_color
     may have just reset it -- e.g., if LS_COLORS is invalid.  */
  if (print_with_color)
    {
      prep_non_filename_text ();
      /* Avoid following symbolic links when possible.  */
      if (color_indicator[C_ORPHAN].string != NULL
      || (color_indicator[C_MISSING].string != NULL
          && format == long_format))
    check_symlink_color = 1;

      {
    unsigned j;
    static int const sigs[] = { SIGHUP, SIGINT, SIGPIPE,
                    SIGQUIT, SIGTERM, SIGTSTP };
    unsigned nsigs = sizeof sigs / sizeof *sigs;
#ifdef SA_NOCLDSTOP
    struct sigaction oldact, newact;
    sigset_t caught_signals;

    sigemptyset (&caught_signals);
    for (j = 0; j < nsigs; j++)
      sigaddset (&caught_signals, sigs[j]);
    newact.sa_handler = sighandler;
    newact.sa_mask = caught_signals;
    newact.sa_flags = 0;
#endif

    for (j = 0; j < nsigs; j++)
      {
        int sig = sigs[j];
#ifdef SA_NOCLDSTOP
        sigaction (sig, NULL, &oldact);
        if (oldact.sa_handler != SIG_IGN)
          sigaction (sig, &newact, NULL);
#else
        if (signal (sig, SIG_IGN) != SIG_IGN)
          signal (sig, sighandler);
#endif
      }
      }
    }

  if (dereference == DEREF_UNDEFINED)
    dereference = ((immediate_dirs
            || indicator_style == classify
            || format == long_format)
           ? DEREF_NEVER
           : DEREF_COMMAND_LINE_SYMLINK_TO_DIR);

  /* When using -R, initialize a data structure we'll use to
     detect any directory cycles.  */
  if (recursive)
    {
      active_dir_set = hash_initialize (INITIAL_TABLE_SIZE, NULL,
                    dev_ino_hash,
                    dev_ino_compare,
                    dev_ino_free);
      if (active_dir_set == NULL)
    xalloc_die ();

      obstack_init (&dev_ino_obstack);
    }

  format_needs_stat = sort_type == sort_time || sort_type == sort_size
    || format == long_format
    || dereference == DEREF_ALWAYS
    || print_block_size || print_inode;
  format_needs_type = (format_needs_stat == 0
               && (recursive || print_with_color
               || indicator_style != none));

  if (dired)
    {
      obstack_init (&dired_obstack);
      obstack_init (&subdired_obstack);
    }

  nfiles = 100;
  files = XMALLOC (struct fileinfo, nfiles);
  files_index = 0;

  clear_files ();

  n_files = argc - i;
  if (0 < n_files)
    dir_defaulted = 0;

  for (; i < argc; i++)
    {
      gobble_file (argv[i], unknown, 1, "");
    }

  if (dir_defaulted)
    {
      if (immediate_dirs)
    gobble_file (".", directory, 1, "");
      else
    queue_directory (".", 0);
    }

  if (files_index)
    {
      sort_files ();
      if (!immediate_dirs)
    extract_dirs_from_files ("", 0);
      /* `files_index' might be zero now.  */
    }

  /* In the following if/else blocks, it is sufficient to test `pending_dirs'
     (and not pending_dirs->name) because there may be no markers in the queue
     at this point.  A marker may be enqueued when extract_dirs_from_files is
     called with a non-empty string or via print_dir.  */
  if (files_index)
    {
      print_current_files ();
      if (pending_dirs)
    DIRED_PUTCHAR ('\n');
    }
  else if (n_files <= 1 && pending_dirs && pending_dirs->next == 0)
    print_dir_name = 0;

  while (pending_dirs)
    {
      thispend = pending_dirs;
      pending_dirs = pending_dirs->next;

      if (LOOP_DETECT)
    {
      if (thispend->name == NULL)
        {
          /* thispend->name == NULL means this is a marker entry
         indicating we've finished processing the directory.
         Use its dev/ino numbers to remove the corresponding
         entry from the active_dir_set hash table.  */
          struct dev_ino di = dev_ino_pop ();
          struct dev_ino *found = hash_delete (active_dir_set, &di);
          /* ASSERT_MATCHING_DEV_INO (thispend->realname, di); */
          assert (found);
          dev_ino_free (found);
          free_pending_ent (thispend);
          continue;
        }
    }

      print_dir (thispend->name, thispend->realname);

      free_pending_ent (thispend);
      print_dir_name = 1;
    }

  if (dired)
    {
      /* No need to free these since we're about to exit.  */
      dired_dump_obstack ("//DIRED//", &dired_obstack);
      dired_dump_obstack ("//SUBDIRED//", &subdired_obstack);
      printf ("//DIRED-OPTIONS// --quoting-style=%s\n",
          quoting_style_args[get_quoting_style (filename_quoting_options)]);
    }

  /* Restore default color before exiting */
  if (print_with_color)
    {
      put_indicator (&color_indicator[C_LEFT]);
      put_indicator (&color_indicator[C_RIGHT]);
    }

  if (LOOP_DETECT)
    {
      assert (hash_get_n_entries (active_dir_set) == 0);
      hash_free (active_dir_set);
    }

  exit (exit_status);
}

/* Set all the option flags according to the switches specified.
   Return the index of the first non-option argument.  */

static int
decode_switches (int argc, char **argv)
{
  int c;
  char *time_style_option = 0;

  /* Record whether there is an option specifying sort type.  */
  int sort_type_specified = 0;

  qmark_funny_chars = 0;

  /* initialize all switches to default settings */

  switch (ls_mode)
    {
    case LS_MULTI_COL:
      /* This is for the `dir' program.  */
      format = many_per_line;
      set_quoting_style (NULL, escape_quoting_style);
      break;

    case LS_LONG_FORMAT:
      /* This is for the `vdir' program.  */
      format = long_format;
      set_quoting_style (NULL, escape_quoting_style);
      break;

    case LS_LS:
      /* This is for the `ls' program.  */
      if (isatty (STDOUT_FILENO))
    {
      format = many_per_line;
      /* See description of qmark_funny_chars, above.  */
      qmark_funny_chars = 1;
    }
      else
    {
      format = one_per_line;
      qmark_funny_chars = 0;
    }
      break;

    default:
      abort ();
    }

  time_type = time_mtime;
  sort_type = sort_name;
  sort_reverse = 0;
  numeric_ids = 0;
  print_block_size = 0;
  indicator_style = none;
  print_inode = 0;
  dereference = DEREF_UNDEFINED;
  recursive = 0;
  immediate_dirs = 0;
  all_files = 0;
  really_all_files = 0;
  ignore_patterns = 0;

  /* FIXME: put this in a function.  */
  {
    char const *q_style = getenv ("QUOTING_STYLE");
    if (q_style)
      {
    int i = ARGMATCH (q_style, quoting_style_args, quoting_style_vals);
    if (0 <= i)
      set_quoting_style (NULL, quoting_style_vals[i]);
    else
      error (0, 0,
     _("ignoring invalid value of environment variable QUOTING_STYLE: %s"),
         quotearg (q_style));
      }
  }

  {
    char const *ls_block_size = getenv ("LS_BLOCK_SIZE");
    human_output_opts = human_options (ls_block_size, false,
                       &output_block_size);
    if (ls_block_size || getenv ("BLOCK_SIZE"))
      file_output_block_size = output_block_size;
  }

  line_length = 80;
  {
    char const *p = getenv ("COLUMNS");
    if (p && *p)
      {
    long int tmp_long;
    if (xstrtol (p, NULL, 0, &tmp_long, NULL) == LONGINT_OK
        && 0 < tmp_long && tmp_long <= INT_MAX)
      {
        line_length = (int) tmp_long;
      }
    else
      {
        error (0, 0,
           _("ignoring invalid width in environment variable COLUMNS: %s"),
           quotearg (p));
      }
      }
  }

#ifdef TIOCGWINSZ
  {
    struct winsize ws;

    if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && ws.ws_col != 0)
      line_length = ws.ws_col;
  }
#endif

  /* Using the TABSIZE environment variable is not POSIX-approved.
     Ignore it when POSIXLY_CORRECT is set.  */
  {
    char const *p;
    tabsize = 8;
    if (!getenv ("POSIXLY_CORRECT") && (p = getenv ("TABSIZE")))
      {
    long int tmp_long;
    if (xstrtol (p, NULL, 0, &tmp_long, NULL) == LONGINT_OK
        && 0 <= tmp_long && tmp_long <= INT_MAX)
      {
        tabsize = (int) tmp_long;
      }
    else
      {
        error (0, 0,
         _("ignoring invalid tab size in environment variable TABSIZE: %s"),
           quotearg (p));
      }
      }
  }

  while ((c = getopt_long (argc, argv,
               "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UX1",
               long_options, NULL)) != -1)
    {
      switch (c)
    {
    case 0:
      break;

    case 'a':
      all_files = 1;
      really_all_files = 1;
      break;

    case 'b':
      set_quoting_style (NULL, escape_quoting_style);
      break;

    case 'c':
      time_type = time_ctime;
      break;

    case 'd':
      immediate_dirs = 1;
      break;

    case 'f':
      /* Same as enabling -a -U and disabling -l -s.  */
      all_files = 1;
      really_all_files = 1;
      sort_type = sort_none;
      sort_type_specified = 1;
      /* disable -l */
      if (format == long_format)
        format = (isatty (STDOUT_FILENO) ? many_per_line : one_per_line);
      print_block_size = 0; /* disable -s */
      print_with_color = 0; /* disable --color */
      break;

    case 'g':
      format = long_format;
      print_owner = 0;
      break;

    case 'h':
      human_output_opts = human_autoscale | human_SI | human_base_1024;
      file_output_block_size = output_block_size = 1;
      break;

    case 'i':
      print_inode = 1;
      break;

    case 'k':
      human_output_opts = 0;
      file_output_block_size = output_block_size = 1024;
      break;

    case 'l':
      format = long_format;
      break;

    case 'm':
      format = with_commas;
      break;

    case 'n':
      numeric_ids = 1;
      format = long_format;
      break;

    case 'o':  /* Just like -l, but don't display group info.  */
      format = long_format;
      print_group = 0;
      break;

    case 'p':
      indicator_style = file_type;
      break;

    case 'q':
      qmark_funny_chars = 1;
      break;

    case 'r':
      sort_reverse = 1;
      break;

    case 's':
      print_block_size = 1;
      break;

    case 't':
      sort_type = sort_time;
      sort_type_specified = 1;
      break;

    case 'u':
      time_type = time_atime;
      break;

    case 'v':
      sort_type = sort_version;
      sort_type_specified = 1;
      break;

    case 'w':
      {
        long int tmp_long;
        if (xstrtol (optarg, NULL, 0, &tmp_long, NULL) != LONGINT_OK
        || tmp_long <= 0 || tmp_long > INT_MAX)
          error (EXIT_FAILURE, 0, _("invalid line width: %s"),
             quotearg (optarg));
        line_length = (int) tmp_long;
        break;
      }

    case 'x':
      format = horizontal;
      break;

    case 'A':
      really_all_files = 0;
      all_files = 1;
      break;

    case 'B':
      add_ignore_pattern ("*~");
      add_ignore_pattern (".*~");
      break;

    case 'C':
      format = many_per_line;
      break;

    case 'D':
      dired = 1;
      break;

    case 'F':
      indicator_style = classify;
      break;

    case 'G':       /* inhibit display of group info */
      print_group = 0;
      break;

    case 'H':
      dereference = DEREF_COMMAND_LINE_ARGUMENTS;
      break;

    case DEREFERENCE_COMMAND_LINE_SYMLINK_TO_DIR_OPTION:
      dereference = DEREF_COMMAND_LINE_SYMLINK_TO_DIR;
      break;

    case 'I':
      add_ignore_pattern (optarg);
      break;

    case 'L':
      dereference = DEREF_ALWAYS;
      break;

    case 'N':
      set_quoting_style (NULL, literal_quoting_style);
      break;

    case 'Q':
      set_quoting_style (NULL, c_quoting_style);
      break;

    case 'R':
      recursive = 1;
      break;

    case 'S':
      sort_type = sort_size;
      sort_type_specified = 1;
      break;

    case 'T':
      {
        long int tmp_long;
        if (xstrtol (optarg, NULL, 0, &tmp_long, NULL) != LONGINT_OK
        || tmp_long < 0 || tmp_long > INT_MAX)
          error (EXIT_FAILURE, 0, _("invalid tab size: %s"),
             quotearg (optarg));
        tabsize = (int) tmp_long;
        break;
      }

    case 'U':
      sort_type = sort_none;
      sort_type_specified = 1;
      break;

    case 'X':
      sort_type = sort_extension;
      sort_type_specified = 1;
      break;

    case '1':
      /* -1 has no effect after -l.  */
      if (format != long_format)
        format = one_per_line;
      break;

        case AUTHOR_OPTION:
          print_author = true;
          break;

    case SORT_OPTION:
      sort_type = XARGMATCH ("--sort", optarg, sort_args, sort_types);
      sort_type_specified = 1;
      break;

    case TIME_OPTION:
      time_type = XARGMATCH ("--time", optarg, time_args, time_types);
      break;

    case FORMAT_OPTION:
      format = XARGMATCH ("--format", optarg, format_args, format_types);
      break;

    case FULL_TIME_OPTION:
      format = long_format;
      time_style_option = "full-iso";
      break;

    case COLOR_OPTION:
      {
        int i;
        if (optarg)
          i = XARGMATCH ("--color", optarg, color_args, color_types);
        else
          /* Using --color with no argument is equivalent to using
         --color=always.  */
          i = color_always;

        print_with_color = (i == color_always
                || (i == color_if_tty
                    && isatty (STDOUT_FILENO)));

        if (print_with_color)
          {
        /* Don't use TAB characters in output.  Some terminal
           emulators can't handle the combination of tabs and
           color codes on the same line.  */
        tabsize = 0;
          }
        break;
      }

    case INDICATOR_STYLE_OPTION:
      indicator_style = XARGMATCH ("--indicator-style", optarg,
                       indicator_style_args,
                       indicator_style_types);
      break;

    case QUOTING_STYLE_OPTION:
      set_quoting_style (NULL,
                 XARGMATCH ("--quoting-style", optarg,
                    quoting_style_args,
                    quoting_style_vals));
      break;

    case TIME_STYLE_OPTION:
      time_style_option = optarg;
      break;

    case SHOW_CONTROL_CHARS_OPTION:
      qmark_funny_chars = 0;
      break;

    case BLOCK_SIZE_OPTION:
      human_output_opts = human_options (optarg, true, &output_block_size);
      file_output_block_size = output_block_size;
      break;

    case SI_OPTION:
      human_output_opts = human_autoscale | human_SI;
      file_output_block_size = output_block_size = 1;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
    }

  filename_quoting_options = clone_quoting_options (NULL);
  if (get_quoting_style (filename_quoting_options) == escape_quoting_style)
    set_char_quoting (filename_quoting_options, ' ', 1);
  if (indicator_style != none)
    {
      char const *p;
      for (p = "*=@|" + (int) indicator_style - 1;  *p;  p++)
    set_char_quoting (filename_quoting_options, *p, 1);
    }

  dirname_quoting_options = clone_quoting_options (NULL);
  set_char_quoting (dirname_quoting_options, ':', 1);

  /* --dired is meaningful only with --format=long (-l).
     Otherwise, ignore it.  FIXME: warn about this?
     Alternatively, make --dired imply --format=long?  */
  if (dired && format != long_format)
    dired = 0;

  /* If -c or -u is specified and not -l (or any other option that implies -l),
     and no sort-type was specified, then sort by the ctime (-c) or atime (-u).
     The behavior of ls when using either -c or -u but with neither -l nor -t
     appears to be unspecified by POSIX.  So, with GNU ls, `-u' alone means
     sort by atime (this is the one that's not specified by the POSIX spec),
     -lu means show atime and sort by name, -lut means show atime and sort
     by atime.  */

  if ((time_type == time_ctime || time_type == time_atime)
      && !sort_type_specified && format != long_format)
    {
      sort_type = sort_time;
    }

  if (format == long_format)
    {
      char *style = time_style_option;
      static char const posix_prefix[] = "posix-";

      if (! style)
    if (! (style = getenv ("TIME_STYLE")))
      style = "posix-long-iso";

      while (strncmp (style, posix_prefix, sizeof posix_prefix - 1) == 0)
    {
      if (! hard_locale (LC_TIME))
        return optind;
      style += sizeof posix_prefix - 1;
    }

      if (*style == '+')
    {
      char *p0 = style + 1;
      char *p1 = strchr (p0, '\n');
      if (! p1)
        p1 = p0;
      else
        {
          if (strchr (p1 + 1, '\n'))
        error (EXIT_FAILURE, 0, _("invalid time style format %s"),
               quote (p0));
          *p1++ = '\0';
        }
      long_time_format[0] = p0;
      long_time_format[1] = p1;
    }
      else
    switch (XARGMATCH ("time style", style,
               time_style_args,
               time_style_types))
      {
      case full_iso_time_style:
        long_time_format[0] = long_time_format[1] =
          "%Y-%m-%d %H:%M:%S.%N %z";
        break;

      case long_iso_time_style:
        long_time_format[0] = long_time_format[1] = "%Y-%m-%d %H:%M";
        break;

      case iso_time_style:
        long_time_format[0] = "%Y-%m-%d ";
        long_time_format[1] = "%m-%d %H:%M";
        break;

      case locale_time_style:
        if (hard_locale (LC_TIME))
          {
        unsigned int i;
        for (i = 0; i < 2; i++)
          long_time_format[i] =
            dcgettext (NULL, long_time_format[i], LC_TIME);
          }
      }
    }

  return optind;
}

/* Parse a string as part of the LS_COLORS variable; this may involve
   decoding all kinds of escape characters.  If equals_end is set an
   unescaped equal sign ends the string, otherwise only a : or \0
   does.  Returns the number of characters output, or -1 on failure.

   The resulting string is *not* null-terminated, but may contain
   embedded nulls.

   Note that both dest and src are char **; on return they point to
   the first free byte after the array and the character that ended
   the input string, respectively.  */

static int
get_funky_string (char **dest, const char **src, int equals_end)
{
  int num;          /* For numerical codes */
  int count;            /* Something to count with */
  enum {
    ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR
  } state;
  const char *p;
  char *q;

  p = *src;         /* We don't want to double-indirect */
  q = *dest;            /* the whole darn time.  */

  count = 0;            /* No characters counted in yet.  */
  num = 0;

  state = ST_GND;       /* Start in ground state.  */
  while (state < ST_END)
    {
      switch (state)
    {
    case ST_GND:        /* Ground state (no escapes) */
      switch (*p)
        {
        case ':':
        case '\0':
          state = ST_END;   /* End of string */
          break;
        case '\\':
          state = ST_BACKSLASH; /* Backslash scape sequence */
          ++p;
          break;
        case '^':
          state = ST_CARET; /* Caret escape */
          ++p;
          break;
        case '=':
          if (equals_end)
        {
          state = ST_END; /* End */
          break;
        }
          /* else fall through */
        default:
          *(q++) = *(p++);
          ++count;
          break;
        }
      break;

    case ST_BACKSLASH:  /* Backslash escaped character */
      switch (*p)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
          state = ST_OCTAL; /* Octal sequence */
          num = *p - '0';
          break;
        case 'x':
        case 'X':
          state = ST_HEX;   /* Hex sequence */
          num = 0;
          break;
        case 'a':       /* Bell */
          num = 7;      /* Not all C compilers know what \a means */
          break;
        case 'b':       /* Backspace */
          num = '\b';
          break;
        case 'e':       /* Escape */
          num = 27;
          break;
        case 'f':       /* Form feed */
          num = '\f';
          break;
        case 'n':       /* Newline */
          num = '\n';
          break;
        case 'r':       /* Carriage return */
          num = '\r';
          break;
        case 't':       /* Tab */
          num = '\t';
          break;
        case 'v':       /* Vtab */
          num = '\v';
          break;
        case '?':       /* Delete */
              num = 127;
          break;
        case '_':       /* Space */
          num = ' ';
          break;
        case '\0':      /* End of string */
          state = ST_ERROR; /* Error! */
          break;
        default:        /* Escaped character like \ ^ : = */
          num = *p;
          break;
        }
      if (state == ST_BACKSLASH)
        {
          *(q++) = num;
          ++count;
          state = ST_GND;
        }
      ++p;
      break;

    case ST_OCTAL:      /* Octal sequence */
      if (*p < '0' || *p > '7')
        {
          *(q++) = num;
          ++count;
          state = ST_GND;
        }
      else
        num = (num << 3) + (*(p++) - '0');
      break;

    case ST_HEX:        /* Hex sequence */
      switch (*p)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
          num = (num << 4) + (*(p++) - '0');
          break;
        case 'a':
        case 'b':
        case 'c':
        case 'd':
        case 'e':
        case 'f':
          num = (num << 4) + (*(p++) - 'a') + 10;
          break;
        case 'A':
        case 'B':
        case 'C':
        case 'D':
        case 'E':
        case 'F':
          num = (num << 4) + (*(p++) - 'A') + 10;
          break;
        default:
          *(q++) = num;
          ++count;
          state = ST_GND;
          break;
        }
      break;

    case ST_CARET:      /* Caret escape */
      state = ST_GND;   /* Should be the next state... */
      if (*p >= '@' && *p <= '~')
        {
          *(q++) = *(p++) & 037;
          ++count;
        }
      else if (*p == '?')
        {
          *(q++) = 127;
          ++count;
        }
      else
        state = ST_ERROR;
      break;

    default:
      abort ();
    }
    }

  *dest = q;
  *src = p;

  return state == ST_ERROR ? -1 : count;
}

static void
parse_ls_color (void)
{
  const char *p;        /* Pointer to character being parsed */
  char *buf;            /* color_buf buffer pointer */
  int state;            /* State of parser */
  int ind_no;           /* Indicator number */
  char label[3];        /* Indicator label */
  struct color_ext_type *ext;   /* Extension we are working on */

  if ((p = getenv ("LS_COLORS")) == NULL || *p == '\0')
    return;

  ext = NULL;
  strcpy (label, "??");

  /* This is an overly conservative estimate, but any possible
     LS_COLORS string will *not* generate a color_buf longer than
     itself, so it is a safe way of allocating a buffer in
     advance.  */
  buf = color_buf = xstrdup (p);

  state = 1;
  while (state > 0)
    {
      switch (state)
    {
    case 1:     /* First label character */
      switch (*p)
        {
        case ':':
          ++p;
          break;

        case '*':
          /* Allocate new extension block and add to head of
         linked list (this way a later definition will
         override an earlier one, which can be useful for
         having terminal-specific defs override global).  */

          ext = XMALLOC (struct color_ext_type, 1);
          ext->next = color_ext_list;
          color_ext_list = ext;

          ++p;
          ext->ext.string = buf;

          state = (ext->ext.len =
               get_funky_string (&buf, &p, 1)) < 0 ? -1 : 4;
          break;

        case '\0':
          state = 0;    /* Done! */
          break;

        default:    /* Assume it is file type label */
          label[0] = *(p++);
          state = 2;
          break;
        }
      break;

    case 2:     /* Second label character */
      if (*p)
        {
          label[1] = *(p++);
          state = 3;
        }
      else
        state = -1; /* Error */
      break;

    case 3:     /* Equal sign after indicator label */
      state = -1;   /* Assume failure... */
      if (*(p++) == '=')/* It *should* be... */
        {
          for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
        {
          if (STREQ (label, indicator_name[ind_no]))
            {
              color_indicator[ind_no].string = buf;
              state = ((color_indicator[ind_no].len =
                get_funky_string (&buf, &p, 0)) < 0 ? -1 : 1);
              break;
            }
        }
          if (state == -1)
        error (0, 0, _("unrecognized prefix: %s"), quotearg (label));
        }
     break;

    case 4:     /* Equal sign after *.ext */
      if (*(p++) == '=')
        {
          ext->seq.string = buf;
          state = (ext->seq.len =
               get_funky_string (&buf, &p, 0)) < 0 ? -1 : 1;
        }
      else
        state = -1;
      break;
    }
    }

  if (state < 0)
    {
      struct color_ext_type *e;
      struct color_ext_type *e2;

      error (0, 0,
         _("unparsable value for LS_COLORS environment variable"));
      free (color_buf);
      for (e = color_ext_list; e != NULL; /* empty */)
    {
      e2 = e;
      e = e->next;
      free (e2);
    }
      print_with_color = 0;
    }

  if (color_indicator[C_LINK].len == 6
      && !strncmp (color_indicator[C_LINK].string, "target", 6))
    color_symlink_as_referent = 1;
}

/* Request that the directory named NAME have its contents listed later.
   If REALNAME is nonzero, it will be used instead of NAME when the
   directory name is printed.  This allows symbolic links to directories
   to be treated as regular directories but still be listed under their
   real names.  NAME == NULL is used to insert a marker entry for the
   directory named in REALNAME.
   If F is non-NULL, we use its dev/ino information to save
   a call to stat -- when doing a recursive (-R) traversal.  */

static void
queue_directory (const char *name, const char *realname)
{
  struct pending *new;

  new = XMALLOC (struct pending, 1);
  new->realname = realname ? xstrdup (realname) : NULL;
  new->name = name ? xstrdup (name) : NULL;
  new->next = pending_dirs;
  pending_dirs = new;
}

/* Read directory `name', and list the files in it.
   If `realname' is nonzero, print its name instead of `name';
   this is used for symbolic links to directories. */

static void
print_dir (const char *name, const char *realname)
{
  register DIR *dirp;
  register struct dirent *next;
  register uintmax_t total_blocks = 0;
  static int first = 1;

  errno = 0;
  dirp = opendir (name);
  if (!dirp)
    {
      error (0, errno, "%s", quotearg_colon (name));
      exit_status = 1;
      return;
    }

  if (LOOP_DETECT)
    {
      struct stat dir_stat;
      int fd = dirfd (dirp);

      /* If dirfd failed, endure the overhead of using stat.  */
      if ((0 <= fd
       ? fstat (fd, &dir_stat)
       : stat (name, &dir_stat)) < 0)
    {
      error (0, errno, _("cannot determine device and inode of %s"),
         quotearg_colon (name));
      exit_status = 1;
      return;
    }

      /* If we've already visited this dev/inode pair, warn that
     we've found a loop, and do not process this directory.  */
      if (visit_dir (dir_stat.st_dev, dir_stat.st_ino))
    {
      error (0, 0, _("not listing already-listed directory: %s"),
         quotearg_colon (name));
      return;
    }

      DEV_INO_PUSH (dir_stat.st_dev, dir_stat.st_ino);
    }

  /* Read the directory entries, and insert the subfiles into the `files'
     table.  */

  clear_files ();

  while (1)
    {
      /* Set errno to zero so we can distinguish between a readdir failure
     and when readdir simply finds that there are no more entries.  */
      errno = 0;
      if ((next = readdir (dirp)) == NULL)
    {
      if (errno)
        {
          /* Save/restore errno across closedir call.  */
          int e = errno;
          closedir (dirp);
          errno = e;

          /* Arrange to give a diagnostic after exiting this loop.  */
          dirp = NULL;
        }
      break;
    }

      if (file_interesting (next))
    {
      enum filetype type = unknown;

#if HAVE_STRUCT_DIRENT_D_TYPE
      if (next->d_type == DT_BLK
          || next->d_type == DT_CHR
          || next->d_type == DT_DIR
          || next->d_type == DT_FIFO
          || next->d_type == DT_LNK
          || next->d_type == DT_REG
          || next->d_type == DT_SOCK)
        type = next->d_type;
#endif
      total_blocks += gobble_file (next->d_name, type, 0, name);
    }
    }

  if (dirp == NULL || CLOSEDIR (dirp))
    {
      error (0, errno, _("reading directory %s"), quotearg_colon (name));
      exit_status = 1;
      /* Don't return; print whatever we got. */
    }

  /* Sort the directory contents.  */
  sort_files ();

  /* If any member files are subdirectories, perhaps they should have their
     contents listed rather than being mentioned here as files.  */

  if (recursive)
    extract_dirs_from_files (name, 1);

  if (recursive || print_dir_name)
    {
      if (!first)
    DIRED_PUTCHAR ('\n');
      first = 0;
      DIRED_INDENT ();
      PUSH_CURRENT_DIRED_POS (&subdired_obstack);
      dired_pos += quote_name (stdout, realname ? realname : name,
                   dirname_quoting_options, NULL);
      PUSH_CURRENT_DIRED_POS (&subdired_obstack);
      DIRED_FPUTS_LITERAL (":\n", stdout);
    }

  if (format == long_format || print_block_size)
    {
      const char *p;
      char buf[LONGEST_HUMAN_READABLE + 1];

      DIRED_INDENT ();
      p = _("total");
      DIRED_FPUTS (p, stdout, strlen (p));
      DIRED_PUTCHAR (' ');
      p = human_readable (total_blocks, buf, human_output_opts,
              ST_NBLOCKSIZE, output_block_size);
      DIRED_FPUTS (p, stdout, strlen (p));
      DIRED_PUTCHAR ('\n');
    }

  if (files_index)
    print_current_files ();
}

/* Add `pattern' to the list of patterns for which files that match are
   not listed.  */

static void
add_ignore_pattern (const char *pattern)
{
  register struct ignore_pattern *ignore;

  ignore = XMALLOC (struct ignore_pattern, 1);
  ignore->pattern = pattern;
  /* Add it to the head of the linked list. */
  ignore->next = ignore_patterns;
  ignore_patterns = ignore;
}

/* Return nonzero if the file in `next' should be listed. */

static int
file_interesting (const struct dirent *next)
{
  register struct ignore_pattern *ignore;

  for (ignore = ignore_patterns; ignore; ignore = ignore->next)
    if (fnmatch (ignore->pattern, next->d_name, FNM_PERIOD) == 0)
      return 0;

  if (really_all_files
      || next->d_name[0] != '.'
      || (all_files
      && next->d_name[1] != '\0'
      && (next->d_name[1] != '.' || next->d_name[2] != '\0')))
    return 1;

  return 0;
}

/* Enter and remove entries in the table `files'.  */

/* Empty the table of files. */

static void
clear_files (void)
{
  register int i;

  for (i = 0; i < files_index; i++)
    {
      free (files[i].name);
      if (files[i].linkname)
    free (files[i].linkname);
    }

  files_index = 0;
  block_size_size = 4;
}

/* Add a file to the current table of files.
   Verify that the file exists, and print an error message if it does not.
   Return the number of blocks that the file occupies.  */

static uintmax_t
gobble_file (const char *name, enum filetype type, int explicit_arg,
         const char *dirname)
{
  register uintmax_t blocks;
  register char *path;

  if (files_index == nfiles)
    {
      nfiles *= 2;
      files = XREALLOC (files, struct fileinfo, nfiles);
    }

  files[files_index].linkname = 0;
  files[files_index].linkmode = 0;
  files[files_index].linkok = 0;

  if (explicit_arg
      || format_needs_stat
      || (format_needs_type
      && (type == unknown

          /* FIXME: remove this disjunct.
         I don't think we care about symlinks here, but for now
         this won't make a big performance difference.  */
          || type == symbolic_link

          /* --indicator-style=classify (aka -F)
         requires that we stat each regular file
         to see if it's executable.  */
          || (type == normal && (indicator_style == classify
                     /* This is so that --color ends up
                    highlighting files with the executable
                    bit set even when options like -F are
                    not specified.  */
                     || print_with_color)))))

    {
      /* `path' is the absolute pathname of this file. */
      int err;

      if (name[0] == '/' || dirname[0] == 0)
    path = (char *) name;
      else
    {
      path = (char *) alloca (strlen (name) + strlen (dirname) + 2);
      attach (path, dirname, name);
    }

      switch (dereference)
    {
    case DEREF_ALWAYS:
      err = stat (path, &files[files_index].stat);
      break;

    case DEREF_COMMAND_LINE_ARGUMENTS:
    case DEREF_COMMAND_LINE_SYMLINK_TO_DIR:
      if (explicit_arg)
        {
          int need_lstat;
          err = stat (path, &files[files_index].stat);

          if (dereference == DEREF_COMMAND_LINE_ARGUMENTS)
        break;

          need_lstat = (err < 0
                ? errno == ENOENT
                : ! S_ISDIR (files[files_index].stat.st_mode));
          if (!need_lstat)
        break;

          /* stat failed because of ENOENT, maybe indicating a dangling
         symlink.  Or stat succeeded, PATH does not refer to a
         directory, and --dereference-command-line-symlink-to-dir is
         in effect.  Fall through so that we call lstat instead.  */
        }

    default: /* DEREF_NEVER */
      err = lstat (path, &files[files_index].stat);
      break;
    }

      if (err < 0)
    {
      error (0, errno, "%s", quotearg_colon (path));
      exit_status = 1;
      return 0;
    }

#if HAVE_ACL
      if (format == long_format)
    {
      int n = file_has_acl (path, &files[files_index].stat);
      files[files_index].have_acl = (0 < n);
      if (n < 0)
        error (0, errno, "%s", quotearg_colon (path));
    }
#endif

      if (S_ISLNK (files[files_index].stat.st_mode)
      && (format == long_format || check_symlink_color))
    {
      char *linkpath;
      struct stat linkstats;

      get_link_name (path, &files[files_index]);
      linkpath = make_link_path (path, files[files_index].linkname);

      /* Avoid following symbolic links when possible, ie, when
         they won't be traced and when no indicator is needed. */
      if (linkpath
          && (indicator_style != none || check_symlink_color)
          && stat (linkpath, &linkstats) == 0)
        {
          files[files_index].linkok = 1;

          /* Symbolic links to directories that are mentioned on the
             command line are automatically traced if not being
             listed as files.  */
          if (!explicit_arg || format == long_format
          || !S_ISDIR (linkstats.st_mode))
        {
          /* Get the linked-to file's mode for the filetype indicator
             in long listings.  */
          files[files_index].linkmode = linkstats.st_mode;
          files[files_index].linkok = 1;
        }
        }
      if (linkpath)
        free (linkpath);
    }

      if (S_ISLNK (files[files_index].stat.st_mode))
    files[files_index].filetype = symbolic_link;
      else if (S_ISDIR (files[files_index].stat.st_mode))
    {
      if (explicit_arg && !immediate_dirs)
        files[files_index].filetype = arg_directory;
      else
        files[files_index].filetype = directory;
    }
      else
    files[files_index].filetype = normal;

      blocks = ST_NBLOCKS (files[files_index].stat);
      {
    char buf[LONGEST_HUMAN_READABLE + 1];
    int len = strlen (human_readable (blocks, buf, human_output_opts,
                      ST_NBLOCKSIZE, output_block_size));
    if (block_size_size < len)
      block_size_size = len < 7 ? len : 7;
      }
    }
  else
    {
      files[files_index].filetype = type;
#if HAVE_STRUCT_DIRENT_D_TYPE
      files[files_index].stat.st_mode = DTTOIF (type);
#endif
      blocks = 0;
    }

  files[files_index].name = xstrdup (name);
  files_index++;

  return blocks;
}

#ifdef S_ISLNK

/* Put the name of the file that `filename' is a symbolic link to
   into the `linkname' field of `f'. */

static void
get_link_name (const char *filename, struct fileinfo *f)
{
  f->linkname = xreadlink (filename);
  if (f->linkname == NULL)
    {
      error (0, errno, _("cannot read symbolic link %s"),
         quotearg_colon (filename));
      exit_status = 1;
    }
}

/* If `linkname' is a relative path and `path' contains one or more
   leading directories, return `linkname' with those directories
   prepended; otherwise, return a copy of `linkname'.
   If `linkname' is zero, return zero. */

static char *
make_link_path (const char *path, const char *linkname)
{
  char *linkbuf;
  size_t bufsiz;

  if (linkname == 0)
    return 0;

  if (*linkname == '/')
    return xstrdup (linkname);

  /* The link is to a relative path.  Prepend any leading path
     in `path' to the link name. */
  linkbuf = strrchr (path, '/');
  if (linkbuf == 0)
    return xstrdup (linkname);

  bufsiz = linkbuf - path + 1;
  linkbuf = xmalloc (bufsiz + strlen (linkname) + 1);
  strncpy (linkbuf, path, bufsiz);
  strcpy (linkbuf + bufsiz, linkname);
  return linkbuf;
}
#endif

/* Return nonzero if base_name (NAME) ends in `.' or `..'
   This is so we don't try to recurse on `././././. ...' */

static int
basename_is_dot_or_dotdot (const char *name)
{
  char const *base = base_name (name);
  return DOT_OR_DOTDOT (base);
}

/* Remove any entries from `files' that are for directories,
   and queue them to be listed as directories instead.
   `dirname' is the prefix to prepend to each dirname
   to make it correct relative to ls's working dir.
   If IGNORE_DOT_AND_DOT_DOT is nonzero don't treat `.' and `..' as dirs.
   This is desirable when processing directories recursively.  */

static void
extract_dirs_from_files (const char *dirname, int ignore_dot_and_dot_dot)
{
  register int i, j;

  if (*dirname && LOOP_DETECT)
    {
      /* Insert a marker entry first.  When we dequeue this marker entry,
     we'll know that DIRNAME has been processed and may be removed
     from the set of active directories.  */
      queue_directory (NULL, dirname);
    }

  /* Queue the directories last one first, because queueing reverses the
     order.  */
  for (i = files_index - 1; i >= 0; i--)
    if ((files[i].filetype == directory || files[i].filetype == arg_directory)
    && (!ignore_dot_and_dot_dot
        || !basename_is_dot_or_dotdot (files[i].name)))
      {
    if (files[i].name[0] == '/' || dirname[0] == 0)
      {
        queue_directory (files[i].name, files[i].linkname);
      }
    else
      {
        char *path = path_concat (dirname, files[i].name, NULL);
        queue_directory (path, files[i].linkname);
        free (path);
      }
    if (files[i].filetype == arg_directory)
      free (files[i].name);
      }

  /* Now delete the directories from the table, compacting all the remaining
     entries.  */

  for (i = 0, j = 0; i < files_index; i++)
    if (files[i].filetype != arg_directory)
      files[j++] = files[i];
  files_index = j;
}

/* Use strcoll to compare strings in this locale.  If an error occurs,
   report an error and longjmp to failed_strcoll.  */

static jmp_buf failed_strcoll;

static int
xstrcoll (char const *a, char const *b)
{
  int diff;
  errno = 0;
  diff = strcoll (a, b);
  if (errno)
    {
      error (0, errno, _("cannot compare file names %s and %s"),
         quote_n (0, a), quote_n (1, b));
      exit_status = 1;
      longjmp (failed_strcoll, 1);
    }
  return diff;
}

/* Comparison routines for sorting the files. */

typedef void const *V;

static inline int
cmp_ctime (struct fileinfo const *a, struct fileinfo const *b,
       int (*cmp) (char const *, char const *))
{
  int diff = CTIME_CMP (b->stat, a->stat);
  return diff ? diff : cmp (a->name, b->name);
}
static int compare_ctime (V a, V b) { return cmp_ctime (a, b, xstrcoll); }
static int compstr_ctime (V a, V b) { return cmp_ctime (a, b, strcmp); }
static int rev_cmp_ctime (V a, V b) { return compare_ctime (b, a); }
static int rev_str_ctime (V a, V b) { return compstr_ctime (b, a); }

static inline int
cmp_mtime (struct fileinfo const *a, struct fileinfo const *b,
       int (*cmp) (char const *, char const *))
{
  int diff = MTIME_CMP (b->stat, a->stat);
  return diff ? diff : cmp (a->name, b->name);
}
static int compare_mtime (V a, V b) { return cmp_mtime (a, b, xstrcoll); }
static int compstr_mtime (V a, V b) { return cmp_mtime (a, b, strcmp); }
static int rev_cmp_mtime (V a, V b) { return compare_mtime (b, a); }
static int rev_str_mtime (V a, V b) { return compstr_mtime (b, a); }

static inline int
cmp_atime (struct fileinfo const *a, struct fileinfo const *b,
       int (*cmp) (char const *, char const *))
{
  int diff = ATIME_CMP (b->stat, a->stat);
  return diff ? diff : cmp (a->name, b->name);
}
static int compare_atime (V a, V b) { return cmp_atime (a, b, xstrcoll); }
static int compstr_atime (V a, V b) { return cmp_atime (a, b, strcmp); }
static int rev_cmp_atime (V a, V b) { return compare_atime (b, a); }
static int rev_str_atime (V a, V b) { return compstr_atime (b, a); }

static inline int
cmp_size (struct fileinfo const *a, struct fileinfo const *b,
      int (*cmp) (char const *, char const *))
{
  int diff = longdiff (b->stat.st_size, a->stat.st_size);
  return diff ? diff : cmp (a->name, b->name);
}
static int compare_size (V a, V b) { return cmp_size (a, b, xstrcoll); }
static int compstr_size (V a, V b) { return cmp_size (a, b, strcmp); }
static int rev_cmp_size (V a, V b) { return compare_size (b, a); }
static int rev_str_size (V a, V b) { return compstr_size (b, a); }

static inline int
cmp_version (struct fileinfo const *a, struct fileinfo const *b)
{
  return strverscmp (a->name, b->name);
}
static int compare_version (V a, V b) { return cmp_version (a, b); }
static int rev_cmp_version (V a, V b) { return compare_version (b, a); }

static inline int
cmp_name (struct fileinfo const *a, struct fileinfo const *b,
      int (*cmp) (char const *, char const *))
{
  return cmp (a->name, b->name);
}
static int compare_name (V a, V b) { return cmp_name (a, b, xstrcoll); }
static int compstr_name (V a, V b) { return cmp_name (a, b, strcmp); }
static int rev_cmp_name (V a, V b) { return compare_name (b, a); }
static int rev_str_name (V a, V b) { return compstr_name (b, a); }

/* Compare file extensions.  Files with no extension are `smallest'.
   If extensions are the same, compare by filenames instead. */

static inline int
cmp_extension (struct fileinfo const *a, struct fileinfo const *b,
           int (*cmp) (char const *, char const *))
{
  char const *base1 = strrchr (a->name, '.');
  char const *base2 = strrchr (b->name, '.');
  int diff = cmp (base1 ? base1 : "", base2 ? base2 : "");
  return diff ? diff : cmp (a->name, b->name);
}
static int compare_extension (V a, V b) { return cmp_extension (a, b, xstrcoll); }
static int compstr_extension (V a, V b) { return cmp_extension (a, b, strcmp); }
static int rev_cmp_extension (V a, V b) { return compare_extension (b, a); }
static int rev_str_extension (V a, V b) { return compstr_extension (b, a); }

/* Sort the files now in the table.  */

static void
sort_files (void)
{
  int (*func) (V, V);

  switch (sort_type)
    {
    case sort_none:
      return;
    case sort_time:
      switch (time_type)
    {
    case time_ctime:
      func = sort_reverse ? rev_cmp_ctime : compare_ctime;
      break;
    case time_mtime:
      func = sort_reverse ? rev_cmp_mtime : compare_mtime;
      break;
    case time_atime:
      func = sort_reverse ? rev_cmp_atime : compare_atime;
      break;
    default:
      abort ();
    }
      break;
    case sort_name:
      func = sort_reverse ? rev_cmp_name : compare_name;
      break;
    case sort_extension:
      func = sort_reverse ? rev_cmp_extension : compare_extension;
      break;
    case sort_size:
      func = sort_reverse ? rev_cmp_size : compare_size;
      break;
    case sort_version:
      func = sort_reverse ? rev_cmp_version : compare_version;
      break;
    default:
      abort ();
    }

  /* Try strcoll.  If it fails, fall back on strcmp.  We can't safely
     ignore strcoll failures, as a failing strcoll might be a
     comparison function that is not a total order, and if we ignored
     the failure this might cause qsort to dump core.  */

  if (setjmp (failed_strcoll))
    {
      switch (sort_type)
    {
    case sort_time:
      switch (time_type)
        {
        case time_ctime:
          func = sort_reverse ? rev_str_ctime : compstr_ctime;
          break;
        case time_mtime:
          func = sort_reverse ? rev_str_mtime : compstr_mtime;
          break;
        case time_atime:
          func = sort_reverse ? rev_str_atime : compstr_atime;
          break;
        default:
          abort ();
        }
      break;
    case sort_name:
      func = sort_reverse ? rev_str_name : compstr_name;
      break;
    case sort_extension:
      func = sort_reverse ? rev_str_extension : compstr_extension;
      break;
    case sort_size:
      func = sort_reverse ? rev_str_size : compstr_size;
      break;
    default:
      abort ();
    }
    }

  qsort (files, files_index, sizeof (struct fileinfo), func);
}

/* List all the files now in the table.  */

static void
print_current_files (void)
{
  register int i;

  switch (format)
    {
    case one_per_line:
      for (i = 0; i < files_index; i++)
    {
      print_file_name_and_frills (files + i);
      putchar ('\n');
    }
      break;

    case many_per_line:
      init_column_info ();
      print_many_per_line ();
      break;

    case horizontal:
      init_column_info ();
      print_horizontal ();
      break;

    case with_commas:
      print_with_commas ();
      break;

    case long_format:
      for (i = 0; i < files_index; i++)
    {
      print_long_format (files + i);
      DIRED_PUTCHAR ('\n');
    }
      break;
    }
}

/* Return the expected number of columns in a long-format time stamp,
   or zero if it cannot be calculated.  */

static int
long_time_expected_width (void)
{
  static int width = -1;

  if (width < 0)
    {
      time_t epoch = 0;
      struct tm const *tm = localtime (&epoch);
      char const *fmt = long_time_format[0];
      char initbuf[100];
      char *buf = initbuf;
      size_t bufsize = sizeof initbuf;
      size_t len;

      for (;;)
    {
      *buf = '\1';
      len = nstrftime (buf, bufsize, fmt, tm, 0, 0);
      if (len || ! *buf)
        break;
      buf = alloca (bufsize *= 2);
    }

      width = mbsnwidth (buf, len, 0);
      if (width < 0)
    width = 0;
    }

  return width;
}

/* Get the current time.  */

static void
get_current_time (void)
{
#if HAVE_CLOCK_GETTIME && defined CLOCK_REALTIME
  {
    struct timespec timespec;
    if (clock_gettime (CLOCK_REALTIME, &timespec) == 0)
      {
    current_time = timespec.tv_sec;
    current_time_ns = timespec.tv_nsec;
    return;
      }
  }
#endif

  /* The clock does not have nanosecond resolution, so get the maximum
     possible value for the current time that is consistent with the
     reported clock.  That way, files are not considered to be in the
     future merely because their time stamps have higher resolution
     than the clock resolution.  */

#if HAVE_GETTIMEOFDAY
  {
    struct timeval timeval;
    if (gettimeofday (&timeval, NULL) == 0)
      {
    current_time = timeval.tv_sec;
    current_time_ns = timeval.tv_usec * 1000 + 999;
    return;
      }
  }
#endif

  current_time = time (NULL);
  current_time_ns = 999999999;
}

/* Format into BUFFER the name or id of the user with id U.  Return
   the length of the formatted buffer, not counting the terminating
   null.  */

static size_t
format_user (char *buffer, uid_t u)
{
  char const *name = (numeric_ids ? NULL : getuser (u));
  if (name)
    sprintf (buffer, "%-8s ", name);
  else
    sprintf (buffer, "%-8lu ", (unsigned long) u);
  return strlen (buffer);
}

/* Print information about F in long format.  */

static void
print_long_format (const struct fileinfo *f)
{
  char modebuf[12];
  char init_bigbuf
    [LONGEST_HUMAN_READABLE + 1     /* inode */
     + LONGEST_HUMAN_READABLE + 1   /* size in blocks */
     + sizeof (modebuf) - 1 + 1     /* mode string */
     + LONGEST_HUMAN_READABLE + 1   /* st_nlink */
     + ID_LENGTH_MAX + 1        /* owner name */
     + ID_LENGTH_MAX + 1        /* group name */
     + ID_LENGTH_MAX + 1        /* author name */
     + LONGEST_HUMAN_READABLE + 1   /* major device number */
     + LONGEST_HUMAN_READABLE + 1   /* minor device number */
     + 35 + 1   /* usual length of time/date -- may be longer; see below */
     ];
  char *buf = init_bigbuf;
  size_t bufsize = sizeof (init_bigbuf);
  size_t s;
  char *p;
  time_t when;
  int when_ns IF_LINT (= 0);
  struct tm *when_local;

  /* Compute mode string.  On most systems, it's based on st_mode.
     On systems with migration (via the stat.st_dm_mode field), use
     the file's migrated status.  */
  mode_string (ST_DM_MODE (f->stat), modebuf);

  modebuf[10] = (FILE_HAS_ACL (f) ? '+' : ' ');
  modebuf[11] = '\0';

  switch (time_type)
    {
    case time_ctime:
      when = f->stat.st_ctime;
      when_ns = TIMESPEC_NS (f->stat.st_ctim);
      break;
    case time_mtime:
      when = f->stat.st_mtime;
      when_ns = TIMESPEC_NS (f->stat.st_mtim);
      break;
    case time_atime:
      when = f->stat.st_atime;
      when_ns = TIMESPEC_NS (f->stat.st_atim);
      break;
    }

  p = buf;

  if (print_inode)
    {
      char hbuf[LONGEST_HUMAN_READABLE + 1];
      sprintf (p, "%*s ", INODE_DIGITS, umaxtostr (f->stat.st_ino, hbuf));
      p += strlen (p);
    }

  if (print_block_size)
    {
      char hbuf[LONGEST_HUMAN_READABLE + 1];
      sprintf (p, "%*s ", block_size_size,
           human_readable (ST_NBLOCKS (f->stat), hbuf, human_output_opts,
                   ST_NBLOCKSIZE, output_block_size));
      p += strlen (p);
    }

  /* The last byte of the mode string is the POSIX
     "optional alternate access method flag".  */
  sprintf (p, "%s %3lu ", modebuf, (unsigned long) f->stat.st_nlink);
  p += strlen (p);

  if (print_owner)
    p += format_user (p, f->stat.st_uid);

  if (print_group)
    {
      char const *group_name = (numeric_ids ? NULL : getgroup (f->stat.st_gid));
      if (group_name)
    sprintf (p, "%-8s ", group_name);
      else
    sprintf (p, "%-8lu ", (unsigned long) f->stat.st_gid);
      p += strlen (p);
    }

  if (print_author)
    p += format_user (p, f->stat.st_author);

  if (S_ISCHR (f->stat.st_mode) || S_ISBLK (f->stat.st_mode))
    sprintf (p, "%3lu, %3lu ",
         (unsigned long) major (f->stat.st_rdev),
         (unsigned long) minor (f->stat.st_rdev));
  else
    {
      char hbuf[LONGEST_HUMAN_READABLE + 1];
      uintmax_t size = f->stat.st_size;

      /* POSIX requires that the size be printed without a sign, even
     when negative.  Assume the typical case where negative sizes
     are actually positive values that have wrapped around.  */
      size += (f->stat.st_size < 0) * ((uintmax_t) OFF_T_MAX - OFF_T_MIN + 1);

      sprintf (p, "%8s ",
           human_readable (size, hbuf, human_output_opts,
                   1, file_output_block_size));
    }

  p += strlen (p);

  if ((when_local = localtime (&when)))
    {
      time_t six_months_ago;
      int recent;
      char const *fmt;

      /* If the file appears to be in the future, update the current
     time, in case the file happens to have been modified since
     the last time we checked the clock.  */
      if (current_time < when
      || (current_time == when && current_time_ns < when_ns))
    {
      /* Note that get_current_time calls gettimeofday which, on some non-
         compliant systems, clobbers the buffer used for localtime's result.
         But it's ok here, because we use a gettimeofday wrapper that
         saves and restores the buffer around the gettimeofday call.  */
      get_current_time ();
    }

      /* Consider a time to be recent if it is within the past six
     months.  A Gregorian year has 365.2425 * 24 * 60 * 60 ==
     31556952 seconds on the average.  Write this value as an
     integer constant to avoid floating point hassles.  */
      six_months_ago = current_time - 31556952 / 2;
      recent = (six_months_ago <= when
        && (when < current_time
            || (when == current_time && when_ns <= current_time_ns)));
      fmt = long_time_format[recent];

      for (;;)
    {
      char *newbuf;
      *p = '\1';
      s = nstrftime (p, buf + bufsize - p - 1, fmt,
             when_local, 0, when_ns);
      if (s || ! *p)
        break;
      newbuf = alloca (bufsize *= 2);
      memcpy (newbuf, buf, p - buf);
      p = newbuf + (p - buf);
      buf = newbuf;
    }

      p += s;
      *p++ = ' ';

      /* NUL-terminate the string -- fputs (via DIRED_FPUTS) requires it.  */
      *p = '\0';
    }
  else
    {
      /* The time cannot be represented as a local time;
     print it as a huge integer number of seconds.  */
      char hbuf[INT_BUFSIZE_BOUND (intmax_t)];
      sprintf (p, "%*s ", long_time_expected_width (),
           (TYPE_SIGNED (time_t)
        ? imaxtostr (when, hbuf)
        : umaxtostr (when, hbuf)));
      p += strlen (p);
    }

  DIRED_INDENT ();
  DIRED_FPUTS (buf, stdout, p - buf);
  print_name_with_quoting (f->name, FILE_OR_LINK_MODE (f), f->linkok,
               &dired_obstack);

  if (f->filetype == symbolic_link)
    {
      if (f->linkname)
    {
      DIRED_FPUTS_LITERAL (" -> ", stdout);
      print_name_with_quoting (f->linkname, f->linkmode, f->linkok - 1,
                   NULL);
      if (indicator_style != none)
        print_type_indicator (f->linkmode);
    }
    }
  else if (indicator_style != none)
    print_type_indicator (f->stat.st_mode);
}

/* Output to OUT a quoted representation of the file name NAME,
   using OPTIONS to control quoting.  Produce no output if OUT is NULL.
   Store the number of screen columns occupied by NAME's quoted
   representation into WIDTH, if non-NULL.  Return the number of bytes
   produced.  */

static size_t
quote_name (FILE *out, const char *name, struct quoting_options const *options,
        size_t *width)
{
  char smallbuf[BUFSIZ];
  size_t len = quotearg_buffer (smallbuf, sizeof smallbuf, name, -1, options);
  char *buf;
  size_t displayed_width IF_LINT (= 0);

  if (len < sizeof smallbuf)
    buf = smallbuf;
  else
    {
      buf = (char *) alloca (len + 1);
      quotearg_buffer (buf, len + 1, name, -1, options);
    }

  if (qmark_funny_chars)
    {
#if HAVE_MBRTOWC
      if (MB_CUR_MAX > 1)
    {
      char const *p = buf;
      char const *plimit = buf + len;
      char *q = buf;
      displayed_width = 0;

      while (p < plimit)
        switch (*p)
          {
        case ' ': case '!': case '"': case '#': case '%':
        case '&': case '\'': case '(': case ')': case '*':
        case '+': case ',': case '-': case '.': case '/':
        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
        case ':': case ';': case '<': case '=': case '>':
        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':
        case '[': case '\\': case ']': case '^': 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': case '{': case '|': case '}': case '~':
          /* These characters are printable ASCII characters.  */
          *q++ = *p++;
          displayed_width += 1;
          break;
        default:
          /* If we have a multibyte sequence, copy it until we
             reach its end, replacing each non-printable multibyte
             character with a single question mark.  */
          {
            mbstate_t mbstate;
            memset (&mbstate, 0, sizeof mbstate);
            do
              {
            wchar_t wc;
            size_t bytes;
            int w;

            bytes = mbrtowc (&wc, p, plimit - p, &mbstate);

            if (bytes == (size_t) -1)
              {
                /* An invalid multibyte sequence was
                   encountered.  Skip one input byte, and
                   put a question mark.  */
                p++;
                *q++ = '?';
                displayed_width += 1;
                break;
              }

            if (bytes == (size_t) -2)
              {
                /* An incomplete multibyte character
                   at the end.  Replace it entirely with
                   a question mark.  */
                p = plimit;
                *q++ = '?';
                displayed_width += 1;
                break;
              }

            if (bytes == 0)
              /* A null wide character was encountered.  */
              bytes = 1;

            w = wcwidth (wc);
            if (w >= 0)
              {
                /* A printable multibyte character.
                   Keep it.  */
                for (; bytes > 0; --bytes)
                  *q++ = *p++;
                displayed_width += w;
              }
            else
              {
                /* An unprintable multibyte character.
                   Replace it entirely with a question
                   mark.  */
                p += bytes;
                *q++ = '?';
                displayed_width += 1;
              }
              }
            while (! mbsinit (&mbstate));
          }
          break;
          }

      /* The buffer may have shrunk.  */
      len = q - buf;
    }
      else
#endif
    {
      char *p = buf;
      char const *plimit = buf + len;

      while (p < plimit)
        {
          if (! ISPRINT ((unsigned char) *p))
        *p = '?';
          p++;
        }
      displayed_width = len;
    }
    }
  else if (width != NULL)
    {
#if HAVE_MBRTOWC
      if (MB_CUR_MAX > 1)
    displayed_width = mbsnwidth (buf, len, 0);
      else
#endif
    {
      char const *p = buf;
      char const *plimit = buf + len;

      displayed_width = 0;
      while (p < plimit)
        {
          if (ISPRINT ((unsigned char) *p))
        displayed_width++;
          p++;
        }
    }
    }

  if (out != NULL)
    fwrite (buf, 1, len, out);
  if (width != NULL)
    *width = displayed_width;
  return len;
}

static void
print_name_with_quoting (const char *p, mode_t mode, int linkok,
             struct obstack *stack)
{
  if (print_with_color)
    print_color_indicator (p, mode, linkok);

  if (stack)
    PUSH_CURRENT_DIRED_POS (stack);

  dired_pos += quote_name (stdout, p, filename_quoting_options, NULL);

  if (stack)
    PUSH_CURRENT_DIRED_POS (stack);

  if (print_with_color)
    prep_non_filename_text ();
}

static void
prep_non_filename_text (void)
{
  if (color_indicator[C_END].string != NULL)
    put_indicator (&color_indicator[C_END]);
  else
    {
      put_indicator (&color_indicator[C_LEFT]);
      put_indicator (&color_indicator[C_NORM]);
      put_indicator (&color_indicator[C_RIGHT]);
    }
}

/* Print the file name of `f' with appropriate quoting.
   Also print file size, inode number, and filetype indicator character,
   as requested by switches.  */

static void
print_file_name_and_frills (const struct fileinfo *f)
{
  char buf[MAX (LONGEST_HUMAN_READABLE + 1, INT_BUFSIZE_BOUND (uintmax_t))];

  if (print_inode)
    printf ("%*s ", INODE_DIGITS, umaxtostr (f->stat.st_ino, buf));

  if (print_block_size)
    printf ("%*s ", block_size_size,
        human_readable (ST_NBLOCKS (f->stat), buf, human_output_opts,
                ST_NBLOCKSIZE, output_block_size));

  print_name_with_quoting (f->name, FILE_OR_LINK_MODE (f), f->linkok, NULL);

  if (indicator_style != none)
    print_type_indicator (f->stat.st_mode);
}

static void
print_type_indicator (mode_t mode)
{
  int c;

  if (S_ISREG (mode))
    {
      if (indicator_style == classify && (mode & S_IXUGO))
    c ='*';
      else
    c = 0;
    }
  else
    {
      if (S_ISDIR (mode))
    c = '/';
      else if (S_ISLNK (mode))
    c = '@';
      else if (S_ISFIFO (mode))
    c = '|';
      else if (S_ISSOCK (mode))
    c = '=';
      else if (S_ISDOOR (mode))
    c = '>';
      else
    c = 0;
    }

  if (c)
    DIRED_PUTCHAR (c);
}

static void
print_color_indicator (const char *name, mode_t mode, int linkok)
{
  int type = C_FILE;
  struct color_ext_type *ext;   /* Color extension */
  size_t len;           /* Length of name */

  /* Is this a nonexistent file?  If so, linkok == -1.  */

  if (linkok == -1 && color_indicator[C_MISSING].string != NULL)
    {
      ext = NULL;
      type = C_MISSING;
    }
  else
    {
      if (S_ISDIR (mode))
    type = C_DIR;
      else if (S_ISLNK (mode))
    type = ((!linkok && color_indicator[C_ORPHAN].string)
        ? C_ORPHAN : C_LINK);
      else if (S_ISFIFO (mode))
    type = C_FIFO;
      else if (S_ISSOCK (mode))
    type = C_SOCK;
      else if (S_ISBLK (mode))
    type = C_BLK;
      else if (S_ISCHR (mode))
    type = C_CHR;
      else if (S_ISDOOR (mode))
    type = C_DOOR;

      if (type == C_FILE && (mode & S_IXUGO) != 0)
    type = C_EXEC;

      /* Check the file's suffix only if still classified as C_FILE.  */
      ext = NULL;
      if (type == C_FILE)
    {
      /* Test if NAME has a recognized suffix.  */

      len = strlen (name);
      name += len;      /* Pointer to final \0.  */
      for (ext = color_ext_list; ext != NULL; ext = ext->next)
        {
          if ((size_t) ext->ext.len <= len
          && strncmp (name - ext->ext.len, ext->ext.string,
                  ext->ext.len) == 0)
        break;
        }
    }
    }

  put_indicator (&color_indicator[C_LEFT]);
  put_indicator (ext ? &(ext->seq) : &color_indicator[type]);
  put_indicator (&color_indicator[C_RIGHT]);
}

/* Output a color indicator (which may contain nulls).  */
static void
put_indicator (const struct bin_str *ind)
{
  register int i;
  register const char *p;

  p = ind->string;

  for (i = ind->len; i > 0; --i)
    putchar (*(p++));
}

/* Output a color indicator, but don't use stdio, for use from signal handlers.
   Return zero if the write is successful or if the string length is zero.
   Return nonzero if the write fails.  */
static int
put_indicator_direct (const struct bin_str *ind)
{
  size_t len;
  if (ind->len <= 0)
    return 0;

  len = ind->len;
  return (full_write (STDOUT_FILENO, ind->string, len) != len);
}

static int
length_of_file_name_and_frills (const struct fileinfo *f)
{
  register int len = 0;
  size_t name_width;

  if (print_inode)
    len += INODE_DIGITS + 1;

  if (print_block_size)
    len += 1 + block_size_size;

  quote_name (NULL, f->name, filename_quoting_options, &name_width);
  len += name_width;

  if (indicator_style != none)
    {
      mode_t filetype = f->stat.st_mode;

      if (S_ISREG (filetype))
    {
      if (indicator_style == classify
          && (f->stat.st_mode & S_IXUGO))
        len += 1;
    }
      else if (S_ISDIR (filetype)
           || S_ISLNK (filetype)
           || S_ISFIFO (filetype)
           || S_ISSOCK (filetype)
           || S_ISDOOR (filetype)
           )
    len += 1;
    }

  return len;
}

static void
print_many_per_line (void)
{
  struct column_info *line_fmt;
  int filesno;          /* Index into files. */
  int row;          /* Current row. */
  int max_name_length;      /* Length of longest file name + frills. */
  int name_length;      /* Length of each file name + frills. */
  int pos;          /* Current character column. */
  int cols;         /* Number of files across. */
  int rows;         /* Maximum number of files down. */
  int max_cols;

  /* Normally the maximum number of columns is determined by the
     screen width.  But if few files are available this might limit it
     as well.  */
  max_cols = max_idx > files_index ? files_index : max_idx;

  /* Compute the maximum number of possible columns.  */
  for (filesno = 0; filesno < files_index; ++filesno)
    {
      int i;

      name_length = length_of_file_name_and_frills (files + filesno);

      for (i = 0; i < max_cols; ++i)
    {
      if (column_info[i].valid_len)
        {
          int idx = filesno / ((files_index + i) / (i + 1));
          int real_length = name_length + (idx == i ? 0 : 2);

          if (real_length > column_info[i].col_arr[idx])
        {
          column_info[i].line_len += (real_length
                       - column_info[i].col_arr[idx]);
          column_info[i].col_arr[idx] = real_length;
          column_info[i].valid_len = column_info[i].line_len < line_length;
        }
        }
    }
    }

  /* Find maximum allowed columns.  */
  for (cols = max_cols; cols > 1; --cols)
    {
      if (column_info[cols - 1].valid_len)
    break;
    }

  line_fmt = &column_info[cols - 1];

  /* Calculate the number of rows that will be in each column except possibly
     for a short column on the right. */
  rows = files_index / cols + (files_index % cols != 0);

  for (row = 0; row < rows; row++)
    {
      int col = 0;
      filesno = row;
      pos = 0;
      /* Print the next row.  */
      while (1)
    {
      print_file_name_and_frills (files + filesno);
      name_length = length_of_file_name_and_frills (files + filesno);
      max_name_length = line_fmt->col_arr[col++];

      filesno += rows;
      if (filesno >= files_index)
        break;

      indent (pos + name_length, pos + max_name_length);
      pos += max_name_length;
    }
      putchar ('\n');
    }
}

static void
print_horizontal (void)
{
  struct column_info *line_fmt;
  int filesno;
  int max_name_length;
  int name_length;
  int cols;
  int pos;
  int max_cols;

  /* Normally the maximum number of columns is determined by the
     screen width.  But if few files are available this might limit it
     as well.  */
  max_cols = max_idx > files_index ? files_index : max_idx;

  /* Compute the maximum file name length.  */
  max_name_length = 0;
  for (filesno = 0; filesno < files_index; ++filesno)
    {
      int i;

      name_length = length_of_file_name_and_frills (files + filesno);

      for (i = 0; i < max_cols; ++i)
    {
      if (column_info[i].valid_len)
        {
          int idx = filesno % (i + 1);
          int real_length = name_length + (idx == i ? 0 : 2);

          if (real_length > column_info[i].col_arr[idx])
        {
          column_info[i].line_len += (real_length
                       - column_info[i].col_arr[idx]);
          column_info[i].col_arr[idx] = real_length;
          column_info[i].valid_len = column_info[i].line_len < line_length;
        }
        }
    }
    }

  /* Find maximum allowed columns.  */
  for (cols = max_cols; cols > 1; --cols)
    {
      if (column_info[cols - 1].valid_len)
    break;
    }

  line_fmt = &column_info[cols - 1];

  pos = 0;

  /* Print first entry.  */
  print_file_name_and_frills (files);
  name_length = length_of_file_name_and_frills (files);
  max_name_length = line_fmt->col_arr[0];

  /* Now the rest.  */
  for (filesno = 1; filesno < files_index; ++filesno)
    {
      int col = filesno % cols;

      if (col == 0)
    {
      putchar ('\n');
      pos = 0;
    }
      else
    {
      indent (pos + name_length, pos + max_name_length);
      pos += max_name_length;
    }

      print_file_name_and_frills (files + filesno);

      name_length = length_of_file_name_and_frills (files + filesno);
      max_name_length = line_fmt->col_arr[col];
    }
  putchar ('\n');
}

static void
print_with_commas (void)
{
  int filesno;
  int pos, old_pos;

  pos = 0;

  for (filesno = 0; filesno < files_index; filesno++)
    {
      old_pos = pos;

      pos += length_of_file_name_and_frills (files + filesno);
      if (filesno + 1 < files_index)
    pos += 2;       /* For the comma and space */

      if (old_pos != 0 && pos >= line_length)
    {
      putchar ('\n');
      pos -= old_pos;
    }

      print_file_name_and_frills (files + filesno);
      if (filesno + 1 < files_index)
    {
      putchar (',');
      putchar (' ');
    }
    }
  putchar ('\n');
}

/* Assuming cursor is at position FROM, indent up to position TO.
   Use a TAB character instead of two or more spaces whenever possible.  */

static void
indent (int from, int to)
{
  while (from < to)
    {
      if (tabsize > 0 && to / tabsize > (from + 1) / tabsize)
    {
      putchar ('\t');
      from += tabsize - from % tabsize;
    }
      else
    {
      putchar (' ');
      from++;
    }
    }
}

/* Put DIRNAME/NAME into DEST, handling `.' and `/' properly. */
/* FIXME: maybe remove this function someday.  See about using a
   non-malloc'ing version of path_concat.  */

static void
attach (char *dest, const char *dirname, const char *name)
{
  const char *dirnamep = dirname;

  /* Copy dirname if it is not ".". */
  if (dirname[0] != '.' || dirname[1] != 0)
    {
      while (*dirnamep)
    *dest++ = *dirnamep++;
      /* Add '/' if `dirname' doesn't already end with it. */
      if (dirnamep > dirname && dirnamep[-1] != '/')
    *dest++ = '/';
    }
  while (*name)
    *dest++ = *name++;
  *dest = 0;
}

static void
init_column_info (void)
{
  int i;
  int allocate = 0;

  max_idx = line_length / MIN_COLUMN_WIDTH;
  if (max_idx == 0)
    max_idx = 1;

  if (column_info == NULL)
    {
      column_info = XMALLOC (struct column_info, max_idx);
      allocate = 1;
    }

  for (i = 0; i < max_idx; ++i)
    {
      int j;

      column_info[i].valid_len = 1;
      column_info[i].line_len = (i + 1) * MIN_COLUMN_WIDTH;

      if (allocate)
    column_info[i].col_arr = XMALLOC (int, i + 1);

      for (j = 0; j <= i; ++j)
    column_info[i].col_arr[j] = MIN_COLUMN_WIDTH;
    }
}

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
      fputs (_("\
List information about the FILEs (the current directory by default).\n\
Sort entries alphabetically if none of -cftuSUX nor --sort.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -a, --all                  do not hide entries starting with .\n\
  -A, --almost-all           do not list implied . and ..\n\
      --author               print the author of each file\n\
  -b, --escape               print octal escapes for nongraphic characters\n\
"), stdout);
      fputs (_("\
      --block-size=SIZE      use SIZE-byte blocks\n\
  -B, --ignore-backups       do not list implied entries ending with ~\n\
  -c                         with -lt: sort by, and show, ctime (time of last\n\
                               modification of file status information)\n\
                               with -l: show ctime and sort by name\n\
                               otherwise: sort by ctime\n\
"), stdout);
      fputs (_("\
  -C                         list entries by columns\n\
      --color[=WHEN]         control whether color is used to distinguish file\n\
                               types.  WHEN may be `never', `always', or `auto'\n\
  -d, --directory            list directory entries instead of contents,\n\
                               and do not dereference symbolic links\n\
  -D, --dired                generate output designed for Emacs' dired mode\n\
"), stdout);
      fputs (_("\
  -f                         do not sort, enable -aU, disable -lst\n\
  -F, --classify             append indicator (one of */=@|) to entries\n\
      --format=WORD          across -x, commas -m, horizontal -x, long -l,\n\
                               single-column -1, verbose -l, vertical -C\n\
      --full-time            like -l --time-style=full-iso\n\
"), stdout);
      fputs (_("\
  -g                         like -l, but do not list owner\n\
  -G, --no-group             inhibit display of group information\n\
  -h, --human-readable  print sizes in human readable format (e.g., 1K 234M 2G)\n\
      --si                   likewise, but use powers of 1000 not 1024\n\
  -H, --dereference-command-line\n\
                             follow symbolic links listed on the command line\n\
      --dereference-command-line-symlink-to-dir\n\
                             follow each command line symbolic link\n\
                               that points to a directory\n\
"), stdout);
      fputs (_("\
      --indicator-style=WORD append indicator with style WORD to entry names:\n\
                               none (default), classify (-F), file-type (-p)\n\
  -i, --inode                print index number of each file\n\
  -I, --ignore=PATTERN       do not list implied entries matching shell PATTERN\n\
  -k                         like --block-size=1K\n\
"), stdout);
      fputs (_("\
  -l                         use a long listing format\n\
  -L, --dereference          when showing file information for a symbolic\n\
                               link, show information for the file the link\n\
                               references rather than for the link itself\n\
  -m                         fill width with a comma separated list of entries\n\
"), stdout);
      fputs (_("\
  -n, --numeric-uid-gid      like -l, but list numeric UIDs and GIDs\n\
  -N, --literal              print raw entry names (don't treat e.g. control\n\
                               characters specially)\n\
  -o                         like -l, but do not list group information\n\
  -p, --file-type            append indicator (one of /=@|) to entries\n\
"), stdout);
      fputs (_("\
  -q, --hide-control-chars   print ? instead of non graphic characters\n\
      --show-control-chars   show non graphic characters as-is (default\n\
                             unless program is `ls' and output is a terminal)\n\
  -Q, --quote-name           enclose entry names in double quotes\n\
      --quoting-style=WORD   use quoting style WORD for entry names:\n\
                               literal, locale, shell, shell-always, c, escape\n\
"), stdout);
      fputs (_("\
  -r, --reverse              reverse order while sorting\n\
  -R, --recursive            list subdirectories recursively\n\
  -s, --size                 print size of each file, in blocks\n\
"), stdout);
      fputs (_("\
  -S                         sort by file size\n\
      --sort=WORD            extension -X, none -U, size -S, time -t,\n\
                               version -v\n\
                             status -c, time -t, atime -u, access -u, use -u\n\
      --time=WORD            show time as WORD instead of modification time:\n\
                               atime, access, use, ctime or status; use\n\
                               specified time as sort key if --sort=time\n\
"), stdout);
      fputs (_("\
      --time-style=STYLE     show times using style STYLE:\n\
                               full-iso, long-iso, iso, locale, +FORMAT\n\
                             FORMAT is interpreted like `date'; if FORMAT is\n\
                             FORMAT1<newline>FORMAT2, FORMAT1 applies to\n\
                             non-recent files and FORMAT2 to recent files;\n\
                             if STYLE is prefixed with `posix-', STYLE\n\
                             takes effect only outside the POSIX locale\n\
  -t                         sort by modification time\n\
  -T, --tabsize=COLS         assume tab stops at each COLS instead of 8\n\
"), stdout);
      fputs (_("\
  -u                         with -lt: sort by, and show, access time\n\
                               with -l: show access time and sort by name\n\
                               otherwise: sort by access time\n\
  -U                         do not sort; list entries in directory order\n\
  -v                         sort by version\n\
"), stdout);
      fputs (_("\
  -w, --width=COLS           assume screen width instead of current value\n\
  -x                         list entries by lines instead of by columns\n\
  -X                         sort alphabetically by entry extension\n\
  -1                         list one file per line\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\n\
SIZE may be (or may be an integer optionally followed by) one of following:\n\
kB 1000, K 1024, MB 1,000,000, M 1,048,576, and so on for G, T, P, E, Z, Y.\n\
"), stdout);
      fputs (_("\
\n\
By default, color is not used to distinguish types of files.  That is\n\
equivalent to using --color=none.  Using the --color option without the\n\
optional WHEN argument is equivalent to using --color=always.  With\n\
--color=auto, color codes are output only if standard output is connected\n\
to a terminal (tty).\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/touch-getdate.patch

File: ../git/live-bootstrap/sysa/coreutils-5.0/patches/touch-getdate.patch used in: Step 743
SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>

SPDX-License-Identifier: GPL-2.0-or-later

getdate.c is pre-compiled from getdate.y
At this point we don't have bison yet and in any case getdate.y does not
compile when generated with modern bison.

--- src/touch.c.orig    2021-03-13 18:16:05.344355958 +0000
+++ src/touch.c 2021-03-13 18:16:26.204891355 +0000
@@ -306,7 +306,7 @@
 
    case 'd':
      flexible_date++;
-     newtime = get_date (optarg, NULL);
+     newtime = 0;
      if (newtime == (time_t) -1)
        error (EXIT_FAILURE, 0, _("invalid date format %s"), quote (optarg));
      date_set++;

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/touch.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/touch.c used in: Step 743, Step 744, Step 951
/* touch -- change modification and access times of files
   Copyright (C) 87, 1989-1991, 1995-2002 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.  */

/* Written by Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie,
   and Randy Smith. */

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>

#include "system.h"
#include "argmatch.h"
#include "error.h"
#include "getdate.h"
#include "posixtm.h"
#include "posixver.h"
#include "quote.h"
#include "safe-read.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "touch"

#define AUTHORS \
N_ ("Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie, and Randy Smith")

#ifndef STDC_HEADERS
time_t time ();
#endif

/* Bitmasks for `change_times'. */
#define CH_ATIME 1
#define CH_MTIME 2

#if !defined O_NDELAY
# define O_NDELAY 0
#endif

#if !defined O_NONBLOCK
# define O_NONBLOCK O_NDELAY
#endif

#if !defined O_NOCTTY
# define O_NOCTTY 0
#endif

#if !defined EISDIR
# define EISDIR 0
#endif

/* The name by which this program was run. */
char *program_name;

/* Which timestamps to change. */
static int change_times;

/* (-c) If nonzero, don't create if not already there. */
static int no_create;

/* (-d) If nonzero, date supplied on command line in get_date formats. */
static int flexible_date;

/* (-r) If nonzero, use times from a reference file. */
static int use_ref;

/* (-t) If nonzero, date supplied on command line in POSIX format. */
static int posix_date;

/* If nonzero, the only thing we have to do is change both the
   modification and access time to the current time, so we don't
   have to own the file, just be able to read and write it.
   On some systems, we can do this if we own the file, even though
   we have neither read nor write access to it.  */
static int amtime_now;

/* New time to use when setting time. */
static time_t newtime;

/* File to use for -r. */
static char *ref_file;

/* Info about the reference file. */
static struct stat ref_stats;

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  TIME_OPTION = CHAR_MAX + 1
};

static struct option const longopts[] =
{
  {"time", required_argument, 0, TIME_OPTION},
  {"no-create", no_argument, 0, 'c'},
  {"date", required_argument, 0, 'd'},
  {"file", required_argument, 0, 'r'}, /* FIXME: phase out --file */
  {"reference", required_argument, 0, 'r'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {0, 0, 0, 0}
};

/* Valid arguments to the `--time' option. */
static char const* const time_args[] =
{
  "atime", "access", "use", "mtime", "modify", 0
};

/* The bits in `change_times' that those arguments set. */
static int const time_masks[] =
{
  CH_ATIME, CH_ATIME, CH_ATIME, CH_MTIME, CH_MTIME
};

/* Update the time of file FILE according to the options given.
   Return 0 if successful, 1 if an error occurs. */

static int
touch (const char *file)
{
  int status;
  struct stat sbuf;
  int fd = -1;
  int open_errno = 0;

  if (! no_create)
    {
      /* Try to open FILE, creating it if necessary.  */
      fd = open (file, O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY,
         S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);

      /* Don't save a copy of errno if it's EISDIR, since that would lead
     touch to give a bogus diagnostic for e.g., `touch /' (assuming
     we don't own / or have write access to it).  On Solaris 5.6,
     and probably other systems, it is EINVAL.  On SunOS4, it's EPERM.  */
      if (fd == -1 && errno != EISDIR && errno != EINVAL && errno != EPERM)
    open_errno = errno;
    }

  if (! amtime_now)
    {
      /* We're setting only one of the time values.  stat the target to get
     the other one.  If we have the file descriptor already, use fstat.
     Otherwise, either we're in no-create mode (and hence didn't call open)
     or FILE is inaccessible or a directory, so we have to use stat.  */
      if (fd != -1 ? fstat (fd, &sbuf) : stat (file, &sbuf))
    {
      if (open_errno)
        error (0, open_errno, _("creating %s"), quote (file));
      else
        {
          if (no_create && errno == ENOENT)
        return 0;
          error (0, errno, _("failed to get attributes of %s"),
             quote (file));
        }
      close (fd);
      return 1;
    }
    }

  if (fd != -1 && close (fd) < 0)
    {
      error (0, errno, _("creating %s"), quote (file));
      return 1;
    }

  if (amtime_now)
    {
      /* Pass NULL to utime so it will not fail if we just have
     write access to the file, but don't own it.  */
      status = utime (file, NULL);
    }
  else
    {
      struct utimbuf utb;

      /* There's currently no interface to set file timestamps with
     better than 1-second resolution, so discard any fractional
     part of the source timestamp.  */

      if (use_ref)
    {
      utb.actime = ref_stats.st_atime;
      utb.modtime = ref_stats.st_mtime;
    }
      else
    utb.actime = utb.modtime = newtime;

      if (!(change_times & CH_ATIME))
    utb.actime = sbuf.st_atime;

      if (!(change_times & CH_MTIME))
    utb.modtime = sbuf.st_mtime;

      status = utime (file, &utb);
    }

  if (status)
    {
      if (open_errno)
    {
      /* The wording of this diagnostic should cover at least two cases:
         - the file does not exist, but the parent directory is unwritable
         - the file exists, but it isn't writable
         I think it's not worth trying to distinguish them.  */
      error (0, open_errno, _("cannot touch %s"), quote (file));
    }
      else
    {
      if (no_create && errno == ENOENT)
        return 0;
      error (0, errno, _("setting times of %s"), quote (file));
    }
      return 1;
    }

  return 0;
}

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
      fputs (_("\
Update the access and modification times of each FILE to the current time.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -a                     change only the access time\n\
  -c, --no-create        do not create any files\n\
  -d, --date=STRING      parse STRING and use it instead of current time\n\
  -f                     (ignored)\n\
  -m                     change only the modification time\n\
"), stdout);
      fputs (_("\
  -r, --reference=FILE   use this file's times instead of current time\n\
  -t STAMP               use [[CC]YY]MMDDhhmm[.ss] instead of current time\n\
  --time=WORD            set time given by WORD: access atime use (same as -a)\n\
                           modify mtime (same as -m)\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
Note that the -d and -t options accept different time-date formats.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  int c;
  int date_set = 0;
  int err = 0;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  change_times = no_create = use_ref = posix_date = flexible_date = 0;

  while ((c = getopt_long (argc, argv, "acd:fmr:t:", longopts, NULL)) != -1)
    {
      switch (c)
    {
    case 0:
      break;

    case 'a':
      change_times |= CH_ATIME;
      break;

    case 'c':
      no_create++;
      break;

    case 'd':
      flexible_date++;
      newtime = get_date (optarg, NULL);
      if (newtime == (time_t) -1)
        error (EXIT_FAILURE, 0, _("invalid date format %s"), quote (optarg));
      date_set++;
      break;

    case 'f':
      break;

    case 'm':
      change_times |= CH_MTIME;
      break;

    case 'r':
      use_ref++;
      ref_file = optarg;
      break;

    case 't':
      posix_date++;
      if (! posixtime (&newtime, optarg,
               PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS))
        error (EXIT_FAILURE, 0, _("invalid date format %s"), quote (optarg));
      date_set++;
      break;

    case TIME_OPTION:   /* --time */
      change_times |= XARGMATCH ("--time", optarg,
                     time_args, time_masks);
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (change_times == 0)
    change_times = CH_ATIME | CH_MTIME;

  if ((use_ref && (posix_date || flexible_date))
      || (posix_date && flexible_date))
    {
      error (0, 0, _("cannot specify times from more than one source"));
      usage (EXIT_FAILURE);
    }

  if (use_ref)
    {
      if (stat (ref_file, &ref_stats))
    error (EXIT_FAILURE, errno,
           _("failed to get attributes of %s"), quote (ref_file));
      date_set++;
    }

  /* The obsolete `MMDDhhmm[YY]' form is valid IFF there are
     two or more non-option arguments.  */
  if (!date_set && 2 <= argc - optind && !STREQ (argv[optind - 1], "--")
      && posix2_version () < 200112)
    {
      if (posixtime (&newtime, argv[optind], PDS_TRAILING_YEAR))
    {
      if (! getenv ("POSIXLY_CORRECT"))
        {
          struct tm const *tm = localtime (&newtime);
          error (0, 0,
             _("warning: `touch %s' is obsolete; use\
 `touch -t %04d%02d%02d%02d%02d.%02d'"),
             argv[optind],
             tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
             tm->tm_hour, tm->tm_min, tm->tm_sec);
        }

      optind++;
      date_set++;
    }
    }
  if (!date_set)
    {
      if ((change_times & (CH_ATIME | CH_MTIME)) == (CH_ATIME | CH_MTIME))
    amtime_now = 1;
      else
    time (&newtime);
    }

  if (optind == argc)
    {
      error (0, 0, _("file arguments missing"));
      usage (EXIT_FAILURE);
    }

  for (; optind < argc; ++optind)
    err += touch (argv[optind]);

  exit (err != 0);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/touch-dereference.patch

File: ../git/live-bootstrap/sysa/coreutils-5.0/patches/touch-dereference.patch used in: Step 744
SPDX-FileCopyrightText: 2022 Andrius Å tikonas <andrius@stikonas.eu>
SPDX-FileCopyrightText: 2009 Eric Blake <ebb9@byu.net>

SPDX-License-Identifier: GPL-2.0-or-later

touch: add -h to change symlink timestamps, where supported

diff -r -U3 coreutils-5.0.orig/src/touch.c coreutils-5.0/src/touch.c
--- src/touch.c 2002-12-20 20:09:22.000000000 +0000
+++ src/touch.c 2022-05-16 20:31:37.801988595 +0100
@@ -77,6 +77,9 @@
 /* (-r) If nonzero, use times from a reference file. */
 static int use_ref;
 
+/* (-h) If true, change the times of an existing symlink, if possible.  */
+static int no_dereference;
+
 /* (-t) If nonzero, date supplied on command line in POSIX format. */
 static int posix_date;
 
@@ -110,6 +113,7 @@
   {"date", required_argument, 0, 'd'},
   {"file", required_argument, 0, 'r'}, /* FIXME: phase out --file */
   {"reference", required_argument, 0, 'r'},
+  {"no-dereference", no_argument, NULL, 'h'},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
   {0, 0, 0, 0}
@@ -138,7 +142,7 @@
   int fd = -1;
   int open_errno = 0;
 
-  if (! no_create)
+  if (! (no_create || no_dereference))
     {
       /* Try to open FILE, creating it if necessary.  */
       fd = open (file, O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY,
@@ -158,7 +162,7 @@
     the other one.  If we have the file descriptor already, use fstat.
     Otherwise, either we're in no-create mode (and hence didn't call open)
     or FILE is inaccessible or a directory, so we have to use stat.  */
-      if (fd != -1 ? fstat (fd, &sbuf) : stat (file, &sbuf))
+      if (fd != -1 ? fstat (fd, &sbuf) : (no_dereference ? lstat (file, &sbuf) : stat (file, &sbuf)))
    {
      if (open_errno)
        error (0, open_errno, _("creating %s"), quote (file));
@@ -223,7 +227,7 @@
    }
       else
    {
-     if (no_create && errno == ENOENT)
+     if ((no_create || no_dereference) && errno == ENOENT)
        return 0;
      error (0, errno, _("setting times of %s"), quote (file));
    }
@@ -254,6 +258,9 @@
   -c, --no-create        do not create any files\n\
   -d, --date=STRING      parse STRING and use it instead of current time\n\
   -f                     (ignored)\n\
+  -h, --no-dereference   affect each symbolic link instead of any referenced\n\
+                         file (useful only on systems that can change the\n\
+                         timestamps of a symlink)\n\
   -m                     change only the modification time\n\
 "), stdout);
       fputs (_("\
@@ -289,7 +296,7 @@
 
   change_times = no_create = use_ref = posix_date = flexible_date = 0;
 
-  while ((c = getopt_long (argc, argv, "acd:fmr:t:", longopts, NULL)) != -1)
+  while ((c = getopt_long (argc, argv, "acd:fhmr:t:", longopts, NULL)) != -1)
     {
       switch (c)
    {
@@ -315,6 +322,10 @@
    case 'f':
      break;
 
+   case 'h':
+     no_dereference = true;
+     break;
+
    case 'm':
      change_times |= CH_MTIME;
      break;
@@ -358,7 +369,10 @@
 
   if (use_ref)
     {
-      if (stat (ref_file, &ref_stats))
+      /* Don't use (no_dereference ? lstat : stat) (args), since stat
+         might be an object-like macro.  */
+      if (no_dereference ? lstat (ref_file, &ref_stats)
+          : stat (ref_file, &ref_stats))
    error (EXIT_FAILURE, errno,
           _("failed to get attributes of %s"), quote (ref_file));
       date_set++;

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/tac-uint64.patch

File: ../git/live-bootstrap/sysa/coreutils-5.0/patches/tac-uint64.patch used in: Step 745
SPDX-FileCopyrightText: 2022 Andrius Å tikonas <andrius@stikonas.eu>

SPDX-License-Identifier: GPL-2.0-or-later

uint64_t is not supported in tcc 0.9.27
--- lib/tempname.c     2002-12-01 10:40:32.000000000 +0000
+++ lib/tempname.c     2022-06-22 20:57:37.449423973 +0100
@@ -231,8 +231,8 @@
 {
   int len;
   char *XXXXXX;
-  static uint64_t value;
-  uint64_t random_time_bits;
+  static unsigned long long value;
+  unsigned long long random_time_bits;
   unsigned int count;
   int fd = -1;
   int save_errno = errno;
@@ -278,7 +278,7 @@
 
   for (count = 0; count < attempts; value += 7777, ++count)
     {
-      uint64_t v = value;
+      unsigned long long v = value;
 
       /* Fill in the random bits.  */
       XXXXXX[0] = letters[v % 62];

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/tempname.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/tempname.c used in: Step 745, Step 845
/* tempname.c - generate the name of a temporary file.

   Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
   2000, 2001, 2002 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>
#include <assert.h>

#include <errno.h>
#ifndef __set_errno
# define __set_errno(Val) errno = (Val)
#endif

#include <stdio.h>
#ifndef P_tmpdir
# define P_tmpdir "/tmp"
#endif
#ifndef TMP_MAX
# define TMP_MAX 238328
#endif
#ifndef __GT_FILE
# define __GT_FILE  0
# define __GT_BIGFILE   1
# define __GT_DIR   2
# define __GT_NOCREATE  3
#endif

#if STDC_HEADERS || _LIBC
# include <stddef.h>
# include <string.h>
#endif

#include <stdlib.h>

#if HAVE_FCNTL_H || _LIBC
# include <fcntl.h>
#endif

#if HAVE_SYS_TIME_H || _LIBC
# include <sys/time.h>
#endif

#if HAVE_STDINT_H || _LIBC
# include <stdint.h>
#endif

#if HAVE_UNISTD_H || _LIBC
# include <unistd.h>
#endif

#include <sys/stat.h>
#if STAT_MACROS_BROKEN
# undef S_ISDIR
#endif
#if !defined S_ISDIR && defined S_IFDIR
# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif
#if !S_IRUSR && S_IREAD
# define S_IRUSR S_IREAD
#endif
#if !S_IRUSR
# define S_IRUSR 00400
#endif
#if !S_IWUSR && S_IWRITE
# define S_IWUSR S_IWRITE
#endif
#if !S_IWUSR
# define S_IWUSR 00200
#endif
#if !S_IXUSR && S_IEXEC
# define S_IXUSR S_IEXEC
#endif
#if !S_IXUSR
# define S_IXUSR 00100
#endif

#if _LIBC
# define struct_stat64 struct stat64
#else
# define struct_stat64 struct stat
# define __getpid getpid
# define __gettimeofday gettimeofday
# define __mkdir mkdir
# define __open open
# define __open64 open
# define __lxstat64(version, path, buf) lstat (path, buf)
# define __xstat64(version, path, buf) stat (path, buf)
#endif

#if ! (HAVE___SECURE_GETENV || _LIBC)
# define __secure_getenv getenv
#endif

#ifdef _LIBC
# include <hp-timing.h>
# if HP_TIMING_AVAIL
#  define RANDOM_BITS(Var) \
  if (__builtin_expect (value == UINT64_C (0), 0))                \
    {                                         \
      /* If this is the first time this function is used initialize       \
     the variable we accumulate the value in to some somewhat         \
     random value.  If we'd not do this programs at startup time          \
     might have a reduced set of possible names, at least on slow         \
     machines.  */                                \
      struct timeval tv;                              \
      __gettimeofday (&tv, NULL);                         \
      value = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;              \
    }                                         \
  HP_TIMING_NOW (Var)
# endif
#endif

/* Use the widest available unsigned type if uint64_t is not
   available.  The algorithm below extracts a number less than 62**6
   (approximately 2**35.725) from uint64_t, so ancient hosts where
   uintmax_t is only 32 bits lose about 3.725 bits of randomness,
   which is better than not having mkstemp at all.  */
#if !defined UINT64_MAX && !defined uint64_t
# define uint64_t uintmax_t
#endif

/* Return nonzero if DIR is an existent directory.  */
static int
direxists (const char *dir)
{
  struct_stat64 buf;
  return __xstat64 (_STAT_VER, dir, &buf) == 0 && S_ISDIR (buf.st_mode);
}

/* Path search algorithm, for tmpnam, tmpfile, etc.  If DIR is
   non-null and exists, uses it; otherwise uses the first of $TMPDIR,
   P_tmpdir, /tmp that exists.  Copies into TMPL a template suitable
   for use with mk[s]temp.  Will fail (-1) if DIR is non-null and
   doesn't exist, none of the searched dirs exists, or there's not
   enough space in TMPL. */
int
__path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx,
           int try_tmpdir)
{
  const char *d;
  size_t dlen, plen;

  if (!pfx || !pfx[0])
    {
      pfx = "file";
      plen = 4;
    }
  else
    {
      plen = strlen (pfx);
      if (plen > 5)
    plen = 5;
    }

  if (try_tmpdir)
    {
      d = __secure_getenv ("TMPDIR");
      if (d != NULL && direxists (d))
    dir = d;
      else if (dir != NULL && direxists (dir))
    /* nothing */ ;
      else
    dir = NULL;
    }
  if (dir == NULL)
    {
      if (direxists (P_tmpdir))
    dir = P_tmpdir;
      else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp"))
    dir = "/tmp";
      else
    {
      __set_errno (ENOENT);
      return -1;
    }
    }

  dlen = strlen (dir);
  while (dlen > 1 && dir[dlen - 1] == '/')
    dlen--;         /* remove trailing slashes */

  /* check we have room for "${dir}/${pfx}XXXXXX\0" */
  if (tmpl_len < dlen + 1 + plen + 6 + 1)
    {
      __set_errno (EINVAL);
      return -1;
    }

  sprintf (tmpl, "%.*s/%.*sXXXXXX", (int) dlen, dir, (int) plen, pfx);
  return 0;
}

/* These are the characters used in temporary filenames.  */
static const char letters[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

/* Generate a temporary file name based on TMPL.  TMPL must match the
   rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
   does not exist at the time of the call to __gen_tempname.  TMPL is
   overwritten with the result.

   KIND may be one of:
   __GT_NOCREATE:   simply verify that the name does not exist
            at the time of the call.
   __GT_FILE:       create the file using open(O_CREAT|O_EXCL)
            and return a read-write fd.  The file is mode 0600.
   __GT_BIGFILE:    same as __GT_FILE but use open64().
   __GT_DIR:        create a directory, which will be mode 0700.

   We use a clever algorithm to get hard-to-predict names. */
int
__gen_tempname (char *tmpl, int kind)
{
  int len;
  char *XXXXXX;
  static uint64_t value;
  uint64_t random_time_bits;
  unsigned int count;
  int fd = -1;
  int save_errno = errno;
  struct_stat64 st;

  /* A lower bound on the number of temporary files to attempt to
     generate.  The maximum total number of temporary file names that
     can exist for a given template is 62**6.  It should never be
     necessary to try all these combinations.  Instead if a reasonable
     number of names is tried (we define reasonable as 62**3) fail to
     give the system administrator the chance to remove the problems.  */
  unsigned int attempts_min = 62 * 62 * 62;

  /* The number of times to attempt to generate a temporary file.  To
     conform to POSIX, this must be no smaller than TMP_MAX.  */
  unsigned int attempts = attempts_min < TMP_MAX ? TMP_MAX : attempts_min;

  len = strlen (tmpl);
  if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
    {
      __set_errno (EINVAL);
      return -1;
    }

  /* This is where the Xs start.  */
  XXXXXX = &tmpl[len - 6];

  /* Get some more or less random data.  */
#ifdef RANDOM_BITS
  RANDOM_BITS (random_time_bits);
#else
# if HAVE_GETTIMEOFDAY || _LIBC
  {
    struct timeval tv;
    __gettimeofday (&tv, NULL);
    random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;
  }
# else
  random_time_bits = time (NULL);
# endif
#endif
  value += random_time_bits ^ __getpid ();

  for (count = 0; count < attempts; value += 7777, ++count)
    {
      uint64_t v = value;

      /* Fill in the random bits.  */
      XXXXXX[0] = letters[v % 62];
      v /= 62;
      XXXXXX[1] = letters[v % 62];
      v /= 62;
      XXXXXX[2] = letters[v % 62];
      v /= 62;
      XXXXXX[3] = letters[v % 62];
      v /= 62;
      XXXXXX[4] = letters[v % 62];
      v /= 62;
      XXXXXX[5] = letters[v % 62];

      switch (kind)
    {
    case __GT_FILE:
      fd = __open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
      break;

    case __GT_BIGFILE:
      fd = __open64 (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
      break;

    case __GT_DIR:
      fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR);
      break;

    case __GT_NOCREATE:
      /* This case is backward from the other three.  __gen_tempname
         succeeds if __xstat fails because the name does not exist.
         Note the continue to bypass the common logic at the bottom
         of the loop.  */
      if (__lxstat64 (_STAT_VER, tmpl, &st) < 0)
        {
          if (errno == ENOENT)
        {
          __set_errno (save_errno);
          return 0;
        }
          else
        /* Give up now. */
        return -1;
        }
      continue;

    default:
      assert (! "invalid KIND in __gen_tempname");
    }

      if (fd >= 0)
    {
      __set_errno (save_errno);
      return fd;
    }
      else if (errno != EEXIST)
    return -1;
    }

  /* We got out of the loop because we ran out of combinations to try.  */
  __set_errno (EEXIST);
  return -1;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/expr-strcmp.patch

File: ../git/live-bootstrap/sysa/coreutils-5.0/patches/expr-strcmp.patch used in: Step 746
SPDX-FileCopyrightText: 2023 Emily Trau <emily@downunderctf.com>

SPDX-License-Identifier: GPL-2.0-or-later

strcoll() does not exist in mes libc, change it to strcmp.

--- src/expr.c
+++ src/expr.c
@@ -332,7 +332,7 @@ nextarg (char *str)
     return 0;
   else
     {
-      int r = strcoll (*args, str) == 0;
+      int r = strcmp (*args, str) == 0;
       args += r;
       return r;
     }
@@ -668,7 +668,7 @@ eval2 (void)
       r = eval3 ();
       tostring (l);
       tostring (r);
-      lval = strcoll (l->u.s, r->u.s);
+      lval = strcmp (l->u.s, r->u.s);
       rval = 0;
       if (toarith (l) && toarith (r))
    {

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/expr.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/expr.c used in: Step 746, Step 865
/* expr -- evaluate expressions.
   Copyright (C) 86, 1991-1997, 1999-2002 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.  */

/* Author: Mike Parker.

   This program evaluates expressions.  Each token (operator, operand,
   parenthesis) of the expression must be a seperate argument.  The
   parser used is a reasonably general one, though any incarnation of
   it is language-specific.  It is especially nice for expressions.

   No parse tree is needed; a new node is evaluated immediately.
   One function can handle multiple operators all of equal precedence,
   provided they all associate ((x op x) op x).

   Define EVAL_TRACE to print an evaluation trace.  */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include "system.h"

#include <regex.h>
#include "long-options.h"
#include "error.h"
#include "closeout.h"
#include "inttostr.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "expr"

#define AUTHORS "Mike Parker"

#undef NEW
#define NEW(Type) XMALLOC (Type, 1)
#define OLD(x) free ((char *) x)

/* The kinds of value we can have.  */
enum valtype
{
  integer,
  string
};
typedef enum valtype TYPE;

/* A value is.... */
struct valinfo
{
  TYPE type;            /* Which kind. */
  union
  {             /* The value itself. */
    intmax_t i;
    char *s;
  } u;
};
typedef struct valinfo VALUE;

/* The arguments given to the program, minus the program name.  */
static char **args;

/* The name this program was run with. */
char *program_name;

static VALUE *eval (void);
static int nomoreargs (void);
static int null (VALUE *v);
static void printv (VALUE *v);

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s EXPRESSION\n\
  or:  %s OPTION\n\
"),
          program_name, program_name);
      putchar ('\n');
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
Print the value of EXPRESSION to standard output.  A blank line below\n\
separates increasing precedence groups.  EXPRESSION may be:\n\
\n\
  ARG1 | ARG2       ARG1 if it is neither null nor 0, otherwise ARG2\n\
\n\
  ARG1 & ARG2       ARG1 if neither argument is null or 0, otherwise 0\n\
"), stdout);
      fputs (_("\
\n\
  ARG1 < ARG2       ARG1 is less than ARG2\n\
  ARG1 <= ARG2      ARG1 is less than or equal to ARG2\n\
  ARG1 = ARG2       ARG1 is equal to ARG2\n\
  ARG1 != ARG2      ARG1 is unequal to ARG2\n\
  ARG1 >= ARG2      ARG1 is greater than or equal to ARG2\n\
  ARG1 > ARG2       ARG1 is greater than ARG2\n\
"), stdout);
      fputs (_("\
\n\
  ARG1 + ARG2       arithmetic sum of ARG1 and ARG2\n\
  ARG1 - ARG2       arithmetic difference of ARG1 and ARG2\n\
"), stdout);
      fputs (_("\
\n\
  ARG1 * ARG2       arithmetic product of ARG1 and ARG2\n\
  ARG1 / ARG2       arithmetic quotient of ARG1 divided by ARG2\n\
  ARG1 % ARG2       arithmetic remainder of ARG1 divided by ARG2\n\
"), stdout);
      fputs (_("\
\n\
  STRING : REGEXP   anchored pattern match of REGEXP in STRING\n\
\n\
  match STRING REGEXP        same as STRING : REGEXP\n\
  substr STRING POS LENGTH   substring of STRING, POS counted from 1\n\
  index STRING CHARS         index in STRING where any CHARS is found, or 0\n\
  length STRING              length of STRING\n\
"), stdout);
      fputs (_("\
  + TOKEN                    interpret TOKEN as a string, even if it is a\n\
                               keyword like `match' or an operator like `/'\n\
\n\
  ( EXPRESSION )             value of EXPRESSION\n\
"), stdout);
      fputs (_("\
\n\
Beware that many operators need to be escaped or quoted for shells.\n\
Comparisons are arithmetic if both ARGs are numbers, else lexicographical.\n\
Pattern matches return the string matched between \\( and \\) or null; if\n\
\\( and \\) are not used, they return the number of characters matched or 0.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  VALUE *v;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              AUTHORS, usage);
  /* The above handles --help and --version.
     Since there is no other invocation of getopt, handle `--' here.  */
  if (argc > 1 && STREQ (argv[1], "--"))
    {
      --argc;
      ++argv;
    }

  if (argc == 1)
    {
      error (0, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }

  args = argv + 1;

  v = eval ();
  if (!nomoreargs ())
    error (2, 0, _("syntax error"));
  printv (v);

  exit (null (v));
}

/* Return a VALUE for I.  */

static VALUE *
int_value (intmax_t i)
{
  VALUE *v;

  v = NEW (VALUE);
  v->type = integer;
  v->u.i = i;
  return v;
}

/* Return a VALUE for S.  */

static VALUE *
str_value (char *s)
{
  VALUE *v;

  v = NEW (VALUE);
  v->type = string;
  v->u.s = xstrdup (s);
  return v;
}

/* Free VALUE V, including structure components.  */

static void
freev (VALUE *v)
{
  if (v->type == string)
    free (v->u.s);
  OLD (v);
}

/* Print VALUE V.  */

static void
printv (VALUE *v)
{
  char *p;
  char buf[INT_STRLEN_BOUND (intmax_t) + 1];

  switch (v->type)
    {
    case integer:
      p = imaxtostr (v->u.i, buf);
      break;
    case string:
      p = v->u.s;
      break;
    default:
      abort ();
    }

  puts (p);
}

/* Return nonzero if V is a null-string or zero-number.  */

static int
null (VALUE *v)
{
  switch (v->type)
    {
    case integer:
      return v->u.i == 0;
    case string:
      return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0;
    default:
      abort ();
    }
}

/* Coerce V to a string value (can't fail).  */

static void
tostring (VALUE *v)
{
  char buf[INT_STRLEN_BOUND (intmax_t) + 1];

  switch (v->type)
    {
    case integer:
      v->u.s = xstrdup (imaxtostr (v->u.i, buf));
      v->type = string;
      break;
    case string:
      break;
    default:
      abort ();
    }
}

/* Coerce V to an integer value.  Return 1 on success, 0 on failure.  */

static int
toarith (VALUE *v)
{
  intmax_t i;
  int neg;
  char *cp;

  switch (v->type)
    {
    case integer:
      return 1;
    case string:
      i = 0;
      cp = v->u.s;
      neg = (*cp == '-');
      if (neg)
    cp++;

      do
    {
      if (ISDIGIT (*cp))
        i = i * 10 + *cp - '0';
      else
        return 0;
    }
      while (*++cp);

      free (v->u.s);
      v->u.i = i * (neg ? -1 : 1);
      v->type = integer;
      return 1;
    default:
      abort ();
    }
}

/* Return nonzero and advance if the next token matches STR exactly.
   STR must not be NULL.  */

static int
nextarg (char *str)
{
  if (*args == NULL)
    return 0;
  else
    {
      int r = strcoll (*args, str) == 0;
      args += r;
      return r;
    }
}

/* Return nonzero if there no more tokens.  */

static int
nomoreargs (void)
{
  return *args == 0;
}

#ifdef EVAL_TRACE
/* Print evaluation trace and args remaining.  */

static void
trace (fxn)
     char *fxn;
{
  char **a;

  printf ("%s:", fxn);
  for (a = args; *a; a++)
    printf (" %s", *a);
  putchar ('\n');
}
#endif

/* Do the : operator.
   SV is the VALUE for the lhs (the string),
   PV is the VALUE for the rhs (the pattern).  */

static VALUE *
docolon (VALUE *sv, VALUE *pv)
{
  VALUE *v;
  const char *errmsg;
  struct re_pattern_buffer re_buffer;
  struct re_registers re_regs;
  size_t len;
  int matchlen;

  tostring (sv);
  tostring (pv);

  if (pv->u.s[0] == '^')
    {
      error (0, 0, _("\
warning: unportable BRE: `%s': using `^' as the first character\n\
of the basic regular expression is not portable; it is being ignored"),
         pv->u.s);
    }

  len = strlen (pv->u.s);
  memset (&re_buffer, 0, sizeof (re_buffer));
  memset (&re_regs, 0, sizeof (re_regs));
  re_buffer.allocated = 2 * len;
  if (re_buffer.allocated < len)
    xalloc_die ();
  re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
  re_buffer.translate = 0;
  re_syntax_options = RE_SYNTAX_POSIX_BASIC;
  errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
  if (errmsg)
    error (2, 0, "%s", errmsg);

  matchlen = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
  if (0 <= matchlen)
    {
      /* Were \(...\) used? */
      if (re_buffer.re_nsub > 0)/* was (re_regs.start[1] >= 0) */
    {
      sv->u.s[re_regs.end[1]] = '\0';
      v = str_value (sv->u.s + re_regs.start[1]);
    }
      else
    v = int_value (matchlen);
    }
  else
    {
      /* Match failed -- return the right kind of null.  */
      if (re_buffer.re_nsub > 0)
    v = str_value ("");
      else
    v = int_value (0);
    }
  free (re_buffer.buffer);
  return v;
}

/* Handle bare operands and ( expr ) syntax.  */

static VALUE *
eval7 (void)
{
  VALUE *v;

#ifdef EVAL_TRACE
  trace ("eval7");
#endif
  if (nomoreargs ())
    error (2, 0, _("syntax error"));

  if (nextarg ("("))
    {
      v = eval ();
      if (!nextarg (")"))
    error (2, 0, _("syntax error"));
      return v;
    }

  if (nextarg (")"))
    error (2, 0, _("syntax error"));

  return str_value (*args++);
}

/* Handle match, substr, index, and length keywords, and quoting "+".  */

static VALUE *
eval6 (void)
{
  VALUE *l;
  VALUE *r;
  VALUE *v;
  VALUE *i1;
  VALUE *i2;

#ifdef EVAL_TRACE
  trace ("eval6");
#endif
  if (nextarg ("+"))
    {
      if (nomoreargs ())
    error (2, 0, _("syntax error"));
      return str_value (*args++);
    }
  else if (nextarg ("length"))
    {
      r = eval6 ();
      tostring (r);
      v = int_value (strlen (r->u.s));
      freev (r);
      return v;
    }
  else if (nextarg ("match"))
    {
      l = eval6 ();
      r = eval6 ();
      v = docolon (l, r);
      freev (l);
      freev (r);
      return v;
    }
  else if (nextarg ("index"))
    {
      l = eval6 ();
      r = eval6 ();
      tostring (l);
      tostring (r);
      v = int_value (strcspn (l->u.s, r->u.s) + 1);
      if (v->u.i == strlen (l->u.s) + 1)
    v->u.i = 0;
      freev (l);
      freev (r);
      return v;
    }
  else if (nextarg ("substr"))
    {
      l = eval6 ();
      i1 = eval6 ();
      i2 = eval6 ();
      tostring (l);
      if (!toarith (i1) || !toarith (i2)
      || strlen (l->u.s) < i1->u.i
      || i1->u.i <= 0 || i2->u.i <= 0)
    v = str_value ("");
      else
    {
      v = NEW (VALUE);
      v->type = string;
      v->u.s = strncpy ((char *) xmalloc (i2->u.i + 1),
                l->u.s + i1->u.i - 1, i2->u.i);
      v->u.s[i2->u.i] = 0;
    }
      freev (l);
      freev (i1);
      freev (i2);
      return v;
    }
  else
    return eval7 ();
}

/* Handle : operator (pattern matching).
   Calls docolon to do the real work.  */

static VALUE *
eval5 (void)
{
  VALUE *l;
  VALUE *r;
  VALUE *v;

#ifdef EVAL_TRACE
  trace ("eval5");
#endif
  l = eval6 ();
  while (1)
    {
      if (nextarg (":"))
    {
      r = eval6 ();
      v = docolon (l, r);
      freev (l);
      freev (r);
      l = v;
    }
      else
    return l;
    }
}

/* Handle *, /, % operators.  */

static VALUE *
eval4 (void)
{
  VALUE *l;
  VALUE *r;
  enum { multiply, divide, mod } fxn;
  intmax_t val;

#ifdef EVAL_TRACE
  trace ("eval4");
#endif
  l = eval5 ();
  while (1)
    {
      if (nextarg ("*"))
    fxn = multiply;
      else if (nextarg ("/"))
    fxn = divide;
      else if (nextarg ("%"))
    fxn = mod;
      else
    return l;
      r = eval5 ();
      if (!toarith (l) || !toarith (r))
    error (2, 0, _("non-numeric argument"));
      if (fxn == multiply)
    val = l->u.i * r->u.i;
      else
    {
      if (r->u.i == 0)
        error (2, 0, _("division by zero"));
      val = fxn == divide ? l->u.i / r->u.i : l->u.i % r->u.i;
    }
      freev (l);
      freev (r);
      l = int_value (val);
    }
}

/* Handle +, - operators.  */

static VALUE *
eval3 (void)
{
  VALUE *l;
  VALUE *r;
  enum { plus, minus } fxn;
  intmax_t val;

#ifdef EVAL_TRACE
  trace ("eval3");
#endif
  l = eval4 ();
  while (1)
    {
      if (nextarg ("+"))
    fxn = plus;
      else if (nextarg ("-"))
    fxn = minus;
      else
    return l;
      r = eval4 ();
      if (!toarith (l) || !toarith (r))
    error (2, 0, _("non-numeric argument"));
      val = fxn == plus ? l->u.i + r->u.i : l->u.i - r->u.i;
      freev (l);
      freev (r);
      l = int_value (val);
    }
}

/* Handle comparisons.  */

static VALUE *
eval2 (void)
{
  VALUE *l;
  VALUE *r;
  enum
  {
    less_than, less_equal, equal, not_equal, greater_equal, greater_than
  } fxn;
  int val;
  intmax_t lval;
  intmax_t rval;

#ifdef EVAL_TRACE
  trace ("eval2");
#endif
  l = eval3 ();
  while (1)
    {
      if (nextarg ("<"))
    fxn = less_than;
      else if (nextarg ("<="))
    fxn = less_equal;
      else if (nextarg ("=") || nextarg ("=="))
    fxn = equal;
      else if (nextarg ("!="))
    fxn = not_equal;
      else if (nextarg (">="))
    fxn = greater_equal;
      else if (nextarg (">"))
    fxn = greater_than;
      else
    return l;
      r = eval3 ();
      tostring (l);
      tostring (r);
      lval = strcoll (l->u.s, r->u.s);
      rval = 0;
      if (toarith (l) && toarith (r))
    {
      lval = l->u.i;
      rval = r->u.i;
    }
      switch (fxn)
    {
    case less_than:     val = (lval <  rval); break;
    case less_equal:    val = (lval <= rval); break;
    case equal:         val = (lval == rval); break;
    case not_equal:     val = (lval != rval); break;
    case greater_equal: val = (lval >= rval); break;
    case greater_than:  val = (lval >  rval); break;
    default: abort ();
    }
      freev (l);
      freev (r);
      l = int_value (val);
    }
}

/* Handle &.  */

static VALUE *
eval1 (void)
{
  VALUE *l;
  VALUE *r;

#ifdef EVAL_TRACE
  trace ("eval1");
#endif
  l = eval2 ();
  while (1)
    {
      if (nextarg ("&"))
    {
      r = eval2 ();
      if (null (l) || null (r))
        {
          freev (l);
          freev (r);
          l = int_value (0);
        }
      else
        freev (r);
    }
      else
    return l;
    }
}

/* Handle |.  */

static VALUE *
eval (void)
{
  VALUE *l;
  VALUE *r;

#ifdef EVAL_TRACE
  trace ("eval");
#endif
  l = eval1 ();
  while (1)
    {
      if (nextarg ("|"))
    {
      r = eval1 ();
      if (null (l))
        {
          freev (l);
          l = r;
        }
      else
        freev (r);
    }
      else
    return l;
    }
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/sort-locale.patch

File: ../git/live-bootstrap/sysa/coreutils-5.0/patches/sort-locale.patch used in: Step 747
SPDX-FileCopyrightText: 2023 Emily Trau <emily@downunderctf.com>

SPDX-License-Identifier: GPL-2.0-or-later

strcoll() does not exist in mes libc, change it to strcmp.
hard_LC_COLLATE is used but not declared when HAVE_SETLOCALE is unset.

--- lib/memcoll.c
+++ lib/memcoll.c
@@ -47,7 +47,7 @@ memcoll (char *s1, size_t s1len, char *s2, size_t s2len)
   s1[s1len++] = '\0';
   s2[s2len++] = '\0';
 
-  while (! (errno = 0, (diff = strcoll (s1, s2)) || errno))
+  while (! (errno = 0, (diff = strcmp (s1, s2)) || errno))
     {
       /* strcoll found no difference, but perhaps it was fooled by NUL
     characters in the data.  Work around this problem by advancing

--- src/sort.c
+++ src/sort.c
@@ -91,13 +91,13 @@ double strtod ();
 #define NEGATION_SIGN   '-'
 #define NUMERIC_ZERO    '0'
 
+/* Nonzero if the corresponding locales are hard.  */
+static int hard_LC_COLLATE;
 #if HAVE_SETLOCALE
 
 static char decimal_point;
 static int th_sep; /* if CHAR_MAX + 1, then there is no thousands separator */
 
-/* Nonzero if the corresponding locales are hard.  */
-static int hard_LC_COLLATE;
 # if HAVE_NL_LANGINFO
 static int hard_LC_TIME;
 # endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/memcoll.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/memcoll.c used in: Step 747, Step 795
/* Locale-specific memory comparison.
   Copyright 1999, 2002 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.  */

/* Contributed by Paul Eggert <eggert@twinsun.com>.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#include <sys/types.h>

#if HAVE_STRING_H
# include <string.h>
#endif

/* Compare S1 (with length S1LEN) and S2 (with length S2LEN) according
   to the LC_COLLATE locale.  S1 and S2 do not overlap, and are not
   adjacent.  Temporarily modify the bytes after S1 and S2, but
   restore their original contents before returning.  Set errno to an
   error number if there is an error, and to zero otherwise.  */
int
memcoll (char *s1, size_t s1len, char *s2, size_t s2len)
{
  int diff;
  char n1 = s1[s1len];
  char n2 = s2[s2len];

  s1[s1len++] = '\0';
  s2[s2len++] = '\0';

  while (! (errno = 0, (diff = strcoll (s1, s2)) || errno))
    {
      /* strcoll found no difference, but perhaps it was fooled by NUL
     characters in the data.  Work around this problem by advancing
     past the NUL chars.  */
      size_t size1 = strlen (s1) + 1;
      size_t size2 = strlen (s2) + 1;
      s1 += size1;
      s2 += size2;
      s1len -= size1;
      s2len -= size2;

      if (s1len == 0)
    {
      if (s2len != 0)
        diff = -1;
      break;
    }
      else if (s2len == 0)
    {
      diff = 1;
      break;
    }
    }

  s1[s1len - 1] = n1;
  s2[s2len - 1] = n2;

  return diff;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/sort.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/sort.c used in: Step 747, Step 923
/* sort - sort lines of text (with all kinds of options).
   Copyright (C) 88, 1991-2002 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.

   Written December 1988 by Mike Haertel.
   The author may be reached (Email) at the address mike@gnu.ai.mit.edu,
   or (US mail) as Mike Haertel c/o Free Software Foundation.

   Ørn E. Hansen added NLS support in 1997.  */

#include <config.h>

#include <getopt.h>
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <assert.h>
#include "system.h"
#include "long-options.h"
#include "error.h"
#include "hard-locale.h"
#include "inttostr.h"
#include "physmem.h"
#include "posixver.h"
#include "stdio-safer.h"
#include "xmemcoll.h"
#include "xstrtol.h"

#if HAVE_SYS_RESOURCE_H
# include <sys/resource.h>
#endif
#ifndef RLIMIT_DATA
struct rlimit { size_t rlim_cur; };
# define getrlimit(Resource, Rlp) (-1)
#endif

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "sort"

#define AUTHORS N_ ("Mike Haertel and Paul Eggert")

#if HAVE_LANGINFO_CODESET
# include <langinfo.h>
#endif

#ifndef SA_NOCLDSTOP
# define sigprocmask(How, Set, Oset) /* empty */
# define sigset_t int
#endif

#ifndef STDC_HEADERS
double strtod ();
#endif

/* Undefine, to avoid warning about redefinition on some systems.  */
/* FIXME: Remove these: use MIN/MAX from sys2.h.  */
#undef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#undef max
#define max(a, b) ((a) > (b) ? (a) : (b))

#define UCHAR_LIM (UCHAR_MAX + 1)
#define UCHAR(c) ((unsigned char) (c))

#ifndef DEFAULT_TMPDIR
# define DEFAULT_TMPDIR "/tmp"
#endif

/* Use this as exit status in case of error, not EXIT_FAILURE.  This
   is necessary because EXIT_FAILURE is usually 1 and POSIX requires
   that sort exit with status 1 IFF invoked with -c and the input is
   not properly sorted.  Any other irregular exit must exit with a
   status code greater than 1.  */
#define SORT_FAILURE 2
#define SORT_OUT_OF_ORDER 1

#define C_DECIMAL_POINT '.'
#define NEGATION_SIGN   '-'
#define NUMERIC_ZERO    '0'

#if HAVE_SETLOCALE

static char decimal_point;
static int th_sep; /* if CHAR_MAX + 1, then there is no thousands separator */

/* Nonzero if the corresponding locales are hard.  */
static int hard_LC_COLLATE;
# if HAVE_NL_LANGINFO
static int hard_LC_TIME;
# endif

# define IS_THOUSANDS_SEP(x) ((x) == th_sep)

#else

# define decimal_point C_DECIMAL_POINT
# define IS_THOUSANDS_SEP(x) 0

#endif

#define NONZERO(x) (x != 0)

/* The kind of blanks for '-b' to skip in various options. */
enum blanktype { bl_start, bl_end, bl_both };

/* The character marking end of line. Default to \n. */
static int eolchar = '\n';

/* Lines are held in core as counted strings. */
struct line
{
  char *text;           /* Text of the line. */
  size_t length;        /* Length including final newline. */
  char *keybeg;         /* Start of first key. */
  char *keylim;         /* Limit of first key. */
};

/* Input buffers. */
struct buffer
{
  char *buf;            /* Dynamically allocated buffer,
                   partitioned into 3 regions:
                   - input data;
                   - unused area;
                   - an array of lines, in reverse order.  */
  size_t used;          /* Number of bytes used for input data.  */
  size_t nlines;        /* Number of lines in the line array.  */
  size_t alloc;         /* Number of bytes allocated. */
  size_t left;          /* Number of bytes left from previous reads. */
  size_t line_bytes;        /* Number of bytes to reserve for each line. */
  int eof;          /* An EOF has been read.  */
};

struct keyfield
{
  size_t sword;         /* Zero-origin 'word' to start at. */
  size_t schar;         /* Additional characters to skip. */
  int skipsblanks;      /* Skip leading white space at start. */
  size_t eword;         /* Zero-origin first word after field. */
  size_t echar;         /* Additional characters in field. */
  int skipeblanks;      /* Skip trailing white space at finish. */
  int *ignore;          /* Boolean array of characters to ignore. */
  char *translate;      /* Translation applied to characters. */
  int numeric;          /* Flag for numeric comparison.  Handle
                   strings of digits with optional decimal
                   point, but no exponential notation. */
  int general_numeric;      /* Flag for general, numeric comparison.
                   Handle numbers in exponential notation. */
  int month;            /* Flag for comparison by month name. */
  int reverse;          /* Reverse the sense of comparison. */
  struct keyfield *next;    /* Next keyfield to try. */
};

struct month
{
  char *name;
  int val;
};

/* The name this program was run with. */
char *program_name;

/* FIXME: None of these tables work with multibyte character sets.
   Also, there are many other bugs when handling multibyte characters,
   or even unibyte encodings where line boundaries are not in the
   initial shift state.  One way to fix this is to rewrite `sort' to
   use wide characters internally, but doing this with good
   performance is a bit tricky.  */

/* Table of white space. */
static int blanks[UCHAR_LIM];

/* Table of non-printing characters. */
static int nonprinting[UCHAR_LIM];

/* Table of non-dictionary characters (not letters, digits, or blanks). */
static int nondictionary[UCHAR_LIM];

/* Translation table folding lower case to upper.  */
static char fold_toupper[UCHAR_LIM];

#define MONTHS_PER_YEAR 12

/* Table mapping month names to integers.
   Alphabetic order allows binary search. */
static struct month monthtab[] =
{
  {"APR", 4},
  {"AUG", 8},
  {"DEC", 12},
  {"FEB", 2},
  {"JAN", 1},
  {"JUL", 7},
  {"JUN", 6},
  {"MAR", 3},
  {"MAY", 5},
  {"NOV", 11},
  {"OCT", 10},
  {"SEP", 9}
};

/* During the merge phase, the number of files to merge at once. */
#define NMERGE 16

/* Minimum size for a merge or check buffer.  */
#define MIN_MERGE_BUFFER_SIZE (2 + sizeof (struct line))

/* Minimum sort size; the code might not work with smaller sizes.  */
#define MIN_SORT_SIZE (NMERGE * MIN_MERGE_BUFFER_SIZE)

/* The number of bytes needed for a merge or check buffer, which can
   function relatively efficiently even if it holds only one line.  If
   a longer line is seen, this value is increased.  */
static size_t merge_buffer_size = MAX (MIN_MERGE_BUFFER_SIZE, 256 * 1024);

/* The approximate maximum number of bytes of main memory to use, as
   specified by the user.  Zero if the user has not specified a size.  */
static size_t sort_size;

/* The guessed size for non-regular files.  */
#define INPUT_FILE_SIZE_GUESS (1024 * 1024)

/* Array of directory names in which any temporary files are to be created. */
static char const **temp_dirs;

/* Number of temporary directory names used.  */
static size_t temp_dir_count;

/* Number of allocated slots in temp_dirs.  */
static size_t temp_dir_alloc;

/* Flag to reverse the order of all comparisons. */
static int reverse;

/* Flag for stable sort.  This turns off the last ditch bytewise
   comparison of lines, and instead leaves lines in the same order
   they were read if all keys compare equal.  */
static int stable;

/* Tab character separating fields.  If NUL, then fields are separated
   by the empty string between a non-whitespace character and a whitespace
   character. */
static char tab;

/* Flag to remove consecutive duplicate lines from the output.
   Only the last of a sequence of equal lines will be output. */
static int unique;

/* Nonzero if any of the input files are the standard input. */
static int have_read_stdin;

/* List of key field comparisons to be tried.  */
static struct keyfield *keylist;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
          program_name);
      fputs (_("\
Write sorted concatenation of all FILE(s) to standard output.\n\
\n\
Ordering options:\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -b, --ignore-leading-blanks ignore leading blanks\n\
  -d, --dictionary-order      consider only blanks and alphanumeric characters\n\
  -f, --ignore-case           fold lower case to upper case characters\n\
"), stdout);
      fputs (_("\
  -g, --general-numeric-sort  compare according to general numerical value\n\
  -i, --ignore-nonprinting    consider only printable characters\n\
  -M, --month-sort            compare (unknown) < `JAN' < ... < `DEC'\n\
  -n, --numeric-sort          compare according to string numerical value\n\
  -r, --reverse               reverse the result of comparisons\n\
\n\
"), stdout);
      fputs (_("\
Other options:\n\
\n\
  -c, --check               check whether input is sorted; do not sort\n\
  -k, --key=POS1[,POS2]     start a key at POS1, end it at POS 2 (origin 1)\n\
  -m, --merge               merge already sorted files; do not sort\n\
  -o, --output=FILE         write result to FILE instead of standard output\n\
  -s, --stable              stabilize sort by disabling last-resort comparison\n\
  -S, --buffer-size=SIZE    use SIZE for main memory buffer\n\
"), stdout);
      printf (_("\
  -t, --field-separator=SEP use SEP instead of non- to whitespace transition\n\
  -T, --temporary-directory=DIR  use DIR for temporaries, not $TMPDIR or %s\n\
                              multiple options specify multiple directories\n\
  -u, --unique              with -c: check for strict ordering\n\
                              otherwise: output only the first of an equal run\n\
"), DEFAULT_TMPDIR);
      fputs (_("\
  -z, --zero-terminated     end lines with 0 byte, not newline\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
POS is F[.C][OPTS], where F is the field number and C the character position\n\
in the field.  OPTS is one or more single-letter ordering options, which\n\
override global ordering options for that key.  If no key is given, use the\n\
entire line as the key.\n\
\n\
SIZE may be followed by the following multiplicative suffixes:\n\
"), stdout);
      fputs (_("\
% 1% of memory, b 1, K 1024 (default), and so on for M, G, T, P, E, Z, Y.\n\
\n\
With no FILE, or when FILE is -, read standard input.\n\
\n\
*** WARNING ***\n\
The locale specified by the environment affects sort order.\n\
Set LC_ALL=C to get the traditional sort order that uses\n\
native byte values.\n\
"), stdout );
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  /* Don't use EXIT_FAILURE here in case it is defined to be 1.
     POSIX requires that sort return 1 IFF invoked with -c and
     the input is not properly sorted.  */
  assert (status == 0 || status == SORT_FAILURE);
  exit (status);
}

#define COMMON_SHORT_OPTIONS "-bcdfgik:mMno:rsS:t:T:uz"

static struct option const long_options[] =
{
  {"ignore-leading-blanks", no_argument, NULL, 'b'},
  {"check", no_argument, NULL, 'c'},
  {"dictionary-order", no_argument, NULL, 'd'},
  {"ignore-case", no_argument, NULL, 'f'},
  {"general-numeric-sort", no_argument, NULL, 'g'},
  {"ignore-nonprinting", no_argument, NULL, 'i'},
  {"key", required_argument, NULL, 'k'},
  {"merge", no_argument, NULL, 'm'},
  {"month-sort", no_argument, NULL, 'M'},
  {"numeric-sort", no_argument, NULL, 'n'},
  {"output", required_argument, NULL, 'o'},
  {"reverse", no_argument, NULL, 'r'},
  {"stable", no_argument, NULL, 's'},
  {"buffer-size", required_argument, NULL, 'S'},
  {"field-separator", required_argument, NULL, 't'},
  {"temporary-directory", required_argument, NULL, 'T'},
  {"unique", no_argument, NULL, 'u'},
  {"zero-terminated", no_argument, NULL, 'z'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {0, 0, 0, 0},
};

/* The set of signals that are caught.  */
static sigset_t caught_signals;

/* The list of temporary files. */
struct tempnode
{
  struct tempnode *volatile next;
  char name[1];  /* Actual size is 1 + file name length.  */
};
static struct tempnode *volatile temphead;

/* Clean up any remaining temporary files. */

static void
cleanup (void)
{
  struct tempnode *node;

  for (node = temphead; node; node = node->next)
    unlink (node->name);
}

/* Report MESSAGE for FILE, then clean up and exit.  */

static void die (char const *, char const *) ATTRIBUTE_NORETURN;
static void
die (char const *message, char const *file)
{
  error (0, errno, "%s: %s", message, file);
  exit (SORT_FAILURE);
}

/* Create a new temporary file, returning its newly allocated name.
   Store into *PFP a stream open for writing.  */

static char *
create_temp_file (FILE **pfp)
{
  static char const slashbase[] = "/sortXXXXXX";
  static size_t temp_dir_index;
  sigset_t oldset;
  int fd;
  int saved_errno;
  char const *temp_dir = temp_dirs[temp_dir_index];
  size_t len = strlen (temp_dir);
  struct tempnode *node =
    (struct tempnode *) xmalloc (sizeof node->next + len + sizeof slashbase);
  char *file = node->name;

  memcpy (file, temp_dir, len);
  memcpy (file + len, slashbase, sizeof slashbase);
  node->next = temphead;
  if (++temp_dir_index == temp_dir_count)
    temp_dir_index = 0;

  /* Create the temporary file in a critical section, to avoid races.  */
  sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
  fd = mkstemp (file);
  if (0 <= fd)
    temphead = node;
  saved_errno = errno;
  sigprocmask (SIG_SETMASK, &oldset, NULL);
  errno = saved_errno;

  if (fd < 0 || (*pfp = fdopen (fd, "w")) == NULL)
    die (_("cannot create temporary file"), file);

  return file;
}

static FILE *
xfopen (const char *file, const char *how)
{
  FILE *fp;

  if (STREQ (file, "-"))
    {
      if (*how == 'r')
    {
      have_read_stdin = 1;
      fp = stdin;
    }
      else
    fp = stdout;
    }
  else
    {
      if ((fp = fopen_safer (file, how)) == NULL)
    die (_("open failed"), file);
    }

  return fp;
}

/* Close FP, whose name is FILE, and report any errors.  */

static void
xfclose (FILE *fp, char const *file)
{
  if (fp == stdin)
    {
      /* Allow reading stdin from tty more than once. */
      if (feof (fp))
    clearerr (fp);
    }
  else
    {
      if (fclose (fp) != 0)
    die (_("close failed"), file);
    }
}

static void
write_bytes (const char *buf, size_t n_bytes, FILE *fp, const char *output_file)
{
  if (fwrite (buf, 1, n_bytes, fp) != n_bytes)
    die (_("write failed"), output_file);
}

/* Append DIR to the array of temporary directory names.  */
static void
add_temp_dir (char const *dir)
{
  if (temp_dir_count == temp_dir_alloc)
    {
      temp_dir_alloc = temp_dir_alloc ? temp_dir_alloc * 2 : 16;
      temp_dirs = xrealloc (temp_dirs, sizeof (temp_dirs) * temp_dir_alloc);
    }

  temp_dirs[temp_dir_count++] = dir;
}

/* Search through the list of temporary files for NAME;
   remove it if it is found on the list. */

static void
zaptemp (const char *name)
{
  struct tempnode *volatile *pnode;
  struct tempnode *node;

  for (pnode = &temphead; (node = *pnode); pnode = &node->next)
    if (node->name == name)
      {
    unlink (name);
    *pnode = node->next;
    free ((char *) node);
    break;
      }
}

#if HAVE_NL_LANGINFO

static int
struct_month_cmp (const void *m1, const void *m2)
{
  return strcmp (((const struct month *) m1)->name,
         ((const struct month *) m2)->name);
}

#endif

/* Initialize the character class tables. */

static void
inittables (void)
{
  int i;

  for (i = 0; i < UCHAR_LIM; ++i)
    {
      if (ISBLANK (i))
    blanks[i] = 1;
      if (!ISPRINT (i))
    nonprinting[i] = 1;
      if (!ISALNUM (i) && !ISBLANK (i))
    nondictionary[i] = 1;
      if (ISLOWER (i))
    fold_toupper[i] = toupper (i);
      else
    fold_toupper[i] = i;
    }

#if HAVE_NL_LANGINFO
  /* If we're not in the "C" locale, read different names for months.  */
  if (hard_LC_TIME)
    {
      for (i = 0; i < MONTHS_PER_YEAR; i++)
    {
      char *s;
      size_t s_len;
      size_t j;
      char *name;

      s = (char *) nl_langinfo (ABMON_1 + i);
      s_len = strlen (s);
      monthtab[i].name = name = (char *) xmalloc (s_len + 1);
      monthtab[i].val = i + 1;

      for (j = 0; j < s_len; j++)
        name[j] = fold_toupper[UCHAR (s[j])];
      name[j] = '\0';
    }
      qsort ((void *) monthtab, MONTHS_PER_YEAR,
         sizeof (struct month), struct_month_cmp);
    }
#endif
}

/* Specify the amount of main memory to use when sorting.  */
static void
specify_sort_size (char const *s)
{
  uintmax_t n;
  char *suffix;
  enum strtol_error e = xstrtoumax (s, &suffix, 10, &n, "EgGkKmMPtTYZ");

  /* The default unit is KiB.  */
  if (e == LONGINT_OK && ISDIGIT (suffix[-1]))
    {
      if (n <= UINTMAX_MAX / 1024)
    n *= 1024;
      else
    e = LONGINT_OVERFLOW;
    }

  /* A 'b' suffix means bytes; a '%' suffix means percent of memory.  */
  if (e == LONGINT_INVALID_SUFFIX_CHAR && ISDIGIT (suffix[-1]) && ! suffix[1])
    switch (suffix[0])
      {
      case 'b':
    e = LONGINT_OK;
    break;

      case '%':
    {
      double mem = physmem_total () * n / 100;

      /* Use "<", not "<=", to avoid problems with rounding.  */
      if (mem < UINTMAX_MAX)
        {
          n = mem;
          e = LONGINT_OK;
        }
      else
        e = LONGINT_OVERFLOW;
    }
    break;
      }

  if (e == LONGINT_OK)
    {
      sort_size = n;
      if (sort_size == n)
    {
      sort_size = MAX (sort_size, MIN_SORT_SIZE);
      return;
    }

      e = LONGINT_OVERFLOW;
    }

  STRTOL_FATAL_ERROR (s, _("sort size"), e);
}

/* Return the default sort size.  */
static size_t
default_sort_size (void)
{
  /* Let MEM be available memory or 1/8 of total memory, whichever
     is greater.  */
  double avail = physmem_available ();
  double total = physmem_total ();
  double mem = MAX (avail, total / 8);
  struct rlimit rlimit;

  /* Let SIZE be MEM, but no more than the maximum object size or
     system resource limits.  Avoid the MIN macro here, as it is not
     quite right when only one argument is floating point.  Don't
     bother to check for values like RLIM_INFINITY since in practice
     they are not much less than SIZE_MAX.  */
  size_t size = SIZE_MAX;
  if (mem < size)
    size = mem;
  if (getrlimit (RLIMIT_DATA, &rlimit) == 0 && rlimit.rlim_cur < size)
    size = rlimit.rlim_cur;
#ifdef RLIMIT_AS
  if (getrlimit (RLIMIT_AS, &rlimit) == 0 && rlimit.rlim_cur < size)
    size = rlimit.rlim_cur;
#endif

  /* Leave a large safety margin for the above limits, as failure can
     occur when they are exceeded.  */
  size /= 2;

#ifdef RLIMIT_RSS
  /* Leave a 1/16 margin for RSS to leave room for code, stack, etc.
     Exceeding RSS is not fatal, but can be quite slow.  */
  if (getrlimit (RLIMIT_RSS, &rlimit) == 0 && rlimit.rlim_cur / 16 * 15 < size)
    size = rlimit.rlim_cur / 16 * 15;
#endif

  /* Use no less than the minimum.  */
  return MAX (size, MIN_SORT_SIZE);
}

/* Return the sort buffer size to use with the input files identified
   by FPS and FILES, which are alternate paths to the same files.
   NFILES gives the number of input files; NFPS may be less.  Assume
   that each input line requires LINE_BYTES extra bytes' worth of line
   information.  Return at most SIZE_BOUND.  */

static size_t
sort_buffer_size (FILE *const *fps, int nfps,
          char *const *files, int nfiles,
          size_t line_bytes, size_t size_bound)
{
  /* In the worst case, each input byte is a newline.  */
  size_t worst_case_per_input_byte = line_bytes + 1;

  /* Keep enough room for one extra input line and an extra byte.
     This extra room might be needed when preparing to read EOF.  */
  size_t size = worst_case_per_input_byte + 1;

  int i;

  for (i = 0; i < nfiles; i++)
    {
      struct stat st;
      off_t file_size;
      size_t worst_case;

      if ((i < nfps ? fstat (fileno (fps[i]), &st)
       : strcmp (files[i], "-") == 0 ? fstat (STDIN_FILENO, &st)
       : stat (files[i], &st))
      != 0)
    die (_("stat failed"), files[i]);

      file_size = S_ISREG (st.st_mode) ? st.st_size : INPUT_FILE_SIZE_GUESS;

      /* Add the amount of memory needed to represent the worst case
     where the input consists entirely of newlines followed by a
     single non-newline.  Check for overflow.  */
      worst_case = file_size * worst_case_per_input_byte + 1;
      if (file_size != worst_case / worst_case_per_input_byte
      || size_bound - size <= worst_case)
    return size_bound;
      size += worst_case;
    }

  return size;
}

/* Initialize BUF.  Reserve LINE_BYTES bytes for each line; LINE_BYTES
   must be at least sizeof (struct line).  Allocate ALLOC bytes
   initially.  */

static void
initbuf (struct buffer *buf, size_t line_bytes, size_t alloc)
{
  /* Ensure that the line array is properly aligned.  If the desired
     size cannot be allocated, repeatedly halve it until allocation
     succeeds.  The smaller allocation may hurt overall performance,
     but that's better than failing.  */
  for (;;)
    {
      alloc += sizeof (struct line) - alloc % sizeof (struct line);
      buf->buf = malloc (alloc);
      if (buf->buf)
    break;
      alloc /= 2;
      if (alloc <= line_bytes + 1)
    xalloc_die ();
    }

  buf->line_bytes = line_bytes;
  buf->alloc = alloc;
  buf->used = buf->left = buf->nlines = 0;
  buf->eof = 0;
}

/* Return one past the limit of the line array.  */

static inline struct line *
buffer_linelim (struct buffer const *buf)
{
  return (struct line *) (buf->buf + buf->alloc);
}

/* Return a pointer to the first character of the field specified
   by KEY in LINE. */

static char *
begfield (const struct line *line, const struct keyfield *key)
{
  register char *ptr = line->text, *lim = ptr + line->length - 1;
  register size_t sword = key->sword;
  register size_t schar = key->schar;
  register size_t remaining_bytes;

  if (tab)
    while (ptr < lim && sword--)
      {
    while (ptr < lim && *ptr != tab)
      ++ptr;
    if (ptr < lim)
      ++ptr;
      }
  else
    while (ptr < lim && sword--)
      {
    while (ptr < lim && blanks[UCHAR (*ptr)])
      ++ptr;
    while (ptr < lim && !blanks[UCHAR (*ptr)])
      ++ptr;
      }

  if (key->skipsblanks)
    while (ptr < lim && blanks[UCHAR (*ptr)])
      ++ptr;

  /* Advance PTR by SCHAR (if possible), but no further than LIM.  */
  remaining_bytes = lim - ptr;
  if (schar < remaining_bytes)
    ptr += schar;
  else
    ptr = lim;

  return ptr;
}

/* Return the limit of (a pointer to the first character after) the field
   in LINE specified by KEY. */

static char *
limfield (const struct line *line, const struct keyfield *key)
{
  register char *ptr = line->text, *lim = ptr + line->length - 1;
  register size_t eword = key->eword, echar = key->echar;
  register size_t remaining_bytes;

  /* Note: from the POSIX spec:
     The leading field separator itself is included in
     a field when -t is not used.  FIXME: move this comment up... */

  /* Move PTR past EWORD fields or to one past the last byte on LINE,
     whichever comes first.  If there are more than EWORD fields, leave
     PTR pointing at the beginning of the field having zero-based index,
     EWORD.  If a delimiter character was specified (via -t), then that
     `beginning' is the first character following the delimiting TAB.
     Otherwise, leave PTR pointing at the first `blank' character after
     the preceding field.  */
  if (tab)
    while (ptr < lim && eword--)
      {
    while (ptr < lim && *ptr != tab)
      ++ptr;
    if (ptr < lim && (eword | echar))
      ++ptr;
      }
  else
    while (ptr < lim && eword--)
      {
    while (ptr < lim && blanks[UCHAR (*ptr)])
      ++ptr;
    while (ptr < lim && !blanks[UCHAR (*ptr)])
      ++ptr;
      }

#ifdef POSIX_UNSPECIFIED
  /* The following block of code makes GNU sort incompatible with
     standard Unix sort, so it's ifdef'd out for now.
     The POSIX spec isn't clear on how to interpret this.
     FIXME: request clarification.

     From: kwzh@gnu.ai.mit.edu (Karl Heuer)
     Date: Thu, 30 May 96 12:20:41 -0400
     [Translated to POSIX 1003.1-2001 terminology by Paul Eggert.]

     [...]I believe I've found another bug in `sort'.

     $ cat /tmp/sort.in
     a b c 2 d
     pq rs 1 t
     $ textutils-1.15/src/sort -k1.7,1.7 </tmp/sort.in
     a b c 2 d
     pq rs 1 t
     $ /bin/sort -k1.7,1.7 </tmp/sort.in
     pq rs 1 t
     a b c 2 d

     Unix sort produced the answer I expected: sort on the single character
     in column 7.  GNU sort produced different results, because it disagrees
     on the interpretation of the key-end spec "M.N".  Unix sort reads this
     as "skip M-1 fields, then N-1 characters"; but GNU sort wants it to mean
     "skip M-1 fields, then either N-1 characters or the rest of the current
     field, whichever comes first".  This extra clause applies only to
     key-ends, not key-starts.
     */

  /* Make LIM point to the end of (one byte past) the current field.  */
  if (tab)
    {
      char *newlim;
      newlim = memchr (ptr, tab, lim - ptr);
      if (newlim)
    lim = newlim;
    }
  else
    {
      char *newlim;
      newlim = ptr;
      while (newlim < lim && blanks[UCHAR (*newlim)])
    ++newlim;
      while (newlim < lim && !blanks[UCHAR (*newlim)])
    ++newlim;
      lim = newlim;
    }
#endif

  /* If we're skipping leading blanks, don't start counting characters
     until after skipping past any leading blanks.  */
  if (key->skipsblanks)
    while (ptr < lim && blanks[UCHAR (*ptr)])
      ++ptr;

  /* Advance PTR by ECHAR (if possible), but no further than LIM.  */
  remaining_bytes = lim - ptr;
  if (echar < remaining_bytes)
    ptr += echar;
  else
    ptr = lim;

  return ptr;
}

/* FIXME */

static void
trim_trailing_blanks (const char *a_start, char **a_end)
{
  while (*a_end > a_start && blanks[UCHAR (*(*a_end - 1))])
    --(*a_end);
}

/* Fill BUF reading from FP, moving buf->left bytes from the end
   of buf->buf to the beginning first.  If EOF is reached and the
   file wasn't terminated by a newline, supply one.  Set up BUF's line
   table too.  FILE is the name of the file corresponding to FP.
   Return nonzero if some input was read.  */

static int
fillbuf (struct buffer *buf, register FILE *fp, char const *file)
{
  struct keyfield const *key = keylist;
  char eol = eolchar;
  size_t line_bytes = buf->line_bytes;
  size_t mergesize = merge_buffer_size - MIN_MERGE_BUFFER_SIZE;

  if (buf->eof)
    return 0;

  if (buf->used != buf->left)
    {
      memmove (buf->buf, buf->buf + buf->used - buf->left, buf->left);
      buf->used = buf->left;
      buf->nlines = 0;
    }

  for (;;)
    {
      char *ptr = buf->buf + buf->used;
      struct line *linelim = buffer_linelim (buf);
      struct line *line = linelim - buf->nlines;
      size_t avail = (char *) linelim - buf->nlines * line_bytes - ptr;
      char *line_start = buf->nlines ? line->text + line->length : buf->buf;

      while (line_bytes + 1 < avail)
    {
      /* Read as many bytes as possible, but do not read so many
         bytes that there might not be enough room for the
         corresponding line array.  The worst case is when the
         rest of the input file consists entirely of newlines,
         except that the last byte is not a newline.  */
      size_t readsize = (avail - 1) / (line_bytes + 1);
      size_t bytes_read = fread (ptr, 1, readsize, fp);
      char *ptrlim = ptr + bytes_read;
      char *p;
      avail -= bytes_read;

      if (bytes_read != readsize)
        {
          if (ferror (fp))
        die (_("read failed"), file);
          if (feof (fp))
        {
          buf->eof = 1;
          if (buf->buf == ptrlim)
            return 0;
          if (ptrlim[-1] != eol)
            *ptrlim++ = eol;
        }
        }

      /* Find and record each line in the just-read input.  */
      while ((p = memchr (ptr, eol, ptrlim - ptr)))
        {
          ptr = p + 1;
          line--;
          line->text = line_start;
          line->length = ptr - line_start;
          mergesize = MAX (mergesize, line->length);
          avail -= line_bytes;

          if (key)
        {
          /* Precompute the position of the first key for
                     efficiency. */
          line->keylim = (key->eword == (size_t) -1
                  ? p
                  : limfield (line, key));

          if (key->sword != (size_t) -1)
            line->keybeg = begfield (line, key);
          else
            {
              if (key->skipsblanks)
            while (blanks[UCHAR (*line_start)])
              line_start++;
              line->keybeg = line_start;
            }
          if (key->skipeblanks)
            trim_trailing_blanks (line->keybeg, &line->keylim);
        }

          line_start = ptr;
        }

      ptr = ptrlim;
      if (buf->eof)
        break;
    }

      buf->used = ptr - buf->buf;
      buf->nlines = buffer_linelim (buf) - line;
      if (buf->nlines != 0)
    {
      buf->left = ptr - line_start;
      merge_buffer_size = mergesize + MIN_MERGE_BUFFER_SIZE;
      return 1;
    }

      /* The current input line is too long to fit in the buffer.
     Double the buffer size and try again.  */
      if (2 * buf->alloc < buf->alloc)
    xalloc_die ();
      buf->alloc *= 2;
      buf->buf = xrealloc (buf->buf, buf->alloc);
    }
}

/* Compare strings A and B containing decimal fractions < 1.  Each string
   should begin with a decimal point followed immediately by the digits
   of the fraction.  Strings not of this form are considered to be zero. */

/* The goal here, is to take two numbers a and b... compare these
   in parallel.  Instead of converting each, and then comparing the
   outcome.  Most likely stopping the comparison before the conversion
   is complete.  The algorithm used, in the old sort:

   Algorithm: fraccompare
   Action   : compare two decimal fractions
   accepts  : char *a, char *b
   returns  : -1 if a<b, 0 if a=b, 1 if a>b.
   implement:

   if *a == decimal_point AND *b == decimal_point
     find first character different in a and b.
     if both are digits, return the difference *a - *b.
     if *a is a digit
       skip past zeros
       if digit return 1, else 0
     if *b is a digit
       skip past zeros
       if digit return -1, else 0
   if *a is a decimal_point
     skip past decimal_point and zeros
     if digit return 1, else 0
   if *b is a decimal_point
     skip past decimal_point and zeros
     if digit return -1, else 0
   return 0 */

static int
fraccompare (register const char *a, register const char *b)
{
  if (*a == decimal_point && *b == decimal_point)
    {
      while (*++a == *++b)
    if (! ISDIGIT (*a))
      return 0;
      if (ISDIGIT (*a) && ISDIGIT (*b))
    return *a - *b;
      if (ISDIGIT (*a))
    goto a_trailing_nonzero;
      if (ISDIGIT (*b))
    goto b_trailing_nonzero;
      return 0;
    }
  else if (*a++ == decimal_point)
    {
    a_trailing_nonzero:
      while (*a == NUMERIC_ZERO)
    a++;
      return ISDIGIT (*a);
    }
  else if (*b++ == decimal_point)
    {
    b_trailing_nonzero:
      while (*b == NUMERIC_ZERO)
    b++;
      return - ISDIGIT (*b);
    }
  return 0;
}

/* Compare strings A and B as numbers without explicitly converting them to
   machine numbers.  Comparatively slow for short strings, but asymptotically
   hideously fast. */

static int
numcompare (register const char *a, register const char *b)
{
  register int tmpa, tmpb, tmp;
  register size_t loga, logb;

  tmpa = *a;
  tmpb = *b;

  while (blanks[UCHAR (tmpa)])
    tmpa = *++a;
  while (blanks[UCHAR (tmpb)])
    tmpb = *++b;

  if (tmpa == NEGATION_SIGN)
    {
      do
    tmpa = *++a;
      while (tmpa == NUMERIC_ZERO || IS_THOUSANDS_SEP (tmpa));
      if (tmpb != NEGATION_SIGN)
    {
      if (tmpa == decimal_point)
        do
          tmpa = *++a;
        while (tmpa == NUMERIC_ZERO);
      if (ISDIGIT (tmpa))
        return -1;
      while (tmpb == NUMERIC_ZERO || IS_THOUSANDS_SEP (tmpb))
        tmpb = *++b;
      if (tmpb == decimal_point)
        do
          tmpb = *++b;
        while (tmpb == NUMERIC_ZERO);
      if (ISDIGIT (tmpb))
        return -1;
      return 0;
    }
      do
    tmpb = *++b;
      while (tmpb == NUMERIC_ZERO || IS_THOUSANDS_SEP (tmpb));

      while (tmpa == tmpb && ISDIGIT (tmpa))
    {
      do
        tmpa = *++a;
      while (IS_THOUSANDS_SEP (tmpa));
      do
        tmpb = *++b;
      while (IS_THOUSANDS_SEP (tmpb));
    }

      if ((tmpa == decimal_point && !ISDIGIT (tmpb))
      || (tmpb == decimal_point && !ISDIGIT (tmpa)))
    return -fraccompare (a, b);

      tmp = tmpb - tmpa;

      for (loga = 0; ISDIGIT (tmpa); ++loga)
    do
      tmpa = *++a;
    while (IS_THOUSANDS_SEP (tmpa));

      for (logb = 0; ISDIGIT (tmpb); ++logb)
    do
      tmpb = *++b;
    while (IS_THOUSANDS_SEP (tmpb));

      if (loga != logb)
    return loga < logb ? 1 : -1;

      if (!loga)
    return 0;

      return tmp;
    }
  else if (tmpb == NEGATION_SIGN)
    {
      do
    tmpb = *++b;
      while (tmpb == NUMERIC_ZERO || IS_THOUSANDS_SEP (tmpb));
      if (tmpb == decimal_point)
    do
      tmpb = *++b;
    while (tmpb == NUMERIC_ZERO);
      if (ISDIGIT (tmpb))
    return 1;
      while (tmpa == NUMERIC_ZERO || IS_THOUSANDS_SEP (tmpa))
    tmpa = *++a;
      if (tmpa == decimal_point)
    do
      tmpa = *++a;
    while (tmpa == NUMERIC_ZERO);
      if (ISDIGIT (tmpa))
    return 1;
      return 0;
    }
  else
    {
      while (tmpa == NUMERIC_ZERO || IS_THOUSANDS_SEP (tmpa))
    tmpa = *++a;
      while (tmpb == NUMERIC_ZERO || IS_THOUSANDS_SEP (tmpb))
    tmpb = *++b;

      while (tmpa == tmpb && ISDIGIT (tmpa))
    {
      do
        tmpa = *++a;
      while (IS_THOUSANDS_SEP (tmpa));
      do
        tmpb = *++b;
      while (IS_THOUSANDS_SEP (tmpb));
    }

      if ((tmpa == decimal_point && !ISDIGIT (tmpb))
      || (tmpb == decimal_point && !ISDIGIT (tmpa)))
    return fraccompare (a, b);

      tmp = tmpa - tmpb;

      for (loga = 0; ISDIGIT (tmpa); ++loga)
    do
      tmpa = *++a;
    while (IS_THOUSANDS_SEP (tmpa));

      for (logb = 0; ISDIGIT (tmpb); ++logb)
    do
      tmpb = *++b;
    while (IS_THOUSANDS_SEP (tmpb));

      if (loga != logb)
    return loga < logb ? -1 : 1;

      if (!loga)
    return 0;

      return tmp;
    }
}

static int
general_numcompare (const char *sa, const char *sb)
{
  /* FIXME: add option to warn about failed conversions.  */
  /* FIXME: maybe add option to try expensive FP conversion
     only if A and B can't be compared more cheaply/accurately.  */

  char *ea;
  char *eb;
  double a = strtod (sa, &ea);
  double b = strtod (sb, &eb);

  /* Put conversion errors at the start of the collating sequence.  */
  if (sa == ea)
    return sb == eb ? 0 : -1;
  if (sb == eb)
    return 1;

  /* Sort numbers in the usual way, where -0 == +0.  Put NaNs after
     conversion errors but before numbers; sort them by internal
     bit-pattern, for lack of a more portable alternative.  */
  return (a < b ? -1
      : a > b ? 1
      : a == b ? 0
      : b == b ? -1
      : a == a ? 1
      : memcmp ((char *) &a, (char *) &b, sizeof a));
}

/* Return an integer in 1..12 of the month name S with length LEN.
   Return 0 if the name in S is not recognized.  */

static int
getmonth (const char *s, size_t len)
{
  char *month;
  register size_t i;
  register int lo = 0, hi = MONTHS_PER_YEAR, result;

  while (len > 0 && blanks[UCHAR (*s)])
    {
      ++s;
      --len;
    }

  if (len == 0)
    return 0;

  month = (char *) alloca (len + 1);
  for (i = 0; i < len; ++i)
    month[i] = fold_toupper[UCHAR (s[i])];
  while (blanks[UCHAR (month[i - 1])])
    --i;
  month[i] = '\0';

  do
    {
      int ix = (lo + hi) / 2;

      if (strncmp (month, monthtab[ix].name, strlen (monthtab[ix].name)) < 0)
    hi = ix;
      else
    lo = ix;
    }
  while (hi - lo > 1);

  result = (!strncmp (month, monthtab[lo].name, strlen (monthtab[lo].name))
        ? monthtab[lo].val : 0);

  return result;
}

/* Compare two lines A and B trying every key in sequence until there
   are no more keys or a difference is found. */

static int
keycompare (const struct line *a, const struct line *b)
{
  struct keyfield *key = keylist;

  /* For the first iteration only, the key positions have been
     precomputed for us. */
  register char *texta = a->keybeg;
  register char *textb = b->keybeg;
  register char *lima = a->keylim;
  register char *limb = b->keylim;

  int diff;

  for (;;)
    {
      register unsigned char *translate = (unsigned char *) key->translate;
      register int *ignore = key->ignore;

      /* Find the lengths. */
      size_t lena = lima <= texta ? 0 : lima - texta;
      size_t lenb = limb <= textb ? 0 : limb - textb;

      if (key->skipeblanks)
    {
      char *a_end = texta + lena;
      char *b_end = textb + lenb;
      trim_trailing_blanks (texta, &a_end);
      trim_trailing_blanks (textb, &b_end);
      lena = a_end - texta;
      lenb = b_end - textb;
    }

      /* Actually compare the fields. */
      if (key->numeric | key->general_numeric)
    {
      char savea = *lima, saveb = *limb;

      *lima = *limb = '\0';
      diff = ((key->numeric ? numcompare : general_numcompare)
          (texta, textb));
      *lima = savea, *limb = saveb;
    }
      else if (key->month)
    diff = getmonth (texta, lena) - getmonth (textb, lenb);
      /* Sorting like this may become slow, so in a simple locale the user
         can select a faster sort that is similar to ascii sort  */
      else if (HAVE_SETLOCALE && hard_LC_COLLATE)
    {
      if (ignore || translate)
        {
          char *copy_a = (char *) alloca (lena + 1 + lenb + 1);
          char *copy_b = copy_a + lena + 1;
          size_t new_len_a, new_len_b, i;

          /* Ignore and/or translate chars before comparing.  */
          for (new_len_a = new_len_b = i = 0; i < max (lena, lenb); i++)
        {
          if (i < lena)
            {
              copy_a[new_len_a] = (translate
                       ? translate[UCHAR (texta[i])]
                       : texta[i]);
              if (!ignore || !ignore[UCHAR (texta[i])])
            ++new_len_a;
            }
          if (i < lenb)
            {
              copy_b[new_len_b] = (translate
                       ? translate[UCHAR (textb[i])]
                       : textb [i]);
              if (!ignore || !ignore[UCHAR (textb[i])])
            ++new_len_b;
            }
        }

          diff = xmemcoll (copy_a, new_len_a, copy_b, new_len_b);
        }
      else if (lena == 0)
        diff = - NONZERO (lenb);
      else if (lenb == 0)
        goto greater;
      else
        diff = xmemcoll (texta, lena, textb, lenb);
    }
      else if (ignore)
    {
#define CMP_WITH_IGNORE(A, B)                       \
  do                                    \
    {                                   \
      for (;;)                          \
        {                               \
          while (texta < lima && ignore[UCHAR (*texta)])        \
        ++texta;                        \
          while (textb < limb && ignore[UCHAR (*textb)])        \
        ++textb;                        \
          if (! (texta < lima && textb < limb))         \
        break;                          \
          diff = UCHAR (A) - UCHAR (B);             \
          if (diff)                         \
        goto not_equal;                     \
          ++texta;                          \
          ++textb;                          \
        }                               \
                                    \
      diff = (texta < lima) - (textb < limb);           \
    }                                   \
  while (0)

      if (translate)
        CMP_WITH_IGNORE (translate[UCHAR (*texta)],
                 translate[UCHAR (*textb)]);
      else
        CMP_WITH_IGNORE (UCHAR (*texta), UCHAR (*textb));
    }
      else if (lena == 0)
    diff = - NONZERO (lenb);
      else if (lenb == 0)
    goto greater;
      else
    {
      if (translate)
        {
          while (texta < lima && textb < limb)
        {
          diff = (UCHAR (translate[UCHAR (*texta++)])
              - UCHAR (translate[UCHAR (*textb++)]));
          if (diff)
            goto not_equal;
        }
        }
      else
        {
          diff = memcmp (texta, textb, min (lena, lenb));
          if (diff)
        goto not_equal;
        }
      diff = lena < lenb ? -1 : lena != lenb;
    }

      if (diff)
    goto not_equal;

      key = key->next;
      if (! key)
    break;

      /* Find the beginning and limit of the next field.  */
      if (key->eword != (size_t) -1)
    lima = limfield (a, key), limb = limfield (b, key);
      else
    lima = a->text + a->length - 1, limb = b->text + b->length - 1;

      if (key->sword != (size_t) -1)
    texta = begfield (a, key), textb = begfield (b, key);
      else
    {
      texta = a->text, textb = b->text;
      if (key->skipsblanks)
        {
          while (texta < lima && blanks[UCHAR (*texta)])
        ++texta;
          while (textb < limb && blanks[UCHAR (*textb)])
        ++textb;
        }
    }
    }

  return 0;

 greater:
  diff = 1;
 not_equal:
  return key->reverse ? -diff : diff;
}

/* Compare two lines A and B, returning negative, zero, or positive
   depending on whether A compares less than, equal to, or greater than B. */

static int
compare (register const struct line *a, register const struct line *b)
{
  int diff;
  size_t alen, blen;

  /* First try to compare on the specified keys (if any).
     The only two cases with no key at all are unadorned sort,
     and unadorned sort -r. */
  if (keylist)
    {
      diff = keycompare (a, b);
      alloca (0);
      if (diff != 0 || unique || stable)
    return diff;
    }

  /* If the keys all compare equal (or no keys were specified)
     fall through to the default comparison.  */
  alen = a->length - 1, blen = b->length - 1;

  if (alen == 0)
    diff = - NONZERO (blen);
  else if (blen == 0)
    diff = NONZERO (alen);
  else if (HAVE_SETLOCALE && hard_LC_COLLATE)
    diff = xmemcoll (a->text, alen, b->text, blen);
  else if (! (diff = memcmp (a->text, b->text, min (alen, blen))))
    diff = alen < blen ? -1 : alen != blen;

  return reverse ? -diff : diff;
}

/* Check that the lines read from the given FP come in order.  Print a
   diagnostic (FILE_NAME, line number, contents of line) to stderr and return
   one if they are not in order.  Otherwise, print no diagnostic
   and return zero.  */

static int
checkfp (FILE *fp, char *file_name)
{
  struct buffer buf;        /* Input buffer. */
  struct line temp;     /* Copy of previous line. */
  size_t alloc = 0;
  uintmax_t line_number = 0;
  struct keyfield *key = keylist;
  int nonunique = 1 - unique;
  int disordered = 0;

  initbuf (&buf, sizeof (struct line),
       MAX (merge_buffer_size, sort_size));
  temp.text = NULL;

  while (fillbuf (&buf, fp, file_name))
    {
      struct line const *line = buffer_linelim (&buf);
      struct line const *linebase = line - buf.nlines;

      /* Make sure the line saved from the old buffer contents is
     less than or equal to the first line of the new buffer. */
      if (alloc && nonunique <= compare (&temp, line - 1))
    {
    found_disorder:
      {
        struct line const *disorder_line = line - 1;
        uintmax_t disorder_line_number =
          buffer_linelim (&buf) - disorder_line + line_number;
        char hr_buf[INT_BUFSIZE_BOUND (uintmax_t)];
        fprintf (stderr, _("%s: %s:%s: disorder: "),
             program_name, file_name,
             umaxtostr (disorder_line_number, hr_buf));
        write_bytes (disorder_line->text, disorder_line->length, stderr,
             _("standard error"));
        disordered = 1;
        break;
      }
    }

      /* Compare each line in the buffer with its successor.  */
      while (linebase < --line)
    if (nonunique <= compare (line, line - 1))
      goto found_disorder;

      line_number += buf.nlines;

      /* Save the last line of the buffer.  */
      if (alloc < line->length)
    {
      do
        {
          alloc *= 2;
          if (! alloc)
        {
          alloc = line->length;
          break;
        }
        }
      while (alloc < line->length);

      temp.text = xrealloc (temp.text, alloc);
    }
      memcpy (temp.text, line->text, line->length);
      temp.length = line->length;
      if (key)
    {
      temp.keybeg = temp.text + (line->keybeg - line->text);
      temp.keylim = temp.text + (line->keylim - line->text);
    }
    }

  xfclose (fp, file_name);
  free (buf.buf);
  if (temp.text)
    free (temp.text);
  return disordered;
}

/* Merge lines from FILES onto OFP.  NFILES cannot be greater than
   NMERGE.  Close input and output files before returning.
   OUTPUT_FILE gives the name of the output file; if OFP is NULL, the
   output file has not been opened yet.  */

static void
mergefps (char **files, register int nfiles,
      FILE *ofp, const char *output_file)
{
  FILE *fps[NMERGE];        /* Input streams for each file.  */
  struct buffer buffer[NMERGE]; /* Input buffers for each file. */
  struct line saved;        /* Saved line storage for unique check. */
  struct line const *savedline = NULL;
                /* &saved if there is a saved line. */
  size_t savealloc = 0;     /* Size allocated for the saved line. */
  struct line const *cur[NMERGE]; /* Current line in each line table. */
  struct line const *base[NMERGE]; /* Base of each line table.  */
  int ord[NMERGE];      /* Table representing a permutation of fps,
                   such that cur[ord[0]] is the smallest line
                   and will be next output. */
  register int i, j, t;
  struct keyfield *key = keylist;
  saved.text = NULL;

  /* Read initial lines from each input file. */
  for (i = 0; i < nfiles; )
    {
      fps[i] = xfopen (files[i], "r");
      initbuf (&buffer[i], sizeof (struct line),
           MAX (merge_buffer_size, sort_size / nfiles));
      if (fillbuf (&buffer[i], fps[i], files[i]))
    {
      struct line const *linelim = buffer_linelim (&buffer[i]);
      cur[i] = linelim - 1;
      base[i] = linelim - buffer[i].nlines;
      i++;
    }
      else
    {
      /* fps[i] is empty; eliminate it from future consideration.  */
      xfclose (fps[i], files[i]);
      zaptemp (files[i]);
      free (buffer[i].buf);
      --nfiles;
      for (j = i; j < nfiles; ++j)
        files[j] = files[j + 1];
    }
    }

  if (! ofp)
    ofp = xfopen (output_file, "w");

  /* Set up the ord table according to comparisons among input lines.
     Since this only reorders two items if one is strictly greater than
     the other, it is stable. */
  for (i = 0; i < nfiles; ++i)
    ord[i] = i;
  for (i = 1; i < nfiles; ++i)
    if (0 < compare (cur[ord[i - 1]], cur[ord[i]]))
      t = ord[i - 1], ord[i - 1] = ord[i], ord[i] = t, i = 0;

  /* Repeatedly output the smallest line until no input remains. */
  while (nfiles)
    {
      struct line const *smallest = cur[ord[0]];

      /* If uniquified output is turned on, output only the first of
     an identical series of lines. */
      if (unique)
    {
      if (savedline && compare (savedline, smallest))
        {
          savedline = 0;
          write_bytes (saved.text, saved.length, ofp, output_file);
        }
      if (!savedline)
        {
          savedline = &saved;
          if (savealloc < smallest->length)
        {
          do
            if (! savealloc)
              {
            savealloc = smallest->length;
            break;
              }
          while ((savealloc *= 2) < smallest->length);

          saved.text = xrealloc (saved.text, savealloc);
        }
          saved.length = smallest->length;
          memcpy (saved.text, smallest->text, saved.length);
          if (key)
        {
          saved.keybeg =
            saved.text + (smallest->keybeg - smallest->text);
          saved.keylim =
            saved.text + (smallest->keylim - smallest->text);
        }
        }
    }
      else
    write_bytes (smallest->text, smallest->length, ofp, output_file);

      /* Check if we need to read more lines into core. */
      if (base[ord[0]] < smallest)
    cur[ord[0]] = smallest - 1;
      else
    {
      if (fillbuf (&buffer[ord[0]], fps[ord[0]], files[ord[0]]))
        {
          struct line const *linelim = buffer_linelim (&buffer[ord[0]]);
          cur[ord[0]] = linelim - 1;
          base[ord[0]] = linelim - buffer[ord[0]].nlines;
        }
      else
        {
          /* We reached EOF on fps[ord[0]]. */
          for (i = 1; i < nfiles; ++i)
        if (ord[i] > ord[0])
          --ord[i];
          --nfiles;
          xfclose (fps[ord[0]], files[ord[0]]);
          zaptemp (files[ord[0]]);
          free (buffer[ord[0]].buf);
          for (i = ord[0]; i < nfiles; ++i)
        {
          fps[i] = fps[i + 1];
          files[i] = files[i + 1];
          buffer[i] = buffer[i + 1];
          cur[i] = cur[i + 1];
          base[i] = base[i + 1];
        }
          for (i = 0; i < nfiles; ++i)
        ord[i] = ord[i + 1];
          continue;
        }
    }

      /* The new line just read in may be larger than other lines
     already in core; push it back in the queue until we encounter
     a line larger than it. */
      for (i = 1; i < nfiles; ++i)
    {
      t = compare (cur[ord[0]], cur[ord[i]]);
      if (!t)
        t = ord[0] - ord[i];
      if (t < 0)
        break;
    }
      t = ord[0];
      for (j = 1; j < i; ++j)
    ord[j - 1] = ord[j];
      ord[i - 1] = t;
    }

  if (unique && savedline)
    {
      write_bytes (saved.text, saved.length, ofp, output_file);
      free (saved.text);
    }

  xfclose (ofp, output_file);
}

/* Sort the array LINES with NLINES members, using TEMP for temporary space.
   The input and output arrays are in reverse order, and LINES and
   TEMP point just past the end of their respective arrays.  */

static void
sortlines (struct line *lines, size_t nlines, struct line *temp)
{
  register struct line *lo, *hi, *t;
  register size_t nlo, nhi;

  if (nlines == 2)
    {
      if (0 < compare (&lines[-1], &lines[-2]))
    {
      struct line tmp = lines[-1];
      lines[-1] = lines[-2];
      lines[-2] = tmp;
    }
      return;
    }

  nlo = nlines / 2;
  lo = lines;
  nhi = nlines - nlo;
  hi = lines - nlo;

  if (nlo > 1)
    sortlines (lo, nlo, temp);

  if (nhi > 1)
    sortlines (hi, nhi, temp);

  t = temp;

  while (nlo && nhi)
    if (compare (lo - 1, hi - 1) <= 0)
      *--t = *--lo, --nlo;
    else
      *--t = *--hi, --nhi;
  while (nlo--)
    *--t = *--lo;

  for (lo = lines, nlo = nlines - nhi, t = temp; nlo; --nlo)
    *--lo = *--t;
}

/* Return the index of the first of NFILES FILES that is the same file
   as OUTFILE.  If none can be the same, return NFILES.  Consider an
   input pipe to be the same as OUTFILE, since the pipe might be the
   output of a command like "cat OUTFILE".  */

static int
first_same_file (char **files, int nfiles, char const *outfile)
{
  int i;
  int got_outstat = 0;
  struct stat instat, outstat;

  for (i = 0; i < nfiles; i++)
    {
      int standard_input = STREQ (files[i], "-");

      if (STREQ (outfile, files[i]) && ! standard_input)
    return i;

      if (! got_outstat)
    {
      got_outstat = 1;
      if ((STREQ (outfile, "-")
           ? fstat (STDOUT_FILENO, &outstat)
           : stat (outfile, &outstat))
          != 0)
        return nfiles;
    }

      if (((standard_input
        ? fstat (STDIN_FILENO, &instat)
        : stat (files[i], &instat))
       == 0)
      && (S_ISFIFO (instat.st_mode) || SAME_INODE (instat, outstat)))
    return i;
    }

  return nfiles;
}

/* Check that each of the NFILES FILES is ordered.
   Return a count of disordered files. */

static int
check (char **files, int nfiles)
{
  int i, disorders = 0;
  FILE *fp;

  for (i = 0; i < nfiles; ++i)
    {
      fp = xfopen (files[i], "r");
      disorders += checkfp (fp, files[i]);
    }
  return disorders;
}

/* Merge NFILES FILES onto OUTPUT_FILE.  However, merge at most
   MAX_MERGE input files directly onto OUTPUT_FILE.  MAX_MERGE cannot
   exceed NMERGE.  */

static void
merge (char **files, int nfiles, int max_merge, char const *output_file)
{
  while (max_merge < nfiles)
    {
      FILE *tfp;
      int i, t = 0;
      char *temp;
      for (i = 0; i < nfiles / NMERGE; ++i)
    {
      temp = create_temp_file (&tfp);
      mergefps (&files[i * NMERGE], NMERGE, tfp, temp);
      files[t++] = temp;
    }
      temp = create_temp_file (&tfp);
      mergefps (&files[i * NMERGE], nfiles % NMERGE, tfp, temp);
      files[t++] = temp;
      nfiles = t;
      if (nfiles == 1)
    break;
    }

  mergefps (files, nfiles, NULL, output_file);
}

/* Sort NFILES FILES onto OUTPUT_FILE. */

static void
sort (char **files, int nfiles, char const *output_file)
{
  struct buffer buf;
  int n_temp_files = 0;
  int output_file_created = 0;

  static size_t size;
  if (! size && ! (size = sort_size))
    size = default_sort_size ();

  buf.alloc = 0;

  while (nfiles)
    {
      char const *temp_output;
      char const *file = *files;
      FILE *fp = xfopen (file, "r");
      FILE *tfp;

      if (! buf.alloc)
    initbuf (&buf, 2 * sizeof (struct line),
         sort_buffer_size (&fp, 1, files, nfiles,
                   2 * sizeof (struct line), size));
      buf.eof = 0;
      files++;
      nfiles--;

      while (fillbuf (&buf, fp, file))
    {
      struct line *line;
      struct line *linebase;

      if (buf.eof && nfiles
          && (2 * sizeof (struct line) + 1
          < (buf.alloc - buf.used
             - 2 * sizeof (struct line) * buf.nlines)))
        {
          /* End of file, but there is more input and buffer room.
         Concatenate the next input file; this is faster in
         the usual case.  */
          buf.left = buf.used;
          break;
        }

      line = buffer_linelim (&buf);
      linebase = line - buf.nlines;
      sortlines (line, buf.nlines, linebase);
      if (buf.eof && !nfiles && !n_temp_files && !buf.left)
        {
          xfclose (fp, file);
          tfp = xfopen (output_file, "w");
          temp_output = output_file;
          output_file_created = 1;
        }
      else
        {
          ++n_temp_files;
          temp_output = create_temp_file (&tfp);
        }

      do
        {
          line--;
          write_bytes (line->text, line->length, tfp, temp_output);
          if (unique)
        while (linebase < line && compare (line, line - 1) == 0)
          line--;
        }
      while (linebase < line);

      xfclose (tfp, temp_output);

      if (output_file_created)
        goto finish;
    }
      xfclose (fp, file);
    }

 finish:
  free (buf.buf);

  if (! output_file_created)
    {
      int i = n_temp_files;
      struct tempnode *node;
      char **tempfiles = (char **) xmalloc (n_temp_files * sizeof (char *));
      for (node = temphead; i > 0; node = node->next)
    tempfiles[--i] = node->name;
      merge (tempfiles, n_temp_files, NMERGE, output_file);
      free ((char *) tempfiles);
    }
}

/* Insert key KEY at the end of the key list.  */

static void
insertkey (struct keyfield *key)
{
  struct keyfield **p;

  for (p = &keylist; *p; p = &(*p)->next)
    continue;
  *p = key;
  key->next = NULL;
}

/* Report a bad field specification SPEC, with extra info MSGID.  */

static void badfieldspec (char const *, char const *)
     ATTRIBUTE_NORETURN;
static void
badfieldspec (char const *spec, char const *msgid)
{
  error (SORT_FAILURE, 0, _("%s: invalid field specification `%s'"),
     _(msgid), spec);
  abort ();
}

/* Parse the leading integer in STRING and store the resulting value
   (which must fit into size_t) into *VAL.  Return the address of the
   suffix after the integer.  If MSGID is NULL, return NULL after
   failure; otherwise, report MSGID and exit on failure.  */

static char const *
parse_field_count (char const *string, size_t *val, char const *msgid)
{
  char *suffix;
  uintmax_t n;

  switch (xstrtoumax (string, &suffix, 10, &n, ""))
    {
    case LONGINT_OK:
    case LONGINT_INVALID_SUFFIX_CHAR:
      *val = n;
      if (*val == n)
    break;
      /* Fall through.  */
    case LONGINT_OVERFLOW:
      if (msgid)
    error (SORT_FAILURE, 0, _("%s: count `%.*s' too large"),
           _(msgid), (int) (suffix - string), string);
      return NULL;

    case LONGINT_INVALID:
      if (msgid)
    error (SORT_FAILURE, 0, _("%s: invalid count at start of `%s'"),
           _(msgid), string);
      return NULL;
    }

  return suffix;
}

/* Handle interrupts and hangups. */

static void
sighandler (int sig)
{
#ifndef SA_NOCLDSTOP
  signal (sig, SIG_IGN);
#endif

  cleanup ();

#ifdef SA_NOCLDSTOP
  {
    struct sigaction sigact;

    sigact.sa_handler = SIG_DFL;
    sigemptyset (&sigact.sa_mask);
    sigact.sa_flags = 0;
    sigaction (sig, &sigact, NULL);
  }
#else
  signal (sig, SIG_DFL);
#endif

  raise (sig);
}

/* Set the ordering options for KEY specified in S.
   Return the address of the first character in S that
   is not a valid ordering option.
   BLANKTYPE is the kind of blanks that 'b' should skip. */

static char *
set_ordering (register const char *s, struct keyfield *key,
          enum blanktype blanktype)
{
  while (*s)
    {
      switch (*s)
    {
    case 'b':
      if (blanktype == bl_start || blanktype == bl_both)
        key->skipsblanks = 1;
      if (blanktype == bl_end || blanktype == bl_both)
        key->skipeblanks = 1;
      break;
    case 'd':
      key->ignore = nondictionary;
      break;
    case 'f':
      key->translate = fold_toupper;
      break;
    case 'g':
      key->general_numeric = 1;
      break;
    case 'i':
      key->ignore = nonprinting;
      break;
    case 'M':
      key->month = 1;
      break;
    case 'n':
      key->numeric = 1;
      break;
    case 'r':
      key->reverse = 1;
      break;
    default:
      return (char *) s;
    }
      ++s;
    }
  return (char *) s;
}

static struct keyfield *
new_key (void)
{
  struct keyfield *key = (struct keyfield *) xcalloc (1, sizeof *key);
  key->eword = -1;
  return key;
}

int
main (int argc, char **argv)
{
  struct keyfield *key;
  struct keyfield gkey;
  char const *s;
  int c = 0;
  int checkonly = 0, mergeonly = 0, nfiles = 0;
  int posix_pedantic = (getenv ("POSIXLY_CORRECT") != NULL);
  bool obsolete_usage = (posix2_version () < 200112);
  char const *short_options = (obsolete_usage
                   ? COMMON_SHORT_OPTIONS "y::"
                   : COMMON_SHORT_OPTIONS "y:");
  char *minus = "-", **files;
  char const *outfile = minus;
  static int const sigs[] = { SIGHUP, SIGINT, SIGPIPE, SIGTERM };
  unsigned nsigs = sizeof sigs / sizeof *sigs;
#ifdef SA_NOCLDSTOP
  struct sigaction oldact, newact;
#endif

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (cleanup);

  hard_LC_COLLATE = hard_locale (LC_COLLATE);
#if HAVE_NL_LANGINFO
  hard_LC_TIME = hard_locale (LC_TIME);
#endif

#if HAVE_SETLOCALE
  /* Let's get locale's representation of the decimal point */
  {
    struct lconv *lconvp = localeconv ();

    /* If the locale doesn't define a decimal point, or if the decimal
       point is multibyte, use the C decimal point.  We don't support
       multibyte decimal points yet.  */
    decimal_point = *lconvp->decimal_point;
    if (! decimal_point || lconvp->decimal_point[1])
      decimal_point = C_DECIMAL_POINT;

    /* We don't support multibyte thousands separators yet.  */
    th_sep = *lconvp->thousands_sep;
    if (! th_sep || lconvp->thousands_sep[1])
      th_sep = CHAR_MAX + 1;
  }
#endif

  have_read_stdin = 0;
  inittables ();

  /* Change the way library functions fail.  */
  xalloc_exit_failure = SORT_FAILURE;
  xmemcoll_exit_failure = SORT_FAILURE;

#ifdef SA_NOCLDSTOP
  {
    unsigned i;
    sigemptyset (&caught_signals);
    for (i = 0; i < nsigs; i++)
      sigaddset (&caught_signals, sigs[i]);
    newact.sa_handler = sighandler;
    newact.sa_mask = caught_signals;
    newact.sa_flags = 0;
  }
#endif

  {
    unsigned i;
    for (i = 0; i < nsigs; i++)
      {
    int sig = sigs[i];
#ifdef SA_NOCLDSTOP
    sigaction (sig, NULL, &oldact);
    if (oldact.sa_handler != SIG_IGN)
      sigaction (sig, &newact, NULL);
#else
    if (signal (sig, SIG_IGN) != SIG_IGN)
      signal (sig, sighandler);
#endif
      }
  }

  gkey.sword = gkey.eword = -1;
  gkey.ignore = NULL;
  gkey.translate = NULL;
  gkey.numeric = gkey.general_numeric = gkey.month = gkey.reverse = 0;
  gkey.skipsblanks = gkey.skipeblanks = 0;

  files = (char **) xmalloc (sizeof (char *) * argc);

  for (;;)
    {
      /* Parse an operand as a file after "--" was seen; or if
         pedantic and a file was seen, unless the POSIX version
         predates 1003.1-2001 and -c was not seen and the operand is
         "-o FILE" or "-oFILE".  */

      if (c == -1
      || (posix_pedantic && nfiles != 0
          && ! (obsolete_usage
            && ! checkonly
            && optind != argc
            && argv[optind][0] == '-' && argv[optind][1] == 'o'
            && (argv[optind][2] || optind + 1 != argc)))
      || ((c = getopt_long (argc, argv, short_options,
                long_options, NULL))
          == -1))
    {
      if (optind == argc)
        break;
      files[nfiles++] = argv[optind++];
    }
      else switch (c)
    {
    case 1:
      key = NULL;
      if (obsolete_usage && optarg[0] == '+')
        {
          /* Treat +POS1 [-POS2] as a key if possible; but silently
         treat an operand as a file if it is not a valid +POS1.  */
          key = new_key ();
          s = parse_field_count (optarg + 1, &key->sword, NULL);
          if (s && *s == '.')
        s = parse_field_count (s + 1, &key->schar, NULL);
          if (! (key->sword | key->schar))
        key->sword = -1;
          if (! s || *set_ordering (s, key, bl_start))
        {
          free (key);
          key = NULL;
        }
          else
        {
          if (optind != argc && argv[optind][0] == '-'
              && ISDIGIT (argv[optind][1]))
            {
              char const *optarg1 = argv[optind++];
              s = parse_field_count (optarg1 + 1, &key->eword,
                         N_("invalid number after `-'"));
              if (*s == '.')
            s = parse_field_count (s + 1, &key->echar,
                           N_("invalid number after `.'"));
              if (*set_ordering (s, key, bl_end))
            badfieldspec (optarg1,
                      N_("stray character in field spec"));
            }
          insertkey (key);
        }
        }
      if (! key)
        files[nfiles++] = optarg;
      break;

    case 'b':
    case 'd':
    case 'f':
    case 'g':
    case 'i':
    case 'M':
    case 'n':
    case 'r':
      {
        char str[2];
        str[0] = c;
        str[1] = '\0';
        set_ordering (str, &gkey, bl_both);
      }
      break;

    case 'c':
      checkonly = 1;
      break;

    case 'k':
      key = new_key ();

      /* Get POS1. */
      s = parse_field_count (optarg, &key->sword,
                 N_("invalid number at field start"));
      if (! key->sword--)
        {
          /* Provoke with `sort -k0' */
          badfieldspec (optarg, N_("field number is zero"));
        }
      if (*s == '.')
        {
          s = parse_field_count (s + 1, &key->schar,
                     N_("invalid number after `.'"));
          if (! key->schar--)
        {
          /* Provoke with `sort -k1.0' */
          badfieldspec (optarg, N_("character offset is zero"));
        }
        }
      if (! (key->sword | key->schar))
        key->sword = -1;
      s = set_ordering (s, key, bl_start);
      if (*s != ',')
        {
          key->eword = -1;
          key->echar = 0;
        }
      else
        {
          /* Get POS2. */
          s = parse_field_count (s + 1, &key->eword,
                     N_("invalid number after `,'"));
          if (! key->eword--)
        {
          /* Provoke with `sort -k1,0' */
          badfieldspec (optarg, N_("field number is zero"));
        }
          if (*s == '.')
        s = parse_field_count (s + 1, &key->echar,
                       N_("invalid number after `.'"));
          else
        {
          /* `-k 2,3' is equivalent to `+1 -3'.  */
          key->eword++;
        }
          s = set_ordering (s, key, bl_end);
        }
      if (*s)
        badfieldspec (optarg, N_("stray character in field spec"));
      insertkey (key);
      break;

    case 'm':
      mergeonly = 1;
      break;

    case 'o':
      outfile = optarg;
      break;

    case 's':
      stable = 1;
      break;

    case 'S':
      specify_sort_size (optarg);
      break;

    case 't':
      tab = optarg[0];
      if (tab && optarg[1])
        {
          /* Provoke with `sort -txx'.  Complain about
         "multi-character tab" instead of "multibyte tab", so
         that the diagnostic's wording does not need to be
         changed once multibyte characters are supported.  */
          error (SORT_FAILURE, 0, _("multi-character tab `%s'"), optarg);
        }
      break;

    case 'T':
      add_temp_dir (optarg);
      break;

    case 'u':
      unique = 1;
      break;

    case 'y':
      /* Accept and ignore e.g. -y0 for compatibility with Solaris
         2.x through Solaris 7.  -y is marked as obsolete starting
         with Solaris 8.  */
      break;

    case 'z':
      eolchar = 0;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (SORT_FAILURE);
    }
    }

  /* Inheritance of global options to individual keys. */
  for (key = keylist; key; key = key->next)
    if (!key->ignore && !key->translate && !key->skipsblanks && !key->reverse
    && !key->skipeblanks && !key->month && !key->numeric
    && !key->general_numeric)
      {
    key->ignore = gkey.ignore;
    key->translate = gkey.translate;
    key->skipsblanks = gkey.skipsblanks;
    key->skipeblanks = gkey.skipeblanks;
    key->month = gkey.month;
    key->numeric = gkey.numeric;
    key->general_numeric = gkey.general_numeric;
    key->reverse = gkey.reverse;
      }

  if (!keylist && (gkey.ignore || gkey.translate || gkey.skipsblanks
           || gkey.skipeblanks || gkey.month || gkey.numeric
           || gkey.general_numeric))
    insertkey (&gkey);
  reverse = gkey.reverse;

  if (temp_dir_count == 0)
    {
      char const *tmp_dir = getenv ("TMPDIR");
      add_temp_dir (tmp_dir ? tmp_dir : DEFAULT_TMPDIR);
    }

  if (nfiles == 0)
    {
      nfiles = 1;
      files = &minus;
    }

  if (checkonly)
    {
      if (nfiles > 1)
    error (SORT_FAILURE, 0, _("extra operand `%s' not allowed with -c"),
           files[1]);

      /* POSIX requires that sort return 1 IFF invoked with -c and the
     input is not properly sorted.  */
      exit (check (files, nfiles) == 0 ? EXIT_SUCCESS : SORT_OUT_OF_ORDER);
    }

  if (mergeonly)
    {
      int max_merge = first_same_file (files, MIN (nfiles, NMERGE), outfile);
      merge (files, nfiles, max_merge, outfile);
    }
  else
    sort (files, nfiles, outfile);

  if (have_read_stdin && fclose (stdin) == EOF)
    die (_("close failed"), "-");

  exit (EXIT_SUCCESS);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/patches/uniq-fopen.patch

File: ../git/live-bootstrap/sysa/coreutils-5.0/patches/uniq-fopen.patch used in: Step 748
SPDX-FileCopyrightText: 2005 Paul Eggert <eggert@cs.ucla.edu>
SPDX-FileCopyrightText: 2023 Emily Trau <emily@downunderctf.com>

SPDX-License-Identifier: GPL-2.0-or-later

uniq: don't assume fopen cannot return stdin or stdout.
Backport of https://git.savannah.gnu.org/cgit/coreutils.git/commit/?id=786ebb2ceca72f69aa2de701671fb41f53cb1489

--- src/uniq.c
+++ src/uniq.c
@@ -30,6 +30,7 @@
 #include "error.h"
 #include "hard-locale.h"
 #include "posixver.h"
+#include "stdio-safer.h"
 #include "xmemcoll.h"
 #include "xstrtol.h"
 #include "memcasecmp.h"
@@ -267,20 +268,26 @@ check_file (const char *infile, const char *outfile)
   FILE *ostream;
   struct linebuffer lb1, lb2;
   struct linebuffer *thisline, *prevline;
+  bool is_stdin = STREQ (infile, "-");
+  bool is_stdout = STREQ (outfile, "-");
 
-  if (STREQ (infile, "-"))
+  if (is_stdin)
     istream = stdin;
   else
-    istream = fopen (infile, "r");
-  if (istream == NULL)
-    error (EXIT_FAILURE, errno, "%s", infile);
+    {
+      istream = fopen_safer (infile, "r");
+      if (! istream)
+        error (EXIT_FAILURE, errno, "%s", infile);
+    }
 
-  if (STREQ (outfile, "-"))
+  if (is_stdout)
     ostream = stdout;
   else
-    ostream = fopen (outfile, "w");
-  if (ostream == NULL)
-    error (EXIT_FAILURE, errno, "%s", outfile);
+    {
+      ostream = fopen_safer (outfile, "w");
+      if (! ostream)
+        error (EXIT_FAILURE, errno, "%s", outfile);
+    }
 
   thisline = &lb1;
   prevline = &lb2;
@@ -377,12 +384,12 @@ check_file (const char *infile, const char *outfile)
     }
 
  closefiles:
-  if (ferror (istream) || fclose (istream) == EOF)
+  if (!is_stdin && (ferror (istream) || fclose (istream) != 0))
     error (EXIT_FAILURE, errno, _("error reading %s"), infile);
 
   /* Close ostream only if it's not stdout -- the latter is closed
      via the atexit-invoked close_stdout.  */
-  if (ostream != stdout && (ferror (ostream) || fclose (ostream) == EOF))
+  if (!is_stdout && (ferror (ostream) || fclose (ostream) != 0))
     error (EXIT_FAILURE, errno, _("error writing %s"), outfile);
 
   free (lb1.buffer);

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/uniq.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/uniq.c used in: Step 748, Step 939
/* uniq -- remove duplicate lines from a sorted file
   Copyright (C) 86, 91, 1995-2002, 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.  */

/* Written by Richard Stallman and David MacKenzie. */


#include <config.h>

#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>

#include "system.h"
#include "closeout.h"
#include "argmatch.h"
#include "linebuffer.h"
#include "error.h"
#include "hard-locale.h"
#include "posixver.h"
#include "xmemcoll.h"
#include "xstrtol.h"
#include "memcasecmp.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "uniq"

#define AUTHORS N_ ("Richard Stallman and David MacKenzie")

#define SWAP_LINES(A, B)            \
  do                        \
    {                       \
      struct linebuffer *_tmp;          \
      _tmp = (A);               \
      (A) = (B);                \
      (B) = _tmp;               \
    }                       \
  while (0)

/* The name this program was run with. */
char *program_name;

/* Nonzero if the LC_COLLATE locale is hard.  */
static int hard_LC_COLLATE;

/* Number of fields to skip on each line when doing comparisons. */
static size_t skip_fields;

/* Number of chars to skip after skipping any fields. */
static size_t skip_chars;

/* Number of chars to compare. */
static size_t check_chars;

enum countmode
{
  count_occurrences,        /* -c Print count before output lines. */
  count_none            /* Default.  Do not print counts. */
};

/* Whether and how to precede the output lines with a count of the number of
   times they occurred in the input. */
static enum countmode countmode;

enum output_mode
{
  output_repeated,      /* -d Only lines that are repeated. */
  output_all_repeated,      /* -D All lines that are repeated. */
  output_unique,        /* -u Only lines that are not repeated. */
  output_all            /* Default.  Print first copy of each line. */
};

/* Which lines to output. */
static enum output_mode mode;

/* If nonzero, ignore case when comparing.  */
static int ignore_case;

enum delimit_method
{
  /* No delimiters output.  --all-repeated[=none] */
  DM_NONE,

  /* Delimiter precedes all groups.  --all-repeated=prepend */
  DM_PREPEND,

  /* Delimit all groups.  --all-repeated=separate */
  DM_SEPARATE
};

static char const *const delimit_method_string[] =
{
  "none", "prepend", "separate", 0
};

static enum delimit_method const delimit_method_map[] =
{
  DM_NONE, DM_PREPEND, DM_SEPARATE
};

/* Select whether/how to delimit groups of duplicate lines.  */
static enum delimit_method delimit_groups;

static struct option const longopts[] =
{
  {"count", no_argument, NULL, 'c'},
  {"repeated", no_argument, NULL, 'd'},
  {"all-repeated", optional_argument, NULL, 'D'},
  {"ignore-case", no_argument, NULL, 'i'},
  {"unique", no_argument, NULL, 'u'},
  {"skip-fields", required_argument, NULL, 'f'},
  {"skip-chars", required_argument, NULL, 's'},
  {"check-chars", required_argument, NULL, 'w'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [INPUT [OUTPUT]]\n\
"),
          program_name);
      fputs (_("\
Discard all but one of successive identical lines from INPUT (or\n\
standard input), writing to OUTPUT (or standard output).\n\
\n\
"), stdout);
     fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
     fputs (_("\
  -c, --count           prefix lines by the number of occurrences\n\
  -d, --repeated        only print duplicate lines\n\
"), stdout);
     fputs (_("\
  -D, --all-repeated[=delimit-method] print all duplicate lines\n\
                        delimit-method={none(default),prepend,separate}\n\
                        Delimiting is done with blank lines.\n\
  -f, --skip-fields=N   avoid comparing the first N fields\n\
  -i, --ignore-case     ignore differences in case when comparing\n\
  -s, --skip-chars=N    avoid comparing the first N characters\n\
  -u, --unique          only print unique lines\n\
"), stdout);
     fputs (_("\
  -w, --check-chars=N   compare no more than N characters in lines\n\
"), stdout);
     fputs (HELP_OPTION_DESCRIPTION, stdout);
     fputs (VERSION_OPTION_DESCRIPTION, stdout);
     fputs (_("\
\n\
A field is a run of whitespace, then non-whitespace characters.\n\
Fields are skipped before chars.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Convert OPT to size_t, reporting an error using MSGID if it does
   not fit.  */

static size_t
size_opt (char const *opt, char const *msgid)
{
  unsigned long int size;
  if (xstrtoul (opt, NULL, 10, &size, "") != LONGINT_OK
      || SIZE_MAX < size)
    error (EXIT_FAILURE, 0, "%s: %s", opt, _(msgid));
  return size;
}

/* Given a linebuffer LINE,
   return a pointer to the beginning of the line's field to be compared. */

static char *
find_field (const struct linebuffer *line)
{
  register size_t count;
  register char *lp = line->buffer;
  register size_t size = line->length - 1;
  register size_t i = 0;

  for (count = 0; count < skip_fields && i < size; count++)
    {
      while (i < size && ISBLANK (lp[i]))
    i++;
      while (i < size && !ISBLANK (lp[i]))
    i++;
    }

  for (count = 0; count < skip_chars && i < size; count++)
    i++;

  return lp + i;
}

/* Return zero if two strings OLD and NEW match, nonzero if not.
   OLD and NEW point not to the beginnings of the lines
   but rather to the beginnings of the fields to compare.
   OLDLEN and NEWLEN are their lengths. */

static int
different (char *old, char *new, size_t oldlen, size_t newlen)
{
  if (check_chars < oldlen)
    oldlen = check_chars;
  if (check_chars < newlen)
    newlen = check_chars;

  if (ignore_case)
    {
      /* FIXME: This should invoke strcoll somehow.  */
      return oldlen != newlen || memcasecmp (old, new, oldlen);
    }
  else if (HAVE_SETLOCALE && hard_LC_COLLATE)
    return xmemcoll (old, oldlen, new, newlen);
  else
    return oldlen != newlen || memcmp (old, new, oldlen);
}

/* Output the line in linebuffer LINE to stream STREAM
   provided that the switches say it should be output.
   If requested, print the number of times it occurred, as well;
   LINECOUNT + 1 is the number of times that the line occurred. */

static void
writeline (const struct linebuffer *line, FILE *stream, int linecount)
{
  if ((mode == output_unique && linecount != 0)
      || (mode == output_repeated && linecount == 0)
      || (mode == output_all_repeated && linecount == 0))
    return;

  if (countmode == count_occurrences)
    fprintf (stream, "%7d\t", linecount + 1);

  fwrite (line->buffer, sizeof (char), line->length, stream);
}

/* Process input file INFILE with output to OUTFILE.
   If either is "-", use the standard I/O stream for it instead. */

static void
check_file (const char *infile, const char *outfile)
{
  FILE *istream;
  FILE *ostream;
  struct linebuffer lb1, lb2;
  struct linebuffer *thisline, *prevline;

  if (STREQ (infile, "-"))
    istream = stdin;
  else
    istream = fopen (infile, "r");
  if (istream == NULL)
    error (EXIT_FAILURE, errno, "%s", infile);

  if (STREQ (outfile, "-"))
    ostream = stdout;
  else
    ostream = fopen (outfile, "w");
  if (ostream == NULL)
    error (EXIT_FAILURE, errno, "%s", outfile);

  thisline = &lb1;
  prevline = &lb2;

  initbuffer (thisline);
  initbuffer (prevline);

  /* The duplication in the following `if' and `else' blocks is an
     optimization to distinguish the common case (in which none of
     the following options has been specified: --count, -repeated,
     --all-repeated, --unique) from the others.  In the common case,
     this optimization lets uniq output each different line right away,
     without waiting to see if the next one is different.  */

  if (mode == output_all && countmode == count_none)
    {
      char *prevfield IF_LINT (= NULL);
      size_t prevlen IF_LINT (= 0);

      while (!feof (istream))
    {
      char *thisfield;
      size_t thislen;
      if (readline (thisline, istream) == 0)
        break;
      thisfield = find_field (thisline);
      thislen = thisline->length - 1 - (thisfield - thisline->buffer);
      if (prevline->length == 0
          || different (thisfield, prevfield, thislen, prevlen))
        {
          fwrite (thisline->buffer, sizeof (char),
              thisline->length, ostream);

          SWAP_LINES (prevline, thisline);
          prevfield = thisfield;
          prevlen = thislen;
        }
    }
    }
  else
    {
      char *prevfield;
      size_t prevlen;
      int match_count = 0;
      int first_delimiter = 1;

      if (readline (prevline, istream) == 0)
    goto closefiles;
      prevfield = find_field (prevline);
      prevlen = prevline->length - 1 - (prevfield - prevline->buffer);

      while (!feof (istream))
    {
      int match;
      char *thisfield;
      size_t thislen;
      if (readline (thisline, istream) == 0)
        break;
      thisfield = find_field (thisline);
      thislen = thisline->length - 1 - (thisfield - thisline->buffer);
      match = !different (thisfield, prevfield, thislen, prevlen);

      if (match)
        ++match_count;

          if (mode == output_all_repeated && delimit_groups != DM_NONE)
        {
          if (!match)
        {
          if (match_count) /* a previous match */
            first_delimiter = 0; /* Only used when DM_SEPARATE */
        }
          else if (match_count == 1)
        {
          if ((delimit_groups == DM_PREPEND)
              || (delimit_groups == DM_SEPARATE
              && !first_delimiter))
            putc ('\n', ostream);
        }
        }

      if (!match || mode == output_all_repeated)
        {
          writeline (prevline, ostream, match_count);
          SWAP_LINES (prevline, thisline);
          prevfield = thisfield;
          prevlen = thislen;
          if (!match)
        match_count = 0;
        }
    }

      writeline (prevline, ostream, match_count);
    }

 closefiles:
  if (ferror (istream) || fclose (istream) == EOF)
    error (EXIT_FAILURE, errno, _("error reading %s"), infile);

  /* Close ostream only if it's not stdout -- the latter is closed
     via the atexit-invoked close_stdout.  */
  if (ostream != stdout && (ferror (ostream) || fclose (ostream) == EOF))
    error (EXIT_FAILURE, errno, _("error writing %s"), outfile);

  free (lb1.buffer);
  free (lb2.buffer);
}

int
main (int argc, char **argv)
{
  int optc = 0;
  bool posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
  bool obsolete_skip_fields = false;
  int nfiles = 0;
  char const *file[2];

  file[0] = file[1] = "-";
  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);
  hard_LC_COLLATE = hard_locale (LC_COLLATE);

  atexit (close_stdout);

  skip_chars = 0;
  skip_fields = 0;
  check_chars = SIZE_MAX;
  mode = output_all;
  countmode = count_none;
  delimit_groups = DM_NONE;

  for (;;)
    {
      /* Parse an operand with leading "+" as a file after "--" was
         seen; or if pedantic and a file was seen; or if not
         obsolete.  */

      if (optc == -1
      || (posixly_correct && nfiles != 0)
      || ((optc = getopt_long (argc, argv,
                   "-0123456789Dcdf:is:uw:", longopts, NULL))
          == -1))
    {
      if (optind == argc)
        break;
      if (nfiles == 2)
        {
          error (0, 0, _("extra operand `%s'"), argv[optind]);
          usage (EXIT_FAILURE);
        }
      file[nfiles++] = argv[optind++];
    }
      else switch (optc)
    {
    case 1:
      {
        unsigned long int size;
        if (optarg[0] == '+'
        && posix2_version () < 200112
        && xstrtoul (optarg, NULL, 10, &size, "") == LONGINT_OK
        && size <= SIZE_MAX)
          skip_chars = size;
        else if (nfiles == 2)
          {
        error (0, 0, _("extra operand `%s'"), optarg);
        usage (EXIT_FAILURE);
          }
        else
          file[nfiles++] = optarg;
      }
      break;

    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      {
        size_t s = skip_fields;
        skip_fields = s * 10 + optc - '0';
        if (SIZE_MAX / 10 < s || skip_fields < s)
          error (EXIT_FAILURE, 0, "%s",
             _("invalid number of fields to skip"));
        obsolete_skip_fields = true;
      }
      break;

    case 'c':
      countmode = count_occurrences;
      break;

    case 'd':
      mode = output_repeated;
      break;

    case 'D':
      mode = output_all_repeated;
      if (optarg == NULL)
        delimit_groups = DM_NONE;
      else
        delimit_groups = XARGMATCH ("--all-repeated", optarg,
                    delimit_method_string,
                    delimit_method_map);
      break;

    case 'f':       /* Like '-#'. */
      skip_fields = size_opt (optarg,
                  N_("invalid number of fields to skip"));
      break;

    case 'i':
      ignore_case = 1;
      break;

    case 's':       /* Like '+#'. */
      skip_chars = size_opt (optarg,
                 N_("invalid number of bytes to skip"));
      break;

    case 'u':
      mode = output_unique;
      break;

    case 'w':
      check_chars = size_opt (optarg,
                  N_("invalid number of bytes to compare"));
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (obsolete_skip_fields && 200112 <= posix2_version ())
    {
      error (0, 0, _("`-%lu' option is obsolete; use `-f %lu'"),
         (unsigned long) skip_fields, (unsigned long) skip_fields);
      usage (EXIT_FAILURE);
    }

  if (countmode == count_occurrences && mode == output_all_repeated)
    {
      error (0, 0,
       _("printing all duplicated lines and repeat counts is meaningless"));
      usage (EXIT_FAILURE);
    }

  check_file (file[0], file[1]);

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/basename.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/basename.c used in: Step 750
/* basename -- strip directory and suffix from filenames
   Copyright (C) 1990-1997, 1999-2002 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.  */

/* Usage: basename name [suffix]
   NAME is a pathname; SUFFIX is a suffix to strip from it.

   basename /usr/foo/lossage/functions.l
   => functions.l
   basename /usr/foo/lossage/functions.l .l
   => functions
   basename functions.lisp p
   => functions.lis */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>

#include "system.h"
#include "long-options.h"
#include "dirname.h"
#include "error.h"
#include "closeout.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "basename"

#define AUTHORS "FIXME unknown"

/* The name this program was run with. */
char *program_name;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s NAME [SUFFIX]\n\
  or:  %s OPTION\n\
"),
          program_name, program_name);
      fputs (_("\
Print NAME with any leading directory components removed.\n\
If specified, also remove a trailing SUFFIX.\n\
\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

/* Remove SUFFIX from the end of NAME if it is there, unless NAME
   consists entirely of SUFFIX. */

static void
remove_suffix (char *name, const char *suffix)
{
  char *np;
  const char *sp;

  np = name + strlen (name);
  sp = suffix + strlen (suffix);

  while (np > name && sp > suffix)
    if (*--np != *--sp)
      return;
  if (np > name)
    *np = '\0';
}

int
main (int argc, char **argv)
{
  char *name;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              AUTHORS, usage);
  /* The above handles --help and --version.
     Since there is no other invocation of getopt, handle `--' here.  */
  if (argc > 1 && STREQ (argv[1], "--"))
    {
      --argc;
      ++argv;
    }

  if (argc == 1 || argc > 3)
    {
      error (0, 0, (argc == 1 ? _("too few arguments")
            : _("too many arguments")));
      usage (EXIT_FAILURE);
    }

  name = base_name (argv[1]);
  name[base_len (name)] = '\0';

  if (argc == 3)
    remove_suffix (name, argv[2]);

  puts (name);

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/system.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/system.h used in: Step 750, Step 849, Step 851, Step 853, Step 855, Step 857, Step 859, Step 861, Step 863, Step 865, Step 867, Step 871, Step 873, Step 875, Step 877, Step 879, Step 881, Step 883, Step 887, Step 891, Step 893, Step 895, Step 897, Step 899, Step 901, Step 903, Step 905, Step 907, Step 909, Step 911, Step 913, Step 915, Step 917, Step 919, Step 921, Step 923, Step 925, Step 927, Step 929, Step 931, Step 933, Step 935, Step 937, Step 939, Step 941, Step 945, Step 947, Step 951, Step 953, Step 955, Step 959, Step 964, Step 965, Step 966, Step 970, Step 972, Step 974
/* system-dependent definitions for fileutils, textutils, and sh-utils packages.
   Copyright (C) 1989, 1991-2002 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 sys/types.h before this file.  */

#if 2 <= __GLIBC__ && 2 <= __GLIBC_MINOR__
# if ! defined _SYS_TYPES_H
you must include <sys/types.h> before including this file
# endif
#endif

#include <sys/stat.h>

#if !defined HAVE_MKFIFO
# define mkfifo(path, mode) (mknod ((path), (mode) | S_IFIFO, 0))
#endif

#if HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif

/* <unistd.h> should be included before any preprocessor test
   of _POSIX_VERSION.  */
#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#ifndef STDIN_FILENO
# define STDIN_FILENO 0
#endif

#ifndef STDOUT_FILENO
# define STDOUT_FILENO 1
#endif

#ifndef STDERR_FILENO
# define STDERR_FILENO 2
#endif


#if HAVE_LIMITS_H
/* limits.h must come before pathmax.h because limits.h on some systems
   undefs PATH_MAX, whereas pathmax.h sets PATH_MAX.  */
# include <limits.h>
#endif

#include "pathmax.h"

#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif

/* Since major is a function on SVR4, we can't use `ifndef major'.  */
#if MAJOR_IN_MKDEV
# include <sys/mkdev.h>
# define HAVE_MAJOR
#endif
#if MAJOR_IN_SYSMACROS
# include <sys/sysmacros.h>
# define HAVE_MAJOR
#endif
#ifdef major            /* Might be defined in sys/types.h.  */
# define HAVE_MAJOR
#endif

#ifndef HAVE_MAJOR
# define major(dev)  (((dev) >> 8) & 0xff)
# define minor(dev)  ((dev) & 0xff)
# define makedev(maj, min)  (((maj) << 8) | (min))
#endif
#undef HAVE_MAJOR

#if HAVE_UTIME_H
# include <utime.h>
#endif

/* Some systems (even some that do have <utime.h>) don't declare this
   structure anywhere.  */
#ifndef HAVE_STRUCT_UTIMBUF
struct utimbuf
{
  long actime;
  long modtime;
};
#endif

/* Don't use bcopy!  Use memmove if source and destination may overlap,
   memcpy otherwise.  */

#if HAVE_STRING_H
# if !STDC_HEADERS && HAVE_MEMORY_H
#  include <memory.h>
# endif
# include <string.h>
#else
# include <strings.h>
#endif
#if ! HAVE_DECL_MEMRCHR
void *memrchr (const void *, int, size_t);
#endif

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#if HAVE_STDBOOL_H
# include <stdbool.h>
#else
typedef enum {false = 0, true = 1} bool;
#endif

#if HAVE_STDLIB_H
# define getopt system_getopt
# include <stdlib.h>
# undef getopt
#endif

/* The following test is to work around the gross typo in
   systems like Sony NEWS-OS Release 4.0C, whereby EXIT_FAILURE
   is defined to 0, not 1.  */
#if !EXIT_FAILURE
# undef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif

#ifndef EXIT_SUCCESS
# define EXIT_SUCCESS 0
#endif

#if HAVE_FCNTL_H
# include <fcntl.h>
#else
# include <sys/file.h>
#endif

#if !defined SEEK_SET
# define SEEK_SET 0
# define SEEK_CUR 1
# define SEEK_END 2
#endif
#ifndef F_OK
# define F_OK 0
# define X_OK 1
# define W_OK 2
# define R_OK 4
#endif

/* For systems that distinguish between text and binary I/O.
   O_BINARY is usually declared in fcntl.h  */
#if !defined O_BINARY && defined _O_BINARY
  /* For MSC-compatible compilers.  */
# define O_BINARY _O_BINARY
# define O_TEXT _O_TEXT
#endif

#ifdef __BEOS__
  /* BeOS 5 has O_BINARY and O_TEXT, but they have no effect.  */
# undef O_BINARY
# undef O_TEXT
#endif

#if O_BINARY
# ifndef __DJGPP__
#  define setmode _setmode
#  define fileno(_fp) _fileno (_fp)
# endif /* not DJGPP */
# define SET_MODE(_f, _m) setmode (_f, _m)
# define SET_BINARY(_f) do {if (!isatty(_f)) setmode (_f, O_BINARY);} while (0)
# define SET_BINARY2(_f1, _f2)      \
  do {                  \
    if (!isatty (_f1))          \
      {                 \
        setmode (_f1, O_BINARY);    \
    if (!isatty (_f2))      \
      setmode (_f2, O_BINARY);  \
      }                 \
  } while(0)
#else
# define SET_MODE(_f, _m) (void)0
# define SET_BINARY(f) (void)0
# define SET_BINARY2(f1,f2) (void)0
# define O_BINARY 0
# define O_TEXT 0
#endif /* O_BINARY */

#if HAVE_DIRENT_H
# include <dirent.h>
# define NLENGTH(direct) (strlen((direct)->d_name))
#else /* not HAVE_DIRENT_H */
# define dirent direct
# define NLENGTH(direct) ((direct)->d_namlen)
# if HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif /* HAVE_SYS_NDIR_H */
# if HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif /* HAVE_SYS_DIR_H */
# if HAVE_NDIR_H
#  include <ndir.h>
# endif /* HAVE_NDIR_H */
#endif /* HAVE_DIRENT_H */

#if CLOSEDIR_VOID
/* Fake a return value. */
# define CLOSEDIR(d) (closedir (d), 0)
#else
# define CLOSEDIR(d) closedir (d)
#endif

/* Get or fake the disk device blocksize.
   Usually defined by sys/param.h (if at all).  */
#if !defined DEV_BSIZE && defined BSIZE
# define DEV_BSIZE BSIZE
#endif
#if !defined DEV_BSIZE && defined BBSIZE /* SGI */
# define DEV_BSIZE BBSIZE
#endif
#ifndef DEV_BSIZE
# define DEV_BSIZE 4096
#endif

/* Extract or fake data from a `struct stat'.
   ST_BLKSIZE: Preferred I/O blocksize for the file, in bytes.
   ST_NBLOCKS: Number of blocks in the file, including indirect blocks.
   ST_NBLOCKSIZE: Size of blocks used when calculating ST_NBLOCKS.  */
#ifndef HAVE_STRUCT_STAT_ST_BLOCKS
# define ST_BLKSIZE(statbuf) DEV_BSIZE
# if defined _POSIX_SOURCE || !defined BSIZE /* fileblocks.c uses BSIZE.  */
#  define ST_NBLOCKS(statbuf) \
  ((statbuf).st_size / ST_NBLOCKSIZE + ((statbuf).st_size % ST_NBLOCKSIZE != 0))
# else /* !_POSIX_SOURCE && BSIZE */
#  define ST_NBLOCKS(statbuf) \
  (S_ISREG ((statbuf).st_mode) \
   || S_ISDIR ((statbuf).st_mode) \
   ? st_blocks ((statbuf).st_size) : 0)
# endif /* !_POSIX_SOURCE && BSIZE */
#else /* HAVE_STRUCT_STAT_ST_BLOCKS */
/* Some systems, like Sequents, return st_blksize of 0 on pipes.
   Also, when running `rsh hpux11-system cat any-file', cat would
   determine that the output stream had an st_blksize of 2147421096.
   So here we arbitrarily limit the `optimal' block size to 4MB.
   If anyone knows of a system for which the legitimate value for
   st_blksize can exceed 4MB, please report it as a bug in this code.  */
# define ST_BLKSIZE(statbuf) ((0 < (statbuf).st_blksize \
                   && (statbuf).st_blksize <= (1 << 22)) /* 4MB */ \
                  ? (statbuf).st_blksize : DEV_BSIZE)
# if defined hpux || defined __hpux__ || defined __hpux
/* HP-UX counts st_blocks in 1024-byte units.
   This loses when mixing HP-UX and BSD filesystems with NFS.  */
#  define ST_NBLOCKSIZE 1024
# else /* !hpux */
#  if defined _AIX && defined _I386
/* AIX PS/2 counts st_blocks in 4K units.  */
#   define ST_NBLOCKSIZE (4 * 1024)
#  else /* not AIX PS/2 */
#   if defined _CRAY
#    define ST_NBLOCKS(statbuf) \
  (S_ISREG ((statbuf).st_mode) \
   || S_ISDIR ((statbuf).st_mode) \
   ? (statbuf).st_blocks * ST_BLKSIZE(statbuf)/ST_NBLOCKSIZE : 0)
#   endif /* _CRAY */
#  endif /* not AIX PS/2 */
# endif /* !hpux */
#endif /* HAVE_STRUCT_STAT_ST_BLOCKS */

#ifndef ST_NBLOCKS
# define ST_NBLOCKS(statbuf) ((statbuf).st_blocks)
#endif

#ifndef ST_NBLOCKSIZE
# define ST_NBLOCKSIZE 512
#endif

#include "sys2.h"

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/long-options.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/long-options.h used in: Step 750, Step 859, Step 861, Step 865, Step 867, Step 879, Step 887, Step 891, Step 905, Step 909, Step 913, Step 915, Step 921, Step 923, Step 935, Step 941, Step 945, Step 955
/* long-options.h -- declaration for --help- and --version-handling function.
   Copyright (C) 1993, 1994, 1998, 1999 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   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.  */

/* Written by Jim Meyering.  */

#ifndef PARAMS
# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#  define PARAMS(Args) Args
# else
#  define PARAMS(Args) ()
# endif
#endif

void
  parse_long_options PARAMS ((int _argc,
                  char **_argv,
                  const char *_command_name,
                  const char *_package,
                  const char *_version,
                  const char *_authors,
                  void (*_usage) (int)));

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/dirname.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/dirname.h used in: Step 750, Step 859, Step 895, Step 917, Step 925
/*  Copyright (C) 1998, 2001 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.  */

#ifndef DIRNAME_H_
# define DIRNAME_H_ 1

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

# ifndef DIRECTORY_SEPARATOR
#  define DIRECTORY_SEPARATOR '/'
# endif

# ifndef ISSLASH
#  define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR)
# endif

# ifndef FILESYSTEM_PREFIX_LEN
#  define FILESYSTEM_PREFIX_LEN(Filename) 0
# endif

char *base_name PARAMS ((char const *path));
char *dir_name PARAMS ((char const *path));
size_t base_len PARAMS ((char const *path));
size_t dir_len PARAMS ((char const *path));

int strip_trailing_slashes PARAMS ((char *path));

#endif /* not DIRNAME_H_ */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/error.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/error.h used in: Step 750, Step 849, Step 851, Step 855, Step 857, Step 859, Step 863, Step 865, Step 867, Step 873, Step 875, Step 877, Step 879, Step 881, Step 883, Step 887, Step 893, Step 895, Step 897, Step 899, Step 901, Step 903, Step 905, Step 907, Step 909, Step 911, Step 913, Step 915, Step 917, Step 919, Step 921, Step 923, Step 925, Step 927, Step 929, Step 931, Step 933, Step 935, Step 937, Step 939, Step 941, Step 947, Step 951, Step 955, Step 959, Step 964, Step 966, Step 970, Step 972
/* Declaration for error-reporting function
   Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.


   NOTE: The canonical source of this file is maintained with the GNU C Library.
   Bugs can be reported to bug-glibc@prep.ai.mit.edu.

   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.  */

#ifndef _ERROR_H
#define _ERROR_H 1

#ifndef __attribute__
/* This feature is available in gcc versions 2.5 and later.  */
# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
#  define __attribute__(Spec) /* empty */
# endif
/* The __-protected variants of `format' and `printf' attributes
   are accepted by gcc versions 2.6.4 (effectively 2.7) and later.  */
# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7)
#  define __format__ format
#  define __printf__ printf
# endif
#endif

#ifdef  __cplusplus
extern "C" {
#endif

#if defined (__STDC__) && __STDC__

/* Print a message with `fprintf (stderr, FORMAT, ...)';
   if ERRNUM is nonzero, follow it with ": " and strerror (ERRNUM).
   If STATUS is nonzero, terminate the program with `exit (STATUS)'.  */

extern void error (int status, int errnum, const char *format, ...)
     __attribute__ ((__format__ (__printf__, 3, 4)));

extern void error_at_line (int status, int errnum, const char *fname,
               unsigned int lineno, const char *format, ...)
     __attribute__ ((__format__ (__printf__, 5, 6)));

/* If NULL, error will flush stdout, then print on stderr the program
   name, a colon and a space.  Otherwise, error will call this
   function without parameters instead.  */
extern void (*error_print_progname) (void);

#else
void error ();
void error_at_line ();
extern void (*error_print_progname) ();
#endif

/* This variable is incremented each time `error' is called.  */
extern unsigned int error_message_count;

/* Sometimes we want to have at most one error per line.  This
   variable controls whether this mode is selected or not.  */
extern int error_one_per_line;

#ifdef  __cplusplus
}
#endif

#endif /* error.h */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/closeout.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/closeout.h used in: Step 750, Step 849, Step 855, Step 857, Step 859, Step 861, Step 863, Step 865, Step 867, Step 871, Step 873, Step 875, Step 877, Step 879, Step 881, Step 883, Step 891, Step 899, Step 901, Step 903, Step 905, Step 907, Step 909, Step 911, Step 913, Step 919, Step 921, Step 925, Step 927, Step 929, Step 931, Step 933, Step 935, Step 937, Step 939, Step 945, Step 947, Step 953, Step 955, Step 966
#ifndef CLOSEOUT_H
# define CLOSEOUT_H 1

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

void close_stdout_set_status PARAMS ((int status));
void close_stdout_set_file_name PARAMS ((const char *file));
void close_stdout PARAMS ((void));
void close_stdout_status PARAMS ((int status));

#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/acl.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/acl.c used in: Step 751
/* acl.c - access control lists

   Copyright (C) 2002 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.

   Written by Paul Eggert.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/stat.h>
#ifndef S_ISLNK
# define S_ISLNK(Mode) 0
#endif

#include "acl.h"

#include <errno.h>
#ifndef ENOSYS
# define ENOSYS (-1)
#endif

#ifndef MIN_ACL_ENTRIES
# define MIN_ACL_ENTRIES 4
#endif

/* Return 1 if PATH has a nontrivial access control list, 0 if not,
   and -1 (setting errno) if an error is encountered.  */

int
file_has_acl (char const *path, struct stat const *pathstat)
{
  /* FIXME: This implementation should work on recent-enough versions
     of HP-UX, Solaris, and Unixware, but it simply returns 0 with
     POSIX 1003.1e (draft 17 -- abandoned), AIX, GNU/Linux, Irix, and
     Tru64.  Please see Samba's source/lib/sysacls.c file for
     fix-related ideas.  */

#if HAVE_ACL && defined GETACLCNT
  if (! S_ISLNK (pathstat->st_mode))
    {
      int n = acl (path, GETACLCNT, 0, NULL);
      return n < 0 ? (errno == ENOSYS ? 0 : -1) : (MIN_ACL_ENTRIES < n);
    }
#endif

  return 0;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/posixtm.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/posixtm.c used in: Step 752
/* Parse dates for touch and date.
   Copyright (C) 1989, 1990, 1991, 1998, 2000-2002 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.  */

/* Yacc-based version written by Jim Kingdon and David MacKenzie.
   Rewritten by Jim Meyering.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_STDBOOL_H
# include <stdbool.h>
#else
typedef enum {false = 0, true = 1} bool;
#endif

#include <stdio.h>
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#include <sys/types.h>
#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif

#ifdef TM_IN_SYS_TIME
# include <sys/time.h>
#else
# include <time.h>
#endif

#include "posixtm.h"
#include "unlocked-io.h"

/* ISDIGIT differs from isdigit, as follows:
   - Its arg may be any int or unsigned int; it need not be an unsigned char.
   - It's guaranteed to evaluate its argument exactly once.
   - It's typically faster.
   POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
   ISDIGIT_LOCALE unless it's important to use the locale's definition
   of `digit' even when the host does not conform to POSIX.  */
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)

time_t mktime ();

/*
  POSIX requires:

  touch -t [[CC]YY]mmddhhmm[.ss] FILE...
    8, 10, or 12 digits, followed by optional .ss
    (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS)

  touch mmddhhmm[YY] FILE... (obsoleted by POSIX 1003.1-2001)
    8 or 10 digits
    (PDS_TRAILING_YEAR)

  date mmddhhmm[[CC]YY]
    8, 10, or 12 digits
    (PDS_TRAILING_YEAR | PDS_CENTURY)

*/

static int
year (struct tm *tm, const int *digit_pair, size_t n, int allow_century)
{
  switch (n)
    {
    case 1:
      tm->tm_year = *digit_pair;
      /* Deduce the century based on the year.
     POSIX requires that 00-68 be interpreted as 2000-2068,
     and that 69-99 be interpreted as 1969-1999.  */
      if (digit_pair[0] <= 68)
    tm->tm_year += 100;
      break;

    case 2:
      if (!allow_century)
    return 1;
      tm->tm_year = digit_pair[0] * 100 + digit_pair[1] - 1900;
      break;

    case 0:
      {
    time_t now;
    struct tm *tmp;

    /* Use current year.  */
    time (&now);
    tmp = localtime (&now);
    if (! tmp)
      return 1;
    tm->tm_year = tmp->tm_year;
      }
      break;

    default:
      abort ();
    }

  return 0;
}

static int
posix_time_parse (struct tm *tm, const char *s, unsigned int syntax_bits)
{
  const char *dot = NULL;
  int pair[6];
  int *p;
  unsigned int i;

  size_t s_len = strlen (s);
  size_t len = (((syntax_bits & PDS_SECONDS) && (dot = strchr (s, '.')))
        ? (size_t) (dot - s)
        : s_len);

  if (len != 8 && len != 10 && len != 12)
    return 1;

  if (dot)
    {
      if (!(syntax_bits & PDS_SECONDS))
    return 1;

      if (s_len - len != 3)
    return 1;
    }

  for (i = 0; i < len; i++)
    if (!ISDIGIT (s[i]))
      return 1;

  len /= 2;
  for (i = 0; i < len; i++)
    pair[i] = 10 * (s[2*i] - '0') + s[2*i + 1] - '0';

  p = pair;
  if (syntax_bits & PDS_LEADING_YEAR)
    {
      if (year (tm, p, len - 4, syntax_bits & PDS_CENTURY))
    return 1;
      p += len - 4;
      len = 4;
    }

  /* Handle 8 digits worth of `MMDDhhmm'.  */
  tm->tm_mon = *p++ - 1;
  tm->tm_mday = *p++;
  tm->tm_hour = *p++;
  tm->tm_min = *p++;
  len -= 4;

  /* Handle any trailing year.  */
  if (syntax_bits & PDS_TRAILING_YEAR)
    {
      if (year (tm, p, len, syntax_bits & PDS_CENTURY))
    return 1;
    }

  /* Handle seconds.  */
  if (!dot)
    {
      tm->tm_sec = 0;
    }
  else
    {
      int seconds;

      ++dot;
      if (!ISDIGIT (dot[0]) || !ISDIGIT (dot[1]))
    return 1;
      seconds = 10 * (dot[0] - '0') + dot[1] - '0';

      tm->tm_sec = seconds;
    }

  return 0;
}

/* Parse a POSIX-style date, returning true if successful.  */

bool
posixtime (time_t *p, const char *s, unsigned int syntax_bits)
{
  struct tm tm0;
  struct tm tm1;
  struct tm const *tm;
  time_t t;

  if (posix_time_parse (&tm0, s, syntax_bits))
    return false;

  tm1 = tm0;
  tm1.tm_isdst = -1;
  t = mktime (&tm1);

  if (t != (time_t) -1)
    tm = &tm1;
  else
    {
      /* mktime returns -1 for errors, but -1 is also a valid time_t
     value.  Check whether an error really occurred.  */
      tm = localtime (&t);
      if (! tm)
    return false;
    }

  /* Reject dates like "September 31" and times like "25:61".  */
  if ((tm0.tm_year ^ tm->tm_year)
      | (tm0.tm_mon ^ tm->tm_mon)
      | (tm0.tm_mday ^ tm->tm_mday)
      | (tm0.tm_hour ^ tm->tm_hour)
      | (tm0.tm_min ^ tm->tm_min)
      | (tm0.tm_sec ^ tm->tm_sec))
    return false;

  *p = t;
  return true;
}

#ifdef TEST_POSIXTIME
/*
    Test mainly with syntax_bits == 13
    (aka: (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS))

    This test data assumes Universal Time, e.g., TZ="UTC0".

    This test data also assumes that time_t is signed and is at least
    39 bits wide, so that it can represent all years from 0000 through
    9999.  A host with 32-bit signed time_t can represent only time
    stamps in the range 1901-12-13 20:45:52 through 2038-01-18
    03:14:07 UTC, assuming POSIX time_t with no leap seconds, so test
    cases outside this range will not work on such a host.

    Also, the first two lines of test data assume that the current
    year is 2002.

BEGIN-DATA
12131415.16     13   1039788916 Fri Dec 13 14:15:16 2002
12131415.16     13   1039788916 Fri Dec 13 14:15:16 2002
000001010000.00 13 -62167132800 Sun Jan  1 00:00:00 0000
190112132045.52 13  -2147483648 Fri Dec 13 20:45:52 1901
190112132045.53 13  -2147483647 Fri Dec 13 20:45:53 1901
190112132046.52 13  -2147483588 Fri Dec 13 20:46:52 1901
190112132145.52 13  -2147480048 Fri Dec 13 21:45:52 1901
190112142045.52 13  -2147397248 Sat Dec 14 20:45:52 1901
190201132045.52 13  -2144805248 Mon Jan 13 20:45:52 1902
196912312359.59 13           -1 Wed Dec 31 23:59:59 1969
197001010000.00 13            0 Thu Jan  1 00:00:00 1970
197001010000.01 13            1 Thu Jan  1 00:00:01 1970
197001010001.00 13           60 Thu Jan  1 00:01:00 1970
197001010100.00 13         3600 Thu Jan  1 01:00:00 1970
197001020000.00 13        86400 Fri Jan  2 00:00:00 1970
197002010000.00 13      2678400 Sun Feb  1 00:00:00 1970
197101010000.00 13     31536000 Fri Jan  1 00:00:00 1971
197001000000.00 13            * *
197000010000.00 13            * *
197001010000.60 13            * *
197001010060.00 13            * *
197001012400.00 13            * *
197001320000.00 13            * *
197013010000.00 13            * *
203801190314.06 13   2147483646 Tue Jan 19 03:14:06 2038
203801190314.07 13   2147483647 Tue Jan 19 03:14:07 2038
203801190314.08 13   2147483648 Tue Jan 19 03:14:08 2038
999912312359.59 13 253402300799 Fri Dec 31 23:59:59 9999
1112131415      13   1323785700 Tue Dec 13 14:15:00 2011
1112131415.16   13   1323785716 Tue Dec 13 14:15:16 2011
201112131415.16 13   1323785716 Tue Dec 13 14:15:16 2011
191112131415.16 13  -1831974284 Wed Dec 13 14:15:16 1911
203712131415.16 13   2144326516 Sun Dec 13 14:15:16 2037
3712131415.16   13   2144326516 Sun Dec 13 14:15:16 2037
6812131415.16   13   3122633716 Thu Dec 13 14:15:16 2068
6912131415.16   13     -1590284 Sat Dec 13 14:15:16 1969
7012131415.16   13     29945716 Sun Dec 13 14:15:16 1970
1213141599       2    945094500 Mon Dec 13 14:15:00 1999
1213141500       2    976716900 Wed Dec 13 14:15:00 2000
END-DATA

*/

# define MAX_BUFF_LEN 1024

int
main ()
{
  char buff[MAX_BUFF_LEN + 1];

  buff[MAX_BUFF_LEN] = 0;
  while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
    {
      char time_str[MAX_BUFF_LEN];
      unsigned int syntax_bits;
      time_t t;
      if (sscanf (buff, "%s %u", time_str, &syntax_bits) != 2)
    printf ("*\n");
      else
    {
      printf ("%-15s %2u ", time_str, syntax_bits);
      if (posixtime (&t, time_str, syntax_bits))
        printf ("%12ld %s", (long) t, ctime (&t));
      else
        printf ("%12s %s", "*", "*\n");
    }
    }
  exit (0);

}
#endif


/*
Local Variables:
compile-command: "gcc -DTEST_POSIXTIME -DHAVE_CONFIG_H -I.. -g -O -Wall -W posixtm.c"
End:
*/

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/posixver.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/posixver.c used in: Step 753
/* Which POSIX version to conform to, for utilities.

   Copyright (C) 2002 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU Library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library 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.  */

/* Written by Paul Eggert.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <limits.h>

#include <stdlib.h>
#if !HAVE_DECL_GETENV && !defined getenv
char *getenv ();
#endif

#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifndef _POSIX2_VERSION
# define _POSIX2_VERSION 0
#endif

/* The POSIX version that utilities should conform to.  The default is
   specified by the system.  */

int
posix2_version (void)
{
  long int v = _POSIX2_VERSION;
  char const *s = getenv ("_POSIX2_VERSION");

  if (s && *s)
    {
      char *e;
      long int i = strtol (s, &e, 10);
      if (! *e)
    v = i;
    }

  return v < INT_MIN ? INT_MIN : v < INT_MAX ? v : INT_MAX;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/strftime.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/strftime.c used in: Step 754
/* Copyright (C) 1991-1999, 2000, 2001, 2003 Free Software Foundation, Inc.

   NOTE: The canonical source of this file is maintained with the GNU C Library.
   Bugs can be reported to bug-glibc@prep.ai.mit.edu.

   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
   Library 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.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifdef _LIBC
# define HAVE_LIMITS_H 1
# define HAVE_MBLEN 1
# define HAVE_MBRLEN 1
# define HAVE_STRUCT_ERA_ENTRY 1
# define HAVE_TM_GMTOFF 1
# define HAVE_TM_ZONE 1
# define HAVE_TZNAME 1
# define HAVE_TZSET 1
# define MULTIBYTE_IS_FORMAT_SAFE 1
# define STDC_HEADERS 1
# include "../locale/localeinfo.h"
#endif

#if defined emacs && !defined HAVE_BCOPY
# define HAVE_MEMCPY 1
#endif

#include <ctype.h>
#include <sys/types.h>      /* Some systems define `time_t' here.  */

#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif
#if HAVE_TZNAME
extern char *tzname[];
#endif

/* Do multibyte processing if multibytes are supported, unless
   multibyte sequences are safe in formats.  Multibyte sequences are
   safe if they cannot contain byte sequences that look like format
   conversion specifications.  The GNU C Library uses UTF8 multibyte
   encoding, which is safe for formats, but strftime.c can be used
   with other C libraries that use unsafe encodings.  */
#define DO_MULTIBYTE (HAVE_MBLEN && ! MULTIBYTE_IS_FORMAT_SAFE)

#if DO_MULTIBYTE
# if HAVE_MBRLEN
#  include <wchar.h>
# else
   /* Simulate mbrlen with mblen as best we can.  */
#  define mbstate_t int
#  define mbrlen(s, n, ps) mblen (s, n)
#  define mbsinit(ps) (*(ps) == 0)
# endif
  static const mbstate_t mbstate_zero;
#endif

#if HAVE_LIMITS_H
# include <limits.h>
#endif

#if STDC_HEADERS
# include <stddef.h>
# include <stdlib.h>
# include <string.h>
#else
# ifndef HAVE_MEMCPY
#  define memcpy(d, s, n) bcopy ((s), (d), (n))
# endif
#endif

#ifdef COMPILE_WIDE
# include <endian.h>
# define CHAR_T wchar_t
# define UCHAR_T unsigned int
# define L_(Str) L##Str
# define NLW(Sym) _NL_W##Sym

# define MEMCPY(d, s, n) __wmemcpy (d, s, n)
# define STRLEN(s) __wcslen (s)

#else
# define CHAR_T char
# define UCHAR_T unsigned char
# define L_(Str) Str
# define NLW(Sym) Sym

# if !defined STDC_HEADERS && !defined HAVE_MEMCPY
#  define MEMCPY(d, s, n) bcopy ((s), (d), (n))
# else
#  define MEMCPY(d, s, n) memcpy ((d), (s), (n))
# endif
# define STRLEN(s) strlen (s)

# ifdef _LIBC
#  define MEMPCPY(d, s, n) __mempcpy (d, s, n)
# else
#  ifndef HAVE_MEMPCPY
#   define MEMPCPY(d, s, n) ((void *) ((char *) memcpy (d, s, n) + (n)))
#  endif
# endif
#endif

#ifndef __P
# if defined __GNUC__ || (defined __STDC__ && __STDC__)
#  define __P(args) args
# else
#  define __P(args) ()
# endif  /* GCC.  */
#endif  /* Not __P.  */

#ifndef PTR
# ifdef __STDC__
#  define PTR void *
# else
#  define PTR char *
# endif
#endif

#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif

#ifndef NULL
# define NULL 0
#endif

#define TYPE_SIGNED(t) ((t) -1 < 0)

/* Bound on length of the string representing an integer value of type t.
   Subtract one for the sign bit if t is signed;
   302 / 1000 is log10 (2) rounded up;
   add one for integer division truncation;
   add one more for a minus sign if t is signed.  */
#define INT_STRLEN_BOUND(t) \
 ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 + 1 + TYPE_SIGNED (t))

#define TM_YEAR_BASE 1900

#ifndef __isleap
/* Nonzero if YEAR is a leap year (every 4 years,
   except every 100th isn't, and every 400th is).  */
# define __isleap(year) \
  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
#endif


#ifdef _LIBC
# define my_strftime_gmtime_r __gmtime_r
# define my_strftime_localtime_r __localtime_r
# define tzname __tzname
# define tzset __tzset
#else

/* If we're a strftime substitute in a GNU program, then prefer gmtime
   to gmtime_r, since many gmtime_r implementations are buggy.
   Similarly for localtime_r.  */

# if ! HAVE_TM_GMTOFF
static struct tm *my_strftime_gmtime_r __P ((const time_t *, struct tm *));
static struct tm *
my_strftime_gmtime_r (t, tp)
     const time_t *t;
     struct tm *tp;
{
  struct tm *l = gmtime (t);
  if (! l)
    return 0;
  *tp = *l;
  return tp;
}

static struct tm *my_strftime_localtime_r __P ((const time_t *, struct tm *));
static struct tm *
my_strftime_localtime_r (t, tp)
     const time_t *t;
     struct tm *tp;
{
  struct tm *l = localtime (t);
  if (! l)
    return 0;
  *tp = *l;
  return tp;
}
# endif /* ! HAVE_TM_GMTOFF */
#endif /* ! defined _LIBC */


#if !defined memset && !defined HAVE_MEMSET && !defined _LIBC
/* Some systems lack the `memset' function and we don't want to
   introduce additional dependencies.  */
/* The SGI compiler reportedly barfs on the trailing null
   if we use a string constant as the initializer.  28 June 1997, rms.  */
static const CHAR_T spaces[16] = /* "                " */
{
  L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),
  L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' '),L_(' ')
};
static const CHAR_T zeroes[16] = /* "0000000000000000" */
{
  L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),
  L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0'),L_('0')
};

# define memset_space(P, Len) \
  do {                                        \
    int _len = (Len);                                 \
                                          \
    do                                        \
      {                                       \
    int _this = _len > 16 ? 16 : _len;                    \
    (P) = MEMPCPY ((P), spaces, _this * sizeof (CHAR_T));             \
    _len -= _this;                                \
      }                                       \
    while (_len > 0);                                 \
  } while (0)

# define memset_zero(P, Len) \
  do {                                        \
    int _len = (Len);                                 \
                                          \
    do                                        \
      {                                       \
    int _this = _len > 16 ? 16 : _len;                    \
    (P) = MEMPCPY ((P), zeroes, _this * sizeof (CHAR_T));             \
    _len -= _this;                                \
      }                                       \
    while (_len > 0);                                 \
  } while (0)
#else
# ifdef COMPILE_WIDE
#  define memset_space(P, Len) (wmemset ((P), L' ', (Len)), (P) += (Len))
#  define memset_zero(P, Len) (wmemset ((P), L'0', (Len)), (P) += (Len))
# else
#  define memset_space(P, Len) (memset ((P), ' ', (Len)), (P) += (Len))
#  define memset_zero(P, Len) (memset ((P), '0', (Len)), (P) += (Len))
# endif
#endif

#define add(n, f)                                 \
  do                                          \
    {                                         \
      int _n = (n);                               \
      int _delta = width - _n;                            \
      int _incr = _n + (_delta > 0 ? _delta : 0);                 \
      if ((size_t) _incr >= maxsize - i)                      \
    return 0;                                 \
      if (p)                                      \
    {                                     \
      if (_delta > 0)                             \
        {                                     \
          if (pad == L_('0'))                         \
        memset_zero (p, _delta);                      \
          else                                \
        memset_space (p, _delta);                     \
        }                                     \
      f;                                      \
      p += _n;                                \
    }                                     \
      i += _incr;                                 \
    } while (0)

#define cpy(n, s) \
    add ((n),                                     \
     if (to_lowcase)                              \
       memcpy_lowcase (p, (s), _n LOCALE_ARG);                \
     else if (to_uppcase)                             \
       memcpy_uppcase (p, (s), _n LOCALE_ARG);                \
     else                                     \
       MEMCPY ((PTR) p, (const PTR) (s), _n))

#ifdef COMPILE_WIDE
# ifndef USE_IN_EXTENDED_LOCALE_MODEL
#  undef __mbsrtowcs_l
#  define __mbsrtowcs_l(d, s, l, st, loc) __mbsrtowcs (d, s, l, st)
# endif
# define widen(os, ws, l) \
  {                                       \
    mbstate_t __st;                               \
    const char *__s = os;                             \
    memset (&__st, '\0', sizeof (__st));                      \
    l = __mbsrtowcs_l (NULL, &__s, 0, &__st, loc);                \
    ws = (wchar_t *) alloca ((l + 1) * sizeof (wchar_t));             \
    (void) __mbsrtowcs_l (ws, &__s, l, &__st, loc);               \
  }
#endif


#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
/* We use this code also for the extended locale handling where the
   function gets as an additional argument the locale which has to be
   used.  To access the values we have to redefine the _NL_CURRENT
   macro.  */
# define strftime       __strftime_l
# define wcsftime       __wcsftime_l
# undef _NL_CURRENT
# define _NL_CURRENT(category, item) \
  (current->values[_NL_ITEM_INDEX (item)].string)
# define LOCALE_PARAM , loc
# define LOCALE_ARG , loc
# define LOCALE_PARAM_DECL  __locale_t loc;
# define LOCALE_PARAM_PROTO , __locale_t loc
# define HELPER_LOCALE_ARG  , current
#else
# define LOCALE_PARAM
# define LOCALE_PARAM_PROTO
# define LOCALE_ARG
# define LOCALE_PARAM_DECL
# ifdef _LIBC
#  define HELPER_LOCALE_ARG , _NL_CURRENT_DATA (LC_TIME)
# else
#  define HELPER_LOCALE_ARG
# endif
#endif

#ifdef COMPILE_WIDE
# ifdef USE_IN_EXTENDED_LOCALE_MODEL
#  define TOUPPER(Ch, L) __towupper_l (Ch, L)
#  define TOLOWER(Ch, L) __towlower_l (Ch, L)
# else
#  define TOUPPER(Ch, L) towupper (Ch)
#  define TOLOWER(Ch, L) towlower (Ch)
# endif
#else
# ifdef _LIBC
#  ifdef USE_IN_EXTENDED_LOCALE_MODEL
#   define TOUPPER(Ch, L) __toupper_l (Ch, L)
#   define TOLOWER(Ch, L) __tolower_l (Ch, L)
#  else
#   define TOUPPER(Ch, L) toupper (Ch)
#   define TOLOWER(Ch, L) tolower (Ch)
#  endif
# else
#  define TOUPPER(Ch, L) (islower (Ch) ? toupper (Ch) : (Ch))
#  define TOLOWER(Ch, L) (isupper (Ch) ? tolower (Ch) : (Ch))
# endif
#endif
/* We don't use `isdigit' here since the locale dependent
   interpretation is not what we want here.  We only need to accept
   the arabic digits in the ASCII range.  One day there is perhaps a
   more reliable way to accept other sets of digits.  */
#define ISDIGIT(Ch) ((unsigned int) (Ch) - L_('0') <= 9)

static CHAR_T *memcpy_lowcase __P ((CHAR_T *dest, const CHAR_T *src,
                    size_t len LOCALE_PARAM_PROTO));

static CHAR_T *
memcpy_lowcase (dest, src, len LOCALE_PARAM)
     CHAR_T *dest;
     const CHAR_T *src;
     size_t len;
     LOCALE_PARAM_DECL
{
  while (len-- > 0)
    dest[len] = TOLOWER ((UCHAR_T) src[len], loc);
  return dest;
}

static CHAR_T *memcpy_uppcase __P ((CHAR_T *dest, const CHAR_T *src,
                    size_t len LOCALE_PARAM_PROTO));

static CHAR_T *
memcpy_uppcase (dest, src, len LOCALE_PARAM)
     CHAR_T *dest;
     const CHAR_T *src;
     size_t len;
     LOCALE_PARAM_DECL
{
  while (len-- > 0)
    dest[len] = TOUPPER ((UCHAR_T) src[len], loc);
  return dest;
}


#if ! HAVE_TM_GMTOFF
/* Yield the difference between *A and *B,
   measured in seconds, ignoring leap seconds.  */
# define tm_diff ftime_tm_diff
static int tm_diff __P ((const struct tm *, const struct tm *));
static int
tm_diff (a, b)
     const struct tm *a;
     const struct tm *b;
{
  /* Compute intervening leap days correctly even if year is negative.
     Take care to avoid int overflow in leap day calculations,
     but it's OK to assume that A and B are close to each other.  */
  int a4 = (a->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (a->tm_year & 3);
  int b4 = (b->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (b->tm_year & 3);
  int a100 = a4 / 25 - (a4 % 25 < 0);
  int b100 = b4 / 25 - (b4 % 25 < 0);
  int a400 = a100 >> 2;
  int b400 = b100 >> 2;
  int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
  int years = a->tm_year - b->tm_year;
  int days = (365 * years + intervening_leap_days
          + (a->tm_yday - b->tm_yday));
  return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
        + (a->tm_min - b->tm_min))
      + (a->tm_sec - b->tm_sec));
}
#endif /* ! HAVE_TM_GMTOFF */



/* The number of days from the first day of the first ISO week of this
   year to the year day YDAY with week day WDAY.  ISO weeks start on
   Monday; the first ISO week has the year's first Thursday.  YDAY may
   be as small as YDAY_MINIMUM.  */
#define ISO_WEEK_START_WDAY 1 /* Monday */
#define ISO_WEEK1_WDAY 4 /* Thursday */
#define YDAY_MINIMUM (-366)
static int iso_week_days __P ((int, int));
#ifdef __GNUC__
__inline__
#endif
static int
iso_week_days (yday, wday)
     int yday;
     int wday;
{
  /* Add enough to the first operand of % to make it nonnegative.  */
  int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
  return (yday
      - (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
      + ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
}


#if !(defined _NL_CURRENT || HAVE_STRFTIME)
static CHAR_T const weekday_name[][10] =
  {
    L_("Sunday"), L_("Monday"), L_("Tuesday"), L_("Wednesday"),
    L_("Thursday"), L_("Friday"), L_("Saturday")
  };
static CHAR_T const month_name[][10] =
  {
    L_("January"), L_("February"), L_("March"), L_("April"), L_("May"),
    L_("June"), L_("July"), L_("August"), L_("September"), L_("October"),
    L_("November"), L_("December")
  };
#endif


/* When compiling this file, GNU applications can #define my_strftime
   to a symbol (typically nstrftime) to get an extended strftime with
   extra arguments UT and NS.  Emacs is a special case for now, but
   this Emacs-specific code can be removed once Emacs's config.h
   defines my_strftime.  */
#if defined emacs && !defined my_strftime
# define my_strftime nstrftime
#endif

#ifdef my_strftime
# define extra_args , ut, ns
# define extra_args_spec int ut; int ns;
# define extra_args_spec_iso , int ut, int ns
#else
# ifdef COMPILE_WIDE
#  define my_strftime wcsftime
#  define nl_get_alt_digit _nl_get_walt_digit
# else
#  define my_strftime strftime
#  define nl_get_alt_digit _nl_get_alt_digit
# endif
# define extra_args
# define extra_args_spec
# define extra_args_spec_iso
/* We don't have this information in general.  */
# define ut 0
# define ns 0
#endif

#if !defined _LIBC && HAVE_TZNAME && HAVE_TZSET
  /* Solaris 2.5 tzset sometimes modifies the storage returned by localtime.
     Work around this bug by copying *tp before it might be munged.  */
  size_t _strftime_copytm __P ((char *, size_t, const char *,
                    const struct tm * extra_args_spec_iso));
  size_t
  my_strftime (s, maxsize, format, tp extra_args)
      CHAR_T *s;
      size_t maxsize;
      const CHAR_T *format;
      const struct tm *tp;
      extra_args_spec
  {
    struct tm tmcopy;
    tmcopy = *tp;
    return _strftime_copytm (s, maxsize, format, &tmcopy extra_args);
  }
# undef my_strftime
# define my_strftime _strftime_copytm
#endif


/* Write information from TP into S according to the format
   string FORMAT, writing no more that MAXSIZE characters
   (including the terminating '\0') and returning number of
   characters written.  If S is NULL, nothing will be written
   anywhere, so to determine how many characters would be
   written, use NULL for S and (size_t) UINT_MAX for MAXSIZE.  */
size_t
my_strftime (s, maxsize, format, tp extra_args LOCALE_PARAM)
      CHAR_T *s;
      size_t maxsize;
      const CHAR_T *format;
      const struct tm *tp;
      extra_args_spec
      LOCALE_PARAM_DECL
{
#if defined _LIBC && defined USE_IN_EXTENDED_LOCALE_MODEL
  struct locale_data *const current = loc->__locales[LC_TIME];
#endif

  int hour12 = tp->tm_hour;
#ifdef _NL_CURRENT
  /* We cannot make the following values variables since we must delay
     the evaluation of these values until really needed since some
     expressions might not be valid in every situation.  The `struct tm'
     might be generated by a strptime() call that initialized
     only a few elements.  Dereference the pointers only if the format
     requires this.  Then it is ok to fail if the pointers are invalid.  */
# define a_wkday \
  ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABDAY_1) + tp->tm_wday))
# define f_wkday \
  ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(DAY_1) + tp->tm_wday))
# define a_month \
  ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ABMON_1) + tp->tm_mon))
# define f_month \
  ((const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(MON_1) + tp->tm_mon))
# define ampm \
  ((const CHAR_T *) _NL_CURRENT (LC_TIME, tp->tm_hour > 11            \
                 ? NLW(PM_STR) : NLW(AM_STR)))

# define aw_len STRLEN (a_wkday)
# define am_len STRLEN (a_month)
# define ap_len STRLEN (ampm)
#else
# if !HAVE_STRFTIME
#  define f_wkday (weekday_name[tp->tm_wday])
#  define f_month (month_name[tp->tm_mon])
#  define a_wkday f_wkday
#  define a_month f_month
#  define ampm (L_("AMPM") + 2 * (tp->tm_hour > 11))

  size_t aw_len = 3;
  size_t am_len = 3;
  size_t ap_len = 2;
# endif
#endif
  const char *zone;
  size_t i = 0;
  CHAR_T *p = s;
  const CHAR_T *f;
#if DO_MULTIBYTE && !defined COMPILE_WIDE
  const char *format_end = NULL;
#endif

  zone = NULL;
#if HAVE_TM_ZONE
  /* The POSIX test suite assumes that setting
     the environment variable TZ to a new value before calling strftime()
     will influence the result (the %Z format) even if the information in
     TP is computed with a totally different time zone.
     This is bogus: though POSIX allows bad behavior like this,
     POSIX does not require it.  Do the right thing instead.  */
  zone = (const char *) tp->tm_zone;
#endif
#if HAVE_TZNAME
  if (ut)
    {
      if (! (zone && *zone))
    zone = "GMT";
    }
  else
    {
      /* POSIX.1 requires that local time zone information be used as
     though strftime called tzset.  */
# if HAVE_TZSET
      tzset ();
# endif
    }
#endif

  if (hour12 > 12)
    hour12 -= 12;
  else
    if (hour12 == 0)
      hour12 = 12;

  for (f = format; *f != '\0'; ++f)
    {
      int pad = 0;      /* Padding for number ('-', '_', or 0).  */
      int modifier;     /* Field modifier ('E', 'O', or 0).  */
      int digits;       /* Max digits for numeric format.  */
      int number_value;     /* Numeric value to be printed.  */
      int negative_number;  /* 1 if the number is negative.  */
      const CHAR_T *subfmt;
      CHAR_T *bufp;
      CHAR_T buf[1 + (sizeof (int) < sizeof (time_t)
              ? INT_STRLEN_BOUND (time_t)
              : INT_STRLEN_BOUND (int))];
      int width = -1;
      int to_lowcase = 0;
      int to_uppcase = 0;
      int change_case = 0;
      int format_char;

#if DO_MULTIBYTE && !defined COMPILE_WIDE
      switch (*f)
    {
    case L_('%'):
      break;

    case L_('\b'): case L_('\t'): case L_('\n'):
    case L_('\v'): case L_('\f'): case L_('\r'):
    case L_(' '): case L_('!'): case L_('"'): case L_('#'): case L_('&'):
    case L_('\''): case L_('('): case L_(')'): case L_('*'): case L_('+'):
    case L_(','): case L_('-'): case L_('.'): case L_('/'): case L_('0'):
    case L_('1'): case L_('2'): case L_('3'): case L_('4'): case L_('5'):
    case L_('6'): case L_('7'): case L_('8'): case L_('9'): case L_(':'):
    case L_(';'): case L_('<'): case L_('='): case L_('>'): case L_('?'):
    case L_('A'): case L_('B'): case L_('C'): case L_('D'): case L_('E'):
    case L_('F'): case L_('G'): case L_('H'): case L_('I'): case L_('J'):
    case L_('K'): case L_('L'): case L_('M'): case L_('N'): case L_('O'):
    case L_('P'): case L_('Q'): case L_('R'): case L_('S'): case L_('T'):
    case L_('U'): case L_('V'): case L_('W'): case L_('X'): case L_('Y'):
    case L_('Z'): case L_('['): case L_('\\'): case L_(']'): case L_('^'):
    case L_('_'): case L_('a'): case L_('b'): case L_('c'): case L_('d'):
    case L_('e'): case L_('f'): case L_('g'): case L_('h'): case L_('i'):
    case L_('j'): case L_('k'): case L_('l'): case L_('m'): case L_('n'):
    case L_('o'): case L_('p'): case L_('q'): case L_('r'): case L_('s'):
    case L_('t'): case L_('u'): case L_('v'): case L_('w'): case L_('x'):
    case L_('y'): case L_('z'): case L_('{'): case L_('|'): case L_('}'):
    case L_('~'):
      /* The C Standard requires these 98 characters (plus '%') to
         be in the basic execution character set.  None of these
         characters can start a multibyte sequence, so they need
         not be analyzed further.  */
      add (1, *p = *f);
      continue;

    default:
      /* Copy this multibyte sequence until we reach its end, find
         an error, or come back to the initial shift state.  */
      {
        mbstate_t mbstate = mbstate_zero;
        size_t len = 0;
        size_t fsize;

        if (! format_end)
          format_end = f + strlen (f) + 1;
        fsize = format_end - f;

        do
          {
        size_t bytes = mbrlen (f + len, fsize - len, &mbstate);

        if (bytes == 0)
          break;

        if (bytes == (size_t) -2)
          {
            len += strlen (f + len);
            break;
          }

        if (bytes == (size_t) -1)
          {
            len++;
            break;
          }

        len += bytes;
          }
        while (! mbsinit (&mbstate));

        cpy (len, f);
        f += len - 1;
        continue;
      }
    }

#else /* ! DO_MULTIBYTE */

      /* Either multibyte encodings are not supported, they are
     safe for formats, so any non-'%' byte can be copied through,
     or this is the wide character version.  */
      if (*f != L_('%'))
    {
      add (1, *p = *f);
      continue;
    }

#endif /* ! DO_MULTIBYTE */

      /* Check for flags that can modify a format.  */
      while (1)
    {
      switch (*++f)
        {
          /* This influences the number formats.  */
        case L_('_'):
        case L_('-'):
        case L_('0'):
          pad = *f;
          continue;

          /* This changes textual output.  */
        case L_('^'):
          to_uppcase = 1;
          continue;
        case L_('#'):
          change_case = 1;
          continue;

        default:
          break;
        }
      break;
    }

      /* As a GNU extension we allow to specify the field width.  */
      if (ISDIGIT (*f))
    {
      width = 0;
      do
        {
          if (width > INT_MAX / 10
          || (width == INT_MAX / 10 && *f - L_('0') > INT_MAX % 10))
        /* Avoid overflow.  */
        width = INT_MAX;
          else
        {
          width *= 10;
          width += *f - L_('0');
        }
          ++f;
        }
      while (ISDIGIT (*f));
    }

      /* Check for modifiers.  */
      switch (*f)
    {
    case L_('E'):
    case L_('O'):
      modifier = *f++;
      break;

    default:
      modifier = 0;
      break;
    }

      /* Now do the specified format.  */
      format_char = *f;
      switch (format_char)
    {
#define DO_NUMBER(d, v) \
      digits = d > width ? d : width;                     \
      number_value = v; goto do_number
#define DO_NUMBER_SPACEPAD(d, v) \
      digits = d > width ? d : width;                     \
      number_value = v; goto do_number_spacepad

    case L_('%'):
      if (modifier != 0)
        goto bad_format;
      add (1, *p = *f);
      break;

    case L_('a'):
      if (modifier != 0)
        goto bad_format;
      if (change_case)
        {
          to_uppcase = 1;
          to_lowcase = 0;
        }
#if defined _NL_CURRENT || !HAVE_STRFTIME
      cpy (aw_len, a_wkday);
      break;
#else
      goto underlying_strftime;
#endif

    case 'A':
      if (modifier != 0)
        goto bad_format;
      if (change_case)
        {
          to_uppcase = 1;
          to_lowcase = 0;
        }
#if defined _NL_CURRENT || !HAVE_STRFTIME
      cpy (STRLEN (f_wkday), f_wkday);
      break;
#else
      goto underlying_strftime;
#endif

    case L_('b'):
    case L_('h'):
      if (change_case)
        {
          to_uppcase = 1;
          to_lowcase = 0;
        }
      if (modifier != 0)
        goto bad_format;
#if defined _NL_CURRENT || !HAVE_STRFTIME
      cpy (am_len, a_month);
      break;
#else
      goto underlying_strftime;
#endif

    case L_('B'):
      if (modifier != 0)
        goto bad_format;
      if (change_case)
        {
          to_uppcase = 1;
          to_lowcase = 0;
        }
#if defined _NL_CURRENT || !HAVE_STRFTIME
      cpy (STRLEN (f_month), f_month);
      break;
#else
      goto underlying_strftime;
#endif

    case L_('c'):
      if (modifier == L_('O'))
        goto bad_format;
#ifdef _NL_CURRENT
      if (! (modifier == 'E'
         && (*(subfmt =
               (const CHAR_T *) _NL_CURRENT (LC_TIME,
                             NLW(ERA_D_T_FMT)))
             != '\0')))
        subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_T_FMT));
#else
# if HAVE_STRFTIME
      goto underlying_strftime;
# else
      subfmt = L_("%a %b %e %H:%M:%S %Y");
# endif
#endif

    subformat:
      {
        CHAR_T *old_start = p;
        size_t len = my_strftime (NULL, (size_t) -1, subfmt,
                      tp extra_args LOCALE_ARG);
        add (len, my_strftime (p, maxsize - i, subfmt,
                   tp extra_args LOCALE_ARG));

        if (to_uppcase)
          while (old_start < p)
        {
          *old_start = TOUPPER ((UCHAR_T) *old_start, loc);
          ++old_start;
        }
      }
      break;

#if HAVE_STRFTIME && ! (defined _NL_CURRENT && HAVE_STRUCT_ERA_ENTRY)
    underlying_strftime:
      {
        /* The relevant information is available only via the
           underlying strftime implementation, so use that.  */
        char ufmt[4];
        char *u = ufmt;
        char ubuf[1024]; /* enough for any single format in practice */
        size_t len;
        /* Make sure we're calling the actual underlying strftime.
           In some cases, config.h contains something like
           "#define strftime rpl_strftime".  */
# ifdef strftime
#  undef strftime
        size_t strftime ();
# endif

        *u++ = '%';
        if (modifier != 0)
          *u++ = modifier;
        *u++ = format_char;
        *u = '\0';
        len = strftime (ubuf, sizeof ubuf, ufmt, tp);
        if (len == 0 && ubuf[0] != '\0')
          return 0;
        cpy (len, ubuf);
      }
      break;
#endif

    case L_('C'):
      if (modifier == L_('O'))
        goto bad_format;
      if (modifier == L_('E'))
        {
#if HAVE_STRUCT_ERA_ENTRY
          struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
          if (era)
        {
# ifdef COMPILE_WIDE
          size_t len = __wcslen (era->era_wname);
          cpy (len, era->era_wname);
# else
          size_t len = strlen (era->era_name);
          cpy (len, era->era_name);
# endif
          break;
        }
#else
# if HAVE_STRFTIME
          goto underlying_strftime;
# endif
#endif
        }

      {
        int year = tp->tm_year + TM_YEAR_BASE;
        DO_NUMBER (1, year / 100 - (year % 100 < 0));
      }

    case L_('x'):
      if (modifier == L_('O'))
        goto bad_format;
#ifdef _NL_CURRENT
      if (! (modifier == L_('E')
         && (*(subfmt =
               (const CHAR_T *)_NL_CURRENT (LC_TIME, NLW(ERA_D_FMT)))
             != L_('\0'))))
        subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(D_FMT));
      goto subformat;
#else
# if HAVE_STRFTIME
      goto underlying_strftime;
# else
      /* Fall through.  */
# endif
#endif
    case L_('D'):
      if (modifier != 0)
        goto bad_format;
      subfmt = L_("%m/%d/%y");
      goto subformat;

    case L_('d'):
      if (modifier == L_('E'))
        goto bad_format;

      DO_NUMBER (2, tp->tm_mday);

    case L_('e'):
      if (modifier == L_('E'))
        goto bad_format;

      DO_NUMBER_SPACEPAD (2, tp->tm_mday);

      /* All numeric formats set DIGITS and NUMBER_VALUE and then
         jump to one of these two labels.  */

    do_number_spacepad:
      /* Force `_' flag unless overwritten by `0' flag.  */
      if (pad != L_('0'))
        pad = L_('_');

    do_number:
      /* Format the number according to the MODIFIER flag.  */

      if (modifier == L_('O') && 0 <= number_value)
        {
#ifdef _NL_CURRENT
          /* Get the locale specific alternate representation of
         the number NUMBER_VALUE.  If none exist NULL is returned.  */
          const CHAR_T *cp = nl_get_alt_digit (number_value
                           HELPER_LOCALE_ARG);

          if (cp != NULL)
        {
          size_t digitlen = STRLEN (cp);
          if (digitlen != 0)
            {
              cpy (digitlen, cp);
              break;
            }
        }
#else
# if HAVE_STRFTIME
          goto underlying_strftime;
# endif
#endif
        }
      {
        unsigned int u = number_value;

        bufp = buf + sizeof (buf) / sizeof (buf[0]);
        negative_number = number_value < 0;

        if (negative_number)
          u = -u;

        do
          *--bufp = u % 10 + L_('0');
        while ((u /= 10) != 0);
      }

    do_number_sign_and_padding:
      if (negative_number)
        *--bufp = L_('-');

      if (pad != L_('-'))
        {
          int padding = digits - (buf + (sizeof (buf) / sizeof (buf[0]))
                      - bufp);

          if (padding > 0)
        {
          if (pad == L_('_'))
            {
              if ((size_t) padding >= maxsize - i)
            return 0;

              if (p)
            memset_space (p, padding);
              i += padding;
              width = width > padding ? width - padding : 0;
            }
          else
            {
              if ((size_t) digits >= maxsize - i)
            return 0;

              if (negative_number)
            {
              ++bufp;

              if (p)
                *p++ = L_('-');
              ++i;
            }

              if (p)
            memset_zero (p, padding);
              i += padding;
              width = 0;
            }
        }
        }

      cpy (buf + sizeof (buf) / sizeof (buf[0]) - bufp, bufp);
      break;

    case L_('F'):
      if (modifier != 0)
        goto bad_format;
      subfmt = L_("%Y-%m-%d");
      goto subformat;

    case L_('H'):
      if (modifier == L_('E'))
        goto bad_format;

      DO_NUMBER (2, tp->tm_hour);

    case L_('I'):
      if (modifier == L_('E'))
        goto bad_format;

      DO_NUMBER (2, hour12);

    case L_('k'):       /* GNU extension.  */
      if (modifier == L_('E'))
        goto bad_format;

      DO_NUMBER_SPACEPAD (2, tp->tm_hour);

    case L_('l'):       /* GNU extension.  */
      if (modifier == L_('E'))
        goto bad_format;

      DO_NUMBER_SPACEPAD (2, hour12);

    case L_('j'):
      if (modifier == L_('E'))
        goto bad_format;

      DO_NUMBER (3, 1 + tp->tm_yday);

    case L_('M'):
      if (modifier == L_('E'))
        goto bad_format;

      DO_NUMBER (2, tp->tm_min);

    case L_('m'):
      if (modifier == L_('E'))
        goto bad_format;

      DO_NUMBER (2, tp->tm_mon + 1);

#ifndef _LIBC
    case L_('N'):       /* GNU extension.  */
      if (modifier == L_('E'))
        goto bad_format;

      number_value = ns;
      if (width != -1)
        {
          /* Take an explicit width less than 9 as a precision.  */
          int j;
          for (j = width; j < 9; j++)
        number_value /= 10;
        }

      DO_NUMBER (9, number_value);
#endif

    case L_('n'):
      add (1, *p = L_('\n'));
      break;

    case L_('P'):
      to_lowcase = 1;
#if !defined _NL_CURRENT && HAVE_STRFTIME
      format_char = L_('p');
#endif
      /* FALLTHROUGH */

    case L_('p'):
      if (change_case)
        {
          to_uppcase = 0;
          to_lowcase = 1;
        }
#if defined _NL_CURRENT || !HAVE_STRFTIME
      cpy (ap_len, ampm);
      break;
#else
      goto underlying_strftime;
#endif

    case L_('R'):
      subfmt = L_("%H:%M");
      goto subformat;

    case L_('r'):
#ifdef _NL_CURRENT
      if (*(subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME,
                               NLW(T_FMT_AMPM)))
          == L_('\0'))
#endif
        subfmt = L_("%I:%M:%S %p");
      goto subformat;

    case L_('S'):
      if (modifier == L_('E'))
        goto bad_format;

      DO_NUMBER (2, tp->tm_sec);

    case L_('s'):       /* GNU extension.  */
      {
        struct tm ltm;
        time_t t;

        ltm = *tp;
        t = mktime (&ltm);

        /* Generate string value for T using time_t arithmetic;
           this works even if sizeof (long) < sizeof (time_t).  */

        bufp = buf + sizeof (buf) / sizeof (buf[0]);
        negative_number = t < 0;

        do
          {
        int d = t % 10;
        t /= 10;

        if (negative_number)
          {
            d = -d;

            /* Adjust if division truncates to minus infinity.  */
            if (0 < -1 % 10 && d < 0)
              {
            t++;
            d += 10;
              }
          }

        *--bufp = d + L_('0');
          }
        while (t != 0);

        digits = 1;
        goto do_number_sign_and_padding;
      }

    case L_('X'):
      if (modifier == L_('O'))
        goto bad_format;
#ifdef _NL_CURRENT
      if (! (modifier == L_('E')
         && (*(subfmt =
               (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(ERA_T_FMT)))
             != L_('\0'))))
        subfmt = (const CHAR_T *) _NL_CURRENT (LC_TIME, NLW(T_FMT));
      goto subformat;
#else
# if HAVE_STRFTIME
      goto underlying_strftime;
# else
      /* Fall through.  */
# endif
#endif
    case L_('T'):
      subfmt = L_("%H:%M:%S");
      goto subformat;

    case L_('t'):
      add (1, *p = L_('\t'));
      break;

    case L_('u'):
      DO_NUMBER (1, (tp->tm_wday - 1 + 7) % 7 + 1);

    case L_('U'):
      if (modifier == L_('E'))
        goto bad_format;

      DO_NUMBER (2, (tp->tm_yday - tp->tm_wday + 7) / 7);

    case L_('V'):
    case L_('g'):
    case L_('G'):
      if (modifier == L_('E'))
        goto bad_format;
      {
        int year = tp->tm_year + TM_YEAR_BASE;
        int days = iso_week_days (tp->tm_yday, tp->tm_wday);

        if (days < 0)
          {
        /* This ISO week belongs to the previous year.  */
        year--;
        days = iso_week_days (tp->tm_yday + (365 + __isleap (year)),
                      tp->tm_wday);
          }
        else
          {
        int d = iso_week_days (tp->tm_yday - (365 + __isleap (year)),
                       tp->tm_wday);
        if (0 <= d)
          {
            /* This ISO week belongs to the next year.  */
            year++;
            days = d;
          }
          }

        switch (*f)
          {
          case L_('g'):
        DO_NUMBER (2, (year % 100 + 100) % 100);

          case L_('G'):
        DO_NUMBER (1, year);

          default:
        DO_NUMBER (2, days / 7 + 1);
          }
      }

    case L_('W'):
      if (modifier == L_('E'))
        goto bad_format;

      DO_NUMBER (2, (tp->tm_yday - (tp->tm_wday - 1 + 7) % 7 + 7) / 7);

    case L_('w'):
      if (modifier == L_('E'))
        goto bad_format;

      DO_NUMBER (1, tp->tm_wday);

    case L_('Y'):
      if (modifier == 'E')
        {
#if HAVE_STRUCT_ERA_ENTRY
          struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
          if (era)
        {
# ifdef COMPILE_WIDE
          subfmt = era->era_wformat;
# else
          subfmt = era->era_format;
# endif
          goto subformat;
        }
#else
# if HAVE_STRFTIME
          goto underlying_strftime;
# endif
#endif
        }
      if (modifier == L_('O'))
        goto bad_format;
      else
        DO_NUMBER (1, tp->tm_year + TM_YEAR_BASE);

    case L_('y'):
      if (modifier == L_('E'))
        {
#if HAVE_STRUCT_ERA_ENTRY
          struct era_entry *era = _nl_get_era_entry (tp HELPER_LOCALE_ARG);
          if (era)
        {
          int delta = tp->tm_year - era->start_date[0];
          DO_NUMBER (1, (era->offset
                 + delta * era->absolute_direction));
        }
#else
# if HAVE_STRFTIME
          goto underlying_strftime;
# endif
#endif
        }
      DO_NUMBER (2, (tp->tm_year % 100 + 100) % 100);

    case L_('Z'):
      if (change_case)
        {
          to_uppcase = 0;
          to_lowcase = 1;
        }

#if HAVE_TZNAME
      /* The tzset() call might have changed the value.  */
      if (!(zone && *zone) && tp->tm_isdst >= 0)
        zone = tzname[tp->tm_isdst];
#endif
      if (! zone)
        zone = "";

#ifdef COMPILE_WIDE
      {
        /* The zone string is always given in multibyte form.  We have
           to transform it first.  */
        wchar_t *wczone;
        size_t len;
        widen (zone, wczone, len);
        cpy (len, wczone);
      }
#else
      cpy (strlen (zone), zone);
#endif
      break;

    case L_('z'):
      if (tp->tm_isdst < 0)
        break;

      {
        int diff;
#if HAVE_TM_GMTOFF
        diff = tp->tm_gmtoff;
#else
        if (ut)
          diff = 0;
        else
          {
        struct tm gtm;
        struct tm ltm;
        time_t lt;

        ltm = *tp;
        lt = mktime (&ltm);

        if (lt == (time_t) -1)
          {
            /* mktime returns -1 for errors, but -1 is also a
               valid time_t value.  Check whether an error really
               occurred.  */
            struct tm tm;

            if (! my_strftime_localtime_r (&lt, &tm)
            || ((ltm.tm_sec ^ tm.tm_sec)
                | (ltm.tm_min ^ tm.tm_min)
                | (ltm.tm_hour ^ tm.tm_hour)
                | (ltm.tm_mday ^ tm.tm_mday)
                | (ltm.tm_mon ^ tm.tm_mon)
                | (ltm.tm_year ^ tm.tm_year)))
              break;
          }

        if (! my_strftime_gmtime_r (&lt, &gtm))
          break;

        diff = tm_diff (&ltm, &gtm);
          }
#endif

        if (diff < 0)
          {
        add (1, *p = L_('-'));
        diff = -diff;
          }
        else
          add (1, *p = L_('+'));

        diff /= 60;
        DO_NUMBER (4, (diff / 60) * 100 + diff % 60);
      }

    case L_('\0'):      /* GNU extension: % at end of format.  */
        --f;
        /* Fall through.  */
    default:
      /* Unknown format; output the format, including the '%',
         since this is most likely the right thing to do if a
         multibyte string has been misparsed.  */
    bad_format:
      {
        int flen;
        for (flen = 1; f[1 - flen] != L_('%'); flen++)
          continue;
        cpy (flen, &f[1 - flen]);
      }
      break;
    }
    }

  if (p && maxsize != 0)
    *p = L_('\0');
  return i;
}
#ifdef _LIBC
libc_hidden_def (my_strftime)
#endif


#ifdef emacs
/* For Emacs we have a separate interface which corresponds to the normal
   strftime function plus the ut argument, but without the ns argument.  */
size_t
emacs_strftimeu (s, maxsize, format, tp, ut)
      char *s;
      size_t maxsize;
      const char *format;
      const struct tm *tp;
      int ut;
{
  return my_strftime (s, maxsize, format, tp, ut, 0);
}
#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getopt.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getopt.c used in: Step 755
/* Getopt for GNU.
   NOTE: getopt is now part of the C library, so if you don't know what
   "Keep this file name-space clean" means, talk to drepper@gnu.org
   before changing it!
   Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001,2002
    Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */


/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
   Ditto for AIX 3.2 and <stdlib.h>.  */
#ifndef _NO_PROTO
# define _NO_PROTO
#endif

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#if !defined __STDC__ || !__STDC__
/* This is a separate conditional since some stdc systems
   reject `defined (const)'.  */
# ifndef const
#  define const
# endif
#endif

#include <stdio.h>

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#define GETOPT_INTERFACE_VERSION 2
#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
# include <gnu-versions.h>
# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
#  define ELIDE_CODE
# endif
#endif

#ifndef ELIDE_CODE


/* This needs to come after some library #include
   to get __GNU_LIBRARY__ defined.  */
#ifdef  __GNU_LIBRARY__
/* Don't include stdlib.h for non-GNU C libraries because some of them
   contain conflicting prototypes for getopt.  */
# include <stdlib.h>
# include <unistd.h>
#endif  /* GNU C library.  */

#ifdef VMS
# include <unixlib.h>
# if HAVE_STRING_H - 0
#  include <string.h>
# endif
#endif

#ifdef _LIBC
# include <libintl.h>
#else
/* This is for other GNU distributions with internationalized messages.  */
# include "gettext.h"
#endif
#define _(msgid) gettext (msgid)

#if defined _LIBC && defined USE_IN_LIBIO
# include <wchar.h>
#endif

#ifndef attribute_hidden
# define attribute_hidden
#endif

/* This version of `getopt' appears to the caller like standard Unix `getopt'
   but it behaves differently for the user, since it allows the user
   to intersperse the options with the other arguments.

   As `getopt' works, it permutes the elements of ARGV so that,
   when it is done, all the options precede everything else.  Thus
   all application programs are extended to handle flexible argument order.

   Setting the environment variable POSIXLY_CORRECT disables permutation.
   Then the behavior is completely standard.

   GNU application programs can use a third alternative mode in which
   they can distinguish the relative order of options and other arguments.  */

#include "getopt.h"

/* For communication from `getopt' to the caller.
   When `getopt' finds an option that takes an argument,
   the argument value is returned here.
   Also, when `ordering' is RETURN_IN_ORDER,
   each non-option ARGV-element is returned here.  */

char *optarg;

/* Index in ARGV of the next element to be scanned.
   This is used for communication to and from the caller
   and for communication between successive calls to `getopt'.

   On entry to `getopt', zero means this is the first call; initialize.

   When `getopt' returns -1, this is the index of the first of the
   non-option elements that the caller should itself scan.

   Otherwise, `optind' communicates from one call to the next
   how much of ARGV has been scanned so far.  */

/* 1003.2 says this must be 1 before any call.  */
int optind = 1;

/* Formerly, initialization of getopt depended on optind==0, which
   causes problems with re-calling getopt as programs generally don't
   know that. */

int __getopt_initialized attribute_hidden;

/* The next char to be scanned in the option-element
   in which the last option character we returned was found.
   This allows us to pick up the scan where we left off.

   If this is zero, or a null string, it means resume the scan
   by advancing to the next ARGV-element.  */

static char *nextchar;

/* Callers store zero here to inhibit the error message
   for unrecognized options.  */

int opterr = 1;

/* Set to an option character which was unrecognized.
   This must be initialized on some systems to avoid linking in the
   system's own getopt implementation.  */

int optopt = '?';

/* Describe how to deal with options that follow non-option ARGV-elements.

   If the caller did not specify anything,
   the default is REQUIRE_ORDER if the environment variable
   POSIXLY_CORRECT is defined, PERMUTE otherwise.

   REQUIRE_ORDER means don't recognize them as options;
   stop option processing when the first non-option is seen.
   This is what Unix does.
   This mode of operation is selected by either setting the environment
   variable POSIXLY_CORRECT, or using `+' as the first character
   of the list of option characters.

   PERMUTE is the default.  We permute the contents of ARGV as we scan,
   so that eventually all the non-options are at the end.  This allows options
   to be given in any order, even with programs that were not written to
   expect this.

   RETURN_IN_ORDER is an option available to programs that were written
   to expect options and other ARGV-elements in any order and that care about
   the ordering of the two.  We describe each non-option ARGV-element
   as if it were the argument of an option with character code 1.
   Using `-' as the first character of the list of option characters
   selects this mode of operation.

   The special argument `--' forces an end of option-scanning regardless
   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */

static enum
{
  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
} ordering;

/* Value of POSIXLY_CORRECT environment variable.  */
static char *posixly_correct;


#ifdef  __GNU_LIBRARY__
/* We want to avoid inclusion of string.h with non-GNU libraries
   because there are many ways it can cause trouble.
   On some systems, it contains special magic macros that don't work
   in GCC.  */
# include <string.h>
# define my_index   strchr
#else

# if HAVE_STRING_H
#  include <string.h>
# else
#  include <strings.h>
# endif

/* Avoid depending on library functions or files
   whose names are inconsistent.  */

#ifndef getenv
extern char *getenv ();
#endif

static char *
my_index (str, chr)
     const char *str;
     int chr;
{
  while (*str)
    {
      if (*str == chr)
    return (char *) str;
      str++;
    }
  return 0;
}

/* If using GCC, we can safely declare strlen this way.
   If not using GCC, it is ok not to declare it.  */
#ifdef __GNUC__
/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
   That was relevant to code that was here before.  */
# if (!defined __STDC__ || !__STDC__) && !defined strlen
/* gcc with -traditional declares the built-in strlen to return int,
   and has done so at least since version 2.4.5. -- rms.  */
extern int strlen (const char *);
# endif /* not __STDC__ */
#endif /* __GNUC__ */

#endif /* not __GNU_LIBRARY__ */


/* Handle permutation of arguments.  */

/* Describe the part of ARGV that contains non-options that have
   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
   `last_nonopt' is the index after the last of them.  */

static int first_nonopt;
static int last_nonopt;

#ifdef _LIBC
/* Stored original parameters.
   XXX This is no good solution.  We should rather copy the args so
   that we can compare them later.  But we must not use malloc(3).  */
extern int __libc_argc;
extern char **__libc_argv;

/* Bash 2.0 gives us an environment variable containing flags
   indicating ARGV elements that should not be considered arguments.  */

# ifdef USE_NONOPTION_FLAGS
/* Defined in getopt_init.c  */
extern char *__getopt_nonoption_flags;

static int nonoption_flags_max_len;
static int nonoption_flags_len;
# endif

# ifdef USE_NONOPTION_FLAGS
#  define SWAP_FLAGS(ch1, ch2) \
  if (nonoption_flags_len > 0)                            \
    {                                         \
      char __tmp = __getopt_nonoption_flags[ch1];                 \
      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];          \
      __getopt_nonoption_flags[ch2] = __tmp;                      \
    }
# else
#  define SWAP_FLAGS(ch1, ch2)
# endif
#else   /* !_LIBC */
# define SWAP_FLAGS(ch1, ch2)
#endif  /* _LIBC */

/* Exchange two adjacent subsequences of ARGV.
   One subsequence is elements [first_nonopt,last_nonopt)
   which contains all the non-options that have been skipped so far.
   The other is elements [last_nonopt,optind), which contains all
   the options processed since those non-options were skipped.

   `first_nonopt' and `last_nonopt' are relocated so that they describe
   the new indices of the non-options in ARGV after they are moved.  */

#if defined __STDC__ && __STDC__
static void exchange (char **);
#endif

static void
exchange (argv)
     char **argv;
{
  int bottom = first_nonopt;
  int middle = last_nonopt;
  int top = optind;
  char *tem;

  /* Exchange the shorter segment with the far end of the longer segment.
     That puts the shorter segment into the right place.
     It leaves the longer segment in the right place overall,
     but it consists of two parts that need to be swapped next.  */

#if defined _LIBC && defined USE_NONOPTION_FLAGS
  /* First make sure the handling of the `__getopt_nonoption_flags'
     string can work normally.  Our top argument must be in the range
     of the string.  */
  if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
    {
      /* We must extend the array.  The user plays games with us and
     presents new arguments.  */
      char *new_str = malloc (top + 1);
      if (new_str == NULL)
    nonoption_flags_len = nonoption_flags_max_len = 0;
      else
    {
      memset (__mempcpy (new_str, __getopt_nonoption_flags,
                 nonoption_flags_max_len),
          '\0', top + 1 - nonoption_flags_max_len);
      nonoption_flags_max_len = top + 1;
      __getopt_nonoption_flags = new_str;
    }
    }
#endif

  while (top > middle && middle > bottom)
    {
      if (top - middle > middle - bottom)
    {
      /* Bottom segment is the short one.  */
      int len = middle - bottom;
      register int i;

      /* Swap it with the top part of the top segment.  */
      for (i = 0; i < len; i++)
        {
          tem = argv[bottom + i];
          argv[bottom + i] = argv[top - (middle - bottom) + i];
          argv[top - (middle - bottom) + i] = tem;
          SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
        }
      /* Exclude the moved bottom segment from further swapping.  */
      top -= len;
    }
      else
    {
      /* Top segment is the short one.  */
      int len = top - middle;
      register int i;

      /* Swap it with the bottom part of the bottom segment.  */
      for (i = 0; i < len; i++)
        {
          tem = argv[bottom + i];
          argv[bottom + i] = argv[middle + i];
          argv[middle + i] = tem;
          SWAP_FLAGS (bottom + i, middle + i);
        }
      /* Exclude the moved top segment from further swapping.  */
      bottom += len;
    }
    }

  /* Update records for the slots the non-options now occupy.  */

  first_nonopt += (optind - last_nonopt);
  last_nonopt = optind;
}

/* Initialize the internal data when the first call is made.  */

#if defined __STDC__ && __STDC__
static const char *_getopt_initialize (int, char *const *, const char *);
#endif
static const char *
_getopt_initialize (argc, argv, optstring)
     int argc;
     char *const *argv;
     const char *optstring;
{
  /* Start processing options with ARGV-element 1 (since ARGV-element 0
     is the program name); the sequence of previously skipped
     non-option ARGV-elements is empty.  */

  first_nonopt = last_nonopt = optind;

  nextchar = NULL;

  posixly_correct = getenv ("POSIXLY_CORRECT");

  /* Determine how to handle the ordering of options and nonoptions.  */

  if (optstring[0] == '-')
    {
      ordering = RETURN_IN_ORDER;
      ++optstring;
    }
  else if (optstring[0] == '+')
    {
      ordering = REQUIRE_ORDER;
      ++optstring;
    }
  else if (posixly_correct != NULL)
    ordering = REQUIRE_ORDER;
  else
    ordering = PERMUTE;

#if defined _LIBC && defined USE_NONOPTION_FLAGS
  if (posixly_correct == NULL
      && argc == __libc_argc && argv == __libc_argv)
    {
      if (nonoption_flags_max_len == 0)
    {
      if (__getopt_nonoption_flags == NULL
          || __getopt_nonoption_flags[0] == '\0')
        nonoption_flags_max_len = -1;
      else
        {
          const char *orig_str = __getopt_nonoption_flags;
          int len = nonoption_flags_max_len = strlen (orig_str);
          if (nonoption_flags_max_len < argc)
        nonoption_flags_max_len = argc;
          __getopt_nonoption_flags =
        (char *) malloc (nonoption_flags_max_len);
          if (__getopt_nonoption_flags == NULL)
        nonoption_flags_max_len = -1;
          else
        memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
            '\0', nonoption_flags_max_len - len);
        }
    }
      nonoption_flags_len = nonoption_flags_max_len;
    }
  else
    nonoption_flags_len = 0;
#endif

  return optstring;
}


/* Scan elements of ARGV (whose length is ARGC) for option characters
   given in OPTSTRING.

   If an element of ARGV starts with '-', and is not exactly "-" or "--",
   then it is an option element.  The characters of this element
   (aside from the initial '-') are option characters.  If `getopt'
   is called repeatedly, it returns successively each of the option characters
   from each of the option elements.

   If `getopt' finds another option character, it returns that character,
   updating `optind' and `nextchar' so that the next call to `getopt' can
   resume the scan with the following option character or ARGV-element.

   If there are no more option characters, `getopt' returns -1.
   Then `optind' is the index in ARGV of the first ARGV-element
   that is not an option.  (The ARGV-elements have been permuted
   so that those that are not options now come last.)

   OPTSTRING is a string containing the legitimate option characters.
   If an option character is seen that is not listed in OPTSTRING,
   return '?' after printing an error message.  If you set `opterr' to
   zero, the error message is suppressed but we still return '?'.

   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
   so the following text in the same ARGV-element, or the text of the following
   ARGV-element, is returned in `optarg'.  Two colons mean an option that
   wants an optional arg; if there is text in the current ARGV-element,
   it is returned in `optarg', otherwise `optarg' is set to zero.

   If OPTSTRING starts with `-' or `+', it requests different methods of
   handling the non-option ARGV-elements.
   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.

   Long-named options begin with `--' instead of `-'.
   Their names may be abbreviated as long as the abbreviation is unique
   or is an exact match for some defined option.  If they have an
   argument, it follows the option name in the same ARGV-element, separated
   from the option name by a `=', or else the in next ARGV-element.
   When `getopt' finds a long-named option, it returns 0 if that option's
   `flag' field is nonzero, the value of the option's `val' field
   if the `flag' field is zero.

   The elements of ARGV aren't really const, because we permute them.
   But we pretend they're const in the prototype to be compatible
   with other systems.

   LONGOPTS is a vector of `struct option' terminated by an
   element containing a name which is zero.

   LONGIND returns the index in LONGOPT of the long-named option found.
   It is only valid when a long-named option has been found by the most
   recent call.

   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
   long-named options.  */

int
_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
     int argc;
     char *const *argv;
     const char *optstring;
     const struct option *longopts;
     int *longind;
     int long_only;
{
  int print_errors = opterr;
  if (optstring[0] == ':')
    print_errors = 0;

  if (argc < 1)
    return -1;

  optarg = NULL;

  if (optind == 0 || !__getopt_initialized)
    {
      if (optind == 0)
    optind = 1; /* Don't scan ARGV[0], the program name.  */
      optstring = _getopt_initialize (argc, argv, optstring);
      __getopt_initialized = 1;
    }

  /* Test whether ARGV[optind] points to a non-option argument.
     Either it does not have option syntax, or there is an environment flag
     from the shell indicating it is not an option.  The later information
     is only used when the used in the GNU libc.  */
#if defined _LIBC && defined USE_NONOPTION_FLAGS
# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'       \
              || (optind < nonoption_flags_len                \
              && __getopt_nonoption_flags[optind] == '1'))
#else
# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
#endif

  if (nextchar == NULL || *nextchar == '\0')
    {
      /* Advance to the next ARGV-element.  */

      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
     moved back by the user (who may also have changed the arguments).  */
      if (last_nonopt > optind)
    last_nonopt = optind;
      if (first_nonopt > optind)
    first_nonopt = optind;

      if (ordering == PERMUTE)
    {
      /* If we have just processed some options following some non-options,
         exchange them so that the options come first.  */

      if (first_nonopt != last_nonopt && last_nonopt != optind)
        exchange ((char **) argv);
      else if (last_nonopt != optind)
        first_nonopt = optind;

      /* Skip any additional non-options
         and extend the range of non-options previously skipped.  */

      while (optind < argc && NONOPTION_P)
        optind++;
      last_nonopt = optind;
    }

      /* The special ARGV-element `--' means premature end of options.
     Skip it like a null option,
     then exchange with previous non-options as if it were an option,
     then skip everything else like a non-option.  */

      if (optind != argc && !strcmp (argv[optind], "--"))
    {
      optind++;

      if (first_nonopt != last_nonopt && last_nonopt != optind)
        exchange ((char **) argv);
      else if (first_nonopt == last_nonopt)
        first_nonopt = optind;
      last_nonopt = argc;

      optind = argc;
    }

      /* If we have done all the ARGV-elements, stop the scan
     and back over any non-options that we skipped and permuted.  */

      if (optind == argc)
    {
      /* Set the next-arg-index to point at the non-options
         that we previously skipped, so the caller will digest them.  */
      if (first_nonopt != last_nonopt)
        optind = first_nonopt;
      return -1;
    }

      /* If we have come to a non-option and did not permute it,
     either stop the scan or describe it to the caller and pass it by.  */

      if (NONOPTION_P)
    {
      if (ordering == REQUIRE_ORDER)
        return -1;
      optarg = argv[optind++];
      return 1;
    }

      /* We have found another option-ARGV-element.
     Skip the initial punctuation.  */

      nextchar = (argv[optind] + 1
          + (longopts != NULL && argv[optind][1] == '-'));
    }

  /* Decode the current option-ARGV-element.  */

  /* Check whether the ARGV-element is a long option.

     If long_only and the ARGV-element has the form "-f", where f is
     a valid short option, don't consider it an abbreviated form of
     a long option that starts with f.  Otherwise there would be no
     way to give the -f short option.

     On the other hand, if there's a long option "fubar" and
     the ARGV-element is "-fu", do consider that an abbreviation of
     the long option, just like "--fu", and not "-f" with arg "u".

     This distinction seems to be the most useful approach.  */

  if (longopts != NULL
      && (argv[optind][1] == '-'
      || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
    {
      char *nameend;
      const struct option *p;
      const struct option *pfound = NULL;
      int exact = 0;
      int ambig = 0;
      int indfound = -1;
      int option_index;

      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
    /* Do nothing.  */ ;

      /* Test all long options for either exact match
     or abbreviated matches.  */
      for (p = longopts, option_index = 0; p->name; p++, option_index++)
    if (!strncmp (p->name, nextchar, nameend - nextchar))
      {
        if ((unsigned int) (nameend - nextchar)
        == (unsigned int) strlen (p->name))
          {
        /* Exact match found.  */
        pfound = p;
        indfound = option_index;
        exact = 1;
        break;
          }
        else if (pfound == NULL)
          {
        /* First nonexact match found.  */
        pfound = p;
        indfound = option_index;
          }
        else if (long_only
             || pfound->has_arg != p->has_arg
             || pfound->flag != p->flag
             || pfound->val != p->val)
          /* Second or later nonexact match found.  */
          ambig = 1;
      }

      if (ambig && !exact)
    {
      if (print_errors)
        {
#if defined _LIBC && defined USE_IN_LIBIO
          char *buf;

          if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"),
                  argv[0], argv[optind]) >= 0)
        {

          if (_IO_fwide (stderr, 0) > 0)
            __fwprintf (stderr, L"%s", buf);
          else
            fputs (buf, stderr);

          free (buf);
        }
#else
          fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
               argv[0], argv[optind]);
#endif
        }
      nextchar += strlen (nextchar);
      optind++;
      optopt = 0;
      return '?';
    }

      if (pfound != NULL)
    {
      option_index = indfound;
      optind++;
      if (*nameend)
        {
          /* Don't test has_arg with >, because some C compilers don't
         allow it to be used on enums.  */
          if (pfound->has_arg)
        optarg = nameend + 1;
          else
        {
          if (print_errors)
            {
#if defined _LIBC && defined USE_IN_LIBIO
              char *buf;
              int n;
#endif

              if (argv[optind - 1][1] == '-')
            {
              /* --option */
#if defined _LIBC && defined USE_IN_LIBIO
              n = __asprintf (&buf, _("\
%s: option `--%s' doesn't allow an argument\n"),
                      argv[0], pfound->name);
#else
              fprintf (stderr, _("\
%s: option `--%s' doesn't allow an argument\n"),
                   argv[0], pfound->name);
#endif
            }
              else
            {
              /* +option or -option */
#if defined _LIBC && defined USE_IN_LIBIO
              n = __asprintf (&buf, _("\
%s: option `%c%s' doesn't allow an argument\n"),
                      argv[0], argv[optind - 1][0],
                      pfound->name);
#else
              fprintf (stderr, _("\
%s: option `%c%s' doesn't allow an argument\n"),
                   argv[0], argv[optind - 1][0], pfound->name);
#endif
            }

#if defined _LIBC && defined USE_IN_LIBIO
              if (n >= 0)
            {
              if (_IO_fwide (stderr, 0) > 0)
                __fwprintf (stderr, L"%s", buf);
              else
                fputs (buf, stderr);

              free (buf);
            }
#endif
            }

          nextchar += strlen (nextchar);

          optopt = pfound->val;
          return '?';
        }
        }
      else if (pfound->has_arg == 1)
        {
          if (optind < argc)
        optarg = argv[optind++];
          else
        {
          if (print_errors)
            {
#if defined _LIBC && defined USE_IN_LIBIO
              char *buf;

              if (__asprintf (&buf, _("\
%s: option `%s' requires an argument\n"),
                      argv[0], argv[optind - 1]) >= 0)
            {
              if (_IO_fwide (stderr, 0) > 0)
                __fwprintf (stderr, L"%s", buf);
              else
                fputs (buf, stderr);

              free (buf);
            }
#else
              fprintf (stderr,
                   _("%s: option `%s' requires an argument\n"),
                   argv[0], argv[optind - 1]);
#endif
            }
          nextchar += strlen (nextchar);
          optopt = pfound->val;
          return optstring[0] == ':' ? ':' : '?';
        }
        }
      nextchar += strlen (nextchar);
      if (longind != NULL)
        *longind = option_index;
      if (pfound->flag)
        {
          *(pfound->flag) = pfound->val;
          return 0;
        }
      return pfound->val;
    }

      /* Can't find it as a long option.  If this is not getopt_long_only,
     or the option starts with '--' or is not a valid short
     option, then it's an error.
     Otherwise interpret it as a short option.  */
      if (!long_only || argv[optind][1] == '-'
      || my_index (optstring, *nextchar) == NULL)
    {
      if (print_errors)
        {
#if defined _LIBC && defined USE_IN_LIBIO
          char *buf;
          int n;
#endif

          if (argv[optind][1] == '-')
        {
          /* --option */
#if defined _LIBC && defined USE_IN_LIBIO
          n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"),
                  argv[0], nextchar);
#else
          fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
               argv[0], nextchar);
#endif
        }
          else
        {
          /* +option or -option */
#if defined _LIBC && defined USE_IN_LIBIO
          n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"),
                  argv[0], argv[optind][0], nextchar);
#else
          fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
               argv[0], argv[optind][0], nextchar);
#endif
        }

#if defined _LIBC && defined USE_IN_LIBIO
          if (n >= 0)
        {
          if (_IO_fwide (stderr, 0) > 0)
            __fwprintf (stderr, L"%s", buf);
          else
            fputs (buf, stderr);

          free (buf);
        }
#endif
        }
      nextchar = (char *) "";
      optind++;
      optopt = 0;
      return '?';
    }
    }

  /* Look at and handle the next short option-character.  */

  {
    char c = *nextchar++;
    char *temp = my_index (optstring, c);

    /* Increment `optind' when we start to process its last character.  */
    if (*nextchar == '\0')
      ++optind;

    if (temp == NULL || c == ':')
      {
    if (print_errors)
      {
#if defined _LIBC && defined USE_IN_LIBIO
          char *buf;
          int n;
#endif

        if (posixly_correct)
          {
        /* 1003.2 specifies the format of this message.  */
#if defined _LIBC && defined USE_IN_LIBIO
        n = __asprintf (&buf, _("%s: illegal option -- %c\n"),
                argv[0], c);
#else
        fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c);
#endif
          }
        else
          {
#if defined _LIBC && defined USE_IN_LIBIO
        n = __asprintf (&buf, _("%s: invalid option -- %c\n"),
                argv[0], c);
#else
        fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c);
#endif
          }

#if defined _LIBC && defined USE_IN_LIBIO
        if (n >= 0)
          {
        if (_IO_fwide (stderr, 0) > 0)
          __fwprintf (stderr, L"%s", buf);
        else
          fputs (buf, stderr);

        free (buf);
          }
#endif
      }
    optopt = c;
    return '?';
      }
    /* Convenience. Treat POSIX -W foo same as long option --foo */
    if (temp[0] == 'W' && temp[1] == ';')
      {
    char *nameend;
    const struct option *p;
    const struct option *pfound = NULL;
    int exact = 0;
    int ambig = 0;
    int indfound = 0;
    int option_index;

    /* This is an option that requires an argument.  */
    if (*nextchar != '\0')
      {
        optarg = nextchar;
        /* If we end this ARGV-element by taking the rest as an arg,
           we must advance to the next element now.  */
        optind++;
      }
    else if (optind == argc)
      {
        if (print_errors)
          {
        /* 1003.2 specifies the format of this message.  */
#if defined _LIBC && defined USE_IN_LIBIO
        char *buf;

        if (__asprintf (&buf,
                _("%s: option requires an argument -- %c\n"),
                argv[0], c) >= 0)
          {
            if (_IO_fwide (stderr, 0) > 0)
              __fwprintf (stderr, L"%s", buf);
            else
              fputs (buf, stderr);

            free (buf);
          }
#else
        fprintf (stderr, _("%s: option requires an argument -- %c\n"),
             argv[0], c);
#endif
          }
        optopt = c;
        if (optstring[0] == ':')
          c = ':';
        else
          c = '?';
        return c;
      }
    else
      /* We already incremented `optind' once;
         increment it again when taking next ARGV-elt as argument.  */
      optarg = argv[optind++];

    /* optarg is now the argument, see if it's in the
       table of longopts.  */

    for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
      /* Do nothing.  */ ;

    /* Test all long options for either exact match
       or abbreviated matches.  */
    for (p = longopts, option_index = 0; p->name; p++, option_index++)
      if (!strncmp (p->name, nextchar, nameend - nextchar))
        {
          if ((unsigned int) (nameend - nextchar) == strlen (p->name))
        {
          /* Exact match found.  */
          pfound = p;
          indfound = option_index;
          exact = 1;
          break;
        }
          else if (pfound == NULL)
        {
          /* First nonexact match found.  */
          pfound = p;
          indfound = option_index;
        }
          else
        /* Second or later nonexact match found.  */
        ambig = 1;
        }
    if (ambig && !exact)
      {
        if (print_errors)
          {
#if defined _LIBC && defined USE_IN_LIBIO
        char *buf;

        if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"),
                argv[0], argv[optind]) >= 0)
          {
            if (_IO_fwide (stderr, 0) > 0)
              __fwprintf (stderr, L"%s", buf);
            else
              fputs (buf, stderr);

            free (buf);
          }
#else
        fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
             argv[0], argv[optind]);
#endif
          }
        nextchar += strlen (nextchar);
        optind++;
        return '?';
      }
    if (pfound != NULL)
      {
        option_index = indfound;
        if (*nameend)
          {
        /* Don't test has_arg with >, because some C compilers don't
           allow it to be used on enums.  */
        if (pfound->has_arg)
          optarg = nameend + 1;
        else
          {
            if (print_errors)
              {
#if defined _LIBC && defined USE_IN_LIBIO
            char *buf;

            if (__asprintf (&buf, _("\
%s: option `-W %s' doesn't allow an argument\n"),
                    argv[0], pfound->name) >= 0)
              {
                if (_IO_fwide (stderr, 0) > 0)
                  __fwprintf (stderr, L"%s", buf);
                else
                  fputs (buf, stderr);

                free (buf);
              }
#else
            fprintf (stderr, _("\
%s: option `-W %s' doesn't allow an argument\n"),
                 argv[0], pfound->name);
#endif
              }

            nextchar += strlen (nextchar);
            return '?';
          }
          }
        else if (pfound->has_arg == 1)
          {
        if (optind < argc)
          optarg = argv[optind++];
        else
          {
            if (print_errors)
              {
#if defined _LIBC && defined USE_IN_LIBIO
            char *buf;

            if (__asprintf (&buf, _("\
%s: option `%s' requires an argument\n"),
                    argv[0], argv[optind - 1]) >= 0)
              {
                if (_IO_fwide (stderr, 0) > 0)
                  __fwprintf (stderr, L"%s", buf);
                else
                  fputs (buf, stderr);

                free (buf);
              }
#else
            fprintf (stderr,
                 _("%s: option `%s' requires an argument\n"),
                 argv[0], argv[optind - 1]);
#endif
              }
            nextchar += strlen (nextchar);
            return optstring[0] == ':' ? ':' : '?';
          }
          }
        nextchar += strlen (nextchar);
        if (longind != NULL)
          *longind = option_index;
        if (pfound->flag)
          {
        *(pfound->flag) = pfound->val;
        return 0;
          }
        return pfound->val;
      }
      nextchar = NULL;
      return 'W';   /* Let the application handle it.   */
      }
    if (temp[1] == ':')
      {
    if (temp[2] == ':')
      {
        /* This is an option that accepts an argument optionally.  */
        if (*nextchar != '\0')
          {
        optarg = nextchar;
        optind++;
          }
        else
          optarg = NULL;
        nextchar = NULL;
      }
    else
      {
        /* This is an option that requires an argument.  */
        if (*nextchar != '\0')
          {
        optarg = nextchar;
        /* If we end this ARGV-element by taking the rest as an arg,
           we must advance to the next element now.  */
        optind++;
          }
        else if (optind == argc)
          {
        if (print_errors)
          {
            /* 1003.2 specifies the format of this message.  */
#if defined _LIBC && defined USE_IN_LIBIO
            char *buf;

            if (__asprintf (&buf, _("\
%s: option requires an argument -- %c\n"),
                    argv[0], c) >= 0)
              {
            if (_IO_fwide (stderr, 0) > 0)
              __fwprintf (stderr, L"%s", buf);
            else
              fputs (buf, stderr);

            free (buf);
              }
#else
            fprintf (stderr,
                 _("%s: option requires an argument -- %c\n"),
                 argv[0], c);
#endif
          }
        optopt = c;
        if (optstring[0] == ':')
          c = ':';
        else
          c = '?';
          }
        else
          /* We already incremented `optind' once;
         increment it again when taking next ARGV-elt as argument.  */
          optarg = argv[optind++];
        nextchar = NULL;
      }
      }
    return c;
  }
}

int
getopt (argc, argv, optstring)
     int argc;
     char *const *argv;
     const char *optstring;
{
  return _getopt_internal (argc, argv, optstring,
               (const struct option *) 0,
               (int *) 0,
               0);
}

#endif  /* Not ELIDE_CODE.  */


#ifdef TEST

/* Compile with -DTEST to make an executable for use in testing
   the above definition of `getopt'.  */

int
main (argc, argv)
     int argc;
     char **argv;
{
  int c;
  int digit_optind = 0;

  while (1)
    {
      int this_option_optind = optind ? optind : 1;

      c = getopt (argc, argv, "abc:d:0123456789");
      if (c == -1)
    break;

      switch (c)
    {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (digit_optind != 0 && digit_optind != this_option_optind)
        printf ("digits occur in two different argv-elements.\n");
      digit_optind = this_option_optind;
      printf ("option %c\n", c);
      break;

    case 'a':
      printf ("option a\n");
      break;

    case 'b':
      printf ("option b\n");
      break;

    case 'c':
      printf ("option c with value `%s'\n", optarg);
      break;

    case '?':
      break;

    default:
      printf ("?? getopt returned character code 0%o ??\n", c);
    }
    }

  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
    printf ("%s ", argv[optind++]);
      printf ("\n");
    }

  exit (0);
}

#endif /* TEST */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getopt1.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getopt1.c used in: Step 756
/* getopt_long and getopt_long_only entry points for GNU getopt.
   Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
     Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef _LIBC
# include <getopt.h>
#else
# include "getopt.h"
#endif

#if !defined __STDC__ || !__STDC__
/* This is a separate conditional since some stdc systems
   reject `defined (const)'.  */
#ifndef const
#define const
#endif
#endif

#include <stdio.h>

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#define GETOPT_INTERFACE_VERSION 2
#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
#include <gnu-versions.h>
#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
#define ELIDE_CODE
#endif
#endif

#ifndef ELIDE_CODE


/* This needs to come after some library #include
   to get __GNU_LIBRARY__ defined.  */
#ifdef __GNU_LIBRARY__
#include <stdlib.h>
#endif

#ifndef NULL
#define NULL 0
#endif

int
getopt_long (argc, argv, options, long_options, opt_index)
     int argc;
     char *const *argv;
     const char *options;
     const struct option *long_options;
     int *opt_index;
{
  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
}

/* Like getopt_long, but '-' as well as '--' can indicate a long option.
   If an option that starts with '-' (not '--') doesn't match a long option,
   but does match a short option, it is parsed as a short option
   instead.  */

int
getopt_long_only (argc, argv, options, long_options, opt_index)
     int argc;
     char *const *argv;
     const char *options;
     const struct option *long_options;
     int *opt_index;
{
  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
}

# ifdef _LIBC
libc_hidden_def (getopt_long)
libc_hidden_def (getopt_long_only)
# endif

#endif  /* Not ELIDE_CODE.  */


#ifdef TEST

#include <stdio.h>

int
main (argc, argv)
     int argc;
     char **argv;
{
  int c;
  int digit_optind = 0;

  while (1)
    {
      int this_option_optind = optind ? optind : 1;
      int option_index = 0;
      static struct option long_options[] =
      {
    {"add", 1, 0, 0},
    {"append", 0, 0, 0},
    {"delete", 1, 0, 0},
    {"verbose", 0, 0, 0},
    {"create", 0, 0, 0},
    {"file", 1, 0, 0},
    {0, 0, 0, 0}
      };

      c = getopt_long (argc, argv, "abc:d:0123456789",
               long_options, &option_index);
      if (c == -1)
    break;

      switch (c)
    {
    case 0:
      printf ("option %s", long_options[option_index].name);
      if (optarg)
        printf (" with arg %s", optarg);
      printf ("\n");
      break;

    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (digit_optind != 0 && digit_optind != this_option_optind)
        printf ("digits occur in two different argv-elements.\n");
      digit_optind = this_option_optind;
      printf ("option %c\n", c);
      break;

    case 'a':
      printf ("option a\n");
      break;

    case 'b':
      printf ("option b\n");
      break;

    case 'c':
      printf ("option c with value `%s'\n", optarg);
      break;

    case 'd':
      printf ("option d with value `%s'\n", optarg);
      break;

    case '?':
      break;

    default:
      printf ("?? getopt returned character code 0%o ??\n", c);
    }
    }

  if (optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
    printf ("%s ", argv[optind++]);
      printf ("\n");
    }

  exit (0);
}

#endif /* TEST */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/hash.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/hash.c used in: Step 757
/* hash - hashing table processing.
   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
   Written by Jim Meyering, 1992.

   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.  */

/* A generic hash table package.  */

/* Define USE_OBSTACK to 1 if you want the allocator to use obstacks instead
   of malloc.  If you change USE_OBSTACK, you have to recompile!  */

#if HAVE_CONFIG_H
# include <config.h>
#endif
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#if HAVE_STDBOOL_H
# include <stdbool.h>
#else
typedef enum {false = 0, true = 1} bool;
#endif
#include <stdio.h>
#include <assert.h>

#ifndef HAVE_DECL_FREE
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_FREE
void free ();
#endif

#ifndef HAVE_DECL_MALLOC
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_MALLOC
char *malloc ();
#endif

#if USE_OBSTACK
# include "obstack.h"
# ifndef obstack_chunk_alloc
#  define obstack_chunk_alloc malloc
# endif
# ifndef obstack_chunk_free
#  define obstack_chunk_free free
# endif
#endif

#include "hash.h"

struct hash_table
  {
    /* The array of buckets starts at BUCKET and extends to BUCKET_LIMIT-1,
       for a possibility of N_BUCKETS.  Among those, N_BUCKETS_USED buckets
       are not empty, there are N_ENTRIES active entries in the table.  */
    struct hash_entry *bucket;
    struct hash_entry *bucket_limit;
    unsigned n_buckets;
    unsigned n_buckets_used;
    unsigned n_entries;

    /* Tuning arguments, kept in a physicaly separate structure.  */
    const Hash_tuning *tuning;

    /* Three functions are given to `hash_initialize', see the documentation
       block for this function.  In a word, HASHER randomizes a user entry
       into a number up from 0 up to some maximum minus 1; COMPARATOR returns
       true if two user entries compare equally; and DATA_FREER is the cleanup
       function for a user entry.  */
    Hash_hasher hasher;
    Hash_comparator comparator;
    Hash_data_freer data_freer;

    /* A linked list of freed struct hash_entry structs.  */
    struct hash_entry *free_entry_list;

#if USE_OBSTACK
    /* Whenever obstacks are used, it is possible to allocate all overflowed
       entries into a single stack, so they all can be freed in a single
       operation.  It is not clear if the speedup is worth the trouble.  */
    struct obstack entry_stack;
#endif
  };

/* A hash table contains many internal entries, each holding a pointer to
   some user provided data (also called a user entry).  An entry indistinctly
   refers to both the internal entry and its associated user entry.  A user
   entry contents may be hashed by a randomization function (the hashing
   function, or just `hasher' for short) into a number (or `slot') between 0
   and the current table size.  At each slot position in the hash table,
   starts a linked chain of entries for which the user data all hash to this
   slot.  A bucket is the collection of all entries hashing to the same slot.

   A good `hasher' function will distribute entries rather evenly in buckets.
   In the ideal case, the length of each bucket is roughly the number of
   entries divided by the table size.  Finding the slot for a data is usually
   done in constant time by the `hasher', and the later finding of a precise
   entry is linear in time with the size of the bucket.  Consequently, a
   larger hash table size (that is, a larger number of buckets) is prone to
   yielding shorter chains, *given* the `hasher' function behaves properly.

   Long buckets slow down the lookup algorithm.  One might use big hash table
   sizes in hope to reduce the average length of buckets, but this might
   become inordinate, as unused slots in the hash table take some space.  The
   best bet is to make sure you are using a good `hasher' function (beware
   that those are not that easy to write! :-), and to use a table size
   larger than the actual number of entries.  */

/* If an insertion makes the ratio of nonempty buckets to table size larger
   than the growth threshold (a number between 0.0 and 1.0), then increase
   the table size by multiplying by the growth factor (a number greater than
   1.0).  The growth threshold defaults to 0.8, and the growth factor
   defaults to 1.414, meaning that the table will have doubled its size
   every second time 80% of the buckets get used.  */
#define DEFAULT_GROWTH_THRESHOLD 0.8
#define DEFAULT_GROWTH_FACTOR 1.414

/* If a deletion empties a bucket and causes the ratio of used buckets to
   table size to become smaller than the shrink threshold (a number between
   0.0 and 1.0), then shrink the table by multiplying by the shrink factor (a
   number greater than the shrink threshold but smaller than 1.0).  The shrink
   threshold and factor default to 0.0 and 1.0, meaning that the table never
   shrinks.  */
#define DEFAULT_SHRINK_THRESHOLD 0.0
#define DEFAULT_SHRINK_FACTOR 1.0

/* Use this to initialize or reset a TUNING structure to
   some sensible values. */
static const Hash_tuning default_tuning =
  {
    DEFAULT_SHRINK_THRESHOLD,
    DEFAULT_SHRINK_FACTOR,
    DEFAULT_GROWTH_THRESHOLD,
    DEFAULT_GROWTH_FACTOR,
    false
  };

/* Information and lookup.  */

/* The following few functions provide information about the overall hash
   table organization: the number of entries, number of buckets and maximum
   length of buckets.  */

/* Return the number of buckets in the hash table.  The table size, the total
   number of buckets (used plus unused), or the maximum number of slots, are
   the same quantity.  */

unsigned
hash_get_n_buckets (const Hash_table *table)
{
  return table->n_buckets;
}

/* Return the number of slots in use (non-empty buckets).  */

unsigned
hash_get_n_buckets_used (const Hash_table *table)
{
  return table->n_buckets_used;
}

/* Return the number of active entries.  */

unsigned
hash_get_n_entries (const Hash_table *table)
{
  return table->n_entries;
}

/* Return the length of the longest chain (bucket).  */

unsigned
hash_get_max_bucket_length (const Hash_table *table)
{
  struct hash_entry *bucket;
  unsigned max_bucket_length = 0;

  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
    {
      if (bucket->data)
    {
      struct hash_entry *cursor = bucket;
      unsigned bucket_length = 1;

      while (cursor = cursor->next, cursor)
        bucket_length++;

      if (bucket_length > max_bucket_length)
        max_bucket_length = bucket_length;
    }
    }

  return max_bucket_length;
}

/* Do a mild validation of a hash table, by traversing it and checking two
   statistics.  */

bool
hash_table_ok (const Hash_table *table)
{
  struct hash_entry *bucket;
  unsigned n_buckets_used = 0;
  unsigned n_entries = 0;

  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
    {
      if (bucket->data)
    {
      struct hash_entry *cursor = bucket;

      /* Count bucket head.  */
      n_buckets_used++;
      n_entries++;

      /* Count bucket overflow.  */
      while (cursor = cursor->next, cursor)
        n_entries++;
    }
    }

  if (n_buckets_used == table->n_buckets_used && n_entries == table->n_entries)
    return true;

  return false;
}

void
hash_print_statistics (const Hash_table *table, FILE *stream)
{
  unsigned n_entries = hash_get_n_entries (table);
  unsigned n_buckets = hash_get_n_buckets (table);
  unsigned n_buckets_used = hash_get_n_buckets_used (table);
  unsigned max_bucket_length = hash_get_max_bucket_length (table);

  fprintf (stream, "# entries:         %u\n", n_entries);
  fprintf (stream, "# buckets:         %u\n", n_buckets);
  fprintf (stream, "# buckets used:    %u (%.2f%%)\n", n_buckets_used,
       (100.0 * n_buckets_used) / n_buckets);
  fprintf (stream, "max bucket length: %u\n", max_bucket_length);
}

/* If ENTRY matches an entry already in the hash table, return the
   entry from the table.  Otherwise, return NULL.  */

void *
hash_lookup (const Hash_table *table, const void *entry)
{
  struct hash_entry *bucket
    = table->bucket + table->hasher (entry, table->n_buckets);
  struct hash_entry *cursor;

  assert (bucket < table->bucket_limit);

  if (bucket->data == NULL)
    return NULL;

  for (cursor = bucket; cursor; cursor = cursor->next)
    if (table->comparator (entry, cursor->data))
      return cursor->data;

  return NULL;
}

/* Walking.  */

/* The functions in this page traverse the hash table and process the
   contained entries.  For the traversal to work properly, the hash table
   should not be resized nor modified while any particular entry is being
   processed.  In particular, entries should not be added or removed.  */

/* Return the first data in the table, or NULL if the table is empty.  */

void *
hash_get_first (const Hash_table *table)
{
  struct hash_entry *bucket;

  if (table->n_entries == 0)
    return NULL;

  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
    if (bucket->data)
      return bucket->data;

  assert (0);
  return NULL;
}

/* Return the user data for the entry following ENTRY, where ENTRY has been
   returned by a previous call to either `hash_get_first' or `hash_get_next'.
   Return NULL if there are no more entries.  */

void *
hash_get_next (const Hash_table *table, const void *entry)
{
  struct hash_entry *bucket
    = table->bucket + table->hasher (entry, table->n_buckets);
  struct hash_entry *cursor;

  assert (bucket < table->bucket_limit);

  /* Find next entry in the same bucket.  */
  for (cursor = bucket; cursor; cursor = cursor->next)
    if (cursor->data == entry && cursor->next)
      return cursor->next->data;

  /* Find first entry in any subsequent bucket.  */
  while (++bucket < table->bucket_limit)
    if (bucket->data)
      return bucket->data;

  /* None found.  */
  return NULL;
}

/* Fill BUFFER with pointers to active user entries in the hash table, then
   return the number of pointers copied.  Do not copy more than BUFFER_SIZE
   pointers.  */

unsigned
hash_get_entries (const Hash_table *table, void **buffer,
          unsigned buffer_size)
{
  unsigned counter = 0;
  struct hash_entry *bucket;
  struct hash_entry *cursor;

  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
    {
      if (bucket->data)
    {
      for (cursor = bucket; cursor; cursor = cursor->next)
        {
          if (counter >= buffer_size)
        return counter;
          buffer[counter++] = cursor->data;
        }
    }
    }

  return counter;
}

/* Call a PROCESSOR function for each entry of a hash table, and return the
   number of entries for which the processor function returned success.  A
   pointer to some PROCESSOR_DATA which will be made available to each call to
   the processor function.  The PROCESSOR accepts two arguments: the first is
   the user entry being walked into, the second is the value of PROCESSOR_DATA
   as received.  The walking continue for as long as the PROCESSOR function
   returns nonzero.  When it returns zero, the walking is interrupted.  */

unsigned
hash_do_for_each (const Hash_table *table, Hash_processor processor,
          void *processor_data)
{
  unsigned counter = 0;
  struct hash_entry *bucket;
  struct hash_entry *cursor;

  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
    {
      if (bucket->data)
    {
      for (cursor = bucket; cursor; cursor = cursor->next)
        {
          if (!(*processor) (cursor->data, processor_data))
        return counter;
          counter++;
        }
    }
    }

  return counter;
}

/* Allocation and clean-up.  */

/* Return a hash index for a NUL-terminated STRING between 0 and N_BUCKETS-1.
   This is a convenience routine for constructing other hashing functions.  */

#if USE_DIFF_HASH

/* About hashings, Paul Eggert writes to me (FP), on 1994-01-01: "Please see
   B. J. McKenzie, R. Harries & T. Bell, Selecting a hashing algorithm,
   Software--practice & experience 20, 2 (Feb 1990), 209-224.  Good hash
   algorithms tend to be domain-specific, so what's good for [diffutils'] io.c
   may not be good for your application."  */

unsigned
hash_string (const char *string, unsigned n_buckets)
{
# ifndef CHAR_BIT
#  define CHAR_BIT 8
# endif
# define ROTATE_LEFT(Value, Shift) \
  ((Value) << (Shift) | (Value) >> ((sizeof (unsigned) * CHAR_BIT) - (Shift)))
# define HASH_ONE_CHAR(Value, Byte) \
  ((Byte) + ROTATE_LEFT (Value, 7))

  unsigned value = 0;

  for (; *string; string++)
    value = HASH_ONE_CHAR (value, *(const unsigned char *) string);
  return value % n_buckets;

# undef ROTATE_LEFT
# undef HASH_ONE_CHAR
}

#else /* not USE_DIFF_HASH */

/* This one comes from `recode', and performs a bit better than the above as
   per a few experiments.  It is inspired from a hashing routine found in the
   very old Cyber `snoop', itself written in typical Greg Mansfield style.
   (By the way, what happened to this excellent man?  Is he still alive?)  */

unsigned
hash_string (const char *string, unsigned n_buckets)
{
  unsigned value = 0;

  while (*string)
    value = ((value * 31 + (int) *(const unsigned char *) string++)
         % n_buckets);
  return value;
}

#endif /* not USE_DIFF_HASH */

/* Return true if CANDIDATE is a prime number.  CANDIDATE should be an odd
   number at least equal to 11.  */

static bool
is_prime (unsigned long candidate)
{
  unsigned long divisor = 3;
  unsigned long square = divisor * divisor;

  while (square < candidate && (candidate % divisor))
    {
      divisor++;
      square += 4 * divisor;
      divisor++;
    }

  return (candidate % divisor ? true : false);
}

/* Round a given CANDIDATE number up to the nearest prime, and return that
   prime.  Primes lower than 10 are merely skipped.  */

static unsigned long
next_prime (unsigned long candidate)
{
  /* Skip small primes.  */
  if (candidate < 10)
    candidate = 10;

  /* Make it definitely odd.  */
  candidate |= 1;

  while (!is_prime (candidate))
    candidate += 2;

  return candidate;
}

void
hash_reset_tuning (Hash_tuning *tuning)
{
  *tuning = default_tuning;
}

/* For the given hash TABLE, check the user supplied tuning structure for
   reasonable values, and return true if there is no gross error with it.
   Otherwise, definitively reset the TUNING field to some acceptable default
   in the hash table (that is, the user loses the right of further modifying
   tuning arguments), and return false.  */

static bool
check_tuning (Hash_table *table)
{
  const Hash_tuning *tuning = table->tuning;

  if (tuning->growth_threshold > 0.0
      && tuning->growth_threshold < 1.0
      && tuning->growth_factor > 1.0
      && tuning->shrink_threshold >= 0.0
      && tuning->shrink_threshold < 1.0
      && tuning->shrink_factor > tuning->shrink_threshold
      && tuning->shrink_factor <= 1.0
      && tuning->shrink_threshold < tuning->growth_threshold)
    return true;

  table->tuning = &default_tuning;
  return false;
}

/* Allocate and return a new hash table, or NULL upon failure.  The initial
   number of buckets is automatically selected so as to _guarantee_ that you
   may insert at least CANDIDATE different user entries before any growth of
   the hash table size occurs.  So, if have a reasonably tight a-priori upper
   bound on the number of entries you intend to insert in the hash table, you
   may save some table memory and insertion time, by specifying it here.  If
   the IS_N_BUCKETS field of the TUNING structure is true, the CANDIDATE
   argument has its meaning changed to the wanted number of buckets.

   TUNING points to a structure of user-supplied values, in case some fine
   tuning is wanted over the default behavior of the hasher.  If TUNING is
   NULL, the default tuning parameters are used instead.

   The user-supplied HASHER function should be provided.  It accepts two
   arguments ENTRY and TABLE_SIZE.  It computes, by hashing ENTRY contents, a
   slot number for that entry which should be in the range 0..TABLE_SIZE-1.
   This slot number is then returned.

   The user-supplied COMPARATOR function should be provided.  It accepts two
   arguments pointing to user data, it then returns true for a pair of entries
   that compare equal, or false otherwise.  This function is internally called
   on entries which are already known to hash to the same bucket index.

   The user-supplied DATA_FREER function, when not NULL, may be later called
   with the user data as an argument, just before the entry containing the
   data gets freed.  This happens from within `hash_free' or `hash_clear'.
   You should specify this function only if you want these functions to free
   all of your `data' data.  This is typically the case when your data is
   simply an auxiliary struct that you have malloc'd to aggregate several
   values.  */

Hash_table *
hash_initialize (unsigned candidate, const Hash_tuning *tuning,
         Hash_hasher hasher, Hash_comparator comparator,
         Hash_data_freer data_freer)
{
  Hash_table *table;
  struct hash_entry *bucket;

  if (hasher == NULL || comparator == NULL)
    return NULL;

  table = (Hash_table *) malloc (sizeof (Hash_table));
  if (table == NULL)
    return NULL;

  if (!tuning)
    tuning = &default_tuning;
  table->tuning = tuning;
  if (!check_tuning (table))
    {
      /* Fail if the tuning options are invalid.  This is the only occasion
     when the user gets some feedback about it.  Once the table is created,
     if the user provides invalid tuning options, we silently revert to
     using the defaults, and ignore further request to change the tuning
     options.  */
      free (table);
      return NULL;
    }

  table->n_buckets
    = next_prime (tuning->is_n_buckets ? candidate
          : (unsigned) (candidate / tuning->growth_threshold));

  table->bucket = (struct hash_entry *)
    malloc (table->n_buckets * sizeof (struct hash_entry));
  if (table->bucket == NULL)
    {
      free (table);
      return NULL;
    }
  table->bucket_limit = table->bucket + table->n_buckets;

  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
    {
      bucket->data = NULL;
      bucket->next = NULL;
    }
  table->n_buckets_used = 0;
  table->n_entries = 0;

  table->hasher = hasher;
  table->comparator = comparator;
  table->data_freer = data_freer;

  table->free_entry_list = NULL;
#if USE_OBSTACK
  obstack_init (&table->entry_stack);
#endif
  return table;
}

/* Make all buckets empty, placing any chained entries on the free list.
   Apply the user-specified function data_freer (if any) to the datas of any
   affected entries.  */

void
hash_clear (Hash_table *table)
{
  struct hash_entry *bucket;

  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
    {
      if (bucket->data)
    {
      struct hash_entry *cursor;
      struct hash_entry *next;

      /* Free the bucket overflow.  */
      for (cursor = bucket->next; cursor; cursor = next)
        {
          if (table->data_freer)
        (*table->data_freer) (cursor->data);
          cursor->data = NULL;

          next = cursor->next;
          /* Relinking is done one entry at a time, as it is to be expected
         that overflows are either rare or short.  */
          cursor->next = table->free_entry_list;
          table->free_entry_list = cursor;
        }

      /* Free the bucket head.  */
      if (table->data_freer)
        (*table->data_freer) (bucket->data);
      bucket->data = NULL;
      bucket->next = NULL;
    }
    }

  table->n_buckets_used = 0;
  table->n_entries = 0;
}

/* Reclaim all storage associated with a hash table.  If a data_freer
   function has been supplied by the user when the hash table was created,
   this function applies it to the data of each entry before freeing that
   entry.  */

void
hash_free (Hash_table *table)
{
  struct hash_entry *bucket;
  struct hash_entry *cursor;
  struct hash_entry *next;

  /* Call the user data_freer function.  */
  if (table->data_freer && table->n_entries)
    {
      for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
    {
      if (bucket->data)
        {
          for (cursor = bucket; cursor; cursor = cursor->next)
        {
          (*table->data_freer) (cursor->data);
        }
        }
    }
    }

#if USE_OBSTACK

  obstack_free (&table->entry_stack, NULL);

#else

  /* Free all bucket overflowed entries.  */
  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
    {
      for (cursor = bucket->next; cursor; cursor = next)
    {
      next = cursor->next;
      free (cursor);
    }
    }

  /* Also reclaim the internal list of previously freed entries.  */
  for (cursor = table->free_entry_list; cursor; cursor = next)
    {
      next = cursor->next;
      free (cursor);
    }

#endif

  /* Free the remainder of the hash table structure.  */
  free (table->bucket);
  free (table);
}

/* Insertion and deletion.  */

/* Get a new hash entry for a bucket overflow, possibly by reclying a
   previously freed one.  If this is not possible, allocate a new one.  */

static struct hash_entry *
allocate_entry (Hash_table *table)
{
  struct hash_entry *new;

  if (table->free_entry_list)
    {
      new = table->free_entry_list;
      table->free_entry_list = new->next;
    }
  else
    {
#if USE_OBSTACK
      new = (struct hash_entry *)
    obstack_alloc (&table->entry_stack, sizeof (struct hash_entry));
#else
      new = (struct hash_entry *) malloc (sizeof (struct hash_entry));
#endif
    }

  return new;
}

/* Free a hash entry which was part of some bucket overflow,
   saving it for later recycling.  */

static void
free_entry (Hash_table *table, struct hash_entry *entry)
{
  entry->data = NULL;
  entry->next = table->free_entry_list;
  table->free_entry_list = entry;
}

/* This private function is used to help with insertion and deletion.  When
   ENTRY matches an entry in the table, return a pointer to the corresponding
   user data and set *BUCKET_HEAD to the head of the selected bucket.
   Otherwise, return NULL.  When DELETE is true and ENTRY matches an entry in
   the table, unlink the matching entry.  */

static void *
hash_find_entry (Hash_table *table, const void *entry,
         struct hash_entry **bucket_head, bool delete)
{
  struct hash_entry *bucket
    = table->bucket + table->hasher (entry, table->n_buckets);
  struct hash_entry *cursor;

  assert (bucket < table->bucket_limit);
  *bucket_head = bucket;

  /* Test for empty bucket.  */
  if (bucket->data == NULL)
    return NULL;

  /* See if the entry is the first in the bucket.  */
  if ((*table->comparator) (entry, bucket->data))
    {
      void *data = bucket->data;

      if (delete)
    {
      if (bucket->next)
        {
          struct hash_entry *next = bucket->next;

          /* Bump the first overflow entry into the bucket head, then save
         the previous first overflow entry for later recycling.  */
          *bucket = *next;
          free_entry (table, next);
        }
      else
        {
          bucket->data = NULL;
        }
    }

      return data;
    }

  /* Scan the bucket overflow.  */
  for (cursor = bucket; cursor->next; cursor = cursor->next)
    {
      if ((*table->comparator) (entry, cursor->next->data))
    {
      void *data = cursor->next->data;

      if (delete)
        {
          struct hash_entry *next = cursor->next;

          /* Unlink the entry to delete, then save the freed entry for later
         recycling.  */
          cursor->next = next->next;
          free_entry (table, next);
        }

      return data;
    }
    }

  /* No entry found.  */
  return NULL;
}

/* For an already existing hash table, change the number of buckets through
   specifying CANDIDATE.  The contents of the hash table are preserved.  The
   new number of buckets is automatically selected so as to _guarantee_ that
   the table may receive at least CANDIDATE different user entries, including
   those already in the table, before any other growth of the hash table size
   occurs.  If TUNING->IS_N_BUCKETS is true, then CANDIDATE specifies the
   exact number of buckets desired.  */

bool
hash_rehash (Hash_table *table, unsigned candidate)
{
  Hash_table *new_table;
  struct hash_entry *bucket;
  struct hash_entry *cursor;
  struct hash_entry *next;

  new_table = hash_initialize (candidate, table->tuning, table->hasher,
                   table->comparator, table->data_freer);
  if (new_table == NULL)
    return false;

  /* Merely reuse the extra old space into the new table.  */
#if USE_OBSTACK
  obstack_free (&new_table->entry_stack, NULL);
  new_table->entry_stack = table->entry_stack;
#endif
  new_table->free_entry_list = table->free_entry_list;

  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
    if (bucket->data)
      for (cursor = bucket; cursor; cursor = next)
    {
      void *data = cursor->data;
      struct hash_entry *new_bucket
        = (new_table->bucket
           + new_table->hasher (data, new_table->n_buckets));

      assert (new_bucket < new_table->bucket_limit);
      next = cursor->next;

      if (new_bucket->data)
        {
          if (cursor == bucket)
        {
          /* Allocate or recycle an entry, when moving from a bucket
             header into a bucket overflow.  */
          struct hash_entry *new_entry = allocate_entry (new_table);

          if (new_entry == NULL)
            return false;

          new_entry->data = data;
          new_entry->next = new_bucket->next;
          new_bucket->next = new_entry;
        }
          else
        {
          /* Merely relink an existing entry, when moving from a
             bucket overflow into a bucket overflow.  */
          cursor->next = new_bucket->next;
          new_bucket->next = cursor;
        }
        }
      else
        {
          /* Free an existing entry, when moving from a bucket
         overflow into a bucket header.  Also take care of the
         simple case of moving from a bucket header into a bucket
         header.  */
          new_bucket->data = data;
          new_table->n_buckets_used++;
          if (cursor != bucket)
        free_entry (new_table, cursor);
        }
    }

  free (table->bucket);
  table->bucket = new_table->bucket;
  table->bucket_limit = new_table->bucket_limit;
  table->n_buckets = new_table->n_buckets;
  table->n_buckets_used = new_table->n_buckets_used;
  table->free_entry_list = new_table->free_entry_list;
  /* table->n_entries already holds its value.  */
#if USE_OBSTACK
  table->entry_stack = new_table->entry_stack;
#endif
  free (new_table);

  return true;
}

/* If ENTRY matches an entry already in the hash table, return the pointer
   to the entry from the table.  Otherwise, insert ENTRY and return ENTRY.
   Return NULL if the storage required for insertion cannot be allocated.  */

void *
hash_insert (Hash_table *table, const void *entry)
{
  void *data;
  struct hash_entry *bucket;

  assert (entry);       /* cannot insert a NULL entry */

  /* If there's a matching entry already in the table, return that.  */
  if ((data = hash_find_entry (table, entry, &bucket, false)) != NULL)
    return data;

  /* ENTRY is not matched, it should be inserted.  */

  if (bucket->data)
    {
      struct hash_entry *new_entry = allocate_entry (table);

      if (new_entry == NULL)
    return NULL;

      /* Add ENTRY in the overflow of the bucket.  */

      new_entry->data = (void *) entry;
      new_entry->next = bucket->next;
      bucket->next = new_entry;
      table->n_entries++;
      return (void *) entry;
    }

  /* Add ENTRY right in the bucket head.  */

  bucket->data = (void *) entry;
  table->n_entries++;
  table->n_buckets_used++;

  /* If the growth threshold of the buckets in use has been reached, increase
     the table size and rehash.  There's no point in checking the number of
     entries:  if the hashing function is ill-conditioned, rehashing is not
     likely to improve it.  */

  if (table->n_buckets_used
      > table->tuning->growth_threshold * table->n_buckets)
    {
      /* Check more fully, before starting real work.  If tuning arguments
     became invalid, the second check will rely on proper defaults.  */
      check_tuning (table);
      if (table->n_buckets_used
      > table->tuning->growth_threshold * table->n_buckets)
    {
      const Hash_tuning *tuning = table->tuning;
      unsigned candidate
        = (unsigned) (tuning->is_n_buckets
              ? (table->n_buckets * tuning->growth_factor)
              : (table->n_buckets * tuning->growth_factor
                 * tuning->growth_threshold));

      /* If the rehash fails, arrange to return NULL.  */
      if (!hash_rehash (table, candidate))
        entry = NULL;
    }
    }

  return (void *) entry;
}

/* If ENTRY is already in the table, remove it and return the just-deleted
   data (the user may want to deallocate its storage).  If ENTRY is not in the
   table, don't modify the table and return NULL.  */

void *
hash_delete (Hash_table *table, const void *entry)
{
  void *data;
  struct hash_entry *bucket;

  data = hash_find_entry (table, entry, &bucket, true);
  if (!data)
    return NULL;

  table->n_entries--;
  if (!bucket->data)
    {
      table->n_buckets_used--;

      /* If the shrink threshold of the buckets in use has been reached,
     rehash into a smaller table.  */

      if (table->n_buckets_used
      < table->tuning->shrink_threshold * table->n_buckets)
    {
      /* Check more fully, before starting real work.  If tuning arguments
         became invalid, the second check will rely on proper defaults.  */
      check_tuning (table);
      if (table->n_buckets_used
          < table->tuning->shrink_threshold * table->n_buckets)
        {
          const Hash_tuning *tuning = table->tuning;
          unsigned candidate
        = (unsigned) (tuning->is_n_buckets
                  ? table->n_buckets * tuning->shrink_factor
                  : (table->n_buckets * tuning->shrink_factor
                 * tuning->growth_threshold));

          hash_rehash (table, candidate);
        }
    }
    }

  return data;
}

/* Testing.  */

#if TESTING

void
hash_print (const Hash_table *table)
{
  struct hash_entry *bucket;

  for (bucket = table->bucket; bucket < table->bucket_limit; bucket++)
    {
      struct hash_entry *cursor;

      if (bucket)
    printf ("%d:\n", bucket - table->bucket);

      for (cursor = bucket; cursor; cursor = cursor->next)
    {
      char *s = (char *) cursor->data;
      /* FIXME */
      if (s)
        printf ("  %s\n", s);
    }
    }
}

#endif /* TESTING */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/hash-pjw.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/hash-pjw.c used in: Step 758
/* hash-pjw.c -- compute a hash value from a NUL-terminated string.
   Copyright 2001 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include "hash-pjw.h"

/* A hash function for NUL-terminated char* strings using
   the method described in Aho, Sethi, & Ullman, p 436. */

unsigned int
hash_pjw (const void *x, unsigned int tablesize)
{
  const char *s = x;
  unsigned int h = 0;
  unsigned int g;

  while (*s != 0)
    {
      h = (h << 4) + *s++;
      if ((g = h & (unsigned int) 0xf0000000) != 0)
        h = (h ^ (g >> 24)) ^ g;
    }

  return (h % tablesize);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/addext.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/addext.c used in: Step 759
/* addext.c -- add an extension to a file name
   Copyright 1990, 1997, 1998, 1999, 2001, 2002 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu> and Paul Eggert */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#ifndef HAVE_DOS_FILE_NAMES
# define HAVE_DOS_FILE_NAMES 0
#endif
#ifndef HAVE_LONG_FILE_NAMES
# define HAVE_LONG_FILE_NAMES 0
#endif

#if HAVE_LIMITS_H
# include <limits.h>
#endif
#ifndef _POSIX_NAME_MAX
# define _POSIX_NAME_MAX 14
#endif

#include <sys/types.h>
#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif

#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#include "backupfile.h"
#include "dirname.h"

/* Append to FILENAME the extension EXT, unless the result would be too long,
   in which case just append the character E.  */

void
addext (char *filename, char const *ext, int e)
{
  char *s = base_name (filename);
  size_t slen = base_len (s);
  size_t extlen = strlen (ext);
  size_t slen_max = HAVE_LONG_FILE_NAMES ? 255 : _POSIX_NAME_MAX;

#if HAVE_PATHCONF && defined _PC_NAME_MAX
  if (_POSIX_NAME_MAX < slen + extlen || HAVE_DOS_FILE_NAMES)
    {
      /* The new base name is long enough to require a pathconf check.  */
      long name_max;
      errno = 0;
      if (s == filename)
    name_max = pathconf (".", _PC_NAME_MAX);
      else
    {
      char c = *s;
      if (! ISSLASH (c))
        *s = 0;
      name_max = pathconf (filename, _PC_NAME_MAX);
      *s = c;
    }
      if (0 <= name_max || errno == 0)
    slen_max = name_max == (long) (size_t) name_max ? name_max : -1;
    }
#endif

  if (HAVE_DOS_FILE_NAMES && slen_max <= 12)
    {
      /* Live within DOS's 8.3 limit.  */
      char *dot = strchr (s, '.');
      if (dot)
    {
      slen -= dot + 1 - s;
      s = dot + 1;
      slen_max = 3;
    }
      else
    slen_max = 8;
      extlen = 9; /* Don't use EXT.  */
    }

  if (slen + extlen <= slen_max)
    strcpy (s + slen, ext);
  else
    {
      if (slen_max <= slen)
    slen = slen_max - 1;
      s[slen] = e;
      s[slen + 1] = 0;
    }
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/argmatch.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/argmatch.c used in: Step 760
/* argmatch.c -- find a match for a string in an array

   Copyright (C) 1990, 1998, 1999, 2001, 2002, 2003 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.  */

/* Written by David MacKenzie <djm@ai.mit.edu>
   Modified by Akim Demaille <demaille@inf.enst.fr> */

#if HAVE_CONFIG_H
# include <config.h>
#endif

/* Specification.  */
#include "argmatch.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "gettext.h"
#define _(msgid) gettext (msgid)

#include "error.h"
#include "quotearg.h"
#include "quote.h"
#include "unlocked-io.h"

/* When reporting an invalid argument, show nonprinting characters
   by using the quoting style ARGMATCH_QUOTING_STYLE.  Do not use
   literal_quoting_style.  */
#ifndef ARGMATCH_QUOTING_STYLE
# define ARGMATCH_QUOTING_STYLE locale_quoting_style
#endif

#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif

/* Non failing version of argmatch call this function after failing. */
#ifndef ARGMATCH_DIE
# define ARGMATCH_DIE exit (EXIT_FAILURE)
#endif

#ifdef ARGMATCH_DIE_DECL
ARGMATCH_DIE_DECL;
#endif

static void
__argmatch_die (void)
{
  ARGMATCH_DIE;
}

/* Used by XARGMATCH and XARGCASEMATCH.  See description in argmatch.h.
   Default to __argmatch_die, but allow caller to change this at run-time. */
argmatch_exit_fn argmatch_die = __argmatch_die;



/* If ARG is an unambiguous match for an element of the
   null-terminated array ARGLIST, return the index in ARGLIST
   of the matched element, else -1 if it does not match any element
   or -2 if it is ambiguous (is a prefix of more than one element).

   If VALLIST is none null, use it to resolve ambiguities limited to
   synonyms, i.e., for
     "yes", "yop" -> 0
     "no", "nope" -> 1
   "y" is a valid argument, for `0', and "n" for `1'.  */

int
argmatch (const char *arg, const char *const *arglist,
      const char *vallist, size_t valsize)
{
  int i;            /* Temporary index in ARGLIST.  */
  size_t arglen;        /* Length of ARG.  */
  int matchind = -1;        /* Index of first nonexact match.  */
  int ambiguous = 0;        /* If nonzero, multiple nonexact match(es).  */

  arglen = strlen (arg);

  /* Test all elements for either exact match or abbreviated matches.  */
  for (i = 0; arglist[i]; i++)
    {
      if (!strncmp (arglist[i], arg, arglen))
    {
      if (strlen (arglist[i]) == arglen)
        /* Exact match found.  */
        return i;
      else if (matchind == -1)
        /* First nonexact match found.  */
        matchind = i;
      else
        {
          /* Second nonexact match found.  */
          if (vallist == NULL
          || memcmp (vallist + valsize * matchind,
                 vallist + valsize * i, valsize))
        {
          /* There is a real ambiguity, or we could not
             disambiguate. */
          ambiguous = 1;
        }
        }
    }
    }
  if (ambiguous)
    return -2;
  else
    return matchind;
}

/* Error reporting for argmatch.
   CONTEXT is a description of the type of entity that was being matched.
   VALUE is the invalid value that was given.
   PROBLEM is the return value from argmatch.  */

void
argmatch_invalid (const char *context, const char *value, int problem)
{
  char const *format = (problem == -1
            ? _("invalid argument %s for %s")
            : _("ambiguous argument %s for %s"));

  error (0, 0, format, quotearg_n_style (0, ARGMATCH_QUOTING_STYLE, value),
     quote_n (1, context));
}

/* List the valid arguments for argmatch.
   ARGLIST is the same as in argmatch.
   VALLIST is a pointer to an array of values.
   VALSIZE is the size of the elements of VALLIST */
void
argmatch_valid (const char *const *arglist,
        const char *vallist, size_t valsize)
{
  int i;
  const char *last_val = NULL;

  /* We try to put synonyms on the same line.  The assumption is that
     synonyms follow each other */
  fprintf (stderr, _("Valid arguments are:"));
  for (i = 0; arglist[i]; i++)
    if ((i == 0)
    || memcmp (last_val, vallist + valsize * i, valsize))
      {
    fprintf (stderr, "\n  - `%s'", arglist[i]);
    last_val = vallist + valsize * i;
      }
    else
      {
    fprintf (stderr, ", `%s'", arglist[i]);
      }
  putc ('\n', stderr);
}

/* Never failing versions of the previous functions.

   CONTEXT is the context for which argmatch is called (e.g.,
   "--version-control", or "$VERSION_CONTROL" etc.).  Upon failure,
   calls the (supposed never to return) function EXIT_FN. */

int
__xargmatch_internal (const char *context,
              const char *arg, const char *const *arglist,
              const char *vallist, size_t valsize,
              argmatch_exit_fn exit_fn)
{
  int res = argmatch (arg, arglist, vallist, valsize);
  if (res >= 0)
    /* Success. */
    return res;

  /* We failed.  Explain why. */
  argmatch_invalid (context, arg, res);
  argmatch_valid (arglist, vallist, valsize);
  (*exit_fn) ();

  return -1; /* To please the compilers. */
}

/* Look for VALUE in VALLIST, an array of objects of size VALSIZE and
   return the first corresponding argument in ARGLIST */
const char *
argmatch_to_argument (const char *value,
              const char *const *arglist,
              const char *vallist, size_t valsize)
{
  int i;

  for (i = 0; arglist[i]; i++)
    if (!memcmp (value, vallist + valsize * i, valsize))
      return arglist[i];
  return NULL;
}

#ifdef TEST
/*
 * Based on "getversion.c" by David MacKenzie <djm@gnu.ai.mit.edu>
 */
char *program_name;
extern const char *getenv ();

/* When to make backup files.  */
enum backup_type
{
  /* Never make backups.  */
  none,

  /* Make simple backups of every file.  */
  simple,

  /* Make numbered backups of files that already have numbered backups,
     and simple backups of the others.  */
  numbered_existing,

  /* Make numbered backups of every file.  */
  numbered
};

/* Two tables describing arguments (keys) and their corresponding
   values */
static const char *const backup_args[] =
{
  "no", "none", "off",
  "simple", "never",
  "existing", "nil",
  "numbered", "t",
  0
};

static const enum backup_type backup_vals[] =
{
  none, none, none,
  simple, simple,
  numbered_existing, numbered_existing,
  numbered, numbered
};

int
main (int argc, const char *const *argv)
{
  const char *cp;
  enum backup_type backup_type = none;

  program_name = (char *) argv[0];

  if (argc > 2)
    {
      fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name);
      exit (1);
    }

  if ((cp = getenv ("VERSION_CONTROL")))
    backup_type = XARGMATCH ("$VERSION_CONTROL", cp,
                 backup_args, backup_vals);

  if (argc == 2)
    backup_type = XARGMATCH (program_name, argv[1],
                 backup_args, backup_vals);

  printf ("The version control is `%s'\n",
      ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals));

  return 0;
}
#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/backupfile.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/backupfile.c used in: Step 761
/* backupfile.c -- make Emacs style backup file names
   Copyright (C) 1990,91,92,93,94,95,96,97,98,99,2000, 2001, 2002 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu>.
   Some algorithms adapted from GNU Emacs. */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <sys/types.h>
#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif

#if HAVE_DIRENT_H
# include <dirent.h>
# define NLENGTH(direct) strlen ((direct)->d_name)
#else
# define dirent direct
# define NLENGTH(direct) ((size_t) (direct)->d_namlen)
# if HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
# if HAVE_NDIR_H
#  include <ndir.h>
# endif
#endif

#if CLOSEDIR_VOID
/* Fake a return value. */
# define CLOSEDIR(d) (closedir (d), 0)
#else
# define CLOSEDIR(d) closedir (d)
#endif

#if HAVE_STDLIB_H
# include <stdlib.h>
#endif

#ifndef HAVE_DECL_GETENV
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_GETENV
char *getenv ();
#endif

#ifndef HAVE_DECL_MALLOC
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_MALLOC
char *malloc ();
#endif

#if HAVE_DIRENT_H || HAVE_NDIR_H || HAVE_SYS_DIR_H || HAVE_SYS_NDIR_H
# define HAVE_DIR 1
#else
# define HAVE_DIR 0
#endif

#if HAVE_LIMITS_H
# include <limits.h>
#endif
#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif
/* Upper bound on the string length of an integer converted to string.
   302 / 1000 is ceil (log10 (2.0)).  Subtract 1 for the sign bit;
   add 1 for integer division truncation; add 1 more for a minus sign.  */
#define INT_STRLEN_BOUND(t) ((sizeof (t) * CHAR_BIT - 1) * 302 / 1000 + 2)

/* ISDIGIT differs from isdigit, as follows:
   - Its arg may be any int or unsigned int; it need not be an unsigned char.
   - It's guaranteed to evaluate its argument exactly once.
   - It's typically faster.
   POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
   ISDIGIT_LOCALE unless it's important to use the locale's definition
   of `digit' even when the host does not conform to POSIX.  */
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)

#if D_INO_IN_DIRENT
# define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0)
#else
# define REAL_DIR_ENTRY(dp) 1
#endif

#include "argmatch.h"
#include "backupfile.h"
#include "dirname.h"

/* The extension added to file names to produce a simple (as opposed
   to numbered) backup file name. */
const char *simple_backup_suffix = "~";

static int max_backup_version PARAMS ((const char *, const char *));
static int version_number PARAMS ((const char *, const char *, size_t));

/* Return the name of the new backup file for file FILE,
   allocated with malloc.  Return 0 if out of memory.
   FILE must not end with a '/' unless it is the root directory.
   Do not call this function if backup_type == none. */

char *
find_backup_file_name (const char *file, enum backup_type backup_type)
{
  size_t backup_suffix_size_max;
  size_t file_len = strlen (file);
  size_t numbered_suffix_size_max = INT_STRLEN_BOUND (int) + 4;
  char *s;
  const char *suffix = simple_backup_suffix;

  /* Allow room for simple or `.~N~' backups.  */
  backup_suffix_size_max = strlen (simple_backup_suffix) + 1;
  if (HAVE_DIR && backup_suffix_size_max < numbered_suffix_size_max)
    backup_suffix_size_max = numbered_suffix_size_max;

  s = malloc (file_len + 1
          + backup_suffix_size_max + numbered_suffix_size_max);
  if (s)
    {
#if HAVE_DIR
      if (backup_type != simple)
    {
      int highest_backup;
      size_t dirlen = dir_len (file);

      memcpy (s, file, dirlen);
      if (dirlen == FILESYSTEM_PREFIX_LEN (file))
        s[dirlen++] = '.';
      s[dirlen] = '\0';
      highest_backup = max_backup_version (base_name (file), s);
      if (! (backup_type == numbered_existing && highest_backup == 0))
        {
          char *numbered_suffix = s + (file_len + backup_suffix_size_max);
          sprintf (numbered_suffix, ".~%d~", highest_backup + 1);
          suffix = numbered_suffix;
        }
    }
#endif /* HAVE_DIR */

      strcpy (s, file);
      addext (s, suffix, '~');
    }
  return s;
}

#if HAVE_DIR

/* Return the number of the highest-numbered backup file for file
   FILE in directory DIR.  If there are no numbered backups
   of FILE in DIR, or an error occurs reading DIR, return 0.
   */

static int
max_backup_version (const char *file, const char *dir)
{
  DIR *dirp;
  struct dirent *dp;
  int highest_version;
  int this_version;
  size_t file_name_length;

  dirp = opendir (dir);
  if (!dirp)
    return 0;

  highest_version = 0;
  file_name_length = base_len (file);

  while ((dp = readdir (dirp)) != 0)
    {
      if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) < file_name_length + 4)
    continue;

      this_version = version_number (file, dp->d_name, file_name_length);
      if (this_version > highest_version)
    highest_version = this_version;
    }
  if (CLOSEDIR (dirp))
    return 0;
  return highest_version;
}

/* If BACKUP is a numbered backup of BASE, return its version number;
   otherwise return 0.  BASE_LENGTH is the length of BASE.
   */

static int
version_number (const char *base, const char *backup, size_t base_length)
{
  int version;
  const char *p;

  version = 0;
  if (strncmp (base, backup, base_length) == 0
      && backup[base_length] == '.'
      && backup[base_length + 1] == '~')
    {
      for (p = &backup[base_length + 2]; ISDIGIT (*p); ++p)
    version = version * 10 + *p - '0';
      if (p[0] != '~' || p[1])
    version = 0;
    }
  return version;
}
#endif /* HAVE_DIR */

static const char * const backup_args[] =
{
  /* In a series of synonyms, present the most meaning full first, so
     that argmatch_valid be more readable. */
  "none", "off",
  "simple", "never",
  "existing", "nil",
  "numbered", "t",
  0
};

static const enum backup_type backup_types[] =
{
  none, none,
  simple, simple,
  numbered_existing, numbered_existing,
  numbered, numbered
};

/* Return the type of backup specified by VERSION.
   If VERSION is NULL or the empty string, return numbered_existing.
   If VERSION is invalid or ambiguous, fail with a diagnostic appropriate
   for the specified CONTEXT.  Unambiguous abbreviations are accepted.  */

enum backup_type
get_version (const char *context, const char *version)
{
  if (version == 0 || *version == 0)
    return numbered_existing;
  else
    return XARGMATCH (context, version, backup_args, backup_types);
}


/* Return the type of backup specified by VERSION.
   If VERSION is NULL, use the value of the envvar VERSION_CONTROL.
   If the specified string is invalid or ambiguous, fail with a diagnostic
   appropriate for the specified CONTEXT.
   Unambiguous abbreviations are accepted.  */

enum backup_type
xget_version (const char *context, const char *version)
{
  if (version && *version)
    return get_version (context, version);
  else
    return get_version ("$VERSION_CONTROL", getenv ("VERSION_CONTROL"));
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/basename.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/basename.c used in: Step 762
/* basename.c -- return the last element in a path
   Copyright (C) 1990, 1998, 1999, 2000, 2001 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if STDC_HEADERS || HAVE_STRING_H
# include <string.h>
#endif
#include "dirname.h"

/* 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.  */

char *
base_name (char const *name)
{
  char const *base = name + FILESYSTEM_PREFIX_LEN (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.  */

size_t
base_len (char const *name)
{
  size_t len;

  for (len = strlen (name);  1 < len && ISSLASH (name[len - 1]);  len--)
    continue;

  return len;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/canon-host.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/canon-host.c used in: Step 763
/* Host name canonicalization

   Copyright (C) 1995, 1999, 2000 Free Software Foundation, Inc.

   Written by Miles Bader <miles@gnu.ai.mit.edu>

   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.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif

#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif

/* Returns the canonical hostname associated with HOST (allocated in a static
   buffer), or 0 if it can't be determined.  */
char *
canon_host (const char *host)
{
#ifdef HAVE_GETHOSTBYNAME
  struct hostent *he = gethostbyname (host);

  if (he)
    {
# ifdef HAVE_GETHOSTBYADDR
      char *addr = 0;

      /* Try and get an ascii version of the numeric host address.  */
      switch (he->h_addrtype)
    {
#  ifdef HAVE_INET_NTOA
    case AF_INET:
      addr = inet_ntoa (*(struct in_addr *) he->h_addr);
      break;
#  endif /* HAVE_INET_NTOA */
    }

      if (addr && strcmp (he->h_name, addr) == 0)
    {
      /* gethostbyname has returned a string representation of the IP
         address, for example, "127.0.0.1".  So now, look up the host
         name via the address.  Although it may seem reasonable to look
         up the host name via the address, we must not pass `he->h_addr'
         directly to gethostbyaddr because on some systems he->h_addr
         is located in a static library buffer that is reused in the
         gethostbyaddr call.  Make a copy and use that instead.  */
      char *h_addr_copy = (char *) malloc (he->h_length);
      if (h_addr_copy == NULL)
        he = NULL;
      else
        {
          memcpy (h_addr_copy, he->h_addr, he->h_length);
          he = gethostbyaddr (h_addr_copy, he->h_length, he->h_addrtype);
          free (h_addr_copy);
        }
    }
# endif /* HAVE_GETHOSTBYADDR */

      if (he)
    return (char *) (he->h_name);
    }
#endif /* HAVE_GETHOSTBYNAME */
  return 0;
}

#ifdef TEST_CANON_HOST
int
main (int argc, char **argv)
{
  int i;
  for (i = 1; i < argc; i++)
    {
      char *s = canon_host (argv[i]);
      printf ("%s: %s\n", argv[i], (s ? s : "<undef>"));
    }
  exit (0);
}
#endif /* TEST_CANON_HOST */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/closeout.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/closeout.c used in: Step 764
/* closeout.c - close standard output
   Copyright (C) 1998, 1999, 2000, 2001, 2002 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif

#include <stdio.h>

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#include "gettext.h"
#define _(msgid) gettext (msgid)

#include "closeout.h"
#include "error.h"
#include "quotearg.h"
#include "unlocked-io.h"
#include "__fpending.h"

static int default_exit_status = EXIT_FAILURE;
static const char *file_name;

/* Set the value to be used for the exit status when close_stdout is called.
   This is useful when it is not convenient to call close_stdout_status,
   e.g., when close_stdout is called via atexit.  */
void
close_stdout_set_status (int status)
{
  default_exit_status = status;
}

/* Set the file name to be reported in the event an error is detected
   by close_stdout_status.  */
void
close_stdout_set_file_name (const char *file)
{
  file_name = file;
}

/* Close standard output, exiting with status STATUS on failure.
   If a program writes *anything* to stdout, that program should `fflush'
   stdout and make sure that it succeeds before exiting.  Otherwise,
   suppose that you go to the extreme of checking the return status
   of every function that does an explicit write to stdout.  The last
   printf can succeed in writing to the internal stream buffer, and yet
   the fclose(stdout) could still fail (due e.g., to a disk full error)
   when it tries to write out that buffered data.  Thus, you would be
   left with an incomplete output file and the offending program would
   exit successfully.

   FIXME: note the fflush suggested above is implicit in the fclose
   we actually do below.  Consider doing only the fflush and/or using
   setvbuf to inhibit buffering.

   Besides, it's wasteful to check the return value from every call
   that writes to stdout -- just let the internal stream state record
   the failure.  That's what the ferror test is checking below.

   It's important to detect such failures and exit nonzero because many
   tools (most notably `make' and other build-management systems) depend
   on being able to detect failure in other tools via their exit status.  */

void
close_stdout_status (int status)
{
  int e = ferror (stdout) ? 0 : -1;

  /* If the stream's error bit is clear and there is nothing to flush,
     then return right away.  */
  if (e && __fpending (stdout) == 0)
    return;

  if (fclose (stdout) != 0)
    e = errno;

  if (0 <= e)
    {
      char const *write_error = _("write error");
      if (file_name)
    error (status, e, "%s: %s", quotearg_colon (file_name), write_error);
      else
    error (status, e, "%s", write_error);
    }
}

/* Close standard output, exiting with status EXIT_FAILURE on failure.  */
void
close_stdout (void)
{
  close_stdout_status (default_exit_status);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/cycle-check.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/cycle-check.c used in: Step 765
/* help detect directory cycles efficiently
   Copyright 2003 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Written by Jim Meyering */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

#if HAVE_STDBOOL_H
# include <stdbool.h>
#else
typedef enum {false = 0, true = 1} bool;
#endif

#include "cycle-check.h"
#include "xalloc.h"

#define SAME_INODE(Stat_buf_1, Stat_buf_2) \
  ((Stat_buf_1).st_ino == (Stat_buf_2).st_ino \
   && (Stat_buf_1).st_dev == (Stat_buf_2).st_dev)

#define CC_MAGIC 9827862

static inline bool
is_power_of_two (unsigned int i)
{
  return (i & (i - 1)) == 0;
}

void
cycle_check_init (struct cycle_check_state *state)
{
  state->chdir_counter = 0;
  state->magic = CC_MAGIC;
}

/* In traversing a directory hierarchy, call this function once for each
   descending chdir call, with SB corresponding to the chdir operand.
   If SB corresponds to a directory that has already been seen,
   return true to indicate that there is a directory cycle.
   Note that this is done `lazily', which means that some of
   the directories in the cycle may be processed twice before
   the cycle is detected.  */

bool
cycle_check (struct cycle_check_state *state, struct stat const *sb)
{
  assert (state->magic == CC_MAGIC);

  /* If the current directory ever happens to be the same
     as the one we last recorded for the cycle detection,
     then it's obviously part of a cycle.  */
  if (state->chdir_counter && SAME_INODE (*sb, state->dev_ino))
    return true;

  /* If the number of `descending' chdir calls is a power of two,
     record the dev/ino of the current directory.  */
  if (is_power_of_two (++(state->chdir_counter)))
    {
      state->dev_ino.st_dev = sb->st_dev;
      state->dev_ino.st_ino = sb->st_ino;
    }

  return false;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/diacrit.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/diacrit.c used in: Step 766
/* Diacritics processing for a few character codes.
   Copyright (C) 1990, 1991, 1992, 1993, 2000 Free Software Foundation, Inc.
   François Pinard <pinard@iro.umontreal.ca>, 1988.

   All this file is a temporary hack, waiting for locales in GNU.
*/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include "diacrit.h"

/* ISO 8859-1 Latin-1 code is used as the underlying character set.  If
   MSDOS is defined, IBM-PC's character set code is used instead.  */

/*--------------------------------------------------------------------.
| For each alphabetic character, returns what it would be without its |
| possible diacritic symbol.                          |
`--------------------------------------------------------------------*/

const char diacrit_base[256] =
{
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      'A',    'B',    'C',    'D',    'E',    'F',    'G',
  'H',    'I',    'J',    'K',    'L',    'M',    'N',    'O',
  'P',    'Q',    'R',    'S',    'T',    'U',    'V',    'W',
  'X',    'Y',    'Z',    0,      0,      0,      0,      0,
  0,      'a',    'b',    'c',    'd',    'e',    'f',    'g',
  'h',    'i',    'j',    'k',    'l',    'm',    'n',    'o',
  'p',    'q',    'r',    's',    't',    'u',    'v',    'w',
  'x',    'y',    'z',    0,      0,      0,      0,      0,

#ifdef __MSDOS__

  'C',    'u',    'e',    'a',    'a',    'a',    'a',    'c',
  'e',    'e',    'e',    'i',    'i',    'i',    'A',    'A',
  'E',    'e',    'E',    'o',    'o',    'o',    'u',    'u',
  'y',    'O',    'U',    0,      0,      0,      0,      0,
  'a',    'i',    'o',    'u',    'n',    'N',    0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,

#else

  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  'A',    'A',    'A',    'A',    'A',    'A',    'A',    'C',
  'E',    'E',    'E',    'E',    'I',    'I',    'I',    'I',
  0,      'N',    'O',    'O',    'O',    'O',    'O',    0,
  'O',    'U',    'U',    'U',    'U',    'Y',    0,      0,
  'a',    'a',    'a',    'a',    'a',    'a',    'a',    'c',
  'e',    'e',    'e',    'e',    'i',    'i',    'i',    'i',
  0,      'n',    'o',    'o',    'o',    'o',    'o',    0,
  'o',    'u',    'u',    'u',    'u',    'y',    0,      'y',

#endif
};

/*------------------------------------------------------------------------.
| For each alphabetic character, returns a code of what its diacritic is, |
| according to the following codes: 1 (eE) over aA for latin diphtongs; 2 |
| (') acute accent; 3 (`) grave accent; 4 (^) circumflex accent; 5 (")    |
| umlaut or diaraesis; 6 (~) tilda; 7 (,) cedilla; 8 (o) covering degree  |
| symbol; 9 (|) slashed character.                    |
`------------------------------------------------------------------------*/

const char diacrit_diac[256] =
{
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      4,      0,
  3,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      6,      0,

#ifdef __MSDOS__

  7,      5,      2,      4,      5,      3,      8,      7,
  4,      5,      3,      5,      4,      3,      5,      8,
  2,      1,      1,      4,      5,      3,      4,      3,
  5,      5,      5,      0,      0,      0,      0,      0,
  2,      2,      2,      2,      6,      6,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,

#else

  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  0,      0,      0,      0,      0,      0,      0,      0,
  3,      2,      4,      6,      5,      8,      1,      7,
  3,      2,      4,      5,      3,      2,      4,      5,
  0,      6,      3,      2,      4,      6,      5,      0,
  9,      3,      2,      4,      5,      2,      0,      0,
  3,      2,      4,      6,      5,      8,      1,      7,
  3,      2,      4,      5,      3,      2,      4,      5,
  0,      6,      3,      2,      4,      6,      5,      0,
  9,      3,      2,      4,      5,      2,      0,      0,

#endif
};

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/dirname.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/dirname.c used in: Step 767
/* dirname.c -- return all but the last element in a path
   Copyright 1990, 1998, 2000, 2001 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if STDC_HEADERS || HAVE_STRING_H
# include <string.h>
#endif

#include "dirname.h"
#include "xalloc.h"

/* Return the length of `dirname (PATH)', or zero if PATH is
   in the working directory.  Works properly even if
   there are trailing slashes (by effectively ignoring them).  */
size_t
dir_len (char const *path)
{
  size_t prefix_length = FILESYSTEM_PREFIX_LEN (path);
  size_t length;

  /* Strip the basename and any redundant slashes before it.  */
  for (length = base_name (path) - path;  prefix_length < length;  length--)
    if (! ISSLASH (path[length - 1]))
      return length;

  /* But don't strip the only slash from "/".  */
  return prefix_length + ISSLASH (path[prefix_length]);
}

/* Return the leading directories part of PATH,
   allocated with xmalloc.
   Works properly even if there are trailing slashes
   (by effectively ignoring them).  */

char *
dir_name (char const *path)
{
  size_t length = dir_len (path);
  int append_dot = (length == FILESYSTEM_PREFIX_LEN (path));
  char *newpath = xmalloc (length + append_dot + 1);
  memcpy (newpath, path, length);
  if (append_dot)
    newpath[length++] = '.';
  newpath[length] = 0;
  return newpath;
}

#ifdef TEST_DIRNAME
/*

Run the test like this (expect no output):
  gcc -DHAVE_CONFIG_H -DTEST_DIRNAME -I.. -O -Wall \
     basename.c dirname.c xmalloc.c
  sed -n '/^BEGIN-DATA$/,/^END-DATA$/p' dirname.c|grep -v DATA|./a.out

BEGIN-DATA
foo//// .
bar/foo//// bar
foo/ .
/ /
. .
a .
END-DATA

*/

# define MAX_BUFF_LEN 1024
# include <stdio.h>

int
main ()
{
  char buff[MAX_BUFF_LEN + 1];

  buff[MAX_BUFF_LEN] = 0;
  while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
    {
      char path[MAX_BUFF_LEN];
      char expected_result[MAX_BUFF_LEN];
      char const *result;
      sscanf (buff, "%s %s", path, expected_result);
      result = dir_name (path);
      if (strcmp (result, expected_result))
    printf ("%s: got %s, expected %s\n", path, result, expected_result);
    }
  return 0;
}
#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/dup-safer.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/dup-safer.c used in: Step 768
/* Invoke dup, but avoid some glitches.
   Copyright (C) 2001 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.  */

/* Written by Paul Eggert.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#if HAVE_FCNTL_H
# include <fcntl.h>
#endif

#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifndef STDERR_FILENO
# define STDERR_FILENO 2
#endif

#include <unistd-safer.h>

/* Like dup, but do not return STDIN_FILENO, STDOUT_FILENO, or
   STDERR_FILENO.  */

int
dup_safer (int fd)
{
#ifdef F_DUPFD
  return fcntl (fd, F_DUPFD, STDERR_FILENO + 1);
#else
  int f = dup (fd);
  if (0 <= f && f <= STDERR_FILENO)
    {
      int f1 = dup_safer (f);
      int e = errno;
      close (f);
      errno = e;
      f = f1;
    }
  return f;
#endif
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/error.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/error.c used in: Step 769
/* Error handler for noninteractive utilities
   Copyright (C) 1990-1998, 2000, 2001, 2002, 2003 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.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>

#ifdef _LIBC
# include <libintl.h>
#else
# include "gettext.h"
#endif

#ifdef _LIBC
# include <wchar.h>
# define mbsrtowcs __mbsrtowcs
#endif

#if HAVE_VPRINTF || HAVE_DOPRNT || _LIBC
# if __STDC__
#  include <stdarg.h>
#  define VA_START(args, lastarg) va_start(args, lastarg)
# else
#  include <varargs.h>
#  define VA_START(args, lastarg) va_start(args)
# endif
#else
# define va_alist a1, a2, a3, a4, a5, a6, a7, a8
# define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
#endif

#if STDC_HEADERS || _LIBC
# include <stdlib.h>
# include <string.h>
#else
void exit ();
#endif

#include "error.h"

#if !_LIBC
# include "unlocked-io.h"
#endif

#ifndef _
# define _(String) String
#endif

/* If NULL, error will flush stdout, then print on stderr the program
   name, a colon and a space.  Otherwise, error will call this
   function without parameters instead.  */
void (*error_print_progname) (
#if __STDC__ - 0
                  void
#endif
                  );

/* This variable is incremented each time `error' is called.  */
unsigned int error_message_count;

#ifdef _LIBC
/* In the GNU C library, there is a predefined variable for this.  */

# define program_name program_invocation_name
# include <errno.h>
# include <libio/libioP.h>

/* In GNU libc we want do not want to use the common name `error' directly.
   Instead make it a weak alias.  */
extern void __error (int status, int errnum, const char *message, ...)
     __attribute__ ((__format__ (__printf__, 3, 4)));
extern void __error_at_line (int status, int errnum, const char *file_name,
                 unsigned int line_number, const char *message,
                 ...)
     __attribute__ ((__format__ (__printf__, 5, 6)));;
# define error __error
# define error_at_line __error_at_line

# include <libio/iolibio.h>
# define fflush(s) INTUSE(_IO_fflush) (s)
# undef putc
# define putc(c, fp) INTUSE(_IO_putc) (c, fp)

#else /* not _LIBC */

# if !HAVE_DECL_STRERROR_R && STRERROR_R_CHAR_P
#  ifndef HAVE_DECL_STRERROR_R
"this configure-time declaration test was not run"
#  endif
char *strerror_r ();
# endif

/* The calling program should define program_name and set it to the
   name of the executing program.  */
extern char *program_name;

# if HAVE_STRERROR_R || defined strerror_r
#  define __strerror_r strerror_r
# else
#  if HAVE_STRERROR
#   ifndef HAVE_DECL_STRERROR
"this configure-time declaration test was not run"
#   endif
#   if !HAVE_DECL_STRERROR
char *strerror ();
#   endif
#  else
static char *
private_strerror (int errnum)
{
  extern char *sys_errlist[];
  extern int sys_nerr;

  if (errnum > 0 && errnum <= sys_nerr)
    return _(sys_errlist[errnum]);
  return _("Unknown system error");
}
#   define strerror private_strerror
#  endif /* HAVE_STRERROR */
# endif /* HAVE_STRERROR_R || defined strerror_r */
#endif  /* not _LIBC */

static void
print_errno_message (int errnum)
{
  char const *s;

#if defined HAVE_STRERROR_R || _LIBC
  char errbuf[1024];
# if STRERROR_R_CHAR_P || _LIBC
  s = __strerror_r (errnum, errbuf, sizeof errbuf);
# else
  if (__strerror_r (errnum, errbuf, sizeof errbuf) == 0)
    s = errbuf;
  else
    s = 0;
# endif
#else
  s = strerror (errnum);
#endif

#if !_LIBC
  if (! s)
    s = _("Unknown system error");
#endif

#if _LIBC
  if (_IO_fwide (stderr, 0) > 0)
    {
      __fwprintf (stderr, L": %s", s);
      return;
    }
#endif

  fprintf (stderr, ": %s", s);
}

#ifdef VA_START
static void
error_tail (int status, int errnum, const char *message, va_list args)
{
# if HAVE_VPRINTF || _LIBC
#  if _LIBC
  if (_IO_fwide (stderr, 0) > 0)
    {
#   define ALLOCA_LIMIT 2000
      size_t len = strlen (message) + 1;
      wchar_t *wmessage = NULL;
      mbstate_t st;
      size_t res;
      const char *tmp;

      do
    {
      if (len < ALLOCA_LIMIT)
        wmessage = (wchar_t *) alloca (len * sizeof (wchar_t));
      else
        {
          if (wmessage != NULL && len / 2 < ALLOCA_LIMIT)
        wmessage = NULL;

          wmessage = (wchar_t *) realloc (wmessage,
                          len * sizeof (wchar_t));

          if (wmessage == NULL)
        {
          fputws_unlocked (L"out of memory\n", stderr);
          return;
        }
        }

      memset (&st, '\0', sizeof (st));
      tmp =message;
    }
      while ((res = mbsrtowcs (wmessage, &tmp, len, &st)) == len);

      if (res == (size_t) -1)
    /* The string cannot be converted.  */
    wmessage = (wchar_t *) L"???";

      __vfwprintf (stderr, wmessage, args);
    }
  else
#  endif
    vfprintf (stderr, message, args);
# else
  _doprnt (message, args, stderr);
# endif
  va_end (args);

  ++error_message_count;
  if (errnum)
    print_errno_message (errnum);
# if _LIBC
  if (_IO_fwide (stderr, 0) > 0)
    putwc (L'\n', stderr);
  else
# endif
    putc ('\n', stderr);
  fflush (stderr);
  if (status)
    exit (status);
}
#endif


/* Print the program name and error message MESSAGE, which is a printf-style
   format string with optional args.
   If ERRNUM is nonzero, print its corresponding system error message.
   Exit with status STATUS if it is nonzero.  */
/* VARARGS */
void
#if defined VA_START && __STDC__
error (int status, int errnum, const char *message, ...)
#else
error (status, errnum, message, va_alist)
     int status;
     int errnum;
     char *message;
     va_dcl
#endif
{
#ifdef VA_START
  va_list args;
#endif

  fflush (stdout);
#ifdef _LIBC
  _IO_flockfile (stderr);
#endif
  if (error_print_progname)
    (*error_print_progname) ();
  else
    {
#if _LIBC
      if (_IO_fwide (stderr, 0) > 0)
    __fwprintf (stderr, L"%s: ", program_name);
      else
#endif
    fprintf (stderr, "%s: ", program_name);
    }

#ifdef VA_START
  VA_START (args, message);
  error_tail (status, errnum, message, args);
#else
  fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);

  ++error_message_count;
  if (errnum)
    print_errno_message (errnum);
  putc ('\n', stderr);
  fflush (stderr);
  if (status)
    exit (status);
#endif

#ifdef _LIBC
  _IO_funlockfile (stderr);
#endif
}


/* Sometimes we want to have at most one error per line.  This
   variable controls whether this mode is selected or not.  */
int error_one_per_line;

void
#if defined VA_START && __STDC__
error_at_line (int status, int errnum, const char *file_name,
           unsigned int line_number, const char *message, ...)
#else
error_at_line (status, errnum, file_name, line_number, message, va_alist)
     int status;
     int errnum;
     const char *file_name;
     unsigned int line_number;
     char *message;
     va_dcl
#endif
{
#ifdef VA_START
  va_list args;
#endif

  if (error_one_per_line)
    {
      static const char *old_file_name;
      static unsigned int old_line_number;

      if (old_line_number == line_number
      && (file_name == old_file_name
          || strcmp (old_file_name, file_name) == 0))
    /* Simply return and print nothing.  */
    return;

      old_file_name = file_name;
      old_line_number = line_number;
    }

  fflush (stdout);
#ifdef _LIBC
  _IO_flockfile (stderr);
#endif
  if (error_print_progname)
    (*error_print_progname) ();
  else
    {
#if _LIBC
      if (_IO_fwide (stderr, 0) > 0)
    __fwprintf (stderr, L"%s: ", program_name);
      else
#endif
    fprintf (stderr, "%s:", program_name);
    }

  if (file_name != NULL)
    {
#if _LIBC
      if (_IO_fwide (stderr, 0) > 0)
    __fwprintf (stderr, L"%s:%d: ", file_name, line_number);
      else
#endif
    fprintf (stderr, "%s:%d: ", file_name, line_number);
    }

#ifdef VA_START
  VA_START (args, message);
  error_tail (status, errnum, message, args);
#else
  fprintf (stderr, message, a1, a2, a3, a4, a5, a6, a7, a8);

  ++error_message_count;
  if (errnum)
    print_errno_message (errnum);
  putc ('\n', stderr);
  fflush (stderr);
  if (status)
    exit (status);
#endif

#ifdef _LIBC
  _IO_funlockfile (stderr);
#endif
}

#ifdef _LIBC
/* Make the weak alias.  */
# undef error
# undef error_at_line
weak_alias (__error, error)
weak_alias (__error_at_line, error_at_line)
#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/exclude.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/exclude.c used in: Step 770
/* exclude.c -- exclude file names

   Copyright (C) 1992, 1993, 1994, 1997, 1999, 2000, 2001, 2002, 2003 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Written by Paul Eggert <eggert@twinsun.com>  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_STDBOOL_H
# include <stdbool.h>
#else
typedef enum {false = 0, true = 1} bool;
#endif

#include <errno.h>
#ifndef errno
extern int errno;
#endif
#include <stdio.h>
#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#if HAVE_STRING_H
# include <string.h>
#endif
#if HAVE_STRINGS_H
# include <strings.h>
#endif
#if HAVE_INTTYPES_H
# include <inttypes.h>
#else
# if HAVE_STDINT_H
#  include <stdint.h>
# endif
#endif

#include "exclude.h"
#include "fnmatch.h"
#include "unlocked-io.h"
#include "xalloc.h"

#ifndef SIZE_MAX
# define SIZE_MAX ((size_t) -1)
#endif

/* Verify a requirement at compile-time (unlike assert, which is runtime).  */
#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }

/* Non-GNU systems lack these options, so we don't need to check them.  */
#ifndef FNM_CASEFOLD
# define FNM_CASEFOLD 0
#endif
#ifndef FNM_LEADING_DIR
# define FNM_LEADING_DIR 0
#endif

verify (EXCLUDE_macros_do_not_collide_with_FNM_macros,
    (((EXCLUDE_ANCHORED | EXCLUDE_INCLUDE | EXCLUDE_WILDCARDS)
      & (FNM_PATHNAME | FNM_NOESCAPE | FNM_PERIOD | FNM_LEADING_DIR
         | FNM_CASEFOLD))
     == 0));

/* An exclude pattern-options pair.  The options are fnmatch options
   ORed with EXCLUDE_* options.  */

struct patopts
  {
    char const *pattern;
    int options;
  };

/* An exclude list, of pattern-options pairs.  */

struct exclude
  {
    struct patopts *exclude;
    size_t exclude_alloc;
    size_t exclude_count;
  };

/* Return a newly allocated and empty exclude list.  */

struct exclude *
new_exclude (void)
{
  struct exclude *ex = (struct exclude *) xmalloc (sizeof *ex);
  ex->exclude_count = 0;
  ex->exclude_alloc = (1 << 6); /* This must be a power of 2.  */
  ex->exclude = (struct patopts *) xmalloc (ex->exclude_alloc
                        * sizeof ex->exclude[0]);
  return ex;
}

/* Free the storage associated with an exclude list.  */

void
free_exclude (struct exclude *ex)
{
  free (ex->exclude);
  free (ex);
}

/* Return zero if PATTERN matches F, obeying OPTIONS, except that
   (unlike fnmatch) wildcards are disabled in PATTERN.  */

static int
fnmatch_no_wildcards (char const *pattern, char const *f, int options)
{
  if (! (options & FNM_LEADING_DIR))
    return ((options & FNM_CASEFOLD)
        ? strcasecmp (pattern, f)
        : strcmp (pattern, f));
  else
    {
      size_t patlen = strlen (pattern);
      int r = ((options & FNM_CASEFOLD)
        ? strncasecmp (pattern, f, patlen)
        : strncmp (pattern, f, patlen));
      if (! r)
    {
      r = f[patlen];
      if (r == '/')
        r = 0;
    }
      return r;
    }
}

/* Return true if EX excludes F.  */

bool
excluded_filename (struct exclude const *ex, char const *f)
{
  size_t exclude_count = ex->exclude_count;

  /* If no options are given, the default is to include.  */
  if (exclude_count == 0)
    return false;
  else
    {
      struct patopts const *exclude = ex->exclude;
      size_t i;

      /* Otherwise, the default is the opposite of the first option.  */
      bool excluded = !! (exclude[0].options & EXCLUDE_INCLUDE);

      /* Scan through the options, seeing whether they change F from
     excluded to included or vice versa.  */
      for (i = 0;  i < exclude_count;  i++)
    {
      char const *pattern = exclude[i].pattern;
      int options = exclude[i].options;
      if (excluded == !! (options & EXCLUDE_INCLUDE))
        {
          int (*matcher) (char const *, char const *, int) =
        (options & EXCLUDE_WILDCARDS
         ? fnmatch
         : fnmatch_no_wildcards);
          bool matched = ((*matcher) (pattern, f, options) == 0);
          char const *p;

          if (! (options & EXCLUDE_ANCHORED))
        for (p = f; *p && ! matched; p++)
          if (*p == '/' && p[1] != '/')
            matched = ((*matcher) (pattern, p + 1, options) == 0);

          excluded ^= matched;
        }
    }

      return excluded;
    }
}

/* Append to EX the exclusion PATTERN with OPTIONS.  */

void
add_exclude (struct exclude *ex, char const *pattern, int options)
{
  struct patopts *patopts;

  if (ex->exclude_alloc <= ex->exclude_count)
    {
      size_t s = 2 * ex->exclude_alloc;
      if (! (0 < s && s <= SIZE_MAX / sizeof ex->exclude[0]))
    xalloc_die ();
      ex->exclude_alloc = s;
      ex->exclude = (struct patopts *) xrealloc (ex->exclude,
                         s * sizeof ex->exclude[0]);
    }

  patopts = &ex->exclude[ex->exclude_count++];
  patopts->pattern = pattern;
  patopts->options = options;
}

/* Use ADD_FUNC to append to EX the patterns in FILENAME, each with
   OPTIONS.  LINE_END terminates each pattern in the file.  Return -1
   on failure, 0 on success.  */

int
add_exclude_file (void (*add_func) (struct exclude *, char const *, int),
          struct exclude *ex, char const *filename, int options,
          char line_end)
{
  bool use_stdin = filename[0] == '-' && !filename[1];
  FILE *in;
  char *buf;
  char *p;
  char const *pattern;
  char const *lim;
  size_t buf_alloc = (1 << 10);  /* This must be a power of two.  */
  size_t buf_count = 0;
  int c;
  int e = 0;

  if (use_stdin)
    in = stdin;
  else if (! (in = fopen (filename, "r")))
    return -1;

  buf = xmalloc (buf_alloc);

  while ((c = getc (in)) != EOF)
    {
      buf[buf_count++] = c;
      if (buf_count == buf_alloc)
    {
      buf_alloc *= 2;
      if (! buf_alloc)
        xalloc_die ();
      buf = xrealloc (buf, buf_alloc);
    }
    }

  if (ferror (in))
    e = errno;

  if (!use_stdin && fclose (in) != 0)
    e = errno;

  buf = xrealloc (buf, buf_count + 1);

  for (pattern = p = buf, lim = buf + buf_count;  p <= lim;  p++)
    if (p < lim ? *p == line_end : buf < p && p[-1])
      {
    *p = '\0';
    (*add_func) (ex, pattern, options);
    pattern = p + 1;
      }

  errno = e;
  return e ? -1 : 0;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/exitfail.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/exitfail.c used in: Step 771
/* Failure exit status

   Copyright (C) 2002 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif

int volatile exit_failure = EXIT_FAILURE;

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/filemode.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/filemode.c used in: Step 772
/* filemode.c -- make a string describing file modes
   Copyright (C) 1985, 1990, 1993, 1998-2000 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>

#include "filemode.h"

#if !S_IRUSR
# if S_IREAD
#  define S_IRUSR S_IREAD
# else
#  define S_IRUSR 00400
# endif
#endif

#if !S_IWUSR
# if S_IWRITE
#  define S_IWUSR S_IWRITE
# else
#  define S_IWUSR 00200
# endif
#endif

#if !S_IXUSR
# if S_IEXEC
#  define S_IXUSR S_IEXEC
# else
#  define S_IXUSR 00100
# endif
#endif

#if !S_IRGRP
# define S_IRGRP (S_IRUSR >> 3)
#endif
#if !S_IWGRP
# define S_IWGRP (S_IWUSR >> 3)
#endif
#if !S_IXGRP
# define S_IXGRP (S_IXUSR >> 3)
#endif
#if !S_IROTH
# define S_IROTH (S_IRUSR >> 6)
#endif
#if !S_IWOTH
# define S_IWOTH (S_IWUSR >> 6)
#endif
#if !S_IXOTH
# define S_IXOTH (S_IXUSR >> 6)
#endif

#ifdef STAT_MACROS_BROKEN
# undef S_ISBLK
# undef S_ISCHR
# undef S_ISDIR
# undef S_ISFIFO
# undef S_ISLNK
# undef S_ISMPB
# undef S_ISMPC
# undef S_ISNWK
# undef S_ISREG
# undef S_ISSOCK
#endif /* STAT_MACROS_BROKEN.  */

#if !defined S_ISBLK && defined S_IFBLK
# define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
#endif
#if !defined S_ISCHR && defined S_IFCHR
# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#endif
#if !defined S_ISDIR && defined S_IFDIR
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#if !defined S_ISREG && defined S_IFREG
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
#if !defined S_ISFIFO && defined S_IFIFO
# define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#endif
#if !defined S_ISLNK && defined S_IFLNK
# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#endif
#if !defined S_ISSOCK && defined S_IFSOCK
# define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
#endif
#if !defined S_ISMPB && defined S_IFMPB /* V7 */
# define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB)
# define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC)
#endif
#if !defined S_ISNWK && defined S_IFNWK /* HP/UX */
# define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK)
#endif
#if !defined S_ISDOOR && defined S_IFDOOR /* Solaris 2.5 and up */
# define S_ISDOOR(m) (((m) & S_IFMT) == S_IFDOOR)
#endif
#if !defined S_ISCTG && defined S_IFCTG /* MassComp */
# define S_ISCTG(m) (((m) & S_IFMT) == S_IFCTG)
#endif



/* Set the 's' and 't' flags in file attributes string CHARS,
   according to the file mode BITS.  */

static void
setst (mode_t bits, char *chars)
{
#ifdef S_ISUID
  if (bits & S_ISUID)
    {
      if (chars[3] != 'x')
    /* Set-uid, but not executable by owner.  */
    chars[3] = 'S';
      else
    chars[3] = 's';
    }
#endif
#ifdef S_ISGID
  if (bits & S_ISGID)
    {
      if (chars[6] != 'x')
    /* Set-gid, but not executable by group.  */
    chars[6] = 'S';
      else
    chars[6] = 's';
    }
#endif
#ifdef S_ISVTX
  if (bits & S_ISVTX)
    {
      if (chars[9] != 'x')
    /* Sticky, but not executable by others.  */
    chars[9] = 'T';
      else
    chars[9] = 't';
    }
#endif
}

/* Return a character indicating the type of file described by
   file mode BITS:
   'd' for directories
   'D' for doors
   'b' for block special files
   'c' for character special files
   'n' for network special files
   'm' for multiplexor files
   'M' for an off-line (regular) file
   'l' for symbolic links
   's' for sockets
   'p' for fifos
   'C' for contigous data files
   '-' for regular files
   '?' for any other file type.  */

static char
ftypelet (mode_t bits)
{
#ifdef S_ISBLK
  if (S_ISBLK (bits))
    return 'b';
#endif
  if (S_ISCHR (bits))
    return 'c';
  if (S_ISDIR (bits))
    return 'd';
  if (S_ISREG (bits))
    return '-';
#ifdef S_ISFIFO
  if (S_ISFIFO (bits))
    return 'p';
#endif
#ifdef S_ISLNK
  if (S_ISLNK (bits))
    return 'l';
#endif
#ifdef S_ISSOCK
  if (S_ISSOCK (bits))
    return 's';
#endif
#ifdef S_ISMPC
  if (S_ISMPC (bits))
    return 'm';
#endif
#ifdef S_ISNWK
  if (S_ISNWK (bits))
    return 'n';
#endif
#ifdef S_ISDOOR
  if (S_ISDOOR (bits))
    return 'D';
#endif
#ifdef S_ISCTG
  if (S_ISCTG (bits))
    return 'C';
#endif

  /* The following two tests are for Cray DMF (Data Migration
     Facility), which is a HSM file system.  A migrated file has a
     `st_dm_mode' that is different from the normal `st_mode', so any
     tests for migrated files should use the former.  */

#ifdef S_ISOFD
  if (S_ISOFD (bits))
    /* off line, with data  */
    return 'M';
#endif
#ifdef S_ISOFL
  /* off line, with no data  */
  if (S_ISOFL (bits))
    return 'M';
#endif
  return '?';
}

/* Like filemodestring, but only the relevant part of the `struct stat'
   is given as an argument.  */

void
mode_string (mode_t mode, char *str)
{
  str[0] = ftypelet (mode);
  str[1] = mode & S_IRUSR ? 'r' : '-';
  str[2] = mode & S_IWUSR ? 'w' : '-';
  str[3] = mode & S_IXUSR ? 'x' : '-';
  str[4] = mode & S_IRGRP ? 'r' : '-';
  str[5] = mode & S_IWGRP ? 'w' : '-';
  str[6] = mode & S_IXGRP ? 'x' : '-';
  str[7] = mode & S_IROTH ? 'r' : '-';
  str[8] = mode & S_IWOTH ? 'w' : '-';
  str[9] = mode & S_IXOTH ? 'x' : '-';
  setst (mode, str);
}

/* filemodestring - fill in string STR with an ls-style ASCII
   representation of the st_mode field of file stats block STATP.
   10 characters are stored in STR; no terminating null is added.
   The characters stored in STR are:

   0    File type.  'd' for directory, 'c' for character
    special, 'b' for block special, 'm' for multiplex,
    'l' for symbolic link, 's' for socket, 'p' for fifo,
    '-' for regular, '?' for any other file type

   1    'r' if the owner may read, '-' otherwise.

   2    'w' if the owner may write, '-' otherwise.

   3    'x' if the owner may execute, 's' if the file is
    set-user-id, '-' otherwise.
    'S' if the file is set-user-id, but the execute
    bit isn't set.

   4    'r' if group members may read, '-' otherwise.

   5    'w' if group members may write, '-' otherwise.

   6    'x' if group members may execute, 's' if the file is
    set-group-id, '-' otherwise.
    'S' if it is set-group-id but not executable.

   7    'r' if any user may read, '-' otherwise.

   8    'w' if any user may write, '-' otherwise.

   9    'x' if any user may execute, 't' if the file is "sticky"
    (will be retained in swap space after execution), '-'
    otherwise.
    'T' if the file is sticky but not executable.  */

void
filemodestring (struct stat *statp, char *str)
{
  mode_string (statp->st_mode, str);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/__fpending.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/__fpending.c used in: Step 773
/* __fpending.c -- return the number of pending output bytes on a stream
   Copyright (C) 2000 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.  */

/* Written by Jim Meyering. */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include "__fpending.h"

size_t
__fpending (FILE *fp)
{
  return PENDING_OUTPUT_N_BYTES;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/file-type.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/file-type.c used in: Step 774
/* Return a string describing the type of a file.

   Copyright (C) 1993, 1994, 2001, 2002 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.  */

/* Written by Paul Eggert.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include "file-type.h"

#include <gettext.h>
#define _(text) gettext (text)

char const *
file_type (struct stat const *st)
{
  /* See POSIX 1003.1-2001 XCU Table 4-8 lines 17093-17107 for some of
     these formats.

     To keep diagnostics grammatical in English, the returned string
     must start with a consonant.  */

  if (S_ISREG (st->st_mode))
    return st->st_size == 0 ? _("regular empty file") : _("regular file");

  if (S_ISDIR (st->st_mode))
    return _("directory");

  if (S_ISBLK (st->st_mode))
    return _("block special file");

  if (S_ISCHR (st->st_mode))
    return _("character special file");

  if (S_ISFIFO (st->st_mode))
    return _("fifo");

  if (S_ISLNK (st->st_mode))
    return _("symbolic link");

  if (S_ISSOCK (st->st_mode))
    return _("socket");

  if (S_TYPEISMQ (st))
    return _("message queue");

  if (S_TYPEISSEM (st))
    return _("semaphore");

  if (S_TYPEISSHM (st))
    return _("shared memory object");

  return _("weird file");
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/fnmatch.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/fnmatch.c used in: Step 775
/* Copyright (C) 1991, 1992, 1993, 1996, 1997, 1998, 1999, 2000, 2001,
   2002 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

/* Enable GNU extensions in fnmatch.h.  */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE    1
#endif

#ifdef __GNUC__
# define alloca __builtin_alloca
# define HAVE_ALLOCA 1
#else
# if defined HAVE_ALLOCA_H || defined _LIBC
#  include <alloca.h>
# else
#  ifdef _AIX
 #  pragma alloca
#  else
#   ifndef alloca
char *alloca ();
#   endif
#  endif
# endif
#endif

#if ! defined __builtin_expect && __GNUC__ < 3
# define __builtin_expect(expr, expected) (expr)
#endif

#include <assert.h>
#include <errno.h>
#include <fnmatch.h>
#include <ctype.h>

#if HAVE_STRING_H || defined _LIBC
# include <string.h>
#else
# if HAVE_STRINGS_H
#  include <strings.h>
# endif
#endif

#if defined STDC_HEADERS || defined _LIBC
# include <stddef.h>
# include <stdlib.h>
#endif

#define WIDE_CHAR_SUPPORT (HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_BTOWC)

/* For platform which support the ISO C amendement 1 functionality we
   support user defined character classes.  */
#if defined _LIBC || WIDE_CHAR_SUPPORT
/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>.  */
# include <wchar.h>
# include <wctype.h>
#endif

/* We need some of the locale data (the collation sequence information)
   but there is no interface to get this information in general.  Therefore
   we support a correct implementation only in glibc.  */
#ifdef _LIBC
# include "../locale/localeinfo.h"
# include "../locale/elem-hash.h"
# include "../locale/coll-lookup.h"
# include <shlib-compat.h>

# define CONCAT(a,b) __CONCAT(a,b)
# define mbsinit __mbsinit
# define mbsrtowcs __mbsrtowcs
# define fnmatch __fnmatch
extern int fnmatch (const char *pattern, const char *string, int flags);
#endif

/* We often have to test for FNM_FILE_NAME and FNM_PERIOD being both set.  */
#define NO_LEADING_PERIOD(flags) \
  ((flags & (FNM_FILE_NAME | FNM_PERIOD)) == (FNM_FILE_NAME | FNM_PERIOD))

/* Comment out all this code if we are using the GNU C Library, are not
   actually compiling the library itself, and have not detected a bug
   in the library.  This code is part of the GNU C
   Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object files,
   it is simpler to just do this in the source for each such file.  */

#if defined _LIBC || !defined __GNU_LIBRARY__ || !HAVE_FNMATCH_GNU


# if defined STDC_HEADERS || !defined isascii
#  define ISASCII(c) 1
# else
#  define ISASCII(c) isascii(c)
# endif

# ifdef isblank
#  define ISBLANK(c) (ISASCII (c) && isblank (c))
# else
#  define ISBLANK(c) ((c) == ' ' || (c) == '\t')
# endif
# ifdef isgraph
#  define ISGRAPH(c) (ISASCII (c) && isgraph (c))
# else
#  define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
# endif

# define ISPRINT(c) (ISASCII (c) && isprint (c))
# define ISDIGIT(c) (ISASCII (c) && isdigit (c))
# define ISALNUM(c) (ISASCII (c) && isalnum (c))
# define ISALPHA(c) (ISASCII (c) && isalpha (c))
# define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
# define ISLOWER(c) (ISASCII (c) && islower (c))
# define ISPUNCT(c) (ISASCII (c) && ispunct (c))
# define ISSPACE(c) (ISASCII (c) && isspace (c))
# define ISUPPER(c) (ISASCII (c) && isupper (c))
# define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))

# define STREQ(s1, s2) ((strcmp (s1, s2) == 0))

# if defined _LIBC || WIDE_CHAR_SUPPORT
/* The GNU C library provides support for user-defined character classes
   and the functions from ISO C amendement 1.  */
#  ifdef CHARCLASS_NAME_MAX
#   define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
#  else
/* This shouldn't happen but some implementation might still have this
   problem.  Use a reasonable default value.  */
#   define CHAR_CLASS_MAX_LENGTH 256
#  endif

#  ifdef _LIBC
#   define IS_CHAR_CLASS(string) __wctype (string)
#  else
#   define IS_CHAR_CLASS(string) wctype (string)
#  endif

#  ifdef _LIBC
#   define ISWCTYPE(WC, WT) __iswctype (WC, WT)
#  else
#   define ISWCTYPE(WC, WT) iswctype (WC, WT)
#  endif

#  if (HAVE_MBSTATE_T && HAVE_MBSRTOWCS) || _LIBC
/* In this case we are implementing the multibyte character handling.  */
#   define HANDLE_MULTIBYTE 1
#  endif

# else
#  define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */

#  define IS_CHAR_CLASS(string)                           \
   (STREQ (string, "alpha") || STREQ (string, "upper")                \
    || STREQ (string, "lower") || STREQ (string, "digit")             \
    || STREQ (string, "alnum") || STREQ (string, "xdigit")            \
    || STREQ (string, "space") || STREQ (string, "print")             \
    || STREQ (string, "punct") || STREQ (string, "graph")             \
    || STREQ (string, "cntrl") || STREQ (string, "blank"))
# endif

/* Avoid depending on library functions or files
   whose names are inconsistent.  */

# if !defined _LIBC && !defined getenv && !HAVE_DECL_GETENV
extern char *getenv ();
# endif

# ifndef errno
extern int errno;
# endif

/* Global variable.  */
static int posixly_correct;

# ifndef internal_function
/* Inside GNU libc we mark some function in a special way.  In other
   environments simply ignore the marking.  */
#  define internal_function
# endif

/* Note that this evaluates C many times.  */
# ifdef _LIBC
#  define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c))
# else
#  define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
# endif
# define CHAR   char
# define UCHAR  unsigned char
# define INT    int
# define FCT    internal_fnmatch
# define EXT    ext_match
# define END    end_pattern
# define L(CS)  CS
# ifdef _LIBC
#  define BTOWC(C)  __btowc (C)
# else
#  define BTOWC(C)  btowc (C)
# endif
# define STRLEN(S) strlen (S)
# define STRCAT(D, S) strcat (D, S)
# ifdef _LIBC
#  define MEMPCPY(D, S, N) __mempcpy (D, S, N)
# else
#  if HAVE_MEMPCPY
#   define MEMPCPY(D, S, N) mempcpy (D, S, N)
#  else
#   define MEMPCPY(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
#  endif
# endif
# define MEMCHR(S, C, N) memchr (S, C, N)
# define STRCOLL(S1, S2) strcoll (S1, S2)
# include "fnmatch_loop.c"


# if HANDLE_MULTIBYTE
#  define FOLD(c) ((flags & FNM_CASEFOLD) ? towlower (c) : (c))
#  define CHAR  wchar_t
#  define UCHAR wint_t
#  define INT   wint_t
#  define FCT   internal_fnwmatch
#  define EXT   ext_wmatch
#  define END   end_wpattern
#  define L(CS) L##CS
#  define BTOWC(C)  (C)
#  ifdef _LIBC
#   define STRLEN(S) __wcslen (S)
#   define STRCAT(D, S) __wcscat (D, S)
#   define MEMPCPY(D, S, N) __wmempcpy (D, S, N)
#  else
#   define STRLEN(S) wcslen (S)
#   define STRCAT(D, S) wcscat (D, S)
#   if HAVE_WMEMPCPY
#    define MEMPCPY(D, S, N) wmempcpy (D, S, N)
#   else
#    define MEMPCPY(D, S, N) (wmemcpy (D, S, N) + (N))
#   endif
#  endif
#  define MEMCHR(S, C, N) wmemchr (S, C, N)
#  define STRCOLL(S1, S2) wcscoll (S1, S2)
#  define WIDE_CHAR_VERSION 1

#  undef IS_CHAR_CLASS
/* We have to convert the wide character string in a multibyte string.  But
   we know that the character class names consist of alphanumeric characters
   from the portable character set, and since the wide character encoding
   for a member of the portable character set is the same code point as
   its single-byte encoding, we can use a simplified method to convert the
   string to a multibyte character string.  */
static wctype_t
is_char_class (const wchar_t *wcs)
{
  char s[CHAR_CLASS_MAX_LENGTH + 1];
  char *cp = s;

  do
    {
      /* Test for a printable character from the portable character set.  */
#  ifdef _LIBC
      if (*wcs < 0x20 || *wcs > 0x7e
      || *wcs == 0x24 || *wcs == 0x40 || *wcs == 0x60)
    return (wctype_t) 0;
#  else
      switch (*wcs)
    {
    case L' ': case L'!': case L'"': case L'#': case L'%':
    case L'&': case L'\'': case L'(': case L')': case L'*':
    case L'+': case L',': case L'-': case L'.': case L'/':
    case L'0': case L'1': case L'2': case L'3': case L'4':
    case L'5': case L'6': case L'7': case L'8': case L'9':
    case L':': case L';': case L'<': case L'=': case L'>':
    case L'?':
    case L'A': case L'B': case L'C': case L'D': case L'E':
    case L'F': case L'G': case L'H': case L'I': case L'J':
    case L'K': case L'L': case L'M': case L'N': case L'O':
    case L'P': case L'Q': case L'R': case L'S': case L'T':
    case L'U': case L'V': case L'W': case L'X': case L'Y':
    case L'Z':
    case L'[': case L'\\': case L']': case L'^': case L'_':
    case L'a': case L'b': case L'c': case L'd': case L'e':
    case L'f': case L'g': case L'h': case L'i': case L'j':
    case L'k': case L'l': case L'm': case L'n': case L'o':
    case L'p': case L'q': case L'r': case L's': case L't':
    case L'u': case L'v': case L'w': case L'x': case L'y':
    case L'z': case L'{': case L'|': case L'}': case L'~':
      break;
    default:
      return (wctype_t) 0;
    }
#  endif

      /* Avoid overrunning the buffer.  */
      if (cp == s + CHAR_CLASS_MAX_LENGTH)
    return (wctype_t) 0;

      *cp++ = (char) *wcs++;
    }
  while (*wcs != L'\0');

  *cp = '\0';

#  ifdef _LIBC
  return __wctype (s);
#  else
  return wctype (s);
#  endif
}
#  define IS_CHAR_CLASS(string) is_char_class (string)

#  include "fnmatch_loop.c"
# endif


int
fnmatch (pattern, string, flags)
     const char *pattern;
     const char *string;
     int flags;
{
# if HANDLE_MULTIBYTE
  if (__builtin_expect (MB_CUR_MAX, 1) != 1)
    {
      mbstate_t ps;
      size_t n;
      wchar_t *wpattern;
      wchar_t *wstring;

      /* Convert the strings into wide characters.  */
      memset (&ps, '\0', sizeof (ps));
      n = mbsrtowcs (NULL, &pattern, 0, &ps);
      if (__builtin_expect (n, 0) == (size_t) -1)
    /* Something wrong.
       XXX Do we have to set `errno' to something which mbsrtows hasn't
       already done?  */
    return -1;
      wpattern = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
      assert (mbsinit (&ps));
      (void) mbsrtowcs (wpattern, &pattern, n + 1, &ps);

      assert (mbsinit (&ps));
      n = mbsrtowcs (NULL, &string, 0, &ps);
      if (__builtin_expect (n, 0) == (size_t) -1)
    /* Something wrong.
       XXX Do we have to set `errno' to something which mbsrtows hasn't
       already done?  */
    return -1;
      wstring = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
      assert (mbsinit (&ps));
      (void) mbsrtowcs (wstring, &string, n + 1, &ps);

      return internal_fnwmatch (wpattern, wstring, wstring + n,
                flags & FNM_PERIOD, flags);
    }
# endif  /* mbstate_t and mbsrtowcs or _LIBC.  */

  return internal_fnmatch (pattern, string, string + strlen (string),
               flags & FNM_PERIOD, flags);
}

# ifdef _LIBC
#  undef fnmatch
versioned_symbol (libc, __fnmatch, fnmatch, GLIBC_2_2_3);
#  if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_2_3)
strong_alias (__fnmatch, __fnmatch_old)
compat_symbol (libc, __fnmatch_old, fnmatch, GLIBC_2_0);
#  endif
# endif

#endif  /* _LIBC or not __GNU_LIBRARY__.  */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/fopen-safer.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/fopen-safer.c used in: Step 776
/* Invoke fopen, but avoid some glitches.
   Copyright (C) 2001 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.  */

/* Written by Paul Eggert.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <unistd-safer.h>

#ifndef STDERR_FILENO
# define STDERR_FILENO 2
#endif

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#include <stdio.h>
#include <stdio-safer.h>

/* Like fopen, but do not return stdin, stdout, or stderr.  */

FILE *
fopen_safer (char const *file, char const *mode)
{
  FILE *fp = fopen (file, mode);

  if (fp)
    {
      int fd = fileno (fp);

      if (0 <= fd && fd <= STDERR_FILENO)
    {
      int f = dup_safer (fd);

      if (f < 0)
        {
          int e = errno;
          fclose (fp);
          errno = e;
          return NULL;
        }

      if (fclose (fp) != 0
          || ! (fp = fdopen (f, mode)))
        {
          int e = errno;
          close (f);
          errno = e;
          return NULL;
        }
    }
    }

  return fp;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/full-read.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/full-read.c used in: Step 777
/* An interface to read that retries after partial reads and interrupts.
   Copyright (C) 2002, 2003 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.  */

#define FULL_READ
#include "full-write.c"

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/full-write.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/full-write.c used in: Step 777, Step 778
/* An interface to read and write that retries (if necessary) until complete.

   Copyright (C) 1993, 1994, 1997-2003 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#ifdef FULL_READ
# include "full-read.h"
# include "safe-read.h"
# define safe_rw safe_read
# define full_rw full_read
# undef const
# define const /* empty */
#else
# include "full-write.h"
# include "safe-write.h"
# define safe_rw safe_write
# define full_rw full_write
#endif

#ifdef FULL_READ
/* Set errno to zero upon EOF.  */
# define ZERO_BYTE_TRANSFER_ERRNO 0
#else
/* Some buggy drivers return 0 when one tries to write beyond
   a device's end.  (Example: Linux 1.2.13 on /dev/fd0.)
   Set errno to ENOSPC so they get a sensible diagnostic.  */
# define ZERO_BYTE_TRANSFER_ERRNO ENOSPC
#endif

/* Write(read) COUNT bytes at BUF to(from) descriptor FD, retrying if
   interrupted or if a partial write(read) occurs.  Return the number
   of bytes transferred.
   When writing, set errno if fewer than COUNT bytes are written.
   When reading, if fewer than COUNT bytes are read, you must examine
   errno to distinguish failure from EOF (errno == 0).  */
size_t
full_rw (int fd, const void *buf, size_t count)
{
  size_t total = 0;
  const char *ptr = buf;

  while (count > 0)
    {
      size_t n_rw = safe_rw (fd, ptr, count);
      if (n_rw == (size_t) -1)
    break;
      if (n_rw == 0)
    {
      errno = ZERO_BYTE_TRANSFER_ERRNO;
      break;
    }
      total += n_rw;
      ptr += n_rw;
      count -= n_rw;
    }

  return total;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/gethostname.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/gethostname.c used in: Step 779
/* gethostname emulation for SysV and POSIX.1.
   Copyright (C) 1992 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.  */

/* David MacKenzie <djm@gnu.ai.mit.edu> */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifdef HAVE_UNAME
# include <sys/utsname.h>
#endif

/* Put up to LEN chars of the host name into NAME.
   Null terminate it if the name is shorter than LEN.
   Return 0 if ok, -1 if error.  */

int
gethostname (name, len)
     char *name;
     int len;
{
#ifdef HAVE_UNAME
  struct utsname uts;

  if (uname (&uts) == -1)
    return -1;
  if (len > sizeof (uts.nodename))
    {
      /* More space than we need is available.  */
      name[sizeof (uts.nodename)] = '\0';
      len = sizeof (uts.nodename);
    }
  strncpy (name, uts.nodename, len);
#else
  strcpy (name, "");        /* Hardcode your system name if you want.  */
#endif
  return 0;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getline.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getline.c used in: Step 780
/* getline.c -- Replacement for GNU C library function getline

Copyright (C) 1993, 1996, 1997, 1998, 2000 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 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. */

/* Written by Jan Brittenson, bson@gnu.ai.mit.edu.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

/* The `getdelim' function is only declared if the following symbol
   is defined.  */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif

#include <stdio.h>
#include <sys/types.h>

#if defined __GNU_LIBRARY__ && HAVE_GETDELIM

int
getline (char **lineptr, size_t *n, FILE *stream)
{
  return getdelim (lineptr, n, '\n', stream);
}

#else /* ! have getdelim */

# include "getstr.h"

int
getline (char **lineptr, size_t *n, FILE *stream)
{
  return getstr (lineptr, n, stream, '\n', 0, 0);
}

int
getdelim (char **lineptr, size_t *n, int delimiter, FILE *stream)
{
  return getstr (lineptr, n, stream, delimiter, 0, 0);
}
#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getstr.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getstr.c used in: Step 781
/* getstr.c -- core function for GNU C library getline replacement function

   Copyright (C) 1993, 1996-2001 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 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. */

/* Written by Jan Brittenson, bson@gnu.ai.mit.edu.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <sys/types.h>

#if STDC_HEADERS
# include <stdlib.h>
#else
char *malloc (), *realloc ();
#endif

#include "unlocked-io.h"

/* Always add at least this many bytes when extending the buffer.  */
#define MIN_CHUNK 64

/* Read up to (and including) a delimiter DELIM1 from STREAM into *LINEPTR
   + OFFSET (and NUL-terminate it).  If DELIM2 is non-zero, then read up
   and including the first occurrence of DELIM1 or DELIM2.  *LINEPTR is
   a pointer returned from malloc (or NULL), pointing to *N characters of
   space.  It is realloc'd as necessary.  Return the number of characters
   read (not including the NUL terminator), or -1 on error or EOF.  */

int
getstr (char **lineptr, size_t *n, FILE *stream, int delim1, int delim2,
    size_t offset)
{
  size_t nchars_avail;      /* Allocated but unused chars in *LINEPTR.  */
  char *read_pos;       /* Where we're reading into *LINEPTR. */
  int ret;

  if (!lineptr || !n || !stream)
    return -1;

  if (!*lineptr)
    {
      *n = MIN_CHUNK;
      *lineptr = malloc (*n);
      if (!*lineptr)
    return -1;
    }

  if (*n < offset)
    return -1;

  nchars_avail = *n - offset;
  read_pos = *lineptr + offset;

  for (;;)
    {
      register int c = getc (stream);

      /* We always want at least one char left in the buffer, since we
     always (unless we get an error while reading the first char)
     NUL-terminate the line buffer.  */

      if (nchars_avail < 2)
    {
      if (*n > MIN_CHUNK)
        *n *= 2;
      else
        *n += MIN_CHUNK;

      nchars_avail = *n + *lineptr - read_pos;
      *lineptr = realloc (*lineptr, *n);
      if (!*lineptr)
        return -1;
      read_pos = *n - nchars_avail + *lineptr;
    }

      if (c == EOF || ferror (stream))
    {
      /* Return partial line, if any.  */
      if (read_pos == *lineptr)
        return -1;
      else
        break;
    }

      *read_pos++ = c;
      nchars_avail--;

      if (c == delim1 || (delim2 && c == delim2))
    /* Return the line.  */
    break;
    }

  /* Done - NUL terminate and return the number of chars read.  */
  *read_pos = '\0';

  ret = read_pos - (*lineptr + offset);
  return ret;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/gettime.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/gettime.c used in: Step 782
/* gettime -- get the system clock
   Copyright (C) 2002 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.  */

/* Written by Paul Eggert.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include "timespec.h"

/* Get the system time.  */

int
gettime (struct timespec *ts)
{
#if defined CLOCK_REALTIME && HAVE_CLOCK_GETTIME
  if (clock_gettime (CLOCK_REALTIME, ts) == 0)
    return 0;
#endif

  {
    struct timeval tv;
    int r = gettimeofday (&tv, 0);
    if (r == 0)
      {
    ts->tv_sec = tv.tv_sec;
    ts->tv_nsec = tv.tv_usec * 1000;
      }
    return r;
  }
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/hard-locale.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/hard-locale.c used in: Step 783
/* hard-locale.c -- Determine whether a locale is hard.

   Copyright (C) 1997, 1998, 1999, 2002 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_LOCALE_H
# include <locale.h>
#endif

#if HAVE_STDLIB_H
# include <stdlib.h>
#endif

#if HAVE_STRING_H
# include <string.h>
#endif

#include "hard-locale.h"

/* Return nonzero if the current CATEGORY locale is hard, i.e. if you
   can't get away with assuming traditional C or POSIX behavior.  */
int
hard_locale (int category)
{
#if ! HAVE_SETLOCALE
  return 0;
#else

  int hard = 1;
  char const *p = setlocale (category, 0);

  if (p)
    {
# if defined __GLIBC__ && 2 <= __GLIBC__
      if (strcmp (p, "C") == 0 || strcmp (p, "POSIX") == 0)
    hard = 0;
# else
      char *locale = malloc (strlen (p) + 1);
      if (locale)
    {
      strcpy (locale, p);

      /* Temporarily set the locale to the "C" and "POSIX" locales
         to find their names, so that we can determine whether one
         or the other is the caller's locale.  */
      if (((p = setlocale (category, "C"))
           && strcmp (p, locale) == 0)
          || ((p = setlocale (category, "POSIX"))
          && strcmp (p, locale) == 0))
        hard = 0;

      /* Restore the caller's locale.  */
      setlocale (category, locale);
      free (locale);
    }
# endif
    }

  return hard;

#endif
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/human.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/human.c used in: Step 784
/* human.c -- print human readable file size

   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002 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.  */

/* Originally contributed by lm@sgi.com;
   --si, output block size selection, large file support,
   and grouping added by eggert@twinsun.com.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_STDBOOL_H
# include <stdbool.h>
#else
typedef enum {false = 0, true = 1} bool;
#endif

#if HAVE_INTTYPES_H
# include <inttypes.h>
#else
# if HAVE_STDINT_H
#  include <stdint.h>
# endif
#endif
#ifndef SIZE_MAX
# define SIZE_MAX ((size_t) -1)
#endif
#ifndef UINTMAX_MAX
# define UINTMAX_MAX ((uintmax_t) -1)
#endif

#include <limits.h>

#if HAVE_LOCALE_H && HAVE_LOCALECONV
# include <locale.h>
#endif

#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifndef HAVE_DECL_GETENV
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_GETENV
char *getenv ();
#endif

#if HAVE_STRING_H
# include <string.h>
#endif

#if HAVE_STRINGS_H
# include <strings.h>
#endif

#include <stdio.h>
#include <sys/types.h>

#include <gettext.h>
#define _(text) gettext (text)

#include <argmatch.h>
#include <error.h>
#include <xstrtol.h>

#include "human.h"

/* The maximum length of a suffix like "KiB".  */
#define HUMAN_READABLE_SUFFIX_LENGTH_MAX 3

static const char power_letter[] =
{
  0,    /* not used */
  'K',  /* kibi ('k' for kilo is a special case) */
  'M',  /* mega or mebi */
  'G',  /* giga or gibi */
  'T',  /* tera or tebi */
  'P',  /* peta or pebi */
  'E',  /* exa or exbi */
  'Z',  /* zetta or 2**70 */
  'Y'   /* yotta or 2**80 */
};


/* If INEXACT_STYLE is not human_round_to_nearest, and if easily
   possible, adjust VALUE according to the style.  */

static long double
adjust_value (int inexact_style, long double value)
{
  /* Do not use the floorl or ceill functions, as that would mean
     checking for their presence and possibly linking with the
     standard math library, which is a porting pain.  So leave the
     value alone if it is too large to easily round.  */
  if (inexact_style != human_round_to_nearest && value < UINTMAX_MAX)
    {
      uintmax_t u = value;
      value = u + (inexact_style == human_ceiling && u != value);
    }

  return value;
}

/* Group the digits of NUMBER according to the grouping rules of the
   current locale.  NUMBER contains NUMBERLEN digits.  Modify the
   bytes pointed to by NUMBER in place, subtracting 1 from NUMBER for
   each byte inserted.  Return the starting address of the modified
   number.

   To group the digits, use GROUPING and THOUSANDS_SEP as in `struct
   lconv' from <locale.h>.  */

static char *
group_number (char *number, size_t numberlen,
          char const *grouping, char const *thousands_sep)
{
  register char *d;
  size_t grouplen = SIZE_MAX;
  size_t thousands_seplen = strlen (thousands_sep);
  size_t i = numberlen;

  /* The maximum possible value for NUMBERLEN is the number of digits
     in the square of the largest uintmax_t, so double the size of
     uintmax_t before converting to a bound.  302 / 1000 is ceil
     (log10 (2.0)).  Add 1 for integer division truncation.  */
  char buf[2 * sizeof (uintmax_t) * CHAR_BIT * 302 / 1000 + 1];

  memcpy (buf, number, numberlen);
  d = number + numberlen;

  for (;;)
    {
      unsigned char g = *grouping;

      if (g)
    {
      grouplen = g < CHAR_MAX ? g : i;
      grouping++;
    }

      if (i < grouplen)
    grouplen = i;

      d -= grouplen;
      i -= grouplen;
      memcpy (d, buf + i, grouplen);
      if (i == 0)
    return d;

      d -= thousands_seplen;
      memcpy (d, thousands_sep, thousands_seplen);
    }
}

/* Convert N to a human readable format in BUF, using the options OPTS.

   N is expressed in units of FROM_BLOCK_SIZE.  FROM_BLOCK_SIZE must
   be nonnegative.

   Use units of TO_BLOCK_SIZE in the output number.  TO_BLOCK_SIZE
   must be positive.

   Use (OPTS & (human_round_to_nearest | human_floor | human_ceiling))
   to determine whether to take the ceiling or floor of any result
   that cannot be expressed exactly.

   If (OPTS & human_group_digits), group the thousands digits
   according to the locale, e.g., `1,000,000' in an American English
   locale.

   If (OPTS & human_autoscale), deduce the output block size
   automatically; TO_BLOCK_SIZE must be 1 but it has no effect on the
   output.  Use powers of 1024 if (OPTS & human_base_1024), and powers
   of 1000 otherwise.  For example, assuming powers of 1024, 8500
   would be converted to 8.3, 133456345 to 127, 56990456345 to 53, and
   so on.  Numbers smaller than the power aren't modified.
   human_autoscale is normally used together with human_SI.

   If (OPTS & human_SI), append an SI prefix indicating which power is
   being used.  If in addition (OPTS & human_B), append "B" (if base
   1000) or "iB" (if base 1024) to the SI prefix.  When ((OPTS &
   human_SI) && ! (OPTS & human_autoscale)), TO_BLOCK_SIZE must be a
   power of 1024 or of 1000, depending on (OPTS &
   human_base_1024).  */

char *
human_readable (uintmax_t n, char *buf, int opts,
        uintmax_t from_block_size, uintmax_t to_block_size)
{
  int inexact_style =
    opts & (human_round_to_nearest | human_floor | human_ceiling);
  unsigned int base = opts & human_base_1024 ? 1024 : 1000;
  uintmax_t amt;
  int tenths;
  int exponent = -1;
  int exponent_max = sizeof power_letter - 1;
  char *p;
  char *psuffix;
  char const *integerlim;

  /* 0 means adjusted N == AMT.TENTHS;
     1 means AMT.TENTHS < adjusted N < AMT.TENTHS + 0.05;
     2 means adjusted N == AMT.TENTHS + 0.05;
     3 means AMT.TENTHS + 0.05 < adjusted N < AMT.TENTHS + 0.1.  */
  int rounding;

  char const *decimal_point = ".";
  size_t decimal_pointlen = 1;
  char const *grouping = "";
  char const *thousands_sep = "";
#if HAVE_LOCALE_H && HAVE_LOCALECONV
  struct lconv const *l = localeconv ();
  size_t pointlen = strlen (l->decimal_point);
  if (0 < pointlen && pointlen <= MB_LEN_MAX)
    {
      decimal_point = l->decimal_point;
      decimal_pointlen = pointlen;
    }
  grouping = l->grouping;
  if (strlen (l->thousands_sep) <= MB_LEN_MAX)
    thousands_sep = l->thousands_sep;
#endif

  psuffix = buf + LONGEST_HUMAN_READABLE - HUMAN_READABLE_SUFFIX_LENGTH_MAX;
  p = psuffix;

  /* Adjust AMT out of FROM_BLOCK_SIZE units and into TO_BLOCK_SIZE
     units.  If this can be done exactly with integer arithmetic, do
     not use floating point operations.  */
  if (to_block_size <= from_block_size)
    {
      if (from_block_size % to_block_size == 0)
    {
      uintmax_t multiplier = from_block_size / to_block_size;
      amt = n * multiplier;
      if (amt / multiplier == n)
        {
          tenths = 0;
          rounding = 0;
          goto use_integer_arithmetic;
        }
    }
    }
  else if (from_block_size != 0 && to_block_size % from_block_size == 0)
    {
      uintmax_t divisor = to_block_size / from_block_size;
      uintmax_t r10 = (n % divisor) * 10;
      uintmax_t r2 = (r10 % divisor) * 2;
      amt = n / divisor;
      tenths = r10 / divisor;
      rounding = r2 < divisor ? 0 < r2 : 2 + (divisor < r2);
      goto use_integer_arithmetic;
    }

  {
    /* Either the result cannot be computed easily using uintmax_t,
       or from_block_size is zero.  Fall back on floating point.
       FIXME: This can yield answers that are slightly off.  */

    long double dto_block_size = to_block_size;
    long double damt = n * (from_block_size / dto_block_size);
    size_t buflen;
    size_t nonintegerlen;

    if (! (opts & human_autoscale))
      {
    sprintf (buf, "%.0Lf", adjust_value (inexact_style, damt));
    buflen = strlen (buf);
    nonintegerlen = 0;
      }
    else
      {
    long double e = 1;
    exponent = 0;

    do
      {
        e *= base;
        exponent++;
      }
    while (e * base <= damt && exponent < exponent_max);

    damt /= e;

    sprintf (buf, "%.1Lf", adjust_value (inexact_style, damt));
    buflen = strlen (buf);
    nonintegerlen = decimal_pointlen + 1;

    if (1 + nonintegerlen + ! (opts & human_base_1024) < buflen
        || ((opts & human_suppress_point_zero)
        && buf[buflen - 1] == '0'))
      {
        sprintf (buf, "%.0Lf",
             adjust_value (inexact_style, damt * 10) / 10);
        buflen = strlen (buf);
        nonintegerlen = 0;
      }
      }

    p = psuffix - buflen;
    memmove (p, buf, buflen);
    integerlim = p + buflen - nonintegerlen;
  }
  goto do_grouping;

 use_integer_arithmetic:
  {
    /* The computation can be done exactly, with integer arithmetic.

       Use power of BASE notation if requested and if adjusted AMT is
       large enough.  */

    if (opts & human_autoscale)
      {
    exponent = 0;

    if (base <= amt)
      {
        do
          {
        unsigned r10 = (amt % base) * 10 + tenths;
        unsigned r2 = (r10 % base) * 2 + (rounding >> 1);
        amt /= base;
        tenths = r10 / base;
        rounding = (r2 < base
                ? (r2 + rounding) != 0
                : 2 + (base < r2 + rounding));
        exponent++;
          }
        while (base <= amt && exponent < exponent_max);

        if (amt < 10)
          {
        if (inexact_style == human_round_to_nearest
            ? 2 < rounding + (tenths & 1)
            : inexact_style == human_ceiling && 0 < rounding)
          {
            tenths++;
            rounding = 0;

            if (tenths == 10)
              {
            amt++;
            tenths = 0;
              }
          }

        if (amt < 10
            && (tenths || ! (opts & human_suppress_point_zero)))
          {
            *--p = '0' + tenths;
            p -= decimal_pointlen;
            memcpy (p, decimal_point, decimal_pointlen);
            tenths = rounding = 0;
          }
          }
      }
      }

    if (inexact_style == human_ceiling
    ? 0 < tenths + rounding
    : inexact_style == human_round_to_nearest
    ? 5 < tenths + (2 < rounding + (amt & 1))
    : /* inexact_style == human_floor */ 0)
      {
    amt++;

    if ((opts & human_autoscale)
        && amt == base && exponent < exponent_max)
      {
        exponent++;
        if (! (opts & human_suppress_point_zero))
          {
        *--p = '0';
        p -= decimal_pointlen;
        memcpy (p, decimal_point, decimal_pointlen);
          }
        amt = 1;
      }
      }

    integerlim = p;

    do
      {
    int digit = amt % 10;
    *--p = digit + '0';
      }
    while ((amt /= 10) != 0);
  }

 do_grouping:
  if (opts & human_group_digits)
    p = group_number (p, integerlim - p, grouping, thousands_sep);

  if (opts & human_SI)
    {
      if (exponent < 0)
    {
      uintmax_t power;
      exponent = 0;
      for (power = 1; power < to_block_size; power *= base)
        if (++exponent == exponent_max)
          break;
    }

      if (exponent)
    *psuffix++ = (! (opts & human_base_1024) && exponent == 1
              ? 'k'
              : power_letter[exponent]);

      if (opts & human_B)
    {
      if ((opts & human_base_1024) && exponent)
        *psuffix++ = 'i';
      *psuffix++ = 'B';
    }
    }

  *psuffix = '\0';

  return p;
}


/* The default block size used for output.  This number may change in
   the future as disks get larger.  */
#ifndef DEFAULT_BLOCK_SIZE
# define DEFAULT_BLOCK_SIZE 1024
#endif

static char const *const block_size_args[] = { "human-readable", "si", 0 };
static int const block_size_opts[] =
  {
    human_autoscale + human_SI + human_base_1024,
    human_autoscale + human_SI
  };

static uintmax_t
default_block_size (void)
{
  return getenv ("POSIXLY_CORRECT") ? 512 : DEFAULT_BLOCK_SIZE;
}

static strtol_error
humblock (char const *spec, uintmax_t *block_size, int *options)
{
  int i;
  int opts = 0;

  if (! spec && ! (spec = getenv ("BLOCK_SIZE")))
    *block_size = default_block_size ();
  else
    {
      if (*spec == '\'')
    {
      opts |= human_group_digits;
      spec++;
    }

      if (0 <= (i = ARGMATCH (spec, block_size_args, block_size_opts)))
    {
      opts |= block_size_opts[i];
      *block_size = 1;
    }
      else
    {
      char *ptr;
      strtol_error e = xstrtoumax (spec, &ptr, 0, block_size,
                       "eEgGkKmMpPtTyYzZ0");
      if (e != LONGINT_OK)
        return e;
      if (*ptr)
        return LONGINT_INVALID_SUFFIX_CHAR;
      for (; ! ('0' <= *spec && *spec <= '9'); spec++)
        if (spec == ptr)
          {
        opts |= human_SI;
        if (ptr[-1] == 'B')
          opts |= human_B;
        if (ptr[-1] != 'B' || ptr[-2] == 'i')
          opts |= human_base_1024;
        break;
          }
    }
    }

  *options = opts;
  return LONGINT_OK;
}

int
human_options (char const *spec, bool report_errors, uintmax_t *block_size)
{
  int opts;
  strtol_error e = humblock (spec, block_size, &opts);
  if (*block_size == 0)
    {
      *block_size = default_block_size ();
      e = LONGINT_INVALID;
    }
  if (e != LONGINT_OK && report_errors)
    STRTOL_FATAL_ERROR (spec, _("block size"), e);
  return opts;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/idcache.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/idcache.c used in: Step 785
/* idcache.c -- map user and group IDs, cached for speed
   Copyright (C) 1985, 1988, 1989, 1990, 1997, 1998, 2003 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>

#if STDC_HEADERS || HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif

#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#include "xalloc.h"

#ifndef _POSIX_VERSION
struct passwd *getpwuid ();
struct passwd *getpwnam ();
struct group *getgrgid ();
struct group *getgrnam ();
#endif

#ifdef __DJGPP__
static char digits[] = "0123456789";
#endif

struct userid
{
  union
    {
      uid_t u;
      gid_t g;
    } id;
  char *name;
  struct userid *next;
};

static struct userid *user_alist;

/* The members of this list have names not in the local passwd file.  */
static struct userid *nouser_alist;

/* Translate UID to a login name, with cache, or NULL if unresolved.  */

char *
getuser (uid_t uid)
{
  register struct userid *tail;
  struct passwd *pwent;

  for (tail = user_alist; tail; tail = tail->next)
    if (tail->id.u == uid)
      return tail->name;

  pwent = getpwuid (uid);
  tail = (struct userid *) xmalloc (sizeof (struct userid));
  tail->id.u = uid;
  tail->name = pwent ? xstrdup (pwent->pw_name) : NULL;

  /* Add to the head of the list, so most recently used is first.  */
  tail->next = user_alist;
  user_alist = tail;
  return tail->name;
}

/* Translate USER to a UID, with cache.
   Return NULL if there is no such user.
   (We also cache which user names have no passwd entry,
   so we don't keep looking them up.)  */

uid_t *
getuidbyname (const char *user)
{
  register struct userid *tail;
  struct passwd *pwent;

  for (tail = user_alist; tail; tail = tail->next)
    /* Avoid a function call for the most common case.  */
    if (*tail->name == *user && !strcmp (tail->name, user))
      return &tail->id.u;

  for (tail = nouser_alist; tail; tail = tail->next)
    /* Avoid a function call for the most common case.  */
    if (*tail->name == *user && !strcmp (tail->name, user))
      return 0;

  pwent = getpwnam (user);
#ifdef __DJGPP__
  /* We need to pretend to be the user USER, to make
     pwd functions know about an arbitrary user name.  */
  if (!pwent && strspn (user, digits) < strlen (user))
    {
      setenv ("USER", user, 1);
      pwent = getpwnam (user);  /* now it will succeed */
    }
#endif

  tail = (struct userid *) xmalloc (sizeof (struct userid));
  tail->name = xstrdup (user);

  /* Add to the head of the list, so most recently used is first.  */
  if (pwent)
    {
      tail->id.u = pwent->pw_uid;
      tail->next = user_alist;
      user_alist = tail;
      return &tail->id.u;
    }

  tail->next = nouser_alist;
  nouser_alist = tail;
  return 0;
}

/* Use the same struct as for userids.  */
static struct userid *group_alist;
static struct userid *nogroup_alist;

/* Translate GID to a group name, with cache, or NULL if unresolved.  */

char *
getgroup (gid_t gid)
{
  register struct userid *tail;
  struct group *grent;

  for (tail = group_alist; tail; tail = tail->next)
    if (tail->id.g == gid)
      return tail->name;

  grent = getgrgid (gid);
  tail = (struct userid *) xmalloc (sizeof (struct userid));
  tail->id.g = gid;
  tail->name = grent ? xstrdup (grent->gr_name) : NULL;

  /* Add to the head of the list, so most recently used is first.  */
  tail->next = group_alist;
  group_alist = tail;
  return tail->name;
}

/* Translate GROUP to a GID, with cache.
   Return NULL if there is no such group.
   (We also cache which group names have no group entry,
   so we don't keep looking them up.)  */

gid_t *
getgidbyname (const char *group)
{
  register struct userid *tail;
  struct group *grent;

  for (tail = group_alist; tail; tail = tail->next)
    /* Avoid a function call for the most common case.  */
    if (*tail->name == *group && !strcmp (tail->name, group))
      return &tail->id.g;

  for (tail = nogroup_alist; tail; tail = tail->next)
    /* Avoid a function call for the most common case.  */
    if (*tail->name == *group && !strcmp (tail->name, group))
      return 0;

  grent = getgrnam (group);
#ifdef __DJGPP__
  /* We need to pretend to belong to group GROUP, to make
     grp functions know about any arbitrary group name.  */
  if (!grent && strspn (group, digits) < strlen (group))
    {
      setenv ("GROUP", group, 1);
      grent = getgrnam (group); /* now it will succeed */
    }
#endif

  tail = (struct userid *) xmalloc (sizeof (struct userid));
  tail->name = xstrdup (group);

  /* Add to the head of the list, so most recently used is first.  */
  if (grent)
    {
      tail->id.g = grent->gr_gid;
      tail->next = group_alist;
      group_alist = tail;
      return &tail->id.g;
    }

  tail->next = nogroup_alist;
  nogroup_alist = tail;
  return 0;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/isdir.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/isdir.c used in: Step 786
/* isdir.c -- determine whether a directory exists
   Copyright (C) 1990, 1998 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>

#if STAT_MACROS_BROKEN
# undef S_ISDIR
#endif

#if !defined S_ISDIR && defined S_IFDIR
# define S_ISDIR(Mode) (((Mode) & S_IFMT) == S_IFDIR)
#endif

/* If PATH is an existing directory or symbolic link to a directory,
   return nonzero, else 0.  */

int
isdir (const char *path)
{
  struct stat stats;

  return stat (path, &stats) == 0 && S_ISDIR (stats.st_mode);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/imaxtostr.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/imaxtostr.c used in: Step 787
#define inttostr imaxtostr
#define inttype intmax_t
#include "inttostr.c"

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/inttostr.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/inttostr.c used in: Step 787, Step 797, Step 815
/* inttostr.c -- convert integers to printable strings

   Copyright (C) 2001 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.  */

/* Written by Paul Eggert */

#include "inttostr.h"

/* Convert I to a printable string in BUF, which must be at least
   INT_BUFSIZE_BOUND (INTTYPE) bytes long.  Return the address of the
   printable string, which need not start at BUF.  */

char *
inttostr (inttype i, char *buf)
{
  char *p = buf + INT_STRLEN_BOUND (inttype);
  *p = 0;

  if (i < 0)
    {
      do
    *--p = '0' - i % 10;
      while ((i /= 10) != 0);

      *--p = '-';
    }
  else
    {
      do
    *--p = '0' + i % 10;
      while ((i /= 10) != 0);
    }

  return p;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/inttostr.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/inttostr.h used in: Step 787, Step 797, Step 815, Step 855, Step 865, Step 867, Step 923, Step 929
/* inttostr.h -- convert integers to printable strings

   Copyright (C) 2001, 2002 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.  */

/* Written by Paul Eggert */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif

#if HAVE_LIMITS_H
# include <limits.h>
#endif
#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif

#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif

/* Upper bound on the string length of an integer converted to string.
   302 / 1000 is ceil (log10 (2.0)).  Subtract 1 for the sign bit;
   add 1 for integer division truncation; add 1 more for a minus sign.  */
#define INT_STRLEN_BOUND(t) ((sizeof (t) * CHAR_BIT - 1) * 302 / 1000 + 2)

#define INT_BUFSIZE_BOUND(t) (INT_STRLEN_BOUND (t) + 1)

#ifndef PARAMS
# if defined PROTOTYPES || defined __STDC__
#  define PARAMS(Args) Args
# else
#  define PARAMS(Args) ()
# endif
#endif

char *offtostr PARAMS ((off_t, char *));
char *imaxtostr PARAMS ((intmax_t, char *));
char *umaxtostr PARAMS ((uintmax_t, char *));

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/linebuffer.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/linebuffer.c used in: Step 788
/* linebuffer.c -- read arbitrarily long lines
   Copyright (C) 1986, 1991, 1998, 1999, 2001 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.  */

/* Written by Richard Stallman. */


#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <sys/types.h>
#include "linebuffer.h"
#include "unlocked-io.h"
#include "xalloc.h"

void free ();

/* Initialize linebuffer LINEBUFFER for use. */

void
initbuffer (struct linebuffer *linebuffer)
{
  linebuffer->length = 0;
  linebuffer->size = 200;
  linebuffer->buffer = xmalloc (linebuffer->size);
}

/* Read an arbitrarily long line of text from STREAM into LINEBUFFER.
   Keep the newline; append a newline if it's the last line of a file
   that ends in a non-newline character.  Do not null terminate.
   Therefore the stream can contain NUL bytes, and the length
   (including the newline) is returned in linebuffer->length.
   Return NULL upon error, or when STREAM is empty.
   Otherwise, return LINEBUFFER.  */
struct linebuffer *
readline (struct linebuffer *linebuffer, FILE *stream)
{
  int c;
  char *buffer = linebuffer->buffer;
  char *p = linebuffer->buffer;
  char *end = buffer + linebuffer->size; /* Sentinel. */

  if (feof (stream) || ferror (stream))
    return NULL;

  do
    {
      c = getc (stream);
      if (c == EOF)
    {
      if (p == buffer)
        return NULL;
      if (p[-1] == '\n')
        break;
      c = '\n';
    }
      if (p == end)
    {
      linebuffer->size *= 2;
      buffer = xrealloc (buffer, linebuffer->size);
      p = p - linebuffer->buffer + buffer;
      linebuffer->buffer = buffer;
      end = buffer + linebuffer->size;
    }
      *p++ = c;
    }
  while (c != '\n');

  linebuffer->length = p - buffer;
  return linebuffer;
}

/* Free linebuffer LINEBUFFER and its data, all allocated with malloc. */

void
freebuffer (struct linebuffer *linebuffer)
{
  free (linebuffer->buffer);
  free (linebuffer);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/localcharset.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/localcharset.c used in: Step 789
/* Determine a canonical name for the current locale's character encoding.

   Copyright (C) 2000-2002 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU Library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library 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.  */

/* Written by Bruno Haible <bruno@clisp.org>.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_STDDEF_H
# include <stddef.h>
#endif

#include <stdio.h>
#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif

#if defined _WIN32 || defined __WIN32__
# undef WIN32   /* avoid warning on mingw32 */
# define WIN32
#endif

#if defined __EMX__
/* Assume EMX program runs on OS/2, even if compiled under DOS.  */
# define OS2
#endif

#if !defined WIN32
# if HAVE_LANGINFO_CODESET
#  include <langinfo.h>
# else
#  if HAVE_SETLOCALE
#   include <locale.h>
#  endif
# endif
#elif defined WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif
#if defined OS2
# define INCL_DOS
# include <os2.h>
#endif

#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
  /* Win32, OS/2, DOS */
# define ISSLASH(C) ((C) == '/' || (C) == '\\')
#endif

#ifndef DIRECTORY_SEPARATOR
# define DIRECTORY_SEPARATOR '/'
#endif

#ifndef ISSLASH
# define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR)
#endif

#ifdef HAVE_GETC_UNLOCKED
# undef getc
# define getc getc_unlocked
#endif

#ifdef __cplusplus
/* When compiling with "gcc -x c++", produce a function with C linkage.  */
extern "C" const char * locale_charset (void);
#endif

/* The following static variable is declared 'volatile' to avoid a
   possible multithread problem in the function get_charset_aliases. If we
   are running in a threaded environment, and if two threads initialize
   'charset_aliases' simultaneously, both will produce the same value,
   and everything will be ok if the two assignments to 'charset_aliases'
   are atomic. But I don't know what will happen if the two assignments mix.  */
#if __STDC__ != 1
# define volatile /* empty */
#endif
/* Pointer to the contents of the charset.alias file, if it has already been
   read, else NULL.  Its format is:
   ALIAS_1 '\0' CANONICAL_1 '\0' ... ALIAS_n '\0' CANONICAL_n '\0' '\0'  */
static const char * volatile charset_aliases;

/* Return a pointer to the contents of the charset.alias file.  */
static const char *
get_charset_aliases ()
{
  const char *cp;

  cp = charset_aliases;
  if (cp == NULL)
    {
#if !defined WIN32
      FILE *fp;
      const char *dir = LIBDIR;
      const char *base = "charset.alias";
      char *file_name;

      /* Concatenate dir and base into freshly allocated file_name.  */
      {
    size_t dir_len = strlen (dir);
    size_t base_len = strlen (base);
    int add_slash = (dir_len > 0 && !ISSLASH (dir[dir_len - 1]));
    file_name = (char *) malloc (dir_len + add_slash + base_len + 1);
    if (file_name != NULL)
      {
        memcpy (file_name, dir, dir_len);
        if (add_slash)
          file_name[dir_len] = DIRECTORY_SEPARATOR;
        memcpy (file_name + dir_len + add_slash, base, base_len + 1);
      }
      }

      if (file_name == NULL || (fp = fopen (file_name, "r")) == NULL)
    /* Out of memory or file not found, treat it as empty.  */
    cp = "";
      else
    {
      /* Parse the file's contents.  */
      int c;
      char buf1[50+1];
      char buf2[50+1];
      char *res_ptr = NULL;
      size_t res_size = 0;
      size_t l1, l2;

      for (;;)
        {
          c = getc (fp);
          if (c == EOF)
        break;
          if (c == '\n' || c == ' ' || c == '\t')
        continue;
          if (c == '#')
        {
          /* Skip comment, to end of line.  */
          do
            c = getc (fp);
          while (!(c == EOF || c == '\n'));
          if (c == EOF)
            break;
          continue;
        }
          ungetc (c, fp);
          if (fscanf (fp, "%50s %50s", buf1, buf2) < 2)
        break;
          l1 = strlen (buf1);
          l2 = strlen (buf2);
          if (res_size == 0)
        {
          res_size = l1 + 1 + l2 + 1;
          res_ptr = (char *) malloc (res_size + 1);
        }
          else
        {
          res_size += l1 + 1 + l2 + 1;
          res_ptr = (char *) realloc (res_ptr, res_size + 1);
        }
          if (res_ptr == NULL)
        {
          /* Out of memory. */
          res_size = 0;
          break;
        }
          strcpy (res_ptr + res_size - (l2 + 1) - (l1 + 1), buf1);
          strcpy (res_ptr + res_size - (l2 + 1), buf2);
        }
      fclose (fp);
      if (res_size == 0)
        cp = "";
      else
        {
          *(res_ptr + res_size) = '\0';
          cp = res_ptr;
        }
    }

      if (file_name != NULL)
    free (file_name);

#else

      /* To avoid the troubles of installing a separate file in the same
     directory as the DLL and of retrieving the DLL's directory at
     runtime, simply inline the aliases here.  */

# if defined WIN32
      cp = "CP936" "\0" "GBK" "\0"
       "CP1361" "\0" "JOHAB" "\0"
       "CP20127" "\0" "ASCII" "\0"
       "CP20866" "\0" "KOI8-R" "\0"
       "CP21866" "\0" "KOI8-RU" "\0"
       "CP28591" "\0" "ISO-8859-1" "\0"
       "CP28592" "\0" "ISO-8859-2" "\0"
       "CP28593" "\0" "ISO-8859-3" "\0"
       "CP28594" "\0" "ISO-8859-4" "\0"
       "CP28595" "\0" "ISO-8859-5" "\0"
       "CP28596" "\0" "ISO-8859-6" "\0"
       "CP28597" "\0" "ISO-8859-7" "\0"
       "CP28598" "\0" "ISO-8859-8" "\0"
       "CP28599" "\0" "ISO-8859-9" "\0"
       "CP28605" "\0" "ISO-8859-15" "\0";
# endif
#endif

      charset_aliases = cp;
    }

  return cp;
}

/* Determine the current locale's character encoding, and canonicalize it
   into one of the canonical names listed in config.charset.
   The result must not be freed; it is statically allocated.
   If the canonical name cannot be determined, the result is a non-canonical
   name.  */

#ifdef STATIC
STATIC
#endif
const char *
locale_charset ()
{
  const char *codeset;
  const char *aliases;

#if !(defined WIN32 || defined OS2)

# if HAVE_LANGINFO_CODESET

  /* Most systems support nl_langinfo (CODESET) nowadays.  */
  codeset = nl_langinfo (CODESET);

# else

  /* On old systems which lack it, use setlocale or getenv.  */
  const char *locale = NULL;

  /* But most old systems don't have a complete set of locales.  Some
     (like SunOS 4 or DJGPP) have only the C locale.  Therefore we don't
     use setlocale here; it would return "C" when it doesn't support the
     locale name the user has set.  */
#  if HAVE_SETLOCALE && 0
  locale = setlocale (LC_CTYPE, NULL);
#  endif
  if (locale == NULL || locale[0] == '\0')
    {
      locale = getenv ("LC_ALL");
      if (locale == NULL || locale[0] == '\0')
    {
      locale = getenv ("LC_CTYPE");
      if (locale == NULL || locale[0] == '\0')
        locale = getenv ("LANG");
    }
    }

  /* On some old systems, one used to set locale = "iso8859_1". On others,
     you set it to "language_COUNTRY.charset". In any case, we resolve it
     through the charset.alias file.  */
  codeset = locale;

# endif

#elif defined WIN32

  static char buf[2 + 10 + 1];

  /* Woe32 has a function returning the locale's codepage as a number.  */
  sprintf (buf, "CP%u", GetACP ());
  codeset = buf;

#elif defined OS2

  const char *locale;
  static char buf[2 + 10 + 1];
  ULONG cp[3];
  ULONG cplen;

  /* Allow user to override the codeset, as set in the operating system,
     with standard language environment variables.  */
  locale = getenv ("LC_ALL");
  if (locale == NULL || locale[0] == '\0')
    {
      locale = getenv ("LC_CTYPE");
      if (locale == NULL || locale[0] == '\0')
    locale = getenv ("LANG");
    }
  if (locale != NULL && locale[0] != '\0')
    {
      /* If the locale name contains an encoding after the dot, return it.  */
      const char *dot = strchr (locale, '.');

      if (dot != NULL)
    {
      const char *modifier;

      dot++;
      /* Look for the possible @... trailer and remove it, if any.  */
      modifier = strchr (dot, '@');
      if (modifier == NULL)
        return dot;
      if (modifier - dot < sizeof (buf))
        {
          memcpy (buf, dot, modifier - dot);
          buf [modifier - dot] = '\0';
          return buf;
        }
    }

      /* Resolve through the charset.alias file.  */
      codeset = locale;
    }
  else
    {
      /* OS/2 has a function returning the locale's codepage as a number.  */
      if (DosQueryCp (sizeof (cp), cp, &cplen))
    codeset = "";
      else
    {
      sprintf (buf, "CP%u", cp[0]);
      codeset = buf;
    }
    }

#endif

  if (codeset == NULL)
    /* The canonical name cannot be determined.  */
    codeset = "";

  /* Resolve alias. */
  for (aliases = get_charset_aliases ();
       *aliases != '\0';
       aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1)
    if (strcmp (codeset, aliases) == 0
    || (aliases[0] == '*' && aliases[1] == '\0'))
      {
    codeset = aliases + strlen (aliases) + 1;
    break;
      }

  /* Don't return an empty string.  GNU libc and GNU libiconv interpret
     the empty string as denoting "the locale's character encoding",
     thus GNU libiconv would call this function a second time.  */
  if (codeset[0] == '\0')
    codeset = "ASCII";

  return codeset;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/long-options.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/long-options.c used in: Step 790
/* Utility to accept --help and --version options as unobtrusively as possible.
   Copyright (C) 1993-1994, 1998-1999, 2000, 2002 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.  */

/* Written by Jim Meyering.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <getopt.h>
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif

#include "long-options.h"
#include "version-etc.h"

static struct option const long_options[] =
{
  {"help", no_argument, 0, 'h'},
  {"version", no_argument, 0, 'v'},
  {0, 0, 0, 0}
};

/* Process long options --help and --version, but only if argc == 2.
   Be careful not to gobble up `--'.  */

void
parse_long_options (int argc,
            char **argv,
            const char *command_name,
            const char *package,
            const char *version,
            const char *authors,
            void (*usage_func)())
{
  int c;
  int saved_opterr;

  saved_opterr = opterr;

  /* Don't print an error message for unrecognized options.  */
  opterr = 0;

  if (argc == 2
      && (c = getopt_long (argc, argv, "+", long_options, NULL)) != -1)
    {
      switch (c)
    {
    case 'h':
      (*usage_func) (0);

    case 'v':
      version_etc (stdout, command_name, package, version, authors);
      exit (0);

    default:
      /* Don't process any other long-named options.  */
      break;
    }
    }

  /* Restore previous value.  */
  opterr = saved_opterr;

  /* Reset this to zero so that getopt internals get initialized from
     the probably-new parameters when/if getopt is called later.  */
  optind = 0;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/makepath.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/makepath.c used in: Step 791
/* makepath.c -- Ensure that a directory path exists.
   Copyright (C) 1990, 1997, 1998, 1999, 2000, 2002, 2003 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.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu> and Jim Meyering.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if __GNUC__
# define alloca __builtin_alloca
#else
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
 #  pragma alloca
#  else
char *alloca ();
#  endif
# endif
#endif

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#if STAT_MACROS_BROKEN
# undef S_ISDIR
#endif

#if !defined S_ISDIR && defined S_IFDIR
# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif

#ifndef S_IRWXUGO
# define S_IRWXUGO (S_IRWXU | S_IRWXG | S_IRWXO)
#endif

#if STDC_HEADERS
# include <stdlib.h>
#endif

#include <errno.h>

#ifndef errno
extern int errno;
#endif

#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
# ifndef strchr
#  define strchr index
# endif
#endif

#ifndef S_ISUID
# define S_ISUID 04000
#endif
#ifndef S_ISGID
# define S_ISGID 02000
#endif
#ifndef S_ISVTX
# define S_ISVTX 01000
#endif
#ifndef S_IRUSR
# define S_IRUSR 0200
#endif
#ifndef S_IWUSR
# define S_IWUSR 0200
#endif
#ifndef S_IXUSR
# define S_IXUSR 0100
#endif

#ifndef S_IRWXU
# define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
#endif

#define WX_USR (S_IWUSR | S_IXUSR)

/* Include this before libintl.h so we get our definition of PARAMS. */
#include "makepath.h"

#include "gettext.h"
#define _(msgid) gettext (msgid)

#include "save-cwd.h"
#include "dirname.h"
#include "error.h"
#include "quote.h"

#define CLEANUP_CWD                 \
  do                            \
    {                           \
      /* We're done operating on basename_dir.      \
     Restore working directory.  */         \
      if (do_chdir)                 \
    {                       \
      if (restore_cwd (&cwd) != 0)          \
        {                       \
          int _saved_errno = errno;         \
          error (0, errno,              \
        _("failed to return to initial working directory")); \
          free_cwd (&cwd);              \
          errno = _saved_errno;         \
          return 1;                 \
        }                       \
      free_cwd (&cwd);              \
    }                       \
    }                           \
  while (0)

#define CLEANUP                     \
  do                            \
    {                           \
      umask (oldmask);                  \
      CLEANUP_CWD;                  \
    }                           \
  while (0)

/* Attempt to create directory DIR (aka DIRPATH) with the specified MODE.
   If CREATED_DIR_P is non-NULL, set *CREATED_DIR_P to non-zero if this
   function creates DIR and to zero otherwise.  Give a diagnostic and
   return non-zero if DIR cannot be created or cannot be determined to
   exist already.  Use DIRPATH in any diagnostic, not DIR.
   Note that if DIR already exists, this function returns zero
   (indicating success) and sets *CREATED_DIR_P to zero.  */

int
make_dir (const char *dir, const char *dirpath, mode_t mode, int *created_dir_p)
{
  int fail = 0;
  int created_dir;

  created_dir = (mkdir (dir, mode) == 0);

  if (!created_dir)
    {
      struct stat stats;
      int saved_errno = errno;

      /* The mkdir and stat calls below may appear to be reversed.
     They are not.  It is important to call mkdir first and then to
     call stat (to distinguish the three cases) only if mkdir fails.
     The alternative to this approach is to `stat' each directory,
     then to call mkdir if it doesn't exist.  But if some other process
     were to create the directory between the stat & mkdir, the mkdir
     would fail with EEXIST.  */

      if (stat (dir, &stats))
    {
      error (0, saved_errno, _("cannot create directory %s"),
         quote (dirpath));
      fail = 1;
    }
      else if (!S_ISDIR (stats.st_mode))
    {
      error (0, 0, _("%s exists but is not a directory"), quote (dirpath));
      fail = 1;
    }
      else
    {
      /* DIR (aka DIRPATH) already exists and is a directory. */
    }
    }

  if (created_dir_p)
    *created_dir_p = created_dir;

  return fail;
}

/* Ensure that the directory ARGPATH exists.

   Create any leading directories that don't already exist, with
   permissions PARENT_MODE.
   If the last element of ARGPATH does not exist, create it as
   a new directory with permissions MODE.
   If OWNER and GROUP are non-negative, use them to set the UID and GID of
   any created directories.
   If VERBOSE_FMT_STRING is nonzero, use it as a printf format
   string for printing a message after successfully making a directory,
   with the name of the directory that was just made as an argument.
   If PRESERVE_EXISTING is non-zero and ARGPATH is an existing directory,
   then do not attempt to set its permissions and ownership.

   Return 0 if ARGPATH exists as a directory with the proper
   ownership and permissions when done, otherwise 1.  */

int
make_path (const char *argpath,
       int mode,
       int parent_mode,
       uid_t owner,
       gid_t group,
       int preserve_existing,
       const char *verbose_fmt_string)
{
  struct stat stats;
  int retval = 0;

  if (stat (argpath, &stats))
    {
      char *slash;
      int tmp_mode;     /* Initial perms for leading dirs.  */
      int re_protect;       /* Should leading dirs be unwritable? */
      struct ptr_list
      {
    char *dirname_end;
    struct ptr_list *next;
      };
      struct ptr_list *p, *leading_dirs = NULL;
      int do_chdir;     /* Whether to chdir before each mkdir.  */
      struct saved_cwd cwd;
      char *basename_dir;
      char *dirpath;

      /* Temporarily relax umask in case it's overly restrictive.  */
      mode_t oldmask = umask (0);

      /* Make a copy of ARGPATH that we can scribble NULs on.  */
      dirpath = (char *) alloca (strlen (argpath) + 1);
      strcpy (dirpath, argpath);
      strip_trailing_slashes (dirpath);

      /* If leading directories shouldn't be writable or executable,
     or should have set[ug]id or sticky bits set and we are setting
     their owners, we need to fix their permissions after making them.  */
      if (((parent_mode & WX_USR) != WX_USR)
      || ((owner != (uid_t) -1 || group != (gid_t) -1)
          && (parent_mode & (S_ISUID | S_ISGID | S_ISVTX)) != 0))
    {
      tmp_mode = S_IRWXU;
      re_protect = 1;
    }
      else
    {
      tmp_mode = parent_mode;
      re_protect = 0;
    }

      /* If we can record the current working directory, we may be able
     to do the chdir optimization.  */
      do_chdir = !save_cwd (&cwd);

      /* If we've saved the cwd and DIRPATH is an absolute pathname,
     we must chdir to `/' in order to enable the chdir optimization.
         So if chdir ("/") fails, turn off the optimization.  */
      if (do_chdir && *dirpath == '/' && chdir ("/") < 0)
    do_chdir = 0;

      slash = dirpath;

      /* Skip over leading slashes.  */
      while (*slash == '/')
    slash++;

      while (1)
    {
      int newly_created_dir;
      int fail;

      /* slash points to the leftmost unprocessed component of dirpath.  */
      basename_dir = slash;

      slash = strchr (slash, '/');
      if (slash == NULL)
        break;

      /* If we're *not* doing chdir before each mkdir, then we have to refer
         to the target using the full (multi-component) directory name.  */
      if (!do_chdir)
        basename_dir = dirpath;

      *slash = '\0';
      fail = make_dir (basename_dir, dirpath, tmp_mode, &newly_created_dir);
      if (fail)
        {
          CLEANUP;
          return 1;
        }

      if (newly_created_dir)
        {
          if (verbose_fmt_string)
        error (0, 0, verbose_fmt_string, quote (dirpath));

          if ((owner != (uid_t) -1 || group != (gid_t) -1)
          && chown (basename_dir, owner, group)
#if defined AFS && defined EPERM
          && errno != EPERM
#endif
          )
        {
          error (0, errno, _("cannot change owner and/or group of %s"),
             quote (dirpath));
          CLEANUP;
          return 1;
        }

          if (re_protect)
        {
          struct ptr_list *new = (struct ptr_list *)
            alloca (sizeof (struct ptr_list));
          new->dirname_end = slash;
          new->next = leading_dirs;
          leading_dirs = new;
        }
        }

      /* If we were able to save the initial working directory,
         then we can use chdir to change into each directory before
         creating an entry in that directory.  This avoids making
         stat and mkdir process O(n^2) file name components.  */
      if (do_chdir && chdir (basename_dir) < 0)
        {
          error (0, errno, _("cannot chdir to directory %s"),
             quote (dirpath));
          CLEANUP;
          return 1;
        }

      *slash++ = '/';

      /* Avoid unnecessary calls to `stat' when given
         pathnames containing multiple adjacent slashes.  */
      while (*slash == '/')
        slash++;
    }

      if (!do_chdir)
    basename_dir = dirpath;

      /* Done creating leading directories.  Restore original umask.  */
      umask (oldmask);

      /* We're done making leading directories.
     Create the final component of the path.  */

      if (make_dir (basename_dir, dirpath, mode, NULL))
    {
      CLEANUP;
      return 1;
    }

      if (verbose_fmt_string != NULL)
    error (0, 0, verbose_fmt_string, quote (dirpath));

      if (owner != (uid_t) -1 || group != (gid_t) -1)
    {
      if (chown (basename_dir, owner, group)
#ifdef AFS
          && errno != EPERM
#endif
          )
        {
          error (0, errno, _("cannot change owner and/or group of %s"),
             quote (dirpath));
          retval = 1;
        }
    }

      /* The above chown may have turned off some permission bits in MODE.
     Another reason we may have to use chmod here is that mkdir(2) is
     required to honor only the file permission bits.  In particular,
     it need not honor the `special' bits, so if MODE includes any
     special bits, set them here.  */
      if ((mode & ~S_IRWXUGO)
      && chmod (basename_dir, mode))
    {
      error (0, errno, _("cannot change permissions of %s"),
         quote (dirpath));
      retval = 1;
    }

      CLEANUP_CWD;

      /* If the mode for leading directories didn't include owner "wx"
     privileges, we have to reset their protections to the correct
     value.  */
      for (p = leading_dirs; p != NULL; p = p->next)
    {
      *(p->dirname_end) = '\0';
      if (chmod (dirpath, parent_mode))
        {
          error (0, errno, "cannot change permissions of %s",
             quote (dirpath));
          retval = 1;
        }
    }
    }
  else
    {
      /* We get here if the entire path already exists.  */

      const char *dirpath = argpath;

      if (!S_ISDIR (stats.st_mode))
    {
      error (0, 0, _("%s exists but is not a directory"), quote (dirpath));
      return 1;
    }

      if (!preserve_existing)
    {
      /* chown must precede chmod because on some systems,
         chown clears the set[ug]id bits for non-superusers,
         resulting in incorrect permissions.
         On System V, users can give away files with chown and then not
         be able to chmod them.  So don't give files away.  */

      if ((owner != (uid_t) -1 || group != (gid_t) -1)
          && chown (dirpath, owner, group)
#ifdef AFS
          && errno != EPERM
#endif
          )
        {
          error (0, errno, _("cannot change owner and/or group of %s"),
             quote (dirpath));
          retval = 1;
        }
      if (chmod (dirpath, mode))
        {
          error (0, errno, _("cannot change permissions of %s"),
                 quote (dirpath));
          retval = 1;
        }
    }
    }

  return retval;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/mbswidth.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/mbswidth.c used in: Step 792
/* Determine the number of screen columns needed for a string.
   Copyright (C) 2000-2002 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.  */

/* Written by Bruno Haible <haible@clisp.cons.org>.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

/* Specification.  */
#include "mbswidth.h"

/* Get MB_CUR_MAX.  */
#include <stdlib.h>

#include <string.h>

/* Get isprint().  */
#include <ctype.h>

/* Get mbstate_t, mbrtowc(), mbsinit(), wcwidth().  */
#if HAVE_WCHAR_H
# include <wchar.h>
#endif

/* Get iswprint(), iswcntrl().  */
#if HAVE_WCTYPE_H
# include <wctype.h>
#endif
#if !defined iswprint && !HAVE_ISWPRINT
# define iswprint(wc) 1
#endif
#if !defined iswcntrl && !HAVE_ISWCNTRL
# define iswcntrl(wc) 0
#endif

#ifndef mbsinit
# if !HAVE_MBSINIT
#  define mbsinit(ps) 1
# endif
#endif

#ifndef HAVE_DECL_WCWIDTH
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_WCWIDTH
int wcwidth ();
#endif

#ifndef wcwidth
# if !HAVE_WCWIDTH
/* wcwidth doesn't exist, so assume all printable characters have
   width 1.  */
#  define wcwidth(wc) ((wc) == 0 ? 0 : iswprint (wc) ? 1 : -1)
# endif
#endif

/* Get ISPRINT.  */
#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
# define IN_CTYPE_DOMAIN(c) 1
#else
# define IN_CTYPE_DOMAIN(c) isascii(c)
#endif
/* Undefine to protect against the definition in wctype.h of solaris2.6.   */
#undef ISPRINT
#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c))
#undef ISCNTRL
#define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl (c))

/* Returns the number of columns needed to represent the multibyte
   character string pointed to by STRING.  If a non-printable character
   occurs, and MBSW_REJECT_UNPRINTABLE is specified, -1 is returned.
   With flags = MBSW_REJECT_INVALID | MBSW_REJECT_UNPRINTABLE, this is
   the multibyte analogon of the wcswidth function.  */
int
mbswidth (const char *string, int flags)
{
  return mbsnwidth (string, strlen (string), flags);
}

/* Returns the number of columns needed to represent the multibyte
   character string pointed to by STRING of length NBYTES.  If a
   non-printable character occurs, and MBSW_REJECT_UNPRINTABLE is
   specified, -1 is returned.  */
int
mbsnwidth (const char *string, size_t nbytes, int flags)
{
  const char *p = string;
  const char *plimit = p + nbytes;
  int width;

  width = 0;
#if HAVE_MBRTOWC
  if (MB_CUR_MAX > 1)
    {
      while (p < plimit)
    switch (*p)
      {
        case ' ': case '!': case '"': case '#': case '%':
        case '&': case '\'': case '(': case ')': case '*':
        case '+': case ',': case '-': case '.': case '/':
        case '0': case '1': case '2': case '3': case '4':
        case '5': case '6': case '7': case '8': case '9':
        case ':': case ';': case '<': case '=': case '>':
        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':
        case '[': case '\\': case ']': case '^': 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': case '{': case '|': case '}': case '~':
          /* These characters are printable ASCII characters.  */
          p++;
          width++;
          break;
        default:
          /* If we have a multibyte sequence, scan it up to its end.  */
          {
        mbstate_t mbstate;
        memset (&mbstate, 0, sizeof mbstate);
        do
          {
            wchar_t wc;
            size_t bytes;
            int w;

            bytes = mbrtowc (&wc, p, plimit - p, &mbstate);

            if (bytes == (size_t) -1)
              /* An invalid multibyte sequence was encountered.  */
              {
            if (!(flags & MBSW_REJECT_INVALID))
              {
                p++;
                width++;
                break;
              }
            else
              return -1;
              }

            if (bytes == (size_t) -2)
              /* An incomplete multibyte character at the end.  */
              {
            if (!(flags & MBSW_REJECT_INVALID))
              {
                p = plimit;
                width++;
                break;
              }
            else
              return -1;
              }

            if (bytes == 0)
              /* A null wide character was encountered.  */
              bytes = 1;

            w = wcwidth (wc);
            if (w >= 0)
              /* A printable multibyte character.  */
              width += w;
            else
              /* An unprintable multibyte character.  */
              if (!(flags & MBSW_REJECT_UNPRINTABLE))
            width += (iswcntrl (wc) ? 0 : 1);
              else
            return -1;

            p += bytes;
          }
        while (! mbsinit (&mbstate));
          }
          break;
      }
      return width;
    }
#endif

  while (p < plimit)
    {
      unsigned char c = (unsigned char) *p++;

      if (ISPRINT (c))
    width++;
      else if (!(flags & MBSW_REJECT_UNPRINTABLE))
    width += (ISCNTRL (c) ? 0 : 1);
      else
    return -1;
    }
  return width;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/md5.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/md5.c used in: Step 793
/* md5.c - Functions to compute MD5 message digest of files or memory blocks
   according to the definition of MD5 in RFC 1321 from April 1992.
   Copyright (C) 1995, 1996, 2001, 2003 Free Software Foundation, Inc.
   NOTE: The canonical source of this file is maintained with the GNU C
   Library.  Bugs can be reported to bug-glibc@prep.ai.mit.edu.

   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.  */

/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>

#if STDC_HEADERS || defined _LIBC
# include <stdlib.h>
# include <string.h>
#else
# ifndef HAVE_MEMCPY
#  define memcpy(d, s, n) bcopy ((s), (d), (n))
# endif
#endif

#include "md5.h"
#include "unlocked-io.h"

#ifdef _LIBC
# include <endian.h>
# if __BYTE_ORDER == __BIG_ENDIAN
#  define WORDS_BIGENDIAN 1
# endif
/* We need to keep the namespace clean so define the MD5 function
   protected using leading __ .  */
# define md5_init_ctx __md5_init_ctx
# define md5_process_block __md5_process_block
# define md5_process_bytes __md5_process_bytes
# define md5_finish_ctx __md5_finish_ctx
# define md5_read_ctx __md5_read_ctx
# define md5_stream __md5_stream
# define md5_buffer __md5_buffer
#endif

#ifdef WORDS_BIGENDIAN
# define SWAP(n)                            \
    (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
#else
# define SWAP(n) (n)
#endif

#define BLOCKSIZE 4096
/* Ensure that BLOCKSIZE is a multiple of 64.  */
#if BLOCKSIZE % 64 != 0
/* FIXME-someday (soon?): use #error instead of this kludge.  */
"invalid BLOCKSIZE"
#endif

/* This array contains the bytes used to pad the buffer to the next
   64-byte boundary.  (RFC 1321, 3.1: Step 1)  */
static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ...  */ };


/* Initialize structure containing state of computation.
   (RFC 1321, 3.3: Step 3)  */
void
md5_init_ctx (ctx)
     struct md5_ctx *ctx;
{
  ctx->A = 0x67452301;
  ctx->B = 0xefcdab89;
  ctx->C = 0x98badcfe;
  ctx->D = 0x10325476;

  ctx->total[0] = ctx->total[1] = 0;
  ctx->buflen = 0;
}

/* Put result from CTX in first 16 bytes following RESBUF.  The result
   must be in little endian byte order.

   IMPORTANT: On some systems it is required that RESBUF is correctly
   aligned for a 32 bits value.  */
void *
md5_read_ctx (ctx, resbuf)
     const struct md5_ctx *ctx;
     void *resbuf;
{
  ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A);
  ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B);
  ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C);
  ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D);

  return resbuf;
}

/* Process the remaining bytes in the internal buffer and the usual
   prolog according to the standard and write the result to RESBUF.

   IMPORTANT: On some systems it is required that RESBUF is correctly
   aligned for a 32 bits value.  */
void *
md5_finish_ctx (ctx, resbuf)
     struct md5_ctx *ctx;
     void *resbuf;
{
  /* Take yet unprocessed bytes into account.  */
  md5_uint32 bytes = ctx->buflen;
  size_t pad;

  /* Now count remaining bytes.  */
  ctx->total[0] += bytes;
  if (ctx->total[0] < bytes)
    ++ctx->total[1];

  pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
  memcpy (&ctx->buffer[bytes], fillbuf, pad);

  /* Put the 64-bit file length in *bits* at the end of the buffer.  */
  *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3);
  *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) |
                            (ctx->total[0] >> 29));

  /* Process last bytes.  */
  md5_process_block (ctx->buffer, bytes + pad + 8, ctx);

  return md5_read_ctx (ctx, resbuf);
}

/* Compute MD5 message digest for bytes read from STREAM.  The
   resulting message digest number will be written into the 16 bytes
   beginning at RESBLOCK.  */
int
md5_stream (stream, resblock)
     FILE *stream;
     void *resblock;
{
  struct md5_ctx ctx;
  char buffer[BLOCKSIZE + 72];
  size_t sum;

  /* Initialize the computation context.  */
  md5_init_ctx (&ctx);

  /* Iterate over full file contents.  */
  while (1)
    {
      /* We read the file in blocks of BLOCKSIZE bytes.  One call of the
     computation function processes the whole buffer so that with the
     next round of the loop another block can be read.  */
      size_t n;
      sum = 0;

      /* Read block.  Take care for partial reads.  */
      while (1)
    {
      n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);

      sum += n;

      if (sum == BLOCKSIZE)
        break;

      if (n == 0)
        {
          /* Check for the error flag IFF N == 0, so that we don't
         exit the loop after a partial read due to e.g., EAGAIN
         or EWOULDBLOCK.  */
          if (ferror (stream))
        return 1;
          goto process_partial_block;
        }

      /* We've read at least one byte, so ignore errors.  But always
         check for EOF, since feof may be true even though N > 0.
         Otherwise, we could end up calling fread after EOF.  */
      if (feof (stream))
        goto process_partial_block;
    }

      /* Process buffer with BLOCKSIZE bytes.  Note that
            BLOCKSIZE % 64 == 0
       */
      md5_process_block (buffer, BLOCKSIZE, &ctx);
    }

 process_partial_block:;

  /* Process any remaining bytes.  */
  if (sum > 0)
    md5_process_bytes (buffer, sum, &ctx);

  /* Construct result in desired memory.  */
  md5_finish_ctx (&ctx, resblock);
  return 0;
}

/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
   result is always in little endian byte order, so that a byte-wise
   output yields to the wanted ASCII representation of the message
   digest.  */
void *
md5_buffer (buffer, len, resblock)
     const char *buffer;
     size_t len;
     void *resblock;
{
  struct md5_ctx ctx;

  /* Initialize the computation context.  */
  md5_init_ctx (&ctx);

  /* Process whole buffer but last len % 64 bytes.  */
  md5_process_bytes (buffer, len, &ctx);

  /* Put result in desired memory area.  */
  return md5_finish_ctx (&ctx, resblock);
}


void
md5_process_bytes (buffer, len, ctx)
     const void *buffer;
     size_t len;
     struct md5_ctx *ctx;
{
  /* When we already have some bits in our internal buffer concatenate
     both inputs first.  */
  if (ctx->buflen != 0)
    {
      size_t left_over = ctx->buflen;
      size_t add = 128 - left_over > len ? len : 128 - left_over;

      memcpy (&ctx->buffer[left_over], buffer, add);
      ctx->buflen += add;

      if (ctx->buflen > 64)
    {
      md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx);

      ctx->buflen &= 63;
      /* The regions in the following copy operation cannot overlap.  */
      memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
          ctx->buflen);
    }

      buffer = (const char *) buffer + add;
      len -= add;
    }

  /* Process available complete blocks.  */
  if (len >= 64)
    {
#if !_STRING_ARCH_unaligned
/* To check alignment gcc has an appropriate operator.  Other
   compilers don't.  */
# if __GNUC__ >= 2
#  define UNALIGNED_P(p) (((md5_uintptr) p) % __alignof__ (md5_uint32) != 0)
# else
#  define UNALIGNED_P(p) (((md5_uintptr) p) % sizeof (md5_uint32) != 0)
# endif
      if (UNALIGNED_P (buffer))
    while (len > 64)
      {
        md5_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx);
        buffer = (const char *) buffer + 64;
        len -= 64;
      }
      else
#endif
    {
      md5_process_block (buffer, len & ~63, ctx);
      buffer = (const char *) buffer + (len & ~63);
      len &= 63;
    }
    }

  /* Move remaining bytes in internal buffer.  */
  if (len > 0)
    {
      size_t left_over = ctx->buflen;

      memcpy (&ctx->buffer[left_over], buffer, len);
      left_over += len;
      if (left_over >= 64)
    {
      md5_process_block (ctx->buffer, 64, ctx);
      left_over -= 64;
      memcpy (ctx->buffer, &ctx->buffer[64], left_over);
    }
      ctx->buflen = left_over;
    }
}


/* These are the four functions used in the four steps of the MD5 algorithm
   and defined in the RFC 1321.  The first function is a little bit optimized
   (as found in Colin Plumbs public domain implementation).  */
/* #define FF(b, c, d) ((b & c) | (~b & d)) */
#define FF(b, c, d) (d ^ (b & (c ^ d)))
#define FG(b, c, d) FF (d, b, c)
#define FH(b, c, d) (b ^ c ^ d)
#define FI(b, c, d) (c ^ (b | ~d))

/* Process LEN bytes of BUFFER, accumulating context into CTX.
   It is assumed that LEN % 64 == 0.  */

void
md5_process_block (buffer, len, ctx)
     const void *buffer;
     size_t len;
     struct md5_ctx *ctx;
{
  md5_uint32 correct_words[16];
  const md5_uint32 *words = buffer;
  size_t nwords = len / sizeof (md5_uint32);
  const md5_uint32 *endp = words + nwords;
  md5_uint32 A = ctx->A;
  md5_uint32 B = ctx->B;
  md5_uint32 C = ctx->C;
  md5_uint32 D = ctx->D;

  /* First increment the byte count.  RFC 1321 specifies the possible
     length of the file up to 2^64 bits.  Here we only compute the
     number of bytes.  Do a double word increment.  */
  ctx->total[0] += len;
  if (ctx->total[0] < len)
    ++ctx->total[1];

  /* Process all bytes in the buffer with 64 bytes in each round of
     the loop.  */
  while (words < endp)
    {
      md5_uint32 *cwp = correct_words;
      md5_uint32 A_save = A;
      md5_uint32 B_save = B;
      md5_uint32 C_save = C;
      md5_uint32 D_save = D;

      /* First round: using the given function, the context and a constant
     the next context is computed.  Because the algorithms processing
     unit is a 32-bit word and it is determined to work on words in
     little endian byte order we perhaps have to change the byte order
     before the computation.  To reduce the work for the next steps
     we store the swapped words in the array CORRECT_WORDS.  */

#define OP(a, b, c, d, s, T)                        \
      do                                \
        {                               \
      a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T;     \
      ++words;                          \
      a = rol (a, s);                       \
      a += b;                           \
        }                               \
      while (0)

      /* Before we start, one word to the strange constants.
     They are defined in RFC 1321 as

     T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64, or
     perl -e 'foreach(1..64){printf "0x%08x\n", int (4294967296 * abs (sin $_))}'
       */

      /* Round 1.  */
      OP (A, B, C, D,  7, 0xd76aa478);
      OP (D, A, B, C, 12, 0xe8c7b756);
      OP (C, D, A, B, 17, 0x242070db);
      OP (B, C, D, A, 22, 0xc1bdceee);
      OP (A, B, C, D,  7, 0xf57c0faf);
      OP (D, A, B, C, 12, 0x4787c62a);
      OP (C, D, A, B, 17, 0xa8304613);
      OP (B, C, D, A, 22, 0xfd469501);
      OP (A, B, C, D,  7, 0x698098d8);
      OP (D, A, B, C, 12, 0x8b44f7af);
      OP (C, D, A, B, 17, 0xffff5bb1);
      OP (B, C, D, A, 22, 0x895cd7be);
      OP (A, B, C, D,  7, 0x6b901122);
      OP (D, A, B, C, 12, 0xfd987193);
      OP (C, D, A, B, 17, 0xa679438e);
      OP (B, C, D, A, 22, 0x49b40821);

      /* For the second to fourth round we have the possibly swapped words
     in CORRECT_WORDS.  Redefine the macro to take an additional first
     argument specifying the function to use.  */
#undef OP
#define OP(f, a, b, c, d, k, s, T)                  \
      do                                \
    {                               \
      a += f (b, c, d) + correct_words[k] + T;          \
      a = rol (a, s);                       \
      a += b;                           \
    }                               \
      while (0)

      /* Round 2.  */
      OP (FG, A, B, C, D,  1,  5, 0xf61e2562);
      OP (FG, D, A, B, C,  6,  9, 0xc040b340);
      OP (FG, C, D, A, B, 11, 14, 0x265e5a51);
      OP (FG, B, C, D, A,  0, 20, 0xe9b6c7aa);
      OP (FG, A, B, C, D,  5,  5, 0xd62f105d);
      OP (FG, D, A, B, C, 10,  9, 0x02441453);
      OP (FG, C, D, A, B, 15, 14, 0xd8a1e681);
      OP (FG, B, C, D, A,  4, 20, 0xe7d3fbc8);
      OP (FG, A, B, C, D,  9,  5, 0x21e1cde6);
      OP (FG, D, A, B, C, 14,  9, 0xc33707d6);
      OP (FG, C, D, A, B,  3, 14, 0xf4d50d87);
      OP (FG, B, C, D, A,  8, 20, 0x455a14ed);
      OP (FG, A, B, C, D, 13,  5, 0xa9e3e905);
      OP (FG, D, A, B, C,  2,  9, 0xfcefa3f8);
      OP (FG, C, D, A, B,  7, 14, 0x676f02d9);
      OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a);

      /* Round 3.  */
      OP (FH, A, B, C, D,  5,  4, 0xfffa3942);
      OP (FH, D, A, B, C,  8, 11, 0x8771f681);
      OP (FH, C, D, A, B, 11, 16, 0x6d9d6122);
      OP (FH, B, C, D, A, 14, 23, 0xfde5380c);
      OP (FH, A, B, C, D,  1,  4, 0xa4beea44);
      OP (FH, D, A, B, C,  4, 11, 0x4bdecfa9);
      OP (FH, C, D, A, B,  7, 16, 0xf6bb4b60);
      OP (FH, B, C, D, A, 10, 23, 0xbebfbc70);
      OP (FH, A, B, C, D, 13,  4, 0x289b7ec6);
      OP (FH, D, A, B, C,  0, 11, 0xeaa127fa);
      OP (FH, C, D, A, B,  3, 16, 0xd4ef3085);
      OP (FH, B, C, D, A,  6, 23, 0x04881d05);
      OP (FH, A, B, C, D,  9,  4, 0xd9d4d039);
      OP (FH, D, A, B, C, 12, 11, 0xe6db99e5);
      OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8);
      OP (FH, B, C, D, A,  2, 23, 0xc4ac5665);

      /* Round 4.  */
      OP (FI, A, B, C, D,  0,  6, 0xf4292244);
      OP (FI, D, A, B, C,  7, 10, 0x432aff97);
      OP (FI, C, D, A, B, 14, 15, 0xab9423a7);
      OP (FI, B, C, D, A,  5, 21, 0xfc93a039);
      OP (FI, A, B, C, D, 12,  6, 0x655b59c3);
      OP (FI, D, A, B, C,  3, 10, 0x8f0ccc92);
      OP (FI, C, D, A, B, 10, 15, 0xffeff47d);
      OP (FI, B, C, D, A,  1, 21, 0x85845dd1);
      OP (FI, A, B, C, D,  8,  6, 0x6fa87e4f);
      OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
      OP (FI, C, D, A, B,  6, 15, 0xa3014314);
      OP (FI, B, C, D, A, 13, 21, 0x4e0811a1);
      OP (FI, A, B, C, D,  4,  6, 0xf7537e82);
      OP (FI, D, A, B, C, 11, 10, 0xbd3af235);
      OP (FI, C, D, A, B,  2, 15, 0x2ad7d2bb);
      OP (FI, B, C, D, A,  9, 21, 0xeb86d391);

      /* Add the starting values of the context.  */
      A += A_save;
      B += B_save;
      C += C_save;
      D += D_save;
    }

  /* Put checksum in context given as argument.  */
  ctx->A = A;
  ctx->B = B;
  ctx->C = C;
  ctx->D = D;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/memcasecmp.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/memcasecmp.c used in: Step 794
/* Case-insensitive buffer comparator.
   Copyright (C) 1996, 1997, 2000 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.  */

/* Written by Jim Meyering.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>
#include <ctype.h>

#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
# define IN_CTYPE_DOMAIN(Char) 1
#else
# define IN_CTYPE_DOMAIN(Char) isascii(Char)
#endif
#define ISLOWER(Char) (IN_CTYPE_DOMAIN (Char) && islower (Char))

#if _LIBC || STDC_HEADERS
# define TOUPPER(Char) toupper (Char)
#else
# define TOUPPER(Char) (ISLOWER (Char) ? toupper (Char) : (Char))
#endif

#include "memcasecmp.h"

/* Like memcmp, but ignore differences in case.
   Convert to upper case (not lower) before comparing so that
   join -i works with sort -f.  */

int
memcasecmp (const void *vs1, const void *vs2, size_t n)
{
  unsigned int i;
  unsigned char const *s1 = (unsigned char const *) vs1;
  unsigned char const *s2 = (unsigned char const *) vs2;
  for (i = 0; i < n; i++)
    {
      unsigned char u1 = *s1++;
      unsigned char u2 = *s2++;
      if (TOUPPER (u1) != TOUPPER (u2))
        return TOUPPER (u1) - TOUPPER (u2);
    }
  return 0;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/offtostr.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/offtostr.c used in: Step 797
#define inttostr offtostr
#define inttype off_t
#include "inttostr.c"

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/path-concat.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/path-concat.c used in: Step 798
/* path-concat.c -- concatenate two arbitrary pathnames

   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002 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.  */

/* Written by Jim Meyering.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#ifndef HAVE_MEMPCPY
# define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
#endif

#include <stdio.h>

#if HAVE_STRING_H
# if !STDC_HEADERS && HAVE_MEMORY_H
#  include <memory.h>
# endif
# include <string.h>
#else
# if HAVE_STRINGS_H
#  include <strings.h>
# endif
#endif
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#ifndef HAVE_DECL_MALLOC
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_MALLOC
char *malloc ();
#endif

#ifndef strdup
char *strdup ();
#endif

#include "dirname.h"
#include "xalloc.h"
#include "path-concat.h"

/* Concatenate two pathname components, DIR and BASE, in
   newly-allocated storage and return the result.  Return 0 if out of
   memory.  Add a slash between DIR and BASE in the result if neither
   would contribute one.  If each would contribute at least one, elide
   one from the end of DIR.  Otherwise, simply concatenate DIR and
   BASE.  In any case, if BASE_IN_RESULT is non-NULL, set
   *BASE_IN_RESULT to point to the copy of BASE in the returned
   concatenation.

   DIR may be NULL, BASE must not be.

   Return NULL if memory is exhausted.  */

char *
path_concat (const char *dir, const char *base, char **base_in_result)
{
  char *p;
  char *p_concat;
  size_t baselen;
  size_t dirlen;

  if (!dir)
    {
      p_concat = strdup (base);
      if (base_in_result)
        *base_in_result = p_concat;
      return p_concat;
    }

  /* DIR is not empty. */
  baselen = base_len (base);
  dirlen = strlen (dir);

  p_concat = malloc (dirlen + baselen + 2);
  if (!p_concat)
    return 0;

  p = mempcpy (p_concat, dir, dirlen);

  if (FILESYSTEM_PREFIX_LEN (dir) < dirlen)
    {
      if (ISSLASH (*(p - 1)) && ISSLASH (*base))
    --p;
      else if (!ISSLASH (*(p - 1)) && !ISSLASH (*base))
    *p++ = DIRECTORY_SEPARATOR;
    }

  if (base_in_result)
    *base_in_result = p;

  memcpy (p, base, baselen);
  p[baselen] = '\0';

  return p_concat;
}

/* Same, but die when memory is exhausted. */

char *
xpath_concat (const char *dir, const char *base, char **base_in_result)
{
  char *res = path_concat (dir, base, base_in_result);
  if (! res)
    xalloc_die ();
  return res;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/physmem.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/physmem.c used in: Step 799
/* Calculate the size of physical memory.
   Copyright 2000, 2001, 2003 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.  */

/* Written by Paul Eggert.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#if HAVE_SYS_PSTAT_H
# include <sys/pstat.h>
#endif

#if HAVE_SYS_SYSMP_H
# include <sys/sysmp.h>
#endif

#if HAVE_SYS_SYSINFO_H && HAVE_MACHINE_HAL_SYSINFO_H
# include <sys/sysinfo.h>
# include <machine/hal_sysinfo.h>
#endif

#if HAVE_SYS_TABLE_H
# include <sys/table.h>
#endif

#include <sys/types.h>

#if HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif

#if HAVE_SYS_SYSCTL_H
# include <sys/sysctl.h>
#endif

#if HAVE_SYS_SYSTEMCFG_H
# include <sys/systemcfg.h>
#endif

#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
/*  MEMORYSTATUSEX is missing from older windows headers, so define
    a local replacement.  */
typedef struct
{
  DWORD dwLength;
  DWORD dwMemoryLoad;
  DWORDLONG ullTotalPhys;
  DWORDLONG ullAvailPhys;
  DWORDLONG ullTotalPageFile;
  DWORDLONG ullAvailPageFile;
  DWORDLONG ullTotalVirtual;
  DWORDLONG ullAvailVirtual;
  DWORDLONG ullAvailExtendedVirtual;
} lMEMORYSTATUSEX;
typedef WINBOOL (WINAPI *PFN_MS_EX) (lMEMORYSTATUSEX*);
#endif

#include "physmem.h"

#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))

/* Return the total amount of physical memory.  */
double
physmem_total ()
{
#if defined _SC_PHYS_PAGES && defined _SC_PAGESIZE
  { /* This works on linux-gnu, solaris2 and cygwin.  */
    double pages = sysconf (_SC_PHYS_PAGES);
    double pagesize = sysconf (_SC_PAGESIZE);
    if (0 <= pages && 0 <= pagesize)
      return pages * pagesize;
  }
#endif

#if HAVE_PSTAT_GETSTATIC
  { /* This works on hpux11.  */
    struct pst_static pss;
    if (0 <= pstat_getstatic (&pss, sizeof pss, 1, 0))
      {
    double pages = pss.physical_memory;
    double pagesize = pss.page_size;
    if (0 <= pages && 0 <= pagesize)
      return pages * pagesize;
      }
  }
#endif

#if HAVE_SYSMP && defined MP_SAGET && defined MPSA_RMINFO && defined _SC_PAGESIZE
  { /* This works on irix6. */
    struct rminfo realmem;
    if (sysmp (MP_SAGET, MPSA_RMINFO, &realmem, sizeof realmem) == 0)
      {
    double pagesize = sysconf (_SC_PAGESIZE);
    double pages = realmem.physmem;
    if (0 <= pages && 0 <= pagesize)
      return pages * pagesize;
      }
  }
#endif

#if HAVE_GETSYSINFO && defined GSI_PHYSMEM
  { /* This works on Tru64 UNIX V4/5.  */
    int physmem;

    if (getsysinfo (GSI_PHYSMEM, (caddr_t) &physmem, sizeof (physmem),
            NULL, NULL, NULL) == 1)
      {
    double kbytes = physmem;

    if (0 <= kbytes)
      return kbytes * 1024.0;
      }
  }
#endif

#if HAVE_SYSCTL && defined HW_PHYSMEM
  { /* This works on *bsd and darwin.  */
    unsigned int physmem;
    size_t len = sizeof physmem;
    static int mib[2] = { CTL_HW, HW_PHYSMEM };

    if (sysctl (mib, ARRAY_SIZE (mib), &physmem, &len, NULL, 0) == 0
    && len == sizeof (physmem))
      return (double) physmem;
  }
#endif

#if HAVE__SYSTEM_CONFIGURATION
  /* This works on AIX.  */
  return _system_configuration.physmem;
#endif

#if defined _WIN32
  { /* this works on windows */
    PFN_MS_EX pfnex;
    HMODULE h = GetModuleHandle ("kernel32.dll");

    if (!h)
      return 0.0;

    /*  Use GlobalMemoryStatusEx if available.  */
    if ((pfnex = (PFN_MS_EX) GetProcAddress (h, "GlobalMemoryStatusEx")))
      {
    lMEMORYSTATUSEX lms_ex;
    lms_ex.dwLength = sizeof lms_ex;
    if (!pfnex (&lms_ex))
      return 0.0;
    return (double) lms_ex.ullTotalPhys;
      }

    /*  Fall back to GlobalMemoryStatus which is always available.
        but returns wrong results for physical memory > 4GB.  */
    else
      {
    MEMORYSTATUS ms;
    GlobalMemoryStatus (&ms);
    return (double) ms.dwTotalPhys;
      }
  }
#endif

  /* Guess 64 MB.  It's probably an older host, so guess small.  */
  return 64 * 1024 * 1024;
}

/* Return the amount of physical memory available.  */
double
physmem_available ()
{
#if defined _SC_AVPHYS_PAGES && defined _SC_PAGESIZE
  { /* This works on linux-gnu, solaris2 and cygwin.  */
    double pages = sysconf (_SC_AVPHYS_PAGES);
    double pagesize = sysconf (_SC_PAGESIZE);
    if (0 <= pages && 0 <= pagesize)
      return pages * pagesize;
  }
#endif

#if HAVE_PSTAT_GETSTATIC && HAVE_PSTAT_GETDYNAMIC
  { /* This works on hpux11.  */
    struct pst_static pss;
    struct pst_dynamic psd;
    if (0 <= pstat_getstatic (&pss, sizeof pss, 1, 0)
    && 0 <= pstat_getdynamic (&psd, sizeof psd, 1, 0))
      {
    double pages = psd.psd_free;
    double pagesize = pss.page_size;
    if (0 <= pages && 0 <= pagesize)
      return pages * pagesize;
      }
  }
#endif

#if HAVE_SYSMP && defined MP_SAGET && defined MPSA_RMINFO && defined _SC_PAGESIZE
  { /* This works on irix6. */
    struct rminfo realmem;
    if (sysmp (MP_SAGET, MPSA_RMINFO, &realmem, sizeof realmem) == 0)
      {
    double pagesize = sysconf (_SC_PAGESIZE);
    double pages = realmem.availrmem;
    if (0 <= pages && 0 <= pagesize)
      return pages * pagesize;
      }
  }
#endif

#if HAVE_TABLE && defined TBL_VMSTATS
  { /* This works on Tru64 UNIX V4/5.  */
    struct tbl_vmstats vmstats;

    if (table (TBL_VMSTATS, 0, &vmstats, 1, sizeof (vmstats)) == 1)
      {
    double pages = vmstats.free_count;
    double pagesize = vmstats.pagesize;

    if (0 <= pages && 0 <= pagesize)
      return pages * pagesize;
      }
  }
#endif

#if HAVE_SYSCTL && defined HW_USERMEM
  { /* This works on *bsd and darwin.  */
    unsigned int usermem;
    size_t len = sizeof usermem;
    static int mib[2] = { CTL_HW, HW_USERMEM };

    if (sysctl (mib, ARRAY_SIZE (mib), &usermem, &len, NULL, 0) == 0
    && len == sizeof (usermem))
      return (double) usermem;
  }
#endif

#if defined _WIN32
  { /* this works on windows */
    PFN_MS_EX pfnex;
    HMODULE h = GetModuleHandle ("kernel32.dll");

    if (!h)
      return 0.0;

    /*  Use GlobalMemoryStatusEx if available.  */
    if ((pfnex = (PFN_MS_EX) GetProcAddress (h, "GlobalMemoryStatusEx")))
      {
    lMEMORYSTATUSEX lms_ex;
    lms_ex.dwLength = sizeof lms_ex;
    if (!pfnex (&lms_ex))
      return 0.0;
    return (double) lms_ex.ullAvailPhys;
      }

    /*  Fall back to GlobalMemoryStatus which is always available.
        but returns wrong results for physical memory > 4GB  */
    else
      {
    MEMORYSTATUS ms;
    GlobalMemoryStatus (&ms);
    return (double) ms.dwAvailPhys;
      }
  }
#endif

  /* Guess 25% of physical memory.  */
  return physmem_total () / 4;
}


#if DEBUG

# include <stdio.h>
# include <stdlib.h>

int
main ()
{
  printf ("%12.f %12.f\n", physmem_total (), physmem_available ());
  exit (0);
}

#endif /* DEBUG */

/*
Local Variables:
compile-command: "gcc -DDEBUG -DHAVE_CONFIG_H -I.. -g -O -Wall -W physmem.c"
End:
*/

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/quote.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/quote.c used in: Step 800
/* quote.c - quote arguments for output
   Copyright (C) 1998, 1999, 2000, 2001, 2003 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.  */

/* Written by Paul Eggert <eggert@twinsun.com> */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_STDDEF_H
# include <stddef.h>  /* For the definition of size_t on windows w/MSVC.  */
#endif
#include <sys/types.h>
#include "quotearg.h"
#include "quote.h"

/* Return an unambiguous printable representation of NAME,
   allocated in slot N, suitable for diagnostics.  */
char const *
quote_n (int n, char const *name)
{
  return quotearg_n_style (n, locale_quoting_style, name);
}

/* Return an unambiguous printable representation of NAME,
   suitable for diagnostics.  */
char const *
quote (char const *name)
{
  return quote_n (0, name);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/readtokens.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/readtokens.c used in: Step 802
/* readtokens.c  -- Functions for reading tokens from an input stream.
   Copyright (C) 1990-1991, 1999, 2001, 2003 Jim Meyering.

   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.

   Written by Jim Meyering. */

/* This almost supercedes xreadline stuff -- using delim="\n"
   gives the same functionality, except that these functions
   would never return empty lines.

   To Do:
     - To allow '\0' as a delimiter, I will have to change
       interfaces to permit specification of delimiter-string
       length.
   */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>

#ifdef STDC_HEADERS
# include <stdlib.h>
#endif

#if defined (STDC_HEADERS) || defined(HAVE_STRING_H)
# include <string.h>
/* An ANSI string.h and pre-ANSI memory.h might conflict.  */
# if !defined (STDC_HEADERS) && defined (HAVE_MEMORY_H)
#  include <memory.h>
# endif /* not STDC_HEADERS and HAVE_MEMORY_H */
#else /* not STDC_HEADERS and not HAVE_STRING_H */
# include <strings.h>
/* memory.h and strings.h conflict on some systems.  */
#endif /* not STDC_HEADERS and not HAVE_STRING_H */

#include "readtokens.h"
#include "unlocked-io.h"
#include "xalloc.h"

#define STREQ(a,b) ((a) == (b) || ((a) && (b) && *(a) == *(b) \
                   && strcmp(a, b) == 0))

/* Initialize a tokenbuffer. */

void
init_tokenbuffer (tokenbuffer)
     token_buffer *tokenbuffer;
{
  tokenbuffer->size = INITIAL_TOKEN_LENGTH;
  tokenbuffer->buffer = ((char *) xmalloc (INITIAL_TOKEN_LENGTH));
}

/* Read a token from `stream' into `tokenbuffer'.
   Upon return, the token is in tokenbuffer->buffer and
   has a trailing '\0' instead of the original delimiter.
   The function value is the length of the token not including
   the final '\0'.  When EOF is reached (i.e. on the call
   after the last token is read), -1 is returned and tokenbuffer
   isn't modified.

   This function will work properly on lines containing NUL bytes
   and on files that aren't newline-terminated.  */

long
readtoken (FILE *stream,
       const char *delim,
       int n_delim,
       token_buffer *tokenbuffer)
{
  char *p;
  int c, i, n;
  static const char *saved_delim = NULL;
  static char isdelim[256];
  int same_delimiters;

  if (delim == NULL && saved_delim == NULL)
    abort ();

  same_delimiters = 0;
  if (delim != saved_delim && saved_delim != NULL)
    {
      same_delimiters = 1;
      for (i = 0; i < n_delim; i++)
    {
      if (delim[i] != saved_delim[i])
        {
          same_delimiters = 0;
          break;
        }
    }
    }

  if (!same_delimiters)
    {
      const char *t;
      unsigned int j;
      saved_delim = delim;
      for (j = 0; j < sizeof (isdelim); j++)
    isdelim[j] = 0;
      for (t = delim; *t; t++)
    isdelim[(unsigned char) *t] = 1;
    }

  p = tokenbuffer->buffer;
  n = tokenbuffer->size;
  i = 0;

  /* FIXME: don't fool with this caching BS.  Use strchr instead.  */
  /* skip over any leading delimiters */
  for (c = getc (stream); c >= 0 && isdelim[c]; c = getc (stream))
    {
      /* empty */
    }

  for (;;)
    {
      if (i >= n)
    {
      n = 3 * (n / 2 + 1);
      p = xrealloc (p, (unsigned int) n);
    }
      if (c < 0)
    {
      if (i == 0)
        return (-1);
      p[i] = 0;
      break;
    }
      if (isdelim[c])
    {
      p[i] = 0;
      break;
    }
      p[i++] = c;
      c = getc (stream);
    }

  tokenbuffer->buffer = p;
  tokenbuffer->size = n;
  return (i);
}

/* Return a NULL-terminated array of pointers to tokens
   read from `stream.'  The number of tokens is returned
   as the value of the function.
   All storage is obtained through calls to malloc();

   %%% Question: is it worth it to do a single
   %%% realloc() of `tokens' just before returning? */

int
readtokens (FILE *stream,
        int projected_n_tokens,
        const char *delim,
        int n_delim,
        char ***tokens_out,
        long **token_lengths)
{
  token_buffer tb, *token = &tb;
  int token_length;
  char **tokens;
  long *lengths;
  int sz;
  int n_tokens;

  n_tokens = 0;
  if (projected_n_tokens > 0)
    projected_n_tokens++;   /* add one for trailing NULL pointer */
  else
    projected_n_tokens = 64;
  sz = projected_n_tokens;
  tokens = (char **) xmalloc (sz * sizeof (char *));
  lengths = (long *) xmalloc (sz * sizeof (long));

  init_tokenbuffer (token);
  for (;;)
    {
      char *tmp;
      token_length = readtoken (stream, delim, n_delim, token);
      if (n_tokens >= sz)
    {
      sz *= 2;
      tokens = (char **) xrealloc (tokens, sz * sizeof (char *));
      lengths = (long *) xrealloc (lengths, sz * sizeof (long));
    }

      if (token_length < 0)
    {
      /* don't increment n_tokens for NULL entry */
      tokens[n_tokens] = NULL;
      lengths[n_tokens] = -1;
      break;
    }
      tmp = (char *) xmalloc ((token_length + 1) * sizeof (char));
      lengths[n_tokens] = token_length;
      tokens[n_tokens] = strncpy (tmp, token->buffer,
                  (unsigned) (token_length + 1));
      n_tokens++;
    }

  free (token->buffer);
  *tokens_out = tokens;
  if (token_lengths != NULL)
    *token_lengths = lengths;
  return n_tokens;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/rpmatch.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/rpmatch.c used in: Step 803
/* Determine whether string value is affirmation or negative response
   according to current locale's data.
   Copyright (C) 1996, 1998, 2000, 2002 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if STDC_HEADERS || _LIBC
# include <stddef.h>
# include <stdlib.h>
#else
# ifndef NULL
#  define NULL 0
# endif
#endif

#if ENABLE_NLS
# include <sys/types.h>
# if HAVE_LIMITS_H
#  include <limits.h>
# endif
# include <regex.h>
# include "gettext.h"
# define _(msgid) gettext (msgid)

static int
try (const char *response, const char *pattern, const int match,
     const int nomatch, const char **lastp, regex_t *re)
{
  if (pattern != *lastp)
    {
      /* The pattern has changed.  */
      if (*lastp)
    {
      /* Free the old compiled pattern.  */
      regfree (re);
      *lastp = NULL;
    }
      /* Compile the pattern and cache it for future runs.  */
      if (regcomp (re, pattern, REG_EXTENDED) != 0)
    return -1;
      *lastp = pattern;
    }

  /* See if the regular expression matches RESPONSE.  */
  return regexec (re, response, 0, NULL, 0) == 0 ? match : nomatch;
}
#endif


int
rpmatch (const char *response)
{
#if ENABLE_NLS
  /* Match against one of the response patterns, compiling the pattern
     first if necessary.  */

  /* We cache the response patterns and compiled regexps here.  */
  static const char *yesexpr, *noexpr;
  static regex_t yesre, nore;
  int result;

  return ((result = try (response, _("^[yY]"), 1, 0,
             &yesexpr, &yesre))
      ? result
      : try (response, _("^[nN]"), 0, -1, &noexpr, &nore));
#else
  /* Test against "^[yY]" and "^[nN]", hardcoded to avoid requiring regex */
  return (*response == 'y' || *response == 'Y' ? 1
      : *response == 'n' || *response == 'N' ? 0 : -1);
#endif
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/safe-read.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/safe-read.c used in: Step 804, Step 805
/* An interface to read and write that retries after interrupts.
   Copyright (C) 1993, 1994, 1998, 2002 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

/* Get ssize_t.  */
#include <sys/types.h>
#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#ifdef EINTR
# define IS_EINTR(x) ((x) == EINTR)
#else
# define IS_EINTR(x) 0
#endif

#include <limits.h>

#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif

/* The extra casts work around common compiler bugs.  */
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
   It is necessary at least when t == time_t.  */
#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
                  ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))

#ifndef INT_MAX
# define INT_MAX TYPE_MAXIMUM (int)
#endif

#ifdef SAFE_WRITE
# include "safe-write.h"
# define safe_rw safe_write
# define rw write
#else
# include "safe-read.h"
# define safe_rw safe_read
# define rw read
# undef const
# define const /* empty */
#endif

/* Read(write) up to COUNT bytes at BUF from(to) descriptor FD, retrying if
   interrupted.  Return the actual number of bytes read(written), zero for EOF,
   or SAFE_READ_ERROR(SAFE_WRITE_ERROR) upon error.  */
size_t
safe_rw (int fd, void const *buf, size_t count)
{
  ssize_t result;

  /* POSIX limits COUNT to SSIZE_MAX, but we limit it further, requiring
     that COUNT <= INT_MAX, to avoid triggering a bug in Tru64 5.1.
     When decreasing COUNT, keep the file pointer block-aligned.
     Note that in any case, read(write) may succeed, yet read(write)
     fewer than COUNT bytes, so the caller must be prepared to handle
     partial results.  */
  if (count > INT_MAX)
    count = INT_MAX & ~8191;

  do
    {
      result = rw (fd, buf, count);
    }
  while (result < 0 && IS_EINTR (errno));

  return (size_t) result;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/safe-write.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/safe-write.c used in: Step 805
/* An interface to write that retries after interrupts.
   Copyright (C) 2002 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.  */

#define SAFE_WRITE
#include "safe-read.c"

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/same.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/same.c used in: Step 806
/* Determine whether two file names refer to the same file.
   Copyright (C) 1997-2000, 2002-2003 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.  */

/* written by Jim Meyering */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#ifndef errno
extern int errno;
#endif

#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif

#include "same.h"
#include "dirname.h"
#include "error.h"
#include "xalloc.h"

#ifndef HAVE_DECL_FREE
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_FREE
void free ();
#endif

#define SAME_INODE(Stat_buf_1, Stat_buf_2) \
  ((Stat_buf_1).st_ino == (Stat_buf_2).st_ino \
   && (Stat_buf_1).st_dev == (Stat_buf_2).st_dev)

/* Return nonzero if SOURCE and DEST point to the same name in the same
   directory.  */

int
same_name (const char *source, const char *dest)
{
  /* Compare the basenames.  */
  char const *source_basename = base_name (source);
  char const *dest_basename = base_name (dest);
  size_t source_baselen = base_len (source_basename);
  size_t dest_baselen = base_len (dest_basename);

  if (source_baselen == dest_baselen
      && memcmp (source_basename, dest_basename, dest_baselen) == 0)
    {
      struct stat source_dir_stats;
      struct stat dest_dir_stats;
      char *source_dirname, *dest_dirname;

      /* Compare the parent directories (via the device and inode numbers).  */
      source_dirname = dir_name (source);
      dest_dirname = dir_name (dest);

      if (stat (source_dirname, &source_dir_stats))
    {
      /* Shouldn't happen.  */
      error (1, errno, "%s", source_dirname);
    }

      if (stat (dest_dirname, &dest_dir_stats))
    {
      /* Shouldn't happen.  */
      error (1, errno, "%s", dest_dirname);
    }

      free (source_dirname);
      free (dest_dirname);

      if (SAME_INODE (source_dir_stats, dest_dir_stats))
    return 1;
    }

  return 0;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/save-cwd.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/save-cwd.c used in: Step 807
/* save-cwd.c -- Save and restore current working directory.
   Copyright (C) 1995, 1997, 1998, 2003 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.  */

/* Written by Jim Meyering.  */

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>

#ifdef STDC_HEADERS
# include <stdlib.h>
#endif

#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#if HAVE_FCNTL_H
# include <fcntl.h>
#else
# include <sys/file.h>
#endif

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#ifndef O_DIRECTORY
# define O_DIRECTORY 0
#endif

#include "save-cwd.h"
#include "error.h"
#include "xgetcwd.h"

/* Record the location of the current working directory in CWD so that
   the program may change to other directories and later use restore_cwd
   to return to the recorded location.  This function may allocate
   space using malloc (via xgetcwd) or leave a file descriptor open;
   use free_cwd to perform the necessary free or close.  Upon failure,
   no memory is allocated, any locally opened file descriptors are
   closed;  return non-zero -- in that case, free_cwd need not be
   called, but doing so is ok.  Otherwise, return zero.

   The `raison d'etre' for this interface is that some systems lack
   support for fchdir, and getcwd is not robust or as efficient.
   So, we prefer to use the open/fchdir approach, but fall back on
   getcwd if necessary.  Some systems lack fchdir altogether: OS/2,
   Cygwin (as of March 2003), SCO Xenix.  At least SunOS4 and Irix 5.3
   provide the function, yet it doesn't work for partitions on which
   auditing is enabled.  */

int
save_cwd (struct saved_cwd *cwd)
{
  static int have_working_fchdir = 1;

  cwd->desc = -1;
  cwd->name = NULL;

  if (have_working_fchdir)
    {
#if HAVE_FCHDIR
      cwd->desc = open (".", O_RDONLY | O_DIRECTORY);
      if (cwd->desc < 0)
    {
      error (0, errno, "cannot open current directory");
      return 1;
    }

# if __sun__ || sun
      /* On SunOS 4 and IRIX 5.3, fchdir returns EINVAL when auditing
     is enabled, so we have to fall back to chdir.  */
      if (fchdir (cwd->desc))
    {
      if (errno == EINVAL)
        {
          close (cwd->desc);
          cwd->desc = -1;
          have_working_fchdir = 0;
        }
      else
        {
          error (0, errno, "current directory");
          close (cwd->desc);
          cwd->desc = -1;
          return 1;
        }
    }
# endif /* __sun__ || sun */
#else
# define fchdir(x) (abort (), 0)
      have_working_fchdir = 0;
#endif
    }

  if (!have_working_fchdir)
    {
      cwd->name = xgetcwd ();
      if (cwd->name == NULL)
    {
      error (0, errno, "cannot get current directory");
      return 1;
    }
    }
  return 0;
}

/* Change to recorded location, CWD, in directory hierarchy.
   Upon failure, return nonzero (errno is set by chdir or fchdir).
   Upon success, return zero.  */

int
restore_cwd (const struct saved_cwd *cwd)
{
  if (0 <= cwd->desc)
    return fchdir (cwd->desc) < 0;
  else
    return chdir (cwd->name) < 0;
}

void
free_cwd (struct saved_cwd *cwd)
{
  if (cwd->desc >= 0)
    close (cwd->desc);
  if (cwd->name)
    free (cwd->name);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/savedir.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/savedir.c used in: Step 808
/* savedir.c -- save the list of files in a directory in a string

   Copyright 1990, 1997, 1998, 1999, 2000, 2001 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.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#if HAVE_DIRENT_H
# include <dirent.h>
#else
# define dirent direct
# if HAVE_SYS_NDIR_H
#  include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
#  include <sys/dir.h>
# endif
# if HAVE_NDIR_H
#  include <ndir.h>
# endif
#endif

#ifdef CLOSEDIR_VOID
/* Fake a return value. */
# define CLOSEDIR(d) (closedir (d), 0)
#else
# define CLOSEDIR(d) closedir (d)
#endif

#ifdef STDC_HEADERS
# include <stdlib.h>
# include <string.h>
#endif
#ifndef NULL
# define NULL 0
#endif

#include "savedir.h"
#include "xalloc.h"

/* Return a freshly allocated string containing the filenames
   in directory DIR, separated by '\0' characters;
   the end is marked by two '\0' characters in a row.
   Return NULL (setting errno) if DIR cannot be opened, read, or closed.  */

#ifndef NAME_SIZE_DEFAULT
# define NAME_SIZE_DEFAULT 512
#endif

char *
savedir (const char *dir)
{
  DIR *dirp;
  struct dirent *dp;
  char *name_space;
  size_t allocated = NAME_SIZE_DEFAULT;
  size_t used = 0;
  int save_errno;

  dirp = opendir (dir);
  if (dirp == NULL)
    return NULL;

  name_space = xmalloc (allocated);

  errno = 0;
  while ((dp = readdir (dirp)) != NULL)
    {
      /* Skip "", ".", and "..".  "" is returned by at least one buggy
         implementation: Solaris 2.4 readdir on NFS filesystems.  */
      char const *entry = dp->d_name;
      if (entry[entry[0] != '.' ? 0 : entry[1] != '.' ? 1 : 2] != '\0')
    {
      size_t entry_size = strlen (entry) + 1;
      if (used + entry_size < used)
        xalloc_die ();
      if (allocated <= used + entry_size)
        {
          do
        {
          if (2 * allocated < allocated)
            xalloc_die ();
          allocated *= 2;
        }
          while (allocated <= used + entry_size);

          name_space = xrealloc (name_space, allocated);
        }
      memcpy (name_space + used, entry, entry_size);
      used += entry_size;
    }
    }
  name_space[used] = '\0';
  save_errno = errno;
  if (CLOSEDIR (dirp) != 0)
    save_errno = errno;
  if (save_errno != 0)
    {
      free (name_space);
      errno = save_errno;
      return NULL;
    }
  return name_space;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/settime.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/settime.c used in: Step 809
/* settime -- set the system clock
   Copyright (C) 2002 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.  */

/* Written by Paul Eggert.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include "timespec.h"

/* Set the system time.  */

int
settime (struct timespec const *ts)
{
#if defined CLOCK_REALTIME && HAVE_CLOCK_SETTIME
  if (clock_settime (CLOCK_REALTIME, ts) == 0)
    return 0;
#endif

  {
    struct timeval tv;

    tv.tv_sec = ts->tv_sec;
    tv.tv_usec = ts->tv_nsec / 1000;
    return settimeofday (&tv, 0);
  }
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/sha.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/sha.c used in: Step 810
/* sha.c - Functions to compute the SHA1 hash (message-digest) of files
   or blocks of memory.  Complies to the NIST specification FIPS-180-1.

   Copyright (C) 2000, 2001, 2003 Scott G. Miller

   Credits:
      Robert Klep <robert@ilse.nl>  -- Expansion function fix
*/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>

#if STDC_HEADERS || defined _LIBC
# include <stdlib.h>
# include <string.h>
#else
# ifndef HAVE_MEMCPY
#  define memcpy(d, s, n) bcopy ((s), (d), (n))
# endif
#endif

#include "md5.h"
#include "sha.h"
#include "unlocked-io.h"

/*
  Not-swap is a macro that does an endian swap on architectures that are
  big-endian, as SHA needs some data in a little-endian format
*/

#ifdef WORDS_BIGENDIAN
# define NOTSWAP(n) (n)
# define SWAP(n)                            \
    (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
#else
# define NOTSWAP(n)                                                         \
    (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
# define SWAP(n) (n)
#endif

#define BLOCKSIZE 4096
/* Ensure that BLOCKSIZE is a multiple of 64.  */
#if BLOCKSIZE % 64 != 0
/* FIXME-someday (soon?): use #error instead of this kludge.  */
"invalid BLOCKSIZE"
#endif

/* This array contains the bytes used to pad the buffer to the next
   64-byte boundary.  (RFC 1321, 3.1: Step 1)  */
static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ...  */ };


/*
  Takes a pointer to a 160 bit block of data (five 32 bit ints) and
  intializes it to the start constants of the SHA1 algorithm.  This
  must be called before using hash in the call to sha_hash
*/
void
sha_init_ctx (struct sha_ctx *ctx)
{
  ctx->A = 0x67452301;
  ctx->B = 0xefcdab89;
  ctx->C = 0x98badcfe;
  ctx->D = 0x10325476;
  ctx->E = 0xc3d2e1f0;

  ctx->total[0] = ctx->total[1] = 0;
  ctx->buflen = 0;
}

/* Put result from CTX in first 20 bytes following RESBUF.  The result
   must be in little endian byte order.

   IMPORTANT: On some systems it is required that RESBUF is correctly
   aligned for a 32 bits value.  */
void *
sha_read_ctx (const struct sha_ctx *ctx, void *resbuf)
{
  ((md5_uint32 *) resbuf)[0] = NOTSWAP (ctx->A);
  ((md5_uint32 *) resbuf)[1] = NOTSWAP (ctx->B);
  ((md5_uint32 *) resbuf)[2] = NOTSWAP (ctx->C);
  ((md5_uint32 *) resbuf)[3] = NOTSWAP (ctx->D);
  ((md5_uint32 *) resbuf)[4] = NOTSWAP (ctx->E);

  return resbuf;
}

/* Process the remaining bytes in the internal buffer and the usual
   prolog according to the standard and write the result to RESBUF.

   IMPORTANT: On some systems it is required that RESBUF is correctly
   aligned for a 32 bits value.  */
void *
sha_finish_ctx (struct sha_ctx *ctx, void *resbuf)
{
  /* Take yet unprocessed bytes into account.  */
  md5_uint32 bytes = ctx->buflen;
  size_t pad;

  /* Now count remaining bytes.  */
  ctx->total[0] += bytes;
  if (ctx->total[0] < bytes)
    ++ctx->total[1];

  pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
  memcpy (&ctx->buffer[bytes], fillbuf, pad);

  /* Put the 64-bit file length in *bits* at the end of the buffer.  */
  *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = NOTSWAP (ctx->total[0] << 3);
  *(md5_uint32 *) &ctx->buffer[bytes + pad] = NOTSWAP ((ctx->total[1] << 3) |
                            (ctx->total[0] >> 29));

  /* Process last bytes.  */
  sha_process_block (ctx->buffer, bytes + pad + 8, ctx);

  return sha_read_ctx (ctx, resbuf);
}

/* Compute SHA1 message digest for bytes read from STREAM.  The
   resulting message digest number will be written into the 16 bytes
   beginning at RESBLOCK.  */
int
sha_stream (FILE *stream, void *resblock)
{
  struct sha_ctx ctx;
  char buffer[BLOCKSIZE + 72];
  size_t sum;

  /* Initialize the computation context.  */
  sha_init_ctx (&ctx);

  /* Iterate over full file contents.  */
  while (1)
    {
      /* We read the file in blocks of BLOCKSIZE bytes.  One call of the
     computation function processes the whole buffer so that with the
     next round of the loop another block can be read.  */
      size_t n;
      sum = 0;

      /* Read block.  Take care for partial reads.  */
      while (1)
    {
      n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream);

      sum += n;

      if (sum == BLOCKSIZE)
        break;

      if (n == 0)
        {
          /* Check for the error flag IFF N == 0, so that we don't
         exit the loop after a partial read due to e.g., EAGAIN
         or EWOULDBLOCK.  */
          if (ferror (stream))
        return 1;
          goto process_partial_block;
        }

      /* We've read at least one byte, so ignore errors.  But always
         check for EOF, since feof may be true even though N > 0.
         Otherwise, we could end up calling fread after EOF.  */
      if (feof (stream))
        goto process_partial_block;
    }

      /* Process buffer with BLOCKSIZE bytes.  Note that
            BLOCKSIZE % 64 == 0
       */
      sha_process_block (buffer, BLOCKSIZE, &ctx);
    }

 process_partial_block:;

  /* Process any remaining bytes.  */
  if (sum > 0)
    sha_process_bytes (buffer, sum, &ctx);

  /* Construct result in desired memory.  */
  sha_finish_ctx (&ctx, resblock);
  return 0;
}

/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
   result is always in little endian byte order, so that a byte-wise
   output yields to the wanted ASCII representation of the message
   digest.  */
void *
sha_buffer (const char *buffer, size_t len, void *resblock)
{
  struct sha_ctx ctx;

  /* Initialize the computation context.  */
  sha_init_ctx (&ctx);

  /* Process whole buffer but last len % 64 bytes.  */
  sha_process_bytes (buffer, len, &ctx);

  /* Put result in desired memory area.  */
  return sha_finish_ctx (&ctx, resblock);
}

void
sha_process_bytes (const void *buffer, size_t len, struct sha_ctx *ctx)
{
  /* When we already have some bits in our internal buffer concatenate
     both inputs first.  */
  if (ctx->buflen != 0)
    {
      size_t left_over = ctx->buflen;
      size_t add = 128 - left_over > len ? len : 128 - left_over;

      memcpy (&ctx->buffer[left_over], buffer, add);
      ctx->buflen += add;

      if (ctx->buflen > 64)
    {
      sha_process_block (ctx->buffer, ctx->buflen & ~63, ctx);

      ctx->buflen &= 63;
      /* The regions in the following copy operation cannot overlap.  */
      memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63],
          ctx->buflen);
    }

      buffer = (const char *) buffer + add;
      len -= add;
    }

  /* Process available complete blocks.  */
  if (len >= 64)
    {
#if !_STRING_ARCH_unaligned
/* To check alignment gcc has an appropriate operator.  Other
   compilers don't.  */
# if __GNUC__ >= 2
#  define UNALIGNED_P(p) (((md5_uintptr) p) % __alignof__ (md5_uint32) != 0)
# else
#  define UNALIGNED_P(p) (((md5_uintptr) p) % sizeof (md5_uint32) != 0)
# endif
      if (UNALIGNED_P (buffer))
    while (len > 64)
      {
        sha_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx);
        buffer = (const char *) buffer + 64;
        len -= 64;
      }
      else
#endif
    {
      sha_process_block (buffer, len & ~63, ctx);
      buffer = (const char *) buffer + (len & ~63);
      len &= 63;
    }
    }

  /* Move remaining bytes in internal buffer.  */
  if (len > 0)
    {
      size_t left_over = ctx->buflen;

      memcpy (&ctx->buffer[left_over], buffer, len);
      left_over += len;
      if (left_over >= 64)
    {
      sha_process_block (ctx->buffer, 64, ctx);
      left_over -= 64;
      memcpy (ctx->buffer, &ctx->buffer[64], left_over);
    }
      ctx->buflen = left_over;
    }
}

/* --- Code below is the primary difference between md5.c and sha.c --- */

/* SHA1 round constants */
#define K1 0x5a827999L
#define K2 0x6ed9eba1L
#define K3 0x8f1bbcdcL
#define K4 0xca62c1d6L

/* Round functions.  Note that F2 is the same as F4.  */
#define F1(B,C,D) ( D ^ ( B & ( C ^ D ) ) )
#define F2(B,C,D) (B ^ C ^ D)
#define F3(B,C,D) ( ( B & C ) | ( D & ( B | C ) ) )
#define F4(B,C,D) (B ^ C ^ D)

/* Process LEN bytes of BUFFER, accumulating context into CTX.
   It is assumed that LEN % 64 == 0.
   Most of this code comes from GnuPG's cipher/sha1.c.  */

void
sha_process_block (const void *buffer, size_t len, struct sha_ctx *ctx)
{
  const md5_uint32 *words = buffer;
  size_t nwords = len / sizeof (md5_uint32);
  const md5_uint32 *endp = words + nwords;
  md5_uint32 x[16];
  md5_uint32 a = ctx->A;
  md5_uint32 b = ctx->B;
  md5_uint32 c = ctx->C;
  md5_uint32 d = ctx->D;
  md5_uint32 e = ctx->E;

  /* First increment the byte count.  RFC 1321 specifies the possible
     length of the file up to 2^64 bits.  Here we only compute the
     number of bytes.  Do a double word increment.  */
  ctx->total[0] += len;
  if (ctx->total[0] < len)
    ++ctx->total[1];

#define M(I) ( tm =   x[I&0x0f] ^ x[(I-14)&0x0f] \
            ^ x[(I-8)&0x0f] ^ x[(I-3)&0x0f] \
           , (x[I&0x0f] = rol(tm, 1)) )

#define R(A,B,C,D,E,F,K,M)  do { E += rol( A, 5 )     \
                      + F( B, C, D )  \
                      + K         \
                      + M;        \
                 B = rol( B, 30 );    \
                   } while(0)

  while (words < endp)
    {
      md5_uint32 tm;
      int t;
      /* FIXME: see sha1.c for a better implementation.  */
      for (t = 0; t < 16; t++)
    {
      x[t] = NOTSWAP (*words);
      words++;
    }

      R( a, b, c, d, e, F1, K1, x[ 0] );
      R( e, a, b, c, d, F1, K1, x[ 1] );
      R( d, e, a, b, c, F1, K1, x[ 2] );
      R( c, d, e, a, b, F1, K1, x[ 3] );
      R( b, c, d, e, a, F1, K1, x[ 4] );
      R( a, b, c, d, e, F1, K1, x[ 5] );
      R( e, a, b, c, d, F1, K1, x[ 6] );
      R( d, e, a, b, c, F1, K1, x[ 7] );
      R( c, d, e, a, b, F1, K1, x[ 8] );
      R( b, c, d, e, a, F1, K1, x[ 9] );
      R( a, b, c, d, e, F1, K1, x[10] );
      R( e, a, b, c, d, F1, K1, x[11] );
      R( d, e, a, b, c, F1, K1, x[12] );
      R( c, d, e, a, b, F1, K1, x[13] );
      R( b, c, d, e, a, F1, K1, x[14] );
      R( a, b, c, d, e, F1, K1, x[15] );
      R( e, a, b, c, d, F1, K1, M(16) );
      R( d, e, a, b, c, F1, K1, M(17) );
      R( c, d, e, a, b, F1, K1, M(18) );
      R( b, c, d, e, a, F1, K1, M(19) );
      R( a, b, c, d, e, F2, K2, M(20) );
      R( e, a, b, c, d, F2, K2, M(21) );
      R( d, e, a, b, c, F2, K2, M(22) );
      R( c, d, e, a, b, F2, K2, M(23) );
      R( b, c, d, e, a, F2, K2, M(24) );
      R( a, b, c, d, e, F2, K2, M(25) );
      R( e, a, b, c, d, F2, K2, M(26) );
      R( d, e, a, b, c, F2, K2, M(27) );
      R( c, d, e, a, b, F2, K2, M(28) );
      R( b, c, d, e, a, F2, K2, M(29) );
      R( a, b, c, d, e, F2, K2, M(30) );
      R( e, a, b, c, d, F2, K2, M(31) );
      R( d, e, a, b, c, F2, K2, M(32) );
      R( c, d, e, a, b, F2, K2, M(33) );
      R( b, c, d, e, a, F2, K2, M(34) );
      R( a, b, c, d, e, F2, K2, M(35) );
      R( e, a, b, c, d, F2, K2, M(36) );
      R( d, e, a, b, c, F2, K2, M(37) );
      R( c, d, e, a, b, F2, K2, M(38) );
      R( b, c, d, e, a, F2, K2, M(39) );
      R( a, b, c, d, e, F3, K3, M(40) );
      R( e, a, b, c, d, F3, K3, M(41) );
      R( d, e, a, b, c, F3, K3, M(42) );
      R( c, d, e, a, b, F3, K3, M(43) );
      R( b, c, d, e, a, F3, K3, M(44) );
      R( a, b, c, d, e, F3, K3, M(45) );
      R( e, a, b, c, d, F3, K3, M(46) );
      R( d, e, a, b, c, F3, K3, M(47) );
      R( c, d, e, a, b, F3, K3, M(48) );
      R( b, c, d, e, a, F3, K3, M(49) );
      R( a, b, c, d, e, F3, K3, M(50) );
      R( e, a, b, c, d, F3, K3, M(51) );
      R( d, e, a, b, c, F3, K3, M(52) );
      R( c, d, e, a, b, F3, K3, M(53) );
      R( b, c, d, e, a, F3, K3, M(54) );
      R( a, b, c, d, e, F3, K3, M(55) );
      R( e, a, b, c, d, F3, K3, M(56) );
      R( d, e, a, b, c, F3, K3, M(57) );
      R( c, d, e, a, b, F3, K3, M(58) );
      R( b, c, d, e, a, F3, K3, M(59) );
      R( a, b, c, d, e, F4, K4, M(60) );
      R( e, a, b, c, d, F4, K4, M(61) );
      R( d, e, a, b, c, F4, K4, M(62) );
      R( c, d, e, a, b, F4, K4, M(63) );
      R( b, c, d, e, a, F4, K4, M(64) );
      R( a, b, c, d, e, F4, K4, M(65) );
      R( e, a, b, c, d, F4, K4, M(66) );
      R( d, e, a, b, c, F4, K4, M(67) );
      R( c, d, e, a, b, F4, K4, M(68) );
      R( b, c, d, e, a, F4, K4, M(69) );
      R( a, b, c, d, e, F4, K4, M(70) );
      R( e, a, b, c, d, F4, K4, M(71) );
      R( d, e, a, b, c, F4, K4, M(72) );
      R( c, d, e, a, b, F4, K4, M(73) );
      R( b, c, d, e, a, F4, K4, M(74) );
      R( a, b, c, d, e, F4, K4, M(75) );
      R( e, a, b, c, d, F4, K4, M(76) );
      R( d, e, a, b, c, F4, K4, M(77) );
      R( c, d, e, a, b, F4, K4, M(78) );
      R( b, c, d, e, a, F4, K4, M(79) );

      a = ctx->A += a;
      b = ctx->B += b;
      c = ctx->C += c;
      d = ctx->D += d;
      e = ctx->E += e;
    }
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/stpcpy.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/stpcpy.c used in: Step 811
/* stpcpy.c -- copy a string and return pointer to end of new string
   Copyright (C) 1992, 1995, 1997, 1998 Free Software Foundation, Inc.

   NOTE: The canonical source of this file is maintained with the GNU C Library.
   Bugs can be reported to bug-glibc@prep.ai.mit.edu.

   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.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <string.h>

#undef __stpcpy
#undef stpcpy

#ifndef weak_alias
# define __stpcpy stpcpy
#endif

/* Copy SRC to DEST, returning the address of the terminating '\0' in DEST.  */
char *
__stpcpy (char *dest, const char *src)
{
  register char *d = dest;
  register const char *s = src;

  do
    *d++ = *s;
  while (*s++ != '\0');

  return d - 1;
}
#ifdef weak_alias
weak_alias (__stpcpy, stpcpy)
#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/stripslash.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/stripslash.c used in: Step 812
/* stripslash.c -- remove redundant trailing slashes from a file name
   Copyright (C) 1990, 2001 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if STDC_HEADERS || HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif

#include "dirname.h"

/* Remove trailing slashes from PATH.
   Return nonzero 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).  */

int
strip_trailing_slashes (char *path)
{
  char *base = base_name (path);
  char *base_lim = base + base_len (base);
  int had_slash = *base_lim;
  *base_lim = '\0';
  return had_slash;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/strtoimax.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/strtoimax.c used in: Step 813, Step 814
/* Convert string representation of a number into an intmax_t value.
   Copyright 1999, 2001 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.  */

/* Written by Paul Eggert. */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif

#if HAVE_STDLIB_H
# include <stdlib.h>
#endif

#ifndef PARAMS
# if defined PROTOTYPES || defined __STDC__
#  define PARAMS(Args) Args
# else
#  define PARAMS(Args) ()
# endif
#endif

/* Verify a requirement at compile-time (unlike assert, which is runtime).  */
#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }

#ifdef UNSIGNED
# ifndef HAVE_DECL_STRTOUL
"this configure-time declaration test was not run"
# endif
# if !HAVE_DECL_STRTOUL
unsigned long strtoul PARAMS ((char const *, char **, int));
# endif
# ifndef HAVE_DECL_STRTOULL
"this configure-time declaration test was not run"
# endif
# if !HAVE_DECL_STRTOULL && HAVE_UNSIGNED_LONG_LONG
unsigned long long strtoull PARAMS ((char const *, char **, int));
# endif

#else

# ifndef HAVE_DECL_STRTOL
"this configure-time declaration test was not run"
# endif
# if !HAVE_DECL_STRTOL
long strtol PARAMS ((char const *, char **, int));
# endif
# ifndef HAVE_DECL_STRTOLL
"this configure-time declaration test was not run"
# endif
# if !HAVE_DECL_STRTOLL && HAVE_UNSIGNED_LONG_LONG
long long strtoll PARAMS ((char const *, char **, int));
# endif
#endif

#ifdef UNSIGNED
# undef HAVE_LONG_LONG
# define HAVE_LONG_LONG HAVE_UNSIGNED_LONG_LONG
# define INT uintmax_t
# define strtoimax strtoumax
# define strtol strtoul
# define strtoll strtoull
#else
# define INT intmax_t
#endif

INT
strtoimax (char const *ptr, char **endptr, int base)
{
#if HAVE_LONG_LONG
  verify (size_is_that_of_long_or_long_long,
      (sizeof (INT) == sizeof (long)
       || sizeof (INT) == sizeof (long long)));

  if (sizeof (INT) != sizeof (long))
    return strtoll (ptr, endptr, base);
#else
  verify (size_is_that_of_long,
      sizeof (INT) == sizeof (long));
#endif

  return strtol (ptr, endptr, base);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/strtoumax.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/strtoumax.c used in: Step 814
#define UNSIGNED 1
#include "strtoimax.c"

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/umaxtostr.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/umaxtostr.c used in: Step 815
#define inttostr umaxtostr
#define inttype uintmax_t
#include "inttostr.c"

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/unicodeio.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/unicodeio.c used in: Step 816
/* Unicode character output to streams with locale dependent encoding.

   Copyright (C) 2000-2002 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU Library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library 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.  */

/* Written by Bruno Haible <haible@clisp.cons.org>.  */

/* Note: This file requires the locale_charset() function.  See in
   libiconv-1.8/libcharset/INTEGRATE for how to obtain it.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_STDDEF_H
# include <stddef.h>
#endif

#include <stdio.h>
#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#if HAVE_ICONV
# include <iconv.h>
#endif

#include <error.h>

#include "gettext.h"
#define _(msgid) gettext (msgid)
#define N_(msgid) msgid

/* Specification.  */
#include "unicodeio.h"

/* When we pass a Unicode character to iconv(), we must pass it in a
   suitable encoding. The standardized Unicode encodings are
   UTF-8, UCS-2, UCS-4, UTF-16, UTF-16BE, UTF-16LE, UTF-7.
   UCS-2 supports only characters up to \U0000FFFF.
   UTF-16 and variants support only characters up to \U0010FFFF.
   UTF-7 is way too complex and not supported by glibc-2.1.
   UCS-4 specification leaves doubts about endianness and byte order
   mark. glibc currently interprets it as big endian without byte order
   mark, but this is not backed by an RFC.
   So we use UTF-8. It supports characters up to \U7FFFFFFF and is
   unambiguously defined.  */

/* Stores the UTF-8 representation of the Unicode character wc in r[0..5].
   Returns the number of bytes stored, or -1 if wc is out of range.  */
static int
utf8_wctomb (unsigned char *r, unsigned int wc)
{
  int count;

  if (wc < 0x80)
    count = 1;
  else if (wc < 0x800)
    count = 2;
  else if (wc < 0x10000)
    count = 3;
  else if (wc < 0x200000)
    count = 4;
  else if (wc < 0x4000000)
    count = 5;
  else if (wc <= 0x7fffffff)
    count = 6;
  else
    return -1;

  switch (count)
    {
      /* Note: code falls through cases! */
      case 6: r[5] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x4000000;
      case 5: r[4] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x200000;
      case 4: r[3] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x10000;
      case 3: r[2] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x800;
      case 2: r[1] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0xc0;
      case 1: r[0] = wc;
    }

  return count;
}

/* Luckily, the encoding's name is platform independent.  */
#define UTF8_NAME "UTF-8"

/* Converts the Unicode character CODE to its multibyte representation
   in the current locale and calls the SUCCESS callback on the resulting
   byte sequence.  If an error occurs, invokes the FAILURE callback instead,
   passing it CODE and an English error string.
   Returns whatever the callback returned.
   Assumes that the locale doesn't change between two calls.  */
long
unicode_to_mb (unsigned int code,
           long (*success) PARAMS ((const char *buf, size_t buflen,
                    void *callback_arg)),
           long (*failure) PARAMS ((unsigned int code, const char *msg,
                    void *callback_arg)),
           void *callback_arg)
{
  static int initialized;
  static int is_utf8;
#if HAVE_ICONV
  static iconv_t utf8_to_local;
#endif

  char inbuf[6];
  int count;

  if (!initialized)
    {
      extern const char *locale_charset PARAMS ((void));
      const char *charset = locale_charset ();

      is_utf8 = !strcmp (charset, UTF8_NAME);
#if HAVE_ICONV
      if (!is_utf8)
    {
      utf8_to_local = iconv_open (charset, UTF8_NAME);
      if (utf8_to_local == (iconv_t)(-1))
        /* For an unknown encoding, assume ASCII.  */
        utf8_to_local = iconv_open ("ASCII", UTF8_NAME);
    }
#endif
      initialized = 1;
    }

  /* Test whether the utf8_to_local converter is available at all.  */
  if (!is_utf8)
    {
#if HAVE_ICONV
      if (utf8_to_local == (iconv_t)(-1))
    return failure (code, N_("iconv function not usable"), callback_arg);
#else
      return failure (code, N_("iconv function not available"), callback_arg);
#endif
    }

  /* Convert the character to UTF-8.  */
  count = utf8_wctomb ((unsigned char *) inbuf, code);
  if (count < 0)
    return failure (code, N_("character out of range"), callback_arg);

#if HAVE_ICONV
  if (!is_utf8)
    {
      char outbuf[25];
      const char *inptr;
      size_t inbytesleft;
      char *outptr;
      size_t outbytesleft;
      size_t res;

      inptr = inbuf;
      inbytesleft = count;
      outptr = outbuf;
      outbytesleft = sizeof (outbuf);

      /* Convert the character from UTF-8 to the locale's charset.  */
      res = iconv (utf8_to_local,
           (ICONV_CONST char **)&inptr, &inbytesleft,
           &outptr, &outbytesleft);
      if (inbytesleft > 0 || res == (size_t)(-1)
      /* Irix iconv() inserts a NUL byte if it cannot convert. */
# if !defined _LIBICONV_VERSION && (defined sgi || defined __sgi)
      || (res > 0 && code != 0 && outptr - outbuf == 1 && *outbuf == '\0')
# endif
         )
    return failure (code, NULL, callback_arg);

      /* Avoid glibc-2.1 bug and Solaris 2.7 bug.  */
# if defined _LIBICONV_VERSION \
    || !((__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) || defined __sun)

      /* Get back to the initial shift state.  */
      res = iconv (utf8_to_local, NULL, NULL, &outptr, &outbytesleft);
      if (res == (size_t)(-1))
    return failure (code, NULL, callback_arg);
# endif

      return success (outbuf, outptr - outbuf, callback_arg);
    }
#endif

  /* At this point, is_utf8 is true, so no conversion is needed.  */
  return success (inbuf, count, callback_arg);
}

/* Simple success callback that outputs the converted string.
   The STREAM is passed as callback_arg.  */
long
fwrite_success_callback (const char *buf, size_t buflen, void *callback_arg)
{
  FILE *stream = (FILE *) callback_arg;

  fwrite (buf, 1, buflen, stream);
  return 0;
}

/* Simple failure callback that displays an error and exits.  */
static long
exit_failure_callback (unsigned int code, const char *msg, void *callback_arg)
{
  if (msg == NULL)
    error (1, 0, _("cannot convert U+%04X to local character set"), code);
  else
    error (1, 0, _("cannot convert U+%04X to local character set: %s"), code,
       gettext (msg));
  return -1;
}

/* Simple failure callback that displays a fallback representation in plain
   ASCII, using the same notation as ISO C99 strings.  */
static long
fallback_failure_callback (unsigned int code, const char *msg, void *callback_arg)
{
  FILE *stream = (FILE *) callback_arg;

  if (code < 0x10000)
    fprintf (stream, "\\u%04X", code);
  else
    fprintf (stream, "\\U%08X", code);
  return -1;
}

/* Outputs the Unicode character CODE to the output stream STREAM.
   Upon failure, exit if exit_on_error is true, otherwise output a fallback
   notation.  */
void
print_unicode_char (FILE *stream, unsigned int code, int exit_on_error)
{
  unicode_to_mb (code, fwrite_success_callback,
         exit_on_error
         ? exit_failure_callback
         : fallback_failure_callback,
         stream);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/userspec.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/userspec.c used in: Step 817
/* userspec.c -- Parse a user and group string.
   Copyright (C) 1989-1992, 1997, 1998, 2000, 2002 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.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#ifdef __GNUC__
# define alloca __builtin_alloca
#else
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
 #  pragma alloca
#  else
char *alloca ();
#  endif
# endif
#endif

#include <stdio.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>

#if HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif

#if HAVE_LIMITS_H
# include <limits.h>
#endif

#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
# ifndef strchr
#  define strchr index
# endif
#endif

#if STDC_HEADERS
# include <stdlib.h>
#endif

#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#include "xalloc.h"
#include "xstrtol.h"

#include "gettext.h"
#define _(msgid) gettext (msgid)
#define N_(msgid) msgid

#ifndef _POSIX_VERSION
struct passwd *getpwnam ();
struct group *getgrnam ();
struct group *getgrgid ();
#endif

#ifndef HAVE_ENDGRENT
# define endgrent() ((void) 0)
#endif

#ifndef HAVE_ENDPWENT
# define endpwent() ((void) 0)
#endif

#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif

/* The extra casts work around common compiler bugs.  */
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
   It is necessary at least when t == time_t.  */
#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
                  ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))

#ifndef UID_T_MAX
# define UID_T_MAX TYPE_MAXIMUM (uid_t)
#endif

#ifndef GID_T_MAX
# define GID_T_MAX TYPE_MAXIMUM (gid_t)
#endif

/* MAXUID may come from limits.h or sys/params.h.  */
#ifndef MAXUID
# define MAXUID UID_T_MAX
#endif
#ifndef MAXGID
# define MAXGID GID_T_MAX
#endif

/* Perform the equivalent of the statement `dest = strdup (src);',
   but obtaining storage via alloca instead of from the heap.  */

#define V_STRDUP(dest, src)                     \
  do                                    \
    {                                   \
      int _len = strlen ((src));                    \
      (dest) = (char *) alloca (_len + 1);              \
      strcpy (dest, src);                       \
    }                                   \
  while (0)

/* ISDIGIT differs from isdigit, as follows:
   - Its arg may be any int or unsigned int; it need not be an unsigned char.
   - It's guaranteed to evaluate its argument exactly once.
   - It's typically faster.
   POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
   ISDIGIT_LOCALE unless it's important to use the locale's definition
   of `digit' even when the host does not conform to POSIX.  */
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)

#ifndef strdup
char *strdup ();
#endif

/* Return nonzero if STR represents an unsigned decimal integer,
   otherwise return 0. */

static int
is_number (const char *str)
{
  for (; *str; str++)
    if (!ISDIGIT (*str))
      return 0;
  return 1;
}

/* Extract from NAME, which has the form "[user][:.][group]",
   a USERNAME, UID U, GROUPNAME, and GID G.
   Either user or group, or both, must be present.
   If the group is omitted but the ":" separator is given,
   use the given user's login group.
   If SPEC_ARG contains a `:', then use that as the separator, ignoring
   any `.'s.  If there is no `:', but there is a `.', then first look
   up the entire SPEC_ARG as a login name.  If that look-up fails, then
   try again interpreting the `.'  as a separator.

   USERNAME and GROUPNAME will be in newly malloc'd memory.
   Either one might be NULL instead, indicating that it was not
   given and the corresponding numeric ID was left unchanged.

   Return NULL if successful, a static error message string if not.  */

const char *
parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid,
         char **username_arg, char **groupname_arg)
{
  static const char *E_invalid_user = N_("invalid user");
  static const char *E_invalid_group = N_("invalid group");
  static const char *E_bad_spec =
    N_("cannot get the login group of a numeric UID");
  static const char *E_cannot_omit_both =
    N_("cannot omit both user and group");

  const char *error_msg;
  char *spec;           /* A copy we can write on.  */
  struct passwd *pwd;
  struct group *grp;
  char *g, *u, *separator;
  char *groupname;
  int maybe_retry = 0;
  char *dot = NULL;

  error_msg = NULL;
  *username_arg = *groupname_arg = NULL;
  groupname = NULL;

  V_STRDUP (spec, spec_arg);

  /* Find the POSIX `:' separator if there is one.  */
  separator = strchr (spec, ':');

  /* If there is no colon, then see if there's a `.'.  */
  if (separator == NULL)
    {
      dot = strchr (spec, '.');
      /* If there's no colon but there is a `.', then first look up the
     whole spec, in case it's an OWNER name that includes a dot.
     If that fails, then we'll try again, but interpreting the `.'
     as a separator.  */
      /* FIXME: accepting `.' as the separator is contrary to POSIX.
     someday we should drop support for this.  */
      if (dot)
    maybe_retry = 1;
    }

 retry:

  /* Replace separator with a NUL.  */
  if (separator != NULL)
    *separator = '\0';

  /* Set U and G to non-zero length strings corresponding to user and
     group specifiers or to NULL.  */
  u = (*spec == '\0' ? NULL : spec);

  g = (separator == NULL || *(separator + 1) == '\0'
       ? NULL
       : separator + 1);

  if (u == NULL && g == NULL)
    return _(E_cannot_omit_both);

#ifdef __DJGPP__
  /* Pretend that we are the user U whose group is G.  This makes
     pwd and grp functions ``know'' about the UID and GID of these.  */
  if (u && !is_number (u))
    setenv ("USER", u, 1);
  if (g && !is_number (g))
    setenv ("GROUP", g, 1);
#endif

  if (u != NULL)
    {
      pwd = getpwnam (u);
      if (pwd == NULL)
    {

      if (!is_number (u))
        error_msg = E_invalid_user;
      else
        {
          int use_login_group;
          use_login_group = (separator != NULL && g == NULL);
          if (use_login_group)
        error_msg = E_bad_spec;
          else
        {
          unsigned long int tmp_long;
          if (xstrtoul (u, NULL, 0, &tmp_long, NULL) != LONGINT_OK
              || tmp_long > MAXUID)
            return _(E_invalid_user);
          *uid = tmp_long;
        }
        }
    }
      else
    {
      *uid = pwd->pw_uid;
      if (g == NULL && separator != NULL)
        {
          /* A separator was given, but a group was not specified,
             so get the login group.  */
          *gid = pwd->pw_gid;
          grp = getgrgid (pwd->pw_gid);
          if (grp == NULL)
        {
          /* This is enough room to hold the unsigned decimal
             representation of any 32-bit quantity and the trailing
             zero byte.  */
          char uint_buf[21];
          sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid));
          V_STRDUP (groupname, uint_buf);
        }
          else
        {
          V_STRDUP (groupname, grp->gr_name);
        }
          endgrent ();
        }
    }
      endpwent ();
    }

  if (g != NULL && error_msg == NULL)
    {
      /* Explicit group.  */
      grp = getgrnam (g);
      if (grp == NULL)
    {
      if (!is_number (g))
        error_msg = E_invalid_group;
      else
        {
          unsigned long int tmp_long;
          if (xstrtoul (g, NULL, 0, &tmp_long, NULL) != LONGINT_OK
          || tmp_long > MAXGID)
        return _(E_invalid_group);
          *gid = tmp_long;
        }
    }
      else
    *gid = grp->gr_gid;
      endgrent ();      /* Save a file descriptor.  */

      if (error_msg == NULL)
    V_STRDUP (groupname, g);
    }

  if (error_msg == NULL)
    {
      if (u != NULL)
    {
      *username_arg = strdup (u);
      if (*username_arg == NULL)
        error_msg = xalloc_msg_memory_exhausted;
    }

      if (groupname != NULL && error_msg == NULL)
    {
      *groupname_arg = strdup (groupname);
      if (*groupname_arg == NULL)
        {
          if (*username_arg != NULL)
        {
          free (*username_arg);
          *username_arg = NULL;
        }
          error_msg = xalloc_msg_memory_exhausted;
        }
    }
    }

  if (error_msg && maybe_retry)
    {
      maybe_retry = 0;
      separator = dot;
      error_msg = NULL;
      goto retry;
    }

  return _(error_msg);
}

#ifdef TEST

# define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s))

int
main (int argc, char **argv)
{
  int i;

  for (i = 1; i < argc; i++)
    {
      const char *e;
      char *username, *groupname;
      uid_t uid;
      gid_t gid;
      char *tmp;

      tmp = strdup (argv[i]);
      e = parse_user_spec (tmp, &uid, &gid, &username, &groupname);
      free (tmp);
      printf ("%s: %u %u %s %s %s\n",
          argv[i],
          (unsigned int) uid,
          (unsigned int) gid,
          NULL_CHECK (username),
          NULL_CHECK (groupname),
          NULL_CHECK (e));
    }

  exit (0);
}

#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/version-etc.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/version-etc.c used in: Step 818
/* Utility to help print --version output in a consistent format.
   Copyright (C) 1999, 2000, 2001, 2002, 2003 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.  */

/* Written by Jim Meyering. */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include "unlocked-io.h"
#include "version-etc.h"

#include "gettext.h"
#define _(msgid) gettext (msgid)

/* Default copyright goes to the FSF. */

char* version_etc_copyright =
  /* Do *not* mark this string for translation.  */
  "Copyright (C) 2003 Free Software Foundation, Inc.";


/* Display the --version information the standard way.

   If COMMAND_NAME is NULL, the PACKAGE is asumed to be the name of
   the program.  The formats are therefore:

   PACKAGE VERSION

   or

   COMMAND_NAME (PACKAGE) VERSION.  */
void
version_etc (FILE *stream,
         const char *command_name, const char *package,
         const char *version, const char *authors)
{
  if (command_name)
    fprintf (stream, "%s (%s) %s\n", command_name, package, version);
  else
    fprintf (stream, "%s %s\n", package, version);
  fprintf (stream, _("Written by %s.\n"), authors);
  putc ('\n', stream);

  fputs (version_etc_copyright, stream);
  putc ('\n', stream);

  fputs (_("\
This is free software; see the source for copying conditions.  There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"),
     stream);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xgetcwd.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xgetcwd.c used in: Step 819
/* xgetcwd.c -- return current directory with unlimited length
   Copyright (C) 1992, 1996, 2000, 2001, 2003 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.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <errno.h>
#ifndef errno
extern int errno;
#endif

#include <sys/types.h>

#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#if HAVE_GETCWD
char *getcwd ();
#else
# include "pathmax.h"
# define INITIAL_BUFFER_SIZE (PATH_MAX + 1)
char *getwd ();
# define getcwd(Buf, Max) getwd (Buf)
#endif

#include "xalloc.h"
#include "xgetcwd.h"

/* Return the current directory, newly allocated, assuming it fits
   within PATH_MAX bytes -- this is a common system-imposed limit
   on how getcwd works.
   Upon an out-of-memory error, call xalloc_die.
   Upon any other type of error, return NULL.  */

char *
xgetcwd (void)
{
#if HAVE_GETCWD_NULL
  char *cwd = getcwd (NULL, 0);
  if (! cwd && errno == ENOMEM)
    xalloc_die ();
  return cwd;
#else

  /* The initial buffer size for the working directory.  A power of 2
     detects arithmetic overflow earlier, but is not required.  */
# ifndef INITIAL_BUFFER_SIZE
#  define INITIAL_BUFFER_SIZE 128
# endif

  size_t buf_size = INITIAL_BUFFER_SIZE;

  while (1)
    {
      char *buf = xmalloc (buf_size);
      char *cwd = getcwd (buf, buf_size);
      int saved_errno;
      if (cwd)
    return cwd;
      saved_errno = errno;
      free (buf);
      if (saved_errno != ERANGE)
    return NULL;
      buf_size *= 2;
      if (buf_size == 0)
    xalloc_die ();
    }
#endif
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xgethostname.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xgethostname.c used in: Step 820
/* xgethostname.c -- return current hostname with unlimited length
   Copyright (C) 1992, 1996, 2000, 2001 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.  */

/* written by Jim Meyering */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#include "error.h"
#include "xalloc.h"

#ifndef ENAMETOOLONG
# define ENAMETOOLONG 9999
#endif

#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif

int gethostname ();

#ifndef INITIAL_HOSTNAME_LENGTH
# define INITIAL_HOSTNAME_LENGTH 34
#endif

char *
xgethostname ()
{
  char *hostname;
  size_t size;

  size = INITIAL_HOSTNAME_LENGTH;
  /* Use size + 1 here rather than size to work around the bug
     in SunOS5.5's gethostname whereby it NUL-terminates HOSTNAME
     even when the name is longer than the supplied buffer.  */
  hostname = xmalloc (size + 1);
  while (1)
    {
      int k = size - 1;
      int err;

      errno = 0;
      hostname[k] = '\0';
      err = gethostname (hostname, size);
      if (err >= 0 && hostname[k] == '\0')
    break;
      else if (err < 0 && errno != ENAMETOOLONG && errno != 0)
    error (EXIT_FAILURE, errno, "gethostname");
      size *= 2;
      hostname = xrealloc (hostname, size + 1);
    }

  return hostname;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xmalloc.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xmalloc.c used in: Step 821
/* xmalloc.c -- malloc with out of memory checking
   Copyright (C) 1990-1999, 2000, 2002 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>

#if STDC_HEADERS
# include <stdlib.h>
#else
void *calloc ();
void *malloc ();
void *realloc ();
void free ();
#endif

#include "gettext.h"
#define _(msgid) gettext (msgid)
#define N_(msgid) msgid

#include "error.h"
#include "xalloc.h"

#ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
#endif

/* The following tests require AC_PREREQ(2.54).  */

#ifndef HAVE_MALLOC
"you must run the autoconf test for a GNU libc compatible malloc"
#endif

#ifndef HAVE_REALLOC
"you must run the autoconf test for a GNU libc compatible realloc"
#endif

/* Exit value when the requested amount of memory is not available.
   The caller may set it to some other value.  */
int xalloc_exit_failure = EXIT_FAILURE;

/* If non NULL, call this function when memory is exhausted. */
void (*xalloc_fail_func) PARAMS ((void)) = 0;

/* If XALLOC_FAIL_FUNC is NULL, or does return, display this message
   before exiting when memory is exhausted.  Goes through gettext. */
char const xalloc_msg_memory_exhausted[] = N_("memory exhausted");

void
xalloc_die (void)
{
  if (xalloc_fail_func)
    (*xalloc_fail_func) ();
  error (xalloc_exit_failure, 0, "%s", _(xalloc_msg_memory_exhausted));
  /* The `noreturn' cannot be given to error, since it may return if
     its first argument is 0.  To help compilers understand the
     xalloc_die does terminate, call exit. */
  exit (EXIT_FAILURE);
}

/* Allocate N bytes of memory dynamically, with error checking.  */

void *
xmalloc (size_t n)
{
  void *p;

  p = malloc (n);
  if (p == 0)
    xalloc_die ();
  return p;
}

/* Change the size of an allocated block of memory P to N bytes,
   with error checking.  */

void *
xrealloc (void *p, size_t n)
{
  p = realloc (p, n);
  if (p == 0)
    xalloc_die ();
  return p;
}

/* Allocate memory for N elements of S bytes, with error checking.  */

void *
xcalloc (size_t n, size_t s)
{
  void *p;

  p = calloc (n, s);
  if (p == 0)
    xalloc_die ();
  return p;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xmemcoll.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xmemcoll.c used in: Step 822
/* Locale-specific memory comparison.
   Copyright (C) 2002 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.  */

/* Contributed by Paul Eggert <eggert@twinsun.com>.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#include <stdlib.h>

#include "gettext.h"
#define _(msgid) gettext (msgid)

#include "error.h"
#include "memcoll.h"
#include "quotearg.h"
#include "xmemcoll.h"

/* Exit value when xmemcoll fails.
   The caller may set it to some other value.  */
int xmemcoll_exit_failure = EXIT_FAILURE;

/* Compare S1 (with length S1LEN) and S2 (with length S2LEN) according
   to the LC_COLLATE locale.  S1 and S2 do not overlap, and are not
   adjacent.  Temporarily modify the bytes after S1 and S2, but
   restore their original contents before returning.  Report an error
   and exit if there is an error.  */

int
xmemcoll (char *s1, size_t s1len, char *s2, size_t s2len)
{
  int diff = memcoll (s1, s1len, s2, s2len);
  int collation_errno = errno;

  if (collation_errno)
    {
      error (0, collation_errno, _("string comparison failed"));
      error (0, 0, _("Set LC_ALL='C' to work around the problem."));
      error (xmemcoll_exit_failure, 0,
         _("The strings compared were %s and %s."),
         quotearg_n_style_mem (0, locale_quoting_style, s1, s1len),
         quotearg_n_style_mem (1, locale_quoting_style, s2, s2len));
    }

  return diff;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xnanosleep.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xnanosleep.c used in: Step 823
/* xnanosleep.c -- a more convenient interface to nanosleep
   Copyright (C) 2002, 2003 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.  */

/* Mostly written (for sleep.c) by Paul Eggert.
   Factored out (creating this file) by Jim Meyering.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <time.h>

#if HAVE_CLOCK_GETTIME && defined CLOCK_REALTIME
# define USE_CLOCK_GETTIME 1
#else
# define USE_CLOCK_GETTIME 0
#endif

#if ! USE_CLOCK_GETTIME
# include <sys/time.h>
#endif

#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif

/* The extra casts work around common compiler bugs.  */
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
   It is necessary at least when t == time_t.  */
#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
                  ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
#define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t)))

#ifndef TIME_T_MAX
# define TIME_T_MAX TYPE_MAXIMUM (time_t)
#endif

#include "timespec.h"
#include "xalloc.h"
#include "xnanosleep.h"
#include "xstrtod.h"

/* Subtract the `struct timespec' values X and Y,
   storing the difference in DIFF.
   Return 1 if the difference is positive, otherwise 0.
   Derived from code in the GNU libc manual.  */

static int
timespec_subtract (struct timespec *diff,
           const struct timespec *x, struct timespec *y)
{
  /* Perform the carry for the later subtraction by updating Y. */
  if (x->tv_nsec < y->tv_nsec)
    {
      int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000 + 1;
      y->tv_nsec -= 1000000000 * nsec;
      y->tv_sec += nsec;
    }

  if (1000000000 < x->tv_nsec - y->tv_nsec)
    {
      int nsec = (y->tv_nsec - x->tv_nsec) / 1000000000;
      y->tv_nsec += 1000000000 * nsec;
      y->tv_sec -= nsec;
    }

  /* Compute the time remaining to wait.
     `tv_nsec' is certainly positive. */
  diff->tv_sec = x->tv_sec - y->tv_sec;
  diff->tv_nsec = x->tv_nsec - y->tv_nsec;

  /* Return 1 if result is positive. */
  return y->tv_sec < x->tv_sec;
}

struct timespec *
clock_get_realtime (struct timespec *ts)
{
  int fail;
#if USE_CLOCK_GETTIME
  fail = clock_gettime (CLOCK_REALTIME, ts);
#else
  struct timeval tv;
  fail = gettimeofday (&tv, NULL);
  if (!fail)
    {
      ts->tv_sec = tv.tv_sec;
      ts->tv_nsec = 1000 * tv.tv_usec;
    }
#endif

  if (fail)
    return NULL;

  return ts;
}

/* Sleep until the time (call it WAKE_UP_TIME) specified as
   SECONDS seconds after the time this function is called.
   SECONDS must be non-negative.  If SECONDS is so large that
   it is not representable as a `struct timespec', then use
   the maximum value for that interval.  Return -1 on failure
   (setting errno), 0 on success.  */

int
xnanosleep (double seconds)
{
  int overflow;
  double ns;
  struct timespec ts_start;
  struct timespec ts_sleep;
  struct timespec ts_stop;

  assert (0 <= seconds);

  if (clock_get_realtime (&ts_start) == NULL)
    return -1;

  /* Separate whole seconds from nanoseconds.
     Be careful to detect any overflow.  */
  ts_sleep.tv_sec = seconds;
  ns = 1e9 * (seconds - ts_sleep.tv_sec);
  overflow = ! (ts_sleep.tv_sec <= seconds && 0 <= ns && ns <= 1e9);
  ts_sleep.tv_nsec = ns;

  /* Round up to the next whole number, if necessary, so that we
     always sleep for at least the requested amount of time.  Assuming
     the default rounding mode, we don't have to worry about the
     rounding error when computing 'ns' above, since the error won't
     cause 'ns' to drop below an integer boundary.  */
  ts_sleep.tv_nsec += (ts_sleep.tv_nsec < ns);

  /* Normalize the interval length.  nanosleep requires this.  */
  if (1000000000 <= ts_sleep.tv_nsec)
    {
      time_t t = ts_sleep.tv_sec + 1;

      /* Detect integer overflow.  */
      overflow |= (t < ts_sleep.tv_sec);

      ts_sleep.tv_sec = t;
      ts_sleep.tv_nsec -= 1000000000;
    }

  /* Compute the time until which we should sleep.  */
  ts_stop.tv_sec = ts_start.tv_sec + ts_sleep.tv_sec;
  ts_stop.tv_nsec = ts_start.tv_nsec + ts_sleep.tv_nsec;
  if (1000000000 <= ts_stop.tv_nsec)
    {
      ++ts_stop.tv_sec;
      ts_stop.tv_nsec -= 1000000000;
    }

  /* Detect integer overflow.  */
  overflow |= (ts_stop.tv_sec < ts_start.tv_sec
           || (ts_stop.tv_sec == ts_start.tv_sec
           && ts_stop.tv_nsec < ts_start.tv_nsec));

  if (overflow)
    {
      /* Fix ts_sleep and ts_stop, which may be garbage due to overflow.  */
      ts_sleep.tv_sec = ts_stop.tv_sec = TIME_T_MAX;
      ts_sleep.tv_nsec = ts_stop.tv_nsec = 999999999;
    }

  while (nanosleep (&ts_sleep, NULL) != 0)
    {
      if (errno != EINTR)
    return -1;

      /* POSIX.1-2001 requires that when a process is suspended, then
     resumed, nanosleep (A, B) returns -1, sets errno to EINTR,
     and sets *B to the time remaining at the point of resumption.
     However, some versions of the Linux kernel incorrectly return
     the time remaining at the point of suspension.  Work around
     this bug by computing the remaining time here, rather than by
     relying on nanosleep's computation.  */

      if (! timespec_subtract (&ts_sleep, &ts_stop,
                   clock_get_realtime (&ts_start)))
    break;
    }

  return 0;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xreadlink.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xreadlink.c used in: Step 824
/* xreadlink.c -- readlink wrapper to return the link name in malloc'd storage

   Copyright 2001 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Written by Jim Meyering <jim@meyering.net>  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <errno.h>
#ifndef errno
extern int errno;
#endif

#if HAVE_LIMITS_H
# include <limits.h>
#endif
#if HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#if HAVE_UNISTD_H
# include <unistd.h>
#endif

#ifndef SIZE_MAX
# define SIZE_MAX ((size_t) -1)
#endif
#ifndef SSIZE_MAX
# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
#endif

#include "xalloc.h"
#include "xreadlink.h"

/* Call readlink to get the symbolic link value of FILENAME.
   Return a pointer to that NUL-terminated string in malloc'd storage.
   If readlink fails, return NULL (caller may use errno to diagnose).
   If realloc fails, or if the link value is longer than SIZE_MAX :-),
   give a diagnostic and exit.  */

char *
xreadlink (char const *filename)
{
  /* The initial buffer size for the link value.  A power of 2
     detects arithmetic overflow earlier, but is not required.  */
  size_t buf_size = 128;

  while (1)
    {
      char *buffer = xmalloc (buf_size);
      ssize_t link_length = readlink (filename, buffer, buf_size);

      if (link_length < 0)
    {
      int saved_errno = errno;
      free (buffer);
      errno = saved_errno;
      return NULL;
    }

      if ((size_t) link_length < buf_size)
    {
      buffer[link_length] = 0;
      return buffer;
    }

      free (buffer);
      buf_size *= 2;
      if (SSIZE_MAX < buf_size || (SIZE_MAX / 2 < SSIZE_MAX && buf_size == 0))
    xalloc_die ();
    }
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrdup.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrdup.c used in: Step 825
/* xstrdup.c -- copy a string with out of memory checking
   Copyright (C) 1990, 1996, 1998, 2001 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if STDC_HEADERS || HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif

#include <sys/types.h>

#include "xalloc.h"

/* Return a newly allocated copy of STRING.  */

char *
xstrdup (const char *string)
{
  return strcpy (xmalloc (strlen (string) + 1), string);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtod.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtod.c used in: Step 826
/* xstrtod.c - error-checking interface to strtod
   Copyright (C) 1996, 1999, 2000 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.  */

/* Written by Jim Meyering.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifdef STDC_HEADERS
# include <stdlib.h>
#else
double strtod ();
#endif

#include <errno.h>
#include <stdio.h>
#include <limits.h>
#include "xstrtod.h"

/* Tell the compiler that non-default rounding modes are used.  */
#if 199901 <= __STDC_VERSION__
 #pragma STDC FENV_ACCESS ON
#endif

/* An interface to strtod that encapsulates all the error checking
   one should usually perform.  Like strtod, but upon successful
   conversion put the result in *RESULT and return zero.  Return
   non-zero and don't modify *RESULT upon any failure.  */

int
xstrtod (str, ptr, result)
     const char *str;
     const char **ptr;
     double *result;
{
  double val;
  char *terminator;
  int fail;

  fail = 0;
  errno = 0;
  val = strtod (str, &terminator);

  /* Having a non-zero terminator is an error only when PTR is NULL. */
  if (terminator == str || (ptr == NULL && *terminator != '\0'))
    fail = 1;
  else
    {
      /* Allow underflow (in which case strtod returns zero),
     but flag overflow as an error. */
      if (val != 0.0 && errno == ERANGE)
    fail = 1;
    }

  if (ptr != NULL)
    *ptr = terminator;

  *result = val;
  return fail;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtol.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtol.c used in: Step 827, Step 828
/* A more useful interface to strtol.
   Copyright (C) 1995, 1996, 1998-2001 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.  */

/* Written by Jim Meyering. */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#ifndef __strtol
# define __strtol strtol
# define __strtol_t long int
# define __xstrtol xstrtol
#endif

/* Some pre-ANSI implementations (e.g. SunOS 4)
   need stderr defined if assertion checking is enabled.  */
#include <stdio.h>

#if STDC_HEADERS
# include <stdlib.h>
#endif

#if HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
# ifndef strchr
#  define strchr index
# endif
#endif

#include <assert.h>
#include <ctype.h>

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#if HAVE_LIMITS_H
# include <limits.h>
#endif

#ifndef CHAR_BIT
# define CHAR_BIT 8
#endif

/* The extra casts work around common compiler bugs.  */
#define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
/* The outer cast is needed to work around a bug in Cray C 5.0.3.0.
   It is necessary at least when t == time_t.  */
#define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \
                  ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) : (t) 0))
#define TYPE_MAXIMUM(t) (~ (t) 0 - TYPE_MINIMUM (t))

#if defined (STDC_HEADERS) || (!defined (isascii) && !defined (HAVE_ISASCII))
# define IN_CTYPE_DOMAIN(c) 1
#else
# define IN_CTYPE_DOMAIN(c) isascii(c)
#endif

#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))

#include "xstrtol.h"

#if !HAVE_DECL_STRTOL && !defined strtol
long int strtol ();
#endif

#if !HAVE_DECL_STRTOUL && !defined strtoul
unsigned long int strtoul ();
#endif

#if !HAVE_DECL_STRTOIMAX && !defined strtoimax
intmax_t strtoimax ();
#endif

#if !HAVE_DECL_STRTOUMAX && !defined strtoumax
uintmax_t strtoumax ();
#endif

static int
bkm_scale (__strtol_t *x, int scale_factor)
{
  __strtol_t product = *x * scale_factor;
  if (*x != product / scale_factor)
    return 1;
  *x = product;
  return 0;
}

static int
bkm_scale_by_power (__strtol_t *x, int base, int power)
{
  while (power--)
    if (bkm_scale (x, base))
      return 1;

  return 0;
}

/* FIXME: comment.  */

strtol_error
__xstrtol (const char *s, char **ptr, int strtol_base,
       __strtol_t *val, const char *valid_suffixes)
{
  char *t_ptr;
  char **p;
  __strtol_t tmp;

  assert (0 <= strtol_base && strtol_base <= 36);

  p = (ptr ? ptr : &t_ptr);

  if (! TYPE_SIGNED (__strtol_t))
    {
      const char *q = s;
      while (ISSPACE ((unsigned char) *q))
    ++q;
      if (*q == '-')
    return LONGINT_INVALID;
    }

  errno = 0;
  tmp = __strtol (s, p, strtol_base);
  if (errno != 0)
    return LONGINT_OVERFLOW;

  if (*p == s)
    {
      /* If there is no number but there is a valid suffix, assume the
     number is 1.  The string is invalid otherwise.  */
      if (valid_suffixes && **p && strchr (valid_suffixes, **p))
    tmp = 1;
      else
    return LONGINT_INVALID;
    }

  /* Let valid_suffixes == NULL mean `allow any suffix'.  */
  /* FIXME: update all callers except the ones that allow suffixes
     after the number, changing last parameter NULL to `""'.  */
  if (!valid_suffixes)
    {
      *val = tmp;
      return LONGINT_OK;
    }

  if (**p != '\0')
    {
      int base = 1024;
      int suffixes = 1;
      int overflow;

      if (!strchr (valid_suffixes, **p))
    {
      *val = tmp;
      return LONGINT_INVALID_SUFFIX_CHAR;
    }

      if (strchr (valid_suffixes, '0'))
    {
      /* The ``valid suffix'' '0' is a special flag meaning that
         an optional second suffix is allowed, which can change
         the base.  A suffix "B" (e.g. "100MB") stands for a power
         of 1000, whereas a suffix "iB" (e.g. "100MiB") stands for
         a power of 1024.  If no suffix (e.g. "100M"), assume
         power-of-1024.  */

      switch (p[0][1])
        {
        case 'i':
          if (p[0][2] == 'B')
        suffixes += 2;
          break;

        case 'B':
        case 'D': /* 'D' is obsolescent */
          base = 1000;
          suffixes++;
          break;
        }
    }

      switch (**p)
    {
    case 'b':
      overflow = bkm_scale (&tmp, 512);
      break;

    case 'B':
      overflow = bkm_scale (&tmp, 1024);
      break;

    case 'c':
      overflow = 0;
      break;

    case 'E': /* exa or exbi */
      overflow = bkm_scale_by_power (&tmp, base, 6);
      break;

    case 'G': /* giga or gibi */
    case 'g': /* 'g' is undocumented; for compatibility only */
      overflow = bkm_scale_by_power (&tmp, base, 3);
      break;

    case 'k': /* kilo */
    case 'K': /* kibi */
      overflow = bkm_scale_by_power (&tmp, base, 1);
      break;

    case 'M': /* mega or mebi */
    case 'm': /* 'm' is undocumented; for compatibility only */
      overflow = bkm_scale_by_power (&tmp, base, 2);
      break;

    case 'P': /* peta or pebi */
      overflow = bkm_scale_by_power (&tmp, base, 5);
      break;

    case 'T': /* tera or tebi */
    case 't': /* 't' is undocumented; for compatibility only */
      overflow = bkm_scale_by_power (&tmp, base, 4);
      break;

    case 'w':
      overflow = bkm_scale (&tmp, 2);
      break;

    case 'Y': /* yotta or 2**80 */
      overflow = bkm_scale_by_power (&tmp, base, 8);
      break;

    case 'Z': /* zetta or 2**70 */
      overflow = bkm_scale_by_power (&tmp, base, 7);
      break;

    default:
      *val = tmp;
      return LONGINT_INVALID_SUFFIX_CHAR;
      break;
    }

      if (overflow)
    return LONGINT_OVERFLOW;

      (*p) += suffixes;
    }

  *val = tmp;
  return LONGINT_OK;
}

#ifdef TESTING_XSTRTO

# include <stdio.h>
# include "error.h"

char *program_name;

int
main (int argc, char** argv)
{
  strtol_error s_err;
  int i;

  program_name = argv[0];
  for (i=1; i<argc; i++)
    {
      char *p;
      __strtol_t val;

      s_err = __xstrtol (argv[i], &p, 0, &val, "bckmw");
      if (s_err == LONGINT_OK)
    {
      printf ("%s->%lu (%s)\n", argv[i], val, p);
    }
      else
    {
      STRTOL_FATAL_ERROR (argv[i], "arg", s_err);
    }
    }
  exit (0);
}

#endif /* TESTING_XSTRTO */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtoul.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtoul.c used in: Step 828
#define __strtol strtoul
#define __strtol_t unsigned long int
#define __xstrtol xstrtoul
#include "xstrtol.c"

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtoimax.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtoimax.c used in: Step 829
/* xstrtoimax.c -- A more useful interface to strtoimax.
   Copyright 2001 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.  */

/* Cloned by Jim Meyering. */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif

#define __strtol strtoimax
#define __strtol_t intmax_t
#define __xstrtol xstrtoimax
#include "xstrtol.c"

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtoumax.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtoumax.c used in: Step 830
/* xstrtoumax.c -- A more useful interface to strtoumax.
   Copyright 1999 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   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.  */

/* Written by Paul Eggert. */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_INTTYPES_H
# include <inttypes.h>
#endif

#define __strtol strtoumax
#define __strtol_t uintmax_t
#define __xstrtol xstrtoumax
#include "xstrtol.c"

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/yesno.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/yesno.c used in: Step 831
/* yesno.c -- read a yes/no response from stdin
   Copyright (C) 1990, 1998, 2001 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <ctype.h>
#if HAVE_STDLIB_H
# include <stdlib.h>
#endif
#include <stdio.h>
#include "unlocked-io.h"

/* Read one line from standard input
   and return nonzero if that line begins with y or Y,
   otherwise return 0. */

int rpmatch ();

int
yesno ()
{
  /* We make some assumptions here:
     a) leading white space in the response are not vital
     b) the first 128 characters of the answer are enough (the rest can
    be ignored)
     I cannot think for a situation where this is not ok.  --drepper@gnu  */
  char buf[128];
  int len = 0;
  int c;

  while ((c = getchar ()) != EOF && c != '\n')
    if ((len > 0 && len < 127) || (len == 0 && !isspace (c)))
      buf[len++] = c;
  buf[len] = '\0';

  return rpmatch (buf) == 1;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/strnlen.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/strnlen.c used in: Step 832
/* Find the length of STRING, but scan at most MAXLEN characters.
   Copyright (C) 1996, 1997, 1998, 2000-2002 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if HAVE_STRING_H
# if !STDC_HEADERS && HAVE_MEMORY_H
#  include <memory.h>
# endif
/* Temporarily redefine strnlen so that an inconsistent prototype
   (on at least AIX4.3.2.0 w/gcc-2.95.3) doesn't cause trouble.  */
# define strnlen system_strnlen
# include <string.h>
# undef strnlen
#else
# include <strings.h>
#endif

#ifndef HAVE_DECL_MEMCHR
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_MEMCHR
char *memchr ();
#endif

#undef __strnlen
#undef strnlen

#ifndef weak_alias
# define __strnlen strnlen
#endif

/* Find the length of STRING, but scan at most MAXLEN characters.
   If no '\0' terminator is found in that many characters, return MAXLEN.  */

size_t
__strnlen (const char *string, size_t maxlen)
{
  const char *end = memchr (string, '\0', maxlen);
  return end ? (size_t) (end - string) : maxlen;
}
#ifdef weak_alias
weak_alias (__strnlen, strnlen)
#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getcwd.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getcwd.c used in: Step 833
/* Provide a replacement for the POSIX getcwd function.
   Copyright (C) 2003 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.  */

/* written by Jim Meyering */

#include <config.h>

#include <stdlib.h>
#include <string.h>

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#include <sys/types.h>

#include "pathmax.h"
#include "same.h"

/* Guess high, because that makes the test below more conservative.
   But this is a kludge, because we should really use
   pathconf (".", _PC_NAME_MAX).  But it's probably not worth the cost.  */
#define KLUDGE_POSIX_NAME_MAX 255

#define MAX_SAFE_LEN (PATH_MAX - 1 - KLUDGE_POSIX_NAME_MAX - 1)

/* Undefine getcwd here, as near the use as possible, in case any
   of the files included above define it to rpl_getcwd.  */
#undef getcwd

/* Any declaration of getcwd from headers included above has
   been changed to a declaration of rpl_getcwd.  Declare it here.  */
extern char *getcwd (char *buf, size_t size);

/* This is a wrapper for getcwd.
   Some implementations (at least GNU libc 2.3.1 + linux-2.4.20) return
   non-NULL for a working directory name longer than PATH_MAX, yet the
   returned string is a strict prefix of the desired directory name.
   Upon such a failure, free the offending string, set errno to
   ENAMETOOLONG, and return NULL.

   I've heard that this is a Linux kernel bug, and that it has
   been fixed between 2.4.21-pre3 and 2.4.21-pre4.  */

char *
rpl_getcwd (char *buf, size_t size)
{
  char *cwd = getcwd (buf, size);

  if (cwd == NULL)
    return NULL;

  if (strlen (cwd) <= MAX_SAFE_LEN || same_name (cwd, "."))
    return cwd;

  free (cwd);
  errno = ENAMETOOLONG;
  return NULL;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/pathmax.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/pathmax.h used in: Step 833
/* Define PATH_MAX somehow.  Requires sys/types.h.
   Copyright (C) 1992, 1999, 2001 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.  */

#ifndef _PATHMAX_H
# define _PATHMAX_H

# if HAVE_UNISTD_H
#  include <unistd.h>
# endif

/* Non-POSIX BSD systems might have gcc's limits.h, which doesn't define
   PATH_MAX but might cause redefinition warnings when sys/param.h is
   later included (as on MORE/BSD 4.3).  */
# if defined _POSIX_VERSION || (defined HAVE_LIMITS_H && !defined __GNUC__)
#  include <limits.h>
# endif

# ifndef _POSIX_PATH_MAX
#  define _POSIX_PATH_MAX 255
# endif

# if !defined PATH_MAX && defined _PC_PATH_MAX
#  define PATH_MAX (pathconf ("/", _PC_PATH_MAX) < 1 ? 1024 \
            : pathconf ("/", _PC_PATH_MAX))
# endif

/* Don't include sys/param.h if it already has been.  */
# if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
#  include <sys/param.h>
# endif

# if !defined PATH_MAX && defined MAXPATHLEN
#  define PATH_MAX MAXPATHLEN
# endif

# ifndef PATH_MAX
#  define PATH_MAX _POSIX_PATH_MAX
# endif

#endif /* _PATHMAX_H */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/same.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/same.h used in: Step 833, Step 959, Step 964, Step 970
/* Determine whether two file names refer to the same file.
   Copyright (C) 1997-2000, 2003 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.  */

#ifndef SAME_H_
# define SAME_H_ 1

int
same_name (const char *source, const char *dest);

#endif /* SAME_H_ */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/sig2str.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/sig2str.c used in: Step 834
/* sig2str.c -- convert between signal names and numbers

   Copyright (C) 2002 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.  */

/* Written by Paul Eggert.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <limits.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "sig2str.h"

#ifndef SIGRTMIN
# define SIGRTMIN 0
# undef SIGRTMAX
#endif
#ifndef SIGRTMAX
# define SIGRTMAX (SIGRTMIN - 1)
#endif

#define NUMNAME(name) { SIG##name, #name }

/* Signal names and numbers.  Put the preferred name first.  */
static struct numname { int num; char const name[8]; } numname_table[] =
  {
    /* Signals required by POSIX 1003.1-2001 base, listed in
       traditional numeric order.  */
#ifdef SIGHUP
    NUMNAME (HUP),
#endif
#ifdef SIGINT
    NUMNAME (INT),
#endif
#ifdef SIGQUIT
    NUMNAME (QUIT),
#endif
#ifdef SIGILL
    NUMNAME (ILL),
#endif
#ifdef SIGTRAP
    NUMNAME (TRAP),
#endif
#ifdef SIGABRT
    NUMNAME (ABRT),
#endif
#ifdef SIGFPE
    NUMNAME (FPE),
#endif
#ifdef SIGKILL
    NUMNAME (KILL),
#endif
#ifdef SIGBUS
    NUMNAME (BUS),
#endif
#ifdef SIGSEGV
    NUMNAME (SEGV),
#endif
#ifdef SIGPIPE
    NUMNAME (PIPE),
#endif
#ifdef SIGALRM
    NUMNAME (ALRM),
#endif
#ifdef SIGTERM
    NUMNAME (TERM),
#endif
#ifdef SIGUSR1
    NUMNAME (USR1),
#endif
#ifdef SIGUSR2
    NUMNAME (USR2),
#endif
#ifdef SIGCHLD
    NUMNAME (CHLD),
#endif
#ifdef SIGURG
    NUMNAME (URG),
#endif
#ifdef SIGSTOP
    NUMNAME (STOP),
#endif
#ifdef SIGTSTP
    NUMNAME (TSTP),
#endif
#ifdef SIGCONT
    NUMNAME (CONT),
#endif
#ifdef SIGTTIN
    NUMNAME (TTIN),
#endif
#ifdef SIGTTOU
    NUMNAME (TTOU),
#endif

    /* Signals required by POSIX 1003.1-2001 with the XSI extension.  */
#ifdef SIGSYS
    NUMNAME (SYS),
#endif
#ifdef SIGPOLL
    NUMNAME (POLL),
#endif
#ifdef SIGVTALRM
    NUMNAME (VTALRM),
#endif
#ifdef SIGPROF
    NUMNAME (PROF),
#endif
#ifdef SIGXCPU
    NUMNAME (XCPU),
#endif
#ifdef SIGXFSZ
    NUMNAME (XFSZ),
#endif

    /* Unix Version 7.  */
#ifdef SIGIOT
    NUMNAME (IOT),  /* Older name for ABRT.  */
#endif
#ifdef SIGEMT
    NUMNAME (EMT),
#endif

    /* USG Unix.  */
#ifdef SIGPHONE
    NUMNAME (PHONE),
#endif
#ifdef SIGWIND
    NUMNAME (WIND),
#endif

    /* Unix System V.  */
#ifdef SIGCLD
    NUMNAME (CLD),
#endif
#ifdef SIGPWR
    NUMNAME (PWR),
#endif

    /* GNU/Linux 2.2 and Solaris 8.  */
#ifdef SIGCANCEL
    NUMNAME (CANCEL),
#endif
#ifdef SIGLWP
    NUMNAME (LWP),
#endif
#ifdef SIGWAITING
    NUMNAME (WAITING),
#endif
#ifdef SIGFREEZE
    NUMNAME (FREEZE),
#endif
#ifdef SIGTHAW
    NUMNAME (THAW),
#endif
#ifdef SIGLOST
    NUMNAME (LOST),
#endif
#ifdef SIGWINCH
    NUMNAME (WINCH),
#endif

    /* GNU/Linux 2.2.  */
#ifdef SIGINFO
    NUMNAME (INFO),
#endif
#ifdef SIGIO
    NUMNAME (IO),
#endif
#ifdef SIGSTKFLT
    NUMNAME (STKFLT),
#endif

    /* AIX 5L.  */
#ifdef SIGDANGER
    NUMNAME (DANGER),
#endif
#ifdef SIGGRANT
    NUMNAME (GRANT),
#endif
#ifdef SIGMIGRATE
    NUMNAME (MIGRATE),
#endif
#ifdef SIGMSG
    NUMNAME (MSG),
#endif
#ifdef SIGPRE
    NUMNAME (PRE),
#endif
#ifdef SIGRETRACT
    NUMNAME (RETRACT),
#endif
#ifdef SIGSAK
    NUMNAME (SAK),
#endif
#ifdef SIGSOUND
    NUMNAME (SOUND),
#endif

    /* Older AIX versions.  */
#ifdef SIGALRM1
    NUMNAME (ALRM1),    /* unknown; taken from Bash 2.05 */
#endif
#ifdef SIGKAP
    NUMNAME (KAP),  /* Older name for SIGGRANT.  */
#endif
#ifdef SIGVIRT
    NUMNAME (VIRT), /* unknown; taken from Bash 2.05 */
#endif
#ifdef SIGWINDOW
    NUMNAME (WINDOW),   /* Older name for SIGWINCH.  */
#endif

    /* BeOS */
#ifdef SIGKILLTHR
    NUMNAME (KILLTHR),
#endif

    /* Older HP-UX versions.  */
#ifdef SIGDIL
    NUMNAME (DIL),
#endif

    /* Korn shell and Bash, of uncertain vintage.  */
    { 0, "EXIT" }
  };

#define NUMNAME_ENTRIES (sizeof numname_table / sizeof numname_table[0])

/* ISDIGIT differs from isdigit, as follows:
   - Its arg may be any int or unsigned int; it need not be an unsigned char.
   - It's guaranteed to evaluate its argument exactly once.
   - It's typically faster.
   POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
   ISDIGIT_LOCALE unless it's important to use the locale's definition
   of `digit' even when the host does not conform to POSIX.  */
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)

/* Convert the signal name SIGNAME to a signal number.  Return the
   signal number if successful, -1 otherwise.  */

static int
str2signum (char const *signame)
{
  if (ISDIGIT (*signame))
    {
      char *endp;
      long int n = strtol (signame, &endp, 10);
      if (! *endp && n <= SIGNUM_BOUND)
    return n;
    }
  else
    {
      unsigned i;
      for (i = 0; i < NUMNAME_ENTRIES; i++)
    if (strcmp (numname_table[i].name, signame) == 0)
      return numname_table[i].num;

      {
    char *endp;
    int rtmin = SIGRTMIN;
    int rtmax = SIGRTMAX;

    if (0 < rtmin && strncmp (signame, "RTMIN", 5) == 0)
      {
        long int n = strtol (signame + 5, &endp, 10);
        if (! *endp && 0 <= n && n <= rtmax - rtmin)
          return rtmin + n;
      }
    else if (0 < rtmax && strncmp (signame, "RTMAX", 5) == 0)
      {
        long int n = strtol (signame + 5, &endp, 10);
        if (! *endp && rtmin - rtmax <= n && n <= 0)
          return rtmax + n;
      }
      }
    }

  return -1;
}

/* Convert the signal name SIGNAME to the signal number *SIGNUM.
   Return 0 if successful, -1 otherwise.  */

int
str2sig (char const *signame, int *signum)
{
  *signum = str2signum (signame);
  return *signum < 0 ? -1 : 0;
}

/* Convert SIGNUM to a signal name in SIGNAME.  SIGNAME must point to
   a buffer of at least SIG2STR_MAX bytes.  Return 0 if successful, -1
   otherwise.  */

int
sig2str (int signum, char *signame)
{
  unsigned i;
  for (i = 0; i < NUMNAME_ENTRIES; i++)
    if (numname_table[i].num == signum)
      {
    strcpy (signame, numname_table[i].name);
    return 0;
      }

  {
    int rtmin = SIGRTMIN;
    int rtmax = SIGRTMAX;

    if (! (rtmin <= signum && signum <= rtmax))
      return -1;

    if (signum <= rtmin + (rtmax - rtmin) / 2)
      {
    int delta = signum - rtmin;
    sprintf (signame, delta ? "RTMIN+%d" : "RTMIN", delta);
      }
    else
      {
    int delta = rtmax - signum;
    sprintf (signame, delta ? "RTMAX-%d" : "RTMAX", delta);
      }

    return 0;
  }
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/mountlist.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/mountlist.c used in: Step 835
/* mountlist.c -- return a list of mounted filesystems
   Copyright (C) 1991, 1992, 1997-2003 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <sys/types.h>


#ifdef STDC_HEADERS
# include <stdlib.h>
#else
void free ();
#endif
#if defined STDC_HEADERS || defined HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif
#include "xalloc.h"

#ifndef strstr
char *strstr ();
#endif

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif

#if HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif

#if defined MOUNTED_GETFSSTAT   /* OSF_1 and Darwin1.3.x */
# if HAVE_SYS_UCRED_H
#  include <sys/ucred.h> /* needed by powerpc-apple-darwin1.3.7 */
# endif
# if HAVE_SYS_MOUNT_H
#  include <sys/mount.h>
# endif
# if HAVE_SYS_FS_TYPES_H
#  include <sys/fs_types.h> /* needed by powerpc-apple-darwin1.3.7 */
# endif
# if HAVE_STRUCT_FSSTAT_F_FSTYPENAME
#  define FS_TYPE(Ent) ((Ent).f_fstypename)
# else
#  define FS_TYPE(Ent) mnt_names[(Ent).f_type]
# endif
#endif /* MOUNTED_GETFSSTAT */

#ifdef MOUNTED_GETMNTENT1   /* 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
# include <mntent.h>
# if !defined MOUNTED
#  if defined _PATH_MOUNTED /* GNU libc  */
#   define MOUNTED _PATH_MOUNTED
#  endif
#  if defined MNT_MNTTAB    /* HP-UX.  */
#   define MOUNTED MNT_MNTTAB
#  endif
#  if defined MNTTABNAME    /* Dynix.  */
#   define MOUNTED MNTTABNAME
#  endif
# endif
#endif

#ifdef MOUNTED_GETMNTINFO   /* 4.4BSD.  */
# include <sys/mount.h>
#endif

#ifdef MOUNTED_GETMNT       /* Ultrix.  */
# include <sys/mount.h>
# include <sys/fs_types.h>
#endif

#ifdef MOUNTED_FS_STAT_DEV  /* BeOS.  */
# include <fs_info.h>
# include <dirent.h>
#endif

#ifdef MOUNTED_FREAD        /* SVR2.  */
# include <mnttab.h>
#endif

#ifdef MOUNTED_FREAD_FSTYP  /* SVR3.  */
# include <mnttab.h>
# include <sys/fstyp.h>
# include <sys/statfs.h>
#endif

#ifdef MOUNTED_LISTMNTENT
# include <mntent.h>
#endif

#ifdef MOUNTED_GETMNTENT2   /* SVR4.  */
# include <sys/mnttab.h>
#endif

#ifdef MOUNTED_VMOUNT       /* AIX.  */
# include <fshelp.h>
# include <sys/vfs.h>
#endif

#ifdef DOLPHIN
/* So special that it's not worth putting this in autoconf.  */
# undef MOUNTED_FREAD_FSTYP
# define MOUNTED_GETMNTTBL
#endif

#if HAVE_SYS_MNTENT_H
/* This is to get MNTOPT_IGNORE on e.g. SVR4.  */
# include <sys/mntent.h>
#endif

#undef MNT_IGNORE
#if defined MNTOPT_IGNORE && defined HAVE_HASMNTOPT
# define MNT_IGNORE(M) hasmntopt ((M), MNTOPT_IGNORE)
#else
# define MNT_IGNORE(M) 0
#endif

#include "mountlist.h"
#include "unlocked-io.h"

#ifdef MOUNTED_GETMNTENT1   /* 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
/* Return the value of the hexadecimal number represented by CP.
   No prefix (like '0x') or suffix (like 'h') is expected to be
   part of CP. */
/* FIXME: this can overflow */

static int
xatoi (char *cp)
{
  int val;

  val = 0;
  while (*cp)
    {
      if (*cp >= 'a' && *cp <= 'f')
    val = val * 16 + *cp - 'a' + 10;
      else if (*cp >= 'A' && *cp <= 'F')
    val = val * 16 + *cp - 'A' + 10;
      else if (*cp >= '0' && *cp <= '9')
    val = val * 16 + *cp - '0';
      else
    break;
      cp++;
    }
  return val;
}
#endif /* MOUNTED_GETMNTENT1.  */

#if MOUNTED_GETMNTINFO

# if ! HAVE_F_FSTYPENAME_IN_STATFS
static char *
fstype_to_string (short t)
{
  switch (t)
    {
#  ifdef MOUNT_PC
    case MOUNT_PC:
      return "pc";
#  endif
#  ifdef MOUNT_MFS
    case MOUNT_MFS:
      return "mfs";
#  endif
#  ifdef MOUNT_LO
    case MOUNT_LO:
      return "lo";
#  endif
#  ifdef MOUNT_TFS
    case MOUNT_TFS:
      return "tfs";
#  endif
#  ifdef MOUNT_TMP
    case MOUNT_TMP:
      return "tmp";
#  endif
#  ifdef MOUNT_UFS
   case MOUNT_UFS:
     return "ufs" ;
#  endif
#  ifdef MOUNT_NFS
   case MOUNT_NFS:
     return "nfs" ;
#  endif
#  ifdef MOUNT_MSDOS
   case MOUNT_MSDOS:
     return "msdos" ;
#  endif
#  ifdef MOUNT_LFS
   case MOUNT_LFS:
     return "lfs" ;
#  endif
#  ifdef MOUNT_LOFS
   case MOUNT_LOFS:
     return "lofs" ;
#  endif
#  ifdef MOUNT_FDESC
   case MOUNT_FDESC:
     return "fdesc" ;
#  endif
#  ifdef MOUNT_PORTAL
   case MOUNT_PORTAL:
     return "portal" ;
#  endif
#  ifdef MOUNT_NULL
   case MOUNT_NULL:
     return "null" ;
#  endif
#  ifdef MOUNT_UMAP
   case MOUNT_UMAP:
     return "umap" ;
#  endif
#  ifdef MOUNT_KERNFS
   case MOUNT_KERNFS:
     return "kernfs" ;
#  endif
#  ifdef MOUNT_PROCFS
   case MOUNT_PROCFS:
     return "procfs" ;
#  endif
#  ifdef MOUNT_AFS
   case MOUNT_AFS:
     return "afs" ;
#  endif
#  ifdef MOUNT_CD9660
   case MOUNT_CD9660:
     return "cd9660" ;
#  endif
#  ifdef MOUNT_UNION
   case MOUNT_UNION:
     return "union" ;
#  endif
#  ifdef MOUNT_DEVFS
   case MOUNT_DEVFS:
     return "devfs" ;
#  endif
#  ifdef MOUNT_EXT2FS
   case MOUNT_EXT2FS:
     return "ext2fs" ;
#  endif
    default:
      return "?";
    }
}
# endif /* ! HAVE_F_FSTYPENAME_IN_STATFS */

/* __NetBSD__ || BSD_NET2 || __OpenBSD__ */
static char *
fsp_to_string (const struct statfs *fsp)
{
# if defined HAVE_F_FSTYPENAME_IN_STATFS
  return (char *) (fsp->f_fstypename);
# else
  return fstype_to_string (fsp->f_type);
# endif
}

#endif /* MOUNTED_GETMNTINFO */

#ifdef MOUNTED_VMOUNT       /* AIX.  */
static char *
fstype_to_string (int t)
{
  struct vfs_ent *e;

  e = getvfsbytype (t);
  if (!e || !e->vfsent_name)
    return "none";
  else
    return e->vfsent_name;
}
#endif /* MOUNTED_VMOUNT */

/* Return a list of the currently mounted filesystems, or NULL on error.
   Add each entry to the tail of the list so that they stay in order.
   If NEED_FS_TYPE is nonzero, ensure that the filesystem type fields in
   the returned list are valid.  Otherwise, they might not be.  */

struct mount_entry *
read_filesystem_list (int need_fs_type)
{
  struct mount_entry *mount_list;
  struct mount_entry *me;
  struct mount_entry **mtail = &mount_list;

#ifdef MOUNTED_LISTMNTENT
  {
    struct tabmntent *mntlist, *p;
    struct mntent *mnt;
    struct mount_entry *me;

    /* the third and fourth arguments could be used to filter mounts,
       but Crays doesn't seem to have any mounts that we want to
       remove. Specifically, automount create normal NFS mounts.
       */

    if(listmntent(&mntlist, KMTAB, NULL, NULL) < 0)
      return NULL;
    for (p = mntlist; p; p = p->next) {
      mnt = p->ment;
      me = (struct mount_entry*) xmalloc(sizeof (struct mount_entry));
      me->me_devname = xstrdup(mnt->mnt_fsname);
      me->me_mountdir = xstrdup(mnt->mnt_dir);
      me->me_type = xstrdup(mnt->mnt_type);
      me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
      me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
      me->me_dev = -1;
      *mtail = me;
      mtail = &me->me_next;
    }
    freemntlist(mntlist);
  }
#endif

#ifdef MOUNTED_GETMNTENT1   /* 4.3BSD, SunOS, HP-UX, Dynix, Irix.  */
  {
    struct mntent *mnt;
    char *table = MOUNTED;
    FILE *fp;
    char *devopt;

    fp = setmntent (table, "r");
    if (fp == NULL)
      return NULL;

    while ((mnt = getmntent (fp)))
      {
    me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
    me->me_devname = xstrdup (mnt->mnt_fsname);
    me->me_mountdir = xstrdup (mnt->mnt_dir);
    me->me_type = xstrdup (mnt->mnt_type);
    me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
    me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
    devopt = strstr (mnt->mnt_opts, "dev=");
    if (devopt)
      {
        if (devopt[4] == '0' && (devopt[5] == 'x' || devopt[5] == 'X'))
          me->me_dev = xatoi (devopt + 6);
        else
          me->me_dev = xatoi (devopt + 4);
      }
    else
      me->me_dev = (dev_t) -1;  /* Magic; means not known yet. */

    /* Add to the linked list. */
    *mtail = me;
    mtail = &me->me_next;
      }

    if (endmntent (fp) == 0)
      goto free_then_fail;
  }
#endif /* MOUNTED_GETMNTENT1. */

#ifdef MOUNTED_GETMNTINFO   /* 4.4BSD.  */
  {
    struct statfs *fsp;
    int entries;

    entries = getmntinfo (&fsp, MNT_NOWAIT);
    if (entries < 0)
      return NULL;
    for (; entries-- > 0; fsp++)
      {
    char *fs_type = fsp_to_string (fsp);

    me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
    me->me_devname = xstrdup (fsp->f_mntfromname);
    me->me_mountdir = xstrdup (fsp->f_mntonname);
    me->me_type = fs_type;
    me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
    me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
    me->me_dev = (dev_t) -1;    /* Magic; means not known yet. */

    /* Add to the linked list. */
    *mtail = me;
    mtail = &me->me_next;
      }
  }
#endif /* MOUNTED_GETMNTINFO */

#ifdef MOUNTED_GETMNT       /* Ultrix.  */
  {
    int offset = 0;
    int val;
    struct fs_data fsd;

    while (errno = 0,
       0 < (val = getmnt (&offset, &fsd, sizeof (fsd), NOSTAT_MANY,
                  (char *) 0)))
      {
    me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
    me->me_devname = xstrdup (fsd.fd_req.devname);
    me->me_mountdir = xstrdup (fsd.fd_req.path);
    me->me_type = gt_names[fsd.fd_req.fstype];
    me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
    me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
    me->me_dev = fsd.fd_req.dev;

    /* Add to the linked list. */
    *mtail = me;
    mtail = &me->me_next;
      }
    if (val < 0)
      goto free_then_fail;
  }
#endif /* MOUNTED_GETMNT. */

#if defined MOUNTED_FS_STAT_DEV /* BeOS */
  {
    /* The next_dev() and fs_stat_dev() system calls give the list of
       all filesystems, including the information returned by statvfs()
       (fs type, total blocks, free blocks etc.), but without the mount
       point. But on BeOS all filesystems except / are mounted in the
       rootfs, directly under /.
       The directory name of the mount point is often, but not always,
       identical to the volume name of the device.
       We therefore get the list of subdirectories of /, and the list
       of all filesystems, and match the two lists.  */

    DIR *dirp;
    struct rootdir_entry
      {
        char *name;
        dev_t dev;
        ino_t ino;
        struct rootdir_entry *next;
      };
    struct rootdir_entry *rootdir_list;
    struct rootdir_entry **rootdir_tail;
    int32 pos;
    dev_t dev;
    fs_info fi;

    /* All volumes are mounted in the rootfs, directly under /. */
    rootdir_list = NULL;
    rootdir_tail = &rootdir_list;
    dirp = opendir ("/");
    if (dirp)
      {
        struct dirent *d;

        while ((d = readdir (dirp)) != NULL)
          {
            char *name;
            struct stat statbuf;

            if (strcmp (d->d_name, "..") == 0)
              continue;

            if (strcmp (d->d_name, ".") == 0)
              name = xstrdup ("/");
            else
              {
                name = xmalloc (1 + strlen (d->d_name) + 1);
                name[0] = '/';
                strcpy (name + 1, d->d_name);
              }

            if (lstat (name, &statbuf) >= 0 && S_ISDIR (statbuf.st_mode))
              {
                struct rootdir_entry *re;

                re = (struct rootdir_entry *) xmalloc (sizeof (struct rootdir_entry));
                re->name = name;
                re->dev = statbuf.st_dev;
                re->ino = statbuf.st_ino;

                /* Add to the linked list.  */
                *rootdir_tail = re;
                rootdir_tail = &re->next;
              }
            else
              free (name);
          }
        closedir (dirp);
      }
    *rootdir_tail = NULL;

    for (pos = 0; (dev = next_dev (&pos)) >= 0; )
      if (fs_stat_dev (dev, &fi) >= 0)
        {
          /* Note: fi.dev == dev. */
          struct rootdir_entry *re;

          for (re = rootdir_list; re; re = re->next)
            if (re->dev == fi.dev && re->ino == fi.root)
              break;

          me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
          me->me_devname = xstrdup (fi.device_name[0] != '\0' ? fi.device_name : fi.fsh_name);
          me->me_mountdir = xstrdup (re != NULL ? re->name : fi.fsh_name);
          me->me_type = xstrdup (fi.fsh_name);
          me->me_dev = fi.dev;
          me->me_dummy = 0;
          me->me_remote = (fi.flags & B_FS_IS_SHARED) != 0;

          /* Add to the linked list. */
          *mtail = me;
          mtail = &me->me_next;
        }
    *mtail = NULL;

    while (rootdir_list != NULL)
      {
        struct rootdir_entry *re = rootdir_list;
        rootdir_list = re->next;
        free (re->name);
        free (re);
      }
  }
#endif /* MOUNTED_FS_STAT_DEV */

#if defined MOUNTED_GETFSSTAT   /* __alpha running OSF_1 */
  {
    int numsys, counter, bufsize;
    struct statfs *stats;

    numsys = getfsstat ((struct statfs *)0, 0L, MNT_WAIT);
    if (numsys < 0)
      return (NULL);

    bufsize = (1 + numsys) * sizeof (struct statfs);
    stats = (struct statfs *)xmalloc (bufsize);
    numsys = getfsstat (stats, bufsize, MNT_WAIT);

    if (numsys < 0)
      {
    free (stats);
    return (NULL);
      }

    for (counter = 0; counter < numsys; counter++)
      {
    me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
    me->me_devname = xstrdup (stats[counter].f_mntfromname);
    me->me_mountdir = xstrdup (stats[counter].f_mntonname);
    me->me_type = xstrdup (FS_TYPE (stats[counter]));
    me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
    me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
    me->me_dev = (dev_t) -1;    /* Magic; means not known yet. */

    /* Add to the linked list. */
    *mtail = me;
    mtail = &me->me_next;
      }

    free (stats);
  }
#endif /* MOUNTED_GETFSSTAT */

#if defined MOUNTED_FREAD || defined MOUNTED_FREAD_FSTYP /* SVR[23].  */
  {
    struct mnttab mnt;
    char *table = "/etc/mnttab";
    FILE *fp;

    fp = fopen (table, "r");
    if (fp == NULL)
      return NULL;

    while (fread (&mnt, sizeof mnt, 1, fp) > 0)
      {
    me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
# ifdef GETFSTYP            /* SVR3.  */
    me->me_devname = xstrdup (mnt.mt_dev);
# else
    me->me_devname = xmalloc (strlen (mnt.mt_dev) + 6);
    strcpy (me->me_devname, "/dev/");
    strcpy (me->me_devname + 5, mnt.mt_dev);
# endif
    me->me_mountdir = xstrdup (mnt.mt_filsys);
    me->me_dev = (dev_t) -1;    /* Magic; means not known yet. */
    me->me_type = "";
# ifdef GETFSTYP            /* SVR3.  */
    if (need_fs_type)
      {
        struct statfs fsd;
        char typebuf[FSTYPSZ];

        if (statfs (me->me_mountdir, &fsd, sizeof fsd, 0) != -1
        && sysfs (GETFSTYP, fsd.f_fstyp, typebuf) != -1)
          me->me_type = xstrdup (typebuf);
      }
# endif
    me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
    me->me_remote = ME_REMOTE (me->me_devname, me->me_type);

    /* Add to the linked list. */
    *mtail = me;
    mtail = &me->me_next;
      }

    if (ferror (fp))
      {
    int saved_errno = errno;
    fclose (fp);
    errno = saved_errno;
    goto free_then_fail;
      }

    if (fclose (fp) == EOF)
      goto free_then_fail;
  }
#endif /* MOUNTED_FREAD || MOUNTED_FREAD_FSTYP.  */

#ifdef MOUNTED_GETMNTTBL    /* DolphinOS goes it's own way */
  {
    struct mntent **mnttbl=getmnttbl(),**ent;
    for (ent=mnttbl;*ent;ent++)
      {
    me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
    me->me_devname = xstrdup ( (*ent)->mt_resource);
    me->me_mountdir = xstrdup( (*ent)->mt_directory);
    me->me_type =  xstrdup ((*ent)->mt_fstype);
    me->me_dummy = ME_DUMMY (me->me_devname, me->me_type);
    me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
    me->me_dev = (dev_t) -1;    /* Magic; means not known yet. */

    /* Add to the linked list. */
    *mtail = me;
    mtail = &me->me_next;
      }
    endmnttbl();
  }
#endif

#ifdef MOUNTED_GETMNTENT2   /* SVR4.  */
  {
    struct mnttab mnt;
    char *table = MNTTAB;
    FILE *fp;
    int ret;
    int lockfd = -1;

# if defined F_RDLCK && defined F_SETLKW
    /* MNTTAB_LOCK is a macro name of our own invention; it's not present in
       e.g. Solaris 2.6.  If the SVR4 folks ever define a macro
       for this file name, we should use their macro name instead.
       (Why not just lock MNTTAB directly?  We don't know.)  */
#  ifndef MNTTAB_LOCK
#   define MNTTAB_LOCK "/etc/.mnttab.lock"
#  endif
    lockfd = open (MNTTAB_LOCK, O_RDONLY);
    if (0 <= lockfd)
      {
    struct flock flock;
    flock.l_type = F_RDLCK;
    flock.l_whence = SEEK_SET;
    flock.l_start = 0;
    flock.l_len = 0;
    while (fcntl (lockfd, F_SETLKW, &flock) == -1)
      if (errno != EINTR)
        {
          int saved_errno = errno;
          close (lockfd);
          errno = saved_errno;
          return NULL;
        }
      }
    else if (errno != ENOENT)
      return NULL;
# endif

    errno = 0;
    fp = fopen (table, "r");
    if (fp == NULL)
      ret = errno;
    else
      {
    while ((ret = getmntent (fp, &mnt)) == 0)
      {
        me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
        me->me_devname = xstrdup (mnt.mnt_special);
        me->me_mountdir = xstrdup (mnt.mnt_mountp);
        me->me_type = xstrdup (mnt.mnt_fstype);
        me->me_dummy = MNT_IGNORE (&mnt) != 0;
        me->me_remote = ME_REMOTE (me->me_devname, me->me_type);
        me->me_dev = (dev_t) -1;    /* Magic; means not known yet. */

        /* Add to the linked list. */
        *mtail = me;
        mtail = &me->me_next;
      }

    ret = fclose (fp) == EOF ? errno : 0 < ret ? 0 : -1;
      }

    if (0 <= lockfd && close (lockfd) != 0)
      ret = errno;

    if (0 <= ret)
      {
    errno = ret;
    goto free_then_fail;
      }
  }
#endif /* MOUNTED_GETMNTENT2.  */

#ifdef MOUNTED_VMOUNT       /* AIX.  */
  {
    int bufsize;
    char *entries, *thisent;
    struct vmount *vmp;
    int n_entries;
    int i;

    /* Ask how many bytes to allocate for the mounted filesystem info.  */
    if (mntctl (MCTL_QUERY, sizeof bufsize, (struct vmount *) &bufsize) != 0)
      return NULL;
    entries = xmalloc (bufsize);

    /* Get the list of mounted filesystems.  */
    n_entries = mntctl (MCTL_QUERY, bufsize, (struct vmount *) entries);
    if (n_entries < 0)
      {
    int saved_errno = errno;
    free (entries);
    errno = saved_errno;
    return NULL;
      }

    for (i = 0, thisent = entries;
     i < n_entries;
     i++, thisent += vmp->vmt_length)
      {
    char *options, *ignore;

    vmp = (struct vmount *) thisent;
    me = (struct mount_entry *) xmalloc (sizeof (struct mount_entry));
    if (vmp->vmt_flags & MNT_REMOTE)
      {
        char *host, *path;

        me->me_remote = 1;
        /* Prepend the remote pathname.  */
        host = thisent + vmp->vmt_data[VMT_HOSTNAME].vmt_off;
        path = thisent + vmp->vmt_data[VMT_OBJECT].vmt_off;
        me->me_devname = xmalloc (strlen (host) + strlen (path) + 2);
        strcpy (me->me_devname, host);
        strcat (me->me_devname, ":");
        strcat (me->me_devname, path);
      }
    else
      {
        me->me_remote = 0;
        me->me_devname = xstrdup (thisent +
                      vmp->vmt_data[VMT_OBJECT].vmt_off);
      }
    me->me_mountdir = xstrdup (thisent + vmp->vmt_data[VMT_STUB].vmt_off);
    me->me_type = xstrdup (fstype_to_string (vmp->vmt_gfstype));
    options = thisent + vmp->vmt_data[VMT_ARGS].vmt_off;
    ignore = strstr (options, "ignore");
    me->me_dummy = (ignore
            && (ignore == options || ignore[-1] == ',')
            && (ignore[sizeof "ignore" - 1] == ','
                || ignore[sizeof "ignore" - 1] == '\0'));
    me->me_dev = (dev_t) -1; /* vmt_fsid might be the info we want.  */

    /* Add to the linked list. */
    *mtail = me;
    mtail = &me->me_next;
      }
    free (entries);
  }
#endif /* MOUNTED_VMOUNT. */

  *mtail = NULL;
  return mount_list;


 free_then_fail:
  {
    int saved_errno = errno;
    *mtail = NULL;

    while (mount_list)
      {
    me = mount_list->me_next;
    free (mount_list->me_devname);
    free (mount_list->me_mountdir);
    /* FIXME: me_type is not always malloced.  */
    free (mount_list);
    mount_list = me;
      }

    errno = saved_errno;
    return NULL;
  }
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/regex.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/regex.c used in: Step 836
/* Extended regular expression matching and search library,
   version 0.12.
   (Implements POSIX draft P1003.2/D11.2, except for some of the
   internationalization features.)
   Copyright (C) 1993-1999, 2000, 2001 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

/* AIX requires this to be the first thing in the file. */
#if defined _AIX && !defined REGEX_MALLOC
  #pragma alloca
#endif

#undef  _GNU_SOURCE
#define _GNU_SOURCE

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifndef PARAMS
# if defined __GNUC__ || (defined __STDC__ && __STDC__)
#  define PARAMS(args) args
# else
#  define PARAMS(args) ()
# endif  /* GCC.  */
#endif  /* Not PARAMS.  */

#ifndef INSIDE_RECURSION

# if defined STDC_HEADERS && !defined emacs
#  include <stddef.h>
# else
/* We need this for `regex.h', and perhaps for the Emacs include files.  */
#  include <sys/types.h>
# endif

# define WIDE_CHAR_SUPPORT (HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_BTOWC)

/* For platform which support the ISO C amendement 1 functionality we
   support user defined character classes.  */
# if defined _LIBC || WIDE_CHAR_SUPPORT
/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>.  */
#  include <wchar.h>
#  include <wctype.h>
# endif

# ifdef _LIBC
/* We have to keep the namespace clean.  */
#  define regfree(preg) __regfree (preg)
#  define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
#  define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
#  define regerror(errcode, preg, errbuf, errbuf_size) \
    __regerror(errcode, preg, errbuf, errbuf_size)
#  define re_set_registers(bu, re, nu, st, en) \
    __re_set_registers (bu, re, nu, st, en)
#  define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
    __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
#  define re_match(bufp, string, size, pos, regs) \
    __re_match (bufp, string, size, pos, regs)
#  define re_search(bufp, string, size, startpos, range, regs) \
    __re_search (bufp, string, size, startpos, range, regs)
#  define re_compile_pattern(pattern, length, bufp) \
    __re_compile_pattern (pattern, length, bufp)
#  define re_set_syntax(syntax) __re_set_syntax (syntax)
#  define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
    __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
#  define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)

#  define btowc __btowc
#  define iswctype __iswctype
#  define mbrtowc __mbrtowc
#  define wcslen __wcslen
#  define wcscoll __wcscoll
#  define wcrtomb __wcrtomb

/* We are also using some library internals.  */
#  include <locale/localeinfo.h>
#  include <locale/elem-hash.h>
#  include <langinfo.h>
#  include <locale/coll-lookup.h>
# endif

/* This is for other GNU distributions with internationalized messages.  */
# if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
#  include <libintl.h>
#  ifdef _LIBC
#   undef gettext
#   define gettext(msgid) __dcgettext ("libc", msgid, LC_MESSAGES)
#  endif
# else
#  define gettext(msgid) (msgid)
# endif

# ifndef gettext_noop
/* This define is so xgettext can find the internationalizable
   strings.  */
#  define gettext_noop(String) String
# endif

/* Support for bounded pointers.  */
# if !defined _LIBC && !defined __BOUNDED_POINTERS__
#  define __bounded /* nothing */
#  define __unbounded   /* nothing */
#  define __ptrvalue    /* nothing */
# endif

/* The `emacs' switch turns on certain matching commands
   that make sense only in Emacs. */
# ifdef emacs

#  include "lisp.h"
#  include "buffer.h"
#  include "syntax.h"

# else  /* not emacs */

/* If we are not linking with Emacs proper,
   we can't use the relocating allocator
   even if config.h says that we can.  */
#  undef REL_ALLOC

#  if defined STDC_HEADERS || defined _LIBC
#   include <stdlib.h>
#  else
char *malloc ();
char *realloc ();
#  endif

/* When used in Emacs's lib-src, we need to get bzero and bcopy somehow.
   If nothing else has been done, use the method below.  */
#  ifdef INHIBIT_STRING_HEADER
#   if !(defined HAVE_BZERO && defined HAVE_BCOPY)
#    if !defined bzero && !defined bcopy
#     undef INHIBIT_STRING_HEADER
#    endif
#   endif
#  endif

/* This is the normal way of making sure we have a bcopy and a bzero.
   This is used in most programs--a few other programs avoid this
   by defining INHIBIT_STRING_HEADER.  */
#  ifndef INHIBIT_STRING_HEADER
#   if defined HAVE_STRING_H || defined STDC_HEADERS || defined _LIBC
#    include <string.h>
#    ifndef bzero
#     ifndef _LIBC
#      define bzero(s, n)   (memset (s, '\0', n), (s))
#     else
#      define bzero(s, n)   __bzero (s, n)
#     endif
#    endif
#   else
#    include <strings.h>
#    ifndef memcmp
#     define memcmp(s1, s2, n)  bcmp (s1, s2, n)
#    endif
#    ifndef memcpy
#     define memcpy(d, s, n)    (bcopy (s, d, n), (d))
#    endif
#   endif
#  endif

/* Define the syntax stuff for \<, \>, etc.  */

/* This must be nonzero for the wordchar and notwordchar pattern
   commands in re_match_2.  */
#  ifndef Sword
#   define Sword 1
#  endif

#  ifdef SWITCH_ENUM_BUG
#   define SWITCH_ENUM_CAST(x) ((int)(x))
#  else
#   define SWITCH_ENUM_CAST(x) (x)
#  endif

# endif /* not emacs */

# if defined _LIBC || HAVE_LIMITS_H
#  include <limits.h>
# endif

# ifndef MB_LEN_MAX
#  define MB_LEN_MAX 1
# endif


/* Get the interface, including the syntax bits.  */
# include <regex.h>

/* isalpha etc. are used for the character classes.  */
# include <ctype.h>

/* Jim Meyering writes:

   "... Some ctype macros are valid only for character codes that
   isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
   using /bin/cc or gcc but without giving an ansi option).  So, all
   ctype uses should be through macros like ISPRINT...  If
   STDC_HEADERS is defined, then autoconf has verified that the ctype
   macros don't need to be guarded with references to isascii. ...
   Defining isascii to 1 should let any compiler worth its salt
   eliminate the && through constant folding."
   Solaris defines some of these symbols so we must undefine them first.  */

# if defined STDC_HEADERS || (!defined isascii && !defined HAVE_ISASCII)
#  define IN_CTYPE_DOMAIN(c) 1
# else
#  define IN_CTYPE_DOMAIN(c) isascii(c)
# endif

# ifdef isblank
#  define ISBLANK(c) (IN_CTYPE_DOMAIN (c) && isblank (c))
# else
#  define ISBLANK(c) ((c) == ' ' || (c) == '\t')
# endif
# ifdef isgraph
#  define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isgraph (c))
# else
#  define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isprint (c) && !isspace (c))
# endif

# undef ISPRINT
# define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c))
# define ISDIGIT(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
# define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum (c))
# define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
# define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl (c))
# define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
# define ISPUNCT(c) (IN_CTYPE_DOMAIN (c) && ispunct (c))
# define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
# define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
# define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit (c))

# ifdef _tolower
#  define TOLOWER(c) _tolower(c)
# else
#  define TOLOWER(c) tolower(c)
# endif

# ifndef NULL
#  define NULL (void *)0
# endif

/* We remove any previous definition of `SIGN_EXTEND_CHAR',
   since ours (we hope) works properly with all combinations of
   machines, compilers, `char' and `unsigned char' argument types.
   (Per Bothner suggested the basic approach.)  */
# undef SIGN_EXTEND_CHAR
# if __STDC__
#  define SIGN_EXTEND_CHAR(c) ((signed char) (c))
# else  /* not __STDC__ */
/* As in Harbison and Steele.  */
#  define SIGN_EXTEND_CHAR(c) ((((unsigned char) (c)) ^ 128) - 128)
# endif


# ifndef emacs
/* How many characters in the character set.  */
#  define CHAR_SET_SIZE 256

#  ifdef SYNTAX_TABLE

extern char *re_syntax_table;

#  else /* not SYNTAX_TABLE */

static char re_syntax_table[CHAR_SET_SIZE];

static void init_syntax_once PARAMS ((void));

static void
init_syntax_once ()
{
   register int c;
   static int done = 0;

   if (done)
     return;
   bzero (re_syntax_table, sizeof re_syntax_table);

   for (c = 0; c < CHAR_SET_SIZE; ++c)
     if (ISALNUM (c))
    re_syntax_table[c] = Sword;

   re_syntax_table['_'] = Sword;

   done = 1;
}

#  endif /* not SYNTAX_TABLE */

#  define SYNTAX(c) re_syntax_table[(unsigned char) (c)]

# endif /* emacs */


/* Should we use malloc or alloca?  If REGEX_MALLOC is not defined, we
   use `alloca' instead of `malloc'.  This is because using malloc in
   re_search* or re_match* could cause memory leaks when C-g is used in
   Emacs; also, malloc is slower and causes storage fragmentation.  On
   the other hand, malloc is more portable, and easier to debug.

   Because we sometimes use alloca, some routines have to be macros,
   not functions -- `alloca'-allocated space disappears at the end of the
   function it is called in.  */

# ifdef REGEX_MALLOC

#  define REGEX_ALLOCATE malloc
#  define REGEX_REALLOCATE(source, osize, nsize) realloc (source, nsize)
#  define REGEX_FREE free

# else /* not REGEX_MALLOC  */

/* Emacs already defines alloca, sometimes.  */
#  ifndef alloca

/* Make alloca work the best possible way.  */
#   ifdef __GNUC__
#    define alloca __builtin_alloca
#   else /* not __GNUC__ */
#    if HAVE_ALLOCA_H
#     include <alloca.h>
#    endif /* HAVE_ALLOCA_H */
#   endif /* not __GNUC__ */

#  endif /* not alloca */

#  define REGEX_ALLOCATE alloca

/* Assumes a `char *destination' variable.  */
#  define REGEX_REALLOCATE(source, osize, nsize)            \
  (destination = (char *) alloca (nsize),               \
   memcpy (destination, source, osize))

/* No need to do anything to free, after alloca.  */
#  define REGEX_FREE(arg) ((void)0) /* Do nothing!  But inhibit gcc warning.  */

# endif /* not REGEX_MALLOC */

/* Define how to allocate the failure stack.  */

# if defined REL_ALLOC && defined REGEX_MALLOC

#  define REGEX_ALLOCATE_STACK(size)                \
  r_alloc (&failure_stack_ptr, (size))
#  define REGEX_REALLOCATE_STACK(source, osize, nsize)      \
  r_re_alloc (&failure_stack_ptr, (nsize))
#  define REGEX_FREE_STACK(ptr)                 \
  r_alloc_free (&failure_stack_ptr)

# else /* not using relocating allocator */

#  ifdef REGEX_MALLOC

#   define REGEX_ALLOCATE_STACK malloc
#   define REGEX_REALLOCATE_STACK(source, osize, nsize) realloc (source, nsize)
#   define REGEX_FREE_STACK free

#  else /* not REGEX_MALLOC */

#   define REGEX_ALLOCATE_STACK alloca

#   define REGEX_REALLOCATE_STACK(source, osize, nsize)         \
   REGEX_REALLOCATE (source, osize, nsize)
/* No need to explicitly free anything.  */
#   define REGEX_FREE_STACK(arg)

#  endif /* not REGEX_MALLOC */
# endif /* not using relocating allocator */


/* True if `size1' is non-NULL and PTR is pointing anywhere inside
   `string1' or just past its end.  This works if PTR is NULL, which is
   a good thing.  */
# define FIRST_STRING_P(ptr)                    \
  (size1 && string1 <= (ptr) && (ptr) <= string1 + size1)

/* (Re)Allocate N items of type T using malloc, or fail.  */
# define TALLOC(n, t) ((t *) malloc ((n) * sizeof (t)))
# define RETALLOC(addr, n, t) ((addr) = (t *) realloc (addr, (n) * sizeof (t)))
# define RETALLOC_IF(addr, n, t) \
  if (addr) RETALLOC((addr), (n), t); else (addr) = TALLOC ((n), t)
# define REGEX_TALLOC(n, t) ((t *) REGEX_ALLOCATE ((n) * sizeof (t)))

# define BYTEWIDTH 8 /* In bits.  */

# define STREQ(s1, s2) ((strcmp (s1, s2) == 0))

# undef MAX
# undef MIN
# define MAX(a, b) ((a) > (b) ? (a) : (b))
# define MIN(a, b) ((a) < (b) ? (a) : (b))

typedef char boolean;
# define false 0
# define true 1

static reg_errcode_t byte_regex_compile _RE_ARGS ((const char *pattern, size_t size,
                                                   reg_syntax_t syntax,
                                                   struct re_pattern_buffer *bufp));

static int byte_re_match_2_internal PARAMS ((struct re_pattern_buffer *bufp,
                         const char *string1, int size1,
                         const char *string2, int size2,
                         int pos,
                         struct re_registers *regs,
                         int stop));
static int byte_re_search_2 PARAMS ((struct re_pattern_buffer *bufp,
                     const char *string1, int size1,
                     const char *string2, int size2,
                     int startpos, int range,
                     struct re_registers *regs, int stop));
static int byte_re_compile_fastmap PARAMS ((struct re_pattern_buffer *bufp));

#ifdef MBS_SUPPORT
static reg_errcode_t wcs_regex_compile _RE_ARGS ((const char *pattern, size_t size,
                                                   reg_syntax_t syntax,
                                                   struct re_pattern_buffer *bufp));


static int wcs_re_match_2_internal PARAMS ((struct re_pattern_buffer *bufp,
                        const char *cstring1, int csize1,
                        const char *cstring2, int csize2,
                        int pos,
                        struct re_registers *regs,
                        int stop,
                        wchar_t *string1, int size1,
                        wchar_t *string2, int size2,
                        int *mbs_offset1, int *mbs_offset2));
static int wcs_re_search_2 PARAMS ((struct re_pattern_buffer *bufp,
                    const char *string1, int size1,
                    const char *string2, int size2,
                    int startpos, int range,
                    struct re_registers *regs, int stop));
static int wcs_re_compile_fastmap PARAMS ((struct re_pattern_buffer *bufp));
#endif


/* These are the command codes that appear in compiled regular
   expressions.  Some opcodes are followed by argument bytes.  A
   command code can specify any interpretation whatsoever for its
   arguments.  Zero bytes may appear in the compiled regular expression.  */

typedef enum
{
  no_op = 0,

  /* Succeed right away--no more backtracking.  */
  succeed,

        /* Followed by one byte giving n, then by n literal bytes.  */
  exactn,

# ifdef MBS_SUPPORT
    /* Same as exactn, but contains binary data.  */
  exactn_bin,
# endif

        /* Matches any (more or less) character.  */
  anychar,

        /* Matches any one char belonging to specified set.  First
           following byte is number of bitmap bytes.  Then come bytes
           for a bitmap saying which chars are in.  Bits in each byte
           are ordered low-bit-first.  A character is in the set if its
           bit is 1.  A character too large to have a bit in the map is
           automatically not in the set.  */
        /* ifdef MBS_SUPPORT, following element is length of character
       classes, length of collating symbols, length of equivalence
       classes, length of character ranges, and length of characters.
       Next, character class element, collating symbols elements,
       equivalence class elements, range elements, and character
       elements follow.
       See regex_compile function.  */
  charset,

        /* Same parameters as charset, but match any character that is
           not one of those specified.  */
  charset_not,

        /* Start remembering the text that is matched, for storing in a
           register.  Followed by one byte with the register number, in
           the range 0 to one less than the pattern buffer's re_nsub
           field.  Then followed by one byte with the number of groups
           inner to this one.  (This last has to be part of the
           start_memory only because we need it in the on_failure_jump
           of re_match_2.)  */
  start_memory,

        /* Stop remembering the text that is matched and store it in a
           memory register.  Followed by one byte with the register
           number, in the range 0 to one less than `re_nsub' in the
           pattern buffer, and one byte with the number of inner groups,
           just like `start_memory'.  (We need the number of inner
           groups here because we don't have any easy way of finding the
           corresponding start_memory when we're at a stop_memory.)  */
  stop_memory,

        /* Match a duplicate of something remembered. Followed by one
           byte containing the register number.  */
  duplicate,

        /* Fail unless at beginning of line.  */
  begline,

        /* Fail unless at end of line.  */
  endline,

        /* Succeeds if at beginning of buffer (if emacs) or at beginning
           of string to be matched (if not).  */
  begbuf,

        /* Analogously, for end of buffer/string.  */
  endbuf,

        /* Followed by two byte relative address to which to jump.  */
  jump,

    /* Same as jump, but marks the end of an alternative.  */
  jump_past_alt,

        /* Followed by two-byte relative address of place to resume at
           in case of failure.  */
        /* ifdef MBS_SUPPORT, the size of address is 1.  */
  on_failure_jump,

        /* Like on_failure_jump, but pushes a placeholder instead of the
           current string position when executed.  */
  on_failure_keep_string_jump,

        /* Throw away latest failure point and then jump to following
           two-byte relative address.  */
        /* ifdef MBS_SUPPORT, the size of address is 1.  */
  pop_failure_jump,

        /* Change to pop_failure_jump if know won't have to backtrack to
           match; otherwise change to jump.  This is used to jump
           back to the beginning of a repeat.  If what follows this jump
           clearly won't match what the repeat does, such that we can be
           sure that there is no use backtracking out of repetitions
           already matched, then we change it to a pop_failure_jump.
           Followed by two-byte address.  */
        /* ifdef MBS_SUPPORT, the size of address is 1.  */
  maybe_pop_jump,

        /* Jump to following two-byte address, and push a dummy failure
           point. This failure point will be thrown away if an attempt
           is made to use it for a failure.  A `+' construct makes this
           before the first repeat.  Also used as an intermediary kind
           of jump when compiling an alternative.  */
        /* ifdef MBS_SUPPORT, the size of address is 1.  */
  dummy_failure_jump,

    /* Push a dummy failure point and continue.  Used at the end of
       alternatives.  */
  push_dummy_failure,

        /* Followed by two-byte relative address and two-byte number n.
           After matching N times, jump to the address upon failure.  */
        /* ifdef MBS_SUPPORT, the size of address is 1.  */
  succeed_n,

        /* Followed by two-byte relative address, and two-byte number n.
           Jump to the address N times, then fail.  */
        /* ifdef MBS_SUPPORT, the size of address is 1.  */
  jump_n,

        /* Set the following two-byte relative address to the
           subsequent two-byte number.  The address *includes* the two
           bytes of number.  */
        /* ifdef MBS_SUPPORT, the size of address is 1.  */
  set_number_at,

  wordchar, /* Matches any word-constituent character.  */
  notwordchar,  /* Matches any char that is not a word-constituent.  */

  wordbeg,  /* Succeeds if at word beginning.  */
  wordend,  /* Succeeds if at word end.  */

  wordbound,    /* Succeeds if at a word boundary.  */
  notwordbound  /* Succeeds if not at a word boundary.  */

# ifdef emacs
  ,before_dot,  /* Succeeds if before point.  */
  at_dot,   /* Succeeds if at point.  */
  after_dot,    /* Succeeds if after point.  */

    /* Matches any character whose syntax is specified.  Followed by
           a byte which contains a syntax code, e.g., Sword.  */
  syntaxspec,

    /* Matches any character whose syntax is not that specified.  */
  notsyntaxspec
# endif /* emacs */
} re_opcode_t;
#endif /* not INSIDE_RECURSION */



#ifdef BYTE
# define CHAR_T char
# define UCHAR_T unsigned char
# define COMPILED_BUFFER_VAR bufp->buffer
# define OFFSET_ADDRESS_SIZE 2
# define PREFIX(name) byte_##name
# define ARG_PREFIX(name) name
# define PUT_CHAR(c) putchar (c)
#else
# ifdef WCHAR
#  define CHAR_T wchar_t
#  define UCHAR_T wchar_t
#  define COMPILED_BUFFER_VAR wc_buffer
#  define OFFSET_ADDRESS_SIZE 1 /* the size which STORE_NUMBER macro use */
#  define CHAR_CLASS_SIZE ((__alignof__(wctype_t)+sizeof(wctype_t))/sizeof(CHAR_T)+1)
#  define PREFIX(name) wcs_##name
#  define ARG_PREFIX(name) c##name
/* Should we use wide stream??  */
#  define PUT_CHAR(c) printf ("%C", c);
#  define TRUE 1
#  define FALSE 0
# else
#  ifdef MBS_SUPPORT
#   define WCHAR
#   define INSIDE_RECURSION
#   include "regex.c"
#   undef INSIDE_RECURSION
#  endif
#  define BYTE
#  define INSIDE_RECURSION
#  include "regex.c"
#  undef INSIDE_RECURSION
# endif
#endif
#include "unlocked-io.h"

#ifdef INSIDE_RECURSION
/* Common operations on the compiled pattern.  */

/* Store NUMBER in two contiguous bytes starting at DESTINATION.  */
/* ifdef MBS_SUPPORT, we store NUMBER in 1 element.  */

# ifdef WCHAR
#  define STORE_NUMBER(destination, number)             \
  do {                                  \
    *(destination) = (UCHAR_T)(number);             \
  } while (0)
# else /* BYTE */
#  define STORE_NUMBER(destination, number)             \
  do {                                  \
    (destination)[0] = (number) & 0377;                 \
    (destination)[1] = (number) >> 8;                   \
  } while (0)
# endif /* WCHAR */

/* Same as STORE_NUMBER, except increment DESTINATION to
   the byte after where the number is stored.  Therefore, DESTINATION
   must be an lvalue.  */
/* ifdef MBS_SUPPORT, we store NUMBER in 1 element.  */

# define STORE_NUMBER_AND_INCR(destination, number)         \
  do {                                  \
    STORE_NUMBER (destination, number);                 \
    (destination) += OFFSET_ADDRESS_SIZE;               \
  } while (0)

/* Put into DESTINATION a number stored in two contiguous bytes starting
   at SOURCE.  */
/* ifdef MBS_SUPPORT, we store NUMBER in 1 element.  */

# ifdef WCHAR
#  define EXTRACT_NUMBER(destination, source)               \
  do {                                  \
    (destination) = *(source);                      \
  } while (0)
# else /* BYTE */
#  define EXTRACT_NUMBER(destination, source)               \
  do {                                  \
    (destination) = *(source) & 0377;                   \
    (destination) += SIGN_EXTEND_CHAR (*((source) + 1)) << 8;       \
  } while (0)
# endif

# ifdef DEBUG
static void PREFIX(extract_number) _RE_ARGS ((int *dest, UCHAR_T *source));
static void
PREFIX(extract_number) (dest, source)
    int *dest;
    UCHAR_T *source;
{
#  ifdef WCHAR
  *dest = *source;
#  else /* BYTE */
  int temp = SIGN_EXTEND_CHAR (*(source + 1));
  *dest = *source & 0377;
  *dest += temp << 8;
#  endif
}

#  ifndef EXTRACT_MACROS /* To debug the macros.  */
#   undef EXTRACT_NUMBER
#   define EXTRACT_NUMBER(dest, src) PREFIX(extract_number) (&dest, src)
#  endif /* not EXTRACT_MACROS */

# endif /* DEBUG */

/* Same as EXTRACT_NUMBER, except increment SOURCE to after the number.
   SOURCE must be an lvalue.  */

# define EXTRACT_NUMBER_AND_INCR(destination, source)           \
  do {                                  \
    EXTRACT_NUMBER (destination, source);               \
    (source) += OFFSET_ADDRESS_SIZE;                    \
  } while (0)

# ifdef DEBUG
static void PREFIX(extract_number_and_incr) _RE_ARGS ((int *destination,
                               UCHAR_T **source));
static void
PREFIX(extract_number_and_incr) (destination, source)
    int *destination;
    UCHAR_T **source;
{
  PREFIX(extract_number) (destination, *source);
  *source += OFFSET_ADDRESS_SIZE;
}

#  ifndef EXTRACT_MACROS
#   undef EXTRACT_NUMBER_AND_INCR
#   define EXTRACT_NUMBER_AND_INCR(dest, src) \
  PREFIX(extract_number_and_incr) (&dest, &src)
#  endif /* not EXTRACT_MACROS */

# endif /* DEBUG */




/* If DEBUG is defined, Regex prints many voluminous messages about what
   it is doing (if the variable `debug' is nonzero).  If linked with the
   main program in `iregex.c', you can enter patterns and strings
   interactively.  And if linked with the main program in `main.c' and
   the other test files, you can run the already-written tests.  */

# ifdef DEBUG

#  ifndef DEFINED_ONCE

/* We use standard I/O for debugging.  */
#   include <stdio.h>

/* It is useful to test things that ``must'' be true when debugging.  */
#   include <assert.h>

static int debug;

#   define DEBUG_STATEMENT(e) e
#   define DEBUG_PRINT1(x) if (debug) printf (x)
#   define DEBUG_PRINT2(x1, x2) if (debug) printf (x1, x2)
#   define DEBUG_PRINT3(x1, x2, x3) if (debug) printf (x1, x2, x3)
#   define DEBUG_PRINT4(x1, x2, x3, x4) if (debug) printf (x1, x2, x3, x4)
#  endif /* not DEFINED_ONCE */

#  define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)             \
  if (debug) PREFIX(print_partial_compiled_pattern) (s, e)
#  define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)        \
  if (debug) PREFIX(print_double_string) (w, s1, sz1, s2, sz2)


/* Print the fastmap in human-readable form.  */

#  ifndef DEFINED_ONCE
void
print_fastmap (fastmap)
    char *fastmap;
{
  unsigned was_a_range = 0;
  unsigned i = 0;

  while (i < (1 << BYTEWIDTH))
    {
      if (fastmap[i++])
    {
      was_a_range = 0;
          putchar (i - 1);
          while (i < (1 << BYTEWIDTH)  &&  fastmap[i])
            {
              was_a_range = 1;
              i++;
            }
      if (was_a_range)
            {
              printf ("-");
              putchar (i - 1);
            }
        }
    }
  putchar ('\n');
}
#  endif /* not DEFINED_ONCE */


/* Print a compiled pattern string in human-readable form, starting at
   the START pointer into it and ending just before the pointer END.  */

void
PREFIX(print_partial_compiled_pattern) (start, end)
    UCHAR_T *start;
    UCHAR_T *end;
{
  int mcnt, mcnt2;
  UCHAR_T *p1;
  UCHAR_T *p = start;
  UCHAR_T *pend = end;

  if (start == NULL)
    {
      printf ("(null)\n");
      return;
    }

  /* Loop over pattern commands.  */
  while (p < pend)
    {
#  ifdef _LIBC
      printf ("%td:\t", p - start);
#  else
      printf ("%ld:\t", (long int) (p - start));
#  endif

      switch ((re_opcode_t) *p++)
    {
        case no_op:
          printf ("/no_op");
          break;

    case exactn:
      mcnt = *p++;
          printf ("/exactn/%d", mcnt);
          do
        {
              putchar ('/');
          PUT_CHAR (*p++);
            }
          while (--mcnt);
          break;

#  ifdef MBS_SUPPORT
    case exactn_bin:
      mcnt = *p++;
      printf ("/exactn_bin/%d", mcnt);
          do
        {
          printf("/%lx", (long int) *p++);
            }
          while (--mcnt);
          break;
#  endif /* MBS_SUPPORT */

    case start_memory:
          mcnt = *p++;
          printf ("/start_memory/%d/%ld", mcnt, (long int) *p++);
          break;

    case stop_memory:
          mcnt = *p++;
      printf ("/stop_memory/%d/%ld", mcnt, (long int) *p++);
          break;

    case duplicate:
      printf ("/duplicate/%ld", (long int) *p++);
      break;

    case anychar:
      printf ("/anychar");
      break;

    case charset:
        case charset_not:
          {
#  ifdef WCHAR
        int i, length;
        wchar_t *workp = p;
        printf ("/charset [%s",
                (re_opcode_t) *(workp - 1) == charset_not ? "^" : "");
        p += 5;
        length = *workp++; /* the length of char_classes */
        for (i=0 ; i<length ; i++)
          printf("[:%lx:]", (long int) *p++);
        length = *workp++; /* the length of collating_symbol */
        for (i=0 ; i<length ;)
          {
        printf("[.");
        while(*p != 0)
          PUT_CHAR((i++,*p++));
        i++,p++;
        printf(".]");
          }
        length = *workp++; /* the length of equivalence_class */
        for (i=0 ; i<length ;)
          {
        printf("[=");
        while(*p != 0)
          PUT_CHAR((i++,*p++));
        i++,p++;
        printf("=]");
          }
        length = *workp++; /* the length of char_range */
        for (i=0 ; i<length ; i++)
          {
        wchar_t range_start = *p++;
        wchar_t range_end = *p++;
        printf("%C-%C", range_start, range_end);
          }
        length = *workp++; /* the length of char */
        for (i=0 ; i<length ; i++)
          printf("%C", *p++);
        putchar (']');
#  else
            register int c, last = -100;
        register int in_range = 0;

        printf ("/charset [%s",
                (re_opcode_t) *(p - 1) == charset_not ? "^" : "");

            assert (p + *p < pend);

            for (c = 0; c < 256; c++)
          if (c / 8 < *p
          && (p[1 + (c/8)] & (1 << (c % 8))))
        {
          /* Are we starting a range?  */
          if (last + 1 == c && ! in_range)
            {
              putchar ('-');
              in_range = 1;
            }
          /* Have we broken a range?  */
          else if (last + 1 != c && in_range)
              {
              putchar (last);
              in_range = 0;
            }

          if (! in_range)
            putchar (c);

          last = c;
              }

        if (in_range)
          putchar (last);

        putchar (']');

        p += 1 + *p;
#  endif /* WCHAR */
      }
      break;

    case begline:
      printf ("/begline");
          break;

    case endline:
          printf ("/endline");
          break;

    case on_failure_jump:
          PREFIX(extract_number_and_incr) (&mcnt, &p);
#  ifdef _LIBC
      printf ("/on_failure_jump to %td", p + mcnt - start);
#  else
      printf ("/on_failure_jump to %ld", (long int) (p + mcnt - start));
#  endif
          break;

    case on_failure_keep_string_jump:
          PREFIX(extract_number_and_incr) (&mcnt, &p);
#  ifdef _LIBC
      printf ("/on_failure_keep_string_jump to %td", p + mcnt - start);
#  else
      printf ("/on_failure_keep_string_jump to %ld",
          (long int) (p + mcnt - start));
#  endif
          break;

    case dummy_failure_jump:
          PREFIX(extract_number_and_incr) (&mcnt, &p);
#  ifdef _LIBC
      printf ("/dummy_failure_jump to %td", p + mcnt - start);
#  else
      printf ("/dummy_failure_jump to %ld", (long int) (p + mcnt - start));
#  endif
          break;

    case push_dummy_failure:
          printf ("/push_dummy_failure");
          break;

        case maybe_pop_jump:
          PREFIX(extract_number_and_incr) (&mcnt, &p);
#  ifdef _LIBC
      printf ("/maybe_pop_jump to %td", p + mcnt - start);
#  else
      printf ("/maybe_pop_jump to %ld", (long int) (p + mcnt - start));
#  endif
      break;

        case pop_failure_jump:
      PREFIX(extract_number_and_incr) (&mcnt, &p);
#  ifdef _LIBC
      printf ("/pop_failure_jump to %td", p + mcnt - start);
#  else
      printf ("/pop_failure_jump to %ld", (long int) (p + mcnt - start));
#  endif
      break;

        case jump_past_alt:
      PREFIX(extract_number_and_incr) (&mcnt, &p);
#  ifdef _LIBC
      printf ("/jump_past_alt to %td", p + mcnt - start);
#  else
      printf ("/jump_past_alt to %ld", (long int) (p + mcnt - start));
#  endif
      break;

        case jump:
      PREFIX(extract_number_and_incr) (&mcnt, &p);
#  ifdef _LIBC
      printf ("/jump to %td", p + mcnt - start);
#  else
      printf ("/jump to %ld", (long int) (p + mcnt - start));
#  endif
      break;

        case succeed_n:
          PREFIX(extract_number_and_incr) (&mcnt, &p);
      p1 = p + mcnt;
          PREFIX(extract_number_and_incr) (&mcnt2, &p);
#  ifdef _LIBC
      printf ("/succeed_n to %td, %d times", p1 - start, mcnt2);
#  else
      printf ("/succeed_n to %ld, %d times",
          (long int) (p1 - start), mcnt2);
#  endif
          break;

        case jump_n:
          PREFIX(extract_number_and_incr) (&mcnt, &p);
      p1 = p + mcnt;
          PREFIX(extract_number_and_incr) (&mcnt2, &p);
      printf ("/jump_n to %d, %d times", p1 - start, mcnt2);
          break;

        case set_number_at:
          PREFIX(extract_number_and_incr) (&mcnt, &p);
      p1 = p + mcnt;
          PREFIX(extract_number_and_incr) (&mcnt2, &p);
#  ifdef _LIBC
      printf ("/set_number_at location %td to %d", p1 - start, mcnt2);
#  else
      printf ("/set_number_at location %ld to %d",
          (long int) (p1 - start), mcnt2);
#  endif
          break;

        case wordbound:
      printf ("/wordbound");
      break;

    case notwordbound:
      printf ("/notwordbound");
          break;

    case wordbeg:
      printf ("/wordbeg");
      break;

    case wordend:
      printf ("/wordend");
      break;

#  ifdef emacs
    case before_dot:
      printf ("/before_dot");
          break;

    case at_dot:
      printf ("/at_dot");
          break;

    case after_dot:
      printf ("/after_dot");
          break;

    case syntaxspec:
          printf ("/syntaxspec");
      mcnt = *p++;
      printf ("/%d", mcnt);
          break;

    case notsyntaxspec:
          printf ("/notsyntaxspec");
      mcnt = *p++;
      printf ("/%d", mcnt);
      break;
#  endif /* emacs */

    case wordchar:
      printf ("/wordchar");
          break;

    case notwordchar:
      printf ("/notwordchar");
          break;

    case begbuf:
      printf ("/begbuf");
          break;

    case endbuf:
      printf ("/endbuf");
          break;

        default:
          printf ("?%ld", (long int) *(p-1));
    }

      putchar ('\n');
    }

#  ifdef _LIBC
  printf ("%td:\tend of pattern.\n", p - start);
#  else
  printf ("%ld:\tend of pattern.\n", (long int) (p - start));
#  endif
}


void
PREFIX(print_compiled_pattern) (bufp)
    struct re_pattern_buffer *bufp;
{
  UCHAR_T *buffer = (UCHAR_T*) bufp->buffer;

  PREFIX(print_partial_compiled_pattern) (buffer, buffer
                  + bufp->used / sizeof(UCHAR_T));
  printf ("%ld bytes used/%ld bytes allocated.\n",
      bufp->used, bufp->allocated);

  if (bufp->fastmap_accurate && bufp->fastmap)
    {
      printf ("fastmap: ");
      print_fastmap (bufp->fastmap);
    }

#  ifdef _LIBC
  printf ("re_nsub: %Zd\t", bufp->re_nsub);
#  else
  printf ("re_nsub: %ld\t", (long int) bufp->re_nsub);
#  endif
  printf ("regs_alloc: %d\t", bufp->regs_allocated);
  printf ("can_be_null: %d\t", bufp->can_be_null);
  printf ("newline_anchor: %d\n", bufp->newline_anchor);
  printf ("no_sub: %d\t", bufp->no_sub);
  printf ("not_bol: %d\t", bufp->not_bol);
  printf ("not_eol: %d\t", bufp->not_eol);
  printf ("syntax: %lx\n", bufp->syntax);
  /* Perhaps we should print the translate table?  */
}


void
PREFIX(print_double_string) (where, string1, size1, string2, size2)
    const CHAR_T *where;
    const CHAR_T *string1;
    const CHAR_T *string2;
    int size1;
    int size2;
{
  int this_char;

  if (where == NULL)
    printf ("(null)");
  else
    {
      int cnt;

      if (FIRST_STRING_P (where))
        {
          for (this_char = where - string1; this_char < size1; this_char++)
        PUT_CHAR (string1[this_char]);

          where = string2;
        }

      cnt = 0;
      for (this_char = where - string2; this_char < size2; this_char++)
    {
      PUT_CHAR (string2[this_char]);
      if (++cnt > 100)
        {
          fputs ("...", stdout);
          break;
        }
    }
    }
}

#  ifndef DEFINED_ONCE
void
printchar (c)
     int c;
{
  putc (c, stderr);
}
#  endif

# else /* not DEBUG */

#  ifndef DEFINED_ONCE
#   undef assert
#   define assert(e)

#   define DEBUG_STATEMENT(e)
#   define DEBUG_PRINT1(x)
#   define DEBUG_PRINT2(x1, x2)
#   define DEBUG_PRINT3(x1, x2, x3)
#   define DEBUG_PRINT4(x1, x2, x3, x4)
#  endif /* not DEFINED_ONCE */
#  define DEBUG_PRINT_COMPILED_PATTERN(p, s, e)
#  define DEBUG_PRINT_DOUBLE_STRING(w, s1, sz1, s2, sz2)

# endif /* not DEBUG */




# ifdef WCHAR
/* This  convert a multibyte string to a wide character string.
   And write their correspondances to offset_buffer(see below)
   and write whether each wchar_t is binary data to is_binary.
   This assume invalid multibyte sequences as binary data.
   We assume offset_buffer and is_binary is already allocated
   enough space.  */

static size_t convert_mbs_to_wcs (CHAR_T *dest, const unsigned char* src,
                  size_t len, int *offset_buffer,
                  char *is_binary);
static size_t
convert_mbs_to_wcs (dest, src, len, offset_buffer, is_binary)
     CHAR_T *dest;
     const unsigned char* src;
     size_t len; /* the length of multibyte string.  */

     /* It hold correspondances between src(char string) and
    dest(wchar_t string) for optimization.
    e.g. src  = "xxxyzz"
             dest = {'X', 'Y', 'Z'}
          (each "xxx", "y" and "zz" represent one multibyte character
           corresponding to 'X', 'Y' and 'Z'.)
      offset_buffer = {0, 0+3("xxx"), 0+3+1("y"), 0+3+1+2("zz")}
                = {0, 3, 4, 6}
     */
     int *offset_buffer;
     char *is_binary;
{
  wchar_t *pdest = dest;
  const unsigned char *psrc = src;
  size_t wc_count = 0;

  mbstate_t mbs;
  int i, consumed;
  size_t mb_remain = len;
  size_t mb_count = 0;

  /* Initialize the conversion state.  */
  memset (&mbs, 0, sizeof (mbstate_t));

  offset_buffer[0] = 0;
  for( ; mb_remain > 0 ; ++wc_count, ++pdest, mb_remain -= consumed,
     psrc += consumed)
    {
      consumed = mbrtowc (pdest, psrc, mb_remain, &mbs);

      if (consumed <= 0)
    /* failed to convert. maybe src contains binary data.
       So we consume 1 byte manualy.  */
    {
      *pdest = *psrc;
      consumed = 1;
      is_binary[wc_count] = TRUE;
    }
      else
    is_binary[wc_count] = FALSE;
      /* In sjis encoding, we use yen sign as escape character in
     place of reverse solidus. So we convert 0x5c(yen sign in
     sjis) to not 0xa5(yen sign in UCS2) but 0x5c(reverse
     solidus in UCS2).  */
      if (consumed == 1 && (int) *psrc == 0x5c && (int) *pdest == 0xa5)
    *pdest = (wchar_t) *psrc;

      offset_buffer[wc_count + 1] = mb_count += consumed;
    }

  /* Fill remain of the buffer with sentinel.  */
  for (i = wc_count + 1 ; i <= len ; i++)
    offset_buffer[i] = mb_count + 1;

  return wc_count;
}

# endif /* WCHAR */

#else /* not INSIDE_RECURSION */

/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
   also be assigned to arbitrarily: each pattern buffer stores its own
   syntax, so it can be changed between regex compilations.  */
/* This has no initializer because initialized variables in Emacs
   become read-only after dumping.  */
reg_syntax_t re_syntax_options;


/* Specify the precise syntax of regexps for compilation.  This provides
   for compatibility for various utilities which historically have
   different, incompatible syntaxes.

   The argument SYNTAX is a bit mask comprised of the various bits
   defined in regex.h.  We return the old syntax.  */

reg_syntax_t
re_set_syntax (syntax)
    reg_syntax_t syntax;
{
  reg_syntax_t ret = re_syntax_options;

  re_syntax_options = syntax;
# ifdef DEBUG
  if (syntax & RE_DEBUG)
    debug = 1;
  else if (debug) /* was on but now is not */
    debug = 0;
# endif /* DEBUG */
  return ret;
}
# ifdef _LIBC
weak_alias (__re_set_syntax, re_set_syntax)
# endif


/* This table gives an error message for each of the error codes listed
   in regex.h.  Obviously the order here has to be same as there.
   POSIX doesn't require that we do anything for REG_NOERROR,
   but why not be nice?  */

static const char re_error_msgid[] =
  {
# define REG_NOERROR_IDX    0
    gettext_noop ("Success")    /* REG_NOERROR */
    "\0"
# define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
    gettext_noop ("No match")   /* REG_NOMATCH */
    "\0"
# define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match")
    gettext_noop ("Invalid regular expression") /* REG_BADPAT */
    "\0"
# define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
    gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
    "\0"
# define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character")
    gettext_noop ("Invalid character class name") /* REG_ECTYPE */
    "\0"
# define REG_EESCAPE_IDX    (REG_ECTYPE_IDX + sizeof "Invalid character class name")
    gettext_noop ("Trailing backslash") /* REG_EESCAPE */
    "\0"
# define REG_ESUBREG_IDX    (REG_EESCAPE_IDX + sizeof "Trailing backslash")
    gettext_noop ("Invalid back reference") /* REG_ESUBREG */
    "\0"
# define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference")
    gettext_noop ("Unmatched [ or [^")  /* REG_EBRACK */
    "\0"
# define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
    gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
    "\0"
# define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
    gettext_noop ("Unmatched \\{") /* REG_EBRACE */
    "\0"
# define REG_BADBR_IDX  (REG_EBRACE_IDX + sizeof "Unmatched \\{")
    gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
    "\0"
# define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
    gettext_noop ("Invalid range end")  /* REG_ERANGE */
    "\0"
# define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end")
    gettext_noop ("Memory exhausted") /* REG_ESPACE */
    "\0"
# define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted")
    gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
    "\0"
# define REG_EEND_IDX   (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
    gettext_noop ("Premature end of regular expression") /* REG_EEND */
    "\0"
# define REG_ESIZE_IDX  (REG_EEND_IDX + sizeof "Premature end of regular expression")
    gettext_noop ("Regular expression too big") /* REG_ESIZE */
    "\0"
# define REG_ERPAREN_IDX    (REG_ESIZE_IDX + sizeof "Regular expression too big")
    gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
  };

static const size_t re_error_msgid_idx[] =
  {
    REG_NOERROR_IDX,
    REG_NOMATCH_IDX,
    REG_BADPAT_IDX,
    REG_ECOLLATE_IDX,
    REG_ECTYPE_IDX,
    REG_EESCAPE_IDX,
    REG_ESUBREG_IDX,
    REG_EBRACK_IDX,
    REG_EPAREN_IDX,
    REG_EBRACE_IDX,
    REG_BADBR_IDX,
    REG_ERANGE_IDX,
    REG_ESPACE_IDX,
    REG_BADRPT_IDX,
    REG_EEND_IDX,
    REG_ESIZE_IDX,
    REG_ERPAREN_IDX
  };


#endif /* INSIDE_RECURSION */

#ifndef DEFINED_ONCE
/* Avoiding alloca during matching, to placate r_alloc.  */

/* Define MATCH_MAY_ALLOCATE unless we need to make sure that the
   searching and matching functions should not call alloca.  On some
   systems, alloca is implemented in terms of malloc, and if we're
   using the relocating allocator routines, then malloc could cause a
   relocation, which might (if the strings being searched are in the
   ralloc heap) shift the data out from underneath the regexp
   routines.

   Here's another reason to avoid allocation: Emacs
   processes input from X in a signal handler; processing X input may
   call malloc; if input arrives while a matching routine is calling
   malloc, then we're scrod.  But Emacs can't just block input while
   calling matching routines; then we don't notice interrupts when
   they come in.  So, Emacs blocks input around all regexp calls
   except the matching calls, which it leaves unprotected, in the
   faith that they will not malloc.  */

/* Normally, this is fine.  */
# define MATCH_MAY_ALLOCATE

/* When using GNU C, we are not REALLY using the C alloca, no matter
   what config.h may say.  So don't take precautions for it.  */
# ifdef __GNUC__
#  undef C_ALLOCA
# endif

/* The match routines may not allocate if (1) they would do it with malloc
   and (2) it's not safe for them to use malloc.
   Note that if REL_ALLOC is defined, matching would not use malloc for the
   failure stack, but we would still use it for the register vectors;
   so REL_ALLOC should not affect this.  */
# if (defined C_ALLOCA || defined REGEX_MALLOC) && defined emacs
#  undef MATCH_MAY_ALLOCATE
# endif
#endif /* not DEFINED_ONCE */


#ifdef INSIDE_RECURSION
/* Failure stack declarations and macros; both re_compile_fastmap and
   re_match_2 use a failure stack.  These have to be macros because of
   REGEX_ALLOCATE_STACK.  */


/* Number of failure points for which to initially allocate space
   when matching.  If this number is exceeded, we allocate more
   space, so it is not a hard limit.  */
# ifndef INIT_FAILURE_ALLOC
#  define INIT_FAILURE_ALLOC 5
# endif

/* Roughly the maximum number of failure points on the stack.  Would be
   exactly that if always used MAX_FAILURE_ITEMS items each time we failed.
   This is a variable only so users of regex can assign to it; we never
   change it ourselves.  */

# ifdef INT_IS_16BIT

#  ifndef DEFINED_ONCE
#   if defined MATCH_MAY_ALLOCATE
/* 4400 was enough to cause a crash on Alpha OSF/1,
   whose default stack limit is 2mb.  */
long int re_max_failures = 4000;
#   else
long int re_max_failures = 2000;
#   endif
#  endif

union PREFIX(fail_stack_elt)
{
  UCHAR_T *pointer;
  long int integer;
};

typedef union PREFIX(fail_stack_elt) PREFIX(fail_stack_elt_t);

typedef struct
{
  PREFIX(fail_stack_elt_t) *stack;
  unsigned long int size;
  unsigned long int avail;      /* Offset of next open position.  */
} PREFIX(fail_stack_type);

# else /* not INT_IS_16BIT */

#  ifndef DEFINED_ONCE
#   if defined MATCH_MAY_ALLOCATE
/* 4400 was enough to cause a crash on Alpha OSF/1,
   whose default stack limit is 2mb.  */
int re_max_failures = 4000;
#   else
int re_max_failures = 2000;
#   endif
#  endif

union PREFIX(fail_stack_elt)
{
  UCHAR_T *pointer;
  int integer;
};

typedef union PREFIX(fail_stack_elt) PREFIX(fail_stack_elt_t);

typedef struct
{
  PREFIX(fail_stack_elt_t) *stack;
  unsigned size;
  unsigned avail;           /* Offset of next open position.  */
} PREFIX(fail_stack_type);

# endif /* INT_IS_16BIT */

# ifndef DEFINED_ONCE
#  define FAIL_STACK_EMPTY()     (fail_stack.avail == 0)
#  define FAIL_STACK_PTR_EMPTY() (fail_stack_ptr->avail == 0)
#  define FAIL_STACK_FULL()      (fail_stack.avail == fail_stack.size)
# endif


/* Define macros to initialize and free the failure stack.
   Do `return -2' if the alloc fails.  */

# ifdef MATCH_MAY_ALLOCATE
#  define INIT_FAIL_STACK()                     \
  do {                                  \
    fail_stack.stack = (PREFIX(fail_stack_elt_t) *)     \
      REGEX_ALLOCATE_STACK (INIT_FAILURE_ALLOC * sizeof (PREFIX(fail_stack_elt_t))); \
                                    \
    if (fail_stack.stack == NULL)               \
      return -2;                            \
                                    \
    fail_stack.size = INIT_FAILURE_ALLOC;           \
    fail_stack.avail = 0;                   \
  } while (0)

#  define RESET_FAIL_STACK()  REGEX_FREE_STACK (fail_stack.stack)
# else
#  define INIT_FAIL_STACK()                     \
  do {                                  \
    fail_stack.avail = 0;                   \
  } while (0)

#  define RESET_FAIL_STACK()
# endif


/* Double the size of FAIL_STACK, up to approximately `re_max_failures' items.

   Return 1 if succeeds, and 0 if either ran out of memory
   allocating space for it or it was already too large.

   REGEX_REALLOCATE_STACK requires `destination' be declared.   */

# define DOUBLE_FAIL_STACK(fail_stack)                  \
  ((fail_stack).size > (unsigned) (re_max_failures * MAX_FAILURE_ITEMS) \
   ? 0                                  \
   : ((fail_stack).stack = (PREFIX(fail_stack_elt_t) *)         \
        REGEX_REALLOCATE_STACK ((fail_stack).stack,             \
          (fail_stack).size * sizeof (PREFIX(fail_stack_elt_t)),    \
          ((fail_stack).size << 1) * sizeof (PREFIX(fail_stack_elt_t))),\
                                    \
      (fail_stack).stack == NULL                    \
      ? 0                               \
      : ((fail_stack).size <<= 1,                   \
         1)))


/* Push pointer POINTER on FAIL_STACK.
   Return 1 if was able to do so and 0 if ran out of memory allocating
   space to do so.  */
# define PUSH_PATTERN_OP(POINTER, FAIL_STACK)               \
  ((FAIL_STACK_FULL ()                          \
    && !DOUBLE_FAIL_STACK (FAIL_STACK))                 \
   ? 0                                  \
   : ((FAIL_STACK).stack[(FAIL_STACK).avail++].pointer = POINTER,   \
      1))

/* Push a pointer value onto the failure stack.
   Assumes the variable `fail_stack'.  Probably should only
   be called from within `PUSH_FAILURE_POINT'.  */
# define PUSH_FAILURE_POINTER(item)                 \
  fail_stack.stack[fail_stack.avail++].pointer = (UCHAR_T *) (item)

/* This pushes an integer-valued item onto the failure stack.
   Assumes the variable `fail_stack'.  Probably should only
   be called from within `PUSH_FAILURE_POINT'.  */
# define PUSH_FAILURE_INT(item)                 \
  fail_stack.stack[fail_stack.avail++].integer = (item)

/* Push a fail_stack_elt_t value onto the failure stack.
   Assumes the variable `fail_stack'.  Probably should only
   be called from within `PUSH_FAILURE_POINT'.  */
# define PUSH_FAILURE_ELT(item)                 \
  fail_stack.stack[fail_stack.avail++] =  (item)

/* These three POP... operations complement the three PUSH... operations.
   All assume that `fail_stack' is nonempty.  */
# define POP_FAILURE_POINTER() fail_stack.stack[--fail_stack.avail].pointer
# define POP_FAILURE_INT() fail_stack.stack[--fail_stack.avail].integer
# define POP_FAILURE_ELT() fail_stack.stack[--fail_stack.avail]

/* Used to omit pushing failure point id's when we're not debugging.  */
# ifdef DEBUG
#  define DEBUG_PUSH PUSH_FAILURE_INT
#  define DEBUG_POP(item_addr) *(item_addr) = POP_FAILURE_INT ()
# else
#  define DEBUG_PUSH(item)
#  define DEBUG_POP(item_addr)
# endif


/* Push the information about the state we will need
   if we ever fail back to it.

   Requires variables fail_stack, regstart, regend, reg_info, and
   num_regs_pushed be declared.  DOUBLE_FAIL_STACK requires `destination'
   be declared.

   Does `return FAILURE_CODE' if runs out of memory.  */

# define PUSH_FAILURE_POINT(pattern_place, string_place, failure_code)  \
  do {                                  \
    char *destination;                          \
    /* Must be int, so when we don't save any registers, the arithmetic \
       of 0 + -1 isn't done as unsigned.  */                \
    /* Can't be int, since there is not a shred of a guarantee that int \
       is wide enough to hold a value of something to which pointer can \
       be assigned */                           \
    active_reg_t this_reg;                      \
                                        \
    DEBUG_STATEMENT (failure_id++);                 \
    DEBUG_STATEMENT (nfailure_points_pushed++);             \
    DEBUG_PRINT2 ("\nPUSH_FAILURE_POINT #%u:\n", failure_id);       \
    DEBUG_PRINT2 ("  Before push, next avail: %d\n", (fail_stack).avail);\
    DEBUG_PRINT2 ("                     size: %d\n", (fail_stack).size);\
                                    \
    DEBUG_PRINT2 ("  slots needed: %ld\n", NUM_FAILURE_ITEMS);      \
    DEBUG_PRINT2 ("     available: %d\n", REMAINING_AVAIL_SLOTS);   \
                                    \
    /* Ensure we have enough space allocated for what we will push.  */ \
    while (REMAINING_AVAIL_SLOTS < NUM_FAILURE_ITEMS)           \
      {                                 \
        if (!DOUBLE_FAIL_STACK (fail_stack))                \
          return failure_code;                      \
                                    \
        DEBUG_PRINT2 ("\n  Doubled stack; size now: %d\n",      \
               (fail_stack).size);              \
        DEBUG_PRINT2 ("  slots available: %d\n", REMAINING_AVAIL_SLOTS);\
      }                                 \
                                    \
    /* Push the info, starting with the registers.  */          \
    DEBUG_PRINT1 ("\n");                        \
                                    \
    if (1)                              \
      for (this_reg = lowest_active_reg; this_reg <= highest_active_reg; \
       this_reg++)                          \
    {                               \
      DEBUG_PRINT2 ("  Pushing reg: %lu\n", this_reg);      \
      DEBUG_STATEMENT (num_regs_pushed++);              \
                                    \
      DEBUG_PRINT2 ("    start: %p\n", regstart[this_reg]);     \
      PUSH_FAILURE_POINTER (regstart[this_reg]);            \
                                    \
      DEBUG_PRINT2 ("    end: %p\n", regend[this_reg]);     \
      PUSH_FAILURE_POINTER (regend[this_reg]);          \
                                    \
      DEBUG_PRINT2 ("    info: %p\n      ",             \
            reg_info[this_reg].word.pointer);       \
      DEBUG_PRINT2 (" match_null=%d",               \
            REG_MATCH_NULL_STRING_P (reg_info[this_reg]));  \
      DEBUG_PRINT2 (" active=%d", IS_ACTIVE (reg_info[this_reg]));  \
      DEBUG_PRINT2 (" matched_something=%d",            \
            MATCHED_SOMETHING (reg_info[this_reg]));    \
      DEBUG_PRINT2 (" ever_matched=%d",             \
            EVER_MATCHED_SOMETHING (reg_info[this_reg]));   \
      DEBUG_PRINT1 ("\n");                      \
      PUSH_FAILURE_ELT (reg_info[this_reg].word);           \
    }                               \
                                    \
    DEBUG_PRINT2 ("  Pushing  low active reg: %ld\n", lowest_active_reg);\
    PUSH_FAILURE_INT (lowest_active_reg);               \
                                    \
    DEBUG_PRINT2 ("  Pushing high active reg: %ld\n", highest_active_reg);\
    PUSH_FAILURE_INT (highest_active_reg);              \
                                    \
    DEBUG_PRINT2 ("  Pushing pattern %p:\n", pattern_place);        \
    DEBUG_PRINT_COMPILED_PATTERN (bufp, pattern_place, pend);       \
    PUSH_FAILURE_POINTER (pattern_place);               \
                                    \
    DEBUG_PRINT2 ("  Pushing string %p: `", string_place);      \
    DEBUG_PRINT_DOUBLE_STRING (string_place, string1, size1, string2,   \
                 size2);                \
    DEBUG_PRINT1 ("'\n");                       \
    PUSH_FAILURE_POINTER (string_place);                \
                                    \
    DEBUG_PRINT2 ("  Pushing failure id: %u\n", failure_id);        \
    DEBUG_PUSH (failure_id);                        \
  } while (0)

# ifndef DEFINED_ONCE
/* This is the number of items that are pushed and popped on the stack
   for each register.  */
#  define NUM_REG_ITEMS  3

/* Individual items aside from the registers.  */
#  ifdef DEBUG
#   define NUM_NONREG_ITEMS 5 /* Includes failure point id.  */
#  else
#   define NUM_NONREG_ITEMS 4
#  endif

/* We push at most this many items on the stack.  */
/* We used to use (num_regs - 1), which is the number of registers
   this regexp will save; but that was changed to 5
   to avoid stack overflow for a regexp with lots of parens.  */
#  define MAX_FAILURE_ITEMS (5 * NUM_REG_ITEMS + NUM_NONREG_ITEMS)

/* We actually push this many items.  */
#  define NUM_FAILURE_ITEMS             \
  (((0                          \
     ? 0 : highest_active_reg - lowest_active_reg + 1)  \
    * NUM_REG_ITEMS)                    \
   + NUM_NONREG_ITEMS)

/* How many items can still be added to the stack without overflowing it.  */
#  define REMAINING_AVAIL_SLOTS ((fail_stack).size - (fail_stack).avail)
# endif /* not DEFINED_ONCE */


/* Pops what PUSH_FAIL_STACK pushes.

   We restore into the parameters, all of which should be lvalues:
     STR -- the saved data position.
     PAT -- the saved pattern position.
     LOW_REG, HIGH_REG -- the highest and lowest active registers.
     REGSTART, REGEND -- arrays of string positions.
     REG_INFO -- array of information about each subexpression.

   Also assumes the variables `fail_stack' and (if debugging), `bufp',
   `pend', `string1', `size1', `string2', and `size2'.  */
# define POP_FAILURE_POINT(str, pat, low_reg, high_reg, regstart, regend, reg_info)\
{                                   \
  DEBUG_STATEMENT (unsigned failure_id;)                \
  active_reg_t this_reg;                        \
  const UCHAR_T *string_temp;                       \
                                    \
  assert (!FAIL_STACK_EMPTY ());                    \
                                    \
  /* Remove failure points and point to how many regs pushed.  */   \
  DEBUG_PRINT1 ("POP_FAILURE_POINT:\n");                \
  DEBUG_PRINT2 ("  Before pop, next avail: %d\n", fail_stack.avail);    \
  DEBUG_PRINT2 ("                    size: %d\n", fail_stack.size); \
                                    \
  assert (fail_stack.avail >= NUM_NONREG_ITEMS);            \
                                    \
  DEBUG_POP (&failure_id);                      \
  DEBUG_PRINT2 ("  Popping failure id: %u\n", failure_id);      \
                                    \
  /* If the saved string location is NULL, it came from an      \
     on_failure_keep_string_jump opcode, and we want to throw away the  \
     saved NULL, thus retaining our current position in the string.  */ \
  string_temp = POP_FAILURE_POINTER ();                 \
  if (string_temp != NULL)                      \
    str = (const CHAR_T *) string_temp;                 \
                                    \
  DEBUG_PRINT2 ("  Popping string %p: `", str);             \
  DEBUG_PRINT_DOUBLE_STRING (str, string1, size1, string2, size2);  \
  DEBUG_PRINT1 ("'\n");                         \
                                    \
  pat = (UCHAR_T *) POP_FAILURE_POINTER ();             \
  DEBUG_PRINT2 ("  Popping pattern %p:\n", pat);            \
  DEBUG_PRINT_COMPILED_PATTERN (bufp, pat, pend);           \
                                    \
  /* Restore register info.  */                     \
  high_reg = (active_reg_t) POP_FAILURE_INT ();             \
  DEBUG_PRINT2 ("  Popping high active reg: %ld\n", high_reg);      \
                                    \
  low_reg = (active_reg_t) POP_FAILURE_INT ();              \
  DEBUG_PRINT2 ("  Popping  low active reg: %ld\n", low_reg);       \
                                    \
  if (1)                                \
    for (this_reg = high_reg; this_reg >= low_reg; this_reg--)      \
      {                                 \
    DEBUG_PRINT2 ("    Popping reg: %ld\n", this_reg);      \
                                    \
    reg_info[this_reg].word = POP_FAILURE_ELT ();           \
    DEBUG_PRINT2 ("      info: %p\n",               \
              reg_info[this_reg].word.pointer);         \
                                    \
    regend[this_reg] = (const CHAR_T *) POP_FAILURE_POINTER (); \
    DEBUG_PRINT2 ("      end: %p\n", regend[this_reg]);     \
                                    \
    regstart[this_reg] = (const CHAR_T *) POP_FAILURE_POINTER ();   \
    DEBUG_PRINT2 ("      start: %p\n", regstart[this_reg]);     \
      }                                 \
  else                                  \
    {                                   \
      for (this_reg = highest_active_reg; this_reg > high_reg; this_reg--) \
    {                               \
      reg_info[this_reg].word.integer = 0;              \
      regend[this_reg] = 0;                     \
      regstart[this_reg] = 0;                   \
    }                               \
      highest_active_reg = high_reg;                    \
    }                                   \
                                    \
  set_regs_matched_done = 0;                        \
  DEBUG_STATEMENT (nfailure_points_popped++);               \
} /* POP_FAILURE_POINT */


/* Structure for per-register (a.k.a. per-group) information.
   Other register information, such as the
   starting and ending positions (which are addresses), and the list of
   inner groups (which is a bits list) are maintained in separate
   variables.

   We are making a (strictly speaking) nonportable assumption here: that
   the compiler will pack our bit fields into something that fits into
   the type of `word', i.e., is something that fits into one item on the
   failure stack.  */


/* Declarations and macros for re_match_2.  */

typedef union
{
  PREFIX(fail_stack_elt_t) word;
  struct
  {
      /* This field is one if this group can match the empty string,
         zero if not.  If not yet determined,  `MATCH_NULL_UNSET_VALUE'.  */
# define MATCH_NULL_UNSET_VALUE 3
    unsigned match_null_string_p : 2;
    unsigned is_active : 1;
    unsigned matched_something : 1;
    unsigned ever_matched_something : 1;
  } bits;
} PREFIX(register_info_type);

# ifndef DEFINED_ONCE
#  define REG_MATCH_NULL_STRING_P(R)  ((R).bits.match_null_string_p)
#  define IS_ACTIVE(R)  ((R).bits.is_active)
#  define MATCHED_SOMETHING(R)  ((R).bits.matched_something)
#  define EVER_MATCHED_SOMETHING(R)  ((R).bits.ever_matched_something)


/* Call this when have matched a real character; it sets `matched' flags
   for the subexpressions which we are currently inside.  Also records
   that those subexprs have matched.  */
#  define SET_REGS_MATCHED()                        \
  do                                    \
    {                                   \
      if (!set_regs_matched_done)                   \
    {                               \
      active_reg_t r;                       \
      set_regs_matched_done = 1;                    \
      for (r = lowest_active_reg; r <= highest_active_reg; r++) \
        {                               \
          MATCHED_SOMETHING (reg_info[r])               \
        = EVER_MATCHED_SOMETHING (reg_info[r])          \
        = 1;                            \
        }                               \
    }                               \
    }                                   \
  while (0)
# endif /* not DEFINED_ONCE */

/* Registers are set to a sentinel when they haven't yet matched.  */
static CHAR_T PREFIX(reg_unset_dummy);
# define REG_UNSET_VALUE (&PREFIX(reg_unset_dummy))
# define REG_UNSET(e) ((e) == REG_UNSET_VALUE)

/* Subroutine declarations and macros for regex_compile.  */
static void PREFIX(store_op1) _RE_ARGS ((re_opcode_t op, UCHAR_T *loc, int arg));
static void PREFIX(store_op2) _RE_ARGS ((re_opcode_t op, UCHAR_T *loc,
                 int arg1, int arg2));
static void PREFIX(insert_op1) _RE_ARGS ((re_opcode_t op, UCHAR_T *loc,
                  int arg, UCHAR_T *end));
static void PREFIX(insert_op2) _RE_ARGS ((re_opcode_t op, UCHAR_T *loc,
                  int arg1, int arg2, UCHAR_T *end));
static boolean PREFIX(at_begline_loc_p) _RE_ARGS ((const CHAR_T *pattern,
                       const CHAR_T *p,
                       reg_syntax_t syntax));
static boolean PREFIX(at_endline_loc_p) _RE_ARGS ((const CHAR_T *p,
                       const CHAR_T *pend,
                       reg_syntax_t syntax));
# ifdef WCHAR
static reg_errcode_t wcs_compile_range _RE_ARGS ((CHAR_T range_start,
                          const CHAR_T **p_ptr,
                          const CHAR_T *pend,
                          char *translate,
                          reg_syntax_t syntax,
                          UCHAR_T *b,
                          CHAR_T *char_set));
static void insert_space _RE_ARGS ((int num, CHAR_T *loc, CHAR_T *end));
# else /* BYTE */
static reg_errcode_t byte_compile_range _RE_ARGS ((unsigned int range_start,
                           const char **p_ptr,
                           const char *pend,
                           char *translate,
                           reg_syntax_t syntax,
                           unsigned char *b));
# endif /* WCHAR */

/* Fetch the next character in the uncompiled pattern---translating it
   if necessary.  Also cast from a signed character in the constant
   string passed to us by the user to an unsigned char that we can use
   as an array index (in, e.g., `translate').  */
/* ifdef MBS_SUPPORT, we translate only if character <= 0xff,
   because it is impossible to allocate 4GB array for some encodings
   which have 4 byte character_set like UCS4.  */
# ifndef PATFETCH
#  ifdef WCHAR
#   define PATFETCH(c)                          \
  do {if (p == pend) return REG_EEND;                   \
    c = (UCHAR_T) *p++;                         \
    if (translate && (c <= 0xff)) c = (UCHAR_T) translate[c];       \
  } while (0)
#  else /* BYTE */
#   define PATFETCH(c)                          \
  do {if (p == pend) return REG_EEND;                   \
    c = (unsigned char) *p++;                       \
    if (translate) c = (unsigned char) translate[c];            \
  } while (0)
#  endif /* WCHAR */
# endif

/* Fetch the next character in the uncompiled pattern, with no
   translation.  */
# define PATFETCH_RAW(c)                        \
  do {if (p == pend) return REG_EEND;                   \
    c = (UCHAR_T) *p++;                             \
  } while (0)

/* Go backwards one character in the pattern.  */
# define PATUNFETCH p--


/* If `translate' is non-null, return translate[D], else just D.  We
   cast the subscript to translate because some data is declared as
   `char *', to avoid warnings when a string constant is passed.  But
   when we use a character as a subscript we must make it unsigned.  */
/* ifdef MBS_SUPPORT, we translate only if character <= 0xff,
   because it is impossible to allocate 4GB array for some encodings
   which have 4 byte character_set like UCS4.  */

# ifndef TRANSLATE
#  ifdef WCHAR
#   define TRANSLATE(d) \
  ((translate && ((UCHAR_T) (d)) <= 0xff) \
   ? (char) translate[(unsigned char) (d)] : (d))
# else /* BYTE */
#   define TRANSLATE(d) \
  (translate ? (char) translate[(unsigned char) (d)] : (d))
#  endif /* WCHAR */
# endif


/* Macros for outputting the compiled pattern into `buffer'.  */

/* If the buffer isn't allocated when it comes in, use this.  */
# define INIT_BUF_SIZE  (32 * sizeof(UCHAR_T))

/* Make sure we have at least N more bytes of space in buffer.  */
# ifdef WCHAR
#  define GET_BUFFER_SPACE(n)                       \
    while (((unsigned long)b - (unsigned long)COMPILED_BUFFER_VAR   \
            + (n)*sizeof(CHAR_T)) > bufp->allocated)            \
      EXTEND_BUFFER ()
# else /* BYTE */
#  define GET_BUFFER_SPACE(n)                       \
    while ((unsigned long) (b - bufp->buffer + (n)) > bufp->allocated)  \
      EXTEND_BUFFER ()
# endif /* WCHAR */

/* Make sure we have one more byte of buffer space and then add C to it.  */
# define BUF_PUSH(c)                            \
  do {                                  \
    GET_BUFFER_SPACE (1);                       \
    *b++ = (UCHAR_T) (c);                       \
  } while (0)


/* Ensure we have two more bytes of buffer space and then append C1 and C2.  */
# define BUF_PUSH_2(c1, c2)                     \
  do {                                  \
    GET_BUFFER_SPACE (2);                       \
    *b++ = (UCHAR_T) (c1);                      \
    *b++ = (UCHAR_T) (c2);                      \
  } while (0)


/* As with BUF_PUSH_2, except for three bytes.  */
# define BUF_PUSH_3(c1, c2, c3)                     \
  do {                                  \
    GET_BUFFER_SPACE (3);                       \
    *b++ = (UCHAR_T) (c1);                      \
    *b++ = (UCHAR_T) (c2);                      \
    *b++ = (UCHAR_T) (c3);                      \
  } while (0)

/* Store a jump with opcode OP at LOC to location TO.  We store a
   relative address offset by the three bytes the jump itself occupies.  */
# define STORE_JUMP(op, loc, to) \
 PREFIX(store_op1) (op, loc, (int) ((to) - (loc) - (1 + OFFSET_ADDRESS_SIZE)))

/* Likewise, for a two-argument jump.  */
# define STORE_JUMP2(op, loc, to, arg) \
  PREFIX(store_op2) (op, loc, (int) ((to) - (loc) - (1 + OFFSET_ADDRESS_SIZE)), arg)

/* Like `STORE_JUMP', but for inserting.  Assume `b' is the buffer end.  */
# define INSERT_JUMP(op, loc, to) \
  PREFIX(insert_op1) (op, loc, (int) ((to) - (loc) - (1 + OFFSET_ADDRESS_SIZE)), b)

/* Like `STORE_JUMP2', but for inserting.  Assume `b' is the buffer end.  */
# define INSERT_JUMP2(op, loc, to, arg) \
  PREFIX(insert_op2) (op, loc, (int) ((to) - (loc) - (1 + OFFSET_ADDRESS_SIZE)),\
          arg, b)

/* This is not an arbitrary limit: the arguments which represent offsets
   into the pattern are two bytes long.  So if 2^16 bytes turns out to
   be too small, many things would have to change.  */
/* Any other compiler which, like MSC, has allocation limit below 2^16
   bytes will have to use approach similar to what was done below for
   MSC and drop MAX_BUF_SIZE a bit.  Otherwise you may end up
   reallocating to 0 bytes.  Such thing is not going to work too well.
   You have been warned!!  */
# ifndef DEFINED_ONCE
#  if defined _MSC_VER  && !defined WIN32
/* Microsoft C 16-bit versions limit malloc to approx 65512 bytes.
   The REALLOC define eliminates a flurry of conversion warnings,
   but is not required. */
#   define MAX_BUF_SIZE  65500L
#   define REALLOC(p,s) realloc ((p), (size_t) (s))
#  else
#   define MAX_BUF_SIZE (1L << 16)
#   define REALLOC(p,s) realloc ((p), (s))
#  endif

/* Extend the buffer by twice its current size via realloc and
   reset the pointers that pointed into the old block to point to the
   correct places in the new one.  If extending the buffer results in it
   being larger than MAX_BUF_SIZE, then flag memory exhausted.  */
#  if __BOUNDED_POINTERS__
#   define SET_HIGH_BOUND(P) (__ptrhigh (P) = __ptrlow (P) + bufp->allocated)
#   define MOVE_BUFFER_POINTER(P) \
  (__ptrlow (P) += incr, SET_HIGH_BOUND (P), __ptrvalue (P) += incr)
#   define ELSE_EXTEND_BUFFER_HIGH_BOUND    \
  else                      \
    {                       \
      SET_HIGH_BOUND (b);           \
      SET_HIGH_BOUND (begalt);          \
      if (fixup_alt_jump)           \
    SET_HIGH_BOUND (fixup_alt_jump);    \
      if (laststart)                \
    SET_HIGH_BOUND (laststart);     \
      if (pending_exact)            \
    SET_HIGH_BOUND (pending_exact);     \
    }
#  else
#   define MOVE_BUFFER_POINTER(P) (P) += incr
#   define ELSE_EXTEND_BUFFER_HIGH_BOUND
#  endif
# endif /* not DEFINED_ONCE */

# ifdef WCHAR
#  define EXTEND_BUFFER()                       \
  do {                                  \
    UCHAR_T *old_buffer = COMPILED_BUFFER_VAR;              \
    int wchar_count;                            \
    if (bufp->allocated + sizeof(UCHAR_T) > MAX_BUF_SIZE)       \
      return REG_ESIZE;                         \
    bufp->allocated <<= 1;                      \
    if (bufp->allocated > MAX_BUF_SIZE)                 \
      bufp->allocated = MAX_BUF_SIZE;                   \
    /* How many characters the new buffer can have?  */         \
    wchar_count = bufp->allocated / sizeof(UCHAR_T);            \
    if (wchar_count == 0) wchar_count = 1;              \
    /* Truncate the buffer to CHAR_T align.  */         \
    bufp->allocated = wchar_count * sizeof(UCHAR_T);            \
    RETALLOC (COMPILED_BUFFER_VAR, wchar_count, UCHAR_T);       \
    bufp->buffer = (char*)COMPILED_BUFFER_VAR;              \
    if (COMPILED_BUFFER_VAR == NULL)                    \
      return REG_ESPACE;                        \
    /* If the buffer moved, move all the pointers into it.  */      \
    if (old_buffer != COMPILED_BUFFER_VAR)              \
      {                                 \
    int incr = COMPILED_BUFFER_VAR - old_buffer;            \
    MOVE_BUFFER_POINTER (b);                    \
    MOVE_BUFFER_POINTER (begalt);                   \
    if (fixup_alt_jump)                     \
      MOVE_BUFFER_POINTER (fixup_alt_jump);             \
    if (laststart)                          \
      MOVE_BUFFER_POINTER (laststart);              \
    if (pending_exact)                      \
      MOVE_BUFFER_POINTER (pending_exact);              \
      }                                 \
    ELSE_EXTEND_BUFFER_HIGH_BOUND                   \
  } while (0)
# else /* BYTE */
#  define EXTEND_BUFFER()                       \
  do {                                  \
    UCHAR_T *old_buffer = COMPILED_BUFFER_VAR;              \
    if (bufp->allocated == MAX_BUF_SIZE)                \
      return REG_ESIZE;                         \
    bufp->allocated <<= 1;                      \
    if (bufp->allocated > MAX_BUF_SIZE)                 \
      bufp->allocated = MAX_BUF_SIZE;                   \
    bufp->buffer = (UCHAR_T *) REALLOC (COMPILED_BUFFER_VAR,        \
                        bufp->allocated);   \
    if (COMPILED_BUFFER_VAR == NULL)                    \
      return REG_ESPACE;                        \
    /* If the buffer moved, move all the pointers into it.  */      \
    if (old_buffer != COMPILED_BUFFER_VAR)              \
      {                                 \
    int incr = COMPILED_BUFFER_VAR - old_buffer;            \
    MOVE_BUFFER_POINTER (b);                    \
    MOVE_BUFFER_POINTER (begalt);                   \
    if (fixup_alt_jump)                     \
      MOVE_BUFFER_POINTER (fixup_alt_jump);             \
    if (laststart)                          \
      MOVE_BUFFER_POINTER (laststart);              \
    if (pending_exact)                      \
      MOVE_BUFFER_POINTER (pending_exact);              \
      }                                 \
    ELSE_EXTEND_BUFFER_HIGH_BOUND                   \
  } while (0)
# endif /* WCHAR */

# ifndef DEFINED_ONCE
/* Since we have one byte reserved for the register number argument to
   {start,stop}_memory, the maximum number of groups we can report
   things about is what fits in that byte.  */
#  define MAX_REGNUM 255

/* But patterns can have more than `MAX_REGNUM' registers.  We just
   ignore the excess.  */
typedef unsigned regnum_t;


/* Macros for the compile stack.  */

/* Since offsets can go either forwards or backwards, this type needs to
   be able to hold values from -(MAX_BUF_SIZE - 1) to MAX_BUF_SIZE - 1.  */
/* int may be not enough when sizeof(int) == 2.  */
typedef long pattern_offset_t;

typedef struct
{
  pattern_offset_t begalt_offset;
  pattern_offset_t fixup_alt_jump;
  pattern_offset_t inner_group_offset;
  pattern_offset_t laststart_offset;
  regnum_t regnum;
} compile_stack_elt_t;


typedef struct
{
  compile_stack_elt_t *stack;
  unsigned size;
  unsigned avail;           /* Offset of next open position.  */
} compile_stack_type;


#  define INIT_COMPILE_STACK_SIZE 32

#  define COMPILE_STACK_EMPTY  (compile_stack.avail == 0)
#  define COMPILE_STACK_FULL  (compile_stack.avail == compile_stack.size)

/* The next available element.  */
#  define COMPILE_STACK_TOP (compile_stack.stack[compile_stack.avail])

# endif /* not DEFINED_ONCE */

/* Set the bit for character C in a list.  */
# ifndef DEFINED_ONCE
#  define SET_LIST_BIT(c)                               \
  (b[((unsigned char) (c)) / BYTEWIDTH]               \
   |= 1 << (((unsigned char) c) % BYTEWIDTH))
# endif /* DEFINED_ONCE */

/* Get the next unsigned number in the uncompiled pattern.  */
# define GET_UNSIGNED_NUMBER(num) \
  {                                 \
    while (p != pend)                           \
      {                                 \
    PATFETCH (c);                           \
    if (c < '0' || c > '9')                     \
      break;                            \
    if (num <= RE_DUP_MAX)                      \
      {                             \
        if (num < 0)                        \
          num = 0;                          \
        num = num * 10 + c - '0';                   \
      }                             \
      }                                 \
  }

# ifndef DEFINED_ONCE
#  if defined _LIBC || WIDE_CHAR_SUPPORT
/* The GNU C library provides support for user-defined character classes
   and the functions from ISO C amendement 1.  */
#   ifdef CHARCLASS_NAME_MAX
#    define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
#   else
/* This shouldn't happen but some implementation might still have this
   problem.  Use a reasonable default value.  */
#    define CHAR_CLASS_MAX_LENGTH 256
#   endif

#   ifdef _LIBC
#    define IS_CHAR_CLASS(string) __wctype (string)
#   else
#    define IS_CHAR_CLASS(string) wctype (string)
#   endif
#  else
#   define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */

#   define IS_CHAR_CLASS(string)                    \
   (STREQ (string, "alpha") || STREQ (string, "upper")          \
    || STREQ (string, "lower") || STREQ (string, "digit")       \
    || STREQ (string, "alnum") || STREQ (string, "xdigit")      \
    || STREQ (string, "space") || STREQ (string, "print")       \
    || STREQ (string, "punct") || STREQ (string, "graph")       \
    || STREQ (string, "cntrl") || STREQ (string, "blank"))
#  endif
# endif /* DEFINED_ONCE */


# ifndef MATCH_MAY_ALLOCATE

/* If we cannot allocate large objects within re_match_2_internal,
   we make the fail stack and register vectors global.
   The fail stack, we grow to the maximum size when a regexp
   is compiled.
   The register vectors, we adjust in size each time we
   compile a regexp, according to the number of registers it needs.  */

static PREFIX(fail_stack_type) fail_stack;

/* Size with which the following vectors are currently allocated.
   That is so we can make them bigger as needed,
   but never make them smaller.  */
#  ifdef DEFINED_ONCE
static int regs_allocated_size;

static const char **     regstart, **     regend;
static const char ** old_regstart, ** old_regend;
static const char **best_regstart, **best_regend;
static const char **reg_dummy;
#  endif /* DEFINED_ONCE */

static PREFIX(register_info_type) *PREFIX(reg_info);
static PREFIX(register_info_type) *PREFIX(reg_info_dummy);

/* Make the register vectors big enough for NUM_REGS registers,
   but don't make them smaller.  */

static void
PREFIX(regex_grow_registers) (num_regs)
     int num_regs;
{
  if (num_regs > regs_allocated_size)
    {
      RETALLOC_IF (regstart,     num_regs, const char *);
      RETALLOC_IF (regend,   num_regs, const char *);
      RETALLOC_IF (old_regstart, num_regs, const char *);
      RETALLOC_IF (old_regend,   num_regs, const char *);
      RETALLOC_IF (best_regstart, num_regs, const char *);
      RETALLOC_IF (best_regend,  num_regs, const char *);
      RETALLOC_IF (PREFIX(reg_info), num_regs, PREFIX(register_info_type));
      RETALLOC_IF (reg_dummy,    num_regs, const char *);
      RETALLOC_IF (PREFIX(reg_info_dummy), num_regs, PREFIX(register_info_type));

      regs_allocated_size = num_regs;
    }
}

# endif /* not MATCH_MAY_ALLOCATE */


# ifndef DEFINED_ONCE
static boolean group_in_compile_stack _RE_ARGS ((compile_stack_type
                         compile_stack,
                         regnum_t regnum));
# endif /* not DEFINED_ONCE */

/* `regex_compile' compiles PATTERN (of length SIZE) according to SYNTAX.
   Returns one of error codes defined in `regex.h', or zero for success.

   Assumes the `allocated' (and perhaps `buffer') and `translate'
   fields are set in BUFP on entry.

   If it succeeds, results are put in BUFP (if it returns an error, the
   contents of BUFP are undefined):
     `buffer' is the compiled pattern;
     `syntax' is set to SYNTAX;
     `used' is set to the length of the compiled pattern;
     `fastmap_accurate' is zero;
     `re_nsub' is the number of subexpressions in PATTERN;
     `not_bol' and `not_eol' are zero;

   The `fastmap' and `newline_anchor' fields are neither
   examined nor set.  */

/* Return, freeing storage we allocated.  */
# ifdef WCHAR
#  define FREE_STACK_RETURN(value)      \
  return (free(pattern), free(mbs_offset), free(is_binary), free (compile_stack.stack), value)
# else
#  define FREE_STACK_RETURN(value)      \
  return (free (compile_stack.stack), value)
# endif /* WCHAR */

static reg_errcode_t
PREFIX(regex_compile) (ARG_PREFIX(pattern), ARG_PREFIX(size), syntax, bufp)
     const char *ARG_PREFIX(pattern);
     size_t ARG_PREFIX(size);
     reg_syntax_t syntax;
     struct re_pattern_buffer *bufp;
{
  /* We fetch characters from PATTERN here.  Even though PATTERN is
     `char *' (i.e., signed), we declare these variables as unsigned, so
     they can be reliably used as array indices.  */
  register UCHAR_T c, c1;

#ifdef WCHAR
  /* A temporary space to keep wchar_t pattern and compiled pattern.  */
  CHAR_T *pattern, *COMPILED_BUFFER_VAR;
  size_t size;
  /* offset buffer for optimization. See convert_mbs_to_wc.  */
  int *mbs_offset = NULL;
  /* It hold whether each wchar_t is binary data or not.  */
  char *is_binary = NULL;
  /* A flag whether exactn is handling binary data or not.  */
  char is_exactn_bin = FALSE;
#endif /* WCHAR */

  /* A random temporary spot in PATTERN.  */
  const CHAR_T *p1;

  /* Points to the end of the buffer, where we should append.  */
  register UCHAR_T *b;

  /* Keeps track of unclosed groups.  */
  compile_stack_type compile_stack;

  /* Points to the current (ending) position in the pattern.  */
#ifdef WCHAR
  const CHAR_T *p;
  const CHAR_T *pend;
#else /* BYTE */
  const CHAR_T *p = pattern;
  const CHAR_T *pend = pattern + size;
#endif /* WCHAR */

  /* How to translate the characters in the pattern.  */
  RE_TRANSLATE_TYPE translate = bufp->translate;

  /* Address of the count-byte of the most recently inserted `exactn'
     command.  This makes it possible to tell if a new exact-match
     character can be added to that command or if the character requires
     a new `exactn' command.  */
  UCHAR_T *pending_exact = 0;

  /* Address of start of the most recently finished expression.
     This tells, e.g., postfix * where to find the start of its
     operand.  Reset at the beginning of groups and alternatives.  */
  UCHAR_T *laststart = 0;

  /* Address of beginning of regexp, or inside of last group.  */
  UCHAR_T *begalt;

  /* Address of the place where a forward jump should go to the end of
     the containing expression.  Each alternative of an `or' -- except the
     last -- ends with a forward jump of this sort.  */
  UCHAR_T *fixup_alt_jump = 0;

  /* Counts open-groups as they are encountered.  Remembered for the
     matching close-group on the compile stack, so the same register
     number is put in the stop_memory as the start_memory.  */
  regnum_t regnum = 0;

#ifdef WCHAR
  /* Initialize the wchar_t PATTERN and offset_buffer.  */
  p = pend = pattern = TALLOC(csize + 1, CHAR_T);
  mbs_offset = TALLOC(csize + 1, int);
  is_binary = TALLOC(csize + 1, char);
  if (pattern == NULL || mbs_offset == NULL || is_binary == NULL)
    {
      free(pattern);
      free(mbs_offset);
      free(is_binary);
      return REG_ESPACE;
    }
  pattern[csize] = L'\0';   /* sentinel */
  size = convert_mbs_to_wcs(pattern, cpattern, csize, mbs_offset, is_binary);
  pend = p + size;
  if (size < 0)
    {
      free(pattern);
      free(mbs_offset);
      free(is_binary);
      return REG_BADPAT;
    }
#endif

#ifdef DEBUG
  DEBUG_PRINT1 ("\nCompiling pattern: ");
  if (debug)
    {
      unsigned debug_count;

      for (debug_count = 0; debug_count < size; debug_count++)
        PUT_CHAR (pattern[debug_count]);
      putchar ('\n');
    }
#endif /* DEBUG */

  /* Initialize the compile stack.  */
  compile_stack.stack = TALLOC (INIT_COMPILE_STACK_SIZE, compile_stack_elt_t);
  if (compile_stack.stack == NULL)
    {
#ifdef WCHAR
      free(pattern);
      free(mbs_offset);
      free(is_binary);
#endif
      return REG_ESPACE;
    }

  compile_stack.size = INIT_COMPILE_STACK_SIZE;
  compile_stack.avail = 0;

  /* Initialize the pattern buffer.  */
  bufp->syntax = syntax;
  bufp->fastmap_accurate = 0;
  bufp->not_bol = bufp->not_eol = 0;

  /* Set `used' to zero, so that if we return an error, the pattern
     printer (for debugging) will think there's no pattern.  We reset it
     at the end.  */
  bufp->used = 0;

  /* Always count groups, whether or not bufp->no_sub is set.  */
  bufp->re_nsub = 0;

#if !defined emacs && !defined SYNTAX_TABLE
  /* Initialize the syntax table.  */
   init_syntax_once ();
#endif

  if (bufp->allocated == 0)
    {
      if (bufp->buffer)
    { /* If zero allocated, but buffer is non-null, try to realloc
             enough space.  This loses if buffer's address is bogus, but
             that is the user's responsibility.  */
#ifdef WCHAR
      /* Free bufp->buffer and allocate an array for wchar_t pattern
         buffer.  */
          free(bufp->buffer);
          COMPILED_BUFFER_VAR = TALLOC (INIT_BUF_SIZE/sizeof(UCHAR_T),
                    UCHAR_T);
#else
          RETALLOC (COMPILED_BUFFER_VAR, INIT_BUF_SIZE, UCHAR_T);
#endif /* WCHAR */
        }
      else
        { /* Caller did not allocate a buffer.  Do it for them.  */
          COMPILED_BUFFER_VAR = TALLOC (INIT_BUF_SIZE / sizeof(UCHAR_T),
                    UCHAR_T);
        }

      if (!COMPILED_BUFFER_VAR) FREE_STACK_RETURN (REG_ESPACE);
#ifdef WCHAR
      bufp->buffer = (char*)COMPILED_BUFFER_VAR;
#endif /* WCHAR */
      bufp->allocated = INIT_BUF_SIZE;
    }
#ifdef WCHAR
  else
    COMPILED_BUFFER_VAR = (UCHAR_T*) bufp->buffer;
#endif

  begalt = b = COMPILED_BUFFER_VAR;

  /* Loop through the uncompiled pattern until we're at the end.  */
  while (p != pend)
    {
      PATFETCH (c);

      switch (c)
        {
        case '^':
          {
            if (   /* If at start of pattern, it's an operator.  */
                   p == pattern + 1
                   /* If context independent, it's an operator.  */
                || syntax & RE_CONTEXT_INDEP_ANCHORS
                   /* Otherwise, depends on what's come before.  */
                || PREFIX(at_begline_loc_p) (pattern, p, syntax))
              BUF_PUSH (begline);
            else
              goto normal_char;
          }
          break;


        case '$':
          {
            if (   /* If at end of pattern, it's an operator.  */
                   p == pend
                   /* If context independent, it's an operator.  */
                || syntax & RE_CONTEXT_INDEP_ANCHORS
                   /* Otherwise, depends on what's next.  */
                || PREFIX(at_endline_loc_p) (p, pend, syntax))
               BUF_PUSH (endline);
             else
               goto normal_char;
           }
           break;


    case '+':
        case '?':
          if ((syntax & RE_BK_PLUS_QM)
              || (syntax & RE_LIMITED_OPS))
            goto normal_char;
        handle_plus:
        case '*':
          /* If there is no previous pattern... */
          if (!laststart)
            {
              if (syntax & RE_CONTEXT_INVALID_OPS)
                FREE_STACK_RETURN (REG_BADRPT);
              else if (!(syntax & RE_CONTEXT_INDEP_OPS))
                goto normal_char;
            }

          {
            /* Are we optimizing this jump?  */
            boolean keep_string_p = false;

            /* 1 means zero (many) matches is allowed.  */
            char zero_times_ok = 0, many_times_ok = 0;

            /* If there is a sequence of repetition chars, collapse it
               down to just one (the right one).  We can't combine
               interval operators with these because of, e.g., `a{2}*',
               which should only match an even number of `a's.  */

            for (;;)
              {
                zero_times_ok |= c != '+';
                many_times_ok |= c != '?';

                if (p == pend)
                  break;

                PATFETCH (c);

                if (c == '*'
                    || (!(syntax & RE_BK_PLUS_QM) && (c == '+' || c == '?')))
                  ;

                else if (syntax & RE_BK_PLUS_QM  &&  c == '\\')
                  {
                    if (p == pend) FREE_STACK_RETURN (REG_EESCAPE);

                    PATFETCH (c1);
                    if (!(c1 == '+' || c1 == '?'))
                      {
                        PATUNFETCH;
                        PATUNFETCH;
                        break;
                      }

                    c = c1;
                  }
                else
                  {
                    PATUNFETCH;
                    break;
                  }

                /* If we get here, we found another repeat character.  */
               }

            /* Star, etc. applied to an empty pattern is equivalent
               to an empty pattern.  */
            if (!laststart)
              break;

            /* Now we know whether or not zero matches is allowed
               and also whether or not two or more matches is allowed.  */
            if (many_times_ok)
              { /* More than one repetition is allowed, so put in at the
                   end a backward relative jump from `b' to before the next
                   jump we're going to put in below (which jumps from
                   laststart to after this jump).

                   But if we are at the `*' in the exact sequence `.*\n',
                   insert an unconditional jump backwards to the .,
                   instead of the beginning of the loop.  This way we only
                   push a failure point once, instead of every time
                   through the loop.  */
                assert (p - 1 > pattern);

                /* Allocate the space for the jump.  */
                GET_BUFFER_SPACE (1 + OFFSET_ADDRESS_SIZE);

                /* We know we are not at the first character of the pattern,
                   because laststart was nonzero.  And we've already
                   incremented `p', by the way, to be the character after
                   the `*'.  Do we have to do something analogous here
                   for null bytes, because of RE_DOT_NOT_NULL?  */
                if (TRANSLATE (*(p - 2)) == TRANSLATE ('.')
            && zero_times_ok
                    && p < pend && TRANSLATE (*p) == TRANSLATE ('\n')
                    && !(syntax & RE_DOT_NEWLINE))
                  { /* We have .*\n.  */
                    STORE_JUMP (jump, b, laststart);
                    keep_string_p = true;
                  }
                else
                  /* Anything else.  */
                  STORE_JUMP (maybe_pop_jump, b, laststart -
                  (1 + OFFSET_ADDRESS_SIZE));

                /* We've added more stuff to the buffer.  */
                b += 1 + OFFSET_ADDRESS_SIZE;
              }

            /* On failure, jump from laststart to b + 3, which will be the
               end of the buffer after this jump is inserted.  */
        /* ifdef WCHAR, 'b + 1 + OFFSET_ADDRESS_SIZE' instead of
           'b + 3'.  */
            GET_BUFFER_SPACE (1 + OFFSET_ADDRESS_SIZE);
            INSERT_JUMP (keep_string_p ? on_failure_keep_string_jump
                                       : on_failure_jump,
                         laststart, b + 1 + OFFSET_ADDRESS_SIZE);
            pending_exact = 0;
            b += 1 + OFFSET_ADDRESS_SIZE;

            if (!zero_times_ok)
              {
                /* At least one repetition is required, so insert a
                   `dummy_failure_jump' before the initial
                   `on_failure_jump' instruction of the loop. This
                   effects a skip over that instruction the first time
                   we hit that loop.  */
                GET_BUFFER_SPACE (1 + OFFSET_ADDRESS_SIZE);
                INSERT_JUMP (dummy_failure_jump, laststart, laststart +
                 2 + 2 * OFFSET_ADDRESS_SIZE);
                b += 1 + OFFSET_ADDRESS_SIZE;
              }
            }
      break;


    case '.':
          laststart = b;
          BUF_PUSH (anychar);
          break;


        case '[':
          {
            boolean had_char_class = false;
#ifdef WCHAR
        CHAR_T range_start = 0xffffffff;
#else
        unsigned int range_start = 0xffffffff;
#endif
            if (p == pend) FREE_STACK_RETURN (REG_EBRACK);

#ifdef WCHAR
        /* We assume a charset(_not) structure as a wchar_t array.
           charset[0] = (re_opcode_t) charset(_not)
               charset[1] = l (= length of char_classes)
               charset[2] = m (= length of collating_symbols)
               charset[3] = n (= length of equivalence_classes)
           charset[4] = o (= length of char_ranges)
           charset[5] = p (= length of chars)

               charset[6] = char_class (wctype_t)
               charset[6+CHAR_CLASS_SIZE] = char_class (wctype_t)
                         ...
               charset[l+5]  = char_class (wctype_t)

               charset[l+6]  = collating_symbol (wchar_t)
                            ...
               charset[l+m+5]  = collating_symbol (wchar_t)
                    ifdef _LIBC we use the index if
                    _NL_COLLATE_SYMB_EXTRAMB instead of
                    wchar_t string.

               charset[l+m+6]  = equivalence_classes (wchar_t)
                              ...
               charset[l+m+n+5]  = equivalence_classes (wchar_t)
                    ifdef _LIBC we use the index in
                    _NL_COLLATE_WEIGHT instead of
                    wchar_t string.

           charset[l+m+n+6] = range_start
           charset[l+m+n+7] = range_end
                           ...
           charset[l+m+n+2o+4] = range_start
           charset[l+m+n+2o+5] = range_end
                    ifdef _LIBC we use the value looked up
                    in _NL_COLLATE_COLLSEQ instead of
                    wchar_t character.

           charset[l+m+n+2o+6] = char
                              ...
           charset[l+m+n+2o+p+5] = char

         */

        /* We need at least 6 spaces: the opcode, the length of
               char_classes, the length of collating_symbols, the length of
               equivalence_classes, the length of char_ranges, the length of
               chars.  */
        GET_BUFFER_SPACE (6);

        /* Save b as laststart. And We use laststart as the pointer
           to the first element of the charset here.
           In other words, laststart[i] indicates charset[i].  */
            laststart = b;

            /* We test `*p == '^' twice, instead of using an if
               statement, so we only need one BUF_PUSH.  */
            BUF_PUSH (*p == '^' ? charset_not : charset);
            if (*p == '^')
              p++;

            /* Push the length of char_classes, the length of
               collating_symbols, the length of equivalence_classes, the
               length of char_ranges and the length of chars.  */
            BUF_PUSH_3 (0, 0, 0);
            BUF_PUSH_2 (0, 0);

            /* Remember the first position in the bracket expression.  */
            p1 = p;

            /* charset_not matches newline according to a syntax bit.  */
            if ((re_opcode_t) b[-6] == charset_not
                && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
          {
        BUF_PUSH('\n');
        laststart[5]++; /* Update the length of characters  */
          }

            /* Read in characters and ranges, setting map bits.  */
            for (;;)
              {
                if (p == pend) FREE_STACK_RETURN (REG_EBRACK);

                PATFETCH (c);

                /* \ might escape characters inside [...] and [^...].  */
                if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
                  {
                    if (p == pend) FREE_STACK_RETURN (REG_EESCAPE);

                    PATFETCH (c1);
            BUF_PUSH(c1);
            laststart[5]++; /* Update the length of chars  */
            range_start = c1;
                    continue;
                  }

                /* Could be the end of the bracket expression.  If it's
                   not (i.e., when the bracket expression is `[]' so
                   far), the ']' character bit gets set way below.  */
                if (c == ']' && p != p1 + 1)
                  break;

                /* Look ahead to see if it's a range when the last thing
                   was a character class.  */
                if (had_char_class && c == '-' && *p != ']')
                  FREE_STACK_RETURN (REG_ERANGE);

                /* Look ahead to see if it's a range when the last thing
                   was a character: if this is a hyphen not at the
                   beginning or the end of a list, then it's the range
                   operator.  */
                if (c == '-'
                    && !(p - 2 >= pattern && p[-2] == '[')
                    && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
                    && *p != ']')
                  {
                    reg_errcode_t ret;
            /* Allocate the space for range_start and range_end.  */
            GET_BUFFER_SPACE (2);
            /* Update the pointer to indicate end of buffer.  */
                    b += 2;
                    ret = wcs_compile_range (range_start, &p, pend, translate,
                                         syntax, b, laststart);
                    if (ret != REG_NOERROR) FREE_STACK_RETURN (ret);
                    range_start = 0xffffffff;
                  }
                else if (p[0] == '-' && p[1] != ']')
                  { /* This handles ranges made up of characters only.  */
                    reg_errcode_t ret;

            /* Move past the `-'.  */
                    PATFETCH (c1);
            /* Allocate the space for range_start and range_end.  */
            GET_BUFFER_SPACE (2);
            /* Update the pointer to indicate end of buffer.  */
                    b += 2;
                    ret = wcs_compile_range (c, &p, pend, translate, syntax, b,
                                         laststart);
                    if (ret != REG_NOERROR) FREE_STACK_RETURN (ret);
            range_start = 0xffffffff;
                  }

                /* See if we're at the beginning of a possible character
                   class.  */
                else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
                  { /* Leave room for the null.  */
                    char str[CHAR_CLASS_MAX_LENGTH + 1];

                    PATFETCH (c);
                    c1 = 0;

                    /* If pattern is `[[:'.  */
                    if (p == pend) FREE_STACK_RETURN (REG_EBRACK);

                    for (;;)
                      {
                        PATFETCH (c);
                        if ((c == ':' && *p == ']') || p == pend)
                          break;
            if (c1 < CHAR_CLASS_MAX_LENGTH)
              str[c1++] = c;
            else
              /* This is in any case an invalid class name.  */
              str[0] = '\0';
                      }
                    str[c1] = '\0';

                    /* If isn't a word bracketed by `[:' and `:]':
                       undo the ending character, the letters, and leave
                       the leading `:' and `[' (but store them as character).  */
                    if (c == ':' && *p == ']')
                      {
            wctype_t wt;
            uintptr_t alignedp;

            /* Query the character class as wctype_t.  */
            wt = IS_CHAR_CLASS (str);
            if (wt == 0)
              FREE_STACK_RETURN (REG_ECTYPE);

                        /* Throw away the ] at the end of the character
                           class.  */
                        PATFETCH (c);

                        if (p == pend) FREE_STACK_RETURN (REG_EBRACK);

            /* Allocate the space for character class.  */
                        GET_BUFFER_SPACE(CHAR_CLASS_SIZE);
            /* Update the pointer to indicate end of buffer.  */
                        b += CHAR_CLASS_SIZE;
            /* Move data which follow character classes
                not to violate the data.  */
                        insert_space(CHAR_CLASS_SIZE,
                     laststart + 6 + laststart[1],
                     b - 1);
            alignedp = ((uintptr_t)(laststart + 6 + laststart[1])
                    + __alignof__(wctype_t) - 1)
                    & ~(uintptr_t)(__alignof__(wctype_t) - 1);
            /* Store the character class.  */
                        *((wctype_t*)alignedp) = wt;
                        /* Update length of char_classes */
                        laststart[1] += CHAR_CLASS_SIZE;

                        had_char_class = true;
                      }
                    else
                      {
                        c1++;
                        while (c1--)
                          PATUNFETCH;
                        BUF_PUSH ('[');
                        BUF_PUSH (':');
                        laststart[5] += 2; /* Update the length of characters  */
            range_start = ':';
                        had_char_class = false;
                      }
                  }
                else if (syntax & RE_CHAR_CLASSES && c == '[' && (*p == '='
                              || *p == '.'))
          {
            CHAR_T str[128];    /* Should be large enough.  */
            CHAR_T delim = *p; /* '=' or '.'  */
# ifdef _LIBC
            uint32_t nrules =
              _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
# endif
            PATFETCH (c);
            c1 = 0;

            /* If pattern is `[[=' or '[[.'.  */
            if (p == pend) FREE_STACK_RETURN (REG_EBRACK);

            for (;;)
              {
            PATFETCH (c);
            if ((c == delim && *p == ']') || p == pend)
              break;
            if (c1 < sizeof (str) - 1)
              str[c1++] = c;
            else
              /* This is in any case an invalid class name.  */
              str[0] = '\0';
                      }
            str[c1] = '\0';

            if (c == delim && *p == ']' && str[0] != '\0')
              {
                        unsigned int i, offset;
            /* If we have no collation data we use the default
               collation in which each character is in a class
               by itself.  It also means that ASCII is the
               character set and therefore we cannot have character
               with more than one byte in the multibyte
               representation.  */

                        /* If not defined _LIBC, we push the name and
               `\0' for the sake of matching performance.  */
            int datasize = c1 + 1;

# ifdef _LIBC
            int32_t idx = 0;
            if (nrules == 0)
# endif
              {
                if (c1 != 1)
                  FREE_STACK_RETURN (REG_ECOLLATE);
              }
# ifdef _LIBC
            else
              {
                const int32_t *table;
                const int32_t *weights;
                const int32_t *extra;
                const int32_t *indirect;
                wint_t *cp;

                /* This #include defines a local function!  */
#  include <locale/weightwc.h>

                if(delim == '=')
                  {
                /* We push the index for equivalence class.  */
                cp = (wint_t*)str;

                table = (const int32_t *)
                  _NL_CURRENT (LC_COLLATE,
                           _NL_COLLATE_TABLEWC);
                weights = (const int32_t *)
                  _NL_CURRENT (LC_COLLATE,
                           _NL_COLLATE_WEIGHTWC);
                extra = (const int32_t *)
                  _NL_CURRENT (LC_COLLATE,
                           _NL_COLLATE_EXTRAWC);
                indirect = (const int32_t *)
                  _NL_CURRENT (LC_COLLATE,
                           _NL_COLLATE_INDIRECTWC);

                idx = findidx ((const wint_t**)&cp);
                if (idx == 0 || cp < (wint_t*) str + c1)
                  /* This is no valid character.  */
                  FREE_STACK_RETURN (REG_ECOLLATE);

                str[0] = (wchar_t)idx;
                  }
                else /* delim == '.' */
                  {
                /* We push collation sequence value
                   for collating symbol.  */
                int32_t table_size;
                const int32_t *symb_table;
                const unsigned char *extra;
                int32_t idx;
                int32_t elem;
                int32_t second;
                int32_t hash;
                char char_str[c1];

                /* We have to convert the name to a single-byte
                   string.  This is possible since the names
                   consist of ASCII characters and the internal
                   representation is UCS4.  */
                for (i = 0; i < c1; ++i)
                  char_str[i] = str[i];

                table_size =
                  _NL_CURRENT_WORD (LC_COLLATE,
                            _NL_COLLATE_SYMB_HASH_SIZEMB);
                symb_table = (const int32_t *)
                  _NL_CURRENT (LC_COLLATE,
                           _NL_COLLATE_SYMB_TABLEMB);
                extra = (const unsigned char *)
                  _NL_CURRENT (LC_COLLATE,
                           _NL_COLLATE_SYMB_EXTRAMB);

                /* Locate the character in the hashing table.  */
                hash = elem_hash (char_str, c1);

                idx = 0;
                elem = hash % table_size;
                second = hash % (table_size - 2);
                while (symb_table[2 * elem] != 0)
                  {
                    /* First compare the hashing value.  */
                    if (symb_table[2 * elem] == hash
                    && c1 == extra[symb_table[2 * elem + 1]]
                    && memcmp (char_str,
                           &extra[symb_table[2 * elem + 1]
                             + 1], c1) == 0)
                      {
                    /* Yep, this is the entry.  */
                    idx = symb_table[2 * elem + 1];
                    idx += 1 + extra[idx];
                    break;
                      }

                    /* Next entry.  */
                    elem += second;
                  }

                if (symb_table[2 * elem] != 0)
                  {
                    /* Compute the index of the byte sequence
                       in the table.  */
                    idx += 1 + extra[idx];
                    /* Adjust for the alignment.  */
                    idx = (idx + 3) & ~3;

                    str[0] = (wchar_t) idx + 4;
                  }
                else if (symb_table[2 * elem] == 0 && c1 == 1)
                  {
                    /* No valid character.  Match it as a
                       single byte character.  */
                    had_char_class = false;
                    BUF_PUSH(str[0]);
                    /* Update the length of characters  */
                    laststart[5]++;
                    range_start = str[0];

                    /* Throw away the ] at the end of the
                       collating symbol.  */
                    PATFETCH (c);
                    /* exit from the switch block.  */
                    continue;
                  }
                else
                  FREE_STACK_RETURN (REG_ECOLLATE);
                  }
                datasize = 1;
              }
# endif
                        /* Throw away the ] at the end of the equivalence
                           class (or collating symbol).  */
                        PATFETCH (c);

            /* Allocate the space for the equivalence class
               (or collating symbol) (and '\0' if needed).  */
                        GET_BUFFER_SPACE(datasize);
            /* Update the pointer to indicate end of buffer.  */
                        b += datasize;

            if (delim == '=')
              { /* equivalence class  */
                /* Calculate the offset of char_ranges,
                   which is next to equivalence_classes.  */
                offset = laststart[1] + laststart[2]
                  + laststart[3] +6;
                /* Insert space.  */
                insert_space(datasize, laststart + offset, b - 1);

                /* Write the equivalence_class and \0.  */
                for (i = 0 ; i < datasize ; i++)
                  laststart[offset + i] = str[i];

                /* Update the length of equivalence_classes.  */
                laststart[3] += datasize;
                had_char_class = true;
              }
            else /* delim == '.' */
              { /* collating symbol  */
                /* Calculate the offset of the equivalence_classes,
                   which is next to collating_symbols.  */
                offset = laststart[1] + laststart[2] + 6;
                /* Insert space and write the collationg_symbol
                   and \0.  */
                insert_space(datasize, laststart + offset, b-1);
                for (i = 0 ; i < datasize ; i++)
                  laststart[offset + i] = str[i];

                /* In re_match_2_internal if range_start < -1, we
                   assume -range_start is the offset of the
                   collating symbol which is specified as
                   the character of the range start.  So we assign
                   -(laststart[1] + laststart[2] + 6) to
                   range_start.  */
                range_start = -(laststart[1] + laststart[2] + 6);
                /* Update the length of collating_symbol.  */
                laststart[2] += datasize;
                had_char_class = false;
              }
              }
                    else
                      {
                        c1++;
                        while (c1--)
                          PATUNFETCH;
                        BUF_PUSH ('[');
                        BUF_PUSH (delim);
                        laststart[5] += 2; /* Update the length of characters  */
            range_start = delim;
                        had_char_class = false;
                      }
          }
                else
                  {
                    had_char_class = false;
            BUF_PUSH(c);
            laststart[5]++;  /* Update the length of characters  */
            range_start = c;
                  }
          }

#else /* BYTE */
            /* Ensure that we have enough space to push a charset: the
               opcode, the length count, and the bitset; 34 bytes in all.  */
        GET_BUFFER_SPACE (34);

            laststart = b;

            /* We test `*p == '^' twice, instead of using an if
               statement, so we only need one BUF_PUSH.  */
            BUF_PUSH (*p == '^' ? charset_not : charset);
            if (*p == '^')
              p++;

            /* Remember the first position in the bracket expression.  */
            p1 = p;

            /* Push the number of bytes in the bitmap.  */
            BUF_PUSH ((1 << BYTEWIDTH) / BYTEWIDTH);

            /* Clear the whole map.  */
            bzero (b, (1 << BYTEWIDTH) / BYTEWIDTH);

            /* charset_not matches newline according to a syntax bit.  */
            if ((re_opcode_t) b[-2] == charset_not
                && (syntax & RE_HAT_LISTS_NOT_NEWLINE))
              SET_LIST_BIT ('\n');

            /* Read in characters and ranges, setting map bits.  */
            for (;;)
              {
                if (p == pend) FREE_STACK_RETURN (REG_EBRACK);

                PATFETCH (c);

                /* \ might escape characters inside [...] and [^...].  */
                if ((syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) && c == '\\')
                  {
                    if (p == pend) FREE_STACK_RETURN (REG_EESCAPE);

                    PATFETCH (c1);
                    SET_LIST_BIT (c1);
            range_start = c1;
                    continue;
                  }

                /* Could be the end of the bracket expression.  If it's
                   not (i.e., when the bracket expression is `[]' so
                   far), the ']' character bit gets set way below.  */
                if (c == ']' && p != p1 + 1)
                  break;

                /* Look ahead to see if it's a range when the last thing
                   was a character class.  */
                if (had_char_class && c == '-' && *p != ']')
                  FREE_STACK_RETURN (REG_ERANGE);

                /* Look ahead to see if it's a range when the last thing
                   was a character: if this is a hyphen not at the
                   beginning or the end of a list, then it's the range
                   operator.  */
                if (c == '-'
                    && !(p - 2 >= pattern && p[-2] == '[')
                    && !(p - 3 >= pattern && p[-3] == '[' && p[-2] == '^')
                    && *p != ']')
                  {
                    reg_errcode_t ret
                      = byte_compile_range (range_start, &p, pend, translate,
                        syntax, b);
                    if (ret != REG_NOERROR) FREE_STACK_RETURN (ret);
            range_start = 0xffffffff;
                  }

                else if (p[0] == '-' && p[1] != ']')
                  { /* This handles ranges made up of characters only.  */
                    reg_errcode_t ret;

            /* Move past the `-'.  */
                    PATFETCH (c1);

                    ret = byte_compile_range (c, &p, pend, translate, syntax, b);
                    if (ret != REG_NOERROR) FREE_STACK_RETURN (ret);
            range_start = 0xffffffff;
                  }

                /* See if we're at the beginning of a possible character
                   class.  */

                else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == ':')
                  { /* Leave room for the null.  */
                    char str[CHAR_CLASS_MAX_LENGTH + 1];

                    PATFETCH (c);
                    c1 = 0;

                    /* If pattern is `[[:'.  */
                    if (p == pend) FREE_STACK_RETURN (REG_EBRACK);

                    for (;;)
                      {
                        PATFETCH (c);
                        if ((c == ':' && *p == ']') || p == pend)
                          break;
            if (c1 < CHAR_CLASS_MAX_LENGTH)
              str[c1++] = c;
            else
              /* This is in any case an invalid class name.  */
              str[0] = '\0';
                      }
                    str[c1] = '\0';

                    /* If isn't a word bracketed by `[:' and `:]':
                       undo the ending character, the letters, and leave
                       the leading `:' and `[' (but set bits for them).  */
                    if (c == ':' && *p == ']')
                      {
# if defined _LIBC || WIDE_CHAR_SUPPORT
                        boolean is_lower = STREQ (str, "lower");
                        boolean is_upper = STREQ (str, "upper");
            wctype_t wt;
                        int ch;

            wt = IS_CHAR_CLASS (str);
            if (wt == 0)
              FREE_STACK_RETURN (REG_ECTYPE);

                        /* Throw away the ] at the end of the character
                           class.  */
                        PATFETCH (c);

                        if (p == pend) FREE_STACK_RETURN (REG_EBRACK);

                        for (ch = 0; ch < 1 << BYTEWIDTH; ++ch)
              {
                if (iswctype (btowc (ch), wt))
                  SET_LIST_BIT (ch);

                if (translate && (is_upper || is_lower)
                && (ISUPPER (ch) || ISLOWER (ch)))
                  SET_LIST_BIT (ch);
              }

                        had_char_class = true;
# else
                        int ch;
                        boolean is_alnum = STREQ (str, "alnum");
                        boolean is_alpha = STREQ (str, "alpha");
                        boolean is_blank = STREQ (str, "blank");
                        boolean is_cntrl = STREQ (str, "cntrl");
                        boolean is_digit = STREQ (str, "digit");
                        boolean is_graph = STREQ (str, "graph");
                        boolean is_lower = STREQ (str, "lower");
                        boolean is_print = STREQ (str, "print");
                        boolean is_punct = STREQ (str, "punct");
                        boolean is_space = STREQ (str, "space");
                        boolean is_upper = STREQ (str, "upper");
                        boolean is_xdigit = STREQ (str, "xdigit");

                        if (!IS_CHAR_CLASS (str))
              FREE_STACK_RETURN (REG_ECTYPE);

                        /* Throw away the ] at the end of the character
                           class.  */
                        PATFETCH (c);

                        if (p == pend) FREE_STACK_RETURN (REG_EBRACK);

                        for (ch = 0; ch < 1 << BYTEWIDTH; ch++)
                          {
                /* This was split into 3 if's to
                   avoid an arbitrary limit in some compiler.  */
                            if (   (is_alnum  && ISALNUM (ch))
                                || (is_alpha  && ISALPHA (ch))
                                || (is_blank  && ISBLANK (ch))
                                || (is_cntrl  && ISCNTRL (ch)))
                  SET_LIST_BIT (ch);
                if (   (is_digit  && ISDIGIT (ch))
                                || (is_graph  && ISGRAPH (ch))
                                || (is_lower  && ISLOWER (ch))
                                || (is_print  && ISPRINT (ch)))
                  SET_LIST_BIT (ch);
                if (   (is_punct  && ISPUNCT (ch))
                                || (is_space  && ISSPACE (ch))
                                || (is_upper  && ISUPPER (ch))
                                || (is_xdigit && ISXDIGIT (ch)))
                  SET_LIST_BIT (ch);
                if (   translate && (is_upper || is_lower)
                && (ISUPPER (ch) || ISLOWER (ch)))
                  SET_LIST_BIT (ch);
                          }
                        had_char_class = true;
# endif /* libc || wctype.h */
                      }
                    else
                      {
                        c1++;
                        while (c1--)
                          PATUNFETCH;
                        SET_LIST_BIT ('[');
                        SET_LIST_BIT (':');
            range_start = ':';
                        had_char_class = false;
                      }
                  }
                else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == '=')
          {
            unsigned char str[MB_LEN_MAX + 1];
# ifdef _LIBC
            uint32_t nrules =
              _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
# endif

            PATFETCH (c);
            c1 = 0;

            /* If pattern is `[[='.  */
            if (p == pend) FREE_STACK_RETURN (REG_EBRACK);

            for (;;)
              {
            PATFETCH (c);
            if ((c == '=' && *p == ']') || p == pend)
              break;
            if (c1 < MB_LEN_MAX)
              str[c1++] = c;
            else
              /* This is in any case an invalid class name.  */
              str[0] = '\0';
                      }
            str[c1] = '\0';

            if (c == '=' && *p == ']' && str[0] != '\0')
              {
            /* If we have no collation data we use the default
               collation in which each character is in a class
               by itself.  It also means that ASCII is the
               character set and therefore we cannot have character
               with more than one byte in the multibyte
               representation.  */
# ifdef _LIBC
            if (nrules == 0)
# endif
              {
                if (c1 != 1)
                  FREE_STACK_RETURN (REG_ECOLLATE);

                /* Throw away the ] at the end of the equivalence
                   class.  */
                PATFETCH (c);

                /* Set the bit for the character.  */
                SET_LIST_BIT (str[0]);
              }
# ifdef _LIBC
            else
              {
                /* Try to match the byte sequence in `str' against
                   those known to the collate implementation.
                   First find out whether the bytes in `str' are
                   actually from exactly one character.  */
                const int32_t *table;
                const unsigned char *weights;
                const unsigned char *extra;
                const int32_t *indirect;
                int32_t idx;
                const unsigned char *cp = str;
                int ch;

                /* This #include defines a local function!  */
#  include <locale/weight.h>

                table = (const int32_t *)
                  _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
                weights = (const unsigned char *)
                  _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
                extra = (const unsigned char *)
                  _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
                indirect = (const int32_t *)
                  _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);

                idx = findidx (&cp);
                if (idx == 0 || cp < str + c1)
                  /* This is no valid character.  */
                  FREE_STACK_RETURN (REG_ECOLLATE);

                /* Throw away the ] at the end of the equivalence
                   class.  */
                PATFETCH (c);

                /* Now we have to go throught the whole table
                   and find all characters which have the same
                   first level weight.

                   XXX Note that this is not entirely correct.
                   we would have to match multibyte sequences
                   but this is not possible with the current
                   implementation.  */
                for (ch = 1; ch < 256; ++ch)
                  /* XXX This test would have to be changed if we
                 would allow matching multibyte sequences.  */
                  if (table[ch] > 0)
                {
                  int32_t idx2 = table[ch];
                  size_t len = weights[idx2];

                  /* Test whether the lenghts match.  */
                  if (weights[idx] == len)
                    {
                      /* They do.  New compare the bytes of
                     the weight.  */
                      size_t cnt = 0;

                      while (cnt < len
                         && (weights[idx + 1 + cnt]
                         == weights[idx2 + 1 + cnt]))
                    ++cnt;

                      if (cnt == len)
                    /* They match.  Mark the character as
                       acceptable.  */
                    SET_LIST_BIT (ch);
                    }
                }
              }
# endif
            had_char_class = true;
              }
                    else
                      {
                        c1++;
                        while (c1--)
                          PATUNFETCH;
                        SET_LIST_BIT ('[');
                        SET_LIST_BIT ('=');
            range_start = '=';
                        had_char_class = false;
                      }
          }
                else if (syntax & RE_CHAR_CLASSES && c == '[' && *p == '.')
          {
            unsigned char str[128]; /* Should be large enough.  */
# ifdef _LIBC
            uint32_t nrules =
              _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
# endif

            PATFETCH (c);
            c1 = 0;

            /* If pattern is `[[.'.  */
            if (p == pend) FREE_STACK_RETURN (REG_EBRACK);

            for (;;)
              {
            PATFETCH (c);
            if ((c == '.' && *p == ']') || p == pend)
              break;
            if (c1 < sizeof (str))
              str[c1++] = c;
            else
              /* This is in any case an invalid class name.  */
              str[0] = '\0';
                      }
            str[c1] = '\0';

            if (c == '.' && *p == ']' && str[0] != '\0')
              {
            /* If we have no collation data we use the default
               collation in which each character is the name
               for its own class which contains only the one
               character.  It also means that ASCII is the
               character set and therefore we cannot have character
               with more than one byte in the multibyte
               representation.  */
# ifdef _LIBC
            if (nrules == 0)
# endif
              {
                if (c1 != 1)
                  FREE_STACK_RETURN (REG_ECOLLATE);

                /* Throw away the ] at the end of the equivalence
                   class.  */
                PATFETCH (c);

                /* Set the bit for the character.  */
                SET_LIST_BIT (str[0]);
                range_start = ((const unsigned char *) str)[0];
              }
# ifdef _LIBC
            else
              {
                /* Try to match the byte sequence in `str' against
                   those known to the collate implementation.
                   First find out whether the bytes in `str' are
                   actually from exactly one character.  */
                int32_t table_size;
                const int32_t *symb_table;
                const unsigned char *extra;
                int32_t idx;
                int32_t elem;
                int32_t second;
                int32_t hash;

                table_size =
                  _NL_CURRENT_WORD (LC_COLLATE,
                        _NL_COLLATE_SYMB_HASH_SIZEMB);
                symb_table = (const int32_t *)
                  _NL_CURRENT (LC_COLLATE,
                       _NL_COLLATE_SYMB_TABLEMB);
                extra = (const unsigned char *)
                  _NL_CURRENT (LC_COLLATE,
                       _NL_COLLATE_SYMB_EXTRAMB);

                /* Locate the character in the hashing table.  */
                hash = elem_hash (str, c1);

                idx = 0;
                elem = hash % table_size;
                second = hash % (table_size - 2);
                while (symb_table[2 * elem] != 0)
                  {
                /* First compare the hashing value.  */
                if (symb_table[2 * elem] == hash
                    && c1 == extra[symb_table[2 * elem + 1]]
                    && memcmp (str,
                           &extra[symb_table[2 * elem + 1]
                             + 1],
                           c1) == 0)
                  {
                    /* Yep, this is the entry.  */
                    idx = symb_table[2 * elem + 1];
                    idx += 1 + extra[idx];
                    break;
                  }

                /* Next entry.  */
                elem += second;
                  }

                if (symb_table[2 * elem] == 0)
                  /* This is no valid character.  */
                  FREE_STACK_RETURN (REG_ECOLLATE);

                /* Throw away the ] at the end of the equivalence
                   class.  */
                PATFETCH (c);

                /* Now add the multibyte character(s) we found
                   to the accept list.

                   XXX Note that this is not entirely correct.
                   we would have to match multibyte sequences
                   but this is not possible with the current
                   implementation.  Also, we have to match
                   collating symbols, which expand to more than
                   one file, as a whole and not allow the
                   individual bytes.  */
                c1 = extra[idx++];
                if (c1 == 1)
                  range_start = extra[idx];
                while (c1-- > 0)
                  {
                SET_LIST_BIT (extra[idx]);
                ++idx;
                  }
              }
# endif
            had_char_class = false;
              }
                    else
                      {
                        c1++;
                        while (c1--)
                          PATUNFETCH;
                        SET_LIST_BIT ('[');
                        SET_LIST_BIT ('.');
            range_start = '.';
                        had_char_class = false;
                      }
          }
                else
                  {
                    had_char_class = false;
                    SET_LIST_BIT (c);
            range_start = c;
                  }
              }

            /* Discard any (non)matching list bytes that are all 0 at the
               end of the map.  Decrease the map-length byte too.  */
            while ((int) b[-1] > 0 && b[b[-1] - 1] == 0)
              b[-1]--;
            b += b[-1];
#endif /* WCHAR */
          }
          break;


    case '(':
          if (syntax & RE_NO_BK_PARENS)
            goto handle_open;
          else
            goto normal_char;


        case ')':
          if (syntax & RE_NO_BK_PARENS)
            goto handle_close;
          else
            goto normal_char;


        case '\n':
          if (syntax & RE_NEWLINE_ALT)
            goto handle_alt;
          else
            goto normal_char;


    case '|':
          if (syntax & RE_NO_BK_VBAR)
            goto handle_alt;
          else
            goto normal_char;


        case '{':
           if (syntax & RE_INTERVALS && syntax & RE_NO_BK_BRACES)
             goto handle_interval;
           else
             goto normal_char;


        case '\\':
          if (p == pend) FREE_STACK_RETURN (REG_EESCAPE);

          /* Do not translate the character after the \, so that we can
             distinguish, e.g., \B from \b, even if we normally would
             translate, e.g., B to b.  */
          PATFETCH_RAW (c);

          switch (c)
            {
            case '(':
              if (syntax & RE_NO_BK_PARENS)
                goto normal_backslash;

            handle_open:
              bufp->re_nsub++;
              regnum++;

              if (COMPILE_STACK_FULL)
                {
                  RETALLOC (compile_stack.stack, compile_stack.size << 1,
                            compile_stack_elt_t);
                  if (compile_stack.stack == NULL) return REG_ESPACE;

                  compile_stack.size <<= 1;
                }

              /* These are the values to restore when we hit end of this
                 group.  They are all relative offsets, so that if the
                 whole pattern moves because of realloc, they will still
                 be valid.  */
              COMPILE_STACK_TOP.begalt_offset = begalt - COMPILED_BUFFER_VAR;
              COMPILE_STACK_TOP.fixup_alt_jump
                = fixup_alt_jump ? fixup_alt_jump - COMPILED_BUFFER_VAR + 1 : 0;
              COMPILE_STACK_TOP.laststart_offset = b - COMPILED_BUFFER_VAR;
              COMPILE_STACK_TOP.regnum = regnum;

              /* We will eventually replace the 0 with the number of
                 groups inner to this one.  But do not push a
                 start_memory for groups beyond the last one we can
                 represent in the compiled pattern.  */
              if (regnum <= MAX_REGNUM)
                {
                  COMPILE_STACK_TOP.inner_group_offset = b
            - COMPILED_BUFFER_VAR + 2;
                  BUF_PUSH_3 (start_memory, regnum, 0);
                }

              compile_stack.avail++;

              fixup_alt_jump = 0;
              laststart = 0;
              begalt = b;
          /* If we've reached MAX_REGNUM groups, then this open
         won't actually generate any code, so we'll have to
         clear pending_exact explicitly.  */
          pending_exact = 0;
              break;


            case ')':
              if (syntax & RE_NO_BK_PARENS) goto normal_backslash;

              if (COMPILE_STACK_EMPTY)
        {
          if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
            goto normal_backslash;
          else
            FREE_STACK_RETURN (REG_ERPAREN);
        }

            handle_close:
              if (fixup_alt_jump)
                { /* Push a dummy failure point at the end of the
                     alternative for a possible future
                     `pop_failure_jump' to pop.  See comments at
                     `push_dummy_failure' in `re_match_2'.  */
                  BUF_PUSH (push_dummy_failure);

                  /* We allocated space for this jump when we assigned
                     to `fixup_alt_jump', in the `handle_alt' case below.  */
                  STORE_JUMP (jump_past_alt, fixup_alt_jump, b - 1);
                }

              /* See similar code for backslashed left paren above.  */
              if (COMPILE_STACK_EMPTY)
        {
          if (syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)
            goto normal_char;
          else
            FREE_STACK_RETURN (REG_ERPAREN);
        }

              /* Since we just checked for an empty stack above, this
                 ``can't happen''.  */
              assert (compile_stack.avail != 0);
              {
                /* We don't just want to restore into `regnum', because
                   later groups should continue to be numbered higher,
                   as in `(ab)c(de)' -- the second group is #2.  */
                regnum_t this_group_regnum;

                compile_stack.avail--;
                begalt = COMPILED_BUFFER_VAR + COMPILE_STACK_TOP.begalt_offset;
                fixup_alt_jump
                  = COMPILE_STACK_TOP.fixup_alt_jump
                    ? COMPILED_BUFFER_VAR + COMPILE_STACK_TOP.fixup_alt_jump - 1
                    : 0;
                laststart = COMPILED_BUFFER_VAR + COMPILE_STACK_TOP.laststart_offset;
                this_group_regnum = COMPILE_STACK_TOP.regnum;
        /* If we've reached MAX_REGNUM groups, then this open
           won't actually generate any code, so we'll have to
           clear pending_exact explicitly.  */
        pending_exact = 0;

                /* We're at the end of the group, so now we know how many
                   groups were inside this one.  */
                if (this_group_regnum <= MAX_REGNUM)
                  {
            UCHAR_T *inner_group_loc
                      = COMPILED_BUFFER_VAR + COMPILE_STACK_TOP.inner_group_offset;

                    *inner_group_loc = regnum - this_group_regnum;
                    BUF_PUSH_3 (stop_memory, this_group_regnum,
                                regnum - this_group_regnum);
                  }
              }
              break;


            case '|':                   /* `\|'.  */
              if (syntax & RE_LIMITED_OPS || syntax & RE_NO_BK_VBAR)
                goto normal_backslash;
            handle_alt:
              if (syntax & RE_LIMITED_OPS)
                goto normal_char;

              /* Insert before the previous alternative a jump which
                 jumps to this alternative if the former fails.  */
              GET_BUFFER_SPACE (1 + OFFSET_ADDRESS_SIZE);
              INSERT_JUMP (on_failure_jump, begalt,
               b + 2 + 2 * OFFSET_ADDRESS_SIZE);
              pending_exact = 0;
              b += 1 + OFFSET_ADDRESS_SIZE;

              /* The alternative before this one has a jump after it
                 which gets executed if it gets matched.  Adjust that
                 jump so it will jump to this alternative's analogous
                 jump (put in below, which in turn will jump to the next
                 (if any) alternative's such jump, etc.).  The last such
                 jump jumps to the correct final destination.  A picture:
                          _____ _____
                          |   | |   |
                          |   v |   v
                         a | b   | c

                 If we are at `b', then fixup_alt_jump right now points to a
                 three-byte space after `a'.  We'll put in the jump, set
                 fixup_alt_jump to right after `b', and leave behind three
                 bytes which we'll fill in when we get to after `c'.  */

              if (fixup_alt_jump)
                STORE_JUMP (jump_past_alt, fixup_alt_jump, b);

              /* Mark and leave space for a jump after this alternative,
                 to be filled in later either by next alternative or
                 when know we're at the end of a series of alternatives.  */
              fixup_alt_jump = b;
              GET_BUFFER_SPACE (1 + OFFSET_ADDRESS_SIZE);
              b += 1 + OFFSET_ADDRESS_SIZE;

              laststart = 0;
              begalt = b;
              break;


            case '{':
              /* If \{ is a literal.  */
              if (!(syntax & RE_INTERVALS)
                     /* If we're at `\{' and it's not the open-interval
                        operator.  */
          || (syntax & RE_NO_BK_BRACES))
                goto normal_backslash;

            handle_interval:
              {
                /* If got here, then the syntax allows intervals.  */

                /* At least (most) this many matches must be made.  */
                int lower_bound = -1, upper_bound = -1;

        /* Place in the uncompiled pattern (i.e., just after
           the '{') to go back to if the interval is invalid.  */
        const CHAR_T *beg_interval = p;

                if (p == pend)
          goto invalid_interval;

                GET_UNSIGNED_NUMBER (lower_bound);

                if (c == ',')
                  {
                    GET_UNSIGNED_NUMBER (upper_bound);
            if (upper_bound < 0)
              upper_bound = RE_DUP_MAX;
                  }
                else
                  /* Interval such as `{1}' => match exactly once. */
                  upper_bound = lower_bound;

                if (! (0 <= lower_bound && lower_bound <= upper_bound))
          goto invalid_interval;

                if (!(syntax & RE_NO_BK_BRACES))
                  {
            if (c != '\\' || p == pend)
              goto invalid_interval;
                    PATFETCH (c);
                  }

                if (c != '}')
          goto invalid_interval;

                /* If it's invalid to have no preceding re.  */
                if (!laststart)
                  {
            if (syntax & RE_CONTEXT_INVALID_OPS
            && !(syntax & RE_INVALID_INTERVAL_ORD))
                      FREE_STACK_RETURN (REG_BADRPT);
                    else if (syntax & RE_CONTEXT_INDEP_OPS)
                      laststart = b;
                    else
                      goto unfetch_interval;
                  }

                /* We just parsed a valid interval.  */

                if (RE_DUP_MAX < upper_bound)
          FREE_STACK_RETURN (REG_BADBR);

                /* If the upper bound is zero, don't want to succeed at
                   all; jump from `laststart' to `b + 3', which will be
           the end of the buffer after we insert the jump.  */
        /* ifdef WCHAR, 'b + 1 + OFFSET_ADDRESS_SIZE'
           instead of 'b + 3'.  */
                 if (upper_bound == 0)
                   {
                     GET_BUFFER_SPACE (1 + OFFSET_ADDRESS_SIZE);
                     INSERT_JUMP (jump, laststart, b + 1
                  + OFFSET_ADDRESS_SIZE);
                     b += 1 + OFFSET_ADDRESS_SIZE;
                   }

                 /* Otherwise, we have a nontrivial interval.  When
                    we're all done, the pattern will look like:
                      set_number_at <jump count> <upper bound>
                      set_number_at <succeed_n count> <lower bound>
                      succeed_n <after jump addr> <succeed_n count>
                      <body of loop>
                      jump_n <succeed_n addr> <jump count>
                    (The upper bound and `jump_n' are omitted if
                    `upper_bound' is 1, though.)  */
                 else
                   { /* If the upper bound is > 1, we need to insert
                        more at the end of the loop.  */
                     unsigned nbytes = 2 + 4 * OFFSET_ADDRESS_SIZE +
               (upper_bound > 1) * (2 + 4 * OFFSET_ADDRESS_SIZE);

                     GET_BUFFER_SPACE (nbytes);

                     /* Initialize lower bound of the `succeed_n', even
                        though it will be set during matching by its
                        attendant `set_number_at' (inserted next),
                        because `re_compile_fastmap' needs to know.
                        Jump to the `jump_n' we might insert below.  */
                     INSERT_JUMP2 (succeed_n, laststart,
                                   b + 1 + 2 * OFFSET_ADDRESS_SIZE
                   + (upper_bound > 1) * (1 + 2 * OFFSET_ADDRESS_SIZE)
                   , lower_bound);
                     b += 1 + 2 * OFFSET_ADDRESS_SIZE;

                     /* Code to initialize the lower bound.  Insert
                        before the `succeed_n'.  The `5' is the last two
                        bytes of this `set_number_at', plus 3 bytes of
                        the following `succeed_n'.  */
             /* ifdef WCHAR, The '1+2*OFFSET_ADDRESS_SIZE'
            is the 'set_number_at', plus '1+OFFSET_ADDRESS_SIZE'
            of the following `succeed_n'.  */
                     PREFIX(insert_op2) (set_number_at, laststart, 1
                 + 2 * OFFSET_ADDRESS_SIZE, lower_bound, b);
                     b += 1 + 2 * OFFSET_ADDRESS_SIZE;

                     if (upper_bound > 1)
                       { /* More than one repetition is allowed, so
                            append a backward jump to the `succeed_n'
                            that starts this interval.

                            When we've reached this during matching,
                            we'll have matched the interval once, so
                            jump back only `upper_bound - 1' times.  */
                         STORE_JUMP2 (jump_n, b, laststart
                      + 2 * OFFSET_ADDRESS_SIZE + 1,
                                      upper_bound - 1);
                         b += 1 + 2 * OFFSET_ADDRESS_SIZE;

                         /* The location we want to set is the second
                            parameter of the `jump_n'; that is `b-2' as
                            an absolute address.  `laststart' will be
                            the `set_number_at' we're about to insert;
                            `laststart+3' the number to set, the source
                            for the relative address.  But we are
                            inserting into the middle of the pattern --
                            so everything is getting moved up by 5.
                            Conclusion: (b - 2) - (laststart + 3) + 5,
                            i.e., b - laststart.

                            We insert this at the beginning of the loop
                            so that if we fail during matching, we'll
                            reinitialize the bounds.  */
                         PREFIX(insert_op2) (set_number_at, laststart,
                         b - laststart,
                         upper_bound - 1, b);
                         b += 1 + 2 * OFFSET_ADDRESS_SIZE;
                       }
                   }
                pending_exact = 0;
        break;

          invalid_interval:
        if (!(syntax & RE_INVALID_INTERVAL_ORD))
          FREE_STACK_RETURN (p == pend ? REG_EBRACE : REG_BADBR);
          unfetch_interval:
        /* Match the characters as literals.  */
        p = beg_interval;
        c = '{';
        if (syntax & RE_NO_BK_BRACES)
          goto normal_char;
        else
          goto normal_backslash;
          }

#ifdef emacs
            /* There is no way to specify the before_dot and after_dot
               operators.  rms says this is ok.  --karl  */
            case '=':
              BUF_PUSH (at_dot);
              break;

            case 's':
              laststart = b;
              PATFETCH (c);
              BUF_PUSH_2 (syntaxspec, syntax_spec_code[c]);
              break;

            case 'S':
              laststart = b;
              PATFETCH (c);
              BUF_PUSH_2 (notsyntaxspec, syntax_spec_code[c]);
              break;
#endif /* emacs */


            case 'w':
          if (syntax & RE_NO_GNU_OPS)
        goto normal_char;
              laststart = b;
              BUF_PUSH (wordchar);
              break;


            case 'W':
          if (syntax & RE_NO_GNU_OPS)
        goto normal_char;
              laststart = b;
              BUF_PUSH (notwordchar);
              break;


            case '<':
          if (syntax & RE_NO_GNU_OPS)
        goto normal_char;
              BUF_PUSH (wordbeg);
              break;

            case '>':
          if (syntax & RE_NO_GNU_OPS)
        goto normal_char;
              BUF_PUSH (wordend);
              break;

            case 'b':
          if (syntax & RE_NO_GNU_OPS)
        goto normal_char;
              BUF_PUSH (wordbound);
              break;

            case 'B':
          if (syntax & RE_NO_GNU_OPS)
        goto normal_char;
              BUF_PUSH (notwordbound);
              break;

            case '`':
          if (syntax & RE_NO_GNU_OPS)
        goto normal_char;
              BUF_PUSH (begbuf);
              break;

            case '\'':
          if (syntax & RE_NO_GNU_OPS)
        goto normal_char;
              BUF_PUSH (endbuf);
              break;

            case '1': case '2': case '3': case '4': case '5':
            case '6': case '7': case '8': case '9':
              if (syntax & RE_NO_BK_REFS)
                goto normal_char;

              c1 = c - '0';

              if (c1 > regnum)
                FREE_STACK_RETURN (REG_ESUBREG);

              /* Can't back reference to a subexpression if inside of it.  */
              if (group_in_compile_stack (compile_stack, (regnum_t) c1))
                goto normal_char;

              laststart = b;
              BUF_PUSH_2 (duplicate, c1);
              break;


            case '+':
            case '?':
              if (syntax & RE_BK_PLUS_QM)
                goto handle_plus;
              else
                goto normal_backslash;

            default:
            normal_backslash:
              /* You might think it would be useful for \ to mean
                 not to translate; but if we don't translate it
                 it will never match anything.  */
              c = TRANSLATE (c);
              goto normal_char;
            }
          break;


    default:
        /* Expects the character in `c'.  */
    normal_char:
          /* If no exactn currently being built.  */
          if (!pending_exact
#ifdef WCHAR
          /* If last exactn handle binary(or character) and
         new exactn handle character(or binary).  */
          || is_exactn_bin != is_binary[p - 1 - pattern]
#endif /* WCHAR */

              /* If last exactn not at current position.  */
              || pending_exact + *pending_exact + 1 != b

              /* We have only one byte following the exactn for the count.  */
          || *pending_exact == (1 << BYTEWIDTH) - 1

              /* If followed by a repetition operator.  */
              || *p == '*' || *p == '^'
          || ((syntax & RE_BK_PLUS_QM)
          ? *p == '\\' && (p[1] == '+' || p[1] == '?')
          : (*p == '+' || *p == '?'))
          || ((syntax & RE_INTERVALS)
                  && ((syntax & RE_NO_BK_BRACES)
              ? *p == '{'
                      : (p[0] == '\\' && p[1] == '{'))))
        {
          /* Start building a new exactn.  */

              laststart = b;

#ifdef WCHAR
          /* Is this exactn binary data or character? */
          is_exactn_bin = is_binary[p - 1 - pattern];
          if (is_exactn_bin)
          BUF_PUSH_2 (exactn_bin, 0);
          else
          BUF_PUSH_2 (exactn, 0);
#else
          BUF_PUSH_2 (exactn, 0);
#endif /* WCHAR */
          pending_exact = b - 1;
            }

      BUF_PUSH (c);
          (*pending_exact)++;
      break;
        } /* switch (c) */
    } /* while p != pend */


  /* Through the pattern now.  */

  if (fixup_alt_jump)
    STORE_JUMP (jump_past_alt, fixup_alt_jump, b);

  if (!COMPILE_STACK_EMPTY)
    FREE_STACK_RETURN (REG_EPAREN);

  /* If we don't want backtracking, force success
     the first time we reach the end of the compiled pattern.  */
  if (syntax & RE_NO_POSIX_BACKTRACKING)
    BUF_PUSH (succeed);

#ifdef WCHAR
  free (pattern);
  free (mbs_offset);
  free (is_binary);
#endif
  free (compile_stack.stack);

  /* We have succeeded; set the length of the buffer.  */
#ifdef WCHAR
  bufp->used = (uintptr_t) b - (uintptr_t) COMPILED_BUFFER_VAR;
#else
  bufp->used = b - bufp->buffer;
#endif

#ifdef DEBUG
  if (debug)
    {
      DEBUG_PRINT1 ("\nCompiled pattern: \n");
      PREFIX(print_compiled_pattern) (bufp);
    }
#endif /* DEBUG */

#ifndef MATCH_MAY_ALLOCATE
  /* Initialize the failure stack to the largest possible stack.  This
     isn't necessary unless we're trying to avoid calling alloca in
     the search and match routines.  */
  {
    int num_regs = bufp->re_nsub + 1;

    /* Since DOUBLE_FAIL_STACK refuses to double only if the current size
       is strictly greater than re_max_failures, the largest possible stack
       is 2 * re_max_failures failure points.  */
    if (fail_stack.size < (2 * re_max_failures * MAX_FAILURE_ITEMS))
      {
    fail_stack.size = (2 * re_max_failures * MAX_FAILURE_ITEMS);

# ifdef emacs
    if (! fail_stack.stack)
      fail_stack.stack
        = (PREFIX(fail_stack_elt_t) *) xmalloc (fail_stack.size
                    * sizeof (PREFIX(fail_stack_elt_t)));
    else
      fail_stack.stack
        = (PREFIX(fail_stack_elt_t) *) xrealloc (fail_stack.stack,
                     (fail_stack.size
                      * sizeof (PREFIX(fail_stack_elt_t))));
# else /* not emacs */
    if (! fail_stack.stack)
      fail_stack.stack
        = (PREFIX(fail_stack_elt_t) *) malloc (fail_stack.size
                   * sizeof (PREFIX(fail_stack_elt_t)));
    else
      fail_stack.stack
        = (PREFIX(fail_stack_elt_t) *) realloc (fail_stack.stack,
                        (fail_stack.size
                     * sizeof (PREFIX(fail_stack_elt_t))));
# endif /* not emacs */
      }

   PREFIX(regex_grow_registers) (num_regs);
  }
#endif /* not MATCH_MAY_ALLOCATE */

  return REG_NOERROR;
} /* regex_compile */

/* Subroutines for `regex_compile'.  */

/* Store OP at LOC followed by two-byte integer parameter ARG.  */
/* ifdef WCHAR, integer parameter is 1 wchar_t.  */

static void
PREFIX(store_op1) (op, loc, arg)
    re_opcode_t op;
    UCHAR_T *loc;
    int arg;
{
  *loc = (UCHAR_T) op;
  STORE_NUMBER (loc + 1, arg);
}


/* Like `store_op1', but for two two-byte parameters ARG1 and ARG2.  */
/* ifdef WCHAR, integer parameter is 1 wchar_t.  */

static void
PREFIX(store_op2) (op, loc, arg1, arg2)
    re_opcode_t op;
    UCHAR_T *loc;
    int arg1, arg2;
{
  *loc = (UCHAR_T) op;
  STORE_NUMBER (loc + 1, arg1);
  STORE_NUMBER (loc + 1 + OFFSET_ADDRESS_SIZE, arg2);
}


/* Copy the bytes from LOC to END to open up three bytes of space at LOC
   for OP followed by two-byte integer parameter ARG.  */
/* ifdef WCHAR, integer parameter is 1 wchar_t.  */

static void
PREFIX(insert_op1) (op, loc, arg, end)
    re_opcode_t op;
    UCHAR_T *loc;
    int arg;
    UCHAR_T *end;
{
  register UCHAR_T *pfrom = end;
  register UCHAR_T *pto = end + 1 + OFFSET_ADDRESS_SIZE;

  while (pfrom != loc)
    *--pto = *--pfrom;

  PREFIX(store_op1) (op, loc, arg);
}


/* Like `insert_op1', but for two two-byte parameters ARG1 and ARG2.  */
/* ifdef WCHAR, integer parameter is 1 wchar_t.  */

static void
PREFIX(insert_op2) (op, loc, arg1, arg2, end)
    re_opcode_t op;
    UCHAR_T *loc;
    int arg1, arg2;
    UCHAR_T *end;
{
  register UCHAR_T *pfrom = end;
  register UCHAR_T *pto = end + 1 + 2 * OFFSET_ADDRESS_SIZE;

  while (pfrom != loc)
    *--pto = *--pfrom;

  PREFIX(store_op2) (op, loc, arg1, arg2);
}


/* P points to just after a ^ in PATTERN.  Return true if that ^ comes
   after an alternative or a begin-subexpression.  We assume there is at
   least one character before the ^.  */

static boolean
PREFIX(at_begline_loc_p) (pattern, p, syntax)
    const CHAR_T *pattern, *p;
    reg_syntax_t syntax;
{
  const CHAR_T *prev = p - 2;
  boolean prev_prev_backslash = prev > pattern && prev[-1] == '\\';

  return
       /* After a subexpression?  */
       (*prev == '(' && (syntax & RE_NO_BK_PARENS || prev_prev_backslash))
       /* After an alternative?  */
    || (*prev == '|' && (syntax & RE_NO_BK_VBAR || prev_prev_backslash));
}


/* The dual of at_begline_loc_p.  This one is for $.  We assume there is
   at least one character after the $, i.e., `P < PEND'.  */

static boolean
PREFIX(at_endline_loc_p) (p, pend, syntax)
    const CHAR_T *p, *pend;
    reg_syntax_t syntax;
{
  const CHAR_T *next = p;
  boolean next_backslash = *next == '\\';
  const CHAR_T *next_next = p + 1 < pend ? p + 1 : 0;

  return
       /* Before a subexpression?  */
       (syntax & RE_NO_BK_PARENS ? *next == ')'
        : next_backslash && next_next && *next_next == ')')
       /* Before an alternative?  */
    || (syntax & RE_NO_BK_VBAR ? *next == '|'
        : next_backslash && next_next && *next_next == '|');
}

#else /* not INSIDE_RECURSION */

/* Returns true if REGNUM is in one of COMPILE_STACK's elements and
   false if it's not.  */

static boolean
group_in_compile_stack (compile_stack, regnum)
    compile_stack_type compile_stack;
    regnum_t regnum;
{
  int this_element;

  for (this_element = compile_stack.avail - 1;
       this_element >= 0;
       this_element--)
    if (compile_stack.stack[this_element].regnum == regnum)
      return true;

  return false;
}
#endif /* not INSIDE_RECURSION */

#ifdef INSIDE_RECURSION

#ifdef WCHAR
/* This insert space, which size is "num", into the pattern at "loc".
   "end" must point the end of the allocated buffer.  */
static void
insert_space (num, loc, end)
     int num;
     CHAR_T *loc;
     CHAR_T *end;
{
  register CHAR_T *pto = end;
  register CHAR_T *pfrom = end - num;

  while (pfrom >= loc)
    *pto-- = *pfrom--;
}
#endif /* WCHAR */

#ifdef WCHAR
static reg_errcode_t
wcs_compile_range (range_start_char, p_ptr, pend, translate, syntax, b,
           char_set)
     CHAR_T range_start_char;
     const CHAR_T **p_ptr, *pend;
     CHAR_T *char_set, *b;
     RE_TRANSLATE_TYPE translate;
     reg_syntax_t syntax;
{
  const CHAR_T *p = *p_ptr;
  CHAR_T range_start, range_end;
  reg_errcode_t ret;
# ifdef _LIBC
  uint32_t nrules;
  uint32_t start_val, end_val;
# endif
  if (p == pend)
    return REG_ERANGE;

# ifdef _LIBC
  nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
  if (nrules != 0)
    {
      const char *collseq = (const char *) _NL_CURRENT(LC_COLLATE,
                               _NL_COLLATE_COLLSEQWC);
      const unsigned char *extra = (const unsigned char *)
    _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);

      if (range_start_char < -1)
    {
      /* range_start is a collating symbol.  */
      int32_t *wextra;
      /* Retreive the index and get collation sequence value.  */
      wextra = (int32_t*)(extra + char_set[-range_start_char]);
      start_val = wextra[1 + *wextra];
    }
      else
    start_val = collseq_table_lookup(collseq, TRANSLATE(range_start_char));

      end_val = collseq_table_lookup (collseq, TRANSLATE (p[0]));

      /* Report an error if the range is empty and the syntax prohibits
     this.  */
      ret = ((syntax & RE_NO_EMPTY_RANGES)
         && (start_val > end_val))? REG_ERANGE : REG_NOERROR;

      /* Insert space to the end of the char_ranges.  */
      insert_space(2, b - char_set[5] - 2, b - 1);
      *(b - char_set[5] - 2) = (wchar_t)start_val;
      *(b - char_set[5] - 1) = (wchar_t)end_val;
      char_set[4]++; /* ranges_index */
    }
  else
# endif
    {
      range_start = (range_start_char >= 0)? TRANSLATE (range_start_char):
    range_start_char;
      range_end = TRANSLATE (p[0]);
      /* Report an error if the range is empty and the syntax prohibits
     this.  */
      ret = ((syntax & RE_NO_EMPTY_RANGES)
         && (range_start > range_end))? REG_ERANGE : REG_NOERROR;

      /* Insert space to the end of the char_ranges.  */
      insert_space(2, b - char_set[5] - 2, b - 1);
      *(b - char_set[5] - 2) = range_start;
      *(b - char_set[5] - 1) = range_end;
      char_set[4]++; /* ranges_index */
    }
  /* Have to increment the pointer into the pattern string, so the
     caller isn't still at the ending character.  */
  (*p_ptr)++;

  return ret;
}
#else /* BYTE */
/* Read the ending character of a range (in a bracket expression) from the
   uncompiled pattern *P_PTR (which ends at PEND).  We assume the
   starting character is in `P[-2]'.  (`P[-1]' is the character `-'.)
   Then we set the translation of all bits between the starting and
   ending characters (inclusive) in the compiled pattern B.

   Return an error code.

   We use these short variable names so we can use the same macros as
   `regex_compile' itself.  */

static reg_errcode_t
byte_compile_range (range_start_char, p_ptr, pend, translate, syntax, b)
     unsigned int range_start_char;
     const char **p_ptr, *pend;
     RE_TRANSLATE_TYPE translate;
     reg_syntax_t syntax;
     unsigned char *b;
{
  unsigned this_char;
  const char *p = *p_ptr;
  reg_errcode_t ret;
# if _LIBC
  const unsigned char *collseq;
  unsigned int start_colseq;
  unsigned int end_colseq;
# else
  unsigned end_char;
# endif

  if (p == pend)
    return REG_ERANGE;

  /* Have to increment the pointer into the pattern string, so the
     caller isn't still at the ending character.  */
  (*p_ptr)++;

  /* Report an error if the range is empty and the syntax prohibits this.  */
  ret = syntax & RE_NO_EMPTY_RANGES ? REG_ERANGE : REG_NOERROR;

# if _LIBC
  collseq = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
                         _NL_COLLATE_COLLSEQMB);

  start_colseq = collseq[(unsigned char) TRANSLATE (range_start_char)];
  end_colseq = collseq[(unsigned char) TRANSLATE (p[0])];
  for (this_char = 0; this_char <= (unsigned char) -1; ++this_char)
    {
      unsigned int this_colseq = collseq[(unsigned char) TRANSLATE (this_char)];

      if (start_colseq <= this_colseq && this_colseq <= end_colseq)
    {
      SET_LIST_BIT (TRANSLATE (this_char));
      ret = REG_NOERROR;
    }
    }
# else
  /* Here we see why `this_char' has to be larger than an `unsigned
     char' -- we would otherwise go into an infinite loop, since all
     characters <= 0xff.  */
  range_start_char = TRANSLATE (range_start_char);
  /* TRANSLATE(p[0]) is casted to char (not unsigned char) in TRANSLATE,
     and some compilers cast it to int implicitly, so following for_loop
     may fall to (almost) infinite loop.
     e.g. If translate[p[0]] = 0xff, end_char may equals to 0xffffffff.
     To avoid this, we cast p[0] to unsigned int and truncate it.  */
  end_char = ((unsigned)TRANSLATE(p[0]) & ((1 << BYTEWIDTH) - 1));

  for (this_char = range_start_char; this_char <= end_char; ++this_char)
    {
      SET_LIST_BIT (TRANSLATE (this_char));
      ret = REG_NOERROR;
    }
# endif

  return ret;
}
#endif /* WCHAR */


/* re_compile_fastmap computes a ``fastmap'' for the compiled pattern in
   BUFP.  A fastmap records which of the (1 << BYTEWIDTH) possible
   characters can start a string that matches the pattern.  This fastmap
   is used by re_search to skip quickly over impossible starting points.

   The caller must supply the address of a (1 << BYTEWIDTH)-byte data
   area as BUFP->fastmap.

   We set the `fastmap', `fastmap_accurate', and `can_be_null' fields in
   the pattern buffer.

   Returns 0 if we succeed, -2 if an internal error.   */

#ifdef WCHAR
/* local function for re_compile_fastmap.
   truncate wchar_t character to char.  */
static unsigned char truncate_wchar (CHAR_T c);

static unsigned char
truncate_wchar (c)
     CHAR_T c;
{
  unsigned char buf[MB_CUR_MAX];
  mbstate_t state;
  int retval;
  memset (&state, '\0', sizeof (state));
  retval = wcrtomb (buf, c, &state);
  return retval > 0 ? buf[0] : (unsigned char) c;
}
#endif /* WCHAR */

static int
PREFIX(re_compile_fastmap) (bufp)
     struct re_pattern_buffer *bufp;
{
  int j, k;
#ifdef MATCH_MAY_ALLOCATE
  PREFIX(fail_stack_type) fail_stack;
#endif
#ifndef REGEX_MALLOC
  char *destination;
#endif

  register char *fastmap = bufp->fastmap;

#ifdef WCHAR
  /* We need to cast pattern to (wchar_t*), because we casted this compiled
     pattern to (char*) in regex_compile.  */
  UCHAR_T *pattern = (UCHAR_T*)bufp->buffer;
  register UCHAR_T *pend = (UCHAR_T*) (bufp->buffer + bufp->used);
#else /* BYTE */
  UCHAR_T *pattern = bufp->buffer;
  register UCHAR_T *pend = pattern + bufp->used;
#endif /* WCHAR */
  UCHAR_T *p = pattern;

#ifdef REL_ALLOC
  /* This holds the pointer to the failure stack, when
     it is allocated relocatably.  */
  fail_stack_elt_t *failure_stack_ptr;
#endif

  /* Assume that each path through the pattern can be null until
     proven otherwise.  We set this false at the bottom of switch
     statement, to which we get only if a particular path doesn't
     match the empty string.  */
  boolean path_can_be_null = true;

  /* We aren't doing a `succeed_n' to begin with.  */
  boolean succeed_n_p = false;

  assert (fastmap != NULL && p != NULL);

  INIT_FAIL_STACK ();
  bzero (fastmap, 1 << BYTEWIDTH);  /* Assume nothing's valid.  */
  bufp->fastmap_accurate = 1;       /* It will be when we're done.  */
  bufp->can_be_null = 0;

  while (1)
    {
      if (p == pend || *p == succeed)
    {
      /* We have reached the (effective) end of pattern.  */
      if (!FAIL_STACK_EMPTY ())
        {
          bufp->can_be_null |= path_can_be_null;

          /* Reset for next path.  */
          path_can_be_null = true;

          p = fail_stack.stack[--fail_stack.avail].pointer;

          continue;
        }
      else
        break;
    }

      /* We should never be about to go beyond the end of the pattern.  */
      assert (p < pend);

      switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++))
    {

        /* I guess the idea here is to simply not bother with a fastmap
           if a backreference is used, since it's too hard to figure out
           the fastmap for the corresponding group.  Setting
           `can_be_null' stops `re_search_2' from using the fastmap, so
           that is all we do.  */
    case duplicate:
      bufp->can_be_null = 1;
          goto done;


      /* Following are the cases which match a character.  These end
         with `break'.  */

#ifdef WCHAR
    case exactn:
          fastmap[truncate_wchar(p[1])] = 1;
      break;
#else /* BYTE */
    case exactn:
          fastmap[p[1]] = 1;
      break;
#endif /* WCHAR */
#ifdef MBS_SUPPORT
    case exactn_bin:
      fastmap[p[1]] = 1;
      break;
#endif

#ifdef WCHAR
        /* It is hard to distinguish fastmap from (multi byte) characters
           which depends on current locale.  */
        case charset:
    case charset_not:
    case wordchar:
    case notwordchar:
          bufp->can_be_null = 1;
          goto done;
#else /* BYTE */
        case charset:
          for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
        if (p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH)))
              fastmap[j] = 1;
      break;


    case charset_not:
      /* Chars beyond end of map must be allowed.  */
      for (j = *p * BYTEWIDTH; j < (1 << BYTEWIDTH); j++)
            fastmap[j] = 1;

      for (j = *p++ * BYTEWIDTH - 1; j >= 0; j--)
        if (!(p[j / BYTEWIDTH] & (1 << (j % BYTEWIDTH))))
              fastmap[j] = 1;
          break;


    case wordchar:
      for (j = 0; j < (1 << BYTEWIDTH); j++)
        if (SYNTAX (j) == Sword)
          fastmap[j] = 1;
      break;


    case notwordchar:
      for (j = 0; j < (1 << BYTEWIDTH); j++)
        if (SYNTAX (j) != Sword)
          fastmap[j] = 1;
      break;
#endif /* WCHAR */

        case anychar:
      {
        int fastmap_newline = fastmap['\n'];

        /* `.' matches anything ...  */
        for (j = 0; j < (1 << BYTEWIDTH); j++)
          fastmap[j] = 1;

        /* ... except perhaps newline.  */
        if (!(bufp->syntax & RE_DOT_NEWLINE))
          fastmap['\n'] = fastmap_newline;

        /* Return if we have already set `can_be_null'; if we have,
           then the fastmap is irrelevant.  Something's wrong here.  */
        else if (bufp->can_be_null)
          goto done;

        /* Otherwise, have to check alternative paths.  */
        break;
      }

#ifdef emacs
        case syntaxspec:
      k = *p++;
      for (j = 0; j < (1 << BYTEWIDTH); j++)
        if (SYNTAX (j) == (enum syntaxcode) k)
          fastmap[j] = 1;
      break;


    case notsyntaxspec:
      k = *p++;
      for (j = 0; j < (1 << BYTEWIDTH); j++)
        if (SYNTAX (j) != (enum syntaxcode) k)
          fastmap[j] = 1;
      break;


      /* All cases after this match the empty string.  These end with
         `continue'.  */


    case before_dot:
    case at_dot:
    case after_dot:
          continue;
#endif /* emacs */


        case no_op:
        case begline:
        case endline:
    case begbuf:
    case endbuf:
    case wordbound:
    case notwordbound:
    case wordbeg:
    case wordend:
        case push_dummy_failure:
          continue;


    case jump_n:
        case pop_failure_jump:
    case maybe_pop_jump:
    case jump:
        case jump_past_alt:
    case dummy_failure_jump:
          EXTRACT_NUMBER_AND_INCR (j, p);
      p += j;
      if (j > 0)
        continue;

          /* Jump backward implies we just went through the body of a
             loop and matched nothing.  Opcode jumped to should be
             `on_failure_jump' or `succeed_n'.  Just treat it like an
             ordinary jump.  For a * loop, it has pushed its failure
             point already; if so, discard that as redundant.  */
          if ((re_opcode_t) *p != on_failure_jump
          && (re_opcode_t) *p != succeed_n)
        continue;

          p++;
          EXTRACT_NUMBER_AND_INCR (j, p);
          p += j;

          /* If what's on the stack is where we are now, pop it.  */
          if (!FAIL_STACK_EMPTY ()
          && fail_stack.stack[fail_stack.avail - 1].pointer == p)
            fail_stack.avail--;

          continue;


        case on_failure_jump:
        case on_failure_keep_string_jump:
    handle_on_failure_jump:
          EXTRACT_NUMBER_AND_INCR (j, p);

          /* For some patterns, e.g., `(a?)?', `p+j' here points to the
             end of the pattern.  We don't want to push such a point,
             since when we restore it above, entering the switch will
             increment `p' past the end of the pattern.  We don't need
             to push such a point since we obviously won't find any more
             fastmap entries beyond `pend'.  Such a pattern can match
             the null string, though.  */
          if (p + j < pend)
            {
              if (!PUSH_PATTERN_OP (p + j, fail_stack))
        {
          RESET_FAIL_STACK ();
          return -2;
        }
            }
          else
            bufp->can_be_null = 1;

          if (succeed_n_p)
            {
              EXTRACT_NUMBER_AND_INCR (k, p);   /* Skip the n.  */
              succeed_n_p = false;
        }

          continue;


    case succeed_n:
          /* Get to the number of times to succeed.  */
          p += OFFSET_ADDRESS_SIZE;

          /* Increment p past the n for when k != 0.  */
          EXTRACT_NUMBER_AND_INCR (k, p);
          if (k == 0)
        {
              p -= 2 * OFFSET_ADDRESS_SIZE;
          succeed_n_p = true;  /* Spaghetti code alert.  */
              goto handle_on_failure_jump;
            }
          continue;


    case set_number_at:
          p += 2 * OFFSET_ADDRESS_SIZE;
          continue;


    case start_memory:
        case stop_memory:
      p += 2;
      continue;


    default:
          abort (); /* We have listed all the cases.  */
        } /* switch *p++ */

      /* Getting here means we have found the possible starting
         characters for one path of the pattern -- and that the empty
         string does not match.  We need not follow this path further.
         Instead, look at the next alternative (remembered on the
         stack), or quit if no more.  The test at the top of the loop
         does these things.  */
      path_can_be_null = false;
      p = pend;
    } /* while p */

  /* Set `can_be_null' for the last path (also the first path, if the
     pattern is empty).  */
  bufp->can_be_null |= path_can_be_null;

 done:
  RESET_FAIL_STACK ();
  return 0;
}

#else /* not INSIDE_RECURSION */

int
re_compile_fastmap (bufp)
     struct re_pattern_buffer *bufp;
{
# ifdef MBS_SUPPORT
  if (MB_CUR_MAX != 1)
    return wcs_re_compile_fastmap(bufp);
  else
# endif
    return byte_re_compile_fastmap(bufp);
} /* re_compile_fastmap */
#ifdef _LIBC
weak_alias (__re_compile_fastmap, re_compile_fastmap)
#endif



/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
   ENDS.  Subsequent matches using PATTERN_BUFFER and REGS will use
   this memory for recording register information.  STARTS and ENDS
   must be allocated using the malloc library routine, and must each
   be at least NUM_REGS * sizeof (regoff_t) bytes long.

   If NUM_REGS == 0, then subsequent matches should allocate their own
   register data.

   Unless this function is called, the first search or match using
   PATTERN_BUFFER will allocate its own register data, without
   freeing the old data.  */

void
re_set_registers (bufp, regs, num_regs, starts, ends)
    struct re_pattern_buffer *bufp;
    struct re_registers *regs;
    unsigned num_regs;
    regoff_t *starts, *ends;
{
  if (num_regs)
    {
      bufp->regs_allocated = REGS_REALLOCATE;
      regs->num_regs = num_regs;
      regs->start = starts;
      regs->end = ends;
    }
  else
    {
      bufp->regs_allocated = REGS_UNALLOCATED;
      regs->num_regs = 0;
      regs->start = regs->end = (regoff_t *) 0;
    }
}
#ifdef _LIBC
weak_alias (__re_set_registers, re_set_registers)
#endif


/* Searching routines.  */

/* Like re_search_2, below, but only one string is specified, and
   doesn't let you say where to stop matching.  */

int
re_search (bufp, string, size, startpos, range, regs)
     struct re_pattern_buffer *bufp;
     const char *string;
     int size, startpos, range;
     struct re_registers *regs;
{
  return re_search_2 (bufp, NULL, 0, string, size, startpos, range,
              regs, size);
}
#ifdef _LIBC
weak_alias (__re_search, re_search)
#endif


/* Using the compiled pattern in BUFP->buffer, first tries to match the
   virtual concatenation of STRING1 and STRING2, starting first at index
   STARTPOS, then at STARTPOS + 1, and so on.

   STRING1 and STRING2 have length SIZE1 and SIZE2, respectively.

   RANGE is how far to scan while trying to match.  RANGE = 0 means try
   only at STARTPOS; in general, the last start tried is STARTPOS +
   RANGE.

   In REGS, return the indices of the virtual concatenation of STRING1
   and STRING2 that matched the entire BUFP->buffer and its contained
   subexpressions.

   Do not consider matching one past the index STOP in the virtual
   concatenation of STRING1 and STRING2.

   We return either the position in the strings at which the match was
   found, -1 if no match, or -2 if error (such as failure
   stack overflow).  */

int
re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
     struct re_pattern_buffer *bufp;
     const char *string1, *string2;
     int size1, size2;
     int startpos;
     int range;
     struct re_registers *regs;
     int stop;
{
# ifdef MBS_SUPPORT
  if (MB_CUR_MAX != 1)
    return wcs_re_search_2 (bufp, string1, size1, string2, size2, startpos,
                range, regs, stop);
  else
# endif
    return byte_re_search_2 (bufp, string1, size1, string2, size2, startpos,
                 range, regs, stop);
} /* re_search_2 */
#ifdef _LIBC
weak_alias (__re_search_2, re_search_2)
#endif

#endif /* not INSIDE_RECURSION */

#ifdef INSIDE_RECURSION

#ifdef MATCH_MAY_ALLOCATE
# define FREE_VAR(var) if (var) REGEX_FREE (var); var = NULL
#else
# define FREE_VAR(var) if (var) free (var); var = NULL
#endif

#ifdef WCHAR
# define MAX_ALLOCA_SIZE    2000

# define FREE_WCS_BUFFERS() \
  do {                                        \
    if (size1 > MAX_ALLOCA_SIZE)                          \
      {                                       \
    free (wcs_string1);                           \
    free (mbs_offset1);                           \
      }                                       \
    else                                      \
      {                                       \
    FREE_VAR (wcs_string1);                           \
    FREE_VAR (mbs_offset1);                           \
      }                                       \
    if (size2 > MAX_ALLOCA_SIZE)                          \
      {                                       \
    free (wcs_string2);                           \
    free (mbs_offset2);                           \
      }                                       \
    else                                      \
      {                                       \
    FREE_VAR (wcs_string2);                           \
    FREE_VAR (mbs_offset2);                           \
      }                                       \
  } while (0)

#endif


static int
PREFIX(re_search_2) (bufp, string1, size1, string2, size2, startpos, range,
             regs, stop)
     struct re_pattern_buffer *bufp;
     const char *string1, *string2;
     int size1, size2;
     int startpos;
     int range;
     struct re_registers *regs;
     int stop;
{
  int val;
  register char *fastmap = bufp->fastmap;
  register RE_TRANSLATE_TYPE translate = bufp->translate;
  int total_size = size1 + size2;
  int endpos = startpos + range;
#ifdef WCHAR
  /* We need wchar_t* buffers correspond to cstring1, cstring2.  */
  wchar_t *wcs_string1 = NULL, *wcs_string2 = NULL;
  /* We need the size of wchar_t buffers correspond to csize1, csize2.  */
  int wcs_size1 = 0, wcs_size2 = 0;
  /* offset buffer for optimizatoin. See convert_mbs_to_wc.  */
  int *mbs_offset1 = NULL, *mbs_offset2 = NULL;
  /* They hold whether each wchar_t is binary data or not.  */
  char *is_binary = NULL;
#endif /* WCHAR */

  /* Check for out-of-range STARTPOS.  */
  if (startpos < 0 || startpos > total_size)
    return -1;

  /* Fix up RANGE if it might eventually take us outside
     the virtual concatenation of STRING1 and STRING2.
     Make sure we won't move STARTPOS below 0 or above TOTAL_SIZE.  */
  if (endpos < 0)
    range = 0 - startpos;
  else if (endpos > total_size)
    range = total_size - startpos;

  /* If the search isn't to be a backwards one, don't waste time in a
     search for a pattern that must be anchored.  */
  if (bufp->used > 0 && range > 0
      && ((re_opcode_t) bufp->buffer[0] == begbuf
      /* `begline' is like `begbuf' if it cannot match at newlines.  */
      || ((re_opcode_t) bufp->buffer[0] == begline
          && !bufp->newline_anchor)))
    {
      if (startpos > 0)
    return -1;
      else
    range = 1;
    }

#ifdef emacs
  /* In a forward search for something that starts with \=.
     don't keep searching past point.  */
  if (bufp->used > 0 && (re_opcode_t) bufp->buffer[0] == at_dot && range > 0)
    {
      range = PT - startpos;
      if (range <= 0)
    return -1;
    }
#endif /* emacs */

  /* Update the fastmap now if not correct already.  */
  if (fastmap && !bufp->fastmap_accurate)
    if (re_compile_fastmap (bufp) == -2)
      return -2;

#ifdef WCHAR
  /* Allocate wchar_t array for wcs_string1 and wcs_string2 and
     fill them with converted string.  */
  if (size1 != 0)
    {
      if (size1 > MAX_ALLOCA_SIZE)
    {
      wcs_string1 = TALLOC (size1 + 1, CHAR_T);
      mbs_offset1 = TALLOC (size1 + 1, int);
      is_binary = TALLOC (size1 + 1, char);
    }
      else
    {
      wcs_string1 = REGEX_TALLOC (size1 + 1, CHAR_T);
      mbs_offset1 = REGEX_TALLOC (size1 + 1, int);
      is_binary = REGEX_TALLOC (size1 + 1, char);
    }
      if (!wcs_string1 || !mbs_offset1 || !is_binary)
    {
      if (size1 > MAX_ALLOCA_SIZE)
        {
          free (wcs_string1);
          free (mbs_offset1);
          free (is_binary);
        }
      else
        {
          FREE_VAR (wcs_string1);
          FREE_VAR (mbs_offset1);
          FREE_VAR (is_binary);
        }
      return -2;
    }
      wcs_size1 = convert_mbs_to_wcs(wcs_string1, string1, size1,
                     mbs_offset1, is_binary);
      wcs_string1[wcs_size1] = L'\0'; /* for a sentinel  */
      if (size1 > MAX_ALLOCA_SIZE)
    free (is_binary);
      else
    FREE_VAR (is_binary);
    }
  if (size2 != 0)
    {
      if (size2 > MAX_ALLOCA_SIZE)
    {
      wcs_string2 = TALLOC (size2 + 1, CHAR_T);
      mbs_offset2 = TALLOC (size2 + 1, int);
      is_binary = TALLOC (size2 + 1, char);
    }
      else
    {
      wcs_string2 = REGEX_TALLOC (size2 + 1, CHAR_T);
      mbs_offset2 = REGEX_TALLOC (size2 + 1, int);
      is_binary = REGEX_TALLOC (size2 + 1, char);
    }
      if (!wcs_string2 || !mbs_offset2 || !is_binary)
    {
      FREE_WCS_BUFFERS ();
      if (size2 > MAX_ALLOCA_SIZE)
        free (is_binary);
      else
        FREE_VAR (is_binary);
      return -2;
    }
      wcs_size2 = convert_mbs_to_wcs(wcs_string2, string2, size2,
                     mbs_offset2, is_binary);
      wcs_string2[wcs_size2] = L'\0'; /* for a sentinel  */
      if (size2 > MAX_ALLOCA_SIZE)
    free (is_binary);
      else
    FREE_VAR (is_binary);
    }
#endif /* WCHAR */


  /* Loop through the string, looking for a place to start matching.  */
  for (;;)
    {
      /* If a fastmap is supplied, skip quickly over characters that
         cannot be the start of a match.  If the pattern can match the
         null string, however, we don't need to skip characters; we want
         the first null string.  */
      if (fastmap && startpos < total_size && !bufp->can_be_null)
    {
      if (range > 0)    /* Searching forwards.  */
        {
          register const char *d;
          register int lim = 0;
          int irange = range;

              if (startpos < size1 && startpos + range >= size1)
                lim = range - (size1 - startpos);

          d = (startpos >= size1 ? string2 - size1 : string1) + startpos;

              /* Written out as an if-else to avoid testing `translate'
                 inside the loop.  */
          if (translate)
                while (range > lim
                       && !fastmap[(unsigned char)
                   translate[(unsigned char) *d++]])
                  range--;
          else
                while (range > lim && !fastmap[(unsigned char) *d++])
                  range--;

          startpos += irange - range;
        }
      else              /* Searching backwards.  */
        {
          register CHAR_T c = (size1 == 0 || startpos >= size1
                      ? string2[startpos - size1]
                      : string1[startpos]);

          if (!fastmap[(unsigned char) TRANSLATE (c)])
        goto advance;
        }
    }

      /* If can't match the null string, and that's all we have left, fail.  */
      if (range >= 0 && startpos == total_size && fastmap
          && !bufp->can_be_null)
       {
#ifdef WCHAR
         FREE_WCS_BUFFERS ();
#endif
         return -1;
       }

#ifdef WCHAR
      val = wcs_re_match_2_internal (bufp, string1, size1, string2,
                     size2, startpos, regs, stop,
                     wcs_string1, wcs_size1,
                     wcs_string2, wcs_size2,
                     mbs_offset1, mbs_offset2);
#else /* BYTE */
      val = byte_re_match_2_internal (bufp, string1, size1, string2,
                      size2, startpos, regs, stop);
#endif /* BYTE */

#ifndef REGEX_MALLOC
# ifdef C_ALLOCA
      alloca (0);
# endif
#endif

      if (val >= 0)
    {
#ifdef WCHAR
      FREE_WCS_BUFFERS ();
#endif
      return startpos;
    }

      if (val == -2)
    {
#ifdef WCHAR
      FREE_WCS_BUFFERS ();
#endif
      return -2;
    }

    advance:
      if (!range)
        break;
      else if (range > 0)
        {
          range--;
          startpos++;
        }
      else
        {
          range++;
          startpos--;
        }
    }
#ifdef WCHAR
  FREE_WCS_BUFFERS ();
#endif
  return -1;
}

#ifdef WCHAR
/* This converts PTR, a pointer into one of the search wchar_t strings
   `string1' and `string2' into an multibyte string offset from the
   beginning of that string. We use mbs_offset to optimize.
   See convert_mbs_to_wcs.  */
# define POINTER_TO_OFFSET(ptr)                     \
  (FIRST_STRING_P (ptr)                         \
   ? ((regoff_t)(mbs_offset1 != NULL? mbs_offset1[(ptr)-string1] : 0))  \
   : ((regoff_t)((mbs_offset2 != NULL? mbs_offset2[(ptr)-string2] : 0)  \
         + csize1)))
#else /* BYTE */
/* This converts PTR, a pointer into one of the search strings `string1'
   and `string2' into an offset from the beginning of that string.  */
# define POINTER_TO_OFFSET(ptr)         \
  (FIRST_STRING_P (ptr)             \
   ? ((regoff_t) ((ptr) - string1))     \
   : ((regoff_t) ((ptr) - string2 + size1)))
#endif /* WCHAR */

/* Macros for dealing with the split strings in re_match_2.  */

#define MATCHING_IN_FIRST_STRING  (dend == end_match_1)

/* Call before fetching a character with *d.  This switches over to
   string2 if necessary.  */
#define PREFETCH()                          \
  while (d == dend)                             \
    {                                   \
      /* End of string2 => fail.  */                    \
      if (dend == end_match_2)                      \
        goto fail;                          \
      /* End of string1 => advance to string2.  */          \
      d = string2;                              \
      dend = end_match_2;                       \
    }

/* Test if at very beginning or at very end of the virtual concatenation
   of `string1' and `string2'.  If only one string, it's `string2'.  */
#define AT_STRINGS_BEG(d) ((d) == (size1 ? string1 : string2) || !size2)
#define AT_STRINGS_END(d) ((d) == end2)


/* Test if D points to a character which is word-constituent.  We have
   two special cases to check for: if past the end of string1, look at
   the first character in string2; and if before the beginning of
   string2, look at the last character in string1.  */
#ifdef WCHAR
/* Use internationalized API instead of SYNTAX.  */
# define WORDCHAR_P(d)                          \
  (iswalnum ((wint_t)((d) == end1 ? *string2                \
           : (d) == string2 - 1 ? *(end1 - 1) : *(d))) != 0     \
   || ((d) == end1 ? *string2                       \
       : (d) == string2 - 1 ? *(end1 - 1) : *(d)) == L'_')
#else /* BYTE */
# define WORDCHAR_P(d)                          \
  (SYNTAX ((d) == end1 ? *string2                   \
           : (d) == string2 - 1 ? *(end1 - 1) : *(d))           \
   == Sword)
#endif /* WCHAR */

/* Disabled due to a compiler bug -- see comment at case wordbound */
#if 0
/* Test if the character before D and the one at D differ with respect
   to being word-constituent.  */
#define AT_WORD_BOUNDARY(d)                     \
  (AT_STRINGS_BEG (d) || AT_STRINGS_END (d)             \
   || WORDCHAR_P (d - 1) != WORDCHAR_P (d))
#endif

/* Free everything we malloc.  */
#ifdef MATCH_MAY_ALLOCATE
# ifdef WCHAR
#  define FREE_VARIABLES()                      \
  do {                                  \
    REGEX_FREE_STACK (fail_stack.stack);                \
    FREE_VAR (regstart);                        \
    FREE_VAR (regend);                          \
    FREE_VAR (old_regstart);                        \
    FREE_VAR (old_regend);                      \
    FREE_VAR (best_regstart);                       \
    FREE_VAR (best_regend);                     \
    FREE_VAR (reg_info);                        \
    FREE_VAR (reg_dummy);                       \
    FREE_VAR (reg_info_dummy);                      \
    if (!cant_free_wcs_buf)                     \
      {                                 \
        FREE_VAR (string1);                     \
        FREE_VAR (string2);                     \
        FREE_VAR (mbs_offset1);                     \
        FREE_VAR (mbs_offset2);                     \
      }                                 \
  } while (0)
# else /* BYTE */
#  define FREE_VARIABLES()                      \
  do {                                  \
    REGEX_FREE_STACK (fail_stack.stack);                \
    FREE_VAR (regstart);                        \
    FREE_VAR (regend);                          \
    FREE_VAR (old_regstart);                        \
    FREE_VAR (old_regend);                      \
    FREE_VAR (best_regstart);                       \
    FREE_VAR (best_regend);                     \
    FREE_VAR (reg_info);                        \
    FREE_VAR (reg_dummy);                       \
    FREE_VAR (reg_info_dummy);                      \
  } while (0)
# endif /* WCHAR */
#else
# ifdef WCHAR
#  define FREE_VARIABLES()                      \
  do {                                  \
    if (!cant_free_wcs_buf)                     \
      {                                 \
        FREE_VAR (string1);                     \
        FREE_VAR (string2);                     \
        FREE_VAR (mbs_offset1);                     \
        FREE_VAR (mbs_offset2);                     \
      }                                 \
  } while (0)
# else /* BYTE */
#  define FREE_VARIABLES() ((void)0) /* Do nothing!  But inhibit gcc warning. */
# endif /* WCHAR */
#endif /* not MATCH_MAY_ALLOCATE */

/* These values must meet several constraints.  They must not be valid
   register values; since we have a limit of 255 registers (because
   we use only one byte in the pattern for the register number), we can
   use numbers larger than 255.  They must differ by 1, because of
   NUM_FAILURE_ITEMS above.  And the value for the lowest register must
   be larger than the value for the highest register, so we do not try
   to actually save any registers when none are active.  */
#define NO_HIGHEST_ACTIVE_REG (1 << BYTEWIDTH)
#define NO_LOWEST_ACTIVE_REG (NO_HIGHEST_ACTIVE_REG + 1)


#else /* not INSIDE_RECURSION */
/* Matching routines.  */

#ifndef emacs   /* Emacs never uses this.  */
/* re_match is like re_match_2 except it takes only a single string.  */

int
re_match (bufp, string, size, pos, regs)
     struct re_pattern_buffer *bufp;
     const char *string;
     int size, pos;
     struct re_registers *regs;
{
  int result;
# ifdef MBS_SUPPORT
  if (MB_CUR_MAX != 1)
    result = wcs_re_match_2_internal (bufp, NULL, 0, string, size,
                      pos, regs, size,
                      NULL, 0, NULL, 0, NULL, NULL);
  else
# endif
    result = byte_re_match_2_internal (bufp, NULL, 0, string, size,
                  pos, regs, size);
# ifndef REGEX_MALLOC
#  ifdef C_ALLOCA
  alloca (0);
#  endif
# endif
  return result;
}
# ifdef _LIBC
weak_alias (__re_match, re_match)
# endif
#endif /* not emacs */

#endif /* not INSIDE_RECURSION */

#ifdef INSIDE_RECURSION
static boolean PREFIX(group_match_null_string_p) _RE_ARGS ((UCHAR_T **p,
                            UCHAR_T *end,
                    PREFIX(register_info_type) *reg_info));
static boolean PREFIX(alt_match_null_string_p) _RE_ARGS ((UCHAR_T *p,
                          UCHAR_T *end,
                    PREFIX(register_info_type) *reg_info));
static boolean PREFIX(common_op_match_null_string_p) _RE_ARGS ((UCHAR_T **p,
                            UCHAR_T *end,
                    PREFIX(register_info_type) *reg_info));
static int PREFIX(bcmp_translate) _RE_ARGS ((const CHAR_T *s1, const CHAR_T *s2,
                     int len, char *translate));
#else /* not INSIDE_RECURSION */

/* re_match_2 matches the compiled pattern in BUFP against the
   the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
   and SIZE2, respectively).  We start matching at POS, and stop
   matching at STOP.

   If REGS is non-null and the `no_sub' field of BUFP is nonzero, we
   store offsets for the substring each group matched in REGS.  See the
   documentation for exactly how many groups we fill.

   We return -1 if no match, -2 if an internal error (such as the
   failure stack overflowing).  Otherwise, we return the length of the
   matched substring.  */

int
re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
     struct re_pattern_buffer *bufp;
     const char *string1, *string2;
     int size1, size2;
     int pos;
     struct re_registers *regs;
     int stop;
{
  int result;
# ifdef MBS_SUPPORT
  if (MB_CUR_MAX != 1)
    result = wcs_re_match_2_internal (bufp, string1, size1, string2, size2,
                      pos, regs, stop,
                      NULL, 0, NULL, 0, NULL, NULL);
  else
# endif
    result = byte_re_match_2_internal (bufp, string1, size1, string2, size2,
                  pos, regs, stop);

#ifndef REGEX_MALLOC
# ifdef C_ALLOCA
  alloca (0);
# endif
#endif
  return result;
}
#ifdef _LIBC
weak_alias (__re_match_2, re_match_2)
#endif

#endif /* not INSIDE_RECURSION */

#ifdef INSIDE_RECURSION

#ifdef WCHAR
static int count_mbs_length PARAMS ((int *, int));

/* This check the substring (from 0, to length) of the multibyte string,
   to which offset_buffer correspond. And count how many wchar_t_characters
   the substring occupy. We use offset_buffer to optimization.
   See convert_mbs_to_wcs.  */

static int
count_mbs_length(offset_buffer, length)
     int *offset_buffer;
     int length;
{
  int upper, lower;

  /* Check whether the size is valid.  */
  if (length < 0)
    return -1;

  if (offset_buffer == NULL)
    return 0;

  /* If there are no multibyte character, offset_buffer[i] == i.
   Optmize for this case.  */
  if (offset_buffer[length] == length)
    return length;

  /* Set up upper with length. (because for all i, offset_buffer[i] >= i)  */
  upper = length;
  lower = 0;

  while (true)
    {
      int middle = (lower + upper) / 2;
      if (middle == lower || middle == upper)
    break;
      if (offset_buffer[middle] > length)
    upper = middle;
      else if (offset_buffer[middle] < length)
    lower = middle;
      else
    return middle;
    }

  return -1;
}
#endif /* WCHAR */

/* This is a separate function so that we can force an alloca cleanup
   afterwards.  */
#ifdef WCHAR
static int
wcs_re_match_2_internal (bufp, cstring1, csize1, cstring2, csize2, pos,
             regs, stop, string1, size1, string2, size2,
             mbs_offset1, mbs_offset2)
     struct re_pattern_buffer *bufp;
     const char *cstring1, *cstring2;
     int csize1, csize2;
     int pos;
     struct re_registers *regs;
     int stop;
     /* string1 == string2 == NULL means string1/2, size1/2 and
    mbs_offset1/2 need seting up in this function.  */
     /* We need wchar_t* buffers correspond to cstring1, cstring2.  */
     wchar_t *string1, *string2;
     /* We need the size of wchar_t buffers correspond to csize1, csize2.  */
     int size1, size2;
     /* offset buffer for optimizatoin. See convert_mbs_to_wc.  */
     int *mbs_offset1, *mbs_offset2;
#else /* BYTE */
static int
byte_re_match_2_internal (bufp, string1, size1,string2, size2, pos,
              regs, stop)
     struct re_pattern_buffer *bufp;
     const char *string1, *string2;
     int size1, size2;
     int pos;
     struct re_registers *regs;
     int stop;
#endif /* BYTE */
{
  /* General temporaries.  */
  int mcnt;
  UCHAR_T *p1;
#ifdef WCHAR
  /* They hold whether each wchar_t is binary data or not.  */
  char *is_binary = NULL;
  /* If true, we can't free string1/2, mbs_offset1/2.  */
  int cant_free_wcs_buf = 1;
#endif /* WCHAR */

  /* Just past the end of the corresponding string.  */
  const CHAR_T *end1, *end2;

  /* Pointers into string1 and string2, just past the last characters in
     each to consider matching.  */
  const CHAR_T *end_match_1, *end_match_2;

  /* Where we are in the data, and the end of the current string.  */
  const CHAR_T *d, *dend;

  /* Where we are in the pattern, and the end of the pattern.  */
#ifdef WCHAR
  UCHAR_T *pattern, *p;
  register UCHAR_T *pend;
#else /* BYTE */
  UCHAR_T *p = bufp->buffer;
  register UCHAR_T *pend = p + bufp->used;
#endif /* WCHAR */

  /* Mark the opcode just after a start_memory, so we can test for an
     empty subpattern when we get to the stop_memory.  */
  UCHAR_T *just_past_start_mem = 0;

  /* We use this to map every character in the string.  */
  RE_TRANSLATE_TYPE translate = bufp->translate;

  /* Failure point stack.  Each place that can handle a failure further
     down the line pushes a failure point on this stack.  It consists of
     restart, regend, and reg_info for all registers corresponding to
     the subexpressions we're currently inside, plus the number of such
     registers, and, finally, two char *'s.  The first char * is where
     to resume scanning the pattern; the second one is where to resume
     scanning the strings.  If the latter is zero, the failure point is
     a ``dummy''; if a failure happens and the failure point is a dummy,
     it gets discarded and the next next one is tried.  */
#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global.  */
  PREFIX(fail_stack_type) fail_stack;
#endif
#ifdef DEBUG
  static unsigned failure_id;
  unsigned nfailure_points_pushed = 0, nfailure_points_popped = 0;
#endif

#ifdef REL_ALLOC
  /* This holds the pointer to the failure stack, when
     it is allocated relocatably.  */
  fail_stack_elt_t *failure_stack_ptr;
#endif

  /* We fill all the registers internally, independent of what we
     return, for use in backreferences.  The number here includes
     an element for register zero.  */
  size_t num_regs = bufp->re_nsub + 1;

  /* The currently active registers.  */
  active_reg_t lowest_active_reg = NO_LOWEST_ACTIVE_REG;
  active_reg_t highest_active_reg = NO_HIGHEST_ACTIVE_REG;

  /* Information on the contents of registers. These are pointers into
     the input strings; they record just what was matched (on this
     attempt) by a subexpression part of the pattern, that is, the
     regnum-th regstart pointer points to where in the pattern we began
     matching and the regnum-th regend points to right after where we
     stopped matching the regnum-th subexpression.  (The zeroth register
     keeps track of what the whole pattern matches.)  */
#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
  const CHAR_T **regstart, **regend;
#endif

  /* If a group that's operated upon by a repetition operator fails to
     match anything, then the register for its start will need to be
     restored because it will have been set to wherever in the string we
     are when we last see its open-group operator.  Similarly for a
     register's end.  */
#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
  const CHAR_T **old_regstart, **old_regend;
#endif

  /* The is_active field of reg_info helps us keep track of which (possibly
     nested) subexpressions we are currently in. The matched_something
     field of reg_info[reg_num] helps us tell whether or not we have
     matched any of the pattern so far this time through the reg_num-th
     subexpression.  These two fields get reset each time through any
     loop their register is in.  */
#ifdef MATCH_MAY_ALLOCATE /* otherwise, this is global.  */
  PREFIX(register_info_type) *reg_info;
#endif

  /* The following record the register info as found in the above
     variables when we find a match better than any we've seen before.
     This happens as we backtrack through the failure points, which in
     turn happens only if we have not yet matched the entire string. */
  unsigned best_regs_set = false;
#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
  const CHAR_T **best_regstart, **best_regend;
#endif

  /* Logically, this is `best_regend[0]'.  But we don't want to have to
     allocate space for that if we're not allocating space for anything
     else (see below).  Also, we never need info about register 0 for
     any of the other register vectors, and it seems rather a kludge to
     treat `best_regend' differently than the rest.  So we keep track of
     the end of the best match so far in a separate variable.  We
     initialize this to NULL so that when we backtrack the first time
     and need to test it, it's not garbage.  */
  const CHAR_T *match_end = NULL;

  /* This helps SET_REGS_MATCHED avoid doing redundant work.  */
  int set_regs_matched_done = 0;

  /* Used when we pop values we don't care about.  */
#ifdef MATCH_MAY_ALLOCATE /* otherwise, these are global.  */
  const CHAR_T **reg_dummy;
  PREFIX(register_info_type) *reg_info_dummy;
#endif

#ifdef DEBUG
  /* Counts the total number of registers pushed.  */
  unsigned num_regs_pushed = 0;
#endif

  /* Definitions for state transitions.  More efficiently for gcc.  */
#ifdef __GNUC__
# if defined HAVE_SUBTRACT_LOCAL_LABELS && defined SHARED
#  define NEXT \
      do                                      \
    {                                     \
      int offset;                                 \
      const void *__unbounded ptr;                        \
      offset = (p == pend                             \
            ? 0 : jmptable[SWITCH_ENUM_CAST ((re_opcode_t) *p++)]);   \
      ptr = &&end_of_pattern + offset;                    \
      goto *ptr;                                  \
    }                                     \
      while (0)
#  define REF(x) \
  &&label_##x - &&end_of_pattern
#  define JUMP_TABLE_TYPE const int
# else
#  define NEXT \
      do                                      \
    {                                     \
      const void *__unbounded ptr;                        \
      ptr = (p == pend ? &&end_of_pattern                     \
         : jmptable[SWITCH_ENUM_CAST ((re_opcode_t) *p++)]);          \
      goto *ptr;                                  \
    }                                     \
      while (0)
#  define REF(x) \
  &&label_##x
#  define JUMP_TABLE_TYPE const void *const
# endif
# define CASE(x) label_##x
  static JUMP_TABLE_TYPE jmptable[] =
    {
    REF (no_op),
    REF (succeed),
    REF (exactn),
# ifdef MBS_SUPPORT
    REF (exactn_bin),
# endif
    REF (anychar),
    REF (charset),
    REF (charset_not),
    REF (start_memory),
    REF (stop_memory),
    REF (duplicate),
    REF (begline),
    REF (endline),
    REF (begbuf),
    REF (endbuf),
    REF (jump),
    REF (jump_past_alt),
    REF (on_failure_jump),
    REF (on_failure_keep_string_jump),
    REF (pop_failure_jump),
    REF (maybe_pop_jump),
    REF (dummy_failure_jump),
    REF (push_dummy_failure),
    REF (succeed_n),
    REF (jump_n),
    REF (set_number_at),
    REF (wordchar),
    REF (notwordchar),
    REF (wordbeg),
    REF (wordend),
    REF (wordbound),
    REF (notwordbound)
# ifdef emacs
    ,REF (before_dot),
    REF (at_dot),
    REF (after_dot),
    REF (syntaxspec),
    REF (notsyntaxspec)
# endif
    };
#else
# define NEXT \
  break
# define CASE(x) \
  case x
#endif

  DEBUG_PRINT1 ("\n\nEntering re_match_2.\n");

  INIT_FAIL_STACK ();

#ifdef MATCH_MAY_ALLOCATE
  /* Do not bother to initialize all the register variables if there are
     no groups in the pattern, as it takes a fair amount of time.  If
     there are groups, we include space for register 0 (the whole
     pattern), even though we never use it, since it simplifies the
     array indexing.  We should fix this.  */
  if (bufp->re_nsub)
    {
      regstart = REGEX_TALLOC (num_regs, const CHAR_T *);
      regend = REGEX_TALLOC (num_regs, const CHAR_T *);
      old_regstart = REGEX_TALLOC (num_regs, const CHAR_T *);
      old_regend = REGEX_TALLOC (num_regs, const CHAR_T *);
      best_regstart = REGEX_TALLOC (num_regs, const CHAR_T *);
      best_regend = REGEX_TALLOC (num_regs, const CHAR_T *);
      reg_info = REGEX_TALLOC (num_regs, PREFIX(register_info_type));
      reg_dummy = REGEX_TALLOC (num_regs, const CHAR_T *);
      reg_info_dummy = REGEX_TALLOC (num_regs, PREFIX(register_info_type));

      if (!(regstart && regend && old_regstart && old_regend && reg_info
            && best_regstart && best_regend && reg_dummy && reg_info_dummy))
        {
          FREE_VARIABLES ();
          return -2;
        }
    }
  else
    {
      /* We must initialize all our variables to NULL, so that
         `FREE_VARIABLES' doesn't try to free them.  */
      regstart = regend = old_regstart = old_regend = best_regstart
        = best_regend = reg_dummy = NULL;
      reg_info = reg_info_dummy = (PREFIX(register_info_type) *) NULL;
    }
#endif /* MATCH_MAY_ALLOCATE */

  /* The starting position is bogus.  */
#ifdef WCHAR
  if (pos < 0 || pos > csize1 + csize2)
#else /* BYTE */
  if (pos < 0 || pos > size1 + size2)
#endif
    {
      FREE_VARIABLES ();
      return -1;
    }

#ifdef WCHAR
  /* Allocate wchar_t array for string1 and string2 and
     fill them with converted string.  */
  if (string1 == NULL && string2 == NULL)
    {
      /* We need seting up buffers here.  */

      /* We must free wcs buffers in this function.  */
      cant_free_wcs_buf = 0;

      if (csize1 != 0)
    {
      string1 = REGEX_TALLOC (csize1 + 1, CHAR_T);
      mbs_offset1 = REGEX_TALLOC (csize1 + 1, int);
      is_binary = REGEX_TALLOC (csize1 + 1, char);
      if (!string1 || !mbs_offset1 || !is_binary)
        {
          FREE_VAR (string1);
          FREE_VAR (mbs_offset1);
          FREE_VAR (is_binary);
          return -2;
        }
    }
      if (csize2 != 0)
    {
      string2 = REGEX_TALLOC (csize2 + 1, CHAR_T);
      mbs_offset2 = REGEX_TALLOC (csize2 + 1, int);
      is_binary = REGEX_TALLOC (csize2 + 1, char);
      if (!string2 || !mbs_offset2 || !is_binary)
        {
          FREE_VAR (string1);
          FREE_VAR (mbs_offset1);
          FREE_VAR (string2);
          FREE_VAR (mbs_offset2);
          FREE_VAR (is_binary);
          return -2;
        }
      size2 = convert_mbs_to_wcs(string2, cstring2, csize2,
                     mbs_offset2, is_binary);
      string2[size2] = L'\0'; /* for a sentinel  */
      FREE_VAR (is_binary);
    }
    }

  /* We need to cast pattern to (wchar_t*), because we casted this compiled
     pattern to (char*) in regex_compile.  */
  p = pattern = (CHAR_T*)bufp->buffer;
  pend = (CHAR_T*)(bufp->buffer + bufp->used);

#endif /* WCHAR */

  /* Initialize subexpression text positions to -1 to mark ones that no
     start_memory/stop_memory has been seen for. Also initialize the
     register information struct.  */
  for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++)
    {
      regstart[mcnt] = regend[mcnt]
        = old_regstart[mcnt] = old_regend[mcnt] = REG_UNSET_VALUE;

      REG_MATCH_NULL_STRING_P (reg_info[mcnt]) = MATCH_NULL_UNSET_VALUE;
      IS_ACTIVE (reg_info[mcnt]) = 0;
      MATCHED_SOMETHING (reg_info[mcnt]) = 0;
      EVER_MATCHED_SOMETHING (reg_info[mcnt]) = 0;
    }

  /* We move `string1' into `string2' if the latter's empty -- but not if
     `string1' is null.  */
  if (size2 == 0 && string1 != NULL)
    {
      string2 = string1;
      size2 = size1;
      string1 = 0;
      size1 = 0;
#ifdef WCHAR
      mbs_offset2 = mbs_offset1;
      csize2 = csize1;
      mbs_offset1 = NULL;
      csize1 = 0;
#endif
    }
  end1 = string1 + size1;
  end2 = string2 + size2;

  /* Compute where to stop matching, within the two strings.  */
#ifdef WCHAR
  if (stop <= csize1)
    {
      mcnt = count_mbs_length(mbs_offset1, stop);
      end_match_1 = string1 + mcnt;
      end_match_2 = string2;
    }
  else
    {
      if (stop > csize1 + csize2)
    stop = csize1 + csize2;
      end_match_1 = end1;
      mcnt = count_mbs_length(mbs_offset2, stop-csize1);
      end_match_2 = string2 + mcnt;
    }
  if (mcnt < 0)
    { /* count_mbs_length return error.  */
      FREE_VARIABLES ();
      return -1;
    }
#else
  if (stop <= size1)
    {
      end_match_1 = string1 + stop;
      end_match_2 = string2;
    }
  else
    {
      end_match_1 = end1;
      end_match_2 = string2 + stop - size1;
    }
#endif /* WCHAR */

  /* `p' scans through the pattern as `d' scans through the data.
     `dend' is the end of the input string that `d' points within.  `d'
     is advanced into the following input string whenever necessary, but
     this happens before fetching; therefore, at the beginning of the
     loop, `d' can be pointing at the end of a string, but it cannot
     equal `string2'.  */
#ifdef WCHAR
  if (size1 > 0 && pos <= csize1)
    {
      mcnt = count_mbs_length(mbs_offset1, pos);
      d = string1 + mcnt;
      dend = end_match_1;
    }
  else
    {
      mcnt = count_mbs_length(mbs_offset2, pos-csize1);
      d = string2 + mcnt;
      dend = end_match_2;
    }

  if (mcnt < 0)
    { /* count_mbs_length return error.  */
      FREE_VARIABLES ();
      return -1;
    }
#else
  if (size1 > 0 && pos <= size1)
    {
      d = string1 + pos;
      dend = end_match_1;
    }
  else
    {
      d = string2 + pos - size1;
      dend = end_match_2;
    }
#endif /* WCHAR */

  DEBUG_PRINT1 ("The compiled pattern is:\n");
  DEBUG_PRINT_COMPILED_PATTERN (bufp, p, pend);
  DEBUG_PRINT1 ("The string to match is: `");
  DEBUG_PRINT_DOUBLE_STRING (d, string1, size1, string2, size2);
  DEBUG_PRINT1 ("'\n");

  /* This loops over pattern commands.  It exits by returning from the
     function if the match is complete, or it drops through if the match
     fails at this starting point in the input data.  */
  for (;;)
    {
#ifdef _LIBC
      DEBUG_PRINT2 ("\n%p: ", p);
#else
      DEBUG_PRINT2 ("\n0x%x: ", p);
#endif

#ifdef __GNUC__
      NEXT;
#else
      if (p == pend)
#endif
    {
#ifdef __GNUC__
    end_of_pattern:
#endif
      /* End of pattern means we might have succeeded.  */
      DEBUG_PRINT1 ("end of pattern ... ");

      /* If we haven't matched the entire string, and we want the
         longest match, try backtracking.  */
      if (d != end_match_2)
        {
          /* 1 if this match ends in the same string (string1 or string2)
         as the best previous match.  */
          boolean same_str_p = (FIRST_STRING_P (match_end)
                    == MATCHING_IN_FIRST_STRING);
          /* 1 if this match is the best seen so far.  */
          boolean best_match_p;

          /* AIX compiler got confused when this was combined
         with the previous declaration.  */
          if (same_str_p)
        best_match_p = d > match_end;
          else
        best_match_p = !MATCHING_IN_FIRST_STRING;

          DEBUG_PRINT1 ("backtracking.\n");

          if (!FAIL_STACK_EMPTY ())
        { /* More failure points to try.  */

          /* If exceeds best match so far, save it.  */
          if (!best_regs_set || best_match_p)
            {
              best_regs_set = true;
              match_end = d;

              DEBUG_PRINT1 ("\nSAVING match as best so far.\n");

              for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++)
            {
              best_regstart[mcnt] = regstart[mcnt];
              best_regend[mcnt] = regend[mcnt];
            }
            }
          goto fail;
        }

          /* If no failure points, don't restore garbage.  And if
         last match is real best match, don't restore second
         best one. */
          else if (best_regs_set && !best_match_p)
        {
        restore_best_regs:
          /* Restore best match.  It may happen that `dend ==
             end_match_1' while the restored d is in string2.
             For example, the pattern `x.*y.*z' against the
             strings `x-' and `y-z-', if the two strings are
             not consecutive in memory.  */
          DEBUG_PRINT1 ("Restoring best registers.\n");

          d = match_end;
          dend = ((d >= string1 && d <= end1)
              ? end_match_1 : end_match_2);

          for (mcnt = 1; (unsigned) mcnt < num_regs; mcnt++)
            {
              regstart[mcnt] = best_regstart[mcnt];
              regend[mcnt] = best_regend[mcnt];
            }
        }
        } /* d != end_match_2 */

    succeed_label:
      DEBUG_PRINT1 ("Accepting match.\n");
      /* If caller wants register contents data back, do it.  */
      if (regs && !bufp->no_sub)
        {
          /* Have the register data arrays been allocated?  */
          if (bufp->regs_allocated == REGS_UNALLOCATED)
        { /* No.  So allocate them with malloc.  We need one
             extra element beyond `num_regs' for the `-1' marker
             GNU code uses.  */
          regs->num_regs = MAX (RE_NREGS, num_regs + 1);
          regs->start = TALLOC (regs->num_regs, regoff_t);
          regs->end = TALLOC (regs->num_regs, regoff_t);
          if (regs->start == NULL || regs->end == NULL)
            {
              FREE_VARIABLES ();
              return -2;
            }
          bufp->regs_allocated = REGS_REALLOCATE;
        }
          else if (bufp->regs_allocated == REGS_REALLOCATE)
        { /* Yes.  If we need more elements than were already
             allocated, reallocate them.  If we need fewer, just
             leave it alone.  */
          if (regs->num_regs < num_regs + 1)
            {
              regs->num_regs = num_regs + 1;
              RETALLOC (regs->start, regs->num_regs, regoff_t);
              RETALLOC (regs->end, regs->num_regs, regoff_t);
              if (regs->start == NULL || regs->end == NULL)
            {
              FREE_VARIABLES ();
              return -2;
            }
            }
        }
          else
        {
          /* These braces fend off a "empty body in an else-statement"
             warning under GCC when assert expands to nothing.  */
          assert (bufp->regs_allocated == REGS_FIXED);
        }

          /* Convert the pointer data in `regstart' and `regend' to
         indices.  Register zero has to be set differently,
         since we haven't kept track of any info for it.  */
          if (regs->num_regs > 0)
        {
          regs->start[0] = pos;
#ifdef WCHAR
          if (MATCHING_IN_FIRST_STRING)
            regs->end[0] = (mbs_offset1 != NULL ?
                    mbs_offset1[d-string1] : 0);
          else
            regs->end[0] = csize1 + (mbs_offset2 != NULL
                         ? mbs_offset2[d-string2] : 0);
#else
          regs->end[0] = (MATCHING_IN_FIRST_STRING
                  ? ((regoff_t) (d - string1))
                  : ((regoff_t) (d - string2 + size1)));
#endif /* WCHAR */
        }

          /* Go through the first `min (num_regs, regs->num_regs)'
         registers, since that is all we initialized.  */
          for (mcnt = 1; (unsigned) mcnt < MIN (num_regs, regs->num_regs);
           mcnt++)
        {
          if (REG_UNSET (regstart[mcnt]) || REG_UNSET (regend[mcnt]))
            regs->start[mcnt] = regs->end[mcnt] = -1;
          else
            {
              regs->start[mcnt]
            = (regoff_t) POINTER_TO_OFFSET (regstart[mcnt]);
              regs->end[mcnt]
            = (regoff_t) POINTER_TO_OFFSET (regend[mcnt]);
            }
        }

          /* If the regs structure we return has more elements than
         were in the pattern, set the extra elements to -1.  If
         we (re)allocated the registers, this is the case,
         because we always allocate enough to have at least one
         -1 at the end.  */
          for (mcnt = num_regs; (unsigned) mcnt < regs->num_regs; mcnt++)
        regs->start[mcnt] = regs->end[mcnt] = -1;
        } /* regs && !bufp->no_sub */

      DEBUG_PRINT4 ("%u failure points pushed, %u popped (%u remain).\n",
            nfailure_points_pushed, nfailure_points_popped,
            nfailure_points_pushed - nfailure_points_popped);
      DEBUG_PRINT2 ("%u registers pushed.\n", num_regs_pushed);

#ifdef WCHAR
      if (MATCHING_IN_FIRST_STRING)
        mcnt = mbs_offset1 != NULL ? mbs_offset1[d-string1] : 0;
      else
        mcnt = (mbs_offset2 != NULL ? mbs_offset2[d-string2] : 0) +
          csize1;
      mcnt -= pos;
#else
      mcnt = d - pos - (MATCHING_IN_FIRST_STRING
                ? string1 : string2 - size1);
#endif /* WCHAR */

      DEBUG_PRINT2 ("Returning %d from re_match_2.\n", mcnt);

      FREE_VARIABLES ();
      return mcnt;
    }

#ifndef __GNUC__
      /* Otherwise match next pattern command.  */
      switch (SWITCH_ENUM_CAST ((re_opcode_t) *p++))
    {
#endif
        /* Ignore these.  Used to ignore the n of succeed_n's which
           currently have n == 0.  */
        CASE (no_op):
          DEBUG_PRINT1 ("EXECUTING no_op.\n");
          NEXT;

    CASE (succeed):
          DEBUG_PRINT1 ("EXECUTING succeed.\n");
      goto succeed_label;

        /* Match the next n pattern characters exactly.  The following
           byte in the pattern defines n, and the n bytes after that
           are the characters to match.  */
    CASE (exactn):
#ifdef MBS_SUPPORT
    CASE (exactn_bin):
#endif
      mcnt = *p++;
          DEBUG_PRINT2 ("EXECUTING exactn %d.\n", mcnt);

          /* This is written out as an if-else so we don't waste time
             testing `translate' inside the loop.  */
          if (translate)
        {
          do
        {
          PREFETCH ();
#ifdef WCHAR
          if (*d <= 0xff)
            {
              if ((UCHAR_T) translate[(unsigned char) *d++]
              != (UCHAR_T) *p++)
            goto fail;
            }
          else
            {
              if (*d++ != (CHAR_T) *p++)
            goto fail;
            }
#else
          if ((UCHAR_T) translate[(unsigned char) *d++]
              != (UCHAR_T) *p++)
                    goto fail;
#endif /* WCHAR */
        }
          while (--mcnt);
        }
      else
        {
          do
        {
          PREFETCH ();
          if (*d++ != (CHAR_T) *p++) goto fail;
        }
          while (--mcnt);
        }
      SET_REGS_MATCHED ();
          NEXT;


        /* Match any character except possibly a newline or a null.  */
    CASE (anychar):
          DEBUG_PRINT1 ("EXECUTING anychar.\n");

          PREFETCH ();

          if ((!(bufp->syntax & RE_DOT_NEWLINE) && TRANSLATE (*d) == '\n')
              || (bufp->syntax & RE_DOT_NOT_NULL && TRANSLATE (*d) == '\000'))
        goto fail;

          SET_REGS_MATCHED ();
          DEBUG_PRINT2 ("  Matched `%ld'.\n", (long int) *d);
          d++;
      NEXT;


    CASE (charset):
    CASE (charset_not):
      {
        register UCHAR_T c;
#ifdef WCHAR
        unsigned int i, char_class_length, coll_symbol_length,
              equiv_class_length, ranges_length, chars_length, length;
        CHAR_T *workp, *workp2, *charset_top;
#define WORK_BUFFER_SIZE 128
            CHAR_T str_buf[WORK_BUFFER_SIZE];
# ifdef _LIBC
        uint32_t nrules;
# endif /* _LIBC */
#endif /* WCHAR */
        boolean not = (re_opcode_t) *(p - 1) == charset_not;

            DEBUG_PRINT2 ("EXECUTING charset%s.\n", not ? "_not" : "");
        PREFETCH ();
        c = TRANSLATE (*d); /* The character to match.  */
#ifdef WCHAR
# ifdef _LIBC
        nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
# endif /* _LIBC */
        charset_top = p - 1;
        char_class_length = *p++;
        coll_symbol_length = *p++;
        equiv_class_length = *p++;
        ranges_length = *p++;
        chars_length = *p++;
        /* p points charset[6], so the address of the next instruction
           (charset[l+m+n+2o+k+p']) equals p[l+m+n+2*o+p'],
           where l=length of char_classes, m=length of collating_symbol,
           n=equivalence_class, o=length of char_range,
           p'=length of character.  */
        workp = p;
        /* Update p to indicate the next instruction.  */
        p += char_class_length + coll_symbol_length+ equiv_class_length +
              2*ranges_length + chars_length;

            /* match with char_class?  */
        for (i = 0; i < char_class_length ; i += CHAR_CLASS_SIZE)
          {
        wctype_t wctype;
        uintptr_t alignedp = ((uintptr_t)workp
                      + __alignof__(wctype_t) - 1)
                      & ~(uintptr_t)(__alignof__(wctype_t) - 1);
        wctype = *((wctype_t*)alignedp);
        workp += CHAR_CLASS_SIZE;
        if (iswctype((wint_t)c, wctype))
          goto char_set_matched;
          }

            /* match with collating_symbol?  */
# ifdef _LIBC
        if (nrules != 0)
          {
        const unsigned char *extra = (const unsigned char *)
          _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);

        for (workp2 = workp + coll_symbol_length ; workp < workp2 ;
             workp++)
          {
            int32_t *wextra;
            wextra = (int32_t*)(extra + *workp++);
            for (i = 0; i < *wextra; ++i)
              if (TRANSLATE(d[i]) != wextra[1 + i])
            break;

            if (i == *wextra)
              {
            /* Update d, however d will be incremented at
               char_set_matched:, we decrement d here.  */
            d += i - 1;
            goto char_set_matched;
              }
          }
          }
        else /* (nrules == 0) */
# endif
          /* If we can't look up collation data, we use wcscoll
         instead.  */
          {
        for (workp2 = workp + coll_symbol_length ; workp < workp2 ;)
          {
            const CHAR_T *backup_d = d, *backup_dend = dend;
            length = wcslen (workp);

            /* If wcscoll(the collating symbol, whole string) > 0,
               any substring of the string never match with the
               collating symbol.  */
            if (wcscoll (workp, d) > 0)
              {
            workp += length + 1;
            continue;
              }

            /* First, we compare the collating symbol with
               the first character of the string.
               If it don't match, we add the next character to
               the compare buffer in turn.  */
            for (i = 0 ; i < WORK_BUFFER_SIZE-1 ; i++, d++)
              {
            int match;
            if (d == dend)
              {
                if (dend == end_match_2)
                  break;
                d = string2;
                dend = end_match_2;
              }

            /* add next character to the compare buffer.  */
            str_buf[i] = TRANSLATE(*d);
            str_buf[i+1] = '\0';

            match = wcscoll (workp, str_buf);
            if (match == 0)
              goto char_set_matched;

            if (match < 0)
              /* (str_buf > workp) indicate (str_buf + X > workp),
                 because for all X (str_buf + X > str_buf).
                 So we don't need continue this loop.  */
              break;

            /* Otherwise(str_buf < workp),
               (str_buf+next_character) may equals (workp).
               So we continue this loop.  */
              }
            /* not matched */
            d = backup_d;
            dend = backup_dend;
            workp += length + 1;
          }
              }
            /* match with equivalence_class?  */
# ifdef _LIBC
        if (nrules != 0)
          {
                const CHAR_T *backup_d = d, *backup_dend = dend;
        /* Try to match the equivalence class against
           those known to the collate implementation.  */
        const int32_t *table;
        const int32_t *weights;
        const int32_t *extra;
        const int32_t *indirect;
        int32_t idx, idx2;
        wint_t *cp;
        size_t len;

        /* This #include defines a local function!  */
#  include <locale/weightwc.h>

        table = (const int32_t *)
          _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEWC);
        weights = (const wint_t *)
          _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTWC);
        extra = (const wint_t *)
          _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAWC);
        indirect = (const int32_t *)
          _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTWC);

        /* Write 1 collating element to str_buf, and
           get its index.  */
        idx2 = 0;

        for (i = 0 ; idx2 == 0 && i < WORK_BUFFER_SIZE - 1; i++)
          {
            cp = (wint_t*)str_buf;
            if (d == dend)
              {
            if (dend == end_match_2)
              break;
            d = string2;
            dend = end_match_2;
              }
            str_buf[i] = TRANSLATE(*(d+i));
            str_buf[i+1] = '\0'; /* sentinel */
            idx2 = findidx ((const wint_t**)&cp);
          }

        /* Update d, however d will be incremented at
           char_set_matched:, we decrement d here.  */
        d = backup_d + ((wchar_t*)cp - (wchar_t*)str_buf - 1);
        if (d >= dend)
          {
            if (dend == end_match_2)
            d = dend;
            else
              {
            d = string2;
            dend = end_match_2;
              }
          }

        len = weights[idx2];

        for (workp2 = workp + equiv_class_length ; workp < workp2 ;
             workp++)
          {
            idx = (int32_t)*workp;
            /* We already checked idx != 0 in regex_compile. */

            if (idx2 != 0 && len == weights[idx])
              {
            int cnt = 0;
            while (cnt < len && (weights[idx + 1 + cnt]
                         == weights[idx2 + 1 + cnt]))
              ++cnt;

            if (cnt == len)
              goto char_set_matched;
              }
          }
        /* not matched */
                d = backup_d;
                dend = backup_dend;
          }
        else /* (nrules == 0) */
# endif
          /* If we can't look up collation data, we use wcscoll
         instead.  */
          {
        for (workp2 = workp + equiv_class_length ; workp < workp2 ;)
          {
            const CHAR_T *backup_d = d, *backup_dend = dend;
            length = wcslen (workp);

            /* If wcscoll(the collating symbol, whole string) > 0,
               any substring of the string never match with the
               collating symbol.  */
            if (wcscoll (workp, d) > 0)
              {
            workp += length + 1;
            break;
              }

            /* First, we compare the equivalence class with
               the first character of the string.
               If it don't match, we add the next character to
               the compare buffer in turn.  */
            for (i = 0 ; i < WORK_BUFFER_SIZE - 1 ; i++, d++)
              {
            int match;
            if (d == dend)
              {
                if (dend == end_match_2)
                  break;
                d = string2;
                dend = end_match_2;
              }

            /* add next character to the compare buffer.  */
            str_buf[i] = TRANSLATE(*d);
            str_buf[i+1] = '\0';

            match = wcscoll (workp, str_buf);

            if (match == 0)
              goto char_set_matched;

            if (match < 0)
            /* (str_buf > workp) indicate (str_buf + X > workp),
               because for all X (str_buf + X > str_buf).
               So we don't need continue this loop.  */
              break;

            /* Otherwise(str_buf < workp),
               (str_buf+next_character) may equals (workp).
               So we continue this loop.  */
              }
            /* not matched */
            d = backup_d;
            dend = backup_dend;
            workp += length + 1;
          }
          }

            /* match with char_range?  */
# ifdef _LIBC
        if (nrules != 0)
          {
        uint32_t collseqval;
        const char *collseq = (const char *)
          _NL_CURRENT(LC_COLLATE, _NL_COLLATE_COLLSEQWC);

        collseqval = collseq_table_lookup (collseq, c);

        for (; workp < p - chars_length ;)
          {
            uint32_t start_val, end_val;

            /* We already compute the collation sequence value
               of the characters (or collating symbols).  */
            start_val = (uint32_t) *workp++; /* range_start */
            end_val = (uint32_t) *workp++; /* range_end */

            if (start_val <= collseqval && collseqval <= end_val)
              goto char_set_matched;
          }
          }
        else
# endif
          {
        /* We set range_start_char at str_buf[0], range_end_char
           at str_buf[4], and compared char at str_buf[2].  */
        str_buf[1] = 0;
        str_buf[2] = c;
        str_buf[3] = 0;
        str_buf[5] = 0;
        for (; workp < p - chars_length ;)
          {
            wchar_t *range_start_char, *range_end_char;

            /* match if (range_start_char <= c <= range_end_char).  */

            /* If range_start(or end) < 0, we assume -range_start(end)
               is the offset of the collating symbol which is specified
               as the character of the range start(end).  */

            /* range_start */
            if (*workp < 0)
              range_start_char = charset_top - (*workp++);
            else
              {
            str_buf[0] = *workp++;
            range_start_char = str_buf;
              }

            /* range_end */
            if (*workp < 0)
              range_end_char = charset_top - (*workp++);
            else
              {
            str_buf[4] = *workp++;
            range_end_char = str_buf + 4;
              }

            if (wcscoll (range_start_char, str_buf+2) <= 0
            && wcscoll (str_buf+2, range_end_char) <= 0)
              goto char_set_matched;
          }
          }

            /* match with char?  */
        for (; workp < p ; workp++)
          if (c == *workp)
        goto char_set_matched;

        not = !not;

      char_set_matched:
        if (not) goto fail;
#else
            /* Cast to `unsigned' instead of `unsigned char' in case the
               bit list is a full 32 bytes long.  */
        if (c < (unsigned) (*p * BYTEWIDTH)
        && p[1 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
          not = !not;

        p += 1 + *p;

        if (!not) goto fail;
#undef WORK_BUFFER_SIZE
#endif /* WCHAR */
        SET_REGS_MATCHED ();
            d++;
        NEXT;
      }


        /* The beginning of a group is represented by start_memory.
           The arguments are the register number in the next byte, and the
           number of groups inner to this one in the next.  The text
           matched within the group is recorded (in the internal
           registers data structure) under the register number.  */
        CASE (start_memory):
      DEBUG_PRINT3 ("EXECUTING start_memory %ld (%ld):\n",
            (long int) *p, (long int) p[1]);

          /* Find out if this group can match the empty string.  */
      p1 = p;       /* To send to group_match_null_string_p.  */

          if (REG_MATCH_NULL_STRING_P (reg_info[*p]) == MATCH_NULL_UNSET_VALUE)
            REG_MATCH_NULL_STRING_P (reg_info[*p])
              = PREFIX(group_match_null_string_p) (&p1, pend, reg_info);

          /* Save the position in the string where we were the last time
             we were at this open-group operator in case the group is
             operated upon by a repetition operator, e.g., with `(a*)*b'
             against `ab'; then we want to ignore where we are now in
             the string in case this attempt to match fails.  */
          old_regstart[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
                             ? REG_UNSET (regstart[*p]) ? d : regstart[*p]
                             : regstart[*p];
      DEBUG_PRINT2 ("  old_regstart: %d\n",
             POINTER_TO_OFFSET (old_regstart[*p]));

          regstart[*p] = d;
      DEBUG_PRINT2 ("  regstart: %d\n", POINTER_TO_OFFSET (regstart[*p]));

          IS_ACTIVE (reg_info[*p]) = 1;
          MATCHED_SOMETHING (reg_info[*p]) = 0;

      /* Clear this whenever we change the register activity status.  */
      set_regs_matched_done = 0;

          /* This is the new highest active register.  */
          highest_active_reg = *p;

          /* If nothing was active before, this is the new lowest active
             register.  */
          if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
            lowest_active_reg = *p;

          /* Move past the register number and inner group count.  */
          p += 2;
      just_past_start_mem = p;

          NEXT;


        /* The stop_memory opcode represents the end of a group.  Its
           arguments are the same as start_memory's: the register
           number, and the number of inner groups.  */
    CASE (stop_memory):
      DEBUG_PRINT3 ("EXECUTING stop_memory %ld (%ld):\n",
            (long int) *p, (long int) p[1]);

          /* We need to save the string position the last time we were at
             this close-group operator in case the group is operated
             upon by a repetition operator, e.g., with `((a*)*(b*)*)*'
             against `aba'; then we want to ignore where we are now in
             the string in case this attempt to match fails.  */
          old_regend[*p] = REG_MATCH_NULL_STRING_P (reg_info[*p])
                           ? REG_UNSET (regend[*p]) ? d : regend[*p]
               : regend[*p];
      DEBUG_PRINT2 ("      old_regend: %d\n",
             POINTER_TO_OFFSET (old_regend[*p]));

          regend[*p] = d;
      DEBUG_PRINT2 ("      regend: %d\n", POINTER_TO_OFFSET (regend[*p]));

          /* This register isn't active anymore.  */
          IS_ACTIVE (reg_info[*p]) = 0;

      /* Clear this whenever we change the register activity status.  */
      set_regs_matched_done = 0;

          /* If this was the only register active, nothing is active
             anymore.  */
          if (lowest_active_reg == highest_active_reg)
            {
              lowest_active_reg = NO_LOWEST_ACTIVE_REG;
              highest_active_reg = NO_HIGHEST_ACTIVE_REG;
            }
          else
            { /* We must scan for the new highest active register, since
                 it isn't necessarily one less than now: consider
                 (a(b)c(d(e)f)g).  When group 3 ends, after the f), the
                 new highest active register is 1.  */
              UCHAR_T r = *p - 1;
              while (r > 0 && !IS_ACTIVE (reg_info[r]))
                r--;

              /* If we end up at register zero, that means that we saved
                 the registers as the result of an `on_failure_jump', not
                 a `start_memory', and we jumped to past the innermost
                 `stop_memory'.  For example, in ((.)*) we save
                 registers 1 and 2 as a result of the *, but when we pop
                 back to the second ), we are at the stop_memory 1.
                 Thus, nothing is active.  */
          if (r == 0)
                {
                  lowest_active_reg = NO_LOWEST_ACTIVE_REG;
                  highest_active_reg = NO_HIGHEST_ACTIVE_REG;
                }
              else
                highest_active_reg = r;
            }

          /* If just failed to match something this time around with a
             group that's operated on by a repetition operator, try to
             force exit from the ``loop'', and restore the register
             information for this group that we had before trying this
             last match.  */
          if ((!MATCHED_SOMETHING (reg_info[*p])
               || just_past_start_mem == p - 1)
          && (p + 2) < pend)
            {
              boolean is_a_jump_n = false;

              p1 = p + 2;
              mcnt = 0;
              switch ((re_opcode_t) *p1++)
                {
                  case jump_n:
            is_a_jump_n = true;
                  case pop_failure_jump:
          case maybe_pop_jump:
          case jump:
          case dummy_failure_jump:
                    EXTRACT_NUMBER_AND_INCR (mcnt, p1);
            if (is_a_jump_n)
              p1 += OFFSET_ADDRESS_SIZE;
                    break;

                  default:
                    /* do nothing */ ;
                }
          p1 += mcnt;

              /* If the next operation is a jump backwards in the pattern
             to an on_failure_jump right before the start_memory
                 corresponding to this stop_memory, exit from the loop
                 by forcing a failure after pushing on the stack the
                 on_failure_jump's jump in the pattern, and d.  */
              if (mcnt < 0 && (re_opcode_t) *p1 == on_failure_jump
                  && (re_opcode_t) p1[1+OFFSET_ADDRESS_SIZE] == start_memory
          && p1[2+OFFSET_ADDRESS_SIZE] == *p)
        {
                  /* If this group ever matched anything, then restore
                     what its registers were before trying this last
                     failed match, e.g., with `(a*)*b' against `ab' for
                     regstart[1], and, e.g., with `((a*)*(b*)*)*'
                     against `aba' for regend[3].

                     Also restore the registers for inner groups for,
                     e.g., `((a*)(b*))*' against `aba' (register 3 would
                     otherwise get trashed).  */

                  if (EVER_MATCHED_SOMETHING (reg_info[*p]))
            {
              unsigned r;

                      EVER_MATCHED_SOMETHING (reg_info[*p]) = 0;

              /* Restore this and inner groups' (if any) registers.  */
                      for (r = *p; r < (unsigned) *p + (unsigned) *(p + 1);
               r++)
                        {
                          regstart[r] = old_regstart[r];

                          /* xx why this test?  */
                          if (old_regend[r] >= regstart[r])
                            regend[r] = old_regend[r];
                        }
                    }
          p1++;
                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
                  PUSH_FAILURE_POINT (p1 + mcnt, d, -2);

                  goto fail;
                }
            }

          /* Move past the register number and the inner group count.  */
          p += 2;
          NEXT;


    /* \<digit> has been turned into a `duplicate' command which is
           followed by the numeric value of <digit> as the register number.  */
        CASE (duplicate):
      {
        register const CHAR_T *d2, *dend2;
        int regno = *p++;   /* Get which register to match against.  */
        DEBUG_PRINT2 ("EXECUTING duplicate %d.\n", regno);

        /* Can't back reference a group which we've never matched.  */
            if (REG_UNSET (regstart[regno]) || REG_UNSET (regend[regno]))
              goto fail;

            /* Where in input to try to start matching.  */
            d2 = regstart[regno];

            /* Where to stop matching; if both the place to start and
               the place to stop matching are in the same string, then
               set to the place to stop, otherwise, for now have to use
               the end of the first string.  */

            dend2 = ((FIRST_STRING_P (regstart[regno])
              == FIRST_STRING_P (regend[regno]))
             ? regend[regno] : end_match_1);
        for (;;)
          {
        /* If necessary, advance to next segment in register
                   contents.  */
        while (d2 == dend2)
          {
            if (dend2 == end_match_2) break;
            if (dend2 == regend[regno]) break;

                    /* End of string1 => advance to string2. */
                    d2 = string2;
                    dend2 = regend[regno];
          }
        /* At end of register contents => success */
        if (d2 == dend2) break;

        /* If necessary, advance to next segment in data.  */
        PREFETCH ();

        /* How many characters left in this segment to match.  */
        mcnt = dend - d;

        /* Want how many consecutive characters we can match in
                   one shot, so, if necessary, adjust the count.  */
                if (mcnt > dend2 - d2)
          mcnt = dend2 - d2;

        /* Compare that many; failure if mismatch, else move
                   past them.  */
        if (translate
                    ? PREFIX(bcmp_translate) (d, d2, mcnt, translate)
                    : memcmp (d, d2, mcnt*sizeof(UCHAR_T)))
          goto fail;
        d += mcnt, d2 += mcnt;

        /* Do this because we've match some characters.  */
        SET_REGS_MATCHED ();
          }
      }
      NEXT;


        /* begline matches the empty string at the beginning of the string
           (unless `not_bol' is set in `bufp'), and, if
           `newline_anchor' is set, after newlines.  */
    CASE (begline):
          DEBUG_PRINT1 ("EXECUTING begline.\n");

          if (AT_STRINGS_BEG (d))
            {
              if (!bufp->not_bol)
        {
          NEXT;
        }
            }
          else if (d[-1] == '\n' && bufp->newline_anchor)
            {
              NEXT;
            }
          /* In all other cases, we fail.  */
          goto fail;


        /* endline is the dual of begline.  */
    CASE (endline):
          DEBUG_PRINT1 ("EXECUTING endline.\n");

          if (AT_STRINGS_END (d))
            {
              if (!bufp->not_eol)
        {
          NEXT;
        }
            }

          /* We have to ``prefetch'' the next character.  */
          else if ((d == end1 ? *string2 : *d) == '\n'
                   && bufp->newline_anchor)
            {
              NEXT;
            }
          goto fail;


    /* Match at the very beginning of the data.  */
        CASE (begbuf):
          DEBUG_PRINT1 ("EXECUTING begbuf.\n");
          if (AT_STRINGS_BEG (d))
        {
          NEXT;
        }
          goto fail;


    /* Match at the very end of the data.  */
        CASE (endbuf):
          DEBUG_PRINT1 ("EXECUTING endbuf.\n");
      if (AT_STRINGS_END (d))
        {
          NEXT;
        }
          goto fail;


        /* on_failure_keep_string_jump is used to optimize `.*\n'.  It
           pushes NULL as the value for the string on the stack.  Then
           `pop_failure_point' will keep the current value for the
           string, instead of restoring it.  To see why, consider
           matching `foo\nbar' against `.*\n'.  The .* matches the foo;
           then the . fails against the \n.  But the next thing we want
           to do is match the \n against the \n; if we restored the
           string value, we would be back at the foo.

           Because this is used only in specific cases, we don't need to
           check all the things that `on_failure_jump' does, to make
           sure the right things get saved on the stack.  Hence we don't
           share its code.  The only reason to push anything on the
           stack at all is that otherwise we would have to change
           `anychar's code to do something besides goto fail in this
           case; that seems worse than this.  */
        CASE (on_failure_keep_string_jump):
          DEBUG_PRINT1 ("EXECUTING on_failure_keep_string_jump");

          EXTRACT_NUMBER_AND_INCR (mcnt, p);
#ifdef _LIBC
          DEBUG_PRINT3 (" %d (to %p):\n", mcnt, p + mcnt);
#else
          DEBUG_PRINT3 (" %d (to 0x%x):\n", mcnt, p + mcnt);
#endif

          PUSH_FAILURE_POINT (p + mcnt, NULL, -2);
          NEXT;


    /* Uses of on_failure_jump:

           Each alternative starts with an on_failure_jump that points
           to the beginning of the next alternative.  Each alternative
           except the last ends with a jump that in effect jumps past
           the rest of the alternatives.  (They really jump to the
           ending jump of the following alternative, because tensioning
           these jumps is a hassle.)

           Repeats start with an on_failure_jump that points past both
           the repetition text and either the following jump or
           pop_failure_jump back to this on_failure_jump.  */
    CASE (on_failure_jump):
        on_failure:
          DEBUG_PRINT1 ("EXECUTING on_failure_jump");

          EXTRACT_NUMBER_AND_INCR (mcnt, p);
#ifdef _LIBC
          DEBUG_PRINT3 (" %d (to %p)", mcnt, p + mcnt);
#else
          DEBUG_PRINT3 (" %d (to 0x%x)", mcnt, p + mcnt);
#endif

          /* If this on_failure_jump comes right before a group (i.e.,
             the original * applied to a group), save the information
             for that group and all inner ones, so that if we fail back
             to this point, the group's information will be correct.
             For example, in \(a*\)*\1, we need the preceding group,
             and in \(zz\(a*\)b*\)\2, we need the inner group.  */

          /* We can't use `p' to check ahead because we push
             a failure point to `p + mcnt' after we do this.  */
          p1 = p;

          /* We need to skip no_op's before we look for the
             start_memory in case this on_failure_jump is happening as
             the result of a completed succeed_n, as in \(a\)\{1,3\}b\1
             against aba.  */
          while (p1 < pend && (re_opcode_t) *p1 == no_op)
            p1++;

          if (p1 < pend && (re_opcode_t) *p1 == start_memory)
            {
              /* We have a new highest active register now.  This will
                 get reset at the start_memory we are about to get to,
                 but we will have saved all the registers relevant to
                 this repetition op, as described above.  */
              highest_active_reg = *(p1 + 1) + *(p1 + 2);
              if (lowest_active_reg == NO_LOWEST_ACTIVE_REG)
                lowest_active_reg = *(p1 + 1);
            }

          DEBUG_PRINT1 (":\n");
          PUSH_FAILURE_POINT (p + mcnt, d, -2);
          NEXT;


        /* A smart repeat ends with `maybe_pop_jump'.
       We change it to either `pop_failure_jump' or `jump'.  */
        CASE (maybe_pop_jump):
          EXTRACT_NUMBER_AND_INCR (mcnt, p);
          DEBUG_PRINT2 ("EXECUTING maybe_pop_jump %d.\n", mcnt);
          {
        register UCHAR_T *p2 = p;

            /* Compare the beginning of the repeat with what in the
               pattern follows its end. If we can establish that there
               is nothing that they would both match, i.e., that we
               would have to backtrack because of (as in, e.g., `a*a')
               then we can change to pop_failure_jump, because we'll
               never have to backtrack.

               This is not true in the case of alternatives: in
               `(a|ab)*' we do need to backtrack to the `ab' alternative
               (e.g., if the string was `ab').  But instead of trying to
               detect that here, the alternative has put on a dummy
               failure point which is what we will end up popping.  */

        /* Skip over open/close-group commands.
           If what follows this loop is a ...+ construct,
           look at what begins its body, since we will have to
           match at least one of that.  */
        while (1)
          {
        if (p2 + 2 < pend
            && ((re_opcode_t) *p2 == stop_memory
            || (re_opcode_t) *p2 == start_memory))
          p2 += 3;
        else if (p2 + 2 + 2 * OFFSET_ADDRESS_SIZE < pend
             && (re_opcode_t) *p2 == dummy_failure_jump)
          p2 += 2 + 2 * OFFSET_ADDRESS_SIZE;
        else
          break;
          }

        p1 = p + mcnt;
        /* p1[0] ... p1[2] are the `on_failure_jump' corresponding
           to the `maybe_finalize_jump' of this case.  Examine what
           follows.  */

            /* If we're at the end of the pattern, we can change.  */
            if (p2 == pend)
          {
        /* Consider what happens when matching ":\(.*\)"
           against ":/".  I don't really understand this code
           yet.  */
            p[-(1+OFFSET_ADDRESS_SIZE)] = (UCHAR_T)
          pop_failure_jump;
                DEBUG_PRINT1
                  ("  End of pattern: change to `pop_failure_jump'.\n");
              }

            else if ((re_opcode_t) *p2 == exactn
#ifdef MBS_SUPPORT
             || (re_opcode_t) *p2 == exactn_bin
#endif
             || (bufp->newline_anchor && (re_opcode_t) *p2 == endline))
          {
        register UCHAR_T c
                  = *p2 == (UCHAR_T) endline ? '\n' : p2[2];

                if (((re_opcode_t) p1[1+OFFSET_ADDRESS_SIZE] == exactn
#ifdef MBS_SUPPORT
             || (re_opcode_t) p1[1+OFFSET_ADDRESS_SIZE] == exactn_bin
#endif
            ) && p1[3+OFFSET_ADDRESS_SIZE] != c)
                  {
            p[-(1+OFFSET_ADDRESS_SIZE)] = (UCHAR_T)
              pop_failure_jump;
#ifdef WCHAR
              DEBUG_PRINT3 ("  %C != %C => pop_failure_jump.\n",
                    (wint_t) c,
                    (wint_t) p1[3+OFFSET_ADDRESS_SIZE]);
#else
              DEBUG_PRINT3 ("  %c != %c => pop_failure_jump.\n",
                    (char) c,
                    (char) p1[3+OFFSET_ADDRESS_SIZE]);
#endif
                  }

#ifndef WCHAR
        else if ((re_opcode_t) p1[3] == charset
             || (re_opcode_t) p1[3] == charset_not)
          {
            int not = (re_opcode_t) p1[3] == charset_not;

            if (c < (unsigned) (p1[4] * BYTEWIDTH)
            && p1[5 + c / BYTEWIDTH] & (1 << (c % BYTEWIDTH)))
              not = !not;

                    /* `not' is equal to 1 if c would match, which means
                        that we can't change to pop_failure_jump.  */
            if (!not)
                      {
                p[-3] = (unsigned char) pop_failure_jump;
                        DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
                      }
          }
#endif /* not WCHAR */
          }
#ifndef WCHAR
            else if ((re_opcode_t) *p2 == charset)
          {
        /* We win if the first character of the loop is not part
                   of the charset.  */
                if ((re_opcode_t) p1[3] == exactn
            && ! ((int) p2[1] * BYTEWIDTH > (int) p1[5]
              && (p2[2 + p1[5] / BYTEWIDTH]
                  & (1 << (p1[5] % BYTEWIDTH)))))
          {
            p[-3] = (unsigned char) pop_failure_jump;
            DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
                  }

        else if ((re_opcode_t) p1[3] == charset_not)
          {
            int idx;
            /* We win if the charset_not inside the loop
               lists every character listed in the charset after.  */
            for (idx = 0; idx < (int) p2[1]; idx++)
              if (! (p2[2 + idx] == 0
                 || (idx < (int) p1[4]
                 && ((p2[2 + idx] & ~ p1[5 + idx]) == 0))))
            break;

            if (idx == p2[1])
                      {
                p[-3] = (unsigned char) pop_failure_jump;
                        DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
                      }
          }
        else if ((re_opcode_t) p1[3] == charset)
          {
            int idx;
            /* We win if the charset inside the loop
               has no overlap with the one after the loop.  */
            for (idx = 0;
             idx < (int) p2[1] && idx < (int) p1[4];
             idx++)
              if ((p2[2 + idx] & p1[5 + idx]) != 0)
            break;

            if (idx == p2[1] || idx == p1[4])
                      {
                p[-3] = (unsigned char) pop_failure_jump;
                        DEBUG_PRINT1 ("  No match => pop_failure_jump.\n");
                      }
          }
          }
#endif /* not WCHAR */
      }
      p -= OFFSET_ADDRESS_SIZE; /* Point at relative address again.  */
      if ((re_opcode_t) p[-1] != pop_failure_jump)
        {
          p[-1] = (UCHAR_T) jump;
              DEBUG_PRINT1 ("  Match => jump.\n");
          goto unconditional_jump;
        }
        /* Note fall through.  */


    /* The end of a simple repeat has a pop_failure_jump back to
           its matching on_failure_jump, where the latter will push a
           failure point.  The pop_failure_jump takes off failure
           points put on by this pop_failure_jump's matching
           on_failure_jump; we got through the pattern to here from the
           matching on_failure_jump, so didn't fail.  */
        CASE (pop_failure_jump):
          {
            /* We need to pass separate storage for the lowest and
               highest registers, even though we don't care about the
               actual values.  Otherwise, we will restore only one
               register from the stack, since lowest will == highest in
               `pop_failure_point'.  */
            active_reg_t dummy_low_reg, dummy_high_reg;
            UCHAR_T *pdummy = NULL;
            const CHAR_T *sdummy = NULL;

            DEBUG_PRINT1 ("EXECUTING pop_failure_jump.\n");
            POP_FAILURE_POINT (sdummy, pdummy,
                               dummy_low_reg, dummy_high_reg,
                               reg_dummy, reg_dummy, reg_info_dummy);
          }
      /* Note fall through.  */

    unconditional_jump:
#ifdef _LIBC
      DEBUG_PRINT2 ("\n%p: ", p);
#else
      DEBUG_PRINT2 ("\n0x%x: ", p);
#endif
          /* Note fall through.  */

        /* Unconditionally jump (without popping any failure points).  */
        CASE (jump):
      EXTRACT_NUMBER_AND_INCR (mcnt, p);    /* Get the amount to jump.  */
          DEBUG_PRINT2 ("EXECUTING jump %d ", mcnt);
      p += mcnt;                /* Do the jump.  */
#ifdef _LIBC
          DEBUG_PRINT2 ("(to %p).\n", p);
#else
          DEBUG_PRINT2 ("(to 0x%x).\n", p);
#endif
      NEXT;


        /* We need this opcode so we can detect where alternatives end
           in `group_match_null_string_p' et al.  */
        CASE (jump_past_alt):
          DEBUG_PRINT1 ("EXECUTING jump_past_alt.\n");
          goto unconditional_jump;


        /* Normally, the on_failure_jump pushes a failure point, which
           then gets popped at pop_failure_jump.  We will end up at
           pop_failure_jump, also, and with a pattern of, say, `a+', we
           are skipping over the on_failure_jump, so we have to push
           something meaningless for pop_failure_jump to pop.  */
        CASE (dummy_failure_jump):
          DEBUG_PRINT1 ("EXECUTING dummy_failure_jump.\n");
          /* It doesn't matter what we push for the string here.  What
             the code at `fail' tests is the value for the pattern.  */
          PUSH_FAILURE_POINT (NULL, NULL, -2);
          goto unconditional_jump;


        /* At the end of an alternative, we need to push a dummy failure
           point in case we are followed by a `pop_failure_jump', because
           we don't want the failure point for the alternative to be
           popped.  For example, matching `(a|ab)*' against `aab'
           requires that we match the `ab' alternative.  */
        CASE (push_dummy_failure):
          DEBUG_PRINT1 ("EXECUTING push_dummy_failure.\n");
          /* See comments just above at `dummy_failure_jump' about the
             two zeroes.  */
          PUSH_FAILURE_POINT (NULL, NULL, -2);
          NEXT;

        /* Have to succeed matching what follows at least n times.
           After that, handle like `on_failure_jump'.  */
        CASE (succeed_n):
          EXTRACT_NUMBER (mcnt, p + OFFSET_ADDRESS_SIZE);
          DEBUG_PRINT2 ("EXECUTING succeed_n %d.\n", mcnt);

          assert (mcnt >= 0);
          /* Originally, this is how many times we HAVE to succeed.  */
          if (mcnt > 0)
            {
               mcnt--;
           p += OFFSET_ADDRESS_SIZE;
               STORE_NUMBER_AND_INCR (p, mcnt);
#ifdef _LIBC
               DEBUG_PRINT3 ("  Setting %p to %d.\n", p - OFFSET_ADDRESS_SIZE
                 , mcnt);
#else
               DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p - OFFSET_ADDRESS_SIZE
                 , mcnt);
#endif
            }
      else if (mcnt == 0)
            {
#ifdef _LIBC
              DEBUG_PRINT2 ("  Setting two bytes from %p to no_op.\n",
                p + OFFSET_ADDRESS_SIZE);
#else
              DEBUG_PRINT2 ("  Setting two bytes from 0x%x to no_op.\n",
                p + OFFSET_ADDRESS_SIZE);
#endif /* _LIBC */

#ifdef WCHAR
          p[1] = (UCHAR_T) no_op;
#else
          p[2] = (UCHAR_T) no_op;
              p[3] = (UCHAR_T) no_op;
#endif /* WCHAR */
              goto on_failure;
            }
          NEXT;

        CASE (jump_n):
          EXTRACT_NUMBER (mcnt, p + OFFSET_ADDRESS_SIZE);
          DEBUG_PRINT2 ("EXECUTING jump_n %d.\n", mcnt);

          /* Originally, this is how many times we CAN jump.  */
          if (mcnt)
            {
               mcnt--;
               STORE_NUMBER (p + OFFSET_ADDRESS_SIZE, mcnt);

#ifdef _LIBC
               DEBUG_PRINT3 ("  Setting %p to %d.\n", p + OFFSET_ADDRESS_SIZE,
                 mcnt);
#else
               DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p + OFFSET_ADDRESS_SIZE,
                 mcnt);
#endif /* _LIBC */
           goto unconditional_jump;
            }
          /* If don't have to jump any more, skip over the rest of command.  */
      else
        p += 2 * OFFSET_ADDRESS_SIZE;
          NEXT;

    CASE (set_number_at):
      {
            DEBUG_PRINT1 ("EXECUTING set_number_at.\n");

            EXTRACT_NUMBER_AND_INCR (mcnt, p);
            p1 = p + mcnt;
            EXTRACT_NUMBER_AND_INCR (mcnt, p);
#ifdef _LIBC
            DEBUG_PRINT3 ("  Setting %p to %d.\n", p1, mcnt);
#else
            DEBUG_PRINT3 ("  Setting 0x%x to %d.\n", p1, mcnt);
#endif
        STORE_NUMBER (p1, mcnt);
            NEXT;
          }

#if 0
    /* The DEC Alpha C compiler 3.x generates incorrect code for the
       test  WORDCHAR_P (d - 1) != WORDCHAR_P (d)  in the expansion of
       AT_WORD_BOUNDARY, so this code is disabled.  Expanding the
       macro and introducing temporary variables works around the bug.  */

    CASE (wordbound):
      DEBUG_PRINT1 ("EXECUTING wordbound.\n");
      if (AT_WORD_BOUNDARY (d))
        {
          NEXT;
        }
      goto fail;

    CASE (notwordbound):
      DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
      if (AT_WORD_BOUNDARY (d))
        goto fail;
      NEXT;
#else
    CASE (wordbound):
    {
      boolean prevchar, thischar;

      DEBUG_PRINT1 ("EXECUTING wordbound.\n");
      if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d))
        {
          NEXT;
        }

      prevchar = WORDCHAR_P (d - 1);
      thischar = WORDCHAR_P (d);
      if (prevchar != thischar)
        {
          NEXT;
        }
      goto fail;
    }

      CASE (notwordbound):
    {
      boolean prevchar, thischar;

      DEBUG_PRINT1 ("EXECUTING notwordbound.\n");
      if (AT_STRINGS_BEG (d) || AT_STRINGS_END (d))
        goto fail;

      prevchar = WORDCHAR_P (d - 1);
      thischar = WORDCHAR_P (d);
      if (prevchar != thischar)
        goto fail;
      NEXT;
    }
#endif

    CASE (wordbeg):
          DEBUG_PRINT1 ("EXECUTING wordbeg.\n");
      if (!AT_STRINGS_END (d) && WORDCHAR_P (d)
          && (AT_STRINGS_BEG (d) || !WORDCHAR_P (d - 1)))
        {
          NEXT;
        }
          goto fail;

    CASE (wordend):
          DEBUG_PRINT1 ("EXECUTING wordend.\n");
      if (!AT_STRINGS_BEG (d) && WORDCHAR_P (d - 1)
              && (AT_STRINGS_END (d) || !WORDCHAR_P (d)))
        {
          NEXT;
        }
          goto fail;

#ifdef emacs
    CASE (before_dot):
          DEBUG_PRINT1 ("EXECUTING before_dot.\n");
      if (PTR_CHAR_POS ((unsigned char *) d) >= point)
        goto fail;
      NEXT;

    CASE (at_dot):
          DEBUG_PRINT1 ("EXECUTING at_dot.\n");
      if (PTR_CHAR_POS ((unsigned char *) d) != point)
        goto fail;
      NEXT;

    CASE (after_dot):
          DEBUG_PRINT1 ("EXECUTING after_dot.\n");
          if (PTR_CHAR_POS ((unsigned char *) d) <= point)
        goto fail;
      NEXT;

    CASE (syntaxspec):
          DEBUG_PRINT2 ("EXECUTING syntaxspec %d.\n", mcnt);
      mcnt = *p++;
      goto matchsyntax;

        CASE (wordchar):
          DEBUG_PRINT1 ("EXECUTING Emacs wordchar.\n");
      mcnt = (int) Sword;
        matchsyntax:
      PREFETCH ();
      /* Can't use *d++ here; SYNTAX may be an unsafe macro.  */
      d++;
      if (SYNTAX (d[-1]) != (enum syntaxcode) mcnt)
        goto fail;
          SET_REGS_MATCHED ();
      NEXT;

    CASE (notsyntaxspec):
          DEBUG_PRINT2 ("EXECUTING notsyntaxspec %d.\n", mcnt);
      mcnt = *p++;
      goto matchnotsyntax;

        CASE (notwordchar):
          DEBUG_PRINT1 ("EXECUTING Emacs notwordchar.\n");
      mcnt = (int) Sword;
        matchnotsyntax:
      PREFETCH ();
      /* Can't use *d++ here; SYNTAX may be an unsafe macro.  */
      d++;
      if (SYNTAX (d[-1]) == (enum syntaxcode) mcnt)
        goto fail;
      SET_REGS_MATCHED ();
          NEXT;

#else /* not emacs */
    CASE (wordchar):
          DEBUG_PRINT1 ("EXECUTING non-Emacs wordchar.\n");
      PREFETCH ();
          if (!WORDCHAR_P (d))
            goto fail;
      SET_REGS_MATCHED ();
          d++;
      NEXT;

    CASE (notwordchar):
          DEBUG_PRINT1 ("EXECUTING non-Emacs notwordchar.\n");
      PREFETCH ();
      if (WORDCHAR_P (d))
            goto fail;
          SET_REGS_MATCHED ();
          d++;
      NEXT;
#endif /* not emacs */

#ifndef __GNUC__
        default:
          abort ();
    }
      continue;  /* Successfully executed one pattern command; keep going.  */
#endif


    /* We goto here if a matching operation fails. */
    fail:
      if (!FAIL_STACK_EMPTY ())
    { /* A restart point is known.  Restore to that state.  */
          DEBUG_PRINT1 ("\nFAIL:\n");
          POP_FAILURE_POINT (d, p,
                             lowest_active_reg, highest_active_reg,
                             regstart, regend, reg_info);

          /* If this failure point is a dummy, try the next one.  */
          if (!p)
        goto fail;

          /* If we failed to the end of the pattern, don't examine *p.  */
      assert (p <= pend);
          if (p < pend)
            {
              boolean is_a_jump_n = false;

              /* If failed to a backwards jump that's part of a repetition
                 loop, need to pop this failure point and use the next one.  */
              switch ((re_opcode_t) *p)
                {
                case jump_n:
                  is_a_jump_n = true;
                case maybe_pop_jump:
                case pop_failure_jump:
                case jump:
                  p1 = p + 1;
                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
                  p1 += mcnt;

                  if ((is_a_jump_n && (re_opcode_t) *p1 == succeed_n)
                      || (!is_a_jump_n
                          && (re_opcode_t) *p1 == on_failure_jump))
                    goto fail;
                  break;
                default:
                  /* do nothing */ ;
                }
            }

          if (d >= string1 && d <= end1)
        dend = end_match_1;
        }
      else
        break;   /* Matching at this starting point really fails.  */
    } /* for (;;) */

  if (best_regs_set)
    goto restore_best_regs;

  FREE_VARIABLES ();

  return -1;                    /* Failure to match.  */
} /* re_match_2 */


/* Subroutine definitions for re_match_2.  */


/* We are passed P pointing to a register number after a start_memory.

   Return true if the pattern up to the corresponding stop_memory can
   match the empty string, and false otherwise.

   If we find the matching stop_memory, sets P to point to one past its number.
   Otherwise, sets P to an undefined byte less than or equal to END.

   We don't handle duplicates properly (yet).  */

static boolean
PREFIX(group_match_null_string_p) (p, end, reg_info)
    UCHAR_T **p, *end;
    PREFIX(register_info_type) *reg_info;
{
  int mcnt;
  /* Point to after the args to the start_memory.  */
  UCHAR_T *p1 = *p + 2;

  while (p1 < end)
    {
      /* Skip over opcodes that can match nothing, and return true or
     false, as appropriate, when we get to one that can't, or to the
         matching stop_memory.  */

      switch ((re_opcode_t) *p1)
        {
        /* Could be either a loop or a series of alternatives.  */
        case on_failure_jump:
          p1++;
          EXTRACT_NUMBER_AND_INCR (mcnt, p1);

          /* If the next operation is not a jump backwards in the
         pattern.  */

      if (mcnt >= 0)
        {
              /* Go through the on_failure_jumps of the alternatives,
                 seeing if any of the alternatives cannot match nothing.
                 The last alternative starts with only a jump,
                 whereas the rest start with on_failure_jump and end
                 with a jump, e.g., here is the pattern for `a|b|c':

                 /on_failure_jump/0/6/exactn/1/a/jump_past_alt/0/6
                 /on_failure_jump/0/6/exactn/1/b/jump_past_alt/0/3
                 /exactn/1/c

                 So, we have to first go through the first (n-1)
                 alternatives and then deal with the last one separately.  */


              /* Deal with the first (n-1) alternatives, which start
                 with an on_failure_jump (see above) that jumps to right
                 past a jump_past_alt.  */

              while ((re_opcode_t) p1[mcnt-(1+OFFSET_ADDRESS_SIZE)] ==
             jump_past_alt)
                {
                  /* `mcnt' holds how many bytes long the alternative
                     is, including the ending `jump_past_alt' and
                     its number.  */

                  if (!PREFIX(alt_match_null_string_p) (p1, p1 + mcnt -
                        (1 + OFFSET_ADDRESS_SIZE),
                        reg_info))
                    return false;

                  /* Move to right after this alternative, including the
             jump_past_alt.  */
                  p1 += mcnt;

                  /* Break if it's the beginning of an n-th alternative
                     that doesn't begin with an on_failure_jump.  */
                  if ((re_opcode_t) *p1 != on_failure_jump)
                    break;

          /* Still have to check that it's not an n-th
             alternative that starts with an on_failure_jump.  */
          p1++;
                  EXTRACT_NUMBER_AND_INCR (mcnt, p1);
                  if ((re_opcode_t) p1[mcnt-(1+OFFSET_ADDRESS_SIZE)] !=
              jump_past_alt)
                    {
              /* Get to the beginning of the n-th alternative.  */
                      p1 -= 1 + OFFSET_ADDRESS_SIZE;
                      break;
                    }
                }

              /* Deal with the last alternative: go back and get number
                 of the `jump_past_alt' just before it.  `mcnt' contains
                 the length of the alternative.  */
              EXTRACT_NUMBER (mcnt, p1 - OFFSET_ADDRESS_SIZE);

              if (!PREFIX(alt_match_null_string_p) (p1, p1 + mcnt, reg_info))
                return false;

              p1 += mcnt;   /* Get past the n-th alternative.  */
            } /* if mcnt > 0 */
          break;


        case stop_memory:
      assert (p1[1] == **p);
          *p = p1 + 2;
          return true;


        default:
          if (!PREFIX(common_op_match_null_string_p) (&p1, end, reg_info))
            return false;
        }
    } /* while p1 < end */

  return false;
} /* group_match_null_string_p */


/* Similar to group_match_null_string_p, but doesn't deal with alternatives:
   It expects P to be the first byte of a single alternative and END one
   byte past the last. The alternative can contain groups.  */

static boolean
PREFIX(alt_match_null_string_p) (p, end, reg_info)
    UCHAR_T *p, *end;
    PREFIX(register_info_type) *reg_info;
{
  int mcnt;
  UCHAR_T *p1 = p;

  while (p1 < end)
    {
      /* Skip over opcodes that can match nothing, and break when we get
         to one that can't.  */

      switch ((re_opcode_t) *p1)
        {
    /* It's a loop.  */
        case on_failure_jump:
          p1++;
          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
          p1 += mcnt;
          break;

    default:
          if (!PREFIX(common_op_match_null_string_p) (&p1, end, reg_info))
            return false;
        }
    }  /* while p1 < end */

  return true;
} /* alt_match_null_string_p */


/* Deals with the ops common to group_match_null_string_p and
   alt_match_null_string_p.

   Sets P to one after the op and its arguments, if any.  */

static boolean
PREFIX(common_op_match_null_string_p) (p, end, reg_info)
    UCHAR_T **p, *end;
    PREFIX(register_info_type) *reg_info;
{
  int mcnt;
  boolean ret;
  int reg_no;
  UCHAR_T *p1 = *p;

  switch ((re_opcode_t) *p1++)
    {
    case no_op:
    case begline:
    case endline:
    case begbuf:
    case endbuf:
    case wordbeg:
    case wordend:
    case wordbound:
    case notwordbound:
#ifdef emacs
    case before_dot:
    case at_dot:
    case after_dot:
#endif
      break;

    case start_memory:
      reg_no = *p1;
      assert (reg_no > 0 && reg_no <= MAX_REGNUM);
      ret = PREFIX(group_match_null_string_p) (&p1, end, reg_info);

      /* Have to set this here in case we're checking a group which
         contains a group and a back reference to it.  */

      if (REG_MATCH_NULL_STRING_P (reg_info[reg_no]) == MATCH_NULL_UNSET_VALUE)
        REG_MATCH_NULL_STRING_P (reg_info[reg_no]) = ret;

      if (!ret)
        return false;
      break;

    /* If this is an optimized succeed_n for zero times, make the jump.  */
    case jump:
      EXTRACT_NUMBER_AND_INCR (mcnt, p1);
      if (mcnt >= 0)
        p1 += mcnt;
      else
        return false;
      break;

    case succeed_n:
      /* Get to the number of times to succeed.  */
      p1 += OFFSET_ADDRESS_SIZE;
      EXTRACT_NUMBER_AND_INCR (mcnt, p1);

      if (mcnt == 0)
        {
          p1 -= 2 * OFFSET_ADDRESS_SIZE;
          EXTRACT_NUMBER_AND_INCR (mcnt, p1);
          p1 += mcnt;
        }
      else
        return false;
      break;

    case duplicate:
      if (!REG_MATCH_NULL_STRING_P (reg_info[*p1]))
        return false;
      break;

    case set_number_at:
      p1 += 2 * OFFSET_ADDRESS_SIZE;

    default:
      /* All other opcodes mean we cannot match the empty string.  */
      return false;
  }

  *p = p1;
  return true;
} /* common_op_match_null_string_p */


/* Return zero if TRANSLATE[S1] and TRANSLATE[S2] are identical for LEN
   bytes; nonzero otherwise.  */

static int
PREFIX(bcmp_translate) (s1, s2, len, translate)
     const CHAR_T *s1, *s2;
     register int len;
     RE_TRANSLATE_TYPE translate;
{
  register const UCHAR_T *p1 = (const UCHAR_T *) s1;
  register const UCHAR_T *p2 = (const UCHAR_T *) s2;
  while (len)
    {
#ifdef WCHAR
      if (((*p1<=0xff)?translate[*p1++]:*p1++)
      != ((*p2<=0xff)?translate[*p2++]:*p2++))
    return 1;
#else /* BYTE */
      if (translate[*p1++] != translate[*p2++]) return 1;
#endif /* WCHAR */
      len--;
    }
  return 0;
}



#else /* not INSIDE_RECURSION */

/* Entry points for GNU code.  */

/* re_compile_pattern is the GNU regular expression compiler: it
   compiles PATTERN (of length SIZE) and puts the result in BUFP.
   Returns 0 if the pattern was valid, otherwise an error string.

   Assumes the `allocated' (and perhaps `buffer') and `translate' fields
   are set in BUFP on entry.

   We call regex_compile to do the actual compilation.  */

const char *
re_compile_pattern (pattern, length, bufp)
     const char *pattern;
     size_t length;
     struct re_pattern_buffer *bufp;
{
  reg_errcode_t ret;

  /* GNU code is written to assume at least RE_NREGS registers will be set
     (and at least one extra will be -1).  */
  bufp->regs_allocated = REGS_UNALLOCATED;

  /* And GNU code determines whether or not to get register information
     by passing null for the REGS argument to re_match, etc., not by
     setting no_sub.  */
  bufp->no_sub = 0;

  /* Match anchors at newline.  */
  bufp->newline_anchor = 1;

# ifdef MBS_SUPPORT
  if (MB_CUR_MAX != 1)
    ret = wcs_regex_compile (pattern, length, re_syntax_options, bufp);
  else
# endif
    ret = byte_regex_compile (pattern, length, re_syntax_options, bufp);

  if (!ret)
    return NULL;
  return gettext (re_error_msgid + re_error_msgid_idx[(int) ret]);
}
#ifdef _LIBC
weak_alias (__re_compile_pattern, re_compile_pattern)
#endif


/* Entry points compatible with 4.2 BSD regex library.  We don't define
   them unless specifically requested.  */

#if defined _REGEX_RE_COMP || defined _LIBC

/* BSD has one and only one pattern buffer.  */
static struct re_pattern_buffer re_comp_buf;

char *
#ifdef _LIBC
/* Make these definitions weak in libc, so POSIX programs can redefine
   these names if they don't use our functions, and still use
   regcomp/regexec below without link errors.  */
weak_function
#endif
re_comp (s)
    const char *s;
{
  reg_errcode_t ret;

  if (!s)
    {
      if (!re_comp_buf.buffer)
    return gettext ("No previous regular expression");
      return 0;
    }

  if (!re_comp_buf.buffer)
    {
      re_comp_buf.buffer = (unsigned char *) malloc (200);
      if (re_comp_buf.buffer == NULL)
        return (char *) gettext (re_error_msgid
                 + re_error_msgid_idx[(int) REG_ESPACE]);
      re_comp_buf.allocated = 200;

      re_comp_buf.fastmap = (char *) malloc (1 << BYTEWIDTH);
      if (re_comp_buf.fastmap == NULL)
    return (char *) gettext (re_error_msgid
                 + re_error_msgid_idx[(int) REG_ESPACE]);
    }

  /* Since `re_exec' always passes NULL for the `regs' argument, we
     don't need to initialize the pattern buffer fields which affect it.  */

  /* Match anchors at newlines.  */
  re_comp_buf.newline_anchor = 1;

# ifdef MBS_SUPPORT
  if (MB_CUR_MAX != 1)
    ret = wcs_regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);
  else
# endif
    ret = byte_regex_compile (s, strlen (s), re_syntax_options, &re_comp_buf);

  if (!ret)
    return NULL;

  /* Yes, we're discarding `const' here if !HAVE_LIBINTL.  */
  return (char *) gettext (re_error_msgid + re_error_msgid_idx[(int) ret]);
}


int
#ifdef _LIBC
weak_function
#endif
re_exec (s)
    const char *s;
{
  const int len = strlen (s);
  return
    0 <= re_search (&re_comp_buf, s, len, 0, len, (struct re_registers *) 0);
}

#endif /* _REGEX_RE_COMP */


/* POSIX.2 functions.  Don't define these for Emacs.  */

#ifndef emacs

/* regcomp takes a regular expression as a string and compiles it.

   PREG is a regex_t *.  We do not expect any fields to be initialized,
   since POSIX says we shouldn't.  Thus, we set

     `buffer' to the compiled pattern;
     `used' to the length of the compiled pattern;
     `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
       REG_EXTENDED bit in CFLAGS is set; otherwise, to
       RE_SYNTAX_POSIX_BASIC;
     `newline_anchor' to REG_NEWLINE being set in CFLAGS;
     `fastmap' to an allocated space for the fastmap;
     `fastmap_accurate' to zero;
     `re_nsub' to the number of subexpressions in PATTERN.

   PATTERN is the address of the pattern string.

   CFLAGS is a series of bits which affect compilation.

     If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
     use POSIX basic syntax.

     If REG_NEWLINE is set, then . and [^...] don't match newline.
     Also, regexec will try a match beginning after every newline.

     If REG_ICASE is set, then we considers upper- and lowercase
     versions of letters to be equivalent when matching.

     If REG_NOSUB is set, then when PREG is passed to regexec, that
     routine will report only success or failure, and nothing about the
     registers.

   It returns 0 if it succeeds, nonzero if it doesn't.  (See regex.h for
   the return codes and their meanings.)  */

int
regcomp (preg, pattern, cflags)
    regex_t *preg;
    const char *pattern;
    int cflags;
{
  reg_errcode_t ret;
  reg_syntax_t syntax
    = (cflags & REG_EXTENDED) ?
      RE_SYNTAX_POSIX_EXTENDED : RE_SYNTAX_POSIX_BASIC;

  /* regex_compile will allocate the space for the compiled pattern.  */
  preg->buffer = 0;
  preg->allocated = 0;
  preg->used = 0;

  /* Try to allocate space for the fastmap.  */
  preg->fastmap = (char *) malloc (1 << BYTEWIDTH);

  if (cflags & REG_ICASE)
    {
      unsigned i;

      preg->translate
    = (RE_TRANSLATE_TYPE) malloc (CHAR_SET_SIZE
                      * sizeof (*(RE_TRANSLATE_TYPE)0));
      if (preg->translate == NULL)
        return (int) REG_ESPACE;

      /* Map uppercase characters to corresponding lowercase ones.  */
      for (i = 0; i < CHAR_SET_SIZE; i++)
        preg->translate[i] = ISUPPER (i) ? TOLOWER (i) : i;
    }
  else
    preg->translate = NULL;

  /* If REG_NEWLINE is set, newlines are treated differently.  */
  if (cflags & REG_NEWLINE)
    { /* REG_NEWLINE implies neither . nor [^...] match newline.  */
      syntax &= ~RE_DOT_NEWLINE;
      syntax |= RE_HAT_LISTS_NOT_NEWLINE;
      /* It also changes the matching behavior.  */
      preg->newline_anchor = 1;
    }
  else
    preg->newline_anchor = 0;

  preg->no_sub = !!(cflags & REG_NOSUB);

  /* POSIX says a null character in the pattern terminates it, so we
     can use strlen here in compiling the pattern.  */
# ifdef MBS_SUPPORT
  if (MB_CUR_MAX != 1)
    ret = wcs_regex_compile (pattern, strlen (pattern), syntax, preg);
  else
# endif
    ret = byte_regex_compile (pattern, strlen (pattern), syntax, preg);

  /* POSIX doesn't distinguish between an unmatched open-group and an
     unmatched close-group: both are REG_EPAREN.  */
  if (ret == REG_ERPAREN) ret = REG_EPAREN;

  if (ret == REG_NOERROR && preg->fastmap)
    {
      /* Compute the fastmap now, since regexec cannot modify the pattern
     buffer.  */
      if (re_compile_fastmap (preg) == -2)
    {
      /* Some error occurred while computing the fastmap, just forget
         about it.  */
      free (preg->fastmap);
      preg->fastmap = NULL;
    }
    }

  return (int) ret;
}
#ifdef _LIBC
weak_alias (__regcomp, regcomp)
#endif


/* regexec searches for a given pattern, specified by PREG, in the
   string STRING.

   If NMATCH is zero or REG_NOSUB was set in the cflags argument to
   `regcomp', we ignore PMATCH.  Otherwise, we assume PMATCH has at
   least NMATCH elements, and we set them to the offsets of the
   corresponding matched substrings.

   EFLAGS specifies `execution flags' which affect matching: if
   REG_NOTBOL is set, then ^ does not match at the beginning of the
   string; if REG_NOTEOL is set, then $ does not match at the end.

   We return 0 if we find a match and REG_NOMATCH if not.  */

int
regexec (preg, string, nmatch, pmatch, eflags)
    const regex_t *preg;
    const char *string;
    size_t nmatch;
    regmatch_t pmatch[];
    int eflags;
{
  int ret;
  struct re_registers regs;
  regex_t private_preg;
  int len = strlen (string);
  boolean want_reg_info = !preg->no_sub && nmatch > 0;

  private_preg = *preg;

  private_preg.not_bol = !!(eflags & REG_NOTBOL);
  private_preg.not_eol = !!(eflags & REG_NOTEOL);

  /* The user has told us exactly how many registers to return
     information about, via `nmatch'.  We have to pass that on to the
     matching routines.  */
  private_preg.regs_allocated = REGS_FIXED;

  if (want_reg_info)
    {
      regs.num_regs = nmatch;
      regs.start = TALLOC (nmatch * 2, regoff_t);
      if (regs.start == NULL)
        return (int) REG_NOMATCH;
      regs.end = regs.start + nmatch;
    }

  /* Perform the searching operation.  */
  ret = re_search (&private_preg, string, len,
                   /* start: */ 0, /* range: */ len,
                   want_reg_info ? &regs : (struct re_registers *) 0);

  /* Copy the register information to the POSIX structure.  */
  if (want_reg_info)
    {
      if (ret >= 0)
        {
          unsigned r;

          for (r = 0; r < nmatch; r++)
            {
              pmatch[r].rm_so = regs.start[r];
              pmatch[r].rm_eo = regs.end[r];
            }
        }

      /* If we needed the temporary register info, free the space now.  */
      free (regs.start);
    }

  /* We want zero return to mean success, unlike `re_search'.  */
  return ret >= 0 ? (int) REG_NOERROR : (int) REG_NOMATCH;
}
#ifdef _LIBC
weak_alias (__regexec, regexec)
#endif


/* Returns a message corresponding to an error code, ERRCODE, returned
   from either regcomp or regexec.   We don't use PREG here.  */

size_t
regerror (errcode, preg, errbuf, errbuf_size)
    int errcode;
    const regex_t *preg;
    char *errbuf;
    size_t errbuf_size;
{
  const char *msg;
  size_t msg_size;

  if (errcode < 0
      || errcode >= (int) (sizeof (re_error_msgid_idx)
               / sizeof (re_error_msgid_idx[0])))
    /* Only error codes returned by the rest of the code should be passed
       to this routine.  If we are given anything else, or if other regex
       code generates an invalid error code, then the program has a bug.
       Dump core so we can fix it.  */
    abort ();

  msg = gettext (re_error_msgid + re_error_msgid_idx[errcode]);

  msg_size = strlen (msg) + 1; /* Includes the null.  */

  if (errbuf_size != 0)
    {
      if (msg_size > errbuf_size)
        {
#if defined HAVE_MEMPCPY || defined _LIBC
      *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0';
#else
          memcpy (errbuf, msg, errbuf_size - 1);
          errbuf[errbuf_size - 1] = 0;
#endif
        }
      else
        memcpy (errbuf, msg, msg_size);
    }

  return msg_size;
}
#ifdef _LIBC
weak_alias (__regerror, regerror)
#endif


/* Free dynamically allocated space used by PREG.  */

void
regfree (preg)
    regex_t *preg;
{
  if (preg->buffer != NULL)
    free (preg->buffer);
  preg->buffer = NULL;

  preg->allocated = 0;
  preg->used = 0;

  if (preg->fastmap != NULL)
    free (preg->fastmap);
  preg->fastmap = NULL;
  preg->fastmap_accurate = 0;

  if (preg->translate != NULL)
    free (preg->translate);
  preg->translate = NULL;
}
#ifdef _LIBC
weak_alias (__regfree, regfree)
#endif

#endif /* not emacs  */

#endif /* not INSIDE_RECURSION */



#undef STORE_NUMBER
#undef STORE_NUMBER_AND_INCR
#undef EXTRACT_NUMBER
#undef EXTRACT_NUMBER_AND_INCR

#undef DEBUG_PRINT_COMPILED_PATTERN
#undef DEBUG_PRINT_DOUBLE_STRING

#undef INIT_FAIL_STACK
#undef RESET_FAIL_STACK
#undef DOUBLE_FAIL_STACK
#undef PUSH_PATTERN_OP
#undef PUSH_FAILURE_POINTER
#undef PUSH_FAILURE_INT
#undef PUSH_FAILURE_ELT
#undef POP_FAILURE_POINTER
#undef POP_FAILURE_INT
#undef POP_FAILURE_ELT
#undef DEBUG_PUSH
#undef DEBUG_POP
#undef PUSH_FAILURE_POINT
#undef POP_FAILURE_POINT

#undef REG_UNSET_VALUE
#undef REG_UNSET

#undef PATFETCH
#undef PATFETCH_RAW
#undef PATUNFETCH
#undef TRANSLATE

#undef INIT_BUF_SIZE
#undef GET_BUFFER_SPACE
#undef BUF_PUSH
#undef BUF_PUSH_2
#undef BUF_PUSH_3
#undef STORE_JUMP
#undef STORE_JUMP2
#undef INSERT_JUMP
#undef INSERT_JUMP2
#undef EXTEND_BUFFER
#undef GET_UNSIGNED_NUMBER
#undef FREE_STACK_RETURN

# undef POINTER_TO_OFFSET
# undef MATCHING_IN_FRST_STRING
# undef PREFETCH
# undef AT_STRINGS_BEG
# undef AT_STRINGS_END
# undef WORDCHAR_P
# undef FREE_VAR
# undef FREE_VARIABLES
# undef NO_HIGHEST_ACTIVE_REG
# undef NO_LOWEST_ACTIVE_REG

# undef CHAR_T
# undef UCHAR_T
# undef COMPILED_BUFFER_VAR
# undef OFFSET_ADDRESS_SIZE
# undef CHAR_CLASS_SIZE
# undef PREFIX
# undef ARG_PREFIX
# undef PUT_CHAR
# undef BYTE
# undef WCHAR

# define DEFINED_ONCE

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/canonicalize.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/canonicalize.c used in: Step 837
/* Return the canonical absolute name of a given file.
   Copyright (C) 1996-2001, 2002, 2003 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; see the file COPYING.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifdef STDC_HEADERS
# include <stdlib.h>
#else
void free ();
#endif

#if defined STDC_HEADERS || defined HAVE_STRING_H
# include <string.h>
#else
# include <strings.h>
#endif

#if HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif

#include <sys/stat.h>

#include <errno.h>

#include "path-concat.h"
#include "xalloc.h"
#include "xgetcwd.h"

#ifndef errno
extern int errno;
#endif

#ifndef __set_errno
# define __set_errno(Val) errno = (Val)
#endif

#if !HAVE_RESOLVEPATH

/* If __PTRDIFF_TYPE__ is
   defined, as with GNU C, use that; that way we don't pollute the
   namespace with <stddef.h>'s symbols.  Otherwise, if <stddef.h> is
   available, include it and use ptrdiff_t.  In traditional C, long is
   the best that we can do.  */

# ifdef __PTRDIFF_TYPE__
#  define PTR_INT_TYPE __PTRDIFF_TYPE__
# else
#  ifdef HAVE_STDDEF_H
#   include <stddef.h>
#   define PTR_INT_TYPE ptrdiff_t
#  else
#   define PTR_INT_TYPE long
#  endif
# endif

# include "pathmax.h"
# include "xreadlink.h"

# ifdef STAT_MACROS_BROKEN
#  undef S_ISLNK
# endif

# ifndef S_ISLNK
#  ifdef S_IFLNK
#   define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
#  endif
# endif

#endif /* !HAVE_RESOLVEPATH */

/* Return the canonical absolute name of file NAME.  A canonical name
   does not contain any `.', `..' components nor any repeated path
   separators ('/') or symlinks.  All path components must exist.
   The result is malloc'd.  */

char *
canonicalize_file_name (const char *name)
{
#if HAVE_RESOLVEPATH

  char *resolved, *extra_buf = NULL;
  size_t resolved_size;
  ssize_t resolved_len;

#else /* !HAVE_RESOLVEPATH */

  char *rpath, *dest, *extra_buf = NULL;
  const char *start, *end, *rpath_limit;
  size_t extra_len = 0;
  int num_links = 0;

#endif /* !HAVE_RESOLVEPATH */

  if (name == NULL)
    {
      __set_errno (EINVAL);
      return NULL;
    }

  if (name[0] == '\0')
    {
      __set_errno (ENOENT);
      return NULL;
    }

#if HAVE_RESOLVEPATH

  /* All known hosts with resolvepath (e.g. Solaris 7) don't turn
     relative names into absolute ones, so prepend the working
     directory if the path is not absolute.  */
  if (name[0] != '/')
    {
      char *wd;

      if (!(wd = xgetcwd ()));
    return NULL;

      extra_buf = path_concat (wd, name, NULL);
      if (!extra_buf)
    xalloc_die ();

      name = extra_buf;
      free (wd);
    }

  resolved_size = strlen (name);
  while (1)
    {
      resolved_size = 2 * resolved_size + 1;
      resolved = xmalloc (resolved_size);
      resolved_len = resolvepath (name, resolved, resolved_size);
      if (resolved_len < resolved_size)
    break;
      free (resolved);
    }

  if (resolved_len < 0)
    {
      free (resolved);
      resolved = NULL;
    }

  free (extra_buf);
  return resolved;

#else /* !HAVE_RESOLVEPATH */

  if (name[0] != '/')
    {
      rpath = xgetcwd ();
      if (!rpath)
    return NULL;
      dest = strchr (rpath, '\0');
      if (dest < rpath + PATH_MAX)
    {
      rpath = xrealloc (rpath, PATH_MAX);
      rpath_limit = rpath + PATH_MAX;
    }
      else
    {
      rpath_limit = dest;
    }
    }
  else
    {
      rpath = xmalloc (PATH_MAX);
      rpath_limit = rpath + PATH_MAX;
      rpath[0] = '/';
      dest = rpath + 1;
    }

  for (start = end = name; *start; start = end)
    {
      /* Skip sequence of multiple path-separators.  */
      while (*start == '/')
    ++start;

      /* Find end of path component.  */
      for (end = start; *end && *end != '/'; ++end)
    /* Nothing.  */;

      if (end - start == 0)
    break;
      else if (end - start == 1 && start[0] == '.')
    /* nothing */;
      else if (end - start == 2 && start[0] == '.' && start[1] == '.')
    {
      /* Back up to previous component, ignore if at root already.  */
      if (dest > rpath + 1)
        while ((--dest)[-1] != '/');
    }
      else
    {
      struct stat st;

      if (dest[-1] != '/')
        *dest++ = '/';

      if (dest + (end - start) >= rpath_limit)
        {
          PTR_INT_TYPE dest_offset = dest - rpath;
          size_t new_size = rpath_limit - rpath;

          if (end - start + 1 > PATH_MAX)
        new_size += end - start + 1;
          else
        new_size += PATH_MAX;
          rpath = (char *) xrealloc (rpath, new_size);
          rpath_limit = rpath + new_size;

          dest = rpath + dest_offset;
        }

      dest = memcpy (dest, start, end - start);
      dest += end - start;
      *dest = '\0';

      if (lstat (rpath, &st) < 0)
        goto error;

# ifdef S_ISLNK
      if (S_ISLNK (st.st_mode))
        {
          char *buf;
          size_t n, len;

#  ifdef MAXSYMLINKS
          if (++num_links > MAXSYMLINKS)
        {
          __set_errno (ELOOP);
          goto error;
        }
#  endif /* MAXSYMLINKS */

          buf = xreadlink (rpath);
          if (!buf)
        goto error;

          n = strlen (buf);
          len = strlen (end);

          if (!extra_len)
        {
          extra_len =
            ((n + len + 1) > PATH_MAX) ? (n + len + 1) : PATH_MAX;
          extra_buf = xmalloc (extra_len);
        }
          else if ((n + len + 1) > extra_len)
        {
          extra_len = n + len + 1;
          extra_buf = xrealloc (extra_buf, extra_len);
        }

          /* Careful here, end may be a pointer into extra_buf... */
          memmove (&extra_buf[n], end, len + 1);
          name = end = memcpy (extra_buf, buf, n);

          if (buf[0] == '/')
        dest = rpath + 1;   /* It's an absolute symlink */
          else
        /* Back up to previous component, ignore if at root already: */
        if (dest > rpath + 1)
          while ((--dest)[-1] != '/');

          free (buf);
        }
# endif /* S_ISLNK */
    }
    }
  if (dest > rpath + 1 && dest[-1] == '/')
    --dest;
  *dest = '\0';

  free (extra_buf);
  return rpath;

error:
  free (extra_buf);
  free (rpath);
  return NULL;
#endif /* !HAVE_RESOLVEPATH */
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/mkstemp.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/mkstemp.c used in: Step 838
/* Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
   This file is derived from the one in the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include <config.h>

/* Disable the definition of mkstemp to rpl_mkstemp (from config.h) in this
   file.  Otherwise, we'd get conflicting prototypes for rpl_mkstemp on
   most systems.  */
#undef mkstemp

#include <stdio.h>
#include <stdlib.h>

#ifndef __GT_FILE
# define __GT_FILE 0
#endif

int __gen_tempname ();

/* Generate a unique temporary file name from TEMPLATE.
   The last six characters of TEMPLATE must be "XXXXXX";
   they are replaced with a string that makes the filename unique.
   Then open the file and return a fd. */
int
rpl_mkstemp (char *template)
{
  return __gen_tempname (template, __GT_FILE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/memrchr.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/memrchr.c used in: Step 839
/* memrchr -- find the last occurrence of a byte in a memory block
   Copyright (C) 1991, 93, 96, 97, 99, 2000 Free Software Foundation, Inc.
   Based on strlen implementation by Torbjorn Granlund (tege@sics.se),
   with help from Dan Sahlin (dan@sics.se) and
   commentary by Jim Blandy (jimb@ai.mit.edu);
   adaptation to memchr suggested by Dick Karpinski (dick@cca.ucsf.edu),
   and implemented by Roland McGrath (roland@ai.mit.edu).

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdlib.h>

#undef __ptr_t
#if defined (__cplusplus) || (defined (__STDC__) && __STDC__)
# define __ptr_t void *
#else /* Not C++ or ANSI C.  */
# define __ptr_t char *
#endif /* C++ or ANSI C.  */

#if defined (_LIBC)
# include <string.h>
# include <memcopy.h>
#else
# define reg_char char
#endif

#if defined (HAVE_LIMITS_H) || defined (_LIBC)
# include <limits.h>
#endif

#define LONG_MAX_32_BITS 2147483647

#ifndef LONG_MAX
# define LONG_MAX LONG_MAX_32_BITS
#endif

#include <sys/types.h>

#undef __memrchr
#undef memrchr

#ifndef weak_alias
# define __memrchr memrchr
#endif

/* Search no more than N bytes of S for C.  */
__ptr_t
__memrchr (s, c_in, n)
     const __ptr_t s;
     int c_in;
     size_t n;
{
  const unsigned char *char_ptr;
  const unsigned long int *longword_ptr;
  unsigned long int longword, magic_bits, charmask;
  unsigned reg_char c;

  c = (unsigned char) c_in;

  /* Handle the last few characters by reading one character at a time.
     Do this until CHAR_PTR is aligned on a longword boundary.  */
  for (char_ptr = (const unsigned char *) s + n;
       n > 0 && ((unsigned long int) char_ptr
         & (sizeof (longword) - 1)) != 0;
       --n)
    if (*--char_ptr == c)
      return (__ptr_t) char_ptr;

  /* All these elucidatory comments refer to 4-byte longwords,
     but the theory applies equally well to 8-byte longwords.  */

  longword_ptr = (unsigned long int *) char_ptr;

  /* Bits 31, 24, 16, and 8 of this number are zero.  Call these bits
     the "holes."  Note that there is a hole just to the left of
     each byte, with an extra at the end:

     bits:  01111110 11111110 11111110 11111111
     bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD

     The 1-bits make sure that carries propagate to the next 0-bit.
     The 0-bits provide holes for carries to fall into.  */

  if (sizeof (longword) != 4 && sizeof (longword) != 8)
    abort ();

#if LONG_MAX <= LONG_MAX_32_BITS
  magic_bits = 0x7efefeff;
#else
  magic_bits = ((unsigned long int) 0x7efefefe << 32) | 0xfefefeff;
#endif

  /* Set up a longword, each of whose bytes is C.  */
  charmask = c | (c << 8);
  charmask |= charmask << 16;
#if LONG_MAX > LONG_MAX_32_BITS
  charmask |= charmask << 32;
#endif

  /* Instead of the traditional loop which tests each character,
     we will test a longword at a time.  The tricky part is testing
     if *any of the four* bytes in the longword in question are zero.  */
  while (n >= sizeof (longword))
    {
      /* We tentatively exit the loop if adding MAGIC_BITS to
     LONGWORD fails to change any of the hole bits of LONGWORD.

     1) Is this safe?  Will it catch all the zero bytes?
     Suppose there is a byte with all zeros.  Any carry bits
     propagating from its left will fall into the hole at its
     least significant bit and stop.  Since there will be no
     carry from its most significant bit, the LSB of the
     byte to the left will be unchanged, and the zero will be
     detected.

     2) Is this worthwhile?  Will it ignore everything except
     zero bytes?  Suppose every byte of LONGWORD has a bit set
     somewhere.  There will be a carry into bit 8.  If bit 8
     is set, this will carry into bit 16.  If bit 8 is clear,
     one of bits 9-15 must be set, so there will be a carry
     into bit 16.  Similarly, there will be a carry into bit
     24.  If one of bits 24-30 is set, there will be a carry
     into bit 31, so all of the hole bits will be changed.

     The one misfire occurs when bits 24-30 are clear and bit
     31 is set; in this case, the hole at bit 31 is not
     changed.  If we had access to the processor carry flag,
     we could close this loophole by putting the fourth hole
     at bit 32!

     So it ignores everything except 128's, when they're aligned
     properly.

     3) But wait!  Aren't we looking for C, not zero?
     Good point.  So what we do is XOR LONGWORD with a longword,
     each of whose bytes is C.  This turns each byte that is C
     into a zero.  */

      longword = *--longword_ptr ^ charmask;

      /* Add MAGIC_BITS to LONGWORD.  */
      if ((((longword + magic_bits)

        /* Set those bits that were unchanged by the addition.  */
        ^ ~longword)

       /* Look at only the hole bits.  If any of the hole bits
          are unchanged, most likely one of the bytes was a
          zero.  */
       & ~magic_bits) != 0)
    {
      /* Which of the bytes was C?  If none of them were, it was
         a misfire; continue the search.  */

      const unsigned char *cp = (const unsigned char *) longword_ptr;

#if LONG_MAX > 2147483647
      if (cp[7] == c)
        return (__ptr_t) &cp[7];
      if (cp[6] == c)
        return (__ptr_t) &cp[6];
      if (cp[5] == c)
        return (__ptr_t) &cp[5];
      if (cp[4] == c)
        return (__ptr_t) &cp[4];
#endif
      if (cp[3] == c)
        return (__ptr_t) &cp[3];
      if (cp[2] == c)
        return (__ptr_t) &cp[2];
      if (cp[1] == c)
        return (__ptr_t) &cp[1];
      if (cp[0] == c)
        return (__ptr_t) cp;
    }

      n -= sizeof (longword);
    }

  char_ptr = (const unsigned char *) longword_ptr;

  while (n-- > 0)
    {
      if (*--char_ptr == c)
    return (__ptr_t) char_ptr;
    }

  return 0;
}
#ifdef weak_alias
weak_alias (__memrchr, memrchr)
#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/euidaccess.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/euidaccess.c used in: Step 840
/* euidaccess -- check if effective user id can access file
   Copyright (C) 1990, 1991, 1995, 1998, 2000, 2003 Free Software Foundation, Inc.

This file is part of the GNU C Library.

The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

The GNU C Library 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.

You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 59 Temple Place -
Suite 330, Boston, MA 02111-1307, USA.  */

/* Written by David MacKenzie and Torbjorn Granlund.
   Adapted for GNU C library by Roland McGrath.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>

#ifdef S_IEXEC
# ifndef S_IXUSR
#  define S_IXUSR S_IEXEC
# endif
# ifndef S_IXGRP
#  define S_IXGRP (S_IEXEC >> 3)
# endif
# ifndef S_IXOTH
#  define S_IXOTH (S_IEXEC >> 6)
# endif
#endif /* S_IEXEC */

#if defined (HAVE_UNISTD_H) || defined (_LIBC)
# include <unistd.h>
#endif

#ifdef _POSIX_VERSION
# include <limits.h>
# if !defined(NGROUPS_MAX) || NGROUPS_MAX < 1
#  undef NGROUPS_MAX
#  define NGROUPS_MAX sysconf (_SC_NGROUPS_MAX)
# endif /* NGROUPS_MAX */

#else /* not _POSIX_VERSION */
uid_t getuid ();
gid_t getgid ();
uid_t geteuid ();
gid_t getegid ();
# include <sys/param.h>
# if !defined(NGROUPS_MAX) && defined(NGROUPS)
#  define NGROUPS_MAX NGROUPS
# endif /* not NGROUPS_MAX and NGROUPS */
#endif /* not POSIX_VERSION */

#include <errno.h>
#ifndef errno
extern int errno;
#endif
#ifndef __set_errno
# define __set_errno(val) errno = (val)
#endif
#include "euidaccess.h"

#if defined(EACCES) && !defined(EACCESS)
# define EACCESS EACCES
#endif

#ifndef F_OK
# define F_OK 0
# define X_OK 1
# define W_OK 2
# define R_OK 4
#endif

#if !defined (S_IROTH) && defined (R_OK)
# define S_IROTH R_OK
#endif

#if !defined (S_IWOTH) && defined (W_OK)
# define S_IWOTH W_OK
#endif

#if !defined (S_IXOTH) && defined (X_OK)
# define S_IXOTH X_OK
#endif

#ifdef _LIBC

# define group_member __group_member
# define euidaccess __euidaccess

#else

/* The user's real user id. */
static uid_t uid;

/* The user's real group id. */
static gid_t gid;

# if HAVE_GETGROUPS
int group_member ();
# else
#  define group_member(gid) 0
# endif

#endif

/* The user's effective user id. */
static uid_t euid;

/* The user's effective group id. */
static gid_t egid;

/* Nonzero if UID, GID, EUID, and EGID have valid values. */
static int have_ids;


/* Return 0 if the user has permission of type MODE on file PATH;
   otherwise, return -1 and set `errno' to EACCESS.
   Like access, except that it uses the effective user and group
   id's instead of the real ones, and it does not check for read-only
   filesystem, text busy, etc. */

int
euidaccess (const char *path, int mode)
{
  struct stat stats;
  int granted;

#ifdef  _LIBC
  if (! __libc_enable_secure)
    /* If we are not set-uid or set-gid, access does the same.  */
    return __access (path, mode);
#else
  if (have_ids == 0)
    {
      have_ids = 1;
      uid = getuid ();
      gid = getgid ();
      euid = geteuid ();
      egid = getegid ();
    }

  if (uid == euid && gid == egid)
    /* If we are not set-uid or set-gid, access does the same.  */
    return access (path, mode);
#endif

  if (stat (path, &stats))
    return -1;

  mode &= (X_OK | W_OK | R_OK); /* Clear any bogus bits. */
#if R_OK != S_IROTH || W_OK != S_IWOTH || X_OK != S_IXOTH
  ?error Oops, portability assumptions incorrect.
#endif

  if (mode == F_OK)
    return 0;           /* The file exists. */

#ifdef  _LIBC
  /* Now we need the IDs.  */
  if (have_ids == 0)
    {
      have_ids = 1;
      euid = __geteuid ();
      egid = __getegid ();
    }
#endif

  /* The super-user can read and write any file, and execute any file
     that anyone can execute. */
  if (euid == 0 && ((mode & X_OK) == 0
            || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))))
    return 0;

  if (euid == stats.st_uid)
    granted = (unsigned) (stats.st_mode & (mode << 6)) >> 6;
  else if (egid == stats.st_gid || group_member (stats.st_gid))
    granted = (unsigned) (stats.st_mode & (mode << 3)) >> 3;
  else
    granted = (stats.st_mode & mode);
  if (granted == mode)
    return 0;
  __set_errno (EACCESS);
  return -1;
}
#undef euidaccess
#ifdef weak_alias
weak_alias (__euidaccess, euidaccess)
#endif


#ifdef TEST
# include <stdio.h>
# include <errno.h>
# include "error.h"

char *program_name;

int
main (argc, argv)
     int argc;
     char **argv;
{
  char *file;
  int mode;
  int err;

  program_name = argv[0];
  if (argc < 3)
    abort ();
  file = argv[1];
  mode = atoi (argv[2]);

  err = euidaccess (file, mode);
  printf ("%d\n", err);
  if (err != 0)
    error (0, errno, "%s", file);
  exit (0);
}
#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/ftw.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/ftw.c used in: Step 841
/* File tree walker functions.
   Copyright (C) 1996-2001, 2002, 2003 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#if __GNUC__
# define alloca __builtin_alloca
#else
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
 #  pragma alloca
#  else
char *alloca ();
#  endif
# endif
#endif

#include <sys/types.h>

#if defined _LIBC
# include <dirent.h>
# define NAMLEN(dirent) _D_EXACT_NAMLEN (dirent)
#else
# if HAVE_DIRENT_H
#  include <dirent.h>
#  define NAMLEN(dirent) strlen ((dirent)->d_name)
# else
#  define dirent direct
#  define NAMLEN(dirent) (dirent)->d_namlen
#  if HAVE_SYS_NDIR_H
#   include <sys/ndir.h>
#  endif
#  if HAVE_SYS_DIR_H
#   include <sys/dir.h>
#  endif
#  if HAVE_NDIR_H
#   include <ndir.h>
#  endif
# endif
#endif

#include <errno.h>
#include <ftw.h>
#include <limits.h>
#include <search.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#if HAVE_SYS_PARAM_H || defined _LIBC
# include <sys/param.h>
#endif
#ifdef _LIBC
# include <include/sys/stat.h>
#else
# include <sys/stat.h>
#endif

#if ! _LIBC && !HAVE_DECL_STPCPY && !defined stpcpy
char *stpcpy ();
#endif

#if ! _LIBC && ! defined HAVE_MEMPCPY && ! defined mempcpy
/* Be CAREFUL that there are no side effects in N.  */
# define mempcpy(D, S, N) ((void *) ((char *) memcpy (D, S, N) + (N)))
#endif

/* #define NDEBUG 1 */
#include <assert.h>

#include "save-cwd.h"

#ifndef _LIBC
# undef __chdir
# define __chdir chdir
# undef __closedir
# define __closedir closedir
# undef __fchdir
# define __fchdir fchdir
# undef __mempcpy
# define __mempcpy mempcpy
# undef __opendir
# define __opendir opendir
# undef __readdir64
# define __readdir64 readdir
# undef __stpcpy
# define __stpcpy stpcpy
# undef __tdestroy
# define __tdestroy tdestroy
# undef __tfind
# define __tfind tfind
# undef __tsearch
# define __tsearch tsearch
# undef internal_function
# define internal_function /* empty */
# undef dirent64
# define dirent64 dirent
# undef MAX
# define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif

/* Arrange to make lstat calls go through the wrapper function
   on systems with an lstat function that does not dereference symlinks
   that are specified with a trailing slash.  */
#if ! _LIBC && ! LSTAT_FOLLOWS_SLASHED_SYMLINK
int rpl_lstat (const char *, struct stat *);
# undef lstat
# define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf)
#endif

#ifndef __set_errno
# define __set_errno(Val) errno = (Val)
#endif

/* Support for the LFS API version.  */
#ifndef FTW_NAME
# define FTW_NAME ftw
# define NFTW_NAME nftw
# define INO_T ino_t
# define FTW_STAT stat
# ifdef _LIBC
#  define LXSTAT __lxstat
#  define XSTAT __xstat
# else
#  define LXSTAT(V,f,sb) lstat (f,sb)
#  define XSTAT(V,f,sb) stat (f,sb)
# endif
# define FTW_FUNC_T __ftw_func_t
# define NFTW_FUNC_T __nftw_func_t
#endif

/* We define PATH_MAX if the system does not provide a definition.
   This does not artificially limit any operation.  PATH_MAX is simply
   used as a guesstimate for the expected maximal path length.
   Buffers will be enlarged if necessary.  */
#ifndef PATH_MAX
# define PATH_MAX 1024
#endif

#ifndef S_IFMT
# define S_IFMT 0170000
#endif

#if STAT_MACROS_BROKEN
# undef S_ISLNK
#endif

#ifndef S_ISLNK
# ifdef S_IFLNK
#  define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
# else
#  define S_ISLNK(m) 0
# endif
#endif

struct dir_data
{
  DIR *stream;
  char *content;
};

struct known_object
{
  dev_t dev;
  INO_T ino;
};

struct ftw_data
{
  /* Array with pointers to open directory streams.  */
  struct dir_data **dirstreams;
  size_t actdir;
  size_t maxdir;

  /* Buffer containing name of currently processed object.  */
  char *dirbuf;
  size_t dirbufsize;

  /* Passed as fourth argument to `nftw' callback.  The `base' member
     tracks the content of the `dirbuf'.  */
  struct FTW ftw;

  /* Flags passed to `nftw' function.  0 for `ftw'.  */
  int flags;

  /* Conversion array for flag values.  It is the identity mapping for
     `nftw' calls, otherwise it maps the values to those known by
     `ftw'.  */
  const int *cvt_arr;

  /* Callback function.  We always use the `nftw' form.  */
  NFTW_FUNC_T func;

  /* Device of starting point.  Needed for FTW_MOUNT.  */
  dev_t dev;

  /* Data structure for keeping fingerprints of already processed
     object.  This is needed when not using FTW_PHYS.  */
  void *known_objects;
};


/* Internally we use the FTW_* constants used for `nftw'.  When invoked
   as `ftw', map each flag to the subset of values used by `ftw'.  */
static const int nftw_arr[] =
{
  FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_SL, FTW_DP, FTW_SLN
};

static const int ftw_arr[] =
{
  FTW_F, FTW_D, FTW_DNR, FTW_NS, FTW_F, FTW_D, FTW_NS
};


/* Forward declarations of local functions.  */
static int ftw_dir (struct ftw_data *data, struct FTW_STAT *st)
     internal_function;


static int
object_compare (const void *p1, const void *p2)
{
  /* We don't need a sophisticated and useful comparison.  We are only
     interested in equality.  However, we must be careful not to
     accidentally compare `holes' in the structure.  */
  const struct known_object *kp1 = p1, *kp2 = p2;
  int cmp1;
  cmp1 = (kp1->ino > kp2->ino) - (kp1->ino < kp2->ino);
  if (cmp1 != 0)
    return cmp1;
  return (kp1->dev > kp2->dev) - (kp1->dev < kp2->dev);
}


static inline int
add_object (struct ftw_data *data, struct FTW_STAT *st)
{
  struct known_object *newp = malloc (sizeof (struct known_object));
  if (newp == NULL)
    return -1;
  newp->dev = st->st_dev;
  newp->ino = st->st_ino;
  return __tsearch (newp, &data->known_objects, object_compare) ? 0 : -1;
}


static inline int
find_object (struct ftw_data *data, struct FTW_STAT *st)
{
  struct known_object obj;
  obj.dev = st->st_dev;
  obj.ino = st->st_ino;
  return __tfind (&obj, &data->known_objects, object_compare) != NULL;
}


static inline int
open_dir_stream (struct ftw_data *data, struct dir_data *dirp)
{
  int result = 0;

  if (data->dirstreams[data->actdir] != NULL)
    {
      /* Oh, oh.  We must close this stream.  Get all remaining
     entries and store them as a list in the `content' member of
     the `struct dir_data' variable.  */
      size_t bufsize = 1024;
      char *buf = malloc (bufsize);

      if (buf == NULL)
    result = -1;
      else
    {
      DIR *st = data->dirstreams[data->actdir]->stream;
      struct dirent64 *d;
      size_t actsize = 0;

      while ((d = __readdir64 (st)) != NULL)
        {
          size_t this_len = NAMLEN (d);
          if (actsize + this_len + 2 >= bufsize)
        {
          char *newp;
          bufsize += MAX (1024, 2 * this_len);
          newp = (char *) realloc (buf, bufsize);
          if (newp == NULL)
            {
              /* No more memory.  */
              int save_err = errno;
              free (buf);
              __set_errno (save_err);
              result = -1;
              break;
            }
          buf = newp;
        }

          *((char *) __mempcpy (buf + actsize, d->d_name, this_len))
        = '\0';
          actsize += this_len + 1;
        }

      /* Terminate the list with an additional NUL byte.  */
      buf[actsize++] = '\0';

      /* Shrink the buffer to what we actually need.  */
      data->dirstreams[data->actdir]->content = realloc (buf, actsize);
      if (data->dirstreams[data->actdir]->content == NULL)
        {
          int save_err = errno;
          free (buf);
          __set_errno (save_err);
          result = -1;
        }
      else
        {
          __closedir (st);
          data->dirstreams[data->actdir]->stream = NULL;
          data->dirstreams[data->actdir] = NULL;
        }
    }
    }

  /* Open the new stream.  */
  if (result == 0)
    {
      const char *name = ((data->flags & FTW_CHDIR)
              ? data->dirbuf + data->ftw.base: data->dirbuf);
      assert (data->dirstreams[data->actdir] == NULL);

      dirp->stream = __opendir (name);
      if (dirp->stream == NULL)
    result = -1;
      else
    {
      dirp->content = NULL;
      data->dirstreams[data->actdir] = dirp;

      if (++data->actdir == data->maxdir)
        data->actdir = 0;
    }
    }

  return result;
}


static inline int
process_entry (struct ftw_data *data, struct dir_data *dir, const char *name,
           size_t namlen)
{
  struct FTW_STAT st;
  int result = 0;
  int flag = 0;
  size_t new_buflen;

  if (name[0] == '.' && (name[1] == '\0'
             || (name[1] == '.' && name[2] == '\0')))
    /* Don't process the "." and ".." entries.  */
    return 0;

  new_buflen = data->ftw.base + namlen + 2;
  if (data->dirbufsize < new_buflen)
    {
      /* Enlarge the buffer.  */
      char *newp;

      data->dirbufsize = 2 * new_buflen;
      newp = (char *) realloc (data->dirbuf, data->dirbufsize);
      if (newp == NULL)
    return -1;
      data->dirbuf = newp;
    }

  *((char *) __mempcpy (data->dirbuf + data->ftw.base, name, namlen)) = '\0';

  if ((data->flags & FTW_CHDIR) == 0)
    name = data->dirbuf;

  if (((data->flags & FTW_PHYS)
       ? LXSTAT (_STAT_VER, name, &st)
       : XSTAT (_STAT_VER, name, &st)) < 0)
    {
      if (errno != EACCES && errno != ENOENT)
    result = -1;
      else if (!(data->flags & FTW_PHYS)
           && LXSTAT (_STAT_VER, name, &st) == 0
           && S_ISLNK (st.st_mode))
    flag = FTW_SLN;
      else
    flag = FTW_NS;
    }
  else
    {
      if (S_ISDIR (st.st_mode))
    flag = FTW_D;
      else if (S_ISLNK (st.st_mode))
    flag = FTW_SL;
      else
    flag = FTW_F;
    }

  if (result == 0
      && (flag == FTW_NS
      || !(data->flags & FTW_MOUNT) || st.st_dev == data->dev))
    {
      if (flag == FTW_D)
    {
      if ((data->flags & FTW_PHYS)
          || (!find_object (data, &st)
          /* Remember the object.  */
          && (result = add_object (data, &st)) == 0))
        {
          /* When processing a directory as part of a depth-first traversal,
         invoke the user's callback function with type=FTW_DPRE
         just before processing any entry in that directory.
         And if the callback sets ftw.skip, then don't process
         any entries of the directory.  */
          if ((data->flags & FTW_DEPTH)
          && (result = (*data->func) (data->dirbuf, &st, FTW_DPRE,
                          &data->ftw)) == 0
          && ! data->ftw.skip)
        result = ftw_dir (data, &st);

          if (result == 0 && (data->flags & FTW_CHDIR))
        {
          /* Change back to the parent directory.  */
          int done = 0;
          if (dir->stream != NULL)
            if (__fchdir (dirfd (dir->stream)) == 0)
              done = 1;

          if (!done)
            {
              if (data->ftw.base == 1)
            {
              if (__chdir ("/") < 0)
                result = -1;
            }
              else
            if (__chdir ("..") < 0)
              result = -1;
            }

          if (result < 0)
            {
              result = (*data->func) (data->dirbuf, NULL, FTW_DCHP,
                &data->ftw);
            }
        }
        }
    }
      else
    result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag],
                &data->ftw);
    }

  return result;
}


static int
internal_function
ftw_dir (struct ftw_data *data, struct FTW_STAT *st)
{
  struct dir_data dir;
  struct dirent64 *d;
  int previous_base = data->ftw.base;
  int result;
  char *startp;

  /* Open the stream for this directory.  This might require that
     another stream has to be closed.  */
  result = open_dir_stream (data, &dir);
  if (result != 0)
    {
      if (errno == EACCES)
    /* We cannot read the directory.  Signal this with a special flag.  */
    result = (*data->func) (data->dirbuf, st, FTW_DNR, &data->ftw);

      return result;
    }

  /* First, report the directory (if not depth-first).  */
  if (!(data->flags & FTW_DEPTH))
    {
      result = (*data->func) (data->dirbuf, st, FTW_D, &data->ftw);
      if (result != 0)
    return result;
    }

  /* If necessary, change to this directory.  */
  if (data->flags & FTW_CHDIR)
    {
      if (__fchdir (dirfd (dir.stream)) < 0)
    {
      if (errno == ENOSYS)
        {
          if (__chdir (data->dirbuf) < 0)
        result = -1;
        }
      else
        result = -1;
    }

      if (result != 0)
    {
      int save_err = errno;
      __closedir (dir.stream);
      __set_errno (save_err);

      if (data->actdir-- == 0)
        data->actdir = data->maxdir - 1;
      data->dirstreams[data->actdir] = NULL;

      /* We cannot change to the directory.
         Signal this with a special flag.  */
      result = (*data->func) (data->dirbuf, st, FTW_DCH, &data->ftw);

      return result;
    }
    }

  /* Next, update the `struct FTW' information.  */
  ++data->ftw.level;
  startp = strchr (data->dirbuf, '\0');
  /* There always must be a directory name.  */
  assert (startp != data->dirbuf);
  if (startp[-1] != '/')
    *startp++ = '/';
  data->ftw.base = startp - data->dirbuf;

  while (dir.stream != NULL && (d = __readdir64 (dir.stream)) != NULL)
    {
      result = process_entry (data, &dir, d->d_name, NAMLEN (d));
      if (result != 0)
    break;
    }

  if (dir.stream != NULL)
    {
      /* The stream is still open.  I.e., we did not need more
     descriptors.  Simply close the stream now.  */
      int save_err = errno;

      assert (dir.content == NULL);

      __closedir (dir.stream);
      __set_errno (save_err);

      if (data->actdir-- == 0)
    data->actdir = data->maxdir - 1;
      data->dirstreams[data->actdir] = NULL;
    }
  else
    {
      int save_err;
      char *runp = dir.content;

      while (result == 0 && *runp != '\0')
    {
      char *endp = strchr (runp, '\0');

      result = process_entry (data, &dir, runp, endp - runp);

      runp = endp + 1;
    }

      save_err = errno;
      free (dir.content);
      __set_errno (save_err);
    }

  /* Prepare the return, revert the `struct FTW' information.  */
  data->dirbuf[data->ftw.base - 1] = '\0';
  --data->ftw.level;
  data->ftw.base = previous_base;

  /* Finally, if we process depth-first report the directory.  */
  if (result == 0 && (data->flags & FTW_DEPTH))
    result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw);

  return result;
}


#ifdef _LIBC
# define ISSLASH(C) ((C) == '/')
# define FILESYSTEM_PREFIX_LEN(Filename) 0
#endif

/* 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 + FILESYSTEM_PREFIX_LEN (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;
}


static int
internal_function
ftw_startup (const char *dir, int is_nftw, NFTW_FUNC_T func, int descriptors,
         int flags)
{
  struct ftw_data data;
  struct FTW_STAT st;
  int result = 0;
  int save_err;
  struct saved_cwd cwd;
  size_t dir_len;

  /* First make sure the parameters are reasonable.  */
  if (dir[0] == '\0')
    {
      __set_errno (ENOENT);
      return -1;
    }

  data.maxdir = descriptors < 1 ? 1 : descriptors;
  data.actdir = 0;
  data.dirstreams = (struct dir_data **) alloca (data.maxdir
                         * sizeof (struct dir_data *));
  if (data.dirstreams == NULL)
    return -1;
  memset (data.dirstreams, '\0', data.maxdir * sizeof (struct dir_data *));

  /* PATH_MAX is always defined when we get here.  */
  dir_len = strlen (dir);
  data.dirbufsize = MAX (2 * dir_len, PATH_MAX);
  data.dirbuf = (char *) malloc (data.dirbufsize);
  if (data.dirbuf == NULL)
    return -1;
  memcpy (data.dirbuf, dir, dir_len + 1);

  data.ftw.level = 0;

  /* Find offset of basename.  */
  data.ftw.base = base_name (data.dirbuf) - data.dirbuf;

  data.flags = flags;

  /* This assignment might seem to be strange but it is what we want.
     The trick is that the first three arguments to the `ftw' and
     `nftw' callback functions are equal.  Therefore we can call in
     every case the callback using the format of the `nftw' version
     and get the correct result since the stack layout for a function
     call in C allows this.  */
  data.func = func;

  /* Since we internally use the complete set of FTW_* values we need
     to reduce the value range before calling a `ftw' callback.  */
  data.cvt_arr = is_nftw ? nftw_arr : ftw_arr;

  /* No object known so far.  */
  data.known_objects = NULL;

  /* Now go to the directory containing the initial file/directory.  */
  if (flags & FTW_CHDIR)
    {
      if (save_cwd (&cwd))
    result = -1;
      else if (data.ftw.base > 0)
    {
      /* Change to the directory the file is in.  In data.dirbuf
         we have a writable copy of the file name.  Just NUL
         terminate it for now and change the directory.  */
      if (data.ftw.base == 1)
        /* I.e., the file is in the root directory.  */
        result = __chdir ("/");
      else
        {
          char ch = data.dirbuf[data.ftw.base - 1];
          data.dirbuf[data.ftw.base - 1] = '\0';
          result = __chdir (data.dirbuf);
          data.dirbuf[data.ftw.base - 1] = ch;
        }
    }
    }

  /* Get stat info for start directory.  */
  if (result == 0)
    {
      const char *name = ((data.flags & FTW_CHDIR)
              ? data.dirbuf + data.ftw.base
              : data.dirbuf);

      if (((flags & FTW_PHYS)
       ? LXSTAT (_STAT_VER, name, &st)
       : XSTAT (_STAT_VER, name, &st)) < 0)
    {
      if (!(flags & FTW_PHYS)
          && errno == ENOENT
          && LXSTAT (_STAT_VER, name, &st) == 0
          && S_ISLNK (st.st_mode))
        result = (*data.func) (data.dirbuf, &st, data.cvt_arr[FTW_SLN],
                   &data.ftw);
      else
        /* No need to call the callback since we cannot say anything
           about the object.  */
        result = -1;
    }
      else
    {
      if (S_ISDIR (st.st_mode))
        {
          /* Remember the device of the initial directory in case
         FTW_MOUNT is given.  */
          data.dev = st.st_dev;

          /* We know this directory now.  */
          if (!(flags & FTW_PHYS))
        result = add_object (&data, &st);

          if (result == 0)
        {
          /* If we're doing a depth-first traversal, give the user
             a chance to prune the top-level directory.  */
          if ((flags & FTW_DEPTH)
              && (result = (*data.func) (data.dirbuf, &st, FTW_DPRE,
                         &data.ftw)) == 0
              && ! data.ftw.skip)
            result = ftw_dir (&data, &st);
        }
        }
      else
        {
          int flag = S_ISLNK (st.st_mode) ? FTW_SL : FTW_F;

          result = (*data.func) (data.dirbuf, &st, data.cvt_arr[flag],
                     &data.ftw);
        }
    }
    }

  /* Return to the start directory (if necessary).  */
  if (flags & FTW_CHDIR)
    {
      save_err = errno;
      /* If restore_cwd fails and there wasn't a prior failure,
     then let this new errno override any prior value.
     FIXME: ideally, we'd be able to return some indication
     of what the failure means.  Otherwise, the caller will
     have a hard time distinguishing between e.g., `out of memory'
     and this sort of failure.  */
      if (restore_cwd (&cwd) && result == 0)
    {
      save_err = errno;
      result = -1;
    }

      __set_errno (save_err);
    }

  /* Free all memory.  */
  save_err = errno;
  __tdestroy (data.known_objects, free);
  free (data.dirbuf);
  __set_errno (save_err);

  return result;
}



/* Entry points.  */

int
FTW_NAME (path, func, descriptors)
     const char *path;
     FTW_FUNC_T func;
     int descriptors;
{
  return ftw_startup (path, 0, (NFTW_FUNC_T) func, descriptors, 0);
}

int
NFTW_NAME (path, func, descriptors, flags)
     const char *path;
     NFTW_FUNC_T func;
     int descriptors;
     int flags;
{
  return ftw_startup (path, 1, func, descriptors, flags);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/dirfd.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/dirfd.c used in: Step 842
/* dirfd.c -- return the file descriptor associated with an open DIR*
   Copyright (C) 2001 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.  */

/* Written by Jim Meyering. */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include "dirfd.h"

int
dirfd (DIR const *dir_p)
{
  return DIR_TO_FD (dir_p);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/obstack.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/obstack.c used in: Step 843
/* obstack.c - subroutines used implicitly by object stack macros
   Copyright (C) 1988-1994, 1996-1999, 2000-2002 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.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include "obstack.h"

/* NOTE BEFORE MODIFYING THIS FILE: This version number must be
   incremented whenever callers compiled using an old obstack.h can no
   longer properly call the functions in this obstack.c.  */
#define OBSTACK_INTERFACE_VERSION 1

/* Comment out all this code if we are using the GNU C Library, and are not
   actually compiling the library itself, and the installed library
   supports the same library interface we do.  This code is part of the GNU
   C Library, but also included in many other GNU distributions.  Compiling
   and linking in this code is a waste when using the GNU C library
   (especially if it is a shared library).  Rather than having every GNU
   program understand `configure --with-gnu-libc' and omit the object
   files, it is simpler to just do this in the source for each such file.  */

#include <stdio.h>      /* Random thing to get __GNU_LIBRARY__.  */
#if !defined _LIBC && defined __GNU_LIBRARY__ && __GNU_LIBRARY__ > 1
# include <gnu-versions.h>
# if _GNU_OBSTACK_INTERFACE_VERSION == OBSTACK_INTERFACE_VERSION
#  define ELIDE_CODE
# endif
#endif

#if defined _LIBC && defined USE_IN_LIBIO
# include <wchar.h>
#endif

#ifndef ELIDE_CODE


# if defined __STDC__ && __STDC__
#  define POINTER void *
# else
#  define POINTER char *
# endif

/* Determine default alignment.  */
struct fooalign {char x; double d;};
# define DEFAULT_ALIGNMENT  \
  ((PTR_INT_TYPE) ((char *) &((struct fooalign *) 0)->d - (char *) 0))
/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
   But in fact it might be less smart and round addresses to as much as
   DEFAULT_ROUNDING.  So we prepare for it to do that.  */
union fooround {long x; double d;};
# define DEFAULT_ROUNDING (sizeof (union fooround))

/* When we copy a long block of data, this is the unit to do it with.
   On some machines, copying successive ints does not work;
   in such a case, redefine COPYING_UNIT to `long' (if that works)
   or `char' as a last resort.  */
# ifndef COPYING_UNIT
#  define COPYING_UNIT int
# endif


/* The functions allocating more room by calling `obstack_chunk_alloc'
   jump to the handler pointed to by `obstack_alloc_failed_handler'.
   This can be set to a user defined function which should either
   abort gracefully or use longjump - but shouldn't return.  This
   variable by default points to the internal function
   `print_and_abort'.  */
# if defined __STDC__ && __STDC__
static void print_and_abort (void);
void (*obstack_alloc_failed_handler) (void) = print_and_abort;
# else
static void print_and_abort ();
void (*obstack_alloc_failed_handler) () = print_and_abort;
# endif

/* Exit value used when `print_and_abort' is used.  */
# if defined __GNU_LIBRARY__ || defined HAVE_STDLIB_H
#  include <stdlib.h>
# endif
# ifndef EXIT_FAILURE
#  define EXIT_FAILURE 1
# endif
int obstack_exit_failure = EXIT_FAILURE;

/* The non-GNU-C macros copy the obstack into this global variable
   to avoid multiple evaluation.  */

struct obstack *_obstack;

/* Define a macro that either calls functions with the traditional malloc/free
   calling interface, or calls functions with the mmalloc/mfree interface
   (that adds an extra first argument), based on the state of use_extra_arg.
   For free, do not use ?:, since some compilers, like the MIPS compilers,
   do not allow (expr) ? void : void.  */

# if defined __STDC__ && __STDC__
#  define CALL_CHUNKFUN(h, size) \
  (((h) -> use_extra_arg) \
   ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
   : (*(struct _obstack_chunk *(*) (long)) (h)->chunkfun) ((size)))

#  define CALL_FREEFUN(h, old_chunk) \
  do { \
    if ((h) -> use_extra_arg) \
      (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
    else \
      (*(void (*) (void *)) (h)->freefun) ((old_chunk)); \
  } while (0)
# else
#  define CALL_CHUNKFUN(h, size) \
  (((h) -> use_extra_arg) \
   ? (*(h)->chunkfun) ((h)->extra_arg, (size)) \
   : (*(struct _obstack_chunk *(*) ()) (h)->chunkfun) ((size)))

#  define CALL_FREEFUN(h, old_chunk) \
  do { \
    if ((h) -> use_extra_arg) \
      (*(h)->freefun) ((h)->extra_arg, (old_chunk)); \
    else \
      (*(void (*) ()) (h)->freefun) ((old_chunk)); \
  } while (0)
# endif



/* Initialize an obstack H for use.  Specify chunk size SIZE (0 means default).
   Objects start on multiples of ALIGNMENT (0 means use default).
   CHUNKFUN is the function to use to allocate chunks,
   and FREEFUN the function to free them.

   Return nonzero if successful, calls obstack_alloc_failed_handler if
   allocation fails.  */

int
_obstack_begin (h, size, alignment, chunkfun, freefun)
     struct obstack *h;
     int size;
     int alignment;
# if defined __STDC__ && __STDC__
     POINTER (*chunkfun) (long);
     void (*freefun) (void *);
# else
     POINTER (*chunkfun) ();
     void (*freefun) ();
# endif
{
  register struct _obstack_chunk *chunk; /* points to new chunk */

  if (alignment == 0)
    alignment = (int) DEFAULT_ALIGNMENT;
  if (size == 0)
    /* Default size is what GNU malloc can fit in a 4096-byte block.  */
    {
      /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
     Use the values for range checking, because if range checking is off,
     the extra bytes won't be missed terribly, but if range checking is on
     and we used a larger request, a whole extra 4096 bytes would be
     allocated.

     These number are irrelevant to the new GNU malloc.  I suspect it is
     less sensitive to the size of the request.  */
      int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
            + 4 + DEFAULT_ROUNDING - 1)
           & ~(DEFAULT_ROUNDING - 1));
      size = 4096 - extra;
    }

# if defined __STDC__ && __STDC__
  h->chunkfun = (struct _obstack_chunk * (*)(void *, long)) chunkfun;
  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
# else
  h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
  h->freefun = freefun;
# endif
  h->chunk_size = size;
  h->alignment_mask = alignment - 1;
  h->use_extra_arg = 0;

  chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
  if (!chunk)
    (*obstack_alloc_failed_handler) ();
  h->next_free = h->object_base = chunk->contents;
  h->chunk_limit = chunk->limit
    = (char *) chunk + h->chunk_size;
  chunk->prev = 0;
  /* The initial chunk now contains no empty object.  */
  h->maybe_empty_object = 0;
  h->alloc_failed = 0;
  return 1;
}

int
_obstack_begin_1 (h, size, alignment, chunkfun, freefun, arg)
     struct obstack *h;
     int size;
     int alignment;
# if defined __STDC__ && __STDC__
     POINTER (*chunkfun) (POINTER, long);
     void (*freefun) (POINTER, POINTER);
# else
     POINTER (*chunkfun) ();
     void (*freefun) ();
# endif
     POINTER arg;
{
  register struct _obstack_chunk *chunk; /* points to new chunk */

  if (alignment == 0)
    alignment = (int) DEFAULT_ALIGNMENT;
  if (size == 0)
    /* Default size is what GNU malloc can fit in a 4096-byte block.  */
    {
      /* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
     Use the values for range checking, because if range checking is off,
     the extra bytes won't be missed terribly, but if range checking is on
     and we used a larger request, a whole extra 4096 bytes would be
     allocated.

     These number are irrelevant to the new GNU malloc.  I suspect it is
     less sensitive to the size of the request.  */
      int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
            + 4 + DEFAULT_ROUNDING - 1)
           & ~(DEFAULT_ROUNDING - 1));
      size = 4096 - extra;
    }

# if defined __STDC__ && __STDC__
  h->chunkfun = (struct _obstack_chunk * (*)(void *,long)) chunkfun;
  h->freefun = (void (*) (void *, struct _obstack_chunk *)) freefun;
# else
  h->chunkfun = (struct _obstack_chunk * (*)()) chunkfun;
  h->freefun = freefun;
# endif
  h->chunk_size = size;
  h->alignment_mask = alignment - 1;
  h->extra_arg = arg;
  h->use_extra_arg = 1;

  chunk = h->chunk = CALL_CHUNKFUN (h, h -> chunk_size);
  if (!chunk)
    (*obstack_alloc_failed_handler) ();
  h->next_free = h->object_base = chunk->contents;
  h->chunk_limit = chunk->limit
    = (char *) chunk + h->chunk_size;
  chunk->prev = 0;
  /* The initial chunk now contains no empty object.  */
  h->maybe_empty_object = 0;
  h->alloc_failed = 0;
  return 1;
}

/* Allocate a new current chunk for the obstack *H
   on the assumption that LENGTH bytes need to be added
   to the current object, or a new object of length LENGTH allocated.
   Copies any partial object from the end of the old chunk
   to the beginning of the new one.  */

void
_obstack_newchunk (h, length)
     struct obstack *h;
     int length;
{
  register struct _obstack_chunk *old_chunk = h->chunk;
  register struct _obstack_chunk *new_chunk;
  register long new_size;
  register long obj_size = h->next_free - h->object_base;
  register long i;
  long already;
  char *object_base;

  /* Compute size for new chunk.  */
  new_size = (obj_size + length) + (obj_size >> 3) + h->alignment_mask + 100;
  if (new_size < h->chunk_size)
    new_size = h->chunk_size;

  /* Allocate and initialize the new chunk.  */
  new_chunk = CALL_CHUNKFUN (h, new_size);
  if (!new_chunk)
    (*obstack_alloc_failed_handler) ();
  h->chunk = new_chunk;
  new_chunk->prev = old_chunk;
  new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;

  /* Compute an aligned object_base in the new chunk */
  object_base =
    __INT_TO_PTR ((__PTR_TO_INT (new_chunk->contents) + h->alignment_mask)
          & ~ (h->alignment_mask));

  /* Move the existing object to the new chunk.
     Word at a time is fast and is safe if the object
     is sufficiently aligned.  */
  if (h->alignment_mask + 1 >= DEFAULT_ALIGNMENT)
    {
      for (i = obj_size / sizeof (COPYING_UNIT) - 1;
       i >= 0; i--)
    ((COPYING_UNIT *)object_base)[i]
      = ((COPYING_UNIT *)h->object_base)[i];
      /* We used to copy the odd few remaining bytes as one extra COPYING_UNIT,
     but that can cross a page boundary on a machine
     which does not do strict alignment for COPYING_UNITS.  */
      already = obj_size / sizeof (COPYING_UNIT) * sizeof (COPYING_UNIT);
    }
  else
    already = 0;
  /* Copy remaining bytes one by one.  */
  for (i = already; i < obj_size; i++)
    object_base[i] = h->object_base[i];

  /* If the object just copied was the only data in OLD_CHUNK,
     free that chunk and remove it from the chain.
     But not if that chunk might contain an empty object.  */
  if (h->object_base == old_chunk->contents && ! h->maybe_empty_object)
    {
      new_chunk->prev = old_chunk->prev;
      CALL_FREEFUN (h, old_chunk);
    }

  h->object_base = object_base;
  h->next_free = h->object_base + obj_size;
  /* The new chunk certainly contains no empty object yet.  */
  h->maybe_empty_object = 0;
}

/* Return nonzero if object OBJ has been allocated from obstack H.
   This is here for debugging.
   If you use it in a program, you are probably losing.  */

# if defined __STDC__ && __STDC__
/* Suppress -Wmissing-prototypes warning.  We don't want to declare this in
   obstack.h because it is just for debugging.  */
int _obstack_allocated_p (struct obstack *h, POINTER obj);
# endif

int
_obstack_allocated_p (h, obj)
     struct obstack *h;
     POINTER obj;
{
  register struct _obstack_chunk *lp;   /* below addr of any objects in this chunk */
  register struct _obstack_chunk *plp;  /* point to previous chunk if any */

  lp = (h)->chunk;
  /* We use >= rather than > since the object cannot be exactly at
     the beginning of the chunk but might be an empty object exactly
     at the end of an adjacent chunk.  */
  while (lp != 0 && ((POINTER) lp >= obj || (POINTER) (lp)->limit < obj))
    {
      plp = lp->prev;
      lp = plp;
    }
  return lp != 0;
}


/* Free objects in obstack H, including OBJ and everything allocate
   more recently than OBJ.  If OBJ is zero, free everything in H.  */

# undef obstack_free

/* This function has two names with identical definitions.
   This is the first one, called from non-ANSI code.  */

void
_obstack_free (h, obj)
     struct obstack *h;
     POINTER obj;
{
  register struct _obstack_chunk *lp;   /* below addr of any objects in this chunk */
  register struct _obstack_chunk *plp;  /* point to previous chunk if any */

  lp = h->chunk;
  /* We use >= because there cannot be an object at the beginning of a chunk.
     But there can be an empty object at that address
     at the end of another chunk.  */
  while (lp != 0 && ((POINTER) lp >= obj || (POINTER) (lp)->limit < obj))
    {
      plp = lp->prev;
      CALL_FREEFUN (h, lp);
      lp = plp;
      /* If we switch chunks, we can't tell whether the new current
     chunk contains an empty object, so assume that it may.  */
      h->maybe_empty_object = 1;
    }
  if (lp)
    {
      h->object_base = h->next_free = (char *) (obj);
      h->chunk_limit = lp->limit;
      h->chunk = lp;
    }
  else if (obj != 0)
    /* obj is not in any of the chunks! */
    abort ();
}

/* This function is used from ANSI code.  */

void
obstack_free (h, obj)
     struct obstack *h;
     POINTER obj;
{
  register struct _obstack_chunk *lp;   /* below addr of any objects in this chunk */
  register struct _obstack_chunk *plp;  /* point to previous chunk if any */

  lp = h->chunk;
  /* We use >= because there cannot be an object at the beginning of a chunk.
     But there can be an empty object at that address
     at the end of another chunk.  */
  while (lp != 0 && ((POINTER) lp >= obj || (POINTER) (lp)->limit < obj))
    {
      plp = lp->prev;
      CALL_FREEFUN (h, lp);
      lp = plp;
      /* If we switch chunks, we can't tell whether the new current
     chunk contains an empty object, so assume that it may.  */
      h->maybe_empty_object = 1;
    }
  if (lp)
    {
      h->object_base = h->next_free = (char *) (obj);
      h->chunk_limit = lp->limit;
      h->chunk = lp;
    }
  else if (obj != 0)
    /* obj is not in any of the chunks! */
    abort ();
}


int
_obstack_memory_used (h)
     struct obstack *h;
{
  register struct _obstack_chunk* lp;
  register int nbytes = 0;

  for (lp = h->chunk; lp != 0; lp = lp->prev)
    {
      nbytes += lp->limit - (char *) lp;
    }
  return nbytes;
}


/* Define the error handler.  */
# ifdef _LIBC
#  include <libintl.h>
# else
#  include "gettext.h"
# endif
# define _(msgid) gettext (msgid)

# if defined _LIBC && defined USE_IN_LIBIO
#  include <libio/iolibio.h>
#  define fputs(s, f) _IO_fputs (s, f)
# endif

# ifndef __attribute__
/* This feature is available in gcc versions 2.5 and later.  */
#  if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
#   define __attribute__(Spec) /* empty */
#  endif
# endif

static void
__attribute__ ((noreturn))
print_and_abort ()
{
  /* Don't change any of these strings.  Yes, it would be possible to add
     the newline to the string and use fputs or so.  But this must not
     happen because the "memory exhausted" message appears in other places
     like this and the translation should be reused instead of creating
     a very similar string which requires a separate translation.  */
# if defined _LIBC && defined USE_IN_LIBIO
  if (_IO_fwide (stderr, 0) > 0)
    __fwprintf (stderr, L"%s\n", _("memory exhausted"));
  else
# endif
    fprintf (stderr, "%s\n", _("memory exhausted"));
  exit (obstack_exit_failure);
}


# if 0
/* These are now turned off because the applications do not use it
   and it uses bcopy via obstack_grow, which causes trouble on sysV.  */

/* Now define the functional versions of the obstack macros.
   Define them to simply use the corresponding macros to do the job.  */

#  if defined __STDC__ && __STDC__
/* These function definitions do not work with non-ANSI preprocessors;
   they won't pass through the macro names in parentheses.  */

/* The function names appear in parentheses in order to prevent
   the macro-definitions of the names from being expanded there.  */

POINTER (obstack_base) (obstack)
     struct obstack *obstack;
{
  return obstack_base (obstack);
}

POINTER (obstack_next_free) (obstack)
     struct obstack *obstack;
{
  return obstack_next_free (obstack);
}

int (obstack_object_size) (obstack)
     struct obstack *obstack;
{
  return obstack_object_size (obstack);
}

int (obstack_room) (obstack)
     struct obstack *obstack;
{
  return obstack_room (obstack);
}

int (obstack_make_room) (obstack, length)
     struct obstack *obstack;
     int length;
{
  return obstack_make_room (obstack, length);
}

void (obstack_grow) (obstack, data, length)
     struct obstack *obstack;
     const POINTER data;
     int length;
{
  obstack_grow (obstack, data, length);
}

void (obstack_grow0) (obstack, data, length)
     struct obstack *obstack;
     const POINTER data;
     int length;
{
  obstack_grow0 (obstack, data, length);
}

void (obstack_1grow) (obstack, character)
     struct obstack *obstack;
     int character;
{
  obstack_1grow (obstack, character);
}

void (obstack_blank) (obstack, length)
     struct obstack *obstack;
     int length;
{
  obstack_blank (obstack, length);
}

void (obstack_1grow_fast) (obstack, character)
     struct obstack *obstack;
     int character;
{
  obstack_1grow_fast (obstack, character);
}

void (obstack_blank_fast) (obstack, length)
     struct obstack *obstack;
     int length;
{
  obstack_blank_fast (obstack, length);
}

POINTER (obstack_finish) (obstack)
     struct obstack *obstack;
{
  return obstack_finish (obstack);
}

POINTER (obstack_alloc) (obstack, length)
     struct obstack *obstack;
     int length;
{
  return obstack_alloc (obstack, length);
}

POINTER (obstack_copy) (obstack, address, length)
     struct obstack *obstack;
     const POINTER address;
     int length;
{
  return obstack_copy (obstack, address, length);
}

POINTER (obstack_copy0) (obstack, address, length)
     struct obstack *obstack;
     const POINTER address;
     int length;
{
  return obstack_copy0 (obstack, address, length);
}

#  endif /* __STDC__ */

# endif /* 0 */

#endif  /* !ELIDE_CODE */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/strverscmp.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/strverscmp.c used in: Step 844
/* Compare strings while treating digits characters numerically.
   Copyright (C) 1997, 2000, 2002 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   Contributed by Jean-François Bignolles <bignolle@ecoledoc.ibp.fr>, 1997.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <string.h>
#include <ctype.h>

/* states: S_N: normal, S_I: comparing integral part, S_F: comparing
           fractional parts, S_Z: idem but with leading Zeroes only */
#define S_N    0x0
#define S_I    0x4
#define S_F    0x8
#define S_Z    0xC

/* result_type: CMP: return diff; LEN: compare using len_diff/diff */
#define CMP    2
#define LEN    3


/* ISDIGIT differs from isdigit, as follows:
   - Its arg may be any int or unsigned int; it need not be an unsigned char.
   - It's guaranteed to evaluate its argument exactly once.
   - It's typically faster.
   POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
   ISDIGIT_LOCALE unless it's important to use the locale's definition
   of `digit' even when the host does not conform to POSIX.  */
#define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)

#undef __strverscmp
#undef strverscmp

#ifndef weak_alias
# define __strverscmp strverscmp
#endif

/* Compare S1 and S2 as strings holding indices/version numbers,
   returning less than, equal to or greater than zero if S1 is less than,
   equal to or greater than S2 (for more info, see the texinfo doc).
*/

int
__strverscmp (const char *s1, const char *s2)
{
  const unsigned char *p1 = (const unsigned char *) s1;
  const unsigned char *p2 = (const unsigned char *) s2;
  unsigned char c1, c2;
  int state;
  int diff;

  /* Symbol(s)    0       [1-9]   others  (padding)
     Transition   (10) 0  (01) d  (00) x  (11) -   */
  static const unsigned int next_state[] =
  {
      /* state    x    d    0    - */
      /* S_N */  S_N, S_I, S_Z, S_N,
      /* S_I */  S_N, S_I, S_I, S_I,
      /* S_F */  S_N, S_F, S_F, S_F,
      /* S_Z */  S_N, S_F, S_Z, S_Z
  };

  static const int result_type[] =
  {
      /* state   x/x  x/d  x/0  x/-  d/x  d/d  d/0  d/-
                 0/x  0/d  0/0  0/-  -/x  -/d  -/0  -/- */

      /* S_N */  CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
                 CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
      /* S_I */  CMP, -1,  -1,  CMP,  1,  LEN, LEN, CMP,
                  1,  LEN, LEN, CMP, CMP, CMP, CMP, CMP,
      /* S_F */  CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
                 CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
      /* S_Z */  CMP,  1,   1,  CMP, -1,  CMP, CMP, CMP,
                 -1,  CMP, CMP, CMP
  };

  if (p1 == p2)
    return 0;

  c1 = *p1++;
  c2 = *p2++;
  /* Hint: '0' is a digit too.  */
  state = S_N | ((c1 == '0') + (ISDIGIT (c1) != 0));

  while ((diff = c1 - c2) == 0 && c1 != '\0')
    {
      state = next_state[state];
      c1 = *p1++;
      c2 = *p2++;
      state |= (c1 == '0') + (ISDIGIT (c1) != 0);
    }

  state = result_type[state << 2 | ((c2 == '0') + (ISDIGIT (c2) != 0))];

  switch (state)
    {
    case CMP:
      return diff;

    case LEN:
      while (ISDIGIT (*p1++))
    if (!ISDIGIT (*p2++))
      return 1;

      return ISDIGIT (*p2) ? -1 : diff;

    default:
      return state;
    }
}
#ifdef weak_alias
weak_alias (__strverscmp, strverscmp)
#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/tsearch.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/tsearch.c used in: Step 846
/* Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   Contributed by Bernd Schmidt <crux@Pool.Informatik.RWTH-Aachen.DE>, 1997.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

/* Tree search for red/black trees.
   The algorithm for adding nodes is taken from one of the many "Algorithms"
   books by Robert Sedgewick, although the implementation differs.
   The algorithm for deleting nodes can probably be found in a book named
   "Introduction to Algorithms" by Cormen/Leiserson/Rivest.  At least that's
   the book that my professor took most algorithms from during the "Data
   Structures" course...

   Totally public domain.  */

/* Red/black trees are binary trees in which the edges are colored either red
   or black.  They have the following properties:
   1. The number of black edges on every path from the root to a leaf is
      constant.
   2. No two red edges are adjacent.
   Therefore there is an upper bound on the length of every path, it's
   O(log n) where n is the number of nodes in the tree.  No path can be longer
   than 1+2*P where P is the length of the shortest path in the tree.
   Useful for the implementation:
   3. If one of the children of a node is NULL, then the other one is red
      (if it exists).

   In the implementation, not the edges are colored, but the nodes.  The color
   interpreted as the color of the edge leading to this node.  The color is
   meaningless for the root node, but we color the root node black for
   convenience.  All added nodes are red initially.

   Adding to a red/black tree is rather easy.  The right place is searched
   with a usual binary tree search.  Additionally, whenever a node N is
   reached that has two red successors, the successors are colored black and
   the node itself colored red.  This moves red edges up the tree where they
   pose less of a problem once we get to really insert the new node.  Changing
   N's color to red may violate rule 2, however, so rotations may become
   necessary to restore the invariants.  Adding a new red leaf may violate
   the same rule, so afterwards an additional check is run and the tree
   possibly rotated.

   Deleting is hairy.  There are mainly two nodes involved: the node to be
   deleted (n1), and another node that is to be unchained from the tree (n2).
   If n1 has a successor (the node with a smallest key that is larger than
   n1), then the successor becomes n2 and its contents are copied into n1,
   otherwise n1 becomes n2.
   Unchaining a node may violate rule 1: if n2 is black, one subtree is
   missing one black edge afterwards.  The algorithm must try to move this
   error upwards towards the root, so that the subtree that does not have
   enough black edges becomes the whole tree.  Once that happens, the error
   has disappeared.  It may not be necessary to go all the way up, since it
   is possible that rotations and recoloring can fix the error before that.

   Although the deletion algorithm must walk upwards through the tree, we
   do not store parent pointers in the nodes.  Instead, delete allocates a
   small array of parent pointers and fills it while descending the tree.
   Since we know that the length of a path is O(log n), where n is the number
   of nodes, this is likely to use less memory.  */

/* Tree rotations look like this:
      A                C
     / \              / \
    B   C            A   G
   / \ / \  -->     / \
   D E F G         B   F
                  / \
                 D   E

   In this case, A has been rotated left.  This preserves the ordering of the
   binary tree.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#if __GNUC__
# define alloca __builtin_alloca
#else
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
 #  pragma alloca
#  else
char *alloca ();
#  endif
# endif
#endif

#include <stdlib.h>
#include <string.h>
#include <search.h>

#ifndef weak_alias
# define __tsearch tsearch
# define __tfind tfind
# define __tdelete tdelete
# define __twalk twalk
# define __tdestroy tdestroy
#endif

#ifndef _LIBC
# define weak_alias(f,g)
# define internal_function
#endif

typedef struct node_t
{
  /* Callers expect this to be the first element in the structure - do not
     move!  */
  const void *key;
  struct node_t *left;
  struct node_t *right;
  unsigned int red:1;
} *node;
typedef const struct node_t *const_node;

#undef DEBUGGING

#ifdef DEBUGGING

/* Routines to check tree invariants.  */

# include <assert.h>

# define CHECK_TREE(a) check_tree(a)

static void
check_tree_recurse (node p, int d_sofar, int d_total)
{
  if (p == NULL)
    {
      assert (d_sofar == d_total);
      return;
    }

  check_tree_recurse (p->left, d_sofar + (p->left && !p->left->red), d_total);
  check_tree_recurse (p->right, d_sofar + (p->right && !p->right->red), d_total);
  if (p->left)
    assert (!(p->left->red && p->red));
  if (p->right)
    assert (!(p->right->red && p->red));
}

static void
check_tree (node root)
{
  int cnt = 0;
  node p;
  if (root == NULL)
    return;
  root->red = 0;
  for(p = root->left; p; p = p->left)
    cnt += !p->red;
  check_tree_recurse (root, 0, cnt);
}


#else

# define CHECK_TREE(a)

#endif

/* Possibly "split" a node with two red successors, and/or fix up two red
   edges in a row.  ROOTP is a pointer to the lowest node we visited, PARENTP
   and GPARENTP pointers to its parent/grandparent.  P_R and GP_R contain the
   comparison values that determined which way was taken in the tree to reach
   ROOTP.  MODE is 1 if we need not do the split, but must check for two red
   edges between GPARENTP and ROOTP.  */
static void
maybe_split_for_insert (node *rootp, node *parentp, node *gparentp,
            int p_r, int gp_r, int mode)
{
  node root = *rootp;
  node *rp, *lp;
  rp = &(*rootp)->right;
  lp = &(*rootp)->left;

  /* See if we have to split this node (both successors red).  */
  if (mode == 1
      || ((*rp) != NULL && (*lp) != NULL && (*rp)->red && (*lp)->red))
    {
      /* This node becomes red, its successors black.  */
      root->red = 1;
      if (*rp)
    (*rp)->red = 0;
      if (*lp)
    (*lp)->red = 0;

      /* If the parent of this node is also red, we have to do
     rotations.  */
      if (parentp != NULL && (*parentp)->red)
    {
      node gp = *gparentp;
      node p = *parentp;
      /* There are two main cases:
         1. The edge types (left or right) of the two red edges differ.
         2. Both red edges are of the same type.
         There exist two symmetries of each case, so there is a total of
         4 cases.  */
      if ((p_r > 0) != (gp_r > 0))
        {
          /* Put the child at the top of the tree, with its parent
         and grandparent as successors.  */
          p->red = 1;
          gp->red = 1;
          root->red = 0;
          if (p_r < 0)
        {
          /* Child is left of parent.  */
          p->left = *rp;
          *rp = p;
          gp->right = *lp;
          *lp = gp;
        }
          else
        {
          /* Child is right of parent.  */
          p->right = *lp;
          *lp = p;
          gp->left = *rp;
          *rp = gp;
        }
          *gparentp = root;
        }
      else
        {
          *gparentp = *parentp;
          /* Parent becomes the top of the tree, grandparent and
         child are its successors.  */
          p->red = 0;
          gp->red = 1;
          if (p_r < 0)
        {
          /* Left edges.  */
          gp->left = p->right;
          p->right = gp;
        }
          else
        {
          /* Right edges.  */
          gp->right = p->left;
          p->left = gp;
        }
        }
    }
    }
}

/* Find or insert datum into search tree.
   KEY is the key to be located, ROOTP is the address of tree root,
   COMPAR the ordering function.  */
void *
__tsearch (const void *key, void **vrootp, __compar_fn_t compar)
{
  node q;
  node *parentp = NULL, *gparentp = NULL;
  node *rootp = (node *) vrootp;
  node *nextp;
  int r = 0, p_r = 0, gp_r = 0; /* No they might not, Mr Compiler.  */

  if (rootp == NULL)
    return NULL;

  /* This saves some additional tests below.  */
  if (*rootp != NULL)
    (*rootp)->red = 0;

  CHECK_TREE (*rootp);

  nextp = rootp;
  while (*nextp != NULL)
    {
      node root = *rootp;
      r = (*compar) (key, root->key);
      if (r == 0)
    return root;

      maybe_split_for_insert (rootp, parentp, gparentp, p_r, gp_r, 0);
      /* If that did any rotations, parentp and gparentp are now garbage.
     That doesn't matter, because the values they contain are never
     used again in that case.  */

      nextp = r < 0 ? &root->left : &root->right;
      if (*nextp == NULL)
    break;

      gparentp = parentp;
      parentp = rootp;
      rootp = nextp;

      gp_r = p_r;
      p_r = r;
    }

  q = (struct node_t *) malloc (sizeof (struct node_t));
  if (q != NULL)
    {
      *nextp = q;           /* link new node to old */
      q->key = key;         /* initialize new node */
      q->red = 1;
      q->left = q->right = NULL;
    }
  if (nextp != rootp)
    /* There may be two red edges in a row now, which we must avoid by
       rotating the tree.  */
    maybe_split_for_insert (nextp, rootp, parentp, r, p_r, 1);

  return q;
}
#ifdef weak_alias
weak_alias (__tsearch, tsearch)
#endif


/* Find datum in search tree.
   KEY is the key to be located, ROOTP is the address of tree root,
   COMPAR the ordering function.  */
void *
__tfind (key, vrootp, compar)
     const void *key;
     void *const *vrootp;
     __compar_fn_t compar;
{
  node *rootp = (node *) vrootp;

  if (rootp == NULL)
    return NULL;

  CHECK_TREE (*rootp);

  while (*rootp != NULL)
    {
      node root = *rootp;
      int r;

      r = (*compar) (key, root->key);
      if (r == 0)
    return root;

      rootp = r < 0 ? &root->left : &root->right;
    }
  return NULL;
}
#ifdef weak_alias
weak_alias (__tfind, tfind)
#endif


/* Delete node with given key.
   KEY is the key to be deleted, ROOTP is the address of the root of tree,
   COMPAR the comparison function.  */
void *
__tdelete (const void *key, void **vrootp, __compar_fn_t compar)
{
  node p, q, r, retval;
  int cmp;
  node *rootp = (node *) vrootp;
  node root, unchained;
  /* Stack of nodes so we remember the parents without recursion.  It's
     _very_ unlikely that there are paths longer than 40 nodes.  The tree
     would need to have around 250.000 nodes.  */
  int stacksize = 40;
  int sp = 0;
  node **nodestack = (node **) alloca (sizeof (node *) * stacksize);

  if (rootp == NULL)
    return NULL;
  p = *rootp;
  if (p == NULL)
    return NULL;

  CHECK_TREE (p);

  while ((cmp = (*compar) (key, (*rootp)->key)) != 0)
    {
      if (sp == stacksize)
    {
      node **newstack;
      stacksize += 20;
      newstack = (node **) alloca (sizeof (node *) * stacksize);
      nodestack = memcpy (newstack, nodestack, sp * sizeof (node *));
    }

      nodestack[sp++] = rootp;
      p = *rootp;
      rootp = ((cmp < 0)
           ? &(*rootp)->left
           : &(*rootp)->right);
      if (*rootp == NULL)
    return NULL;
    }

  /* This is bogus if the node to be deleted is the root... this routine
     really should return an integer with 0 for success, -1 for failure
     and errno = ESRCH or something.  */
  retval = p;

  /* We don't unchain the node we want to delete. Instead, we overwrite
     it with its successor and unchain the successor.  If there is no
     successor, we really unchain the node to be deleted.  */

  root = *rootp;

  r = root->right;
  q = root->left;

  if (q == NULL || r == NULL)
    unchained = root;
  else
    {
      node *parent = rootp, *up = &root->right;
      for (;;)
    {
      if (sp == stacksize)
        {
          node **newstack;
          stacksize += 20;
          newstack = (node **) alloca (sizeof (node *) * stacksize);
          nodestack = memcpy (newstack, nodestack, sp * sizeof (node *));
        }
      nodestack[sp++] = parent;
      parent = up;
      if ((*up)->left == NULL)
        break;
      up = &(*up)->left;
    }
      unchained = *up;
    }

  /* We know that either the left or right successor of UNCHAINED is NULL.
     R becomes the other one, it is chained into the parent of UNCHAINED.  */
  r = unchained->left;
  if (r == NULL)
    r = unchained->right;
  if (sp == 0)
    *rootp = r;
  else
    {
      q = *nodestack[sp-1];
      if (unchained == q->right)
    q->right = r;
      else
    q->left = r;
    }

  if (unchained != root)
    root->key = unchained->key;
  if (!unchained->red)
    {
      /* Now we lost a black edge, which means that the number of black
     edges on every path is no longer constant.  We must balance the
     tree.  */
      /* NODESTACK now contains all parents of R.  R is likely to be NULL
     in the first iteration.  */
      /* NULL nodes are considered black throughout - this is necessary for
     correctness.  */
      while (sp > 0 && (r == NULL || !r->red))
    {
      node *pp = nodestack[sp - 1];
      p = *pp;
      /* Two symmetric cases.  */
      if (r == p->left)
        {
          /* Q is R's brother, P is R's parent.  The subtree with root
         R has one black edge less than the subtree with root Q.  */
          q = p->right;
          if (q != NULL && q->red)
        {
          /* If Q is red, we know that P is black. We rotate P left
             so that Q becomes the top node in the tree, with P below
             it.  P is colored red, Q is colored black.
             This action does not change the black edge count for any
             leaf in the tree, but we will be able to recognize one
             of the following situations, which all require that Q
             is black.  */
          q->red = 0;
          p->red = 1;
          /* Left rotate p.  */
          p->right = q->left;
          q->left = p;
          *pp = q;
          /* Make sure pp is right if the case below tries to use
             it.  */
          nodestack[sp++] = pp = &q->left;
          q = p->right;
        }
          /* We know that Q can't be NULL here.  We also know that Q is
         black.  */
          if ((q->left == NULL || !q->left->red)
          && (q->right == NULL || !q->right->red))
        {
          /* Q has two black successors.  We can simply color Q red.
             The whole subtree with root P is now missing one black
             edge.  Note that this action can temporarily make the
             tree invalid (if P is red).  But we will exit the loop
             in that case and set P black, which both makes the tree
             valid and also makes the black edge count come out
             right.  If P is black, we are at least one step closer
             to the root and we'll try again the next iteration.  */
          q->red = 1;
          r = p;
        }
          else
        {
          /* Q is black, one of Q's successors is red.  We can
             repair the tree with one operation and will exit the
             loop afterwards.  */
          if (q->right == NULL || !q->right->red)
            {
              /* The left one is red.  We perform the same action as
             in maybe_split_for_insert where two red edges are
             adjacent but point in different directions:
             Q's left successor (let's call it Q2) becomes the
             top of the subtree we are looking at, its parent (Q)
             and grandparent (P) become its successors. The former
             successors of Q2 are placed below P and Q.
             P becomes black, and Q2 gets the color that P had.
             This changes the black edge count only for node R and
             its successors.  */
              node q2 = q->left;
              q2->red = p->red;
              p->right = q2->left;
              q->left = q2->right;
              q2->right = q;
              q2->left = p;
              *pp = q2;
              p->red = 0;
            }
          else
            {
              /* It's the right one.  Rotate P left. P becomes black,
             and Q gets the color that P had.  Q's right successor
             also becomes black.  This changes the black edge
             count only for node R and its successors.  */
              q->red = p->red;
              p->red = 0;

              q->right->red = 0;

              /* left rotate p */
              p->right = q->left;
              q->left = p;
              *pp = q;
            }

          /* We're done.  */
          sp = 1;
          r = NULL;
        }
        }
      else
        {
          /* Comments: see above.  */
          q = p->left;
          if (q != NULL && q->red)
        {
          q->red = 0;
          p->red = 1;
          p->left = q->right;
          q->right = p;
          *pp = q;
          nodestack[sp++] = pp = &q->right;
          q = p->left;
        }
          if ((q->right == NULL || !q->right->red)
               && (q->left == NULL || !q->left->red))
        {
          q->red = 1;
          r = p;
        }
          else
        {
          if (q->left == NULL || !q->left->red)
            {
              node q2 = q->right;
              q2->red = p->red;
              p->left = q2->right;
              q->right = q2->left;
              q2->left = q;
              q2->right = p;
              *pp = q2;
              p->red = 0;
            }
          else
            {
              q->red = p->red;
              p->red = 0;
              q->left->red = 0;
              p->left = q->right;
              q->right = p;
              *pp = q;
            }
          sp = 1;
          r = NULL;
        }
        }
      --sp;
    }
      if (r != NULL)
    r->red = 0;
    }

  free (unchained);
  return retval;
}
#ifdef weak_alias
weak_alias (__tdelete, tdelete)
#endif


/* Walk the nodes of a tree.
   ROOT is the root of the tree to be walked, ACTION the function to be
   called at each node.  LEVEL is the level of ROOT in the whole tree.  */
static void
internal_function
trecurse (const void *vroot, __action_fn_t action, int level)
{
  const_node root = (const_node) vroot;

  if (root->left == NULL && root->right == NULL)
    (*action) (root, leaf, level);
  else
    {
      (*action) (root, preorder, level);
      if (root->left != NULL)
    trecurse (root->left, action, level + 1);
      (*action) (root, postorder, level);
      if (root->right != NULL)
    trecurse (root->right, action, level + 1);
      (*action) (root, endorder, level);
    }
}


/* Walk the nodes of a tree.
   ROOT is the root of the tree to be walked, ACTION the function to be
   called at each node.  */
void
__twalk (const void *vroot, __action_fn_t action)
{
  const_node root = (const_node) vroot;

  CHECK_TREE (root);

  if (root != NULL && action != NULL)
    trecurse (root, action, 0);
}
#ifdef weak_alias
weak_alias (__twalk, twalk)
#endif



/* The standardized functions miss an important functionality: the
   tree cannot be removed easily.  We provide a function to do this.  */
static void
internal_function
tdestroy_recurse (node root, void (*freefct)(void *))
{
  if (root->left != NULL)
    tdestroy_recurse (root->left, freefct);
  if (root->right != NULL)
    tdestroy_recurse (root->right, freefct);
  (*freefct) ((void *) root->key);
  /* Free the node itself.  */
  free (root);
}

void
__tdestroy (void *vroot, void (*freefct)(void *))
{
  node root = (node) vroot;

  CHECK_TREE (root);

  if (root != NULL)
    tdestroy_recurse (root, freefct);
}
#ifdef weak_alias
weak_alias (__tdestroy, tdestroy)
#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/cat.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/cat.c used in: Step 849
/* cat -- concatenate files and print on the standard output.
   Copyright (C) 88, 90, 91, 1995-2002 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.  */

/* Differences from the Unix cat:
   * Always unbuffered, -u is ignored.
   * Usually much faster than other versions of cat, the difference
   is especially apparent when using the -v option.

   By tege@sics.se, Torbjorn Granlund, advised by rms, Richard Stallman. */

#include <config.h>

#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#ifndef _POSIX_SOURCE
# include <sys/ioctl.h>
#endif
#include "system.h"
#include "closeout.h"
#include "error.h"
#include "full-write.h"
#include "safe-read.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "cat"

#define AUTHORS N_ ("Torbjorn Granlund and Richard M. Stallman")

/* Undefine, to avoid warning about redefinition on some systems.  */
#undef max
#define max(h,i) ((h) > (i) ? (h) : (i))

/* Name under which this program was invoked.  */
char *program_name;

/* Name of input file.  May be "-".  */
static char *infile;

/* Descriptor on which input file is open.  */
static int input_desc;

/* Buffer for line numbers.
   An 11 digit counter may overflow within an hour on a P2/466,
   an 18 digit counter needs about 1000y */
#define LINE_COUNTER_BUF_LEN 20
static char line_buf[LINE_COUNTER_BUF_LEN] =
  {
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '0',
    '\t', '\0'
  };

/* Position in `line_buf' where printing starts.  This will not change
   unless the number of lines is larger than 999999.  */
static char *line_num_print = line_buf + LINE_COUNTER_BUF_LEN - 8;

/* Position of the first digit in `line_buf'.  */
static char *line_num_start = line_buf + LINE_COUNTER_BUF_LEN - 3;

/* Position of the last digit in `line_buf'.  */
static char *line_num_end = line_buf + LINE_COUNTER_BUF_LEN - 3;

/* Preserves the `cat' function's local `newlines' between invocations.  */
static int newlines2 = 0;

/* Count of non-fatal error conditions.  */
static int exit_status = 0;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION] [FILE]...\n\
"),
          program_name);
      fputs (_("\
Concatenate FILE(s), or standard input, to standard output.\n\
\n\
  -A, --show-all           equivalent to -vET\n\
  -b, --number-nonblank    number nonblank output lines\n\
  -e                       equivalent to -vE\n\
  -E, --show-ends          display $ at end of each line\n\
  -n, --number             number all output lines\n\
  -s, --squeeze-blank      never more than one single blank line\n\
"), stdout);
      fputs (_("\
  -t                       equivalent to -vT\n\
  -T, --show-tabs          display TAB characters as ^I\n\
  -u                       (ignored)\n\
  -v, --show-nonprinting   use ^ and M- notation, except for LFD and TAB\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
With no FILE, or when FILE is -, read standard input.\n\
"), stdout);
#if O_BINARY
      fputs (_("\
\n\
  -B, --binary             use binary writes to the console device.\n\n\
"), stdout);
#endif
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Compute the next line number.  */

static void
next_line_num (void)
{
  char *endp = line_num_end;
  do
    {
      if ((*endp)++ < '9')
    return;
      *endp-- = '0';
    }
  while (endp >= line_num_start);
  if (line_num_start > line_buf)
    *--line_num_start = '1';
  else
    *line_buf = '>';
  if (line_num_start < line_num_print)
    line_num_print--;
}

/* Plain cat.  Copies the file behind `input_desc' to STDOUT_FILENO.  */

static void
simple_cat (
     /* Pointer to the buffer, used by reads and writes.  */
     char *buf,

     /* Number of characters preferably read or written by each read and write
        call.  */
     int bufsize)
{
  /* Actual number of characters read, and therefore written.  */
  size_t n_read;

  /* Loop until the end of the file.  */

  for (;;)
    {
      /* Read a block of input.  */

      n_read = safe_read (input_desc, buf, bufsize);
      if (n_read == SAFE_READ_ERROR)
    {
      error (0, errno, "%s", infile);
      exit_status = 1;
      return;
    }

      /* End of this file?  */

      if (n_read == 0)
    break;

      /* Write this block out.  */

      {
    /* The following is ok, since we know that 0 < n_read.  */
    size_t n = n_read;
    if (full_write (STDOUT_FILENO, buf, n) != n)
      error (EXIT_FAILURE, errno, _("write error"));
      }
    }
}

/* Cat the file behind INPUT_DESC to the file behind OUTPUT_DESC.
   Called if any option more than -u was specified.

   A newline character is always put at the end of the buffer, to make
   an explicit test for buffer end unnecessary.  */

static void
cat (
     /* Pointer to the beginning of the input buffer.  */
     char *inbuf,

     /* Number of characters read in each read call.  */
     size_t insize,

     /* Pointer to the beginning of the output buffer.  */
     char *outbuf,

     /* Number of characters written by each write call.  */
     size_t outsize,

     /* Variables that have values according to the specified options.  */
     int quote,
     int output_tabs,
     int numbers,
     int numbers_at_empty_lines,
     int mark_line_ends,
     int squeeze_empty_lines)
{
  /* Last character read from the input buffer.  */
  unsigned char ch;

  /* Pointer to the next character in the input buffer.  */
  char *bpin;

  /* Pointer to the first non-valid byte in the input buffer, i.e. the
     current end of the buffer.  */
  char *eob;

  /* Pointer to the position where the next character shall be written.  */
  char *bpout;

  /* Number of characters read by the last read call.  */
  size_t n_read;

  /* Determines how many consecutive newlines there have been in the
     input.  0 newlines makes NEWLINES -1, 1 newline makes NEWLINES 1,
     etc.  Initially 0 to indicate that we are at the beginning of a
     new line.  The "state" of the procedure is determined by
     NEWLINES.  */
  int newlines = newlines2;

#ifdef FIONREAD
  /* If nonzero, use the FIONREAD ioctl, as an optimization.
     (On Ultrix, it is not supported on NFS filesystems.)  */
  int use_fionread = 1;
#endif

  /* The inbuf pointers are initialized so that BPIN > EOB, and thereby input
     is read immediately.  */

  eob = inbuf;
  bpin = eob + 1;

  bpout = outbuf;

  for (;;)
    {
      do
    {
      /* Write if there are at least OUTSIZE bytes in OUTBUF.  */

      if (outbuf + outsize <= bpout)
        {
          char *wp = outbuf;
          size_t remaining_bytes;
          do
        {
          if (full_write (STDOUT_FILENO, wp, outsize) != outsize)
            error (EXIT_FAILURE, errno, _("write error"));
          wp += outsize;
          remaining_bytes = bpout - wp;
        }
          while (outsize <= remaining_bytes);

          /* Move the remaining bytes to the beginning of the
         buffer.  */

          memmove (outbuf, wp, remaining_bytes);
          bpout = outbuf + remaining_bytes;
        }

      /* Is INBUF empty?  */

      if (bpin > eob)
        {
#ifdef FIONREAD
          int n_to_read = 0;

          /* Is there any input to read immediately?
         If not, we are about to wait,
         so write all buffered output before waiting.  */

          if (use_fionread
          && ioctl (input_desc, FIONREAD, &n_to_read) < 0)
        {
          /* Ultrix returns EOPNOTSUPP on NFS;
             HP-UX returns ENOTTY on pipes.
             SunOS returns EINVAL and
             More/BSD returns ENODEV on special files
             like /dev/null.
             Irix-5 returns ENOSYS on pipes.  */
          if (errno == EOPNOTSUPP || errno == ENOTTY
              || errno == EINVAL || errno == ENODEV
# ifdef ENOSYS
              || errno == ENOSYS
# endif
              )
            use_fionread = 0;
          else
            {
              error (0, errno, _("cannot do ioctl on `%s'"), infile);
              exit_status = 1;
              newlines2 = newlines;
              return;
            }
        }
          if (n_to_read == 0)
#endif
        {
          size_t n_write = bpout - outbuf;

          if (full_write (STDOUT_FILENO, outbuf, n_write) != n_write)
            error (EXIT_FAILURE, errno, _("write error"));
          bpout = outbuf;
        }

          /* Read more input into INBUF.  */

          n_read = safe_read (input_desc, inbuf, insize);
          if (n_read == SAFE_READ_ERROR)
        {
          error (0, errno, "%s", infile);
          exit_status = 1;
          newlines2 = newlines;
          return;
        }
          if (n_read == 0)
        {
          newlines2 = newlines;
          return;
        }

          /* Update the pointers and insert a sentinel at the buffer
         end.  */

          bpin = inbuf;
          eob = bpin + n_read;
          *eob = '\n';
        }
      else
        {
          /* It was a real (not a sentinel) newline.  */

          /* Was the last line empty?
         (i.e. have two or more consecutive newlines been read?)  */

          if (++newlines > 0)
        {
          if (newlines >= 2)
            {
              /* Limit this to 2 here.  Otherwise, with lots of
             consecutive newlines, the counter could wrap
             around at INT_MAX.  */
              newlines = 2;

              /* Are multiple adjacent empty lines to be substituted
             by single ditto (-s), and this was the second empty
             line?  */
              if (squeeze_empty_lines)
            {
              ch = *bpin++;
              continue;
            }
            }

          /* Are line numbers to be written at empty lines (-n)?  */

          if (numbers && numbers_at_empty_lines)
            {
              next_line_num ();
              bpout = stpcpy (bpout, line_num_print);
            }
        }

          /* Output a currency symbol if requested (-e).  */

          if (mark_line_ends)
        *bpout++ = '$';

          /* Output the newline.  */

          *bpout++ = '\n';
        }
      ch = *bpin++;
    }
      while (ch == '\n');

      /* Are we at the beginning of a line, and line numbers are requested?  */

      if (newlines >= 0 && numbers)
    {
      next_line_num ();
      bpout = stpcpy (bpout, line_num_print);
    }

      /* Here CH cannot contain a newline character.  */

      /* The loops below continue until a newline character is found,
     which means that the buffer is empty or that a proper newline
     has been found.  */

      /* If quoting, i.e. at least one of -v, -e, or -t specified,
     scan for chars that need conversion.  */
      if (quote)
    {
      for (;;)
        {
          if (ch >= 32)
        {
          if (ch < 127)
            *bpout++ = ch;
          else if (ch == 127)
            {
              *bpout++ = '^';
              *bpout++ = '?';
            }
          else
            {
              *bpout++ = 'M';
              *bpout++ = '-';
              if (ch >= 128 + 32)
            {
              if (ch < 128 + 127)
                *bpout++ = ch - 128;
              else
                {
                  *bpout++ = '^';
                  *bpout++ = '?';
                }
            }
              else
            {
              *bpout++ = '^';
              *bpout++ = ch - 128 + 64;
            }
            }
        }
          else if (ch == '\t' && output_tabs)
        *bpout++ = '\t';
          else if (ch == '\n')
        {
          newlines = -1;
          break;
        }
          else
        {
          *bpout++ = '^';
          *bpout++ = ch + 64;
        }

          ch = *bpin++;
        }
    }
      else
    {
      /* Not quoting, neither of -v, -e, or -t specified.  */
      for (;;)
        {
          if (ch == '\t' && !output_tabs)
        {
          *bpout++ = '^';
          *bpout++ = ch + 64;
        }
          else if (ch != '\n')
        *bpout++ = ch;
          else
        {
          newlines = -1;
          break;
        }

          ch = *bpin++;
        }
    }
    }
}

/* This is gross, but necessary, because of the way close_stdout
   works and because this program closes STDOUT_FILENO directly.  */
static void (*closeout_func) (void) = close_stdout;

static void
close_stdout_wrapper (void)
{
  if (closeout_func)
    (*closeout_func) ();
}

int
main (int argc, char **argv)
{
  /* Optimal size of i/o operations of output.  */
  size_t outsize;

  /* Optimal size of i/o operations of input.  */
  size_t insize;

  /* Pointer to the input buffer.  */
  char *inbuf;

  /* Pointer to the output buffer.  */
  char *outbuf;

  int c;

  /* Index in argv to processed argument.  */
  int argind;

  /* Device number of the output (file or whatever).  */
  dev_t out_dev;

  /* I-node number of the output.  */
  ino_t out_ino;

  /* Nonzero if the output file should not be the same as any input file. */
  int check_redirection = 1;

  /* Nonzero if we have ever read standard input. */
  int have_read_stdin = 0;

  struct stat stat_buf;

  /* Variables that are set according to the specified options.  */
  int numbers = 0;
  int numbers_at_empty_lines = 1;
  int squeeze_empty_lines = 0;
  int mark_line_ends = 0;
  int quote = 0;
  int output_tabs = 1;
#if O_BINARY
  int binary_files  = 0;
  int binary_output = 0;
#endif
  int file_open_mode = O_RDONLY;

/* If nonzero, call cat, otherwise call simple_cat to do the actual work. */
  int options = 0;

  static struct option const long_options[] =
  {
    {"number-nonblank", no_argument, NULL, 'b'},
    {"number", no_argument, NULL, 'n'},
    {"squeeze-blank", no_argument, NULL, 's'},
    {"show-nonprinting", no_argument, NULL, 'v'},
    {"show-ends", no_argument, NULL, 'E'},
    {"show-tabs", no_argument, NULL, 'T'},
    {"show-all", no_argument, NULL, 'A'},
#if O_BINARY
    {"binary", no_argument, NULL, 'B'},
#endif
    {GETOPT_HELP_OPTION_DECL},
    {GETOPT_VERSION_OPTION_DECL},
    {NULL, 0, NULL, 0}
  };

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  /* Arrange to close stdout if we exit via the
     case_GETOPT_HELP_CHAR or case_GETOPT_VERSION_CHAR code.  */
  atexit (close_stdout_wrapper);

  /* Parse command line options.  */

  while ((c = getopt_long (argc, argv,
#if O_BINARY
               "benstuvABET"
#else
               "benstuvAET"
#endif
               , long_options, NULL)) != -1)
    {
      switch (c)
    {
    case 0:
      break;

    case 'b':
      ++options;
      numbers = 1;
      numbers_at_empty_lines = 0;
      break;

    case 'e':
      ++options;
      mark_line_ends = 1;
      quote = 1;
      break;

    case 'n':
      ++options;
      numbers = 1;
      break;

    case 's':
      ++options;
      squeeze_empty_lines = 1;
      break;

    case 't':
      ++options;
      output_tabs = 0;
      quote = 1;
      break;

    case 'u':
      /* We provide the -u feature unconditionally.  */
      break;

    case 'v':
      ++options;
      quote = 1;
      break;

    case 'A':
      ++options;
      quote = 1;
      mark_line_ends = 1;
      output_tabs = 0;
      break;

#if O_BINARY
    case 'B':
      ++options;
      binary_files = 1;
      break;
#endif

    case 'E':
      ++options;
      mark_line_ends = 1;
      break;

    case 'T':
      ++options;
      output_tabs = 0;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
    }

  /* Don't close stdout on exit from here on.  */
  closeout_func = NULL;

  /* Get device, i-node number, and optimal blocksize of output.  */

  if (fstat (STDOUT_FILENO, &stat_buf) < 0)
    error (EXIT_FAILURE, errno, _("standard output"));

  outsize = ST_BLKSIZE (stat_buf);
  /* Input file can be output file for non-regular files.
     fstat on pipes returns S_IFSOCK on some systems, S_IFIFO
     on others, so the checking should not be done for those types,
     and to allow things like cat < /dev/tty > /dev/tty, checking
     is not done for device files either. */

  if (S_ISREG (stat_buf.st_mode))
    {
      out_dev = stat_buf.st_dev;
      out_ino = stat_buf.st_ino;
    }
  else
    {
      check_redirection = 0;
#ifdef lint  /* Suppress `used before initialized' warning.  */
      out_dev = 0;
      out_ino = 0;
#endif
    }

#if O_BINARY
  /* We always read and write in BINARY mode, since this is the
     best way to copy the files verbatim.  Exceptions are when
     they request line numbering, squeezing of empty lines or
     marking lines' ends: then we use text I/O, because otherwise
     -b, -s and -E would surprise users on DOS/Windows where a line
     with only CR-LF is an empty line.  (Besides, if they ask for
     one of these options, they don't care much about the original
     file contents anyway).  */
  if ((!isatty (STDOUT_FILENO)
       && !(numbers || squeeze_empty_lines || mark_line_ends))
      || binary_files)
    {
      /* Switch stdout to BINARY mode.  */
      binary_output = 1;
      SET_BINARY (STDOUT_FILENO);
      /* When stdout is in binary mode, make sure all input files are
     also read in binary mode.  */
      file_open_mode |= O_BINARY;
    }
  else if (quote)
    {
      /* If they want to see the non-printables, let's show them
     those CR characters as well, so make the input binary.
     But keep console output in text mode, so that LF causes
     both CR and LF on output, and the output is readable.  */
      file_open_mode |= O_BINARY;
      SET_BINARY (0);

      /* Setting stdin to binary switches the console device to
     raw I/O, which also affects stdout to console.  Undo that.  */
      if (isatty (STDOUT_FILENO))
    setmode (STDOUT_FILENO, O_TEXT);
    }
#endif

  /* Check if any of the input files are the same as the output file.  */

  /* Main loop.  */

  infile = "-";
  argind = optind;

  do
    {
      if (argind < argc)
    infile = argv[argind];

      if (infile[0] == '-' && infile[1] == 0)
    {
      have_read_stdin = 1;
      input_desc = 0;

#if O_BINARY
      /* Switch stdin to BINARY mode if needed.  */
      if (binary_output)
        {
          int tty_in = isatty (input_desc);

          /* If stdin is a terminal device, and it is the ONLY
         input file (i.e. we didn't write anything to the
         output yet), switch the output back to TEXT mode.
         This is so "cat > xyzzy" creates a DOS-style text
         file, like people expect.  */
          if (tty_in && optind <= argc)
        setmode (STDOUT_FILENO, O_TEXT);
          else
        {
          SET_BINARY (input_desc);
# ifdef __DJGPP__
          /* This is DJGPP-specific.  By default, switching console
             to binary mode disables SIGINT.  But we want terminal
             reads to be interruptible.  */
          if (tty_in)
            __djgpp_set_ctrl_c (1);
# endif
        }
        }
#endif
    }
      else
    {
      input_desc = open (infile, file_open_mode);
      if (input_desc < 0)
        {
          error (0, errno, "%s", infile);
          exit_status = 1;
          continue;
        }
    }

      if (fstat (input_desc, &stat_buf) < 0)
    {
      error (0, errno, "%s", infile);
      exit_status = 1;
      goto contin;
    }
      insize = ST_BLKSIZE (stat_buf);

      /* Compare the device and i-node numbers of this input file with
     the corresponding values of the (output file associated with)
     stdout, and skip this input file if they coincide.  Input
     files cannot be redirected to themselves.  */

      if (check_redirection
      && stat_buf.st_dev == out_dev && stat_buf.st_ino == out_ino
      && (input_desc != STDIN_FILENO))
    {
      error (0, 0, _("%s: input file is output file"), infile);
      exit_status = 1;
      goto contin;
    }

      /* Select which version of `cat' to use. If any options (more than -u,
     --version, or --help) were specified, use `cat', otherwise use
     `simple_cat'.  */

      if (options == 0)
    {
      insize = max (insize, outsize);
      inbuf = xmalloc (insize);

      simple_cat (inbuf, insize);
    }
      else
    {
      inbuf = xmalloc (insize + 1);

      /* Why are (OUTSIZE - 1 + INSIZE * 4 + LINE_COUNTER_BUF_LEN)
         bytes allocated for the output buffer?

         A test whether output needs to be written is done when the input
         buffer empties or when a newline appears in the input.  After
         output is written, at most (OUTSIZE - 1) bytes will remain in the
         buffer.  Now INSIZE bytes of input is read.  Each input character
         may grow by a factor of 4 (by the prepending of M-^).  If all
         characters do, and no newlines appear in this block of input, we
         will have at most (OUTSIZE - 1 + INSIZE * 4) bytes in the buffer.
         If the last character in the preceding block of input was a
         newline, a line number may be written (according to the given
         options) as the first thing in the output buffer. (Done after the
         new input is read, but before processing of the input begins.)
         A line number requires seldom more than LINE_COUNTER_BUF_LEN
         positions.  */

      outbuf = xmalloc (outsize - 1 + insize * 4 + LINE_COUNTER_BUF_LEN);

      cat (inbuf, insize, outbuf, outsize, quote,
           output_tabs, numbers, numbers_at_empty_lines, mark_line_ends,
           squeeze_empty_lines);

      free (outbuf);
    }

      free (inbuf);

    contin:
      if (!STREQ (infile, "-") && close (input_desc) < 0)
    {
      error (0, errno, "%s", infile);
      exit_status = 1;
    }
    }
  while (++argind < argc);

  if (have_read_stdin && close (STDIN_FILENO) < 0)
    error (EXIT_FAILURE, errno, _("closing standard input"));

  if (close (STDOUT_FILENO) < 0)
    error (EXIT_FAILURE, errno, _("closing standard output"));

  exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getopt.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getopt.h used in: Step 849, Step 851, Step 855, Step 857, Step 863, Step 873, Step 875, Step 877, Step 881, Step 883, Step 885, Step 887, Step 891, Step 893, Step 895, Step 897, Step 899, Step 901, Step 903, Step 905, Step 907, Step 909, Step 911, Step 915, Step 917, Step 919, Step 921, Step 923, Step 925, Step 927, Step 929, Step 931, Step 933, Step 935, Step 937, Step 939, Step 941, Step 943, Step 945, Step 947, Step 951, Step 955, Step 966, Step 972
/* Declarations for getopt.
   Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#ifndef _GETOPT_H

#ifndef __need_getopt
# define _GETOPT_H 1
#endif

/* If __GNU_LIBRARY__ is not already defined, either we are being used
   standalone, or this is the first header included in the source file.
   If we are being used with glibc, we need to include <features.h>, but
   that does not exist if we are standalone.  So: if __GNU_LIBRARY__ is
   not defined, include <ctype.h>, which will pull in <features.h> for us
   if it's from glibc.  (Why ctype.h?  It's guaranteed to exist and it
   doesn't flood the namespace with stuff the way some other headers do.)  */
#if !defined __GNU_LIBRARY__
# include <ctype.h>
#endif

#ifdef  __cplusplus
extern "C" {
#endif

/* For communication from `getopt' to the caller.
   When `getopt' finds an option that takes an argument,
   the argument value is returned here.
   Also, when `ordering' is RETURN_IN_ORDER,
   each non-option ARGV-element is returned here.  */

extern char *optarg;

/* Index in ARGV of the next element to be scanned.
   This is used for communication to and from the caller
   and for communication between successive calls to `getopt'.

   On entry to `getopt', zero means this is the first call; initialize.

   When `getopt' returns -1, this is the index of the first of the
   non-option elements that the caller should itself scan.

   Otherwise, `optind' communicates from one call to the next
   how much of ARGV has been scanned so far.  */

extern int optind;

/* Callers store zero here to inhibit the error message `getopt' prints
   for unrecognized options.  */

extern int opterr;

/* Set to an option character which was unrecognized.  */

extern int optopt;

#ifndef __need_getopt
/* Describe the long-named options requested by the application.
   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
   of `struct option' terminated by an element containing a name which is
   zero.

   The field `has_arg' is:
   no_argument      (or 0) if the option does not take an argument,
   required_argument    (or 1) if the option requires an argument,
   optional_argument    (or 2) if the option takes an optional argument.

   If the field `flag' is not NULL, it points to a variable that is set
   to the value given in the field `val' when the option is found, but
   left unchanged if the option is not found.

   To have a long-named option do something other than set an `int' to
   a compiled-in constant, such as set a value from `optarg', set the
   option's `flag' field to zero and its `val' field to a nonzero
   value (the equivalent single-letter option character, if there is
   one).  For long options that have a zero `flag' field, `getopt'
   returns the contents of the `val' field.  */

struct option
{
# if (defined __STDC__ && __STDC__) || defined __cplusplus
  const char *name;
# else
  char *name;
# endif
  /* has_arg can't be an enum because some compilers complain about
     type mismatches in all the code that assumes it is an int.  */
  int has_arg;
  int *flag;
  int val;
};

/* Names for the values of the `has_arg' field of `struct option'.  */

# define no_argument        0
# define required_argument  1
# define optional_argument  2
#endif  /* need getopt */


/* Get definitions and prototypes for functions to process the
   arguments in ARGV (ARGC of them, minus the program name) for
   options given in OPTS.

   Return the option character from OPTS just read.  Return -1 when
   there are no more options.  For unrecognized options, or options
   missing arguments, `optopt' is set to the option letter, and '?' is
   returned.

   The OPTS string is a list of characters which are recognized option
   letters, optionally followed by colons, specifying that that letter
   takes an argument, to be placed in `optarg'.

   If a letter in OPTS is followed by two colons, its argument is
   optional.  This behavior is specific to the GNU `getopt'.

   The argument `--' causes premature termination of argument
   scanning, explicitly telling `getopt' that there are no more
   options.

   If OPTS begins with `--', then non-option arguments are treated as
   arguments to the option '\0'.  This behavior is specific to the GNU
   `getopt'.  */

#if (defined __STDC__ && __STDC__) || defined __cplusplus
# ifdef __GNU_LIBRARY__
/* Many other libraries have conflicting prototypes for getopt, with
   differences in the consts, in stdlib.h.  To avoid compilation
   errors, only prototype getopt for the GNU C library.  */
extern int getopt (int ___argc, char *const *___argv, const char *__shortopts);
# else /* not __GNU_LIBRARY__ */
extern int getopt ();
# endif /* __GNU_LIBRARY__ */

# ifndef __need_getopt
extern int getopt_long (int ___argc, char *const *___argv,
            const char *__shortopts,
                const struct option *__longopts, int *__longind);
extern int getopt_long_only (int ___argc, char *const *___argv,
                 const char *__shortopts,
                     const struct option *__longopts, int *__longind);

/* Internal only.  Users should not call this directly.  */
extern int _getopt_internal (int ___argc, char *const *___argv,
                 const char *__shortopts,
                     const struct option *__longopts, int *__longind,
                 int __long_only);
# endif
#else /* not __STDC__ */
extern int getopt ();
# ifndef __need_getopt
extern int getopt_long ();
extern int getopt_long_only ();

extern int _getopt_internal ();
# endif
#endif /* __STDC__ */

#ifdef  __cplusplus
}
#endif

/* Make sure we later can get all the definitions and declarations.  */
#undef __need_getopt

#endif /* getopt.h */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/full-write.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/full-write.h used in: Step 849, Step 925
/* An interface to write() that writes all it is asked to write.

   Copyright (C) 2002 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 <stddef.h>

/* Write COUNT bytes at BUF to descriptor FD, retrying if interrupted
   or if partial writes occur.  Return the number of bytes successfully
   written, setting errno if that is less than COUNT.  */
extern size_t full_write (int fd, const void *buf, size_t count);

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/safe-read.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/safe-read.h used in: Step 849, Step 855, Step 877, Step 925, Step 927, Step 929, Step 933, Step 947, Step 951
/* An interface to read() that retries after interrupts.
   Copyright (C) 2002 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 <stddef.h>

#define SAFE_READ_ERROR ((size_t) -1)

/* Read up to COUNT bytes at BUF from descriptor FD, retrying if interrupted.
   Return the actual number of bytes read, zero for EOF, or SAFE_READ_ERROR
   upon error.  */
extern size_t safe_read (int fd, void *buf, size_t count);

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/chmod.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/chmod.c used in: Step 851
/* chmod -- change permission modes of files
   Copyright (C) 89, 90, 91, 1995-2002 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.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu> */

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>

#include "system.h"
#include "error.h"
#include "filemode.h"
#include "modechange.h"
#include "quote.h"
#include "savedir.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "chmod"

#define AUTHORS "David MacKenzie"

enum Change_status
{
  CH_SUCCEEDED,
  CH_FAILED,
  CH_NO_CHANGE_REQUESTED
};

enum Verbosity
{
  /* Print a message for each file that is processed.  */
  V_high,

  /* Print a message for each file whose attributes we change.  */
  V_changes_only,

  /* Do not be verbose.  This is the default. */
  V_off
};

static int change_dir_mode (const char *dir, const struct mode_change *changes);

/* The name the program was run with. */
char *program_name;

/* If nonzero, change the modes of directories recursively. */
static int recurse;

/* If nonzero, force silence (no error messages). */
static int force_silent;

/* Level of verbosity.  */
static enum Verbosity verbosity = V_off;

/* The argument to the --reference option.  Use the owner and group IDs
   of this file.  This file must exist.  */
static char *reference_file;

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  REFERENCE_FILE_OPTION = CHAR_MAX + 1
};

static struct option const long_options[] =
{
  {"recursive", no_argument, 0, 'R'},
  {"changes", no_argument, 0, 'c'},
  {"silent", no_argument, 0, 'f'},
  {"quiet", no_argument, 0, 'f'},
  {"reference", required_argument, 0, REFERENCE_FILE_OPTION},
  {"verbose", no_argument, 0, 'v'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {0, 0, 0, 0}
};

static int
mode_changed (const char *file, mode_t old_mode)
{
  struct stat new_stats;

  if (stat (file, &new_stats))
    {
      if (force_silent == 0)
    error (0, errno, _("getting new attributes of %s"), quote (file));
      return 0;
    }

  return old_mode != new_stats.st_mode;
}

/* Tell the user how/if the MODE of FILE has been changed.
   CHANGED describes what (if anything) has happened. */

static void
describe_change (const char *file, mode_t mode,
         enum Change_status changed)
{
  char perms[11];       /* "-rwxrwxrwx" ls-style modes. */
  const char *fmt;

  mode_string (mode, perms);
  perms[10] = '\0';     /* `mode_string' does not null terminate. */
  switch (changed)
    {
    case CH_SUCCEEDED:
      fmt = _("mode of %s changed to %04lo (%s)\n");
      break;
    case CH_FAILED:
      fmt = _("failed to change mode of %s to %04lo (%s)\n");
      break;
    case CH_NO_CHANGE_REQUESTED:
      fmt = _("mode of %s retained as %04lo (%s)\n");
      break;
    default:
      abort ();
    }
  printf (fmt, quote (file),
      (unsigned long) (mode & CHMOD_MODE_BITS), &perms[1]);
}

/* Change the mode of FILE according to the list of operations CHANGES.
   If DEREF_SYMLINK is nonzero and FILE is a symbolic link, change the
   mode of the referenced file.  If DEREF_SYMLINK is zero, ignore symbolic
   links.  Return 0 if successful, 1 if errors occurred. */

static int
change_file_mode (const char *file, const struct mode_change *changes,
          const int deref_symlink)
{
  struct stat file_stats;
  mode_t newmode;
  int errors = 0;
  int fail;
  int saved_errno;

  if (deref_symlink ? stat (file, &file_stats) : lstat (file, &file_stats))
    {
      if (force_silent == 0)
    error (0, errno, _("failed to get attributes of %s"), quote (file));
      return 1;
    }

#ifdef S_ISLNK
  if (S_ISLNK (file_stats.st_mode))
    return 0;
#endif

  newmode = mode_adjust (file_stats.st_mode, changes);

  fail = chmod (file, newmode);
  saved_errno = errno;

  if (verbosity == V_high
      || (verbosity == V_changes_only
      && !fail && mode_changed (file, file_stats.st_mode)))
    describe_change (file, newmode, (fail ? CH_FAILED : CH_SUCCEEDED));

  if (fail)
    {
      if (force_silent == 0)
    error (0, saved_errno, _("changing permissions of %s"),
           quote (file));
      errors = 1;
    }

  if (recurse && S_ISDIR (file_stats.st_mode))
    errors |= change_dir_mode (file, changes);
  return errors;
}

/* Recursively change the modes of the files in directory DIR
   according to the list of operations CHANGES.
   Return 0 if successful, 1 if errors occurred. */

static int
change_dir_mode (const char *dir, const struct mode_change *changes)
{
  char *name_space, *namep;
  char *path;           /* Full path of each entry to process. */
  unsigned dirlength;       /* Length of DIR and '\0'. */
  unsigned filelength;      /* Length of each pathname to process. */
  unsigned pathlength;      /* Bytes allocated for `path'. */
  int errors = 0;

  name_space = savedir (dir);
  if (name_space == NULL)
    {
      if (force_silent == 0)
    error (0, errno, "%s", quote (dir));
      return 1;
    }

  dirlength = strlen (dir) + 1; /* + 1 is for the trailing '/'. */
  pathlength = dirlength + 1;
  /* Give `path' a dummy value; it will be reallocated before first use. */
  path = xmalloc (pathlength);
  strcpy (path, dir);
  path[dirlength - 1] = '/';

  for (namep = name_space; *namep; namep += filelength - dirlength)
    {
      filelength = dirlength + strlen (namep) + 1;
      if (filelength > pathlength)
    {
      pathlength = filelength * 2;
      path = xrealloc (path, pathlength);
    }
      strcpy (path + dirlength, namep);
      errors |= change_file_mode (path, changes, 0);
    }
  free (path);
  free (name_space);
  return errors;
}

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... MODE[,MODE]... FILE...\n\
  or:  %s [OPTION]... OCTAL-MODE FILE...\n\
  or:  %s [OPTION]... --reference=RFILE FILE...\n\
"),
          program_name, program_name, program_name);
      fputs (_("\
Change the mode of each FILE to MODE.\n\
\n\
  -c, --changes           like verbose but report only when a change is made\n\
  -f, --silent, --quiet   suppress most error messages\n\
  -v, --verbose           output a diagnostic for every file processed\n\
      --reference=RFILE   use RFILE's mode instead of MODE values\n\
  -R, --recursive         change files and directories recursively\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
Each MODE is one or more of the letters ugoa, one of the symbols +-= and\n\
one or more of the letters rwxXstugo.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

/* Parse the ASCII mode given on the command line into a linked list
   of `struct mode_change' and apply that to each file argument. */

int
main (int argc, char **argv)
{
  struct mode_change *changes;
  int errors = 0;
  int modeind = 0;      /* Index of the mode argument in `argv'. */
  int thisind;
  int c;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  recurse = force_silent = 0;

  while (1)
    {
      thisind = optind ? optind : 1;

      c = getopt_long (argc, argv, "RcfvrwxXstugoa,+-=", long_options, NULL);
      if (c == -1)
    break;

      switch (c)
    {
    case 0:
      break;
    case 'r':
    case 'w':
    case 'x':
    case 'X':
    case 's':
    case 't':
    case 'u':
    case 'g':
    case 'o':
    case 'a':
    case ',':
    case '+':
    case '-':
    case '=':
      if (modeind != 0 && modeind != thisind)
        {
          static char char_string[2] = {0, 0};
          char_string[0] = c;
          error (EXIT_FAILURE, 0, _("invalid character %s in mode string %s"),
             quote_n (0, char_string), quote_n (1, argv[thisind]));
        }
      modeind = thisind;
      break;
    case REFERENCE_FILE_OPTION:
      reference_file = optarg;
      break;
    case 'R':
      recurse = 1;
      break;
    case 'c':
      verbosity = V_changes_only;
      break;
    case 'f':
      force_silent = 1;
      break;
    case 'v':
      verbosity = V_high;
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (EXIT_FAILURE);
    }
    }

  if (modeind == 0 && reference_file == NULL)
    modeind = optind++;

  if (optind >= argc)
    {
      error (0, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }

  changes = (reference_file ? mode_create_from_ref (reference_file)
         : mode_compile (argv[modeind], MODE_MASK_ALL));

  if (changes == MODE_INVALID)
    error (EXIT_FAILURE, 0,
       _("invalid mode string: %s"), quote (argv[modeind]));
  else if (changes == MODE_MEMORY_EXHAUSTED)
    xalloc_die ();
  else if (changes == MODE_BAD_REFERENCE)
    error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
       quote (reference_file));

  for (; optind < argc; ++optind)
    errors |= change_file_mode (argv[optind], changes, 1);

  exit (errors);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/filemode.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/filemode.h used in: Step 851
#ifndef FILEMODE_H_

# if HAVE_CONFIG_H
#  include <config.h>
# endif

# include <sys/types.h>

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

void mode_string PARAMS ((mode_t mode, char *str));

#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/modechange.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/modechange.h used in: Step 851, Step 893, Step 895, Step 897
/* modechange.h -- definitions for file mode manipulation
   Copyright (C) 1989, 1990, 1997 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.  */

/* Masks for the `flags' field in a `struct mode_change'. */

#if ! defined MODECHANGE_H_
# define MODECHANGE_H_

# if HAVE_CONFIG_H
#  include <config.h>
# endif

# include <sys/types.h>

/* Affect the execute bits only if at least one execute bit is set already,
   or if the file is a directory. */
# define MODE_X_IF_ANY_X 01

/* If set, copy some existing permissions for u, g, or o onto the other two.
   Which of u, g, or o is copied is determined by which bits are set in the
   `value' field. */
# define MODE_COPY_EXISTING 02

struct mode_change
{
  char op;          /* One of "=+-". */
  char flags;           /* Special operations. */
  mode_t affected;      /* Set for u/g/o/s/s/t, if to be affected. */
  mode_t value;         /* Bits to add/remove. */
  struct mode_change *next; /* Link to next change in list. */
};

/* Masks for mode_compile argument. */
# define MODE_MASK_EQUALS 1
# define MODE_MASK_PLUS 2
# define MODE_MASK_MINUS 4
# define MODE_MASK_ALL (MODE_MASK_EQUALS | MODE_MASK_PLUS | MODE_MASK_MINUS)

/* Error return values for mode_compile. */
# define MODE_INVALID (struct mode_change *) 0
# define MODE_MEMORY_EXHAUSTED (struct mode_change *) 1
# define MODE_BAD_REFERENCE (struct mode_change *) 2

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

struct mode_change *mode_compile PARAMS ((const char *, unsigned));
struct mode_change *mode_create_from_ref PARAMS ((const char *));
mode_t mode_adjust PARAMS ((mode_t, const struct mode_change *));
void mode_free PARAMS ((struct mode_change *));

#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/quote.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/quote.h used in: Step 851, Step 887, Step 893, Step 895, Step 897, Step 915, Step 917, Step 941, Step 951, Step 959, Step 964, Step 970
/* quote.h - prototypes for quote.c
   Copyright (C) 1998, 1999, 2000, 2001 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.  */


#ifndef PARAMS
# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#  define PARAMS(Args) Args
# else
#  define PARAMS(Args) ()
# endif
#endif

char const *quote_n PARAMS ((int n, char const *name));
char const *quote PARAMS ((char const *name));

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/savedir.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/savedir.h used in: Step 851
#if !defined SAVEDIR_H_
# define SAVEDIR_H_

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

char *savedir PARAMS ((const char *dir));

#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/cksum.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/cksum.c used in: Step 853
/* cksum -- calculate and print POSIX checksums and sizes of files
   Copyright (C) 92, 1995-2003 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.  */


/* Written by Q. Frank Xia, qx@math.columbia.edu.
   Cosmetic changes and reorganization by David MacKenzie, djm@gnu.ai.mit.edu.

  Usage: cksum [file...]

  The code segment between "#ifdef CRCTAB" and "#else" is the code
  which calculates the "crctab". It is included for those who want
  verify the correctness of the "crctab". To recreate the "crctab",
  do following:

      cc -DCRCTAB -o crctab cksum.c
      crctab > crctab.h

  As Bruce Evans pointed out to me, the crctab in the sample C code
  in 4.9.10 Rationale of P1003.2/D11.2 is represented in reversed order.
  Namely, 0x01 is represented as 0x80, 0x02 is represented as 0x40, etc.
  The generating polynomial is crctab[0x80]=0xedb88320 instead of
  crctab[1]=0x04C11DB7.  But the code works only for a non-reverse order
  crctab.  Therefore, the sample implementation is wrong.

  This software is compatible with neither the System V nor the BSD
  `sum' program.  It is supposed to conform to P1003.2/D11.2,
  except foreign language interface (4.9.5.3 of P1003.2/D11.2) support.
  Any inconsistency with the standard except 4.9.5.3 is a bug.  */

#include <config.h>

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "cksum"

#define AUTHORS "Q. Frank Xia"

#include <stdio.h>
#include <sys/types.h>
#include "system.h"

#if !defined UINT_FAST32_MAX && !defined uint_fast32_t
# define uint_fast32_t unsigned int
#endif

#ifdef CRCTAB

# define BIT(x) ((uint_fast32_t) 1 << (x))
# define SBIT   BIT (31)

/* The generating polynomial is

          32   26   23   22   16   12   11   10   8   7   5   4   2   1
    G(X)=X  + X  + X  + X  + X  + X  + X  + X  + X + X + X + X + X + X + 1

  The i bit in GEN is set if X^i is a summand of G(X) except X^32.  */

# define GEN    (BIT (26) | BIT (23) | BIT (22) | BIT (16) | BIT (12) \
         | BIT (11) | BIT (10) | BIT (8) | BIT (7) | BIT (5) \
         | BIT (4) | BIT (2) | BIT (1) | BIT (0))

static uint_fast32_t r[8];

static void
fill_r (void)
{
  int i;

  r[0] = GEN;
  for (i = 1; i < 8; i++)
    r[i] = (r[i - 1] << 1) ^ ((r[i - 1] & SBIT) ? GEN : 0);
}

static uint_fast32_t
remainder (int m)
{
  uint_fast32_t rem = 0;
  int i;

  for (i = 0; i < 8; i++)
    if (BIT (i) & m)
      rem ^= r[i];

  return rem & 0xFFFFFFFF;  /* Make it run on 64-bit machine.  */
}

int
main (void)
{
  int i;

  fill_r ();
  printf ("static uint_fast32_t crctab[256] =\n{\n  0x0");
  for (i = 0; i < 51; i++)
    {
      printf (",\n  0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X",
          remainder (i * 5 + 1), remainder (i * 5 + 2),
          remainder (i * 5 + 3), remainder (i * 5 + 4),
          remainder (i * 5 + 5));
    }
  printf ("\n};\n");
  exit (EXIT_SUCCESS);
}

#else /* !CRCTAB */

# include <getopt.h>
# include "closeout.h"
# include "long-options.h"
# include "error.h"
# include "inttostr.h"

/* Number of bytes to read at once.  */
# define BUFLEN (1 << 16)

/* The name this program was run with.  */
char *program_name;

static struct option const long_options[] =
{
  {0, 0, 0, 0}
};

static uint_fast32_t crctab[256] =
{
  0x0,
  0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B,
  0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6,
  0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD,
  0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC,
  0x5BD4B01B, 0x569796C2, 0x52568B75, 0x6A1936C8, 0x6ED82B7F,
  0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A,
  0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039,
  0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58,
  0xBAEA46EF, 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033,
  0xA4AD16EA, 0xA06C0B5D, 0xD4326D90, 0xD0F37027, 0xDDB056FE,
  0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95,
  0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4,
  0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, 0x30476DC0,
  0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5,
  0x2AC12072, 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16,
  0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, 0x7897AB07,
  0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C,
  0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1,
  0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,
  0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B,
  0xBB60ADFC, 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698,
  0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, 0x902B669D,
  0x94EA7B2A, 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E,
  0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, 0xC6BCF05F,
  0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34,
  0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80,
  0x644FC637, 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB,
  0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A,
  0x58C1663D, 0x558240E4, 0x51435D53, 0x251D3B9E, 0x21DC2629,
  0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C,
  0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF,
  0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E,
  0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65,
  0xEBA91BBC, 0xEF68060B, 0xD727BBB6, 0xD3E6A601, 0xDEA580D8,
  0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3,
  0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2,
  0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71,
  0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74,
  0x857130C3, 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640,
  0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, 0x7B827D21,
  0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A,
  0x61043093, 0x65C52D24, 0x119B4BE9, 0x155A565E, 0x18197087,
  0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC,
  0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D,
  0x2056CD3A, 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE,
  0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB,
  0xDBEE767C, 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18,
  0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, 0x89B8FD09,
  0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662,
  0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF,
  0xA2F33668, 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4
};

/* Nonzero if any of the files read were the standard input. */
static int have_read_stdin;

/* Calculate and print the checksum and length in bytes
   of file FILE, or of the standard input if FILE is "-".
   If PRINT_NAME is nonzero, print FILE next to the checksum and size.
   Return 0 if successful, -1 if an error occurs. */

static int
cksum (const char *file, int print_name)
{
  unsigned char buf[BUFLEN];
  uint_fast32_t crc = 0;
  uintmax_t length = 0;
  size_t bytes_read;
  register FILE *fp;
  char length_buf[INT_BUFSIZE_BOUND (uintmax_t)];
  char const *hp;

  if (STREQ (file, "-"))
    {
      fp = stdin;
      have_read_stdin = 1;
    }
  else
    {
      fp = fopen (file, "r");
      if (fp == NULL)
    {
      error (0, errno, "%s", file);
      return -1;
    }
    }

  /* Read input in BINARY mode, unless it is a console device.  */
  SET_BINARY (fileno (fp));

  while ((bytes_read = fread (buf, 1, BUFLEN, fp)) > 0)
    {
      unsigned char *cp = buf;

      if (length + bytes_read < length)
    error (EXIT_FAILURE, 0, _("%s: file too long"), file);
      length += bytes_read;
      while (bytes_read--)
    crc = (crc << 8) ^ crctab[((crc >> 24) ^ *cp++) & 0xFF];
      if (feof (fp))
    break;
    }

  if (ferror (fp))
    {
      error (0, errno, "%s", file);
      if (!STREQ (file, "-"))
    fclose (fp);
      return -1;
    }

  if (!STREQ (file, "-") && fclose (fp) == EOF)
    {
      error (0, errno, "%s", file);
      return -1;
    }

  hp = umaxtostr (length, length_buf);

  for (; length; length >>= 8)
    crc = (crc << 8) ^ crctab[((crc >> 24) ^ length) & 0xFF];

  crc = ~crc & 0xFFFFFFFF;

  if (print_name)
    printf ("%u %s %s\n", (unsigned) crc, hp, file);
  else
    printf ("%u %s\n", (unsigned) crc, hp);

  if (ferror (stdout))
    error (EXIT_FAILURE, errno, "-: %s", _("write error"));

  return 0;
}

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [FILE]...\n\
  or:  %s [OPTION]\n\
"),
          program_name, program_name);
      fputs (_("\
Print CRC checksum and byte counts of each FILE.\n\
\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

int
main (int argc, char **argv)
{
  int i, c;
  int errors = 0;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, VERSION,
              AUTHORS, usage);

  have_read_stdin = 0;

  while ((c = getopt_long (argc, argv, "", long_options, NULL)) != -1)
    {
      switch (c)
    {
    case 0:
      break;

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (optind == argc)
    errors |= cksum ("-", 0);
  else
    {
      for (i = optind; i < argc; i++)
    errors |= cksum (argv[i], 1);
    }

  if (have_read_stdin && fclose (stdin) == EOF)
    error (EXIT_FAILURE, errno, "-");
  exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

#endif /* !CRCTAB */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/csplit.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/csplit.c used in: Step 855
/* csplit - split a file into sections determined by context lines
   Copyright (C) 91, 1995-2002 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.  */

/* Written by Stuart Kemp, cpsrk@groper.jcu.edu.au.
   Modified by David MacKenzie, djm@gnu.ai.mit.edu. */

#include <config.h>

#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include <signal.h>

#include "system.h"
#include "closeout.h"

#include <regex.h>

#include "error.h"
#include "inttostr.h"
#include "safe-read.h"
#include "xstrtol.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "csplit"

#define AUTHORS N_ ("Stuart Kemp and David MacKenzie")

#ifndef TRUE
# define FALSE 0
# define TRUE 1
#endif

/* Increment size of area for control records. */
#define ALLOC_SIZE 20

/* The default prefix for output file names. */
#define DEFAULT_PREFIX  "xx"

typedef int boolean;

/* A compiled pattern arg. */
struct control
{
  char *regexpr;        /* Non-compiled regular expression. */
  struct re_pattern_buffer re_compiled; /* Compiled regular expression. */
  int offset;           /* Offset from regexp to split at. */
  uintmax_t lines_required; /* Number of lines required. */
  uintmax_t repeat;     /* Repeat count. */
  int repeat_forever;       /* Non-zero if `*' used as a repeat count. */
  int argnum;           /* ARGV index. */
  boolean ignore;       /* If true, produce no output (for regexp). */
};

/* Initial size of data area in buffers. */
#define START_SIZE  8191

/* Increment size for data area. */
#define INCR_SIZE   2048

/* Number of lines kept in each node in line list. */
#define CTRL_SIZE   80

#ifdef DEBUG
/* Some small values to test the algorithms. */
# define START_SIZE 200
# define INCR_SIZE  10
# define CTRL_SIZE  1
#endif

/* A string with a length count. */
struct cstring
{
  unsigned int len;
  char *str;
};

/* Pointers to the beginnings of lines in the buffer area.
   These structures are linked together if needed. */
struct line
{
  unsigned used;        /* Number of offsets used in this struct. */
  unsigned insert_index;    /* Next offset to use when inserting line. */
  unsigned retrieve_index;  /* Next index to use when retrieving line. */
  struct cstring starts[CTRL_SIZE]; /* Lines in the data area. */
  struct line *next;        /* Next in linked list. */
};

/* The structure to hold the input lines.
   Contains a pointer to the data area and a list containing
   pointers to the individual lines. */
struct buffer_record
{
  unsigned bytes_alloc;     /* Size of the buffer area. */
  unsigned bytes_used;      /* Bytes used in the buffer area. */
  unsigned start_line;      /* First line number in this buffer. */
  unsigned first_available; /* First line that can be retrieved. */
  unsigned num_lines;       /* Number of complete lines in this buffer. */
  char *buffer;         /* Data area. */
  struct line *line_start;  /* Head of list of pointers to lines. */
  struct line *curr_line;   /* The line start record currently in use. */
  struct buffer_record *next;
};

static void close_output_file (void);
static void create_output_file (void);
static void delete_all_files (void);
static void save_line_to_file (const struct cstring *line);
void usage (int status);

/* The name this program was run with. */
char *program_name;

/* Convert the number of 8-bit bytes of a binary representation to
   the number of characters required to represent the same quantity
   as an unsigned octal.  For example, a 32-bit (4-byte) quantity may
   require a field width as wide as 11 characters.  */
static const unsigned int bytes_to_octal_digits[] =
{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};

/* Input file descriptor. */
static int input_desc = 0;

/* List of available buffers. */
static struct buffer_record *free_list = NULL;

/* Start of buffer list. */
static struct buffer_record *head = NULL;

/* Partially read line. */
static char *hold_area = NULL;

/* Number of chars in `hold_area'. */
static unsigned hold_count = 0;

/* Number of the last line in the buffers. */
static unsigned last_line_number = 0;

/* Number of the line currently being examined. */
static unsigned current_line = 0;

/* If TRUE, we have read EOF. */
static boolean have_read_eof = FALSE;

/* Name of output files. */
static char *filename_space = NULL;

/* Prefix part of output file names. */
static char *prefix = NULL;

/* Suffix part of output file names. */
static char *suffix = NULL;

/* Number of digits to use in output file names. */
static int digits = 2;

/* Number of files created so far. */
static unsigned int files_created = 0;

/* Number of bytes written to current file. */
static unsigned int bytes_written;

/* Output file pointer. */
static FILE *output_stream = NULL;

/* Output file name. */
static char *output_filename = NULL;

/* Perhaps it would be cleaner to pass arg values instead of indexes. */
static char **global_argv;

/* If TRUE, do not print the count of bytes in each output file. */
static boolean suppress_count;

/* If TRUE, remove output files on error. */
static boolean remove_files;

/* If TRUE, remove all output files which have a zero length. */
static boolean elide_empty_files;

/* The compiled pattern arguments, which determine how to split
   the input file. */
static struct control *controls;

/* Number of elements in `controls'. */
static unsigned int control_used;

static struct option const longopts[] =
{
  {"digits", required_argument, NULL, 'n'},
  {"quiet", no_argument, NULL, 'q'},
  {"silent", no_argument, NULL, 's'},
  {"keep-files", no_argument, NULL, 'k'},
  {"elide-empty-files", no_argument, NULL, 'z'},
  {"prefix", required_argument, NULL, 'f'},
  {"suffix-format", required_argument, NULL, 'b'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

/* Optionally remove files created so far; then exit.
   Called when an error detected. */

static void
cleanup (void)
{
  if (output_stream)
    close_output_file ();

  if (remove_files)
    delete_all_files ();
}

static void
cleanup_fatal (void)
{
  cleanup ();
  exit (EXIT_FAILURE);
}

static RETSIGTYPE
interrupt_handler (int sig)
{
#ifdef SA_NOCLDSTOP
  struct sigaction sigact;

  sigact.sa_handler = SIG_DFL;
  sigemptyset (&sigact.sa_mask);
  sigact.sa_flags = 0;
  sigaction (sig, &sigact, NULL);
#else
  signal (sig, SIG_DFL);
#endif
  cleanup ();
  raise (sig);
}

/* Keep track of NUM chars of a partial line in buffer START.
   These chars will be retrieved later when another large buffer is read.
   It is not necessary to create a new buffer for these chars; instead,
   we keep a pointer to the existing buffer.  This buffer *is* on the
   free list, and when the next buffer is obtained from this list
   (even if it is this one), these chars will be placed at the
   start of the new buffer. */

static void
save_to_hold_area (char *start, unsigned int num)
{
  hold_area = start;
  hold_count = num;
}

/* Read up to MAX_N_BYTES chars from the input stream into DEST.
   Return the number of chars read. */
/* FIXME: MAX_N_BYTES should be of type size_t, but if you pull
   that thread, you'll find there are many other `unsigned' types
   in this file that should also be changed.  */

static size_t
read_input (char *dest, int max_n_bytes)
{
  size_t bytes_read;

  if (max_n_bytes == 0)
    return 0;

  bytes_read = safe_read (input_desc, dest, max_n_bytes);

  if (bytes_read == 0)
    have_read_eof = TRUE;

  if (bytes_read == SAFE_READ_ERROR)
    {
      error (0, errno, _("read error"));
      cleanup_fatal ();
    }

  return bytes_read;
}

/* Initialize existing line record P. */

static void
clear_line_control (struct line *p)
{
  p->used = 0;
  p->insert_index = 0;
  p->retrieve_index = 0;
}

/* Initialize all line records in B. */

static void
clear_all_line_control (struct buffer_record *b)
{
  struct line *l;

  for (l = b->line_start; l; l = l->next)
    clear_line_control (l);
}

/* Return a new, initialized line record. */

static struct line *
new_line_control (void)
{
  struct line *p;

  p = (struct line *) xmalloc (sizeof (struct line));

  p->next = NULL;
  clear_line_control (p);

  return p;
}

/* Record LINE_START, which is the address of the start of a line
   of length LINE_LEN in the large buffer, in the lines buffer of B. */

static void
keep_new_line (struct buffer_record *b, char *line_start, int line_len)
{
  struct line *l;

  /* If there is no existing area to keep line info, get some. */
  if (b->line_start == NULL)
    b->line_start = b->curr_line = new_line_control ();

  /* If existing area for lines is full, get more. */
  if (b->curr_line->used == CTRL_SIZE)
    {
      b->curr_line->next = new_line_control ();
      b->curr_line = b->curr_line->next;
    }

  l = b->curr_line;

  /* Record the start of the line, and update counters. */
  l->starts[l->insert_index].str = line_start;
  l->starts[l->insert_index].len = line_len;
  l->used++;
  l->insert_index++;
}

/* Scan the buffer in B for newline characters
   and record the line start locations and lengths in B.
   Return the number of lines found in this buffer.

   There may be an incomplete line at the end of the buffer;
   a pointer is kept to this area, which will be used when
   the next buffer is filled. */

static unsigned int
record_line_starts (struct buffer_record *b)
{
  char *line_start;     /* Start of current line. */
  char *line_end;       /* End of each line found. */
  unsigned int bytes_left;  /* Length of incomplete last line. */
  unsigned int lines;       /* Number of lines found. */
  unsigned int line_length; /* Length of each line found. */

  if (b->bytes_used == 0)
    return 0;

  lines = 0;
  line_start = b->buffer;
  bytes_left = b->bytes_used;

  for (;;)
    {
      line_end = memchr (line_start, '\n', bytes_left);
      if (line_end == NULL)
    break;
      line_length = line_end - line_start + 1;
      keep_new_line (b, line_start, line_length);
      bytes_left -= line_length;
      line_start = line_end + 1;
      lines++;
    }

  /* Check for an incomplete last line. */
  if (bytes_left)
    {
      if (have_read_eof)
    {
      keep_new_line (b, line_start, bytes_left);
      lines++;
    }
      else
    save_to_hold_area (line_start, bytes_left);
    }

  b->num_lines = lines;
  b->first_available = b->start_line = last_line_number + 1;
  last_line_number += lines;

  return lines;
}

/* Return a new buffer with room to store SIZE bytes, plus
   an extra byte for safety. */

static struct buffer_record *
create_new_buffer (unsigned int size)
{
  struct buffer_record *new_buffer;

  new_buffer = (struct buffer_record *)
    xmalloc (sizeof (struct buffer_record));

  new_buffer->buffer = (char *) xmalloc (size + 1);

  new_buffer->bytes_alloc = size;
  new_buffer->line_start = new_buffer->curr_line = NULL;

  return new_buffer;
}

/* Return a new buffer of at least MINSIZE bytes.  If a buffer of at
   least that size is currently free, use it, otherwise create a new one. */

static struct buffer_record *
get_new_buffer (unsigned int min_size)
{
  struct buffer_record *p, *q;
  struct buffer_record *new_buffer; /* Buffer to return. */
  unsigned int alloc_size;  /* Actual size that will be requested. */

  alloc_size = START_SIZE;
  while (min_size > alloc_size)
    alloc_size += INCR_SIZE;

  if (free_list == NULL)
    new_buffer = create_new_buffer (alloc_size);
  else
    {
      /* Use first-fit to find a buffer. */
      p = new_buffer = NULL;
      q = free_list;

      do
    {
      if (q->bytes_alloc >= min_size)
        {
          if (p == NULL)
        free_list = q->next;
          else
        p->next = q->next;
          break;
        }
      p = q;
      q = q->next;
    }
      while (q);

      new_buffer = (q ? q : create_new_buffer (alloc_size));

      new_buffer->curr_line = new_buffer->line_start;
      clear_all_line_control (new_buffer);
    }

  new_buffer->num_lines = 0;
  new_buffer->bytes_used = 0;
  new_buffer->start_line = new_buffer->first_available = last_line_number + 1;
  new_buffer->next = NULL;

  return new_buffer;
}

/* Add buffer BUF to the list of free buffers. */

static void
free_buffer (struct buffer_record *buf)
{
  buf->next = free_list;
  free_list = buf;
}

/* Append buffer BUF to the linked list of buffers that contain
   some data yet to be processed. */

static void
save_buffer (struct buffer_record *buf)
{
  struct buffer_record *p;

  buf->next = NULL;
  buf->curr_line = buf->line_start;

  if (head == NULL)
    head = buf;
  else
    {
      for (p = head; p->next; p = p->next)
    /* Do nothing. */ ;
      p->next = buf;
    }
}

/* Fill a buffer of input.

   Set the initial size of the buffer to a default.
   Fill the buffer (from the hold area and input stream)
   and find the individual lines.
   If no lines are found (the buffer is too small to hold the next line),
   release the current buffer (whose contents would have been put in the
   hold area) and repeat the process with another large buffer until at least
   one entire line has been read.

   Return TRUE if a new buffer was obtained, otherwise false
   (in which case end-of-file must have been encountered). */

static boolean
load_buffer (void)
{
  struct buffer_record *b;
  unsigned int bytes_wanted = START_SIZE; /* Minimum buffer size. */
  unsigned int bytes_avail;     /* Size of new buffer created. */
  unsigned int lines_found;     /* Number of lines in this new buffer. */
  char *p;          /* Place to load into buffer. */

  if (have_read_eof)
    return FALSE;

  /* We must make the buffer at least as large as the amount of data
     in the partial line left over from the last call. */
  if (bytes_wanted < hold_count)
    bytes_wanted = hold_count;

  do
    {
      b = get_new_buffer (bytes_wanted);
      bytes_avail = b->bytes_alloc; /* Size of buffer returned. */
      p = b->buffer;

      /* First check the `holding' area for a partial line. */
      if (hold_count)
    {
      if (p != hold_area)
        memcpy (p, hold_area, hold_count);
      p += hold_count;
      b->bytes_used += hold_count;
      bytes_avail -= hold_count;
      hold_count = 0;
    }

      b->bytes_used += (unsigned int) read_input (p, bytes_avail);

      lines_found = record_line_starts (b);
      bytes_wanted = b->bytes_alloc * 2;
      if (!lines_found)
    free_buffer (b);
    }
  while (!lines_found && !have_read_eof);

  if (lines_found)
    save_buffer (b);

  return lines_found != 0;
}

/* Return the line number of the first line that has not yet been retrieved. */

static unsigned int
get_first_line_in_buffer (void)
{
  if (head == NULL && !load_buffer ())
    error (EXIT_FAILURE, errno, _("input disappeared"));

  return head->first_available;
}

/* Return a pointer to the logical first line in the buffer and make the
   next line the logical first line.
   Return NULL if there is no more input. */

static struct cstring *
remove_line (void)
{
  struct cstring *line;     /* Return value. */
  struct line *l;       /* For convenience. */

  if (head == NULL && !load_buffer ())
    return NULL;

  if (current_line < head->first_available)
    current_line = head->first_available;

  ++(head->first_available);

  l = head->curr_line;

  line = &l->starts[l->retrieve_index];

  /* Advance index to next line. */
  if (++l->retrieve_index == l->used)
    {
      /* Go on to the next line record. */
      head->curr_line = l->next;
      if (head->curr_line == NULL || head->curr_line->used == 0)
    {
      /* Go on to the next data block. */
      struct buffer_record *b = head;
      head = head->next;
      free_buffer (b);
    }
    }

  return line;
}

/* Search the buffers for line LINENUM, reading more input if necessary.
   Return a pointer to the line, or NULL if it is not found in the file. */

static struct cstring *
find_line (unsigned int linenum)
{
  struct buffer_record *b;

  if (head == NULL && !load_buffer ())
    return NULL;

  if (linenum < head->start_line)
    return NULL;

  for (b = head;;)
    {
      if (linenum < b->start_line + b->num_lines)
    {
      /* The line is in this buffer. */
      struct line *l;
      unsigned int offset;  /* How far into the buffer the line is. */

      l = b->line_start;
      offset = linenum - b->start_line;
      /* Find the control record. */
      while (offset >= CTRL_SIZE)
        {
          l = l->next;
          offset -= CTRL_SIZE;
        }
      return &l->starts[offset];
    }
      if (b->next == NULL && !load_buffer ())
    return NULL;
      b = b->next;      /* Try the next data block. */
    }
}

/* Return TRUE if at least one more line is available for input. */

static boolean
no_more_lines (void)
{
  return (find_line (current_line + 1) == NULL) ? TRUE : FALSE;
}

/* Set the name of the input file to NAME and open it. */

static void
set_input_file (const char *name)
{
  if (STREQ (name, "-"))
    input_desc = 0;
  else
    {
      input_desc = open (name, O_RDONLY);
      if (input_desc < 0)
    error (EXIT_FAILURE, errno, "%s", name);
    }
}

/* Write all lines from the beginning of the buffer up to, but
   not including, line LAST_LINE, to the current output file.
   If IGNORE is TRUE, do not output lines selected here.
   ARGNUM is the index in ARGV of the current pattern. */

static void
write_to_file (unsigned int last_line, boolean ignore, int argnum)
{
  struct cstring *line;
  unsigned int first_line;      /* First available input line. */
  unsigned int lines;       /* Number of lines to output. */
  unsigned int i;

  first_line = get_first_line_in_buffer ();

  if (first_line > last_line)
    {
      error (0, 0, _("%s: line number out of range"), global_argv[argnum]);
      cleanup_fatal ();
    }

  lines = last_line - first_line;

  for (i = 0; i < lines; i++)
    {
      line = remove_line ();
      if (line == NULL)
    {
      error (0, 0, _("%s: line number out of range"), global_argv[argnum]);
      cleanup_fatal ();
    }
      if (!ignore)
    save_line_to_file (line);
    }
}

/* Output any lines left after all regexps have been processed. */

static void
dump_rest_of_file (void)
{
  struct cstring *line;

  while ((line = remove_line ()) != NULL)
    save_line_to_file (line);
}

/* Handle an attempt to read beyond EOF under the control of record P,
   on iteration REPETITION if nonzero. */

static void
handle_line_error (const struct control *p, int repetition)
{
  char buf[INT_BUFSIZE_BOUND (uintmax_t)];

  fprintf (stderr, _("%s: `%s': line number out of range"),
       program_name, umaxtostr (p->lines_required, buf));
  if (repetition)
    fprintf (stderr, _(" on repetition %d\n"), repetition);
  else
    fprintf (stderr, "\n");

  cleanup_fatal ();
}

/* Determine the line number that marks the end of this file,
   then get those lines and save them to the output file.
   P is the control record.
   REPETITION is the repetition number. */

static void
process_line_count (const struct control *p, int repetition)
{
  unsigned int linenum;
  uintmax_t last_line_to_save = p->lines_required * (repetition + 1);
  struct cstring *line;

  create_output_file ();

  linenum = get_first_line_in_buffer ();

  while (linenum++ < last_line_to_save)
    {
      line = remove_line ();
      if (line == NULL)
    handle_line_error (p, repetition);
      save_line_to_file (line);
    }

  close_output_file ();

  /* Ensure that the line number specified is not 1 greater than
     the number of lines in the file. */
  if (no_more_lines ())
    handle_line_error (p, repetition);
}

static void
regexp_error (struct control *p, int repetition, boolean ignore)
{
  fprintf (stderr, _("%s: `%s': match not found"),
       program_name, global_argv[p->argnum]);

  if (repetition)
    fprintf (stderr, _(" on repetition %d\n"), repetition);
  else
    fprintf (stderr, "\n");

  if (!ignore)
    {
      dump_rest_of_file ();
      close_output_file ();
    }
  cleanup_fatal ();
}

/* Read the input until a line matches the regexp in P, outputting
   it unless P->IGNORE is TRUE.
   REPETITION is this repeat-count; 0 means the first time. */

static void
process_regexp (struct control *p, int repetition)
{
  struct cstring *line;     /* From input file. */
  unsigned int line_len;    /* To make "$" in regexps work. */
  unsigned int break_line;  /* First line number of next file. */
  boolean ignore = p->ignore;   /* If TRUE, skip this section. */
  int ret;

  if (!ignore)
    create_output_file ();

  /* If there is no offset for the regular expression, or
     it is positive, then it is not necessary to buffer the lines. */

  if (p->offset >= 0)
    {
      for (;;)
    {
      line = find_line (++current_line);
      if (line == NULL)
        {
          if (p->repeat_forever)
        {
          if (!ignore)
            {
              dump_rest_of_file ();
              close_output_file ();
            }
          exit (EXIT_SUCCESS);
        }
          else
        regexp_error (p, repetition, ignore);
        }
      line_len = line->len;
      if (line->str[line_len - 1] == '\n')
        line_len--;
      ret = re_search (&p->re_compiled, line->str, line_len,
               0, line_len, (struct re_registers *) 0);
      if (ret == -2)
        {
          error (0, 0, _("error in regular expression search"));
          cleanup_fatal ();
        }
      if (ret == -1)
        {
          line = remove_line ();
          if (!ignore)
        save_line_to_file (line);
        }
      else
        break;
    }
    }
  else
    {
      /* Buffer the lines. */
      for (;;)
    {
      line = find_line (++current_line);
      if (line == NULL)
        {
          if (p->repeat_forever)
        {
          if (!ignore)
            {
              dump_rest_of_file ();
              close_output_file ();
            }
          exit (EXIT_SUCCESS);
        }
          else
        regexp_error (p, repetition, ignore);
        }
      line_len = line->len;
      if (line->str[line_len - 1] == '\n')
        line_len--;
      ret = re_search (&p->re_compiled, line->str, line_len,
               0, line_len, (struct re_registers *) 0);
      if (ret == -2)
        {
          error (0, 0, _("error in regular expression search"));
          cleanup_fatal ();
        }
      if (ret >= 0)
        break;
    }
    }

  /* Account for any offset from this regexp. */
  break_line = current_line + p->offset;

  write_to_file (break_line, ignore, p->argnum);

  if (!ignore)
    close_output_file ();

  if (p->offset > 0)
    current_line = break_line;
}

/* Split the input file according to the control records we have built. */

static void
split_file (void)
{
  unsigned int i, j;

  for (i = 0; i < control_used; i++)
    {
      if (controls[i].regexpr)
    {
      for (j = 0; (controls[i].repeat_forever
               || j <= controls[i].repeat); j++)
        process_regexp (&controls[i], j);
    }
      else
    {
      for (j = 0; (controls[i].repeat_forever
               || j <= controls[i].repeat); j++)
        process_line_count (&controls[i], j);
    }
    }

  create_output_file ();
  dump_rest_of_file ();
  close_output_file ();
}

/* Return the name of output file number NUM. */

static char *
make_filename (unsigned int num)
{
  strcpy (filename_space, prefix);
  if (suffix)
    sprintf (filename_space+strlen(prefix), suffix, num);
  else
    sprintf (filename_space+strlen(prefix), "%0*d", digits, num);
  return filename_space;
}

/* Create the next output file. */

static void
create_output_file (void)
{
  output_filename = make_filename (files_created);
  output_stream = fopen (output_filename, "w");
  if (output_stream == NULL)
    {
      error (0, errno, "%s", output_filename);
      cleanup_fatal ();
    }
  files_created++;
  bytes_written = 0;
}

/* Delete all the files we have created. */

static void
delete_all_files (void)
{
  unsigned int i;
  char *name;

  for (i = 0; i < files_created; i++)
    {
      name = make_filename (i);
      if (unlink (name))
    error (0, errno, "%s", name);
    }
}

/* Close the current output file and print the count
   of characters in this file. */

static void
close_output_file (void)
{
  if (output_stream)
    {
      if (ferror (output_stream) || fclose (output_stream) == EOF)
    {
      error (0, errno, _("write error for `%s'"), output_filename);
      output_stream = NULL;
      cleanup_fatal ();
    }
      if (bytes_written == 0 && elide_empty_files)
    {
      if (unlink (output_filename))
        error (0, errno, "%s", output_filename);
      files_created--;
    }
      else
    {
      /* FIXME: if we write to stdout here, we have to close stdout
         and check for errors.  */
      if (!suppress_count)
        fprintf (stdout, "%d\n", bytes_written);
    }
      output_stream = NULL;
    }
}

/* Save line LINE to the output file and
   increment the character count for the current file. */

static void
save_line_to_file (const struct cstring *line)
{
  fwrite (line->str, sizeof (char), line->len, output_stream);
  bytes_written += line->len;
}

/* Return a new, initialized control record. */

static struct control *
new_control_record (void)
{
  static unsigned control_allocated = 0; /* Total space allocated. */
  struct control *p;

  if (control_allocated == 0)
    {
      control_allocated = ALLOC_SIZE;
      controls = (struct control *)
    xmalloc (sizeof (struct control) * control_allocated);
    }
  else if (control_used == control_allocated)
    {
      control_allocated += ALLOC_SIZE;
      controls = (struct control *)
    xrealloc ((char *) controls,
          sizeof (struct control) * control_allocated);
    }
  p = &controls[control_used++];
  p->regexpr = NULL;
  p->repeat = 0;
  p->repeat_forever = 0;
  p->lines_required = 0;
  p->offset = 0;
  return p;
}

/* Check if there is a numeric offset after a regular expression.
   STR is the entire command line argument.
   P is the control record for this regular expression.
   NUM is the numeric part of STR. */

static void
check_for_offset (struct control *p, const char *str, const char *num)
{
  unsigned long val;

  if (*num != '-' && *num != '+')
    error (EXIT_FAILURE, 0, _("%s: `+' or `-' expected after delimeter"), str);

  if (xstrtoul (num + 1, NULL, 10, &val, "") != LONGINT_OK
      || val > UINT_MAX)
    error (EXIT_FAILURE, 0, _("%s: integer expected after `%c'"), str, *num);
  p->offset = (unsigned int) val;

  if (*num == '-')
    p->offset = -p->offset;
}

/* Given that the first character of command line arg STR is '{',
   make sure that the rest of the string is a valid repeat count
   and store its value in P.
   ARGNUM is the ARGV index of STR. */

static void
parse_repeat_count (int argnum, struct control *p, char *str)
{
  uintmax_t val;
  char *end;

  end = str + strlen (str) - 1;
  if (*end != '}')
    error (EXIT_FAILURE, 0, _("%s: `}' is required in repeat count"), str);
  *end = '\0';

  if (str+1 == end-1 && *(str+1) == '*')
    p->repeat_forever = 1;
  else
    {
      if (xstrtoumax (str + 1, NULL, 10, &val, "") != LONGINT_OK)
    {
      error (EXIT_FAILURE, 0,
         _("%s}: integer required between `{' and `}'"),
         global_argv[argnum]);
    }
      p->repeat = val;
    }

  *end = '}';
}

/* Extract the regular expression from STR and check for a numeric offset.
   STR should start with the regexp delimiter character.
   Return a new control record for the regular expression.
   ARGNUM is the ARGV index of STR.
   Unless IGNORE is TRUE, mark these lines for output. */

static struct control *
extract_regexp (int argnum, boolean ignore, char *str)
{
  int len;          /* Number of chars in this regexp. */
  char delim = *str;
  char *closing_delim;
  struct control *p;
  const char *err;

  closing_delim = strrchr (str + 1, delim);
  if (closing_delim == NULL)
    error (EXIT_FAILURE, 0,
       _("%s: closing delimeter `%c' missing"), str, delim);

  len = closing_delim - str - 1;
  p = new_control_record ();
  p->argnum = argnum;
  p->ignore = ignore;

  p->regexpr = (char *) xmalloc ((unsigned) (len + 1));
  strncpy (p->regexpr, str + 1, len);
  p->re_compiled.allocated = len * 2;
  p->re_compiled.buffer = (unsigned char *) xmalloc (p->re_compiled.allocated);
  p->re_compiled.fastmap = xmalloc (256);
  p->re_compiled.translate = 0;
  err = re_compile_pattern (p->regexpr, len, &p->re_compiled);
  if (err)
    {
      error (0, 0, _("%s: invalid regular expression: %s"), str, err);
      cleanup_fatal ();
    }

  if (closing_delim[1])
    check_for_offset (p, str, closing_delim + 1);

  return p;
}

/* Extract the break patterns from args START through ARGC - 1 of ARGV.
   After each pattern, check if the next argument is a repeat count. */

static void
parse_patterns (int argc, int start, char **argv)
{
  int i;            /* Index into ARGV. */
  struct control *p;        /* New control record created. */
  uintmax_t val;
  static uintmax_t last_val = 0;

  for (i = start; i < argc; i++)
    {
      if (*argv[i] == '/' || *argv[i] == '%')
    {
      p = extract_regexp (i, *argv[i] == '%', argv[i]);
    }
      else
    {
      p = new_control_record ();
      p->argnum = i;

      if (xstrtoumax (argv[i], NULL, 10, &val, "") != LONGINT_OK)
        error (EXIT_FAILURE, 0, _("%s: invalid pattern"), argv[i]);
      if (val == 0)
        error (EXIT_FAILURE, 0,
           _("%s: line number must be greater than zero"),
           argv[i]);
      if (val < last_val)
        {
          char buf[INT_BUFSIZE_BOUND (uintmax_t)];
          error (EXIT_FAILURE, 0,
           _("line number `%s' is smaller than preceding line number, %s"),
             argv[i], umaxtostr (last_val, buf));
        }

      if (val == last_val)
        error (0, 0,
       _("warning: line number `%s' is the same as preceding line number"),
           argv[i]);

      last_val = val;

      p->lines_required = val;
    }

      if (i + 1 < argc && *argv[i + 1] == '{')
    {
      /* We have a repeat count. */
      i++;
      parse_repeat_count (i, p, argv[i]);
    }
    }
}

static unsigned
get_format_flags (char **format_ptr)
{
  unsigned count = 0;

  for (; **format_ptr; (*format_ptr)++)
    {
      switch (**format_ptr)
    {
    case '-':
      break;

    case '+':
    case ' ':
      count++;
      break;

    case '#':
      count += 2;   /* Allow for 0x prefix preceeding an `x' conversion.  */
      break;

    default:
      return count;
    }
    }
  return count;
}

static unsigned
get_format_width (char **format_ptr)
{
  unsigned count = 0;
  char *start;
  int ch_save;

  start = *format_ptr;
  for (; ISDIGIT (**format_ptr); (*format_ptr)++)
    continue;

  ch_save = **format_ptr;
  **format_ptr = '\0';
  /* In the case where no minimum field width is explicitly specified,
     allow for enough octal digits to represent the value of LONG_MAX.  */
  count = ((*format_ptr == start)
       ? bytes_to_octal_digits[sizeof (long)]
       /* FIXME: don't use atoi, it may silently overflow.
          Besides, we know the result is non-negative, so shouldn't
          need that cast.  */
       : (unsigned) atoi (start));
  **format_ptr = ch_save;
  return count;
}

static unsigned
get_format_prec (char **format_ptr)
{
  unsigned count = 0;
  char *start;
  int ch_save;
  int is_negative;

  if (**format_ptr != '.')
    return 0;
  (*format_ptr)++;

  if (**format_ptr == '-' || **format_ptr == '+')
    {
      is_negative = (**format_ptr == '-');
      (*format_ptr)++;
    }
  else
    {
      is_negative = 0;
    }

  start = *format_ptr;
  for (; ISDIGIT (**format_ptr); (*format_ptr)++)
    continue;

  /* ANSI 4.9.6.1 says that if the precision is negative, it's as good as
     not there. */
  if (is_negative)
    start = *format_ptr;

  ch_save = **format_ptr;
  **format_ptr = '\0';
  count = (*format_ptr == start) ? 11 : atoi (start);
  **format_ptr = ch_save;

  return count;
}

static void
get_format_conv_type (char **format_ptr)
{
  int ch = *((*format_ptr)++);

  switch (ch)
    {
    case 'd':
    case 'i':
    case 'o':
    case 'u':
    case 'x':
    case 'X':
      break;

    case 0:
      error (EXIT_FAILURE, 0, _("missing conversion specifier in suffix"));
      break;

    default:
      if (ISPRINT (ch))
        error (EXIT_FAILURE, 0,
           _("invalid conversion specifier in suffix: %c"), ch);
      else
    error (EXIT_FAILURE, 0,
           _("invalid conversion specifier in suffix: \\%.3o"), ch);
    }
}

static unsigned
max_out (char *format)
{
  unsigned out_count = 0;
  unsigned percents = 0;

  for (; *format; )
    {
      int ch = *format++;

      if (ch != '%')
        out_count++;
      else
    {
      percents++;
      out_count += get_format_flags (&format);
      {
        int width = get_format_width (&format);
        int prec = get_format_prec (&format);

        out_count += MAX (width, prec);
      }
      get_format_conv_type (&format);
    }
    }

  if (percents == 0)
    error (EXIT_FAILURE, 0,
       _("missing %% conversion specification in suffix"));
  else if (percents > 1)
    error (EXIT_FAILURE, 0,
       _("too many %% conversion specifications in suffix"));

  return out_count;
}

int
main (int argc, char **argv)
{
  int optc;
  unsigned long val;
#ifdef SA_NOCLDSTOP
  struct sigaction oldact, newact;
#endif

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  global_argv = argv;
  controls = NULL;
  control_used = 0;
  suppress_count = FALSE;
  remove_files = TRUE;
  prefix = DEFAULT_PREFIX;

  /* Change the way xmalloc and xrealloc fail.  */
  xalloc_fail_func = cleanup;

#ifdef SA_NOCLDSTOP
  newact.sa_handler = interrupt_handler;
  sigemptyset (&newact.sa_mask);
  newact.sa_flags = 0;

  sigaction (SIGHUP, NULL, &oldact);
  if (oldact.sa_handler != SIG_IGN)
    sigaction (SIGHUP, &newact, NULL);

  sigaction (SIGINT, NULL, &oldact);
  if (oldact.sa_handler != SIG_IGN)
    sigaction (SIGINT, &newact, NULL);

  sigaction (SIGQUIT, NULL, &oldact);
  if (oldact.sa_handler != SIG_IGN)
    sigaction (SIGQUIT, &newact, NULL);

  sigaction (SIGTERM, NULL, &oldact);
  if (oldact.sa_handler != SIG_IGN)
    sigaction (SIGTERM, &newact, NULL);
#else
  if (signal (SIGHUP, SIG_IGN) != SIG_IGN)
    signal (SIGHUP, interrupt_handler);
  if (signal (SIGINT, SIG_IGN) != SIG_IGN)
    signal (SIGINT, interrupt_handler);
  if (signal (SIGQUIT, SIG_IGN) != SIG_IGN)
    signal (SIGQUIT, interrupt_handler);
  if (signal (SIGTERM, SIG_IGN) != SIG_IGN)
    signal (SIGTERM, interrupt_handler);
#endif

  while ((optc = getopt_long (argc, argv, "f:b:kn:sqz", longopts, NULL)) != -1)
    switch (optc)
      {
      case 0:
    break;

      case 'f':
    prefix = optarg;
    break;

      case 'b':
    suffix = optarg;
    break;

      case 'k':
    remove_files = FALSE;
    break;

      case 'n':
    if (xstrtoul (optarg, NULL, 10, &val, "") != LONGINT_OK
        || val > INT_MAX)
      error (EXIT_FAILURE, 0, _("%s: invalid number"), optarg);
    digits = (int) val;
    break;

      case 's':
      case 'q':
    suppress_count = TRUE;
    break;

      case 'z':
    elide_empty_files = TRUE;
    break;

      case_GETOPT_HELP_CHAR;

      case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

      default:
    usage (EXIT_FAILURE);
      }

  if (argc - optind < 2)
    {
      error (0, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }

  if (suffix)
    filename_space = (char *) xmalloc (strlen (prefix) + max_out (suffix) + 2);
  else
    filename_space = (char *) xmalloc (strlen (prefix) + digits + 2);

  set_input_file (argv[optind++]);

  parse_patterns (argc, optind, argv);

  split_file ();

  if (close (input_desc) < 0)
    {
      error (0, errno, _("read error"));
      cleanup_fatal ();
    }

  exit (EXIT_SUCCESS);
}

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... FILE PATTERN...\n\
"),
          program_name);
      fputs (_("\
Output pieces of FILE separated by PATTERN(s) to files `xx01', `xx02', ...,\n\
and output byte counts of each piece to standard output.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -b, --suffix-format=FORMAT use sprintf FORMAT instead of %d\n\
  -f, --prefix=PREFIX        use PREFIX instead of `xx'\n\
  -k, --keep-files           do not remove output files on errors\n\
"), stdout);
      fputs (_("\
  -n, --digits=DIGITS        use specified number of digits instead of 2\n\
  -s, --quiet, --silent      do not print counts of output file sizes\n\
  -z, --elide-empty-files    remove empty output files\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
Read standard input if FILE is -.  Each PATTERN may be:\n\
"), stdout);
      fputs (_("\
\n\
  INTEGER            copy up to but not including specified line number\n\
  /REGEXP/[OFFSET]   copy up to but not including a matching line\n\
  %REGEXP%[OFFSET]   skip to, but not including a matching line\n\
  {INTEGER}          repeat the previous pattern specified number of times\n\
  {*}                repeat the previous pattern as many times as possible\n\
\n\
A line OFFSET is a required `+' or `-' followed by a positive integer.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/regex.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/regex.h used in: Step 855, Step 865, Step 899, Step 911, Step 947
/* Definitions for data structures and routines for the regular
   expression library, version 0.12.
   Copyright (C) 1985,1989-93,1995-98,2000,2001 Free Software Foundation, Inc.
   This file is part of the GNU C Library.  Its master source is NOT part of
   the C library, however.  The master source lives in /gd/gnu/lib.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#ifndef _REGEX_H
#define _REGEX_H 1

/* Allow the use in C++ code.  */
#ifdef __cplusplus
extern "C" {
#endif

/* POSIX says that <sys/types.h> must be included (by the caller) before
   <regex.h>.  */

#if !defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE && defined VMS
/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
   should be there.  */
# include <stddef.h>
#endif

/* The following two types have to be signed and unsigned integer type
   wide enough to hold a value of a pointer.  For most ANSI compilers
   ptrdiff_t and size_t should be likely OK.  Still size of these two
   types is 2 for Microsoft C.  Ugh... */
typedef long int s_reg_t;
typedef unsigned long int active_reg_t;

/* The following bits are used to determine the regexp syntax we
   recognize.  The set/not-set meanings are chosen so that Emacs syntax
   remains the value 0.  The bits are given in alphabetical order, and
   the definitions shifted by one from the previous bit; thus, when we
   add or remove a bit, only one other definition need change.  */
typedef unsigned long int reg_syntax_t;

/* If this bit is not set, then \ inside a bracket expression is literal.
   If set, then such a \ quotes the following character.  */
#define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)

/* If this bit is not set, then + and ? are operators, and \+ and \? are
     literals.
   If set, then \+ and \? are operators and + and ? are literals.  */
#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)

/* If this bit is set, then character classes are supported.  They are:
     [:alpha:], [:upper:], [:lower:],  [:digit:], [:alnum:], [:xdigit:],
     [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
   If not set, then character classes are not supported.  */
#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)

/* If this bit is set, then ^ and $ are always anchors (outside bracket
     expressions, of course).
   If this bit is not set, then it depends:
        ^  is an anchor if it is at the beginning of a regular
           expression or after an open-group or an alternation operator;
        $  is an anchor if it is at the end of a regular expression, or
           before a close-group or an alternation operator.

   This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
   POSIX draft 11.2 says that * etc. in leading positions is undefined.
   We already implemented a previous draft which made those constructs
   invalid, though, so we haven't changed the code back.  */
#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)

/* If this bit is set, then special characters are always special
     regardless of where they are in the pattern.
   If this bit is not set, then special characters are special only in
     some contexts; otherwise they are ordinary.  Specifically,
     * + ? and intervals are only special when not after the beginning,
     open-group, or alternation operator.  */
#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)

/* If this bit is set, then *, +, ?, and { cannot be first in an re or
     immediately after an alternation or begin-group operator.  */
#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)

/* If this bit is set, then . matches newline.
   If not set, then it doesn't.  */
#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)

/* If this bit is set, then . doesn't match NUL.
   If not set, then it does.  */
#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)

/* If this bit is set, nonmatching lists [^...] do not match newline.
   If not set, they do.  */
#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)

/* If this bit is set, either \{...\} or {...} defines an
     interval, depending on RE_NO_BK_BRACES.
   If not set, \{, \}, {, and } are literals.  */
#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)

/* If this bit is set, +, ? and | aren't recognized as operators.
   If not set, they are.  */
#define RE_LIMITED_OPS (RE_INTERVALS << 1)

/* If this bit is set, newline is an alternation operator.
   If not set, newline is literal.  */
#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)

/* If this bit is set, then `{...}' defines an interval, and \{ and \}
     are literals.
  If not set, then `\{...\}' defines an interval.  */
#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)

/* If this bit is set, (...) defines a group, and \( and \) are literals.
   If not set, \(...\) defines a group, and ( and ) are literals.  */
#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)

/* If this bit is set, then \<digit> matches <digit>.
   If not set, then \<digit> is a back-reference.  */
#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)

/* If this bit is set, then | is an alternation operator, and \| is literal.
   If not set, then \| is an alternation operator, and | is literal.  */
#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)

/* If this bit is set, then an ending range point collating higher
     than the starting range point, as in [z-a], is invalid.
   If not set, then when ending range point collates higher than the
     starting range point, the range is ignored.  */
#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)

/* If this bit is set, then an unmatched ) is ordinary.
   If not set, then an unmatched ) is invalid.  */
#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)

/* If this bit is set, succeed as soon as we match the whole pattern,
   without further backtracking.  */
#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)

/* If this bit is set, do not process the GNU regex operators.
   If not set, then the GNU regex operators are recognized. */
#define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)

/* If this bit is set, turn on internal regex debugging.
   If not set, and debugging was on, turn it off.
   This only works if regex.c is compiled -DDEBUG.
   We define this bit always, so that all that's needed to turn on
   debugging is to recompile regex.c; the calling code can always have
   this bit set, and it won't affect anything in the normal case. */
#define RE_DEBUG (RE_NO_GNU_OPS << 1)

/* If this bit is set, a syntactically invalid interval is treated as
   a string of ordinary characters.  For example, the ERE 'a{1' is
   treated as 'a\{1'.  */
#define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1)

/* This global variable defines the particular regexp syntax to use (for
   some interfaces).  When a regexp is compiled, the syntax used is
   stored in the pattern buffer, so changing this does not affect
   already-compiled regexps.  */
extern reg_syntax_t re_syntax_options;


/* Define combinations of the above bits for the standard possibilities.
   (The [[[ comments delimit what gets put into the Texinfo file, so
   don't delete them!)  */
/* [[[begin syntaxes]]] */
#define RE_SYNTAX_EMACS 0

#define RE_SYNTAX_AWK                           \
  (RE_BACKSLASH_ESCAPE_IN_LISTS   | RE_DOT_NOT_NULL         \
   | RE_NO_BK_PARENS              | RE_NO_BK_REFS           \
   | RE_NO_BK_VBAR                | RE_NO_EMPTY_RANGES          \
   | RE_DOT_NEWLINE       | RE_CONTEXT_INDEP_ANCHORS        \
   | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)

#define RE_SYNTAX_GNU_AWK                       \
  ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG) \
   & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS))

#define RE_SYNTAX_POSIX_AWK                         \
  (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS      \
   | RE_INTERVALS       | RE_NO_GNU_OPS)

#define RE_SYNTAX_GREP                          \
  (RE_BK_PLUS_QM              | RE_CHAR_CLASSES             \
   | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS                \
   | RE_NEWLINE_ALT)

#define RE_SYNTAX_EGREP                         \
  (RE_CHAR_CLASSES        | RE_CONTEXT_INDEP_ANCHORS            \
   | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE            \
   | RE_NEWLINE_ALT       | RE_NO_BK_PARENS             \
   | RE_NO_BK_VBAR)

#define RE_SYNTAX_POSIX_EGREP                       \
  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES         \
   | RE_INVALID_INTERVAL_ORD)

/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff.  */
#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC

#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC

/* Syntax bits common to both basic and extended POSIX regex syntax.  */
#define _RE_SYNTAX_POSIX_COMMON                     \
  (RE_CHAR_CLASSES | RE_DOT_NEWLINE      | RE_DOT_NOT_NULL      \
   | RE_INTERVALS  | RE_NO_EMPTY_RANGES)

#define RE_SYNTAX_POSIX_BASIC                       \
  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)

/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
   RE_LIMITED_OPS, i.e., \? \+ \| are not recognized.  Actually, this
   isn't minimal, since other operators, such as \`, aren't disabled.  */
#define RE_SYNTAX_POSIX_MINIMAL_BASIC                   \
  (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)

#define RE_SYNTAX_POSIX_EXTENDED                    \
  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS          \
   | RE_CONTEXT_INDEP_OPS   | RE_NO_BK_BRACES               \
   | RE_NO_BK_PARENS        | RE_NO_BK_VBAR             \
   | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD)

/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is
   removed and RE_NO_BK_REFS is added.  */
#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED                \
  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS          \
   | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES               \
   | RE_NO_BK_PARENS        | RE_NO_BK_REFS             \
   | RE_NO_BK_VBAR      | RE_UNMATCHED_RIGHT_PAREN_ORD)
/* [[[end syntaxes]]] */


/* Maximum number of duplicates an interval can allow.  Some systems
   (erroneously) define this in other header files, but we want our
   value, so remove any previous define.  */
#ifdef RE_DUP_MAX
# undef RE_DUP_MAX
#endif
/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows.  */
#define RE_DUP_MAX (0x7fff)


/* POSIX `cflags' bits (i.e., information for `regcomp').  */

/* If this bit is set, then use extended regular expression syntax.
   If not set, then use basic regular expression syntax.  */
#define REG_EXTENDED 1

/* If this bit is set, then ignore case when matching.
   If not set, then case is significant.  */
#define REG_ICASE (REG_EXTENDED << 1)

/* If this bit is set, then anchors do not match at newline
     characters in the string.
   If not set, then anchors do match at newlines.  */
#define REG_NEWLINE (REG_ICASE << 1)

/* If this bit is set, then report only success or fail in regexec.
   If not set, then returns differ between not matching and errors.  */
#define REG_NOSUB (REG_NEWLINE << 1)


/* POSIX `eflags' bits (i.e., information for regexec).  */

/* If this bit is set, then the beginning-of-line operator doesn't match
     the beginning of the string (presumably because it's not the
     beginning of a line).
   If not set, then the beginning-of-line operator does match the
     beginning of the string.  */
#define REG_NOTBOL 1

/* Like REG_NOTBOL, except for the end-of-line.  */
#define REG_NOTEOL (1 << 1)


/* If any error codes are removed, changed, or added, update the
   `re_error_msg' table in regex.c.  */
typedef enum
{
#ifdef _XOPEN_SOURCE
  REG_ENOSYS = -1,  /* This will never happen for this implementation.  */
#endif

  REG_NOERROR = 0,  /* Success.  */
  REG_NOMATCH,      /* Didn't find a match (for regexec).  */

  /* POSIX regcomp return error codes.  (In the order listed in the
     standard.)  */
  REG_BADPAT,       /* Invalid pattern.  */
  REG_ECOLLATE,     /* Not implemented.  */
  REG_ECTYPE,       /* Invalid character class name.  */
  REG_EESCAPE,      /* Trailing backslash.  */
  REG_ESUBREG,      /* Invalid back reference.  */
  REG_EBRACK,       /* Unmatched left bracket.  */
  REG_EPAREN,       /* Parenthesis imbalance.  */
  REG_EBRACE,       /* Unmatched \{.  */
  REG_BADBR,        /* Invalid contents of \{\}.  */
  REG_ERANGE,       /* Invalid range end.  */
  REG_ESPACE,       /* Ran out of memory.  */
  REG_BADRPT,       /* No preceding re for repetition op.  */

  /* Error codes we've added.  */
  REG_EEND,     /* Premature end.  */
  REG_ESIZE,        /* Compiled pattern bigger than 2^16 bytes.  */
  REG_ERPAREN       /* Unmatched ) or \); not returned from regcomp.  */
} reg_errcode_t;


/* This data structure represents a compiled pattern.  Before calling
   the pattern compiler, the fields `buffer', `allocated', `fastmap',
   `translate', and `no_sub' can be set.  After the pattern has been
   compiled, the `re_nsub' field is available.  All other fields are
   private to the regex routines.  */

#ifndef RE_TRANSLATE_TYPE
# define RE_TRANSLATE_TYPE char *
#endif

struct re_pattern_buffer
{
/* [[[begin pattern_buffer]]] */
    /* Space that holds the compiled pattern.  It is declared as
          `unsigned char *' because its elements are
           sometimes used as array indexes.  */
  unsigned char *buffer;

    /* Number of bytes to which `buffer' points.  */
  unsigned long int allocated;

    /* Number of bytes actually used in `buffer'.  */
  unsigned long int used;

        /* Syntax setting with which the pattern was compiled.  */
  reg_syntax_t syntax;

        /* Pointer to a fastmap, if any, otherwise zero.  re_search uses
           the fastmap, if there is one, to skip over impossible
           starting points for matches.  */
  char *fastmap;

        /* Either a translate table to apply to all characters before
           comparing them, or zero for no translation.  The translation
           is applied to a pattern when it is compiled and to a string
           when it is matched.  */
  RE_TRANSLATE_TYPE translate;

    /* Number of subexpressions found by the compiler.  */
  size_t re_nsub;

        /* Zero if this pattern cannot match the empty string, one else.
           Well, in truth it's used only in `re_search_2', to see
           whether or not we should use the fastmap, so we don't set
           this absolutely perfectly; see `re_compile_fastmap' (the
           `duplicate' case).  */
  unsigned can_be_null : 1;

        /* If REGS_UNALLOCATED, allocate space in the `regs' structure
             for `max (RE_NREGS, re_nsub + 1)' groups.
           If REGS_REALLOCATE, reallocate space if necessary.
           If REGS_FIXED, use what's there.  */
#define REGS_UNALLOCATED 0
#define REGS_REALLOCATE 1
#define REGS_FIXED 2
  unsigned regs_allocated : 2;

        /* Set to zero when `regex_compile' compiles a pattern; set to one
           by `re_compile_fastmap' if it updates the fastmap.  */
  unsigned fastmap_accurate : 1;

        /* If set, `re_match_2' does not return information about
           subexpressions.  */
  unsigned no_sub : 1;

        /* If set, a beginning-of-line anchor doesn't match at the
           beginning of the string.  */
  unsigned not_bol : 1;

        /* Similarly for an end-of-line anchor.  */
  unsigned not_eol : 1;

        /* If true, an anchor at a newline matches.  */
  unsigned newline_anchor : 1;

/* [[[end pattern_buffer]]] */
};

typedef struct re_pattern_buffer regex_t;


/* Type for byte offsets within the string.  POSIX mandates this.  */
typedef int regoff_t;


/* This is the structure we store register match data in.  See
   regex.texinfo for a full description of what registers match.  */
struct re_registers
{
  unsigned num_regs;
  regoff_t *start;
  regoff_t *end;
};


/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
   `re_match_2' returns information about at least this many registers
   the first time a `regs' structure is passed.  */
#ifndef RE_NREGS
# define RE_NREGS 30
#endif


/* POSIX specification for registers.  Aside from the different names than
   `re_registers', POSIX uses an array of structures, instead of a
   structure of arrays.  */
typedef struct
{
  regoff_t rm_so;  /* Byte offset from string's start to substring's start.  */
  regoff_t rm_eo;  /* Byte offset from string's start to substring's end.  */
} regmatch_t;


/* Declarations for routines.  */

/* To avoid duplicating every routine declaration -- once with a
   prototype (if we are ANSI), and once without (if we aren't) -- we
   use the following macro to declare argument types.  This
   unfortunately clutters up the declarations a bit, but I think it's
   worth it.  */

#if __STDC__

# define _RE_ARGS(args) args

#else /* not __STDC__ */

# define _RE_ARGS(args) ()

#endif /* not __STDC__ */

/* Sets the current default syntax to SYNTAX, and return the old syntax.
   You can also simply assign to the `re_syntax_options' variable.  */
extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));

/* Compile the regular expression PATTERN, with length LENGTH
   and syntax given by the global `re_syntax_options', into the buffer
   BUFFER.  Return NULL if successful, and an error string if not.  */
extern const char *re_compile_pattern
  _RE_ARGS ((const char *pattern, size_t length,
             struct re_pattern_buffer *buffer));


/* Compile a fastmap for the compiled pattern in BUFFER; used to
   accelerate searches.  Return 0 if successful and -2 if was an
   internal error.  */
extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));


/* Search in the string STRING (with length LENGTH) for the pattern
   compiled into BUFFER.  Start searching at position START, for RANGE
   characters.  Return the starting position of the match, -1 for no
   match, or -2 for an internal error.  Also return register
   information in REGS (if REGS and BUFFER->no_sub are nonzero).  */
extern int re_search
  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
            int length, int start, int range, struct re_registers *regs));


/* Like `re_search', but search in the concatenation of STRING1 and
   STRING2.  Also, stop searching at index START + STOP.  */
extern int re_search_2
  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
             int length1, const char *string2, int length2,
             int start, int range, struct re_registers *regs, int stop));


/* Like `re_search', but return how many characters in STRING the regexp
   in BUFFER matched, starting at position START.  */
extern int re_match
  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
             int length, int start, struct re_registers *regs));


/* Relates to `re_match' as `re_search_2' relates to `re_search'.  */
extern int re_match_2
  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
             int length1, const char *string2, int length2,
             int start, struct re_registers *regs, int stop));


/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
   ENDS.  Subsequent matches using BUFFER and REGS will use this memory
   for recording register information.  STARTS and ENDS must be
   allocated with malloc, and must each be at least `NUM_REGS * sizeof
   (regoff_t)' bytes long.

   If NUM_REGS == 0, then subsequent matches should allocate their own
   register data.

   Unless this function is called, the first search or match using
   PATTERN_BUFFER will allocate its own register data, without
   freeing the old data.  */
extern void re_set_registers
  _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
             unsigned num_regs, regoff_t *starts, regoff_t *ends));

#if defined _REGEX_RE_COMP || defined _LIBC
# ifndef _CRAY
/* 4.2 bsd compatibility.  */
extern char *re_comp _RE_ARGS ((const char *));
extern int re_exec _RE_ARGS ((const char *));
# endif
#endif

/* GCC 2.95 and later have "__restrict"; C99 compilers have
   "restrict", and "configure" may have defined "restrict".  */
#ifndef __restrict
# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__))
#  if defined restrict || 199901L <= __STDC_VERSION__
#   define __restrict restrict
#  else
#   define __restrict
#  endif
# endif
#endif
/* gcc 3.1 and up support the [restrict] syntax.  */
#ifndef __restrict_arr
# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
#  define __restrict_arr __restrict
# else
#  define __restrict_arr
# endif
#endif

/* POSIX compatibility.  */
extern int regcomp _RE_ARGS ((regex_t *__restrict __preg,
                  const char *__restrict __pattern,
                  int __cflags));

extern int regexec _RE_ARGS ((const regex_t *__restrict __preg,
                  const char *__restrict __string, size_t __nmatch,
                  regmatch_t __pmatch[__restrict_arr],
                  int __eflags));

extern size_t regerror _RE_ARGS ((int __errcode, const regex_t *__preg,
                  char *__errbuf, size_t __errbuf_size));

extern void regfree _RE_ARGS ((regex_t *__preg));


#ifdef __cplusplus
}
#endif  /* C++ */

#endif /* regex.h */


/*
Local variables:
make-backup-files: t
version-control: t
trim-versions-without-asking: nil
End:
*/

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtol.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtol.h used in: Step 855, Step 867, Step 873, Step 875, Step 877, Step 883, Step 897, Step 899, Step 901, Step 907, Step 919, Step 923, Step 925, Step 929, Step 933, Step 939
/* A more useful interface to strtol.
   Copyright 1995, 1996, 1998, 1999, 2001 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.  */

#ifndef XSTRTOL_H_
# define XSTRTOL_H_ 1

# if HAVE_INTTYPES_H
#  include <inttypes.h> /* for uintmax_t */
# endif

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

# ifndef _STRTOL_ERROR
enum strtol_error
  {
    LONGINT_OK, LONGINT_INVALID, LONGINT_INVALID_SUFFIX_CHAR, LONGINT_OVERFLOW
  };
typedef enum strtol_error strtol_error;
# endif

# define _DECLARE_XSTRTOL(name, type) \
  strtol_error \
    name PARAMS ((const char *s, char **ptr, int base, \
          type *val, const char *valid_suffixes));
_DECLARE_XSTRTOL (xstrtol, long int)
_DECLARE_XSTRTOL (xstrtoul, unsigned long int)
_DECLARE_XSTRTOL (xstrtoimax, intmax_t)
_DECLARE_XSTRTOL (xstrtoumax, uintmax_t)

# define _STRTOL_ERROR(Exit_code, Str, Argument_type_string, Err)   \
  do                                    \
    {                                   \
      switch ((Err))                            \
    {                               \
    case LONGINT_OK:                        \
      abort ();                         \
                                    \
    case LONGINT_INVALID:                       \
      error ((Exit_code), 0, "invalid %s `%s'",         \
         (Argument_type_string), (Str));            \
      break;                            \
                                    \
    case LONGINT_INVALID_SUFFIX_CHAR:               \
      error ((Exit_code), 0, "invalid character following %s in `%s'", \
         (Argument_type_string), (Str));            \
      break;                            \
                                    \
    case LONGINT_OVERFLOW:                      \
      error ((Exit_code), 0, "%s `%s' too large",           \
         (Argument_type_string), (Str));            \
      break;                            \
    }                               \
    }                                   \
  while (0)

# define STRTOL_FATAL_ERROR(Str, Argument_type_string, Err)     \
  _STRTOL_ERROR (2, Str, Argument_type_string, Err)

# define STRTOL_FAIL_WARN(Str, Argument_type_string, Err)       \
  _STRTOL_ERROR (0, Str, Argument_type_string, Err)

#endif /* not XSTRTOL_H_ */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/cut.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/cut.c used in: Step 857
/* cut - remove parts of lines of files
   Copyright (C) 1984, 1997, 1998, 1999, 2000, 2001, 2002, 2003 by David M. Ihnat

   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.  */

/* Written by David Ihnat.  */

/* POSIX changes, bug fixes, long-named options, and cleanup
   by David MacKenzie <djm@gnu.ai.mit.edu>.

   Rewrite cut_fields and cut_bytes -- Jim Meyering.  */

#include <config.h>

#include <stdio.h>
#include <assert.h>
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
#include "getstr.h"
#include "closeout.h"
#include "error.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "cut"

#define AUTHORS N_ ("David Ihnat, David MacKenzie, and Jim Meyering")

#define FATAL_ERROR(Message)                        \
  do                                    \
    {                                   \
      error (0, 0, (Message));                      \
      usage (2);                            \
    }                                   \
  while (0)

/* Append LOW, HIGH to the list RP of range pairs, allocating additional
   space if necessary.  Update local variable N_RP.  When allocating,
   update global variable N_RP_ALLOCATED.  */

#define ADD_RANGE_PAIR(rp, low, high)                   \
  do                                    \
    {                                   \
      if (n_rp >= n_rp_allocated)                   \
    {                               \
      n_rp_allocated *= 2;                      \
      (rp) = (struct range_pair *) xrealloc ((char *) (rp),     \
                   n_rp_allocated * sizeof (*(rp)));    \
    }                               \
      rp[n_rp].lo = (low);                      \
      rp[n_rp].hi = (high);                     \
      ++n_rp;                               \
    }                                   \
  while (0)

struct range_pair
  {
    unsigned int lo;
    unsigned int hi;
  };

/* This buffer is used to support the semantics of the -s option
   (or lack of same) when the specified field list includes (does
   not include) the first field.  In both of those cases, the entire
   first field must be read into this buffer to determine whether it
   is followed by a delimiter or a newline before any of it may be
   output.  Otherwise, cut_fields can do the job without using this
   buffer.  */
static char *field_1_buffer;

/* The number of bytes allocated for FIELD_1_BUFFER.  */
static size_t field_1_bufsize;

/* The largest field or byte index used as an endpoint of a closed
   or degenerate range specification;  this doesn't include the starting
   index of right-open-ended ranges.  For example, with either range spec
   `2-5,9-', `2-3,5,9-' this variable would be set to 5.  */
static unsigned int max_range_endpoint;

/* If nonzero, this is the index of the first field in a range that goes
   to end of line. */
static unsigned int eol_range_start;

/* A nonzero, non-1 value with which to distinguish the index
   corresponding to the lower bound of a range.  */
#define RANGE_START_SENTINEL 2

/* In byte mode, which bytes to output.
   In field mode, which DELIM-separated fields to output.
   Both bytes and fields are numbered starting with 1,
   so the zeroth element of this array is unused.
   A field or byte K has been selected if
   (K <= MAX_RANGE_ENDPOINT and PRINTABLE_FIELD[K])
    || (EOL_RANGE_START > 0 && K >= EOL_RANGE_START).  */
static int *printable_field;

enum operating_mode
  {
    undefined_mode,

    /* Output characters that are in the given bytes. */
    byte_mode,

    /* Output the given delimeter-separated fields. */
    field_mode
  };

/* The name this program was run with. */
char *program_name;

static enum operating_mode operating_mode;

/* If nonzero do not output lines containing no delimeter characters.
   Otherwise, all such lines are printed.  This option is valid only
   with field mode.  */
static int suppress_non_delimited;

/* The delimeter character for field mode. */
static int delim;

/* Nonzero if the --output-delimiter=STRING option was specified.  */
static int output_delimiter_specified;

/* The length of output_delimiter_string.  */
static size_t output_delimiter_length;

/* The output field separator string.  Defaults to the 1-character
   string consisting of the input delimiter.  */
static char *output_delimiter_string;

/* Nonzero if we have ever read standard input. */
static int have_read_stdin;

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  OUTPUT_DELIMITER_OPTION = CHAR_MAX + 1
};

static struct option const longopts[] =
{
  {"bytes", required_argument, 0, 'b'},
  {"characters", required_argument, 0, 'c'},
  {"fields", required_argument, 0, 'f'},
  {"delimiter", required_argument, 0, 'd'},
  {"only-delimited", no_argument, 0, 's'},
  {"output-delimiter", required_argument, 0, OUTPUT_DELIMITER_OPTION},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {0, 0, 0, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
          program_name);
      fputs (_("\
Print selected parts of lines from each FILE to standard output.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -b, --bytes=LIST        output only these bytes\n\
  -c, --characters=LIST   output only these characters\n\
  -d, --delimiter=DELIM   use DELIM instead of TAB for field delimiter\n\
"), stdout);
      fputs (_("\
  -f, --fields=LIST       output only these fields;  also print any line\n\
                            that contains no delimiter character, unless\n\
                            the -s option is specified\n\
  -n                      (ignored)\n\
"), stdout);
      fputs (_("\
  -s, --only-delimited    do not print lines not containing delimiters\n\
      --output-delimiter=STRING  use STRING as the output delimiter\n\
                            the default is to use the input delimiter\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
Use one, and only one of -b, -c or -f.  Each LIST is made up of one\n\
range, or many ranges separated by commas.  Each range is one of:\n\
\n\
  N     N'th byte, character or field, counted from 1\n\
  N-    from N'th byte, character or field, to end of line\n\
  N-M   from N'th to M'th (included) byte, character or field\n\
  -M    from first to M'th (included) byte, character or field\n\
\n\
With no FILE, or when FILE is -, read standard input.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Return nonzero if the K'th field or byte is printable.
   When returning nonzero, if RANGE_START is non-NULL,
   set *RANGE_START to nonzero if K is the beginning of a range, and
   set *RANGE_START to zero if K is not the beginning of a range.  */

static int
print_kth (unsigned int k, int *range_start)
{
  if (0 < eol_range_start && eol_range_start <= k)
    {
      if (range_start)
    *range_start = (k == eol_range_start);
      return 1;
    }

  if (k <= max_range_endpoint && printable_field[k])
    {
      if (range_start)
    *range_start = (printable_field[k] == RANGE_START_SENTINEL);
      return 1;
    }

  return 0;
}

/* Given the list of field or byte range specifications FIELDSTR, set
   MAX_RANGE_ENDPOINT and allocate and initialize the PRINTABLE_FIELD
   array.  If there is a right-open-ended range, set EOL_RANGE_START
   to its starting index.  FIELDSTR should be composed of one or more
   numbers or ranges of numbers, separated by blanks or commas.
   Incomplete ranges may be given: `-m' means `1-m'; `n-' means `n'
   through end of line.  Return nonzero if FIELDSTR contains at least
   one field specification, zero otherwise.  */

/* FIXME-someday:  What if the user wants to cut out the 1,000,000-th field
   of some huge input file?  This function shouldn't have to allocate a table
   of a million ints just so we can test every field < 10^6 with an array
   dereference.  Instead, consider using a dynamic hash table.  It would be
   simpler and nearly as good a solution to use a 32K x 4-byte table with
   one bit per field index instead of a whole `int' per index.  */

static int
set_fields (const char *fieldstr)
{
  unsigned int initial = 1; /* Value of first number in a range.  */
  unsigned int value = 0;   /* If nonzero, a number being accumulated.  */
  int dash_found = 0;       /* Nonzero if a '-' is found in this field.  */
  int field_found = 0;      /* Non-zero if at least one field spec
                   has been processed.  */

  struct range_pair *rp;
  unsigned int n_rp;
  unsigned int n_rp_allocated;
  unsigned int i;

  n_rp = 0;
  n_rp_allocated = 16;
  rp = (struct range_pair *) xmalloc (n_rp_allocated * sizeof (*rp));

  /* Collect and store in RP the range end points.
     It also sets EOL_RANGE_START if appropriate.  */

  for (;;)
    {
      if (*fieldstr == '-')
    {
      /* Starting a range. */
      if (dash_found)
        FATAL_ERROR (_("invalid byte or field list"));
      dash_found++;
      fieldstr++;

      if (value)
        {
          initial = value;
          value = 0;
        }
      else
        initial = 1;
    }
      else if (*fieldstr == ',' || ISBLANK (*fieldstr) || *fieldstr == '\0')
    {
      /* Ending the string, or this field/byte sublist. */
      if (dash_found)
        {
          dash_found = 0;

          /* A range.  Possibilites: -n, m-n, n-.
         In any case, `initial' contains the start of the range. */
          if (value == 0)
        {
          /* `n-'.  From `initial' to end of line. */
          eol_range_start = initial;
          field_found = 1;
        }
          else
        {
          /* `m-n' or `-n' (1-n). */
          if (value < initial)
            FATAL_ERROR (_("invalid byte or field list"));

          /* Is there already a range going to end of line? */
          if (eol_range_start != 0)
            {
              /* Yes.  Is the new sequence already contained
             in the old one?  If so, no processing is
             necessary. */
              if (initial < eol_range_start)
            {
              /* No, the new sequence starts before the
                 old.  Does the old range going to end of line
                 extend into the new range?  */
              if (eol_range_start <= value)
                {
                  /* Yes.  Simply move the end of line marker. */
                  eol_range_start = initial;
                }
              else
                {
                  /* No.  A simple range, before and disjoint from
                 the range going to end of line.  Fill it. */
                  ADD_RANGE_PAIR (rp, initial, value);
                }

              /* In any case, some fields were selected. */
              field_found = 1;
            }
            }
          else
            {
              /* There is no range going to end of line. */
              ADD_RANGE_PAIR (rp, initial, value);
              field_found = 1;
            }
          value = 0;
        }
        }
      else if (value != 0)
        {
          /* A simple field number, not a range. */
          ADD_RANGE_PAIR (rp, value, value);
          value = 0;
          field_found = 1;
        }

      if (*fieldstr == '\0')
        {
          break;
        }

      fieldstr++;
    }
      else if (ISDIGIT (*fieldstr))
    {
      /* FIXME: detect overflow?  */
      value = 10 * value + *fieldstr - '0';
      fieldstr++;
    }
      else
    FATAL_ERROR (_("invalid byte or field list"));
    }

  max_range_endpoint = 0;
  for (i = 0; i < n_rp; i++)
    {
      if (rp[i].hi > max_range_endpoint)
    max_range_endpoint = rp[i].hi;
    }

  /* Allocate an array large enough so that it may be indexed by
     the field numbers corresponding to all finite ranges
     (i.e. `2-6' or `-4', but not `5-') in FIELDSTR.  */

  printable_field = (int *) xmalloc ((max_range_endpoint + 1) * sizeof (int));
  memset (printable_field, 0, (max_range_endpoint + 1) * sizeof (int));

  /* Set the array entries corresponding to integers in the ranges of RP.  */
  for (i = 0; i < n_rp; i++)
    {
      unsigned int j = rp[i].lo;

      /* Mark the first position of field or range with a sentinel,
     but not if it's already part of another range.  */
      if (j <= rp[i].hi && ! printable_field[j])
    printable_field[j] = RANGE_START_SENTINEL;
      for (++j; j <= rp[i].hi; j++)
    {
      printable_field[j] = 1;
    }
    }

  free (rp);

  return field_found;
}

/* Read from stream STREAM, printing to standard output any selected bytes.  */

static void
cut_bytes (FILE *stream)
{
  unsigned int byte_idx;    /* Number of bytes in the line so far. */
  /* Whether to begin printing delimiters between ranges for the current line.
     Set after we've begun printing data corresponding to the first range.  */
  int print_delimiter;

  byte_idx = 0;
  print_delimiter = 0;
  while (1)
    {
      register int c;       /* Each character from the file. */

      c = getc (stream);

      if (c == '\n')
    {
      putchar ('\n');
      byte_idx = 0;
      print_delimiter = 0;
    }
      else if (c == EOF)
    {
      if (byte_idx > 0)
        putchar ('\n');
      break;
    }
      else
    {
      int range_start;
      if (print_kth (++byte_idx, &range_start))
        {
          if (range_start && print_delimiter && output_delimiter_specified)
        {
          fwrite (output_delimiter_string, sizeof (char),
              output_delimiter_length, stdout);
        }
          print_delimiter = 1;
          putchar (c);
        }
    }
    }
}

/* Read from stream STREAM, printing to standard output any selected fields.  */

static void
cut_fields (FILE *stream)
{
  int c;
  unsigned int field_idx;
  int found_any_selected_field;
  int buffer_first_field;
  int empty_input;

  found_any_selected_field = 0;
  field_idx = 1;

  c = getc (stream);
  empty_input = (c == EOF);
  if (c != EOF)
    ungetc (c, stream);

  /* To support the semantics of the -s flag, we may have to buffer
     all of the first field to determine whether it is `delimited.'
     But that is unnecessary if all non-delimited lines must be printed
     and the first field has been selected, or if non-delimited lines
     must be suppressed and the first field has *not* been selected.
     That is because a non-delimited line has exactly one field.  */
  buffer_first_field = (suppress_non_delimited ^ !print_kth (1, NULL));

  while (1)
    {
      if (field_idx == 1 && buffer_first_field)
    {
      int len;
      size_t n_bytes;

      len = getstr (&field_1_buffer, &field_1_bufsize, stream,
            delim, '\n', 0);
      if (len < 0)
        {
          if (ferror (stream) || feof (stream))
        break;
          xalloc_die ();
        }

      n_bytes = len;
      assert (n_bytes != 0);

      /* If the first field extends to the end of line (it is not
         delimited) and we are printing all non-delimited lines,
         print this one.  */
      if ((unsigned char) field_1_buffer[n_bytes - 1] != delim)
        {
          if (suppress_non_delimited)
        {
          /* Empty.  */
        }
          else
        {
          fwrite (field_1_buffer, sizeof (char), n_bytes, stdout);
          /* Make sure the output line is newline terminated.  */
          if (field_1_buffer[n_bytes - 1] != '\n')
            putchar ('\n');
        }
          continue;
        }
      if (print_kth (1, NULL))
        {
          /* Print the field, but not the trailing delimiter.  */
          fwrite (field_1_buffer, sizeof (char), n_bytes - 1, stdout);
          found_any_selected_field = 1;
        }
      ++field_idx;
    }

      if (c != EOF)
    {
      if (print_kth (field_idx, NULL))
        {
          if (found_any_selected_field)
        {
          fwrite (output_delimiter_string, sizeof (char),
              output_delimiter_length, stdout);
        }
          found_any_selected_field = 1;

          while ((c = getc (stream)) != delim && c != '\n' && c != EOF)
        {
          putchar (c);
        }
        }
      else
        {
          while ((c = getc (stream)) != delim && c != '\n' && c != EOF)
        {
          /* Empty.  */
        }
        }
    }

      if (c == '\n')
    {
      c = getc (stream);
      if (c != EOF)
        {
          ungetc (c, stream);
          c = '\n';
        }
    }

      if (c == delim)
    ++field_idx;
      else if (c == '\n' || c == EOF)
    {
      if (found_any_selected_field
          || (!empty_input && !(suppress_non_delimited && field_idx == 1)))
        putchar ('\n');
      if (c == EOF)
        break;
      field_idx = 1;
      found_any_selected_field = 0;
    }
    }
}

static void
cut_stream (FILE *stream)
{
  if (operating_mode == byte_mode)
    cut_bytes (stream);
  else
    cut_fields (stream);
}

/* Process file FILE to standard output.
   Return 0 if successful, 1 if not. */

static int
cut_file (char *file)
{
  FILE *stream;

  if (STREQ (file, "-"))
    {
      have_read_stdin = 1;
      stream = stdin;
    }
  else
    {
      stream = fopen (file, "r");
      if (stream == NULL)
    {
      error (0, errno, "%s", file);
      return 1;
    }
    }

  cut_stream (stream);

  if (ferror (stream))
    {
      error (0, errno, "%s", file);
      return 1;
    }
  if (STREQ (file, "-"))
    clearerr (stream);      /* Also clear EOF. */
  else if (fclose (stream) == EOF)
    {
      error (0, errno, "%s", file);
      return 1;
    }
  return 0;
}

int
main (int argc, char **argv)
{
  int optc, exit_status = 0;
  int delim_specified = 0;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  operating_mode = undefined_mode;

  /* By default, all non-delimited lines are printed.  */
  suppress_non_delimited = 0;

  delim = '\0';
  have_read_stdin = 0;

  while ((optc = getopt_long (argc, argv, "b:c:d:f:ns", longopts, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:
      break;

    case 'b':
    case 'c':
      /* Build the byte list. */
      if (operating_mode != undefined_mode)
        FATAL_ERROR (_("only one type of list may be specified"));
      operating_mode = byte_mode;
      if (set_fields (optarg) == 0)
        FATAL_ERROR (_("missing list of positions"));
      break;

    case 'f':
      /* Build the field list. */
      if (operating_mode != undefined_mode)
        FATAL_ERROR (_("only one type of list may be specified"));
      operating_mode = field_mode;
      if (set_fields (optarg) == 0)
        FATAL_ERROR (_("missing list of fields"));
      break;

    case 'd':
      /* New delimiter. */
      /* Interpret -d '' to mean `use the NUL byte as the delimiter.'  */
      if (optarg[0] != '\0' && optarg[1] != '\0')
        FATAL_ERROR (_("the delimiter must be a single character"));
      delim = (unsigned char) optarg[0];
      delim_specified = 1;
      break;

    case OUTPUT_DELIMITER_OPTION:
      output_delimiter_specified = 1;
      /* Interpret --output-delimiter='' to mean
         `use the NUL byte as the delimiter.'  */
      output_delimiter_length = (optarg[0] == '\0'
                     ? 1 : strlen (optarg));
      output_delimiter_string = xstrdup (optarg);
      break;

    case 'n':
      break;

    case 's':
      suppress_non_delimited = 1;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (2);
    }
    }

  if (operating_mode == undefined_mode)
    FATAL_ERROR (_("you must specify a list of bytes, characters, or fields"));

  if (delim != '\0' && operating_mode != field_mode)
    FATAL_ERROR (_("an input delimiter may be specified only\
 when operating on fields"));

  if (suppress_non_delimited && operating_mode != field_mode)
    FATAL_ERROR (_("suppressing non-delimited lines makes sense\n\
\tonly when operating on fields"));

  if (!delim_specified)
    delim = '\t';

  if (output_delimiter_string == NULL)
    {
      static char dummy[2];
      dummy[0] = delim;
      dummy[1] = '\0';
      output_delimiter_string = dummy;
      output_delimiter_length = 1;
    }

  if (optind == argc)
    exit_status |= cut_file ("-");
  else
    for (; optind < argc; optind++)
      exit_status |= cut_file (argv[optind]);

  if (have_read_stdin && fclose (stdin) == EOF)
    {
      error (0, errno, "-");
      exit_status = 1;
    }

  exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getstr.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getstr.h used in: Step 857
#ifndef GETSTR_H_
# define GETSTR_H_ 1

# include <stdio.h>

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

int
getstr PARAMS ((char **lineptr, size_t *n, FILE *stream,
        int delim1, int delim2,
        size_t offset));

#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/dirname.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/dirname.c used in: Step 859
/* dirname -- strip filename suffix from pathname
   Copyright (C) 1990-1997, 1999-2002 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.  */

/* Written by David MacKenzie and Jim Meyering. */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>

#include "system.h"
#include "long-options.h"
#include "error.h"
#include "dirname.h"
#include "closeout.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "dirname"

#define AUTHORS N_ ("David MacKenzie and Jim Meyering")

/* The name this program was run with. */
char *program_name;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s NAME\n\
  or:  %s OPTION\n\
"),
          program_name, program_name);
      fputs (_("\
Print NAME with its trailing /component removed; if NAME contains no /'s,\n\
output `.' (meaning the current directory).\n\
\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  static char const dot = '.';
  char const *result;
  size_t len;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              AUTHORS, usage);
  /* The above handles --help and --version.
     Since there is no other invocation of getopt, handle `--' here.  */
  if (argc > 1 && STREQ (argv[1], "--"))
    {
      --argc;
      ++argv;
    }

  if (argc != 2)
    {
      error (0, 0, argc < 2 ? _("too few arguments")
         : _("too many arguments"));
      usage (EXIT_FAILURE);
    }

  result = argv[1];
  len = dir_len (result);

  if (! len)
    {
      result = &dot;
      len = 1;
    }

  fwrite (result, 1, len, stdout);
  putchar ('\n');

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/echo.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/echo.c used in: Step 861
/* echo.c, derived from code echo.c in Bash.
   Copyright (C) 87,89, 1991-1997, 1999-2002 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 <config.h>
#include <stdio.h>
#include <sys/types.h>
#include "system.h"
#include "closeout.h"
#include "long-options.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "echo"

#define AUTHORS "FIXME unknown"

/* echo [-neE] [arg ...]
Output the ARGs.  If -n is specified, the trailing newline is
suppressed.  If the -e option is given, interpretation of the
following backslash-escaped characters is turned on:
    \a  alert (bell)
    \b  backspace
    \c  suppress trailing newline
    \f  form feed
    \n  new line
    \r  carriage return
    \t  horizontal tab
    \v  vertical tab
    \\  backslash
    \num    the character whose ASCII code is NUM (octal).

You can explicitly turn off the interpretation of the above characters
on System V systems with the -E option.
*/

/* If defined, interpret backslash escapes if -e is given.  */
#define V9_ECHO

/* If defined, interpret backslash escapes unless -E is given.
   V9_ECHO must also be defined.  */
/* #define V9_DEFAULT */

#if defined (V9_ECHO)
# if defined (V9_DEFAULT)
#  define VALID_ECHO_OPTIONS "neE"
# else
#  define VALID_ECHO_OPTIONS "ne"
# endif /* !V9_DEFAULT */
#else /* !V9_ECHO */
# define VALID_ECHO_OPTIONS "n"
#endif /* !V9_ECHO */

/* The name this program was run with. */
char *program_name;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]... [STRING]...\n"), program_name);
      fputs (_("\
Echo the STRING(s) to standard output.\n\
\n\
  -n              do not output the trailing newline\n\
  -e              enable interpretation of the backslash-escaped characters\n\
                    listed below\n\
  -E              disable interpretation of those sequences in STRINGs\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
Without -E, the following sequences are recognized and interpolated:\n\
\n\
  \\NNN   the character whose ASCII code is NNN (octal)\n\
  \\\\     backslash\n\
  \\a     alert (BEL)\n\
  \\b     backspace\n\
"), stdout);
      fputs (_("\
  \\c     suppress trailing newline\n\
  \\f     form feed\n\
  \\n     new line\n\
  \\r     carriage return\n\
  \\t     horizontal tab\n\
  \\v     vertical tab\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

/* Print the words in LIST to standard output.  If the first word is
   `-n', then don't print a trailing newline.  We also support the
   echo syntax from Version 9 unix systems. */

int
main (int argc, char **argv)
{
  int display_return = 1, do_v9 = 0;
  int allow_options = 1;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  /* Don't recognize --help or --version if POSIXLY_CORRECT is set.  */
  if (getenv ("POSIXLY_CORRECT") == NULL)
    parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              AUTHORS, usage);
  else
    allow_options = 0;

/* System V machines already have a /bin/sh with a v9 behaviour.  We
   use the identical behaviour for these machines so that the
   existing system shell scripts won't barf. */
#if defined (V9_ECHO) && defined (V9_DEFAULT)
  do_v9 = allow_options;
#endif

  --argc;
  ++argv;

  while (argc > 0 && *argv[0] == '-')
    {
      register char *temp;
      register int i;

      /* If it appears that we are handling options, then make sure that
     all of the options specified are actually valid.  Otherwise, the
     string should just be echoed. */
      temp = argv[0] + 1;

      for (i = 0; temp[i]; i++)
    {
      if (strrchr (VALID_ECHO_OPTIONS, temp[i]) == 0)
        goto just_echo;
    }

      if (!*temp)
    goto just_echo;

      /* All of the options in TEMP are valid options to ECHO.
     Handle them. */
      while (*temp)
    {
      if (allow_options && *temp == 'n')
        display_return = 0;
#if defined (V9_ECHO)
      else if (allow_options && *temp == 'e')
        do_v9 = 1;
# if defined (V9_DEFAULT)
      else if (allow_options && *temp == 'E')
        do_v9 = 0;
# endif /* V9_DEFAULT */
#endif /* V9_ECHO */
      else
        goto just_echo;

      temp++;
    }
      argc--;
      argv++;
    }

just_echo:

  if (argc > 0)
    {
#if defined (V9_ECHO)
      if (do_v9)
    {
      while (argc > 0)
        {
          register char *s = argv[0];
          register int c;

          while ((c = *s++))
        {
          if (c == '\\' && *s)
            {
              switch (c = *s++)
            {
            case 'a': c = '\007'; break;
            case 'b': c = '\b'; break;
            case 'c': display_return = 0; continue;
            case 'f': c = '\f'; break;
            case 'n': c = '\n'; break;
            case 'r': c = '\r'; break;
            case 't': c = '\t'; break;
            case 'v': c = (int) 0x0B; break;
            case '0': case '1': case '2': case '3':
            case '4': case '5': case '6': case '7':
              c -= '0';
              if (*s >= '0' && *s <= '7')
                c = c * 8 + (*s++ - '0');
              if (*s >= '0' && *s <= '7')
                c = c * 8 + (*s++ - '0');
              break;
            case '\\': break;
            default:  putchar ('\\'); break;
            }
            }
          putchar(c);
        }
          argc--;
          argv++;
          if (argc > 0)
        putchar(' ');
        }
    }
      else
#endif /* V9_ECHO */
    {
      while (argc > 0)
        {
          fputs (argv[0], stdout);
          argc--;
          argv++;
          if (argc > 0)
        putchar (' ');
        }
    }
    }
  if (display_return)
    putchar ('\n');
  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/expand.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/expand.c used in: Step 863
/* expand - convert tabs to spaces
   Copyright (C) 89, 91, 1995-2002 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.  */

/* By default, convert all tabs to spaces.
   Preserves backspace characters in the output; they decrement the
   column count for tab calculations.
   The default action is equivalent to -8.

   Options:
   --tabs=tab1[,tab2[,...]]
   -t tab1[,tab2[,...]]
   -tab1[,tab2[,...]]   If only one tab stop is given, set the tabs tab1
            spaces apart instead of the default 8.  Otherwise,
            set the tabs at columns tab1, tab2, etc. (numbered from
            0); replace any tabs beyond the tabstops given with
            single spaces.
   --initial
   -i           Only convert initial tabs on each line to spaces.

   David MacKenzie <djm@gnu.ai.mit.edu> */

#include <config.h>

#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
#include "closeout.h"
#include "error.h"
#include "posixver.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "expand"

#define AUTHORS "David MacKenzie"

/* The number of bytes added at a time to the amount of memory
   allocated for the output line. */
#define OUTPUT_BLOCK 256

/* The number of bytes added at a time to the amount of memory
   allocated for the list of tabstops. */
#define TABLIST_BLOCK 256

/* The name this program was run with. */
char *program_name;

/* If nonzero, convert blanks even after nonblank characters have been
   read on the line. */
static int convert_entire_line;

/* If nonzero, the size of all tab stops.  If zero, use `tab_list' instead. */
static int tab_size;

/* Array of the explicit column numbers of the tab stops;
   after `tab_list' is exhausted, each additional tab is replaced
   by a space.  The first column is column 0. */
static int *tab_list;

/* The index of the first invalid element of `tab_list',
   where the next element can be added. */
static int first_free_tab;

/* Null-terminated array of input filenames. */
static char **file_list;

/* Default for `file_list' if no files are given on the command line. */
static char *stdin_argv[] =
{
  "-", NULL
};

/* Nonzero if we have ever read standard input. */
static int have_read_stdin;

/* Status to return to the system. */
static int exit_status;

static struct option const longopts[] =
{
  {"tabs", required_argument, NULL, 't'},
  {"initial", no_argument, NULL, 'i'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
          program_name);
      fputs (_("\
Convert tabs in each FILE to spaces, writing to standard output.\n\
With no FILE, or when FILE is -, read standard input.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -i, --initial       do not convert TABs after non whitespace\n\
  -t, --tabs=NUMBER   have tabs NUMBER characters apart, not 8\n\
"), stdout);
      fputs (_("\
  -t, --tabs=LIST     use comma separated list of explicit tab positions\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Add tab stop TABVAL to the end of `tab_list', except
   if TABVAL is -1, do nothing. */

static void
add_tabstop (int tabval)
{
  if (tabval == -1)
    return;
  if (first_free_tab % TABLIST_BLOCK == 0)
    tab_list = (int *) xrealloc ((char *) tab_list,
                 (first_free_tab
                  + TABLIST_BLOCK * sizeof (tab_list[0])));
  tab_list[first_free_tab++] = tabval;
}

/* Add the comma or blank separated list of tabstops STOPS
   to the list of tabstops. */

static void
parse_tabstops (char *stops)
{
  int tabval = -1;

  for (; *stops; stops++)
    {
      if (*stops == ',' || ISBLANK (*stops))
    {
      add_tabstop (tabval);
      tabval = -1;
    }
      else if (ISDIGIT (*stops))
    {
      if (tabval == -1)
        tabval = 0;
      tabval = tabval * 10 + *stops - '0';
    }
      else
    error (EXIT_FAILURE, 0, _("tab size contains an invalid character"));
    }

  add_tabstop (tabval);
}

/* Check that the list of tabstops TABS, with ENTRIES entries,
   contains only nonzero, ascending values. */

static void
validate_tabstops (int *tabs, int entries)
{
  int prev_tab = 0;
  int i;

  for (i = 0; i < entries; i++)
    {
      if (tabs[i] == 0)
    error (EXIT_FAILURE, 0, _("tab size cannot be 0"));
      if (tabs[i] <= prev_tab)
    error (EXIT_FAILURE, 0, _("tab sizes must be ascending"));
      prev_tab = tabs[i];
    }
}

/* Close the old stream pointer FP if it is non-NULL,
   and return a new one opened to read the next input file.
   Open a filename of `-' as the standard input.
   Return NULL if there are no more input files.  */

static FILE *
next_file (FILE *fp)
{
  static char *prev_file;
  char *file;

  if (fp)
    {
      if (ferror (fp))
    {
      error (0, errno, "%s", prev_file);
      exit_status = 1;
    }
      if (fp == stdin)
    clearerr (fp);      /* Also clear EOF. */
      else if (fclose (fp) == EOF)
    {
      error (0, errno, "%s", prev_file);
      exit_status = 1;
    }
    }

  while ((file = *file_list++) != NULL)
    {
      if (file[0] == '-' && file[1] == '\0')
    {
      have_read_stdin = 1;
      prev_file = file;
      return stdin;
    }
      fp = fopen (file, "r");
      if (fp)
    {
      prev_file = file;
      return fp;
    }
      error (0, errno, "%s", file);
      exit_status = 1;
    }
  return NULL;
}

/* Change tabs to spaces, writing to stdout.
   Read each file in `file_list', in order. */

static void
expand (void)
{
  FILE *fp;         /* Input stream. */
  int c;            /* Each input character. */
  int tab_index = 0;        /* Index in `tab_list' of next tabstop. */
  int column = 0;       /* Column on screen of the next char. */
  int next_tab_column;      /* Column the next tab stop is on. */
  int convert = 1;      /* If nonzero, perform translations. */

  fp = next_file ((FILE *) NULL);
  if (fp == NULL)
    return;

  /* Binary I/O will preserve the original EOL style (DOS/Unix) of files.  */
  SET_BINARY2 (fileno (fp), STDOUT_FILENO);

  for (;;)
    {
      c = getc (fp);
      if (c == EOF)
    {
      fp = next_file (fp);
      if (fp == NULL)
        break;      /* No more files. */
      else
        {
          SET_BINARY2 (fileno (fp), STDOUT_FILENO);
          continue;
        }
    }

      if (c == '\n')
    {
      putchar (c);
      tab_index = 0;
      column = 0;
      convert = 1;
    }
      else if (c == '\t' && convert)
    {
      if (tab_size == 0)
        {
          /* Do not let tab_index == first_free_tab;
         stop when it is 1 less. */
          while (tab_index < first_free_tab - 1
             && column >= tab_list[tab_index])
        tab_index++;
          next_tab_column = tab_list[tab_index];
          if (tab_index < first_free_tab - 1)
        tab_index++;
          if (column >= next_tab_column)
        next_tab_column = column + 1; /* Ran out of tab stops. */
        }
      else
        {
          next_tab_column = column + tab_size - column % tab_size;
        }
      while (column < next_tab_column)
        {
          putchar (' ');
          ++column;
        }
    }
      else
    {
      if (convert)
        {
          if (c == '\b')
        {
          if (column > 0)
            --column;
        }
          else
        {
          ++column;
          if (convert_entire_line == 0)
            convert = 0;
        }
        }
      putchar (c);
    }
    }
}

int
main (int argc, char **argv)
{
  int tabval = -1;      /* Value of tabstop being read, or -1. */
  int c;            /* Option character. */

  bool obsolete_tablist = false;

  have_read_stdin = 0;
  exit_status = 0;
  convert_entire_line = 1;
  tab_list = NULL;
  first_free_tab = 0;
  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  while ((c = getopt_long (argc, argv, "it:,0123456789", longopts, NULL)) != -1)
    {
      switch (c)
    {
    case 0:
      break;

    case '?':
      usage (EXIT_FAILURE);
    case 'i':
      convert_entire_line = 0;
      break;
    case 't':
      parse_tabstops (optarg);
      break;
    case ',':
      add_tabstop (tabval);
      tabval = -1;
      obsolete_tablist = true;
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      if (tabval == -1)
        tabval = 0;
      tabval = tabval * 10 + c - '0';
      obsolete_tablist = true;
      break;
    }
    }

  if (obsolete_tablist && 200112 <= posix2_version ())
    {
      error (0, 0, _("`-LIST' option is obsolete; use `-t LIST'"));
      usage (EXIT_FAILURE);
    }

  add_tabstop (tabval);

  validate_tabstops (tab_list, first_free_tab);

  if (first_free_tab == 0)
    tab_size = 8;
  else if (first_free_tab == 1)
    tab_size = tab_list[0];
  else
    tab_size = 0;

  if (optind == argc)
    file_list = stdin_argv;
  else
    file_list = &argv[optind];

  expand ();

  if (have_read_stdin && fclose (stdin) == EOF)
    error (EXIT_FAILURE, errno, "-");

  exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/posixver.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/posixver.h used in: Step 863, Step 875, Step 877, Step 901, Step 907, Step 923, Step 925, Step 929, Step 937, Step 939, Step 951
int posix2_version (void);

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/factor.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/factor.c used in: Step 867
/* factor -- print prime factors of n.
   Copyright (C) 86, 1995-2002 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.  */

/* Written by Paul Rubin <phr@ocf.berkeley.edu>.
   Adapted for GNU, fixed to factor UINT_MAX by Jim Meyering.  */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>

#include <assert.h>
#define NDEBUG 1

#include "system.h"
#include "closeout.h"
#include "error.h"
#include "inttostr.h"
#include "long-options.h"
#include "readtokens.h"
#include "xstrtol.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "factor"

#define AUTHORS "Paul Rubin"

/* Token delimiters when reading from a file.  */
#define DELIM "\n\t "

/* FIXME: if this program is ever modified to factor integers larger
   than 2^128, this constant (and the algorithm :-) will have to change.  */
#define MAX_N_FACTORS 128

/* The trial divisor increment wheel.  Use it to skip over divisors that
   are composites of 2, 3, 5, 7, or 11.  The part from WHEEL_START up to
   WHEEL_END is reused periodically, while the "lead in" is used to test
   for those primes and to jump onto the wheel.  For more information, see
   http://www.utm.edu/research/primes/glossary/WheelFactorization.html  */

#include "wheel-size.h"  /* For the definition of WHEEL_SIZE.  */
static const unsigned int wheel_tab[] =
  {
#include "wheel.h"
  };

#define WHEEL_START (wheel_tab + WHEEL_SIZE)
#define WHEEL_END (wheel_tab + (sizeof wheel_tab / sizeof wheel_tab[0]))

/* The name this program was run with. */
char *program_name;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [NUMBER]...\n\
  or:  %s OPTION\n\
"),
          program_name, program_name);
      fputs (_("\
Print the prime factors of each NUMBER.\n\
\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
  Print the prime factors of all specified integer NUMBERs.  If no arguments\n\
  are specified on the command line, they are read from standard input.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

/* FIXME: comment */

static int
factor (uintmax_t n0, int max_n_factors, uintmax_t *factors)
{
  register uintmax_t n = n0, d, q;
  int n_factors = 0;
  unsigned int const *w = wheel_tab;

  if (n < 1)
    return n_factors;

  /* The exit condition in the following loop is correct because
     any time it is tested one of these 3 conditions holds:
      (1) d divides n
      (2) n is prime
      (3) n is composite but has no factors less than d.
     If (1) or (2) obviously the right thing happens.
     If (3), then since n is composite it is >= d^2. */

  d = 2;
  do
    {
      q = n / d;
      while (n == q * d)
    {
      assert (n_factors < max_n_factors);
      factors[n_factors++] = d;
      n = q;
      q = n / d;
    }
      d += *(w++);
      if (w == WHEEL_END)
    w = WHEEL_START;
    }
  while (d <= q);

  if (n != 1 || n0 == 1)
    {
      assert (n_factors < max_n_factors);
      factors[n_factors++] = n;
    }

  return n_factors;
}

/* FIXME: comment */

static int
print_factors (const char *s)
{
  uintmax_t factors[MAX_N_FACTORS];
  uintmax_t n;
  int n_factors;
  int i;
  char buf[INT_BUFSIZE_BOUND (uintmax_t)];

  if (xstrtoumax (s, NULL, 10, &n, "") != LONGINT_OK)
    {
      error (0, 0, _("`%s' is not a valid positive integer"), s);
      return 1;
    }
  n_factors = factor (n, MAX_N_FACTORS, factors);
  printf ("%s:", umaxtostr (n, buf));
  for (i = 0; i < n_factors; i++)
    printf (" %s", umaxtostr (factors[i], buf));
  putchar ('\n');
  return 0;
}

static int
do_stdin (void)
{
  int fail = 0;
  token_buffer tokenbuffer;

  init_tokenbuffer (&tokenbuffer);

  for (;;)
    {
      long int token_length;

      token_length = readtoken (stdin, DELIM, sizeof (DELIM) - 1,
                &tokenbuffer);
      if (token_length < 0)
    break;
      fail |= print_factors (tokenbuffer.buffer);
    }
  free (tokenbuffer.buffer);

  return fail;
}

int
main (int argc, char **argv)
{
  int fail;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              AUTHORS, usage);
  /* The above handles --help and --version.
     Since there is no other invocation of getopt, handle `--' here.  */
  if (argc > 1 && STREQ (argv[1], "--"))
    {
      --argc;
      ++argv;
    }

  fail = 0;
  if (argc == 1)
    fail = do_stdin ();
  else
    {
      int i;
      for (i = 1; i < argc; i++)
    fail |= print_factors (argv[i]);
    }
  if (fail)
    usage (EXIT_FAILURE);

  exit (fail);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/readtokens.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/readtokens.h used in: Step 867, Step 935
#ifndef H_READTOKENS_H
# define H_READTOKENS_H

# ifndef INITIAL_TOKEN_LENGTH
#  define INITIAL_TOKEN_LENGTH 20
# endif

# ifndef TOKENBUFFER_DEFINED
#  define TOKENBUFFER_DEFINED
struct tokenbuffer
{
  long size;
  char *buffer;
};
typedef struct tokenbuffer token_buffer;

# endif /* not TOKENBUFFER_DEFINED */

# undef __P
# if defined (__STDC__) && __STDC__
#  define   __P(x) x
# else
#  define   __P(x) ()
# endif

void init_tokenbuffer __P ((token_buffer *tokenbuffer));

long
  readtoken __P ((FILE *stream, const char *delim, int n_delim,
         token_buffer *tokenbuffer));
int
  readtokens __P ((FILE *stream, int projected_n_tokens,
          const char *delim, int n_delim,
          char ***tokens_out, long **token_lengths));

#endif /* not H_READTOKENS_H */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/wheel-size.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/wheel-size.h used in: Step 867
#define WHEEL_SIZE 5

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/wheel.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/wheel.h used in: Step 867
/* The first 4 elements correspond to the incremental offsets of the
   first 5 primes (2 3 5 7 11).  The 5(th) element is the
   difference between that last prime and the next largest integer
   that is not a multiple of those primes.  The remaining numbers
   define the wheel.  For more information, see
   http://www.utm.edu/research/primes/glossary/WheelFactorization.html.  */
1,
2,
2,
4,
2,
4,
2,
4,
6,
2,
6,
4,
2,
4,
6,
6,
2,
6,
4,
2,
6,
4,
6,
8,
4,
2,
4,
2,
4,
14,
4,
6,
2,
10,
2,
6,
6,
4,
2,
4,
6,
2,
10,
2,
4,
2,
12,
10,
2,
4,
2,
4,
6,
2,
6,
4,
6,
6,
6,
2,
6,
4,
2,
6,
4,
6,
8,
4,
2,
4,
6,
8,
6,
10,
2,
4,
6,
2,
6,
6,
4,
2,
4,
6,
2,
6,
4,
2,
6,
10,
2,
10,
2,
4,
2,
4,
6,
8,
4,
2,
4,
12,
2,
6,
4,
2,
6,
4,
6,
12,
2,
4,
2,
4,
8,
6,
4,
6,
2,
4,
6,
2,
6,
10,
2,
4,
6,
2,
6,
4,
2,
4,
2,
10,
2,
10,
2,
4,
6,
6,
2,
6,
6,
4,
6,
6,
2,
6,
4,
2,
6,
4,
6,
8,
4,
2,
6,
4,
8,
6,
4,
6,
2,
4,
6,
8,
6,
4,
2,
10,
2,
6,
4,
2,
4,
2,
10,
2,
10,
2,
4,
2,
4,
8,
6,
4,
2,
4,
6,
6,
2,
6,
4,
8,
4,
6,
8,
4,
2,
4,
2,
4,
8,
6,
4,
6,
6,
6,
2,
6,
6,
4,
2,
4,
6,
2,
6,
4,
2,
4,
2,
10,
2,
10,
2,
6,
4,
6,
2,
6,
4,
2,
4,
6,
6,
8,
4,
2,
6,
10,
8,
4,
2,
4,
2,
4,
8,
10,
6,
2,
4,
8,
6,
6,
4,
2,
4,
6,
2,
6,
4,
6,
2,
10,
2,
10,
2,
4,
2,
4,
6,
2,
6,
4,
2,
4,
6,
6,
2,
6,
6,
6,
4,
6,
8,
4,
2,
4,
2,
4,
8,
6,
4,
8,
4,
6,
2,
6,
6,
4,
2,
4,
6,
8,
4,
2,
4,
2,
10,
2,
10,
2,
4,
2,
4,
6,
2,
10,
2,
4,
6,
8,
6,
4,
2,
6,
4,
6,
8,
4,
6,
2,
4,
8,
6,
4,
6,
2,
4,
6,
2,
6,
6,
4,
6,
6,
2,
6,
6,
4,
2,
10,
2,
10,
2,
4,
2,
4,
6,
2,
6,
4,
2,
10,
6,
2,
6,
4,
2,
6,
4,
6,
8,
4,
2,
4,
2,
12,
6,
4,
6,
2,
4,
6,
2,
12,
4,
2,
4,
8,
6,
4,
2,
4,
2,
10,
2,
10,
6,
2,
4,
6,
2,
6,
4,
2,
4,
6,
6,
2,
6,
4,
2,
10,
6,
8,
6,
4,
2,
4,
8,
6,
4,
6,
2,
4,
6,
2,
6,
6,
6,
4,
6,
2,
6,
4,
2,
4,
2,
10,
12,
2,
4,
2,
10,
2,
6,
4,
2,
4,
6,
6,
2,
10,
2,
6,
4,
14,
4,
2,
4,
2,
4,
8,
6,
4,
6,
2,
4,
6,
2,
6,
6,
4,
2,
4,
6,
2,
6,
4,
2,
4,
12,
2,
12

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/true.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/true.c used in: Step 869, Step 953
/* Exit with a status code indicating success.
   Copyright (C) 1999-2003 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 <config.h>
#include <stdio.h>
#include <sys/types.h>
#include "system.h"
#include "version-etc.h"
#include "closeout.h"

#define PROGRAM_NAME "true"
#define AUTHORS "Jim Meyering"

/* The name this program was run with. */
char *program_name;

void
usage (int status)
{
  printf (_("\
Usage: %s [ignored command line arguments]\n\
  or:  %s OPTION\n\
Exit with a status code indicating success.\n\
\n\
These option names may not be abbreviated.\n\
\n\
"),
      program_name, program_name);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
  printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
  exit (status);
}

int
main (int argc, char **argv)
{
  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  /* Recognize --help or --version only if it's the only command-line
     argument and if POSIXLY_CORRECT is not set.  */
  if (argc == 2 && getenv ("POSIXLY_CORRECT") == NULL)
    {
      if (STREQ (argv[1], "--help"))
    usage (EXIT_SUCCESS);

      if (STREQ (argv[1], "--version"))
    version_etc (stdout, PROGRAM_NAME, GNU_PACKAGE, VERSION, AUTHORS);
    }

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/version-etc.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/version-etc.h used in: Step 871, Step 953
/* Utility to help print --version output in a consistent format.
   Copyright (C) 1999 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   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.  */

/* Written by Jim Meyering. */

#ifndef VERSION_ETC_H
# define VERSION_ETC_H 1

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

extern char *version_etc_copyright;

void
version_etc PARAMS ((FILE *stream,
             const char *command_name, const char *package,
             const char *version, const char *authors));

#endif /* VERSION_ETC_H */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/fmt.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/fmt.c used in: Step 873
/* GNU fmt -- simple text formatter.
   Copyright (C) 1994-2003 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.  */

/* Written by Ross Paterson <rap@doc.ic.ac.uk>.  */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>

/* Redefine.  Otherwise, systems (Unicos for one) with headers that define
   it to be a type get syntax errors for the variable declaration below.  */
#define word unused_word_type

#include "system.h"
#include "closeout.h"
#include "error.h"
#include "xstrtol.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "fmt"

#define AUTHORS "Ross Paterson"

/* The following parameters represent the program's idea of what is
   "best".  Adjust to taste, subject to the caveats given.  */

/* Default longest permitted line length (max_width).  */
#define WIDTH   75

/* Prefer lines to be LEEWAY % shorter than the maximum width, giving
   room for optimization.  */
#define LEEWAY  7

/* The default secondary indent of tagged paragraph used for unindented
   one-line paragraphs not preceded by any multi-line paragraphs.  */
#define DEF_INDENT 3

/* Costs and bonuses are expressed as the equivalent departure from the
   optimal line length, multiplied by 10.  e.g. assigning something a
   cost of 50 means that it is as bad as a line 5 characters too short
   or too long.  The definition of SHORT_COST(n) should not be changed.
   However, EQUIV(n) may need tuning.  */

typedef long COST;

#define MAXCOST TYPE_MAXIMUM (COST)

#define SQR(n)      ((n) * (n))
#define EQUIV(n)    SQR ((COST) (n))

/* Cost of a filled line n chars longer or shorter than best_width.  */
#define SHORT_COST(n)   EQUIV ((n) * 10)

/* Cost of the difference between adjacent filled lines.  */
#define RAGGED_COST(n)  (SHORT_COST (n) / 2)

/* Basic cost per line.  */
#define LINE_COST   EQUIV (70)

/* Cost of breaking a line after the first word of a sentence, where
   the length of the word is N.  */
#define WIDOW_COST(n)   (EQUIV (200) / ((n) + 2))

/* Cost of breaking a line before the last word of a sentence, where
   the length of the word is N.  */
#define ORPHAN_COST(n)  (EQUIV (150) / ((n) + 2))

/* Bonus for breaking a line at the end of a sentence.  */
#define SENTENCE_BONUS  EQUIV (50)

/* Cost of breaking a line after a period not marking end of a sentence.
   With the definition of sentence we are using (borrowed from emacs, see
   get_line()) such a break would then look like a sentence break.  Hence
   we assign a very high cost -- it should be avoided unless things are
   really bad.  */
#define NOBREAK_COST    EQUIV (600)

/* Bonus for breaking a line before open parenthesis.  */
#define PAREN_BONUS EQUIV (40)

/* Bonus for breaking a line after other punctuation.  */
#define PUNCT_BONUS EQUIV(40)

/* Credit for breaking a long paragraph one line later.  */
#define LINE_CREDIT EQUIV(3)

/* Size of paragraph buffer, in words and characters.  Longer paragraphs
   are handled neatly (cf. flush_paragraph()), so there's little to gain
   by making these larger.  */
#define MAXWORDS    1000
#define MAXCHARS    5000

/* Extra ctype(3)-style macros.  */

#define isopen(c)   (strchr ("([`'\"", c) != NULL)
#define isclose(c)  (strchr (")]'\"", c) != NULL)
#define isperiod(c) (strchr (".?!", c) != NULL)

/* Size of a tab stop, for expansion on input and re-introduction on
   output.  */
#define TABWIDTH    8

/* Miscellaneous definitions.  */

#undef TRUE
#define TRUE true
#undef FALSE
#define FALSE false

/* Word descriptor structure.  */

typedef struct Word WORD;

struct Word
  {

    /* Static attributes determined during input.  */

    const char *text;       /* the text of the word */
    int length;         /* length of this word */
    int space;          /* the size of the following space */
    unsigned int paren:1;   /* starts with open paren */
    unsigned int period:1;  /* ends in [.?!])* */
    unsigned int punct:1;   /* ends in punctuation */
    unsigned int final:1;   /* end of sentence */

    /* The remaining fields are computed during the optimization.  */

    int line_length;        /* length of the best line starting here */
    COST best_cost;     /* cost of best paragraph starting here */
    WORD *next_break;       /* break which achieves best_cost */
  };

/* Forward declarations.  */

static void set_prefix (char *p);
static void fmt (FILE *f);
static bool get_paragraph (FILE *f);
static int get_line (FILE *f, int c);
static int get_prefix (FILE *f);
static int get_space (FILE *f, int c);
static int copy_rest (FILE *f, int c);
static bool same_para (int c);
static void flush_paragraph (void);
static void fmt_paragraph (void);
static void check_punctuation (WORD *w);
static COST base_cost (WORD *this);
static COST line_cost (WORD *next, int len);
static void put_paragraph (WORD *finish);
static void put_line (WORD *w, int indent);
static void put_word (WORD *w);
static void put_space (int space);

/* The name this program was run with.  */
const char *program_name;

/* Option values.  */

/* If TRUE, first 2 lines may have different indent (default FALSE).  */
static bool crown;

/* If TRUE, first 2 lines _must_ have different indent (default FALSE).  */
static bool tagged;

/* If TRUE, each line is a paragraph on its own (default FALSE).  */
static bool split;

/* If TRUE, don't preserve inter-word spacing (default FALSE).  */
static bool uniform;

/* Prefix minus leading and trailing spaces (default "").  */
static const char *prefix;

/* User-supplied maximum line width (default WIDTH).  The only output
   lines longer than this will each comprise a single word.  */
static int max_width;

/* Values derived from the option values.  */

/* The length of prefix minus leading space.  */
static int prefix_full_length;

/* The length of the leading space trimmed from the prefix.  */
static int prefix_lead_space;

/* The length of prefix minus leading and trailing space.  */
static int prefix_length;

/* The preferred width of text lines, set to LEEWAY % less than max_width.  */
static int best_width;

/* Dynamic variables.  */

/* Start column of the character most recently read from the input file.  */
static int in_column;

/* Start column of the next character to be written to stdout.  */
static int out_column;

/* Space for the paragraph text -- longer paragraphs are handled neatly
   (cf. flush_paragraph()).  */
static char parabuf[MAXCHARS];

/* A pointer into parabuf, indicating the first unused character position.  */
static char *wptr;

/* The words of a paragraph -- longer paragraphs are handled neatly
   (cf. flush_paragraph()).  */
static WORD word[MAXWORDS];

/* A pointer into the above word array, indicating the first position
   after the last complete word.  Sometimes it will point at an incomplete
   word.  */
static WORD *word_limit;

/* If TRUE, current input file contains tab characters, and so tabs can be
   used for white space on output.  */
static bool tabs;

/* Space before trimmed prefix on each line of the current paragraph.  */
static int prefix_indent;

/* Indentation of the first line of the current paragraph.  */
static int first_indent;

/* Indentation of other lines of the current paragraph */
static int other_indent;

/* To detect the end of a paragraph, we need to look ahead to the first
   non-blank character after the prefix on the next line, or the first
   character on the following line that failed to match the prefix.
   We can reconstruct the lookahead from that character (next_char), its
   position on the line (in_column) and the amount of space before the
   prefix (next_prefix_indent).  See get_paragraph() and copy_rest().  */

/* The last character read from the input file.  */
static int next_char;

/* The space before the trimmed prefix (or part of it) on the next line
   after the current paragraph.  */
static int next_prefix_indent;

/* If nonzero, the length of the last line output in the current
   paragraph, used to charge for raggedness at the split point for long
   paragraphs chosen by fmt_paragraph().  */
static int last_line_length;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [-DIGITS] [OPTION]... [FILE]...\n"), program_name);
      fputs (_("\
Reformat each paragraph in the FILE(s), writing to standard output.\n\
If no FILE or if FILE is `-', read standard input.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -c, --crown-margin        preserve indentation of first two lines\n\
  -p, --prefix=STRING       combine only lines having STRING as prefix\n\
  -s, --split-only          split long lines, but do not refill\n\
"),
         stdout);
      fputs (_("\
  -t, --tagged-paragraph    indentation of first line different from second\n\
  -u, --uniform-spacing     one space between words, two after sentences\n\
  -w, --width=NUMBER        maximum line width (default of 75 columns)\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
In -wNUMBER, the letter `w' may be omitted.\n"),
         stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Decode options and launch execution.  */

static const struct option long_options[] =
{
  {"crown-margin", no_argument, NULL, 'c'},
  {"prefix", required_argument, NULL, 'p'},
  {"split-only", no_argument, NULL, 's'},
  {"tagged-paragraph", no_argument, NULL, 't'},
  {"uniform-spacing", no_argument, NULL, 'u'},
  {"width", required_argument, NULL, 'w'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {0, 0, 0, 0},
};

int
main (register int argc, register char **argv)
{
  int optchar;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  crown = tagged = split = uniform = FALSE;
  max_width = WIDTH;
  prefix = "";
  prefix_length = prefix_lead_space = prefix_full_length = 0;

  if (argc > 1 && argv[1][0] == '-' && ISDIGIT (argv[1][1]))
    {
      const char *s = argv[1] + 1;
      max_width = 0;
      /* Old option syntax; a dash followed by one or more digits.
         Move past the number. */
      for (; ISDIGIT (*s); ++s)
    {
      int old_max = max_width;
      max_width = max_width * 10 + *s - '0';
      if (INT_MAX / 10 < old_max || max_width < old_max)
        error (EXIT_FAILURE, 0, _("invalid width option: `%s'"), argv[1]);
    }
      /* Make the options we just parsed invisible to getopt. */
      argv[1] = argv[0];
      argv++;
      argc--;
    }

  while ((optchar = getopt_long (argc, argv, "0123456789cstuw:p:",
                 long_options, NULL))
     != -1)
    switch (optchar)
      {
      default:
    usage (EXIT_FAILURE);

      case 0:
    break;

      case 'c':
    crown = TRUE;
    break;

      case 's':
    split = TRUE;
    break;

      case 't':
    tagged = TRUE;
    break;

      case 'u':
    uniform = TRUE;
    break;

      case 'w':
    {
      long int tmp_long;
      if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
          || tmp_long <= 0 || tmp_long > INT_MAX)
        error (EXIT_FAILURE, 0, _("invalid width: `%s'"),
           optarg);
      max_width = (int) tmp_long;
    }
    break;

      case 'p':
    set_prefix (optarg);
    break;

      case_GETOPT_HELP_CHAR;

      case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

      }

  best_width = max_width * (2 * (100 - LEEWAY) + 1) / 200;

  if (optind == argc)
    fmt (stdin);
  else
    {
      for (; optind < argc; optind++)
    {
      char *file = argv[optind];
      if (STREQ (file, "-"))
        fmt (stdin);
      else
        {
          FILE *in_stream;
          in_stream = fopen (file, "r");
          if (in_stream != NULL)
        {
          fmt (in_stream);
          if (fclose (in_stream) == EOF)
            error (EXIT_FAILURE, errno, "%s", file);
        }
          else
        error (0, errno, "%s", file);
        }
    }
    }

  exit (EXIT_SUCCESS);
}

/* Trim space from the front and back of the string P, yielding the prefix,
   and record the lengths of the prefix and the space trimmed.  */

static void
set_prefix (register char *p)
{
  register char *s;

  prefix_lead_space = 0;
  while (*p == ' ')
    {
      prefix_lead_space++;
      p++;
    }
  prefix = p;
  prefix_full_length = strlen (p);
  s = p + prefix_full_length;
  while (s > p && s[-1] == ' ')
    s--;
  *s = '\0';
  prefix_length = s - p;
}

/* read file F and send formatted output to stdout.  */

static void
fmt (FILE *f)
{
  tabs = FALSE;
  other_indent = 0;
  next_char = get_prefix (f);
  while (get_paragraph (f))
    {
      fmt_paragraph ();
      put_paragraph (word_limit);
    }
}

/* Set the global variable `other_indent' according to SAME_PARAGRAPH
   and other global variables.  */

static void
set_other_indent (bool same_paragraph)
{
  if (split)
    other_indent = first_indent;
  else if (crown)
    {
      other_indent = (same_paragraph ? in_column : first_indent);
    }
  else if (tagged)
    {
      if (same_paragraph && in_column != first_indent)
    {
      other_indent = in_column;
    }

      /* Only one line: use the secondary indent from last time if it
         splits, or 0 if there have been no multi-line paragraphs in the
         input so far.  But if these rules make the two indents the same,
         pick a new secondary indent.  */

      else if (other_indent == first_indent)
    other_indent = first_indent == 0 ? DEF_INDENT : 0;
    }
  else
    {
      other_indent = first_indent;
    }
}

/* Read a paragraph from input file F.  A paragraph consists of a
   maximal number of non-blank (excluding any prefix) lines subject to:
   * In split mode, a paragraph is a single non-blank line.
   * In crown mode, the second and subsequent lines must have the
   same indentation, but possibly different from the indent of the
   first line.
   * Tagged mode is similar, but the first and second lines must have
   different indentations.
   * Otherwise, all lines of a paragraph must have the same indent.
   If a prefix is in effect, it must be present at the same indent for
   each line in the paragraph.

   Return FALSE if end-of-file was encountered before the start of a
   paragraph, else TRUE.  */

static bool
get_paragraph (FILE *f)
{
  register int c;

  last_line_length = 0;
  c = next_char;

  /* Scan (and copy) blank lines, and lines not introduced by the prefix.  */

  while (c == '\n' || c == EOF
     || next_prefix_indent < prefix_lead_space
     || in_column < next_prefix_indent + prefix_full_length)
    {
      c = copy_rest (f, c);
      if (c == EOF)
    {
      next_char = EOF;
      return FALSE;
    }
      putchar ('\n');
      c = get_prefix (f);
    }

  /* Got a suitable first line for a paragraph.  */

  prefix_indent = next_prefix_indent;
  first_indent = in_column;
  wptr = parabuf;
  word_limit = word;
  c = get_line (f, c);
  set_other_indent (same_para (c));

  /* Read rest of paragraph (unless split is specified).  */

  if (split)
    {
      /* empty */
    }
  else if (crown)
    {
      if (same_para (c))
    {
      do
        {           /* for each line till the end of the para */
          c = get_line (f, c);
        }
      while (same_para (c) && in_column == other_indent);
    }
    }
  else if (tagged)
    {
      if (same_para (c) && in_column != first_indent)
    {
      do
        {           /* for each line till the end of the para */
          c = get_line (f, c);
        }
      while (same_para (c) && in_column == other_indent);
    }
    }
  else
    {
      while (same_para (c) && in_column == other_indent)
    c = get_line (f, c);
    }
  (word_limit - 1)->period = (word_limit - 1)->final = TRUE;
  next_char = c;
  return TRUE;
}

/* Copy to the output a line that failed to match the prefix, or that
   was blank after the prefix.  In the former case, C is the character
   that failed to match the prefix.  In the latter, C is \n or EOF.
   Return the character (\n or EOF) ending the line.  */

static int
copy_rest (FILE *f, register int c)
{
  register const char *s;

  out_column = 0;
  if (in_column > next_prefix_indent && c != '\n' && c != EOF)
    {
      put_space (next_prefix_indent);
      for (s = prefix; out_column != in_column && *s; out_column++)
    putchar (*(unsigned char *)s++);
      put_space (in_column - out_column);
    }
  while (c != '\n' && c != EOF)
    {
      putchar (c);
      c = getc (f);
    }
  return c;
}

/* Return TRUE if a line whose first non-blank character after the
   prefix (if any) is C could belong to the current paragraph,
   otherwise FALSE.  */

static bool
same_para (register int c)
{
  return (next_prefix_indent == prefix_indent
      && in_column >= next_prefix_indent + prefix_full_length
      && c != '\n' && c != EOF);
}

/* Read a line from input file F, given first non-blank character C
   after the prefix, and the following indent, and break it into words.
   A word is a maximal non-empty string of non-white characters.  A word
   ending in [.?!]["')\]]* and followed by end-of-line or at least two
   spaces ends a sentence, as in emacs.

   Return the first non-blank character of the next line.  */

static int
get_line (FILE *f, register int c)
{
  int start;
  register char *end_of_parabuf;
  register WORD *end_of_word;

  end_of_parabuf = &parabuf[MAXCHARS];
  end_of_word = &word[MAXWORDS - 2];

  do
    {               /* for each word in a line */

      /* Scan word.  */

      word_limit->text = wptr;
      do
    {
      if (wptr == end_of_parabuf)
        {
          set_other_indent (true);
          flush_paragraph ();
        }
      *wptr++ = c;
      c = getc (f);
    }
      while (c != EOF && !ISSPACE (c));
      in_column += word_limit->length = wptr - word_limit->text;
      check_punctuation (word_limit);

      /* Scan inter-word space.  */

      start = in_column;
      c = get_space (f, c);
      word_limit->space = in_column - start;
      word_limit->final = (c == EOF
               || (word_limit->period
                   && (c == '\n' || word_limit->space > 1)));
      if (c == '\n' || c == EOF || uniform)
    word_limit->space = word_limit->final ? 2 : 1;
      if (word_limit == end_of_word)
    {
      set_other_indent (true);
      flush_paragraph ();
    }
      word_limit++;
      if (c == EOF)
    return EOF;
    }
  while (c != '\n');
  return get_prefix (f);
}

/* Read a prefix from input file F.  Return either first non-matching
   character, or first non-blank character after the prefix.  */

static int
get_prefix (FILE *f)
{
  register int c;

  in_column = 0;
  c = get_space (f, getc (f));
  if (prefix_length == 0)
    next_prefix_indent = prefix_lead_space < in_column ?
      prefix_lead_space : in_column;
  else
    {
      const char *p;
      next_prefix_indent = in_column;
      for (p = prefix; *p != '\0'; p++)
    {
      if (c != *(unsigned char *)p)
        return c;
      in_column++;
      c = getc (f);
    }
      c = get_space (f, c);
    }
  return c;
}

/* Read blank characters from input file F, starting with C, and keeping
   in_column up-to-date.  Return first non-blank character.  */

static int
get_space (FILE *f, register int c)
{
  for (;;)
    {
      if (c == ' ')
    in_column++;
      else if (c == '\t')
    {
      tabs = TRUE;
      in_column = (in_column / TABWIDTH + 1) * TABWIDTH;
    }
      else
    return c;
      c = getc (f);
    }
}

/* Set extra fields in word W describing any attached punctuation.  */

static void
check_punctuation (register WORD *w)
{
  const unsigned char *start, *finish;

  start = (unsigned char *) w->text;
  finish = start + (w->length - 1);
  w->paren = isopen (*start);
  w->punct = ISPUNCT (*finish);
  while (isclose (*finish) && finish > start)
    finish--;
  w->period = isperiod (*finish);
}

/* Flush part of the paragraph to make room.  This function is called on
   hitting the limit on the number of words or characters.  */

static void
flush_paragraph (void)
{
  WORD *split_point;
  register WORD *w;
  int shift;
  COST best_break;

  /* In the special case where it's all one word, just flush it.  */

  if (word_limit == word)
    {
      printf ("%*s", wptr - parabuf, parabuf);
      wptr = parabuf;
      return;
    }

  /* Otherwise:
     - format what you have so far as a paragraph,
     - find a low-cost line break near the end,
     - output to there,
     - make that the start of the paragraph.  */

  fmt_paragraph ();

  /* Choose a good split point.  */

  split_point = word_limit;
  best_break = MAXCOST;
  for (w = word->next_break; w != word_limit; w = w->next_break)
    {
      if (w->best_cost - w->next_break->best_cost < best_break)
    {
      split_point = w;
      best_break = w->best_cost - w->next_break->best_cost;
    }
      if (best_break <= MAXCOST - LINE_CREDIT)
    best_break += LINE_CREDIT;
    }
  put_paragraph (split_point);

  /* Copy text of words down to start of parabuf -- we use memmove because
     the source and target may overlap.  */

  memmove (parabuf, split_point->text, (size_t) (wptr - split_point->text));
  shift = split_point->text - parabuf;
  wptr -= shift;

  /* Adjust text pointers.  */

  for (w = split_point; w <= word_limit; w++)
    w->text -= shift;

  /* Copy words from split_point down to word -- we use memmove because
     the source and target may overlap.  */

  memmove ((char *) word, (char *) split_point,
     (word_limit - split_point + 1) * sizeof (WORD));
  word_limit -= split_point - word;
}

/* Compute the optimal formatting for the whole paragraph by computing
   and remembering the optimal formatting for each suffix from the empty
   one to the whole paragraph.  */

static void
fmt_paragraph (void)
{
  register WORD *start, *w;
  register int len;
  register COST wcost, best;
  int saved_length;

  word_limit->best_cost = 0;
  saved_length = word_limit->length;
  word_limit->length = max_width;   /* sentinel */

  for (start = word_limit - 1; start >= word; start--)
    {
      best = MAXCOST;
      len = start == word ? first_indent : other_indent;

      /* At least one word, however long, in the line.  */

      w = start;
      len += w->length;
      do
    {
      w++;

      /* Consider breaking before w.  */

      wcost = line_cost (w, len) + w->best_cost;
      if (start == word && last_line_length > 0)
        wcost += RAGGED_COST (len - last_line_length);
      if (wcost < best)
        {
          best = wcost;
          start->next_break = w;
          start->line_length = len;
        }

      /* This is a kludge to keep us from computing `len' as the
         sum of the sentinel length and some non-zero number.
         Since the sentinel w->length may be INT_MAX, adding
         to that would give a negative result.  */
      if (w == word_limit)
        break;

      len += (w - 1)->space + w->length;    /* w > start >= word */
    }
      while (len < max_width);
      start->best_cost = best + base_cost (start);
    }

  word_limit->length = saved_length;
}

/* Return the constant component of the cost of breaking before the
   word THIS.  */

static COST
base_cost (register WORD *this)
{
  register COST cost;

  cost = LINE_COST;

  if (this > word)
    {
      if ((this - 1)->period)
    {
      if ((this - 1)->final)
        cost -= SENTENCE_BONUS;
      else
        cost += NOBREAK_COST;
    }
      else if ((this - 1)->punct)
    cost -= PUNCT_BONUS;
      else if (this > word + 1 && (this - 2)->final)
    cost += WIDOW_COST ((this - 1)->length);
    }

  if (this->paren)
    cost -= PAREN_BONUS;
  else if (this->final)
    cost += ORPHAN_COST (this->length);

  return cost;
}

/* Return the component of the cost of breaking before word NEXT that
   depends on LEN, the length of the line beginning there.  */

static COST
line_cost (register WORD *next, register int len)
{
  register int n;
  register COST cost;

  if (next == word_limit)
    return 0;
  n = best_width - len;
  cost = SHORT_COST (n);
  if (next->next_break != word_limit)
    {
      n = len - next->line_length;
      cost += RAGGED_COST (n);
    }
  return cost;
}

/* Output to stdout a paragraph from word up to (but not including)
   FINISH, which must be in the next_break chain from word.  */

static void
put_paragraph (register WORD *finish)
{
  register WORD *w;

  put_line (word, first_indent);
  for (w = word->next_break; w != finish; w = w->next_break)
    put_line (w, other_indent);
}

/* Output to stdout the line beginning with word W, beginning in column
   INDENT, including the prefix (if any).  */

static void
put_line (register WORD *w, int indent)
{
  register WORD *endline;

  out_column = 0;
  put_space (prefix_indent);
  fputs (prefix, stdout);
  out_column += prefix_length;
  put_space (indent - out_column);

  endline = w->next_break - 1;
  for (; w != endline; w++)
    {
      put_word (w);
      put_space (w->space);
    }
  put_word (w);
  last_line_length = out_column;
  putchar ('\n');
}

/* Output to stdout the word W.  */

static void
put_word (register WORD *w)
{
  register const char *s;
  register int n;

  s = w->text;
  for (n = w->length; n != 0; n--)
    putchar (*s++);
  out_column += w->length;
}

/* Output to stdout SPACE spaces, or equivalent tabs.  */

static void
put_space (int space)
{
  register int space_target, tab_target;

  space_target = out_column + space;
  if (tabs)
    {
      tab_target = space_target / TABWIDTH * TABWIDTH;
      if (out_column + 1 < tab_target)
    while (out_column < tab_target)
      {
        putchar ('\t');
        out_column = (out_column / TABWIDTH + 1) * TABWIDTH;
      }
    }
  while (out_column < space_target)
    {
      putchar (' ');
      out_column++;
    }
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/fold.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/fold.c used in: Step 875
/* fold -- wrap each input line to fit in specified width.
   Copyright (C) 91, 1995-2002 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.  */

/* Written by David MacKenzie, djm@gnu.ai.mit.edu. */

#include <config.h>

#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>

#include "system.h"
#include "closeout.h"
#include "error.h"
#include "posixver.h"
#include "xstrtol.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "fold"

#define AUTHORS "David MacKenzie"

/* The name this program was run with. */
char *program_name;

/* If nonzero, try to break on whitespace. */
static int break_spaces;

/* If nonzero, count bytes, not column positions. */
static int count_bytes;

/* If nonzero, at least one of the files we read was standard input. */
static int have_read_stdin;

static struct option const longopts[] =
{
  {"bytes", no_argument, NULL, 'b'},
  {"spaces", no_argument, NULL, 's'},
  {"width", required_argument, NULL, 'w'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
          program_name);
      fputs (_("\
Wrap input lines in each FILE (standard input by default), writing to\n\
standard output.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -b, --bytes         count bytes rather than columns\n\
  -s, --spaces        break at spaces\n\
  -w, --width=WIDTH   use WIDTH columns instead of 80\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Assuming the current column is COLUMN, return the column that
   printing C will move the cursor to.
   The first column is 0. */

static int
adjust_column (int column, char c)
{
  if (!count_bytes)
    {
      if (c == '\b')
    {
      if (column > 0)
        column--;
    }
      else if (c == '\r')
    column = 0;
      else if (c == '\t')
    column = column + 8 - column % 8;
      else /* if (isprint (c)) */
    column++;
    }
  else
    column++;
  return column;
}

/* Fold file FILENAME, or standard input if FILENAME is "-",
   to stdout, with maximum line length WIDTH.
   Return 0 if successful, 1 if an error occurs. */

static int
fold_file (char *filename, int width)
{
  FILE *istream;
  register int c;
  int column = 0;       /* Screen column where next char will go. */
  int offset_out = 0;   /* Index in `line_out' for next char. */
  static char *line_out = NULL;
  static int allocated_out = 0;

  if (STREQ (filename, "-"))
    {
      istream = stdin;
      have_read_stdin = 1;
    }
  else
    istream = fopen (filename, "r");

  if (istream == NULL)
    {
      error (0, errno, "%s", filename);
      return 1;
    }

  while ((c = getc (istream)) != EOF)
    {
      if (offset_out + 1 >= allocated_out)
    {
      allocated_out += 1024;
      line_out = xrealloc (line_out, allocated_out);
    }

      if (c == '\n')
    {
      line_out[offset_out++] = c;
      fwrite (line_out, sizeof (char), (size_t) offset_out, stdout);
      column = offset_out = 0;
      continue;
    }

    rescan:
      column = adjust_column (column, c);

      if (column > width)
    {
      /* This character would make the line too long.
         Print the line plus a newline, and make this character
         start the next line. */
      if (break_spaces)
        {
          /* Look for the last blank. */
          int logical_end;

          for (logical_end = offset_out - 1; logical_end >= 0;
           logical_end--)
        if (ISBLANK (line_out[logical_end]))
          break;
          if (logical_end >= 0)
        {
          int i;

          /* Found a blank.  Don't output the part after it. */
          logical_end++;
          fwrite (line_out, sizeof (char), (size_t) logical_end,
              stdout);
          putchar ('\n');
          /* Move the remainder to the beginning of the next line.
             The areas being copied here might overlap. */
          memmove (line_out, line_out + logical_end,
               offset_out - logical_end);
          offset_out -= logical_end;
          for (column = i = 0; i < offset_out; i++)
            column = adjust_column (column, line_out[i]);
          goto rescan;
        }
        }
      else
        {
          if (offset_out == 0)
        {
          line_out[offset_out++] = c;
          continue;
        }
        }
      line_out[offset_out++] = '\n';
      fwrite (line_out, sizeof (char), (size_t) offset_out, stdout);
      column = offset_out = 0;
      goto rescan;
    }

      line_out[offset_out++] = c;
    }

  if (offset_out)
    fwrite (line_out, sizeof (char), (size_t) offset_out, stdout);

  if (ferror (istream))
    {
      error (0, errno, "%s", filename);
      if (!STREQ (filename, "-"))
    fclose (istream);
      return 1;
    }
  if (!STREQ (filename, "-") && fclose (istream) == EOF)
    {
      error (0, errno, "%s", filename);
      return 1;
    }

  return 0;
}

int
main (int argc, char **argv)
{
  int width = 80;
  int i;
  int optc;
  int errs = 0;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  break_spaces = count_bytes = have_read_stdin = 0;

  /* Turn any numeric options into -w options.  */
  for (i = 1; i < argc; i++)
    {
      char const *a = argv[i];
      if (a[0] == '-')
    {
      if (a[1] == '-' && ! a[2])
        break;
      if (ISDIGIT (a[1]))
        {
          char *s = xmalloc (strlen (a) + 2);
          s[0] = '-';
          s[1] = 'w';
          strcpy (s + 2, a + 1);
          argv[i] = s;
          if (200112 <= posix2_version ())
        {
          error (0, 0, _("`%s' option is obsolete; use `%s'"), a, s);
          usage (EXIT_FAILURE);
        }
        }
    }
    }

  while ((optc = getopt_long (argc, argv, "bsw:", longopts, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:
      break;

    case 'b':       /* Count bytes rather than columns. */
      count_bytes = 1;
      break;

    case 's':       /* Break at word boundaries. */
      break_spaces = 1;
      break;

    case 'w':       /* Line width. */
      {
        long int tmp_long;
        if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
        || tmp_long <= 0 || tmp_long > INT_MAX)
          error (EXIT_FAILURE, 0,
             _("invalid number of columns: `%s'"), optarg);
        width = (int) tmp_long;
      }
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (argc == optind)
    errs |= fold_file ("-", width);
  else
    for (i = optind; i < argc; i++)
      errs |= fold_file (argv[i], width);

  if (have_read_stdin && fclose (stdin) == EOF)
    error (EXIT_FAILURE, errno, "-");

  exit (errs == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/head.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/head.c used in: Step 877
/* head -- output first part of file(s)
   Copyright (C) 89, 90, 91, 1995-2002 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.  */

/* Options: (see usage)
   Reads from standard input if no files are given or when a filename of
   ``-'' is encountered.
   By default, filename headers are printed only if more than one file
   is given.
   By default, prints the first 10 lines (head -n 10).

   David MacKenzie <djm@gnu.ai.mit.edu> */

#include <config.h>

#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
#include "closeout.h"
#include "error.h"
#include "posixver.h"
#include "xstrtol.h"
#include "safe-read.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "head"

#define AUTHORS "David MacKenzie"

/* Number of lines/chars/blocks to head. */
#define DEFAULT_NUMBER 10

/* Size of atomic reads. */
#define BUFSIZE (512 * 8)

/* If nonzero, print filename headers. */
static int print_headers;

/* When to print the filename banners. */
enum header_mode
{
  multiple_files, always, never
};

/* Options corresponding to header_mode values.  */
static char const header_mode_option[][4] = { "", " -v", " -q" };

/* The name this program was run with. */
char *program_name;

/* Have we ever read standard input?  */
static int have_read_stdin;

static struct option const long_options[] =
{
  {"bytes", required_argument, NULL, 'c'},
  {"lines", required_argument, NULL, 'n'},
  {"quiet", no_argument, NULL, 'q'},
  {"silent", no_argument, NULL, 'q'},
  {"verbose", no_argument, NULL, 'v'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
          program_name);
      fputs (_("\
Print first 10 lines of each FILE to standard output.\n\
With more than one FILE, precede each with a header giving the file name.\n\
With no FILE, or when FILE is -, read standard input.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -c, --bytes=SIZE         print first SIZE bytes\n\
  -n, --lines=NUMBER       print first NUMBER lines instead of first 10\n\
"), stdout);
      fputs (_("\
  -q, --quiet, --silent    never print headers giving file names\n\
  -v, --verbose            always print headers giving file names\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
SIZE may have a multiplier suffix: b for 512, k for 1K, m for 1 Meg.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

static void
write_header (const char *filename)
{
  static int first_file = 1;

  printf ("%s==> %s <==\n", (first_file ? "" : "\n"), filename);
  first_file = 0;
}

static int
head_bytes (const char *filename, int fd, uintmax_t bytes_to_write)
{
  char buffer[BUFSIZE];
  size_t bytes_to_read = BUFSIZE;

  /* Need BINARY I/O for the byte counts to be accurate.  */
  SET_BINARY2 (fd, fileno (stdout));

  while (bytes_to_write)
    {
      size_t bytes_read;
      if (bytes_to_write < bytes_to_read)
    bytes_to_read = bytes_to_write;
      bytes_read = safe_read (fd, buffer, bytes_to_read);
      if (bytes_read == SAFE_READ_ERROR)
    {
      error (0, errno, "%s", filename);
      return 1;
    }
      if (bytes_read == 0)
    break;
      if (fwrite (buffer, 1, bytes_read, stdout) == 0)
    error (EXIT_FAILURE, errno, _("write error"));
      bytes_to_write -= bytes_read;
    }
  return 0;
}

static int
head_lines (const char *filename, int fd, uintmax_t lines_to_write)
{
  char buffer[BUFSIZE];

  /* Need BINARY I/O for the byte counts to be accurate.  */
  SET_BINARY2 (fd, fileno (stdout));

  while (lines_to_write)
    {
      size_t bytes_read = safe_read (fd, buffer, BUFSIZE);
      size_t bytes_to_write = 0;

      if (bytes_read == SAFE_READ_ERROR)
    {
      error (0, errno, "%s", filename);
      return 1;
    }
      if (bytes_read == 0)
    break;
      while (bytes_to_write < bytes_read)
    if (buffer[bytes_to_write++] == '\n' && --lines_to_write == 0)
      {
        off_t n_bytes_past_EOL = bytes_read - bytes_to_write;
        /* If we have read more data than that on the specified number
           of lines, try to seek back to the position we would have
           gotten to had we been reading one byte at a time.  */
        if (lseek (fd, -n_bytes_past_EOL, SEEK_CUR) < 0)
          {
        int e = errno;
        struct stat st;
        if (fstat (fd, &st) != 0 || S_ISREG (st.st_mode))
          error (0, e, _("cannot reposition file pointer for %s"),
             filename);
          }
        break;
      }
      if (fwrite (buffer, 1, bytes_to_write, stdout) == 0)
    error (EXIT_FAILURE, errno, _("write error"));
    }
  return 0;
}

static int
head (const char *filename, int fd, uintmax_t n_units, int count_lines)
{
  if (print_headers)
    write_header (filename);

  if (count_lines)
    return head_lines (filename, fd, n_units);
  else
    return head_bytes (filename, fd, n_units);
}

static int
head_file (const char *filename, uintmax_t n_units, int count_lines)
{
  int fd;

  if (STREQ (filename, "-"))
    {
      have_read_stdin = 1;
      return head (_("standard input"), STDIN_FILENO, n_units, count_lines);
    }
  else
    {
      fd = open (filename, O_RDONLY);
      if (fd >= 0)
    {
      int errors;

      errors = head (filename, fd, n_units, count_lines);
      if (close (fd) == 0)
        return errors;
    }
      error (0, errno, "%s", filename);
      return 1;
    }
}

/* Convert a string of decimal digits, N_STRING, with a single, optional suffix
   character (b, k, or m) to an integral value.  Upon successful conversion,
   return that value.  If it cannot be converted, give a diagnostic and exit.
   COUNT_LINES indicates whether N_STRING is a number of bytes or a number
   of lines.  It is used solely to give a more specific diagnostic.  */

static uintmax_t
string_to_integer (int count_lines, const char *n_string)
{
  strtol_error s_err;
  uintmax_t n;

  s_err = xstrtoumax (n_string, NULL, 10, &n, "bkm");

  if (s_err == LONGINT_OVERFLOW)
    {
      error (EXIT_FAILURE, 0,
         _("%s: %s is so large that it is not representable"), n_string,
         count_lines ? _("number of lines") : _("number of bytes"));
    }

  if (s_err != LONGINT_OK)
    {
      error (EXIT_FAILURE, 0, "%s: %s", n_string,
         (count_lines
          ? _("invalid number of lines")
          : _("invalid number of bytes")));
    }

  return n;
}

int
main (int argc, char **argv)
{
  enum header_mode header_mode = multiple_files;
  int exit_status = 0;
  int c;

  /* Number of items to print. */
  uintmax_t n_units = DEFAULT_NUMBER;

  /* If nonzero, interpret the numeric argument as the number of lines.
     Otherwise, interpret it as the number of bytes.  */
  int count_lines = 1;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  have_read_stdin = 0;

  print_headers = 0;

  if (1 < argc && argv[1][0] == '-' && ISDIGIT (argv[1][1]))
    {
      char *a = argv[1];
      char *n_string = ++a;
      char *end_n_string;
      char multiplier_char = 0;

      /* Old option syntax; a dash, one or more digits, and one or
     more option letters.  Move past the number. */
      do ++a;
      while (ISDIGIT (*a));

      /* Pointer to the byte after the last digit.  */
      end_n_string = a;

      /* Parse any appended option letters. */
      for (; *a; a++)
    {
      switch (*a)
        {
        case 'c':
          count_lines = 0;
          multiplier_char = 0;
          break;

        case 'b':
        case 'k':
        case 'm':
          count_lines = 0;
          multiplier_char = *a;
          break;

        case 'l':
          count_lines = 1;
          break;

        case 'q':
          header_mode = never;
          break;

        case 'v':
          header_mode = always;
          break;

        default:
          error (0, 0, _("unrecognized option `-%c'"), *a);
          usage (EXIT_FAILURE);
        }
    }

      if (200112 <= posix2_version ())
    {
      error (0, 0, _("`-%s' option is obsolete; use `-%c %.*s%.*s%s'"),
         n_string, count_lines ? 'n' : 'c',
         (int) (end_n_string - n_string), n_string,
         multiplier_char != 0, &multiplier_char,
         header_mode_option[header_mode]);
      usage (EXIT_FAILURE);
    }

      /* Append the multiplier character (if any) onto the end of
     the digit string.  Then add NUL byte if necessary.  */
      *end_n_string = multiplier_char;
      if (multiplier_char)
    *(++end_n_string) = 0;

      n_units = string_to_integer (count_lines, n_string);

      /* Make the options we just parsed invisible to getopt. */
      argv[1] = argv[0];
      argv++;
      argc--;

      /* FIXME: allow POSIX options if there were obsolescent ones?  */

    }

  while ((c = getopt_long (argc, argv, "c:n:qv", long_options, NULL)) != -1)
    {
      switch (c)
    {
    case 0:
      break;

    case 'c':
      count_lines = 0;
      n_units = string_to_integer (count_lines, optarg);
      break;

    case 'n':
      count_lines = 1;
      n_units = string_to_integer (count_lines, optarg);
      break;

    case 'q':
      header_mode = never;
      break;

    case 'v':
      header_mode = always;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (header_mode == always
      || (header_mode == multiple_files && optind < argc - 1))
    print_headers = 1;

  if (optind == argc)
    exit_status |= head_file ("-", n_units, count_lines);

  for (; optind < argc; ++optind)
    exit_status |= head_file (argv[optind], n_units, count_lines);

  if (have_read_stdin && close (STDIN_FILENO) < 0)
    error (EXIT_FAILURE, errno, "-");

  exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/hostname.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/hostname.c used in: Step 879
/* hostname - set or print the name of current host system
   Copyright (C) 1994-1997, 1999-2002 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.  */

/* Jim Meyering <meyering@comco.com> */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>

#include "system.h"
#include "closeout.h"
#include "long-options.h"
#include "error.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "hostname"

#define AUTHORS "Jim Meyering"

#if !defined(HAVE_SETHOSTNAME) && defined(HAVE_SYSINFO) && \
     defined (HAVE_SYS_SYSTEMINFO_H) && defined(HAVE_LIMITS_H)
# include <sys/systeminfo.h>

int
sethostname (name, namelen)
     char *name;
     int namelen;
{
  /* Using sysinfo() is the SVR4 mechanism to set a hostname. */
  int result;

  result = sysinfo (SI_SET_HOSTNAME, name, namelen);

  return (result == -1 ? result : 0);
}

# define HAVE_SETHOSTNAME 1  /* Now we have it... */
#endif

char *xgethostname ();

/* The name this program was run with. */
char *program_name;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [NAME]\n\
  or:  %s OPTION\n\
Print or set the hostname of the current system.\n\
\n\
"),
             program_name, program_name);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  char *hostname;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              AUTHORS, usage);

#ifdef HAVE_SETHOSTNAME
  if (argc == 2)
    {
      int err;

      /* Set hostname to argv[1].  */
      err = sethostname (argv[1], strlen (argv[1]));
      if (err != 0)
    error (EXIT_FAILURE, errno, _("cannot set hostname to `%s'"), argv[1]);
      exit (EXIT_SUCCESS);
    }
#else
  if (argc == 2)
    error (EXIT_FAILURE, 0,
       _("cannot set hostname; this system lacks the functionality"));
#endif

  if (argc == 1)
    {
      hostname = xgethostname ();
      if (hostname == NULL)
    error (EXIT_FAILURE, errno, _("cannot determine hostname"));
      printf ("%s\n", hostname);
    }
  else
    {
      error (2, 0, _("too many arguments"));
      usage (EXIT_FAILURE);
    }

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/id.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/id.c used in: Step 881
/* id -- print real and effective UIDs and GIDs
   Copyright (C) 1989-2003 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.  */

/* Written by Arnold Robbins.
   Major rewrite by David MacKenzie, djm@gnu.ai.mit.edu. */

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <getopt.h>

#include "system.h"
#include "error.h"
#include "closeout.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "id"

#define AUTHORS N_ ("Arnold Robbins and David MacKenzie")

#ifndef _POSIX_VERSION
struct passwd *getpwuid ();
struct group *getgrgid ();
uid_t getuid ();
gid_t getgid ();
uid_t geteuid ();
gid_t getegid ();
#endif /* not _POSIX_VERSION */

int getugroups ();

static void print_user (uid_t uid);
static void print_group (gid_t gid);
static void print_group_list (const char *username);
static void print_full_info (const char *username);

/* The name this program was run with. */
char *program_name;

/* If nonzero, output user/group name instead of ID number. -n */
static int use_name = 0;

/* The real and effective IDs of the user to print. */
static uid_t ruid, euid;
static gid_t rgid, egid;

/* The number of errors encountered so far. */
static int problems = 0;

static struct option const longopts[] =
{
  {"group", no_argument, NULL, 'g'},
  {"groups", no_argument, NULL, 'G'},
  {"name", no_argument, NULL, 'n'},
  {"real", no_argument, NULL, 'r'},
  {"user", no_argument, NULL, 'u'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]... [USERNAME]\n"), program_name);
      fputs (_("\
Print information for USERNAME, or the current user.\n\
\n\
  -a              ignore, for compatibility with other versions\n\
  -g, --group     print only the effective group ID\n\
  -G, --groups    print all group IDs\n\
  -n, --name      print a name instead of a number, for -ugG\n\
  -r, --real      print the real ID instead of the effective ID, with -ugG\n\
  -u, --user      print only the effective user ID\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
Without any OPTION, print some useful set of identified information.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  int optc;

  /* If nonzero, output the list of all group IDs. -G */
  int just_group_list = 0;
  /* If nonzero, output only the group ID(s). -g */
  int just_group = 0;
  /* If nonzero, output real UID/GID instead of default effective UID/GID. -r */
  int use_real = 0;
  /* If nonzero, output only the user ID(s). -u */
  int just_user = 0;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  while ((optc = getopt_long (argc, argv, "agnruG", longopts, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:
      break;
    case 'a':
      /* Ignore -a, for compatibility with SVR4.  */
      break;
    case 'g':
      just_group = 1;
      break;
    case 'n':
      use_name = 1;
      break;
    case 'r':
      use_real = 1;
      break;
    case 'u':
      just_user = 1;
      break;
    case 'G':
      just_group_list = 1;
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (EXIT_FAILURE);
    }
    }

  if (just_user + just_group + just_group_list > 1)
    error (EXIT_FAILURE, 0, _("cannot print only user and only group"));

  if (just_user + just_group + just_group_list == 0 && (use_real || use_name))
    error (EXIT_FAILURE, 0,
       _("cannot print only names or real IDs in default format"));

  if (argc - optind > 1)
    usage (EXIT_FAILURE);

  if (argc - optind == 1)
    {
      struct passwd *pwd = getpwnam (argv[optind]);
      if (pwd == NULL)
    error (EXIT_FAILURE, 0, _("%s: No such user"), argv[optind]);
      ruid = euid = pwd->pw_uid;
      rgid = egid = pwd->pw_gid;
    }
  else
    {
      euid = geteuid ();
      ruid = getuid ();
      egid = getegid ();
      rgid = getgid ();
    }

  if (just_user)
    print_user (use_real ? ruid : euid);
  else if (just_group)
    print_group (use_real ? rgid : egid);
  else if (just_group_list)
    print_group_list (argv[optind]);
  else
    print_full_info (argv[optind]);
  putchar ('\n');

  exit (problems != 0);
}

/* Print the name or value of user ID UID. */

static void
print_user (uid_t uid)
{
  struct passwd *pwd = NULL;

  if (use_name)
    {
      pwd = getpwuid (uid);
      if (pwd == NULL)
    {
      error (0, 0, _("cannot find name for user ID %u"), uid);
      problems++;
    }
    }

  if (pwd == NULL)
    printf ("%u", (unsigned) uid);
  else
    printf ("%s", pwd->pw_name);
}

/* Print the name or value of group ID GID. */

static void
print_group (gid_t gid)
{
  struct group *grp = NULL;

  if (use_name)
    {
      grp = getgrgid (gid);
      if (grp == NULL)
    {
      error (0, 0, _("cannot find name for group ID %u"), gid);
      problems++;
    }
    }

  if (grp == NULL)
    printf ("%u", (unsigned) gid);
  else
    printf ("%s", grp->gr_name);
}

#if HAVE_GETGROUPS

/* FIXME: document */

static int
xgetgroups (const char *username, gid_t gid, int *n_groups,
        GETGROUPS_T **groups)
{
  int max_n_groups;
  int ng;
  GETGROUPS_T *g;
  int fail = 0;

  if (username == 0)
    max_n_groups = getgroups (0, NULL);
  else
    max_n_groups = getugroups (0, NULL, username, gid);

  /* Add 1 just in case max_n_groups is zero.  */
  g = (GETGROUPS_T *) xmalloc (max_n_groups * sizeof (GETGROUPS_T) + 1);
  if (username == 0)
    ng = getgroups (max_n_groups, g);
  else
    ng = getugroups (max_n_groups, g, username, gid);

  if (ng < 0)
    {
      error (0, errno, _("cannot get supplemental group list"));
      ++fail;
      free (groups);
    }
  if (!fail)
    {
      *n_groups = ng;
      *groups = g;
    }
  return fail;
}

#endif /* HAVE_GETGROUPS */

/* Print all of the distinct groups the user is in. */

static void
print_group_list (const char *username)
{
  struct passwd *pwd;

  pwd = getpwuid (ruid);
  if (pwd == NULL)
    problems++;

  print_group (rgid);
  if (egid != rgid)
    {
      putchar (' ');
      print_group (egid);
    }

#if HAVE_GETGROUPS
  {
    int n_groups;
    GETGROUPS_T *groups;
    register int i;

    if (xgetgroups (username, (pwd ? pwd->pw_gid : (gid_t) -1),
            &n_groups, &groups))
      {
    ++problems;
    return;
      }

    for (i = 0; i < n_groups; i++)
      if (groups[i] != rgid && groups[i] != egid)
    {
      putchar (' ');
      print_group (groups[i]);
    }
    free (groups);
  }
#endif /* HAVE_GETGROUPS */
}

/* Print all of the info about the user's user and group IDs. */

static void
print_full_info (const char *username)
{
  struct passwd *pwd;
  struct group *grp;

  printf ("uid=%u", (unsigned) ruid);
  pwd = getpwuid (ruid);
  if (pwd == NULL)
    problems++;
  else
    printf ("(%s)", pwd->pw_name);

  printf (" gid=%u", (unsigned) rgid);
  grp = getgrgid (rgid);
  if (grp == NULL)
    problems++;
  else
    printf ("(%s)", grp->gr_name);

  if (euid != ruid)
    {
      printf (" euid=%u", (unsigned) euid);
      pwd = getpwuid (euid);
      if (pwd == NULL)
    problems++;
      else
    printf ("(%s)", pwd->pw_name);
    }

  if (egid != rgid)
    {
      printf (" egid=%u", (unsigned) egid);
      grp = getgrgid (egid);
      if (grp == NULL)
    problems++;
      else
    printf ("(%s)", grp->gr_name);
    }

#if HAVE_GETGROUPS
  {
    int n_groups;
    GETGROUPS_T *groups;
    register int i;

    if (xgetgroups (username, (pwd ? pwd->pw_gid : (gid_t) -1),
            &n_groups, &groups))
      {
    ++problems;
    return;
      }

    if (n_groups > 0)
      fputs (_(" groups="), stdout);
    for (i = 0; i < n_groups; i++)
      {
    if (i > 0)
      putchar (',');
    printf ("%u", (unsigned) groups[i]);
    grp = getgrgid (groups[i]);
    if (grp == NULL)
      problems++;
    else
      printf ("(%s)", grp->gr_name);
      }
    free (groups);
  }
#endif /* HAVE_GETGROUPS */
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/join.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/join.c used in: Step 883
/* join - join lines of two files on a common field
   Copyright (C) 91, 1995-2003 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.

   Written by Mike Haertel, mike@gnu.ai.mit.edu.  */

#include <config.h>

#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
#include <getopt.h>

#include "system.h"
#include "closeout.h"
#include "error.h"
#include "hard-locale.h"
#include "linebuffer.h"
#include "memcasecmp.h"
#include "xmemcoll.h"
#include "xstrtol.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "join"

#define AUTHORS "Mike Haertel"

#define join system_join

/* Undefine, to avoid warning about redefinition on some systems.  */
#undef min
#undef max
#define min(A, B) ((A) < (B) ? (A) : (B))
#define max(A, B) ((A) > (B) ? (A) : (B))

/* An element of the list identifying which fields to print for each
   output line.  */
struct outlist
  {
    /* File number: 0, 1, or 2.  0 means use the join field.
       1 means use the first file argument, 2 the second.  */
    int file;

    /* Field index (zero-based), specified only when FILE is 1 or 2.  */
    int field;

    struct outlist *next;
  };

/* A field of a line.  */
struct field
  {
    const unsigned char *beg;   /* First character in field.  */
    size_t len;         /* The length of the field.  */
  };

/* A line read from an input file.  */
struct line
  {
    struct linebuffer buf;  /* The line itself.  */
    int nfields;        /* Number of elements in `fields'.  */
    int nfields_allocated;  /* Number of elements in `fields'.  */
    struct field *fields;
  };

/* One or more consecutive lines read from a file that all have the
   same join field value.  */
struct seq
  {
    int count;          /* Elements used in `lines'.  */
    int alloc;          /* Elements allocated in `lines'.  */
    struct line *lines;
  };

/* The name this program was run with.  */
char *program_name;

/* Nonzero if the LC_COLLATE locale is hard.  */
static int hard_LC_COLLATE;

/* If nonzero, print unpairable lines in file 1 or 2.  */
static int print_unpairables_1, print_unpairables_2;

/* If nonzero, print pairable lines.  */
static int print_pairables;

/* Empty output field filler.  */
static char *empty_filler;

/* Field to join on.  */
static int join_field_1, join_field_2;

/* List of fields to print.  */
static struct outlist outlist_head;

/* Last element in `outlist', where a new element can be added.  */
static struct outlist *outlist_end = &outlist_head;

/* Tab character separating fields; if this is NUL fields are separated
   by any nonempty string of white space, otherwise by exactly one
   tab character.  */
static unsigned char tab;

/* When using getopt_long_only, no long option can start with
   a character that is a short option.  */
static struct option const longopts[] =
{
  {"ignore-case", no_argument, NULL, 'i'},
  {"j", required_argument, NULL, 'j'},
  {"j1", required_argument, NULL, '1'},
  {"j2", required_argument, NULL, '2'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

/* Used to print non-joining lines */
static struct line uni_blank;

/* If nonzero, ignore case when comparing join fields.  */
static int ignore_case;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... FILE1 FILE2\n\
"),
          program_name);
      fputs (_("\
For each pair of input lines with identical join fields, write a line to\n\
standard output.  The default join field is the first, delimited\n\
by whitespace.  When FILE1 or FILE2 (not both) is -, read standard input.\n\
\n\
  -a FILENUM        print unpairable lines coming from file FILENUM, where\n\
                      FILENUM is 1 or 2, corresponding to FILE1 or FILE2\n\
  -e EMPTY          replace missing input fields with EMPTY\n\
"), stdout);
      fputs (_("\
  -i, --ignore-case ignore differences in case when comparing fields\n\
  -j FIELD          (obsolescent) equivalent to `-1 FIELD -2 FIELD'\n\
  -j1 FIELD         (obsolescent) equivalent to `-1 FIELD'\n\
  -j2 FIELD         (obsolescent) equivalent to `-2 FIELD'\n\
  -o FORMAT         obey FORMAT while constructing output line\n\
  -t CHAR           use CHAR as input and output field separator\n\
"), stdout);
      fputs (_("\
  -v FILENUM        like -a FILENUM, but suppress joined output lines\n\
  -1 FIELD          join on this FIELD of file 1\n\
  -2 FIELD          join on this FIELD of file 2\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
Unless -t CHAR is given, leading blanks separate fields and are ignored,\n\
else fields are separated by CHAR.  Any FIELD is a field number counted\n\
from 1.  FORMAT is one or more comma or blank separated specifications,\n\
each being `FILENUM.FIELD' or `0'.  Default FORMAT outputs the join field,\n\
the remaining fields from FILE1, the remaining fields from FILE2, all\n\
separated by CHAR.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

static void
ADD_FIELD (struct line *line, const unsigned char *field, size_t len)
{
  if (line->nfields >= line->nfields_allocated)
    {
      line->nfields_allocated = (3 * line->nfields_allocated) / 2 + 1;
      line->fields = (struct field *) xrealloc ((char *) line->fields,
                        (line->nfields_allocated
                         * sizeof (struct field)));
    }
  line->fields[line->nfields].beg = field;
  line->fields[line->nfields].len = len;
  ++(line->nfields);
}

/* Fill in the `fields' structure in LINE.  */

static void
xfields (struct line *line)
{
  int i;
  unsigned char *ptr0 = (unsigned char *) line->buf.buffer;
  unsigned char *ptr;
  unsigned char *lim;

  ptr = ptr0;
  lim = ptr0 + line->buf.length - 1;

  if (!tab)
    {
      /* Skip leading blanks before the first field.  */
      while (ptr < lim && ISBLANK (*ptr))
    ++ptr;
    }

  for (i = 0; ptr < lim; ++i)
    {
      if (tab)
    {
      unsigned char *beg;

      beg = ptr;
      while (ptr < lim && *ptr != tab)
        ++ptr;
      ADD_FIELD (line, beg, ptr - beg);
      if (ptr < lim)
        ++ptr;
    }
      else
    {
      unsigned char *beg;

      beg = ptr;
      while (ptr < lim && !ISBLANK (*ptr))
        ++ptr;
      ADD_FIELD (line, beg, ptr - beg);
      while (ptr < lim && ISBLANK (*ptr))
        ++ptr;
    }
    }

  if (ptr != ptr0 && ((!tab && ISBLANK (ptr[-1])) || ptr[-1] == tab))
    {
      /* Add one more (empty) field because the last character of the
     line was a delimiter.  */
      ADD_FIELD (line, NULL, 0);
    }
}

/* Read a line from FP into LINE and split it into fields.
   Return 0 if EOF, 1 otherwise.  */

static int
get_line (FILE *fp, struct line *line)
{
  initbuffer (&line->buf);

  if (! readline (&line->buf, fp))
    {
      free (line->buf.buffer);
      line->buf.buffer = NULL;
      return 0;
    }

  line->nfields_allocated = 0;
  line->nfields = 0;
  line->fields = NULL;
  xfields (line);
  return 1;
}

static void
freeline (struct line *line)
{
  free ((char *) line->fields);
  free (line->buf.buffer);
  line->buf.buffer = NULL;
}

static void
initseq (struct seq *seq)
{
  seq->count = 0;
  seq->alloc = 1;
  seq->lines = (struct line *) xmalloc (seq->alloc * sizeof (struct line));
}

/* Read a line from FP and add it to SEQ.  Return 0 if EOF, 1 otherwise.  */

static int
getseq (FILE *fp, struct seq *seq)
{
  if (seq->count == seq->alloc)
    {
      seq->alloc *= 2;
      seq->lines = (struct line *)
    xrealloc ((char *) seq->lines, seq->alloc * sizeof (struct line));
    }

  if (get_line (fp, &seq->lines[seq->count]))
    {
      ++seq->count;
      return 1;
    }
  return 0;
}

static void
delseq (struct seq *seq)
{
  int i;
  for (i = 0; i < seq->count; i++)
    if (seq->lines[i].buf.buffer)
      freeline (&seq->lines[i]);
  free ((char *) seq->lines);
}

/* Return <0 if the join field in LINE1 compares less than the one in LINE2;
   >0 if it compares greater; 0 if it compares equal.
   Report an error and exit if the comparison fails.  */

static int
keycmp (struct line *line1, struct line *line2)
{
  /* Start of field to compare in each file.  */
  const unsigned char *beg1, *beg2;

  size_t len1, len2;        /* Length of fields to compare.  */
  int diff;

  if (join_field_1 < line1->nfields)
    {
      beg1 = line1->fields[join_field_1].beg;
      len1 = line1->fields[join_field_1].len;
    }
  else
    {
      beg1 = NULL;
      len1 = 0;
    }

  if (join_field_2 < line2->nfields)
    {
      beg2 = line2->fields[join_field_2].beg;
      len2 = line2->fields[join_field_2].len;
    }
  else
    {
      beg2 = NULL;
      len2 = 0;
    }

  if (len1 == 0)
    return len2 == 0 ? 0 : -1;
  if (len2 == 0)
    return 1;

  /* Use an if-statement here rather than a function variable to
     avoid portability hassles of getting a non-conflicting declaration
     of memcmp.  */
  if (ignore_case)
    {
      /* FIXME: ignore_case does not work with NLS (in particular,
         with multibyte chars).  */
      diff = memcasecmp (beg1, beg2, min (len1, len2));
    }
  else
    {
      if (HAVE_SETLOCALE && hard_LC_COLLATE)
    return xmemcoll ((char *) beg1, len1, (char *) beg2, len2);
      diff = memcmp (beg1, beg2, min (len1, len2));
    }

  if (diff)
    return diff;
  return len1 < len2 ? -1 : len1 != len2;
}

/* Print field N of LINE if it exists and is nonempty, otherwise
   `empty_filler' if it is nonempty.  */

static void
prfield (int n, struct line *line)
{
  size_t len;

  if (n < line->nfields)
    {
      len = line->fields[n].len;
      if (len)
    fwrite (line->fields[n].beg, 1, len, stdout);
      else if (empty_filler)
    fputs (empty_filler, stdout);
    }
  else if (empty_filler)
    fputs (empty_filler, stdout);
}

/* Print the join of LINE1 and LINE2.  */

static void
prjoin (struct line *line1, struct line *line2)
{
  const struct outlist *outlist;

  outlist = outlist_head.next;
  if (outlist)
    {
      const struct outlist *o;

      o = outlist;
      while (1)
    {
      int field;
      struct line *line;

      if (o->file == 0)
        {
          if (line1 == &uni_blank)
            {
          line = line2;
          field = join_field_2;
        }
          else
            {
          line = line1;
          field = join_field_1;
        }
        }
      else
        {
          line = (o->file == 1 ? line1 : line2);
          assert (o->field >= 0);
          field = o->field;
        }
      prfield (field, line);
      o = o->next;
      if (o == NULL)
        break;
      putchar (tab ? tab : ' ');
    }
      putchar ('\n');
    }
  else
    {
      int i;

      if (line1 == &uni_blank)
    {
      struct line *t;
      t = line1;
      line1 = line2;
      line2 = t;
    }
      prfield (join_field_1, line1);
      for (i = 0; i < join_field_1 && i < line1->nfields; ++i)
    {
      putchar (tab ? tab : ' ');
      prfield (i, line1);
    }
      for (i = join_field_1 + 1; i < line1->nfields; ++i)
    {
      putchar (tab ? tab : ' ');
      prfield (i, line1);
    }

      for (i = 0; i < join_field_2 && i < line2->nfields; ++i)
    {
      putchar (tab ? tab : ' ');
      prfield (i, line2);
    }
      for (i = join_field_2 + 1; i < line2->nfields; ++i)
    {
      putchar (tab ? tab : ' ');
      prfield (i, line2);
    }
      putchar ('\n');
    }
}

/* Print the join of the files in FP1 and FP2.  */

static void
join (FILE *fp1, FILE *fp2)
{
  struct seq seq1, seq2;
  struct line line;
  int diff, i, j, eof1, eof2;

  /* Read the first line of each file.  */
  initseq (&seq1);
  getseq (fp1, &seq1);
  initseq (&seq2);
  getseq (fp2, &seq2);

  while (seq1.count && seq2.count)
    {
      diff = keycmp (&seq1.lines[0], &seq2.lines[0]);
      if (diff < 0)
    {
      if (print_unpairables_1)
        prjoin (&seq1.lines[0], &uni_blank);
      freeline (&seq1.lines[0]);
      seq1.count = 0;
      getseq (fp1, &seq1);
      continue;
    }
      if (diff > 0)
    {
      if (print_unpairables_2)
        prjoin (&uni_blank, &seq2.lines[0]);
      freeline (&seq2.lines[0]);
      seq2.count = 0;
      getseq (fp2, &seq2);
      continue;
    }

      /* Keep reading lines from file1 as long as they continue to
         match the current line from file2.  */
      eof1 = 0;
      do
    if (!getseq (fp1, &seq1))
      {
        eof1 = 1;
        ++seq1.count;
        break;
      }
      while (!keycmp (&seq1.lines[seq1.count - 1], &seq2.lines[0]));

      /* Keep reading lines from file2 as long as they continue to
         match the current line from file1.  */
      eof2 = 0;
      do
    if (!getseq (fp2, &seq2))
      {
        eof2 = 1;
        ++seq2.count;
        break;
      }
      while (!keycmp (&seq1.lines[0], &seq2.lines[seq2.count - 1]));

      if (print_pairables)
    {
      for (i = 0; i < seq1.count - 1; ++i)
        for (j = 0; j < seq2.count - 1; ++j)
          prjoin (&seq1.lines[i], &seq2.lines[j]);
    }

      for (i = 0; i < seq1.count - 1; ++i)
    freeline (&seq1.lines[i]);
      if (!eof1)
    {
      seq1.lines[0] = seq1.lines[seq1.count - 1];
      seq1.count = 1;
    }
      else
    seq1.count = 0;

      for (i = 0; i < seq2.count - 1; ++i)
    freeline (&seq2.lines[i]);
      if (!eof2)
    {
      seq2.lines[0] = seq2.lines[seq2.count - 1];
      seq2.count = 1;
    }
      else
    seq2.count = 0;
    }

  if (print_unpairables_1 && seq1.count)
    {
      prjoin (&seq1.lines[0], &uni_blank);
      freeline (&seq1.lines[0]);
      while (get_line (fp1, &line))
    {
      prjoin (&line, &uni_blank);
      freeline (&line);
    }
    }

  if (print_unpairables_2 && seq2.count)
    {
      prjoin (&uni_blank, &seq2.lines[0]);
      freeline (&seq2.lines[0]);
      while (get_line (fp2, &line))
    {
      prjoin (&uni_blank, &line);
      freeline (&line);
    }
    }

  delseq (&seq1);
  delseq (&seq2);
}

/* Add a field spec for field FIELD of file FILE to `outlist'.  */

static void
add_field (int file, int field)
{
  struct outlist *o;

  assert (file == 0 || file == 1 || file == 2);
  assert (file == 0 ? field < 0 : field >= 0);

  o = (struct outlist *) xmalloc (sizeof (struct outlist));
  o->file = file;
  o->field = field;
  o->next = NULL;

  /* Add to the end of the list so the fields are in the right order.  */
  outlist_end->next = o;
  outlist_end = o;
}

/* Convert a single field specifier string, S, to a *FILE_INDEX, *FIELD_INDEX
   pair.  In S, the field index string is 1-based; *FIELD_INDEX is zero-based.
   If S is valid, return zero.  Otherwise, give a diagnostic, don't update
   *FILE_INDEX or *FIELD_INDEX, and return nonzero.  */

static int
decode_field_spec (const char *s, int *file_index, int *field_index)
{
  int invalid = 1;

  /* The first character must be 0, 1, or 2.  */
  switch (s[0])
    {
    case '0':
      if (s[1] == '\0')
    {
      *file_index = 0;
      /* Give *field_index an invalid value.  */
      *field_index = -1;
      invalid = 0;
    }
      else
        {
      /* `0' must be all alone -- no `.FIELD'.  */
      error (0, 0, _("invalid field specifier: `%s'"), s);
    }
      break;

    case '1':
    case '2':
      if (s[1] == '.' && s[2] != '\0')
        {
      strtol_error s_err;
      long int tmp_long;

      s_err = xstrtol (s + 2, NULL, 10, &tmp_long, "");
      if (s_err != LONGINT_OK || tmp_long <= 0 || tmp_long > INT_MAX)
        {
          error (0, 0, _("invalid field number: `%s'"), s + 2);
        }
      else
        {
          *file_index = s[0] - '0';
          /* Convert to a zero-based index.  */
          *field_index = (int) tmp_long - 1;
          invalid = 0;
        }
    }
      break;

    default:
      error (0, 0, _("invalid file number in field spec: `%s'"), s);
      break;
    }
  return invalid;
}

/* Add the comma or blank separated field spec(s) in STR to `outlist'.
   Return nonzero to indicate failure.  */

static int
add_field_list (const char *c_str)
{
  char *p, *str;

  /* Make a writable copy of c_str.  */
  str = (char *) alloca (strlen (c_str) + 1);
  strcpy (str, c_str);

  p = str;
  do
    {
      int invalid;
      int file_index, field_index;
      char *spec_item = p;

      p = strpbrk (p, ", \t");
      if (p)
        *p++ = 0;
      invalid = decode_field_spec (spec_item, &file_index, &field_index);
      if (invalid)
    return 1;
      add_field (file_index, field_index);
      uni_blank.nfields = max (uni_blank.nfields, field_index);
    }
  while (p);
  return 0;
}

/* Create a blank line with COUNT fields separated by tabs.  */

static void
make_blank (struct line *blank, int count)
{
  int i;
  unsigned char *buffer;
  struct field *fields;
  blank->nfields = count;
  blank->buf.size = blank->buf.length = count + 1;
  blank->buf.buffer = xmalloc (blank->buf.size);
  buffer = (unsigned char *) blank->buf.buffer;
  blank->fields = fields =
    (struct field *) xmalloc (sizeof (struct field) * count);
  for (i = 0; i < count; i++)
    {
      buffer[i] = '\t';
      fields[i].beg = &buffer[i];
      fields[i].len = 0;
    }
  buffer[i] = '\n';
}

int
main (int argc, char **argv)
{
  char *names[2];
  FILE *fp1, *fp2;
  int optc, prev_optc = 0, nfiles;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);
  hard_LC_COLLATE = hard_locale (LC_COLLATE);

  atexit (close_stdout);

  /* Initialize this before parsing options.  In parsing options,
     it may be increased.  */
  uni_blank.nfields = 1;

  nfiles = 0;
  print_pairables = 1;

  while ((optc = getopt_long_only (argc, argv, "-a:e:i1:2:o:t:v:", longopts,
                   NULL)) != -1)
    {
      long int val;

      switch (optc)
    {
    case 0:
      break;

    case 'v':
        print_pairables = 0;
        /* Fall through.  */

    case 'a':
      if (xstrtol (optarg, NULL, 10, &val, "") != LONGINT_OK
          || (val != 1 && val != 2))
        error (EXIT_FAILURE, 0, _("invalid field number: `%s'"), optarg);
      if (val == 1)
        print_unpairables_1 = 1;
      else
        print_unpairables_2 = 1;
      break;

    case 'e':
      empty_filler = optarg;
      break;

    case 'i':
      ignore_case = 1;
      break;

    case '1':
      if (xstrtol (optarg, NULL, 10, &val, "") != LONGINT_OK
          || val <= 0 || val > INT_MAX)
        {
          error (EXIT_FAILURE, 0,
             _("invalid field number for file 1: `%s'"), optarg);
        }
      join_field_1 = (int) val - 1;
      break;

    case '2':
      if (xstrtol (optarg, NULL, 10, &val, "") != LONGINT_OK
          || val <= 0 || val > INT_MAX)
        error (EXIT_FAILURE, 0,
           _("invalid field number for file 2: `%s'"), optarg);
      join_field_2 = (int) val - 1;
      break;

    case 'j':
      if (xstrtol (optarg, NULL, 10, &val, "") != LONGINT_OK
          || val <= 0 || val > INT_MAX)
        error (EXIT_FAILURE, 0, _("invalid field number: `%s'"), optarg);
      join_field_1 = join_field_2 = (int) val - 1;
      break;

    case 'o':
      if (add_field_list (optarg))
        exit (EXIT_FAILURE);
      break;

    case 't':
      tab = *optarg;
      break;

    case 1:     /* Non-option argument.  */
      if (prev_optc == 'o' && optind <= argc - 2)
        {
          if (add_field_list (optarg))
        exit (EXIT_FAILURE);

          /* Might be continuation of args to -o.  */
          continue;     /* Don't change `prev_optc'.  */
        }

      if (nfiles > 1)
        {
          error (0, 0, _("too many non-option arguments"));
          usage (EXIT_FAILURE);
        }
      names[nfiles++] = optarg;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
      prev_optc = optc;
    }

  /* Now that we've seen the options, we can construct the blank line
     structure.  */
  make_blank (&uni_blank, uni_blank.nfields);

  if (nfiles != 2)
    {
      error (0, 0, _("too few non-option arguments"));
      usage (EXIT_FAILURE);
    }

  fp1 = STREQ (names[0], "-") ? stdin : fopen (names[0], "r");
  if (!fp1)
    error (EXIT_FAILURE, errno, "%s", names[0]);
  fp2 = STREQ (names[1], "-") ? stdin : fopen (names[1], "r");
  if (!fp2)
    error (EXIT_FAILURE, errno, "%s", names[1]);
  if (fp1 == fp2)
    error (EXIT_FAILURE, errno, _("both files cannot be standard input"));
  join (fp1, fp2);

  if (fp1 != stdin && fclose (fp1) == EOF)
    error (EXIT_FAILURE, errno, "%s", names[0]);
  if (fp2 != stdin && fclose (fp2) == EOF)
    error (EXIT_FAILURE, errno, "%s", names[1]);
  if ((fp1 == stdin || fp2 == stdin) && fclose (stdin) == EOF)
    error (EXIT_FAILURE, errno, "-");

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/hard-locale.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/hard-locale.h used in: Step 883, Step 923, Step 939
#ifndef HARD_LOCALE_H_
# define HARD_LOCALE_H_ 1

# if HAVE_CONFIG_H
#  include <config.h>
# endif

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

int hard_locale PARAMS ((int));

#endif /* HARD_LOCALE_H_ */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/linebuffer.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/linebuffer.h used in: Step 883, Step 899, Step 939
/* linebuffer.h -- declarations for reading arbitrarily long lines
   Copyright (C) 1986, 1991, 1998, 1999 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   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.  */

#if !defined LINEBUFFER_H
# define LINEBUFFER_H

/* A `struct linebuffer' holds a line of text. */

struct linebuffer
{
  size_t size;          /* Allocated. */
  size_t length;        /* Used. */
  char *buffer;
};

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

/* Initialize linebuffer LINEBUFFER for use. */
void initbuffer PARAMS ((struct linebuffer *linebuffer));

/* Read an arbitrarily long line of text from STREAM into LINEBUFFER.
   Keep the newline; append a newline if it's the last line of a file
   that ends in a non-newline character.  Do not null terminate.
   Return LINEBUFFER, except at end of file return 0.  */
struct linebuffer *readline PARAMS ((struct linebuffer *linebuffer,
                     FILE *stream));

/* Free linebuffer LINEBUFFER and its data, all allocated with malloc. */
void freebuffer PARAMS ((struct linebuffer *));

#endif /* LINEBUFFER_H */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/memcasecmp.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/memcasecmp.h used in: Step 883, Step 939
#ifndef PARAMS
# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#  define PARAMS(Args) Args
# else
#  define PARAMS(Args) ()
# endif
#endif

int
  memcasecmp PARAMS ((const void *vs1, const void *vs2, size_t n));

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xmemcoll.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xmemcoll.h used in: Step 883, Step 923, Step 939
extern int xmemcoll_exit_failure;
int xmemcoll (char *, size_t, char *, size_t);

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/kill.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/kill.c used in: Step 885
/* kill -- send a signal to a process
   Copyright (C) 2002 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.  */

/* Written by Paul Eggert.  */

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include <signal.h>

#if HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#ifndef WIFSIGNALED
# define WIFSIGNALED(s) (((s) & 0xFFFF) - 1 < (unsigned int) 0xFF)
#endif
#ifndef WTERMSIG
# define WTERMSIG(s) ((s) & 0x7F)
#endif

#include "system.h"
#include "closeout.h"
#include "error.h"
#include "sig2str.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "kill"

#define AUTHORS "Paul Eggert"


#if ! (HAVE_DECL_STRTOIMAX || defined strtoimax)
intmax_t strtoimax ();
#endif

#if ! (HAVE_DECL_STRSIGNAL || defined strsignal)
# if ! (HAVE_DECL_SYS_SIGLIST || defined sys_siglist)
#  if HAVE_DECL__SYS_SIGLIST || defined _sys_siglist
#   define sys_siglist _sys_siglist
#  endif
# endif
# if HAVE_DECL_SYS_SIGLIST || defined sys_siglist
#  define strsignal(signum) (0 <= (signum) && (signum) <= SIGNUM_BOUND \
                 ? sys_siglist[signum] \
                 : 0)
# endif
# ifndef strsignal
#  define strsignal(signum) 0
# endif
#endif


/* The name this program was run with, for error messages.  */
char *program_name;

static char const short_options[] =
  "0::1::2::3::4::5::6::7::8::9::"
  "A::B::C::D::E::F::G::H::I::J::K::L::M::"
  "N::O::P::Q::R::S::T::U::V::W::X::Y::Z::"
  "ln:s:t";

static struct option const long_options[] =
{
  {"list", no_argument, NULL, 'l'},
  {"signal", required_argument, NULL, 's'},
  {"table", no_argument, NULL, 't'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [-s SIGNAL | -SIGNAL] PID...\n\
  or:  %s -l [SIGNAL]...\n\
  or:  %s -t [SIGNAL]...\n\
"),
          program_name, program_name, program_name);
      fputs (_("\
Send signals to processes, or list signals.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -s, --signal=SIGNAL, -SIGNAL\n\
                   specify the name or number of the signal to be sent\n\
  -l, --list       list signal names, or convert signal names to/from numbers\n\
  -t, --table      print a table of signal information\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\n\
SIGNAL may be a signal name like `HUP', or a signal number like `1',\n\
or an exit status of a process terminated by a signal.\n\
PID is an integer; if negative it identifies a process group.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}


/* Convert OPERAND to a signal number with printable representation SIGNAME.
   Return the signal number, or -1 if unsuccessful.  */

static int
operand2sig (char const *operand, char *signame)
{
  int signum;

  if (ISDIGIT (*operand))
    {
      char *endp;
      long int l = (errno = 0, strtol (operand, &endp, 10));
      int i = l;
      signum = (operand == endp || *endp || errno || i != l ? -1
        : WIFSIGNALED (i) ? WTERMSIG (i)
        : i);
    }
  else
    {
      /* Convert signal to upper case in the C locale, not in the
     current locale.  Don't assume ASCII; it might be EBCDIC.  */
      char *upcased = xstrdup (operand);
      char *p;
      for (p = upcased; *p; p++)
    if (strchr ("abcdefghijklmnopqrstuvwxyz", *p))
      *p += 'A' - 'a';

      /* Look for the signal name, possibly prefixed by "SIG",
     and possibly lowercased.  */
      if (! (str2sig (upcased, &signum) == 0
         || (upcased[0] == 'S' && upcased[1] == 'I' && upcased[2] == 'G'
         && str2sig (upcased + 3, &signum) == 0)))
    signum = -1;

      free (upcased);
    }

  if (signum < 0 || sig2str (signum, signame) != 0)
    {
      error (0, 0, _("%s: invalid signal"), operand);
      return -1;
    }

  return signum;
}


/* Print a row of `kill -t' output.  NUM_WIDTH is the maximum signal
   number width, and SIGNUM is the signal number to print.  The
   maximum name width is NAME_WIDTH, and SIGNAME is the name to print.  */

static void
print_table_row (unsigned int num_width, int signum,
         unsigned int name_width, char const *signame)
{
  char const *description = strsignal (signum);
  printf ("%*d %-*s %s\n", num_width, signum, name_width, signame,
      description ? description : "?");
}

/* Print a list of signal names.  If TABLE, print a table.
   Print the names specified by ARGV if nonzero; otherwise,
   print all known names.  Return a suitable exit status.  */

static int
list_signals (bool table, char *const *argv)
{
  int signum;
  int status = EXIT_SUCCESS;
  char signame[SIG2STR_MAX];

  if (table)
    {
      unsigned int name_width = 0;

      /* Compute the maximum width of a signal number.  */
      unsigned int num_width = 1;
      for (signum = 1; signum <= SIGNUM_BOUND / 10; signum *= 10)
    num_width++;

      /* Compute the maximum width of a signal name.  */
      for (signum = 1; signum <= SIGNUM_BOUND; signum++)
    if (sig2str (signum, signame) == 0)
      {
        size_t len = strlen (signame);
        if (name_width < len)
          name_width = len;
      }

      if (argv)
    for (; *argv; argv++)
      {
        signum = operand2sig (*argv, signame);
        if (signum < 0)
          status = EXIT_FAILURE;
        else
          print_table_row (num_width, signum, name_width, signame);
      }
      else
    for (signum = 1; signum <= SIGNUM_BOUND; signum++)
      if (sig2str (signum, signame) == 0)
        print_table_row (num_width, signum, name_width, signame);
    }
  else
    {
      if (argv)
    for (; *argv; argv++)
      {
        signum = operand2sig (*argv, signame);
        if (signum < 0)
          status = EXIT_FAILURE;
        else
          {
        if (ISDIGIT (**argv))
          puts (signame);
        else
          printf ("%d\n", signum);
          }
      }
      else
    for (signum = 1; signum <= SIGNUM_BOUND; signum++)
      if (sig2str (signum, signame) == 0)
        puts (signame);
    }

  return status;
}


/* Send signal SIGNUM to all the processes or process groups specified
   by ARGV.  Return a suitable exit status.  */

static int
send_signals (int signum, char *const *argv)
{
  int status = EXIT_SUCCESS;
  char const *arg = *argv;

  if (! arg)
    {
      error (0, 0, _("missing operand after `%s'"), argv[-1]);
      usage (EXIT_FAILURE);
    }

  do
    {
      char *endp;
      intmax_t n = (errno = 0, strtoimax (arg, &endp, 10));
      pid_t pid = n;

      if (errno == ERANGE || pid != n || arg == endp || *endp)
    {
      error (0, 0, _("%s: invalid process id"), arg);
      status = EXIT_FAILURE;
    }
      else if (kill (pid, signum) != 0)
    {
      error (0, errno, "%s", arg);
      status = EXIT_FAILURE;
    }
    }
  while ((arg = *++argv));

  return status;
}


int
main (int argc, char **argv)
{
  int optc;
  bool list = false;
  bool table = false;
  int signum = -1;
  char signame[SIG2STR_MAX];

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  while ((optc = getopt_long (argc, argv, short_options, long_options, NULL))
     != -1)
    switch (optc)
      {
      case '0': case '1': case '2': case '3': case '4':
      case '5': case '6': case '7': case '8': case '9':
    if (optind != 2)
      {
        /* This option is actually a process-id.  */
        optind--;
        goto no_more_options;
      }
    /* Fall through.  */
      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':
    if (! optarg)
      optarg = argv[optind - 1] + strlen (argv[optind - 1]);
    if (optarg != argv[optind - 1] + 2)
      {
        error (0, 0, _("invalid option -- %c"), optc);
        usage (EXIT_FAILURE);
      }
    optarg--;
    /* Fall through.  */
      case 'n': /* -n is not documented, but is for Bash compatibility.  */
      case 's':
    if (0 <= signum)
      {
        error (0, 0, _("%s: multiple signals specified"), optarg);
        usage (EXIT_FAILURE);
      }
    signum = operand2sig (optarg, signame);
    if (signum < 0)
      usage (EXIT_FAILURE);
    break;

      case 't':
    table = true;
    /* Fall through.  */
      case 'l':
    if (list)
      {
        error (0, 0, _("multiple -l or -t options specified"));
        usage (EXIT_FAILURE);
      }
    list = true;
    break;

      case_GETOPT_HELP_CHAR;
      case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
      default:
    usage (EXIT_FAILURE);
      }
 no_more_options:;

  if (signum < 0)
    signum = SIGTERM;
  else if (list)
    {
      error (0, 0, _("cannot combine signal with -l or -t"));
      usage (EXIT_FAILURE);
    }

  return (list
      ? list_signals (table, optind == argc ? NULL : argv + optind)
      : send_signals (signum, argv + optind));
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/link.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/link.c used in: Step 887
/* link utility for GNU.
   Copyright (C) 2001-2002 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.  */

/* Written by Michael Stone */

/* Implementation overview:

   Simply call the system 'link' function */

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include <assert.h>

#include "system.h"
#include "error.h"
#include "long-options.h"
#include "quote.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "link"

#define AUTHORS "Michael Stone"

/* Name this program was run with.  */
char *program_name;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s FILE1 FILE2\n\
  or:  %s OPTION\n"), program_name, program_name);
      fputs (_("Call the link function to create a link named FILE2\
 to an existing FILE1.\n\n"),
         stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              AUTHORS, usage);

  /* The above handles --help and --version.
     Since there is no other invocation of getopt, handle `--' here.  */
  if (1 < argc && STREQ (argv[1], "--"))
    {
      --argc;
      ++argv;
    }

  if (argc < 3)
    {
      error (0, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }

  if (3 < argc)
    {
      error (0, 0, _("too many arguments"));
      usage (EXIT_FAILURE);
    }

  if (link (argv[1], argv[2]) != 0)
    error (EXIT_FAILURE, errno, _("cannot create link %s to %s"),
       quote_n (0, argv[2]), quote_n (1, argv[1]));

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ln.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/ln.c used in: Step 889
/* `ln' program to create links between files.
   Copyright (C) 86, 89, 90, 91, 1995-2002 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.  */


/* Written by Mike Parker and David MacKenzie. */

#ifdef _AIX
 #pragma alloca
#endif

#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>

#include "system.h"
#include "same.h"
#include "backupfile.h"
#include "dirname.h"
#include "error.h"
#include "quote.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "ln"

#define AUTHORS N_ ("Mike Parker and David MacKenzie")

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  TARGET_DIRECTORY_OPTION = CHAR_MAX + 1
};

int link ();            /* Some systems don't declare this anywhere. */

#ifdef S_ISLNK
int symlink ();
#endif

/* In being careful not even to try to make hard links to directories,
   we have to know whether link(2) follows symlinks.  If it does, then
   we have to *stat* the `source' to see if the resulting link would be
   to a directory.  Otherwise, we have to use *lstat* so that we allow
   users to make hard links to symlinks-that-point-to-directories.  */

#if LINK_FOLLOWS_SYMLINKS
# define STAT_LIKE_LINK(File, Stat_buf) \
  stat (File, Stat_buf)
#else
# define STAT_LIKE_LINK(File, Stat_buf) \
  lstat (File, Stat_buf)
#endif

/* Construct a string NEW_DEST by concatenating DEST, a slash, and
   basename(SOURCE) in alloca'd memory.  Don't modify DEST or SOURCE.  */

#define PATH_BASENAME_CONCAT(new_dest, dest, source)            \
    do                                  \
      {                                 \
    const char *source_base;                    \
    char *tmp_source;                       \
                                    \
    tmp_source = (char *) alloca (strlen ((source)) + 1);       \
    strcpy (tmp_source, (source));                  \
    strip_trailing_slashes (tmp_source);                \
    source_base = base_name (tmp_source);               \
                                    \
    (new_dest) = (char *) alloca (strlen ((dest)) + 1       \
                      + strlen (source_base) + 1);  \
    stpcpy (stpcpy (stpcpy ((new_dest), (dest)), "/"), source_base);\
      }                                 \
    while (0)

int isdir ();
int yesno ();

/* The name by which the program was run, for error messages.  */
char *program_name;

/* FIXME: document */
enum backup_type backup_type;

/* A pointer to the function used to make links.  This will point to either
   `link' or `symlink'. */
static int (*linkfunc) ();

/* If nonzero, make symbolic links; otherwise, make hard links.  */
static int symbolic_link;

/* If nonzero, ask the user before removing existing files.  */
static int interactive;

/* If nonzero, remove existing files unconditionally.  */
static int remove_existing_files;

/* If nonzero, list each file as it is moved. */
static int verbose;

/* If nonzero, allow the superuser to make hard links to directories. */
static int hard_dir_link;

/* If nonzero, and the specified destination is a symbolic link to a
   directory, treat it just as if it were a directory.  Otherwise, the
   command `ln --force --no-dereference file symlink-to-dir' deletes
   symlink-to-dir before creating the new link.  */
static int dereference_dest_dir_symlinks = 1;

static struct option const long_options[] =
{
  {"backup", optional_argument, NULL, 'b'},
  {"directory", no_argument, NULL, 'F'},
  {"no-dereference", no_argument, NULL, 'n'},
  {"force", no_argument, NULL, 'f'},
  {"interactive", no_argument, NULL, 'i'},
  {"suffix", required_argument, NULL, 'S'},
  {"target-directory", required_argument, NULL, TARGET_DIRECTORY_OPTION},
  {"symbolic", no_argument, NULL, 's'},
  {"verbose", no_argument, NULL, 'v'},
  {"version-control", required_argument, NULL, 'V'}, /* Deprecated. FIXME. */
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

/* Make a link DEST to the (usually) existing file SOURCE.
   Symbolic links to nonexistent files are allowed.
   If DEST is a directory, put the link to SOURCE in that directory.
   Return 1 if there is an error, otherwise 0.  */

static int
do_link (const char *source, const char *dest)
{
  struct stat source_stats;
  struct stat dest_stats;
  char *dest_backup = NULL;
  int lstat_status;
  int backup_succeeded = 0;

  /* Use stat here instead of lstat.
     On SVR4, link does not follow symlinks, so this check disallows
     making hard links to symlinks that point to directories.  Big deal.
     On other systems, link follows symlinks, so this check is right.  */
  if (!symbolic_link)
    {
      if (STAT_LIKE_LINK (source, &source_stats) != 0)
    {
      error (0, errno, _("accessing %s"), quote (source));
      return 1;
    }

      if (S_ISLNK (source_stats.st_mode))
    {
      error (0, 0, _("%s: warning: making a hard link to a symbolic link\
 is not portable"),
         quote (source));
    }

      if (!hard_dir_link && S_ISDIR (source_stats.st_mode))
    {
      error (0, 0, _("%s: hard link not allowed for directory"),
         quote (source));
      return 1;
    }
    }

  lstat_status = lstat (dest, &dest_stats);
  if (lstat_status != 0 && errno != ENOENT)
    {
      error (0, errno, _("accessing %s"), quote (dest));
      return 1;
    }

  /* If the destination is a directory or (it is a symlink to a directory
     and the user has not specified --no-dereference), then form the
     actual destination name by appending base_name (source) to the
     specified destination directory.  */
  if ((lstat_status == 0
       && S_ISDIR (dest_stats.st_mode))
#ifdef S_ISLNK
      || (dereference_dest_dir_symlinks
      && (lstat_status == 0
          && S_ISLNK (dest_stats.st_mode)
          && isdir (dest)))
#endif
     )
    {
      /* Target is a directory; build the full filename. */
      char *new_dest;
      PATH_BASENAME_CONCAT (new_dest, dest, source);
      dest = new_dest;

      /* Get stats for new DEST.  */
      lstat_status = lstat (dest, &dest_stats);
      if (lstat_status != 0 && errno != ENOENT)
    {
      error (0, errno, _("accessing %s"), quote (dest));
      return 1;
    }
    }

  /* If --force (-f) has been specified without --backup, then before
     making a link ln must remove the destination file if it exists.
     (with --backup, it just renames any existing destination file)
     But if the source and destination are the same, don't remove
     anything and fail right here.  */
  if (remove_existing_files
      && lstat_status == 0
      /* Allow `ln -sf --backup k k' to succeed in creating the
     self-referential symlink, but don't allow the hard-linking
     equivalent: `ln -f k k' (with or without --backup) to get
     beyond this point, because the error message you'd get is
     misleading.  */
      && (backup_type == none || !symbolic_link)
      && (!symbolic_link || stat (source, &source_stats) == 0)
      && source_stats.st_dev == dest_stats.st_dev
      && source_stats.st_ino == dest_stats.st_ino
      /* The following detects whether removing DEST will also remove
     SOURCE.  If the file has only one link then both are surely
     the same link.  Otherwise check whether they point to the same
     name in the same directory.  */
      && (source_stats.st_nlink == 1 || same_name (source, dest)))
    {
      error (0, 0, _("%s and %s are the same file"),
         quote_n (0, source), quote_n (1, dest));
      return 1;
    }

  if (lstat_status == 0 || lstat (dest, &dest_stats) == 0)
    {
      if (S_ISDIR (dest_stats.st_mode))
    {
      error (0, 0, _("%s: cannot overwrite directory"), quote (dest));
      return 1;
    }
      if (interactive)
    {
      fprintf (stderr, _("%s: replace %s? "), program_name, quote (dest));
      if (!yesno ())
        return 0;
    }
      else if (!remove_existing_files && backup_type == none)
    {
      error (0, 0, _("%s: File exists"), quote (dest));
      return 1;
    }

      if (backup_type != none)
    {
      char *tmp_backup = find_backup_file_name (dest, backup_type);
      if (tmp_backup == NULL)
        xalloc_die ();
      dest_backup = (char *) alloca (strlen (tmp_backup) + 1);
      strcpy (dest_backup, tmp_backup);
      free (tmp_backup);
      if (rename (dest, dest_backup))
        {
          if (errno != ENOENT)
        {
          error (0, errno, _("cannot backup %s"), quote (dest));
          return 1;
        }
          else
        dest_backup = NULL;
        }
      else
        {
          backup_succeeded = 1;
        }
    }

      /* Try to unlink DEST even if we may have renamed it.  In some unusual
     cases (when DEST and DEST_BACKUP are hard-links that refer to the
     same file), rename succeeds and DEST remains.  If we didn't remove
     DEST in that case, the subsequent LINKFUNC call would fail.  */
      if (unlink (dest) && errno != ENOENT)
    {
      error (0, errno, _("cannot remove %s"), quote (dest));
      return 1;
    }
    }
  else if (errno != ENOENT)
    {
      error (0, errno, _("accessing %s"), quote (dest));
      return 1;
    }

  if (verbose)
    {
      printf ((symbolic_link
           ? _("create symbolic link %s to %s")
           : _("create hard link %s to %s")),
          quote_n (0, dest), quote_n (1, source));
      if (backup_succeeded)
    printf (_(" (backup: %s)"), quote (dest_backup));
      putchar ('\n');
    }

  if ((*linkfunc) (source, dest) == 0)
    {
      return 0;
    }

  error (0, errno,
     (symbolic_link
      ? _("creating symbolic link %s to %s")
      : _("creating hard link %s to %s")),
     quote_n (0, dest), quote_n (1, source));

  if (dest_backup)
    {
      if (rename (dest_backup, dest))
    error (0, errno, _("cannot un-backup %s"), quote (dest));
    }
  return 1;
}

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... TARGET [LINK_NAME]\n\
  or:  %s [OPTION]... TARGET... DIRECTORY\n\
  or:  %s [OPTION]... --target-directory=DIRECTORY TARGET...\n\
"),
          program_name, program_name, program_name);
      fputs (_("\
Create a link to the specified TARGET with optional LINK_NAME.\n\
If LINK_NAME is omitted, a link with the same basename as the TARGET is\n\
created in the current directory.  When using the second form with more\n\
than one TARGET, the last argument must be a directory;  create links\n\
in DIRECTORY to each TARGET.  Create hard links by default, symbolic\n\
links with --symbolic.  When creating hard links, each TARGET must exist.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
      --backup[=CONTROL]      make a backup of each existing destination file\n\
  -b                          like --backup but does not accept an argument\n\
  -d, -F, --directory         hard link directories (super-user only)\n\
  -f, --force                 remove existing destination files\n\
"), stdout);
      fputs (_("\
  -n, --no-dereference        treat destination that is a symlink to a\n\
                                directory as if it were a normal file\n\
  -i, --interactive           prompt whether to remove destinations\n\
  -s, --symbolic              make symbolic links instead of hard links\n\
"), stdout);
      fputs (_("\
  -S, --suffix=SUFFIX         override the usual backup suffix\n\
      --target-directory=DIRECTORY  specify the DIRECTORY in which to create\n\
                                the links\n\
  -v, --verbose               print name of each file before linking\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
The version control method may be selected via the --backup option or through\n\
the VERSION_CONTROL environment variable.  Here are the values:\n\
\n\
"), stdout);
      fputs (_("\
  none, off       never make backups (even if --backup is given)\n\
  numbered, t     make numbered backups\n\
  existing, nil   numbered if numbered backups exist, simple otherwise\n\
  simple, never   always make simple backups\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  int c;
  int errors;
  int make_backups = 0;
  char *backup_suffix_string;
  char *version_control_string = NULL;
  char *target_directory = NULL;
  int target_directory_specified;
  unsigned int n_files;
  char **file;
  int dest_is_dir;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
     we'll actually use backup_suffix_string.  */
  backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");

  symbolic_link = remove_existing_files = interactive = verbose
    = hard_dir_link = 0;
  errors = 0;

  while ((c = getopt_long (argc, argv, "bdfinsvFS:V:", long_options, NULL))
     != -1)
    {
      switch (c)
    {
    case 0:         /* Long-named option. */
      break;

    case 'V':  /* FIXME: this is deprecated.  Remove it in 2001.  */
      error (0, 0,
         _("warning: --version-control (-V) is obsolete;  support for\
 it\nwill be removed in some future release.  Use --backup=%s instead."
           ), optarg);
      /* Fall through.  */

    case 'b':
      make_backups = 1;
      if (optarg)
        version_control_string = optarg;
      break;
    case 'd':
    case 'F':
      hard_dir_link = 1;
      break;
    case 'f':
      remove_existing_files = 1;
      interactive = 0;
      break;
    case 'i':
      remove_existing_files = 0;
      interactive = 1;
      break;
    case 'n':
      dereference_dest_dir_symlinks = 0;
      break;
    case 's':
#ifdef S_ISLNK
      symbolic_link = 1;
#else
      error (EXIT_FAILURE, 0,
         _("symbolic links are not supported on this system"));
#endif
      break;
    case TARGET_DIRECTORY_OPTION:
      target_directory = optarg;
      break;
    case 'v':
      verbose = 1;
      break;
    case 'S':
      make_backups = 1;
      backup_suffix_string = optarg;
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (EXIT_FAILURE);
      break;
    }
    }

  n_files = argc - optind;
  file = argv + optind;

  if (n_files == 0)
    {
      error (0, 0, _("missing file argument"));
      usage (EXIT_FAILURE);
    }

  target_directory_specified = (target_directory != NULL);
  if (!target_directory)
    target_directory = file[n_files - 1];

  /* If target directory is not specified, and there's only one
     file argument, then pretend `.' was given as the second argument.  */
  if (!target_directory_specified && n_files == 1)
    {
      static char *dummy[2];
      dummy[0] = file[0];
      dummy[1] = ".";
      file = dummy;
      n_files = 2;
      dest_is_dir = 1;
    }
  else
    {
      dest_is_dir = isdir (target_directory);
    }

  if (symbolic_link)
    linkfunc = symlink;
  else
    linkfunc = link;

  if (target_directory_specified && !dest_is_dir)
    {
      error (0, 0, _("%s: specified target directory is not a directory"),
         quote (target_directory));
      usage (EXIT_FAILURE);
    }

  if (backup_suffix_string)
    simple_backup_suffix = xstrdup (backup_suffix_string);

  backup_type = (make_backups
         ? xget_version (_("backup type"), version_control_string)
         : none);

  if (target_directory_specified || n_files > 2)
    {
      unsigned int i;
      unsigned int last_file_idx = (target_directory_specified
                    ? n_files - 1
                    : n_files - 2);

      if (!target_directory_specified && !dest_is_dir)
    error (EXIT_FAILURE, 0,
       _("when making multiple links, last argument must be a directory"));
      for (i = 0; i <= last_file_idx; ++i)
    errors += do_link (file[i], target_directory);
    }
  else
    {
      struct stat source_stats;
      const char *source;
      char *dest;
      char *new_dest;

      source = file[0];
      dest = file[1];

      /* When the destination is specified with a trailing slash and the
     source exists but is not a directory, convert the user's command
     `ln source dest/' to `ln source dest/basename(source)'.  */

      if (dest[strlen (dest) - 1] == '/'
      && lstat (source, &source_stats) == 0
      && !S_ISDIR (source_stats.st_mode))
    {
      PATH_BASENAME_CONCAT (new_dest, dest, source);
    }
      else
    {
      new_dest = dest;
    }

      errors = do_link (source, new_dest);
    }

  exit (errors != 0);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/logname.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/logname.c used in: Step 891
/* logname -- print user's login name
   Copyright (C) 1990-1997, 1999-2002 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 <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>

#include "system.h"
#include "closeout.h"
#include "long-options.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "logname"

#define AUTHORS "FIXME: unknown"

/* The name this program was run with. */
char *program_name;

static struct option const long_options[] =
{
  {0, 0, 0, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]\n"), program_name);
      fputs (_("\
Print the name of the current user.\n\
\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  register char *cp;
  int c;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              AUTHORS, usage);

  while ((c = getopt_long (argc, argv, "", long_options, NULL)) != -1)
    {
      switch (c)
    {
    case 0:
      break;

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (argc - optind != 0)
    usage (EXIT_FAILURE);

  /* POSIX requires using getlogin (or equivalent code).  */
  cp = getlogin ();
  if (cp)
    {
      puts (cp);
      exit (EXIT_SUCCESS);
    }
  /* POSIX prohibits using a fallback technique.  */
  fprintf (stderr, _("%s: no login name\n"), argv[0]);
  exit (EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/mkfifo.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/mkfifo.c used in: Step 893
/* mkfifo -- make fifo's (named pipes)
   Copyright (C) 90, 91, 1995-2002 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.  */

/* David MacKenzie <djm@ai.mit.edu>  */

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>

#include "system.h"
#include "error.h"
#include "modechange.h"
#include "quote.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "mkfifo"

#define AUTHORS "David MacKenzie"

/* The name this program was run with. */
char *program_name;

static struct option const longopts[] =
{
  {"mode", required_argument, NULL, 'm'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

#ifdef S_ISFIFO
void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION] NAME...\n"), program_name);
      fputs (_("\
Create named pipes (FIFOs) with the given NAMEs.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -m, --mode=MODE   set permission mode (as in chmod), not a=rw - umask\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}
#endif

int
main (int argc, char **argv)
{
  mode_t newmode;
  struct mode_change *change;
  const char *specified_mode;
  int errors = 0;
  int optc;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  specified_mode = NULL;

#ifndef S_ISFIFO
  error (4, 0, _("fifo files not supported"));
#else
  while ((optc = getopt_long (argc, argv, "m:", longopts, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:
      break;
    case 'm':
      specified_mode = optarg;
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (EXIT_FAILURE);
    }
    }

  if (optind == argc)
    {
      error (0, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }

  newmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
  if (specified_mode)
    {
      newmode &= ~ umask (0);
      change = mode_compile (specified_mode, 0);
      if (change == MODE_INVALID)
    error (EXIT_FAILURE, 0, _("invalid mode"));
      else if (change == MODE_MEMORY_EXHAUSTED)
    xalloc_die ();
      newmode = mode_adjust (newmode, change);
    }

  for (; optind < argc; ++optind)
    {
      int fail = mkfifo (argv[optind], newmode);
      if (fail)
    error (0, errno, _("cannot create fifo %s"), quote (argv[optind]));

      /* If the containing directory happens to have a default ACL, chmod
     ensures the file mode permission bits are still set as desired.  */

      if (fail == 0 && specified_mode)
    {
      fail = chmod (argv[optind], newmode);
      if (fail)
        error (0, errno, _("cannot set permissions of fifo %s"),
           quote (argv[optind]));
    }

      if (fail)
    errors = 1;
    }

  exit (errors);
#endif
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/mkdir.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/mkdir.c used in: Step 895
/* mkdir -- make directories
   Copyright (C) 90, 1995-2002 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.  */

/* David MacKenzie <djm@ai.mit.edu>  */

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>

#include "system.h"
#include "dirname.h"
#include "error.h"
#include "makepath.h"
#include "modechange.h"
#include "quote.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "mkdir"

#define AUTHORS "David MacKenzie"

/* The name this program was run with. */
char *program_name;

/* If nonzero, ensure that all parents of the specified directory exist.  */
static int create_parents;

static struct option const longopts[] =
{
  {"mode", required_argument, NULL, 'm'},
  {"parents", no_argument, NULL, 'p'},
  {"verbose", no_argument, NULL, 'v'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION] DIRECTORY...\n"), program_name);
      fputs (_("\
Create the DIRECTORY(ies), if they do not already exist.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -m, --mode=MODE   set permission mode (as in chmod), not rwxrwxrwx - umask\n\
  -p, --parents     no error if existing, make parent directories as needed\n\
  -v, --verbose     print a message for each created directory\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  mode_t newmode;
  mode_t parent_mode;
  const char *specified_mode = NULL;
  const char *verbose_fmt_string = NULL;
  int errors = 0;
  int optc;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  create_parents = 0;

  while ((optc = getopt_long (argc, argv, "pm:v", longopts, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:         /* Long option. */
      break;
    case 'p':
      create_parents = 1;
      break;
    case 'm':
      specified_mode = optarg;
      break;
    case 'v': /* --verbose  */
      verbose_fmt_string = _("created directory %s");
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (EXIT_FAILURE);
    }
    }

  if (optind == argc)
    {
      error (0, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }

  newmode = S_IRWXUGO;
  {
    mode_t umask_value = umask (0);
    umask (umask_value);        /* Restore the old value. */
    parent_mode = (newmode & (~ umask_value)) | S_IWUSR | S_IXUSR;
  }

  if (specified_mode)
    {
      struct mode_change *change = mode_compile (specified_mode, 0);
      newmode &= ~ umask (0);
      if (change == MODE_INVALID)
    error (EXIT_FAILURE, 0, _("invalid mode %s"), quote (specified_mode));
      else if (change == MODE_MEMORY_EXHAUSTED)
    xalloc_die ();
      newmode = mode_adjust (newmode, change);
    }

  for (; optind < argc; ++optind)
    {
      int fail = 0;

      if (create_parents)
    {
      char *dir = argv[optind];
      fail = make_path (dir, newmode, parent_mode,
                -1, -1, 1, verbose_fmt_string);
    }
      else
    {
      const char *dir = argv[optind];
      int dir_created;
      fail = make_dir (dir, dir, newmode, &dir_created);
      if (fail)
        {
          /* make_dir already gave a diagnostic.  */
        }
      else if (!create_parents && !dir_created)
        {
          /* make_dir `succeeds' when DIR already exists.
         In that case, mkdir must fail, unless --parents (-p)
         was specified.  */
          error (0, EEXIST, _("cannot create directory %s"),
             quote (dir));
          fail = 1;
        }
      else if (verbose_fmt_string)
        error (0, 0, verbose_fmt_string, quote (dir));

      /* mkdir(2) is required to honor only the file permission bits.
         In particular, it needn't do anything about `special' bits,
         so if any were set in newmode, apply them with chmod.
         This extra step is necessary in some cases when the containing
         directory has a default ACL.  */

      /* Set the permissions only if this directory has just
         been created.  */

      if (fail == 0 && specified_mode && dir_created)
        {
          fail = chmod (dir, newmode);
          if (fail)
        error (0, errno, _("cannot set permissions of directory %s"),
               quote (dir));
        }
    }

      if (fail)
    errors = 1;
    }

  exit (errors);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/makepath.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/makepath.h used in: Step 895
#ifndef PARAMS
# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#  define PARAMS(Args) Args
# else
#  define PARAMS(Args) ()
# endif
#endif

int
make_path PARAMS ((const char *_argpath,
           int _mode,
           int _parent_mode,
           uid_t _owner,
           gid_t _group,
           int _preserve_existing,
           const char *_verbose_fmt_string));

int
make_dir PARAMS ((const char *dir,
          const char *dirpath,
          mode_t mode,
          int *created_dir_p));

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/mknod.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/mknod.c used in: Step 897
/* mknod -- make special files
   Copyright (C) 90, 91, 1995-2002 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.  */

/* Written by David MacKenzie <djm@ai.mit.edu>  */

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>

#include "system.h"
#include "error.h"
#include "modechange.h"
#include "quote.h"
#include "xstrtol.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "mknod"

#define AUTHORS "David MacKenzie"

/* The name this program was run with. */
char *program_name;

static struct option const longopts[] =
{
  {"mode", required_argument, NULL, 'm'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]... NAME TYPE [MAJOR MINOR]\n"),
          program_name);
      fputs (_("\
Create the special file NAME of the given TYPE.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -m, --mode=MODE   set permission mode (as in chmod), not a=rw - umask\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
Both MAJOR and MINOR must be specified when TYPE is b, c, or u, and they\n\
must be omitted when TYPE is p.  If MAJOR or MINOR begins with 0x or 0X,\n\
it is interpreted as hexadecimal; otherwise, if it begins with 0, as octal;\n\
otherwise, as decimal.  TYPE may be:\n\
"), stdout);
      fputs (_("\
\n\
  b      create a block (buffered) special file\n\
  c, u   create a character (unbuffered) special file\n\
  p      create a FIFO\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  mode_t newmode;
  struct mode_change *change;
  const char *specified_mode;
  int optc;
  mode_t node_type;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  specified_mode = NULL;

  while ((optc = getopt_long (argc, argv, "m:", longopts, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:
      break;
    case 'm':
      specified_mode = optarg;
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (EXIT_FAILURE);
    }
    }

  newmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
  if (specified_mode)
    {
      newmode &= ~ umask (0);
      change = mode_compile (specified_mode, 0);
      if (change == MODE_INVALID)
    error (EXIT_FAILURE, 0, _("invalid mode"));
      else if (change == MODE_MEMORY_EXHAUSTED)
    xalloc_die ();
      newmode = mode_adjust (newmode, change);
    }

  if (argc - optind != 2 && argc - optind != 4)
    {
      const char *msg;
      if (argc - optind < 2)
    msg = _("too few arguments");
      else if (argc - optind > 4)
    msg = _("too many arguments");
      else
    msg = _("wrong number of arguments");
      error (0, 0, "%s", msg);
      usage (EXIT_FAILURE);
    }

  /* Only check the first character, to allow mnemonic usage like
     `mknod /dev/rst0 character 18 0'. */

  switch (argv[optind + 1][0])
    {
    case 'b':           /* `block' or `buffered' */
#ifndef S_IFBLK
      error (4, 0, _("block special files not supported"));
#else
      node_type = S_IFBLK;
#endif
      goto block_or_character;

    case 'c':           /* `character' */
    case 'u':           /* `unbuffered' */
#ifndef S_IFCHR
      error (4, 0, _("character special files not supported"));
#else
      node_type = S_IFCHR;
#endif
      goto block_or_character;

    block_or_character:
      if (argc - optind != 4)
    {
      error (0, 0, _("\
when creating special files, major and minor device\n\
numbers must be specified"));
      usage (EXIT_FAILURE);
    }

      {
    char const *s_major = argv[optind + 2];
    char const *s_minor = argv[optind + 3];
    uintmax_t i_major, i_minor;
    dev_t device;

    if (xstrtoumax (s_major, NULL, 0, &i_major, NULL) != LONGINT_OK
        || i_major != (major_t) i_major)
      error (EXIT_FAILURE, 0,
         _("invalid major device number %s"), quote (s_major));

    if (xstrtoumax (s_minor, NULL, 0, &i_minor, NULL) != LONGINT_OK
        || i_minor != (minor_t) i_minor)
      error (EXIT_FAILURE, 0,
         _("invalid minor device number %s"), quote (s_minor));

    device = makedev (i_major, i_minor);
#ifdef NODEV
    if (device == NODEV)
      error (EXIT_FAILURE, 0, _("invalid device %s %s"), s_major, s_minor);
#endif

    if (mknod (argv[optind], newmode | node_type, device) != 0)
      error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
      }
      break;

    case 'p':           /* `pipe' */
#ifndef S_ISFIFO
      error (4, 0, _("fifo files not supported"));
#else
      if (argc - optind != 2)
    {
      error (0, 0, _("\
major and minor device numbers may not be specified for fifo files"));
      usage (EXIT_FAILURE);
    }
      if (mkfifo (argv[optind], newmode))
    error (EXIT_FAILURE, errno, "%s", quote (argv[optind]));
#endif
      break;

    default:
      error (0, 0, "invalid device type %s", quote (argv[optind + 1]));
      usage (EXIT_FAILURE);
    }

  /* Perform an explicit chmod to ensure the file mode permission bits
     are set as specified.  This extra step is necessary in some cases
     when the containing directory has a default ACL.  */

  if (specified_mode)
    {
      if (chmod (argv[optind], newmode))
        error (0, errno, _("cannot set permissions of %s"),
               quote (argv[optind]));
    }

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/nl.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/nl.c used in: Step 899
/* nl -- number lines of files
   Copyright (C) 89, 92, 1995-2002 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.  */


/* Written by Scott Bartram (nancy!scott@uunet.uu.net)
   Revised by David MacKenzie (djm@gnu.ai.mit.edu) */

#include <config.h>

#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>

#include "system.h"
#include "closeout.h"

#include <regex.h>

#include "error.h"
#include "linebuffer.h"
#include "xstrtol.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "nl"

#define AUTHORS N_ ("Scott Bartram and David MacKenzie")

#ifndef TRUE
# define TRUE   1
# define FALSE  0
#endif

/* Line-number formats. */
enum number_format
{
  FORMAT_RIGHT_NOLZ,        /* Right justified, no leading zeroes.  */
  FORMAT_RIGHT_LZ,      /* Right justified, leading zeroes.  */
  FORMAT_LEFT           /* Left justified, no leading zeroes.  */
};

/* Default section delimiter characters.  */
#define DEFAULT_SECTION_DELIMITERS  "\\:"

/* Types of input lines: either one of the section delimiters,
   or text to output. */
enum section
{
  Header, Body, Footer, Text
};

/* The name this program was run with. */
char *program_name;

/* Format of body lines (-b).  */
static char *body_type = "t";

/* Format of header lines (-h).  */
static char *header_type = "n";

/* Format of footer lines (-f).  */
static char *footer_type = "n";

/* Format currently being used (body, header, or footer).  */
static char *current_type;

/* Regex for body lines to number (-bp).  */
static struct re_pattern_buffer body_regex;

/* Regex for header lines to number (-hp).  */
static struct re_pattern_buffer header_regex;

/* Regex for footer lines to number (-fp).  */
static struct re_pattern_buffer footer_regex;

/* Pointer to current regex, if any.  */
static struct re_pattern_buffer *current_regex = NULL;

/* Separator string to print after line number (-s).  */
static char *separator_str = "\t";

/* Input section delimiter string (-d).  */
static char *section_del = DEFAULT_SECTION_DELIMITERS;

/* Header delimiter string.  */
static char *header_del = NULL;

/* Header section delimiter length.  */
static size_t header_del_len;

/* Body delimiter string.  */
static char *body_del = NULL;

/* Body section delimiter length.  */
static size_t body_del_len;

/* Footer delimiter string.  */
static char *footer_del = NULL;

/* Footer section delimiter length.  */
static size_t footer_del_len;

/* Input buffer.  */
static struct linebuffer line_buf;

/* printf format string for line number.  */
static char *print_fmt;

/* printf format string for unnumbered lines.  */
static char *print_no_line_fmt = NULL;

/* Starting line number on each page (-v).  */
static int starting_line_number = 1;

/* Line number increment (-i).  */
static int page_incr = 1;

/* If TRUE, reset line number at start of each page (-p).  */
static int reset_numbers = TRUE;

/* Number of blank lines to consider to be one line for numbering (-l).  */
static int blank_join = 1;

/* Width of line numbers (-w).  */
static int lineno_width = 6;

/* Line number format (-n).  */
static enum number_format lineno_format = FORMAT_RIGHT_NOLZ;

/* Current print line number.  */
static int line_no;

/* Nonzero if we have ever read standard input. */
static int have_read_stdin;

static struct option const longopts[] =
{
  {"header-numbering", required_argument, NULL, 'h'},
  {"body-numbering", required_argument, NULL, 'b'},
  {"footer-numbering", required_argument, NULL, 'f'},
  {"starting-line-number", required_argument, NULL, 'v'},
  {"page-increment", required_argument, NULL, 'i'},
  {"no-renumber", no_argument, NULL, 'p'},
  {"join-blank-lines", required_argument, NULL, 'l'},
  {"number-separator", required_argument, NULL, 's'},
  {"number-width", required_argument, NULL, 'w'},
  {"number-format", required_argument, NULL, 'n'},
  {"section-delimiter", required_argument, NULL, 'd'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

/* Print a usage message and quit. */

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
          program_name);
      fputs (_("\
Write each FILE to standard output, with line numbers added.\n\
With no FILE, or when FILE is -, read standard input.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -b, --body-numbering=STYLE      use STYLE for numbering body lines\n\
  -d, --section-delimiter=CC      use CC for separating logical pages\n\
  -f, --footer-numbering=STYLE    use STYLE for numbering footer lines\n\
"), stdout);
      fputs (_("\
  -h, --header-numbering=STYLE    use STYLE for numbering header lines\n\
  -i, --page-increment=NUMBER     line number increment at each line\n\
  -l, --join-blank-lines=NUMBER   group of NUMBER empty lines counted as one\n\
  -n, --number-format=FORMAT      insert line numbers according to FORMAT\n\
  -p, --no-renumber               do not reset line numbers at logical pages\n\
  -s, --number-separator=STRING   add STRING after (possible) line number\n\
"), stdout);
      fputs (_("\
  -v, --first-page=NUMBER         first line number on each logical page\n\
  -w, --number-width=NUMBER       use NUMBER columns for line numbers\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
By default, selects -v1 -i1 -l1 -sTAB -w6 -nrn -hn -bt -fn.  CC are\n\
two delimiter characters for separating logical pages, a missing\n\
second character implies :.  Type \\\\ for \\.  STYLE is one of:\n\
"), stdout);
      fputs (_("\
\n\
  a         number all lines\n\
  t         number only nonempty lines\n\
  n         number no lines\n\
  pREGEXP   number only lines that contain a match for REGEXP\n\
\n\
FORMAT is one of:\n\
\n\
  ln   left justified, no leading zeros\n\
  rn   right justified, no leading zeros\n\
  rz   right justified, leading zeros\n\
\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Build the printf format string, based on `lineno_format'. */

static void
build_print_fmt (void)
{
  print_fmt = xmalloc (  1 /* for `%' */
               + 1 /* for `-' or `0' */
               + INT_STRLEN_BOUND (lineno_width)
               + 1 /* for `d' */
               + 1 /* for trailing NUL byte */ );
  switch (lineno_format)
    {
    case FORMAT_RIGHT_NOLZ:
      sprintf (print_fmt, "%%%dd", lineno_width);
      break;
    case FORMAT_RIGHT_LZ:
      sprintf (print_fmt, "%%0%dd", lineno_width);
      break;
    case FORMAT_LEFT:
      sprintf (print_fmt, "%%-%dd", lineno_width);
      break;
    }
}

/* Set the command line flag TYPEP and possibly the regex pointer REGEXP,
   according to `optarg'.  */

static int
build_type_arg (char **typep, struct re_pattern_buffer *regexp)
{
  const char *errmsg;
  int rval = TRUE;
  int optlen;

  switch (*optarg)
    {
    case 'a':
    case 't':
    case 'n':
      *typep = optarg;
      break;
    case 'p':
      *typep = optarg++;
      optlen = strlen (optarg);
      regexp->allocated = optlen * 2;
      regexp->buffer = (unsigned char *) xmalloc (regexp->allocated);
      regexp->translate = NULL;
      regexp->fastmap = xmalloc (256);
      regexp->fastmap_accurate = 0;
      errmsg = re_compile_pattern (optarg, optlen, regexp);
      if (errmsg)
    error (EXIT_FAILURE, 0, "%s", errmsg);
      break;
    default:
      rval = FALSE;
      break;
    }
  return rval;
}

/* Print the line number and separator; increment the line number. */

static void
print_lineno (void)
{
  printf (print_fmt, line_no);
  fputs (separator_str, stdout);
  line_no += page_incr;
}

/* Switch to a header section. */

static void
proc_header (void)
{
  current_type = header_type;
  current_regex = &header_regex;
  if (reset_numbers)
    line_no = starting_line_number;
  putchar ('\n');
}

/* Switch to a body section. */

static void
proc_body (void)
{
  current_type = body_type;
  current_regex = &body_regex;
  putchar ('\n');
}

/* Switch to a footer section. */

static void
proc_footer (void)
{
  current_type = footer_type;
  current_regex = &footer_regex;
  putchar ('\n');
}

/* Process a regular text line in `line_buf'. */

static void
proc_text (void)
{
  static int blank_lines = 0;   /* Consecutive blank lines so far. */

  switch (*current_type)
    {
    case 'a':
      if (blank_join > 1)
    {
      if (1 < line_buf.length || ++blank_lines == blank_join)
        {
          print_lineno ();
          blank_lines = 0;
        }
      else
        puts (print_no_line_fmt);
    }
      else
    print_lineno ();
      break;
    case 't':
      if (1 < line_buf.length)
    print_lineno ();
      else
    puts (print_no_line_fmt);
      break;
    case 'n':
      puts (print_no_line_fmt);
      break;
    case 'p':
      if (re_search (current_regex, line_buf.buffer, line_buf.length - 1,
             0, line_buf.length - 1, (struct re_registers *) 0) < 0)
    puts (print_no_line_fmt);
      else
    print_lineno ();
      break;
    }
  fwrite (line_buf.buffer, sizeof (char), line_buf.length, stdout);
}

/* Return the type of line in `line_buf'. */

static enum section
check_section (void)
{
  size_t len = line_buf.length - 1;

  if (len < 2 || memcmp (line_buf.buffer, section_del, 2))
    return Text;
  if (len == header_del_len
      && !memcmp (line_buf.buffer, header_del, header_del_len))
    return Header;
  if (len == body_del_len
      && !memcmp (line_buf.buffer, body_del, body_del_len))
    return Body;
  if (len == footer_del_len
      && !memcmp (line_buf.buffer, footer_del, footer_del_len))
    return Footer;
  return Text;
}

/* Read and process the file pointed to by FP. */

static void
process_file (FILE *fp)
{
  while (readline (&line_buf, fp))
    {
      switch ((int) check_section ())
    {
    case Header:
      proc_header ();
      break;
    case Body:
      proc_body ();
      break;
    case Footer:
      proc_footer ();
      break;
    case Text:
      proc_text ();
      break;
    }
    }
}

/* Process file FILE to standard output.
   Return 0 if successful, 1 if not. */

static int
nl_file (const char *file)
{
  FILE *stream;

  if (STREQ (file, "-"))
    {
      have_read_stdin = 1;
      stream = stdin;
    }
  else
    {
      stream = fopen (file, "r");
      if (stream == NULL)
    {
      error (0, errno, "%s", file);
      return 1;
    }
    }

  process_file (stream);

  if (ferror (stream))
    {
      error (0, errno, "%s", file);
      return 1;
    }
  if (STREQ (file, "-"))
    clearerr (stream);      /* Also clear EOF. */
  else if (fclose (stream) == EOF)
    {
      error (0, errno, "%s", file);
      return 1;
    }
  return 0;
}

int
main (int argc, char **argv)
{
  int c, exit_status = 0;
  size_t len;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  have_read_stdin = 0;

  while ((c = getopt_long (argc, argv, "h:b:f:v:i:pl:s:w:n:d:", longopts,
               NULL)) != -1)
    {
      switch (c)
    {
    case 0:
      break;

    case 'h':
      if (build_type_arg (&header_type, &header_regex) != TRUE)
        usage (2);
      break;
    case 'b':
      if (build_type_arg (&body_type, &body_regex) != TRUE)
        usage (2);
      break;
    case 'f':
      if (build_type_arg (&footer_type, &footer_regex) != TRUE)
        usage (2);
      break;
    case 'v':
      {
        long int tmp_long;
        if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
        /* Allow it to be negative.  */
        || tmp_long > INT_MAX)
          error (EXIT_FAILURE, 0, _("invalid starting line number: `%s'"),
             optarg);
        starting_line_number = (int) tmp_long;
      }
      break;
    case 'i':
      {
        long int tmp_long;
        if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
        || tmp_long <= 0 || tmp_long > INT_MAX)
          error (EXIT_FAILURE, 0, _("invalid line number increment: `%s'"),
             optarg);
        page_incr = (int) tmp_long;
      }
      break;
    case 'p':
      reset_numbers = FALSE;
      break;
    case 'l':
      {
        long int tmp_long;
        if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
        || tmp_long <= 0 || tmp_long > INT_MAX)
          error (EXIT_FAILURE, 0, _("invalid number of blank lines: `%s'"),
             optarg);
        blank_join = (int) tmp_long;
      }
      break;
    case 's':
      separator_str = optarg;
      break;
    case 'w':
      {
        long int tmp_long;
        if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
        || tmp_long <= 0 || tmp_long > INT_MAX)
          error (EXIT_FAILURE, 0,
             _("invalid line number field width: `%s'"),
             optarg);
        lineno_width = (int) tmp_long;
      }
      break;
    case 'n':
      switch (*optarg)
        {
        case 'l':
          if (optarg[1] == 'n')
        lineno_format = FORMAT_LEFT;
          else
        usage (2);
          break;
        case 'r':
          switch (optarg[1])
        {
        case 'n':
          lineno_format = FORMAT_RIGHT_NOLZ;
          break;
        case 'z':
          lineno_format = FORMAT_RIGHT_LZ;
          break;
        default:
          usage (2);
          break;
        }
          break;
        default:
          usage (2);
          break;
        }
      break;
    case 'd':
      section_del = optarg;
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (2);
      break;
    }
    }

  /* Initialize the section delimiters.  */
  len = strlen (section_del);

  header_del_len = len * 3;
  header_del = xmalloc (header_del_len + 1);
  strcat (strcat (strcpy (header_del, section_del), section_del), section_del);

  body_del_len = len * 2;
  body_del = xmalloc (body_del_len + 1);
  strcat (strcpy (body_del, section_del), section_del);

  footer_del_len = len;
  footer_del = xmalloc (footer_del_len + 1);
  strcpy (footer_del, section_del);

  /* Initialize the input buffer.  */
  initbuffer (&line_buf);

  /* Initialize the printf format for unnumbered lines. */
  len = strlen (separator_str);
  print_no_line_fmt = xmalloc (lineno_width + len + 1);
  memset (print_no_line_fmt, ' ', lineno_width + len);
  print_no_line_fmt[lineno_width + len] = '\0';

  line_no = starting_line_number;
  current_type = body_type;
  current_regex = &body_regex;
  build_print_fmt ();

  /* Main processing. */

  if (optind == argc)
    exit_status |= nl_file ("-");
  else
    for (; optind < argc; optind++)
      exit_status |= nl_file (argv[optind]);

  if (have_read_stdin && fclose (stdin) == EOF)
    {
      error (0, errno, "-");
      exit_status = 1;
    }

  exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/od.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/od.c used in: Step 901
/* od -- dump files in octal and other formats
   Copyright (C) 92, 1995-2002 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.  */

/* Written by Jim Meyering.  */

#include <config.h>

#include <stdio.h>
#include <assert.h>
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
#include "closeout.h"
#include "error.h"
#include "posixver.h"
#include "xstrtol.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "od"

#define AUTHORS "Jim Meyering"

#if defined(__GNUC__) || defined(STDC_HEADERS)
# include <float.h>
#endif

#ifdef HAVE_LONG_DOUBLE
typedef long double LONG_DOUBLE;
#else
typedef double LONG_DOUBLE;
#endif

/* The default number of input bytes per output line.  */
#define DEFAULT_BYTES_PER_BLOCK 16

/* The number of decimal digits of precision in a float.  */
#ifndef FLT_DIG
# define FLT_DIG 7
#endif

/* The number of decimal digits of precision in a double.  */
#ifndef DBL_DIG
# define DBL_DIG 15
#endif

/* The number of decimal digits of precision in a long double.  */
#ifndef LDBL_DIG
# define LDBL_DIG DBL_DIG
#endif

#if HAVE_UNSIGNED_LONG_LONG
typedef unsigned long long ulonglong_t;
#else
/* This is just a place-holder to avoid a few `#if' directives.
   In this case, the type isn't actually used.  */
typedef unsigned long int ulonglong_t;
#endif

enum size_spec
  {
    NO_SIZE,
    CHAR,
    SHORT,
    INT,
    LONG,
    LONG_LONG,
    /* FIXME: add INTMAX support, too */
    FLOAT_SINGLE,
    FLOAT_DOUBLE,
    FLOAT_LONG_DOUBLE,
    N_SIZE_SPECS
  };

enum output_format
  {
    SIGNED_DECIMAL,
    UNSIGNED_DECIMAL,
    OCTAL,
    HEXADECIMAL,
    FLOATING_POINT,
    NAMED_CHARACTER,
    CHARACTER
  };

/* Each output format specification (from `-t spec' or from
   old-style options) is represented by one of these structures.  */
struct tspec
  {
    enum output_format fmt;
    enum size_spec size;
    void (*print_function) (size_t, const char *, const char *);
    char *fmt_string;
    int hexl_mode_trailer;
    int field_width;
  };

/* The name this program was run with.  */
char *program_name;

/* Convert the number of 8-bit bytes of a binary representation to
   the number of characters (digits + sign if the type is signed)
   required to represent the same quantity in the specified base/type.
   For example, a 32-bit (4-byte) quantity may require a field width
   as wide as the following for these types:
   11   unsigned octal
   11   signed decimal
   10   unsigned decimal
   8    unsigned hexadecimal  */

static const unsigned int bytes_to_oct_digits[] =
{0, 3, 6, 8, 11, 14, 16, 19, 22, 25, 27, 30, 32, 35, 38, 41, 43};

static const unsigned int bytes_to_signed_dec_digits[] =
{1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, 33, 35, 37, 40};

static const unsigned int bytes_to_unsigned_dec_digits[] =
{0, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 27, 29, 32, 34, 37, 39};

static const unsigned int bytes_to_hex_digits[] =
{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32};

/* Convert enum size_spec to the size of the named type.  */
static const int width_bytes[] =
{
  -1,
  sizeof (char),
  sizeof (short int),
  sizeof (int),
  sizeof (long int),
  sizeof (ulonglong_t),
  sizeof (float),
  sizeof (double),
  sizeof (LONG_DOUBLE)
};

/* Ensure that for each member of `enum size_spec' there is an
   initializer in the width_bytes array.  */
struct dummy
{
  int assert_width_bytes_matches_size_spec_decl
    [sizeof width_bytes / sizeof width_bytes[0] == N_SIZE_SPECS ? 1 : -1];
};

/* Names for some non-printing characters.  */
static const char *const charname[33] =
{
  "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
  "bs", "ht", "nl", "vt", "ff", "cr", "so", "si",
  "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
  "can", "em", "sub", "esc", "fs", "gs", "rs", "us",
  "sp"
};

/* Address base (8, 10 or 16).  */
static int address_base;

/* The number of octal digits required to represent the largest
   address value.  */
#define MAX_ADDRESS_LENGTH \
  ((sizeof (uintmax_t) * CHAR_BIT + CHAR_BIT - 1) / 3)

/* Width of a normal address.  */
static int address_pad_len;

static size_t string_min;
static int flag_dump_strings;

/* Non-zero if we should recognize the older non-option arguments
   that specified at most one file and optional arguments specifying
   offset and pseudo-start address.  */
static int traditional;

/* Non-zero if an old-style `pseudo-address' was specified.  */
static int flag_pseudo_start;

/* The difference between the old-style pseudo starting address and
   the number of bytes to skip.  */
static uintmax_t pseudo_offset;

/* Function that accepts an address and an optional following char,
   and prints the address and char to stdout.  */
static void (*format_address) (uintmax_t, char);

/* The number of input bytes to skip before formatting and writing.  */
static uintmax_t n_bytes_to_skip = 0;

/* When zero, MAX_BYTES_TO_FORMAT and END_OFFSET are ignored, and all
   input is formatted.  */
static int limit_bytes_to_format = 0;

/* The maximum number of bytes that will be formatted.  */
static uintmax_t max_bytes_to_format;

/* The offset of the first byte after the last byte to be formatted.  */
static uintmax_t end_offset;

/* When nonzero and two or more consecutive blocks are equal, format
   only the first block and output an asterisk alone on the following
   line to indicate that identical blocks have been elided.  */
static int abbreviate_duplicate_blocks = 1;

/* An array of specs describing how to format each input block.  */
static struct tspec *spec;

/* The number of format specs.  */
static size_t n_specs;

/* The allocated length of SPEC.  */
static size_t n_specs_allocated;

/* The number of input bytes formatted per output line.  It must be
   a multiple of the least common multiple of the sizes associated with
   the specified output types.  It should be as large as possible, but
   no larger than 16 -- unless specified with the -w option.  */
static size_t bytes_per_block;

/* Human-readable representation of *file_list (for error messages).
   It differs from *file_list only when *file_list is "-".  */
static char const *input_filename;

/* A NULL-terminated list of the file-arguments from the command line.  */
static char const *const *file_list;

/* Initializer for file_list if no file-arguments
   were specified on the command line.  */
static char const *const default_file_list[] = {"-", NULL};

/* The input stream associated with the current file.  */
static FILE *in_stream;

/* If nonzero, at least one of the files we read was standard input.  */
static int have_read_stdin;

#define MAX_INTEGRAL_TYPE_SIZE sizeof (ulonglong_t)
static enum size_spec integral_type_size[MAX_INTEGRAL_TYPE_SIZE + 1];

#define MAX_FP_TYPE_SIZE sizeof(LONG_DOUBLE)
static enum size_spec fp_type_size[MAX_FP_TYPE_SIZE + 1];

#define COMMON_SHORT_OPTIONS "A:N:abcdfhij:lot:vx"

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  TRADITIONAL_OPTION = CHAR_MAX + 1
};

static struct option const long_options[] =
{
  {"skip-bytes", required_argument, NULL, 'j'},
  {"address-radix", required_argument, NULL, 'A'},
  {"read-bytes", required_argument, NULL, 'N'},
  {"format", required_argument, NULL, 't'},
  {"output-duplicates", no_argument, NULL, 'v'},
  {"strings", optional_argument, NULL, 's'},
  {"traditional", no_argument, NULL, TRADITIONAL_OPTION},
  {"width", optional_argument, NULL, 'w'},

  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
  or:  %s --traditional [FILE] [[+]OFFSET [[+]LABEL]]\n\
"),
          program_name, program_name);
      fputs (_("\n\
Write an unambiguous representation, octal bytes by default,\n\
of FILE to standard output.  With more than one FILE argument,\n\
concatenate them in the listed order to form the input.\n\
With no FILE, or when FILE is -, read standard input.\n\
\n\
"), stdout);
      fputs (_("\
All arguments to long options are mandatory for short options.\n\
"), stdout);
      fputs (_("\
  -A, --address-radix=RADIX   decide how file offsets are printed\n\
  -j, --skip-bytes=BYTES      skip BYTES input bytes first\n\
"), stdout);
      fputs (_("\
  -N, --read-bytes=BYTES      limit dump to BYTES input bytes\n\
  -s, --strings[=BYTES]       output strings of at least BYTES graphic chars\n\
  -t, --format=TYPE           select output format or formats\n\
  -v, --output-duplicates     do not use * to mark line suppression\n\
  -w, --width[=BYTES]         output BYTES bytes per output line\n\
      --traditional           accept arguments in traditional form\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
Traditional format specifications may be intermixed; they accumulate:\n\
  -a   same as -t a,  select named characters\n\
  -b   same as -t oC, select octal bytes\n\
  -c   same as -t c,  select ASCII characters or backslash escapes\n\
  -d   same as -t u2, select unsigned decimal shorts\n\
"), stdout);
      fputs (_("\
  -f   same as -t fF, select floats\n\
  -h   same as -t x2, select hexadecimal shorts\n\
  -i   same as -t d2, select decimal shorts\n\
  -l   same as -t d4, select decimal longs\n\
  -o   same as -t o2, select octal shorts\n\
  -x   same as -t x2, select hexadecimal shorts\n\
"), stdout);
      fputs (_("\
\n\
For older syntax (second call format), OFFSET means -j OFFSET.  LABEL\n\
is the pseudo-address at first byte printed, incremented when dump is\n\
progressing.  For OFFSET and LABEL, a 0x or 0X prefix indicates\n\
hexadecimal, suffixes may be . for octal and b for multiply by 512.\n\
\n\
TYPE is made up of one or more of these specifications:\n\
\n\
  a          named character\n\
  c          ASCII character or backslash escape\n\
"), stdout);
      fputs (_("\
  d[SIZE]    signed decimal, SIZE bytes per integer\n\
  f[SIZE]    floating point, SIZE bytes per integer\n\
  o[SIZE]    octal, SIZE bytes per integer\n\
  u[SIZE]    unsigned decimal, SIZE bytes per integer\n\
  x[SIZE]    hexadecimal, SIZE bytes per integer\n\
"), stdout);
      fputs (_("\
\n\
SIZE is a number.  For TYPE in doux, SIZE may also be C for\n\
sizeof(char), S for sizeof(short), I for sizeof(int) or L for\n\
sizeof(long).  If TYPE is f, SIZE may also be F for sizeof(float), D\n\
for sizeof(double) or L for sizeof(long double).\n\
"), stdout);
      fputs (_("\
\n\
RADIX is d for decimal, o for octal, x for hexadecimal or n for none.\n\
BYTES is hexadecimal with 0x or 0X prefix, it is multiplied by 512\n\
with b suffix, by 1024 with k and by 1048576 with m.  Adding a z suffix to\n\
any type adds a display of printable characters to the end of each line\n\
of output.  \
"), stdout);
      fputs (_("\
--string without a number implies 3.  --width without a number\n\
implies 32.  By default, od uses -A o -t d2 -w 16.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Compute the greatest common denominator of U and V
   using Euclid's algorithm.  */

static unsigned int
gcd (unsigned int u, unsigned int v)
{
  unsigned int t;
  while (v != 0)
    {
      t = u % v;
      u = v;
      v = t;
    }
  return u;
}

/* Compute the least common multiple of U and V.  */

static unsigned int
lcm (unsigned int u, unsigned int v)
{
  unsigned int t = gcd (u, v);
  if (t == 0)
    return 0;
  return u * v / t;
}

static void
print_s_char (size_t n_bytes, const char *block, const char *fmt_string)
{
  size_t i;
  for (i = n_bytes; i > 0; i--)
    {
      int tmp = (unsigned) *(const unsigned char *) block;
      if (tmp > SCHAR_MAX)
    tmp -= SCHAR_MAX - SCHAR_MIN + 1;
      assert (tmp <= SCHAR_MAX);
      printf (fmt_string, tmp);
      block += sizeof (unsigned char);
    }
}

static void
print_char (size_t n_bytes, const char *block, const char *fmt_string)
{
  size_t i;
  for (i = n_bytes; i > 0; i--)
    {
      unsigned int tmp = *(const unsigned char *) block;
      printf (fmt_string, tmp);
      block += sizeof (unsigned char);
    }
}

static void
print_s_short (size_t n_bytes, const char *block, const char *fmt_string)
{
  size_t i;
  for (i = n_bytes / sizeof (unsigned short); i > 0; i--)
    {
      int tmp = (unsigned) *(const unsigned short *) block;
      if (tmp > SHRT_MAX)
    tmp -= SHRT_MAX - SHRT_MIN + 1;
      assert (tmp <= SHRT_MAX);
      printf (fmt_string, tmp);
      block += sizeof (unsigned short);
    }
}

static void
print_short (size_t n_bytes, const char *block, const char *fmt_string)
{
  size_t i;
  for (i = n_bytes / sizeof (unsigned short); i > 0; i--)
    {
      unsigned int tmp = *(const unsigned short *) block;
      printf (fmt_string, tmp);
      block += sizeof (unsigned short);
    }
}

static void
print_int (size_t n_bytes, const char *block, const char *fmt_string)
{
  size_t i;
  for (i = n_bytes / sizeof (unsigned int); i > 0; i--)
    {
      unsigned int tmp = *(const unsigned int *) block;
      printf (fmt_string, tmp);
      block += sizeof (unsigned int);
    }
}

static void
print_long (size_t n_bytes, const char *block, const char *fmt_string)
{
  size_t i;
  for (i = n_bytes / sizeof (unsigned long); i > 0; i--)
    {
      unsigned long tmp = *(const unsigned long *) block;
      printf (fmt_string, tmp);
      block += sizeof (unsigned long);
    }
}

static void
print_long_long (size_t n_bytes, const char *block, const char *fmt_string)
{
  size_t i;
  for (i = n_bytes / sizeof (ulonglong_t); i > 0; i--)
    {
      ulonglong_t tmp = *(const ulonglong_t *) block;
      printf (fmt_string, tmp);
      block += sizeof (ulonglong_t);
    }
}

static void
print_float (size_t n_bytes, const char *block, const char *fmt_string)
{
  size_t i;
  for (i = n_bytes / sizeof (float); i > 0; i--)
    {
      float tmp = *(const float *) block;
      printf (fmt_string, tmp);
      block += sizeof (float);
    }
}

static void
print_double (size_t n_bytes, const char *block, const char *fmt_string)
{
  size_t i;
  for (i = n_bytes / sizeof (double); i > 0; i--)
    {
      double tmp = *(const double *) block;
      printf (fmt_string, tmp);
      block += sizeof (double);
    }
}

#ifdef HAVE_LONG_DOUBLE
static void
print_long_double (size_t n_bytes, const char *block, const char *fmt_string)
{
  size_t i;
  for (i = n_bytes / sizeof (LONG_DOUBLE); i > 0; i--)
    {
      LONG_DOUBLE tmp = *(const LONG_DOUBLE *) block;
      printf (fmt_string, tmp);
      block += sizeof (LONG_DOUBLE);
    }
}

#endif

static void
dump_hexl_mode_trailer (size_t n_bytes, const char *block)
{
  size_t i;
  fputs ("  >", stdout);
  for (i = n_bytes; i > 0; i--)
    {
      unsigned int c = *(const unsigned char *) block;
      unsigned int c2 = (ISPRINT(c) ? c : '.');
      putchar (c2);
      block += sizeof (unsigned char);
    }
  putchar ('<');
}

static void
print_named_ascii (size_t n_bytes, const char *block,
           const char *unused_fmt_string ATTRIBUTE_UNUSED)
{
  size_t i;
  for (i = n_bytes; i > 0; i--)
    {
      unsigned int c = *(const unsigned char *) block;
      unsigned int masked_c = (0x7f & c);
      const char *s;
      char buf[5];

      if (masked_c == 127)
    s = "del";
      else if (masked_c <= 040)
    s = charname[masked_c];
      else
    {
      sprintf (buf, "  %c", masked_c);
      s = buf;
    }

      printf (" %3s", s);
      block += sizeof (unsigned char);
    }
}

static void
print_ascii (size_t n_bytes, const char *block,
         const char *unused_fmt_string ATTRIBUTE_UNUSED)
{
  size_t i;
  for (i = n_bytes; i > 0; i--)
    {
      unsigned int c = *(const unsigned char *) block;
      const char *s;
      char buf[5];

      switch (c)
    {
    case '\0':
      s = " \\0";
      break;

    case '\007':
      s = " \\a";
      break;

    case '\b':
      s = " \\b";
      break;

    case '\f':
      s = " \\f";
      break;

    case '\n':
      s = " \\n";
      break;

    case '\r':
      s = " \\r";
      break;

    case '\t':
      s = " \\t";
      break;

    case '\v':
      s = " \\v";
      break;

    default:
      sprintf (buf, (ISPRINT (c) ? "  %c" : "%03o"), c);
      s = (const char *) buf;
    }

      printf (" %3s", s);
      block += sizeof (unsigned char);
    }
}

/* Convert a null-terminated (possibly zero-length) string S to an
   unsigned long integer value.  If S points to a non-digit set *P to S,
   *VAL to 0, and return 0.  Otherwise, accumulate the integer value of
   the string of digits.  If the string of digits represents a value
   larger than ULONG_MAX, don't modify *VAL or *P and return nonzero.
   Otherwise, advance *P to the first non-digit after S, set *VAL to
   the result of the conversion and return zero.  */

static int
simple_strtoul (const char *s, const char **p, long unsigned int *val)
{
  unsigned long int sum;

  sum = 0;
  while (ISDIGIT (*s))
    {
      unsigned int c = *s++ - '0';
      if (sum > (ULONG_MAX - c) / 10)
    return 1;
      sum = sum * 10 + c;
    }
  *p = s;
  *val = sum;
  return 0;
}

/* If S points to a single valid modern od format string, put
   a description of that format in *TSPEC, make *NEXT point at the
   character following the just-decoded format (if *NEXT is non-NULL),
   and return zero.  If S is not valid, don't modify *NEXT or *TSPEC,
   give a diagnostic, and return nonzero.  For example, if S were
   "d4afL" *NEXT would be set to "afL" and *TSPEC would be
     {
       fmt = SIGNED_DECIMAL;
       size = INT or LONG; (whichever integral_type_size[4] resolves to)
       print_function = print_int; (assuming size == INT)
       fmt_string = "%011d%c";
      }
   S_ORIG is solely for reporting errors.  It should be the full format
   string argument.
   */

static int
decode_one_format (const char *s_orig, const char *s, const char **next,
           struct tspec *tspec)
{
  enum size_spec size_spec;
  unsigned long int size;
  enum output_format fmt;
  const char *pre_fmt_string;
  char *fmt_string;
  void (*print_function) (size_t, const char *, const char *);
  const char *p;
  unsigned int c;
  unsigned int field_width = 0;

  assert (tspec != NULL);

  switch (*s)
    {
    case 'd':
    case 'o':
    case 'u':
    case 'x':
      c = *s;
      ++s;
      switch (*s)
    {
    case 'C':
      ++s;
      size = sizeof (char);
      break;

    case 'S':
      ++s;
      size = sizeof (short);
      break;

    case 'I':
      ++s;
      size = sizeof (int);
      break;

    case 'L':
      ++s;
      size = sizeof (long int);
      break;

    default:
      if (simple_strtoul (s, &p, &size) != 0)
        {
          /* The integer at P in S would overflow an unsigned long.
         A digit string that long is sufficiently odd looking
         that the following diagnostic is sufficient.  */
          error (0, 0, _("invalid type string `%s'"), s_orig);
          return 1;
        }
      if (p == s)
        size = sizeof (int);
      else
        {
          if (MAX_INTEGRAL_TYPE_SIZE < size
          || integral_type_size[size] == NO_SIZE)
        {
          error (0, 0, _("invalid type string `%s';\n\
this system doesn't provide a %lu-byte integral type"), s_orig, size);
          return 1;
        }
          s = p;
        }
      break;
    }

#define ISPEC_TO_FORMAT(Spec, Min_format, Long_format, Max_format)  \
  ((Spec) == LONG_LONG ? (Max_format)                   \
   : ((Spec) == LONG ? (Long_format)                    \
      : (Min_format)))                          \

#define FMT_BYTES_ALLOCATED 9
      fmt_string = xmalloc (FMT_BYTES_ALLOCATED);

      size_spec = integral_type_size[size];

      switch (c)
    {
    case 'd':
      fmt = SIGNED_DECIMAL;
      sprintf (fmt_string, " %%%u%s",
           (field_width = bytes_to_signed_dec_digits[size]),
           ISPEC_TO_FORMAT (size_spec, "d", "ld", PRIdMAX));
      break;

    case 'o':
      fmt = OCTAL;
      sprintf (fmt_string, " %%0%u%s",
           (field_width = bytes_to_oct_digits[size]),
           ISPEC_TO_FORMAT (size_spec, "o", "lo", PRIoMAX));
      break;

    case 'u':
      fmt = UNSIGNED_DECIMAL;
      sprintf (fmt_string, " %%%u%s",
           (field_width = bytes_to_unsigned_dec_digits[size]),
           ISPEC_TO_FORMAT (size_spec, "u", "lu", PRIuMAX));
      break;

    case 'x':
      fmt = HEXADECIMAL;
      sprintf (fmt_string, " %%0%u%s",
           (field_width = bytes_to_hex_digits[size]),
           ISPEC_TO_FORMAT (size_spec, "x", "lx", PRIxMAX));
      break;

    default:
      abort ();
    }

      assert (strlen (fmt_string) < FMT_BYTES_ALLOCATED);

      switch (size_spec)
    {
    case CHAR:
      print_function = (fmt == SIGNED_DECIMAL
                ? print_s_char
                : print_char);
      break;

    case SHORT:
      print_function = (fmt == SIGNED_DECIMAL
                ? print_s_short
                : print_short);
      break;

    case INT:
      print_function = print_int;
      break;

    case LONG:
      print_function = print_long;
      break;

    case LONG_LONG:
      print_function = print_long_long;
      break;

    default:
      abort ();
    }
      break;

    case 'f':
      fmt = FLOATING_POINT;
      ++s;
      switch (*s)
    {
    case 'F':
      ++s;
      size = sizeof (float);
      break;

    case 'D':
      ++s;
      size = sizeof (double);
      break;

    case 'L':
      ++s;
      size = sizeof (LONG_DOUBLE);
      break;

    default:
      if (simple_strtoul (s, &p, &size) != 0)
        {
          /* The integer at P in S would overflow an unsigned long.
         A digit string that long is sufficiently odd looking
         that the following diagnostic is sufficient.  */
          error (0, 0, _("invalid type string `%s'"), s_orig);
          return 1;
        }
      if (p == s)
        size = sizeof (double);
      else
        {
          if (size > MAX_FP_TYPE_SIZE
          || fp_type_size[size] == NO_SIZE)
        {
          error (0, 0, _("invalid type string `%s';\n\
this system doesn't provide a %lu-byte floating point type"), s_orig, size);
          return 1;
        }
          s = p;
        }
      break;
    }
      size_spec = fp_type_size[size];

      switch (size_spec)
    {
    case FLOAT_SINGLE:
      print_function = print_float;
      /* Don't use %#e; not all systems support it.  */
      pre_fmt_string = " %%%d.%de";
      fmt_string = xmalloc (strlen (pre_fmt_string));
      sprintf (fmt_string, pre_fmt_string,
           (field_width = FLT_DIG + 8), FLT_DIG);
      break;

    case FLOAT_DOUBLE:
      print_function = print_double;
      pre_fmt_string = " %%%d.%de";
      fmt_string = xmalloc (strlen (pre_fmt_string));
      sprintf (fmt_string, pre_fmt_string,
           (field_width = DBL_DIG + 8), DBL_DIG);
      break;

#ifdef HAVE_LONG_DOUBLE
    case FLOAT_LONG_DOUBLE:
      print_function = print_long_double;
      pre_fmt_string = " %%%d.%dLe";
      fmt_string = xmalloc (strlen (pre_fmt_string));
      sprintf (fmt_string, pre_fmt_string,
           (field_width = LDBL_DIG + 8), LDBL_DIG);
      break;
#endif

    default:
      abort ();
    }
      break;

    case 'a':
      ++s;
      fmt = NAMED_CHARACTER;
      size_spec = CHAR;
      fmt_string = NULL;
      print_function = print_named_ascii;
      field_width = 3;
      break;

    case 'c':
      ++s;
      fmt = CHARACTER;
      size_spec = CHAR;
      fmt_string = NULL;
      print_function = print_ascii;
      field_width = 3;
      break;

    default:
      error (0, 0, _("invalid character `%c' in type string `%s'"),
         *s, s_orig);
      return 1;
    }

  tspec->size = size_spec;
  tspec->fmt = fmt;
  tspec->print_function = print_function;
  tspec->fmt_string = fmt_string;

  tspec->field_width = field_width;
  tspec->hexl_mode_trailer = (*s == 'z');
  if (tspec->hexl_mode_trailer)
    s++;

  if (next != NULL)
    *next = s;

  return 0;
}

/* Given a list of one or more input filenames FILE_LIST, set the global
   file pointer IN_STREAM and the global string INPUT_FILENAME to the
   first one that can be successfully opened. Modify FILE_LIST to
   reference the next filename in the list.  A file name of "-" is
   interpreted as standard input.  If any file open fails, give an error
   message and return nonzero.  */

static int
open_next_file (void)
{
  int err = 0;

  do
    {
      input_filename = *file_list;
      if (input_filename == NULL)
    return err;
      ++file_list;

      if (STREQ (input_filename, "-"))
    {
      input_filename = _("standard input");
      in_stream = stdin;
      have_read_stdin = 1;
    }
      else
    {
      in_stream = fopen (input_filename, "r");
      if (in_stream == NULL)
        {
          error (0, errno, "%s", input_filename);
          err = 1;
        }
    }
    }
  while (in_stream == NULL);

  if (limit_bytes_to_format && !flag_dump_strings)
    SETVBUF (in_stream, NULL, _IONBF, 0);
  SET_BINARY (fileno (in_stream));

  return err;
}

/* Test whether there have been errors on in_stream, and close it if
   it is not standard input.  Return nonzero if there has been an error
   on in_stream or stdout; return zero otherwise.  This function will
   report more than one error only if both a read and a write error
   have occurred.  */

static int
check_and_close (void)
{
  int err = 0;

  if (in_stream != NULL)
    {
      if (ferror (in_stream))
    {
      error (0, errno, "%s", input_filename);
      if (in_stream != stdin)
        fclose (in_stream);
      err = 1;
    }
      else if (in_stream != stdin && fclose (in_stream) == EOF)
    {
      error (0, errno, "%s", input_filename);
      err = 1;
    }

      in_stream = NULL;
    }

  if (ferror (stdout))
    {
      error (0, errno, _("standard output"));
      err = 1;
    }

  return err;
}

/* Decode the modern od format string S.  Append the decoded
   representation to the global array SPEC, reallocating SPEC if
   necessary.  Return zero if S is valid, nonzero otherwise.  */

static int
decode_format_string (const char *s)
{
  const char *s_orig = s;
  assert (s != NULL);

  while (*s != '\0')
    {
      struct tspec tspec;
      const char *next;

      if (decode_one_format (s_orig, s, &next, &tspec))
    return 1;

      assert (s != next);
      s = next;

      if (n_specs >= n_specs_allocated)
    {
      n_specs_allocated = 1 + (3 * n_specs_allocated) / 2;
      spec = (struct tspec *) xrealloc ((char *) spec,
                        (n_specs_allocated
                         * sizeof (struct tspec)));
    }

      memcpy ((char *) &spec[n_specs], (char *) &tspec,
          sizeof (struct tspec));
      ++n_specs;
    }

  return 0;
}

/* Given a list of one or more input filenames FILE_LIST, set the global
   file pointer IN_STREAM to position N_SKIP in the concatenation of
   those files.  If any file operation fails or if there are fewer than
   N_SKIP bytes in the combined input, give an error message and return
   nonzero.  When possible, use seek rather than read operations to
   advance IN_STREAM.  */

static int
skip (uintmax_t n_skip)
{
  int err = 0;

  if (n_skip == 0)
    return 0;

  while (in_stream != NULL) /* EOF.  */
    {
      struct stat file_stats;

      /* First try seeking.  For large offsets, this extra work is
     worthwhile.  If the offset is below some threshold it may be
     more efficient to move the pointer by reading.  There are two
     issues when trying to seek:
       - the file must be seekable.
       - before seeking to the specified position, make sure
         that the new position is in the current file.
         Try to do that by getting file's size using fstat.
         But that will work only for regular files.  */

      if (fstat (fileno (in_stream), &file_stats) == 0)
    {
      /* The st_size field is valid only for regular files
         (and for symbolic links, which cannot occur here).
         If the number of bytes left to skip is at least
         as large as the size of the current file, we can
         decrement n_skip and go on to the next file.  */

      if (S_ISREG (file_stats.st_mode) && 0 <= file_stats.st_size)
        {
          if ((uintmax_t) file_stats.st_size <= n_skip)
        n_skip -= file_stats.st_size;
          else
        {
          if (fseeko (in_stream, n_skip, SEEK_CUR) != 0)
            {
              error (0, errno, "%s", input_filename);
              err = 1;
            }
          n_skip = 0;
        }
        }

      /* If it's not a regular file with nonnegative size,
         position the file pointer by reading.  */

      else
        {
          char buf[BUFSIZ];
          size_t n_bytes_read, n_bytes_to_read = BUFSIZ;

          while (0 < n_skip)
        {
          if (n_skip < n_bytes_to_read)
            n_bytes_to_read = n_skip;
          n_bytes_read = fread (buf, 1, n_bytes_to_read, in_stream);
          n_skip -= n_bytes_read;
          if (n_bytes_read != n_bytes_to_read)
            break;
        }
        }

      if (n_skip == 0)
        break;
    }

      else   /* cannot fstat() file */
    {
      error (0, errno, "%s", input_filename);
      err = 1;
    }

      err |= check_and_close ();

      err |= open_next_file ();
    }

  if (n_skip != 0)
    error (EXIT_FAILURE, 0, _("cannot skip past end of combined input"));

  return err;
}

static void
format_address_none (uintmax_t address ATTRIBUTE_UNUSED, char c ATTRIBUTE_UNUSED)
{
}

static void
format_address_std (uintmax_t address, char c)
{
  char buf[MAX_ADDRESS_LENGTH + 2];
  char *p = buf + sizeof buf;
  char const *pbound;

  *--p = '\0';
  *--p = c;
  pbound = p - address_pad_len;

  /* Use a special case of the code for each base.  This is measurably
     faster than generic code.  */
  switch (address_base)
    {
    case 8:
      do
    *--p = '0' + (address & 7);
      while ((address >>= 3) != 0);
      break;

    case 10:
      do
    *--p = '0' + (address % 10);
      while ((address /= 10) != 0);
      break;

    case 16:
      do
    *--p = "0123456789abcdef"[address & 15];
      while ((address >>= 4) != 0);
      break;
    }

  while (pbound < p)
    *--p = '0';

  fputs (p, stdout);
}

static void
format_address_paren (uintmax_t address, char c)
{
  putchar ('(');
  format_address_std (address, ')');
  putchar (c);
}

static void
format_address_label (uintmax_t address, char c)
{
  format_address_std (address, ' ');
  format_address_paren (address + pseudo_offset, c);
}

/* Write N_BYTES bytes from CURR_BLOCK to standard output once for each
   of the N_SPEC format specs.  CURRENT_OFFSET is the byte address of
   CURR_BLOCK in the concatenation of input files, and it is printed
   (optionally) only before the output line associated with the first
   format spec.  When duplicate blocks are being abbreviated, the output
   for a sequence of identical input blocks is the output for the first
   block followed by an asterisk alone on a line.  It is valid to compare
   the blocks PREV_BLOCK and CURR_BLOCK only when N_BYTES == BYTES_PER_BLOCK.
   That condition may be false only for the last input block -- and then
   only when it has not been padded to length BYTES_PER_BLOCK.  */

static void
write_block (uintmax_t current_offset, size_t n_bytes,
         const char *prev_block, const char *curr_block)
{
  static int first = 1;
  static int prev_pair_equal = 0;

#define EQUAL_BLOCKS(b1, b2) (memcmp ((b1), (b2), bytes_per_block) == 0)

  if (abbreviate_duplicate_blocks
      && !first && n_bytes == bytes_per_block
      && EQUAL_BLOCKS (prev_block, curr_block))
    {
      if (prev_pair_equal)
    {
      /* The two preceding blocks were equal, and the current
         block is the same as the last one, so print nothing.  */
    }
      else
    {
      printf ("*\n");
      prev_pair_equal = 1;
    }
    }
  else
    {
      size_t i;

      prev_pair_equal = 0;
      for (i = 0; i < n_specs; i++)
    {
      if (i == 0)
        format_address (current_offset, '\0');
      else
        printf ("%*s", address_pad_len, "");
      (*spec[i].print_function) (n_bytes, curr_block, spec[i].fmt_string);
      if (spec[i].hexl_mode_trailer)
        {
          /* space-pad out to full line width, then dump the trailer */
          int datum_width = width_bytes[spec[i].size];
          int blank_fields = (bytes_per_block - n_bytes) / datum_width;
          int field_width = spec[i].field_width + 1;
          printf ("%*s", blank_fields * field_width, "");
          dump_hexl_mode_trailer (n_bytes, curr_block);
        }
      putchar ('\n');
    }
    }
  first = 0;
}

/* Read a single byte into *C from the concatenation of the input files
   named in the global array FILE_LIST.  On the first call to this
   function, the global variable IN_STREAM is expected to be an open
   stream associated with the input file INPUT_FILENAME.  If IN_STREAM
   is at end-of-file, close it and update the global variables IN_STREAM
   and INPUT_FILENAME so they correspond to the next file in the list.
   Then try to read a byte from the newly opened file.  Repeat if
   necessary until EOF is reached for the last file in FILE_LIST, then
   set *C to EOF and return.  Subsequent calls do likewise.  The return
   value is nonzero if any errors occured, zero otherwise.  */

static int
read_char (int *c)
{
  int err = 0;

  *c = EOF;

  while (in_stream != NULL) /* EOF.  */
    {
      *c = fgetc (in_stream);

      if (*c != EOF)
    break;

      err |= check_and_close ();

      err |= open_next_file ();
    }

  return err;
}

/* Read N bytes into BLOCK from the concatenation of the input files
   named in the global array FILE_LIST.  On the first call to this
   function, the global variable IN_STREAM is expected to be an open
   stream associated with the input file INPUT_FILENAME.  If all N
   bytes cannot be read from IN_STREAM, close IN_STREAM and update
   the global variables IN_STREAM and INPUT_FILENAME.  Then try to
   read the remaining bytes from the newly opened file.  Repeat if
   necessary until EOF is reached for the last file in FILE_LIST.
   On subsequent calls, don't modify BLOCK and return zero.  Set
   *N_BYTES_IN_BUFFER to the number of bytes read.  If an error occurs,
   it will be detected through ferror when the stream is about to be
   closed.  If there is an error, give a message but continue reading
   as usual and return nonzero.  Otherwise return zero.  */

static int
read_block (size_t n, char *block, size_t *n_bytes_in_buffer)
{
  int err = 0;

  assert (0 < n && n <= bytes_per_block);

  *n_bytes_in_buffer = 0;

  if (n == 0)
    return 0;

  while (in_stream != NULL) /* EOF.  */
    {
      size_t n_needed;
      size_t n_read;

      n_needed = n - *n_bytes_in_buffer;
      n_read = fread (block + *n_bytes_in_buffer, 1, n_needed, in_stream);

      *n_bytes_in_buffer += n_read;

      if (n_read == n_needed)
    break;

      err |= check_and_close ();

      err |= open_next_file ();
    }

  return err;
}

/* Return the least common multiple of the sizes associated
   with the format specs.  */

static int
get_lcm (void)
{
  size_t i;
  int l_c_m = 1;

  for (i = 0; i < n_specs; i++)
    l_c_m = lcm (l_c_m, width_bytes[(int) spec[i].size]);
  return l_c_m;
}

/* If S is a valid traditional offset specification with an optional
   leading '+' return nonzero and set *OFFSET to the offset it denotes.  */

static int
parse_old_offset (const char *s, uintmax_t *offset)
{
  int radix;
  enum strtol_error s_err;

  if (*s == '\0')
    return 0;

  /* Skip over any leading '+'. */
  if (s[0] == '+')
    ++s;

  /* Determine the radix we'll use to interpret S.  If there is a `.',
     it's decimal, otherwise, if the string begins with `0X'or `0x',
     it's hexadecimal, else octal.  */
  if (strchr (s, '.') != NULL)
    radix = 10;
  else
    {
      if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
    radix = 16;
      else
    radix = 8;
    }

  s_err = xstrtoumax (s, NULL, radix, offset, "Bb");
  if (s_err != LONGINT_OK)
    {
      STRTOL_FAIL_WARN (s, _("old-style offset"), s_err);
      return 0;
    }
  return 1;
}

/* Read a chunk of size BYTES_PER_BLOCK from the input files, write the
   formatted block to standard output, and repeat until the specified
   maximum number of bytes has been read or until all input has been
   processed.  If the last block read is smaller than BYTES_PER_BLOCK
   and its size is not a multiple of the size associated with a format
   spec, extend the input block with zero bytes until its length is a
   multiple of all format spec sizes.  Write the final block.  Finally,
   write on a line by itself the offset of the byte after the last byte
   read.  Accumulate return values from calls to read_block and
   check_and_close, and if any was nonzero, return nonzero.
   Otherwise, return zero.  */

static int
dump (void)
{
  char *block[2];
  uintmax_t current_offset;
  int idx;
  int err;
  size_t n_bytes_read;

  block[0] = (char *) alloca (bytes_per_block);
  block[1] = (char *) alloca (bytes_per_block);

  current_offset = n_bytes_to_skip;

  idx = 0;
  err = 0;
  if (limit_bytes_to_format)
    {
      while (1)
    {
      size_t n_needed;
      if (current_offset >= end_offset)
        {
          n_bytes_read = 0;
          break;
        }
      n_needed = MIN (end_offset - current_offset,
              (uintmax_t) bytes_per_block);
      err |= read_block (n_needed, block[idx], &n_bytes_read);
      if (n_bytes_read < bytes_per_block)
        break;
      assert (n_bytes_read == bytes_per_block);
      write_block (current_offset, n_bytes_read,
               block[!idx], block[idx]);
      current_offset += n_bytes_read;
      idx = !idx;
    }
    }
  else
    {
      while (1)
    {
      err |= read_block (bytes_per_block, block[idx], &n_bytes_read);
      if (n_bytes_read < bytes_per_block)
        break;
      assert (n_bytes_read == bytes_per_block);
      write_block (current_offset, n_bytes_read,
               block[!idx], block[idx]);
      current_offset += n_bytes_read;
      idx = !idx;
    }
    }

  if (n_bytes_read > 0)
    {
      int l_c_m;
      size_t bytes_to_write;

      l_c_m = get_lcm ();

      /* Make bytes_to_write the smallest multiple of l_c_m that
     is at least as large as n_bytes_read.  */
      bytes_to_write = l_c_m * ((n_bytes_read + l_c_m - 1) / l_c_m);

      memset (block[idx] + n_bytes_read, 0, bytes_to_write - n_bytes_read);
      write_block (current_offset, bytes_to_write,
           block[!idx], block[idx]);
      current_offset += n_bytes_read;
    }

  format_address (current_offset, '\n');

  if (limit_bytes_to_format && current_offset >= end_offset)
    err |= check_and_close ();

  return err;
}

/* STRINGS mode.  Find each "string constant" in the input.
   A string constant is a run of at least `string_min' ASCII
   graphic (or formatting) characters terminated by a null.
   Based on a function written by Richard Stallman for a
   traditional version of od.  Return nonzero if an error
   occurs.  Otherwise, return zero.  */

static int
dump_strings (void)
{
  size_t bufsize = MAX (100, string_min);
  char *buf = xmalloc (bufsize);
  uintmax_t address = n_bytes_to_skip;
  int err;

  err = 0;
  while (1)
    {
      size_t i;
      int c;

      /* See if the next `string_min' chars are all printing chars.  */
    tryline:

      if (limit_bytes_to_format
      && (end_offset < string_min || end_offset - string_min <= address))
    break;

      for (i = 0; i < string_min; i++)
    {
      err |= read_char (&c);
      address++;
      if (c < 0)
        {
          free (buf);
          return err;
        }
      if (!ISPRINT (c))
        /* Found a non-printing.  Try again starting with next char.  */
        goto tryline;
      buf[i] = c;
    }

      /* We found a run of `string_min' printable characters.
     Now see if it is terminated with a null byte.  */
      while (!limit_bytes_to_format || address < end_offset)
    {
      if (i == bufsize)
        {
          bufsize = 1 + 3 * bufsize / 2;
          buf = xrealloc (buf, bufsize);
        }
      err |= read_char (&c);
      address++;
      if (c < 0)
        {
          free (buf);
          return err;
        }
      if (c == '\0')
        break;      /* It is; print this string.  */
      if (!ISPRINT (c))
        goto tryline;   /* It isn't; give up on this string.  */
      buf[i++] = c;     /* String continues; store it all.  */
    }

      /* If we get here, the string is all printable and null-terminated,
     so print it.  It is all in `buf' and `i' is its length.  */
      buf[i] = 0;
      format_address (address - i - 1, ' ');

      for (i = 0; (c = buf[i]); i++)
    {
      switch (c)
        {
        case '\007':
          fputs ("\\a", stdout);
          break;

        case '\b':
          fputs ("\\b", stdout);
          break;

        case '\f':
          fputs ("\\f", stdout);
          break;

        case '\n':
          fputs ("\\n", stdout);
          break;

        case '\r':
          fputs ("\\r", stdout);
          break;

        case '\t':
          fputs ("\\t", stdout);
          break;

        case '\v':
          fputs ("\\v", stdout);
          break;

        default:
          putc (c, stdout);
        }
    }
      putchar ('\n');
    }

  /* We reach this point only if we search through
     (max_bytes_to_format - string_min) bytes before reaching EOF.  */

  free (buf);

  err |= check_and_close ();
  return err;
}

int
main (int argc, char **argv)
{
  int c;
  int n_files;
  size_t i;
  int l_c_m;
  size_t desired_width IF_LINT (= 0);
  int width_specified = 0;
  int n_failed_decodes = 0;
  int err;
  char const *short_options = (posix2_version () < 200112
                   ? COMMON_SHORT_OPTIONS "s::w::"
                   : COMMON_SHORT_OPTIONS "s:w:");

  /* The old-style `pseudo starting address' to be printed in parentheses
     after any true address.  */
  uintmax_t pseudo_start IF_LINT (= 0);

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  err = 0;

  for (i = 0; i <= MAX_INTEGRAL_TYPE_SIZE; i++)
    integral_type_size[i] = NO_SIZE;

  integral_type_size[sizeof (char)] = CHAR;
  integral_type_size[sizeof (short int)] = SHORT;
  integral_type_size[sizeof (int)] = INT;
  integral_type_size[sizeof (long int)] = LONG;
#if HAVE_UNSIGNED_LONG_LONG
  /* If `long' and `long long' have the same size, it's fine
     to overwrite the entry for `long' with this one.  */
  integral_type_size[sizeof (ulonglong_t)] = LONG_LONG;
#endif

  for (i = 0; i <= MAX_FP_TYPE_SIZE; i++)
    fp_type_size[i] = NO_SIZE;

  fp_type_size[sizeof (float)] = FLOAT_SINGLE;
  /* The array entry for `double' is filled in after that for LONG_DOUBLE
     so that if `long double' is the same type or if long double isn't
     supported FLOAT_LONG_DOUBLE will never be used.  */
  fp_type_size[sizeof (LONG_DOUBLE)] = FLOAT_LONG_DOUBLE;
  fp_type_size[sizeof (double)] = FLOAT_DOUBLE;

  n_specs = 0;
  n_specs_allocated = 5;
  spec = (struct tspec *) xmalloc (n_specs_allocated * sizeof (struct tspec));

  format_address = format_address_std;
  address_base = 8;
  address_pad_len = 7;
  flag_dump_strings = 0;

  while ((c = getopt_long (argc, argv, short_options, long_options, NULL))
     != -1)
    {
      uintmax_t tmp;
      enum strtol_error s_err;

      switch (c)
    {
    case 0:
      break;

    case 'A':
      switch (optarg[0])
        {
        case 'd':
          format_address = format_address_std;
          address_base = 10;
          address_pad_len = 7;
          break;
        case 'o':
          format_address = format_address_std;
          address_base = 8;
          address_pad_len = 7;
          break;
        case 'x':
          format_address = format_address_std;
          address_base = 16;
          address_pad_len = 6;
          break;
        case 'n':
          format_address = format_address_none;
          address_pad_len = 0;
          break;
        default:
          error (EXIT_FAILURE, 0,
             _("invalid output address radix `%c'; \
it must be one character from [doxn]"),
             optarg[0]);
          break;
        }
      break;

    case 'j':
      s_err = xstrtoumax (optarg, NULL, 0, &n_bytes_to_skip, "bkm");
      if (s_err != LONGINT_OK)
        STRTOL_FATAL_ERROR (optarg, _("skip argument"), s_err);
      break;

    case 'N':
      limit_bytes_to_format = 1;

      s_err = xstrtoumax (optarg, NULL, 0, &max_bytes_to_format, "bkm");
      if (s_err != LONGINT_OK)
        STRTOL_FATAL_ERROR (optarg, _("limit argument"), s_err);
      break;

    case 's':
      if (optarg == NULL)
        string_min = 3;
      else
        {
          s_err = xstrtoumax (optarg, NULL, 0, &tmp, "bkm");
          if (s_err != LONGINT_OK)
        STRTOL_FATAL_ERROR (optarg, _("minimum string length"), s_err);

          /* The minimum string length may be no larger than SIZE_MAX,
         since we may allocate a buffer of this size.  */
          if (SIZE_MAX < tmp)
        error (EXIT_FAILURE, 0, _("%s is too large"), optarg);

          string_min = tmp;
        }
      flag_dump_strings = 1;
      break;

    case 't':
      if (decode_format_string (optarg))
        ++n_failed_decodes;
      break;

    case 'v':
      abbreviate_duplicate_blocks = 0;
      break;

    case TRADITIONAL_OPTION:
      traditional = 1;
      break;

      /* The next several cases map the traditional format
         specification options to the corresponding modern format
         specs.  GNU od accepts any combination of old- and
         new-style options.  Format specification options accumulate.  */

#define CASE_OLD_ARG(old_char,new_string)       \
    case old_char:                  \
      {                     \
        if (decode_format_string (new_string))  \
          ++n_failed_decodes;           \
      }                     \
      break

      CASE_OLD_ARG ('a', "a");
      CASE_OLD_ARG ('b', "oC");
      CASE_OLD_ARG ('c', "c");
      CASE_OLD_ARG ('d', "u2");
      CASE_OLD_ARG ('f', "fF");
      CASE_OLD_ARG ('h', "x2");
      CASE_OLD_ARG ('i', "d2");
      CASE_OLD_ARG ('l', "d4");
      CASE_OLD_ARG ('o', "o2");
      CASE_OLD_ARG ('x', "x2");

      /* FIXME: POSIX 1003.1-2001 with XSI requires this:

         CASE_OLD_ARG ('s', "d2");

         for the traditional syntax, but this conflicts with case
         's' above. */

#undef CASE_OLD_ARG

    case 'w':
      width_specified = 1;
      if (optarg == NULL)
        {
          desired_width = 32;
        }
      else
        {
          uintmax_t w_tmp;
          s_err = xstrtoumax (optarg, NULL, 10, &w_tmp, "");
          if (s_err != LONGINT_OK)
        STRTOL_FATAL_ERROR (optarg, _("width specification"), s_err);
          if (SIZE_MAX < w_tmp)
        error (EXIT_FAILURE, 0, _("%s is too large"), optarg);
          desired_width = w_tmp;
        }
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
      break;
    }
    }

  if (n_failed_decodes > 0)
    exit (EXIT_FAILURE);

  if (flag_dump_strings && n_specs > 0)
    error (EXIT_FAILURE, 0,
       _("no type may be specified when dumping strings"));

  n_files = argc - optind;

  /* If the --traditional option is used, there may be from
     0 to 3 remaining command line arguments;  handle each case
     separately.
    od [file] [[+]offset[.][b] [[+]label[.][b]]]
     The offset and pseudo_start have the same syntax.

     FIXME: POSIX 1003.1-2001 with XSI requires support for the
     traditional syntax even if --traditional is not given.  */

  if (traditional)
    {
      uintmax_t offset;

      if (n_files == 1)
    {
      if (parse_old_offset (argv[optind], &offset))
        {
          n_bytes_to_skip = offset;
          --n_files;
          ++argv;
        }
    }
      else if (n_files == 2)
    {
      uintmax_t o1, o2;
      if (parse_old_offset (argv[optind], &o1)
          && parse_old_offset (argv[optind + 1], &o2))
        {
          n_bytes_to_skip = o1;
          flag_pseudo_start = 1;
          pseudo_start = o2;
          argv += 2;
          n_files -= 2;
        }
      else if (parse_old_offset (argv[optind + 1], &o2))
        {
          n_bytes_to_skip = o2;
          --n_files;
          argv[optind + 1] = argv[optind];
          ++argv;
        }
      else
        {
          error (0, 0,
             _("invalid second operand in compatibility mode `%s'"),
             argv[optind + 1]);
          usage (EXIT_FAILURE);
        }
    }
      else if (n_files == 3)
    {
      uintmax_t o1, o2;
      if (parse_old_offset (argv[optind + 1], &o1)
          && parse_old_offset (argv[optind + 2], &o2))
        {
          n_bytes_to_skip = o1;
          flag_pseudo_start = 1;
          pseudo_start = o2;
          argv[optind + 2] = argv[optind];
          argv += 2;
          n_files -= 2;
        }
      else
        {
          error (0, 0,
        _("in compatibility mode, the last two arguments must be offsets"));
          usage (EXIT_FAILURE);
        }
    }
      else if (n_files > 3)
    {
      error (0, 0,
         _("compatibility mode supports at most three arguments"));
      usage (EXIT_FAILURE);
    }

      if (flag_pseudo_start)
    {
      if (format_address == format_address_none)
        {
          address_base = 8;
          address_pad_len = 7;
          format_address = format_address_paren;
        }
      else
        format_address = format_address_label;
    }
    }

  if (limit_bytes_to_format)
    {
      end_offset = n_bytes_to_skip + max_bytes_to_format;
      if (end_offset < n_bytes_to_skip)
    error (EXIT_FAILURE, 0, "skip-bytes + read-bytes is too large");
    }

  if (n_specs == 0)
    {
      if (decode_one_format ("o2", "o2", NULL, &(spec[0])))
    {
      /* This happens on Cray systems that don't have a 2-byte
         integral type.  */
      exit (EXIT_FAILURE);
    }

      n_specs = 1;
    }

  if (n_files > 0)
    {
      /* Set the global pointer FILE_LIST so that it
     references the first file-argument on the command-line.  */

      file_list = (char const *const *) &argv[optind];
    }
  else
    {
      /* No files were listed on the command line.
     Set the global pointer FILE_LIST so that it
     references the null-terminated list of one name: "-".  */

      file_list = default_file_list;
    }

  /* open the first input file */
  err |= open_next_file ();
  if (in_stream == NULL)
    goto cleanup;

  /* skip over any unwanted header bytes */
  err |= skip (n_bytes_to_skip);
  if (in_stream == NULL)
    goto cleanup;

  pseudo_offset = (flag_pseudo_start ? pseudo_start - n_bytes_to_skip : 0);

  /* Compute output block length.  */
  l_c_m = get_lcm ();

  if (width_specified)
    {
      if (desired_width != 0 && desired_width % l_c_m == 0)
    bytes_per_block = desired_width;
      else
    {
      error (0, 0, _("warning: invalid width %lu; using %d instead"),
         (unsigned long) desired_width, l_c_m);
      bytes_per_block = l_c_m;
    }
    }
  else
    {
      if (l_c_m < DEFAULT_BYTES_PER_BLOCK)
    bytes_per_block = l_c_m * (DEFAULT_BYTES_PER_BLOCK / l_c_m);
      else
    bytes_per_block = l_c_m;
    }

#ifdef DEBUG
  for (i = 0; i < n_specs; i++)
    {
      printf (_("%d: fmt=\"%s\" width=%d\n"),
          i, spec[i].fmt_string, width_bytes[spec[i].size]);
    }
#endif

  err |= (flag_dump_strings ? dump_strings () : dump ());

cleanup:;

  if (have_read_stdin && fclose (stdin) == EOF)
    error (EXIT_FAILURE, errno, _("standard input"));

  exit (err == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/paste.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/paste.c used in: Step 903
/* paste - merge lines of files
   Copyright (C) 1984, 1997-2002 by David M. Ihnat

   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.  */

/* Written by David Ihnat.  */

/* The list of valid escape sequences has been expanded over the Unix
   version, to include \b, \f, \r, and \v.

   POSIX changes, bug fixes, long-named options, and cleanup
   by David MacKenzie <djm@gnu.ai.mit.edu>.

   Options:
   --serial
   -s               Paste one file at a time rather than
                one line from each file.
   --delimiters=delim-list
   -d delim-list        Consecutively use the characters in
                DELIM-LIST instead of tab to separate
                merged lines.  When DELIM-LIST is exhausted,
                start again at its beginning.
   A FILE of `-' means standard input.
   If no FILEs are given, standard input is used. */

#include <config.h>

#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
#include "closeout.h"
#include "error.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "paste"

#define AUTHORS N_ ("David M. Ihnat and David MacKenzie")

/* Indicates that no delimiter should be added in the current position. */
#define EMPTY_DELIM '\0'

static FILE dummy_closed;
/* Element marking a file that has reached EOF and been closed. */
#define CLOSED (&dummy_closed)

static FILE dummy_endlist;
/* Element marking end of list of open files. */
#define ENDLIST (&dummy_endlist)

/* Name this program was run with. */
char *program_name;

/* If nonzero, we have read standard input at some point. */
static int have_read_stdin;

/* If nonzero, merge subsequent lines of each file rather than
   corresponding lines from each file in parallel. */
static int serial_merge;

/* The delimeters between lines of input files (used cyclically). */
static char *delims;

/* A pointer to the character after the end of `delims'. */
static char *delim_end;

static struct option const longopts[] =
{
  {"serial", no_argument, 0, 's'},
  {"delimiters", required_argument, 0, 'd'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {0, 0, 0, 0}
};

/* Replace backslash representations of special characters in
   STRPTR with their actual values.
   The set of possible backslash characters has been expanded beyond
   that recognized by the Unix version.

   Return a pointer to the character after the new end of STRPTR. */

static char *
collapse_escapes (char *strptr)
{
  register char *strout;

  strout = strptr;      /* Start at the same place, anyway. */

  while (*strptr)
    {
      if (*strptr != '\\')  /* Is it an escape character? */
    *strout++ = *strptr++;  /* No, just transfer it. */
      else
    {
      switch (*++strptr)
        {
        case '0':
          *strout++ = EMPTY_DELIM;
          break;

        case 'b':
          *strout++ = '\b';
          break;

        case 'f':
          *strout++ = '\f';
          break;

        case 'n':
          *strout++ = '\n';
          break;

        case 'r':
          *strout++ = '\r';
          break;

        case 't':
          *strout++ = '\t';
          break;

        case 'v':
          *strout++ = '\v';
          break;

        default:
          *strout++ = *strptr;
          break;
        }
      strptr++;
    }
    }
  return strout;
}

/* Perform column paste on the NFILES files named in FNAMPTR.
   Return 0 if no errors, 1 if one or more files could not be
   opened or read. */

static int
paste_parallel (int nfiles, char **fnamptr)
{
  int errors = 0;       /* 1 if open or read errors occur. */
  /* Number of files for which space is allocated in `delbuf' and `fileptr'.
     Enlarged as necessary. */
  int file_list_size = 12;
  int chr IF_LINT (= 0);    /* Input character. */
  int line_length;      /* Number of chars in line. */
  int somedone;         /* 0 if all files empty for this line. */
  /* If all files are just ready to be closed, or will be on this
     round, the string of delimiters must be preserved.
     delbuf[0] through delbuf[file_list_size]
     store the delimiters for closed files. */
  char *delbuf;
  size_t delims_saved;      /* Number of delims saved in `delbuf'. */
  register char *delimptr;  /* Cycling pointer into `delims'. */
  FILE **fileptr;       /* Streams open to the files to process. */
  int files_open;       /* Number of files still open to process. */
  int i;            /* Loop index. */
  int opened_stdin = 0;     /* Nonzero if any fopen got fd 0. */

  delbuf = (char *) xmalloc (file_list_size + 2);
  fileptr = (FILE **) xmalloc ((file_list_size + 1) * sizeof (FILE *));

  /* Attempt to open all files.  This could be expanded to an infinite
     number of files, but at the (considerable) expense of remembering
     each file and its current offset, then opening/reading/closing.  */

  for (files_open = 0; files_open < nfiles; ++files_open)
    {
      if (files_open == file_list_size - 2)
    {
      file_list_size += 12;
      delbuf = (char *) xrealloc (delbuf, file_list_size + 2);
      fileptr = (FILE **) xrealloc ((char *) fileptr, (file_list_size + 1)
                    * sizeof (FILE *));
    }
      if (STREQ (fnamptr[files_open], "-"))
    {
      have_read_stdin = 1;
      fileptr[files_open] = stdin;
    }
      else
    {
      fileptr[files_open] = fopen (fnamptr[files_open], "r");
      if (fileptr[files_open] == NULL)
        error (EXIT_FAILURE, errno, "%s", fnamptr[files_open]);
      else if (fileno (fileptr[files_open]) == 0)
        opened_stdin = 1;
    }
    }

  fileptr[files_open] = ENDLIST;

  if (opened_stdin && have_read_stdin)
    error (EXIT_FAILURE, 0, _("standard input is closed"));

  /* Read a line from each file and output it to stdout separated by a
     delimiter, until we go through the loop without successfully
     reading from any of the files. */

  while (files_open)
    {
      /* Set up for the next line. */
      somedone = 0;
      delimptr = delims;
      delims_saved = 0;

      for (i = 0; fileptr[i] != ENDLIST && files_open; i++)
    {
      line_length = 0;  /* Clear so we can easily detect EOF. */
      if (fileptr[i] != CLOSED)
        {
          chr = getc (fileptr[i]);
          if (chr != EOF && delims_saved)
        {
          fwrite (delbuf, sizeof (char), delims_saved, stdout);
          delims_saved = 0;
        }

          while (chr != EOF)
        {
          line_length++;
          if (chr == '\n')
            break;
          putc (chr, stdout);
          chr = getc (fileptr[i]);
        }
        }

      if (line_length == 0)
        {
          /* EOF, read error, or closed file.
         If an EOF or error, close the file and mark it in the list. */
          if (fileptr[i] != CLOSED)
        {
          if (ferror (fileptr[i]))
            {
              error (0, errno, "%s", fnamptr[i]);
              errors = 1;
            }
          if (fileptr[i] == stdin)
            clearerr (fileptr[i]); /* Also clear EOF. */
          else if (fclose (fileptr[i]) == EOF)
            {
              error (0, errno, "%s", fnamptr[i]);
              errors = 1;
            }

          fileptr[i] = CLOSED;
          files_open--;
        }

          if (fileptr[i + 1] == ENDLIST)
        {
          /* End of this output line.
             Is this the end of the whole thing? */
          if (somedone)
            {
              /* No.  Some files were not closed for this line. */
              if (delims_saved)
            {
              fwrite (delbuf, sizeof (char), delims_saved, stdout);
              delims_saved = 0;
            }
              putc ('\n', stdout);
            }
          continue; /* Next read of files, or exit. */
        }
          else
        {
          /* Closed file; add delimiter to `delbuf'. */
          if (*delimptr != EMPTY_DELIM)
            delbuf[delims_saved++] = *delimptr;
          if (++delimptr == delim_end)
            delimptr = delims;
        }
        }
      else
        {
          /* Some data read. */
          somedone++;

          /* Except for last file, replace last newline with delim. */
          if (fileptr[i + 1] != ENDLIST)
        {
          if (chr != '\n')
            putc (chr, stdout);
          if (*delimptr != EMPTY_DELIM)
            putc (*delimptr, stdout);
          if (++delimptr == delim_end)
            delimptr = delims;
        }
          else
        putc (chr, stdout);
        }
    }
    }
  return errors;
}

/* Perform serial paste on the NFILES files named in FNAMPTR.
   Return 0 if no errors, 1 if one or more files could not be
   opened or read. */

static int
paste_serial (int nfiles, char **fnamptr)
{
  int errors = 0;       /* 1 if open or read errors occur. */
  register int charnew, charold; /* Current and previous char read. */
  register char *delimptr;  /* Current delimiter char. */
  register FILE *fileptr;   /* Open for reading current file. */

  for (; nfiles; nfiles--, fnamptr++)
    {
      if (STREQ (*fnamptr, "-"))
    {
      have_read_stdin = 1;
      fileptr = stdin;
    }
      else
    {
      fileptr = fopen (*fnamptr, "r");
      if (fileptr == NULL)
        {
          error (0, errno, "%s", *fnamptr);
          errors = 1;
          continue;
        }
    }

      delimptr = delims;    /* Set up for delimiter string. */

      charold = getc (fileptr);
      if (charold != EOF)
    {
      /* `charold' is set up.  Hit it!
         Keep reading characters, stashing them in `charnew';
         output `charold', converting to the appropriate delimiter
         character if needed.  After the EOF, output `charold'
         if it's a newline; otherwise, output it and then a newline. */

      while ((charnew = getc (fileptr)) != EOF)
        {
          /* Process the old character. */
          if (charold == '\n')
        {
          if (*delimptr != EMPTY_DELIM)
            putc (*delimptr, stdout);

          if (++delimptr == delim_end)
            delimptr = delims;
        }
          else
        putc (charold, stdout);

          charold = charnew;
        }

      /* Hit EOF.  Process that last character. */
      putc (charold, stdout);
    }

      if (charold != '\n')
    putc ('\n', stdout);

      if (ferror (fileptr))
    {
      error (0, errno, "%s", *fnamptr);
      errors = 1;
    }
      if (fileptr == stdin)
    clearerr (fileptr); /* Also clear EOF. */
      else if (fclose (fileptr) == EOF)
    {
      error (0, errno, "%s", *fnamptr);
      errors = 1;
    }
    }
  return errors;
}

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
          program_name);
      fputs (_("\
Write lines consisting of the sequentially corresponding lines from\n\
each FILE, separated by TABs, to standard output.\n\
With no FILE, or when FILE is -, read standard input.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -d, --delimiters=LIST   reuse characters from LIST instead of TABs\n\
  -s, --serial            paste one file at a time instead of in parallel\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      /* FIXME: add a couple of examples.  */
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

int
main (int argc, char **argv)
{
  int optc, exit_status;
  char default_delims[2], zero_delims[3];

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  have_read_stdin = 0;
  serial_merge = 0;
  delims = default_delims;
  strcpy (delims, "\t");
  strcpy (zero_delims, "\\0");

  while ((optc = getopt_long (argc, argv, "d:s", longopts, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:
      break;

    case 'd':
      /* Delimiter character(s). */
      if (optarg[0] == '\0')
        optarg = zero_delims;
      delims = optarg;
      break;

    case 's':
      serial_merge++;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (optind == argc)
    argv[argc++] = "-";

  delim_end = collapse_escapes (delims);

  if (!serial_merge)
    exit_status = paste_parallel (argc - optind, &argv[optind]);
  else
    exit_status = paste_serial (argc - optind, &argv[optind]);
  if (have_read_stdin && fclose (stdin) == EOF)
    error (EXIT_FAILURE, errno, "-");
  exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/pathchk.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/pathchk.c used in: Step 905
/* pathchk -- check whether pathnames are valid or portable
   Copyright (C) 1991-2003 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.  */

/* Usage: pathchk [-p] [--portability] path...

   For each PATH, print a message if any of these conditions are false:
   * all existing leading directories in PATH have search (execute) permission
   * strlen (PATH) <= PATH_MAX
   * strlen (each_directory_in_PATH) <= NAME_MAX

   Exit status:
   0            All PATH names passed all of the tests.
   1            An error occurred.

   Options:
   -p, --portability    Instead of performing length checks on the
            underlying filesystem, test the length of the
            pathname and its components against the POSIX
            minimum limits for portability, _POSIX_NAME_MAX
            and _POSIX_PATH_MAX in 2.9.2.  Also check that
            the pathname contains no character not in the
            portable filename character set.

   David MacKenzie <djm@gnu.ai.mit.edu>
   and Jim Meyering <meyering@cs.utexas.edu> */

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#include "system.h"
#include "error.h"
#include "long-options.h"
#include "closeout.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "pathchk"

#define AUTHORS N_ ("David MacKenzie and Jim Meyering")

#define NEED_PATHCONF_WRAPPER 0
#if HAVE_PATHCONF
# ifndef PATH_MAX
#  define PATH_MAX_FOR(p) pathconf_wrapper ((p), _PC_PATH_MAX)
#  define NEED_PATHCONF_WRAPPER 1
# endif /* not PATH_MAX */
# ifndef NAME_MAX
#  define NAME_MAX_FOR(p) pathconf_wrapper ((p), _PC_NAME_MAX);
#  undef NEED_PATHCONF_WRAPPER
#  define NEED_PATHCONF_WRAPPER 1
# endif /* not NAME_MAX */

#else

# include <sys/param.h>
# ifndef PATH_MAX
#  ifdef MAXPATHLEN
#   define PATH_MAX MAXPATHLEN
#  else /* not MAXPATHLEN */
#   define PATH_MAX _POSIX_PATH_MAX
#  endif /* not MAXPATHLEN */
# endif /* not PATH_MAX */

# ifndef NAME_MAX
#  ifdef MAXNAMLEN
#   define NAME_MAX MAXNAMLEN
#  else /* not MAXNAMLEN */
#   define NAME_MAX _POSIX_NAME_MAX
#  endif /* not MAXNAMLEN */
# endif /* not NAME_MAX */

#endif

#ifndef _POSIX_PATH_MAX
# define _POSIX_PATH_MAX 255
#endif
#ifndef _POSIX_NAME_MAX
# define _POSIX_NAME_MAX 14
#endif

#ifndef PATH_MAX_FOR
# define PATH_MAX_FOR(p) PATH_MAX
#endif
#ifndef NAME_MAX_FOR
# define NAME_MAX_FOR(p) NAME_MAX
#endif

static int validate_path (char *path, int portability);

/* The name this program was run with. */
char *program_name;

static struct option const longopts[] =
{
  {"portability", no_argument, NULL, 'p'},
  {NULL, 0, NULL, 0}
};

#if NEED_PATHCONF_WRAPPER
/* Distinguish between the cases when pathconf fails and when it reports there
   is no limit (the latter is the case for PATH_MAX on the Hurd).  When there
   is no limit, return LONG_MAX.  Otherwise, return pathconf's return value.  */

static long int
pathconf_wrapper (const char *filename, int param)
{
  long int ret;

  errno = 0;
  ret = pathconf (filename, param);
  if (ret < 0 && errno == 0)
    return LONG_MAX;

  return ret;
}
#endif

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]... NAME...\n"), program_name);
      fputs (_("\
Diagnose unportable constructs in NAME.\n\
\n\
  -p, --portability   check for all POSIX systems, not only this one\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  int exit_status = 0;
  int check_portability = 0;
  int optc;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              AUTHORS, usage);

  while ((optc = getopt_long (argc, argv, "p", longopts, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:
      break;

    case 'p':
      check_portability = 1;
      break;

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (optind == argc)
    {
      error (0, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }

  for (; optind < argc; ++optind)
    exit_status |= validate_path (argv[optind], check_portability);

  exit (exit_status);
}

/* Each element is nonzero if the corresponding ASCII character is
   in the POSIX portable character set, and zero if it is not.
   In addition, the entry for `/' is nonzero to simplify checking. */
static char const portable_chars[256] =
{
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
  0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
  0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

/* If PATH contains only portable characters, return 1, else 0.  */

static int
portable_chars_only (const char *path)
{
  const char *p;

  for (p = path; *p; ++p)
    if (portable_chars[(unsigned char) *p] == 0)
      {
    error (0, 0, _("path `%s' contains nonportable character `%c'"),
           path, *p);
    return 0;
      }
  return 1;
}

/* Return 1 if PATH is a usable leading directory, 0 if not,
   2 if it doesn't exist.  */

static int
dir_ok (const char *path)
{
  struct stat stats;

  if (stat (path, &stats))
    return 2;

  if (!S_ISDIR (stats.st_mode))
    {
      error (0, 0, _("`%s' is not a directory"), path);
      return 0;
    }

  /* Use access to test for search permission because
     testing permission bits of st_mode can lose with new
     access control mechanisms.  Of course, access loses if you're
     running setuid. */
  if (access (path, X_OK) != 0)
    {
      if (errno == EACCES)
    error (0, 0, _("directory `%s' is not searchable"), path);
      else
    error (0, errno, "%s", path);
      return 0;
    }

  return 1;
}

/* Make sure that
   strlen (PATH) <= PATH_MAX
   && strlen (each-existing-directory-in-PATH) <= NAME_MAX

   If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
   _POSIX_NAME_MAX instead, and make sure that PATH contains no
   characters not in the POSIX portable filename character set, which
   consists of A-Z, a-z, 0-9, ., _, -.

   Make sure that all leading directories along PATH that exist have
   `x' permission.

   Return 0 if all of these tests are successful, 1 if any fail. */

static int
validate_path (char *path, int portability)
{
  long int path_max;
  int last_elem;        /* Nonzero if checking last element of path. */
  int exists IF_LINT (= 0); /* 2 if the path element exists.  */
  char *slash;
  char *parent;         /* Last existing leading directory so far.  */

  if (portability && !portable_chars_only (path))
    return 1;

  if (*path == '\0')
    return 0;

  /* Figure out the parent of the first element in PATH.  */
  parent = xstrdup (*path == '/' ? "/" : ".");

  slash = path;
  last_elem = 0;
  while (1)
    {
      long int name_max;
      long int length;      /* Length of partial path being checked. */
      char *start;      /* Start of path element being checked. */

      /* Find the end of this element of the path.
     Then chop off the rest of the path after this element. */
      while (*slash == '/')
    slash++;
      start = slash;
      slash = strchr (slash, '/');
      if (slash != NULL)
    *slash = '\0';
      else
    {
      last_elem = 1;
      slash = strchr (start, '\0');
    }

      if (!last_elem)
    {
      exists = dir_ok (path);
      if (exists == 0)
        {
          free (parent);
          return 1;
        }
    }

      length = slash - start;
      /* Since we know that `parent' is a directory, it's ok to call
     pathconf with it as the argument.  (If `parent' isn't a directory
     or doesn't exist, the behavior of pathconf is undefined.)
     But if `parent' is a directory and is on a remote file system,
     it's likely that pathconf can't give us a reasonable value
     and will return -1.  (NFS and tempfs are not POSIX . . .)
     In that case, we have no choice but to assume the pessimal
     POSIX minimums.  */
      name_max = portability ? _POSIX_NAME_MAX : NAME_MAX_FOR (parent);
      if (name_max < 0)
    name_max = _POSIX_NAME_MAX;
      if (length > name_max)
    {
      error (0, 0, _("name `%s' has length %ld; exceeds limit of %ld"),
         start, length, name_max);
      free (parent);
      return 1;
    }

      if (last_elem)
    break;

      if (exists == 1)
    {
      free (parent);
      parent = xstrdup (path);
    }

      *slash++ = '/';
    }

  /* `parent' is now the last existing leading directory in the whole path,
     so it's ok to call pathconf with it as the argument.  */
  path_max = portability ? _POSIX_PATH_MAX : PATH_MAX_FOR (parent);
  if (path_max < 0)
    path_max = _POSIX_PATH_MAX;
  free (parent);
  if (strlen (path) > (size_t) path_max)
    {
      error (0, 0, _("path `%s' has length %d; exceeds limit of %ld"),
         path, strlen (path), path_max);
      return 1;
    }

  return 0;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/pr.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/pr.c used in: Step 907
/* pr -- convert text files for printing.
   Copyright (C) 88, 91, 1995-2002 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.  */

/*  By Pete TerMaat, with considerable refinement by Roland Huebner.  */


/* Things to watch: Sys V screws up on ...
   pr -n -3 -s: /usr/dict/words
   pr -m -o10 -n /usr/dict/words{,,,}
   pr -6 -a -n -o5 /usr/dict/words

   Ideas:

   Keep a things_to_do list of functions to call when we know we have
   something to print.  Cleaner than current series of checks.

   Improve the printing of control prefixes.

   Expand the filename in the centered header line to a full pathname.


   Concept:

   If the input_tab_char differs from the default value TAB
   (`-e[CHAR[...]]' is used), any input text tab is expanded to the
   default width of 8 spaces (compare char_to_clump). - Same as SunOS
   does.

   The treatment of the number_separator (compare add_line_number):
   The default value TAB of the number_separator (`-n[SEP[...]]') doesn't
   be thought to be an input character. An optional `-e'-input has no
   effect.
   -  With single column output
      only one POSIX requirement has to be met:
   The default n-separator should be a TAB. The consequence is a
   different width between the number and the text if the output position
   of the separator changes, i.e. it depends upon the left margin used.
   That's not nice but easy-to-use together with the defaults of other
   utilities, e.g. sort or cut. - Same as SunOS does.
   -  With multicolumn output
      two conflicting POSIX requirements exist:
   First `default n-separator is TAB', second `output text columns shall
   be of equal width'. Moreover POSIX specifies the number+separator a
   part of the column, together with `-COLUMN' and `-a -COLUMN'.
   (With -m output the number shall occupy each line only once. Exactly
   the same situation as single column output exists.)
      GNU pr gives priority to the 2nd requirement and observes POSIX
   column definition. The n-separator TAB is expanded to the same number
   of spaces in each column using the default value 8. Tabification is
   only performed if it is compatible with the output position.
   Consequence: The output text columns are of equal width. The layout
   of a page does not change if the left margin varies. - Looks better
   than the SunOS approach.
      SunOS pr gives priority to the 1st requirement. n-separator TAB
   width varies with each column. Only the width of text part of the
   column is fixed.
   Consequence: The output text columns don't have equal width. The
   widths and the layout of the whole page varies with the left margin.
   An overflow of the line length (without margin) over the input value
   PAGE_WIDTH may occur.

   The interference of the POSIX-compliant small letter options -w and -s:
   (`interference' means `setting a _separator_ with -s switches off the
   column sturctur and the default - not generally - page_width,
   acts on -w option')
       options:       text form  / separator:     equivalent new options:
       -w l   -s[x]
    --------------------------------------------------------------------
    1.  --     --     columns    / space          --
                      trunc. to page_width = 72
    2.  --    -s[:]   full lines / TAB[:]         -J  --sep-string[="<TAB>"|:]
                      no truncation
    3.  -w l   --     columns    / space          -W l
                      trunc. to page_width = l
    4.  -w l  -s[:]   columns    / no sep.[:]     -W l  --sep-string[=:]
                      trunc. to page_width = l
    --------------------------------------------------------------------


   Options:

   Including version 1.22i:
   Some SMALL LETTER options has been redefined with the object of a
   better POSIX compliance. The output of some further cases has been
   adapted to other UNIXes. A violation of downward compatibility has to
   be accepted.
   Some NEW CAPITAL LETTER options ( -J, -S, -W) has been introduced to
   turn off unexpected interferences of small letter options (-s and -w
   together with the three column options).
   -N option and the second argument LAST_PAGE of +FIRST_PAGE offer more
   flexibility; The detailed handling of form feeds set in the input
   files requires -T option.

   Capital letter options dominate small letter ones.

   Some of the option-arguments cannot be specified as separate arguments
   from the preceding option letter (already stated in POSIX specification).

   Form feeds in the input cause page breaks in the output. Multiple
   form feeds produce empty pages.

   +FIRST_PAGE[:LAST_PAGE], --pages=FIRST_PAGE[:LAST_PAGE]
        begin [stop] printing with page FIRST_[LAST_]PAGE

   -COLUMN, --columns=COLUMN
        Produce output that is COLUMN columns wide and
        print columns down, unless -a is used. Balance number of
        lines in the columns on each page.

   -a, --across     Print columns across rather than down, used
        together with -COLUMN. The input
        one
        two
        three
        four
        will be printed with `-a -3' as
        one two three
        four

   -b       Balance columns on the last page.
        -b is no longer an independent option. It's always used
        together with -COLUMN (unless -a is used) to get a
        consistent formulation with "FF set by hand" in input
        files. Each formfeed found terminates the number of lines
        to be read with the actual page. The situation for
        printing columns down is equivalent to that on the last
        page. So we need a balancing.

        Keeping -b as an underground option guarantees some
        downward compatibility. Utilities using pr with -b
        (a most frequently used form) still work as usual.

   -c, --show-control-chars
        Print unprintable characters as control prefixes.
        Control-g is printed as ^G (use hat notation) and
        octal backslash notation.

   -d, --double-space   Double space the output.

   -D FORMAT, --date-format=FORMAT  Use FORMAT for the header date.

   -e[CHAR[WIDTH]], --expand-tabs[=CHAR[WIDTH]]
        Expand tabs to spaces on input.  Optional argument CHAR
        is the input TAB character. (Default is TAB).  Optional
        argument WIDTH is the input TAB character's width.
        (Default is 8.)

   -F, -f, --form-feed  Use formfeeds instead of newlines to separate
        pages. A three line HEADER is used, no TRAILER with -F,
        without -F both HEADER and TRAILER are made of five lines.

   -h HEADER, --header=HEADER
        Replace the filename in the header with the string HEADER.
        A centered header is used.

   -i[CHAR[WIDTH]], --output-tabs[=CHAR[WIDTH]]
        Replace spaces with tabs on output.  Optional argument
        CHAR is the output TAB character. (Default is TAB).
        Optional argument WIDTH is the output TAB character's
        width. (Default is 8)

   -J, --join-lines Merge lines of full length, turns off -W/-w
        line truncation, no column alignment, --sep-string[=STRING]
        sets separators, works with all column options
        (-COLUMN | -a -COLUMN | -m).
        -J has been introduced (together with -W and --sep-string) to
        disentangle the old (POSIX compliant) options -w, -s
        along with the 3 column options.

   -l PAGE_LENGTH, --length=PAGE_LENGTH
        Set the page length to PAGE_LENGTH lines. Default is 66,
        including 5 lines of HEADER and 5 lines of TRAILER
        without -F, but only 3 lines of HEADER and no TRAILER
        with -F (i.e the number of text lines defaults to 56 or
        63 respectively).

   -m, --merge      Print files in parallel; pad_across_to align
        columns; truncate lines and print separator strings;
        Do it also with empty columns to get a continuous line
        numbering and column marking by separators throughout
        the whole merged file.

        Empty pages in some input files produce empty columns
        [marked by separators] in the merged pages. Completely
        empty merged pages show no column separators at all.

        The layout of a merged page is ruled by the largest form
        feed distance of the single pages at that page. Shorter
        columns will be filled up with empty lines.

        Together with -J option join lines of full length and
        set separators when -S option is used.

   -n[SEP[DIGITS]], --number-lines[=SEP[DIGITS]]
        Provide DIGITS digit line numbering (default for DIGITS
        is 5). With multicolumn output the number occupies the
        first DIGITS column positions of each text column or only
        each line of -m output.
        With single column output the number precedes each line
        just as -m output.
        Optional argument SEP is the character appended to the
        line number to separate it from the text followed.
        The default separator is a TAB. In a strict sense a TAB
        is always printed with single column output only. The
        TAB-width varies with the TAB-position, e.g. with the
        left margin specified by -o option.
        With multicolumn output priority is given to `equal width
        of output columns' (a POSIX specification). The TAB-width
        is fixed to the value of the 1st column and does not
        change with different values of left margin. That means a
        fixed number of spaces is always printed in the place of
        a TAB. The tabification depends upon the output
        position.

        Default counting of the line numbers starts with 1st
        line of the input file (not the 1st line printed,
        compare the --page option and -N option).

   -N NUMBER, --first-line-number=NUMBER
        Start line counting with the number NUMBER at the 1st
        line of first page printed (mostly not the 1st line of
        the input file).

   -o MARGIN, --indent=MARGIN
        Offset each line with a margin MARGIN spaces wide.
        Total page width is the size of the margin plus the
        PAGE_WIDTH set with -W/-w option.

   -r, --no-file-warnings
        Omit warning when a file cannot be opened.

   -s[CHAR], --separator[=CHAR]
        Separate columns by a single character CHAR, default for
        CHAR is the TAB character without -w and 'no char' with -w.
        Without `-s' default separator `space' is set.
        -s[CHAR] turns off line truncation of all 3 column options
        (-COLUMN|-a -COLUMN|-m) except -w is set. That is a POSIX
        compliant formulation. The source code translates -s into
        the new options -S and -J, also -W if required.

   -S STRING, --sep-string[=STRING]
        Separate columns by any string STRING. The -S option
        doesn't react upon the -W/-w option (unlike -s option
        does). It defines a separator nothing else.
        Without -S: Default separator TAB is used with -J and
        `space' otherwise (same as -S" ").
        With -S "": No separator is used.
        Quotes should be used with blanks and some shell active
        characters.
        -S is problematic because in its obsolete form you
        cannot use -S "STRING", but in its standard form you
        must use -S "STRING" if STRING is empty.  Use
        --sep-string to avoid the ambiguity.

   -t, --omit-header    Do not print headers or footers but retain form
        feeds set in the input files.

   -T, --omit-pagination
        Do not print headers or footers, eliminate any pagination
        by form feeds set in the input files.

   -v, --show-nonprinting
        Print unprintable characters as escape sequences. Use
        octal backslash notation. Control-G becomes \007.

   -w PAGE_WIDTH, --width=PAGE_WIDTH
        Set page width to PAGE_WIDTH characters for multiple
        text-column output only (default for PAGE_WIDTH is 72).
        -s[CHAR] turns off the default page width and any line
        truncation. Lines of full length will be merged,
        regardless of the column options set. A POSIX compliant
        formulation.

   -W PAGE_WIDTH, --page-width=PAGE_WIDTH
        Set the page width to PAGE_WIDTH characters. That's valid
        with and without a column option. Text lines will be
        truncated, unless -J is used. Together with one of the
        column options (-COLUMN| -a -COLUMN| -m) column alignment
        is always used.
        Default is 72 characters.
        Without -W PAGE_WIDTH
        - but with one of the column options default truncation of
          72 characters is used (to keep downward compatibility
          and to simplify most frequently met column tasks).
          Column alignment and column separators are used.
        - and without any of the column options NO line truncation
          is used (to keep downward compatibility and to meet most
          frequent tasks). That's equivalent to  -W 72 -J .

            With/without  -W PAGE_WIDTH  the header line is always
        truncated to avoid line overflow.

        (In pr versions newer than 1.14 -S option does no longer
        affect -W option.)

*/



#include <config.h>

#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include <time.h>
#include "system.h"
#include "closeout.h"
#include "error.h"
#include "mbswidth.h"
#include "posixver.h"
#include "xstrtol.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "pr"

#define AUTHORS N_ ("Pete TerMaat and Roland Huebner")

#ifndef TRUE
# define TRUE   1
# define FALSE  0
#endif

/* Used with start_position in the struct COLUMN described below.
   If start_position == ANYWHERE, we aren't truncating columns and
   can begin printing a column anywhere.  Otherwise we must pad to
   the horizontal position start_position. */
#define ANYWHERE    0

/* Each column has one of these structures allocated for it.
   If we're only dealing with one file, fp is the same for all
   columns.

   The general strategy is to spend time setting up these column
   structures (storing columns if necessary), after which printing
   is a matter of flitting from column to column and calling
   print_func.

   Parallel files, single files printing across in multiple
   columns, and single files printing down in multiple columns all
   fit the same printing loop.

   print_func       Function used to print lines in this column.
            If we're storing this column it will be
            print_stored(), Otherwise it will be read_line().

   char_func        Function used to process characters in this column.
            If we're storing this column it will be store_char(),
            otherwise it will be print_char().

   current_line     Index of the current entry in line_vector, which
            contains the index of the first character of the
            current line in buff[].

   lines_stored     Number of lines in this column which are stored in
            buff.

   lines_to_print   If we're storing this column, lines_to_print is
            the number of stored_lines which remain to be
            printed.  Otherwise it is the number of lines
            we can print without exceeding lines_per_body.

   start_position   The horizontal position we want to be in before we
            print the first character in this column.

   numbered     True means precede this column with a line number. */

struct COLUMN;
struct COLUMN
  {
    FILE *fp;           /* Input stream for this column. */
    char const *name;       /* File name. */
    enum
      {
    OPEN,
    FF_FOUND,       /* used with -b option, set with \f, changed
                   to ON_HOLD after print_header */
    ON_HOLD,        /* Hit a form feed. */
    CLOSED
      }
    status;         /* Status of the file pointer. */

    /* Func to print lines in this col. */
    int (*print_func) (struct COLUMN *);

    /* Func to print/store chars in this col. */
    void (*char_func) (int);

    int current_line;       /* Index of current place in line_vector. */
    int lines_stored;       /* Number of lines stored in buff. */
    int lines_to_print;     /* No. lines stored or space left on page. */
    int start_position;     /* Horizontal position of first char. */
    int numbered;
    int full_page_printed;  /* True means printed without a FF found. */

    /* p->full_page_printed  controls a special case of "FF set by hand":
       True means a full page has been printed without FF found. To avoid an
       additional empty page we have to ignore a FF immediately following in
       the next line. */
  };

typedef struct COLUMN COLUMN;

#define NULLCOL (COLUMN *)0

static int char_to_clump (int c);
static int read_line (COLUMN *p);
static int print_page (void);
static int print_stored (COLUMN *p);
static int open_file (char *name, COLUMN *p);
static int skip_to_page (int page);
static void print_header (void);
static void pad_across_to (int position);
static void add_line_number (COLUMN *p);
static void getoptarg (char *arg, char switch_char, char *character,
               int *number);
void usage (int status);
static void print_files (int number_of_files, char **av);
static void init_parameters (int number_of_files);
static void init_header (char *filename, int desc);
static int init_fps (int number_of_files, char **av);
static void init_funcs (void);
static void init_store_cols (void);
static void store_columns (void);
static void balance (int total_stored);
static void store_char (int c);
static void pad_down (int lines);
static void read_rest_of_line (COLUMN *p);
static void skip_read (COLUMN *p, int column_number);
static void print_char (int c);
static void cleanup (void);
static void first_last_page (char *pages);
static void print_sep_string (void);
static void separator_string (const char *optarg_S);

/* The name under which this program was invoked. */
char *program_name;

/* All of the columns to print.  */
static COLUMN *column_vector;

/* When printing a single file in multiple downward columns,
   we store the leftmost columns contiguously in buff.
   To print a line from buff, get the index of the first character
   from line_vector[i], and print up to line_vector[i + 1]. */
static char *buff;

/* Index of the position in buff where the next character
   will be stored. */
static int buff_current;

/* The number of characters in buff.
   Used for allocation of buff and to detect overflow of buff. */
static int buff_allocated;

/* Array of indices into buff.
   Each entry is an index of the first character of a line.
   This is used when storing lines to facilitate shuffling when
   we do column balancing on the last page. */
static int *line_vector;

/* Array of horizonal positions.
   For each line in line_vector, end_vector[line] is the horizontal
   position we are in after printing that line.  We keep track of this
   so that we know how much we need to pad to prepare for the next
   column. */
static int *end_vector;

/* (-m) True means we're printing multiple files in parallel. */
static int parallel_files = FALSE;

/* (-m) True means a line starts with some empty columns (some files
   already CLOSED or ON_HOLD) which we have to align. */
static int align_empty_cols;

/* (-m) True means we have not yet found any printable column in a line.
   align_empty_cols = TRUE  has to be maintained. */
static int empty_line;

/* (-m) False means printable column output precedes a form feed found.
   Column alignment is done only once. No additional action with that form
   feed.
   True means we found only a form feed in a column. Maybe we have to do
   some column alignment with that form feed. */
static int FF_only;

/* (-[0-9]+) True means we're given an option explicitly specifying
   number of columns.  Used to detect when this option is used with -m
   and when translating old options to new/long options. */
static int explicit_columns = FALSE;

/* (-t|-T) False means we aren't printing headers and footers. */
static int extremities = TRUE;

/* (-t) True means we retain all FF set by hand in input files.
   False is set with -T option. */
static int keep_FF = FALSE;
static int print_a_FF = FALSE;

/* True means we need to print a header as soon as we know we've got input
   to print after it. */
static int print_a_header;

/* (-f) True means use formfeeds instead of newlines to separate pages. */
static int use_form_feed = FALSE;

/* True means we have read the standard input. */
static int have_read_stdin = FALSE;

/* True means the -a flag has been given. */
static int print_across_flag = FALSE;

/* True means we're printing one file in multiple (>1) downward columns. */
static int storing_columns = TRUE;

/* (-b) True means balance columns on the last page as Sys V does. */
/* That's no longer an independent option. With storing_columns = TRUE
   balance_columns = TRUE is used too (s. function init_parameters).
   We get a consistent formulation with "FF set by hand" in input files. */
static int balance_columns = FALSE;

/* (-l) Number of lines on a page, including header and footer lines. */
static int lines_per_page = 66;

/* Number of lines in the header and footer can be reset to 0 using
   the -t flag. */
static int lines_per_header = 5;
static int lines_per_body;
static int lines_per_footer = 5;

/* (-w|-W) Width in characters of the page.  Does not include the width of
   the margin. */
static int chars_per_line = 72;

/* (-w|W) True means we truncate lines longer than chars_per_column. */
static int truncate_lines = FALSE;

/* (-J) True means we join lines without any line truncation. -J
   dominates -w option. */
static int join_lines = FALSE;

/* Number of characters in a column.  Based on col_sep_length and
   page width. */
static int chars_per_column;

/* (-e) True means convert tabs to spaces on input. */
static int untabify_input = FALSE;

/* (-e) The input tab character. */
static char input_tab_char = '\t';

/* (-e) Tabstops are at chars_per_tab, 2*chars_per_tab, 3*chars_per_tab, ...
   where the leftmost column is 1. */
static int chars_per_input_tab = 8;

/* (-i) True means convert spaces to tabs on output. */
static int tabify_output = FALSE;

/* (-i) The output tab character. */
static char output_tab_char = '\t';

/* (-i) The width of the output tab. */
static int chars_per_output_tab = 8;

/* Keeps track of pending white space.  When we hit a nonspace
   character after some whitespace, we print whitespace, tabbing
   if necessary to get to output_position + spaces_not_printed. */
static int spaces_not_printed;

/* (-o) Number of spaces in the left margin (tabs used when possible). */
static int chars_per_margin = 0;

/* Position where the next character will fall.
   Leftmost position is 0 + chars_per_margin.
   Rightmost position is chars_per_margin + chars_per_line - 1.
   This is important for converting spaces to tabs on output. */
static int output_position;

/* Horizontal position relative to the current file.
   (output_position depends on where we are on the page;
   input_position depends on where we are in the file.)
   Important for converting tabs to spaces on input. */
static int input_position;

/* Count number of failed opens so we can exit with nonzero
   status if there were any.  */
static int failed_opens = 0;

/* The number of spaces taken up if we print a tab character with width
   c_ from position h_. */
#define TAB_WIDTH(c_, h_) ((c_) - ((h_) % (c_)))

/* The horizontal position we'll be at after printing a tab character
   of width c_ from the position h_. */
#define POS_AFTER_TAB(c_, h_) ((h_) + TAB_WIDTH (c_, h_))

/* (-NNN) Number of columns of text to print. */
static int columns = 1;

/* (+NNN:MMM) Page numbers on which to begin and stop printing.
   first_page_number = 0  will be used to check input only. */
static int first_page_number = 0;
static int last_page_number = 0;

/* Number of files open (not closed, not on hold). */
static int files_ready_to_read = 0;

/* Current page number.  Displayed in header. */
static int page_number;

/* Current line number.  Displayed when -n flag is specified.

   When printing files in parallel (-m flag), line numbering is as follows:
   1    foo     goo     moo
   2    hoo     too     zoo

   When printing files across (-a flag), ...
   1    foo     2       moo     3       goo
   4    hoo     5       too     6       zoo

   Otherwise, line numbering is as follows:
   1    foo     3       goo     5       too
   2    moo     4       hoo     6       zoo */
static int line_number;

/* With line_number overflow, we use power_10 to cut off the higher-order
   digits of the line_number */
static int power_10;

/* (-n) True means lines should be preceded by numbers. */
static int numbered_lines = FALSE;

/* (-n) Character which follows each line number. */
static char number_separator = '\t';

/* (-n) line counting starts with 1st line of input file (not with 1st
   line of 1st page printed). */
static int line_count = 1;

/* (-n) True means counting of skipped lines starts with 1st line of
   input file. False means -N option is used in addition, counting of
   skipped lines not required. */
static int skip_count = TRUE;

/* (-N) Counting starts with start_line_number = NUMBER at 1st line of
   first page printed, usually not 1st page of input file. */
static int start_line_num = 1;

/* (-n) Width in characters of a line number. */
static int chars_per_number = 5;

/* Used when widening the first column to accommodate numbers -- only
   needed when printing files in parallel.  Includes width of both the
   number and the number_separator. */
static int number_width;

/* Buffer sprintf uses to format a line number. */
static char *number_buff;

/* (-v) True means unprintable characters are printed as escape sequences.
   control-g becomes \007. */
static int use_esc_sequence = FALSE;

/* (-c) True means unprintable characters are printed as control prefixes.
   control-g becomes ^G. */
static int use_cntrl_prefix = FALSE;

/* (-d) True means output is double spaced. */
static int double_space = FALSE;

/* Number of files opened initially in init_files.  Should be 1
   unless we're printing multiple files in parallel. */
static int total_files = 0;

/* (-r) True means don't complain if we can't open a file. */
static int ignore_failed_opens = FALSE;

/* (-S) True means we separate columns with a specified string.
   -S option does not affect line truncation nor column alignment. */
static int use_col_separator = FALSE;

/* String used to separate columns if the -S option has been specified.
   Default without -S but together with one of the column options
   -a|COLUMN|-m is a `space' and with the -J option a `tab'. */
static char *col_sep_string = "";
static int col_sep_length = 0;
static char *column_separator = " ";
static char *line_separator = "\t";

/* Number of separator characters waiting to be printed as soon as we
   know that we have any input remaining to be printed. */
static int separators_not_printed;

/* Position we need to pad to, as soon as we know that we have input
   remaining to be printed. */
static int padding_not_printed;

/* True means we should pad the end of the page.  Remains false until we
   know we have a page to print. */
static int pad_vertically;

/* (-h) String of characters used in place of the filename in the header. */
static char *custom_header;

/* (-D) Date format for the header.  */
static char const *date_format;

/* Date and file name for the header.  */
static char *date_text;
static char const *file_text;

/* Output columns available, not counting the date and file name.  */
static int header_width_available;

static int *clump_buff;

/* True means we read the line no. lines_per_body in skip_read
   called by skip_to_page. That variable controls the coincidence of a
   "FF set by hand" and "full_page_printed", see above the definition of
   structure COLUMN. */
static int last_line = FALSE;

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  COLUMNS_OPTION = CHAR_MAX + 1,
  PAGES_OPTION
};

#define COMMON_SHORT_OPTIONS \
    "-0123456789D:FJN:TW:abcde::fh:i::l:mn::o:rs::tvw:"

static struct option const long_options[] =
{
  {"pages", required_argument, NULL, PAGES_OPTION},
  {"columns", required_argument, NULL, COLUMNS_OPTION},
  {"across", no_argument, NULL, 'a'},
  {"show-control-chars", no_argument, NULL, 'c'},
  {"double-space", no_argument, NULL, 'd'},
  {"date-format", required_argument, NULL, 'D'},
  {"expand-tabs", optional_argument, NULL, 'e'},
  {"form-feed", no_argument, NULL, 'f'},
  {"header", required_argument, NULL, 'h'},
  {"output-tabs", optional_argument, NULL, 'i'},
  {"join-lines", no_argument, NULL, 'J'},
  {"length", required_argument, NULL, 'l'},
  {"merge", no_argument, NULL, 'm'},
  {"number-lines", optional_argument, NULL, 'n'},
  {"first-line-number", required_argument, NULL, 'N'},
  {"indent", required_argument, NULL, 'o'},
  {"no-file-warnings", no_argument, NULL, 'r'},
  {"separator", optional_argument, NULL, 's'},
  {"sep-string", optional_argument, NULL, 'S'},
  {"omit-header", no_argument, NULL, 't'},
  {"omit-pagination", no_argument, NULL, 'T'},
  {"show-nonprinting", no_argument, NULL, 'v'},
  {"width", required_argument, NULL, 'w'},
  {"page-width", required_argument, NULL, 'W'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {0, 0, 0, 0}
};

/* Return the number of columns that have either an open file or
   stored lines. */

static int
cols_ready_to_print (void)
{
  COLUMN *q;
  int i;
  int n;

  n = 0;
  for (q = column_vector, i = 0; i < columns; ++q, ++i)
    if (q->status == OPEN ||
    q->status == FF_FOUND ||    /* With -b: To print a header only */
    (storing_columns && q->lines_stored > 0 && q->lines_to_print > 0))
      ++n;
  return n;
}

/* Estimate first_ / last_page_number
   using option +FIRST_PAGE:LAST_PAGE */

static void
first_last_page (char *pages)
{
  char *str1;

  if (*pages == ':')
    {
      error (0, 0, _("`--pages' invalid range of page numbers: `%s'"), pages);
      usage (2);
    }

  str1 = strchr (pages, ':');
  if (str1 != NULL)
    *str1 = '\0';

  {
    long int tmp_long;
    if (xstrtol (pages, NULL, 10, &tmp_long, "") != LONGINT_OK
    || tmp_long < 1 || tmp_long > INT_MAX)
      error (EXIT_FAILURE, 0, _("`--pages' invalid starting page number: `%s'"),
         pages);
    first_page_number = (int) tmp_long;
  }

  if (str1 == NULL)
    return;

  {
    long int tmp_long;
    if (xstrtol (str1 + 1, NULL, 10, &tmp_long, "") != LONGINT_OK
    || tmp_long <= 0 || tmp_long > INT_MAX)
      error (EXIT_FAILURE, 0, _("`--pages' invalid ending page number: `%s'"),
         str1 + 1);
    last_page_number = (int) tmp_long;
  }

  if (first_page_number > last_page_number)
    error (EXIT_FAILURE, 0,
    _("`--pages' starting page number is larger than ending page number"));
}

/* Estimate length of col_sep_string with option -S.  */

static void
separator_string (const char *optarg_S)
{
  col_sep_length = (int) strlen (optarg_S);
  col_sep_string = (char *) xmalloc (col_sep_length + 1);
  strcpy (col_sep_string, optarg_S);
}

int
main (int argc, char **argv)
{
  int c;
  int accum = 0;
  int n_files;
  int old_options = FALSE;
  int old_w = FALSE;
  int old_s = FALSE;
  char **file_names;
  char const *short_options = (posix2_version () < 200112
                   ? COMMON_SHORT_OPTIONS "S::"
                   : COMMON_SHORT_OPTIONS "S:");

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  n_files = 0;
  file_names = (argc > 1
        ? (char **) xmalloc ((argc - 1) * sizeof (char *))
        : NULL);

  while ((c = getopt_long (argc, argv, short_options, long_options, NULL))
     != -1)
    {
      if (ISDIGIT (c))
    {
      accum = accum * 10 + c - '0';
      columns = accum;
      explicit_columns = TRUE;
      continue;
    }

      if (accum > 0)            /* reset for subsequent params */
    accum = 0;

      switch (c)
    {
    case 0:         /* getopt long option */
      break;

    case 1:         /* Non-option argument. */
      if (*optarg == '+')
        {
          /* long option --page dominates old `+FIRST_PAGE ...' */
          if (first_page_number <= 0 && last_page_number <= 0)
        first_last_page (optarg);
        }
      else
        file_names[n_files++] = optarg;
      break;

    case PAGES_OPTION:  /* --pages=FIRST_PAGE[:LAST_PAGE] */
      {         /* dominates old opt +... */
        if (optarg)
          first_last_page (optarg);
        else
          error (EXIT_FAILURE, 0,
             _("`--pages=FIRST_PAGE[:LAST_PAGE]' missing argument"));
        break;
      }

    case COLUMNS_OPTION:    /* --columns=COLUMN */
      {
        long int tmp_long;
        if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
        || tmp_long <= 0 || tmp_long > INT_MAX)
          {
        error (EXIT_FAILURE, 0,
               _("`--columns=COLUMN' invalid number of columns: `%s'"),
               optarg);
          }
        columns = (int) tmp_long;
        break;
      }

    case 'a':
      print_across_flag = TRUE;
      storing_columns = FALSE;
      break;
    case 'b':
      balance_columns = TRUE;
      break;
    case 'c':
      use_cntrl_prefix = TRUE;
      break;
    case 'd':
      double_space = TRUE;
      break;
    case 'D':
      date_format = optarg;
      break;
    case 'e':
      if (optarg)
        getoptarg (optarg, 'e', &input_tab_char,
               &chars_per_input_tab);
      /* Could check tab width > 0. */
      untabify_input = TRUE;
      break;
    case 'f':
    case 'F':
      use_form_feed = TRUE;
      break;
    case 'h':
      custom_header = optarg;
      break;
    case 'i':
      if (optarg)
        getoptarg (optarg, 'i', &output_tab_char,
               &chars_per_output_tab);
      /* Could check tab width > 0. */
      tabify_output = TRUE;
      break;
    case 'J':
      join_lines = TRUE;
      break;
    case 'l':
      {
        long int tmp_long;
        if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
        || tmp_long <= 0 || tmp_long > INT_MAX)
          {
        error (EXIT_FAILURE, 0,
               _("`-l PAGE_LENGTH' invalid number of lines: `%s'"),
               optarg);
          }
        lines_per_page = (int) tmp_long;
        break;
      }
    case 'm':
      parallel_files = TRUE;
      storing_columns = FALSE;
      break;
    case 'n':
      numbered_lines = TRUE;
      if (optarg)
        getoptarg (optarg, 'n', &number_separator,
               &chars_per_number);
      break;
    case 'N':
      skip_count = FALSE;
      {
        long int tmp_long;
        if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
        || tmp_long > INT_MAX)
          {
        error (EXIT_FAILURE, 0,
               _("`-N NUMBER' invalid starting line number: `%s'"),
               optarg);
          }
        start_line_num = (int) tmp_long;
        break;
      }
    case 'o':
      {
        long int tmp_long;
        if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
        || tmp_long < 0 || tmp_long > INT_MAX)
          error (EXIT_FAILURE, 0,
             _("`-o MARGIN' invalid line offset: `%s'"), optarg);
        chars_per_margin = (int) tmp_long;
        break;
      }
    case 'r':
      ignore_failed_opens = TRUE;
      break;
    case 's':
      old_options = TRUE;
      old_s = TRUE;
      if (!use_col_separator && optarg)
        separator_string (optarg);
      break;
    case 'S':
      old_s = FALSE;
      /* Reset an additional input of -s, -S dominates -s */
      col_sep_string = "";
      col_sep_length = 0;
      use_col_separator = TRUE;
      if (optarg)
        separator_string (optarg);
      break;
    case 't':
      extremities = FALSE;
      keep_FF = TRUE;
      break;
    case 'T':
      extremities = FALSE;
      keep_FF = FALSE;
      break;
    case 'v':
      use_esc_sequence = TRUE;
      break;
    case 'w':
      old_options = TRUE;
      old_w = TRUE;
      {
        long int tmp_long;
        if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
            || tmp_long <= 0 || tmp_long > INT_MAX)
          error (EXIT_FAILURE, 0,
             _("`-w PAGE_WIDTH' invalid number of characters: `%s'"), optarg);
        if (!truncate_lines)  chars_per_line = (int) tmp_long;
        break;
      }
    case 'W':
      old_w = FALSE;            /* dominates -w */
      truncate_lines = TRUE;
      {
        long int tmp_long;
        if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
        || tmp_long <= 0 || tmp_long > INT_MAX)
          error (EXIT_FAILURE, 0,
         _("`-W PAGE_WIDTH' invalid number of characters: `%s'"), optarg);
        chars_per_line = (int) tmp_long;
        break;
      }
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (2);
      break;
    }
    }

  if (! date_format)
    date_format = (getenv ("POSIXLY_CORRECT")
           ? dcgettext (NULL, "%b %e %H:%M %Y", LC_TIME)
           : "%Y-%m-%d %H:%M");

  /* Now we can set a reasonable initial value: */
  if (first_page_number == 0)
    first_page_number = 1;

  if (parallel_files && explicit_columns)
    error (EXIT_FAILURE, 0,
     _("Cannot specify number of columns when printing in parallel."));

  if (parallel_files && print_across_flag)
    error (EXIT_FAILURE, 0,
       _("Cannot specify both printing across and printing in parallel."));

/* Translate some old short options to new/long options.
   To meet downward compatibility with other UNIX pr utilities
   and some POSIX specifications. */

  if (old_options)
    {
      if (old_w)
        {
          if (parallel_files || explicit_columns)
        {
          /* activate -W */
          truncate_lines = TRUE;
          if (old_s)
            /* adapt HP-UX and SunOS: -s = no separator;
               activate -S */
            use_col_separator = TRUE;
        }
      else
        /* old -w sets width with columns only
           activate -J */
        join_lines = TRUE;
    }
      else if (!use_col_separator)
        {
      /* No -S option read */
          if (old_s && (parallel_files || explicit_columns))
        {
          if (!truncate_lines)
        {
          /* old -s (without -w and -W) annuls column alignment,
          uses fields, activate -J */
          join_lines = TRUE;
          if (col_sep_length > 0)
            /* activate -S */
            use_col_separator = TRUE;
        }
          else
        /* with -W */
        /* adapt HP-UX and SunOS: -s = no separator;
           activate -S */
        use_col_separator = TRUE;
        }
    }
    }

  for (; optind < argc; optind++)
    {
      file_names[n_files++] = argv[optind];
    }

  if (n_files == 0)
    {
      /* No file arguments specified;  read from standard input.  */
      print_files (0, NULL);
    }
  else
    {
      if (parallel_files)
    print_files (n_files, file_names);
      else
    {
      int i;
      for (i = 0; i < n_files; i++)
        print_files (1, &file_names[i]);
    }
    }

  cleanup ();

  if (have_read_stdin && fclose (stdin) == EOF)
    error (EXIT_FAILURE, errno, _("standard input"));
  if (failed_opens > 0)
    exit (EXIT_FAILURE);
  exit (EXIT_SUCCESS);
}

/* Parse options of the form -scNNN.

   Example: -nck, where 'n' is the option, c is the optional number
   separator, and k is the optional width of the field used when printing
   a number. */

static void
getoptarg (char *arg, char switch_char, char *character, int *number)
{
  if (!ISDIGIT (*arg))
    *character = *arg++;
  if (*arg)
    {
      long int tmp_long;
      if (xstrtol (arg, NULL, 10, &tmp_long, "") != LONGINT_OK
      || tmp_long <= 0 || tmp_long > INT_MAX)
    {
      error (0, 0,
         _("`-%c' extra characters or invalid number in the argument: `%s'"),
         switch_char, arg);
      usage (2);
    }
      *number = (int) tmp_long;
    }
}


/* Set parameters related to formatting. */

static void
init_parameters (int number_of_files)
{
  int chars_used_by_number = 0;

  if (use_form_feed)
    {
      lines_per_header = 3;
      lines_per_footer = 0;
    }

  lines_per_body = lines_per_page - lines_per_header - lines_per_footer;
  if (lines_per_body <= 0)
    {
      extremities = FALSE;
      keep_FF = TRUE;
    }
  if (extremities == FALSE)
    lines_per_body = lines_per_page;

  if (double_space)
    lines_per_body = lines_per_body / 2;

  /* If input is stdin, cannot print parallel files.  BSD dumps core
     on this. */
  if (number_of_files == 0)
    parallel_files = FALSE;

  if (parallel_files)
    columns = number_of_files;

  /* One file, multi columns down: -b option is set to get a consistent
     formulation with "FF set by hand" in input files. */
  if (storing_columns)
    balance_columns = TRUE;

  /* Tabification is assumed for multiple columns. */
  if (columns > 1)
    {
      if (!use_col_separator)
    {
      /* Use default separator */
      if (join_lines)
        col_sep_string = line_separator;
      else
        col_sep_string = column_separator;

      col_sep_length = 1;
      use_col_separator = TRUE;
    }
      /* It's rather pointless to define a TAB separator with column
         alignment */
      else if (!join_lines && *col_sep_string == '\t')
        col_sep_string = column_separator;

      truncate_lines = TRUE;
      untabify_input = TRUE;
      tabify_output = TRUE;
    }
  else
    storing_columns = FALSE;

  /* -J dominates -w in any case */
  if (join_lines)
    truncate_lines = FALSE;

  if (numbered_lines)
    {
      int tmp_i;
      int chars_per_default_tab = 8;

      line_count = start_line_num;

      /* To allow input tab-expansion (-e sensitive) use:
     if (number_separator == input_tab_char)
       number_width = chars_per_number +
         TAB_WIDTH (chars_per_input_tab, chars_per_number);   */

      /* Estimate chars_per_text without any margin and keep it constant. */
      if (number_separator == '\t')
    number_width = chars_per_number +
      TAB_WIDTH (chars_per_default_tab, chars_per_number);
      else
    number_width = chars_per_number + 1;

      /* The number is part of the column width unless we are
     printing files in parallel. */
      if (parallel_files)
    chars_used_by_number = number_width;

      /* We use power_10 to cut off the higher-order digits of the
         line_number in function add_line_number */
      tmp_i = chars_per_number;
      for (power_10 = 1; tmp_i > 0; --tmp_i)
        power_10 = 10 * power_10;
    }

  chars_per_column = (chars_per_line - chars_used_by_number -
             (columns - 1) * col_sep_length) / columns;

  if (chars_per_column < 1)
    error (EXIT_FAILURE, 0, _("page width too narrow"));

  if (numbered_lines)
    {
      if (number_buff != NULL)
    free (number_buff);
      number_buff = (char *) xmalloc (2 * chars_per_number);
    }

  /* Pick the maximum between the tab width and the width of an
     escape sequence.
     The width of an escape sequence (4) isn't the lower limit any longer.
     We've to use 8 as the lower limit, if we use chars_per_default_tab = 8
     to expand a tab which is not an input_tab-char. */
  if (clump_buff != NULL)
    free (clump_buff);
  clump_buff = (int *) xmalloc ((chars_per_input_tab > 8
                ? chars_per_input_tab : 8) * sizeof (int));
}


/* Open the necessary files,
   maintaining a COLUMN structure for each column.

   With multiple files, each column p has a different p->fp.
   With single files, each column p has the same p->fp.
   Return 1 if (number_of_files > 0) and no files can be opened,
   0 otherwise.

   With each column/file p, p->full_page_printed is initialized,
   see also open_file.  */

static int
init_fps (int number_of_files, char **av)
{
  int i, files_left;
  COLUMN *p;
  FILE *firstfp;
  char const *firstname;

  total_files = 0;

  if (column_vector != NULLCOL)
    free ((char *) column_vector);
  column_vector = (COLUMN *) xmalloc (columns * sizeof (COLUMN));

  if (parallel_files)
    {
      files_left = number_of_files;
      for (p = column_vector; files_left--; ++p, ++av)
    {
      if (open_file (*av, p) == 0)
        {
          --p;
          --columns;
        }
    }
      if (columns == 0)
    return 1;
      init_header ("", -1);
    }
  else
    {
      p = column_vector;
      if (number_of_files > 0)
    {
      if (open_file (*av, p) == 0)
        return 1;
      init_header (*av, fileno (p->fp));
      p->lines_stored = 0;
    }
      else
    {
      p->name = _("standard input");
      p->fp = stdin;
      have_read_stdin = TRUE;
      p->status = OPEN;
      p->full_page_printed = FALSE;
      ++total_files;
      init_header ("", -1);
      p->lines_stored = 0;
    }

      firstname = p->name;
      firstfp = p->fp;
      for (i = columns - 1, ++p; i; --i, ++p)
    {
      p->name = firstname;
      p->fp = firstfp;
      p->status = OPEN;
      p->full_page_printed = FALSE;
      p->lines_stored = 0;
    }
    }
  files_ready_to_read = total_files;
  return 0;
}


/* Determine print_func and char_func, the functions
   used by each column for printing and/or storing.

   Determine the horizontal position desired when we begin
   printing a column (p->start_position). */

static void
init_funcs (void)
{
  int i, h, h_next;
  COLUMN *p;

  h = chars_per_margin;

  if (!truncate_lines)
    h_next = ANYWHERE;
  else
    {
      /* When numbering lines of parallel files, we enlarge the
         first column to accomodate the number.  Looks better than
         the Sys V approach. */
      if (parallel_files && numbered_lines)
    h_next = h + chars_per_column + number_width;
      else
    h_next = h + chars_per_column;
    }

  /* Enlarge p->start_position of first column to use the same form of
     padding_not_printed with all columns. */
  h = h + col_sep_length;

  /* This loop takes care of all but the rightmost column. */

  for (p = column_vector, i = 1; i < columns; ++p, ++i)
    {
      if (storing_columns)  /* One file, multi columns down. */
    {
      p->char_func = store_char;
      p->print_func = print_stored;
    }
      else
    /* One file, multi columns across; or parallel files.  */
    {
      p->char_func = print_char;
      p->print_func = read_line;
    }

      /* Number only the first column when printing files in
         parallel. */
      p->numbered = numbered_lines && (!parallel_files || i == 1);
      p->start_position = h;

      /* If we don't truncate lines, all start_positions are
         ANYWHERE, except the first column's start_position when
         using a margin. */

      if (!truncate_lines)
    {
      h = ANYWHERE;
      h_next = ANYWHERE;
    }
      else
    {
      h = h_next + col_sep_length;
      h_next = h + chars_per_column;
    }
    }

  /* The rightmost column.

     Doesn't need to be stored unless we intend to balance
     columns on the last page. */
  if (storing_columns && balance_columns)
    {
      p->char_func = store_char;
      p->print_func = print_stored;
    }
  else
    {
      p->char_func = print_char;
      p->print_func = read_line;
    }

  p->numbered = numbered_lines && (!parallel_files || i == 1);
  p->start_position = h;
}


/* Open a file.  Return nonzero if successful, zero if failed.

   With each file p, p->full_page_printed is initialized,
   see also init_fps. */

static int
open_file (char *name, COLUMN *p)
{
  if (STREQ (name, "-"))
    {
      p->name = _("standard input");
      p->fp = stdin;
      have_read_stdin = 1;
    }
  else
    {
      p->name = name;
      p->fp = fopen (name, "r");
    }
  if (p->fp == NULL)
    {
      ++failed_opens;
      if (!ignore_failed_opens)
    error (0, errno, "%s", name);
      return 0;
    }
  p->status = OPEN;
  p->full_page_printed = FALSE;
  ++total_files;
  return 1;
}

/* Close the file in P.

   If we aren't dealing with multiple files in parallel, we change
   the status of all columns in the column list to reflect the close. */

static void
close_file (COLUMN *p)
{
  COLUMN *q;
  int i;

  if (p->status == CLOSED)
    return;
  if (ferror (p->fp))
    error (EXIT_FAILURE, errno, "%s", p->name);
  if (p->fp != stdin && fclose (p->fp) == EOF)
    error (EXIT_FAILURE, errno, "%s", p->name);

  if (!parallel_files)
    {
      for (q = column_vector, i = columns; i; ++q, --i)
    {
      q->status = CLOSED;
      if (q->lines_stored == 0)
        {
          q->lines_to_print = 0;
        }
    }
    }
  else
    {
      p->status = CLOSED;
      p->lines_to_print = 0;
    }

  --files_ready_to_read;
}

/* Put a file on hold until we start a new page,
   since we've hit a form feed.

   If we aren't dealing with parallel files, we must change the
   status of all columns in the column list. */

static void
hold_file (COLUMN *p)
{
  COLUMN *q;
  int i;

  if (!parallel_files)
    for (q = column_vector, i = columns; i; ++q, --i)
      {
    if (storing_columns)
      q->status = FF_FOUND;
    else
      q->status = ON_HOLD;
      }
  else
    p->status = ON_HOLD;

  p->lines_to_print = 0;
  --files_ready_to_read;
}

/* Undo hold_file -- go through the column list and change any
   ON_HOLD columns to OPEN.  Used at the end of each page. */

static void
reset_status (void)
{
  int i = columns;
  COLUMN *p;

  for (p = column_vector; i; --i, ++p)
    if (p->status == ON_HOLD)
      {
    p->status = OPEN;
    files_ready_to_read++;
      }

  if (storing_columns)
    {
      if (column_vector->status == CLOSED)
        /* We use the info to output an error message in  skip_to_page. */
        files_ready_to_read = 0;
      else
        files_ready_to_read = 1;
    }
}


/* Print a single file, or multiple files in parallel.

   Set up the list of columns, opening the necessary files.
   Allocate space for storing columns, if necessary.
   Skip to first_page_number, if user has asked to skip leading pages.
   Determine which functions are appropriate to store/print lines
   in each column.
   Print the file(s). */

static void
print_files (int number_of_files, char **av)
{
  init_parameters (number_of_files);
  if (init_fps (number_of_files, av))
    return;
  if (storing_columns)
    init_store_cols ();

  if (first_page_number > 1)
    {
      if (!skip_to_page (first_page_number))
    return;
      else
    page_number = first_page_number;
    }
  else
    page_number = 1;

  init_funcs ();

  line_number = line_count;
  while (print_page ())
    ;
}


/* Initialize header information.
   If DESC is non-negative, it is a file descriptor open to
   FILENAME for reading.  */

static void
init_header (char *filename, int desc)
{
  char *buf;
  char initbuf[MAX (256, INT_STRLEN_BOUND (long) + 1)];
  struct stat st;
  struct tm *tm;

  /* If parallel files or standard input, use current date. */
  if (STREQ (filename, "-"))
    desc = -1;
  if (desc < 0 || fstat (desc, &st) != 0)
    st.st_mtime = time (NULL);

  buf = initbuf;
  tm = localtime (&st.st_mtime);
  if (! tm)
    sprintf (buf, "%ld", (long) st.st_mtime);
  else
    {
      size_t bufsize = sizeof initbuf;
      for (;;)
    {
      *buf = '\1';
      if (strftime (buf, bufsize, date_format, tm) || ! *buf)
        break;
      buf = alloca (bufsize *= 2);
    }
    }

  if (date_text)
    free (date_text);
  date_text = xstrdup (buf);
  file_text = custom_header ? custom_header : desc < 0 ? "" : filename;
  header_width_available = (chars_per_line
                - mbswidth (date_text, 0)
                - mbswidth (file_text, 0));
}


/* Set things up for printing a page

   Scan through the columns ...
   Determine which are ready to print
   (i.e., which have lines stored or open files)
   Set p->lines_to_print appropriately
   (to p->lines_stored if we're storing, or lines_per_body
   if we're reading straight from the file)
   Keep track of this total so we know when to stop printing */

static void
init_page (void)
{
  int j;
  COLUMN *p;

  if (storing_columns)
    {
      store_columns ();
      for (j = columns - 1, p = column_vector; j; --j, ++p)
    {
      p->lines_to_print = p->lines_stored;
    }

      /* Last column. */
      if (balance_columns)
    {
      p->lines_to_print = p->lines_stored;
    }
      /* Since we're not balancing columns, we don't need to store
         the rightmost column.   Read it straight from the file. */
      else
    {
      if (p->status == OPEN)
        {
          p->lines_to_print = lines_per_body;
        }
      else
        p->lines_to_print = 0;
    }
    }
  else
    for (j = columns, p = column_vector; j; --j, ++p)
      if (p->status == OPEN)
    {
      p->lines_to_print = lines_per_body;
    }
      else
    p->lines_to_print = 0;
}

/* Align empty columns and print separators.
   Empty columns will be formed by files with status ON_HOLD or CLOSED
   when printing multiple files in parallel. */

static void
align_column (COLUMN *p)
{
  padding_not_printed = p->start_position;
  if (padding_not_printed - col_sep_length > 0)
    {
      pad_across_to (padding_not_printed - col_sep_length);
      padding_not_printed = ANYWHERE;
    }

  if (use_col_separator)
    print_sep_string ();

  if (p->numbered)
    add_line_number (p);
}

/* Print one page.

   As long as there are lines left on the page and columns ready to print,
   Scan across the column list
   if the column has stored lines or the file is open
   pad to the appropriate spot
   print the column
   pad the remainder of the page with \n or \f as requested
   reset the status of all files -- any files which where on hold because
   of formfeeds are now put back into the lineup. */

static int
print_page (void)
{
  int j;
  int lines_left_on_page;
  COLUMN *p;

  /* Used as an accumulator (with | operator) of successive values of
     pad_vertically.  The trick is to set pad_vertically
     to zero before each run through the inner loop, then after that
     loop, it tells us whether a line was actually printed (whether a
     newline needs to be output -- or two for double spacing).  But those
     values have to be accumulated (in pv) so we can invoke pad_down
     properly after the outer loop completes. */
  int pv;

  init_page ();

  if (cols_ready_to_print () == 0)
    return FALSE;

  if (extremities)
    print_a_header = TRUE;

  /* Don't pad unless we know a page was printed. */
  pad_vertically = FALSE;
  pv = FALSE;

  lines_left_on_page = lines_per_body;
  if (double_space)
    lines_left_on_page *= 2;

  while (lines_left_on_page > 0 && cols_ready_to_print () > 0)
    {
      output_position = 0;
      spaces_not_printed = 0;
      separators_not_printed = 0;
      pad_vertically = FALSE;
      align_empty_cols = FALSE;
      empty_line = TRUE;

      for (j = 1, p = column_vector; j <= columns; ++j, ++p)
    {
      input_position = 0;
      if (p->lines_to_print > 0 || p->status == FF_FOUND)
        {
          FF_only = FALSE;
          padding_not_printed = p->start_position;
          if (!(p->print_func) (p))
        read_rest_of_line (p);
          pv |= pad_vertically;

          --p->lines_to_print;
          if (p->lines_to_print <= 0)
        {
          if (cols_ready_to_print () <= 0)
            break;
        }

          /* File p changed its status to ON_HOLD or CLOSED */
          if (parallel_files && p->status != OPEN)
        {
          if (empty_line)
            align_empty_cols = TRUE;
          else if (p->status == CLOSED ||
               (p->status == ON_HOLD && FF_only))
            align_column (p);
        }
        }
      else if (parallel_files)
        {
          /* File status ON_HOLD or CLOSED */
          if (empty_line)
        align_empty_cols = TRUE;
          else
        align_column (p);
        }

      /* We need it also with an empty column */
      if (use_col_separator)
        ++separators_not_printed;
    }

      if (pad_vertically)
    {
      putchar ('\n');
      --lines_left_on_page;
    }

      if (cols_ready_to_print () <= 0 && !extremities)
    break;

      if (double_space && pv)
    {
      putchar ('\n');
      --lines_left_on_page;
    }
    }

  if (lines_left_on_page == 0)
    for (j = 1, p = column_vector; j <= columns; ++j, ++p)
      if (p->status == OPEN)
    p->full_page_printed = TRUE;

  pad_vertically = pv;

  if (pad_vertically && extremities)
    pad_down (lines_left_on_page + lines_per_footer);
  else if (keep_FF && print_a_FF)
    {
      putchar ('\f');
      print_a_FF = FALSE;
    }

  if (last_page_number && page_number > last_page_number)
    return FALSE;       /* Stop printing with LAST_PAGE */

  reset_status ();      /* Change ON_HOLD to OPEN. */

  return TRUE;          /* More pages to go. */
}


/* Allocate space for storing columns.

   This is necessary when printing multiple columns from a single file.
   Lines are stored consecutively in buff, separated by '\0'.

   The following doesn't apply any longer - any tuning possible?
   (We can't use a fixed offset since with the '-s' flag lines aren't
   truncated.)

   We maintain a list (line_vector) of pointers to the beginnings
   of lines in buff.  We allocate one more than the number of lines
   because the last entry tells us the index of the last character,
   which we need to know in order to print the last line in buff. */

static void
init_store_cols (void)
{
  int total_lines = lines_per_body * columns;
  int chars_if_truncate = total_lines * (chars_per_column + 1);

  if (line_vector != NULL)
    free ((int *) line_vector);
  /* FIXME: here's where it was allocated.  */
  line_vector = (int *) xmalloc ((total_lines + 1) * sizeof (int *));

  if (end_vector != NULL)
    free ((int *) end_vector);
  end_vector = (int *) xmalloc (total_lines * sizeof (int *));

  if (buff != NULL)
    free (buff);
  buff_allocated = (use_col_separator
            ? 2 * chars_if_truncate
            : chars_if_truncate);   /* Tune this. */
  buff = (char *) xmalloc (buff_allocated);
}

/* Store all but the rightmost column.
   (Used when printing a single file in multiple downward columns)

   For each column
   set p->current_line to be the index in line_vector of the
   first line in the column
   For each line in the column
   store the line in buff
   add to line_vector the index of the line's first char
   buff_start is the index in buff of the first character in the
   current line. */

static void
store_columns (void)
{
  int i, j;
  int line = 0;
  int buff_start;
  int last_col;         /* The rightmost column which will be saved in buff */
  COLUMN *p;

  buff_current = 0;
  buff_start = 0;

  if (balance_columns)
    last_col = columns;
  else
    last_col = columns - 1;

  for (i = 1, p = column_vector; i <= last_col; ++i, ++p)
    p->lines_stored = 0;

  for (i = 1, p = column_vector; i <= last_col && files_ready_to_read;
       ++i, ++p)
    {
      p->current_line = line;
      for (j = lines_per_body; j && files_ready_to_read; --j)

    if (p->status == OPEN)  /* Redundant.  Clean up. */
      {
        input_position = 0;

        if (!read_line (p))
          read_rest_of_line (p);

        if (p->status == OPEN
        || buff_start != buff_current)
          {
        ++p->lines_stored;
        line_vector[line] = buff_start;
        end_vector[line++] = input_position;
        buff_start = buff_current;
          }
      }
    }

  /* Keep track of the location of the last char in buff. */
  line_vector[line] = buff_start;

  if (balance_columns)
    balance (line);
}

static void
balance (int total_stored)
{
  COLUMN *p;
  int i, lines;
  int first_line = 0;

  for (i = 1, p = column_vector; i <= columns; ++i, ++p)
    {
      lines = total_stored / columns;
      if (i <= total_stored % columns)
    ++lines;

      p->lines_stored = lines;
      p->current_line = first_line;

      first_line += lines;
    }
}

/* Store a character in the buffer. */

static void
store_char (int c)
{
  if (buff_current >= buff_allocated)
    {
      /* May be too generous. */
      buff_allocated = 2 * buff_allocated;
      buff = (char *) xrealloc (buff, buff_allocated * sizeof (char));
    }
  buff[buff_current++] = (char) c;
}

static void
add_line_number (COLUMN *p)
{
  int i;
  char *s;
  int left_cut;

  /* Cutting off the higher-order digits is more informative than
     lower-order cut off*/
  if (line_number < power_10)
    sprintf (number_buff, "%*d", chars_per_number, line_number);
  else
    {
      left_cut = line_number % power_10;
      sprintf (number_buff, "%0*d", chars_per_number, left_cut);
    }
  line_number++;
  s = number_buff;
  for (i = chars_per_number; i > 0; i--)
    (p->char_func) ((int) *s++);

  if (columns > 1)
    {
      /* Tabification is assumed for multiple columns, also for n-separators,
     but `default n-separator = TAB' hasn't been given priority over
     equal column_width also specified by POSIX. */
      if (number_separator == '\t')
        {
          i = number_width - chars_per_number;
          while (i-- > 0)
        (p->char_func) ((int) ' ');
        }
      else
        (p->char_func) ((int) number_separator);
    }
  else
    /* To comply with POSIX, we avoid any expansion of default TAB
       separator with a single column output. No column_width requirement
       has to be considered. */
    {
      (p->char_func) ((int) number_separator);
      if (number_separator == '\t')
        output_position = POS_AFTER_TAB (chars_per_output_tab,
              output_position);
    }

  if (truncate_lines && !parallel_files)
    input_position += number_width;
}


/* Print (or store) padding until the current horizontal position
   is position. */

static void
pad_across_to (int position)
{
  register int h = output_position;

  if (tabify_output)
    spaces_not_printed = position - output_position;
  else
    {
      while (++h <= position)
    putchar (' ');
      output_position = position;
    }
}

/* Pad to the bottom of the page.

   If the user has requested a formfeed, use one.
   Otherwise, use newlines. */

static void
pad_down (int lines)
{
  register int i;

  if (use_form_feed)
    putchar ('\f');
  else
    for (i = lines; i; --i)
      putchar ('\n');
}

/* Read the rest of the line.

   Read from the current column's file until an end of line is
   hit.  Used when we've truncated a line and we no longer need
   to print or store its characters. */

static void
read_rest_of_line (COLUMN *p)
{
  register int c;
  FILE *f = p->fp;

  while ((c = getc (f)) != '\n')
    {
      if (c == '\f')
    {
      if ((c = getc (f)) != '\n')
        ungetc (c, f);
      if (keep_FF)
        print_a_FF = TRUE;
      hold_file (p);
      break;
    }
      else if (c == EOF)
    {
      close_file (p);
      break;
    }
    }
}

/* Read a line with skip_to_page.

   Read from the current column's file until an end of line is
   hit.  Used when we read full lines to skip pages.
   With skip_to_page we have to check for FF-coincidence which is done
   in function read_line otherwise.
   Count lines of skipped pages to find the line number of 1st page
   printed relative to 1st line of input file (start_line_num). */

static void
skip_read (COLUMN *p, int column_number)
{
  register int c;
  FILE *f = p->fp;
  int i, single_ff = FALSE;
  COLUMN *q;

  /* Read 1st character in a line or any character succeeding a FF */
  if ((c = getc (f)) == '\f' && p->full_page_printed)
    /* A FF-coincidence with a previous full_page_printed.
       To avoid an additional empty page, eliminate the FF */
    if ((c = getc (f)) == '\n')
      c = getc (f);

  p->full_page_printed = FALSE;

  /* 1st character a FF means a single FF without any printable
     characters. Don't count it as a line with -n option. */
  if (c == '\f')
    single_ff = TRUE;

  /* Preparing for a FF-coincidence: Maybe we finish that page
     without a FF found */
  if (last_line)
    p->full_page_printed = TRUE;

  while (c != '\n')
    {
      if (c == '\f')
    {
      /* No FF-coincidence possible,
         no catching up of a FF-coincidence with next page */
      if (last_line)
        {
          if (!parallel_files)
        for (q = column_vector, i = columns; i; ++q, --i)
          q->full_page_printed = FALSE;
          else
        p->full_page_printed = FALSE;
        }

      if ((c = getc (f)) != '\n')
        ungetc (c, f);
      hold_file (p);
      break;
    }
      else if (c == EOF)
    {
      close_file (p);
      break;
    }
      c = getc (f);
    }

  if (skip_count)
    if ((!parallel_files || column_number == 1) && !single_ff)
      ++line_count;
}


/* If we're tabifying output,

   When print_char encounters white space it keeps track
   of our desired horizontal position and delays printing
   until this function is called. */

static void
print_white_space (void)
{
  register int h_new;
  register int h_old = output_position;
  register int goal = h_old + spaces_not_printed;

  while (goal - h_old > 1
     && (h_new = POS_AFTER_TAB (chars_per_output_tab, h_old)) <= goal)
    {
      putchar (output_tab_char);
      h_old = h_new;
    }
  while (++h_old <= goal)
    putchar (' ');

  output_position = goal;
  spaces_not_printed = 0;
}

/* Print column separators.

   We keep a count until we know that we'll be printing a line,
   then print_sep_string() is called. */

static void
print_sep_string ()
{
  char *s;
  int l = col_sep_length;

  s = col_sep_string;

  if (separators_not_printed <= 0)
    {
      /* We'll be starting a line with chars_per_margin, anything else? */
      if (spaces_not_printed > 0)
    print_white_space ();
    }
  else
    {
      for (; separators_not_printed > 0; --separators_not_printed)
    {
      while (l-- > 0)
        {
          /* 3 types of sep_strings: spaces only, spaces and chars,
          chars only */
          if (*s == ' ')
        {
          /* We're tabifying output; consecutive spaces in
          sep_string may have to be converted to tabs */
          s++;
          ++spaces_not_printed;
        }
          else
        {
          if (spaces_not_printed > 0)
            print_white_space ();
          putchar (*s++);
          ++output_position;
        }
        }
          /* sep_string ends with some spaces */
      if (spaces_not_printed > 0)
        print_white_space ();
    }
    }
}

/* Print (or store, depending on p->char_func) a clump of N
   characters. */

static void
print_clump (COLUMN *p, int n, int *clump)
{
  while (n--)
    (p->char_func) (*clump++);
}

/* Print a character.

   Update the following comment: process-char hasn't been used any
   longer.
   If we're tabifying, all tabs have been converted to spaces by
   process_char().  Keep a count of consecutive spaces, and when
   a nonspace is encountered, call print_white_space() to print the
   required number of tabs and spaces. */

static void
print_char (int c)
{
  if (tabify_output)
    {
      if (c == ' ')
    {
      ++spaces_not_printed;
      return;
    }
      else if (spaces_not_printed > 0)
    print_white_space ();

      /* Nonprintables are assumed to have width 0, except '\b'. */
      if (!ISPRINT (c))
    {
      if (c == '\b')
        --output_position;
    }
      else
    ++output_position;
    }
  putchar (c);
}

/* Skip to page PAGE before printing.
   PAGE may be larger than total number of pages. */

static int
skip_to_page (int page)
{
  int n, i, j;
  COLUMN *p;

  for (n = 1; n < page; ++n)
    {
      for (i = 1; i < lines_per_body; ++i)
    {
      for (j = 1, p = column_vector; j <= columns; ++j, ++p)
        if (p->status == OPEN)
          skip_read (p, j);
    }
      last_line = TRUE;
      for (j = 1, p = column_vector; j <= columns; ++j, ++p)
    if (p->status == OPEN)
      skip_read (p, j);

      if (storing_columns)  /* change FF_FOUND to ON_HOLD */
    for (j = 1, p = column_vector; j <= columns; ++j, ++p)
      if (p->status != CLOSED)
        p->status = ON_HOLD;

      reset_status ();
      last_line = FALSE;

      if (files_ready_to_read < 1)
        {
      /* It's very helpful, normally the total number of pages is
         not known in advance.  */
      error (0, 0,
         _("starting page number larger than total number of pages: `%d'"),
         n);
          break;
    }
    }
  return files_ready_to_read > 0;
}

/* Print a header.

   Formfeeds are assumed to use up two lines at the beginning of
   the page. */

static void
print_header (void)
{
  char page_text[256 + INT_STRLEN_BOUND (int)];
  int available_width;
  int lhs_spaces;
  int rhs_spaces;

  if (!use_form_feed)
    printf ("\n\n");

  output_position = 0;
  pad_across_to (chars_per_margin);
  print_white_space ();

  /* The translator must ensure that formatting the translation of
     "Page %d" does not generate more than (sizeof page_text - 1)
     bytes.  */
  sprintf (page_text, _("Page %d"), page_number++);
  available_width = header_width_available - mbswidth (page_text, 0);
  available_width = MAX (0, available_width);
  lhs_spaces = available_width >> 1;
  rhs_spaces = available_width - lhs_spaces;

  printf ("%s%*s%s%*s%s\n\n\n",
      date_text, lhs_spaces, " ", file_text, rhs_spaces, " ", page_text);

  print_a_header = FALSE;
  output_position = 0;
}

/* Print (or store, if p->char_func is store_char()) a line.

   Read a character to determine whether we have a line or not.
   (We may hit EOF, \n, or \f)

   Once we know we have a line,
   set pad_vertically = TRUE, meaning it's safe
   to pad down at the end of the page, since we do have a page.
   print a header if needed.
   pad across to padding_not_printed if needed.
   print any separators which need to be printed.
   print a line number if it needs to be printed.

   Print the clump which corresponds to the first character.

   Enter a loop and keep printing until an end of line condition
   exists, or until we exceed chars_per_column.

   Return FALSE if we exceed chars_per_column before reading
   an end of line character, TRUE otherwise. */

static int
read_line (COLUMN *p)
{
  int c;
  int chars IF_LINT (= 0);
  int last_input_position;
  int j, k;
  COLUMN *q;

  /* read 1st character in each line or any character succeeding a FF: */
  c = getc (p->fp);

  last_input_position = input_position;

  if (c == '\f' && p->full_page_printed)
    if ((c = getc (p->fp)) == '\n')
      c = getc (p->fp);
  p->full_page_printed = FALSE;

  switch (c)
    {
    case '\f':
      if ((c = getc (p->fp)) != '\n')
    ungetc (c, p->fp);
      FF_only = TRUE;
      if (print_a_header && !storing_columns)
    {
      pad_vertically = TRUE;
      print_header ();
    }
      else if (keep_FF)
    print_a_FF = TRUE;
      hold_file (p);
      return TRUE;
    case EOF:
      close_file (p);
      return TRUE;
    case '\n':
      break;
    default:
      chars = char_to_clump (c);
    }

  if (truncate_lines && input_position > chars_per_column)
    {
      input_position = last_input_position;
      return FALSE;
    }

  if (p->char_func != store_char)
    {
      pad_vertically = TRUE;

      if (print_a_header && !storing_columns)
    print_header ();

      if (parallel_files && align_empty_cols)
    {
      /* We have to align empty columns at the beginning of a line. */
      k = separators_not_printed;
      separators_not_printed = 0;
      for (j = 1, q = column_vector; j <= k; ++j, ++q)
        {
          align_column (q);
          separators_not_printed += 1;
        }
      padding_not_printed = p->start_position;
      if (truncate_lines)
        spaces_not_printed = chars_per_column;
      else
        spaces_not_printed = 0;
      align_empty_cols = FALSE;
    }

      if (padding_not_printed - col_sep_length > 0)
    {
      pad_across_to (padding_not_printed - col_sep_length);
      padding_not_printed = ANYWHERE;
    }

      if (use_col_separator)
    print_sep_string ();
    }

  if (p->numbered)
    add_line_number (p);

  empty_line = FALSE;
  if (c == '\n')
    return TRUE;

  print_clump (p, chars, clump_buff);

  for (;;)
    {
      c = getc (p->fp);

      switch (c)
    {
    case '\n':
      return TRUE;
    case '\f':
      if ((c = getc (p->fp)) != '\n')
        ungetc (c, p->fp);
      if (keep_FF)
        print_a_FF = TRUE;
      hold_file (p);
      return TRUE;
    case EOF:
      close_file (p);
      return TRUE;
    }

      last_input_position = input_position;
      chars = char_to_clump (c);
      if (truncate_lines && input_position > chars_per_column)
    {
      input_position = last_input_position;
      return FALSE;
    }

      print_clump (p, chars, clump_buff);
    }
}

/* Print a line from buff.

   If this function has been called, we know we have "something to
   print". But it remains to be seen whether we have a real text page
   or an empty page (a single form feed) with/without a header only.
   Therefore first we set pad_vertically to TRUE and print a header
   if necessary.
   If FF_FOUND and we are using -t|-T option we omit any newline by
   setting pad_vertically to FALSE (see print_page).
   Otherwise we pad across if necessary, print separators if necessary
   and text of COLUMN *p.

   Return TRUE, meaning there is no need to call read_rest_of_line. */

static int
print_stored (COLUMN *p)
{
  COLUMN *q;
  int i;

  int line = p->current_line++;
  register char *first = &buff[line_vector[line]];
  /* FIXME
     UMR: Uninitialized memory read:
     * This is occurring while in:
     print_stored   [pr.c:2239]
     * Reading 4 bytes from 0x5148c in the heap.
     * Address 0x5148c is 4 bytes into a malloc'd block at 0x51488 of 676 bytes
     * This block was allocated from:
     malloc         [rtlib.o]
     xmalloc        [xmalloc.c:94]
     init_store_cols [pr.c:1648]
     */
  register char *last = &buff[line_vector[line + 1]];

  pad_vertically = TRUE;

  if (print_a_header)
    print_header ();

  if (p->status == FF_FOUND)
    {
      for (i = 1, q = column_vector; i <= columns; ++i, ++q)
    q->status = ON_HOLD;
      if (column_vector->lines_to_print <= 0)
    {
      if (!extremities)
        pad_vertically = FALSE;
      return TRUE;      /* print a header only */
    }
    }

  if (padding_not_printed - col_sep_length > 0)
    {
      pad_across_to (padding_not_printed - col_sep_length);
      padding_not_printed = ANYWHERE;
    }

  if (use_col_separator)
    print_sep_string ();

  while (first != last)
    print_char (*first++);

  if (spaces_not_printed == 0)
    {
      output_position = p->start_position + end_vector[line];
      if (p->start_position - col_sep_length == chars_per_margin)
    output_position -= col_sep_length;
    }

  return TRUE;
}

/* Convert a character to the proper format and return the number of
   characters in the resulting clump.  Increment input_position by
   the width of the clump.

   Tabs are converted to clumps of spaces.
   Nonprintable characters may be converted to clumps of escape
   sequences or control prefixes.

   Note: the width of a clump is not necessarily equal to the number of
   characters in clump_buff.  (e.g, the width of '\b' is -1, while the
   number of characters is 1.) */

static int
char_to_clump (int c)
{
  register int *s = clump_buff;
  register int i;
  char esc_buff[4];
  int width;
  int chars;
  int chars_per_c = 8;

  if (c == input_tab_char)
    chars_per_c = chars_per_input_tab;

  if (c == input_tab_char || c == '\t')
    {
      width = TAB_WIDTH (chars_per_c, input_position);

      if (untabify_input)
    {
      for (i = width; i; --i)
        *s++ = ' ';
      chars = width;
    }
      else
    {
      *s = c;
      chars = 1;
    }

    }
  else if (!ISPRINT (c))
    {
      if (use_esc_sequence)
    {
      width = 4;
      chars = 4;
      *s++ = '\\';
      sprintf (esc_buff, "%03o", c);
      for (i = 0; i <= 2; ++i)
        *s++ = (int) esc_buff[i];
    }
      else if (use_cntrl_prefix)
    {
      if (c < 0200)
        {
          width = 2;
          chars = 2;
          *s++ = '^';
          *s++ = c ^ 0100;
        }
      else
        {
          width = 4;
          chars = 4;
          *s++ = '\\';
          sprintf (esc_buff, "%03o", c);
          for (i = 0; i <= 2; ++i)
        *s++ = (int) esc_buff[i];
        }
    }
      else if (c == '\b')
    {
      width = -1;
      chars = 1;
      *s = c;
    }
      else
    {
      width = 0;
      chars = 1;
      *s = c;
    }
    }
  else
    {
      width = 1;
      chars = 1;
      *s = c;
    }

  input_position += width;
  return chars;
}

/* We've just printed some files and need to clean up things before
   looking for more options and printing the next batch of files.

   Free everything we've xmalloc'ed, except `header'. */

static void
cleanup (void)
{
  if (number_buff)
    free (number_buff);
  if (clump_buff)
    free (clump_buff);
  if (column_vector)
    free (column_vector);
  if (line_vector)
    free (line_vector);
  if (end_vector)
    free (end_vector);
  if (buff)
    free (buff);
}


/* Complain, print a usage message, and die. */

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
          program_name);

      fputs (_("\
Paginate or columnate FILE(s) for printing.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  +FIRST_PAGE[:LAST_PAGE], --pages=FIRST_PAGE[:LAST_PAGE]\n\
                    begin [stop] printing with page FIRST_[LAST_]PAGE\n\
  -COLUMN, --columns=COLUMN\n\
                    output COLUMN columns and print columns down,\n\
                    unless -a is used. Balance number of lines in the\n\
                    columns on each page.\n\
"), stdout);
      fputs (_("\
  -a, --across      print columns across rather than down, used together\n\
                    with -COLUMN\n\
  -c, --show-control-chars\n\
                    use hat notation (^G) and octal backslash notation\n\
  -d, --double-space\n\
                    double space the output\n\
"), stdout);
      fputs (_("\
  -D, --date-format=FORMAT\n\
                    use FORMAT for the header date\n\
  -e[CHAR[WIDTH]], --expand-tabs[=CHAR[WIDTH]]\n\
                    expand input CHARs (TABs) to tab WIDTH (8)\n\
  -F, -f, --form-feed\n\
                    use form feeds instead of newlines to separate pages\n\
                    (by a 3-line page header with -F or a 5-line header\n\
                    and trailer without -F)\n\
"), stdout);
      fputs (_("\
  -h HEADER, --header=HEADER\n\
                    use a centered HEADER instead of filename in page header,\n\
                    -h \"\" prints a blank line, don't use -h\"\"\n\
  -i[CHAR[WIDTH]], --output-tabs[=CHAR[WIDTH]]\n\
                    replace spaces with CHARs (TABs) to tab WIDTH (8)\n\
  -J, --join-lines  merge full lines, turns off -W line truncation, no column\n\
                    alignment, --sep-string[=STRING] sets separators\n\
"), stdout);
      fputs (_("\
  -l PAGE_LENGTH, --length=PAGE_LENGTH\n\
                    set the page length to PAGE_LENGTH (66) lines\n\
                    (default number of lines of text 56, and with -F 63)\n\
  -m, --merge       print all files in parallel, one in each column,\n\
                    truncate lines, but join lines of full length with -J\n\
"), stdout);
      fputs (_("\
  -n[SEP[DIGITS]], --number-lines[=SEP[DIGITS]]\n\
                    number lines, use DIGITS (5) digits, then SEP (TAB),\n\
                    default counting starts with 1st line of input file\n\
  -N NUMBER, --first-line-number=NUMBER\n\
                    start counting with NUMBER at 1st line of first\n\
                    page printed (see +FIRST_PAGE)\n\
"), stdout);
      fputs (_("\
  -o MARGIN, --indent=MARGIN\n\
                    offset each line with MARGIN (zero) spaces, do not\n\
                    affect -w or -W, MARGIN will be added to PAGE_WIDTH\n\
  -r, --no-file-warnings\n\
                    omit warning when a file cannot be opened\n\
"), stdout);
      fputs (_("\
  -s[CHAR],--separator[=CHAR]\n\
                    separate columns by a single character, default for CHAR\n\
                    is the <TAB> character without -w and \'no char\' with -w\n\
                    -s[CHAR] turns off line truncation of all 3 column\n\
                    options (-COLUMN|-a -COLUMN|-m) except -w is set\n\
"), stdout);
      fputs (_("\
  -SSTRING, --sep-string[=STRING]\n\
"), stdout);
      fputs (_("\
                    separate columns by STRING,\n\
                    without -S: Default separator <TAB> with -J and <space>\n\
                    otherwise (same as -S\" \"), no effect on column options\n\
  -t, --omit-header omit page headers and trailers\n\
"), stdout);
      fputs (_("\
  -T, --omit-pagination\n\
                    omit page headers and trailers, eliminate any pagination\n\
                    by form feeds set in input files\n\
  -v, --show-nonprinting\n\
                    use octal backslash notation\n\
  -w PAGE_WIDTH, --width=PAGE_WIDTH\n\
                    set page width to PAGE_WIDTH (72) characters for\n\
                    multiple text-column output only, -s[char] turns off (72)\n\
"), stdout);
      fputs (_("\
  -W PAGE_WIDTH, --page-width=PAGE_WIDTH\n\
                    set page width to PAGE_WIDTH (72) characters always,\n\
                    truncate lines, except -J option is set, no interference\n\
                    with -S or -s\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
-T implied by -l nn when nn <= 10 or <= 3 with -F. With no FILE, or when\n\
FILE is -, read standard input.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/mbswidth.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/mbswidth.h used in: Step 907
/* Determine the number of screen columns needed for a string.
   Copyright (C) 2000-2003 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 <stddef.h>

/* Avoid a clash of our mbswidth() with a function of the same name defined
   in UnixWare 7.1.1 <wchar.h>.  We need this #include before the #define
   below.  */
#if HAVE_WCHAR_H
# include <wchar.h>
#endif


/* Optional flags to influence mbswidth/mbsnwidth behavior.  */

/* If this bit is set, return -1 upon finding an invalid or incomplete
   character.  Otherwise, assume invalid characters have width 1.  */
#define MBSW_REJECT_INVALID 1

/* If this bit is set, return -1 upon finding a non-printable character.
   Otherwise, assume unprintable characters have width 0 if they are
   control characters and 1 otherwise.  */
#define MBSW_REJECT_UNPRINTABLE 2

/* Returns the number of screen columns needed for STRING.  */
#define mbswidth gnu_mbswidth  /* avoid clash with UnixWare 7.1.1 function */
extern int mbswidth (const char *string, int flags);

/* Returns the number of screen columns needed for the NBYTES bytes
   starting at BUF.  */
extern int mbsnwidth (const char *buf, size_t nbytes, int flags);

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/printf.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/printf.c used in: Step 909
/* printf - format and print data
   Copyright (C) 1990-2003, 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.  */

/* Usage: printf format [argument...]

   A front end to the printf function that lets it be used from the shell.

   Backslash escapes:

   \" = double quote
   \\ = backslash
   \a = alert (bell)
   \b = backspace
   \c = produce no further output
   \f = form feed
   \n = new line
   \r = carriage return
   \t = horizontal tab
   \v = vertical tab
   \0ooo = octal number (ooo is 0 to 3 digits)
   \xhh = hexadecimal number (hhh is 1 to 2 digits)
   \uhhhh = 16-bit Unicode character (hhhh is 4 digits)
   \Uhhhhhhhh = 32-bit Unicode character (hhhhhhhh is 8 digits)

   Additional directive:

   %b = print an argument string, interpreting backslash escapes

   The `format' argument is re-used as many times as necessary
   to convert all of the given arguments.

   David MacKenzie <djm@gnu.ai.mit.edu> */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>

#include "system.h"
#include "long-options.h"
#include "error.h"
#include "closeout.h"
#include "unicodeio.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "printf"

#define AUTHORS "David MacKenzie"

#ifndef STDC_HEADERS
double strtod ();
long int strtol ();
unsigned long int strtoul ();
#endif

#define isodigit(c) ((c) >= '0' && (c) <= '7')
#define hextobin(c) ((c) >= 'a' && (c) <= 'f' ? (c) - 'a' + 10 : \
             (c) >= 'A' && (c) <= 'F' ? (c) - 'A' + 10 : (c) - '0')
#define octtobin(c) ((c) - '0')

/* A value for field_width or precision that indicates it was not specified.  */
#define UNSPECIFIED INT_MIN

/* The value to return to the calling program.  */
static int exit_status;

/* Non-zero if the POSIXLY_CORRECT environment variable is set.  */
static int posixly_correct;

/* This message appears in N_() here rather than just in _() below because
   the sole use would have been in a #define.  */
static char *const cfcc_msg =
 N_("warning: %s: character(s) following character constant have been ignored");

/* The name this program was run with. */
char *program_name;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s FORMAT [ARGUMENT]...\n\
  or:  %s OPTION\n\
"),
          program_name, program_name);
      fputs (_("\
Print ARGUMENT(s) according to FORMAT.\n\
\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
FORMAT controls the output as in C printf.  Interpreted sequences are:\n\
\n\
  \\\"      double quote\n\
  \\0NNN   character with octal value NNN (0 to 3 digits)\n\
  \\\\      backslash\n\
"), stdout);
      fputs (_("\
  \\a      alert (BEL)\n\
  \\b      backspace\n\
  \\c      produce no further output\n\
  \\f      form feed\n\
"), stdout);
      fputs (_("\
  \\n      new line\n\
  \\r      carriage return\n\
  \\t      horizontal tab\n\
  \\v      vertical tab\n\
"), stdout);
      fputs (_("\
  \\xNN    byte with hexadecimal value NN (1 to 2 digits)\n\
\n\
  \\uNNNN  character with hexadecimal value NNNN (4 digits)\n\
  \\UNNNNNNNN  character with hexadecimal value NNNNNNNN (8 digits)\n\
"), stdout);
      fputs (_("\
  %%      a single %\n\
  %b      ARGUMENT as a string with `\\' escapes interpreted\n\
\n\
and all C format specifications ending with one of diouxXfeEgGcs, with\n\
ARGUMENTs converted to proper type first.  Variable widths are handled.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

static void
verify (const char *s, const char *end)
{
  if (errno)
    {
      error (0, errno, "%s", s);
      exit_status = 1;
    }
  else if (*end)
    {
      if (s == end)
    error (0, 0, _("%s: expected a numeric value"), s);
      else
    error (0, 0, _("%s: value not completely converted"), s);
      exit_status = 1;
    }
}

#define STRTOX(TYPE, FUNC_NAME, LIB_FUNC_EXPR)               \
static TYPE                              \
FUNC_NAME (s)                                \
     const char *s;                          \
{                                    \
  char *end;                                 \
  TYPE val;                              \
                                     \
  if (*s == '\"' || *s == '\'')                      \
    {                                    \
      val = *(unsigned char *) ++s;                  \
      /* If POSIXLY_CORRECT is not set, then give a warning that there   \
     are characters following the character constant and that GNU    \
     printf is ignoring those characters.  If POSIXLY_CORRECT *is*   \
     set, then don't give the warning.  */               \
      if (*++s != 0 && !posixly_correct)                 \
    error (0, 0, _(cfcc_msg), s);                    \
    }                                    \
  else                                   \
    {                                    \
      errno = 0;                             \
      val = LIB_FUNC_EXPR;                       \
      verify (s, end);                           \
    }                                    \
  return val;                                \
}                                    \

STRTOX (unsigned long int, xstrtoul, (strtoul (s, &end, 0)))
STRTOX (long int,          xstrtol,  (strtol  (s, &end, 0)))
STRTOX (double,            xstrtod,  (strtod  (s, &end)))

/* Output a single-character \ escape.  */

static void
print_esc_char (int c)
{
  switch (c)
    {
    case 'a':           /* Alert. */
      putchar (7);
      break;
    case 'b':           /* Backspace. */
      putchar (8);
      break;
    case 'c':           /* Cancel the rest of the output. */
      exit (EXIT_SUCCESS);
      break;
    case 'f':           /* Form feed. */
      putchar (12);
      break;
    case 'n':           /* New line. */
      putchar (10);
      break;
    case 'r':           /* Carriage return. */
      putchar (13);
      break;
    case 't':           /* Horizontal tab. */
      putchar (9);
      break;
    case 'v':           /* Vertical tab. */
      putchar (11);
      break;
    default:
      putchar (c);
      break;
    }
}

/* Print a \ escape sequence starting at ESCSTART.
   Return the number of characters in the escape sequence
   besides the backslash. */

static int
print_esc (const char *escstart)
{
  register const char *p = escstart + 1;
  int esc_value = 0;        /* Value of \nnn escape. */
  int esc_length;       /* Length of \nnn escape. */

  if (!posixly_correct && *p == 'x')
    {
      /* A hexadecimal \xhh escape sequence must have 1 or 2 hex. digits.  */
      for (esc_length = 0, ++p;
       esc_length < 2 && ISXDIGIT (*p);
       ++esc_length, ++p)
    esc_value = esc_value * 16 + hextobin (*p);
      if (esc_length == 0)
    error (EXIT_FAILURE, 0, _("missing hexadecimal number in escape"));
      putchar (esc_value);
    }
  else if (*p == '0')
    {
      /* An octal \0ooo escape sequence has 0 to 3 octal digits
     after the leading \0.  */
      for (esc_length = 0, ++p;
       esc_length < 3 && isodigit (*p);
       ++esc_length, ++p)
    esc_value = esc_value * 8 + octtobin (*p);
      putchar (esc_value);
    }
  else if (*p && strchr ("\"\\abcfnrtv", *p))
    print_esc_char (*p++);
  else if (!posixly_correct && (*p == 'u' || *p == 'U'))
    {
      char esc_char = *p;
      unsigned int uni_value;

      uni_value = 0;
      for (esc_length = (esc_char == 'u' ? 4 : 8), ++p;
       esc_length > 0;
       --esc_length, ++p)
    {
      if (!ISXDIGIT (*p))
        error (EXIT_FAILURE, 0, _("missing hexadecimal number in escape"));
      uni_value = uni_value * 16 + hextobin (*p);
    }

      /* A universal character name shall not specify a character short
     identifier in the range 00000000 through 00000020, 0000007F through
     0000009F, or 0000D800 through 0000DFFF inclusive. A universal
     character name shall not designate a character in the required
     character set.  */
      if ((uni_value <= 0x9f
       && uni_value != 0x24 && uni_value != 0x40 && uni_value != 0x60)
      || (uni_value >= 0xd800 && uni_value <= 0xdfff))
    error (EXIT_FAILURE, 0, _("invalid universal character name \\%c%0*x"),
           esc_char, (esc_char == 'u' ? 4 : 8), uni_value);

      print_unicode_char (stdout, uni_value, 0);
    }
  else
    {
      putchar ('\\');
      if (*p)
    {
      putchar (*p);
      p++;
    }
    }
  return p - escstart - 1;
}

/* Print string STR, evaluating \ escapes. */

static void
print_esc_string (const char *str)
{
  for (; *str; str++)
    if (*str == '\\')
      str += print_esc (str);
    else
      putchar (*str);
}

/* Output a % directive.  START is the start of the directive,
   LENGTH is its length, and ARGUMENT is its argument.
   If FIELD_WIDTH or PRECISION is UNSPECIFIED, they are args for
   '*' values in those fields. */

static void
print_direc (const char *start, size_t length, int field_width,
         int precision, const char *argument)
{
  char *p;      /* Null-terminated copy of % directive. */

  p = xmalloc ((unsigned) (length + 1));
  strncpy (p, start, length);
  p[length] = 0;

  switch (p[length - 1])
    {
    case 'd':
    case 'i':
      if (field_width == UNSPECIFIED)
    {
      if (precision == UNSPECIFIED)
        printf (p, xstrtol (argument));
      else
        printf (p, precision, xstrtol (argument));
    }
      else
    {
      if (precision == UNSPECIFIED)
        printf (p, field_width, xstrtol (argument));
      else
        printf (p, field_width, precision, xstrtol (argument));
    }
      break;

    case 'o':
    case 'u':
    case 'x':
    case 'X':
      if (field_width == UNSPECIFIED)
    {
      if (precision == UNSPECIFIED)
        printf (p, xstrtoul (argument));
      else
        printf (p, precision, xstrtoul (argument));
    }
      else
    {
      if (precision == UNSPECIFIED)
        printf (p, field_width, xstrtoul (argument));
      else
        printf (p, field_width, precision, xstrtoul (argument));
    }
      break;

    case 'f':
    case 'e':
    case 'E':
    case 'g':
    case 'G':
      if (field_width == UNSPECIFIED)
    {
      if (precision == UNSPECIFIED)
        printf (p, xstrtod (argument));
      else
        printf (p, precision, xstrtod (argument));
    }
      else
    {
      if (precision == UNSPECIFIED)
        printf (p, field_width, xstrtod (argument));
      else
        printf (p, field_width, precision, xstrtod (argument));
    }
      break;

    case 'c':
      printf (p, *argument);
      break;

    case 's':
      if (field_width == UNSPECIFIED)
    {
      if (precision == UNSPECIFIED)
        printf (p, argument);
      else
        printf (p, precision, argument);
    }
      else
    {
      if (precision == UNSPECIFIED)
        printf (p, field_width, argument);
      else
        printf (p, field_width, precision, argument);
    }
      break;
    }

  free (p);
}

/* Print the text in FORMAT, using ARGV (with ARGC elements) for
   arguments to any `%' directives.
   Return the number of elements of ARGV used.  */

static int
print_formatted (const char *format, int argc, char **argv)
{
  int save_argc = argc;     /* Preserve original value.  */
  const char *f;        /* Pointer into `format'.  */
  const char *direc_start;  /* Start of % directive.  */
  size_t direc_length;      /* Length of % directive.  */
  int field_width;      /* Arg to first '*', or UNSPECIFIED if none. */
  int precision;        /* Arg to second '*', or UNSPECIFIED if none. */

  for (f = format; *f; ++f)
    {
      switch (*f)
    {
    case '%':
      direc_start = f++;
      direc_length = 1;
      field_width = precision = UNSPECIFIED;
      if (*f == '%')
        {
          putchar ('%');
          break;
        }
      if (*f == 'b')
        {
          if (argc > 0)
        {
          print_esc_string (*argv);
          ++argv;
          --argc;
        }
          break;
        }
      while (*f == ' ' || *f == '#' || *f == '+' || *f == '-')
        {
          ++f;
          ++direc_length;
        }
      if (*f == '*')
        {
          ++f;
          ++direc_length;
          if (argc > 0)
        {
          field_width = xstrtoul (*argv);
          if (field_width == UNSPECIFIED)
            error (EXIT_FAILURE, 0, _("invalid field width: %s"),
               *argv);
          ++argv;
          --argc;
        }
          else
        field_width = 0;
        }
      else
        while (ISDIGIT (*f))
          {
        ++f;
        ++direc_length;
          }
      if (*f == '.')
        {
          ++f;
          ++direc_length;
          if (*f == '*')
        {
          ++f;
          ++direc_length;
          if (argc > 0)
            {
              precision = xstrtoul (*argv);
              if (precision == UNSPECIFIED)
            error (EXIT_FAILURE, 0, _("invalid precision: %s"),
                   *argv);
              ++argv;
              --argc;
            }
          else
            precision = 0;
        }
          else
        while (ISDIGIT (*f))
          {
            ++f;
            ++direc_length;
          }
        }
      if (*f == 'l' || *f == 'L' || *f == 'h')
        {
          ++f;
          ++direc_length;
        }
      if (! (*f && strchr ("diouxXfeEgGcs", *f)))
        error (EXIT_FAILURE, 0, _("%%%c: invalid directive"), *f);
      ++direc_length;
      if (argc > 0)
        {
          print_direc (direc_start, direc_length, field_width,
               precision, *argv);
          ++argv;
          --argc;
        }
      else
        print_direc (direc_start, direc_length, field_width,
             precision, "");
      break;

    case '\\':
      f += print_esc (f);
      break;

    default:
      putchar (*f);
    }
    }

  return save_argc - argc;
}

int
main (int argc, char **argv)
{
  char *format;
  int args_used;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  exit_status = 0;

  /* Don't recognize --help or --version if POSIXLY_CORRECT is set.  */
  posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
  if (!posixly_correct)
    parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
            AUTHORS, usage);

  /* The above handles --help and --version.
     Since there is no other invocation of getopt, handle `--' here.  */
  if (1 < argc && STREQ (argv[1], "--"))
    {
      --argc;
      ++argv;
    }

  if (argc == 1)
    {
      fprintf (stderr, _("Usage: %s format [argument...]\n"), program_name);
      exit (EXIT_FAILURE);
    }

  format = argv[1];
  argc -= 2;
  argv += 2;

  do
    {
      args_used = print_formatted (format, argc, argv);
      argc -= args_used;
      argv += args_used;
    }
  while (args_used > 0 && argc > 0);

  if (argc > 0)
    error (0, 0,
       _("warning: ignoring excess arguments, starting with `%s'"),
       argv[0]);

  exit (exit_status);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/unicodeio.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/unicodeio.h used in: Step 909
/* Unicode character output to streams with locale dependent encoding.

   Copyright (C) 2000-2002 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU Library 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library 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 UNICODEIO_H
# define UNICODEIO_H

# include <stdio.h>

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

/* Outputs the Unicode character CODE to the output stream STREAM.
   Upon failure, exit if exit_on_error is true, otherwise output a fallback
   notation.  */
extern void print_unicode_char PARAMS ((FILE *stream, unsigned int code,
                    int exit_on_error));

/* Simple success callback that outputs the converted string.
   The STREAM is passed as callback_arg.  */
extern long fwrite_success_callback PARAMS ((const char *buf, size_t buflen,
                         void *callback_arg));

#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ptx.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/ptx.c used in: Step 911
/* Permuted index for GNU, with keywords in their context.
   Copyright (C) 1990, 1991, 1993, 1998-2003 Free Software Foundation, Inc.
   François Pinard <pinard@iro.umontreal.ca>, 1988.

   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.

   François Pinard <pinard@iro.umontreal.ca> */

#include <config.h>

#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
#include "closeout.h"
#include "argmatch.h"
#include "bumpalloc.h"
#include "diacrit.h"
#include "error.h"
#include "regex.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "ptx"

#define AUTHORS "François Pinard"

/* Number of possible characters in a byte.  */
#define CHAR_SET_SIZE 256

#define ISODIGIT(C) ((C) >= '0' && (C) <= '7')
#define HEXTOBIN(C) ((C) >= 'a' && (C) <= 'f' ? (C)-'a'+10 \
             : (C) >= 'A' && (C) <= 'F' ? (C)-'A'+10 : (C)-'0')
#define OCTTOBIN(C) ((C) - '0')

/* Debugging the memory allocator.  */

#if WITH_DMALLOC
# define MALLOC_FUNC_CHECK 1
# include <dmalloc.h>
#endif


/* Global definitions.  */

/* Reallocation step when swallowing non regular files.  The value is not
   the actual reallocation step, but its base two logarithm.  */
#define SWALLOW_REALLOC_LOG 12

/* Imported from "regex.c".  */
#define Sword 1

/* The name this program was run with. */
char *program_name;

/* Program options.  */

enum Format
{
  UNKNOWN_FORMAT,       /* output format still unknown */
  DUMB_FORMAT,          /* output for a dumb terminal */
  ROFF_FORMAT,          /* output for `troff' or `nroff' */
  TEX_FORMAT            /* output for `TeX' or `LaTeX' */
};

int gnu_extensions = 1;     /* trigger all GNU extensions */
int auto_reference = 0;     /* references are `file_name:line_number:' */
int input_reference = 0;    /* references at beginning of input lines */
int right_reference = 0;    /* output references after right context  */
int line_width = 72;        /* output line width in characters */
int gap_size = 3;       /* number of spaces between output fields */
const char *truncation_string = "/";
                /* string used to mark line truncations */
const char *macro_name = "xx";  /* macro name for roff or TeX output */
enum Format output_format = UNKNOWN_FORMAT;
                /* output format */

int ignore_case = 0;        /* fold lower to upper case for sorting */
const char *context_regex_string = NULL;
                /* raw regex for end of context */
const char *word_regex_string = NULL;
                /* raw regex for a keyword */
const char *break_file = NULL;  /* name of the `Break characters' file */
const char *only_file = NULL;   /* name of the `Only words' file */
const char *ignore_file = NULL; /* name of the `Ignore words' file */

/* A BLOCK delimit a region in memory of arbitrary size, like the copy of a
   whole file.  A WORD is something smaller, its length should fit in a
   short integer.  A WORD_TABLE may contain several WORDs.  */

typedef struct
  {
    char *start;        /* pointer to beginning of region */
    char *end;          /* pointer to end + 1 of region */
  }
BLOCK;

typedef struct
  {
    char *start;        /* pointer to beginning of region */
    short size;         /* length of the region */
  }
WORD;

typedef struct
  {
    WORD *start;        /* array of WORDs */
    size_t length;      /* number of entries */
  }
WORD_TABLE;

/* Pattern description tables.  */

/* For each character, provide its folded equivalent.  */
unsigned char folded_chars[CHAR_SET_SIZE];

/* For each character, indicate if it is part of a word.  */
char syntax_table[CHAR_SET_SIZE];
char *re_syntax_table = syntax_table;

/* Compiled regex for end of context.  */
struct re_pattern_buffer *context_regex;

/* End of context pattern register indices.  */
struct re_registers context_regs;

/* Compiled regex for a keyword.  */
struct re_pattern_buffer *word_regex;

/* Keyword pattern register indices.  */
struct re_registers word_regs;

/* A word characters fastmap is used only when no word regexp has been
   provided.  A word is then made up of a sequence of one or more characters
   allowed by the fastmap.  Contains !0 if character allowed in word.  Not
   only this is faster in most cases, but it simplifies the implementation
   of the Break files.  */
char word_fastmap[CHAR_SET_SIZE];

/* Maximum length of any word read.  */
int maximum_word_length;

/* Maximum width of any reference used.  */
int reference_max_width;

/* Ignore and Only word tables.  */

WORD_TABLE ignore_table;    /* table of words to ignore */
WORD_TABLE only_table;      /* table of words to select */

#define ALLOC_NEW_WORD(table) \
  BUMP_ALLOC ((table)->start, (table)->length, 8, WORD)

/* Source text table, and scanning macros.  */

int number_input_files;     /* number of text input files */
int total_line_count;       /* total number of lines seen so far */
const char **input_file_name;   /* array of text input file names */
int *file_line_count;       /* array of `total_line_count' values at end */

BLOCK text_buffer;      /* file to study */
char *text_buffer_maxend;   /* allocated end of text_buffer */

/* SKIP_NON_WHITE used only for getting or skipping the reference.  */

#define SKIP_NON_WHITE(cursor, limit) \
  while (cursor < limit && !ISSPACE(*cursor))               \
    cursor++

#define SKIP_WHITE(cursor, limit) \
  while (cursor < limit && ISSPACE(*cursor))                \
    cursor++

#define SKIP_WHITE_BACKWARDS(cursor, start) \
  while (cursor > start && ISSPACE(cursor[-1]))             \
    cursor--

#define SKIP_SOMETHING(cursor, limit) \
  if (word_regex_string)                        \
    {                                   \
      int count;                            \
      count = re_match (word_regex, cursor, limit - cursor, 0, NULL);   \
      cursor += count <= 0 ? 1 : count;                 \
    }                                   \
  else if (word_fastmap[(unsigned char) *cursor])           \
    while (cursor < limit && word_fastmap[(unsigned char) *cursor]) \
      cursor++;                             \
  else                                  \
    cursor++

/* Occurrences table.

   The `keyword' pointer provides the central word, which is surrounded
   by a left context and a right context.  The `keyword' and `length'
   field allow full 8-bit characters keys, even including NULs.  At other
   places in this program, the name `keyafter' refers to the keyword
   followed by its right context.

   The left context does not extend, towards the beginning of the file,
   further than a distance given by the `left' value.  This value is
   relative to the keyword beginning, it is usually negative.  This
   insures that, except for white space, we will never have to backward
   scan the source text, when it is time to generate the final output
   lines.

   The right context, indirectly attainable through the keyword end, does
   not extend, towards the end of the file, further than a distance given
   by the `right' value.  This value is relative to the keyword
   beginning, it is usually positive.

   When automatic references are used, the `reference' value is the
   overall line number in all input files read so far, in this case, it
   is of type (int).  When input references are used, the `reference'
   value indicates the distance between the keyword beginning and the
   start of the reference field, it is of type (DELTA) and usually
   negative.  */

typedef short DELTA;        /* to hold displacement within one context */

typedef struct
  {
    WORD key;           /* description of the keyword */
    DELTA left;         /* distance to left context start */
    DELTA right;        /* distance to right context end */
    int reference;      /* reference descriptor */
  }
OCCURS;

/* The various OCCURS tables are indexed by the language.  But the time
   being, there is no such multiple language support.  */

OCCURS *occurs_table[1];    /* all words retained from the read text */
size_t number_of_occurs[1]; /* number of used slots in occurs_table */

#define ALLOC_NEW_OCCURS(language) \
  BUMP_ALLOC (occurs_table[language], number_of_occurs[language], 9, OCCURS)

/* Communication among output routines.  */

/* Indicate if special output processing is requested for each character.  */
char edited_flag[CHAR_SET_SIZE];

int half_line_width;        /* half of line width, reference excluded */
int before_max_width;       /* maximum width of before field */
int keyafter_max_width;     /* maximum width of keyword-and-after field */
int truncation_string_length;   /* length of string used to flag truncation */

/* When context is limited by lines, wraparound may happen on final output:
   the `head' pointer gives access to some supplementary left context which
   will be seen at the end of the output line, the `tail' pointer gives
   access to some supplementary right context which will be seen at the
   beginning of the output line. */

BLOCK tail;         /* tail field */
int tail_truncation;        /* flag truncation after the tail field */

BLOCK before;           /* before field */
int before_truncation;      /* flag truncation before the before field */

BLOCK keyafter;         /* keyword-and-after field */
int keyafter_truncation;    /* flag truncation after the keyafter field */

BLOCK head;         /* head field */
int head_truncation;        /* flag truncation before the head field */

BLOCK reference;        /* reference field for input reference mode */


/* Miscellaneous routines.  */

/*------------------------------------------------------.
| Duplicate string STRING, while evaluating \-escapes.  |
`------------------------------------------------------*/

/* Loosely adapted from GNU sh-utils printf.c code.  */

static char *
copy_unescaped_string (const char *string)
{
  char *result;         /* allocated result */
  char *cursor;         /* cursor in result */
  int value;            /* value of \nnn escape */
  int length;           /* length of \nnn escape */

  result = xmalloc (strlen (string) + 1);
  cursor = result;

  while (*string)
    if (*string == '\\')
      {
    string++;
    switch (*string)
      {
      case 'x':     /* \xhhh escape, 3 chars maximum */
        value = 0;
        for (length = 0, string++;
         length < 3 && ISXDIGIT (*string);
         length++, string++)
          value = value * 16 + HEXTOBIN (*string);
        if (length == 0)
          {
        *cursor++ = '\\';
        *cursor++ = 'x';
          }
        else
          *cursor++ = value;
        break;

      case '0':     /* \0ooo escape, 3 chars maximum */
        value = 0;
        for (length = 0, string++;
         length < 3 && ISODIGIT (*string);
         length++, string++)
          value = value * 8 + OCTTOBIN (*string);
        *cursor++ = value;
        break;

      case 'a':     /* alert */
#if __STDC__
        *cursor++ = '\a';
#else
        *cursor++ = 7;
#endif
        string++;
        break;

      case 'b':     /* backspace */
        *cursor++ = '\b';
        string++;
        break;

      case 'c':     /* cancel the rest of the output */
        while (*string)
          string++;
        break;

      case 'f':     /* form feed */
        *cursor++ = '\f';
        string++;
        break;

      case 'n':     /* new line */
        *cursor++ = '\n';
        string++;
        break;

      case 'r':     /* carriage return */
        *cursor++ = '\r';
        string++;
        break;

      case 't':     /* horizontal tab */
        *cursor++ = '\t';
        string++;
        break;

      case 'v':     /* vertical tab */
#if __STDC__
        *cursor++ = '\v';
#else
        *cursor++ = 11;
#endif
        string++;
        break;

      default:
        *cursor++ = '\\';
        *cursor++ = *string++;
        break;
      }
      }
    else
      *cursor++ = *string++;

  *cursor = '\0';
  return result;
}

/*-------------------------------------------------------------------.
| Compile the regex represented by STRING, diagnose and abort if any |
| error.  Returns the compiled regex structure.              |
`-------------------------------------------------------------------*/

static struct re_pattern_buffer *
alloc_and_compile_regex (const char *string)
{
  struct re_pattern_buffer *pattern; /* newly allocated structure */
  const char *message;      /* error message returned by regex.c */

  pattern = (struct re_pattern_buffer *)
    xmalloc (sizeof (struct re_pattern_buffer));
  memset (pattern, 0, sizeof (struct re_pattern_buffer));

  pattern->buffer = NULL;
  pattern->allocated = 0;
  pattern->translate = ignore_case ? (char *) folded_chars : NULL;
  pattern->fastmap = (char *) xmalloc ((size_t) CHAR_SET_SIZE);

  message = re_compile_pattern (string, (int) strlen (string), pattern);
  if (message)
    error (EXIT_FAILURE, 0, _("%s (for regexp `%s')"), message, string);

  /* The fastmap should be compiled before `re_match'.  The following
     call is not mandatory, because `re_search' is always called sooner,
     and it compiles the fastmap if this has not been done yet.  */

  re_compile_fastmap (pattern);

  /* Do not waste extra allocated space.  */

  if (pattern->allocated > pattern->used)
    {
      pattern->buffer
    = (unsigned char *) xrealloc (pattern->buffer, (size_t) pattern->used);
      pattern->allocated = pattern->used;
    }

  return pattern;
}

/*------------------------------------------------------------------------.
| This will initialize various tables for pattern match and compiles some |
| regexps.                                |
`------------------------------------------------------------------------*/

static void
initialize_regex (void)
{
  int character;        /* character value */

  /* Initialize the regex syntax table.  */

  for (character = 0; character < CHAR_SET_SIZE; character++)
    syntax_table[character] = ISALPHA (character) ? Sword : 0;

  /* Initialize the case folding table.  */

  if (ignore_case)
    for (character = 0; character < CHAR_SET_SIZE; character++)
      folded_chars[character] = TOUPPER (character);

  /* Unless the user already provided a description of the end of line or
     end of sentence sequence, select an end of line sequence to compile.
     If the user provided an empty definition, thus disabling end of line
     or sentence feature, make it NULL to speed up tests.  If GNU
     extensions are enabled, use end of sentence like in GNU emacs.  If
     disabled, use end of lines.  */

  if (context_regex_string)
    {
      if (!*context_regex_string)
    context_regex_string = NULL;
    }
  else if (gnu_extensions && !input_reference)
    context_regex_string = "[.?!][]\"')}]*\\($\\|\t\\|  \\)[ \t\n]*";
  else
    context_regex_string = "\n";

  if (context_regex_string)
    context_regex = alloc_and_compile_regex (context_regex_string);

  /* If the user has already provided a non-empty regexp to describe
     words, compile it.  Else, unless this has already been done through
     a user provided Break character file, construct a fastmap of
     characters that may appear in a word.  If GNU extensions enabled,
     include only letters of the underlying character set.  If disabled,
     include almost everything, even punctuations; stop only on white
     space.  */

  if (word_regex_string && *word_regex_string)
    word_regex = alloc_and_compile_regex (word_regex_string);
  else if (!break_file)
    {
      if (gnu_extensions)
    {

      /* Simulate \w+.  */

      for (character = 0; character < CHAR_SET_SIZE; character++)
        word_fastmap[character] = ISALPHA (character) ? 1 : 0;
    }
      else
    {

      /* Simulate [^ \t\n]+.  */

      memset (word_fastmap, 1, CHAR_SET_SIZE);
      word_fastmap[' '] = 0;
      word_fastmap['\t'] = 0;
      word_fastmap['\n'] = 0;
    }
    }
}

/*------------------------------------------------------------------------.
| This routine will attempt to swallow a whole file name FILE_NAME into a |
| contiguous region of memory and return a description of it into BLOCK.  |
| Standard input is assumed whenever FILE_NAME is NULL, empty or "-".     |
|                                     |
| Previously, in some cases, white space compression was attempted while  |
| inputting text.  This was defeating some regexps like default end of    |
| sentence, which checks for two consecutive spaces.  If white space      |
| compression is ever reinstated, it should be in output routines.    |
`------------------------------------------------------------------------*/

static void
swallow_file_in_memory (const char *file_name, BLOCK *block)
{
  int file_handle;      /* file descriptor number */
  struct stat stat_block;   /* stat block for file */
  size_t allocated_length;  /* allocated length of memory buffer */
  size_t used_length;       /* used length in memory buffer */
  int read_length;      /* number of character gotten on last read */

  /* As special cases, a file name which is NULL or "-" indicates standard
     input, which is already opened.  In all other cases, open the file from
     its name.  */
  bool using_stdin = !file_name || !*file_name || strcmp (file_name, "-") == 0;
  if (using_stdin)
    file_handle = STDIN_FILENO;
  else
    if ((file_handle = open (file_name, O_RDONLY)) < 0)
      error (EXIT_FAILURE, errno, "%s", file_name);

  /* If the file is a plain, regular file, allocate the memory buffer all at
     once and swallow the file in one blow.  In other cases, read the file
     repeatedly in smaller chunks until we have it all, reallocating memory
     once in a while, as we go.  */

  if (fstat (file_handle, &stat_block) < 0)
    error (EXIT_FAILURE, errno, "%s", file_name);

  if (S_ISREG (stat_block.st_mode))
    {
      size_t in_memory_size;

      block->start = (char *) xmalloc ((size_t) stat_block.st_size);

      if ((in_memory_size = read (file_handle,
                  block->start, (size_t) stat_block.st_size))
      != stat_block.st_size)
    {
#if MSDOS
      /* On MSDOS, in memory size may be smaller than the file
         size, because of end of line conversions.  But it can
         never be smaller than half the file size, because the
         minimum is when all lines are empty and terminated by
         CR+LF.  */
      if (in_memory_size != (size_t)-1
          && in_memory_size >= stat_block.st_size / 2)
        block->start = (char *) xrealloc (block->start, in_memory_size);
      else
#endif /* not MSDOS */

        error (EXIT_FAILURE, errno, "%s", file_name);
    }
      block->end = block->start + in_memory_size;
    }
  else
    {
      block->start = (char *) xmalloc ((size_t) 1 << SWALLOW_REALLOC_LOG);
      used_length = 0;
      allocated_length = (1 << SWALLOW_REALLOC_LOG);

      while (read_length = read (file_handle,
                 block->start + used_length,
                 allocated_length - used_length),
         read_length > 0)
    {
      used_length += read_length;
      if (used_length == allocated_length)
        {
          allocated_length += (1 << SWALLOW_REALLOC_LOG);
          block->start
        = (char *) xrealloc (block->start, allocated_length);
        }
    }

      if (read_length < 0)
    error (EXIT_FAILURE, errno, "%s", file_name);

      block->end = block->start + used_length;
    }

  /* Close the file, but only if it was not the standard input.  */

  if (! using_stdin && close (file_handle) != 0)
    error (EXIT_FAILURE, errno, "%s", file_name);
}


/* Sort and search routines.  */

/*--------------------------------------------------------------------------.
| Compare two words, FIRST and SECOND, and return 0 if they are identical.  |
| Return less than 0 if the first word goes before the second; return       |
| greater than 0 if the first word goes after the second.           |
|                                       |
| If a word is indeed a prefix of the other, the shorter should go first.   |
`--------------------------------------------------------------------------*/

static int
compare_words (const void *void_first, const void *void_second)
{
#define first ((const WORD *) void_first)
#define second ((const WORD *) void_second)
  int length;           /* minimum of two lengths */
  int counter;          /* cursor in words */
  int value;            /* value of comparison */

  length = first->size < second->size ? first->size : second->size;

  if (ignore_case)
    {
      for (counter = 0; counter < length; counter++)
    {
      value = (folded_chars [(unsigned char) (first->start[counter])]
           - folded_chars [(unsigned char) (second->start[counter])]);
      if (value != 0)
        return value;
    }
    }
  else
    {
      for (counter = 0; counter < length; counter++)
    {
      value = ((unsigned char) first->start[counter]
           - (unsigned char) second->start[counter]);
      if (value != 0)
        return value;
    }
    }

  return first->size - second->size;
#undef first
#undef second
}

/*-----------------------------------------------------------------------.
| Decides which of two OCCURS, FIRST or SECOND, should lexicographically |
| go first.  In case of a tie, preserve the original order through a     |
| pointer comparison.                            |
`-----------------------------------------------------------------------*/

static int
compare_occurs (const void *void_first, const void *void_second)
{
#define first ((const OCCURS *) void_first)
#define second ((const OCCURS *) void_second)
  int value;

  value = compare_words (&first->key, &second->key);
  return value == 0 ? first->key.start - second->key.start : value;
#undef first
#undef second
}

/*------------------------------------------------------------.
| Return !0 if WORD appears in TABLE.  Uses a binary search.  |
`------------------------------------------------------------*/

static int
search_table (WORD *word, WORD_TABLE *table)
{
  int lowest;           /* current lowest possible index */
  int highest;          /* current highest possible index */
  int middle;           /* current middle index */
  int value;            /* value from last comparison */

  lowest = 0;
  highest = table->length - 1;
  while (lowest <= highest)
    {
      middle = (lowest + highest) / 2;
      value = compare_words (word, table->start + middle);
      if (value < 0)
    highest = middle - 1;
      else if (value > 0)
    lowest = middle + 1;
      else
    return 1;
    }
  return 0;
}

/*---------------------------------------------------------------------.
| Sort the whole occurs table in memory.  Presumably, `qsort' does not |
| take intermediate copies or table elements, so the sort will be      |
| stabilized throughout the comparison routine.                |
`---------------------------------------------------------------------*/

static void
sort_found_occurs (void)
{

  /* Only one language for the time being.  */

  qsort (occurs_table[0], number_of_occurs[0], sizeof (OCCURS),
     compare_occurs);
}


/* Parameter files reading routines.  */

/*----------------------------------------------------------------------.
| Read a file named FILE_NAME, containing a set of break characters.    |
| Build a content to the array word_fastmap in which all characters are |
| allowed except those found in the file.  Characters may be repeated.  |
`----------------------------------------------------------------------*/

static void
digest_break_file (const char *file_name)
{
  BLOCK file_contents;      /* to receive a copy of the file */
  char *cursor;         /* cursor in file copy */

  swallow_file_in_memory (file_name, &file_contents);

  /* Make the fastmap and record the file contents in it.  */

  memset (word_fastmap, 1, CHAR_SET_SIZE);
  for (cursor = file_contents.start; cursor < file_contents.end; cursor++)
    word_fastmap[(unsigned char) *cursor] = 0;

  if (!gnu_extensions)
    {

      /* If GNU extensions are enabled, the only way to avoid newline as
     a break character is to write all the break characters in the
     file with no newline at all, not even at the end of the file.
     If disabled, spaces, tabs and newlines are always considered as
     break characters even if not included in the break file.  */

      word_fastmap[' '] = 0;
      word_fastmap['\t'] = 0;
      word_fastmap['\n'] = 0;
    }

  /* Return the space of the file, which is no more required.  */

  free (file_contents.start);
}

/*-----------------------------------------------------------------------.
| Read a file named FILE_NAME, containing one word per line, then    |
| construct in TABLE a table of WORD descriptors for them.  The routine  |
| swallows the whole file in memory; this is at the expense of space     |
| needed for newlines, which are useless; however, the reading is fast.  |
`-----------------------------------------------------------------------*/

static void
digest_word_file (const char *file_name, WORD_TABLE *table)
{
  BLOCK file_contents;      /* to receive a copy of the file */
  char *cursor;         /* cursor in file copy */
  char *word_start;     /* start of the current word */

  swallow_file_in_memory (file_name, &file_contents);

  table->start = NULL;
  table->length = 0;

  /* Read the whole file.  */

  cursor = file_contents.start;
  while (cursor < file_contents.end)
    {

      /* Read one line, and save the word in contains.  */

      word_start = cursor;
      while (cursor < file_contents.end && *cursor != '\n')
    cursor++;

      /* Record the word in table if it is not empty.  */

      if (cursor > word_start)
    {
      ALLOC_NEW_WORD (table);
      table->start[table->length].start = word_start;
      table->start[table->length].size = cursor - word_start;
      table->length++;
    }

      /* This test allows for an incomplete line at end of file.  */

      if (cursor < file_contents.end)
    cursor++;
    }

  /* Finally, sort all the words read.  */

  qsort (table->start, table->length, (size_t) sizeof (WORD), compare_words);
}


/* Keyword recognition and selection.  */

/*----------------------------------------------------------------------.
| For each keyword in the source text, constructs an OCCURS structure.  |
`----------------------------------------------------------------------*/

static void
find_occurs_in_text (void)
{
  char *cursor;         /* for scanning the source text */
  char *scan;           /* for scanning the source text also */
  char *line_start;     /* start of the current input line */
  char *line_scan;      /* newlines scanned until this point */
  int reference_length;     /* length of reference in input mode */
  WORD possible_key;        /* possible key, to ease searches */
  OCCURS *occurs_cursor;    /* current OCCURS under construction */

  char *context_start;      /* start of left context */
  char *context_end;        /* end of right context */
  char *word_start;     /* start of word */
  char *word_end;       /* end of word */
  char *next_context_start; /* next start of left context */

  /* reference_length is always used within `if (input_reference)'.
     However, GNU C diagnoses that it may be used uninitialized.  The
     following assignment is merely to shut it up.  */

  reference_length = 0;

  /* Tracking where lines start is helpful for reference processing.  In
     auto reference mode, this allows counting lines.  In input reference
     mode, this permits finding the beginning of the references.

     The first line begins with the file, skip immediately this very first
     reference in input reference mode, to help further rejection any word
     found inside it.  Also, unconditionally assigning these variable has
     the happy effect of shutting up lint.  */

  line_start = text_buffer.start;
  line_scan = line_start;
  if (input_reference)
    {
      SKIP_NON_WHITE (line_scan, text_buffer.end);
      reference_length = line_scan - line_start;
      SKIP_WHITE (line_scan, text_buffer.end);
    }

  /* Process the whole buffer, one line or one sentence at a time.  */

  for (cursor = text_buffer.start;
       cursor < text_buffer.end;
       cursor = next_context_start)
    {

      /* `context_start' gets initialized before the processing of each
     line, or once for the whole buffer if no end of line or sentence
     sequence separator.  */

      context_start = cursor;

      /* If a end of line or end of sentence sequence is defined and
     non-empty, `next_context_start' will be recomputed to be the end of
     each line or sentence, before each one is processed.  If no such
     sequence, then `next_context_start' is set at the end of the whole
     buffer, which is then considered to be a single line or sentence.
     This test also accounts for the case of an incomplete line or
     sentence at the end of the buffer.  */

      if (context_regex_string
      && (re_search (context_regex, cursor, text_buffer.end - cursor,
             0, text_buffer.end - cursor, &context_regs)
          >= 0))
    next_context_start = cursor + context_regs.end[0];

      else
    next_context_start = text_buffer.end;

      /* Include the separator into the right context, but not any suffix
     white space in this separator; this insures it will be seen in
     output and will not take more space than necessary.  */

      context_end = next_context_start;
      SKIP_WHITE_BACKWARDS (context_end, context_start);

      /* Read and process a single input line or sentence, one word at a
     time.  */

      while (1)
    {
      if (word_regex)

        /* If a word regexp has been compiled, use it to skip at the
           beginning of the next word.  If there is no such word, exit
           the loop.  */

        {
          if (re_search (word_regex, cursor, context_end - cursor,
                 0, context_end - cursor, &word_regs)
          < 0)
        break;
          word_start = cursor + word_regs.start[0];
          word_end = cursor + word_regs.end[0];
        }
      else

        /* Avoid re_search and use the fastmap to skip to the
           beginning of the next word.  If there is no more word in
           the buffer, exit the loop.  */

        {
          scan = cursor;
          while (scan < context_end
             && !word_fastmap[(unsigned char) *scan])
        scan++;

          if (scan == context_end)
        break;

          word_start = scan;

          while (scan < context_end
             && word_fastmap[(unsigned char) *scan])
        scan++;

          word_end = scan;
        }

      /* Skip right to the beginning of the found word.  */

      cursor = word_start;

      /* Skip any zero length word.  Just advance a single position,
         then go fetch the next word.  */

      if (word_end == word_start)
        {
          cursor++;
          continue;
        }

      /* This is a genuine, non empty word, so save it as a possible
         key.  Then skip over it.  Also, maintain the maximum length of
         all words read so far.  It is mandatory to take the maximum
         length of all words in the file, without considering if they
         are actually kept or rejected, because backward jumps at output
         generation time may fall in *any* word.  */

      possible_key.start = cursor;
      possible_key.size = word_end - word_start;
      cursor += possible_key.size;

      if (possible_key.size > maximum_word_length)
        maximum_word_length = possible_key.size;

      /* In input reference mode, update `line_start' from its previous
         value.  Count the lines just in case auto reference mode is
         also selected. If it happens that the word just matched is
         indeed part of a reference; just ignore it.  */

      if (input_reference)
        {
          while (line_scan < possible_key.start)
        if (*line_scan == '\n')
          {
            total_line_count++;
            line_scan++;
            line_start = line_scan;
            SKIP_NON_WHITE (line_scan, text_buffer.end);
            reference_length = line_scan - line_start;
          }
        else
          line_scan++;
          if (line_scan > possible_key.start)
        continue;
        }

      /* Ignore the word if an `Ignore words' table exists and if it is
         part of it.  Also ignore the word if an `Only words' table and
         if it is *not* part of it.

         It is allowed that both tables be used at once, even if this
         may look strange for now.  Just ignore a word that would appear
         in both.  If regexps are eventually implemented for these
         tables, the Ignore table could then reject words that would
         have been previously accepted by the Only table.  */

      if (ignore_file && search_table (&possible_key, &ignore_table))
        continue;
      if (only_file && !search_table (&possible_key, &only_table))
        continue;

      /* A non-empty word has been found.  First of all, insure
         proper allocation of the next OCCURS, and make a pointer to
         where it will be constructed.  */

      ALLOC_NEW_OCCURS (0);
      occurs_cursor = occurs_table[0] + number_of_occurs[0];

      /* Define the refence field, if any.  */

      if (auto_reference)
        {

          /* While auto referencing, update `line_start' from its
         previous value, counting lines as we go.  If input
         referencing at the same time, `line_start' has been
         advanced earlier, and the following loop is never really
         executed.  */

          while (line_scan < possible_key.start)
        if (*line_scan == '\n')
          {
            total_line_count++;
            line_scan++;
            line_start = line_scan;
            SKIP_NON_WHITE (line_scan, text_buffer.end);
          }
        else
          line_scan++;

          occurs_cursor->reference = total_line_count;
        }
      else if (input_reference)
        {

          /* If only input referencing, `line_start' has been computed
         earlier to detect the case the word matched would be part
         of the reference.  The reference position is simply the
         value of `line_start'.  */

          occurs_cursor->reference
        = (DELTA) (line_start - possible_key.start);
          if (reference_length > reference_max_width)
        reference_max_width = reference_length;
        }

      /* Exclude the reference from the context in simple cases.  */

      if (input_reference && line_start == context_start)
        {
          SKIP_NON_WHITE (context_start, context_end);
          SKIP_WHITE (context_start, context_end);
        }

      /* Completes the OCCURS structure.  */

      occurs_cursor->key = possible_key;
      occurs_cursor->left = context_start - possible_key.start;
      occurs_cursor->right = context_end - possible_key.start;

      number_of_occurs[0]++;
    }
    }
}


/* Formatting and actual output - service routines.  */

/*-----------------------------------------.
| Prints some NUMBER of spaces on stdout.  |
`-----------------------------------------*/

static void
print_spaces (int number)
{
  int counter;

  for (counter = number; counter > 0; counter--)
    putchar (' ');
}

/*-------------------------------------.
| Prints the field provided by FIELD.  |
`-------------------------------------*/

static void
print_field (BLOCK field)
{
  char *cursor;         /* Cursor in field to print */
  int character;        /* Current character */
  int base;         /* Base character, without diacritic */
  int diacritic;        /* Diacritic code for the character */

  /* Whitespace is not really compressed.  Instead, each white space
     character (tab, vt, ht etc.) is printed as one single space.  */

  for (cursor = field.start; cursor < field.end; cursor++)
    {
      character = (unsigned char) *cursor;
      if (edited_flag[character])
    {

      /* First check if this is a diacriticized character.

         This works only for TeX.  I do not know how diacriticized
         letters work with `roff'.  Please someone explain it to me!  */

      diacritic = todiac (character);
      if (diacritic != 0 && output_format == TEX_FORMAT)
        {
          base = tobase (character);
          switch (diacritic)
        {

        case 1:     /* Latin diphthongs */
          switch (base)
            {
            case 'o':
              fputs ("\\oe{}", stdout);
              break;

            case 'O':
              fputs ("\\OE{}", stdout);
              break;

            case 'a':
              fputs ("\\ae{}", stdout);
              break;

            case 'A':
              fputs ("\\AE{}", stdout);
              break;

            default:
              putchar (' ');
            }
          break;

        case 2:     /* Acute accent */
          printf ("\\'%s%c", (base == 'i' ? "\\" : ""), base);
          break;

        case 3:     /* Grave accent */
          printf ("\\`%s%c", (base == 'i' ? "\\" : ""), base);
          break;

        case 4:     /* Circumflex accent */
          printf ("\\^%s%c", (base == 'i' ? "\\" : ""), base);
          break;

        case 5:     /* Diaeresis */
          printf ("\\\"%s%c", (base == 'i' ? "\\" : ""), base);
          break;

        case 6:     /* Tilde accent */
          printf ("\\~%s%c", (base == 'i' ? "\\" : ""), base);
          break;

        case 7:     /* Cedilla */
          printf ("\\c{%c}", base);
          break;

        case 8:     /* Small circle beneath */
          switch (base)
            {
            case 'a':
              fputs ("\\aa{}", stdout);
              break;

            case 'A':
              fputs ("\\AA{}", stdout);
              break;

            default:
              putchar (' ');
            }
          break;

        case 9:     /* Strike through */
          switch (base)
            {
            case 'o':
              fputs ("\\o{}", stdout);
              break;

            case 'O':
              fputs ("\\O{}", stdout);
              break;

            default:
              putchar (' ');
            }
          break;
        }
        }
      else

        /* This is not a diacritic character, so handle cases which are
           really specific to `roff' or TeX.  All white space processing
           is done as the default case of this switch.  */

        switch (character)
          {
          case '"':
        /* In roff output format, double any quote.  */
        putchar ('"');
        putchar ('"');
        break;

          case '$':
          case '%':
          case '&':
          case '#':
          case '_':
        /* In TeX output format, precede these with a backslash.  */
        putchar ('\\');
        putchar (character);
        break;

          case '{':
          case '}':
        /* In TeX output format, precede these with a backslash and
           force mathematical mode.  */
        printf ("$\\%c$", character);
        break;

          case '\\':
        /* In TeX output mode, request production of a backslash.  */
        fputs ("\\backslash{}", stdout);
        break;

          default:
        /* Any other flagged character produces a single space.  */
        putchar (' ');
          }
    }
      else
    putchar (*cursor);
    }
}


/* Formatting and actual output - planning routines.  */

/*--------------------------------------------------------------------.
| From information collected from command line options and input file |
| readings, compute and fix some output parameter values.         |
`--------------------------------------------------------------------*/

static void
fix_output_parameters (void)
{
  int file_index;       /* index in text input file arrays */
  int line_ordinal;     /* line ordinal value for reference */
  char ordinal_string[12];  /* edited line ordinal for reference */
  int reference_width;      /* width for the whole reference */
  int character;        /* character ordinal */
  const char *cursor;       /* cursor in some constant strings */

  /* In auto reference mode, the maximum width of this field is
     precomputed and subtracted from the overall line width.  Add one for
     the column which separate the file name from the line number.  */

  if (auto_reference)
    {
      reference_max_width = 0;
      for (file_index = 0; file_index < number_input_files; file_index++)
    {
      line_ordinal = file_line_count[file_index] + 1;
      if (file_index > 0)
        line_ordinal -= file_line_count[file_index - 1];
      sprintf (ordinal_string, "%d", line_ordinal);
      reference_width = strlen (ordinal_string);
      if (input_file_name[file_index])
        reference_width += strlen (input_file_name[file_index]);
      if (reference_width > reference_max_width)
        reference_max_width = reference_width;
    }
      reference_max_width++;
      reference.start = (char *) xmalloc ((size_t) reference_max_width + 1);
    }

  /* If the reference appears to the left of the output line, reserve some
     space for it right away, including one gap size.  */

  if ((auto_reference || input_reference) && !right_reference)
    line_width -= reference_max_width + gap_size;

  /* The output lines, minimally, will contain from left to right a left
     context, a gap, and a keyword followed by the right context with no
     special intervening gap.  Half of the line width is dedicated to the
     left context and the gap, the other half is dedicated to the keyword
     and the right context; these values are computed once and for all here.
     There also are tail and head wrap around fields, used when the keyword
     is near the beginning or the end of the line, or when some long word
     cannot fit in, but leave place from wrapped around shorter words.  The
     maximum width of these fields are recomputed separately for each line,
     on a case by case basis.  It is worth noting that it cannot happen that
     both the tail and head fields are used at once.  */

  half_line_width = line_width / 2;
  before_max_width = half_line_width - gap_size;
  keyafter_max_width = half_line_width;

  /* If truncation_string is the empty string, make it NULL to speed up
     tests.  In this case, truncation_string_length will never get used, so
     there is no need to set it.  */

  if (truncation_string && *truncation_string)
    truncation_string_length = strlen (truncation_string);
  else
    truncation_string = NULL;

  if (gnu_extensions)
    {

      /* When flagging truncation at the left of the keyword, the
     truncation mark goes at the beginning of the before field,
     unless there is a head field, in which case the mark goes at the
     left of the head field.  When flagging truncation at the right
     of the keyword, the mark goes at the end of the keyafter field,
     unless there is a tail field, in which case the mark goes at the
     end of the tail field.  Only eight combination cases could arise
     for truncation marks:

     . None.
     . One beginning the before field.
     . One beginning the head field.
     . One ending the keyafter field.
     . One ending the tail field.
     . One beginning the before field, another ending the keyafter field.
     . One ending the tail field, another beginning the before field.
     . One ending the keyafter field, another beginning the head field.

     So, there is at most two truncation marks, which could appear both
     on the left side of the center of the output line, both on the
     right side, or one on either side.  */

      before_max_width -= 2 * truncation_string_length;
      keyafter_max_width -= 2 * truncation_string_length;
    }
  else
    {

      /* I never figured out exactly how UNIX' ptx plans the output width
     of its various fields.  If GNU extensions are disabled, do not
     try computing the field widths correctly; instead, use the
     following formula, which does not completely imitate UNIX' ptx,
     but almost.  */

      keyafter_max_width -= 2 * truncation_string_length + 1;
    }

  /* Compute which characters need special output processing.  Initialize
     by flagging any white space character.  Some systems do not consider
     form feed as a space character, but we do.  */

  for (character = 0; character < CHAR_SET_SIZE; character++)
    edited_flag[character] = ISSPACE (character) != 0;
  edited_flag['\f'] = 1;

  /* Complete the special character flagging according to selected output
     format.  */

  switch (output_format)
    {
    case UNKNOWN_FORMAT:
      /* Should never happen.  */

    case DUMB_FORMAT:
      break;

    case ROFF_FORMAT:

      /* `Quote' characters should be doubled.  */

      edited_flag['"'] = 1;
      break;

    case TEX_FORMAT:

      /* Various characters need special processing.  */

      for (cursor = "$%&#_{}\\"; *cursor; cursor++)
    edited_flag[(unsigned char) *cursor] = 1;

      /* Any character with 8th bit set will print to a single space, unless
     it is diacriticized.  */

      for (character = 0200; character < CHAR_SET_SIZE; character++)
    edited_flag[character] = todiac (character) != 0;
      break;
    }
}

/*------------------------------------------------------------------.
| Compute the position and length of all the output fields, given a |
| pointer to some OCCURS.                       |
`------------------------------------------------------------------*/

static void
define_all_fields (OCCURS *occurs)
{
  int tail_max_width;       /* allowable width of tail field */
  int head_max_width;       /* allowable width of head field */
  char *cursor;         /* running cursor in source text */
  char *left_context_start; /* start of left context */
  char *right_context_end;  /* end of right context */
  char *left_field_start;   /* conservative start for `head'/`before' */
  int file_index;       /* index in text input file arrays */
  const char *file_name;    /* file name for reference */
  int line_ordinal;     /* line ordinal for reference */

  /* Define `keyafter', start of left context and end of right context.
     `keyafter' starts at the saved position for keyword and extend to the
     right from the end of the keyword, eating separators or full words, but
     not beyond maximum allowed width for `keyafter' field or limit for the
     right context.  Suffix spaces will be removed afterwards.  */

  keyafter.start = occurs->key.start;
  keyafter.end = keyafter.start + occurs->key.size;
  left_context_start = keyafter.start + occurs->left;
  right_context_end = keyafter.start + occurs->right;

  cursor = keyafter.end;
  while (cursor < right_context_end
     && cursor <= keyafter.start + keyafter_max_width)
    {
      keyafter.end = cursor;
      SKIP_SOMETHING (cursor, right_context_end);
    }
  if (cursor <= keyafter.start + keyafter_max_width)
    keyafter.end = cursor;

  keyafter_truncation = truncation_string && keyafter.end < right_context_end;

  SKIP_WHITE_BACKWARDS (keyafter.end, keyafter.start);

  /* When the left context is wide, it might take some time to catch up from
     the left context boundary to the beginning of the `head' or `before'
     fields.  So, in this case, to speed the catchup, we jump back from the
     keyword, using some secure distance, possibly falling in the middle of
     a word.  A secure backward jump would be at least half the maximum
     width of a line, plus the size of the longest word met in the whole
     input.  We conclude this backward jump by a skip forward of at least
     one word.  In this manner, we should not inadvertently accept only part
     of a word.  From the reached point, when it will be time to fix the
     beginning of `head' or `before' fields, we will skip forward words or
     delimiters until we get sufficiently near.  */

  if (-occurs->left > half_line_width + maximum_word_length)
    {
      left_field_start
    = keyafter.start - (half_line_width + maximum_word_length);
      SKIP_SOMETHING (left_field_start, keyafter.start);
    }
  else
    left_field_start = keyafter.start + occurs->left;

  /* `before' certainly ends at the keyword, but not including separating
     spaces.  It starts after than the saved value for the left context, by
     advancing it until it falls inside the maximum allowed width for the
     before field.  There will be no prefix spaces either.  `before' only
     advances by skipping single separators or whole words. */

  before.start = left_field_start;
  before.end = keyafter.start;
  SKIP_WHITE_BACKWARDS (before.end, before.start);

  while (before.start + before_max_width < before.end)
    SKIP_SOMETHING (before.start, before.end);

  if (truncation_string)
    {
      cursor = before.start;
      SKIP_WHITE_BACKWARDS (cursor, text_buffer.start);
      before_truncation = cursor > left_context_start;
    }
  else
    before_truncation = 0;

  SKIP_WHITE (before.start, text_buffer.end);

  /* The tail could not take more columns than what has been left in the
     left context field, and a gap is mandatory.  It starts after the
     right context, and does not contain prefixed spaces.  It ends at
     the end of line, the end of buffer or when the tail field is full,
     whichever comes first.  It cannot contain only part of a word, and
     has no suffixed spaces.  */

  tail_max_width
    = before_max_width - (before.end - before.start) - gap_size;

  if (tail_max_width > 0)
    {
      tail.start = keyafter.end;
      SKIP_WHITE (tail.start, text_buffer.end);

      tail.end = tail.start;
      cursor = tail.end;
      while (cursor < right_context_end
         && cursor < tail.start + tail_max_width)
    {
      tail.end = cursor;
      SKIP_SOMETHING (cursor, right_context_end);
    }

      if (cursor < tail.start + tail_max_width)
    tail.end = cursor;

      if (tail.end > tail.start)
    {
      keyafter_truncation = 0;
      tail_truncation = truncation_string && tail.end < right_context_end;
    }
      else
    tail_truncation = 0;

      SKIP_WHITE_BACKWARDS (tail.end, tail.start);
    }
  else
    {

      /* No place left for a tail field.  */

      tail.start = NULL;
      tail.end = NULL;
      tail_truncation = 0;
    }

  /* `head' could not take more columns than what has been left in the right
     context field, and a gap is mandatory.  It ends before the left
     context, and does not contain suffixed spaces.  Its pointer is advanced
     until the head field has shrunk to its allowed width.  It cannot
     contain only part of a word, and has no suffixed spaces.  */

  head_max_width
    = keyafter_max_width - (keyafter.end - keyafter.start) - gap_size;

  if (head_max_width > 0)
    {
      head.end = before.start;
      SKIP_WHITE_BACKWARDS (head.end, text_buffer.start);

      head.start = left_field_start;
      while (head.start + head_max_width < head.end)
    SKIP_SOMETHING (head.start, head.end);

      if (head.end > head.start)
    {
      before_truncation = 0;
      head_truncation = (truncation_string
                 && head.start > left_context_start);
    }
      else
    head_truncation = 0;

      SKIP_WHITE (head.start, head.end);
    }
  else
    {

      /* No place left for a head field.  */

      head.start = NULL;
      head.end = NULL;
      head_truncation = 0;
    }

  if (auto_reference)
    {

      /* Construct the reference text in preallocated space from the file
     name and the line number.  Find out in which file the reference
     occurred.  Standard input yields an empty file name.  Insure line
     numbers are one based, even if they are computed zero based.  */

      file_index = 0;
      while (file_line_count[file_index] < occurs->reference)
    file_index++;

      file_name = input_file_name[file_index];
      if (!file_name)
    file_name = "";

      line_ordinal = occurs->reference + 1;
      if (file_index > 0)
    line_ordinal -= file_line_count[file_index - 1];

      sprintf (reference.start, "%s:%d", file_name, line_ordinal);
      reference.end = reference.start + strlen (reference.start);
    }
  else if (input_reference)
    {

      /* Reference starts at saved position for reference and extends right
     until some white space is met.  */

      reference.start = keyafter.start + (DELTA) occurs->reference;
      reference.end = reference.start;
      SKIP_NON_WHITE (reference.end, right_context_end);
    }
}


/* Formatting and actual output - control routines.  */

/*----------------------------------------------------------------------.
| Output the current output fields as one line for `troff' or `nroff'.  |
`----------------------------------------------------------------------*/

static void
output_one_roff_line (void)
{
  /* Output the `tail' field.  */

  printf (".%s \"", macro_name);
  print_field (tail);
  if (tail_truncation)
    fputs (truncation_string, stdout);
  putchar ('"');

  /* Output the `before' field.  */

  fputs (" \"", stdout);
  if (before_truncation)
    fputs (truncation_string, stdout);
  print_field (before);
  putchar ('"');

  /* Output the `keyafter' field.  */

  fputs (" \"", stdout);
  print_field (keyafter);
  if (keyafter_truncation)
    fputs (truncation_string, stdout);
  putchar ('"');

  /* Output the `head' field.  */

  fputs (" \"", stdout);
  if (head_truncation)
    fputs (truncation_string, stdout);
  print_field (head);
  putchar ('"');

  /* Conditionally output the `reference' field.  */

  if (auto_reference || input_reference)
    {
      fputs (" \"", stdout);
      print_field (reference);
      putchar ('"');
    }

  putchar ('\n');
}

/*---------------------------------------------------------.
| Output the current output fields as one line for `TeX'.  |
`---------------------------------------------------------*/

static void
output_one_tex_line (void)
{
  BLOCK key;            /* key field, isolated */
  BLOCK after;          /* after field, isolated */
  char *cursor;         /* running cursor in source text */

  printf ("\\%s ", macro_name);
  putchar ('{');
  print_field (tail);
  fputs ("}{", stdout);
  print_field (before);
  fputs ("}{", stdout);
  key.start = keyafter.start;
  after.end = keyafter.end;
  cursor = keyafter.start;
  SKIP_SOMETHING (cursor, keyafter.end);
  key.end = cursor;
  after.start = cursor;
  print_field (key);
  fputs ("}{", stdout);
  print_field (after);
  fputs ("}{", stdout);
  print_field (head);
  putchar ('}');
  if (auto_reference || input_reference)
    {
      putchar ('{');
      print_field (reference);
      putchar ('}');
    }
  putchar ('\n');
}

/*-------------------------------------------------------------------.
| Output the current output fields as one line for a dumb terminal.  |
`-------------------------------------------------------------------*/

static void
output_one_dumb_line (void)
{
  if (!right_reference)
    {
      if (auto_reference)
    {

      /* Output the `reference' field, in such a way that GNU emacs
         next-error will handle it.  The ending colon is taken from the
         gap which follows.  */

      print_field (reference);
      putchar (':');
      print_spaces (reference_max_width
            + gap_size
            - (reference.end - reference.start)
            - 1);
    }
      else
    {

      /* Output the `reference' field and its following gap.  */

      print_field (reference);
      print_spaces (reference_max_width
            + gap_size
            - (reference.end - reference.start));
    }
    }

  if (tail.start < tail.end)
    {
      /* Output the `tail' field.  */

      print_field (tail);
      if (tail_truncation)
    fputs (truncation_string, stdout);

      print_spaces (half_line_width - gap_size
            - (before.end - before.start)
            - (before_truncation ? truncation_string_length : 0)
            - (tail.end - tail.start)
            - (tail_truncation ? truncation_string_length : 0));
    }
  else
    print_spaces (half_line_width - gap_size
          - (before.end - before.start)
          - (before_truncation ? truncation_string_length : 0));

  /* Output the `before' field.  */

  if (before_truncation)
    fputs (truncation_string, stdout);
  print_field (before);

  print_spaces (gap_size);

  /* Output the `keyafter' field.  */

  print_field (keyafter);
  if (keyafter_truncation)
    fputs (truncation_string, stdout);

  if (head.start < head.end)
    {
      /* Output the `head' field.  */

      print_spaces (half_line_width
            - (keyafter.end - keyafter.start)
            - (keyafter_truncation ? truncation_string_length : 0)
            - (head.end - head.start)
            - (head_truncation ? truncation_string_length : 0));
      if (head_truncation)
    fputs (truncation_string, stdout);
      print_field (head);
    }
  else

    if ((auto_reference || input_reference) && right_reference)
      print_spaces (half_line_width
            - (keyafter.end - keyafter.start)
            - (keyafter_truncation ? truncation_string_length : 0));

  if ((auto_reference || input_reference) && right_reference)
    {
      /* Output the `reference' field.  */

      print_spaces (gap_size);
      print_field (reference);
    }

  putchar ('\n');
}

/*------------------------------------------------------------------------.
| Scan the whole occurs table and, for each entry, output one line in the |
| appropriate format.                             |
`------------------------------------------------------------------------*/

static void
generate_all_output (void)
{
  size_t occurs_index;      /* index of keyword entry being processed */
  OCCURS *occurs_cursor;    /* current keyword entry being processed */

  /* The following assignments are useful to provide default values in case
     line contexts or references are not used, in which case these variables
     would never be computed.  */

  tail.start = NULL;
  tail.end = NULL;
  tail_truncation = 0;

  head.start = NULL;
  head.end = NULL;
  head_truncation = 0;

  /* Loop over all keyword occurrences.  */

  occurs_cursor = occurs_table[0];

  for (occurs_index = 0; occurs_index < number_of_occurs[0]; occurs_index++)
    {
      /* Compute the exact size of every field and whenever truncation flags
     are present or not.  */

      define_all_fields (occurs_cursor);

      /* Produce one output line according to selected format.  */

      switch (output_format)
    {
    case UNKNOWN_FORMAT:
      /* Should never happen.  */

    case DUMB_FORMAT:
      output_one_dumb_line ();
      break;

    case ROFF_FORMAT:
      output_one_roff_line ();
      break;

    case TEX_FORMAT:
      output_one_tex_line ();
      break;
    }

      /* Advance the cursor into the occurs table.  */

      occurs_cursor++;
    }
}


/* Option decoding and main program.  */

/*------------------------------------------------------.
| Print program identification and options, then exit.  |
`------------------------------------------------------*/

void
usage (int status)
{
  if (status != EXIT_SUCCESS)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [INPUT]...   (without -G)\n\
  or:  %s -G [OPTION]... [INPUT [OUTPUT]]\n"),
          program_name, program_name);
      fputs (_("\
Output a permuted index, including context, of the words in the input files.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -A, --auto-reference           output automatically generated references\n\
  -C, --copyright                display Copyright and copying conditions\n\
  -G, --traditional              behave more like System V `ptx'\n\
  -F, --flag-truncation=STRING   use STRING for flagging line truncations\n\
"), stdout);
      fputs (_("\
  -M, --macro-name=STRING        macro name to use instead of `xx'\n\
  -O, --format=roff              generate output as roff directives\n\
  -R, --right-side-refs          put references at right, not counted in -w\n\
  -S, --sentence-regexp=REGEXP   for end of lines or end of sentences\n\
  -T, --format=tex               generate output as TeX directives\n\
"), stdout);
      fputs (_("\
  -W, --word-regexp=REGEXP       use REGEXP to match each keyword\n\
  -b, --break-file=FILE          word break characters in this FILE\n\
  -f, --ignore-case              fold lower case to upper case for sorting\n\
  -g, --gap-size=NUMBER          gap size in columns between output fields\n\
  -i, --ignore-file=FILE         read ignore word list from FILE\n\
  -o, --only-file=FILE           read only word list from this FILE\n\
"), stdout);
      fputs (_("\
  -r, --references               first field of each line is a reference\n\
  -t, --typeset-mode               - not implemented -\n\
  -w, --width=NUMBER             output width in columns, reference excluded\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
With no FILE or if FILE is -, read Standard Input.  `-F /' by default.\n\
"), stdout);
    }
  exit (status);
}

/*----------------------------------------------------------------------.
| Main program.  Decode ARGC arguments passed through the ARGV array of |
| strings, then launch execution.                       |
`----------------------------------------------------------------------*/

/* Long options equivalences.  */
static const struct option long_options[] =
{
  {"auto-reference", no_argument, NULL, 'A'},
  {"break-file", required_argument, NULL, 'b'},
  {"copyright", no_argument, NULL, 'C'},
  {"flag-truncation", required_argument, NULL, 'F'},
  {"ignore-case", no_argument, NULL, 'f'},
  {"gap-size", required_argument, NULL, 'g'},
  {"ignore-file", required_argument, NULL, 'i'},
  {"macro-name", required_argument, NULL, 'M'},
  {"only-file", required_argument, NULL, 'o'},
  {"references", no_argument, NULL, 'r'},
  {"right-side-refs", no_argument, NULL, 'R'},
  {"format", required_argument, NULL, 10},
  {"sentence-regexp", required_argument, NULL, 'S'},
  {"traditional", no_argument, NULL, 'G'},
  {"typeset-mode", no_argument, NULL, 't'},
  {"width", required_argument, NULL, 'w'},
  {"word-regexp", required_argument, NULL, 'W'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {0, 0, 0, 0},
};

static char const* const format_args[] =
{
  "roff", "tex", 0
};

static enum Format const format_vals[] =
{
  ROFF_FORMAT, TEX_FORMAT
};

int
main (int argc, char **argv)
{
  int optchar;          /* argument character */
  int file_index;       /* index in text input file arrays */

  /* Decode program options.  */

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

#if HAVE_SETCHRCLASS
  setchrclass (NULL);
#endif

  while (optchar = getopt_long (argc, argv, "ACF:GM:ORS:TW:b:i:fg:o:trw:",
                long_options, NULL),
     optchar != EOF)
    {
      switch (optchar)
    {
    default:
      usage (EXIT_FAILURE);

    case 0:
      break;

    case 'C':
      fputs (_("\
This program is free software; you can redistribute it and/or modify\n\
it under the terms of the GNU General Public License as published by\n\
the Free Software Foundation; either version 2, or (at your option)\n\
any later version.\n\
\n\
"), stdout);
      fputs (_("\
This program is distributed in the hope that it will be useful,\n\
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
GNU General Public License for more details.\n\
\n\
"), stdout);
      fputs (_("\
You should have received a copy of the GNU General Public License\n\
along with this program; if not, write to the Free Software Foundation,\n\
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\n"),
         stdout);

      exit (EXIT_SUCCESS);

    case 'G':
      gnu_extensions = 0;
      break;

    case 'b':
      break_file = optarg;
      break;

    case 'f':
      ignore_case = 1;
      break;

    case 'g':
      gap_size = atoi (optarg);
      break;

    case 'i':
      ignore_file = optarg;
      break;

    case 'o':
      only_file = optarg;
      break;

    case 'r':
      input_reference = 1;
      break;

    case 't':
      /* Yet to understand...  */
      break;

    case 'w':
      line_width = atoi (optarg);
      break;

    case 'A':
      auto_reference = 1;
      break;

    case 'F':
      truncation_string = copy_unescaped_string (optarg);
      break;

    case 'M':
      macro_name = optarg;
      break;

    case 'O':
      output_format = ROFF_FORMAT;
      break;

    case 'R':
      right_reference = 1;
      break;

    case 'S':
      context_regex_string = copy_unescaped_string (optarg);
      break;

    case 'T':
      output_format = TEX_FORMAT;
      break;

    case 'W':
      word_regex_string = copy_unescaped_string (optarg);
      break;

    case 10:
      output_format = XARGMATCH ("--format", optarg,
                     format_args, format_vals);
    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    }
    }

  /* Change the default Ignore file if one is defined.  */

#ifdef DEFAULT_IGNORE_FILE
  if (!ignore_file)
    ignore_file = DEFAULT_IGNORE_FILE;
#endif

  /* Process remaining arguments.  If GNU extensions are enabled, process
     all arguments as input parameters.  If disabled, accept at most two
     arguments, the second of which is an output parameter.  */

  if (optind == argc)
    {

      /* No more argument simply means: read standard input.  */

      input_file_name = (const char **) xmalloc (sizeof (const char *));
      file_line_count = (int *) xmalloc (sizeof (int));
      number_input_files = 1;
      input_file_name[0] = NULL;
    }
  else if (gnu_extensions)
    {
      number_input_files = argc - optind;
      input_file_name
    = (const char **) xmalloc (number_input_files * sizeof (const char *));
      file_line_count
    = (int *) xmalloc (number_input_files * sizeof (int));

      for (file_index = 0; file_index < number_input_files; file_index++)
    {
      input_file_name[file_index] = argv[optind];
      if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
        input_file_name[0] = NULL;
      else
        input_file_name[0] = argv[optind];
      optind++;
    }
    }
  else
    {

      /* There is one necessary input file.  */

      number_input_files = 1;
      input_file_name = (const char **) xmalloc (sizeof (const char *));
      file_line_count = (int *) xmalloc (sizeof (int));
      if (!*argv[optind] || strcmp (argv[optind], "-") == 0)
    input_file_name[0] = NULL;
      else
    input_file_name[0] = argv[optind];
      optind++;

      /* Redirect standard output, only if requested.  */

      if (optind < argc)
    {
      /* FIXME: don't fclose here? */
      fclose (stdout);
      if (fopen (argv[optind], "w") == NULL)
        error (EXIT_FAILURE, errno, "%s", argv[optind]);
      optind++;
    }

      /* Diagnose any other argument as an error.  */

      if (optind < argc)
    usage (EXIT_FAILURE);
    }

  /* If the output format has not been explicitly selected, choose dumb
     terminal format if GNU extensions are enabled, else `roff' format.  */

  if (output_format == UNKNOWN_FORMAT)
    output_format = gnu_extensions ? DUMB_FORMAT : ROFF_FORMAT;

  /* Initialize the main tables.  */

  initialize_regex ();

  /* Read `Break character' file, if any.  */

  if (break_file)
    digest_break_file (break_file);

  /* Read `Ignore words' file and `Only words' files, if any.  If any of
     these files is empty, reset the name of the file to NULL, to avoid
     unnecessary calls to search_table. */

  if (ignore_file)
    {
      digest_word_file (ignore_file, &ignore_table);
      if (ignore_table.length == 0)
    ignore_file = NULL;
    }

  if (only_file)
    {
      digest_word_file (only_file, &only_table);
      if (only_table.length == 0)
    only_file = NULL;
    }

  /* Prepare to study all the input files.  */

  number_of_occurs[0] = 0;
  total_line_count = 0;
  maximum_word_length = 0;
  reference_max_width = 0;

  for (file_index = 0; file_index < number_input_files; file_index++)
    {

      /* Read the file in core, than study it.  */

      swallow_file_in_memory (input_file_name[file_index], &text_buffer);
      find_occurs_in_text ();

      /* Maintain for each file how many lines has been read so far when its
     end is reached.  Incrementing the count first is a simple kludge to
     handle a possible incomplete line at end of file.  */

      total_line_count++;
      file_line_count[file_index] = total_line_count;
    }

  /* Do the output process phase.  */

  sort_found_occurs ();
  fix_output_parameters ();
  generate_all_output ();

  /* All done.  */

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/argmatch.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/argmatch.h used in: Step 911, Step 929, Step 939, Step 951
/* argmatch.h -- definitions and prototypes for argmatch.c
   Copyright (C) 1990, 1998, 1999, 2001, 2002 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.  */

/* Written by David MacKenzie <djm@ai.mit.edu>
   Modified by Akim Demaille <demaille@inf.enst.fr> */

#ifndef ARGMATCH_H_
# define ARGMATCH_H_ 1

# include <stddef.h>

# define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))

# define ARGMATCH_CONSTRAINT(Arglist, Vallist) \
  (ARRAY_CARDINALITY (Arglist) == ARRAY_CARDINALITY (Vallist) + 1)

/* Assert there are as many real arguments as there are values
   (argument list ends with a NULL guard).  ARGMATCH_VERIFY is
   preferred, since it is guaranteed to be checked at compile-time.
   ARGMATCH_ASSERT is for backward compatibility only.  */

# define ARGMATCH_VERIFY(Arglist, Vallist)                \
  struct argmatch_verify                          \
  {                                   \
    char argmatch_verify[ARGMATCH_CONSTRAINT(Arglist, Vallist) ? 1 : -1]; \
  }

# define ARGMATCH_ASSERT(Arglist, Vallist) \
  assert (ARGMATCH_CONSTRAINT (Arglist, Vallist))

/* Return the index of the element of ARGLIST (NULL terminated) that
   matches with ARG.  If VALLIST is not NULL, then use it to resolve
   false ambiguities (i.e., different matches of ARG but corresponding
   to the same values in VALLIST).  */

int argmatch (char const *arg, char const *const *arglist,
          char const *vallist, size_t valsize);

# define ARGMATCH(Arg, Arglist, Vallist) \
  argmatch (Arg, Arglist, (char const *) (Vallist), sizeof *(Vallist))

/* xargmatch calls this function when it fails.  This function should not
   return.  By default, this is a function that calls ARGMATCH_DIE which
   in turn defaults to `exit (EXIT_FAILURE)'.  */
typedef void (*argmatch_exit_fn) (void);
extern argmatch_exit_fn argmatch_die;

/* Report on stderr why argmatch failed.  Report correct values. */

void argmatch_invalid (char const *context, char const *value, int problem);

/* Left for compatibility with the old name invalid_arg */

# define invalid_arg(Context, Value, Problem) \
  argmatch_invalid (Context, Value, Problem)



/* Report on stderr the list of possible arguments.  */

void argmatch_valid (char const *const *arglist,
             char const *vallist, size_t valsize);

# define ARGMATCH_VALID(Arglist, Vallist) \
  argmatch_valid (Arglist, (char const *) (Vallist), sizeof *(Vallist))



/* Same as argmatch, but upon failure, reports a explanation on the
   failure, and exits using the function EXIT_FN. */

int __xargmatch_internal (char const *context,
              char const *arg, char const *const *arglist,
              char const *vallist, size_t valsize,
              argmatch_exit_fn exit_fn);

/* Programmer friendly interface to __xargmatch_internal. */

# define XARGMATCH(Context, Arg, Arglist, Vallist)      \
  ((Vallist) [__xargmatch_internal (Context, Arg, Arglist,  \
                    (char const *) (Vallist),   \
                    sizeof *(Vallist),      \
                    argmatch_die)])

/* Convert a value into a corresponding argument. */

char const *argmatch_to_argument (char const *value,
                  char const *const *arglist,
                  char const *vallist, size_t valsize);

# define ARGMATCH_TO_ARGUMENT(Value, Arglist, Vallist)          \
  argmatch_to_argument (Value, Arglist,                 \
            (char const *) (Vallist), sizeof *(Vallist))

#endif /* ARGMATCH_H_ */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/bumpalloc.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/bumpalloc.h used in: Step 911
/* BUMP_ALLOC macro - increase table allocation by one element.
   Copyright (C) 1990, 1991, 1993, 1998, 2000 Free Software Foundation, Inc.
   François Pinard <pinard@iro.umontreal.ca>, 1990.

   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.  */

/*-------------------------------------------------------------------------.
| Bump the allocation of the array pointed to by TABLE whenever required.  |
| The table already has already COUNT elements in it, this macro ensure it |
| has enough space to accommodate at least one more element.  Space is     |
| allocated (2 ^ EXPONENT) elements at a time.  Each element of the array  |
| is of type TYPE.                             |
`-------------------------------------------------------------------------*/

/* Routines `xmalloc' and `xrealloc' are called to do the actual memory
   management.  This implies that the program will abort with a "memory
   exhausted" error if any problem arise.

   To work correctly, at least EXPONENT and TYPE should always be the
   same for all uses of this macro for any given TABLE.  A secure way to
   achieve this is to never use this macro directly, but use it to define
   other macros, which would then be TABLE-specific.

   The first time through, COUNT is usually zero.  Note that COUNT is not
   updated by this macro, but it should be update elsewhere, later.  This
   is convenient, because it allows TABLE[COUNT] to refer to the new
   element at the end.  Once its construction is completed, COUNT++ will
   record it in the table.  Calling this macro several times in a row
   without updating COUNT is a bad thing to do.  */

#define BUMP_ALLOC(Table, Count, Exponent, Type) \
  BUMP_ALLOC_WITH_SIZE ((Table), (Count), (Exponent), Type, sizeof (Type))

/* In cases `sizeof TYPE' would not always yield the correct value for
   the size of each element entry, this macro accepts a supplementary
   SIZE argument.  The EXPONENT, TYPE and SIZE parameters should still
   have the same value for all macro calls related to a specific TABLE.  */

#define BUMP_ALLOC_WITH_SIZE(Table, Count, Exponent, Type, Size)    \
  do                                    \
    {                                   \
      if (((Count) & (~(~0 << (Exponent)))) == 0)           \
    {                               \
      if ((Count) == 0)                     \
        (Table) = (Type *) xmalloc ((1 << (Exponent)) * (Size));    \
      else                              \
        (Table) = (Type *)                      \
          xrealloc ((Table), ((Count) + (1 << (Exponent))) * (Size)); \
    }                               \
    }                                   \
  while (0)

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/diacrit.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/diacrit.h used in: Step 911
/* Diacritics processing for a few character codes.
   Copyright (C) 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
   François Pinard <pinard@iro.umontreal.ca>, 1988.

   All this file is a temporary hack, waiting for locales in GNU.
*/

extern const char diacrit_base[]; /* characters without diacritics */
extern const char diacrit_diac[]; /* diacritic code for each character */

/* Returns CHAR without its diacritic.  CHAR is known to be alphabetic.  */
#define tobase(Char) (diacrit_base[(unsigned char) (Char)])

/* Returns a diacritic code for CHAR.  CHAR is known to be alphabetic.  */
#define todiac(Char) (diacrit_diac[(unsigned char) (Char)])

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/pwd.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/pwd.c used in: Step 913
/* pwd - print current directory
   Copyright (C) 1994-1997, 1999-2003 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.  */

/* Jim Meyering <meyering@comco.com> */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>

#include "system.h"
#include "closeout.h"
#include "long-options.h"
#include "error.h"
#include "xgetcwd.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "pwd"

#define AUTHORS "Jim Meyering"

/* The name this program was run with. */
char *program_name;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]\n"), program_name);
      fputs (_("\
Print the full filename of the current working directory.\n\
\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  char *wd;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              AUTHORS, usage);

  if (argc != 1)
    error (0, 0, _("ignoring non-option arguments"));

  wd = xgetcwd ();
  if (wd == NULL)
    error (EXIT_FAILURE, errno, _("cannot get current directory"));
  printf ("%s\n", wd);

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xgetcwd.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xgetcwd.h used in: Step 913
/* prototype for xgetcwd
   Copyright (C) 2003 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.  */

extern char *xgetcwd (void);

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/readlink.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/readlink.c used in: Step 915
/* readlink -- display value of a symbolic link.
   Copyright (C) 2002, 2003 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.  */

/* Written by Dmitry V. Levin */

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <getopt.h>

#include "system.h"
#include "canonicalize.h"
#include "error.h"
#include "xreadlink.h"
#include "long-options.h"
#include "quote.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "readlink"

#define AUTHORS "Dmitry V. Levin"

/* Name this program was run with.  */
char *program_name;

  /* If nonzero, canonicalize file name. */
static int canonicalize;
  /* If nonzero, do not output the trailing newline. */
static int no_newline;
  /* If nonzero, report error messages. */
static int verbose;

static struct option const longopts[] =
{
  {"canonicalize", no_argument, 0, 'f'},
  {"no-newline", no_argument, 0, 'n'},
  {"quiet", no_argument, 0, 'q'},
  {"silent", no_argument, 0, 's'},
  {"verbose", no_argument, 0, 'v'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != EXIT_SUCCESS)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]... FILE\n"), program_name);
      fputs (_("Display value of a symbolic link on standard output.\n\n"),
         stdout);
      fputs (_("\
  -f, --canonicalize      canonicalize by following every symlink in every\n\
                          component of the given path recursively\n\
  -n, --no-newline        do not output the trailing newline\n\
  -q, --quiet,\n\
  -s, --silent            suppress most error messages\n\
  -v, --verbose           report error messages\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char *const argv[])
{
  const char *fname;
  char *value;
  int optc;

  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  if (argc < 1)
    error (EXIT_FAILURE, 0, _("too few arguments"));

  program_name = argv[0];

  while ((optc = getopt_long (argc, argv, "fnqsv", longopts, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:
      break;
    case 'f':
      canonicalize = 1;
      break;
    case 'n':
      no_newline = 1;
      break;
    case 'q':
    case 's':
      verbose = 0;
      break;
    case 'v':
      verbose = 1;
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (EXIT_FAILURE);
    }
    }

  if (optind >= argc)
    {
      error (EXIT_SUCCESS, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }

  fname = argv[optind++];

  if (optind < argc)
    {
      error (EXIT_SUCCESS, 0, _("too many arguments"));
      usage (EXIT_FAILURE);
    }

  value = (canonicalize ? canonicalize_file_name : xreadlink) (fname);
  if (value)
    {
      printf ("%s%s", value, (no_newline ? "" : "\n"));
      free (value);
      return EXIT_SUCCESS;
    }

  if (verbose)
    error (EXIT_SUCCESS, errno, "%s", fname);

  return EXIT_FAILURE;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/canonicalize.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/canonicalize.h used in: Step 915
#if !HAVE_CANONICALIZE_FILE_NAME
char *canonicalize_file_name (const char *);
#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xreadlink.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xreadlink.h used in: Step 915
#ifndef PARAMS
# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#  define PARAMS(Args) Args
# else
#  define PARAMS(Args) ()
# endif
#endif

char *xreadlink PARAMS ((char const *));

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/rmdir.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/rmdir.c used in: Step 917
/* rmdir -- remove directories
   Copyright (C) 90, 91, 1995-2002 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.  */

/* Options:
   -p, --parent     Remove any parent dirs that are explicitly mentioned
            in an argument, if they become empty after the
            argument file is removed.

   David MacKenzie <djm@ai.mit.edu>  */

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>

#include "system.h"
#include "dirname.h"
#include "error.h"
#include "quote.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "rmdir"

#define AUTHORS "David MacKenzie"

#ifndef EEXIST
# define EEXIST 0
#endif

#ifndef ENOTEMPTY
# define ENOTEMPTY 0
#endif

/* The name this program was run with. */
char *program_name;

/* If nonzero, remove empty parent directories. */
static int empty_paths;

/* If nonzero, don't treat failure to remove a nonempty directory
   as an error.  */
static int ignore_fail_on_non_empty;

/* If nonzero, output a diagnostic for every directory processed.  */
static int verbose;

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  IGNORE_FAIL_ON_NON_EMPTY_OPTION = CHAR_MAX + 1
};

static struct option const longopts[] =
{
  /* Don't name this `--force' because it's not close enough in meaning
     to e.g. rm's -f option.  */
  {"ignore-fail-on-non-empty", no_argument, NULL,
   IGNORE_FAIL_ON_NON_EMPTY_OPTION},

  {"path", no_argument, NULL, 'p'},
  {"parents", no_argument, NULL, 'p'},
  {"verbose", no_argument, NULL, 'v'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

/* Return nonzero if ERROR_NUMBER is one of the values associated
   with a failed rmdir due to non-empty target directory.  */

static int
errno_rmdir_non_empty (int error_number)
{
  return (error_number == RMDIR_ERRNO_NOT_EMPTY);
}

/* Remove any empty parent directories of PATH.
   If PATH contains slash characters, at least one of them
   (beginning with the rightmost) is replaced with a NUL byte.  */

static int
remove_parents (char *path)
{
  char *slash;
  int fail = 0;

  strip_trailing_slashes (path);
  while (1)
    {
      slash = strrchr (path, '/');
      if (slash == NULL)
    break;
      /* Remove any characters after the slash, skipping any extra
     slashes in a row. */
      while (slash > path && *slash == '/')
    --slash;
      slash[1] = 0;

      /* Give a diagnostic for each attempted removal if --verbose.  */
      if (verbose)
    error (0, 0, _("removing directory, %s"), path);

      fail = rmdir (path);

      if (fail)
    {
      /* Stop quietly if --ignore-fail-on-non-empty. */
      if (ignore_fail_on_non_empty
          && errno_rmdir_non_empty (errno))
        {
          fail = 0;
        }
      else
        {
          error (0, errno, "%s", quote (path));
        }
      break;
    }
    }
  return fail;
}

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]... DIRECTORY...\n"), program_name);
      fputs (_("\
Remove the DIRECTORY(ies), if they are empty.\n\
\n\
      --ignore-fail-on-non-empty\n\
                  ignore each failure that is solely because a directory\n\
                  is non-empty\n\
"), stdout);
      fputs (_("\
  -p, --parents   remove DIRECTORY, then try to remove each directory\n\
                  component of that path name.  E.g., `rmdir -p a/b/c' is\n\
                  similar to `rmdir a/b/c a/b a'.\n\
  -v, --verbose   output a diagnostic for every directory processed\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  int errors = 0;
  int optc;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  empty_paths = 0;

  while ((optc = getopt_long (argc, argv, "pv", longopts, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:         /* Long option. */
      break;
    case 'p':
      empty_paths = 1;
      break;
    case IGNORE_FAIL_ON_NON_EMPTY_OPTION:
      ignore_fail_on_non_empty = 1;
      break;
    case 'v':
      verbose = 1;
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (EXIT_FAILURE);
    }
    }

  if (optind == argc)
    {
      error (0, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }

  for (; optind < argc; ++optind)
    {
      int fail;
      char *dir = argv[optind];

      /* Give a diagnostic for each attempted removal if --verbose.  */
      if (verbose)
    error (0, 0, _("removing directory, %s"), dir);

      fail = rmdir (dir);

      if (fail)
    {
      if (ignore_fail_on_non_empty
          && errno_rmdir_non_empty (errno))
        continue;

      error (0, errno, "%s", quote (dir));
      errors = 1;
    }
      else if (empty_paths)
    {
      errors += remove_parents (dir);
    }
    }

  exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/seq.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/seq.c used in: Step 919
/* seq - print sequence of numbers to standard output.
   Copyright (C) 1994-2003 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.  */

/* Written by Ulrich Drepper.  */

#include <config.h>
#include <getopt.h>
#include <math.h>
#include <stdio.h>
#include <sys/types.h>

#include "system.h"
#include "closeout.h"
#include "error.h"
#include "xstrtol.h"
#include "xstrtod.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "seq"

#define AUTHORS "Ulrich Drepper"

/* If nonzero print all number with equal width.  */
static int equal_width;

/* The name that this program was run with.  */
char *program_name;

/* The string used to separate two numbers.  */
static char *separator;

/* The string output after all numbers have been output.
   Usually "\n" or "\0".  */
/* FIXME: make this an option.  */
static char *terminator = "\n";

/* The representation of the decimal point in the current locale.
   Always "." if the localeconv function is not supported.  */
static char *decimal_point = ".";

/* The starting number.  */
static double first;

/* The increment.  */
static double step;

/* The last number.  */
static double last;

static struct option const long_options[] =
{
  { "equal-width", no_argument, NULL, 'w'},
  { "format", required_argument, NULL, 'f'},
  { "separator", required_argument, NULL, 's'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  { NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... LAST\n\
  or:  %s [OPTION]... FIRST LAST\n\
  or:  %s [OPTION]... FIRST INCREMENT LAST\n\
"), program_name, program_name, program_name);
      fputs (_("\
Print numbers from FIRST to LAST, in steps of INCREMENT.\n\
\n\
  -f, --format=FORMAT      use printf style floating-point FORMAT (default: %g)\n\
  -s, --separator=STRING   use STRING to separate numbers (default: \\n)\n\
  -w, --equal-width        equalize width by padding with leading zeroes\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
If FIRST or INCREMENT is omitted, it defaults to 1.\n\
FIRST, INCREMENT, and LAST are interpreted as floating point values.\n\
INCREMENT should be positive if FIRST is smaller than LAST, and negative\n\
otherwise.  When given, the FORMAT argument must contain exactly one of\n\
the printf-style, floating point output formats %e, %f, %g\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

/* Read a double value from the command line.
   Return if the string is correct else signal error.  */

static double
scan_double_arg (const char *arg)
{
  double ret_val;

  if (xstrtod (arg, NULL, &ret_val))
    {
      error (0, 0, _("invalid floating point argument: %s"), arg);
      usage (EXIT_FAILURE);
    }

  return ret_val;
}

/* Check whether the format string is valid for a single `double'
   argument.  Return 0 if not, 1 if correct. */

static int
valid_format (const char *fmt)
{
  while (*fmt != '\0')
    {
      if (*fmt == '%')
    {
      fmt++;
      if (*fmt != '%')
        break;
    }

      fmt++;
    }
  if (*fmt == '\0')
    return 0;

  fmt += strspn (fmt, "-+#0 '");
  if (ISDIGIT (*fmt) || *fmt == '.')
    {
      fmt += strspn (fmt, "0123456789");

      if (*fmt == '.')
    {
      ++fmt;
      fmt += strspn (fmt, "0123456789");
    }
    }

  if (!(*fmt == 'e' || *fmt == 'f' || *fmt == 'g'))
    return 0;

  fmt++;
  while (*fmt != '\0')
    {
      if (*fmt == '%')
    {
      fmt++;
      if (*fmt != '%')
        return 0;
    }

      fmt++;
    }

  return 1;
}

/* Actually print the sequence of numbers in the specified range, with the
   given or default stepping and format.  */
static int
print_numbers (const char *fmt)
{
  if (first > last)
    {
      int i;

      if (step >= 0)
    {
      error (0, 0,
         _("when the starting value is larger than the limit,\n\
the increment must be negative"));
      usage (EXIT_FAILURE);
    }

      printf (fmt, first);
      for (i = 1; /* empty */; i++)
    {
      double x = first + i * step;

      if (x < last)
        break;

      fputs (separator, stdout);
      printf (fmt, x);
    }
    }
  else
    {
      int i;

      if (step <= 0)
    {
      error (0, 0,
         _("when the starting value is smaller than the limit,\n\
the increment must be positive"));
      usage (EXIT_FAILURE);
    }

      printf (fmt, first);
      for (i = 1; /* empty */; i++)
    {
      double x = first + i * step;

      if (x > last)
        break;

      fputs (separator, stdout);
      printf (fmt, x);
    }
    }
  fputs (terminator, stdout);

  return 0;
}

#if HAVE_RINT && HAVE_MODF && HAVE_FLOOR

/* Return a printf-style format string with which all selected numbers
   will format to strings of the same width.  */

static char *
get_width_format ()
{
  static char buffer[256];
  int full_width;
  int frac_width;
  int width1, width2;
  double max_val;
  double min_val;
  double temp;

  if (first > last)
    {
      min_val = first - step * floor ((first - last) / step);
      max_val = first;
    }
  else
    {
      min_val = first;
      max_val = first + step * floor ((last - first) / step);
    }

  sprintf (buffer, "%g", rint (max_val));
  if (buffer[strspn (buffer, "0123456789")] != '\0')
    return "%g";
  width1 = strlen (buffer);

  if (min_val < 0.0)
    {
      double int_min_val = rint (min_val);
      sprintf (buffer, "%g", int_min_val);
      if (buffer[strspn (buffer, "-0123456789")] != '\0')
    return "%g";
      /* On some systems, `seq -w -.1 .1 .1' results in buffer being `-0'.
     On others, it is just `0'.  The former results in better output.  */
      width2 = (int_min_val == 0 ? 2 : strlen (buffer));

      width1 = width1 > width2 ? width1 : width2;
    }
  full_width = width1;

  sprintf (buffer, "%g", 1.0 + modf (fabs (min_val), &temp));
  width1 = strlen (buffer);
  if (width1 == 1)
    width1 = 0;
  else
    {
      if (buffer[0] != '1'
      /* FIXME: assumes that decimal_point is a single character
         string.  */
      || buffer[1] != decimal_point[0]
      || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
    return "%g";
      width1 -= 2;
    }

  sprintf (buffer, "%g", 1.0 + modf (fabs (step), &temp));
  width2 = strlen (buffer);
  if (width2 == 1)
    width2 = 0;
  else
    {
      if (buffer[0] != '1'
      /* FIXME: assumes that decimal_point is a single character
         string.  */
      || buffer[1] != decimal_point[0]
      || buffer[2 + strspn (&buffer[2], "0123456789")] != '\0')
    return "%g";
      width2 -= 2;
    }
  frac_width = width1 > width2 ? width1 : width2;

  if (frac_width)
    sprintf (buffer, "%%0%d.%df", full_width + 1 + frac_width, frac_width);
  else
    sprintf (buffer, "%%0%dg", full_width);

  return buffer;
}

#else   /* one of the math functions rint, modf, floor is missing.  */

static char *
get_width_format (void)
{
  /* We cannot compute the needed information to determine the correct
     answer.  So we simply return a value that works for all cases.  */
  return "%g";
}

#endif

int
main (int argc, char **argv)
{
  int errs;
  int optc;
  int step_is_set;

  /* The printf(3) format used for output.  */
  char *format_str = NULL;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  equal_width = 0;
  separator = "\n";
  first = 1.0;
  step_is_set = 0;

  /* Figure out the locale's idea of a decimal point.  */
#if HAVE_LOCALECONV
  {
    struct lconv *locale;

    locale = localeconv ();
    /* Paranoia.  */
    if (locale && locale->decimal_point && locale->decimal_point[0] != '\0')
      decimal_point = locale->decimal_point;
  }
#endif

  /* We have to handle negative numbers in the command line but this
     conflicts with the command line arguments.  So explicitly check first
     whether the next argument looks like a negative number.  */
  while (optind < argc)
    {
      if (argv[optind][0] == '-'
      && ((optc = argv[optind][1]) == decimal_point[0]
          || ISDIGIT (optc)))
    {
      /* means negative number */
      break;
    }

      optc = getopt_long (argc, argv, "+f:s:w", long_options, NULL);
      if (optc == -1)
    break;

      switch (optc)
    {
    case 0:
      break;

    case 'f':
      format_str = optarg;
      break;

    case 's':
      separator = optarg;
      break;

    case 'w':
      equal_width = 1;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (argc - optind < 1)
    {
      error (0, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }

  if (3 < argc - optind)
    {
      error (0, 0, _("too many arguments"));
      usage (EXIT_FAILURE);
    }

  if (format_str && !valid_format (format_str))
    {
      error (0, 0, _("invalid format string: `%s'"), format_str);
      usage (EXIT_FAILURE);
    }

  last = scan_double_arg (argv[optind++]);

  if (optind < argc)
    {
      first = last;
      last = scan_double_arg (argv[optind++]);

      if (optind < argc)
    {
      step = last;
      step_is_set = 1;
      last = scan_double_arg (argv[optind++]);

    }
    }

  if (format_str != NULL && equal_width)
    {
      error (0, 0, _("\
format string may not be specified when printing equal width strings"));
      usage (EXIT_FAILURE);
    }

  if (!step_is_set)
    {
      step = first <= last ? 1.0 : -1.0;
    }

  if (format_str == NULL)
    {
      if (equal_width)
    format_str = get_width_format ();
      else
    format_str = "%g";
    }

  errs = print_numbers (format_str);

  exit (errs);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xstrtod.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xstrtod.h used in: Step 919, Step 921, Step 929
#ifndef XSTRTOD_H
# define XSTRTOD_H 1

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

int
  xstrtod PARAMS ((const char *str, const char **ptr, double *result));

#endif /* not XSTRTOD_H */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/sleep.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/sleep.c used in: Step 921
/* sleep - delay for a specified amount of time.
   Copyright (C) 84, 1991-1997, 1999-2003 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 <config.h>
#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
#include <getopt.h>

#include "system.h"
#include "closeout.h"
#include "error.h"
#include "long-options.h"
#include "xnanosleep.h"
#include "xstrtod.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "sleep"

#define AUTHORS N_ ("Jim Meyering and Paul Eggert")

/* The name by which this program was run. */
char *program_name;

static struct option const long_options[] =
{
  {0, 0, 0, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s NUMBER[SUFFIX]...\n\
  or:  %s OPTION\n\
Pause for NUMBER seconds.  SUFFIX may be `s' for seconds (the default),\n\
`m' for minutes, `h' for hours or `d' for days.  Unlike most implementations\n\
that require NUMBER be an integer, here NUMBER may be an arbitrary floating\n\
point number.\n\
\n\
"),
          program_name, program_name);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

/* Given a floating point value *X, and a suffix character, SUFFIX_CHAR,
   scale *X by the multiplier implied by SUFFIX_CHAR.  SUFFIX_CHAR may
   be the NUL byte or `s' to denote seconds, `m' for minutes, `h' for
   hours, or `d' for days.  If SUFFIX_CHAR is invalid, don't modify *X
   and return nonzero.  Otherwise return zero.  */

static int
apply_suffix (double *x, char suffix_char)
{
  unsigned int multiplier;

  switch (suffix_char)
    {
    case 0:
    case 's':
      multiplier = 1;
      break;
    case 'm':
      multiplier = 60;
      break;
    case 'h':
      multiplier = 60 * 60;
      break;
    case 'd':
      multiplier = 60 * 60 * 24;
      break;
    default:
      multiplier = 0;
    }

  if (multiplier == 0)
    return 1;

  *x *= multiplier;

  return 0;
}

int
main (int argc, char **argv)
{
  int i;
  double seconds = 0.0;
  int c;
  int fail = 0;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              AUTHORS, usage);

  while ((c = getopt_long (argc, argv, "", long_options, NULL)) != -1)
    {
      switch (c)
    {
    case 0:
      break;

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (argc == 1)
    {
      error (0, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }

  for (i = optind; i < argc; i++)
    {
      double s;
      const char *p;
      if (xstrtod (argv[i], &p, &s)
      /* Nonnegative interval.  */
      || ! (0 <= s)
      /* No extra chars after the number and an optional s,m,h,d char.  */
      || (*p && *(p+1))
      /* Check any suffix char and update S based on the suffix.  */
      || apply_suffix (&s, *p))
    {
      error (0, 0, _("invalid time interval `%s'"), argv[i]);
      fail = 1;
    }

      seconds += s;
    }

  if (fail)
    usage (EXIT_FAILURE);

  if (xnanosleep (seconds))
    error (EXIT_FAILURE, errno, _("cannot read realtime clock"));

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/xnanosleep.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/xnanosleep.h used in: Step 921, Step 929
int xnanosleep (double);

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/physmem.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/physmem.h used in: Step 923
#ifndef PHYSMEM_H_
# define PHYSMEM_H_ 1

# if HAVE_CONFIG_H
#  include <config.h>
# endif

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

double physmem_total PARAMS ((void));
double physmem_available PARAMS ((void));

#endif /* PHYSMEM_H_ */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/stdio-safer.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/stdio-safer.h used in: Step 923, Step 939
#ifndef PARAMS
# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#  define PARAMS(Args) Args
# else
#  define PARAMS(Args) ()
# endif
#endif

FILE *fopen_safer PARAMS ((char const *, char const *));

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/split.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/split.c used in: Step 925
/* split.c -- split a file into pieces.
   Copyright (C) 88, 91, 1995-2003 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.  */


/* By tege@sics.se, with rms.

   To do:
   * Implement -t CHAR or -t REGEX to specify break characters other
     than newline. */

#include <config.h>

#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>

#include "system.h"
#include "closeout.h"
#include "dirname.h"
#include "error.h"
#include "full-read.h"
#include "full-write.h"
#include "posixver.h"
#include "safe-read.h"
#include "xstrtol.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "split"

#define AUTHORS N_ ("Torbjorn Granlund and Richard M. Stallman")

#define DEFAULT_SUFFIX_LENGTH 2

/* The name this program was run with. */
char *program_name;

/* Base name of output files.  */
static char const *outbase;

/* Name of output files.  */
static char *outfile;

/* Pointer to the end of the prefix in OUTFILE.
   Suffixes are inserted here.  */
static char *outfile_mid;

/* Length of OUTFILE's suffix.  */
static size_t suffix_length = DEFAULT_SUFFIX_LENGTH;

/* Name of input file.  May be "-".  */
static char *infile;

/* Descriptor on which input file is open.  */
static int input_desc;

/* Descriptor on which output file is open.  */
static int output_desc;

/* If nonzero, print a diagnostic on standard error just before each
   output file is opened. */
static int verbose;

static struct option const longopts[] =
{
  {"bytes", required_argument, NULL, 'b'},
  {"lines", required_argument, NULL, 'l'},
  {"line-bytes", required_argument, NULL, 'C'},
  {"suffix-length", required_argument, NULL, 'a'},
  {"verbose", no_argument, &verbose, 0},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION] [INPUT [PREFIX]]\n\
"),
          program_name);
    fputs (_("\
Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default\n\
PREFIX is `x'.  With no INPUT, or when INPUT is -, read standard input.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fprintf (stdout, _("\
  -a, --suffix-length=N   use suffixes of length N (default %d)\n\
  -b, --bytes=SIZE        put SIZE bytes per output file\n\
  -C, --line-bytes=SIZE   put at most SIZE bytes of lines per output file\n\
  -l, --lines=NUMBER      put NUMBER lines per output file\n\
"), DEFAULT_SUFFIX_LENGTH);
      fputs (_("\
      --verbose           print a diagnostic to standard error just\n\
                            before each output file is opened\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
SIZE may have a multiplier suffix: b for 512, k for 1K, m for 1 Meg.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Compute the next sequential output file name and store it into the
   string `outfile'.  */

static void
next_file_name (void)
{
  if (! outfile)
    {
      /* Allocate and initialize the first file name.  */

      size_t outbase_length = strlen (outbase);
      size_t outfile_length = outbase_length + suffix_length;
      if (outfile_length + 1 < outbase_length)
    xalloc_die ();
      outfile = xmalloc (outfile_length + 1);
      outfile_mid = outfile + outbase_length;
      memcpy (outfile, outbase, outbase_length);
      memset (outfile_mid, 'a', suffix_length);
      outfile[outfile_length] = 0;

#if ! _POSIX_NO_TRUNC && HAVE_PATHCONF && defined _PC_NAME_MAX
      /* POSIX requires that if the output file name is too long for
     its directory, `split' must fail without creating any files.
     This must be checked for explicitly on operating systems that
     silently truncate file names.  */
      {
    char *dir = dir_name (outfile);
    long name_max = pathconf (dir, _PC_NAME_MAX);
    if (0 <= name_max && name_max < base_len (base_name (outfile)))
      error (EXIT_FAILURE, ENAMETOOLONG, "%s", outfile);
    free (dir);
      }
#endif
    }
  else
    {
      /* Increment the suffix in place, if possible.  */

      char *p;
      for (p = outfile_mid + suffix_length; outfile_mid < p; *--p = 'a')
    if (p[-1]++ != 'z')
      return;
      error (EXIT_FAILURE, 0, _("Output file suffixes exhausted"));
    }
}

/* Write BYTES bytes at BP to an output file.
   If NEW_FILE_FLAG is nonzero, open the next output file.
   Otherwise add to the same output file already in use.  */

static void
cwrite (int new_file_flag, const char *bp, size_t bytes)
{
  if (new_file_flag)
    {
      if (output_desc >= 0 && close (output_desc) < 0)
    error (EXIT_FAILURE, errno, "%s", outfile);

      next_file_name ();
      if (verbose)
    fprintf (stderr, _("creating file `%s'\n"), outfile);
      output_desc = open (outfile,
              O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666);
      if (output_desc < 0)
    error (EXIT_FAILURE, errno, "%s", outfile);
    }
  if (full_write (output_desc, bp, bytes) != bytes)
    error (EXIT_FAILURE, errno, "%s", outfile);
}

/* Split into pieces of exactly NCHARS bytes.
   Use buffer BUF, whose size is BUFSIZE.  */

static void
bytes_split (size_t nchars, char *buf, size_t bufsize)
{
  size_t n_read;
  int new_file_flag = 1;
  size_t to_read;
  size_t to_write = nchars;
  char *bp_out;

  do
    {
      n_read = full_read (input_desc, buf, bufsize);
      if (n_read == SAFE_READ_ERROR)
        error (EXIT_FAILURE, errno, "%s", infile);
      bp_out = buf;
      to_read = n_read;
      for (;;)
    {
      if (to_read < to_write)
        {
          if (to_read)  /* do not write 0 bytes! */
        {
          cwrite (new_file_flag, bp_out, to_read);
          to_write -= to_read;
          new_file_flag = 0;
        }
          break;
        }

      cwrite (new_file_flag, bp_out, to_write);
      bp_out += to_write;
      to_read -= to_write;
      new_file_flag = 1;
      to_write = nchars;
    }
    }
  while (n_read == bufsize);
}

/* Split into pieces of exactly NLINES lines.
   Use buffer BUF, whose size is BUFSIZE.  */

static void
lines_split (size_t nlines, char *buf, size_t bufsize)
{
  size_t n_read;
  char *bp, *bp_out, *eob;
  int new_file_flag = 1;
  size_t n = 0;

  do
    {
      n_read = full_read (input_desc, buf, bufsize);
      if (n_read == SAFE_READ_ERROR)
    error (EXIT_FAILURE, errno, "%s", infile);
      bp = bp_out = buf;
      eob = bp + n_read;
      *eob = '\n';
      for (;;)
    {
      bp = memchr (bp, '\n', eob - bp + 1);
      if (bp == eob)
        {
          if (eob != bp_out) /* do not write 0 bytes! */
        {
          size_t len = eob - bp_out;
          cwrite (new_file_flag, bp_out, len);
          new_file_flag = 0;
        }
          break;
        }

      ++bp;
      if (++n >= nlines)
        {
          cwrite (new_file_flag, bp_out, bp - bp_out);
          bp_out = bp;
          new_file_flag = 1;
          n = 0;
        }
    }
    }
  while (n_read == bufsize);
}


/* Split into pieces that are as large as possible while still not more
   than NCHARS bytes, and are split on line boundaries except
   where lines longer than NCHARS bytes occur. */

static void
line_bytes_split (size_t nchars)
{
  size_t n_read;
  char *bp;
  int eof = 0;
  size_t n_buffered = 0;
  char *buf = (char *) xmalloc (nchars);

  do
    {
      /* Fill up the full buffer size from the input file.  */

      n_read = full_read (input_desc, buf + n_buffered, nchars - n_buffered);
      if (n_read == SAFE_READ_ERROR)
    error (EXIT_FAILURE, errno, "%s", infile);

      n_buffered += n_read;
      if (n_buffered != nchars)
    eof = 1;

      /* Find where to end this chunk.  */
      bp = buf + n_buffered;
      if (n_buffered == nchars)
    {
      while (bp > buf && bp[-1] != '\n')
        bp--;
    }

      /* If chunk has no newlines, use all the chunk.  */
      if (bp == buf)
    bp = buf + n_buffered;

      /* Output the chars as one output file.  */
      cwrite (1, buf, bp - buf);

      /* Discard the chars we just output; move rest of chunk
     down to be the start of the next chunk.  Source and
     destination probably overlap.  */
      n_buffered -= bp - buf;
      if (n_buffered > 0)
    memmove (buf, bp, n_buffered);
    }
  while (!eof);
  free (buf);
}

#define FAIL_ONLY_ONE_WAY()                 \
  do                                \
    {                               \
      error (0, 0, _("cannot split in more than one way")); \
      usage (EXIT_FAILURE);                 \
    }                               \
  while (0)

int
main (int argc, char **argv)
{
  struct stat stat_buf;
  size_t num;           /* numeric argument from command line */
  enum
    {
      type_undef, type_bytes, type_byteslines, type_lines, type_digits
    } split_type = type_undef;
  size_t in_blk_size;       /* optimal block size of input file device */
  char *buf;            /* file i/o buffer */
  size_t accum = 0;
  int c;
  int digits_optind = 0;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  /* Parse command line options.  */

  infile = "-";
  outbase = "x";

  while (1)
    {
      /* This is the argv-index of the option we will read next.  */
      int this_optind = optind ? optind : 1;
      long int tmp_long;

      c = getopt_long (argc, argv, "0123456789C:a:b:l:", longopts, NULL);
      if (c == -1)
    break;

      switch (c)
    {
    case 0:
      break;

    case 'a':
      {
        unsigned long tmp;
        if (xstrtoul (optarg, NULL, 10, &tmp, "") != LONGINT_OK
        || SIZE_MAX < tmp)
          {
        error (0, 0, _("%s: invalid suffix length"), optarg);
        usage (EXIT_FAILURE);
          }
        suffix_length = tmp;
      }
      break;

    case 'b':
      if (split_type != type_undef)
        FAIL_ONLY_ONE_WAY ();
      split_type = type_bytes;
      if (xstrtol (optarg, NULL, 10, &tmp_long, "bkm") != LONGINT_OK
          || tmp_long < 0 || tmp_long > INT_MAX)
        {
          error (0, 0, _("%s: invalid number of bytes"), optarg);
          usage (EXIT_FAILURE);
        }
      accum = /* FIXME: */ (int) tmp_long;
      break;

    case 'l':
      if (split_type != type_undef)
        FAIL_ONLY_ONE_WAY ();
      split_type = type_lines;
      if (xstrtol (optarg, NULL, 10, &tmp_long, "") != LONGINT_OK
          || tmp_long < 0 || tmp_long > INT_MAX)
        {
          error (0, 0, _("%s: invalid number of lines"), optarg);
          usage (EXIT_FAILURE);
        }
      accum = /* FIXME */ (int) tmp_long;
      break;

    case 'C':
      if (split_type != type_undef)
        FAIL_ONLY_ONE_WAY ();
      split_type = type_byteslines;
      if (xstrtol (optarg, NULL, 10, &tmp_long, "bkm") != LONGINT_OK
          || tmp_long < 0 ||  tmp_long > INT_MAX)
        {
          error (0, 0, _("%s: invalid number of bytes"), optarg);
          usage (EXIT_FAILURE);
        }
      accum = /* FIXME */ (int) tmp_long;
      break;

    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (split_type != type_undef && split_type != type_digits)
        FAIL_ONLY_ONE_WAY ();
      if (digits_optind != 0 && digits_optind != this_optind)
        accum = 0;      /* More than one number given; ignore other. */
      digits_optind = this_optind;
      split_type = type_digits;
      accum = accum * 10 + c - '0';
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (digits_optind && 200112 <= posix2_version ())
    {
      error (0, 0, _("`-%d' option is obsolete; use `-l %d'"), accum, accum);
      usage (EXIT_FAILURE);
    }

  /* Handle default case.  */
  if (split_type == type_undef)
    {
      split_type = type_lines;
      accum = 1000;
    }

  if (accum < 1)
    {
      error (0, 0, _("invalid number"));
      usage (EXIT_FAILURE);
    }
  num = accum;

  /* Get out the filename arguments.  */

  if (optind < argc)
    infile = argv[optind++];

  if (optind < argc)
    outbase = argv[optind++];

  if (optind < argc)
    {
      error (0, 0, _("too many arguments"));
      usage (EXIT_FAILURE);
    }

  /* Open the input file.  */
  if (STREQ (infile, "-"))
    input_desc = 0;
  else
    {
      input_desc = open (infile, O_RDONLY);
      if (input_desc < 0)
    error (EXIT_FAILURE, errno, "%s", infile);
    }
  /* Binary I/O is safer when bytecounts are used.  */
  SET_BINARY (input_desc);

  /* No output file is open now.  */
  output_desc = -1;

  /* Get the optimal block size of input device and make a buffer.  */

  if (fstat (input_desc, &stat_buf) < 0)
    error (EXIT_FAILURE, errno, "%s", infile);
  in_blk_size = ST_BLKSIZE (stat_buf);

  buf = xmalloc (in_blk_size + 1);

  switch (split_type)
    {
    case type_digits:
    case type_lines:
      lines_split (num, buf, in_blk_size);
      break;

    case type_bytes:
      bytes_split (num, buf, in_blk_size);
      break;

    case type_byteslines:
      line_bytes_split (num);
      break;

    default:
      abort ();
    }

  if (close (input_desc) < 0)
    error (EXIT_FAILURE, errno, "%s", infile);
  if (output_desc >= 0 && close (output_desc) < 0)
    error (EXIT_FAILURE, errno, "%s", outfile);

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/full-read.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/full-read.h used in: Step 925
/* An interface to read() that reads all it is asked to read.

   Copyright (C) 2002 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, read to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include <stddef.h>

/* Read COUNT bytes at BUF to descriptor FD, retrying if interrupted
   or if partial reads occur.  Return the number of bytes successfully
   read, setting errno if that is less than COUNT.  errno = 0 means EOF.  */
extern size_t full_read (int fd, void *buf, size_t count);

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/sum.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/sum.c used in: Step 927
/* sum -- checksum and count the blocks in a file
   Copyright (C) 86, 89, 91, 1995-2002 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.  */

/* Like BSD sum or SysV sum -r, except like SysV sum if -s option is given. */

/* Written by Kayvan Aghaiepour and David MacKenzie. */

#include <config.h>

#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>
#include "system.h"
#include "closeout.h"
#include "error.h"
#include "human.h"
#include "safe-read.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "sum"

#define AUTHORS N_ ("Kayvan Aghaiepour and David MacKenzie")

/* The name this program was run with. */
char *program_name;

/* Nonzero if any of the files read were the standard input. */
static int have_read_stdin;

static struct option const longopts[] =
{
  {"sysv", no_argument, NULL, 's'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
          program_name);
      fputs (_("\
Print checksum and block counts for each FILE.\n\
\n\
  -r              defeat -s, use BSD sum algorithm, use 1K blocks\n\
  -s, --sysv      use System V sum algorithm, use 512 bytes blocks\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
With no FILE, or when FILE is -, read standard input.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Calculate and print the rotated checksum and the size in 1K blocks
   of file FILE, or of the standard input if FILE is "-".
   If PRINT_NAME is >1, print FILE next to the checksum and size.
   The checksum varies depending on sizeof(int).
   Return 0 if successful, -1 if an error occurs. */

static int
bsd_sum_file (const char *file, int print_name)
{
  register FILE *fp;
  register int checksum = 0;    /* The checksum mod 2^16. */
  register uintmax_t total_bytes = 0;   /* The number of bytes. */
  register int ch;      /* Each character read. */
  char hbuf[LONGEST_HUMAN_READABLE + 1];

  if (STREQ (file, "-"))
    {
      fp = stdin;
      have_read_stdin = 1;
    }
  else
    {
      fp = fopen (file, "r");
      if (fp == NULL)
    {
      error (0, errno, "%s", file);
      return -1;
    }
    }
  /* Need binary I/O, or else byte counts and checksums are incorrect.  */
  SET_BINARY (fileno(fp));

  while ((ch = getc (fp)) != EOF)
    {
      total_bytes++;
      checksum = (checksum >> 1) + ((checksum & 1) << 15);
      checksum += ch;
      checksum &= 0xffff;   /* Keep it within bounds. */
    }

  if (ferror (fp))
    {
      error (0, errno, "%s", file);
      if (!STREQ (file, "-"))
    fclose (fp);
      return -1;
    }

  if (!STREQ (file, "-") && fclose (fp) == EOF)
    {
      error (0, errno, "%s", file);
      return -1;
    }

  printf ("%05d %5s", checksum,
      human_readable (total_bytes, hbuf, human_ceiling, 1, 1024));
  if (print_name > 1)
    printf (" %s", file);
  putchar ('\n');

  return 0;
}

/* Calculate and print the checksum and the size in 512-byte blocks
   of file FILE, or of the standard input if FILE is "-".
   If PRINT_NAME is >0, print FILE next to the checksum and size.
   Return 0 if successful, -1 if an error occurs. */

static int
sysv_sum_file (const char *file, int print_name)
{
  int fd;
  unsigned char buf[8192];
  uintmax_t total_bytes = 0;
  char hbuf[LONGEST_HUMAN_READABLE + 1];
  int r;
  int checksum;

  /* The sum of all the input bytes, modulo (UINT_MAX + 1).  */
  unsigned int s = 0;

  if (STREQ (file, "-"))
    {
      fd = 0;
      have_read_stdin = 1;
    }
  else
    {
      fd = open (file, O_RDONLY);
      if (fd == -1)
    {
      error (0, errno, "%s", file);
      return -1;
    }
    }
  /* Need binary I/O, or else byte counts and checksums are incorrect.  */
  SET_BINARY (fd);

  while (1)
    {
      size_t i;
      size_t bytes_read = safe_read (fd, buf, sizeof buf);

      if (bytes_read == 0)
    break;

      if (bytes_read == SAFE_READ_ERROR)
    {
      error (0, errno, "%s", file);
      if (!STREQ (file, "-"))
        close (fd);
      return -1;
    }

      for (i = 0; i < bytes_read; i++)
    s += buf[i];
      total_bytes += bytes_read;
    }

  if (!STREQ (file, "-") && close (fd) == -1)
    {
      error (0, errno, "%s", file);
      return -1;
    }

  r = (s & 0xffff) + ((s & 0xffffffff) >> 16);
  checksum = (r & 0xffff) + (r >> 16);

  printf ("%d %s", checksum,
      human_readable (total_bytes, hbuf, human_ceiling, 1, 512));
  if (print_name)
    printf (" %s", file);
  putchar ('\n');

  return 0;
}

int
main (int argc, char **argv)
{
  int errors = 0;
  int optc;
  int files_given;
  int (*sum_func) (const char *, int) = bsd_sum_file;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  have_read_stdin = 0;

  while ((optc = getopt_long (argc, argv, "rs", longopts, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:
      break;

    case 'r':       /* For SysV compatibility. */
      sum_func = bsd_sum_file;
      break;

    case 's':
      sum_func = sysv_sum_file;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
    }

  files_given = argc - optind;
  if (files_given == 0)
    {
      if ((*sum_func) ("-", files_given) < 0)
    errors = 1;
    }
  else
    for (; optind < argc; optind++)
      if ((*sum_func) (argv[optind], files_given) < 0)
    errors = 1;

  if (have_read_stdin && fclose (stdin) == EOF)
    error (EXIT_FAILURE, errno, "-");
  exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/human.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/human.h used in: Step 927
#ifndef HUMAN_H_
# define HUMAN_H_ 1

/* Before including this file, you need something like the following:

    #if HAVE_CONFIG_H
    # include <config.h>
    #endif

    #if HAVE_STDBOOL_H
    # include <stdbool.h>
    #else
    typedef enum {false = 0, true = 1} bool;
    #endif

    #if HAVE_INTTYPES_H
    # include <inttypes.h>
    #else
    # if HAVE_STDINT_H
    #  include <stdint.h>
    # endif
    #endif

    #include <limits.h>

   so that the proper identifiers are all declared.  */

/* A conservative bound on the maximum length of a human-readable string.
   The output can be the square of the largest uintmax_t, so double
   its size before converting to a bound.
   302 / 1000 is ceil (log10 (2.0)).  Add 1 for integer division truncation.
   Also, the output can have a thousands separator between every digit,
   so multiply by MB_LEN_MAX + 1 and then subtract MB_LEN_MAX.
   Finally, append 3, the maximum length of a suffix.  */
# define LONGEST_HUMAN_READABLE \
  ((2 * sizeof (uintmax_t) * CHAR_BIT * 302 / 1000 + 1) * (MB_LEN_MAX + 1) \
   - MB_LEN_MAX + 3)

/* Options for human_readable.  */
enum
{
  /* Unless otherwise specified these options may be ORed together.  */

  /* The following three options are mutually exclusive.  */
  /* Round to plus infinity (default).  */
  human_ceiling = 0,
  /* Round to nearest, ties to even.  */
  human_round_to_nearest = 1,
  /* Round to minus infinity.  */
  human_floor = 2,

  /* Group digits together, e.g. `1,000,000'.  This uses the
     locale-defined grouping; the traditional C locale does not group,
     so this has effect only if some other locale is in use.  */
  human_group_digits = 4,

  /* When autoscaling, suppress ".0" at end.  */
  human_suppress_point_zero = 8,

  /* Scale output and use SI-style units, ignoring the output block size.  */
  human_autoscale = 16,

  /* Prefer base 1024 to base 1000.  */
  human_base_1024 = 32,

  /* Append SI prefix, e.g. "k" or "M".  */
  human_SI = 64,

  /* Append "B" (if base 1000) or "iB" (if base 1024) to SI prefix.  */
  human_B = 128
};

char *human_readable (uintmax_t, char *, int, uintmax_t, uintmax_t);

int human_options (char const *, bool, uintmax_t *);

#endif /* HUMAN_H_ */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/tail.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/tail.c used in: Step 929
/* tail -- output the last part of file(s)
   Copyright (C) 1989, 90, 91, 1995-2002 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.  */

/* Can display any amount of data, unlike the Unix version, which uses
   a fixed size buffer and therefore can only deliver a limited number
   of lines.

   Original version by Paul Rubin <phr@ocf.berkeley.edu>.
   Extensions by David MacKenzie <djm@gnu.ai.mit.edu>.
   tail -f for multiple files by Ian Lance Taylor <ian@airs.com>.  */

#include <config.h>

#include <stdio.h>
#include <assert.h>
#include <getopt.h>
#include <sys/types.h>
#include <signal.h>

#include "system.h"
#include "closeout.h"
#include "argmatch.h"
#include "error.h"
#include "inttostr.h"
#include "posixver.h"
#include "safe-read.h"
#include "xnanosleep.h"
#include "xstrtol.h"
#include "xstrtod.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "tail"

#define AUTHORS \
  N_ ("Paul Rubin, David MacKenzie, Ian Lance Taylor, and Jim Meyering")

#ifndef ENOSYS
  /* Some systems don't have ENOSYS -- this should be a big enough
     value that no valid errno value will match it.  */
# define ENOSYS 99999
#endif

/* Number of items to tail.  */
#define DEFAULT_N_LINES 10

/* Size of atomic reads.  */
#ifndef BUFSIZ
# define BUFSIZ (512 * 8)
#endif

/* A special value for dump_remainder's N_BYTES parameter.  */
#define COPY_TO_EOF OFF_T_MAX

/* FIXME: make Follow_name the default?  */
#define DEFAULT_FOLLOW_MODE Follow_descriptor

enum Follow_mode
{
  /* Follow the name of each file: if the file is renamed, try to reopen
     that name and track the end of the new file if/when it's recreated.
     This is useful for tracking logs that are occasionally rotated.  */
  Follow_name = 1,

  /* Follow each descriptor obtained upon opening a file.
     That means we'll continue to follow the end of a file even after
     it has been renamed or unlinked.  */
  Follow_descriptor = 2
};

/* The types of files for which tail works.  */
#define IS_TAILABLE_FILE_TYPE(Mode) \
  (S_ISREG (Mode) || S_ISFIFO (Mode) || S_ISCHR (Mode))

static char const *const follow_mode_string[] =
{
  "descriptor", "name", 0
};

static enum Follow_mode const follow_mode_map[] =
{
  Follow_descriptor, Follow_name,
};

struct File_spec
{
  /* The actual file name, or "-" for stdin.  */
  char *name;

  /* File descriptor on which the file is open; -1 if it's not open.  */
  int fd;

  /* The size of the file the last time we checked.  */
  off_t size;

  /* The device and inode of the file the last time we checked.  */
  dev_t dev;
  ino_t ino;

  /* The specified name initially referred to a directory or some other
     type for which tail isn't meaningful.  Unlike for a permission problem
     (tailable, below) once this is set, the name is not checked ever again.  */
  int ignore;

  /* See description of DEFAULT_MAX_N_... below.  */
  unsigned int n_unchanged_stats;

  /* See description of DEFAULT_MAX_N_... below.  */
  unsigned int n_consecutive_size_changes;

  /* A file is tailable if it exists, is readable, and is of type
     IS_TAILABLE_FILE_TYPE.  */
  int tailable;

  /* The value of errno seen last time we checked this file.  */
  int errnum;

};

/* Keep trying to open a file even if it is inaccessible when tail starts
   or if it becomes inaccessible later -- useful only with -f.  */
static int reopen_inaccessible_files;

/* If nonzero, interpret the numeric argument as the number of lines.
   Otherwise, interpret it as the number of bytes.  */
static int count_lines;

/* Whether we follow the name of each file or the file descriptor
   that is initially associated with each name.  */
static enum Follow_mode follow_mode = Follow_descriptor;

/* If nonzero, read from the ends of all specified files until killed.  */
static int forever;

/* If nonzero, count from start of file instead of end.  */
static int from_start;

/* If nonzero, print filename headers.  */
static int print_headers;

/* When to print the filename banners.  */
enum header_mode
{
  multiple_files, always, never
};

/* When tailing a file by name, if there have been this many consecutive
   iterations for which the size has remained the same, then open/fstat
   the file to determine if that file name is still associated with the
   same device/inode-number pair as before.  This option is meaningful only
   when following by name.  --max-unchanged-stats=N  */
#define DEFAULT_MAX_N_UNCHANGED_STATS_BETWEEN_OPENS 5
static unsigned long max_n_unchanged_stats_between_opens =
  DEFAULT_MAX_N_UNCHANGED_STATS_BETWEEN_OPENS;

/* This variable is used to ensure that a file that is unlinked or moved
   aside, yet always growing will be recognized as having been renamed.
   After detecting this many consecutive size changes for a file, open/fstat
   the file to determine if that file name is still associated with the
   same device/inode-number pair as before.  This option is meaningful only
   when following by name.  --max-consecutive-size-changes=N  */
#define DEFAULT_MAX_N_CONSECUTIVE_SIZE_CHANGES 200
static unsigned long max_n_consecutive_size_changes_between_opens =
  DEFAULT_MAX_N_CONSECUTIVE_SIZE_CHANGES;

/* The name this program was run with.  */
char *program_name;

/* The process ID of the process (presumably on the current host)
   that is writing to all followed files.  */
static pid_t pid;

/* Nonzero if we have ever read standard input.  */
static int have_read_stdin;

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  RETRY_OPTION = CHAR_MAX + 1,
  MAX_UNCHANGED_STATS_OPTION,

  /* FIXME: remove this in 2001, unless someone can show a good
     reason to keep it.  */
  MAX_CONSECUTIVE_SIZE_CHANGES_OPTION,

  PID_OPTION,
  LONG_FOLLOW_OPTION
};

static struct option const long_options[] =
{
  /* --allow-missing is deprecated; use --retry instead
     FIXME: remove it some day */
  {"allow-missing", no_argument, NULL, RETRY_OPTION},
  {"bytes", required_argument, NULL, 'c'},
  {"follow", optional_argument, NULL, LONG_FOLLOW_OPTION},
  {"lines", required_argument, NULL, 'n'},
  {"max-unchanged-stats", required_argument, NULL, MAX_UNCHANGED_STATS_OPTION},
  {"max-consecutive-size-changes", required_argument, NULL,
   MAX_CONSECUTIVE_SIZE_CHANGES_OPTION},
  {"pid", required_argument, NULL, PID_OPTION},
  {"quiet", no_argument, NULL, 'q'},
  {"retry", no_argument, NULL, RETRY_OPTION},
  {"silent", no_argument, NULL, 'q'},
  {"sleep-interval", required_argument, NULL, 's'},
  {"verbose", no_argument, NULL, 'v'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
          program_name);
      printf (_("\
Print the last %d lines of each FILE to standard output.\n\
With more than one FILE, precede each with a header giving the file name.\n\
With no FILE, or when FILE is -, read standard input.\n\
\n\
"), DEFAULT_N_LINES);
     fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
     fputs (_("\
      --retry              keep trying to open a file even if it is\n\
                           inaccessible when tail starts or if it becomes\n\
                           inaccessible later -- useful only with -f\n\
  -c, --bytes=N            output the last N bytes\n\
"), stdout);
     fputs (_("\
  -f, --follow[={name|descriptor}]\n\
                           output appended data as the file grows;\n\
                           -f, --follow, and --follow=descriptor are\n\
                           equivalent\n\
  -F                       same as --follow=name --retry\n\
"), stdout);
     printf (_("\
  -n, --lines=N            output the last N lines, instead of the last %d\n\
      --max-unchanged-stats=N\n\
                           with --follow=name, reopen a FILE which has not\n\
                           changed size after N (default %d) iterations\n\
                           to see if it has been unlinked or renamed\n\
                           (this is the usual case of rotated log files)\n\
"),
         DEFAULT_N_LINES,
         DEFAULT_MAX_N_UNCHANGED_STATS_BETWEEN_OPENS
         );
     fputs (_("\
      --pid=PID            with -f, terminate after process ID, PID dies\n\
  -q, --quiet, --silent    never output headers giving file names\n\
  -s, --sleep-interval=S   with -f, sleep for approximately S seconds\n\
                           (default 1.0) between iterations.\n\
  -v, --verbose            always output headers giving file names\n\
"), stdout);
     fputs (HELP_OPTION_DESCRIPTION, stdout);
     fputs (VERSION_OPTION_DESCRIPTION, stdout);
     fputs (_("\
\n\
If the first character of N (the number of bytes or lines) is a `+',\n\
print beginning with the Nth item from the start of each file, otherwise,\n\
print the last N items in the file.  N may have a multiplier suffix:\n\
b for 512, k for 1024, m for 1048576 (1 Meg).\n\
\n\
"), stdout);
     fputs (_("\
With --follow (-f), tail defaults to following the file descriptor, which\n\
means that even if a tail'ed file is renamed, tail will continue to track\n\
its end.  \
"), stdout);
     fputs (_("\
This default behavior is not desirable when you really want to\n\
track the actual name of the file, not the file descriptor (e.g., log\n\
rotation).  Use --follow=name in that case.  That causes tail to track the\n\
named file by reopening it periodically to see if it has been removed and\n\
recreated by some other program.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

static int
valid_file_spec (struct File_spec const *f)
{
  /* Exactly one of the following subexpressions must be true. */
  return ((f->fd == -1) ^ (f->errnum == 0));
}

static char *
pretty_name (struct File_spec const *f)
{
  return (STREQ (f->name, "-") ? "standard input" : f->name);
}

static void
xwrite (int fd, char const *buffer, size_t n_bytes)
{
  assert (fd == STDOUT_FILENO);
  if (n_bytes > 0 && fwrite (buffer, 1, n_bytes, stdout) == 0)
    error (EXIT_FAILURE, errno, _("write error"));
}

static void
close_fd (int fd, const char *filename)
{
  if (fd != -1 && fd != STDIN_FILENO && close (fd))
    {
      error (0, errno, _("closing %s (fd=%d)"), filename, fd);
    }
}

static void
write_header (const char *pretty_filename)
{
  static int first_file = 1;

  printf ("%s==> %s <==\n", (first_file ? "" : "\n"), pretty_filename);
  first_file = 0;
}

/* Read and output N_BYTES of file PRETTY_FILENAME starting at the current
   position in FD.  If N_BYTES is COPY_TO_EOF, then copy until end of file.
   Return the number of bytes read from the file.  */

static long
dump_remainder (const char *pretty_filename, int fd, off_t n_bytes)
{
  char buffer[BUFSIZ];
  size_t bytes_read;
  long n_written;
  off_t n_remaining = n_bytes;

  n_written = 0;
  while (1)
    {
      long n = MIN (n_remaining, (off_t) BUFSIZ);
      bytes_read = safe_read (fd, buffer, n);
      if (bytes_read == SAFE_READ_ERROR)
    error (EXIT_FAILURE, errno, "%s", pretty_filename);
      if (bytes_read == 0)
    break;
      xwrite (STDOUT_FILENO, buffer, bytes_read);
      n_remaining -= bytes_read;
      n_written += bytes_read;
    }

  return n_written;
}

/* Call lseek with the specified arguments, where file descriptor FD
   corresponds to the file, FILENAME.
   Give a diagnostic and exit nonzero if lseek fails.  */

static void
xlseek (int fd, off_t offset, int whence, char const *filename)
{
  off_t new_offset = lseek (fd, offset, whence);
  char buf[INT_BUFSIZE_BOUND (off_t)];
  char *s;

  if (0 <= new_offset)
    return;

  s = offtostr (offset, buf);
  switch (whence)
    {
    case SEEK_SET:
      error (EXIT_FAILURE, errno, _("%s: cannot seek to offset %s"),
         filename, s);
      break;
    case SEEK_CUR:
      error (EXIT_FAILURE, errno, _("%s: cannot seek to relative offset %s"),
         filename, s);
      break;
    case SEEK_END:
      error (EXIT_FAILURE, errno,
         _("%s: cannot seek to end-relative offset %s"),
         filename, s);
      break;
    default:
      abort ();
    }
}

/* Print the last N_LINES lines from the end of file FD.
   Go backward through the file, reading `BUFSIZ' bytes at a time (except
   probably the first), until we hit the start of the file or have
   read NUMBER newlines.
   START_POS is the starting position of the read pointer for the file
   associated with FD (may be nonzero).
   FILE_LENGTH is the length of the file (one more than the offset of the
   last byte of the file).
   Return 0 if successful, 1 if an error occurred.  */

static int
file_lines (const char *pretty_filename, int fd, long int n_lines,
        off_t start_pos, off_t file_length)
{
  char buffer[BUFSIZ];
  size_t bytes_read;
  off_t pos = file_length;

  if (n_lines == 0)
    return 0;

  /* Set `bytes_read' to the size of the last, probably partial, buffer;
     0 < `bytes_read' <= `BUFSIZ'.  */
  bytes_read = (pos - start_pos) % BUFSIZ;
  if (bytes_read == 0)
    bytes_read = BUFSIZ;
  /* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
     reads will be on block boundaries, which might increase efficiency.  */
  pos -= bytes_read;
  xlseek (fd, pos, SEEK_SET, pretty_filename);
  bytes_read = safe_read (fd, buffer, bytes_read);
  if (bytes_read == SAFE_READ_ERROR)
    {
      error (0, errno, "%s", pretty_filename);
      return 1;
    }

  /* Count the incomplete line on files that don't end with a newline.  */
  if (bytes_read && buffer[bytes_read - 1] != '\n')
    --n_lines;

  do
    {
      /* Scan backward, counting the newlines in this bufferfull.  */

      size_t n = bytes_read;
      while (n)
    {
      char const *nl;
      nl = memrchr (buffer, '\n', n);
      if (nl == NULL)
        break;
      n = nl - buffer;
      if (n_lines-- == 0)
        {
          /* If this newline isn't the last character in the buffer,
             output the part that is after it.  */
          if (n != bytes_read - 1)
        xwrite (STDOUT_FILENO, nl + 1, bytes_read - (n + 1));
          dump_remainder (pretty_filename, fd,
                  file_length - (pos + bytes_read));
          return 0;
        }
    }

      /* Not enough newlines in that bufferfull.  */
      if (pos == start_pos)
    {
      /* Not enough lines in the file; print the entire file.  */
      xlseek (fd, start_pos, SEEK_SET, pretty_filename);
      dump_remainder (pretty_filename, fd, file_length);
      return 0;
    }
      pos -= BUFSIZ;
      xlseek (fd, pos, SEEK_SET, pretty_filename);

      bytes_read = safe_read (fd, buffer, BUFSIZ);
      if (bytes_read == SAFE_READ_ERROR)
    {
      error (0, errno, "%s", pretty_filename);
      return 1;
    }
    }
  while (bytes_read > 0);

  return 0;
}

/* Print the last N_LINES lines from the end of the standard input,
   open for reading as pipe FD.
   Buffer the text as a linked list of LBUFFERs, adding them as needed.
   Return 0 if successful, 1 if an error occured.  */

static int
pipe_lines (const char *pretty_filename, int fd, long int n_lines)
{
  struct linebuffer
  {
    int nbytes, nlines;
    char buffer[BUFSIZ];
    struct linebuffer *next;
  };
  typedef struct linebuffer LBUFFER;
  LBUFFER *first, *last, *tmp;
  int i;            /* Index into buffers.  */
  int total_lines = 0;      /* Total number of newlines in all buffers.  */
  int errors = 0;
  int nbytes;           /* Size of most recent read */

  first = last = (LBUFFER *) xmalloc (sizeof (LBUFFER));
  first->nbytes = first->nlines = 0;
  first->next = NULL;
  tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER));

  /* Input is always read into a fresh buffer.  */
  while ((nbytes = tmp->nbytes = safe_read (fd, tmp->buffer, BUFSIZ)) > 0)
    {
      tmp->nlines = 0;
      tmp->next = NULL;

      /* Count the number of newlines just read.  */
      for (i = 0; i < tmp->nbytes; i++)
    if (tmp->buffer[i] == '\n')
      ++tmp->nlines;
      total_lines += tmp->nlines;

      /* If there is enough room in the last buffer read, just append the new
         one to it.  This is because when reading from a pipe, `nbytes' can
         often be very small.  */
      if (tmp->nbytes + last->nbytes < BUFSIZ)
    {
      memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
      last->nbytes += tmp->nbytes;
      last->nlines += tmp->nlines;
    }
      else
    {
      /* If there's not enough room, link the new buffer onto the end of
         the list, then either free up the oldest buffer for the next
         read if that would leave enough lines, or else malloc a new one.
         Some compaction mechanism is possible but probably not
         worthwhile.  */
      last = last->next = tmp;
      if (total_lines - first->nlines > n_lines)
        {
          tmp = first;
          total_lines -= first->nlines;
          first = first->next;
        }
      else
        tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER));
    }
    }

  free ((char *) tmp);

  if (nbytes < 0)
    {
      error (0, errno, "%s", pretty_filename);
      errors = 1;
      goto free_lbuffers;
    }

  /* If the file is empty, then bail out.  */
  if (last->nbytes == 0)
    goto free_lbuffers;

  /* This prevents a core dump when the pipe contains no newlines.  */
  if (n_lines == 0)
    goto free_lbuffers;

  /* Count the incomplete line on files that don't end with a newline.  */
  if (last->buffer[last->nbytes - 1] != '\n')
    {
      ++last->nlines;
      ++total_lines;
    }

  /* Run through the list, printing lines.  First, skip over unneeded
     buffers.  */
  for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
    total_lines -= tmp->nlines;

  /* Find the correct beginning, then print the rest of the file.  */
  if (total_lines > n_lines)
    {
      const char *cp;

      /* Skip `total_lines' - `n_lines' newlines.  We made sure that
         `total_lines' - `n_lines' <= `tmp->nlines'.  */
      cp = tmp->buffer;
      for (i = total_lines - n_lines; i; --i)
    while (*cp++ != '\n')
      /* Do nothing.  */ ;
      i = cp - tmp->buffer;
    }
  else
    i = 0;
  xwrite (STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);

  for (tmp = tmp->next; tmp; tmp = tmp->next)
    xwrite (STDOUT_FILENO, tmp->buffer, tmp->nbytes);

free_lbuffers:
  while (first)
    {
      tmp = first->next;
      free ((char *) first);
      first = tmp;
    }
  return errors;
}

/* Print the last N_BYTES characters from the end of pipe FD.
   This is a stripped down version of pipe_lines.
   Return 0 if successful, 1 if an error occurred.  */

static int
pipe_bytes (const char *pretty_filename, int fd, off_t n_bytes)
{
  struct charbuffer
  {
    int nbytes;
    char buffer[BUFSIZ];
    struct charbuffer *next;
  };
  typedef struct charbuffer CBUFFER;
  CBUFFER *first, *last, *tmp;
  int i;            /* Index into buffers.  */
  int total_bytes = 0;      /* Total characters in all buffers.  */
  int errors = 0;

  first = last = (CBUFFER *) xmalloc (sizeof (CBUFFER));
  first->nbytes = 0;
  first->next = NULL;
  tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER));

  /* Input is always read into a fresh buffer.  */
  while ((tmp->nbytes = safe_read (fd, tmp->buffer, BUFSIZ)) > 0)
    {
      tmp->next = NULL;

      total_bytes += tmp->nbytes;
      /* If there is enough room in the last buffer read, just append the new
         one to it.  This is because when reading from a pipe, `nbytes' can
         often be very small.  */
      if (tmp->nbytes + last->nbytes < BUFSIZ)
    {
      memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
      last->nbytes += tmp->nbytes;
    }
      else
    {
      /* If there's not enough room, link the new buffer onto the end of
         the list, then either free up the oldest buffer for the next
         read if that would leave enough characters, or else malloc a new
         one.  Some compaction mechanism is possible but probably not
         worthwhile.  */
      last = last->next = tmp;
      if (total_bytes - first->nbytes > n_bytes)
        {
          tmp = first;
          total_bytes -= first->nbytes;
          first = first->next;
        }
      else
        {
          tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER));
        }
    }
    }
  if (tmp->nbytes == -1)
    {
      error (0, errno, "%s", pretty_filename);
      errors = 1;
      free ((char *) tmp);
      goto free_cbuffers;
    }

  free ((char *) tmp);

  /* Run through the list, printing characters.  First, skip over unneeded
     buffers.  */
  for (tmp = first; total_bytes - tmp->nbytes > n_bytes; tmp = tmp->next)
    total_bytes -= tmp->nbytes;

  /* Find the correct beginning, then print the rest of the file.
     We made sure that `total_bytes' - `n_bytes' <= `tmp->nbytes'.  */
  if (total_bytes > n_bytes)
    i = total_bytes - n_bytes;
  else
    i = 0;
  xwrite (STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);

  for (tmp = tmp->next; tmp; tmp = tmp->next)
    xwrite (STDOUT_FILENO, tmp->buffer, tmp->nbytes);

free_cbuffers:
  while (first)
    {
      tmp = first->next;
      free ((char *) first);
      first = tmp;
    }
  return errors;
}

/* Skip N_BYTES characters from the start of pipe FD, and print
   any extra characters that were read beyond that.
   Return 1 on error, 0 if ok, -1 if EOF.  */

static int
start_bytes (const char *pretty_filename, int fd, off_t n_bytes)
{
  char buffer[BUFSIZ];
  size_t bytes_read = 0;

  while (0 < n_bytes)
    {
      bytes_read = safe_read (fd, buffer, BUFSIZ);
      if (bytes_read == 0)
    return -1;
      if (bytes_read == SAFE_READ_ERROR)
    {
      error (0, errno, "%s", pretty_filename);
      return 1;
    }
      n_bytes -= bytes_read;
    }

  if (n_bytes < 0)
    xwrite (STDOUT_FILENO, &buffer[bytes_read + n_bytes], -n_bytes);

  return 0;
}

/* Skip N_LINES lines at the start of file or pipe FD, and print
   any extra characters that were read beyond that.
   Return 1 on error, 0 if ok, -1 if EOF.  */

static int
start_lines (const char *pretty_filename, int fd, long int n_lines)
{
  char buffer[BUFSIZ];
  size_t bytes_read = 0;
  size_t bytes_to_skip = 0;

  if (n_lines == 0)
    return 0;

  while (n_lines)
    {
      bytes_to_skip = 0;
      bytes_read = safe_read (fd, buffer, BUFSIZ);
      if (bytes_read == 0) /* EOF */
    return -1;
      if (bytes_read == SAFE_READ_ERROR) /* error */
    {
      error (0, errno, "%s", pretty_filename);
      return 1;
    }

      while (bytes_to_skip < bytes_read)
    if (buffer[bytes_to_skip++] == '\n' && --n_lines == 0)
      break;
    }

  if (bytes_to_skip < bytes_read)
    {
      xwrite (STDOUT_FILENO, &buffer[bytes_to_skip],
          bytes_read - bytes_to_skip);
    }

  return 0;
}

/* FIXME: describe */

static void
recheck (struct File_spec *f)
{
  /* open/fstat the file and announce if dev/ino have changed */
  struct stat new_stats;
  int fd;
  int fail = 0;
  int is_stdin = (STREQ (f->name, "-"));
  int was_tailable = f->tailable;
  int prev_errnum = f->errnum;
  int new_file;

  assert (valid_file_spec (f));

  fd = (is_stdin ? STDIN_FILENO : open (f->name, O_RDONLY));

  /* If the open fails because the file doesn't exist,
     then mark the file as not tailable.  */
  f->tailable = !(reopen_inaccessible_files && fd == -1);

  if (fd == -1 || fstat (fd, &new_stats) < 0)
    {
      fail = 1;
      f->errnum = errno;
      if (!f->tailable)
    {
      if (was_tailable)
        {
          /* FIXME-maybe: detect the case in which the file first becomes
         unreadable (perms), and later becomes readable again and can
         be seen to be the same file (dev/ino).  Otherwise, tail prints
         the entire contents of the file when it becomes readable.  */
          error (0, f->errnum, _("`%s' has become inaccessible"),
             pretty_name (f));
        }
      else
        {
          /* say nothing... it's still not tailable */
        }
    }
      else if (prev_errnum != errno)
    {
      error (0, errno, "%s", pretty_name (f));
    }
    }
  else if (!IS_TAILABLE_FILE_TYPE (new_stats.st_mode))
    {
      fail = 1;
      f->errnum = -1;
      error (0, 0, _("`%s' has been replaced with an untailable file;\
 giving up on this name"),
         pretty_name (f));
      f->ignore = 1;
    }
  else
    {
      f->errnum = 0;
    }

  new_file = 0;
  if (fail)
    {
      close_fd (fd, pretty_name (f));
      close_fd (f->fd, pretty_name (f));
      f->fd = -1;
    }
  else if (prev_errnum && prev_errnum != ENOENT)
    {
      new_file = 1;
      assert (f->fd == -1);
      error (0, 0, _("`%s' has become accessible"), pretty_name (f));
    }
  else if (f->ino != new_stats.st_ino || f->dev != new_stats.st_dev)
    {
      new_file = 1;
      if (f->fd == -1)
    {
      error (0, 0,
         _("`%s' has appeared;  following end of new file"),
         pretty_name (f));
    }
      else
    {
      /* Close the old one.  */
      close_fd (f->fd, pretty_name (f));

      /* File has been replaced (e.g., via log rotation) --
         tail the new one.  */
      error (0, 0,
         _("`%s' has been replaced;  following end of new file"),
         pretty_name (f));
    }
    }
  else
    {
      if (f->fd == -1)
    {
      /* This happens when one iteration finds the file missing,
         then the preceding <dev,inode> pair is reused as the
         file is recreated.  */
      new_file = 1;
    }
      else
    {
      close_fd (fd, pretty_name (f));
    }
    }

  if (new_file)
    {
      /* Record new file info in f.  */
      f->fd = fd;
      f->size = 0; /* Start at the beginning of the file...  */
      f->dev = new_stats.st_dev;
      f->ino = new_stats.st_ino;
      f->n_unchanged_stats = 0;
      f->n_consecutive_size_changes = 0;
      f->ignore = 0;
      xlseek (f->fd, f->size, SEEK_SET, pretty_name (f));
    }
}

/* FIXME: describe */

static unsigned int
n_live_files (const struct File_spec *f, int n_files)
{
  int i;
  unsigned int n_live = 0;

  for (i = 0; i < n_files; i++)
    {
      if (f[i].fd >= 0)
    ++n_live;
    }
  return n_live;
}

/* Tail NFILES files forever, or until killed.
   The pertinent information for each file is stored in an entry of F.
   Loop over each of them, doing an fstat to see if they have changed size,
   and an occasional open/fstat to see if any dev/ino pair has changed.
   If none of them have changed size in one iteration, sleep for a
   while and try again.  Continue until the user interrupts us.  */

static void
tail_forever (struct File_spec *f, int nfiles, double sleep_interval)
{
  int last;
  int writer_is_dead = 0;

  last = nfiles - 1;

  while (1)
    {
      int i;
      int any_changed;

      any_changed = 0;
      for (i = 0; i < nfiles; i++)
    {
      struct stat stats;

      if (f[i].ignore)
        continue;

      if (f[i].fd < 0)
        {
          recheck (&f[i]);
          continue;
        }

      if (fstat (f[i].fd, &stats) < 0)
        {
          f[i].fd = -1;
          f[i].errnum = errno;
          error (0, errno, "%s", pretty_name (&f[i]));
          continue;
        }

      if (stats.st_size == f[i].size)
        {
          f[i].n_consecutive_size_changes = 0;
          if ((max_n_unchanged_stats_between_opens
           <= f[i].n_unchanged_stats++)
          && follow_mode == Follow_name)
        {
          recheck (&f[i]);
          f[i].n_unchanged_stats = 0;
        }
          continue;
        }

      /* Ensure that a file that's unlinked or moved aside, yet always
         growing will be recognized as having been renamed.  */
      if ((max_n_consecutive_size_changes_between_opens
           <= f[i].n_consecutive_size_changes++)
          && follow_mode == Follow_name)
        {
          f[i].n_consecutive_size_changes = 0;
          recheck (&f[i]);
          continue;
        }

      /* This file has changed size.  Print out what we can, and
         then keep looping.  */

      any_changed = 1;

      /* reset counter */
      f[i].n_unchanged_stats = 0;

      if (stats.st_size < f[i].size)
        {
          error (0, 0, _("%s: file truncated"), pretty_name (&f[i]));
          last = i;
          xlseek (f[i].fd, (off_t) stats.st_size, SEEK_SET,
              pretty_name (&f[i]));
          f[i].size = stats.st_size;
          continue;
        }

      if (i != last)
        {
          if (print_headers)
        write_header (pretty_name (&f[i]));
          last = i;
        }
      f[i].size += dump_remainder (pretty_name (&f[i]), f[i].fd,
                       COPY_TO_EOF);
    }

      if (n_live_files (f, nfiles) == 0 && ! reopen_inaccessible_files)
    {
      error (0, 0, _("no files remaining"));
      break;
    }

      /* If none of the files changed size, sleep.  */
      if (!any_changed)
    {
      if (writer_is_dead)
        break;

      if (xnanosleep (sleep_interval))
        error (EXIT_FAILURE, errno, _("cannot read realtime clock"));

      /* Once the writer is dead, read the files once more to
         avoid a race condition.  */
      writer_is_dead = (pid != 0
                && kill (pid, 0) != 0
                /* Handle the case in which you cannot send a
                   signal to the writer, so kill fails and sets
                   errno to EPERM.  */
                && errno != EPERM);
    }
    }
}

/* Output the last N_BYTES bytes of file FILENAME open for reading in FD.
   Return 0 if successful, 1 if an error occurred.  */

static int
tail_bytes (const char *pretty_filename, int fd, off_t n_bytes)
{
  struct stat stats;

  /* We need binary input, since `tail' relies on `lseek' and byte counts,
     while binary output will preserve the style (Unix/DOS) of text file.  */
  SET_BINARY2 (fd, STDOUT_FILENO);

  if (fstat (fd, &stats))
    {
      error (0, errno, "%s", pretty_filename);
      return 1;
    }

  if (from_start)
    {
      if (S_ISREG (stats.st_mode))
    {
      xlseek (fd, n_bytes, SEEK_CUR, pretty_filename);
    }
      else
    {
      int t;
      if ((t = start_bytes (pretty_filename, fd, n_bytes)) < 0)
        return 0;
      if (t)
        return 1;
    }
      dump_remainder (pretty_filename, fd, COPY_TO_EOF);
    }
  else
    {
      if (S_ISREG (stats.st_mode))
    {
      off_t current_pos, end_pos;
      off_t bytes_remaining;

      if ((current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
          && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
        {
          off_t diff;
          /* Be careful here.  The current position may actually be
         beyond the end of the file.  */
          bytes_remaining = (diff = end_pos - current_pos) < 0 ? 0 : diff;
        }
      else
        {
          error (0, errno, "%s", pretty_filename);
          return 1;
        }

      if (bytes_remaining <= n_bytes)
        {
          /* From the current position to end of file, there are no
         more bytes than have been requested.  So reposition the
         file pointer to the incoming current position and print
         everything after that.  */
          xlseek (fd, current_pos, SEEK_SET, pretty_filename);
        }
      else
        {
          /* There are more bytes remaining than were requested.
         Back up.  */
          xlseek (fd, -n_bytes, SEEK_END, pretty_filename);
        }
      dump_remainder (pretty_filename, fd, n_bytes);
    }
      else
    return pipe_bytes (pretty_filename, fd, n_bytes);
    }
  return 0;
}

/* Output the last N_LINES lines of file FILENAME open for reading in FD.
   Return 0 if successful, 1 if an error occurred.  */

static int
tail_lines (const char *pretty_filename, int fd, long int n_lines)
{
  struct stat stats;

  /* We need binary input, since `tail' relies on `lseek' and byte counts,
     while binary output will preserve the style (Unix/DOS) of text file.  */
  SET_BINARY2 (fd, STDOUT_FILENO);

  if (fstat (fd, &stats))
    {
      error (0, errno, "%s", pretty_filename);
      return 1;
    }

  if (from_start)
    {
      int t;
      if ((t = start_lines (pretty_filename, fd, n_lines)) < 0)
    return 0;
      if (t)
    return 1;
      dump_remainder (pretty_filename, fd, COPY_TO_EOF);
    }
  else
    {
      off_t length;
      off_t start_pos;

      /* Use file_lines only if FD refers to a regular file for
     which lseek (... SEEK_END) works.  */
      if (S_ISREG (stats.st_mode)
      && (start_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
      && start_pos < (length = lseek (fd, (off_t) 0, SEEK_END)))
    {
      if (length != 0 && file_lines (pretty_filename, fd, n_lines,
                     start_pos, length))
        return 1;
    }
      else
    return pipe_lines (pretty_filename, fd, n_lines);
    }
  return 0;
}

/* Display the last N_UNITS units of file FILENAME, open for reading
   in FD.
   Return 0 if successful, 1 if an error occurred.  */

static int
tail (const char *pretty_filename, int fd, off_t n_units)
{
  if (count_lines)
    return tail_lines (pretty_filename, fd, (long) n_units);
  else
    return tail_bytes (pretty_filename, fd, n_units);
}

/* Display the last N_UNITS units of the file described by F.
   Return 0 if successful, 1 if an error occurred.  */

static int
tail_file (struct File_spec *f, off_t n_units)
{
  int fd, errors;

  int is_stdin = (STREQ (f->name, "-"));

  if (is_stdin)
    {
      have_read_stdin = 1;
      fd = STDIN_FILENO;
    }
  else
    {
      fd = open (f->name, O_RDONLY);
    }

  f->tailable = !(reopen_inaccessible_files && fd == -1);

  if (fd == -1)
    {
      if (forever)
    {
      f->fd = -1;
      f->errnum = errno;
      f->ignore = 0;
      f->ino = 0;
      f->dev = 0;
    }
      error (0, errno, "%s", pretty_name (f));
      errors = 1;
    }
  else
    {
      if (print_headers)
    write_header (pretty_name (f));
      errors = tail (pretty_name (f), fd, n_units);
      if (forever)
    {
      struct stat stats;

      f->errnum = 0;
      if (fstat (fd, &stats) < 0)
        {
          errors = 1;
          f->errnum = errno;
          error (0, errno, "%s", pretty_name (f));
        }
      else if (!IS_TAILABLE_FILE_TYPE (stats.st_mode))
        {
          error (0, 0, _("%s: cannot follow end of this type of file;\
 giving up on this name"),
             pretty_name (f));
          errors = 1;
          f->errnum = -1;
          f->ignore = 1;
        }

      if (errors)
        {
          close_fd (fd, pretty_name (f));
          f->fd = -1;
        }
      else
        {
          f->fd = fd;
          f->size = stats.st_size;
          f->dev = stats.st_dev;
          f->ino = stats.st_ino;
          f->n_unchanged_stats = 0;
          f->n_consecutive_size_changes = 0;
          f->ignore = 0;
        }
    }
      else
    {
      if (!is_stdin && close (fd))
        {
          error (0, errno, "%s", pretty_name (f));
          errors = 1;
        }
    }
    }

  return errors;
}

/* If the command line arguments are of the obsolescent form and the
   option string is well-formed, set *FAIL to zero, set *N_UNITS, the
   globals COUNT_LINES, FOREVER, and FROM_START, and return non-zero.
   Otherwise, if the command line arguments appear to be of the
   obsolescent form but the option string is malformed, set *FAIL to
   non-zero, don't modify any other parameter or global variable, and
   return non-zero. Otherwise, return zero and don't modify any parameter
   or global variable.  */

static int
parse_obsolescent_option (int argc, const char *const *argv,
              off_t *n_units, int *fail)
{
  const char *p = argv[1];
  const char *n_string = NULL;
  const char *n_string_end;
  bool obsolete_usage;

  int t_from_start;
  int t_count_lines;
  int t_forever;

  /* With the obsolescent form, there is one option string and
     (technically) at most one file argument.  But we allow two or more
     by default.  */
  if (argc < 2)
    return 0;

  obsolete_usage = (posix2_version () < 200112);

  /* If P starts with `+' and the POSIX version predates 1003.1-2001,
     or if P starts with `-N' (where N is a digit), or `-l', then it
     is obsolescent.  Return zero otherwise.  */
  if (! ((p[0] == '+' && obsolete_usage)
     || (p[0] == '-' && (p[1] == 'l' || ISDIGIT (p[1])))))
    return 0;

  if (*p == '+')
    t_from_start = 1;
  else if (*p == '-')
    t_from_start = 0;
  else
    return 0;

  ++p;
  if (ISDIGIT (*p))
    {
      n_string = p;
      do
    {
      ++p;
    }
      while (ISDIGIT (*p));
    }
  n_string_end = p;

  t_count_lines = 1;
  if (*p == 'c' || *p == 'b')
    {
      t_count_lines = 0;
      ++p;
    }
  else if (*p == 'l')
    {
      ++p;
    }

  t_forever = 0;
  if (*p == 'f')
    {
      t_forever = 1;
      ++p;
    }

  if (*p != '\0')
    {
      /* If (argv[1] begins with a `+' or if it begins with `-' followed
     by a digit), but has an invalid suffix character, give a diagnostic
     and indicate to caller that this *is* of the obsolescent form,
     but that it's an invalid option.  */
      if (t_from_start || n_string)
    {
      error (0, 0,
         _("%c: invalid suffix character in obsolescent option" ), *p);
      *fail = 1;
      return 1;
    }

      /* Otherwise, it might be a valid non-obsolescent option like -n.  */
      return 0;
    }

  *fail = 0;
  if (n_string == NULL)
    *n_units = DEFAULT_N_LINES;
  else
    {
      strtol_error s_err;
      uintmax_t tmp;
      char *end;

      s_err = xstrtoumax (n_string, &end, 10, &tmp,
              *n_string_end == 'b' ? "b" : NULL);
      if (s_err == LONGINT_OK && tmp <= OFF_T_MAX)
    *n_units = (off_t) tmp;
      else
    {
      /* Extract a NUL-terminated string for the error message.  */
      size_t len = n_string_end - n_string;
      char *n_string_tmp = xmalloc (len + 1);

      strncpy (n_string_tmp, n_string, len);
      n_string_tmp[len] = '\0';

      error (0, 0,
         _("%s: %s is so large that it is not representable"),
         n_string_tmp, (t_count_lines
                ? _("number of lines")
                : _("number of bytes")));
      free (n_string_tmp);
      *fail = 1;
    }
    }

  if (!*fail)
    {
      if (argc > 3)
    {
      /* When POSIXLY_CORRECT is set, enforce the `at most one
         file argument' requirement.  */
      if (getenv ("POSIXLY_CORRECT"))
        {
          error (0, 0, _("\
too many arguments;  When using tail's obsolescent option syntax (%s)\n\
there may be no more than one file argument.  Use the equivalent -n or -c\n\
option instead."), argv[1]);
          *fail = 1;
          return 1;
        }

#if DISABLED  /* FIXME: enable or remove this warning.  */
      error (0, 0, _("\
Warning: it is not portable to use two or more file arguments with\n\
tail's obsolescent option syntax (%s).  Use the equivalent -n or -c\n\
option instead."), argv[1]);
#endif
    }

      if (! obsolete_usage)
    {
      error (0, 0, _("`%s' option is obsolete; use `%s-%c %.*s'"),
         argv[1], t_forever ? " -f" : "", t_count_lines ? 'n' : 'c',
         (int) (n_string_end - n_string), n_string);
      usage (EXIT_FAILURE);
    }

      /* Set globals.  */
      from_start = t_from_start;
      count_lines = t_count_lines;
      forever = t_forever;
    }

  return 1;
}

static void
parse_options (int argc, char **argv,
           off_t *n_units, enum header_mode *header_mode,
           double *sleep_interval)
{
  int c;

  count_lines = 1;
  forever = from_start = print_headers = 0;

  while ((c = getopt_long (argc, argv, "c:n:fFqs:v", long_options, NULL))
     != -1)
    {
      switch (c)
    {
    case 0:
      break;

    case 'F':
      forever = 1;
      follow_mode = Follow_name;
      reopen_inaccessible_files = 1;
      break;

    case 'c':
    case 'n':
      count_lines = (c == 'n');
      if (*optarg == '+')
        from_start = 1;
      else if (*optarg == '-')
        ++optarg;

      {
        strtol_error s_err;
        uintmax_t n;
        s_err = xstrtoumax (optarg, NULL, 10, &n, "bkm");
        if (s_err != LONGINT_OK)
          {
        error (EXIT_FAILURE, 0, "%s: %s", optarg,
               (c == 'n'
            ? _("invalid number of lines")
            : _("invalid number of bytes")));
          }

        if (OFF_T_MAX < n)
          error (EXIT_FAILURE, 0,
           _("%s is larger than the maximum file size on this system"),
             optarg);
        *n_units = (off_t) n;
      }
      break;

    case 'f':
    case LONG_FOLLOW_OPTION:
      forever = 1;
      if (optarg == NULL)
        follow_mode = DEFAULT_FOLLOW_MODE;
      else
        follow_mode = XARGMATCH ("--follow", optarg,
                     follow_mode_string, follow_mode_map);
      break;

    case RETRY_OPTION:
      reopen_inaccessible_files = 1;
      break;

    case MAX_UNCHANGED_STATS_OPTION:
      /* --max-unchanged-stats=N */
      if (xstrtoul (optarg, NULL, 10,
            &max_n_unchanged_stats_between_opens, "") != LONGINT_OK)
        {
          error (EXIT_FAILURE, 0,
           _("%s: invalid maximum number of unchanged stats between opens"),
             optarg);
        }
      break;

    case MAX_CONSECUTIVE_SIZE_CHANGES_OPTION:
      /* --max-consecutive-size-changes=N */
      if (xstrtoul (optarg, NULL, 10,
            &max_n_consecutive_size_changes_between_opens, "")
          != LONGINT_OK)
        {
          error (EXIT_FAILURE, 0,
           _("%s: invalid maximum number of consecutive size changes"),
             optarg);
        }
      break;

    case PID_OPTION:
      {
        strtol_error s_err;
        unsigned long int tmp_ulong;
        s_err = xstrtoul (optarg, NULL, 10, &tmp_ulong, "");
        if (s_err != LONGINT_OK || tmp_ulong > PID_T_MAX)
          {
        error (EXIT_FAILURE, 0, _("%s: invalid PID"), optarg);
          }
        pid = tmp_ulong;
      }
      break;

    case 'q':
      *header_mode = never;
      break;

    case 's':
      {
        double s;
        if (xstrtod (optarg, NULL, &s) || ! (0 <= s))
          error (EXIT_FAILURE, 0,
             _("%s: invalid number of seconds"), optarg);
        *sleep_interval = s;
      }
      break;

    case 'v':
      *header_mode = always;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (reopen_inaccessible_files && follow_mode != Follow_name)
    error (0, 0, _("warning: --retry is useful only when following by name"));

  if (pid && !forever)
    error (0, 0,
       _("warning: PID ignored; --pid=PID is useful only when following"));
  else if (pid && kill (pid, 0) != 0 && errno == ENOSYS)
    {
      error (0, 0, _("warning: --pid=PID is not supported on this system"));
      pid = 0;
    }
}

int
main (int argc, char **argv)
{
  enum header_mode header_mode = multiple_files;
  int exit_status = 0;
  /* If from_start, the number of items to skip before printing; otherwise,
     the number of items at the end of the file to print.  Although the type
     is signed, the value is never negative.  */
  off_t n_units = DEFAULT_N_LINES;
  int n_files;
  char **file;
  struct File_spec *F;
  int i;

  /* The number of seconds to sleep between iterations.
     During one iteration, every file name or descriptor is checked to
     see if it has changed.  */
  double sleep_interval = 1.0;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  have_read_stdin = 0;

  {
    int fail;

    if (parse_obsolescent_option (argc,
                  (const char *const *) argv,
                  &n_units, &fail))
      {
    if (fail)
      exit (EXIT_FAILURE);
    optind = 2;
      }
    else
      {
    parse_options (argc, argv, &n_units, &header_mode, &sleep_interval);
      }
  }

  /* To start printing with item N_UNITS from the start of the file, skip
     N_UNITS - 1 items.  `tail -n +0' is actually meaningless, but for Unix
     compatibility it's treated the same as `tail -n +1'.  */
  if (from_start)
    {
      if (n_units)
    --n_units;
    }

  n_files = argc - optind;
  file = argv + optind;

  if (n_files == 0)
    {
      static char *dummy_stdin = "-";
      n_files = 1;
      file = &dummy_stdin;
    }

  F = (struct File_spec *) xmalloc (n_files * sizeof (F[0]));
  for (i = 0; i < n_files; i++)
    F[i].name = file[i];

  if (header_mode == always
      || (header_mode == multiple_files && n_files > 1))
    print_headers = 1;

  for (i = 0; i < n_files; i++)
    exit_status |= tail_file (&F[i], n_units);

  if (forever)
    {
      /* This fflush appears to be required only on Solaris2.7.  */
      if (fflush (stdout) < 0)
    error (EXIT_FAILURE, errno, _("write error"));

      SETVBUF (stdout, NULL, _IONBF, 0);
      tail_forever (F, n_files, sleep_interval);
    }

  if (have_read_stdin && close (STDIN_FILENO) < 0)
    error (EXIT_FAILURE, errno, "-");
  exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/tee.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/tee.c used in: Step 931
/* tee - read from standard input and write to standard output and files.
   Copyright (C) 85,1990-2002 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.  */

/* Mike Parker, Richard M. Stallman, and David MacKenzie */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <getopt.h>

#include "system.h"
#include "closeout.h"
#include "error.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "tee"

#define AUTHORS N_ ("Mike Parker, Richard M. Stallman, and David MacKenzie")

static int tee (int nfiles, const char **files);

/* If nonzero, append to output files rather than truncating them. */
static int append;

/* If nonzero, ignore interrupts. */
static int ignore_interrupts;

/* The name that this program was run with. */
char *program_name;

static struct option const long_options[] =
{
  {"append", no_argument, NULL, 'a'},
  {"ignore-interrupts", no_argument, NULL, 'i'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
      fputs (_("\
Copy standard input to each FILE, and also to standard output.\n\
\n\
  -a, --append              append to the given FILEs, do not overwrite\n\
  -i, --ignore-interrupts   ignore interrupt signals\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  int errs;
  int optc;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  append = 0;
  ignore_interrupts = 0;

  while ((optc = getopt_long (argc, argv, "ai", long_options, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:
      break;

    case 'a':
      append = 1;
      break;

    case 'i':
      ignore_interrupts = 1;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (ignore_interrupts)
    {
#ifdef _POSIX_SOURCE
      struct sigaction sigact;

      sigact.sa_handler = SIG_IGN;
      sigemptyset (&sigact.sa_mask);
      sigact.sa_flags = 0;
      sigaction (SIGINT, &sigact, NULL);
#else               /* !_POSIX_SOURCE */
      signal (SIGINT, SIG_IGN);
#endif              /* _POSIX_SOURCE */
    }

  /* Don't let us be killed if one of the output files is a pipe that
     doesn't consume all its input.  */
#ifdef _POSIX_SOURCE
  {
    struct sigaction sigact;

    sigact.sa_handler = SIG_IGN;
    sigemptyset (&sigact.sa_mask);
    sigact.sa_flags = 0;
    sigaction (SIGPIPE, &sigact, NULL);
  }
#else
  signal (SIGPIPE, SIG_IGN);
#endif

  /* Do *not* warn if tee is given no file arguments.
     POSIX requires that it work when given no arguments.  */

  errs = tee (argc - optind, (const char **) &argv[optind]);
  if (close (STDIN_FILENO) != 0)
    error (EXIT_FAILURE, errno, _("standard input"));

  exit (errs);
}

/* Copy the standard input into each of the NFILES files in FILES
   and into the standard output.
   Return 0 if successful, 1 if any errors occur. */

static int
tee (int nfiles, const char **files)
{
  FILE **descriptors;
  char buffer[BUFSIZ];
  int bytes_read, i;
  int ret = 0;
  const char *mode_string = (append ? "a" : "w");

  descriptors = (FILE **) xmalloc ((nfiles + 1) * sizeof (descriptors[0]));

  /* Move all the names `up' one in the argv array to make room for
     the entry for standard output.  This writes into argv[argc].  */
  for (i = nfiles; i >= 1; i--)
    files[i] = files[i - 1];

  SET_BINARY2 (0, 1);

  /* In the array of NFILES + 1 descriptors, make
     the first one correspond to standard output.   */
  descriptors[0] = stdout;
  files[0] = _("standard output");
  SETVBUF (stdout, NULL, _IONBF, 0);

  for (i = 1; i <= nfiles; i++)
    {
      descriptors[i] = fopen (files[i], mode_string);
      if (descriptors[i] == NULL)
    {
      error (0, errno, "%s", files[i]);
      ret = 1;
    }
      else
    {
      SETVBUF (descriptors[i], NULL, _IONBF, 0);
      SET_BINARY (fileno (descriptors[i]));
    }
    }

  while (1)
    {
      bytes_read = read (0, buffer, sizeof buffer);
#ifdef EINTR
      if (bytes_read < 0 && errno == EINTR)
        continue;
#endif
      if (bytes_read <= 0)
    break;

      /* Write to all NFILES + 1 descriptors.
     Standard output is the first one.  */
      for (i = 0; i <= nfiles; i++)
    {
      if (descriptors[i] != NULL)
        fwrite (buffer, bytes_read, 1, descriptors[i]);
    }
    }

  if (bytes_read == -1)
    {
      error (0, errno, _("read error"));
      ret = 1;
    }

  /* Close the files, but not standard output.  */
  for (i = 1; i <= nfiles; i++)
    if (descriptors[i] != NULL
    && (ferror (descriptors[i]) || fclose (descriptors[i]) == EOF))
      {
    error (0, errno, "%s", files[i]);
    ret = 1;
      }

  free (descriptors);

  return ret;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/tr.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/tr.c used in: Step 933
/* tr -- a filter to translate characters
   Copyright (C) 91, 1995-2002 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.  */

/* Written by Jim Meyering */

#include <config.h>

#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <getopt.h>

#include "system.h"
#include "closeout.h"
#include "error.h"
#include "safe-read.h"
#include "xstrtol.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "tr"

#define AUTHORS "Jim Meyering"

#define N_CHARS (UCHAR_MAX + 1)

/* A pointer to a filtering function.  */
typedef size_t (*Filter) (/* unsigned char *, size_t, Filter */);

/* Convert from character C to its index in the collating
   sequence array.  Just cast to an unsigned int to avoid
   problems with sign-extension.  */
#define ORD(c) (unsigned int)(c)

/* The inverse of ORD.  */
#define CHR(i) (unsigned char)(i)

/* The value for Spec_list->state that indicates to
   get_next that it should initialize the tail pointer.
   Its value should be as large as possible to avoid conflict
   a valid value for the state field -- and that may be as
   large as any valid repeat_count.  */
#define BEGIN_STATE (INT_MAX - 1)

/* The value for Spec_list->state that indicates to
   get_next that the element pointed to by Spec_list->tail is
   being considered for the first time on this pass through the
   list -- it indicates that get_next should make any necessary
   initializations.  */
#define NEW_ELEMENT (BEGIN_STATE + 1)

/* A value distinct from any character that may have been stored in a
   buffer as the result of a block-read in the function squeeze_filter.  */
#define NOT_A_CHAR (unsigned int)(-1)

/* The following (but not CC_NO_CLASS) are indices into the array of
   valid character class strings.  */
enum Char_class
  {
    CC_ALNUM = 0, CC_ALPHA = 1, CC_BLANK = 2, CC_CNTRL = 3,
    CC_DIGIT = 4, CC_GRAPH = 5, CC_LOWER = 6, CC_PRINT = 7,
    CC_PUNCT = 8, CC_SPACE = 9, CC_UPPER = 10, CC_XDIGIT = 11,
    CC_NO_CLASS = 9999
  };

/* Character class to which a character (returned by get_next) belonged;
   but it is set only if the construct from which the character was obtained
   was one of the character classes [:upper:] or [:lower:].  The value
   is used only when translating and then, only to make sure that upper
   and lower class constructs have the same relative positions in string1
   and string2.  */
enum Upper_Lower_class
  {
    UL_LOWER = 0,
    UL_UPPER = 1,
    UL_NONE = 2
  };

/* A shortcut to ensure that when constructing the translation array,
   one of the values returned by paired calls to get_next (from s1 and s2)
   is from [:upper:] and the other is from [:lower:], or neither is from
   upper or lower.  By default, GNU tr permits the identity mappings: from
   [:upper:] to [:upper:] and [:lower:] to [:lower:].  But when
   POSIXLY_CORRECT is set, those evoke diagnostics. This array is indexed
   by values of type enum Upper_Lower_class.  */
static int const class_ok[3][3] =
{
  {1, 1, 0},
  {1, 1, 0},
  {0, 0, 1}
};

/* The type of a List_element.  See build_spec_list for more details.  */
enum Range_element_type
  {
    RE_NO_TYPE = 0,
    RE_NORMAL_CHAR,
    RE_RANGE,
    RE_CHAR_CLASS,
    RE_EQUIV_CLASS,
    RE_REPEATED_CHAR
  };

/* One construct in one of tr's argument strings.
   For example, consider the POSIX version of the classic tr command:
       tr -cs 'a-zA-Z_' '[\n*]'
   String1 has 3 constructs, two of which are ranges (a-z and A-Z),
   and a single normal character, `_'.  String2 has one construct.  */
struct List_element
  {
    enum Range_element_type type;
    struct List_element *next;
    union
      {
    int normal_char;
    struct          /* unnamed */
      {
        unsigned int first_char;
        unsigned int last_char;
      }
    range;
    enum Char_class char_class;
    int equiv_code;
    struct          /* unnamed */
      {
        unsigned int the_repeated_char;
        size_t repeat_count;
      }
    repeated_char;
      }
    u;
  };

/* Each of tr's argument strings is parsed into a form that is easier
   to work with: a linked list of constructs (struct List_element).
   Each Spec_list structure also encapsulates various attributes of
   the corresponding argument string.  The attributes are used mainly
   to verify that the strings are valid in the context of any options
   specified (like -s, -d, or -c).  The main exception is the member
   `tail', which is first used to construct the list.  After construction,
   it is used by get_next to save its state when traversing the list.
   The member `state' serves a similar function.  */
struct Spec_list
  {
    /* Points to the head of the list of range elements.
       The first struct is a dummy; its members are never used.  */
    struct List_element *head;

    /* When appending, points to the last element.  When traversing via
       get_next(), points to the element to process next.  Setting
       Spec_list.state to the value BEGIN_STATE before calling get_next
       signals get_next to initialize tail to point to head->next.  */
    struct List_element *tail;

    /* Used to save state between calls to get_next().  */
    unsigned int state;

    /* Length, in the sense that length ('a-z[:digit:]123abc')
       is 42 ( = 26 + 10 + 6).  */
    size_t length;

    /* The number of [c*] and [c*0] constructs that appear in this spec.  */
    int n_indefinite_repeats;

    /* If n_indefinite_repeats is nonzero, this points to the List_element
       corresponding to the last [c*] or [c*0] construct encountered in
       this spec.  Otherwise it is undefined.  */
    struct List_element *indefinite_repeat_element;

    /* Non-zero if this spec contains at least one equivalence
       class construct e.g. [=c=].  */
    int has_equiv_class;

    /* Non-zero if this spec contains at least one character class
       construct.  E.g. [:digit:].  */
    int has_char_class;

    /* Non-zero if this spec contains at least one of the character class
       constructs (all but upper and lower) that aren't allowed in s2.  */
    int has_restricted_char_class;
  };

/* A representation for escaped string1 or string2.  As a string is parsed,
   any backslash-escaped characters (other than octal or \a, \b, \f, \n,
   etc.) are marked as such in this structure by setting the corresponding
   entry in the ESCAPED vector.  */
struct E_string
{
  unsigned char *s;
  int *escaped;
  size_t len;
};

/* Return nonzero if the Ith character of escaped string ES matches C
   and is not escaped itself.  */
#define ES_MATCH(ES, I, C) ((ES)->s[(I)] == (C) && !(ES)->escaped[(I)])

/* The name by which this program was run.  */
char *program_name;

/* When nonzero, each sequence in the input of a repeated character
   (call it c) is replaced (in the output) by a single occurrence of c
   for every c in the squeeze set.  */
static int squeeze_repeats = 0;

/* When nonzero, removes characters in the delete set from input.  */
static int delete = 0;

/* Use the complement of set1 in place of set1.  */
static int complement = 0;

/* When nonzero, this flag causes GNU tr to provide strict
   compliance with POSIX draft 1003.2.11.2.  The POSIX spec
   says that when -d is used without -s, string2 (if present)
   must be ignored.  Silently ignoring arguments is a bad idea.
   The default GNU behavior is to give a usage message and exit.
   Additionally, when this flag is nonzero, tr prints warnings
   on stderr if it is being used in a manner that is not portable.
   Applicable warnings are given by default, but are suppressed
   if the environment variable `POSIXLY_CORRECT' is set, since
   being POSIX conformant means we can't issue such messages.
   Warnings on the following topics are suppressed when this
   variable is nonzero:
   1. Ambiguous octal escapes.  */
static int posix_pedantic;

/* When tr is performing translation and string1 is longer than string2,
   POSIX says that the result is undefined.  That gives the implementor
   of a POSIX conforming version of tr two reasonable choices for the
   semantics of this case.

   * The BSD tr pads string2 to the length of string1 by
   repeating the last character in string2.

   * System V tr ignores characters in string1 that have no
   corresponding character in string2.  That is, string1 is effectively
   truncated to the length of string2.

   When nonzero, this flag causes GNU tr to imitate the behavior
   of System V tr when translating with string1 longer than string2.
   The default is to emulate BSD tr.  This flag is ignored in modes where
   no translation is performed.  Emulating the System V tr
   in this exceptional case causes the relatively common BSD idiom:

       tr -cs A-Za-z0-9 '\012'

   to break (it would convert only zero bytes, rather than all
   non-alphanumerics, to newlines).

   WARNING: This switch does not provide general BSD or System V
   compatibility.  For example, it doesn't disable the interpretation
   of the POSIX constructs [:alpha:], [=c=], and [c*10], so if by
   some unfortunate coincidence you use such constructs in scripts
   expecting to use some other version of tr, the scripts will break.  */
static int truncate_set1 = 0;

/* An alias for (!delete && non_option_args == 2).
   It is set in main and used there and in validate().  */
static int translating;

#ifndef BUFSIZ
# define BUFSIZ 8192
#endif

#define IO_BUF_SIZE BUFSIZ
static unsigned char io_buf[IO_BUF_SIZE];

static char const *const char_class_name[] =
{
  "alnum", "alpha", "blank", "cntrl", "digit", "graph",
  "lower", "print", "punct", "space", "upper", "xdigit"
};
#define N_CHAR_CLASSES (sizeof(char_class_name) / sizeof(char_class_name[0]))

typedef char SET_TYPE;

/* Array of boolean values.  A character `c' is a member of the
   squeeze set if and only if in_squeeze_set[c] is true.  The squeeze
   set is defined by the last (possibly, the only) string argument
   on the command line when the squeeze option is given.  */
static SET_TYPE in_squeeze_set[N_CHARS];

/* Array of boolean values.  A character `c' is a member of the
   delete set if and only if in_delete_set[c] is true.  The delete
   set is defined by the first (or only) string argument on the
   command line when the delete option is given.  */
static SET_TYPE in_delete_set[N_CHARS];

/* Array of character values defining the translation (if any) that
   tr is to perform.  Translation is performed only when there are
   two specification strings and the delete switch is not given.  */
static char xlate[N_CHARS];

static struct option const long_options[] =
{
  {"complement", no_argument, NULL, 'c'},
  {"delete", no_argument, NULL, 'd'},
  {"squeeze-repeats", no_argument, NULL, 's'},
  {"truncate-set1", no_argument, NULL, 't'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};


void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... SET1 [SET2]\n\
"),
          program_name);
      fputs (_("\
Translate, squeeze, and/or delete characters from standard input,\n\
writing to standard output.\n\
\n\
  -c, --complement        first complement SET1\n\
  -d, --delete            delete characters in SET1, do not translate\n\
  -s, --squeeze-repeats   replace each input sequence of a repeated character\n\
                            that is listed in SET1 with a single occurrence\n\
                            of that character\n\
  -t, --truncate-set1     first truncate SET1 to length of SET2\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
SETs are specified as strings of characters.  Most represent themselves.\n\
Interpreted sequences are:\n\
\n\
  \\NNN            character with octal value NNN (1 to 3 octal digits)\n\
  \\\\              backslash\n\
  \\a              audible BEL\n\
  \\b              backspace\n\
  \\f              form feed\n\
  \\n              new line\n\
  \\r              return\n\
  \\t              horizontal tab\n\
"), stdout);
     fputs (_("\
  \\v              vertical tab\n\
  CHAR1-CHAR2     all characters from CHAR1 to CHAR2 in ascending order\n\
  [CHAR*]         in SET2, copies of CHAR until length of SET1\n\
  [CHAR*REPEAT]   REPEAT copies of CHAR, REPEAT octal if starting with 0\n\
  [:alnum:]       all letters and digits\n\
  [:alpha:]       all letters\n\
  [:blank:]       all horizontal whitespace\n\
  [:cntrl:]       all control characters\n\
  [:digit:]       all digits\n\
"), stdout);
     fputs (_("\
  [:graph:]       all printable characters, not including space\n\
  [:lower:]       all lower case letters\n\
  [:print:]       all printable characters, including space\n\
  [:punct:]       all punctuation characters\n\
  [:space:]       all horizontal or vertical whitespace\n\
  [:upper:]       all upper case letters\n\
  [:xdigit:]      all hexadecimal digits\n\
  [=CHAR=]        all characters which are equivalent to CHAR\n\
"), stdout);
     fputs (_("\
\n\
Translation occurs if -d is not given and both SET1 and SET2 appear.\n\
-t may be used only when translating.  SET2 is extended to length of\n\
SET1 by repeating its last character as necessary.  \
"), stdout);
     fputs (_("\
Excess characters\n\
of SET2 are ignored.  Only [:lower:] and [:upper:] are guaranteed to\n\
expand in ascending order; used in SET2 while translating, they may\n\
only be used in pairs to specify case conversion.  \
"), stdout);
     fputs (_("\
-s uses SET1 if not\n\
translating nor deleting; else squeezing uses SET2 and occurs after\n\
translation or deletion.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Return nonzero if the character C is a member of the
   equivalence class containing the character EQUIV_CLASS.  */

static int
is_equiv_class_member (unsigned int equiv_class, unsigned int c)
{
  return (equiv_class == c);
}

/* Return nonzero if the character C is a member of the
   character class CHAR_CLASS.  */

static int
is_char_class_member (enum Char_class char_class, unsigned int c)
{
  int result;

  switch (char_class)
    {
    case CC_ALNUM:
      result = ISALNUM (c);
      break;
    case CC_ALPHA:
      result = ISALPHA (c);
      break;
    case CC_BLANK:
      result = ISBLANK (c);
      break;
    case CC_CNTRL:
      result = ISCNTRL (c);
      break;
    case CC_DIGIT:
      result = ISDIGIT_LOCALE (c);
      break;
    case CC_GRAPH:
      result = ISGRAPH (c);
      break;
    case CC_LOWER:
      result = ISLOWER (c);
      break;
    case CC_PRINT:
      result = ISPRINT (c);
      break;
    case CC_PUNCT:
      result = ISPUNCT (c);
      break;
    case CC_SPACE:
      result = ISSPACE (c);
      break;
    case CC_UPPER:
      result = ISUPPER (c);
      break;
    case CC_XDIGIT:
      result = ISXDIGIT (c);
      break;
    default:
      abort ();
      break;
    }
  return result;
}

static void
es_free (struct E_string *es)
{
  free (es->s);
  free (es->escaped);
}

/* Perform the first pass over each range-spec argument S, converting all
   \c and \ddd escapes to their one-byte representations.  The conversion
   is done in-place, so S must point to writable storage.  If an invalid
   quote sequence is found print an error message and return nonzero.
   Otherwise set *LEN to the length of the resulting string and return
   zero.  The resulting array of characters may contain zero-bytes;
   however, on input, S is assumed to be null-terminated, and hence
   cannot contain actual (non-escaped) zero bytes.  */

static int
unquote (const unsigned char *s, struct E_string *es)
{
  size_t i, j;
  size_t len;

  len = strlen ((char *) s);

  es->s = (unsigned char *) xmalloc (len);
  es->escaped = (int *) xmalloc (len * sizeof (es->escaped[0]));
  for (i = 0; i < len; i++)
    es->escaped[i] = 0;

  j = 0;
  for (i = 0; s[i]; i++)
    {
      switch (s[i])
    {
      int c;
    case '\\':
      switch (s[i + 1])
        {
          int oct_digit;
        case '\\':
          c = '\\';
          break;
        case 'a':
          c = '\007';
          break;
        case 'b':
          c = '\b';
          break;
        case 'f':
          c = '\f';
          break;
        case 'n':
          c = '\n';
          break;
        case 'r':
          c = '\r';
          break;
        case 't':
          c = '\t';
          break;
        case 'v':
          c = '\v';
          break;
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
          c = s[i + 1] - '0';
          oct_digit = s[i + 2] - '0';
          if (0 <= oct_digit && oct_digit <= 7)
        {
          c = 8 * c + oct_digit;
          ++i;
          oct_digit = s[i + 2] - '0';
          if (0 <= oct_digit && oct_digit <= 7)
            {
              if (8 * c + oct_digit < N_CHARS)
            {
              c = 8 * c + oct_digit;
              ++i;
            }
              else if (!posix_pedantic)
            {
              /* A 3-digit octal number larger than \377 won't
                 fit in 8 bits.  So we stop when adding the
                 next digit would put us over the limit and
                 give a warning about the ambiguity.  POSIX
                 isn't clear on this, but one person has said
                 that in his interpretation, POSIX says tr
                 can't even give a warning.  */
              error (0, 0, _("warning: the ambiguous octal escape \
\\%c%c%c is being\n\tinterpreted as the 2-byte sequence \\0%c%c, `%c'"),
                 s[i], s[i + 1], s[i + 2],
                 s[i], s[i + 1], s[i + 2]);
            }
            }
        }
          break;
        case '\0':
          error (0, 0, _("invalid backslash escape at end of string"));
          return 1;

        default:
          if (posix_pedantic)
        {
          error (0, 0, _("invalid backslash escape `\\%c'"), s[i + 1]);
          return 1;
        }
          else
            {
          c = s[i + 1];
          es->escaped[j] = 1;
        }
        }
      ++i;
      es->s[j++] = c;
      break;
    default:
      es->s[j++] = s[i];
      break;
    }
    }
  es->len = j;
  return 0;
}

/* If CLASS_STR is a valid character class string, return its index
   in the global char_class_name array.  Otherwise, return CC_NO_CLASS.  */

static enum Char_class
look_up_char_class (const unsigned char *class_str, size_t len)
{
  unsigned int i;

  for (i = 0; i < N_CHAR_CLASSES; i++)
    if (strncmp ((const char *) class_str, char_class_name[i], len) == 0
    && strlen (char_class_name[i]) == len)
      return (enum Char_class) i;
  return CC_NO_CLASS;
}

/* Return a newly allocated string with a printable version of C.
   This function is used solely for formatting error messages.  */

static char *
make_printable_char (unsigned int c)
{
  char *buf = xmalloc (5);

  assert (c < N_CHARS);
  if (ISPRINT (c))
    {
      buf[0] = c;
      buf[1] = '\0';
    }
  else
    {
      sprintf (buf, "\\%03o", c);
    }
  return buf;
}

/* Return a newly allocated copy of S which is suitable for printing.
   LEN is the number of characters in S.  Most non-printing
   (isprint) characters are represented by a backslash followed by
   3 octal digits.  However, the characters represented by \c escapes
   where c is one of [abfnrtv] are represented by their 2-character \c
   sequences.  This function is used solely for printing error messages.  */

static char *
make_printable_str (const unsigned char *s, size_t len)
{
  /* Worst case is that every character expands to a backslash
     followed by a 3-character octal escape sequence.  */
  char *printable_buf = xmalloc (4 * len + 1);
  char *p = printable_buf;
  size_t i;

  for (i = 0; i < len; i++)
    {
      char buf[5];
      char *tmp = NULL;

      switch (s[i])
    {
    case '\\':
      tmp = "\\";
      break;
    case '\007':
      tmp = "\\a";
      break;
    case '\b':
      tmp = "\\b";
      break;
    case '\f':
      tmp = "\\f";
      break;
    case '\n':
      tmp = "\\n";
      break;
    case '\r':
      tmp = "\\r";
      break;
    case '\t':
      tmp = "\\t";
      break;
    case '\v':
      tmp = "\\v";
      break;
    default:
      if (ISPRINT (s[i]))
        {
          buf[0] = s[i];
          buf[1] = '\0';
        }
      else
        sprintf (buf, "\\%03o", s[i]);
      tmp = buf;
      break;
    }
      p = stpcpy (p, tmp);
    }
  return printable_buf;
}

/* Append a newly allocated structure representing a
   character C to the specification list LIST.  */

static void
append_normal_char (struct Spec_list *list, unsigned int c)
{
  struct List_element *new;

  new = (struct List_element *) xmalloc (sizeof (struct List_element));
  new->next = NULL;
  new->type = RE_NORMAL_CHAR;
  new->u.normal_char = c;
  assert (list->tail);
  list->tail->next = new;
  list->tail = new;
}

/* Append a newly allocated structure representing the range
   of characters from FIRST to LAST to the specification list LIST.
   Return nonzero if LAST precedes FIRST in the collating sequence,
   zero otherwise.  This means that '[c-c]' is acceptable.  */

static int
append_range (struct Spec_list *list, unsigned int first, unsigned int last)
{
  struct List_element *new;

  if (ORD (first) > ORD (last))
    {
      char *tmp1 = make_printable_char (first);
      char *tmp2 = make_printable_char (last);

      error (0, 0,
       _("range-endpoints of `%s-%s' are in reverse collating sequence order"),
         tmp1, tmp2);
      free (tmp1);
      free (tmp2);
      return 1;
    }
  new = (struct List_element *) xmalloc (sizeof (struct List_element));
  new->next = NULL;
  new->type = RE_RANGE;
  new->u.range.first_char = first;
  new->u.range.last_char = last;
  assert (list->tail);
  list->tail->next = new;
  list->tail = new;
  return 0;
}

/* If CHAR_CLASS_STR is a valid character class string, append a
   newly allocated structure representing that character class to the end
   of the specification list LIST and return 0.  If CHAR_CLASS_STR is not
   a valid string return nonzero.  */

static int
append_char_class (struct Spec_list *list,
           const unsigned char *char_class_str, size_t len)
{
  enum Char_class char_class;
  struct List_element *new;

  char_class = look_up_char_class (char_class_str, len);
  if (char_class == CC_NO_CLASS)
    return 1;
  new = (struct List_element *) xmalloc (sizeof (struct List_element));
  new->next = NULL;
  new->type = RE_CHAR_CLASS;
  new->u.char_class = char_class;
  assert (list->tail);
  list->tail->next = new;
  list->tail = new;
  return 0;
}

/* Append a newly allocated structure representing a [c*n]
   repeated character construct to the specification list LIST.
   THE_CHAR is the single character to be repeated, and REPEAT_COUNT
   is a non-negative repeat count.  */

static void
append_repeated_char (struct Spec_list *list, unsigned int the_char,
              size_t repeat_count)
{
  struct List_element *new;

  new = (struct List_element *) xmalloc (sizeof (struct List_element));
  new->next = NULL;
  new->type = RE_REPEATED_CHAR;
  new->u.repeated_char.the_repeated_char = the_char;
  new->u.repeated_char.repeat_count = repeat_count;
  assert (list->tail);
  list->tail->next = new;
  list->tail = new;
}

/* Given a string, EQUIV_CLASS_STR, from a [=str=] context and
   the length of that string, LEN, if LEN is exactly one, append
   a newly allocated structure representing the specified
   equivalence class to the specification list, LIST and return zero.
   If LEN is not 1, return nonzero.  */

static int
append_equiv_class (struct Spec_list *list,
            const unsigned char *equiv_class_str, size_t len)
{
  struct List_element *new;

  if (len != 1)
    return 1;
  new = (struct List_element *) xmalloc (sizeof (struct List_element));
  new->next = NULL;
  new->type = RE_EQUIV_CLASS;
  new->u.equiv_code = *equiv_class_str;
  assert (list->tail);
  list->tail->next = new;
  list->tail = new;
  return 0;
}

/* Return a newly allocated copy of the LEN-byte prefix of P.
   The returned string may contain NUL bytes and is *not* NUL-terminated.  */

static unsigned char *
xmemdup (const unsigned char *p, size_t len)
{
  unsigned char *tmp = (unsigned char *) xmalloc (len);

  /* Use memcpy rather than strncpy because `p' may contain zero-bytes.  */
  memcpy (tmp, p, len);
  return tmp;
}

/* Search forward starting at START_IDX for the 2-char sequence
   (PRE_BRACKET_CHAR,']') in the string P of length P_LEN.  If such
   a sequence is found, set *RESULT_IDX to the index of the first
   character and return nonzero. Otherwise return zero.  P may contain
   zero bytes.  */

static int
find_closing_delim (const struct E_string *es, size_t start_idx,
            unsigned int pre_bracket_char, size_t *result_idx)
{
  size_t i;

  for (i = start_idx; i < es->len - 1; i++)
    if (es->s[i] == pre_bracket_char && es->s[i + 1] == ']'
    && !es->escaped[i] && !es->escaped[i + 1])
      {
    *result_idx = i;
    return 1;
      }
  return 0;
}

/* Parse the bracketed repeat-char syntax.  If the P_LEN characters
   beginning with P[ START_IDX ] comprise a valid [c*n] construct,
   then set *CHAR_TO_REPEAT, *REPEAT_COUNT, and *CLOSING_BRACKET_IDX
   and return zero. If the second character following
   the opening bracket is not `*' or if no closing bracket can be
   found, return -1.  If a closing bracket is found and the
   second char is `*', but the string between the `*' and `]' isn't
   empty, an octal number, or a decimal number, print an error message
   and return -2.  */

static int
find_bracketed_repeat (const struct E_string *es, size_t start_idx,
               unsigned int *char_to_repeat, size_t *repeat_count,
               size_t *closing_bracket_idx)
{
  size_t i;

  assert (start_idx + 1 < es->len);
  if (!ES_MATCH (es, start_idx + 1, '*'))
    return -1;

  for (i = start_idx + 2; i < es->len; i++)
    {
      if (ES_MATCH (es, i, ']'))
    {
      size_t digit_str_len = i - start_idx - 2;

      *char_to_repeat = es->s[start_idx];
      if (digit_str_len == 0)
        {
          /* We've matched [c*] -- no explicit repeat count.  */
          *repeat_count = 0;
          *closing_bracket_idx = i;
          return 0;
        }

      /* Here, we have found [c*s] where s should be a string
         of octal (if it starts with `0') or decimal digits.  */
      {
        const char *digit_str = (const char *) &es->s[start_idx + 2];
        unsigned long int tmp_ulong;
        char *d_end;
        int base = 10;
        /* Select the base manually so we can be sure it's either 8 or 10.
           If the spec allowed it to be interpreted as hexadecimal, we
           could have used `0' and let xstrtoul decide.  */
        if (*digit_str == '0')
          {
        base = 8;
        ++digit_str;
        --digit_str_len;
          }
        if (xstrtoul (digit_str, &d_end, base, &tmp_ulong, NULL)
          != LONGINT_OK
        || BEGIN_STATE < tmp_ulong
        || digit_str + digit_str_len != d_end)
          {
        char *tmp = make_printable_str (es->s + start_idx + 2,
                        i - start_idx - 2);
        error (0, 0, _("invalid repeat count `%s' in [c*n] construct"),
               tmp);
        free (tmp);
        return -2;
          }
        *repeat_count = tmp_ulong;
      }
      *closing_bracket_idx = i;
      return 0;
    }
    }
  return -1;            /* No bracket found.  */
}

/* Return nonzero if the string at ES->s[IDX] matches the regular
   expression `\*[0-9]*\]', zero otherwise.  To match, the `*' and
   the `]' must not be escaped.  */

static int
star_digits_closebracket (const struct E_string *es, size_t idx)
{
  size_t i;

  if (!ES_MATCH (es, idx, '*'))
    return 0;

  for (i = idx + 1; i < es->len; i++)
    {
      if (!ISDIGIT (es->s[i]))
    {
      if (ES_MATCH (es, i, ']'))
        return 1;
      return 0;
    }
    }
  return 0;
}

/* Convert string UNESCAPED_STRING (which has been preprocessed to
   convert backslash-escape sequences) of length LEN characters into
   a linked list of the following 5 types of constructs:
      - [:str:] Character class where `str' is one of the 12 valid strings.
      - [=c=] Equivalence class where `c' is any single character.
      - [c*n] Repeat the single character `c' `n' times. n may be omitted.
      However, if `n' is present, it must be a non-negative octal or
      decimal integer.
      - r-s Range of characters from `r' to `s'.  The second endpoint must
      not precede the first in the current collating sequence.
      - c Any other character is interpreted as itself.  */

static int
build_spec_list (const struct E_string *es, struct Spec_list *result)
{
  const unsigned char *p;
  size_t i;

  p = es->s;

  /* The main for-loop below recognizes the 4 multi-character constructs.
     A character that matches (in its context) none of the multi-character
     constructs is classified as `normal'.  Since all multi-character
     constructs have at least 3 characters, any strings of length 2 or
     less are composed solely of normal characters.  Hence, the index of
     the outer for-loop runs only as far as LEN-2.  */

  for (i = 0; i + 2 < es->len; /* empty */)
    {
      if (ES_MATCH (es, i, '['))
    {
      int matched_multi_char_construct;
      size_t closing_bracket_idx;
      unsigned int char_to_repeat;
      size_t repeat_count;
      int err;

      matched_multi_char_construct = 1;
      if (ES_MATCH (es, i + 1, ':')
          || ES_MATCH (es, i + 1, '='))
        {
          size_t closing_delim_idx;
          int found;

          found = find_closing_delim (es, i + 2, p[i + 1],
                      &closing_delim_idx);
          if (found)
        {
          int parse_failed;
          size_t opnd_str_len = closing_delim_idx - 1 - (i + 2) + 1;
          unsigned char *opnd_str;

          if (opnd_str_len == 0)
            {
              if (p[i + 1] == ':')
            error (0, 0, _("missing character class name `[::]'"));
              else
            error (0, 0,
                   _("missing equivalence class character `[==]'"));
              return 1;
            }

          opnd_str = xmemdup (p + i + 2, opnd_str_len);

          if (p[i + 1] == ':')
            {
              parse_failed = append_char_class (result, opnd_str,
                            opnd_str_len);

              /* FIXME: big comment.  */
              if (parse_failed)
            {
              if (star_digits_closebracket (es, i + 2))
                {
                  free (opnd_str);
                  goto try_bracketed_repeat;
                }
              else
                {
                  char *tmp = make_printable_str (opnd_str,
                                  opnd_str_len);
                  error (0, 0, _("invalid character class `%s'"),
                     tmp);
                  free (tmp);
                  return 1;
                }
            }
            }
          else
            {
              parse_failed = append_equiv_class (result, opnd_str,
                             opnd_str_len);

              /* FIXME: big comment.  */
              if (parse_failed)
            {
              if (star_digits_closebracket (es, i + 2))
                {
                  free (opnd_str);
                  goto try_bracketed_repeat;
                }
              else
                {
                  char *tmp = make_printable_str (opnd_str,
                                  opnd_str_len);
                  error (0, 0,
           _("%s: equivalence class operand must be a single character"),
                     tmp);
                  free (tmp);
                  return 1;
                }
            }
            }
          free (opnd_str);

          /* Return nonzero if append_*_class reports a problem.  */
          if (parse_failed)
            return 1;
          else
            i = closing_delim_idx + 2;
          continue;
        }
          /* Else fall through.  This could be [:*] or [=*].  */
        }

    try_bracketed_repeat:

      /* Determine whether this is a bracketed repeat range
         matching the RE \[.\*(dec_or_oct_number)?\].  */
      err = find_bracketed_repeat (es, i + 1, &char_to_repeat,
                       &repeat_count,
                       &closing_bracket_idx);
      if (err == 0)
        {
          append_repeated_char (result, char_to_repeat, repeat_count);
          i = closing_bracket_idx + 1;
        }
      else if (err == -1)
        {
          matched_multi_char_construct = 0;
        }
      else
        {
          /* Found a string that looked like [c*n] but the
         numeric part was invalid.  */
          return 1;
        }

      if (matched_multi_char_construct)
        continue;

      /* We reach this point if P does not match [:str:], [=c=],
         [c*n], or [c*].  Now, see if P looks like a range `[-c'
         (from `[' to `c').  */
    }

      /* Look ahead one char for ranges like a-z.  */
      if (ES_MATCH (es, i + 1, '-'))
    {
      if (append_range (result, p[i], p[i + 2]))
        return 1;
      i += 3;
    }
      else
    {
      append_normal_char (result, p[i]);
      ++i;
    }
    }

  /* Now handle the (2 or fewer) remaining characters p[i]..p[es->len - 1].  */
  for (; i < es->len; i++)
    append_normal_char (result, p[i]);

  return 0;
}

/* Given a Spec_list S (with its saved state implicit in the values
   of its members `tail' and `state'), return the next single character
   in the expansion of S's constructs.  If the last character of S was
   returned on the previous call or if S was empty, this function
   returns -1.  For example, successive calls to get_next where S
   represents the spec-string 'a-d[y*3]' will return the sequence
   of values a, b, c, d, y, y, y, -1.  Finally, if the construct from
   which the returned character comes is [:upper:] or [:lower:], the
   parameter CLASS is given a value to indicate which it was.  Otherwise
   CLASS is set to UL_NONE.  This value is used only when constructing
   the translation table to verify that any occurrences of upper and
   lower class constructs in the spec-strings appear in the same relative
   positions.  */

static int
get_next (struct Spec_list *s, enum Upper_Lower_class *class)
{
  struct List_element *p;
  int return_val;
  int i;

  if (class)
    *class = UL_NONE;

  if (s->state == BEGIN_STATE)
    {
      s->tail = s->head->next;
      s->state = NEW_ELEMENT;
    }

  p = s->tail;
  if (p == NULL)
    return -1;

  switch (p->type)
    {
    case RE_NORMAL_CHAR:
      return_val = p->u.normal_char;
      s->state = NEW_ELEMENT;
      s->tail = p->next;
      break;

    case RE_RANGE:
      if (s->state == NEW_ELEMENT)
    s->state = ORD (p->u.range.first_char);
      else
    ++(s->state);
      return_val = CHR (s->state);
      if (s->state == ORD (p->u.range.last_char))
    {
      s->tail = p->next;
      s->state = NEW_ELEMENT;
    }
      break;

    case RE_CHAR_CLASS:
      if (class)
    {
      int upper_or_lower;
      switch (p->u.char_class)
        {
        case CC_LOWER:
          *class = UL_LOWER;
          upper_or_lower = 1;
          break;
        case CC_UPPER:
          *class = UL_UPPER;
          upper_or_lower = 1;
          break;
        default:
          upper_or_lower = 0;
          break;
        }

      if (upper_or_lower)
        {
          s->tail = p->next;
          s->state = NEW_ELEMENT;
          return_val = 0;
          break;
        }
    }

      if (s->state == NEW_ELEMENT)
    {
      for (i = 0; i < N_CHARS; i++)
        if (is_char_class_member (p->u.char_class, i))
          break;
      assert (i < N_CHARS);
      s->state = i;
    }
      assert (is_char_class_member (p->u.char_class, s->state));
      return_val = CHR (s->state);
      for (i = s->state + 1; i < N_CHARS; i++)
    if (is_char_class_member (p->u.char_class, i))
      break;
      if (i < N_CHARS)
    s->state = i;
      else
    {
      s->tail = p->next;
      s->state = NEW_ELEMENT;
    }
      break;

    case RE_EQUIV_CLASS:
      /* FIXME: this assumes that each character is alone in its own
         equivalence class (which appears to be correct for my
         LC_COLLATE.  But I don't know of any function that allows
         one to determine a character's equivalence class.  */

      return_val = p->u.equiv_code;
      s->state = NEW_ELEMENT;
      s->tail = p->next;
      break;

    case RE_REPEATED_CHAR:
      /* Here, a repeat count of n == 0 means don't repeat at all.  */
      if (p->u.repeated_char.repeat_count == 0)
    {
      s->tail = p->next;
      s->state = NEW_ELEMENT;
      return_val = get_next (s, class);
    }
      else
    {
      if (s->state == NEW_ELEMENT)
        {
          s->state = 0;
        }
      ++(s->state);
      return_val = p->u.repeated_char.the_repeated_char;
      if (p->u.repeated_char.repeat_count > 0
          && s->state == p->u.repeated_char.repeat_count)
        {
          s->tail = p->next;
          s->state = NEW_ELEMENT;
        }
    }
      break;

    case RE_NO_TYPE:
      abort ();
      break;

    default:
      abort ();
      break;
    }

  return return_val;
}

/* This is a minor kludge.  This function is called from
   get_spec_stats to determine the cardinality of a set derived
   from a complemented string.  It's a kludge in that some of the
   same operations are (duplicated) performed in set_initialize.  */

static int
card_of_complement (struct Spec_list *s)
{
  int c;
  int cardinality = N_CHARS;
  SET_TYPE in_set[N_CHARS];

  memset (in_set, 0, N_CHARS * sizeof (in_set[0]));
  s->state = BEGIN_STATE;
  while ((c = get_next (s, NULL)) != -1)
    if (!in_set[c]++)
      --cardinality;
  return cardinality;
}

/* Gather statistics about the spec-list S in preparation for the tests
   in validate that determine the consistency of the specs.  This function
   is called at most twice; once for string1, and again for any string2.
   LEN_S1 < 0 indicates that this is the first call and that S represents
   string1.  When LEN_S1 >= 0, it is the length of the expansion of the
   constructs in string1, and we can use its value to resolve any
   indefinite repeat construct in S (which represents string2).  Hence,
   this function has the side-effect that it converts a valid [c*]
   construct in string2 to [c*n] where n is large enough (or 0) to give
   string2 the same length as string1.  For example, with the command
   tr a-z 'A[\n*]Z' on the second call to get_spec_stats, LEN_S1 would
   be 26 and S (representing string2) would be converted to 'A[\n*24]Z'.  */

static void
get_spec_stats (struct Spec_list *s)
{
  struct List_element *p;
  int len = 0;

  s->n_indefinite_repeats = 0;
  s->has_equiv_class = 0;
  s->has_restricted_char_class = 0;
  s->has_char_class = 0;
  for (p = s->head->next; p; p = p->next)
    {
      switch (p->type)
    {
      int i;
    case RE_NORMAL_CHAR:
      ++len;
      break;

    case RE_RANGE:
      assert (p->u.range.last_char >= p->u.range.first_char);
      len += p->u.range.last_char - p->u.range.first_char + 1;
      break;

    case RE_CHAR_CLASS:
      s->has_char_class = 1;
      for (i = 0; i < N_CHARS; i++)
        if (is_char_class_member (p->u.char_class, i))
          ++len;
      switch (p->u.char_class)
        {
        case CC_UPPER:
        case CC_LOWER:
          break;
        default:
          s->has_restricted_char_class = 1;
          break;
        }
      break;

    case RE_EQUIV_CLASS:
      for (i = 0; i < N_CHARS; i++)
        if (is_equiv_class_member (p->u.equiv_code, i))
          ++len;
      s->has_equiv_class = 1;
      break;

    case RE_REPEATED_CHAR:
      if (p->u.repeated_char.repeat_count > 0)
        len += p->u.repeated_char.repeat_count;
      else if (p->u.repeated_char.repeat_count == 0)
        {
          s->indefinite_repeat_element = p;
          ++(s->n_indefinite_repeats);
        }
      break;

    case RE_NO_TYPE:
      assert (0);
      break;
    }
    }

  s->length = len;
}

static void
get_s1_spec_stats (struct Spec_list *s1)
{
  get_spec_stats (s1);
  if (complement)
    s1->length = card_of_complement (s1);
}

static void
get_s2_spec_stats (struct Spec_list *s2, size_t len_s1)
{
  get_spec_stats (s2);
  if (len_s1 >= s2->length && s2->n_indefinite_repeats == 1)
    {
      s2->indefinite_repeat_element->u.repeated_char.repeat_count =
    len_s1 - s2->length;
      s2->length = len_s1;
    }
}

static void
spec_init (struct Spec_list *spec_list)
{
  spec_list->head = spec_list->tail =
    (struct List_element *) xmalloc (sizeof (struct List_element));
  spec_list->head->next = NULL;
}

/* This function makes two passes over the argument string S.  The first
   one converts all \c and \ddd escapes to their one-byte representations.
   The second constructs a linked specification list, SPEC_LIST, of the
   characters and constructs that comprise the argument string.  If either
   of these passes detects an error, this function returns nonzero.  */

static int
parse_str (const unsigned char *s, struct Spec_list *spec_list)
{
  struct E_string es;
  int fail;

  fail = unquote (s, &es);
  if (!fail)
    fail = build_spec_list (&es, spec_list);
  es_free (&es);
  return fail;
}

/* Given two specification lists, S1 and S2, and assuming that
   S1->length > S2->length, append a single [c*n] element to S2 where c
   is the last character in the expansion of S2 and n is the difference
   between the two lengths.
   Upon successful completion, S2->length is set to S1->length.  The only
   way this function can fail to make S2 as long as S1 is when S2 has
   zero-length, since in that case, there is no last character to repeat.
   So S2->length is required to be at least 1.

   Providing this functionality allows the user to do some pretty
   non-BSD (and non-portable) things:  For example, the command
       tr -cs '[:upper:]0-9' '[:lower:]'
   is almost guaranteed to give results that depend on your collating
   sequence.  */

static void
string2_extend (const struct Spec_list *s1, struct Spec_list *s2)
{
  struct List_element *p;
  int char_to_repeat;
  int i;

  assert (translating);
  assert (s1->length > s2->length);
  assert (s2->length > 0);

  p = s2->tail;
  switch (p->type)
    {
    case RE_NORMAL_CHAR:
      char_to_repeat = p->u.normal_char;
      break;
    case RE_RANGE:
      char_to_repeat = p->u.range.last_char;
      break;
    case RE_CHAR_CLASS:
      for (i = N_CHARS; i >= 0; i--)
    if (is_char_class_member (p->u.char_class, i))
      break;
      assert (i >= 0);
      char_to_repeat = CHR (i);
      break;

    case RE_REPEATED_CHAR:
      char_to_repeat = p->u.repeated_char.the_repeated_char;
      break;

    case RE_EQUIV_CLASS:
      /* This shouldn't happen, because validate exits with an error
         if it finds an equiv class in string2 when translating.  */
      abort ();
      break;

    case RE_NO_TYPE:
      abort ();
      break;

    default:
      abort ();
      break;
    }

  append_repeated_char (s2, char_to_repeat, s1->length - s2->length);
  s2->length = s1->length;
}

/* Return non-zero if S is a non-empty list in which exactly one
   character (but potentially, many instances of it) appears.
   E.g.  [X*] or xxxxxxxx.  */

static int
homogeneous_spec_list (struct Spec_list *s)
{
  int b, c;

  s->state = BEGIN_STATE;

  if ((b = get_next (s, NULL)) == -1)
    return 0;

  while ((c = get_next (s, NULL)) != -1)
    if (c != b)
      return 0;

  return 1;
}

/* Die with an error message if S1 and S2 describe strings that
   are not valid with the given command line switches.
   A side effect of this function is that if a valid [c*] or
   [c*0] construct appears in string2, it is converted to [c*n]
   with a value for n that makes s2->length == s1->length.  By
   the same token, if the --truncate-set1 option is not
   given, S2 may be extended.  */

static void
validate (struct Spec_list *s1, struct Spec_list *s2)
{
  get_s1_spec_stats (s1);
  if (s1->n_indefinite_repeats > 0)
    {
      error (EXIT_FAILURE, 0,
         _("the [c*] repeat construct may not appear in string1"));
    }

  if (s2)
    {
      get_s2_spec_stats (s2, s1->length);

      if (s2->n_indefinite_repeats > 1)
    {
      error (EXIT_FAILURE, 0,
         _("only one [c*] repeat construct may appear in string2"));
    }

      if (translating)
    {
      if (s2->has_equiv_class)
        {
          error (EXIT_FAILURE, 0,
             _("[=c=] expressions may not appear in string2 \
when translating"));
        }

      if (s1->length > s2->length)
        {
          if (!truncate_set1)
        {
          /* string2 must be non-empty unless --truncate-set1 is
             given or string1 is empty.  */

          if (s2->length == 0)
            error (EXIT_FAILURE, 0,
             _("when not truncating set1, string2 must be non-empty"));
          string2_extend (s1, s2);
        }
        }

      if (complement && s1->has_char_class
          && ! (s2->length == s1->length && homogeneous_spec_list (s2)))
        {
          error (EXIT_FAILURE, 0,
             _("when translating with complemented character classes,\
\nstring2 must map all characters in the domain to one"));
        }

      if (s2->has_restricted_char_class)
        {
          error (EXIT_FAILURE, 0,
             _("when translating, the only character classes that may \
appear in\nstring2 are `upper' and `lower'"));
        }
    }
      else
    /* Not translating.  */
    {
      if (s2->n_indefinite_repeats > 0)
        error (EXIT_FAILURE, 0,
           _("the [c*] construct may appear in string2 only \
when translating"));
    }
    }
}

/* Read buffers of SIZE bytes via the function READER (if READER is
   NULL, read from stdin) until EOF.  When non-NULL, READER is either
   read_and_delete or read_and_xlate.  After each buffer is read, it is
   processed and written to stdout.  The buffers are processed so that
   multiple consecutive occurrences of the same character in the input
   stream are replaced by a single occurrence of that character if the
   character is in the squeeze set.  */

static void
squeeze_filter (unsigned char *buf, size_t size, Filter reader)
{
  unsigned int char_to_squeeze = NOT_A_CHAR;
  size_t i = 0;
  size_t nr = 0;

  for (;;)
    {
      size_t begin;

      if (i >= nr)
    {
      if (reader == NULL)
        {
          nr = safe_read (0, (char *) buf, size);
          if (nr == SAFE_READ_ERROR)
        error (EXIT_FAILURE, errno, _("read error"));
        }
      else
        {
          nr = (*reader) (buf, size, NULL);
        }

      if (nr == 0)
        break;
      i = 0;
    }

      begin = i;

      if (char_to_squeeze == NOT_A_CHAR)
    {
      size_t out_len;
      /* Here, by being a little tricky, we can get a significant
         performance increase in most cases when the input is
         reasonably large.  Since tr will modify the input only
         if two consecutive (and identical) input characters are
         in the squeeze set, we can step by two through the data
         when searching for a character in the squeeze set.  This
         means there may be a little more work in a few cases and
         perhaps twice as much work in the worst cases where most
         of the input is removed by squeezing repeats.  But most
         uses of this functionality seem to remove less than 20-30%
         of the input.  */
      for (; i < nr && !in_squeeze_set[buf[i]]; i += 2)
        ;           /* empty */

      /* There is a special case when i == nr and we've just
         skipped a character (the last one in buf) that is in
         the squeeze set.  */
      if (i == nr && in_squeeze_set[buf[i - 1]])
        --i;

      if (i >= nr)
        out_len = nr - begin;
      else
        {
          char_to_squeeze = buf[i];
          /* We're about to output buf[begin..i].  */
          out_len = i - begin + 1;

          /* But since we stepped by 2 in the loop above,
             out_len may be one too large.  */
          if (i > 0 && buf[i - 1] == char_to_squeeze)
        --out_len;

          /* Advance i to the index of first character to be
             considered when looking for a char different from
             char_to_squeeze.  */
          ++i;
        }
      if (out_len > 0
          && fwrite ((char *) &buf[begin], 1, out_len, stdout) == 0)
        error (EXIT_FAILURE, errno, _("write error"));
    }

      if (char_to_squeeze != NOT_A_CHAR)
    {
      /* Advance i to index of first char != char_to_squeeze
         (or to nr if all the rest of the characters in this
         buffer are the same as char_to_squeeze).  */
      for (; i < nr && buf[i] == char_to_squeeze; i++)
        ;           /* empty */
      if (i < nr)
        char_to_squeeze = NOT_A_CHAR;
      /* If (i >= nr) we've squeezed the last character in this buffer.
         So now we have to read a new buffer and continue comparing
         characters against char_to_squeeze.  */
    }
    }
}

/* Read buffers of SIZE bytes from stdin until one is found that
   contains at least one character not in the delete set.  Store
   in the array BUF, all characters from that buffer that are not
   in the delete set, and return the number of characters saved
   or 0 upon EOF.  */

static size_t
read_and_delete (unsigned char *buf, size_t size, Filter not_used)
{
  size_t n_saved;
  static int hit_eof = 0;

  assert (not_used == NULL);

  if (hit_eof)
    return 0;

  /* This enclosing do-while loop is to make sure that
     we don't return zero (indicating EOF) when we've
     just deleted all the characters in a buffer.  */
  do
    {
      size_t i;
      size_t nr = safe_read (0, (char *) buf, size);

      if (nr == SAFE_READ_ERROR)
    error (EXIT_FAILURE, errno, _("read error"));
      if (nr == 0)
    {
      hit_eof = 1;
      return 0;
    }

      /* This first loop may be a waste of code, but gives much
         better performance when no characters are deleted in
         the beginning of a buffer.  It just avoids the copying
         of buf[i] into buf[n_saved] when it would be a NOP.  */

      for (i = 0; i < nr && !in_delete_set[buf[i]]; i++)
    /* empty */ ;
      n_saved = i;

      for (++i; i < nr; i++)
    if (!in_delete_set[buf[i]])
      buf[n_saved++] = buf[i];
    }
  while (n_saved == 0);

  return n_saved;
}

/* Read at most SIZE bytes from stdin into the array BUF.  Then
   perform the in-place and one-to-one mapping specified by the global
   array `xlate'.  Return the number of characters read, or 0 upon EOF.  */

static size_t
read_and_xlate (unsigned char *buf, size_t size, Filter not_used)
{
  size_t bytes_read = 0;
  static int hit_eof = 0;
  size_t i;

  assert (not_used == NULL);

  if (hit_eof)
    return 0;

  bytes_read = safe_read (0, (char *) buf, size);
  if (bytes_read == SAFE_READ_ERROR)
    error (EXIT_FAILURE, errno, _("read error"));
  if (bytes_read == 0)
    {
      hit_eof = 1;
      return 0;
    }

  for (i = 0; i < bytes_read; i++)
    buf[i] = xlate[buf[i]];

  return bytes_read;
}

/* Initialize a boolean membership set IN_SET with the character
   values obtained by traversing the linked list of constructs S
   using the function `get_next'.  If COMPLEMENT_THIS_SET is
   nonzero the resulting set is complemented.  */

static void
set_initialize (struct Spec_list *s, int complement_this_set, SET_TYPE *in_set)
{
  int c;
  size_t i;

  memset (in_set, 0, N_CHARS * sizeof (in_set[0]));
  s->state = BEGIN_STATE;
  while ((c = get_next (s, NULL)) != -1)
    in_set[c] = 1;
  if (complement_this_set)
    for (i = 0; i < N_CHARS; i++)
      in_set[i] = (!in_set[i]);
}

int
main (int argc, char **argv)
{
  int c;
  int non_option_args;
  struct Spec_list buf1, buf2;
  struct Spec_list *s1 = &buf1;
  struct Spec_list *s2 = &buf2;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  while ((c = getopt_long (argc, argv, "cdst", long_options, NULL)) != -1)
    {
      switch (c)
    {
    case 0:
      break;

    case 'c':
      complement = 1;
      break;

    case 'd':
      delete = 1;
      break;

    case 's':
      squeeze_repeats = 1;
      break;

    case 't':
      truncate_set1 = 1;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (2);
      break;
    }
    }

  posix_pedantic = (getenv ("POSIXLY_CORRECT") != NULL);

  non_option_args = argc - optind;
  translating = (non_option_args == 2 && !delete);

  /* Change this test if it is valid to give tr no options and
     no args at all.  POSIX doesn't specifically say anything
     either way, but it looks like they implied it's invalid
     by omission.  If you want to make tr do a slow imitation
     of `cat' use `tr a a'.  */
  if (non_option_args > 2)
    {
      error (0, 0, _("too many arguments"));
      usage (2);
    }

  if (!delete && !squeeze_repeats && non_option_args != 2)
    error (EXIT_FAILURE, 0, _("two strings must be given when translating"));

  if (delete && squeeze_repeats && non_option_args != 2)
    error (EXIT_FAILURE, 0, _("two strings must be given when both \
deleting and squeezing repeats"));

  /* If --delete is given without --squeeze-repeats, then
     only one string argument may be specified.  But POSIX
     says to ignore any string2 in this case, so if POSIXLY_CORRECT
     is set, pretend we never saw string2.  But I think
     this deserves a fatal error, so that's the default.  */
  if ((delete && !squeeze_repeats) && non_option_args != 1)
    {
      if (posix_pedantic && non_option_args == 2)
    --non_option_args;
      else
    error (EXIT_FAILURE, 0,
           _("only one string may be given when deleting \
without squeezing repeats"));
    }

  if (squeeze_repeats && non_option_args == 0)
    error (EXIT_FAILURE, 0,
       _("at least one string must be given when squeezing repeats"));

  spec_init (s1);
  if (parse_str ((unsigned char *) argv[optind], s1))
    exit (EXIT_FAILURE);

  if (non_option_args == 2)
    {
      spec_init (s2);
      if (parse_str ((unsigned char *) argv[optind + 1], s2))
    exit (EXIT_FAILURE);
    }
  else
    s2 = NULL;

  validate (s1, s2);

  /* Use binary I/O, since `tr' is sometimes used to transliterate
     non-printable characters, or characters which are stripped away
     by text-mode reads (like CR and ^Z).  */
  SET_BINARY2 (STDIN_FILENO, STDOUT_FILENO);

  if (squeeze_repeats && non_option_args == 1)
    {
      set_initialize (s1, complement, in_squeeze_set);
      squeeze_filter (io_buf, IO_BUF_SIZE, NULL);
    }
  else if (delete && non_option_args == 1)
    {
      size_t nr;

      set_initialize (s1, complement, in_delete_set);
      do
    {
      nr = read_and_delete (io_buf, IO_BUF_SIZE, NULL);
      if (nr > 0 && fwrite ((char *) io_buf, 1, nr, stdout) == 0)
        error (EXIT_FAILURE, errno, _("write error"));
    }
      while (nr > 0);
    }
  else if (squeeze_repeats && delete && non_option_args == 2)
    {
      set_initialize (s1, complement, in_delete_set);
      set_initialize (s2, 0, in_squeeze_set);
      squeeze_filter (io_buf, IO_BUF_SIZE, read_and_delete);
    }
  else if (translating)
    {
      if (complement)
    {
      int i;
      SET_TYPE *in_s1 = in_delete_set;

      set_initialize (s1, 0, in_s1);
      s2->state = BEGIN_STATE;
      for (i = 0; i < N_CHARS; i++)
        xlate[i] = i;
      for (i = 0; i < N_CHARS; i++)
        {
          if (!in_s1[i])
        {
          int ch = get_next (s2, NULL);
          assert (ch != -1 || truncate_set1);
          if (ch == -1)
            {
              /* This will happen when tr is invoked like e.g.
                 tr -cs A-Za-z0-9 '\012'.  */
              break;
            }
          xlate[i] = ch;
        }
        }
      assert (get_next (s2, NULL) == -1 || truncate_set1);
    }
      else
    {
      int c1, c2;
      int i;
      enum Upper_Lower_class class_s1;
      enum Upper_Lower_class class_s2;

      for (i = 0; i < N_CHARS; i++)
        xlate[i] = i;
      s1->state = BEGIN_STATE;
      s2->state = BEGIN_STATE;
      for (;;)
        {
          c1 = get_next (s1, &class_s1);
          c2 = get_next (s2, &class_s2);
          if (!class_ok[(int) class_s1][(int) class_s2])
        error (EXIT_FAILURE, 0,
               _("misaligned [:upper:] and/or [:lower:] construct"));

          if (class_s1 == UL_LOWER && class_s2 == UL_UPPER)
        {
          for (i = 0; i < N_CHARS; i++)
            if (ISLOWER (i))
              xlate[i] = toupper (i);
        }
          else if (class_s1 == UL_UPPER && class_s2 == UL_LOWER)
        {
          for (i = 0; i < N_CHARS; i++)
            if (ISUPPER (i))
              xlate[i] = tolower (i);
        }
          else if ((class_s1 == UL_LOWER && class_s2 == UL_LOWER)
               || (class_s1 == UL_UPPER && class_s2 == UL_UPPER))
        {
          /* By default, GNU tr permits the identity mappings: from
             [:upper:] to [:upper:] and [:lower:] to [:lower:].  But
             when POSIXLY_CORRECT is set, those evoke diagnostics.  */
          if (posix_pedantic)
            {
              error (EXIT_FAILURE, 0,
                 _("\
invalid identity mapping;  when translating, any [:lower:] or [:upper:]\n\
construct in string1 must be aligned with a corresponding construct\n\
([:upper:] or [:lower:], respectively) in string2"));
            }
        }
          else
        {
          /* The following should have been checked by validate...  */
          if (c1 == -1 || c2 == -1)
            break;
          xlate[c1] = c2;
        }
        }
      assert (c1 == -1 || truncate_set1);
    }
      if (squeeze_repeats)
    {
      set_initialize (s2, 0, in_squeeze_set);
      squeeze_filter (io_buf, IO_BUF_SIZE, read_and_xlate);
    }
      else
    {
      size_t bytes_read;

      do
        {
          bytes_read = read_and_xlate (io_buf, IO_BUF_SIZE, NULL);
          if (bytes_read > 0
          && fwrite ((char *) io_buf, 1, bytes_read, stdout) == 0)
        error (EXIT_FAILURE, errno, _("write error"));
        }
      while (bytes_read > 0);
    }
    }

  if (close (STDIN_FILENO) != 0)
    error (EXIT_FAILURE, errno, _("standard input"));

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/tsort.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/tsort.c used in: Step 935
/* tsort - topological sort.
   Copyright (C) 1998-2002 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.  */

/* Written by Mark Kettenis <kettenis@phys.uva.nl>.  */

/* The topological sort is done according to Algorithm T (Topological
   sort) in Donald E. Knuth, The Art of Computer Programming, Volume
   1/Fundamental Algorithms, page 262.  */

#include <config.h>

#include <stdio.h>
#include <assert.h>
#include <getopt.h>
#include <sys/types.h>

#include "system.h"
#include "closeout.h"
#include "long-options.h"
#include "error.h"
#include "readtokens.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "tsort"

#define AUTHORS "Mark Kettenis"

/* Token delimiters when reading from a file.  */
#define DELIM " \t\n"

/* Members of the list of successors.  */
struct successor
{
  struct item *suc;
  struct successor *next;
};

/* Each string is held in core as the head of a list of successors.  */
struct item
{
  const char *str;
  struct item *left, *right;
  int balance;
  int count;
  struct item *qlink;
  struct successor *top;
};

/* The name this program was run with. */
char *program_name;

/* Nonzero if any of the input files are the standard input. */
static int have_read_stdin;

/* The error code to return to the system. */
static int exit_status;

/* The head of the sorted list.  */
static struct item *head = NULL;

/* The tail of the list of `zeros', strings that have no predecessors.  */
static struct item *zeros = NULL;

/* Used for loop detection.  */
static struct item *loop = NULL;

/* The number of strings to sort.  */
static int n_strings = 0;

static struct option const long_options[] =
{
  { NULL, 0, NULL, 0}
};


void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION] [FILE]\n\
Write totally ordered list consistent with the partial ordering in FILE.\n\
With no FILE, or when FILE is -, read standard input.\n\
\n\
"), program_name);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }

  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Create a new item/node for STR.  */
static struct item *
new_item (const char *str)
{
  struct item *k = xmalloc (sizeof (struct item));

  k->str = (str ? xstrdup (str): NULL);
  k->left = k->right = NULL;
  k->balance = 0;

  /* T1. Initialize (COUNT[k] <- 0 and TOP[k] <- ^).  */
  k->count = 0;
  k->qlink = NULL;
  k->top = NULL;

  return k;
}

/* Search binary tree rooted at *ROOT for STR.  Allocate a new tree if
   *ROOT is NULL.  Insert a node/item for STR if not found.  Return
   the node/item found/created for STR.

   This is done according to Algorithm A (Balanced tree search and
   insertion) in Donald E. Knuth, The Art of Computer Programming,
   Volume 3/Searching and Sorting, pages 455--457.  */

static struct item *
search_item (struct item *root, const char *str)
{
  struct item *p, *q, *r, *s, *t;
  int a;

  assert (root);

  /* Make sure the tree is not empty, since that is what the algorithm
     below expects.  */
  if (root->right == NULL)
    return (root->right = new_item (str));

  /* A1. Initialize.  */
  t = root;
  s = p = root->right;

  for (;;)
    {
      /* A2. Compare.  */
      a = strcmp (str, p->str);
      if (a == 0)
    return p;

      /* A3 & A4.  Move left & right.  */
      if (a < 0)
    q = p->left;
      else
    q = p->right;

      if (q == NULL)
    {
      /* A5. Insert.  */
      q = new_item (str);

      /* A3 & A4.  (continued).  */
      if (a < 0)
        p->left = q;
      else
        p->right = q;

      /* A6. Adjust balance factors.  */
      assert (!STREQ (str, s->str));
      if (strcmp (str, s->str) < 0)
        {
          r = p = s->left;
          a = -1;
        }
      else
        {
          r = p = s->right;
          a = 1;
        }

      while (p != q)
        {
          assert (!STREQ (str, p->str));
          if (strcmp (str, p->str) < 0)
        {
          p->balance = -1;
          p = p->left;
        }
          else
        {
          p->balance = 1;
          p = p->right;
        }
        }

      /* A7. Balancing act.  */
      if (s->balance == 0 || s->balance == -a)
        {
          s->balance += a;
          return q;
        }

      if (r->balance == a)
        {
          /* A8. Single Rotation.  */
          p = r;
          if (a < 0)
        {
          s->left = r->right;
          r->right = s;
        }
          else
        {
          s->right = r->left;
          r->left = s;
        }
          s->balance = r->balance = 0;
        }
      else
        {
          /* A9. Double rotation.  */
          if (a < 0)
        {
          p = r->right;
          r->right = p->left;
          p->left = r;
          s->left = p->right;
          p->right = s;
        }
          else
        {
          p = r->left;
          r->left = p->right;
          p->right = r;
          s->right = p->left;
          p->left = s;
        }

          s->balance = 0;
          r->balance = 0;
          if (p->balance == a)
        s->balance = -a;
          else if (p->balance == -a)
        r->balance = a;
          p->balance = 0;
        }

      /* A10. Finishing touch.  */
      if (s == t->right)
        t->right = p;
      else
        t->left = p;

      return q;
    }

      /* A3 & A4.  (continued).  */
      if (q->balance)
    {
      t = p;
      s = q;
    }

      p = q;
    }

  /* NOTREACHED */
}

/* Record the fact that J precedes K.  */

static void
record_relation (struct item *j, struct item *k)
{
  struct successor *p;

  if (!STREQ (j->str, k->str))
    {
      k->count++;
      p = xmalloc (sizeof (struct successor));
      p->suc = k;
      p->next = j->top;
      j->top = p;
    }
}

static int
count_items (struct item *unused ATTRIBUTE_UNUSED)
{
  n_strings++;
  return 0;
}

static int
scan_zeros (struct item *k)
{
  /* Ignore strings that have already been printed.  */
  if (k->count == 0 && k->str)
    {
      if (head == NULL)
    head = k;
      else
    zeros->qlink = k;

      zeros = k;
    }

  return 0;
}

/* Try to detect the loop.  If we have detected that K is part of a
   loop, print the loop on standard error, remove a relation to break
   the loop, and return non-zero.

   The loop detection strategy is as follows: Realise that what we're
   dealing with is essentially a directed graph.  If we find an item
   that is part of a graph that contains a cycle we traverse the graph
   in backwards direction.  In general there is no unique way to do
   this, but that is no problem.  If we encounter an item that we have
   encountered before, we know that we've found a cycle.  All we have
   to do now is retrace our steps, printing out the items until we
   encounter that item again.  (This is not necessarily the item that
   we started from originally.)  Since the order in which the items
   are stored in the tree is not related to the specified partial
   ordering, we may need to walk the tree several times before the
   loop has completely been constructed.  If the loop was found, the
   global variable LOOP will be NULL.  */

static int
detect_loop (struct item *k)
{
  if (k->count > 0)
    {
      /* K does not have to be part of a cycle.  It is however part of
     a graph that contains a cycle.  */

      if (loop == NULL)
    /* Start traversing the graph at K.  */
    loop = k;
      else
    {
      struct successor **p = &k->top;

      while (*p)
        {
          if ((*p)->suc == loop)
        {
          if (k->qlink)
            {
              /* We have found a loop.  Retrace our steps.  */
              while (loop)
            {
              struct item *tmp = loop->qlink;

              fprintf (stderr, "%s: %s\n", program_name,
                   loop->str);

              /* Until we encounter K again.  */
              if (loop == k)
                {
                  /* Remove relation.  */
                  (*p)->suc->count--;
                  *p = (*p)->next;
                  break;
                }

              /* Tidy things up since we might have to
                             detect another loop.  */
              loop->qlink = NULL;
              loop = tmp;
            }

              while (loop)
            {
              struct item *tmp = loop->qlink;

              loop->qlink = NULL;
              loop = tmp;
            }

              /* Since we have found the loop, stop walking
                         the tree.  */
              return 1;
            }
          else
            {
              k->qlink = loop;
              loop = k;
              break;
            }
        }

          p = &(*p)->next;
        }
    }
    }

  return 0;
}

/* Recurse (sub)tree rooted at ROOT, calling ACTION for each node.
   Stop when ACTION returns non-zero.  */

static int
recurse_tree (struct item *root, int (*action) (struct item *))
{
  if (root->left == NULL && root->right == NULL)
    return (*action) (root);
  else
    {
      if (root->left != NULL)
    if (recurse_tree (root->left, action))
      return 1;
      if ((*action) (root))
    return 1;
      if (root->right != NULL)
    if (recurse_tree (root->right, action))
      return 1;
    }

  return 0;
}

/* Walk the tree specified by the head ROOT, calling ACTION for
   each node.  */

static void
walk_tree (struct item *root, int (*action) (struct item *))
{
  if (root->right)
    recurse_tree (root->right, action);
}

/* Do a topological sort on FILE.   */

static void
tsort (const char *file)
{
  struct item *root;
  struct item *j = NULL;
  struct item *k = NULL;
  register FILE *fp;
  token_buffer tokenbuffer;

  /* Intialize the head of the tree will hold the strings we're sorting.  */
  root = new_item (NULL);

  if (STREQ (file, "-"))
    {
      fp = stdin;
      have_read_stdin = 1;
    }
  else
    {
      fp = fopen (file, "r");
      if (fp == NULL)
    error (EXIT_FAILURE, errno, "%s", file);
    }

  init_tokenbuffer (&tokenbuffer);

  while (1)
    {
      long int len;

      /* T2. Next Relation.  */
      len = readtoken (fp, DELIM, sizeof (DELIM) - 1, &tokenbuffer);
      if (len < 0)
    break;

      assert (len != 0);

      k = search_item (root, tokenbuffer.buffer);
      if (j)
    {
      /* T3. Record the relation.  */
      record_relation (j, k);
      k = NULL;
    }

      j = k;
    }

  /* T1. Initialize (N <- n).  */
  walk_tree (root, count_items);

  while (n_strings > 0)
    {
      /* T4. Scan for zeros.  */
      walk_tree (root, scan_zeros);

      while (head)
    {
      struct successor *p = head->top;

      /* T5. Output front of queue.  */
      printf ("%s\n", head->str);
      head->str = NULL; /* Avoid printing the same string twice.  */
      n_strings--;

      /* T6. Erase relations.  */
      while (p)
        {
          p->suc->count--;
          if (p->suc->count == 0)
        {
          zeros->qlink = p->suc;
          zeros = p->suc;
        }

          p = p->next;
        }

      /* T7. Remove from queue.  */
      head = head->qlink;
    }

      /* T8.  End of process.  */
      assert (n_strings >= 0);
      if (n_strings > 0)
    {
      /* The input contains a loop.  */
      error (0, 0, _("%s: input contains a loop:"),
         (have_read_stdin ? "-" : file));
      exit_status = 1;

      /* Print the loop and remove a relation to break it.  */
      do
        walk_tree (root, detect_loop);
      while (loop);
    }
    }
}

int
main (int argc, char **argv)
{
  int opt;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  exit_status = 0;

  parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE, VERSION,
              AUTHORS, usage);

  while ((opt = getopt_long (argc, argv, "", long_options, NULL)) != -1)
    switch (opt)
      {
      case 0:           /* long option */
    break;
      default:
    usage (EXIT_FAILURE);
      }

  have_read_stdin = 0;

  if (optind + 1 < argc)
    {
      error (0, 0, _("only one argument may be specified"));
      usage (EXIT_FAILURE);
    }

  if (optind < argc)
    tsort (argv[optind]);
  else
    tsort ("-");

  if (have_read_stdin && fclose (stdin) == EOF)
    error (EXIT_FAILURE, errno, _("standard input"));

  exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/unexpand.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/unexpand.c used in: Step 937
/* unexpand - convert spaces to tabs
   Copyright (C) 89, 91, 1995-2003 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.  */

/* By default, convert only maximal strings of initial blanks and tabs
   into tabs.
   Preserves backspace characters in the output; they decrement the
   column count for tab calculations.
   The default action is equivalent to -8.

   Options:
   --tabs=tab1[,tab2[,...]]
   -t tab1[,tab2[,...]]
   -tab1[,tab2[,...]]   If only one tab stop is given, set the tabs tab1
            spaces apart instead of the default 8.  Otherwise,
            set the tabs at columns tab1, tab2, etc. (numbered from
            0); replace any tabs beyond the tabstops given with
            single spaces.
   --all
   -a           Use tabs wherever they would replace 2 or more spaces,
            not just at the beginnings of lines.

   David MacKenzie <djm@gnu.ai.mit.edu> */

#include <config.h>

#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
#include "closeout.h"
#include "error.h"
#include "posixver.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "unexpand"

#define AUTHORS "David MacKenzie"

/* The number of bytes added at a time to the amount of memory
   allocated for the output line. */
#define OUTPUT_BLOCK 256

/* The number of bytes added at a time to the amount of memory
   allocated for the list of tabstops. */
#define TABLIST_BLOCK 256

/* A sentinel value that's placed at the end of the list of tab stops.
   This value must be a large number, but not so large that adding the
   length of a line to it would cause the column variable to overflow.  */
#define TAB_STOP_SENTINEL INT_MAX

/* The name this program was run with. */
char *program_name;

/* If nonzero, convert blanks even after nonblank characters have been
   read on the line. */
static int convert_entire_line;

/* If nonzero, the size of all tab stops.  If zero, use `tab_list' instead. */
static int tab_size;

/* Array of the explicit column numbers of the tab stops;
   after `tab_list' is exhausted, the rest of the line is printed
   unchanged.  The first column is column 0. */
static int *tab_list;

/* The index of the first invalid element of `tab_list',
   where the next element can be added. */
static int first_free_tab;

/* Null-terminated array of input filenames. */
static char **file_list;

/* Default for `file_list' if no files are given on the command line. */
static char *stdin_argv[] =
{
  "-", NULL
};

/* Nonzero if we have ever read standard input. */
static int have_read_stdin;

/* Status to return to the system. */
static int exit_status;

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  CONVERT_FIRST_ONLY_OPTION = CHAR_MAX + 1
};

static struct option const longopts[] =
{
  {"tabs", required_argument, NULL, 't'},
  {"all", no_argument, NULL, 'a'},
  {"first-only", no_argument, NULL, CONVERT_FIRST_ONLY_OPTION},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

/* Add tab stop TABVAL to the end of `tab_list', except
   if TABVAL is -1, do nothing. */

static void
add_tabstop (int tabval)
{
  if (tabval == -1)
    return;
  if (first_free_tab % TABLIST_BLOCK == 0)
    tab_list = (int *) xrealloc ((char *) tab_list,
                 first_free_tab + TABLIST_BLOCK);
  tab_list[first_free_tab++] = tabval;
}

/* Add the comma or blank separated list of tabstops STOPS
   to the list of tabstops. */

static void
parse_tabstops (const char *stops)
{
  int tabval = -1;

  for (; *stops; stops++)
    {
      if (*stops == ',' || ISBLANK (*stops))
    {
      add_tabstop (tabval);
      tabval = -1;
    }
      else if (ISDIGIT (*stops))
    {
      if (tabval == -1)
        tabval = 0;
      tabval = tabval * 10 + *stops - '0';
    }
      else
    error (EXIT_FAILURE, 0, _("tab size contains an invalid character"));
    }

  add_tabstop (tabval);
}

/* Check that the list of tabstops TABS, with ENTRIES entries,
   contains only nonzero, ascending values. */

static void
validate_tabstops (const int *tabs, int entries)
{
  int prev_tab = 0;
  int i;

  for (i = 0; i < entries; i++)
    {
      if (tabs[i] == 0)
    error (EXIT_FAILURE, 0, _("tab size cannot be 0"));
      if (tabs[i] <= prev_tab)
    error (EXIT_FAILURE, 0, _("tab sizes must be ascending"));
      prev_tab = tabs[i];
    }
}

/* Close the old stream pointer FP if it is non-NULL,
   and return a new one opened to read the next input file.
   Open a filename of `-' as the standard input.
   Return NULL if there are no more input files.  */

static FILE *
next_file (FILE *fp)
{
  static char *prev_file;
  char *file;

  if (fp)
    {
      if (ferror (fp))
    {
      error (0, errno, "%s", prev_file);
      exit_status = 1;
    }
      if (fp == stdin)
    clearerr (fp);      /* Also clear EOF. */
      else if (fclose (fp) == EOF)
    {
      error (0, errno, "%s", prev_file);
      exit_status = 1;
    }
    }

  while ((file = *file_list++) != NULL)
    {
      if (file[0] == '-' && file[1] == '\0')
    {
      have_read_stdin = 1;
      prev_file = file;
      return stdin;
    }
      fp = fopen (file, "r");
      if (fp)
    {
      prev_file = file;
      return fp;
    }
      error (0, errno, "%s", file);
      exit_status = 1;
    }
  return NULL;
}

/* Change spaces to tabs, writing to stdout.
   Read each file in `file_list', in order. */

static void
unexpand (void)
{
  FILE *fp;         /* Input stream. */
  int c;            /* Each input character. */
  /* Index in `tab_list' of next tabstop: */
  int tab_index = 0;        /* For calculating width of pending tabs. */
  int print_tab_index = 0;  /* For printing as many tabs as possible. */
  unsigned int column = 0;  /* Column on screen of next char. */
  int next_tab_column;      /* Column the next tab stop is on. */
  int convert = 1;      /* If nonzero, perform translations. */
  unsigned int pending = 0; /* Pending columns of blanks. */

  fp = next_file ((FILE *) NULL);
  if (fp == NULL)
    return;

  /* Binary I/O will preserve the original EOL style (DOS/Unix) of files.  */
  SET_BINARY2 (fileno (fp), STDOUT_FILENO);

  for (;;)
    {
      c = getc (fp);

      if (c == ' ' && convert && column < TAB_STOP_SENTINEL)
    {
      ++pending;
      ++column;
    }
      else if (c == '\t' && convert)
    {
      if (tab_size == 0)
        {
          /* Do not let tab_index == first_free_tab;
         stop when it is 1 less. */
          while (tab_index < first_free_tab - 1
             && column >= tab_list[tab_index])
        tab_index++;
          next_tab_column = tab_list[tab_index];
          if (tab_index < first_free_tab - 1)
        tab_index++;
          if (column >= next_tab_column)
        {
          convert = 0;  /* Ran out of tab stops. */
          goto flush_pend;
        }
        }
      else
        {
          next_tab_column = column + tab_size - column % tab_size;
        }
      pending += next_tab_column - column;
      column = next_tab_column;
    }
      else
    {
    flush_pend:
      /* Flush pending spaces.  Print as many tabs as possible,
         then print the rest as spaces. */
      if (pending == 1)
        {
          putchar (' ');
          pending = 0;
        }
      column -= pending;
      while (pending > 0)
        {
          if (tab_size == 0)
        {
          /* Do not let print_tab_index == first_free_tab;
             stop when it is 1 less. */
          while (print_tab_index < first_free_tab - 1
             && column >= tab_list[print_tab_index])
            print_tab_index++;
          next_tab_column = tab_list[print_tab_index];
          if (print_tab_index < first_free_tab - 1)
            print_tab_index++;
        }
          else
        {
          next_tab_column = column + tab_size - column % tab_size;
        }
          if (next_tab_column - column <= pending)
        {
          putchar ('\t');
          pending -= next_tab_column - column;
          column = next_tab_column;
        }
          else
        {
          --print_tab_index;
          column += pending;
          while (pending != 0)
            {
              putchar (' ');
              pending--;
            }
        }
        }

      if (c == EOF)
        {
          fp = next_file (fp);
          if (fp == NULL)
        break;      /* No more files. */
          else
        {
          SET_BINARY2 (fileno (fp), STDOUT_FILENO);
          continue;
        }
        }

      if (convert)
        {
          if (c == '\b')
        {
          if (column > 0)
            --column;
        }
          else
        {
          ++column;
          if (convert_entire_line == 0)
            convert = 0;
        }
        }

      putchar (c);

      if (c == '\n')
        {
          tab_index = print_tab_index = 0;
          column = pending = 0;
          convert = 1;
        }
    }
    }
}

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
          program_name);
      fputs (_("\
Convert spaces in each FILE to tabs, writing to standard output.\n\
With no FILE, or when FILE is -, read standard input.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -a, --all        convert all whitespace, instead of just initial whitespace\n\
      --first-only convert only leading sequences of whitespace (overrides -a)\n\
  -t, --tabs=N     have tabs N characters apart instead of 8 (enables -a)\n\
  -t, --tabs=LIST  use comma separated LIST of tab positions (enables -a)\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

int
main (int argc, char **argv)
{
  int tabval = -1;      /* Value of tabstop being read, or -1. */
  int c;            /* Option character. */

  /* If nonzero, cancel the effect of any -a (explicit or implicit in -t),
     so that only leading white space will be considered.  */
  int convert_first_only = 0;

  bool obsolete_tablist = false;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  have_read_stdin = 0;
  exit_status = 0;
  convert_entire_line = 0;
  tab_list = NULL;
  first_free_tab = 0;

  while ((c = getopt_long (argc, argv, ",0123456789at:", longopts, NULL))
     != -1)
    {
      switch (c)
    {
    case 0:
      break;

    case '?':
      usage (EXIT_FAILURE);
    case 'a':
      convert_entire_line = 1;
      break;
    case 't':
      convert_entire_line = 1;
      parse_tabstops (optarg);
      break;
    case CONVERT_FIRST_ONLY_OPTION:
      convert_first_only = 1;
      break;
    case ',':
      add_tabstop (tabval);
      tabval = -1;
      obsolete_tablist = true;
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      if (tabval == -1)
        tabval = 0;
      tabval = tabval * 10 + c - '0';
      obsolete_tablist = true;
      break;
    }
    }

  if (obsolete_tablist && 200112 <= posix2_version ())
    {
      error (0, 0,
         _("`-LIST' option is obsolete; use `--first-only -t LIST'"));
      usage (EXIT_FAILURE);
    }

  if (convert_first_only)
    convert_entire_line = 0;

  add_tabstop (tabval);

  validate_tabstops (tab_list, first_free_tab);

  if (first_free_tab == 0)
    tab_size = 8;
  else if (first_free_tab == 1)
    tab_size = tab_list[0];
  else
    {
      /* Append a sentinel to the list of tab stop indices.  */
      add_tabstop (TAB_STOP_SENTINEL);
      tab_size = 0;
    }

  if (optind == argc)
    file_list = stdin_argv;
  else
    file_list = &argv[optind];

  unexpand ();

  if (have_read_stdin && fclose (stdin) == EOF)
    error (EXIT_FAILURE, errno, "-");
  exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/unlink.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/unlink.c used in: Step 941
/* unlink utility for GNU.
   Copyright (C) 2001-2002 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.  */

/* Written by Michael Stone */

/* Implementation overview:

   Simply call the system 'unlink' function */

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include <assert.h>

#include "system.h"
#include "error.h"
#include "long-options.h"
#include "quote.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "unlink"

#define AUTHORS "Michael Stone"

/* Name this program was run with.  */
char *program_name;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s FILE\n\
  or:  %s OPTION\n"), program_name, program_name);
      fputs (_("Call the unlink function to remove the specified FILE.\n\n"),
         stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  /* Don't recognize --help or --version if POSIXLY_CORRECT is set.  */
  if (getenv ("POSIXLY_CORRECT") == NULL)
    parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
            AUTHORS, usage);

  /* The above handles --help and --version.
     Since there is no other invocation of getopt, handle `--' here.  */
  if (1 < argc && STREQ (argv[1], "--"))
    {
      --argc;
      ++argv;
    }

  if (argc < 2)
    {
      error (0, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }

  if (2 < argc)
    {
      error (0, 0, _("too many arguments"));
      usage (EXIT_FAILURE);
    }

  if (unlink (argv[1]) != 0)
    error (EXIT_FAILURE, errno, _("cannot unlink %s"), quote (argv[1]));

  exit (EXIT_SUCCESS);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/wc.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/wc.c used in: Step 943
/* wc - print the number of bytes, words, and lines in files
   Copyright (C) 85, 91, 1995-2002 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.  */

/* Written by Paul Rubin, phr@ocf.berkeley.edu
   and David MacKenzie, djm@gnu.ai.mit.edu. */


#include <config.h>

#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>

/* Get mbstate_t, mbrtowc(), wcwidth().  */
#if HAVE_WCHAR_H
# include <wchar.h>
#endif

/* Get iswprint(), iswspace().  */
#if HAVE_WCTYPE_H
# include <wctype.h>
#endif
#if !defined iswprint && !HAVE_ISWPRINT
# define iswprint(wc) 1
#endif
#if !defined iswspace && !HAVE_ISWSPACE
# define iswspace(wc) \
    ((wc) == (unsigned char) (wc) && ISSPACE ((unsigned char) (wc)))
#endif

/* Include this after wctype.h so that we `#undef' ISPRINT
   (from Solaris's euc.h, from widec.h, from wctype.h) before
   redefining and using it. */
#include "system.h"

#include "closeout.h"
#include "error.h"
#include "inttostr.h"
#include "safe-read.h"

/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t.  */
#if HAVE_MBRTOWC && defined mbstate_t
# define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0)
#endif

#ifndef HAVE_DECL_WCWIDTH
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_WCWIDTH
extern int wcwidth ();
#endif

/* If wcwidth() doesn't exist, assume all printable characters have
   width 1.  */
#if !defined wcwidth && !HAVE_WCWIDTH
# define wcwidth(wc) ((wc) == 0 ? 0 : iswprint (wc) ? 1 : -1)
#endif

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "wc"

#define AUTHORS N_ ("Paul Rubin and David MacKenzie")

/* Size of atomic reads. */
#define BUFFER_SIZE (16 * 1024)

/* The name this program was run with. */
char *program_name;

/* Cumulative number of lines, words, chars and bytes in all files so far.
   max_line_length is the maximum over all files processed so far.  */
static uintmax_t total_lines;
static uintmax_t total_words;
static uintmax_t total_chars;
static uintmax_t total_bytes;
static uintmax_t max_line_length;

/* Which counts to print. */
static int print_lines, print_words, print_chars, print_bytes;
static int print_linelength;

/* Nonzero if we have ever read the standard input. */
static int have_read_stdin;

/* The error code to return to the system. */
static int exit_status;

/* If nonzero, do not line up columns but instead separate numbers by
   a single space as specified in Single Unix Specification and POSIX. */
static int posixly_correct;

static struct option const longopts[] =
{
  {"bytes", no_argument, NULL, 'c'},
  {"chars", no_argument, NULL, 'm'},
  {"lines", no_argument, NULL, 'l'},
  {"words", no_argument, NULL, 'w'},
  {"max-line-length", no_argument, NULL, 'L'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
          program_name);
      fputs (_("\
Print byte, word, and newline counts for each FILE, and a total line if\n\
more than one FILE is specified.  With no FILE, or when FILE is -,\n\
read standard input.\n\
  -c, --bytes            print the byte counts\n\
  -m, --chars            print the character counts\n\
  -l, --lines            print the newline counts\n\
"), stdout);
      fputs (_("\
  -L, --max-line-length  print the length of the longest line\n\
  -w, --words            print the word counts\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

static void
write_counts (uintmax_t lines,
          uintmax_t words,
          uintmax_t chars,
          uintmax_t bytes,
          uintmax_t linelength,
          const char *file)
{
  char buf[INT_BUFSIZE_BOUND (uintmax_t)];
  char const *space = "";
  char const *format_int = (posixly_correct ? "%s" : "%7s");
  char const *format_sp_int = (posixly_correct ? "%s%s" : "%s%7s");

  if (print_lines)
    {
      printf (format_int, umaxtostr (lines, buf));
      space = " ";
    }
  if (print_words)
    {
      printf (format_sp_int, space, umaxtostr (words, buf));
      space = " ";
    }
  if (print_chars)
    {
      printf (format_sp_int, space, umaxtostr (chars, buf));
      space = " ";
    }
  if (print_bytes)
    {
      printf (format_sp_int, space, umaxtostr (bytes, buf));
      space = " ";
    }
  if (print_linelength)
    {
      printf (format_sp_int, space, umaxtostr (linelength, buf));
    }
  if (*file)
    printf (" %s", file);
  putchar ('\n');
}

static void
wc (int fd, const char *file)
{
  char buf[BUFFER_SIZE + 1];
  size_t bytes_read;
  uintmax_t lines, words, chars, bytes, linelength;
  int count_bytes, count_chars, count_complicated;

  lines = words = chars = bytes = linelength = 0;

  /* If in the current locale, chars are equivalent to bytes, we prefer
     counting bytes, because that's easier.  */
#if HAVE_MBRTOWC && (MB_LEN_MAX > 1)
  if (MB_CUR_MAX > 1)
    {
      count_bytes = print_bytes;
      count_chars = print_chars;
    }
  else
#endif
    {
      count_bytes = print_bytes + print_chars;
      count_chars = 0;
    }
  count_complicated = print_words + print_linelength;

  /* We need binary input, since `wc' relies on `lseek' and byte counts.  */
  SET_BINARY (fd);

  /* When counting only bytes, save some line- and word-counting
     overhead.  If FD is a `regular' Unix file, using lseek is enough
     to get its `size' in bytes.  Otherwise, read blocks of BUFFER_SIZE
     bytes at a time until EOF.  Note that the `size' (number of bytes)
     that wc reports is smaller than stats.st_size when the file is not
     positioned at its beginning.  That's why the lseek calls below are
     necessary.  For example the command
     `(dd ibs=99k skip=1 count=0; ./wc -c) < /etc/group'
     should make wc report `0' bytes.  */

  if (count_bytes && !count_chars && !print_lines && !count_complicated)
    {
      off_t current_pos, end_pos;
      struct stat stats;

      if (fstat (fd, &stats) == 0 && S_ISREG (stats.st_mode)
      && (current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) != -1
      && (end_pos = lseek (fd, (off_t) 0, SEEK_END)) != -1)
    {
      off_t diff;
      /* Be careful here.  The current position may actually be
         beyond the end of the file.  As in the example above.  */
      bytes = (diff = end_pos - current_pos) < 0 ? 0 : diff;
    }
      else
    {
      while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
        {
          if (bytes_read == SAFE_READ_ERROR)
        {
          error (0, errno, "%s", file);
          exit_status = 1;
          break;
        }
          bytes += bytes_read;
        }
    }
    }
  else if (!count_chars && !count_complicated)
    {
      /* Use a separate loop when counting only lines or lines and bytes --
     but not chars or words.  */
      while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
    {
      register char *p = buf;

      if (bytes_read == SAFE_READ_ERROR)
        {
          error (0, errno, "%s", file);
          exit_status = 1;
          break;
        }

      while ((p = memchr (p, '\n', (buf + bytes_read) - p)))
        {
          ++p;
          ++lines;
        }
      bytes += bytes_read;
    }
    }
#if HAVE_MBRTOWC && (MB_LEN_MAX > 1)
# define SUPPORT_OLD_MBRTOWC 1
  else if (MB_CUR_MAX > 1)
    {
      int in_word = 0;
      uintmax_t linepos = 0;
      mbstate_t state;
      uintmax_t last_error_line = 0;
      int last_error_errno = 0;
# if SUPPORT_OLD_MBRTOWC
      /* Back-up the state before each multibyte character conversion and
     move the last incomplete character of the buffer to the front
     of the buffer.  This is needed because we don't know whether
     the `mbrtowc' function updates the state when it returns -2, -
     this is the ISO C 99 and glibc-2.2 behaviour - or not - amended
     ANSI C, glibc-2.1 and Solaris 2.7 behaviour.  We don't have an
     autoconf test for this, yet.  */
      size_t prev = 0; /* number of bytes carried over from previous round */
# else
      const size_t prev = 0;
# endif

      memset (&state, 0, sizeof (mbstate_t));
      while ((bytes_read = safe_read (fd, buf + prev, BUFFER_SIZE - prev)) > 0)
    {
      const char *p;
# if SUPPORT_OLD_MBRTOWC
      mbstate_t backup_state;
# endif
      if (bytes_read == SAFE_READ_ERROR)
        {
          error (0, errno, "%s", file);
          exit_status = 1;
          break;
        }

      bytes += bytes_read;
      p = buf;
      bytes_read += prev;
      do
        {
          wchar_t wide_char;
          size_t n;

# if SUPPORT_OLD_MBRTOWC
          backup_state = state;
# endif
          n = mbrtowc (&wide_char, p, bytes_read, &state);
          if (n == (size_t) -2)
        {
# if SUPPORT_OLD_MBRTOWC
          state = backup_state;
# endif
          break;
        }
          if (n == (size_t) -1)
        {
          /* Signal repeated errors only once per line.  */
          if (!(lines + 1 == last_error_line
            && errno == last_error_errno))
            {
              char line_number_buf[INT_BUFSIZE_BOUND (uintmax_t)];
              last_error_line = lines + 1;
              last_error_errno = errno;
              error (0, errno, "%s:%s", file,
                 umaxtostr (last_error_line, line_number_buf));
            }
          p++;
          bytes_read--;
        }
          else
        {
          if (n == 0)
            {
              wide_char = 0;
              n = 1;
            }
          p += n;
          bytes_read -= n;
          chars++;
          switch (wide_char)
            {
            case '\n':
              lines++;
              /* Fall through. */
            case '\r':
            case '\f':
              if (linepos > linelength)
            linelength = linepos;
              linepos = 0;
              goto mb_word_separator;
            case '\t':
              linepos += 8 - (linepos % 8);
              goto mb_word_separator;
            case ' ':
              linepos++;
              /* Fall through. */
            case '\v':
            mb_word_separator:
              if (in_word)
            {
              in_word = 0;
              words++;
            }
              break;
            default:
              if (iswprint (wide_char))
            {
              int width = wcwidth (wide_char);
              if (width > 0)
                linepos += width;
              if (iswspace (wide_char))
                goto mb_word_separator;
              in_word = 1;
            }
              break;
            }
        }
        }
      while (bytes_read > 0);

# if SUPPORT_OLD_MBRTOWC
      if (bytes_read > 0)
        {
          if (bytes_read == BUFFER_SIZE)
        {
          /* Encountered a very long redundant shift sequence.  */
          p++;
          bytes_read--;
        }
          memmove (buf, p, bytes_read);
        }
      prev = bytes_read;
# endif
    }
      if (linepos > linelength)
    linelength = linepos;
      if (in_word)
    words++;
    }
#endif
  else
    {
      int in_word = 0;
      uintmax_t linepos = 0;

      while ((bytes_read = safe_read (fd, buf, BUFFER_SIZE)) > 0)
    {
      const char *p = buf;
      if (bytes_read == SAFE_READ_ERROR)
        {
          error (0, errno, "%s", file);
          exit_status = 1;
          break;
        }

      bytes += bytes_read;
      do
        {
          switch (*p++)
        {
        case '\n':
          lines++;
          /* Fall through. */
        case '\r':
        case '\f':
          if (linepos > linelength)
            linelength = linepos;
          linepos = 0;
          goto word_separator;
        case '\t':
          linepos += 8 - (linepos % 8);
          goto word_separator;
        case ' ':
          linepos++;
          /* Fall through. */
        case '\v':
        word_separator:
          if (in_word)
            {
              in_word = 0;
              words++;
            }
          break;
        default:
          if (ISPRINT ((unsigned char) p[-1]))
            {
              linepos++;
              if (ISSPACE ((unsigned char) p[-1]))
            goto word_separator;
              in_word = 1;
            }
          break;
        }
        }
      while (--bytes_read);
    }
      if (linepos > linelength)
    linelength = linepos;
      if (in_word)
    words++;
    }

  if (count_chars < print_chars)
    chars = bytes;

  write_counts (lines, words, chars, bytes, linelength, file);
  total_lines += lines;
  total_words += words;
  total_chars += chars;
  total_bytes += bytes;
  if (linelength > max_line_length)
    max_line_length = linelength;
}

static void
wc_file (const char *file)
{
  if (STREQ (file, "-"))
    {
      have_read_stdin = 1;
      wc (0, file);
    }
  else
    {
      int fd = open (file, O_RDONLY);
      if (fd == -1)
    {
      error (0, errno, "%s", file);
      exit_status = 1;
      return;
    }
      wc (fd, file);
      if (close (fd))
    {
      error (0, errno, "%s", file);
      exit_status = 1;
    }
    }
}

int
main (int argc, char **argv)
{
  int optc;
  int nfiles;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  exit_status = 0;
  posixly_correct = (getenv ("POSIXLY_CORRECT") != NULL);
  print_lines = print_words = print_chars = print_bytes = print_linelength = 0;
  total_lines = total_words = total_chars = total_bytes = max_line_length = 0;

  while ((optc = getopt_long (argc, argv, "clLmw", longopts, NULL)) != -1)
    switch (optc)
      {
      case 0:
    break;

      case 'c':
    print_bytes = 1;
    break;

      case 'm':
    print_chars = 1;
    break;

      case 'l':
    print_lines = 1;
    break;

      case 'w':
    print_words = 1;
    break;

      case 'L':
    print_linelength = 1;
    break;

      case_GETOPT_HELP_CHAR;

      case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

      default:
    usage (EXIT_FAILURE);
      }

  if (print_lines + print_words + print_chars + print_bytes + print_linelength
      == 0)
    print_lines = print_words = print_bytes = 1;

  nfiles = argc - optind;

  if (nfiles == 0)
    {
      have_read_stdin = 1;
      wc (0, "");
    }
  else
    {
      for (; optind < argc; ++optind)
    wc_file (argv[optind]);

      if (nfiles > 1)
    write_counts (total_lines, total_words, total_chars, total_bytes,
              max_line_length, _("total"));
    }

  if (have_read_stdin && close (STDIN_FILENO) != 0)
    error (EXIT_FAILURE, errno, "-");

  exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/whoami.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/whoami.c used in: Step 945
/* whoami -- print effective userid
   Copyright (C) 89,90, 1991-1997, 1999-2002 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.  */

/* Equivalent to `id -un'. */
/* Written by Richard Mlynarik. */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <pwd.h>
#include <getopt.h>

#include "system.h"
#include "long-options.h"
#include "closeout.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "whoami"

#define AUTHORS "Richard Mlynarik"

/* The name this program was run with. */
char *program_name;

static struct option const long_options[] =
{
  {0, 0, 0, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]...\n"), program_name);
      fputs (_("\
Print the user name associated with the current effective user id.\n\
Same as id -un.\n\
\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  register struct passwd *pw;
  register uid_t uid;
  int c;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              AUTHORS, usage);

  while ((c = getopt_long (argc, argv, "", long_options, NULL)) != -1)
    {
      switch (c)
    {
    case 0:
      break;

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (optind != argc)
    usage (EXIT_FAILURE);

  uid = geteuid ();
  pw = getpwuid (uid);
  if (pw)
    {
      puts (pw->pw_name);
      exit (EXIT_SUCCESS);
    }
  fprintf (stderr, _("%s: cannot find username for UID %u\n"),
       program_name, (unsigned) uid);
  exit (EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/tac.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/tac.c used in: Step 947
/* tac - concatenate and print files in reverse
   Copyright (C) 1988-1991, 1995-2002 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.  */

/* Written by Jay Lepreau (lepreau@cs.utah.edu).
   GNU enhancements by David MacKenzie (djm@gnu.ai.mit.edu). */

/* Copy each FILE, or the standard input if none are given or when a
   FILE name of "-" is encountered, to the standard output with the
   order of the records reversed.  The records are separated by
   instances of a string, or a newline if none is given.  By default, the
   separator string is attached to the end of the record that it
   follows in the file.

   Options:
   -b, --before         The separator is attached to the beginning
                of the record that it precedes in the file.
   -r, --regex          The separator is a regular expression.
   -s, --separator=separator    Use SEPARATOR as the record separator.

   To reverse a file byte by byte, use (in bash, ksh, or sh):
tac -r -s '.\|
' file */

#include <config.h>

#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
#include "closeout.h"

#include <regex.h>

#include "error.h"
#include "safe-read.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "tac"

#define AUTHORS N_ ("Jay Lepreau and David MacKenzie")

#if defined __MSDOS__ || defined _WIN32
/* Define this to non-zero on systems for which the regular mechanism
   (of unlinking an open file and expecting to be able to write, seek
   back to the beginning, then reread it) doesn't work.  E.g., on Windows
   and DOS systems.  */
# define DONT_UNLINK_WHILE_OPEN 1
#endif


#ifndef DEFAULT_TMPDIR
# define DEFAULT_TMPDIR "/tmp"
#endif

/* The number of bytes per atomic read. */
#define INITIAL_READSIZE 8192

/* The number of bytes per atomic write. */
#define WRITESIZE 8192

/* The name this program was run with. */
char *program_name;

/* The string that separates the records of the file. */
static char *separator;

/* If nonzero, print `separator' along with the record preceding it
   in the file; otherwise with the record following it. */
static int separator_ends_record;

/* 0 if `separator' is to be matched as a regular expression;
   otherwise, the length of `separator', used as a sentinel to
   stop the search. */
static size_t sentinel_length;

/* The length of a match with `separator'.  If `sentinel_length' is 0,
   `match_length' is computed every time a match succeeds;
   otherwise, it is simply the length of `separator'. */
static int match_length;

/* The input buffer. */
static char *G_buffer;

/* The number of bytes to read at once into `buffer'. */
static size_t read_size;

/* The size of `buffer'.  This is read_size * 2 + sentinel_length + 2.
   The extra 2 bytes allow `past_end' to have a value beyond the
   end of `G_buffer' and `match_start' to run off the front of `G_buffer'. */
static unsigned G_buffer_size;

/* The compiled regular expression representing `separator'. */
static struct re_pattern_buffer compiled_separator;

static struct option const longopts[] =
{
  {"before", no_argument, NULL, 'b'},
  {"regex", no_argument, NULL, 'r'},
  {"separator", required_argument, NULL, 's'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
          program_name);
      fputs (_("\
Write each FILE to standard output, last line first.\n\
With no FILE, or when FILE is -, read standard input.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -b, --before             attach the separator before instead of after\n\
  -r, --regex              interpret the separator as a regular expression\n\
  -s, --separator=STRING   use STRING as the separator instead of newline\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Print the characters from START to PAST_END - 1.
   If START is NULL, just flush the buffer. */

static void
output (const char *start, const char *past_end)
{
  static char buffer[WRITESIZE];
  static size_t bytes_in_buffer = 0;
  size_t bytes_to_add = past_end - start;
  size_t bytes_available = WRITESIZE - bytes_in_buffer;

  if (start == 0)
    {
      fwrite (buffer, 1, bytes_in_buffer, stdout);
      bytes_in_buffer = 0;
      return;
    }

  /* Write out as many full buffers as possible. */
  while (bytes_to_add >= bytes_available)
    {
      memcpy (buffer + bytes_in_buffer, start, bytes_available);
      bytes_to_add -= bytes_available;
      start += bytes_available;
      fwrite (buffer, 1, WRITESIZE, stdout);
      bytes_in_buffer = 0;
      bytes_available = WRITESIZE;
    }

  memcpy (buffer + bytes_in_buffer, start, bytes_to_add);
  bytes_in_buffer += bytes_to_add;
}

/* Print in reverse the file open on descriptor FD for reading FILE.
   Return 0 if ok, 1 if an error occurs. */

static int
tac_seekable (int input_fd, const char *file)
{
  /* Pointer to the location in `G_buffer' where the search for
     the next separator will begin. */
  char *match_start;

  /* Pointer to one past the rightmost character in `G_buffer' that
     has not been printed yet. */
  char *past_end;

  /* Length of the record growing in `G_buffer'. */
  size_t saved_record_size;

  /* Offset in the file of the next read. */
  off_t file_pos;

  /* Nonzero if `output' has not been called yet for any file.
     Only used when the separator is attached to the preceding record. */
  int first_time = 1;
  char first_char = *separator; /* Speed optimization, non-regexp. */
  char *separator1 = separator + 1; /* Speed optimization, non-regexp. */
  int match_length1 = match_length - 1; /* Speed optimization, non-regexp. */
  struct re_registers regs;

  /* Find the size of the input file. */
  file_pos = lseek (input_fd, (off_t) 0, SEEK_END);
  if (file_pos < 1)
    return 0;           /* It's an empty file. */

  /* Arrange for the first read to lop off enough to leave the rest of the
     file a multiple of `read_size'.  Since `read_size' can change, this may
     not always hold during the program run, but since it usually will, leave
     it here for i/o efficiency (page/sector boundaries and all that).
     Note: the efficiency gain has not been verified. */
  saved_record_size = file_pos % read_size;
  if (saved_record_size == 0)
    saved_record_size = read_size;
  file_pos -= saved_record_size;
  /* `file_pos' now points to the start of the last (probably partial) block
     in the input file. */

  if (lseek (input_fd, file_pos, SEEK_SET) < 0)
    error (0, errno, "%s: seek failed", file);

  if (safe_read (input_fd, G_buffer, saved_record_size) != saved_record_size)
    {
      error (0, errno, "%s", file);
      return 1;
    }

  match_start = past_end = G_buffer + saved_record_size;
  /* For non-regexp search, move past impossible positions for a match. */
  if (sentinel_length)
    match_start -= match_length1;

  for (;;)
    {
      /* Search backward from `match_start' - 1 to `G_buffer' for a match
     with `separator'; for speed, use strncmp if `separator' contains no
     metacharacters.
     If the match succeeds, set `match_start' to point to the start of
     the match and `match_length' to the length of the match.
     Otherwise, make `match_start' < `G_buffer'. */
      if (sentinel_length == 0)
    {
      int i = match_start - G_buffer;
      int ret;

      ret = re_search (&compiled_separator, G_buffer, i, i - 1, -i, &regs);
      if (ret == -1)
        match_start = G_buffer - 1;
      else if (ret == -2)
        {
          error (EXIT_FAILURE, 0,
             _("error in regular expression search"));
        }
      else
        {
          match_start = G_buffer + regs.start[0];
          match_length = regs.end[0] - regs.start[0];
        }
    }
      else
    {
      /* `match_length' is constant for non-regexp boundaries. */
      while (*--match_start != first_char
         || (match_length1 && strncmp (match_start + 1, separator1,
                           match_length1)))
        /* Do nothing. */ ;
    }

      /* Check whether we backed off the front of `G_buffer' without finding
         a match for `separator'. */
      if (match_start < G_buffer)
    {
      if (file_pos == 0)
        {
          /* Hit the beginning of the file; print the remaining record. */
          output (G_buffer, past_end);
          return 0;
        }

      saved_record_size = past_end - G_buffer;
      if (saved_record_size > read_size)
        {
          /* `G_buffer_size' is about twice `read_size', so since
         we want to read in another `read_size' bytes before
         the data already in `G_buffer', we need to increase
         `G_buffer_size'. */
          char *newbuffer;
          int offset = sentinel_length ? sentinel_length : 1;

          read_size *= 2;
          G_buffer_size = read_size * 2 + sentinel_length + 2;
          newbuffer = xrealloc (G_buffer - offset, G_buffer_size);
          newbuffer += offset;
          /* Adjust the pointers for the new buffer location.  */
          match_start += newbuffer - G_buffer;
          past_end += newbuffer - G_buffer;
          G_buffer = newbuffer;
        }

      /* Back up to the start of the next bufferfull of the file.  */
      if (file_pos >= read_size)
        file_pos -= read_size;
      else
        {
          read_size = file_pos;
          file_pos = 0;
        }
      lseek (input_fd, file_pos, SEEK_SET);

      /* Shift the pending record data right to make room for the new.
         The source and destination regions probably overlap.  */
      memmove (G_buffer + read_size, G_buffer, saved_record_size);
      past_end = G_buffer + read_size + saved_record_size;
      /* For non-regexp searches, avoid unneccessary scanning. */
      if (sentinel_length)
        match_start = G_buffer + read_size;
      else
        match_start = past_end;

      if (safe_read (input_fd, G_buffer, read_size) != read_size)
        {
          error (0, errno, "%s", file);
          return 1;
        }
    }
      else
    {
      /* Found a match of `separator'. */
      if (separator_ends_record)
        {
          char *match_end = match_start + match_length;

          /* If this match of `separator' isn't at the end of the
             file, print the record. */
          if (first_time == 0 || match_end != past_end)
        output (match_end, past_end);
          past_end = match_end;
          first_time = 0;
        }
      else
        {
          output (match_start, past_end);
          past_end = match_start;
        }

      /* For non-regex matching, we can back up.  */
      if (sentinel_length > 0)
        match_start -= match_length - 1;
    }
    }
}

/* Print FILE in reverse.
   Return 0 if ok, 1 if an error occurs. */

static int
tac_file (const char *file)
{
  int errors;
  FILE *in;

  in = fopen (file, "r");
  if (in == NULL)
    {
      error (0, errno, "%s", file);
      return 1;
    }
  SET_BINARY (fileno (in));
  errors = tac_seekable (fileno (in), file);
  if (ferror (in) || fclose (in) == EOF)
    {
      error (0, errno, "%s", file);
      return 1;
    }
  return errors;
}

#if DONT_UNLINK_WHILE_OPEN

static const char *file_to_remove;
static FILE *fp_to_close;

static void
unlink_tempfile (void)
{
  fclose (fp_to_close);
  unlink (file_to_remove);
}

static void
record_tempfile (const char *fn, FILE *fp)
{
  if (!file_to_remove)
    {
      file_to_remove = fn;
      fp_to_close = fp;
      atexit (unlink_tempfile);
    }
}

#endif

/* Make a copy of the standard input in `FIXME'. */

static void
save_stdin (FILE **g_tmp, char **g_tempfile)
{
  static char *template = NULL;
  static char *tempdir;
  char *tempfile;
  FILE *tmp;
  int fd;

  if (template == NULL)
    {
      tempdir = getenv ("TMPDIR");
      if (tempdir == NULL)
    tempdir = DEFAULT_TMPDIR;
      template = xmalloc (strlen (tempdir) + 11);
    }
  sprintf (template, "%s/tacXXXXXX", tempdir);
  tempfile = template;
  fd = mkstemp (template);
  if (fd == -1)
    error (EXIT_FAILURE, errno, "%s", tempfile);

  tmp = fdopen (fd, "w+");
  if (tmp == NULL)
    error (EXIT_FAILURE, errno, "%s", tempfile);

#if DONT_UNLINK_WHILE_OPEN
  record_tempfile (tempfile, tmp);
#else
  unlink (tempfile);
#endif

  while (1)
    {
      size_t bytes_read = safe_read (STDIN_FILENO, G_buffer, read_size);
      if (bytes_read == 0)
    break;
      if (bytes_read == SAFE_READ_ERROR)
    error (EXIT_FAILURE, errno, _("stdin: read error"));

      if (fwrite (G_buffer, 1, bytes_read, tmp) != bytes_read)
    break;
    }

  if (ferror (tmp) || fflush (tmp) == EOF)
    error (EXIT_FAILURE, errno, "%s", tempfile);

  SET_BINARY (fileno (tmp));
  *g_tmp = tmp;
  *g_tempfile = tempfile;
}

/* Print the standard input in reverse, saving it to temporary
   file first if it is a pipe.
   Return 0 if ok, 1 if an error occurs. */

static int
tac_stdin (void)
{
  int errors;
  struct stat stats;

  /* No tempfile is needed for "tac < file".
     Use fstat instead of checking for errno == ESPIPE because
     lseek doesn't work on some special files but doesn't return an
     error, either. */
  if (fstat (STDIN_FILENO, &stats))
    {
      error (0, errno, _("standard input"));
      return 1;
    }

  if (S_ISREG (stats.st_mode))
    {
      errors = tac_seekable (fileno (stdin), _("standard input"));
    }
  else
    {
      FILE *tmp_stream;
      char *tmp_file;
      save_stdin (&tmp_stream, &tmp_file);
      errors = tac_seekable (fileno (tmp_stream), tmp_file);
    }

  return errors;
}

#if 0
/* BUF_END points one byte past the end of the buffer to be searched.  */

static void *
memrchr (const char *buf_start, const char *buf_end, int c)
{
  const char *p = buf_end;
  while (buf_start <= --p)
    {
      if (*(const unsigned char *) p == c)
    return (void *) p;
    }
  return NULL;
}

/* FIXME: describe */

static int
tac_mem (const char *buf, size_t n_bytes, FILE *out)
{
  const char *nl;
  const char *bol;

  if (n_bytes == 0)
    return 0;

  nl = memrchr (buf, buf + n_bytes, '\n');
  bol = (nl == NULL ? buf : nl + 1);

  /* If the last line of the input file has no terminating newline,
     treat it as a special case.  */
  if (bol < buf + n_bytes)
    {
      /* Print out the line from bol to end of input.  */
      fwrite (bol, 1, (buf + n_bytes) - bol, out);

      /* Add a newline here.  Otherwise, the first and second lines
     of output would appear to have been joined.  */
      fputc ('\n', out);
    }

  while ((nl = memrchr (buf, bol - 1, '\n')) != NULL)
    {
      /* Output the line (which includes a trailing newline)
     from NL+1 to BOL-1.  */
      fwrite (nl + 1, 1, bol - (nl + 1), out);

      bol = nl + 1;
    }

  /* If there's anything left, output the last line: BUF .. BOL-1.
     When the first byte of the input is a newline, there is nothing
     left to do here.  */
  if (buf < bol)
    fwrite (buf, 1, bol - buf, out);

  /* FIXME: this is work in progress.... */
  return ferror (out);
}

/* FIXME: describe */

static int
tac_stdin_to_mem (void)
{
  char *buf = NULL;
  size_t bufsiz = 8 * BUFSIZ;
  size_t delta = 8 * BUFSIZ;
  size_t n_bytes = 0;

  while (1)
    {
      size_t bytes_read;
      if (buf == NULL)
    buf = (char *) malloc (bufsiz);
      else
    buf = (char *) realloc (buf, bufsiz);

      if (buf == NULL)
    {
      /* Free the buffer and fall back on the code that relies on a
         temporary file.  */
      free (buf);
      /* FIXME */
      abort ();
    }
      bytes_read = safe_read (STDIN_FILENO, buf + n_bytes, bufsiz - n_bytes);
      if (bytes_read == 0)
    break;
      if (bytes_read == SAFE_READ_ERROR)
    error (EXIT_FAILURE, errno, _("stdin: read error"));
      n_bytes += bytes_read;

      bufsiz += delta;
    }

  tac_mem (buf, n_bytes, stdout);

  return 0;
}
#endif

int
main (int argc, char **argv)
{
  const char *error_message;    /* Return value from re_compile_pattern. */
  int optc, errors;
  int have_read_stdin = 0;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  errors = 0;
  separator = "\n";
  sentinel_length = 1;
  separator_ends_record = 1;

  while ((optc = getopt_long (argc, argv, "brs:", longopts, NULL)) != -1)
    {
      switch (optc)
    {
    case 0:
      break;
    case 'b':
      separator_ends_record = 0;
      break;
    case 'r':
      sentinel_length = 0;
      break;
    case 's':
      separator = optarg;
      if (*separator == 0)
        error (EXIT_FAILURE, 0, _("separator cannot be empty"));
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (EXIT_FAILURE);
    }
    }

  if (sentinel_length == 0)
    {
      compiled_separator.allocated = 100;
      compiled_separator.buffer = (unsigned char *)
    xmalloc (compiled_separator.allocated);
      compiled_separator.fastmap = xmalloc (256);
      compiled_separator.translate = 0;
      error_message = re_compile_pattern (separator, strlen (separator),
                      &compiled_separator);
      if (error_message)
    error (EXIT_FAILURE, 0, "%s", error_message);
    }
  else
    match_length = sentinel_length = strlen (separator);

  read_size = INITIAL_READSIZE;
  /* A precaution that will probably never be needed. */
  while (sentinel_length * 2 >= read_size)
    read_size *= 2;
  G_buffer_size = read_size * 2 + sentinel_length + 2;
  G_buffer = xmalloc (G_buffer_size);
  if (sentinel_length)
    {
      strcpy (G_buffer, separator);
      G_buffer += sentinel_length;
    }
  else
    {
      ++G_buffer;
    }

  if (optind == argc)
    {
      have_read_stdin = 1;
      /* We need binary I/O, since `tac' relies
     on `lseek' and byte counts.  */
      SET_BINARY2 (STDIN_FILENO, STDOUT_FILENO);
      errors = tac_stdin ();
    }
  else
    {
      for (; optind < argc; ++optind)
    {
      if (STREQ (argv[optind], "-"))
        {
          have_read_stdin = 1;
          SET_BINARY2 (STDIN_FILENO, STDOUT_FILENO);
          errors |= tac_stdin ();
        }
      else
        {
          /* Binary output will leave the lines' ends (NL or
         CR/LF) intact when the output is a disk file.
         Writing a file with CR/LF pairs at end of lines in
         text mode has no visible effect on console output,
         since two CRs in a row are just like one CR.  */
          SET_BINARY (STDOUT_FILENO);
          errors |= tac_file (argv[optind]);
        }
    }
    }

  /* Flush the output buffer. */
  output ((char *) NULL, (char *) NULL);

  if (have_read_stdin && close (STDIN_FILENO) < 0)
    error (EXIT_FAILURE, errno, "-");
  exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/test.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/test.c used in: Step 949
/* GNU test program (ksb and mjb) */

/* Modified to run with the GNU shell by bfox. */

/* Copyright (C) 1987-2003 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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.  */

/* Define TEST_STANDALONE to get the /bin/test version.  Otherwise, you get
   the shell builtin version. */
/* #define TEST_STANDALONE */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "test"

#define TEST_STANDALONE 1

#if !defined (TEST_STANDALONE)
# include "shell.h"
# include "posixstat.h"
# include "filecntl.h"
#else /* TEST_STANDALONE */
# include "system.h"
# include "error.h"
# include "euidaccess.h"
# if !defined (S_IXUGO)
#  define S_IXUGO 0111
# endif /* S_IXUGO */
# if defined (_POSIX_VERSION)
#  include <limits.h>
# else /* !_POSIX_VERSION */
#  include <sys/param.h>
# endif /* _POSIX_VERSION */
# define whitespace(c) (((c) == ' ') || ((c) == '\t'))
# define digit(c)  ((c) >= '0' && (c) <= '9')
# define digit_value(c) ((c) - '0')
char *program_name;
#endif /* TEST_STANDALONE */

#if !defined (_POSIX_VERSION)
# include <sys/file.h>
#endif /* !_POSIX_VERSION */

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#undef STREQ
#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)

#if !defined (member)
# define member(c, s) ((c) ? (strchr ((s), (c)) ? 1 : 0) : 0)
#endif /* !member */

extern gid_t getegid ();
extern uid_t geteuid ();

#if !defined (R_OK)
# define R_OK 4
# define W_OK 2
# define X_OK 1
# define F_OK 0
#endif /* R_OK */

/* This name is used solely when printing --version information.  */
#define PROGRAM_NAME "test"

/* The following few defines control the truth and false output of each stage.
   TRUE and FALSE are what we use to compute the final output value.
   SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
   TRUTH_OR is how to do logical or with TRUE and FALSE.
   TRUTH_AND is how to do logical and with TRUE and FALSE..
   Default is TRUE = 1, FALSE = 0, TRUTH_OR = a | b, TRUTH_AND = a & b,
    SHELL_BOOLEAN = (!value). */
#define TRUE 1
#define FALSE 0
#define SHELL_BOOLEAN(value) (!(value))
#define TRUTH_OR(a, b) ((a) | (b))
#define TRUTH_AND(a, b) ((a) & (b))

#if defined (TEST_STANDALONE)
# define test_exit(val) exit (val)
#else
   static jmp_buf test_exit_buf;
   static int test_error_return = 0;
# define test_exit(val) test_error_return = val, longjmp (test_exit_buf, 1)
#endif /* !TEST_STANDALONE */

static int pos;     /* The offset of the current argument in ARGV. */
static int argc;    /* The number of arguments present in ARGV. */
static char **argv; /* The argument list. */

static int unop (int op);
static int binop (char *s);
static int unary_operator (void);
static int binary_operator (void);
static int two_arguments (void);
static int three_arguments (void);
static int posixtest (void);

static int expr (void);
static int term (void);
static int and (void);
static int or (void);

static void test_syntax_error (char const *format, char const *arg)
     ATTRIBUTE_NORETURN;
static void beyond (void) ATTRIBUTE_NORETURN;

static void
test_syntax_error (char const *format, char const *arg)
{
  fprintf (stderr, "%s: ", argv[0]);
  fprintf (stderr, format, arg);
  fflush (stderr);
  test_exit (SHELL_BOOLEAN (FALSE));
}

#if HAVE_SETREUID && HAVE_SETREGID
/* Do the same thing access(2) does, but use the effective uid and gid.  */

static int
eaccess (char const *file, int mode)
{
  static int have_ids;
  static uid_t uid, euid;
  static gid_t gid, egid;
  int result;

  if (have_ids == 0)
    {
      have_ids = 1;
      uid = getuid ();
      gid = getgid ();
      euid = geteuid ();
      egid = getegid ();
    }

  /* Set the real user and group IDs to the effective ones.  */
  if (uid != euid)
    setreuid (euid, uid);
  if (gid != egid)
    setregid (egid, gid);

  result = access (file, mode);

  /* Restore them.  */
  if (uid != euid)
    setreuid (uid, euid);
  if (gid != egid)
    setregid (gid, egid);

  return result;
}
#else
# define eaccess(F, M) euidaccess (F, M)
#endif

/* Increment our position in the argument list.  Check that we're not
   past the end of the argument list.  This check is supressed if the
   argument is FALSE.  Made a macro for efficiency. */
#define advance(f)                          \
  do                                    \
    {                                   \
      ++pos;                                \
      if ((f) && pos >= argc)                       \
    beyond ();                          \
    }                                   \
  while (0)

#if !defined (advance)
static int
advance (int f)
{
  ++pos;

  if (f && pos >= argc)
    beyond ();
}
#endif /* advance */

#define unary_advance()                         \
  do                                    \
    {                                   \
      advance (1);                          \
      ++pos;                                \
    }                                   \
  while (0)

/*
 * beyond - call when we're beyond the end of the argument list (an
 *  error condition)
 */
static void
beyond (void)
{
  test_syntax_error (_("argument expected\n"), NULL);
}

/* Syntax error for when an integer argument was expected, but
   something else was found. */
static void
integer_expected_error (char const *pch)
{
  test_syntax_error (_("integer expression expected %s\n"), pch);
}

/* Return nonzero if the characters pointed to by STRING constitute a
   valid number.  Stuff the converted number into RESULT if RESULT is
   not null.  */
static int
isint (register char *string, intmax_t *result)
{
  int sign;
  intmax_t value;

  sign = 1;
  value = 0;

  if (result)
    *result = 0;

  /* Skip leading whitespace characters. */
  while (whitespace (*string))
    string++;

  if (!*string)
    return (0);

  /* We allow leading `-' or `+'. */
  if (*string == '-' || *string == '+')
    {
      if (!digit (string[1]))
    return (0);

      if (*string == '-')
    sign = -1;

      string++;
    }

  while (digit (*string))
    {
      if (result)
    value = (value * 10) + digit_value (*string);
      string++;
    }

  /* Skip trailing whitespace, if any. */
  while (whitespace (*string))
    string++;

  /* Error if not at end of string. */
  if (*string)
    return (0);

  if (result)
    {
      value *= sign;
      *result = value;
    }

  return (1);
}

/* Find the modification time of FILE, and stuff it into *AGE.
   Return 0 if successful, -1 if not.  */
static int
age_of (char *filename, time_t *age)
{
  struct stat finfo;
  int r = stat (filename, &finfo);
  if (r == 0)
    *age = finfo.st_mtime;
  return r;
}

/*
 * term - parse a term and return 1 or 0 depending on whether the term
 *  evaluates to true or false, respectively.
 *
 * term ::=
 *  '-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
 *  '-'('L'|'x') filename
 *  '-t' [ int ]
 *  '-'('z'|'n') string
 *  string
 *  string ('!='|'=') string
 *  <int> '-'(eq|ne|le|lt|ge|gt) <int>
 *  file '-'(nt|ot|ef) file
 *  '(' <expr> ')'
 * int ::=
 *  '-l' string
 *  positive and negative integers
 */
static int
term (void)
{
  int value;

  if (pos >= argc)
    beyond ();

  /* Deal with leading "not"'s. */
  if ('!' == argv[pos][0] && '\000' == argv[pos][1])
    {
      value = FALSE;
      while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
    {
      advance (1);
      value ^= (TRUE);
    }

      return (value ^ (term ()));
    }

  /* A paren-bracketed argument. */
  if (argv[pos][0] == '(' && !argv[pos][1])
    {
      advance (1);
      value = expr ();
      if (!argv[pos])
    test_syntax_error (_("')' expected\n"), NULL);
      else
        if (argv[pos][0] != ')' || argv[pos][1])
      test_syntax_error (_("')' expected, found %s\n"), argv[pos]);
      advance (0);
      return (TRUE == (value));
    }

  /* are there enough arguments left that this could be dyadic? */
  if (((pos + 3 <= argc) && binop (argv[pos + 1])) ||
      ((pos + 4 <= argc && STREQ (argv[pos], "-l") && binop (argv[pos + 2]))))
    value = binary_operator ();

  /* Might be a switch type argument */
  else if ('-' == argv[pos][0] && argv[pos][1] && 0 == argv[pos][2])
    {
      if (unop (argv[pos][1]))
    value = unary_operator ();
      else
    test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
    }
  else
    {
      value = (argv[pos][0] != '\0');
      advance (0);
    }

  return (value);
}

static int
binary_operator (void)
{
  register int op;
  struct stat stat_buf, stat_spare;
  intmax_t l, r;
  int value;
  /* Are the left and right integer expressions of the form '-l string'? */
  int l_is_l, r_is_l;

  if (strcmp (argv[pos], "-l") == 0)
    {
      l_is_l = 1;
      op = pos + 2;

      /* Make sure that OP is still a valid binary operator. */
      if ((op >= argc - 1) || (binop (argv[op]) == 0))
    test_syntax_error (_("%s: binary operator expected\n"), argv[op]);

      advance (0);
    }
  else
    {
      l_is_l = 0;
      op = pos + 1;
    }

  if ((op < argc - 2) && (strcmp (argv[op + 1], "-l") == 0))
    {
      r_is_l = 1;
      advance (0);
    }
  else
    r_is_l = 0;

  if (argv[op][0] == '-')
    {
      /* check for eq, nt, and stuff */
      switch (argv[op][1])
    {
    default:
      break;

    case 'l':
      if (argv[op][2] == 't' && !argv[op][3])
        {
          /* lt */
          if (l_is_l)
        l = strlen (argv[op - 1]);
          else
        {
          if (!isint (argv[op - 1], &l))
            integer_expected_error (_("before -lt"));
        }

          if (r_is_l)
        r = strlen (argv[op + 2]);
          else
        {
          if (!isint (argv[op + 1], &r))
            integer_expected_error (_("after -lt"));
        }
          pos += 3;
          return (TRUE == (l < r));
        }

      if (argv[op][2] == 'e' && !argv[op][3])
        {
          /* le */
          if (l_is_l)
        l = strlen (argv[op - 1]);
          else
        {
          if (!isint (argv[op - 1], &l))
            integer_expected_error (_("before -le"));
        }
          if (r_is_l)
        r = strlen (argv[op + 2]);
          else
        {
          if (!isint (argv[op + 1], &r))
            integer_expected_error (_("after -le"));
        }
          pos += 3;
          return (TRUE == (l <= r));
        }
      break;

    case 'g':
      if (argv[op][2] == 't' && !argv[op][3])
        {
          /* gt integer greater than */
          if (l_is_l)
        l = strlen (argv[op - 1]);
          else
        {
          if (!isint (argv[op - 1], &l))
            integer_expected_error (_("before -gt"));
        }
          if (r_is_l)
        r = strlen (argv[op + 2]);
          else
        {
          if (!isint (argv[op + 1], &r))
            integer_expected_error (_("after -gt"));
        }
          pos += 3;
          return (TRUE == (l > r));
        }

      if (argv[op][2] == 'e' && !argv[op][3])
        {
          /* ge - integer greater than or equal to */
          if (l_is_l)
        l = strlen (argv[op - 1]);
          else
        {
          if (!isint (argv[op - 1], &l))
            integer_expected_error (_("before -ge"));
        }
          if (r_is_l)
        r = strlen (argv[op + 2]);
          else
        {
          if (!isint (argv[op + 1], &r))
            integer_expected_error (_("after -ge"));
        }
          pos += 3;
          return (TRUE == (l >= r));
        }
      break;

    case 'n':
      if (argv[op][2] == 't' && !argv[op][3])
        {
          /* nt - newer than */
          time_t lt, rt;
          int le, re;
          pos += 3;
          if (l_is_l || r_is_l)
        test_syntax_error (_("-nt does not accept -l\n"), NULL);
          le = age_of (argv[op - 1], &lt);
          re = age_of (argv[op + 1], &rt);
          return le > re || (le == 0 && lt > rt);
        }

      if (argv[op][2] == 'e' && !argv[op][3])
        {
          /* ne - integer not equal */
          if (l_is_l)
        l = strlen (argv[op - 1]);
          else
        {
          if (!isint (argv[op - 1], &l))
            integer_expected_error (_("before -ne"));
        }
          if (r_is_l)
        r = strlen (argv[op + 2]);
          else
        {
          if (!isint (argv[op + 1], &r))
            integer_expected_error (_("after -ne"));
        }
          pos += 3;
          return (TRUE == (l != r));
        }
      break;

    case 'e':
      if (argv[op][2] == 'q' && !argv[op][3])
        {
          /* eq - integer equal */
          if (l_is_l)
        l = strlen (argv[op - 1]);
          else
        {
          if (!isint (argv[op - 1], &l))
            integer_expected_error (_("before -eq"));
        }
          if (r_is_l)
        r = strlen (argv[op + 2]);
          else
        {
          if (!isint (argv[op + 1], &r))
            integer_expected_error (_("after -eq"));
        }
          pos += 3;
          return (TRUE == (l == r));
        }

      if (argv[op][2] == 'f' && !argv[op][3])
        {
          /* ef - hard link? */
          pos += 3;
          if (l_is_l || r_is_l)
        test_syntax_error (_("-ef does not accept -l\n"), NULL);
          if (stat (argv[op - 1], &stat_buf) < 0)
        return (FALSE);
          if (stat (argv[op + 1], &stat_spare) < 0)
        return (FALSE);
          return (TRUE ==
              (stat_buf.st_dev == stat_spare.st_dev &&
               stat_buf.st_ino == stat_spare.st_ino));
        }
      break;

    case 'o':
      if ('t' == argv[op][2] && '\000' == argv[op][3])
        {
          /* ot - older than */
          time_t lt, rt;
          int le, re;
          pos += 3;
          if (l_is_l || r_is_l)
        test_syntax_error (_("-ot does not accept -l\n"), NULL);
          le = age_of (argv[op - 1], &lt);
          re = age_of (argv[op + 1], &rt);
          return le < re || (re == 0 && lt < rt);
        }
      break;
    }
      test_syntax_error (_("unknown binary operator"), argv[op]);
    }

  if (argv[op][0] == '=' && !argv[op][1])
    {
      value = (strcmp (argv[pos], argv[pos + 2]) == 0);
      pos += 3;
      return (TRUE == value);
    }

  if (strcmp (argv[op], "!=") == 0)
    {
      value = (strcmp (argv[pos], argv[pos + 2]) != 0);
      pos += 3;
      return (TRUE == value);
    }

  /* Not reached.  */
  abort ();
}

static int
unary_operator (void)
{
  int value;
  struct stat stat_buf;

  switch (argv[pos][1])
    {
    default:
      return (FALSE);

      /* All of the following unary operators use unary_advance (), which
     checks to make sure that there is an argument, and then advances
     pos right past it.  This means that pos - 1 is the location of the
     argument. */

    case 'a':           /* file exists in the file system? */
    case 'e':
      unary_advance ();
      value = -1 != stat (argv[pos - 1], &stat_buf);
      return (TRUE == value);

    case 'r':           /* file is readable? */
      unary_advance ();
      value = -1 != eaccess (argv[pos - 1], R_OK);
      return (TRUE == value);

    case 'w':           /* File is writable? */
      unary_advance ();
      value = -1 != eaccess (argv[pos - 1], W_OK);
      return (TRUE == value);

    case 'x':           /* File is executable? */
      unary_advance ();
      value = -1 != eaccess (argv[pos - 1], X_OK);
      return (TRUE == value);

    case 'O':           /* File is owned by you? */
      unary_advance ();
      if (stat (argv[pos - 1], &stat_buf) < 0)
    return (FALSE);

      return (TRUE == (geteuid () == stat_buf.st_uid));

    case 'G':           /* File is owned by your group? */
      unary_advance ();
      if (stat (argv[pos - 1], &stat_buf) < 0)
    return (FALSE);

      return (TRUE == (getegid () == stat_buf.st_gid));

    case 'f':           /* File is a file? */
      unary_advance ();
      if (stat (argv[pos - 1], &stat_buf) < 0)
    return (FALSE);

      /* Under POSIX, -f is true if the given file exists
     and is a regular file. */
      return (TRUE == ((S_ISREG (stat_buf.st_mode)) ||
               (0 == (stat_buf.st_mode & S_IFMT))));

    case 'd':           /* File is a directory? */
      unary_advance ();
      if (stat (argv[pos - 1], &stat_buf) < 0)
    return (FALSE);

      return (TRUE == (S_ISDIR (stat_buf.st_mode)));

    case 's':           /* File has something in it? */
      unary_advance ();
      if (stat (argv[pos - 1], &stat_buf) < 0)
    return (FALSE);

      return (TRUE == (stat_buf.st_size > (off_t) 0));

    case 'S':           /* File is a socket? */
#if !defined (S_ISSOCK)
      return (FALSE);
#else
      unary_advance ();

      if (stat (argv[pos - 1], &stat_buf) < 0)
    return (FALSE);

      return (TRUE == (S_ISSOCK (stat_buf.st_mode)));
#endif              /* S_ISSOCK */

    case 'c':           /* File is character special? */
      unary_advance ();
      if (stat (argv[pos - 1], &stat_buf) < 0)
    return (FALSE);

      return (TRUE == (S_ISCHR (stat_buf.st_mode)));

    case 'b':           /* File is block special? */
      unary_advance ();
      if (stat (argv[pos - 1], &stat_buf) < 0)
    return (FALSE);

      return (TRUE == (S_ISBLK (stat_buf.st_mode)));

    case 'p':           /* File is a named pipe? */
      unary_advance ();
#ifndef S_ISFIFO
      return (FALSE);
#else
      if (stat (argv[pos - 1], &stat_buf) < 0)
    return (FALSE);
      return (TRUE == (S_ISFIFO (stat_buf.st_mode)));
#endif              /* S_ISFIFO */

    case 'L':           /* Same as -h  */
      /*FALLTHROUGH*/

    case 'h':           /* File is a symbolic link? */
      unary_advance ();
#ifndef S_ISLNK
      return (FALSE);
#else
      /* An empty filename is not a valid pathname. */
      if ((argv[pos - 1][0] == '\0') ||
      (lstat (argv[pos - 1], &stat_buf) < 0))
    return (FALSE);

      return (TRUE == (S_ISLNK (stat_buf.st_mode)));
#endif              /* S_IFLNK */

    case 'u':           /* File is setuid? */
      unary_advance ();
#ifndef S_ISUID
      return (FALSE);
#else
      if (stat (argv[pos - 1], &stat_buf) < 0)
    return (FALSE);

      return (TRUE == (0 != (stat_buf.st_mode & S_ISUID)));
#endif

    case 'g':           /* File is setgid? */
      unary_advance ();
#ifndef S_ISGID
      return (FALSE);
#else
      if (stat (argv[pos - 1], &stat_buf) < 0)
    return (FALSE);

      return (TRUE == (0 != (stat_buf.st_mode & S_ISGID)));
#endif

    case 'k':           /* File has sticky bit set? */
      unary_advance ();
      if (stat (argv[pos - 1], &stat_buf) < 0)
    return (FALSE);
#ifndef S_ISVTX
      /* This is not Posix, and is not defined on some Posix systems. */
      return (FALSE);
#else
      return (TRUE == (0 != (stat_buf.st_mode & S_ISVTX)));
#endif

    case 't':           /* File (fd) is a terminal? */
      {
    intmax_t fd;
    advance (0);
    if (pos < argc)
      {
        if (!isint (argv[pos], &fd))
          integer_expected_error (_("after -t"));
        advance (0);
      }
    else
      {
        fd = 1;
      }
    return (TRUE == (fd == (int) fd && isatty (fd)));
      }

    case 'n':           /* True if arg has some length. */
      unary_advance ();
      return (TRUE == (argv[pos - 1][0] != 0));

    case 'z':           /* True if arg has no length. */
      unary_advance ();
      return (TRUE == (argv[pos - 1][0] == '\0'));
    }
}

/*
 * and:
 *  term
 *  term '-a' and
 */
static int
and (void)
{
  int value;

  value = term ();
  while ((pos < argc) && strcmp (argv[pos], "-a") == 0)
    {
      advance (0);
      value = TRUTH_AND (value, and ());
    }
  return (TRUE == value);
}

/*
 * or:
 *  and
 *  and '-o' or
 */
static int
or (void)
{
  int value;

  value = and ();

  while ((pos < argc) && strcmp (argv[pos], "-o") == 0)
    {
      advance (0);
      value = TRUTH_OR (value, or ());
    }

  return (TRUE == value);
}

/*
 * expr:
 *  or
 */
static int
expr (void)
{
  if (pos >= argc)
    beyond ();

  return (FALSE ^ (or ()));     /* Same with this. */
}

/* Return TRUE if S is one of the test command's binary operators. */
static int
binop (char *s)
{
  return ((STREQ (s,   "=")) || (STREQ (s,  "!=")) || (STREQ (s, "-nt")) ||
      (STREQ (s, "-ot")) || (STREQ (s, "-ef")) || (STREQ (s, "-eq")) ||
      (STREQ (s, "-ne")) || (STREQ (s, "-lt")) || (STREQ (s, "-le")) ||
      (STREQ (s, "-gt")) || (STREQ (s, "-ge")));
}

/* Return nonzero if OP is one of the test command's unary operators. */
static int
unop (int op)
{
  return (member (op, "abcdefgkLhprsStuwxOGnz"));
}

static int
one_argument (const char *s)
{
  if (STREQ (s, "-t"))
    return (TRUE == (isatty (1)));

  return strlen (s) != 0;
}

static int
two_arguments (void)
{
  int value;

  if (STREQ (argv[pos], "!"))
    value = ! one_argument (argv[pos+1]);
  else if (argv[pos][0] == '-'
       && argv[pos][1] != '\0'
       && argv[pos][2] == '\0')
    {
      if (unop (argv[pos][1]))
    value = unary_operator ();
      else
    test_syntax_error (_("%s: unary operator expected\n"), argv[pos]);
    }
  else
    beyond ();
  return (value);
}

static int
three_arguments (void)
{
  int value;

  if (STREQ (argv[pos], "!"))
    {
      advance (1);
      value = !two_arguments ();
    }
  else if (binop (argv[pos+1]))
    {
      value = binary_operator ();
      pos = argc;
    }
  else if ((STREQ (argv[pos+1], "-a")) || (STREQ (argv[pos+1], "-o")) ||
       (argv[pos][0] == '('))
    value = expr ();
  else
    test_syntax_error (_("%s: binary operator expected\n"), argv[pos+1]);
  return (value);
}

/* This is an implementation of a Posix.2 proposal by David Korn. */
static int
posixtest (void)
{
  int value;

  switch (argc - 1) /* one extra passed in */
    {
      case 0:
    value = FALSE;
    pos = argc;
    break;

      case 1:
    value = one_argument (argv[1]);
    pos = argc;
    break;

      case 2:
    value = two_arguments ();
    pos = argc;
    break;

      case 3:
    value = three_arguments ();
    break;

      case 4:
    if (STREQ (argv[pos], "!"))
      {
        advance (1);
        value = !three_arguments ();
        break;
      }
    /* FALLTHROUGH */
      case 5:
      default:
    value = expr ();
    }

  return (value);
}

#if defined (TEST_STANDALONE)
# include "long-options.h"
# include "closeout.h"

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s EXPRESSION\n\
  or:  [ EXPRESSION ]\n\
  or:  %s OPTION\n\
"),
          program_name, program_name);
      fputs (_("\
Exit with the status determined by EXPRESSION.\n\
\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
EXPRESSION is true or false and sets exit status.  It is one of:\n\
"), stdout);
      fputs (_("\
\n\
  ( EXPRESSION )               EXPRESSION is true\n\
  ! EXPRESSION                 EXPRESSION is false\n\
  EXPRESSION1 -a EXPRESSION2   both EXPRESSION1 and EXPRESSION2 are true\n\
  EXPRESSION1 -o EXPRESSION2   either EXPRESSION1 or EXPRESSION2 is true\n\
"), stdout);
      fputs (_("\
\n\
  [-n] STRING          the length of STRING is nonzero\n\
  -z STRING            the length of STRING is zero\n\
  STRING1 = STRING2    the strings are equal\n\
  STRING1 != STRING2   the strings are not equal\n\
"), stdout);
      fputs (_("\
\n\
  INTEGER1 -eq INTEGER2   INTEGER1 is equal to INTEGER2\n\
  INTEGER1 -ge INTEGER2   INTEGER1 is greater than or equal to INTEGER2\n\
  INTEGER1 -gt INTEGER2   INTEGER1 is greater than INTEGER2\n\
  INTEGER1 -le INTEGER2   INTEGER1 is less than or equal to INTEGER2\n\
  INTEGER1 -lt INTEGER2   INTEGER1 is less than INTEGER2\n\
  INTEGER1 -ne INTEGER2   INTEGER1 is not equal to INTEGER2\n\
"), stdout);
      fputs (_("\
\n\
  FILE1 -ef FILE2   FILE1 and FILE2 have the same device and inode numbers\n\
  FILE1 -nt FILE2   FILE1 is newer (modification date) than FILE2\n\
  FILE1 -ot FILE2   FILE1 is older than FILE2\n\
"), stdout);
      fputs (_("\
\n\
  -b FILE     FILE exists and is block special\n\
  -c FILE     FILE exists and is character special\n\
  -d FILE     FILE exists and is a directory\n\
  -e FILE     FILE exists\n\
"), stdout);
      fputs (_("\
  -f FILE     FILE exists and is a regular file\n\
  -g FILE     FILE exists and is set-group-ID\n\
  -h FILE     FILE exists and is a symbolic link (same as -L)\n\
  -G FILE     FILE exists and is owned by the effective group ID\n\
  -k FILE     FILE exists and has its sticky bit set\n\
"), stdout);
      fputs (_("\
  -L FILE     FILE exists and is a symbolic link (same as -h)\n\
  -O FILE     FILE exists and is owned by the effective user ID\n\
  -p FILE     FILE exists and is a named pipe\n\
  -r FILE     FILE exists and is readable\n\
  -s FILE     FILE exists and has a size greater than zero\n\
"), stdout);
      fputs (_("\
  -S FILE     FILE exists and is a socket\n\
  -t [FD]     file descriptor FD (stdout by default) is opened on a terminal\n\
  -u FILE     FILE exists and its set-user-ID bit is set\n\
  -w FILE     FILE exists and is writable\n\
  -x FILE     FILE exists and is executable\n\
"), stdout);
      fputs (_("\
\n\
Beware that parentheses need to be escaped (e.g., by backslashes) for shells.\n\
INTEGER may also be -l STRING, which evaluates to the length of STRING.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}
#endif /* TEST_STANDALONE */

#if !defined (TEST_STANDALONE)
# define main test_command
#endif

#define AUTHORS N_ ("FIXME: ksb and mjb")

/*
 * [:
 *  '[' expr ']'
 * test:
 *  test expr
 */
int
main (int margc, char **margv)
{
  int value;

#if !defined (TEST_STANDALONE)
  int code;

  code = setjmp (test_exit_buf);

  if (code)
    return (test_error_return);
#else /* TEST_STANDALONE */
  program_name = margv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);
#endif /* TEST_STANDALONE */

  argv = margv;

  if (margv[0] && strcmp (margv[0], "[") == 0)
    {
      /* Don't recognize --help or --version if POSIXLY_CORRECT is set.  */
      if (getenv ("POSIXLY_CORRECT") == NULL)
    parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
                AUTHORS, usage);

      --margc;

      if (margc < 2)
    test_exit (SHELL_BOOLEAN (FALSE));

      if (margv[margc] && strcmp (margv[margc], "]") != 0)
    test_syntax_error (_("missing `]'\n"), NULL);
    }

  argc = margc;
  pos = 1;

  if (pos >= argc)
    test_exit (SHELL_BOOLEAN (FALSE));

  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
              AUTHORS, usage);
  value = posixtest ();

  if (pos != argc)
    test_syntax_error (_("too many arguments\n"), NULL);

  test_exit (SHELL_BOOLEAN (value));
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getdate.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getdate.h used in: Step 951
/*  Copyright (C) 1995, 1997, 1998 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#ifndef PARAMS
# if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#  define PARAMS(Args) Args
# else
#  define PARAMS(Args) ()
# endif
#endif

#ifdef vms
# include <types.h>
# include <time.h>
#else
# include <sys/types.h>
# if TIME_WITH_SYS_TIME
#  include <sys/time.h>
#  include <time.h>
# else
#  if HAVE_SYS_TIME_H
#   include <sys/time.h>
#  else
#   include <time.h>
#  endif
# endif
#endif /* defined (vms) */

time_t get_date PARAMS ((const char *p, const time_t *now));

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/posixtm.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/posixtm.h used in: Step 951
#ifndef POSIXTM_H_
# define POSIXTM_H_

/* POSIX Date Syntax flags.  */
# define PDS_LEADING_YEAR 1
# define PDS_TRAILING_YEAR 2
# define PDS_CENTURY 4
# define PDS_SECONDS 8

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

bool
posixtime PARAMS ((time_t *p, const char *s, unsigned int syntax_bits));

#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/yes.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/yes.c used in: Step 955
/* yes - output a string repeatedly until killed
   Copyright (C) 1991-1997, 1999-2002 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.  */

/* David MacKenzie <djm@gnu.ai.mit.edu> */

#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <getopt.h>

#include "error.h"
#include "system.h"
#include "long-options.h"
#include "closeout.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "yes"

#define AUTHORS "David MacKenzie"

/* How many iterations between ferror checks.  */
#define UNROLL 10000

/* The name this program was run with. */
char *program_name;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [STRING]...\n\
  or:  %s OPTION\n\
"),
          program_name, program_name);

      fputs (_("\
Repeatedly output a line with all specified STRING(s), or `y'.\n\
\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  /* Don't recognize --help or --version if POSIXLY_CORRECT is set.  */
  if (getenv ("POSIXLY_CORRECT") == NULL)
    parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
            AUTHORS, usage);

  if (argc == 1)
    {
      while (1)
    {
      int i;
      for (i = 0; i < UNROLL; i++)
        puts ("y");
      if (ferror (stdout))
        break;
    }
    }
  else
    {
      while (1)
    {
      int i;
      for (i = 0; i < UNROLL; i++)
        {
          int j;
          for (j = 1; j < argc; j++)
        {
          fputs (argv[j], stdout);
          putchar (j == argc - 1 ? '\n' : ' ');
        }
        }
      if (ferror (stdout))
        break;
    }
    }

  error (0, errno, "standard output");
  exit (EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/cp.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/cp.c used in: Step 957
/* cp.c  -- file copying (main routines)
   Copyright (C) 89, 90, 91, 1995-2003 Free Software Foundation.

   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.

   Written by Torbjorn Granlund, David MacKenzie, and Jim Meyering. */

#ifdef _AIX
 #pragma alloca
#endif

#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <assert.h>
#include <getopt.h>

#include "system.h"
#include "argmatch.h"
#include "backupfile.h"
#include "copy.h"
#include "cp-hash.h"
#include "error.h"
#include "dirname.h"
#include "path-concat.h"
#include "quote.h"

#define ASSIGN_BASENAME_STRDUPA(Dest, File_name)    \
  do                            \
    {                           \
      char *tmp_abns_;                  \
      ASSIGN_STRDUPA (tmp_abns_, (File_name));      \
      strip_trailing_slashes (tmp_abns_);       \
      Dest = base_name (tmp_abns_);         \
    }                           \
  while (0)

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "cp"

#define AUTHORS N_ ("Torbjorn Granlund, David MacKenzie, and Jim Meyering")

#ifndef _POSIX_VERSION
uid_t geteuid ();
#endif

/* Used by do_copy, make_path_private, and re_protect
   to keep a list of leading directories whose protections
   need to be fixed after copying. */
struct dir_attr
{
  int is_new_dir;
  int slash_offset;
  struct dir_attr *next;
};

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  COPY_CONTENTS_OPTION = CHAR_MAX + 1,
  NO_PRESERVE_ATTRIBUTES_OPTION,
  PARENTS_OPTION,
  PRESERVE_ATTRIBUTES_OPTION,
  REPLY_OPTION,
  SPARSE_OPTION,
  STRIP_TRAILING_SLASHES_OPTION,
  TARGET_DIRECTORY_OPTION,
  UNLINK_DEST_BEFORE_OPENING
};

/* Initial number of entries in each hash table entry's table of inodes.  */
#define INITIAL_HASH_MODULE 100

/* Initial number of entries in the inode hash table.  */
#define INITIAL_ENTRY_TAB_SIZE 70

/* The invocation name of this program.  */
char *program_name;

/* If nonzero, the command "cp x/e_file e_dir" uses "e_dir/x/e_file"
   as its destination instead of the usual "e_dir/e_file." */
static int flag_path = 0;

/* Remove any trailing slashes from each SOURCE argument.  */
static int remove_trailing_slashes;

static char const *const sparse_type_string[] =
{
  "never", "auto", "always", 0
};

static enum Sparse_type const sparse_type[] =
{
  SPARSE_NEVER, SPARSE_AUTO, SPARSE_ALWAYS
};

/* Valid arguments to the `--reply' option. */
static char const* const reply_args[] =
{
  "yes", "no", "query", 0
};

/* The values that correspond to the above strings. */
static int const reply_vals[] =
{
  I_ALWAYS_YES, I_ALWAYS_NO, I_ASK_USER
};

/* The error code to return to the system. */
static int exit_status = 0;

static struct option const long_opts[] =
{
  {"archive", no_argument, NULL, 'a'},
  {"backup", optional_argument, NULL, 'b'},
  {"copy-contents", no_argument, NULL, COPY_CONTENTS_OPTION},
  {"dereference", no_argument, NULL, 'L'},
  {"force", no_argument, NULL, 'f'},
  {"interactive", no_argument, NULL, 'i'},
  {"link", no_argument, NULL, 'l'},
  {"no-dereference", no_argument, NULL, 'P'},
  {"no-preserve", required_argument, NULL, NO_PRESERVE_ATTRIBUTES_OPTION},
  {"one-file-system", no_argument, NULL, 'x'},
  {"parents", no_argument, NULL, PARENTS_OPTION},
  {"path", no_argument, NULL, PARENTS_OPTION},   /* Deprecated.  */
  {"preserve", optional_argument, NULL, PRESERVE_ATTRIBUTES_OPTION},
  {"recursive", no_argument, NULL, 'R'},
  {"remove-destination", no_argument, NULL, UNLINK_DEST_BEFORE_OPENING},
  {"reply", required_argument, NULL, REPLY_OPTION},
  {"sparse", required_argument, NULL, SPARSE_OPTION},
  {"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
  {"suffix", required_argument, NULL, 'S'},
  {"symbolic-link", no_argument, NULL, 's'},
  {"target-directory", required_argument, NULL, TARGET_DIRECTORY_OPTION},
  {"update", no_argument, NULL, 'u'},
  {"verbose", no_argument, NULL, 'v'},
  {"version-control", required_argument, NULL, 'V'}, /* Deprecated. FIXME. */
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... SOURCE DEST\n\
  or:  %s [OPTION]... SOURCE... DIRECTORY\n\
  or:  %s [OPTION]... --target-directory=DIRECTORY SOURCE...\n\
"),
          program_name, program_name, program_name);
      fputs (_("\
Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
  -a, --archive                same as -dpR\n\
      --backup[=CONTROL]       make a backup of each existing destination file\n\
  -b                           like --backup but does not accept an argument\n\
      --copy-contents          copy contents of special files when recursive\n\
  -d                           same as --no-dereference --preserve=link\n\
"), stdout);
      fputs (_("\
      --no-dereference         never follow symbolic links\n\
  -f, --force                  if an existing destination file cannot be\n\
                                 opened, remove it and try again\n\
  -i, --interactive            prompt before overwrite\n\
  -H                           follow command-line symbolic links\n\
"), stdout);
      fputs (_("\
  -l, --link                   link files instead of copying\n\
  -L, --dereference            always follow symbolic links\n\
  -p                           same as --preserve=mode,ownership,timestamps\n\
      --preserve[=ATTR_LIST]   preserve the specified attributes (default:\n\
                                 mode,ownership,timestamps), if possible\n\
                                 additional attributes: links, all\n\
"), stdout);
      fputs (_("\
      --no-preserve=ATTR_LIST  don't preserve the specified attributes\n\
      --parents                append source path to DIRECTORY\n\
  -P                           same as `--no-dereference'\n\
"), stdout);
      fputs (_("\
  -R, -r, --recursive          copy directories recursively\n\
      --remove-destination     remove each existing destination file before\n\
                                 attempting to open it (contrast with --force)\n\
"), stdout);
      fputs (_("\
      --reply={yes,no,query}   specify how to handle the prompt about an\n\
                                 existing destination file\n\
      --sparse=WHEN            control creation of sparse files\n\
      --strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
                                 argument\n\
"), stdout);
      fputs (_("\
  -s, --symbolic-link          make symbolic links instead of copying\n\
  -S, --suffix=SUFFIX          override the usual backup suffix\n\
      --target-directory=DIRECTORY  move all SOURCE arguments into DIRECTORY\n\
"), stdout);
      fputs (_("\
  -u, --update                 copy only when the SOURCE file is newer\n\
                                 than the destination file or when the\n\
                                 destination file is missing\n\
  -v, --verbose                explain what is being done\n\
  -x, --one-file-system        stay on this file system\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
By default, sparse SOURCE files are detected by a crude heuristic and the\n\
corresponding DEST file is made sparse as well.  That is the behavior\n\
selected by --sparse=auto.  Specify --sparse=always to create a sparse DEST\n\
file whenever the SOURCE file contains a long enough sequence of zero bytes.\n\
Use --sparse=never to inhibit creation of sparse files.\n\
\n\
"), stdout);
      fputs (_("\
The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
The version control method may be selected via the --backup option or through\n\
the VERSION_CONTROL environment variable.  Here are the values:\n\
\n\
"), stdout);
      fputs (_("\
  none, off       never make backups (even if --backup is given)\n\
  numbered, t     make numbered backups\n\
  existing, nil   numbered if numbered backups exist, simple otherwise\n\
  simple, never   always make simple backups\n\
"), stdout);
      fputs (_("\
\n\
As a special case, cp makes a backup of SOURCE when the force and backup\n\
options are given and SOURCE and DEST are the same name for an existing,\n\
regular file.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

/* Ensure that the parent directories of CONST_DST_PATH have the
   correct protections, for the --parents option.  This is done
   after all copying has been completed, to allow permissions
   that don't include user write/execute.

   SRC_OFFSET is the index in CONST_DST_PATH of the beginning of the
   source directory name.

   ATTR_LIST is a null-terminated linked list of structures that
   indicates the end of the filename of each intermediate directory
   in CONST_DST_PATH that may need to have its attributes changed.
   The command `cp --parents --preserve a/b/c d/e_dir' changes the
   attributes of the directories d/e_dir/a and d/e_dir/a/b to match
   the corresponding source directories regardless of whether they
   existed before the `cp' command was given.

   Return 0 if the parent of CONST_DST_PATH and any intermediate
   directories specified by ATTR_LIST have the proper permissions
   when done, otherwise 1. */

static int
re_protect (const char *const_dst_path, int src_offset,
        struct dir_attr *attr_list, const struct cp_options *x)
{
  struct dir_attr *p;
  char *dst_path;       /* A copy of CONST_DST_PATH we can change. */
  char *src_path;       /* The source name in `dst_path'. */
  uid_t myeuid = geteuid ();

  dst_path = (char *) alloca (strlen (const_dst_path) + 1);
  strcpy (dst_path, const_dst_path);
  src_path = dst_path + src_offset;

  for (p = attr_list; p; p = p->next)
    {
      struct stat src_sb;

      dst_path[p->slash_offset] = '\0';

      if ((*(x->xstat)) (src_path, &src_sb))
    {
      error (0, errno, _("failed to get attributes of %s"),
         quote (src_path));
      return 1;
    }

      /* Adjust the times (and if possible, ownership) for the copy.
     chown turns off set[ug]id bits for non-root,
     so do the chmod last.  */

      if (x->preserve_timestamps)
    {
      struct utimbuf utb;

      /* There's currently no interface to set file timestamps with
         better than 1-second resolution, so discard any fractional
         part of the source timestamp.  */

      utb.actime = src_sb.st_atime;
      utb.modtime = src_sb.st_mtime;

      if (utime (dst_path, &utb))
        {
          error (0, errno, _("failed to preserve times for %s"),
             quote (dst_path));
          return 1;
        }
    }

      if (x->preserve_ownership)
    {
      /* If non-root uses -p, it's ok if we can't preserve ownership.
         But root probably wants to know, e.g. if NFS disallows it,
         or if the target system doesn't support file ownership.  */
      if (chown (dst_path, src_sb.st_uid, src_sb.st_gid)
          && ((errno != EPERM && errno != EINVAL) || myeuid == 0))
        {
          error (0, errno, _("failed to preserve ownership for %s"),
             quote (dst_path));
          return 1;
        }
    }

      if (x->preserve_mode || p->is_new_dir)
    {
      if (chmod (dst_path, src_sb.st_mode & x->umask_kill))
        {
          error (0, errno, _("failed to preserve permissions for %s"),
             quote (dst_path));
          return 1;
        }
    }

      dst_path[p->slash_offset] = '/';
    }
  return 0;
}

/* Ensure that the parent directory of CONST_DIRPATH exists, for
   the --parents option.

   SRC_OFFSET is the index in CONST_DIRPATH (which is a destination
   path) of the beginning of the source directory name.
   Create any leading directories that don't already exist,
   giving them permissions MODE.
   If VERBOSE_FMT_STRING is nonzero, use it as a printf format
   string for printing a message after successfully making a directory.
   The format should take two string arguments: the names of the
   source and destination directories.
   Creates a linked list of attributes of intermediate directories,
   *ATTR_LIST, for re_protect to use after calling copy.
   Sets *NEW_DST to 1 if this function creates parent of CONST_DIRPATH.

   Return 0 if parent of CONST_DIRPATH exists as a directory with the proper
   permissions when done, otherwise 1. */

/* FIXME: find a way to synch this function with the one in lib/makepath.c. */

static int
make_path_private (const char *const_dirpath, int src_offset, int mode,
           const char *verbose_fmt_string, struct dir_attr **attr_list,
           int *new_dst, int (*xstat)())
{
  struct stat stats;
  char *dirpath;        /* A copy of CONST_DIRPATH we can change. */
  char *src;            /* Source name in `dirpath'. */
  char *dst_dirname;        /* Leading path of `dirpath'. */
  size_t dirlen;        /* Length of leading path of `dirpath'. */

  dirpath = (char *) alloca (strlen (const_dirpath) + 1);
  strcpy (dirpath, const_dirpath);

  src = dirpath + src_offset;

  dirlen = dir_len (dirpath);
  dst_dirname = (char *) alloca (dirlen + 1);
  memcpy (dst_dirname, dirpath, dirlen);
  dst_dirname[dirlen] = '\0';

  *attr_list = NULL;

  if ((*xstat) (dst_dirname, &stats))
    {
      /* Parent of CONST_DIRNAME does not exist.
     Make all missing intermediate directories. */
      char *slash;

      slash = src;
      while (*slash == '/')
    slash++;
      while ((slash = strchr (slash, '/')))
    {
      /* Add this directory to the list of directories whose modes need
         fixing later. */
      struct dir_attr *new =
        (struct dir_attr *) xmalloc (sizeof (struct dir_attr));
      new->slash_offset = slash - dirpath;
      new->next = *attr_list;
      *attr_list = new;

      *slash = '\0';
      if ((*xstat) (dirpath, &stats))
        {
          /* This element of the path does not exist.  We must set
         *new_dst and new->is_new_dir inside this loop because,
         for example, in the command `cp --parents ../a/../b/c e_dir',
         make_path_private creates only e_dir/../a if ./b already
         exists. */
          *new_dst = 1;
          new->is_new_dir = 1;
          if (mkdir (dirpath, mode))
        {
          error (0, errno, _("cannot make directory %s"),
             quote (dirpath));
          return 1;
        }
          else
        {
          if (verbose_fmt_string != NULL)
            printf (verbose_fmt_string, src, dirpath);
        }
        }
      else if (!S_ISDIR (stats.st_mode))
        {
          error (0, 0, _("%s exists but is not a directory"),
             quote (dirpath));
          return 1;
        }
      else
        {
          new->is_new_dir = 0;
          *new_dst = 0;
        }
      *slash++ = '/';

      /* Avoid unnecessary calls to `stat' when given
         pathnames containing multiple adjacent slashes.  */
      while (*slash == '/')
        slash++;
    }
    }

  /* We get here if the parent of `dirpath' already exists. */

  else if (!S_ISDIR (stats.st_mode))
    {
      error (0, 0, _("%s exists but is not a directory"), quote (dst_dirname));
      return 1;
    }
  else
    {
      *new_dst = 0;
    }
  return 0;
}

/* Scan the arguments, and copy each by calling copy.
   Return 0 if successful, 1 if any errors occur. */

static int
do_copy (int n_files, char **file, const char *target_directory,
     struct cp_options *x)
{
  const char *dest;
  struct stat sb;
  int new_dst = 0;
  int ret = 0;
  int dest_is_dir = 0;

  if (n_files <= 0)
    {
      error (0, 0, _("missing file argument"));
      usage (EXIT_FAILURE);
    }
  if (n_files == 1 && !target_directory)
    {
      error (0, 0, _("missing destination file"));
      usage (EXIT_FAILURE);
    }

  if (target_directory)
    dest = target_directory;
  else
    {
      dest = file[n_files - 1];
      --n_files;
    }

  /* Initialize these hash tables only if we'll need them.
     The problems they're used to detect can arise only if
     there are two or more files to copy.  */
  if (n_files >= 2)
    {
      dest_info_init (x);
      src_info_init (x);
    }

  if (lstat (dest, &sb))
    {
      if (errno != ENOENT)
    {
      error (0, errno, _("accessing %s"), quote (dest));
      return 1;
    }

      new_dst = 1;
    }
  else
    {
      struct stat sbx;

      /* If `dest' is not a symlink to a nonexistent file, use
     the results of stat instead of lstat, so we can copy files
     into symlinks to directories. */
      if (stat (dest, &sbx) == 0)
    sb = sbx;

      dest_is_dir = S_ISDIR (sb.st_mode);
    }

  if (!dest_is_dir)
    {
      if (target_directory)
    {
      error (0, 0, _("%s: specified target is not a directory"),
         quote (dest));
      usage (EXIT_FAILURE);
    }

      if (n_files > 1)
    {
      error (0, 0,
     _("copying multiple files, but last argument %s is not a directory"),
         quote (dest));
      usage (EXIT_FAILURE);
    }
    }

  if (dest_is_dir)
    {
      /* cp file1...filen edir
     Copy the files `file1' through `filen'
     to the existing directory `edir'. */
      int i;

      for (i = 0; i < n_files; i++)
    {
      char *dst_path;
      int parent_exists = 1; /* True if dir_name (dst_path) exists. */
      struct dir_attr *attr_list;
      char *arg_in_concat = NULL;
      char *arg = file[i];

      /* Trailing slashes are meaningful (i.e., maybe worth preserving)
         only in the source file names.  */
      if (remove_trailing_slashes)
        strip_trailing_slashes (arg);

      if (flag_path)
        {
          char *arg_no_trailing_slash;

          /* Use `arg' without trailing slashes in constructing destination
         file names.  Otherwise, we can end up trying to create a
         directory via `mkdir ("dst/foo/"...', which is not portable.
         It fails, due to the trailing slash, on at least
         NetBSD 1.[34] systems.  */
          ASSIGN_STRDUPA (arg_no_trailing_slash, arg);
          strip_trailing_slashes (arg_no_trailing_slash);

          /* Append all of `arg' (minus any trailing slash) to `dest'.  */
          dst_path = path_concat (dest, arg_no_trailing_slash,
                      &arg_in_concat);
          if (dst_path == NULL)
        xalloc_die ();

          /* For --parents, we have to make sure that the directory
             dir_name (dst_path) exists.  We may have to create a few
             leading directories. */
          parent_exists = !make_path_private (dst_path,
                          arg_in_concat - dst_path,
                          S_IRWXU,
                          (x->verbose
                           ? "%s -> %s\n" : NULL),
                          &attr_list, &new_dst,
                          x->xstat);
        }
      else
        {
          char *arg_base;
          /* Append the last component of `arg' to `dest'.  */

          ASSIGN_BASENAME_STRDUPA (arg_base, arg);
          /* For `cp -R source/.. dest', don't copy into `dest/..'. */
          dst_path = (STREQ (arg_base, "..")
              ? xstrdup (dest)
              : path_concat (dest, arg_base, NULL));
        }

      if (!parent_exists)
        {
          /* make_path_private failed, so don't even attempt the copy. */
          ret = 1;
        }
      else
        {
          int copy_into_self;
          ret |= copy (arg, dst_path, new_dst, x, &copy_into_self, NULL);

          if (flag_path)
        {
          ret |= re_protect (dst_path, arg_in_concat - dst_path,
                     attr_list, x);
        }
        }

      free (dst_path);
    }
      return ret;
    }
  else /* if (n_files == 1) */
    {
      char *new_dest;
      char *source;
      int unused;
      struct stat source_stats;

      if (flag_path)
    {
      error (0, 0,
           _("when preserving paths, the destination must be a directory"));
      usage (EXIT_FAILURE);
    }

      source = file[0];

      /* When the force and backup options have been specified and
     the source and destination are the same name for an existing
     regular file, convert the user's command, e.g.,
     `cp --force --backup foo foo' to `cp --force foo fooSUFFIX'
     where SUFFIX is determined by any version control options used.  */

      if (x->unlink_dest_after_failed_open
      && x->backup_type != none
      && STREQ (source, dest)
      && !new_dst && S_ISREG (sb.st_mode))
    {
      static struct cp_options x_tmp;

      new_dest = find_backup_file_name (dest, x->backup_type);
      /* Set x->backup_type to `none' so that the normal backup
         mechanism is not used when performing the actual copy.
         backup_type must be set to `none' only *after* the above
         call to find_backup_file_name -- that function uses
         backup_type to determine the suffix it applies.  */
      x_tmp = *x;
      x_tmp.backup_type = none;
      x = &x_tmp;

      if (new_dest == NULL)
        xalloc_die ();
    }

      /* When the destination is specified with a trailing slash and the
     source exists but is not a directory, convert the user's command
     `cp source dest/' to `cp source dest/basename(source)'.  Doing
     this ensures that the command `cp non-directory file/' will now
     fail rather than performing the copy.  COPY diagnoses the case of
     `cp directory non-directory'.  */

      else if (dest[strlen (dest) - 1] == '/'
      && lstat (source, &source_stats) == 0
      && !S_ISDIR (source_stats.st_mode))
    {
      char *source_base;

      ASSIGN_BASENAME_STRDUPA (source_base, source);
      new_dest = (char *) alloca (strlen (dest)
                      + strlen (source_base) + 1);
      stpcpy (stpcpy (new_dest, dest), source_base);
    }
      else
    {
      new_dest = (char *) dest;
    }

      return copy (source, new_dest, new_dst, x, &unused, NULL);
    }

  /* unreachable */
}

static void
cp_option_init (struct cp_options *x)
{
  x->copy_as_regular = 1;
  x->dereference = DEREF_UNDEFINED;
  x->unlink_dest_before_opening = 0;
  x->unlink_dest_after_failed_open = 0;
  x->hard_link = 0;
  x->interactive = I_UNSPECIFIED;
  x->myeuid = geteuid ();
  x->move_mode = 0;
  x->one_file_system = 0;

  x->preserve_ownership = 0;
  x->preserve_links = 0;
  x->preserve_mode = 0;
  x->preserve_timestamps = 0;

  x->require_preserve = 0;
  x->recursive = 0;
  x->sparse_mode = SPARSE_AUTO;
  x->symbolic_link = 0;
  x->set_mode = 0;
  x->mode = 0;

  /* Not used.  */
  x->stdin_tty = 0;

  /* Find out the current file creation mask, to knock the right bits
     when using chmod.  The creation mask is set to be liberal, so
     that created directories can be written, even if it would not
     have been allowed with the mask this process was started with.  */
  x->umask_kill = ~ umask (0);

  x->update = 0;
  x->verbose = 0;
  x->dest_info = NULL;
  x->src_info = NULL;
}

/* Given a string, ARG, containing a comma-separated list of arguments
   to the --preserve option, set the appropriate fields of X to ON_OFF.  */
static void
decode_preserve_arg (char const *arg, struct cp_options *x, int on_off)
{
  enum File_attribute
    {
      PRESERVE_MODE,
      PRESERVE_TIMESTAMPS,
      PRESERVE_OWNERSHIP,
      PRESERVE_LINK,
      PRESERVE_ALL
    };
  static enum File_attribute const preserve_vals[] =
    {
      PRESERVE_MODE, PRESERVE_TIMESTAMPS,
      PRESERVE_OWNERSHIP, PRESERVE_LINK, PRESERVE_ALL
    };

  /* Valid arguments to the `--preserve' option. */
  static char const* const preserve_args[] =
    {
      "mode", "timestamps",
      "ownership", "links", "all", 0
    };

  char *arg_writable = xstrdup (arg);
  char *s = arg_writable;
  do
    {
      /* find next comma */
      char *comma = strchr (s, ',');
      enum File_attribute val;

      /* If we found a comma, put a NUL in its place and advance.  */
      if (comma)
    *comma++ = 0;

      /* process S.  */
      val = XARGMATCH ("--preserve", s, preserve_args, preserve_vals);
      switch (val)
    {
    case PRESERVE_MODE:
      x->preserve_mode = on_off;
      break;

    case PRESERVE_TIMESTAMPS:
      x->preserve_timestamps = on_off;
      break;

    case PRESERVE_OWNERSHIP:
      x->preserve_ownership = on_off;
      break;

    case PRESERVE_LINK:
      x->preserve_links = on_off;
      break;

    case PRESERVE_ALL:
      x->preserve_mode = on_off;
      x->preserve_timestamps = on_off;
      x->preserve_ownership = on_off;
      x->preserve_links = on_off;
      break;

    default:
      abort ();
    }
      s = comma;
    }
  while (s);

  free (arg_writable);
}

int
main (int argc, char **argv)
{
  int c;
  int make_backups = 0;
  char *backup_suffix_string;
  char *version_control_string = NULL;
  struct cp_options x;
  int copy_contents = 0;
  char *target_directory = NULL;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  cp_option_init (&x);

  /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
     we'll actually use backup_suffix_string.  */
  backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");

  while ((c = getopt_long (argc, argv, "abdfHilLprsuvxPRS:V:", long_opts, NULL))
     != -1)
    {
      switch (c)
    {
    case 0:
      break;

    case SPARSE_OPTION:
      x.sparse_mode = XARGMATCH ("--sparse", optarg,
                     sparse_type_string, sparse_type);
      break;

    case 'a':       /* Like -dpPR. */
      x.dereference = DEREF_NEVER;
      x.preserve_links = 1;
      x.preserve_ownership = 1;
      x.preserve_mode = 1;
      x.preserve_timestamps = 1;
      x.require_preserve = 1;
      x.recursive = 1;
      break;

    case 'V':  /* FIXME: this is deprecated.  Remove it in 2001.  */
      error (0, 0,
         _("warning: --version-control (-V) is obsolete;  support for\
 it\nwill be removed in some future release.  Use --backup=%s instead."
           ), optarg);
      /* Fall through.  */

    case 'b':
      make_backups = 1;
      if (optarg)
        version_control_string = optarg;
      break;

    case COPY_CONTENTS_OPTION:
      copy_contents = 1;
      break;

    case 'd':
      x.preserve_links = 1;
      x.dereference = DEREF_NEVER;
      break;

    case 'f':
      x.unlink_dest_after_failed_open = 1;
      break;

    case 'H':
      x.dereference = DEREF_COMMAND_LINE_ARGUMENTS;
      break;

    case 'i':
      x.interactive = I_ASK_USER;
      break;

    case 'l':
      x.hard_link = 1;
      break;

    case 'L':
      x.dereference = DEREF_ALWAYS;
      break;

    case 'P':
      x.dereference = DEREF_NEVER;
      break;

    case NO_PRESERVE_ATTRIBUTES_OPTION:
      decode_preserve_arg (optarg, &x, 0);
      break;

    case PRESERVE_ATTRIBUTES_OPTION:
      if (optarg == NULL)
        {
          /* Fall through to the case for `p' below.  */
        }
      else
        {
          decode_preserve_arg (optarg, &x, 1);
          x.require_preserve = 1;
          break;
        }

    case 'p':
      x.preserve_ownership = 1;
      x.preserve_mode = 1;
      x.preserve_timestamps = 1;
      x.require_preserve = 1;
      break;

    case PARENTS_OPTION:
      flag_path = 1;
      break;

    case 'r':
    case 'R':
      x.recursive = 1;
      break;

    case REPLY_OPTION:
      x.interactive = XARGMATCH ("--reply", optarg,
                     reply_args, reply_vals);
      break;

    case UNLINK_DEST_BEFORE_OPENING:
      x.unlink_dest_before_opening = 1;
      break;

    case STRIP_TRAILING_SLASHES_OPTION:
      remove_trailing_slashes = 1;
      break;

    case 's':
#ifdef S_ISLNK
      x.symbolic_link = 1;
#else
      error (EXIT_FAILURE, 0,
         _("symbolic links are not supported on this system"));
#endif
      break;

    case TARGET_DIRECTORY_OPTION:
      target_directory = optarg;
      break;

    case 'u':
      x.update = 1;
      break;

    case 'v':
      x.verbose = 1;
      break;

    case 'x':
      x.one_file_system = 1;
      break;

    case 'S':
      make_backups = 1;
      backup_suffix_string = optarg;
      break;

    case_GETOPT_HELP_CHAR;

    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);

    default:
      usage (EXIT_FAILURE);
    }
    }

  if (x.hard_link && x.symbolic_link)
    {
      error (0, 0, _("cannot make both hard and symbolic links"));
      usage (EXIT_FAILURE);
    }

  if (backup_suffix_string)
    simple_backup_suffix = xstrdup (backup_suffix_string);

  x.backup_type = (make_backups
           ? xget_version (_("backup type"),
                   version_control_string)
           : none);

  if (x.preserve_mode == 1)
    x.umask_kill = ~ (mode_t) 0;

  if (x.dereference == DEREF_UNDEFINED)
    {
      if (x.recursive)
    /* This is compatible with FreeBSD.  */
    x.dereference = DEREF_NEVER;
      else
    x.dereference = DEREF_ALWAYS;
    }

  /* The key difference between -d (--no-dereference) and not is the version
     of `stat' to call.  */

  if (x.dereference == DEREF_NEVER)
    x.xstat = lstat;
  else
    {
      /* For DEREF_COMMAND_LINE_ARGUMENTS, x.xstat must be stat for
     each command line argument, but must later be `lstat' for
     any symlinks that are found via recursive traversal.  */
      x.xstat = stat;
    }

  if (x.recursive)
    x.copy_as_regular = copy_contents;

  /* If --force (-f) was specified and we're in link-creation mode,
     first remove any existing destination file.  */
  if (x.unlink_dest_after_failed_open && (x.hard_link || x.symbolic_link))
    x.unlink_dest_before_opening = 1;

  /* Allocate space for remembering copied and created files.  */

  hash_init ();

  exit_status |= do_copy (argc - optind, argv + optind, target_directory, &x);

  forget_all ();

  exit (exit_status);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/copy.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/copy.c used in: Step 958
/* copy.c -- core functions for copying files and directories
   Copyright (C) 89, 90, 91, 1995-2003 Free Software Foundation.

   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.  */

/* Extracted from cp.c and librarified by Jim Meyering.  */

#ifdef _AIX
 #pragma alloca
#endif

#include <config.h>
#include <stdio.h>
#include <assert.h>
#include <sys/types.h>

#if HAVE_HURD_H
# include <hurd.h>
#endif

#include "system.h"
#include "error.h"
#include "backupfile.h"
#include "savedir.h"
#include "copy.h"
#include "cp-hash.h"
#include "hash.h"
#include "hash-pjw.h"
#include "same.h"
#include "dirname.h"
#include "full-write.h"
#include "path-concat.h"
#include "quote.h"
#include "same.h"
#include "xreadlink.h"

#define DO_CHOWN(Chown, File, New_uid, New_gid)             \
  (Chown (File, New_uid, New_gid)                   \
   /* If non-root uses -p, it's ok if we can't preserve ownership.  \
      But root probably wants to know, e.g. if NFS disallows it,    \
      or if the target system doesn't support file ownership.  */   \
   && ((errno != EPERM && errno != EINVAL) || x->myeuid == 0))

#define SAME_OWNER(A, B) ((A).st_uid == (B).st_uid)
#define SAME_GROUP(A, B) ((A).st_gid == (B).st_gid)
#define SAME_OWNER_AND_GROUP(A, B) (SAME_OWNER (A, B) && SAME_GROUP (A, B))

#define UNWRITABLE(File_name, File_mode)        \
  ( /* euidaccess is not meaningful for symlinks */ \
    ! S_ISLNK (File_mode)               \
    && euidaccess (File_name, W_OK) != 0)

struct dir_list
{
  struct dir_list *parent;
  ino_t ino;
  dev_t dev;
};

/* Describe a just-created or just-renamed destination file.  */
struct F_triple
{
  char const* name;
  ino_t st_ino;
  dev_t st_dev;
};

/* Initial size of the above hash table.  */
#define DEST_INFO_INITIAL_CAPACITY 61

int euidaccess ();
int yesno ();

static int copy_internal (const char *src_path, const char *dst_path,
              int new_dst, dev_t device,
              struct dir_list *ancestors,
              const struct cp_options *x,
              int command_line_arg,
              int *copy_into_self,
              int *rename_succeeded);

/* Pointers to the file names:  they're used in the diagnostic that is issued
   when we detect the user is trying to copy a directory into itself.  */
static char const *top_level_src_path;
static char const *top_level_dst_path;

/* The invocation name of this program.  */
extern char *program_name;

/* Encapsulate selection of the file mode to be applied to
   new non-directories.  */

static mode_t
get_dest_mode (const struct cp_options *option, mode_t mode)
{
  /* In some applications (e.g., install), use precisely the
     specified mode.  */
  if (option->set_mode)
    return option->mode;

  /* Honor the umask for `cp', but not for `mv' or `cp -p'.
     In addition, `cp' without -p must clear the set-user-ID and set-group-ID
     bits.  POSIX requires it do that when creating new files.  */
  if (!option->move_mode && !option->preserve_mode)
    mode &= (option->umask_kill & ~(S_ISUID | S_ISGID));

  return mode;
}

/* FIXME: describe */
/* FIXME: rewrite this to use a hash table so we avoid the quadratic
   performance hit that's probably noticeable only on trees deeper
   than a few hundred levels.  See use of active_dir_map in remove.c  */

static int
is_ancestor (const struct stat *sb, const struct dir_list *ancestors)
{
  while (ancestors != 0)
    {
      if (ancestors->ino == sb->st_ino && ancestors->dev == sb->st_dev)
    return 1;
      ancestors = ancestors->parent;
    }
  return 0;
}

/* Read the contents of the directory SRC_PATH_IN, and recursively
   copy the contents to DST_PATH_IN.  NEW_DST is nonzero if
   DST_PATH_IN is a directory that was created previously in the
   recursion.   SRC_SB and ANCESTORS describe SRC_PATH_IN.
   Set *COPY_INTO_SELF to nonzero if SRC_PATH_IN is a parent of
   (or the same as) DST_PATH_IN;  otherwise, set it to zero.
   Return 0 if successful, -1 if an error occurs. */

static int
copy_dir (const char *src_path_in, const char *dst_path_in, int new_dst,
      const struct stat *src_sb, struct dir_list *ancestors,
      const struct cp_options *x, int *copy_into_self)
{
  char *name_space;
  char *namep;
  struct cp_options non_command_line_options = *x;
  int ret = 0;

  name_space = savedir (src_path_in);
  if (name_space == NULL)
    {
      /* This diagnostic is a bit vague because savedir can fail in
         several different ways.  */
      error (0, errno, _("cannot access %s"), quote (src_path_in));
      return -1;
    }

  /* For cp's -H option, dereference command line arguments, but do not
     dereference symlinks that are found via recursive traversal.  */
  if (x->dereference == DEREF_COMMAND_LINE_ARGUMENTS)
    non_command_line_options.xstat = lstat;

  namep = name_space;
  while (*namep != '\0')
    {
      int local_copy_into_self;
      char *src_path = path_concat (src_path_in, namep, NULL);
      char *dst_path = path_concat (dst_path_in, namep, NULL);

      if (dst_path == NULL || src_path == NULL)
    xalloc_die ();

      ret |= copy_internal (src_path, dst_path, new_dst, src_sb->st_dev,
                ancestors, &non_command_line_options, 0,
                &local_copy_into_self, NULL);
      *copy_into_self |= local_copy_into_self;

      free (dst_path);
      free (src_path);

      namep += strlen (namep) + 1;
    }
  free (name_space);
  return -ret;
}

/* Copy a regular file from SRC_PATH to DST_PATH.
   If the source file contains holes, copies holes and blocks of zeros
   in the source file as holes in the destination file.
   (Holes are read as zeroes by the `read' system call.)
   Use DST_MODE as the 3rd argument in the call to open.
   X provides many option settings.
   Return 0 if successful, -1 if an error occurred.
   *NEW_DST is as in copy_internal.  SRC_SB is the result
   of calling xstat (aka stat in this case) on SRC_PATH.  */

static int
copy_reg (const char *src_path, const char *dst_path,
      const struct cp_options *x, mode_t dst_mode, int *new_dst,
      struct stat const *src_sb)
{
  char *buf;
  int buf_size;
  int dest_desc;
  int source_desc;
  struct stat sb;
  struct stat src_open_sb;
  char *cp;
  int *ip;
  int return_val = 0;
  off_t n_read_total = 0;
  int last_write_made_hole = 0;
  int make_holes = (x->sparse_mode == SPARSE_ALWAYS);

  source_desc = open (src_path, O_RDONLY);
  if (source_desc < 0)
    {
      error (0, errno, _("cannot open %s for reading"), quote (src_path));
      return -1;
    }

  if (fstat (source_desc, &src_open_sb))
    {
      error (0, errno, _("cannot fstat %s"), quote (src_path));
      return_val = -1;
      goto close_src_desc;
    }

  /* Compare the source dev/ino from the open file to the incoming,
     saved ones obtained via a previous call to stat.  */
  if (! SAME_INODE (*src_sb, src_open_sb))
    {
      error (0, 0,
         _("skipping file %s, as it was replaced while being copied"),
         quote (src_path));
      return_val = -1;
      goto close_src_desc;
    }

  /* These semantics are required for cp.
     The if-block will be taken in move_mode.  */
  if (*new_dst)
    {
      dest_desc = open (dst_path, O_WRONLY | O_CREAT, dst_mode);
    }
  else
    {
      dest_desc = open (dst_path, O_WRONLY | O_TRUNC, dst_mode);

      if (dest_desc < 0 && x->unlink_dest_after_failed_open)
    {
      if (unlink (dst_path))
        {
          error (0, errno, _("cannot remove %s"), quote (dst_path));
          return_val = -1;
          goto close_src_desc;
        }

      /* Tell caller that the destination file was unlinked.  */
      *new_dst = 1;

      /* Try the open again, but this time with different flags.  */
      dest_desc = open (dst_path, O_WRONLY | O_CREAT, dst_mode);
    }
    }

  if (dest_desc < 0)
    {
      error (0, errno, _("cannot create regular file %s"), quote (dst_path));
      return_val = -1;
      goto close_src_desc;
    }

  /* Determine the optimal buffer size.  */

  if (fstat (dest_desc, &sb))
    {
      error (0, errno, _("cannot fstat %s"), quote (dst_path));
      return_val = -1;
      goto close_src_and_dst_desc;
    }

  buf_size = ST_BLKSIZE (sb);

#if HAVE_STRUCT_STAT_ST_BLOCKS
  if (x->sparse_mode == SPARSE_AUTO && S_ISREG (sb.st_mode))
    {
      /* Use a heuristic to determine whether SRC_PATH contains any
     sparse blocks. */

      if (fstat (source_desc, &sb))
    {
      error (0, errno, _("cannot fstat %s"), quote (src_path));
      return_val = -1;
      goto close_src_and_dst_desc;
    }

      /* If the file has fewer blocks than would normally
     be needed for a file of its size, then
     at least one of the blocks in the file is a hole. */
      if (S_ISREG (sb.st_mode)
      && sb.st_size / ST_NBLOCKSIZE > ST_NBLOCKS (sb))
    make_holes = 1;
    }
#endif

  /* Make a buffer with space for a sentinel at the end.  */

  buf = (char *) alloca (buf_size + sizeof (int));

  for (;;)
    {
      ssize_t n_read = read (source_desc, buf, buf_size);
      if (n_read < 0)
    {
#ifdef EINTR
      if (errno == EINTR)
        continue;
#endif
      error (0, errno, _("reading %s"), quote (src_path));
      return_val = -1;
      goto close_src_and_dst_desc;
    }
      if (n_read == 0)
    break;

      n_read_total += n_read;

      ip = 0;
      if (make_holes)
    {
      buf[n_read] = 1;  /* Sentinel to stop loop.  */

      /* Find first nonzero *word*, or the word with the sentinel.  */

      ip = (int *) buf;
      while (*ip++ == 0)
        ;

      /* Find the first nonzero *byte*, or the sentinel.  */

      cp = (char *) (ip - 1);
      while (*cp++ == 0)
        ;

      /* If we found the sentinel, the whole input block was zero,
         and we can make a hole.  */

      if (cp > buf + n_read)
        {
          /* Make a hole.  */
          if (lseek (dest_desc, (off_t) n_read, SEEK_CUR) < 0L)
        {
          error (0, errno, _("cannot lseek %s"), quote (dst_path));
          return_val = -1;
          goto close_src_and_dst_desc;
        }
          last_write_made_hole = 1;
        }
      else
        /* Clear to indicate that a normal write is needed. */
        ip = 0;
    }
      if (ip == 0)
    {
      size_t n = n_read;
      if (full_write (dest_desc, buf, n) != n)
        {
          error (0, errno, _("writing %s"), quote (dst_path));
          return_val = -1;
          goto close_src_and_dst_desc;
        }
      last_write_made_hole = 0;
    }
    }

  /* If the file ends with a `hole', something needs to be written at
     the end.  Otherwise the kernel would truncate the file at the end
     of the last write operation.  */

  if (last_write_made_hole)
    {
#if HAVE_FTRUNCATE
      /* Write a null character and truncate it again.  */
      if (full_write (dest_desc, "", 1) != 1
      || ftruncate (dest_desc, n_read_total) < 0)
#else
      /* Seek backwards one character and write a null.  */
      if (lseek (dest_desc, (off_t) -1, SEEK_CUR) < 0L
      || full_write (dest_desc, "", 1) != 1)
#endif
    {
      error (0, errno, _("writing %s"), quote (dst_path));
      return_val = -1;
    }
    }

close_src_and_dst_desc:
  if (close (dest_desc) < 0)
    {
      error (0, errno, _("closing %s"), quote (dst_path));
      return_val = -1;
    }
close_src_desc:
  if (close (source_desc) < 0)
    {
      error (0, errno, _("closing %s"), quote (src_path));
      return_val = -1;
    }

  return return_val;
}

/* Return nonzero if it's ok that the source and destination
   files are the `same' by some measure.  The goal is to avoid
   making the `copy' operation remove both copies of the file
   in that case, while still allowing the user to e.g., move or
   copy a regular file onto a symlink that points to it.
   Try to minimize the cost of this function in the common case.  */

static int
same_file_ok (const char *src_path, const struct stat *src_sb,
          const char *dst_path, const struct stat *dst_sb,
          const struct cp_options *x, int *return_now)
{
  const struct stat *src_sb_link;
  const struct stat *dst_sb_link;
  struct stat tmp_dst_sb;
  struct stat tmp_src_sb;

  int same_link;
  int same = (SAME_INODE (*src_sb, *dst_sb));

  *return_now = 0;

  /* FIXME: this should (at the very least) be moved into the following
     if-block.  More likely, it should be removed, because it inhibits
     making backups.  But removing it will result in a change in behavior
     that will probably have to be documented -- and tests will have to
     be updated.  */
  if (same && x->hard_link)
    {
      *return_now = 1;
      return 1;
    }

  if (x->xstat == lstat)
    {
      same_link = same;

      /* If both the source and destination files are symlinks (and we'll
     know this here IFF preserving symlinks (aka xstat == lstat),
     then it's ok -- as long as they are distinct.  */
      if (S_ISLNK (src_sb->st_mode) && S_ISLNK (dst_sb->st_mode))
    return ! same_name (src_path, dst_path);

      src_sb_link = src_sb;
      dst_sb_link = dst_sb;
    }
  else
    {
      if (!same)
    return 1;

      if (lstat (dst_path, &tmp_dst_sb)
      || lstat (src_path, &tmp_src_sb))
    return 1;

      src_sb_link = &tmp_src_sb;
      dst_sb_link = &tmp_dst_sb;

      same_link = SAME_INODE (*src_sb_link, *dst_sb_link);

      /* If both are symlinks, then it's ok, but only if the destination
     will be unlinked before being opened.  This is like the test
     above, but with the addition of the unlink_dest_before_opening
     conjunct because otherwise, with two symlinks to the same target,
     we'd end up truncating the source file.  */
      if (S_ISLNK (src_sb_link->st_mode) && S_ISLNK (dst_sb_link->st_mode)
      && x->unlink_dest_before_opening)
    return 1;
    }

  /* The backup code ensures there's a copy, so it's usually ok to
     remove any destination file.  One exception is when both
     source and destination are the same directory entry.  In that
     case, moving the destination file aside (in making the backup)
     would also rename the source file and result in an error.  */
  if (x->backup_type != none)
    {
      if (!same_link)
    {
      /* In copy mode when dereferencing symlinks, if the source is a
         symlink and the dest is not, then backing up the destination
         (moving it aside) would make it a dangling symlink, and the
         subsequent attempt to open it in copy_reg would fail with
         a misleading diagnostic.  Avoid that by returning zero in
         that case so the caller can make cp (or mv when it has to
         resort to reading the source file) fail now.  */

      /* FIXME-note: even with the following kludge, we can still provoke
         the offending diagnostic.  It's just a little harder to do :-)
         $ rm -f a b c; touch c; ln -s c b; ln -s b a; cp -b a b
         cp: cannot open `a' for reading: No such file or directory
         That's misleading, since a subsequent `ls' shows that `a'
         is still there.
         One solution would be to open the source file *before* moving
         aside the destination, but that'd involve a big rewrite. */
      if ( ! x->move_mode
           && x->dereference != DEREF_NEVER
           && S_ISLNK (src_sb_link->st_mode)
           && ! S_ISLNK (dst_sb_link->st_mode))
        return 0;

      return 1;
    }

      return ! same_name (src_path, dst_path);
    }

#if 0
  /* FIXME: use or remove */

  /* If we're making a backup, we'll detect the problem case in
     copy_reg because SRC_PATH will no longer exist.  Allowing
     the test to be deferred lets cp do some useful things.
     But when creating hardlinks and SRC_PATH is a symlink
     but DST_PATH is not we must test anyway.  */
  if (x->hard_link
      || !S_ISLNK (src_sb_link->st_mode)
      || S_ISLNK (dst_sb_link->st_mode))
    return 1;

  if (x->dereference != DEREF_NEVER)
    return 1;
#endif

  /* They may refer to the same file if we're in move mode and the
     target is a symlink.  That is ok, since we remove any existing
     destination file before opening it -- via `rename' if they're on
     the same file system, via `unlink (DST_PATH)' otherwise.
     It's also ok if they're distinct hard links to the same file.  */
  if ((x->move_mode || x->unlink_dest_before_opening)
      && (S_ISLNK (dst_sb_link->st_mode)
      || (same_link && !same_name (src_path, dst_path))))
    return 1;

  /* If neither is a symlink, then it's ok as long as they aren't
     hard links to the same file.  */
  if (!S_ISLNK (src_sb_link->st_mode) && !S_ISLNK (dst_sb_link->st_mode))
    {
      if (!SAME_INODE (*src_sb_link, *dst_sb_link))
    return 1;

      /* If they are the same file, it's ok if we're making hard links.  */
      if (x->hard_link)
    {
      *return_now = 1;
      return 1;
    }
    }

  /* It's ok to remove a destination symlink.  But that works only when we
     unlink before opening the destination and when the source and destination
     files are on the same partition.  */
  if (x->unlink_dest_before_opening
      && S_ISLNK (dst_sb_link->st_mode))
    return dst_sb_link->st_dev == src_sb_link->st_dev;

  if (x->xstat == lstat)
    {
      if ( ! S_ISLNK (src_sb_link->st_mode))
    tmp_src_sb = *src_sb_link;
      else if (stat (src_path, &tmp_src_sb))
    return 1;

      if ( ! S_ISLNK (dst_sb_link->st_mode))
    tmp_dst_sb = *dst_sb_link;
      else if (stat (dst_path, &tmp_dst_sb))
    return 1;

      if ( ! SAME_INODE (tmp_src_sb, tmp_dst_sb))
    return 1;

      /* FIXME: shouldn't this be testing whether we're making symlinks?  */
      if (x->hard_link)
    {
      *return_now = 1;
      return 1;
    }
    }

  return 0;
}

static void
overwrite_prompt (char const *dst_path, struct stat const *dst_sb)
{
  if (euidaccess (dst_path, W_OK) != 0)
    {
      fprintf (stderr,
           _("%s: overwrite %s, overriding mode %04lo? "),
           program_name, quote (dst_path),
           (unsigned long) (dst_sb->st_mode & CHMOD_MODE_BITS));
    }
  else
    {
      fprintf (stderr, _("%s: overwrite %s? "),
           program_name, quote (dst_path));
    }
}

/* Hash an F_triple.  */
static unsigned int
triple_hash (void const *x, unsigned int table_size)
{
  struct F_triple const *p = x;

  /* Also take the name into account, so that when moving N hard links to the
     same file (all listed on the command line) all into the same directory,
     we don't experience any N^2 behavior.  */
  /* FIXME-maybe: is it worth the overhead of doing this
     just to avoid N^2 in such an unusual case?  N would have
     to be very large to make the N^2 factor noticable, and
     one would probably encounter a limit on the length of
     a command line before it became a problem.  */
  unsigned int tmp = hash_pjw (p->name, table_size);

  /* Ignoring the device number here should be fine.  */
  return (tmp | p->st_ino) % table_size;
}

/* Hash an F_triple.  */
static unsigned int
triple_hash_no_name (void const *x, unsigned int table_size)
{
  struct F_triple const *p = x;

  /* Ignoring the device number here should be fine.  */
  return p->st_ino % table_size;
}

/* Compare two F_triple structs.  */
static bool
triple_compare (void const *x, void const *y)
{
  struct F_triple const *a = x;
  struct F_triple const *b = y;
  return (SAME_INODE (*a, *b) && same_name (a->name, b->name)) ? true : false;
}

/* Free an F_triple.  */
static void
triple_free (void *x)
{
  struct F_triple *a = x;
  free ((char *) (a->name));
  free (a);
}

/* Initialize the hash table implementing a set of F_triple entries
   corresponding to destination files.  */
void
dest_info_init (struct cp_options *x)
{
  x->dest_info
    = hash_initialize (DEST_INFO_INITIAL_CAPACITY,
               NULL,
               triple_hash,
               triple_compare,
               triple_free);
}

/* Initialize the hash table implementing a set of F_triple entries
   corresponding to source files listed on the command line.  */
void
src_info_init (struct cp_options *x)
{

  /* Note that we use triple_hash_no_name here.
     Contrast with the use of triple_hash above.
     That is necessary because a source file may be specified
     in many different ways.  We want to warn about this
       cp a a d/
     as well as this:
       cp a ./a d/
  */
  x->src_info
    = hash_initialize (DEST_INFO_INITIAL_CAPACITY,
               NULL,
               triple_hash_no_name,
               triple_compare,
               triple_free);
}

/* Return nonzero if there is an entry in hash table, HT,
   for the file described by FILENAME and STATS.
   Otherwise, return zero.  */
static int
seen_file (Hash_table const *ht, char const *filename,
       struct stat const *stats)
{
  struct F_triple new_ent;

  if (ht == NULL)
    return 0;

  new_ent.name = filename;
  new_ent.st_ino = stats->st_ino;
  new_ent.st_dev = stats->st_dev;

  return !!hash_lookup (ht, &new_ent);
}

/* Record destination filename, FILENAME, and dev/ino from *STATS,
   in the hash table, HT.  If HT is NULL, return immediately.
   If STATS is NULL, call lstat on FILENAME to get the device
   and inode numbers.  If that lstat fails, simply return.
   If memory allocation fails, exit immediately.  */
static void
record_file (Hash_table *ht, char const *filename,
         struct stat const *stats)
{
  struct F_triple *ent;

  if (ht == NULL)
    return;

  ent = (struct F_triple *) xmalloc (sizeof *ent);
  ent->name = xstrdup (filename);
  if (stats)
    {
      ent->st_ino = stats->st_ino;
      ent->st_dev = stats->st_dev;
    }
  else
    {
      struct stat sb;
      if (lstat (filename, &sb))
    return;
      ent->st_ino = sb.st_ino;
      ent->st_dev = sb.st_dev;
    }

  {
    struct F_triple *ent_from_table = hash_insert (ht, ent);
    if (ent_from_table == NULL)
      {
    /* Insertion failed due to lack of memory.  */
    xalloc_die ();
      }

    if (ent_from_table != ent)
      {
    /* There was alread a matching entry in the table, so ENT was
       not inserted.  Free it.  */
    triple_free (ent);
      }
  }
}

/* Copy the file SRC_PATH to the file DST_PATH.  The files may be of
   any type.  NEW_DST should be nonzero if the file DST_PATH cannot
   exist because its parent directory was just created; NEW_DST should
   be zero if DST_PATH might already exist.  DEVICE is the device
   number of the parent directory, or 0 if the parent of this file is
   not known.  ANCESTORS points to a linked, null terminated list of
   devices and inodes of parent directories of SRC_PATH.  COMMAND_LINE_ARG
   is nonzero iff SRC_PATH was specified on the command line.
   Set *COPY_INTO_SELF to nonzero if SRC_PATH is a parent of (or the
   same as) DST_PATH;  otherwise, set it to zero.
   Return 0 if successful, 1 if an error occurs. */

static int
copy_internal (const char *src_path, const char *dst_path,
           int new_dst,
           dev_t device,
           struct dir_list *ancestors,
           const struct cp_options *x,
           int command_line_arg,
           int *copy_into_self,
           int *rename_succeeded)
{
  struct stat src_sb;
  struct stat dst_sb;
  mode_t src_mode;
  mode_t src_type;
  char *earlier_file = NULL;
  char *dst_backup = NULL;
  int backup_succeeded = 0;
  int delayed_fail;
  int copied_as_regular = 0;
  int ran_chown = 0;
  int preserve_metadata;

  if (x->move_mode && rename_succeeded)
    *rename_succeeded = 0;

  *copy_into_self = 0;
  if ((*(x->xstat)) (src_path, &src_sb))
    {
      error (0, errno, _("cannot stat %s"), quote (src_path));
      return 1;
    }

  src_type = src_sb.st_mode;

  src_mode = src_sb.st_mode;

  if (S_ISDIR (src_type) && !x->recursive)
    {
      error (0, 0, _("omitting directory %s"), quote (src_path));
      return 1;
    }

  /* Detect the case in which the same source file appears more than
     once on the command line and no backup option has been selected.
     If so, simply warn and don't copy it the second time.
     This check is enabled only if x->src_info is non-NULL.  */
  if (command_line_arg)
    {
      if ( ! S_ISDIR (src_sb.st_mode)
       && x->backup_type == none
       && seen_file (x->src_info, src_path, &src_sb))
    {
      error (0, 0, _("warning: source file %s specified more than once"),
         quote (src_path));
      return 0;
    }

      record_file (x->src_info, src_path, &src_sb);
    }

  if (!new_dst)
    {
      if ((*(x->xstat)) (dst_path, &dst_sb))
    {
      if (errno != ENOENT)
        {
          error (0, errno, _("cannot stat %s"), quote (dst_path));
          return 1;
        }
      else
        {
          new_dst = 1;
        }
    }
      else
    {
      int return_now;
      int ok = same_file_ok (src_path, &src_sb, dst_path, &dst_sb,
                 x, &return_now);
      if (return_now)
        return 0;

      if (! ok)
        {
          error (0, 0, _("%s and %s are the same file"),
             quote_n (0, src_path), quote_n (1, dst_path));
          return 1;
        }

      if (!S_ISDIR (dst_sb.st_mode))
        {
          if (S_ISDIR (src_type))
        {
          error (0, 0,
             _("cannot overwrite non-directory %s with directory %s"),
             quote_n (0, dst_path), quote_n (1, src_path));
          return 1;
        }

          /* Don't let the user destroy their data, even if they try hard:
         This mv command must fail (likewise for cp):
           rm -rf a b c; mkdir a b c; touch a/f b/f; mv a/f b/f c
         Otherwise, the contents of b/f would be lost.
         In the case of `cp', b/f would be lost if the user simulated
         a move using cp and rm.
         Note that it works fine if you use --backup=numbered.  */
          if (command_line_arg
          && x->backup_type != numbered
          && seen_file (x->dest_info, dst_path, &dst_sb))
        {
          error (0, 0,
             _("will not overwrite just-created %s with %s"),
             quote_n (0, dst_path), quote_n (1, src_path));
          return 1;
        }
        }

      if (!S_ISDIR (src_type))
        {
          if (S_ISDIR (dst_sb.st_mode))
        {
          error (0, 0,
               _("cannot overwrite directory %s with non-directory"),
             quote (dst_path));
          return 1;
        }

          if (x->update && MTIME_CMP (src_sb, dst_sb) <= 0)
        {
          /* We're using --update and the source file is older
             than the destination file, so there is no need to
             copy or move.  */
          /* Pretend the rename succeeded, so the caller (mv)
             doesn't end up removing the source file.  */
          if (rename_succeeded)
            *rename_succeeded = 1;
          return 0;
        }
        }

      /* When there is an existing destination file, we may end up
         returning early, and hence not copying/moving the file.
         This may be due to an interactive `negative' reply to the
         prompt about the existing file.  It may also be due to the
         use of the --reply=no option.  */
      if (!S_ISDIR (src_type))
        {
          /* cp and mv treat -i and -f differently.  */
          if (x->move_mode)
        {
          if ((x->interactive == I_ALWAYS_NO
               && UNWRITABLE (dst_path, dst_sb.st_mode))
              || ((x->interactive == I_ASK_USER
               || (x->interactive == I_UNSPECIFIED
                   && x->stdin_tty
                   && UNWRITABLE (dst_path, dst_sb.st_mode)))
              && (overwrite_prompt (dst_path, &dst_sb), 1)
              && ! yesno ()))
            {
              /* Pretend the rename succeeded, so the caller (mv)
             doesn't end up removing the source file.  */
              if (rename_succeeded)
            *rename_succeeded = 1;
              return 0;
            }
        }
          else
        {
          if (x->interactive == I_ALWAYS_NO
              || (x->interactive == I_ASK_USER
              && (overwrite_prompt (dst_path, &dst_sb), 1)
              && ! yesno ()))
            {
              return 0;
            }
        }
        }

      if (x->move_mode)
        {
          /* In move_mode, DEST may not be an existing directory.  */
          if (S_ISDIR (dst_sb.st_mode))
        {
          error (0, 0, _("cannot overwrite directory %s"),
             quote (dst_path));
          return 1;
        }

          /* Don't allow user to move a directory onto a non-directory.  */
          if (S_ISDIR (src_sb.st_mode) && !S_ISDIR (dst_sb.st_mode))
        {
          error (0, 0,
               _("cannot move directory onto non-directory: %s -> %s"),
             quote_n (0, src_path), quote_n (0, dst_path));
          return 1;
        }
        }

      if (x->backup_type != none && !S_ISDIR (dst_sb.st_mode))
        {
          char *tmp_backup = find_backup_file_name (dst_path,
                            x->backup_type);
          if (tmp_backup == NULL)
        xalloc_die ();

          /* Detect (and fail) when creating the backup file would
         destroy the source file.  Before, running the commands
         cd /tmp; rm -f a a~; : > a; echo A > a~; cp --b=simple a~ a
         would leave two zero-length files: a and a~.  */
          /* FIXME: but simply change e.g., the final a~ to `./a~'
         and the source will still be destroyed.  */
          if (STREQ (tmp_backup, src_path))
        {
          const char *fmt;
          fmt = (x->move_mode
         ? _("backing up %s would destroy source;  %s not moved")
         : _("backing up %s would destroy source;  %s not copied"));
          error (0, 0, fmt,
             quote_n (0, dst_path),
             quote_n (1, src_path));
          free (tmp_backup);
          return 1;
        }

          dst_backup = (char *) alloca (strlen (tmp_backup) + 1);
          strcpy (dst_backup, tmp_backup);
          free (tmp_backup);
          if (rename (dst_path, dst_backup))
        {
          if (errno != ENOENT)
            {
              error (0, errno, _("cannot backup %s"), quote (dst_path));
              return 1;
            }
          else
            {
              dst_backup = NULL;
            }
        }
          else
        {
          backup_succeeded = 1;
        }
          new_dst = 1;
        }
      else if (! S_ISDIR (dst_sb.st_mode)
           && (x->unlink_dest_before_opening
               || (x->xstat == lstat
               && ! S_ISREG (src_sb.st_mode))))
        {
          if (unlink (dst_path) && errno != ENOENT)
        {
          error (0, errno, _("cannot remove %s"), quote (dst_path));
          return 1;
        }
          new_dst = 1;
        }
    }
    }

  /* If the source is a directory, we don't always create the destination
     directory.  So --verbose should not announce anything until we're
     sure we'll create a directory. */
  if (x->verbose && !S_ISDIR (src_type))
    {
      printf ("%s -> %s", quote_n (0, src_path), quote_n (1, dst_path));
      if (backup_succeeded)
    printf (_(" (backup: %s)"), quote (dst_backup));
      putchar ('\n');
    }

  /* Associate the destination path with the source device and inode
     so that if we encounter a matching dev/ino pair in the source tree
     we can arrange to create a hard link between the corresponding names
     in the destination tree.

     Sometimes, when preserving links, we have to record dev/ino even
     though st_nlink == 1:
     - when using -H and processing a command line argument;
    that command line argument could be a symlink pointing to another
    command line argument.  With `cp -H --preserve=link', we hard-link
    those two destination files.
     - likewise for -L except that it applies to all files, not just
    command line arguments.

     Also record directory dev/ino when using --recursive.  We'll use that
     info to detect this problem: cp -R dir dir.  FIXME-maybe: ideally,
     directory info would be recorded in a separate hash table, since
     such entries are useful only while a single command line hierarchy
     is being copied -- so that separate table could be cleared between
     command line args.  Using the same hash table to preserve hard
     links means that it may not be cleared.  */

  if ((x->preserve_links
       && (1 < src_sb.st_nlink
       || (command_line_arg
           && x->dereference == DEREF_COMMAND_LINE_ARGUMENTS)
       || x->dereference == DEREF_ALWAYS))
      || (x->recursive && S_ISDIR (src_type)))
    {
      earlier_file = remember_copied (dst_path, src_sb.st_ino, src_sb.st_dev);
    }

  /* Did we copy this inode somewhere else (in this command line argument)
     and therefore this is a second hard link to the inode?  */

  if (earlier_file)
    {
      /* Avoid damaging the destination filesystem by refusing to preserve
     hard-linked directories (which are found at least in Netapp snapshot
     directories).  */
      if (S_ISDIR (src_type))
    {
      /* If src_path and earlier_file refer to the same directory entry,
         then warn about copying a directory into itself.  */
      if (same_name (src_path, earlier_file))
        {
          error (0, 0, _("cannot copy a directory, %s, into itself, %s"),
             quote_n (0, top_level_src_path),
             quote_n (1, top_level_dst_path));
          *copy_into_self = 1;
        }
      else
        {
          error (0, 0, _("will not create hard link %s to directory %s"),
             quote_n (0, dst_path), quote_n (1, earlier_file));
        }

      goto un_backup;
    }

      {
    int link_failed;

    link_failed = link (earlier_file, dst_path);

    /* If the link failed because of an existing destination,
       remove that file and then call link again.  */
    if (link_failed && errno == EEXIST)
      {
        if (unlink (dst_path))
          {
        error (0, errno, _("cannot remove %s"), quote (dst_path));
        goto un_backup;
          }
        link_failed = link (earlier_file, dst_path);
      }

    if (link_failed)
      {
        error (0, errno, _("cannot create hard link %s to %s"),
           quote_n (0, dst_path), quote_n (1, earlier_file));
        goto un_backup;
      }

    return 0;
      }
    }

  if (x->move_mode)
    {
      if (rename (src_path, dst_path) == 0)
    {
      if (x->verbose && S_ISDIR (src_type))
        printf ("%s -> %s\n", quote_n (0, src_path), quote_n (1, dst_path));
      if (rename_succeeded)
        *rename_succeeded = 1;

      if (command_line_arg)
        {
          /* Record destination dev/ino/filename, so that if we are asked
         to overwrite that file again, we can detect it and fail.  */
          /* It's fine to use the _source_ stat buffer (src_sb) to get the
             _destination_ dev/ino, since the rename above can't have
         changed those, and `mv' always uses lstat.
         We could limit it further by operating
         only on non-directories.  */
          record_file (x->dest_info, dst_path, &src_sb);
        }

      return 0;
    }

      /* FIXME: someday, consider what to do when moving a directory into
     itself but when source and destination are on different devices.  */

      /* This happens when attempting to rename a directory to a
     subdirectory of itself.  */
      if (errno == EINVAL

      /* When src_path is on an NFS file system, some types of
         clients, e.g., SunOS4.1.4 and IRIX-5.3, set errno to EIO
         instead.  Testing for this here risks misinterpreting a real
         I/O error as an attempt to move a directory into itself, so
         FIXME: consider not doing this.  */
      || errno == EIO

      /* And with SunOS-4.1.4 client and OpenBSD-2.3 server,
         we get ENOTEMPTY.  */
      || errno == ENOTEMPTY)
    {
      /* FIXME: this is a little fragile in that it relies on rename(2)
         failing with a specific errno value.  Expect problems on
         non-POSIX systems.  */
      error (0, 0, _("cannot move %s to a subdirectory of itself, %s"),
         quote_n (0, top_level_src_path),
         quote_n (1, top_level_dst_path));

      /* Note that there is no need to call forget_created here,
         (compare with the other calls in this file) since the
         destination directory didn't exist before.  */

      *copy_into_self = 1;
      /* FIXME-cleanup: Don't return zero here; adjust mv.c accordingly.
         The only caller that uses this code (mv.c) ends up setting its
         exit status to nonzero when copy_into_self is nonzero.  */
      return 0;
    }

      /* WARNING: there probably exist systems for which an inter-device
     rename fails with a value of errno not handled here.
     If/as those are reported, add them to the condition below.
     If this happens to you, please do the following and send the output
     to the bug-reporting address (e.g., in the output of cp --help):
       touch k; perl -e 'rename "k","/tmp/k" or print "$!(",$!+0,")\n"'
     where your current directory is on one partion and /tmp is the other.
     Also, please try to find the E* errno macro name corresponding to
     the diagnostic and parenthesized integer, and include that in your
     e-mail.  One way to do that is to run a command like this
       find /usr/include/. -type f \
         | xargs grep 'define.*\<E[A-Z]*\>.*\<18\>' /dev/null
     where you'd replace `18' with the integer in parentheses that
     was output from the perl one-liner above.
     If necessary, of course, change `/tmp' to some other directory.  */
      if (errno != EXDEV)
    {
      /* There are many ways this can happen due to a race condition.
         When something happens between the initial xstat and the
         subsequent rename, we can get many different types of errors.
         For example, if the destination is initially a non-directory
         or non-existent, but it is created as a directory, the rename
         fails.  If two `mv' commands try to rename the same file at
         about the same time, one will succeed and the other will fail.
         If the permissions on the directory containing the source or
         destination file are made too restrictive, the rename will
         fail.  Etc.  */
      error (0, errno,
         _("cannot move %s to %s"),
         quote_n (0, src_path), quote_n (1, dst_path));
      forget_created (src_sb.st_ino, src_sb.st_dev);
      return 1;
    }

      /* The rename attempt has failed.  Remove any existing destination
     file so that a cross-device `mv' acts as if it were really using
     the rename syscall.  */
      if (unlink (dst_path) && errno != ENOENT)
    {
      error (0, errno,
         _("inter-device move failed: %s to %s; unable to remove target"),
         quote_n (0, src_path), quote_n (1, dst_path));
      forget_created (src_sb.st_ino, src_sb.st_dev);
      return 1;
    }

      new_dst = 1;
    }

  delayed_fail = 0;

  /* In certain modes (cp's --symbolic-link), and for certain file types
     (symlinks and hard links) it doesn't make sense to preserve metadata,
     or it's possible to preserve only some of it.
     In such cases, set this variable to zero.  */
  preserve_metadata = 1;

  if (S_ISDIR (src_type))
    {
      struct dir_list *dir;

      /* If this directory has been copied before during the
         recursion, there is a symbolic link to an ancestor
         directory of the symbolic link.  It is impossible to
         continue to copy this, unless we've got an infinite disk.  */

      if (is_ancestor (&src_sb, ancestors))
    {
      error (0, 0, _("cannot copy cyclic symbolic link %s"),
         quote (src_path));
      goto un_backup;
    }

      /* Insert the current directory in the list of parents.  */

      dir = (struct dir_list *) alloca (sizeof (struct dir_list));
      dir->parent = ancestors;
      dir->ino = src_sb.st_ino;
      dir->dev = src_sb.st_dev;

      if (new_dst || !S_ISDIR (dst_sb.st_mode))
    {
      /* Create the new directory writable and searchable, so
             we can create new entries in it.  */

      if (mkdir (dst_path, (src_mode & x->umask_kill) | S_IRWXU))
        {
          error (0, errno, _("cannot create directory %s"),
             quote (dst_path));
          goto un_backup;
        }

      /* Insert the created directory's inode and device
             numbers into the search structure, so that we can
             avoid copying it again.  */

      if (remember_created (dst_path))
        goto un_backup;

      if (x->verbose)
        printf ("%s -> %s\n", quote_n (0, src_path), quote_n (1, dst_path));
    }

      /* Are we crossing a file system boundary?  */
      if (x->one_file_system && device != 0 && device != src_sb.st_dev)
    return 0;

      /* Copy the contents of the directory.  */

      if (copy_dir (src_path, dst_path, new_dst, &src_sb, dir, x,
            copy_into_self))
    {
      /* Don't just return here -- otherwise, the failure to read a
         single file in a source directory would cause the containing
         destination directory not to have owner/perms set properly.  */
      delayed_fail = 1;
    }
    }
#ifdef S_ISLNK
  else if (x->symbolic_link)
    {
      preserve_metadata = 0;

      if (*src_path != '/')
    {
      /* Check that DST_PATH denotes a file in the current directory.  */
      struct stat dot_sb;
      struct stat dst_parent_sb;
      char *dst_parent;
      int in_current_dir;

      dst_parent = dir_name (dst_path);

      in_current_dir = (STREQ (".", dst_parent)
                /* If either stat call fails, it's ok not to report
                   the failure and say dst_path is in the current
                   directory.  Other things will fail later.  */
                || stat (".", &dot_sb)
                || stat (dst_parent, &dst_parent_sb)
                || SAME_INODE (dot_sb, dst_parent_sb));
      free (dst_parent);

      if (! in_current_dir)
        {
          error (0, 0,
       _("%s: can make relative symbolic links only in current directory"),
             quote (dst_path));
          goto un_backup;
        }
    }
      if (symlink (src_path, dst_path))
    {
      error (0, errno, _("cannot create symbolic link %s to %s"),
         quote_n (0, dst_path), quote_n (1, src_path));
      goto un_backup;
    }
    }
#endif
  else if (x->hard_link)
    {
      preserve_metadata = 0;
      if (link (src_path, dst_path))
    {
      error (0, errno, _("cannot create link %s"), quote (dst_path));
      goto un_backup;
    }
    }
  else if (S_ISREG (src_type)
       || (x->copy_as_regular && !S_ISDIR (src_type)
#ifdef S_ISLNK
           && !S_ISLNK (src_type)
#endif
           ))
    {
      copied_as_regular = 1;
      /* POSIX says the permission bits of the source file must be
     used as the 3rd argument in the open call, but that's not consistent
     with historical practice.  */
      if (copy_reg (src_path, dst_path, x,
            get_dest_mode (x, src_mode), &new_dst, &src_sb))
    goto un_backup;
    }
  else
#ifdef S_ISFIFO
  if (S_ISFIFO (src_type))
    {
      if (mkfifo (dst_path, get_dest_mode (x, src_mode)))
    {
      error (0, errno, _("cannot create fifo %s"), quote (dst_path));
      goto un_backup;
    }
    }
  else
#endif
    if (S_ISBLK (src_type) || S_ISCHR (src_type)
#ifdef S_ISSOCK
    || S_ISSOCK (src_type)
#endif
    )
    {
      if (mknod (dst_path, get_dest_mode (x, src_mode), src_sb.st_rdev))
    {
      error (0, errno, _("cannot create special file %s"),
         quote (dst_path));
      goto un_backup;
    }
    }
  else
#ifdef S_ISLNK
  if (S_ISLNK (src_type))
    {
      char *src_link_val = xreadlink (src_path);
      if (src_link_val == NULL)
    {
      error (0, errno, _("cannot read symbolic link %s"), quote (src_path));
      goto un_backup;
    }

      if (!symlink (src_link_val, dst_path))
    free (src_link_val);
      else
    {
      int saved_errno = errno;
      int same_link = 0;
      if (x->update && !new_dst && S_ISLNK (dst_sb.st_mode))
        {
          /* See if the destination is already the desired symlink.  */
          size_t src_link_len = strlen (src_link_val);
          char *dest_link_val = (char *) alloca (src_link_len + 1);
          int dest_link_len = readlink (dst_path, dest_link_val,
                        src_link_len + 1);
          if ((size_t) dest_link_len == src_link_len
          && strncmp (dest_link_val, src_link_val, src_link_len) == 0)
        same_link = 1;
        }
      free (src_link_val);

      if (! same_link)
        {
          error (0, saved_errno, _("cannot create symbolic link %s"),
             quote (dst_path));
          goto un_backup;
        }
    }

      /* There's no need to preserve timestamps or permissions.  */
      preserve_metadata = 0;

      if (x->preserve_ownership)
    {
      /* Preserve the owner and group of the just-`copied'
         symbolic link, if possible.  */
# if HAVE_LCHOWN
      if (DO_CHOWN (lchown, dst_path, src_sb.st_uid, src_sb.st_gid))
        {
          error (0, errno, _("failed to preserve ownership for %s"),
             dst_path);
          goto un_backup;
        }
# else
      /* Can't preserve ownership of symlinks.
         FIXME: maybe give a warning or even error for symlinks
         in directories with the sticky bit set -- there, not
         preserving owner/group is a potential security problem.  */
# endif
    }
    }
  else
#endif
    {
      error (0, 0, _("%s has unknown file type"), quote (src_path));
      goto un_backup;
    }

  if (command_line_arg)
    record_file (x->dest_info, dst_path, NULL);

  if ( ! preserve_metadata)
    return 0;

  /* POSIX says that `cp -p' must restore the following:
     - permission bits
     - setuid, setgid bits
     - owner and group
     If it fails to restore any of those, we may give a warning but
     the destination must not be removed.
     FIXME: implement the above. */

  /* Adjust the times (and if possible, ownership) for the copy.
     chown turns off set[ug]id bits for non-root,
     so do the chmod last.  */

  if (x->preserve_timestamps)
    {
      struct utimbuf utb;

      /* There's currently no interface to set file timestamps with
     better than 1-second resolution, so discard any fractional
     part of the source timestamp.  */

      utb.actime = src_sb.st_atime;
      utb.modtime = src_sb.st_mtime;

      if (utime (dst_path, &utb))
    {
      error (0, errno, _("preserving times for %s"), quote (dst_path));
      if (x->require_preserve)
        return 1;
    }
    }

  /* Avoid calling chown if we know it's not necessary.  */
  if (x->preserve_ownership
      && (new_dst || !SAME_OWNER_AND_GROUP (src_sb, dst_sb)))
    {
      ran_chown = 1;
      if (DO_CHOWN (chown, dst_path, src_sb.st_uid, src_sb.st_gid))
    {
      error (0, errno, _("failed to preserve ownership for %s"),
         quote (dst_path));
      if (x->require_preserve)
        return 1;
    }
    }

#if HAVE_STRUCT_STAT_ST_AUTHOR
  /* Preserve the st_author field.  */
  {
    file_t file = file_name_lookup (dst_path, 0, 0);
    if (file_chauthor (file, src_sb.st_author))
      error (0, errno, _("failed to preserve authorship for %s"),
         quote (dst_path));
    mach_port_deallocate (mach_task_self (), file);
  }
#endif

  /* Permissions of newly-created regular files were set upon `open' in
     copy_reg.  But don't return early if there were any special bits and
     we had to run chown, because the chown must have reset those bits.  */
  if ((new_dst && copied_as_regular)
      && !(ran_chown && (src_mode & ~S_IRWXUGO)))
    return delayed_fail;

  if ((x->preserve_mode || new_dst)
      && (x->copy_as_regular || S_ISREG (src_type) || S_ISDIR (src_type)))
    {
      if (chmod (dst_path, get_dest_mode (x, src_mode)))
    {
      error (0, errno, _("setting permissions for %s"), quote (dst_path));
      if (x->set_mode || x->require_preserve)
        return 1;
    }
    }

  return delayed_fail;

un_backup:

  /* We have failed to create the destination file.
     If we've just added a dev/ino entry via the remember_copied
     call above (i.e., unless we've just failed to create a hard link),
     remove the entry associating the source dev/ino with the
     destination file name, so we don't try to `preserve' a link
     to a file we didn't create.  */
  if (earlier_file == NULL)
    forget_created (src_sb.st_ino, src_sb.st_dev);

  if (dst_backup)
    {
      if (rename (dst_backup, dst_path))
    error (0, errno, _("cannot un-backup %s"), quote (dst_path));
      else
    {
      if (x->verbose)
        printf (_("%s -> %s (unbackup)\n"),
            quote_n (0, dst_backup), quote_n (1, dst_path));
    }
    }
  return 1;
}

static int
valid_options (const struct cp_options *co)
{
  assert (co != NULL);

  assert (VALID_BACKUP_TYPE (co->backup_type));

  /* FIXME: for some reason this assertion always fails,
     at least on Solaris2.5.1.  Just disable it for now.  */
  /* assert (co->xstat == lstat || co->xstat == stat); */

  /* Make sure xstat and dereference are consistent.  */
  /* FIXME */

  assert (VALID_SPARSE_MODE (co->sparse_mode));

  return 1;
}

/* Copy the file SRC_PATH to the file DST_PATH.  The files may be of
   any type.  NONEXISTENT_DST should be nonzero if the file DST_PATH
   is known not to exist (e.g., because its parent directory was just
   created);  NONEXISTENT_DST should be zero if DST_PATH might already
   exist.  OPTIONS is ... FIXME-describe
   Set *COPY_INTO_SELF to nonzero if SRC_PATH is a parent of (or the
   same as) DST_PATH;  otherwise, set it to zero.
   Return 0 if successful, 1 if an error occurs. */

int
copy (const char *src_path, const char *dst_path,
      int nonexistent_dst, const struct cp_options *options,
      int *copy_into_self, int *rename_succeeded)
{
  assert (valid_options (options));

  /* Record the file names: they're used in case of error, when copying
     a directory into itself.  I don't like to make these tools do *any*
     extra work in the common case when that work is solely to handle
     exceptional cases, but in this case, I don't see a way to derive the
     top level source and destination directory names where they're used.
     An alternative is to use COPY_INTO_SELF and print the diagnostic
     from every caller -- but I don't want to do that.  */
  top_level_src_path = src_path;
  top_level_dst_path = dst_path;

  return copy_internal (src_path, dst_path, nonexistent_dst, 0, NULL,
            options, 1, copy_into_self, rename_succeeded);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/cp-hash.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/cp-hash.c used in: Step 959, Step 964, Step 970
/* cp-hash.c  -- file copying (hash search routines)
   Copyright (C) 89, 90, 91, 1995-2002 Free Software Foundation.

   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.

   Written by Torbjorn Granlund, Sweden (tege@sics.se).
   Rewritten to use lib/hash.c by Jim Meyering.  */

#include <config.h>

#include <stdio.h>
#include <sys/types.h>
#include "system.h"

#include "same.h"
#include "quote.h"
#include "hash.h"
#include "error.h"
#include "cp-hash.h"

/* Use ST_DEV and ST_INO as the key, FILENAME as the value.
   These are used e.g., in copy.c to associate the destination path with
   the source device/inode pair so that if we encounter a matching dev/ino
   pair in the source tree we can arrange to create a hard link between
   the corresponding names in the destination tree.  */
struct Src_to_dest
{
  ino_t st_ino;
  dev_t st_dev;
  /* Destination path name (of non-directory or pre-existing directory)
     corresponding to the dev/ino of a copied file, or the destination path
     name corresponding to a dev/ino pair for a newly-created directory. */
  char const* name;
};

/* This table maps source dev/ino to destination file name.
   We use it to preserve hard links when copying.  */
static Hash_table *src_to_dest;

/* Initial size of the above hash table.  */
#define INITIAL_TABLE_SIZE 103

static unsigned int
src_to_dest_hash (void const *x, unsigned int table_size)
{
  struct Src_to_dest const *p = x;

  /* Ignoring the device number here should be fine.  */
  /* The cast to uintmax_t prevents negative remainders
     if st_ino is negative.  */
  return (uintmax_t) p->st_ino % table_size;
}

/* Compare two Src_to_dest entries.
   Return true if their keys are judged `equal'.  */
static bool
src_to_dest_compare (void const *x, void const *y)
{
  struct Src_to_dest const *a = x;
  struct Src_to_dest const *b = y;
  return SAME_INODE (*a, *b) ? true : false;
}

static void
src_to_dest_free (void *x)
{
  struct Src_to_dest *a = x;
  free ((char *) (a->name));
  free (x);
}

/* Remove the entry matching INO/DEV from the table
   that maps source ino/dev to destination file name.  */
void
forget_created (ino_t ino, dev_t dev)
{
  struct Src_to_dest probe;
  struct Src_to_dest *ent;

  probe.st_ino = ino;
  probe.st_dev = dev;
  probe.name = NULL;

  ent = hash_delete (src_to_dest, &probe);
  if (ent)
    src_to_dest_free (ent);
}

/* Add PATH to the list of files that we have created.
   Return 1 if we can't stat PATH, otherwise 0.  */

int
remember_created (const char *path)
{
  struct stat sb;

  if (stat (path, &sb) < 0)
    {
      error (0, errno, "%s", quote (path));
      return 1;
    }

  remember_copied (path, sb.st_ino, sb.st_dev);
  return 0;
}

/* Add path NAME, copied from inode number INO and device number DEV,
   to the list of files we have copied.
   Return NULL if inserted, otherwise non-NULL. */

char *
remember_copied (const char *name, ino_t ino, dev_t dev)
{
  struct Src_to_dest *ent;
  struct Src_to_dest *ent_from_table;

  ent = (struct Src_to_dest *) xmalloc (sizeof *ent);
  ent->name = xstrdup (name);
  ent->st_ino = ino;
  ent->st_dev = dev;

  ent_from_table = hash_insert (src_to_dest, ent);
  if (ent_from_table == NULL)
    {
      /* Insertion failed due to lack of memory.  */
      xalloc_die ();
    }

  /* Determine whether there was already an entry in the table
     with a matching key.  If so, free ENT (it wasn't inserted) and
     return the `name' from the table entry.  */
  if (ent_from_table != ent)
    {
      src_to_dest_free (ent);
      return (char *) ent_from_table->name;
    }

  /* New key;  insertion succeeded.  */
  return NULL;
}

/* Initialize the hash table.  */
void
hash_init (void)
{
  src_to_dest = hash_initialize (INITIAL_TABLE_SIZE, NULL,
                 src_to_dest_hash,
                 src_to_dest_compare,
                 src_to_dest_free);
  if (src_to_dest == NULL)
    xalloc_die ();
}

/* Reset the hash structure in the global variable `htab' to
   contain no entries.  */

void
forget_all (void)
{
  hash_free (src_to_dest);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/hash.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/hash.h used in: Step 959, Step 964, Step 970
/* hash - hashing table processing.
   Copyright (C) 1998, 1999, 2001 Free Software Foundation, Inc.
   Written by Jim Meyering <meyering@ascend.com>, 1998.

   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.  */

/* A generic hash table package.  */

/* Make sure USE_OBSTACK is defined to 1 if you want the allocator to use
   obstacks instead of malloc, and recompile `hash.c' with same setting.  */

#ifndef HASH_H_
# define HASH_H_

# ifndef PARAMS
#  if PROTOTYPES || __STDC__
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

typedef unsigned (*Hash_hasher) PARAMS ((const void *, unsigned));
typedef bool (*Hash_comparator) PARAMS ((const void *, const void *));
typedef void (*Hash_data_freer) PARAMS ((void *));
typedef bool (*Hash_processor) PARAMS ((void *, void *));

struct hash_entry
  {
    void *data;
    struct hash_entry *next;
  };

struct hash_tuning
  {
    /* This structure is mainly used for `hash_initialize', see the block
       documentation of `hash_reset_tuning' for more complete comments.  */

    float shrink_threshold; /* ratio of used buckets to trigger a shrink */
    float shrink_factor;    /* ratio of new smaller size to original size */
    float growth_threshold; /* ratio of used buckets to trigger a growth */
    float growth_factor;    /* ratio of new bigger size to original size */
    bool is_n_buckets;      /* if CANDIDATE really means table size */
  };

typedef struct hash_tuning Hash_tuning;

struct hash_table;

typedef struct hash_table Hash_table;

/* Information and lookup.  */
unsigned hash_get_n_buckets PARAMS ((const Hash_table *));
unsigned hash_get_n_buckets_used PARAMS ((const Hash_table *));
unsigned hash_get_n_entries PARAMS ((const Hash_table *));
unsigned hash_get_max_bucket_length PARAMS ((const Hash_table *));
bool hash_table_ok PARAMS ((const Hash_table *));
void hash_print_statistics PARAMS ((const Hash_table *, FILE *));
void *hash_lookup PARAMS ((const Hash_table *, const void *));

/* Walking.  */
void *hash_get_first PARAMS ((const Hash_table *));
void *hash_get_next PARAMS ((const Hash_table *, const void *));
unsigned hash_get_entries PARAMS ((const Hash_table *, void **, unsigned));
unsigned hash_do_for_each PARAMS ((const Hash_table *, Hash_processor, void *));

/* Allocation and clean-up.  */
unsigned hash_string PARAMS ((const char *, unsigned));
void hash_reset_tuning PARAMS ((Hash_tuning *));
Hash_table *hash_initialize PARAMS ((unsigned, const Hash_tuning *,
                     Hash_hasher, Hash_comparator,
                     Hash_data_freer));
void hash_clear PARAMS ((Hash_table *));
void hash_free PARAMS ((Hash_table *));

/* Insertion and deletion.  */
bool hash_rehash PARAMS ((Hash_table *, unsigned));
void *hash_insert PARAMS ((Hash_table *, const void *));
void *hash_delete PARAMS ((Hash_table *, const void *));

#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/cp-hash.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/cp-hash.h used in: Step 959, Step 964, Step 970
void hash_init (void);
void forget_all (void);
void forget_created (ino_t ino, dev_t dev);
char *remember_copied (const char *node, ino_t ino, dev_t dev);
int remember_created (const char *path);

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls-ls.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/ls-ls.c used in: Step 961
#include "ls.h"
int ls_mode = LS_LS;

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/ls.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/ls.h used in: Step 961
/* This is for the `ls' program.  */
#define LS_LS 1

/* This is for the `dir' program.  */
#define LS_MULTI_COL 2

/* This is for the `vdir' program.  */
#define LS_LONG_FORMAT 3

extern int ls_mode;

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/install.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/install.c used in: Step 963
/* install - copy files and set attributes
   Copyright (C) 89, 90, 91, 1995-2002 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.  */

/* Written by David MacKenzie <djm@gnu.ai.mit.edu> */

#ifdef _AIX
 #pragma alloca
#endif

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>

#include "system.h"
#include "backupfile.h"
#include "error.h"
#include "cp-hash.h"
#include "copy.h"
#include "dirname.h"
#include "makepath.h"
#include "modechange.h"
#include "path-concat.h"
#include "quote.h"
#include "xstrtol.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "install"

#define AUTHORS "David MacKenzie"

#if HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif

struct passwd *getpwnam ();
struct group *getgrnam ();

#ifndef _POSIX_VERSION
uid_t getuid ();
gid_t getgid ();
#endif

#if ! HAVE_ENDGRENT
# define endgrent() ((void) 0)
#endif

#if ! HAVE_ENDPWENT
# define endpwent() ((void) 0)
#endif

/* Initial number of entries in each hash table entry's table of inodes.  */
#define INITIAL_HASH_MODULE 100

/* Initial number of entries in the inode hash table.  */
#define INITIAL_ENTRY_TAB_SIZE 70

/* Number of bytes of a file to copy at a time. */
#define READ_SIZE (32 * 1024)

int isdir ();

int stat ();

static int change_timestamps (const char *from, const char *to);
static int change_attributes (const char *path);
static int copy_file (const char *from, const char *to,
              const struct cp_options *x);
static int install_file_to_path (const char *from, const char *to,
                 const struct cp_options *x);
static int install_file_in_dir (const char *from, const char *to_dir,
                const struct cp_options *x);
static int install_file_in_file (const char *from, const char *to,
                 const struct cp_options *x);
static void get_ids (void);
static void strip (const char *path);
void usage (int status);

/* The name this program was run with, for error messages. */
char *program_name;

/* The user name that will own the files, or NULL to make the owner
   the current user ID. */
static char *owner_name;

/* The user ID corresponding to `owner_name'. */
static uid_t owner_id;

/* The group name that will own the files, or NULL to make the group
   the current group ID. */
static char *group_name;

/* The group ID corresponding to `group_name'. */
static gid_t group_id;

/* The permissions to which the files will be set.  The umask has
   no effect. */
static mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;

/* If nonzero, strip executable files after copying them. */
static int strip_files;

/* If nonzero, install a directory instead of a regular file. */
static int dir_arg;

static struct option const long_options[] =
{
  {"backup", optional_argument, NULL, 'b'},
  {"directory", no_argument, NULL, 'd'},
  {"group", required_argument, NULL, 'g'},
  {"mode", required_argument, NULL, 'm'},
  {"owner", required_argument, NULL, 'o'},
  {"preserve-timestamps", no_argument, NULL, 'p'},
  {"strip", no_argument, NULL, 's'},
  {"suffix", required_argument, NULL, 'S'},
  {"version-control", required_argument, NULL, 'V'}, /* Deprecated. FIXME. */
  {"verbose", no_argument, NULL, 'v'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

static void
cp_option_init (struct cp_options *x)
{
  x->copy_as_regular = 1;
  x->dereference = DEREF_ALWAYS;
  x->unlink_dest_before_opening = 1;
  x->unlink_dest_after_failed_open = 0;
  x->hard_link = 0;
  x->interactive = I_UNSPECIFIED;
  x->move_mode = 0;
  x->myeuid = geteuid ();
  x->one_file_system = 0;
  x->preserve_ownership = 0;
  x->preserve_links = 0;
  x->preserve_mode = 0;
  x->preserve_timestamps = 0;
  x->require_preserve = 0;
  x->recursive = 0;
  x->sparse_mode = SPARSE_AUTO;
  x->symbolic_link = 0;
  x->backup_type = none;

  /* Create destination files initially writable so we can run strip on them.
     Although GNU strip works fine on read-only files, some others
     would fail.  */
  x->set_mode = 1;
  x->mode = S_IRUSR | S_IWUSR;
  x->stdin_tty = 0;

  x->umask_kill = 0;
  x->update = 0;
  x->verbose = 0;
  x->xstat = stat;
  x->dest_info = NULL;
  x->src_info = NULL;
}

int
main (int argc, char **argv)
{
  int optc;
  int errors = 0;
  const char *specified_mode = NULL;
  int make_backups = 0;
  char *backup_suffix_string;
  char *version_control_string = NULL;
  int mkdir_and_install = 0;
  struct cp_options x;
  int n_files;
  char **file;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  cp_option_init (&x);

  owner_name = NULL;
  group_name = NULL;
  strip_files = 0;
  dir_arg = 0;
  umask (0);

  /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
     we'll actually use backup_suffix_string.  */
  backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");

  while ((optc = getopt_long (argc, argv, "bcsDdg:m:o:pvV:S:", long_options,
                  NULL)) != -1)
    {
      switch (optc)
    {
    case 0:
      break;

    case 'V':  /* FIXME: this is deprecated.  Remove it in 2001.  */
      error (0, 0,
         _("warning: --version-control (-V) is obsolete;  support for\
 it\nwill be removed in some future release.  Use --backup=%s instead."
           ), optarg);
      /* Fall through.  */

    case 'b':
      make_backups = 1;
      if (optarg)
        version_control_string = optarg;
      break;
    case 'c':
      break;
    case 's':
      strip_files = 1;
      break;
    case 'd':
      dir_arg = 1;
      break;
    case 'D':
      mkdir_and_install = 1;
      break;
    case 'v':
      x.verbose = 1;
      break;
    case 'g':
      group_name = optarg;
      break;
    case 'm':
      specified_mode = optarg;
      break;
    case 'o':
      owner_name = optarg;
      break;
    case 'p':
      x.preserve_timestamps = 1;
      break;
    case 'S':
      make_backups = 1;
      backup_suffix_string = optarg;
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (EXIT_FAILURE);
    }
    }

  /* Check for invalid combinations of arguments. */
  if (dir_arg && strip_files)
    error (EXIT_FAILURE, 0,
       _("the strip option may not be used when installing a directory"));

  if (backup_suffix_string)
    simple_backup_suffix = xstrdup (backup_suffix_string);

  x.backup_type = (make_backups
           ? xget_version (_("backup type"),
                   version_control_string)
           : none);

  n_files = argc - optind;
  file = argv + optind;

  if (n_files == 0 || (n_files == 1 && !dir_arg))
    {
      error (0, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }

  if (specified_mode)
    {
      struct mode_change *change = mode_compile (specified_mode, 0);
      if (change == MODE_INVALID)
    error (EXIT_FAILURE, 0, _("invalid mode %s"), quote (specified_mode));
      else if (change == MODE_MEMORY_EXHAUSTED)
    xalloc_die ();
      mode = mode_adjust (0, change);
    }

  get_ids ();

  if (dir_arg)
    {
      int i;
      for (i = 0; i < n_files; i++)
    {
      errors |=
        make_path (file[i], mode, mode, owner_id, group_id, 0,
               (x.verbose ? _("creating directory %s") : NULL));
    }
    }
  else
    {
      /* FIXME: it's a little gross that this initialization is
     required by copy.c::copy. */
      hash_init ();

      if (n_files == 2)
        {
          if (mkdir_and_install)
        errors = install_file_to_path (file[0], file[1], &x);
      else if (!isdir (file[1]))
        errors = install_file_in_file (file[0], file[1], &x);
      else
        errors = install_file_in_dir (file[0], file[1], &x);
    }
      else
    {
      int i;
      const char *dest = file[n_files - 1];
      if (!isdir (dest))
        {
          error (0, 0,
             _("installing multiple files, but last argument, %s \
is not a directory"),
             quote (dest));
          usage (EXIT_FAILURE);
        }

      dest_info_init (&x);
      for (i = 0; i < n_files - 1; i++)
        {
          errors |= install_file_in_dir (file[i], dest, &x);
        }
    }
    }

  exit (errors);
}

/* Copy file FROM onto file TO, creating any missing parent directories of TO.
   Return 0 if successful, 1 if an error occurs */

static int
install_file_to_path (const char *from, const char *to,
              const struct cp_options *x)
{
  char *dest_dir;
  int fail = 0;

  dest_dir = dir_name (to);

  /* check to make sure this is a path (not install a b ) */
  if (!STREQ (dest_dir, ".")
      && !isdir (dest_dir))
    {
      /* Someone will probably ask for a new option or three to specify
     owner, group, and permissions for parent directories.  Remember
     that this option is intended mainly to help installers when the
     distribution doesn't provide proper install rules.  */
#define DIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
      fail = make_path (dest_dir, DIR_MODE, DIR_MODE, owner_id, group_id, 0,
            (x->verbose ? _("creating directory %s") : NULL));
    }

  if (fail == 0)
    fail = install_file_in_file (from, to, x);

  free (dest_dir);

  return fail;
}

/* Copy file FROM onto file TO and give TO the appropriate
   attributes.
   Return 0 if successful, 1 if an error occurs. */

static int
install_file_in_file (const char *from, const char *to,
              const struct cp_options *x)
{
  if (copy_file (from, to, x))
    return 1;
  if (strip_files)
    strip (to);
  if (change_attributes (to))
    return 1;
  if (x->preserve_timestamps)
    return change_timestamps (from, to);
  return 0;
}

/* Copy file FROM into directory TO_DIR, keeping its same name,
   and give the copy the appropriate attributes.
   Return 0 if successful, 1 if not. */

static int
install_file_in_dir (const char *from, const char *to_dir,
             const struct cp_options *x)
{
  const char *from_base;
  char *to;
  int ret;

  from_base = base_name (from);
  to = path_concat (to_dir, from_base, NULL);
  ret = install_file_in_file (from, to, x);
  free (to);
  return ret;
}

/* Copy file FROM onto file TO, creating TO if necessary.
   Return 0 if the copy is successful, 1 if not.  */

static int
copy_file (const char *from, const char *to, const struct cp_options *x)
{
  int fail;
  int nonexistent_dst = 0;
  int copy_into_self;

  /* Allow installing from non-regular files like /dev/null.
     Charles Karney reported that some Sun version of install allows that
     and that sendmail's installation process relies on the behavior.  */
  if (isdir (from))
    {
      error (0, 0, _("%s is a directory"), quote (from));
      return 1;
    }

  fail = copy (from, to, nonexistent_dst, x, &copy_into_self, NULL);

  return fail;
}

/* Set the attributes of file or directory PATH.
   Return 0 if successful, 1 if not. */

static int
change_attributes (const char *path)
{
  int err = 0;

  /* chown must precede chmod because on some systems,
     chown clears the set[ug]id bits for non-superusers,
     resulting in incorrect permissions.
     On System V, users can give away files with chown and then not
     be able to chmod them.  So don't give files away.

     We don't normally ignore errors from chown because the idea of
     the install command is that the file is supposed to end up with
     precisely the attributes that the user specified (or defaulted).
     If the file doesn't end up with the group they asked for, they'll
     want to know.  But AFS returns EPERM when you try to change a
     file's group; thus the kludge.  */

  if (chown (path, owner_id, group_id)
#ifdef AFS
      && errno != EPERM
#endif
      )
    {
      error (0, errno, "cannot change ownership of %s", quote (path));
      err = 1;
    }

  if (!err && chmod (path, mode))
    {
      error (0, errno, "cannot change permissions of %s", quote (path));
      err = 1;
    }

  return err;
}

/* Set the timestamps of file TO to match those of file FROM.
   Return 0 if successful, 1 if not. */

static int
change_timestamps (const char *from, const char *to)
{
  struct stat stb;
  struct utimbuf utb;

  if (stat (from, &stb))
    {
      error (0, errno, _("cannot obtain time stamps for %s"), quote (from));
      return 1;
    }

  /* There's currently no interface to set file timestamps with
     better than 1-second resolution, so discard any fractional
     part of the source timestamp.  */

  utb.actime = stb.st_atime;
  utb.modtime = stb.st_mtime;
  if (utime (to, &utb))
    {
      error (0, errno, _("cannot set time stamps for %s"), quote (to));
      return 1;
    }
  return 0;
}

/* Strip the symbol table from the file PATH.
   We could dig the magic number out of the file first to
   determine whether to strip it, but the header files and
   magic numbers vary so much from system to system that making
   it portable would be very difficult.  Not worth the effort. */

static void
strip (const char *path)
{
  int status;
  pid_t pid = fork ();

  switch (pid)
    {
    case -1:
      error (EXIT_FAILURE, errno, _("fork system call failed"));
      break;
    case 0:         /* Child. */
      execlp ("strip", "strip", path, NULL);
      error (EXIT_FAILURE, errno, _("cannot run strip"));
      break;
    default:            /* Parent. */
      /* Parent process. */
      while (pid != wait (&status)) /* Wait for kid to finish. */
    /* Do nothing. */ ;
      if (status)
    error (EXIT_FAILURE, 0, _("strip failed"));
      break;
    }
}

/* Initialize the user and group ownership of the files to install. */

static void
get_ids (void)
{
  struct passwd *pw;
  struct group *gr;

  if (owner_name)
    {
      pw = getpwnam (owner_name);
      if (pw == NULL)
    {
      unsigned long int tmp;
      if (xstrtoul (owner_name, NULL, 0, &tmp, NULL) != LONGINT_OK
          || UID_T_MAX < tmp)
        error (EXIT_FAILURE, 0, _("invalid user %s"), quote (owner_name));
      owner_id = tmp;
    }
      else
    owner_id = pw->pw_uid;
      endpwent ();
    }
  else
    owner_id = (uid_t) -1;

  if (group_name)
    {
      gr = getgrnam (group_name);
      if (gr == NULL)
    {
      unsigned long int tmp;
      if (xstrtoul (group_name, NULL, 0, &tmp, NULL) != LONGINT_OK
          || GID_T_MAX < tmp)
        error (EXIT_FAILURE, 0, _("invalid group %s"), quote (group_name));
      group_id = tmp;
    }
      else
    group_id = gr->gr_gid;
      endgrent ();
    }
  else
    group_id = (gid_t) -1;
}

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... SOURCE DEST           (1st format)\n\
  or:  %s [OPTION]... SOURCE... DIRECTORY   (2nd format)\n\
  or:  %s -d [OPTION]... DIRECTORY...       (3rd format)\n\
"),
          program_name, program_name, program_name);
      fputs (_("\
In the first two formats, copy SOURCE to DEST or multiple SOURCE(s) to\n\
the existing DIRECTORY, while setting permission modes and owner/group.\n\
In the third format, create all components of the given DIRECTORY(ies).\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
      --backup[=CONTROL] make a backup of each existing destination file\n\
  -b                  like --backup but does not accept an argument\n\
  -c                  (ignored)\n\
  -d, --directory     treat all arguments as directory names; create all\n\
                        components of the specified directories\n\
"), stdout);
      fputs (_("\
  -D                  create all leading components of DEST except the last,\n\
                        then copy SOURCE to DEST;  useful in the 1st format\n\
  -g, --group=GROUP   set group ownership, instead of process' current group\n\
  -m, --mode=MODE     set permission mode (as in chmod), instead of rwxr-xr-x\n\
  -o, --owner=OWNER   set ownership (super-user only)\n\
"), stdout);
      fputs (_("\
  -p, --preserve-timestamps   apply access/modification times of SOURCE files\n\
                        to corresponding destination files\n\
  -s, --strip         strip symbol tables, only for 1st and 2nd formats\n\
  -S, --suffix=SUFFIX override the usual backup suffix\n\
  -v, --verbose       print the name of each directory as it is created\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
The version control method may be selected via the --backup option or through\n\
the VERSION_CONTROL environment variable.  Here are the values:\n\
\n\
"), stdout);
      fputs (_("\
  none, off       never make backups (even if --backup is given)\n\
  numbered, t     make numbered backups\n\
  existing, nil   numbered if numbered backups exist, simple otherwise\n\
  simple, never   always make simple backups\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/md5.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/md5.c used in: Step 965
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include "system.h"
#include "checksum.h"

int algorithm = ALG_MD5;

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/checksum.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/checksum.h used in: Step 965, Step 966, Step 974
#include <config.h>

#include <sys/types.h>
#include <stdio.h>

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  ALG_UNSPECIFIED = 0,
  ALG_MD5 = CHAR_MAX + 1,
  ALG_SHA1
};

extern int algorithm;

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/md5sum.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/md5sum.c used in: Step 966
/* Compute MD5 or SHA1 checksum of files or strings
   Copyright (C) 1995-2002 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.  */

/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>.  */

#include <config.h>

#include <getopt.h>
#include <stdio.h>
#include <sys/types.h>

#include "system.h"

#include "md5.h"
#include "sha.h"
#include "checksum.h"
#include "getline.h"
#include "closeout.h"
#include "error.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME (algorithm == ALG_MD5 ? "md5sum" : "shasum")

#define AUTHORS N_ ("Ulrich Drepper and Scott Miller")

/* Most systems do not distinguish between external and internal
   text representations.  */
/* FIXME: This begs for an autoconf test.  */
#if O_BINARY
# define OPENOPTS(BINARY) ((BINARY) != 0 ? TEXT1TO1 : TEXTCNVT)
# define TEXT1TO1 "rb"
# define TEXTCNVT "r"
#else
# if defined VMS
#  define OPENOPTS(BINARY) ((BINARY) != 0 ? TEXT1TO1 : TEXTCNVT)
#  define TEXT1TO1 "rb", "ctx=stm"
#  define TEXTCNVT "r", "ctx=stm"
# else
#  if UNIX || __UNIX__ || unix || __unix__ || _POSIX_VERSION
#   define OPENOPTS(BINARY) "r"
#  else
    /* The following line is intended to evoke an error.
       Using #error is not portable enough.  */
    "Cannot determine system type."
#  endif
# endif
#endif


#define DIGEST_TYPE_STRING(Alg) ((Alg) == ALG_MD5 ? "MD5" : "SHA1")
#define DIGEST_STREAM(Alg) ((Alg) == ALG_MD5 ? md5_stream : sha_stream)

#define DIGEST_BITS(Alg) ((Alg) == ALG_MD5 ? 128 : 160)
#define DIGEST_HEX_BYTES(Alg) (DIGEST_BITS (Alg) / 4)
#define DIGEST_BIN_BYTES(Alg) (DIGEST_BITS (Alg) / 8)

#define MAX_DIGEST_BIN_BYTES MAX (DIGEST_BIN_BYTES (ALG_MD5), \
                  DIGEST_BIN_BYTES (ALG_SHA1))

/* The minimum length of a valid digest line.  This length does
   not include any newline character at the end of a line.  */
#define MIN_DIGEST_LINE_LENGTH(Alg) \
  (DIGEST_HEX_BYTES (Alg) /* length of hexadecimal message digest */ \
   + 2 /* blank and binary indicator */ \
   + 1 /* minimum filename length */ )

/* Nonzero if any of the files read were the standard input. */
static int have_read_stdin;

/* The minimum length of a valid checksum line for the selected algorithm.  */
static size_t min_digest_line_length;

/* Set to the length of a digest hex string for the selected algorithm.  */
static size_t digest_hex_bytes;

/* With --check, don't generate any output.
   The exit code indicates success or failure.  */
static int status_only = 0;

/* With --check, print a message to standard error warning about each
   improperly formatted checksum line.  */
static int warn = 0;

/* Declared and set via one of the wrapper .c files.  */
/* int algorithm = ALG_UNSPECIFIED; */

/* The name this program was run with.  */
char *program_name;

static const struct option long_options[] =
{
  { "binary", no_argument, 0, 'b' },
  { "check", no_argument, 0, 'c' },
  { "status", no_argument, 0, 2 },
  { "string", required_argument, 0, 1 },
  { "text", no_argument, 0, 't' },
  { "warn", no_argument, 0, 'w' },
  { GETOPT_HELP_OPTION_DECL },
  { GETOPT_VERSION_OPTION_DECL },
  { NULL, 0, NULL, 0 }
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION] [FILE]...\n\
  or:  %s [OPTION] --check [FILE]\n\
Print or check %s (%d-bit) checksums.\n\
With no FILE, or when FILE is -, read standard input.\n\
"),
          program_name, program_name,
          DIGEST_TYPE_STRING (algorithm),
          DIGEST_BITS (algorithm));
      printf (_("\
\n\
  -b, --binary            read files in binary mode (default on DOS/Windows)\n\
  -c, --check             check %s sums against given list\n\
  -t, --text              read files in text mode (default)\n\
\n\
"),
          DIGEST_TYPE_STRING (algorithm));
      fputs (_("\
The following two options are useful only when verifying checksums:\n\
      --status            don't output anything, status code shows success\n\
  -w, --warn              warn about improperly formated checksum lines\n\
\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\
\n\
The sums are computed as described in %s.  When checking, the input\n\
should be a former output of this program.  The default mode is to print\n\
a line with checksum, a character indicating type (`*' for binary, ` ' for\n\
text), and name for each FILE.\n"),
          (algorithm == ALG_MD5 ? "RFC 1321" : "FIPS-180-1"));
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }

  exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

/* Split the string S (of length S_LEN) into three parts:
   a hexadecimal digest, binary flag, and the file name.
   S is modified.  */

static int
split_3 (char *s, size_t s_len,
     unsigned char **hex_digest, int *binary, char **file_name)
{
  size_t i;
  int escaped_filename = 0;

#define ISWHITE(c) ((c) == ' ' || (c) == '\t')

  i = 0;
  while (ISWHITE (s[i]))
    ++i;

  /* Ignore this line if it is too short.
     Each line must have at least `min_digest_line_length - 1' (or one more, if
     the first is a backslash) more characters to contain correct message digest
     information.  */
  if (s_len - i < min_digest_line_length + (s[i] == '\\'))
    return 1;

  if (s[i] == '\\')
    {
      ++i;
      escaped_filename = 1;
    }
  *hex_digest = (unsigned char *) &s[i];

  /* The first field has to be the n-character hexadecimal
     representation of the message digest.  If it is not followed
     immediately by a white space it's an error.  */
  i += digest_hex_bytes;
  if (!ISWHITE (s[i]))
    return 1;

  s[i++] = '\0';

  if (s[i] != ' ' && s[i] != '*')
    return 1;
  *binary = (s[i++] == '*');

  /* All characters between the type indicator and end of line are
     significant -- that includes leading and trailing white space.  */
  *file_name = &s[i];

  if (escaped_filename)
    {
      /* Translate each `\n' string in the file name to a NEWLINE,
     and each `\\' string to a backslash.  */

      char *dst = &s[i];

      while (i < s_len)
    {
      switch (s[i])
        {
        case '\\':
          if (i == s_len - 1)
        {
          /* A valid line does not end with a backslash.  */
          return 1;
        }
          ++i;
          switch (s[i++])
        {
        case 'n':
          *dst++ = '\n';
          break;
        case '\\':
          *dst++ = '\\';
          break;
        default:
          /* Only `\' or `n' may follow a backslash.  */
          return 1;
        }
          break;

        case '\0':
          /* The file name may not contain a NUL.  */
          return 1;
          break;

        default:
          *dst++ = s[i++];
          break;
        }
    }
      *dst = '\0';
    }
  return 0;
}

static int
hex_digits (unsigned char const *s)
{
  while (*s)
    {
      if (!ISXDIGIT (*s))
        return 0;
      ++s;
    }
  return 1;
}

/* An interface to the function, DIGEST_STREAM,
   (either md5_stream or sha_stream).
   Operate on FILENAME (it may be "-") and put the result in *BIN_RESULT.
   Return non-zero upon failure, zero to indicate success.  */

static int
digest_file (const char *filename, int binary, unsigned char *bin_result,
       int (*digest_stream)(FILE *, void *))
{
  FILE *fp;
  int err;

  if (STREQ (filename, "-"))
    {
      have_read_stdin = 1;
      fp = stdin;
#if O_BINARY
      /* If we need binary reads from a pipe or redirected stdin, we need
     to switch it to BINARY mode here, since stdin is already open.  */
      if (binary)
    SET_BINARY (fileno (stdin));
#endif
    }
  else
    {
      /* OPENOPTS is a macro.  It varies with the system.
     Some systems distinguish between internal and
     external text representations.  */

      fp = fopen (filename, OPENOPTS (binary));
      if (fp == NULL)
    {
      error (0, errno, "%s", filename);
      return 1;
    }
    }

  err = (*digest_stream) (fp, bin_result);
  if (err)
    {
      error (0, errno, "%s", filename);
      if (fp != stdin)
    fclose (fp);
      return 1;
    }

  if (fp != stdin && fclose (fp) == EOF)
    {
      error (0, errno, "%s", filename);
      return 1;
    }

  return 0;
}

static int
digest_check (const char *checkfile_name, int (*digest_stream)(FILE *, void *))
{
  FILE *checkfile_stream;
  int n_properly_formated_lines = 0;
  int n_mismatched_checksums = 0;
  int n_open_or_read_failures = 0;
  unsigned char bin_buffer[MAX_DIGEST_BIN_BYTES];
  size_t line_number;
  char *line;
  size_t line_chars_allocated;

  if (STREQ (checkfile_name, "-"))
    {
      have_read_stdin = 1;
      checkfile_name = _("standard input");
      checkfile_stream = stdin;
    }
  else
    {
      checkfile_stream = fopen (checkfile_name, "r");
      if (checkfile_stream == NULL)
    {
      error (0, errno, "%s", checkfile_name);
      return 1;
    }
    }

  SET_MODE (fileno (checkfile_stream), O_TEXT);
  line_number = 0;
  line = NULL;
  line_chars_allocated = 0;
  do
    {
      char *filename;
      int binary;
      unsigned char *hex_digest;
      int err;
      int line_length;

      ++line_number;

      line_length = getline (&line, &line_chars_allocated, checkfile_stream);
      if (line_length <= 0)
    break;

      /* Ignore comment lines, which begin with a '#' character.  */
      if (line[0] == '#')
    continue;

      /* Remove any trailing newline.  */
      if (line[line_length - 1] == '\n')
    line[--line_length] = '\0';

      err = split_3 (line, line_length, &hex_digest, &binary, &filename);
      if (err || !hex_digits (hex_digest))
    {
      if (warn)
        {
          error (0, 0,
             _("%s: %lu: improperly formatted %s checksum line"),
             checkfile_name, (unsigned long) line_number,
             DIGEST_TYPE_STRING (algorithm));
        }
    }
      else
    {
      static const char bin2hex[] = { '0', '1', '2', '3',
                      '4', '5', '6', '7',
                      '8', '9', 'a', 'b',
                      'c', 'd', 'e', 'f' };
      int fail;

      ++n_properly_formated_lines;

      fail = digest_file (filename, binary, bin_buffer, digest_stream);

      if (fail)
        {
          ++n_open_or_read_failures;
          if (!status_only)
        {
          printf (_("%s: FAILED open or read\n"), filename);
          fflush (stdout);
        }
        }
      else
        {
          size_t digest_bin_bytes = digest_hex_bytes / 2;
          size_t cnt;
          /* Compare generated binary number with text representation
         in check file.  Ignore case of hex digits.  */
          for (cnt = 0; cnt < digest_bin_bytes; ++cnt)
        {
          if (TOLOWER (hex_digest[2 * cnt])
              != bin2hex[bin_buffer[cnt] >> 4]
              || (TOLOWER (hex_digest[2 * cnt + 1])
              != (bin2hex[bin_buffer[cnt] & 0xf])))
            break;
        }
          if (cnt != digest_bin_bytes)
        ++n_mismatched_checksums;

          if (!status_only)
        {
          printf ("%s: %s\n", filename,
              (cnt != digest_bin_bytes ? _("FAILED") : _("OK")));
          fflush (stdout);
        }
        }
    }
    }
  while (!feof (checkfile_stream) && !ferror (checkfile_stream));

  if (line)
    free (line);

  if (ferror (checkfile_stream))
    {
      error (0, 0, _("%s: read error"), checkfile_name);
      return 1;
    }

  if (checkfile_stream != stdin && fclose (checkfile_stream) == EOF)
    {
      error (0, errno, "%s", checkfile_name);
      return 1;
    }

  if (n_properly_formated_lines == 0)
    {
      /* Warn if no tests are found.  */
      error (0, 0, _("%s: no properly formatted %s checksum lines found"),
         checkfile_name, DIGEST_TYPE_STRING (algorithm));
    }
  else
    {
      if (!status_only)
    {
      int n_computed_checkums = (n_properly_formated_lines
                     - n_open_or_read_failures);

      if (n_open_or_read_failures > 0)
        {
          error (0, 0,
             _("WARNING: %d of %d listed %s could not be read"),
             n_open_or_read_failures, n_properly_formated_lines,
             (n_properly_formated_lines == 1
              ? _("file") : _("files")));
        }

      if (n_mismatched_checksums > 0)
        {
          error (0, 0,
           _("WARNING: %d of %d computed %s did NOT match"),
             n_mismatched_checksums, n_computed_checkums,
             (n_computed_checkums == 1
              ? _("checksum") : _("checksums")));
        }
    }
    }

  return ((n_properly_formated_lines > 0 && n_mismatched_checksums == 0
       && n_open_or_read_failures == 0) ? 0 : 1);
}

int
main (int argc, char **argv)
{
  unsigned char bin_buffer[MAX_DIGEST_BIN_BYTES];
  int do_check = 0;
  int opt;
  char **string = NULL;
  size_t n_strings = 0;
  size_t err = 0;
  int file_type_specified = 0;

#if O_BINARY
  /* Binary is default on MSDOS, so the actual file contents
     are used in computation.  */
  int binary = 1;
#else
  /* Text is default of the Plumb/Lankester format.  */
  int binary = 0;
#endif

  /* Setting values of global variables.  */
  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  while ((opt = getopt_long (argc, argv, "bctw", long_options, NULL)) != -1)
    switch (opt)
      {
      case 0:           /* long option */
    break;
      case 1: /* --string */
    {
      if (string == NULL)
        string = (char **) xmalloc ((argc - 1) * sizeof (char *));

      if (optarg == NULL)
        optarg = "";
      string[n_strings++] = optarg;
    }
    break;
      case 'b':
    file_type_specified = 1;
    binary = 1;
    break;
      case 'c':
    do_check = 1;
    break;
      case 2:
    status_only = 1;
    warn = 0;
    break;
      case 't':
    file_type_specified = 1;
    binary = 0;
    break;
      case 'w':
    status_only = 0;
    warn = 1;
    break;
      case_GETOPT_HELP_CHAR;
      case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
      default:
    usage (EXIT_FAILURE);
      }

  min_digest_line_length = MIN_DIGEST_LINE_LENGTH (algorithm);
  digest_hex_bytes = DIGEST_HEX_BYTES (algorithm);

  if (file_type_specified && do_check)
    {
      error (0, 0, _("the --binary and --text options are meaningless when \
verifying checksums"));
      usage (EXIT_FAILURE);
    }

  if (n_strings > 0 && do_check)
    {
      error (0, 0,
         _("the --string and --check options are mutually exclusive"));
      usage (EXIT_FAILURE);
    }

  if (status_only && !do_check)
    {
      error (0, 0,
       _("the --status option is meaningful only when verifying checksums"));
      usage (EXIT_FAILURE);
    }

  if (warn && !do_check)
    {
      error (0, 0,
       _("the --warn option is meaningful only when verifying checksums"));
      usage (EXIT_FAILURE);
    }

  if (n_strings > 0)
    {
      size_t i;

      if (optind < argc)
    {
      error (0, 0, _("no files may be specified when using --string"));
      usage (EXIT_FAILURE);
    }
      for (i = 0; i < n_strings; ++i)
    {
      size_t cnt;
      if (algorithm == ALG_MD5)
        md5_buffer (string[i], strlen (string[i]), bin_buffer);
      else
        sha_buffer (string[i], strlen (string[i]), bin_buffer);

      for (cnt = 0; cnt < (digest_hex_bytes / 2); ++cnt)
        printf ("%02x", bin_buffer[cnt]);

      printf ("  \"%s\"\n", string[i]);
    }
    }
  else if (do_check)
    {
      if (optind + 1 < argc)
    {
      error (0, 0,
         _("only one argument may be specified when using --check"));
      usage (EXIT_FAILURE);
    }

      err = digest_check ((optind == argc) ? "-" : argv[optind],
              DIGEST_STREAM (algorithm));
    }
  else
    {
      if (optind == argc)
    argv[argc++] = "-";

      for (; optind < argc; ++optind)
    {
      int fail;
      char *file = argv[optind];

      fail = digest_file (file, binary, bin_buffer,
                  DIGEST_STREAM (algorithm));
      err |= fail;
      if (!fail)
        {
          size_t i;

          /* Output a leading backslash if the file name contains
         a newline or backslash.  */
          if (strchr (file, '\n') || strchr (file, '\\'))
        putchar ('\\');

          for (i = 0; i < (digest_hex_bytes / 2); ++i)
        printf ("%02x", bin_buffer[i]);

          putchar (' ');
          if (binary)
        putchar ('*');
          else
        putchar (' ');

          /* Translate each NEWLINE byte to the string, "\\n",
         and each backslash to "\\\\".  */
          for (i = 0; i < strlen (file); ++i)
        {
          switch (file[i])
            {
            case '\n':
              fputs ("\\n", stdout);
              break;

            case '\\':
              fputs ("\\\\", stdout);
              break;

            default:
              putchar (file[i]);
              break;
            }
        }
          putchar ('\n');
        }
    }
    }

  if (have_read_stdin && fclose (stdin) == EOF)
    error (EXIT_FAILURE, errno, _("standard input"));

  exit (err == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/md5.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/md5.h used in: Step 966
/* md5.h - Declaration of functions and data types used for MD5 sum
   computing library functions.
   Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc.
   NOTE: The canonical source of this file is maintained with the GNU C
   Library.  Bugs can be reported to bug-glibc@prep.ai.mit.edu.

   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.  */

#ifndef _MD5_H
#define _MD5_H 1

#include <stdio.h>

#if defined HAVE_LIMITS_H || _LIBC
# include <limits.h>
#endif

/* The following contortions are an attempt to use the C preprocessor
   to determine an unsigned integral type that is 32 bits wide.  An
   alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
   doing that would require that the configure script compile and *run*
   the resulting executable.  Locally running cross-compiled executables
   is usually not possible.  */

#ifdef _LIBC
# include <stdint.h>
typedef uint32_t md5_uint32;
typedef uintptr_t md5_uintptr;
#else
# if defined __STDC__ && __STDC__
#  define UINT_MAX_32_BITS 4294967295U
# else
#  define UINT_MAX_32_BITS 0xFFFFFFFF
# endif

/* If UINT_MAX isn't defined, assume it's a 32-bit type.
   This should be valid for all systems GNU cares about because
   that doesn't include 16-bit systems, and only modern systems
   (that certainly have <limits.h>) have 64+-bit integral types.  */

# ifndef UINT_MAX
#  define UINT_MAX UINT_MAX_32_BITS
# endif

# if UINT_MAX == UINT_MAX_32_BITS
   typedef unsigned int md5_uint32;
# else
#  if USHRT_MAX == UINT_MAX_32_BITS
    typedef unsigned short md5_uint32;
#  else
#   if ULONG_MAX == UINT_MAX_32_BITS
     typedef unsigned long md5_uint32;
#   else
     /* The following line is intended to evoke an error.
        Using #error is not portable enough.  */
     "Cannot determine unsigned 32-bit data type."
#   endif
#  endif
# endif
/* We have to make a guess about the integer type equivalent in size
   to pointers which should always be correct.  */
typedef unsigned long int md5_uintptr;
#endif

#undef __P
#if defined (__STDC__) && __STDC__
#define __P(x) x
#else
#define __P(x) ()
#endif

/* Structure to save state of computation between the single steps.  */
struct md5_ctx
{
  md5_uint32 A;
  md5_uint32 B;
  md5_uint32 C;
  md5_uint32 D;

  md5_uint32 total[2];
  md5_uint32 buflen;
  char buffer[128];
};

/*
 * The following three functions are build up the low level used in
 * the functions `md5_stream' and `md5_buffer'.
 */

/* Initialize structure containing state of computation.
   (RFC 1321, 3.3: Step 3)  */
extern void md5_init_ctx __P ((struct md5_ctx *ctx));

/* Starting with the result of former calls of this function (or the
   initialization function update the context for the next LEN bytes
   starting at BUFFER.
   It is necessary that LEN is a multiple of 64!!! */
extern void md5_process_block __P ((const void *buffer, size_t len,
                    struct md5_ctx *ctx));

/* Starting with the result of former calls of this function (or the
   initialization function update the context for the next LEN bytes
   starting at BUFFER.
   It is NOT required that LEN is a multiple of 64.  */
extern void md5_process_bytes __P ((const void *buffer, size_t len,
                    struct md5_ctx *ctx));

/* Process the remaining bytes in the buffer and put result from CTX
   in first 16 bytes following RESBUF.  The result is always in little
   endian byte order, so that a byte-wise output yields to the wanted
   ASCII representation of the message digest.

   IMPORTANT: On some systems it is required that RESBUF be correctly
   aligned for a 32 bits value.  */
extern void *md5_finish_ctx __P ((struct md5_ctx *ctx, void *resbuf));


/* Put result from CTX in first 16 bytes following RESBUF.  The result is
   always in little endian byte order, so that a byte-wise output yields
   to the wanted ASCII representation of the message digest.

   IMPORTANT: On some systems it is required that RESBUF is correctly
   aligned for a 32 bits value.  */
extern void *md5_read_ctx __P ((const struct md5_ctx *ctx, void *resbuf));


/* Compute MD5 message digest for bytes read from STREAM.  The
   resulting message digest number will be written into the 16 bytes
   beginning at RESBLOCK.  */
extern int md5_stream __P ((FILE *stream, void *resblock));

/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
   result is always in little endian byte order, so that a byte-wise
   output yields to the wanted ASCII representation of the message
   digest.  */
extern void *md5_buffer __P ((const char *buffer, size_t len, void *resblock));

/* The following is from gnupg-1.0.2's cipher/bithelp.h.  */
/* Rotate a 32 bit integer by n bytes */
#if defined __GNUC__ && defined __i386__
static inline md5_uint32
rol(md5_uint32 x, int n)
{
  __asm__("roll %%cl,%0"
      :"=r" (x)
      :"0" (x),"c" (n));
  return x;
}
#else
# define rol(x,n) ( ((x) << (n)) | ((x) >> (32-(n))) )
#endif

#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/sha.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/sha.h used in: Step 966
/* sha.h - Declaration of functions and datatypes for SHA1 sum computing
   library functions.

   Copyright (C) 1999, Scott G. Miller
*/

#ifndef _SHA_H
# define _SHA_H 1

# include <stdio.h>
# include "md5.h"

/* Structure to save state of computation between the single steps.  */
struct sha_ctx
{
  md5_uint32 A;
  md5_uint32 B;
  md5_uint32 C;
  md5_uint32 D;
  md5_uint32 E;

  md5_uint32 total[2];
  md5_uint32 buflen;
  char buffer[128];
};


/* Starting with the result of former calls of this function (or the
   initialization function update the context for the next LEN bytes
   starting at BUFFER.
   It is necessary that LEN is a multiple of 64!!! */
extern void sha_process_block __P ((const void *buffer, size_t len,
                            struct sha_ctx *ctx));

/* Starting with the result of former calls of this function (or the
   initialization function update the context for the next LEN bytes
   starting at BUFFER.
   It is NOT required that LEN is a multiple of 64.  */
extern void sha_process_bytes __P((const void *buffer, size_t len,
                    struct sha_ctx *ctx));

/* Initialize structure containing state of computation. */
extern void sha_init_ctx __P ((struct sha_ctx *ctx));

/* Process the remaining bytes in the buffer and put result from CTX
   in first 16 bytes following RESBUF.  The result is always in little
   endian byte order, so that a byte-wise output yields to the wanted
   ASCII representation of the message digest.

   IMPORTANT: On some systems it is required that RESBUF is correctly
   aligned for a 32 bits value.  */
extern void *sha_finish_ctx __P ((struct sha_ctx *ctx, void *resbuf));


/* Put result from CTX in first 16 bytes following RESBUF.  The result is
   always in little endian byte order, so that a byte-wise output yields
   to the wanted ASCII representation of the message digest.

   IMPORTANT: On some systems it is required that RESBUF is correctly
   aligned for a 32 bits value.  */
extern void *sha_read_ctx __P ((const struct sha_ctx *ctx, void *resbuf));


/* Compute MD5 message digest for bytes read from STREAM.  The
   resulting message digest number will be written into the 16 bytes
   beginning at RESBLOCK.  */
extern int sha_stream __P ((FILE *stream, void *resblock));

/* Compute MD5 message digest for LEN bytes beginning at BUFFER.  The
   result is always in little endian byte order, so that a byte-wise
   output yields to the wanted ASCII representation of the message
   digest.  */
extern void *sha_buffer __P ((const char *buffer, size_t len, void *resblock));

#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/getline.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/getline.h used in: Step 966
/*  Copyright (C) 1995, 1997, 1999 Free Software Foundation, Inc.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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.  */

#ifndef GETLINE_H_
# define GETLINE_H_ 1

# include <stdio.h>

# ifndef PARAMS
#  if defined PROTOTYPES || (defined __STDC__ && __STDC__)
#   define PARAMS(Args) Args
#  else
#   define PARAMS(Args) ()
#  endif
# endif

# if __GLIBC__ < 2
int
getline PARAMS ((char **_lineptr, size_t *_n, FILE *_stream));

int
getdelim PARAMS ((char **_lineptr, size_t *_n, int _delimiter, FILE *_stream));
# endif

#endif /* not GETLINE_H_ */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/mv.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/mv.c used in: Step 968
/* mv -- move or rename files
   Copyright (C) 86, 89, 90, 91, 1995-2002 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.  */

/* Written by Mike Parker, David MacKenzie, and Jim Meyering */

#ifdef _AIX
 #pragma alloca
#endif

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include <assert.h>

#include "system.h"
#include "argmatch.h"
#include "backupfile.h"
#include "copy.h"
#include "cp-hash.h"
#include "dirname.h"
#include "error.h"
#include "path-concat.h"
#include "quote.h"
#include "remove.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "mv"

#define AUTHORS N_ ("Mike Parker, David MacKenzie, and Jim Meyering")

/* Initial number of entries in each hash table entry's table of inodes.  */
#define INITIAL_HASH_MODULE 100

/* Initial number of entries in the inode hash table.  */
#define INITIAL_ENTRY_TAB_SIZE 70

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  TARGET_DIRECTORY_OPTION = CHAR_MAX + 1,
  STRIP_TRAILING_SLASHES_OPTION,
  REPLY_OPTION
};

int isdir ();
int lstat ();

/* The name this program was run with. */
char *program_name;

/* Remove any trailing slashes from each SOURCE argument.  */
static int remove_trailing_slashes;

/* Valid arguments to the `--reply' option. */
static char const* const reply_args[] =
{
  "yes", "no", "query", 0
};

/* The values that correspond to the above strings. */
static int const reply_vals[] =
{
  I_ALWAYS_YES, I_ALWAYS_NO, I_ASK_USER
};

static struct option const long_options[] =
{
  {"backup", optional_argument, NULL, 'b'},
  {"force", no_argument, NULL, 'f'},
  {"interactive", no_argument, NULL, 'i'},
  {"reply", required_argument, NULL, REPLY_OPTION},
  {"strip-trailing-slashes", no_argument, NULL, STRIP_TRAILING_SLASHES_OPTION},
  {"suffix", required_argument, NULL, 'S'},
  {"target-directory", required_argument, NULL, TARGET_DIRECTORY_OPTION},
  {"update", no_argument, NULL, 'u'},
  {"verbose", no_argument, NULL, 'v'},
  {"version-control", required_argument, NULL, 'V'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

static void
rm_option_init (struct rm_options *x)
{
  x->unlink_dirs = 0;

  x->ignore_missing_files = 0;

  x->recursive = 1;

  /* Should we prompt for removal, too?  No.  Prompting for the `move'
     part is enough.  It implies removal.  */
  x->interactive = 0;
  x->stdin_tty = 0;

  x->verbose = 0;
}

static void
cp_option_init (struct cp_options *x)
{
  x->copy_as_regular = 0;  /* FIXME: maybe make this an option */
  x->dereference = DEREF_NEVER;
  x->unlink_dest_before_opening = 0;
  x->unlink_dest_after_failed_open = 0;
  x->hard_link = 0;
  x->interactive = I_UNSPECIFIED;
  x->move_mode = 1;
  x->myeuid = geteuid ();
  x->one_file_system = 0;
  x->preserve_ownership = 1;
  x->preserve_links = 1;
  x->preserve_mode = 1;
  x->preserve_timestamps = 1;
  x->require_preserve = 0;  /* FIXME: maybe make this an option */
  x->recursive = 1;
  x->sparse_mode = SPARSE_AUTO;  /* FIXME: maybe make this an option */
  x->symbolic_link = 0;
  x->set_mode = 0;
  x->mode = 0;
  x->stdin_tty = isatty (STDIN_FILENO);

  /* Find out the current file creation mask, to knock the right bits
     when using chmod.  The creation mask is set to be liberal, so
     that created directories can be written, even if it would not
     have been allowed with the mask this process was started with.  */
  x->umask_kill = ~ umask (0);

  x->update = 0;
  x->verbose = 0;
  x->xstat = lstat;
  x->dest_info = NULL;
  x->src_info = NULL;
}

/* If PATH is an existing directory, return nonzero, else 0.  */

static int
is_real_dir (const char *path)
{
  struct stat stats;

  return lstat (path, &stats) == 0 && S_ISDIR (stats.st_mode);
}

/* Move SOURCE onto DEST.  Handles cross-filesystem moves.
   If SOURCE is a directory, DEST must not exist.
   Return 0 if successful, non-zero if an error occurred.  */

static int
do_move (const char *source, const char *dest, const struct cp_options *x)
{
  static int first = 1;
  int copy_into_self;
  int rename_succeeded;
  int fail;

  if (first)
    {
      first = 0;

      /* Allocate space for remembering copied and created files.  */
      hash_init ();
    }

  fail = copy (source, dest, 0, x, &copy_into_self, &rename_succeeded);

  if (!fail)
    {
      char const *dir_to_remove;
      if (copy_into_self)
    {
      /* In general, when copy returns with copy_into_self set, SOURCE is
         the same as, or a parent of DEST.  In this case we know it's a
         parent.  It doesn't make sense to move a directory into itself, and
         besides in some situations doing so would give highly nonintuitive
         results.  Run this `mkdir b; touch a c; mv * b' in an empty
         directory.  Here's the result of running echo `find b -print`:
         b b/a b/b b/b/a b/c.  Notice that only file `a' was copied
         into b/b.  Handle this by giving a diagnostic, removing the
         copied-into-self directory, DEST (`b/b' in the example),
         and failing.  */

      dir_to_remove = NULL;
      fail = 1;
    }
      else if (rename_succeeded)
    {
      /* No need to remove anything.  SOURCE was successfully
         renamed to DEST.  Or the user declined to rename a file.  */
      dir_to_remove = NULL;
    }
      else
    {
      /* This may mean SOURCE and DEST referred to different devices.
         It may also conceivably mean that even though they referred
         to the same device, rename wasn't implemented for that device.

         E.g., (from Joel N. Weber),
         [...] there might someday be cases where you can't rename
         but you can copy where the device name is the same, especially
         on Hurd.  Consider an ftpfs with a primitive ftp server that
         supports uploading, downloading and deleting, but not renaming.

         Also, note that comparing device numbers is not a reliable
         check for `can-rename'.  Some systems can be set up so that
         files from many different physical devices all have the same
         st_dev field.  This is a feature of some NFS mounting
         configurations.

         We reach this point if SOURCE has been successfully copied
         to DEST.  Now we have to remove SOURCE.

         This function used to resort to copying only when rename
         failed and set errno to EXDEV.  */

      dir_to_remove = source;
    }

      if (dir_to_remove != NULL)
    {
      struct rm_options rm_options;
      enum RM_status status;

      rm_option_init (&rm_options);
      rm_options.verbose = x->verbose;

      status = rm (1, &dir_to_remove, &rm_options);
      assert (VALID_STATUS (status));
      if (status == RM_ERROR)
        fail = 1;
    }
    }

  return fail;
}

/* Move file SOURCE onto DEST.  Handles the case when DEST is a directory.
   DEST_IS_DIR must be nonzero when DEST is a directory or a symlink to a
   directory and zero otherwise.
   Return 0 if successful, non-zero if an error occurred.  */

static int
movefile (char *source, char *dest, int dest_is_dir,
      const struct cp_options *x)
{
  int dest_had_trailing_slash = strip_trailing_slashes (dest);
  int fail;

  /* This code was introduced to handle the ambiguity in the semantics
     of mv that is induced by the varying semantics of the rename function.
     Some systems (e.g., Linux) have a rename function that honors a
     trailing slash, while others (like Solaris 5,6,7) have a rename
     function that ignores a trailing slash.  I believe the Linux
     rename semantics are POSIX and susv2 compliant.  */

  if (remove_trailing_slashes)
    strip_trailing_slashes (source);

  /* In addition to when DEST is a directory, if DEST has a trailing
     slash and neither SOURCE nor DEST is a directory, presume the target
     is DEST/`basename source`.  This converts `mv x y/' to `mv x y/x'.
     This change means that the command `mv any file/' will now fail
     rather than performing the move.  The case when SOURCE is a
     directory and DEST is not is properly diagnosed by do_move.  */

  if (dest_is_dir || (dest_had_trailing_slash && !is_real_dir (source)))
    {
      /* DEST is a directory; build full target filename. */
      char const *src_basename = base_name (source);
      char *new_dest = path_concat (dest, src_basename, NULL);
      if (new_dest == NULL)
    xalloc_die ();
      strip_trailing_slashes (new_dest);
      fail = do_move (source, new_dest, x);
      free (new_dest);
    }
  else
    {
      fail = do_move (source, dest, x);
    }

  return fail;
}

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("\
Usage: %s [OPTION]... SOURCE DEST\n\
  or:  %s [OPTION]... SOURCE... DIRECTORY\n\
  or:  %s [OPTION]... --target-directory=DIRECTORY SOURCE...\n\
"),
          program_name, program_name, program_name);
      fputs (_("\
Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n\
\n\
"), stdout);
      fputs (_("\
Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
      fputs (_("\
      --backup[=CONTROL]       make a backup of each existing destination file\n\
  -b                           like --backup but does not accept an argument\n\
  -f, --force                  do not prompt before overwriting\n\
                                 equivalent to --reply=yes\n\
  -i, --interactive            prompt before overwrite\n\
                                 equivalent to --reply=query\n\
"), stdout);
      fputs (_("\
      --reply={yes,no,query}   specify how to handle the prompt about an\n\
                                 existing destination file\n\
      --strip-trailing-slashes remove any trailing slashes from each SOURCE\n\
                                 argument\n\
  -S, --suffix=SUFFIX          override the usual backup suffix\n\
"), stdout);
      fputs (_("\
      --target-directory=DIRECTORY  move all SOURCE arguments into DIRECTORY\n\
  -u, --update                 move only when the SOURCE file is newer\n\
                                 than the destination file or when the\n\
                                 destination file is missing\n\
  -v, --verbose                explain what is being done\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      fputs (_("\
\n\
The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
The version control method may be selected via the --backup option or through\n\
the VERSION_CONTROL environment variable.  Here are the values:\n\
\n\
"), stdout);
      fputs (_("\
  none, off       never make backups (even if --backup is given)\n\
  numbered, t     make numbered backups\n\
  existing, nil   numbered if numbered backups exist, simple otherwise\n\
  simple, never   always make simple backups\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

int
main (int argc, char **argv)
{
  int c;
  int errors;
  int make_backups = 0;
  int dest_is_dir;
  char *backup_suffix_string;
  char *version_control_string = NULL;
  struct cp_options x;
  char *target_directory = NULL;
  int target_directory_specified;
  unsigned int n_files;
  char **file;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  cp_option_init (&x);

  /* FIXME: consider not calling getenv for SIMPLE_BACKUP_SUFFIX unless
     we'll actually use backup_suffix_string.  */
  backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");

  errors = 0;

  while ((c = getopt_long (argc, argv, "bfiuvS:V:", long_options, NULL)) != -1)
    {
      switch (c)
    {
    case 0:
      break;

    case 'V':  /* FIXME: this is deprecated.  Remove it in 2001.  */
      error (0, 0,
         _("warning: --version-control (-V) is obsolete;  support for\
 it\nwill be removed in some future release.  Use --backup=%s instead."
           ), optarg);
      /* Fall through.  */

    case 'b':
      make_backups = 1;
      if (optarg)
        version_control_string = optarg;
      break;
    case 'f':
      x.interactive = I_ALWAYS_YES;
      break;
    case 'i':
      x.interactive = I_ASK_USER;
      break;
    case REPLY_OPTION:
      x.interactive = XARGMATCH ("--reply", optarg,
                     reply_args, reply_vals);
      break;
    case STRIP_TRAILING_SLASHES_OPTION:
      remove_trailing_slashes = 1;
      break;
    case TARGET_DIRECTORY_OPTION:
      target_directory = optarg;
      break;
    case 'u':
      x.update = 1;
      break;
    case 'v':
      x.verbose = 1;
      break;
    case 'S':
      make_backups = 1;
      backup_suffix_string = optarg;
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (EXIT_FAILURE);
    }
    }

  n_files = argc - optind;
  file = argv + optind;

  target_directory_specified = (target_directory != NULL);
  if (target_directory == NULL && n_files != 0)
    target_directory = file[n_files - 1];

  dest_is_dir = (n_files > 0 && isdir (target_directory));

  if (n_files == 0 || (n_files == 1 && !target_directory_specified))
    {
      error (0, 0, _("missing file argument"));
      usage (EXIT_FAILURE);
    }

  if (target_directory_specified)
    {
      if (!dest_is_dir)
    {
      error (0, 0, _("specified target, %s is not a directory"),
         quote (target_directory));
      usage (EXIT_FAILURE);
    }
    }
  else if (n_files > 2 && !dest_is_dir)
    {
      error (0, 0,
        _("when moving multiple files, last argument must be a directory"));
      usage (EXIT_FAILURE);
    }

  if (backup_suffix_string)
    simple_backup_suffix = xstrdup (backup_suffix_string);

  x.backup_type = (make_backups
           ? xget_version (_("backup type"),
                   version_control_string)
           : none);

  /* Move each arg but the last into the target_directory.  */
  {
    unsigned int last_file_idx = (target_directory_specified
                  ? n_files - 1
                  : n_files - 2);
    unsigned int i;

    /* Initialize the hash table only if we'll need it.
       The problem it is used to detect can arise only if there are
       two or more files to move.  */
    if (last_file_idx)
      dest_info_init (&x);

    for (i = 0; i <= last_file_idx; ++i)
      errors |= movefile (file[i], target_directory, dest_is_dir, &x);
  }

  exit (errors);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/remove.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/remove.c used in: Step 969
/* remove.c -- core functions for removing files and directories
   Copyright (C) 88, 90, 91, 1994-2003 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.  */

/* Extracted from rm.c and librarified, then rewritten by Jim Meyering.  */

#ifdef _AIX
 #pragma alloca
#endif

#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <setjmp.h>
#include <assert.h>

#include "save-cwd.h"
#include "system.h"
#include "cycle-check.h"
#include "dirname.h"
#include "error.h"
#include "euidaccess.h"
#include "file-type.h"
#include "hash.h"
#include "hash-pjw.h"
#include "obstack.h"
#include "quote.h"
#include "remove.h"

/* Avoid shadowing warnings because these are functions declared
   in dirname.h as well as locals used below.  */
#define dir_name rm_dir_name
#define dir_len rm_dir_len

#define obstack_chunk_alloc malloc
#define obstack_chunk_free free

/* FIXME: if possible, use autoconf...  */
#ifdef __GLIBC__
# define ROOT_CAN_UNLINK_DIRS 0
#else
# define ROOT_CAN_UNLINK_DIRS 1
#endif

enum Ternary
  {
    T_UNKNOWN = 2,
    T_NO,
    T_YES
  };
typedef enum Ternary Ternary;

/* The prompt function may be called twice a given directory.
   The first time, we ask whether to descend into it, and the
   second time, we ask whether to remove it.  */
enum Prompt_action
  {
    PA_DESCEND_INTO_DIR = 2,
    PA_REMOVE_DIR
  };

/* On systems with an lstat function that accepts the empty string,
   arrange to make lstat calls go through the wrapper function.  */
#if HAVE_LSTAT_EMPTY_STRING_BUG
int rpl_lstat (const char *, struct stat *);
# define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf)
#endif

#ifdef D_INO_IN_DIRENT
# define D_INO(dp) ((dp)->d_ino)
# define ENABLE_CYCLE_CHECK
#else
/* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */
# define D_INO(dp) 1
#endif

/* Initial capacity of per-directory hash table of entries that have
   been processed but not been deleted.  */
#define HT_UNREMOVABLE_INITIAL_CAPACITY 13

/* An entry in the active directory stack.
   Each entry corresponds to an `active' directory.  */
struct AD_ent
{
  /* For a given active directory, this is the set of names of
     entries in that directory that could/should not be removed.
     For example, `.' and `..', as well as files/dirs for which
     unlink/rmdir failed e.g., due to access restrictions.  */
  Hash_table *unremovable;

  /* Record the status for a given active directory; we need to know
     whether an entry was not removed, either because of an error or
     because the user declined.  */
  enum RM_status status;

  union
  {
    /* The directory's dev/ino.  Used to ensure that `chdir some-subdir', then
       `chdir ..' takes us back to the same directory from which we started).
       (valid for all but the bottommost entry on the stack.  */
    struct dev_ino a;

    /* Enough information to restore the initial working directory.
       (valid only for the bottommost entry on the stack)  */
    struct saved_cwd saved_cwd;
  } u;
};

int yesno ();

extern char *program_name;

struct dirstack_state
{
  /* The name of the directory (starting with and relative to a command
     line argument) being processed.  When a subdirectory is entered, a new
     component is appended (pushed).  Remove (pop) the top component
     upon chdir'ing out of a directory.  This is used to form the full
     name of the current directory or a file therein, when necessary.  */
  struct obstack dir_stack;

  /* Stack of lengths of directory names (including trailing slash)
     appended to dir_stack.  We have to have a separate stack of lengths
     (rather than just popping back to previous slash) because the first
     element pushed onto the dir stack may contain slashes.  */
  struct obstack len_stack;

  /* Stack of active directory entries.
     The first `active' directory is the initial working directory.
     Additional active dirs are pushed onto the stack as we `chdir'
     into each directory to be processed.  When finished with the
     hierarchy under a directory, pop the active dir stack.  */
  struct obstack Active_dir;

  /* Used to detect cycles.  */
  struct cycle_check_state cycle_check_state;

  /* Target of a longjmp in case rm detects a directory cycle.  */
  jmp_buf current_arg_jumpbuf;
};
typedef struct dirstack_state Dirstack_state;

Dirstack_state *
ds_init ()
{
  Dirstack_state *ds = XMALLOC (struct dirstack_state, 1);
  obstack_init (&ds->dir_stack);
  obstack_init (&ds->len_stack);
  obstack_init (&ds->Active_dir);
  return ds;
}

void
ds_free (Dirstack_state *ds)
{
  obstack_free (&ds->dir_stack, NULL);
  obstack_free (&ds->len_stack, NULL);
  obstack_free (&ds->Active_dir, NULL);
}

static void
hash_freer (void *x)
{
  free (x);
}

static bool
hash_compare_strings (void const *x, void const *y)
{
  return STREQ (x, y) ? true : false;
}

static inline void
push_dir (Dirstack_state *ds, const char *dir_name)
{
  size_t len;

  len = strlen (dir_name);

  /* Append the string onto the stack.  */
  obstack_grow (&ds->dir_stack, dir_name, len);

  /* Append a trailing slash.  */
  obstack_1grow (&ds->dir_stack, '/');

  /* Add one for the slash.  */
  ++len;

  /* Push the length (including slash) onto its stack.  */
  obstack_grow (&ds->len_stack, &len, sizeof (len));
}

/* Return the entry name of the directory on the top of the stack
   in malloc'd storage.  */
static inline char *
top_dir (Dirstack_state const *ds)
{
  int n_lengths = obstack_object_size (&ds->len_stack) / sizeof (size_t);
  size_t *length = (size_t *) obstack_base (&ds->len_stack);
  size_t top_len = length[n_lengths - 1];
  char const *p = obstack_next_free (&ds->dir_stack) - top_len;
  char *q = xmalloc (top_len);
  memcpy (q, p, top_len - 1);
  q[top_len - 1] = 0;
  return q;
}

static inline void
pop_dir (Dirstack_state *ds)
{
  int n_lengths = obstack_object_size (&ds->len_stack) / sizeof (size_t);
  size_t *length = (size_t *) obstack_base (&ds->len_stack);
  size_t top_len;

  assert (n_lengths > 0);
  top_len = length[n_lengths - 1];
  assert (top_len >= 2);

  /* Pop off the specified length of pathname.  */
  assert (obstack_object_size (&ds->dir_stack) >= top_len);
  obstack_blank (&ds->dir_stack, -top_len);

  /* Pop the length stack, too.  */
  assert (obstack_object_size (&ds->len_stack) >= sizeof (size_t));
  obstack_blank (&ds->len_stack, -(int) sizeof (size_t));
}

/* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
   buffer, DST, so that the last source byte is at the end of the destination
   buffer.  If SRC_LEN is longer than DST_LEN, then set *TRUNCATED to non-zero.
   Set *RESULT to point to the beginning of (the portion of) the source data
   in DST.  Return the number of bytes remaining in the destination buffer.  */

static size_t
right_justify (char *dst, size_t dst_len, const char *src, size_t src_len,
           char **result, int *truncated)
{
  const char *sp;
  char *dp;

  if (src_len <= dst_len)
    {
      sp = src;
      dp = dst + (dst_len - src_len);
      *truncated = 0;
    }
  else
    {
      sp = src + (src_len - dst_len);
      dp = dst;
      src_len = dst_len;
      *truncated = 1;
    }

  *result = memcpy (dp, sp, src_len);
  return dst_len - src_len;
}

/* Using the global directory name obstack, create the full path to FILENAME.
   Return it in sometimes-realloc'd space that should not be freed by the
   caller.  Realloc as necessary.  If realloc fails, use a static buffer
   and put as long a suffix in that buffer as possible.  */

#define full_filename(Filename) full_filename_ (ds, Filename)
static char *
full_filename_ (Dirstack_state const *ds, const char *filename)
{
  static char *buf = NULL;
  static size_t n_allocated = 0;

  int dir_len = obstack_object_size (&ds->dir_stack);
  char *dir_name = (char *) obstack_base (&ds->dir_stack);
  size_t n_bytes_needed;
  size_t filename_len;

  filename_len = strlen (filename);
  n_bytes_needed = dir_len + filename_len + 1;

  if (n_allocated < n_bytes_needed)
    {
      /* This code requires that realloc accept NULL as the first arg.
         This function must not use xrealloc.  Otherwise, an out-of-memory
     error involving a file name to be expanded here wouldn't ever
     be issued.  Use realloc and fall back on using a static buffer
     if memory allocation fails.  */
      buf = realloc (buf, n_bytes_needed);
      n_allocated = n_bytes_needed;

      if (buf == NULL)
    {
#define SBUF_SIZE 512
#define ELLIPSES_PREFIX "[...]"
      static char static_buf[SBUF_SIZE];
      int truncated;
      size_t len;
      char *p;

      len = right_justify (static_buf, SBUF_SIZE, filename,
                   filename_len + 1, &p, &truncated);
      right_justify (static_buf, len, dir_name, dir_len, &p, &truncated);
      if (truncated)
        {
          memcpy (static_buf, ELLIPSES_PREFIX,
              sizeof (ELLIPSES_PREFIX) - 1);
        }
      return p;
    }
    }

  if (filename_len == 1 && *filename == '.' && dir_len)
    {
      /* FILENAME is just `.' and dir_len is nonzero.
     Copy the directory part, omitting the trailing slash,
     and append a trailing zero byte.  */
      char *p = mempcpy (buf, dir_name, dir_len - 1);
      *p = 0;
    }
  else
    {
      /* Copy the directory part, including trailing slash, and then
     append the filename part, including a trailing zero byte.  */
      memcpy (mempcpy (buf, dir_name, dir_len), filename, filename_len + 1);
      assert (strlen (buf) + 1 == n_bytes_needed);
    }

  return buf;
}

static size_t
AD_stack_height (Dirstack_state const *ds)
{
  return obstack_object_size (&ds->Active_dir) / sizeof (struct AD_ent);
}

static struct AD_ent *
AD_stack_top (Dirstack_state const *ds)
{
  return (struct AD_ent *)
    ((char *) obstack_next_free (&ds->Active_dir) - sizeof (struct AD_ent));
}

static void
AD_stack_pop (Dirstack_state *ds)
{
  /* operate on Active_dir.  pop and free top entry */
  struct AD_ent *top = AD_stack_top (ds);
  if (top->unremovable)
    hash_free (top->unremovable);
  obstack_blank (&ds->Active_dir, -(int) sizeof (struct AD_ent));
  pop_dir (ds);
}

/* chdir `up' one level.
   Whenever using chdir '..', verify that the post-chdir
   dev/ino numbers for `.' match the saved ones.
   Return the name (in malloc'd storage) of the
   directory (usually now empty) from which we're coming.  */
static char *
AD_pop_and_chdir (Dirstack_state *ds)
{
  /* Get the name of the current directory from the top of the stack.  */
  char *dir = top_dir (ds);
  enum RM_status old_status = AD_stack_top(ds)->status;
  struct stat sb;
  struct AD_ent *top;

  AD_stack_pop (ds);

  /* Propagate any failure to parent.  */
  UPDATE_STATUS (AD_stack_top(ds)->status, old_status);

  assert (AD_stack_height (ds));

  top = AD_stack_top (ds);
  if (1 < AD_stack_height (ds))
    {
      /* We can give a better diagnostic here, since the target is relative. */
      if (chdir (".."))
    {
      error (EXIT_FAILURE, errno,
         _("cannot chdir from %s to .."),
         quote (full_filename (".")));
    }
    }
  else
    {
      if (restore_cwd (&top->u.saved_cwd))
    error (EXIT_FAILURE, errno,
           _("failed to return to initial working directory"));
    }

  if (lstat (".", &sb))
    error (EXIT_FAILURE, errno,
       _("cannot lstat `.' in %s"), quote (full_filename (".")));

  if (1 < AD_stack_height (ds))
    {
      /*  Ensure that post-chdir dev/ino match the stored ones.  */
      if ( ! SAME_INODE (sb, top->u.a))
    error (EXIT_FAILURE, 0,
           _("%s changed dev/ino"), quote (full_filename (".")));
    }

  return dir;
}

/* Initialize *HT if it is NULL.
   Insert FILENAME into HT.  */
static void
AD_mark_helper (Hash_table **ht, char const *filename)
{
  if (*ht == NULL)
    *ht = hash_initialize (HT_UNREMOVABLE_INITIAL_CAPACITY, NULL, hash_pjw,
               hash_compare_strings, hash_freer);
  if (*ht == NULL)
    xalloc_die ();
  if (! hash_insert (*ht, filename))
    xalloc_die ();
}

/* Mark FILENAME (in current directory) as unremovable.  */
static void
AD_mark_as_unremovable (Dirstack_state *ds, char const *filename)
{
  AD_mark_helper (&AD_stack_top(ds)->unremovable, xstrdup (filename));
}

/* Mark the current directory as unremovable.  I.e., mark the entry
   in the parent directory corresponding to `.'.
   This happens e.g., when an opendir fails and the only name
   the caller has conveniently at hand is `.'.  */
static void
AD_mark_current_as_unremovable (Dirstack_state *ds)
{
  struct AD_ent *top = AD_stack_top (ds);
  const char *curr = top_dir (ds);

  assert (1 < AD_stack_height (ds));

  --top;
  AD_mark_helper (&top->unremovable, curr);
}

/* Push the initial cwd info onto the stack.
   This will always be the bottommost entry on the stack.  */
static void
AD_push_initial (Dirstack_state *ds, struct saved_cwd const *cwd)
{
  struct AD_ent *top;

  /* Extend the stack.  */
  obstack_blank (&ds->Active_dir, sizeof (struct AD_ent));

  /* Fill in the new values.  */
  top = AD_stack_top (ds);
  top->u.saved_cwd = *cwd;
  top->unremovable = NULL;
}

/* Push info about the current working directory (".") onto the
   active directory stack.  DIR is the ./-relative name through
   which we've just `chdir'd to this directory.  DIR_SB_FROM_PARENT
   is the result of calling lstat on DIR from the parent of DIR.  */
static void
AD_push (Dirstack_state *ds, char const *dir,
     struct stat const *dir_sb_from_parent)
{
  struct stat sb;
  struct AD_ent *top;

  push_dir (ds, dir);

  if (lstat (".", &sb))
    error (EXIT_FAILURE, errno,
       _("cannot lstat `.' in %s"), quote (full_filename (".")));

  if ( ! SAME_INODE (sb, *dir_sb_from_parent))
    error (EXIT_FAILURE, errno,
       _("%s changed dev/ino"), quote (full_filename (".")));

  /* Extend the stack.  */
  obstack_blank (&ds->Active_dir, sizeof (struct AD_ent));

  /* Fill in the new values.  */
  top = AD_stack_top (ds);
  top->u.a.st_dev = sb.st_dev;
  top->u.a.st_ino = sb.st_ino;
  top->unremovable = NULL;
}

static int
AD_is_removable (Dirstack_state const *ds, char const *file)
{
  struct AD_ent *top = AD_stack_top (ds);
  return ! (top->unremovable && hash_lookup (top->unremovable, file));
}

static bool
is_empty_dir (char const *dir)
{
  DIR *dirp = opendir (dir);
  if (dirp == NULL)
    {
      closedir (dirp);
      return false;
    }

  while (1)
    {
      struct dirent *dp;
      const char *f;

      errno = 0;
      dp = readdir (dirp);
      if (dp == NULL)
    {
      int saved_errno = errno;
      closedir (dirp);
      return saved_errno == 0 ? true : false;
    }

      f = dp->d_name;
      if ( ! DOT_OR_DOTDOT (f))
    {
      closedir (dirp);
      return false;
    }
    }
}

/* Prompt whether to remove FILENAME, if required via a combination of
   the options specified by X and/or file attributes.  If the file may
   be removed, return RM_OK.  If the user declines to remove the file,
   return RM_USER_DECLINED.  If not ignoring missing files and we
   cannot lstat FILENAME, then return RM_ERROR.

   Depending on MODE, ask whether to `descend into' or to `remove' the
   directory FILENAME.  MODE is ignored when FILENAME is not a directory.
   Set *IS_EMPTY to T_YES if FILENAME is an empty directory, and it is
   appropriate to try to remove it with rmdir (e.g. recursive mode).
   Don't even try to set *IS_EMPTY when MODE == PA_REMOVE_DIR.
   Set *IS_DIR to T_YES or T_NO if we happen to determine whether
   FILENAME is a directory.  */
static enum RM_status
prompt (Dirstack_state const *ds, char const *filename,
    struct rm_options const *x, enum Prompt_action mode,
    Ternary *is_dir, Ternary *is_empty)
{
  int write_protected = 0;
  *is_empty = T_UNKNOWN;
  *is_dir = T_UNKNOWN;

  if ((!x->ignore_missing_files && (x->interactive || x->stdin_tty)
       && (write_protected = (euidaccess (filename, W_OK) && errno == EACCES)))
      || x->interactive)
    {
      struct stat sbuf;
      if (lstat (filename, &sbuf))
    {
      /* lstat failed.  This happens e.g., with `rm '''.  */
      error (0, errno, _("cannot lstat %s"),
         quote (full_filename (filename)));
      return RM_ERROR;
    }

      /* Using permissions doesn't make sense for symlinks.  */
      if (S_ISLNK (sbuf.st_mode))
    {
      if ( ! x->interactive)
        return RM_OK;
      write_protected = 0;
    }

      /* Issue the prompt.  */
      {
    char const *quoted_name = quote (full_filename (filename));

    *is_dir = (S_ISDIR (sbuf.st_mode) ? T_YES : T_NO);

    /* FIXME: use a variant of error (instead of fprintf) that doesn't
       append a newline.  Then we won't have to declare program_name in
       this file.  */
    if (S_ISDIR (sbuf.st_mode)
        && x->recursive
        && mode == PA_DESCEND_INTO_DIR
        && ((*is_empty = (is_empty_dir (filename) ? T_YES : T_NO))
        == T_NO))
      fprintf (stderr,
           (write_protected
            ? _("%s: descend into write-protected directory %s? ")
            : _("%s: descend into directory %s? ")),
           program_name, quoted_name);
    else
      {
        /* TRANSLATORS: You may find it more convenient to translate
           the equivalent of _("%s: remove %s (write-protected) %s? ").
           It should avoid grammatical problems with the output
           of file_type.  */
        fprintf (stderr,
             (write_protected
              ? _("%s: remove write-protected %s %s? ")
              : _("%s: remove %s %s? ")),
             program_name, file_type (&sbuf), quoted_name);
      }

    if (!yesno ())
      return RM_USER_DECLINED;
      }
    }
  return RM_OK;
}

#if HAVE_STRUCT_DIRENT_D_TYPE
# define DT_IS_DIR(D) ((D)->d_type == DT_DIR)
#else
/* Use this only if the member exists -- i.e., don't return 0.  */
# define DT_IS_DIR(D) do_not_use_this_macro
#endif

#define DO_UNLINK(Filename, X)                      \
  do                                    \
    {                                   \
      if (unlink (Filename) == 0)                   \
    {                               \
      if ((X)->verbose)                     \
        printf (_("removed %s\n"), quote (full_filename (Filename))); \
      return RM_OK;                         \
    }                               \
                                    \
      if (errno == ENOENT && (X)->ignore_missing_files)         \
    return RM_OK;                           \
    }                                   \
  while (0)

#define DO_RMDIR(Filename, X)               \
  do                            \
    {                           \
      if (rmdir (Filename) == 0)            \
    {                       \
      if ((X)->verbose)             \
        printf (_("removed directory: %s\n"),   \
            quote (full_filename (Filename)));  \
      return RM_OK;                 \
    }                       \
                            \
      if (errno == ENOENT && (X)->ignore_missing_files) \
    return RM_OK;                   \
                            \
      if (errno == ENOTEMPTY || errno == EEXIST)    \
    return RM_NONEMPTY_DIR;             \
    }                           \
  while (0)

/* Remove the file or directory specified by FILENAME.
   Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED if not.
   But if FILENAME specifies a non-empty directory, return RM_NONEMPTY_DIR. */

static enum RM_status
remove_entry (Dirstack_state const *ds, char const *filename,
          struct rm_options const *x, struct dirent const *dp)
{
  Ternary is_dir;
  Ternary is_empty_directory;
  enum RM_status s = prompt (ds, filename, x, PA_DESCEND_INTO_DIR,
                 &is_dir, &is_empty_directory);

  if (s != RM_OK)
    return s;

  /* Why bother with the following #if/#else block?  Because on systems with
     an unlink function that *can* unlink directories, we must determine the
     type of each entry before removing it.  Otherwise, we'd risk unlinking an
     entire directory tree simply by unlinking a single directory;  then all
     the storage associated with that hierarchy would not be freed until the
     next reboot.  Not nice.  To avoid that, on such slightly losing systems, we
     need to call lstat to determine the type of each entry, and that represents
     extra overhead that -- it turns out -- we can avoid on GNU-libc-based
     systems, since there, unlink will never remove a directory.  */

#if ROOT_CAN_UNLINK_DIRS

  /* If we don't already know whether FILENAME is a directory, find out now.
     Then, if it's a non-directory, we can use unlink on it.  */
  if (is_dir == T_UNKNOWN)
    {
# if HAVE_STRUCT_DIRENT_D_TYPE
      if (dp && dp->d_type != DT_UNKNOWN)
    is_dir = DT_IS_DIR (dp) ? T_YES : T_NO;
      else
# endif
    {
      struct stat sbuf;
      if (lstat (filename, &sbuf))
        {
          if (errno == ENOENT && x->ignore_missing_files)
        return RM_OK;

          error (0, errno,
             _("cannot lstat %s"), quote (full_filename (filename)));
          return RM_ERROR;
        }

      is_dir = S_ISDIR (sbuf.st_mode) ? T_YES : T_NO;
    }
    }

  if (is_dir == T_NO)
    {
      /* At this point, barring race conditions, FILENAME is known
     to be a non-directory, so it's ok to try to unlink it.  */
      DO_UNLINK (filename, x);

      /* unlink failed with some other error code.  report it.  */
      error (0, errno, _("cannot remove %s"),
         quote (full_filename (filename)));
      return RM_ERROR;
    }

  if (! x->recursive)
    {
      error (0, EISDIR, _("cannot remove directory %s"),
         quote (full_filename (filename)));
      return RM_ERROR;
    }

  if (is_empty_directory == T_YES)
    {
      DO_RMDIR (filename, x);
      /* Don't diagnose any failure here.
     It'll be detected when the caller tries another way.  */
    }


#else /* ! ROOT_CAN_UNLINK_DIRS */

  if (is_dir == T_YES && ! x->recursive)
    {
      error (0, EISDIR, _("cannot remove directory %s"),
         quote (full_filename (filename)));
      return RM_ERROR;
    }

  /* is_empty_directory is set iff it's ok to use rmdir.
     Note that it's set only in interactive mode -- in which case it's
     an optimization that arranges so that the user is asked just
     once whether to remove the directory.  */
  if (is_empty_directory == T_YES)
    DO_RMDIR (filename, x);

  /* If we happen to know that FILENAME is a directory, return now
     and let the caller remove it -- this saves the overhead of a failed
     unlink call.  If FILENAME is a command-line argument, then dp is NULL,
     so we'll first try to unlink it.  Using unlink here is ok, because it
     cannot remove a directory.  */
  if ((dp && DT_IS_DIR (dp)) || is_dir == T_YES)
    return RM_NONEMPTY_DIR;

  DO_UNLINK (filename, x);

  /* Accept either EISDIR or EPERM as an indication that FILENAME may be
     a directory.  POSIX says that unlink must set errno to EPERM when it
     fails to remove a directory, while Linux-2.4.18 sets it to EISDIR.  */
  if ((errno != EISDIR && errno != EPERM) || ! x->recursive)
    {
      /* some other error code.  Report it and fail.
     Likewise, if we're trying to remove a directory without
     the --recursive option.  */
      error (0, errno, _("cannot remove %s"),
         quote (full_filename (filename)));
      return RM_ERROR;
    }
#endif

  return RM_NONEMPTY_DIR;
}

/* Remove entries in `.', the current working directory (cwd).
   Upon finding a directory that is both non-empty and that can be chdir'd
   into, return RM_OK and set *SUBDIR and fill in SUBDIR_SB, where
   SUBDIR is the malloc'd name of the subdirectory if the chdir succeeded,
   NULL otherwise (e.g., if opendir failed or if there was no subdirectory).
   Likewise, SUBDIR_SB is the result of calling lstat on SUBDIR.
   Return RM_OK if all entries are removed.  Return RM_ERROR if any
   entry cannot be removed.  Otherwise, return RM_USER_DECLINED if
   the user declines to remove at least one entry.  Remove as much as
   possible, continuing even if we fail to remove some entries.  */
static enum RM_status
remove_cwd_entries (Dirstack_state *ds, char **subdir, struct stat *subdir_sb,
            struct rm_options const *x)
{
  DIR *dirp = opendir (".");
  struct AD_ent *top = AD_stack_top (ds);
  enum RM_status status = top->status;

  assert (VALID_STATUS (status));
  *subdir = NULL;

  if (dirp == NULL)
    {
      if (errno != ENOENT || !x->ignore_missing_files)
    {
      error (0, errno, _("cannot open directory %s"),
         quote (full_filename (".")));
      return RM_ERROR;
    }
    }

  while (1)
    {
      struct dirent *dp;
      enum RM_status tmp_status;
      const char *f;

      /* Set errno to zero so we can distinguish between a readdir failure
     and when readdir simply finds that there are no more entries.  */
      errno = 0;
      if ((dp = readdir (dirp)) == NULL)
    {
      if (errno)
        {
          /* Save/restore errno across closedir call.  */
          int e = errno;
          closedir (dirp);
          errno = e;

          /* Arrange to give a diagnostic after exiting this loop.  */
          dirp = NULL;
        }
      break;
    }

      f = dp->d_name;
      if (DOT_OR_DOTDOT (f))
    continue;

      /* Skip files we've already tried/failed to remove.  */
      if ( ! AD_is_removable (ds, f))
    continue;

      /* Pass dp->d_type info to remove_entry so the non-glibc
     case can decide whether to use unlink or chdir.
     Systems without the d_type member will have to endure
     the performance hit of first calling lstat F. */
      tmp_status = remove_entry (ds, f, x, dp);
      switch (tmp_status)
    {
    case RM_OK:
      /* do nothing */
      break;

    case RM_ERROR:
    case RM_USER_DECLINED:
      AD_mark_as_unremovable (ds, f);
      UPDATE_STATUS (status, tmp_status);
      break;

    case RM_NONEMPTY_DIR:
      {
        /* Save a copy of errno, in case the preceding unlink (from
           remove_entry's DO_UNLINK) of a non-directory failed due
           to EPERM.  */
        int saved_errno = errno;

        /* Record dev/ino of F so that we can compare
           that with dev/ino of `.' after the chdir.
           This dev/ino pair is also used in cycle detection.  */
        if (lstat (f, subdir_sb))
          error (EXIT_FAILURE, errno, _("cannot lstat %s"),
             quote (full_filename (f)));

        if (chdir (f))
          {
        /* It is much more common that we reach this point for an
           inaccessible directory.  Hence the second diagnostic, below.
           However it is also possible that F is a non-directory.
           That can happen when we use the `! ROOT_CAN_UNLINK_DIRS'
           block of code and when DO_UNLINK fails due to EPERM.
           In that case, give a better diagnostic.  */
        if (errno == ENOTDIR)
          error (0, saved_errno, _("cannot remove %s"),
             quote (full_filename (f)));
        else
          error (0, errno, _("cannot chdir from %s to %s"),
             quote_n (0, full_filename (".")), quote_n (1, f));
        AD_mark_as_unremovable (ds, f);
        status = RM_ERROR;
        break;
          }
        if (cycle_check (&ds->cycle_check_state, subdir_sb))
          {
        error (0, 0, _("\
WARNING: Circular directory structure.\n\
This almost certainly means that you have a corrupted file system.\n\
NOTIFY YOUR SYSTEM MANAGER.\n\
The following directory is part of the cycle:\n  %s\n"),
               quote (full_filename (".")));
        longjmp (ds->current_arg_jumpbuf, 1);
          }

        *subdir = xstrdup (f);
        break;
      }
    }

      /* Record status for this directory.  */
      UPDATE_STATUS (top->status, status);

      if (*subdir)
    break;
    }

  if (dirp == NULL || CLOSEDIR (dirp) != 0)
    {
      /* Note that this diagnostic serves for both readdir
     and closedir failures.  */
      error (0, errno, _("reading directory %s"), quote (full_filename (".")));
      status = RM_ERROR;
    }

  return status;
}

/* Do this after each call to AD_push or AD_push_initial.
   Because the status = RM_OK bit is too remove-specific to
   go into the general-purpose AD_* package.  */
#define AD_INIT_OTHER_MEMBERS()         \
  do                        \
    {                       \
      AD_stack_top(ds)->status = RM_OK;     \
    }                       \
  while (0)

/*  Remove the hierarchy rooted at DIR.
    Do that by changing into DIR, then removing its contents, then
    returning to the original working directory and removing DIR itself.
    Don't use recursion.  Be careful when using chdir ".." that we
    return to the same directory from which we came, if necessary.
    Return 1 for success, 0 if some file cannot be removed or if
    a chdir fails.
    If the working directory cannot be restored, exit immediately.  */

static enum RM_status
remove_dir (Dirstack_state *ds, char const *dir, struct saved_cwd **cwd_state,
        struct rm_options const *x)
{
  enum RM_status status;
  struct stat dir_sb;

  /* Save any errno (from caller's failed remove_entry call), in case DIR
     is not a directory, so that we can give a reasonable diagnostic.  */
  int saved_errno = errno;

  if (*cwd_state == NULL)
    {
      *cwd_state = XMALLOC (struct saved_cwd, 1);
      if (save_cwd (*cwd_state))
    return RM_ERROR;
      AD_push_initial (ds, *cwd_state);
      AD_INIT_OTHER_MEMBERS ();
    }

  /* There is a race condition in that an attacker could replace the nonempty
     directory, DIR, with a symlink between the preceding call to rmdir
     (in our caller) and the chdir below.  However, the following lstat,
     along with the `stat (".",...' and dev/ino comparison in AD_push
     ensure that we detect it and fail.  */

  if (lstat (dir, &dir_sb))
    {
      error (0, errno,
         _("cannot lstat %s"), quote (full_filename (dir)));
      return RM_ERROR;
    }

  if (chdir (dir))
    {
      if (! S_ISDIR (dir_sb.st_mode))
    {
      /* This happens on Linux-2.4.18 when a non-privileged user tries
         to delete a file that is owned by another user in a directory
         like /tmp that has the S_ISVTX flag set.  */
      assert (saved_errno == EPERM);
      error (0, saved_errno,
         _("cannot remove %s"), quote (full_filename (dir)));
    }
      else
    {
      error (0, errno,
         _("cannot chdir from %s to %s"),
         quote_n (0, full_filename (".")), quote_n (1, dir));
    }
      return RM_ERROR;
    }

  AD_push (ds, dir, &dir_sb);
  AD_INIT_OTHER_MEMBERS ();

  status = RM_OK;

  while (1)
    {
      char *subdir = NULL;
      struct stat subdir_sb;
      enum RM_status tmp_status = remove_cwd_entries (ds,
                              &subdir, &subdir_sb, x);
      if (tmp_status != RM_OK)
    {
      UPDATE_STATUS (status, tmp_status);
      AD_mark_current_as_unremovable (ds);
    }
      if (subdir)
    {
      AD_push (ds, subdir, &subdir_sb);
      AD_INIT_OTHER_MEMBERS ();

      free (subdir);
      continue;
    }

      /* Execution reaches this point when we've removed the last
     removable entry from the current directory.  */
      {
    char *d = AD_pop_and_chdir (ds);

    /* Try to remove D only if remove_cwd_entries succeeded.  */
    if (tmp_status == RM_OK)
      {
        /* This does a little more work than necessary when it actually
           prompts the user.  E.g., we already know that D is a directory
           and that it's almost certainly empty, yet we lstat it.
           But that's no big deal since we're interactive.  */
        Ternary is_dir;
        Ternary is_empty;
        enum RM_status s = prompt (ds, d, x, PA_REMOVE_DIR,
                       &is_dir, &is_empty);

        if (s != RM_OK)
          {
        free (d);
        return s;
          }

        if (rmdir (d) == 0)
          {
        if (x->verbose)
          printf (_("removed directory: %s\n"),
              quote (full_filename (d)));
          }
        else
          {
        error (0, errno, _("cannot remove directory %s"),
               quote (full_filename (d)));
        AD_mark_as_unremovable (ds, d);
        status = RM_ERROR;
        UPDATE_STATUS (AD_stack_top(ds)->status, status);
          }
      }

    free (d);

    if (AD_stack_height (ds) == 1)
      break;
      }
    }

  return status;
}

/* Remove the file or directory specified by FILENAME.
   Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED if not.
   On input, the first time this function is called, CWD_STATE should be
   the address of a NULL pointer.  Do not modify it for any subsequent calls.
   On output, it is either that same NULL pointer or the address of
   a malloc'd `struct saved_cwd' that may be freed.  */

static enum RM_status
rm_1 (Dirstack_state *ds, char const *filename,
      struct rm_options const *x, struct saved_cwd **cwd_state)
{
  char *base = base_name (filename);
  enum RM_status status;

  if (DOT_OR_DOTDOT (base))
    {
      error (0, 0, _("cannot remove `.' or `..'"));
      return RM_ERROR;
    }

  status = remove_entry (ds, filename, x, NULL);
  if (status != RM_NONEMPTY_DIR)
    return status;

  return remove_dir (ds, filename, cwd_state, x);
}

/* Remove all files and/or directories specified by N_FILES and FILE.
   Apply the options in X.  */
enum RM_status
rm (size_t n_files, char const *const *file, struct rm_options const *x)
{
  struct saved_cwd *cwd_state = NULL;
  Dirstack_state *ds;

  /* Put the following two variables in static storage, so they can't
     be clobbered by the potential longjmp into this function.  */
  static enum RM_status status = RM_OK;
  static size_t i;

  ds = ds_init ();

  for (i = 0; i < n_files; i++)
    {
      enum RM_status s;
      cycle_check_init (&ds->cycle_check_state);
      /* In the event that rm_1->remove_dir->remove_cwd_entries detects
     a directory cycle, arrange to fail, give up on this FILE, but
     continue on with any other arguments.  */
      if (setjmp (ds->current_arg_jumpbuf))
    s = RM_ERROR;
      else
    s = rm_1 (ds, file[i], x, &cwd_state);
      assert (VALID_STATUS (s));
      UPDATE_STATUS (status, s);
    }

  ds_free (ds);

  XFREE (cwd_state);

  return status;
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/rm.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/rm.c used in: Step 972
/* `rm' file deletion utility for GNU.
   Copyright (C) 88, 90, 91, 1994-2003 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.  */

/* Written by Paul Rubin, David MacKenzie, and Richard Stallman.
   Reworked to use chdir and avoid recursion by Jim Meyering.  */

/* Implementation overview:

   In the `usual' case, RM saves no state for directories it is processing.
   When a removal fails (either due to an error or to an interactive `no'
   reply), the failure is noted (see description of `ht' in remove.c's
   remove_cwd_entries function) so that when/if the containing directory
   is reopened, RM doesn't try to remove the entry again.

   RM may delete arbitrarily deep hierarchies -- even ones in which file
   names (from root to leaf) are longer than the system-imposed maximum.
   It does this by using chdir to change to each directory in turn before
   removing the entries in that directory.

   RM detects directory cycles lazily.  See lib/cycle-check.c.

   RM is careful to avoid forming full file names whenever possible.
   A full file name is formed only when it is about to be used -- e.g.
   in a diagnostic or in an interactive-mode prompt.

   RM minimizes the number of lstat system calls it makes.  On systems
   that have valid d_type data in directory entries, RM makes only one
   lstat call per command line argument -- regardless of the depth of
   the hierarchy.  */

#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include <assert.h>

#include "system.h"
#include "error.h"
#include "remove.h"
#include "save-cwd.h"

/* The official name of this program (e.g., no `g' prefix).  */
#define PROGRAM_NAME "rm"

#define AUTHORS \
  N_ ("Paul Rubin, David MacKenzie, Richard Stallman, and Jim Meyering")

/* Name this program was run with.  */
char *program_name;

/* For long options that have no equivalent short option, use a
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
enum
{
  PRESUME_INPUT_TTY_OPTION = CHAR_MAX + 1
};

static struct option const long_opts[] =
{
  {"directory", no_argument, NULL, 'd'},
  {"force", no_argument, NULL, 'f'},
  {"interactive", no_argument, NULL, 'i'},

  /* This is solely for testing.  Do not document.  */
  /* It is relatively difficult to ensure that there is a tty on stdin.
     Since rm acts differently depending on that, without this option,
     it'd be harder to test the parts of rm that depend on that setting.  */
  {"presume-input-tty", no_argument, NULL, PRESUME_INPUT_TTY_OPTION},

  {"recursive", no_argument, NULL, 'r'},
  {"verbose", no_argument, NULL, 'v'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
         program_name);
  else
    {
      printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
      fputs (_("\
Remove (unlink) the FILE(s).\n\
\n\
  -d, --directory       unlink FILE, even if it is a non-empty directory\n\
                          (super-user only)\n\
  -f, --force           ignore nonexistent files, never prompt\n\
  -i, --interactive     prompt before any removal\n\
  -r, -R, --recursive   remove the contents of directories recursively\n\
  -v, --verbose         explain what is being done\n\
"), stdout);
      fputs (HELP_OPTION_DESCRIPTION, stdout);
      fputs (VERSION_OPTION_DESCRIPTION, stdout);
      printf (_("\
\n\
To remove a file whose name starts with a `-', for example `-foo',\n\
use one of these commands:\n\
  %s -- -foo\n\
\n\
  %s ./-foo\n\
"),
          program_name, program_name);
      fputs (_("\
\n\
Note that if you use rm to remove a file, it is usually possible to recover\n\
the contents of that file.  If you want more assurance that the contents are\n\
truly unrecoverable, consider using shred.\n\
"), stdout);
      printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
    }
  exit (status);
}

static void
rm_option_init (struct rm_options *x)
{
  x->unlink_dirs = 0;
  x->ignore_missing_files = 0;
  x->interactive = 0;
  x->recursive = 0;
  x->stdin_tty = isatty (STDIN_FILENO);
  x->verbose = 0;
}

int
main (int argc, char **argv)
{
  struct rm_options x;
  int fail = 0;
  int c;

  program_name = argv[0];
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  atexit (close_stdout);

  rm_option_init (&x);

  while ((c = getopt_long (argc, argv, "dfirvR", long_opts, NULL)) != -1)
    {
      switch (c)
    {
    case 0:     /* Long option.  */
      break;
    case 'd':
      x.unlink_dirs = 1;
      break;
    case 'f':
      x.interactive = 0;
      x.ignore_missing_files = 1;
      break;
    case 'i':
      x.interactive = 1;
      x.ignore_missing_files = 0;
      break;
    case 'r':
    case 'R':
      x.recursive = 1;
      break;
    case PRESUME_INPUT_TTY_OPTION:
      x.stdin_tty = 1;
      break;
    case 'v':
      x.verbose = 1;
      break;
    case_GETOPT_HELP_CHAR;
    case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
    default:
      usage (EXIT_FAILURE);
    }
    }

  if (optind == argc)
    {
      if (x.ignore_missing_files)
    exit (EXIT_SUCCESS);
      else
    {
      error (0, 0, _("too few arguments"));
      usage (EXIT_FAILURE);
    }
    }

  {
    size_t n_files = argc - optind;
    char const *const *file = (char const *const *) argv + optind;

    enum RM_status status = rm (n_files, file, &x);
    assert (VALID_STATUS (status));
    if (status == RM_ERROR)
      fail = 1;
  }

  exit (fail);
}

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/remove.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/remove.h used in: Step 972
#ifndef REMOVE_H
# define REMOVE_H

struct rm_options
{
  /* If nonzero, ignore nonexistent files.  */
  int ignore_missing_files;

  /* If nonzero, query the user about whether to remove each file.  */
  int interactive;

  /* If nonzero, recursively remove directories.  */
  int recursive;

  /* If nonzero, stdin is a tty.  */
  int stdin_tty;

  /* If nonzero, remove directories with unlink instead of rmdir, and don't
     require a directory to be empty before trying to unlink it.
     Only works for the super-user.  */
  int unlink_dirs;

  /* If nonzero, display the name of each file removed.  */
  int verbose;
};

enum RM_status
{
  /* These must be listed in order of increasing seriousness. */
  RM_OK = 2,
  RM_USER_DECLINED,
  RM_ERROR,
  RM_NONEMPTY_DIR
};

# define VALID_STATUS(S) \
  ((S) == RM_OK || (S) == RM_USER_DECLINED || (S) == RM_ERROR)

# define UPDATE_STATUS(S, New_value)                \
  do                                \
    {                               \
      if ((New_value) == RM_ERROR               \
      || ((New_value) == RM_USER_DECLINED && (S) == RM_OK)) \
    (S) = (New_value);                  \
    }                               \
  while (0)

enum RM_status rm (size_t n_files, char const *const *file,
           struct rm_options const *x);

#endif

https://git.savannah.gnu.org/cgit/coreutils.git/tree/lib/save-cwd.h?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/lib/save-cwd.h used in: Step 972
#ifndef SAVE_CWD_H
# define SAVE_CWD_H 1

struct saved_cwd
  {
    int desc;
    char *name;
  };

int save_cwd (struct saved_cwd *cwd);
int restore_cwd (const struct saved_cwd *cwd);
void free_cwd (struct saved_cwd *cwd);

#endif /* SAVE_CWD_H */

https://git.savannah.gnu.org/cgit/coreutils.git/tree/src/sha1sum.c?h=COREUTILS-5_0

File: ../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/src/sha1sum.c used in: Step 974
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include "system.h"
#include "checksum.h"

int algorithm = ALG_SHA1;

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/coreutils-5.0/coreutils-5.0.checksums

File: ../git/live-bootstrap/sysa/coreutils-5.0/coreutils-5.0.checksums used in: Step 980
dafe043ca795c8bea27a6bc5cbdfbc4f59671809f6f9774b9e94831ea6405a80  /usr/bin/install
78cf8e0ee96e7428496d0fdccc60e8465f7aaf3728a463123fefb8f46bc5d073  /usr/bin/basename
70146b4ce3092cd7ccaf933d2eb6e93c1b12358240838f66a1a7743a8100e870  /usr/bin/cat
c0b5b1eb102784c8202ec82990fffc4c57ba0da5724ac44544994a1d5a50615f  /usr/bin/chmod
113910e58f8bb9506f5fd3c5394e7792b47bf9c7e7479941882c1368481b2e40  /usr/bin/cksum
2f666cac19e38d7653975e68485ade9f6a4bb9414fa9364716414b294f3720d1  /usr/bin/cp
27f6fe3aa713d62cd9367ccafb2c52029f1f87e2d07cbadf7b71023240149fda  /usr/bin/csplit
11068650333e2c0af8e9c8abb79c3d5c8fbe261b5a72b454ca632ad3ec5b965b  /usr/bin/cut
7f5afda1460b2148f8b362bc37e252caf6bf02df10aab02a21f5581295921b97  /usr/bin/dirname
ecbbb0202e9b8274c93e41d831eddc809f7714c6e8004136e2fc7f1a0a30e799  /usr/bin/echo
a2962caac7a407f50a94c1d6496de9ca65c8ff66a3aa3e814a69f6aa3253dea2  /usr/bin/expand
61ffbe47da01a828ce0a0e2b871deb065bcaf874b831c019131d62bb5156e194  /usr/bin/expr
28c8920fb8ce6e5ed74f8be8a854688b8aa5155baf70812259c471fd84d2d716  /usr/bin/factor
1a851ce640196e1562d392f2a71cf294937513a43fd9b52e146c803722bd6c9e  /usr/bin/false
fda23f5d23a65799794b49b3befa8d515e1e0f221c4b67e9dead07ca4b492c4c  /usr/bin/fmt
2241e52196cc6f204528c32d844361cb917031cdfe5efda90ee6f2f3b2fb6a1a  /usr/bin/fold
813bc16b973c2d13b96980d22d8ad49974aac356226781868af68973ba8229a2  /usr/bin/head
7948309bd45223be43e385367a56cf5fcd30ac7c94685423e651fab2d1c0a80d  /usr/bin/hostname
fd55b2ed55a27de3c57dccf64c1457613a109c231dc0f8a40b650fb2f6114ab2  /usr/bin/id
9cb2877544e4dfc2521d54c3b18811dad170a6b31aeaa7556b73edcf57cb6668  /usr/bin/join
a532dfb88e4226ba1600ab8eb3c7a2801b6b7bc2d50200ba70470723322babfe  /usr/bin/kill
495a20a8342ab6d61806342ef61d8ad135a7c8f2bfa8a2edbd6cb5e839d577a7  /usr/bin/link
38b90400c1be7792872bbb0781627b2dba35a70abf41899e9ee19d59ef351fe9  /usr/bin/ln
323097645c46aab33711f562ff7ed1fda4609970d7e55e6ab2fd31dd1ed0bfb0  /usr/bin/logname
354547f7467df48dd0bbaf9c7f36c8168dc8a38ccfe97409847dbb50c05cba90  /usr/bin/mkfifo
2326009b57ace4bc7da598d322fcf7c2cdcd702723c6cdb81a50a1ceb0631933  /usr/bin/mkdir
f964752c462185f7ea54246b4e53bca7ee84cfc4b590bef04baa8700312e2224  /usr/bin/mknod
a9311865c1e95766e3be810617fce3892d5ea693fd3cdb26d680f75c579670f2  /usr/bin/nl
c68b80b2dff6423f5c92ec1cf02f30c61c9560f2a3499acf617aeee7c407d7b5  /usr/bin/od
775ec8b0a5c37a7360535c1c040b5c98e6177e69f978f520ee928efab39ed519  /usr/bin/paste
b45c0d13af7f9b1cad618fdcd48ec7db80d9e59acf37ced076c4a65412b3a5a7  /usr/bin/pathchk
267b1a8a23bf764f833cef10da9f0c77583234baed24bd67362a008015231ee3  /usr/bin/pr
6375dd4cb3d1326c895844fa7128d3d7c88df9da7b85830e23bc61d99e0bc186  /usr/bin/printf
9665a522e43fb6d2a7d66c217873cc9eb02936a51ff97b426214316b76a92792  /usr/bin/ptx
7af4c3a707f382730836223ce8651850e78e9aac4079cf2e0e7996cac8449011  /usr/bin/pwd
6c2fc79aef8bcb5ce79961481b6d30fb5bf3648d71e60ab1df9a75621f0471d0  /usr/bin/readlink
3249f3b8a09e6862ce1a0c09b4e805fa03f6cfef70ee81c70704f0b481b20f45  /usr/bin/rmdir
014f6c56864a02903fcddc8f681459ab455524f12ace562fb4ff50b67cd07dab  /usr/bin/seq
de46fe9655bad40463d8af57ec6fe0dd4d56fb4056a7fcef9e57eebf9c497959  /usr/bin/sleep
a6bbb377c517f2c00652b16f8eb14e8ba54fa318faf6c90fa82ebcc84fb14986  /usr/bin/sort
7d62c06a21c4eb9989f01bacf406bffe1b7e9d69596eac4fa6373e9ce7f7cb3b  /usr/bin/split
00a0f9b9dc137193e6ddb0b7dba679ee98865dfc69589bcb619de0044fdb5550  /usr/bin/sum
38b82cf9f2a4818061cabc32e78df8cc552c1384cecf669b8a221f44fb1cf06b  /usr/bin/tail
e2683f200a8c14eef6d92d6ee3b4fdbc48b4f1cf5ea58ccab57259f6b8a315d5  /usr/bin/tee
ff9492d8e088acf1a689e907c9550ba78029a778bd2818674d4989cddbc28853  /usr/bin/tr
159651d7a72db3e4752ee184288c55a1fee0484443aa67e4f3a9984ad7320e78  /usr/bin/tsort
9f6f55239fa384564f78347b70c1287310283121446d63071045f6fd3eac90b1  /usr/bin/unexpand
58b692ef7cc3f644eca0b21cd792ac6037202788e9bcee1204ba155a750e27ca  /usr/bin/uniq
1c6c0306829c5c0695d9e5336682aac3494eb78b86b0806fb8b94b4edbe9e670  /usr/bin/unlink
c601ba79b716d782bd8f01e2e3804bd2a66fdc6519c7c27dc709def51558ed53  /usr/bin/wc
762c3ac63eeafd49de5f324b16b1895167110e2ddee553c6f5198c7d71880caf  /usr/bin/whoami
e0a3acd8a5ea91c112bc63281f54a1e56d11991f0ec3b72d5a5becaf11bf6c9a  /usr/bin/tac
a728386f022a00b79a683ab39f9f85a2561e110ff6e6786d304320d8ed9e4e97  /usr/bin/test
3948a25742e25552b30b018269038723d7f6eb80a4c18c2f85bb84fe13729930  /usr/bin/touch
f4f039c380c8f979bd40b02fc458840db3f1086d7e7f9a209e860340a548a42b  /usr/bin/true
43e5a7794084a505fdfd367d6d66e5f0b8ba3c3d843cdc6036416d49c68b79b5  /usr/bin/yes
3e187de38d3c75ef52d74c4bba8dae228dad81e81e9209e3994a6bffe8ee09c9  /usr/bin/ls
4917fb8bb8a4fb9e24b6cc27de9dd1944acef84b8f5f9b150c0aa9f87b37f6ca  /usr/bin/md5sum
8c8ed60125c5b34b4d3d156baf2b4c682dee313bc795bb1b4b15427827d62ca8  /usr/bin/mv
48c1f9f1847bff208be5bf04ea5897a0c1454f5b8e748a451e755631519c85f1  /usr/bin/rm
5d717b9176ebb05216c869e7ee9711e0fdc6ac2f8dec15d9e94a2ed35832c9c9  /usr/bin/sha1sum

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/sources

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/sources used in: Step 982
http://downloads.sourceforge.net/project/heirloom/heirloom-devtools/070527/heirloom-devtools-070527.tar.bz2 9f233d8b78e4351fe9dd2d50d83958a0e5af36f54e9818521458a08e058691ba

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/patches/yacc_remove_wchar.patch

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/patches/yacc_remove_wchar.patch used in: Step 989
SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>
SPDX-FileCopyrightText: 2023 Emily Trau <emily@downunderctf.com>

SPDX-License-Identifier: CDDL-1.0

Remove all kinds of wchar support. Mes Libc does not support wchar in any form,
so we need to remove it.

heirloom-devtools is all kinds of broken in this way. C standard dictates that
wchar_t may be defined as char, however heirloom-devtools does not respect this,
which makes this non-trivial.

diff -U3 -r yacc/dextern yacc/dextern
--- yacc/dextern    2005-11-10 20:31:45.000000000 +0000
+++ yacc/dextern    2021-01-25 18:21:45.286602527 +0000
@@ -39,7 +39,6 @@
 #include <ctype.h>
 #include <string.h>
 #include <stdlib.h>
-#include <wctype.h>
 
    /*  MANIFEST CONSTANT DEFINITIONS */
 #define    WORD32
@@ -166,17 +165,17 @@
 } ITEM;
 
 typedef struct toksymb {
-   wchar_t *name;
+   char *name;
    int value;
 } TOKSYMB;
 
 typedef struct mbclit {
-   wchar_t character;
+   char character;
    int tvalue; /* token issued for the character */
 } MBCLIT;
 
 typedef struct ntsymb {
-   wchar_t *name;
+   char *name;
    int tvalue;
 } NTSYMB;
 
@@ -212,7 +211,7 @@
 extern int nprod;  /* number of productions */
 extern int **prdptr;   /* pointers to descriptions of productions */
 extern int *levprd;    /* contains production levels to break conflicts */
-extern wchar_t *had_act; /* set if reduction has associated action code */
+extern char *had_act; /* set if reduction has associated action code */
 
    /* state information */
 
@@ -273,24 +272,15 @@
 extern void go2out(void);
 extern void hideprod(void);
 extern void callopt(void);
-extern void warray(wchar_t *, int *, int);
-extern wchar_t *symnam(int);
-extern wchar_t *writem(int *);
+extern void warray(char *, int *, int);
+extern char *symnam(int);
+extern char *writem(int *);
 extern void exp_mem(int);
 extern void exp_act(int **);
 extern int apack(int *, int);
 extern int state(int);
-extern void fprintf3(FILE *, const char *, const wchar_t *, const char *, ...);
-extern void error3(const char *, const wchar_t *, const char *, ...);
-
-   /* multibyte i/o */
-
-#undef getwc
-#define    getwc(f)    yacc_getwc(f)
-extern wint_t yacc_getwc(FILE *);
-#undef putwc
-#define    putwc(c, f) yacc_putwc(c, f)
-extern wint_t yacc_putwc(wchar_t, FILE *);
+extern void fprintf3(FILE *, const char *, const char *, const char *, ...);
+extern void error3(const char *, const char *, const char *, ...);
 
    /* yaccpar location */
 
diff -U3 -r yacc/y1.c yacc/y1.c
--- yacc/y1.c   2005-11-26 17:37:56.000000000 +0000
+++ yacc/y1.c   2021-01-25 18:02:31.251542365 +0000
@@ -41,12 +41,11 @@
 #include <unistd.h>
 #include <locale.h>
 #include <stdarg.h>    /* For error() */
-#include <wchar.h>
 
 static void mktbls(void);
 static void others(void);
 static void summary(void);
-static wchar_t *chcopy(wchar_t *, wchar_t *);
+static char *chcopy(char *, char *);
 static int setunion(int *, int *);
 static void prlook(LOOKSETS *);
 static void cpres(void);
@@ -229,13 +228,13 @@
    if (finput == NULL)
        error("cannot find parser %s", parser);
 
-   warray(L"yyr1", levprd, nprod);
+   warray("yyr1", levprd, nprod);
 
    aryfil(temp1, nprod, 0);
                    /* had_act[i] is either 1 or 0 */
    PLOOP(1, i)
        temp1[i] = ((prdptr[i+1] - prdptr[i]-2) << 1) | had_act[i];
-   warray(L"yyr2", temp1, nprod);
+   warray("yyr2", temp1, nprod);
 
    aryfil(temp1, nstate, -10000000);
    TLOOP(i)
@@ -244,14 +243,14 @@
    NTLOOP(i)
        for (j = ntstates[i]; j != 0; j = mstates[j])
            temp1[j] = -i;
-   warray(L"yychk", temp1, nstate);
+   warray("yychk", temp1, nstate);
 
-   warray(L"yydef", defact, nstate);
+   warray("yydef", defact, nstate);
 
    if ((fdebug = fopen(DEBUGNAME, "r")) == NULL)
        error("cannot open yacc.debug");
-   while ((c = getwc(fdebug)) != EOF)
-       putwc(c, ftable);
+   while ((c = getc(fdebug)) != EOF)
+       putc(c, ftable);
    fclose(fdebug);
    ZAPFILE(DEBUGNAME);
 
@@ -259,19 +258,19 @@
        fprintf(ftable, "# line\t1 \"%s\"\n", parser);
    tmpline = 1;
    /* copy parser text */
-   while ((c = getwc(finput)) != EOF) {
+   while ((c = getc(finput)) != EOF) {
        if (c == '\n')
            tmpline++;
        if (c == L'$') {
-           if ((c = getwc(finput)) != L'A')
-               putwc(L'$', ftable);
+           if ((c = getc(finput)) != L'A')
+               putc(L'$', ftable);
            else { /* copy actions */
                tmpline++;
                faction = fopen(ACTNAME, "r");
                if (faction == NULL)
                    error("cannot open action tempfile");
-               while ((c = getwc(faction)) != EOF)
-                   putwc(c, ftable);
+               while ((c = getc(faction)) != EOF)
+                   putc(c, ftable);
                fclose(faction);
                if (gen_lines)
                    fprintf(ftable,
@@ -279,18 +278,18 @@
                        tmpline,
                        parser);
                ZAPFILE(ACTNAME);
-               c = getwc(finput);
+               c = getc(finput);
            }
        }
-       putwc(c, ftable);
+       putc(c, ftable);
    }
    fclose(ftable);
 }
 
 
 /* copies string q into p, returning next free char ptr */
-static wchar_t *
-chcopy(wchar_t *p, wchar_t *q)
+static char *
+chcopy(char *p, char *q)
 {
    while (*p = *q++)
        ++p;
@@ -299,16 +298,16 @@
 
 #define    ISIZE 400
 /* creates output string for item pointed to by pp */
-wchar_t *
+char *
 writem(int *pp)
 {
    int i, *p;
    static int isize = ISIZE;
-   static wchar_t *sarr = NULL;
-   wchar_t *q;
+   static char *sarr = NULL;
+   char *q;
 
    if (sarr == NULL) {
-       sarr = malloc(sizeof (wchar_t) * isize);
+       sarr = malloc(sizeof (char) * isize);
        if (sarr == NULL)
            error("could not allocate output string array");
        for (i = 0; i < isize; ++i)
@@ -317,7 +316,7 @@
    for (p = pp; *p > 0; ++p) /* EMPTY */;
    p = prdptr[-*p];
    q = chcopy(sarr, nontrst[*p-NTBASE].name);
-   q = chcopy(q, L" : ");
+   q = chcopy(q, " : ");
 
    for (;;) {
        *q++ = ++p == pp ? L'_' : L' ';
@@ -326,7 +325,7 @@
            break;
        q = chcopy(q, symnam(i));
        while (q > &sarr[isize-30]) {
-           static wchar_t *sarrbase;
+           static char *sarrbase;
 
            sarrbase = sarr;
            isize += ISIZE;
@@ -339,17 +338,17 @@
 
    /* an item calling for a reduction */
    if ((i = *pp) < 0) {
-       q = chcopy(q, L"    (");
-       swprintf(q, q + isize - sarr, L"%d)", -i);
+       q = chcopy(q, "    (");
+       snprintf(q, q + isize - sarr, "%d)", -i);
    }
    return (sarr);
 }
 
 /* return a pointer to the name of symbol i */
-wchar_t *
+char *
 symnam(int i)
 {
-   wchar_t *cp;
+   char *cp;
 
    cp = (i >= NTBASE) ? nontrst[i-NTBASE].name : tokset[i].name;
    if (*cp == L' ')
diff -U3 -r yacc/y2.c yacc/y2.c
--- yacc/y2.c   2005-11-26 17:39:44.000000000 +0000
+++ yacc/y2.c   2021-01-25 19:02:52.472120036 +0000
@@ -35,9 +35,9 @@
  * Sccsid @(#)y2.c 1.11 (gritter) 11/26/05
  */
 
+#include <getopt.h>
 #include "dextern"
 #include "sgs.h"
-#include <wchar.h>
 #include <unistd.h>
 #define    IDENTIFIER 257
 
@@ -66,17 +66,17 @@
 char *infile;              /* input file name      */
 static int numbval;            /* value of an input number     */
 static int toksize = NAMESIZE;
-static wchar_t *tokname;   /* input token name     */
+static char *tokname;  /* input token name     */
 char *parser = NULL;       /* location of common parser    */
 
 static void finact(void);
-static wchar_t *cstash(wchar_t *);
+static char *cstash(char *);
 static void defout(void);
 static void cpyunion(void);
 static void cpycode(void);
 static void cpyact(int);
-static void lhsfill(wchar_t *);
-static void rhsfill(wchar_t *);
+static void lhsfill(char *);
+static void rhsfill(char *);
 static void lrprnt(void);
 static void beg_debug(void);
 static void end_toks(void);
@@ -85,9 +85,9 @@
 static void exp_prod(void);
 static void exp_ntok(void);
 static void exp_nonterm(void);
-static int defin(int, wchar_t *);
+static int defin(int, char *);
 static int gettok(void);
-static int chfind(int, wchar_t *);
+static int chfind(int, char *);
 static int skipcom(void);
 static int findchtok(int);
 static void put_prefix_define(char *);
@@ -101,11 +101,11 @@
  * points to initial block - more space
  * is allocated as needed.
  */
-static wchar_t cnamesblk0[CNAMSZ];
-static wchar_t *cnames = cnamesblk0;
+static char cnamesblk0[CNAMSZ];
+static char *cnames = cnamesblk0;
 
 /* place where next name is to be put in */
-static wchar_t *cnamp = cnamesblk0;
+static char *cnamp = cnamesblk0;
 
 /* number of defined symbols output */
 static int ndefout = 3;
@@ -113,7 +113,7 @@
    /* storage of types */
 static int defunion = 0;   /* union of types defined? */
 static int ntypes = 0;     /* number of types defined */
-static wchar_t *typeset[NTYPES]; /* pointers to type tags */
+static char *typeset[NTYPES]; /* pointers to type tags */
 
    /* symbol tables for tokens and nonterminals */
 
@@ -143,8 +143,8 @@
 
    /* output string */
 
-static wchar_t *lhstext;
-static wchar_t *rhstext;
+static char *lhstext;
+static char *rhstext;
 
    /* storage for grammar rules */
 
@@ -158,7 +158,7 @@
 
 int **prdptr;
 int *levprd;
-wchar_t *had_act;
+char *had_act;
 
 /* flag for generating the # line's default is yes */
 int gen_lines = 1;
@@ -181,7 +181,7 @@
    int c;
    int *p;
    char *cp;
-   wchar_t actname[8];
+   char actname[8];
    unsigned int options = 0;
    char *file_prefix = DEFAULT_PREFIX;
    char *sym_prefix = "";
@@ -192,16 +192,16 @@
    fdefine = NULL;
    i = 1;
 
-   tokname = malloc(sizeof (wchar_t) * toksize);
+   tokname = malloc(sizeof (char) * toksize);
    tokset = malloc(sizeof (TOKSYMB) * ntoksz);
    toklev = malloc(sizeof (int) * ntoksz);
    nontrst = malloc(sizeof (NTSYMB) * nnontersz);
    mem0 = malloc(sizeof (int) * new_memsize);
    prdptr = malloc(sizeof (int *) * (nprodsz+2));
    levprd = malloc(sizeof (int) * (nprodsz+2));
-   had_act = calloc(nprodsz + 2, sizeof (wchar_t));
-   lhstext = malloc(sizeof (wchar_t) * LHS_TEXT_LEN);
-   rhstext = malloc(sizeof (wchar_t) * RHS_TEXT_LEN);
+   had_act = calloc(nprodsz + 2, sizeof (char));
+   lhstext = malloc(sizeof (char) * LHS_TEXT_LEN);
+   rhstext = malloc(sizeof (char) * RHS_TEXT_LEN);
    aryfil(toklev, ntoksz, 0);
    aryfil(levprd, nprodsz, 0);
    for (ii = 0; ii < ntoksz; ++ii)
@@ -307,10 +307,10 @@
 
    lineno = 1;
    cnamp = cnames;
-   defin(0, L"$end");
+   defin(0, "$end");
    extval = 0400;
-   defin(0, L"error");
-   defin(1, L"$accept");
+   defin(0, "error");
+   defin(1, "$accept");
    mem = mem0;
    lev = 0;
    ty = 0;
@@ -588,7 +588,7 @@
        /* process a rule */
 
        if (t == L'|') {
-           rhsfill((wchar_t *)0); /* restart fill of rhs */
+           rhsfill((char *)0); /* restart fill of rhs */
            *mem = *prdptr[nprod-1];
            if (++mem >= &tracemem[new_memsize])
                exp_mem(1);
@@ -638,8 +638,8 @@
                /* action within rule... */
 
                lrprnt();       /* dump lhs, rhs */
-               swprintf(actname, sizeof actname,
-                       L"$$%d", nprod);
+               snprintf(actname, sizeof actname,
+                       "$$%d", nprod);
                /*
                 * make it nonterminal
                 */
@@ -722,8 +722,8 @@
        if (gen_lines)
            fprintf(ftable, "\n# line %d \"%s\"\n",
                lineno, infile);
-       while ((c = getwc(finput)) != EOF)
-           putwc(c, ftable);
+       while ((c = getc(finput)) != EOF)
+           putc(c, ftable);
        }
    fclose(finput);
 }
@@ -736,15 +736,15 @@
    fprintf(ftable, "# define YYERRCODE %d\n", tokset[2].value);
 }
 
-static wchar_t *
+static char *
 cstash(s)
-register wchar_t *s;
+register char *s;
 {
-   wchar_t *temp;
+   char *temp;
    static int used = 0;
    static int used_save = 0;
    static int exp_cname = CNAMSZ;
-   int len = wcslen(s);
+   int len = strlen(s);
 
    /*
     * 2/29/88 -
@@ -755,7 +755,7 @@
        exp_cname += CNAMSZ;
        if (!used)
            free(cnames);
-       if ((cnames = malloc(sizeof (wchar_t)*exp_cname)) == NULL)
+       if ((cnames = malloc(sizeof (char)*exp_cname)) == NULL)
            error("cannot expand string dump");
        cnamp = cnames;
        used = 0;
@@ -770,7 +770,7 @@
 }
 
 static int
-defin(int t, register wchar_t *s)
+defin(int t, register char *s)
 {
    /* define s to be a terminal if t=0 or a nonterminal if t=1 */
 
@@ -820,7 +820,7 @@
        } else if (s[2] <= L'7' && s[2] >= L'0') { /* \nnn sequence */
            int i = 3;
            val = s[2] - L'0';
-           while (iswdigit(s[i]) && i <= 4) {
+           while (isdigit(s[i]) && i <= 4) {
                if (s[i] >= L'0' && s[i] <= L'7')
                    val = val * 8 + s[i] - L'0';
                else
@@ -831,17 +831,17 @@
                error("illegal \\nnn construction");
            if (val > 255)
                error(
-"\\nnn exceed \\377; use \\xnnnnnnnn for wchar_t value of multibyte char");
+"\\nnn exceed \\377; use \\xnnnnnnnn for char value of multibyte char");
            if (val == 0 && i >= 4)
                error("'\\000' is illegal");
        } else if (s[2] == L'x') { /* hexadecimal \xnnn sequence */
            int i = 3;
            val = 0;
            warning(1, "\\x is ANSI C hex escape");
-           if (iswxdigit(s[i]))
-               while (iswxdigit(s[i])) {
+           if (isxdigit(s[i]))
+               while (isxdigit(s[i])) {
                    int tmpval;
-                   if (iswdigit(s[i]))
+                   if (isdigit(s[i]))
                        tmpval = s[i] - L'0';
                    else if (s[i] >= L'a')
                        tmpval = s[i] - L'a' + 10;
@@ -876,7 +876,7 @@
    /* write out the defines (at the end of the declaration section) */
 
    register int i, c;
-   register wchar_t *cp;
+   register char *cp;
 
    for (i = ndefout; i <= ntokens; ++i) {
 
@@ -889,8 +889,8 @@
        }
 
        for (; (c = *cp) != 0; ++cp) {
-           if (iswlower(c) || iswupper(c) ||
-               iswdigit(c) || c == L'_') /* EMPTY */;
+           if (islower(c) || isupper(c) ||
+               isdigit(c) || c == L'_') /* EMPTY */;
            else
                goto nodef;
            }
@@ -919,14 +919,14 @@
    reserve = 0;
    lineno += peekline;
    peekline = 0;
-   c = getwc(finput);
+   c = getc(finput);
    /*
     * while (c == ' ' || c == '\n' || c == '\t' || c == '\f') {
     */
-   while (iswspace(c)) {
+   while (isspace(c)) {
        if (c == L'\n')
            ++lineno;
-       c = getwc(finput);
+       c = getc(finput);
    }
    if (c == L'/') { /* skip comment */
        lineno += skipcom();
@@ -938,11 +938,11 @@
    case EOF:
        return (ENDFILE);
    case L'{':
-       ungetwc(c, finput);
+       ungetc(c, finput);
        return (L'=');  /* action ... */
    case L'<':  /* get, and look up, a type name (union member name) */
        i = 0;
-       while ((c = getwc(finput)) != L'>' &&
+       while ((c = getc(finput)) != L'>' &&
                c != EOF && c != L'\n') {
            tokname[i] = c;
            if (++i >= toksize)
@@ -954,7 +954,7 @@
        if (i == 0)
            error("missing type name in < ... > clause");
        for (i = 1; i <= ntypes; ++i) {
-           if (!wcscmp(typeset[i], tokname)) {
+           if (!strcmp(typeset[i], tokname)) {
                numbval = i;
                return (TYPENAME);
                }
@@ -968,11 +968,11 @@
        tokname[0] = L' ';
        i = 1;
        for (;;) {
-           c = getwc(finput);
+           c = getc(finput);
            if (c == L'\n' || c == EOF)
                error("illegal or missing ' or \"");
            if (c == L'\\') {
-               c = getwc(finput);
+               c = getc(finput);
                tokname[i] = L'\\';
                if (++i >= toksize)
                    exp_tokname();
@@ -986,7 +986,7 @@
    case L'%':
    case L'\\':
 
-       switch (c = getwc(finput)) {
+       switch (c = getc(finput)) {
 
        case L'0':  return (TERM);
        case L'<':  return (LEFT);
@@ -1001,81 +1001,81 @@
 
    default:
 
-       if (iswdigit(c)) { /* number */
+       if (isdigit(c)) { /* number */
            numbval = c - L'0';
            base = (c == L'0') ? 8 : 10;
-           for (c = getwc(finput);
-                   iswdigit(c);
-                   c = getwc(finput)) {
+           for (c = getc(finput);
+                   isdigit(c);
+                   c = getc(finput)) {
                numbval = numbval*base + c - L'0';
                }
-           ungetwc(c, finput);
+           ungetc(c, finput);
            return (NUMBER);
-       } else if (iswlower(c) || iswupper(c) ||
+       } else if (islower(c) || isupper(c) ||
                c == L'_' || c == L'.' ||
                c == L'$') {
            i = 0;
-           while (iswlower(c) || iswupper(c) ||
-                   iswdigit(c) || c == L'_' ||
+           while (islower(c) || isupper(c) ||
+                   isdigit(c) || c == L'_' ||
                    c == L'.' || c == L'$') {
                tokname[i] = c;
-               if (reserve && iswupper(c))
-                   tokname[i] = towlower(c);
+               if (reserve && isupper(c))
+                   tokname[i] = tolower(c);
                if (++i >= toksize)
                    exp_tokname();
-               c = getwc(finput);
+               c = getc(finput);
                }
            }
        else
            return (c);
 
-       ungetwc(c, finput);
+       ungetc(c, finput);
        }
 
    tokname[i] = 0;
 
    if (reserve) { /* find a reserved word */
-       if (!wcscmp(tokname, L"term"))
+       if (!strcmp(tokname, "term"))
            return (TERM);
-       if (!wcscmp(tokname, L"token"))
+       if (!strcmp(tokname, "token"))
            return (TERM);
-       if (!wcscmp(tokname, L"left"))
+       if (!strcmp(tokname, "left"))
            return (LEFT);
-       if (!wcscmp(tokname, L"nonassoc"))
+       if (!strcmp(tokname, "nonassoc"))
            return (BINARY);
-       if (!wcscmp(tokname, L"binary"))
+       if (!strcmp(tokname, "binary"))
            return (BINARY);
-       if (!wcscmp(tokname, L"right"))
+       if (!strcmp(tokname, "right"))
            return (RIGHT);
-       if (!wcscmp(tokname, L"prec"))
+       if (!strcmp(tokname, "prec"))
            return (PREC);
-       if (!wcscmp(tokname, L"start"))
+       if (!strcmp(tokname, "start"))
            return (START);
-       if (!wcscmp(tokname, L"type"))
+       if (!strcmp(tokname, "type"))
            return (TYPEDEF);
-       if (!wcscmp(tokname, L"union"))
+       if (!strcmp(tokname, "union"))
            return (UNION);
        error("invalid escape, or illegal reserved word: %ls", tokname);
        }
 
    /* look ahead to distinguish IDENTIFIER from C_IDENTIFIER */
 
-   c = getwc(finput);
+   c = getc(finput);
    /*
     * while (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '/')
     * {
     */
-   while (iswspace(c) || c == L'/') {
+   while (isspace(c) || c == L'/') {
        if (c == L'\n') {
            ++peekline;
        } else if (c == L'/') { /* look for comments */
            peekline += skipcom();
            }
-       c = getwc(finput);
+       c = getc(finput);
        }
    if (c == L':')
        return (C_IDENTIFIER);
-   ungetwc(c, finput);
+   ungetc(c, finput);
    return (IDENTIFIER);
 }
 
@@ -1096,19 +1096,19 @@
 }
 
 static int
-chfind(int t, register wchar_t *s)
+chfind(int t, register char *s)
 {
    int i;
 
    if (s[0] == ' ')
        t = 0;
    TLOOP(i) {
-       if (!wcscmp(s, tokset[i].name)) {
+       if (!strcmp(s, tokset[i].name)) {
            return (i);
        }
    }
    NTLOOP(i) {
-       if (!wcscmp(s, nontrst[i].name)) {
+       if (!strcmp(s, nontrst[i].name)) {
            return (i + NTBASE);
        }
    }
@@ -1137,11 +1137,11 @@
 
    level = 0;
    for (;;) {
-       if ((c = getwc(finput)) == EOF)
+       if ((c = getc(finput)) == EOF)
            error("EOF encountered while processing %%union");
-       putwc(c, ftable);
+       putc(c, ftable);
        if (fdefine)
-           putwc(c, fdefine);
+           putc(c, fdefine);
 
        switch (c) {
 
@@ -1172,29 +1172,29 @@
    /* copies code between \{ and \} */
 
    int c;
-   c = getwc(finput);
+   c = getc(finput);
    if (c == L'\n') {
-       c = getwc(finput);
+       c = getc(finput);
        lineno++;
        }
    if (gen_lines)
        fprintf(ftable, "\n# line %d \"%s\"\n", lineno, infile);
    while (c != EOF) {
        if (c == L'\\') {
-           if ((c = getwc(finput)) == L'}')
+           if ((c = getc(finput)) == L'}')
                return;
            else
-               putwc(L'\\', ftable);
+               putc(L'\\', ftable);
        } else if (c == L'%') {
-           if ((c = getwc(finput)) == L'}')
+           if ((c = getc(finput)) == L'}')
                return;
            else
-               putwc(L'%', ftable);
+               putc(L'%', ftable);
        }
-       putwc(c, ftable);
+       putc(c, ftable);
        if (c == L'\n')
            ++lineno;
-       c = getwc(finput);
+       c = getc(finput);
        }
    error("eof before %%}");
 }
@@ -1207,17 +1207,17 @@
 
    /* skipcom is called after reading a / */
 
-   if (getwc(finput) != L'*')
+   if (getc(finput) != L'*')
        error("illegal comment");
-   c = getwc(finput);
+   c = getc(finput);
    while (c != EOF) {
        while (c == L'*') {
-           if ((c = getwc(finput)) == L'/')
+           if ((c = getc(finput)) == L'/')
                return (i);
            }
        if (c == L'\n')
            ++i;
-       c = getwc(finput);
+       c = getc(finput);
        }
    error("EOF inside comment");
    /* NOTREACHED */
@@ -1229,7 +1229,7 @@
 {
    /* copy C action to the next ; or closing } */
    int brac, c, match, i, t, j, s, tok, argument, m;
-   wchar_t id_name[NAMESIZE+1];
+   char id_name[NAMESIZE+1];
    int id_idx = 0;
 
    if (gen_lines) {
@@ -1239,12 +1239,12 @@
    brac = 0;
    id_name[0] = 0;
 loop:
-   c = getwc(finput);
+   c = getc(finput);
 swt:
    switch (c) {
    case L';':
        if (brac == 0) {
-           putwc(c, faction);
+           putc(c, faction);
            return;
        }
        goto lcopy;
@@ -1255,13 +1255,13 @@
        s = 1;
        tok = -1;
        argument = 1;
-       while ((c = getwc(finput)) == L' ' || c == L'\t') /* EMPTY */;
+       while ((c = getc(finput)) == L' ' || c == L'\t') /* EMPTY */;
        if (c == L'<') { /* type description */
-           ungetwc(c, finput);
+           ungetc(c, finput);
            if (gettok() != TYPENAME)
                error("bad syntax on $<ident> clause");
            tok = numbval;
-           c = getwc(finput);
+           c = getc(finput);
        }
        if (c == L'$') {
            fprintf(faction, "yyval");
@@ -1272,10 +1272,10 @@
            }
            goto loop;
        }
-       if (iswalpha(c)) {
+       if (isalpha(c)) {
            int same = 0;
            int id_sw = 0;
-           ungetwc(c, finput);
+           ungetc(c, finput);
            if (gettok() != IDENTIFIER)
                error("bad action format");
            /*
@@ -1290,16 +1290,16 @@
                id_sw = 1;
            else
                id_sw = 0;
-           while ((c = getwc(finput)) == L' ' ||
+           while ((c = getc(finput)) == L' ' ||
                c == L'\t') /* EMPTY */;
            if (c == L'#') {
-               while ((c = getwc(finput)) == L' ' ||
+               while ((c = getc(finput)) == L' ' ||
                    c == L'\t') /* EMPTY */;
-               if (iswdigit(c)) {
+               if (isdigit(c)) {
                    m = 0;
-                   while (iswdigit(c)) {
+                   while (isdigit(c)) {
                        m = m*10+c-L'0';
-                       c = getwc(finput);
+                       c = getc(finput);
                    }
                    argument = m;
                } else
@@ -1339,13 +1339,13 @@
        }
        if (c == '-') {
            s = -s;
-           c = getwc(finput);
+           c = getc(finput);
        }
-       if (iswdigit(c)) {
+       if (isdigit(c)) {
            j = 0;
-           while (iswdigit(c)) {
+           while (isdigit(c)) {
                j = j*10 + c - L'0';
-               c = getwc(finput);
+               c = getc(finput);
            }
            j = j*s - offset;
            if (j > 0) {
@@ -1363,51 +1363,51 @@
            }
            goto swt;
        }
-       putwc(L'$', faction);
+       putc(L'$', faction);
        if (s < 0)
-           putwc(L'-', faction);
+           putc(L'-', faction);
        goto swt;
    case L'}':
        if (--brac)
            goto lcopy;
-       putwc(c, faction);
+       putc(c, faction);
        return;
    case L'/':  /* look for comments */
-       putwc(c, faction);
-       c = getwc(finput);
+       putc(c, faction);
+       c = getc(finput);
        if (c != L'*')
            goto swt;
        /* it really is a comment */
-       putwc(c, faction);
-       c = getwc(finput);
+       putc(c, faction);
+       c = getc(finput);
        while (c != EOF) {
            while (c == L'*') {
-               putwc(c, faction);
-               if ((c = getwc(finput)) == L'/')
+               putc(c, faction);
+               if ((c = getc(finput)) == L'/')
                    goto lcopy;
            }
-           putwc(c, faction);
+           putc(c, faction);
            if (c == L'\n')
                ++lineno;
-           c = getwc(finput);
+           c = getc(finput);
        }
        error("EOF inside comment");
        /* FALLTHRU */
    case L'\'': /* character constant */
    case L'"':  /* character string */
        match = c;
-       putwc(c, faction);
-       while ((c = getwc(finput)) != EOF) {
+       putc(c, faction);
+       while ((c = getc(finput)) != EOF) {
            if (c == L'\\') {
-               putwc(c, faction);
-               c = getwc(finput);
+               putc(c, faction);
+               c = getc(finput);
                if (c == L'\n')
                    ++lineno;
            } else if (c == match)
                goto lcopy;
            else if (c == L'\n')
                error("newline in string or char. const.");
-           putwc(c, faction);
+           putc(c, faction);
        }
        error("EOF in string or character constant");
        /* FALLTHRU */
@@ -1419,7 +1419,7 @@
        goto lcopy;
    }
 lcopy:
-   putwc(c, faction);
+   putc(c, faction);
    /*
     * Save the possible identifier name.
     * Used to print out a warning message.
@@ -1434,7 +1434,7 @@
     * If c has a possibility to be a
     * part of identifier, save it.
     */
-   else if (iswalnum(c) || c == L'_') {
+   else if (isalnum(c) || c == L'_') {
        id_name[id_idx++] = c;
        id_name[id_idx] = 0;
    } else {
@@ -1446,28 +1446,28 @@
 
 static void
 lhsfill(s) /* new rule, dump old (if exists), restart strings */
-wchar_t *s;
+char *s;
 {
    static int lhs_len = LHS_TEXT_LEN;
-   int s_lhs = wcslen(s);
+   int s_lhs = strlen(s);
    if (s_lhs >= lhs_len) {
        lhs_len = s_lhs + 2;
-       lhstext = realloc(lhstext, sizeof (wchar_t)*lhs_len);
+       lhstext = realloc(lhstext, sizeof (char)*lhs_len);
        if (lhstext == NULL)
            error("couldn't expanded LHS length");
    }
    rhsfill(NULL);
-   wcscpy(lhstext, s); /* don't worry about too long of a name */
+   strcpy(lhstext, s); /* don't worry about too long of a name */
 }
 
 static void
-rhsfill(wchar_t *s)    /* either name or 0 */
+rhsfill(char *s)   /* either name or 0 */
 {
-   static wchar_t *loc;    /* next free location in rhstext */
+   static char *loc;   /* next free location in rhstext */
    static int rhs_len = RHS_TEXT_LEN;
    static int used = 0;
-   int s_rhs = (s == NULL ? 0 : wcslen(s));
-   register wchar_t *p;
+   int s_rhs = (s == NULL ? 0 : strlen(s));
+   register char *p;
 
    if (!s) /* print out and erase old text */
    {
@@ -1481,10 +1481,10 @@
 
    used = loc - rhstext;
    if ((s_rhs + 3) >= (rhs_len - used)) {
-       static wchar_t *textbase;
+       static char *textbase;
        textbase = rhstext;
        rhs_len += s_rhs + RHS_TEXT_LEN;
-       rhstext = realloc(rhstext, sizeof (wchar_t)*rhs_len);
+       rhstext = realloc(rhstext, sizeof (char)*rhs_len);
        if (rhstext == NULL)
            error("couldn't expanded RHS length");
        loc = loc - textbase + rhstext;
@@ -1508,15 +1508,15 @@
 static void 
 lrprnt (void)  /* print out the left and right hand sides */
 {
-   wchar_t *rhs;
-   wchar_t *m_rhs = NULL;
+   char *rhs;
+   char *m_rhs = NULL;
 
    if (!*rhstext)      /* empty rhs - print usual comment */
-       rhs = L" /* empty */";
+       rhs = " /* empty */";
    else {
        int idx1; /* tmp idx used to find if there are d_quotes */
        int idx2; /* tmp idx used to generate escaped string */
-       wchar_t *p;
+       char *p;
        /*
         * Check if there are any double quote in RHS.
         */
@@ -1525,8 +1525,8 @@
                /*
                 * A double quote is found.
                 */
-               idx2 = wcslen(rhstext)*2;
-               p = m_rhs = malloc((idx2 + 1)*sizeof (wchar_t));
+               idx2 = strlen(rhstext)*2;
+               p = m_rhs = malloc((idx2 + 1)*sizeof (char));
                if (m_rhs == NULL)
                    error(
                    "Couldn't allocate memory for RHS.");
@@ -1617,7 +1617,7 @@
 exp_tokname(void)
 {
    toksize += NAMESIZE;
-   tokname = realloc(tokname, sizeof (wchar_t) * toksize);
+   tokname = realloc(tokname, sizeof (char) * toksize);
 }
 
 
@@ -1633,7 +1633,7 @@
 
    prdptr = realloc(prdptr, sizeof (int *) * (nprodsz+2));
    levprd  = realloc(levprd, sizeof (int) * (nprodsz+2));
-   had_act = realloc(had_act, sizeof (wchar_t) * (nprodsz+2));
+   had_act = realloc(had_act, sizeof (char) * (nprodsz+2));
    for (i = nprodsz-NPROD; i < nprodsz+2; ++i)
        had_act[i] = 0;
 
diff -U3 -r yacc/y3.c yacc/y3.c
--- yacc/y3.c   2005-11-26 17:37:56.000000000 +0000
+++ yacc/y3.c   2021-01-25 19:01:48.390375872 +0000
@@ -41,7 +41,7 @@
 static void precftn(int, int, int);
 static void wract(int);
 static void wrstate(int);
-static void wdef(wchar_t *, int);
+static void wdef(char *, int);
 #ifndef    NOLIBW
 static void wrmbchars(void);
 #endif /* !NOLIBW */
@@ -121,7 +121,7 @@
    }
 
    fprintf(ftable, "\t};\n");
-   wdef(L"YYNPROD", nprod);
+   wdef("YYNPROD", nprod);
 #ifndef    NOLIBW
    if (nmbchars > 0) {
        wrmbchars();
@@ -485,14 +485,14 @@
 }
 
 static void
-wdef(wchar_t *s, int n)
+wdef(char *s, int n)
 {
    /* output a definition of s to the value n */
    fprintf(ftable, "# define %ls %d\n", s, n);
 }
 
 void
-warray(wchar_t *s, int *v, int n)
+warray(char *s, int *v, int n)
 {
    register int i;
    fprintf(ftable, "static YYCONST yytabelem %ls[]={\n", s);
@@ -549,11 +549,11 @@
 wrmbchars(void)
 {
    int i;
-   wdef(L"YYNMBCHARS", nmbchars);
+   wdef("YYNMBCHARS", nmbchars);
    qsort(mbchars, nmbchars, sizeof (*mbchars),
        (int (*)(const void *, const void *))cmpmbchars);
    fprintf(ftable,
-       "static struct{\n\twchar_t character;"
+       "static struct{\n\tchar character;"
        "\n\tint tvalue;\n}yymbchars[YYNMBCHARS]={\n");
    for (i = 0; i < nmbchars; ++i) {
        fprintf(ftable, "\t{%#x,%d}",
diff -U3 -r yacc/y4.c yacc/y4.c
--- yacc/y4.c   2005-11-26 17:37:56.000000000 +0000
+++ yacc/y4.c   2021-01-25 19:02:06.780876425 +0000
@@ -36,7 +36,6 @@
  */
 
 #include "dextern"
-#include <wchar.h>
 #include <unistd.h>
 #define    NOMORE -1000
 
@@ -44,7 +43,7 @@
 static void stin(int);
 static void osummary(void);
 static void aoutput(void);
-static void arout(wchar_t *, int *, int);
+static void arout(char *, int *, int);
 static int nxti(void);
 static int gtnm(void);
 
@@ -414,13 +413,13 @@
    /* write out the optimized parser */
 
    fprintf(ftable, "# define YYLAST %d\n", maxa-amem + 1);
-   arout(L"yyact", amem, (maxa - amem) + 1);
-   arout(L"yypact", indgo, nstate);
-   arout(L"yypgo", pgo, nnonter + 1);
+   arout("yyact", amem, (maxa - amem) + 1);
+   arout("yypact", indgo, nstate);
+   arout("yypgo", pgo, nnonter + 1);
 }
 
 static void
-arout(wchar_t *s, int *v, int n)
+arout(char *s, int *v, int n)
 {
    register int i;
 
@@ -448,8 +447,8 @@
    s = 1;
    val = 0;
 
-   while ((c = getwc(finput)) != EOF) {
-       if (iswdigit(c))
+   while ((c = getc(finput)) != EOF) {
+       if (isdigit(c))
            val = val * 10 + c - L'0';
        else if (c == L'-')
            s = -1;
diff -U3 -r yacc/Makefile.mk yacc/Makefile.mk
--- yacc/Makefile.mk    2007-01-04 23:31:24.000000000 +0000
+++ yacc/Makefile.mk    2021-01-25 01:25:15.592235579 +0000
@@ -1,4 +1,4 @@
-YOBJ = y1.o y2.o y3.o y4.o y5.o getopt.o
+YOBJ = y1.o y2.o y3.o y4.o getopt.o
 
 LOBJ = libmai.o libzer.o
 

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/dextern

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/dextern used in: Step 989, Step 992, Step 993, Step 994, Step 995
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */

/*  from OpenSolaris "dextern   6.17    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)dextern   1.6 (gritter) 11/10/05
 */

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <wctype.h>

    /*  MANIFEST CONSTANT DEFINITIONS */
#define WORD32

    /* base of nonterminal internal numbers */

#define NTBASE (10000000)

    /* internal codes for error and accept actions */

#define ERRCODE  8190
#define ACCEPTCODE 8191

    /* sizes and limits */

#define ACTSIZE 4000
#define MEMSIZE 2000
#define PSTSIZE 1024
#define NSTATES 1000
#define NTERMS 127
#define NPROD 300
#define NNONTERM 600
#define TEMPSIZE 800
#define CNAMSZ 1000
#define LSETSIZE 950
#define WSETSIZE 850

#define NAMESIZE 50
#define NTYPES 1000

#define NMBCHARSZ 100
#define LKFACTOR 5

#ifdef WORD32
    /* bit packing macros (may be machine dependent) */
#define BIT(a, i) ((a)[(i)>>5] & (1<<((i)&037)))
#define SETBIT(a, i) ((a)[(i)>>5] |= (1<<((i)&037)))

    /* number of words needed to hold n+1 bits */
#define NWORDS(n) (((n)+32)/32)

#else

    /* bit packing macros (may be machine dependent) */
#define BIT(a, i) ((a)[(i)>>4] & (1<<((i)&017)))
#define SETBIT(a, i) ((a)[(i)>>4] |= (1<<((i)&017)))

    /* number of words needed to hold n+1 bits */
#define NWORDS(n) (((n)+16)/16)
#endif

    /*
     * relationships which must hold:
     * TBITSET ints must hold NTERMS+1 bits...
     * WSETSIZE >= NNONTERM
     * LSETSIZE >= NNONTERM
     * TEMPSIZE >= NTERMS + NNONTERMs + 1
     * TEMPSIZE >= NSTATES
     */

    /* associativities */

#define NOASC 0  /* no assoc. */
#define LASC 1  /* left assoc. */
#define RASC 2  /* right assoc. */
#define BASC 3  /* binary assoc. */

    /* flags for state generation */

#define DONE 0
#define MUSTDO 1
#define MUSTLOOKAHEAD 2

    /* flags for a rule having an action, and being reduced */

#define ACTFLAG 04
#define REDFLAG 010

    /* output parser flags */
#define YYFLAG1 (-10000000)

    /* macros for getting associativity and precedence levels */

#define ASSOC(i) ((i)&07)
#define PLEVEL(i) (((i)>>4)&077)
#define TYPE(i)  ((i>>10)&077)

    /* macros for setting associativity and precedence levels */

#define SETASC(i, j) i |= j
#define SETPLEV(i, j) i |= (j<<4)
#define SETTYPE(i, j) i |= (j<<10)

    /* looping macros */

#define TLOOP(i) for (i = 1; i <= ntokens; ++i)
#define NTLOOP(i) for (i = 0; i <= nnonter; ++i)
#define PLOOP(s, i) for (i = s; i < nprod; ++i)
#define SLOOP(i) for (i = 0; i < nstate; ++i)
#define WSBUMP(x) ++x
#define WSLOOP(s, j) for (j = s; j < &wsets[cwp]; ++j)
#define ITMLOOP(i, p, q) q = pstate[i+1]; for (p = pstate[i]; p < q; ++p)
#define SETLOOP(i) for (i = 0; i < tbitset; ++i)

    /* I/O descriptors */

extern FILE * finput;       /* input file */
extern FILE * faction;      /* file for saving actions */
extern FILE * fdefine;      /* file for #defines */
extern FILE * ftable;       /* y.tab.c file */
extern FILE * ftemp;        /* tempfile to pass 2 */
extern FILE * fdebug;       /* tempfile for two debugging info arrays */
extern FILE * foutput;      /* y.output file */

    /* structure declarations */

typedef struct looksets {
    int *lset;
} LOOKSETS;

typedef struct item {
    int *pitem;
    LOOKSETS *look;
} ITEM;

typedef struct toksymb {
    wchar_t *name;
    int value;
} TOKSYMB;

typedef struct mbclit {
    wchar_t character;
    int tvalue; /* token issued for the character */
} MBCLIT;

typedef struct ntsymb {
    wchar_t *name;
    int tvalue;
} NTSYMB;

typedef struct wset {
    int *pitem;
    int flag;
    LOOKSETS ws;
} WSET;

    /* token information */

extern int ntokens; /* number of tokens */
extern TOKSYMB *tokset;
extern int ntoksz;

    /*
     * multibyte (c > 255) character literals are
     * handled as though they were tokens except
     * that it generates a separate mapping table.
     */
extern int nmbchars;    /* number of mb literals */
extern MBCLIT *mbchars;
extern int nmbcharsz;

    /* nonterminal information */

extern int nnonter; /* the number of nonterminals */
extern NTSYMB *nontrst;
extern int nnontersz;

    /* grammar rule information */

extern int nprod;   /* number of productions */
extern int **prdptr;    /* pointers to descriptions of productions */
extern int *levprd; /* contains production levels to break conflicts */
extern wchar_t *had_act; /* set if reduction has associated action code */

    /* state information */

extern int nstate;      /* number of states */
extern ITEM **pstate;   /* pointers to the descriptions of the states */
extern int *tystate;    /* contains type information about the states */
extern int *defact; /* the default action of the state */

extern int size;

    /* lookahead set information */

extern int TBITSET;
extern LOOKSETS *lkst;
extern int nolook;  /* flag to turn off lookahead computations */

    /* working set information */

extern WSET *wsets;

    /* storage for productions */

extern int *mem0;
extern int *mem;
extern int *tracemem;
extern int new_memsize;

    /* storage for action table */

extern int *amem;
extern int *memp;       /* next free action table position */
extern int *indgo;      /* index to the stored goto table */
extern int new_actsize;

    /* temporary vector, indexable by states, terms, or ntokens */

extern int *temp1;
extern int lineno; /* current line number */

    /* statistics collection variables */

extern int zzgoent;
extern int zzgobest;
extern int zzacent;
extern int zzexcp;
extern int zzrrconf;
extern int zzsrconf;

    /* define external functions */

extern void setup(int, char * []);
extern void closure(int);
extern void output(void);
extern void aryfil(int *, int, int);
extern void error(char *, ...);
extern void warning(int, char *, ...);
extern void putitem(int *, LOOKSETS *);
extern void go2out(void);
extern void hideprod(void);
extern void callopt(void);
extern void warray(wchar_t *, int *, int);
extern wchar_t *symnam(int);
extern wchar_t *writem(int *);
extern void exp_mem(int);
extern void exp_act(int **);
extern int apack(int *, int);
extern int state(int);
extern void fprintf3(FILE *, const char *, const wchar_t *, const char *, ...);
extern void error3(const char *, const wchar_t *, const char *, ...);

    /* multibyte i/o */

#undef  getwc
#define getwc(f)    yacc_getwc(f)
extern wint_t yacc_getwc(FILE *);
#undef  putwc
#define putwc(c, f) yacc_putwc(c, f)
extern wint_t yacc_putwc(wchar_t, FILE *);

    /* yaccpar location */

extern char *parser;

    /* default settings for a number of macros */

    /* name of yacc tempfiles */

#ifndef TEMPNAME
#define TEMPNAME "yacc.tmp"
#endif

#ifndef ACTNAME
#define ACTNAME "yacc.acts"
#endif

#ifndef DEBUGNAME
#define DEBUGNAME "yacc.debug"
#endif

    /* command to clobber tempfiles after use */

#ifndef ZAPFILE
#define ZAPFILE(x) unlink(x)
#endif

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y1.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y1.c used in: Step 989, Step 992
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1990 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */

/*  from OpenSolaris "y1.c  6.27    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)y1.c  1.7 (gritter) 11/26/05
 */

#include "dextern"
#include <sys/param.h>
#include <errno.h>
#include <unistd.h>
#include <locale.h>
#include <stdarg.h> /* For error() */
#include <wchar.h>

static void mktbls(void);
static void others(void);
static void summary(void);
static wchar_t *chcopy(wchar_t *, wchar_t *);
static int setunion(int *, int *);
static void prlook(LOOKSETS *);
static void cpres(void);
static void cpfir(void);
static void cempty(void);
static void stagen(void);
static LOOKSETS *flset(LOOKSETS *);
static void exp_lkst(void);
static void exp_wsets(void);
static void exp_states(void);
static void exp_psmem(void);

    /* lookahead computations */

int TBITSET;
static int tbitset; /* size of lookahead sets */
LOOKSETS *lkst;
static int lsetsize;

static int nlset = 0;   /* next lookahead set index */
int nolook = 0;     /* flag to suppress lookahead computations */
static LOOKSETS clset;  /* temporary storage for lookahead computations */

static ITEM *psmem, *zzmemsz;
static int new_pstsize = PSTSIZE;

    /* working set computations */

WSET *wsets;
int cwp;
static int wsetsz = 0;      /* number of WSET items in wsets block */

    /* state information */

int nstate = 0;         /* number of states */
static int nstatesz = NSTATES;  /* number of state space allocated  */
ITEM **pstate;          /* ptr to descriptions of the states    */
int *tystate;           /* contains type info about the states  */
int *indgo;         /* index to the stored goto table   */
static int *tmp_lset;
static int *tstates;        /* states generated by terminal gotos   */
static int *ntstates;       /* states generated by non-term gotos   */
static int *mstates;        /* chain of overflows of term/nonterm   */
                /* generation lists         */

    /* storage for the actions in the parser */

int *amem, *memp;       /* next free action table position */
int new_actsize = ACTSIZE;

    /* other storage areas */

int *temp1;     /* temp storate, indexed by terms+ntokens or states */
int lineno = 0;     /* current input line number */
int size;
static int fatfl = 1;   /* if on, error is fatal */
static int nerrors = 0; /* number of errors */

    /* storage for information about the nonterminals */

static int ***pres;     /* vector of pointers to productions    */
                /* yielding each nonterminal        */
static LOOKSETS **pfirst;   /* vector of pointers to first sets for */
                /* each nonterminal         */
static int *pempty;     /* vector of nonterminals nontrivially  */
                /* deriving e               */
extern int nprodsz;

static char *sav_argv0;
char run_directory[MAXPATHLEN];
char current_work_directory[MAXPATHLEN];

int 
main(int argc, char *argv[])
{
    setlocale(LC_CTYPE, "");

    sav_argv0 = argv[0];
    setup(argc, argv);      /* initialize and read productions */
    TBITSET = NWORDS(ntoksz*LKFACTOR);
    tbitset = NWORDS(ntokens*LKFACTOR);
    mktbls();
    cpres();    /* make table of which productions yield a */
            /* given nonterminal */
    cempty();   /* make a table of which nonterminals can match */
            /* the empty string */
    cpfir();    /* make a table of firsts of nonterminals */
    stagen();   /* generate the states  */
    output();   /* write the states and the tables */
    go2out();
    hideprod();
    summary();
    callopt();
    others();
    return 0;
}


static void 
mktbls(void)
{
    int i;

    size = ntoksz + nnontersz +1;
    if (size < nstatesz)
        size = nstatesz;
    if (size < new_memsize)
        size = new_memsize;

    amem = malloc(sizeof (int) * new_actsize);
    psmem = malloc(sizeof (ITEM) * new_pstsize);
    if ((psmem == NULL) || (amem == NULL))
        error("couldn't allocate initial table");
    zzmemsz = psmem;
    memp = amem;

    /*
     * For lkst
     */
#define INIT_LSIZE  nnontersz*LKFACTOR
    tmp_lset = calloc((size_t)(TBITSET * (INIT_LSIZE+1)), sizeof (int));
    if (tmp_lset == NULL)
        error("could not allocate lookset array");
    lkst = malloc(sizeof (LOOKSETS) * (INIT_LSIZE + 1));
    for (i = 0; i <= INIT_LSIZE; ++i)
        lkst[i].lset = tmp_lset + TBITSET * i;
    tmp_lset = NULL;

    /*
     * For wsets
     */
    tmp_lset = calloc((size_t)(TBITSET * (nnontersz+1)), sizeof (int));
    if (tmp_lset == NULL)
        error("could not allocate lookset array");
    wsets = (WSET *) malloc(sizeof (WSET) * (nnontersz + 1));
    for (i = 0; i <= nnontersz; ++i)
        wsets[i].ws.lset = tmp_lset + TBITSET * i;
    tmp_lset = NULL;

    clset.lset = malloc(sizeof (int)*TBITSET);
    tstates = malloc(sizeof (int)*(ntoksz + 1));
    ntstates = malloc(sizeof (int)*(nnontersz + 1));
    temp1 = malloc(sizeof (int)*size);
    pres = malloc(sizeof (int **)*(nnontersz + 2));
    pfirst = malloc(sizeof (LOOKSETS *)*(nnontersz + 2));
    pempty = malloc(sizeof (int)*(nnontersz + 1));

    pstate = malloc(sizeof (ITEM *)*(nstatesz+2));
    tystate = malloc(sizeof (int)*nstatesz);
    indgo = malloc(sizeof (int)*nstatesz);
    mstates = malloc(sizeof (int)*nstatesz);
    defact = malloc(sizeof (int)*nstatesz);

    if ((lkst == NULL) || (wsets == NULL) || (tstates == NULL) ||
        (ntstates == NULL) || (temp1 == NULL) || (pres == NULL) ||
        (pfirst == NULL) || (pempty == NULL) || (pstate == NULL) ||
        (tystate == NULL) || (indgo == NULL) || (mstates == NULL) ||
        (defact == NULL) || (clset.lset == NULL))
            error("cannot allocate tables in mktbls()");

    aryfil(ntstates, nnontersz+1, 0);
    aryfil(tstates, ntoksz+1, 0);
    wsetsz = nnontersz + 1;
    lsetsize = INIT_LSIZE + 1;
}

/* put out other arrays, copy the parsers */
static void 
others(void)
{
    extern int gen_lines;
    register int c, i, j;
    int tmpline;

    /* This  routine has been "stolen" from the driver */
    if (parser == NULL)
        parser = PARSER;

    finput = fopen(parser, "r");
    if (finput == NULL)
        error("cannot find parser %s", parser);

    warray(L"yyr1", levprd, nprod);

    aryfil(temp1, nprod, 0);
                    /* had_act[i] is either 1 or 0 */
    PLOOP(1, i)
        temp1[i] = ((prdptr[i+1] - prdptr[i]-2) << 1) | had_act[i];
    warray(L"yyr2", temp1, nprod);

    aryfil(temp1, nstate, -10000000);
    TLOOP(i)
        for (j = tstates[i]; j != 0; j = mstates[j])
            temp1[j] = tokset[i].value;
    NTLOOP(i)
        for (j = ntstates[i]; j != 0; j = mstates[j])
            temp1[j] = -i;
    warray(L"yychk", temp1, nstate);

    warray(L"yydef", defact, nstate);

    if ((fdebug = fopen(DEBUGNAME, "r")) == NULL)
        error("cannot open yacc.debug");
    while ((c = getwc(fdebug)) != EOF)
        putwc(c, ftable);
    fclose(fdebug);
    ZAPFILE(DEBUGNAME);

    if (gen_lines)
        fprintf(ftable, "# line\t1 \"%s\"\n", parser);
    tmpline = 1;
    /* copy parser text */
    while ((c = getwc(finput)) != EOF) {
        if (c == '\n')
            tmpline++;
        if (c == L'$') {
            if ((c = getwc(finput)) != L'A')
                putwc(L'$', ftable);
            else { /* copy actions */
                tmpline++;
                faction = fopen(ACTNAME, "r");
                if (faction == NULL)
                    error("cannot open action tempfile");
                while ((c = getwc(faction)) != EOF)
                    putwc(c, ftable);
                fclose(faction);
                if (gen_lines)
                    fprintf(ftable,
                        "\n# line\t%d \"%s\"",
                        tmpline,
                        parser);
                ZAPFILE(ACTNAME);
                c = getwc(finput);
            }
        }
        putwc(c, ftable);
    }
    fclose(ftable);
}


/* copies string q into p, returning next free char ptr */
static wchar_t *
chcopy(wchar_t *p, wchar_t *q)
{
    while (*p = *q++)
        ++p;
    return (p);
}

#define ISIZE 400
/* creates output string for item pointed to by pp */
wchar_t *
writem(int *pp)
{
    int i, *p;
    static int isize = ISIZE;
    static wchar_t *sarr = NULL;
    wchar_t *q;

    if (sarr == NULL) {
        sarr = malloc(sizeof (wchar_t) * isize);
        if (sarr == NULL)
            error("could not allocate output string array");
        for (i = 0; i < isize; ++i)
            sarr[i] = L' ';
    }
    for (p = pp; *p > 0; ++p) /* EMPTY */;
    p = prdptr[-*p];
    q = chcopy(sarr, nontrst[*p-NTBASE].name);
    q = chcopy(q, L" : ");

    for (;;) {
        *q++ = ++p == pp ? L'_' : L' ';
        *q = 0;
        if ((i = *p) <= 0)
            break;
        q = chcopy(q, symnam(i));
        while (q > &sarr[isize-30]) {
            static wchar_t *sarrbase;

            sarrbase = sarr;
            isize += ISIZE;
            sarr = realloc(sarr, sizeof (*sarr) * isize);
            if (sarr == NULL)
                error("cannot expand sarr arrays");
            q = q - sarrbase + sarr;
        }
    }

    /* an item calling for a reduction */
    if ((i = *pp) < 0) {
        q = chcopy(q, L"    (");
        swprintf(q, q + isize - sarr, L"%d)", -i);
    }
    return (sarr);
}

/* return a pointer to the name of symbol i */
wchar_t *
symnam(int i)
{
    wchar_t *cp;

    cp = (i >= NTBASE) ? nontrst[i-NTBASE].name : tokset[i].name;
    if (*cp == L' ')
        ++cp;
    return (cp);
}

static int zzcwp = 0;
static int zzclose = 0;
int zzgoent = 0;
int zzgobest = 0;
int zzacent = 0;
int zzexcp = 0;
int zzsrconf = 0;
int zzrrconf = 0;

/* output the summary on the tty */
static void 
summary(void)
{
    if (foutput != NULL) {
        fprintf(foutput,
            "\n%d/%d terminals, %d/%d nonterminals\n",
            ntokens, ntoksz, nnonter, nnontersz);
        fprintf(foutput,
            "%d/%d grammar rules, %d/%d states\n",
            nprod, nprodsz, nstate, nstatesz);
        fprintf(foutput,
        "%d shift/reduce, %d reduce/reduce conflicts reported\n",
            zzsrconf, zzrrconf);
        fprintf(foutput,
            "%d/%d working sets used\n", zzcwp, wsetsz);
        fprintf(foutput,
            "memory: states,etc. %d/%d, parser %d/%d\n",
            mem-tracemem, new_memsize, memp-amem, new_actsize);
        fprintf(foutput,
            "%d/%d distinct lookahead sets\n", nlset, lsetsize);
        fprintf(foutput,
            "%d extra closures\n", zzclose - 2*nstate);
        fprintf(foutput,
            "%d shift entries, %d exceptions\n", zzacent, zzexcp);
        fprintf(foutput,
            "%d goto entries\n", zzgoent);
        fprintf(foutput,
            "%d entries saved by goto default\n", zzgobest);
    }
    if (zzsrconf != 0 || zzrrconf != 0) {
        fprintf(stderr, "\nconflicts: ");
        if (zzsrconf)
            fprintf(stderr, "%d shift/reduce", zzsrconf);
        if (zzsrconf && zzrrconf)
            fprintf(stderr, ", ");
        if (zzrrconf)
            fprintf(stderr, "%d reduce/reduce", zzrrconf);
        fprintf(stderr, "\n");
    }

    if (ftemp != NULL)
        fclose(ftemp);
    if (fdefine != NULL)
        fclose(fdefine);
}

/* write out error comment */
void
error(char *s, ...)
{
    extern char *infile;
    va_list ap;

    va_start(ap, s);

    ++nerrors;
    if (!lineno)
        fprintf(stderr, "command line: fatal: ");
    else {
        fprintf(stderr, "\"%s\", ", infile);
        fprintf(stderr, "line %d: fatal: ", lineno);
    }
    vfprintf(stderr, s, ap);
    fprintf(stderr, "\n");
    va_end(ap);
    if (!fatfl)
        return;
    summary();
    exit(1);
}

/*
 * Print out a warning message.
 */
void
warning(int flag, char *s, ...)
{
    extern char *infile;
    va_list ap;
    va_start(ap, s);

    fprintf(stderr, "\"%s\", ", infile);
    /*
     * If flag, print lineno as well.
     */
    if (flag == 0)
        fprintf(stderr, "warning: ");
    else
        fprintf(stderr, "line %d: warning: ", lineno);
    vfprintf(stderr, s, ap);
    fprintf(stderr, "\n");
    va_end(ap);
}

/* set elements 0 through n-1 to c */
void 
aryfil(int *v, int n, int c)
{
    int i;
    for (i = 0; i < n; ++i)
        v[i] = c;
}

/* set a to the union of a and b */
/* return 1 if b is not a subset of a, 0 otherwise */
static int
setunion(register int *a, register int *b)
{
    register int i, x, sub;

    sub = 0;
    SETLOOP(i) {
        *a = (x = *a) | *b++;
        if (*a++ != x)
            sub = 1;
    }
    return (sub);
}

static void 
prlook(LOOKSETS *p)
{
    register int j, *pp;
    pp = p->lset;
    if (pp == 0)
        fprintf(foutput, "\tNULL");
    else {
        fprintf(foutput, " { ");
        TLOOP(j) {
            if (BIT(pp, j))
                fprintf(foutput,  "%ls ", symnam(j));
        }
        fprintf(foutput,  "}");
    }
}

/*
 * compute an array with the beginnings of  productions yielding
 * given nonterminals
 * The array pres points to these lists
 * the array pyield has the lists: the total size is only NPROD+1
 */
static void 
cpres(void)
{
    register int **ptrpy;
    int **pyield;
    register int c, j, i;

    /*
     * 2/29/88 -
     * nprodsz is the size of the tables describing the productions.
     * Normally this will be NPROD unless the production tables have
     * been expanded, in which case the tables will be NPROD * N(where
     * N is the number of times the tables had to be expanded.)
     */
    if ((pyield = malloc(sizeof (int *) * nprodsz)) == NULL)
        error("cannot allocate space for pyield array");

    ptrpy = pyield;

    NTLOOP(i) {
        c = i+NTBASE;
        pres[i] = ptrpy;
        fatfl = 0;  /* make undefined  symbols  nonfatal */
        PLOOP(0, j) {
            if (*prdptr[j] == c)    /* linear search for all c's */
                *ptrpy++ =  prdptr[j] + 1;
        }
        if (pres[i] == ptrpy) {         /* c not found */
            error("undefined nonterminal: %ls", nontrst[i].name);
        }
    }
    pres[i] = ptrpy;
    fatfl = 1;
    if (nerrors) {
        summary();
        exit(1);
    }
    if (ptrpy != &pyield[nprod])
        error("internal Yacc error: pyield %d", ptrpy-&pyield[nprod]);
}

static int indebug = 0;
/* compute an array with the first of nonterminals */
static void 
cpfir(void)
{
    register int *p, **s, i, **t, ch, changes;

    zzcwp = nnonter;
    NTLOOP(i) {
        aryfil(wsets[i].ws.lset, tbitset, 0);
        t = pres[i+1];
        /* initially fill the sets */
        for (s = pres[i]; s < t; ++s) {
            /* check if ch is non-terminal */
            for (p = *s; (ch = *p) > 0; ++p) {
                if (ch < NTBASE) {  /* should be token */
                    SETBIT(wsets[i].ws.lset, ch);
                    break;
                } else if (!pempty[ch-NTBASE])
                    break;
            }
        }
    }

    /* now, reflect transitivity */

    changes = 1;
    while (changes) {
        changes = 0;
        NTLOOP(i) {
            t = pres[i+1];
            for (s = pres[i]; s < t; ++s) {
                for (p = *s; (ch = (*p-NTBASE)) >= 0; ++p) {
                    changes |= setunion(wsets[i].ws.lset,
                            wsets[ch].ws.lset);
                    if (!pempty[ch])
                        break;
                }
            }
        }
    }

    NTLOOP(i)
        pfirst[i] = flset(&wsets[i].ws);
    if (!indebug)
        return;
    if ((foutput != NULL)) {
        NTLOOP(i) {
            fprintf(foutput, "\n%ls: ", nontrst[i].name);
            prlook(pfirst[i]);
            fprintf(foutput, " %d\n", pempty[i]);
        }
    }
}

/* sorts last state,and sees if it equals earlier ones. returns state number */
int
state(int c)
{
    int size1, size2;
    register int i;
    ITEM *p1, *p2, *k, *l, *q1, *q2;
    p1 = pstate[nstate];
    p2 = pstate[nstate+1];
    if (p1 == p2)
        return (0); /* null state */
    /* sort the items */
    for (k = p2 - 1; k > p1; k--) { /* make k the biggest */
        for (l = k-1; l >= p1; --l)
            if (l->pitem > k->pitem) {
                int *s;
                LOOKSETS *ss;
                s = k->pitem;
                k->pitem = l->pitem;
                l->pitem = s;
                ss = k->look;
                k->look = l->look;
                l->look = ss;
            }
    }
    size1 = p2 - p1; /* size of state */

    for (i = (c >= NTBASE) ? ntstates[c-NTBASE] : tstates[c];
            i != 0;
            i = mstates[i]) {
        /* get ith state */
        q1 = pstate[i];
        q2 = pstate[i+1];
        size2 = q2 - q1;
        if (size1 != size2)
            continue;
        k = p1;
        for (l = q1; l < q2; l++) {
            if (l->pitem != k->pitem)
                break;
            ++k;
        }
        if (l != q2)
            continue;
        /* found it */
        pstate[nstate+1] = pstate[nstate]; /* delete last state */
        /* fix up lookaheads */
        if (nolook)
            return (i);
        for (l = q1, k = p1; l < q2; ++l, ++k) {
            int s;
            SETLOOP(s)
                clset.lset[s] = l->look->lset[s];
            if (setunion(clset.lset, k->look->lset)) {
                tystate[i] = MUSTDO;
                /* register the new set */
                l->look = flset(&clset);
            }
        }
        return (i);
    }
    /* state is new */
    if (nolook)
        error("yacc state/nolook error");
    pstate[nstate+2] = p2;
    if (nstate+1 >= nstatesz)
        exp_states();
    if (c >= NTBASE) {
        mstates[nstate] = ntstates[c - NTBASE];
        ntstates[c - NTBASE] = nstate;
    } else {
        mstates[nstate] = tstates[c];
        tstates[c] = nstate;
    }
    tystate[nstate] = MUSTDO;
    return (nstate++);
}

static int pidebug = 0;

void 
putitem(int *ptr, LOOKSETS *lptr)
{
    register ITEM *j;

    if (pidebug && (foutput != NULL))
        fprintf(foutput,
            "putitem(%ls), state %d\n", writem(ptr), nstate);
    j = pstate[nstate+1];
    j->pitem = ptr;
    if (!nolook)
        j->look = flset(lptr);
    pstate[nstate+1] = ++j;
    if (j > zzmemsz) {
        zzmemsz = j;
        if (zzmemsz >=  &psmem[new_pstsize])
            exp_psmem();
            /* error("out of state space"); */
    }
}

/*
 * mark nonterminals which derive the empty string
 * also, look for nonterminals which don't derive any token strings
 */
static void 
cempty(void)
{
#define EMPTY 1
#define WHOKNOWS 0
#define OK 1
    register int i, *p;

    /*
     * first, use the array pempty to detect productions
     * that can never be reduced
     */

    /* set pempty to WHONOWS */
    aryfil(pempty, nnonter+1, WHOKNOWS);

    /*
     * now, look at productions, marking nonterminals which
     * derive something
     */
    more:
    PLOOP(0, i) {
        if (pempty[*prdptr[i] - NTBASE])
            continue;
        for (p = prdptr[i] + 1; *p >= 0; ++p)
            if (*p >= NTBASE && pempty[*p-NTBASE] == WHOKNOWS)
                break;
        if (*p < 0) { /* production can be derived */
            pempty[*prdptr[i]-NTBASE] = OK;
            goto more;
        }
    }

    /* now, look at the nonterminals, to see if they are all OK */

    NTLOOP(i) {
        /*
         * the added production rises or falls as the
         * start symbol ...
         */
        if (i == 0)
            continue;
        if (pempty[i] != OK) {
            fatfl = 0;
            error("nonterminal %ls never derives any token string",
            nontrst[i].name);
        }
    }

    if (nerrors) {
        summary();
        exit(1);
    }

    /*
     * now, compute the pempty array, to see which nonterminals
     * derive the empty string
     */

    /* set pempty to WHOKNOWS */

    aryfil(pempty, nnonter+1, WHOKNOWS);

    /* loop as long as we keep finding empty nonterminals */

again:
    PLOOP(1, i) {
        /* not known to be empty */
        if (pempty[*prdptr[i]-NTBASE] == WHOKNOWS) {
            for (p = prdptr[i]+1;
                *p >= NTBASE && pempty[*p-NTBASE] == EMPTY;
                ++p);
            /* we have a nontrivially empty nonterminal */
            if (*p < 0) {
                pempty[*prdptr[i]-NTBASE] = EMPTY;
                goto again; /* got one ... try for another */
            }
        }
    }
}

/* generate the states */
static int gsdebug = 0;
static void 
stagen(void)
{
    int i, j;
    register int c;
    register WSET *p, *q;

    /* initialize */

    nstate = 0;

    pstate[0] = pstate[1] = psmem;
    aryfil(clset.lset, tbitset, 0);
    putitem(prdptr[0] + 1, &clset);
    tystate[0] = MUSTDO;
    nstate = 1;
    pstate[2] = pstate[1];

    aryfil(amem, new_actsize, 0);

    /* now, the main state generation loop */

    more:
    SLOOP(i) {
        if (tystate[i] != MUSTDO)
            continue;
        tystate[i] = DONE;
        aryfil(temp1, nnonter + 1, 0);
        /* take state i, close it, and do gotos */
        closure(i);
        WSLOOP(wsets, p) { /* generate goto's */
            if (p->flag)
                continue;
            p->flag = 1;
            c = *(p->pitem);
            if (c <= 1) {
                if (pstate[i+1]-pstate[i] <= p-wsets)
                    tystate[i] = MUSTLOOKAHEAD;
                continue;
            }
            /* do a goto on c */
            WSLOOP(p, q) {
                /* this item contributes to the goto */
                if (c == *(q->pitem)) {
                    putitem(q->pitem + 1, &q->ws);
                    q->flag = 1;
                }
            }
            if (c < NTBASE)
                state(c);  /* register new state */
            else temp1[c-NTBASE] = state(c);
        }
        if (gsdebug && (foutput != NULL)) {
            fprintf(foutput,  "%d: ", i);
            NTLOOP(j) {
                if (temp1[j])
                    fprintf(foutput,
                        "%ls %d, ", nontrst[j].name,
                        temp1[j]);
            }
            fprintf(foutput, "\n");
        }
        indgo[i] = apack(&temp1[1], nnonter - 1) - 1;
        goto more; /* we have done one goto; do some more */
    }
    /* no more to do... stop */
}

/* generate the closure of state i */
static int cldebug = 0; /* debugging flag for closure */
void 
closure(int i)
{
    int c, ch, work, k;
    register WSET *u, *v;
    int *pi;
    int **s, **t;
    ITEM *q;
    register ITEM *p;
    int idx1 = 0;

    ++zzclose;

    /* first, copy kernel of state i to wsets */
    cwp = 0;
    ITMLOOP(i, p, q) {
        wsets[cwp].pitem = p->pitem;
        wsets[cwp].flag = 1;    /* this item must get closed */
        SETLOOP(k)
            wsets[cwp].ws.lset[k] = p->look->lset[k];
        WSBUMP(cwp);
    }

    /* now, go through the loop, closing each item */

    work = 1;
    while (work) {
        work = 0;
        /*
         * WSLOOP(wsets, u) {
         */
        for (idx1 = 0; idx1 < cwp; idx1++) {
            u = &wsets[idx1];
            if (u->flag == 0)
                continue;
            c = *(u->pitem);  /* dot is before c */
            if (c < NTBASE) {
                u->flag = 0;
                /*
                 * only interesting case is where . is
                 * before nonterminal
                 */
                continue;
            }

            /* compute the lookahead */
            aryfil(clset.lset, tbitset, 0);

            /* find items involving c */

            WSLOOP(u, v) {
                if (v->flag == 1 && *(pi = v->pitem) == c) {
                    v->flag = 0;
                    if (nolook)
                        continue;
                    while ((ch = *++pi) > 0) {
                        /* terminal symbol */
                        if (ch < NTBASE) {
                            SETBIT(clset.lset, ch);
                            break;
                        }
                        /* nonterminal symbol */
                        setunion(clset.lset,
                        pfirst[ch-NTBASE]->lset);
                        if (!pempty[ch-NTBASE])
                            break;
                    }
                    if (ch <= 0)
                        setunion(clset.lset,
                                v->ws.lset);
                }
            }

            /*  now loop over productions derived from c */

            c -= NTBASE; /* c is now nonterminal number */

            t = pres[c+1];
            for (s = pres[c]; s < t; ++s) {
                /* put these items into the closure */
                WSLOOP(wsets, v) { /* is the item there */
                    /* yes, it is there */
                    if (v->pitem == *s) {
                        if (nolook)
                            goto nexts;
                        if (setunion(v->ws.lset,
                            clset.lset))
                            v->flag = work = 1;
                        goto nexts;
                    }
                }

                /*  not there; make a new entry */
                if (cwp + 1 >= wsetsz)
                    exp_wsets();

                wsets[cwp].pitem = *s;
                wsets[cwp].flag = 1;
                if (!nolook) {
                    work = 1;
                    SETLOOP(k)
                        wsets[cwp].ws.lset[k] =
                            clset.lset[k];
                }
                WSBUMP(cwp);
                nexts:;
            }
        }
    }

    /* have computed closure; flags are reset; return */

    if (&wsets[cwp] > &wsets[zzcwp])
        zzcwp = cwp;
    if (cldebug && (foutput != NULL)) {
        fprintf(foutput, "\nState %d, nolook = %d\n", i, nolook);
        WSLOOP(wsets, u) {
            if (u->flag)
                fprintf(foutput, "flag set!\n");
            u->flag = 0;
            fprintf(foutput, "\t%ls", writem(u->pitem));
            prlook(&u->ws);
            fprintf(foutput,  "\n");
        }
    }
}

static LOOKSETS *
flset(LOOKSETS *p)
{
    /* decide if the lookahead set pointed to by p is known */
    /* return pointer to a perminent location for the set */

    int j, *w;
    register int *u, *v;
    register LOOKSETS *q;

    for (q = &lkst[nlset]; q-- > lkst; ) {
        u = p->lset;
        v = q->lset;
        w = & v[tbitset];
        while (v < w)
            if (*u++ != *v++)
                goto more;
        /* we have matched */
        return (q);
        more:;
    }
    /* add a new one */
    q = &lkst[nlset++];
    if (nlset >= lsetsize) {
        exp_lkst();
        q = &lkst[nlset++];
    }
    SETLOOP(j) q->lset[j] = p->lset[j];
    return (q);
}

static void 
exp_lkst(void)
{
    int i, j;
    static LOOKSETS *lookbase;

    lookbase = lkst;
    lsetsize += LSETSIZE;
    tmp_lset = calloc(TBITSET * (lsetsize-LSETSIZE), sizeof (int));
    if (tmp_lset == NULL)
        error("could not expand lookset array");
    lkst = realloc(lkst, sizeof (LOOKSETS) * lsetsize);
    for (i = lsetsize-LSETSIZE, j = 0; i < lsetsize; ++i, ++j)
        lkst[i].lset = tmp_lset + TBITSET * j;
    tmp_lset = NULL;
    if (lkst == NULL)
        error("could not expand lookahead sets");
    for (i = 0; i <= nnonter; ++i)
        pfirst[i] = pfirst[i] - lookbase + lkst;
    for (i = 0; i <= nstate+1; ++i) {
        if (psmem[i].look)
            psmem[i].look = psmem[i].look - lookbase + lkst;
        if (pstate[i]->look)
            pstate[i]->look = pstate[i]->look - lookbase + lkst;
    }
}

static void 
exp_wsets(void)
{
    int i, j;

    wsetsz += WSETSIZE;
    tmp_lset = calloc(TBITSET * (wsetsz-WSETSIZE), sizeof (int));
    if (tmp_lset == NULL)
        error("could not expand lookset array");
    wsets = realloc(wsets, sizeof (WSET) * wsetsz);
    for (i = wsetsz-WSETSIZE, j = 0; i < wsetsz; ++i, ++j)
        wsets[i].ws.lset = tmp_lset + TBITSET * j;
    tmp_lset = NULL;
    if (wsets == NULL)
        error("could not expand working sets");
}

static void 
exp_states(void)
{
    nstatesz += NSTATES;

    pstate = realloc(pstate, sizeof (ITEM *)*(nstatesz+2));
    mstates = realloc(mstates, sizeof (int)*nstatesz);
    defact = realloc(defact, sizeof (int)*nstatesz);
    tystate = realloc(tystate, sizeof (int)*nstatesz);
    indgo = realloc(indgo, sizeof (int)*nstatesz);

    if ((*pstate == NULL) || (tystate == NULL) || (defact == NULL) ||
        (indgo == NULL) || (mstates == NULL))
        error("cannot expand table of states");
}

static void 
exp_psmem(void)
{
    int i;

    new_pstsize += PSTSIZE;
    psmem = realloc(psmem, sizeof (ITEM) * new_pstsize);
    if (psmem == NULL)
        error("cannot expand pstate memory");

    zzmemsz = zzmemsz - pstate[0] + psmem;
    for (i = 1; i <= nstate+1; ++i)
        pstate[i] = pstate[i] - pstate[0] + psmem;
    pstate[0] = psmem;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y2.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y2.c used in: Step 989, Step 993
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */

/*  from OpenSolaris "y2.c  6.35    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)y2.c  1.11 (gritter) 11/26/05
 */

#include "dextern"
#include "sgs.h"
#include <wchar.h>
#include <unistd.h>
#define IDENTIFIER 257

#define MARK 258
#define TERM 259
#define LEFT 260
#define RIGHT 261
#define BINARY 262
#define PREC 263
#define LCURLY 264
#define C_IDENTIFIER 265  /* name followed by colon */
#define NUMBER 266
#define START 267
#define TYPEDEF 268
#define TYPENAME 269
#define UNION 270
#define ENDFILE 0
#define LHS_TEXT_LEN        80  /* length of lhstext */
#define RHS_TEXT_LEN        640 /* length of rhstext */
    /* communication variables between various I/O routines */

#define v_FLAG  0x01
#define d_FLAG  0x02
#define DEFAULT_PREFIX  "y"

char *infile;               /* input file name      */
static int numbval;         /* value of an input number     */
static int toksize = NAMESIZE;
static wchar_t *tokname;    /* input token name     */
char *parser = NULL;        /* location of common parser    */

static void finact(void);
static wchar_t *cstash(wchar_t *);
static void defout(void);
static void cpyunion(void);
static void cpycode(void);
static void cpyact(int);
static void lhsfill(wchar_t *);
static void rhsfill(wchar_t *);
static void lrprnt(void);
static void beg_debug(void);
static void end_toks(void);
static void end_debug(void);
static void exp_tokname(void);
static void exp_prod(void);
static void exp_ntok(void);
static void exp_nonterm(void);
static int defin(int, wchar_t *);
static int gettok(void);
static int chfind(int, wchar_t *);
static int skipcom(void);
static int findchtok(int);
static void put_prefix_define(char *);


/* storage of names */

/*
 * initial block to place token and
 * nonterminal names are stored
 * points to initial block - more space
 * is allocated as needed.
 */
static wchar_t cnamesblk0[CNAMSZ];
static wchar_t *cnames = cnamesblk0;

/* place where next name is to be put in */
static wchar_t *cnamp = cnamesblk0;

/* number of defined symbols output */
static int ndefout = 3;

    /* storage of types */
static int defunion = 0;    /* union of types defined? */
static int ntypes = 0;      /* number of types defined */
static wchar_t *typeset[NTYPES]; /* pointers to type tags */

    /* symbol tables for tokens and nonterminals */

int ntokens = 0;
int ntoksz = NTERMS;
TOKSYMB *tokset;
int *toklev;

int nnonter = -1;
NTSYMB *nontrst;
int nnontersz = NNONTERM;

static int start;   /* start symbol */

    /* assigned token type values */
static int extval = 0;

    /* input and output file descriptors */

FILE *finput;       /* yacc input file */
FILE *faction;      /* file for saving actions */
FILE *fdefine;      /* file for # defines */
FILE *ftable;       /* y.tab.c file */
FILE *ftemp;        /* tempfile to pass 2 */
FILE *fdebug;       /* where the strings for debugging are stored */
FILE *foutput;      /* y.output file */

    /* output string */

static wchar_t *lhstext;
static wchar_t *rhstext;

    /* storage for grammar rules */

int *mem0; /* production storage */
int *mem;
int *tracemem;
extern int *optimmem;
int new_memsize = MEMSIZE;
int nprod = 1;  /* number of productions */
int nprodsz = NPROD;

int **prdptr;
int *levprd;
wchar_t *had_act;

/* flag for generating the # line's default is yes */
int gen_lines = 1;
int act_lines = 0;

/* flag for whether to include runtime debugging */
static int gen_testing = 0;

/* flag for version stamping--default turned off */
static char *v_stmp = "n";

int nmbchars = 0;   /* number of mb literals in mbchars */
MBCLIT *mbchars = (MBCLIT *) 0; /* array of mb literals */
int nmbcharsz = 0; /* allocated space for mbchars */

void 
setup(int argc, char *argv[])
{   int ii, i, j, lev, t, ty;
        /* ty is the sequencial number of token name in tokset */
    int c;
    int *p;
    char *cp;
    wchar_t actname[8];
    unsigned int options = 0;
    char *file_prefix = DEFAULT_PREFIX;
    char *sym_prefix = "";
#define F_NAME_LENGTH   4096
    char    fname[F_NAME_LENGTH+1];

    foutput = NULL;
    fdefine = NULL;
    i = 1;

    tokname = malloc(sizeof (wchar_t) * toksize);
    tokset = malloc(sizeof (TOKSYMB) * ntoksz);
    toklev = malloc(sizeof (int) * ntoksz);
    nontrst = malloc(sizeof (NTSYMB) * nnontersz);
    mem0 = malloc(sizeof (int) * new_memsize);
    prdptr = malloc(sizeof (int *) * (nprodsz+2));
    levprd = malloc(sizeof (int) * (nprodsz+2));
    had_act = calloc(nprodsz + 2, sizeof (wchar_t));
    lhstext = malloc(sizeof (wchar_t) * LHS_TEXT_LEN);
    rhstext = malloc(sizeof (wchar_t) * RHS_TEXT_LEN);
    aryfil(toklev, ntoksz, 0);
    aryfil(levprd, nprodsz, 0);
    for (ii = 0; ii < ntoksz; ++ii)
        tokset[ii].value = 0;
    for (ii = 0; ii < nnontersz; ++ii)
        nontrst[ii].tvalue = 0;
    aryfil(mem0, new_memsize, 0);
    mem = mem0;
    tracemem = mem0;

    while ((c = getopt(argc, argv, "vVdltp:Q:Y:P:b:")) != EOF)
        switch (c) {
        case 'v':
            options |= v_FLAG;
            break;
        case 'V':
            fprintf(stderr, "yacc: %s , %s\n", pkg, rel);
            break;
        case 'Q':
            v_stmp = optarg;
            if (*v_stmp != 'y' && *v_stmp != 'n')
                error("yacc: -Q should be followed by [y/n]");
            break;
        case 'd':
            options |= d_FLAG;
            break;
        case 'l':
            gen_lines = 0;  /* don't gen #lines */
            break;
        case 't':
            gen_testing = 1;    /* set YYDEBUG on */
            break;
        case 'Y':
            cp = malloc(strlen(optarg) + sizeof ("/yaccpar") + 1);
            cp = strcpy(cp, optarg);
            parser = strcat(cp, "/yaccpar");
            break;
        case 'P':
            parser = optarg;
            break;
        case 'p':
            if (strcmp(optarg, "yy") != 0)
                sym_prefix = optarg;
            else
                sym_prefix = "";
            break;
        case 'b':
            file_prefix = optarg;
            break;
        case '?':
        default:
            fprintf(stderr,
"Usage: yacc [-vVdlt] [-Q(y/n)] [-P driver_file] file\n");
            exit(1);
        }
    /*
     * Open y.output if -v is specified
     */
    if (options & v_FLAG) {
        strncpy(fname,
            file_prefix,
            F_NAME_LENGTH-strlen(".output"));
        strcat(fname, ".output");
        foutput = fopen(fname, "w");
        if (foutput == NULL)
            error("cannot open y.output");
    }

    /*
     * Open y.tab.h if -d is specified
     */
    if (options & d_FLAG) {
        strncpy(fname,
            file_prefix,
            F_NAME_LENGTH-strlen(".tab.h"));
        strcat(fname, ".tab.h");
        fdefine = fopen(fname, "w");
        if (fdefine == NULL)
            error("cannot open y.tab.h");
    }

    fdebug = fopen(DEBUGNAME, "w");
    if (fdebug == NULL)
        error("cannot open yacc.debug");
    /*
     * Open y.tab.c
     */
    strncpy(fname,
        file_prefix,
        F_NAME_LENGTH-strlen(".tab.c"));
    strcat(fname, ".tab.c");
    ftable = fopen(fname, "w");
    if (ftable == NULL)
        error("cannot open %s", fname);

    ftemp = fopen(TEMPNAME, "w");
    faction = fopen(ACTNAME, "w");
    if (ftemp == NULL || faction == NULL)
        error("cannot open temp file");

    if ((finput = fopen(infile = argv[optind], "r")) == NULL)
        error("cannot open input file");

    lineno = 1;
    cnamp = cnames;
    defin(0, L"$end");
    extval = 0400;
    defin(0, L"error");
    defin(1, L"$accept");
    mem = mem0;
    lev = 0;
    ty = 0;
    i = 0;
    beg_debug();    /* initialize fdebug file */

    /*
     * sorry -- no yacc parser here.....
     *  we must bootstrap somehow...
     */

    t = gettok();
    if (*v_stmp == 'y')
        fprintf(ftable, "\
#if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4\n\
#define YYUSED  __attribute__ ((used))\n\
#elif defined __GNUC__\n\
#define YYUSED  __attribute__ ((unused))\n\
#else\n\
#define YYUSED\n\
#endif\n\
static const char yyident[] USED = \"yacc: %s\"\n", rel);
    for (; t != MARK && t != ENDFILE; ) {
        int tok_in_line;
        switch (t) {

        case L';':
            t = gettok();
            break;

        case START:
            if ((t = gettok()) != IDENTIFIER) {
                error("bad %%start construction");
                }
            start = chfind(1, tokname);
            t = gettok();
            continue;

        case TYPEDEF:
            tok_in_line = 0;
            if ((t = gettok()) != TYPENAME)
                error("bad syntax in %%type");
            ty = numbval;
            for (;;) {
                t = gettok();
                switch (t) {

                case IDENTIFIER:
            /*
             * The following lines are idented to left.
             */
            tok_in_line = 1;
            if ((t = chfind(1, tokname)) < NTBASE) {
                j = TYPE(toklev[t]);
                if (j != 0 && j != ty) {
                    error("type redeclaration of token %ls",
                    tokset[t].name);
                    }
                else
                    SETTYPE(toklev[t], ty);
            } else {
                j = nontrst[t-NTBASE].tvalue;
                if (j != 0 && j != ty) {
                    error(
                    "type redeclaration of nonterminal %ls",
                        nontrst[t-NTBASE].name);
                    }
                else
                    nontrst[t-NTBASE].tvalue = ty;
                }
            /* FALLTHRU */
            /*
             * End Indentation
             */
                case L',':
                    continue;

                case L';':
                    t = gettok();
                    break;
                default:
                    break;
                    }
                if (!tok_in_line)
                    error(
                    "missing tokens or illegal tokens");
                break;
                }
            continue;

        case UNION:
            /* copy the union declaration to the output */
            cpyunion();
            defunion = 1;
            t = gettok();
            continue;

        case LEFT:
        case BINARY:
        case RIGHT:
            i++;
            /* FALLTHRU */
        case TERM:
            tok_in_line = 0;

            /* nonzero means new prec. and assoc. */
            lev = (t-TERM) | 04;
            ty = 0;

            /* get identifiers so defined */

            t = gettok();
            if (t == TYPENAME) { /* there is a type defined */
                ty = numbval;
                t = gettok();
                }

            for (;;) {
                switch (t) {

                case L',':
                    t = gettok();
                    continue;

                case L';':
                    break;

                case IDENTIFIER:
                    tok_in_line = 1;
                    j = chfind(0, tokname);
                    if (j > NTBASE) {
                        error("%ls is not a token.",
                        tokname);
                    }
                    if (lev & ~04) {
                        if (ASSOC(toklev[j]) & ~04)
                            error(
                "redeclaration of precedence of %ls",
                        tokname);
                        SETASC(toklev[j], lev);
                        SETPLEV(toklev[j], i);
                    } else {
                        if (ASSOC(toklev[j]))
                        warning(1,
                "redeclaration of precedence of %ls.",
                            tokname);
                        SETASC(toklev[j], lev);
                        }
                    if (ty) {
                        if (TYPE(toklev[j]))
                            error(
                        "redeclaration of type of %ls",
                            tokname);
                        SETTYPE(toklev[j], ty);
                        }
                    if ((t = gettok()) == NUMBER) {
                        tokset[j].value = numbval;
                        if (j < ndefout && j > 2) {
                            error(
                "type number of %ls should be defined earlier",
                            tokset[j].name);
                            }
                        if (numbval >= -YYFLAG1) {
                            error(
                "token numbers must be less than %d",
                            -YYFLAG1);
                            }
                        t = gettok();
                        }
                    continue;

                    }
                if (!tok_in_line)
                    error(
                    "missing tokens or illegal tokens");
                break;
                }
            continue;

        case LCURLY:
            defout();
            cpycode();
            t = gettok();
            continue;

        default:
            error("syntax error");

            }

        }

    if (t == ENDFILE) {
        error("unexpected EOF before %%%%");
        }

    /* t is MARK */

    defout();
    end_toks(); /* all tokens dumped - get ready for reductions */

    fprintf(ftable, "\n#ifdef __STDC__\n");
    fprintf(ftable, "#include <stdlib.h>\n");
    fprintf(ftable, "#include <string.h>\n");
    fprintf(ftable, "#define    YYCONST const\n");
    fprintf(ftable, "#else\n");
    fprintf(ftable, "#include <malloc.h>\n");
    fprintf(ftable, "#include <memory.h>\n");
    fprintf(ftable, "#define    YYCONST\n");
    fprintf(ftable, "#endif\n");

    if (sym_prefix[0] != '\0')
        put_prefix_define(sym_prefix);

    fprintf(ftable, "\n#if defined(__cplusplus) || defined(__STDC__)\n");
    fprintf(ftable,
    "\n#if defined(__cplusplus) && defined(__EXTERN_C__)\n");
    fprintf(ftable, "extern \"C\" {\n");
    fprintf(ftable, "#endif\n");
    fprintf(ftable, "#ifndef yyerror\n");
    fprintf(ftable, "#if defined(__cplusplus)\n");
    fprintf(ftable, "   void yyerror(YYCONST char *);\n");
    fprintf(ftable, "#endif\n");
    fprintf(ftable, "#endif\n");
    fprintf(ftable, "#ifndef yylex\n");
    fprintf(ftable, "   int yylex(void);\n");
    fprintf(ftable, "#endif\n");
    fprintf(ftable, "   int yyparse(void);\n");
    fprintf(ftable, "#if defined(__cplusplus) && defined(__EXTERN_C__)\n");
    fprintf(ftable, "}\n");
    fprintf(ftable, "#endif\n");
    fprintf(ftable, "\n#endif\n\n");

    fprintf(ftable, "#define yyclearin yychar = -1\n");
    fprintf(ftable, "#define yyerrok yyerrflag = 0\n");
    fprintf(ftable, "extern int yychar;\nextern int yyerrflag;\n");
    if (!(defunion || ntypes))
        fprintf(ftable,
            "#ifndef YYSTYPE\n#define YYSTYPE int\n#endif\n");
    fprintf(ftable, "YYSTYPE yylval;\n");
    fprintf(ftable, "YYSTYPE yyval;\n");
    fprintf(ftable, "typedef int yytabelem;\n");
    fprintf(ftable,
        "#ifndef YYMAXDEPTH\n#define YYMAXDEPTH 150\n#endif\n");
    fprintf(ftable, "#if YYMAXDEPTH > 0\n");
    fprintf(ftable, "int yy_yys[YYMAXDEPTH], *yys = yy_yys;\n");
    fprintf(ftable, "YYSTYPE yy_yyv[YYMAXDEPTH], *yyv = yy_yyv;\n");
    fprintf(ftable, "#else  /* user does initial allocation */\n");
    fprintf(ftable, "int *yys;\nYYSTYPE *yyv;\n#endif\n");
    fprintf(ftable, "static int yymaxdepth = YYMAXDEPTH;\n");

    prdptr[0] = mem;
    /* added production */
    *mem++ = NTBASE;

    /* if start is 0, we will overwrite with the lhs of the first rule */
    *mem++ = start;
    *mem++ = 1;
    *mem++ = 0;
    prdptr[1] = mem;

    while ((t = gettok()) == LCURLY)
        cpycode();

    if (t != C_IDENTIFIER)
        error("bad syntax on first rule");

    if (!start)
        prdptr[0][1] = chfind(1, tokname);

    /* read rules */

    while (t != MARK && t != ENDFILE) {

        /* process a rule */

        if (t == L'|') {
            rhsfill((wchar_t *)0); /* restart fill of rhs */
            *mem = *prdptr[nprod-1];
            if (++mem >= &tracemem[new_memsize])
                exp_mem(1);
        } else if (t == C_IDENTIFIER) {
            *mem = chfind(1, tokname);
            if (*mem < NTBASE)
                error("illegal nonterminal in grammar rule");
            if (++mem >= &tracemem[new_memsize])
                exp_mem(1);
            lhsfill(tokname);   /* new rule: restart strings */
        } else
            error("illegal rule: missing semicolon or | ?");

        /* read rule body */


        t = gettok();
    more_rule:
        while (t == IDENTIFIER) {
            *mem = chfind(1, tokname);
            if (*mem < NTBASE)
                levprd[nprod] = toklev[*mem]& ~04;
            if (++mem >= &tracemem[new_memsize])
                exp_mem(1);
            rhsfill(tokname);   /* add to rhs string */
            t = gettok();
            }

        if (t == PREC) {
            if (gettok() != IDENTIFIER)
                error("illegal %%prec syntax");
            j = chfind(2, tokname);
            if (j >= NTBASE)
                error("nonterminal %ls illegal after %%prec",
                nontrst[j-NTBASE].name);
            levprd[nprod] = toklev[j] & ~04;
            t = gettok();
            }

        if (t == L'=') {
            had_act[nprod] = 1;
            levprd[nprod] |= ACTFLAG;
            fprintf(faction, "\ncase %d:", nprod);
            cpyact(mem-prdptr[nprod] - 1);
            fprintf(faction, " break;");
            if ((t = gettok()) == IDENTIFIER) {
                /* action within rule... */

                lrprnt();       /* dump lhs, rhs */
                swprintf(actname, sizeof actname,
                        L"$$%d", nprod);
                /*
                 * make it nonterminal
                 */
                j = chfind(1, actname);

                /*
                 * the current rule will become rule
                 * number nprod+1 move the contents down,
                 * and make room for the null
                 */

                if (mem + 2 >= &tracemem[new_memsize])
                    exp_mem(1);
                for (p = mem; p >= prdptr[nprod]; --p)
                    p[2] = *p;
                mem += 2;

                /* enter null production for action */

                p = prdptr[nprod];

                *p++ = j;
                *p++ = -nprod;

                /* update the production information */

                levprd[nprod+1] = levprd[nprod] & ~ACTFLAG;
                levprd[nprod] = ACTFLAG;

                if (++nprod >= nprodsz)
                    exp_prod();
                prdptr[nprod] = p;

                /*
                 * make the action appear in
                 * the original rule
                 */
                *mem++ = j;
                if (mem >= &tracemem[new_memsize])
                    exp_mem(1);
                /* get some more of the rule */
                goto more_rule;
            }
        }
        while (t == L';')
            t = gettok();
        *mem++ = -nprod;
        if (mem >= &tracemem[new_memsize])
            exp_mem(1);

        /* check that default action is reasonable */

        if (ntypes && !(levprd[nprod] & ACTFLAG) &&
                nontrst[*prdptr[nprod]-NTBASE].tvalue) {
            /* no explicit action, LHS has value */
            register int tempty;
            tempty = prdptr[nprod][1];
            if (tempty < 0)
                error("must return a value, since LHS has a type");
            else if (tempty >= NTBASE)
                tempty = nontrst[tempty-NTBASE].tvalue;
            else
                tempty = TYPE(toklev[tempty]);
            if (tempty != nontrst[*prdptr[nprod]-NTBASE].tvalue) {
                error(
                "default action causes potential type clash");
            }
        }

        if (++nprod >= nprodsz)
            exp_prod();
        prdptr[nprod] = mem;
        levprd[nprod] = 0;
        }
    /* end of all rules */

    end_debug();        /* finish fdebug file's input */
    finact();
    if (t == MARK) {
        if (gen_lines)
            fprintf(ftable, "\n# line %d \"%s\"\n",
                lineno, infile);
        while ((c = getwc(finput)) != EOF)
            putwc(c, ftable);
        }
    fclose(finput);
}

static void 
finact(void)
{
    /* finish action routine */
    fclose(faction);
    fprintf(ftable, "# define YYERRCODE %d\n", tokset[2].value);
}

static wchar_t *
cstash(s)
register wchar_t *s;
{
    wchar_t *temp;
    static int used = 0;
    static int used_save = 0;
    static int exp_cname = CNAMSZ;
    int len = wcslen(s);

    /*
     * 2/29/88 -
     * Don't need to expand the table, just allocate new space.
     */
    used_save = used;
    while (len >= (exp_cname - used_save)) {
        exp_cname += CNAMSZ;
        if (!used)
            free(cnames);
        if ((cnames = malloc(sizeof (wchar_t)*exp_cname)) == NULL)
            error("cannot expand string dump");
        cnamp = cnames;
        used = 0;
    }

    temp = cnamp;
    do {
        *cnamp++ = *s;
    } while (*s++);
    used += cnamp - temp;
    return (temp);
}

static int
defin(int t, register wchar_t *s)
{
    /* define s to be a terminal if t=0 or a nonterminal if t=1 */

    register int val = 0;

    if (t) {
        if (++nnonter >= nnontersz)
            exp_nonterm();
        nontrst[nnonter].name = cstash(s);
        return (NTBASE + nnonter);
        }
    /* must be a token */
    if (++ntokens >= ntoksz)
        exp_ntok();
    tokset[ntokens].name = cstash(s);

    /* establish value for token */

    if (s[0] == L' ' && s[2] == 0) { /* single character literal */
        val = findchtok(s[1]);
    } else if (s[0] == L' ' && s[1] == L'\\') { /* escape sequence */
        if (s[3] == 0) { /* single character escape sequence */
            switch (s[2]) {
                /* character which is escaped */
            case L'a':
                warning(1,
        "\\a is ANSI C \"alert\" character");
#if __STDC__ - 1 == 0
                val = L'\a';
                break;
#else
                val = L'\007';
                break;
#endif
            case L'v': val = L'\v'; break;
            case L'n': val = L'\n'; break;
            case L'r': val = L'\r'; break;
            case L'b': val = L'\b'; break;
            case L't': val = L'\t'; break;
            case L'f': val = L'\f'; break;
            case L'\'': val = L'\''; break;
            case L'"': val = L'"'; break;
            case L'?': val = L'?'; break;
            case L'\\': val = L'\\'; break;
            default: error("invalid escape");
            }
        } else if (s[2] <= L'7' && s[2] >= L'0') { /* \nnn sequence */
            int i = 3;
            val = s[2] - L'0';
            while (iswdigit(s[i]) && i <= 4) {
                if (s[i] >= L'0' && s[i] <= L'7')
                    val = val * 8 + s[i] - L'0';
                else
                    error("illegal octal number");
                i++;
            }
            if (s[i] != 0)
                error("illegal \\nnn construction");
            if (val > 255)
                error(
"\\nnn exceed \\377; use \\xnnnnnnnn for wchar_t value of multibyte char");
            if (val == 0 && i >= 4)
                error("'\\000' is illegal");
        } else if (s[2] == L'x') { /* hexadecimal \xnnn sequence */
            int i = 3;
            val = 0;
            warning(1, "\\x is ANSI C hex escape");
            if (iswxdigit(s[i]))
                while (iswxdigit(s[i])) {
                    int tmpval;
                    if (iswdigit(s[i]))
                        tmpval = s[i] - L'0';
                    else if (s[i] >= L'a')
                        tmpval = s[i] - L'a' + 10;
                    else
                        tmpval = s[i] - L'A' + 10;
                    val = 16 * val + tmpval;
                    i++;
                }
            else
                error("illegal hexadecimal number");
            if (s[i] != 0)
                error("illegal \\xnn construction");
#define LWCHAR_MAX  0x7fffffff
            if ((unsigned)val > LWCHAR_MAX)
                error(" \\xnnnnnnnn exceed %#x", LWCHAR_MAX);
            if (val == 0)
                error("'\\x00' is illegal");
            val = findchtok(val);
        } else
            error("invalid escape");
    } else {
        val = extval++;
    }
    tokset[ntokens].value = val;
    toklev[ntokens] = 0;
    return (ntokens);
}

static void 
defout(void)
{
    /* write out the defines (at the end of the declaration section) */

    register int i, c;
    register wchar_t *cp;

    for (i = ndefout; i <= ntokens; ++i) {

        cp = tokset[i].name;
        if (*cp == L' ')    /* literals */
        {
            fprintf(fdebug, "\t\"%ls\",\t%d,\n",
                tokset[i].name + 1, tokset[i].value);
            continue;   /* was cp++ */
        }

        for (; (c = *cp) != 0; ++cp) {
            if (iswlower(c) || iswupper(c) ||
                iswdigit(c) || c == L'_') /* EMPTY */;
            else
                goto nodef;
            }

        fprintf(fdebug, "\t\"%ls\",\t%d,\n", tokset[i].name,
            tokset[i].value);
        fprintf(ftable, "# define %ls %d\n", tokset[i].name,
            tokset[i].value);
        if (fdefine != NULL)
            fprintf(fdefine, "# define %ls %d\n",
                tokset[i].name,
                tokset[i].value);

    nodef:;
    }
    ndefout = ntokens+1;
}

static int
gettok(void)
{
    register int i, base;
    static int peekline; /* number of '\n' seen in lookahead */
    register int c, match, reserve;
begin:
    reserve = 0;
    lineno += peekline;
    peekline = 0;
    c = getwc(finput);
    /*
     * while (c == ' ' || c == '\n' || c == '\t' || c == '\f') {
     */
    while (iswspace(c)) {
        if (c == L'\n')
            ++lineno;
        c = getwc(finput);
    }
    if (c == L'/') { /* skip comment */
        lineno += skipcom();
        goto begin;
    }

    switch (c) {

    case EOF:
        return (ENDFILE);
    case L'{':
        ungetwc(c, finput);
        return (L'=');  /* action ... */
    case L'<':  /* get, and look up, a type name (union member name) */
        i = 0;
        while ((c = getwc(finput)) != L'>' &&
                c != EOF && c != L'\n') {
            tokname[i] = c;
            if (++i >= toksize)
                exp_tokname();
            }
        if (c != L'>')
            error("unterminated < ... > clause");
        tokname[i] = 0;
        if (i == 0)
            error("missing type name in < ... > clause");
        for (i = 1; i <= ntypes; ++i) {
            if (!wcscmp(typeset[i], tokname)) {
                numbval = i;
                return (TYPENAME);
                }
            }
        typeset[numbval = ++ntypes] = cstash(tokname);
        return (TYPENAME);

    case L'"':
    case L'\'':
        match = c;
        tokname[0] = L' ';
        i = 1;
        for (;;) {
            c = getwc(finput);
            if (c == L'\n' || c == EOF)
                error("illegal or missing ' or \"");
            if (c == L'\\') {
                c = getwc(finput);
                tokname[i] = L'\\';
                if (++i >= toksize)
                    exp_tokname();
            } else if (c == match) break;
            tokname[i] = c;
            if (++i >= toksize)
                exp_tokname();
            }
        break;

    case L'%':
    case L'\\':

        switch (c = getwc(finput)) {

        case L'0':  return (TERM);
        case L'<':  return (LEFT);
        case L'2':  return (BINARY);
        case L'>':  return (RIGHT);
        case L'%':
        case L'\\': return (MARK);
        case L'=':  return (PREC);
        case L'{':  return (LCURLY);
        default:    reserve = 1;
            }

    default:

        if (iswdigit(c)) { /* number */
            numbval = c - L'0';
            base = (c == L'0') ? 8 : 10;
            for (c = getwc(finput);
                    iswdigit(c);
                    c = getwc(finput)) {
                numbval = numbval*base + c - L'0';
                }
            ungetwc(c, finput);
            return (NUMBER);
        } else if (iswlower(c) || iswupper(c) ||
                c == L'_' || c == L'.' ||
                c == L'$') {
            i = 0;
            while (iswlower(c) || iswupper(c) ||
                    iswdigit(c) || c == L'_' ||
                    c == L'.' || c == L'$') {
                tokname[i] = c;
                if (reserve && iswupper(c))
                    tokname[i] = towlower(c);
                if (++i >= toksize)
                    exp_tokname();
                c = getwc(finput);
                }
            }
        else
            return (c);

        ungetwc(c, finput);
        }

    tokname[i] = 0;

    if (reserve) { /* find a reserved word */
        if (!wcscmp(tokname, L"term"))
            return (TERM);
        if (!wcscmp(tokname, L"token"))
            return (TERM);
        if (!wcscmp(tokname, L"left"))
            return (LEFT);
        if (!wcscmp(tokname, L"nonassoc"))
            return (BINARY);
        if (!wcscmp(tokname, L"binary"))
            return (BINARY);
        if (!wcscmp(tokname, L"right"))
            return (RIGHT);
        if (!wcscmp(tokname, L"prec"))
            return (PREC);
        if (!wcscmp(tokname, L"start"))
            return (START);
        if (!wcscmp(tokname, L"type"))
            return (TYPEDEF);
        if (!wcscmp(tokname, L"union"))
            return (UNION);
        error("invalid escape, or illegal reserved word: %ls", tokname);
        }

    /* look ahead to distinguish IDENTIFIER from C_IDENTIFIER */

    c = getwc(finput);
    /*
     * while (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '/')
     * {
     */
    while (iswspace(c) || c == L'/') {
        if (c == L'\n') {
            ++peekline;
        } else if (c == L'/') { /* look for comments */
            peekline += skipcom();
            }
        c = getwc(finput);
        }
    if (c == L':')
        return (C_IDENTIFIER);
    ungetwc(c, finput);
    return (IDENTIFIER);
}

static int
fdtype(int t)
{
    /* determine the type of a symbol */
    register int v;
    if (t >= NTBASE)
        v = nontrst[t-NTBASE].tvalue;
    else
        v = TYPE(toklev[t]);
    if (v <= 0)
        error("must specify type for %ls",
            (t >= NTBASE) ? nontrst[t-NTBASE].name:
            tokset[t].name);
    return (v);
}

static int
chfind(int t, register wchar_t *s)
{
    int i;

    if (s[0] == ' ')
        t = 0;
    TLOOP(i) {
        if (!wcscmp(s, tokset[i].name)) {
            return (i);
        }
    }
    NTLOOP(i) {
        if (!wcscmp(s, nontrst[i].name)) {
            return (i + NTBASE);
        }
    }
    /* cannot find name */
    if (t > 1)
        error("%ls should have been defined earlier", s);
    return (defin(t, s));
}

static void 
cpyunion(void)
{
    /*
     * copy the union declaration to the output,
     * and the define file if present
     */
    int level, c;
    if (gen_lines)
        fprintf(ftable, "\n# line %d \"%s\"\n", lineno, infile);
    fprintf(ftable, "typedef union\n");
    if (fdefine)
        fprintf(fdefine, "\ntypedef union\n");
    fprintf(ftable, "#ifdef __cplusplus\n\tYYSTYPE\n#endif\n");
    if (fdefine)
        fprintf(fdefine, "#ifdef __cplusplus\n\tYYSTYPE\n#endif\n");

    level = 0;
    for (;;) {
        if ((c = getwc(finput)) == EOF)
            error("EOF encountered while processing %%union");
        putwc(c, ftable);
        if (fdefine)
            putwc(c, fdefine);

        switch (c) {

        case L'\n':
            ++lineno;
            break;

        case L'{':
            ++level;
            break;

        case L'}':
            --level;
            if (level == 0) { /* we are finished copying */
                fprintf(ftable, " YYSTYPE;\n");
                if (fdefine)
                    fprintf(fdefine,
                    " YYSTYPE;\nextern YYSTYPE yylval;\n");
                return;
                }
            }
        }
}

static void 
cpycode(void)
{
    /* copies code between \{ and \} */

    int c;
    c = getwc(finput);
    if (c == L'\n') {
        c = getwc(finput);
        lineno++;
        }
    if (gen_lines)
        fprintf(ftable, "\n# line %d \"%s\"\n", lineno, infile);
    while (c != EOF) {
        if (c == L'\\') {
            if ((c = getwc(finput)) == L'}')
                return;
            else
                putwc(L'\\', ftable);
        } else if (c == L'%') {
            if ((c = getwc(finput)) == L'}')
                return;
            else
                putwc(L'%', ftable);
        }
        putwc(c, ftable);
        if (c == L'\n')
            ++lineno;
        c = getwc(finput);
        }
    error("eof before %%}");
}

static int
skipcom(void)
{
    /* skip over comments */
    register int c, i = 0;  /* i is the number of lines skipped */

    /* skipcom is called after reading a / */

    if (getwc(finput) != L'*')
        error("illegal comment");
    c = getwc(finput);
    while (c != EOF) {
        while (c == L'*') {
            if ((c = getwc(finput)) == L'/')
                return (i);
            }
        if (c == L'\n')
            ++i;
        c = getwc(finput);
        }
    error("EOF inside comment");
    /* NOTREACHED */
    return 0;
}

static void 
cpyact(int offset)
{
    /* copy C action to the next ; or closing } */
    int brac, c, match, i, t, j, s, tok, argument, m;
    wchar_t id_name[NAMESIZE+1];
    int id_idx = 0;

    if (gen_lines) {
        fprintf(faction, "\n# line %d \"%s\"\n", lineno, infile);
        act_lines++;
    }
    brac = 0;
    id_name[0] = 0;
loop:
    c = getwc(finput);
swt:
    switch (c) {
    case L';':
        if (brac == 0) {
            putwc(c, faction);
            return;
        }
        goto lcopy;
    case L'{':
        brac++;
        goto lcopy;
    case L'$':
        s = 1;
        tok = -1;
        argument = 1;
        while ((c = getwc(finput)) == L' ' || c == L'\t') /* EMPTY */;
        if (c == L'<') { /* type description */
            ungetwc(c, finput);
            if (gettok() != TYPENAME)
                error("bad syntax on $<ident> clause");
            tok = numbval;
            c = getwc(finput);
        }
        if (c == L'$') {
            fprintf(faction, "yyval");
            if (ntypes) { /* put out the proper tag... */
                if (tok < 0)
                    tok = fdtype(*prdptr[nprod]);
                fprintf(faction, ".%ls", typeset[tok]);
            }
            goto loop;
        }
        if (iswalpha(c)) {
            int same = 0;
            int id_sw = 0;
            ungetwc(c, finput);
            if (gettok() != IDENTIFIER)
                error("bad action format");
            /*
             * Save the number of non-terminal
             */
            id_sw = nnonter;
            t = chfind(1, tokname);
            /*
             * Check if the identifier is added as a non-terminal
             */
            if (id_sw != nnonter)
                id_sw = 1;
            else
                id_sw = 0;
            while ((c = getwc(finput)) == L' ' ||
                c == L'\t') /* EMPTY */;
            if (c == L'#') {
                while ((c = getwc(finput)) == L' ' ||
                    c == L'\t') /* EMPTY */;
                if (iswdigit(c)) {
                    m = 0;
                    while (iswdigit(c)) {
                        m = m*10+c-L'0';
                        c = getwc(finput);
                    }
                    argument = m;
                } else
                    error("illegal character \"#\"");
            }
            if (argument < 1)
                error("illegal action argument no.");
            for (i = 1; i <= offset; ++i)
                if (prdptr[nprod][i] == t)
                    if (++same == argument) {
                        fprintf(faction,
                            "yypvt[-%d]", offset-i);
                        if (ntypes) {
                            if (tok < 0)
                                tok =
                                /* CSTYLED */
                                fdtype(prdptr[nprod][i]);
                            fprintf(faction,
                            ".%ls", typeset[tok]);
                        }
                        goto swt;
                    }
            /*
             * This used to be handled as error.
             * Treat this as a valid C statement.
             * (Likely id with $ in.)
             * If non-terminal is added, remove it from the list.
             */
            fprintf(faction, "$%ls", tokname);
            warning(1, 
    "Illegal character '$' in Ansi C symbol: %ls$%ls.",
                id_name, tokname);

            if (id_sw == 1)
                --nnonter;
            goto swt;
        }
        if (c == '-') {
            s = -s;
            c = getwc(finput);
        }
        if (iswdigit(c)) {
            j = 0;
            while (iswdigit(c)) {
                j = j*10 + c - L'0';
                c = getwc(finput);
            }
            j = j*s - offset;
            if (j > 0) {
                error("Illegal use of $%d", j + offset);
            }
            fprintf(faction, "yypvt[-%d]", -j);
            if (ntypes) { /* put out the proper tag */
                if (j + offset <= 0 && tok < 0)
                    error("must specify type of $%d",
                    j + offset);
                if (tok < 0)
                    tok = fdtype(prdptr[nprod][j+offset]);
                fprintf(faction,
                    ".%ls", typeset[tok]);
            }
            goto swt;
        }
        putwc(L'$', faction);
        if (s < 0)
            putwc(L'-', faction);
        goto swt;
    case L'}':
        if (--brac)
            goto lcopy;
        putwc(c, faction);
        return;
    case L'/':  /* look for comments */
        putwc(c, faction);
        c = getwc(finput);
        if (c != L'*')
            goto swt;
        /* it really is a comment */
        putwc(c, faction);
        c = getwc(finput);
        while (c != EOF) {
            while (c == L'*') {
                putwc(c, faction);
                if ((c = getwc(finput)) == L'/')
                    goto lcopy;
            }
            putwc(c, faction);
            if (c == L'\n')
                ++lineno;
            c = getwc(finput);
        }
        error("EOF inside comment");
        /* FALLTHRU */
    case L'\'': /* character constant */
    case L'"':  /* character string */
        match = c;
        putwc(c, faction);
        while ((c = getwc(finput)) != EOF) {
            if (c == L'\\') {
                putwc(c, faction);
                c = getwc(finput);
                if (c == L'\n')
                    ++lineno;
            } else if (c == match)
                goto lcopy;
            else if (c == L'\n')
                error("newline in string or char. const.");
            putwc(c, faction);
        }
        error("EOF in string or character constant");
        /* FALLTHRU */
    case EOF:
        error("action does not terminate");
        /* FALLTHRU */
    case L'\n':
        ++lineno;
        goto lcopy;
    }
lcopy:
    putwc(c, faction);
    /*
     * Save the possible identifier name.
     * Used to print out a warning message.
     */
    if (id_idx >= NAMESIZE) {
        /*
         * Error. Silently ignore.
         */
        ;
    }
    /*
     * If c has a possibility to be a
     * part of identifier, save it.
     */
    else if (iswalnum(c) || c == L'_') {
        id_name[id_idx++] = c;
        id_name[id_idx] = 0;
    } else {
        id_idx = 0;
        id_name[id_idx] = 0;
    }
    goto loop;
}

static void
lhsfill(s)  /* new rule, dump old (if exists), restart strings */
wchar_t *s;
{
    static int lhs_len = LHS_TEXT_LEN;
    int s_lhs = wcslen(s);
    if (s_lhs >= lhs_len) {
        lhs_len = s_lhs + 2;
        lhstext = realloc(lhstext, sizeof (wchar_t)*lhs_len);
        if (lhstext == NULL)
            error("couldn't expanded LHS length");
    }
    rhsfill(NULL);
    wcscpy(lhstext, s); /* don't worry about too long of a name */
}

static void
rhsfill(wchar_t *s) /* either name or 0 */
{
    static wchar_t *loc;    /* next free location in rhstext */
    static int rhs_len = RHS_TEXT_LEN;
    static int used = 0;
    int s_rhs = (s == NULL ? 0 : wcslen(s));
    register wchar_t *p;

    if (!s) /* print out and erase old text */
    {
        if (*lhstext)       /* there was an old rule - dump it */
            lrprnt();
        (loc = rhstext)[0] = 0;
        return;
    }
    /* add to stuff in rhstext */
    p = s;

    used = loc - rhstext;
    if ((s_rhs + 3) >= (rhs_len - used)) {
        static wchar_t *textbase;
        textbase = rhstext;
        rhs_len += s_rhs + RHS_TEXT_LEN;
        rhstext = realloc(rhstext, sizeof (wchar_t)*rhs_len);
        if (rhstext == NULL)
            error("couldn't expanded RHS length");
        loc = loc - textbase + rhstext;
    }

    *loc++ = L' ';
    if (*s == L' ') /* special quoted symbol */
    {
        *loc++ = L'\''; /* add first quote */
        p++;
    }
    while (*loc = *p++)
        if (loc++ > &rhstext[ RHS_TEXT_LEN ] - 3)
            break;

    if (*s == L' ')
        *loc++ = L'\'';
    *loc = 0;       /* terminate the string */
}

static void 
lrprnt (void)   /* print out the left and right hand sides */
{
    wchar_t *rhs;
    wchar_t *m_rhs = NULL;

    if (!*rhstext)      /* empty rhs - print usual comment */
        rhs = L" /* empty */";
    else {
        int idx1; /* tmp idx used to find if there are d_quotes */
        int idx2; /* tmp idx used to generate escaped string */
        wchar_t *p;
        /*
         * Check if there are any double quote in RHS.
         */
        for (idx1 = 0; rhstext[idx1] != 0; idx1++) {
            if (rhstext[idx1] == L'"') {
                /*
                 * A double quote is found.
                 */
                idx2 = wcslen(rhstext)*2;
                p = m_rhs = malloc((idx2 + 1)*sizeof (wchar_t));
                if (m_rhs == NULL)
                    error(
                    "Couldn't allocate memory for RHS.");
                /*
                 * Copy string
                 */
                for (idx2 = 0; rhstext[idx2] != 0; idx2++) {
                    /*
                     * Check if this quote is escaped or not
                     */
                    if (rhstext[idx2] == L'"') {
                        int tmp_l = idx2-1;
                        int cnt = 0;
                        while (tmp_l >= 0 &&
                        rhstext[tmp_l] == '\\') {
                            cnt++;
                            tmp_l--;
                        }
                        /*
                         * If quote is not escaped,
                         * then escape it.
                         */
                        if (cnt%2 == 0)
                            *p++ = L'\\';
                    }
                    *p++ = rhstext[idx2];
                }
                *p = 0;
                /*
                 * Break from the loop
                 */
                break;
            }
        }
        if (m_rhs == NULL)
            rhs = rhstext;
        else
            rhs = m_rhs;
    }
    fprintf(fdebug, "\t\"%ls :%ls\",\n", lhstext, rhs);
    if (m_rhs)
        free(m_rhs);
}


static void 
beg_debug (void)    /* dump initial sequence for fdebug file */
{
    fprintf(fdebug, "typedef struct\n");
    fprintf(fdebug, "#ifdef __cplusplus\n\tyytoktype\n");
    fprintf(fdebug, "#endif\n{\n");
    fprintf(fdebug, "#ifdef __cplusplus\nconst\n#endif\n");
    fprintf(fdebug, "char *t_name; int t_val; } yytoktype;\n");
    fprintf(fdebug, "#ifndef YYDEBUG\n#\tdefine YYDEBUG\t%d", gen_testing);
    fprintf(fdebug, "\t/*%sallow debugging */\n#endif\n\n",
        gen_testing ? " " : " don't ");
    fprintf(fdebug, "#if YYDEBUG\n\nyytoktype yytoks[] =\n{\n");
}


static void 
end_toks (void) /* finish yytoks array, get ready for yyred's strings */
{
    fprintf(fdebug, "\t\"-unknown-\",\t-1\t/* ends search */\n");
    fprintf(fdebug, "};\n\n");
    fprintf(fdebug,
        "#ifdef __cplusplus\nconst\n#endif\n");
    fprintf(fdebug, "char * yyreds[] =\n{\n");
    fprintf(fdebug, "\t\"-no such reduction-\",\n");
}


static void 
end_debug (void)    /* finish yyred array, close file */
{
    lrprnt();       /* dump last lhs, rhs */
    fprintf(fdebug, "};\n#endif /* YYDEBUG */\n");
    fclose(fdebug);
}


/*
 * 2/29/88 -
 * The normal length for token sizes is NAMESIZE - If a token is
 * seen that has a longer length, expand "tokname" by NAMESIZE.
 */
static void 
exp_tokname(void)
{
    toksize += NAMESIZE;
    tokname = realloc(tokname, sizeof (wchar_t) * toksize);
}


/*
 * 2/29/88 -
 *
 */
static void 
exp_prod(void)
{
    int i;
    nprodsz += NPROD;

    prdptr = realloc(prdptr, sizeof (int *) * (nprodsz+2));
    levprd  = realloc(levprd, sizeof (int) * (nprodsz+2));
    had_act = realloc(had_act, sizeof (wchar_t) * (nprodsz+2));
    for (i = nprodsz-NPROD; i < nprodsz+2; ++i)
        had_act[i] = 0;

    if ((*prdptr == NULL) || (levprd == NULL) || (had_act == NULL))
        error("couldn't expand productions");
}

/*
 * 2/29/88 -
 * Expand the number of terminals.  Initially there are NTERMS;
 * each time space runs out, the size is increased by NTERMS.
 * The total size, however, cannot exceed MAXTERMS because of
 * the way LOOKSETS(struct looksets) is set up.
 * Tables affected:
 *  tokset, toklev : increased to ntoksz
 *
 *  tables with initial dimensions of TEMPSIZE must be changed if
 *  (ntoksz + NNONTERM) >= TEMPSIZE : temp1[]
 */
static void 
exp_ntok(void)
{
    ntoksz += NTERMS;

    tokset = realloc(tokset, sizeof (TOKSYMB) * ntoksz);
    toklev = realloc(toklev, sizeof (int) * ntoksz);

    if ((tokset == NULL) || (toklev == NULL))
        error("couldn't expand NTERMS");
}


static void 
exp_nonterm(void)
{
    nnontersz += NNONTERM;

    nontrst = realloc(nontrst, sizeof (TOKSYMB) * nnontersz);
    if (nontrst == NULL)
        error("couldn't expand NNONTERM");
}

void 
exp_mem(int flag)
{
    int i;
    static int *membase;
    new_memsize += MEMSIZE;

    membase = tracemem;
    tracemem = realloc(tracemem, sizeof (int) * new_memsize);
    if (tracemem == NULL)
        error("couldn't expand mem table");
    if (flag) {
        for (i = 0; i <= nprod; ++i)
            prdptr[i] = prdptr[i] - membase + tracemem;
        mem = mem - membase + tracemem;
    } else {
        size += MEMSIZE;
        temp1 = realloc(temp1, sizeof (int)*size);
        optimmem = optimmem - membase + tracemem;
    }
}

static int 
findchtok(int chlit)
/*
 * findchtok(chlit) returns the token number for a character literal
 * chlit that is "bigger" than 255 -- the max char value that the
 * original yacc was build for.  This yacc treate them as though
 * an ordinary token.
 */
{
    int i;

    if (chlit < 0xff)
        return (chlit); /* single-byte char */
    for (i = 0; i < nmbchars; ++i) {
        if (mbchars->character == chlit)
            return (mbchars->tvalue);
    }

    /* Not found.  Register it! */
    if (++nmbchars > nmbcharsz) { /* Make sure there's enough space */
        nmbcharsz += NMBCHARSZ;
        mbchars = realloc(mbchars, sizeof (MBCLIT)*nmbcharsz);
        if (mbchars == NULL)
            error("too many character literals");
    }
    mbchars[nmbchars-1].character = chlit;
    return (mbchars[nmbchars-1].tvalue = extval++);
    /* Return the newly assigned token. */
}

/*
 * When -p is specified, symbol prefix for
 *  yy{parse, lex, error}(),
 *  yy{lval, val, char, debug, errflag, nerrs}
 * are defined to the specified name.
 */
static void
put_prefix_define(char *pre)
{
    char *syms[] = {
        /* Functions */
        "parse",
        "lex",
        "error",
        /* Variables */
        "lval",
        "val",
        "char",
        "debug",
        "errflag",
        "nerrs",
        NULL};
    int i;

    for (i = 0; syms[i]; i++)
        fprintf(ftable, "#define\tyy%s\t%s%s\n",
            syms[i], pre, syms[i]);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y3.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y3.c used in: Step 989, Step 994
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1990 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */

/*  from OpenSolaris "y3.c  6.17    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)y3.c  1.5 (gritter) 11/26/05
 */

#include "dextern"

static void go2gen(int);
static void precftn(int, int, int);
static void wract(int);
static void wrstate(int);
static void wdef(wchar_t *, int);
#ifndef NOLIBW
static void wrmbchars(void);
#endif /* !NOLIBW */
    /* important local variables */
static int lastred; /* number of the last reduction of a state */
int *defact;
extern int *toklev;
extern int cwp;

/* print the output for the states */
void 
output(void)
{
    int i, k, c;
    register WSET *u, *v;

    fprintf(ftable, "static YYCONST yytabelem yyexca[] ={\n");

    SLOOP(i) { /* output the stuff for state i */
        nolook = !(tystate[i] == MUSTLOOKAHEAD);
        closure(i);
        /* output actions */
        nolook = 1;
        aryfil(temp1, ntoksz+nnontersz+1, 0);
        WSLOOP(wsets, u) {
            c = *(u->pitem);
            if (c > 1 && c < NTBASE && temp1[c] == 0) {
                WSLOOP(u, v) {
                    if (c == *(v->pitem))
                        putitem(v->pitem + 1,
                            (LOOKSETS *)0);
                }
                temp1[c] = state(c);
            } else if (c > NTBASE &&
                temp1[(c -= NTBASE) + ntokens] == 0) {
                temp1[c + ntokens] = amem[indgo[i] + c];
            }
        }
        if (i == 1)
            temp1[1] = ACCEPTCODE;
        /* now, we have the shifts; look at the reductions */
        lastred = 0;
        WSLOOP(wsets, u) {
            c = *(u->pitem);
            if (c <= 0) { /* reduction */
                lastred = -c;
                TLOOP(k) {
                    if (BIT(u->ws.lset, k)) {
                        if (temp1[k] == 0)
                            temp1[k] = c;
                        else if (temp1[k] < 0) {
                            /*
                             * reduce/reduce
                             * conflict
                             */
                            if (foutput != NULL)
                    fprintf(foutput,
                "\n%d: reduce/reduce conflict"
                " (red'ns %d and %d ) on %ls",
                            i, -temp1[k],
                            lastred, symnam(k));
                            if (-temp1[k] > lastred)
                            temp1[k] = -lastred;
                            ++zzrrconf;
                        } else
                            /*
                             * potentia
                             * shift/reduce
                             * conflict.
                             */
                            precftn(lastred, k, i);
                    }
                }
            }
        }
        wract(i);
    }

    fprintf(ftable, "\t};\n");
    wdef(L"YYNPROD", nprod);
#ifndef NOLIBW
    if (nmbchars > 0) {
        wrmbchars();
    }
#endif /* !NOLIBW */
}

static int pkdebug = 0;
int 
apack(int *p, int n)
{
    /* pack state i from temp1 into amem */
    int off;
    register int *pp, *qq;
    int *q, /**r,*/ *rr;
    int diff;

    /*
     * we don't need to worry about checking because we
     * we will only look up entries known to be there...
     */

    /* eliminate leading and trailing 0's */

    q = p + n;
    for (pp = p, off = 0; *pp == 0 && pp <= q; ++pp, --off)
        /* EMPTY */;
    if (pp > q)
        return (0);  /* no actions */
    p = pp;

    /* now, find a place for the elements from p to q, inclusive */
    /* for( rr=amem; rr<=r; ++rr,++off ){ */  /* try rr */
    rr = amem;
    for (; ; ++rr, ++off) {
        while (rr >= &amem[new_actsize-1])
            exp_act(&rr);
        qq = rr;
        for (pp = p; pp <= q; ++pp, ++qq) {
            if (*pp) {
                diff = qq - rr;
                while (qq >= &amem[new_actsize-1]) {
                    exp_act(&rr);
                    qq = diff + rr;
                }
                if (*pp != *qq && *qq != 0)
                    goto nextk;
            }
        }

        /* we have found an acceptable k */

        if (pkdebug && foutput != NULL)
            fprintf(foutput, "off = %d, k = %d\n", off, rr-amem);

        qq = rr;
        for (pp = p; pp <= q; ++pp, ++qq) {
            if (*pp) {
                diff = qq - rr;
                while (qq >= &amem[new_actsize-1]) {
                    exp_act(&rr);
                    qq = diff + rr;
                }
                if (qq > memp)
                    memp = qq;
                *qq = *pp;
            }
        }
        if (pkdebug && foutput != NULL) {
            for (pp = amem; pp <= memp; pp += 10) {
                fprintf(foutput, "\t");
                for (qq = pp; qq <= pp + 9; ++qq)
                    fprintf(foutput, "%d ", *qq);
                fprintf(foutput, "\n");
            }
        }
        return (off);
        nextk:;
    }
    /* error("no space in action table" ); */
    /* NOTREACHED */
}

void 
go2out(void)
{
    /* output the gotos for the nontermninals */
    int i, j, k, best, count, cbest, times;

    fprintf(ftemp, "$\n");  /* mark begining of gotos */

    for (i = 1; i <= nnonter; ++i) {
        go2gen(i);
        /* find the best one to make default */
        best = -1;
        times = 0;
        for (j = 0; j < nstate; ++j) { /* is j the most frequent */
            if (tystate[j] == 0)
                continue;
            if (tystate[j] == best)
                continue;
            /* is tystate[j] the most frequent */
            count = 0;
            cbest = tystate[j];
            for (k = j; k < nstate; ++k)
                if (tystate[k] == cbest)
                    ++count;
            if (count > times) {
                best = cbest;
                times = count;
            }
        }

        /* best is now the default entry */
        zzgobest += (times-1);
        for (j = 0; j < nstate; ++j) {
            if (tystate[j] != 0 && tystate[j] != best) {
                fprintf(ftemp, "%d,%d,", j, tystate[j]);
                zzgoent += 1;
            }
        }

        /* now, the default */

        zzgoent += 1;
        fprintf(ftemp, "%d\n", best);

    }
}

static int g2debug = 0;
static void 
go2gen(int c)
{
    /* output the gotos for nonterminal c */
    int i, work, cc;
    ITEM *p, *q;

    /* first, find nonterminals with gotos on c */
    aryfil(temp1, nnonter + 1, 0);
    temp1[c] = 1;

    work = 1;
    while (work) {
        work = 0;
        PLOOP(0, i) {
            if ((cc = prdptr[i][1] - NTBASE) >= 0) {
                /* cc is a nonterminal */
                if (temp1[cc] != 0) {
                    /*
                     * cc has a goto on c
                     * thus, the left side of
                     * production i does too.
                     */
                    cc = *prdptr[i] - NTBASE;
                    if (temp1[cc] == 0) {
                        work = 1;
                        temp1[cc] = 1;
                    }
                }
            }
        }
    }

    /* now, we have temp1[c] = 1 if a goto on c in closure of cc */

    if (g2debug && foutput != NULL) {
        fprintf(foutput, "%ls: gotos on ", nontrst[c].name);
        NTLOOP(i) if (temp1[i])
            fprintf(foutput, "%ls ", nontrst[i].name);
        fprintf(foutput, "\n");
    }

    /* now, go through and put gotos into tystate */
    aryfil(tystate, nstate, 0);
    SLOOP(i) {
        ITMLOOP(i, p, q) {
            if ((cc = *p->pitem) >= NTBASE) {
                if (temp1[cc -= NTBASE]) {
                    /* goto on c is possible */
                    tystate[i] = amem[indgo[i] + c];
                    break;
                }
            }
        }
    }
}

/* decide a shift/reduce conflict by precedence.  */
static void 
precftn(int r, int t, int s)
{

    /*
     * r is a rule number, t a token number
     * the conflict is in state s
     * temp1[t] is changed to reflect the action
     */

    int lp, lt, action;

    lp = levprd[r];
    lt = toklev[t];
    if (PLEVEL(lt) == 0 || PLEVEL(lp) == 0) {
        /* conflict */
        if (foutput != NULL)
            fprintf(foutput,
                "\n%d: shift/reduce conflict"
                " (shift %d, red'n %d) on %ls",
                s, temp1[t], r, symnam(t));
        ++zzsrconf;
        return;
    }
    if (PLEVEL(lt) == PLEVEL(lp))
        action = ASSOC(lt) & ~04;
    else if (PLEVEL(lt) > PLEVEL(lp))
        action = RASC;  /* shift */
    else
        action = LASC;  /* reduce */

    switch (action) {
    case BASC:  /* error action */
        temp1[t] = ERRCODE;
        return;
    case LASC:  /* reduce */
        temp1[t] = -r;
        return;
    }
}

static void 
wract(int i)
{
    /* output state i */
    /* temp1 has the actions, lastred the default */
    int p, p0, p1;
    int ntimes, tred, count, j;
    int flag;

    /* find the best choice for lastred */

    lastred = 0;
    ntimes = 0;
    TLOOP(j) {
        if (temp1[j] >= 0)
            continue;
        if (temp1[j] + lastred == 0)
            continue;
        /* count the number of appearances of temp1[j] */
        count = 0;
        tred = -temp1[j];
        levprd[tred] |= REDFLAG;
        TLOOP(p) {
            if (temp1[p] + tred == 0)
                ++count;
        }
        if (count > ntimes) {
            lastred = tred;
            ntimes = count;
        }
    }

    /*
     * for error recovery, arrange that, if there is a shift on the
     * error recovery token, `error', that the default be the error action
     */
    if (temp1[2] > 0)
        lastred = 0;

    /* clear out entries in temp1 which equal lastred */
    TLOOP(p) {
        if (temp1[p] + lastred == 0)
            temp1[p] = 0;
    }

    wrstate(i);
    defact[i] = lastred;

    flag = 0;
    TLOOP(p0) {
        if ((p1 = temp1[p0]) != 0) {
            if (p1 < 0) {
                p1 = -p1;
                goto exc;
            } else if (p1 == ACCEPTCODE) {
                p1 = -1;
                goto exc;
            } else if (p1 == ERRCODE) {
                p1 = 0;
                goto exc;
            exc:
                if (flag++ == 0)
                    fprintf(ftable, "-1, %d,\n", i);
                fprintf(ftable,
                    "\t%d, %d,\n", tokset[p0].value, p1);
                ++zzexcp;
            } else {
                fprintf(ftemp,
                    "%d,%d,", tokset[p0].value, p1);
                ++zzacent;
            }
        }
    }
    if (flag) {
        defact[i] = -2;
        fprintf(ftable, "\t-2, %d,\n", lastred);
    }
    fprintf(ftemp, "\n");
}

static void 
wrstate(int i)
{
    /* writes state i */
    register int j0, j1;
    register ITEM *pp, *qq;
    register WSET *u;

    if (foutput == NULL)
        return;
    fprintf(foutput, "\nstate %d\n", i);
    ITMLOOP(i, pp, qq) {
        fprintf(foutput, "\t%ls\n", writem(pp->pitem));
    }
    if (tystate[i] == MUSTLOOKAHEAD) {
        /* print out empty productions in closure */
        WSLOOP(wsets + (pstate[i + 1] - pstate[i]), u) {
            if (*(u->pitem) < 0)
                fprintf(foutput, "\t%ls\n", writem(u->pitem));
        }
    }

    /* check for state equal to another */
    TLOOP(j0) if ((j1 = temp1[j0]) != 0) {
        fprintf(foutput, "\n\t%ls  ", symnam(j0));
        if (j1 > 0) { /* shift, error, or accept */
            if (j1 == ACCEPTCODE)
                fprintf(foutput,  "accept");
            else if (j1 == ERRCODE)
                fprintf(foutput, "error");
            else
                fprintf(foutput, "shift %d", j1);
        }
        else
            fprintf(foutput, "reduce %d", -j1);
    }

    /* output the final production */
    if (lastred)
        fprintf(foutput, "\n\t.  reduce %d\n\n", lastred);
    else
        fprintf(foutput, "\n\t.  error\n\n");

    /* now, output nonterminal actions */
    j1 = ntokens;
    for (j0 = 1; j0 <= nnonter; ++j0) {
        if (temp1[++j1])
            fprintf(foutput, "\t%ls  goto %d\n",
                symnam(j0 + NTBASE), temp1[j1]);
    }
}

static void
wdef(wchar_t *s, int n)
{
    /* output a definition of s to the value n */
    fprintf(ftable, "# define %ls %d\n", s, n);
}

void
warray(wchar_t *s, int *v, int n)
{
    register int i;
    fprintf(ftable, "static YYCONST yytabelem %ls[]={\n", s);
    for (i = 0; i < n; ) {
        if (i % 10 == 0)
            fprintf(ftable, "\n");
        fprintf(ftable, "%6d", v[i]);
        if (++i == n)
            fprintf(ftable, " };\n");
        else
            fprintf(ftable, ",");
    }
}

void 
hideprod(void)
{
    /*
     * in order to free up the mem and amem arrays for the optimizer,
     * and still be able to output yyr1, etc., after the sizes of
     * the action array is known, we hide the nonterminals
     * derived by productions in levprd.
     */

    register int i, j;

    j = 0;
    levprd[0] = 0;
    PLOOP(1, i) {
        if (!(levprd[i] & REDFLAG)) {
            ++j;
            if (foutput != NULL) {
                fprintf(foutput,
                    "Rule not reduced:   %ls\n",
                    writem(prdptr[i]));
            }
        }
        levprd[i] = *prdptr[i] - NTBASE;
    }
    if (j)
        fprintf(stderr, "%d rules never reduced\n", j);
}


#ifndef NOLIBW
static int 
cmpmbchars(MBCLIT *p, MBCLIT *q)
{
    /* Compare two MBLITs. */
    return ((p->character) - (q->character));
}

static void 
wrmbchars(void)
{
    int i;
    wdef(L"YYNMBCHARS", nmbchars);
    qsort(mbchars, nmbchars, sizeof (*mbchars),
        (int (*)(const void *, const void *))cmpmbchars);
    fprintf(ftable,
        "static struct{\n\twchar_t character;"
        "\n\tint tvalue;\n}yymbchars[YYNMBCHARS]={\n");
    for (i = 0; i < nmbchars; ++i) {
        fprintf(ftable, "\t{%#x,%d}",
            (int)mbchars[i].character, mbchars[i].tvalue);
        if (i < nmbchars - 1) {
            /* Not the last. */
            fprintf(ftable, ",\n");
        }
    }
    fprintf(ftable, "\n};\n");
}
#endif /* !NOLIBW */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y4.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/y4.c used in: Step 989, Step 995
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1990 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */

/*  from OpenSolaris "y4.c  6.15    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)y4.c  1.5 (gritter) 11/26/05
 */

#include "dextern"
#include <wchar.h>
#include <unistd.h>
#define NOMORE -1000

static void gin(int);
static void stin(int);
static void osummary(void);
static void aoutput(void);
static void arout(wchar_t *, int *, int);
static int nxti(void);
static int gtnm(void);

static int *ggreed;
static int *pgo;
static int *yypgo;

static int maxspr = 0;  /* maximum spread of any entry */
static int maxoff = 0;  /* maximum offset into an array */
int *optimmem;
static int *maxa;

static int nxdb = 0;
static int adb = 0;

void 
callopt(void)
{
    register int i, *p, j, k, *q;

    ggreed = malloc(sizeof (int) * size);
    pgo = malloc(sizeof (int) * size);
    yypgo = &nontrst[0].tvalue;

    /* read the arrays from tempfile and set parameters */

    if ((finput = fopen(TEMPNAME, "r")) == NULL)
        error("optimizer cannot open tempfile");

    optimmem = tracemem;
    pgo[0] = 0;
    temp1[0] = 0;
    nstate = 0;
    nnonter = 0;
    for (;;) {
        switch (gtnm()) {

        case L'\n':
            temp1[++nstate] = (--optimmem) - tracemem;
            /* FALLTHRU */

        case L',':
            continue;

        case L'$':
            break;

        default:
            error("bad tempfile");
        }
        break;
    }

    temp1[nstate] = yypgo[0] = (--optimmem) - tracemem;

    for (;;) {
        switch (gtnm()) {

        case L'\n':
            yypgo[++nnonter] = optimmem-tracemem;
            /* FALLTHRU */
        case L',':
            continue;

        case EOF:
            break;

        default:
            error("bad tempfile");
        }
        break;
    }

    yypgo[nnonter--] = (--optimmem) - tracemem;

    for (i = 0; i < nstate; ++i) {
        k = 32000000;
        j = 0;
        q = tracemem + temp1[i+1];
        for (p = tracemem + temp1[i]; p < q; p += 2) {
            if (*p > j)
                j = *p;
            if (*p < k)
                k = *p;
        }
        if (k <= j) {
            /*
             * nontrivial situation
             * temporarily, kill this for compatibility
             */
            /* j -= k;  j is now the range */
            if (k > maxoff)
                maxoff = k;
        }
        tystate[i] = (temp1[i+1] - temp1[i]) + 2*j;
        if (j > maxspr)
            maxspr = j;
    }

    /* initialize ggreed table */
    for (i = 1; i <= nnonter; ++i) {
        ggreed[i] = 1;
        j = 0;
        /* minimum entry index is always 0 */
        q = tracemem + yypgo[i+1] -1;
        for (p = tracemem + yypgo[i]; p < q; p += 2) {
            ggreed[i] += 2;
            if (*p > j)
                j = *p;
        }
        ggreed[i] = ggreed[i] + 2*j;
        if (j > maxoff)
            maxoff = j;
    }

    /* now, prepare to put the shift actions into the amem array */
    for (i = 0; i < new_actsize; ++i)
        amem[i] = 0;
    maxa = amem;

    for (i = 0; i < nstate; ++i) {
        if (tystate[i] == 0 && adb > 1)
            fprintf(ftable, "State %d: null\n", i);
        indgo[i] = YYFLAG1;
    }

    while ((i = nxti()) != NOMORE) {
        if (i >= 0)
            stin(i);
        else
            gin(-i);
    }

    if (adb > 2) { /* print a array */
        for (p = amem; p <= maxa; p += 10) {
            fprintf(ftable, "%4d  ", p-amem);
            for (i = 0; i < 10; ++i)
                fprintf(ftable, "%4d  ", p[i]);
            fprintf(ftable, "\n");
        }
    }
    /* write out the output appropriate to the language */
    aoutput();
    osummary();
    ZAPFILE(TEMPNAME);
}

static void 
gin(int i)
{
    register int *r, *s, *q1, *q2;
    int *p;

    /* enter gotos on nonterminal i into array amem */
    ggreed[i] = 0;

    q2 = tracemem + yypgo[i+1] - 1;
    q1 = tracemem + yypgo[i];

    /* now, find a place for it */

    /* for( p=amem; p < &amem[new_actsize]; ++p ){ */
    p = amem;
    for (;;) {
        while (p >= &amem[new_actsize])
            exp_act(&p);
        if (*p)
            goto nextgp;
        for (r = q1; r < q2; r += 2) {
            s = p + *r + 1;
            /*
             * Check if action table needs to
             * be expanded or not. If so,
             * expand it.
             */
            while (s >= &amem[new_actsize]) {
                exp_act(&p);
                s = p + *r + 1;
            }
            if (*s)
                goto nextgp;
            if (s > maxa) {
                while ((maxa = s) >= &amem[new_actsize])
                    /* error( "amem array overflow" ); */
                    exp_act(&p);
            }
        }
        /* we have found a spot */
        *p = *q2;
        if (p > maxa) {
            while ((maxa = p) >= &amem[new_actsize])
                /* error("amem array overflow"); */
                exp_act(&p);
        }
        for (r = q1; r < q2; r += 2) {
            s = p + *r + 1;
            /*
             * Check if action table needs to
             * be expanded or not. If so,
             * expand it.
             */
            while (s >= &amem[new_actsize]) {
                exp_act(&p);
                s = p + *r + 1;
            }
            *s = r[1];
        }

        pgo[i] = p - amem;
        if (adb > 1)
            fprintf(ftable,
                "Nonterminal %d, entry at %d\n", i, pgo[i]);
        goto nextgi;

        nextgp:
            ++p;
    }
    /* error( "cannot place goto %d\n", i ); */
    nextgi:;
}

static void 
stin(int i)
{
    register int *r, n, nn, flag, j, *q1, *q2;
    int *s;

    tystate[i] = 0;

    /* Enter state i into the amem array */

    q2 = tracemem + temp1[i + 1];
    q1 = tracemem + temp1[i];
    /* Find an acceptable place */

    nn = -maxoff;
    more:
    for (n = nn; n < new_actsize; ++n) {
        flag = 0;
        for (r = q1; r < q2; r += 2) {
            s = *r + n + amem;
            if (s < amem)
                goto nextn;
            /*
             * Check if action table needs to
             * be expanded or not. If so,
             * expand it.
             */
            while (s >= &amem[new_actsize]) {
                exp_act(NULL);
                s = *r + n + amem;
            }
            if (*s == 0)
                ++flag;
            else if (*s != r[1])
                goto nextn;
        }

        /*
         * check that the position equals another
         * only if the states are identical
         */
        for (j = 0; j < nstate; ++j) {
            if (indgo[j] == n) {
                if (flag)
                    /*
                     * we have some disagreement.
                     */
                    goto nextn;
                if (temp1[j+1] + temp1[i] ==
                    temp1[j] + temp1[i+1]) {
                    /* states are equal */
                    indgo[i] = n;
                    if (adb > 1)
                        fprintf(ftable,
                        "State %d: entry at"
                        " %d equals state %d\n",
                        i, n, j);
                    return;
                }
                goto nextn;  /* we have some disagreement */
            }
        }

        for (r = q1; r < q2; r += 2) {
            while ((s = *r + n + amem) >= &amem[new_actsize]) {
                /*
                 * error( "out of space");
                 */
                exp_act(NULL);
            }
            if (s > maxa)
                maxa = s;
            if (*s != 0 && *s != r[1])
                error(
                "clobber of amem array, pos'n %d, by %d",
                s-amem, r[1]);
            *s = r[1];
        }
        indgo[i] = n;
        if (adb > 1)
            fprintf(ftable,
                "State %d: entry at %d\n", i, indgo[i]);
        return;
        nextn:;
    }

    /* error( "Error; failure to place state %d\n", i ); */
    exp_act(NULL);
    nn = new_actsize - ACTSIZE;
    goto more;
    /* NOTREACHED */
}

static int
nxti(void)
{
    /* finds the next i */
    register int i, max, maxi = 0;
    max = 0;

    for (i = 1; i <= nnonter; ++i)
        if (ggreed[i] >= max) {
        max = ggreed[i];
        maxi = -i;
        }

    for (i = 0; i < nstate; ++i)
        if (tystate[i] >= max) {
            max = tystate[i];
            maxi = i;
        }
    if (nxdb)
        fprintf(ftable, "nxti = %d, max = %d\n", maxi, max);
    if (max == 0)
        return (NOMORE);
    else
        return (maxi);
}

static void 
osummary(void)
{
    /* write summary */
    register int i, *p;

    if (foutput == NULL)
        return;
    i = 0;
    for (p = maxa; p >= amem; --p) {
        if (*p == 0)
            ++i;
    }

    fprintf(foutput, "Optimizer space used: input %d/%d, output %d/%d\n",
        optimmem-tracemem + 1, new_memsize, maxa-amem + 1, new_actsize);
    fprintf(foutput, "%d table entries, %d zero\n", (maxa-amem) + 1, i);
    fprintf(foutput,
        "maximum spread: %d, maximum offset: %d\n", maxspr, maxoff);

}

static void 
aoutput(void)
{
    /* this version is for C */
    /* write out the optimized parser */

    fprintf(ftable, "# define YYLAST %d\n", maxa-amem + 1);
    arout(L"yyact", amem, (maxa - amem) + 1);
    arout(L"yypact", indgo, nstate);
    arout(L"yypgo", pgo, nnonter + 1);
}

static void
arout(wchar_t *s, int *v, int n)
{
    register int i;

    fprintf(ftable, "static YYCONST yytabelem %ls[]={\n", s);
    for (i = 0; i < n; ) {
        if (i % 10 == 0)
            fprintf(ftable, "\n");
        fprintf(ftable, "%6d", v[i]);
        if (++i == n)
            fprintf(ftable, " };\n");
        else
            fprintf(ftable, ",");
    }
}

static int
gtnm(void)
{
    register int s, val, c;

    /* read and convert an integer from the standard input */
    /* return the terminating character */
    /* blanks, tabs, and newlines are ignored */

    s = 1;
    val = 0;

    while ((c = getwc(finput)) != EOF) {
        if (iswdigit(c))
            val = val * 10 + c - L'0';
        else if (c == L'-')
            s = -1;
        else
            break;
    }
    *optimmem++ = s*val;
    if (optimmem >= &tracemem[new_memsize])
        exp_mem(0);
    return (c);
}

void 
exp_act(int **ptr)
{
    static int *actbase;
    int i;
    new_actsize += ACTSIZE;

    actbase = amem;
    amem = realloc(amem, sizeof (int) * new_actsize);
    if (amem == NULL)
        error("couldn't expand action table");

    for (i = new_actsize-ACTSIZE; i < new_actsize; ++i)
        amem[i] = 0;
    if (ptr != NULL)
        *ptr = *ptr - actbase + amem;
    if (memp >= amem)
        memp = memp - actbase + amem;
    if (maxa >= amem)
        maxa = maxa - actbase + amem;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/Makefile.mk

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/Makefile.mk used in: Step 989, Step 991
YOBJ = y1.o y2.o y3.o y4.o y5.o getopt.o

LOBJ = libmai.o libzer.o

.c.o: ; $(CC) -c $(CFLAGS) $(CPPFLAGS) $(WARN) -DPARSER='"$(LIBDIR)/yaccpar"' $<

all: yacc liby.a

yacc: $(YOBJ)
    $(CC) $(LDFLAGS) $(YOBJ) $(LIBS) -o yacc

liby.a: $(LOBJ)
    $(AR) -rv liby.a $(LOBJ)
    $(RANLIB) $@

install: all
    test -d $(ROOT)$(BINDIR) || mkdir -p $(ROOT)$(BINDIR)
    test -d $(ROOT)$(LIBDIR) || mkdir -p $(ROOT)$(LIBDIR)
    $(INSTALL) -c -m 755 yacc $(ROOT)$(BINDIR)/yacc
    $(STRIP) $(ROOT)$(BINDIR)/yacc
    $(INSTALL) -c -m 644 yaccpar $(ROOT)$(LIBDIR)/yaccpar
    $(INSTALL) -c -m 644 liby.a $(ROOT)$(LIBDIR)/liby.a
    test -d $(ROOT)$(MANDIR)/man1 || mkdir -p $(ROOT)$(MANDIR)/man1
    $(INSTALL) -c -m 644 yacc.1 $(ROOT)$(MANDIR)/man1/yacc.1

clean:
    rm -f yacc liby.a $(YOBJ) $(LOBJ) y.tab.[ch] core log *~

mrproper: clean

libmai.o: libmai.c
libzer.o: libzer.c
y1.o: y1.c dextern
y2.o: y2.c dextern sgs.h
y3.o: y3.c dextern
y4.o: y4.c dextern

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/patches/lex_remove_wchar.patch

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/patches/lex_remove_wchar.patch used in: Step 990
SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>

SPDX-License-Identifier: CDDL-1.0

From 508eb06d40498acf954fc51ecb9171d2ce2236f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andrius=20=C5=A0tikonas?= <andrius@stikonas.eu>
Date: Wed, 27 Jan 2021 00:40:19 +0000
Subject: [PATCH] Workaround for lex to work with mes libc.

Similarly to yacc, remove wchar. See yacc patch for further
information.

---
 lex/Makefile.mk |  2 +-
 lex/allprint.c  |  6 ++--
 lex/ldefs.c     |  8 -----
 lex/main.c      |  1 +
 lex/parser.y    | 77 +++++++++++++++++++++++++++++++++++--------------
 lex/reject.c    |  1 -
 lex/sub1.c      | 44 ++++++++++++++++++----------
 lex/sub3.c      |  2 +-
 8 files changed, 89 insertions(+), 52 deletions(-)

diff --git lex/Makefile.mk lex/Makefile.mk
index 577f7cb..a413867 100644
--- lex/Makefile.mk
+++ lex/Makefile.mk
@@ -1,4 +1,4 @@
-XOBJ = main.o sub1.o sub2.o sub3.o header.o wcio.o parser.o getopt.o lsearch.o
+XOBJ = main.o sub1.o sub2.o sub3.o header.o parser.o getopt.o lsearch.o
 
 LOBJ = allprint.o libmain.o reject.o yyless.o yywrap.o \
    allprint_w.o reject_w.o yyless_w.o reject_e.o yyless_e.o
diff --git lex/allprint.c lex/allprint.c
index 6e82495..cb0c6bb 100644
--- lex/allprint.c
+++ lex/allprint.c
@@ -42,8 +42,6 @@
 #include <sys/euc.h>
 #include <widec.h>
 #endif
-#include <wctype.h>
-#include <wchar.h>
 
 extern FILE *yyout;
 
@@ -78,10 +76,10 @@ allprint(CHR c)
        fprintf(yyout, "\\_");
        break;
    default:
-       if (!iswprint(c))
+       if (!iwprint(c))
            fprintf(yyout, "\\x%-2x", (int)c);
        else
-           putwc(c, yyout);
+           putc(c, yyout);
        break;
    }
 }
diff --git lex/ldefs.c lex/ldefs.c
index ff99665..c5dcbaf 100644
--- lex/ldefs.c
+++ lex/ldefs.c
@@ -43,7 +43,6 @@
 #ifdef __sun
 #include <widec.h>
 #endif
-#include <wctype.h>
 
 #define    CHR wchar_t
 #define    BYTE char
@@ -296,13 +295,6 @@ int mn1(int a, intptr_t d);
 int mn0(int a);
 int dupl(int n);
 
-#undef getwc
-#define    getwc(f)    lex_getwc(f)
-extern wint_t lex_getwc(FILE *);
-#undef putwc
-#define    putwc(c, f) lex_putwc(c, f)
-extern wint_t lex_putwc(wchar_t, FILE *);
-
 #undef index
 #define    index       lex_index
 
diff --git lex/main.c lex/main.c
index 8aee8ea..52c892a 100644
--- lex/main.c
+++ lex/main.c
@@ -38,6 +38,7 @@
  * Sccsid @(#)main.c   1.9 (gritter) 11/26/05
  */
 
+#include <getopt.h>
 #include <string.h>
 #include "once.h"
 #include "sgs.h"
diff --git lex/parser.y lex/parser.y
index b8618e3..34a7e9a 100644
--- lex/parser.y
+++ lex/parser.y
@@ -43,7 +43,6 @@
 void yyerror(char *);
 
 #include <ctype.h>
-#include <wchar.h>
 #include <inttypes.h>
 #ifndef    __sun
 #define    wcsetno(c)  0
@@ -289,6 +288,19 @@ r: CHAR
    ;
 
 %%
+
+/*
+Copy multibyte string into char string.
+Mes C library does not support wide strings, and fprintf truncates all strings to 1 character.
+This happens because wchar_t strings have 0 in a second byte.
+*/
+int
+wstrcpy(char const *destination, wchar_t const *source) {
+   int i = 0;
+   do {
+       destination[i]=source[i];
+   } while (source[i++] != 0);
+}
 int
 yylex(void)
 {
@@ -353,7 +365,7 @@ yylex(void)
                            (*(p+2) == 'O')) {
                            if(lgatflg)
                            error("Too late for %%pointer");
-                           while(*p && !iswspace(*p))
+                           while(*p && !isspace(*p))
                            p++;
                            isArray = 0;
                            continue;
@@ -397,7 +409,7 @@ yylex(void)
                            (*(p+2) == 'R')) {
                            if(lgatflg)
                            error("Too late for %%array");
-                           while(*p && !iswspace(*p))
+                           while(*p && !isspace(*p))
                            p++;
                            isArray = 1;
                            continue;
@@ -426,7 +438,7 @@ yylex(void)
                        if(handleeuc)
                            error("\
 Character table (%t) is supported only in ASCII compatibility mode.\n");
-                       ZCH = wcstol(p+2, NULL, 10);
+                       ZCH = strtol(p+2, NULL, 10);
                        if (ZCH < NCH) ZCH = NCH;
                        if (ZCH > 2*NCH) error("ch table needs redeclaration");
                        chset = TRUE;
@@ -438,13 +450,13 @@ Character table (%t) is supported only in ASCII compatibility mode.\n");
                                continue;
                                }
                            while(digit(*p)) p++;
-                           if(!iswspace(*p)) error("bad translation format");
-                           while(iswspace(*p)) p++;
+                           if(!isspace(*p)) error("bad translation format");
+                           while(isspace(*p)) p++;
                            t = p;
                            while(*t){
                                c = ctrans(&t);
                                if(ctable[(unsigned)c]){
-                                   if (iswprint(c))
+                                   if (isprint(c))
                                        warning("Character '%lc' used twice",c);
 
                                    else
@@ -485,8 +497,12 @@ Character table (%t) is supported only in ASCII compatibility mode.\n");
                        while(getl(p) && scomp(p, L_PctCbr) != 0)
                            if(p[0]=='/' && p[1]=='*')
                                cpycom(p);
-                           else
-                               fprintf(fout,"%ls\n",p);
+                           else {
+                               char p2[100];
+                               wstrcpy(p2, p);
+                               fprintf(fout,"%ls\n",p2);
+                               memset(p2, 0, sizeof p2);
+                           }
                        if(p[0] == '%') continue;
                        if (*p) error("EOF before %%%%");
                        else error("EOF before %%}");
@@ -501,12 +517,12 @@ Character table (%t) is supported only in ASCII compatibility mode.\n");
 start:
                        lgate();
 
-                       while(*p && !iswspace(*p) && ((*p) != (wchar_t)',')) p++;
+                       while(*p && !isspace(*p) && ((*p) != (wchar_t)',')) p++;
                        n = TRUE;
                        while(n){
-                           while(*p && (iswspace(*p) || ((*p) == (wchar_t)','))) p++;
+                           while(*p && (isspace(*p) || ((*p) == (wchar_t)','))) p++;
                            t = p;
-                           while(*p && !iswspace(*p) && ((*p) != (wchar_t)',')) {
+                           while(*p && !isspace(*p) && ((*p) != (wchar_t)',')) {
                                if(!isascii(*p))
                                error("None-ASCII characters in start condition.");
                                p++;
@@ -516,7 +532,10 @@ start:
                            if (*t == 0) continue;
                            i = sptr*2;
                            if(!ratfor)fprintf(fout,"# ");
-                           fprintf(fout,"define %ls %d\n",t,i);
+                           char t2[100];
+                           wstrcpy(t2, t);
+                           fprintf(fout,"define %ls %d\n",t2,i);
+                           memset(t2, 0, sizeof t2);
                            scopy(t,sp);
                            sname[sptr] = sp;
                            /* XCU4: save exclusive flag with start name */
@@ -537,14 +556,20 @@ start:
                case ' ': case '\t':        /* must be code */
                    lgate();
                    if( p[1]=='/' && p[2]=='*' ) cpycom(p);
-                   else fprintf(fout, "%ls\n",p);
+
+                   else {
+                       char p2[100];
+                       wstrcpy(p2, p);
+                       fprintf(fout, "%ls\n",p2);
+                       memset(p2, 0, sizeof p2);
+                   }
                    continue;
                case '/':   /* look for comments */
                    lgate();
                    if((*(p+1))=='*') cpycom(p);
                    /* FALLTHRU */
                default:        /* definition */
-                   while(*p && !iswspace(*p)) p++;
+                   while(*p && !isspace(*p)) p++;
                    if(*p == 0)
                        continue;
                    prev = *p;
@@ -557,7 +582,7 @@ start:
                }
            } else { /* still sect 1, but prev != '\n' */
                p = bptr;
-               while(*p && iswspace(*p)) p++;
+               while(*p && isspace(*p)) p++;
                if(*p == 0)
                    warning("No translation given - null string assumed");
                scopy(p,token);
@@ -632,8 +657,11 @@ start:
                    while(!eof&& getl(buf) && scomp(L_PctCbr,buf)!=0)
                        if(buf[0]=='/' && buf[1]=='*')
                            cpycom(buf);
-                       else
-                           fprintf(fout,"%ls\n",buf);
+                       else {
+                           char buf2[100];
+                           wstrcpy(buf2, p);
+                           fprintf(fout,"%ls\n",buf2);
+                                                }
                    continue;
                    }
                if(peek == '%'){
@@ -944,9 +972,16 @@ Character range specified between different codesets.");
        else    
            fprintf(fout,
                "\n# line %d \"%s\"\n", yyline-1, sargv[optind]);
-       fprintf(fout,"%ls\n",buf);
-       while(getl(buf) && !eof)
-           fprintf(fout,"%ls\n",buf);
+       char buf2[100];
+       wstrcpy(buf2, buf);
+       fprintf(fout,"%ls\n",buf2);
+       memset(buf2, 0, sizeof buf2);
+       while(getl(buf) && !eof) {
+           wstrcpy(buf2, buf);
+           fprintf(fout,"%ls\n",buf2);
+           memset(buf2, 0, sizeof buf2);
+       }
+       memset(buf2, 0, sizeof buf2);
         }
 
    return(freturn(0));
diff --git lex/reject.c lex/reject.c
index 31928e7..ef08c57 100644
--- lex/reject.c
+++ lex/reject.c
@@ -38,7 +38,6 @@
 #include <euc.h>
 #include <widec.h>
 #else  /* !sun */
-#include <wchar.h>
 #endif /* !sun */
 #include <limits.h>
 #endif
diff --git lex/sub1.c lex/sub1.c
index b867948..eead84b 100644
--- lex/sub1.c
+++ lex/sub1.c
@@ -38,7 +38,6 @@
 
 #include "ldefs.c"
 #include <limits.h>
-#include <wchar.h>
 #include <ctype.h>
 #include <stdarg.h>
 
@@ -394,6 +393,19 @@ cclinter(int sw)
    }
 }
 
+int
+mbtowc(wchar_t *pwc, const char *s, size_t n)
+{
+   if (s != 0) {
+       if (n < 1)
+           return -1;
+       if (pwc != 0)
+           *pwc = *s & 0377;
+       return *s != '\0';
+   } else
+       return 0;
+}
+
 int
 usescape(int c)
 {
@@ -546,7 +558,7 @@ cpyact(void)
            break;
        case ';':
            if (brac == 0) {
-               putwc(c, fout);
+               putc(c, fout);
                putc('\n', fout);
                return (1);
            }
@@ -558,26 +570,26 @@ cpyact(void)
        case '}':
            brac--;
            if (brac == 0) {
-               putwc(c, fout);
+               putc(c, fout);
                putc('\n', fout);
                return (1);
            }
            break;
        case '/':
-           putwc(c, fout);
+           putc(c, fout);
            c = gch();
            if (c != '*')
                goto swt;
-           putwc(c, fout);
+           putc(c, fout);
            savline = yyline;
            while (c = gch()) {
                while (c == '*') {
-                   putwc(c, fout);
+                   putc(c, fout);
                    if ((c = gch()) == '/') {
                        putc('/', fout);
                        while ((c = gch()) == ' ' ||
                            c == '\t' || c == '\n')
-                           putwc(c, fout);
+                           putc(c, fout);
                        goto swt;
                    }
                }
@@ -590,16 +602,16 @@ cpyact(void)
        case '\'': /* character constant */
        case '"': /* character string */
            mth = c;
-           putwc(c, fout);
+           putc(c, fout);
            while (c = gch()) {
                if (c == '\\') {
-                   putwc(c, fout);
+                   putc(c, fout);
                    c = gch();
                }
                else
                    if (c == mth)
                        goto loop;
-               putwc(c, fout);
+               putc(c, fout);
                if (c == '\n') {
                    yyline--;
                    error(
@@ -620,7 +632,7 @@ cpyact(void)
    loop:
        if (c != ' ' && c != '\t' && c != '\n')
            sw = FALSE;
-       putwc(c, fout);
+       putc(c, fout);
        if (peek == '\n' && !brac && copy_line) {
            putc('\n', fout);
            return (1);
@@ -636,7 +648,7 @@ gch(void)
    int c;
    prev = pres;
    c = pres = peek;
-   peek = pushptr > pushc ? *--pushptr : getwc(fin);
+   peek = pushptr > pushc ? *--pushptr : getc(fin);
    while (peek == EOF) {
        if (no_input) {
            if (!yyline)
@@ -650,7 +662,7 @@ gch(void)
                if (fin == NULL)
                    error("Cannot open file -- %s",
                    sargv[optind]);
-               peek = getwc(fin);
+               peek = getc(fin);
            } else
                break;
        } else {
@@ -856,11 +868,11 @@ allprint(CHR c)
        printf("\\_");
        break;
    default:
-       if (!iswprint(c)) {
+       if (!isprint(c)) {
            printf("\\x%-2x", c); /* up to fashion. */
            charc += 3;
        } else
-           putwc(c, stdout);
+           putc(c, stdout);
        break;
    }
    charc++;
@@ -903,7 +915,7 @@ sect1dump(void)
                allprint(i);
                putchar(' ');
                iswprint(ctable[i]) ?
-                   putwc(ctable[i], stdout) :
+                   putc(ctable[i], stdout) :
                    printf("%d", ctable[i]);
                putchar('\n');
            }
diff --git lex/sub3.c lex/sub3.c
index 186bcbc..aa07f66 100644
--- lex/sub3.c
+++ lex/sub3.c
@@ -147,7 +147,7 @@ remch(wchar_t c)
     */
    if (!handleeuc) {
        if (!isascii(c))
-           if (iswprint(c))
+           if (isprint(c))
            warning(
 "Non-ASCII character '%lc' in pattern; use -w or -e lex option.", c);
            else warning(
-- 
2.26.2

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/Makefile.mk

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/Makefile.mk used in: Step 990, Step 1004
XOBJ = main.o sub1.o sub2.o sub3.o header.o wcio.o parser.o getopt.o lsearch.o

LOBJ = allprint.o libmain.o reject.o yyless.o yywrap.o \
    allprint_w.o reject_w.o yyless_w.o reject_e.o yyless_e.o

WFLAGS = -DEUC -DJLSLEX -DWOPTION
EFLAGS = -DEUC -DJLSLEX -DEOPTION

LEXDIR = $(LIBDIR)/lex

.c.o: ; $(CC) -c $(CFLAGS) $(CPPFLAGS) $(WARN) -DFORMPATH='"$(LEXDIR)"' $<

all: lex libl.a

lex: $(XOBJ)
    $(CC) $(LDFLAGS) $(XOBJ) $(LIBS) -o lex

libl.a: $(LOBJ)
    $(AR) -rv libl.a $(LOBJ)
    $(RANLIB) $@

allprint_w.o: allprint.c
    $(CC) -c $(CFLAGS) $(CPPFLAGS) $(WARN) $(WFLAGS) allprint.c -o $@

reject_w.o: reject.c
    $(CC) -c $(CFLAGS) $(CPPFLAGS) $(WARN) $(WFLAGS) reject.c -o $@

yyless_w.o: yyless.c
    $(CC) -c $(CFLAGS) $(CPPFLAGS) $(WARN) $(WFLAGS) yyless.c -o $@

reject_e.o: reject.c
    $(CC) -c $(CFLAGS) $(CPPFLAGS) $(WARN) $(EFLAGS) reject.c -o $@

yyless_e.o: yyless.c
    $(CC) -c $(CFLAGS) $(CPPFLAGS) $(WARN) $(EFLAGS) yyless.c -o $@

install: all
    test -d $(ROOT)$(BINDIR) || mkdir -p $(ROOT)$(BINDIR)
    test -d $(ROOT)$(LEXDIR) || mkdir -p $(ROOT)$(LEXDIR)
    $(INSTALL) -c -m 755 lex $(ROOT)$(BINDIR)/lex
    $(STRIP) $(ROOT)$(BINDIR)/lex
    $(INSTALL) -c -m 644 ncform $(ROOT)$(LEXDIR)/ncform
    $(INSTALL) -c -m 644 nceucform $(ROOT)$(LEXDIR)/nceucform
    $(INSTALL) -c -m 644 nrform $(ROOT)$(LEXDIR)/nrform
    $(INSTALL) -c -m 644 libl.a $(ROOT)$(LIBDIR)/libl.a
    test -d $(ROOT)$(MANDIR)/man1 || mkdir -p $(ROOT)$(MANDIR)/man1
    $(INSTALL) -c -m 644 lex.1 $(ROOT)$(MANDIR)/man1/lex.1

clean:
    rm -f lex libl.a $(XOBJ) $(LOBJ) parser.c core log *~

mrproper: clean

allprint.o: allprint.c
header.o: header.c ldefs.c
ldefs.o: ldefs.c
libmain.o: libmain.c
main.o: main.c once.h ldefs.c sgs.h
reject.o: reject.c
sub1.o: sub1.c ldefs.c
sub2.o: sub2.c ldefs.c
sub3.o: sub3.c ldefs.c search.h
yyless.o: yyless.c
yywrap.o: yywrap.c
lsearch.o: search.h

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/allprint.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/allprint.c used in: Step 990, Step 1016, Step 1021
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*  Copyright (c) 1989 AT&T */
/*    All Rights Reserved   */

/*  from OpenSolaris "allprint.c    6.11    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)allprint.c    1.4 (gritter) 11/27/05
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef  __sun
#include <sys/euc.h>
#include <widec.h>
#endif
#include <wctype.h>
#include <wchar.h>

extern FILE *yyout;

#ifndef JLSLEX
#define CHR    char
#endif

#ifdef WOPTION
#define CHR wchar_t
#define sprint  sprint_w
#define allprint    allprint_w
#endif

#ifdef EOPTION
#define CHR wchar_t
#endif

void
allprint(CHR c)
{
    switch (c) {
    case '\n':
        fprintf(yyout, "\\n");
        break;
    case '\t':
        fprintf(yyout, "\\t");
        break;
    case '\b':
        fprintf(yyout, "\\b");
        break;
    case ' ':
        fprintf(yyout, "\\_");
        break;
    default:
        if (!iswprint(c))
            fprintf(yyout, "\\x%-2x", (int)c);
        else
            putwc(c, yyout);
        break;
    }
}

void
sprint(CHR *s)
{
    while (*s)
        allprint(*s++);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/ldefs.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/ldefs.c used in: Step 990, Step 1005, Step 1006, Step 1007, Step 1009, Step 1012
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.
 * All rights reserved.
 * Use is subject to license terms.
 */

/*  Copyright (c) 1988 AT&T */
/*  All Rights Reserved */

/*  from OpenSolaris "ldefs.c   6.16    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)ldefs.c   1.7 (gritter) 4/14/07
 */

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

#ifdef  __sun
#include <widec.h>
#endif
#include <wctype.h>

#define CHR wchar_t
#define BYTE char
#define Boolean char
#define LONG_WCHAR_T 1

#define PP 1
#ifdef u370
#define CWIDTH 8
#define CMASK 0377
#define ASCII 1
#else

#ifdef unix
#define CWIDTH 7
#define CMASK 0177
#define ASCII 1
#endif

#ifdef gcos
#define CWIDTH 9
#define CMASK 0777
#define ASCII 1
#endif

#ifdef ibm
#define CWIDTH 8
#define CMASK 0377
#define EBCDIC 1
#endif
#endif

#define NCH 256
#define TOKENSIZE 10000
#define DEFSIZE 1000
#define DEFCHAR 2000
#define BUF_SIZ 2000
#define STARTCHAR 2560
#define STARTSIZE 256
#define CCLSIZE 20000


#ifdef SMALL
#define TREESIZE 600
#define NTRANS 1500
#define NSTATES 300
#define MAXPOS 1500
#define MAXPOSSTATE 500
#define NOUTPUT 1500
#endif

#ifndef SMALL
#define TREESIZE 1000
#define NSTATES 500
#define MAXPOS 2500
#define MAXPOSSTATE 4*1000
#define NTRANS 2000
#define NOUTPUT 4*3000
#endif
#define NACTIONS 4*1000
#define ALITTLEEXTRA 300

#define RCCL        0x4000
#define RNCCL       0x4001
#define RSTR        0x4002
#define RSCON       0x4003
/* XCU4: add RXSCON */
#define RXSCON      0x4011
#define RNEWE       0x4004
#define FINAL       0x4005
#define RNULLS      0x4006
#define RCAT        0x4007
#define STAR        0x4008
#define PLUS        0x4009
#define QUEST       0x400a
#define DIV     0x400b
#define BAR     0x400c
#define CARAT       0x400d
#define S1FINAL     0x400e
#define S2FINAL     0x400f
#define DOT     0x4010
#define ISOPERATOR(n)   ((n & 0xc080) == 0x4000)

/*
 * New to JLE; this is not really a node tag.
 * This is used in a string pointed to by
 * the leaf of an RCCL or RNCCL node as a
 * special prefix code that substitutes
 * the infix '-' range operator.  For
 * example, a lex character class "[_0-9a-zA-Z]"
 * would be translated to the intermidiate
 * form:
 *      RCCL
 *   |
 *   |
 *   v
 *       "_<RANGE>09<RANGE>a-z<RANGE>A-Z"
 */
#define RANGE       0x40ff

#define MAXNCG 1000
extern int ncgidtbl;
extern int ncg; /* ncg == ncgidtbl * 2 */
typedef unsigned long lchar;
extern lchar yycgidtbl[];
extern int yycgid(wchar_t);
extern Boolean handleeuc; /* TRUE iff -w or -e option is specified. */
extern Boolean widecio; /* TRUE iff -w option is specified. */

#define DEFSECTION 1
#define RULESECTION 2
#define ENDSECTION 5

#define PC 1
#define PS 1

#ifdef DEBUG
#define LINESIZE 110
extern int yydebug;
extern int debug;       /* 1 = on */
extern int charc;
#endif

#ifndef DEBUG
#define freturn(s) s
#endif

#undef  FALSE
#undef  TRUE
enum {
    FALSE,
    TRUE
};


extern int optind;
extern int no_input;
extern int sargc;
extern char **sargv;
extern char *v_stmp;
extern char *release_string;
extern CHR buf[];
extern int ratfor;      /* 1 = ratfor, 0 = C */
extern int fatal;
extern int n_error;
extern int copy_line;
extern int yyline;      /* line number of file */
extern int sect;
extern int eof;
extern int lgatflg;
extern int divflg;
extern int funcflag;
extern int pflag;
extern int casecount;
extern int chset;   /* 1 = CHR set modified */
extern FILE *fin, *fout, *fother, *errorf;
extern int fptr;
extern char *ratname, *cname;
extern int prev;    /* previous input character */
extern int pres;    /* present input character */
extern int peek;    /* next input character */
extern int *name;
extern intptr_t *left;
extern intptr_t *right;
extern int *parent;
extern Boolean *nullstr;
extern int tptr;
extern CHR pushc[TOKENSIZE];
extern CHR *pushptr;
extern CHR slist[STARTSIZE];
extern CHR *slptr;
extern CHR **def, **subs, *dchar;
extern CHR **sname, *schar;
/* XCU4: %x exclusive start */
extern int *exclusive;
extern CHR *ccl;
extern CHR *ccptr;
extern CHR *dp, *sp;
extern int dptr, sptr;
extern CHR *bptr;       /* store input position */
extern CHR *tmpstat;
extern int count;
extern int **foll;
extern int *nxtpos;
extern int *positions;
extern int *gotof;
extern int *nexts;
extern CHR *nchar;
extern int **state;
extern int *sfall;      /* fallback state num */
extern Boolean *cpackflg;   /* true if state has been character packed */
extern int *atable, aptr;
extern int nptr;
extern Boolean symbol[MAXNCG];
extern CHR cindex[MAXNCG];
extern int xstate;
extern int stnum;
extern int ctable[];
extern int ZCH;
extern int ccount;
extern CHR match[MAXNCG];
extern BYTE extra[];
extern CHR *pcptr, *pchar;
extern int pchlen;
extern int nstates, maxpos;
extern int yytop;
extern int report;
extern int ntrans, treesize, outsize;
extern long rcount;
extern int optim;
extern int *verify, *advance, *stoff;
extern int scon;
extern CHR *psave;
extern CHR *getl(CHR *);
extern void *myalloc(int, int);

void phead1(void);
void phead2(void);
void ptail(void);
void statistics(void);
void error_tail(void);
void error(const char *, ...);
void warning(const char *, ...);
void lgate(void);
void scopy(CHR *s, CHR *t);
void cclinter(int sw);
void cpycom(CHR *p);
void munput(int t, CHR *p);
void cfoll(int v);
void cgoto(void);
void mkmatch(void);
void layout(void);
void remch(wchar_t c);
void sortcgidtbl(void);
void repbycgid(void);
int gch(void);
int slength(CHR *s);
int yyparse(void);
int scomp(CHR *x, CHR *y);
int space(int ch);
int siconv(CHR *t);
int digit(int c);
int ctrans(CHR **ss);
int cpyact(void);
int lookup(CHR *s, CHR **t);
int usescape(int c);
int alpha(int c);
int mn2(int a, intptr_t d, intptr_t c);
int mn1(int a, intptr_t d);
int mn0(int a);
int dupl(int n);

#undef  getwc
#define getwc(f)    lex_getwc(f)
extern wint_t lex_getwc(FILE *);
#undef  putwc
#define putwc(c, f) lex_putwc(c, f)
extern wint_t lex_putwc(wchar_t, FILE *);

#undef  index
#define index       lex_index

extern int isArray;     /* XCU4: for %array %pointer */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/main.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/main.c used in: Step 990, Step 1005
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.
 * All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */

/* Copyright 1976, Bell Telephone Laboratories, Inc. */

/*  from OpenSolaris "main.c    6.16    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)main.c    1.9 (gritter) 11/26/05
 */

#include <string.h>
#include "once.h"
#include "sgs.h"
#include <locale.h>
#include <limits.h>
#include <unistd.h>

static wchar_t  L_INITIAL[] = {'I', 'N', 'I', 'T', 'I', 'A', 'L', 0};

static void get1core(void);
static void free1core(void);
static void get2core(void);
static void free2core(void);
static void get3core(void);
#ifdef DEBUG
static void free3core(void);
#endif

int
main(int argc, char **argv)
{
    int i;
    int c;
    char *path = NULL;
    Boolean eoption = 0, woption = 0;

    sargv = argv;
    sargc = argc;
    errorf = stderr;
    setlocale(LC_CTYPE, "");
#ifdef DEBUG
    while ((c = getopt(argc, argv, "dyctvnewVQ:Y:")) != EOF) {
#else
    while ((c = getopt(argc, argv, "ctvnewVQ:Y:")) != EOF) {
#endif
        switch (c) {
#ifdef DEBUG
            case 'd':
                debug++;
                break;
            case 'y':
                yydebug = TRUE;
                break;
#endif
            case 'V':
                fprintf(stderr, "lex:%s , %s\n", pkg, rel);
                break;
            case 'Q':
                v_stmp = optarg;
                if (*v_stmp != 'y' && *v_stmp != 'n')
                    error(
                    "lex: -Q should be followed by [y/n]");
                break;
            case 'Y':
                path = malloc(strlen(optarg) +
                    sizeof ("/nceucform") + 1);
                path = strcpy(path, optarg);
                break;
            case 'c':
                ratfor = FALSE;
                break;
            case 't':
                fout = stdout;
                break;
            case 'v':
                report = 1;
                break;
            case 'n':
                report = 0;
                break;
            case 'w':
            case 'W':
                woption = 1;
                handleeuc = 1;
                widecio = 1;
                break;
            case 'e':
            case 'E':
                eoption = 1;
                handleeuc = 1;
                widecio = 0;
                break;
            default:
                fprintf(stderr,
                "Usage: lex [-ctvnV] [-Q(y/n)] [files...]\n");
                exit(1);
        }
    }
    if (woption && eoption) {
        error(
        "You may not specify both -w and -e simultaneously.");
    }
    no_input = argc - optind;
    if (no_input) {
        /* XCU4: recognize "-" file operand for stdin */
        if (strcmp(argv[optind], "-") == 0)
            fin = stdin;
        else {
            fin = fopen(argv[optind], "r");
            if (fin == NULL)
                error(
                "Can't open input file -- %s", argv[optind]);
        }
    } else
        fin = stdin;

    /* may be gotten: def, subs, sname, schar, ccl, dchar */
    gch();

    /* may be gotten: name, left, right, nullstr, parent */
    get1core();

    scopy(L_INITIAL, sp);
    sname[0] = sp;
    sp += slength(L_INITIAL) + 1;
    sname[1] = 0;

    /* XCU4: %x exclusive start */
    exclusive[0] = 0;

    if (!handleeuc) {
        /*
         * Set ZCH and ncg to their default values
         * as they may be needed to handle %t directive.
         */
        ZCH = ncg = NCH; /* ncg behaves as constant in this mode. */
    }

    /* may be disposed of: def, subs, dchar */
    if (yyparse())
        exit(1);    /* error return code */

    if (handleeuc) {
        ncg = ncgidtbl * 2;
        ZCH = ncg;
        if (ncg >= MAXNCG)
            error(
            "Too complex rules -- requires too many char groups.");
        sortcgidtbl();
    }
    repbycgid(); /* Call this even in ASCII compat. mode. */

    /*
     * maybe get:
     *      tmpstat, foll, positions, gotof, nexts,
     *      nchar, state, atable, sfall, cpackflg
     */
    free1core();
    get2core();
    ptail();
    mkmatch();
#ifdef DEBUG
    if (debug)
        pccl();
#endif
    sect  = ENDSECTION;
    if (tptr > 0)
        cfoll(tptr-1);
#ifdef DEBUG
    if (debug)
        pfoll();
#endif
    cgoto();
#ifdef DEBUG
    if (debug) {
        printf("Print %d states:\n", stnum + 1);
        for (i = 0; i <= stnum; i++)
            stprt(i);
    }
#endif
    /*
     * may be disposed of:
     *      positions, tmpstat, foll, state, name,
     *      left, right, parent, ccl, schar, sname
     * maybe get:    verify, advance, stoff
     */
    free2core();
    get3core();
    layout();
    /*
     * may be disposed of:
     *      verify, advance, stoff, nexts, nchar,
     *      gotof, atable, ccpackflg, sfall
     */

#ifdef DEBUG
    free3core();
#endif
    if (path == NULL) {
        static char formpath[sizeof FORMPATH + 20] = FORMPATH;
        path = formpath;
    }

    if (handleeuc) {
        if (ratfor)
            error("Ratfor is not supported by -w or -e option.");
        strcat(path, "/nceucform");
    }
    else
        strcat(path, ratfor ? "/nrform" : "/ncform");

    fother = fopen(path, "r");
    if (fother == NULL)
        error("Lex driver missing, file %s", path);
    while ((i = getc(fother)) != EOF)
        putc(i, fout);
    fclose(fother);
    fclose(fout);
    if (report == 1)
        statistics();
    fclose(stdout);
    fclose(stderr);
    return (0); /* success return code */
}

static void
get1core(void)
{
    ccptr = ccl = myalloc(CCLSIZE, sizeof (*ccl));
    pcptr = pchar = myalloc(pchlen, sizeof (*pchar));
    def = myalloc(DEFSIZE, sizeof (*def));
    subs = myalloc(DEFSIZE, sizeof (*subs));
    dp = dchar = myalloc(DEFCHAR, sizeof (*dchar));
    sname = myalloc(STARTSIZE, sizeof (*sname));
    /* XCU4: exclusive start array */
    exclusive = myalloc(STARTSIZE, sizeof (*exclusive));
    sp = schar = myalloc(STARTCHAR, sizeof (*schar));
    if (ccl == 0 || def == 0 ||
        pchar == 0 || subs == 0 || dchar == 0 ||
        sname == 0 || exclusive == 0 || schar == 0)
        error("Too little core to begin");
}

static void
free1core(void)
{
    free(def);
    free(subs);
    free(dchar);
}

static void
get2core(void)
{
    int i;
    gotof = myalloc(nstates, sizeof (*gotof));
    nexts = myalloc(ntrans, sizeof (*nexts));
    nchar = myalloc(ntrans, sizeof (*nchar));
    state = myalloc(nstates, sizeof (*state));
    atable =myalloc(nstates, sizeof (*atable));
    sfall = myalloc(nstates, sizeof (*sfall));
    cpackflg = myalloc(nstates, sizeof (*cpackflg));
    tmpstat = myalloc(tptr+1, sizeof (*tmpstat));
    foll = myalloc(tptr+1, sizeof (*foll));
    nxtpos = positions = myalloc(maxpos, sizeof (*positions));
    if (tmpstat == 0 || foll == 0 || positions == 0 ||
        gotof == 0 || nexts == 0 || nchar == 0 ||
        state == 0 || atable == 0 || sfall == 0 || cpackflg == 0)
        error("Too little core for state generation");
    for (i = 0; i <= tptr; i++)
        foll[i] = 0;
}

static void
free2core(void)
{
    free(positions);
    free(tmpstat);
    free(foll);
    free(name);
    free(left);
    free(right);
    free(parent);
    free(nullstr);
    free(state);
    free(sname);
    /* XCU4: exclusive start array */
    free(exclusive);
    free(schar);
    free(ccl);
}

static void
get3core(void)
{
    verify = myalloc(outsize, sizeof (*verify));
    advance = myalloc(outsize, sizeof (*advance));
    stoff = myalloc(stnum+2, sizeof (*stoff));
    if (verify == 0 || advance == 0 || stoff == 0)
        error("Too little core for final packing");
}

#ifdef DEBUG
static void
free3core(void)
{
    free(advance);
    free(verify);
    free(stoff);
    free(gotof);
    free(nexts);
    free(nchar);
    free(atable);
    free(sfall);
    free(cpackflg);
}
#endif

void *
myalloc(int a, int b)
{
    void *i;
    i = calloc(a,  b);
    if (i == NULL)
        warning("calloc returns a 0");
    return (i);
}

void
yyerror(char *s)
{
    fprintf(stderr,
        "\"%s\":line %d: Error: %s\n", sargv[optind], yyline, s);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/parser.y

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/parser.y used in: Step 990, Step 1010
%{
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
%}
/*
 * Copyright 2005 Sun Microsystems, Inc.
 * All rights reserved.
 * Use is subject to license terms.
 */

/*  Copyright (c) 1988 AT&T */
/*    All Rights Reserved   */


%{
/*  from OpenSolaris "parser.y  6.15    05/06/10 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)parser.y  1.8 (gritter) 11/26/05
 */

void yyerror(char *);

#include <ctype.h>
#include <wchar.h>
#include <inttypes.h>
#ifndef __sun
#define wcsetno(c)  0
#endif

%}
/* parser.y */

/* XCU4: add XSCON: %x exclusive start token */
/* XCU4: add ARRAY: %a yytext is char array */
/* XCU4: add POINTER: %p yytext is a pointer to char */
%token CHAR CCL NCCL STR DELIM SCON ITER NEWE NULLS XSCON ARRAY POINTER

%nonassoc ARRAY POINTER
%left XSCON SCON NEWE
%left '/'
/*
 * XCU4: lower the precedence of $ and ^ to less than the or operator
 * per Spec. 1170
 */
%left '$' '^'
%left '|'
%left CHAR CCL NCCL '(' '.' STR NULLS
%left ITER
%left CAT
%left '*' '+' '?'

%{
#include "ldefs.c"

#define YYSTYPE union _yystype_
union _yystype_
{
    int i;
    CHR *cp;
};
int peekon = 0; /* need this to check if "^" came in a definition section */

%}
%%
%{
int i;
int j,k;
int g;
CHR *p;
static wchar_t  L_PctUpT[]= {'%', 'T', 0};
static wchar_t  L_PctLoT[]= {'%', 't', 0};
static wchar_t  L_PctCbr[]= {'%', '}', 0};
%}
acc :   lexinput
    ={  
# ifdef DEBUG
        if(debug) sect2dump();
# endif
    }
    ;
lexinput:   defns delim prods end
    |   defns delim end
    ={
        if(!funcflag)phead2();
        funcflag = TRUE;
    }
    | error
    ={
# ifdef DEBUG
        if(debug) {
            sect1dump();
            sect2dump();
            }
# endif
        fatal = 0;
        n_error++;
        error("Illegal definition");
        fatal = 1;
        }
    ;
end:        delim | ;
defns:  defns STR STR
    ={  scopy($2.cp,dp);
        def[dptr] = dp;
        dp += slength($2.cp) + 1;
        scopy($3.cp,dp);
        subs[dptr++] = dp;
        if(dptr >= DEFSIZE)
            error("Too many definitions");
        dp += slength($3.cp) + 1;
        if(dp >= dchar+DEFCHAR)
            error("Definitions too long");
        subs[dptr]=def[dptr]=0; /* for lookup - require ending null */
    }
    |
    ;
delim:  DELIM
    ={
# ifdef DEBUG
        if(sect == DEFSECTION && debug) sect1dump();
# endif
        sect++;
        }
    ;
prods:  prods pr
    ={  $$.i = mn2(RNEWE,$1.i,$2.i);
        }
    |   pr
    ={  $$.i = $1.i;}
    ;
pr: r NEWE
    ={
        if(divflg == TRUE)
            i = mn1(S1FINAL,casecount);
        else i = mn1(FINAL,casecount);
        $$.i = mn2(RCAT,$1.i,i);
        divflg = FALSE;
        if((++casecount)>NACTIONS)
            error("Too many (>%d) pattern-action rules.", NACTIONS);
        }
    | error NEWE
    ={
# ifdef DEBUG
        if(debug) sect2dump();
# endif
        fatal = 0;
        yyline--;
        n_error++;
        error("Illegal rule");
        fatal = 1;
        yyline++;
        }
r:  CHAR
    ={  $$.i = mn0($1.i); }
    | STR
    ={
        p = (CHR *)$1.cp;
        i = mn0((unsigned)(*p++));
        while(*p)
            i = mn2(RSTR,i,(unsigned)(*p++));
        $$.i = i;
        }
    | '.'
    ={
        $$.i = mn0(DOT);
        }
    | CCL
    ={  $$.i = mn1(RCCL,(intptr_t)$1.cp); }
    | NCCL
    ={  $$.i = mn1(RNCCL,(intptr_t)$1.cp); }
    | r '*'
    ={  $$.i = mn1(STAR,$1.i); }
    | r '+'
    ={  $$.i = mn1(PLUS,$1.i); }
    | r '?'
    ={  $$.i = mn1(QUEST,$1.i); }
    | r '|' r
    ={  $$.i = mn2(BAR,$1.i,$3.i); }
    | r r %prec CAT
    ={  $$.i = mn2(RCAT,$1.i,$2.i); }
    | r '/' r
    ={  if(!divflg){
            j = mn1(S2FINAL,-casecount);
            i = mn2(RCAT,$1.i,j);
            $$.i = mn2(DIV,i,$3.i);
            }
        else {
            $$.i = mn2(RCAT,$1.i,$3.i);
            error("illegal extra slash");
            }
        divflg = TRUE;
        }
    | r ITER ',' ITER '}'
    ={  if($2.i > $4.i){
            i = $2.i;
            $2.i = $4.i;
            $4.i = i;
            }
        if($4.i <= 0)
            error("iteration range must be positive");
        else {
            j = $1.i;
            for(k = 2; k<=$2.i;k++)
                j = mn2(RCAT,j,dupl($1.i));
            for(i = $2.i+1; i<=$4.i; i++){
                g = dupl($1.i);
                for(k=2;k<=i;k++)
                    g = mn2(RCAT,g,dupl($1.i));
                j = mn2(BAR,j,g);
                }
            $$.i = j;
            }
    }
    | r ITER '}'
    ={
        if($2.i < 0)error("can't have negative iteration");
        else if($2.i == 0) $$.i = mn0(RNULLS);
        else {
            j = $1.i;
            for(k=2;k<=$2.i;k++)
                j = mn2(RCAT,j,dupl($1.i));
            $$.i = j;
            }
        }
    | r ITER ',' '}'
    ={
                /* from n to infinity */
        if($2.i < 0)error("can't have negative iteration");
        else if($2.i == 0) $$.i = mn1(STAR,$1.i);
        else if($2.i == 1)$$.i = mn1(PLUS,$1.i);
        else {      /* >= 2 iterations minimum */
            j = $1.i;
            for(k=2;k<$2.i;k++)
                j = mn2(RCAT,j,dupl($1.i));
            k = mn1(PLUS,dupl($1.i));
            $$.i = mn2(RCAT,j,k);
            }
        }
    | SCON r
    ={  $$.i = mn2(RSCON,$2.i,(intptr_t)$1.cp); }

    /* XCU4: add XSCON */
    | XSCON r
    ={  $$.i = mn2(RXSCON,$2.i,(intptr_t)$1.cp); }
    | '^' r
    ={  $$.i = mn1(CARAT,$2.i); }
    | r '$'
    ={  i = mn0('\n');
        if(!divflg){
            j = mn1(S2FINAL,-casecount);
            k = mn2(RCAT,$1.i,j);
            $$.i = mn2(DIV,k,i);
            }
        else $$.i = mn2(RCAT,$1.i,i);
        divflg = TRUE;
        }
    | '(' r ')'
    ={  $$.i = $2.i; }
    |   NULLS
    ={  $$.i = mn0(RNULLS); }

    /* XCU4: add ARRAY and POINTER */
    | ARRAY 
    ={ isArray = 1; };
    |     POINTER
    ={ isArray = 0; };
    ;

%%
int
yylex(void)
{
    CHR *p;
    int  i;
    CHR *xp;
    int lex_startcond_lookupval;
    CHR  *t, c;
    int n, j = 0, k, x;
    CHR ch;
    static int sectbegin;
    static CHR token[TOKENSIZE];
    static int iter;
    int ccs; /* Current CodeSet. */
    CHR *ccp;
    int exclusive_flag; /* XCU4: exclusive start flag */

# ifdef DEBUG
    yylval.i = 0;
# endif

    if(sect == DEFSECTION) {        /* definitions section */
        while(!eof) {
            if(prev == '\n'){    /* next char is at beginning of line */
                getl(p=buf);
                switch(*p){
                case '%':
                    switch(c= *(p+1)){
                    case '%':
                        if(scomp(p, (CHR *)"%%")) {
                            p++;
                            while(*(++p))
                                if(!space(*p)) {
                                    warning("invalid string following %%%% be ignored");
                                    break;
                                }
                        }
                        lgate();
                        if(!ratfor)fprintf(fout,"# ");
                        fprintf(fout,"define YYNEWLINE %d\n",ctable['\n']);
                        if(!ratfor) {
                            fprintf(fout,"int yylex(){\nint nstr = 0; extern int yyprevious;\n");
                        }
                        sectbegin = TRUE;
                        i = treesize*(sizeof(*name)+sizeof(*left)+
                            sizeof(*right)+sizeof(*nullstr)+sizeof(*parent))+ALITTLEEXTRA;
                        p = myalloc(i,1);
                        if(p == NULL)
                            error("Too little core for parse tree");
                        free(p);
                        name = myalloc(treesize,sizeof(*name));
                        left = myalloc(treesize,sizeof(*left));
                        right = myalloc(treesize,sizeof(*right));
                        nullstr = myalloc(treesize,sizeof(*nullstr));
                        parent = myalloc(treesize,sizeof(*parent));
                        if(name == 0 || left == 0 || right == 0 || parent == 0 || nullstr == 0)
                            error("Too little core for parse tree");
                        return(freturn(DELIM));
                    case 'p': case 'P':
                            /* %p or %pointer */
                        if ((*(p+2) == 'o') ||
                            (*(p+2) == 'O')) {
                            if(lgatflg)
                            error("Too late for %%pointer");
                            while(*p && !iswspace(*p))
                            p++;
                            isArray = 0;
                            continue;
                        }
                        /* has overridden number of positions */
                        p += 2;
                        maxpos = siconv(p);
                        if (maxpos<=0)error("illegal position number");
# ifdef DEBUG
                        if (debug) printf("positions (%%p) now %d\n",maxpos);
# endif
                        if(report == 2)report = 1;
                        continue;
                    case 'n': case 'N': /* has overridden number of states */
                        p += 2;
                        nstates = siconv(p);
                        if(nstates<=0)error("illegal state number");
# ifdef DEBUG
                        if(debug)printf( " no. states (%%n) now %d\n",nstates);
# endif
                        if(report == 2)report = 1;
                        continue;
                    case 'e': case 'E':     /* has overridden number of tree nodes */
                        p += 2;
                        treesize = siconv(p);
                        if(treesize<=0)error("illegal number of parse tree nodes");
# ifdef DEBUG
                        if (debug) printf("treesize (%%e) now %d\n",treesize);
# endif
                        if(report == 2)report = 1;
                        continue;
                    case 'o': case 'O':
                        p += 2;
                        outsize = siconv(p);
                        if(outsize<=0)error("illegal size of output array");
                        if (report ==2) report=1;
                        continue;
                    case 'a': case 'A':
                            /* %a or %array */
                        if ((*(p+2) == 'r') ||
                            (*(p+2) == 'R')) {
                            if(lgatflg)
                            error("Too late for %%array");
                            while(*p && !iswspace(*p))
                            p++;
                            isArray = 1;
                            continue;
                        }
                        /* has overridden number of transitions */
                        p += 2;
                        ntrans = siconv(p);
                        if(ntrans<=0)error("illegal translation number");
# ifdef DEBUG
                        if (debug)printf("N. trans (%%a) now %d\n",ntrans);
# endif
                        if(report == 2)report = 1;
                        continue;
                    case 'k': case 'K': /* overriden packed char classes */
                        p += 2;
                        free(pchar);
                        pchlen = siconv(p);
                        if(pchlen<=0)error("illegal number of packed character class");
# ifdef DEBUG
                        if (debug) printf( "Size classes (%%k) now %d\n",pchlen);
# endif
                        pchar=pcptr=myalloc(pchlen, sizeof(*pchar));
                        if (report==2) report=1;
                        continue;
                    case 't': case 'T':     /* character set specifier */
                        if(handleeuc)
                            error("\
Character table (%t) is supported only in ASCII compatibility mode.\n");
                        ZCH = wcstol(p+2, NULL, 10);
                        if (ZCH < NCH) ZCH = NCH;
                        if (ZCH > 2*NCH) error("ch table needs redeclaration");
                        chset = TRUE;
                        for(i = 0; i<ZCH; i++)
                            ctable[i] = 0;
                        while(getl(p) && scomp(p,L_PctUpT) != 0 && scomp(p,L_PctLoT) != 0){
                            if((n = siconv(p)) <= 0 || n > ZCH){
                                error("Character value %d out of range",n);
                                continue;
                                }
                            while(digit(*p)) p++;
                            if(!iswspace(*p)) error("bad translation format");
                            while(iswspace(*p)) p++;
                            t = p;
                            while(*t){
                                c = ctrans(&t);
                                if(ctable[(unsigned)c]){
                                    if (iswprint(c))
                                        warning("Character '%lc' used twice",c);

                                    else
                                        error("Chararter %o used twice",c);
                                    }
                                else ctable[(unsigned)c] = n;
                                t++;
                                }
                            p = buf;
                            }
                        {
                        char chused[2*NCH]; int kr;
                        for(i=0; i<ZCH; i++)
                            chused[i]=0;
                        for(i=0; i<NCH; i++)
                            chused[ctable[i]]=1;
                        for(kr=i=1; i<NCH; i++)
                            if (ctable[i]==0)
                                {
                                while (chused[kr] == 0)
                                    kr++;
                                ctable[i]=kr;
                                chused[kr]=1;
                                }
                        }
                        lgate();
                        continue;
                    case 'r': case 'R':
                        c = 'r';
                        /* FALLTHRU */
                    case 'c': case 'C':
                        if(lgatflg)
                            error("Too late for language specifier");
                        ratfor = (c == 'r');
                        continue;
                    case '{':
                        lgate();
                        while(getl(p) && scomp(p, L_PctCbr) != 0)
                            if(p[0]=='/' && p[1]=='*')
                                cpycom(p);
                            else
                                fprintf(fout,"%ls\n",p);
                        if(p[0] == '%') continue;
                        if (*p) error("EOF before %%%%");
                        else error("EOF before %%}");
                        break;

                    case 'x': case 'X':     /* XCU4: exclusive start conditions */
                        exclusive_flag = 1;
                        goto start;

                    case 's': case 'S':     /* start conditions */
                        exclusive_flag = 0;
start:
                        lgate();

                        while(*p && !iswspace(*p) && ((*p) != (wchar_t)',')) p++;
                        n = TRUE;
                        while(n){
                            while(*p && (iswspace(*p) || ((*p) == (wchar_t)','))) p++;
                            t = p;
                            while(*p && !iswspace(*p) && ((*p) != (wchar_t)',')) {
                                if(!isascii(*p))
                                error("None-ASCII characters in start condition.");
                                p++;
                            }
                            if(!*p) n = FALSE;
                            *p++ = 0;
                            if (*t == 0) continue;
                            i = sptr*2;
                            if(!ratfor)fprintf(fout,"# ");
                            fprintf(fout,"define %ls %d\n",t,i);
                            scopy(t,sp);
                            sname[sptr] = sp;
                            /* XCU4: save exclusive flag with start name */
                            exclusive[sptr++] = exclusive_flag;
                            sname[sptr] = 0;    /* required by lookup */
                            if(sptr >= STARTSIZE)
                                error("Too many start conditions");
                            sp += slength(sp) + 1;
                            if(sp >= schar+STARTCHAR)
                                error("Start conditions too long");
                            }
                        continue;
                    default:
                        error("Invalid request %s",p);
                        continue;
                        }   /* end of switch after seeing '%' */
                    break;
                case ' ': case '\t':        /* must be code */
                    lgate();
                    if( p[1]=='/' && p[2]=='*' ) cpycom(p);
                    else fprintf(fout, "%ls\n",p);
                    continue;
                case '/':   /* look for comments */
                    lgate();
                    if((*(p+1))=='*') cpycom(p);
                    /* FALLTHRU */
                default:        /* definition */
                    while(*p && !iswspace(*p)) p++;
                    if(*p == 0)
                        continue;
                    prev = *p;
                    *p = 0;
                    bptr = p+1;
                    yylval.cp = (CHR *)buf;
                    if(digit(buf[0]))
                        warning("Substitution strings may not begin with digits");
                    return(freturn(STR));
                }
            } else { /* still sect 1, but prev != '\n' */
                p = bptr;
                while(*p && iswspace(*p)) p++;
                if(*p == 0)
                    warning("No translation given - null string assumed");
                scopy(p,token);
                yylval.cp = (CHR *)token;
                prev = '\n';
                return(freturn(STR));
                }
            }
        error("unexpected EOF before %%%%");
        /* end of section one processing */
    } else if(sect == RULESECTION){     /* rules and actions */
        lgate();
        while(!eof){
            static int first_test=TRUE, first_value;
            static int reverse=FALSE;
            switch(c=gch()){
            case '\0':
                if(n_error)error_tail();
                return(freturn(0));
            case '\n':
                if(prev == '\n') continue;
                x = NEWE;
                break;
            case ' ':
            case '\t':
                if(prev == '\n') copy_line = TRUE;
                if(sectbegin == TRUE){
                    cpyact();
                    copy_line = FALSE;
                    while((c=gch()) && c != '\n');
                    continue;
                    }
                if(!funcflag)phead2();
                funcflag = TRUE;
                if(ratfor)fprintf(fout,"%d\n",30000+casecount);
                else fprintf(fout,"case %d:\n",casecount);
                if(cpyact()){
                    if(ratfor)fprintf(fout,"goto 30997\n");
                    else fprintf(fout,"break;\n");
                    }
                while((c=gch()) && c != '\n') {
                    if (c=='/') {
                        if((c=gch())=='*') {
                            c=gch();
                            while(c !=EOF) {
                                while (c=='*')
                                    if ((c=gch()) == '/') goto w_loop;
                                c = gch();
                            }
                            error("EOF inside comment");
                        } else
                            warning("undefined string");
                    } else if (c=='}')
                        error("illegal extra \"}\"");
                w_loop: ;
                }
                /* while ((c=gch())== ' ' || c == '\t') ; */
                /* if (!space(c)) error("undefined action string"); */
                if(peek == ' ' || peek == '\t' || sectbegin == TRUE){
                    fatal = 0;
                    n_error++;
                    error("executable statements should occur right after %%%%");
                    fatal = 1;
                    continue;
                    }
                x = NEWE;
                break;
            case '%':
                if(prev != '\n') goto character;
                if(peek == '{'){    /* included code */
                    getl(buf);
                    while(!eof&& getl(buf) && scomp(L_PctCbr,buf)!=0)
                        if(buf[0]=='/' && buf[1]=='*')
                            cpycom(buf);
                        else
                            fprintf(fout,"%ls\n",buf);
                    continue;
                    }
                if(peek == '%'){
                    c = gch();
                    c = gch();
                    x = DELIM;
                    break;
                    }
                goto character;
            case '|':
                if(peek == ' ' || peek == '\t' || peek == '\n'){
                    if(ratfor)fprintf(fout,"%d\n",30000+casecount++);
                    else fprintf(fout,"case %d:\n",casecount++);
                    continue;
                    }
                x = '|';
                break;
            case '$':
                if(peek == '\n' || peek == ' ' || peek == '\t' || peek == '|' || peek == '/'){
                    x = c;
                    break;
                    }
                goto character;
            case '^':
                                if(peekon && (prev == '}')){
                                        x = c;
                                        break;
                                }
                if(prev != '\n' && scon != TRUE) goto character;
                /* valid only at line begin */
                x = c;
                break;
            case '?':
            case '+':
            case '*':
                if(prev == '\n' ) {
                    fatal = 0;
                    n_error++;
                    error("illegal operator -- %c",c);
                    fatal = 1;
                }
                /* FALLTHRU */
            case '.':
            case '(':
            case ')':
            case ',':
            case '/':
                x = c;
                break;
            case '}':
                iter = FALSE;
                x = c;
                break;
            case '{':   /* either iteration or definition */
                if(digit(c=gch())){ /* iteration */
                    iter = TRUE;
                    if(prev=='{') first_test = TRUE;
                ieval:
                    i = 0;
                    while(digit(c)){
                        token[i++] = c;
                        c = gch();
                        }
                    token[i] = 0;
                    yylval.i = siconv(token);
                    if(first_test) {
                        first_test = FALSE;
                        first_value = yylval.i;
                    } else
                        if(first_value>yylval.i)warning("the values between braces are reversed");
                    ch = c;
                    munput('c',&ch);
                    x = ITER;
                    break;
                    }
                else {      /* definition */
                    i = 0;
                    while(c && c!='}'){
                        token[i++] = c;
                        if(i >= TOKENSIZE)
                            error("definition too long");
                        c = gch();
                        }
                    token[i] = 0;
                    i = lookup(token,def);
                    if(i < 0)
                        error("definition %ls not found",token);
                    else
                        munput('s',(CHR *)(subs[i]));
                            if (peek == '^')
                                                peekon = 1;
                    continue;
                    }
            case '<':       /* start condition ? */
                if(prev != '\n')  /* not at line begin, not start */
                    goto character;
                t = slptr;
                do {
                    i = 0;
                    if(!isascii(c = gch()))
                        error("Non-ASCII characters in start condition.");
                    while(c != ',' && c && c != '>'){
                        token[i++] = c;
                        if(i >= TOKENSIZE)
                            error("string name too long");
                        if(!isascii(c = gch()))
                            error("None-ASCII characters in start condition.");
                        }
                    token[i] = 0;
                    if(i == 0)
                        goto character;
                    i = lookup(token,sname);
                    lex_startcond_lookupval = i;
                    if(i < 0) {
                        fatal = 0;
                        n_error++;
                        error("undefined start condition %ls",token);
                        fatal = 1;
                        continue;
                        }
                    *slptr++ = i+1;
                    } while(c && c != '>');
                *slptr++ = 0;
                /* check if previous value re-usable */
                for (xp=slist; xp<t; )
                    {
                    if (scomp(xp, t)==0)
                        break;
                    while (*xp++);
                    }
                if (xp<t)
                    {
                    /* re-use previous pointer to string */
                    slptr=t;
                    t=xp;
                    }
                if(slptr > slist+STARTSIZE) /* note not packed */
                    error("Too many start conditions used");
                yylval.cp = (CHR *)t;

                /* XCU4: add XSCON */

                if (exclusive[lex_startcond_lookupval])
                    x = XSCON;
                else
                    x = SCON;
                break;
            case '"':
                i = 0;
                while((c=gch()) && c != '"' && c != '\n'){
                    if(c == '\\') c = usescape(c=gch());
                    remch(c);
                    token[i++] = c;
                    if(i >= TOKENSIZE){
                        warning("String too long");
                        i = TOKENSIZE-1;
                        break;
                        }
                    }
                if(c == '\n') {
                    yyline--;
                    warning("Non-terminated string");
                    yyline++;
                    }
                token[i] = 0;
                if(i == 0)x = NULLS;
                else if(i == 1){
                    yylval.i = (unsigned)token[0];
                    x = CHAR;
                    }
                else {
                    yylval.cp = (CHR *)token;
                    x = STR;
                    }
                break;
            case '[':
                reverse = FALSE;
                x = CCL;
                if((c = gch()) == '^'){
                    x = NCCL;
                    reverse = TRUE;
                    c = gch();
                    }
                i = 0;
                while(c != ']' && c){
                    static int light=TRUE, ESCAPE=FALSE;
                    if(c == '-' && prev == '^' && reverse){
                        symbol[(unsigned)c] = 1;
                        c = gch();
                        continue;
                    }
                    if(c == '\\') {
                        c = usescape(c=gch());
                        ESCAPE = TRUE;
                    }
                    if(c=='-' && !ESCAPE && prev!='[' && peek!=']'){
                    /* range specified */
                        if (light) {
                            c = gch();
                            if(c == '\\') 
                                c=usescape(c=gch());
                            remch(c);
                            k = c;
                            ccs=wcsetno(k);
                            if(wcsetno(j)!=ccs)
                                error("\
Character range specified between different codesets.");
                            if((unsigned)j > (unsigned)k) {
                                n = j;
                                j = k;
                                k = n;
                                }
                            if(!handleeuc)
                            if(!(('A'<=j && k<='Z') ||
                                     ('a'<=j && k<='z') ||
                                     ('0'<=j && k<='9')))
                                warning("Non-portable Character Class");
                            token[i++] = RANGE;
                            token[i++] = j;
                            token[i++] = k;
                            light = FALSE;
                        } else {
                            error("unmatched hyphen");
                            if(symbol[(unsigned)c])warning("\"%c\" redefined inside brackets",c);
                            else symbol[(unsigned)c] = 1;
                        }
                        ESCAPE = FALSE;
                    } else {
                        j = c;
                        remch(c);
                        token[i++] = c; /* Remember whatever.*/
                        light = TRUE;
                        ESCAPE = FALSE;
                    }
                    c = gch();
                }
                /* try to pack ccl's */

                token[i] = 0;
                ccp = ccl;
                while (ccp < ccptr && scomp(token, ccp) != 0) ccp++;
                if (ccp < ccptr) {  /* found in ccl */
                    yylval.cp = ccp;
                } else {            /* not in ccl, add it */
                    scopy(token,ccptr);
                    yylval.cp = ccptr;
                    ccptr += slength(token) + 1;
                    if(ccptr >= ccl+CCLSIZE)
                      error("Too many large character classes");
                }
                break;
            case '\\':
                c = usescape(c=gch());
            default:
            character:
                if(iter){   /* second part of an iteration */
                    iter = FALSE;
                    if('0' <= c && c <= '9')
                        goto ieval;
                    }
                remch(c);
                if(alpha(peek)){
                    i = 0;
                    yylval.cp = (CHR *)token;
                    token[i++] = c;
                    while(alpha(peek)) {
                        remch(token[i++] = gch());
                        if(i >= TOKENSIZE) {
                            warning("string too long");
                            i = TOKENSIZE - 1;
                            break;
                            }
                        }
                    if(peek == '?' || peek == '*' || peek == '+')
                        munput('c',&token[--i]);
                    token[i] = 0;
                    if(i == 1){
                        yylval.i = (unsigned)(token[0]);
                        x = CHAR;
                        }
                    else x = STR;
                    }
                else {
                    yylval.i = (unsigned)c;
                    x = CHAR;
                    }
                }
            scon = FALSE;
            peekon = 0;
            if((x == SCON) || (x == XSCON))
                scon = TRUE;
            sectbegin = FALSE;
            return(freturn(x));
            /* NOTREACHED */
            }
        }
    /* section three */
    lgate();
    ptail();
# ifdef DEBUG
    if(debug)
        fprintf(fout,"\n/*this comes from section three - debug */\n");
# endif

    if(getl(buf) && !eof) {
        if (sargv[optind] == NULL)
            fprintf(fout, "\n# line %d\n", yyline-1);
        else    
            fprintf(fout,
                "\n# line %d \"%s\"\n", yyline-1, sargv[optind]);
        fprintf(fout,"%ls\n",buf);
        while(getl(buf) && !eof)
            fprintf(fout,"%ls\n",buf);
        }

    return(freturn(0));
    }
/* end of yylex */
# ifdef DEBUG
freturn(i)
  int i; {
    if(yydebug) {
        printf("now return ");
        if((unsigned)i < NCH) allprint(i);
        else printf("%d",i);
        printf("   yylval = ");
        switch(i){
            case STR: case CCL: case NCCL:
                strpt(yylval.cp);
                break;
            case CHAR:
                allprint(yylval.i);
                break;
            default:
                printf("%d",yylval.i);
                break;
            }
        putchar('\n');
        }
    return(i);
    }
# endif

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/reject.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/reject.c used in: Step 990, Step 1018, Step 1022, Step 1024
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*  Copyright (c) 1989 AT&T */
/*    All Rights Reserved   */


/*  from OpenSolaris "reject.c  6.10    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)reject.c  1.4 (gritter) 11/27/05
 */

#include <stdio.h>

#ifdef EUC
#ifdef  __sun
#include <euc.h>
#include <widec.h>
#else   /* !sun */
#include <wchar.h>
#endif  /* !sun */
#include <limits.h>
#endif


#ifndef JLSLEX

#define CHR    char
#define YYTEXT yytext
#define YYLENG yyleng
#define YYINPUT yyinput
#define YYUNPUT yyunput
#define YYOUTPUT yyoutput
#define YYREJECT yyreject
#endif

#ifdef WOPTION

#define CHR    wchar_t
#define YYTEXT yytext
#define YYLENG yyleng
#define YYINPUT yyinput
#define YYUNPUT yyunput
#define YYOUTPUT yyoutput
#define YYREJECT yyreject_w
#endif

#ifdef EOPTION

#define CHR    wchar_t
#define YYTEXT yywtext
#define YYLENG yywleng
#define YYINPUT yywinput
#define YYUNPUT yywunput
#define YYOUTPUT yywoutput
#define YYREJECT yyreject_e
extern unsigned char yytext[];
extern int yyleng;
#endif

#if defined(__cplusplus) || defined(__STDC__)
extern int  yyback(int *, int);
extern int  YYINPUT(void);
extern void YYUNPUT(int);
#ifdef EUC
    static int  yyracc(int);
#else
    extern int  yyracc(int);
#endif
#ifdef EOPTION
    extern size_t   wcstombs(char *, const wchar_t *, size_t);
#endif
#endif

extern FILE *yyout, *yyin;

extern int yyprevious, *yyfnd;

extern char yyextra[];

extern int YYLENG;
extern CHR YYTEXT[];

extern struct {int *yyaa, *yybb; int *yystops; } *yylstate[], **yylsp, **yyolsp;
#if defined(__cplusplus) || defined(__STDC__)
int
YYREJECT(void)
#else
YYREJECT()
#endif
{
    for (; yylsp < yyolsp; yylsp++)
        YYTEXT[YYLENG++] = YYINPUT();
    if (*yyfnd > 0)
        return (yyracc(*yyfnd++));
    while (yylsp-- > yylstate) {
        YYUNPUT(YYTEXT[YYLENG-1]);
        YYTEXT[--YYLENG] = 0;
        if (*yylsp != 0 && (yyfnd = (*yylsp)->yystops) && *yyfnd > 0)
            return (yyracc(*yyfnd++));
    }
#ifdef EOPTION
    yyleng = wcstombs((char *)yytext, YYTEXT, YYLENG*MB_LEN_MAX);
#endif
    if (YYTEXT[0] == 0)
        return (0);
    YYLENG = 0;
#ifdef EOPTION
    yyleng = 0;
#endif
    return (-1);
}

#ifdef  EUC
static
#endif
#if defined(__cplusplus) || defined(__STDC__)
int
yyracc(int m)
#else
yyracc(m)
#endif
{
    yyolsp = yylsp;
    if (yyextra[m]) {
        while (yyback((*yylsp)->yystops, -m) != 1 && yylsp > yylstate) {
            yylsp--;
            YYUNPUT(YYTEXT[--YYLENG]);
        }
    }
    yyprevious = YYTEXT[YYLENG-1];
    YYTEXT[YYLENG] = 0;
#ifdef EOPTION
    yyleng = wcstombs((char *)yytext, YYTEXT, YYLENG*MB_LEN_MAX);
#endif
    return (m);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sub1.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sub1.c used in: Step 990, Step 1006
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.
 * All rights reserved.
 * Use is subject to license terms.
 */

/*  Copyright (c) 1988 AT&T */
/*  All Rights Reserved */

/*  from OpenSolaris "sub1.c    6.18    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)sub1.c    1.5 (gritter) 11/26/05
 */

#include "ldefs.c"
#include <limits.h>
#include <wchar.h>
#include <ctype.h>
#include <stdarg.h>

/*
 * return next line of input, throw away trailing '\n'
 * and also throw away trailing blanks (spaces and tabs)
 * returns 0 if eof is had immediately
 */

CHR *
getl(CHR *p)
{
    int c;
    CHR *s, *t, *u = NULL;
    int blank = 0;

    t = s = p;
    while (((c = gch()) != 0) && c != '\n') {
        if (t >= &p[BUF_SIZ])
            error("definitions too long");
        if (c == ' ' || c == '\t') {
            if (!blank) {
            blank = 1;
            u = t;
            }
        } else
            blank = 0;

        *t++ = c;
    }
    if (blank)
        *u = 0;
    else
        *t = 0;

    if (c == 0 && s == t)
        return ((CHR *) 0);
    prev = '\n';
    pres = '\n';
    return (s);
}

int
space(int ch)
{
    switch (ch) {
        case ' ':
        case '\t':
        case '\n':
            return (1);
    }
    return (0);
}

int
digit(int c)
{
    return (c >= '0' && c <= '9');
}

/* VARARGS1 */
void
error(const char *s, ...)
{
    va_list ap;

    /* if(!eof) */
    if (!yyline)
        fprintf(errorf, "Command line: ");
    else {
        fprintf(errorf, !no_input ? "" : "\"%s\":", sargv[optind]);
        fprintf(errorf, "line %d: ", yyline);
    }
    fprintf(errorf, "Error: ");
    va_start(ap, s);
    vfprintf(errorf, s, ap);
    va_end(ap);
    putc('\n', errorf);
    if (fatal)
        error_tail();
}

void
error_tail(void)
{
#ifdef DEBUG
    if (debug && sect != ENDSECTION) {
        sect1dump();
        sect2dump();
    }
#endif

    if (report == 1)
        statistics();
    exit(1);
    /* NOTREACHED */
}

/* VARARGS1 */
void
warning(const char *s, ...)
{
    va_list ap;

    if (!eof)
        if (!yyline)
            fprintf(errorf, "Command line: ");
        else {
            fprintf(errorf, !no_input?"":"\"%s\":", sargv[optind]);
            fprintf(errorf, "line %d: ", yyline);
        }
    fprintf(errorf, "Warning: ");
    va_start(ap, s);
    vfprintf(errorf, s, ap);
    va_end(ap);
    putc('\n', errorf);
    fflush(errorf);
    if (fout)
        fflush(fout);
    fflush(stdout);
}

int
index(int a, CHR *s)
{
    int k;
    for (k = 0; s[k]; k++)
        if (s[k] == a)
            return (k);
    return (-1);
}

int
alpha(int c)
{
    return ('a' <= c && c <= 'z' ||
        'A' <= c && c <= 'Z');
}

int
printable(int c)
{
    return (c > 040 && c < 0177);
}

void
lgate(void)
{
    char fname[20];

    if (lgatflg)
        return;
    lgatflg = 1;
    if (fout == NULL) {
        sprintf(fname, "lex.yy.%c", ratfor ? 'r' : 'c');
        fout = fopen(fname, "w");
    }
    if (fout == NULL)
        error("Can't open %s", fname);
    if (ratfor)
        fprintf(fout, "#\n");
    phead1();
}

/*
 * scopy(ptr to str, ptr to str) - copy first arg str to second
 * returns ptr to second arg
 */
void
scopy(CHR *s, CHR *t)
{
    CHR *i;
    i = t;
    while (*i++ = *s++);
}

/*
 * convert string t, return integer value
 */
int
siconv(CHR *t)
{
    int i, sw;
    CHR *s;
    s = t;
    while (space(*s))
        s++;
    if (!digit(*s) && *s != '-')
        error("missing translation value");
    sw = 0;
    if (*s == '-') {
        sw = 1;
        s++;
    }
    if (!digit(*s))
        error("incomplete translation format");
    i = 0;
    while ('0' <= *s && *s <= '9')
        i = i * 10 + (*(s++)-'0');
    return (sw ? -i : i);
}

/*
 * slength(ptr to str) - return integer length of string arg
 * excludes '\0' terminator
 */
int
slength(CHR *s)
{
    int n;
    CHR *t;
    t = s;
    for (n = 0; *t++; n++);
    return (n);
}

/*
 * scomp(x,y) - return -1 if x < y,
 *      0 if x == y,
 *      return 1 if x > y, all lexicographically
 */
int
scomp(CHR *x, CHR *y)
{
    CHR *a, *d;
    a = (CHR *) x;
    d = (CHR *) y;
    while (*a || *d) {
        if (*a > *d)
            return (1);
        if (*a < *d)
            return (-1);
        a++;
        d++;
    }
    return (0);
}

int
ctrans(CHR **ss)
{
    int c, k;
    if ((c = **ss) != '\\')
        return (c);
    switch (c = *++*ss) {
    case 'a':
        c = '\a';
        warning("\\a is ANSI C \"alert\" character");
        break;
    case 'v': c = '\v'; break;
    case 'n': c = '\n'; break;
    case 't': c = '\t'; break;
    case 'r': c = '\r'; break;
    case 'b': c = '\b'; break;
    case 'f': c = 014; break;       /* form feed for ascii */
    case '\\': c = '\\'; break;
    case 'x': {
        int dd;
        warning("\\x is ANSI C hex escape");
        if (digit((dd = *++*ss)) ||
            ('a' <= dd && dd <= 'f') ||
            ('A' <= dd && dd <= 'F')) {
            c = 0;
            while (digit(dd) ||
                ('A' <= dd && dd <= 'F') ||
                ('a' <= dd && dd <= 'f')) {
                if (digit(dd))
                    c = c*16 + dd - '0';
                else if (dd >= 'a')
                    c = c*16 + 10 + dd - 'a';
                else
                    c = c*16 + 10 + dd - 'A';
                dd = *++*ss;
            }
        } else
            c = 'x';
        break;
        }
    case '0': case '1': case '2': case '3':
    case '4': case '5': case '6': case '7':
        c -= '0';
        while ((k = *(*ss+1)) >= '0' && k <= '7') {
            c = c*8 + k - '0';
            (*ss)++;
        }
        break;
    }
    return (c);
}

void
cclinter(int sw)
{
    /* sw = 1 ==> ccl */
    int i, j, k;
    int m;
    if (!sw) { /* is NCCL */
        for (i = 1; i < ncg; i++)
            symbol[i] ^= 1; /* reverse value */
    }
    for (i = 1; i < ncg; i++)
        if (symbol[i])
            break;
    if (i >= ncg)
        return;
    i = cindex[i];
    /* see if ccl is already in our table */
    j = 0;
    if (i) {
        for (j = 1; j < ncg; j++) {
            if ((symbol[j] && cindex[j] != i) ||
                (!symbol[j] && cindex[j] == i))
                break;
        }
    }
    if (j >= ncg)
        return;     /* already in */
    m = 0;
    k = 0;
    for (i = 1; i < ncg; i++) {
        if (symbol[i]) {
            if (!cindex[i]) {
                cindex[i] = ccount;
                symbol[i] = 0;
                m = 1;
            } else
                k = 1;
        }
    }
    /* m == 1 implies last value of ccount has been used */
    if (m)
        ccount++;
    if (k == 0)
        return; /* is now in as ccount wholly */
    /* intersection must be computed */
    for (i = 1; i < ncg; i++) {
        if (symbol[i]) {
            m = 0;
            j = cindex[i];  /* will be non-zero */
            for (k = 1; k < ncg; k++) {
                if (cindex[k] == j) {
                    if (symbol[k])
                        symbol[k] = 0;
                    else {
                        cindex[k] = ccount;
                        m = 1;
                    }
                }
            }
            if (m)
                ccount++;
        }
    }
}

int
usescape(int c)
{
    char d;
    switch (c) {
    case 'a':
        c = '\a';
        warning("\\a is ANSI C \"alert\" character"); break;
    case 'v': c = '\v'; break;
    case 'n': c = '\n'; break;
    case 'r': c = '\r'; break;
    case 't': c = '\t'; break;
    case 'b': c = '\b'; break;
    case 'f': c = 014; break;       /* form feed for ascii */
    case 'x': {
        int dd;
        if (digit((dd = gch())) ||
            ('A' <= dd && dd <= 'F') ||
            ('a' <= dd && dd <= 'f')) {
            c = 0;
            while (digit(dd) ||
                ('A' <= dd && dd <= 'F') ||
                ('a' <= dd && dd <= 'f')) {
                if (digit(dd))
                    c = c*16 + dd - '0';
                else if (dd >= 'a')
                    c = c*16 + 10 + dd - 'a';
                else
                    c = c*16 + 10 + dd - 'A';
                if (!digit(peek) &&
                    !('A' <= peek && peek <= 'F') &&
                    !('a' <= peek && peek <= 'f'))
                    break;
                dd = gch();
            }

        } else
            c = 'x';
        break;
    }
    case '0': case '1': case '2': case '3':
    case '4': case '5': case '6': case '7':
        c -= '0';
        while ('0' <= (d = gch()) && d <= '7') {
            c = c * 8 + (d-'0');
            if (!('0' <= peek && peek <= '7')) break;
            }

        break;
    }

    if (handleeuc && !isascii(c)) {
        char tmpchar = c & 0x00ff;
        wchar_t wc;
        mbtowc(&wc, &tmpchar, sizeof (tmpchar));
        c = wc;
    }
    return (c);
}

int
lookup(CHR *s, CHR **t)
{
    int i;
    i = 0;
    while (*t) {
        if (scomp(s, *t) == 0)
            return (i);
        i++;
        t++;
    }
    return (-1);
}

void
cpycom(CHR *p)
{
    static CHR *t;
    static int c;
    t = p;

    if (sargv[optind] == NULL)
        fprintf(fout, "\n# line %d\n", yyline);
    else
        fprintf(fout, "\n# line %d \"%s\"\n", yyline, sargv[optind]);

    putc(*t, fout), t++;
    putc(*t, fout), t++;
    while (*t) {
        while (*t == '*') {
            putc(*t, fout), t++;
            if (*t == '/')
                goto backcall;
        }
        /*
         * FIX BUG #1058428, not parsing comments correctly
         * that span more than one line
         */
        if (*t != '\0')
            putc(*t, fout), t++;
    }
    putc('\n', fout);
    while (c = gch()) {
        while (c == '*') {
            putc(c, fout);
            if ((c = gch()) == '/') {
                while ((c = gch()) == ' ' || c == '\t');
                if (!space(c))
                    error("unacceptable statement");
                prev = '\n';
                goto backcall;
            }
        }
        putc(c, fout);
    }
    error("unexpected EOF inside comment");
backcall:
    putc('/', fout);
    putc('\n', fout);
}

/*
 * copy C action to the next ; or closing
 */
int
cpyact(void)
{
    int brac, c, mth;
    static int sw, savline;

    brac = 0;
    sw = TRUE;
    savline = yyline;

    if (sargv[optind] == NULL)
        fprintf(fout, "\n# line %d\n", yyline);
    else
        fprintf(fout, "\n# line %d \"%s\"\n", yyline, sargv[optind]);

    while (!eof) {
        c = gch();
    swt:
        switch (c) {
        case '|':
            if (brac == 0 && sw == TRUE) {
                if (peek == '|')
                    gch(); /* eat up an extra '|' */
                return (0);
            }
            break;
        case ';':
            if (brac == 0) {
                putwc(c, fout);
                putc('\n', fout);
                return (1);
            }
            break;
        case '{':
            brac++;
            savline = yyline;
            break;
        case '}':
            brac--;
            if (brac == 0) {
                putwc(c, fout);
                putc('\n', fout);
                return (1);
            }
            break;
        case '/':
            putwc(c, fout);
            c = gch();
            if (c != '*')
                goto swt;
            putwc(c, fout);
            savline = yyline;
            while (c = gch()) {
                while (c == '*') {
                    putwc(c, fout);
                    if ((c = gch()) == '/') {
                        putc('/', fout);
                        while ((c = gch()) == ' ' ||
                            c == '\t' || c == '\n')
                            putwc(c, fout);
                        goto swt;
                    }
                }
                putc(c, fout);
            }
            yyline = savline;
            error("EOF inside comment");
            /* NOTREACHED */
            break;
        case '\'': /* character constant */
        case '"': /* character string */
            mth = c;
            putwc(c, fout);
            while (c = gch()) {
                if (c == '\\') {
                    putwc(c, fout);
                    c = gch();
                }
                else
                    if (c == mth)
                        goto loop;
                putwc(c, fout);
                if (c == '\n') {
                    yyline--;
                    error(
"Non-terminated string or character constant");
                }
            }
            error("EOF in string or character constant");
            /* NOTREACHED */
            break;
        case '\0':
            yyline = savline;
            error("Action does not terminate");
            /* NOTREACHED */
            break;
        default:
            break; /* usual character */
        }
    loop:
        if (c != ' ' && c != '\t' && c != '\n')
            sw = FALSE;
        putwc(c, fout);
        if (peek == '\n' && !brac && copy_line) {
            putc('\n', fout);
            return (1);
        }
    }
    error("Premature EOF");
    return (0);
}

int
gch(void)
{
    int c;
    prev = pres;
    c = pres = peek;
    peek = pushptr > pushc ? *--pushptr : getwc(fin);
    while (peek == EOF) {
        if (no_input) {
            if (!yyline)
                error("Cannot read from -- %s",
                sargv[optind]);
            if (optind < sargc-1) {
                yyline = 0;
                if (fin != stdin)
                    fclose(fin);
                fin = fopen(sargv[++optind], "r");
                if (fin == NULL)
                    error("Cannot open file -- %s",
                    sargv[optind]);
                peek = getwc(fin);
            } else
                break;
        } else {
            if (fin != stdin)
                fclose(fin);
            if (!yyline)
                error("Cannot read from -- standard input");
            else
                break;
        }
    }
    if (c == EOF) {
        eof = TRUE;
        return (0);
    }
    if (c == '\n')
        yyline++;
    return (c);
}

int
mn2(int a, intptr_t d, intptr_t c)
{
    if (tptr >= treesize) {
        tptr++;
        error("Parse tree too big %s",
            (treesize == TREESIZE ? "\nTry using %e num" : ""));
    }
    if (d >= treesize) {
        error("Parse error");
    }
    name[tptr] = a;
    left[tptr] = d;
    right[tptr] = c;
    parent[tptr] = 0;
    nullstr[tptr] = 0;
    switch (a) {
    case RSTR:
        parent[d] = tptr;
        break;
    case BAR:
    case RNEWE:
        if (nullstr[d] || nullstr[c])
            nullstr[tptr] = TRUE;
        parent[d] = parent[c] = tptr;
        break;
    case RCAT:
    case DIV:
        if (nullstr[d] && nullstr[c])
            nullstr[tptr] = TRUE;
        parent[d] = parent[c] = tptr;
        break;
    /* XCU4: add RXSCON */
    case RXSCON:
    case RSCON:
        parent[d] = tptr;
        nullstr[tptr] = nullstr[d];
        break;
#ifdef DEBUG
    default:
        warning("bad switch mn2 %d %d", a, d);
        break;
#endif
    }
    return (tptr++);
}

int
mn1(int a, intptr_t d)
{
    if (tptr >= treesize) {
        tptr++;
        error("Parse tree too big %s",
        (treesize == TREESIZE ? "\nTry using %e num" : ""));
    }
    name[tptr] = a;
    left[tptr] = d;
    parent[tptr] = 0;
    nullstr[tptr] = 0;
    switch (a) {
    case RCCL:
    case RNCCL:
        if (slength((CHR *)d) == 0)
            nullstr[tptr] = TRUE;
        break;
    case STAR:
    case QUEST:
        nullstr[tptr] = TRUE;
        parent[d] = tptr;
        break;
    case PLUS:
    case CARAT:
        nullstr[tptr] = nullstr[d];
        parent[d] = tptr;
        break;
    case S2FINAL:
        nullstr[tptr] = TRUE;
        break;
#ifdef DEBUG
    case FINAL:
    case S1FINAL:
        break;
    default:
        warning("bad switch mn1 %d %d", a, d);
        break;
#endif
    }
    return (tptr++);
}

int
mn0(int a)
{
    if (tptr >= treesize) {
        tptr++;
        error("Parse tree too big %s",
            (treesize == TREESIZE ? "\nTry using %e num" : ""));
    }

    name[tptr] = a;
    parent[tptr] = 0;
    nullstr[tptr] = 0;
    if (ISOPERATOR(a)) {
        switch (a) {
        case DOT: break;
        case RNULLS: nullstr[tptr] = TRUE; break;
#ifdef DEBUG
        default:
            warning("bad switch mn0 %d", a);
            break;
#endif
        }
    }
    return (tptr++);
}

void
munput(int t, CHR *p)
{
    int i, j;
    if (t == 'c') {
        *pushptr++ = peek;
        peek = *p;
    } else if (t == 's') {
        *pushptr++ = peek;
        peek = p[0];
        i = slength(p);
        for (j = i - 1; j >= 1; j--)
            *pushptr++ = p[j];
    }
    if (pushptr >= pushc + TOKENSIZE)
        error("Too many characters pushed");
}

int
dupl(int n)
{
    /* duplicate the subtree whose root is n, return ptr to it */
    int i;
    i = name[n];
    if (!ISOPERATOR(i))
        return (mn0(i));
    switch (i) {
    case DOT:
    case RNULLS:
        return (mn0(i));
    case RCCL: case RNCCL: case FINAL: case S1FINAL: case S2FINAL:
        return (mn1(i, left[n]));
    case STAR: case QUEST: case PLUS: case CARAT:
        return (mn1(i, dupl(left[n])));

    /* XCU4: add RXSCON */
    case RSTR: case RSCON: case RXSCON:
        return (mn2(i, dupl(left[n]), right[n]));
    case BAR: case RNEWE: case RCAT: case DIV:
        return (mn2(i, dupl(left[n]), dupl(right[n])));
    }
    return (0);
}

#ifdef DEBUG
void
allprint(CHR c)
{
    switch (c) {
    case 014:
        printf("\\f");
        charc++;
        break;
    case '\n':
        printf("\\n");
        charc++;
        break;
    case '\t':
        printf("\\t");
        charc++;
        break;
    case '\b':
        printf("\\b");
        charc++;
        break;
    case ' ':
        printf("\\_");
        break;
    default:
        if (!iswprint(c)) {
            printf("\\x%-2x", c); /* up to fashion. */
            charc += 3;
        } else
            putwc(c, stdout);
        break;
    }
    charc++;
}

void
strpt(CHR *s)
{
    charc = 0;
    while (*s) {
        allprint(*s++);
        if (charc > LINESIZE) {
            charc = 0;
            printf("\n\t");
        }
    }
}

void
sect1dump(void)
{
    int i;
    printf("Sect 1:\n");
    if (def[0]) {
        printf("str trans\n");
        i = -1;
        while (def[++i])
            printf("%ls\t%ls\n", def[i], subs[i]);
    }
    if (sname[0]) {
        printf("start names\n");
        i = -1;
        while (sname[++i])
            printf("%ls\n", sname[i]);
    }
    if (chset == TRUE) {
        printf("char set changed\n");
        for (i = 1; i < NCH; i++) {
            if (i != ctable[i]) {
                allprint(i);
                putchar(' ');
                iswprint(ctable[i]) ?
                    putwc(ctable[i], stdout) :
                    printf("%d", ctable[i]);
                putchar('\n');
            }
        }
    }
}

void
sect2dump(void)
{
    printf("Sect 2:\n");
    treedump();
}

void
treedump(void)
{
    int t;
    CHR *p;
    printf("treedump %d nodes:\n", tptr);
    for (t = 0; t < tptr; t++) {
        printf("%4d ", t);
        parent[t] ? printf("p=%4d", parent[t]) : printf("      ");
        printf("  ");
        if (!ISOPERATOR(name[t])) {
            allprint(name[t]);
        } else
            switch (name[t]) {
            case RSTR:
                printf("%ld ", (long)left[t]);
                allprint(right[t]);
                break;
            case RCCL:
                printf("ccl ");
                strpt((CHR *)left[t]);
                break;
            case RNCCL:
                printf("nccl ");
                strpt((CHR *)left[t]);
                break;
            case DIV:
                printf("/ %ld %ld",
                    (long)left[t], (long)right[t]);
                break;
            case BAR:
                printf("| %ld %ld",
                    (long)left[t], (long)right[t]);
                break;
            case RCAT:
                printf("cat %ld %ld",
                    (long)left[t], (long)right[t]);
                break;
            case PLUS:
                printf("+ %ld", (long)left[t]);
                break;
            case STAR:
                printf("* %ld", (long)left[t]);
                break;
            case CARAT:
                printf("^ %ld", (long)left[t]);
                break;
            case QUEST:
                printf("? %ld", (long)left[t]);
                break;
            case RNULLS:
                printf("nullstring");
                break;
            case FINAL:
                printf("final %ld", (long)left[t]);
                break;
            case S1FINAL:
                printf("s1final %ld", (long)left[t]);
                break;
            case S2FINAL:
                printf("s2final %ld", (long)left[t]);
                break;
            case RNEWE:
                printf("new %ld %ld",
                    (long)left[t], (long)right[t]);
                break;

            /* XCU4: add RXSCON */
            case RXSCON:
                p = (CHR *)right[t];
                printf("exstart %s", sname[*p++-1]);
                while (*p)
                    printf(", %ls", sname[*p++-1]);
                printf(" %ld", (long)left[t]);
                break;
            case RSCON:
                p = (CHR *)right[t];
                printf("start %s", sname[*p++-1]);
                while (*p)
                    printf(", %ls", sname[*p++-1]);
                printf(" %ld", (long)left[t]);
                break;
            case DOT:
                printf("dot");
                break;
            default:
                printf(
                "unknown %d %ld %ld", name[t],
                    (long)left[t], (long)right[t]);
                break;
            }
        if (nullstr[t])
            printf("\t(null poss.)");
        putchar('\n');
    }
}
#endif

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sub3.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sub3.c used in: Step 990, Step 1008
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.
 * All rights reserved.
 * Use is subject to license terms.
 */

/*  from OpenSolaris "sub3.c    1.8 05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)sub3.c    1.4 (gritter) 11/26/05
 */

/*
 * sub3.c ... ALE enhancement.
 * Since a typical Asian language has a huge character set, it is not
 * ideal to index an array by a character code itself, which requires
 * as large as 2**16 entries per array.
 * To get arround this problem, we identify a set of characters that
 * causes the same transition on all states and call it character group.
 * Every character in a same character group has a unique number called
 * character group id.  A function yycgid(c) maps the character c (in process
 * code) to the id.  This mapping is determined by analyzing all regular
 * expressions in the lex program.
 *
 */
#include    <stdlib.h>
#ifdef  __sun
#include    <widec.h>
#endif
#include    "search.h"
#include    "ldefs.c"
#include    <ctype.h>

/*
 * "lchar" stands for linearized character.  It is a variant of
 * process code.  AT&T's 16-bit process code has a drawback in which
 * for three three process code C, D and E where C <= D <= E,
 * codeset(C)==codeset(E) does not mean codeset(D)==codeset(C).
 * In other words, four codesets alternates as the magnitude
 * of character increases.
 * The lchar representation holds this property:
 *   If three lchar C', D' and E' have the relationship C' < D' <  E' and
 *   codeset(C') == codeset(E') then D' is guaranteed to belong to
 *   the same codeset as C' and E'.
 * lchar is implemented as 32 bit entities and the function linearize()
 * that maps a wchar_t to lchar is defined below.  There is no
 * reverse function for it though.
 * The 32-bit process code by AT&T, used only for Taiwanese version at the
 * time of wrting, has no such problem and we use it as it is.
 */

lchar   yycgidtbl[MAXNCG] = {
    0,      /* For ease of computation of the id. */
    '\n',       /* Newline is always special because '.' exclude it. */
    0x000000ff,     /* The upper limit of codeset 0. */
    0x20ffffff, /* The upper limit of codeset 2. */
    0x40ffffff  /* The upper limit of codeset 3. */
/*  0x60ffffff     The upper limit of codeset 1. */
    /* Above assumes the number of significant bits of wchar_t is <= 24. */
};
int ncgidtbl = 5; /* # elements in yycgidtbl. */
int ncg; /* Should set to ncgidtbl*2; this is the largest value yycgid() */
        /* returns plus 1. */

static void setsymbol(int i);

/*
 * For given 16-bit wchar_t (See NOTE), lchar is computed as illustrated below:
 *
 *  wc: axxxxxxbyyyyyyy
 *
 * returns: 0ab0000000000000axxxxxxxbyyyyyyy
 *
 * linearize() doesn't do any if compiled with 32-bit wchar_t, use of
 * which is flagged with LONG_WCHAR_T macro.
 * NOTE:
 * The implementation is highly depends on the process code representation.
 * This function should be modified when 32-bit process code is used.
 * There is no need to keep 'a' and 'b' bits in the lower half of lchar.
 * You can actually omit these and squeeze the xxxxxx part one bit right.
 * We don't do that here just in sake of speed.
 */
lchar
linearize(wchar_t wc)
{
#ifdef LONG_WCHAR_T
    return ((lchar)wc); /* Don't do anything. */
#else

    lchar   prefix;
    switch (wc&0x8080) {
    case 0x0000: prefix = 0x00000000; break;
    case 0x0080: prefix = 0x20000000; break;
    case 0x8000: prefix = 0x40000000; break;
    case 0x8080: prefix = 0x60000000; break;
    }
    return (prefix|wc);
#endif
}

/* compare liniear characters pointed to by pc1 and pc2 */
int
cmplc(const void *arg1, const void *arg2)
{
    lchar *pc1 = (lchar *)arg1;
    lchar *pc2 = (lchar *)arg2;

    if (*pc1 > *pc2)
        return (1);
    else if (*pc1 == *pc2)
        return (0);
    else
        return (-1);
}

void
remch(wchar_t c)
{
    lchar   lc = linearize(c);

    /*
     * User-friendliness consideration:
     * Make sure no EUC chars are used in reg. exp.
     */
    if (!handleeuc) {
        if (!isascii(c))
            if (iswprint(c))
            warning(
"Non-ASCII character '%lc' in pattern; use -w or -e lex option.", c);
            else warning(
"Non-ASCII character of value %#x in pattern; use -w or -e lex option.", c);
        /* In any case, we don't need to construct ncgidtbl[]. */
        return;
    }

    xlsearch(&lc, yycgidtbl,
        (unsigned *)&ncgidtbl, sizeof (lchar), cmplc);
}

void
sortcgidtbl(void)
{
    if (!handleeuc)
        return;
    qsort(yycgidtbl, ncgidtbl, sizeof (lchar), cmplc);
}

/*
 * int yycgid(wchar_t c)
 *  Takes c and returns its character group id, determind by the
 *  following algorithm.  The program also uses the binary search
 *  algorithm, generalized from Knuth (6.2.1) Algorithm B.
 *
 *  This function computes the "character group id" based on
 *  a table yycgidtbl of which each lchar entry is pre-sorted
 *  in ascending sequence  The number of valid entries is given
 *  by YYNCGIDTBL.  There is no duplicate entries in yycgidtbl.
 *      const int YYNCGIDTBL;
 *      lchar   yycgidtbl[YYNCGIDTBL];
 *
 *  yycgidtbl[0] is guaranteed to have zero.
 *
 *  For given c, yycgid(c) returns:
 *      2*i iff yycgidtbl[i] == lc
 *      2*i+1   iff yycgidtbl[i] < lc < yycgidtbl[i+1]
 *      YYNCGIDTBL*2-1
 *          iff yycgidtbl[YYNCGIDTBL-1] < lc
 *  where lc=linearize(c).
 *
 *  Some interesting properties.:
 *  1.  For any c, 0 <= yycgid(c) <= 2*YYNCGIDTBL-1
 *  2.  yycgid(c) == 0  iff  c == 0.
 *  3.  For any wchar_t c and d, if linearize(c) < linearize(d) then
 *      yycgid(c) <= yycgid(d).
 *  4.  For any wchar_t c and d, if yycgid(c) < yycgid(d) then
 *      linearize(c) < linearize(d).
 */
#define YYNCGIDTBL ncgidtbl

int
yycgid(wchar_t c)
{
    int first = 0;
    int last = YYNCGIDTBL - 1;
    lchar lc;

    /*
     * In ASCII compat. mode, each character forms a "group" and the
     * group-id is itself...
     */
    if (!handleeuc)
        return (c);

    lc = linearize(c);

    /* An exceptional case: yycgidtbl[YYNCGIDTBL-1] < lc */
    if (yycgidtbl[YYNCGIDTBL - 1] < lc)
        return (YYNCGIDTBL*2 - 1);

    while (last >= 0) {
        int i = (first+last)/2;
        if (lc == yycgidtbl[i])
            return (2*i);   /* lc  exactly matches an element. */
        else if (yycgidtbl[i] < lc) {
            if (lc < yycgidtbl[i+1])
                return (2*i+1); /* lc is in between two elements. */
            else
                first = i + 1;
        } else
            last = i - 1;
    }
    error(
    "system error in yycgid():binary search failed for c=0x%04x\n", c);
    return (0);
}

/*
 * repbycgid --- replaces each character in the parsing tree by its
 * character group id.   This, however, should be called even in
 * the ASCII compat. mode to process DOT nodes and to call cclinter()
 * for the DOT and CCL nodes.
 */
void
repbycgid(void)
{
    int i, c;

    for (i = 0; i < tptr; ++i) {
        c = name[i];
        if (!ISOPERATOR(c)) {
        /* If not an operator, it must be a char.  */
            name[i] = yycgid((wchar_t)c); /* So replace it. */
#ifdef DEBUG
            if (debug) {
                printf("name[%d]:'%c'->%d;\n", i, c, name[i]);
            }
#endif
        } else if (c == RSTR) {
            c = right[i];
            right[i] = yycgid((wchar_t)c);
#ifdef DEBUG
            if (debug) {
                printf(
                "name[%d].right:'%c'->%d;\n", i, c, right[i]);
            }
#endif
        } else if ((c == RCCL) || (c == RNCCL)) {
            CHR cc, *s;
            int j;
            CHR ccltoken[CCLSIZE];
            CHR *ccp;
            int m;
            /*
             * This node represetns a character class RE [ccccc]
             * s points to the string of characters that forms
             * the class and/or a special prefix notation
             * <RANGE>XY which corresponds to the RE X-Y,
             * characters in the range of X and Y.  Here,
             * X <= Y is guranteed.
             * We transform these characters into a string
             * of sorted character group ids.
             *
             * There is another mechanism of packing tables
             * that is inherited from the ASCII lex.  Call of
             * cclinter() is required for this packing.
             * This used to be done as yylex() reads the lex
             * rules but we have to do this here because the
             * transition table is made to work on the char-group
             * ids and the mapping cannot be determined until
             * the entire file is read.
             */
#ifdef DEBUG
            if (debug) {
                printf("name[%d]:R[N]CCL of \"", i);
                strpt((CHR *)left[i]);
                printf(" -> {");
            }
#endif
            /* Prepare symbol[] for cclinter(). */
            for (j = 0; j < ncg; ++j)
                symbol[j] = FALSE;

            s = (CHR *) left[i];
            while (cc = *s++) {
                if (cc == RANGE) {
                    int low, high, i;
                    /*
                     * Special form: <RANGE>XY
                     * This means the range X-Y.
                     * We mark all symbols[]
                     * elements for yycgid(X) thru
                     * yycgid(Y), inclusively.
                     */
                    low = yycgid(*s++);
                    high = yycgid(*s++);
                    for (i = low; i <= high; ++i)
                        setsymbol(i);
                } else {
                    setsymbol(yycgid(cc));
                }
            }

            /* Now make a transformed string of cgids. */
            s = ccptr;
            m = 0;
            for (j = 0; j < ncg; ++j)
                if (symbol[j]) {
                    ccltoken[m++] = (CHR)j;
#ifdef DEBUG
                    if (debug) printf("%d, ", j);
#endif
                }

#ifdef DEBUG
            if (debug) printf("}\n");
#endif
            ccltoken[m] = 0;
            ccp = ccl;
            while (ccp < ccptr && scomp(ccltoken, ccp) != 0)
                ccp++;
            if (ccp < ccptr) {  /* character class found in ccl */
                left[i] = (intptr_t)ccp;
            } else { /* not in ccl, add it */
                left[i] = (intptr_t)ccptr;
                scopy(ccltoken, ccptr);
                ccptr += slength(ccltoken) + 1;
                if (ccptr > ccl + CCLSIZE)
                error("Too many large character classes");
            }
            cclinter(c == RCCL);
        } else if (c == DOT) {
            if (psave == 0) { /* First DOT node. */
                int j, nlid;
                /*
                 * Make symbol[k]=TRUE for all k
                 *  except k == yycgid('\n').
                 */
                nlid = yycgid('\n');
                psave = ccptr;
                for (j = 1; j < ncg; ++j) {
                    if (j == nlid) {
                        symbol[j] = FALSE;
                    } else {
                        symbol[j] = TRUE;
                        *ccptr++ = (CHR) j;
                    }
                }
                *ccptr++ = 0;
                if (ccptr > ccl + CCLSIZE)
                    error("Too many large character classes");
            }
            /* Mimic mn1(RCCL,psave)... */
            name[i] = RCCL;
            left[i] = (intptr_t)psave;
            cclinter(1);
        }
    }
#ifdef DEBUG
    if (debug) {
        printf("treedump after repbycgid().\n");
        treedump();
    }
#endif
}

static void
setsymbol(int i)
{
    if (i > sizeof (symbol))
        error("setsymbol: (SYSERR) %d out of range", i);
    symbol[i] = TRUE;
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/sgs.h

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/sgs.h used in: Step 993
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)main.c    1.2 (gritter) 6/14/05
 */
#if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
#define USED    __attribute__ ((used))
#elif defined __GNUC__
#define USED    __attribute__ ((unused))
#else
#define USED
#endif
static const char sccsid[] USED = "@(#)yacc.sl  2.6 (gritter) 11/26/05";
static const char pkg[] = "Heirloom Development Tools";
static const char rel[] = "2.6 (gritter) 11/26/05";
/* SLIST */
/*
dextern: * Sccsid @(#)dextern   1.6 (gritter) 11/10/05
getopt.c: * Sccsid @(#)getopt.c 1.8 (gritter) 8/2/05
libmai.c: * Sccsid @(#)libmai.c 1.3 (gritter) 6/18/05
libzer.c: * Sccsid @(#)libzer.c 1.3 (gritter) 6/18/05
y1.c: * Sccsid @(#)y1.c 1.7 (gritter) 11/26/05
y2.c: * Sccsid @(#)y2.c 1.11 (gritter) 11/26/05
y3.c: * Sccsid @(#)y3.c 1.5 (gritter) 11/26/05
y4.c: * Sccsid @(#)y4.c 1.5 (gritter) 11/26/05
y5.c: * Sccsid @(#)y5.c 1.1 (gritter) 6/25/05
yaccpar: * Sccsid @(#)yaccpar   1.5 (gritter) 11/26/05
*/

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/getopt.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/getopt.c used in: Step 996
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)getopt.c  1.9 (gritter) 4/2/07
 */
/* from OpenSolaris "getopt.c   1.23    05/06/08 SMI" */

/*  Copyright (c) 1988 AT&T */
/*    All Rights Reserved   */


/*
 * See getopt(3C) and SUS/XPG getopt() for function definition and
 * requirements.
 *
 * This actual implementation is a bit looser than the specification
 * as it allows any character other than ':' to be used as an option
 * character - The specification only guarantees the alnum characters
 * ([a-z][A-Z][0-9]).
 */

#include <unistd.h>
#include <string.h>
#include <stdio.h>

char    *optarg = NULL;
int optind = 1;
int opterr = 1;
int optopt = 0;

#define ERR(s, c)   err(s, c, optstring, argv[0])
static void
err(const char *s, int c, const char *optstring, const char *argv0)
{
    char errbuf[256], *ep = errbuf;
    const char  *cp;

    if (opterr && optstring[0] != ':') {
        for (cp = argv0; *cp && ep<&errbuf[sizeof errbuf]; cp++, ep++)
            *ep = *cp;
        for (cp = ": "; *cp && ep<&errbuf[sizeof errbuf]; cp++, ep++)
            *ep = *cp;
        for (cp = s; *cp && ep<&errbuf[sizeof errbuf]; cp++, ep++)
            *ep = *cp;
        for (cp = " -- "; *cp && ep<&errbuf[sizeof errbuf]; cp++, ep++)
            *ep = *cp;
        if (ep<&errbuf[sizeof errbuf])
            *ep++ = c;
        if (ep<&errbuf[sizeof errbuf])
            *ep++ = '\n';
        write(2, errbuf, ep - errbuf);
    }
}

/*
 * getopt_sp is required to keep state between successive calls to getopt()
 * while extracting aggregated options (ie: -abcd). Hence, getopt() is not
 * thread safe or reentrant, but it really doesn't matter.
 *
 * So, why isn't this "static" you ask?  Because the historical Bourne
 * shell has actually latched on to this little piece of private data.
 */
int getopt_sp = 1;

/*
 * Determine if the specified character (c) is present in the string
 * (optstring) as a regular, single character option. If the option is found,
 * return a pointer into optstring pointing at the option character,
 * otherwise return null. The character ':' is not allowed.
 */
static char *
parse(const char *optstring, const char c)
{
    char *cp = (char *)optstring;

    if (c == ':')
        return (NULL);
    do {
        if (*cp == c)
            return (cp);
    } while (*cp++ != '\0');
    return (NULL);
}

/*
 * External function entry point.
 */
int
getopt(int argc, char *const *argv, const char *optstring)
{
    char    c;
    char    *cp;

    /*
     * Has the end of the options been encountered?  The following
     * implements the SUS requirements:
     *
     * If, when getopt() is called:
     *  argv[optind]    is a null pointer
     *  *argv[optind]   is not the character '-'
     *  argv[optind]    points to the string "-"
     * getopt() returns -1 without changing optind. If
     *  argv[optind]    points to the string "--"
     * getopt() returns -1 after incrementing optind.
     */
    if (getopt_sp == 1) {
        if (optind >= argc || argv[optind][0] != '-' ||
            argv[optind] == NULL || argv[optind][1] == '\0')
            return (EOF);
        else if (strcmp(argv[optind], "--") == 0) {
            optind++;
            return (EOF);
        }
    }

    /*
     * Getting this far indicates that an option has been encountered.
     * Note that the syntax of optstring applies special meanings to
     * the characters ':' and '(', so they are not permissible as
     * option letters. A special meaning is also applied to the ')'
     * character, but its meaning can be determined from context.
     * Note that the specification only requires that the alnum
     * characters be accepted.
     */
    optopt = c = (unsigned char)argv[optind][getopt_sp];
    optarg = NULL;
    if ((cp = parse(optstring, c)) == NULL) {
        /* LINTED: variable format specifier */
        ERR("illegal option", c);
        if (argv[optind][++getopt_sp] == '\0') {
            optind++;
            getopt_sp = 1;
        }
        return ('?');
    }
    optopt = c = *cp;

    /*
     * A valid option has been identified.  If it should have an
     * option-argument, process that now.  SUS defines the setting
     * of optarg as follows:
     *
     *   1. If the option was the last character in the string pointed to
     *  by an element of argv, then optarg contains the next element
     *  of argv, and optind is incremented by 2. If the resulting
     *  value of optind is not less than argc, this indicates a
     *  missing option-argument, and getopt() returns an error
     *  indication.
     *
     *   2. Otherwise, optarg points to the string following the option
     *  character in that element of argv, and optind is incremented
     *  by 1.
     *
     * The second clause allows -abcd (where b requires an option-argument)
     * to be interpreted as "-a -b cd".
     */
    if (*(cp + 1) == ':') {
        /* The option takes an argument */
        if (argv[optind][getopt_sp+1] != '\0') {
            optarg = &argv[optind++][getopt_sp+1];
        } else if (++optind >= argc) {
            /* LINTED: variable format specifier */
            ERR("option requires an argument", c);
            getopt_sp = 1;
            optarg = NULL;
            return (optstring[0] == ':' ? ':' : '?');
        } else
            optarg = argv[optind++];
        getopt_sp = 1;
    } else {
        /* The option does NOT take an argument */
        if (argv[optind][++getopt_sp] == '\0') {
            getopt_sp = 1;
            optind++;
        }
        optarg = NULL;
    }
    return (c);
} /* getopt() */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/libmai.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/libmai.c used in: Step 998
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*  Copyright (c) 1989 AT&T */
/*    All Rights Reserved   */


/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*  from OpenSolaris "libmai.c  6.9 05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)libmai.c  1.3 (gritter) 6/18/05
 */

#include <locale.h>

extern int yyparse(void);

int 
main(void)
{
    setlocale(LC_ALL, "");
    yyparse();
    return (0);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/libzer.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/libzer.c used in: Step 999
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*  Copyright (c) 1989 AT&T */
/*    All Rights Reserved   */


/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*  from OpenSolaris "libzer.c  6.6 05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)libzer.c  1.3 (gritter) 6/18/05
 */

#include <stdio.h>

void
yyerror(const char *s)
{
    fprintf(stderr, "%s\n", s);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/yaccpar

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/yacc/yaccpar used in: Step 1003
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */

/*  from OpenSolaris "yaccpar   6.18    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)yaccpar   1.5 (gritter) 11/26/05
 */

/*
** Skeleton parser driver for yacc output
*/

/*
** yacc user known macros and defines
*/
#define YYERROR     goto yyerrlab
#define YYACCEPT    return(0)
#define YYABORT     return(1)
#define YYBACKUP( newtoken, newvalue )\
{\
    if ( yychar >= 0 || ( yyr2[ yytmp ] >> 1 ) != 1 )\
    {\
        yyerror( "syntax error - cannot backup" );\
        goto yyerrlab;\
    }\
    yychar = newtoken;\
    yystate = *yyps;\
    yylval = newvalue;\
    goto yynewstate;\
}
#define YYRECOVERING()  (!!yyerrflag)
#define YYNEW(type) malloc(sizeof(type) * yynewmax)
#define YYCOPY(to, from, type) \
    (type *) memcpy(to, (char *) from, yymaxdepth * sizeof (type))
#define YYENLARGE( from, type) \
    (type *) realloc((char *) from, yynewmax * sizeof(type))
#ifndef YYDEBUG
#   define YYDEBUG  1   /* make debugging available */
#endif

/*
** user known globals
*/
int yydebug;            /* set to 1 to get debugging */

/*
** driver internal defines
*/
#define YYFLAG      (-10000000)

/*
** global variables used by the parser
*/
YYSTYPE *yypv;          /* top of value stack */
int *yyps;          /* top of state stack */

int yystate;            /* current state */
int yytmp;          /* extra var (lasts between blocks) */

int yynerrs;            /* number of errors */
int yyerrflag;          /* error recovery flag */
int yychar;         /* current input token number */



#ifdef YYNMBCHARS
#define YYLEX()     yycvtok(yylex())
/*
** yycvtok - return a token if i is a wchar_t value that exceeds 255.
**  If i<255, i itself is the token.  If i>255 but the neither 
**  of the 30th or 31st bit is on, i is already a token.
*/
#if defined(__STDC__) || defined(__cplusplus)
int yycvtok(int i)
#else
int yycvtok(i) int i;
#endif
{
    int first = 0;
    int last = YYNMBCHARS - 1;
    int mid;
    wchar_t j;

    if(i&0x60000000){/*Must convert to a token. */
        if( yymbchars[last].character < i ){
            return i;/*Giving up*/
        }
        while ((last>=first)&&(first>=0)) {/*Binary search loop*/
            mid = (first+last)/2;
            j = yymbchars[mid].character;
            if( j==i ){/*Found*/ 
                return yymbchars[mid].tvalue;
            }else if( j<i ){
                first = mid + 1;
            }else{
                last = mid -1;
            }
        }
        /*No entry in the table.*/
        return i;/* Giving up.*/
    }else{/* i is already a token. */
        return i;
    }
}
#else/*!YYNMBCHARS*/
#define YYLEX()     yylex()
#endif/*!YYNMBCHARS*/

/*
** yyparse - return 0 if worked, 1 if syntax error not recovered from
*/
#if defined(__STDC__) || defined(__cplusplus)
int yyparse(void)
#else
int yyparse()
#endif
{
    register YYSTYPE *yypvt = 0;    /* top of value stack for $vars */

#if defined(__cplusplus) || defined(lint) || defined(__GNUC__)
/*
    hacks to please C++, lint, and gcc - goto's inside
    switch should never be executed
*/
    static int _yaccpar_lint_hack = -1;
    switch (_yaccpar_lint_hack)
    {
        case 1: goto yyerrlab;
        case 2: goto yynewstate;
    }
#endif

    /*
    ** Initialize externals - yyparse may be called more than once
    */
    yypv = &yyv[-1];
    yyps = &yys[-1];
    yystate = 0;
    yytmp = 0;
    yynerrs = 0;
    yyerrflag = 0;
    yychar = -1;

#if YYMAXDEPTH <= 0
    if (yymaxdepth <= 0)
    {
        if ((yymaxdepth = YYEXPAND(0)) <= 0)
        {
            yyerror("yacc initialization error");
            YYABORT;
        }
    }
#endif

    {
        register YYSTYPE *yy_pv;    /* top of value stack */
        register int *yy_ps;        /* top of state stack */
        register int yy_state;      /* current state */
        register int  yy_n;     /* internal state number info */
    goto yystack;   /* moved from 6 lines above to here to please C++ */

        /*
        ** get globals into registers.
        ** branch to here only if YYBACKUP was called.
        */
    yynewstate:
        yy_pv = yypv;
        yy_ps = yyps;
        yy_state = yystate;
        goto yy_newstate;

        /*
        ** get globals into registers.
        ** either we just started, or we just finished a reduction
        */
    yystack:
        yy_pv = yypv;
        yy_ps = yyps;
        yy_state = yystate;

        /*
        ** top of for (;;) loop while no reductions done
        */
    yy_stack:
        /*
        ** put a state and value onto the stacks
        */
#if YYDEBUG
        /*
        ** if debugging, look up token value in list of value vs.
        ** name pairs.  0 and negative (-1) are special values.
        ** Note: linear search is used since time is not a real
        ** consideration while debugging.
        */
        if ( yydebug )
        {
            register int yy_i;

            printf( "State %d, token ", yy_state );
            if ( yychar == 0 )
                printf( "end-of-file\n" );
            else if ( yychar < 0 )
                printf( "-none-\n" );
            else
            {
                for ( yy_i = 0; yytoks[yy_i].t_val >= 0;
                    yy_i++ )
                {
                    if ( yytoks[yy_i].t_val == yychar )
                        break;
                }
                printf( "%s\n", yytoks[yy_i].t_name );
            }
        }
#endif /* YYDEBUG */
        if ( ++yy_ps >= &yys[ yymaxdepth ] )    /* room on stack? */
        {
            /*
            ** reallocate and recover.  Note that pointers
            ** have to be reset, or bad things will happen
            */
            long yyps_index = (yy_ps - yys);
            long yypv_index = (yy_pv - yyv);
            long yypvt_index = (yypvt - yyv);
            int yynewmax;
#ifdef YYEXPAND
            yynewmax = YYEXPAND(yymaxdepth);
#else
            yynewmax = 2 * yymaxdepth;  /* double table size */
            if (yymaxdepth == YYMAXDEPTH)   /* first time growth */
            {
                char *newyys = (char *)YYNEW(int);
                char *newyyv = (char *)YYNEW(YYSTYPE);
                if (newyys != 0 && newyyv != 0)
                {
                    yys = YYCOPY(newyys, yys, int);
                    yyv = YYCOPY(newyyv, yyv, YYSTYPE);
                }
                else
                    yynewmax = 0;   /* failed */
            }
            else                /* not first time */
            {
                yys = YYENLARGE(yys, int);
                yyv = YYENLARGE(yyv, YYSTYPE);
                if (yys == 0 || yyv == 0)
                    yynewmax = 0;   /* failed */
            }
#endif
            if (yynewmax <= yymaxdepth) /* tables not expanded */
            {
                yyerror( "yacc stack overflow" );
                YYABORT;
            }
            yymaxdepth = yynewmax;

            yy_ps = yys + yyps_index;
            yy_pv = yyv + yypv_index;
            yypvt = yyv + yypvt_index;
        }
        *yy_ps = yy_state;
        *++yy_pv = yyval;

        /*
        ** we have a new state - find out what to do
        */
    yy_newstate:
        if ( ( yy_n = yypact[ yy_state ] ) <= YYFLAG )
            goto yydefault;     /* simple state */
#if YYDEBUG
        /*
        ** if debugging, need to mark whether new token grabbed
        */
        yytmp = yychar < 0;
#endif
        if ( ( yychar < 0 ) && ( ( yychar = YYLEX() ) < 0 ) )
            yychar = 0;     /* reached EOF */
#if YYDEBUG
        if ( yydebug && yytmp )
        {
            register int yy_i;

            printf( "Received token " );
            if ( yychar == 0 )
                printf( "end-of-file\n" );
            else if ( yychar < 0 )
                printf( "-none-\n" );
            else
            {
                for ( yy_i = 0; yytoks[yy_i].t_val >= 0;
                    yy_i++ )
                {
                    if ( yytoks[yy_i].t_val == yychar )
                        break;
                }
                printf( "%s\n", yytoks[yy_i].t_name );
            }
        }
#endif /* YYDEBUG */
        if ( ( ( yy_n += yychar ) < 0 ) || ( yy_n >= YYLAST ) )
            goto yydefault;
        if ( yychk[ yy_n = yyact[ yy_n ] ] == yychar )  /*valid shift*/
        {
            yychar = -1;
            yyval = yylval;
            yy_state = yy_n;
            if ( yyerrflag > 0 )
                yyerrflag--;
            goto yy_stack;
        }

    yydefault:
        if ( ( yy_n = yydef[ yy_state ] ) == -2 )
        {
#if YYDEBUG
            yytmp = yychar < 0;
#endif
            if ( ( yychar < 0 ) && ( ( yychar = YYLEX() ) < 0 ) )
                yychar = 0;     /* reached EOF */
#if YYDEBUG
            if ( yydebug && yytmp )
            {
                register int yy_i;

                printf( "Received token " );
                if ( yychar == 0 )
                    printf( "end-of-file\n" );
                else if ( yychar < 0 )
                    printf( "-none-\n" );
                else
                {
                    for ( yy_i = 0;
                        yytoks[yy_i].t_val >= 0;
                        yy_i++ )
                    {
                        if ( yytoks[yy_i].t_val
                            == yychar )
                        {
                            break;
                        }
                    }
                    printf( "%s\n", yytoks[yy_i].t_name );
                }
            }
#endif /* YYDEBUG */
            /*
            ** look through exception table
            */
            {
                register YYCONST int *yyxi = yyexca;

                while ( ( *yyxi != -1 ) ||
                    ( yyxi[1] != yy_state ) )
                {
                    yyxi += 2;
                }
                while ( ( *(yyxi += 2) >= 0 ) &&
                    ( *yyxi != yychar ) )
                    ;
                if ( ( yy_n = yyxi[1] ) < 0 )
                    YYACCEPT;
            }
        }

        /*
        ** check for syntax error
        */
        if ( yy_n == 0 )    /* have an error */
        {
            /* no worry about speed here! */
            switch ( yyerrflag )
            {
            case 0:     /* new error */
                yyerror( "syntax error" );
                goto skip_init;
            yyerrlab:
                /*
                ** get globals into registers.
                ** we have a user generated syntax type error
                */
                yy_pv = yypv;
                yy_ps = yyps;
                yy_state = yystate;
            skip_init:
                yynerrs++;
                /* FALLTHRU */
            case 1:
            case 2:     /* incompletely recovered error */
                    /* try again... */
                yyerrflag = 3;
                /*
                ** find state where "error" is a legal
                ** shift action
                */
                while ( yy_ps >= yys )
                {
                    yy_n = yypact[ *yy_ps ] + YYERRCODE;
                    if ( yy_n >= 0 && yy_n < YYLAST &&
                        yychk[yyact[yy_n]] == YYERRCODE)                    {
                        /*
                        ** simulate shift of "error"
                        */
                        yy_state = yyact[ yy_n ];
                        goto yy_stack;
                    }
                    /*
                    ** current state has no shift on
                    ** "error", pop stack
                    */
#if YYDEBUG
#   define _POP_ "Error recovery pops state %d, uncovers state %d\n"
                    if ( yydebug )
                        printf( _POP_, *yy_ps,
                            yy_ps[-1] );
#   undef _POP_
#endif
                    yy_ps--;
                    yy_pv--;
                }
                /*
                ** there is no state on stack with "error" as
                ** a valid shift.  give up.
                */
                YYABORT;
            case 3:     /* no shift yet; eat a token */
#if YYDEBUG
                /*
                ** if debugging, look up token in list of
                ** pairs.  0 and negative shouldn't occur,
                ** but since timing doesn't matter when
                ** debugging, it doesn't hurt to leave the
                ** tests here.
                */
                if ( yydebug )
                {
                    register int yy_i;

                    printf( "Error recovery discards " );
                    if ( yychar == 0 )
                        printf( "token end-of-file\n" );
                    else if ( yychar < 0 )
                        printf( "token -none-\n" );
                    else
                    {
                        for ( yy_i = 0;
                            yytoks[yy_i].t_val >= 0;
                            yy_i++ )
                        {
                            if ( yytoks[yy_i].t_val
                                == yychar )
                            {
                                break;
                            }
                        }
                        printf( "token %s\n",
                            yytoks[yy_i].t_name );
                    }
                }
#endif /* YYDEBUG */
                if ( yychar == 0 )  /* reached EOF. quit */
                    YYABORT;
                yychar = -1;
                goto yy_newstate;
            }
        }/* end if ( yy_n == 0 ) */
        /*
        ** reduction by production yy_n
        ** put stack tops, etc. so things right after switch
        */
#if YYDEBUG
        /*
        ** if debugging, print the string that is the user's
        ** specification of the reduction which is just about
        ** to be done.
        */
        if ( yydebug )
            printf( "Reduce by (%d) \"%s\"\n",
                yy_n, yyreds[ yy_n ] );
#endif
        yytmp = yy_n;           /* value to switch over */
        yypvt = yy_pv;          /* $vars top of value stack */
        /*
        ** Look in goto table for next state
        ** Sorry about using yy_state here as temporary
        ** register variable, but why not, if it works...
        ** If yyr2[ yy_n ] doesn't have the low order bit
        ** set, then there is no action to be done for
        ** this reduction.  So, no saving & unsaving of
        ** registers done.  The only difference between the
        ** code just after the if and the body of the if is
        ** the goto yy_stack in the body.  This way the test
        ** can be made before the choice of what to do is needed.
        */
        {
            /* length of production doubled with extra bit */
            register int yy_len = yyr2[ yy_n ];

            if ( !( yy_len & 01 ) )
            {
                yy_len >>= 1;
                yyval = ( yy_pv -= yy_len )[1]; /* $$ = $1 */
                yy_state = yypgo[ yy_n = yyr1[ yy_n ] ] +
                    *( yy_ps -= yy_len ) + 1;
                if ( yy_state >= YYLAST ||
                    yychk[ yy_state =
                    yyact[ yy_state ] ] != -yy_n )
                {
                    yy_state = yyact[ yypgo[ yy_n ] ];
                }
                goto yy_stack;
            }
            yy_len >>= 1;
            yyval = ( yy_pv -= yy_len )[1]; /* $$ = $1 */
            yy_state = yypgo[ yy_n = yyr1[ yy_n ] ] +
                *( yy_ps -= yy_len ) + 1;
            if ( yy_state >= YYLAST ||
                yychk[ yy_state = yyact[ yy_state ] ] != -yy_n )
            {
                yy_state = yyact[ yypgo[ yy_n ] ];
            }
        }
                    /* save until reenter driver code */
        yystate = yy_state;
        yyps = yy_ps;
        yypv = yy_pv;
    }
    /*
    ** code supplied by user is placed in this switch
    */
    switch( yytmp )
    {
        $A
    }
    goto yystack;       /* reset registers in driver code */
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/once.h

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/once.h used in: Step 1005
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.
 * All rights reserved.
 * Use is subject to license terms.
 */

/*  Copyright (c) 1988 AT&T */
/*  All Rights Reserved */

/*  from OpenSolaris "once.h    6.9 05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)once.h    1.4 (gritter) 11/26/05
 */

#include "ldefs.c"

/* once.c */
    /* because of external definitions, this code should occur only once */
int ctable[2*NCH] = {
    0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
    10,  11,  12,  13,  14,  15,  16,  17,  18,  19,
    20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
    30,  31,  32,  33,  34,  35,  36,  37,  38,  39,
    40,  41,  42,  43,  44,  45,  46,  47,  48,  49,
    50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
    60,  61,  62,  63,  64,  65,  66,  67,  68,  69,
    70,  71,  72,  73,  74,  75,  76,  77,  78,  79,
    80,  81,  82,  83,  84,  85,  86,  87,  88,  89,
    90,  91,  92,  93,  94,  95,  96,  97,  98,  99,
    100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
    110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
    120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
    130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
    140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
    150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
    160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
    170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
    180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
    190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
    200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
    210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
    220, 221, 222, 223, 224, 225, 226, 227, 228, 229,
    230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
    240, 241, 242, 243, 244, 245, 246, 247, 248, 249,
    250, 251, 252, 253, 254, 255
};

int ZCH = NCH;
FILE    *fout = NULL, *errorf;
int sect = DEFSECTION;
int prev = '\n';    /* previous input character */
int pres = '\n';    /* present input character */
int peek = '\n';    /* next input character */
CHR *pushptr = pushc;
CHR *slptr = slist;

#ifndef CNAME
#define CNAME "./ncform"
#endif
#ifndef RATNAME
#define RATNAME "./nrform"
#endif
char    *cname = CNAME;
char    *ratname = RATNAME;

int ccount = 1;
int casecount = 1;
int aptr = 1;
int nstates = NSTATES, maxpos = MAXPOS;
int treesize = TREESIZE, ntrans = NTRANS;
int yytop;
int outsize = NOUTPUT;
int sptr = 1;
int optim = TRUE;
int report = 2;
int debug;      /* 1 = on */
int charc;
char *v_stmp = "n";
int no_input;
int copy_line;
int n_error = 0;
int fatal = 1;
int sargc;
char **sargv;
CHR buf[BUF_SIZ];
int ratfor;     /* 1 = ratfor, 0 = C */
int yyline;     /* line number of file */
int eof;
int lgatflg;
int divflg;
int funcflag;
int pflag;
int chset;  /* 1 = char set modified */
FILE *fin, *fother;
int fptr;
int *name;
intptr_t *left;
intptr_t *right;
int *parent;
Boolean *nullstr;
int tptr;
CHR pushc[TOKENSIZE];
CHR slist[STARTSIZE];
CHR **def, **subs, *dchar;
/* XCU4: %x exclusive start */
int *exclusive;
CHR **sname, *schar;
CHR *ccl;
CHR *ccptr;
CHR *dp, *sp;
int dptr;
CHR *bptr;      /* store input position */
CHR *tmpstat;
int count;
int **foll;
int *nxtpos;
int *positions;
int *gotof;
int *nexts;
CHR *nchar;
int **state;
int *sfall;     /* fallback state num */
Boolean *cpackflg;      /* true if state has been character packed */
int *atable;
int nptr;
Boolean symbol[MAXNCG];
CHR cindex[MAXNCG];
int xstate;
int stnum;
CHR match[MAXNCG];
BYTE extra[NACTIONS];
CHR *pchar, *pcptr;
int pchlen = TOKENSIZE;
long rcount;
int *verify, *advance, *stoff;
int scon;
CHR *psave;

Boolean handleeuc = FALSE;
Boolean widecio = FALSE;

int isArray = 1;    /* XCU4: for %array %pointer */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sgs.h

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sgs.h used in: Step 1005
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 */
const char sccsid[] = "@(#)lex.sl   2.13 (gritter) 4/14/07";
const char pkg[] = "Heirloom Development Tools";
const char rel[] = "2.13 (gritter) 4/14/07";
/* SLIST */
/*
allprint.c: * Sccsid @(#)allprint.c 1.4 (gritter) 11/27/05
getopt.c: * Sccsid @(#)getopt.c 1.9 (gritter) 4/2/07
header.c: * Sccsid @(#)header.c 1.12 (gritter) 9/23/06
ldefs.c: * Sccsid @(#)ldefs.c   1.7 (gritter) 4/14/07
libmain.c: * Sccsid @(#)libmain.c   1.4 (gritter) 11/26/05
lsearch.c: * Sccsid @(#)lsearch.c   1.4 (gritter) 11/26/05
main.c: * Sccsid @(#)main.c 1.9 (gritter) 11/26/05
once.h: * Sccsid @(#)once.h 1.4 (gritter) 11/26/05
parser.y: * Sccsid @(#)parser.y 1.8 (gritter) 11/26/05
reject.c: * Sccsid @(#)reject.c 1.4 (gritter) 11/27/05
search.h: * Sccsid @(#)search.h 1.4 (gritter) 11/26/05
sub1.c: * Sccsid @(#)sub1.c 1.5 (gritter) 11/26/05
sub2.c: * Sccsid @(#)sub2.c 1.7 (gritter) 01/12/07
sub3.c: * Sccsid @(#)sub3.c 1.4 (gritter) 11/26/05
wcio.c: * Sccsid @(#)wcio.c 1.1 (gritter) 6/25/05
yyless.c: * Sccsid @(#)yyless.c 1.6 (gritter) 11/27/05
yywrap.c: * Sccsid @(#)yywrap.c 1.3 (gritter) 6/18/05
nceucform: * Sccsid @(#)nceucform   1.6 (gritter) 11/18/05
ncform: * Sccsid @(#)ncform 1.4 (gritter) 11/18/05
nrform:# Sccsid @(#)nrform  1.4 (gritter) 10/20/06

*/

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sub2.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/sub2.c used in: Step 1007
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.
 * All rights reserved.
 * Use is subject to license terms.
 */

/*  Copyright (c) 1988 AT&T */
/*  All Rights Reserved */

/*  from OpenSolaris "sub2.c    6.15    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)sub2.c    1.7 (gritter) 01/12/07
 */

#include "ldefs.c"

static void add(int **array, int n);
static void follow(int v);
static void first(int v);
static void nextstate(int s, int c);
static void packtrans(int st, CHR *tch, int *tst, int cnt, int tryit);
static void acompute(int s);
static void rprint(int *a, char *s, int n);
static void shiftr(int *a, int n);
static void upone(int *a, int n);
static void bprint(char *a, char *s, int n);
static int notin(int n);
static int member(int d, CHR *t);

#ifdef PP
static void padd(int **array, int n);
#endif

void
cfoll(int v)
{
    int i, j, k;
    CHR *p;
    i = name[v];
    if (!ISOPERATOR(i))
        i = 1;
    switch (i) {
        case 1: case RSTR: case RCCL: case RNCCL: case RNULLS:
            for (j = 0; j < tptr; j++)
                tmpstat[j] = FALSE;
            count = 0;
            follow(v);
#ifdef PP
            padd(foll, v); /* packing version */
#else
            add(foll, v); /* no packing version */
#endif
            if (i == RSTR)
                cfoll(left[v]);
            else if (i == RCCL || i == RNCCL) {
                for (j = 1; j < ncg; j++)
                    symbol[j] = (i == RNCCL);
                p = (CHR *) left[v];
                while (*p)
                    symbol[*p++] = (i == RCCL);
                p = pcptr;
                for (j = 1; j < ncg; j++)
                    if (symbol[j]) {
                        for (k = 0; p + k < pcptr; k++)
                            if (cindex[j] == *(p + k))
                            break;
                        if (p + k >= pcptr)
                            *pcptr++ = cindex[j];
                    }
                *pcptr++ = 0;
                if (pcptr > pchar + pchlen)
                    error(
                    "Too many packed character classes");
                left[v] = (intptr_t)p;
                name[v] = RCCL; /* RNCCL eliminated */
#ifdef DEBUG
                if (debug && *p) {
                    printf("ccl %d: %d", v, *p++);
                    while (*p)
                        printf(", %d", *p++);
                    putchar('\n');
                }
#endif
            }
            break;
        case CARAT:
            cfoll(left[v]);
            break;

        /* XCU4: add RXSCON */
        case RXSCON:

        case STAR: case PLUS: case QUEST: case RSCON:
            cfoll(left[v]);
            break;
        case BAR: case RCAT: case DIV: case RNEWE:
            cfoll(left[v]);
            cfoll(right[v]);
            break;
#ifdef DEBUG
        case FINAL:
        case S1FINAL:
        case S2FINAL:
            break;
        default:
            warning("bad switch cfoll %d", v);
#endif
    }
}

#ifdef DEBUG
void
pfoll(void)
{
    int i, k, *p;
    int j;
    /* print sets of chars which may follow positions */
    printf("pos\tchars\n");
    for (i = 0; i < tptr; i++)
        if (p = foll[i]) {
            j = *p++;
            if (j >= 1) {
                printf("%d:\t%d", i, *p++);
                for (k = 2; k <= j; k++)
                    printf(", %d", *p++);
                putchar('\n');
            }
        }
}
#endif

static void
add(int **array, int n)
{
    int i, *temp;
    CHR *ctemp;
    temp = nxtpos;
    ctemp = tmpstat;
    array[n] = nxtpos;  /* note no packing is done in positions */
    *temp++ = count;
    for (i = 0; i < tptr; i++)
        if (ctemp[i] == TRUE)
            *temp++ = i;
    nxtpos = temp;
    if (nxtpos >= positions+maxpos)
        error(
        "Too many positions %s",
        (maxpos == MAXPOS ? "\nTry using %p num" : ""));
}

static void
follow(int v)
{
    int p;
    if (v >= tptr-1)
        return;
    p = parent[v];
    if (p == 0)
        return;
    switch (name[p]) {
    /* will not be CHAR RNULLS FINAL S1FINAL S2FINAL RCCL RNCCL */
    case RSTR:
        if (tmpstat[p] == FALSE) {
            count++;
            tmpstat[p] = TRUE;
        }
        break;
    case STAR: case PLUS:
        first(v);
        follow(p);
        break;
    case BAR: case QUEST: case RNEWE:
        follow(p);
        break;
    case RCAT: case DIV:
        if (v == left[p]) {
            if (nullstr[right[p]])
                follow(p);
            first(right[p]);
        }
        else
            follow(p);
        break;
    /* XCU4: add RXSCON */
    case RXSCON:
    case RSCON: case CARAT:
        follow(p);
        break;
#ifdef DEBUG
    default:
        warning("bad switch follow %d", p);
#endif
    }
}

/*
 * Check if I have a RXSCON in my upper node
 */
static int
check_me(int v)
{
    int tmp = parent[v];

    while (name[tmp] != RNEWE) {
        if (name[tmp] == RXSCON)
            return (1);
        tmp = parent[tmp];
    }
    return (0);
}

/* calculate set of positions with v as root which can be active initially */
static void
first(int v)
{
    int i;
    CHR *p;
    i = name[v];
    if (!ISOPERATOR(i))
        i = 1;
    switch (i) {
    case 1: case RCCL: case RNCCL:
    case RNULLS: case FINAL:
    case S1FINAL: case S2FINAL:
    /*
     * XCU4: if we are working on an exclusive start state and
     * the parent of this position is not RXSCON or RSTR this
     * is not an active position.
     *
     * (There is a possibility that RSXCON appreas as the
     *  (parent)* node. Check it by check_me().)
     */
        if ((exclusive[stnum/2]) &&
            ISOPERATOR(name[parent[v]]) &&
            (name[parent[v]] != RXSCON) &&
            (name[parent[v]] != RSTR) &&
            (check_me(v) == 0)) {
                break;
        }
        if (tmpstat[v] == FALSE) {
            count++;
            tmpstat[v] = TRUE;
        }
        break;
    case BAR: case RNEWE:
        first(left[v]);
        first(right[v]);
        break;
    case CARAT:
        if (stnum % 2 == 1)
            first(left[v]);
        break;
    /* XCU4: add RXSCON */
    case RXSCON:
    case RSCON:
        i = stnum/2 +1;
        p = (CHR *) right[v];
        while (*p)
            if (*p++ == i) {
                first(left[v]);
                break;
            }
        break;
    case STAR: case QUEST:
    case PLUS:  case RSTR:
    /*
     * XCU4: if we are working on an exclusive start state and
     * the parent of this position is not RXSCON or RSTR this
     * is not an active position.
     *
     * (There is a possibility that RSXCON appreas as the
     *  (parent)* node. Check it by check_me().)
     */
        if ((exclusive[stnum/2]) &&
            ISOPERATOR(name[parent[v]]) &&
            (name[parent[v]] != RXSCON) &&
            (name[parent[v]] != RSTR) &&
            (check_me(v) == 0)) {
                break;
        }
        first(left[v]);
        break;
    case RCAT: case DIV:
        first(left[v]);
        if (nullstr[left[v]])
            first(right[v]);
        break;
#ifdef DEBUG
    default:
        warning("bad switch first %d", v);
#endif
    }
}

void
cgoto(void)
{
    int i, j;
    static int s;
    int npos, curpos, n;
    int tryit;
    CHR tch[MAXNCG];
    int tst[MAXNCG];
    CHR *q;
    /* generate initial state, for each start condition */
    if (ratfor) {
        fprintf(fout, "blockdata\n");
        fprintf(fout, "common /Lvstop/ vstop\n");
        fprintf(fout, "define Svstop %d\n", nstates+1);
        fprintf(fout, "integer vstop(Svstop)\n");
    } else
        fprintf(fout, "int yyvstop[] = {\n0,\n");
    while (stnum < 2 || stnum/2 < sptr) {
        for (i = 0; i < tptr; i++)
            tmpstat[i] = 0;
        count = 0;
        if (tptr > 0)
            first(tptr-1);
        add(state, stnum);
#ifdef DEBUG
        if (debug) {
            if (stnum > 1)
                printf("%ls:\n", sname[stnum/2]);
            pstate(stnum);
        }
#endif
        stnum++;
    }
    stnum--;
    /* even stnum = might not be at line begin */
    /* odd stnum  = must be at line begin */
    /* even states can occur anywhere, odd states only at line begin */
    for (s = 0; s <= stnum; s++) {
        tryit = FALSE;
        cpackflg[s] = FALSE;
        sfall[s] = -1;
        acompute(s);
        for (i = 0; i < ncg; i++)
            symbol[i] = 0;
        npos = *state[s];
        for (i = 1; i <= npos; i++) {
            curpos = *(state[s]+i);
            if (!ISOPERATOR(name[curpos]))
                symbol[name[curpos]] = TRUE;
            else {
                switch (name[curpos]) {
                case RCCL:
                    tryit = TRUE;
                    q = (CHR *)left[curpos];
                    while (*q) {
                        for (j = 1; j < ncg; j++)
                            if (cindex[j] == *q)
                            symbol[j] = TRUE;
                        q++;
                    }
                    break;
                case RSTR:
                    symbol[right[curpos]] = TRUE;
                    break;
#ifdef DEBUG
                case RNULLS:
                case FINAL:
                case S1FINAL:
                case S2FINAL:
                    break;
                default:
                    warning(
                    "bad switch cgoto %d state %d",
                    curpos, s);
                    break;
#endif
                }
            }
        }
#ifdef DEBUG
        if (debug) {
            printf("State %d transitions on char-group {", s);
            charc = 0;
            for (i = 1; i < ncg; i++) {
                if (symbol[i]) {
                    printf("%d,", i);
                }
                if (i == ncg-1)
                    printf("}\n");
                if (charc > LINESIZE/4) {
                    charc = 0;
                    printf("\n\t");
                }
            }
        }
#endif
        /* for each char, calculate next state */
        n = 0;
        for (i = 1; i < ncg; i++) {
            if (symbol[i]) {
                /* executed for each state, transition pair */
                nextstate(s, i);
                xstate = notin(stnum);
                if (xstate == -2)
                    warning("bad state  %d %o", s, i);
                else if (xstate == -1) {
                    if (stnum+1 >= nstates) {
                        stnum++;
                        error("Too many states %s",
                        (nstates == NSTATES ?
                        "\nTry using %n num":""));
                    }
                    add(state, ++stnum);
#ifdef DEBUG
                    if (debug)
                        pstate(stnum);
#endif
                    tch[n] = i;
                    tst[n++] = stnum;
                } else { /* xstate >= 0 ==> state exists */
                    tch[n] = i;
                    tst[n++] = xstate;
                }
            }
        }
        tch[n] = 0;
        tst[n] = -1;
        /* pack transitions into permanent array */
        if (n > 0)
            packtrans(s, tch, tst, n, tryit);
        else
            gotof[s] = -1;
    }
    ratfor ? fprintf(fout, "end\n") : fprintf(fout, "0};\n");
}

/*
 * Beware -- 70% of total CPU time is spent in this subroutine -
 * if you don't believe me - try it yourself !
 */
static void
nextstate(int s, int c)
{
    int j, *newpos;
    CHR *temp, *tz;
    int *pos, i, *f, num, curpos, number;
    /* state to goto from state s on char c */
    num = *state[s];
    temp = tmpstat;
    pos = state[s] + 1;
    for (i = 0; i < num; i++) {
        curpos = *pos++;
        j = name[curpos];
        if ((!ISOPERATOR(j)) && j == c ||
            j == RSTR && c == right[curpos] ||
            j == RCCL && member(c, (CHR *) left[curpos])) {
            f = foll[curpos];
            number = *f;
            newpos = f+1;
            for (j = 0; j < number; j++)
                temp[*newpos++] = 2;
        }
    }
    j = 0;
    tz = temp + tptr;
    while (temp < tz) {
        if (*temp == 2) {
            j++;
            *temp++ = 1;
        }
        else
            *temp++ = 0;
    }
    count = j;
}

static int
notin(int n)
{   /* see if tmpstat occurs previously */
    int *j, k;
    CHR *temp;
    int i;
    if (count == 0)
        return (-2);
    temp = tmpstat;
    for (i = n; i >= 0; i--) { /* for each state */
        j = state[i];
        if (count == *j++) {
            for (k = 0; k < count; k++)
                if (!temp[*j++])
                    break;
            if (k >= count)
                return (i);
        }
    }
    return (-1);
}

static void
packtrans(int st, CHR *tch, int *tst, int cnt, int tryit)
{
    /*
     * pack transitions into nchar, nexts
     * nchar is terminated by '\0', nexts uses cnt, followed by elements
     * gotof[st] = index into nchr, nexts for state st
     * sfall[st] =  t implies t is fall back state for st
     * == -1 implies no fall back
     */

    int cmin, cval, tcnt, diff, p, *ast;
    int i, j, k;
    CHR *ach;
    int go[MAXNCG], temp[MAXNCG], index, c;
    int swork[MAXNCG];
    CHR cwork[MAXNCG];
    int upper;

    rcount += (long)cnt;
    cmin = -1;
    cval = ncg;
    ast = tst;
    ach = tch;
    /* try to pack transitions using ccl's */
    if (!optim)
        goto nopack; /* skip all compaction */
    if (tryit) { /* ccl's used */
        for (i = 1; i < ncg; i++) {
            go[i] = temp[i] = -1;
            symbol[i] = 1;
        }
        for (i = 0; i < cnt; i++) {
            index = (unsigned char) tch[i];
            if ((index >= 0) && (index < NCH)) {
                go[index] = tst[i];
                symbol[index] = 0;
            } else {
                fprintf(stderr,
"lex`sub2`packtran: tch[%d] out of bounds (%ld)\n",
                i, (long)tch[i]);
            }
        }
        for (i = 0; i < cnt; i++) {
            c = match[tch[i]];
            if (go[c] != tst[i] || c == tch[i])
                temp[tch[i]] = tst[i];
        }
        /* fill in error entries */
        for (i = 1; i < ncg; i++)
            if (symbol[i])
                temp[i] = -2;   /* error trans */
        /* count them */
        k = 0;
        for (i = 1; i < ncg; i++)
            if (temp[i] != -1)
                k++;
        if (k < cnt) { /* compress by char */
#ifdef DEBUG
            if (debug)
                printf(
                "use compression  %d,  %d vs %d\n", st, k, cnt);
#endif
            k = 0;
            for (i = 1; i < ncg; i++)
                if (temp[i] != -1) {
                    cwork[k] = i;
                    swork[k++] =
                    (temp[i] == -2 ? -1 : temp[i]);
                }
            cwork[k] = 0;
#ifdef PC
            ach = cwork;
            ast = swork;
            cnt = k;
            cpackflg[st] = TRUE;
#endif
        }
    }
    /*
     * get most similar state
     * reject state with more transitions,
     * state already represented by a third state,
     * and state which is compressed by char if ours is not to be
     */
    for (i = 0; i < st; i++) {
        if (sfall[i] != -1)
            continue;
        if (cpackflg[st] == 1)
            if (!(cpackflg[i] == 1))
                continue;
        p = gotof[i];
        if (p == -1) /* no transitions */
            continue;
        tcnt = nexts[p];
        if (tcnt > cnt)
            continue;
        diff = 0;
        k = 0;
        j = 0;
        upper = p + tcnt;
        while (ach[j] && p < upper) {
            while (ach[j] < nchar[p] && ach[j]) {
                diff++;
                j++;
            }
            if (ach[j] == 0)
                break;
            if (ach[j] > nchar[p]) {
                diff = ncg;
                break;
            }
            /* ach[j] == nchar[p] */
            if (ast[j] != nexts[++p] ||
                ast[j] == -1 ||
                (cpackflg[st] && ach[j] != match[ach[j]]))
                diff++;
            j++;
        }
        while (ach[j]) {
            diff++;
            j++;
        }
        if (p < upper)
            diff = ncg;
        if (diff < cval && diff < tcnt) {
            cval = diff;
            cmin = i;
            if (cval == 0)
                break;
        }
    }
    /* cmin = state "most like" state st */
#ifdef DEBUG
    if (debug)
        printf("select st %d for st %d diff %d\n",
            cmin, st, cval);
#endif
#ifdef PS
    if (cmin != -1) { /* if we can use st cmin */
        gotof[st] = nptr;
        k = 0;
        sfall[st] = cmin;
        p = gotof[cmin] + 1;
        j = 0;
        while (ach[j]) {
            /* if cmin has a transition on c, then so will st */
            /* st may be "larger" than cmin, however */
            while (ach[j] < nchar[p-1] && ach[j]) {
                k++;
                nchar[nptr] = ach[j];
                nexts[++nptr] = ast[j];
                j++;
            }
            if (nchar[p-1] == 0)
                break;
            if (ach[j] > nchar[p-1]) {
                warning("bad transition %d %d", st, cmin);
                goto nopack;
            }
            /* ach[j] == nchar[p-1] */
            if (ast[j] != nexts[p] ||
                ast[j] == -1 ||
                (cpackflg[st] && ach[j] != match[ach[j]])) {
                k++;
                nchar[nptr] = ach[j];
                nexts[++nptr] = ast[j];
            }
            p++;
            j++;
        }
        while (ach[j]) {
            nchar[nptr] = ach[j];
            nexts[++nptr] = ast[j++];
            k++;
        }
        nexts[gotof[st]] = cnt = k;
        nchar[nptr++] = 0;
    } else {
#endif
nopack:
    /* stick it in */
        gotof[st] = nptr;
        nexts[nptr] = cnt;
        for (i = 0; i < cnt; i++) {
            nchar[nptr] = ach[i];
            nexts[++nptr] = ast[i];
        }
        nchar[nptr++] = 0;
#ifdef PS
    }
#endif
    if (cnt < 1) {
        gotof[st] = -1;
        nptr--;
    } else
        if (nptr > ntrans)
            error(
            "Too many transitions %s",
            (ntrans == NTRANS ? "\nTry using %a num" : ""));
}

#ifdef DEBUG
void
pstate(int s)
{
    int *p, i, j;
    printf("State %d:\n", s);
    p = state[s];
    i = *p++;
    if (i == 0)
        return;
    printf("%4d", *p++);
    for (j = 1; j < i; j++) {
        printf(", %4d", *p++);
        if (j%30 == 0)
            putchar('\n');
    }
    putchar('\n');
}
#endif

static int
member(int d, CHR *t)
{
    int c;
    CHR *s;
    c = d;
    s = t;
    c = cindex[c];
    while (*s)
        if (*s++ == c)
            return (1);
    return (0);
}

#ifdef DEBUG
void
stprt(int i)
{
    int p, t;
    printf("State %d:", i);
    /* print actions, if any */
    t = atable[i];
    if (t != -1)
        printf(" final");
    putchar('\n');
    if (cpackflg[i] == TRUE)
        printf("backup char in use\n");
    if (sfall[i] != -1)
        printf("fall back state %d\n", sfall[i]);
    p = gotof[i];
    if (p == -1)
        return;
    printf("(%d transitions)\n", nexts[p]);
    while (nchar[p]) {
        charc = 0;
        if (nexts[p+1] >= 0)
            printf("%d\t", nexts[p+1]);
        else
            printf("err\t");
        allprint(nchar[p++]);
        while (nexts[p] == nexts[p+1] && nchar[p]) {
            if (charc > LINESIZE) {
                charc = 0;
                printf("\n\t");
            }
            allprint(nchar[p++]);
        }
        putchar('\n');
    }
    putchar('\n');
}
#endif

/* compute action list = set of poss. actions */
static void
acompute(int s)
{
    int *p, i, j;
    int q, r;
    int cnt, m;
    int temp[MAXPOSSTATE], k, neg[MAXPOSSTATE], n;
    k = 0;
    n = 0;
    p = state[s];
    cnt = *p++;
    if (cnt > MAXPOSSTATE)
        error("Too many positions for one state - acompute");
    for (i = 0; i < cnt; i++) {
        q = *p;
        if (name[q] == FINAL)
            temp[k++] = left[q];
        else if (name[q] == S1FINAL) {
            temp[k++] = left[q];
            if ((r = left[q]) >= NACTIONS)
                error(
                "INTERNAL ERROR:left[%d]==%d>=NACTIONS", q, r);
            extra[r] = 1;
        } else if (name[q] == S2FINAL)
            neg[n++] = left[q];
        p++;
    }
    atable[s] = -1;
    if (k < 1 && n < 1)
        return;
#ifdef DEBUG
    if (debug)
        printf("final %d actions:", s);
#endif
    /* sort action list */
    for (i = 0; i < k; i++)
        for (j = i+1; j < k; j++)
            if (temp[j] < temp[i]) {
                m = temp[j];
                temp[j] = temp[i];
                temp[i] = m;
            }
    /* remove dups */
    for (i = 0; i < k-1; i++)
        if (temp[i] == temp[i+1])
            temp[i] = 0;
    /* copy to permanent quarters */
    atable[s] = aptr;
#ifdef DEBUG
    if (!ratfor)
        fprintf(fout, "/* actions for state %d */", s);
#endif
    putc('\n', fout);
    for (i = 0; i < k; i++)
        if (temp[i] != 0) {
            ratfor ?
            fprintf(fout, "data vstop(%d)/%d/\n", aptr, temp[i]) :
            fprintf(fout, "%d,\n", temp[i]);
#ifdef DEBUG
            if (debug)
                printf("%d ", temp[i]);
#endif
            aptr++;
        }
    for (i = 0; i < n; i++) { /* copy fall back actions - all neg */
        ratfor ?
        fprintf(fout, "data vstop(%d)/%d/\n", aptr, neg[i]) :
        fprintf(fout, "%d,\n", neg[i]);
        aptr++;
#ifdef DEBUG
        if (debug)
            printf("%d ", neg[i]);
#endif
        }
#ifdef DEBUG
    if (debug)
        putchar('\n');
#endif
    ratfor ? fprintf(fout, "data vstop (%d)/0/\n", aptr) :
        fprintf(fout, "0, \n");
    aptr++;
}

#ifdef DEBUG
void
pccl(void)
{
    /* print character class sets */
    int i, j;
    printf("char class intersection\n");
    for (i = 0; i < ccount; i++) {
        charc = 0;
        printf("class %d:\n\t", i);
        for (j = 1; j < ncg; j++)
            if (cindex[j] == i) {
                allprint(j);
                if (charc > LINESIZE) {
                    printf("\n\t");
                    charc = 0;
                }
            }
        putchar('\n');
    }
    charc = 0;
    printf("match:\n");
    for (i = 0; i < ncg; i++) {
        allprint(match[i]);
        if (charc > LINESIZE) {
            putchar('\n');
            charc = 0;
        }
    }
    putchar('\n');
}
#endif

void
mkmatch(void)
{
    int i;
    CHR tab[MAXNCG];
    for (i = 0; i < ccount; i++)
        tab[i] = 0;
    for (i = 1; i < ncg; i++)
        if (tab[cindex[i]] == 0)
            tab[cindex[i]] = i;
    /* tab[i] = principal char for new ccl i */
    for (i = 1; i < ncg; i++)
        match[i] = tab[cindex[i]];
}

void
layout(void)
{
    /* format and output final program's tables */
    int i, j, k;
    int  top, bot, startup, omin;
    startup = 0;
    for (i = 0; i < outsize; i++)
        verify[i] = advance[i] = 0;
    omin = 0;
    yytop = 0;
    for (i = 0; i <= stnum; i++) { /* for each state */
        j = gotof[i];
        if (j == -1) {
            stoff[i] = 0;
            continue;
        }
        bot = j;
        while (nchar[j])
            j++;
        top = j - 1;
#if DEBUG
        if (debug) {
            printf("State %d: (layout)\n", i);
            for (j = bot; j <= top; j++) {
                printf("  %o", nchar[j]);
                if (j % 10 == 0)
                    putchar('\n');
            }
            putchar('\n');
        }
#endif
        while (verify[omin+ZCH])
            omin++;
        startup = omin;
#if DEBUG
        if (debug)
            printf(
            "bot,top %d, %d startup begins %d\n",
            bot, top, startup);
#endif
        if (chset) {
            do {
                startup += 1;
                if (startup > outsize - ZCH)
                    error("output table overflow");
                for (j = bot; j <= top; j++) {
                    k = startup+ctable[nchar[j]];
                    if (verify[k])
                        break;
                }
            } while (j <= top);
#if DEBUG
            if (debug)
                printf(" startup will be %d\n", startup);
#endif
            /* have found place */
            for (j = bot; j <= top; j++) {
                k = startup + ctable[nchar[j]];
                if (ctable[nchar[j]] <= 0)
                    printf(
                    "j %d nchar %ld ctable.nch %d\n",
                    j, (long)nchar[j], ctable[nchar[k]]);
                verify[k] = i + 1;  /* state number + 1 */
                advance[k] = nexts[j+1]+1;
                if (yytop < k)
                    yytop = k;
            }
        } else {
            do {
                startup += 1;
                if (startup > outsize - ZCH)
                    error("output table overflow");
                for (j = bot; j <= top; j++) {
                    k = startup + nchar[j];
                    if (verify[k])
                        break;
                }
            } while (j <= top);
            /* have found place */
#if DEBUG
    if (debug)
        printf(" startup going to be %d\n", startup);
#endif
            for (j = bot; j <= top; j++) {
                k = startup + nchar[j];
                verify[k] = i+1; /* state number + 1 */
                advance[k] = nexts[j+1] + 1;
                if (yytop < k)
                    yytop = k;
            }
        }
        stoff[i] = startup;
    }

    /* stoff[i] = offset into verify, advance for trans for state i */
    /* put out yywork */
    if (ratfor) {
        fprintf(fout, "define YYTOPVAL %d\n", yytop);
        rprint(verify, "verif", yytop+1);
        rprint(advance, "advan", yytop+1);
        shiftr(stoff, stnum);
        rprint(stoff, "stoff", stnum+1);
        shiftr(sfall, stnum);
        upone(sfall, stnum+1);
        rprint(sfall, "fall", stnum+1);
        bprint(extra, "extra", casecount+1);
        bprint((char *)match, "match", ncg);
        shiftr(atable, stnum);
        rprint(atable, "atable", stnum+1);
    }
    fprintf(fout,
    "# define YYTYPE %s\n", stnum+1 >= NCH ? "int" : "unsigned char");
    fprintf(fout,
    "struct yywork { YYTYPE verify, advance; } yycrank[] = {\n");
    for (i = 0; i <= yytop; i += 4) {
        for (j = 0; j < 4; j++) {
            k = i+j;
            if (verify[k])
                fprintf(fout,
                "{ %d,%d },\t", verify[k], advance[k]);
            else
                fprintf(fout, "{ 0,0 },\t");
        }
        putc('\n', fout);
    }
    fprintf(fout, "{0,0}};\n");

    /* put out yysvec */

    fprintf(fout, "struct yysvf yysvec[] = {\n");
    fprintf(fout, "{ 0,\t0,\t0 },\n");
    for (i = 0; i <= stnum; i++) {  /* for each state */
        if (cpackflg[i])
            stoff[i] = -stoff[i];
        fprintf(fout, "{ yycrank+%d,\t", stoff[i]);
        if (sfall[i] != -1)
            fprintf(fout,
            "yysvec+%d,\t", sfall[i]+1); /* state + 1 */
        else
            fprintf(fout, "0,\t\t");
        if (atable[i] != -1)
            fprintf(fout, "yyvstop+%d", atable[i]);
        else
            fprintf(fout, "0");
#ifdef DEBUG
        fprintf(fout, " },\t\t/* state %d */", i);
#endif
        fprintf(fout, " },\t\t/* state %d */", i);
    }
    fprintf(fout, "{ 0,\t0,\t0}};\n");

    /* put out yymatch */

    fprintf(fout, "struct yywork *yytop = yycrank+%d;\n", yytop);
    fprintf(fout, "struct yysvf *yybgin = yysvec+1;\n");
    if (optim) {
        if (handleeuc) {
            fprintf(fout, "int yymatch[] = {\n");
        } else {
            fprintf(fout, "char yymatch[] = {\n");
        }
        if (chset == 0) { /* no chset, put out in normal order */
            for (i = 0; i < ncg; i += 8) {
                for (j = 0; j < 8; j++) {
                    int fbch;
                    fbch = match[i+j];
                    fprintf(fout, "%3d, ", fbch);
                }
                putc('\n', fout);
            }
        } else {
            int *fbarr;
            fbarr = myalloc(2*MAXNCG, sizeof (*fbarr));
            if (fbarr == 0)
                error("No space for char table reverse", 0);
            for (i = 0; i < MAXNCG; i++)
                fbarr[i] = 0;
            for (i = 0; i < ncg; i++)
                fbarr[ctable[i]] = ctable[match[i]];
            for (i = 0; i < ncg; i += 8) {
                for (j = 0; j < 8; j++)
                    fprintf(fout, "0%-3o,", fbarr[i+j]);
                putc('\n', fout);
            }
            free(fbarr);
        }
        fprintf(fout, "0};\n");
    }
    /* put out yyextra */
    fprintf(fout, "char yyextra[] = {\n");
    for (i = 0; i < casecount; i += 8) {
        for (j = 0; j < 8; j++)
            fprintf(fout, "%d,", i+j < NACTIONS ?
                extra[i+j] : 0);
        putc('\n', fout);
    }
    fprintf(fout, "0};\n");
    if (handleeuc) {
        /* Put out yycgidtbl */
        fprintf(fout, "#define YYNCGIDTBL %d\n", ncgidtbl);
        fprintf(fout, "\tunsigned long yycgidtbl[]={");
        /*
         * Use "unsigned long" instead of "lchar" to minimize
         * the name-space polution for the application program.
         */
        for (i = 0; i < ncgidtbl; ++i) {
            if (i%8 == 0)
                fprintf(fout, "\n\t\t");
            fprintf(fout, "0x%08lx, ",  yycgidtbl[i]);
        }
        fprintf(fout, "\n\t0};\n");
    }
}

static void
rprint(int *a, char *s, int n)
{
    int i;
    fprintf(fout, "block data\n");
    fprintf(fout, "common /L%s/ %s\n", s, s);
    fprintf(fout, "define S%s %d\n", s, n);
    fprintf(fout, "integer %s (S%s)\n", s, s);
    for (i = 1; i <= n; i++) {
        if (i%8 == 1)
            fprintf(fout, "data ");
        fprintf(fout, "%s (%d)/%d/", s, i, a[i]);
        fprintf(fout, (i%8 && i < n) ? ", " : "\n");
    }
    fprintf(fout, "end\n");
}

static void
shiftr(int *a, int n)
{
    int i;
    for (i = n; i >= 0; i--)
        a[i+1] = a[i];
}

static void
upone(int *a, int n)
{
    int i;
    for (i = 0; i <= n; i++)
        a[i]++;
}

static void
bprint(char *a, char *s, int n)
{
    int i, j, k;
    fprintf(fout, "block data\n");
    fprintf(fout, "common /L%s/ %s\n", s, s);
    fprintf(fout, "define S%s %d\n", s, n);
    fprintf(fout, "integer %s (S%s)\n", s, s);
    for (i = 1; i < n; i += 8) {
        fprintf(fout, "data %s (%d)/%d/", s, i, a[i]);
        for (j = 1; j < 8; j++) {
            k = i+j;
            if (k < n)
                fprintf(fout, ", %s (%d)/%d/", s, k, a[k]);
        }
        putc('\n', fout);
    }
    fprintf(fout, "end\n");
}

#ifdef PP
static void
padd(int **array, int n)
{
    int i, *j, k;
    array[n] = nxtpos;
    if (count == 0) {
        *nxtpos++ = 0;
        return;
    }
    for (i = tptr-1; i >= 0; i--) {
        j = array[i];
        if (j && *j++ == count) {
            for (k = 0; k < count; k++)
                if (!tmpstat[*j++])
                    break;
            if (k >= count) {
                array[n] = array[i];
                return;
            }
        }
    }
    add(array, n);
}
#endif

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/header.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/header.c used in: Step 1009
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.
 * All rights reserved.
 * Use is subject to license terms.
 */

/*  Copyright (c) 1988 AT&T */
/*  All Rights Reserved */

/*  from OpenSolaris "header.c  6.22    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)header.c  1.12 (gritter) 9/23/06
 */

#include "ldefs.c"

static void rhd1(void);
static void chd1(void);
static void chd2(void);
static void ctail(void);
static void rtail(void);

void
phead1(void)
{
    ratfor ? rhd1() : chd1();
}

static void
chd1(void)
{
    if (*v_stmp == 'y') {
        extern const char   rel[];
        fprintf(fout, "\
#if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4\n\
#define YYUSED  __attribute__ ((used))\n\
#elif defined __GNUC__\n\
#define YYUSED  __attribute__ ((unused))\n\
#else\n\
#define YYUSED\n\
#endif\n\
static const char yylexid[] USED = \"lex: %s\"\n", rel);
    }
    if (handleeuc) {
        fprintf(fout, "#ifndef EUC\n");
        fprintf(fout, "#define EUC\n");
        fprintf(fout, "#endif\n");
        fprintf(fout, "#include <stdio.h>\n");
        fprintf(fout, "#include <stdlib.h>\n");
        fprintf(fout, "#ifdef   __sun\n");
        fprintf(fout, "#include <widec.h>\n");
        fprintf(fout, "#else    /* !__sun */\n");
        fprintf(fout, "#include <wchar.h>\n");
        fprintf(fout, "#endif   /* !__sun */\n");
        if (widecio) { /* -w option */
            fprintf(fout, "#define YYTEXT yytext\n");
            fprintf(fout, "#define YYLENG yyleng\n");
            fprintf(fout, "#ifndef __cplusplus\n");
            fprintf(fout, "#define YYINPUT input\n");
            fprintf(fout, "#define YYOUTPUT output\n");
            fprintf(fout, "#else\n");
            fprintf(fout, "#define YYINPUT lex_input\n");
            fprintf(fout, "#define YYOUTPUT lex_output\n");
            fprintf(fout, "#endif\n");
            fprintf(fout, "#define YYUNPUT unput\n");
        } else { /* -e option */
            fprintf(fout, "#include <limits.h>\n");
            fprintf(fout, "#ifdef   __sun\n");
            fprintf(fout, "#include <sys/euc.h>\n");
            fprintf(fout, "#endif   /* __sun */\n");
            fprintf(fout, "#define YYLEX_E 1\n");
            fprintf(fout, "#define YYTEXT yywtext\n");
            fprintf(fout, "#define YYLENG yywleng\n");
            fprintf(fout, "#define YYINPUT yywinput\n");
            fprintf(fout, "#define YYOUTPUT yywoutput\n");
            fprintf(fout, "#define YYUNPUT yywunput\n");
        }
    } else { /* ASCII compatibility mode. */
        fprintf(fout, "#include <stdio.h>\n");
        fprintf(fout, "#include <stdlib.h>\n");
    }
    if (ZCH > NCH)
        fprintf(fout, "# define U(x) ((x)&0377)\n");
    else
    fprintf(fout, "# define U(x) x\n");
    fprintf(fout, "# define NLSTATE yyprevious=YYNEWLINE\n");
    fprintf(fout, "# define BEGIN yybgin = yysvec + 1 +\n");
    fprintf(fout, "# define INITIAL 0\n");
    fprintf(fout, "# define YYLERR yysvec\n");
    fprintf(fout, "# define YYSTATE (yyestate-yysvec-1)\n");
    if (optim)
        fprintf(fout, "# define YYOPTIM 1\n");
#ifdef DEBUG
    fprintf(fout, "# define LEXDEBUG 1\n");
#endif
    fprintf(fout, "# ifndef YYLMAX \n");
    fprintf(fout, "# define YYLMAX BUFSIZ\n");
    fprintf(fout, "# endif \n");
    fprintf(fout, "#ifndef __cplusplus\n");
    if (widecio)
        fprintf(fout, "# define output(c) (void)putwc(c,yyout)\n");
    else
        fprintf(fout, "# define output(c) (void)putc(c,yyout)\n");
    fprintf(fout, "#else\n");
    if (widecio)
        fprintf(fout, "# define lex_output(c) (void)putwc(c,yyout)\n");
    else
        fprintf(fout, "# define lex_output(c) (void)putc(c,yyout)\n");
    fprintf(fout, "#endif\n");
    fprintf(fout, "\n#if defined(__cplusplus) || defined(__STDC__)\n");
    fprintf(fout, "\n#if defined(__cplusplus) && defined(__EXTERN_C__)\n");
    fprintf(fout, "extern \"C\" {\n");
    fprintf(fout, "#endif\n");
    fprintf(fout, "\tint yyback(int *, int);\n"); /* ? */
    fprintf(fout, "\tint yyinput(void);\n"); /* ? */
    fprintf(fout, "\tint yylook(void);\n"); /* ? */
    fprintf(fout, "\tvoid yyoutput(int);\n"); /* ? */
    fprintf(fout, "\tint yyracc(int);\n"); /* ? */
    fprintf(fout, "\tint yyreject(void);\n"); /* ? */
    fprintf(fout, "\tvoid yyunput(int);\n"); /* ? */
    fprintf(fout, "\tint yylex(void);\n");
    fprintf(fout, "#ifdef YYLEX_E\n");
    fprintf(fout, "\tvoid yywoutput(wchar_t);\n");
    fprintf(fout, "\twchar_t yywinput(void);\n");
    fprintf(fout, "\tvoid yywunput(wchar_t);\n");
    fprintf(fout, "#endif\n");

    /* XCU4: type of yyless is int */
    fprintf(fout, "#ifndef yyless\n");
    fprintf(fout, "\tint yyless(int);\n");
    fprintf(fout, "#endif\n");
    fprintf(fout, "#ifndef yywrap\n");
    fprintf(fout, "\tint yywrap(void);\n");
    fprintf(fout, "#endif\n");
    fprintf(fout, "#ifdef LEXDEBUG\n");
    fprintf(fout, "\tvoid allprint(char);\n");
    fprintf(fout, "\tvoid sprint(char *);\n");
    fprintf(fout, "#endif\n");
    fprintf(fout,
    "#if defined(__cplusplus) && defined(__EXTERN_C__)\n");
    fprintf(fout, "}\n");
    fprintf(fout, "#endif\n\n");
    fprintf(fout, "#ifdef __cplusplus\n");
    fprintf(fout, "extern \"C\" {\n");
    fprintf(fout, "#endif\n");
    fprintf(fout, "\tvoid exit(int);\n");
    fprintf(fout, "#ifdef __cplusplus\n");
    fprintf(fout, "}\n");
    fprintf(fout, "#endif\n\n");
    fprintf(fout, "#endif\n");
    fprintf(fout,
    "# define unput(c)"
    " {yytchar= (c);if(yytchar=='\\n')yylineno--;*yysptr++=yytchar;}\n");
    fprintf(fout, "# define yymore() (yymorfg=1)\n");
    if (widecio) {
        fprintf(fout, "#ifndef __cplusplus\n");
        fprintf(fout, "%s%d%s\n",
"# define input() (((yytchar=yysptr>yysbuf?U(*--yysptr):yygetwchar())==",
        ctable['\n'],
"?(yylineno++,yytchar):yytchar)==EOF?0:yytchar)");
        fprintf(fout, "#else\n");
        fprintf(fout, "%s%d%s\n",
"# define lex_input() (((yytchar=yysptr>yysbuf?U(*--yysptr):yygetwchar())==",
        ctable['\n'],
"?(yylineno++,yytchar):yytchar)==EOF?0:yytchar)");
        fprintf(fout, "#endif\n");
        fprintf(fout,
        "# define ECHO (void)fprintf(yyout, \"%%ls\",yytext)\n");
        fprintf(fout,
        "# define REJECT { nstr = yyreject_w(); goto yyfussy;}\n");
        fprintf(fout, "#define yyless yyless_w\n");
        fprintf(fout, "int yyreject_w(void);\n");
        fprintf(fout, "int yyleng;\n");

        /*
         * XCU4:
         * If %array, yytext[] contains the token.
         * If %pointer, yytext is a pointer to yy_tbuf[].
         */

        if (isArray) {
            fprintf(fout, "#define YYISARRAY\n");
            fprintf(fout, "wchar_t yytext[YYLMAX];\n");
        } else {
            fprintf(fout, "wchar_t yy_tbuf[YYLMAX];\n");
            fprintf(fout, "wchar_t * yytext = yy_tbuf;\n");
            fprintf(fout, "int yytextsz = YYLMAX;\n");
            fprintf(fout, "#ifndef YYTEXTSZINC\n");
            fprintf(fout, "#define YYTEXTSZINC 100\n");
            fprintf(fout, "#endif\n");
        }
    } else {
        fprintf(fout, "#ifndef __cplusplus\n");
        fprintf(fout, "%s%d%s\n",
"# define input() (((yytchar=yysptr>yysbuf?U(*--yysptr):getc(yyin))==",
        ctable['\n'],
"?(yylineno++,yytchar):yytchar)==EOF?0:yytchar)");
        fprintf(fout, "#else\n");
        fprintf(fout, "%s%d%s\n",
"# define lex_input() (((yytchar=yysptr>yysbuf?U(*--yysptr):getc(yyin))==",
        ctable['\n'],
"?(yylineno++,yytchar):yytchar)==EOF?0:yytchar)");
        fprintf(fout, "#endif\n");
        fprintf(fout, "#define ECHO fprintf(yyout, \"%%s\",yytext)\n");
        if (handleeuc) {
            fprintf(fout,
"# define REJECT { nstr = yyreject_e(); goto yyfussy;}\n");
            fprintf(fout, "int yyreject_e(void);\n");
            fprintf(fout, "int yyleng;\n");
            fprintf(fout, "size_t yywleng;\n");
            /*
             * XCU4:
             * If %array, yytext[] contains the token.
             * If %pointer, yytext is a pointer to yy_tbuf[].
             */
            if (isArray) {
                fprintf(fout, "#define YYISARRAY\n");
                fprintf(fout,
                "unsigned char yytext[YYLMAX*MB_LEN_MAX];\n");
                fprintf(fout,
                "wchar_t yywtext[YYLMAX];\n");
            } else {
                fprintf(fout,
                "wchar_t yy_twbuf[YYLMAX];\n");
                fprintf(fout,
                "wchar_t yy_tbuf[YYLMAX*MB_LEN_MAX];\n");
                fprintf(fout,
                "unsigned char * yytext ="
                "(unsigned char *)yy_tbuf;\n");
                fprintf(fout,
                "wchar_t * yywtext = yy_twbuf;\n");
                fprintf(fout,
                        "int yytextsz = YYLMAX;\n");
                fprintf(fout, "#ifndef YYTEXTSZINC\n");
                fprintf(fout, "#define YYTEXTSZINC 100\n");
                fprintf(fout, "#endif\n");
            }
        } else {
            fprintf(fout,
"# define REJECT { nstr = yyreject(); goto yyfussy;}\n");
            fprintf(fout, "int yyleng;\n");

            /*
             * XCU4:
             * If %array, yytext[] contains the token.
             * If %pointer, yytext is a pointer to yy_tbuf[].
             */
            if (isArray) {
                fprintf(fout, "#define YYISARRAY\n");
                fprintf(fout, "char yytext[YYLMAX];\n");
            } else {
                fprintf(fout, "char yy_tbuf[YYLMAX];\n");
                fprintf(fout,
                "char * yytext = yy_tbuf;\n");
                fprintf(fout,
                    "int yytextsz = YYLMAX;\n");
                fprintf(fout, "#ifndef YYTEXTSZINC\n");
                fprintf(fout,
                    "#define YYTEXTSZINC 100\n");
                fprintf(fout, "#endif\n");
            }
        }
    }
    fprintf(fout, "int yymorfg;\n");
    if (handleeuc)
        fprintf(fout, "extern wchar_t *yysptr, yysbuf[];\n");
    else
        fprintf(fout, "extern char *yysptr, yysbuf[];\n");
    fprintf(fout, "int yytchar;\n");
    fprintf(fout, "FILE *yyin = (FILE *)-1, *yyout = (FILE *)-1;\n");
    fprintf(fout, "#if defined (__GNUC__)\n");
    fprintf(fout,
        "static void _yyioinit(void) __attribute__ ((constructor));\n");
    fprintf(fout, "#elif defined (__SUNPRO_C)\n");
    fprintf(fout, "#pragma init (_yyioinit)\n");
    fprintf(fout, "#elif defined (__HP_aCC) || defined (__hpux)\n");
    fprintf(fout, "#pragma INIT \"_yyioinit\"\n");
    fprintf(fout, "#endif\n");
    fprintf(fout, "static void _yyioinit(void) {\n");
    fprintf(fout, "yyin = stdin; yyout = stdout; }\n");
    fprintf(fout, "extern int yylineno;\n");
    fprintf(fout, "struct yysvf { \n");
    fprintf(fout, "\tstruct yywork *yystoff;\n");
    fprintf(fout, "\tstruct yysvf *yyother;\n");
    fprintf(fout, "\tint *yystops;};\n");
    fprintf(fout, "struct yysvf *yyestate;\n");
    fprintf(fout, "extern struct yysvf yysvec[], *yybgin;\n");
}

static void
rhd1(void)
{
    fprintf(fout, "integer function yylex(dummy)\n");
    fprintf(fout, "define YYLMAX 200\n");
    fprintf(fout, "define ECHO call yyecho(yytext,yyleng)\n");
    fprintf(fout,
    "define REJECT nstr = yyrjct(yytext,yyleng);goto 30998\n");
    fprintf(fout, "integer nstr,yylook,yywrap\n");
    fprintf(fout, "integer yyleng, yytext(YYLMAX)\n");
    fprintf(fout, "common /yyxel/ yyleng, yytext\n");
    fprintf(fout,
    "common /yyldat/ yyfnd, yymorf, yyprev, yybgin, yylsp, yylsta\n");
    fprintf(fout,
    "integer yyfnd, yymorf, yyprev, yybgin, yylsp, yylsta(YYLMAX)\n");
    fprintf(fout, "for(;;){\n");
    fprintf(fout, "\t30999 nstr = yylook(dummy)\n");
    fprintf(fout, "\tgoto 30998\n");
    fprintf(fout, "\t30000 k = yywrap(dummy)\n");
    fprintf(fout, "\tif(k .ne. 0){\n");
    fprintf(fout, "\tyylex=0; return; }\n");
    fprintf(fout, "\t\telse goto 30998\n");
}

void
phead2(void)
{
    if (!ratfor)
        chd2();
}

static void
chd2(void)
{
    fprintf(fout, "if (yyin == (FILE *)-1) yyin = stdin;\n");
    fprintf(fout, "if (yyout == (FILE *)-1) yyout = stdout;\n");
    fprintf(fout, "#if defined (__cplusplus) || defined (__GNUC__)\n");
    fprintf(fout,
    "/* to avoid CC and lint complaining yyfussy not being used ...*/\n");
    fprintf(fout, "{static int __lex_hack = 0;\n");
    fprintf(fout, "if (__lex_hack) { yyprevious = 0; goto yyfussy; } }\n");
    fprintf(fout, "#endif\n");
    fprintf(fout, "while((nstr = yylook()) >= 0)\n");
    fprintf(fout, "yyfussy: switch(nstr){\n");
    fprintf(fout, "case 0:\n");
    fprintf(fout, "if(yywrap()) return(0); break;\n");
}

void
ptail(void)
{
    if (!pflag)
        ratfor ? rtail() : ctail();
    pflag = 1;
}

static void
ctail(void)
{
    fprintf(fout, "case -1:\nbreak;\n");        /* for reject */
    fprintf(fout, "default:\n");
    fprintf(fout,
    "(void)fprintf(yyout,\"bad switch yylook %%d\",nstr);\n");
    fprintf(fout, "} return(0); }\n");
    fprintf(fout, "/* end of yylex */\n");
}

static void
rtail(void)
{
    int i;
    fprintf(fout,
    "\n30998 if(nstr .lt. 0 .or. nstr .gt. %d)goto 30999\n", casecount);
    fprintf(fout, "nstr = nstr + 1\n");
    fprintf(fout, "goto(\n");
    for (i = 0; i < casecount; i++)
        fprintf(fout, "%d,\n", 30000+i);
    fprintf(fout, "30999),nstr\n");
    fprintf(fout, "30997 continue\n");
    fprintf(fout, "}\nend\n");
}

void
statistics(void)
{
    fprintf(errorf,
"%d/%d nodes(%%e), %d/%d positions(%%p), %d/%d (%%n), %ld transitions,\n",
    tptr, treesize, nxtpos-positions, maxpos, stnum + 1, nstates, rcount);
    fprintf(errorf,
    "%d/%d packed char classes(%%k), ", pcptr-pchar, pchlen);
    if (optim)
        fprintf(errorf,
        " %d/%d packed transitions(%%a), ", nptr, ntrans);
    fprintf(errorf, " %d/%d output slots(%%o)", yytop, outsize);
    putc('\n', errorf);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/getopt.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/getopt.c used in: Step 1013
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)getopt.c  1.9 (gritter) 4/2/07
 */
/* from OpenSolaris "getopt.c   1.23    05/06/08 SMI" */

/*  Copyright (c) 1988 AT&T */
/*    All Rights Reserved   */


/*
 * See getopt(3C) and SUS/XPG getopt() for function definition and
 * requirements.
 *
 * This actual implementation is a bit looser than the specification
 * as it allows any character other than ':' to be used as an option
 * character - The specification only guarantees the alnum characters
 * ([a-z][A-Z][0-9]).
 */

#include <unistd.h>
#include <string.h>
#include <stdio.h>

char    *optarg = NULL;
int optind = 1;
int opterr = 1;
int optopt = 0;

#define ERR(s, c)   err(s, c, optstring, argv[0])
static void
err(const char *s, int c, const char *optstring, const char *argv0)
{
    char errbuf[256], *ep = errbuf;
    const char  *cp;

    if (opterr && optstring[0] != ':') {
        for (cp = argv0; *cp && ep<&errbuf[sizeof errbuf]; cp++, ep++)
            *ep = *cp;
        for (cp = ": "; *cp && ep<&errbuf[sizeof errbuf]; cp++, ep++)
            *ep = *cp;
        for (cp = s; *cp && ep<&errbuf[sizeof errbuf]; cp++, ep++)
            *ep = *cp;
        for (cp = " -- "; *cp && ep<&errbuf[sizeof errbuf]; cp++, ep++)
            *ep = *cp;
        if (ep<&errbuf[sizeof errbuf])
            *ep++ = c;
        if (ep<&errbuf[sizeof errbuf])
            *ep++ = '\n';
        write(2, errbuf, ep - errbuf);
    }
}

/*
 * getopt_sp is required to keep state between successive calls to getopt()
 * while extracting aggregated options (ie: -abcd). Hence, getopt() is not
 * thread safe or reentrant, but it really doesn't matter.
 *
 * So, why isn't this "static" you ask?  Because the historical Bourne
 * shell has actually latched on to this little piece of private data.
 */
int getopt_sp = 1;

/*
 * Determine if the specified character (c) is present in the string
 * (optstring) as a regular, single character option. If the option is found,
 * return a pointer into optstring pointing at the option character,
 * otherwise return null. The character ':' is not allowed.
 */
static char *
parse(const char *optstring, const char c)
{
    char *cp = (char *)optstring;

    if (c == ':')
        return (NULL);
    do {
        if (*cp == c)
            return (cp);
    } while (*cp++ != '\0');
    return (NULL);
}

/*
 * External function entry point.
 */
int
getopt(int argc, char *const *argv, const char *optstring)
{
    char    c;
    char    *cp;

    /*
     * Has the end of the options been encountered?  The following
     * implements the SUS requirements:
     *
     * If, when getopt() is called:
     *  argv[optind]    is a null pointer
     *  *argv[optind]   is not the character '-'
     *  argv[optind]    points to the string "-"
     * getopt() returns -1 without changing optind. If
     *  argv[optind]    points to the string "--"
     * getopt() returns -1 after incrementing optind.
     */
    if (getopt_sp == 1) {
        if (optind >= argc || argv[optind][0] != '-' ||
            argv[optind] == NULL || argv[optind][1] == '\0')
            return (EOF);
        else if (strcmp(argv[optind], "--") == 0) {
            optind++;
            return (EOF);
        }
    }

    /*
     * Getting this far indicates that an option has been encountered.
     * Note that the syntax of optstring applies special meanings to
     * the characters ':' and '(', so they are not permissible as
     * option letters. A special meaning is also applied to the ')'
     * character, but its meaning can be determined from context.
     * Note that the specification only requires that the alnum
     * characters be accepted.
     */
    optopt = c = (unsigned char)argv[optind][getopt_sp];
    optarg = NULL;
    if ((cp = parse(optstring, c)) == NULL) {
        /* LINTED: variable format specifier */
        ERR("illegal option", c);
        if (argv[optind][++getopt_sp] == '\0') {
            optind++;
            getopt_sp = 1;
        }
        return ('?');
    }
    optopt = c = *cp;

    /*
     * A valid option has been identified.  If it should have an
     * option-argument, process that now.  SUS defines the setting
     * of optarg as follows:
     *
     *   1. If the option was the last character in the string pointed to
     *  by an element of argv, then optarg contains the next element
     *  of argv, and optind is incremented by 2. If the resulting
     *  value of optind is not less than argc, this indicates a
     *  missing option-argument, and getopt() returns an error
     *  indication.
     *
     *   2. Otherwise, optarg points to the string following the option
     *  character in that element of argv, and optind is incremented
     *  by 1.
     *
     * The second clause allows -abcd (where b requires an option-argument)
     * to be interpreted as "-a -b cd".
     */
    if (*(cp + 1) == ':') {
        /* The option takes an argument */
        if (argv[optind][getopt_sp+1] != '\0') {
            optarg = &argv[optind++][getopt_sp+1];
        } else if (++optind >= argc) {
            /* LINTED: variable format specifier */
            ERR("option requires an argument", c);
            getopt_sp = 1;
            optarg = NULL;
            return (optstring[0] == ':' ? ':' : '?');
        } else
            optarg = argv[optind++];
        getopt_sp = 1;
    } else {
        /* The option does NOT take an argument */
        if (argv[optind][++getopt_sp] == '\0') {
            getopt_sp = 1;
            optind++;
        }
        optarg = NULL;
    }
    return (c);
} /* getopt() */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/lsearch.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/lsearch.c used in: Step 1014
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*  from OpenSolaris "lsearch.c 1.15    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)lsearch.c 1.4 (gritter) 11/26/05
 */

/*  Copyright (c) 1988 AT&T */
/*    All Rights Reserved   */


/*
 * Linear search algorithm, generalized from Knuth (6.1) Algorithm Q.
 *
 * This version no longer has anything to do with Knuth's Algorithm Q,
 * which first copies the new element into the table, then looks for it.
 * The assumption there was that the cost of checking for the end of the
 * table before each comparison outweighed the cost of the comparison, which
 * isn't true when an arbitrary comparison function must be called and when the
 * copy itself takes a significant number of cycles.
 * Actually, it has now reverted to Algorithm S, which is "simpler."
 */

#include <sys/types.h>
#include <stddef.h>
#include <string.h>
#include "search.h"

void *
xlsearch(const void *ky, void *bs, unsigned *nelp, unsigned width,
    int (*compar)(const void *, const void *))
{
    char *key = (char *)ky;
    char *base = (char *)bs;
    char *next = base + *nelp * width;  /* End of table */
    void *res;

    for (; base < next; base += width)
        if ((*compar)(key, base) == 0)
            return (base);  /* Key found */
    ++*nelp;            /* Not found, add to table */
    res = memcpy(base, key, width); /* base now == next */
    return (res);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/search.h

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/search.h used in: Step 1014
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*  Copyright (c) 1988 AT&T */
/*    All Rights Reserved   */


/*
 * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#ifndef SEARCH_H
#define SEARCH_H

/*  from OpenSolaris "search.h  1.19    05/06/08 SMI"    SVr4.0 1.3.1.11    */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)search.h  1.4 (gritter) 11/26/05
 */

#include <sys/types.h>


void *xlsearch(const void *, void *, unsigned *, unsigned,
        int (*)(const void *, const void *));

#endif  /* SEARCH_H */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/libmain.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/libmain.c used in: Step 1017
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*  Copyright (c) 1989 AT&T */
/*    All Rights Reserved   */


/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*  from OpenSolaris "libmain.c 6.6 05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)libmain.c 1.4 (gritter) 11/26/05
 */

#include "stdio.h"

extern int  yylex(void);

int
main(void)
{
    yylex();
    return (0);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/yyless.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/yyless.c used in: Step 1019, Step 1023, Step 1025
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*  Copyright (c) 1988 AT&T */
/*    All Rights Reserved   */


/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*  from OpenSolaris "yyless.c  6.14    05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)yyless.c  1.6 (gritter) 11/27/05
 */

#include <stdlib.h>
#ifdef  __sun
#include <sys/euc.h>
#include <widec.h>
#endif
#include <limits.h>
#include <inttypes.h>
#include <unistd.h>

extern int yyprevious;

#ifndef JLSLEX
#define CHR    char

extern CHR yytext[];

#define YYTEXT yytext
#define YYLENG yyleng
#define YYINPUT yyinput
#define YYUNPUT yyunput
#define YYOUTPUT yyoutput
#endif

#ifdef WOPTION
#define CHR    wchar_t

extern CHR yytext[];

#define YYTEXT yytext
#define YYLENG yyleng
#define YYINPUT yyinput
#define YYUNPUT yyunput
#define YYOUTPUT yyoutput
#define yyless yyless_w
#endif

#ifdef EOPTION
#define CHR    wchar_t

extern int yyleng;
extern CHR yytext[];
extern CHR yywtext[];

#define YYTEXT yywtext
#define YYLENG yywleng
#define YYINPUT yywinput
#define YYUNPUT yywunput
#define YYOUTPUT yywoutput
#define yyless yyless_e
#endif

extern int YYLENG;
#if defined(__STDC__)
    extern void YYUNPUT(int);
#endif

#if defined(__cplusplus) || defined(__STDC__)
/* XCU4: type of yyless() changes to int */
int
yyless(int x)
#else
yyless(x)
int x;
#endif
{
    register CHR *lastch, *ptr;

    lastch = YYTEXT+YYLENG;
    if (x >= 0 && x <= YYLENG)
        ptr = x + YYTEXT;
    else {
        if (sizeof (int) != sizeof (intptr_t)) {
            static int seen = 0;

            if (!seen) {
                write(2,
                "warning: yyless pointer arg truncated\n", 39);
                seen = 1;
            }
        }
    /*
     * The cast on the next line papers over an unconscionable nonportable
     * glitch to allow the caller to hand the function a pointer instead of
     * an integer and hope that it gets figured out properly.  But it's
     * that way on all systems.
     */
        ptr = (CHR *)(intptr_t)x;
    }
    while (lastch > ptr)
        YYUNPUT(*--lastch);
    *lastch = 0;
    if (ptr > YYTEXT)
        yyprevious = *--lastch;
    YYLENG = ptr-YYTEXT;
#ifdef EOPTION
    yyleng = wcstombs((char *)yytext, YYTEXT, YYLENG*MB_LEN_MAX);
#endif
    return (0);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/yywrap.c

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/yywrap.c used in: Step 1020
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*  Copyright (c) 1989 AT&T */
/*    All Rights Reserved   */


/*  from OpenSolaris "yywrap.c  6.4 05/06/08 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)yywrap.c  1.3 (gritter) 6/18/05
 */

#if defined(__cplusplus) || defined(__STDC__)
int yywrap(void)
#else
yywrap()
#endif
{
    return(1);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/ncform

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/build/heirloom-devtools-070527/lex/ncform used in: Step 1031
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*  Copyright (c) 1989 AT&T */
/*    All Rights Reserved   */

/*  from OpenSolaris "ncform    6.14    05/06/10 SMI"   */

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)ncform    1.4 (gritter) 11/18/05
 */

int yylineno =1;
# define YYU(x) x
# define NLSTATE yyprevious=YYNEWLINE
struct yysvf *yylstate [YYLMAX], **yylsp, **yyolsp;
char yysbuf[YYLMAX];
char *yysptr = yysbuf;
int *yyfnd;
extern struct yysvf *yyestate;
int yyprevious = YYNEWLINE;
#if defined(__cplusplus) || defined(__STDC__)
int yylook(void)
#else
yylook()
#endif
{
    register struct yysvf *yystate, **lsp;
    register struct yywork *yyt;
    struct yysvf *yyz;
    int yych, yyfirst;
    struct yywork *yyr;
# ifdef LEXDEBUG
    int debug;
# endif
    char *yylastch;
    /* start off machines */
# ifdef LEXDEBUG
    debug = 0;
# endif
    yyfirst=1;
    if (!yymorfg)
        yylastch = yytext;
    else {
        yymorfg=0;
        yylastch = yytext+yyleng;
        }
    for(;;){
        lsp = yylstate;
        yyestate = yystate = yybgin;
        if (yyprevious==YYNEWLINE) yystate++;
        for (;;){
# ifdef LEXDEBUG
            if(debug)fprintf(yyout,"state %d\n",yystate-yysvec-1);
# endif
            yyt = yystate->yystoff;
            if(yyt == yycrank && !yyfirst){  /* may not be any transitions */
                yyz = yystate->yyother;
                if(yyz == 0)break;
                if(yyz->yystoff == yycrank)break;
                }
#ifndef __cplusplus
            *yylastch++ = yych = input();
#else
            *yylastch++ = yych = lex_input();
#endif
#ifdef YYISARRAY
            if(yylastch > &yytext[YYLMAX]) {
                fprintf(yyout,"Input string too long, limit %d\n",YYLMAX);
                exit(1);
            }
#else
            if (yylastch >= &yytext[ yytextsz ]) {
                int x = yylastch - yytext;

                yytextsz += YYTEXTSZINC;
                if (yytext == yy_tbuf) {
                    yytext = (char *) malloc(yytextsz);
                    memcpy(yytext, yy_tbuf, sizeof (yy_tbuf));
                }
                else
                    yytext = (char *) realloc(yytext, yytextsz);
                if (!yytext) {
                    fprintf(yyout,
                    "Cannot realloc yytext\n");
                    exit(1);
                }
                yylastch = yytext + x;
            }
#endif
            yyfirst=0;
        tryagain:
# ifdef LEXDEBUG
            if(debug){
                fprintf(yyout,"char ");
                allprint(yych);
                putchar('\n');
                }
# endif
            yyr = yyt;
            if ( yyt > yycrank){
                yyt = yyr + yych;
                if (yyt <= yytop && yyt->verify+yysvec == yystate){
                    if(yyt->advance+yysvec == YYLERR)   /* error transitions */
                        {unput(*--yylastch);break;}
                    *lsp++ = yystate = yyt->advance+yysvec;
                    if(lsp > &yylstate[YYLMAX]) {
                        fprintf(yyout,"Input string too long, limit %d\n",YYLMAX);
                        exit(1);
                    }
                    goto contin;
                    }
                }
# ifdef YYOPTIM
            else if(yyt < yycrank) {    /* r < yycrank */
                yyt = yyr = yycrank+(yycrank-yyt);
# ifdef LEXDEBUG
                if(debug)fprintf(yyout,"compressed state\n");
# endif
                yyt = yyt + yych;
                if(yyt <= yytop && yyt->verify+yysvec == yystate){
                    if(yyt->advance+yysvec == YYLERR)   /* error transitions */
                        {unput(*--yylastch);break;}
                    *lsp++ = yystate = yyt->advance+yysvec;
                    if(lsp > &yylstate[YYLMAX]) {
                        fprintf(yyout,"Input string too long, limit %d\n",YYLMAX);
                        exit(1);
                    }
                    goto contin;
                    }
                yyt = yyr + YYU(yymatch[yych]);
# ifdef LEXDEBUG
                if(debug){
                    fprintf(yyout,"try fall back character ");
                    allprint(YYU(yymatch[yych]));
                    putchar('\n');
                    }
# endif
                if(yyt <= yytop && yyt->verify+yysvec == yystate){
                    if(yyt->advance+yysvec == YYLERR)   /* error transition */
                        {unput(*--yylastch);break;}
                    *lsp++ = yystate = yyt->advance+yysvec;
                    if(lsp > &yylstate[YYLMAX]) {
                        fprintf(yyout,"Input string too long, limit %d\n",YYLMAX);
                        exit(1);
                    }
                    goto contin;
                    }
                }
            if ((yystate = yystate->yyother) && (yyt= yystate->yystoff) != yycrank){
# ifdef LEXDEBUG
                if(debug)fprintf(yyout,"fall back to state %d\n",yystate-yysvec-1);
# endif
                goto tryagain;
                }
# endif
            else
                {unput(*--yylastch);break;}
        contin:
# ifdef LEXDEBUG
            if(debug){
                fprintf(yyout,"state %d char ",yystate-yysvec-1);
                allprint(yych);
                putchar('\n');
                }
# endif
            ;
            }
# ifdef LEXDEBUG
        if(debug){
            fprintf(yyout,"stopped at %d with ",*(lsp-1)-yysvec-1);
            allprint(yych);
            putchar('\n');
            }
# endif
        while (lsp-- > yylstate){
            *yylastch-- = 0;
            if (*lsp != 0 && (yyfnd= (*lsp)->yystops) && *yyfnd > 0){
                yyolsp = lsp;
                if(yyextra[*yyfnd]){        /* must backup */
                    while(yyback((*lsp)->yystops,-*yyfnd) != 1 && lsp > yylstate){
                        lsp--;
                        unput(*yylastch--);
                        }
                    }
                yyprevious = YYU(*yylastch);
                yylsp = lsp;
                yyleng = yylastch-yytext+1;
                yytext[yyleng] = 0;
# ifdef LEXDEBUG
                if(debug){
                    fprintf(yyout,"\nmatch ");
                    sprint(yytext);
                    fprintf(yyout," action %d\n",*yyfnd);
                    }
# endif
                return(*yyfnd++);
                }
            unput(*yylastch);
            }
        if (yytext[0] == 0  /* && feof(yyin) */)
            {
            yysptr=yysbuf;
            return(0);
            }
#ifndef __cplusplus
        yyprevious = yytext[0] = input();
        if (yyprevious>0)
            output(yyprevious);
#else
        yyprevious = yytext[0] = lex_input();
        if (yyprevious>0)
            lex_output(yyprevious);
#endif
        yylastch=yytext;
# ifdef LEXDEBUG
        if(debug)putchar('\n');
# endif
        }
    }
#if defined(__cplusplus) || defined(__STDC__)
int yyback(int *p, int m)
#else
yyback(p, m)
    int *p;
#endif
{
    if (p==0) return(0);
    while (*p) {
        if (*p++ == m)
            return(1);
    }
    return(0);
}
    /* the following are only used in the lex library */
#if defined(__cplusplus) || defined(__STDC__)
int yyinput(void)
#else
yyinput()
#endif
{
#ifndef __cplusplus
    return(input());
#else
    return(lex_input());
#endif
    }
#if defined(__cplusplus) || defined(__STDC__)
void yyoutput(int c)
#else
yyoutput(c)
  int c; 
#endif
{
#ifndef __cplusplus
    output(c);
#else
    lex_output(c);
#endif
    }
#if defined(__cplusplus) || defined(__STDC__)
void yyunput(int c)
#else
yyunput(c)
   int c; 
#endif
{
    unput(c);
    }

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/heirloom-devtools-070527/heirloom-devtools-070527.checksums

File: ../git/live-bootstrap/sysa/heirloom-devtools-070527/heirloom-devtools-070527.checksums used in: Step 1033
9f5186c86f8a2c7a0eb39d11f7017fc70d5a3940d56c648f93f6a3189ac1d67f  /usr/bin/yacc
0fa43f12e3e32987211a433a9692f939b2c5d0dc32cd5523f9ad50bb441ac580  /usr/bin/lex
ffe696afc1bda32a5f4035e29b3275cab73a27df7635ccbe02ed49a30374ccdd  /usr/lib/mes/libl.a
bf3fb293f1ff89ee3dbcb08166c64b7a6793b49a12673d7633e3353ebea80d4d  /yaccpar
ee0f187b844f50d64c912bfcb5d73706662846d6d8a90b8b1fb20dda60464734  /lex/ncform

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/sources

File: ../git/live-bootstrap/sysa/bash-2.05b/sources used in: Step 1035
https://mirrors.kernel.org/gnu/bash/bash-2.05b.tar.gz ba03d412998cc54bd0b0f2d6c32100967d3137098affdc2d32e6e7c11b163fe4

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/mk/main.mk

File: ../git/live-bootstrap/sysa/bash-2.05b/mk/main.mk used in: Step 1042
# SPDX-FileCopyrightText: 2021 Andrius Å tikonas <andrius@stikonas.eu>
# SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>
#
# SPDX-License-Identifier: GPL-3.0-or-later

include common.mk

CFLAGS  = \
          -I. \
          -Iinclude \
          -Ilib \
          -Ilib/sh \
          -Ibuiltins \
          $(COMMON_CFLAGS)

LDFLAGS   = -L. -Lbuiltins -static
LIBRARIES = libsh.a builtins/libbuiltins.a libglob.a libtilde.a

SHLIB_FILES = clktck getcwd getenv oslib setlinebuf strcasecmp strerror strtod \
    vprint itos rename zread zwrite shtty inet_aton netopen \
    strpbrk timeval clock makepath pathcanon pathphys stringlist stringvec \
    tmpfile spell strtrans strindex shquote snprintf mailstat fmtulong \
    fmtullong strtoll strtoull strtoimax strtoumax fmtumax netconn mktime \
    strftime xstrchr zcatfd
# FIXME: for some reason these don't get picked up correctly in the
# final linking cmd
SHLIB_ODD_FILES = zcatfd strtoumax spell pathphys
SHLIB_OBJS = $(addprefix lib/sh/, $(addsuffix .o, $(SHLIB_FILES)))
SHLIB_ODD_OBJS = $(addprefix lib/sh/, $(addsuffix .o, $(SHLIB_ODD_FILES)))

MKBUILTINS_OBJS = builtins/mkbuiltins.o
BUILTINS_DEFS = $(addprefix builtins/, $(addsuffix .def, $(BUILTINS_DEF_FILES)))

GLOB_FILES = glob strmatch smatch xmbsrtowcs
GLOB_OBJS = $(addprefix lib/glob/, $(addsuffix .o, $(GLOB_FILES)))

TILDE_OBJS = lib/tilde/tilde.o

MKSYNTAX_OBJS = mksyntax.o
MKSIGNAMES_OBJS = support/mksignames.o

FILES = shell eval y.tab general make_cmd print_cmd dispose_cmd execute_cmd \
    variables copy_cmd error expr flags nojobs subst hashcmd hashlib mailcheck \
    trap input unwind_prot pathexp sig test version alias array arrayfunc \
    braces bracecomp bashhist bashline list stringlib locale findcmd redir \
    pcomplete pcomplib syntax xmalloc siglist
OBJS = $(addsuffix .o, $(FILES))

all: bash

# Builtins

mkbuiltins: $(MKBUILTINS_OBJS)
    $(CC) $(CFLAGS) $(MKBUILTINS_OBJS) $(LDFLAGS) -o $@
    ./mkbuiltins -externfile builtins/builtext.h -structfile builtins/builtins.c -noproduction $(BUILTINS_DEFS)

# libsh

libsh.a: $(SHLIB_OBJS)
    $(AR) cr $@ $^

# libglob

libglob.a: $(GLOB_OBJS)
    $(AR) cr $@ $^

# libtilde

libtilde.a: $(TILDE_OBJS)
    $(AR) cr $@ $^

# The actual program

mksyntax: $(MKSYNTAX_OBJS)
    $(CC) $^ $(LDFLAGS) -o $@ -lgetopt

syntax.c: mksyntax
    ./mksyntax -o $@

mksignames: $(MKSIGNAMES_OBJS)
    $(CC) $^ $(LDFLAGS) -o $@ -lgetopt

signames.h: mksignames
    ./mksignames $@

y.tab.c: parse.y
    yacc -d $^

trap.c: signames.h

bash: libsh.a libglob.a libtilde.a $(OBJS)
    $(CC) -o bash $(LIBRARIES) $(OBJS) $(SHLIB_ODD_OBJS) $(LDFLAGS) -lsh -lbuiltins -lglob -ltilde

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/mk/builtins.mk

File: ../git/live-bootstrap/sysa/bash-2.05b/mk/builtins.mk used in: Step 1043
# SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>
#
# SPDX-License-Identifier: GPL-3.0-or-later

.SUFFIXES = .def

include ../common.mk

CFLAGS  = \
    -I. \
    -I.. \
    -I../include \
    -I../lib \
    $(COMMON_CFLAGS)

BUILTINS_DEFS = $(addsuffix .def, $(BUILTINS_DEF_FILES))
BUILTINS_DEF_OBJS = $(addsuffix .o, $(BUILTINS_DEF_FILES))
BUILTINS_STATIC_FILES = common evalstring evalfile getopt bashgetopt
BUILTINS_STATIC_OBJS = $(addsuffix .o, $(BUILTINS_STATIC_FILES))
BUILTINS_OBJS = $(BUILTINS_DEF_OBJS) $(BUILTINS_STATIC_OBJS)

%.o: %.def
    ../mkbuiltins $<
    $(CC) -c $(CFLAGS) -o $@ $*.c

libbuiltins.a: $(BUILTINS_OBJS) builtins.o
    $(AR) cr $@ $^

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/mk/common.mk

File: ../git/live-bootstrap/sysa/bash-2.05b/mk/common.mk used in: Step 1044
# SPDX-FileCopyrightText: 2021 Paul Dersey <pdersey@gmail.com>
# SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>
#
# SPDX-License-Identifier: GPL-3.0-or-later

CC      = tcc
LD      = tcc
AR      = tcc -ar

COMMON_CFLAGS  = \
    -DHAVE_DIRENT_H \
    -DSTRUCT_DIRENT_HAS_D_INO \
    -DHAVE_STDINT_H \
    -DHAVE_LIMITS_H \
    -DHAVE_STRING_H \
    -DHAVE_INTTYPES_H \
    -DRETSIGTYPE=void \
    -DHUGE_VAL=10000000000.0 \
    -DTERMIO_TTY_DRIVER \
    -DPREFER_STDARG \
    -DHAVE_DECL_STRTOL \
    -DHAVE_DECL_STRTOLL \
    -DHAVE_DECL_STRTOUL \
    -DHAVE_DECL_STRTOULL \
    -DHAVE_TZNAME \
    -DPIPESIZE=4096 \
    -DDEFAULT_PATH_VALUE=\"$(prefix)/bin\" \
    -DSTANDARD_UTILS_PATH=\"$(prefix)/bin\" \
    -DPPROMPT=\"$ \" \
    -DSPROMPT=\"$ \" \
    -DCONF_MACHTYPE=\"bootstrap\" \
    -DGETGROUPS_T=int \
    -DCOND_COMMAND \
    -DCONF_HOSTTYPE=\"i386\" \
    -DCONF_OSTYPE=\"linux\" \
    -DDEFAULT_MAIL_DIRECTORY=\"/fake-mail\" \
    -DVOID_SIGHANDLER \
    -DDISTVERSION=\"2.05b\" \
    -DBUILDVERSION=\"0\" \
    -DSCCSVERSION=\"2.05b\" \
    -DLC_ALL=\"C\" \
    -DHAVE_STRERROR \
    -DHAVE_MEMSET \
    -DHAVE_DUP2 \
    -DHAVE_STRTOUL \
    -DHAVE_STRTOULL \
    -DHAVE_STRCHR \
    -DHAVE_BCOPY \
    -DHAVE_BZERO \
    -DHAVE_POSIX_SIGNALS \
    -DHAVE_GETCWD \
    -DHAVE_SYS_SIGLIST \
    -Dendpwent\(x\)=0 \
    -Denable_hostname_completion\(on_or_off\)=0

BUILTINS_DEF_FILES = alias bind break builtin cd colon command complete declare \
    echo enable eval exec exit fc fg_bg hash history jobs kill let read return \
    set setattr shift source suspend test times trap type ulimit umask wait \
    getopts pushd shopt printf

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/patches/mes-libc.patch

File: ../git/live-bootstrap/sysa/bash-2.05b/patches/mes-libc.patch used in: Step 1049
SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>

SPDX-License-Identifier: GPL-2.0-or-later

mes libc does not have locale support...

diff --git lib/sh/snprintf.c lib/sh/snprintf.c
index 7669576..747aeba 100644
--- lib/sh/snprintf.c
+++ lib/sh/snprintf.c
@@ -376,7 +376,7 @@ static void xfree __P((void *));
        if ((p)->flags & PF_STAR_P) \
          (p)->precision = GETARG (int)
 
-#if defined (HAVE_LOCALE_H)
+#if 0
 #  define GETLOCALEDATA(d, t, g) \
       do \
    { \

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/snprintf.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/snprintf.c used in: Step 1049, Step 1180
/*
 build a test version with
   gcc -g -DDRIVER -I../.. -I../../include -o test-snprintf snprintf.c fmtu*long.o
*/
 
/*
   Unix snprintf implementation.
   derived from inetutils/libinetutils/snprintf.c Version 1.1

   Copyright (C) 2001 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General 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 License for more details.
   
   You should have received a copy of the GNU General License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   
   Revision History:

   1.1:
      *  added changes from Miles Bader
      *  corrected a bug with %f
      *  added support for %#g
      *  added more comments :-)
   1.0:
      *  supporting must ANSI syntaxic_sugars
   0.0:
      *  support %s %c %d

 THANKS(for the patches and ideas):
     Miles Bader
     Cyrille Rustom
     Jacek Slabocewiz
     Mike Parker(mouse)

*/

/*
 * Currently doesn't handle (and bash/readline doesn't use):
 *  *M$ width, precision specifications
 *  %N$ numbered argument conversions
 *  inf, nan floating values imperfect (if isinf(), isnan() not in libc)
 *  support for `F' is imperfect, since underlying printf may not handle it
 */

#define FLOATING_POINT

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#if defined(DRIVER) && !defined(HAVE_CONFIG_H)
#define HAVE_LONG_LONG
#define HAVE_LONG_DOUBLE
#ifdef __linux__
#define HAVE_PRINTF_A_FORMAT
#endif
#define HAVE_ISINF_IN_LIBC
#define PREFER_STDARG
#define HAVE_STRINGIZE
#define HAVE_LIMITS_H
#define HAVE_STDDEF_H
#define HAVE_LOCALE_H
#define intmax_t long
#endif

#if !defined (HAVE_SNPRINTF) || !defined (HAVE_ASPRINTF)

#include <bashtypes.h>

#if defined(PREFER_STDARG)
#  include <stdarg.h>
#else
#  include <varargs.h>
#endif

#ifdef HAVE_LIMITS_H
#  include <limits.h>
#endif
#include <bashansi.h>
#ifdef HAVE_STDDEF_H
#  include <stddef.h>
#endif
#include <chartypes.h>

#ifdef HAVE_STDINT_H
#  include <stdint.h>
#endif

#ifdef FLOATING_POINT
#  include <float.h>    /* for manifest constants */
#  include <stdio.h>    /* for sprintf */
#endif

#include <typemax.h>

#ifdef HAVE_LOCALE_H
#  include <locale.h>
#endif

#include "stdc.h"
#include <shmbutil.h>

#ifndef DRIVER
#  include "shell.h"
#else
#  define FL_PREFIX     0x01    /* add 0x, 0X, or 0 prefix as appropriate */
#  define FL_ADDBASE    0x02    /* add base# prefix to converted value */
#  define FL_HEXUPPER   0x04    /* use uppercase when converting to hex */
#  define FL_UNSIGNED   0x08    /* don't add any sign */
extern char *fmtulong __P((unsigned long int, int, char *, size_t, int));
extern char *fmtullong __P((unsigned long long int, int, char *, size_t, int));
#endif

#ifndef FREE
#  define FREE(x)   if (x) free (x)
#endif

/* Bound on length of the string representing an integer value of type T.
   Subtract one for the sign bit if T is signed;
   302 / 1000 is log10 (2) rounded up;
   add one for integer division truncation;
   add one more for a minus sign if t is signed.  */
#define INT_STRLEN_BOUND(t) \
  ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 \
     + 1 + TYPE_SIGNED (t))

/* conversion flags */
#define PF_ALTFORM  0x00001     /* # */
#define PF_HEXPREFIX    0x00002     /* 0[Xx] */
#define PF_LADJUST  0x00004     /* - */
#define PF_ZEROPAD  0x00008     /* 0 */
#define PF_PLUS     0x00010     /* + */
#define PF_SPACE    0x00020     /* ' ' */
#define PF_THOUSANDS    0x00040     /* ' */

#define PF_DOT      0x00080     /* `.precision' */
#define PF_STAR_P   0x00100     /* `*' after precision */
#define PF_STAR_W   0x00200     /* `*' before or without precision */

/* length modifiers */
#define PF_SIGNEDCHAR   0x00400     /* hh */
#define PF_SHORTINT 0x00800     /* h */
#define PF_LONGINT  0x01000     /* l */
#define PF_LONGLONG 0x02000     /* ll */
#define PF_LONGDBL  0x04000     /* L */
#define PF_INTMAX_T 0x08000     /* j */
#define PF_SIZE_T   0x10000     /* z */
#define PF_PTRDIFF_T    0x20000     /* t */

#define PF_ALLOCBUF 0x40000     /* for asprintf, vasprintf */

#define PFM_SN      0x01        /* snprintf, vsnprintf */
#define PFM_AS      0x02        /* asprintf, vasprintf */

#define ASBUFSIZE   128

#define x_digs  "0123456789abcdef"
#define X_digs  "0123456789ABCDEF"

static char intbuf[INT_STRLEN_BOUND(unsigned long) + 1];

static int decpoint;
static int thoussep;
static char *grouping;

/* 
 * For the FLOATING POINT FORMAT :
 *  the challenge was finding a way to
 *  manipulate the Real numbers without having
 *  to resort to mathematical function(it
 *  would require to link with -lm) and not
 *  going down to the bit pattern(not portable)
 *
 *  so a number, a real is:

      real = integral + fraction

      integral = ... + a(2)*10^2 + a(1)*10^1 + a(0)*10^0
      fraction = b(1)*10^-1 + b(2)*10^-2 + ...

      where:
       0 <= a(i) => 9 
       0 <= b(i) => 9 
 
    from then it was simple math
 */

/*
 * size of the buffer for the integral part
 * and the fraction part 
 */
#define MAX_INT  99 + 1 /* 1 for the null */
#define MAX_FRACT 307 + 1

/* 
 * These functions use static buffers to store the results,
 * and so are not reentrant
 */
#define itoa(n) fmtulong(n, 10, intbuf, sizeof(intbuf), 0);
#define dtoa(n, p, f) numtoa(n, 10, p, f)

#define SWAP_INT(a,b) {int t; t = (a); (a) = (b); (b) = t;}

#define GETARG(type)    (va_arg(args, type))

/* Macros that do proper sign extension and handle length modifiers.  Used
   for the integer conversion specifiers. */
#define GETSIGNED(p) \
  (((p)->flags & PF_LONGINT) \
    ? GETARG (long) \
    : (((p)->flags & PF_SHORTINT) ? (long)(short)GETARG (int) \
                      : (long)GETARG (int)))

#define GETUNSIGNED(p) \
  (((p)->flags & PF_LONGINT) \
    ? GETARG (unsigned long) \
    : (((p)->flags & PF_SHORTINT) ? (unsigned long)(unsigned short)GETARG (int) \
                      : (unsigned long)GETARG (unsigned int)))


#ifdef HAVE_LONG_DOUBLE
#define GETLDOUBLE(p) GETARG (long double)
#endif
#define GETDOUBLE(p) GETARG (double)

#define SET_SIZE_FLAGS(p, type) \
  if (sizeof (type) > sizeof (int)) \
    (p)->flags |= PF_LONGINT; \
  if (sizeof (type) > sizeof (long)) \
    (p)->flags |= PF_LONGLONG;

/* this struct holds everything we need */
struct DATA
{
  int length;
  char *base;       /* needed for [v]asprintf */
  char *holder;
  int counter;
  const char *pf;

/* FLAGS */
  int flags;
  int justify;
  int width, precision;
  char pad;
};

/* the floating point stuff */
#ifdef FLOATING_POINT
static double pow_10 __P((int));
static int log_10 __P((double));
static double integral __P((double, double *));
static char *numtoa __P((double, int, int, char **));
#endif

static void init_data __P((struct DATA *, char *, size_t, const char *, int));
static void init_conv_flag __P((struct DATA *));

/* for the format */
#ifdef FLOATING_POINT
static void floating __P((struct DATA *, double));
static void exponent __P((struct DATA *, double));
#endif
static void number __P((struct DATA *, unsigned long, int));
#ifdef HAVE_LONG_LONG
static void lnumber __P((struct DATA *, unsigned long long, int));
#endif
static void pointer __P((struct DATA *, unsigned long));
static void strings __P((struct DATA *, char *));

#ifdef FLOATING_POINT
#  define FALLBACK_FMTSIZE  32
#  define FALLBACK_BASE     4096
#  define LFALLBACK_BASE    5120
#  ifdef HAVE_LONG_DOUBLE
static void ldfallback __P((struct DATA *, const char *, const char *, long double));
#  endif
static void dfallback __P((struct DATA *, const char *, const char *, double));
#endif

static char *groupnum __P((char *));

#ifdef DRIVER
static void memory_error_and_abort ();
static void *xmalloc __P((size_t));
static void *xrealloc __P((void *, size_t));
static void xfree __P((void *));
#else
#  include <xmalloc.h>
#endif

/* those are defines specific to snprintf to hopefully
 * make the code clearer :-)
 */
#define RIGHT 1
#define LEFT  0
#define NOT_FOUND -1
#define FOUND 1
#define MAX_FIELD 15

/* round off to the precision */
#define ROUND(d, p) \
        (d < 0.) ? \
         d - pow_10(-(p)->precision) * 0.5 : \
         d + pow_10(-(p)->precision) * 0.5

/* set default precision */
#define DEF_PREC(p) \
        if ((p)->precision == NOT_FOUND) \
          (p)->precision = 6

/* put a char.  increment the number of chars written even if we've exceeded
   the vsnprintf/snprintf buffer size (for the return value) */
#define PUT_CHAR(c, p) \
    do \
      { \
        if (((p)->flags & PF_ALLOCBUF) && ((p)->counter >= (p)->length - 1)) \
          { \
        (p)->length += ASBUFSIZE; \
        (p)->base = (char *)xrealloc((p)->base, (p)->length); \
        (p)->holder = (p)->base + (p)->counter; /* in case reallocated */ \
          } \
        if ((p)->counter < (p)->length) \
          *(p)->holder++ = (c); \
        (p)->counter++; \
      } \
    while (0)

/* Output a string.  P->WIDTH has already been adjusted for padding. */
#define PUT_STRING(string, len, p) \
    do \
      { \
        PAD_RIGHT (p); \
        while ((len)-- > 0) \
          { \
        PUT_CHAR (*(string), (p)); \
        (string)++; \
          } \
        PAD_LEFT (p); \
      } \
    while (0)

#define PUT_PLUS(d, p, zero) \
        if ((d) > zero && (p)->justify == RIGHT) \
          PUT_CHAR('+', p)

#define PUT_SPACE(d, p, zero) \
        if (((p)->flags & PF_SPACE) && (d) > zero) \
          PUT_CHAR(' ', p)

/* pad right */ 
#define PAD_RIGHT(p) \
        if ((p)->width > 0 && (p)->justify != LEFT) \
          for (; (p)->width > 0; (p)->width--) \
         PUT_CHAR((p)->pad, p)

/* pad left */
#define PAD_LEFT(p) \
        if ((p)->width > 0 && (p)->justify == LEFT) \
          for (; (p)->width > 0; (p)->width--) \
         PUT_CHAR((p)->pad, p)

/* if width and prec. in the args */
#define STAR_ARGS(p) \
        if ((p)->flags & PF_STAR_W) \
          (p)->width = GETARG (int); \
        if ((p)->flags & PF_STAR_P) \
          (p)->precision = GETARG (int)

#if defined (HAVE_LOCALE_H)
#  define GETLOCALEDATA(d, t, g) \
      do \
    { \
      struct lconv *lv; \
      if ((d) == 0) { \
      (d) = '.'; (t) = -1; (g) = 0; /* defaults */ \
      lv = localeconv(); \
      if (lv) \
        { \
          if (lv->decimal_point && lv->decimal_point[0]) \
            (d) = lv->decimal_point[0]; \
          if (lv->thousands_sep && lv->thousands_sep[0]) \
            (t) = lv->thousands_sep[0]; \
          (g) = lv->grouping ? lv->grouping : ""; \
          if (*(g) == '\0' || *(g) == CHAR_MAX || (t) == -1) (g) = 0; \
        } \
      } \
    } \
      while (0);
#else
#  define GETLOCALEDATA(d, t, g) \
      ( (d) = '.', (t) = ',', g = "\003" )
#endif

#ifdef FLOATING_POINT
/*
 * Find the nth power of 10
 */
static double
pow_10(n)
     int n;
{ 
  double P;

  /* handle common cases with fast switch statement. */
  switch (n)
    {
    case -3:    return .001;
    case -2:    return .01;
    case -1:    return .1;
    case 0: return 1.;
    case 1: return 10.;
    case 2: return 100.;
    case 3: return 1000.;
    }

  if (n < 0)
    {
      P = .0001;
      for (n += 4; n < 0; n++)
    P /= 10.;
    }
  else
    {
      P = 10000.;
      for (n -= 4; n > 0; n--)
    P *= 10.;
    }

  return P;
}

/*
 * Find the integral part of the log in base 10 
 * Note: this not a real log10()
     I just need and approximation(integerpart) of x in:
      10^x ~= r
 * log_10(200) = 2;
 * log_10(250) = 2;
 */
static int
log_10(r)
     double r;
{ 
  int i = 0;
  double result = 1.;

  if (r < 0.)
    r = -r;

  if (r < 1.)
    {
      while (result >= r)
    {
      result /= 10.;
      i++;
    }
      return (-i);
    }
  else
    {
      while (result <= r)
    {
      result *= 10.;
      i++;
    }
      return (i - 1);
    }
}

/*
 * This function return the fraction part of a double
 * and set in ip the integral part.
 * In many ways it resemble the modf() found on most Un*x
 */
static double
integral(real, ip)
     double real;
     double *ip;
{ 
  int j;
  double i, s, p;
  double real_integral = 0.;

  /* take care of the obvious */
  /* equal to zero ? */
  if (real == 0.)
    {
      *ip = 0.;
      return (0.);
    }

  /* negative number ? */
  if (real < 0.)
    real = -real;

  /* a fraction ? */
  if ( real < 1.)
    {
      *ip = 0.;
      return real;
    }

  /* the real work :-) */
  for (j = log_10(real); j >= 0; j--)
    {
      p = pow_10(j);
      s = (real - real_integral)/p;
      i = 0.;
      while (i + 1. <= s)
    i++;
      real_integral += i*p;
    }
  *ip = real_integral;
  return (real - real_integral);
}

#define PRECISION 1.e-6
/* 
 * return an ascii representation of the integral part of the number
 * and set fract to be an ascii representation of the fraction part
 * the container for the fraction and the integral part or staticly
 * declare with fix size 
 */
static char *
numtoa(number, base, precision, fract)
     double number;
     int base, precision;
     char **fract;
{
  register int i, j;
  double ip, fp; /* integer and fraction part */
  double fraction;
  int digits = MAX_INT - 1;
  static char integral_part[MAX_INT];
  static char fraction_part[MAX_FRACT];
  double sign;
  int ch;

  /* taking care of the obvious case: 0.0 */
  if (number == 0.)
    { 
      integral_part[0] = '0';
      integral_part[1] = '\0';
      fraction_part[0] = '0';
      fraction_part[1] = '\0';
      if (fract)
    *fract = fraction_part;
      return integral_part;
    }

  /* for negative numbers */
  if ((sign = number) < 0.)
    {
      number = -number;
      digits--; /* sign consume one digit */
    }

  fraction = integral(number, &ip);
  number = ip;

  /* do the integral part */
  if (ip == 0.)
    {
      integral_part[0] = '0';
      i = 1;
    }
  else
    {
      for ( i = 0; i < digits && number != 0.; ++i)
    {
      number /= base;
      fp = integral(number, &ip);
      ch = (int)((fp + PRECISION)*base); /* force to round */
      integral_part[i] = (ch <= 9) ? ch + '0' : ch + 'a' - 10;
      if (! ISXDIGIT((unsigned char)integral_part[i]))
        break;  /* bail out overflow !! */
      number = ip;
     }
    }
     
  /* Oh No !! out of bound, ho well fill it up ! */
  if (number != 0.)
    for (i = 0; i < digits; ++i)
      integral_part[i] = '9';

  /* put the sign ? */
  if (sign < 0.)
    integral_part[i++] = '-';

  integral_part[i] = '\0';

  /* reverse every thing */
  for ( i--, j = 0; j < i; j++, i--)
    SWAP_INT(integral_part[i], integral_part[j]);  

  /* the fractional part */
  for (i=0, fp=fraction; precision > 0 && i < MAX_FRACT ; i++, precision--)
    {
      fraction_part[i] = (int)((fp + PRECISION)*10. + '0');
      if (! DIGIT(fraction_part[i])) /* underflow ? */
    break;
      fp = (fp*10.0) - (double)(long)((fp + PRECISION)*10.);
    }
  fraction_part[i] = '\0';

  if (fract != (char **)0)
    *fract = fraction_part;

  return integral_part;
}
#endif

/* for %d and friends, it puts in holder
 * the representation with the right padding
 */
static void
number(p, d, base)
     struct DATA *p;
     unsigned long d;
     int base;
{
  char *tmp, *t;
  long sd;
  int flags;

  sd = d;   /* signed for ' ' padding in base 10 */
  flags = (*p->pf == 'u' || *p->pf == 'U') ? FL_UNSIGNED : 0;
  if (*p->pf == 'X')
    flags |= FL_HEXUPPER;

  tmp = fmtulong (d, base, intbuf, sizeof(intbuf), flags);
  t = 0;
  if ((p->flags & PF_THOUSANDS))
    {
      GETLOCALEDATA(decpoint, thoussep, grouping);
      if (grouping && (t = groupnum (tmp)))
        tmp = t;
    }

  p->width -= strlen(tmp);
  PAD_RIGHT(p);

  switch (base)
    {
    case 10:
      PUT_PLUS(sd, p, 0);
      PUT_SPACE(sd, p, 0);
      break;
    case 8:
      if (p->flags & PF_ALTFORM)
    PUT_CHAR('0', p);
      break;
    case 16:
      if (p->flags & PF_ALTFORM)
    {
      PUT_CHAR('0', p);
      PUT_CHAR(*p->pf, p);
    }
      break;
    }

  while (*tmp)
    {
      PUT_CHAR(*tmp, p);
      tmp++;
    }

  PAD_LEFT(p);
  FREE (t);
}

#ifdef HAVE_LONG_LONG
/*
 * identical to number() but works for `long long'
 */
static void
lnumber(p, d, base)
     struct DATA *p;
     unsigned long long d;
     int base;
{
  char *tmp, *t;
  long long sd;
  int flags;

  sd = d;   /* signed for ' ' padding in base 10 */
  flags = (*p->pf == 'u' || *p->pf == 'U') ? FL_UNSIGNED : 0;
  if (*p->pf == 'X')
    flags |= FL_HEXUPPER;

  tmp = fmtullong (d, base, intbuf, sizeof(intbuf), flags);
  t = 0;
  if ((p->flags & PF_THOUSANDS))
    {
      GETLOCALEDATA(decpoint, thoussep, grouping);
      if (grouping && (t = groupnum (tmp)))
        tmp = t;
    }

  p->width -= strlen(tmp);
  PAD_RIGHT(p);

  switch (base)
    {
    case 10:
      PUT_PLUS(sd, p, 0);
      PUT_SPACE(sd, p, 0);
      break;
    case 8:
      if (p->flags & PF_ALTFORM)
    PUT_CHAR('0', p);
      break;
    case 16:
      if (p->flags & PF_ALTFORM)
    {
      PUT_CHAR('0', p);
      PUT_CHAR(*p->pf, p);
    }
      break;
    }

  while (*tmp)
    {
      PUT_CHAR(*tmp, p);
      tmp++;
    }

  PAD_LEFT(p);
  FREE (t);
}
#endif

static void
pointer(p, d)
     struct DATA *p;
     unsigned long d;
{
  char *tmp;

  tmp = fmtulong(d, 16, intbuf, sizeof(intbuf), 0);
  p->width -= strlen(tmp);
  PAD_RIGHT(p);

  /* prefix '0x' for pointers */
  PUT_CHAR('0', p);
  PUT_CHAR('x', p);

  while (*tmp)
    {
      PUT_CHAR(*tmp, p);
      tmp++;
    }
  PAD_LEFT(p);
}

/* %s strings */
static void
strings(p, tmp)
     struct DATA *p;
     char *tmp;
{
  size_t len;

  len = strlen(tmp);
  if (p->precision != NOT_FOUND) /* the smallest number */
    len = (len < p->precision ? len : p->precision);
  p->width -= len;

  PUT_STRING (tmp, len, p);
}

#if HANDLE_MULTIBYTE
/* %ls wide-character strings */
static void
wstrings(p, tmp)
     struct DATA *p;
     wchar_t *tmp;
{
  size_t len;
  mbstate_t mbs;
  char *os;
  const wchar_t *ws;

  memset (&mbs, '\0', sizeof (mbstate_t));
  ws = (const wchar_t *)tmp;

  os = (char *)NULL;
  if (p->precision != NOT_FOUND)
    {
      os = (char *)xmalloc (p->precision + 1);
      len = wcsrtombs (os, &ws, p->precision, &mbs);
    }
  else
    {
      len = wcsrtombs (NULL, &ws, 0, &mbs);
      if (len != (size_t)-1)
        {
      memset (&mbs, '\0', sizeof (mbstate_t));
      os = (char *)xmalloc (len + 1);
      (void)wcsrtombs (os, &ws, len + 1, &mbs);
        }
    }
  if (len == (size_t)-1)
    {
      /* invalid multibyte sequence; bail now. */
      FREE (os);      
      return;
    }

  p->width -= len;
  PUT_STRING (os, len, p);
  free (os);
}

static void
wchars (p, wc)
     struct DATA *p;
     wint_t wc;
{
  char *lbuf, *l;
  mbstate_t mbs;
  size_t len;

  lbuf = (char *)malloc (MB_CUR_MAX+1);
  if (lbuf == 0)
    return;
  memset (&mbs, '\0', sizeof (mbstate_t));
  len = wcrtomb (lbuf, wc, &mbs);
  if (len == (size_t)-1)
    /* conversion failed; bail now. */
    return;
  p->width -= len;
  l = lbuf;
  PUT_STRING (l, len, p);
  free (lbuf);
}
#endif /* HANDLE_MULTIBYTE */

#ifdef FLOATING_POINT

#ifndef HAVE_ISINF_IN_LIBC
/* Half-assed versions, since we don't want to link with libm. */
static int
isinf(d)
     double d;
{
#ifdef DBL_MAX
  if (d < DBL_MIN)
    return -1;
  else if (d > DBL_MAX)
    return 1;
  else
#endif
    return 0;
}

static int
isnan(d)
     double d;
{
  return 0;
}
#endif

/* Check for [+-]infinity and NaN.  If MODE == 1, we check for Infinity, else
   (mode == 2) we check for NaN.  This does the necessary printing.  Returns
   1 if Inf or Nan, 0 if not. */
static int
chkinfnan(p, d, mode)
     struct DATA *p;
     double d;
     int mode;      /* == 1 for inf, == 2 for nan */
{
  int i;
  char *tmp;
  char *big, *small;

  i = (mode == 1) ? isinf(d) : isnan(d);
  if (i == 0)
    return 0;
  big = (mode == 1) ? "INF" : "NAN";
  small = (mode == 1) ? "inf" : "nan";

  tmp = (*p->pf == 'F' || *p->pf == 'G' || *p->pf == 'E') ? big : small;

  if (i < 0)
    PUT_CHAR('-', p);

  while (*tmp)
    {
      PUT_CHAR (*tmp, p);
      tmp++;
    }

  return 1;
}

/* %f %F %g %G floating point representation */
static void
floating(p, d)
     struct DATA *p;
     double d;
{
  char *tmp, *tmp2, *t;
  int i;

  if (chkinfnan(p, d, 1) || chkinfnan(p, d, 2))
    return; /* already printed nan or inf */

  GETLOCALEDATA(decpoint, thoussep, grouping);
  DEF_PREC(p);
  d = ROUND(d, p);
  tmp = dtoa(d, p->precision, &tmp2);
  t = 0;
  if ((p->flags & PF_THOUSANDS) && grouping && (t = groupnum (tmp)))
    tmp = t;

  /* calculate the padding. 1 for the dot */
  p->width = p->width -
        ((d > 0. && p->justify == RIGHT) ? 1:0) -
        ((p->flags & PF_SPACE) ? 1:0) -
        strlen(tmp) - p->precision - 1;
  PAD_RIGHT(p);  
  PUT_PLUS(d, p, 0.);
  PUT_SPACE(d, p, 0.);

  while (*tmp)
    { /* the integral */
      PUT_CHAR(*tmp, p);
      tmp++;
    }
  FREE (t);

  if (p->precision != 0 || (p->flags & PF_ALTFORM))
    PUT_CHAR(decpoint, p);  /* put the '.' */

  if ((*p->pf == 'g' || *p->pf == 'G') && (p->flags & PF_ALTFORM) == 0)
    /* smash the trailing zeros unless altform */
    for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
      tmp2[i] = '\0'; 

  for (; *tmp2; tmp2++)
    PUT_CHAR(*tmp2, p); /* the fraction */
  
  PAD_LEFT(p);
} 

/* %e %E %g %G exponent representation */
static void
exponent(p, d)
     struct DATA *p;
     double d;
{
  char *tmp, *tmp2;
  int j, i;

  if (chkinfnan(p, d, 1) || chkinfnan(p, d, 2))
    return; /* already printed nan or inf */

  GETLOCALEDATA(decpoint, thoussep, grouping);
  DEF_PREC(p);
  j = log_10(d);
  d = d / pow_10(j);  /* get the Mantissa */
  d = ROUND(d, p);        
  tmp = dtoa(d, p->precision, &tmp2);

  /* 1 for unit, 1 for the '.', 1 for 'e|E',
   * 1 for '+|-', 2 for 'exp' */
  /* calculate how much padding need */
  p->width = p->width - 
         ((d > 0. && p->justify == RIGHT) ? 1:0) -
         ((p->flags & PF_SPACE) ? 1:0) - p->precision - 6;

  PAD_RIGHT(p);
  PUT_PLUS(d, p, 0.);
  PUT_SPACE(d, p, 0.);

  while (*tmp)
    {
      PUT_CHAR(*tmp, p);
      tmp++;
    }

  if (p->precision != 0 || (p->flags & PF_ALTFORM))
      PUT_CHAR(decpoint, p);  /* the '.' */

  if ((*p->pf == 'g' || *p->pf == 'G') && (p->flags & PF_ALTFORM) == 0)
    /* smash the trailing zeros unless altform */
    for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
      tmp2[i] = '\0'; 

  for (; *tmp2; tmp2++)
    PUT_CHAR(*tmp2, p); /* the fraction */

  /* the exponent put the 'e|E' */
  if (*p->pf == 'g' || *p->pf == 'e')
    PUT_CHAR('e', p);
  else
    PUT_CHAR('E', p);

  /* the sign of the exp */
  if (j > 0)
    PUT_CHAR('+', p);
  else
    {
      PUT_CHAR('-', p);
      j = -j;
    }

   tmp = itoa(j);
   /* pad out to at least two spaces.  pad with `0' if the exponent is a
      single digit. */
   if (j <= 9)
     PUT_CHAR('0', p);

   /* the exponent */
   while (*tmp)
     {
       PUT_CHAR(*tmp, p);
       tmp++;
     }
   PAD_LEFT(p);
}
#endif

/* Return a new string with the digits in S grouped according to the locale's
   grouping info and thousands separator.  If no grouping should be performed,
   this returns NULL; the caller needs to check for it. */
static char *
groupnum (s)
     char *s;
{
  char *se, *ret, *re, *g;
  int len, slen;

  if (grouping == 0 || *grouping <= 0 || *grouping == CHAR_MAX)
    return ((char *)NULL);

  /* find min grouping to size returned string */
  for (len = *grouping, g = grouping; *g; g++)
      if (*g > 0 && *g < len)
    len = *g;

  slen = strlen (s);
  len = slen / len + 1;
  ret = (char *)xmalloc (slen + len + 1);
  re = ret + slen + len;
  *re = '\0';

  g = grouping;
  se = s + slen;
  len = *g;

  while (se > s)
    {
      *--re = *--se;

      /* handle `-' inserted by numtoa() and the fmtu* family here. */
      if (se > s && se[-1] == '-')
    continue;

      /* begin new group. */
      if (--len == 0 && se > s)
    {
      *--re = thoussep;
      len = *++g;       /* was g++, but that uses first char twice (glibc bug, too) */
      if (*g == '\0')
        len = *--g;     /* use previous grouping */
      else if (*g == CHAR_MAX)
        {
          do
            *--re = *--se;
          while (se > s);
          break;
        }
    }
    }

  if (re > ret)
#ifdef HAVE_MEMMOVE
    memmove (ret, re, strlen (re) + 1);
#else
    strcpy (ret, re);
#endif
   
  return ret;
}

/* initialize the conversion specifiers */
static void
init_conv_flag (p)
     struct DATA *p;
{
  p->flags &= PF_ALLOCBUF;      /* preserve PF_ALLOCBUF flag */
  p->precision = p->width = NOT_FOUND;
  p->justify = NOT_FOUND;
  p->pad = ' ';
}

static void
init_data (p, string, length, format, mode)
     struct DATA *p;
     char *string;
     size_t length;
     const char *format;
     int mode;
{
  p->length = length - 1; /* leave room for '\0' */
  p->holder = p->base = string;
  p->pf = format;
  p->counter = 0;
  p->flags = (mode == PFM_AS) ? PF_ALLOCBUF : 0;
}

static int
#if defined (__STDC__)
vsnprintf_internal(struct DATA *data, char *string, size_t length, const char *format, va_list args)
#else
vsnprintf_internal(data, string, length, format, args)
     struct DATA *data;
     char *string;
     size_t length;
     const char *format;
     va_list args;
#endif
{
  double d; /* temporary holder */
#ifdef HAVE_LONG_DOUBLE
  long double ld;   /* for later */
#endif
  unsigned long ul;
#ifdef HAVE_LONG_LONG
  unsigned long long ull;
#endif
  int state, i, c, n;
  char *s;
#if HANDLE_MULTIBYTE
  wchar_t *ws;
  wint_t wc;
#endif
  const char *convstart;

  /* Sanity check, the string length must be >= 0.  C99 actually says that
     LENGTH can be zero here, in the case of snprintf/vsnprintf (it's never
     0 in the case of asprintf/vasprintf), and the return value is the number
     of characters that would have been written. */
  if (length < 0)
    return -1;

  if (format == 0)
    return 0;

  /* Reset these for each call because the locale might have changed. */
  decpoint = thoussep = 0;
  grouping = 0;

  for (; c = *(data->pf); data->pf++)
    {
      if (c != '%')
    {
      PUT_CHAR (c, data);
      continue;
    }

      convstart = data->pf;
      init_conv_flag (data); /* initialise format flags */

      state = 1;
      for (state = 1; state && *data->pf; )
    {
      c = *(++data->pf);
          /* fmtend = data->pf */
#if defined (FLOATING_POINT) && defined (HAVE_LONG_DOUBLE)
      if (data->flags & PF_LONGDBL)
        {
          switch (c)
        {
        case 'f': case 'F':
        case 'e': case 'E':
        case 'g': case 'G':
#  ifdef HAVE_PRINTF_A_FORMAT
        case 'a': case 'A':
#  endif
          STAR_ARGS (data);
          ld = GETLDOUBLE (data);
          ldfallback (data, convstart, data->pf, ld);
          goto conv_break;
        }
        }
#endif /* FLOATING_POINT && HAVE_LONG_DOUBLE */

      switch (c)
        {
          /* Parse format flags */
          case '\0': /* a NULL here ? ? bail out */
        *data->holder = '\0';
        return data->counter;
        break;
          case '#':
        data->flags |= PF_ALTFORM;
        continue;
          case '0':
        data->flags |= PF_ZEROPAD;
        data->pad = '0';
        continue;
          case '*':
        if (data->flags & PF_DOT)
          data->flags |= PF_STAR_P;
        else
          data->flags |= PF_STAR_W;
        continue;
          case '-':
        data->flags |= PF_LADJUST;
        data->justify = LEFT;
        continue;
          case ' ':
        if ((data->flags & PF_PLUS) == 0)
          data->flags |= PF_SPACE;
        continue;
          case '+':
        data->flags |= PF_PLUS;
        data->justify = RIGHT;
        continue;
          case '\'':
        data->flags |= PF_THOUSANDS;
        continue;

          case '1': case '2': case '3':
          case '4': case '5': case '6':
          case '7': case '8': case '9':
        n = 0;
        do
          {
            n = n * 10 + TODIGIT(c);
            c = *(++data->pf);
          }
        while (DIGIT(c));
        data->pf--;     /* went too far */
        if (n < 0)
          n = 0;
        if (data->flags & PF_DOT)
          data->precision = n;
        else
          data->width = n;
        continue;

          /* optional precision */
          case '.':
        data->flags |= PF_DOT;
        data->precision = 0;
        continue;

          /* length modifiers */
          case 'h':
        data->flags |= (data->flags & PF_SHORTINT) ? PF_SIGNEDCHAR : PF_SHORTINT;
        continue;
          case 'l':
        data->flags |= (data->flags & PF_LONGINT) ? PF_LONGLONG : PF_LONGINT;
        continue;
          case 'L':
        data->flags |= PF_LONGDBL;
        continue;
          case 'q':
        data->flags |= PF_LONGLONG;
        continue;
          case 'j':
        data->flags |= PF_INTMAX_T;
        SET_SIZE_FLAGS(data, intmax_t);
        continue;
          case 'z':
        data->flags |= PF_SIZE_T;
        SET_SIZE_FLAGS(data, size_t);
        continue;
          case 't':
        data->flags |= PF_PTRDIFF_T;
        SET_SIZE_FLAGS(data, ptrdiff_t);
        continue;
        
          /* Conversion specifiers */
#ifdef FLOATING_POINT
          case 'f':  /* float, double */
          case 'F':
        STAR_ARGS(data);
        d = GETDOUBLE(data);
        floating(data, d);
conv_break:     
        state = 0;
        break;
          case 'g': 
          case 'G':
        STAR_ARGS(data);
        DEF_PREC(data);
        d = GETDOUBLE(data);
        i = log_10(d);
        /*
         * for '%g|%G' ANSI: use f if exponent
         * is in the range or [-4,p] exclusively
         * else use %e|%E
         */
        if (-4 < i && i < data->precision)
          {
            /* reset precision */
            data->precision -= i + 1;
            floating(data, d);
          }
        else
          {
            /* reduce precision by 1 because of leading digit before
               decimal point in e format. */
            data->precision--;
            exponent(data, d);
          }
        state = 0;
        break;
          case 'e':
          case 'E':  /* Exponent double */
        STAR_ARGS(data);
        d = GETDOUBLE(data);
        exponent(data, d);
        state = 0;
        break;
#  ifdef HAVE_PRINTF_A_FORMAT
          case 'a':
          case 'A':
        STAR_ARGS(data);
        d = GETDOUBLE(data);
        dfallback(data, convstart, data->pf, d);
        state = 0;
        break;
#  endif /* HAVE_PRINTF_A_FORMAT */
#endif /* FLOATING_POINT */
          case 'U':
        data->flags |= PF_LONGINT;
        /* FALLTHROUGH */
          case 'u':
        STAR_ARGS(data);
#ifdef HAVE_LONG_LONG
        if (data->flags & PF_LONGLONG)
          {
            ull = GETARG (unsigned long long);
            lnumber(data, ull, 10);
          }
        else
#endif
          {
            ul = GETUNSIGNED(data);
            number(data, ul, 10);
          }
        state = 0;
        break;
          case 'D':
        data->flags |= PF_LONGINT;
        /* FALLTHROUGH */
          case 'd':  /* decimal */
          case 'i':
        STAR_ARGS(data);
#ifdef HAVE_LONG_LONG
        if (data->flags & PF_LONGLONG)
          {
            ull = GETARG (long long);
            lnumber(data, ull, 10);
          }
        else
#endif
          {
            ul = GETSIGNED(data);
            number(data, ul, 10);
          }
        state = 0;
        break;
          case 'o':  /* octal */
        STAR_ARGS(data);
#ifdef HAVE_LONG_LONG
        if (data->flags & PF_LONGLONG)
          {
            ull = GETARG (unsigned long long);
            lnumber(data, ull, 8);
          }
        else
#endif
          {
            ul = GETUNSIGNED(data);
            number(data, ul, 8);
          }
        state = 0;
        break;
          case 'x': 
          case 'X':  /* hexadecimal */
        STAR_ARGS(data);
#ifdef HAVE_LONG_LONG
        if (data->flags & PF_LONGLONG)
          {
            ull = GETARG (unsigned long long);
            lnumber(data, ull, 16);
          }
        else
#endif
          {
            ul = GETUNSIGNED(data);
            number(data, ul, 16);
          }
        state = 0;
        break;
          case 'p':
        STAR_ARGS(data);
        ul = (unsigned long)GETARG (void *);
        pointer(data, ul);
        state = 0;
        break;
#if HANDLE_MULTIBYTE
          case 'C':
        data->flags |= PF_LONGINT;
        /* FALLTHROUGH */
#endif
          case 'c': /* character */
        STAR_ARGS(data);
#if HANDLE_MULTIBYTE
        if (data->flags & PF_LONGINT)
          {
            wc = GETARG (wint_t);
            wchars (data, wc);
          }
        else
#endif
          {     
            ul = GETARG (int);
            PUT_CHAR(ul, data);
          }
        state = 0;
        break;
#if HANDLE_MULTIBYTE
          case 'S':
        data->flags |= PF_LONGINT;
        /* FALLTHROUGH */
#endif
          case 's':  /* string */
        STAR_ARGS(data);
#if HANDLE_MULTIBYTE
        if (data->flags & PF_LONGINT)
          {
            ws = GETARG (wchar_t *);
            wstrings (data, ws);
          }
        else
#endif
          {
            s = GETARG (char *);
            strings(data, s);
          }
        state = 0;
        break;
          case 'n':
#ifdef HAVE_LONG_LONG
        if (data->flags & PF_LONGLONG)
          *(GETARG (long long *)) = data->counter;
        else
#endif
        if (data->flags & PF_LONGINT)
          *(GETARG (long *)) = data->counter;
        else if (data->flags & PF_SHORTINT)
          *(GETARG (short *)) = data->counter;
        else
          *(GETARG (int *)) = data->counter;
        state = 0;
        break;
          case '%':  /* nothing just % */
        PUT_CHAR('%', data);
        state = 0;
        break;
          default:
        /* is this an error ? maybe bail out */
        state = 0;
        break;
    } /* end switch */
      } /* end of `%' for loop */
    } /* end of format string for loop */

  if (data->length >= 0)
    *data->holder = '\0'; /* the end ye ! */

  return data->counter;
}

#if defined (FLOATING_POINT) && defined (HAVE_LONG_DOUBLE)
/*
 * Printing floating point numbers accurately is an art.  I'm not good
 * at it.  Fall back to sprintf for long double formats.
 */
static void
ldfallback (data, fs, fe, ld)
     struct DATA *data;
     const char *fs, *fe;
     long double ld;
{
  register char *x;
  char fmtbuf[FALLBACK_FMTSIZE], *obuf;
  int fl;

  obuf = (char *)xmalloc(LFALLBACK_BASE + (data->precision < 6 ? 6 : data->precision) + 2);
  fl = fe - fs + 1;
  strncpy (fmtbuf, fs, fl);
  fmtbuf[fl] = '\0';
  sprintf (obuf, fmtbuf, ld);
  for (x = obuf; *x; x++)
    PUT_CHAR (*x, data);    
  xfree (obuf);
}
#endif /* FLOATING_POINT && HAVE_LONG_DOUBLE */

#ifdef FLOATING_POINT
/* Used for %a, %A if the libc printf supports them. */
static void
dfallback (data, fs, fe, d)
     struct DATA *data;
     const char *fs, *fe;
     double d;
{
  register char *x;
  char fmtbuf[FALLBACK_FMTSIZE], obuf[FALLBACK_BASE];
  int fl;

  fl = fe - fs + 1;
  strncpy (fmtbuf, fs, fl);
  fmtbuf[fl] = '\0';
  sprintf (obuf, fmtbuf, d);
  for (x = obuf; *x; x++)
    PUT_CHAR (*x, data);    
}
#endif /* FLOATING_POINT */

#ifndef HAVE_SNPRINTF

int
#if defined (__STDC__)
vsnprintf(char *string, size_t length, const char *format, va_list args)
#else
vsnprintf(string, length, format, args)
     char *string;
     size_t length;
     const char *format;
     va_list args;
#endif
{
  struct DATA data;

  if (string == 0 && length != 0)
    return 0;
  init_data (&data, string, length, format, PFM_SN);
  return (vsnprintf_internal(&data, string, length, format, args));
}

int
#if defined(PREFER_STDARG)
snprintf(char *string, size_t length, const char * format, ...)
#else
snprintf(string, length, format, va_alist)
     char *string;
     size_t length;
     const char *format;
     va_dcl
#endif
{
  struct DATA data;
  int rval;
  va_list args;

  SH_VA_START(args, format);

  if (string == 0 && length != 0)
    return 0;
  init_data (&data, string, length, format, PFM_SN);
  rval = vsnprintf_internal (&data, string, length, format, args);

  va_end(args);

  return rval;
}

#endif /* HAVE_SNPRINTF */

#ifndef HAVE_ASPRINTF

int
#if defined (__STDC__)
vasprintf(char **stringp, const char *format, va_list args)
#else
vasprintf(stringp, format, args)
     char **stringp;
     const char *format;
     va_list args;
#endif
{
  struct DATA data;
  char *string;
  int r;

  string = (char *)xmalloc(ASBUFSIZE);
  init_data (&data, string, ASBUFSIZE, format, PFM_AS);
  r = vsnprintf_internal(&data, string, ASBUFSIZE, format, args);
  *stringp = data.base;     /* not string in case reallocated */
  return r;
}

int
#if defined(PREFER_STDARG)
asprintf(char **stringp, const char * format, ...)
#else
asprintf(stringp, format, va_alist)
     char **stringp;
     const char *format;
     va_dcl
#endif
{
  int rval;
  va_list args;

  SH_VA_START(args, format);

  rval = vasprintf (stringp, format, args);

  va_end(args);

  return rval;
}

#endif

#endif

#ifdef DRIVER

static void
memory_error_and_abort ()
{
  write (2, "out of virtual memory\n", 22);
  abort ();
}

static void *
xmalloc(bytes)
     size_t bytes;
{
  void *ret;

  ret = malloc(bytes);
  if (ret == 0)
    memory_error_and_abort ();
  return ret;
}

static void *
xrealloc (pointer, bytes)
     void *pointer;
     size_t bytes;
{
  void *ret;

  ret = pointer ? realloc(pointer, bytes) : malloc(bytes);
  if (ret == 0)
    memory_error_and_abort ();
  return ret;
}

static void
xfree(x)
     void *x;
{
  if (x)
    free (x);
}

/* set of small tests for snprintf() */
main()
{
  char holder[100];
  char *h;
  int i, si, ai;

#ifdef HAVE_LOCALE_H
  setlocale(LC_ALL, "");
#endif

#if 1
  si = snprintf((char *)NULL, 0, "abcde\n");
  printf("snprintf returns %d with NULL first argument and size of 0\n", si);
  si = snprintf(holder, 0, "abcde\n");
  printf("snprintf returns %d with non-NULL first argument and size of 0\n", si);
  si = snprintf((char *)NULL, 16, "abcde\n");
  printf("snprintf returns %d with NULL first argument and non-zero size\n", si);
  
/*
  printf("Suite of test for snprintf:\n");
  printf("a_format\n");
  printf("printf() format\n");
  printf("snprintf() format\n\n");
*/
/* Checking the field widths */

  printf("/%%ld %%ld/, 336, 336\n");
  snprintf(holder, sizeof holder, "/%ld %ld/\n", 336, 336);
  asprintf(&h, "/%ld %ld/\n", 336, 336);
  printf("/%ld %ld/\n", 336, 336);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%d/, 336\n");
  snprintf(holder, sizeof holder, "/%d/\n", 336);
  asprintf(&h, "/%d/\n", 336);
  printf("/%d/\n", 336);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%2d/, 336\n");
  snprintf(holder, sizeof holder, "/%2d/\n", 336);
  asprintf(&h, "/%2d/\n", 336);
  printf("/%2d/\n", 336);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%10d/, 336\n");
  snprintf(holder, sizeof holder, "/%10d/\n", 336);
  asprintf(&h, "/%10d/\n", 336);
  printf("/%10d/\n", 336);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%-10d/, 336\n");
  snprintf(holder, sizeof holder, "/%-10d/\n", 336);
  asprintf(&h, "/%-10d/\n", 336);
  printf("/%-10d/\n", 336);
  printf("%s", holder);
  printf("%s\n", h);


/* floating points */

  printf("/%%f/, 1234.56\n");
  snprintf(holder, sizeof holder, "/%f/\n", 1234.56);
  asprintf(&h, "/%f/\n", 1234.56);
  printf("/%f/\n", 1234.56);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%e/, 1234.56\n");
  snprintf(holder, sizeof holder, "/%e/\n", 1234.56);
  asprintf(&h, "/%e/\n", 1234.56);
  printf("/%e/\n", 1234.56);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%4.2f/, 1234.56\n");
  snprintf(holder, sizeof holder, "/%4.2f/\n", 1234.56);
  asprintf(&h, "/%4.2f/\n", 1234.56);
  printf("/%4.2f/\n", 1234.56);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%3.1f/, 1234.56\n");
  snprintf(holder, sizeof holder, "/%3.1f/\n", 1234.56);
  asprintf(&h, "/%3.1f/\n", 1234.56);
  printf("/%3.1f/\n", 1234.56);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%10.3f/, 1234.56\n");
  snprintf(holder, sizeof holder, "/%10.3f/\n", 1234.56);
  asprintf(&h, "/%10.3f/\n", 1234.56);
  printf("/%10.3f/\n", 1234.56);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%10.3e/, 1234.56\n");
  snprintf(holder, sizeof holder, "/%10.3e/\n", 1234.56);
  asprintf(&h, "/%10.3e/\n", 1234.56);
  printf("/%10.3e/\n", 1234.56);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%+4.2f/, 1234.56\n");
  snprintf(holder, sizeof holder, "/%+4.2f/\n", 1234.56);
  asprintf(&h, "/%+4.2f/\n", 1234.56);
  printf("/%+4.2f/\n", 1234.56);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%010.2f/, 1234.56\n");
  snprintf(holder, sizeof holder, "/%010.2f/\n", 1234.56);
  asprintf(&h, "/%010.2f/\n", 1234.56);
  printf("/%010.2f/\n", 1234.56);
  printf("%s", holder);
  printf("%s\n", h);

#define BLURB "Outstanding acting !"
/* strings precisions */

  printf("/%%2s/, \"%s\"\n", BLURB);
  snprintf(holder, sizeof holder, "/%2s/\n", BLURB);
  asprintf(&h, "/%2s/\n", BLURB);
  printf("/%2s/\n", BLURB);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%22s/ %s\n", BLURB);
  snprintf(holder, sizeof holder, "/%22s/\n", BLURB);
  asprintf(&h, "/%22s/\n", BLURB);
  printf("/%22s/\n", BLURB);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%22.5s/ %s\n", BLURB);
  snprintf(holder, sizeof holder, "/%22.5s/\n", BLURB);
  asprintf(&h, "/%22.5s/\n", BLURB);
  printf("/%22.5s/\n", BLURB);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%-22.5s/ %s\n", BLURB);
  snprintf(holder, sizeof holder, "/%-22.5s/\n", BLURB);
  asprintf(&h, "/%-22.5s/\n", BLURB);
  printf("/%-22.5s/\n", BLURB);
  printf("%s", holder);
  printf("%s\n", h);

/* see some flags */

  printf("%%x %%X %%#x, 31, 31, 31\n");
  snprintf(holder, sizeof holder, "%x %X %#x\n", 31, 31, 31);
  asprintf(&h, "%x %X %#x\n", 31, 31, 31);
  printf("%x %X %#x\n", 31, 31, 31);
  printf("%s", holder);
  printf("%s\n", h);

  printf("**%%d**%% d**%% d**, 42, 42, -42\n");
  snprintf(holder, sizeof holder, "**%d**% d**% d**\n", 42, 42, -42);
  asprintf(&h, "**%d**% d**% d**\n", 42, 42, -42);
  printf("**%d**% d**% d**\n", 42, 42, -42);
  printf("%s", holder);
  printf("%s\n", h);

/* other flags */

  printf("/%%g/, 31.4\n");
  snprintf(holder, sizeof holder, "/%g/\n", 31.4);
  asprintf(&h, "/%g/\n", 31.4);
  printf("/%g/\n", 31.4);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%.6g/, 31.4\n");
  snprintf(holder, sizeof holder, "/%.6g/\n", 31.4);
  asprintf(&h, "/%.6g/\n", 31.4);
  printf("/%.6g/\n", 31.4);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%.1G/, 31.4\n");
  snprintf(holder, sizeof holder, "/%.1G/\n", 31.4);
  asprintf(&h, "/%.1G/\n", 31.4);
  printf("/%.1G/\n", 31.4);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%.1G/, 3100000000.4\n");
  snprintf(holder, sizeof holder, "/%.1G/\n", 3100000000.4);  
  asprintf(&h, "/%.1G/\n", 3100000000.4);  
  printf("/%.1G/\n", 3100000000.4); 
  printf("%s", holder);
  printf("%s\n", h);

  printf("abc%%n\n");
  printf("abc%n", &i); printf("%d\n", i);
  snprintf(holder, sizeof holder, "abc%n", &i);
  printf("%s", holder); printf("%d\n\n", i);
  asprintf(&h, "abc%n", &i);
  printf("%s", h); printf("%d\n\n", i);
  
  printf("%%*.*s --> 10.10\n");
  snprintf(holder, sizeof holder, "%*.*s\n", 10, 10, BLURB);
  asprintf(&h, "%*.*s\n", 10, 10, BLURB);
  printf("%*.*s\n", 10, 10, BLURB);
  printf("%s", holder);
  printf("%s\n", h);

  printf("%%%%%%%%\n");
  snprintf(holder, sizeof holder, "%%%%\n");
  asprintf(&h, "%%%%\n");
  printf("%%%%\n");
  printf("%s", holder);
  printf("%s\n", h);

#define BIG "Hello this is a too big string for the buffer"
/*  printf("A buffer to small of 10, trying to put this:\n");*/
  printf("<%%>, %s\n", BIG); 
  i = snprintf(holder, 10, "%s\n", BIG);
  i = asprintf(&h, "%s", BIG);
  printf("<%s>\n", BIG);
  printf("<%s>\n", holder);
  printf("<%s>\n\n", h);

  printf ("<%%p> vsnprintf\n");
  i = snprintf(holder, 100, "%p", vsnprintf);
  i = asprintf(&h, "%p", vsnprintf);
  printf("<%p>\n", vsnprintf);
  printf("<%s>\n", holder);  
  printf("<%s>\n\n", h);

  printf ("<%%lu> LONG_MAX+1\n");
  i = snprintf(holder, 100, "%lu", (unsigned long)(LONG_MAX)+1);
  i = asprintf(&h, "%lu", (unsigned long)(LONG_MAX)+1);
  printf("<%lu>\n", (unsigned long)(LONG_MAX)+1);
  printf("<%s>\n", holder);
  printf("<%s>\n\n", h);

#ifdef HAVE_LONG_LONG
  printf ("<%%llu> LLONG_MAX+1\n");
  i = snprintf(holder, 100, "%llu", (unsigned long long)(LLONG_MAX)+1);
  i = asprintf(&h, "%llu", (unsigned long long)(LLONG_MAX)+1);
  printf("<%llu>\n", (unsigned long long)(LLONG_MAX)+1);
  printf("<%s>\n", holder);
  printf("<%s>\n\n", h);
#endif

#ifdef HAVE_LONG_DOUBLE
  printf ("<%%6.2LE> 42.42\n");
  i = snprintf(holder, 100, "%6.2LE", (long double)42.42);
  i = asprintf(&h, "%6.2LE", (long double)42.42);
  printf ("<%6.2LE>\n", (long double)42.42);
  printf ("<%s>\n", holder);
  printf ("<%s>\n\n", h);
#endif

#ifdef HAVE_PRINTF_A_FORMAT
  printf ("<%%6.2A> 42.42\n");
  i = snprintf(holder, 100, "%6.2A", 42.42);
  i = asprintf(&h, "%6.2A", 42.42);
  printf ("<%6.2A>\n", 42.42);
  printf ("<%s>\n", holder);
  printf ("<%s>\n\n", h);

  printf ("<%%6.2LA> 42.42\n");
  i = snprintf(holder, 100, "%6.2LA", (long double)42.42);
  i = asprintf(&h, "%6.2LA", (long double)42.42);
  printf ("<%6.2LA>\n", (long double)42.42);
  printf ("<%s>\n", holder);
  printf ("<%s>\n\n", h);
#endif

  printf ("<%%.10240f> DBL_MAX\n");
  si = snprintf(holder, 100, "%.10240f", DBL_MAX);
  ai = asprintf(&h, "%.10240f", DBL_MAX);
  printf ("<%.10240f>\n", DBL_MAX);
  printf ("<%d> <%s>\n", si, holder);
  printf ("<%d> <%s>\n\n", ai, h);

  printf ("<%%.10240Lf> LDBL_MAX\n");
  si = snprintf(holder, 100, "%.10240Lf", (long double)LDBL_MAX);
  ai = asprintf(&h, "%.10240Lf", (long double)LDBL_MAX);
  printf ("<%.10240Lf>\n", (long double)LDBL_MAX);
  printf ("<%d> <%s>\n", si, holder);
  printf ("<%d> <%s>\n\n", ai, h);

  /* huh? */
  printf("/%%g/, 421.2345\n");
  snprintf(holder, sizeof holder, "/%g/\n", 421.2345);
  asprintf(&h, "/%g/\n", 421.2345);
  printf("/%g/\n", 421.2345);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%g/, 4214.2345\n");
  snprintf(holder, sizeof holder, "/%g/\n", 4214.2345);
  asprintf(&h, "/%g/\n", 4214.2345);
  printf("/%g/\n", 4214.2345);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%.5g/, 4214.2345\n");
  snprintf(holder, sizeof holder, "/%.5g/\n", 4214.2345);
  asprintf(&h, "/%.5g/\n", 4214.2345);
  printf("/%.5g/\n", 4214.2345);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%.4g/, 4214.2345\n");
  snprintf(holder, sizeof holder, "/%.4g/\n", 4214.2345);
  asprintf(&h, "/%.4g/\n", 4214.2345);
  printf("/%.4g/\n", 4214.2345);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%'ld %%'ld/, 12345, 1234567\n");
  snprintf(holder, sizeof holder, "/%'ld %'ld/\n", 12345, 1234567);
  asprintf(&h, "/%'ld %'ld/\n", 12345, 1234567);
  printf("/%'ld %'ld/\n", 12345, 1234567);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%'ld %%'ld/, 336, 3336\n");
  snprintf(holder, sizeof holder, "/%'ld %'ld/\n", 336, 3336);
  asprintf(&h, "/%'ld %'ld/\n", 336, 3336);
  printf("/%'ld %'ld/\n", 336, 3336);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%'ld %%'ld/, -42786, -142786\n");
  snprintf(holder, sizeof holder, "/%'ld %'ld/\n", -42786, -142786);
  asprintf(&h, "/%'ld %'ld/\n", -42786, -142786);
  printf("/%'ld %'ld/\n", -42786, -142786);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%'f %%'f/, 421.2345, 421234.56789\n");
  snprintf(holder, sizeof holder, "/%'f %'f/\n", 421.2345, 421234.56789);
  asprintf(&h, "/%'f %'f/\n", 421.2345, 421234.56789);
  printf("/%'f %'f/\n", 421.2345, 421234.56789);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%'f %%'f/, -421.2345, -421234.56789\n");
  snprintf(holder, sizeof holder, "/%'f %'f/\n", -421.2345, -421234.56789);
  asprintf(&h, "/%'f %'f/\n", -421.2345, -421234.56789);
  printf("/%'f %'f/\n", -421.2345, -421234.56789);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%'g %%'g/, 421.2345, 421234.56789\n");
  snprintf(holder, sizeof holder, "/%'g %'g/\n", 421.2345, 421234.56789);
  asprintf(&h, "/%'g %'g/\n", 421.2345, 421234.56789);
  printf("/%'g %'g/\n", 421.2345, 421234.56789);
  printf("%s", holder);
  printf("%s\n", h);

  printf("/%%'g %%'g/, -421.2345, -421234.56789\n");
  snprintf(holder, sizeof holder, "/%'g %'g/\n", -421.2345, -421234.56789);
  asprintf(&h, "/%'g %'g/\n", -421.2345, -421234.56789);
  printf("/%'g %'g/\n", -421.2345, -421234.56789);
  printf("%s", holder);
  printf("%s\n", h);
#endif

  printf("/%%'g/, 4213455.8392\n");
  snprintf(holder, sizeof holder, "/%'g/\n", 4213455.8392);
  asprintf(&h, "/%'g/\n", 4213455.8392);
  printf("/%'g/\n", 4213455.8392);
  printf("%s", holder);
  printf("%s\n", h);

  exit (0);
}
#endif

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/patches/tinycc.patch

File: ../git/live-bootstrap/sysa/bash-2.05b/patches/tinycc.patch used in: Step 1050
SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>

SPDX-License-Identifier: GPL-2.0-or-later

int name, namelen; is wrong for mes libc, it is char* name, so we modify tinycc
to reflect this.

diff --git lib/sh/oslib.c lib/sh/oslib.c
index 90d7be9..37fdf2a 100644
--- lib/sh/oslib.c
+++ lib/sh/oslib.c
@@ -192,8 +192,7 @@ bzero (s, n)
 #    include <sys/utsname.h>
 int
 gethostname (name, namelen)
-     char *name;
-     int namelen;
+     char *name; int namelen;
 {
   int i;
   struct utsname ut;
@@ -209,7 +208,7 @@ gethostname (name, namelen)
 #  else /* !HAVE_UNAME */
 int
 gethostname (name, namelen)
-     int name, namelen;
+     char* name; int namelen;
 {
   strncpy (name, "unknown", namelen);
   name[namelen] = '\0';

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/oslib.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/oslib.c used in: Step 1050, Step 1154
/* oslib.c - functions present only in some unix versions. */

/* Copyright (C) 1995 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA */

#include <config.h>

#include <bashtypes.h>
#ifndef _MINIX
#  include <sys/param.h>
#endif

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#if defined (HAVE_LIMITS_H)
#  include <limits.h>
#endif

#include <posixstat.h>
#include <filecntl.h>
#include <bashansi.h>

#include <stdio.h>
#include <errno.h>
#include <chartypes.h>

#include <shell.h>

#if !defined (errno)
extern int errno;
#endif /* !errno */

/* Make the functions strchr and strrchr if they do not exist. */
#if !defined (HAVE_STRCHR)
char *
strchr (string, c)
     char *string;
     int c;
{
  register char *s;

  for (s = string; s && *s; s++)
    if (*s == c)
      return (s);

  return ((char *) NULL);
}

char *
strrchr (string, c)
     char *string;
     int c;
{
  register char *s, *t;

  for (s = string, t = (char *)NULL; s && *s; s++)
    if (*s == c)
      t = s;
  return (t);
}
#endif /* !HAVE_STRCHR */

#if !defined (HAVE_DUP2) || defined (DUP2_BROKEN)
/* Replacement for dup2 (), for those systems which either don't have it,
   or supply one with broken behaviour. */
int
dup2 (fd1, fd2)
     int fd1, fd2;
{
  int saved_errno, r;

  /* If FD1 is not a valid file descriptor, then return immediately with
     an error. */
  if (fcntl (fd1, F_GETFL, 0) == -1)
    return (-1);

  if (fd2 < 0 || fd2 >= getdtablesize ())
    {
      errno = EBADF;
      return (-1);
    }

  if (fd1 == fd2)
    return (0);

  saved_errno = errno;

  (void) close (fd2);
  r = fcntl (fd1, F_DUPFD, fd2);

  if (r >= 0)
    errno = saved_errno;
  else
    if (errno == EINVAL)
      errno = EBADF;

  /* Force the new file descriptor to remain open across exec () calls. */
  SET_OPEN_ON_EXEC (fd2);
  return (r);
}
#endif /* !HAVE_DUP2 */

/*
 * Return the total number of available file descriptors.
 *
 * On some systems, like 4.2BSD and its descendents, there is a system call
 * that returns the size of the descriptor table: getdtablesize().  There are
 * lots of ways to emulate this on non-BSD systems.
 *
 * On System V.3, this can be obtained via a call to ulimit:
 *  return (ulimit(4, 0L));
 *
 * On other System V systems, NOFILE is defined in /usr/include/sys/param.h
 * (this is what we assume below), so we can simply use it:
 *  return (NOFILE);
 *
 * On POSIX systems, there are specific functions for retrieving various
 * configuration parameters:
 *  return (sysconf(_SC_OPEN_MAX));
 *
 */

#if !defined (HAVE_GETDTABLESIZE)
int
getdtablesize ()
{
#  if defined (_POSIX_VERSION) && defined (HAVE_SYSCONF) && defined (_SC_OPEN_MAX)
  return (sysconf(_SC_OPEN_MAX));   /* Posix systems use sysconf */
#  else /* ! (_POSIX_VERSION && HAVE_SYSCONF && _SC_OPEN_MAX) */
#    if defined (ULIMIT_MAXFDS)
  return (ulimit (4, 0L));  /* System V.3 systems use ulimit(4, 0L) */
#    else /* !ULIMIT_MAXFDS */
#      if defined (NOFILE)  /* Other systems use NOFILE */
  return (NOFILE);
#      else /* !NOFILE */
  return (20);          /* XXX - traditional value is 20 */
#      endif /* !NOFILE */
#    endif /* !ULIMIT_MAXFDS */
#  endif /* ! (_POSIX_VERSION && _SC_OPEN_MAX) */
}
#endif /* !HAVE_GETDTABLESIZE */

#if !defined (HAVE_BCOPY)
#  if defined (bcopy)
#    undef bcopy
#  endif
void
bcopy (s,d,n)
     char *d, *s;
     int n;
{
  FASTCOPY (s, d, n);
}
#endif /* !HAVE_BCOPY */

#if !defined (HAVE_BZERO)
#  if defined (bzero)
#    undef bzero
#  endif
void
bzero (s, n)
     char *s;
     int n;
{
  register int i;
  register char *r;

  for (i = 0, r = s; i < n; i++)
    *r++ = '\0';
}
#endif

#if !defined (HAVE_GETHOSTNAME)
#  if defined (HAVE_UNAME)
#    include <sys/utsname.h>
int
gethostname (name, namelen)
     char *name;
     int namelen;
{
  int i;
  struct utsname ut;

  --namelen;

  uname (&ut);
  i = strlen (ut.nodename) + 1;
  strncpy (name, ut.nodename, i < namelen ? i : namelen);
  name[namelen] = '\0';
  return (0);
}
#  else /* !HAVE_UNAME */
int
gethostname (name, namelen)
     int name, namelen;
{
  strncpy (name, "unknown", namelen);
  name[namelen] = '\0';
  return 0;
}
#  endif /* !HAVE_UNAME */
#endif /* !HAVE_GETHOSTNAME */

#if !defined (HAVE_KILLPG)
int
killpg (pgrp, sig)
     pid_t pgrp;
     int sig;
{
  return (kill (-pgrp, sig));
}
#endif /* !HAVE_KILLPG */

#if !defined (HAVE_MKFIFO) && defined (PROCESS_SUBSTITUTION)
int
mkfifo (path, mode)
     char *path;
     int mode;
{
#if defined (S_IFIFO)
  return (mknod (path, (mode | S_IFIFO), 0));
#else /* !S_IFIFO */
  return (-1);
#endif /* !S_IFIFO */
}
#endif /* !HAVE_MKFIFO && PROCESS_SUBSTITUTION */

#define DEFAULT_MAXGROUPS 64

int
getmaxgroups ()
{
  static int maxgroups = -1;

  if (maxgroups > 0)
    return maxgroups;

#if defined (HAVE_SYSCONF) && defined (_SC_NGROUPS_MAX)
  maxgroups = sysconf (_SC_NGROUPS_MAX);
#else
#  if defined (NGROUPS_MAX)
  maxgroups = NGROUPS_MAX;
#  else /* !NGROUPS_MAX */
#    if defined (NGROUPS)
  maxgroups = NGROUPS;
#    else /* !NGROUPS */
  maxgroups = DEFAULT_MAXGROUPS;
#    endif /* !NGROUPS */
#  endif /* !NGROUPS_MAX */  
#endif /* !HAVE_SYSCONF || !SC_NGROUPS_MAX */

  if (maxgroups <= 0)
    maxgroups = DEFAULT_MAXGROUPS;

  return maxgroups;
}

long
getmaxchild ()
{
  static long maxchild = -1L;

  if (maxchild > 0)
    return maxchild;

#if defined (HAVE_SYSCONF) && defined (_SC_CHILD_MAX)
  maxchild = sysconf (_SC_CHILD_MAX);
#else
#  if defined (CHILD_MAX)
  maxchild = CHILD_MAX;
#  else
#    if defined (MAXUPRC)
  maxchild = MAXUPRC;
#    endif /* MAXUPRC */
#  endif /* CHILD_MAX */
#endif /* !HAVE_SYSCONF || !_SC_CHILD_MAX */

  return (maxchild);
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/patches/missing-defines.patch

File: ../git/live-bootstrap/sysa/bash-2.05b/patches/missing-defines.patch used in: Step 1051
SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>

SPDX-License-Identifier: GPL-2.0-or-later

We don't actually want any of these things, which should really be hidden
behind the ifdefs given here to disable them when they are not being
used (as we do).

--- execute_cmd.c   2021-01-15 09:38:55.730307635 +1100
+++ execute_cmd.c   2021-01-15 09:43:41.046896754 +1100
@@ -286,12 +286,18 @@
     {
       if (currently_executing_command->type == cm_simple)
    return currently_executing_command->value.Simple->line;
+#ifdef COND_COMMAND
       else if (currently_executing_command->type == cm_cond)
    return currently_executing_command->value.Cond->line;
+#endif
+#ifdef DPAREN_ARITHMETIC
       else if (currently_executing_command->type == cm_arith)
    return currently_executing_command->value.Arith->line;
+#endif
+#ifdef ARITH_FOR_COMMAND
       else if (currently_executing_command->type == cm_arith_for)
    return currently_executing_command->value.ArithFor->line;
+#endif
       else
    return line_number;
     }

https://git.savannah.gnu.org/cgit/bash.git/tree/execute_cmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/execute_cmd.c used in: Step 1051, Step 1210
/* execute_command.c -- Execute a COMMAND structure. */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
#include "config.h"

#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
  #pragma alloca
#endif /* _AIX && RISC6000 && !__GNUC__ */

#include <stdio.h>
#include "chartypes.h"
#include "bashtypes.h"
#ifndef _MINIX
#  include <sys/file.h>
#endif
#include "filecntl.h"
#include "posixstat.h"
#include <signal.h>
#ifndef _MINIX
#  include <sys/param.h>
#endif

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "posixtime.h"

#if defined (HAVE_SYS_RESOURCE_H) && !defined (RLIMTYPE)
#  include <sys/resource.h>
#endif

#if defined (HAVE_SYS_TIMES_H) && defined (HAVE_TIMES)
#  include <sys/times.h>
#endif

#include <errno.h>

#if !defined (errno)
extern int errno;
#endif

#include "bashansi.h"

#include "memalloc.h"
#include "shell.h"
#include <y.tab.h>  /* use <...> so we pick it up from the build directory */
#include "flags.h"
#include "builtins.h"
#include "hashlib.h"
#include "jobs.h"
#include "execute_cmd.h"
#include "findcmd.h"
#include "redir.h"
#include "trap.h"
#include "pathexp.h"
#include "hashcmd.h"

#if defined (COND_COMMAND)
#  include "test.h"
#endif

#include "builtins/common.h"
#include "builtins/builtext.h"  /* list of builtins */

#include <glob/strmatch.h>
#include <tilde/tilde.h>

#if defined (BUFFERED_INPUT)
#  include "input.h"
#endif

#if defined (ALIAS)
#  include "alias.h"
#endif

#if defined (HISTORY)
#  include "bashhist.h"
#endif

extern int posixly_correct;
extern int breaking, continuing, loop_level;
extern int expand_aliases;
extern int parse_and_execute_level, running_trap, trap_line_number;
extern int command_string_index, line_number;
extern int dot_found_in_search;
extern int already_making_children;
extern char *the_printed_command, *shell_name;
extern pid_t last_command_subst_pid;
extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
extern char **subshell_argv, **subshell_envp;
extern int subshell_argc;
#if 0
extern char *glob_argv_flags;
#endif

extern int close __P((int));

/* Static functions defined and used in this file. */
static void close_pipes __P((int, int));
static void do_piping __P((int, int));
static void bind_lastarg __P((char *));
static int shell_control_structure __P((enum command_type));
static void cleanup_redirects __P((REDIRECT *));

#if defined (JOB_CONTROL)
static int restore_signal_mask __P((sigset_t *));
#endif

static void async_redirect_stdin __P((void));

static int builtin_status __P((int));

static int execute_for_command __P((FOR_COM *));
#if defined (SELECT_COMMAND)
static int print_index_and_element __P((int, int, WORD_LIST *));
static void indent __P((int, int));
static void print_select_list __P((WORD_LIST *, int, int, int));
static char *select_query __P((WORD_LIST *, int, char *, int));
static int execute_select_command __P((SELECT_COM *));
#endif
#if defined (DPAREN_ARITHMETIC)
static int execute_arith_command __P((ARITH_COM *));
#endif
#if defined (COND_COMMAND)
static int execute_cond_node __P((COND_COM *));
static int execute_cond_command __P((COND_COM *));
#endif
#if defined (COMMAND_TIMING)
static int mkfmt __P((char *, int, int, time_t, int));
static void print_formatted_time __P((FILE *, char *,
                      time_t, int, time_t, int,
                      time_t, int, int));
static int time_command __P((COMMAND *, int, int, int, struct fd_bitmap *));
#endif
#if defined (ARITH_FOR_COMMAND)
static intmax_t eval_arith_for_expr __P((WORD_LIST *, int *));
static int execute_arith_for_command __P((ARITH_FOR_COM *));
#endif
static int execute_case_command __P((CASE_COM *));
static int execute_while_command __P((WHILE_COM *));
static int execute_until_command __P((WHILE_COM *));
static int execute_while_or_until __P((WHILE_COM *, int));
static int execute_if_command __P((IF_COM *));
static int execute_null_command __P((REDIRECT *, int, int, int, pid_t));
static void fix_assignment_words __P((WORD_LIST *));
static int execute_simple_command __P((SIMPLE_COM *, int, int, int, struct fd_bitmap *));
static int execute_builtin __P((sh_builtin_func_t *, WORD_LIST *, int, int));
static int execute_function __P((SHELL_VAR *, WORD_LIST *, int, struct fd_bitmap *, int, int));
static int execute_builtin_or_function __P((WORD_LIST *, sh_builtin_func_t *,
                        SHELL_VAR *,
                        REDIRECT *, struct fd_bitmap *, int));
static void execute_subshell_builtin_or_function __P((WORD_LIST *, REDIRECT *,
                              sh_builtin_func_t *,
                              SHELL_VAR *,
                              int, int, int,
                              struct fd_bitmap *,
                              int));
static void execute_disk_command __P((WORD_LIST *, REDIRECT *, char *,
                      int, int, int, struct fd_bitmap *, int));

static char *getinterp __P((char *, int, int *));
static void initialize_subshell __P((void));
static int execute_in_subshell __P((COMMAND *, int, int, int, struct fd_bitmap *));

static int execute_pipeline __P((COMMAND *, int, int, int, struct fd_bitmap *));

static int execute_connection __P((COMMAND *, int, int, int, struct fd_bitmap *));

static int execute_intern_function __P((WORD_DESC *, COMMAND *));

/* The line number that the currently executing function starts on. */
static int function_line_number;

/* Set to 1 if fd 0 was the subject of redirection to a subshell.  Global
   so that reader_loop can set it to zero before executing a command. */
int stdin_redir;

/* The name of the command that is currently being executed.
   `test' needs this, for example. */
char *this_command_name;

static COMMAND *currently_executing_command;

struct stat SB;     /* used for debugging */

static int special_builtin_failed;

/* For catching RETURN in a function. */
int return_catch_flag;
int return_catch_value;
procenv_t return_catch;

/* The value returned by the last synchronous command. */
int last_command_exit_value;

/* The list of redirections to perform which will undo the redirections
   that I made in the shell. */
REDIRECT *redirection_undo_list = (REDIRECT *)NULL;

/* The list of redirections to perform which will undo the internal
   redirections performed by the `exec' builtin.  These are redirections
   that must be undone even when exec discards redirection_undo_list. */
REDIRECT *exec_redirection_undo_list = (REDIRECT *)NULL;

/* Non-zero if we have just forked and are currently running in a subshell
   environment. */
int subshell_environment;

/* Currently-executing shell function. */
SHELL_VAR *this_shell_function;

struct fd_bitmap *current_fds_to_close = (struct fd_bitmap *)NULL;

#define FD_BITMAP_DEFAULT_SIZE 32

/* Functions to allocate and deallocate the structures used to pass
   information from the shell to its children about file descriptors
   to close. */
struct fd_bitmap *
new_fd_bitmap (size)
     int size;
{
  struct fd_bitmap *ret;

  ret = (struct fd_bitmap *)xmalloc (sizeof (struct fd_bitmap));

  ret->size = size;

  if (size)
    {
      ret->bitmap = (char *)xmalloc (size);
      memset (ret->bitmap, '\0', size);
    }
  else
    ret->bitmap = (char *)NULL;
  return (ret);
}

void
dispose_fd_bitmap (fdbp)
     struct fd_bitmap *fdbp;
{
  FREE (fdbp->bitmap);
  free (fdbp);
}

void
close_fd_bitmap (fdbp)
     struct fd_bitmap *fdbp;
{
  register int i;

  if (fdbp)
    {
      for (i = 0; i < fdbp->size; i++)
    if (fdbp->bitmap[i])
      {
        close (i);
        fdbp->bitmap[i] = 0;
      }
    }
}

/* Return the line number of the currently executing command. */
int
executing_line_number ()
{
  if (executing && (variable_context == 0 || interactive_shell == 0) && currently_executing_command)
    {
      if (currently_executing_command->type == cm_simple)
    return currently_executing_command->value.Simple->line;
      else if (currently_executing_command->type == cm_cond)
    return currently_executing_command->value.Cond->line;
      else if (currently_executing_command->type == cm_arith)
    return currently_executing_command->value.Arith->line;
      else if (currently_executing_command->type == cm_arith_for)
    return currently_executing_command->value.ArithFor->line;
      else
    return line_number;
    }
  else if (running_trap)
    return trap_line_number;
  else
    return line_number;
}

/* Execute the command passed in COMMAND.  COMMAND is exactly what
   read_command () places into GLOBAL_COMMAND.  See "command.h" for the
   details of the command structure.

   EXECUTION_SUCCESS or EXECUTION_FAILURE are the only possible
   return values.  Executing a command with nothing in it returns
   EXECUTION_SUCCESS. */
int
execute_command (command)
     COMMAND *command;
{
  struct fd_bitmap *bitmap;
  int result;

  current_fds_to_close = (struct fd_bitmap *)NULL;
  bitmap = new_fd_bitmap (FD_BITMAP_DEFAULT_SIZE);
  begin_unwind_frame ("execute-command");
  add_unwind_protect (dispose_fd_bitmap, (char *)bitmap);

  /* Just do the command, but not asynchronously. */
  result = execute_command_internal (command, 0, NO_PIPE, NO_PIPE, bitmap);

  dispose_fd_bitmap (bitmap);
  discard_unwind_frame ("execute-command");

#if defined (PROCESS_SUBSTITUTION)
  /* don't unlink fifos if we're in a shell function; wait until the function
     returns. */
  if (variable_context == 0)
    unlink_fifo_list ();
#endif /* PROCESS_SUBSTITUTION */

  return (result);
}

/* Return 1 if TYPE is a shell control structure type. */
static int
shell_control_structure (type)
     enum command_type type;
{
  switch (type)
    {
    case cm_for:
#if defined (ARITH_FOR_COMMAND)
    case cm_arith_for:
#endif
#if defined (SELECT_COMMAND)
    case cm_select:
#endif
#if defined (DPAREN_ARITHMETIC)
    case cm_arith:
#endif
#if defined (COND_COMMAND)
    case cm_cond:
#endif
    case cm_case:
    case cm_while:
    case cm_until:
    case cm_if:
    case cm_group:
      return (1);

    default:
      return (0);
    }
}

/* A function to use to unwind_protect the redirection undo list
   for loops. */
static void
cleanup_redirects (list)
     REDIRECT *list;
{
  do_redirections (list, 1, 0, 0);
  dispose_redirects (list);
}

#if 0
/* Function to unwind_protect the redirections for functions and builtins. */
static void
cleanup_func_redirects (list)
     REDIRECT *list;
{
  do_redirections (list, 1, 0, 0);
}
#endif

void
dispose_exec_redirects ()
{
  if (exec_redirection_undo_list)
    {
      dispose_redirects (exec_redirection_undo_list);
      exec_redirection_undo_list = (REDIRECT *)NULL;
    }
}

#if defined (JOB_CONTROL)
/* A function to restore the signal mask to its proper value when the shell
   is interrupted or errors occur while creating a pipeline. */
static int
restore_signal_mask (set)
     sigset_t *set;
{
  return (sigprocmask (SIG_SETMASK, set, (sigset_t *)NULL));
}
#endif /* JOB_CONTROL */

#ifdef DEBUG
/* A debugging function that can be called from gdb, for instance. */
void
open_files ()
{
  register int i;
  int f, fd_table_size;

  fd_table_size = getdtablesize ();

  fprintf (stderr, "pid %ld open files:", (long)getpid ());
  for (i = 3; i < fd_table_size; i++)
    {
      if ((f = fcntl (i, F_GETFD, 0)) != -1)
    fprintf (stderr, " %d (%s)", i, f ? "close" : "open");
    }
  fprintf (stderr, "\n");
}
#endif

static void
async_redirect_stdin ()
{
  int fd;

  fd = open ("/dev/null", O_RDONLY);
  if (fd > 0)
    {
      dup2 (fd, 0);
      close (fd);
    }
  else if (fd < 0)
    internal_error ("cannot redirect standard input from /dev/null: %s", strerror (errno));
}

#define DESCRIBE_PID(pid) do { if (interactive) describe_pid (pid); } while (0)

/* Execute the command passed in COMMAND, perhaps doing it asynchrounously.
   COMMAND is exactly what read_command () places into GLOBAL_COMMAND.
   ASYNCHROUNOUS, if non-zero, says to do this command in the background.
   PIPE_IN and PIPE_OUT are file descriptors saying where input comes
   from and where it goes.  They can have the value of NO_PIPE, which means
   I/O is stdin/stdout.
   FDS_TO_CLOSE is a list of file descriptors to close once the child has
   been forked.  This list often contains the unusable sides of pipes, etc.

   EXECUTION_SUCCESS or EXECUTION_FAILURE are the only possible
   return values.  Executing a command with nothing in it returns
   EXECUTION_SUCCESS. */
int
execute_command_internal (command, asynchronous, pipe_in, pipe_out,
              fds_to_close)
     COMMAND *command;
     int asynchronous;
     int pipe_in, pipe_out;
     struct fd_bitmap *fds_to_close;
{
  int exec_result, invert, ignore_return, was_error_trap;
  REDIRECT *my_undo_list, *exec_undo_list;
  volatile pid_t last_pid;

  if (command == 0 || breaking || continuing || read_but_dont_execute)
    return (EXECUTION_SUCCESS);

  run_pending_traps ();

  if (running_trap == 0)
    currently_executing_command = command;

  invert = (command->flags & CMD_INVERT_RETURN) != 0;

  /* If we're inverting the return value and `set -e' has been executed,
     we don't want a failing command to inadvertently cause the shell
     to exit. */
  if (exit_immediately_on_error && invert)  /* XXX */
    command->flags |= CMD_IGNORE_RETURN;    /* XXX */

  exec_result = EXECUTION_SUCCESS;

  /* If a command was being explicitly run in a subshell, or if it is
     a shell control-structure, and it has a pipe, then we do the command
     in a subshell. */
  if (command->type == cm_subshell && (command->flags & CMD_NO_FORK))
    return (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));

  if (command->type == cm_subshell ||
      (command->flags & (CMD_WANT_SUBSHELL|CMD_FORCE_SUBSHELL)) ||
      (shell_control_structure (command->type) &&
       (pipe_out != NO_PIPE || pipe_in != NO_PIPE || asynchronous)))
    {
      pid_t paren_pid;

      /* Fork a subshell, turn off the subshell bit, turn off job
     control and call execute_command () on the command again. */
      paren_pid = make_child (savestring (make_command_string (command)),
                  asynchronous);
      if (paren_pid == 0)
    exit (execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close));
    /* NOTREACHED */
      else
    {
      close_pipes (pipe_in, pipe_out);

#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
      unlink_fifo_list ();
#endif
      /* If we are part of a pipeline, and not the end of the pipeline,
         then we should simply return and let the last command in the
         pipe be waited for.  If we are not in a pipeline, or are the
         last command in the pipeline, then we wait for the subshell
         and return its exit status as usual. */
      if (pipe_out != NO_PIPE)
        return (EXECUTION_SUCCESS);

      stop_pipeline (asynchronous, (COMMAND *)NULL);

      if (asynchronous == 0)
        {
          last_command_exit_value = wait_for (paren_pid);

          /* If we have to, invert the return value. */
          if (invert)
        exec_result = ((last_command_exit_value == EXECUTION_SUCCESS)
                ? EXECUTION_FAILURE
                : EXECUTION_SUCCESS);
          else
        exec_result = last_command_exit_value;

          return (last_command_exit_value = exec_result);
        }
      else
        {
          DESCRIBE_PID (paren_pid);

          run_pending_traps ();

          return (EXECUTION_SUCCESS);
        }
    }
    }

#if defined (COMMAND_TIMING)
  if (command->flags & CMD_TIME_PIPELINE)
    {
      if (asynchronous)
    {
      command->flags |= CMD_FORCE_SUBSHELL;
      exec_result = execute_command_internal (command, 1, pipe_in, pipe_out, fds_to_close);
    }
      else
    {
      exec_result = time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close);
      if (running_trap == 0)
        currently_executing_command = (COMMAND *)NULL;
    }
      return (exec_result);
    }
#endif /* COMMAND_TIMING */

  if (shell_control_structure (command->type) && command->redirects)
    stdin_redir = stdin_redirects (command->redirects);

  /* Handle WHILE FOR CASE etc. with redirections.  (Also '&' input
     redirection.)  */
  if (do_redirections (command->redirects, 1, 1, 0) != 0)
    {
      cleanup_redirects (redirection_undo_list);
      redirection_undo_list = (REDIRECT *)NULL;
      dispose_exec_redirects ();
      return (EXECUTION_FAILURE);
    }

  if (redirection_undo_list)
    {
      my_undo_list = (REDIRECT *)copy_redirects (redirection_undo_list);
      dispose_redirects (redirection_undo_list);
      redirection_undo_list = (REDIRECT *)NULL;
    }
  else
    my_undo_list = (REDIRECT *)NULL;

  if (exec_redirection_undo_list)
    {
      exec_undo_list = (REDIRECT *)copy_redirects (exec_redirection_undo_list);
      dispose_redirects (exec_redirection_undo_list);
      exec_redirection_undo_list = (REDIRECT *)NULL;
    }
  else
    exec_undo_list = (REDIRECT *)NULL;

  if (my_undo_list || exec_undo_list)
    begin_unwind_frame ("loop_redirections");

  if (my_undo_list)
    add_unwind_protect ((Function *)cleanup_redirects, my_undo_list);

  if (exec_undo_list)
    add_unwind_protect ((Function *)dispose_redirects, exec_undo_list);

  ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;

  QUIT;

  switch (command->type)
    {
    case cm_simple:
      {
    /* We can't rely on this variable retaining its value across a
       call to execute_simple_command if a longjmp occurs as the
       result of a `return' builtin.  This is true for sure with gcc. */
    last_pid = last_made_pid;
    was_error_trap = signal_is_trapped (ERROR_TRAP) && signal_is_ignored (ERROR_TRAP) == 0;

    if (ignore_return && command->value.Simple)
      command->value.Simple->flags |= CMD_IGNORE_RETURN;
    if (command->flags & CMD_STDIN_REDIR)
      command->value.Simple->flags |= CMD_STDIN_REDIR;
    exec_result =
      execute_simple_command (command->value.Simple, pipe_in, pipe_out,
                  asynchronous, fds_to_close);

    /* The temporary environment should be used for only the simple
       command immediately following its definition. */
    dispose_used_env_vars ();

#if (defined (ultrix) && defined (mips)) || defined (C_ALLOCA)
    /* Reclaim memory allocated with alloca () on machines which
       may be using the alloca emulation code. */
    (void) alloca (0);
#endif /* (ultrix && mips) || C_ALLOCA */

    /* If we forked to do the command, then we must wait_for ()
       the child. */

    /* XXX - this is something to watch out for if there are problems
       when the shell is compiled without job control. */
    if (already_making_children && pipe_out == NO_PIPE &&
        last_pid != last_made_pid)
      {
        stop_pipeline (asynchronous, (COMMAND *)NULL);

        if (asynchronous)
          {
        DESCRIBE_PID (last_made_pid);
          }
        else
#if !defined (JOB_CONTROL)
          /* Do not wait for asynchronous processes started from
         startup files. */
        if (last_made_pid != last_asynchronous_pid)
#endif
        /* When executing a shell function that executes other
           commands, this causes the last simple command in
           the function to be waited for twice. */
          exec_result = wait_for (last_made_pid);
#if defined (RECYCLES_PIDS)
          /* LynxOS, for one, recycles pids very quickly -- so quickly
         that a new process may have the same pid as the last one
         created.  This has been reported to fix the problem. */
          if (exec_result == 0)
        last_made_pid = NO_PID;
#endif
      }
      }

      if (was_error_trap && ignore_return == 0 && invert == 0 && exec_result != EXECUTION_SUCCESS)
    {
      last_command_exit_value = exec_result;
      run_error_trap ();
    }

      if (ignore_return == 0 && invert == 0 &&
      ((posixly_correct && interactive == 0 && special_builtin_failed) ||
       (exit_immediately_on_error && (exec_result != EXECUTION_SUCCESS))))
    {
      last_command_exit_value = exec_result;
      run_pending_traps ();
      jump_to_top_level (EXITPROG);
    }

      break;

    case cm_for:
      if (ignore_return)
    command->value.For->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_for_command (command->value.For);
      break;

#if defined (ARITH_FOR_COMMAND)
    case cm_arith_for:
      if (ignore_return)
    command->value.ArithFor->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_arith_for_command (command->value.ArithFor);
      break;
#endif

#if defined (SELECT_COMMAND)
    case cm_select:
      if (ignore_return)
    command->value.Select->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_select_command (command->value.Select);
      break;
#endif

    case cm_case:
      if (ignore_return)
    command->value.Case->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_case_command (command->value.Case);
      break;

    case cm_while:
      if (ignore_return)
    command->value.While->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_while_command (command->value.While);
      break;

    case cm_until:
      if (ignore_return)
    command->value.While->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_until_command (command->value.While);
      break;

    case cm_if:
      if (ignore_return)
    command->value.If->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_if_command (command->value.If);
      break;

    case cm_group:

      /* This code can be executed from either of two paths: an explicit
     '{}' command, or via a function call.  If we are executed via a
     function call, we have already taken care of the function being
     executed in the background (down there in execute_simple_command ()),
     and this command should *not* be marked as asynchronous.  If we
     are executing a regular '{}' group command, and asynchronous == 1,
     we must want to execute the whole command in the background, so we
     need a subshell, and we want the stuff executed in that subshell
     (this group command) to be executed in the foreground of that
     subshell (i.e. there will not be *another* subshell forked).

     What we do is to force a subshell if asynchronous, and then call
     execute_command_internal again with asynchronous still set to 1,
     but with the original group command, so the printed command will
     look right.

     The code above that handles forking off subshells will note that
     both subshell and async are on, and turn off async in the child
     after forking the subshell (but leave async set in the parent, so
     the normal call to describe_pid is made).  This turning off
     async is *crucial*; if it is not done, this will fall into an
     infinite loop of executions through this spot in subshell after
     subshell until the process limit is exhausted. */

      if (asynchronous)
    {
      command->flags |= CMD_FORCE_SUBSHELL;
      exec_result =
        execute_command_internal (command, 1, pipe_in, pipe_out,
                      fds_to_close);
    }
      else
    {
      if (ignore_return && command->value.Group->command)
        command->value.Group->command->flags |= CMD_IGNORE_RETURN;
      exec_result =
        execute_command_internal (command->value.Group->command,
                      asynchronous, pipe_in, pipe_out,
                      fds_to_close);
    }
      break;

    case cm_connection:
      exec_result = execute_connection (command, asynchronous,
                    pipe_in, pipe_out, fds_to_close);
      break;

#if defined (DPAREN_ARITHMETIC)
    case cm_arith:
      if (ignore_return)
    command->value.Arith->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_arith_command (command->value.Arith);
      break;
#endif

#if defined (COND_COMMAND)
    case cm_cond:
      if (ignore_return)
    command->value.Cond->flags |= CMD_IGNORE_RETURN;
      exec_result = execute_cond_command (command->value.Cond);
      break;
#endif
    
    case cm_function_def:
      exec_result = execute_intern_function (command->value.Function_def->name,
                         command->value.Function_def->command);
      break;

    default:
      command_error ("execute_command", CMDERR_BADTYPE, command->type, 0);
    }

  if (my_undo_list)
    {
      do_redirections (my_undo_list, 1, 0, 0);
      dispose_redirects (my_undo_list);
    }

  if (exec_undo_list)
    dispose_redirects (exec_undo_list);

  if (my_undo_list || exec_undo_list)
    discard_unwind_frame ("loop_redirections");

  /* Invert the return value if we have to */
  if (invert)
    exec_result = (exec_result == EXECUTION_SUCCESS)
            ? EXECUTION_FAILURE
            : EXECUTION_SUCCESS;

  last_command_exit_value = exec_result;
  run_pending_traps ();
  if (running_trap == 0)
    currently_executing_command = (COMMAND *)NULL;
  return (last_command_exit_value);
}

#if defined (COMMAND_TIMING)

#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
extern struct timeval *difftimeval __P((struct timeval *, struct timeval *, struct timeval *));
extern struct timeval *addtimeval __P((struct timeval *, struct timeval *, struct timeval *));
extern int timeval_to_cpu __P((struct timeval *, struct timeval *, struct timeval *));
#endif

#define POSIX_TIMEFORMAT "real %2R\nuser %2U\nsys %2S"
#define BASH_TIMEFORMAT  "\nreal\t%3lR\nuser\t%3lU\nsys\t%3lS"

static int precs[] = { 0, 100, 10, 1 };

/* Expand one `%'-prefixed escape sequence from a time format string. */
static int
mkfmt (buf, prec, lng, sec, sec_fraction)
     char *buf;
     int prec, lng;
     time_t sec;
     int sec_fraction;
{
  time_t min;
  char abuf[INT_STRLEN_BOUND(time_t) + 1];
  int ind, aind;

  ind = 0;
  abuf[sizeof(abuf) - 1] = '\0';

  /* If LNG is non-zero, we want to decompose SEC into minutes and seconds. */
  if (lng)
    {
      min = sec / 60;
      sec %= 60;
      aind = sizeof(abuf) - 2;
      do
    abuf[aind--] = (min % 10) + '0';
      while (min /= 10);
      aind++;
      while (abuf[aind])
    buf[ind++] = abuf[aind++];
      buf[ind++] = 'm';
    }

  /* Now add the seconds. */
  aind = sizeof (abuf) - 2;
  do
    abuf[aind--] = (sec % 10) + '0';
  while (sec /= 10);
  aind++;
  while (abuf[aind])
    buf[ind++] = abuf[aind++];

  /* We want to add a decimal point and PREC places after it if PREC is
     nonzero.  PREC is not greater than 3.  SEC_FRACTION is between 0
     and 999. */
  if (prec != 0)
    {
      buf[ind++] = '.';
      for (aind = 1; aind <= prec; aind++)
    {
      buf[ind++] = (sec_fraction / precs[aind]) + '0';
      sec_fraction %= precs[aind];
    }
    }

  if (lng)
    buf[ind++] = 's';
  buf[ind] = '\0';

  return (ind);
}

/* Interpret the format string FORMAT, interpolating the following escape
   sequences:
        %[prec][l][RUS]

   where the optional `prec' is a precision, meaning the number of
   characters after the decimal point, the optional `l' means to format
   using minutes and seconds (MMmNN[.FF]s), like the `times' builtin',
   and the last character is one of
   
        R   number of seconds of `real' time
        U   number of seconds of `user' time
        S   number of seconds of `system' time

   An occurrence of `%%' in the format string is translated to a `%'.  The
   result is printed to FP, a pointer to a FILE.  The other variables are
   the seconds and thousandths of a second of real, user, and system time,
   resectively. */
static void
print_formatted_time (fp, format, rs, rsf, us, usf, ss, ssf, cpu)
     FILE *fp;
     char *format;
     time_t rs;
     int rsf;
     time_t us;
     int usf;
     time_t ss;
     int ssf, cpu;
{
  int prec, lng, len;
  char *str, *s, ts[INT_STRLEN_BOUND (time_t) + sizeof ("mSS.FFFF")];
  time_t sum;
  int sum_frac;
  int sindex, ssize;

  len = strlen (format);
  ssize = (len + 64) - (len % 64);
  str = (char *)xmalloc (ssize);
  sindex = 0;

  for (s = format; *s; s++)
    {
      if (*s != '%' || s[1] == '\0')
    {
      RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64);
      str[sindex++] = *s;
    }
      else if (s[1] == '%')
    {
      s++;
      RESIZE_MALLOCED_BUFFER (str, sindex, 1, ssize, 64);
      str[sindex++] = *s;
    }
      else if (s[1] == 'P')
    {
      s++;
      if (cpu > 10000)
        cpu = 10000;
      sum = cpu / 100;
      sum_frac = (cpu % 100) * 10;
      len = mkfmt (ts, 2, 0, sum, sum_frac);
      RESIZE_MALLOCED_BUFFER (str, sindex, len, ssize, 64);
      strcpy (str + sindex, ts);
      sindex += len;
    }
      else
    {
      prec = 3; /* default is three places past the decimal point. */
      lng = 0;  /* default is to not use minutes or append `s' */
      s++;
      if (DIGIT (*s))       /* `precision' */
        {
          prec = *s++ - '0';
          if (prec > 3) prec = 3;
        }
      if (*s == 'l')        /* `length extender' */
        {
          lng = 1;
          s++;
        }
      if (*s == 'R' || *s == 'E')
        len = mkfmt (ts, prec, lng, rs, rsf);
      else if (*s == 'U')
        len = mkfmt (ts, prec, lng, us, usf);
      else if (*s == 'S')
        len = mkfmt (ts, prec, lng, ss, ssf);
      else
        {
          internal_error ("bad format character in time format: %c", *s);
          free (str);
          return;
        }
      RESIZE_MALLOCED_BUFFER (str, sindex, len, ssize, 64);
      strcpy (str + sindex, ts);
      sindex += len;
    }
    }

  str[sindex] = '\0';
  fprintf (fp, "%s\n", str);
  fflush (fp);

  free (str);
}

static int
time_command (command, asynchronous, pipe_in, pipe_out, fds_to_close)
     COMMAND *command;
     int asynchronous, pipe_in, pipe_out;
     struct fd_bitmap *fds_to_close;
{
  int rv, posix_time, old_flags;
  time_t rs, us, ss;
  int rsf, usf, ssf;
  int cpu;
  char *time_format;

#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
  struct timeval real, user, sys;
  struct timeval before, after;
  struct timezone dtz;
  struct rusage selfb, selfa, kidsb, kidsa; /* a = after, b = before */
#else
#  if defined (HAVE_TIMES)
  clock_t tbefore, tafter, real, user, sys;
  struct tms before, after;
#  endif
#endif

#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
  gettimeofday (&before, &dtz);
  getrusage (RUSAGE_SELF, &selfb);
  getrusage (RUSAGE_CHILDREN, &kidsb);
#else
#  if defined (HAVE_TIMES)
  tbefore = times (&before);
#  endif
#endif

  posix_time = (command->flags & CMD_TIME_POSIX);

  old_flags = command->flags;
  command->flags &= ~(CMD_TIME_PIPELINE|CMD_TIME_POSIX);
  rv = execute_command_internal (command, asynchronous, pipe_in, pipe_out, fds_to_close);
  command->flags = old_flags;

  rs = us = ss = 0;
  rsf = usf = ssf = cpu = 0;

#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
  gettimeofday (&after, &dtz);
  getrusage (RUSAGE_SELF, &selfa);
  getrusage (RUSAGE_CHILDREN, &kidsa);

  difftimeval (&real, &before, &after);
  timeval_to_secs (&real, &rs, &rsf);

  addtimeval (&user, difftimeval(&after, &selfb.ru_utime, &selfa.ru_utime),
             difftimeval(&before, &kidsb.ru_utime, &kidsa.ru_utime));
  timeval_to_secs (&user, &us, &usf);

  addtimeval (&sys, difftimeval(&after, &selfb.ru_stime, &selfa.ru_stime),
            difftimeval(&before, &kidsb.ru_stime, &kidsa.ru_stime));
  timeval_to_secs (&sys, &ss, &ssf);

  cpu = timeval_to_cpu (&real, &user, &sys);
#else
#  if defined (HAVE_TIMES)
  tafter = times (&after);

  real = tafter - tbefore;
  clock_t_to_secs (real, &rs, &rsf);

  user = (after.tms_utime - before.tms_utime) + (after.tms_cutime - before.tms_cutime);
  clock_t_to_secs (user, &us, &usf);

  sys = (after.tms_stime - before.tms_stime) + (after.tms_cstime - before.tms_cstime);
  clock_t_to_secs (sys, &ss, &ssf);

  cpu = (real == 0) ? 0 : ((user + sys) * 10000) / real;

#  else
  rs = us = ss = 0;
  rsf = usf = ssf = cpu = 0;
#  endif
#endif

  if (posix_time)
    time_format = POSIX_TIMEFORMAT;
  else if ((time_format = get_string_value ("TIMEFORMAT")) == 0)
    time_format = BASH_TIMEFORMAT;

  if (time_format && *time_format)
    print_formatted_time (stderr, time_format, rs, rsf, us, usf, ss, ssf, cpu);

  return rv;
}
#endif /* COMMAND_TIMING */

/* Execute a command that's supposed to be in a subshell.  This must be
   called after make_child and we must be running in the child process.
   The caller will return or exit() immediately with the value this returns. */
static int
execute_in_subshell (command, asynchronous, pipe_in, pipe_out, fds_to_close)
     COMMAND *command;
     int asynchronous;
     int pipe_in, pipe_out;
     struct fd_bitmap *fds_to_close;
{
  int user_subshell, return_code, function_value, should_redir_stdin, invert;
  int ois;
  COMMAND *tcom;

  USE_VAR(user_subshell);
  USE_VAR(invert);
  USE_VAR(tcom);
  USE_VAR(asynchronous);

  should_redir_stdin = (asynchronous && (command->flags & CMD_STDIN_REDIR) &&
              pipe_in == NO_PIPE &&
              stdin_redirects (command->redirects) == 0);

  invert = (command->flags & CMD_INVERT_RETURN) != 0;
  user_subshell = command->type == cm_subshell || ((command->flags & CMD_WANT_SUBSHELL) != 0);

  command->flags &= ~(CMD_FORCE_SUBSHELL | CMD_WANT_SUBSHELL | CMD_INVERT_RETURN);

  /* If a command is asynchronous in a subshell (like ( foo ) & or
     the special case of an asynchronous GROUP command where the
     the subshell bit is turned on down in case cm_group: below),
     turn off `asynchronous', so that two subshells aren't spawned.

     This seems semantically correct to me.  For example,
     ( foo ) & seems to say ``do the command `foo' in a subshell
     environment, but don't wait for that subshell to finish'',
     and "{ foo ; bar ; } &" seems to me to be like functions or
     builtins in the background, which executed in a subshell
     environment.  I just don't see the need to fork two subshells. */

  /* Don't fork again, we are already in a subshell.  A `doubly
     async' shell is not interactive, however. */
  if (asynchronous)
    {
#if defined (JOB_CONTROL)
      /* If a construct like ( exec xxx yyy ) & is given while job
     control is active, we want to prevent exec from putting the
     subshell back into the original process group, carefully
     undoing all the work we just did in make_child. */
      original_pgrp = -1;
#endif /* JOB_CONTROL */
      ois = interactive_shell;
      interactive_shell = 0;
      /* This test is to prevent alias expansion by interactive shells that
     run `(command) &' but to allow scripts that have enabled alias
     expansion with `shopt -s expand_alias' to continue to expand
     aliases. */
      if (ois != interactive_shell)
    expand_aliases = 0;
      asynchronous = 0;
    }

  /* Subshells are neither login nor interactive. */
  login_shell = interactive = 0;

  subshell_environment = user_subshell ? SUBSHELL_PAREN : SUBSHELL_ASYNC;

  reset_terminating_signals ();     /* in sig.c */
  /* Cancel traps, in trap.c. */
  restore_original_signals ();
  if (asynchronous)
    setup_async_signals ();

#if defined (JOB_CONTROL)
  set_sigchld_handler ();
#endif /* JOB_CONTROL */

  set_sigint_handler ();

#if defined (JOB_CONTROL)
  /* Delete all traces that there were any jobs running.  This is
     only for subshells. */
  without_job_control ();
#endif /* JOB_CONTROL */

  if (fds_to_close)
    close_fd_bitmap (fds_to_close);

  do_piping (pipe_in, pipe_out);

  /* If this is a user subshell, set a flag if stdin was redirected.
     This is used later to decide whether to redirect fd 0 to
     /dev/null for async commands in the subshell.  This adds more
     sh compatibility, but I'm not sure it's the right thing to do. */
  if (user_subshell)
    {
      stdin_redir = stdin_redirects (command->redirects);
      restore_default_signal (0);
    }

  /* If this is an asynchronous command (command &), we want to
     redirect the standard input from /dev/null in the absence of
     any specific redirection involving stdin. */
  if (should_redir_stdin && stdin_redir == 0)
    async_redirect_stdin ();

  /* Do redirections, then dispose of them before recursive call. */
  if (command->redirects)
    {
      if (do_redirections (command->redirects, 1, 0, 0) != 0)
    exit (invert ? EXECUTION_SUCCESS : EXECUTION_FAILURE);

      dispose_redirects (command->redirects);
      command->redirects = (REDIRECT *)NULL;
    }

  tcom = (command->type == cm_subshell) ? command->value.Subshell->command : command;

  /* Make sure the subshell inherits any CMD_IGNORE_RETURN flag. */
  if ((command->flags & CMD_IGNORE_RETURN) && tcom != command)
    tcom->flags |= CMD_IGNORE_RETURN;

  /* If this is a simple command, tell execute_disk_command that it
     might be able to get away without forking and simply exec.
     This means things like ( sleep 10 ) will only cause one fork.
     If we're timing the command or inverting its return value, however,
     we cannot do this optimization. */
  if (user_subshell && (tcom->type == cm_simple || tcom->type == cm_subshell) &&
      ((tcom->flags & CMD_TIME_PIPELINE) == 0) &&
      ((tcom->flags & CMD_INVERT_RETURN) == 0))
    {
      tcom->flags |= CMD_NO_FORK;
      if (tcom->type == cm_simple)
    tcom->value.Simple->flags |= CMD_NO_FORK;
    }

  invert = (tcom->flags & CMD_INVERT_RETURN) != 0;
  tcom->flags &= ~CMD_INVERT_RETURN;

  /* If we're inside a function while executing this subshell, we
     need to handle a possible `return'. */
  function_value = 0;
  if (return_catch_flag)
    function_value = setjmp (return_catch);

  if (function_value)
    return_code = return_catch_value;
  else
    return_code = execute_command_internal
      (tcom, asynchronous, NO_PIPE, NO_PIPE, fds_to_close);

  /* If we are asked to, invert the return value. */
  if (invert)
    return_code = (return_code == EXECUTION_SUCCESS) ? EXECUTION_FAILURE
                             : EXECUTION_SUCCESS;

  /* If we were explicitly placed in a subshell with (), we need
     to do the `shell cleanup' things, such as running traps[0]. */
  if (user_subshell && signal_is_trapped (0))
    {
      last_command_exit_value = return_code;
      return_code = run_exit_trap ();
    }

  return (return_code);
  /* NOTREACHED */
}

static int
execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close)
     COMMAND *command;
     int asynchronous, pipe_in, pipe_out;
     struct fd_bitmap *fds_to_close;
{
  int prev, fildes[2], new_bitmap_size, dummyfd, ignore_return, exec_result;
  COMMAND *cmd;
  struct fd_bitmap *fd_bitmap;

#if defined (JOB_CONTROL)
  sigset_t set, oset;
  BLOCK_CHILD (set, oset);
#endif /* JOB_CONTROL */

  ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;

  prev = pipe_in;
  cmd = command;

  while (cmd && cmd->type == cm_connection &&
     cmd->value.Connection && cmd->value.Connection->connector == '|')
    {
      /* Make a pipeline between the two commands. */
      if (pipe (fildes) < 0)
    {
      sys_error ("pipe error");
#if defined (JOB_CONTROL)
      terminate_current_pipeline ();
      kill_current_pipeline ();
#endif /* JOB_CONTROL */
      last_command_exit_value = EXECUTION_FAILURE;
      /* The unwind-protects installed below will take care
         of closing all of the open file descriptors. */
      throw_to_top_level ();
      return (EXECUTION_FAILURE);   /* XXX */
    }

      /* Here is a problem: with the new file close-on-exec
     code, the read end of the pipe (fildes[0]) stays open
     in the first process, so that process will never get a
     SIGPIPE.  There is no way to signal the first process
     that it should close fildes[0] after forking, so it
     remains open.  No SIGPIPE is ever sent because there
     is still a file descriptor open for reading connected
     to the pipe.  We take care of that here.  This passes
     around a bitmap of file descriptors that must be
     closed after making a child process in execute_simple_command. */

      /* We need fd_bitmap to be at least as big as fildes[0].
     If fildes[0] is less than fds_to_close->size, then
     use fds_to_close->size. */
      new_bitmap_size = (fildes[0] < fds_to_close->size)
                ? fds_to_close->size
                : fildes[0] + 8;

      fd_bitmap = new_fd_bitmap (new_bitmap_size);

      /* Now copy the old information into the new bitmap. */
      xbcopy ((char *)fds_to_close->bitmap, (char *)fd_bitmap->bitmap, fds_to_close->size);

      /* And mark the pipe file descriptors to be closed. */
      fd_bitmap->bitmap[fildes[0]] = 1;

      /* In case there are pipe or out-of-processes errors, we
     want all these file descriptors to be closed when
     unwind-protects are run, and the storage used for the
     bitmaps freed up. */
      begin_unwind_frame ("pipe-file-descriptors");
      add_unwind_protect (dispose_fd_bitmap, fd_bitmap);
      add_unwind_protect (close_fd_bitmap, fd_bitmap);
      if (prev >= 0)
    add_unwind_protect (close, prev);
      dummyfd = fildes[1];
      add_unwind_protect (close, dummyfd);

#if defined (JOB_CONTROL)
      add_unwind_protect (restore_signal_mask, &oset);
#endif /* JOB_CONTROL */

      if (ignore_return && cmd->value.Connection->first)
    cmd->value.Connection->first->flags |= CMD_IGNORE_RETURN;
      execute_command_internal (cmd->value.Connection->first, asynchronous,
                prev, fildes[1], fd_bitmap);

      if (prev >= 0)
    close (prev);

      prev = fildes[0];
      close (fildes[1]);

      dispose_fd_bitmap (fd_bitmap);
      discard_unwind_frame ("pipe-file-descriptors");

      cmd = cmd->value.Connection->second;
    }

  /* Now execute the rightmost command in the pipeline.  */
  if (ignore_return && cmd)
    cmd->flags |= CMD_IGNORE_RETURN;
  exec_result = execute_command_internal (cmd, asynchronous, prev, pipe_out, fds_to_close);

  if (prev >= 0)
    close (prev);

#if defined (JOB_CONTROL)
  UNBLOCK_CHILD (oset);
#endif

  return (exec_result);
}

static int
execute_connection (command, asynchronous, pipe_in, pipe_out, fds_to_close)
     COMMAND *command;
     int asynchronous, pipe_in, pipe_out;
     struct fd_bitmap *fds_to_close;
{
  REDIRECT *rp;
  COMMAND *tc, *second;
  int ignore_return, exec_result;

  ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;

  switch (command->value.Connection->connector)
    {
    /* Do the first command asynchronously. */
    case '&':
      tc = command->value.Connection->first;
      if (tc == 0)
    return (EXECUTION_SUCCESS);

      rp = tc->redirects;

      if (ignore_return)
    tc->flags |= CMD_IGNORE_RETURN;
      tc->flags |= CMD_AMPERSAND;

      /* If this shell was compiled without job control support,
     if we are currently in a subshell via `( xxx )', or if job
     control is not active then the standard input for an
     asynchronous command is forced to /dev/null. */
#if defined (JOB_CONTROL)
      if ((subshell_environment || !job_control) && !stdin_redir)
#else
      if (!stdin_redir)
#endif /* JOB_CONTROL */
    tc->flags |= CMD_STDIN_REDIR;

      exec_result = execute_command_internal (tc, 1, pipe_in, pipe_out, fds_to_close);

      if (tc->flags & CMD_STDIN_REDIR)
    tc->flags &= ~CMD_STDIN_REDIR;

      second = command->value.Connection->second;
      if (second)
    {
      if (ignore_return)
        second->flags |= CMD_IGNORE_RETURN;

      exec_result = execute_command_internal (second, asynchronous, pipe_in, pipe_out, fds_to_close);
    }

      break;

    /* Just call execute command on both sides. */
    case ';':
      if (ignore_return)
    {
      if (command->value.Connection->first)
        command->value.Connection->first->flags |= CMD_IGNORE_RETURN;
      if (command->value.Connection->second)
        command->value.Connection->second->flags |= CMD_IGNORE_RETURN;
    }
      QUIT;
      execute_command (command->value.Connection->first);
      QUIT;
      exec_result = execute_command_internal (command->value.Connection->second,
                      asynchronous, pipe_in, pipe_out,
                      fds_to_close);
      break;

    case '|':
      exec_result = execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close);
      break;

    case AND_AND:
    case OR_OR:
      if (asynchronous)
    {
      /* If we have something like `a && b &' or `a || b &', run the
         && or || stuff in a subshell.  Force a subshell and just call
         execute_command_internal again.  Leave asynchronous on
         so that we get a report from the parent shell about the
         background job. */
      command->flags |= CMD_FORCE_SUBSHELL;
      exec_result = execute_command_internal (command, 1, pipe_in, pipe_out, fds_to_close);
      break;
    }

      /* Execute the first command.  If the result of that is successful
     and the connector is AND_AND, or the result is not successful
     and the connector is OR_OR, then execute the second command,
     otherwise return. */

      if (command->value.Connection->first)
    command->value.Connection->first->flags |= CMD_IGNORE_RETURN;

      exec_result = execute_command (command->value.Connection->first);
      QUIT;
      if (((command->value.Connection->connector == AND_AND) &&
       (exec_result == EXECUTION_SUCCESS)) ||
      ((command->value.Connection->connector == OR_OR) &&
       (exec_result != EXECUTION_SUCCESS)))
    {
      if (ignore_return && command->value.Connection->second)
        command->value.Connection->second->flags |= CMD_IGNORE_RETURN;

      exec_result = execute_command (command->value.Connection->second);
    }
      break;

    default:
      command_error ("execute_connection", CMDERR_BADCONN, command->value.Connection->connector, 0);
      jump_to_top_level (DISCARD);
      exec_result = EXECUTION_FAILURE;
    }

  return exec_result;
}

#define REAP() \
  do \
    { \
      if (!interactive_shell) \
    reap_dead_jobs (); \
    } \
  while (0)

/* Execute a FOR command.  The syntax is: FOR word_desc IN word_list;
   DO command; DONE */
static int
execute_for_command (for_command)
     FOR_COM *for_command;
{
  register WORD_LIST *releaser, *list;
  SHELL_VAR *v;
  char *identifier;
  int retval;
#if 0
  SHELL_VAR *old_value = (SHELL_VAR *)NULL; /* Remember the old value of x. */
#endif

  if (check_identifier (for_command->name, 1) == 0)
    {
      if (posixly_correct && interactive_shell == 0)
    {
      last_command_exit_value = EX_USAGE;
      jump_to_top_level (EXITPROG);
    }
      return (EXECUTION_FAILURE);
    }

  loop_level++;
  identifier = for_command->name->word;

  list = releaser = expand_words_no_vars (for_command->map_list);

  begin_unwind_frame ("for");
  add_unwind_protect (dispose_words, releaser);

#if 0
  if (lexical_scoping)
    {
      old_value = copy_variable (find_variable (identifier));
      if (old_value)
    add_unwind_protect (dispose_variable, old_value);
    }
#endif

  if (for_command->flags & CMD_IGNORE_RETURN)
    for_command->action->flags |= CMD_IGNORE_RETURN;

  for (retval = EXECUTION_SUCCESS; list; list = list->next)
    {
      QUIT;
      this_command_name = (char *)NULL;
      v = bind_variable (identifier, list->word->word);
      if (readonly_p (v) || noassign_p (v))
    {
      if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
        {
          last_command_exit_value = EXECUTION_FAILURE;
          jump_to_top_level (FORCE_EOF);
        }
      else
        {
          run_unwind_frame ("for");
          loop_level--;
          return (EXECUTION_FAILURE);
        }
    }
      retval = execute_command (for_command->action);
      REAP ();
      QUIT;

      if (breaking)
    {
      breaking--;
      break;
    }

      if (continuing)
    {
      continuing--;
      if (continuing)
        break;
    }
    }

  loop_level--;

#if 0
  if (lexical_scoping)
    {
      if (!old_value)
        unbind_variable (identifier);
      else
    {
      SHELL_VAR *new_value;

      new_value = bind_variable (identifier, value_cell(old_value));
      new_value->attributes = old_value->attributes;
      dispose_variable (old_value);
    }
    }
#endif

  dispose_words (releaser);
  discard_unwind_frame ("for");
  return (retval);
}

#if defined (ARITH_FOR_COMMAND)
/* Execute an arithmetic for command.  The syntax is

    for (( init ; step ; test ))
    do
        body
    done

   The execution should be exactly equivalent to

    eval \(\( init \)\)
    while eval \(\( test \)\) ; do
        body;
        eval \(\( step \)\)
    done
*/
static intmax_t
eval_arith_for_expr (l, okp)
     WORD_LIST *l;
     int *okp;
{
  WORD_LIST *new;
  intmax_t expresult;

  new = expand_words_no_vars (l);
  if (new)
    {
      if (echo_command_at_execute)
    xtrace_print_arith_cmd (new);
      this_command_name = "((";     /* )) for expression error messages */
      if (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
    run_debug_trap ();
      expresult = evalexp (new->word->word, okp);
      dispose_words (new);
    }
  else
    {
      expresult = 0;
      if (okp)
    *okp = 1;
    }
  return (expresult);
}

static int
execute_arith_for_command (arith_for_command)
     ARITH_FOR_COM *arith_for_command;
{
  intmax_t expresult;
  int expok, body_status, arith_lineno, save_lineno;

  body_status = EXECUTION_SUCCESS;
  loop_level++;

  if (arith_for_command->flags & CMD_IGNORE_RETURN)
    arith_for_command->action->flags |= CMD_IGNORE_RETURN;

  this_command_name = "(("; /* )) for expression error messages */

  /* save the starting line number of the command so we can reset
     line_number before executing each expression -- for $LINENO
     and the DEBUG trap. */
  arith_lineno = arith_for_command->line;
  if (variable_context && interactive_shell)
    line_number = arith_lineno -= function_line_number;

  /* Evaluate the initialization expression. */
  expresult = eval_arith_for_expr (arith_for_command->init, &expok);
  if (expok == 0)
    return (EXECUTION_FAILURE);

  while (1)
    {
      /* Evaluate the test expression. */
      save_lineno = line_number;
      line_number = arith_lineno;
      expresult = eval_arith_for_expr (arith_for_command->test, &expok);
      line_number = save_lineno;

      if (expok == 0)
    {
      body_status = EXECUTION_FAILURE;
      break;
    }
      REAP ();
      if (expresult == 0)
    break;

      /* Execute the body of the arithmetic for command. */
      QUIT;
      body_status = execute_command (arith_for_command->action);
      QUIT;

      /* Handle any `break' or `continue' commands executed by the body. */
      if (breaking)
    {
      breaking--;
      break;
    }

      if (continuing)
    {
      continuing--;
      if (continuing)
        break;
    }

      /* Evaluate the step expression. */
      save_lineno = line_number;
      line_number = arith_lineno;
      expresult = eval_arith_for_expr (arith_for_command->step, &expok);
      line_number = save_lineno;

      if (expok == 0)
    {
      body_status = EXECUTION_FAILURE;
      break;
    }
    }

  loop_level--;
  return (body_status);
}
#endif

#if defined (SELECT_COMMAND)
static int LINES, COLS, tabsize;

#define RP_SPACE ") "
#define RP_SPACE_LEN 2

/* XXX - does not handle numbers > 1000000 at all. */
#define NUMBER_LEN(s) \
((s < 10) ? 1 \
      : ((s < 100) ? 2 \
              : ((s < 1000) ? 3 \
                   : ((s < 10000) ? 4 \
                         : ((s < 100000) ? 5 \
                                : 6)))))

static int
print_index_and_element (len, ind, list)
      int len, ind;
      WORD_LIST *list;
{
  register WORD_LIST *l;
  register int i;

  if (list == 0)
    return (0);
  for (i = ind, l = list; l && --i; l = l->next)
    ;
  fprintf (stderr, "%*d%s%s", len, ind, RP_SPACE, l->word->word);
  return (STRLEN (l->word->word));
}

static void
indent (from, to)
     int from, to;
{
  while (from < to)
    {
      if ((to / tabsize) > (from / tabsize))
    {
      putc ('\t', stderr);
      from += tabsize - from % tabsize;
    }
      else
    {
      putc (' ', stderr);
      from++;
    }
    }
}

static void
print_select_list (list, list_len, max_elem_len, indices_len)
     WORD_LIST *list;
     int list_len, max_elem_len, indices_len;
{
  int ind, row, elem_len, pos, cols, rows;
  int first_column_indices_len, other_indices_len;

  if (list == 0)
    {
      putc ('\n', stderr);
      return;
    }

  cols = max_elem_len ? COLS / max_elem_len : 1;
  if (cols == 0)
    cols = 1;
  rows = list_len ? list_len / cols + (list_len % cols != 0) : 1;
  cols = list_len ? list_len / rows + (list_len % rows != 0) : 1;

  if (rows == 1)
    {
      rows = cols;
      cols = 1;
    }

  first_column_indices_len = NUMBER_LEN (rows);
  other_indices_len = indices_len;

  for (row = 0; row < rows; row++)
    {
      ind = row;
      pos = 0;
      while (1)
    {
      indices_len = (pos == 0) ? first_column_indices_len : other_indices_len;
      elem_len = print_index_and_element (indices_len, ind + 1, list);
      elem_len += indices_len + RP_SPACE_LEN;
      ind += rows;
      if (ind >= list_len)
        break;
      indent (pos + elem_len, pos + max_elem_len);
      pos += max_elem_len;
    }
      putc ('\n', stderr);
    }
}

/* Print the elements of LIST, one per line, preceded by an index from 1 to
   LIST_LEN.  Then display PROMPT and wait for the user to enter a number.
   If the number is between 1 and LIST_LEN, return that selection.  If EOF
   is read, return a null string.  If a blank line is entered, or an invalid
   number is entered, the loop is executed again. */
static char *
select_query (list, list_len, prompt, print_menu)
     WORD_LIST *list;
     int list_len;
     char *prompt;
     int print_menu;
{
  int max_elem_len, indices_len, len;
  intmax_t reply;
  WORD_LIST *l;
  char *repl_string, *t;

  t = get_string_value ("LINES");
  LINES = (t && *t) ? atoi (t) : 24;
  t = get_string_value ("COLUMNS");
  COLS =  (t && *t) ? atoi (t) : 80;

#if 0
  t = get_string_value ("TABSIZE");
  tabsize = (t && *t) ? atoi (t) : 8;
  if (tabsize <= 0)
    tabsize = 8;
#else
  tabsize = 8;
#endif

  max_elem_len = 0;
  for (l = list; l; l = l->next)
    {
      len = STRLEN (l->word->word);
      if (len > max_elem_len)
    max_elem_len = len;
    }
  indices_len = NUMBER_LEN (list_len);
  max_elem_len += indices_len + RP_SPACE_LEN + 2;

  while (1)
    {
      if (print_menu)
    print_select_list (list, list_len, max_elem_len, indices_len);
      fprintf (stderr, "%s", prompt);
      fflush (stderr);
      QUIT;

      if (read_builtin ((WORD_LIST *)NULL) == EXECUTION_FAILURE)
    {
      putchar ('\n');
      return ((char *)NULL);
    }
      repl_string = get_string_value ("REPLY");
      if (*repl_string == 0)
    {
      print_menu = 1;
      continue;
    }
      if (legal_number (repl_string, &reply) == 0)
    return "";
      if (reply < 1 || reply > list_len)
    return "";

      for (l = list; l && --reply; l = l->next)
    ;
      return (l->word->word);
    }
}

/* Execute a SELECT command.  The syntax is:
   SELECT word IN list DO command_list DONE
   Only `break' or `return' in command_list will terminate
   the command. */
static int
execute_select_command (select_command)
     SELECT_COM *select_command;
{
  WORD_LIST *releaser, *list;
  SHELL_VAR *v;
  char *identifier, *ps3_prompt, *selection;
  int retval, list_len, show_menu;

  if (check_identifier (select_command->name, 1) == 0)
    return (EXECUTION_FAILURE);

  loop_level++;
  identifier = select_command->name->word;

  /* command and arithmetic substitution, parameter and variable expansion,
     word splitting, pathname expansion, and quote removal. */
  list = releaser = expand_words_no_vars (select_command->map_list);
  list_len = list_length (list);
  if (list == 0 || list_len == 0)
    {
      if (list)
    dispose_words (list);
      return (EXECUTION_SUCCESS);
    }

  begin_unwind_frame ("select");
  add_unwind_protect (dispose_words, releaser);

  if (select_command->flags & CMD_IGNORE_RETURN)
    select_command->action->flags |= CMD_IGNORE_RETURN;

  retval = EXECUTION_SUCCESS;
  show_menu = 1;

  while (1)
    {
      ps3_prompt = get_string_value ("PS3");
      if (ps3_prompt == 0)
    ps3_prompt = "#? ";

      QUIT;
      selection = select_query (list, list_len, ps3_prompt, show_menu);
      QUIT;
      if (selection == 0)
    {
      /* select_query returns EXECUTION_FAILURE if the read builtin
         fails, so we want to return failure in this case. */
      retval = EXECUTION_FAILURE;
      break;
    }

      v = bind_variable (identifier, selection);
      if (readonly_p (v) || noassign_p (v))
    {
      if (readonly_p (v) && interactive_shell == 0 && posixly_correct)
        {
          last_command_exit_value = EXECUTION_FAILURE;
          jump_to_top_level (FORCE_EOF);
        }
      else
        {
          run_unwind_frame ("select");
          return (EXECUTION_FAILURE);
        }
    }

      retval = execute_command (select_command->action);

      REAP ();
      QUIT;

      if (breaking)
    {
      breaking--;
      break;
    }

      if (continuing)
    {
      continuing--;
      if (continuing)
        break;
    }

#if defined (KSH_COMPATIBLE_SELECT)
      show_menu = 0;
      selection = get_string_value ("REPLY");
      if (selection && *selection == '\0')
        show_menu = 1;
#endif
    }

  loop_level--;

  run_unwind_frame ("select");
  return (retval);
}
#endif /* SELECT_COMMAND */

/* Execute a CASE command.  The syntax is: CASE word_desc IN pattern_list ESAC.
   The pattern_list is a linked list of pattern clauses; each clause contains
   some patterns to compare word_desc against, and an associated command to
   execute. */
static int
execute_case_command (case_command)
     CASE_COM *case_command;
{
  register WORD_LIST *list;
  WORD_LIST *wlist, *es;
  PATTERN_LIST *clauses;
  char *word, *pattern;
  int retval, match, ignore_return;

  /* Posix.2 specifies that the WORD is tilde expanded. */
  if (member ('~', case_command->word->word))
    {
      word = bash_tilde_expand (case_command->word->word, 0);
      free (case_command->word->word);
      case_command->word->word = word;
    }

  wlist = expand_word_unsplit (case_command->word, 0);
  word = wlist ? string_list (wlist) : savestring ("");
  dispose_words (wlist);

  retval = EXECUTION_SUCCESS;
  ignore_return = case_command->flags & CMD_IGNORE_RETURN;

  begin_unwind_frame ("case");
  add_unwind_protect ((Function *)xfree, word);

#define EXIT_CASE()  goto exit_case_command

  for (clauses = case_command->clauses; clauses; clauses = clauses->next)
    {
      QUIT;
      for (list = clauses->patterns; list; list = list->next)
    {
      /* Posix.2 specifies to tilde expand each member of the pattern
         list. */
      if (member ('~', list->word->word))
        {
          pattern = bash_tilde_expand (list->word->word, 0);
          free (list->word->word);
          list->word->word = pattern;
        }

      es = expand_word_leave_quoted (list->word, 0);

      if (es && es->word && es->word->word && *(es->word->word))
        pattern = quote_string_for_globbing (es->word->word, QGLOB_CVTNULL);
      else
        {
          pattern = (char *)xmalloc (1);
          pattern[0] = '\0';
        }

      /* Since the pattern does not undergo quote removal (as per
         Posix.2, section 3.9.4.3), the strmatch () call must be able
         to recognize backslashes as escape characters. */
      match = strmatch (pattern, word, FNMATCH_EXTFLAG) != FNM_NOMATCH;
      free (pattern);

      dispose_words (es);

      if (match)
        {
          if (clauses->action && ignore_return)
        clauses->action->flags |= CMD_IGNORE_RETURN;
          retval = execute_command (clauses->action);
          EXIT_CASE ();
        }

      QUIT;
    }
    }

exit_case_command:
  free (word);
  discard_unwind_frame ("case");
  return (retval);
}

#define CMD_WHILE 0
#define CMD_UNTIL 1

/* The WHILE command.  Syntax: WHILE test DO action; DONE.
   Repeatedly execute action while executing test produces
   EXECUTION_SUCCESS. */
static int
execute_while_command (while_command)
     WHILE_COM *while_command;
{
  return (execute_while_or_until (while_command, CMD_WHILE));
}

/* UNTIL is just like WHILE except that the test result is negated. */
static int
execute_until_command (while_command)
     WHILE_COM *while_command;
{
  return (execute_while_or_until (while_command, CMD_UNTIL));
}

/* The body for both while and until.  The only difference between the
   two is that the test value is treated differently.  TYPE is
   CMD_WHILE or CMD_UNTIL.  The return value for both commands should
   be EXECUTION_SUCCESS if no commands in the body are executed, and
   the status of the last command executed in the body otherwise. */
static int
execute_while_or_until (while_command, type)
     WHILE_COM *while_command;
     int type;
{
  int return_value, body_status;

  body_status = EXECUTION_SUCCESS;
  loop_level++;

  while_command->test->flags |= CMD_IGNORE_RETURN;
  if (while_command->flags & CMD_IGNORE_RETURN)
    while_command->action->flags |= CMD_IGNORE_RETURN;

  while (1)
    {
      return_value = execute_command (while_command->test);
      REAP ();

      /* Need to handle `break' in the test when we would break out of the
         loop.  The job control code will set `breaking' to loop_level
         when a job in a loop is stopped with SIGTSTP.  If the stopped job
         is in the loop test, `breaking' will not be reset unless we do
         this, and the shell will cease to execute commands. */
      if (type == CMD_WHILE && return_value != EXECUTION_SUCCESS)
    {
      if (breaking)
        breaking--;
      break;
    }
      if (type == CMD_UNTIL && return_value == EXECUTION_SUCCESS)
    {
      if (breaking)
        breaking--;
      break;
    }

      QUIT;
      body_status = execute_command (while_command->action);
      QUIT;

      if (breaking)
    {
      breaking--;
      break;
    }

      if (continuing)
    {
      continuing--;
      if (continuing)
        break;
    }
    }
  loop_level--;

  return (body_status);
}

/* IF test THEN command [ELSE command].
   IF also allows ELIF in the place of ELSE IF, but
   the parser makes *that* stupidity transparent. */
static int
execute_if_command (if_command)
     IF_COM *if_command;
{
  int return_value;

  if_command->test->flags |= CMD_IGNORE_RETURN;
  return_value = execute_command (if_command->test);

  if (return_value == EXECUTION_SUCCESS)
    {
      QUIT;

      if (if_command->true_case && (if_command->flags & CMD_IGNORE_RETURN))
    if_command->true_case->flags |= CMD_IGNORE_RETURN;

      return (execute_command (if_command->true_case));
    }
  else
    {
      QUIT;

      if (if_command->false_case && (if_command->flags & CMD_IGNORE_RETURN))
    if_command->false_case->flags |= CMD_IGNORE_RETURN;

      return (execute_command (if_command->false_case));
    }
}

#if defined (DPAREN_ARITHMETIC)
static int
execute_arith_command (arith_command)
     ARITH_COM *arith_command;
{
  int expok;
  intmax_t expresult;
  WORD_LIST *new;

  expresult = 0;

  this_command_name = "(("; /* )) */
  /* If we're in a function, update the line number information. */
  if (variable_context && interactive_shell)
    line_number = arith_command->line - function_line_number;

  /* Run the debug trap before each arithmetic command, but do it after we
     update the line number information and before we expand the various
     words in the expression. */
  if (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
    run_debug_trap ();

  new = expand_words (arith_command->exp);

  /* If we're tracing, make a new word list with `((' at the front and `))'
     at the back and print it. */
  if (echo_command_at_execute)
    xtrace_print_arith_cmd (new);

  expresult = evalexp (new->word->word, &expok);
  dispose_words (new);

  if (expok == 0)
    return (EXECUTION_FAILURE);

  return (expresult == 0 ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
}
#endif /* DPAREN_ARITHMETIC */

#if defined (COND_COMMAND)

static char *nullstr = "";

static int
execute_cond_node (cond)
     COND_COM *cond;
{
  int result, invert, patmatch;
  char *arg1, *arg2;

  invert = (cond->flags & CMD_INVERT_RETURN);

  if (cond->type == COND_EXPR)
    result = execute_cond_node (cond->left);
  else if (cond->type == COND_OR)
    {
      result = execute_cond_node (cond->left);
      if (result != EXECUTION_SUCCESS)
    result = execute_cond_node (cond->right);
    }
  else if (cond->type == COND_AND)
    {
      result = execute_cond_node (cond->left);
      if (result == EXECUTION_SUCCESS)
    result = execute_cond_node (cond->right);
    }
  else if (cond->type == COND_UNARY)
    {
      arg1 = cond_expand_word (cond->left->op, 0);
      if (arg1 == 0)
    arg1 = nullstr;
      if (echo_command_at_execute)
    xtrace_print_cond_term (cond->type, invert, cond->op, arg1, (char *)NULL);
      result = unary_test (cond->op->word, arg1) ? EXECUTION_SUCCESS : EXECUTION_FAILURE;
      if (arg1 != nullstr)
    free (arg1);
    }
  else if (cond->type == COND_BINARY)
    {
      patmatch = ((cond->op->word[1] == '=') && (cond->op->word[2] == '\0') &&
          (cond->op->word[0] == '!' || cond->op->word[0] == '=') ||
          (cond->op->word[0] == '=' && cond->op->word[1] == '\0'));

      arg1 = cond_expand_word (cond->left->op, 0);
      if (arg1 == 0)
    arg1 = nullstr;
      arg2 = cond_expand_word (cond->right->op, patmatch);
      if (arg2 == 0)
    arg2 = nullstr;

      if (echo_command_at_execute)
    xtrace_print_cond_term (cond->type, invert, cond->op, arg1, arg2);

      result = binary_test (cond->op->word, arg1, arg2, TEST_PATMATCH|TEST_ARITHEXP)
                ? EXECUTION_SUCCESS
                : EXECUTION_FAILURE;
      if (arg1 != nullstr)
    free (arg1);
      if (arg2 != nullstr)
    free (arg2);
    }
  else
    {
      command_error ("execute_cond_node", CMDERR_BADTYPE, cond->type, 0);
      jump_to_top_level (DISCARD);
      result = EXECUTION_FAILURE;
    }

  if (invert)
    result = (result == EXECUTION_SUCCESS) ? EXECUTION_FAILURE : EXECUTION_SUCCESS;

  return result;
}

static int
execute_cond_command (cond_command)
     COND_COM *cond_command;
{
  int result;

  result = EXECUTION_SUCCESS;

  this_command_name = "[[";
  /* If we're in a function, update the line number information. */
  if (variable_context && interactive_shell)
    line_number = cond_command->line - function_line_number;

  /* Run the debug trap before each conditional command, but do it after we
     update the line number information. */
  if (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
    run_debug_trap ();

#if 0
  debug_print_cond_command (cond_command);
#endif
  last_command_exit_value = result = execute_cond_node (cond_command);  
  return (result);
}
#endif /* COND_COMMAND */

static void
bind_lastarg (arg)
     char *arg;
{
  SHELL_VAR *var;

  if (arg == 0)
    arg = "";
  var = bind_variable ("_", arg);
  VUNSETATTR (var, att_exported);
}

/* Execute a null command.  Fork a subshell if the command uses pipes or is
   to be run asynchronously.  This handles all the side effects that are
   supposed to take place. */
static int
execute_null_command (redirects, pipe_in, pipe_out, async, old_last_command_subst_pid)
     REDIRECT *redirects;
     int pipe_in, pipe_out, async;
     pid_t old_last_command_subst_pid;
{
  if (pipe_in != NO_PIPE || pipe_out != NO_PIPE || async)
    {
      /* We have a null command, but we really want a subshell to take
     care of it.  Just fork, do piping and redirections, and exit. */
      if (make_child ((char *)NULL, async) == 0)
    {
      /* Cancel traps, in trap.c. */
      restore_original_signals ();      /* XXX */

      do_piping (pipe_in, pipe_out);

      subshell_environment = SUBSHELL_ASYNC;

      if (do_redirections (redirects, 1, 0, 0) == 0)
        exit (EXECUTION_SUCCESS);
      else
        exit (EXECUTION_FAILURE);
    }
      else
    {
      close_pipes (pipe_in, pipe_out);
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
      unlink_fifo_list ();
#endif
      return (EXECUTION_SUCCESS);
    }
    }
  else
    {
      /* Even if there aren't any command names, pretend to do the
     redirections that are specified.  The user expects the side
     effects to take place.  If the redirections fail, then return
     failure.  Otherwise, if a command substitution took place while
     expanding the command or a redirection, return the value of that
     substitution.  Otherwise, return EXECUTION_SUCCESS. */

      if (do_redirections (redirects, 0, 0, 0) != 0)
    return (EXECUTION_FAILURE);
      else if (old_last_command_subst_pid != last_command_subst_pid)
    return (last_command_exit_value);
      else
    return (EXECUTION_SUCCESS);
    }
}

/* This is a hack to suppress word splitting for assignment statements
   given as arguments to builtins with the ASSIGNMENT_BUILTIN flag set. */
static void
fix_assignment_words (words)
     WORD_LIST *words;
{
  WORD_LIST *w;
  struct builtin *b;

  if (words == 0)
    return;

  b = 0;

  for (w = words; w; w = w->next)
    if (w->word->flags & W_ASSIGNMENT)
      {
    if (b == 0)
      {
        b = builtin_address_internal (words->word->word, 0);
        if (b == 0 || (b->flags & ASSIGNMENT_BUILTIN) == 0)
          return;
      }
    w->word->flags |= (W_NOSPLIT|W_NOGLOB|W_TILDEEXP);
      }
}

/* The meaty part of all the executions.  We have to start hacking the
   real execution of commands here.  Fork a process, set things up,
   execute the command. */
static int
execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
     SIMPLE_COM *simple_command;
     int pipe_in, pipe_out, async;
     struct fd_bitmap *fds_to_close;
{
  WORD_LIST *words, *lastword;
  char *command_line, *lastarg, *temp;
  int first_word_quoted, result, builtin_is_special, already_forked, dofork;
  pid_t old_last_command_subst_pid, old_last_async_pid;
  sh_builtin_func_t *builtin;
  SHELL_VAR *func;

  result = EXECUTION_SUCCESS;
  special_builtin_failed = builtin_is_special = 0;
  command_line = (char *)0;

  /* If we're in a function, update the line number information. */
  if (variable_context && interactive_shell)
    line_number = simple_command->line - function_line_number;

  /* Run the debug trap before each simple command, but do it after we
     update the line number information. */
  if (signal_is_trapped (DEBUG_TRAP) && signal_is_ignored (DEBUG_TRAP) == 0)
    run_debug_trap ();

  /* Remember what this command line looks like at invocation. */
  command_string_index = 0;
  print_simple_command (simple_command);

  first_word_quoted =
    simple_command->words ? (simple_command->words->word->flags & W_QUOTED): 0;

  old_last_command_subst_pid = last_command_subst_pid;
  old_last_async_pid = last_asynchronous_pid;

  already_forked = dofork = 0;

  /* If we're in a pipeline or run in the background, set DOFORK so we
     make the child early, before word expansion.  This keeps assignment
     statements from affecting the parent shell's environment when they
     should not. */
  dofork = pipe_in != NO_PIPE || pipe_out != NO_PIPE || async;

  /* Something like `%2 &' should restart job 2 in the background, not cause
     the shell to fork here. */
  if (dofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE &&
    simple_command->words && simple_command->words->word &&
    simple_command->words->word->word &&
    (simple_command->words->word->word[0] == '%'))
    dofork = 0;

  if (dofork)
    {
      /* XXX memory leak if expand_words() error causes a jump_to_top_level */
      command_line = savestring (the_printed_command);

      /* Do this now, because execute_disk_command will do it anyway in the
     vast majority of cases. */
      maybe_make_export_env ();

      if (make_child (command_line, async) == 0)
    {
      already_forked = 1;
      simple_command->flags |= CMD_NO_FORK;

      subshell_environment = (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
                    ? (SUBSHELL_PIPE|SUBSHELL_FORK)
                    : (SUBSHELL_ASYNC|SUBSHELL_FORK);

      /* We need to do this before piping to handle some really
         pathological cases where one of the pipe file descriptors
         is < 2. */
      if (fds_to_close)
        close_fd_bitmap (fds_to_close);

      do_piping (pipe_in, pipe_out);
      pipe_in = pipe_out = NO_PIPE;

      last_asynchronous_pid = old_last_async_pid;
    }
      else
    {
      close_pipes (pipe_in, pipe_out);
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
      unlink_fifo_list ();
#endif
      command_line = (char *)NULL;      /* don't free this. */
      bind_lastarg ((char *)NULL);
      return (result);
    }
    }

  /* If we are re-running this as the result of executing the `command'
     builtin, do not expand the command words a second time. */
  if ((simple_command->flags & CMD_INHIBIT_EXPANSION) == 0)
    {
      current_fds_to_close = fds_to_close;
      fix_assignment_words (simple_command->words);
      words = expand_words (simple_command->words);
      current_fds_to_close = (struct fd_bitmap *)NULL;
    }
  else
    words = copy_word_list (simple_command->words);

  /* It is possible for WORDS not to have anything left in it.
     Perhaps all the words consisted of `$foo', and there was
     no variable `$foo'. */
  if (words == 0)
    {
      result = execute_null_command (simple_command->redirects,
                     pipe_in, pipe_out,
                     already_forked ? 0 : async,
                     old_last_command_subst_pid);
      if (already_forked)
    exit (result);
      else
    {
      bind_lastarg ((char *)NULL);
      set_pipestatus_from_exit (result);
      return (result);
    }
    }

  lastarg = (char *)NULL;

  begin_unwind_frame ("simple-command");

  if (echo_command_at_execute)
    xtrace_print_word_list (words);

  builtin = (sh_builtin_func_t *)NULL;
  func = (SHELL_VAR *)NULL;
  if ((simple_command->flags & CMD_NO_FUNCTIONS) == 0)
    {
      /* Posix.2 says special builtins are found before functions.  We
     don't set builtin_is_special anywhere other than here, because
     this path is followed only when the `command' builtin is *not*
     being used, and we don't want to exit the shell if a special
     builtin executed with `command builtin' fails.  `command' is not
     a special builtin. */
      if (posixly_correct)
    {
      builtin = find_special_builtin (words->word->word);
      if (builtin)
        builtin_is_special = 1;
    }
      if (builtin == 0)
    func = find_function (words->word->word);
    }

  add_unwind_protect (dispose_words, words);
  QUIT;

  /* Bind the last word in this command to "$_" after execution. */
  for (lastword = words; lastword->next; lastword = lastword->next)
    ;
  lastarg = lastword->word->word;

#if defined (JOB_CONTROL)
  /* Is this command a job control related thing? */
  if (words->word->word[0] == '%' && already_forked == 0)
    {
      this_command_name = async ? "bg" : "fg";
      last_shell_builtin = this_shell_builtin;
      this_shell_builtin = builtin_address (this_command_name);
      result = (*this_shell_builtin) (words);
      goto return_result;
    }

  /* One other possiblilty.  The user may want to resume an existing job.
     If they do, find out whether this word is a candidate for a running
     job. */
  if (job_control && already_forked == 0 && async == 0 &&
    !first_word_quoted &&
    !words->next &&
    words->word->word[0] &&
    !simple_command->redirects &&
    pipe_in == NO_PIPE &&
    pipe_out == NO_PIPE &&
    (temp = get_string_value ("auto_resume")))
    {
      int job, jflags, started_status;

      jflags = JM_STOPPED|JM_FIRSTMATCH;
      if (STREQ (temp, "exact"))
    jflags |= JM_EXACT;
      else if (STREQ (temp, "substring"))
    jflags |= JM_SUBSTRING;
      else
    jflags |= JM_PREFIX;
      job = get_job_by_name (words->word->word, jflags);
      if (job != NO_JOB)
    {
      run_unwind_frame ("simple-command");
      this_command_name = "fg";
      last_shell_builtin = this_shell_builtin;
      this_shell_builtin = builtin_address ("fg");

      started_status = start_job (job, 1);
      return ((started_status < 0) ? EXECUTION_FAILURE : started_status);
    }
    }
#endif /* JOB_CONTROL */

  /* Remember the name of this command globally. */
  this_command_name = words->word->word;

  QUIT;

  /* This command could be a shell builtin or a user-defined function.
     We have already found special builtins by this time, so we do not
     set builtin_is_special.  If this is a function or builtin, and we
     have pipes, then fork a subshell in here.  Otherwise, just execute
     the command directly. */
  if (func == 0 && builtin == 0)
    builtin = find_shell_builtin (this_command_name);

  last_shell_builtin = this_shell_builtin;
  this_shell_builtin = builtin;

  if (builtin || func)
    {
      if (already_forked)
    {
      /* reset_terminating_signals (); */   /* XXX */
      /* Cancel traps, in trap.c. */
      restore_original_signals ();

      if (async)
        {
          if ((simple_command->flags & CMD_STDIN_REDIR) &&
            pipe_in == NO_PIPE &&
            (stdin_redirects (simple_command->redirects) == 0))
        async_redirect_stdin ();
          setup_async_signals ();
        }

      execute_subshell_builtin_or_function
        (words, simple_command->redirects, builtin, func,
         pipe_in, pipe_out, async, fds_to_close,
         simple_command->flags);
    }
      else
    {
      result = execute_builtin_or_function
        (words, builtin, func, simple_command->redirects, fds_to_close,
         simple_command->flags);
      if (builtin)
        {
          if (result > EX_SHERRBASE)
        {
          result = builtin_status (result);
          if (builtin_is_special)
            special_builtin_failed = 1;
        }
          /* In POSIX mode, if there are assignment statements preceding
         a special builtin, they persist after the builtin
         completes. */
          if (posixly_correct && builtin_is_special && temporary_env)
        merge_temporary_env ();
        }
      else      /* function */
        {
          if (result == EX_USAGE)
        result = EX_BADUSAGE;
          else if (result > EX_SHERRBASE)
        result = EXECUTION_FAILURE;
        }

      set_pipestatus_from_exit (result);

      goto return_result;
    }
    }

  if (command_line == 0)
    command_line = savestring (the_printed_command);

  execute_disk_command (words, simple_command->redirects, command_line,
            pipe_in, pipe_out, async, fds_to_close,
            simple_command->flags);

 return_result:
  bind_lastarg (lastarg);
  FREE (command_line);
  run_unwind_frame ("simple-command");
  return (result);
}

/* Translate the special builtin exit statuses.  We don't really need a
   function for this; it's a placeholder for future work. */
static int
builtin_status (result)
     int result;
{
  int r;

  switch (result)
    {
    case EX_USAGE:
      r = EX_BADUSAGE;
      break;
    case EX_REDIRFAIL:
    case EX_BADSYNTAX:
    case EX_BADASSIGN:
    case EX_EXPFAIL:
      r = EXECUTION_FAILURE;
      break;
    default:
      r = EXECUTION_SUCCESS;
      break;
    }
  return (r);
}

static int
execute_builtin (builtin, words, flags, subshell)
     sh_builtin_func_t *builtin;
     WORD_LIST *words;
     int flags, subshell;
{
  int old_e_flag, result, eval_unwind;
  int isbltinenv;

  old_e_flag = exit_immediately_on_error;
  /* The eval builtin calls parse_and_execute, which does not know about
     the setting of flags, and always calls the execution functions with
     flags that will exit the shell on an error if -e is set.  If the
     eval builtin is being called, and we're supposed to ignore the exit
     value of the command, we turn the -e flag off ourselves, then
     restore it when the command completes. */
  if (subshell == 0 && builtin == eval_builtin && (flags & CMD_IGNORE_RETURN))
    {
      begin_unwind_frame ("eval_builtin");
      unwind_protect_int (exit_immediately_on_error);
      exit_immediately_on_error = 0;
      eval_unwind = 1;
    }
  else
    eval_unwind = 0;

  /* The temporary environment for a builtin is supposed to apply to
     all commands executed by that builtin.  Currently, this is a
     problem only with the `source' and `eval' builtins. */
  isbltinenv = (builtin == source_builtin || builtin == eval_builtin);
  if (isbltinenv)
    {
      if (subshell == 0)
    begin_unwind_frame ("builtin_env");

      if (temporary_env)
    {
      push_scope (VC_BLTNENV, temporary_env);
      if (subshell == 0)
        add_unwind_protect (pop_scope, "1");
          temporary_env = (HASH_TABLE *)NULL;     
    }
    }

  /* `return' does a longjmp() back to a saved environment in execute_function.
     If a variable assignment list preceded the command, and the shell is
     running in POSIX mode, we need to merge that into the shell_variables
     table, since `return' is a POSIX special builtin. */
  if (posixly_correct && subshell == 0 && builtin == return_builtin && temporary_env)
    {
      begin_unwind_frame ("return_temp_env");
      add_unwind_protect (merge_temporary_env, (char *)NULL);
    }

  result = ((*builtin) (words->next));

  /* This shouldn't happen, but in case `return' comes back instead of
     longjmp'ing, we need to unwind. */
  if (posixly_correct && subshell == 0 && builtin == return_builtin && temporary_env)
    discard_unwind_frame ("return_temp_env");

  if (subshell == 0 && isbltinenv)
    run_unwind_frame ("builtin_env");

  if (eval_unwind)
    {
      exit_immediately_on_error += old_e_flag;
      discard_unwind_frame ("eval_builtin");
    }

  return (result);
}

static int
execute_function (var, words, flags, fds_to_close, async, subshell)
     SHELL_VAR *var;
     WORD_LIST *words;
     int flags;
     struct fd_bitmap *fds_to_close;
     int async, subshell;
{
  int return_val, result;
  COMMAND *tc, *fc;
  char *debug_trap, *error_trap;

  USE_VAR(fc);

  tc = (COMMAND *)copy_command (function_cell (var));
  if (tc && (flags & CMD_IGNORE_RETURN))
    tc->flags |= CMD_IGNORE_RETURN;

  if (subshell == 0)
    {
      begin_unwind_frame ("function_calling");
      push_context (var->name, subshell, temporary_env);
      add_unwind_protect (pop_context, (char *)NULL);
      unwind_protect_int (line_number);
      unwind_protect_int (return_catch_flag);
      unwind_protect_jmp_buf (return_catch);
      add_unwind_protect (dispose_command, (char *)tc);
      unwind_protect_pointer (this_shell_function);
      unwind_protect_int (loop_level);
    }
  else
    push_context (var->name, subshell, temporary_env);  /* don't unwind-protect for subshells */

  temporary_env = (HASH_TABLE *)NULL;

  this_shell_function = var;
  make_funcname_visible (1);

  debug_trap = TRAP_STRING(DEBUG_TRAP);
  error_trap = TRAP_STRING(ERROR_TRAP);
  
  /* The order of the unwind protects for debug_trap and error_trap is
     important here!  unwind-protect commands are run in reverse order
     of registration.  If this causes problems, take out the xfree
     unwind-protect calls and live with the small memory leak. */
  if (debug_trap && (trace_p (var) == 0))
    {
      if (subshell == 0)
    {
      debug_trap = savestring (debug_trap);
      add_unwind_protect (xfree, debug_trap);
      add_unwind_protect (set_debug_trap, debug_trap);
    }
      restore_default_signal (DEBUG_TRAP);
    }

  if (error_trap)
    {
      if (subshell == 0)
    {
      error_trap = savestring (error_trap);
      add_unwind_protect (xfree, error_trap);
      add_unwind_protect (set_error_trap, error_trap);
    }
      restore_default_signal (ERROR_TRAP);
    }

  /* The temporary environment for a function is supposed to apply to
     all commands executed within the function body. */

  remember_args (words->next, 1);

  /* Number of the line on which the function body starts. */
  if (interactive_shell)
    line_number = function_line_number = tc->line;

  if (subshell)
    {
#if defined (JOB_CONTROL)
      stop_pipeline (async, (COMMAND *)NULL);
#endif
      fc = (tc->type == cm_group) ? tc->value.Group->command : tc;

      if (fc && (flags & CMD_IGNORE_RETURN))
    fc->flags |= CMD_IGNORE_RETURN;
    }
  else
    fc = tc;

  return_catch_flag++;
  return_val = setjmp (return_catch);

  if (return_val)
    result = return_catch_value;
  else
    result = execute_command_internal (fc, 0, NO_PIPE, NO_PIPE, fds_to_close);

  if (subshell == 0)
    run_unwind_frame ("function_calling");

  if (variable_context == 0 || this_shell_function == 0)
    make_funcname_visible (0);

  return (result);
}

/* A convenience routine for use by other parts of the shell to execute
   a particular shell function. */
int
execute_shell_function (var, words)
     SHELL_VAR *var;
     WORD_LIST *words;
{
  int ret;
  struct fd_bitmap *bitmap;

  bitmap = new_fd_bitmap (FD_BITMAP_DEFAULT_SIZE);
  begin_unwind_frame ("execute-shell-function");
  add_unwind_protect (dispose_fd_bitmap, (char *)bitmap);
      
  ret = execute_function (var, words, 0, bitmap, 0, 0);

  dispose_fd_bitmap (bitmap);
  discard_unwind_frame ("execute-shell-function");

  return ret;
}

/* Execute a shell builtin or function in a subshell environment.  This
   routine does not return; it only calls exit().  If BUILTIN is non-null,
   it points to a function to call to execute a shell builtin; otherwise
   VAR points at the body of a function to execute.  WORDS is the arguments
   to the command, REDIRECTS specifies redirections to perform before the
   command is executed. */
static void
execute_subshell_builtin_or_function (words, redirects, builtin, var,
                      pipe_in, pipe_out, async, fds_to_close,
                      flags)
     WORD_LIST *words;
     REDIRECT *redirects;
     sh_builtin_func_t *builtin;
     SHELL_VAR *var;
     int pipe_in, pipe_out, async;
     struct fd_bitmap *fds_to_close;
     int flags;
{
  int result, r;
#if defined (JOB_CONTROL)
  int jobs_hack;

  jobs_hack = (builtin == jobs_builtin) &&
        ((subshell_environment & SUBSHELL_ASYNC) == 0 || pipe_out != NO_PIPE);
#endif

  /* A subshell is neither a login shell nor interactive. */
  login_shell = interactive = 0;

  subshell_environment = SUBSHELL_ASYNC;

  maybe_make_export_env (); /* XXX - is this needed? */

#if defined (JOB_CONTROL)
  /* Eradicate all traces of job control after we fork the subshell, so
     all jobs begun by this subshell are in the same process group as
     the shell itself. */

  /* Allow the output of `jobs' to be piped. */
  if (jobs_hack)
    kill_current_pipeline ();
  else
    without_job_control ();

  set_sigchld_handler ();
#endif /* JOB_CONTROL */

  set_sigint_handler ();

  if (fds_to_close)
    close_fd_bitmap (fds_to_close);

  do_piping (pipe_in, pipe_out);

  if (do_redirections (redirects, 1, 0, 0) != 0)
    exit (EXECUTION_FAILURE);

  if (builtin)
    {
      /* Give builtins a place to jump back to on failure,
     so we don't go back up to main(). */
      result = setjmp (top_level);

      if (result == EXITPROG)
    exit (last_command_exit_value);
      else if (result)
    exit (EXECUTION_FAILURE);
      else
    {
      r = execute_builtin (builtin, words, flags, 1);
      if (r == EX_USAGE)
        r = EX_BADUSAGE;
      exit (r);
    }
    }
  else
    exit (execute_function (var, words, flags, fds_to_close, async, 1));
}

/* Execute a builtin or function in the current shell context.  If BUILTIN
   is non-null, it is the builtin command to execute, otherwise VAR points
   to the body of a function.  WORDS are the command's arguments, REDIRECTS
   are the redirections to perform.  FDS_TO_CLOSE is the usual bitmap of
   file descriptors to close.

   If BUILTIN is exec_builtin, the redirections specified in REDIRECTS are
   not undone before this function returns. */
static int
execute_builtin_or_function (words, builtin, var, redirects,
                 fds_to_close, flags)
     WORD_LIST *words;
     sh_builtin_func_t *builtin;
     SHELL_VAR *var;
     REDIRECT *redirects;
     struct fd_bitmap *fds_to_close;
     int flags;
{
  int result;
  REDIRECT *saved_undo_list;
  sh_builtin_func_t *saved_this_shell_builtin;

  if (do_redirections (redirects, 1, 1, 0) != 0)
    {
      cleanup_redirects (redirection_undo_list);
      redirection_undo_list = (REDIRECT *)NULL;
      dispose_exec_redirects ();
      return (EX_REDIRFAIL);    /* was EXECUTION_FAILURE */
    }

  saved_this_shell_builtin = this_shell_builtin;
  saved_undo_list = redirection_undo_list;

  /* Calling the "exec" builtin changes redirections forever. */
  if (builtin == exec_builtin)
    {
      dispose_redirects (saved_undo_list);
      saved_undo_list = exec_redirection_undo_list;
      exec_redirection_undo_list = (REDIRECT *)NULL;
    }
  else
    dispose_exec_redirects ();

  if (saved_undo_list)
    {
      begin_unwind_frame ("saved redirects");
      add_unwind_protect (cleanup_redirects, (char *)saved_undo_list);
    }

  redirection_undo_list = (REDIRECT *)NULL;

  if (builtin)
    result = execute_builtin (builtin, words, flags, 0);
  else
    result = execute_function (var, words, flags, fds_to_close, 0, 0);

  /* If we are executing the `command' builtin, but this_shell_builtin is
     set to `exec_builtin', we know that we have something like
     `command exec [redirection]', since otherwise `exec' would have
     overwritten the shell and we wouldn't get here.  In this case, we
     want to behave as if the `command' builtin had not been specified
     and preserve the redirections. */
  if (builtin == command_builtin && this_shell_builtin == exec_builtin)
    {
      if (saved_undo_list)
    dispose_redirects (saved_undo_list);
      redirection_undo_list = exec_redirection_undo_list;
      saved_undo_list = exec_redirection_undo_list = (REDIRECT *)NULL;      
      discard_unwind_frame ("saved_redirects");
    }

  if (saved_undo_list)
    {
      redirection_undo_list = saved_undo_list;
      discard_unwind_frame ("saved redirects");
    }

  if (redirection_undo_list)
    {
      cleanup_redirects (redirection_undo_list);
      redirection_undo_list = (REDIRECT *)NULL;
    }

  return (result);
}

void
setup_async_signals ()
{
#if defined (__BEOS__)
  set_signal_handler (SIGHUP, SIG_IGN); /* they want csh-like behavior */
#endif

#if defined (JOB_CONTROL)
  if (job_control == 0)
#endif
    {
      set_signal_handler (SIGINT, SIG_IGN);
      set_signal_ignored (SIGINT);
      set_signal_handler (SIGQUIT, SIG_IGN);
      set_signal_ignored (SIGQUIT);
    }
}

/* Execute a simple command that is hopefully defined in a disk file
   somewhere.

   1) fork ()
   2) connect pipes
   3) look up the command
   4) do redirections
   5) execve ()
   6) If the execve failed, see if the file has executable mode set.
   If so, and it isn't a directory, then execute its contents as
   a shell script.

   Note that the filename hashing stuff has to take place up here,
   in the parent.  This is probably why the Bourne style shells
   don't handle it, since that would require them to go through
   this gnarly hair, for no good reason.  */
static void
execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
              async, fds_to_close, cmdflags)
     WORD_LIST *words;
     REDIRECT *redirects;
     char *command_line;
     int pipe_in, pipe_out, async;
     struct fd_bitmap *fds_to_close;
     int cmdflags;
{
  char *pathname, *command, **args;
  int nofork;
  pid_t pid;

  nofork = (cmdflags & CMD_NO_FORK);  /* Don't fork, just exec, if no pipes */
  pathname = words->word->word;

#if defined (RESTRICTED_SHELL)
  if (restricted && xstrchr (pathname, '/'))
    {
      internal_error ("%s: restricted: cannot specify `/' in command names",
            pathname);
      last_command_exit_value = EXECUTION_FAILURE;
      return;
    }
#endif /* RESTRICTED_SHELL */

  command = search_for_command (pathname);

  if (command)
    {
      maybe_make_export_env ();
      put_command_name_into_env (command);
    }

  /* We have to make the child before we check for the non-existence
     of COMMAND, since we want the error messages to be redirected. */
  /* If we can get away without forking and there are no pipes to deal with,
     don't bother to fork, just directly exec the command. */
  if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE)
    pid = 0;
  else
    pid = make_child (savestring (command_line), async);

  if (pid == 0)
    {
      int old_interactive;

#if 0
      /* This has been disabled for the time being. */
#if !defined (ARG_MAX) || ARG_MAX >= 10240
      if (posixly_correct == 0)
    put_gnu_argv_flags_into_env ((long)getpid (), glob_argv_flags);
#endif
#endif

      /* Cancel traps, in trap.c. */
      restore_original_signals ();

      /* restore_original_signals may have undone the work done
     by make_child to ensure that SIGINT and SIGQUIT are ignored
     in asynchronous children. */
      if (async)
    {
      if ((cmdflags & CMD_STDIN_REDIR) &&
        pipe_in == NO_PIPE &&
        (stdin_redirects (redirects) == 0))
        async_redirect_stdin ();
      setup_async_signals ();
    }

      /* This functionality is now provided by close-on-exec of the
     file descriptors manipulated by redirection and piping.
     Some file descriptors still need to be closed in all children
     because of the way bash does pipes; fds_to_close is a
     bitmap of all such file descriptors. */
      if (fds_to_close)
    close_fd_bitmap (fds_to_close);

      do_piping (pipe_in, pipe_out);

      old_interactive = interactive;
      if (async)
    interactive = 0;

      subshell_environment = SUBSHELL_FORK;

      if (redirects && (do_redirections (redirects, 1, 0, 0) != 0))
    {
#if defined (PROCESS_SUBSTITUTION)
      /* Try to remove named pipes that may have been created as the
         result of redirections. */
      unlink_fifo_list ();
#endif /* PROCESS_SUBSTITUTION */
      exit (EXECUTION_FAILURE);
    }

      if (async)
    interactive = old_interactive;

      if (command == 0)
    {
      internal_error ("%s: command not found", pathname);
      exit (EX_NOTFOUND);   /* Posix.2 says the exit status is 127 */
    }

      /* Execve expects the command name to be in args[0].  So we
     leave it there, in the same format that the user used to
     type it in. */
      args = strvec_from_word_list (words, 0, 0, (int *)NULL);
      exit (shell_execve (command, args, export_env));
    }
  else
    {
      /* Make sure that the pipes are closed in the parent. */
      close_pipes (pipe_in, pipe_out);
#if defined (PROCESS_SUBSTITUTION) && defined (HAVE_DEV_FD)
      unlink_fifo_list ();
#endif
      FREE (command);
    }
}

/* CPP defines to decide whether a particular index into the #! line
   corresponds to a valid interpreter name or argument character, or
   whitespace.  The MSDOS define is to allow \r to be treated the same
   as \n. */

#if !defined (MSDOS)
#  define STRINGCHAR(ind) \
    (ind < sample_len && !whitespace (sample[ind]) && sample[ind] != '\n')
#  define WHITECHAR(ind) \
    (ind < sample_len && whitespace (sample[ind]))
#else   /* MSDOS */
#  define STRINGCHAR(ind) \
    (ind < sample_len && !whitespace (sample[ind]) && sample[ind] != '\n' && sample[ind] != '\r')
#  define WHITECHAR(ind) \
    (ind < sample_len && whitespace (sample[ind]))
#endif  /* MSDOS */

static char *
getinterp (sample, sample_len, endp)
     char *sample;
     int sample_len, *endp;
{
  register int i;
  char *execname;
  int start;

  /* Find the name of the interpreter to exec. */
  for (i = 2; i < sample_len && whitespace (sample[i]); i++)
    ;

  for (start = i; STRINGCHAR(i); i++)
    ;

  execname = substring (sample, start, i);

  if (endp)
    *endp = i;
  return execname;
}

#if !defined (HAVE_HASH_BANG_EXEC)
/* If the operating system on which we're running does not handle
   the #! executable format, then help out.  SAMPLE is the text read
   from the file, SAMPLE_LEN characters.  COMMAND is the name of
   the script; it and ARGS, the arguments given by the user, will
   become arguments to the specified interpreter.  ENV is the environment
   to pass to the interpreter.

   The word immediately following the #! is the interpreter to execute.
   A single argument to the interpreter is allowed. */

static int
execute_shell_script (sample, sample_len, command, args, env)
     char *sample;
     int sample_len;
     char *command;
     char **args, **env;
{
  char *execname, *firstarg;
  int i, start, size_increment, larry;

  /* Find the name of the interpreter to exec. */
  execname = getinterp (sample, sample_len, &i);
  size_increment = 1;

  /* Now the argument, if any. */
  for (firstarg = (char *)NULL, start = i; WHITECHAR(i); i++)
    ;

  /* If there is more text on the line, then it is an argument for the
     interpreter. */

  if (STRINGCHAR(i))  
    {
      for (start = i; STRINGCHAR(i); i++)
    ;
      firstarg = substring ((char *)sample, start, i);
      size_increment = 2;
    }

  larry = strvec_len (args) + size_increment;
  args = strvec_resize (args, larry + 1);

  for (i = larry - 1; i; i--)
    args[i] = args[i - size_increment];

  args[0] = execname;
  if (firstarg)
    {
      args[1] = firstarg;
      args[2] = command;
    }
  else
    args[1] = command;

  args[larry] = (char *)NULL;

  return (shell_execve (execname, args, env));
}
#undef STRINGCHAR
#undef WHITECHAR

#endif /* !HAVE_HASH_BANG_EXEC */

static void
initialize_subshell ()
{
#if defined (ALIAS)
  /* Forget about any aliases that we knew of.  We are in a subshell. */
  delete_all_aliases ();
#endif /* ALIAS */

#if defined (HISTORY)
  /* Forget about the history lines we have read.  This is a non-interactive
     subshell. */
  history_lines_this_session = 0;
#endif

#if defined (JOB_CONTROL)
  /* Forget about the way job control was working. We are in a subshell. */
  without_job_control ();
  set_sigchld_handler ();
#endif /* JOB_CONTROL */

  /* Reset the values of the shell flags and options. */
  reset_shell_flags ();
  reset_shell_options ();
  reset_shopt_options ();

  /* Zero out builtin_env, since this could be a shell script run from a
     sourced file with a temporary environment supplied to the `source/.'
     builtin.  Such variables are not supposed to be exported (empirical
     testing with sh and ksh).  Just throw it away; don't worry about a
     memory leak. */
  if (vc_isbltnenv (shell_variables))
    shell_variables = shell_variables->down;

  clear_unwind_protect_list (0);

  /* We're no longer inside a shell function. */
  variable_context = return_catch_flag = 0;

  /* If we're not interactive, close the file descriptor from which we're
     reading the current shell script. */
  if (interactive_shell == 0)
    unset_bash_input (1);
}

#if defined (HAVE_SETOSTYPE) && defined (_POSIX_SOURCE)
#  define SETOSTYPE(x)  __setostype(x)
#else
#  define SETOSTYPE(x)
#endif

#define READ_SAMPLE_BUF(file, buf, len) \
  do \
    { \
      fd = open(file, O_RDONLY); \
      if (fd >= 0) \
    { \
      len = read (fd, buf, 80); \
      close (fd); \
    } \
      else \
    len = -1; \
    } \
  while (0)
      
/* Call execve (), handling interpreting shell scripts, and handling
   exec failures. */
int
shell_execve (command, args, env)
     char *command;
     char **args, **env;
{
  struct stat finfo;
  int larray, i, fd;
  char sample[80];
  int sample_len;

  SETOSTYPE (0);        /* Some systems use for USG/POSIX semantics */
  execve (command, args, env);
  i = errno;            /* error from execve() */
  SETOSTYPE (1);

  /* If we get to this point, then start checking out the file.
     Maybe it is something we can hack ourselves. */
  if (i != ENOEXEC)
    {
      if ((stat (command, &finfo) == 0) && (S_ISDIR (finfo.st_mode)))
    internal_error ("%s: is a directory", command);
      else if (executable_file (command) == 0)
    {
      errno = i;
      file_error (command);
    }
      else
    {
      /* The file has the execute bits set, but the kernel refuses to
         run it for some reason.  See why. */
#if defined (HAVE_HASH_BANG_EXEC)
      READ_SAMPLE_BUF (command, sample, sample_len);
      if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
        {
          char *interp;

          interp = getinterp (sample, sample_len, (int *)NULL);
          errno = i;
          sys_error ("%s: %s: bad interpreter", command, interp ? interp : "");
          FREE (interp);
          return (EX_NOEXEC);
        }
#endif
      errno = i;
      file_error (command);
    }
      return ((i == ENOENT) ? EX_NOTFOUND : EX_NOEXEC); /* XXX Posix.2 says that exit status is 126 */
    }

  /* This file is executable.
     If it begins with #!, then help out people with losing operating
     systems.  Otherwise, check to see if it is a binary file by seeing
     if the contents of the first line (or up to 80 characters) are in the
     ASCII set.  If it's a text file, execute the contents as shell commands,
     otherwise return 126 (EX_BINARY_FILE). */
  READ_SAMPLE_BUF (command, sample, sample_len);

  if (sample_len == 0)
    return (EXECUTION_SUCCESS);

  /* Is this supposed to be an executable script?
     If so, the format of the line is "#! interpreter [argument]".
     A single argument is allowed.  The BSD kernel restricts
     the length of the entire line to 32 characters (32 bytes
     being the size of the BSD exec header), but we allow 80
     characters. */
  if (sample_len > 0)
    {
#if !defined (HAVE_HASH_BANG_EXEC)
      if (sample_len > 2 && sample[0] == '#' && sample[1] == '!')
    return (execute_shell_script (sample, sample_len, command, args, env));
      else
#endif
      if (check_binary_file (sample, sample_len))
    {
      internal_error ("%s: cannot execute binary file", command);
      return (EX_BINARY_FILE);
    }
    }

  /* We have committed to attempting to execute the contents of this file
     as shell commands. */

  initialize_subshell ();

  set_sigint_handler ();

  /* Insert the name of this shell into the argument list. */
  larray = strvec_len (args) + 1;
  args = strvec_resize (args, larray + 1);

  for (i = larray - 1; i; i--)
    args[i] = args[i - 1];

  args[0] = shell_name;
  args[1] = command;
  args[larray] = (char *)NULL;

  if (args[0][0] == '-')
    args[0]++;

#if defined (RESTRICTED_SHELL)
  if (restricted)
    change_flag ('r', FLAG_OFF);
#endif

  if (subshell_argv)
    {
      /* Can't free subshell_argv[0]; that is shell_name. */
      for (i = 1; i < subshell_argc; i++)
    free (subshell_argv[i]);
      free (subshell_argv);
    }

  dispose_command (currently_executing_command);    /* XXX */
  currently_executing_command = (COMMAND *)NULL;

  subshell_argc = larray;
  subshell_argv = args;
  subshell_envp = env;

  unbind_args ();   /* remove the positional parameters */

  longjmp (subshell_top_level, 1);
  /*NOTREACHED*/
}

static int
execute_intern_function (name, function)
     WORD_DESC *name;
     COMMAND *function;
{
  SHELL_VAR *var;

  if (check_identifier (name, posixly_correct) == 0)
    {
      if (posixly_correct && interactive_shell == 0)
    {
      last_command_exit_value = EX_USAGE;
      jump_to_top_level (EXITPROG);
    }
      return (EXECUTION_FAILURE);
    }

  var = find_function (name->word);
  if (var && (readonly_p (var) || noassign_p (var)))
    {
      if (readonly_p (var))
    internal_error ("%s: readonly function", var->name);
      return (EXECUTION_FAILURE);
    }

  bind_function (name->word, function);
  return (EXECUTION_SUCCESS);
}

#if defined (INCLUDE_UNUSED)
#if defined (PROCESS_SUBSTITUTION)
void
close_all_files ()
{
  register int i, fd_table_size;

  fd_table_size = getdtablesize ();
  if (fd_table_size > 256)  /* clamp to a reasonable value */
    fd_table_size = 256;

  for (i = 3; i < fd_table_size; i++)
    close (i);
}
#endif /* PROCESS_SUBSTITUTION */
#endif

static void
close_pipes (in, out)
     int in, out;
{
  if (in >= 0)
    close (in);
  if (out >= 0)
    close (out);
}

/* Redirect input and output to be from and to the specified pipes.
   NO_PIPE and REDIRECT_BOTH are handled correctly. */
static void
do_piping (pipe_in, pipe_out)
     int pipe_in, pipe_out;
{
  if (pipe_in != NO_PIPE)
    {
      if (dup2 (pipe_in, 0) < 0)
    sys_error ("cannot duplicate fd %d to fd 0", pipe_in);
      if (pipe_in > 0)
    close (pipe_in);
    }
  if (pipe_out != NO_PIPE)
    {
      if (pipe_out != REDIRECT_BOTH)
    {
      if (dup2 (pipe_out, 1) < 0)
        sys_error ("cannot duplicate fd %d to fd 1", pipe_out);
      if (pipe_out == 0 || pipe_out > 1)
        close (pipe_out);
    }
      else
    {
      if (dup2 (1, 2) < 0)
        sys_error ("cannot duplicate fd 1 to fd 2");
    }
    }
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/patches/locale.patch

File: ../git/live-bootstrap/sysa/bash-2.05b/patches/locale.patch used in: Step 1052
SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>

SPDX-License-Identifier: GPL-2.0-or-later

mes libc + setting locale = not worky.

--- locale.c    2021-01-15 09:38:55.729307629 +1100
+++ locale.c    2021-01-15 11:19:01.929391346 +1100
@@ -190,7 +190,7 @@
 set_lang (var, value)
      char *var, *value;
 {
-  return ((lc_all == 0 || *lc_all == 0) ? setlocale (LC_ALL, value?value:"") != NULL : 0);
+  return 0;
 }
 
 /* Get the value of one of the locale variables (LC_MESSAGES, LC_CTYPE) */

https://git.savannah.gnu.org/cgit/bash.git/tree/locale.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/locale.c used in: Step 1052, Step 1240
/* locale.c - Miscellaneous internationalization functions. */

/* Copyright (C) 1996 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include "bashtypes.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "bashintl.h"
#include "bashansi.h"
#include <stdio.h>
#include "chartypes.h"

#include "shell.h"
#include "input.h"  /* For bash_input */

extern int dump_translatable_strings, dump_po_strings;

/* The current locale when the program begins */
static char *default_locale;

/* The current domain for textdomain(3). */
static char *default_domain;
static char *default_dir;

/* tracks the value of LC_ALL; used to override values for other locale
   categories */
static char *lc_all;

/* Set the value of default_locale and make the current locale the
   system default locale.  This should be called very early in main(). */
void
set_default_locale ()
{
#if defined (HAVE_SETLOCALE)
  default_locale = setlocale (LC_ALL, "");
  if (default_locale)
    default_locale = savestring (default_locale);
#endif /* HAVE_SETLOCALE */
}

/* Set default values for LC_CTYPE, LC_COLLATE, and LC_MESSAGES if they
   are not specified in the environment, but LANG or LC_ALL is.  This
   should be called from main() after parsing the environment. */
void
set_default_locale_vars ()
{
  char *val;

#if defined (HAVE_SETLOCALE)
  val = get_string_value ("LC_CTYPE");
  if (val == 0 && lc_all && *lc_all)
    setlocale (LC_CTYPE, lc_all);

#  if defined (LC_COLLATE)
  val = get_string_value ("LC_COLLATE");
  if (val == 0 && lc_all && *lc_all)
    setlocale (LC_COLLATE, lc_all);
#  endif /* LC_COLLATE */

#  if defined (LC_MESSAGES)
  val = get_string_value ("LC_MESSAGES");
  if (val == 0 && lc_all && *lc_all)
    setlocale (LC_MESSAGES, lc_all);
#  endif /* LC_MESSAGES */

#  if defined (LC_NUMERIC)
  val = get_string_value ("LC_NUMERIC");
  if (val == 0 && lc_all && *lc_all)
    setlocale (LC_NUMERIC, lc_all);
#  endif /* LC_NUMERIC */

#endif /* HAVE_SETLOCALE */

  val = get_string_value ("TEXTDOMAIN");
  if (val && *val)
    {
      FREE (default_domain);
      default_domain = savestring (val);
      textdomain (default_domain);
    }

  val = get_string_value ("TEXTDOMAINDIR");
  if (val && *val)
    {
      FREE (default_dir);
      default_dir = savestring (val);
      bindtextdomain (default_domain, default_dir);
    }
}

/* Set one of the locale categories (specified by VAR) to VALUE.  Returns 1
  if successful, 0 otherwise. */
int
set_locale_var (var, value)
     char *var, *value;
{
  if (var[0] == 'T' && var[10] == 0)        /* TEXTDOMAIN */
    {
      FREE (default_domain);
      default_domain = value ? savestring (value) : (char *)NULL;
      textdomain (default_domain);
      return (1);
    }
  else if (var[0] == 'T')           /* TEXTDOMAINDIR */
    {
      FREE (default_dir);
      default_dir = value ? savestring (value) : (char *)NULL;
      bindtextdomain (default_domain, default_dir);
      return (1);
    }

  /* var[0] == 'L' && var[1] == 'C' && var[2] == '_' */

  else if (var[3] == 'A')           /* LC_ALL */
    {
      FREE (lc_all);
      if (value)
    lc_all = savestring (value);
      else if (default_locale)
    lc_all = savestring (default_locale);
      else
    {
      lc_all = (char *)xmalloc (1);
      lc_all[0] = '\0';
    }
#if defined (HAVE_SETLOCALE)
      return (setlocale (LC_ALL, lc_all) != 0);
#else
      return (1);
#endif
    }

#if defined (HAVE_SETLOCALE)
  else if (var[3] == 'C' && var[4] == 'T')  /* LC_CTYPE */
    {
      if (lc_all == 0 || *lc_all == '\0')
    return (setlocale (LC_CTYPE, value ? value : "") != 0);
    }
  else if (var[3] == 'C' && var[4] == 'O')  /* LC_COLLATE */
    {
#  if defined (LC_COLLATE)
      if (lc_all == 0 || *lc_all == '\0')
    return (setlocale (LC_COLLATE, value ? value : "") != 0);
#  endif /* LC_COLLATE */
    }
  else if (var[3] == 'M' && var[4] == 'E')  /* LC_MESSAGES */
    {
#  if defined (LC_MESSAGES)
      if (lc_all == 0 || *lc_all == '\0')
    return (setlocale (LC_MESSAGES, value ? value : "") != 0);
#  endif /* LC_MESSAGES */
    }
  else if (var[3] == 'N' && var[4] == 'U')  /* LC_NUMERIC */
    {
#  if defined (LC_NUMERIC)
      if (lc_all == 0 || *lc_all == '\0')
    return (setlocale (LC_NUMERIC, value ? value : "") != 0);
#  endif /* LC_NUMERIC */
    }
#endif /* HAVE_SETLOCALE */

  return (0);
}

/* Called when LANG is assigned a value.  Sets LC_ALL category with
   setlocale(3) if that has not already been set.  Doesn't change any
   shell variables. */
int
set_lang (var, value)
     char *var, *value;
{
  return ((lc_all == 0 || *lc_all == 0) ? setlocale (LC_ALL, value?value:"") != NULL : 0);
}

/* Get the value of one of the locale variables (LC_MESSAGES, LC_CTYPE) */
char *
get_locale_var (var)
     char *var;
{
  char *locale;

  locale = lc_all;

  if (locale == 0)
    locale = get_string_value (var);
  if (locale == 0)
    locale = default_locale;

  return (locale);
}

/* Translate the contents of STRING, a $"..." quoted string, according
   to the current locale.  In the `C' or `POSIX' locale, or if gettext()
   is not available, the passed string is returned unchanged.  The
   length of the translated string is returned in LENP, if non-null. */
char *
localetrans (string, len, lenp)
     char *string;
     int len, *lenp;
{
  char *locale, *t;
#if defined (HAVE_GETTEXT)
  char *translated;
  int tlen;
#endif

  /* Don't try to translate null strings. */
  if (string == 0 || *string == 0)
    {
      if (lenp)
    *lenp = 0;
      return ((char *)NULL);
    }

  locale = get_locale_var ("LC_MESSAGES");

  /* If we don't have setlocale() or the current locale is `C' or `POSIX',
     just return the string.  If we don't have gettext(), there's no use
     doing anything else. */
#if defined (HAVE_GETTEXT)
  if (locale == 0 || locale[0] == '\0' ||
      (locale[0] == 'C' && locale[1] == '\0') || STREQ (locale, "POSIX"))
#endif
    {
      t = (char *)xmalloc (len + 1);
      strcpy (t, string);
      if (lenp)
    *lenp = len;
      return (t);
    }

#if defined (HAVE_GETTEXT)
  /* Now try to translate it. */
  translated = gettext (string);
  if (translated == string) /* gettext returns its argument if untranslatable */
    {
      t = (char *)xmalloc (len + 1);
      strcpy (t, string);
      if (lenp)
    *lenp = len;
    }
  else
    {
      tlen = strlen (translated);
      t = (char *)xmalloc (tlen + 1);
      strcpy (t, translated);
      if (lenp)
    *lenp = tlen;
    }
  return (t);
#endif /* HAVE_GETTEXT */
}

/* Change a bash string into a string suitable for inclusion in a `po' file.
   This backslash-escapes `"' and `\' and changes newlines into \\\n"\n". */
char *
mk_msgstr (string, foundnlp)
     char *string;
     int *foundnlp;
{
  register int c, len;
  char *result, *r, *s;

  for (len = 0, s = string; s && *s; s++)
    {
      len++;
      if (*s == '"' || *s == '\\')
    len++;
      else if (*s == '\n')
    len += 5;
    }
  
  r = result = (char *)xmalloc (len + 3);
  *r++ = '"';

  for (s = string; s && (c = *s); s++)
    {
      if (c == '\n')    /* <NL> -> \n"<NL>" */
    {
      *r++ = '\\';
      *r++ = 'n';
      *r++ = '"';
      *r++ = '\n';
      *r++ = '"';
      if (foundnlp)
        *foundnlp = 1;
      continue;
    }
      if (c == '"' || c == '\\')
    *r++ = '\\';
      *r++ = c;
    }

  *r++ = '"';
  *r++ = '\0';

  return result;
}

/* $"..." -- Translate the portion of STRING between START and END
   according to current locale using gettext (if available) and return
   the result.  The caller will take care of leaving the quotes intact.
   The string will be left without the leading `$' by the caller.
   If translation is performed, the translated string will be double-quoted
   by the caller.  The length of the translated string is returned in LENP,
   if non-null. */
char *
localeexpand (string, start, end, lineno, lenp)
     char *string;
     int start, end, lineno, *lenp;
{
  int len, tlen, foundnl;
  char *temp, *t, *t2;

  temp = (char *)xmalloc (end - start + 1);
  for (tlen = 0, len = start; len < end; )
    temp[tlen++] = string[len++];
  temp[tlen] = '\0';

  /* If we're just dumping translatable strings, don't do anything with the
     string itself, but if we're dumping in `po' file format, convert it into a form more palatable to gettext(3)
     and friends by quoting `"' and `\' with backslashes and converting <NL>
     into `\n"<NL>"'.  If we find a newline in TEMP, we first output a
     `msgid ""' line and then the translated string; otherwise we output the
     `msgid' and translated string all on one line. */
  if (dump_translatable_strings)
    {
      if (dump_po_strings)
    {
      foundnl = 0;
      t = mk_msgstr (temp, &foundnl);
      t2 = foundnl ? "\"\"\n" : "";

      printf ("#: %s:%d\nmsgid %s%s\nmsgstr \"\"\n",
            yy_input_name (), lineno, t2, t);
      free (t);
    }
      else
    printf ("\"%s\"\n", temp);

      if (lenp)
    *lenp = tlen;
      return (temp);
    }
  else if (*temp)
    {
      t = localetrans (temp, tlen, &len);
      free (temp);
      if (lenp)
    *lenp = len;
      return (t);
    }
  else
    {
      if (lenp)
    *lenp = 0;
      return (temp);
    }
}

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/patches/dev-tty.patch

File: ../git/live-bootstrap/sysa/bash-2.05b/patches/dev-tty.patch used in: Step 1053
SPDX-FileCopyrightText: 2021 fosslinux <fosslinux@aussies.space>

SPDX-License-Identifier: GPL-2.0-or-later

We do not have /dev at this stage of the bootstrap, including /dev/tty. For
some reason, bash has a fixation on /dev/tty, even though we are not
interactive. Removing this check entirely fixes this issue.

diff --color -ru shell.c
--- shell.c 2002-07-02 01:27:11.000000000 +1000
+++ shell.c 2021-01-16 11:23:36.407287955 +1100
@@ -342,8 +342,6 @@
 #  endif
 #endif
 
-  check_dev_tty ();
-
 #ifdef __CYGWIN__
   _cygwin32_check_tmp ();

https://git.savannah.gnu.org/cgit/bash.git/tree/shell.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/shell.c used in: Step 1053, Step 1202
/* shell.c -- GNU's idea of the POSIX shell specification. */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

  Birthdate:
  Sunday, January 10th, 1988.
  Initial author: Brian Fox
*/
#define INSTALL_DEBUG_MODE

#include "config.h"

#include "bashtypes.h"
#ifndef _MINIX
#  include <sys/file.h>
#endif
#include "posixstat.h"
#include "posixtime.h"
#include "bashansi.h"
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include "filecntl.h"
#include <pwd.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#define NEED_SH_SETLINEBUF_DECL     /* used in externs.h */

#include "shell.h"
#include "flags.h"
#include "trap.h"
#include "mailcheck.h"
#include "builtins.h"
#include "builtins/common.h"

#if defined (JOB_CONTROL)
#include "jobs.h"
#endif /* JOB_CONTROL */

#include "input.h"
#include "execute_cmd.h"
#include "findcmd.h"

#if defined (USING_BASH_MALLOC) && defined (DEBUG) && !defined (DISABLE_MALLOC_WRAPPERS)
#  include <malloc/shmalloc.h>
#endif

#if defined (HISTORY)
#  include "bashhist.h"
#  include <readline/history.h>
#endif

#include <tilde/tilde.h>
#include <glob/strmatch.h>

#if defined (__OPENNT)
#  include <opennt/opennt.h>
#endif

#if !defined (HAVE_GETPW_DECLS)
extern struct passwd *getpwuid ();
#endif /* !HAVE_GETPW_DECLS */

#if !defined (errno)
extern int errno;
#endif

#if defined (NO_MAIN_ENV_ARG)
extern char **environ;  /* used if no third argument to main() */
#endif

extern char *dist_version, *release_status;
extern int patch_level, build_version;
extern int shell_level;
extern int subshell_environment;
extern int last_command_exit_value;
extern int line_number;
extern char *primary_prompt, *secondary_prompt;
extern int expand_aliases;
extern char *this_command_name;
extern int array_needs_making;

/* Non-zero means that this shell has already been run; i.e. you should
   call shell_reinitialize () if you need to start afresh. */
int shell_initialized = 0;

COMMAND *global_command = (COMMAND *)NULL;

/* Information about the current user. */
struct user_info current_user =
{
  (uid_t)-1, (uid_t)-1, (gid_t)-1, (gid_t)-1,
  (char *)NULL, (char *)NULL, (char *)NULL
};

/* The current host's name. */
char *current_host_name = (char *)NULL;

/* Non-zero means that this shell is a login shell.
   Specifically:
   0 = not login shell.
   1 = login shell from getty (or equivalent fake out)
  -1 = login shell from "--login" (or -l) flag.
  -2 = both from getty, and from flag.
 */
int login_shell = 0;

/* Non-zero means that at this moment, the shell is interactive.  In
   general, this means that the shell is at this moment reading input
   from the keyboard. */
int interactive = 0;

/* Non-zero means that the shell was started as an interactive shell. */
int interactive_shell = 0;

/* Non-zero means to send a SIGHUP to all jobs when an interactive login
   shell exits. */
int hup_on_exit = 0;

/* Tells what state the shell was in when it started:
    0 = non-interactive shell script
    1 = interactive
    2 = -c command
    3 = wordexp evaluation
   This is a superset of the information provided by interactive_shell.
*/
int startup_state = 0;

/* Special debugging helper. */
int debugging_login_shell = 0;

/* The environment that the shell passes to other commands. */
char **shell_environment;

/* Non-zero when we are executing a top-level command. */
int executing = 0;

/* The number of commands executed so far. */
int current_command_number = 1;

/* Non-zero is the recursion depth for commands. */
int indirection_level = 0;

/* The name of this shell, as taken from argv[0]. */
char *shell_name = (char *)NULL;

/* time in seconds when the shell was started */
time_t shell_start_time;

/* Are we running in an emacs shell window? */
int running_under_emacs;

/* The name of the .(shell)rc file. */
static char *bashrc_file = "~/.bashrc";

/* Non-zero means to act more like the Bourne shell on startup. */
static int act_like_sh;

/* Non-zero if this shell is being run by `su'. */
static int su_shell;

/* Non-zero if we have already expanded and sourced $ENV. */
static int sourced_env;

/* Is this shell running setuid? */
static int running_setuid;

/* Values for the long-winded argument names. */
static int debugging;           /* Do debugging things. */
static int no_rc;           /* Don't execute ~/.bashrc */
static int no_profile;          /* Don't execute .profile */
static int do_version;          /* Display interesting version info. */
static int make_login_shell;        /* Make this shell be a `-bash' shell. */
static int want_initial_help;       /* --help option */

int no_line_editing = 0;    /* Don't do fancy line editing. */
int posixly_correct = 0;    /* Non-zero means posix.2 superset. */
int dump_translatable_strings;  /* Dump strings in $"...", don't execute. */
int dump_po_strings;        /* Dump strings in $"..." in po format */
int wordexp_only = 0;       /* Do word expansion only */

/* Some long-winded argument names.  These are obviously new. */
#define Int 1
#define Charp 2
struct {
  char *name;
  int type;
  int *int_value;
  char **char_value;
} long_args[] = {
  { "debug", Int, &debugging, (char **)0x0 },
  { "dump-po-strings", Int, &dump_po_strings, (char **)0x0 },
  { "dump-strings", Int, &dump_translatable_strings, (char **)0x0 },
  { "help", Int, &want_initial_help, (char **)0x0 },
  { "init-file", Charp, (int *)0x0, &bashrc_file },
  { "login", Int, &make_login_shell, (char **)0x0 },
  { "noediting", Int, &no_line_editing, (char **)0x0 },
  { "noprofile", Int, &no_profile, (char **)0x0 },
  { "norc", Int, &no_rc, (char **)0x0 },
  { "posix", Int, &posixly_correct, (char **)0x0 },
  { "rcfile", Charp, (int *)0x0, &bashrc_file },
#if defined (RESTRICTED_SHELL)
  { "restricted", Int, &restricted, (char **)0x0 },
#endif
  { "verbose", Int, &echo_input_at_read, (char **)0x0 },
  { "version", Int, &do_version, (char **)0x0 },
  { "wordexp", Int, &wordexp_only, (char **)0x0 },
  { (char *)0x0, Int, (int *)0x0, (char **)0x0 }
};

/* These are extern so execute_simple_command can set them, and then
   longjmp back to main to execute a shell script, instead of calling
   main () again and resulting in indefinite, possibly fatal, stack
   growth. */
procenv_t subshell_top_level;
int subshell_argc;
char **subshell_argv;
char **subshell_envp;

#if defined (BUFFERED_INPUT)
/* The file descriptor from which the shell is reading input. */
int default_buffered_input = -1;
#endif

/* The following two variables are not static so they can show up in $-. */
int read_from_stdin;        /* -s flag supplied */
int want_pending_command;   /* -c flag supplied */

int malloc_trace_at_exit = 0;

static int shell_reinitialized = 0;
static char *local_pending_command;

static FILE *default_input;

static STRING_INT_ALIST *shopt_alist;
static int shopt_ind = 0, shopt_len = 0;

static int parse_long_options __P((char **, int, int));
static int parse_shell_options __P((char **, int, int));
static int bind_args __P((char **, int, int, int));

static void add_shopt_to_alist __P((char *, int));
static void run_shopt_alist __P((void));

static void execute_env_file __P((char *));
static void run_startup_files __P((void));
static int open_shell_script __P((char *));
static void set_bash_input __P((void));
static int run_one_command __P((char *));
static int run_wordexp __P((char *));

static int uidget __P((void));

static void init_interactive __P((void));
static void init_noninteractive __P((void));

static void set_shell_name __P((char *));
static void shell_initialize __P((void));
static void shell_reinitialize __P((void));

static void show_shell_usage __P((FILE *, int));

#ifdef __CYGWIN__
static void
_cygwin32_check_tmp ()
{
  struct stat sb;

  if (stat ("/tmp", &sb) < 0)
    internal_warning ("could not find /tmp, please create!");
  else
    {
      if (S_ISDIR (sb.st_mode) == 0)
    internal_warning ("/tmp must be a valid directory name");
    }
}
#endif /* __CYGWIN__ */

#if defined (NO_MAIN_ENV_ARG)
/* systems without third argument to main() */
int
main (argc, argv)
     int argc;
     char **argv;
#else /* !NO_MAIN_ENV_ARG */
int
main (argc, argv, env)
     int argc;
     char **argv, **env;
#endif /* !NO_MAIN_ENV_ARG */
{
  register int i;
  int code, old_errexit_flag;
#if defined (RESTRICTED_SHELL)
  int saverst;
#endif
  volatile int locally_skip_execution;
  volatile int arg_index, top_level_arg_index;
#ifdef __OPENNT
  char **env;

  env = environ;
#endif /* __OPENNT */

  USE_VAR(argc);
  USE_VAR(argv);
  USE_VAR(env);
  USE_VAR(code);
  USE_VAR(old_errexit_flag);
#if defined (RESTRICTED_SHELL)
  USE_VAR(saverst);
#endif

  /* Catch early SIGINTs. */
  code = setjmp (top_level);
  if (code)
    exit (2);

#if defined (USING_BASH_MALLOC) && defined (DEBUG) && !defined (DISABLE_MALLOC_WRAPPERS)
#  if 1
  malloc_set_register (1);
#  endif
#endif

  check_dev_tty ();

#ifdef __CYGWIN__
  _cygwin32_check_tmp ();
#endif /* __CYGWIN__ */

  /* Wait forever if we are debugging a login shell. */
  while (debugging_login_shell);

  set_default_locale ();

  running_setuid = uidget ();

  if (getenv ("POSIXLY_CORRECT") || getenv ("POSIX_PEDANTIC"))
    posixly_correct = 1;

#if defined (USE_GNU_MALLOC_LIBRARY)
  mcheck (programming_error, (void (*) ())0);
#endif /* USE_GNU_MALLOC_LIBRARY */

  if (setjmp (subshell_top_level))
    {
      argc = subshell_argc;
      argv = subshell_argv;
      env = subshell_envp;
      sourced_env = 0;
    }

  shell_reinitialized = 0;

  /* Initialize `local' variables for all `invocations' of main (). */
  arg_index = 1;
  local_pending_command = (char *)NULL;
  want_pending_command = locally_skip_execution = read_from_stdin = 0;
  default_input = stdin;
#if defined (BUFFERED_INPUT)
  default_buffered_input = -1;
#endif

  /* Fix for the `infinite process creation' bug when running shell scripts
     from startup files on System V. */
  login_shell = make_login_shell = 0;

  /* If this shell has already been run, then reinitialize it to a
     vanilla state. */
  if (shell_initialized || shell_name)
    {
      /* Make sure that we do not infinitely recurse as a login shell. */
      if (*shell_name == '-')
    shell_name++;

      shell_reinitialize ();
      if (setjmp (top_level))
    exit (2);
    }

  shell_environment = env;
  set_shell_name (argv[0]);
  shell_start_time = NOW;   /* NOW now defined in general.h */

  /* Parse argument flags from the input line. */

  /* Find full word arguments first. */
  arg_index = parse_long_options (argv, arg_index, argc);

  if (want_initial_help)
    {
      show_shell_usage (stdout, 1);
      exit (EXECUTION_SUCCESS);
    }

  if (do_version)
    {
      show_shell_version (1);
      exit (EXECUTION_SUCCESS);
    }

  /* All done with full word options; do standard shell option parsing.*/
  this_command_name = shell_name;   /* for error reporting */
  arg_index = parse_shell_options (argv, arg_index, argc);

  /* If user supplied the "--login" (or -l) flag, then set and invert
     LOGIN_SHELL. */
  if (make_login_shell)
    {
      login_shell++;
      login_shell = -login_shell;
    }

  set_login_shell (login_shell != 0);

  if (dump_po_strings)
    dump_translatable_strings = 1;

  if (dump_translatable_strings)
    read_but_dont_execute = 1;

  if (running_setuid && privileged_mode == 0)
    disable_priv_mode ();

  /* Need to get the argument to a -c option processed in the
     above loop.  The next arg is a command to execute, and the
     following args are $0...$n respectively. */
  if (want_pending_command)
    {
      local_pending_command = argv[arg_index];
      if (local_pending_command == 0)
    {
      report_error ("-c: option requires an argument");
      exit (EX_USAGE);
    }
      arg_index++;
    }
  this_command_name = (char *)NULL;

  cmd_init();       /* initialize the command object caches */

  /* First, let the outside world know about our interactive status.
     A shell is interactive if the `-i' flag was given, or if all of
     the following conditions are met:
    no -c command
    no arguments remaining or the -s flag given
    standard input is a terminal
    standard output is a terminal
     Refer to Posix.2, the description of the `sh' utility. */

  if (forced_interactive ||     /* -i flag */
      (!local_pending_command &&    /* No -c command and ... */
       wordexp_only == 0 &&     /* No --wordexp and ... */
       ((arg_index == argc) ||      /*   no remaining args or... */
    read_from_stdin) &&     /*   -s flag with args, and */
       isatty (fileno (stdin)) &&   /* Input is a terminal and */
       isatty (fileno (stdout))))   /* output is a terminal. */
    init_interactive ();
  else
    init_noninteractive ();

#define CLOSE_FDS_AT_LOGIN
#if defined (CLOSE_FDS_AT_LOGIN)
  /*
   * Some systems have the bad habit of starting login shells with lots of open
   * file descriptors.  For instance, most systems that have picked up the
   * pre-4.0 Sun YP code leave a file descriptor open each time you call one
   * of the getpw* functions, and it's set to be open across execs.  That
   * means one for login, one for xterm, one for shelltool, etc.
   */
  if (login_shell && interactive_shell)
    {
      for (i = 3; i < 20; i++)
    close (i);
    }
#endif /* CLOSE_FDS_AT_LOGIN */

  /* If we're in a strict Posix.2 mode, turn on interactive comments,
     alias expansion in non-interactive shells, and other Posix.2 things. */
  if (posixly_correct)
    {
      bind_variable ("POSIXLY_CORRECT", "y");
      sv_strict_posix ("POSIXLY_CORRECT");
    }

  /* Now we run the shopt_alist and process the options. */
  if (shopt_alist)
    run_shopt_alist ();

  /* From here on in, the shell must be a normal functioning shell.
     Variables from the environment are expected to be set, etc. */
  shell_initialize ();

  set_default_locale_vars ();

  if (interactive_shell)
    {
      char *term;

      term = getenv ("TERM");
      no_line_editing |= term && (STREQ (term, "emacs"));
      term = getenv ("EMACS");
      running_under_emacs = term ? ((strmatch ("*term*", term, 0) == 0) ? 2 : 1)
                 : 0;
      no_line_editing |= term && term[0] == 't' && term[1] == '\0';
    }

  top_level_arg_index = arg_index;
  old_errexit_flag = exit_immediately_on_error;

  /* Give this shell a place to longjmp to before executing the
     startup files.  This allows users to press C-c to abort the
     lengthy startup. */
  code = setjmp (top_level);
  if (code)
    {
      if (code == EXITPROG)
    exit_shell (last_command_exit_value);
      else
    {
#if defined (JOB_CONTROL)
      /* Reset job control, since run_startup_files turned it off. */
      set_job_control (interactive_shell);
#endif
      /* Reset value of `set -e', since it's turned off before running
         the startup files. */
      exit_immediately_on_error += old_errexit_flag;
      locally_skip_execution++;
    }
    }

  arg_index = top_level_arg_index;

  /* Execute the start-up scripts. */

  if (interactive_shell == 0)
    {
      unbind_variable ("PS1");
      unbind_variable ("PS2");
      interactive = 0;
#if 0
      /* This has already been done by init_noninteractive */
      expand_aliases = posixly_correct;
#endif
    }
  else
    {
      change_flag ('i', FLAG_ON);
      interactive = 1;
    }

#if defined (RESTRICTED_SHELL)
  /* Set restricted_shell based on whether the basename of $0 indicates that
     the shell should be restricted or if the `-r' option was supplied at
     startup. */
  restricted_shell = shell_is_restricted (shell_name);

  /* If the `-r' option is supplied at invocation, make sure that the shell
     is not in restricted mode when running the startup files. */
  saverst = restricted;
  restricted = 0;
#endif

  /* The startup files are run with `set -e' temporarily disabled. */
  if (locally_skip_execution == 0 && running_setuid == 0)
    {
      old_errexit_flag = exit_immediately_on_error;
      exit_immediately_on_error = 0;

      run_startup_files ();
      exit_immediately_on_error += old_errexit_flag;
    }

  /* If we are invoked as `sh', turn on Posix mode. */
  if (act_like_sh)
    {
      bind_variable ("POSIXLY_CORRECT", "y");
      sv_strict_posix ("POSIXLY_CORRECT");
    }

#if defined (RESTRICTED_SHELL)
  /* Turn on the restrictions after executing the startup files.  This
     means that `bash -r' or `set -r' invoked from a startup file will
     turn on the restrictions after the startup files are executed. */
  restricted = saverst || restricted;
  if (shell_reinitialized == 0)
    maybe_make_restricted (shell_name);
#endif /* RESTRICTED_SHELL */

  if (wordexp_only)
    {
      startup_state = 3;
      last_command_exit_value = run_wordexp (argv[arg_index]);
      exit_shell (last_command_exit_value);
    }

  if (local_pending_command)
    {
      arg_index = bind_args (argv, arg_index, argc, 0);
      startup_state = 2;
#if defined (ONESHOT)
      executing = 1;
      run_one_command (local_pending_command);
      exit_shell (last_command_exit_value);
#else /* ONESHOT */
      with_input_from_string (local_pending_command, "-c");
      goto read_and_execute;
#endif /* !ONESHOT */
    }

  /* Get possible input filename and set up default_buffered_input or
     default_input as appropriate. */
  if (arg_index != argc && read_from_stdin == 0)
    {
      open_shell_script (argv[arg_index]);
      arg_index++;
    }
  else if (interactive == 0)
    /* In this mode, bash is reading a script from stdin, which is a
       pipe or redirected file. */
#if defined (BUFFERED_INPUT)
    default_buffered_input = fileno (stdin);    /* == 0 */
#else
    setbuf (default_input, (char *)NULL);
#endif /* !BUFFERED_INPUT */

  set_bash_input ();

  /* Bind remaining args to $1 ... $n */
  arg_index = bind_args (argv, arg_index, argc, 1);
  /* Do the things that should be done only for interactive shells. */
  if (interactive_shell)
    {
      /* Set up for checking for presence of mail. */
      remember_mail_dates ();
      reset_mail_timer ();

#if defined (HISTORY)
      /* Initialize the interactive history stuff. */
      bash_initialize_history ();
      /* Don't load the history from the history file if we've already
     saved some lines in this session (e.g., by putting `history -s xx'
     into one of the startup files). */
      if (shell_initialized == 0 && history_lines_this_session == 0)
    load_history ();
#endif /* HISTORY */

      /* Initialize terminal state for interactive shells after the
     .bash_profile and .bashrc are interpreted. */
      get_tty_state ();
    }

#if !defined (ONESHOT)
 read_and_execute:
#endif /* !ONESHOT */

  shell_initialized = 1;

  /* Read commands until exit condition. */
  reader_loop ();
  exit_shell (last_command_exit_value);
}

static int
parse_long_options (argv, arg_start, arg_end)
     char **argv;
     int arg_start, arg_end;
{
  int arg_index, longarg, i;
  char *arg_string;

  arg_index = arg_start;
  while ((arg_index != arg_end) && (arg_string = argv[arg_index]) &&
     (*arg_string == '-'))
    {
      longarg = 0;

      /* Make --login equivalent to -login. */
      if (arg_string[1] == '-' && arg_string[2])
    {
      longarg = 1;
      arg_string++;
    }

      for (i = 0; long_args[i].name; i++)
    {
      if (STREQ (arg_string + 1, long_args[i].name))
        {
          if (long_args[i].type == Int)
        *long_args[i].int_value = 1;
          else if (argv[++arg_index] == 0)
        {
          report_error ("%s: option requires an argument", long_args[i].name);
          exit (EX_USAGE);
        }
          else
        *long_args[i].char_value = argv[arg_index];

          break;
        }
    }
      if (long_args[i].name == 0)
    {
      if (longarg)
        {
          report_error ("%s: invalid option", argv[arg_index]);
          show_shell_usage (stderr, 0);
          exit (EX_USAGE);
        }
      break;        /* No such argument.  Maybe flag arg. */
    }

      arg_index++;
    }

  return (arg_index);
}

static int
parse_shell_options (argv, arg_start, arg_end)
     char **argv;
     int arg_start, arg_end;
{
  int arg_index;
  int arg_character, on_or_off, next_arg, i;
  char *o_option, *arg_string;

  arg_index = arg_start;
  while (arg_index != arg_end && (arg_string = argv[arg_index]) &&
     (*arg_string == '-' || *arg_string == '+'))
    {
      /* There are flag arguments, so parse them. */
      next_arg = arg_index + 1;

      /* A single `-' signals the end of options.  From the 4.3 BSD sh.
     An option `--' means the same thing; this is the standard
     getopt(3) meaning. */
      if (arg_string[0] == '-' &&
       (arg_string[1] == '\0' ||
         (arg_string[1] == '-' && arg_string[2] == '\0')))
    return (next_arg);

      i = 1;
      on_or_off = arg_string[0];
      while (arg_character = arg_string[i++])
    {
      switch (arg_character)
        {
        case 'c':
          want_pending_command = 1;
          break;

        case 'l':
          make_login_shell = 1;
          break;

        case 's':
          read_from_stdin = 1;
          break;

        case 'o':
          o_option = argv[next_arg];
          if (o_option == 0)
        {
          list_minus_o_opts (-1, (on_or_off == '-') ? 0 : 1);
          break;
        }
          if (set_minus_o_option (on_or_off, o_option) != EXECUTION_SUCCESS)
        exit (EX_USAGE);
          next_arg++;
          break;

        case 'O':
          /* Since some of these can be overridden by the normal
         interactive/non-interactive shell initialization or
         initializing posix mode, we save the options and process
         them after initialization. */
          o_option = argv[next_arg];
          if (o_option == 0)
        {
          shopt_listopt (o_option, (on_or_off == '-') ? 0 : 1);
          break;
        }
          add_shopt_to_alist (o_option, on_or_off);
          next_arg++;
          break;

        case 'D':
          dump_translatable_strings = 1;
          break;

        default:
          if (change_flag (arg_character, on_or_off) == FLAG_ERROR)
        {
          report_error ("%c%c: invalid option", on_or_off, arg_character);
          show_shell_usage (stderr, 0);
          exit (EX_USAGE);
        }
        }
    }
      /* Can't do just a simple increment anymore -- what about
     "bash -abouo emacs ignoreeof -hP"? */
      arg_index = next_arg;
    }

  return (arg_index);
}

/* Exit the shell with status S. */
void
exit_shell (s)
     int s;
{
  /* Do trap[0] if defined.  Allow it to override the exit status
     passed to us. */
  if (signal_is_trapped (0))
    s = run_exit_trap ();

#if defined (PROCESS_SUBSTITUTION)
  unlink_fifo_list ();
#endif /* PROCESS_SUBSTITUTION */

#if defined (HISTORY)
  if (interactive_shell)
    maybe_save_shell_history ();
#endif /* HISTORY */

#if defined (JOB_CONTROL)
  /* If the user has run `shopt -s huponexit', hangup all jobs when we exit
     an interactive login shell.  ksh does this unconditionally. */
  if (interactive_shell && login_shell && hup_on_exit)
    hangup_all_jobs ();

  /* If this shell is interactive, terminate all stopped jobs and
     restore the original terminal process group.  Don't do this if we're
     in a subshell and calling exit_shell after, for example, a failed
     word expansion. */
  if (subshell_environment == 0)
    end_job_control ();
#endif /* JOB_CONTROL */

  /* Always return the exit status of the last command to our parent. */
  sh_exit (s);
}

/* A wrapper for exit that (optionally) can do other things, like malloc
   statistics tracing. */
void
sh_exit (s)
     int s;
{
#if defined (MALLOC_DEBUG) && defined (USING_BASH_MALLOC)
  if (malloc_trace_at_exit)
    trace_malloc_stats (get_name_for_error (), (char *)NULL);
#endif

  exit (s);
}

/* Source the bash startup files.  If POSIXLY_CORRECT is non-zero, we obey
   the Posix.2 startup file rules:  $ENV is expanded, and if the file it
   names exists, that file is sourced.  The Posix.2 rules are in effect
   for interactive shells only. (section 4.56.5.3) */

/* Execute ~/.bashrc for most shells.  Never execute it if
   ACT_LIKE_SH is set, or if NO_RC is set.

   If the executable file "/usr/gnu/src/bash/foo" contains:

   #!/usr/gnu/bin/bash
   echo hello

   then:

     COMMAND        EXECUTE BASHRC
     --------------------------------
     bash -c foo        NO
     bash foo       NO
     foo            NO
     rsh machine ls     YES (for rsh, which calls `bash -c')
     rsh machine foo    YES (for shell started by rsh) NO (for foo!)
     echo ls | bash     NO
     login          NO
     bash           YES
*/

static void
execute_env_file (env_file)
      char *env_file;
{
  char *fn;

  if (env_file && *env_file)
    {
      fn = expand_string_unsplit_to_string (env_file, Q_DOUBLE_QUOTES);
      if (fn && *fn)
    maybe_execute_file (fn, 1);
      FREE (fn);
    }
}

static void
run_startup_files ()
{
#if defined (JOB_CONTROL)
  int old_job_control;
#endif
  int sourced_login, run_by_ssh;

  /* get the rshd/sshd case out of the way first. */
  if (interactive_shell == 0 && no_rc == 0 && login_shell == 0 &&
      act_like_sh == 0 && local_pending_command)
    {
#ifdef SSH_SOURCE_BASHRC
      run_by_ssh = (find_variable ("SSH_CLIENT") != (SHELL_VAR *)0) ||
           (find_variable ("SSH2_CLIENT") != (SHELL_VAR *)0);
#else
      run_by_ssh = 0;
#endif

      /* If we were run by sshd or we think we were run by rshd, execute
     ~/.bashrc if we are a top-level shell. */
      if ((run_by_ssh || isnetconn (fileno (stdin))) && shell_level < 2)
    {
#ifdef SYS_BASHRC
#  if defined (__OPENNT)
      maybe_execute_file (_prefixInstallPath(SYS_BASHRC, NULL, 0), 1);
#  else
      maybe_execute_file (SYS_BASHRC, 1);
#  endif
#endif
      maybe_execute_file (bashrc_file, 1);
      return;
    }
    }

#if defined (JOB_CONTROL)
  /* Startup files should be run without job control enabled. */
  old_job_control = interactive_shell ? set_job_control (0) : 0;
#endif

  sourced_login = 0;

  /* A shell begun with the --login (or -l) flag that is not in posix mode
     runs the login shell startup files, no matter whether or not it is
     interactive.  If NON_INTERACTIVE_LOGIN_SHELLS is defined, run the
     startup files if argv[0][0] == '-' as well. */
#if defined (NON_INTERACTIVE_LOGIN_SHELLS)
  if (login_shell && posixly_correct == 0)
#else
  if (login_shell < 0 && posixly_correct == 0)
#endif
    {
      /* We don't execute .bashrc for login shells. */
      no_rc++;

      /* Execute /etc/profile and one of the personal login shell
     initialization files. */
      if (no_profile == 0)
    {
      maybe_execute_file (SYS_PROFILE, 1);

      if (act_like_sh)  /* sh */
        maybe_execute_file ("~/.profile", 1);
      else if ((maybe_execute_file ("~/.bash_profile", 1) == 0) &&
           (maybe_execute_file ("~/.bash_login", 1) == 0))  /* bash */
        maybe_execute_file ("~/.profile", 1);
    }

      sourced_login = 1;
    }

  /* A non-interactive shell not named `sh' and not in posix mode reads and
     executes commands from $BASH_ENV.  If `su' starts a shell with `-c cmd'
     and `-su' as the name of the shell, we want to read the startup files.
     No other non-interactive shells read any startup files. */
  if (interactive_shell == 0 && !(su_shell && login_shell))
    {
      if (posixly_correct == 0 && act_like_sh == 0 && privileged_mode == 0 &&
        sourced_env++ == 0)
    execute_env_file (get_string_value ("BASH_ENV"));
      return;
    }

  /* Interactive shell or `-su' shell. */
  if (posixly_correct == 0)       /* bash, sh */
    {
      if (login_shell && sourced_login++ == 0)
    {
      /* We don't execute .bashrc for login shells. */
      no_rc++;

      /* Execute /etc/profile and one of the personal login shell
         initialization files. */
      if (no_profile == 0)
        {
          maybe_execute_file (SYS_PROFILE, 1);

          if (act_like_sh)  /* sh */
        maybe_execute_file ("~/.profile", 1);
          else if ((maybe_execute_file ("~/.bash_profile", 1) == 0) &&
               (maybe_execute_file ("~/.bash_login", 1) == 0))  /* bash */
        maybe_execute_file ("~/.profile", 1);
        }
    }

      /* bash */
      if (act_like_sh == 0 && no_rc == 0)
    {
#ifdef SYS_BASHRC
#  if defined (__OPENNT)
      maybe_execute_file (_prefixInstallPath(SYS_BASHRC, NULL, 0), 1);
#  else
      maybe_execute_file (SYS_BASHRC, 1);
#  endif
#endif
      maybe_execute_file (bashrc_file, 1);
    }
      /* sh */
      else if (act_like_sh && privileged_mode == 0 && sourced_env++ == 0)
    execute_env_file (get_string_value ("ENV"));
    }
  else      /* bash --posix, sh --posix */
    {
      /* bash and sh */
      if (interactive_shell && privileged_mode == 0 && sourced_env++ == 0)
    execute_env_file (get_string_value ("ENV"));
    }

#if defined (JOB_CONTROL)
  set_job_control (old_job_control);
#endif
}

#if defined (RESTRICTED_SHELL)
/* Return 1 if the shell should be a restricted one based on NAME or the
   value of `restricted'.  Don't actually do anything, just return a
   boolean value. */
int
shell_is_restricted (name)
     char *name;
{
  char *temp;

  if (restricted)
    return 1;
  temp = base_pathname (name);
  return (STREQ (temp, RESTRICTED_SHELL_NAME));
}

/* Perhaps make this shell a `restricted' one, based on NAME.  If the
   basename of NAME is "rbash", then this shell is restricted.  The
   name of the restricted shell is a configurable option, see config.h.
   In a restricted shell, PATH, SHELL, ENV, and BASH_ENV are read-only
   and non-unsettable.
   Do this also if `restricted' is already set to 1; maybe the shell was
   started with -r. */
int
maybe_make_restricted (name)
     char *name;
{
  char *temp;

  temp = base_pathname (name);
  if (restricted || (STREQ (temp, RESTRICTED_SHELL_NAME)))
    {
      set_var_read_only ("PATH");
      set_var_read_only ("SHELL");
      set_var_read_only ("ENV");
      set_var_read_only ("BASH_ENV");
      restricted = 1;
    }
  return (restricted);
}
#endif /* RESTRICTED_SHELL */

/* Fetch the current set of uids and gids and return 1 if we're running
   setuid or setgid. */
static int
uidget ()
{
  uid_t u;

  u = getuid ();
  if (current_user.uid != u)
    {
      FREE (current_user.user_name);
      FREE (current_user.shell);
      FREE (current_user.home_dir);
      current_user.user_name = current_user.shell = current_user.home_dir = (char *)NULL;
    }
  current_user.uid = u;
  current_user.gid = getgid ();
  current_user.euid = geteuid ();
  current_user.egid = getegid ();

  /* See whether or not we are running setuid or setgid. */
  return (current_user.uid != current_user.euid) ||
       (current_user.gid != current_user.egid);
}

void
disable_priv_mode ()
{
  setuid (current_user.uid);
  setgid (current_user.gid);
  current_user.euid = current_user.uid;
  current_user.egid = current_user.gid;
}

static int
run_wordexp (words)
     char *words;
{
  int code, nw, nb;
  WORD_LIST *wl, *result;

  code = setjmp (top_level);

  if (code != NOT_JUMPED)
    {
      switch (code)
    {
      /* Some kind of throw to top_level has occured. */
    case FORCE_EOF:
      return last_command_exit_value = 127;
    case EXITPROG:
      return last_command_exit_value;
    case DISCARD:
      return last_command_exit_value = 1;
    default:
      command_error ("run_wordexp", CMDERR_BADJUMP, code, 0);
    }
    }

  /* Run it through the parser to get a list of words and expand them */
  if (words && *words)
    {
      with_input_from_string (words, "--wordexp");
      if (parse_command () != 0)
    return (126);
      if (global_command == 0)
    {
      printf ("0\n0\n");
      return (0);
    }
      if (global_command->type != cm_simple)
    return (126);
      wl = global_command->value.Simple->words;
      result = wl ? expand_words_no_vars (wl) : (WORD_LIST *)0;
    }
  else
    result = (WORD_LIST *)0;

  last_command_exit_value = 0;

  if (result == 0)
    {
      printf ("0\n0\n");
      return (0);
    }

  /* Count up the number of words and bytes, and print them.  Don't count
     the trailing NUL byte. */
  for (nw = nb = 0, wl = result; wl; wl = wl->next)
    {
      nw++;
      nb += strlen (wl->word->word);
    }
  printf ("%u\n%u\n", nw, nb);
  /* Print each word on a separate line.  This will have to be changed when
     the interface to glibc is completed. */
  for (wl = result; wl; wl = wl->next)
    printf ("%s\n", wl->word->word);

  return (0);
}

#if defined (ONESHOT)
/* Run one command, given as the argument to the -c option.  Tell
   parse_and_execute not to fork for a simple command. */
static int
run_one_command (command)
     char *command;
{
  int code;

  code = setjmp (top_level);

  if (code != NOT_JUMPED)
    {
#if defined (PROCESS_SUBSTITUTION)
      unlink_fifo_list ();
#endif /* PROCESS_SUBSTITUTION */
      switch (code)
    {
      /* Some kind of throw to top_level has occured. */
    case FORCE_EOF:
      return last_command_exit_value = 127;
    case EXITPROG:
      return last_command_exit_value;
    case DISCARD:
      return last_command_exit_value = 1;
    default:
      command_error ("run_one_command", CMDERR_BADJUMP, code, 0);
    }
    }
   return (parse_and_execute (savestring (command), "-c", SEVAL_NOHIST));
}
#endif /* ONESHOT */

static int
bind_args (argv, arg_start, arg_end, start_index)
     char **argv;
     int arg_start, arg_end, start_index;
{
  register int i;
  WORD_LIST *args;

  for (i = arg_start, args = (WORD_LIST *)NULL; i != arg_end; i++)
    args = make_word_list (make_word (argv[i]), args);
  if (args)
    {
      args = REVERSE_LIST (args, WORD_LIST *);
      if (start_index == 0) /* bind to $0...$n for sh -c command */
    {
      /* Posix.2 4.56.3 says that the first argument after sh -c command
         becomes $0, and the rest of the arguments become $1...$n */
      shell_name = savestring (args->word->word);
      FREE (dollar_vars[0]);
      dollar_vars[0] = savestring (args->word->word);
      remember_args (args->next, 1);
    }
      else          /* bind to $1...$n for shell script */
    remember_args (args, 1);

      dispose_words (args);
    }

  return (i);
}

void
unbind_args ()
{
  remember_args ((WORD_LIST *)NULL, 1);
}

static int
open_shell_script (script_name)
     char *script_name;
{
  int fd, e, fd_is_tty;
  char *filename, *path_filename;
  char sample[80];
  int sample_len;
  struct stat sb;

  free (dollar_vars[0]);
  dollar_vars[0] = savestring (script_name);
  filename = savestring (script_name);

  fd = open (filename, O_RDONLY);
  if ((fd < 0) && (errno == ENOENT) && (absolute_program (filename) == 0))
    {
      e = errno;
      /* If it's not in the current directory, try looking through PATH
     for it. */
      path_filename = find_path_file (script_name);
      if (path_filename)
    {
      free (filename);
      filename = path_filename;
      fd = open (filename, O_RDONLY);
    }
      else
    errno = e;
    }

  if (fd < 0)
    {
      e = errno;
      file_error (filename);
      exit ((e == ENOENT) ? EX_NOTFOUND : EX_NOINPUT);
    }

#ifdef HAVE_DEV_FD
  fd_is_tty = isatty (fd);
#else
  fd_is_tty = 0;
#endif

  /* Only do this with non-tty file descriptors we can seek on. */
  if (fd_is_tty == 0 && (lseek (fd, 0L, 1) != -1))
    {
      /* Check to see if the `file' in `bash file' is a binary file
     according to the same tests done by execute_simple_command (),
     and report an error and exit if it is. */
      sample_len = read (fd, sample, sizeof (sample));
      if (sample_len < 0)
    {
      e = errno;
      if ((fstat (fd, &sb) == 0) && S_ISDIR (sb.st_mode))
        internal_error ("%s: is a directory", filename);
      else
        {
          errno = e;
          file_error (filename);
        }
      exit (EX_NOEXEC);
    }
      else if (sample_len > 0 && (check_binary_file (sample, sample_len)))
    {
      internal_error ("%s: cannot execute binary file", filename);
      exit (EX_BINARY_FILE);
    }
      /* Now rewind the file back to the beginning. */
      lseek (fd, 0L, 0);
    }

  /* Open the script.  But try to move the file descriptor to a randomly
     large one, in the hopes that any descriptors used by the script will
     not match with ours. */
  fd = move_to_high_fd (fd, 0, -1);

#if defined (__CYGWIN__) && defined (O_TEXT)
  setmode (fd, O_TEXT);
#endif

#if defined (BUFFERED_INPUT)
  default_buffered_input = fd;
  SET_CLOSE_ON_EXEC (default_buffered_input);
#else /* !BUFFERED_INPUT */
  default_input = fdopen (fd, "r");

  if (default_input == 0)
    {
      file_error (filename);
      exit (EX_NOTFOUND);
    }

  SET_CLOSE_ON_EXEC (fd);
  if (fileno (default_input) != fd)
    SET_CLOSE_ON_EXEC (fileno (default_input));
#endif /* !BUFFERED_INPUT */

  /* Just about the only way for this code to be executed is if something
     like `bash -i /dev/stdin' is executed. */
  if (interactive_shell && fd_is_tty)
    {
      dup2 (fd, 0);
      close (fd);
      fd = 0;
#if defined (BUFFERED_INPUT)
      default_buffered_input = 0;
#else
      fclose (default_input);
      default_input = stdin;
#endif
    }
  else if (forced_interactive && fd_is_tty == 0)
    /* But if a script is called with something like `bash -i scriptname',
       we need to do a non-interactive setup here, since we didn't do it
       before. */
    init_noninteractive ();

  free (filename);
  return (fd);
}

/* Initialize the input routines for the parser. */
static void
set_bash_input ()
{
  /* Make sure the fd from which we are reading input is not in
     no-delay mode. */
#if defined (BUFFERED_INPUT)
  if (interactive == 0)
    sh_unset_nodelay_mode (default_buffered_input);
  else
#endif /* !BUFFERED_INPUT */
    sh_unset_nodelay_mode (fileno (stdin));

  /* with_input_from_stdin really means `with_input_from_readline' */
  if (interactive && no_line_editing == 0)
    with_input_from_stdin ();
  else
#if defined (BUFFERED_INPUT)
    {
      if (interactive == 0)
    with_input_from_buffered_stream (default_buffered_input, dollar_vars[0]);
      else
    with_input_from_stream (default_input, dollar_vars[0]);
    }
#else /* !BUFFERED_INPUT */
    with_input_from_stream (default_input, dollar_vars[0]);
#endif /* !BUFFERED_INPUT */
}

/* Close the current shell script input source and forget about it.  This is
   extern so execute_cmd.c:initialize_subshell() can call it.  If CHECK_ZERO
   is non-zero, we close default_buffered_input even if it's the standard
   input (fd 0). */
void
unset_bash_input (check_zero)
     int check_zero;
{
#if defined (BUFFERED_INPUT)
  if ((check_zero && default_buffered_input >= 0) ||
      (check_zero == 0 && default_buffered_input > 0))
    {
      close_buffered_fd (default_buffered_input);
      default_buffered_input = bash_input.location.buffered_fd = -1;
    }
#else /* !BUFFERED_INPUT */
  if (default_input)
    {
      fclose (default_input);
      default_input = (FILE *)NULL;
    }
#endif /* !BUFFERED_INPUT */
}
      

#if !defined (PROGRAM)
#  define PROGRAM "bash"
#endif

static void
set_shell_name (argv0)
     char *argv0;
{
  /* Here's a hack.  If the name of this shell is "sh", then don't do
     any startup files; just try to be more like /bin/sh. */
  shell_name = base_pathname (argv0);

  if (*shell_name == '-')
    {
      shell_name++;
      login_shell++;
    }

  if (shell_name[0] == 's' && shell_name[1] == 'h' && shell_name[2] == '\0')
    act_like_sh++;
  if (shell_name[0] == 's' && shell_name[1] == 'u' && shell_name[2] == '\0')
    su_shell++;

  shell_name = argv0;
  FREE (dollar_vars[0]);
  dollar_vars[0] = savestring (shell_name);

  /* A program may start an interactive shell with
      "execl ("/bin/bash", "-", NULL)".
     If so, default the name of this shell to our name. */
  if (!shell_name || !*shell_name || (shell_name[0] == '-' && !shell_name[1]))
    shell_name = PROGRAM;
}

static void
init_interactive ()
{
  interactive_shell = startup_state = interactive = 1;
  expand_aliases = 1;
}

static void
init_noninteractive ()
{
#if defined (HISTORY)
  bash_history_reinit (0);
#endif /* HISTORY */
  interactive_shell = startup_state = interactive = 0;
  expand_aliases = posixly_correct; /* XXX - was 0 not posixly_correct */
  no_line_editing = 1;
#if defined (JOB_CONTROL)
  set_job_control (0);
#endif /* JOB_CONTROL */
}

void
get_current_user_info ()
{
  struct passwd *entry;

  /* Don't fetch this more than once. */
  if (current_user.user_name == 0)
    {
      entry = getpwuid (current_user.uid);
      if (entry)
    {
      current_user.user_name = savestring (entry->pw_name);
      current_user.shell = (entry->pw_shell && entry->pw_shell[0])
                ? savestring (entry->pw_shell)
                : savestring ("/bin/sh");
      current_user.home_dir = savestring (entry->pw_dir);
    }
      else
    {
      current_user.user_name = savestring ("I have no name!");
      current_user.shell = savestring ("/bin/sh");
      current_user.home_dir = savestring ("/");
    }
      endpwent ();
    }
}

/* Do whatever is necessary to initialize the shell.
   Put new initializations in here. */
static void
shell_initialize ()
{
  char hostname[256];

  /* Line buffer output for stderr and stdout. */
  if (shell_initialized == 0)
    {
      sh_setlinebuf (stderr);
      sh_setlinebuf (stdout);
    }

  /* Sort the array of shell builtins so that the binary search in
     find_shell_builtin () works correctly. */
  initialize_shell_builtins ();

  /* Initialize the trap signal handlers before installing our own
     signal handlers.  traps.c:restore_original_signals () is responsible
     for restoring the original default signal handlers.  That function
     is called when we make a new child. */
  initialize_traps ();
  initialize_signals (0);

  /* It's highly unlikely that this will change. */
  if (current_host_name == 0)
    {
      /* Initialize current_host_name. */
      if (gethostname (hostname, 255) < 0)
    current_host_name = "??host??";
      else
    current_host_name = savestring (hostname);
    }

  /* Initialize the stuff in current_user that comes from the password
     file.  We don't need to do this right away if the shell is not
     interactive. */
  if (interactive_shell)
    get_current_user_info ();

  /* Initialize our interface to the tilde expander. */
  tilde_initialize ();

  /* Initialize internal and environment variables.  Don't import shell
     functions from the environment if we are running in privileged or
     restricted mode or if the shell is running setuid. */
#if defined (RESTRICTED_SHELL)
  initialize_shell_variables (shell_environment, privileged_mode||restricted||running_setuid);
#else
  initialize_shell_variables (shell_environment, privileged_mode||running_setuid);
#endif

  /* Initialize the data structures for storing and running jobs. */
  initialize_job_control (0);

  /* Initialize input streams to null. */
  initialize_bash_input ();

  initialize_flags ();

  /* Initialize the shell options.  Don't import the shell options
     from the environment variable $SHELLOPTS if we are running in
     privileged or restricted mode or if the shell is running setuid. */
#if defined (RESTRICTED_SHELL)
  initialize_shell_options (privileged_mode||restricted||running_setuid);
#else
  initialize_shell_options (privileged_mode||running_setuid);
#endif
}

/* Function called by main () when it appears that the shell has already
   had some initialization performed.  This is supposed to reset the world
   back to a pristine state, as if we had been exec'ed. */
static void
shell_reinitialize ()
{
  /* The default shell prompts. */
  primary_prompt = PPROMPT;
  secondary_prompt = SPROMPT;

  /* Things that get 1. */
  current_command_number = 1;

  /* We have decided that the ~/.bashrc file should not be executed
     for the invocation of each shell script.  If the variable $ENV
     (or $BASH_ENV) is set, its value is used as the name of a file
     to source. */
  no_rc = no_profile = 1;

  /* Things that get 0. */
  login_shell = make_login_shell = interactive = executing = 0;
  debugging = do_version = line_number = last_command_exit_value = 0;
  forced_interactive = interactive_shell = subshell_environment = 0;
  expand_aliases = 0;

#if defined (HISTORY)
  bash_history_reinit (0);
#endif /* HISTORY */

#if defined (RESTRICTED_SHELL)
  restricted = 0;
#endif /* RESTRICTED_SHELL */

  /* Ensure that the default startup file is used.  (Except that we don't
     execute this file for reinitialized shells). */
  bashrc_file = "~/.bashrc";

  /* Delete all variables and functions.  They will be reinitialized when
     the environment is parsed. */
  delete_all_contexts (shell_variables);
  delete_all_variables (shell_functions);

  shell_reinitialized = 1;
}

static void
show_shell_usage (fp, extra)
     FILE *fp;
     int extra;
{
  int i;
  char *set_opts, *s, *t;

  if (extra)
    fprintf (fp, "GNU bash, version %s-(%s)\n", shell_version_string (), MACHTYPE);
  fprintf (fp, "Usage:\t%s [GNU long option] [option] ...\n\t%s [GNU long option] [option] script-file ...\n",
         shell_name, shell_name);
  fputs ("GNU long options:\n", fp);
  for (i = 0; long_args[i].name; i++)
    fprintf (fp, "\t--%s\n", long_args[i].name);

  fputs ("Shell options:\n", fp);
  fputs ("\t-irsD or -c command or -O shopt_option\t\t(invocation only)\n", fp);

  for (i = 0, set_opts = 0; shell_builtins[i].name; i++)
    if (STREQ (shell_builtins[i].name, "set"))
      set_opts = savestring (shell_builtins[i].short_doc);
  if (set_opts)
    {
      s = xstrchr (set_opts, '[');
      if (s == 0)
    s = set_opts;
      while (*++s == '-')
    ;
      t = xstrchr (s, ']');
      if (t)
    *t = '\0';
      fprintf (fp, "\t-%s or -o option\n", s);
      free (set_opts);
    }

  if (extra)
    {
      fprintf (fp, "Type `%s -c \"help set\"' for more information about shell options.\n", shell_name);
      fprintf (fp, "Type `%s -c help' for more information about shell builtin commands.\n", shell_name);
      fprintf (fp, "Use the `bashbug' command to report bugs.\n");
    }
}

static void
add_shopt_to_alist (opt, on_or_off)
     char *opt;
     int on_or_off;
{
  if (shopt_ind >= shopt_len)
    {
      shopt_len += 8;
      shopt_alist = (STRING_INT_ALIST *)xrealloc (shopt_alist, shopt_len * sizeof (shopt_alist[0]));
    }
  shopt_alist[shopt_ind].word = opt;
  shopt_alist[shopt_ind].token = on_or_off;
  shopt_ind++;
}

static void
run_shopt_alist ()
{
  register int i;

  for (i = 0; i < shopt_ind; i++)
    if (shopt_setopt (shopt_alist[i].word, (shopt_alist[i].token == '-')) != EXECUTION_SUCCESS)
      exit (EX_USAGE);
  free (shopt_alist);
  shopt_alist = 0;
  shopt_ind = shopt_len = 0;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/mkbuiltins.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/mkbuiltins.c used in: Step 1055
/* mkbuiltins.c - Create builtins.c, builtext.h, and builtdoc.c from
   a single source file called builtins.def. */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#ifndef _MINIX
#include "../bashtypes.h"
#include <sys/file.h>
#endif

#include "posixstat.h"
#include "filecntl.h"

#include "../bashansi.h"
#include <stdio.h>
#include <errno.h>

#include "stdc.h"

#define DOCFILE "builtins.texi"

#ifndef errno
extern int errno;
#endif

static char *xmalloc (), *xrealloc ();

#if !defined (__STDC__) && !defined (strcpy)
extern char *strcpy ();
#endif /* !__STDC__ && !strcpy */

#define savestring(x) strcpy (xmalloc (1 + strlen (x)), (x))
#define whitespace(c) (((c) == ' ') || ((c) == '\t'))

/* Flag values that builtins can have. */
#define BUILTIN_FLAG_SPECIAL    0x01
#define BUILTIN_FLAG_ASSIGNMENT 0x02

/* If this stream descriptor is non-zero, then write
   texinfo documentation to it. */
FILE *documentation_file = (FILE *)NULL;

/* Non-zero means to only produce documentation. */
int only_documentation = 0;

/* Non-zero means to not do any productions. */
int inhibit_production = 0;

/* Non-zero means to produce separate help files for each builtin, named by
   the builtin name, in `./helpfiles'. */
int separate_helpfiles = 0;

/* The name of a directory into which the separate external help files will
   eventually be installed. */
char *helpfile_directory;

/* The name of a directory to precede the filename when reporting
   errors. */
char *error_directory = (char *)NULL;

/* The name of the structure file. */
char *struct_filename = (char *)NULL;

/* The name of the external declaration file. */
char *extern_filename = (char *)NULL;

/* Here is a structure for manipulating arrays of data. */
typedef struct {
  int size;     /* Number of slots allocated to array. */
  int sindex;       /* Current location in array. */
  int width;        /* Size of each element. */
  int growth_rate;  /* How fast to grow. */
  char **array;     /* The array itself. */
} ARRAY;

/* Here is a structure defining a single BUILTIN. */
typedef struct {
  char *name;       /* The name of this builtin. */
  char *function;   /* The name of the function to call. */
  char *shortdoc;   /* The short documentation for this builtin. */
  char *docname;    /* Possible name for documentation string. */
  ARRAY *longdoc;   /* The long documentation for this builtin. */
  ARRAY *dependencies;  /* Null terminated array of #define names. */
  int flags;        /* Flags for this builtin. */
} BUILTIN_DESC;

/* Here is a structure which defines a DEF file. */
typedef struct {
  char *filename;   /* The name of the input def file. */
  ARRAY *lines;     /* The contents of the file. */
  int line_number;  /* The current line number. */
  char *production; /* The name of the production file. */
  FILE *output;     /* Open file stream for PRODUCTION. */
  ARRAY *builtins;  /* Null terminated array of BUILTIN_DESC *. */
} DEF_FILE;

/* The array of all builtins encountered during execution of this code. */
ARRAY *saved_builtins = (ARRAY *)NULL;

/* The Posix.2 so-called `special' builtins. */
char *special_builtins[] =
{
  ":", ".", "source", "break", "continue", "eval", "exec", "exit",
  "export", "readonly", "return", "set", "shift", "trap", "unset",
  (char *)NULL
};

/* The builtin commands that take assignment statements as arguments. */
char *assignment_builtins[] =
{
  "alias", "declare", "export", "local", "readonly", "typeset",
  (char *)NULL
};

/* Forward declarations. */
static int is_special_builtin ();
static int is_assignment_builtin ();

#if !defined (HAVE_RENAME)
static int rename ();
#endif

void extract_info ();

void file_error ();
void line_error ();

void write_file_headers ();
void write_file_footers ();
void write_ifdefs ();
void write_endifs ();
void write_documentation ();
void write_longdocs ();
void write_builtins ();

int write_helpfiles ();

void free_defs ();
void add_documentation ();

void must_be_building ();
void remove_trailing_whitespace ();

#define document_name(b)    ((b)->docname ? (b)->docname : (b)->name)



/* For each file mentioned on the command line, process it and
   write the information to STRUCTFILE and EXTERNFILE, while
   creating the production file if neccessary. */
int
main (argc, argv)
     int argc;
     char **argv;
{
  int arg_index = 1;
  FILE *structfile, *externfile;
  char *documentation_filename, *temp_struct_filename;

  structfile = externfile = (FILE *)NULL;
  documentation_filename = DOCFILE;
  temp_struct_filename = (char *)NULL;

  while (arg_index < argc && argv[arg_index][0] == '-')
    {
      char *arg = argv[arg_index++];

      if (strcmp (arg, "-externfile") == 0)
    extern_filename = argv[arg_index++];
      else if (strcmp (arg, "-structfile") == 0)
    struct_filename = argv[arg_index++];
      else if (strcmp (arg, "-noproduction") == 0)
    inhibit_production = 1;
      else if (strcmp (arg, "-document") == 0)
    documentation_file = fopen (documentation_filename, "w");
      else if (strcmp (arg, "-D") == 0)
    {
      int len;

      if (error_directory)
        free (error_directory);

      error_directory = xmalloc (2 + strlen (argv[arg_index]));
      strcpy (error_directory, argv[arg_index]);
      len = strlen (error_directory);

      if (len && error_directory[len - 1] != '/')
        strcat (error_directory, "/");

      arg_index++;
    }
      else if (strcmp (arg, "-documentonly") == 0)
    {
      only_documentation = 1;
      documentation_file = fopen (documentation_filename, "w");
    }
      else if (strcmp (arg, "-H") == 0)
        {
      separate_helpfiles = 1;
      helpfile_directory = argv[arg_index++];
        }
      else
    {
      fprintf (stderr, "%s: Unknown flag %s.\n", argv[0], arg);
      exit (2);
    }
    }

  /* If there are no files to process, just quit now. */
  if (arg_index == argc)
    exit (0);

  if (!only_documentation)
    {
      /* Open the files. */
      if (struct_filename)
    {
      temp_struct_filename = xmalloc (15);
      sprintf (temp_struct_filename, "mk-%ld", (long) getpid ());
      structfile = fopen (temp_struct_filename, "w");

      if (!structfile)
        file_error (temp_struct_filename);
    }

      if (extern_filename)
    {
      externfile = fopen (extern_filename, "w");

      if (!externfile)
        file_error (extern_filename);
    }

      /* Write out the headers. */
      write_file_headers (structfile, externfile);
    }

  if (documentation_file)
    {
      fprintf (documentation_file, "@c Table of builtins created with %s.\n",
           argv[0]);
      fprintf (documentation_file, "@ftable @asis\n");
    }

  /* Process the .def files. */
  while (arg_index < argc)
    {
      register char *arg;

      arg = argv[arg_index++];

      extract_info (arg, structfile, externfile);
    }

  /* Close the files. */
  if (!only_documentation)
    {
      /* Write the footers. */
      write_file_footers (structfile, externfile);

      if (structfile)
    {
      write_longdocs (structfile, saved_builtins);
      fclose (structfile);
      rename (temp_struct_filename, struct_filename);
    }

      if (externfile)
    fclose (externfile);
    }

  if (separate_helpfiles)
    {
      write_helpfiles (saved_builtins);
    }

  if (documentation_file)
    {
      fprintf (documentation_file, "@end ftable\n");
      fclose (documentation_file);
    }

  exit (0);
}

/* **************************************************************** */
/*                                  */
/*        Array Functions and Manipulators          */
/*                                  */
/* **************************************************************** */

/* Make a new array, and return a pointer to it.  The array will
   contain elements of size WIDTH, and is initialized to no elements. */
ARRAY *
array_create (width)
     int width;
{
  ARRAY *array;

  array = (ARRAY *)xmalloc (sizeof (ARRAY));
  array->size = 0;
  array->sindex = 0;
  array->width = width;

  /* Default to increasing size in units of 20. */
  array->growth_rate = 20;

  array->array = (char **)NULL;

  return (array);
}

/* Copy the array of strings in ARRAY. */
ARRAY *
copy_string_array (array)
     ARRAY *array;
{
  register int i;
  ARRAY *copy;

  if (!array)
    return (ARRAY *)NULL;

  copy = array_create (sizeof (char *));

  copy->size = array->size;
  copy->sindex = array->sindex;
  copy->width = array->width;

  copy->array = (char **)xmalloc ((1 + array->sindex) * sizeof (char *));
  
  for (i = 0; i < array->sindex; i++)
    copy->array[i] = savestring (array->array[i]);

  copy->array[i] = (char *)NULL;

  return (copy);
}

/* Add ELEMENT to ARRAY, growing the array if neccessary. */
void
array_add (element, array)
     char *element;
     ARRAY *array;
{
  if (array->sindex + 2 > array->size)
    array->array = (char **)xrealloc
      (array->array, (array->size += array->growth_rate) * array->width);

#if defined (HAVE_BCOPY)
  bcopy (&element, (char *) &(array->array[array->sindex]), array->width);
  array->sindex++;
  bzero ((char *) &(array->array[array->sindex]), array->width);
#else
  array->array[array->sindex++] = element;
  array->array[array->sindex] = (char *)NULL;
#endif /* !HAVE_BCOPY */
}

/* Free an allocated array and data pointer. */
void
array_free (array)
     ARRAY *array;
{
  if (array->array)
    free (array->array);

  free (array);
}

/* **************************************************************** */
/*                                  */
/*             Processing a DEF File                */
/*                                  */
/* **************************************************************** */

/* The definition of a function. */
typedef int Function ();
typedef int mk_handler_func_t __P((char *, DEF_FILE *, char *));

/* Structure handles processor directives. */
typedef struct {
  char *directive;
  mk_handler_func_t *function;
} HANDLER_ENTRY;

extern int builtin_handler __P((char *, DEF_FILE *, char *));
extern int function_handler __P((char *, DEF_FILE *, char *));
extern int short_doc_handler __P((char *, DEF_FILE *, char *));
extern int comment_handler __P((char *, DEF_FILE *, char *));
extern int depends_on_handler __P((char *, DEF_FILE *, char *));
extern int produces_handler __P((char *, DEF_FILE *, char *));
extern int end_handler __P((char *, DEF_FILE *, char *));
extern int docname_handler __P((char *, DEF_FILE *, char *));

HANDLER_ENTRY handlers[] = {
  { "BUILTIN", builtin_handler },
  { "DOCNAME", docname_handler },
  { "FUNCTION", function_handler },
  { "SHORT_DOC", short_doc_handler },
  { "$", comment_handler },
  { "COMMENT", comment_handler },
  { "DEPENDS_ON", depends_on_handler },
  { "PRODUCES", produces_handler },
  { "END", end_handler },
  { (char *)NULL, (mk_handler_func_t *)NULL }
};

/* Return the entry in the table of handlers for NAME. */
HANDLER_ENTRY *
find_directive (directive)
     char *directive;
{
  register int i;

  for (i = 0; handlers[i].directive; i++)
    if (strcmp (handlers[i].directive, directive) == 0)
      return (&handlers[i]);

  return ((HANDLER_ENTRY *)NULL);
}

/* Non-zero indicates that a $BUILTIN has been seen, but not
   the corresponding $END. */
static int building_builtin = 0;

/* Non-zero means to output cpp line and file information before
   printing the current line to the production file. */
int output_cpp_line_info = 0;

/* The main function of this program.  Read FILENAME and act on what is
   found.  Lines not starting with a dollar sign are copied to the
   $PRODUCES target, if one is present.  Lines starting with a dollar sign
   are directives to this program, specifying the name of the builtin, the
   function to call, the short documentation and the long documentation
   strings.  FILENAME can contain multiple $BUILTINs, but only one $PRODUCES
   target.  After the file has been processed, write out the names of
   builtins found in each $BUILTIN.  Plain text found before the $PRODUCES
   is ignored, as is "$$ comment text". */
void
extract_info (filename, structfile, externfile)
     char *filename;
     FILE *structfile, *externfile;
{
  register int i;
  DEF_FILE *defs;
  struct stat finfo;
  size_t file_size;
  char *buffer, *line;
  int fd, nr;

  if (stat (filename, &finfo) == -1)
    file_error (filename);

  fd = open (filename, O_RDONLY, 0666);

  if (fd == -1)
    file_error (filename);

  file_size = (size_t)finfo.st_size;
  buffer = xmalloc (1 + file_size);

  if ((nr = read (fd, buffer, file_size)) < 0)
    file_error (filename);

  /* This is needed on WIN32, and does not hurt on Unix. */
  if (nr < file_size)
    file_size = nr;

  close (fd);

  if (nr == 0)
    {
      fprintf (stderr, "mkbuiltins: %s: skipping zero-length file\n", filename);
      return;
    }

  /* Create and fill in the initial structure describing this file. */
  defs = (DEF_FILE *)xmalloc (sizeof (DEF_FILE));
  defs->filename = filename;
  defs->lines = array_create (sizeof (char *));
  defs->line_number = 0;
  defs->production = (char *)NULL;
  defs->output = (FILE *)NULL;
  defs->builtins = (ARRAY *)NULL;

  /* Build the array of lines. */
  i = 0;
  while (i < file_size)
    {
      array_add (&buffer[i], defs->lines);

      while (buffer[i] != '\n' && i < file_size)
    i++;
      buffer[i++] = '\0';
    }

  /* Begin processing the input file.  We don't write any output
     until we have a file to write output to. */
  output_cpp_line_info = 1;

  /* Process each line in the array. */
  for (i = 0; line = defs->lines->array[i]; i++)
    {
      defs->line_number = i;

      if (*line == '$')
    {
      register int j;
      char *directive;
      HANDLER_ENTRY *handler;

      /* Isolate the directive. */
      for (j = 0; line[j] && !whitespace (line[j]); j++);

      directive = xmalloc (j);
      strncpy (directive, line + 1, j - 1);
      directive[j -1] = '\0';

      /* Get the function handler and call it. */
      handler = find_directive (directive);

      if (!handler)
        {
          line_error (defs, "Unknown directive `%s'", directive);
          free (directive);
          continue;
        }
      else
        {
          /* Advance to the first non-whitespace character. */
          while (whitespace (line[j]))
        j++;

          /* Call the directive handler with the FILE, and ARGS. */
          (*(handler->function)) (directive, defs, line + j);
        }
      free (directive);
    }
      else
    {
      if (building_builtin)
        add_documentation (defs, line);
      else if (defs->output)
        {
          if (output_cpp_line_info)
        {
          /* If we're handed an absolute pathname, don't prepend
             the directory name. */
          if (defs->filename[0] == '/')
            fprintf (defs->output, "#line %d \"%s\"\n",
                 defs->line_number + 1, defs->filename);
          else
            fprintf (defs->output, "#line %d \"%s%s\"\n",
                 defs->line_number + 1,
                 error_directory ? error_directory : "./",
                 defs->filename);
          output_cpp_line_info = 0;
        }

          fprintf (defs->output, "%s\n", line);
        }
    }
    }

  /* Close the production file. */
  if (defs->output)
    fclose (defs->output);

  /* The file has been processed.  Write the accumulated builtins to
     the builtins.c file, and write the extern definitions to the
     builtext.h file. */
  write_builtins (defs, structfile, externfile);

  free (buffer);
  free_defs (defs);
}

#define free_safely(x) if (x) free (x)

static void
free_builtin (builtin)
     BUILTIN_DESC *builtin;
{
  register int i;

  free_safely (builtin->name);
  free_safely (builtin->function);
  free_safely (builtin->shortdoc);
  free_safely (builtin->docname);

  if (builtin->longdoc)
    array_free (builtin->longdoc);

  if (builtin->dependencies)
    {
      for (i = 0; builtin->dependencies->array[i]; i++)
    free (builtin->dependencies->array[i]);
      array_free (builtin->dependencies);
    }
}

/* Free all of the memory allocated to a DEF_FILE. */
void
free_defs (defs)
     DEF_FILE *defs;
{
  register int i;
  register BUILTIN_DESC *builtin;

  if (defs->production)
    free (defs->production);

  if (defs->lines)
    array_free (defs->lines);

  if (defs->builtins)
    {
      for (i = 0; builtin = (BUILTIN_DESC *)defs->builtins->array[i]; i++)
    {
      free_builtin (builtin);
      free (builtin);
    }
      array_free (defs->builtins);
    }
  free (defs);
}

/* **************************************************************** */
/*                                  */
/*           The Handler Functions Themselves           */
/*                                  */
/* **************************************************************** */

/* Strip surrounding whitespace from STRING, and
   return a pointer to the start of it. */
char *
strip_whitespace (string)
     char *string;
{
  while (whitespace (*string))
      string++;

  remove_trailing_whitespace (string);
  return (string);
}

/* Remove only the trailing whitespace from STRING. */
void
remove_trailing_whitespace (string)
     char *string;
{
  register int i;

  i = strlen (string) - 1;

  while (i > 0 && whitespace (string[i]))
    i--;

  string[++i] = '\0';
}

/* Ensure that there is a argument in STRING and return it.
   FOR_WHOM is the name of the directive which needs the argument.
   DEFS is the DEF_FILE in which the directive is found.
   If there is no argument, produce an error. */
char *
get_arg (for_whom, defs, string)
     char *for_whom, *string;
     DEF_FILE *defs;
{
  char *new;

  new = strip_whitespace (string);

  if (!*new)
    line_error (defs, "%s requires an argument", for_whom);

  return (savestring (new));
}

/* Error if not building a builtin. */
void
must_be_building (directive, defs)
     char *directive;
     DEF_FILE *defs;
{
  if (!building_builtin)
    line_error (defs, "%s must be inside of a $BUILTIN block", directive);
}

/* Return the current builtin. */
BUILTIN_DESC *
current_builtin (directive, defs)
     char *directive;
     DEF_FILE *defs;
{
  must_be_building (directive, defs);
  if (defs->builtins)
    return ((BUILTIN_DESC *)defs->builtins->array[defs->builtins->sindex - 1]);
  else
    return ((BUILTIN_DESC *)NULL);
}

/* Add LINE to the long documentation for the current builtin.
   Ignore blank lines until the first non-blank line has been seen. */
void
add_documentation (defs, line)
     DEF_FILE *defs;
     char *line;
{
  register BUILTIN_DESC *builtin;

  builtin = current_builtin ("(implied LONGDOC)", defs);

  remove_trailing_whitespace (line);

  if (!*line && !builtin->longdoc)
    return;

  if (!builtin->longdoc)
    builtin->longdoc = array_create (sizeof (char *));

  array_add (line, builtin->longdoc);
}

/* How to handle the $BUILTIN directive. */
int
builtin_handler (self, defs, arg)
     char *self;
     DEF_FILE *defs;
     char *arg;
{
  BUILTIN_DESC *new;
  char *name;

  /* If we are already building a builtin, we cannot start a new one. */
  if (building_builtin)
    {
      line_error (defs, "%s found before $END", self);
      return (-1);
    }

  output_cpp_line_info++;

  /* Get the name of this builtin, and stick it in the array. */
  name = get_arg (self, defs, arg);

  /* If this is the first builtin, create the array to hold them. */
  if (!defs->builtins)
    defs->builtins = array_create (sizeof (BUILTIN_DESC *));

  new = (BUILTIN_DESC *)xmalloc (sizeof (BUILTIN_DESC));
  new->name = name;
  new->function = (char *)NULL;
  new->shortdoc = (char *)NULL;
  new->docname = (char *)NULL;
  new->longdoc = (ARRAY *)NULL;
  new->dependencies = (ARRAY *)NULL;
  new->flags = 0;

  if (is_special_builtin (name))
    new->flags |= BUILTIN_FLAG_SPECIAL;
  if (is_assignment_builtin (name))
    new->flags |= BUILTIN_FLAG_ASSIGNMENT;

  array_add ((char *)new, defs->builtins);
  building_builtin = 1;

  return (0);
}

/* How to handle the $FUNCTION directive. */
int
function_handler (self, defs, arg)
     char *self;
     DEF_FILE *defs;
     char *arg;
{
  register BUILTIN_DESC *builtin;

  builtin = current_builtin (self, defs);

  if (builtin == 0)
    {
      line_error (defs, "syntax error: no current builtin for $FUNCTION directive");
      exit (1);
    }
  if (builtin->function)
    line_error (defs, "%s already has a function (%s)",
        builtin->name, builtin->function);
  else
    builtin->function = get_arg (self, defs, arg);

  return (0);
}

/* How to handle the $DOCNAME directive. */
int
docname_handler (self, defs, arg)
     char *self;
     DEF_FILE *defs;
     char *arg;
{
  register BUILTIN_DESC *builtin;

  builtin = current_builtin (self, defs);

  if (builtin->docname)
    line_error (defs, "%s already had a docname (%s)",
        builtin->name, builtin->docname);
  else
    builtin->docname = get_arg (self, defs, arg);

  return (0);
}

/* How to handle the $SHORT_DOC directive. */
int
short_doc_handler (self, defs, arg)
     char *self;
     DEF_FILE *defs;
     char *arg;
{
  register BUILTIN_DESC *builtin;

  builtin = current_builtin (self, defs);

  if (builtin->shortdoc)
    line_error (defs, "%s already has short documentation (%s)",
        builtin->name, builtin->shortdoc);
  else
    builtin->shortdoc = get_arg (self, defs, arg);

  return (0);
}

/* How to handle the $COMMENT directive. */
int
comment_handler (self, defs, arg)
     char *self;
     DEF_FILE *defs;
     char *arg;
{
  return (0);
}

/* How to handle the $DEPENDS_ON directive. */
int
depends_on_handler (self, defs, arg)
     char *self;
     DEF_FILE *defs;
     char *arg;
{
  register BUILTIN_DESC *builtin;
  char *dependent;

  builtin = current_builtin (self, defs);
  dependent = get_arg (self, defs, arg);

  if (!builtin->dependencies)
    builtin->dependencies = array_create (sizeof (char *));

  array_add (dependent, builtin->dependencies);

  return (0);
}

/* How to handle the $PRODUCES directive. */
int
produces_handler (self, defs, arg)
     char *self;
     DEF_FILE *defs;
     char *arg;
{
  /* If just hacking documentation, don't change any of the production
     files. */
  if (only_documentation)
    return (0);

  output_cpp_line_info++;

  if (defs->production)
    line_error (defs, "%s already has a %s definition", defs->filename, self);
  else
    {
      defs->production = get_arg (self, defs, arg);

      if (inhibit_production)
    return (0);

      defs->output = fopen (defs->production, "w");

      if (!defs->output)
    file_error (defs->production);

      fprintf (defs->output, "/* %s, created from %s. */\n",
           defs->production, defs->filename);
    }
  return (0);
}

/* How to handle the $END directive. */
int
end_handler (self, defs, arg)
     char *self;
     DEF_FILE *defs;
     char *arg;
{
  must_be_building (self, defs);
  building_builtin = 0;
  return (0);
}

/* **************************************************************** */
/*                                  */
/*          Error Handling Functions                */
/*                                  */
/* **************************************************************** */

/* Produce an error for DEFS with FORMAT and ARGS. */
void
line_error (defs, format, arg1, arg2)
     DEF_FILE *defs;
     char *format, *arg1, *arg2;
{
  if (defs->filename[0] != '/')
    fprintf (stderr, "%s", error_directory ? error_directory : "./");
  fprintf (stderr, "%s:%d:", defs->filename, defs->line_number + 1);
  fprintf (stderr, format, arg1, arg2);
  fprintf (stderr, "\n");
  fflush (stderr);
}

/* Print error message for FILENAME. */
void
file_error (filename)
     char *filename;
{
  perror (filename);
  exit (2);
}

/* **************************************************************** */
/*                                  */
/*          xmalloc and xrealloc ()                 */
/*                                  */
/* **************************************************************** */

static void memory_error_and_abort ();

static char *
xmalloc (bytes)
     int bytes;
{
  char *temp = (char *)malloc (bytes);

  if (!temp)
    memory_error_and_abort ();
  return (temp);
}

static char *
xrealloc (pointer, bytes)
     char *pointer;
     int bytes;
{
  char *temp;

  if (!pointer)
    temp = (char *)malloc (bytes);
  else
    temp = (char *)realloc (pointer, bytes);

  if (!temp)
    memory_error_and_abort ();

  return (temp);
}

static void
memory_error_and_abort ()
{
  fprintf (stderr, "mkbuiltins: out of virtual memory\n");
  abort ();
}

/* **************************************************************** */
/*                                  */
/*        Creating the Struct and Extern Files          */
/*                                  */
/* **************************************************************** */

/* Return a pointer to a newly allocated builtin which is
   an exact copy of BUILTIN. */
BUILTIN_DESC *
copy_builtin (builtin)
     BUILTIN_DESC *builtin;
{
  BUILTIN_DESC *new;

  new = (BUILTIN_DESC *)xmalloc (sizeof (BUILTIN_DESC));

  new->name = savestring (builtin->name);
  new->shortdoc = savestring (builtin->shortdoc);
  new->longdoc = copy_string_array (builtin->longdoc);
  new->dependencies = copy_string_array (builtin->dependencies);

  new->function =
    builtin->function ? savestring (builtin->function) : (char *)NULL;
  new->docname =
    builtin->docname  ? savestring (builtin->docname)  : (char *)NULL;

  return (new);
}

/* How to save away a builtin. */
void
save_builtin (builtin)
     BUILTIN_DESC *builtin;
{
  BUILTIN_DESC *newbuiltin;

  newbuiltin = copy_builtin (builtin);

  /* If this is the first builtin to be saved, create the array
     to hold it. */
  if (!saved_builtins)
      saved_builtins = array_create (sizeof (BUILTIN_DESC *));

  array_add ((char *)newbuiltin, saved_builtins);
}

/* Flags that mean something to write_documentation (). */
#define STRING_ARRAY    1
#define TEXINFO     2
#define PLAINTEXT   4

char *structfile_header[] = {
  "/* builtins.c -- the built in shell commands. */",
  "",
  "/* This file is manufactured by ./mkbuiltins, and should not be",
  "   edited by hand.  See the source to mkbuiltins for details. */",
  "",
  "/* Copyright (C) 1987-2002 Free Software Foundation, Inc.",
  "",
  "   This file is part of GNU Bash, the Bourne Again SHell.",
  "",
  "   Bash 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.",
  "",
  "   Bash 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 Bash; see the file COPYING.  If not, write to the Free",
  "   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */",
  "",
  "/* The list of shell builtins.  Each element is name, function, flags,",
  "   long-doc, short-doc.  The long-doc field contains a pointer to an array",
  "   of help lines.  The function takes a WORD_LIST *; the first word in the",
  "   list is the first arg to the command.  The list has already had word",
  "   expansion performed.",
  "",
  "   Functions which need to look at only the simple commands (e.g.",
  "   the enable_builtin ()), should ignore entries where",
  "   (array[i].function == (sh_builtin_func_t *)NULL).  Such entries are for",
  "   the list of shell reserved control structures, like `if' and `while'.",
  "   The end of the list is denoted with a NULL name field. */",
  "",
  "#include \"../builtins.h\"",
  (char *)NULL
  };

char *structfile_footer[] = {
  "  { (char *)0x0, (sh_builtin_func_t *)0x0, 0, (char **)0x0, (char *)0x0 }",
  "};",
  "",
  "struct builtin *shell_builtins = static_shell_builtins;",
  "struct builtin *current_builtin;",
  "",
  "int num_shell_builtins =",
  "\tsizeof (static_shell_builtins) / sizeof (struct builtin) - 1;",
  (char *)NULL
};

/* Write out any neccessary opening information for
   STRUCTFILE and EXTERNFILE. */
void
write_file_headers (structfile, externfile)
     FILE *structfile, *externfile;
{
  register int i;

  if (structfile)
    {
      for (i = 0; structfile_header[i]; i++)
    fprintf (structfile, "%s\n", structfile_header[i]);

      fprintf (structfile, "#include \"%s\"\n",
           extern_filename ? extern_filename : "builtext.h");
      fprintf (structfile, "\nstruct builtin static_shell_builtins[] = {\n");
    }

  if (externfile)
    fprintf (externfile,
         "/* %s - The list of builtins found in libbuiltins.a. */\n",
         extern_filename ? extern_filename : "builtext.h");
}

/* Write out any necessary closing information for
   STRUCTFILE and EXTERNFILE. */
void
write_file_footers (structfile, externfile)
     FILE *structfile, *externfile;
{
  register int i;

  /* Write out the footers. */
  if (structfile)
    {
      for (i = 0; structfile_footer[i]; i++)
    fprintf (structfile, "%s\n", structfile_footer[i]);
    }
}

/* Write out the information accumulated in DEFS to
   STRUCTFILE and EXTERNFILE. */
void
write_builtins (defs, structfile, externfile)
     DEF_FILE *defs;
     FILE *structfile, *externfile;
{
  register int i;

  /* Write out the information. */
  if (defs->builtins)
    {
      register BUILTIN_DESC *builtin;

      for (i = 0; i < defs->builtins->sindex; i++)
    {
      builtin = (BUILTIN_DESC *)defs->builtins->array[i];

      /* Write out any #ifdefs that may be there. */
      if (!only_documentation)
        {
          if (builtin->dependencies)
        {
          write_ifdefs (externfile, builtin->dependencies->array);
          write_ifdefs (structfile, builtin->dependencies->array);
        }

          /* Write the extern definition. */
          if (externfile)
        {
          if (builtin->function)
            fprintf (externfile, "extern int %s __P((WORD_LIST *));\n",
                 builtin->function);

          fprintf (externfile, "extern char * const %s_doc[];\n",
               document_name (builtin));
        }

          /* Write the structure definition. */
          if (structfile)
        {
          fprintf (structfile, "  { \"%s\", ", builtin->name);

          if (builtin->function)
            fprintf (structfile, "%s, ", builtin->function);
          else
            fprintf (structfile, "(sh_builtin_func_t *)0x0, ");

          fprintf (structfile, "%s%s%s, %s_doc,\n",
            "BUILTIN_ENABLED | STATIC_BUILTIN",
            (builtin->flags & BUILTIN_FLAG_SPECIAL) ? " | SPECIAL_BUILTIN" : "",
            (builtin->flags & BUILTIN_FLAG_ASSIGNMENT) ? " | ASSIGNMENT_BUILTIN" : "",
            document_name (builtin));

          fprintf
            (structfile, "     \"%s\", (char *)NULL },\n",
             builtin->shortdoc ? builtin->shortdoc : builtin->name);

        }

          if (structfile || separate_helpfiles)
        /* Save away this builtin for later writing of the
           long documentation strings. */
        save_builtin (builtin);

          /* Write out the matching #endif, if neccessary. */
          if (builtin->dependencies)
        {
          if (externfile)
            write_endifs (externfile, builtin->dependencies->array);

          if (structfile)
            write_endifs (structfile, builtin->dependencies->array);
        }
        }

      if (documentation_file)
        {
          fprintf (documentation_file, "@item %s\n", builtin->name);
          write_documentation
        (documentation_file, builtin->longdoc->array, 0, TEXINFO);
        }
    }
    }
}

/* Write out the long documentation strings in BUILTINS to STREAM. */
void
write_longdocs (stream, builtins)
     FILE *stream;
     ARRAY *builtins;
{
  register int i;
  register BUILTIN_DESC *builtin;
  char *dname;
  char *sarray[2];

  for (i = 0; i < builtins->sindex; i++)
    {
      builtin = (BUILTIN_DESC *)builtins->array[i];

      if (builtin->dependencies)
    write_ifdefs (stream, builtin->dependencies->array);

      /* Write the long documentation strings. */
      dname = document_name (builtin);
      fprintf (stream, "char * const %s_doc[] =", dname);

      if (separate_helpfiles)
    {
      int l = strlen (helpfile_directory) + strlen (dname) + 1;
      sarray[0] = (char *)xmalloc (l + 1);
      sprintf (sarray[0], "%s/%s", helpfile_directory, dname);
      sarray[1] = (char *)NULL;
      write_documentation (stream, sarray, 0, STRING_ARRAY);
      free (sarray[0]);
    }
      else
    write_documentation (stream, builtin->longdoc->array, 0, STRING_ARRAY);

      if (builtin->dependencies)
    write_endifs (stream, builtin->dependencies->array);

    }
}

/* Write an #ifdef string saying what needs to be defined (or not defined)
   in order to allow compilation of the code that will follow.
   STREAM is the stream to write the information to,
   DEFINES is a null terminated array of define names.
   If a define is preceded by an `!', then the sense of the test is
   reversed. */
void
write_ifdefs (stream, defines)
     FILE *stream;
     char **defines;
{
  register int i;

  if (!stream)
    return;

  fprintf (stream, "#if ");

  for (i = 0; defines[i]; i++)
    {
      char *def = defines[i];

      if (*def == '!')
    fprintf (stream, "!defined (%s)", def + 1);
      else
    fprintf (stream, "defined (%s)", def);

      if (defines[i + 1])
    fprintf (stream, " && ");
    }
  fprintf (stream, "\n");
}

/* Write an #endif string saying what defines controlled the compilation
   of the immediately preceding code.
   STREAM is the stream to write the information to.
   DEFINES is a null terminated array of define names. */
void
write_endifs (stream, defines)
     FILE *stream;
     char **defines;
{
  register int i;

  if (!stream)
    return;

  fprintf (stream, "#endif /* ");

  for (i = 0; defines[i]; i++)
    {
      fprintf (stream, "%s", defines[i]);

      if (defines[i + 1])
    fprintf (stream, " && ");
    }

  fprintf (stream, " */\n");
}

/* Write DOCUMENTAION to STREAM, perhaps surrounding it with double-quotes
   and quoting special characters in the string. */
void
write_documentation (stream, documentation, indentation, flags)
     FILE *stream;
     char **documentation;
     int indentation, flags;
{
  register int i, j;
  register char *line;
  int string_array, texinfo;

  if (!stream)
    return;

  string_array = flags & STRING_ARRAY;
  if (string_array)
    fprintf (stream, " {\n#if defined (HELP_BUILTIN)\n");

  for (i = 0, texinfo = (flags & TEXINFO); line = documentation[i]; i++)
    {
      /* Allow #ifdef's to be written out verbatim. */
      if (*line == '#')
    {
      if (string_array)
        fprintf (stream, "%s\n", line);
      continue;
    }

      if (string_array)
    fprintf (stream, "  \"");

      if (indentation)
    for (j = 0; j < indentation; j++)
      fprintf (stream, " ");

      if (string_array)
    {
      for (j = 0; line[j]; j++)
        {
          switch (line[j])
        {
        case '\\':
        case '"':
          fprintf (stream, "\\%c", line[j]);
          break;

        default:
          fprintf (stream, "%c", line[j]);
        }
        }

      fprintf (stream, "\",\n");
    }
      else if (texinfo)
    {
      for (j = 0; line[j]; j++)
        {
          switch (line[j])
        {
        case '@':
        case '{':
        case '}':
          fprintf (stream, "@%c", line[j]);
          break;

        default:
          fprintf (stream, "%c", line[j]);
        }
        }
      fprintf (stream, "\n");
    }
      else
    fprintf (stream, "%s\n", line);
    }

  if (string_array)
    fprintf (stream, "#endif /* HELP_BUILTIN */\n  (char *)NULL\n};\n");
}

int
write_helpfiles (builtins)
     ARRAY *builtins;
{
  char *helpfile, *bname;
  FILE *helpfp;
  int i, hdlen;
  BUILTIN_DESC *builtin;    

  i = mkdir ("helpfiles", 0777);
  if (i < 0 && errno != EEXIST)
    {
      fprintf (stderr, "write_helpfiles: helpfiles: cannot create directory\n");
      return -1;
    }

  hdlen = strlen ("helpfiles/");
  for (i = 0; i < builtins->sindex; i++)
    {
      builtin = (BUILTIN_DESC *)builtins->array[i];

      bname = document_name (builtin);
      helpfile = (char *)xmalloc (hdlen + strlen (bname) + 1);
      sprintf (helpfile, "helpfiles/%s", bname);

      helpfp = fopen (helpfile, "w");
      if (helpfp == 0)
    {
      fprintf (stderr, "write_helpfiles: cannot open %s\n", helpfile);
      free (helpfile);
      continue;
    }

      write_documentation (helpfp, builtin->longdoc->array, 4, PLAINTEXT);

      fflush (helpfp);
      fclose (helpfp);
      free (helpfile);
    }
  return 0;
}      
                
static int
_find_in_table (name, name_table)
     char *name, *name_table[];
{
  register int i;

  for (i = 0; name_table[i]; i++)
    if (strcmp (name, name_table[i]) == 0)
      return 1;
  return 0;
}

static int
is_special_builtin (name)
     char *name;
{
  return (_find_in_table (name, special_builtins));
}

static int
is_assignment_builtin (name)
     char *name;
{
  return (_find_in_table (name, assignment_builtins));
}

#if !defined (HAVE_RENAME)
static int
rename (from, to)
     char *from, *to;
{
  unlink (to);
  if (link (from, to) < 0)
    return (-1);
  unlink (from);
  return (0);
}
#endif /* !HAVE_RENAME */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/alias.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/alias.def used in: Step 1057, Step 1059
This file is alias.def, from which is created alias.c
It implements the builtins "alias" and "unalias" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$BUILTIN alias
$FUNCTION alias_builtin
$DEPENDS_ON ALIAS
$PRODUCES alias.c
$SHORT_DOC alias [-p] [name[=value] ... ]
`alias' with no arguments or with the -p option prints the list
of aliases in the form alias NAME=VALUE on standard output.
Otherwise, an alias is defined for each NAME whose VALUE is given.
A trailing space in VALUE causes the next word to be checked for
alias substitution when the alias is expanded.  Alias returns
true unless a NAME is given for which no alias has been defined.
$END

#include <config.h>

#if defined (ALIAS)

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#  include "../bashansi.h"

#  include <stdio.h>
#  include "../shell.h"
#  include "../alias.h"
#  include "common.h"
#  include "bashgetopt.h"

static void print_alias __P((alias_t *));

/* Hack the alias command in a Korn shell way. */
int
alias_builtin (list)
     WORD_LIST *list;
{
  int any_failed, offset, pflag;
  alias_t **alias_list, *t;
  char *name, *value;

  pflag = 0;
  reset_internal_getopt ();
  while ((offset = internal_getopt (list, "p")) != -1)
    {
      switch (offset)
    {
    case 'p':
      pflag = 1;
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }

  list = loptend;

  if (list == 0 || pflag)
    {
      if (aliases == 0)
    return (EXECUTION_SUCCESS);

      alias_list = all_aliases ();

      if (alias_list == 0)
    return (EXECUTION_SUCCESS);

      for (offset = 0; alias_list[offset]; offset++)
    print_alias (alias_list[offset]);

      free (alias_list);    /* XXX - Do not free the strings. */

      if (list == 0)
    return (EXECUTION_SUCCESS);
    }

  any_failed = 0;
  while (list)
    {
      name = list->word->word;

      for (offset = 0; name[offset] && name[offset] != '='; offset++)
    ;

      if (offset && name[offset] == '=')
    {
      name[offset] = '\0';
      value = name + offset + 1;

      add_alias (name, value);
    }
      else
    {
      t = find_alias (name);
      if (t)
        print_alias (t);
      else
        {
          sh_notfound (name);
          any_failed++;
        }
    }
      list = list->next;
    }

  return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
}
#endif /* ALIAS */

$BUILTIN unalias
$FUNCTION unalias_builtin
$DEPENDS_ON ALIAS
$SHORT_DOC unalias [-a] [name ...]
Remove NAMEs from the list of defined aliases.  If the -a option is given,
then remove all alias definitions.
$END

#if defined (ALIAS)
/* Remove aliases named in LIST from the aliases database. */
int
unalias_builtin (list)
     register WORD_LIST *list;
{
  register alias_t *alias;
  int opt, aflag;

  aflag = 0;
  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "a")) != -1)
    {
      switch (opt)
    {
    case 'a':
      aflag = 1;
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }

  list = loptend;

  if (aflag)
    {
      delete_all_aliases ();
      return (EXECUTION_SUCCESS);
    }

  aflag = 0;
  while (list)
    {
      alias = find_alias (list->word->word);

      if (alias)
    remove_alias (alias->name);
      else
    {
      sh_notfound (list->word->word);
      aflag++;
    }

      list = list->next;
    }

  return (aflag ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
}

/* Output ALIAS in such a way as to allow it to be read back in. */
static void
print_alias (alias)
     alias_t *alias;
{
  char *value;

  value = sh_single_quote (alias->value);
  printf ("alias %s=%s\n", alias->name, value);
  free (value);

  fflush (stdout);
}
#endif /* ALIAS */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/bind.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/bind.def used in: Step 1057, Step 1061
This file is bind.def, from which is created bind.c.
It implements the builtin "bind" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES bind.c

#include <config.h>

$BUILTIN bind
$DEPENDS_ON READLINE
$FUNCTION bind_builtin
$SHORT_DOC bind [-lpvsPVS] [-m keymap] [-f filename] [-q name] [-u name] [-r keyseq] [-x keyseq:shell-command] [keyseq:readline-function or readline-command]
Bind a key sequence to a Readline function or a macro, or set
a Readline variable.  The non-option argument syntax is equivalent
to that found in ~/.inputrc, but must be passed as a single argument:
bind '"\C-x\C-r": re-read-init-file'.
bind accepts the following options:
  -m  keymap         Use `keymap' as the keymap for the duration of this
                     command.  Acceptable keymap names are emacs,
                     emacs-standard, emacs-meta, emacs-ctlx, vi, vi-move,
                     vi-command, and vi-insert.
  -l                 List names of functions.
  -P                 List function names and bindings.
  -p                 List functions and bindings in a form that can be
                     reused as input.
  -r  keyseq         Remove the binding for KEYSEQ.
  -x  keyseq:shell-command  Cause SHELL-COMMAND to be executed when
                KEYSEQ is entered.
  -f  filename       Read key bindings from FILENAME.
  -q  function-name  Query about which keys invoke the named function.
  -u  function-name  Unbind all keys which are bound to the named function.
  -V                 List variable names and values
  -v                 List variable names and values in a form that can
                     be reused as input.
  -S                 List key sequences that invoke macros and their values
  -s                 List key sequences that invoke macros and their values
                     in a form that can be reused as input.
$END

#if defined (READLINE)

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>
#include <errno.h>
#if !defined (errno)
extern int errno;
#endif /* !errno */

#include <readline/readline.h>
#include <readline/history.h>

#include "../shell.h"
#include "../bashline.h"
#include "bashgetopt.h"
#include "common.h"

static int query_bindings __P((char *));
static int unbind_command __P((char *));

extern int no_line_editing;

#define BIND_RETURN(x)  do { return_code = x; goto bind_exit; } while (0)

#define LFLAG   0x0001
#define PFLAG   0x0002
#define FFLAG   0x0004
#define VFLAG   0x0008
#define QFLAG   0x0010
#define MFLAG   0x0020
#define RFLAG   0x0040
#define PPFLAG  0x0080
#define VVFLAG  0x0100
#define SFLAG   0x0200
#define SSFLAG  0x0400
#define UFLAG   0x0800
#define XFLAG   0x1000

int
bind_builtin (list)
     WORD_LIST *list;
{
  int return_code;
  Keymap kmap, saved_keymap;
  int flags, opt;
  char *initfile, *map_name, *fun_name, *unbind_name, *remove_seq, *cmd_seq;

  if (no_line_editing)
    return (EXECUTION_FAILURE);

  kmap = saved_keymap = (Keymap) NULL;
  flags = 0;
  initfile = map_name = fun_name = unbind_name = remove_seq = (char *)NULL;
  return_code = EXECUTION_SUCCESS;

  if (!bash_readline_initialized)
    initialize_readline ();

  begin_unwind_frame ("bind_builtin");
  unwind_protect_var (rl_outstream);

  rl_outstream = stdout;

  reset_internal_getopt ();  
  while ((opt = internal_getopt (list, "lvpVPsSf:q:u:m:r:x:")) != EOF)
    {
      switch (opt)
    {
    case 'l':
      flags |= LFLAG;
      break;
    case 'v':
      flags |= VFLAG;
      break;
    case 'p':
      flags |= PFLAG;
      break;
    case 'f':
      flags |= FFLAG;
      initfile = list_optarg;
      break;
    case 'm':
      flags |= MFLAG;
      map_name = list_optarg;
      break;
    case 'q':
      flags |= QFLAG;
      fun_name = list_optarg;
      break;
    case 'u':
      flags |= UFLAG;
      unbind_name = list_optarg;
      break;
    case 'r':
      flags |= RFLAG;
      remove_seq = list_optarg;
      break;
    case 'V':
      flags |= VVFLAG;
      break;
    case 'P':
      flags |= PPFLAG;
      break;
    case 's':
      flags |= SFLAG;
      break;
    case 'S':
      flags |= SSFLAG;
      break;
    case 'x':
      flags |= XFLAG;
      cmd_seq = list_optarg;
      break;
    default:
      builtin_usage ();
      BIND_RETURN (EX_USAGE);
    }
    }

  list = loptend;

  /* First, see if we need to install a special keymap for this
     command.  Then start on the arguments. */

  if ((flags & MFLAG) && map_name)
    {
      kmap = rl_get_keymap_by_name (map_name);
      if (!kmap)
    {
      builtin_error ("`%s': invalid keymap name", map_name);
      BIND_RETURN (EXECUTION_FAILURE);
    }
    }

  if (kmap)
    {
      saved_keymap = rl_get_keymap ();
      rl_set_keymap (kmap);
    }

  /* XXX - we need to add exclusive use tests here.  It doesn't make sense
     to use some of these options together. */
  /* Now hack the option arguments */
  if (flags & LFLAG)
    rl_list_funmap_names ();

  if (flags & PFLAG)
    rl_function_dumper (1);

  if (flags & PPFLAG)
    rl_function_dumper (0);

  if (flags & SFLAG)
    rl_macro_dumper (1);

  if (flags & SSFLAG)
    rl_macro_dumper (0);

  if (flags & VFLAG)
    rl_variable_dumper (1);

  if (flags & VVFLAG)
    rl_variable_dumper (0);

  if ((flags & FFLAG) && initfile)
    {
      if (rl_read_init_file (initfile) != 0)
    {
      builtin_error ("%s: cannot read: %s", initfile, strerror (errno));
      BIND_RETURN (EXECUTION_FAILURE);
    }
    }

  if ((flags & QFLAG) && fun_name)
    return_code = query_bindings (fun_name);

  if ((flags & UFLAG) && unbind_name)
    return_code = unbind_command (unbind_name);

  if ((flags & RFLAG) && remove_seq)
    {
      if (rl_set_key (remove_seq, (rl_command_func_t *)NULL, rl_get_keymap ()) != 0)
    {
      builtin_error ("`%s': cannot unbind", remove_seq);
      BIND_RETURN (EXECUTION_FAILURE);
    }
    }

  if (flags & XFLAG)
    return_code = bind_keyseq_to_unix_command (cmd_seq);

  /* Process the rest of the arguments as binding specifications. */
  while (list)
    {
      rl_parse_and_bind (list->word->word);
      list = list->next;
    }

 bind_exit:
  if (saved_keymap)
    rl_set_keymap (saved_keymap);

  run_unwind_frame ("bind_builtin");

  return (return_code);
}

static int
query_bindings (name)
     char *name;
{
  rl_command_func_t *function;
  char **keyseqs;
  int j;

  function = rl_named_function (name);
  if (function == 0)
    {
      builtin_error ("`%s': unknown function name", name);
      return EXECUTION_FAILURE;
    }

  keyseqs = rl_invoking_keyseqs (function);

  if (!keyseqs)
    {
      printf ("%s is not bound to any keys.\n", name);
      return EXECUTION_FAILURE;
    }

  printf ("%s can be invoked via ", name);
  for (j = 0; j < 5 && keyseqs[j]; j++)
    printf ("\"%s\"%s", keyseqs[j], keyseqs[j + 1] ? ", " : ".\n");
  if (keyseqs[j])
    printf ("...\n");
  strvec_dispose (keyseqs);
  return EXECUTION_SUCCESS;
}

static int
unbind_command (name)
     char *name;
{
  rl_command_func_t *function;

  function = rl_named_function (name);
  if (function == 0)
    {
      builtin_error ("`%s': unknown function name", name);
      return EXECUTION_FAILURE;
    }

  rl_unbind_function_in_map (function, rl_get_keymap ());
  return EXECUTION_SUCCESS;
}
#endif /* READLINE */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/break.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/break.def used in: Step 1057, Step 1063
This file is break.def, from which is created break.c.
It implements the builtins "break" and "continue" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES break.c

$BUILTIN break
$FUNCTION break_builtin
$SHORT_DOC break [n]
Exit from within a FOR, WHILE or UNTIL loop.  If N is specified,
break N levels.
$END
#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../shell.h"
#include "common.h"

extern char *this_command_name;
extern int posixly_correct;

static int check_loop_level __P((void));

/* The depth of while's and until's. */
int loop_level = 0;

/* Non-zero when a "break" instruction is encountered. */
int breaking = 0;

/* Non-zero when we have encountered a continue instruction. */
int continuing = 0;

/* Set up to break x levels, where x defaults to 1, but can be specified
   as the first argument. */
int
break_builtin (list)
     WORD_LIST *list;
{
  intmax_t newbreak;

  if (check_loop_level () == 0)
    return (EXECUTION_SUCCESS);

  newbreak = get_numeric_arg (list, 1);

  if (newbreak <= 0)
    {
      sh_erange (list->word->word, "loop count");
      breaking = loop_level;
      return (EXECUTION_FAILURE);
    }

  if (newbreak > loop_level)
    newbreak = loop_level;

  breaking = newbreak;

  return (EXECUTION_SUCCESS);
}

$BUILTIN continue
$FUNCTION continue_builtin
$SHORT_DOC continue [n]
Resume the next iteration of the enclosing FOR, WHILE or UNTIL loop.
If N is specified, resume at the N-th enclosing loop.
$END

/* Set up to continue x levels, where x defaults to 1, but can be specified
   as the first argument. */
int
continue_builtin (list)
     WORD_LIST *list;
{
  intmax_t newcont;

  if (check_loop_level () == 0)
    return (EXECUTION_SUCCESS);

  newcont = get_numeric_arg (list, 1);

  if (newcont <= 0)
    {
      sh_erange (list->word->word, "loop count");
      breaking = loop_level;
      return (EXECUTION_FAILURE);
    }

  if (newcont > loop_level)
    newcont = loop_level;

  continuing = newcont;

  return (EXECUTION_SUCCESS);
}

/* Return non-zero if a break or continue command would be okay.
   Print an error message if break or continue is meaningless here. */
static int
check_loop_level ()
{
#if defined (BREAK_COMPLAINS)
  if (loop_level == 0 && posixly_correct == 0)
    builtin_error ("only meaningful in a `for', `while', or `until' loop");
#endif /* BREAK_COMPLAINS */

  return (loop_level);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/builtin.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/builtin.def used in: Step 1057, Step 1065
This file is builtin.def, from which is created builtin.c.
It implements the builtin "builtin" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES builtin.c

$BUILTIN builtin
$FUNCTION builtin_builtin
$SHORT_DOC builtin [shell-builtin [arg ...]]
Run a shell builtin.  This is useful when you wish to rename a
shell builtin to be a function, but need the functionality of the
builtin within the function itself.
$END
#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../shell.h"
#include "common.h"
#include "bashgetopt.h"

extern char *this_command_name;

/* Run the command mentioned in list directly, without going through the
   normal alias/function/builtin/filename lookup process. */
int
builtin_builtin (list)
     WORD_LIST *list;
{
  sh_builtin_func_t *function;
  register char *command;

  if (no_options (list))
    return (EX_USAGE);
  list = loptend;   /* skip over possible `--' */

  if (list == 0)
    return (EXECUTION_SUCCESS);

  command = list->word->word;
#if defined (DISABLED_BUILTINS)
  function = builtin_address (command);
#else /* !DISABLED_BUILTINS */
  function = find_shell_builtin (command);
#endif /* !DISABLED_BUILTINS */

  if (!function)
    {
      builtin_error ("%s: not a shell builtin", command);
      return (EXECUTION_FAILURE);
    }
  else
    {
      this_command_name = command;
      list = list->next;
      return ((*function) (list));
    }
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/cd.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/cd.def used in: Step 1057, Step 1067
This file is cd.def, from which is created cd.c.  It implements the
builtins "cd" and "pwd" in Bash.

Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES cd.c
#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../bashtypes.h"
#include "posixdir.h"
#include "posixstat.h"
#ifndef _MINIX
#include <sys/param.h>
#endif

#include <stdio.h>

#include "../bashansi.h"

#include <errno.h>
#include <tilde/tilde.h>

#include "../shell.h"
#include "../flags.h"
#include "maxpath.h"
#include "common.h"
#include "bashgetopt.h"

#if !defined (errno)
extern int errno;
#endif /* !errno */

extern int posixly_correct;
extern int array_needs_making;
extern char *bash_getcwd_errstr;

static int bindpwd __P((int));
static int change_to_directory __P((char *, int));

static char *cdspell __P((char *));

/* Change this to 1 to get cd spelling correction by default. */
int cdspelling = 0;

int cdable_vars;

$BUILTIN cd
$FUNCTION cd_builtin
$SHORT_DOC cd [-L|-P] [dir]
Change the current directory to DIR.  The variable $HOME is the
default DIR.  The variable CDPATH defines the search path for
the directory containing DIR.  Alternative directory names in CDPATH
are separated by a colon (:).  A null directory name is the same as
the current directory, i.e. `.'.  If DIR begins with a slash (/),
then CDPATH is not used.  If the directory is not found, and the
shell option `cdable_vars' is set, then try the word as a variable
name.  If that variable has a value, then cd to the value of that
variable.  The -P option says to use the physical directory structure
instead of following symbolic links; the -L option forces symbolic links
to be followed.
$END

static int
bindpwd (no_symlinks)
     int no_symlinks;
{
  char *dirname, *pwdvar;
  int old_anm;
  SHELL_VAR *tvar;

#define tcwd the_current_working_directory
  dirname = tcwd ? (no_symlinks ? sh_physpath (tcwd, 0) : tcwd)
         : get_working_directory ("cd");
#undef tcwd

  old_anm = array_needs_making;
  pwdvar = get_string_value ("PWD");

  tvar = bind_variable ("OLDPWD", pwdvar);
  if (old_anm == 0 && array_needs_making && exported_p (tvar))
    {
      update_export_env_inplace ("OLDPWD=", 7, pwdvar);
      array_needs_making = 0;
    }

  tvar = bind_variable ("PWD", dirname);
  if (old_anm == 0 && array_needs_making && exported_p (tvar))
    {
      update_export_env_inplace ("PWD=", 4, dirname);
      array_needs_making = 0;
    }

  if (dirname && dirname != the_current_working_directory)
    free (dirname);
  return (EXECUTION_SUCCESS);
}

/* Call get_working_directory to reset the value of
   the_current_working_directory () */
static char *
resetpwd ()
{
  char *tdir;
      
  FREE (the_current_working_directory);
  the_current_working_directory = (char *)NULL;
  tdir = get_working_directory ("cd");
  return (tdir);
}

#define LCD_DOVARS  0x001
#define LCD_DOSPELL 0x002
#define LCD_PRINTPATH   0x004
#define LCD_FREEDIRNAME 0x010

/* This builtin is ultimately the way that all user-visible commands should
   change the current working directory.  It is called by cd_to_string (),
   so the programming interface is simple, and it handles errors and
   restrictions properly. */
int
cd_builtin (list)
     WORD_LIST *list;
{
  char *dirname, *cdpath, *path, *temp;
  int path_index, no_symlinks, opt, lflag;

#if defined (RESTRICTED_SHELL)
  if (restricted)
    {
      sh_restricted ((char *)NULL);
      return (EXECUTION_FAILURE);
    }
#endif /* RESTRICTED_SHELL */

  no_symlinks = no_symbolic_links;
  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "LP")) != -1)
    {
      switch (opt)
    {
    case 'P':
      no_symlinks = 1;
      break;
    case 'L':
      no_symlinks = 0;
      break;
    default:
      builtin_usage ();
      return (EXECUTION_FAILURE);
    }
    }
  list = loptend;

  lflag = (cdable_vars ? LCD_DOVARS : 0) |
      ((interactive && cdspelling) ? LCD_DOSPELL : 0);

  if (list == 0)
    {
      /* `cd' without arguments is equivalent to `cd $HOME' */
      dirname = get_string_value ("HOME");

      if (dirname == 0)
    {
      builtin_error ("HOME not set");
      return (EXECUTION_FAILURE);
    }
      lflag = 0;
    }
  else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
    {
      /* This is `cd -', equivalent to `cd $OLDPWD' */
      dirname = get_string_value ("OLDPWD");

      if (dirname == 0)
    {
      builtin_error ("OLDPWD not set");
      return (EXECUTION_FAILURE);
    }
      lflag = interactive ? LCD_PRINTPATH : 0;
    }
  else if (absolute_pathname (list->word->word))
    dirname = list->word->word;
  else if (cdpath = get_string_value ("CDPATH"))
    {
      dirname = list->word->word;

      /* Find directory in $CDPATH. */
      path_index = 0;
      while (path = extract_colon_unit (cdpath, &path_index))
    {
      /* OPT is 1 if the path element is non-empty */
      opt = path[0] != '\0';
      temp = sh_makepath (path, dirname, MP_DOTILDE);
      free (path);

      if (change_to_directory (temp, no_symlinks))
        {
          /* POSIX.2 says that if a nonempty directory from CDPATH
         is used to find the directory to change to, the new
         directory name is echoed to stdout, whether or not
         the shell is interactive. */
          if (opt)
        printf ("%s\n", no_symlinks ? temp : the_current_working_directory);

          free (temp);
          /* Posix.2 says that after using CDPATH, the resultant
         value of $PWD will not contain `.' or `..'. */
          return (bindpwd (posixly_correct || no_symlinks));
        }
      else
        free (temp);
    }

      /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
     try the current directory, so we just punt now with an error
     message if POSIXLY_CORRECT is non-zero.  The check for cdpath[0]
     is so we don't mistakenly treat a CDPATH value of "" as not
     specifying the current directory. */
      if (posixly_correct && cdpath[0])
    {
      builtin_error ("%s: %s", dirname, strerror (ENOENT));
      return (EXECUTION_FAILURE);
    }
    }
  else
    dirname = list->word->word;

  /* When we get here, DIRNAME is the directory to change to.  If we
     chdir successfully, just return. */
  if (change_to_directory (dirname, no_symlinks))
    {
      if (lflag & LCD_PRINTPATH)
    printf ("%s\n", dirname);
      return (bindpwd (no_symlinks));
    }

  /* If the user requests it, then perhaps this is the name of
     a shell variable, whose value contains the directory to
     change to. */
  if (lflag & LCD_DOVARS)
    {
      temp = get_string_value (dirname);
      if (temp && change_to_directory (temp, no_symlinks))
    {
      printf ("%s\n", temp);
      return (bindpwd (no_symlinks));
    }
    }

  /* If the user requests it, try to find a directory name similar in
     spelling to the one requested, in case the user made a simple
     typo.  This is similar to the UNIX 8th and 9th Edition shells. */
  if (lflag & LCD_DOSPELL)
    {
      temp = cdspell (dirname);
      if (temp && change_to_directory (temp, no_symlinks))
    {
      printf ("%s\n", temp);
      return (bindpwd (no_symlinks));
    }
      else
    FREE (temp);
    }

  builtin_error ("%s: %s", dirname, strerror (errno));
  return (EXECUTION_FAILURE);
}

$BUILTIN pwd
$FUNCTION pwd_builtin
$SHORT_DOC pwd [-PL]
Print the current working directory.  With the -P option, pwd prints
the physical directory, without any symbolic links; the -L option
makes pwd follow symbolic links.
$END

/* Non-zero means that pwd always prints the physical directory, without
   symbolic links. */
static int verbatim_pwd;

/* Print the name of the current working directory. */
int
pwd_builtin (list)
     WORD_LIST *list;
{
  char *directory;
  int opt;

  verbatim_pwd = no_symbolic_links;
  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "LP")) != -1)
    {
      switch (opt)
    {
    case 'P':
      verbatim_pwd = 1;
      break;
    case 'L':
      verbatim_pwd = 0;
      break;
    default:
      builtin_usage ();
      return (EXECUTION_FAILURE);
    }
    }
  list = loptend;

#define tcwd the_current_working_directory

  directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd)
           : get_working_directory ("pwd");
#undef tcwd

  if (directory)
    {
      printf ("%s\n", directory);
      if (directory != the_current_working_directory)
    free (directory);
      fflush (stdout);
      if (ferror (stdout))
    {
      builtin_error ("write error: %s", strerror (errno));
      return (EXECUTION_FAILURE);
    }
      return (EXECUTION_SUCCESS);
    }
  else
    return (EXECUTION_FAILURE);
}

/* Do the work of changing to the directory NEWDIR.  Handle symbolic
   link following, etc.  This function *must* return with
   the_current_working_directory either set to NULL (in which case
   getcwd() will eventually be called), or set to a string corresponding
   to the working directory.  Return 1 on success, 0 on failure. */

static int
change_to_directory (newdir, nolinks)
     char *newdir;
     int nolinks;
{
  char *t, *tdir;
  int err, canon_failed;

  tdir = (char *)NULL;

  if (the_current_working_directory == 0)
    {
      t = get_working_directory ("chdir");
      FREE (t);
    }

  t = make_absolute (newdir, the_current_working_directory);

  /* TDIR is either the canonicalized absolute pathname of NEWDIR
     (nolinks == 0) or the absolute physical pathname of NEWDIR
     (nolinks != 0). */
  tdir = nolinks ? sh_physpath (t, 0)
         : sh_canonpath (t, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);

  /* Use the canonicalized version of NEWDIR, or, if canonicalization
     failed, use the non-canonical form. */
  canon_failed = 0;
  if (tdir && *tdir)
    free (t);
  else
    {
      FREE (tdir);
      tdir = t;
      canon_failed = 1;
    }

  /* In POSIX mode, if we're resolving symlinks logically and sh_canonpath
     returns NULL (because it checks the path, it will return NULL if the
     resolved path doesn't exist), fail immediately. */
  if (posixly_correct && nolinks == 0 && canon_failed)
    {
      errno = ENOENT;
      return (0);
    }

  /* If the chdir succeeds, update the_current_working_directory. */
  if (chdir (nolinks ? newdir : tdir) == 0)
    {
      /* If canonicalization failed, but the chdir succeeded, reset the
     shell's idea of the_current_working_directory. */
      if (canon_failed)
    resetpwd ();
      else
    {
      FREE (the_current_working_directory);
      the_current_working_directory = tdir;
    }

      return (1);
    }

  /* We failed to change to the appropriate directory name.  If we tried
     what the user passed (nolinks != 0), punt now. */
  if (nolinks)
    return (0);

  err = errno;
  free (tdir);

  /* We're not in physical mode (nolinks == 0), but we failed to change to
     the canonicalized directory name (TDIR).  Try what the user passed
     verbatim. If we succeed, reinitialize the_current_working_directory. */
  if (chdir (newdir) == 0)
    {
      tdir = resetpwd ();
      FREE (tdir);

      return (1);
    }
  else
    {
      errno = err;
      return (0);
    }
}

/* Code for cd spelling correction.  Original patch submitted by
   Neil Russel (caret@c-side.com). */

static char *
cdspell (dirname)
     char *dirname;
{
  int n;
  char *guess;

  n = (strlen (dirname) * 3 + 1) / 2 + 1;
  guess = (char *)xmalloc (n);

  switch (spname (dirname, guess))
    {
    case -1:
    default:
      free (guess);
      return (char *)NULL;
    case 0:
    case 1:
      return guess;
    }
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/colon.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/colon.def used in: Step 1057, Step 1069
This file is colon.def, from which is created colon.c.
It implements the builtin ":" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES colon.c

$BUILTIN :
$DOCNAME colon
$FUNCTION colon_builtin
$SHORT_DOC :
No effect; the command does nothing.  A zero exit code is returned.
$END

$BUILTIN true
$FUNCTION colon_builtin
$SHORT_DOC true
Return a successful result.
$END

$BUILTIN false
$FUNCTION false_builtin
$SHORT_DOC false
Return an unsuccessful result.
$END

/* Return a successful result. */
int
colon_builtin (ignore)
     char *ignore;
{
  return (0);
}

/* Return an unsuccessful result. */
int
false_builtin (ignore)
     char *ignore;
{
  return (1);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/command.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/command.def used in: Step 1057, Step 1071
This file is command.def, from which is created command.c.
It implements the builtin "command" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES command.c

$BUILTIN command
$FUNCTION command_builtin
$SHORT_DOC command [-pVv] command [arg ...]
Runs COMMAND with ARGS ignoring shell functions.  If you have a shell
function called `ls', and you wish to call the command `ls', you can
say "command ls".  If the -p option is given, a default value is used
for PATH that is guaranteed to find all of the standard utilities.  If
the -V or -v option is given, a string is printed describing COMMAND.
The -V option produces a more verbose description.
$END

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../bashansi.h"

#include "../shell.h"
#include "../execute_cmd.h"
#include "../flags.h"
#include "bashgetopt.h"
#include "common.h"

#if defined (_CS_PATH) && defined (HAVE_CONFSTR) && !HAVE_DECL_CONFSTR
extern size_t confstr __P((int, char *, size_t));
#endif

extern int subshell_environment;

static void restore_path __P((char *));
static char *get_standard_path __P((void));

/* Run the commands mentioned in LIST without paying attention to shell
   functions. */
int
command_builtin (list)
     WORD_LIST *list;
{
  int result, verbose, use_standard_path, opt;
  char *old_path, *standard_path;
  COMMAND *command;

  verbose = use_standard_path = 0;
  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "pvV")) != -1)
    {
      switch (opt)
    {
    case 'p':
      use_standard_path = 1;
      break;
    case 'V':
      verbose = CDESC_SHORTDESC;    /* look in common.h for constants */
      break;
    case 'v':
      verbose = CDESC_REUSABLE; /* ditto */
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }
  list = loptend;

  if (list == 0)
    return (EXECUTION_SUCCESS);

  if (verbose)
    {
      int found, any_found;

      for (any_found = 0; list; list = list->next)
    {
      found = describe_command (list->word->word, verbose);

      if (found == 0)
        sh_notfound (list->word->word);

      any_found += found;
    }
      return (any_found ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
    }

#if defined (RESTRICTED_SHELL)
  if (use_standard_path && restricted)
    {
      sh_restricted ("-p");
      return (EXECUTION_FAILURE);
    }
#endif

  begin_unwind_frame ("command_builtin");

  /* We don't want this to be reparsed (consider command echo 'foo &'), so
     just make a simple_command structure and call execute_command with it. */
  if (use_standard_path)
    {      
      old_path = get_string_value ("PATH");
      /* If old_path is NULL, $PATH is unset.  If so, we want to make sure
     it's unset after this command completes. */
      if (old_path)
    old_path = savestring (old_path);
      add_unwind_protect ((Function *)restore_path, old_path);

      standard_path = get_standard_path ();
      bind_variable ("PATH", standard_path ? standard_path : "");
      FREE (standard_path);
    }

#define COMMAND_BUILTIN_FLAGS (CMD_NO_FUNCTIONS | CMD_INHIBIT_EXPANSION | CMD_COMMAND_BUILTIN)

  command = make_bare_simple_command ();
  command->value.Simple->words = (WORD_LIST *)copy_word_list (list);
  command->value.Simple->redirects = (REDIRECT *)NULL;
  command->flags |= COMMAND_BUILTIN_FLAGS;
  command->value.Simple->flags |= COMMAND_BUILTIN_FLAGS;
#if 0
  /* This breaks for things like ( cd /tmp ; command z ababa ; echo next )
     or $(command echo a ; command echo b;) or even
     { command echo a; command echo b; } & */
  /* If we're in a subshell, see if we can get away without forking
     again, since we've already forked to run this builtin. */
  if (subshell_environment)
    {
      command->flags |= CMD_NO_FORK;
      command->value.Simple->flags |= CMD_NO_FORK;
    }
#endif
  add_unwind_protect ((char *)dispose_command, command);
  result = execute_command (command);

  run_unwind_frame ("command_builtin");

  return (result);
}

/* Restore the value of the $PATH variable after replacing it when
   executing `command -p'. */
static void
restore_path (var)
     char *var;
{
  if (var)
    {
      bind_variable ("PATH", var);
      free (var);
    }
  else
    unbind_variable ("PATH");
}

/* Return a value for PATH that is guaranteed to find all of the standard
   utilities.  This uses Posix.2 configuration variables, if present.  It
   uses a value defined in config.h as a last resort. */
static char *
get_standard_path ()
{
#if defined (_CS_PATH) && defined (HAVE_CONFSTR)
  char *p;
  size_t len;

  len = (size_t)confstr (_CS_PATH, (char *)NULL, (size_t)0);
  if (len > 0)
    {
      p = (char *)xmalloc (len + 2);
      *p = '\0';
      confstr (_CS_PATH, p, len);
      return (p);
    }
  else
    return (savestring (STANDARD_UTILS_PATH));
#else /* !_CS_PATH || !HAVE_CONFSTR  */
#  if defined (CS_PATH)
  return (savestring (CS_PATH));
#  else
  return (savestring (STANDARD_UTILS_PATH));
#  endif /* !CS_PATH */
#endif /* !_CS_PATH || !HAVE_CONFSTR */
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/complete.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/complete.def used in: Step 1057, Step 1073
This file is complete.def, from which is created complete.c.
It implements the builtins "complete" and "compgen" in Bash.

Copyright (C) 1999-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES complete.c

$BUILTIN complete
$DEPENDS_ON PROGRAMMABLE_COMPLETION
$FUNCTION complete_builtin
$SHORT_DOC complete [-abcdefgjksuv] [-pr] [-o option] [-A action] [-G globpat] [-W wordlist] [-P prefix] [-S suffix] [-X filterpat] [-F function] [-C command] [name ...]
For each NAME, specify how arguments are to be completed.
If the -p option is supplied, or if no options are supplied, existing
completion specifications are printed in a way that allows them to be
reused as input.  The -r option removes a completion specification for
each NAME, or, if no NAMEs are supplied, all completion specifications.
$END

#include <config.h>

#include <stdio.h>

#include "../bashtypes.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "../bashansi.h"

#include "../shell.h"
#include "../builtins.h"
#include "../pcomplete.h"

#include "common.h"
#include "bashgetopt.h"

#include <readline/readline.h>

#define STRDUP(x)       ((x) ? savestring (x) : (char *)NULL)

static int find_compact __P((char *));
static int find_compopt __P((char *));

static int build_actions __P((WORD_LIST *, int *, int *, unsigned long *, unsigned long *));

static int remove_cmd_completions __P((WORD_LIST *));

static int print_one_completion __P((char *, COMPSPEC *));
static int print_compitem __P((BUCKET_CONTENTS *));
static void print_all_completions __P((void));
static int print_cmd_completions __P((WORD_LIST *));

static char *Garg, *Warg, *Parg, *Sarg, *Xarg, *Farg, *Carg;

static struct _compacts {
  char *actname;
  int actflag;
  int actopt;
} compacts[] = {
  { "alias",     CA_ALIAS,     'a' },
  { "arrayvar",  CA_ARRAYVAR,   0 },
  { "binding",   CA_BINDING,    0 },
  { "builtin",   CA_BUILTIN,   'b' },
  { "command",   CA_COMMAND,   'c' },
  { "directory", CA_DIRECTORY, 'd' },
  { "disabled",  CA_DISABLED,   0 },
  { "enabled",   CA_ENABLED,    0 },
  { "export",    CA_EXPORT,    'e' },
  { "file",      CA_FILE,      'f' },
  { "function",  CA_FUNCTION,   0 },
  { "helptopic", CA_BUILTIN,  0 },  /* for now */
  { "hostname",  CA_HOSTNAME,   0 },
  { "group",     CA_GROUP,     'g' },
  { "job",       CA_JOB,       'j' },
  { "keyword",   CA_KEYWORD,   'k' },
  { "running",   CA_RUNNING,    0 },
  { "service",   CA_SERVICE,   's' },
  { "setopt",    CA_SETOPT,     0 },
  { "shopt",     CA_SHOPT,      0 },
  { "signal",    CA_SIGNAL,     0 },
  { "stopped",   CA_STOPPED,    0 },
  { "user",      CA_USER,      'u' },
  { "variable",  CA_VARIABLE,  'v' },
  { (char *)NULL, 0, 0 },
};

/* This should be a STRING_INT_ALIST */
static struct _compopt {
  char *optname;
  int optflag;
} compopts[] = {
  { "default",  COPT_DEFAULT },
  { "dirnames", COPT_DIRNAMES },
  { "filenames",COPT_FILENAMES},
  { "nospace",  COPT_NOSPACE },
  { (char *)NULL, 0 },
};

static int
find_compact (name)
     char *name;
{
  register int i;

  for (i = 0; compacts[i].actname; i++)
    if (STREQ (name, compacts[i].actname))
      return i;
  return -1;
}

static int
find_compopt (name)
     char *name;
{
  register int i;

  for (i = 0; compopts[i].optname; i++)
    if (STREQ (name, compopts[i].optname))
      return i;
  return -1;
}

/* Build the actions and compspec options from the options specified in LIST.
   ACTP is a pointer to an unsigned long in which to place the bitmap of
   actions.  OPTP is a pointer to an unsigned long in which to place the
   btmap of compspec options (arguments to `-o').  PP, if non-null, gets 1
   if -p is supplied; RP, if non-null, gets 1 if -r is supplied.
   If either is null, the corresponding option generates an error.
   This also sets variables corresponding to options that take arguments as
   a side effect; the caller should ensure that those variables are set to
   NULL before calling build_actions.  Return value:
    EX_USAGE = bad option
    EXECUTION_SUCCESS = some options supplied
    EXECUTION_FAILURE = no options supplied
*/

static int
build_actions (list, pp, rp, actp, optp)
     WORD_LIST *list;
     int *pp, *rp;
     unsigned long *actp, *optp;
{
  int opt, ind, opt_given;
  unsigned long acts, copts;

  acts = copts = (unsigned long)0L;
  opt_given = 0;

  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "abcdefgjko:prsuvA:G:W:P:S:X:F:C:")) != -1)
    {
      opt_given = 1;
      switch (opt)
    {
    case 'r':
      if (rp)
        {
          *rp = 1;
          break;
        }
      else
        {
          sh_invalidopt ("-r");
          builtin_usage ();
          return (EX_USAGE);
        }

    case 'p':
      if (pp)
        {
          *pp = 1;
          break;
        }
      else
        {
          sh_invalidopt ("-p");
          builtin_usage ();
          return (EX_USAGE);
        }

    case 'a':
      acts |= CA_ALIAS;
      break;
    case 'b':
      acts |= CA_BUILTIN;
      break;
    case 'c':
      acts |= CA_COMMAND;
      break;
    case 'd':
      acts |= CA_DIRECTORY;
      break;
    case 'e':
      acts |= CA_EXPORT;
      break;
    case 'f':
      acts |= CA_FILE;
      break;
    case 'g':
      acts |= CA_GROUP;
      break;
    case 'j':
      acts |= CA_JOB;
      break;
    case 'k':
      acts |= CA_KEYWORD;
      break;
    case 's':
      acts |= CA_SERVICE;
      break;
    case 'u':
      acts |= CA_USER;
      break;
    case 'v':
      acts |= CA_VARIABLE;
      break;
    case 'o':
      ind = find_compopt (list_optarg);
      if (ind < 0)
        {
          sh_invalidoptname (list_optarg);
          return (EX_USAGE);
        }
      copts |= compopts[ind].optflag;
      break;
    case 'A':
      ind = find_compact (list_optarg);
      if (ind < 0)
        {
          builtin_error ("%s: invalid action name", list_optarg);
          return (EX_USAGE);
        }
      acts |= compacts[ind].actflag;
      break;
    case 'C':
      Carg = list_optarg;
      break;
    case 'F':
      Farg = list_optarg;
      break;
    case 'G':
      Garg = list_optarg;
      break;
    case 'P':
      Parg = list_optarg;
      break;
    case 'S':
      Sarg = list_optarg;
      break;
    case 'W':
      Warg = list_optarg;
      break;
    case 'X':
      Xarg = list_optarg;
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }

  *actp = acts;
  *optp = copts;

  return (opt_given ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
}

/* Add, remove, and display completion specifiers. */
int
complete_builtin (list)
     WORD_LIST *list;
{
  int opt_given, pflag, rflag, rval;
  unsigned long acts, copts;
  COMPSPEC *cs;

  if (list == 0)
    {
      print_all_completions ();
      return (EXECUTION_SUCCESS);
    }

  opt_given = pflag = rflag = 0;
  acts = copts = (unsigned long)0L;
  Garg = Warg = Parg = Sarg = Xarg = Farg = Carg = (char *)NULL;
  cs = (COMPSPEC *)NULL;

  /* Build the actions from the arguments.  Also sets the [A-Z]arg variables
     as a side effect if they are supplied as options. */
  rval = build_actions (list, &pflag, &rflag, &acts, &copts);
  if (rval == EX_USAGE)
    return (rval);
  opt_given = rval != EXECUTION_FAILURE;

  list = loptend;

  /* -p overrides everything else */
  if (pflag || (list == 0 && opt_given == 0))
    {
      if (list == 0)
    {
      print_all_completions ();
      return (EXECUTION_SUCCESS);
    }
      return (print_cmd_completions (list));
    }

  /* next, -r overrides everything else. */
  if (rflag)
    {
      if (list == 0)
    {
      progcomp_flush ();
      return (EXECUTION_SUCCESS);
    }
      return (remove_cmd_completions (list));
    }

  if (list == 0 && opt_given)
    {
      builtin_usage ();
      return (EX_USAGE);
    }

  /* If we get here, we need to build a compspec and add it for each
     remaining argument. */
  cs = compspec_create ();
  cs->actions = acts;
  cs->options = copts;

  cs->globpat = STRDUP (Garg);
  cs->words = STRDUP (Warg);
  cs->prefix = STRDUP (Parg);
  cs->suffix = STRDUP (Sarg);
  cs->funcname = STRDUP (Farg);
  cs->command = STRDUP (Carg);
  cs->filterpat = STRDUP (Xarg);

  for (rval = EXECUTION_SUCCESS ; list; list = list->next)
    {
      /* Add CS as the compspec for the specified commands. */
      if (progcomp_insert (list->word->word, cs) == 0)
    rval = EXECUTION_FAILURE;
    }

  return (rval);
}

static int
remove_cmd_completions (list)
     WORD_LIST *list;
{
  WORD_LIST *l;
  int ret;

  for (ret = EXECUTION_SUCCESS, l = list; l; l = l->next)
    {
      if (progcomp_remove (l->word->word) == 0)
    {
      builtin_error ("%s: no completion specification", l->word->word);
      ret = EXECUTION_FAILURE;
    }
    }
  return ret;
}

#define SQPRINTARG(a, f) \
  do { \
    if (a) \
      { \
        x = sh_single_quote (a); \
    printf ("%s %s ", f, x); \
    free (x); \
      } \
  } while (0)

#define PRINTARG(a, f) \
  do { \
    if (a) \
      printf ("%s %s ", f, a); \
  } while (0)

#define PRINTOPT(a, f) \
  do { \
    if (acts & a) \
      printf ("%s ", f); \
  } while (0)

#define PRINTACT(a, f) \
  do { \
    if (acts & a) \
      printf ("-A %s ", f); \
  } while (0)

#define PRINTCOMPOPT(a, f) \
  do { \
    if (copts & a) \
      printf ("-o %s ", f); \
  } while (0)

static int
print_one_completion (cmd, cs)
     char *cmd;
     COMPSPEC *cs;
{
  unsigned long acts, copts;
  char *x;

  printf ("complete ");

  copts = cs->options;

  /* First, print the -o options. */
  PRINTCOMPOPT (COPT_DEFAULT, "default");
  PRINTCOMPOPT (COPT_DIRNAMES, "dirnames");
  PRINTCOMPOPT (COPT_FILENAMES, "filenames");
  PRINTCOMPOPT (COPT_NOSPACE, "nospace");

  acts = cs->actions;

  /* simple flags next */
  PRINTOPT (CA_ALIAS, "-a");
  PRINTOPT (CA_BUILTIN, "-b");
  PRINTOPT (CA_COMMAND, "-c");
  PRINTOPT (CA_DIRECTORY, "-d");
  PRINTOPT (CA_EXPORT, "-e");
  PRINTOPT (CA_FILE, "-f");
  PRINTOPT (CA_GROUP, "-g");
  PRINTOPT (CA_JOB, "-j");
  PRINTOPT (CA_KEYWORD, "-k");
  PRINTOPT (CA_SERVICE, "-s");
  PRINTOPT (CA_USER, "-u");
  PRINTOPT (CA_VARIABLE, "-v");

  /* now the rest of the actions */
  PRINTACT (CA_ARRAYVAR, "arrayvar");
  PRINTACT (CA_BINDING, "binding");
  PRINTACT (CA_DISABLED, "disabled");
  PRINTACT (CA_ENABLED, "enabled");
  PRINTACT (CA_FUNCTION, "function");
  PRINTACT (CA_HELPTOPIC, "helptopic");
  PRINTACT (CA_HOSTNAME, "hostname");
  PRINTACT (CA_RUNNING, "running");
  PRINTACT (CA_SETOPT, "setopt");
  PRINTACT (CA_SHOPT, "shopt");
  PRINTACT (CA_SIGNAL, "signal");
  PRINTACT (CA_STOPPED, "stopped");

  /* now the rest of the arguments */

  /* arguments that require quoting */
  SQPRINTARG (cs->globpat, "-G");
  SQPRINTARG (cs->words, "-W");
  SQPRINTARG (cs->prefix, "-P");
  SQPRINTARG (cs->suffix, "-S");
  SQPRINTARG (cs->filterpat, "-X");

  /* simple arguments that don't require quoting */
  PRINTARG (cs->funcname, "-F");
  PRINTARG (cs->command, "-C");

  printf ("%s\n", cmd);

  return (0);
}

static int
print_compitem (item)
     BUCKET_CONTENTS *item;
{
  COMPSPEC *cs;
  char *cmd;

  cmd = item->key;
  cs = (COMPSPEC *)item->data;

  return (print_one_completion (cmd, cs));
}

static void
print_all_completions ()
{
  progcomp_walk (print_compitem);
}

static int
print_cmd_completions (list)
     WORD_LIST *list;
{
  WORD_LIST *l;
  COMPSPEC *cs;
  int ret;

  for (ret = EXECUTION_SUCCESS, l = list; l; l = l->next)
    {
      cs = progcomp_search (l->word->word);
      if (cs)
    print_one_completion (l->word->word, cs);
      else
    {
      builtin_error ("%s: no completion specification", l->word->word);
      ret = EXECUTION_FAILURE;
    }
    }
  return (ret);
}

$BUILTIN compgen
$DEPENDS_ON PROGRAMMABLE_COMPLETION
$FUNCTION compgen_builtin
$SHORT_DOC compgen [-abcdefgjksuv] [-o option] [-A action] [-G globpat] [-W wordlist] [-P prefix] [-S suffix] [-X filterpat] [-F function] [-C command] [word]
Display the possible completions depending on the options.  Intended
to be used from within a shell function generating possible completions.
If the optional WORD argument is supplied, matches against WORD are
generated.
$END

int
compgen_builtin (list)
     WORD_LIST *list;
{
  int rval;
  unsigned long acts, copts;
  COMPSPEC *cs;
  STRINGLIST *sl;
  char *word;

  if (list == 0)
    return (EXECUTION_SUCCESS);

  acts = copts = (unsigned long)0L;
  Garg = Warg = Parg = Sarg = Xarg = Farg = Carg = (char *)NULL;
  cs = (COMPSPEC *)NULL;

  /* Build the actions from the arguments.  Also sets the [A-Z]arg variables
     as a side effect if they are supplied as options. */
  rval = build_actions (list, (int *)NULL, (int *)NULL, &acts, &copts);
  if (rval == EX_USAGE)
    return (rval);
  if (rval == EXECUTION_FAILURE)
    return (EXECUTION_SUCCESS);

  list = loptend;

  word = (list && list->word) ? list->word->word : "";

  if (Farg)
    internal_warning ("compgen: -F option may not work as you expect");
  if (Carg)
    internal_warning ("compgen: -C option may not work as you expect");

  /* If we get here, we need to build a compspec and evaluate it. */
  cs = compspec_create ();
  cs->actions = acts;
  cs->options = copts;
  cs->refcount = 1;

  cs->globpat = STRDUP (Garg);
  cs->words = STRDUP (Warg);
  cs->prefix = STRDUP (Parg);
  cs->suffix = STRDUP (Sarg);
  cs->funcname = STRDUP (Farg);
  cs->command = STRDUP (Carg);
  cs->filterpat = STRDUP (Xarg);

  rval = EXECUTION_FAILURE;
  sl = gen_compspec_completions (cs, "compgen", word, 0, 0);

  /* This isn't perfect, but it's the best we can do, given what readline
     exports from its set of completion utility functions. */
  if ((sl == 0 || sl->list_len == 0) && (copts & COPT_DEFAULT))
    {
      char **matches;

      matches = rl_completion_matches (word, rl_filename_completion_function);
      sl = completions_to_stringlist (matches);
      strvec_dispose (matches);
    }

  if (sl)
    {
      if (sl->list && sl->list_len)
    {
      rval = EXECUTION_SUCCESS;
      strlist_print (sl, (char *)NULL);
    }
      strlist_dispose (sl);
    }

  compspec_dispose (cs);
  return (rval);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/declare.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/declare.def used in: Step 1057, Step 1075
This file is declare.def, from which is created declare.c.
It implements the builtins "declare" and "local" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES declare.c

$BUILTIN declare
$FUNCTION declare_builtin
$SHORT_DOC declare [-afFirtx] [-p] name[=value] ...
Declare variables and/or give them attributes.  If no NAMEs are
given, then display the values of variables instead.  The -p option
will display the attributes and values of each NAME.

The flags are:

  -a    to make NAMEs arrays (if supported)
  -f    to select from among function names only
  -F    to display function names without definitions
  -i    to make NAMEs have the `integer' attribute
  -r    to make NAMEs readonly
  -t    to make NAMEs have the `trace' attribute
  -x    to make NAMEs export

Variables with the integer attribute have arithmetic evaluation (see
`let') done when the variable is assigned to.

When displaying values of variables, -f displays a function's name
and definition.  The -F option restricts the display to function
name only.

Using `+' instead of `-' turns off the given attribute instead.  When
used in a function, makes NAMEs local, as with the `local' command.
$END

$BUILTIN typeset
$FUNCTION declare_builtin
$SHORT_DOC typeset [-afFirtx] [-p] name[=value] ...
Obsolete.  See `declare'.
$END

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>

#include "../bashansi.h"

#include "../shell.h"
#include "common.h"
#include "builtext.h"
#include "bashgetopt.h"

extern int array_needs_making;

static int declare_internal __P((register WORD_LIST *, int));

/* Declare or change variable attributes. */
int
declare_builtin (list)
     register WORD_LIST *list;
{
  return (declare_internal (list, 0));
}

$BUILTIN local
$FUNCTION local_builtin
$SHORT_DOC local name[=value] ...
Create a local variable called NAME, and give it VALUE.  LOCAL
can only be used within a function; it makes the variable NAME
have a visible scope restricted to that function and its children.
$END
int
local_builtin (list)
     register WORD_LIST *list;
{
  if (variable_context)
    return (declare_internal (list, 1));
  else
    {
      builtin_error ("can only be used in a function");
      return (EXECUTION_FAILURE);
    }
}

#if defined (ARRAY_VARS)
#  define DECLARE_OPTS  "+afiprtxF"
#else
#  define DECLARE_OPTS  "+fiprtxF"
#endif

/* The workhorse function. */
static int
declare_internal (list, local_var)
     register WORD_LIST *list;
     int local_var;
{
  int flags_on, flags_off, *flags, any_failed, assign_error, pflag, nodefs, opt;
  char *t, *subscript_start;
  SHELL_VAR *var;

  flags_on = flags_off = any_failed = assign_error = pflag = nodefs = 0;
  reset_internal_getopt ();
  while ((opt = internal_getopt (list, DECLARE_OPTS)) != EOF)
    {
      flags = list_opttype == '+' ? &flags_off : &flags_on;

      switch (opt)
    {
    case 'a':
#if defined (ARRAY_VARS)
      *flags |= att_array;
#endif
      break;
    case 'p':
      if (local_var == 0)
        pflag++;
      break;
        case 'F':
      nodefs++;
      *flags |= att_function;
      break;
    case 'f':
      *flags |= att_function;
      break;
    case 'i':
      *flags |= att_integer;
      break;
    case 'r':
      *flags |= att_readonly;
      break;
    case 't':
      *flags |= att_trace;
      break;
    case 'x':
      *flags |= att_exported;
      array_needs_making = 1;
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }

  list = loptend;

  /* If there are no more arguments left, then we just want to show
     some variables. */
  if (list == 0)    /* declare -[afFirtx] */
    {
      /* Show local variables defined at this context level if this is
     the `local' builtin. */
      if (local_var)
    {
      register SHELL_VAR **vlist;
      register int i;

      vlist = all_local_variables ();

      if (vlist)
        {
          for (i = 0; vlist[i]; i++)
        print_assignment (vlist[i]);

          free (vlist);
        }
    }
      else
    {
      if (flags_on == 0)
        set_builtin ((WORD_LIST *)NULL);
      else
        set_or_show_attributes ((WORD_LIST *)NULL, flags_on, nodefs);
    }

      fflush (stdout);
      return (EXECUTION_SUCCESS);
    }

  if (pflag)    /* declare -p [-afFirtx] name [name...] */
    {
      for (any_failed = 0; list; list = list->next)
    {
      pflag = show_name_attributes (list->word->word, nodefs);
      if (pflag)
        {
          sh_notfound (list->word->word);
          any_failed++;
        }
    }
      return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
    }

#define NEXT_VARIABLE() free (name); list = list->next; continue

  /* There are arguments left, so we are making variables. */
  while (list)      /* declare [-afFirx] name [name ...] */
    {
      char *value, *name;
      int offset;
#if defined (ARRAY_VARS)
      int making_array_special, compound_array_assign, simple_array_assign;
#endif

      name = savestring (list->word->word);
      offset = assignment (name);

      if (offset)   /* declare [-afFirx] name=value */
    {
      name[offset] = '\0';
      value = name + offset + 1;
    }
      else
    value = "";

#if defined (ARRAY_VARS)
      compound_array_assign = simple_array_assign = 0;
      subscript_start = (char *)NULL;
      if (t = strchr (name, '['))   /* ] */
    {
      subscript_start = t;
      *t = '\0';
      making_array_special = 1;
    }
      else
    making_array_special = 0;
#endif
    
      if (legal_identifier (name) == 0)
    {
      sh_invalidid (name);
      assign_error++;
      NEXT_VARIABLE ();
    }

      /* If VARIABLE_CONTEXT has a non-zero value, then we are executing
     inside of a function.  This means we should make local variables,
     not global ones. */

      /* XXX - this has consequences when we're making a local copy of a
           variable that was in the temporary environment.  Watch out
           for this. */
      if (variable_context && ((flags_on & att_function) == 0))
    {
#if defined (ARRAY_VARS)
      if ((flags_on & att_array) || making_array_special)
        var = make_local_array_variable (name);
      else
#endif
      var = make_local_variable (name);
      if (var == 0)
        {
          any_failed++;
          NEXT_VARIABLE ();
        }
    }
      else
    var = (SHELL_VAR *)NULL;

      /* If we are declaring a function, then complain about it in some way.
     We don't let people make functions by saying `typeset -f foo=bar'. */

      /* There should be a way, however, to let people look at a particular
     function definition by saying `typeset -f foo'. */

      if (flags_on & att_function)
    {
      if (offset)   /* declare -f [-rix] foo=bar */
        {
          builtin_error ("cannot use `-f' to make functions");
          free (name);
          return (EXECUTION_FAILURE);
        }
      else      /* declare -f [-rx] name [name...] */
        {
          var = find_function (name);

          if (var)
        {
          if (readonly_p (var) && (flags_off & att_readonly))
            {
              builtin_error ("%s: readonly function", name);
              any_failed++;
              NEXT_VARIABLE ();
            }

          /* declare -[Ff] name [name...] */
          if (flags_on == att_function && flags_off == 0)
            {
              t = nodefs ? var->name
                 : named_function_string (name, function_cell (var), 1);
              printf ("%s\n", t);
            }
          else      /* declare -[fF] -[rx] name [name...] */
            {
              VSETATTR (var, flags_on);
              VUNSETATTR (var, flags_off);
            }
        }
          else
        any_failed++;
          NEXT_VARIABLE ();
        }
    }
      else      /* declare -[airx] name [name...] */
    {
      /* Non-null if we just created or fetched a local variable. */
      if (var == 0)
        var = find_variable (name);

      if (var == 0)
        {
#if defined (ARRAY_VARS)
          if ((flags_on & att_array) || making_array_special)
        var = make_new_array_variable (name);
          else
#endif
          var = bind_variable (name, "");
        }

      /* Cannot use declare +r to turn off readonly attribute. */ 
      if (readonly_p (var) && (flags_off & att_readonly))
        {
          sh_readonly (name);
          any_failed++;
          NEXT_VARIABLE ();
        }

      /* Cannot use declare to assign value to readonly or noassign
         variable. */
      if ((readonly_p (var) || noassign_p (var)) && offset)
        {
          if (readonly_p (var))
        sh_readonly (name);
          assign_error++;
          NEXT_VARIABLE ();
        }

#if defined (ARRAY_VARS)
      if ((making_array_special || (flags_on & att_array) || array_p (var)) && offset)
        {
          if (value[0] == '(' && strchr (value, ')'))
        compound_array_assign = 1;
          else
        simple_array_assign = 1;
        }

      /* Cannot use declare +a name to remove an array variable. */
      if ((flags_off & att_array) && array_p (var))
        {
          builtin_error ("%s: cannot destroy array variables in this way", name);
          any_failed++;
          NEXT_VARIABLE ();
        }

      /* declare -a name makes name an array variable. */
      if ((making_array_special || (flags_on & att_array)) && array_p (var) == 0)
        var = convert_var_to_array (var);
#endif /* ARRAY_VARS */

      VSETATTR (var, flags_on);
      VUNSETATTR (var, flags_off);

#if defined (ARRAY_VARS)
      if (offset && compound_array_assign)
        assign_array_var_from_string (var, value);
      else if (simple_array_assign && subscript_start)
        {
          /* declare [-a] name[N]=value */
          *subscript_start = '[';   /* ] */
          var = assign_array_element (name, value);
          *subscript_start = '\0';
        }
      else if (simple_array_assign)
        /* let bind_array_variable take care of this. */
        bind_array_variable (name, 0, value);
      else
#endif
      /* bind_variable_value duplicates the essential internals of
         bind_variable() */
      if (offset)
        bind_variable_value (var, value);

      /* If we found this variable in the temporary environment, as with
         `var=value declare -x var', make sure it is treated identically
         to `var=value export var'.  Do the same for `declare -r' and
         `readonly'.  Preserve the attributes, except for att_tempvar. */
      /* XXX -- should this create a variable in the global scope, or
         modify the local variable flags?  ksh93 has it modify the
         global scope.
         Need to handle case like in set_var_attribute where a temporary
         variable is in the same table as the function local vars. */
      if ((flags_on & (att_exported|att_readonly)) && tempvar_p (var))
        {
          SHELL_VAR *tv;
          char *tvalue;

          tv = find_tempenv_variable (var->name);
          if (tv)
        {
          tvalue = var_isset (var) ? savestring (value_cell (var)) : savestring ("");
              tv = bind_variable (var->name, tvalue);
              tv->attributes |= var->attributes & ~att_tempvar;
              if (tv->context > 0)
            VSETATTR (tv, att_propagate);
              free (tvalue);
        }
          VSETATTR (var, att_propagate);
        }
    }

      stupidly_hack_special_variables (name);

      NEXT_VARIABLE ();
    }

  return (assign_error ? EX_BADASSIGN
               : ((any_failed == 0) ? EXECUTION_SUCCESS
                        : EXECUTION_FAILURE));
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/echo.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/echo.def used in: Step 1057, Step 1077
This file is echo.def, from which is created echo.c.
It implements the builtin "echo" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES echo.c
#include <config.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "../bashansi.h"

#include <stdio.h>
#include "../shell.h"

$BUILTIN echo
$FUNCTION echo_builtin
$DEPENDS_ON V9_ECHO
$SHORT_DOC echo [-neE] [arg ...]
Output the ARGs.  If -n is specified, the trailing newline is
suppressed.  If the -e option is given, interpretation of the
following backslash-escaped characters is turned on:
    \a  alert (bell)
    \b  backspace
    \c  suppress trailing newline
    \E  escape character
    \f  form feed
    \n  new line
    \r  carriage return
    \t  horizontal tab
    \v  vertical tab
    \\  backslash
    \num    the character whose ASCII code is NUM (octal).

You can explicitly turn off the interpretation of the above characters
with the -E option.
$END

$BUILTIN echo
$FUNCTION echo_builtin
$DEPENDS_ON !V9_ECHO
$SHORT_DOC echo [-n] [arg ...]
Output the ARGs.  If -n is specified, the trailing newline is suppressed.
$END

#if defined (V9_ECHO)
#  define VALID_ECHO_OPTIONS "neE"
#else /* !V9_ECHO */
#  define VALID_ECHO_OPTIONS "n"
#endif /* !V9_ECHO */

/* System V machines already have a /bin/sh with a v9 behaviour.  We
   give Bash the identical behaviour for these machines so that the
   existing system shells won't barf.  Regrettably, the SUS v2 has
   standardized the Sys V echo behavior.  This variable is external
   so that we can have a `shopt' variable to control it at runtime. */
#if defined (DEFAULT_ECHO_TO_XPG)
int xpg_echo = 1;
#else
int xpg_echo = 0;
#endif /* DEFAULT_ECHO_TO_XPG */

/* Print the words in LIST to standard output.  If the first word is
   `-n', then don't print a trailing newline.  We also support the
   echo syntax from Version 9 Unix systems. */
int
echo_builtin (list)
     WORD_LIST *list;
{
  int display_return, do_v9, i, len;
  char *temp, *s;

  do_v9 = xpg_echo;
  display_return = 1;

  for (; list && (temp = list->word->word) && *temp == '-'; list = list->next)
    {
      /* If it appears that we are handling options, then make sure that
     all of the options specified are actually valid.  Otherwise, the
     string should just be echoed. */
      temp++;

      for (i = 0; temp[i]; i++)
    {
      if (strchr (VALID_ECHO_OPTIONS, temp[i]) == 0)
        break;
    }

      /* echo - and echo -<nonopt> both mean to just echo the arguments. */
      if (*temp == 0 || temp[i])
    break;

      /* All of the options in TEMP are valid options to ECHO.
     Handle them. */
      while (i = *temp++)
    {
      switch (i)
        {
        case 'n':
          display_return = 0;
          break;
#if defined (V9_ECHO)
        case 'e':
          do_v9 = 1;
          break;
        case 'E':
          do_v9 = 0;
          break;
#endif /* V9_ECHO */
        default:
          goto just_echo;   /* XXX */
        }
    }
    }

just_echo:

  while (list)
    {
      i = len = 0;
      temp = do_v9 ? ansicstr (list->word->word, STRLEN (list->word->word), 1, &i, &len)
           : list->word->word;
      if (temp)
    {
      if (do_v9)
        {
          for (s = temp; len > 0; len--)
        putchar (*s++);
        }
      else      
        printf ("%s", temp);
#if defined (SunOS5)
      fflush (stdout);  /* Fix for bug in SunOS 5.5 printf(3) */
#endif
    }
      if (do_v9 && temp)
    free (temp);
      list = list->next;
      if (i)
    {
      display_return = 0;
      break;
    }
      if (list)
    putchar(' ');
    }

  if (display_return)
    putchar ('\n');
  fflush (stdout);
  if (ferror (stdout))
    {
      clearerr (stdout);
      return (EXECUTION_FAILURE);
    }
  return (EXECUTION_SUCCESS);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/enable.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/enable.def used in: Step 1057, Step 1079
This file is enable.def, from which is created enable.c.
It implements the builtin "enable" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES enable.c

$BUILTIN enable
$FUNCTION enable_builtin
$SHORT_DOC enable [-pnds] [-a] [-f filename] [name ...]
Enable and disable builtin shell commands.  This allows
you to use a disk command which has the same name as a shell
builtin without specifying a full pathname.  If -n is used, the
NAMEs become disabled; otherwise NAMEs are enabled.  For example,
to use the `test' found in $PATH instead of the shell builtin
version, type `enable -n test'.  On systems supporting dynamic
loading, the -f option may be used to load new builtins from the
shared object FILENAME.  The -d option will delete a builtin
previously loaded with -f.  If no non-option names are given, or
the -p option is supplied, a list of builtins is printed.  The
-a option means to print every builtin with an indication of whether
or not it is enabled.  The -s option restricts the output to the POSIX.2
`special' builtins.  The -n option displays a list of all disabled builtins.
$END

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>
#include "../bashansi.h"
#include "../shell.h"
#include "../builtins.h"
#include "../flags.h"
#include "common.h"
#include "bashgetopt.h"

#if defined (PROGRAMMABLE_COMPLETION)
#  include "../pcomplete.h"
#endif

#define ENABLED  1
#define DISABLED 2
#define SPECIAL  4

#define AFLAG   0x01
#define DFLAG   0x02
#define FFLAG   0x04
#define NFLAG   0x08
#define PFLAG   0x10
#define SFLAG   0x20

#if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM)
static int dyn_load_builtin __P((WORD_LIST *, int, char *));
#endif

#if defined (HAVE_DLCLOSE)
static int dyn_unload_builtin __P((char *));
static void delete_builtin __P((struct builtin *));
static int local_dlclose __P((void *));
#endif

static void list_some_builtins __P((int));
static int enable_shell_command __P((char *, int));

/* Enable/disable shell commands present in LIST.  If list is not specified,
   then print out a list of shell commands showing which are enabled and
   which are disabled. */
int
enable_builtin (list)
     WORD_LIST *list;
{
  int result, flags;
  int opt, filter;
#if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM)
  char *filename;
#endif

  result = EXECUTION_SUCCESS;
  flags = 0;

  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "adnpsf:")) != -1)
    {
      switch (opt)
    {
    case 'a':
      flags |= AFLAG;
      break;
    case 'n':
      flags |= NFLAG;
      break;
    case 'p':
      flags |= PFLAG;
      break;
    case 's':
      flags |= SFLAG;
      break;
    case 'f':
#if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM)
      flags |= FFLAG;
      filename = list_optarg;
      break;
#else
      builtin_error ("dynamic loading not available");
      return (EX_USAGE);
#endif
#if defined (HAVE_DLCLOSE)
    case 'd':
      flags |= DFLAG;
      break;
#else
      builtin_error ("dynamic loading not available");
      return (EX_USAGE);
#endif /* HAVE_DLCLOSE */
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }

  list = loptend;

#if defined (RESTRICTED_SHELL)
  /* Restricted shells cannot load new builtins. */
  if (restricted && (flags & (FFLAG|DFLAG)))
    {
      sh_restricted ((char *)NULL);
      return (EXECUTION_FAILURE);
    }
#endif

  if (list == 0 || (flags & PFLAG))
    {
      filter = (flags & AFLAG) ? (ENABLED | DISABLED)
                   : (flags & NFLAG) ? DISABLED : ENABLED;

      if (flags & SFLAG)
    filter |= SPECIAL;

      list_some_builtins (filter);
    }
#if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM)
  else if (flags & FFLAG)
    {
      filter = (flags & NFLAG) ? DISABLED : ENABLED;
      if (flags & SFLAG)
    filter |= SPECIAL;

      result = dyn_load_builtin (list, filter, filename);
#if defined (PROGRAMMABLE_COMPLETION)
      set_itemlist_dirty (&it_builtins);
#endif
    }
#endif
#if defined (HAVE_DLCLOSE)
  else if (flags & DFLAG)
    {
      while (list)
    {
      opt = dyn_unload_builtin (list->word->word);
      if (opt == EXECUTION_FAILURE)
        result = EXECUTION_FAILURE;
      list = list->next;
    }
#if defined (PROGRAMMABLE_COMPLETION)
      set_itemlist_dirty (&it_builtins);
#endif
    }
#endif
  else
    {
      while (list)
    {
      opt = enable_shell_command (list->word->word, flags & NFLAG);

      if (opt == EXECUTION_FAILURE)
        {
          builtin_error ("%s: not a shell builtin", list->word->word);
          result = EXECUTION_FAILURE;
        }
      list = list->next;
    }
    }
  return (result);
}

/* List some builtins.
   FILTER is a mask with two slots: ENABLED and DISABLED. */
static void
list_some_builtins (filter)
     int filter;
{
  register int i;

  for (i = 0; i < num_shell_builtins; i++)
    {
      if (shell_builtins[i].function == 0 || (shell_builtins[i].flags & BUILTIN_DELETED))
    continue;

      if ((filter & SPECIAL) &&
      (shell_builtins[i].flags & SPECIAL_BUILTIN) == 0)
    continue;

      if ((filter & ENABLED) && (shell_builtins[i].flags & BUILTIN_ENABLED))
    printf ("enable %s\n", shell_builtins[i].name);
      else if ((filter & DISABLED) &&
           ((shell_builtins[i].flags & BUILTIN_ENABLED) == 0))
    printf ("enable -n %s\n", shell_builtins[i].name);
    }
}

/* Enable the shell command NAME.  If DISABLE_P is non-zero, then
   disable NAME instead. */
static int
enable_shell_command (name, disable_p)
     char *name;
     int disable_p;
{
  struct builtin *b;

  b = builtin_address_internal (name, 1);
  if (b == 0)
    return (EXECUTION_FAILURE);

  if (disable_p)
    b->flags &= ~BUILTIN_ENABLED;
#if defined (RESTRICTED_SHELL)
  else if (restricted && ((b->flags & BUILTIN_ENABLED) == 0))
    {
      sh_restricted ((char *)NULL);
      return (EXECUTION_FAILURE);
    }
#endif
  else
    b->flags |= BUILTIN_ENABLED;

#if defined (PROGRAMMABLE_COMPLETION)
  set_itemlist_dirty (&it_enabled);
  set_itemlist_dirty (&it_disabled);
#endif

  return (EXECUTION_SUCCESS);
}

#if defined (HAVE_DLOPEN) && defined (HAVE_DLSYM)

#if defined (HAVE_DLFCN_H)
#  include <dlfcn.h>
#endif

static int
dyn_load_builtin (list, flags, filename)
     WORD_LIST *list;
     int flags;
     char *filename;
{
  WORD_LIST *l;
  void *handle;
  
  int total, size, new, replaced;
  char *struct_name, *name;
  struct builtin **new_builtins, *b, *new_shell_builtins, *old_builtin;

  if (list == 0)
    return (EXECUTION_FAILURE);

#ifndef RTLD_LAZY
#define RTLD_LAZY 1
#endif

#if defined (_AIX)
  handle = dlopen (filename, RTLD_NOW|RTLD_GLOBAL);
#else
  handle = dlopen (filename, RTLD_LAZY);
#endif /* !_AIX */

  if (handle == 0)
    {
      builtin_error ("cannot open shared object %s: %s", filename, dlerror ());
      return (EXECUTION_FAILURE);
    }

  for (new = 0, l = list; l; l = l->next, new++)
    ;
  new_builtins = (struct builtin **)xmalloc (new * sizeof (struct builtin *));

  /* For each new builtin in the shared object, find it and its describing
     structure.  If this is overwriting an existing builtin, do so, otherwise
     save the loaded struct for creating the new list of builtins. */
  for (replaced = new = 0; list; list = list->next)
    {
      name = list->word->word;

      size = strlen (name);
      struct_name = (char *)xmalloc (size + 8);
      strcpy (struct_name, name);
      strcpy (struct_name + size, "_struct");

      b = (struct builtin *)dlsym (handle, struct_name);
      if (b == 0)
    {
      builtin_error ("cannot find %s in shared object %s: %s", struct_name,
              filename, dlerror ());
      free (struct_name);
      continue;
    }

      free (struct_name);

      b->flags &= ~STATIC_BUILTIN;
      if (flags & SPECIAL)
    b->flags |= SPECIAL_BUILTIN;
      b->handle = handle;

      if (old_builtin = builtin_address_internal (name, 1))
    {
      replaced++;
      FASTCOPY ((char *)b, (char *)old_builtin, sizeof (struct builtin));
    }
      else
      new_builtins[new++] = b;
    }

  if (replaced == 0 && new == 0)
    {
      free (new_builtins);
      dlclose (handle);
      return (EXECUTION_FAILURE);
    }

  if (new)
    {
      total = num_shell_builtins + new;
      size = (total + 1) * sizeof (struct builtin);

      new_shell_builtins = (struct builtin *)xmalloc (size);
      FASTCOPY ((char *)shell_builtins, (char *)new_shell_builtins,
        num_shell_builtins * sizeof (struct builtin));
      for (replaced = 0; replaced < new; replaced++)
    FASTCOPY ((char *)new_builtins[replaced],
          (char *)&new_shell_builtins[num_shell_builtins + replaced],
          sizeof (struct builtin));

      new_shell_builtins[total].name = (char *)0;
      new_shell_builtins[total].function = (sh_builtin_func_t *)0;
      new_shell_builtins[total].flags = 0;

      if (shell_builtins != static_shell_builtins)
    free (shell_builtins);

      shell_builtins = new_shell_builtins;
      num_shell_builtins = total;
      initialize_shell_builtins ();
    }

  free (new_builtins);
  return (EXECUTION_SUCCESS);
}
#endif

#if defined (HAVE_DLCLOSE)
static void
delete_builtin (b)
     struct builtin *b;
{
  int ind, size;
  struct builtin *new_shell_builtins;

  /* XXX - funky pointer arithmetic - XXX */
#ifdef __STDC__
  ind = b - shell_builtins;
#else
  ind = ((int)b - (int)shell_builtins) / sizeof (struct builtin);
#endif
  size = num_shell_builtins * sizeof (struct builtin);
  new_shell_builtins = (struct builtin *)xmalloc (size);

  /* Copy shell_builtins[0]...shell_builtins[ind - 1] to new_shell_builtins */
  if (ind)
    FASTCOPY ((char *)shell_builtins, (char *)new_shell_builtins,
          ind * sizeof (struct builtin));
  /* Copy shell_builtins[ind+1]...shell_builtins[num_shell_builtins to
     new_shell_builtins, starting at ind. */
  FASTCOPY ((char *)(&shell_builtins[ind+1]),
        (char *)(&new_shell_builtins[ind]),
        (num_shell_builtins - ind) * sizeof (struct builtin));

  if (shell_builtins != static_shell_builtins)
    free (shell_builtins);

  /* The result is still sorted. */
  num_shell_builtins--;
  shell_builtins = new_shell_builtins;
}

/* Tenon's MachTen has a dlclose that doesn't return a value, so we
   finesse it with a local wrapper. */
static int
local_dlclose (handle)
     void *handle;
{
#if !defined (__MACHTEN__)
  return (dlclose (handle));
#else /* __MACHTEN__ */
  dlclose (handle);
  return ((dlerror () != NULL) ? -1 : 0);    
#endif /* __MACHTEN__ */
}

static int
dyn_unload_builtin (name)
     char *name;
{
  struct builtin *b;
  void *handle;
  int ref, i;

  b = builtin_address_internal (name, 1);
  if (b == 0)
    {
      builtin_error ("%s: not a shell builtin", name);
      return (EXECUTION_FAILURE);
    }
  if (b->flags & STATIC_BUILTIN)
    {
      builtin_error ("%s: not dynamically loaded", name);
      return (EXECUTION_FAILURE);
    }

  handle = (void *)b->handle;
  for (ref = i = 0; i < num_shell_builtins; i++)
    {
      if (shell_builtins[i].handle == b->handle)
    ref++;
    }

  /* Don't remove the shared object unless the reference count of builtins
     using it drops to zero. */
  if (ref == 1 && local_dlclose (handle) != 0)
    {
      builtin_error ("%s: cannot delete: %s", name, dlerror ());
      return (EXECUTION_FAILURE);
    }

  /* Now remove this entry from the builtin table and reinitialize. */
  delete_builtin (b);

  return (EXECUTION_SUCCESS);
}
#endif

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/eval.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/eval.def used in: Step 1057, Step 1081
This file is eval.def, from which is created eval.c.
It implements the builtin "eval" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES eval.c

$BUILTIN eval
$FUNCTION eval_builtin
$SHORT_DOC eval [arg ...]
Read ARGs as input to the shell and execute the resulting command(s).
$END

#include <config.h>
#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../shell.h"
#include "bashgetopt.h"
#include "common.h"

/* Parse the string that these words make, and execute the command found. */
int
eval_builtin (list)
     WORD_LIST *list;
{
  if (no_options (list))
    return (EX_USAGE);
  list = loptend;   /* skip over possible `--' */

  /* Note that parse_and_execute () frees the string it is passed. */
  return (list ? parse_and_execute (string_list (list), "eval", SEVAL_NOHIST) : EXECUTION_SUCCESS);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/exec.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/exec.def used in: Step 1057, Step 1083
This file is exec.def, from which is created exec.c.
It implements the builtin "exec" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES exec.c

$BUILTIN exec
$FUNCTION exec_builtin
$SHORT_DOC exec [-cl] [-a name] file [redirection ...]
Exec FILE, replacing this shell with the specified program.
If FILE is not specified, the redirections take effect in this
shell.  If the first argument is `-l', then place a dash in the
zeroth arg passed to FILE, as login does.  If the `-c' option
is supplied, FILE is executed with a null environment.  The `-a'
option means to make set argv[0] of the executed process to NAME.
If the file cannot be executed and the shell is not interactive,
then the shell exits, unless the shell option `execfail' is set.
$END

#include <config.h>

#include "../bashtypes.h"
#include "posixstat.h"
#include <signal.h>
#include <errno.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "../bashansi.h"

#include "../shell.h"
#include "../execute_cmd.h"
#include "../findcmd.h"
#if defined (JOB_CONTROL)
#  include "../jobs.h"
#endif
#include "../flags.h"
#include "../trap.h"
#if defined (HISTORY)
#  include "../bashhist.h"
#endif
#include "common.h"
#include "bashgetopt.h"

/* Not all systems declare ERRNO in errno.h... and some systems #define it! */
#if !defined (errno)
extern int errno;
#endif /* !errno */

extern int subshell_environment;
extern REDIRECT *redirection_undo_list;

int no_exit_on_failed_exec;

/* If the user wants this to look like a login shell, then
   prepend a `-' onto NAME and return the new name. */
static char *
mkdashname (name)
     char *name;
{
  char *ret;

  ret = (char *)xmalloc (2 + strlen (name));
  ret[0] = '-';
  strcpy (ret + 1, name);
  return ret;
}

int
exec_builtin (list)
     WORD_LIST *list;
{
  int exit_value = EXECUTION_FAILURE;
  int cleanenv, login, opt;
  char *argv0, *command, **args, **env, *newname, *com2;

  cleanenv = login = 0;
  argv0 = (char *)NULL;

  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "cla:")) != -1)
    {
      switch (opt)
    {
    case 'c':
      cleanenv = 1;
      break;
    case 'l':
      login = 1;
      break;
    case 'a':
      argv0 = list_optarg;
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }
  list = loptend;

  /* First, let the redirections remain. */
  dispose_redirects (redirection_undo_list);
  redirection_undo_list = (REDIRECT *)NULL;

  if (list == 0)
    return (EXECUTION_SUCCESS);

#if defined (RESTRICTED_SHELL)
  if (restricted)
    {
      sh_restricted ((char *)NULL);
      return (EXECUTION_FAILURE);
    }
#endif /* RESTRICTED_SHELL */

  args = strvec_from_word_list (list, 1, 0, (int *)NULL);

  /* A command with a slash anywhere in its name is not looked up in $PATH. */
  command = absolute_program (args[0]) ? args[0] : search_for_command (args[0]);

  if (command == 0)
    {
      sh_notfound (args[0]);
      exit_value = EX_NOTFOUND; /* As per Posix.2, 3.14.6 */
      goto failed_exec;
    }

  com2 = full_pathname (command);
  if (com2)
    {
      if (command != args[0])
    free (command);
      command = com2;
    }

  if (argv0)
    {
      free (args[0]);
      args[0] = login ? mkdashname (argv0) : savestring (argv0);
    }
  else if (login)
    {
      newname = mkdashname (args[0]);
      free (args[0]);
      args[0] = newname;
    }

  /* Decrement SHLVL by 1 so a new shell started here has the same value,
     preserving the appearance.  After we do that, we need to change the
     exported environment to include the new value. */
  if (cleanenv == 0)
    adjust_shell_level (-1);

  if (cleanenv)
    env = (char **)NULL;
  else
    {   
      maybe_make_export_env ();
      env = export_env;
    }

#if defined (HISTORY)
  if (interactive_shell && subshell_environment == 0)
    maybe_save_shell_history ();
#endif /* HISTORY */

  restore_original_signals ();

#if defined (JOB_CONTROL)
  if (subshell_environment == 0)
    end_job_control ();
#endif /* JOB_CONTROL */

  shell_execve (command, args, env);

  /* We have to set this to NULL because shell_execve has called realloc()
     to stuff more items at the front of the array, which may have caused
     the memory to be freed by realloc().  We don't want to free it twice. */
  args = (char **)NULL;
  if (cleanenv == 0)
    adjust_shell_level (1);

  if (executable_file (command) == 0)
    {
      builtin_error ("%s: cannot execute: %s", command, strerror (errno));
      exit_value = EX_NOEXEC;   /* As per Posix.2, 3.14.6 */
    }
  else
    file_error (command);

failed_exec:
  if (command)
    free (command);

  if (subshell_environment || (interactive == 0 && no_exit_on_failed_exec == 0))
    exit_shell (exit_value);

  if (args)
    strvec_dispose (args);

  initialize_traps ();
  initialize_signals (1);

#if defined (JOB_CONTROL)
  restart_job_control ();
#endif /* JOB_CONTROL */

  return (exit_value);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/exit.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/exit.def used in: Step 1057, Step 1085
This file is exit.def, from which is created exit.c.
It implements the builtins "exit", and "logout" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES exit.c

$BUILTIN exit
$FUNCTION exit_builtin
$SHORT_DOC exit [n]
Exit the shell with a status of N.  If N is omitted, the exit status
is that of the last command executed.
$END

#include <config.h>

#include "../bashtypes.h"
#include <stdio.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "../shell.h"
#include "../jobs.h"

#include "common.h"
#include "builtext.h"   /* for jobs_builtin */

extern int last_command_exit_value;
extern int subshell_environment;
extern sh_builtin_func_t *this_shell_builtin;
extern sh_builtin_func_t *last_shell_builtin;

static int exit_or_logout __P((WORD_LIST *));
static int sourced_logout;

int
exit_builtin (list)
     WORD_LIST *list;
{
  if (interactive)
    {
      fprintf (stderr, login_shell ? "logout\n" : "exit\n");
      fflush (stderr);
    }

  return (exit_or_logout (list));
}

$BUILTIN logout
$FUNCTION logout_builtin
$SHORT_DOC logout
Logout of a login shell.
$END

/* How to logout. */
int
logout_builtin (list)
     WORD_LIST *list;
{
  if (login_shell == 0 /* && interactive */)
    {
      builtin_error ("not login shell: use `exit'");
      return (EXECUTION_FAILURE);
    }
  else
    return (exit_or_logout (list));
}

static int
exit_or_logout (list)
     WORD_LIST *list;
{
  int exit_value;

#if defined (JOB_CONTROL)
  int exit_immediate_okay;

  exit_immediate_okay = (interactive  == 0 ||
             last_shell_builtin == exit_builtin ||
             last_shell_builtin == logout_builtin ||
             last_shell_builtin == jobs_builtin);

  /* Check for stopped jobs if the user wants to. */
  if (!exit_immediate_okay)
    {
      register int i;
      for (i = 0; i < job_slots; i++)
    if (jobs[i] && STOPPED (i))
      {
        fprintf (stderr, "There are stopped jobs.\n");

        /* This is NOT superfluous because EOF can get here without
           going through the command parser.  Set both last and this
           so that either `exit', `logout', or ^D will work to exit
           immediately if nothing intervenes. */
        this_shell_builtin = last_shell_builtin = exit_builtin;
        return (EXECUTION_FAILURE);
      }
    }
#endif /* JOB_CONTROL */

  /* Get return value if present.  This means that you can type
     `logout 5' to a shell, and it returns 5. */

  exit_value = get_exitstat (list);

  /* Run our `~/.bash_logout' file if it exists, and this is a login shell. */
  if (login_shell && sourced_logout++ == 0 && subshell_environment == 0)
    {
      maybe_execute_file ("~/.bash_logout", 1);
#ifdef SYS_BASH_LOGOUT
      maybe_execute_file (SYS_BASH_LOGOUT, 1);
#endif
    }

  last_command_exit_value = exit_value;

  /* Exit the program. */
  jump_to_top_level (EXITPROG);
  /*NOTREACHED*/
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/fc.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/fc.def used in: Step 1057, Step 1087
This file is fc.def, from which is created fc.c.
It implements the builtin "fc" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES fc.c

$BUILTIN fc
$FUNCTION fc_builtin
$DEPENDS_ON HISTORY
$SHORT_DOC fc [-e ename] [-nlr] [first] [last] or fc -s [pat=rep] [cmd]

fc is used to list or edit and re-execute commands from the history list.
FIRST and LAST can be numbers specifying the range, or FIRST can be a
string, which means the most recent command beginning with that
string.

   -e ENAME selects which editor to use.  Default is FCEDIT, then EDITOR,
      then vi.

   -l means list lines instead of editing.
   -n means no line numbers listed.
   -r means reverse the order of the lines (making it newest listed first).

With the `fc -s [pat=rep ...] [command]' format, the command is
re-executed after the substitution OLD=NEW is performed.

A useful alias to use with this is r='fc -s', so that typing `r cc'
runs the last command beginning with `cc' and typing `r' re-executes
the last command.
$END

#include <config.h>

#if defined (HISTORY)
#ifndef _MINIX
#  include <sys/param.h>
#endif
#include "../bashtypes.h"
#include "posixstat.h"
#ifndef _MINIX
#  include <sys/file.h>
#endif

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <stdio.h>
#include <chartypes.h>

#include "../bashansi.h"
#include <errno.h>

#include "../shell.h"
#include "../builtins.h"
#include "../flags.h"
#include "../bashhist.h"
#include "maxpath.h"
#include <readline/history.h>
#include "bashgetopt.h"
#include "common.h"

#if !defined (errno)
extern int errno;
#endif /* !errno */

extern int echo_input_at_read;
extern int current_command_line_count;
extern int literal_history;

extern int unlink __P((const char *));

extern FILE *sh_mktmpfp __P((char *, int, char **));

/* **************************************************************** */
/*                                  */
/*  The K*rn shell style fc command (Fix Command)           */
/*                                  */
/* **************************************************************** */

/* fc builtin command (fix command) for Bash for those who
   like K*rn-style history better than csh-style.

     fc [-e ename] [-nlr] [first] [last]

   FIRST and LAST can be numbers specifying the range, or FIRST can be
   a string, which means the most recent command beginning with that
   string.

   -e ENAME selects which editor to use.  Default is FCEDIT, then EDITOR,
      then the editor which corresponds to the current readline editing
      mode, then vi.

   -l means list lines instead of editing.
   -n means no line numbers listed.
   -r means reverse the order of the lines (making it newest listed first).

     fc -e - [pat=rep ...] [command]
     fc -s [pat=rep ...] [command]

   Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's.
*/

/* Data structure describing a list of global replacements to perform. */
typedef struct repl {
  struct repl *next;
  char *pat;
  char *rep;
} REPL;

/* Accessors for HIST_ENTRY lists that are called HLIST. */
#define histline(i) (hlist[(i)]->line)
#define histdata(i) (hlist[(i)]->data)

#define FREE_RLIST() \
    do { \
        for (rl = rlist; rl; ) { \
            REPL *r;    \
            r = rl->next; \
            if (rl->pat) \
                free (rl->pat); \
            if (rl->rep) \
                free (rl->rep); \
            free (rl); \
            rl = r; \
        } \
    } while (0)

static char *fc_dosubs __P((char *, REPL *));
static char *fc_gethist __P((char *, HIST_ENTRY **));
static int fc_gethnum __P((char *, HIST_ENTRY **));
static int fc_number __P((WORD_LIST *));
static void fc_replhist __P((char *));
#ifdef INCLUDE_UNUSED
static char *fc_readline __P((FILE *));
static void fc_addhist __P((char *));
#endif

/* String to execute on a file that we want to edit. */
#define FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-vi}}"

int
fc_builtin (list)
     WORD_LIST *list;
{
  register int i;
  register char *sep;
  int numbering, reverse, listing, execute;
  int histbeg, histend, last_hist, retval, opt;
  FILE *stream;
  REPL *rlist, *rl;
  char *ename, *command, *newcom;
  HIST_ENTRY **hlist;
  char *fn;

  numbering = 1;
  reverse = listing = execute = 0;
  ename = (char *)NULL;

  /* Parse out the options and set which of the two forms we're in. */
  reset_internal_getopt ();
  lcurrent = list;      /* XXX */
  while (fc_number (loptend = lcurrent) == 0 &&
     (opt = internal_getopt (list, ":e:lnrs")) != -1)
    {
      switch (opt)
    {
    case 'n':
      numbering = 0;
      break;

    case 'l':
      listing = 1;
      break;

    case 'r':
      reverse = 1;
      break;

    case 's':
      execute = 1;
      break;

    case 'e':
      ename = list_optarg;
      break;

    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }

  list = loptend;

  if (ename && (*ename == '-') && (ename[1] == '\0'))
    execute = 1;

  /* The "execute" form of the command (re-run, with possible string
     substitutions). */
  if (execute)
    {
      rlist = (REPL *)NULL;
      while (list && ((sep = (char *)strchr (list->word->word, '=')) != NULL))
    {
      *sep++ = '\0';
      rl = (REPL *)xmalloc (sizeof (REPL));
      rl->next = (REPL *)NULL;
      rl->pat = savestring (list->word->word);
      rl->rep = savestring (sep);

      if (rlist == NULL)
        rlist = rl;
      else
        {
          rl->next = rlist;
          rlist = rl;
        }
      list = list->next;
    }

      /* If we have a list of substitutions to do, then reverse it
     to get the replacements in the proper order. */

      rlist = REVERSE_LIST (rlist, REPL *);

      hlist = history_list ();

      /* If we still have something in list, it is a command spec.
     Otherwise, we use the most recent command in time. */
      command = fc_gethist (list ? list->word->word : (char *)NULL, hlist);

      if (command == NULL)
    {
      builtin_error ("no command found");
      if (rlist)
        FREE_RLIST ();

      return (EXECUTION_FAILURE);
    }

      if (rlist)
    {
      newcom = fc_dosubs (command, rlist);
      free (command);
      FREE_RLIST ();
      command = newcom;
    }

      fprintf (stderr, "%s\n", command);
      fc_replhist (command);    /* replace `fc -s' with command */
      return (parse_and_execute (command, "fc", SEVAL_NOHIST));
    }

  /* This is the second form of the command (the list-or-edit-and-rerun
     form). */
  hlist = history_list ();
  if (hlist == 0)
    return (EXECUTION_SUCCESS);
  for (i = 0; hlist[i]; i++);

  /* With the Bash implementation of history, the current command line
     ("fc blah..." and so on) is already part of the history list by
     the time we get to this point.  This just skips over that command
     and makes the last command that this deals with be the last command
     the user entered before the fc.  We need to check whether the
     line was actually added (HISTIGNORE may have caused it to not be),
     so we check hist_last_line_added. */

  last_hist = i - 1 - hist_last_line_added;

  if (list)
    {
      histbeg = fc_gethnum (list->word->word, hlist);
      list = list->next;

      if (list)
    histend = fc_gethnum (list->word->word, hlist);
      else
    histend = listing ? last_hist : histbeg;
    }
  else
    {
      /* The default for listing is the last 16 history items. */
      if (listing)
    {
      histend = last_hist;
      histbeg = histend - 16;
      if (histbeg < 0)
        histbeg = 0;
    }
      else
    /* For editing, it is the last history command. */
    histbeg = histend = last_hist;
    }

  /* We print error messages for line specifications out of range. */
  if ((histbeg < 0) || (histend < 0))
    {
      sh_erange ((char *)NULL, "history specification");
      return (EXECUTION_FAILURE);
    }

  if (histend < histbeg)
    {
      i = histend;
      histend = histbeg;
      histbeg = i;

      reverse = 1;
    }

  if (listing)
    stream = stdout;
  else
    {
      numbering = 0;
      stream = sh_mktmpfp ("bash-fc", MT_USERANDOM|MT_USETMPDIR, &fn);
      if (stream == 0)
    {
      builtin_error ("cannot open temp file %s", fn ? fn : "");
      FREE (fn);
      return (EXECUTION_FAILURE);
    }
    }

  for (i = reverse ? histend : histbeg; reverse ? i >= histbeg : i <= histend; reverse ? i-- : i++)
    {
      QUIT;
      if (numbering)
    fprintf (stream, "%d", i + history_base);
      if (listing)
    fprintf (stream, "\t%c", histdata (i) ? '*' : ' ');
      fprintf (stream, "%s\n", histline (i));
    }

  if (listing)
    return (EXECUTION_SUCCESS);

  fclose (stream);

  /* Now edit the file of commands. */
  if (ename)
    {
      command = (char *)xmalloc (strlen (ename) + strlen (fn) + 2);
      sprintf (command, "%s %s", ename, fn);
    }
  else
    {
      command = (char *)xmalloc (3 + strlen (FC_EDIT_COMMAND) + strlen (fn));
      sprintf (command, "%s %s", FC_EDIT_COMMAND, fn);
    }
  retval = parse_and_execute (command, "fc", SEVAL_NOHIST);
  if (retval != EXECUTION_SUCCESS)
    {
      unlink (fn);
      free (fn);
      return (EXECUTION_FAILURE);
    }

  /* Make sure parse_and_execute doesn't turn this off, even though a
     call to parse_and_execute farther up the function call stack (e.g.,
     if this is called by vi_edit_and_execute_command) may have already
     called bash_history_disable. */
  remember_on_history = 1;

  /* Turn on the `v' flag while fc_execute_file runs so the commands
     will be echoed as they are read by the parser. */
  begin_unwind_frame ("fc builtin");
  add_unwind_protect ((Function *)xfree, fn);
  add_unwind_protect (unlink, fn);
  unwind_protect_int (echo_input_at_read);
  echo_input_at_read = 1;
    
  retval = fc_execute_file (fn);

  run_unwind_frame ("fc builtin");

  return (retval);
}

/* Return 1 if LIST->word->word is a legal number for fc's use. */
static int
fc_number (list)
     WORD_LIST *list;
{
  char *s;

  if (list == 0)
    return 0;
  s = list->word->word;
  if (*s == '-')
    s++;
  return (legal_number (s, (intmax_t *)NULL));
}

/* Return an absolute index into HLIST which corresponds to COMMAND.  If
   COMMAND is a number, then it was specified in relative terms.  If it
   is a string, then it is the start of a command line present in HLIST. */
static int
fc_gethnum (command, hlist)
     char *command;
     HIST_ENTRY **hlist;
{
  int sign = 1, n, clen;
  register int i, j;
  register char *s;

  /* Count history elements. */
  for (i = 0; hlist[i]; i++);

  /* With the Bash implementation of history, the current command line
     ("fc blah..." and so on) is already part of the history list by
     the time we get to this point.  This just skips over that command
     and makes the last command that this deals with be the last command
     the user entered before the fc.  We need to check whether the
     line was actually added (HISTIGNORE may have caused it to not be),
     so we check hist_last_line_added. */
  i -= 1 + hist_last_line_added;

  /* No specification defaults to most recent command. */
  if (command == NULL)
    return (i);

  /* Otherwise, there is a specification.  It can be a number relative to
     the current position, or an absolute history number. */
  s = command;

  /* Handle possible leading minus sign. */
  if (s && (*s == '-'))
    {
      sign = -1;
      s++;
    }

  if (s && DIGIT(*s))
    {
      n = atoi (s);
      n *= sign;

      /* If the value is negative or zero, then it is an offset from
     the current history item. */
      if (n < 0)
    {
      n += i + 1;
      return (n < 0 ? 0 : n);
    }
      else if (n == 0)
    return (i);
      else
    {
      n -= history_base;
      return (i < n ? i : n);
    }
    }

  clen = strlen (command);
  for (j = i; j >= 0; j--)
    {
      if (STREQN (command, histline (j), clen))
    return (j);
    }
  return (-1);
}

/* Locate the most recent history line which begins with
   COMMAND in HLIST, and return a malloc()'ed copy of it. */
static char *
fc_gethist (command, hlist)
     char *command;
     HIST_ENTRY **hlist;
{
  int i;

  if (!hlist)
    return ((char *)NULL);

  i = fc_gethnum (command, hlist);

  if (i >= 0)
    return (savestring (histline (i)));
  else
    return ((char *)NULL);
}

#ifdef INCLUDE_UNUSED
/* Read the edited history lines from STREAM and return them
   one at a time.  This can read unlimited length lines.  The
   caller should free the storage. */
static char *
fc_readline (stream)
     FILE *stream;
{
  register int c;
  int line_len = 0, lindex = 0;
  char *line = (char *)NULL;

  while ((c = getc (stream)) != EOF)
    {
      if ((lindex + 2) >= line_len)
    line = (char *)xrealloc (line, (line_len += 128));

      if (c == '\n')
    {
      line[lindex++] = '\n';
      line[lindex++] = '\0';
      return (line);
    }
      else
    line[lindex++] = c;
    }

  if (!lindex)
    {
      if (line)
    free (line);

      return ((char *)NULL);
    }

  if (lindex + 2 >= line_len)
    line = (char *)xrealloc (line, lindex + 3);

  line[lindex++] = '\n';        /* Finish with newline if none in file */
  line[lindex++] = '\0';
  return (line);
}
#endif

/* Perform the SUBS on COMMAND.
   SUBS is a list of substitutions, and COMMAND is a simple string.
   Return a pointer to a malloc'ed string which contains the substituted
   command. */
static char *
fc_dosubs (command, subs)
     char *command;
     REPL *subs;
{
  register char *new, *t;
  register REPL *r;

  for (new = savestring (command), r = subs; r; r = r->next)
    {
      t = strsub (new, r->pat, r->rep, 1);
      free (new);
      new = t;
    }
  return (new);
}

/* Use `command' to replace the last entry in the history list, which,
   by this time, is `fc blah...'.  The intent is that the new command
   become the history entry, and that `fc' should never appear in the
   history list.  This way you can do `r' to your heart's content. */
static void
fc_replhist (command)
     char *command;
{
  register int i;
  HIST_ENTRY **hlist, *histent, *discard;
  int n;

  if (command == 0 || *command == '\0')
    return;

  hlist = history_list ();

  if (hlist == NULL)
    return;

  for (i = 0; hlist[i]; i++);
  i--;

  /* History_get () takes a parameter that should be
     offset by history_base. */

  histent = history_get (history_base + i); /* Don't free this */
  if (histent == NULL)
    return;

  n = strlen (command);

  if (command[n - 1] == '\n')
    command[n - 1] = '\0';

  if (command && *command)
    {
      discard = remove_history (i);
      if (discard)
    {
      FREE (discard->line);
      free ((char *) discard);
    }
      maybe_add_history (command);  /* Obeys HISTCONTROL setting. */
    }
}

#ifdef INCLUDE_UNUSED
/* Add LINE to the history, after removing a single trailing newline. */
static void
fc_addhist (line)
     char *line;
{
  register int n;

  n = strlen (line);

  if (line[n - 1] == '\n')
    line[n - 1] = '\0';

  if (line && *line)
    maybe_add_history (line);
}
#endif

#endif /* HISTORY */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/fg_bg.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/fg_bg.def used in: Step 1057, Step 1089
This file is fg_bg.def, from which is created fg_bg.c.
It implements the builtins "bg" and "fg" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES fg_bg.c

$BUILTIN fg
$FUNCTION fg_builtin
$DEPENDS_ON JOB_CONTROL
$SHORT_DOC fg [job_spec]
Place JOB_SPEC in the foreground, and make it the current job.  If
JOB_SPEC is not present, the shell's notion of the current job is
used.
$END

#include <config.h>

#include "../bashtypes.h"
#include <signal.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "../shell.h"
#include "../jobs.h"
#include "common.h"
#include "bashgetopt.h"

#if defined (JOB_CONTROL)
extern char *this_command_name;

static int fg_bg __P((WORD_LIST *, int));

/* How to bring a job into the foreground. */
int
fg_builtin (list)
     WORD_LIST *list;
{
  int fg_bit;
  register WORD_LIST *t;

  if (job_control == 0)
    {
      sh_nojobs ((char *)NULL);
      return (EXECUTION_FAILURE);
    }

  if (no_options (list))
    return (EX_USAGE);
  list = loptend;

  /* If the last arg on the line is '&', then start this job in the
     background.  Else, fg the job. */
  for (t = list; t && t->next; t = t->next)
    ;
  fg_bit = (t && t->word->word[0] == '&' && t->word->word[1] == '\0') == 0;

  return (fg_bg (list, fg_bit));
}
#endif /* JOB_CONTROL */

$BUILTIN bg
$FUNCTION bg_builtin
$DEPENDS_ON JOB_CONTROL
$SHORT_DOC bg [job_spec]
Place JOB_SPEC in the background, as if it had been started with
`&'.  If JOB_SPEC is not present, the shell's notion of the current
job is used.
$END

#if defined (JOB_CONTROL)
/* How to put a job into the background. */
int
bg_builtin (list)
     WORD_LIST *list;
{
  if (job_control == 0)
    {
      sh_nojobs ((char *)NULL);
      return (EXECUTION_FAILURE);
    }

  if (no_options (list))
    return (EX_USAGE);
  list = loptend;

  return (fg_bg (list, 0));
}

/* How to put a job into the foreground/background. */
static int
fg_bg (list, foreground)
     WORD_LIST *list;
     int foreground;
{
  sigset_t set, oset;
  int job, status, old_async_pid;

  BLOCK_CHILD (set, oset);
  job = get_job_spec (list);

  if (job < 0 || job >= job_slots || jobs[job] == 0)
    {
      if (job != DUP_JOB)
    sh_badjob (list ? list->word->word : "current");

      goto failure;
    }

  /* Or if jobs[job]->pgrp == shell_pgrp. */
  if (IS_JOBCONTROL (job) == 0)
    {
      builtin_error ("job %%%d started without job control", job + 1);
      goto failure;
    }

  if (foreground == 0)
    {
      old_async_pid = last_asynchronous_pid;
      last_asynchronous_pid = jobs[job]->pgrp;  /* As per Posix.2 5.4.2 */
    }

  status = start_job (job, foreground);

  if (status >= 0)
    {
    /* win: */
      UNBLOCK_CHILD (oset);
      return (status);
    }
  else
    {
      if (foreground == 0)
    last_asynchronous_pid = old_async_pid;

    failure:
      UNBLOCK_CHILD (oset);
      return (EXECUTION_FAILURE);
    }
}
#endif /* JOB_CONTROL */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/hash.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/hash.def used in: Step 1057, Step 1091
This file is hash.def, from which is created hash.c.
It implements the builtin "hash" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES hash.c

$BUILTIN hash
$FUNCTION hash_builtin
$SHORT_DOC hash [-lr] [-p pathname] [-dt] [name ...]
For each NAME, the full pathname of the command is determined and
remembered.  If the -p option is supplied, PATHNAME is used as the
full pathname of NAME, and no path search is performed.  The -r
option causes the shell to forget all remembered locations.  The -d
option causes the shell to forget the remembered location of each NAME.
If the -t option is supplied the full pathname to which each NAME
corresponds is printed.  If multiple NAME arguments are supplied with
-t, the NAME is printed before the hashed full pathname.  The -l option
causes output to be displayed in a format that may be reused as input.
If no arguments are given, information about remembered commands is displayed.
$END

#include <config.h>

#include <stdio.h>

#include "../bashtypes.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <errno.h>

#include "../bashansi.h"

#include "../shell.h"
#include "../builtins.h"
#include "../flags.h"
#include "../findcmd.h"
#include "../hashcmd.h"
#include "common.h"
#include "bashgetopt.h"

extern int dot_found_in_search;
extern char *this_command_name;

static int add_hashed_command __P((char *, int));
static int print_hash_info __P((BUCKET_CONTENTS *));
static int print_portable_hash_info __P((BUCKET_CONTENTS *));
static int print_hashed_commands __P((int));
static int list_hashed_filename_targets __P((WORD_LIST *, int));

/* Print statistics on the current state of hashed commands.  If LIST is
   not empty, then rehash (or hash in the first place) the specified
   commands. */
int
hash_builtin (list)
     WORD_LIST *list;
{
  int expunge_hash_table, list_targets, list_portably, delete, opt;
  char *w, *pathname;

  if (hashing_enabled == 0)
    {
      builtin_error ("hashing disabled");
      return (EXECUTION_FAILURE);
    }

  expunge_hash_table = list_targets = list_portably = delete = 0;
  pathname = (char *)NULL;
  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "dlp:rt")) != -1)
    {
      switch (opt)
    {
    case 'd':
      delete = 1;
      break;
    case 'l':
      list_portably = 1;
      break;
    case 'p':
      pathname = list_optarg;
      break;
    case 'r':
      expunge_hash_table = 1;
      break;
    case 't':
      list_targets = 1;
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }
  list = loptend;

  /* hash -t requires at least one argument. */
  if (list == 0 && list_targets)
    {
      sh_needarg ("-t");
      return (EXECUTION_FAILURE);
    }

  /* We want hash -r to be silent, but hash -- to print hashing info, so
     we test expunge_hash_table. */
  if (list == 0 && expunge_hash_table == 0)
    {
      if (print_hashed_commands (list_portably) == 0)
    printf ("%s: hash table empty\n", this_command_name);

      return (EXECUTION_SUCCESS);
    }

  if (expunge_hash_table)
    phash_flush ();

  /* If someone runs `hash -r -t xyz' he will be disappointed. */
  if (list_targets)
    return (list_hashed_filename_targets (list, list_portably));
      
#if defined (RESTRICTED_SHELL)
  if (restricted && pathname && strchr (pathname, '/'))
    {
      sh_restricted (pathname);
      return (EXECUTION_FAILURE);
    }
#endif

  for (opt = EXECUTION_SUCCESS; list; list = list->next)
    {
      /* Add, remove or rehash the specified commands. */
      w = list->word->word;
      if (pathname)
    {
      if (is_directory (pathname))
        {
#ifdef EISDIR
          builtin_error ("%s: %s", pathname, strerror (EISDIR));
#else
          builtin_error ("%s: is a directory", pathname);
#endif
          opt = EXECUTION_FAILURE;
        }
      else
        phash_insert (w, pathname, 0, 0);
    }
      else if (absolute_program (w))
    continue;
      else if (delete && phash_remove (w))
    {
      sh_notfound (w);
      opt = EXECUTION_FAILURE;
    }
      else if (add_hashed_command (w, 0))
    opt = EXECUTION_FAILURE;
    }

  fflush (stdout);
  return (opt);
}

static int
add_hashed_command (w, quiet)
     char *w;
     int quiet;
{
  int rv;
  char *full_path;

  rv = 0;
  if (find_function (w) == 0 && find_shell_builtin (w) == 0)
    {
      full_path = find_user_command (w);
      if (full_path && executable_file (full_path))
    phash_insert (w, full_path, dot_found_in_search, 0);
      else
    {
      if (quiet == 0)
        sh_notfound (w);
      rv++;
    }
      FREE (full_path);
    }
  return (rv);
}

/* Print information about current hashed info. */
static int
print_hash_info (item)
     BUCKET_CONTENTS *item;
{
  printf ("%4d\t%s\n", item->times_found, pathdata(item)->path);
  return 0;
}

static int
print_portable_hash_info (item)
     BUCKET_CONTENTS *item;
{
  printf ("builtin hash -p %s %s\n", pathdata(item)->path, item->key);
  return 0;
}

static int
print_hashed_commands (fmt)
     int fmt;
{
  if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0)
    return (0);

  if (fmt == 0)
    printf ("hits\tcommand\n");
  hash_walk (hashed_filenames, fmt ? print_portable_hash_info : print_hash_info);
  return (1);
}

static int
list_hashed_filename_targets (list, fmt)
     WORD_LIST *list;
     int fmt;
{
  int all_found, multiple;
  char *target;
  WORD_LIST *l;

  all_found = 1;
  multiple = list->next != 0;

  for (l = list; l; l = l->next)
    {
      target = phash_search (l->word->word);
      if (target == 0)
    {
      all_found = 0;
      sh_notfound (l->word->word);
      continue;
    }
      if (fmt)
    printf ("builtin hash -p %s %s\n", target, l->word->word);
      else
    {
      if (multiple)
        printf ("%s\t", l->word->word);
      printf ("%s\n", target);
    }
    }

  return (all_found ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/history.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/history.def used in: Step 1057, Step 1093
This file is history.def, from which is created history.c.
It implements the builtin "history" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES history.c

$BUILTIN history
$FUNCTION history_builtin
$DEPENDS_ON HISTORY
$SHORT_DOC history [-c] [-d offset] [n] or history -awrn [filename] or history -ps arg [arg...]
Display the history list with line numbers.  Lines listed with
with a `*' have been modified.  Argument of N says to list only
the last N lines.  The `-c' option causes the history list to be
cleared by deleting all of the entries.  The `-d' option deletes
the history entry at offset OFFSET.  The `-w' option writes out the
current history to the history file;  `-r' means to read the file and
append the contents to the history list instead.  `-a' means
to append history lines from this session to the history file.
Argument `-n' means to read all history lines not already read
from the history file and append them to the history list.  If
FILENAME is given, then that is used as the history file else
if $HISTFILE has a value, that is used, else ~/.bash_history.
If the -s option is supplied, the non-option ARGs are appended to
the history list as a single entry.  The -p option means to perform
history expansion on each ARG and display the result, without storing
anything in the history list.
$END

#include <config.h>

#if defined (HISTORY)
#include "../bashtypes.h"
#ifndef _MINIX
#  include <sys/file.h>
#endif
#include "posixstat.h"
#include "filecntl.h"
#include <errno.h>
#include <stdio.h>
#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "../bashansi.h"

#include "../shell.h"
#include "../bashhist.h"
#include <readline/history.h>
#include "bashgetopt.h"
#include "common.h"

#if !defined (errno)
extern int errno;
#endif

extern int current_command_line_count;

static void display_history __P((WORD_LIST *));
static int delete_histent __P((int));
static int delete_last_history __P((void));
static void push_history __P((WORD_LIST *));
static int expand_and_print_history __P((WORD_LIST *));

#define AFLAG   0x01
#define RFLAG   0x02
#define WFLAG   0x04
#define NFLAG   0x08
#define SFLAG   0x10
#define PFLAG   0x20
#define CFLAG   0x40
#define DFLAG   0x80

int
history_builtin (list)
     WORD_LIST *list;
{
  int flags, opt, result, old_history_lines;
  char *filename, *delete_arg;
  intmax_t delete_offset;

  flags = 0;
  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "acd:npsrw")) != -1)
    {
      switch (opt)
    {
    case 'a':
      flags |= AFLAG;
      break;
    case 'c':
      flags |= CFLAG;
      break;
    case 'n':
      flags |= NFLAG;
      break;
    case 'r':
      flags |= RFLAG;
      break;
    case 'w':
      flags |= WFLAG;
      break;
    case 's':
      flags |= SFLAG;
      break;
    case 'd':
      flags |= DFLAG;
      delete_arg = list_optarg;
      break;
    case 'p':
#if defined (BANG_HISTORY)
      flags |= PFLAG;
#endif
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }
  list = loptend;

  opt = flags & (AFLAG|RFLAG|WFLAG|NFLAG);
  if (opt && opt != AFLAG && opt != RFLAG && opt != WFLAG && opt != NFLAG)
    {
      builtin_error ("cannot use more than one of -anrw");
      return (EXECUTION_FAILURE);
    }

  /* clear the history, but allow other arguments to add to it again. */
  if (flags & CFLAG)
    {
      clear_history ();
      if (list == 0)
    return (EXECUTION_SUCCESS);
    }

  if (flags & SFLAG)
    {
      if (list)
    push_history (list);
      return (EXECUTION_SUCCESS);
    }
#if defined (BANG_HISTORY)
  else if (flags & PFLAG)
    {
      if (list)
    return (expand_and_print_history (list));
      return (EXECUTION_SUCCESS);
    }
#endif
  else if (flags & DFLAG)
    {
      if ((legal_number (delete_arg, &delete_offset) == 0)
      || (delete_offset < history_base)
      || (delete_offset > (history_base + history_length)))
    {
      sh_erange (delete_arg, "history position");
      return (EXECUTION_FAILURE);
    }
      opt = delete_offset;
      result = delete_histent (opt - history_base);
      /* Since remove_history changes history_length, this can happen if
     we delete the last history entry. */
      if (where_history () > history_length)
    history_set_pos (history_length);
      return (result ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
    }
  else if ((flags & (AFLAG|RFLAG|NFLAG|WFLAG|CFLAG)) == 0)
    {
      display_history (list);
      return (EXECUTION_SUCCESS);
    }

  filename = list ? list->word->word : get_string_value ("HISTFILE");
  result = EXECUTION_SUCCESS;

  if (flags & AFLAG)        /* Append session's history to file. */
    result = maybe_append_history (filename);
  else if (flags & WFLAG)   /* Write entire history. */
    result = write_history (filename);
  else if (flags & RFLAG)   /* Read entire file. */
    result = read_history (filename);
  else if (flags & NFLAG)   /* Read `new' history from file. */
    {
      /* Read all of the lines in the file that we haven't already read. */
      old_history_lines = history_lines_in_file;
      using_history ();
      result = read_history_range (filename, history_lines_in_file, -1);
      using_history ();
      history_lines_in_file = where_history ();
      history_lines_this_session += history_lines_in_file - old_history_lines;
    }

  return (result ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
}

/* Accessors for HIST_ENTRY lists that are called HLIST. */
#define histline(i) (hlist[(i)]->line)
#define histdata(i) (hlist[(i)]->data)

static void
display_history (list)
     WORD_LIST *list;
{
  register int i;
  intmax_t limit;
  HIST_ENTRY **hlist;

  if (list)
    {
      limit = get_numeric_arg (list, 0);
      if (limit < 0)
    limit = -limit;
    }
  else
    limit = -1;

  hlist = history_list ();

  if (hlist)
    {
      for (i = 0;  hlist[i]; i++)
    ;

      if (0 <= limit && limit < i)
    i -= limit;
      else
    i = 0;

      while (hlist[i])
    {
      QUIT;
      printf ("%5d%c %s\n", i + history_base,
          histdata(i) ? '*' : ' ',
          histline(i));
      i++;
    }
    }
}

/* Delete and free the history list entry at offset I. */
static int
delete_histent (i)
     int i;
{
  HIST_ENTRY *discard;

  discard = remove_history (i);
  if (discard)
    {
      if (discard->line)
    free (discard->line);
      free ((char *) discard);
    }
  return 1;
}

static int
delete_last_history ()
{
  register int i;
  HIST_ENTRY **hlist, *histent;

  hlist = history_list ();
  if (hlist == NULL)
    return 0;

  for (i = 0; hlist[i]; i++)
    ;
  i--;

  /* History_get () takes a parameter that must be offset by history_base. */
  histent = history_get (history_base + i); /* Don't free this */
  if (histent == NULL)
    return 0;

  return (delete_histent (i));
}

/* Remove the last entry in the history list and add each argument in
   LIST to the history. */
static void
push_history (list)
     WORD_LIST *list;
{
  char *s;

  /* Delete the last history entry if it was a single entry added to the
     history list (generally the `history -s' itself), or if `history -s'
     is being used in a compound command and the compound command was
     added to the history as a single element (command-oriented history).
     If you don't want history -s to remove the compound command from the
     history, change #if 0 to #if 1 below. */
#if 0
  if (hist_last_line_added && delete_last_history () == 0)
#else
  if ((hist_last_line_added || (current_command_line_count > 0 && current_command_first_line_saved && command_oriented_history))
      && delete_last_history () == 0)
#endif
      return;

  s = string_list (list);
  /* Call check_add_history with FORCE set to 1 to skip the check against
     current_command_line_count.  If history -s is used in a compound
     command, the above code will delete the compound command's history
     entry and this call will add the line to the history as a separate
     entry.  Without FORCE=1, if current_command_line_count were > 1, the
     line would be appended to the entry before the just-deleted entry. */
  check_add_history (s, 1); /* obeys HISTCONTROL, HISTIGNORE */
  free (s);
}

#if defined (BANG_HISTORY)
static int
expand_and_print_history (list)
     WORD_LIST *list;
{
  char *s;
  int r, result;

  if (hist_last_line_added && delete_last_history () == 0)
    return EXECUTION_FAILURE;
  result = EXECUTION_SUCCESS;
  while (list)
    {
      r = history_expand (list->word->word, &s);
      if (r < 0)
    {
      builtin_error ("%s: history expansion failed", list->word->word);
      result = EXECUTION_FAILURE;
    }
      else
    {
      fputs (s, stdout);
      putchar ('\n');
    }
      FREE (s);
      list = list->next;
    }
  fflush (stdout);
  return result;
}
#endif /* BANG_HISTORY */
#endif /* HISTORY */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/jobs.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/jobs.def used in: Step 1057, Step 1095
This file is jobs.def, from which is created jobs.c.
It implements the builtins "jobs" and "disown" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES jobs.c

$BUILTIN jobs
$FUNCTION jobs_builtin
$DEPENDS_ON JOB_CONTROL
$SHORT_DOC jobs [-lnprs] [jobspec ...] or jobs -x command [args]
Lists the active jobs.  The -l option lists process id's in addition
to the normal information; the -p option lists process id's only.
If -n is given, only processes that have changed status since the last
notification are printed.  JOBSPEC restricts output to that job.  The
-r and -s options restrict output to running and stopped jobs only,
respectively.  Without options, the status of all active jobs is
printed.  If -x is given, COMMAND is run after all job specifications
that appear in ARGS have been replaced with the process ID of that job's
process group leader.
$END

#include <config.h>

#if defined (JOB_CONTROL)
#include "../bashtypes.h"
#include <signal.h>
#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "../bashansi.h"

#include "../shell.h"
#include "../jobs.h"
#include "../execute_cmd.h"
#include "bashgetopt.h"
#include "common.h"

#define JSTATE_ANY  0x0
#define JSTATE_RUNNING  0x1
#define JSTATE_STOPPED  0x2

static int execute_list_with_replacements __P((WORD_LIST *));

/* The `jobs' command.  Prints outs a list of active jobs.  If the
   argument `-l' is given, then the process id's are printed also.
   If the argument `-p' is given, print the process group leader's
   pid only.  If `-n' is given, only processes that have changed
   status since the last notification are printed.  If -x is given,
   replace all job specs with the pid of the appropriate process
   group leader and execute the command.  The -r and -s options mean
   to print info about running and stopped jobs only, respectively. */
int
jobs_builtin (list)
     WORD_LIST *list;
{
  int form, execute, state, opt, any_failed, job;
  sigset_t set, oset;

  if (job_control == 0 && interactive_shell == 0)
    return (EXECUTION_SUCCESS);

  execute = any_failed = 0;
  form = JLIST_STANDARD;
  state = JSTATE_ANY;

  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "lpnxrs")) != -1)
    {
      switch (opt)
    {
    case 'l':
      form = JLIST_LONG;
      break;
    case 'p':
      form = JLIST_PID_ONLY;
      break;
    case 'n':
      form = JLIST_CHANGED_ONLY;
      break;
    case 'x':
      if (form != JLIST_STANDARD)
        {
          builtin_error ("no other options allowed with `-x'");
          return (EXECUTION_FAILURE);
        }
      execute++;
      break;
    case 'r':
      state = JSTATE_RUNNING;
      break;
    case 's':
      state = JSTATE_STOPPED;
      break;

    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }

  list = loptend;

  if (execute)
    return (execute_list_with_replacements (list));

  if (!list)
    {
      switch (state)
    {
    case JSTATE_ANY:
      list_all_jobs (form);
      break;
    case JSTATE_RUNNING:
      list_running_jobs (form);
      break;
    case JSTATE_STOPPED:
      list_stopped_jobs (form);
      break;
    }
      return (EXECUTION_SUCCESS);
    }

  while (list)
    {
      BLOCK_CHILD (set, oset);
      job = get_job_spec (list);

      if ((job == NO_JOB) || !jobs || !jobs[job])
    {
      sh_badjob (list->word->word);
      any_failed++;
    }
      else if (job != DUP_JOB)
    list_one_job ((JOB *)NULL, form, 0, job);

      UNBLOCK_CHILD (oset);
      list = list->next;
    }
  return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
}

static int
execute_list_with_replacements (list)
     WORD_LIST *list;
{
  register WORD_LIST *l;
  int job, result;

  /* First do the replacement of job specifications with pids. */
  for (l = list; l; l = l->next)
    {
      if (l->word->word[0] == '%')  /* we have a winner */
    {
      job = get_job_spec (l);

      /* A bad job spec is not really a job spec! Pass it through. */
      if (job < 0 || job >= job_slots || !jobs[job])
        continue;

      free (l->word->word);
      l->word->word = itos (jobs[job]->pgrp);
    }
    }

  /* Next make a new simple command and execute it. */
  begin_unwind_frame ("jobs_builtin");
  {
    COMMAND *command = (COMMAND *)NULL;

    add_unwind_protect (dispose_command, command);

    command = make_bare_simple_command ();
    command->value.Simple->words = copy_word_list (list);
    command->value.Simple->redirects = (REDIRECT *)NULL;
    command->flags |= CMD_INHIBIT_EXPANSION;
    command->value.Simple->flags |= CMD_INHIBIT_EXPANSION;

    result = execute_command (command);
  }

  run_unwind_frame ("jobs_builtin");
  return (result);
}
#endif /* JOB_CONTROL */

$BUILTIN disown
$FUNCTION disown_builtin
$DEPENDS_ON JOB_CONTROL
$SHORT_DOC disown [-h] [-ar] [jobspec ...]
By default, removes each JOBSPEC argument from the table of active jobs.
If the -h option is given, the job is not removed from the table, but is
marked so that SIGHUP is not sent to the job if the shell receives a
SIGHUP.  The -a option, when JOBSPEC is not supplied, means to remove all
jobs from the job table; the -r option means to remove only running jobs.
$END

#if defined (JOB_CONTROL)
int
disown_builtin (list)
     WORD_LIST *list;
{
  int opt, job, retval, nohup_only, running_jobs, all_jobs;
  sigset_t set, oset;
  intmax_t pid_value;

  nohup_only = running_jobs = all_jobs = 0;
  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "ahr")) != -1)
    {
      switch (opt)
    {
    case 'a':
      all_jobs = 1;
      break;
    case 'h':
      nohup_only = 1;
      break;
    case 'r':
      running_jobs = 1;
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }
  list = loptend;
  retval = EXECUTION_SUCCESS;

  /* `disown -a' or `disown -r' */
  if (list == 0 && (all_jobs || running_jobs))
    {
      if (nohup_only)
    nohup_all_jobs (running_jobs);
      else
    delete_all_jobs (running_jobs);
      return (EXECUTION_SUCCESS);
    }

  do
    {
      BLOCK_CHILD (set, oset);
      job = (list && legal_number (list->word->word, &pid_value) && pid_value == (pid_t) pid_value)
        ? get_job_by_pid ((pid_t) pid_value, 0)
        : get_job_spec (list);

      if (job == NO_JOB || jobs == 0 || job < 0 || job >= job_slots || jobs[job] == 0)
    {
      sh_badjob (list ? list->word->word : "current");
      retval = EXECUTION_FAILURE;
    }
      else if (nohup_only)
    nohup_job (job);
      else
    delete_job (job, 1);
      UNBLOCK_CHILD (oset);

      if (list)
    list = list->next;
    }
  while (list);

  return (retval);
}
#endif /* JOB_CONTROL */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/kill.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/kill.def used in: Step 1057, Step 1097
This file is kill.def, from which is created kill.c.
It implements the builtin "kill" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES kill.c

$BUILTIN kill
$FUNCTION kill_builtin
$DEPENDS_ON JOB_CONTROL
$SHORT_DOC kill [-s sigspec | -n signum | -sigspec] [pid | job]... or kill -l [sigspec]
Send the processes named by PID (or JOB) the signal SIGSPEC.  If
SIGSPEC is not present, then SIGTERM is assumed.  An argument of `-l'
lists the signal names; if arguments follow `-l' they are assumed to
be signal numbers for which names should be listed.  Kill is a shell
builtin for two reasons: it allows job IDs to be used instead of
process IDs, and, if you have reached the limit on processes that
you can create, you don't have to start a process to kill another one.
$END

#include <config.h>

#include <stdio.h>
#include <errno.h>
#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../bashansi.h"

#include "../shell.h"
#include "../trap.h"
#include "../jobs.h"
#include "common.h"

/* Not all systems declare ERRNO in errno.h... and some systems #define it! */
#if !defined (errno)
extern int errno;
#endif /* !errno */

#if defined (JOB_CONTROL)
extern int posixly_correct;

#if !defined (CONTINUE_AFTER_KILL_ERROR)
#  define CONTINUE_OR_FAIL return (EXECUTION_FAILURE)
#else
#  define CONTINUE_OR_FAIL goto continue_killing
#endif /* CONTINUE_AFTER_KILL_ERROR */

/* Here is the kill builtin.  We only have it so that people can type
   kill -KILL %1?  No, if you fill up the process table this way you
   can still kill some. */
int
kill_builtin (list)
     WORD_LIST *list;
{
  int sig, any_succeeded, listing, saw_signal;
  char *sigspec, *word;
  pid_t pid;
  intmax_t pid_value;

  if (list == 0)
    {
      builtin_usage ();
      return (EXECUTION_FAILURE);
    }

  any_succeeded = listing = saw_signal = 0;
  sig = SIGTERM;
  sigspec = "TERM";

  /* Process options. */
  while (list)
    {
      word = list->word->word;

      if (ISOPTION (word, 'l'))
    {
      listing++;
      list = list->next;
    }
      else if (ISOPTION (word, 's') || ISOPTION (word, 'n'))
    {
      list = list->next;
      if (list)
        {
          sigspec = list->word->word;
          if (sigspec[0] == '0' && sigspec[1] == '\0')
        sig = 0;
          else
        sig = decode_signal (sigspec);
          list = list->next;
        }
      else
        {
          sh_needarg (word);
          return (EXECUTION_FAILURE);
        }
    }
      else if (ISOPTION (word, '-'))
    {
      list = list->next;
      break;
    }
      else if (ISOPTION (word, '?'))
    {
      builtin_usage ();
      return (EXECUTION_SUCCESS);
    }
      /* If this is a signal specification then process it.  We only process
     the first one seen; other arguments may signify process groups (e.g,
     -num == process group num). */
      else if ((*word == '-') && !saw_signal)
    {
      sigspec = word + 1;
      sig = decode_signal (sigspec);
      saw_signal++;
      list = list->next;
    }
      else
    break;
    }

  if (listing)
    return (display_signal_list (list, 0));

  /* OK, we are killing processes. */
  if (sig == NO_SIG)
    {
      sh_invalidsig (sigspec);
      return (EXECUTION_FAILURE);
    }

  if (list == 0)
    {
      builtin_usage ();
      return (EXECUTION_FAILURE);
    }

  while (list)
    {
      word = list->word->word;

      if (*word == '-')
    word++;

      /* Use the entire argument in case of minus sign presence. */
      if (*word && legal_number (list->word->word, &pid_value) && (pid_value == (pid_t)pid_value))
    {
      pid = (pid_t) pid_value;

      if ((pid < -1 ? kill_pid (-pid, sig, 1) : kill_pid (pid, sig, 0)) < 0)
        goto signal_error;
      else
        any_succeeded++;
    }
      else if (*list->word->word && *list->word->word != '%')
    {
      builtin_error ("%s: no such pid", list->word->word);
      CONTINUE_OR_FAIL;
    }
      else if (*word && (interactive || job_control))
    /* Posix.2 says you can kill without job control active (4.32.4) */
    {           /* Must be a job spec.  Check it out. */
      int job;
      sigset_t set, oset;

      BLOCK_CHILD (set, oset);
      job = get_job_spec (list);

      if (job < 0 || job >= job_slots || !jobs[job])
        {
          if (job != DUP_JOB)
        sh_badjob (list->word->word);
          UNBLOCK_CHILD (oset);
          CONTINUE_OR_FAIL;
        }

      /* Job spec used.  Kill the process group. If the job was started
         without job control, then its pgrp == shell_pgrp, so we have
         to be careful.  We take the pid of the first job in the pipeline
         in that case. */
      pid = IS_JOBCONTROL (job) ? jobs[job]->pgrp : jobs[job]->pipe->pid;

      UNBLOCK_CHILD (oset);

      if (kill_pid (pid, sig, 1) < 0)
        {
        signal_error:
          if (errno == EINVAL)
        sh_invalidsig (sigspec);
          else
        builtin_error ("(%ld) - %s", (long)pid, strerror (errno));
          CONTINUE_OR_FAIL;
        }
      else
        any_succeeded++;
    }
      else
    {
      sh_badpid (list->word->word);
      CONTINUE_OR_FAIL;
    }
    continue_killing:
      list = list->next;
    }

  return (any_succeeded ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
}
#endif /* JOB_CONTROL */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/let.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/let.def used in: Step 1057, Step 1099
This file is let.def, from which is created let.c.
It implements the builtin "let" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$BUILTIN let
$FUNCTION let_builtin
$PRODUCES let.c
$SHORT_DOC let arg [arg ...]
Each ARG is an arithmetic expression to be evaluated.  Evaluation
is done in fixed-width integers with no check for overflow, though
division by 0 is trapped and flagged as an error.  The following
list of operators is grouped into levels of equal-precedence operators.
The levels are listed in order of decreasing precedence.

    id++, id--  variable post-increment, post-decrement
    ++id, --id  variable pre-increment, pre-decrement
    -, +        unary minus, plus
    !, ~        logical and bitwise negation
    **      exponentiation
    *, /, %     multiplication, division, remainder
    +, -        addition, subtraction
    <<, >>      left and right bitwise shifts
    <=, >=, <, >    comparison
    ==, !=      equality, inequality
    &       bitwise AND
    ^       bitwise XOR
    |       bitwise OR
    &&      logical AND
    ||      logical OR
    expr ? expr : expr
            conditional expression
    =, *=, /=, %=,
    +=, -=, <<=, >>=,
    &=, ^=, |=  assignment

Shell variables are allowed as operands.  The name of the variable
is replaced by its value (coerced to a fixed-width integer) within
an expression.  The variable need not have its integer attribute
turned on to be used in an expression.

Operators are evaluated in order of precedence.  Sub-expressions in
parentheses are evaluated first and may override the precedence
rules above.

If the last ARG evaluates to 0, let returns 1; 0 is returned
otherwise.
$END

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../shell.h"
#include "common.h"

/* Arithmetic LET function. */
int
let_builtin (list)
     WORD_LIST *list;
{
  intmax_t ret;
  int expok;

  /* Skip over leading `--' argument. */
  if (list && list->word && ISOPTION (list->word->word, '-'))
    list = list->next;

  if (list == 0)
    {
      builtin_error ("expression expected");
      return (EXECUTION_FAILURE);
    }

  for (; list; list = list->next)
    {
      ret = evalexp (list->word->word, &expok);
      if (expok == 0)
    return (EXECUTION_FAILURE);
    }

  return ((ret == 0) ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
}

#ifdef INCLUDE_UNUSED
int
exp_builtin (list)
     WORD_LIST *list;
{
  char *exp;
  intmax_t ret;
  int expok;

  if (list == 0)
    {
      builtin_error ("expression expected");
      return (EXECUTION_FAILURE);
    }

  exp = string_list (list);
  ret = evalexp (exp, &expok);
  (void)free (exp);
  return (((ret == 0) || (expok == 0)) ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
}
#endif

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/read.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/read.def used in: Step 1057, Step 1101
This file is read.def, from which is created read.c.
It implements the builtin "read" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES read.c

$BUILTIN read
$FUNCTION read_builtin
$SHORT_DOC read [-ers] [-u fd] [-t timeout] [-p prompt] [-a array] [-n nchars] [-d delim] [name ...]
One line is read from the standard input, or from file descriptor FD if the
-u option is supplied, and the first word is assigned to the first NAME,
the second word to the second NAME, and so on, with leftover words assigned
to the last NAME.  Only the characters found in $IFS are recognized as word
delimiters.  If no NAMEs are supplied, the line read is stored in the REPLY
variable.  If the -r option is given, this signifies `raw' input, and
backslash escaping is disabled.  The -d option causes read to continue
until the first character of DELIM is read, rather than newline.  If the -p
option is supplied, the string PROMPT is output without a trailing newline
before attempting to read.  If -a is supplied, the words read are assigned
to sequential indices of ARRAY, starting at zero.  If -e is supplied and
the shell is interactive, readline is used to obtain the line.  If -n is
supplied with a non-zero NCHARS argument, read returns after NCHARS
characters have been read.  The -s option causes input coming from a
terminal to not be echoed.

The -t option causes read to time out and return failure if a complete line
of input is not read within TIMEOUT seconds.  If the TMOUT variable is set,
its value is the default timeout.  The return code is zero, unless end-of-file
is encountered, read times out, or an invalid file descriptor is supplied as
the argument to -u.
$END

#include <config.h>

#include "bashtypes.h"
#include "posixstat.h"

#include <stdio.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <signal.h>
#include <errno.h>

#ifdef __CYGWIN__
#  include <fcntl.h>
#  include <io.h>
#endif

#include "../shell.h"
#include "common.h"
#include "bashgetopt.h"

#include <shtty.h>

#if defined (READLINE)
#include "../bashline.h"
#include <readline/readline.h>
#endif

#if !defined(errno)
extern int errno;
#endif

extern int interrupt_immediately;

#if defined (READLINE)
static char *edit_line __P((char *));
static void set_eol_delim __P((int));
static void reset_eol_delim __P((char *));
#endif
static SHELL_VAR *bind_read_variable __P((char *, char *));

static sighandler sigalrm __P((int));
static void reset_alarm __P((void));

static procenv_t alrmbuf;
static SigHandler *old_alrm;
static unsigned char delim;

static sighandler
sigalrm (s)
     int s;
{
  longjmp (alrmbuf, 1);
}

static void
reset_alarm ()
{
  set_signal_handler (SIGALRM, old_alrm);
  alarm (0);
}

/* Read the value of the shell variables whose names follow.
   The reading is done from the current input stream, whatever
   that may be.  Successive words of the input line are assigned
   to the variables mentioned in LIST.  The last variable in LIST
   gets the remainder of the words on the line.  If no variables
   are mentioned in LIST, then the default variable is $REPLY. */
int
read_builtin (list)
     WORD_LIST *list;
{
  register char *varname;
  int size, i, pass_next, saw_escape, eof, opt, retval, code;
  int input_is_tty, input_is_pipe, unbuffered_read;
  int raw, edit, nchars, silent, have_timeout, fd;
  unsigned int tmout;
  intmax_t intval;
  char c;
  char *input_string, *orig_input_string, *ifs_chars, *prompt, *arrayname;
  char *e, *t, *t1;
  struct stat tsb;
  SHELL_VAR *var;
#if defined (ARRAY_VARS)
  WORD_LIST *alist;
#endif
#if defined (READLINE)
  char *rlbuf;
  int rlind;
#endif

  USE_VAR(size);
  USE_VAR(i);
  USE_VAR(pass_next);
  USE_VAR(saw_escape);
  USE_VAR(input_is_pipe);
/*  USE_VAR(raw); */
  USE_VAR(edit);
  USE_VAR(tmout);
  USE_VAR(nchars);
  USE_VAR(silent);
  USE_VAR(ifs_chars);
  USE_VAR(prompt);
  USE_VAR(arrayname);
#if defined (READLINE)
  USE_VAR(rlbuf);
  USE_VAR(rlind);
#endif
  USE_VAR(list);

  i = 0;        /* Index into the string that we are reading. */
  raw = edit = 0;   /* Not reading raw input by default. */
  silent = 0;
  arrayname = prompt = (char *)NULL;
  fd = 0;       /* file descriptor to read from */

#if defined (READLINE)
  rlbuf = (char *)0;
  rlind = 0;
#endif

  tmout = 0;        /* no timeout */
  nchars = input_is_tty = input_is_pipe = unbuffered_read = have_timeout = 0;
  delim = '\n';     /* read until newline */

  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "ersa:d:n:p:t:u:")) != -1)
    {
      switch (opt)
    {
    case 'r':
      raw = 1;
      break;
    case 'p':
      prompt = list_optarg;
      break;
    case 's':
      silent = 1;
      break;
    case 'e':
#if defined (READLINE)
      edit = 1;
#endif
      break;
#if defined (ARRAY_VARS)
    case 'a':
      arrayname = list_optarg;
      break;
#endif
    case 't':
      code = legal_number (list_optarg, &intval);
      if (code == 0 || intval < 0 || intval != (unsigned int)intval)
        {
          builtin_error ("%s: invalid timeout specification", list_optarg);
          return (EXECUTION_FAILURE);
        }
      else
        {
          have_timeout = 1;
          tmout = intval;
        }
      break;
    case 'n':
      code = legal_number (list_optarg, &intval);
      if (code == 0 || intval < 0 || intval != (int)intval)
        {
          sh_invalidnum (list_optarg);
          return (EXECUTION_FAILURE);
        }
      else
        nchars = intval;
      break;
    case 'u':
      code = legal_number (list_optarg, &intval);
      if (code == 0 || intval < 0 || intval != (int)intval)
        {
          builtin_error ("%s: invalid file descriptor specification", list_optarg);
          return (EXECUTION_FAILURE);
        }
      else
        fd = intval;
      if (sh_validfd (fd) == 0)
        {
          builtin_error ("%d: invalid file descriptor: %s", fd, strerror (errno));
          return (EXECUTION_FAILURE);
        }
      break;
    case 'd':
      delim = *list_optarg;
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }
  list = loptend;

  /* `read -t 0 var' returns failure immediately.  XXX - should it test
     whether input is available with select/FIONREAD, and fail if those
     are unavailable? */
  if (have_timeout && tmout == 0)
    return (EXECUTION_FAILURE);

  /* IF IFS is unset, we use the default of " \t\n". */
  ifs_chars = getifs ();
  if (ifs_chars == 0)       /* XXX - shouldn't happen */
    ifs_chars = "";

  input_string = (char *)xmalloc (size = 112);  /* XXX was 128 */

  /* $TMOUT, if set, is the default timeout for read. */
  if (have_timeout == 0 && (e = get_string_value ("TMOUT")))
    {
      code = legal_number (e, &intval);
      if (code == 0 || intval < 0 || intval != (unsigned int)intval)
    tmout = 0;
      else
    tmout = intval;
    }

  begin_unwind_frame ("read_builtin");

  input_is_tty = isatty (fd);
  if (input_is_tty == 0)
#ifndef __CYGWIN__
    input_is_pipe = (lseek (0, 0L, SEEK_CUR) < 0) && (errno == ESPIPE);
#else
    input_is_pipe = 1;
#endif

  /* If the -p, -e or -s flags were given, but input is not coming from the
     terminal, turn them off. */
  if ((prompt || edit || silent) && input_is_tty == 0)
    {
      prompt = (char *)NULL;
      edit = silent = 0;
    }

#if defined (READLINE)
  if (edit)
    add_unwind_protect (xfree, rlbuf);
#endif

  if (prompt && edit == 0)
    {
      fprintf (stderr, "%s", prompt);
      fflush (stderr);
    }

  pass_next = 0;    /* Non-zero signifies last char was backslash. */
  saw_escape = 0;   /* Non-zero signifies that we saw an escape char */

  if (tmout > 0)
    {
      /* Turn off the timeout if stdin is a regular file (e.g. from
     input redirection). */
      if ((fstat (fd, &tsb) < 0) || S_ISREG (tsb.st_mode))
    tmout = 0;
    }

  if (tmout > 0)
    {
      code = setjmp (alrmbuf);
      if (code)
    {
      run_unwind_frame ("read_builtin");
      return (EXECUTION_FAILURE);
    }
      old_alrm = set_signal_handler (SIGALRM, sigalrm);
      add_unwind_protect (reset_alarm, (char *)NULL);
      alarm (tmout);
    }

  /* If we've been asked to read only NCHARS chars, or we're using some
     character other than newline to terminate the line, do the right
     thing to readline or the tty. */
  if (nchars > 0 || delim != '\n')
    {
#if defined (READLINE)
      if (edit)
    {
      if (nchars > 0)
        {
          unwind_protect_int (rl_num_chars_to_read);
          rl_num_chars_to_read = nchars;
        }
      if (delim != '\n')
        {
          set_eol_delim (delim);
          add_unwind_protect (reset_eol_delim, (char *)NULL);
        }
    }
      else
#endif
      if (input_is_tty)
    {
      ttsave ();
      if (silent)
        ttcbreak ();
      else
        ttonechar ();
      add_unwind_protect ((Function *)ttrestore, (char *)NULL);
    }
    }
  else if (silent)  /* turn off echo but leave term in canonical mode */
    {
      ttsave ();
      ttnoecho ();
      add_unwind_protect ((Function *)ttrestore, (char *)NULL);
    }

  /* This *must* be the top unwind-protect on the stack, so the manipulation
     of the unwind-protect stack after the realloc() works right. */
  add_unwind_protect (xfree, input_string);
  interrupt_immediately++;

  unbuffered_read = (nchars > 0) || (delim != '\n') || input_is_pipe;

#if defined (__CYGWIN__) && defined (O_TEXT)
  setmode (0, O_TEXT);
#endif

  for (eof = retval = 0;;)
    {
#if defined (READLINE)
      if (edit)
    {
      if (rlbuf && rlbuf[rlind] == '\0')
        {
          xfree (rlbuf);
          rlbuf = (char *)0;
        }
      if (rlbuf == 0)
        {
          rlbuf = edit_line (prompt ? prompt : "");
          rlind = 0;
        }
      if (rlbuf == 0)
        {
          eof = 1;
          break;
        }
      c = rlbuf[rlind++];
    }
      else
    {
#endif

      if (unbuffered_read)
    retval = zread (fd, &c, 1);
      else
    retval = zreadc (fd, &c);

      if (retval <= 0)
    {
      eof = 1;
      break;
    }

#if defined (READLINE)
    }
#endif

      if (i + 2 >= size)
    {
      input_string = (char *)xrealloc (input_string, size += 128);
      remove_unwind_protect ();
      add_unwind_protect (xfree, input_string);
    }

      /* If the next character is to be accepted verbatim, a backslash
     newline pair still disappears from the input. */
      if (pass_next)
    {
      if (c == '\n')
        i--;        /* back up over the CTLESC */
      else
        input_string[i++] = c;
      pass_next = 0;
      continue;
    }

      if (c == '\\' && raw == 0)
    {
      pass_next++;
      saw_escape++;
      input_string[i++] = CTLESC;
      continue;
    }

      if ((unsigned char)c == delim)
    break;

      if (c == CTLESC || c == CTLNUL)
    {
      saw_escape++;
      input_string[i++] = CTLESC;
    }

      input_string[i++] = c;

      if (nchars > 0 && i >= nchars)
    break;
    }
  input_string[i] = '\0';

#if 1
  if (retval < 0)
    {
      builtin_error ("read error: %d: %s", fd, strerror (errno));
      return (EXECUTION_FAILURE);
    }
#endif

  if (tmout > 0)
    reset_alarm ();

  if (nchars > 0 || delim != '\n')
    {
#if defined (READLINE)
      if (edit)
    {
      if (nchars > 0)
        rl_num_chars_to_read = 0;
      if (delim != '\n')
        reset_eol_delim ((char *)NULL);
    }
      else
#endif
      if (input_is_tty)
    ttrestore ();
    }
  else if (silent)
    ttrestore ();

  if (unbuffered_read == 0)
    zsyncfd (fd);

  interrupt_immediately--;
  discard_unwind_frame ("read_builtin");

  retval = eof ? EXECUTION_FAILURE : EXECUTION_SUCCESS;

#if defined (ARRAY_VARS)
  /* If -a was given, take the string read, break it into a list of words,
     an assign them to `arrayname' in turn. */
  if (arrayname)
    {
      var = find_or_make_array_variable (arrayname, 1);
      if (var == 0)
    return EXECUTION_FAILURE;   /* readonly or noassign */
      array_flush (array_cell (var));

      alist = list_string (input_string, ifs_chars, 0);
      if (alist)
    {
      assign_array_var_from_word_list (var, alist);
      dispose_words (alist);
    }
      xfree (input_string);
      return (retval);
    }
#endif /* ARRAY_VARS */ 

  /* If there are no variables, save the text of the line read to the
     variable $REPLY.  ksh93 strips leading and trailing IFS whitespace,
     so that `read x ; echo "$x"' and `read ; echo "$REPLY"' behave the
     same way, but I believe that the difference in behaviors is useful
     enough to not do it.  Without the bash behavior, there is no way
     to read a line completely without interpretation or modification
     unless you mess with $IFS (e.g., setting it to the empty string).
     If you disagree, change the occurrences of `#if 0' to `#if 1' below. */
  if (list == 0)
    {
#if 0
      orig_input_string = input_string;
      for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++)
    ;
      input_string = t;
      input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape);
#endif

      if (saw_escape)
    {
      t = dequote_string (input_string);
      var = bind_variable ("REPLY", t);
      free (t);
    }
      else
    var = bind_variable ("REPLY", input_string);
      VUNSETATTR (var, att_invisible);

      free (input_string);
      return (retval);
    }

  /* This code implements the Posix.2 spec for splitting the words
     read and assigning them to variables. */
  orig_input_string = input_string;

  /* Remove IFS white space at the beginning of the input string.  If
     $IFS is null, no field splitting is performed. */
  for (t = input_string; ifs_chars && *ifs_chars && spctabnl(*t) && isifs(*t); t++)
    ;
  input_string = t;

  for (; list->next; list = list->next)
    {
      varname = list->word->word;
#if defined (ARRAY_VARS)
      if (legal_identifier (varname) == 0 && valid_array_reference (varname) == 0)
#else
      if (legal_identifier (varname) == 0)
#endif
    {
      sh_invalidid (varname);
      xfree (orig_input_string);
      return (EXECUTION_FAILURE);
    }

      /* If there are more variables than words read from the input,
     the remaining variables are set to the empty string. */
      if (*input_string)
    {
      /* This call updates INPUT_STRING. */
      t = get_word_from_string (&input_string, ifs_chars, &e);
      if (t)
        *e = '\0';
      /* Don't bother to remove the CTLESC unless we added one
         somewhere while reading the string. */
      if (t && saw_escape)
        {
          t1 = dequote_string (t);
          var = bind_read_variable (varname, t1);
          xfree (t1);
        }
      else
        var = bind_read_variable (varname, t);
    }
      else
    {
      t = (char *)0;
      var = bind_read_variable (varname, "");
    }

      FREE (t);
      if (var == 0)
    {
      xfree (orig_input_string);
      return (EXECUTION_FAILURE);
    }

      stupidly_hack_special_variables (varname);
      VUNSETATTR (var, att_invisible);
    }

  /* Now assign the rest of the line to the last variable argument. */
#if defined (ARRAY_VARS)
  if (legal_identifier (list->word->word) == 0 && valid_array_reference (list->word->word) == 0)
#else
  if (legal_identifier (list->word->word) == 0)
#endif
    {
      sh_invalidid (list->word->word);
      xfree (orig_input_string);
      return (EXECUTION_FAILURE);
    }

  /* This has to be done this way rather than using string_list
     and list_string because Posix.2 says that the last variable gets the
     remaining words and their intervening separators. */
  input_string = strip_trailing_ifs_whitespace (input_string, ifs_chars, saw_escape);

  if (saw_escape)
    {
      t = dequote_string (input_string);
      var = bind_read_variable (list->word->word, t);
      xfree (t);
    }
  else
    var = bind_read_variable (list->word->word, input_string);
  stupidly_hack_special_variables (list->word->word);
  if (var)
    VUNSETATTR (var, att_invisible);
  xfree (orig_input_string);

  return (retval);
}

static SHELL_VAR *
bind_read_variable (name, value)
     char *name, *value;
{
#if defined (ARRAY_VARS)
  if (valid_array_reference (name) == 0)
    return (bind_variable (name, value));
  else
    return (assign_array_element (name, value));
#else /* !ARRAY_VARS */
  return bind_variable (name, value);
#endif /* !ARRAY_VARS */
}

#if defined (READLINE)
static rl_completion_func_t *old_attempted_completion_function;

static char *
edit_line (p)
     char *p;
{
  char *ret;
  int len;

  if (!bash_readline_initialized)
    initialize_readline ();
  old_attempted_completion_function = rl_attempted_completion_function;
  rl_attempted_completion_function = (rl_completion_func_t *)NULL;
  ret = readline (p);
  rl_attempted_completion_function = old_attempted_completion_function;
  if (ret == 0)
    return ret;
  len = strlen (ret);
  ret = (char *)xrealloc (ret, len + 2);
  ret[len++] = delim;
  ret[len] = '\0';
  return ret;
}

static int old_delim_ctype;
static rl_command_func_t *old_delim_func;
static int old_newline_ctype;
static rl_command_func_t *old_newline_func;

static unsigned char delim_char;

static void
set_eol_delim (c)
     int c;
{
  Keymap cmap;

  if (bash_readline_initialized == 0)
    initialize_readline ();
  cmap = rl_get_keymap ();

  /* Change newline to self-insert */
  old_newline_ctype = cmap[RETURN].type;
  old_newline_func =  cmap[RETURN].function;
  cmap[RETURN].type = ISFUNC;
  cmap[RETURN].function = rl_insert;

  /* Bind the delimiter character to accept-line. */
  old_delim_ctype = cmap[c].type;
  old_delim_func = cmap[c].function;
  cmap[c].type = ISFUNC;
  cmap[c].function = rl_newline;

  delim_char = c;
}

static void
reset_eol_delim (cp)
     char *cp;
{
  Keymap cmap;

  cmap = rl_get_keymap ();

  cmap[RETURN].type = old_newline_ctype;
  cmap[RETURN].function = old_newline_func;

  cmap[delim_char].type = old_delim_ctype;
  cmap[delim_char].function = old_delim_func;
}
#endif

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/return.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/return.def used in: Step 1057, Step 1103
This file is return.def, from which is created return.c.
It implements the builtin "return" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES return.c

$BUILTIN return

$FUNCTION return_builtin
$SHORT_DOC return [n]
Causes a function to exit with the return value specified by N.  If N
is omitted, the return status is that of the last command.
$END

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../shell.h"
#include "common.h"

extern int last_command_exit_value;
extern int subshell_environment;
extern int return_catch_flag, return_catch_value;

/* If we are executing a user-defined function then exit with the value
   specified as an argument.  if no argument is given, then the last
   exit status is used. */
int
return_builtin (list)
     WORD_LIST *list;
{
  return_catch_value = get_exitstat (list);

  if (return_catch_flag)
    longjmp (return_catch, 1);
  else
    {
      builtin_error ("can only `return' from a function or sourced script");
      return (EXECUTION_FAILURE);
    }
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/set.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/set.def used in: Step 1057, Step 1105
This file is set.def, from which is created set.c.
It implements the "set" and "unset" builtins in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES set.c

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>

#include "../bashansi.h"

#include "../shell.h"
#include "../flags.h"
#include "common.h"
#include "bashgetopt.h"

#if defined (READLINE)
#  include "../input.h"
#  include "../bashline.h"
#  include <readline/readline.h>
#endif

#if defined (HISTORY)
#  include "../bashhist.h"
#endif

extern int posixly_correct, ignoreeof, eof_encountered_limit;
#if defined (HISTORY)
extern int dont_save_function_defs;
#endif
#if defined (READLINE)
extern int no_line_editing;
#endif /* READLINE */

$BUILTIN set
$FUNCTION set_builtin
$SHORT_DOC set [--abefhkmnptuvxBCHP] [-o option] [arg ...]
    -a  Mark variables which are modified or created for export.
    -b  Notify of job termination immediately.
    -e  Exit immediately if a command exits with a non-zero status.
    -f  Disable file name generation (globbing).
    -h  Remember the location of commands as they are looked up.
    -k  All assignment arguments are placed in the environment for a
        command, not just those that precede the command name.
    -m  Job control is enabled.
    -n  Read commands but do not execute them.
    -o option-name
        Set the variable corresponding to option-name:
            allexport    same as -a
            braceexpand  same as -B
#if defined (READLINE)
            emacs        use an emacs-style line editing interface
#endif /* READLINE */
            errexit      same as -e
            hashall      same as -h
#if defined (BANG_HISTORY)
            histexpand   same as -H
#endif /* BANG_HISTORY */
#if defined (HISTORY)
            history      enable command history
#endif
            ignoreeof    the shell will not exit upon reading EOF
            interactive-comments
                         allow comments to appear in interactive commands
            keyword      same as -k
            monitor      same as -m
            noclobber    same as -C
            noexec       same as -n
            noglob       same as -f
            nolog        currently accepted but ignored
            notify       same as -b
            nounset      same as -u
            onecmd       same as -t
            physical     same as -P
            posix        change the behavior of bash where the default
                         operation differs from the 1003.2 standard to
                         match the standard
            privileged   same as -p
            verbose      same as -v
#if defined (READLINE)
            vi           use a vi-style line editing interface
#endif /* READLINE */
            xtrace       same as -x
    -p  Turned on whenever the real and effective user ids do not match.
        Disables processing of the $ENV file and importing of shell
        functions.  Turning this option off causes the effective uid and
        gid to be set to the real uid and gid.
    -t  Exit after reading and executing one command.
    -u  Treat unset variables as an error when substituting.
    -v  Print shell input lines as they are read.
    -x  Print commands and their arguments as they are executed.
#if defined (BRACE_EXPANSION)
    -B  the shell will perform brace expansion
#endif /* BRACE_EXPANSION */
    -C  If set, disallow existing regular files to be overwritten
        by redirection of output.
#if defined (BANG_HISTORY)
    -H  Enable ! style history substitution.  This flag is on
        by default.
#endif /* BANG_HISTORY */
    -P  If set, do not follow symbolic links when executing commands
        such as cd which change the current directory.

Using + rather than - causes these flags to be turned off.  The
flags can also be used upon invocation of the shell.  The current
set of flags may be found in $-.  The remaining n ARGs are positional
parameters and are assigned, in order, to $1, $2, .. $n.  If no
ARGs are given, all shell variables are printed.
$END

typedef int setopt_set_func_t __P((int, char *));
typedef int setopt_get_func_t __P((char *));

static void print_minus_o_option __P((char *, int, int));
static void print_all_shell_variables __P((void));

static int set_ignoreeof __P((int, char *));
static int set_posix_mode __P((int, char *));

#if defined (READLINE)
static int set_edit_mode __P((int, char *));
static int get_edit_mode __P((char *));
#endif

#if defined (HISTORY)
static int bash_set_history __P((int, char *));
#endif

static char *on = "on";
static char *off = "off";

/* A struct used to match long options for set -o to the corresponding
   option letter or internal variable.  The functions can be called to
   dynamically generate values. */
struct {
  char *name;
  int letter;
  int *variable;
  setopt_set_func_t *set_func;
  setopt_get_func_t *get_func;
} o_options[] = {
  { "allexport",  'a', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
#if defined (BRACE_EXPANSION)
  { "braceexpand",'B', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
#endif
#if defined (READLINE)
  { "emacs",     '\0', (int *)NULL, set_edit_mode, get_edit_mode },
#endif
  { "errexit",    'e', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
  { "hashall",    'h', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
#if defined (BANG_HISTORY)
  { "histexpand", 'H', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
#endif /* BANG_HISTORY */
#if defined (HISTORY)
  { "history",   '\0', &remember_on_history, bash_set_history, (setopt_get_func_t *)NULL },
#endif
  { "ignoreeof", '\0', &ignoreeof, set_ignoreeof, (setopt_get_func_t *)NULL },
  { "interactive-comments", '\0', &interactive_comments, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
  { "keyword",    'k', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
  { "monitor",    'm', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
  { "noclobber",  'C', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
  { "noexec",     'n', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
  { "noglob",     'f', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
#if defined (HISTORY)
  { "nolog",     '\0', &dont_save_function_defs, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
#endif
#if defined (JOB_CONTROL)
  { "notify",     'b', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
#endif /* JOB_CONTROL */
  { "nounset",    'u', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
  { "onecmd",     't', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
  { "physical",   'P', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
  { "posix",     '\0', &posixly_correct, set_posix_mode, (setopt_get_func_t *)NULL },
  { "privileged", 'p', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
  { "verbose",    'v', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
#if defined (READLINE)
  { "vi",        '\0', (int *)NULL, set_edit_mode, get_edit_mode },
#endif
  { "xtrace",     'x', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL  },
  {(char *)NULL, 0 , (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL },
};

#define N_O_OPTIONS (sizeof (o_options) / sizeof (o_options[0]))

#define GET_BINARY_O_OPTION_VALUE(i, name) \
  ((o_options[i].get_func) ? (*o_options[i].get_func) (name) \
               : (*o_options[i].variable))

#define SET_BINARY_O_OPTION_VALUE(i, onoff, name) \
  ((o_options[i].set_func) ? (*o_options[i].set_func) (onoff, name) \
               : (*o_options[i].variable = (onoff == FLAG_ON)))

int
minus_o_option_value (name)
     char *name;
{
  register int  i;
  int *on_or_off;

  for (i = 0; o_options[i].name; i++)
    {
      if (STREQ (name, o_options[i].name))
    {
      if (o_options[i].letter)
        {
          on_or_off = find_flag (o_options[i].letter);
          return ((on_or_off == FLAG_UNKNOWN) ? -1 : *on_or_off);
        }
      else
        return (GET_BINARY_O_OPTION_VALUE (i, name));
    }
    }

  return (-1);
}

#define MINUS_O_FORMAT "%-15s\t%s\n"

static void
print_minus_o_option (name, value, pflag)
     char *name;
     int value, pflag;
{
  if (pflag == 0)
    printf (MINUS_O_FORMAT, name, value ? on : off);
  else
    printf ("set %co %s\n", value ? '-' : '+', name);
}

void
list_minus_o_opts (mode, reusable)
     int mode, reusable;
{
  register int  i;
  int *on_or_off, value;

  for (i = 0; o_options[i].name; i++)
    {
      if (o_options[i].letter)
    {
      value = 0;
      on_or_off = find_flag (o_options[i].letter);
      if (on_or_off == FLAG_UNKNOWN)
        on_or_off = &value;
      if (mode == -1 || mode == *on_or_off)
        print_minus_o_option (o_options[i].name, *on_or_off, reusable);
    }
      else
    {
      value = GET_BINARY_O_OPTION_VALUE (i, o_options[i].name);
      if (mode == -1 || mode == value)
        print_minus_o_option (o_options[i].name, value, reusable);
    }
    }
}

char **
get_minus_o_opts ()
{
  char **ret;
  int i;

  ret = strvec_create (N_O_OPTIONS + 1);
  for (i = 0; o_options[i].name; i++)
    ret[i] = o_options[i].name;
  ret[i] = (char *)NULL;
  return ret;
}

static int
set_ignoreeof (on_or_off, option_name)
     int on_or_off;
     char *option_name;
{
  ignoreeof = on_or_off == FLAG_ON;
  unbind_variable ("ignoreeof");
  if (ignoreeof)
    bind_variable ("IGNOREEOF", "10"); 
  else
    unbind_variable ("IGNOREEOF");
  sv_ignoreeof ("IGNOREEOF");
  return 0;
}

static int
set_posix_mode (on_or_off, option_name)
     int on_or_off;
     char *option_name;
{
  posixly_correct = on_or_off == FLAG_ON;
  if (posixly_correct == 0)
    unbind_variable ("POSIXLY_CORRECT");
  else
    bind_variable ("POSIXLY_CORRECT", "y");
  sv_strict_posix ("POSIXLY_CORRECT");
  return (0);
}

#if defined (READLINE)
/* Magic.  This code `knows' how readline handles rl_editing_mode. */
static int
set_edit_mode (on_or_off, option_name)
     int on_or_off;
     char *option_name;
{
  int isemacs;

  if (on_or_off == FLAG_ON)
    {
      rl_variable_bind ("editing-mode", option_name);

      if (interactive)
    with_input_from_stdin ();
      no_line_editing = 0;
    }
  else
    {
      isemacs = rl_editing_mode == 1;
      if ((isemacs && *option_name == 'e') || (!isemacs && *option_name == 'v'))
    {
      if (interactive)
        with_input_from_stream (stdin, "stdin");
      no_line_editing = 1;
    }
    }
  return 1-no_line_editing;
}

static int
get_edit_mode (name)
     char *name;
{
  return (*name == 'e' ? no_line_editing == 0 && rl_editing_mode == 1
               : no_line_editing == 0 && rl_editing_mode == 0);
}
#endif /* READLINE */

#if defined (HISTORY)
static int
bash_set_history (on_or_off, option_name)
     int on_or_off;
     char *option_name;
{
  if (on_or_off == FLAG_ON)
    {
      bash_history_enable ();
      if (history_lines_this_session == 0)
    load_history ();
    }
  else
    bash_history_disable ();
  return (1 - remember_on_history);
}
#endif

int
set_minus_o_option (on_or_off, option_name)
     int on_or_off;
     char *option_name;
{
  register int i;

  for (i = 0; o_options[i].name; i++)
    {
      if (STREQ (option_name, o_options[i].name))
    {
      if (o_options[i].letter == 0)
        {
          SET_BINARY_O_OPTION_VALUE (i, on_or_off, option_name);
          return (EXECUTION_SUCCESS);
        }
      else
        {
          if (change_flag (o_options[i].letter, on_or_off) == FLAG_ERROR)
        {
          sh_invalidoptname (option_name);
          return (EXECUTION_FAILURE);
        }
          else
        return (EXECUTION_SUCCESS);
        }

    }
    }

  sh_invalidoptname (option_name);
  return (EXECUTION_FAILURE);
}

static void
print_all_shell_variables ()
{
  SHELL_VAR **vars;

  vars = all_shell_variables ();
  if (vars)
    {
      print_var_list (vars);
      free (vars);
    }

  /* POSIX.2 does not allow function names and definitions to be output when
     `set' is invoked without options (PASC Interp #202). */
  if (posixly_correct == 0)
    {
      vars = all_shell_functions ();
      if (vars)
    {
      print_func_list (vars);
      free (vars);
    }
    }
}

void
set_shellopts ()
{
  char *value;
  char tflag[N_O_OPTIONS];
  int vsize, i, vptr, *ip, exported;
  SHELL_VAR *v;

  for (vsize = i = 0; o_options[i].name; i++)
    {
      tflag[i] = 0;
      if (o_options[i].letter)
    {
      ip = find_flag (o_options[i].letter);
      if (ip && *ip)
        {
          vsize += strlen (o_options[i].name) + 1;
          tflag[i] = 1;
        }
    }
      else if (GET_BINARY_O_OPTION_VALUE (i, o_options[i].name))
    {
      vsize += strlen (o_options[i].name) + 1;
      tflag[i] = 1;
    }
    }

  value = (char *)xmalloc (vsize + 1);

  for (i = vptr = 0; o_options[i].name; i++)
    {
      if (tflag[i])
    {
      strcpy (value + vptr, o_options[i].name);
      vptr += strlen (o_options[i].name);
      value[vptr++] = ':';
    }
    }

  if (vptr)
    vptr--;         /* cut off trailing colon */
  value[vptr] = '\0';

  v = find_variable ("SHELLOPTS");

  /* Turn off the read-only attribute so we can bind the new value, and
     note whether or not the variable was exported. */
  if (v)
    {
      VUNSETATTR (v, att_readonly);
      exported = exported_p (v);
    }
  else
    exported = 0;

  v = bind_variable ("SHELLOPTS", value);

  /* Turn the read-only attribute back on, and turn off the export attribute
     if it was set implicitly by mark_modified_vars and SHELLOPTS was not
     exported before we bound the new value. */
  VSETATTR (v, att_readonly);
  if (mark_modified_vars && exported == 0 && exported_p (v))
    VUNSETATTR (v, att_exported);

  free (value);
}

void
parse_shellopts (value)
     char *value;
{
  char *vname;
  int vptr;

  vptr = 0;
  while (vname = extract_colon_unit (value, &vptr))
    {
      set_minus_o_option (FLAG_ON, vname);
      free (vname);
    }
}

void
initialize_shell_options (no_shellopts)
     int no_shellopts;
{
  char *temp;
  SHELL_VAR *var;

  if (no_shellopts == 0)
    {
      var = find_variable ("SHELLOPTS");
      /* set up any shell options we may have inherited. */
      if (var && imported_p (var))
    {
      temp = (array_p (var)) ? (char *)NULL : savestring (value_cell (var));
      if (temp)
        {
          parse_shellopts (temp);
          free (temp);
        }
    }
    }

  /* Set up the $SHELLOPTS variable. */
  set_shellopts ();
}

/* Reset the values of the -o options that are not also shell flags.  This is
   called from execute_cmd.c:initialize_subshell() when setting up a subshell
   to run an executable shell script without a leading `#!'. */
void
reset_shell_options ()
{
#if defined (HISTORY)
  remember_on_history = 1;
#endif
  ignoreeof = 0;
}

/* Set some flags from the word values in the input list.  If LIST is empty,
   then print out the values of the variables instead.  If LIST contains
   non-flags, then set $1 - $9 to the successive words of LIST. */
int
set_builtin (list)
     WORD_LIST *list;
{
  int on_or_off, flag_name, force_assignment, opts_changed;
  WORD_LIST *l;
  register char *arg;
  char s[3];

  if (list == 0)
    {
      print_all_shell_variables ();
      return (EXECUTION_SUCCESS);
    }

  /* Check validity of flag arguments. */
  reset_internal_getopt ();
  while ((flag_name = internal_getopt (list, optflags)) != -1)
    {
      switch (flag_name)
    {
      case '?':
        builtin_usage ();
        return (list_optopt == '?' ? EXECUTION_SUCCESS : EX_USAGE);
      default:
        break;
    }
    }
    
  /* Do the set command.  While the list consists of words starting with
     '-' or '+' treat them as flags, otherwise, start assigning them to
     $1 ... $n. */
  for (force_assignment = opts_changed = 0; list; )
    {
      arg = list->word->word;

      /* If the argument is `--' or `-' then signal the end of the list
     and remember the remaining arguments. */
      if (arg[0] == '-' && (!arg[1] || (arg[1] == '-' && !arg[2])))
    {
      list = list->next;

      /* `set --' unsets the positional parameters. */
      if (arg[1] == '-')
        force_assignment = 1;

      /* Until told differently, the old shell behaviour of
         `set - [arg ...]' being equivalent to `set +xv [arg ...]'
         stands.  Posix.2 says the behaviour is marked as obsolescent. */
      else
        {
          change_flag ('x', '+');
          change_flag ('v', '+');
          opts_changed = 1;
        }

      break;
    }

      if ((on_or_off = *arg) && (on_or_off == '-' || on_or_off == '+'))
    {
      while (flag_name = *++arg)
        {
          if (flag_name == '?')
        {
          builtin_usage ();
          return (EXECUTION_SUCCESS);
        }
          else if (flag_name == 'o') /* -+o option-name */
        {
          char *option_name;
          WORD_LIST *opt;

          opt = list->next;

          if (opt == 0)
            {
              list_minus_o_opts (-1, (on_or_off == '+'));
              continue;
            }

          option_name = opt->word->word;

          if (option_name == 0 || *option_name == '\0' ||
              *option_name == '-' || *option_name == '+')
            {
              list_minus_o_opts (-1, (on_or_off == '+'));
              continue;
            }
          list = list->next; /* Skip over option name. */

          opts_changed = 1;
          if (set_minus_o_option (on_or_off, option_name) != EXECUTION_SUCCESS)
            {
              set_shellopts ();
              return (EXECUTION_FAILURE);
            }
        }
          else if (change_flag (flag_name, on_or_off) == FLAG_ERROR)
        {
          s[0] = on_or_off;
          s[1] = flag_name;
          s[2] = '\0';
          sh_invalidopt (s);
          builtin_usage ();
          set_shellopts ();
          return (EXECUTION_FAILURE);
        }
          opts_changed = 1;
        }
    }
      else
    {
      break;
    }
      list = list->next;
    }

  /* Assigning $1 ... $n */
  if (list || force_assignment)
    remember_args (list, 1);
  /* Set up new value of $SHELLOPTS */
  if (opts_changed)
    set_shellopts ();
  return (EXECUTION_SUCCESS);
}

$BUILTIN unset
$FUNCTION unset_builtin
$SHORT_DOC unset [-f] [-v] [name ...]
For each NAME, remove the corresponding variable or function.  Given
the `-v', unset will only act on variables.  Given the `-f' flag,
unset will only act on functions.  With neither flag, unset first
tries to unset a variable, and if that fails, then tries to unset a
function.  Some variables cannot be unset; also see readonly.
$END

#define NEXT_VARIABLE() any_failed++; list = list->next; continue;

int
unset_builtin (list)
  WORD_LIST *list;
{
  int unset_function, unset_variable, unset_array, opt, any_failed;
  char *name;

  unset_function = unset_variable = unset_array = any_failed = 0;

  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "fv")) != -1)
    {
      switch (opt)
    {
    case 'f':
      unset_function = 1;
      break;
    case 'v':
      unset_variable = 1;
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }

  list = loptend;

  if (unset_function && unset_variable)
    {
      builtin_error ("cannot simultaneously unset a function and a variable");
      return (EXECUTION_FAILURE);
    }

  while (list)
    {
      SHELL_VAR *var;
      int tem;
#if defined (ARRAY_VARS)
      char *t;
#endif

      name = list->word->word;

#if defined (ARRAY_VARS)
      unset_array = 0;
      if (!unset_function && valid_array_reference (name))
    {
      t = strchr (name, '[');
      *t++ = '\0';
      unset_array++;
    }
#endif

      /* Bash allows functions with names which are not valid identifiers
     to be created when not in posix mode, so check only when in posix
     mode when unsetting a function. */
      if (((unset_function && posixly_correct) || !unset_function) && legal_identifier (name) == 0)
    {
      sh_invalidid (name);
      NEXT_VARIABLE ();
    }

      var = unset_function ? find_function (name) : find_variable (name);

      if (var && !unset_function && non_unsettable_p (var))
    {
      builtin_error ("%s: cannot unset", name);
      NEXT_VARIABLE ();
    }

      /* Posix.2 says that unsetting readonly variables is an error. */
      if (var && readonly_p (var))
    {
      builtin_error ("%s: cannot unset: readonly %s",
             name, unset_function ? "function" : "variable");
      NEXT_VARIABLE ();
    }

      /* Unless the -f option is supplied, the name refers to a variable. */
#if defined (ARRAY_VARS)
      if (var && unset_array)
    {
      if (array_p (var) == 0)
        {
          builtin_error ("%s: not an array variable", name);
          NEXT_VARIABLE ();
        }
      else
        {
          tem = unbind_array_element (var, t);
          if (tem == -1)
        any_failed++;
        }
    }
      else
#endif /* ARRAY_VARS */
      tem = unset_function ? unbind_func (name) : unbind_variable (name);

      /* This is what Posix.2 draft 11+ says.  ``If neither -f nor -v
     is specified, the name refers to a variable; if a variable by
     that name does not exist, a function by that name, if any,
     shall be unset.'' */
      if (tem == -1 && !unset_function && !unset_variable)
    tem = unbind_func (name);

      /* SUSv3, POSIX.1-2001 say:  ``Unsetting a variable or function that
     was not previously set shall not be considered an error.'' */

      if (unset_function == 0)
    stupidly_hack_special_variables (name);

      list = list->next;
    }

  return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/setattr.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/setattr.def used in: Step 1057, Step 1107
This file is setattr.def, from which is created setattr.c.
It implements the builtins "export" and "readonly", in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES setattr.c

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>
#include "../bashansi.h"

#include "../shell.h"
#include "common.h"
#include "bashgetopt.h"

extern int posixly_correct;
extern int array_needs_making;
extern char *this_command_name;
extern sh_builtin_func_t *this_shell_builtin;

#ifdef ARRAY_VARS
extern int declare_builtin __P((WORD_LIST *));
#endif

#define READONLY_OR_EXPORT \
  (this_shell_builtin == readonly_builtin || this_shell_builtin == export_builtin)

$BUILTIN export
$FUNCTION export_builtin
$SHORT_DOC export [-nf] [name[=value] ...] or export -p
NAMEs are marked for automatic export to the environment of
subsequently executed commands.  If the -f option is given,
the NAMEs refer to functions.  If no NAMEs are given, or if `-p'
is given, a list of all names that are exported in this shell is
printed.  An argument of `-n' says to remove the export property
from subsequent NAMEs.  An argument of `--' disables further option
processing.
$END

/* For each variable name in LIST, make that variable appear in the
   environment passed to simple commands.  If there is no LIST, then
   print all such variables.  An argument of `-n' says to remove the
   exported attribute from variables named in LIST.  An argument of
  -f indicates that the names present in LIST refer to functions. */
int
export_builtin (list)
     register WORD_LIST *list;
{
  return (set_or_show_attributes (list, att_exported, 0));
}

$BUILTIN readonly
$FUNCTION readonly_builtin
$SHORT_DOC readonly [-anf] [name[=value] ...] or readonly -p
The given NAMEs are marked readonly and the values of these NAMEs may
not be changed by subsequent assignment.  If the -f option is given,
then functions corresponding to the NAMEs are so marked.  If no
arguments are given, or if `-p' is given, a list of all readonly names
is printed.  An argument of `-n' says to remove the readonly property
from subsequent NAMEs.  The `-a' option means to treat each NAME as
an array variable.  An argument of `--' disables further option
processing.
$END

/* For each variable name in LIST, make that variable readonly.  Given an
   empty LIST, print out all existing readonly variables. */
int
readonly_builtin (list)
     register WORD_LIST *list;
{
  return (set_or_show_attributes (list, att_readonly, 0));
}

#if defined (ARRAY_VARS)
#  define ATTROPTS  "afnp"
#else
#  define ATTROPTS  "fnp"
#endif

/* For each variable name in LIST, make that variable have the specified
   ATTRIBUTE.  An arg of `-n' says to remove the attribute from the the
   remaining names in LIST. */
int
set_or_show_attributes (list, attribute, nodefs)
     register WORD_LIST *list;
     int attribute, nodefs;
{
  register SHELL_VAR *var;
  int assign, undo, functions_only, arrays_only, any_failed, assign_error, opt;
  char *name;
#if defined (ARRAY_VARS)
  WORD_LIST *nlist, *tlist;
  WORD_DESC *w;
#endif

  undo = functions_only = arrays_only = any_failed = assign_error = 0;
  /* Read arguments from the front of the list. */
  reset_internal_getopt ();
  while ((opt = internal_getopt (list, ATTROPTS)) != -1)
    {
      switch (opt)
    {
      case 'n':
        undo = 1;
        break;
      case 'f':
        functions_only = 1;
        break;
#if defined (ARRAY_VARS)
      case 'a':
         arrays_only = 1;
         break;
#endif
      case 'p':
        break;
      default:
        builtin_usage ();
        return (EX_USAGE);
    }
    }
  list = loptend;

  if (list)
    {
      if (attribute & att_exported)
    array_needs_making = 1;

      /* Cannot undo readonly status, silently disallowed. */
      if (undo && (attribute & att_readonly))
    attribute &= ~att_readonly;

      while (list)
    {
      name = list->word->word;

      if (functions_only)       /* xxx -f name */
        {
          var = find_function (name);
          if (var == 0)
        {
          builtin_error ("%s: not a function", name);
          any_failed++;
        }
          else
        SETVARATTR (var, attribute, undo);

          list = list->next;
          continue;
        }

      /* xxx [-np] name[=value] */
      assign = assignment (name);

      if (assign)
        name[assign] = '\0';

      if (legal_identifier (name) == 0)
        {
          sh_invalidid (name);
          if (assign)
        assign_error++;
          else
        any_failed++;
          list = list->next;
          continue;
        }

      if (assign)   /* xxx [-np] name=value */
        {
          name[assign] = '=';
#if defined (ARRAY_VARS)
          /* Let's try something here.  Turn readonly -a xxx=yyy into
         declare -ra xxx=yyy and see what that gets us. */
          if (arrays_only)
        {
          tlist = list->next;
          list->next = (WORD_LIST *)NULL;
          w = make_word ("-ra");
          nlist = make_word_list (w, list);
          opt = declare_builtin (nlist);
          if (opt != EXECUTION_SUCCESS)
            assign_error++;
          list->next = tlist;
          dispose_word (w);
          free (nlist);
        }
          else
#endif
          /* This word has already been expanded once with command
         and parameter expansion.  Call do_assignment_no_expand (),
         which does not do command or parameter substitution.  If
         the assignment is not performed correctly, flag an error. */
          if (do_assignment_no_expand (name) == 0)
        assign_error++;
          name[assign] = '\0';
        }

      set_var_attribute (name, attribute, undo);
      list = list->next;
    }
    }
  else
    {
      SHELL_VAR **variable_list;
      register int i;

      if ((attribute & att_function) || functions_only)
    {
      variable_list = all_shell_functions ();
      if (attribute != att_function)
        attribute &= ~att_function; /* so declare -xf works, for example */
    }
      else
    variable_list = all_shell_variables ();

#if defined (ARRAY_VARS)
      if (attribute & att_array)
    {
      arrays_only++;
      if (attribute != att_array)
        attribute &= ~att_array;
    }
#endif

      if (variable_list)
    {
      for (i = 0; var = variable_list[i]; i++)
        {
#if defined (ARRAY_VARS)
          if (arrays_only && array_p (var) == 0)
        continue;
#endif
          if ((var->attributes & attribute))
        show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
        }
      free (variable_list);
    }
    }

  return (assign_error ? EX_BADASSIGN
               : ((any_failed == 0) ? EXECUTION_SUCCESS
                        : EXECUTION_FAILURE));
}

/* Show the attributes for shell variable VAR.  If NODEFS is non-zero,
   don't show function definitions along with the name.  If PATTR is
   non-zero, it indicates we're being called from `export' or `readonly'.
   In POSIX mode, this prints the name of the calling builtin (`export'
   or `readonly') instead of `declare', and doesn't print function defs
   when called by `export' or `readonly'. */
int
show_var_attributes (var, pattr, nodefs)
     SHELL_VAR *var;
     int pattr, nodefs;
{
  char flags[8], *x;
  int i;

  i = 0;

  /* pattr == 0 means we are called from `declare'. */
  if (pattr == 0 || posixly_correct == 0)
    {
#if defined (ARRAY_VARS)
      if (array_p (var))
    flags[i++] = 'a';
#endif

      if (function_p (var))
    flags[i++] = 'f';

      if (integer_p (var))
    flags[i++] = 'i';

      if (readonly_p (var))
    flags[i++] = 'r';

      if (trace_p (var))
    flags[i++] = 't';

      if (exported_p (var))
    flags[i++] = 'x';
    }
  else
    {
#if defined (ARRAY_VARS)
      if (array_p (var))
    flags[i++] = 'a';
#endif

      if (function_p (var))
    flags[i++] = 'f';
    }

  flags[i] = '\0';

  /* If we're printing functions with definitions, print the function def
     first, then the attributes, instead of printing output that can't be
     reused as input to recreate the current state. */
  if (function_p (var) && nodefs == 0 && (pattr == 0 || posixly_correct == 0))
    {
      printf ("%s\n", named_function_string (var->name, function_cell (var), 1));
      nodefs++;
      if (pattr == 0 && i == 1 && flags[0] == 'f')
    return 0;       /* don't print `declare -f name' */
    }

  if (pattr == 0 || posixly_correct == 0)
    printf ("declare -%s ", i ? flags : "-");
  else if (i)
    printf ("%s -%s ", this_command_name, flags);
  else
    printf ("%s ", this_command_name);

#if defined (ARRAY_VARS)
 if (array_p (var))
    print_array_assignment (var, 1);
  else
#endif
  /* force `readonly' and `export' to not print out function definitions
     when in POSIX mode. */
  if (nodefs || (function_p (var) && pattr != 0 && posixly_correct))
    printf ("%s\n", var->name);
  else if (function_p (var))
    printf ("%s\n", named_function_string (var->name, function_cell (var), 1));
  else if (invisible_p (var))
    printf ("%s\n", var->name);
  else
    {
      x = sh_double_quote (var_isset (var) ? value_cell (var) : "");
      printf ("%s=%s\n", var->name, x);
      free (x);
    }
  return (0);
}

int
show_name_attributes (name, nodefs)
     char *name;
     int nodefs;
{
  SHELL_VAR *var;

  var = find_variable_internal (name, 1);

  if (var && invisible_p (var) == 0)
    {
      show_var_attributes (var, READONLY_OR_EXPORT, nodefs);
      return (0);
    }
  else
    return (1);
}

void
set_var_attribute (name, attribute, undo)
     char *name;
     int attribute, undo;
{
  SHELL_VAR *var, *tv;
  char *tvalue;

  if (undo)
    var = find_variable (name);
  else
    {
      tv = find_tempenv_variable (name);
      /* XXX -- need to handle case where tv is a temp variable in a
     function-scope context, since function_env has been merged into
     the local variables table. */
      if (tv && tempvar_p (tv))
    {
      tvalue = var_isset (tv) ? savestring (value_cell (tv)) : savestring ("");

      var = bind_variable (tv->name, tvalue);
      var->attributes |= tv->attributes & ~att_tempvar;
      VSETATTR (tv, att_propagate);
      if (var->context != 0)
        VSETATTR (var, att_propagate);
      SETVARATTR (tv, attribute, undo); /* XXX */

      free (tvalue);
    }
      else
    {
      var = find_variable_internal (name, 0);
      if (var == 0)
        {
          var = bind_variable (name, (char *)NULL);
          VSETATTR (var, att_invisible);
        }
      else if (var->context != 0)
        VSETATTR (var, att_propagate);
    }
    }

  if (var)
    SETVARATTR (var, attribute, undo);

  if (var && (exported_p (var) || (attribute & att_exported)))
    array_needs_making++;   /* XXX */
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/shift.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/shift.def used in: Step 1057, Step 1109
This file is shift.def, from which is created shift.c.
It implements the builtin "shift" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES shift.c

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../bashansi.h"

#include "../shell.h"
#include "common.h"

$BUILTIN shift
$FUNCTION shift_builtin
$SHORT_DOC shift [n]
The positional parameters from $N+1 ... are renamed to $1 ...  If N is
not given, it is assumed to be 1.
$END

int print_shift_error;

/* Shift the arguments ``left''.  Shift DOLLAR_VARS down then take one
   off of REST_OF_ARGS and place it into DOLLAR_VARS[9].  If LIST has
   anything in it, it is a number which says where to start the
   shifting.  Return > 0 if `times' > $#, otherwise 0. */
int
shift_builtin (list)
     WORD_LIST *list;
{
  intmax_t times;
  register int count;
  WORD_LIST *temp;

  times = get_numeric_arg (list, 0);

  if (times == 0)
    return (EXECUTION_SUCCESS);
  else if (times < 0)
    {
      sh_erange (list->word->word, "shift count");
      return (EXECUTION_FAILURE);
    }
  else if (times > number_of_args ())
    {
      if (print_shift_error)
    sh_erange (list->word->word, "shift count");
      return (EXECUTION_FAILURE);
    }

  while (times-- > 0)
    {
      if (dollar_vars[1])
    free (dollar_vars[1]);

      for (count = 1; count < 9; count++)
    dollar_vars[count] = dollar_vars[count + 1];

      if (rest_of_args)
    {
      temp = rest_of_args;
      dollar_vars[9] = savestring (temp->word->word);
      rest_of_args = rest_of_args->next;
      temp->next = (WORD_LIST *)NULL;
      dispose_words (temp);
    }
      else
    dollar_vars[9] = (char *)NULL;
    }
  return (EXECUTION_SUCCESS);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/source.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/source.def used in: Step 1057, Step 1111
This file is source.def, from which is created source.c.
It implements the builtins "." and  "source" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES source.c

$BUILTIN source
$FUNCTION source_builtin
$SHORT_DOC source filename
Read and execute commands from FILENAME and return.  The pathnames
in $PATH are used to find the directory containing FILENAME.
$END
$BUILTIN .
$DOCNAME dot
$FUNCTION source_builtin
$SHORT_DOC . filename
Read and execute commands from FILENAME and return.  The pathnames
in $PATH are used to find the directory containing FILENAME.
$END
/* source.c - Implements the `.' and `source' builtins. */

#include <config.h>

#include "../bashtypes.h"
#include "posixstat.h"
#include "filecntl.h"
#ifndef _MINIX
#  include <sys/file.h>
#endif
#include <errno.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "../bashansi.h"

#include "../shell.h"
#include "../findcmd.h"
#include "common.h"
#include "bashgetopt.h"

#if !defined (errno)
extern int errno;
#endif /* !errno */

#if defined (RESTRICTED_SHELL)
extern int restricted;
#endif

/* If non-zero, `.' uses $PATH to look up the script to be sourced. */
int source_uses_path = 1;

/* If non-zero, `.' looks in the current directory if the filename argument
   is not found in the $PATH. */
int source_searches_cwd = 1;

/* If this . script is supplied arguments, we save the dollar vars and
   replace them with the script arguments for the duration of the script's
   execution.  If the script does not change the dollar vars, we restore
   what we saved.  If the dollar vars are changed in the script, and we are
   not executing a shell function, we leave the new values alone and free
   the saved values. */
static void
maybe_pop_dollar_vars ()
{
  if (variable_context == 0 && (dollar_vars_changed () & ARGS_SETBLTIN))
    dispose_saved_dollar_vars ();
  else
    pop_dollar_vars ();
  set_dollar_vars_unchanged ();
}

/* Read and execute commands from the file passed as argument.  Guess what.
   This cannot be done in a subshell, since things like variable assignments
   take place in there.  So, I open the file, place it into a large string,
   close the file, and then execute the string. */
int
source_builtin (list)
     WORD_LIST *list;
{
  int result;
  char *filename;

  if (no_options (list))
    return (EX_USAGE);
  list = loptend;

  if (list == 0)
    {
      builtin_error ("filename argument required");
      builtin_usage ();
      return (EX_USAGE);
    }

#if defined (RESTRICTED_SHELL)
  if (restricted && strchr (list->word->word, '/'))
    {
      sh_restricted (list->word->word);
      return (EXECUTION_FAILURE);
    }
#endif

  filename = (char *)NULL;
  if (source_uses_path)
    filename = find_path_file (list->word->word);
  if (filename == 0)
    {
      if (source_searches_cwd == 0)
    {
      builtin_error ("%s: file not found", list->word->word);
      return (EXECUTION_FAILURE);
    }
      else
    filename = savestring (list->word->word);
    }

  begin_unwind_frame ("source");
  add_unwind_protect ((Function *)xfree, filename);

  if (list->next)
    {
      push_dollar_vars ();
      add_unwind_protect ((Function *)maybe_pop_dollar_vars, (char *)NULL);
      remember_args (list->next, 1);
    }
  set_dollar_vars_unchanged ();

  result = source_file (filename);

  run_unwind_frame ("source");

  return (result);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/suspend.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/suspend.def used in: Step 1057, Step 1113
This file is suspend.def, from which is created suspend.c.
It implements the builtin "suspend" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES suspend.c

$BUILTIN suspend
$DEPENDS_ON JOB_CONTROL
$FUNCTION suspend_builtin
$SHORT_DOC suspend [-f]
Suspend the execution of this shell until it receives a SIGCONT
signal.  The `-f' if specified says not to complain about this
being a login shell if it is; just suspend anyway.
$END

#include <config.h>

#if defined (JOB_CONTROL)
#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../bashtypes.h"
#include <signal.h>
#include "../shell.h"
#include "../jobs.h"
#include "common.h"
#include "bashgetopt.h"

static SigHandler *old_cont;
#if 0
static SigHandler *old_stop;
#endif

/* Continue handler. */
sighandler
suspend_continue (sig)
     int sig;
{
  set_signal_handler (SIGCONT, old_cont);
#if 0
  set_signal_handler (SIGSTOP, old_stop);
#endif
  SIGRETURN (0);
}

/* Suspending the shell.  If -f is the arg, then do the suspend
   no matter what.  Otherwise, complain if a login shell. */
int
suspend_builtin (list)
     WORD_LIST *list;
{
  int opt, force;

  reset_internal_getopt ();
  force = 0;
  while ((opt = internal_getopt (list, "f")) != -1)
    switch (opt)
      {
      case 'f':
    force++;
    break;
      default:
    builtin_usage ();
    return (EX_USAGE);
      }
      
  list = loptend;

  if (job_control == 0)
    {
      sh_nojobs ("cannot suspend");
      return (EXECUTION_FAILURE);
    }

  if (force == 0)  
    {
      no_args (list);

      if (login_shell)
    {
      builtin_error ("cannot suspend a login shell");
      return (EXECUTION_FAILURE);
    }
    }

  old_cont = (SigHandler *)set_signal_handler (SIGCONT, suspend_continue);
#if 0
  old_stop = (SigHandler *)set_signal_handler (SIGSTOP, SIG_DFL);
#endif
  killpg (shell_pgrp, SIGSTOP);
  return (EXECUTION_SUCCESS);
}

#endif /* JOB_CONTROL */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/test.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/test.def used in: Step 1057, Step 1115
This file is test.def, from which is created test.c.
It implements the builtin "test" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES test.c

$BUILTIN test
$FUNCTION test_builtin
$SHORT_DOC test [expr]
Exits with a status of 0 (true) or 1 (false) depending on
the evaluation of EXPR.  Expressions may be unary or binary.  Unary
expressions are often used to examine the status of a file.  There
are string operators as well, and numeric comparison operators.

File operators:

    -a FILE        True if file exists.
    -b FILE        True if file is block special.
    -c FILE        True if file is character special.
    -d FILE        True if file is a directory.
    -e FILE        True if file exists.
    -f FILE        True if file exists and is a regular file.
    -g FILE        True if file is set-group-id.
    -h FILE        True if file is a symbolic link.
    -L FILE        True if file is a symbolic link.
    -k FILE        True if file has its `sticky' bit set.
    -p FILE        True if file is a named pipe.
    -r FILE        True if file is readable by you.
    -s FILE        True if file exists and is not empty.
    -S FILE        True if file is a socket.
    -t FD          True if FD is opened on a terminal.
    -u FILE        True if the file is set-user-id.
    -w FILE        True if the file is writable by you.
    -x FILE        True if the file is executable by you.
    -O FILE        True if the file is effectively owned by you.
    -G FILE        True if the file is effectively owned by your group.
    -N FILE        True if the file has been modified since it was last read.

  FILE1 -nt FILE2  True if file1 is newer than file2 (according to
                   modification date).

  FILE1 -ot FILE2  True if file1 is older than file2.

  FILE1 -ef FILE2  True if file1 is a hard link to file2.

String operators:

    -z STRING      True if string is empty.

    -n STRING
    STRING         True if string is not empty.

    STRING1 = STRING2
                   True if the strings are equal.
    STRING1 != STRING2
                   True if the strings are not equal.
    STRING1 < STRING2
                   True if STRING1 sorts before STRING2 lexicographically.
    STRING1 > STRING2
                   True if STRING1 sorts after STRING2 lexicographically.

Other operators:

    -o OPTION      True if the shell option OPTION is enabled.
    ! EXPR         True if expr is false.
    EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.
    EXPR1 -o EXPR2 True if either expr1 OR expr2 is true.

    arg1 OP arg2   Arithmetic tests.  OP is one of -eq, -ne,
                   -lt, -le, -gt, or -ge.

Arithmetic binary operators return true if ARG1 is equal, not-equal,
less-than, less-than-or-equal, greater-than, or greater-than-or-equal
than ARG2.
$END

$BUILTIN [
$DOCNAME test_bracket
$FUNCTION test_builtin
$SHORT_DOC [ arg... ]
This is a synonym for the "test" builtin, but the last
argument must be a literal `]', to match the opening `['.
$END

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../bashansi.h"

#include "../shell.h"
#include "../test.h"
#include "common.h"

extern char *this_command_name;

/* TEST/[ builtin. */
int
test_builtin (list)
     WORD_LIST *list;
{
  char **argv;
  int argc, result;

  /* We let Matthew Bradburn and Kevin Braunsdorf's code do the
     actual test command.  So turn the list of args into an array
     of strings, since that is what their code wants. */
  if (list == 0)
    {
      if (this_command_name[0] == '[' && !this_command_name[1])
    {
      builtin_error ("missing `]'");
      return (EX_BADUSAGE);
    }

      return (EXECUTION_FAILURE);
    }

  argv = make_builtin_argv  (list, &argc);
  result = test_command (argc, argv);
  free ((char *)argv);

  return (result);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/times.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/times.def used in: Step 1057, Step 1117
This file is times.def, from which is created times.c.
It implements the builtin "times" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES times.c

$BUILTIN times
$FUNCTION times_builtin
$SHORT_DOC times
Print the accumulated user and system times for processes run from
the shell.
$END

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>
#include "../bashtypes.h"
#include "../shell.h"

#include <posixtime.h>

#if defined (HAVE_SYS_TIMES_H)
#  include <sys/times.h>
#endif /* HAVE_SYS_TIMES_H */

#if defined (HAVE_SYS_RESOURCE_H) && !defined (RLIMTYPE)
#  include <sys/resource.h>
#endif

#include "common.h"

/* Print the totals for system and user time used. */
int
times_builtin (list)
     WORD_LIST *list;
{
#if defined (HAVE_GETRUSAGE) && defined (HAVE_TIMEVAL) && defined (RUSAGE_SELF)
  struct rusage self, kids;

  USE_VAR(list);

  if (no_options (list))
    return (EX_USAGE);

  getrusage (RUSAGE_SELF, &self);
  getrusage (RUSAGE_CHILDREN, &kids);   /* terminated child processes */

  print_timeval (stdout, &self.ru_utime);
  putchar (' ');
  print_timeval (stdout, &self.ru_stime);
  putchar ('\n');
  print_timeval (stdout, &kids.ru_utime);
  putchar (' ');
  print_timeval (stdout, &kids.ru_stime);
  putchar ('\n');

#else
#  if defined (HAVE_TIMES)
  /* This uses the POSIX.1/XPG5 times(2) interface, which fills in a 
     `struct tms' with values of type clock_t. */
  struct tms t;

  USE_VAR(list);

  if (no_options (list))
    return (EX_USAGE);

  times (&t);

  print_clock_t (stdout, t.tms_utime);
  putchar (' ');
  print_clock_t (stdout, t.tms_stime);
  putchar ('\n');
  print_clock_t (stdout, t.tms_cutime);
  putchar (' ');
  print_clock_t (stdout, t.tms_cstime);
  putchar ('\n');

#  else /* !HAVE_TIMES */

  USE_VAR(list);

  if (no_options (list))
    return (EX_USAGE);
  printf ("0.00 0.00\n0.00 0.00\n");

#  endif /* HAVE_TIMES */
#endif /* !HAVE_TIMES */

  return (EXECUTION_SUCCESS);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/trap.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/trap.def used in: Step 1057, Step 1119
This file is trap.def, from which is created trap.c.
It implements the builtin "trap" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES trap.c

$BUILTIN trap
$FUNCTION trap_builtin
$SHORT_DOC trap [arg] [signal_spec ...] or trap -l
The command ARG is to be read and executed when the shell receives
signal(s) SIGNAL_SPEC.  If ARG is absent all specified signals are
reset to their original values.  If ARG is the null string each
SIGNAL_SPEC is ignored by the shell and by the commands it invokes.
If a SIGNAL_SPEC is EXIT (0) the command ARG is executed on exit from
the shell.  If a SIGNAL_SPEC is DEBUG, ARG is executed after every
command.  If ARG is `-p' then the trap commands associated with
each SIGNAL_SPEC are displayed.  If no arguments are supplied or if
only `-p' is given, trap prints the list of commands associated with
each signal number.  Each SIGNAL_SPEC is either a signal name in <signal.h>
or a signal number.  `trap -l' prints a list of signal names and their
corresponding numbers.  Note that a signal can be sent to the shell
with "kill -signal $$".
$END

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../bashtypes.h"
#include <signal.h>
#include <stdio.h>
#include "../bashansi.h"

#include "../shell.h"
#include "../trap.h"
#include "common.h"
#include "bashgetopt.h"

static void showtrap __P((int));
static int display_traps __P((WORD_LIST *));

/* The trap command:

   trap <arg> <signal ...>
   trap <signal ...>
   trap -l
   trap -p [sigspec ...]
   trap [--]

   Set things up so that ARG is executed when SIGNAL(s) N is recieved.
   If ARG is the empty string, then ignore the SIGNAL(s).  If there is
   no ARG, then set the trap for SIGNAL(s) to its original value.  Just
   plain "trap" means to print out the list of commands associated with
   each signal number.  Single arg of "-l" means list the signal names. */

/* Possible operations to perform on the list of signals.*/
#define SET 0           /* Set this signal to first_arg. */
#define REVERT 1        /* Revert to this signals original value. */
#define IGNORE 2        /* Ignore this signal. */

extern int posixly_correct;

int
trap_builtin (list)
     WORD_LIST *list;
{
  int list_signal_names, display, result, opt;

  list_signal_names = display = 0;
  result = EXECUTION_SUCCESS;
  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "lp")) != -1)
    {
      switch (opt)
    {
    case 'l':
      list_signal_names++;
      break;
    case 'p':
      display++;
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }
  list = loptend;

  if (list_signal_names)
    return (display_signal_list ((WORD_LIST *)NULL, 1));
  else if (display || list == 0)
    return (display_traps (list));
  else
    {
      char *first_arg;
      int operation, sig;

      operation = SET;
      first_arg = list->word->word;
      if (first_arg && *first_arg && (*first_arg != '-' || first_arg[1]) &&
        signal_object_p (first_arg))
    operation = REVERT;
      else
    {
      list = list->next;
      if (*first_arg == '\0')
        operation = IGNORE;
      else if (first_arg[0] == '-' && !first_arg[1])
        operation = REVERT;
    }

      while (list)
    {
      sig = decode_signal (list->word->word);

      if (sig == NO_SIG)
        {
          sh_invalidsig (list->word->word);
          result = EXECUTION_FAILURE;
        }
      else
        {
          switch (operation)
        {
          case SET:
            set_signal (sig, first_arg);
            break;

          case REVERT:
            restore_default_signal (sig);

            /* Signals that the shell treats specially need special
               handling. */
            switch (sig)
              {
              case SIGINT:
            if (interactive)
              set_signal_handler (SIGINT, sigint_sighandler);
            else
              set_signal_handler (SIGINT, termination_unwind_protect);
            break;

              case SIGQUIT:
            /* Always ignore SIGQUIT. */
            set_signal_handler (SIGQUIT, SIG_IGN);
            break;
              case SIGTERM:
#if defined (JOB_CONTROL)
              case SIGTTIN:
              case SIGTTOU:
              case SIGTSTP:
#endif /* JOB_CONTROL */
            if (interactive)
              set_signal_handler (sig, SIG_IGN);
            break;
              }
            break;

          case IGNORE:
            ignore_signal (sig);
            break;
        }
        }
      list = list->next;
    }
    }

  return (result);
}

static void
showtrap (i)
     int i;
{
  char *t, *p, *sn;

  p = trap_list[i];

  if (p == (char *)DEFAULT_SIG)
    return;

  t = (p == (char *)IGNORE_SIG) ? (char *)NULL : sh_single_quote (p);
  sn = signal_name (i);
  /* Make sure that signals whose names are unknown (for whatever reason)
     are printed as signal numbers. */
  if (STREQN (sn, "SIGJUNK", 7) || STREQN (sn, "unknown", 7))
    printf ("trap -- %s %d\n", t ? t : "''", i);
  else if (posixly_correct)
    {
      if (STREQN (sn, "SIG", 3))
    printf ("trap -- %s %s\n", t ? t : "''", sn+3);
      else
    printf ("trap -- %s %s\n", t ? t : "''", sn);
    }
  else
    printf ("trap -- %s %s\n", t ? t : "''", sn);

  FREE (t);
}

static int
display_traps (list)
     WORD_LIST *list;
{
  int result, i;

  if (list == 0)
    {
      for (i = 0; i < BASH_NSIG; i++)
    showtrap (i);
      return (EXECUTION_SUCCESS);
    }

  for (result = EXECUTION_SUCCESS; list; list = list->next)
    {
      i = decode_signal (list->word->word);
      if (i == NO_SIG)
    {
      sh_invalidsig (list->word->word);
      result = EXECUTION_FAILURE;
    }
      else
    showtrap (i);
    }

  return (result);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/type.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/type.def used in: Step 1057, Step 1121
This file is type.def, from which is created type.c.
It implements the builtin "type" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES type.c

$BUILTIN type
$FUNCTION type_builtin
$SHORT_DOC type [-afptP] name [name ...]
For each NAME, indicate how it would be interpreted if used as a
command name.

If the -t option is used, `type' outputs a single word which is one of
`alias', `keyword', `function', `builtin', `file' or `', if NAME is an
alias, shell reserved word, shell function, shell builtin, disk file,
or unfound, respectively.

If the -p flag is used, `type' either returns the name of the disk
file that would be executed, or nothing if `type -t NAME' would not
return `file'.

If the -a flag is used, `type' displays all of the places that contain
an executable named `file'.  This includes aliases, builtins, and
functions, if and only if the -p flag is not also used.

The -f flag suppresses shell function lookup.

The -P flag forces a PATH search for each NAME, even if it is an alias,
builtin, or function, and returns the name of the disk file that would
be executed.
$END

#include <config.h>

#include "../bashtypes.h"
#include "posixstat.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <stdio.h>
#include "../bashansi.h"

#include "../shell.h"
#include "../findcmd.h"
#include "../hashcmd.h"

#if defined (ALIAS)
#include "../alias.h"
#endif /* ALIAS */

#include "common.h"
#include "bashgetopt.h"

extern int find_reserved_word __P((char *));

extern char *this_command_name;

/* For each word in LIST, find out what the shell is going to do with
   it as a simple command. i.e., which file would this shell use to
   execve, or if it is a builtin command, or an alias.  Possible flag
   arguments:
    -t      Returns the "type" of the object, one of
            `alias', `keyword', `function', `builtin',
            or `file'.

    -p      Returns the pathname of the file if -type is
            a file.

    -a      Returns all occurrences of words, whether they
            be a filename in the path, alias, function,
            or builtin.

    -f      Suppress shell function lookup, like `command'.

    -P      Force a path search even in the presence of other
            definitions.

   Order of evaluation:
    alias
    keyword
    function
    builtin
    file
 */

int
type_builtin (list)
     WORD_LIST *list;
{
  int dflags, successful_finds, opt;
  WORD_LIST *this;

  if (list == 0)
    return (EXECUTION_SUCCESS);

  dflags = CDESC_SHORTDESC; /* default */
  successful_finds = 0;

  /* Handle the obsolescent `-type', `-path', and `-all' by prescanning
     the arguments and converting those options to the form that
     internal_getopt recognizes. Converts `--type', `--path', and `--all'
     also. THIS SHOULD REALLY GO AWAY. */
  for (this = list; this && this->word->word[0] == '-'; this = this->next)
    {
      char *flag = &(this->word->word[1]);

      if (STREQ (flag, "type") || STREQ (flag, "-type"))
    {
      this->word->word[1] = 't';
      this->word->word[2] = '\0';
    }
      else if (STREQ (flag, "path") || STREQ (flag, "-path"))
    {
      this->word->word[1] = 'p';
      this->word->word[2] = '\0';
    }
      else if (STREQ (flag, "all") || STREQ (flag, "-all"))
    {
      this->word->word[1] = 'a';
      this->word->word[2] = '\0';
    }
    }

  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "afptP")) != -1)
    {
      switch (opt)
    {
    case 'a':
      dflags |= CDESC_ALL;
      break;
    case 'f':
      dflags |= CDESC_NOFUNCS;
      break;
    case 'p':
      dflags |= CDESC_PATH_ONLY;
      dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC);
      break;
    case 't':
      dflags |= CDESC_TYPE;
      dflags &= ~(CDESC_PATH_ONLY|CDESC_SHORTDESC);
      break;
    case 'P':   /* shorthand for type -ap */
      dflags |= (CDESC_PATH_ONLY|CDESC_FORCE_PATH);
      dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC);
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }
  list = loptend;

  while (list)
    {
      int found;

      found = describe_command (list->word->word, dflags);

      if (!found && (dflags & (CDESC_PATH_ONLY|CDESC_TYPE)) == 0)
    sh_notfound (list->word->word);

      successful_finds += found;
      list = list->next;
    }

  fflush (stdout);

  return ((successful_finds != 0) ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
}

/*
 * Describe COMMAND as required by the type and command builtins.
 *
 * Behavior is controlled by DFLAGS.  Flag values are
 *  CDESC_ALL   print all descriptions of a command
 *  CDESC_SHORTDESC print the description for type and command -V
 *  CDESC_REUSABLE  print in a format that may be reused as input
 *  CDESC_TYPE  print the type for type -t
 *  CDESC_PATH_ONLY print the path for type -p
 *  CDESC_FORCE_PATH    force a path search for type -P
 *  CDESC_NOFUNCS   skip function lookup for type -f
 *
 * CDESC_ALL says whether or not to look for all occurrences of COMMAND, or
 * return after finding it once.
 */
int
describe_command (command, dflags)
     char *command;
     int dflags;
{
  int found, i, found_file, f, all;
  char *full_path, *x;
  SHELL_VAR *func;
#if defined (ALIAS)
  alias_t *alias;
#endif

  all = (dflags & CDESC_ALL) != 0;
  found = found_file = 0;
  full_path = (char *)NULL;

#if defined (ALIAS)
  /* Command is an alias? */
  if (((dflags & CDESC_FORCE_PATH) == 0) && (alias = find_alias (command)))
    {
      if (dflags & CDESC_TYPE)
    puts ("alias");
      else if (dflags & CDESC_SHORTDESC)
    printf ("%s is aliased to `%s'\n", command, alias->value);
      else if (dflags & CDESC_REUSABLE)
    {
      x = sh_single_quote (alias->value);
      printf ("alias %s=%s\n", command, x);
      free (x);
    }

      found = 1;

      if (all == 0)
    return (1);
    }
#endif /* ALIAS */

  /* Command is a shell reserved word? */
  if (((dflags & CDESC_FORCE_PATH) == 0) && (i = find_reserved_word (command)) >= 0)
    {
      if (dflags & CDESC_TYPE)
    puts ("keyword");
      else if (dflags & CDESC_SHORTDESC)
    printf ("%s is a shell keyword\n", command);
      else if (dflags & CDESC_REUSABLE)
    printf ("%s\n", command);

      found = 1;

      if (all == 0)
    return (1);
    }

  /* Command is a function? */
  if (((dflags & (CDESC_FORCE_PATH|CDESC_NOFUNCS)) == 0) && (func = find_function (command)))
    {
      if (dflags & CDESC_TYPE)
    puts ("function");
      else if (dflags & CDESC_SHORTDESC)
    {
#define PRETTY_PRINT_FUNC 1
      char *result;

      printf ("%s is a function\n", command);

      /* We're blowing away THE_PRINTED_COMMAND here... */

      result = named_function_string (command,
                      (COMMAND *) function_cell (func),
                      PRETTY_PRINT_FUNC);
      printf ("%s\n", result);
#undef PRETTY_PRINT_FUNC
    }
      else if (dflags & CDESC_REUSABLE)
    printf ("%s\n", command);

      found = 1;

      if (all == 0)
    return (1);
    }

  /* Command is a builtin? */
  if (((dflags & CDESC_FORCE_PATH) == 0) && find_shell_builtin (command))
    {
      if (dflags & CDESC_TYPE)
    puts ("builtin");
      else if (dflags & CDESC_SHORTDESC)
    printf ("%s is a shell builtin\n", command);
      else if (dflags & CDESC_REUSABLE)
    printf ("%s\n", command);

      found = 1;

      if (all == 0)
    return (1);
    }

  /* Command is a disk file? */
  /* If the command name given is already an absolute command, just
     check to see if it is executable. */
  if (absolute_program (command))
    {
      f = file_status (command);
      if (f & FS_EXECABLE)
    {
      if (dflags & CDESC_TYPE)
        puts ("file");
      else if (dflags & CDESC_SHORTDESC)
        printf ("%s is %s\n", command, command);
      else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
        printf ("%s\n", command);

      /* There's no use looking in the hash table or in $PATH,
         because they're not consulted when an absolute program
         name is supplied. */
      return (1);
    }
    }

  /* If the user isn't doing "-a", then we might care about
     whether the file is present in our hash table. */
  if (all == 0 || (dflags & CDESC_FORCE_PATH))
    {
      if (full_path = phash_search (command))
    {
      if (dflags & CDESC_TYPE)
        puts ("file");
      else if (dflags & CDESC_SHORTDESC)
        printf ("%s is hashed (%s)\n", command, full_path);
      else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
        printf ("%s\n", full_path);

      free (full_path);
      return (1);
    }
    }

  /* Now search through $PATH. */
  while (1)
    {
      if (all == 0)
    full_path = find_user_command (command);
      else
    full_path =
      user_command_matches (command, FS_EXEC_ONLY, found_file);
      /* XXX - should that be FS_EXEC_PREFERRED? */

      if (!full_path)
    break;

      /* If we found the command as itself by looking through $PATH, it
     probably doesn't exist.  Check whether or not the command is an
     executable file.  If it's not, don't report a match. */
      if (STREQ (full_path, command))
    {
      f = file_status (full_path);
      if ((f & FS_EXECABLE) == 0)
        {
          free (full_path);
          full_path = (char *)NULL;
          if (all == 0)
        break;
        }
      else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY|CDESC_SHORTDESC))
        full_path = sh_makepath ((char *)NULL, full_path, MP_DOCWD);
    }

      found_file++;
      found = 1;

      if (dflags & CDESC_TYPE)
    puts ("file");
      else if (dflags & CDESC_SHORTDESC)
    printf ("%s is %s\n", command, full_path);
      else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
    printf ("%s\n", full_path);

      free (full_path);
      full_path = (char *)NULL;

      if (all == 0)
    break;
    }

  return (found);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/ulimit.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/ulimit.def used in: Step 1057, Step 1123
This file is ulimit.def, from which is created ulimit.c.
It implements the builtin "ulimit" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES ulimit.c

$BUILTIN ulimit
$FUNCTION ulimit_builtin
$DEPENDS_ON !_MINIX
$SHORT_DOC ulimit [-SHacdflmnpstuv] [limit]
Ulimit provides control over the resources available to processes
started by the shell, on systems that allow such control.  If an
option is given, it is interpreted as follows:

    -S  use the `soft' resource limit
    -H  use the `hard' resource limit
    -a  all current limits are reported
    -c  the maximum size of core files created
    -d  the maximum size of a process's data segment
    -f  the maximum size of files created by the shell
    -l  the maximum size a process may lock into memory
    -m  the maximum resident set size
    -n  the maximum number of open file descriptors
    -p  the pipe buffer size
    -s  the maximum stack size
    -t  the maximum amount of cpu time in seconds
    -u  the maximum number of user processes
    -v  the size of virtual memory 

If LIMIT is given, it is the new value of the specified resource;
the special LIMIT values `soft', `hard', and `unlimited' stand for
the current soft limit, the current hard limit, and no limit, respectively.
Otherwise, the current value of the specified resource is printed.
If no option is given, then -f is assumed.  Values are in 1024-byte
increments, except for -t, which is in seconds, -p, which is in
increments of 512 bytes, and -u, which is an unscaled number of
processes.
$END

#if !defined (_MINIX)

#include <config.h>

#include "../bashtypes.h"
#ifndef _MINIX
#  include <sys/param.h>
#endif

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <stdio.h>
#include <errno.h>

#include "../shell.h"
#include "common.h"
#include "bashgetopt.h"
#include "pipesize.h"

#if !defined (errno)
extern int errno;
#endif

/* For some reason, HPUX chose to make these definitions visible only if
   _KERNEL is defined, so we define _KERNEL before including <sys/resource.h>
   and #undef it afterward. */
#if defined (HAVE_RESOURCE)
#  include <sys/time.h>
#  if defined (HPUX) && defined (RLIMIT_NEEDS_KERNEL)
#    define _KERNEL
#  endif
#  include <sys/resource.h>
#  if defined (HPUX) && defined (RLIMIT_NEEDS_KERNEL)
#    undef _KERNEL
#  endif
#else
#  include <sys/times.h>
#endif

#if defined (HAVE_LIMITS_H)
#  include <limits.h>
#endif

/* Check for the most basic symbols.  If they aren't present, this
   system's <sys/resource.h> isn't very useful to us. */
#if !defined (RLIMIT_FSIZE) || !defined (HAVE_GETRLIMIT)
#  undef HAVE_RESOURCE
#endif

#if !defined (RLIMTYPE)
#  define RLIMTYPE long
#  define string_to_rlimtype(s) strtol(s, (char **)NULL, 10)
#  define print_rlimtype(num, nl) printf ("%ld%s", num, nl ? "\n" : "")
#endif

/* Some systems use RLIMIT_NOFILE, others use RLIMIT_OFILE */
#if defined (HAVE_RESOURCE) && defined (RLIMIT_OFILE) && !defined (RLIMIT_NOFILE)
#  define RLIMIT_NOFILE RLIMIT_OFILE
#endif /* HAVE_RESOURCE && RLIMIT_OFILE && !RLIMIT_NOFILE */

/* Some systems have these, some do not. */
#ifdef RLIMIT_FSIZE
#  define RLIMIT_FILESIZE   RLIMIT_FSIZE
#else
#  define RLIMIT_FILESIZE   256
#endif

#define RLIMIT_PIPESIZE 257

#ifdef RLIMIT_NOFILE
#  define RLIMIT_OPENFILES  RLIMIT_NOFILE
#else
#  define RLIMIT_OPENFILES  258
#endif

#ifdef RLIMIT_VMEM
#  define RLIMIT_VIRTMEM    RLIMIT_VMEM
#  define RLIMIT_VMBLKSZ    1024
#else
#  ifdef RLIMIT_AS
#    define RLIMIT_VIRTMEM  RLIMIT_AS
#    define RLIMIT_VMBLKSZ  1024
#  else
#    define RLIMIT_VIRTMEM  259
#    define RLIMIT_VMBLKSZ  1
#  endif
#endif

#ifdef RLIMIT_NPROC
#  define RLIMIT_MAXUPROC   RLIMIT_NPROC
#else
#  define RLIMIT_MAXUPROC   260
#endif

#if !defined (RLIM_INFINITY)
#  define RLIM_INFINITY 0x7fffffff
#endif

#if !defined (RLIM_SAVED_CUR)
#  define RLIM_SAVED_CUR RLIM_INFINITY
#endif

#if !defined (RLIM_SAVED_MAX)
#  define RLIM_SAVED_MAX RLIM_INFINITY
#endif

#define LIMIT_HARD 0x01
#define LIMIT_SOFT 0x02

static int _findlim __P((int));

static int ulimit_internal __P((int, char *, int, int));

static int get_limit __P((int, RLIMTYPE *, RLIMTYPE *));
static int set_limit __P((int, RLIMTYPE, int));

static void printone __P((int, RLIMTYPE, int));
static void print_all_limits __P((int));

static int set_all_limits __P((int, RLIMTYPE));

static int filesize __P((RLIMTYPE *));
static int pipesize __P((RLIMTYPE *));
static int getmaxuprc __P((RLIMTYPE *));
static int getmaxvm __P((RLIMTYPE *, RLIMTYPE *));

typedef struct {
  int  option;          /* The ulimit option for this limit. */
  int  parameter;       /* Parameter to pass to get_limit (). */
  int  block_factor;        /* Blocking factor for specific limit. */
  char *description;        /* Descriptive string to output. */
  char *units;          /* scale */
} RESOURCE_LIMITS;

static RESOURCE_LIMITS limits[] = {
#ifdef RLIMIT_CORE
  { 'c',    RLIMIT_CORE,  1024, "core file size",   "blocks" },
#endif
#ifdef RLIMIT_DATA
  { 'd',    RLIMIT_DATA,  1024, "data seg size",    "kbytes" },
#endif
  { 'f',    RLIMIT_FILESIZE, 1024,  "file size",        "blocks" },
#ifdef RLIMIT_MEMLOCK
  { 'l',    RLIMIT_MEMLOCK, 1024,   "max locked memory",    "kbytes" },
#endif
#ifdef RLIMIT_RSS
  { 'm',    RLIMIT_RSS,   1024, "max memory size",  "kbytes" },
#endif /* RLIMIT_RSS */
  { 'n',    RLIMIT_OPENFILES, 1,    "open files",       (char *)NULL},
  { 'p',    RLIMIT_PIPESIZE, 512,   "pipe size",        "512 bytes" },
#ifdef RLIMIT_STACK
  { 's',    RLIMIT_STACK, 1024, "stack size",       "kbytes" },
#endif
#ifdef RLIMIT_CPU
  { 't',    RLIMIT_CPU,      1, "cpu time",     "seconds" },
#endif /* RLIMIT_CPU */
  { 'u',    RLIMIT_MAXUPROC, 1, "max user processes",   (char *)NULL },
#if defined (HAVE_RESOURCE)
  { 'v',    RLIMIT_VIRTMEM, RLIMIT_VMBLKSZ, "virtual memory", "kbytes" },
#endif
#ifdef RLIMIT_SWAP
  { 'w',    RLIMIT_SWAP,    1024,   "swap size",        "kbytes" },
#endif
  { -1, -1, -1, (char *)NULL, (char *)NULL }
};
#define NCMDS   (sizeof(limits) / sizeof(limits[0]))

typedef struct _cmd {
  int cmd;
  char *arg;
} ULCMD;

static ULCMD *cmdlist;
static int ncmd;
static int cmdlistsz;

#if !defined (HAVE_RESOURCE) && !defined (HAVE_ULIMIT)
long
ulimit (cmd, newlim)
     int cmd;
     long newlim;
{
  errno = EINVAL;
  return -1;
}
#endif /* !HAVE_RESOURCE && !HAVE_ULIMIT */

static int
_findlim (opt)
     int opt;
{
  register int i;

  for (i = 0; limits[i].option > 0; i++)
    if (limits[i].option == opt)
      return i;
  return -1;
}

static char optstring[4 + 2 * NCMDS];

/* Report or set limits associated with certain per-process resources.
   See the help documentation in builtins.c for a full description. */
int
ulimit_builtin (list)
     register WORD_LIST *list;
{
  register char *s;
  int c, limind, mode, opt, all_limits;

  mode = 0;

  all_limits = 0;

  /* Idea stolen from pdksh -- build option string the first time called. */
  if (optstring[0] == 0)
    {
      s = optstring;
      *s++ = 'a'; *s++ = 'S'; *s++ = 'H';
      for (c = 0; limits[c].option > 0; c++)
    {
      *s++ = limits[c].option;
      *s++ = ';';
    }
      *s = '\0';
    }

  /* Initialize the command list. */
  if (cmdlistsz == 0)
    cmdlist = (ULCMD *)xmalloc ((cmdlistsz = 16) * sizeof (ULCMD));
  ncmd = 0;

  reset_internal_getopt ();
  while ((opt = internal_getopt (list, optstring)) != -1)
    {
      switch (opt)
    {
    case 'a':
      all_limits++;
      break;

    /* -S and -H are modifiers, not real options.  */
    case 'S':
      mode |= LIMIT_SOFT;
      break;

    case 'H':
      mode |= LIMIT_HARD;
      break;

    case '?':
      builtin_usage ();
      return (EX_USAGE);

    default:
      if (ncmd >= cmdlistsz)
        cmdlist = (ULCMD *)xrealloc (cmdlist, (cmdlistsz *= 2) * sizeof (ULCMD));
      cmdlist[ncmd].cmd = opt;
      cmdlist[ncmd++].arg = list_optarg;
      break;
    }
    }
  list = loptend;

  if (all_limits)
    {
#ifdef NOTYET
      if (list)     /* setting */
        {
          if (STREQ (list->word->word, "unlimited") == 0)
            {
              builtin_error ("%s: invalid limit argument", list->word->word);
              return (EXECUTION_FAILURE);
            }
          return (set_all_limits (mode == 0 ? LIMIT_SOFT|LIMIT_HARD : mode, RLIM_INFINITY));
        }
#endif
      print_all_limits (mode == 0 ? LIMIT_SOFT : mode);
      return (EXECUTION_SUCCESS);
    }

  /* default is `ulimit -f' */
  if (ncmd == 0)
    {
      cmdlist[ncmd].cmd = 'f';
      /* `ulimit something' is same as `ulimit -f something' */
      cmdlist[ncmd++].arg = list ? list->word->word : (char *)NULL;
      if (list)
    list = list->next;
    }

  /* verify each command in the list. */
  for (c = 0; c < ncmd; c++)
    {
      limind = _findlim (cmdlist[c].cmd);
      if (limind == -1)
    {
      builtin_error ("`%c': bad command", cmdlist[c].cmd);
      return (EX_USAGE);
    }
    }

  for (c = 0; c < ncmd; c++)
    if (ulimit_internal (cmdlist[c].cmd, cmdlist[c].arg, mode, ncmd > 1) == EXECUTION_FAILURE)
      return (EXECUTION_FAILURE);

  return (EXECUTION_SUCCESS);
}

static int
ulimit_internal (cmd, cmdarg, mode, multiple)
     int cmd;
     char *cmdarg;
     int mode, multiple;
{
  int opt, limind, setting;
  int block_factor;
  RLIMTYPE soft_limit, hard_limit, real_limit, limit;

  setting = cmdarg != 0;
  limind = _findlim (cmd);
  if (mode == 0)
    mode = setting ? (LIMIT_HARD|LIMIT_SOFT) : LIMIT_SOFT;
  opt = get_limit (limind, &soft_limit, &hard_limit);
  if (opt < 0)
    {
      builtin_error ("%s: cannot get limit: %s", limits[limind].description,
                         strerror (errno));
      return (EXECUTION_FAILURE);
    }

  if (setting == 0) /* print the value of the specified limit */
    {
      printone (limind, (mode & LIMIT_SOFT) ? soft_limit : hard_limit, multiple);
      return (EXECUTION_SUCCESS);
    }
 
  /* Setting the limit. */
  if (STREQ (cmdarg, "hard"))
    real_limit = hard_limit;
  else if (STREQ (cmdarg, "soft"))
    real_limit = soft_limit;
  else if (STREQ (cmdarg, "unlimited"))
    real_limit = RLIM_INFINITY;
  else if (all_digits (cmdarg))
    {
      limit = string_to_rlimtype (cmdarg);
      block_factor = limits[limind].block_factor;
      real_limit = limit * block_factor;

      if ((real_limit / block_factor) != limit)
    {
      sh_erange (cmdarg, "limit");
      return (EXECUTION_FAILURE);
    }
    }
  else
    {
      sh_invalidnum (cmdarg);
      return (EXECUTION_FAILURE);
    }

  if (set_limit (limind, real_limit, mode) < 0)
    {
      builtin_error ("%s: cannot modify limit: %s", limits[limind].description,
                            strerror (errno));
      return (EXECUTION_FAILURE);
    }

  return (EXECUTION_SUCCESS);
}

static int
get_limit (ind, softlim, hardlim)
     int ind;
     RLIMTYPE *softlim, *hardlim;
{
  RLIMTYPE value;
#if defined (HAVE_RESOURCE)
  struct rlimit limit;
#endif

  if (limits[ind].parameter >= 256)
    {
      switch (limits[ind].parameter)
    {
    case RLIMIT_FILESIZE:
      if (filesize (&value) < 0)
        return -1;
      break;
    case RLIMIT_PIPESIZE:
      if (pipesize (&value) < 0)
        return -1;
      break;
    case RLIMIT_OPENFILES:
      value = (RLIMTYPE)getdtablesize ();
      break;
    case RLIMIT_VIRTMEM:
      return (getmaxvm (softlim, hardlim));
    case RLIMIT_MAXUPROC:
      if (getmaxuprc (&value) < 0)
        return -1;
      break;
    default:
      errno = EINVAL;
      return -1;
    }
      *softlim = *hardlim = value;
      return (0);
    }
  else
    {
#if defined (HAVE_RESOURCE)
      if (getrlimit (limits[ind].parameter, &limit) < 0)
    return -1;
      *softlim = limit.rlim_cur;
      *hardlim = limit.rlim_max;
#  if defined (HPUX9)
      if (limits[ind].parameter == RLIMIT_FILESIZE)
    {
      *softlim *= 512;
      *hardlim *= 512;          /* Ugh. */
    }
      else
#  endif /* HPUX9 */
      return 0;
#else
      errno = EINVAL;
      return -1;
#endif
    }
}

static int
set_limit (ind, newlim, mode)
     int ind;
     RLIMTYPE newlim;
     int mode;
{
#if defined (HAVE_RESOURCE)
   struct rlimit limit;
   RLIMTYPE val;
#endif

  if (limits[ind].parameter >= 256)
    switch (limits[ind].parameter)
      {
      case RLIMIT_FILESIZE:
#if !defined (HAVE_RESOURCE)
    return (ulimit (2, newlim / 512L));
#else
    errno = EINVAL;
    return -1;
#endif

      case RLIMIT_OPENFILES:
#if defined (HAVE_SETDTABLESIZE)
#  if defined (__CYGWIN__)
    /* Grrr... Cygwin declares setdtablesize as void. */
    setdtablesize (newlim);
    return 0;
#  else
    return (setdtablesize (newlim));
#  endif
#endif
      case RLIMIT_PIPESIZE:
      case RLIMIT_VIRTMEM:
      case RLIMIT_MAXUPROC:
      default:
    errno = EINVAL;
    return -1;
      }
  else
    {
#if defined (HAVE_RESOURCE)
      if (getrlimit (limits[ind].parameter, &limit) < 0)
    return -1;
#  if defined (HPUX9)
      if (limits[ind].parameter == RLIMIT_FILESIZE)
    newlim /= 512;              /* Ugh. */
#  endif /* HPUX9 */
      val = (current_user.euid != 0 && newlim == RLIM_INFINITY &&
           (mode & LIMIT_HARD) == 0 &&      /* XXX -- test */
           (limit.rlim_cur <= limit.rlim_max))
         ? limit.rlim_max : newlim;
      if (mode & LIMIT_SOFT)
    limit.rlim_cur = val;
      if (mode & LIMIT_HARD)
    limit.rlim_max = val;
      
      return (setrlimit (limits[ind].parameter, &limit));
#else
      errno = EINVAL;
      return -1;
#endif
    }
}

static int
getmaxvm (softlim, hardlim)
     RLIMTYPE *softlim, *hardlim;
{
#if defined (HAVE_RESOURCE)
  struct rlimit datalim, stacklim;

  if (getrlimit (RLIMIT_DATA, &datalim) < 0)
    return -1;

  if (getrlimit (RLIMIT_STACK, &stacklim) < 0)
    return -1;

  /* Protect against overflow. */
  *softlim = (datalim.rlim_cur / 1024L) + (stacklim.rlim_cur / 1024L);
  *hardlim = (datalim.rlim_max / 1024L) + (stacklim.rlim_max / 1024L);
  return 0;
#else
  errno = EINVAL;
  return -1;
#endif /* HAVE_RESOURCE */
}

static int
filesize(valuep)
     RLIMTYPE *valuep;
{
#if !defined (HAVE_RESOURCE)
  long result;
  if ((result = ulimit (1, 0L)) < 0)
    return -1;
  else
    *valuep = (RLIMTYPE) result * 512;
  return 0;
#else
  errno = EINVAL;
  return -1;
#endif
}

static int
pipesize (valuep)
     RLIMTYPE *valuep;
{
#if defined (PIPE_BUF)
  /* This is defined on Posix systems. */
  *valuep = (RLIMTYPE) PIPE_BUF;
  return 0;
#else
#  if defined (PIPESIZE)
  /* This is defined by running a program from the Makefile. */
  *valuep = (RLIMTYPE) PIPESIZE;
  return 0;
#  else
  errno = EINVAL;
  return -1;  
#  endif /* PIPESIZE */
#endif /* PIPE_BUF */
}

static int
getmaxuprc (valuep)
     RLIMTYPE *valuep;
{
  long maxchild;

  maxchild = getmaxchild ();
  if (maxchild < 0)
    {
      errno = EINVAL;
      return -1;
    }
  else
    {
      *valuep = (RLIMTYPE) maxchild;
      return 0;
    }
}

static void
print_all_limits (mode)
     int mode;
{
  register int i;
  RLIMTYPE softlim, hardlim;

  if (mode == 0)
    mode |= LIMIT_SOFT;

  for (i = 0; limits[i].option > 0; i++)
    {
      if (get_limit (i, &softlim, &hardlim) < 0)
    builtin_error ("%s: cannot get limit: %s", limits[i].description,
                           strerror (errno));
      else
    printone (i, (mode & LIMIT_SOFT) ? softlim : hardlim, 1);
    }
}

static void
printone (limind, curlim, pdesc)
     int limind;
     RLIMTYPE curlim;
     int pdesc;
{
  char unitstr[64];

  if (pdesc)
    {
      if (limits[limind].units)
    sprintf (unitstr, "(%s, -%c) ", limits[limind].units, limits[limind].option);
      else
        sprintf (unitstr, "(-%c) ", limits[limind].option);

      printf ("%-18s %16s", limits[limind].description, unitstr);
    }
  if (curlim == RLIM_INFINITY)
    puts ("unlimited");
  else if (curlim == RLIM_SAVED_MAX)
    puts ("hard");
  else if (curlim == RLIM_SAVED_CUR)
    puts ("soft");
  else
    print_rlimtype ((curlim / limits[limind].block_factor), 1);
}

/* Set all limits to NEWLIM.  NEWLIM currently must be RLIM_INFINITY, which
   causes all limits to be set as high as possible depending on mode (like
   csh `unlimit').  Returns -1 if NEWLIM is invalid, 0 if all limits
   were set successfully, and 1 if at least one limit could not be set.

   To raise all soft limits to their corresponding hard limits, use
    ulimit -S -a unlimited
   To attempt to raise all hard limits to infinity (superuser-only), use
    ulimit -H -a unlimited
   To attempt to raise all soft and hard limits to infinity, use
    ulimit -a unlimited
*/

static int
set_all_limits (mode, newlim)
     int mode;
     RLIMTYPE newlim;
{
  register int i;
  int retval = 0;

  if (newlim != RLIM_INFINITY)
    {
      errno = EINVAL;
      return -1;
    }
  
  if (mode == 0)
    mode = LIMIT_SOFT|LIMIT_HARD;

  for (retval = i = 0; limits[i].option > 0; i++)
    if (set_limit (i, newlim, mode) < 0)
      {
    builtin_error ("%s: cannot modify limit: %s", limits[i].description,
                              strerror (errno));
    retval = 1;
      }
  return retval;
}

#endif /* !_MINIX */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/umask.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/umask.def used in: Step 1057, Step 1125
This file is umask.def, from which is created umask.c.
It implements the builtin "umask" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES umask.c

$BUILTIN umask
$FUNCTION umask_builtin
$SHORT_DOC umask [-p] [-S] [mode]
The user file-creation mask is set to MODE.  If MODE is omitted, or if
`-S' is supplied, the current value of the mask is printed.  The `-S'
option makes the output symbolic; otherwise an octal number is output.
If `-p' is supplied, and MODE is omitted, the output is in a form
that may be used as input.  If MODE begins with a digit, it is
interpreted as an octal number, otherwise it is a symbolic mode string
like that accepted by chmod(1).
$END

#include <config.h>

#include "../bashtypes.h"
#include "filecntl.h"
#ifndef _MINIX
#  include <sys/file.h>
#endif

#if defined (HAVE_UNISTD_H)
#include <unistd.h>
#endif

#include <stdio.h>
#include <chartypes.h>

#include "../shell.h"
#include "posixstat.h"
#include "common.h"
#include "bashgetopt.h"

#ifdef __LCC__
#define mode_t int
#endif

/* **************************************************************** */
/*                                                                  */
/*                     UMASK Builtin and Helpers                    */
/*                                                                  */
/* **************************************************************** */

static void print_symbolic_umask __P((mode_t));
static int symbolic_umask __P((WORD_LIST *));

/* Set or display the mask used by the system when creating files.  Flag
   of -S means display the umask in a symbolic mode. */
int
umask_builtin (list)
     WORD_LIST *list;
{
  int print_symbolically, opt, umask_value, pflag;
  mode_t umask_arg;

  print_symbolically = pflag = 0;
  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "Sp")) != -1)
    {
      switch (opt)
    {
    case 'S':
      print_symbolically++;
      break;
    case 'p':
      pflag++;
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }

  list = loptend;

  if (list)
    {
      if (DIGIT (*list->word->word))
    {
      umask_value = read_octal (list->word->word);

      /* Note that other shells just let you set the umask to zero
         by specifying a number out of range.  This is a problem
         with those shells.  We don't change the umask if the input
         is lousy. */
      if (umask_value == -1)
        {
          sh_erange (list->word->word, "octal number");
          return (EXECUTION_FAILURE);
        }
    }
      else
    {
      umask_value = symbolic_umask (list);
      if (umask_value == -1)
        return (EXECUTION_FAILURE);
    }
      umask_arg = (mode_t)umask_value;
      umask (umask_arg);
      if (print_symbolically)
    print_symbolic_umask (umask_arg);
    }
  else              /* Display the UMASK for this user. */
    {
      umask_arg = umask (022);
      umask (umask_arg);

      if (pflag)
    printf ("umask%s ", (print_symbolically ? " -S" : ""));
      if (print_symbolically)
    print_symbolic_umask (umask_arg);
      else
    printf ("%04lo\n", (unsigned long)umask_arg);
    }

  fflush (stdout);
  return (EXECUTION_SUCCESS);
}

/* Print the umask in a symbolic form.  In the output, a letter is
   printed if the corresponding bit is clear in the umask. */
static void
print_symbolic_umask (um)
     mode_t um;
{
  char ubits[4], gbits[4], obits[4];        /* u=rwx,g=rwx,o=rwx */
  int i;

  i = 0;
  if ((um & S_IRUSR) == 0)
    ubits[i++] = 'r';
  if ((um & S_IWUSR) == 0)
    ubits[i++] = 'w';
  if ((um & S_IXUSR) == 0)
    ubits[i++] = 'x';
  ubits[i] = '\0';

  i = 0;
  if ((um & S_IRGRP) == 0)
    gbits[i++] = 'r';
  if ((um & S_IWGRP) == 0)
    gbits[i++] = 'w';
  if ((um & S_IXGRP) == 0)
    gbits[i++] = 'x';
  gbits[i] = '\0';

  i = 0;
  if ((um & S_IROTH) == 0)
    obits[i++] = 'r';
  if ((um & S_IWOTH) == 0)
    obits[i++] = 'w';
  if ((um & S_IXOTH) == 0)
    obits[i++] = 'x';
  obits[i] = '\0';

  printf ("u=%s,g=%s,o=%s\n", ubits, gbits, obits);
}

int
parse_symbolic_mode (mode, initial_bits)
     char *mode;
     int initial_bits;
{
  int who, op, perm, bits, c;
  char *s;

  for (s = mode, bits = initial_bits;;)
    {
      who = op = perm = 0;

      /* Parse the `who' portion of the symbolic mode clause. */
      while (member (*s, "agou"))
    {
      switch (c = *s++)
        {
        case 'u':
          who |= S_IRWXU;
          continue;
        case 'g':
          who |= S_IRWXG;
          continue;
        case 'o':
          who |= S_IRWXO;
          continue;
        case 'a':
          who |= S_IRWXU | S_IRWXG | S_IRWXO;
          continue;
        default:
          break;
        }
    }

      /* The operation is now sitting in *s. */
      op = *s++;
      switch (op)
    {
    case '+':
    case '-':
    case '=':
      break;
    default:
      builtin_error ("`%c': invalid symbolic mode operator", op);
      return (-1);
    }

      /* Parse out the `perm' section of the symbolic mode clause. */
      while (member (*s, "rwx"))
    {
      c = *s++;

      switch (c)
        {
        case 'r':
          perm |= S_IRUGO;
          break;
        case 'w':
          perm |= S_IWUGO;
          break;
        case 'x':
          perm |= S_IXUGO;
          break;
        }
    }

      /* Now perform the operation or return an error for a
     bad permission string. */
      if (!*s || *s == ',')
    {
      if (who)
        perm &= who;

      switch (op)
        {
        case '+':
          bits |= perm;
          break;
        case '-':
          bits &= ~perm;
          break;
        case '=':
          bits &= ~who;
          bits |= perm;
          break;

        /* No other values are possible. */
        }

      if (*s == '\0')
        break;
      else
        s++;    /* skip past ',' */
    }
      else
    {
      builtin_error ("`%c': invalid symbolic mode character", *s);
      return (-1);
    }
    }

  return (bits);
}

/* Set the umask from a symbolic mode string similar to that accepted
   by chmod.  If the -S argument is given, then print the umask in a
   symbolic form. */
static int
symbolic_umask (list)
     WORD_LIST *list;
{
  int um, bits;

  /* Get the initial umask.  Don't change it yet. */
  um = umask (022);
  umask (um);

  /* All work is done with the complement of the umask -- it's
     more intuitive and easier to deal with.  It is complemented
     again before being returned. */
  bits = parse_symbolic_mode (list->word->word, ~um);
  if (bits == -1)
    return (-1);

  um = ~bits & 0777;
  return (um);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/wait.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/wait.def used in: Step 1057, Step 1127
This file is wait.def, from which is created wait.c.
It implements the builtin "wait" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$BUILTIN wait
$FUNCTION wait_builtin
$DEPENDS_ON JOB_CONTROL
$PRODUCES wait.c
$SHORT_DOC wait [n]
Wait for the specified process and report its termination status.  If
N is not given, all currently active child processes are waited for,
and the return code is zero.  N may be a process ID or a job
specification; if a job spec is given, all processes in the job's
pipeline are waited for.
$END

$BUILTIN wait
$FUNCTION wait_builtin
$DEPENDS_ON !JOB_CONTROL
$SHORT_DOC wait [n]
Wait for the specified process and report its termination status.  If
N is not given, all currently active child processes are waited for,
and the return code is zero.  N is a process ID; if it is not given,
all child processes of the shell are waited for.
$END

#include <config.h>

#include "../bashtypes.h"
#include <signal.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <chartypes.h>

#include "../bashansi.h"

#include "../shell.h"
#include "../jobs.h"
#include "common.h"
#include "bashgetopt.h"

extern int interrupt_immediately;
extern int wait_signal_received;

procenv_t wait_intr_buf;

/* Wait for the pid in LIST to stop or die.  If no arguments are given, then
   wait for all of the active background processes of the shell and return
   0.  If a list of pids or job specs are given, return the exit status of
   the last one waited for. */

#define WAIT_RETURN(s) \
  do \
    { \
      interrupt_immediately = old_interrupt_immediately;\
      return (s);\
    } \
  while (0)

int
wait_builtin (list)
     WORD_LIST *list;
{
  int status, code;
  volatile int old_interrupt_immediately;

  USE_VAR(list);

  if (no_options (list))
    return (EX_USAGE);
  list = loptend;

  old_interrupt_immediately = interrupt_immediately;
  interrupt_immediately++;

  /* POSIX.2 says:  When the shell is waiting (by means of the wait utility)
     for asynchronous commands to complete, the reception of a signal for
     which a trap has been set shall cause the wait utility to return
     immediately with an exit status greater than 128, after which the trap
     associated with the signal shall be taken.

     We handle SIGINT here; it's the only one that needs to be treated
     specially (I think), since it's handled specially in {no,}jobs.c. */
  code = setjmp (wait_intr_buf);
  if (code)
    {
      status = 128 + wait_signal_received;
      WAIT_RETURN (status);
    }

  /* We support jobs or pids.
     wait <pid-or-job> [pid-or-job ...] */

  /* But wait without any arguments means to wait for all of the shell's
     currently active background processes. */
  if (list == 0)
    {
      wait_for_background_pids ();
      WAIT_RETURN (EXECUTION_SUCCESS);
    }

  status = EXECUTION_SUCCESS;
  while (list)
    {
      pid_t pid;
      char *w;
      intmax_t pid_value;

      w = list->word->word;
      if (DIGIT (*w))
    {
      if (legal_number (w, &pid_value) && pid_value == (pid_t)pid_value)
        {
          pid = (pid_t)pid_value;
          status = wait_for_single_pid (pid);
        }
      else
        {
          sh_badpid (w);
          WAIT_RETURN (EXECUTION_FAILURE);
        }
    }
#if defined (JOB_CONTROL)
      else if (job_control && *w)
    /* Must be a job spec.  Check it out. */
    {
      int job;
      sigset_t set, oset;

      BLOCK_CHILD (set, oset);
      job = get_job_spec (list);

      if (job < 0 || job >= job_slots || !jobs[job])
        {
          if (job != DUP_JOB)
        sh_badjob (list->word->word);
          UNBLOCK_CHILD (oset);
          status = 127; /* As per Posix.2, section 4.70.2 */
          list = list->next;
          continue;
        }

      /* Job spec used.  Wait for the last pid in the pipeline. */
      UNBLOCK_CHILD (oset);
      status = wait_for_job (job);
    }
      else if (job_control == 0 && *w == '%')
    {
      /* can't use jobspecs as arguments if job control is not active. */
      sh_nojobs ((char *)NULL);
      status = EXECUTION_FAILURE;
    }
#endif /* JOB_CONTROL */
      else
    {
      sh_badpid (w);
      status = EXECUTION_FAILURE;
    }
      list = list->next;
    }

  WAIT_RETURN (status);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/getopts.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/getopts.def used in: Step 1057, Step 1129
This file is getopts.def, from which is created getopts.c.
It implements the builtin "getopts" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES getopts.c

$BUILTIN getopts
$FUNCTION getopts_builtin
$SHORT_DOC getopts optstring name [arg]
Getopts is used by shell procedures to parse positional parameters.

OPTSTRING contains the option letters to be recognized; if a letter
is followed by a colon, the option is expected to have an argument,
which should be separated from it by white space.

Each time it is invoked, getopts will place the next option in the
shell variable $name, initializing name if it does not exist, and
the index of the next argument to be processed into the shell
variable OPTIND.  OPTIND is initialized to 1 each time the shell or
a shell script is invoked.  When an option requires an argument,
getopts places that argument into the shell variable OPTARG.

getopts reports errors in one of two ways.  If the first character
of OPTSTRING is a colon, getopts uses silent error reporting.  In
this mode, no error messages are printed.  If an invalid option is
seen, getopts places the option character found into OPTARG.  If a
required argument is not found, getopts places a ':' into NAME and
sets OPTARG to the option character found.  If getopts is not in
silent mode, and an invalid option is seen, getopts places '?' into
NAME and unsets OPTARG.  If a required option is not found, a '?'
is placed in NAME, OPTARG is unset, and a diagnostic message is
printed.

If the shell variable OPTERR has the value 0, getopts disables the
printing of error messages, even if the first character of
OPTSTRING is not a colon.  OPTERR has the value 1 by default.

Getopts normally parses the positional parameters ($0 - $9), but if
more arguments are given, they are parsed instead.
$END

#include <config.h>

#include <stdio.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../bashansi.h"

#include "../shell.h"
#include "common.h"
#include "bashgetopt.h"
#include "getopt.h"

#define G_EOF       -1
#define G_INVALID_OPT   -2
#define G_ARG_MISSING   -3

extern char *this_command_name;

static int getopts_bind_variable __P((char *, char *));
static int dogetopts __P((int, char **));

/* getopts_reset is magic code for when OPTIND is reset.  N is the
   value that has just been assigned to OPTIND. */
void
getopts_reset (newind)
     int newind;
{
  sh_optind = newind;
  sh_badopt = 0;
}

static int
getopts_bind_variable (name, value)
     char *name, *value;
{
  SHELL_VAR *v;

  if (legal_identifier (name))
    {
      v = bind_variable (name, value);
      return (v && (readonly_p (v) == 0)) ? EXECUTION_SUCCESS : EXECUTION_FAILURE;
    }
  else
    {
      sh_invalidid (name);
      return (EXECUTION_FAILURE);
    }
}

/* Error handling is now performed as specified by Posix.2, draft 11
   (identical to that of ksh-88).  The special handling is enabled if
   the first character of the option string is a colon; this handling
   disables diagnostic messages concerning missing option arguments
   and invalid option characters.  The handling is as follows.

   INVALID OPTIONS:
        name -> "?"
        if (special_error) then
                OPTARG = option character found
                no error output
        else
                OPTARG unset
                diagnostic message
        fi
 
  MISSING OPTION ARGUMENT;
        if (special_error) then
                name -> ":"
                OPTARG = option character found
        else
                name -> "?"
                OPTARG unset
                diagnostic message
        fi
 */

static int
dogetopts (argc, argv)
     int argc;
     char **argv;
{
  int ret, special_error, old_opterr, i, n;
  char strval[2], numval[16];
  char *optstr;         /* list of options */
  char *name;           /* variable to get flag val */
  char *t;

  if (argc < 3)
    {
      builtin_usage ();
      return (EX_USAGE);
    }

  /* argv[0] is "getopts". */

  optstr = argv[1];
  name = argv[2];
  argc -= 2;
  argv += 2;

  special_error = optstr[0] == ':';

  if (special_error)
    {
      old_opterr = sh_opterr;
      optstr++;
      sh_opterr = 0;        /* suppress diagnostic messages */
    }

  if (argc > 1)
    {
      sh_getopt_restore_state (argv);
      t = argv[0];
      argv[0] = dollar_vars[0];
      ret = sh_getopt (argc, argv, optstr);
      argv[0] = t;
    }
  else if (rest_of_args == (WORD_LIST *)NULL)
    {
      for (i = 0; i < 10 && dollar_vars[i]; i++)
    ;

      sh_getopt_restore_state (dollar_vars);
      ret = sh_getopt (i, dollar_vars, optstr);
    }
  else
    {
      register WORD_LIST *words;
      char **v;

      for (i = 0; i < 10 && dollar_vars[i]; i++)
    ;
      for (words = rest_of_args; words; words = words->next, i++)
    ;
      v = strvec_create (i + 1);
      for (i = 0; i < 10 && dollar_vars[i]; i++)
    v[i] = dollar_vars[i];
      for (words = rest_of_args; words; words = words->next, i++)
    v[i] = words->word->word;
      v[i] = (char *)NULL;
      sh_getopt_restore_state (v);
      ret = sh_getopt (i, v, optstr);
      free (v);
    }

  if (special_error)
    sh_opterr = old_opterr;

  /* Set the OPTIND variable in any case, to handle "--" skipping.  It's
     highly unlikely that 14 digits will be too few. */
  if (sh_optind < 10)
    {
      numval[14] = sh_optind + '0';
      numval[15] = '\0';
      i = 14;
    }
  else
    {
      numval[i = 15] = '\0';
      n = sh_optind;
      do
    {
      numval[--i] = (n % 10) + '0';
    }
      while (n /= 10);
    }
  bind_variable ("OPTIND", numval + i);

  /* If an error occurred, decide which one it is and set the return
     code appropriately.  In all cases, the option character in error
     is in OPTOPT.  If an invalid option was encountered, OPTARG is
     NULL.  If a required option argument was missing, OPTARG points
     to a NULL string (that is, sh_optarg[0] == 0). */
  if (ret == '?')
    {
      if (sh_optarg == NULL)
    ret = G_INVALID_OPT;
      else if (sh_optarg[0] == '\0')
    ret = G_ARG_MISSING;
    }
        
  if (ret == G_EOF)
    {
      getopts_bind_variable (name, "?");
      return (EXECUTION_FAILURE);
    }

  if (ret == G_INVALID_OPT)
    {
      /* Invalid option encountered. */
      ret = getopts_bind_variable (name, "?");

      if (special_error)
    {
      strval[0] = (char)sh_optopt;
      strval[1] = '\0';
      bind_variable ("OPTARG", strval);
    }
      else
    unbind_variable ("OPTARG");

      return (ret);
    }

  if (ret == G_ARG_MISSING)
    {
      /* Required argument missing. */
      if (special_error)
    {
      ret = getopts_bind_variable (name, ":");

      strval[0] = (char)sh_optopt;
      strval[1] = '\0';
      bind_variable ("OPTARG", strval);
    }
      else
    {
      ret = getopts_bind_variable (name, "?");
      unbind_variable ("OPTARG");
    }
      return (ret);
    }           

  bind_variable ("OPTARG", sh_optarg);

  strval[0] = (char) ret;
  strval[1] = '\0';
  return (getopts_bind_variable (name, strval));
}

/* The getopts builtin.  Build an argv, and call dogetopts with it. */
int
getopts_builtin (list)
     WORD_LIST *list;
{
  char **av;
  int ac, ret;

  if (list == 0)
    {
      builtin_usage ();
      return EX_USAGE;
    }

  reset_internal_getopt ();
  if (internal_getopt (list, "") != -1)
    {
      builtin_usage ();
      return (EX_USAGE);
    }
  list = loptend;

  av = make_builtin_argv (list, &ac);
  ret = dogetopts (ac, av);
  free ((char *)av);

  return (ret);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/pushd.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/pushd.def used in: Step 1057, Step 1131
This file is pushd.def, from which is created pushd.c.  It implements the
builtins "pushd", "popd", and "dirs" in Bash.

Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES pushd.c

$BUILTIN pushd
$FUNCTION pushd_builtin
$DEPENDS_ON PUSHD_AND_POPD
$SHORT_DOC pushd [dir | +N | -N] [-n]
Adds a directory to the top of the directory stack, or rotates
the stack, making the new top of the stack the current working
directory.  With no arguments, exchanges the top two directories.

+N  Rotates the stack so that the Nth directory (counting
    from the left of the list shown by `dirs', starting with
    zero) is at the top.

-N  Rotates the stack so that the Nth directory (counting
    from the right of the list shown by `dirs', starting with
    zero) is at the top.

-n  suppress the normal change of directory when adding directories
    to the stack, so only the stack is manipulated.

dir adds DIR to the directory stack at the top, making it the
    new current working directory.

You can see the directory stack with the `dirs' command.
$END

$BUILTIN popd
$FUNCTION popd_builtin
$DEPENDS_ON PUSHD_AND_POPD
$SHORT_DOC popd [+N | -N] [-n]
Removes entries from the directory stack.  With no arguments,
removes the top directory from the stack, and cd's to the new
top directory.

+N  removes the Nth entry counting from the left of the list
    shown by `dirs', starting with zero.  For example: `popd +0'
    removes the first directory, `popd +1' the second.

-N  removes the Nth entry counting from the right of the list
    shown by `dirs', starting with zero.  For example: `popd -0'
    removes the last directory, `popd -1' the next to last.

-n  suppress the normal change of directory when removing directories
    from the stack, so only the stack is manipulated.

You can see the directory stack with the `dirs' command.
$END

$BUILTIN dirs
$FUNCTION dirs_builtin
$DEPENDS_ON PUSHD_AND_POPD
$SHORT_DOC dirs [-clpv] [+N] [-N]
Display the list of currently remembered directories.  Directories
find their way onto the list with the `pushd' command; you can get
back up through the list with the `popd' command.

The -l flag specifies that `dirs' should not print shorthand versions
of directories which are relative to your home directory.  This means
that `~/bin' might be displayed as `/homes/bfox/bin'.  The -v flag
causes `dirs' to print the directory stack with one entry per line,
prepending the directory name with its position in the stack.  The -p
flag does the same thing, but the stack position is not prepended.
The -c flag clears the directory stack by deleting all of the elements.

+N  displays the Nth entry counting from the left of the list shown by
    dirs when invoked without options, starting with zero.

-N  displays the Nth entry counting from the right of the list shown by
    dirs when invoked without options, starting with zero.
$END

#include <config.h>

#if defined (PUSHD_AND_POPD)
#include <stdio.h>
#ifndef _MINIX
#  include <sys/param.h>
#endif

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "../bashansi.h"

#include <errno.h>

#include <tilde/tilde.h>

#include "../shell.h"
#include "maxpath.h"
#include "common.h"
#include "builtext.h"

#ifdef LOADABLE_BUILTIN
#  include "builtins.h"
#endif

#if !defined (errno)
extern int errno;
#endif /* !errno */

/* The list of remembered directories. */
static char **pushd_directory_list = (char **)NULL;

/* Number of existing slots in this list. */
static int directory_list_size;

/* Offset to the end of the list. */
static int directory_list_offset;

static void pushd_error __P((int, char *));
static void clear_directory_stack __P((void));
static int cd_to_string __P((char *));
static int change_to_temp __P((char *));
static void add_dirstack_element __P((char *));
static int get_dirstack_index __P((intmax_t, int, int *));

#define NOCD        0x01
#define ROTATE      0x02
#define LONGFORM    0x04
#define CLEARSTAK   0x08

int
pushd_builtin (list)
     WORD_LIST *list;
{
  char *temp, *current_directory, *top;
  int j, flags;
  intmax_t num;
  char direction;

  if (list && list->word && ISOPTION (list->word->word, '-'))
    list = list->next;

  /* If there is no argument list then switch current and
     top of list. */
  if (list == 0)
    {
      if (directory_list_offset == 0)
    {
      builtin_error ("no other directory");
      return (EXECUTION_FAILURE);
    }

      current_directory = get_working_directory ("pushd");
      if (current_directory == 0)
    return (EXECUTION_FAILURE);

      j = directory_list_offset - 1;
      temp = pushd_directory_list[j];
      pushd_directory_list[j] = current_directory;
      j = change_to_temp (temp);
      free (temp);
      return j;
    }

  for (flags = 0; list; list = list->next)
    {
      if (ISOPTION (list->word->word, 'n'))
    {
      flags |= NOCD;
    }
      else if (ISOPTION (list->word->word, '-'))
    {
      list = list->next;
      break;
    }
      else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
    /* Let `pushd -' work like it used to. */
    break;
      else if (((direction = list->word->word[0]) == '+') || direction == '-')
    {
      if (legal_number (list->word->word + 1, &num) == 0)
        {
          sh_invalidnum (list->word->word);
          builtin_usage ();
          return (EXECUTION_FAILURE);
        }

      if (direction == '-')
        num = directory_list_offset - num;

      if (num > directory_list_offset || num < 0)
        {
          pushd_error (directory_list_offset, list->word->word);
          return (EXECUTION_FAILURE);
        }
      flags |= ROTATE;
    }
      else if (*list->word->word == '-')
    {
      sh_invalidopt (list->word->word);
      builtin_usage ();
      return (EXECUTION_FAILURE);
    }
      else
    break;
    }

  if (flags & ROTATE)
    {
      /* Rotate the stack num times.  Remember, the current
     directory acts like it is part of the stack. */
      temp = get_working_directory ("pushd");

      if (num == 0)
    {
      j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
      free (temp);
      return j;
    }

      do
    {
      top = pushd_directory_list[directory_list_offset - 1];

      for (j = directory_list_offset - 2; j > -1; j--)
        pushd_directory_list[j + 1] = pushd_directory_list[j];

      pushd_directory_list[j + 1] = temp;

      temp = top;
      num--;
    }
      while (num);

      j = ((flags & NOCD) == 0) ? change_to_temp (temp) : EXECUTION_SUCCESS;
      free (temp);
      return j;
    }

  if (list == 0)
    return (EXECUTION_SUCCESS);

  /* Change to the directory in list->word->word.  Save the current
     directory on the top of the stack. */
  current_directory = get_working_directory ("pushd");
  if (current_directory == 0)
    return (EXECUTION_FAILURE);

  j = ((flags & NOCD) == 0) ? cd_builtin (list) : EXECUTION_SUCCESS;
  if (j == EXECUTION_SUCCESS)
    {
      add_dirstack_element ((flags & NOCD) ? savestring (list->word->word) : current_directory);
      dirs_builtin ((WORD_LIST *)NULL);
      if (flags & NOCD)
    free (current_directory);
      return (EXECUTION_SUCCESS);
    }
  else
    {
      free (current_directory);
      return (EXECUTION_FAILURE);
    }
}

/* Pop the directory stack, and then change to the new top of the stack.
   If LIST is non-null it should consist of a word +N or -N, which says
   what element to delete from the stack.  The default is the top one. */
int
popd_builtin (list)
     WORD_LIST *list;
{
  register int i;
  intmax_t which;
  int flags;
  char direction;
  char *which_word;

  which_word = (char *)NULL;
  for (flags = 0, which = 0, direction = '+'; list; list = list->next)
    {
      if (ISOPTION (list->word->word, 'n'))
    {
      flags |= NOCD;
    }
      else if (ISOPTION (list->word->word, '-'))
    {
      list = list->next;
      break;
    }
      else if (((direction = list->word->word[0]) == '+') || direction == '-')
    {
      if (legal_number (list->word->word + 1, &which) == 0)
        {
          sh_invalidnum (list->word->word);
          builtin_usage ();
          return (EXECUTION_FAILURE);
        }
      which_word = list->word->word;
    }
      else if (*list->word->word == '-')
    {
      sh_invalidopt (list->word->word);
      builtin_usage ();
      return (EXECUTION_FAILURE);
    }
      else
    break;
    }

  if (which > directory_list_offset || (directory_list_offset == 0 && which == 0))
    {
      pushd_error (directory_list_offset, which_word ? which_word : "");
      return (EXECUTION_FAILURE);
    }

  /* Handle case of no specification, or top of stack specification. */
  if ((direction == '+' && which == 0) ||
      (direction == '-' && which == directory_list_offset))
    {
      i = ((flags & NOCD) == 0) ? cd_to_string (pushd_directory_list[directory_list_offset - 1])
                    : EXECUTION_SUCCESS;
      if (i != EXECUTION_SUCCESS)
    return (i);
      free (pushd_directory_list[--directory_list_offset]);
    }
  else
    {
      /* Since an offset other than the top directory was specified,
     remove that directory from the list and shift the remainder
     of the list into place. */
      i = (direction == '+') ? directory_list_offset - which : which;
      free (pushd_directory_list[i]);
      directory_list_offset--;

      /* Shift the remainder of the list into place. */
      for (; i < directory_list_offset; i++)
    pushd_directory_list[i] = pushd_directory_list[i + 1];
    }

  dirs_builtin ((WORD_LIST *)NULL);
  return (EXECUTION_SUCCESS);
}

/* Print the current list of directories on the directory stack. */
int
dirs_builtin (list)
     WORD_LIST *list;
{
  int flags, desired_index, index_flag, vflag;
  intmax_t i;
  char *temp, *w;

  for (flags = vflag = index_flag = 0, desired_index = -1, w = ""; list; list = list->next)
    {
      if (ISOPTION (list->word->word, 'l'))
    {
      flags |= LONGFORM;
    }
      else if (ISOPTION (list->word->word, 'c'))
    {
      flags |= CLEARSTAK;
    }
      else if (ISOPTION (list->word->word, 'v'))
    {
      vflag |= 2;
    }
      else if (ISOPTION (list->word->word, 'p'))
    {
      vflag |= 1;
    }
      else if (ISOPTION (list->word->word, '-'))
    {
      list = list->next;
      break;
    }
      else if (*list->word->word == '+' || *list->word->word == '-')
    {
      int sign;
      if (legal_number (w = list->word->word + 1, &i) == 0)
        {
          sh_invalidnum (list->word->word);
          builtin_usage ();
          return (EXECUTION_FAILURE);
        }
      sign = (*list->word->word == '+') ? 1 : -1;
      desired_index = get_dirstack_index (i, sign, &index_flag);
    }
      else
    {
      sh_invalidopt (list->word->word);
      builtin_usage ();
      return (EXECUTION_FAILURE);
    }
    }

  if (flags & CLEARSTAK)
    {
      clear_directory_stack ();
      return (EXECUTION_SUCCESS);
    }

  if (index_flag && (desired_index < 0 || desired_index > directory_list_offset))
    {
      pushd_error (directory_list_offset, w);
      return (EXECUTION_FAILURE);
    }

#define DIRSTACK_FORMAT(temp) \
  (flags & LONGFORM) ? temp : polite_directory_format (temp)

  /* The first directory printed is always the current working directory. */
  if (index_flag == 0 || (index_flag == 1 && desired_index == 0))
    {
      temp = get_working_directory ("dirs");
      if (temp == 0)
    temp = savestring ("<no current directory>");
      if (vflag & 2)
    printf ("%2d  %s", 0, DIRSTACK_FORMAT (temp));
      else
    printf ("%s", DIRSTACK_FORMAT (temp));
      free (temp);
      if (index_flag)
    {
      putchar ('\n');
      return EXECUTION_SUCCESS;
    }
    }

#define DIRSTACK_ENTRY(i) \
  (flags & LONGFORM) ? pushd_directory_list[i] \
             : polite_directory_format (pushd_directory_list[i])

  /* Now print the requested directory stack entries. */
  if (index_flag)
    {
      if (vflag & 2)
    printf ("%2d  %s", directory_list_offset - desired_index,
               DIRSTACK_ENTRY (desired_index));
      else
    printf ("%s", DIRSTACK_ENTRY (desired_index));
    }
  else
    for (i = directory_list_offset - 1; i >= 0; i--)
      if (vflag >= 2)
    printf ("\n%2d  %s", directory_list_offset - (int)i, DIRSTACK_ENTRY (i));
      else
    printf ("%s%s", (vflag & 1) ? "\n" : " ", DIRSTACK_ENTRY (i));

  putchar ('\n');
  fflush (stdout);
  return (EXECUTION_SUCCESS);
}

static void
pushd_error (offset, arg)
     int offset;
     char *arg;
{
  if (offset == 0)
    builtin_error ("directory stack empty");
  else
    sh_erange (arg, "directory stack index");
}

static void
clear_directory_stack ()
{
  register int i;

  for (i = 0; i < directory_list_offset; i++)
    free (pushd_directory_list[i]);
  directory_list_offset = 0;
}

/* Switch to the directory in NAME.  This uses the cd_builtin to do the work,
   so if the result is EXECUTION_FAILURE then an error message has already
   been printed. */
static int
cd_to_string (name)
     char *name;
{
  WORD_LIST *tlist;
  int result;

  tlist = make_word_list (make_word (name), NULL);
  result = cd_builtin (tlist);
  dispose_words (tlist);
  return (result);
}

static int
change_to_temp (temp)
     char *temp;
{
  int tt;

  tt = temp ? cd_to_string (temp) : EXECUTION_FAILURE;

  if (tt == EXECUTION_SUCCESS)
    dirs_builtin ((WORD_LIST *)NULL);

  return (tt);
}

static void
add_dirstack_element (dir)
     char *dir;
{
  if (directory_list_offset == directory_list_size)
    pushd_directory_list = strvec_resize (pushd_directory_list, directory_list_size += 10);
  pushd_directory_list[directory_list_offset++] = dir;
}

static int
get_dirstack_index (ind, sign, indexp)
     intmax_t ind;
     int sign, *indexp;
{
  if (indexp)
    *indexp = sign > 0 ? 1 : 2;

  /* dirs +0 prints the current working directory. */
  /* dirs -0 prints last element in directory stack */
  if (ind == 0 && sign > 0)
    return 0;
  else if (ind == directory_list_offset)
    {
      if (indexp)
    *indexp = sign > 0 ? 2 : 1;
      return 0;
    }
  else if (ind >= 0 && ind <= directory_list_offset)
    return (sign > 0 ? directory_list_offset - ind : ind);
  else
    return -1;
}

/* Used by the tilde expansion code. */
char *
get_dirstack_from_string (string)
     char *string;
{
  int ind, sign, index_flag;
  intmax_t i;

  sign = 1;
  if (*string == '-' || *string == '+')
    {
      sign = (*string == '-') ? -1 : 1;
      string++;
    }
  if (legal_number (string, &i) == 0)
    return ((char *)NULL);

  index_flag = 0;
  ind = get_dirstack_index (i, sign, &index_flag);
  if (index_flag && (ind < 0 || ind > directory_list_offset))
    return ((char *)NULL);
  if (index_flag == 0 || (index_flag == 1 && ind == 0))
    return (get_string_value ("PWD"));
  else
    return (pushd_directory_list[ind]);
}

#ifdef INCLUDE_UNUSED
char *
get_dirstack_element (ind, sign)
     intmax_t ind;
     int sign;
{
  int i;

  i = get_dirstack_index (ind, sign, (int *)NULL);
  return (i < 0 || i > directory_list_offset) ? (char *)NULL
                          : pushd_directory_list[i];
}
#endif

void
set_dirstack_element (ind, sign, value)
     intmax_t ind;
     int  sign;
     char *value;
{
  int i;

  i = get_dirstack_index (ind, sign, (int *)NULL);
  if (ind == 0 || i < 0 || i > directory_list_offset)
    return;
  free (pushd_directory_list[i]);
  pushd_directory_list[i] = savestring (value);
}

WORD_LIST *
get_directory_stack ()
{
  register int i;
  WORD_LIST *ret;
  char *d, *t;

  for (ret = (WORD_LIST *)NULL, i = 0; i < directory_list_offset; i++)
    {
      d = polite_directory_format (pushd_directory_list[i]);
      ret = make_word_list (make_word (d), ret);
    }
  /* Now the current directory. */
  d = get_working_directory ("dirstack");
  i = 0;    /* sentinel to decide whether or not to free d */
  if (d == 0)
    d = ".";
  else
    {
      t = polite_directory_format (d);
      /* polite_directory_format sometimes returns its argument unchanged.
     If it does not, we can free d right away.  If it does, we need to
     mark d to be deleted later. */
      if (t != d)
    {
      free (d);
      d = t;
    }
      else /* t == d, so d is what we want */
    i = 1;
    }
  ret = make_word_list (make_word (d), ret);
  if (i)
    free (d);
  return ret;   /* was (REVERSE_LIST (ret, (WORD_LIST *)); */
}

#ifdef LOADABLE_BUILTIN
static char *dirs_doc[] = {
  "Display the list of currently remembered directories.  Directories",
  "find their way onto the list with the `pushd' command; you can get",
  "back up through the list with the `popd' command.",
  "",
  "The -l flag specifies that `dirs' should not print shorthand versions",
  "of directories which are relative to your home directory.  This means",
  "that `~/bin' might be displayed as `/homes/bfox/bin'.  The -v flag",
  "causes `dirs' to print the directory stack with one entry per line,",
  "prepending the directory name with its position in the stack.  The -p",
  "flag does the same thing, but the stack position is not prepended.",
  "The -c flag clears the directory stack by deleting all of the elements.",
  "",
  "+N   displays the Nth entry counting from the left of the list shown by",
  "     dirs when invoked without options, starting with zero.",
  "",
  "-N   displays the Nth entry counting from the right of the list shown by",
  "     dirs when invoked without options, starting with zero.",
  (char *)NULL
};

static char *pushd_doc[] = {
  "Adds a directory to the top of the directory stack, or rotates",
  "the stack, making the new top of the stack the current working",
  "directory.  With no arguments, exchanges the top two directories.",
  "",
  "+N   Rotates the stack so that the Nth directory (counting",
  "     from the left of the list shown by `dirs', starting with",
  "     zero) is at the top.",
  "",
  "-N   Rotates the stack so that the Nth directory (counting",
  "     from the right of the list shown by `dirs', starting with",
  "     zero) is at the top.",
  "",
  "-n   suppress the normal change of directory when adding directories",
  "     to the stack, so only the stack is manipulated.",
  "",
  "dir  adds DIR to the directory stack at the top, making it the",
  "     new current working directory.",
  "",
  "You can see the directory stack with the `dirs' command.",
  (char *)NULL
};

static char *popd_doc[] = {
  "Removes entries from the directory stack.  With no arguments,",
  "removes the top directory from the stack, and cd's to the new",
  "top directory.",
  "",
  "+N   removes the Nth entry counting from the left of the list",
  "     shown by `dirs', starting with zero.  For example: `popd +0'",
  "     removes the first directory, `popd +1' the second.",
  "",
  "-N   removes the Nth entry counting from the right of the list",
  "     shown by `dirs', starting with zero.  For example: `popd -0'",
  "     removes the last directory, `popd -1' the next to last.",
  "",
  "-n   suppress the normal change of directory when removing directories",
  "     from the stack, so only the stack is manipulated.",
  "",
  "You can see the directory stack with the `dirs' command.",
  (char *)NULL
};

struct builtin pushd_struct = {
    "pushd",
    pushd_builtin,
    BUILTIN_ENABLED,
    pushd_doc,
    "pushd [+N | -N] [-n] [dir]",
    0
};

struct builtin popd_struct = {
    "popd",
    popd_builtin,
    BUILTIN_ENABLED,
    popd_doc,
    "popd [+N | -N] [-n]",
    0
};

struct builtin dirs_struct = {
    "dirs",
    dirs_builtin,
    BUILTIN_ENABLED,
    dirs_doc,
    "dirs [-clpv] [+N] [-N]",
    0
};
#endif /* LOADABLE_BUILTIN */

#endif /* PUSHD_AND_POPD */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/shopt.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/shopt.def used in: Step 1057, Step 1133
This file is shopt.def, from which is created shopt.c.
It implements the Bash `shopt' builtin.

Copyright (C) 1994-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES shopt.c

$BUILTIN shopt
$FUNCTION shopt_builtin
$SHORT_DOC shopt [-pqsu] [-o long-option] optname [optname...]
Toggle the values of variables controlling optional behavior.
The -s flag means to enable (set) each OPTNAME; the -u flag
unsets each OPTNAME.  The -q flag suppresses output; the exit
status indicates whether each OPTNAME is set or unset.  The -o
option restricts the OPTNAMEs to those defined for use with
`set -o'.  With no options, or with the -p option, a list of all
settable options is displayed, with an indication of whether or
not each is set.
$END

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>

#include "../shell.h"
#include "../flags.h"
#include "common.h"
#include "bashgetopt.h"

#define UNSETOPT    0
#define SETOPT      1

#define OPTFMT      "%-15s\t%s\n"

extern int allow_null_glob_expansion, glob_dot_filenames;
extern int cdable_vars, mail_warning, source_uses_path;
extern int no_exit_on_failed_exec, print_shift_error;
extern int check_hashed_filenames, promptvars;
extern int cdspelling, expand_aliases;
extern int check_window_size;
extern int glob_ignore_case;
extern int hup_on_exit;
extern int xpg_echo;

#if defined (EXTENDED_GLOB)
extern int extended_glob;
#endif

#if defined (HISTORY)
extern int literal_history, command_oriented_history;
extern int force_append_history;
#endif

#if defined (READLINE)
extern int hist_verify, history_reediting, perform_hostname_completion;
extern int no_empty_command_completion;
extern int enable_hostname_completion __P((int));
#endif

#if defined (PROGRAMMABLE_COMPLETION)
extern int prog_completion_enabled;
#endif

#if defined (RESTRICTED_SHELL)
extern char *shell_name;
#endif

static void shopt_error __P((char *));

static int set_interactive_comments __P((int));

#if defined (RESTRICTED_SHELL)
static int set_restricted_shell __P((int));
#endif

static int shopt_login_shell;

typedef int shopt_set_func_t __P((int));

static struct {
  char *name;
  int  *value;
  shopt_set_func_t *set_func;
} shopt_vars[] = {
  { "cdable_vars", &cdable_vars, (shopt_set_func_t *)NULL },
  { "cdspell", &cdspelling, (shopt_set_func_t *)NULL },
  { "checkhash", &check_hashed_filenames, (shopt_set_func_t *)NULL },
  { "checkwinsize", &check_window_size, (shopt_set_func_t *)NULL },
#if defined (HISTORY)
  { "cmdhist", &command_oriented_history, (shopt_set_func_t *)NULL },
#endif
  { "dotglob", &glob_dot_filenames, (shopt_set_func_t *)NULL },
  { "execfail", &no_exit_on_failed_exec, (shopt_set_func_t *)NULL },
  { "expand_aliases", &expand_aliases, (shopt_set_func_t *)NULL },
#if defined (EXTENDED_GLOB)
  { "extglob", &extended_glob, (shopt_set_func_t *)NULL },
#endif
#if defined (READLINE)
  { "histreedit", &history_reediting, (shopt_set_func_t *)NULL },
#endif
#if defined (HISTORY)
  { "histappend", &force_append_history, (shopt_set_func_t *)NULL },
#endif
#if defined (READLINE)
  { "histverify", &hist_verify, (shopt_set_func_t *)NULL },
  { "hostcomplete", &perform_hostname_completion, enable_hostname_completion },
#endif
  { "huponexit", &hup_on_exit, (shopt_set_func_t *)NULL },
  { "interactive_comments", &interactive_comments, set_interactive_comments },
#if defined (HISTORY)
  { "lithist", &literal_history, (shopt_set_func_t *)NULL },
#endif
  { "login_shell", &shopt_login_shell, set_login_shell },
  { "mailwarn", &mail_warning, (shopt_set_func_t *)NULL },
#if defined (READLINE)
  { "no_empty_cmd_completion", &no_empty_command_completion, (shopt_set_func_t *)NULL },
#endif
  { "nocaseglob", &glob_ignore_case, (shopt_set_func_t *)NULL },
  { "nullglob", &allow_null_glob_expansion, (shopt_set_func_t *)NULL },
#if defined (PROGRAMMABLE_COMPLETION)
  { "progcomp", &prog_completion_enabled, (shopt_set_func_t *)NULL },
#endif
  { "promptvars", &promptvars, (shopt_set_func_t *)NULL },
#if defined (RESTRICTED_SHELL)
  { "restricted_shell", &restricted_shell, set_restricted_shell },
#endif
  { "shift_verbose", &print_shift_error, (shopt_set_func_t *)NULL },
  { "sourcepath", &source_uses_path, (shopt_set_func_t *)NULL },
  { "xpg_echo", &xpg_echo, (shopt_set_func_t *)NULL },
  { (char *)0, (int *)0, (shopt_set_func_t *)NULL }
};

static char *on = "on";
static char *off = "off";

static int find_shopt __P((char *));
static int toggle_shopts __P((int, WORD_LIST *, int));
static void print_shopt __P((char *, int, int));
static int list_shopts __P((WORD_LIST *, int));
static int list_some_shopts __P((int, int));
static int list_shopt_o_options __P((WORD_LIST *, int));
static int list_some_o_options __P((int, int));
static int set_shopt_o_options __P((int, WORD_LIST *, int));

#define SFLAG   0x01
#define UFLAG   0x02
#define QFLAG   0x04
#define OFLAG   0x08
#define PFLAG   0x10

int
shopt_builtin (list)
     WORD_LIST *list;
{
  int opt, flags, rval;

  flags = 0;
  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "psuoq")) != -1)
    {
      switch (opt)
    {
    case 's':
      flags |= SFLAG;
      break;
    case 'u':
      flags |= UFLAG;
      break;
    case 'q':
      flags |= QFLAG;
      break;
    case 'o':
      flags |= OFLAG;
      break;
    case 'p':
      flags |= PFLAG;
      break;
    default:
      builtin_usage ();
      return (EX_USAGE);
    }
    }
  list = loptend;

  if ((flags & (SFLAG|UFLAG)) == (SFLAG|UFLAG))
    {
      builtin_error ("cannot set and unset shell options simultaneously");
      return (EXECUTION_FAILURE);
    }

  rval = EXECUTION_SUCCESS;
  if ((flags & OFLAG) && ((flags & (SFLAG|UFLAG)) == 0))    /* shopt -o */
    rval = list_shopt_o_options (list, flags);
  else if (list && (flags & OFLAG))     /* shopt -so args */
    rval = set_shopt_o_options ((flags & SFLAG) ? FLAG_ON : FLAG_OFF, list, flags & QFLAG);
  else if (flags & OFLAG)   /* shopt -so */
    rval = list_some_o_options ((flags & SFLAG) ? 1 : 0, flags);
  else if (list && (flags & (SFLAG|UFLAG))) /* shopt -su args */
    rval = toggle_shopts ((flags & SFLAG) ? SETOPT : UNSETOPT, list, flags & QFLAG);
  else if ((flags & (SFLAG|UFLAG)) == 0)    /* shopt [args] */
    rval = list_shopts (list, flags);
  else                      /* shopt -su */
    rval = list_some_shopts ((flags & SFLAG) ? SETOPT : UNSETOPT, flags);
  return (rval);
}

/* Reset the options managed by `shopt' to the values they would have at
   shell startup. */
void
reset_shopt_options ()
{
  allow_null_glob_expansion = glob_dot_filenames = 0;
  cdable_vars = mail_warning = 0;
  no_exit_on_failed_exec = print_shift_error = 0;
  check_hashed_filenames = cdspelling = expand_aliases = check_window_size = 0;

  source_uses_path = promptvars = 1;

#if defined (EXTENDED_GLOB)
  extended_glob = 0;
#endif

#if defined (HISTORY)
  literal_history = force_append_history = 0;
  command_oriented_history = 1;
#endif

#if defined (READLINE)
  hist_verify = history_reediting = 0;
  perform_hostname_completion = 1;
#endif

  shopt_login_shell = login_shell;
}

static int
find_shopt (name)
     char *name;
{
  int i;

  for (i = 0; shopt_vars[i].name; i++)
    if (STREQ (name, shopt_vars[i].name))
      return i;
  return -1;
}

static void
shopt_error (s)
     char *s;
{
  builtin_error ("%s: invalid shell option name", s);
}

static int
toggle_shopts (mode, list, quiet)
     int mode;
     WORD_LIST *list;
     int quiet;
{
  WORD_LIST *l;
  int ind, rval;

  for (l = list, rval = EXECUTION_SUCCESS; l; l = l->next)
    {
      ind = find_shopt (l->word->word);
      if (ind < 0)
    {
      shopt_error (l->word->word);
      rval = EXECUTION_FAILURE;
    }
      else
    {
      *shopt_vars[ind].value = mode;    /* 1 for set, 0 for unset */
      if (shopt_vars[ind].set_func)
        (*shopt_vars[ind].set_func) (mode);
    }
    }
  return (rval);
}

static void
print_shopt (name, val, flags)
     char *name;
     int val, flags;
{
  if (flags & PFLAG)
    printf ("shopt %s %s\n", val ? "-s" : "-u", name);
  else
    printf (OPTFMT, name, val ? on : off);
}

/* List the values of all or any of the `shopt' options.  Returns 0 if
   all were listed or all variables queried were on; 1 otherwise. */
static int
list_shopts (list, flags)
     WORD_LIST *list;
     int flags;
{
  WORD_LIST *l;
  int i, val, rval;

  if (list == 0)
    {
      for (i = 0; shopt_vars[i].name; i++)
    {
      val = *shopt_vars[i].value;
      if ((flags & QFLAG) == 0)
        print_shopt (shopt_vars[i].name, val, flags);
    }
      return (EXECUTION_SUCCESS);
    }

  for (l = list, rval = EXECUTION_SUCCESS; l; l = l->next)
    {
      i = find_shopt (l->word->word);
      if (i < 0)
    {
      shopt_error (l->word->word);
      rval = EXECUTION_FAILURE;
      continue;
    }
      val = *shopt_vars[i].value;
      if (val == 0)
    rval = EXECUTION_FAILURE;
      if ((flags & QFLAG) == 0)
    print_shopt (l->word->word, val, flags);
    }

  return (rval);
}

static int
list_some_shopts (mode, flags)
     int mode, flags;
{
  int val, i;

  for (i = 0; shopt_vars[i].name; i++)
    {
      val = *shopt_vars[i].value;
      if (((flags & QFLAG) == 0) && mode == val)
    print_shopt (shopt_vars[i].name, val, flags);
    }
  return (EXECUTION_SUCCESS);
}

static int
list_shopt_o_options (list, flags)
     WORD_LIST *list;
     int flags;
{
  WORD_LIST *l;
  int val, rval;

  if (list == 0)
    {
      if ((flags & QFLAG) == 0)
    list_minus_o_opts (-1, (flags & PFLAG));
      return (EXECUTION_SUCCESS);
    }

  for (l = list, rval = EXECUTION_SUCCESS; l; l = l->next)
    {
      val = minus_o_option_value (l->word->word);
      if (val == -1)
    {
      sh_invalidoptname (l->word->word);
      rval = EXECUTION_FAILURE;
      continue;
    }
      if (val == 0)
    rval = EXECUTION_FAILURE;
      if ((flags & QFLAG) == 0)
    {
      if (flags & PFLAG)
        printf ("set %co %s\n", val ? '-' : '+', l->word->word);
      else
        printf (OPTFMT, l->word->word, val ? on : off);
    }
    }
  return (rval);
}

static int
list_some_o_options (mode, flags)
     int mode, flags;
{
  if ((flags & QFLAG) == 0)
    list_minus_o_opts (mode, (flags & PFLAG));
  return (EXECUTION_SUCCESS);
}

static int
set_shopt_o_options (mode, list, quiet)
     int mode;
     WORD_LIST *list;
     int quiet;
{
  WORD_LIST *l;
  int rval;

  for (l = list, rval = EXECUTION_SUCCESS; l; l = l->next)
    {
      if (set_minus_o_option (mode, l->word->word) == EXECUTION_FAILURE)
    rval = EXECUTION_FAILURE;
    }
  set_shellopts ();
  return rval;
}

/* If we set or unset interactive_comments with shopt, make sure the
   change is reflected in $SHELLOPTS. */
static int
set_interactive_comments (mode)
     int mode;
{
  set_shellopts ();
  return (0);
}

#if defined (RESTRICTED_SHELL)
/* Don't allow the value of restricted_shell to be modified. */

static int
set_restricted_shell (mode)
     int mode;
{
  static int save_restricted = -1;

  if (save_restricted == -1)
    save_restricted = shell_is_restricted (shell_name);

  restricted_shell = save_restricted;
  return (0);
}
#endif /* RESTRICTED_SHELL */

/* Not static so shell.c can call it to initialize shopt_login_shell */
int
set_login_shell (mode)
     int mode;
{
  shopt_login_shell = login_shell != 0;
  return (0);
}

char **
get_shopt_options ()
{
  char **ret;
  int n, i;

  n = sizeof (shopt_vars) / sizeof (shopt_vars[0]);
  ret = strvec_create (n + 1);
  for (i = 0; shopt_vars[i].name; i++)
    ret[i] = savestring (shopt_vars[i].name);
  ret[i] = (char *)NULL;
  return ret;
}

/*
 * External interface for other parts of the shell.  NAME is a string option;
 * MODE is 0 if we want to unset an option; 1 if we want to set an option.
 * REUSABLE is 1 if we want to print output in a form that may be reused.
 */
int
shopt_setopt (name, mode)
     char *name;
     int mode;
{
  WORD_LIST *wl;
  int r;

  wl = add_string_to_list (name, (WORD_LIST *)NULL);
  r = toggle_shopts (mode, wl, 0);
  dispose_words (wl);
  return r;
}

int
shopt_listopt (name, reusable)
     char *name;
     int reusable;
{
  int i;

  if (name == 0)
    return (list_shopts ((WORD_LIST *)NULL, reusable ? PFLAG : 0));

  i = find_shopt (name);
  if (i < 0)
    {
      shopt_error (name);
      return (EXECUTION_FAILURE);
    }

  print_shopt (name, *shopt_vars[i].value, reusable ? PFLAG : 0);
  return (EXECUTION_SUCCESS);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/printf.def?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/printf.def used in: Step 1057, Step 1135
This file is printf.def, from which is created printf.c.
It implements the builtin "printf" in Bash.

Copyright (C) 1997-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA

$PRODUCES printf.c

$BUILTIN printf
$FUNCTION printf_builtin
$SHORT_DOC printf format [arguments]
printf formats and prints ARGUMENTS under control of the FORMAT. 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
argument.  In addition to the standard printf(1) formats, %b means to
expand backslash escape sequences in the corresponding argument, and %q
means to quote the argument in a way that can be reused as shell input.
$END

#include <config.h>

#include "../bashtypes.h"

#include <errno.h>
#if defined (HAVE_LIMITS_H)
#  include <limits.h>
#else
   /* Assume 32-bit ints. */
#  define INT_MAX       2147483647
#  define INT_MIN       (-2147483647-1)
#endif

#include <stdio.h>
#include <chartypes.h>

#ifdef HAVE_INTTYPES_H
#  include <inttypes.h>
#endif

#include "../bashansi.h"

#include "../shell.h"
#include "stdc.h"
#include "bashgetopt.h"
#include "common.h"

#if !defined (PRIdMAX)
#  if HAVE_LONG_LONG
#    define PRIdMAX "lld"
#  else
#    define PRIdMAX "ld"
#  endif
#endif

#if !defined (errno)
extern int errno;
#endif

#define PF(f, func) \
  do { \
    if (have_fieldwidth && have_precision) \
      tw += printf(f, fieldwidth, precision, func); \
    else if (have_fieldwidth) \
      tw += printf(f, fieldwidth, func); \
    else if (have_precision) \
      tw += printf(f, precision, func); \
    else \
      tw += printf(f, func); \
  } while (0)

/* We free the buffer used by mklong() if it's `too big'. */
#define PRETURN(value) \
  do \
    { \
      if (conv_bufsize > 4096 ) \
    { \
      free(conv_buf); \
      conv_bufsize = 0; \
      conv_buf = 0; \
    } \
      fflush (stdout); \
      return (value); \
    } \
  while (0)

#define SKIP1 "#'-+ 0"
#define LENMODS "hjlLtz"

static void printf_erange __P((char *));
static void printstr __P((char *, char *, int, int, int));
static int tescape __P((char *, int, char *, int *));
static char *bexpand __P((char *, int, int *, int *));
static char *mklong __P((char *, char *, size_t));
static int getchr __P((void));
static char *getstr __P((void));
static int  getint __P((void));
static intmax_t getintmax __P((void));
static uintmax_t getuintmax __P((void));

#if defined (HAVE_LONG_DOUBLE) && HAVE_DECL_STRTOLD
typedef long double floatmax_t;
#  define FLOATMAX_CONV "L"
#  define strtofltmax   strtold
#else
typedef double floatmax_t;
#  define FLOATMAX_CONV ""
#  define strtofltmax   strtod
#endif
static floatmax_t getfloatmax __P((void));

static int asciicode __P((void));

static WORD_LIST *garglist;
static int retval;
static int conversion_error;

static char *conv_buf;
static size_t conv_bufsize;

int
printf_builtin (list)
     WORD_LIST *list;
{
  int ch, fieldwidth, precision;
  int have_fieldwidth, have_precision;
  intmax_t tw;
  char convch, thisch, nextch, *format, *modstart, *fmt, *start;

  conversion_error = 0;
  retval = EXECUTION_SUCCESS;

  if (no_options (list))
    return (EX_USAGE);
  list = loptend;   /* skip over possible `--' */

  if (list == 0)
    {
      builtin_usage ();
      return (EX_USAGE);
    }

  if (list->word->word == 0 || list->word->word[0] == '\0')
    return (EXECUTION_SUCCESS);

  format = list->word->word;

  garglist = list->next;

  /* If the format string is empty after preprocessing, return immediately. */
  if (format == 0 || *format == 0)
    return (EXECUTION_SUCCESS);
      
  /* 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. */
  do
    {
      tw = 0;
      /* find next format specification */
      for (fmt = format; *fmt; fmt++)
    {
      precision = fieldwidth = 0;
      have_fieldwidth = have_precision = 0;


      if (*fmt == '\\')
        {
          fmt++;
          /* A NULL fourth argument to tescape means to not do special
         processing for \c. */
          fmt += tescape (fmt, 1, &nextch, (int *)NULL);
          putchar (nextch);
          fmt--;    /* for loop will increment it for us again */
          continue;
        }

      if (*fmt != '%')
        {
          putchar (*fmt);
          continue;
        }

      /* ASSERT(*fmt == '%') */
      start = fmt++;

      if (*fmt == '%')      /* %% prints a % */
        {
          putchar ('%');
          continue;
        }

      /* found format specification, skip to field width */
      for (; *fmt && strchr(SKIP1, *fmt); ++fmt)
        ;

      /* Skip optional field width. */
      if (*fmt == '*')
        {
          fmt++;
          have_fieldwidth = 1;
          fieldwidth = getint ();
        }
      else
        while (DIGIT (*fmt))
          fmt++;

      /* Skip optional '.' and precision */
      if (*fmt == '.')
        {
          ++fmt;
          if (*fmt == '*')
        {
          fmt++;
          have_precision = 1;
          precision = getint ();
        }
          else
        while (DIGIT (*fmt))
          fmt++;
        }

      /* skip possible format modifiers */
      modstart = fmt;
      while (*fmt && strchr (LENMODS, *fmt))
        fmt++;
        
      if (*fmt == 0)
        {
          builtin_error ("`%s': missing format character", start);
          PRETURN (EXECUTION_FAILURE);
        }

      convch = *fmt;
      thisch = modstart[0];
      nextch = modstart[1];
      modstart[0] = convch;
      modstart[1] = '\0';

      switch(convch)
        {
        case 'c':
          {
        char p;

        p = getchr ();
        PF(start, p);
        break;
          }

        case 's':
          {
        char *p;

        p = getstr ();
        PF(start, p);
        break;
          }

        case 'n':
          {
        char *var;

        var = getstr ();
        if (var && *var)
          {
            if (legal_identifier (var))
              bind_var_to_int (var, tw);
            else
              {
            sh_invalidid (var);
            PRETURN (EXECUTION_FAILURE);
              }
          }
        break;
          }

        case 'b':       /* expand escapes in argument */
          {
        char *p, *xp;
        int rlen;

        p = getstr ();
        ch = rlen = 0;
        xp = bexpand (p, strlen (p), &ch, &rlen);

        if (xp)
          {
            /* Have to use printstr because of possible NUL bytes
               in XP -- printf does not handle that well. */
            printstr (start, xp, rlen, fieldwidth, precision);
            free (xp);
          }

        if (ch)
          PRETURN (retval);
        break;
          }

        case 'q':       /* print with shell quoting */
          {
        char *p, *xp;

        p = getstr ();
        if (ansic_shouldquote (p))
          xp = ansic_quote (p, 0, (int *)0);
        else
          xp = sh_backslash_quote (p);
        if (xp)
          {
            /* Use printstr to get fieldwidth and precision right. */
            printstr (start, xp, strlen (xp), fieldwidth, precision);
            free (xp);
          }
        break;
          }

        case 'd':
        case 'i':
          {
        char *f;
        long p;
        intmax_t pp;

        p = pp = getintmax ();
        if (p != pp)
          {
            f = mklong (start, PRIdMAX, sizeof (PRIdMAX) - 2);
            PF (f, pp);
          }
        else
          {
            /* Optimize the common case where the integer fits
               in "long".  This also works around some long
               long and/or intmax_t library bugs in the common
               case, e.g. glibc 2.2 x86.  */
            f = mklong (start, "l", 1);
            PF (f, p);
          }
        break;
          }

        case 'o':
        case 'u':
        case 'x':
        case 'X':
          {
        char *f;
        unsigned long p;
        uintmax_t pp;

        p = pp = getuintmax ();
        if (p != pp)
          {
            f = mklong (start, PRIdMAX, sizeof (PRIdMAX) - 2);
            PF (f, pp);
          }
        else
          {
            f = mklong (start, "l", 1);
            PF (f, p);
          }
        break;
          }

        case 'e':
        case 'E':
        case 'f':
        case 'F':
        case 'g':
        case 'G':
#if defined (HAVE_PRINTF_A_FORMAT)
        case 'a':
        case 'A':
#endif
          {
        char *f;
        floatmax_t p;

        p = getfloatmax ();
        f = mklong (start, FLOATMAX_CONV, sizeof(FLOATMAX_CONV) - 1);
        PF (f, p);
        break;
          }

        /* We don't output unrecognized format characters; we print an
           error message and return a failure exit status. */
        default:
          builtin_error ("`%c': invalid format character", convch);
          PRETURN (EXECUTION_FAILURE);
        }

      modstart[0] = thisch;
      modstart[1] = nextch;
    }
    }
  while (garglist && garglist != list->next);

  if (conversion_error)
    retval = EXECUTION_FAILURE;

  PRETURN (retval);
}

static void
printf_erange (s)
     char *s;
{
  builtin_error ("warning: %s: %s", s, strerror(ERANGE));
}

/* We duplicate a lot of what printf(3) does here. */
static void
printstr (fmt, string, len, fieldwidth, precision)
     char *fmt;         /* format */
     char *string;      /* expanded string argument */
     int len;           /* length of expanded string */
     int fieldwidth;        /* argument for width of `*' */
     int precision;     /* argument for precision of `*' */
{
#if 0
  char *s;
#endif
  int padlen, nc, ljust, i;
  int fw, pr;           /* fieldwidth and precision */

  if (string == 0 || *string == '\0')
    return;

#if 0
  s = fmt;
#endif
  if (*fmt == '%')
    fmt++;

  ljust = fw = 0;
  pr = -1;

  /* skip flags */
  while (strchr (SKIP1, *fmt))
    {
      if (*fmt == '-')
    ljust = 1;
      fmt++;
    }

  /* get fieldwidth, if present */
  if (*fmt == '*')
    {
      fmt++;
      fw = fieldwidth;
      if (fw < 0)
    {
      fw = -fw;
      ljust = 1;
    }
    }
  else if (DIGIT (*fmt))
    {
      fw = *fmt++ - '0';
      while (DIGIT (*fmt))
    fw = (fw * 10) + (*fmt++ - '0');
    }

  /* get precision, if present */
  if (*fmt == '.')
    {
      fmt++;
      if (*fmt == '*')
    {
      fmt++;
      pr = precision;
    }
      else if (DIGIT (*fmt))
    {
      pr = *fmt++ - '0';
      while (DIGIT (*fmt))
        pr = (pr * 10) + (*fmt++ - '0');
    }
    }

#if 0
  /* If we remove this, get rid of `s'. */
  if (*fmt != 'b' && *fmt != 'q')
    {
      internal_error ("format parsing problem: %s", s);
      fw = pr = 0;
    }
#endif

  /* chars from string to print */
  nc = (pr >= 0 && pr <= len) ? pr : len;

  padlen = fw - nc;
  if (padlen < 0)
    padlen = 0;
  if (ljust)
    padlen = -padlen;

  /* leading pad characters */
  for (; padlen > 0; padlen--)
    putchar (' ');

  /* output NC characters from STRING */
  for (i = 0; i < nc; i++)
    putchar (string[i]);

  /* output any necessary trailing padding */
  for (; padlen < 0; padlen++)
    putchar (' ');
}
  
/* Convert STRING by expanding the escape sequences specified by the
   POSIX standard for printf's `%b' format string.  If SAWC is non-null,
   recognize `\c' and use that as a string terminator.  If we see \c, set
   *SAWC to 1 before returning.  LEN is the length of STRING. */

/* Translate a single backslash-escape sequence starting at ESTART (the
   character after the backslash) and return the number of characters
   consumed by the sequence.  CP is the place to return the translated
   value.  *SAWC is set to 1 if the escape sequence was \c, since that means
   to short-circuit the rest of the processing.  If SAWC is null, we don't
   do the \c short-circuiting, and \c is treated as an unrecognized escape
   sequence.  */
static int
tescape (estart, trans_squote, cp, sawc)
     char *estart;
     int trans_squote;
     char *cp;
     int *sawc;
{
  register char *p;
  int temp, c, evalue;

  p = estart;

  switch (c = *p++)
    {
#if defined (__STDC__)
      case 'a': *cp = '\a'; break;
#else
      case 'a': *cp = '\007'; break;
#endif

      case 'b': *cp = '\b'; break;

      case 'e':
      case 'E': *cp = '\033'; break;    /* ESC -- non-ANSI */

      case 'f': *cp = '\f'; break;

      case 'n': *cp = '\n'; break;

      case 'r': *cp = '\r'; break;

      case 't': *cp = '\t'; break;

      case 'v': *cp = '\v'; break;

      /* %b octal constants are `\0' followed by one, two, or three
     octal digits... */
      case '0':
      /* but, as an extension, the other echo-like octal escape
     sequences are supported as well. */
      case '1': case '2': case '3': case '4':
      case '5': case '6': case '7':
    for (temp = 2+(c=='0'), evalue = c - '0'; ISOCTAL (*p) && temp--; p++)
      evalue = (evalue * 8) + OCTVALUE (*p);
    *cp = evalue & 0xFF;
    break;

      /* And, as another extension, we allow \xNNN, where each N is a
     hex digit. */
      case 'x':
    for (temp = 2, evalue = 0; ISXDIGIT ((unsigned char)*p) && temp--; p++)
      evalue = (evalue * 16) + HEXVALUE (*p);
    if (temp == 2)
      {
        builtin_error ("missing hex digit for \\x");
        *cp = '\\';
        return 0;
      }
    *cp = evalue & 0xFF;
    break;

      case '\\':    /* \\ -> \ */
    *cp = c;
    break;

      case '\'':    /* TRANS_SQUOTE != 0 means \' -> ' */
    if (trans_squote)
      *cp = c;
    else
      {
        *cp = '\\';
        return 0;
      }
    break;

      case 'c':
    if (sawc)
      {
        *sawc = 1;
        break;
      }
      /* other backslash escapes are passed through unaltered */
      default:
    *cp = '\\';
    return 0;
      }
  return (p - estart);
}

static char *
bexpand (string, len, sawc, lenp)
     char *string;
     int len, *sawc, *lenp;
{
  int temp;
  char *ret, *r, *s, c;

  if (string == 0 || *string == '\0')
    {
      if (sawc)
    *sawc = 0;
      if (lenp)
    *lenp = 0;
      return ((char *)NULL);
    }

  ret = (char *)xmalloc (len + 1);
  for (r = ret, s = string; s && *s; )
    {
      c = *s++;
      if (c != '\\' || *s == '\0')
    {
      *r++ = c;
      continue;
    }
      temp = 0;
      s += tescape (s, 0, &c, &temp);
      if (temp)
    {
      if (sawc)
        *sawc = 1;
      break;
    }

      *r++ = c;
    }

  *r = '\0';
  if (lenp)
    *lenp = r - ret;
  return ret;
}

static char *
mklong (str, modifiers, mlen)
     char *str;
     char *modifiers;
     size_t mlen;
{
  size_t len, slen;

  slen = strlen (str);
  len = slen + mlen + 1;

  if (len > conv_bufsize)
    {
      conv_bufsize = (((len + 1023) >> 10) << 10);
      conv_buf = (char *)xrealloc (conv_buf, conv_bufsize);
    }

  FASTCOPY (str, conv_buf, slen - 1);
  FASTCOPY (modifiers, conv_buf + slen - 1, mlen);

  conv_buf[len - 2] = str[slen - 1];
  conv_buf[len - 1] = '\0';
  return (conv_buf);
}

static int
getchr ()
{
  int ret;

  if (garglist == 0)
    return ('\0');

  ret = (int)garglist->word->word[0];
  garglist = garglist->next;
  return ret;
}

static char *
getstr ()
{
  char *ret;

  if (garglist == 0)
    return ("");

  ret = garglist->word->word;
  garglist = garglist->next;
  return ret;
}

static int
getint ()
{
  intmax_t ret;

  ret = getintmax ();

  if (ret > INT_MAX)
    {
      printf_erange (garglist->word->word);
      ret = INT_MAX;
    }
  else if (ret < INT_MIN)
    {
      printf_erange (garglist->word->word);
      ret = INT_MIN;
    }

  return ((int)ret);
}

static intmax_t
getintmax ()
{
  intmax_t ret;
  char *ep;

  if (garglist == 0)
    return (0);

  if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"')
    return asciicode ();

  errno = 0;
  ret = strtoimax (garglist->word->word, &ep, 0);

  if (*ep)
    {
      sh_invalidnum (garglist->word->word);
      /* POSIX.2 says ``...a diagnostic message shall be written to standard
     error, and the utility shall not exit with a zero exit status, but
     shall continue processing any remaining operands and shall write the
         value accumulated at the time the error was detected to standard
     output.''  Yecch. */
      ret = 0;
      conversion_error = 1;
    }
  else if (errno == ERANGE)
    printf_erange (garglist->word->word);

  garglist = garglist->next;
  return (ret);
}

static uintmax_t
getuintmax ()
{
  uintmax_t ret;
  char *ep;

  if (garglist == 0)
    return (0);

  if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"')
    return asciicode ();

  errno = 0;
  ret = strtoumax (garglist->word->word, &ep, 0);
  
  if (*ep)
    {
      sh_invalidnum (garglist->word->word);
      /* Same POSIX.2 conversion error requirements as getintmax(). */
      ret = 0;
      conversion_error = 1;
    }
  else if (errno == ERANGE)
    printf_erange (garglist->word->word);

  garglist = garglist->next;
  return (ret);
}

static floatmax_t
getfloatmax ()
{
  floatmax_t ret;
  char *ep;

  if (garglist == 0)
    return (0);

  if (garglist->word->word[0] == '\'' || garglist->word->word[0] == '"')
    return asciicode ();

  errno = 0;
  ret = strtofltmax (garglist->word->word, &ep);

  if (*ep)
    {
      sh_invalidnum (garglist->word->word);
      /* Same thing about POSIX.2 conversion error requirements. */
      ret = 0;
      conversion_error = 1;
    }
  else if (errno == ERANGE)
    printf_erange (garglist->word->word);

  garglist = garglist->next;
  return (ret);
}

/* NO check is needed for garglist here. */
static int
asciicode ()
{
  register int ch;

  ch = garglist->word->word[1];
  garglist = garglist->next;
  return (ch);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/bashtypes.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/bashtypes.h used in: Step 1074, Step 1084, Step 1086, Step 1090, Step 1092, Step 1102, Step 1112, Step 1122, Step 1124, Step 1126, Step 1128, Step 1136, Step 1151, Step 1154, Step 1161, Step 1171, Step 1172, Step 1174, Step 1175, Step 1181, Step 1189, Step 1202, Step 1205, Step 1206, Step 1207, Step 1209, Step 1211, Step 1212, Step 1213, Step 1216, Step 1217, Step 1218, Step 1220, Step 1225, Step 1226, Step 1227, Step 1228, Step 1239, Step 1240, Step 1241
/* bashtypes.h -- Bash system types. */

/* Copyright (C) 1993 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#if !defined (_BASHTYPES_H_)
#  define _BASHTYPES_H_

#if defined (CRAY)
#  define word __word
#endif

#include <sys/types.h>

#if defined (CRAY)
#  undef word
#endif

#if defined (HAVE_INTTYPES_H)
#  include <inttypes.h>
#endif

#endif /* _BASHTYPES_H_ */

https://git.savannah.gnu.org/cgit/bash.git/tree/include/posixstat.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/posixstat.h used in: Step 1084, Step 1102, Step 1112, Step 1122, Step 1161, Step 1171, Step 1172, Step 1175, Step 1181, Step 1189, Step 1202, Step 1206, Step 1211, Step 1218, Step 1220, Step 1225, Step 1241
/* posixstat.h -- Posix stat(2) definitions for systems that
   don't have them. */

/* Copyright (C) 1987,1991 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

/* This file should be included instead of <sys/stat.h>.
   It relies on the local sys/stat.h to work though. */
#if !defined (_POSIXSTAT_H_)
#define _POSIXSTAT_H_

#include <sys/stat.h>

#if defined (STAT_MACROS_BROKEN)
#  undef S_ISBLK
#  undef S_ISCHR
#  undef S_ISDIR
#  undef S_ISFIFO
#  undef S_ISREG
#  undef S_ISLNK
#endif /* STAT_MACROS_BROKEN */

/* These are guaranteed to work only on isc386 */
#if !defined (S_IFDIR) && !defined (S_ISDIR)
#  define S_IFDIR 0040000
#endif /* !S_IFDIR && !S_ISDIR */
#if !defined (S_IFMT)
#  define S_IFMT  0170000
#endif /* !S_IFMT */

/* Posix 1003.1 5.6.1.1 <sys/stat.h> file types */

/* Some Posix-wannabe systems define _S_IF* macros instead of S_IF*, but
   do not provide the S_IS* macros that Posix requires. */

#if defined (_S_IFMT) && !defined (S_IFMT)
#define S_IFMT _S_IFMT
#endif
#if defined (_S_IFIFO) && !defined (S_IFIFO)
#define S_IFIFO _S_IFIFO
#endif
#if defined (_S_IFCHR) && !defined (S_IFCHR)
#define S_IFCHR _S_IFCHR
#endif
#if defined (_S_IFDIR) && !defined (S_IFDIR)
#define S_IFDIR _S_IFDIR
#endif
#if defined (_S_IFBLK) && !defined (S_IFBLK)
#define S_IFBLK _S_IFBLK
#endif
#if defined (_S_IFREG) && !defined (S_IFREG)
#define S_IFREG _S_IFREG
#endif
#if defined (_S_IFLNK) && !defined (S_IFLNK)
#define S_IFLNK _S_IFLNK
#endif
#if defined (_S_IFSOCK) && !defined (S_IFSOCK)
#define S_IFSOCK _S_IFSOCK
#endif

/* Test for each symbol individually and define the ones necessary (some
   systems claiming Posix compatibility define some but not all). */

#if defined (S_IFBLK) && !defined (S_ISBLK)
#define S_ISBLK(m)  (((m)&S_IFMT) == S_IFBLK)   /* block device */
#endif

#if defined (S_IFCHR) && !defined (S_ISCHR)
#define S_ISCHR(m)  (((m)&S_IFMT) == S_IFCHR)   /* character device */
#endif

#if defined (S_IFDIR) && !defined (S_ISDIR)
#define S_ISDIR(m)  (((m)&S_IFMT) == S_IFDIR)   /* directory */
#endif

#if defined (S_IFREG) && !defined (S_ISREG)
#define S_ISREG(m)  (((m)&S_IFMT) == S_IFREG)   /* file */
#endif

#if defined (S_IFIFO) && !defined (S_ISFIFO)
#define S_ISFIFO(m) (((m)&S_IFMT) == S_IFIFO)   /* fifo - named pipe */
#endif

#if defined (S_IFLNK) && !defined (S_ISLNK)
#define S_ISLNK(m)  (((m)&S_IFMT) == S_IFLNK)   /* symbolic link */
#endif

#if defined (S_IFSOCK) && !defined (S_ISSOCK)
#define S_ISSOCK(m) (((m)&S_IFMT) == S_IFSOCK)  /* socket */
#endif

/*
 * POSIX 1003.1 5.6.1.2 <sys/stat.h> File Modes
 */

#if !defined (S_IRWXU)
#  if !defined (S_IREAD)
#    define S_IREAD 00400
#    define S_IWRITE    00200
#    define S_IEXEC 00100
#  endif /* S_IREAD */

#  if !defined (S_IRUSR)
#    define S_IRUSR S_IREAD         /* read, owner */
#    define S_IWUSR S_IWRITE        /* write, owner */
#    define S_IXUSR S_IEXEC         /* execute, owner */

#    define S_IRGRP (S_IREAD  >> 3)     /* read, group */
#    define S_IWGRP (S_IWRITE >> 3)     /* write, group */
#    define S_IXGRP (S_IEXEC  >> 3)     /* execute, group */

#    define S_IROTH (S_IREAD  >> 6)     /* read, other */
#    define S_IWOTH (S_IWRITE >> 6)     /* write, other */
#    define S_IXOTH (S_IEXEC  >> 6)     /* execute, other */
#  endif /* !S_IRUSR */

#  define S_IRWXU   (S_IRUSR | S_IWUSR | S_IXUSR)
#  define S_IRWXG   (S_IRGRP | S_IWGRP | S_IXGRP)
#  define S_IRWXO   (S_IROTH | S_IWOTH | S_IXOTH)
#endif /* !S_IRWXU */

/* These are non-standard, but are used in builtins.c$symbolic_umask() */
#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)

#endif /* _POSIXSTAT_H_ */

https://git.savannah.gnu.org/cgit/bash.git/tree/include/filecntl.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/filecntl.h used in: Step 1112, Step 1126, Step 1175, Step 1189, Step 1202, Step 1205, Step 1207, Step 1216, Step 1225, Step 1241
/* filecntl.h - Definitions to set file descriptors to close-on-exec. */

/* Copyright (C) 1993 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#if !defined (_FILECNTL_H_)
#define _FILECNTL_H_

#include <fcntl.h>

/* Definitions to set file descriptors to close-on-exec, the Posix way. */
#if !defined (FD_CLOEXEC)
#define FD_CLOEXEC     1
#endif

#define FD_NCLOEXEC    0

#define SET_CLOSE_ON_EXEC(fd)  (fcntl ((fd), F_SETFD, FD_CLOEXEC))
#define SET_OPEN_ON_EXEC(fd)   (fcntl ((fd), F_SETFD, FD_NCLOEXEC))

/* How to open a file in non-blocking mode, the Posix.1 way. */
#if !defined (O_NONBLOCK)
#  if defined (O_NDELAY)
#    define O_NONBLOCK O_NDELAY
#  else
#    define O_NONBLOCK 0
#  endif
#endif

#endif /* ! _FILECNTL_H_ */

https://git.savannah.gnu.org/cgit/bash.git/tree/include/chartypes.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/chartypes.h used in: Step 1136, Step 1156, Step 1178, Step 1197, Step 1217, Step 1241, Step 1245
/* chartypes.h -- extend ctype.h */

/* Copyright (C) 2001 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#ifndef _SH_CHARTYPES_H
#define _SH_CHARTYPES_H

#include <ctype.h>

/* Jim Meyering writes:

   "... Some ctype macros are valid only for character codes that
   isascii says are ASCII (SGI's IRIX-4.0.5 is one such system --when
   using /bin/cc or gcc but without giving an ansi option).  So, all
   ctype uses should be through macros like ISPRINT...  If
   STDC_HEADERS is defined, then autoconf has verified that the ctype
   macros don't need to be guarded with references to isascii. ...
   Defining IN_CTYPE_DOMAIN to 1 should let any compiler worth its salt
   eliminate the && through constant folding."
   Solaris defines some of these symbols so we must undefine them first.  */

#if defined STDC_HEADERS || (!defined isascii && !defined HAVE_ISASCII)
#  define IN_CTYPE_DOMAIN(c) 1
#else
#  define IN_CTYPE_DOMAIN(c) isascii(c)
#endif

#if !defined (isspace) && !defined (HAVE_ISSPACE)
#  define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\f')
#endif

#if !defined (isprint) && !defined (HAVE_ISPRINT)
#  define isprint(c) (isalpha(c) || isdigit(c) || ispunct(c))
#endif

#if defined (isblank) || defined (HAVE_ISBLANK)
#  define ISBLANK(c) (IN_CTYPE_DOMAIN (c) && isblank (c))
#else
#  define ISBLANK(c) ((c) == ' ' || (c) == '\t')
#endif

#if defined (isgraph) || defined (HAVE_ISGRAPH)
#  define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isgraph (c))
#else
#  define ISGRAPH(c) (IN_CTYPE_DOMAIN (c) && isprint (c) && !isspace (c))
#endif

#if !defined (isxdigit) && !defined (HAVE_ISXDIGIT)
#  define isxdigit(c)   (((c) >= '0' && (c) <= '9') || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F'))
#endif

#undef ISPRINT

#define ISPRINT(c) (IN_CTYPE_DOMAIN (c) && isprint (c))
#define ISDIGIT(c) (IN_CTYPE_DOMAIN (c) && isdigit (c))
#define ISALNUM(c) (IN_CTYPE_DOMAIN (c) && isalnum (c))
#define ISALPHA(c) (IN_CTYPE_DOMAIN (c) && isalpha (c))
#define ISCNTRL(c) (IN_CTYPE_DOMAIN (c) && iscntrl (c))
#define ISLOWER(c) (IN_CTYPE_DOMAIN (c) && islower (c))
#define ISPUNCT(c) (IN_CTYPE_DOMAIN (c) && ispunct (c))
#define ISSPACE(c) (IN_CTYPE_DOMAIN (c) && isspace (c))
#define ISUPPER(c) (IN_CTYPE_DOMAIN (c) && isupper (c))
#define ISXDIGIT(c) (IN_CTYPE_DOMAIN (c) && isxdigit (c))

#define ISLETTER(c) (ISALPHA(c))

#define DIGIT(c)    ((c) >= '0' && (c) <= '9')

#define ISWORD(c)   (ISLETTER(c) || DIGIT(c) || ((c) == '_'))

#define HEXVALUE(c) \
  (((c) >= 'a' && (c) <= 'f') \
    ? (c)-'a'+10 \
    : (c) >= 'A' && (c) <= 'F' ? (c)-'A'+10 : (c)-'0')
                  
#ifndef ISOCTAL
#  define ISOCTAL(c)    ((c) >= '0' && (c) <= '7')
#endif
#define OCTVALUE(c) ((c) - '0')

#define TODIGIT(c)  ((c) - '0')
#define TOCHAR(c)   ((c) + '0')

#define TOLOWER(c)  (ISUPPER(c) ? tolower(c) : (c))
#define TOUPPER(c)  (ISLOWER(c) ? toupper(c) : (c))

#ifndef TOCTRL
   /* letter to control char -- ASCII.  The TOUPPER is in there so \ce and
      \cE will map to the same character in $'...' expansions. */
#  define TOCTRL(x) (TOUPPER(x) & 037)
#endif
#ifndef UNCTRL
   /* control char to letter -- ASCII */
#  define UNCTRL(x) (TOUPPER((x) | 0x40))
#endif

#endif /* _SH_CHARTYPES_H */

https://git.savannah.gnu.org/cgit/bash.git/tree/bashansi.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/bashansi.h used in: Step 1136, Step 1155, Step 1156, Step 1178, Step 1181, Step 1197, Step 1198, Step 1202, Step 1205, Step 1207, Step 1214, Step 1219, Step 1226, Step 1245
/* bashansi.h -- Typically included information required by picky compilers. */

/* Copyright (C) 1993 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#if !defined (_BASHANSI_H_)
#define _BASHANSI_H_

#if defined (HAVE_STRING_H)
#  if ! defined (STDC_HEADERS) && defined (HAVE_MEMORY_H)
#    include <memory.h>
#  endif
#  include <string.h>
#endif /* !HAVE_STRING_H */

#if defined (HAVE_STRINGS_H)
#  include <strings.h>
#endif /* !HAVE_STRINGS_H */

#if defined (HAVE_STDLIB_H)
#  include <stdlib.h>
#else
#  include "ansi_stdlib.h"
#endif /* !HAVE_STDLIB_H */

#endif /* !_BASHANSI_H_ */

https://git.savannah.gnu.org/cgit/bash.git/tree/shell.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/shell.h used in: Step 1136
/* shell.h -- The data structures used by the shell */

/* Copyright (C) 1993 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "bashjmp.h"

#include "command.h"
#include "syntax.h"
#include "general.h"
#include "error.h"
#include "variables.h"
#include "arrayfunc.h"
#include "quit.h"
#include "maxpath.h"
#include "unwind_prot.h"
#include "dispose_cmd.h"
#include "make_cmd.h"
#include "ocache.h"
#include "subst.h"
#include "sig.h"
#include "pathnames.h"
#include "externs.h"
#include "version.h"

extern int EOF_Reached;

#define NO_PIPE -1
#define REDIRECT_BOTH -2

#define NO_VARIABLE -1

/* Values that can be returned by execute_command (). */
#define EXECUTION_FAILURE 1
#define EXECUTION_SUCCESS 0

/* Usage messages by builtins result in a return status of 2. */
#define EX_BADUSAGE 2

/* Special exit statuses used by the shell, internally and externally. */
#define EX_BINARY_FILE  126
#define EX_NOEXEC   126
#define EX_NOINPUT  126
#define EX_NOTFOUND 127

#define EX_SHERRBASE    256 /* all special error values are > this. */

#define EX_BADSYNTAX    257 /* shell syntax error */
#define EX_USAGE    258 /* syntax error in usage */
#define EX_REDIRFAIL    259 /* redirection failed */
#define EX_BADASSIGN    260 /* variable assignment error */
#define EX_EXPFAIL  261 /* word expansion failed */

/* Flag values that control parameter pattern substitution. */
#define MATCH_ANY   0x0
#define MATCH_BEG   0x1
#define MATCH_END   0x2

#define MATCH_TYPEMASK  0x3

#define MATCH_GLOBREP   0x10
#define MATCH_QUOTED    0x20

/* Some needed external declarations. */
extern char **shell_environment;
extern WORD_LIST *rest_of_args;

/* Generalized global variables. */
extern int executing, login_shell;
extern int interactive, interactive_shell;
extern int startup_state;

/* Structure to pass around that holds a bitmap of file descriptors
   to close, and the size of that structure.  Used in execute_cmd.c. */
struct fd_bitmap {
  int size;
  char *bitmap;
};

#define FD_BITMAP_SIZE 32

#define CTLESC '\001'
#define CTLNUL '\177'

/* Information about the current user. */
struct user_info {
  uid_t uid, euid;
  gid_t gid, egid;
  char *user_name;
  char *shell;      /* shell from the password file */
  char *home_dir;
};

extern struct user_info current_user;

/* Force gcc to not clobber X on a longjmp().  Old versions of gcc mangle
   this badly. */
#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 8)
#  define USE_VAR(x)    ((void) &(x))
#else
#  define USE_VAR(x)
#endif

https://git.savannah.gnu.org/cgit/bash.git/tree/include/stdc.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/stdc.h used in: Step 1136, Step 1155, Step 1156, Step 1178, Step 1196, Step 1197, Step 1198, Step 1230, Step 1248
/* stdc.h -- macros to make source compile on both ANSI C and K&R C
   compilers. */

/* Copyright (C) 1993 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#if !defined (_STDC_H_)
#define _STDC_H_

/* Adapted from BSD /usr/include/sys/cdefs.h. */

/* A function can be defined using prototypes and compile on both ANSI C
   and traditional C compilers with something like this:
    extern char *func __P((char *, char *, int)); */

#if !defined (__P)
#  if defined (__STDC__) || defined (__GNUC__) || defined (__cplusplus) || defined (PROTOTYPES)
#    define __P(protos) protos
#  else 
#    define __P(protos) ()
#  endif
#endif

#if defined (HAVE_STRINGIZE)
#  define __STRING(x) #x
#else
#  define __STRING(x) "x"
#endif

#if !defined (__STDC__)

#if defined (__GNUC__)      /* gcc with -traditional */
#  if !defined (signed)
#    define signed __signed
#  endif
#  if !defined (volatile)
#    define volatile __volatile
#  endif
#else /* !__GNUC__ */
#  if !defined (inline)
#    define inline
#  endif
#  if !defined (signed)
#    define signed
#  endif
#  if !defined (volatile)
#    define volatile
#  endif
#endif /* !__GNUC__ */

#endif /* !__STDC__ */

#ifndef __attribute__
#  if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__
#    define __attribute__(x)
#  endif
#endif

/* For those situations when gcc handles inlining a particular function but
   other compilers complain. */
#ifdef __GNUC__
#  define INLINE inline
#else
#  define INLINE
#endif

#if defined (PREFER_STDARG)
#  define SH_VA_START(va, arg)  va_start(va, arg)
#else
#  define SH_VA_START(va, arg)  va_start(va)
#endif

#endif /* !_STDC_H_ */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/bashgetopt.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/bashgetopt.h used in: Step 1136
/* bashgetopt.h -- extern declarations for stuff defined in bashgetopt.c. */

/* Copyright (C) 1993 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

/* See getopt.h for the explanation of these variables. */

#if !defined (__BASH_GETOPT_H)
#  define __BASH_GETOPT_H

#include <stdc.h>

extern char *list_optarg;

extern int list_optopt;
extern int list_opttype;

extern WORD_LIST *lcurrent;
extern WORD_LIST *loptend;

extern int internal_getopt __P((WORD_LIST *, char *));
extern void reset_internal_getopt __P((void));

#endif /* !__BASH_GETOPT_H */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/common.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/common.h used in: Step 1136
/* common.h -- extern declarations for functions defined in common.c. */

/* Copyright (C) 1993-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#if  !defined (__COMMON_H)
#  define __COMMON_H

#include "stdc.h"

#define ISOPTION(s, c)  (s[0] == '-' && !s[2] && s[1] == c)

/* Flag values for parse_and_execute () */
#define SEVAL_NONINT    0x001
#define SEVAL_INTERACT  0x002
#define SEVAL_NOHIST    0x004
#define SEVAL_NOFREE    0x008

/* Flags for describe_command, shared between type.def and command.def */
#define CDESC_ALL       0x001   /* type -a */
#define CDESC_SHORTDESC     0x002   /* command -V */
#define CDESC_REUSABLE      0x004   /* command -v */
#define CDESC_TYPE      0x008   /* type -t */
#define CDESC_PATH_ONLY     0x010   /* type -p */
#define CDESC_FORCE_PATH    0x020   /* type -ap or type -P */
#define CDESC_NOFUNCS       0x040   /* type -f */

/* Flags for get_job_by_name */
#define JM_PREFIX       0x01    /* prefix of job name */
#define JM_SUBSTRING        0x02    /* substring of job name */
#define JM_EXACT        0x04    /* match job name exactly */
#define JM_STOPPED      0x08    /* match stopped jobs only */
#define JM_FIRSTMATCH       0x10    /* return first matching job */

/* Flags for remember_args and value of changed_dollar_vars */
#define ARGS_NONE       0x0
#define ARGS_INVOC      0x01
#define ARGS_FUNC       0x02
#define ARGS_SETBLTIN       0x04

/* Functions from common.c */
extern void builtin_error __P((const char *, ...))  __attribute__((__format__ (printf, 1, 2)));
extern void builtin_usage __P((void));
extern void no_args __P((WORD_LIST *));
extern int no_options __P((WORD_LIST *));

/* common error message functions */
extern void sh_needarg __P((char *));
extern void sh_neednumarg __P((char *));
extern void sh_notfound __P((char *));
extern void sh_invalidopt __P((char *));
extern void sh_invalidoptname __P((char *));
extern void sh_invalidid __P((char *));
extern void sh_invalidnum __P((char *));
extern void sh_invalidsig __P((char *));
extern void sh_erange __P((char *, char *));
extern void sh_badpid __P((char *));
extern void sh_badjob __P((char *));
extern void sh_readonly __P((const char *));
extern void sh_nojobs __P((char *));
extern void sh_restricted __P((char *));

extern char **make_builtin_argv __P((WORD_LIST *, int *));
extern void remember_args __P((WORD_LIST *, int));

extern int dollar_vars_changed __P((void));
extern void set_dollar_vars_unchanged __P((void));
extern void set_dollar_vars_changed __P((void));

extern intmax_t get_numeric_arg __P((WORD_LIST *, int));
extern int get_exitstat __P((WORD_LIST *));
extern int read_octal __P((char *));

/* Keeps track of the current working directory. */
extern char *the_current_working_directory;
extern char *get_working_directory __P((char *));
extern void set_working_directory __P((char *));

#if defined (JOB_CONTROL)
extern int get_job_by_name __P((const char *, int));
extern int get_job_spec __P((WORD_LIST *));
#endif
extern int display_signal_list __P((WORD_LIST *, int));

/* It's OK to declare a function as returning a Function * without
   providing a definition of what a `Function' is. */
extern struct builtin *builtin_address_internal __P((char *, int));
extern sh_builtin_func_t *find_shell_builtin __P((char *));
extern sh_builtin_func_t *builtin_address __P((char *));
extern sh_builtin_func_t *find_special_builtin __P((char *));
extern void initialize_shell_builtins __P((void));

/* Functions from getopts.def */
extern void getopts_reset __P((int));

/* Functions from set.def */
extern int minus_o_option_value __P((char *));
extern void list_minus_o_opts __P((int, int));
extern char **get_minus_o_opts __P((void));
extern int set_minus_o_option __P((int, char *));

extern void set_shellopts __P((void));
extern void parse_shellopts __P((char *));
extern void initialize_shell_options __P((int));

extern void reset_shell_options __P((void));

/* Functions from shopt.def */
extern void reset_shopt_options __P((void));
extern char **get_shopt_options __P((void));

extern int shopt_setopt __P((char *, int));
extern int shopt_listopt __P((char *, int));

extern int set_login_shell __P((int));

/* Functions from type.def */
extern int describe_command __P((char *, int));

/* Functions from setattr.def */
extern int set_or_show_attributes __P((WORD_LIST *, int, int));
extern int show_var_attributes __P((SHELL_VAR *, int, int));
extern int show_name_attributes __P((char *, int));
extern void set_var_attribute __P((char *, int, int));

/* Functions from pushd.def */
extern char *get_dirstack_from_string __P((char *));
extern char *get_dirstack_element __P((intmax_t, int));
extern void set_dirstack_element __P((intmax_t, int, char *));
extern WORD_LIST *get_directory_stack __P((void));

/* Functions from evalstring.c */
extern int parse_and_execute __P((char *, const char *, int));
extern void parse_and_execute_cleanup __P((void));

/* Functions from evalfile.c */
extern int maybe_execute_file __P((const char *, int));
extern int source_file __P((const char *));
extern int fc_execute_file __P((const char *));

#endif /* !__COMMON_H */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/common.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/common.c used in: Step 1138
/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>
#include <chartypes.h>
#include "../bashtypes.h"
#include "posixstat.h"
#include <signal.h>

#include <errno.h>

#if defined (PREFER_STDARG)
#  include <stdarg.h>
#else
#  include <varargs.h>
#endif

#include "../bashansi.h"

#include "../shell.h"
#include "maxpath.h"
#include "../flags.h"
#include "../jobs.h"
#include "../builtins.h"
#include "../input.h"
#include "../execute_cmd.h"
#include "../trap.h"
#include "bashgetopt.h"
#include "common.h"
#include "builtext.h"
#include <tilde/tilde.h>

#if defined (HISTORY)
#  include "../bashhist.h"
#endif

#if !defined (errno)
extern int errno;   
#endif /* !errno */

extern int indirection_level, subshell_environment;
extern int line_number;
extern int last_command_exit_value;
extern int running_trap;
extern int posixly_correct;
extern char *this_command_name, *shell_name;
extern char *bash_getcwd_errstr;

/* Used by some builtins and the mainline code. */
sh_builtin_func_t *last_shell_builtin = (sh_builtin_func_t *)NULL;
sh_builtin_func_t *this_shell_builtin = (sh_builtin_func_t *)NULL;

/* **************************************************************** */
/*                                  */
/*       Error reporting, usage, and option processing      */
/*                                  */
/* **************************************************************** */

/* This is a lot like report_error (), but it is for shell builtins
   instead of shell control structures, and it won't ever exit the
   shell. */
void
#if defined (PREFER_STDARG)
builtin_error (const char *format, ...)
#else
builtin_error (format, va_alist)
     const char *format;
     va_dcl
#endif
{
  va_list args;
  char *name;

  name = get_name_for_error ();
  fprintf (stderr, "%s: ", name);

  if (interactive_shell == 0)
    fprintf (stderr, "line %d: ", executing_line_number ());

  if (this_command_name && *this_command_name)
    fprintf (stderr, "%s: ", this_command_name);

  SH_VA_START (args, format);

  vfprintf (stderr, format, args);
  va_end (args);
  fprintf (stderr, "\n");
}

/* Print a usage summary for the currently-executing builtin command. */
void
builtin_usage ()
{
  if (this_command_name && *this_command_name)
    fprintf (stderr, "%s: usage: ", this_command_name);
  fprintf (stderr, "%s\n", current_builtin->short_doc);
  fflush (stderr);
}

/* Return if LIST is NULL else barf and jump to top_level.  Used by some
   builtins that do not accept arguments. */
void
no_args (list)
     WORD_LIST *list;
{
  if (list)
    {
      builtin_error ("too many arguments");
      jump_to_top_level (DISCARD);
    }
}

/* Check that no options were given to the currently-executing builtin,
   and return 0 if there were options. */
int
no_options (list)
     WORD_LIST *list;
{
  reset_internal_getopt ();
  if (internal_getopt (list, "") != -1)
    {
      builtin_usage ();
      return (1);
    }
  return (0);
}

void
sh_needarg (s)
     char *s;
{
  builtin_error ("%s: option requires an argument", s);
}

void
sh_neednumarg (s)
     char *s;
{
  builtin_error ("%s: numeric argument required", s);
}

void
sh_notfound (s)
     char *s;
{
  builtin_error ("%s: not found", s);
}

/* Function called when one of the builtin commands detects an invalid
   option. */
void
sh_invalidopt (s)
     char *s;
{
  builtin_error ("%s: invalid option", s);
}

void
sh_invalidoptname (s)
     char *s;
{
  builtin_error ("%s: invalid option name", s);
}

void
sh_invalidid (s)
     char *s;
{
  builtin_error ("`%s': not a valid identifier", s);
}

void
sh_invalidnum (s)
     char *s;
{
  builtin_error ("%s: invalid number", s);
}

void
sh_invalidsig (s)
     char *s;
{
  builtin_error ("%s: invalid signal specification", s);
}

void
sh_badpid (s)
     char *s;
{
  builtin_error ("`%s': not a pid or valid job spec", s);
}

void
sh_readonly (s)
     const char *s;
{
  builtin_error ("%s: readonly variable", s);
}

void
sh_erange (s, desc)
     char *s, *desc;
{
  if (s)
    builtin_error ("%s: %s out of range", s, desc ? desc : "argument");
  else
    builtin_error ("%s out of range", desc ? desc : "argument");
}

#if defined (JOB_CONTROL)
void
sh_badjob (s)
     char *s;
{
  builtin_error ("%s: no such job", s);
}

void
sh_nojobs (s)
     char *s;
{
  if (s)
    builtin_error ("%s: no job control");
  else
    builtin_error ("no job control");
}
#endif

#if defined (RESTRICTED_SHELL)
void
sh_restricted (s)
     char *s;
{
  if (s)
    builtin_error ("%s: restricted", s);
  else
    builtin_error ("restricted");
}
#endif

/* **************************************************************** */
/*                                  */
/*       Shell positional parameter manipulation            */
/*                                  */
/* **************************************************************** */

/* Convert a WORD_LIST into a C-style argv.  Return the number of elements
   in the list in *IP, if IP is non-null.  A convenience function for
   loadable builtins; also used by `test'. */
char **
make_builtin_argv (list, ip)
     WORD_LIST *list;
     int *ip;
{
  char **argv;

  argv = strvec_from_word_list (list, 0, 1, ip);
  argv[0] = this_command_name;
  return argv;
}

/* Remember LIST in $0 ... $9, and REST_OF_ARGS.  If DESTRUCTIVE is
   non-zero, then discard whatever the existing arguments are, else
   only discard the ones that are to be replaced. */
void
remember_args (list, destructive)
     WORD_LIST *list;
     int destructive;
{
  register int i;

  for (i = 1; i < 10; i++)
    {
      if ((destructive || list) && dollar_vars[i])
    {
      free (dollar_vars[i]);
      dollar_vars[i] = (char *)NULL;
    }

      if (list)
    {
      dollar_vars[i] = savestring (list->word->word);
      list = list->next;
    }
    }

  /* If arguments remain, assign them to REST_OF_ARGS.
     Note that copy_word_list (NULL) returns NULL, and
     that dispose_words (NULL) does nothing. */
  if (destructive || list)
    {
      dispose_words (rest_of_args);
      rest_of_args = copy_word_list (list);
    }

  if (destructive)
    set_dollar_vars_changed ();
}

static int changed_dollar_vars;

/* Have the dollar variables been reset to new values since we last
   checked? */
int
dollar_vars_changed ()
{
  return (changed_dollar_vars);
}

void
set_dollar_vars_unchanged ()
{
  changed_dollar_vars = 0;
}

void
set_dollar_vars_changed ()
{
  if (variable_context)
    changed_dollar_vars |= ARGS_FUNC;
  else if (this_shell_builtin == set_builtin)
    changed_dollar_vars |= ARGS_SETBLTIN;
  else
    changed_dollar_vars |= ARGS_INVOC;
}

/* **************************************************************** */
/*                                  */
/*      Validating numeric input and arguments          */
/*                                  */
/* **************************************************************** */

/* Read a numeric arg for this_command_name, the name of the shell builtin
   that wants it.  LIST is the word list that the arg is to come from.
   Accept only the numeric argument; report an error if other arguments
   follow.  If FATAL is true, call throw_to_top_level, which exits the
   shell; if not, call jump_to_top_level (DISCARD), which aborts the
   current command. */
intmax_t
get_numeric_arg (list, fatal)
     WORD_LIST *list;
     int fatal;
{
  intmax_t count = 1;

  if (list && list->word && ISOPTION (list->word->word, '-'))
    list = list->next;

  if (list)
    {
      register char *arg;

      arg = list->word->word;
      if (arg == 0 || (legal_number (arg, &count) == 0))
    {
      sh_neednumarg (list->word->word);
      if (fatal)
        throw_to_top_level ();
      else
        jump_to_top_level (DISCARD);
    }
      no_args (list->next);
    }

  return (count);
}

/* Get an eight-bit status value from LIST */
int
get_exitstat (list)
     WORD_LIST *list;
{
  int status;
  intmax_t sval;
  char *arg;

  if (list && list->word && ISOPTION (list->word->word, '-'))
    list = list->next;

  if (list == 0)
    return (last_command_exit_value);      

  arg = list->word->word;
  if (arg == 0 || legal_number (arg, &sval) == 0)
    {
      sh_neednumarg (list->word->word ? list->word->word : "`'");
      return 255;
    }
  no_args (list->next);

  status = sval & 255;
  return status;
}

/* Return the octal number parsed from STRING, or -1 to indicate
   that the string contained a bad number. */
int
read_octal (string)
     char *string;
{
  int result, digits;

  result = digits = 0;
  while (*string && ISOCTAL (*string))
    {
      digits++;
      result = (result * 8) + (*string++ - '0');
      if (result > 0777)
    return -1;
    }

  if (digits == 0 || *string)
    result = -1;

  return (result);
}

/* **************************************************************** */
/*                                  */
/*       Manipulating the current working directory         */
/*                                  */
/* **************************************************************** */

/* Return a consed string which is the current working directory.
   FOR_WHOM is the name of the caller for error printing.  */
char *the_current_working_directory = (char *)NULL;

char *
get_working_directory (for_whom)
     char *for_whom;
{
  char *directory;

  if (no_symbolic_links)
    {
      if (the_current_working_directory)
    free (the_current_working_directory);

      the_current_working_directory = (char *)NULL;
    }

  if (the_current_working_directory == 0)
    {
      the_current_working_directory = (char *)xmalloc (PATH_MAX);
      the_current_working_directory[0] = '\0';
      directory = getcwd (the_current_working_directory, PATH_MAX);
      if (directory == 0)
    {
      fprintf (stderr, "%s: could not get current directory: %s: %s\n",
           (for_whom && *for_whom) ? for_whom : get_name_for_error (),
           bash_getcwd_errstr, strerror (errno));

      free (the_current_working_directory);
      the_current_working_directory = (char *)NULL;
      return (char *)NULL;
    }
    }

  return (savestring (the_current_working_directory));
}

/* Make NAME our internal idea of the current working directory. */
void
set_working_directory (name)
     char *name;
{
  FREE (the_current_working_directory);
  the_current_working_directory = savestring (name);
}

/* **************************************************************** */
/*                                  */
/*          Job control support functions               */
/*                                  */
/* **************************************************************** */

#if defined (JOB_CONTROL)
int
get_job_by_name (name, flags)
     const char *name;
     int flags;
{
  register int i, wl, cl, match, job;
  register PROCESS *p;

  job = NO_JOB;
  wl = strlen (name);
  for (i = job_slots - 1; i >= 0; i--)
    {
      if (jobs[i] == 0 || ((flags & JM_STOPPED) && JOBSTATE(i) != JSTOPPED))
        continue;

      p = jobs[i]->pipe;
      do
        {
      if (flags & JM_EXACT)
        {
          cl = strlen (p->command);
          match = STREQN (p->command, name, cl);
        }
      else if (flags & JM_SUBSTRING)
        match = strindex (p->command, name) != (char *)0;
      else
        match = STREQN (p->command, name, wl);

      if (match == 0)
        {
          p = p->next;
          continue;
        }
      else if (flags & JM_FIRSTMATCH)
        return i;       /* return first match */
      else if (job != NO_JOB)
        {
          if (this_shell_builtin)
            builtin_error ("%s: ambiguous job spec", name);
          else
            report_error ("%s: ambiguous job spec", name);
          return (DUP_JOB);
        }
      else
        job = i;
        }
      while (p != jobs[i]->pipe);
    }

  return (job);
}

/* Return the job spec found in LIST. */
int
get_job_spec (list)
     WORD_LIST *list;
{
  register char *word;
  int job, jflags;

  if (list == 0)
    return (current_job);

  word = list->word->word;

  if (*word == '\0')
    return (current_job);

  if (*word == '%')
    word++;

  if (DIGIT (*word) && all_digits (word))
    {
      job = atoi (word);
#if 0
      return (job >= job_slots ? NO_JOB : job - 1);
#else
      return (job > job_slots ? NO_JOB : job - 1);
#endif
    }

  jflags = 0;
  switch (*word)
    {
    case 0:
    case '%':
    case '+':
      return (current_job);

    case '-':
      return (previous_job);

    case '?':           /* Substring search requested. */
      jflags |= JM_SUBSTRING;
      word++;
      /* FALLTHROUGH */

    default:
      return get_job_by_name (word, jflags);
    }
}
#endif /* JOB_CONTROL */

int
display_signal_list (list, forcecols)
     WORD_LIST *list;
     int forcecols;
{
  register int i, column;
  char *name;
  int result;
  int signum;
  intmax_t lsignum;

  result = EXECUTION_SUCCESS;
  if (!list)
    {
      for (i = 1, column = 0; i < NSIG; i++)
    {
      name = signal_name (i);
      if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7))
        continue;

      if (posixly_correct && !forcecols)
        printf ("%s%s", name, (i == NSIG - 1) ? "" : " ");
      else
        {
          printf ("%2d) %s", i, name);

          if (++column < 4)
        printf ("\t");
          else
        {
          printf ("\n");
          column = 0;
        }
        }
    }

      if ((posixly_correct && !forcecols) || column != 0)
    printf ("\n");
      return result;
    }

  /* List individual signal names or numbers. */
  while (list)
    {
      if (legal_number (list->word->word, &lsignum))
    {
      /* This is specified by Posix.2 so that exit statuses can be
         mapped into signal numbers. */
      if (lsignum > 128)
        lsignum -= 128;
      if (lsignum < 0 || lsignum >= NSIG)
        {
          sh_invalidsig (list->word->word);
          result = EXECUTION_FAILURE;
          list = list->next;
          continue;
        }

      signum = lsignum;
      name = signal_name (signum);
      if (STREQN (name, "SIGJUNK", 7) || STREQN (name, "Unknown", 7))
        {
          list = list->next;
          continue;
        }
#if defined (JOB_CONTROL)
      /* POSIX.2 says that `kill -l signum' prints the signal name without
         the `SIG' prefix. */
      printf ("%s\n", (this_shell_builtin == kill_builtin) ? name + 3 : name);
#else
      printf ("%s\n", name);
#endif
    }
      else
    {
      signum = decode_signal (list->word->word);
      if (signum == NO_SIG)
        {
          sh_invalidsig (list->word->word);
          result = EXECUTION_FAILURE;
          list = list->next;
          continue;
        }
      printf ("%d\n", signum);
    }
      list = list->next;
    }
  return (result);
}

/* **************************************************************** */
/*                                  */
/*      Finding builtin commands and their functions        */
/*                                  */
/* **************************************************************** */

/* Perform a binary search and return the address of the builtin function
   whose name is NAME.  If the function couldn't be found, or the builtin
   is disabled or has no function associated with it, return NULL.
   Return the address of the builtin.
   DISABLED_OKAY means find it even if the builtin is disabled. */
struct builtin *
builtin_address_internal (name, disabled_okay)
     char *name;
     int disabled_okay;
{
  int hi, lo, mid, j;

  hi = num_shell_builtins - 1;
  lo = 0;

  while (lo <= hi)
    {
      mid = (lo + hi) / 2;

      j = shell_builtins[mid].name[0] - name[0];

      if (j == 0)
    j = strcmp (shell_builtins[mid].name, name);

      if (j == 0)
    {
      /* It must have a function pointer.  It must be enabled, or we
         must have explicitly allowed disabled functions to be found,
         and it must not have been deleted. */
      if (shell_builtins[mid].function &&
          ((shell_builtins[mid].flags & BUILTIN_DELETED) == 0) &&
          ((shell_builtins[mid].flags & BUILTIN_ENABLED) || disabled_okay))
        return (&shell_builtins[mid]);
      else
        return ((struct builtin *)NULL);
    }
      if (j > 0)
    hi = mid - 1;
      else
    lo = mid + 1;
    }
  return ((struct builtin *)NULL);
}

/* Return the pointer to the function implementing builtin command NAME. */
sh_builtin_func_t *
find_shell_builtin (name)
     char *name;
{
  current_builtin = builtin_address_internal (name, 0);
  return (current_builtin ? current_builtin->function : (sh_builtin_func_t *)NULL);
}

/* Return the address of builtin with NAME, whether it is enabled or not. */
sh_builtin_func_t *
builtin_address (name)
     char *name;
{
  current_builtin = builtin_address_internal (name, 1);
  return (current_builtin ? current_builtin->function : (sh_builtin_func_t *)NULL);
}

/* Return the function implementing the builtin NAME, but only if it is a
   POSIX.2 special builtin. */
sh_builtin_func_t *
find_special_builtin (name)
     char *name;
{
  current_builtin = builtin_address_internal (name, 0);
  return ((current_builtin && (current_builtin->flags & SPECIAL_BUILTIN)) ?
            current_builtin->function :
            (sh_builtin_func_t *)NULL);
}
  
static int
shell_builtin_compare (sbp1, sbp2)
     struct builtin *sbp1, *sbp2;
{
  int result;

  if ((result = sbp1->name[0] - sbp2->name[0]) == 0)
    result = strcmp (sbp1->name, sbp2->name);

  return (result);
}

/* Sort the table of shell builtins so that the binary search will work
   in find_shell_builtin. */
void
initialize_shell_builtins ()
{
  qsort (shell_builtins, num_shell_builtins, sizeof (struct builtin),
    (QSFUNC *)shell_builtin_compare);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/evalstring.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/evalstring.c used in: Step 1140
/* Copyright (C) 1996 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>
#include <signal.h>

#include <errno.h>

#include "filecntl.h"
#include "../bashansi.h"

#include "../shell.h"
#include "../jobs.h"
#include "../builtins.h"
#include "../flags.h"
#include "../input.h"
#include "../execute_cmd.h"
#include "../redir.h"
#include "../trap.h"

#if defined (HISTORY)
#  include "../bashhist.h"
#endif

#include "common.h"

#if !defined (errno)
extern int errno;
#endif

#define IS_BUILTIN(s)   (builtin_address_internal(s, 0) != (struct builtin *)NULL)

extern int indirection_level, startup_state, subshell_environment;
extern int line_number;
extern int last_command_exit_value;
extern int running_trap;
extern int posixly_correct;

int parse_and_execute_level = 0;

static int cat_file __P((REDIRECT *));

/* How to force parse_and_execute () to clean up after itself. */
void
parse_and_execute_cleanup ()
{
  if (running_trap)
    {
      run_trap_cleanup (running_trap - 1);
      unfreeze_jobs_list ();
    }
  run_unwind_frame ("parse_and_execute_top");
}

/* Parse and execute the commands in STRING.  Returns whatever
   execute_command () returns.  This frees STRING.  FLAGS is a
   flags word; look in common.h for the possible values.  Actions
   are:
    (flags & SEVAL_NONINT) -> interactive = 0;
    (flags & SEVAL_INTERACT) -> interactive = 1;
    (flags & SEVAL_NOHIST) -> call bash_history_disable ()
    (flags & SEVAL_NOFREE) -> don't free STRING when finished
*/

int
parse_and_execute (string, from_file, flags)
     char *string;
     const char *from_file;
     int flags;
{
  int code, x;
  volatile int should_jump_to_top_level, last_result;
  char *orig_string;
  COMMAND *volatile command;

  orig_string = string;
  /* Unwind protect this invocation of parse_and_execute (). */
  begin_unwind_frame ("parse_and_execute_top");
  unwind_protect_int (parse_and_execute_level);
  unwind_protect_jmp_buf (top_level);
  unwind_protect_int (indirection_level);
  unwind_protect_int (line_number);
  if (flags & (SEVAL_NONINT|SEVAL_INTERACT))
    unwind_protect_int (interactive);

#if defined (HISTORY)
  unwind_protect_int (remember_on_history); /* can be used in scripts */
#  if defined (BANG_HISTORY)
  if (interactive_shell)
    {
      unwind_protect_int (history_expansion_inhibited);
    }
#  endif /* BANG_HISTORY */
#endif /* HISTORY */

  if (interactive_shell)
    {
      x = get_current_prompt_level ();
      add_unwind_protect (set_current_prompt_level, x);
    }
  
  add_unwind_protect (pop_stream, (char *)NULL);
  if (orig_string && ((flags & SEVAL_NOFREE) == 0))
    add_unwind_protect (xfree, orig_string);
  end_unwind_frame ();

  parse_and_execute_level++;
  push_stream (1);  /* reset the line number */
  indirection_level++;
  if (flags & (SEVAL_NONINT|SEVAL_INTERACT))
    interactive = (flags & SEVAL_NONINT) ? 0 : 1;

#if defined (HISTORY)
  if (flags & SEVAL_NOHIST)
    bash_history_disable ();
#endif /* HISTORY */

  code = should_jump_to_top_level = 0;
  last_result = EXECUTION_SUCCESS;
  command = (COMMAND *)NULL;

  with_input_from_string (string, from_file);
  while (*(bash_input.location.string))
    {
      if (interrupt_state)
    {
      last_result = EXECUTION_FAILURE;
      break;
    }

      /* Provide a location for functions which `longjmp (top_level)' to
     jump to.  This prevents errors in substitution from restarting
     the reader loop directly, for example. */
      code = setjmp (top_level);

      if (code)
    {
      should_jump_to_top_level = 0;
      switch (code)
        {
        case FORCE_EOF:
        case EXITPROG:
          run_unwind_frame ("pe_dispose");
          /* Remember to call longjmp (top_level) after the old
         value for it is restored. */
          should_jump_to_top_level = 1;
          goto out;

        case DISCARD:
          run_unwind_frame ("pe_dispose");
          last_result = last_command_exit_value = EXECUTION_FAILURE; /* XXX */
          if (subshell_environment)
        {
          should_jump_to_top_level = 1;
          goto out;
        }
          else
        {
#if 0
          dispose_command (command);    /* pe_dispose does this */
#endif
          continue;
        }

        default:
          command_error ("parse_and_execute", CMDERR_BADJUMP, code, 0);
          break;
        }
    }
      
      if (parse_command () == 0)
    {
      if (interactive_shell == 0 && read_but_dont_execute)
        {
          last_result = EXECUTION_SUCCESS;
          dispose_command (global_command);
          global_command = (COMMAND *)NULL;
        }
      else if (command = global_command)
        {
          struct fd_bitmap *bitmap;

          bitmap = new_fd_bitmap (FD_BITMAP_SIZE);
          begin_unwind_frame ("pe_dispose");
          add_unwind_protect (dispose_fd_bitmap, bitmap);
          add_unwind_protect (dispose_command, command);    /* XXX */

          global_command = (COMMAND *)NULL;

#if defined (ONESHOT)
          /*
           * IF
           *   we were invoked as `bash -c' (startup_state == 2) AND
           *   parse_and_execute has not been called recursively AND
           *   we have parsed the full command (string == '\0') AND
           *   we have a simple command without redirections AND
           *   the command is not being timed
           * THEN
           *   tell the execution code that we don't need to fork
           */
          if (startup_state == 2 && parse_and_execute_level == 1 &&
          *bash_input.location.string == '\0' &&
          command->type == cm_simple &&
          !command->redirects && !command->value.Simple->redirects &&
          ((command->flags & CMD_TIME_PIPELINE) == 0))
        {
          command->flags |= CMD_NO_FORK;
          command->value.Simple->flags |= CMD_NO_FORK;
        }
#endif /* ONESHOT */

          /* See if this is a candidate for $( <file ). */
          if (startup_state == 2 &&
          (subshell_environment & SUBSHELL_COMSUB) &&
          *bash_input.location.string == '\0' &&
          command->type == cm_simple && !command->redirects &&
          (command->flags & CMD_TIME_PIPELINE) == 0 &&
          command->value.Simple->words == 0 &&
          command->value.Simple->redirects &&
          command->value.Simple->redirects->next == 0 &&
          command->value.Simple->redirects->instruction == r_input_direction)
        {
          int r;
          r = cat_file (command->value.Simple->redirects);
          last_result = (r < 0) ? EXECUTION_FAILURE : EXECUTION_SUCCESS;
        }
          else
        last_result = execute_command_internal
                (command, 0, NO_PIPE, NO_PIPE, bitmap);

          dispose_command (command);
          dispose_fd_bitmap (bitmap);
          discard_unwind_frame ("pe_dispose");
        }
    }
      else
    {
      last_result = EXECUTION_FAILURE;

      /* Since we are shell compatible, syntax errors in a script
         abort the execution of the script.  Right? */
      break;
    }
    }

 out:

  run_unwind_frame ("parse_and_execute_top");

  if (interrupt_state && parse_and_execute_level == 0)
    {
      /* An interrupt during non-interactive execution in an
     interactive shell (e.g. via $PROMPT_COMMAND) should
     not cause the shell to exit. */
      interactive = interactive_shell;
      throw_to_top_level ();
    }

  if (should_jump_to_top_level)
    jump_to_top_level (code);

  return (last_result);
}

/* Handle a $( < file ) command substitution.  This expands the filename,
   returning errors as appropriate, then just cats the file to the standard
   output. */
static int
cat_file (r)
     REDIRECT *r;
{
  char lbuf[128], *fn;
  int fd, rval;
  ssize_t nr;

  if (r->instruction != r_input_direction)
    return -1;

  /* Get the filename. */
  if (posixly_correct && !interactive_shell)
    disallow_filename_globbing++;
  fn = redirection_expand (r->redirectee.filename);
  if (posixly_correct && !interactive_shell)
    disallow_filename_globbing--;

  if (fn == 0)
    {
      redirection_error (r, AMBIGUOUS_REDIRECT);
      return -1;
    }

  fd = open(fn, O_RDONLY);
  if (fd < 0)
    {
      file_error (fn);
      free (fn);
      return -1;
    }

  rval = zcatfd (fd, 1, fn);

  free (fn);
  close (fd);

  return (rval);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/evalfile.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/evalfile.c used in: Step 1142
/* Copyright (C) 1996 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "../bashtypes.h"
#include "posixstat.h"
#include "filecntl.h"

#include <stdio.h>
#include <signal.h>
#include <errno.h>

#include "../bashansi.h"

#include "../shell.h"
#include "../jobs.h"
#include "../builtins.h"
#include "../flags.h"
#include "../input.h"
#include "../execute_cmd.h"

#if defined (HISTORY)
#  include "../bashhist.h"
#endif

#include "common.h"

#if !defined (errno)
extern int errno;
#endif

/* Flags for _evalfile() */
#define FEVAL_ENOENTOK      0x001
#define FEVAL_BUILTIN       0x002
#define FEVAL_UNWINDPROT    0x004
#define FEVAL_NONINT        0x008
#define FEVAL_LONGJMP       0x010
#define FEVAL_HISTORY       0x020
#define FEVAL_CHECKBINARY   0x040
#define FEVAL_REGFILE       0x080

extern int posixly_correct;
extern int indirection_level, startup_state, subshell_environment;
extern int return_catch_flag, return_catch_value;
extern int last_command_exit_value;

/* How many `levels' of sourced files we have. */
int sourcelevel = 0;

static int
_evalfile (filename, flags)
     const char *filename;
     int flags;
{
  volatile int old_interactive;
  procenv_t old_return_catch;
  int return_val, fd, result, pflags;
  char *string;
  struct stat finfo;
  size_t file_size;
  sh_vmsg_func_t *errfunc;

  USE_VAR(pflags);

  fd = open (filename, O_RDONLY);

  if (fd < 0 || (fstat (fd, &finfo) == -1))
    {
file_error_and_exit:
      if (((flags & FEVAL_ENOENTOK) == 0) || errno != ENOENT)
    file_error (filename);

      if (flags & FEVAL_LONGJMP)
    {
      last_command_exit_value = 1;
      jump_to_top_level (EXITPROG);
    }

      return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE
                          : ((errno == ENOENT) ? 0 : -1));
    }

  errfunc = ((flags & FEVAL_BUILTIN) ? builtin_error : internal_error);

  if (S_ISDIR (finfo.st_mode))
    {
      (*errfunc) ("%s: is a directory", filename);
      return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1);
    }
  else if ((flags & FEVAL_REGFILE) && S_ISREG (finfo.st_mode) == 0)
    {
      (*errfunc) ("%s: not a regular file", filename);
      return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1);
    }

  file_size = (size_t)finfo.st_size;
  /* Check for overflow with large files. */
  if (file_size != finfo.st_size || file_size + 1 < file_size)
    {
      (*errfunc) ("%s: file is too large", filename);
      return ((flags & FEVAL_BUILTIN) ? EXECUTION_FAILURE : -1);
    }      

#if defined (__CYGWIN__) && defined (O_TEXT)
  setmode (fd, O_TEXT);
#endif

  string = (char *)xmalloc (1 + file_size);
  result = read (fd, string, file_size);
  string[result] = '\0';

  return_val = errno;
  close (fd);
  errno = return_val;

  if (result < 0)       /* XXX was != file_size, not < 0 */
    {
      free (string);
      goto file_error_and_exit;
    }

  if (result == 0)
    {
      free (string);
      return ((flags & FEVAL_BUILTIN) ? EXECUTION_SUCCESS : 1);
    }
      
  if ((flags & FEVAL_CHECKBINARY) && 
      check_binary_file (string, (result > 80) ? 80 : result))
    {
      free (string);
      (*errfunc) ("%s: cannot execute binary file", filename);
      return ((flags & FEVAL_BUILTIN) ? EX_BINARY_FILE : -1);
    }

  if (flags & FEVAL_UNWINDPROT)
    {
      begin_unwind_frame ("_evalfile");

      unwind_protect_int (return_catch_flag);
      unwind_protect_jmp_buf (return_catch);
      if (flags & FEVAL_NONINT)
    unwind_protect_int (interactive);
      unwind_protect_int (sourcelevel);
    }
  else
    {
      COPY_PROCENV (return_catch, old_return_catch);
      if (flags & FEVAL_NONINT)
    old_interactive = interactive;
    }

  if (flags & FEVAL_NONINT)
    interactive = 0;

  return_catch_flag++;
  sourcelevel++;

  /* set the flags to be passed to parse_and_execute */
  pflags = (flags & FEVAL_HISTORY) ? 0 : SEVAL_NOHIST;

  if (flags & FEVAL_BUILTIN)
    result = EXECUTION_SUCCESS;

  return_val = setjmp (return_catch);

  /* If `return' was seen outside of a function, but in the script, then
     force parse_and_execute () to clean up. */
  if (return_val)
    {
      parse_and_execute_cleanup ();
      result = return_catch_value;
    }
  else
    result = parse_and_execute (string, filename, pflags);

  if (flags & FEVAL_UNWINDPROT)
    run_unwind_frame ("_evalfile");
  else
    {
      if (flags & FEVAL_NONINT)
    interactive = old_interactive;
      return_catch_flag--;
      sourcelevel--;
      COPY_PROCENV (old_return_catch, return_catch);
    }

  return ((flags & FEVAL_BUILTIN) ? result : 1);
}

int
maybe_execute_file (fname, force_noninteractive)
     const char *fname;
     int force_noninteractive;
{
  char *filename;
  int result, flags;

  filename = bash_tilde_expand (fname, 0);
  flags = FEVAL_ENOENTOK;
  if (force_noninteractive)
    flags |= FEVAL_NONINT;
  result = _evalfile (filename, flags);
  free (filename);
  return result;
}

#if defined (HISTORY)
int
fc_execute_file (filename)
     const char *filename;
{
  int flags;

  /* We want these commands to show up in the history list if
     remember_on_history is set. */
  flags = FEVAL_ENOENTOK|FEVAL_HISTORY|FEVAL_REGFILE;
  return (_evalfile (filename, flags));
}
#endif /* HISTORY */

int
source_file (filename)
     const char *filename;
{
  int flags;

  flags = FEVAL_BUILTIN|FEVAL_UNWINDPROT|FEVAL_NONINT;
  /* POSIX shells exit if non-interactive and file error. */
  if (posixly_correct && !interactive_shell)
    flags |= FEVAL_LONGJMP;
  return (_evalfile (filename, flags));
}

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/getopt.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/getopt.c used in: Step 1144
/* getopt for BASH.

   Copyright (C) 1993, 1994
    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, 59 Temple Place, Suite 330, Boston, MA 02111 USA.  */

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>
#include "memalloc.h"
#include "../shell.h"
#include "getopt.h"

/* For communication from `sh_getopt' to the caller.
   When `sh_getopt' finds an option that takes an argument,
   the argument value is returned here. */
char *sh_optarg = 0;

/* Index in ARGV of the next element to be scanned.
   This is used for communication to and from the caller
   and for communication between successive calls to `sh_getopt'.

   On entry to `sh_getopt', zero means this is the first call; initialize.

   When `sh_getopt' returns EOF, this is the index of the first of the
   non-option elements that the caller should itself scan.

   Otherwise, `sh_optind' communicates from one call to the next
   how much of ARGV has been scanned so far.  */

/* XXX 1003.2 says this must be 1 before any call.  */
int sh_optind = 0;

/* Index of the current argument. */
static int sh_curopt;

/* The next char to be scanned in the option-element
   in which the last option character we returned was found.
   This allows us to pick up the scan where we left off.

   If this is zero, or a null string, it means resume the scan
   by advancing to the next ARGV-element.  */

static char *nextchar;
static int sh_charindex;

/* Callers store zero here to inhibit the error message
   for unrecognized options.  */

int sh_opterr = 1;

/* Set to an option character which was unrecognized.
   This must be initialized on some systems to avoid linking in the
   system's own getopt implementation.  */

int sh_optopt = '?';

/* Set to 1 when we see an invalid option; public so getopts can reset it. */
int sh_badopt = 0;

/* Scan elements of ARGV (whose length is ARGC) for option characters
   given in OPTSTRING.

   If an element of ARGV starts with '-', and is not exactly "-" or "--",
   then it is an option element.  The characters of this element
   (aside from the initial '-') are option characters.  If `sh_getopt'
   is called repeatedly, it returns successively each of the option characters
   from each of the option elements.

   If `sh_getopt' finds another option character, it returns that character,
   updating `sh_optind' and `nextchar' so that the next call to `sh_getopt' can
   resume the scan with the following option character or ARGV-element.

   If there are no more option characters, `sh_getopt' returns `EOF'.
   Then `sh_optind' is the index in ARGV of the first ARGV-element
   that is not an option.

   OPTSTRING is a string containing the legitimate option characters.
   If an option character is seen that is not listed in OPTSTRING,
   return '?' after printing an error message.  If you set `sh_opterr' to
   zero, the error message is suppressed but we still return '?'.

   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
   so the following text in the same ARGV-element, or the text of the following
   ARGV-element, is returned in `sh_optarg'. */

/* 1003.2 specifies the format of this message.  */
#define BADOPT(x)  fprintf (stderr, "%s: illegal option -- %c\n", argv[0], x)
#define NEEDARG(x) fprintf (stderr, "%s: option requires an argument -- %c\n", argv[0], x)

int
sh_getopt (argc, argv, optstring)
     int argc;
     char *const *argv;
     const char *optstring;
{
  char c, *temp;

  sh_optarg = 0;

  if (sh_optind >= argc || sh_optind < 0)   /* XXX was sh_optind > argc */
    {
      sh_optind = argc;
      return (EOF);
    }

  /* Initialize the internal data when the first call is made.
     Start processing options with ARGV-element 1 (since ARGV-element 0
     is the program name); the sequence of previously skipped
     non-option ARGV-elements is empty.  */

  if (sh_optind == 0)
    {
      sh_optind = 1;
      nextchar = (char *)NULL;
    }

  if (nextchar == 0 || *nextchar == '\0')
    {
      /* If we have done all the ARGV-elements, stop the scan. */
      if (sh_optind >= argc)
    return EOF;

      temp = argv[sh_optind];

      /* Special ARGV-element `--' means premature end of options.
     Skip it like a null option, and return EOF. */
      if (temp[0] == '-' && temp[1] == '-' && temp[2] == '\0')
    {
      sh_optind++;
      return EOF;
    }

      /* If we have come to a non-option, either stop the scan or describe
     it to the caller and pass it by.  This makes the pseudo-option
     `-' mean the end of options, but does not skip over it. */
      if (temp[0] != '-' || temp[1] == '\0')
    return EOF;

      /* We have found another option-ARGV-element.
     Start decoding its characters.  */
      nextchar = argv[sh_curopt = sh_optind] + 1;
      sh_charindex = 1;
    }

  /* Look at and handle the next option-character.  */

  c = *nextchar++; sh_charindex++;
  temp = strchr (optstring, c);

  sh_optopt = c;

  /* Increment `sh_optind' when we start to process its last character.  */
  if (nextchar == 0 || *nextchar == '\0')
    {
      sh_optind++;
      nextchar = (char *)NULL;
    }

  if (sh_badopt = (temp == NULL || c == ':'))
    {
      if (sh_opterr)
    BADOPT (c);

      return '?';
    }

  if (temp[1] == ':')
    {
      if (nextchar && *nextchar)
    {
      /* This is an option that requires an argument.  */
      sh_optarg = nextchar;
      /* If we end this ARGV-element by taking the rest as an arg,
         we must advance to the next element now.  */
      sh_optind++;
    }
      else if (sh_optind == argc)
    {
      if (sh_opterr)
        NEEDARG (c);

      sh_optopt = c;
      sh_optarg = "";   /* Needed by getopts. */
      c = (optstring[0] == ':') ? ':' : '?';
    }
      else
    /* We already incremented `sh_optind' once;
       increment it again when taking next ARGV-elt as argument.  */
    sh_optarg = argv[sh_optind++];
      nextchar = (char *)NULL;
    }
  return c;
}

void
sh_getopt_restore_state (argv)
     char **argv;
{
  if (nextchar)
    nextchar = argv[sh_curopt] + sh_charindex;
}

#if 0
void
sh_getopt_debug_restore_state (argv)
     char **argv;
{
  if (nextchar && nextchar != argv[sh_curopt] + sh_charindex)
    {
      itrace("sh_getopt_debug_restore_state: resetting nextchar");
      nextchar = argv[sh_curopt] + sh_charindex;
    }
}
#endif
 
#ifdef TEST

/* Compile with -DTEST to make an executable for use in testing
   the above definition of `sh_getopt'.  */

int
main (argc, argv)
     int argc;
     char **argv;
{
  int c;
  int digit_sh_optind = 0;

  while (1)
    {
      int this_option_sh_optind = sh_optind ? sh_optind : 1;

      c = sh_getopt (argc, argv, "abc:d:0123456789");
      if (c == EOF)
    break;

      switch (c)
    {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (digit_sh_optind != 0 && digit_sh_optind != this_option_sh_optind)
        printf ("digits occur in two different argv-elements.\n");
      digit_sh_optind = this_option_sh_optind;
      printf ("option %c\n", c);
      break;

    case 'a':
      printf ("option a\n");
      break;

    case 'b':
      printf ("option b\n");
      break;

    case 'c':
      printf ("option c with value `%s'\n", sh_optarg);
      break;

    case '?':
      break;

    default:
      printf ("?? sh_getopt returned character code 0%o ??\n", c);
    }
    }

  if (sh_optind < argc)
    {
      printf ("non-option ARGV-elements: ");
      while (sh_optind < argc)
    printf ("%s ", argv[sh_optind++]);
      printf ("\n");
    }

  exit (0);
}

#endif /* TEST */

https://git.savannah.gnu.org/cgit/bash.git/tree/builtins/bashgetopt.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/builtins/bashgetopt.c used in: Step 1146
/* bashgetopt.c -- `getopt' for use by the builtins. */

/* Copyright (C) 1992-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "../bashansi.h"
#include <chartypes.h>
#include <errno.h>

#include "../shell.h"
#include "common.h"

#define ISOPT(s)    (((*(s) == '-') || (plus && *(s) == '+')) && (s)[1])
#define NOTOPT(s)   (((*(s) != '-') && (!plus || *(s) != '+')) || (s)[1] == '\0')
            
static int  sp;

char    *list_optarg;
int list_optopt;
int list_opttype;

static WORD_LIST *lhead = (WORD_LIST *)NULL;
WORD_LIST   *lcurrent = (WORD_LIST *)NULL;
WORD_LIST   *loptend;   /* Points to the first non-option argument in the list */

int
internal_getopt(list, opts)
WORD_LIST   *list;
char        *opts;
{
    register int c;
    register char *cp;
    int plus;   /* nonzero means to handle +option */
    static char errstr[3] = { '-', '\0', '\0' };

    plus = *opts == '+';
    if (plus)
        opts++;

    if (list == 0) {
        list_optarg = (char *)NULL;
        loptend = (WORD_LIST *)NULL;    /* No non-option arguments */
        return -1;
    }

    if (list != lhead || lhead == 0) {
        /* Hmmm.... called with a different word list.  Reset. */
        sp = 1;
        lcurrent = lhead = list;
        loptend = (WORD_LIST *)NULL;
    }

    if (sp == 1) {
        if (lcurrent == 0 || NOTOPT(lcurrent->word->word)) {
                lhead = (WORD_LIST *)NULL;
                loptend = lcurrent;
            return(-1);
        } else if (lcurrent->word->word[0] == '-' &&
               lcurrent->word->word[1] == '-' &&
               lcurrent->word->word[2] == 0) {
            lhead = (WORD_LIST *)NULL;
            loptend = lcurrent->next;
            return(-1);
        }
        errstr[0] = list_opttype = lcurrent->word->word[0];
    }

    list_optopt = c = lcurrent->word->word[sp];

    if (c == ':' || (cp = strchr(opts, c)) == NULL) {
        errstr[1] = c;
        sh_invalidopt (errstr);     
        if (lcurrent->word->word[++sp] == '\0') {
            lcurrent = lcurrent->next;
            sp = 1;
        }
        list_optarg = NULL;
        if (lcurrent)
            loptend = lcurrent->next;
        return('?');
    }

    if (*++cp == ':' || *cp == ';') {
        /* `:': Option requires an argument. */
        /* `;': option argument may be missing */
        /* We allow -l2 as equivalent to -l 2 */
        if (lcurrent->word->word[sp+1]) {
            list_optarg = lcurrent->word->word + sp + 1;
            lcurrent = lcurrent->next;
        /* If the specifier is `;', don't set optarg if the next
           argument looks like another option. */
#if 0
        } else if (lcurrent->next && (*cp == ':' || lcurrent->next->word->word[0] != '-')) {
#else
        } else if (lcurrent->next && (*cp == ':' || NOTOPT(lcurrent->next->word->word))) {
#endif
            lcurrent = lcurrent->next;
            list_optarg = lcurrent->word->word;
            lcurrent = lcurrent->next;
        } else if (*cp == ';') {
            list_optarg = (char *)NULL;
            lcurrent = lcurrent->next;
        } else {    /* lcurrent->next == NULL */
            errstr[1] = c;
            sh_needarg (errstr);
            sp = 1;
            list_optarg = (char *)NULL;
            return('?');
        }
        sp = 1;
    } else if (*cp == '#') {
        /* option requires a numeric argument */
        if (lcurrent->word->word[sp+1]) {
            if (DIGIT(lcurrent->word->word[sp+1])) {
                list_optarg = lcurrent->word->word + sp + 1;
                lcurrent = lcurrent->next;
            } else
                list_optarg = (char *)NULL;
        } else {
            if (lcurrent->next && legal_number(lcurrent->next->word->word, (intmax_t *)0)) {
                lcurrent = lcurrent->next;
                list_optarg = lcurrent->word->word;
                lcurrent = lcurrent->next;
            } else {
                errstr[1] = c;
                sh_neednumarg (errstr);
                sp = 1;
                list_optarg = (char *)NULL;
                return ('?');
            }
        }

    } else {
        /* No argument, just return the option. */
        if (lcurrent->word->word[++sp] == '\0') {
            sp = 1;
            lcurrent = lcurrent->next;
        }
        list_optarg = (char *)NULL;
    }

    return(c);
}

/*
 * reset_internal_getopt -- force the in[ft]ernal getopt to reset
 */

void
reset_internal_getopt ()
{
    lhead = lcurrent = loptend = (WORD_LIST *)NULL;
    sp = 1;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/clktck.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/clktck.c used in: Step 1151
/* clktck.c - get the value of CLK_TCK. */

/* Copyright (C) 1997 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA */

#include <config.h>

#include <bashtypes.h>
#include <sys/param.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#if defined (HAVE_LIMITS_H)
#  include <limits.h>
#endif

#if !defined (HAVE_SYSCONF) || !defined (_SC_CLK_TCK)
#  if !defined (CLK_TCK)
#    if defined (HZ)
#      define CLK_TCK HZ
#    else
#      define CLK_TCK 60
#    endif
#  endif /* !CLK_TCK */
#endif /* !HAVE_SYSCONF && !_SC_CLK_TCK */

long
get_clk_tck ()
{
  static long retval = 0;

  if (retval != 0)
    return (retval);

#if defined (HAVE_SYSCONF) && defined (_SC_CLK_TCK)
  retval = sysconf (_SC_CLK_TCK);
#else /* !SYSCONF || !_SC_CLK_TCK */
  retval = CLK_TCK;
#endif /* !SYSCONF || !_SC_CLK_TCK */

  return (retval);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/getcwd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/getcwd.c used in: Step 1152
/* getcwd.c -- stolen from the GNU C library and modified to work with bash. */

/* Copyright (C) 1991 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If
   not, write to the Free Software Foundation, Inc.,
   59 Temple Place, Suite 330, Boston, MA 02111 USA.  */

#include <config.h>

#if !defined (HAVE_GETCWD)

#include <bashtypes.h>
#include <errno.h>

#if defined (HAVE_LIMITS_H)
#  include <limits.h>
#endif

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <posixdir.h>
#include <posixstat.h>
#include <maxpath.h>
#include <memalloc.h>

#include <bashansi.h>

#include <xmalloc.h>

#if !defined (errno)
extern int errno;
#endif /* !errno */

#if !defined (HAVE_LSTAT)
#  define lstat stat
#endif

#if !defined (NULL)
#  define NULL 0
#endif

/* Get the pathname of the current working directory,
   and put it in SIZE bytes of BUF.  Returns NULL if the
   directory couldn't be determined or SIZE was too small.
   If successful, returns BUF.  In GNU, if BUF is NULL,
   an array is allocated with `malloc'; the array is SIZE
   bytes long, unless SIZE <= 0, in which case it is as
   big as necessary.  */
#if defined (__STDC__)
char *
getcwd (char *buf, size_t size)
#else /* !__STDC__ */
char *
getcwd (buf, size)
     char *buf;
     size_t size;
#endif /* !__STDC__ */
{
  static const char dots[]
    = "../../../../../../../../../../../../../../../../../../../../../../../\
../../../../../../../../../../../../../../../../../../../../../../../../../../\
../../../../../../../../../../../../../../../../../../../../../../../../../..";
  const char *dotp, *dotlist;
  size_t dotsize;
  dev_t rootdev, thisdev;
  ino_t rootino, thisino;
  char path[PATH_MAX + 1];
  register char *pathp;
  char *pathbuf;
  size_t pathsize;
  struct stat st;
  int saved_errno;

  if (buf != NULL && size == 0)
    {
      errno = EINVAL;
      return ((char *)NULL);
    }

  pathsize = sizeof (path);
  pathp = &path[pathsize];
  *--pathp = '\0';
  pathbuf = path;

  if (stat (".", &st) < 0)
    return ((char *)NULL);
  thisdev = st.st_dev;
  thisino = st.st_ino;

  if (stat ("/", &st) < 0)
    return ((char *)NULL);
  rootdev = st.st_dev;
  rootino = st.st_ino;

  saved_errno = 0;

  dotsize = sizeof (dots) - 1;
  dotp = &dots[sizeof (dots)];
  dotlist = dots;
  while (!(thisdev == rootdev && thisino == rootino))
    {
      register DIR *dirstream;
      register struct dirent *d;
      dev_t dotdev;
      ino_t dotino;
      char mount_point;
      int namlen;

      /* Look at the parent directory.  */
      if (dotp == dotlist)
    {
      /* My, what a deep directory tree you have, Grandma.  */
      char *new;
      if (dotlist == dots)
        {
          new = (char *)malloc (dotsize * 2 + 1);
          if (new == NULL)
        goto lose;
          memcpy (new, dots, dotsize);
        }
      else
        {
          new = (char *)realloc ((PTR_T) dotlist, dotsize * 2 + 1);
          if (new == NULL)
        goto lose;
        }
      memcpy (&new[dotsize], new, dotsize);
      dotp = &new[dotsize];
      dotsize *= 2;
      new[dotsize] = '\0';
      dotlist = new;
    }

      dotp -= 3;

      /* Figure out if this directory is a mount point.  */
      if (stat (dotp, &st) < 0)
    goto lose;
      dotdev = st.st_dev;
      dotino = st.st_ino;
      mount_point = dotdev != thisdev;

      /* Search for the last directory.  */
      dirstream = opendir (dotp);
      if (dirstream == NULL)
    goto lose;
      while ((d = readdir (dirstream)) != NULL)
    {
      if (d->d_name[0] == '.' &&
          (d->d_name[1] == '\0' ||
        (d->d_name[1] == '.' && d->d_name[2] == '\0')))
        continue;
      if (mount_point || d->d_fileno == thisino)
        {
          char *name;

          namlen = D_NAMLEN(d);
          name = (char *)
        alloca (dotlist + dotsize - dotp + 1 + namlen + 1);
          memcpy (name, dotp, dotlist + dotsize - dotp);
          name[dotlist + dotsize - dotp] = '/';
          memcpy (&name[dotlist + dotsize - dotp + 1],
              d->d_name, namlen + 1);
          if (lstat (name, &st) < 0)
        {
#if 0
          int save = errno;
          (void) closedir (dirstream);
          errno = save;
          goto lose;
#else
          saved_errno = errno;
#endif
        }
          if (st.st_dev == thisdev && st.st_ino == thisino)
        break;
        }
    }
      if (d == NULL)
    {
#if 0
      int save = errno;
#else
      int save = errno ? errno : saved_errno;
#endif
      (void) closedir (dirstream);
      errno = save;
      goto lose;
    }
      else
    {
      size_t space;

      while ((space = pathp - pathbuf) <= namlen)
        {
          char *new;

          if (pathbuf == path)
        {
          new = (char *)malloc (pathsize * 2);
          if (!new)
            goto lose;
        }
          else
        {
          new = (char *)realloc ((PTR_T) pathbuf, (pathsize * 2));
          if (!new)
            goto lose;
          pathp = new + space;
        }
          (void) memcpy (new + pathsize + space, pathp, pathsize - space);
          pathp = new + pathsize + space;
          pathbuf = new;
          pathsize *= 2;
        }

      pathp -= namlen;
      (void) memcpy (pathp, d->d_name, namlen);
      *--pathp = '/';
      (void) closedir (dirstream);
    }

      thisdev = dotdev;
      thisino = dotino;
    }

  if (pathp == &path[sizeof(path) - 1])
    *--pathp = '/';

  if (dotlist != dots)
    free ((PTR_T) dotlist);

  {
    size_t len = pathbuf + pathsize - pathp;
    if (buf == NULL)
      {
    if (len < (size_t) size)
      len = size;
    buf = (char *) malloc (len);
    if (buf == NULL)
      goto lose2;
      }
    else if ((size_t) size < len)
      {
    errno = ERANGE;
    goto lose2;
      }
    (void) memcpy((PTR_T) buf, (PTR_T) pathp, len);
  }

  if (pathbuf != path)
    free (pathbuf);

  return (buf);

 lose:
  if ((dotlist != dots) && dotlist)
    {
      int e = errno;
      free ((PTR_T) dotlist);
      errno = e;
    }

 lose2:
  if ((pathbuf != path) && pathbuf)
    {
      int e = errno;
      free ((PTR_T) pathbuf);
      errno = e;
    }
  return ((char *)NULL);
}

#if defined (TEST)
#  include <stdio.h>
main (argc, argv)
     int argc;
     char **argv;
{
  char b[PATH_MAX];

  if (getcwd(b, sizeof(b)))
    {
      printf ("%s\n", b);
      exit (0);
    }
  else
    {
      perror ("cwd: getcwd");
      exit (1);
    }
}
#endif /* TEST */
#endif /* !HAVE_GETCWD */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/getenv.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/getenv.c used in: Step 1153
/* getenv.c - get environment variable value from the shell's variable
          list. */

/* Copyright (C) 1997-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA */

#include <config.h>

#if defined (CAN_REDEFINE_GETENV)

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <bashansi.h>
#include <errno.h>
#include <shell.h>

#ifndef errno
extern int errno;
#endif

extern char **environ;

/* We supply our own version of getenv () because we want library
   routines to get the changed values of exported variables. */

/* The NeXT C library has getenv () defined and used in the same file.
   This screws our scheme.  However, Bash will run on the NeXT using
   the C library getenv (), since right now the only environment variable
   that we care about is HOME, and that is already defined.  */
static char *last_tempenv_value = (char *)NULL;

char *
getenv (name)
     const char *name;
{
  SHELL_VAR *var;

  if (name == 0 || *name == '\0')
    return ((char *)NULL);

  var = find_tempenv_variable ((char *)name);
  if (var)
    {
      FREE (last_tempenv_value);

      last_tempenv_value = value_cell (var) ? savestring (value_cell (var)) : (char *)NULL;
      return (last_tempenv_value);
    }
  else if (shell_variables)
    {
      var = find_variable ((char *)name);
      if (var && exported_p (var))
    return (value_cell (var));
    }
  else
    {
      register int i, len;

      /* In some cases, s5r3 invokes getenv() before main(); BSD systems
     using gprof also exhibit this behavior.  This means that
     shell_variables will be 0 when this is invoked.  We look up the
     variable in the real environment in that case. */

      for (i = 0, len = strlen (name); environ[i]; i++)
    {
      if ((STREQN (environ[i], name, len)) && (environ[i][len] == '='))
        return (environ[i] + len + 1);
    }
    }

  return ((char *)NULL);
}

/* Some versions of Unix use _getenv instead. */
char *
_getenv (name)
     const char *name;
{
  return (getenv (name));
}

/* SUSv3 says argument is a `char *'; BSD implementations disagree */
int
putenv (str)
#ifndef HAVE_STD_PUTENV
     const char *str;
#else
     char *str;
#endif
{
  SHELL_VAR *var;
  char *name, *value;
  int offset;

  if (str == 0 || *str == '\0')
    {
      errno = EINVAL;
      return -1;
    }

  offset = assignment (str);
  if (str[offset] != '=')
    {
      errno = EINVAL;
      return -1;
    }
  name = savestring (str);
  name[offset] = 0;

  value = name + offset + 1;

  /* XXX - should we worry about readonly here? */
  var = bind_variable (name, value);
  if (var == 0)
    {
      errno = EINVAL;
      return -1;
    }

  VUNSETATTR (var, att_invisible);
  VSETATTR (var, att_exported);

  return 0;
}

#if 0
int
_putenv (name)
#ifndef HAVE_STD_PUTENV
     const char *name;
#else
     char *name;
#endif
{
  return putenv (name);
}
#endif

int
setenv (name, value, rewrite)
     const char *name;
     const char *value;
     int rewrite;
{
  SHELL_VAR *var;
  char *v;

  if (name == 0 || *name == '\0' || strchr (name, '=') != 0)
    {
      errno = EINVAL;
      return -1;
    }

  var = 0;
  v = value;
  /* XXX - should we worry about readonly here? */
  if (rewrite == 0)
    var = find_variable (name);

  if (var == 0)
    var = bind_variable (name, v);

  if (var == 0)
    return -1;

  VUNSETATTR (var, att_invisible);
  VSETATTR (var, att_exported);

  return 0;
}

#if 0
int
_setenv (name, value, rewrite)
     const char *name;
     const char *value;
     int rewrite;
{
  return setenv (name, value, rewrite);
}
#endif

/* SUSv3 says unsetenv returns int; existing implementations (BSD) disagree. */

#ifdef HAVE_STD_UNSETENV
#define UNSETENV_RETURN(N)  return(N)
#define UNSETENV_RETTYPE    int
#else
#define UNSETENV_RETURN(N)  return
#define UNSETENV_RETTYPE    void
#endif

UNSETENV_RETTYPE
unsetenv (name)
     const char *name;
{
  if (name == 0 || *name == '\0' || strchr (name, '=') != 0)
    {
      errno = EINVAL;
      UNSETENV_RETURN(-1);
    }

  /* XXX - should we just remove the export attribute here? */
#if 1
  unbind_variable (name);
#else
  SHELL_VAR *v;

  v = find_variable (name);
  if (v)
    VUNSETATTR (v, att_exported);
#endif

  UNSETENV_RETURN(0);
}
#endif /* CAN_REDEFINE_GETENV */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/setlinebuf.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/setlinebuf.c used in: Step 1155
/* setlinebuf.c - line-buffer a stdio stream. */

/* Copyright (C) 1997 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA */

#include <config.h>

#include <stdio.h>

#include <xmalloc.h>

#if defined (USING_BASH_MALLOC)
#  define LBUF_BUFSIZE  1008
#else
#  define LBUF_BUFSIZE  BUFSIZ
#endif

/* Cause STREAM to buffer lines as opposed to characters or blocks. */
int
sh_setlinebuf (stream)
     FILE *stream;
{
  char *local_linebuf;

#if !defined (HAVE_SETLINEBUF) && !defined (HAVE_SETVBUF)
  return (0);
#endif

#if defined (USING_BASH_MALLOC)
  local_linebuf = (char *)xmalloc (LBUF_BUFSIZE);
#else
  local_linebuf = (char *)NULL;
#endif

#if defined (HAVE_SETVBUF)

#  if defined (SETVBUF_REVERSED)
  return (setvbuf (stream, _IOLBF, local_linebuf, LBUF_BUFSIZE));
#  else /* !SETVBUF_REVERSED */
  return (setvbuf (stream, local_linebuf, _IOLBF, LBUF_BUFSIZE));
#  endif /* !SETVBUF_REVERSED */
# else /* !HAVE_SETVBUF */

  setlinebuf (stream);
  return (0);

#endif /* !HAVE_SETVBUF */
}

https://git.savannah.gnu.org/cgit/bash.git/tree/xmalloc.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/xmalloc.h used in: Step 1155, Step 1197
/* xmalloc.h -- defines for the `x' memory allocation functions */

/* Copyright (C) 2001 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#if !defined (_XMALLOC_H_)
#define _XMALLOC_H_

#include "stdc.h"
#include "bashansi.h"

/* Generic pointer type. */
#ifndef PTR_T

#if defined (__STDC__)
#  define PTR_T void *
#else
#  define PTR_T char *
#endif

#endif /* PTR_T */

/* Allocation functions in xmalloc.c */
extern PTR_T xmalloc __P((size_t));
extern PTR_T xrealloc __P((void *, size_t));
extern void xfree __P((void *));

#if defined(USING_BASH_MALLOC) && !defined (DISABLE_MALLOC_WRAPPERS)
extern PTR_T sh_xmalloc __P((size_t, const char *, int));
extern PTR_T sh_xrealloc __P((void *, size_t, const char *, int));
extern void sh_xfree __P((void *, const char *, int));

#define xmalloc(x)  sh_xmalloc((x), __FILE__, __LINE__)
#define xrealloc(x, n)  sh_xrealloc((x), (n), __FILE__, __LINE__)
#define xfree(x)    sh_xfree((x), __FILE__, __LINE__)

#ifdef free
#undef free
#endif
#define free(x)     sh_xfree((x), __FILE__, __LINE__)
#endif  /* USING_BASH_MALLOC */

#endif  /* _XMALLOC_H_ */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strcasecmp.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strcasecmp.c used in: Step 1156
/* strcasecmp.c - functions for case-insensitive string comparison. */

/* Copyright (C) 1995 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA */
   
#include <config.h>

#if !defined (HAVE_STRCASECMP)

#include <stdc.h>
#include <bashansi.h>
#include <chartypes.h>

/* Compare at most COUNT characters from string1 to string2.  Case
   doesn't matter. */
int
strncasecmp (string1, string2, count)
     const char *string1;
     const char *string2;
     int count;
{
  register const char *s1;
  register const char *s2;
  register int r;

  if (count <= 0 || (string1 == string2))
    return 0;

  s1 = string1;
  s2 = string2;
  do
    {
      if ((r = TOLOWER ((unsigned char) *s1) - TOLOWER ((unsigned char) *s2)) != 0)
    return r;
      if (*s1++ == '\0')
    break;
      s2++;
    }
  while (--count != 0);

  return (0);
}

/* strcmp (), but caseless. */
int
strcasecmp (string1, string2)
     const char *string1;
     const char *string2;
{
  register const char *s1;
  register const char *s2;
  register int r;

  s1 = string1;
  s2 = string2;

  if (s1 == s2)
    return (0);

  while ((r = TOLOWER ((unsigned char)*s1) - TOLOWER ((unsigned char)*s2)) == 0)
    {
      if (*s1++ == '\0')
    return 0;
      s2++;
    }

  return (r);
}
#endif /* !HAVE_STRCASECMP */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strerror.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strerror.c used in: Step 1157
/* strerror.c - string corresponding to a particular value of errno. */

/* Copyright (C) 1995 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA */
   
#include <config.h>

#if !defined (HAVE_STRERROR)

#include <bashtypes.h>
#ifndef _MINIX
#  include <sys/param.h>
#endif

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <stdio.h>
#include <errno.h>

#include <shell.h>

#if !defined (errno)
extern int errno;
#endif /* !errno */

/* Return a string corresponding to the error number E.  From
   the ANSI C spec. */
#if defined (strerror)
#  undef strerror
#endif

static char *errbase = "Unknown system error ";

char *
strerror (e)
     int e;
{
  static char emsg[40];
#if defined (HAVE_SYS_ERRLIST)
  extern int sys_nerr;
  extern char *sys_errlist[];

  if (e > 0 && e < sys_nerr)
    return (sys_errlist[e]);
  else
#endif /* HAVE_SYS_ERRLIST */
    {
      char *z;

      z = itos (e);
      strcpy (emsg, errbase);
      strcat (emsg, z);
      free (z);
      return (&emsg[0]);
    }
}
#endif /* HAVE_STRERROR */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strtod.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strtod.c used in: Step 1158
/* Copyright (C) 1991, 1992 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.  */

#if HAVE_CONFIG_H
# include <config.h>
#endif

#ifndef HAVE_STRTOD

#include <errno.h>
#ifndef errno
extern int errno;
#endif

#include <chartypes.h>
#include <math.h>

#if HAVE_FLOAT_H
# include <float.h>
#else
# define DBL_MAX 1.7976931348623159e+308
# define DBL_MIN 2.2250738585072010e-308
#endif

#include <bashansi.h>

#ifndef NULL
#  define NULL 0
#endif

#ifndef HUGE_VAL
#  define HUGE_VAL HUGE
#endif

/* Convert NPTR to a double.  If ENDPTR is not NULL, a pointer to the
   character after the last one used in the number is put in *ENDPTR.  */
double
strtod (nptr, endptr)
     const char *nptr;
     char **endptr;
{
  register const char *s;
  short sign;

  /* The number so far.  */
  double num;

  int got_dot;          /* Found a decimal point.  */
  int got_digit;        /* Seen any digits.  */

  /* The exponent of the number.  */
  long int exponent;

  if (nptr == NULL)
    {
      errno = EINVAL;
      goto noconv;
    }

  s = nptr;

  /* Eat whitespace.  */
  while (ISSPACE ((unsigned char)*s))
    ++s;

  /* Get the sign.  */
  sign = *s == '-' ? -1 : 1;
  if (*s == '-' || *s == '+')
    ++s;

  num = 0.0;
  got_dot = 0;
  got_digit = 0;
  exponent = 0;
  for (;; ++s)
    {
      if (DIGIT (*s))
    {
      got_digit = 1;

      /* Make sure that multiplication by 10 will not overflow.  */
      if (num > DBL_MAX * 0.1)
        /* The value of the digit doesn't matter, since we have already
           gotten as many digits as can be represented in a `double'.
           This doesn't necessarily mean the result will overflow.
           The exponent may reduce it to within range.

           We just need to record that there was another
           digit so that we can multiply by 10 later.  */
        ++exponent;
      else
        num = (num * 10.0) + (*s - '0');

      /* Keep track of the number of digits after the decimal point.
         If we just divided by 10 here, we would lose precision.  */
      if (got_dot)
        --exponent;
    }
      else if (!got_dot && *s == '.')
    /* Record that we have found the decimal point.  */
    got_dot = 1;
      else
    /* Any other character terminates the number.  */
    break;
    }

  if (!got_digit)
    goto noconv;

  if (TOLOWER ((unsigned char)*s) == 'e')
    {
      /* Get the exponent specified after the `e' or `E'.  */
      int save = errno;
      char *end;
      long int exp;

      errno = 0;
      ++s;
      exp = strtol (s, &end, 10);
      if (errno == ERANGE)
    {
      /* The exponent overflowed a `long int'.  It is probably a safe
         assumption that an exponent that cannot be represented by
         a `long int' exceeds the limits of a `double'.  */
      if (endptr != NULL)
        *endptr = end;
      if (exp < 0)
        goto underflow;
      else
        goto overflow;
    }
      else if (end == s)
    /* There was no exponent.  Reset END to point to
       the 'e' or 'E', so *ENDPTR will be set there.  */
    end = (char *) s - 1;
      errno = save;
      s = end;
      exponent += exp;
    }

  if (endptr != NULL)
    *endptr = (char *) s;

  if (num == 0.0)
    return 0.0;

  /* Multiply NUM by 10 to the EXPONENT power,
     checking for overflow and underflow.  */

  if (exponent < 0)
    {
      if (num < DBL_MIN * pow (10.0, (double) -exponent))
    goto underflow;
    }
  else if (exponent > 0)
    {
      if (num > DBL_MAX * pow (10.0, (double) -exponent))
    goto overflow;
    }

  num *= pow (10.0, (double) exponent);

  return num * sign;

overflow:
  /* Return an overflow error.  */
  errno = ERANGE;
  return HUGE_VAL * sign;

underflow:
  /* Return an underflow error.  */
  if (endptr != NULL)
    *endptr = (char *) nptr;
  errno = ERANGE;
  return 0.0;

noconv:
  /* There was no number.  */
  if (endptr != NULL)
    *endptr = (char *) nptr;
  return 0.0;
}

#endif /* !HAVE_STRTOD */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/vprint.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/vprint.c used in: Step 1159
/* vprint.c -- v[fs]printf() for 4.[23] BSD systems. */

/* Copyright (C) 1987,1989 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (USE_VFPRINTF_EMULATION)

#include <stdio.h>

#if !defined (NULL)
#  if defined (__STDC__)
#    define NULL ((void *)0)
#  else
#    define NULL 0x0
#  endif /* __STDC__ */
#endif /* !NULL */

/*
 * Beware!  Don't trust the value returned by either of these functions; it
 * seems that pre-4.3-tahoe implementations of _doprnt () return the first
 * argument, i.e. a char *.
 */
#include <varargs.h>

int
vfprintf (iop, fmt, ap)
     FILE *iop;
     char *fmt;
     va_list ap;
{
  int len;
  char localbuf[BUFSIZ];

  if (iop->_flag & _IONBF)
    {
      iop->_flag &= ~_IONBF;
      iop->_ptr = iop->_base = localbuf;
      len = _doprnt (fmt, ap, iop);
      (void) fflush (iop);
      iop->_flag |= _IONBF;
      iop->_base = NULL;
      iop->_bufsiz = 0;
      iop->_cnt = 0;
    }
  else
    len = _doprnt (fmt, ap, iop);
  return (ferror (iop) ? EOF : len);
}

/*
 * Ditto for vsprintf
 */
int
vsprintf (str, fmt, ap)
     char *str, *fmt;
     va_list ap;
{
  FILE f;
  int len;

  f._flag = _IOWRT|_IOSTRG;
  f._ptr = str;
  f._cnt = 32767;
  len = _doprnt (fmt, ap, &f);
  *f._ptr = 0;
  return (len);
}
#endif /* USE_VFPRINTF_EMULATION */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/itos.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/itos.c used in: Step 1160
/* itos.c -- Convert integer to string. */

/* Copyright (C) 1998-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <bashansi.h>
#include "shell.h"

char *
inttostr (i, buf, len)
     intmax_t i;
     char *buf;
     size_t len;
{
  return (fmtumax (i, 10, buf, len, 0));
}

/* Integer to string conversion.  This conses the string; the
   caller should free it. */
char *
itos (i)
     intmax_t i;
{
  char *p, lbuf[INT_STRLEN_BOUND(intmax_t) + 1];

  p = fmtumax (i, 10, lbuf, sizeof(lbuf), 0);
  return (savestring (p));
}

char *
uinttostr (i, buf, len)
     uintmax_t i;
     char *buf;
     size_t len;
{
  return (fmtumax (i, 10, buf, len, FL_UNSIGNED));
}

/* Integer to string conversion.  This conses the string; the
   caller should free it. */
char *
uitos (i)
     uintmax_t i;
{
  char *p, lbuf[INT_STRLEN_BOUND(uintmax_t) + 1];

  p = fmtumax (i, 10, lbuf, sizeof(lbuf), FL_UNSIGNED);
  return (savestring (p));
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/rename.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/rename.c used in: Step 1161
/*
 * rename - rename a file
 */

/* Copyright (C) 1999 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if !defined (HAVE_RENAME)

#include <bashtypes.h>
#include <posixstat.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif
#include <errno.h>

#include <stdc.h>

#ifndef errno
extern int errno;
#endif

int
rename (from, to)
     const char *from, *to;
{
  struct stat fb, tb;

  if (stat (from, &fb) < 0)
    return -1;  

  if (stat (to, &tb) < 0)
    {
      if (errno != ENOENT)
    return -1;
    }
  else
    {
      if (fb.st_dev == tb.st_dev && fb.st_ino == tb.st_ino)
        return 0;       /* same file */
      if (unlink (to) < 0 && errno != ENOENT)
        return -1;
    }

  if (link (from, to) < 0)
    return (-1);

  if (unlink (from) < 0 && errno != ENOENT)
    {
      int e = errno;
      unlink (to);
      errno = e;
      return (-1);
    }

  return (0);
}
#endif /* !HAVE_RENAME */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/zread.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/zread.c used in: Step 1162
/* Copyright (C) 1999-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include <sys/types.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <errno.h>

#if !defined (errno)
extern int errno;
#endif

#ifndef SEEK_CUR
#  define SEEK_CUR 1
#endif

/* Read LEN bytes from FD into BUF.  Retry the read on EINTR.  Any other
   error causes the loop to break. */
ssize_t
zread (fd, buf, len)
     int fd;
     char *buf;
     size_t len;
{
  ssize_t r;

  while ((r = read (fd, buf, len)) < 0 && errno == EINTR)
    ;
  return r;
}

/* Read LEN bytes from FD into BUF.  Retry the read on EINTR, up to three
   interrupts.  Any other error causes the loop to break. */

#ifdef NUM_INTR
#  undef NUM_INTR
#endif
#define NUM_INTR 3

ssize_t
zreadintr (fd, buf, len)
     int fd;
     char *buf;
     size_t len;
{
  ssize_t r;
  int nintr;

  for (nintr = 0; ; )
    {
      r = read (fd, buf, len);
      if (r >= 0)
    return r;
      if (r == -1 && errno == EINTR)
    {
      if (++nintr > NUM_INTR)
        return -1;
      continue;
    }
      return r;
    }
}

/* Read one character from FD and return it in CP.  Return values are as
   in read(2).  This does some local buffering to avoid many one-character
   calls to read(2), like those the `read' builtin performs. */

static char lbuf[128];
static size_t lind, lused;

ssize_t
zreadc (fd, cp)
     int fd;
     char *cp;
{
  ssize_t nr;

  if (lind == lused || lused == 0)
    {
      nr = zread (fd, lbuf, sizeof (lbuf));
      lind = 0;
      if (nr <= 0)
    {
      lused = 0;
      return nr;
    }
      lused = nr;
    }
  if (cp)
    *cp = lbuf[lind++];
  return 1;
}

void
zreset ()
{
  lind = lused = 0;
}

/* Sync the seek pointer for FD so that the kernel's idea of the last char
   read is the last char returned by zreadc. */
void
zsyncfd (fd)
     int fd;
{
  off_t off;

  off = lused - lind;
  if (off > 0)
    lseek (fd, -off, SEEK_CUR);
  lused = lind = 0;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/zwrite.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/zwrite.c used in: Step 1163
/* Copyright (C) 1999-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include <sys/types.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <errno.h>

#if !defined (errno)
extern int errno;
#endif

/* Write NB bytes from BUF to file descriptor FD, retrying the write if
   it is interrupted.  We retry three times if we get a zero-length
   write.  Any other signal causes this function to return prematurely. */
int
zwrite (fd, buf, nb)
     int fd;
     char *buf;
     size_t nb;
{
  int n, i, nt;

  for (n = nb, nt = 0;;)
    {
      i = write (fd, buf, n);
      if (i > 0)
    {
      n -= i;
      if (n <= 0)
        return nb;
      buf += i;
    }
      else if (i == 0)
    {
      if (++nt > 3)
        return (nb - n);
    }
      else if (errno != EINTR)
    return -1;
    }
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/shtty.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/shtty.c used in: Step 1164
/* Copyright (C) 1999 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

/*
 * shtty.c -- abstract interface to the terminal, focusing on capabilities.
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif

#include <shtty.h>

static TTYSTRUCT ttin, ttout;
static int ttsaved = 0;

int
ttgetattr(fd, ttp)
int fd;
TTYSTRUCT *ttp;
{
#ifdef TERMIOS_TTY_DRIVER
  return tcgetattr(fd, ttp);
#else
#  ifdef TERMIO_TTY_DRIVER
  return ioctl(fd, TCGETA, ttp);
#  else
  return ioctl(fd, TIOCGETP, ttp);
#  endif
#endif
}

int
ttsetattr(fd, ttp)
int fd;
TTYSTRUCT *ttp;
{
#ifdef TERMIOS_TTY_DRIVER
  return tcsetattr(fd, TCSADRAIN, ttp);
#else
#  ifdef TERMIO_TTY_DRIVER
  return ioctl(fd, TCSETAW, ttp);
#  else
  return ioctl(fd, TIOCSETN, ttp);
#  endif
#endif
}

void
ttsave()
{
  if (ttsaved)
   return;
  ttgetattr (0, &ttin);
  ttgetattr (1, &ttout);
  ttsaved = 1;
}

void
ttrestore()
{
  if (ttsaved == 0)
    return;
  ttsetattr (0, &ttin);
  ttsetattr (1, &ttout);
  ttsaved = 0;
}

/* Retrieve the attributes associated with tty fd FD. */
TTYSTRUCT *
ttattr (fd)
     int fd;
{
  if (ttsaved == 0)
    return ((TTYSTRUCT *)0);
  if (fd == 0)
    return &ttin;
  else if (fd == 1)
    return &ttout;
  else
    return ((TTYSTRUCT *)0);
}

/*
 * Change attributes in ttp so that when it is installed using
 * ttsetattr, the terminal will be in one-char-at-a-time mode.
 */
int
tt_setonechar(ttp)
     TTYSTRUCT *ttp;
{
#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)

  /* XXX - might not want this -- it disables erase and kill processing. */
  ttp->c_lflag &= ~ICANON;

  ttp->c_lflag |= ISIG;
#  ifdef IEXTEN
  ttp->c_lflag |= IEXTEN;
#  endif

  ttp->c_iflag |= ICRNL;    /* make sure we get CR->NL on input */
  ttp->c_iflag &= ~INLCR;   /* but no NL->CR */

#  ifdef OPOST
  ttp->c_oflag |= OPOST;
#  endif
#  ifdef ONLCR
  ttp->c_oflag |= ONLCR;
#  endif
#  ifdef OCRNL
  ttp->c_oflag &= ~OCRNL;
#  endif
#  ifdef ONOCR
  ttp->c_oflag &= ~ONOCR;
#  endif
#  ifdef ONLRET
  ttp->c_oflag &= ~ONLRET;
#  endif

  ttp->c_cc[VMIN] = 1;
  ttp->c_cc[VTIME] = 0;

#else

  ttp->sg_flags |= CBREAK;

#endif

  return 0;
}

/* Set the terminal into one-character-at-a-time mode */
int
ttonechar ()
{
  TTYSTRUCT tt;

  if (ttsaved == 0)
    return -1;
  tt = ttin;
  if (tt_setonechar(&tt) < 0)
    return -1;
  return (ttsetattr (0, &tt));
}

/*
 * Change attributes in ttp so that when it is installed using
 * ttsetattr, the terminal will be in no-echo mode.
 */
int
tt_setnoecho(ttp)
     TTYSTRUCT *ttp;
{
#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)
  ttp->c_lflag &= ~(ECHO|ECHOK|ECHONL);
#else
  ttp->sg_flags &= ~ECHO;
#endif

  return 0;
}

/* Set the terminal into no-echo mode */
int
ttnoecho ()
{
  TTYSTRUCT tt;

  if (ttsaved == 0)
    return -1;
  tt = ttin;
  if (tt_setnoecho (&tt) < 0)
    return -1;
  return (ttsetattr (0, &tt));
}

/*
 * Change attributes in ttp so that when it is installed using
 * ttsetattr, the terminal will be in eight-bit mode (pass8).
 */
int
tt_seteightbit (ttp)
     TTYSTRUCT *ttp;
{
#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)
  ttp->c_iflag &= ~ISTRIP;
  ttp->c_cflag |= CS8;
  ttp->c_cflag &= ~PARENB;
#else
  ttp->sg_flags |= ANYP;
#endif

  return 0;
}

/* Set the terminal into eight-bit mode */
int
tteightbit ()
{
  TTYSTRUCT tt;

  if (ttsaved == 0)
    return -1;
  tt = ttin;
  if (tt_seteightbit (&tt) < 0)
    return -1;
  return (ttsetattr (0, &tt));
}

/*
 * Change attributes in ttp so that when it is installed using
 * ttsetattr, the terminal will be in non-canonical input mode.
 */
int
tt_setnocanon (ttp)
     TTYSTRUCT *ttp;
{
#if defined (TERMIOS_TTY_DRIVER) || defined (TERMIO_TTY_DRIVER)
  ttp->c_lflag &= ~ICANON;
#endif

  return 0;
}

/* Set the terminal into non-canonical mode */
int
ttnocanon ()
{
  TTYSTRUCT tt;

  if (ttsaved == 0)
    return -1;
  tt = ttin;
  if (tt_setnocanon (&tt) < 0)
    return -1;
  return (ttsetattr (0, &tt));
}

/*
 * Change attributes in ttp so that when it is installed using
 * ttsetattr, the terminal will be in cbreak, no-echo mode.
 */
int
tt_setcbreak(ttp)
     TTYSTRUCT *ttp;
{
  if (tt_setonechar (ttp) < 0)
    return -1;
  return (tt_setnoecho (ttp));
}

/* Set the terminal into cbreak (no-echo, one-character-at-a-time) mode */
int
ttcbreak ()
{
  TTYSTRUCT tt;

  if (ttsaved == 0)
    return -1;
  tt = ttin;
  if (tt_setcbreak (&tt) < 0)
    return -1;
  return (ttsetattr (0, &tt));
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/inet_aton.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/inet_aton.c used in: Step 1165
/* Snagged from GNU C library, version 2.0.3. */

/*
 * ++Copyright++ 1983, 1990, 1993
 * -
 * Copyright (c) 1983, 1990, 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.
 * 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.
 * -
 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies, and that
 * the name of Digital Equipment Corporation not be used in advertising or
 * publicity pertaining to distribution of the document or software without
 * specific, written prior permission.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 * -
 * --Copyright--
 */

#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)inet_addr.c 8.1 (Berkeley) 6/17/93";
static char rcsid[] = "$Id: inet_addr.c,v 1.5 1996/08/14 03:48:37 drepper Exp $";
#endif /* LIBC_SCCS and not lint */

#include <config.h>

#if !defined (HAVE_INET_ATON) && defined (HAVE_NETWORK) && defined (HAVE_NETINET_IN_H) && defined (HAVE_ARPA_INET_H)

#include <sys/types.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif

#include <bashansi.h>
#include <ctype.h>
#include <stdc.h>

#ifndef INADDR_NONE
#  define INADDR_NONE 0xffffffff
#endif

/* these are compatibility routines, not needed on recent BSD releases */

#if 0
/* Not used, not needed. */
/*
 * Ascii internet address interpretation routine.
 * The value returned is in network order.
 */
u_long
inet_addr(cp)
    register const char *cp;
{
    struct in_addr val;

    if (inet_aton(cp, &val))
        return (val.s_addr);
    return (INADDR_NONE);
}
#endif

/*
 * Check whether "cp" is a valid ascii representation
 * of an Internet address and convert to a binary address.
 * Returns 1 if the address is valid, 0 if not.
 * This replaces inet_addr, the return value from which
 * cannot distinguish between failure and a local broadcast address.
 */
int
inet_aton(cp, addr)
    register const char *cp;
    struct in_addr *addr;
{
    register u_bits32_t val;
    register int base, n;
    register unsigned char c;
    u_int parts[4];
    register u_int *pp = parts;

    c = *cp;
    for (;;) {
        /*
         * Collect number up to ``.''.
         * Values are specified as for C:
         * 0x=hex, 0=octal, isdigit=decimal.
         */
#if 0
        if (!isdigit(c))
#else
        if (c != '0' && c != '1' && c != '2' && c != '3' && c != '4' &&
            c != '5' && c != '6' && c != '7' && c != '8' && c != '9')
#endif
            return (0);
        val = 0; base = 10;
        if (c == '0') {
            c = *++cp;
            if (c == 'x' || c == 'X')
                base = 16, c = *++cp;
            else
                base = 8;
        }
        for (;;) {
            if (isascii(c) && isdigit(c)) {
                val = (val * base) + (c - '0');
                c = *++cp;
            } else if (base == 16 && isascii(c) && isxdigit(c)) {
                val = (val << 4) |
                    (c + 10 - (islower(c) ? 'a' : 'A'));
                c = *++cp;
            } else
                break;
        }
        if (c == '.') {
            /*
             * Internet format:
             *  a.b.c.d
             *  a.b.c   (with c treated as 16 bits)
             *  a.b (with b treated as 24 bits)
             */
            if (pp >= parts + 3)
                return (0);
            *pp++ = val;
            c = *++cp;
        } else
            break;
    }
    /*
     * Check for trailing characters.
     */
    if (c != '\0' && (!isascii(c) || !isspace(c)))
        return (0);
    /*
     * Concoct the address according to
     * the number of parts specified.
     */
    n = pp - parts + 1;
    switch (n) {

    case 0:
        return (0);     /* initial nondigit */

    case 1:             /* a -- 32 bits */
        break;

    case 2:             /* a.b -- 8.24 bits */
        if (val > 0xffffff)
            return (0);
        val |= parts[0] << 24;
        break;

    case 3:             /* a.b.c -- 8.8.16 bits */
        if (val > 0xffff)
            return (0);
        val |= (parts[0] << 24) | (parts[1] << 16);
        break;

    case 4:             /* a.b.c.d -- 8.8.8.8 bits */
        if (val > 0xff)
            return (0);
        val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
        break;
    }
    if (addr)
        addr->s_addr = htonl(val);
    return (1);
}

#endif /* !HAVE_INET_ATON */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/netopen.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/netopen.c used in: Step 1166
/*   
 * netopen.c -- functions to make tcp/udp connections
 *
 * Chet Ramey
 * chet@ins.CWRU.Edu
 */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (HAVE_NETWORK)

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <stdio.h> 
#include <sys/types.h>

#if defined (HAVE_SYS_SOCKET_H)
#  include <sys/socket.h>
#endif

#if defined (HAVE_NETINET_IN_H)
#  include <netinet/in.h>
#endif

#if defined (HAVE_NETDB_H)
#  include <netdb.h>
#endif

#if defined (HAVE_ARPA_INET_H)
#  include <arpa/inet.h>
#endif

#include <bashansi.h>
#include <errno.h>

#include <shell.h>
#include <xmalloc.h>

#ifndef errno
extern int errno;
#endif

#if !defined (HAVE_INET_ATON)
extern int inet_aton __P((const char *, struct in_addr *));
#endif

#ifndef HAVE_GETADDRINFO
/* Stuff the internet address corresponding to HOST into AP, in network
   byte order.  Return 1 on success, 0 on failure. */

static int
_getaddr (host, ap)
     char *host;
     struct in_addr *ap;
{
  struct hostent *h;
  int r;

  r = 0;
  if (host[0] >= '0' && host[0] <= '9')
    {
      /* If the first character is a digit, guess that it's an
     Internet address and return immediately if inet_aton succeeds. */
      r = inet_aton (host, ap);
      if (r)
    return r;
    }
#if !defined (HAVE_GETHOSTBYNAME)
  return 0;
#else
  h = gethostbyname (host);
  if (h && h->h_addr)
    {
      bcopy(h->h_addr, (char *)ap, h->h_length);
      return 1;
    }
#endif
  return 0;
  
}

/* Return 1 if SERV is a valid port number and stuff the converted value into
   PP in network byte order. */   
static int
_getserv (serv, proto, pp)
     char *serv;
     int proto;
     unsigned short *pp;
{
  intmax_t l;
  unsigned short s;

  if (legal_number (serv, &l))
    {
      s = (unsigned short)(l & 0xFFFF);
      if (s != l)
    return (0);
      s = htons (s);
      if (pp)
    *pp = s;
      return 1;
    }
  else
#if defined (HAVE_GETSERVBYNAME)
    {
      struct servent *se;

      se = getservbyname (serv, (proto == 't') ? "tcp" : "udp");
      if (se == 0)
    return 0;
      if (pp)
    *pp = se->s_port;   /* ports returned in network byte order */
      return 1;
    }
#else /* !HAVE_GETSERVBYNAME */
    return 0;
#endif /* !HAVE_GETSERVBYNAME */
}

/*
 * Open a TCP or UDP connection to HOST on port SERV.  Uses the
 * traditional BSD mechanisms.  Returns the connected socket or -1 on error.
 */
static int 
_netopen4(host, serv, typ)
     char *host, *serv;
     int typ;
{
  struct in_addr ina;
  struct sockaddr_in sin;
  unsigned short p;
  int s, e;

  if (_getaddr(host, &ina) == 0)
    {
      internal_error ("%s: host unknown", host);
      errno = EINVAL;
      return -1;
    }

  if (_getserv(serv, typ, &p) == 0)
    {
      internal_error("%s: invalid service", serv);
      errno = EINVAL;
      return -1;
    }
    
  memset ((char *)&sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_port = p;
  sin.sin_addr = ina;

  s = socket(AF_INET, (typ == 't') ? SOCK_STREAM : SOCK_DGRAM, 0);
  if (s < 0)
    {
      sys_error ("socket");
      return (-1);
    }

  if (connect (s, (struct sockaddr *)&sin, sizeof (sin)) < 0)
    {
      e = errno;
      sys_error("connect");
      close(s);
      errno = e;
      return (-1);
    }

  return(s);
}
#endif /* ! HAVE_GETADDRINFO */

#ifdef HAVE_GETADDRINFO
/*
 * Open a TCP or UDP connection to HOST on port SERV.  Uses getaddrinfo(3)
 * which provides support for IPv6.  Returns the connected socket or -1
 * on error.
 */
static int
_netopen6 (host, serv, typ)
     char *host, *serv;
     int typ;
{
  int s, e;
  struct addrinfo hints, *res, *res0;
  int gerr;

  memset ((char *)&hints, 0, sizeof (hints));
  /* XXX -- if problems with IPv6, set to PF_INET for IPv4 only */
#ifdef DEBUG    /* PF_INET is the one that works for me */
  hints.ai_family = PF_INET;
#else
  hints.ai_family = PF_UNSPEC;
#endif
  hints.ai_socktype = (typ == 't') ? SOCK_STREAM : SOCK_DGRAM;

  gerr = getaddrinfo (host, serv, &hints, &res0);
  if (gerr)
    {
      if (gerr == EAI_SERVICE)
    internal_error ("%s: %s", serv, gai_strerror (gerr));
      else
    internal_error ("%s: %s", host, gai_strerror (gerr));
      errno = EINVAL;
      return -1;
    }

  for (res = res0; res; res = res->ai_next)
    {
      if ((s = socket (res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
    {
      if (res->ai_next)
        continue;
      sys_error ("socket");
      freeaddrinfo (res0);
      return -1;
    }
      if (connect (s, res->ai_addr, res->ai_addrlen) < 0)
    {
      if (res->ai_next)
        {
          close (s);
          continue;
        }
      e = errno;
      sys_error ("connect");
      close (s);
      freeaddrinfo (res0);
      errno = e;
      return -1;
    }
      freeaddrinfo (res0);
      break;
    }
  return s;
}
#endif /* HAVE_GETADDRINFO */

/*
 * Open a TCP or UDP connection to HOST on port SERV.  Uses getaddrinfo(3)
 * if available, falling back to the traditional BSD mechanisms otherwise.
 * Returns the connected socket or -1 on error.
 */
static int 
_netopen(host, serv, typ)
     char *host, *serv;
     int typ;
{
#ifdef HAVE_GETADDRINFO
  return (_netopen6 (host, serv, typ));
#else
  return (_netopen4 (host, serv, typ));
#endif
}

/*
 * Open a TCP or UDP connection given a path like `/dev/tcp/host/port' to
 * host `host' on port `port' and return the connected socket.
 */
int
netopen (path)
     char *path;
{
  char *np, *s, *t;
  int fd;

  np = (char *)xmalloc (strlen (path) + 1);
  strcpy (np, path);

  s = np + 9;
  t = strchr (s, '/');
  if (t == 0)
    {
      internal_error ("%s: bad network path specification", path);
      return -1;
    }
  *t++ = '\0';
  fd = _netopen (s, t, path[5]);
  free (np);

  return fd;
}

#if 0
/*
 * Open a TCP connection to host `host' on the port defined for service
 * `serv' and return the connected socket.
 */
int
tcpopen (host, serv)
     char *host, *serv;
{
  return (_netopen (host, serv, 't'));
}

/*
 * Open a UDP connection to host `host' on the port defined for service
 * `serv' and return the connected socket.
 */
int
udpopen (host, serv)
     char *host, *serv;
{
  return _netopen (host, serv, 'u');
}
#endif

#else /* !HAVE_NETWORK */

int
netopen (path)
     char *path;
{
  internal_error ("network operations not supported");
  return -1;
}

#endif /* !HAVE_NETWORK */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strpbrk.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strpbrk.c used in: Step 1167
/* Copyright (C) 1991, 1994 Free Software Foundation, Inc.
   NOTE: The canonical source of this file is maintained with the GNU C Library.
   Bugs can be reported to bug-glibc@prep.ai.mit.edu.

   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.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#if !defined (HAVE_STRPBRK)

#include <stdc.h>

/* Find the first ocurrence in S of any character in ACCEPT.  */
char *
strpbrk (s, accept)
     register const char *s;
     register const char *accept;
{
  while (*s != '\0')
    {
      const char *a = accept;
      while (*a != '\0')
    if (*a++ == *s)
      return (char *) s;
      ++s;
    }

  return 0;
}
#endif

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/timeval.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/timeval.c used in: Step 1168
/* timeval.c - functions to perform operations on struct timevals */

/* Copyright (C) 1999 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (HAVE_TIMEVAL)

#include <sys/types.h>
#include <posixtime.h>

#include <stdio.h>

struct timeval *
difftimeval (d, t1, t2)
     struct timeval *d, *t1, *t2;
{
  d->tv_sec = t2->tv_sec - t1->tv_sec;
  d->tv_usec = t2->tv_usec - t1->tv_usec;
  if (d->tv_usec < 0)
    {
      d->tv_usec += 1000000;
      d->tv_sec -= 1;
      if (d->tv_sec < 0)        /* ??? -- BSD/OS does this */
    {
      d->tv_sec = 0;
      d->tv_usec = 0;
    }
    }
  return d;
}

struct timeval *
addtimeval (d, t1, t2)
     struct timeval *d, *t1, *t2;
{
  d->tv_sec = t1->tv_sec + t2->tv_sec;
  d->tv_usec = t1->tv_usec + t2->tv_usec;
  if (d->tv_usec >= 1000000)
    {
      d->tv_usec -= 1000000;
      d->tv_sec += 1;
    }
  return d;
}

/* Do "cpu = ((user + sys) * 10000) / real;" with timevals.
   Barely-tested code from Deven T. Corzine <deven@ties.org>. */
int
timeval_to_cpu (rt, ut, st)
     struct timeval *rt, *ut, *st;  /* real, user, sys */
{
  struct timeval t1, t2;
  register int i;

  addtimeval (&t1, ut, st);
  t2.tv_sec = rt->tv_sec;
  t2.tv_usec = rt->tv_usec;

  for (i = 0; i < 6; i++)
    {
      if ((t1.tv_sec > 99999999) || (t2.tv_sec > 99999999))
    break;
      t1.tv_sec *= 10;
      t1.tv_sec += t1.tv_usec / 100000;
      t1.tv_usec *= 10;
      t1.tv_usec %= 1000000;
      t2.tv_sec *= 10;
      t2.tv_sec += t2.tv_usec / 100000;
      t2.tv_usec *= 10;
      t2.tv_usec %= 1000000;
    }
  for (i = 0; i < 4; i++)
    {
      if (t1.tv_sec < 100000000)
    t1.tv_sec *= 10;
      else
    t2.tv_sec /= 10;
    }

  return ((t2.tv_sec == 0) ? 0 : t1.tv_sec / t2.tv_sec);
}  

/* Convert a pointer to a struct timeval to seconds and thousandths of a
   second, returning the values in *SP and *SFP, respectively.  This does
   rounding on the fractional part, not just truncation to three places. */
void
timeval_to_secs (tvp, sp, sfp)
     struct timeval *tvp;
     time_t *sp;
     int *sfp;
{
  int rest;

  *sp = tvp->tv_sec;

  *sfp = tvp->tv_usec % 1000000;    /* pretty much a no-op */
  rest = *sfp % 1000;
  *sfp = (*sfp * 1000) / 1000000;
  if (rest >= 500)
    *sfp += 1;

  /* Sanity check */
  if (*sfp >= 1000)
    {
      *sp += 1;
      *sfp -= 1000;
    }
}
  
/* Print the contents of a struct timeval * in a standard way to stdio
   stream FP.  */
void
print_timeval (fp, tvp)
     FILE *fp;
     struct timeval *tvp;
{
  time_t timestamp;
  long minutes;
  int seconds, seconds_fraction;

  timeval_to_secs (tvp, &timestamp, &seconds_fraction);

  minutes = timestamp / 60;
  seconds = timestamp % 60;

  fprintf (fp, "%ldm%d.%03ds",  minutes, seconds, seconds_fraction);
}
#endif /* HAVE_TIMEVAL */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/clock.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/clock.c used in: Step 1169
/* clock.c - operations on struct tms and clock_t's */

/* Copyright (C) 1999 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA */

#include <config.h>

#if defined (HAVE_TIMES)

#include <sys/types.h>
#include <posixtime.h>

#if defined (HAVE_SYS_TIMES_H)
#  include <sys/times.h>
#endif

#include <stdio.h>
#include <stdc.h>

extern long get_clk_tck __P((void));

void
clock_t_to_secs (t, sp, sfp)
     clock_t t;
     time_t *sp;
     int *sfp;
{
  static long clk_tck = -1;

  if (clk_tck == -1)
    clk_tck = get_clk_tck ();

  *sfp = t % clk_tck;
  *sfp = (*sfp * 1000) / clk_tck;

  *sp = t / clk_tck;

  /* Sanity check */
  if (*sfp >= 1000)
    {
      *sp += 1;
      *sfp -= 1000;
    }
}

/* Print the time defined by a clock_t (returned by the `times' and `time'
   system calls) in a standard way to stdio stream FP.  This is scaled in
   terms of the value of CLK_TCK, which is what is returned by the
   `times' call. */
void
print_clock_t (fp, t)
     FILE *fp;
     clock_t t;
{
  time_t timestamp;
  long minutes;
  int seconds, seconds_fraction;

  clock_t_to_secs (t, &timestamp, &seconds_fraction);

  minutes = timestamp / 60;
  seconds = timestamp % 60;

  fprintf (fp, "%ldm%d.%03ds",  minutes, seconds, seconds_fraction);
}
#endif /* HAVE_TIMES */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/makepath.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/makepath.c used in: Step 1170
/* makepath.c - glue PATH and DIR together into a full pathname. */

/* Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <bashansi.h>
#include "shell.h"

#include <tilde/tilde.h>

#ifndef NULL
#  define NULL 0
#endif

/* MAKE SURE THESE AGREE WITH ../../externs.h. */

#ifndef MP_DOTILDE
#  define MP_DOTILDE    0x01
#  define MP_DOCWD  0x02
#  define MP_RMDOT  0x04
#endif

extern char *get_working_directory __P((char *));

/* Take PATH, an element from, e.g., $CDPATH, and DIR, a directory name,
   and paste them together into PATH/DIR.  Tilde expansion is performed on
   PATH if (flags & MP_DOTILDE) is non-zero.  If PATH is NULL or the empty
   string, it is converted to the current directory.  A full pathname is
   used if (flags & MP_DOCWD) is non-zero, otherwise `./' is used.  If
   (flags & MP_RMDOT) is non-zero, any `./' is removed from the beginning
   of DIR. */

#define MAKEDOT() \
  do { \
    xpath = (char *)xmalloc (2); \
    xpath[0] = '.'; \
    xpath[1] = '\0'; \
    pathlen = 1; \
  } while (0)

char *
sh_makepath (path, dir, flags)
     const char *path, *dir;
     int flags;
{
  int dirlen, pathlen;
  char *ret, *xpath, *xdir, *r, *s;

  if (path == 0 || *path == '\0')
    {
      if (flags & MP_DOCWD)
    {
      xpath = get_working_directory ("sh_makepath");
      if (xpath == 0)
        {
          ret = get_string_value ("PWD");
          if (ret)
        xpath = savestring (ret);
        }
      if (xpath == 0)
        MAKEDOT();
      else
        pathlen = strlen (xpath);
    }
      else
    MAKEDOT();
    }
  else
    {
      xpath = ((flags & MP_DOTILDE) && *path == '~') ? bash_tilde_expand (path, 0) : (char *)path;
      pathlen = strlen (xpath);
    }

  xdir = (char *)dir;
  dirlen = strlen (xdir);
  if ((flags & MP_RMDOT) && dir[0] == '.' && dir[1] == '/')
    {
      xdir += 2;
      dirlen -= 2;
    }

  r = ret = (char *)xmalloc (2 + dirlen + pathlen);
  s = xpath;
  while (*s)
    *r++ = *s++;
  if (s[-1] != '/')
    *r++ = '/';      
  s = xdir;
  while (*r++ = *s++)
    ;
  if (xpath != path)
    free (xpath);
  return (ret);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/pathcanon.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/pathcanon.c used in: Step 1171
/* pathcanon.c -- Canonicalize and manipulate pathnames. */

/* Copyright (C) 2000 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include <bashtypes.h>
#ifndef _MINIX
#  include <sys/param.h>
#endif
#include <posixstat.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <filecntl.h>
#include <bashansi.h>
#include <stdio.h>
#include <chartypes.h>

#include "shell.h"

#if defined (__CYGWIN__)
#include <sys/cygwin.h>

static int
_is_cygdrive (path)
     char *path;
{
  static char user[MAXPATHLEN];
  static char system[MAXPATHLEN];
  static int first_time = 1;

  /* If the path is the first part of a network path, treat it as
     existing. */
  if (path[0] == '/' && path[1] == '/' && !strchr (path + 2, '/'))
    return 1; 
  /* Otherwise check for /cygdrive prefix. */
  if (first_time)
    {
      char user_flags[MAXPATHLEN];
      char system_flags[MAXPATHLEN];
      /* Get the cygdrive info */
      cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system, user_flags, system_flags);
      first_time = 0;
    }
  return !strcasecmp (path, user) || !strcasecmp (path, system);
}
#endif /* __CYGWIN__ */ 

/* Return 1 if PATH corresponds to a directory.  A function for debugging. */
static int
_path_isdir (path)
     char *path;
{
  int l;
  struct stat sb;

  l = stat (path, &sb) == 0 && S_ISDIR (sb.st_mode);
#if defined (__CYGWIN__)
  if (l == 0)
    l = _is_cygdrive (path);
#endif
  return l;
}

/* Canonicalize PATH, and return a new path.  The new path differs from PATH
   in that:
    Multple `/'s are collapsed to a single `/'.
    Leading `./'s and trailing `/.'s are removed.
    Trailing `/'s are removed.
    Non-leading `../'s and trailing `..'s are handled by removing
    portions of the path. */

/* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */

#define DOUBLE_SLASH(p) ((p[0] == '/') && (p[1] == '/') && p[2] != '/')

char *
sh_canonpath (path, flags)
     char *path;
     int flags;
{
  char stub_char;
  char *result, *p, *q, *base, *dotdot;
  int rooted, double_slash_path;

  /* The result cannot be larger than the input PATH. */
  result = (flags & PATH_NOALLOC) ? path : savestring (path);

  /* POSIX.2 says to leave a leading `//' alone.  On cygwin, we skip over any
     leading `x:' (dos drive name). */
  if (rooted = ROOTEDPATH(path))
    {
      stub_char = DIRSEP;
#if defined (__CYGWIN__)
      base = (ISALPHA((unsigned char)result[0]) && result[1] == ':') ? result + 3 : result + 1;
#else
      base = result + 1;
#endif
      double_slash_path = DOUBLE_SLASH (path);
      base += double_slash_path;
    }
  else
    {
      stub_char = '.';
#if defined (__CYGWIN__)
      base = (ISALPHA((unsigned char)result[0]) && result[1] == ':') ? result + 2 : result;
#else
      base = result;
#endif
      double_slash_path = 0;
    }

  /*
   * invariants:
   *      base points to the portion of the path we want to modify
   *      p points at beginning of path element we're considering.
   *      q points just past the last path element we wrote (no slash).
   *      dotdot points just past the point where .. cannot backtrack
   *      any further (no slash).
   */
  p = q = dotdot = base;

  while (*p)
    {
      if (ISDIRSEP(p[0])) /* null element */
    p++;
      else if(p[0] == '.' && PATHSEP(p[1])) /* . and ./ */
    p += 1;     /* don't count the separator in case it is nul */
      else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) /* .. and ../ */
    {
      p += 2; /* skip `..' */
      if (q > dotdot)   /* can backtrack */
        {
          if (flags & PATH_CHECKDOTDOT)
        {
          char c;

          /* Make sure what we have so far corresponds to a valid
             path before we chop some of it off. */
          c = *q;
          *q = '\0';
          if (_path_isdir (result) == 0)
            {
              if ((flags & PATH_NOALLOC) == 0)
            free (result);
              return ((char *)NULL);
            }
          *q = c;
        }

          while (--q > dotdot && ISDIRSEP(*q) == 0)
        ;
        }
      else if (rooted == 0)
        {
          /* /.. is / but ./../ is .. */
          if (q != base)
        *q++ = DIRSEP;
          *q++ = '.';
          *q++ = '.';
          dotdot = q;
        }
    }
      else  /* real path element */
    {
      /* add separator if not at start of work portion of result */
      if (q != base)
        *q++ = DIRSEP;
      while (*p && (ISDIRSEP(*p) == 0))
        *q++ = *p++;
      /* Check here for a valid directory with _path_isdir. */
      if (flags & PATH_CHECKEXISTS)
        {
          char c;

          /* Make sure what we have so far corresponds to a valid
         path before we chop some of it off. */
          c = *q;
          *q = '\0';
          if (_path_isdir (result) == 0)
        {
          if ((flags & PATH_NOALLOC) == 0)
            free (result);
          return ((char *)NULL);
        }
          *q = c;
        }
    }
    }

  /* Empty string is really ``.'' or `/', depending on what we started with. */
  if (q == result)
    *q++ = stub_char;
  *q = '\0';

  /* If the result starts with `//', but the original path does not, we
     can turn the // into /.  Because of how we set `base', this should never
     be true, but it's a sanity check. */
  if (DOUBLE_SLASH(result) && double_slash_path == 0)
    {
      if (result[2] == '\0')    /* short-circuit for bare `//' */
    result[1] = '\0';
      else
    strcpy (result, result + 1);
    }

  return (result);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/pathphys.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/pathphys.c used in: Step 1172
/* pathphys.c -- Return pathname with all symlinks expanded. */

/* Copyright (C) 2000 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include <bashtypes.h>
#ifndef _MINIX
#  include <sys/param.h>
#endif
#include <posixstat.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <filecntl.h>
#include <bashansi.h>
#include <stdio.h>
#include <chartypes.h>
#include <errno.h>

#include "shell.h"

#if !defined (MAXSYMLINKS)
#  define MAXSYMLINKS 32
#endif

#if !defined (errno)
extern int errno;
#endif /* !errno */

extern char *get_working_directory __P((char *));

static int
_path_readlink (path, buf, bufsiz)
     char *path;
     char *buf;
     int bufsiz;
{
#ifdef HAVE_READLINK
  return readlink (path, buf, bufsiz);
#else
  errno = EINVAL;
  return -1;
#endif
}

/* Look for ROOTEDPATH, PATHSEP, DIRSEP, and ISDIRSEP in ../../general.h */

#define DOUBLE_SLASH(p) ((p[0] == '/') && (p[1] == '/') && p[2] != '/')

/*
 * Return PATH with all symlinks expanded in newly-allocated memory.
 * This always gets a full pathname.
 */

char *
sh_physpath (path, flags)
     char *path;
     int flags;
{
  char tbuf[PATH_MAX+1], linkbuf[PATH_MAX+1];
  char *result, *p, *q, *qsave, *qbase, *workpath;
  int double_slash_path, linklen, nlink;

  nlink = 0;
  q = result = (char *)xmalloc (PATH_MAX + 1);

  workpath = (char *)xmalloc (PATH_MAX + 1);
  strcpy (workpath, path);

  /* This always gets an absolute pathname. */

  /* POSIX.2 says to leave a leading `//' alone.  On cygwin, we skip over any
     leading `x:' (dos drive name). */
#if defined (__CYGWIN__)
  qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
#else
  qbase = workpath + 1;
#endif
  double_slash_path = DOUBLE_SLASH (workpath);
  qbase += double_slash_path;

  for (p = workpath; p < qbase; )
    *q++ = *p++;
  qbase = q;

  /*
   * invariants:
   *      qbase points to the portion of the result path we want to modify
   *      p points at beginning of path element we're considering.
   *      q points just past the last path element we wrote (no slash).
   *
   * XXX -- need to fix error checking for too-long pathnames
   */

  while (*p)
    {
      if (ISDIRSEP(p[0])) /* null element */
    p++;
      else if(p[0] == '.' && PATHSEP(p[1])) /* . and ./ */
    p += 1;     /* don't count the separator in case it is nul */
      else if (p[0] == '.' && p[1] == '.' && PATHSEP(p[2])) /* .. and ../ */
    {
      p += 2; /* skip `..' */
      if (q > qbase)
        {
          while (--q > qbase && ISDIRSEP(*q) == 0)
        ;
        }
    }
      else  /* real path element */
    {
      /* add separator if not at start of work portion of result */
      qsave = q;
      if (q != qbase)
        *q++ = DIRSEP;
      while (*p && (ISDIRSEP(*p) == 0))
        *q++ = *p++;

      *q = '\0';

      linklen = _path_readlink (result, linkbuf, PATH_MAX);
      if (linklen < 0)  /* if errno == EINVAL, it's not a symlink */
        {
          if (errno != EINVAL)
        goto error;
          continue;
        }

      /* It's a symlink, and the value is in LINKBUF. */
      nlink++;
      if (nlink > MAXSYMLINKS)
        {
#ifdef ELOOP
          errno = ELOOP;
#endif
error:
          free (result);
          free (workpath);
          return ((char *)NULL);
        }

      linkbuf[linklen] = '\0';

      /* Form the new pathname by copying the link value to a temporary
         buffer and appending the rest of `workpath'.  Reset p to point
         to the start of the rest of the path.  If the link value is an
         absolute pathname, reset p, q, and qbase.  If not, reset p
         and q. */
      strcpy (tbuf, linkbuf);
      tbuf[linklen] = '/';
      strcpy (tbuf + linklen, p);
      strcpy (workpath, tbuf);

      if (ABSPATH(linkbuf))
        {
          q = result;
          /* Duplicating some code here... */
#if defined (__CYGWIN__)
          qbase = (ISALPHA((unsigned char)workpath[0]) && workpath[1] == ':') ? workpath + 3 : workpath + 1;
#else
          qbase = workpath + 1;
#endif
          double_slash_path = DOUBLE_SLASH (workpath);
          qbase += double_slash_path;
    
          for (p = workpath; p < qbase; )
        *q++ = *p++;
          qbase = q;
        }
      else
        {
          p = workpath;
          q = qsave;
        }
    }
    }

  *q = '\0';
  free (workpath);

  /* If the result starts with `//', but the original path does not, we
     can turn the // into /.  Because of how we set `qbase', this should never
     be true, but it's a sanity check. */
  if (DOUBLE_SLASH(result) && double_slash_path == 0)
    {
      if (result[2] == '\0')    /* short-circuit for bare `//' */
    result[1] = '\0';
      else
    strcpy (result, result + 1);
    }

  return (result);
}

char *
sh_realpath (pathname, resolved)
     const char *pathname;
     char *resolved;
{
  char *tdir, *wd;

  if (pathname == 0 || *pathname == '\0')
    {
      errno = (pathname == 0) ? EINVAL : ENOENT;
      return ((char *)NULL);
    }

  if (ABSPATH (pathname) == 0)
    {
      wd = get_working_directory ("sh_realpath");
      if (wd == 0)
    return ((char *)NULL);
      tdir = sh_makepath ((char *)pathname, wd, 0);
      free (wd);
    }
  else
    tdir = savestring (pathname);

  wd = sh_physpath (tdir, 0);
  free (tdir);

  if (resolved == 0)
    return (wd);

  if (wd)
    {
      strncpy (resolved, wd, PATH_MAX - 1);
      resolved[PATH_MAX - 1] = '\0';
      return resolved;
    }
  else
    {
      resolved[0] = '\0';
      return wd;
    }
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/stringlist.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/stringlist.c used in: Step 1173
/* stringlist.c - functions to handle a generic `list of strings' structure */

/* Copyright (C) 2000-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <stdio.h>
#include <bashansi.h>

#include "shell.h"

#ifdef STRDUP
#  undef STRDUP
#endif
#define STRDUP(x)   ((x) ? savestring (x) : (char *)NULL)

/* Allocate a new STRINGLIST, with room for N strings. */

STRINGLIST *
strlist_create (n)
     int n;
{
  STRINGLIST *ret;
  register int i;

  ret = (STRINGLIST *)xmalloc (sizeof (STRINGLIST));
  if (n)
    {
      ret->list = strvec_create (n+1);
      ret->list_size = n;
      for (i = 0; i < n; i++)
    ret->list[i] = (char *)NULL;
    }
  else
    {
      ret->list = (char **)NULL;
      ret->list_size = 0;
    }
  ret->list_len = 0;
  return ret;
}

STRINGLIST *
strlist_resize (sl, n)
     STRINGLIST *sl;
     int n;
{
  register int i;

  if (sl == 0)
    return (sl = strlist_create (n));

  if (n > sl->list_size)
    {
      sl->list = strvec_resize (sl->list, n + 1);
      for (i = sl->list_size; i <= n; i++)
    sl->list[i] = (char *)NULL;
      sl->list_size = n;
    }
  return sl;
}

void
strlist_flush (sl)
     STRINGLIST *sl;
{
  if (sl == 0 || sl->list == 0)
    return;
  strvec_flush (sl->list);
  sl->list_len = 0;
}
  
void
strlist_dispose (sl)
     STRINGLIST *sl;
{
  if (sl == 0)
    return;
  if (sl->list)
    strvec_dispose (sl->list);
  free (sl);
}

int
strlist_remove (sl, s)
     STRINGLIST *sl;
     char *s;
{
  int r;

  if (sl == 0 || sl->list == 0 || sl->list_len == 0)
    return 0;

  r = strvec_remove (sl->list, s);
  if (r)
    sl->list_len--;
  return r;
}

STRINGLIST *
strlist_copy (sl)
     STRINGLIST *sl;
{
  STRINGLIST *new;
  register int i;

  if (sl == 0)
    return ((STRINGLIST *)0);
  new = strlist_create (sl->list_size);
  /* I'd like to use strvec_copy, but that doesn't copy everything. */
  if (sl->list)
    {
      for (i = 0; i < sl->list_size; i++)
    new->list[i] = STRDUP (sl->list[i]);
    }
  new->list_size = sl->list_size;
  new->list_len = sl->list_len;
  /* just being careful */
  if (new->list)
    new->list[new->list_len] = (char *)NULL;
  return new;
}

/* Return a new STRINGLIST with everything from M1 and M2. */

STRINGLIST *
strlist_merge (m1, m2)
     STRINGLIST *m1, *m2;
{
  STRINGLIST *sl;
  int i, n, l1, l2;

  l1 = m1 ? m1->list_len : 0;
  l2 = m2 ? m2->list_len : 0;

  sl = strlist_create (l1 + l2 + 1);
  for (i = n = 0; i < l1; i++, n++)
    sl->list[n] = STRDUP (m1->list[i]);
  for (i = 0; i < l2; i++, n++)
    sl->list[n] = STRDUP (m2->list[i]);
  sl->list_len = n;
  sl->list[n] = (char *)NULL;
  return (sl);
}

/* Make STRINGLIST M1 contain everything in M1 and M2. */
STRINGLIST *
strlist_append (m1, m2)
     STRINGLIST *m1, *m2;
{
  register int i, n, len1, len2;

  if (m1 == 0)
    return (m2 ? strlist_copy (m2) : (STRINGLIST *)0);

  len1 = m1->list_len;
  len2 = m2 ? m2->list_len : 0;

  if (len2)
    {
      m1 = strlist_resize (m1, len1 + len2 + 1);
      for (i = 0, n = len1; i < len2; i++, n++)
    m1->list[n] = STRDUP (m2->list[i]);
      m1->list[n] = (char *)NULL;
      m1->list_len = n;
    }

  return m1;
}

STRINGLIST *
strlist_prefix_suffix (sl, prefix, suffix)
     STRINGLIST *sl;
     char *prefix, *suffix;
{
  int plen, slen, tlen, llen, i;
  char *t;

  if (sl == 0 || sl->list == 0 || sl->list_len == 0)
    return sl;

  plen = STRLEN (prefix);
  slen = STRLEN (suffix);

  if (plen == 0 && slen == 0)
    return (sl);

  for (i = 0; i < sl->list_len; i++)
    {
      llen = STRLEN (sl->list[i]);
      tlen = plen + llen + slen + 1;
      t = (char *)xmalloc (tlen + 1);
      if (plen)
    strcpy (t, prefix);
      strcpy (t + plen, sl->list[i]);
      if (slen)
    strcpy (t + plen + llen, suffix);
      free (sl->list[i]);
      sl->list[i] = t;
    }

  return (sl);   
}
   
void
strlist_print (sl, prefix)
     STRINGLIST *sl;
     char *prefix;
{
  register int i;

  if (sl == 0)
    return;
  for (i = 0; i < sl->list_len; i++)
    printf ("%s%s\n", prefix ? prefix : "", sl->list[i]);
}

void
strlist_walk (sl, func)
     STRINGLIST *sl;
     sh_strlist_map_func_t *func;
{
  register int i;

  if (sl == 0)
    return;
  for (i = 0; i < sl->list_len; i++)
    if ((*func)(sl->list[i]) < 0)
      break;
} 
     
void
strlist_sort (sl)
     STRINGLIST *sl;
{
  if (sl == 0 || sl->list_len == 0 || sl->list == 0)
    return;
  strvec_sort (sl->list);
}

STRINGLIST *
strlist_from_word_list (list, alloc, starting_index, ip)
     WORD_LIST *list;
     int alloc, starting_index, *ip;
{
  STRINGLIST *ret;
  int slen, len;

  if (list == 0)
    {
      if (ip)
        *ip = 0;
      return ((STRINGLIST *)0);
    }
  slen = list_length (list);
  ret = (STRINGLIST *)xmalloc (sizeof (STRINGLIST));
  ret->list = strvec_from_word_list (list, alloc, starting_index, &len);
  ret->list_size = slen + starting_index;
  ret->list_len = len;
  if (ip)
    *ip = len;
  return ret;
}

WORD_LIST *
strlist_to_word_list (sl, alloc, starting_index)
     STRINGLIST *sl;
     int alloc, starting_index;
{
  WORD_LIST *list;

  if (sl == 0 || sl->list == 0)
    return ((WORD_LIST *)NULL);

  list = strvec_to_word_list (sl->list, alloc, starting_index);
  return list;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/stringvec.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/stringvec.c used in: Step 1174
/* stringvec.c - functions for managing arrays of strings. */

/* Copyright (C) 2000-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include <bashtypes.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <bashansi.h>
#include <stdio.h>
#include <chartypes.h>

#include "shell.h"

/* Allocate an array of strings with room for N members. */
char **
strvec_create (n)
     int n;
{
  return ((char **)xmalloc ((n) * sizeof (char *)));
}

char **
strvec_resize (array, nsize)
     char **array;
     int nsize;
{
  return ((char **)xrealloc (array, nsize * sizeof (char *)));
}

/* Return the length of ARRAY, a NULL terminated array of char *. */
int
strvec_len (array)
     char **array;
{
  register int i;

  for (i = 0; array[i]; i++);
  return (i);
}

/* Free the contents of ARRAY, a NULL terminated array of char *. */
void
strvec_flush (array)
     char **array;
{
  register int i;

  if (array == 0)
    return;

  for (i = 0; array[i]; i++)
    free (array[i]);
}

void
strvec_dispose (array)
     char **array;
{
  if (array == 0)
    return;

  strvec_flush (array);
  free (array);
}

int
strvec_remove (array, name)
     char **array, *name;
{
  register int i, j;
  char *x;

  if (array == 0)
    return 0;

  for (i = 0; array[i]; i++)
    if (STREQ (name, array[i]))
      {
    x = array[i];
    for (j = i; array[j]; j++)
      array[j] = array[j + 1];
    free (x);
    return 1;
      }
  return 0;
}

#ifdef INCLUDE_UNUSED
/* Find NAME in ARRAY.  Return the index of NAME, or -1 if not present.
   ARRAY should be NULL terminated. */
int
strvec_search (array, name)
     char **array, *name;
{
  int i;

  for (i = 0; array[i]; i++)
    if (STREQ (name, array[i]))
      return (i);

  return (-1);
}
#endif

/* Allocate and return a new copy of ARRAY and its contents. */
char **
strvec_copy (array)
     char **array;
{
  register int i;
  int len;
  char **ret;

  len = strvec_len (array);

  ret = (char **)xmalloc ((len + 1) * sizeof (char *));
  for (i = 0; array[i]; i++)
    ret[i] = savestring (array[i]);
  ret[i] = (char *)NULL;

  return (ret);
}

/* Comparison routine for use with qsort() on arrays of strings.  Uses
   strcoll(3) if available, otherwise it uses strcmp(3). */
int
strvec_strcmp (s1, s2)
     register char **s1, **s2;
{
#if defined (HAVE_STRCOLL)
   return (strcoll (*s1, *s2));
#else /* !HAVE_STRCOLL */
  int result;

  if ((result = **s1 - **s2) == 0)
    result = strcmp (*s1, *s2);

  return (result);
#endif /* !HAVE_STRCOLL */
}

/* Sort ARRAY, a null terminated array of pointers to strings. */
void
strvec_sort (array)
     char **array;
{
  qsort (array, strvec_len (array), sizeof (char *), (QSFUNC *)strvec_strcmp);
}

/* Cons up a new array of words.  The words are taken from LIST,
   which is a WORD_LIST *.  If ALLOC is true, everything is malloc'ed,
   so you should free everything in this array when you are done.
   The array is NULL terminated.  If IP is non-null, it gets the
   number of words in the returned array.  STARTING_INDEX says where
   to start filling in the returned array; it can be used to reserve
   space at the beginning of the array. */

char **
strvec_from_word_list (list, alloc, starting_index, ip)
     WORD_LIST *list;
     int alloc, starting_index, *ip;
{
  int count;
  char **array;

  count = list_length (list);
  array = (char **)xmalloc ((1 + count + starting_index) * sizeof (char *));

  for (count = 0; count < starting_index; count++)
    array[count] = (char *)NULL;
  for (count = starting_index; list; count++, list = list->next)
    array[count] = alloc ? savestring (list->word->word) : list->word->word;
  array[count] = (char *)NULL;

  if (ip)
    *ip = count;
  return (array);
}

/* Convert an array of strings into the form used internally by the shell.
   ALLOC means to allocate new storage for each WORD_DESC in the returned
   list rather than copy the values in ARRAY.  STARTING_INDEX says where
   in ARRAY to begin. */

WORD_LIST *
strvec_to_word_list (array, alloc, starting_index)
     char **array;
     int alloc, starting_index;
{
  WORD_LIST *list;
  WORD_DESC *w;
  int i, count;

  if (array == 0 || array[0] == 0)
    return (WORD_LIST *)NULL;

  for (count = 0; array[count]; count++)
    ;

  for (i = starting_index, list = (WORD_LIST *)NULL; i < count; i++)
    {
      w = make_bare_word (alloc ? array[i] : "");
      if (alloc == 0)
    {
      free (w->word);
      w->word = array[i];
    }
      list = make_word_list (w, list);
    }
  return (REVERSE_LIST (list, WORD_LIST *));
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/tmpfile.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/tmpfile.c used in: Step 1175
/*
 * tmpfile.c - functions to create and safely open temp files for the shell.
 */

/* Copyright (C) 2000 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include <bashtypes.h>
#include <posixstat.h>
#include <posixtime.h>
#include <filecntl.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <stdio.h>
#include <errno.h>

#include <shell.h>

#ifndef errno
extern int errno;
#endif

#define BASEOPENFLAGS   (O_CREAT | O_TRUNC | O_EXCL)

#define DEFAULT_TMPDIR      "." /* bogus default, should be changed */
#define DEFAULT_NAMEROOT    "shtmp"

extern pid_t dollar_dollar_pid;

static char *sys_tmpdir = (char *)NULL;
static int ntmpfiles;
static int tmpnamelen = -1;
static unsigned long filenum = 1L;

static char *
get_sys_tmpdir ()
{
  struct stat sb;

  if (sys_tmpdir)
    return sys_tmpdir;

#ifdef P_tmpdir
  sys_tmpdir = P_tmpdir;
  if (stat (sys_tmpdir, &sb) == 0)
    return sys_tmpdir;
#endif

  sys_tmpdir = "/tmp";
  if (stat (sys_tmpdir, &sb) == 0)
    return sys_tmpdir;

  sys_tmpdir = "/var/tmp";
  if (stat (sys_tmpdir, &sb) == 0)
    return sys_tmpdir;

  sys_tmpdir = "/usr/tmp";
  if (stat (sys_tmpdir, &sb) == 0)
    return sys_tmpdir;

  sys_tmpdir = DEFAULT_TMPDIR;

  return sys_tmpdir;
}

static char *
get_tmpdir (flags)
     int flags;
{
  char *tdir;

  tdir = (flags & MT_USETMPDIR) ? get_string_value ("TMPDIR") : (char *)NULL;
  if (tdir == 0)
    tdir = get_sys_tmpdir ();

#if defined (HAVE_PATHCONF) && defined (_PC_NAME_MAX)
  if (tmpnamelen == -1)
    tmpnamelen = pathconf (tdir, _PC_NAME_MAX);
#else
  tmpnamelen = 0;
#endif

  return tdir;
}

char *
sh_mktmpname (nameroot, flags)
     char *nameroot;
     int flags;
{
  char *filename, *tdir, *lroot;
  struct stat sb;
  int r, tdlen;

  filename = (char *)xmalloc (PATH_MAX + 1);
  tdir = get_tmpdir (flags);
  tdlen = strlen (tdir);

  lroot = nameroot ? nameroot : DEFAULT_NAMEROOT;

#ifdef USE_MKTEMP
  sprintf (filename, "%s/%s.XXXXXX", tdir, lroot);
  if (mktemp (filename) == 0)
    {
      free (filename);
      filename = NULL;
    }
#else  /* !USE_MKTEMP */
  while (1)
    {
      filenum = (filenum << 1) ^
        (unsigned long) time ((time_t *)0) ^
        (unsigned long) dollar_dollar_pid ^
        (unsigned long) ((flags & MT_USERANDOM) ? get_random_number () : ntmpfiles++);
      sprintf (filename, "%s/%s-%lu", tdir, lroot, filenum);
      if (tmpnamelen > 0 && tmpnamelen < 32)
    filename[tdlen + 1 + tmpnamelen] = '\0';
#  ifdef HAVE_LSTAT
      r = lstat (filename, &sb);
#  else
      r = stat (filename, &sb);
#  endif
      if (r < 0 && errno == ENOENT)
    break;
    }
#endif /* !USE_MKTEMP */

  return filename;
}

int
sh_mktmpfd (nameroot, flags, namep)
     char *nameroot;
     int flags;
     char **namep;
{
  char *filename, *tdir, *lroot;
  int fd, tdlen;

  filename = (char *)xmalloc (PATH_MAX + 1);
  tdir = get_tmpdir (flags);
  tdlen = strlen (tdir);

  lroot = nameroot ? nameroot : DEFAULT_NAMEROOT;

#ifdef USE_MKSTEMP
  sprintf (filename, "%s/%s.XXXXXX", tdir, lroot);
  fd = mkstemp (filename);
  if (fd < 0 || namep == 0)
    {
      free (filename);
      filename = NULL;
    }
  if (namep)
    *namep = filename;
  return fd;
#else /* !USE_MKSTEMP */
  do
    {
      filenum = (filenum << 1) ^
        (unsigned long) time ((time_t *)0) ^
        (unsigned long) dollar_dollar_pid ^
        (unsigned long) ((flags & MT_USERANDOM) ? get_random_number () : ntmpfiles++);
      sprintf (filename, "%s/%s-%lu", tdir, lroot, filenum);
      if (tmpnamelen > 0 && tmpnamelen < 32)
    filename[tdlen + 1 + tmpnamelen] = '\0';
      fd = open (filename, BASEOPENFLAGS | ((flags & MT_READWRITE) ? O_RDWR : O_WRONLY), 0600);
    }
  while (fd < 0 && errno == EEXIST);

  if (namep)
    *namep = filename;
  else
    free (filename);

  return fd;
#endif /* !USE_MKSTEMP */
}

FILE *
sh_mktmpfp (nameroot, flags, namep)
     char *nameroot;
     int flags;
     char **namep;
{
  int fd;
  FILE *fp;

  fd = sh_mktmpfd (nameroot, flags, namep);
  if (fd < 0)
    return ((FILE *)NULL);
  fp = fdopen (fd, (flags & MT_READWRITE) ? "w+" : "w");
  if (fp == 0)
    close (fd);
  return fp;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/include/posixtime.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/posixtime.h used in: Step 1175, Step 1202, Step 1211
/* posixtime.h -- wrapper for time.h, sys/times.h mess. */

/* Copyright (C) 1999 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#ifndef _POSIXTIME_H_
#define _POSIXTIME_H_

/* include this after config.h */
/* Some systems require this, mostly for the definition of `struct timezone'.
   For example, Dynix/ptx has that definition in <time.h> rather than
      sys/time.h */
#if defined (TIME_WITH_SYS_TIME)
#  include <sys/time.h>
#  include <time.h>
#else
#  if defined (HAVE_SYS_TIME_H)
#    include <sys/time.h>
#  else
#    include <time.h>
#  endif
#endif

#if !defined (HAVE_SYSCONF) || !defined (_SC_CLK_TCK)
#  if !defined (CLK_TCK)
#    if defined (HZ)
#      define CLK_TCK   HZ
#    else
#      define CLK_TCK   60          /* 60HZ */
#    endif
#  endif /* !CLK_TCK */
#endif /* !HAVE_SYSCONF && !_SC_CLK_TCK */

#endif /* _POSIXTIME_H_ */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/spell.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/spell.c used in: Step 1176
/* spell.c -- spelling correction for pathnames. */

/* Copyright (C) 2000 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <bashtypes.h>
#include <posixdir.h>
#include <posixstat.h>
#ifndef _MINIX
#include <sys/param.h>
#endif

#include <stdio.h>

#include <bashansi.h>
#include <maxpath.h>
#include <stdc.h>

static int mindist __P((char *, char *, char *));
static int spdist __P((char *, char *));

/*
 * `spname' and its helpers are inspired by the code in "The UNIX
 * Programming Environment", Kernighan & Pike, Prentice-Hall 1984,
 * pages 209 - 213.
 */

/*
 *  `spname' -- return a correctly spelled filename
 *
 *  int spname(char * oldname, char * newname)
 *  Returns:  -1 if no reasonable match found
 *         0 if exact match found
 *         1 if corrected
 *  Stores corrected name in `newname'.
 */
int
spname(oldname, newname)
     char *oldname;
     char *newname;
{
  char *op, *np, *p;
  char guess[PATH_MAX + 1], best[PATH_MAX + 1];

  op = oldname;
  np = newname;
  for (;;)
    {
      while (*op == '/')    /* Skip slashes */
    *np++ = *op++;
      *np = '\0';

      if (*op == '\0')    /* Exact or corrected */
    {
      /* `.' is rarely the right thing. */
      if (oldname[1] == '\0' && newname[1] == '\0' &&
        oldname[0] != '.' && newname[0] == '.')
        return -1;
      return strcmp(oldname, newname) != 0;
    }

      /* Copy next component into guess */
      for (p = guess; *op != '/' && *op != '\0'; op++)
    if (p < guess + PATH_MAX)
      *p++ = *op;
      *p = '\0';

      if (mindist(newname, guess, best) >= 3)
    return -1;  /* Hopeless */

      /*
       *  Add to end of newname
       */
      for (p = best; *np = *p++; np++)
    ;
    }
}

/*
 *  Search directory for a guess
 */
static int
mindist(dir, guess, best)
     char *dir;
     char *guess;
     char *best;
{
  DIR *fd;
  struct dirent *dp;
  int dist, x;

  dist = 3;    /* Worst distance */
  if (*dir == '\0')
    dir = ".";

  if ((fd = opendir(dir)) == NULL)
    return dist;

  while ((dp = readdir(fd)) != NULL)
    {
      /*
       *  Look for a better guess.  If the new guess is as
       *  good as the current one, we take it.  This way,
       *  any single character match will be a better match
       *  than ".".
       */
      x = spdist(dp->d_name, guess);
      if (x <= dist && x != 3)
    {
      strcpy(best, dp->d_name);
      dist = x;
      if (dist == 0)    /* Exact match */
        break;
    }
    }
  (void)closedir(fd);

  /* Don't return `.' */
  if (best[0] == '.' && best[1] == '\0')
    dist = 3;
  return dist;
}

/*
 *  `spdist' -- return the "distance" between two names.
 *
 *  int spname(char * oldname, char * newname)
 *  Returns:  0 if strings are identical
 *      1 if two characters are transposed
 *      2 if one character is wrong, added or deleted
 *      3 otherwise
 */
static int
spdist(cur, new)
     char *cur, *new;
{
  while (*cur == *new)
    {
      if (*cur == '\0')
    return 0;    /* Exact match */
      cur++;
      new++;
    }

  if (*cur)
    {
      if (*new)
    {
      if (cur[1] && new[1] && cur[0] == new[1] && cur[1] == new[0] && strcmp (cur + 2, new + 2) == 0)
        return 1;  /* Transposition */

      if (strcmp (cur + 1, new + 1) == 0)
        return 2;  /* One character mismatch */
    }

      if (strcmp(&cur[1], &new[0]) == 0)
    return 2;    /* Extra character */
    }

  if (*new && strcmp(cur, new + 1) == 0)
    return 2;      /* Missing character */

  return 3;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strtrans.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strtrans.c used in: Step 1177
/* strtrans.c - Translate and untranslate strings with ANSI-C escape
        sequences. */

/* Copyright (C) 2000
   Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <bashansi.h>
#include <stdio.h>
#include <chartypes.h>

#include "shell.h"

#ifdef ESC
#undef ESC
#endif
#define ESC '\033'  /* ASCII */

/* Convert STRING by expanding the escape sequences specified by the
   ANSI C standard.  If SAWC is non-null, recognize `\c' and use that
   as a string terminator.  If we see \c, set *SAWC to 1 before
   returning.  LEN is the length of STRING.  If (FLAGS&1) is non-zero,
   that we're translating a string for `echo -e', and therefore should not
   treat a single quote as a character that may be escaped with a backslash.
   If (FLAGS&2) is non-zero, we're expanding for the parser and want to
   quote CTLESC and CTLNUL with CTLESC */
char *
ansicstr (string, len, flags, sawc, rlen)
     char *string;
     int len, flags, *sawc, *rlen;
{
  int c, temp;
  char *ret, *r, *s;

  if (string == 0 || *string == '\0')
    return ((char *)NULL);

  ret = (char *)xmalloc (2*len + 1);    /* 2*len for possible CTLESC */
  for (r = ret, s = string; s && *s; )
    {
      c = *s++;
      if (c != '\\' || *s == '\0')
    *r++ = c;
      else
    {
      switch (c = *s++)
        {
#if defined (__STDC__)
        case 'a': c = '\a'; break;
        case 'v': c = '\v'; break;
#else
        case 'a': c = '\007'; break;
        case 'v': c = (int) 0x0B; break;
#endif
        case 'b': c = '\b'; break;
        case 'e': case 'E':     /* ESC -- non-ANSI */
          c = ESC; break;
        case 'f': c = '\f'; break;
        case 'n': c = '\n'; break;
        case 'r': c = '\r'; break;
        case 't': c = '\t'; break;
        case '0': case '1': case '2': case '3':
        case '4': case '5': case '6': case '7':
          /* If (FLAGS & 1), we're translating a string for echo -e (or
         the equivalent xpg_echo option), so we obey the SUSv3/
         POSIX-2001 requirement and accept 0-3 octal digits after
         a leading `0'. */
          temp = 2 + ((flags & 1) && (c == '0'));
          for (c -= '0'; ISOCTAL (*s) && temp--; s++)
        c = (c * 8) + OCTVALUE (*s);
          c &= 0xFF;
          break;
        case 'x':           /* Hex digit -- non-ANSI */
          for (temp = 2, c = 0; ISXDIGIT ((unsigned char)*s) && temp--; s++)
        c = (c * 16) + HEXVALUE (*s);
          /* \x followed by non-hex digits is passed through unchanged */
          if (temp == 2)
        {
          *r++ = '\\';
          c = 'x';
        }
          c &= 0xFF;
          break;
        case '\\':
          break;
        case '\'':
          if (flags & 1)
        *r++ = '\\';
          break;
        case 'c':
          if (sawc)
        {
          *sawc = 1;
          *r = '\0';
          if (rlen)
            *rlen = r - ret;
          return ret;
        }
          else if ((flags & 1) == 0 && (c = *s))
        {
          s++;
          c = TOCTRL(c);
          break;
        }
        /*FALLTHROUGH*/
        default:  *r++ = '\\'; break;
        }
      if ((flags & 2) && (c == CTLESC || c == CTLNUL))
        *r++ = CTLESC;
      *r++ = c;
    }
    }
  *r = '\0';
  if (rlen)
    *rlen = r - ret;
  return ret;
}

/* Take a string STR, possibly containing non-printing characters, and turn it
   into a $'...' ANSI-C style quoted string.  Returns a new string. */
char *
ansic_quote (str, flags, rlen)
     char *str;
     int flags, *rlen;
{
  char *r, *ret, *s;
  int l, rsize, t;
  unsigned char c;

  if (str == 0 || *str == 0)
    return ((char *)0);

  l = strlen (str);
  rsize = 4 * l + 4;
  r = ret = (char *)xmalloc (rsize);

  *r++ = '$';
  *r++ = '\'';

  for (s = str, l = 0; *s; s++)
    {
      c = *s;
      l = 1;        /* 1 == add backslash; 0 == no backslash */
      switch (c)
    {
    case ESC: c = 'E'; break;
#ifdef __STDC__
    case '\a': c = 'a'; break;
    case '\v': c = 'v'; break;
#else
    case '\007': c = 'a'; break;
    case 0x0b: c = 'v'; break;
#endif

    case '\b': c = 'b'; break;
    case '\f': c = 'f'; break;
    case '\n': c = 'n'; break;
    case '\r': c = 'r'; break;
    case '\t': c = 't'; break;
    case '\\':
    case '\'':
      break;
    default:
      if (ISPRINT (c) == 0)
        {
          *r++ = '\\';
          *r++ = TOCHAR ((c >> 6) & 07);
          *r++ = TOCHAR ((c >> 3) & 07);
          *r++ = TOCHAR (c & 07);
          continue;
        }
      l = 0;
      break;
    }
      if (l)
    *r++ = '\\';
      *r++ = c;
    }

  *r++ = '\'';
  *r = '\0';
  if (rlen)
    *rlen = r - ret;
  return ret;
}

/* return 1 if we need to quote with $'...' because of non-printing chars. */
int
ansic_shouldquote (string)
     const char *string;
{
  const char *s;
  unsigned char c;

  if (string == 0)
    return 0;

  for (s = string; c = *s; s++)
    if (ISPRINT (c) == 0)
      return 1;

  return 0;
}

/* $'...' ANSI-C expand the portion of STRING between START and END and
   return the result.  The result cannot be longer than the input string. */
char *
ansiexpand (string, start, end, lenp)
     char *string;
     int start, end, *lenp;
{
  char *temp, *t;
  int len, tlen;

  temp = (char *)xmalloc (end - start + 1);
  for (tlen = 0, len = start; len < end; )
    temp[tlen++] = string[len++];
  temp[tlen] = '\0';

  if (*temp)
    {
      t = ansicstr (temp, tlen, 2, (int *)NULL, lenp);
      free (temp);
      return (t);
    }
  else
    {
      if (lenp)
    *lenp = 0;
      return (temp);
    }
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strindex.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strindex.c used in: Step 1178
/* strindex.c - Find if one string appears as a substring of another string,
        without regard to case. */

/* Copyright (C) 2000
   Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include <bashansi.h>
#include <chartypes.h>

#include <stdc.h>

/* Determine if s2 occurs in s1.  If so, return a pointer to the
   match in s1.  The compare is case insensitive.  This is a
   case-insensitive strstr(3). */
char *
strindex (s1, s2)
     const char *s1;
     const char *s2;
{
  register int i, l, len, c;

  c = TOLOWER ((unsigned char)s2[0]);
  len = strlen (s1);
  l = strlen (s2);
  for (i = 0; (len - i) >= l; i++)
    if ((TOLOWER ((unsigned char)s1[i]) == c) && (strncasecmp (s1 + i, s2, l) == 0))
      return ((char *)s1 + i);
  return ((char *)0);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/shquote.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/shquote.c used in: Step 1179
/* Copyright (C) 1999 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>

#include "syntax.h"
#include <xmalloc.h>

/* **************************************************************** */
/*                                  */
/*   Functions for quoting strings to be re-read as input       */
/*                                  */
/* **************************************************************** */

/* Return a new string which is the single-quoted version of STRING.
   Used by alias and trap, among others. */
char *
sh_single_quote (string)
     char *string;
{
  register int c;
  char *result, *r, *s;

  result = (char *)xmalloc (3 + (4 * strlen (string)));
  r = result;
  *r++ = '\'';

  for (s = string; s && (c = *s); s++)
    {
      *r++ = c;

      if (c == '\'')
    {
      *r++ = '\\';  /* insert escaped single quote */
      *r++ = '\'';
      *r++ = '\'';  /* start new quoted string */
    }
    }

  *r++ = '\'';
  *r = '\0';

  return (result);
}

/* Quote STRING using double quotes.  Return a new string. */
char *
sh_double_quote (string)
     char *string;
{
  register unsigned char c;
  char *result, *r, *s;

  result = (char *)xmalloc (3 + (2 * strlen (string)));
  r = result;
  *r++ = '"';

  for (s = string; s && (c = *s); s++)
    {
      if (sh_syntaxtab[c] & CBSDQUOTE)
    *r++ = '\\';

      *r++ = c;
    }

  *r++ = '"';
  *r = '\0';

  return (result);
}

/* Remove backslashes that are quoting characters that are special between
   double quotes.  Return a new string. */
char *
sh_un_double_quote (string)
     char *string;
{
  register int c, pass_next;
  char *result, *r, *s;

  r = result = (char *)xmalloc (strlen (string) + 1);

  for (pass_next = 0, s = string; s && (c = *s); s++)
    {
      if (pass_next)
    {
      *r++ = c;
      pass_next = 0;
      continue;
    }
      if (c == '\\' && (sh_syntaxtab[(unsigned char) s[1]] & CBSDQUOTE))
    {
      pass_next = 1;
      continue;
    }
      *r++ = c;
    }

  *r = '\0';
  return result;
}

/* Quote special characters in STRING using backslashes.  Return a new
   string. */
char *
sh_backslash_quote (string)
     char *string;
{
  int c;
  char *result, *r, *s;

  result = (char *)xmalloc (2 * strlen (string) + 1);

  for (r = result, s = string; s && (c = *s); s++)
    {
      switch (c)
    {
    case ' ': case '\t': case '\n':     /* IFS white space */
    case '\'': case '"': case '\\':     /* quoting chars */
    case '|': case '&': case ';':       /* shell metacharacters */
    case '(': case ')': case '<': case '>':
    case '!': case '{': case '}':       /* reserved words */
    case '*': case '[': case '?': case ']': /* globbing chars */
    case '^':
    case '$': case '`':         /* expansion chars */
    case ',':               /* brace expansion */
      *r++ = '\\';
      *r++ = c;
      break;
#if 0
    case '~':               /* tilde expansion */
      if (s == string || s[-1] == '=' || s[-1] == ':')
        *r++ = '\\';
      *r++ = c;
      break;
#endif
    case '#':               /* comment char */
      if (s == string)
        *r++ = '\\';
      /* FALLTHROUGH */
    default:
      *r++ = c;
      break;
    }
    }

  *r = '\0';
  return (result);
}

#if defined (PROMPT_STRING_DECODE)
/* Quote characters that get special treatment when in double quotes in STRING
   using backslashes.  Return a new string. */
char *
sh_backslash_quote_for_double_quotes (string)
     char *string;
{
  unsigned char c;
  char *result, *r, *s;

  result = (char *)xmalloc (2 * strlen (string) + 1);

  for (r = result, s = string; s && (c = *s); s++)
    {
      if (sh_syntaxtab[c] & CBSDQUOTE)
    *r++ = '\\';

      *r++ = c;
    }

  *r = '\0';
  return (result);
}
#endif /* PROMPT_STRING_DECODE */

int
sh_contains_shell_metas (string)
     char *string;
{
  char *s;

  for (s = string; s && *s; s++)
    {
      switch (*s)
    {
    case ' ': case '\t': case '\n':     /* IFS white space */
    case '\'': case '"': case '\\':     /* quoting chars */
    case '|': case '&': case ';':       /* shell metacharacters */
    case '(': case ')': case '<': case '>':
    case '!': case '{': case '}':       /* reserved words */
    case '*': case '[': case '?': case ']': /* globbing chars */
    case '^':
    case '$': case '`':         /* expansion chars */
      return (1);
    case '~':               /* tilde expansion */
      if (s == string || s[-1] == '=' || s[-1] == ':')
        return (1);
      break;
    case '#':
      if (s == string)          /* comment char */
        return (1);
      /* FALLTHROUGH */
    default:
      break;
    }
    }

  return (0);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/mailstat.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/mailstat.c used in: Step 1181
/* mailstat.c -- stat a mailbox file, handling maildir-type mail directories */

/* Copyright (C) 2001 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>
 
#include <stdio.h>
#include <errno.h>

#include <bashtypes.h>
#include <posixstat.h>
#include <posixdir.h>
#include <bashansi.h>

#ifndef _MINIX
#  include <sys/param.h>
#endif

#include <maxpath.h>

/*
 *     Stat a file. If it's a maildir, check all messages
 *     in the maildir and present the grand total as a file.
 *     The fields in the 'struct stat' are from the mail directory.
 *     The following fields are emulated:
 *
 *     st_nlink always 1, unless st_blocks is not present, in which case it's
 *      the total number of messages
 *     st_size   total number of bytes in all files
 *     st_blocks       total number of messages, if present in struct stat
 *     st_atime access time of newest file in maildir
 *     st_mtime modify time of newest file in maildir
 *     st_mode   S_IFDIR changed to S_IFREG
 *
 *     This is good enough for most mail-checking applications.
 */

int
mailstat(path, st)
     const char *path;
     struct stat *st;
{
  static struct stat st_new_last, st_ret_last;
  struct stat st_ret, st_tmp;
  DIR *dd;
  struct dirent *fn;
  char dir[PATH_MAX * 2], file[PATH_MAX * 2];
  int i, l;
  time_t atime, mtime;

  atime = mtime = 0;

  /* First see if it's a directory. */
  if ((i = stat(path, st)) != 0 || S_ISDIR(st->st_mode) == 0)
    return i;

  if (strlen(path) > sizeof(dir) - 5)
    {
#ifdef ENAMETOOLONG
      errno = ENAMETOOLONG;
#else
      errno = EINVAL;
#endif
      return -1;
    }

  st_ret = *st;
  st_ret.st_nlink = 1;
  st_ret.st_size  = 0;
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
  st_ret.st_blocks  = 0;
#else
  st_ret.st_nlink = 0;
#endif
  st_ret.st_mode  &= ~S_IFDIR;
  st_ret.st_mode  |= S_IFREG;

  /* See if cur/ is present */
  sprintf(dir, "%s/cur", path);
  if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0)
    return 0;
  st_ret.st_atime = st_tmp.st_atime;

  /* See if tmp/ is present */
  sprintf(dir, "%s/tmp", path);
  if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0)
    return 0;
  st_ret.st_mtime = st_tmp.st_mtime;

  /* And new/ */
  sprintf(dir, "%s/new", path);
  if (stat(dir, &st_tmp) || S_ISDIR(st_tmp.st_mode) == 0)
    return 0;
  st_ret.st_mtime = st_tmp.st_mtime;

  /* Optimization - if new/ didn't change, nothing else did. */
  if (st_tmp.st_dev == st_new_last.st_dev &&
      st_tmp.st_ino == st_new_last.st_ino &&
      st_tmp.st_atime == st_new_last.st_atime &&
      st_tmp.st_mtime == st_new_last.st_mtime)
    {
      *st = st_ret_last;
      return 0;
    }
  st_new_last = st_tmp;

  /* Loop over new/ and cur/ */
  for (i = 0; i < 2; i++)
    {
      sprintf(dir, "%s/%s", path, i ? "cur" : "new");
      sprintf(file, "%s/", dir);
      l = strlen(file);
      if ((dd = opendir(dir)) == NULL)
    return 0;
      while ((fn = readdir(dd)) != NULL)
    {
      if (fn->d_name[0] == '.' || strlen(fn->d_name) + l >= sizeof(file))
        continue;
      strcpy(file + l, fn->d_name);
      if (stat(file, &st_tmp) != 0)
        continue;
      st_ret.st_size += st_tmp.st_size;
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
      st_ret.st_blocks++;
#else
      st_ret.st_nlink++;
#endif
      if (st_tmp.st_atime != st_tmp.st_mtime && st_tmp.st_atime > atime)
        atime = st_tmp.st_atime;
      if (st_tmp.st_mtime > mtime)
        mtime = st_tmp.st_mtime;
    }
      closedir(dd);
    }

    if (atime)
      st_ret.st_atime = atime;
    if (mtime)
      st_ret.st_mtime = mtime;

    *st = st_ret_last = st_ret;
    return 0;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/include/posixdir.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/posixdir.h used in: Step 1181
/* posixdir.h -- Posix directory reading includes and defines. */

/* Copyright (C) 1987,1991 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

/* This file should be included instead of <dirent.h> or <sys/dir.h>. */

#if !defined (_POSIXDIR_H_)
#define _POSIXDIR_H_

#if defined (HAVE_DIRENT_H)
#  include <dirent.h>
#  define D_NAMLEN(d)   (strlen ((d)->d_name))
#else
#  if defined (HAVE_SYS_NDIR_H)
#    include <sys/ndir.h>
#  endif
#  if defined (HAVE_SYS_DIR_H)
#    include <sys/dir.h>
#  endif
#  if defined (HAVE_NDIR_H)
#    include <ndir.h>
#  endif
#  if !defined (dirent)
#    define dirent direct
#  endif /* !dirent */
#  define D_NAMLEN(d)   ((d)->d_namlen)
#endif /* !HAVE_DIRENT_H */

#if defined (STRUCT_DIRENT_HAS_D_INO) && !defined (STRUCT_DIRENT_HAS_D_FILENO)
#  define d_fileno d_ino
#endif

#if defined (_POSIX_SOURCE) && (!defined (STRUCT_DIRENT_HAS_D_INO) || defined (BROKEN_DIRENT_D_INO))
/* Posix does not require that the d_ino field be present, and some
   systems do not provide it. */
#  define REAL_DIR_ENTRY(dp) 1
#else
#  define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
#endif /* _POSIX_SOURCE */

#endif /* !_POSIXDIR_H_ */

https://git.savannah.gnu.org/cgit/bash.git/tree/include/maxpath.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/maxpath.h used in: Step 1181
/* maxpath.h - Find out what this system thinks PATH_MAX and NAME_MAX are. */

/* Copyright (C) 1993 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#if !defined (_MAXPATH_H_)
#define _MAXPATH_H_

/* These values are supposed to be in <limits.h> or one of the files
   it includes. */
#if defined (HAVE_LIMITS_H)
#  include <limits.h>
#endif /* !HAVE_LIMITS_H */

/* If PATH_MAX is not defined, look for MAXPATHLEN */
#if !defined (PATH_MAX)
#  if defined (HAVE_SYS_PARAM_H)
#    include <sys/param.h>
#    define maxpath_param_h
#  endif
#  if defined (MAXPATHLEN) && !defined (PATH_MAX)
#    define PATH_MAX MAXPATHLEN
#  endif /* MAXPATHLEN && !PATH_MAX */
#endif /* !PATH_MAX */

/* If NAME_MAX is not defined, look for MAXNAMLEN */
#if !defined (NAME_MAX)
#  if defined (HAVE_SYS_PARAM_H) && !defined (maxpath_param_h)
#    include <sys/param.h>
#  endif
#  if defined (MAXNAMLEN) && !defined (NAME_MAX)
#    define NAME_MAX MAXNAMLEN
#  endif /* MAXNAMLEN && !NAME_MAX */
#endif /* !NAME_MAX */

/* Default POSIX values */
#if !defined (PATH_MAX) && defined (_POSIX_PATH_MAX)
#  define PATH_MAX _POSIX_PATH_MAX
#endif

#if !defined (NAME_MAX) && defined (_POSIX_NAME_MAX)
#  define NAME_MAX _POSIX_NAME_MAX
#endif


/* Default values */
#if !defined (PATH_MAX)
#  define PATH_MAX 1024
#endif

#if !defined (NAME_MAX)
#  define NAME_MAX 14
#endif

#if PATH_MAX < 1024
#  undef PATH_MAX
#  define PATH_MAX 1024
#endif

#endif /* _MAXPATH_H_ */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/fmtulong.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/fmtulong.c used in: Step 1182, Step 1188
/* fmtulong.c -- Convert unsigned long int to string. */

/* Copyright (C) 1998-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#if defined (HAVE_LIMITS_H)
#  include <limits.h>
#endif

#include <bashansi.h>
#ifdef HAVE_STDDEF_H
#  include <stddef.h>
#endif

#ifdef HAVE_STDINT_H
#  include <stdint.h>
#endif
#include <chartypes.h>
#include <errno.h>

#include "stdc.h"

#include <typemax.h>

#ifndef errno
extern int errno;
#endif

#define x_digs  "0123456789abcdef"
#define X_digs  "0123456789ABCDEF"

/* XXX -- assumes uppercase letters, lowercase letters, and digits are
   contiguous */
#define FMTCHAR(x) \
  ((x) < 10) ? (x) + '0' \
         : (((x) < 36) ? (x) - 10 + 'a' \
               : (((x) < 62) ? (x) - 36 + 'A' \
                     : (((x) == 62) ? '@' : '_')))

#ifndef FL_PREFIX
#  define FL_PREFIX 0x01    /* add 0x, 0X, or 0 prefix as appropriate */
#  define FL_ADDBASE    0x02    /* add base# prefix to converted value */
#  define FL_HEXUPPER   0x04    /* use uppercase when converting to hex */
#  define FL_UNSIGNED   0x08    /* don't add any sign */
#endif

#ifndef LONG
#  define LONG  long
#  define UNSIGNED_LONG unsigned long
#endif

/* `unsigned long' (or unsigned long long) to string conversion for a given
   base.  The caller passes the output buffer and the size.  This should
   check for buffer underflow, but currently does not. */
char *
fmtulong (ui, base, buf, len, flags)
     UNSIGNED_LONG ui;
     int base;
     char *buf;
     size_t len;
     int flags;
{
  char *p;
  int sign;
  LONG si;

  if (base == 0)
    base = 10;

  if (base < 2 || base > 64)
    {
#if 1
      strncpy (buf, "invalid base", len - 1);
      buf[len] = '\0';
      errno = EINVAL;
      return (p = buf);
#else
      base = 10;
#endif
    }

  sign = 0;
  if ((flags & FL_UNSIGNED) == 0 && (LONG)ui < 0)
    {
      ui = -ui;
      sign = '-';
    }

  p = buf + len - 2;
  p[1] = '\0';

  /* handle common cases explicitly */
  switch (base)
    {
    case 10:
      if (ui < 10)
    {
      *p-- = TOCHAR (ui);
      break;
    }
      /* Favor signed arithmetic over unsigned arithmetic; it is faster on
     many machines. */
      if ((LONG)ui < 0)
    {
      *p-- = TOCHAR (ui % 10);
      si = ui / 10;
    }
      else
        si = ui;
      do
    *p-- = TOCHAR (si % 10);
      while (si /= 10);
      break;

    case 8:
      do
    *p-- = TOCHAR (ui & 7);
      while (ui >>= 3);
      break;

    case 16:
      do
    *p-- = (flags & FL_HEXUPPER) ? X_digs[ui & 15] : x_digs[ui & 15];
      while (ui >>= 4);
      break;

    case 2:
      do
    *p-- = TOCHAR (ui & 1);
      while (ui >>= 1);
      break;

    default:
      do
    *p-- = FMTCHAR (ui % base);
      while (ui /= base);
      break;
    }

  if ((flags & FL_PREFIX) && (base == 8 || base == 16))
    {
      if (base == 16)
    {
      *p-- = (flags & FL_HEXUPPER) ? 'X' : 'x';
      *p-- = '0';
    }
      else if (p[1] != '0')
    *p-- = '0';
    }
  else if ((flags & FL_ADDBASE) && base != 10)
    {
      *p-- = '#';
      *p-- = TOCHAR (base % 10);
      if (base > 10)
        *p-- = TOCHAR (base / 10);
    }

  if (sign)
    *p-- = '-';

  return (p + 1);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/fmtullong.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/fmtullong.c used in: Step 1183
/* fmtullong.c - convert `long long int' to string */

/* Copyright (C) 2001-2002 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 <config.h>

#ifdef HAVE_LONG_LONG

#define LONG        long long
#define UNSIGNED_LONG   unsigned long long
#define fmtulong    fmtullong

#include "fmtulong.c"

#endif

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strtoll.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strtoll.c used in: Step 1184
/* Copyright (C) 1997 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 <config.h>

#if defined (HAVE_LONG_LONG) && !defined (HAVE_STRTOLL)

#define QUAD        1
#undef HAVE_STRTOL

#include "strtol.c"

#endif /* HAVE_LONG_LONG && !HAVE_STRTOLL */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strtoull.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strtoull.c used in: Step 1185
/* Copyright (C) 1997 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 <config.h>

#if defined (HAVE_LONG_LONG) && !defined (HAVE_STRTOULL)

#define QUAD        1
#define UNSIGNED    1
#undef HAVE_STRTOL

#include "strtol.c"

#endif /* HAVE_LONG_LONG && !HAVE_STRTOULL */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strtoimax.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strtoimax.c used in: Step 1186
/* Convert string representation of a number into an intmax_t value.
   Copyright 1999, 2001 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.  */

/* Written by Paul Eggert.  Modified by Chet Ramey for Bash. */

#if HAVE_CONFIG_H
#  include <config.h>
#endif

#if HAVE_INTTYPES_H
#  include <inttypes.h>
#endif

#if HAVE_STDLIB_H
#  include <stdlib.h>
#endif

#include <stdc.h>

/* Verify a requirement at compile-time (unlike assert, which is runtime).  */
#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }

#ifndef HAVE_DECL_STRTOL
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_STRTOL
extern long strtol __P((const char *, char **, int));
#endif

#ifndef HAVE_DECL_STRTOLL
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_STRTOLL && HAVE_LONG_LONG
extern long long strtoll __P((const char *, char **, int));
#endif

intmax_t
strtoimax (ptr, endptr, base)
     const char *ptr;
     char **endptr;
     int base;
{
#if HAVE_LONG_LONG
  verify(size_is_that_of_long_or_long_long,
     (sizeof (intmax_t) == sizeof (long) ||
      sizeof (intmax_t) == sizeof (long long)));

  if (sizeof (intmax_t) != sizeof (long))
    return (strtoll (ptr, endptr, base));
#else
  verify (size_is_that_of_long, sizeof (intmax_t) == sizeof (long));
#endif

  return (strtol (ptr, endptr, base));
}

#ifdef TESTING
# include <stdio.h>
int
main ()
{
  char *p, *endptr;
  intmax_t x;
#if HAVE_LONG_LONG
  long long y;
#endif
  long z;
  
  printf ("sizeof intmax_t: %d\n", sizeof (intmax_t));

#if HAVE_LONG_LONG
  printf ("sizeof long long: %d\n", sizeof (long long));
#endif
  printf ("sizeof long: %d\n", sizeof (long));

  x = strtoimax("42", &endptr, 10);
#if HAVE_LONG_LONG
  y = strtoll("42", &endptr, 10);
#else
  y = -1;
#endif
  z = strtol("42", &endptr, 10);

  printf ("%lld %lld %ld\n", x, y, z);

  exit (0);
}
#endif

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strtoumax.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strtoumax.c used in: Step 1187
/* Convert string representation of a number into an uintmax_t value.
   Copyright 1999, 2001 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.  */

/* Written by Paul Eggert.  Modified by Chet Ramey for Bash. */

#if HAVE_CONFIG_H
#  include <config.h>
#endif

#if HAVE_INTTYPES_H
#  include <inttypes.h>
#endif

#if HAVE_STDLIB_H
#  include <stdlib.h>
#endif

#include <stdc.h>

/* Verify a requirement at compile-time (unlike assert, which is runtime).  */
#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; }

#ifndef HAVE_DECL_STRTOUL
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_STRTOUL
extern unsigned long strtoul __P((const char *, char **, int));
#endif

#ifndef HAVE_DECL_STRTOULL
"this configure-time declaration test was not run"
#endif
#if !HAVE_DECL_STRTOULL && HAVE_UNSIGNED_LONG_LONG
extern unsigned long long strtoull __P((const char *, char **, int));
#endif

uintmax_t
strtoumax (ptr, endptr, base)
     const char *ptr;
     char **endptr;
     int base;
{
#if HAVE_UNSIGNED_LONG_LONG
  verify (size_is_that_of_unsigned_long_or_unsigned_long_long,
      (sizeof (uintmax_t) == sizeof (unsigned long) ||
       sizeof (uintmax_t) == sizeof (unsigned long long)));

  if (sizeof (uintmax_t) != sizeof (unsigned long))
    return (strtoull (ptr, endptr, base));
#else
  verify (size_is_that_of_unsigned_long, sizeof (uintmax_t) == sizeof (unsigned long));
#endif

  return (strtoul (ptr, endptr, base));
}

#ifdef TESTING
# include <stdio.h>
int
main ()
{
  char *p, *endptr;
  uintmax_t x;
#if HAVE_UNSIGNED_LONG_LONG
  unsigned long long y;
#endif
  unsigned long z;

  printf ("sizeof uintmax_t: %d\n", sizeof (uintmax_t));

#if HAVE_UNSIGNED_LONG_LONG
  printf ("sizeof unsigned long long: %d\n", sizeof (unsigned long long));
#endif
  printf ("sizeof unsigned long: %d\n", sizeof (unsigned long));

  x = strtoumax("42", &endptr, 10);
#if HAVE_LONG_LONG
  y = strtoull("42", &endptr, 10);
#else
  y = 0;
#endif
  z = strtoul("42", &endptr, 10);

  printf ("%llu %llu %lu\n", x, y, z);

  exit (0);
}
#endif

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/fmtumax.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/fmtumax.c used in: Step 1188
/* fmtumax.c -- Convert uintmax_t to string.  */

/* Copyright (C) 2002 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 <config.h>

#define LONG    intmax_t
#define UNSIGNED_LONG   uintmax_t
#define fmtulong    fmtumax

#include "fmtulong.c"

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/netconn.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/netconn.c used in: Step 1189
/* netconn.c -- is a particular file descriptor a network connection?. */

/* Copyright (C) 2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/

#include <config.h>

#include <bashtypes.h>
#ifndef _MINIX
#  include <sys/file.h>
#endif
#include <posixstat.h>
#include <filecntl.h>

#include <errno.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

/* The second and subsequent conditions must match those used to decide
   whether or not to call getpeername() in isnetconn(). */
#if defined (HAVE_SYS_SOCKET_H) && defined (HAVE_GETPEERNAME) && !defined (SVR4_2)
#  include <sys/socket.h>
#endif

/* Is FD a socket or network connection? */
int
isnetconn (fd)
     int fd;
{
#if defined (HAVE_GETPEERNAME) && !defined (SVR4_2) && !defined (__BEOS__)
  int rv;
  socklen_t l;
  struct sockaddr sa;

  l = sizeof(sa);
  rv = getpeername(fd, &sa, &l);
  /* Solaris 2.5 getpeername() returns EINVAL if the fd is not a socket. */
  return ((rv < 0 && (errno == ENOTSOCK || errno == EINVAL)) ? 0 : 1);
#else /* !HAVE_GETPEERNAME || SVR4_2 || __BEOS__ */
#  if defined (SVR4) || defined (SVR4_2)
  /* Sockets on SVR4 and SVR4.2 are character special (streams) devices. */
  struct stat sb;

  if (isatty (fd))
    return (0);
  if (fstat (fd, &sb) < 0)
    return (0);
#    if defined (S_ISFIFO)
  if (S_ISFIFO (sb.st_mode))
    return (0);
#    endif /* S_ISFIFO */
  return (S_ISCHR (sb.st_mode));
#  else /* !SVR4 && !SVR4_2 */
#    if defined (S_ISSOCK) && !defined (__BEOS__)
  struct stat sb;

  if (fstat (fd, &sb) < 0)
    return (0);
  return (S_ISSOCK (sb.st_mode));
#    else /* !S_ISSOCK || __BEOS__ */
  return (0);
#    endif /* !S_ISSOCK || __BEOS__ */
#  endif /* !SVR4 && !SVR4_2 */
#endif /* !HAVE_GETPEERNAME || SVR4_2 || __BEOS__ */
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/mktime.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/mktime.c used in: Step 1190
/* Copyright (C) 1993-2002 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   Contributed by Paul Eggert (eggert@twinsun.com).

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   The GNU C Library 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.

   You should have received a copy of the GNU Library General Public
   License along with the GNU C Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

/* Define this to have a standalone program to test this implementation of
   mktime.  */
/* #define DEBUG 1 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef _LIBC
# define HAVE_LIMITS_H 1
# define HAVE_LOCALTIME_R 1
# define STDC_HEADERS 1
#endif

/* Assume that leap seconds are possible, unless told otherwise.
   If the host has a `zic' command with a `-L leapsecondfilename' option,
   then it supports leap seconds; otherwise it probably doesn't.  */
#ifndef LEAP_SECONDS_POSSIBLE
#define LEAP_SECONDS_POSSIBLE 1
#endif

#ifndef VMS
#include <sys/types.h>      /* Some systems define `time_t' here.  */
#else
#include <stddef.h>
#endif
#include <time.h>

#if HAVE_LIMITS_H
#include <limits.h>
#endif

#if DEBUG
#include <stdio.h>
#if STDC_HEADERS
#include <stdlib.h>
#endif
/* Make it work even if the system's libc has its own mktime routine.  */
#define mktime my_mktime
#endif /* DEBUG */

#ifndef __P
#if defined (__GNUC__) || (defined (__STDC__) && __STDC__)
#define __P(args) args
#else
#define __P(args) ()
#endif  /* GCC.  */
#endif  /* Not __P.  */

#ifndef CHAR_BIT
#define CHAR_BIT 8
#endif

#ifndef INT_MIN
#define INT_MIN (~0 << (sizeof (int) * CHAR_BIT - 1))
#endif
#ifndef INT_MAX
#define INT_MAX (~0 - INT_MIN)
#endif

#ifndef TIME_T_MIN
#define TIME_T_MIN (0 < (time_t) -1 ? (time_t) 0 \
            : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1))
#endif
#ifndef TIME_T_MAX
#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN)
#endif

#define TM_YEAR_BASE 1900
#define EPOCH_YEAR 1970

#ifndef __isleap
/* Nonzero if YEAR is a leap year (every 4 years,
   except every 100th isn't, and every 400th is).  */
#define __isleap(year)  \
  ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
#endif

/* How many days come before each month (0-12).  */
const unsigned short int __mon_yday[2][13] =
  {
    /* Normal years.  */
    { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
    /* Leap years.  */
    { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
  };

static time_t ydhms_tm_diff __P ((int, int, int, int, int, const struct tm *));
time_t __mktime_internal __P ((struct tm *,
                   struct tm *(*) (const time_t *, struct tm *),
                   time_t *));


static struct tm *my_localtime_r __P ((const time_t *, struct tm *));
static struct tm *
my_localtime_r (t, tp)
     const time_t *t;
     struct tm *tp;
{
  struct tm *l = localtime (t);
  if (! l)
    return 0;
  *tp = *l;
  return tp;
}


/* Yield the difference between (YEAR-YDAY HOUR:MIN:SEC) and (*TP),
   measured in seconds, ignoring leap seconds.
   YEAR uses the same numbering as TM->tm_year.
   All values are in range, except possibly YEAR.
   If overflow occurs, yield the low order bits of the correct answer.  */
static time_t
ydhms_tm_diff (year, yday, hour, min, sec, tp)
     int year, yday, hour, min, sec;
     const struct tm *tp;
{
  /* Compute intervening leap days correctly even if year is negative.
     Take care to avoid int overflow.  time_t overflow is OK, since
     only the low order bits of the correct time_t answer are needed.
     Don't convert to time_t until after all divisions are done, since
     time_t might be unsigned.  */
  int a4 = (year >> 2) + (TM_YEAR_BASE >> 2) - ! (year & 3);
  int b4 = (tp->tm_year >> 2) + (TM_YEAR_BASE >> 2) - ! (tp->tm_year & 3);
  int a100 = a4 / 25 - (a4 % 25 < 0);
  int b100 = b4 / 25 - (b4 % 25 < 0);
  int a400 = a100 >> 2;
  int b400 = b100 >> 2;
  int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);
  time_t years = year - (time_t) tp->tm_year;
  time_t days = (365 * years + intervening_leap_days
         + (yday - tp->tm_yday));
  return (60 * (60 * (24 * days + (hour - tp->tm_hour))
        + (min - tp->tm_min))
      + (sec - tp->tm_sec));
}


static time_t localtime_offset;

/* Convert *TP to a time_t value.  */
time_t
mktime (tp)
     struct tm *tp;
{
#ifdef _LIBC
  /* POSIX.1 8.1.1 requires that whenever mktime() is called, the
     time zone names contained in the external variable `tzname' shall
     be set as if the tzset() function had been called.  */
  __tzset ();
#endif

  return __mktime_internal (tp, my_localtime_r, &localtime_offset);
}

/* Convert *TP to a time_t value, inverting
   the monotonic and mostly-unit-linear conversion function CONVERT.
   Use *OFFSET to keep track of a guess at the offset of the result,
   compared to what the result would be for UTC without leap seconds.
   If *OFFSET's guess is correct, only one CONVERT call is needed.  */
time_t
__mktime_internal (tp, convert, offset)
     struct tm *tp;
     struct tm *(*convert) __P ((const time_t *, struct tm *));
     time_t *offset;
{
  time_t t, dt, t0;
  struct tm tm;

  /* The maximum number of probes (calls to CONVERT) should be enough
     to handle any combinations of time zone rule changes, solar time,
     and leap seconds.  Posix.1 prohibits leap seconds, but some hosts
     have them anyway.  */
  int remaining_probes = 4;

  /* Time requested.  Copy it in case CONVERT modifies *TP; this can
     occur if TP is localtime's returned value and CONVERT is localtime.  */
  int sec = tp->tm_sec;
  int min = tp->tm_min;
  int hour = tp->tm_hour;
  int mday = tp->tm_mday;
  int mon = tp->tm_mon;
  int year_requested = tp->tm_year;
  int isdst = tp->tm_isdst;

  /* Ensure that mon is in range, and set year accordingly.  */
  int mon_remainder = mon % 12;
  int negative_mon_remainder = mon_remainder < 0;
  int mon_years = mon / 12 - negative_mon_remainder;
  int year = year_requested + mon_years;

  /* The other values need not be in range:
     the remaining code handles minor overflows correctly,
     assuming int and time_t arithmetic wraps around.
     Major overflows are caught at the end.  */

  /* Calculate day of year from year, month, and day of month.
     The result need not be in range.  */
  int yday = ((__mon_yday[__isleap (year + TM_YEAR_BASE)]
           [mon_remainder + 12 * negative_mon_remainder])
          + mday - 1);

#if LEAP_SECONDS_POSSIBLE
  /* Handle out-of-range seconds specially,
     since ydhms_tm_diff assumes every minute has 60 seconds.  */
  int sec_requested = sec;
  if (sec < 0)
    sec = 0;
  if (59 < sec)
    sec = 59;
#endif

  /* Invert CONVERT by probing.  First assume the same offset as last time.
     Then repeatedly use the error to improve the guess.  */

  tm.tm_year = EPOCH_YEAR - TM_YEAR_BASE;
  tm.tm_yday = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
  t0 = ydhms_tm_diff (year, yday, hour, min, sec, &tm);

  for (t = t0 + *offset;
       (dt = ydhms_tm_diff (year, yday, hour, min, sec, (*convert) (&t, &tm)));
       t += dt)
    if (--remaining_probes == 0)
      return -1;

  /* Check whether tm.tm_isdst has the requested value, if any.  */
  if (0 <= isdst && 0 <= tm.tm_isdst)
    {
      int dst_diff = (isdst != 0) - (tm.tm_isdst != 0);
      if (dst_diff)
    {
      /* Move two hours in the direction indicated by the disagreement,
         probe some more, and switch to a new time if found.
         The largest known fallback due to daylight savings is two hours:
         once, in Newfoundland, 1988-10-30 02:00 -> 00:00.  */
      time_t ot = t - 2 * 60 * 60 * dst_diff;
      while (--remaining_probes != 0)
        {
          struct tm otm;
          if (! (dt = ydhms_tm_diff (year, yday, hour, min, sec,
                     (*convert) (&ot, &otm))))
        {
          t = ot;
          tm = otm;
          break;
        }
          if ((ot += dt) == t)
        break;  /* Avoid a redundant probe.  */
        }
    }
    }

  *offset = t - t0;

#if LEAP_SECONDS_POSSIBLE
  if (sec_requested != tm.tm_sec)
    {
      /* Adjust time to reflect the tm_sec requested, not the normalized value.
     Also, repair any damage from a false match due to a leap second.  */
      t += sec_requested - sec + (sec == 0 && tm.tm_sec == 60);
      (*convert) (&t, &tm);
    }
#endif

  if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)
    {
      /* time_t isn't large enough to rule out overflows in ydhms_tm_diff,
     so check for major overflows.  A gross check suffices,
     since if t has overflowed, it is off by a multiple of
     TIME_T_MAX - TIME_T_MIN + 1.  So ignore any component of
     the difference that is bounded by a small value.  */

      double dyear = (double) year_requested + mon_years - tm.tm_year;
      double dday = 366 * dyear + mday;
      double dsec = 60 * (60 * (24 * dday + hour) + min) + sec_requested;

      if (TIME_T_MAX / 3 - TIME_T_MIN / 3 < (dsec < 0 ? - dsec : dsec))
    return -1;
    }

  *tp = tm;
  return t;
}

#ifdef weak_alias
weak_alias (mktime, timelocal)
#endif


#if DEBUG

static int
not_equal_tm (a, b)
     struct tm *a;
     struct tm *b;
{
  return ((a->tm_sec ^ b->tm_sec)
      | (a->tm_min ^ b->tm_min)
      | (a->tm_hour ^ b->tm_hour)
      | (a->tm_mday ^ b->tm_mday)
      | (a->tm_mon ^ b->tm_mon)
      | (a->tm_year ^ b->tm_year)
      | (a->tm_mday ^ b->tm_mday)
      | (a->tm_yday ^ b->tm_yday)
      | (a->tm_isdst ^ b->tm_isdst));
}

static void
print_tm (tp)
     struct tm *tp;
{
  printf ("%04d-%02d-%02d %02d:%02d:%02d yday %03d wday %d isdst %d",
      tp->tm_year + TM_YEAR_BASE, tp->tm_mon + 1, tp->tm_mday,
      tp->tm_hour, tp->tm_min, tp->tm_sec,
      tp->tm_yday, tp->tm_wday, tp->tm_isdst);
}

static int
check_result (tk, tmk, tl, tml)
     time_t tk;
     struct tm tmk;
     time_t tl;
     struct tm tml;
{
  if (tk != tl || not_equal_tm (&tmk, &tml))
    {
      printf ("mktime (");
      print_tm (&tmk);
      printf (")\nyields (");
      print_tm (&tml);
      printf (") == %ld, should be %ld\n", (long) tl, (long) tk);
      return 1;
    }

  return 0;
}

int
main (argc, argv)
     int argc;
     char **argv;
{
  int status = 0;
  struct tm tm, tmk, tml;
  time_t tk, tl;
  char trailer;

  if ((argc == 3 || argc == 4)
      && (sscanf (argv[1], "%d-%d-%d%c",
          &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &trailer)
      == 3)
      && (sscanf (argv[2], "%d:%d:%d%c",
          &tm.tm_hour, &tm.tm_min, &tm.tm_sec, &trailer)
      == 3))
    {
      tm.tm_year -= TM_YEAR_BASE;
      tm.tm_mon--;
      tm.tm_isdst = argc == 3 ? -1 : atoi (argv[3]);
      tmk = tm;
      tl = mktime (&tmk);
      tml = *localtime (&tl);
      printf ("mktime returns %ld == ", (long) tl);
      print_tm (&tmk);
      printf ("\n");
      status = check_result (tl, tmk, tl, tml);
    }
  else if (argc == 4 || (argc == 5 && strcmp (argv[4], "-") == 0))
    {
      time_t from = atol (argv[1]);
      time_t by = atol (argv[2]);
      time_t to = atol (argv[3]);

      if (argc == 4)
    for (tl = from; tl <= to; tl += by)
      {
        tml = *localtime (&tl);
        tmk = tml;
        tk = mktime (&tmk);
        status |= check_result (tk, tmk, tl, tml);
      }
      else
    for (tl = from; tl <= to; tl += by)
      {
        /* Null benchmark.  */
        tml = *localtime (&tl);
        tmk = tml;
        tk = tl;
        status |= check_result (tk, tmk, tl, tml);
      }
    }
  else
    printf ("Usage:\
\t%s YYYY-MM-DD HH:MM:SS [ISDST] # Test given time.\n\
\t%s FROM BY TO # Test values FROM, FROM+BY, ..., TO.\n\
\t%s FROM BY TO - # Do not test those values (for benchmark).\n",
        argv[0], argv[0], argv[0]);

  return status;
}

#endif /* DEBUG */


/*
Local Variables:
compile-command: "gcc -DDEBUG=1 -Wall -O -g mktime.c -o mktime"
End:
*/

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/strftime.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/strftime.c used in: Step 1191
/*
 * Modified slightly by Chet Ramey for inclusion in Bash
 */

/*
 * strftime.c
 *
 * Public-domain implementation of ISO C library routine.
 *
 * If you can't do prototypes, get GCC.
 *
 * The C99 standard now specifies just about all of the formats
 * that were additional in the earlier versions of this file.
 *
 * For extensions from SunOS, add SUNOS_EXT.
 * For extensions from HP/UX, add HPUX_EXT.
 * For VMS dates, add VMS_EXT.
 * For complete POSIX semantics, add POSIX_SEMANTICS.
 *
 * The code for %c, %x, and %X follows the C99 specification for
 * the "C" locale.
 *
 * This version ignores LOCALE information.
 * It also doesn't worry about multi-byte characters.
 * So there.
 *
 * This file is also shipped with GAWK (GNU Awk), gawk specific bits of
 * code are included if GAWK is defined.
 *
 * Arnold Robbins
 * January, February, March, 1991
 * Updated March, April 1992
 * Updated April, 1993
 * Updated February, 1994
 * Updated May, 1994
 * Updated January, 1995
 * Updated September, 1995
 * Updated January, 1996
 * Updated July, 1997
 * Updated October, 1999
 * Updated September, 2000
 *
 * Fixes from ado@elsie.nci.nih.gov,
 * February 1991, May 1992
 * Fixes from Tor Lillqvist tml@tik.vtt.fi,
 * May 1993
 * Further fixes from ado@elsie.nci.nih.gov,
 * February 1994
 * %z code from chip@chinacat.unicom.com,
 * Applied September 1995
 * %V code fixed (again) and %G, %g added,
 * January 1996
 * %v code fixed, better configuration,
 * July 1997
 * Moved to C99 specification.
 * September 2000
 */
#include <config.h>

#ifndef GAWK
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#endif
#if defined(TM_IN_SYS_TIME)
#include <sys/types.h>
#include <sys/time.h>
#endif

#include <stdlib.h>
#include <string.h>

/* defaults: season to taste */
#define SUNOS_EXT   1   /* stuff in SunOS strftime routine */
#define VMS_EXT     1   /* include %v for VMS date format */
#define HPUX_EXT    1   /* non-conflicting stuff in HP-UX date */
#ifndef GAWK
#define POSIX_SEMANTICS 1   /* call tzset() if TZ changes */
#endif

#undef strchr   /* avoid AIX weirdness */

extern void tzset(void);
static int weeknumber(const struct tm *timeptr, int firstweekday);
static int iso8601wknum(const struct tm *timeptr);

#ifdef __GNUC__
#define inline  __inline__
#else
#define inline  /**/
#endif

#define range(low, item, hi)    max(low, min(item, hi))

#if !defined(OS2) && !defined(MSDOS) && defined(HAVE_TZNAME)
extern char *tzname[2];
extern int daylight;
#if defined(SOLARIS) || defined(mips)
extern long int timezone, altzone;
#else
extern int timezone, altzone;
#endif
#endif

#undef min  /* just in case */

/* min --- return minimum of two numbers */

static inline int
min(int a, int b)
{
    return (a < b ? a : b);
}

#undef max  /* also, just in case */

/* max --- return maximum of two numbers */

static inline int
max(int a, int b)
{
    return (a > b ? a : b);
}

/* strftime --- produce formatted time */

size_t
strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
{
    char *endp = s + maxsize;
    char *start = s;
    auto char tbuf[100];
    long off;
    int i, w, y;
    static short first = 1;
#ifdef POSIX_SEMANTICS
    static char *savetz = NULL;
    static int savetzlen = 0;
    char *tz;
#endif /* POSIX_SEMANTICS */
#ifndef HAVE_TM_ZONE
#ifndef HAVE_TM_NAME
#ifndef HAVE_TZNAME
    extern char *timezone();
    struct timeval tv;
    struct timezone zone;
#endif /* HAVE_TZNAME */
#endif /* HAVE_TM_NAME */
#endif /* HAVE_TM_ZONE */

    /* various tables, useful in North America */
    static const char *days_a[] = {
        "Sun", "Mon", "Tue", "Wed",
        "Thu", "Fri", "Sat",
    };
    static const char *days_l[] = {
        "Sunday", "Monday", "Tuesday", "Wednesday",
        "Thursday", "Friday", "Saturday",
    };
    static const char *months_a[] = {
        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
    };
    static const char *months_l[] = {
        "January", "February", "March", "April",
        "May", "June", "July", "August", "September",
        "October", "November", "December",
    };
    static const char *ampm[] = { "AM", "PM", };

    if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0)
        return 0;

    /* quick check if we even need to bother */
    if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize)
        return 0;

#ifndef POSIX_SEMANTICS
    if (first) {
        tzset();
        first = 0;
    }
#else   /* POSIX_SEMANTICS */
#if defined (SHELL)
    tz = get_string_value ("TZ");
#else
    tz = getenv("TZ");
#endif
    if (first) {
        if (tz != NULL) {
            int tzlen = strlen(tz);

            savetz = (char *) malloc(tzlen + 1);
            if (savetz != NULL) {
                savetzlen = tzlen + 1;
                strcpy(savetz, tz);
            }
        }
        tzset();
        first = 0;
    }
    /* if we have a saved TZ, and it is different, recapture and reset */
    if (tz && savetz && (tz[0] != savetz[0] || strcmp(tz, savetz) != 0)) {
        i = strlen(tz) + 1;
        if (i > savetzlen) {
            savetz = (char *) realloc(savetz, i);
            if (savetz) {
                savetzlen = i;
                strcpy(savetz, tz);
            }
        } else
            strcpy(savetz, tz);
        tzset();
    }
#endif  /* POSIX_SEMANTICS */

    for (; *format && s < endp - 1; format++) {
        tbuf[0] = '\0';
        if (*format != '%') {
            *s++ = *format;
            continue;
        }
    again:
        switch (*++format) {
        case '\0':
            *s++ = '%';
            goto out;

        case '%':
            *s++ = '%';
            continue;

        case 'a':   /* abbreviated weekday name */
            if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
                strcpy(tbuf, "?");
            else
                strcpy(tbuf, days_a[timeptr->tm_wday]);
            break;

        case 'A':   /* full weekday name */
            if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
                strcpy(tbuf, "?");
            else
                strcpy(tbuf, days_l[timeptr->tm_wday]);
            break;

        case 'b':   /* abbreviated month name */
        short_month:
            if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
                strcpy(tbuf, "?");
            else
                strcpy(tbuf, months_a[timeptr->tm_mon]);
            break;

        case 'B':   /* full month name */
            if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
                strcpy(tbuf, "?");
            else
                strcpy(tbuf, months_l[timeptr->tm_mon]);
            break;

        case 'c':   /* appropriate date and time representation */
            /*
             * This used to be:
             *
             * strftime(tbuf, sizeof tbuf, "%a %b %e %H:%M:%S %Y", timeptr);
             *
             * Now, per the ISO 1999 C standard, it this:
             */
            strftime(tbuf, sizeof tbuf, "%A %B %d %T %Y", timeptr);
            break;

        case 'C':
        century:
            sprintf(tbuf, "%02d", (timeptr->tm_year + 1900) / 100);
            break;

        case 'd':   /* day of the month, 01 - 31 */
            i = range(1, timeptr->tm_mday, 31);
            sprintf(tbuf, "%02d", i);
            break;

        case 'D':   /* date as %m/%d/%y */
            strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);
            break;

        case 'e':   /* day of month, blank padded */
            sprintf(tbuf, "%2d", range(1, timeptr->tm_mday, 31));
            break;

        case 'E':
            /* POSIX (now C99) locale extensions, ignored for now */
            goto again;

        case 'F':   /* ISO 8601 date representation */
            strftime(tbuf, sizeof tbuf, "%Y-%m-%d", timeptr);
            break;

        case 'g':
        case 'G':
            /*
             * Year of ISO week.
             *
             * If it's December but the ISO week number is one,
             * that week is in next year.
             * If it's January but the ISO week number is 52 or
             * 53, that week is in last year.
             * Otherwise, it's this year.
             */
            w = iso8601wknum(timeptr);
            if (timeptr->tm_mon == 11 && w == 1)
                y = 1900 + timeptr->tm_year + 1;
            else if (timeptr->tm_mon == 0 && w >= 52)
                y = 1900 + timeptr->tm_year - 1;
            else
                y = 1900 + timeptr->tm_year;

            if (*format == 'G')
                sprintf(tbuf, "%d", y);
            else
                sprintf(tbuf, "%02d", y % 100);
            break;

        case 'h':   /* abbreviated month name */
            goto short_month;

        case 'H':   /* hour, 24-hour clock, 00 - 23 */
            i = range(0, timeptr->tm_hour, 23);
            sprintf(tbuf, "%02d", i);
            break;

        case 'I':   /* hour, 12-hour clock, 01 - 12 */
            i = range(0, timeptr->tm_hour, 23);
            if (i == 0)
                i = 12;
            else if (i > 12)
                i -= 12;
            sprintf(tbuf, "%02d", i);
            break;

        case 'j':   /* day of the year, 001 - 366 */
            sprintf(tbuf, "%03d", timeptr->tm_yday + 1);
            break;

        case 'm':   /* month, 01 - 12 */
            i = range(0, timeptr->tm_mon, 11);
            sprintf(tbuf, "%02d", i + 1);
            break;

        case 'M':   /* minute, 00 - 59 */
            i = range(0, timeptr->tm_min, 59);
            sprintf(tbuf, "%02d", i);
            break;

        case 'n':   /* same as \n */
            tbuf[0] = '\n';
            tbuf[1] = '\0';
            break;

        case 'O':
            /* POSIX (now C99) locale extensions, ignored for now */
            goto again;

        case 'p':   /* am or pm based on 12-hour clock */
            i = range(0, timeptr->tm_hour, 23);
            if (i < 12)
                strcpy(tbuf, ampm[0]);
            else
                strcpy(tbuf, ampm[1]);
            break;

        case 'r':   /* time as %I:%M:%S %p */
            strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr);
            break;

        case 'R':   /* time as %H:%M */
            strftime(tbuf, sizeof tbuf, "%H:%M", timeptr);
            break;

#if defined(HAVE_MKTIME) || defined(GAWK)
        case 's':   /* time as seconds since the Epoch */
        {
            struct tm non_const_timeptr;

            non_const_timeptr = *timeptr;
            sprintf(tbuf, "%ld", mktime(& non_const_timeptr));
            break;
        }
#endif /* defined(HAVE_MKTIME) || defined(GAWK) */

        case 'S':   /* second, 00 - 60 */
            i = range(0, timeptr->tm_sec, 60);
            sprintf(tbuf, "%02d", i);
            break;

        case 't':   /* same as \t */
            tbuf[0] = '\t';
            tbuf[1] = '\0';
            break;

        case 'T':   /* time as %H:%M:%S */
        the_time:
            strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);
            break;

        case 'u':
        /* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */
            sprintf(tbuf, "%d", timeptr->tm_wday == 0 ? 7 :
                    timeptr->tm_wday);
            break;

        case 'U':   /* week of year, Sunday is first day of week */
            sprintf(tbuf, "%02d", weeknumber(timeptr, 0));
            break;

        case 'V':   /* week of year according ISO 8601 */
            sprintf(tbuf, "%02d", iso8601wknum(timeptr));
            break;

        case 'w':   /* weekday, Sunday == 0, 0 - 6 */
            i = range(0, timeptr->tm_wday, 6);
            sprintf(tbuf, "%d", i);
            break;

        case 'W':   /* week of year, Monday is first day of week */
            sprintf(tbuf, "%02d", weeknumber(timeptr, 1));
            break;

        case 'x':   /* appropriate date representation */
            strftime(tbuf, sizeof tbuf, "%A %B %d %Y", timeptr);
            break;

        case 'X':   /* appropriate time representation */
            goto the_time;
            break;

        case 'y':   /* year without a century, 00 - 99 */
        year:
            i = timeptr->tm_year % 100;
            sprintf(tbuf, "%02d", i);
            break;

        case 'Y':   /* year with century */
        fullyear:
            sprintf(tbuf, "%d", 1900 + timeptr->tm_year);
            break;

        /*
         * From: Chip Rosenthal <chip@chinacat.unicom.com>
         * Date: Sun, 19 Mar 1995 00:33:29 -0600 (CST)
         * 
         * Warning: the %z [code] is implemented by inspecting the
         * timezone name conditional compile settings, and
         * inferring a method to get timezone offsets. I've tried
         * this code on a couple of machines, but I don't doubt
         * there is some system out there that won't like it.
         * Maybe the easiest thing to do would be to bracket this
         * with an #ifdef that can turn it off. The %z feature
         * would be an admittedly obscure one that most folks can
         * live without, but it would be a great help to those of
         * us that muck around with various message processors.
         */
        case 'z':   /* time zone offset east of GMT e.g. -0600 */
#ifdef HAVE_TM_NAME
            /*
             * Systems with tm_name probably have tm_tzadj as
             * secs west of GMT.  Convert to mins east of GMT.
             */
            off = -timeptr->tm_tzadj / 60;
#else /* !HAVE_TM_NAME */
#ifdef HAVE_TM_ZONE
            /*
             * Systems with tm_zone probably have tm_gmtoff as
             * secs east of GMT.  Convert to mins east of GMT.
             */
            off = timeptr->tm_gmtoff / 60;
#else /* !HAVE_TM_ZONE */
#if HAVE_TZNAME
            /*
             * Systems with tzname[] probably have timezone as
             * secs west of GMT.  Convert to mins east of GMT.
             */
            off = -(daylight ? timezone : altzone) / 60;
#else /* !HAVE_TZNAME */
            off = -zone.tz_minuteswest;
#endif /* !HAVE_TZNAME */
#endif /* !HAVE_TM_ZONE */
#endif /* !HAVE_TM_NAME */
            if (off < 0) {
                tbuf[0] = '-';
                off = -off;
            } else {
                tbuf[0] = '+';
            }
            sprintf(tbuf+1, "%02d%02d", off/60, off%60);
            break;

        case 'Z':   /* time zone name or abbrevation */
#ifdef HAVE_TZNAME
            i = (daylight && timeptr->tm_isdst > 0); /* 0 or 1 */
            strcpy(tbuf, tzname[i]);
#else
#ifdef HAVE_TM_ZONE
            strcpy(tbuf, timeptr->tm_zone);
#else
#ifdef HAVE_TM_NAME
            strcpy(tbuf, timeptr->tm_name);
#else
            gettimeofday(& tv, & zone);
            strcpy(tbuf, timezone(zone.tz_minuteswest,
                        timeptr->tm_isdst > 0));
#endif /* HAVE_TM_NAME */
#endif /* HAVE_TM_ZONE */
#endif /* HAVE_TZNAME */
            break;

#ifdef SUNOS_EXT
        case 'k':   /* hour, 24-hour clock, blank pad */
            sprintf(tbuf, "%2d", range(0, timeptr->tm_hour, 23));
            break;

        case 'l':   /* hour, 12-hour clock, 1 - 12, blank pad */
            i = range(0, timeptr->tm_hour, 23);
            if (i == 0)
                i = 12;
            else if (i > 12)
                i -= 12;
            sprintf(tbuf, "%2d", i);
            break;
#endif

#ifdef HPUX_EXT
        case 'N':   /* Emperor/Era name */
            /* this is essentially the same as the century */
            goto century;   /* %C */

        case 'o':   /* Emperor/Era year */
            goto year;  /* %y */
#endif /* HPUX_EXT */


#ifdef VMS_EXT
        case 'v':   /* date as dd-bbb-YYYY */
            sprintf(tbuf, "%2d-%3.3s-%4d",
                range(1, timeptr->tm_mday, 31),
                months_a[range(0, timeptr->tm_mon, 11)],
                timeptr->tm_year + 1900);
            for (i = 3; i < 6; i++)
                if (islower(tbuf[i]))
                    tbuf[i] = toupper(tbuf[i]);
            break;
#endif

        default:
            tbuf[0] = '%';
            tbuf[1] = *format;
            tbuf[2] = '\0';
            break;
        }
        i = strlen(tbuf);
        if (i) {
            if (s + i < endp - 1) {
                strcpy(s, tbuf);
                s += i;
            } else
                return 0;
        }
    }
out:
    if (s < endp && *format == '\0') {
        *s = '\0';
        return (s - start);
    } else
        return 0;
}

/* isleap --- is a year a leap year? */

static int
isleap(int year)
{
    return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
}


/* iso8601wknum --- compute week number according to ISO 8601 */

static int
iso8601wknum(const struct tm *timeptr)
{
    /*
     * From 1003.2:
     *  If the week (Monday to Sunday) containing January 1
     *  has four or more days in the new year, then it is week 1;
     *  otherwise it is the highest numbered week of the previous
     *  year (52 or 53), and the next week is week 1.
     *
     * ADR: This means if Jan 1 was Monday through Thursday,
     *  it was week 1, otherwise week 52 or 53.
     *
     * XPG4 erroneously included POSIX.2 rationale text in the
     * main body of the standard. Thus it requires week 53.
     */

    int weeknum, jan1day, diff;

    /* get week number, Monday as first day of the week */
    weeknum = weeknumber(timeptr, 1);

    /*
     * With thanks and tip of the hatlo to tml@tik.vtt.fi
     *
     * What day of the week does January 1 fall on?
     * We know that
     *  (timeptr->tm_yday - jan1.tm_yday) MOD 7 ==
     *      (timeptr->tm_wday - jan1.tm_wday) MOD 7
     * and that
     *  jan1.tm_yday == 0
     * and that
     *  timeptr->tm_wday MOD 7 == timeptr->tm_wday
     * from which it follows that. . .
     */
    jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7);
    if (jan1day < 0)
        jan1day += 7;

    /*
     * If Jan 1 was a Monday through Thursday, it was in
     * week 1.  Otherwise it was last year's highest week, which is
     * this year's week 0.
     *
     * What does that mean?
     * If Jan 1 was Monday, the week number is exactly right, it can
     *  never be 0.
     * If it was Tuesday through Thursday, the weeknumber is one
     *  less than it should be, so we add one.
     * Otherwise, Friday, Saturday or Sunday, the week number is
     * OK, but if it is 0, it needs to be 52 or 53.
     */
    switch (jan1day) {
    case 1:     /* Monday */
        break;
    case 2:     /* Tuesday */
    case 3:     /* Wednesday */
    case 4:     /* Thursday */
        weeknum++;
        break;
    case 5:     /* Friday */
    case 6:     /* Saturday */
    case 0:     /* Sunday */
        if (weeknum == 0) {
#ifdef USE_BROKEN_XPG4
            /* XPG4 (as of March 1994) says 53 unconditionally */
            weeknum = 53;
#else
            /* get week number of last week of last year */
            struct tm dec31ly;  /* 12/31 last year */
            dec31ly = *timeptr;
            dec31ly.tm_year--;
            dec31ly.tm_mon = 11;
            dec31ly.tm_mday = 31;
            dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1;
            dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900);
            weeknum = iso8601wknum(& dec31ly);
#endif
        }
        break;
    }

    if (timeptr->tm_mon == 11) {
        /*
         * The last week of the year
         * can be in week 1 of next year.
         * Sigh.
         *
         * This can only happen if
         *  M   T  W
         *  29  30 31
         *  30  31
         *  31
         */
        int wday, mday;

        wday = timeptr->tm_wday;
        mday = timeptr->tm_mday;
        if (   (wday == 1 && (mday >= 29 && mday <= 31))
            || (wday == 2 && (mday == 30 || mday == 31))
            || (wday == 3 &&  mday == 31))
            weeknum = 1;
    }

    return weeknum;
}

/* weeknumber --- figure how many weeks into the year */

/* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */

static int
weeknumber(const struct tm *timeptr, int firstweekday)
{
    int wday = timeptr->tm_wday;
    int ret;

    if (firstweekday == 1) {
        if (wday == 0)  /* sunday */
            wday = 6;
        else
            wday--;
    }
    ret = ((timeptr->tm_yday + 7 - wday) / 7);
    if (ret < 0)
        ret = 0;
    return ret;
}

#if 0
/* ADR --- I'm loathe to mess with ado's code ... */

Date:         Wed, 24 Apr 91 20:54:08 MDT
From: Michal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK>
To: arnold@audiofax.com

Hi Arnold,
in a process of fixing of strftime() in libraries on Atari ST I grabbed
some pieces of code from your own strftime.  When doing that it came
to mind that your weeknumber() function compiles a little bit nicer
in the following form:
/*
 * firstweekday is 0 if starting in Sunday, non-zero if in Monday
 */
{
    return (timeptr->tm_yday - timeptr->tm_wday +
        (firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7;
}
How nicer it depends on a compiler, of course, but always a tiny bit.

   Cheers,
   Michal
   ntomczak@vm.ucs.ualberta.ca
#endif

#ifdef  TEST_STRFTIME

/*
 * NAME:
 *  tst
 *
 * SYNOPSIS:
 *  tst
 *
 * DESCRIPTION:
 *  "tst" is a test driver for the function "strftime".
 *
 * OPTIONS:
 *  None.
 *
 * AUTHOR:
 *  Karl Vogel
 *  Control Data Systems, Inc.
 *  vogelke@c-17igp.wpafb.af.mil
 *
 * BUGS:
 *  None noticed yet.
 *
 * COMPILE:
 *  cc -o tst -DTEST_STRFTIME strftime.c
 */

/* ADR: I reformatted this to my liking, and deleted some unneeded code. */

#ifndef NULL
#include    <stdio.h>
#endif
#include    <sys/time.h>
#include    <string.h>

#define     MAXTIME     132

/*
 * Array of time formats.
 */

static char *array[] =
{
    "(%%A)      full weekday name, var length (Sunday..Saturday)  %A",
    "(%%B)       full month name, var length (January..December)  %B",
    "(%%C)                                               Century  %C",
    "(%%D)                                       date (%%m/%%d/%%y)  %D",
    "(%%E)                           Locale extensions (ignored)  %E",
    "(%%F)       full month name, var length (January..December)  %F",
    "(%%H)                          hour (24-hour clock, 00..23)  %H",
    "(%%I)                          hour (12-hour clock, 01..12)  %I",
    "(%%M)                                       minute (00..59)  %M",
    "(%%N)                                      Emporer/Era Name  %N",
    "(%%O)                           Locale extensions (ignored)  %O",
    "(%%R)                                 time, 24-hour (%%H:%%M)  %R",
    "(%%S)                                       second (00..60)  %S",
    "(%%T)                              time, 24-hour (%%H:%%M:%%S)  %T",
    "(%%U)    week of year, Sunday as first day of week (00..53)  %U",
    "(%%V)                    week of year according to ISO 8601  %V",
    "(%%W)    week of year, Monday as first day of week (00..53)  %W",
    "(%%X)     appropriate locale time representation (%H:%M:%S)  %X",
    "(%%Y)                           year with century (1970...)  %Y",
    "(%%Z) timezone (EDT), or blank if timezone not determinable  %Z",
    "(%%a)          locale's abbreviated weekday name (Sun..Sat)  %a",
    "(%%b)            locale's abbreviated month name (Jan..Dec)  %b",
    "(%%c)           full date (Sat Nov  4 12:02:33 1989)%n%t%t%t  %c",
    "(%%d)                             day of the month (01..31)  %d",
    "(%%e)               day of the month, blank-padded ( 1..31)  %e",
    "(%%h)                                should be same as (%%b)  %h",
    "(%%j)                            day of the year (001..366)  %j",
    "(%%k)               hour, 24-hour clock, blank pad ( 0..23)  %k",
    "(%%l)               hour, 12-hour clock, blank pad ( 0..12)  %l",
    "(%%m)                                        month (01..12)  %m",
    "(%%o)                                      Emporer/Era Year  %o",
    "(%%p)              locale's AM or PM based on 12-hour clock  %p",
    "(%%r)                   time, 12-hour (same as %%I:%%M:%%S %%p)  %r",
    "(%%u) ISO 8601: Weekday as decimal number [1 (Monday) - 7]   %u",
    "(%%v)                                VMS date (dd-bbb-YYYY)  %v",
    "(%%w)                       day of week (0..6, Sunday == 0)  %w",
    "(%%x)                appropriate locale date representation  %x",
    "(%%y)                      last two digits of year (00..99)  %y",
    "(%%z)      timezone offset east of GMT as HHMM (e.g. -0500)  %z",
    (char *) NULL
};

/* main routine. */

int
main(argc, argv)
int argc;
char **argv;
{
    long time();

    char *next;
    char string[MAXTIME];

    int k;
    int length;

    struct tm *tm;

    long clock;

    /* Call the function. */

    clock = time((long *) 0);
    tm = localtime(&clock);

    for (k = 0; next = array[k]; k++) {
        length = strftime(string, MAXTIME, next, tm);
        printf("%s\n", string);
    }

    exit(0);
}
#endif  /* TEST_STRFTIME */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/xstrchr.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/xstrchr.c used in: Step 1192
/* xstrchr.c - strchr(3) that handles multibyte characters. */

/* Copyright (C) 2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#ifdef HAVE_STDLIB_H
#  include <stdlib.h>
#endif

#include "bashansi.h"
#include "shmbutil.h"

#undef xstrchr

/* In some locales, the non-first byte of some multibyte characters have
   the same value as some ascii character.  Faced with these strings, a
   legacy strchr() might return the wrong value. */

char *
#if defined (PROTOTYPES)
xstrchr (const char *s, int c)
#else
xstrchr (s, c)
     const char *s;
     int c;
#endif
{
#if HANDLE_MULTIBYTE
  char *pos;
  mbstate_t state;
  size_t strlength, mblength;

  /* The locale encodings with said weird property are BIG5, BIG5-HKSCS,
     GBK, GB18030, SHIFT_JIS, and JOHAB.  They exhibit the problem only
     when c >= 0x30.  We can therefore use the faster bytewise search if
     c <= 0x30. */
  if ((unsigned char)c >= '0' && MB_CUR_MAX > 1)
    {
      pos = (char *)s;
      memset (&state, '\0', sizeof(mbstate_t));
      strlength = strlen (s);

      while (strlength > 0)
    {
      mblength = mbrlen (pos, strlength, &state);
      if (mblength == (size_t)-2 || mblength == (size_t)-1 || mblength == (size_t)0)
        mblength = 1;

      if (c == (unsigned char)*pos)
        return pos;

      strlength -= mblength;
      pos += mblength;
    }

      return ((char *)NULL);
    }
  else
#endif
  return (strchr (s, c));
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/sh/zcatfd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/sh/zcatfd.c used in: Step 1193
/* Copyright (C) 2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include <sys/types.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <errno.h>

#include <stdc.h>

#if !defined (errno)
extern int errno;
#endif

extern ssize_t zread __P((int, char *, size_t));
extern int zwrite __P((int, char *, ssize_t));

/* Dump contents of file descriptor FD to OFD.  FN is the filename for
   error messages (not used right now). */
int
zcatfd (fd, ofd, fn)
     int fd, ofd;
     char *fn;
{
  ssize_t nr;
  int rval;
  char lbuf[128];

  rval = 0;
  while (1)
    {
      nr = zread (fd, lbuf, sizeof (lbuf));
      if (nr == 0)
    break;
      else if (nr < 0)
    {
      rval = -1;
      break;
    }
      else if (zwrite (ofd, lbuf, nr) < 0)
    {
      rval = -1;
      break;
    }
    }

  return rval;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/glob/glob.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/glob/glob.c used in: Step 1195
/* glob.c -- file-name wildcard pattern matching for Bash.

   Copyright (C) 1985-2002 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 USA.  */

/* To whomever it may concern: I have never seen the code which most
   Unix programs use to perform this function.  I wrote this from scratch
   based on specifications for the pattern matching.  --RMS.  */

#include <config.h>

#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
  #pragma alloca
#endif /* _AIX && RISC6000 && !__GNUC__ */

#include "bashtypes.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "bashansi.h"
#include "posixdir.h"
#include "posixstat.h"
#include "shmbutil.h"
#include "xmalloc.h"

#include "filecntl.h"
#if !defined (F_OK)
#  define F_OK 0
#endif

#include "stdc.h"
#include "memalloc.h"
#include "quit.h"

#include "glob.h"
#include "strmatch.h"

#if !defined (HAVE_BCOPY) && !defined (bcopy)
#  define bcopy(s, d, n) ((void) memcpy ((d), (s), (n)))
#endif /* !HAVE_BCOPY && !bcopy */

#if !defined (NULL)
#  if defined (__STDC__)
#    define NULL ((void *) 0)
#  else
#    define NULL 0x0
#  endif /* __STDC__ */
#endif /* !NULL */

extern void throw_to_top_level __P((void));
extern int test_eaccess __P((char *, int));

extern int extended_glob;

/* Global variable which controls whether or not * matches .*.
   Non-zero means don't match .*.  */
int noglob_dot_filenames = 1;

/* Global variable which controls whether or not filename globbing
   is done without regard to case. */
int glob_ignore_case = 0;

/* Global variable to return to signify an error in globbing. */
char *glob_error_return;

/* Some forward declarations. */
static int skipname __P((char *, char *));
#if HANDLE_MULTIBYTE
static int mbskipname __P((char *, char *));
#endif
#if HANDLE_MULTIBYTE
static void udequote_pathname __P((char *));
static void wdequote_pathname __P((char *));
#else
#  define dequote_pathname udequote_pathname
#endif
static void dequote_pathname __P((char *));
static int glob_testdir __P((char *));
static char **glob_dir_to_array __P((char *, char **, int));

/* Compile `glob_loop.c' for single-byte characters. */
#define CHAR    unsigned char
#define INT int
#define L(CS)   CS
#define INTERNAL_GLOB_PATTERN_P internal_glob_pattern_p
#include "glob_loop.c"

/* Compile `glob_loop.c' again for multibyte characters. */
#if HANDLE_MULTIBYTE

#define CHAR    wchar_t
#define INT wint_t
#define L(CS)   L##CS
#define INTERNAL_GLOB_PATTERN_P internal_glob_wpattern_p
#include "glob_loop.c"

#endif /* HANDLE_MULTIBYTE */

/* And now a function that calls either the single-byte or multibyte version
   of internal_glob_pattern_p. */
int
glob_pattern_p (pattern)
     const char *pattern;
{
#if HANDLE_MULTIBYTE
  mbstate_t ps;
  size_t n;
  wchar_t *wpattern;
  int r;

  if (MB_CUR_MAX == 1)
    return (internal_glob_pattern_p (pattern));

  /* Convert strings to wide chars, and call the multibyte version. */
  memset (&ps, '\0', sizeof (ps));
  n = xmbsrtowcs (NULL, (const char **)&pattern, 0, &ps);
  if (n == (size_t)-1)
    /* Oops.  Invalid multibyte sequence.  Try it as single-byte sequence. */
    return (internal_glob_pattern_p (pattern));
  wpattern = (wchar_t *)xmalloc ((n + 1) * sizeof (wchar_t));
  (void) xmbsrtowcs (wpattern, (const char **)&pattern, n + 1, &ps);
  r = internal_glob_wpattern_p (wpattern);
  free (wpattern);
  return r;
#else
  return (internal_glob_pattern_p (pattern));
#endif
}

/* Return 1 if DNAME should be skipped according to PAT.  Mostly concerned
   with matching leading `.'. */

static int
skipname (pat, dname)
     char *pat;
     char *dname;
{
  /* If a leading dot need not be explicitly matched, and the pattern
     doesn't start with a `.', don't match `.' or `..' */
  if (noglob_dot_filenames == 0 && pat[0] != '.' &&
    (pat[0] != '\\' || pat[1] != '.') &&
    (dname[0] == '.' &&
      (dname[1] == '\0' || (dname[1] == '.' && dname[2] == '\0'))))
    return 1;

  /* If a dot must be explicity matched, check to see if they do. */
  else if (noglob_dot_filenames && dname[0] == '.' && pat[0] != '.' &&
    (pat[0] != '\\' || pat[1] != '.'))
    return 1;

  return 0;
}

#if HANDLE_MULTIBYTE
/* Return 1 if DNAME should be skipped according to PAT.  Handles multibyte
   characters in PAT and DNAME.  Mostly concerned with matching leading `.'. */

static int
mbskipname (pat, dname)
     char *pat, *dname;
{
  char *pat_bak, *dn_bak;
  wchar_t *pat_wc, *dn_wc;
  mbstate_t pat_ps, dn_ps;
  size_t pat_n, dn_n, n;

  n = strlen(pat);
  pat_bak = (char *) alloca (n + 1);
  memcpy (pat_bak, pat, n + 1);

  n = strlen(dname);
  dn_bak = (char *) alloca (n + 1);
  memcpy (dn_bak, dname,  n + 1);

  memset(&pat_ps, '\0', sizeof(mbstate_t));
  memset(&dn_ps, '\0', sizeof(mbstate_t));

  pat_n = xmbsrtowcs (NULL, (const char **)&pat_bak, 0, &pat_ps);
  dn_n = xmbsrtowcs (NULL, (const char **)&dn_bak, 0, &dn_ps);

  if (pat_n != (size_t)-1 && dn_n !=(size_t)-1)
    {
      pat_wc = (wchar_t *) alloca ((pat_n + 1) * sizeof(wchar_t));
      dn_wc = (wchar_t *) alloca ((dn_n + 1) * sizeof(wchar_t));

      (void) xmbsrtowcs (pat_wc, (const char **)&pat_bak, pat_n + 1, &pat_ps);
      (void) xmbsrtowcs (dn_wc, (const char **)&dn_bak, dn_n + 1, &dn_ps);

      /* If a leading dot need not be explicitly matched, and the
     pattern doesn't start with a `.', don't match `.' or `..' */
      if (noglob_dot_filenames == 0 && pat_wc[0] != L'.' &&
        (pat_wc[0] != L'\\' || pat_wc[1] != L'.') &&
        (dn_wc[0] == L'.' &&
          (dn_wc[1] == L'\0' || (dn_wc[1] == L'.' && dn_wc[2] == L'\0'))))
    return 1;

      /* If a leading dot must be explicity matched, check to see if the
     pattern and dirname both have one. */
     else if (noglob_dot_filenames && dn_wc[0] == L'.' &&
       pat_wc[0] != L'.' &&
       (pat_wc[0] != L'\\' || pat_wc[1] != L'.'))
    return 1;
    }

  return 0;
}
#endif /* HANDLE_MULTIBYTE */

/* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */
static void
udequote_pathname (pathname)
     char *pathname;
{
  register int i, j;

  for (i = j = 0; pathname && pathname[i]; )
    {
      if (pathname[i] == '\\')
    i++;

      pathname[j++] = pathname[i++];

      if (!pathname[i - 1])
    break;
    }
  pathname[j] = '\0';
}

#if HANDLE_MULTIBYTE
/* Remove backslashes quoting characters in PATHNAME by modifying PATHNAME. */
static void
wdequote_pathname (pathname)
     char *pathname;
{
  mbstate_t ps;
  size_t len, n;
  wchar_t *wpathname;
  char *pathname_bak;
  int i, j;

  len = strlen (pathname);
  pathname_bak = (char *) alloca (len + 1);
  memcpy (pathname_bak, pathname , len + 1);

  /* Convert the strings into wide characters.  */
  memset (&ps, '\0', sizeof (ps));
  n = xmbsrtowcs (NULL, (const char **)&pathname_bak, 0, &ps);
  if (n == (size_t) -1)
    /* Something wrong. */
    return;

  wpathname = (wchar_t *) alloca ((n + 1) * sizeof (wchar_t));
  (void) xmbsrtowcs (wpathname, (const char **)&pathname_bak, n + 1, &ps);

  for (i = j = 0; wpathname && wpathname[i]; )
    {
      if (wpathname[i] == L'\\')
    i++;

      wpathname[j++] = wpathname[i++];

      if (!wpathname[i - 1])
    break;
    }
  wpathname[j] = L'\0';

  /* Convert the wide character string into unibyte character set. */
  memset (&ps, '\0', sizeof(mbstate_t));
  n = wcsrtombs(pathname, (const wchar_t **)&wpathname, len, &ps);
  pathname[len] = '\0';
}

static void
dequote_pathname (pathname)
     char *pathname;
{
  if (MB_CUR_MAX > 1)
    wdequote_pathname (pathname);
  else
    udequote_pathname (pathname);
}
#endif /* HANDLE_MULTIBYTE */

/* Test whether NAME exists. */

#if defined (HAVE_LSTAT)
#  define GLOB_TESTNAME(name)  (lstat (name, &finfo))
#else /* !HAVE_LSTAT */
#  if !defined (AFS)
#    define GLOB_TESTNAME(name)  (test_eaccess (nextname, F_OK))
#  else /* AFS */
#    define GLOB_TESTNAME(name)  (access (nextname, F_OK))
#  endif /* AFS */
#endif /* !HAVE_LSTAT */

/* Return 0 if DIR is a directory, -1 otherwise. */
static int
glob_testdir (dir)
     char *dir;
{
  struct stat finfo;

  if (stat (dir, &finfo) < 0)
    return (-1);

  if (S_ISDIR (finfo.st_mode) == 0)
    return (-1);

  return (0);
}

/* Return a vector of names of files in directory DIR
   whose names match glob pattern PAT.
   The names are not in any particular order.
   Wildcards at the beginning of PAT do not match an initial period.

   The vector is terminated by an element that is a null pointer.

   To free the space allocated, first free the vector's elements,
   then free the vector.

   Return 0 if cannot get enough memory to hold the pointer
   and the names.

   Return -1 if cannot access directory DIR.
   Look in errno for more information.  */

char **
glob_vector (pat, dir, flags)
     char *pat;
     char *dir;
     int flags;
{
  struct globval
    {
      struct globval *next;
      char *name;
    };

  DIR *d;
  register struct dirent *dp;
  struct globval *lastlink;
  register struct globval *nextlink;
  register char *nextname, *npat;
  unsigned int count;
  int lose, skip;
  register char **name_vector;
  register unsigned int i;
  int mflags;       /* Flags passed to strmatch (). */

  lastlink = 0;
  count = lose = skip = 0;

  /* If PAT is empty, skip the loop, but return one (empty) filename. */
  if (pat == 0 || *pat == '\0')
    {
      if (glob_testdir (dir) < 0)
    return ((char **) &glob_error_return);

      nextlink = (struct globval *)alloca (sizeof (struct globval));
      nextlink->next = (struct globval *)0;
      nextname = (char *) malloc (1);
      if (nextname == 0)
    lose = 1;
      else
    {
      lastlink = nextlink;
      nextlink->name = nextname;
      nextname[0] = '\0';
      count = 1;
    }

      skip = 1;
    }

  /* If the filename pattern (PAT) does not contain any globbing characters,
     we can dispense with reading the directory, and just see if there is
     a filename `DIR/PAT'.  If there is, and we can access it, just make the
     vector to return and bail immediately. */
  if (skip == 0 && glob_pattern_p (pat) == 0)
    {
      int dirlen;
      struct stat finfo;

      if (glob_testdir (dir) < 0)
    return ((char **) &glob_error_return);

      dirlen = strlen (dir);
      nextname = (char *)malloc (dirlen + strlen (pat) + 2);
      npat = (char *)malloc (strlen (pat) + 1);
      if (nextname == 0 || npat == 0)
    lose = 1;
      else
    {
      strcpy (npat, pat);
      dequote_pathname (npat);

      strcpy (nextname, dir);
      nextname[dirlen++] = '/';
      strcpy (nextname + dirlen, npat);

      if (GLOB_TESTNAME (nextname) >= 0)
        {
          free (nextname);
          nextlink = (struct globval *)alloca (sizeof (struct globval));
          nextlink->next = (struct globval *)0;
          lastlink = nextlink;
          nextlink->name = npat;
          count = 1;
        }
      else
        {
          free (nextname);
          free (npat);
        }
    }

      skip = 1;
    }

  if (skip == 0)
    {
      /* Open the directory, punting immediately if we cannot.  If opendir
     is not robust (i.e., it opens non-directories successfully), test
     that DIR is a directory and punt if it's not. */
#if defined (OPENDIR_NOT_ROBUST)
      if (glob_testdir (dir) < 0)
    return ((char **) &glob_error_return);
#endif

      d = opendir (dir);
      if (d == NULL)
    return ((char **) &glob_error_return);

      /* Compute the flags that will be passed to strmatch().  We don't
     need to do this every time through the loop. */
      mflags = (noglob_dot_filenames ? FNM_PERIOD : 0) | FNM_PATHNAME;

#ifdef FNM_CASEFOLD
      if (glob_ignore_case)
    mflags |= FNM_CASEFOLD;
#endif

      if (extended_glob)
    mflags |= FNM_EXTMATCH;

      /* Scan the directory, finding all names that match.
     For each name that matches, allocate a struct globval
     on the stack and store the name in it.
     Chain those structs together; lastlink is the front of the chain.  */
      while (1)
    {
      /* Make globbing interruptible in the shell. */
      if (interrupt_state)
        {
          lose = 1;
          break;
        }
      
      dp = readdir (d);
      if (dp == NULL)
        break;

      /* If this directory entry is not to be used, try again. */
      if (REAL_DIR_ENTRY (dp) == 0)
        continue;

#if HANDLE_MULTIBYTE
      if (MB_CUR_MAX > 1 && mbskipname (pat, dp->d_name))
        continue;
      else
#endif
      if (skipname (pat, dp->d_name))
        continue;

      if (strmatch (pat, dp->d_name, mflags) != FNM_NOMATCH)
        {
          nextlink = (struct globval *) alloca (sizeof (struct globval));
          nextlink->next = lastlink;
          nextname = (char *) malloc (D_NAMLEN (dp) + 1);
          if (nextname == NULL)
        {
          lose = 1;
          break;
        }
          lastlink = nextlink;
          nextlink->name = nextname;
          bcopy (dp->d_name, nextname, D_NAMLEN (dp) + 1);
          ++count;
        }
    }

      (void) closedir (d);
    }

  if (lose == 0)
    {
      name_vector = (char **) malloc ((count + 1) * sizeof (char *));
      lose |= name_vector == NULL;
    }

  /* Have we run out of memory?  */
  if (lose)
    {
      /* Here free the strings we have got.  */
      while (lastlink)
    {
      free (lastlink->name);
      lastlink = lastlink->next;
    }

      QUIT;

      return ((char **)NULL);
    }

  /* Copy the name pointers from the linked list into the vector.  */
  for (i = 0; i < count; ++i)
    {
      name_vector[i] = lastlink->name;
      lastlink = lastlink->next;
    }

  name_vector[count] = NULL;
  return (name_vector);
}

/* Return a new array which is the concatenation of each string in ARRAY
   to DIR.  This function expects you to pass in an allocated ARRAY, and
   it takes care of free()ing that array.  Thus, you might think of this
   function as side-effecting ARRAY.  This should handle GX_MARKDIRS. */
static char **
glob_dir_to_array (dir, array, flags)
     char *dir, **array;
     int flags;
{
  register unsigned int i, l;
  int add_slash;
  char **result, *new;
  struct stat sb;

  l = strlen (dir);
  if (l == 0)
    {
      if (flags & GX_MARKDIRS)
    for (i = 0; array[i]; i++)
      {
        if ((stat (array[i], &sb) == 0) && S_ISDIR (sb.st_mode))
          {
        l = strlen (array[i]);
        new = (char *)realloc (array[i], l + 2);
        if (new == 0)
          return NULL;
        new[l] = '/';
        new[l+1] = '\0';
        array[i] = new;
          }
      }
      return (array);
    }

  add_slash = dir[l - 1] != '/';

  i = 0;
  while (array[i] != NULL)
    ++i;

  result = (char **) malloc ((i + 1) * sizeof (char *));
  if (result == NULL)
    return (NULL);

  for (i = 0; array[i] != NULL; i++)
    {
      /* 3 == 1 for NUL, 1 for slash at end of DIR, 1 for GX_MARKDIRS */
      result[i] = (char *) malloc (l + strlen (array[i]) + 3);

      if (result[i] == NULL)
    return (NULL);

      strcpy (result[i], dir);
      if (add_slash)
    result[i][l] = '/';
      strcpy (result[i] + l + add_slash, array[i]);
      if (flags & GX_MARKDIRS)
    {
      if ((stat (result[i], &sb) == 0) && S_ISDIR (sb.st_mode))
        {
          size_t rlen;
          rlen = strlen (result[i]);
          result[i][rlen] = '/';
          result[i][rlen+1] = '\0';
        }
    }
    }
  result[i] = NULL;

  /* Free the input array.  */
  for (i = 0; array[i] != NULL; i++)
    free (array[i]);
  free ((char *) array);

  return (result);
}

/* Do globbing on PATHNAME.  Return an array of pathnames that match,
   marking the end of the array with a null-pointer as an element.
   If no pathnames match, then the array is empty (first element is null).
   If there isn't enough memory, then return NULL.
   If a file system error occurs, return -1; `errno' has the error code.  */
char **
glob_filename (pathname, flags)
     char *pathname;
     int flags;
{
  char **result;
  unsigned int result_size;
  char *directory_name, *filename;
  unsigned int directory_len;

  result = (char **) malloc (sizeof (char *));
  result_size = 1;
  if (result == NULL)
    return (NULL);

  result[0] = NULL;

  /* Find the filename.  */
  filename = strrchr (pathname, '/');
  if (filename == NULL)
    {
      filename = pathname;
      directory_name = "";
      directory_len = 0;
    }
  else
    {
      directory_len = (filename - pathname) + 1;
      directory_name = (char *) alloca (directory_len + 1);

      bcopy (pathname, directory_name, directory_len);
      directory_name[directory_len] = '\0';
      ++filename;
    }

  /* If directory_name contains globbing characters, then we
     have to expand the previous levels.  Just recurse. */
  if (glob_pattern_p (directory_name))
    {
      char **directories;
      register unsigned int i;

      if (directory_name[directory_len - 1] == '/')
    directory_name[directory_len - 1] = '\0';

      directories = glob_filename (directory_name, flags & ~GX_MARKDIRS);

      if (directories == NULL)
    goto memory_error;
      else if (directories == (char **)&glob_error_return)
    {
      free ((char *) result);
      return ((char **) &glob_error_return);
    }
      else if (*directories == NULL)
    {
      free ((char *) directories);
      free ((char *) result);
      return ((char **) &glob_error_return);
    }

      /* We have successfully globbed the preceding directory name.
     For each name in DIRECTORIES, call glob_vector on it and
     FILENAME.  Concatenate the results together.  */
      for (i = 0; directories[i] != NULL; ++i)
    {
      char **temp_results;

      /* Scan directory even on a NULL pathname.  That way, `*h/'
         returns only directories ending in `h', instead of all
         files ending in `h' with a `/' appended. */
      temp_results = glob_vector (filename, directories[i], flags & ~GX_MARKDIRS);

      /* Handle error cases. */
      if (temp_results == NULL)
        goto memory_error;
      else if (temp_results == (char **)&glob_error_return)
        /* This filename is probably not a directory.  Ignore it.  */
        ;
      else
        {
          char **array;
          register unsigned int l;

          array = glob_dir_to_array (directories[i], temp_results, flags);
          l = 0;
          while (array[l] != NULL)
        ++l;

          result =
        (char **)realloc (result, (result_size + l) * sizeof (char *));

          if (result == NULL)
        goto memory_error;

          for (l = 0; array[l] != NULL; ++l)
        result[result_size++ - 1] = array[l];

          result[result_size - 1] = NULL;

          /* Note that the elements of ARRAY are not freed.  */
          free ((char *) array);
        }
    }
      /* Free the directories.  */
      for (i = 0; directories[i]; i++)
    free (directories[i]);

      free ((char *) directories);

      return (result);
    }

  /* If there is only a directory name, return it. */
  if (*filename == '\0')
    {
      result = (char **) realloc ((char *) result, 2 * sizeof (char *));
      if (result == NULL)
    return (NULL);
      /* Handle GX_MARKDIRS here. */
      result[0] = (char *) malloc (directory_len + 1);
      if (result[0] == NULL)
    goto memory_error;
      bcopy (directory_name, result[0], directory_len + 1);
      result[1] = NULL;
      return (result);
    }
  else
    {
      char **temp_results;

      /* There are no unquoted globbing characters in DIRECTORY_NAME.
     Dequote it before we try to open the directory since there may
     be quoted globbing characters which should be treated verbatim. */
      if (directory_len > 0)
    dequote_pathname (directory_name);

      /* We allocated a small array called RESULT, which we won't be using.
     Free that memory now. */
      free (result);

      /* Just return what glob_vector () returns appended to the
     directory name. */
      temp_results = glob_vector (filename,
                  (directory_len == 0 ? "." : directory_name),
                  flags & ~GX_MARKDIRS);

      if (temp_results == NULL || temp_results == (char **)&glob_error_return)
    return (temp_results);

      return (glob_dir_to_array (directory_name, temp_results, flags));
    }

  /* We get to memory_error if the program has run out of memory, or
     if this is the shell, and we have been interrupted. */
 memory_error:
  if (result != NULL)
    {
      register unsigned int i;
      for (i = 0; result[i] != NULL; ++i)
    free (result[i]);
      free ((char *) result);
    }

  QUIT;

  return (NULL);
}

#if defined (TEST)

main (argc, argv)
     int argc;
     char **argv;
{
  unsigned int i;

  for (i = 1; i < argc; ++i)
    {
      char **value = glob_filename (argv[i], 0);
      if (value == NULL)
    puts ("Out of memory.");
      else if (value == &glob_error_return)
    perror (argv[i]);
      else
    for (i = 0; value[i] != NULL; i++)
      puts (value[i]);
    }

  exit (0);
}
#endif  /* TEST.  */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/glob/strmatch.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/glob/strmatch.c used in: Step 1196
/* strmatch.c -- ksh-like extended pattern matching for the shell and filename
        globbing. */

/* Copyright (C) 1991-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.
   
   Bash 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.
          
   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include "stdc.h"
#include "strmatch.h"

/* Structured this way so that if HAVE_LIBC_FNM_EXTMATCH is defined, the
   matching portion of the library (smatch.c) is not linked into the shell. */

#ifndef HAVE_LIBC_FNM_EXTMATCH
extern int xstrmatch __P((char *, char *, int));
#else
#  define xstrmatch fnmatch
#endif

int
strmatch (pattern, string, flags)
     char *pattern;
     char *string;
     int flags;
{
  if (string == 0 || pattern == 0)
    return FNM_NOMATCH;

  return (xstrmatch (pattern, string, flags));
}

#ifdef TEST
main (c, v)
     int c;
     char **v;
{
  char *string, *pat;

  string = v[1];
  pat = v[2];

  if (strmatch (pat, string, 0) == 0)
    {
      printf ("%s matches %s\n", string, pat);
      exit (0);
    }
  else
    {
      printf ("%s does not match %s\n", string, pat);
      exit (1);
    }
}
#endif

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/glob/strmatch.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/glob/strmatch.h used in: Step 1196, Step 1197
/* Copyright (C) 1991 Free Software Foundation, Inc.
This file is part of the GNU C Library.

The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

The GNU C Library 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.

You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111 USA.  */

#ifndef _STRMATCH_H
#define _STRMATCH_H 1

#ifdef HAVE_LIBC_FNM_EXTMATCH

#include <fnmatch.h>

#else /* !HAVE_LIBC_FNM_EXTMATCH */

#include "stdc.h"

/* We #undef these before defining them because some losing systems
   (HP-UX A.08.07 for example) define these in <unistd.h>.  */
#undef  FNM_PATHNAME
#undef  FNM_NOESCAPE
#undef  FNM_PERIOD

/* Bits set in the FLAGS argument to `strmatch'.  */

/* standard flags are like fnmatch(3). */
#define FNM_PATHNAME    (1 << 0) /* No wildcard can ever match `/'.  */
#define FNM_NOESCAPE    (1 << 1) /* Backslashes don't quote special chars.  */
#define FNM_PERIOD  (1 << 2) /* Leading `.' is matched only explicitly.  */

/* extended flags not available in most libc fnmatch versions, but we undef
   them to avoid any possible warnings. */
#undef FNM_LEADING_DIR
#undef FNM_CASEFOLD
#undef FNM_EXTMATCH

#define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */
#define FNM_CASEFOLD    (1 << 4) /* Compare without regard to case. */
#define FNM_EXTMATCH    (1 << 5) /* Use ksh-like extended matching. */

/* Value returned by `strmatch' if STRING does not match PATTERN.  */
#undef FNM_NOMATCH

#define FNM_NOMATCH 1

/* Match STRING against the filename pattern PATTERN,
   returning zero if it matches, FNM_NOMATCH if not.  */
extern int strmatch __P((char *, char *, int));

#endif /* !HAVE_LIBC_FNM_EXTMATCH */

#endif /* _STRMATCH_H */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/glob/smatch.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/glob/smatch.c used in: Step 1197
/* strmatch.c -- ksh-like extended pattern matching for the shell and filename
        globbing. */

/* Copyright (C) 1991-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.
   
   Bash 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.
          
   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include <stdio.h>  /* for debugging */
                
#include "strmatch.h"
#include <chartypes.h>

#include "bashansi.h"
#include "shmbutil.h"
#include "xmalloc.h"

/* First, compile `sm_loop.c' for single-byte characters. */
#define CHAR    unsigned char
#define U_CHAR  unsigned char
#define XCHAR   char
#define INT int
#define L(CS)   CS
#define INVALID -1

#undef STREQ
#undef STREQN
#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0)
#define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0)

/* We use strcoll(3) for range comparisons in bracket expressions,
   even though it can have unwanted side effects in locales
   other than POSIX or US.  For instance, in the de locale, [A-Z] matches
   all characters. */

#if defined (HAVE_STRCOLL)
/* Helper function for collating symbol equivalence. */
static int rangecmp (c1, c2)
     int c1, c2;
{
  static char s1[2] = { ' ', '\0' };
  static char s2[2] = { ' ', '\0' };
  int ret;

  /* Eight bits only.  Period. */
  c1 &= 0xFF;
  c2 &= 0xFF;

  if (c1 == c2)
    return (0);

  s1[0] = c1;
  s2[0] = c2;

  if ((ret = strcoll (s1, s2)) != 0)
    return ret;
  return (c1 - c2);
}
#else /* !HAVE_STRCOLL */
#  define rangecmp(c1, c2)  ((int)(c1) - (int)(c2))
#endif /* !HAVE_STRCOLL */

#if defined (HAVE_STRCOLL)
static int
collequiv (c1, c2)
     int c1, c2;
{
  return (rangecmp (c1, c2) == 0);
}
#else
#  define collequiv(c1, c2) ((c1) == (c2))
#endif

#define _COLLSYM    _collsym
#define __COLLSYM   __collsym
#define POSIXCOLL   posix_collsyms
#include "collsyms.h"

static int
collsym (s, len)
     char *s;
     int len;
{
  register struct _collsym *csp;

  for (csp = posix_collsyms; csp->name; csp++)
    {
      if (STREQN(csp->name, s, len) && csp->name[len] == '\0')
    return (csp->code);
    }
  if (len == 1)
    return s[0];
  return INVALID;
}

/* unibyte character classification */
#if !defined (isascii) && !defined (HAVE_ISASCII)
#  define isascii(c)    ((unsigned int)(c) <= 0177)
#endif

enum char_class
  {
    CC_NO_CLASS = 0,
    CC_ASCII, CC_ALNUM, CC_ALPHA, CC_BLANK, CC_CNTRL, CC_DIGIT, CC_GRAPH,
    CC_LOWER, CC_PRINT, CC_PUNCT, CC_SPACE, CC_UPPER, CC_WORD, CC_XDIGIT
  };

static char const *const cclass_name[] =
  {
    "",
    "ascii", "alnum", "alpha", "blank", "cntrl", "digit", "graph",
    "lower", "print", "punct", "space", "upper", "word", "xdigit"
  };

#define N_CHAR_CLASS (sizeof(cclass_name) / sizeof (cclass_name[0]))

static int
is_cclass (c, name)
     int c;
     const char *name;
{
  enum char_class char_class = CC_NO_CLASS;
  int i, result;

  for (i = 1; i < N_CHAR_CLASS; i++)
    {
      if (STREQ (name, cclass_name[i]))
    {
      char_class = (enum char_class)i;
      break;
    }
    }

  if (char_class == 0)
    return -1;

  switch (char_class)
    {
      case CC_ASCII:
    result = isascii (c);
    break;
      case CC_ALNUM:
    result = ISALNUM (c);
    break;
      case CC_ALPHA:
    result = ISALPHA (c);
    break;
      case CC_BLANK:  
    result = ISBLANK (c);
    break;
      case CC_CNTRL:
    result = ISCNTRL (c);
    break;
      case CC_DIGIT:
    result = ISDIGIT (c);
    break;
      case CC_GRAPH:
    result = ISGRAPH (c);
    break;
      case CC_LOWER:
    result = ISLOWER (c);
    break;
      case CC_PRINT: 
    result = ISPRINT (c);
    break;
      case CC_PUNCT:
    result = ISPUNCT (c);
    break;
      case CC_SPACE:
    result = ISSPACE (c);
    break;
      case CC_UPPER:
    result = ISUPPER (c);
    break;
      case CC_WORD:
        result = (ISALNUM (c) || c == '_');
    break;
      case CC_XDIGIT:
    result = ISXDIGIT (c);
    break;
      default:
    result = -1;
    break;
    }

  return result;  
}

/* Now include `sm_loop.c' for single-byte characters. */
/* The result of FOLD is an `unsigned char' */
# define FOLD(c) ((flags & FNM_CASEFOLD) \
    ? TOLOWER ((unsigned char)c) \
    : ((unsigned char)c))

#define FCT         internal_strmatch
#define GMATCH          gmatch
#define COLLSYM         collsym
#define PARSE_COLLSYM       parse_collsym
#define BRACKMATCH      brackmatch
#define PATSCAN         patscan
#define STRCOMPARE      strcompare
#define EXTMATCH        extmatch
#define STRCHR(S, C)        strchr((S), (C))
#define STRCOLL(S1, S2)     strcoll((S1), (S2))
#define STRLEN(S)       strlen(S)
#define STRCMP(S1, S2)      strcmp((S1), (S2))
#define RANGECMP(C1, C2)    rangecmp((C1), (C2))
#define COLLEQUIV(C1, C2)   collequiv((C1), (C2))
#define CTYPE_T         enum char_class
#define IS_CCLASS(C, S)     is_cclass((C), (S))
#include "sm_loop.c"

#if HANDLE_MULTIBYTE

#  define CHAR      wchar_t
#  define U_CHAR    wint_t
#  define XCHAR     wchar_t
#  define INT       wint_t
#  define L(CS)     L##CS
#  define INVALID   WEOF

#  undef STREQ
#  undef STREQN
#  define STREQ(s1, s2) ((wcscmp (s1, s2) == 0))
#  define STREQN(a, b, n) ((a)[0] == (b)[0] && wcsncmp(a, b, n) == 0)

static int
rangecmp_wc (c1, c2)
     wint_t c1, c2;
{
  static wchar_t s1[2] = { L' ', L'\0' };
  static wchar_t s2[2] = { L' ', L'\0' };
  int ret;

  if (c1 == c2)
    return 0;

  s1[0] = c1;
  s2[0] = c2;

  return (wcscoll (s1, s2));
}

static int
collequiv_wc (c, equiv)
     wint_t c, equiv;
{
  return (!(c - equiv));
}

/* Helper function for collating symbol. */
#  define _COLLSYM  _collwcsym
#  define __COLLSYM __collwcsym
#  define POSIXCOLL posix_collwcsyms
#  include "collsyms.h"

static wint_t
collwcsym (s, len)
     wchar_t *s;
     int len;
{
  register struct _collwcsym *csp;

  for (csp = posix_collwcsyms; csp->name; csp++)
    {
      if (STREQN(csp->name, s, len) && csp->name[len] == L'\0')
    return (csp->code);
    }
  if (len == 1)
    return s[0];
  return INVALID;
}

static int
is_wcclass (wc, name)
     wint_t wc;
     wchar_t *name;
{
  char *mbs;
  mbstate_t state;
  size_t mbslength;
  wctype_t desc;
  int want_word;

  if ((wctype ("ascii") == (wctype_t)0) && (wcscmp (name, L"ascii") == 0))
    {
      int c;

      if ((c = wctob (wc)) == EOF)
    return 0;
      else
        return (c <= 0x7F);
    }

  want_word = (wcscmp (name, L"word") == 0);
  if (want_word)
    name = L"alnum";

  memset (&state, '\0', sizeof (mbstate_t));
  mbs = (char *) malloc (wcslen(name) * MB_CUR_MAX + 1);
  mbslength = wcsrtombs(mbs, (const wchar_t **)&name, (wcslen(name) * MB_CUR_MAX + 1), &state);

  if (mbslength == (size_t)-1 || mbslength == (size_t)-2)
    {
      free (mbs);
      return -1;
    }
  desc = wctype (mbs);
  free (mbs);

  if (desc == (wctype_t)0)
    return -1;

  if (want_word)
    return (iswctype (wc, desc) || wc == L'_');
  else
    return (iswctype (wc, desc));
}

/* Now include `sm_loop.c' for multibyte characters. */
#define FOLD(c) ((flags & FNM_CASEFOLD) && iswupper (c) ? towlower (c) : (c))
#define FCT         internal_wstrmatch
#define GMATCH          gmatch_wc
#define COLLSYM         collwcsym
#define PARSE_COLLSYM       parse_collwcsym
#define BRACKMATCH      brackmatch_wc
#define PATSCAN         patscan_wc
#define STRCOMPARE      wscompare
#define EXTMATCH        extmatch_wc
#define STRCHR(S, C)        wcschr((S), (C))
#define STRCOLL(S1, S2)     wcscoll((S1), (S2))
#define STRLEN(S)       wcslen(S)
#define STRCMP(S1, S2)      wcscmp((S1), (S2))
#define RANGECMP(C1, C2)    rangecmp_wc((C1), (C2))
#define COLLEQUIV(C1, C2)   collequiv_wc((C1), (C2))
#define CTYPE_T         enum char_class
#define IS_CCLASS(C, S)     is_wcclass((C), (S))
#include "sm_loop.c"

#endif /* HAVE_MULTIBYTE */

int
xstrmatch (pattern, string, flags)
     char *pattern;
     char *string;
     int flags;
{
#if HANDLE_MULTIBYTE
  int ret;
  mbstate_t ps;
  size_t n;
  char *pattern_bak;
  wchar_t *wpattern, *wstring;

  if (MB_CUR_MAX == 1)
    return (internal_strmatch (pattern, string, flags));

  pattern_bak = (char *)xmalloc (strlen (pattern) + 1);
  strcpy (pattern_bak, pattern);

  memset (&ps, '\0', sizeof (mbstate_t));
  n = xmbsrtowcs (NULL, (const char **)&pattern, 0, &ps);
  if (n == (size_t)-1 || n == (size_t)-2)
    {
      free (pattern_bak);
      return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags));
    }

  wpattern = (wchar_t *)xmalloc ((n + 1) * sizeof (wchar_t));
  (void) xmbsrtowcs (wpattern, (const char **)&pattern, n + 1, &ps);

  memset (&ps, '\0', sizeof (mbstate_t));
  n = xmbsrtowcs (NULL, (const char **)&string, 0, &ps);
  if (n == (size_t)-1 || n == (size_t)-2)
    {
      free (wpattern);
      ret = internal_strmatch (pattern_bak, string, flags);
      free (pattern_bak);
      return ret;
    }

  wstring = (wchar_t *)xmalloc ((n + 1) * sizeof (wchar_t));
  (void) xmbsrtowcs (wstring, (const char **)&string, n + 1, &ps);

  ret = internal_wstrmatch (wpattern, wstring, flags);

  free (pattern_bak);
  free (wpattern);
  free (wstring);

  return ret;
#else
  return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags));
#endif /* !HANDLE_MULTIBYTE */
}

https://git.savannah.gnu.org/cgit/bash.git/tree/include/shmbutil.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/include/shmbutil.h used in: Step 1197, Step 1198
/* shmbutil.h -- utility functions for multibyte characters. */

/* Copyright (C) 2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
                                 
#if !defined (_SH_MBUTIL_H_)
#define _SH_MBUTIL_H_

#include "stdc.h"

/************************************************/
/* check multibyte capability for I18N code     */
/************************************************/

/* For platforms which support the ISO C amendement 1 functionality we
   support user defined character classes.  */
   /* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>.  */
#if defined (HAVE_WCTYPE_H) && defined (HAVE_WCHAR_H)
#  include <wchar.h>
#  include <wctype.h>
#  if defined (HAVE_MBSRTOWCS) /* system is supposed to support XPG5 */
#    define HANDLE_MULTIBYTE      1
#  endif
#endif /* HAVE_WCTYPE_H && HAVE_WCHAR_H */

/* Some systems, like BeOS, have multibyte encodings but lack mbstate_t.  */
#if HANDLE_MULTIBYTE && !defined (HAVE_MBSTATE_T)
#  define wcsrtombs(dest, src, len, ps) (wcsrtombs) (dest, src, len, 0)
#  define mbsrtowcs(dest, src, len, ps) (mbsrtowcs) (dest, src, len, 0)
#  define wcrtomb(s, wc, ps) (wcrtomb) (s, wc, 0)
#  define mbrtowc(pwc, s, n, ps) (mbrtowc) (pwc, s, n, 0)
#  define mbrlen(s, n, ps) (mbrlen) (s, n, 0)
#  define mbstate_t int
#endif /* HANDLE_MULTIBYTE && !HAVE_MBSTATE_T */

/* Make sure MB_LEN_MAX is at least 16 on systems that claim to be able to
   handle multibyte chars (some systems define MB_LEN_MAX as 1) */
#ifdef HANDLE_MULTIBYTE
#  include <limits.h>
#  if defined(MB_LEN_MAX) && (MB_LEN_MAX < 16)
#    undef MB_LEN_MAX
#  endif
#  if !defined (MB_LEN_MAX)
#    define MB_LEN_MAX 16
#  endif
#endif /* HANDLE_MULTIBYTE */

/************************************************/
/* end of multibyte capability checks for I18N  */
/************************************************/

#if defined (HANDLE_MULTIBYTE)

extern size_t xmbsrtowcs __P((wchar_t *, const char **, size_t, mbstate_t *));

extern char *xstrchr __P((const char *, int));

#else /* !HANDLE_MULTIBYTE */

#undef MB_LEN_MAX
#undef MB_CUR_MAX

#define MB_LEN_MAX  1
#define MB_CUR_MAX  1

#undef xstrchr
#define xstrchr(s, c)   strchr(s, c)

#endif /* !HANDLE_MULTIBYTE */

/* Declare and initialize a multibyte state.  Call must be terminated
   with `;'. */
#if defined (HANDLE_MULTIBYTE)
#  define DECLARE_MBSTATE \
    mbstate_t state; \
    memset (&state, '\0', sizeof (mbstate_t))
#else
#  define DECLARE_MBSTATE
#endif  /* !HANDLE_MULTIBYTE */

/* Initialize or reinitialize a multibyte state named `state'.  Call must be
   terminated with `;'. */
#if defined (HANDLE_MULTIBYTE)
#  define INITIALIZE_MBSTATE memset (&state, '\0', sizeof (mbstate_t))
#else
#  define INITIALIZE_MBSTATE
#endif  /* !HANDLE_MULTIBYTE */

/* Advance one (possibly multi-byte) character in string _STR of length
   _STRSIZE, starting at index _I.  STATE must have already been declared. */
#if defined (HANDLE_MULTIBYTE)
#  define ADVANCE_CHAR(_str, _strsize, _i) \
    do \
      { \
    if (MB_CUR_MAX > 1) \
      { \
        mbstate_t state_bak; \
        size_t mblength; \
\
        state_bak = state; \
        mblength = mbrlen ((_str) + (_i), (_strsize) - (_i), &state); \
\
        if (mblength == (size_t)-2 || mblength == (size_t)-1) \
          { \
        state = state_bak; \
        (_i)++; \
          } \
        else \
          (_i) += mblength; \
      } \
    else \
      (_i)++; \
      } \
    while (0)
#else
#  define ADVANCE_CHAR(_str, _strsize, _i)  (_i)++
#endif  /* !HANDLE_MULTIBYTE */

/* Advance one (possibly multibyte) character in the string _STR of length
   _STRSIZE.
   SPECIAL:  assume that _STR will be incremented by 1 after this call. */
#if defined (HANDLE_MULTIBYTE)
#  define ADVANCE_CHAR_P(_str, _strsize) \
    do \
      { \
    if (MB_CUR_MAX > 1) \
      { \
        mbstate_t state_bak; \
        size_t mblength; \
\
        state_bak = state; \
        mblength = mbrlen ((_str), (_strsize), &state); \
\
        if (mblength == (size_t)-2 || mblength == (size_t)-1) \
          { \
        state = state_bak; \
        mblength = 1; \
          } \
        else \
          (_str) += (mblength < 1) ? 0 : (mblength - 1); \
      } \
      } \
    while (0)
#else
#  define ADVANCE_CHAR_P(_str, _strsize)
#endif  /* !HANDLE_MULTIBYTE */

/* Copy a single character from the string _SRC to the string _DST.
   _SRCEND is a pointer to the end of _SRC. */
#if defined (HANDLE_MULTIBYTE)
#  define COPY_CHAR_P(_dst, _src, _srcend) \
    do \
      { \
    if (MB_CUR_MAX > 1) \
      { \
        mbstate_t state_bak; \
        size_t mblength; \
        int _k; \
\
        state_bak = state; \
        mblength = mbrlen ((_src), (_srcend) - (_src), &state); \
        if (mblength == (size_t)-2 || mblength == (size_t)-1) \
          { \
        state = state_bak; \
        mblength = 1; \
          } \
        else \
          mblength = (mblength < 1) ? 1 : mblength; \
\
        for (_k = 0; _k < mblength; _k++) \
          *(_dst)++ = *(_src)++; \
      } \
    else \
      *(_dst)++ = *(_src)++; \
      } \
    while (0)
#else
#  define COPY_CHAR_P(_dst, _src, _srcend)  *(_dst)++ = *(_src)++
#endif  /* !HANDLE_MULTIBYTE */

/* Copy a single character from the string _SRC at index _SI to the string
   _DST at index _DI.  _SRCEND is a pointer to the end of _SRC. */
#if defined (HANDLE_MULTIBYTE)
#  define COPY_CHAR_I(_dst, _di, _src, _srcend, _si) \
    do \
      { \
    if (MB_CUR_MAX > 1) \
      { \
        mbstate_t state_bak; \
        size_t mblength; \
        int _k; \
\
        state_bak = state; \
        mblength = mbrlen ((_src) + (_si), (_srcend) - ((_src)+(_si)), &state); \
        if (mblength == (size_t)-2 || mblength == (size_t)-1) \
          { \
        state = state_bak; \
        mblength = 1; \
          } \
        else \
          mblength = (mblength < 1) ? 1 : mblength; \
\
        for (_k = 0; _k < mblength; _k++) \
          _dst[_di++] = _src[_si++]; \
      } \
    else \
      _dst[_di++] = _src[_si++]; \
      } \
    while (0)
#else
#  define COPY_CHAR_I(_dst, _di, _src, _srcend, _si)    _dst[_di++] = _src[_si++]
#endif  /* !HANDLE_MULTIBYTE */

/****************************************************************
 *                              *
 * The following are only guaranteed to work in subst.c     *
 *                              *
 ****************************************************************/

#if defined (HANDLE_MULTIBYTE)
#  define SCOPY_CHAR_I(_dst, _escchar, _sc, _src, _si, _slen) \
    do \
      { \
    if (MB_CUR_MAX > 1) \
      { \
        mbstate_t state_bak; \
        size_t mblength; \
        int _i; \
\
        state_bak = state; \
        mblength = mbrlen ((_src) + (_si), (_slen) - (_si), &state); \
        if (mblength == (size_t)-2 || mblength == (size_t)-1) \
          { \
        state = state_bak; \
        mblength = 1; \
          } \
        else \
          mblength = (mblength < 1) ? 1 : mblength; \
\
        temp = xmalloc (mblength + 2); \
        temp[0] = _escchar; \
        for (_i = 0; _i < mblength; _i++) \
          temp[_i + 1] = _src[_si++]; \
        temp[mblength + 1] = '\0'; \
\
        goto add_string; \
      } \
    else \
      { \
        _dst[0] = _escchar; \
        _dst[1] = _sc; \
      } \
      } \
    while (0)
#else
#  define SCOPY_CHAR_I(_dst, _escchar, _sc, _src, _si, _slen) \
    _dst[0] = _escchar; \
    _dst[1] = _sc
#endif  /* !HANDLE_MULTIBYTE */

#if defined (HANDLE_MULTIBYTE)
#  define SCOPY_CHAR_M(_dst, _src, _srcend, _si) \
    do \
      { \
    if (MB_CUR_MAX > 1) \
      { \
        mbstate_t state_bak; \
        size_t mblength; \
\
        state_bak = state; \
        mblength = mbrlen ((_src) + (_si), (_srcend) - ((_src) + (_si)), &state); \
        if (mblength == (size_t)-2 || mblength == (size_t)-1) \
          { \
        state = state_bak; \
        mblength = 1; \
          } \
        else \
          mblength = (mblength < 1) ? 1 : mblength; \
\
        FASTCOPY(((_src) + (_si)), (_dst), mblength); \
\
        (_dst) += mblength; \
        (_si) += mblength; \
      } \
    else \
      { \
        *(_dst)++ = _src[(_si)]; \
        (_si)++; \
      } \
      } \
    while (0)
#else
#  define SCOPY_CHAR_M(_dst, _src, _srcend, _si) \
    *(_dst)++ = _src[(_si)]; \
    (_si)++
#endif  /* !HANDLE_MULTIBYTE */

#if HANDLE_MULTIBYTE
#  define SADD_MBCHAR(_dst, _src, _si, _srcsize) \
    do \
      { \
    if (MB_CUR_MAX > 1) \
      { \
        int i; \
        mbstate_t state_bak; \
        size_t mblength; \
\
        state_bak = state; \
        mblength = mbrlen ((_src) + (_si), (_srcsize) - (_si), &state); \
        if (mblength == (size_t)-1 || mblength == (size_t)-2) \
          { \
        state = state_bak; \
        mblength = 1; \
          } \
        if (mblength < 1) \
          mblength = 1; \
\
        _dst = (char *)xmalloc (mblength + 1); \
        for (i = 0; i < mblength; i++) \
          (_dst)[i] = (_src)[(_si)++]; \
        (_dst)[mblength] = '\0'; \
\
        goto add_string; \
      } \
      } \
    while (0)

#else
#  define SADD_MBCHAR(_dst, _src, _si, _srcsize)
#endif

#endif /* _SH_MBUTIL_H_ */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/glob/xmbsrtowcs.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/glob/xmbsrtowcs.c used in: Step 1198
/* xmbsrtowcs.c -- replacement function for mbsrtowcs */

/* Copyright (C) 2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
#include <config.h>

#include <bashansi.h>

/* <wchar.h>, <wctype.h> and <stdlib.h> are included in "shmbutil.h".
   If <wchar.h>, <wctype.h>, mbsrtowcs(), exist, HANDLE_MULTIBYTE
   is defined as 1. */
#include <shmbutil.h>

#if HANDLE_MULTIBYTE
/* On some locales (ex. ja_JP.sjis), mbsrtowc doesn't convert 0x5c to U<0x5c>.
   So, this function is made for converting 0x5c to U<0x5c>. */

static mbstate_t local_state;
static int local_state_use = 0;

size_t
xmbsrtowcs (dest, src, len, pstate)
    wchar_t *dest;
    const char **src;
    size_t len;
    mbstate_t *pstate;
{
  mbstate_t *ps;
  size_t mblength, wclength, n;

  ps = pstate;
  if (pstate == NULL)
    {
      if (!local_state_use)
    {
      memset (&local_state, '\0', sizeof(mbstate_t));
      local_state_use = 1;
    }
      ps = &local_state;
    }

  n = strlen(*src) + 1;

  if (dest == NULL)
    {
      wchar_t *wsbuf;
      char *mbsbuf, *mbsbuf_top;
      mbstate_t psbuf;

      wsbuf = (wchar_t *) malloc ((n + 1) * sizeof(wchar_t));
      mbsbuf_top = mbsbuf = (char *) malloc (n + 1);
      memcpy(mbsbuf, *src, n + 1);
      psbuf = *ps;

      wclength = mbsrtowcs (wsbuf, (const char **)&mbsbuf, n, &psbuf);

      free (wsbuf);
      free (mbsbuf_top);
      return wclength;
    }
      
  for(wclength = 0; wclength < len; wclength++, dest++)
    {
      if(mbsinit(ps))
    {
      if (**src == '\0')
        {
          *dest = L'\0';
          *src = NULL;
          return (wclength);
        }
      else if (**src == '\\')
        {
          *dest = L'\\';
          mblength = 1;
        }
      else
        mblength = mbrtowc(dest, *src, n, ps);
    }
      else
    mblength = mbrtowc(dest, *src, n, ps);

      /* Cannot convert multibyte character to wide character. */
      if (mblength == (size_t)-1 || mblength == (size_t)-2)
    return (size_t)-1;

      *src += mblength;
      n -= mblength;

      /* The multibyte string  has  been  completely  converted,
     including  the terminating '\0'. */
      if (*dest == L'\0')
    {
      *src = NULL;
      break;
    }
    }

    return (wclength);
}
#endif /* HANDLE_MULTIBYTE */

https://git.savannah.gnu.org/cgit/bash.git/tree/lib/tilde/tilde.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/lib/tilde/tilde.c used in: Step 1200
/* tilde.c -- Tilde expansion code (~/foo := $HOME/foo). */

/* Copyright (C) 1988,1989 Free Software Foundation, Inc.

   This file is part of GNU Readline, a library for reading lines
   of text with interactive input and history editing.

   Readline 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.

   Readline 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 Readline; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#if defined (HAVE_CONFIG_H)
#  include <config.h>
#endif

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#if defined (HAVE_STRING_H)
#  include <string.h>
#else /* !HAVE_STRING_H */
#  include <strings.h>
#endif /* !HAVE_STRING_H */  

#if defined (HAVE_STDLIB_H)
#  include <stdlib.h>
#else
#  include "ansi_stdlib.h"
#endif /* HAVE_STDLIB_H */

#include <sys/types.h>
#include <pwd.h>

#include "tilde.h"

#if defined (TEST) || defined (STATIC_MALLOC)
static void *xmalloc (), *xrealloc ();
#else
#  include "xmalloc.h"
#endif /* TEST || STATIC_MALLOC */

#if !defined (HAVE_GETPW_DECLS)
extern struct passwd *getpwuid PARAMS((uid_t));
extern struct passwd *getpwnam PARAMS((const char *));
#endif /* !HAVE_GETPW_DECLS */

#if !defined (savestring)
#define savestring(x) strcpy ((char *)xmalloc (1 + strlen (x)), (x))
#endif /* !savestring */

#if !defined (NULL)
#  if defined (__STDC__)
#    define NULL ((void *) 0)
#  else
#    define NULL 0x0
#  endif /* !__STDC__ */
#endif /* !NULL */

/* If being compiled as part of bash, these will be satisfied from
   variables.o.  If being compiled as part of readline, they will
   be satisfied from shell.o. */
extern char *sh_get_home_dir PARAMS((void));
extern char *sh_get_env_value PARAMS((const char *));

/* The default value of tilde_additional_prefixes.  This is set to
   whitespace preceding a tilde so that simple programs which do not
   perform any word separation get desired behaviour. */
static const char *default_prefixes[] =
  { " ~", "\t~", (const char *)NULL };

/* The default value of tilde_additional_suffixes.  This is set to
   whitespace or newline so that simple programs which do not
   perform any word separation get desired behaviour. */
static const char *default_suffixes[] =
  { " ", "\n", (const char *)NULL };

/* If non-null, this contains the address of a function that the application
   wants called before trying the standard tilde expansions.  The function
   is called with the text sans tilde, and returns a malloc()'ed string
   which is the expansion, or a NULL pointer if the expansion fails. */
tilde_hook_func_t *tilde_expansion_preexpansion_hook = (tilde_hook_func_t *)NULL;

/* If non-null, this contains the address of a function to call if the
   standard meaning for expanding a tilde fails.  The function is called
   with the text (sans tilde, as in "foo"), and returns a malloc()'ed string
   which is the expansion, or a NULL pointer if there is no expansion. */
tilde_hook_func_t *tilde_expansion_failure_hook = (tilde_hook_func_t *)NULL;

/* When non-null, this is a NULL terminated array of strings which
   are duplicates for a tilde prefix.  Bash uses this to expand
   `=~' and `:~'. */
char **tilde_additional_prefixes = (char **)default_prefixes;

/* When non-null, this is a NULL terminated array of strings which match
   the end of a username, instead of just "/".  Bash sets this to
   `:' and `=~'. */
char **tilde_additional_suffixes = (char **)default_suffixes;

static int tilde_find_prefix PARAMS((const char *, int *));
static int tilde_find_suffix PARAMS((const char *));
static char *isolate_tilde_prefix PARAMS((const char *, int *));
static char *glue_prefix_and_suffix PARAMS((char *, const char *, int));

/* Find the start of a tilde expansion in STRING, and return the index of
   the tilde which starts the expansion.  Place the length of the text
   which identified this tilde starter in LEN, excluding the tilde itself. */
static int
tilde_find_prefix (string, len)
     const char *string;
     int *len;
{
  register int i, j, string_len;
  register char **prefixes;

  prefixes = tilde_additional_prefixes;

  string_len = strlen (string);
  *len = 0;

  if (*string == '\0' || *string == '~')
    return (0);

  if (prefixes)
    {
      for (i = 0; i < string_len; i++)
    {
      for (j = 0; prefixes[j]; j++)
        {
          if (strncmp (string + i, prefixes[j], strlen (prefixes[j])) == 0)
        {
          *len = strlen (prefixes[j]) - 1;
          return (i + *len);
        }
        }
    }
    }
  return (string_len);
}

/* Find the end of a tilde expansion in STRING, and return the index of
   the character which ends the tilde definition.  */
static int
tilde_find_suffix (string)
     const char *string;
{
  register int i, j, string_len;
  register char **suffixes;

  suffixes = tilde_additional_suffixes;
  string_len = strlen (string);

  for (i = 0; i < string_len; i++)
    {
#if defined (__MSDOS__)
      if (string[i] == '/' || string[i] == '\\' /* || !string[i] */)
#else
      if (string[i] == '/' /* || !string[i] */)
#endif
    break;

      for (j = 0; suffixes && suffixes[j]; j++)
    {
      if (strncmp (string + i, suffixes[j], strlen (suffixes[j])) == 0)
        return (i);
    }
    }
  return (i);
}

/* Return a new string which is the result of tilde expanding STRING. */
char *
tilde_expand (string)
     const char *string;
{
  char *result;
  int result_size, result_index;

  result_index = result_size = 0;
  if (result = strchr (string, '~'))
    result = (char *)xmalloc (result_size = (strlen (string) + 16));
  else
    result = (char *)xmalloc (result_size = (strlen (string) + 1));

  /* Scan through STRING expanding tildes as we come to them. */
  while (1)
    {
      register int start, end;
      char *tilde_word, *expansion;
      int len;

      /* Make START point to the tilde which starts the expansion. */
      start = tilde_find_prefix (string, &len);

      /* Copy the skipped text into the result. */
      if ((result_index + start + 1) > result_size)
    result = (char *)xrealloc (result, 1 + (result_size += (start + 20)));

      strncpy (result + result_index, string, start);
      result_index += start;

      /* Advance STRING to the starting tilde. */
      string += start;

      /* Make END be the index of one after the last character of the
     username. */
      end = tilde_find_suffix (string);

      /* If both START and END are zero, we are all done. */
      if (!start && !end)
    break;

      /* Expand the entire tilde word, and copy it into RESULT. */
      tilde_word = (char *)xmalloc (1 + end);
      strncpy (tilde_word, string, end);
      tilde_word[end] = '\0';
      string += end;

      expansion = tilde_expand_word (tilde_word);
      free (tilde_word);

      len = strlen (expansion);
#ifdef __CYGWIN__
      /* Fix for Cygwin to prevent ~user/xxx from expanding to //xxx when
     $HOME for `user' is /.  On cygwin, // denotes a network drive. */
      if (len > 1 || *expansion != '/' || *string != '/')
#endif
    {
      if ((result_index + len + 1) > result_size)
        result = (char *)xrealloc (result, 1 + (result_size += (len + 20)));

      strcpy (result + result_index, expansion);
      result_index += len;
    }
      free (expansion);
    }

  result[result_index] = '\0';

  return (result);
}

/* Take FNAME and return the tilde prefix we want expanded.  If LENP is
   non-null, the index of the end of the prefix into FNAME is returned in
   the location it points to. */
static char *
isolate_tilde_prefix (fname, lenp)
     const char *fname;
     int *lenp;
{
  char *ret;
  int i;

  ret = (char *)xmalloc (strlen (fname));
#if defined (__MSDOS__)
  for (i = 1; fname[i] && fname[i] != '/' && fname[i] != '\\'; i++)
#else
  for (i = 1; fname[i] && fname[i] != '/'; i++)
#endif
    ret[i - 1] = fname[i];
  ret[i - 1] = '\0';
  if (lenp)
    *lenp = i;
  return ret;
}

/* Return a string that is PREFIX concatenated with SUFFIX starting at
   SUFFIND. */
static char *
glue_prefix_and_suffix (prefix, suffix, suffind)
     char *prefix;
     const char *suffix;
     int suffind;
{
  char *ret;
  int plen, slen;

  plen = (prefix && *prefix) ? strlen (prefix) : 0;
  slen = strlen (suffix + suffind);
  ret = (char *)xmalloc (plen + slen + 1);
  if (plen)
    strcpy (ret, prefix);
  strcpy (ret + plen, suffix + suffind);
  return ret;
}

/* Do the work of tilde expansion on FILENAME.  FILENAME starts with a
   tilde.  If there is no expansion, call tilde_expansion_failure_hook.
   This always returns a newly-allocated string, never static storage. */
char *
tilde_expand_word (filename)
     const char *filename;
{
  char *dirname, *expansion, *username;
  int user_len;
  struct passwd *user_entry;

  if (filename == 0)
    return ((char *)NULL);

  if (*filename != '~')
    return (savestring (filename));

  /* A leading `~/' or a bare `~' is *always* translated to the value of
     $HOME or the home directory of the current user, regardless of any
     preexpansion hook. */
  if (filename[1] == '\0' || filename[1] == '/')
    {
      /* Prefix $HOME to the rest of the string. */
      expansion = sh_get_env_value ("HOME");

      /* If there is no HOME variable, look up the directory in
     the password database. */
      if (expansion == 0)
    expansion = sh_get_home_dir ();

      return (glue_prefix_and_suffix (expansion, filename, 1));
    }

  username = isolate_tilde_prefix (filename, &user_len);

  if (tilde_expansion_preexpansion_hook)
    {
      expansion = (*tilde_expansion_preexpansion_hook) (username);
      if (expansion)
    {
      dirname = glue_prefix_and_suffix (expansion, filename, user_len);
      free (username);
      free (expansion);
      return (dirname);
    }
    }

  /* No preexpansion hook, or the preexpansion hook failed.  Look in the
     password database. */
  dirname = (char *)NULL;
  user_entry = getpwnam (username);
  if (user_entry == 0)
    {
      /* If the calling program has a special syntax for expanding tildes,
     and we couldn't find a standard expansion, then let them try. */
      if (tilde_expansion_failure_hook)
    {
      expansion = (*tilde_expansion_failure_hook) (username);
      if (expansion)
        {
          dirname = glue_prefix_and_suffix (expansion, filename, user_len);
          free (expansion);
        }
    }
      free (username);
      /* If we don't have a failure hook, or if the failure hook did not
     expand the tilde, return a copy of what we were passed. */
      if (dirname == 0)
    dirname = savestring (filename);
    }
  else
    {
      free (username);
      dirname = glue_prefix_and_suffix (user_entry->pw_dir, filename, user_len);
    }

  endpwent ();
  return (dirname);
}



#if defined (TEST)
#undef NULL
#include <stdio.h>

main (argc, argv)
     int argc;
     char **argv;
{
  char *result, line[512];
  int done = 0;

  while (!done)
    {
      printf ("~expand: ");
      fflush (stdout);

      if (!gets (line))
    strcpy (line, "done");

      if ((strcmp (line, "done") == 0) ||
      (strcmp (line, "quit") == 0) ||
      (strcmp (line, "exit") == 0))
    {
      done = 1;
      break;
    }

      result = tilde_expand (line);
      printf ("  --> %s\n", result);
      free (result);
    }
  exit (0);
}

static void memory_error_and_abort ();

static void *
xmalloc (bytes)
     size_t bytes;
{
  void *temp = (char *)malloc (bytes);

  if (!temp)
    memory_error_and_abort ();
  return (temp);
}

static void *
xrealloc (pointer, bytes)
     void *pointer;
     int bytes;
{
  void *temp;

  if (!pointer)
    temp = malloc (bytes);
  else
    temp = realloc (pointer, bytes);

  if (!temp)
    memory_error_and_abort ();

  return (temp);
}

static void
memory_error_and_abort ()
{
  fprintf (stderr, "readline: out of virtual memory\n");
  abort ();
}

/*
 * Local variables:
 * compile-command: "gcc -g -DTEST -o tilde tilde.c"
 * end:
 */
#endif /* TEST */

https://git.savannah.gnu.org/cgit/bash.git/tree/eval.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/eval.c used in: Step 1203
/* eval.c -- reading and evaluating commands. */

/* Copyright (C) 1996 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "bashansi.h"
#include <stdio.h>

#include "shell.h"
#include "flags.h"
#include "trap.h"

#include "builtins/common.h"

#include "input.h"
#include "execute_cmd.h"

#if defined (HISTORY)
#  include "bashhist.h"
#endif

extern int EOF_reached;
extern int indirection_level;
extern int posixly_correct;
extern int subshell_environment, running_under_emacs;
extern int last_command_exit_value, stdin_redir;
extern int need_here_doc;
extern int current_command_number, current_command_line_count, line_number;
extern int expand_aliases;

static void send_pwd_to_eterm __P((void));
static sighandler alrm_catcher __P((int));

/* Read and execute commands until EOF is reached.  This assumes that
   the input source has already been initialized. */
int
reader_loop ()
{
  int our_indirection_level;
  COMMAND *current_command = (COMMAND *)NULL;

  USE_VAR(current_command);

  our_indirection_level = ++indirection_level;

  while (EOF_Reached == 0)
    {
      int code;

      code = setjmp (top_level);

#if defined (PROCESS_SUBSTITUTION)
      unlink_fifo_list ();
#endif /* PROCESS_SUBSTITUTION */

      if (interactive_shell && signal_is_ignored (SIGINT) == 0)
    set_signal_handler (SIGINT, sigint_sighandler);

      if (code != NOT_JUMPED)
    {
      indirection_level = our_indirection_level;

      switch (code)
        {
          /* Some kind of throw to top_level has occured. */
        case FORCE_EOF:
        case EXITPROG:
          current_command = (COMMAND *)NULL;
          if (exit_immediately_on_error)
        variable_context = 0;   /* not in a function */
          EOF_Reached = EOF;
          goto exec_done;

        case DISCARD:
          last_command_exit_value = 1;
          if (subshell_environment)
        {
          current_command = (COMMAND *)NULL;
          EOF_Reached = EOF;
          goto exec_done;
        }
          /* Obstack free command elements, etc. */
          if (current_command)
        {
          dispose_command (current_command);
          current_command = (COMMAND *)NULL;
        }
          break;

        default:
          command_error ("reader_loop", CMDERR_BADJUMP, code, 0);
        }
    }

      executing = 0;
      if (temporary_env)
    dispose_used_env_vars ();

#if (defined (ultrix) && defined (mips)) || defined (C_ALLOCA)
      /* Attempt to reclaim memory allocated with alloca (). */
      (void) alloca (0);
#endif

      if (read_command () == 0)
    {
      if (interactive_shell == 0 && read_but_dont_execute)
        {
          last_command_exit_value = EXECUTION_SUCCESS;
          dispose_command (global_command);
          global_command = (COMMAND *)NULL;
        }
      else if (current_command = global_command)
        {
          global_command = (COMMAND *)NULL;
          current_command_number++;

          executing = 1;
          stdin_redir = 0;
          execute_command (current_command);

        exec_done:
          if (current_command)
        {
          dispose_command (current_command);
          current_command = (COMMAND *)NULL;
        }

          QUIT;
        }
    }
      else
    {
      /* Parse error, maybe discard rest of stream if not interactive. */
      if (interactive == 0)
        EOF_Reached = EOF;
    }
      if (just_one_command)
    EOF_Reached = EOF;
    }
  indirection_level--;
  return (last_command_exit_value);
}

static sighandler
alrm_catcher(i)
     int i;
{
  printf ("\007timed out waiting for input: auto-logout\n");
  jump_to_top_level (EXITPROG);
  SIGRETURN (0);
}

/* Send an escape sequence to emacs term mode to tell it the
   current working directory. */
static void
send_pwd_to_eterm ()
{
  char *pwd;

  pwd = get_string_value ("PWD");
  if (pwd == 0)
    pwd = get_working_directory ("eterm");
  fprintf (stderr, "\032/%s\n", pwd);
}

/* Call the YACC-generated parser and return the status of the parse.
   Input is read from the current input stream (bash_input).  yyparse
   leaves the parsed command in the global variable GLOBAL_COMMAND.
   This is where PROMPT_COMMAND is executed. */
int
parse_command ()
{
  int r;
  char *command_to_execute;

  need_here_doc = 0;
  run_pending_traps ();

  /* Allow the execution of a random command just before the printing
     of each primary prompt.  If the shell variable PROMPT_COMMAND
     is set then the value of it is the command to execute. */
  if (interactive && bash_input.type != st_string)
    {
      command_to_execute = get_string_value ("PROMPT_COMMAND");
      if (command_to_execute)
    execute_prompt_command (command_to_execute);

      if (running_under_emacs == 2)
    send_pwd_to_eterm ();   /* Yuck */
    }

  current_command_line_count = 0;
  r = yyparse ();

  if (need_here_doc)
    gather_here_documents ();

  return (r);
}

/* Read and parse a command, returning the status of the parse.  The command
   is left in the globval variable GLOBAL_COMMAND for use by reader_loop.
   This is where the shell timeout code is executed. */
int
read_command ()
{
  SHELL_VAR *tmout_var;
  int tmout_len, result;
  SigHandler *old_alrm;

  set_current_prompt_level (1);
  global_command = (COMMAND *)NULL;

  /* Only do timeouts if interactive. */
  tmout_var = (SHELL_VAR *)NULL;
  tmout_len = 0;
  old_alrm = (SigHandler *)NULL;

  if (interactive)
    {
      tmout_var = find_variable ("TMOUT");

      if (tmout_var && var_isset (tmout_var))
    {
      tmout_len = atoi (value_cell (tmout_var));
      if (tmout_len > 0)
        {
          old_alrm = set_signal_handler (SIGALRM, alrm_catcher);
          alarm (tmout_len);
        }
    }
    }

  QUIT;

  current_command_line_count = 0;
  result = parse_command ();

  if (interactive && tmout_var && (tmout_len > 0))
    {
      alarm(0);
      set_signal_handler (SIGALRM, old_alrm);
    }

  return (result);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/parse.y?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/parse.y used in: Step 1204
/* Yacc grammar for bash. */

/* Copyright (C) 1989-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file LICENSE.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

%{
#include "config.h"

#include "bashtypes.h"
#include "bashansi.h"

#include "filecntl.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#if defined (HAVE_LOCALE_H)
#  include <locale.h>
#endif

#include <stdio.h>
#include "chartypes.h"
#include <signal.h>

#include "memalloc.h"

#define NEED_STRFTIME_DECL  /* used in externs.h */

#include "shell.h"
#include "trap.h"
#include "flags.h"
#include "parser.h"
#include "mailcheck.h"
#include "test.h"
#include "builtins/common.h"
#include "builtins/builtext.h"

#include "shmbutil.h"

#if defined (READLINE)
#  include "bashline.h"
#  include <readline/readline.h>
#endif /* READLINE */

#if defined (HISTORY)
#  include "bashhist.h"
#  include <readline/history.h>
#endif /* HISTORY */

#if defined (JOB_CONTROL)
#  include "jobs.h"
#endif /* JOB_CONTROL */

#if defined (ALIAS)
#  include "alias.h"
#endif /* ALIAS */

#if defined (PROMPT_STRING_DECODE)
#  ifndef _MINIX
#    include <sys/param.h>
#  endif
#  include <time.h>
#  if defined (TM_IN_SYS_TIME)
#    include <sys/types.h>
#    include <sys/time.h>
#  endif /* TM_IN_SYS_TIME */
#  include "maxpath.h"
#endif /* PROMPT_STRING_DECODE */

#define RE_READ_TOKEN   -99
#define NO_EXPANSION    -100

#ifdef DEBUG
#  define YYDEBUG 1
#else
#  define YYDEBUG 0
#endif

#if defined (HANDLE_MULTIBYTE)
#  define last_shell_getc_is_singlebyte \
    ((shell_input_line_index > 1) \
        ? shell_input_line_property[shell_input_line_index - 1] \
        : 1)
#  define MBTEST(x) ((x) && last_shell_getc_is_singlebyte)
#else
#  define last_shell_getc_is_singlebyte 1
#  define MBTEST(x) ((x))
#endif

#if defined (EXTENDED_GLOB)
extern int extended_glob;
#endif

extern int eof_encountered;
extern int no_line_editing, running_under_emacs;
extern int current_command_number;
extern int sourcelevel;
extern int posixly_correct;
extern int last_command_exit_value;
extern int interrupt_immediately;
extern char *shell_name, *current_host_name;
extern char *dist_version;
extern int patch_level;
extern int dump_translatable_strings, dump_po_strings;
extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
#if defined (BUFFERED_INPUT)
extern int bash_input_fd_changed;
#endif

extern int errno;
/* **************************************************************** */
/*                                  */
/*          "Forward" declarations              */
/*                                  */
/* **************************************************************** */

#ifdef DEBUG
static void debug_parser __P((int));
#endif

static int yy_getc __P((void));
static int yy_ungetc __P((int));

#if defined (READLINE)
static int yy_readline_get __P((void));
static int yy_readline_unget __P((int));
#endif

static int yy_string_get __P((void));
static int yy_string_unget __P((int));
static int yy_stream_get __P((void));
static int yy_stream_unget __P((int));

static int shell_getc __P((int));
static void shell_ungetc __P((int));
static void discard_until __P((int));

#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
static void push_string __P((char *, int, alias_t *));
static void pop_string __P((void));
static void free_string_list __P((void));
#endif

static char *read_a_line __P((int));

static int reserved_word_acceptable __P((int));
static int yylex __P((void));
static int alias_expand_token __P((char *));
static int time_command_acceptable __P((void));
static int special_case_tokens __P((char *));
static int read_token __P((int));
static char *parse_matched_pair __P((int, int, int, int *, int));
#if defined (ARRAY_VARS)
static char *parse_compound_assignment __P((int *));
#endif
#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND)
static int parse_dparen __P((int));
static int parse_arith_cmd __P((char **));
#endif
#if defined (COND_COMMAND)
static void cond_error __P((void));
static COND_COM *cond_expr __P((void));
static COND_COM *cond_or __P((void));
static COND_COM *cond_and __P((void));
static COND_COM *cond_term __P((void));
static int cond_skip_newlines __P((void));
static COMMAND *parse_cond_command __P((void));
#endif
#if defined (ARRAY_VARS)
static int token_is_assignment __P((char *, int));
static int token_is_ident __P((char *, int));
#endif
static int read_token_word __P((int));
static void discard_parser_constructs __P((int));

static char *error_token_from_token __P((int));
static char *error_token_from_text __P((void));
static void print_offending_line __P((void));
static void report_syntax_error __P((char *));

static void handle_eof_input_unit __P((void));
static void prompt_again __P((void));
#if 0
static void reset_readline_prompt __P((void));
#endif
static void print_prompt __P((void));

#if defined (HISTORY)
char *history_delimiting_chars __P((void));
#endif

#if defined (HANDLE_MULTIBYTE)
static void set_line_mbstate __P((void));
static char *shell_input_line_property = NULL;
#else
#  define set_line_mbstate()
#endif

extern int yyerror __P((const char *));

#ifdef DEBUG
extern int yydebug;
#endif

/* Default prompt strings */
char *primary_prompt = PPROMPT;
char *secondary_prompt = SPROMPT;

/* PROMPT_STRING_POINTER points to one of these, never to an actual string. */
char *ps1_prompt, *ps2_prompt;

/* Handle on the current prompt string.  Indirectly points through
   ps1_ or ps2_prompt. */
char **prompt_string_pointer = (char **)NULL;
char *current_prompt_string;

/* Non-zero means we expand aliases in commands. */
int expand_aliases = 0;

/* If non-zero, the decoded prompt string undergoes parameter and
   variable substitution, command substitution, arithmetic substitution,
   string expansion, process substitution, and quote removal in
   decode_prompt_string. */
int promptvars = 1;

/* The decoded prompt string.  Used if READLINE is not defined or if
   editing is turned off.  Analogous to current_readline_prompt. */
static char *current_decoded_prompt;

/* The number of lines read from input while creating the current command. */
int current_command_line_count;

/* Variables to manage the task of reading here documents, because we need to
   defer the reading until after a complete command has been collected. */
static REDIRECT *redir_stack[10];
int need_here_doc;

/* Where shell input comes from.  History expansion is performed on each
   line when the shell is interactive. */
static char *shell_input_line = (char *)NULL;
static int shell_input_line_index;
static int shell_input_line_size;   /* Amount allocated for shell_input_line. */
static int shell_input_line_len;    /* strlen (shell_input_line) */

/* Either zero or EOF. */
static int shell_input_line_terminator;

/* The line number in a script on which a function definition starts. */
static int function_dstart;

/* The line number in a script on which a function body starts. */
static int function_bstart;

/* The line number in a script at which an arithmetic for command starts. */
static int arith_for_lineno;

static REDIRECTEE redir;
%}

%union {
  WORD_DESC *word;      /* the word that we read. */
  int number;           /* the number that we read. */
  WORD_LIST *word_list;
  COMMAND *command;
  REDIRECT *redirect;
  ELEMENT element;
  PATTERN_LIST *pattern;
}

/* Reserved words.  Members of the first group are only recognized
   in the case that they are preceded by a list_terminator.  Members
   of the second group are for [[...]] commands.  Members of the
   third group are recognized only under special circumstances. */
%token IF THEN ELSE ELIF FI CASE ESAC FOR SELECT WHILE UNTIL DO DONE FUNCTION
%token COND_START COND_END COND_ERROR
%token IN BANG TIME TIMEOPT

/* More general tokens. yylex () knows how to make these. */
%token <word> WORD ASSIGNMENT_WORD
%token <number> NUMBER
%token <word_list> ARITH_CMD ARITH_FOR_EXPRS
%token <command> COND_CMD
%token AND_AND OR_OR GREATER_GREATER LESS_LESS LESS_AND LESS_LESS_LESS
%token GREATER_AND SEMI_SEMI LESS_LESS_MINUS AND_GREATER LESS_GREATER
%token GREATER_BAR

/* The types that the various syntactical units return. */

%type <command> inputunit command pipeline pipeline_command
%type <command> list list0 list1 compound_list simple_list simple_list1
%type <command> simple_command shell_command
%type <command> for_command select_command case_command group_command
%type <command> arith_command
%type <command> cond_command
%type <command> arith_for_command
%type <command> function_def function_body if_command elif_clause subshell
%type <redirect> redirection redirection_list
%type <element> simple_command_element
%type <word_list> word_list pattern
%type <pattern> pattern_list case_clause_sequence case_clause
%type <number> timespec

%start inputunit

%left '&' ';' '\n' yacc_EOF
%left AND_AND OR_OR
%right '|'
%%

inputunit:  simple_list simple_list_terminator
            {
              /* Case of regular command.  Discard the error
                 safety net,and return the command just parsed. */
              global_command = $1;
              eof_encountered = 0;
              /* discard_parser_constructs (0); */
              YYACCEPT;
            }
    |   '\n'
            {
              /* Case of regular command, but not a very
                 interesting one.  Return a NULL command. */
              global_command = (COMMAND *)NULL;
              YYACCEPT;
            }
    |   error '\n'
            {
              /* Error during parsing.  Return NULL command. */
              global_command = (COMMAND *)NULL;
              eof_encountered = 0;
              /* discard_parser_constructs (1); */
              if (interactive)
                {
                  YYACCEPT;
                }
              else
                {
                  YYABORT;
                }
            }
    |   yacc_EOF
            {
              /* Case of EOF seen by itself.  Do ignoreeof or
                 not. */
              global_command = (COMMAND *)NULL;
              handle_eof_input_unit ();
              YYACCEPT;
            }
    ;

word_list:  WORD
            { $$ = make_word_list ($1, (WORD_LIST *)NULL); }
    |   word_list WORD
            { $$ = make_word_list ($2, $1); }
    ;

redirection:    '>' WORD
            {
              redir.filename = $2;
              $$ = make_redirection (1, r_output_direction, redir);
            }
    |   '<' WORD
            {
              redir.filename = $2;
              $$ = make_redirection (0, r_input_direction, redir);
            }
    |   NUMBER '>' WORD
            {
              redir.filename = $3;
              $$ = make_redirection ($1, r_output_direction, redir);
            }
    |   NUMBER '<' WORD
            {
              redir.filename = $3;
              $$ = make_redirection ($1, r_input_direction, redir);
            }
    |   GREATER_GREATER WORD
            {
              redir.filename = $2;
              $$ = make_redirection (1, r_appending_to, redir);
            }
    |   NUMBER GREATER_GREATER WORD
            {
              redir.filename = $3;
              $$ = make_redirection ($1, r_appending_to, redir);
            }
    |   LESS_LESS WORD
            {
              redir.filename = $2;
              $$ = make_redirection (0, r_reading_until, redir);
              redir_stack[need_here_doc++] = $$;
            }
    |   NUMBER LESS_LESS WORD
            {
              redir.filename = $3;
              $$ = make_redirection ($1, r_reading_until, redir);
              redir_stack[need_here_doc++] = $$;
            }
    |   LESS_LESS_LESS WORD
            {
              redir.filename = $2;
              $$ = make_redirection (0, r_reading_string, redir);
            }
    |   NUMBER LESS_LESS_LESS WORD
            {
              redir.filename = $3;
              $$ = make_redirection ($1, r_reading_string, redir);
            }
    |   LESS_AND NUMBER
            {
              redir.dest = $2;
              $$ = make_redirection (0, r_duplicating_input, redir);
            }
    |   NUMBER LESS_AND NUMBER
            {
              redir.dest = $3;
              $$ = make_redirection ($1, r_duplicating_input, redir);
            }
    |   GREATER_AND NUMBER
            {
              redir.dest = $2;
              $$ = make_redirection (1, r_duplicating_output, redir);
            }
    |   NUMBER GREATER_AND NUMBER
            {
              redir.dest = $3;
              $$ = make_redirection ($1, r_duplicating_output, redir);
            }
    |   LESS_AND WORD
            {
              redir.filename = $2;
              $$ = make_redirection (0, r_duplicating_input_word, redir);
            }
    |   NUMBER LESS_AND WORD
            {
              redir.filename = $3;
              $$ = make_redirection ($1, r_duplicating_input_word, redir);
            }
    |   GREATER_AND WORD
            {
              redir.filename = $2;
              $$ = make_redirection (1, r_duplicating_output_word, redir);
            }
    |   NUMBER GREATER_AND WORD
            {
              redir.filename = $3;
              $$ = make_redirection ($1, r_duplicating_output_word, redir);
            }
    |   LESS_LESS_MINUS WORD
            {
              redir.filename = $2;
              $$ = make_redirection
                (0, r_deblank_reading_until, redir);
              redir_stack[need_here_doc++] = $$;
            }
    |   NUMBER LESS_LESS_MINUS WORD
            {
              redir.filename = $3;
              $$ = make_redirection
                ($1, r_deblank_reading_until, redir);
              redir_stack[need_here_doc++] = $$;
            }
    |   GREATER_AND '-'
            {
              redir.dest = 0;
              $$ = make_redirection (1, r_close_this, redir);
            }
    |   NUMBER GREATER_AND '-'
            {
              redir.dest = 0;
              $$ = make_redirection ($1, r_close_this, redir);
            }
    |   LESS_AND '-'
            {
              redir.dest = 0;
              $$ = make_redirection (0, r_close_this, redir);
            }
    |   NUMBER LESS_AND '-'
            {
              redir.dest = 0;
              $$ = make_redirection ($1, r_close_this, redir);
            }
    |   AND_GREATER WORD
            {
              redir.filename = $2;
              $$ = make_redirection (1, r_err_and_out, redir);
            }
    |   NUMBER LESS_GREATER WORD
            {
              redir.filename = $3;
              $$ = make_redirection ($1, r_input_output, redir);
            }
    |   LESS_GREATER WORD
            {
              redir.filename = $2;
              $$ = make_redirection (0, r_input_output, redir);
            }
    |   GREATER_BAR WORD
            {
              redir.filename = $2;
              $$ = make_redirection (1, r_output_force, redir);
            }
    |   NUMBER GREATER_BAR WORD
            {
              redir.filename = $3;
              $$ = make_redirection ($1, r_output_force, redir);
            }
    ;

simple_command_element: WORD
            { $$.word = $1; $$.redirect = 0; }
    |   ASSIGNMENT_WORD
            { $$.word = $1; $$.redirect = 0; }
    |   redirection
            { $$.redirect = $1; $$.word = 0; }
    ;

redirection_list: redirection
            {
              $$ = $1;
            }
    |   redirection_list redirection
            {
              register REDIRECT *t;

              for (t = $1; t->next; t = t->next)
                ;
              t->next = $2;
              $$ = $1;
            }
    ;

simple_command: simple_command_element
            { $$ = make_simple_command ($1, (COMMAND *)NULL); }
    |   simple_command simple_command_element
            { $$ = make_simple_command ($2, $1); }
    ;

command:    simple_command
            { $$ = clean_simple_command ($1); }
    |   shell_command
            { $$ = $1; }
    |   shell_command redirection_list
            {
              COMMAND *tc;

              tc = $1;
              if (tc->redirects)
                {
                  register REDIRECT *t;
                  for (t = tc->redirects; t->next; t = t->next)
                ;
                  t->next = $2;
                }
              else
                tc->redirects = $2;
              $$ = $1;
            }
    |   function_def
            { $$ = $1; }
    ;

shell_command:  for_command
            { $$ = $1; }
    |   case_command
            { $$ = $1; }
    |   WHILE compound_list DO compound_list DONE
            { $$ = make_while_command ($2, $4); }
    |   UNTIL compound_list DO compound_list DONE
            { $$ = make_until_command ($2, $4); }
    |   select_command
            { $$ = $1; }
    |   if_command
            { $$ = $1; }
    |   subshell
            { $$ = $1; }
    |   group_command
            { $$ = $1; }
    |   arith_command
            { $$ = $1; }
    |   cond_command
            { $$ = $1; }
    |   arith_for_command
            { $$ = $1; }
    ;

for_command:    FOR WORD newline_list DO compound_list DONE
            { $$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5); }
    |   FOR WORD newline_list '{' compound_list '}'
            { $$ = make_for_command ($2, add_string_to_list ("$@", (WORD_LIST *)NULL), $5); }
    |   FOR WORD ';' newline_list DO compound_list DONE
            { $$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6); }
    |   FOR WORD ';' newline_list '{' compound_list '}'
            { $$ = make_for_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6); }
    |   FOR WORD newline_list IN word_list list_terminator newline_list DO compound_list DONE
            { $$ = make_for_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9); }
    |   FOR WORD newline_list IN word_list list_terminator newline_list '{' compound_list '}'
            { $$ = make_for_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9); }
    |   FOR WORD newline_list IN list_terminator newline_list DO compound_list DONE
            { $$ = make_for_command ($2, (WORD_LIST *)NULL, $8); }
    |   FOR WORD newline_list IN list_terminator newline_list '{' compound_list '}'
            { $$ = make_for_command ($2, (WORD_LIST *)NULL, $8); }
    ;

arith_for_command:  FOR ARITH_FOR_EXPRS list_terminator newline_list DO compound_list DONE
                { $$ = make_arith_for_command ($2, $6, arith_for_lineno); }
    |       FOR ARITH_FOR_EXPRS list_terminator newline_list '{' compound_list '}'
                { $$ = make_arith_for_command ($2, $6, arith_for_lineno); }
    |       FOR ARITH_FOR_EXPRS DO compound_list DONE
                { $$ = make_arith_for_command ($2, $4, arith_for_lineno); }
    |       FOR ARITH_FOR_EXPRS '{' compound_list '}'
                { $$ = make_arith_for_command ($2, $4, arith_for_lineno); }
    ;

select_command: SELECT WORD newline_list DO list DONE
            {
              $$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $5);
            }
    |   SELECT WORD newline_list '{' list '}'
            {
              $$ = make_select_command ($2, add_string_to_list ("$@", (WORD_LIST *)NULL), $5);
            }
    |   SELECT WORD ';' newline_list DO list DONE
            {
              $$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6);
            }
    |   SELECT WORD ';' newline_list '{' list '}'
            {
              $$ = make_select_command ($2, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), $6);
            }
    |   SELECT WORD newline_list IN word_list list_terminator newline_list DO list DONE
            {
              $$ = make_select_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9);
            }
    |   SELECT WORD newline_list IN word_list list_terminator newline_list '{' list '}'
            {
              $$ = make_select_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9);
            }
    ;

case_command:   CASE WORD newline_list IN newline_list ESAC
            { $$ = make_case_command ($2, (PATTERN_LIST *)NULL); }
    |   CASE WORD newline_list IN case_clause_sequence newline_list ESAC
            { $$ = make_case_command ($2, $5); }
    |   CASE WORD newline_list IN case_clause ESAC
            { $$ = make_case_command ($2, $5); }
    ;

function_def:   WORD '(' ')' newline_list function_body
            { $$ = make_function_def ($1, $5, function_dstart, function_bstart); }

    |   FUNCTION WORD '(' ')' newline_list function_body
            { $$ = make_function_def ($2, $6, function_dstart, function_bstart); }

    |   FUNCTION WORD newline_list function_body
            { $$ = make_function_def ($2, $4, function_dstart, function_bstart); }
    ;


function_body:  shell_command
            { $$ = $1; }
    |   shell_command redirection_list
            {
              COMMAND *tc;

              tc = $1;
              /* According to Posix.2 3.9.5, redirections
                 specified after the body of a function should
                 be attached to the function and performed when
                 the function is executed, not as part of the
                 function definition command. */
              /* XXX - I don't think it matters, but we might
                 want to change this in the future to avoid
                 problems differentiating between a function
                 definition with a redirection and a function
                 definition containing a single command with a
                 redirection.  The two are semantically equivalent,
                 though -- the only difference is in how the
                 command printing code displays the redirections. */
              if (tc->redirects)
                {
                  register REDIRECT *t;
                  for (t = tc->redirects; t->next; t = t->next)
                ;
                  t->next = $2;
                }
              else
                tc->redirects = $2;
              $$ = $1;
            }
    ;

subshell:   '(' compound_list ')'
            {
              $$ = make_subshell_command ($2);
              $$->flags |= CMD_WANT_SUBSHELL;
            }
    ;

if_command: IF compound_list THEN compound_list FI
            { $$ = make_if_command ($2, $4, (COMMAND *)NULL); }
    |   IF compound_list THEN compound_list ELSE compound_list FI
            { $$ = make_if_command ($2, $4, $6); }
    |   IF compound_list THEN compound_list elif_clause FI
            { $$ = make_if_command ($2, $4, $5); }
    ;


group_command:  '{' compound_list '}'
            { $$ = make_group_command ($2); }
    ;

arith_command:  ARITH_CMD
            { $$ = make_arith_command ($1); }
    ;

cond_command:   COND_START COND_CMD COND_END
            { $$ = $2; }
    ; 

elif_clause:    ELIF compound_list THEN compound_list
            { $$ = make_if_command ($2, $4, (COMMAND *)NULL); }
    |   ELIF compound_list THEN compound_list ELSE compound_list
            { $$ = make_if_command ($2, $4, $6); }
    |   ELIF compound_list THEN compound_list elif_clause
            { $$ = make_if_command ($2, $4, $5); }
    ;

case_clause:    pattern_list
    |   case_clause_sequence pattern_list
            { $2->next = $1; $$ = $2; }
    ;

pattern_list:   newline_list pattern ')' compound_list
            { $$ = make_pattern_list ($2, $4); }
    |   newline_list pattern ')' newline_list
            { $$ = make_pattern_list ($2, (COMMAND *)NULL); }
    |   newline_list '(' pattern ')' compound_list
            { $$ = make_pattern_list ($3, $5); }
    |   newline_list '(' pattern ')' newline_list
            { $$ = make_pattern_list ($3, (COMMAND *)NULL); }
    ;

case_clause_sequence:  pattern_list SEMI_SEMI
    |   case_clause_sequence pattern_list SEMI_SEMI
            { $2->next = $1; $$ = $2; }
    ;

pattern:    WORD
            { $$ = make_word_list ($1, (WORD_LIST *)NULL); }
    |   pattern '|' WORD
            { $$ = make_word_list ($3, $1); }
    ;

/* A list allows leading or trailing newlines and
   newlines as operators (equivalent to semicolons).
   It must end with a newline or semicolon.
   Lists are used within commands such as if, for, while.  */

list:       newline_list list0
            {
              $$ = $2;
              if (need_here_doc)
                gather_here_documents ();
             }
    ;

compound_list:  list
    |   newline_list list1
            {
              $$ = $2;
            }
    ;

list0:      list1 '\n' newline_list
    |   list1 '&' newline_list
            {
              if ($1->type == cm_connection)
                $$ = connect_async_list ($1, (COMMAND *)NULL, '&');
              else
                $$ = command_connect ($1, (COMMAND *)NULL, '&');
            }
    |   list1 ';' newline_list

    ;

list1:      list1 AND_AND newline_list list1
            { $$ = command_connect ($1, $4, AND_AND); }
    |   list1 OR_OR newline_list list1
            { $$ = command_connect ($1, $4, OR_OR); }
    |   list1 '&' newline_list list1
            {
              if ($1->type == cm_connection)
                $$ = connect_async_list ($1, $4, '&');
              else
                $$ = command_connect ($1, $4, '&');
            }
    |   list1 ';' newline_list list1
            { $$ = command_connect ($1, $4, ';'); }
    |   list1 '\n' newline_list list1
            { $$ = command_connect ($1, $4, ';'); }
    |   pipeline_command
            { $$ = $1; }
    ;

simple_list_terminator: '\n'
    |   yacc_EOF
    ;

list_terminator:'\n'
    |   ';'
    |   yacc_EOF
    ;

newline_list:
    |   newline_list '\n'
    ;

/* A simple_list is a list that contains no significant newlines
   and no leading or trailing newlines.  Newlines are allowed
   only following operators, where they are not significant.

   This is what an inputunit consists of.  */

simple_list:    simple_list1
            {
              $$ = $1;
              if (need_here_doc)
                gather_here_documents ();
            }
    |   simple_list1 '&'
            {
              if ($1->type == cm_connection)
                $$ = connect_async_list ($1, (COMMAND *)NULL, '&');
              else
                $$ = command_connect ($1, (COMMAND *)NULL, '&');
              if (need_here_doc)
                gather_here_documents ();
            }
    |   simple_list1 ';'
            {
              $$ = $1;
              if (need_here_doc)
                gather_here_documents ();
            }
    ;

simple_list1:   simple_list1 AND_AND newline_list simple_list1
            { $$ = command_connect ($1, $4, AND_AND); }
    |   simple_list1 OR_OR newline_list simple_list1
            { $$ = command_connect ($1, $4, OR_OR); }
    |   simple_list1 '&' simple_list1
            {
              if ($1->type == cm_connection)
                $$ = connect_async_list ($1, $3, '&');
              else
                $$ = command_connect ($1, $3, '&');
            }
    |   simple_list1 ';' simple_list1
            { $$ = command_connect ($1, $3, ';'); }

    |   pipeline_command
            { $$ = $1; }
    ;

pipeline_command: pipeline
            { $$ = $1; }
    |   BANG pipeline
            {
              $2->flags |= CMD_INVERT_RETURN;
              $$ = $2;
            }
    |   timespec pipeline
            {
              $2->flags |= $1;
              $$ = $2;
            }
    |   timespec BANG pipeline
            {
              $3->flags |= $1|CMD_INVERT_RETURN;
              $$ = $3;
            }
    |   BANG timespec pipeline
            {
              $3->flags |= $2|CMD_INVERT_RETURN;
              $$ = $3;
            }
    ;

pipeline:
        pipeline '|' newline_list pipeline
            { $$ = command_connect ($1, $4, '|'); }
    |   command
            { $$ = $1; }
    ;

timespec:   TIME
            { $$ = CMD_TIME_PIPELINE; }
    |   TIME TIMEOPT
            { $$ = CMD_TIME_PIPELINE|CMD_TIME_POSIX; }
    ;
%%

/* Possible states for the parser that require it to do special things. */
#define PST_CASEPAT 0x001       /* in a case pattern list */
#define PST_ALEXPNEXT   0x002       /* expand next word for aliases */
#define PST_ALLOWOPNBRC 0x004       /* allow open brace for function def */
#define PST_NEEDCLOSBRC 0x008       /* need close brace */
#define PST_DBLPAREN    0x010       /* double-paren parsing */
#define PST_SUBSHELL    0x020       /* ( ... ) subshell */
#define PST_CMDSUBST    0x040       /* $( ... ) command substitution */
#define PST_CASESTMT    0x080       /* parsing a case statement */
#define PST_CONDCMD 0x100       /* parsing a [[...]] command */
#define PST_CONDEXPR    0x200       /* parsing the guts of [[...]] */
#define PST_ARITHFOR    0x400       /* parsing an arithmetic for command */

/* Initial size to allocate for tokens, and the
   amount to grow them by. */
#define TOKEN_DEFAULT_INITIAL_SIZE 496
#define TOKEN_DEFAULT_GROW_SIZE 512

/* The token currently being read. */
static int current_token;

/* The last read token, or NULL.  read_token () uses this for context
   checking. */
static int last_read_token;

/* The token read prior to last_read_token. */
static int token_before_that;

/* The token read prior to token_before_that. */
static int two_tokens_ago;

/* If non-zero, it is the token that we want read_token to return
   regardless of what text is (or isn't) present to be read.  This
   is reset by read_token.  If token_to_read == WORD or
   ASSIGNMENT_WORD, yylval.word should be set to word_desc_to_read. */
static int token_to_read;
static WORD_DESC *word_desc_to_read;

/* The current parser state. */
static int parser_state;

/* Global var is non-zero when end of file has been reached. */
int EOF_Reached = 0;

#ifdef DEBUG
static void
debug_parser (i)
     int i;
{
#if YYDEBUG != 0
  yydebug = i;
#endif
}
#endif

/* yy_getc () returns the next available character from input or EOF.
   yy_ungetc (c) makes `c' the next character to read.
   init_yy_io (get, unget, type, location) makes the function GET the
   installed function for getting the next character, makes UNGET the
   installed function for un-getting a character, sets the type of stream
   (either string or file) from TYPE, and makes LOCATION point to where
   the input is coming from. */

/* Unconditionally returns end-of-file. */
int
return_EOF ()
{
  return (EOF);
}

/* Variable containing the current get and unget functions.
   See ./input.h for a clearer description. */
BASH_INPUT bash_input;

/* Set all of the fields in BASH_INPUT to NULL.  Free bash_input.name if it
   is non-null, avoiding a memory leak. */
void
initialize_bash_input ()
{
  bash_input.type = st_none;
  FREE (bash_input.name);
  bash_input.name = (char *)NULL;
  bash_input.location.file = (FILE *)NULL;
  bash_input.location.string = (char *)NULL;
  bash_input.getter = (sh_cget_func_t *)NULL;
  bash_input.ungetter = (sh_cunget_func_t *)NULL;
}

/* Set the contents of the current bash input stream from
   GET, UNGET, TYPE, NAME, and LOCATION. */
void
init_yy_io (get, unget, type, name, location)
     sh_cget_func_t *get;
     sh_cunget_func_t *unget;
     enum stream_type type;
     const char *name;
     INPUT_STREAM location;
{
  bash_input.type = type;
  FREE (bash_input.name);
  bash_input.name = name ? savestring (name) : (char *)NULL;

  /* XXX */
#if defined (CRAY)
  memcpy((char *)&bash_input.location.string, (char *)&location.string, sizeof(location));
#else
  bash_input.location = location;
#endif
  bash_input.getter = get;
  bash_input.ungetter = unget;
}

char *
yy_input_name ()
{
  return (bash_input.name ? bash_input.name : "stdin");
}

/* Call this to get the next character of input. */
static int
yy_getc ()
{
  return (*(bash_input.getter)) ();
}

/* Call this to unget C.  That is, to make C the next character
   to be read. */
static int
yy_ungetc (c)
     int c;
{
  return (*(bash_input.ungetter)) (c);
}

#if defined (BUFFERED_INPUT)
#ifdef INCLUDE_UNUSED
int
input_file_descriptor ()
{
  switch (bash_input.type)
    {
    case st_stream:
      return (fileno (bash_input.location.file));
    case st_bstream:
      return (bash_input.location.buffered_fd);
    case st_stdin:
    default:
      return (fileno (stdin));
    }
}
#endif
#endif /* BUFFERED_INPUT */

/* **************************************************************** */
/*                                  */
/*        Let input be read from readline ().           */
/*                                  */
/* **************************************************************** */

#if defined (READLINE)
char *current_readline_prompt = (char *)NULL;
char *current_readline_line = (char *)NULL;
int current_readline_line_index = 0;

static int
yy_readline_get ()
{
  SigHandler *old_sigint;
  int line_len;
  unsigned char c;

  if (!current_readline_line)
    {
      if (!bash_readline_initialized)
    initialize_readline ();

#if defined (JOB_CONTROL)
      if (job_control)
    give_terminal_to (shell_pgrp, 0);
#endif /* JOB_CONTROL */

      old_sigint = (SigHandler *)NULL;
      if (signal_is_ignored (SIGINT) == 0)
    {
      old_sigint = (SigHandler *)set_signal_handler (SIGINT, sigint_sighandler);
      interrupt_immediately++;
    }

      current_readline_line = readline (current_readline_prompt ?
                          current_readline_prompt : "");

      if (signal_is_ignored (SIGINT) == 0 && old_sigint)
    {
      interrupt_immediately--;
      set_signal_handler (SIGINT, old_sigint);
    }

#if 0
      /* Reset the prompt to the decoded value of prompt_string_pointer. */
      reset_readline_prompt ();
#endif

      if (current_readline_line == 0)
    return (EOF);

      current_readline_line_index = 0;
      line_len = strlen (current_readline_line);

      current_readline_line = (char *)xrealloc (current_readline_line, 2 + line_len);
      current_readline_line[line_len++] = '\n';
      current_readline_line[line_len] = '\0';
    }

  if (current_readline_line[current_readline_line_index] == 0)
    {
      free (current_readline_line);
      current_readline_line = (char *)NULL;
      return (yy_readline_get ());
    }
  else
    {
      c = current_readline_line[current_readline_line_index++];
      return (c);
    }
}

static int
yy_readline_unget (c)
     int c;
{
  if (current_readline_line_index && current_readline_line)
    current_readline_line[--current_readline_line_index] = c;
  return (c);
}

void
with_input_from_stdin ()
{
  INPUT_STREAM location;

  if (bash_input.type != st_stdin && stream_on_stack (st_stdin) == 0)
    {
      location.string = current_readline_line;
      init_yy_io (yy_readline_get, yy_readline_unget,
          st_stdin, "readline stdin", location);
    }
}

#else  /* !READLINE */

void
with_input_from_stdin ()
{
  with_input_from_stream (stdin, "stdin");
}
#endif  /* !READLINE */

/* **************************************************************** */
/*                                  */
/*   Let input come from STRING.  STRING is zero terminated.        */
/*                                  */
/* **************************************************************** */

static int
yy_string_get ()
{
  register char *string;
  register unsigned char c;

  string = bash_input.location.string;

  /* If the string doesn't exist, or is empty, EOF found. */
  if (string && *string)
    {
      c = *string++;
      bash_input.location.string = string;
      return (c);
    }
  else
    return (EOF);
}

static int
yy_string_unget (c)
     int c;
{
  *(--bash_input.location.string) = c;
  return (c);
}

void
with_input_from_string (string, name)
     char *string;
     const char *name;
{
  INPUT_STREAM location;

  location.string = string;
  init_yy_io (yy_string_get, yy_string_unget, st_string, name, location);
}

/* **************************************************************** */
/*                                  */
/*           Let input come from STREAM.            */
/*                                  */
/* **************************************************************** */

/* These two functions used to test the value of the HAVE_RESTARTABLE_SYSCALLS
   define, and just use getc/ungetc if it was defined, but since bash
   installs its signal handlers without the SA_RESTART flag, some signals
   (like SIGCHLD, SIGWINCH, etc.) received during a read(2) will not cause
   the read to be restarted.  We need to restart it ourselves. */

static int
yy_stream_get ()
{
  int result;

  result = EOF;
  if (bash_input.location.file)
    result = getc_with_restart (bash_input.location.file);

  return (result);
}

static int
yy_stream_unget (c)
     int c;
{
  return (ungetc_with_restart (c, bash_input.location.file));
}

void
with_input_from_stream (stream, name)
     FILE *stream;
     const char *name;
{
  INPUT_STREAM location;

  location.file = stream;
  init_yy_io (yy_stream_get, yy_stream_unget, st_stream, name, location);
}

typedef struct stream_saver {
  struct stream_saver *next;
  BASH_INPUT bash_input;
  int line;
#if defined (BUFFERED_INPUT)
  BUFFERED_STREAM *bstream;
#endif /* BUFFERED_INPUT */
} STREAM_SAVER;

/* The globally known line number. */
int line_number = 0;

#if defined (COND_COMMAND)
static int cond_lineno;
static int cond_token;
#endif

STREAM_SAVER *stream_list = (STREAM_SAVER *)NULL;

void
push_stream (reset_lineno)
     int reset_lineno;
{
  STREAM_SAVER *saver = (STREAM_SAVER *)xmalloc (sizeof (STREAM_SAVER));

  xbcopy ((char *)&bash_input, (char *)&(saver->bash_input), sizeof (BASH_INPUT));

#if defined (BUFFERED_INPUT)
  saver->bstream = (BUFFERED_STREAM *)NULL;
  /* If we have a buffered stream, clear out buffers[fd]. */
  if (bash_input.type == st_bstream && bash_input.location.buffered_fd >= 0)
    saver->bstream = set_buffered_stream (bash_input.location.buffered_fd,
                          (BUFFERED_STREAM *)NULL);
#endif /* BUFFERED_INPUT */

  saver->line = line_number;
  bash_input.name = (char *)NULL;
  saver->next = stream_list;
  stream_list = saver;
  EOF_Reached = 0;
  if (reset_lineno)
    line_number = 0;
}

void
pop_stream ()
{
  if (!stream_list)
    EOF_Reached = 1;
  else
    {
      STREAM_SAVER *saver = stream_list;

      EOF_Reached = 0;
      stream_list = stream_list->next;

      init_yy_io (saver->bash_input.getter,
          saver->bash_input.ungetter,
          saver->bash_input.type,
          saver->bash_input.name,
          saver->bash_input.location);

#if defined (BUFFERED_INPUT)
      /* If we have a buffered stream, restore buffers[fd]. */
      /* If the input file descriptor was changed while this was on the
     save stack, update the buffered fd to the new file descriptor and
     re-establish the buffer <-> bash_input fd correspondence. */
      if (bash_input.type == st_bstream && bash_input.location.buffered_fd >= 0)
    {
      if (bash_input_fd_changed)
        {
          bash_input_fd_changed = 0;
          if (default_buffered_input >= 0)
        {
          bash_input.location.buffered_fd = default_buffered_input;
          saver->bstream->b_fd = default_buffered_input;
          SET_CLOSE_ON_EXEC (default_buffered_input);
        }
        }
      /* XXX could free buffered stream returned as result here. */
      set_buffered_stream (bash_input.location.buffered_fd, saver->bstream);
    }
#endif /* BUFFERED_INPUT */

      line_number = saver->line;

      FREE (saver->bash_input.name);
      free (saver);
    }
}

/* Return 1 if a stream of type TYPE is saved on the stack. */
int
stream_on_stack (type)
     enum stream_type type;
{
  register STREAM_SAVER *s;

  for (s = stream_list; s; s = s->next)
    if (s->bash_input.type == type)
      return 1;
  return 0;
}

/* Save the current token state and return it in a malloced array. */
int *
save_token_state ()
{
  int *ret;

  ret = (int *)xmalloc (3 * sizeof (int));
  ret[0] = last_read_token;
  ret[1] = token_before_that;
  ret[2] = two_tokens_ago;
  return ret;
}

void
restore_token_state (ts)
     int *ts;
{
  if (ts == 0)
    return;
  last_read_token = ts[0];
  token_before_that = ts[1];
  two_tokens_ago = ts[2];
}

/*
 * This is used to inhibit alias expansion and reserved word recognition
 * inside case statement pattern lists.  A `case statement pattern list' is:
 *
 *  everything between the `in' in a `case word in' and the next ')'
 *  or `esac'
 *  everything between a `;;' and the next `)' or `esac'
 */

#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)

#if !defined (ALIAS)
typedef void *alias_t;
#endif

#define END_OF_ALIAS 0

/*
 * Pseudo-global variables used in implementing token-wise alias expansion.
 */

/*
 * Pushing and popping strings.  This works together with shell_getc to
 * implement alias expansion on a per-token basis.
 */

typedef struct string_saver {
  struct string_saver *next;
  int expand_alias;  /* Value to set expand_alias to when string is popped. */
  char *saved_line;
#if defined (ALIAS)
  alias_t *expander;   /* alias that caused this line to be pushed. */
#endif
  int saved_line_size, saved_line_index, saved_line_terminator;
} STRING_SAVER;

STRING_SAVER *pushed_string_list = (STRING_SAVER *)NULL;

/*
 * Push the current shell_input_line onto a stack of such lines and make S
 * the current input.  Used when expanding aliases.  EXPAND is used to set
 * the value of expand_next_token when the string is popped, so that the
 * word after the alias in the original line is handled correctly when the
 * alias expands to multiple words.  TOKEN is the token that was expanded
 * into S; it is saved and used to prevent infinite recursive expansion.
 */
static void
push_string (s, expand, ap)
     char *s;
     int expand;
     alias_t *ap;
{
  STRING_SAVER *temp = (STRING_SAVER *)xmalloc (sizeof (STRING_SAVER));

  temp->expand_alias = expand;
  temp->saved_line = shell_input_line;
  temp->saved_line_size = shell_input_line_size;
  temp->saved_line_index = shell_input_line_index;
  temp->saved_line_terminator = shell_input_line_terminator;
#if defined (ALIAS)
  temp->expander = ap;
#endif
  temp->next = pushed_string_list;
  pushed_string_list = temp;

#if defined (ALIAS)
  if (ap)
    ap->flags |= AL_BEINGEXPANDED;
#endif

  shell_input_line = s;
  shell_input_line_size = strlen (s);
  shell_input_line_index = 0;
  shell_input_line_terminator = '\0';
  parser_state &= ~PST_ALEXPNEXT;

  set_line_mbstate ();
}

/*
 * Make the top of the pushed_string stack be the current shell input.
 * Only called when there is something on the stack.  Called from shell_getc
 * when it thinks it has consumed the string generated by an alias expansion
 * and needs to return to the original input line.
 */
static void
pop_string ()
{
  STRING_SAVER *t;

  FREE (shell_input_line);
  shell_input_line = pushed_string_list->saved_line;
  shell_input_line_index = pushed_string_list->saved_line_index;
  shell_input_line_size = pushed_string_list->saved_line_size;
  shell_input_line_terminator = pushed_string_list->saved_line_terminator;

  if (pushed_string_list->expand_alias)
    parser_state |= PST_ALEXPNEXT;
  else
    parser_state &= ~PST_ALEXPNEXT;

  t = pushed_string_list;
  pushed_string_list = pushed_string_list->next;

#if defined (ALIAS)
  if (t->expander)
    t->expander->flags &= ~AL_BEINGEXPANDED;
#endif

  free ((char *)t);

  set_line_mbstate ();
}

static void
free_string_list ()
{
  register STRING_SAVER *t, *t1;

  for (t = pushed_string_list; t; )
    {
      t1 = t->next;
      FREE (t->saved_line);
#if defined (ALIAS)
      if (t->expander)
    t->expander->flags &= ~AL_BEINGEXPANDED;
#endif
      free ((char *)t);
      t = t1;
    }
  pushed_string_list = (STRING_SAVER *)NULL;
}

#endif /* ALIAS || DPAREN_ARITHMETIC */

/* Return a line of text, taken from wherever yylex () reads input.
   If there is no more input, then we return NULL.  If REMOVE_QUOTED_NEWLINE
   is non-zero, we remove unquoted \<newline> pairs.  This is used by
   read_secondary_line to read here documents. */
static char *
read_a_line (remove_quoted_newline)
     int remove_quoted_newline;
{
  static char *line_buffer = (char *)NULL;
  static int buffer_size = 0;
  int indx = 0, c, peekc, pass_next;

#if defined (READLINE)
  if (interactive && bash_input.type != st_string && no_line_editing)
#else
  if (interactive && bash_input.type != st_string)
#endif
    print_prompt ();

  pass_next = 0;
  while (1)
    {
      c = yy_getc ();

      /* Allow immediate exit if interrupted during input. */
      QUIT;

      /* Ignore null bytes in input. */
      if (c == 0)
    {
#if 0
      internal_warning ("read_a_line: ignored null byte in input");
#endif
      continue;
    }

      /* If there is no more input, then we return NULL. */
      if (c == EOF)
    {
      if (interactive && bash_input.type == st_stream)
        clearerr (stdin);
      if (indx == 0)
        return ((char *)NULL);
      c = '\n';
    }

      /* `+2' in case the final character in the buffer is a newline. */
      RESIZE_MALLOCED_BUFFER (line_buffer, indx, 2, buffer_size, 128);

      /* IF REMOVE_QUOTED_NEWLINES is non-zero, we are reading a
     here document with an unquoted delimiter.  In this case,
     the line will be expanded as if it were in double quotes.
     We allow a backslash to escape the next character, but we
     need to treat the backslash specially only if a backslash
     quoting a backslash-newline pair appears in the line. */
      if (pass_next)
    {
      line_buffer[indx++] = c;
      pass_next = 0;
    }
      else if (c == '\\' && remove_quoted_newline)
    {
      peekc = yy_getc ();
      if (peekc == '\n')
        continue;   /* Make the unquoted \<newline> pair disappear. */
      else
        {
          yy_ungetc (peekc);
          pass_next = 1;
          line_buffer[indx++] = c;      /* Preserve the backslash. */
        }
    }
      else
    line_buffer[indx++] = c;

      if (c == '\n')
    {
      line_buffer[indx] = '\0';
      return (line_buffer);
    }
    }
}

/* Return a line as in read_a_line (), but insure that the prompt is
   the secondary prompt.  This is used to read the lines of a here
   document.  REMOVE_QUOTED_NEWLINE is non-zero if we should remove
   newlines quoted with backslashes while reading the line.  It is
   non-zero unless the delimiter of the here document was quoted. */
char *
read_secondary_line (remove_quoted_newline)
     int remove_quoted_newline;
{
  prompt_string_pointer = &ps2_prompt;
  prompt_again ();
  return (read_a_line (remove_quoted_newline));
}

/* **************************************************************** */
/*                                  */
/*              YYLEX ()                */
/*                                  */
/* **************************************************************** */

/* Reserved words.  These are only recognized as the first word of a
   command. */
STRING_INT_ALIST word_token_alist[] = {
  { "if", IF },
  { "then", THEN },
  { "else", ELSE },
  { "elif", ELIF },
  { "fi", FI },
  { "case", CASE },
  { "esac", ESAC },
  { "for", FOR },
#if defined (SELECT_COMMAND)
  { "select", SELECT },
#endif
  { "while", WHILE },
  { "until", UNTIL },
  { "do", DO },
  { "done", DONE },
  { "in", IN },
  { "function", FUNCTION },
#if defined (COMMAND_TIMING)
  { "time", TIME },
#endif
  { "{", '{' },
  { "}", '}' },
  { "!", BANG },
#if defined (COND_COMMAND)
  { "[[", COND_START },
  { "]]", COND_END },
#endif
  { (char *)NULL, 0}
};

/* other tokens that can be returned by read_token() */
STRING_INT_ALIST other_token_alist[] = {
  /* Multiple-character tokens with special values */
  { "-p", TIMEOPT },
  { "&&", AND_AND },
  { "||", OR_OR },
  { ">>", GREATER_GREATER },
  { "<<", LESS_LESS },
  { "<&", LESS_AND },
  { ">&", GREATER_AND },
  { ";;", SEMI_SEMI },
  { "<<-", LESS_LESS_MINUS },
  { "<<<", LESS_LESS_LESS },
  { "&>", AND_GREATER },
  { "<>", LESS_GREATER },
  { ">|", GREATER_BAR },
  { "EOF", yacc_EOF },
  /* Tokens whose value is the character itself */
  { ">", '>' },
  { "<", '<' },
  { "-", '-' },
  { "{", '{' },
  { "}", '}' },
  { ";", ';' },
  { "(", '(' },
  { ")", ')' },
  { "|", '|' },
  { "&", '&' },
  { "newline", '\n' },
  { (char *)NULL, 0}
};

/* others not listed here:
    WORD            look at yylval.word
    ASSIGNMENT_WORD     look at yylval.word
    NUMBER          look at yylval.number
    ARITH_CMD       look at yylval.word_list
    ARITH_FOR_EXPRS     look at yylval.word_list
    COND_CMD        look at yylval.command
*/

/* These are used by read_token_word, but appear up here so that shell_getc
   can use them to decide when to add otherwise blank lines to the history. */

/* The primary delimiter stack. */
struct dstack dstack = {  (char *)NULL, 0, 0 };

/* A temporary delimiter stack to be used when decoding prompt strings.
   This is needed because command substitutions in prompt strings (e.g., PS2)
   can screw up the parser's quoting state. */
static struct dstack temp_dstack = { (char *)NULL, 0, 0 };

/* Macro for accessing the top delimiter on the stack.  Returns the
   delimiter or zero if none. */
#define current_delimiter(ds) \
  (ds.delimiter_depth ? ds.delimiters[ds.delimiter_depth - 1] : 0)

#define push_delimiter(ds, character) \
  do \
    { \
      if (ds.delimiter_depth + 2 > ds.delimiter_space) \
    ds.delimiters = (char *)xrealloc \
      (ds.delimiters, (ds.delimiter_space += 10) * sizeof (char)); \
      ds.delimiters[ds.delimiter_depth] = character; \
      ds.delimiter_depth++; \
    } \
  while (0)

#define pop_delimiter(ds)   ds.delimiter_depth--

/* Return the next shell input character.  This always reads characters
   from shell_input_line; when that line is exhausted, it is time to
   read the next line.  This is called by read_token when the shell is
   processing normal command input. */

/* This implements one-character lookahead/lookbehind across physical input
   lines, to avoid something being lost because it's pushed back with
   shell_ungetc when we're at the start of a line. */
static int eol_ungetc_lookahead = 0;

static int
shell_getc (remove_quoted_newline)
     int remove_quoted_newline;
{
  register int i;
  int c;
  unsigned char uc;
  static int mustpop = 0;

  QUIT;

  if (eol_ungetc_lookahead)
    {
      c = eol_ungetc_lookahead;
      eol_ungetc_lookahead = 0;
      return (c);
    }

#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
  /* If shell_input_line[shell_input_line_index] == 0, but there is
     something on the pushed list of strings, then we don't want to go
     off and get another line.  We let the code down below handle it. */

  if (!shell_input_line || ((!shell_input_line[shell_input_line_index]) &&
                (pushed_string_list == (STRING_SAVER *)NULL)))
#else /* !ALIAS && !DPAREN_ARITHMETIC */
  if (!shell_input_line || !shell_input_line[shell_input_line_index])
#endif /* !ALIAS && !DPAREN_ARITHMETIC */
    {
      line_number++;

    restart_read:

      /* Allow immediate exit if interrupted during input. */
      QUIT;

      i = 0;
      shell_input_line_terminator = 0;

#if defined (JOB_CONTROL)
      /* This can cause a problem when reading a command as the result
     of a trap, when the trap is called from flush_child.  This call
     had better not cause jobs to disappear from the job table in
     that case, or we will have big trouble. */
      notify_and_cleanup ();
#else /* !JOB_CONTROL */
      cleanup_dead_jobs ();
#endif /* !JOB_CONTROL */

#if defined (READLINE)
      if (interactive && bash_input.type != st_string && no_line_editing)
#else
      if (interactive && bash_input.type != st_string)
#endif
    print_prompt ();

      if (bash_input.type == st_stream)
    clearerr (stdin);

      while (1)
    {
      c = yy_getc ();

      /* Allow immediate exit if interrupted during input. */
      QUIT;

      if (c == '\0')
        {
#if 0
          internal_warning ("shell_getc: ignored null byte in input");
#endif
          continue;
        }

      RESIZE_MALLOCED_BUFFER (shell_input_line, i, 2, shell_input_line_size, 256);

      if (c == EOF)
        {
          if (bash_input.type == st_stream)
        clearerr (stdin);

          if (i == 0)
        shell_input_line_terminator = EOF;

          shell_input_line[i] = '\0';
          break;
        }

      shell_input_line[i++] = c;

      if (c == '\n')
        {
          shell_input_line[--i] = '\0';
          current_command_line_count++;
          break;
        }
    }

      shell_input_line_index = 0;
      shell_input_line_len = i;     /* == strlen (shell_input_line) */

      set_line_mbstate ();

#if defined (HISTORY)
      if (remember_on_history && shell_input_line && shell_input_line[0])
    {
      char *expansions;
#  if defined (BANG_HISTORY)
      int old_hist;

      /* If the current delimiter is a single quote, we should not be
         performing history expansion, even if we're on a different
         line from the original single quote. */
      old_hist = history_expansion_inhibited;
      if (current_delimiter (dstack) == '\'')
        history_expansion_inhibited = 1;
#  endif
      expansions = pre_process_line (shell_input_line, 1, 1);
#  if defined (BANG_HISTORY)
      history_expansion_inhibited = old_hist;
#  endif
      if (expansions != shell_input_line)
        {
          free (shell_input_line);
          shell_input_line = expansions;
          shell_input_line_len = shell_input_line ?
                    strlen (shell_input_line) : 0;
          if (!shell_input_line_len)
        current_command_line_count--;

          /* We have to force the xrealloc below because we don't know
         the true allocated size of shell_input_line anymore. */
          shell_input_line_size = shell_input_line_len;

          set_line_mbstate ();
        }
    }
      /* Try to do something intelligent with blank lines encountered while
     entering multi-line commands.  XXX - this is grotesque */
      else if (remember_on_history && shell_input_line &&
           shell_input_line[0] == '\0' &&
           current_command_line_count > 1)
    {
      if (current_delimiter (dstack))
        /* We know shell_input_line[0] == 0 and we're reading some sort of
           quoted string.  This means we've got a line consisting of only
           a newline in a quoted string.  We want to make sure this line
           gets added to the history. */
        maybe_add_history (shell_input_line);
      else
        {
          char *hdcs;
          hdcs = history_delimiting_chars ();
          if (hdcs && hdcs[0] == ';')
        maybe_add_history (shell_input_line);
        }
    }

#endif /* HISTORY */

      if (shell_input_line)
    {
      /* Lines that signify the end of the shell's input should not be
         echoed. */
      if (echo_input_at_read && (shell_input_line[0] ||
                     shell_input_line_terminator != EOF))
        fprintf (stderr, "%s\n", shell_input_line);
    }
      else
    {
      shell_input_line_size = 0;
      prompt_string_pointer = &current_prompt_string;
      prompt_again ();
      goto restart_read;
    }

      /* Add the newline to the end of this string, iff the string does
     not already end in an EOF character.  */
      if (shell_input_line_terminator != EOF)
    {
      if (shell_input_line_len + 3 > shell_input_line_size)
        shell_input_line = (char *)xrealloc (shell_input_line,
                    1 + (shell_input_line_size += 2));

      shell_input_line[shell_input_line_len] = '\n';
      shell_input_line[shell_input_line_len + 1] = '\0';

      set_line_mbstate ();
    }
    }

  uc = shell_input_line[shell_input_line_index];

  if (uc)
    shell_input_line_index++;

  if MBTEST(uc == '\\' && remove_quoted_newline && shell_input_line[shell_input_line_index] == '\n')
    {
    prompt_again ();
    line_number++;
    goto restart_read;
    }

#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
  /* If UC is NULL, we have reached the end of the current input string.  If
     pushed_string_list is non-empty, it's time to pop to the previous string
     because we have fully consumed the result of the last alias expansion.
     Do it transparently; just return the next character of the string popped
     to. */
  if (!uc && (pushed_string_list != (STRING_SAVER *)NULL))
    {
      if (mustpop)
    {
      pop_string ();
      uc = shell_input_line[shell_input_line_index];
      if (uc)
        shell_input_line_index++;
      mustpop--;
    }
      else
    {
      mustpop++;
      uc = ' ';
    }
    }
#endif /* ALIAS || DPAREN_ARITHMETIC */

  if (!uc && shell_input_line_terminator == EOF)
    return ((shell_input_line_index != 0) ? '\n' : EOF);

  return (uc);
}

/* Put C back into the input for the shell.  This might need changes for
   HANDLE_MULTIBYTE around EOLs.  Since we (currently) never push back a
   character different than we read, shell_input_line_property doesn't need
   to change when manipulating shell_input_line.  The define for
   last_shell_getc_is_singlebyte should take care of it, though. */
static void
shell_ungetc (c)
     int c;
{
  if (shell_input_line && shell_input_line_index)
    shell_input_line[--shell_input_line_index] = c;
  else
    eol_ungetc_lookahead = c;
}

#ifdef INCLUDE_UNUSED
/* Back the input pointer up by one, effectively `ungetting' a character. */
static void
shell_ungetchar ()
{
  if (shell_input_line && shell_input_line_index)
    shell_input_line_index--;
}
#endif

/* Discard input until CHARACTER is seen, then push that character back
   onto the input stream. */
static void
discard_until (character)
     int character;
{
  int c;

  while ((c = shell_getc (0)) != EOF && c != character)
    ;

  if (c != EOF)
    shell_ungetc (c);
}

void
execute_prompt_command (command)
     char *command;
{
  sh_builtin_func_t *temp_last, *temp_this;
  char *last_lastarg;
  int temp_exit_value, temp_eof_encountered;

  temp_last = last_shell_builtin;
  temp_this = this_shell_builtin;
  temp_exit_value = last_command_exit_value;
  temp_eof_encountered = eof_encountered;
  last_lastarg = get_string_value ("_");
  if (last_lastarg)
    last_lastarg = savestring (last_lastarg);

  parse_and_execute (savestring (command), "PROMPT_COMMAND", SEVAL_NONINT|SEVAL_NOHIST);

  last_shell_builtin = temp_last;
  this_shell_builtin = temp_this;
  last_command_exit_value = temp_exit_value;
  eof_encountered = temp_eof_encountered;

  bind_variable ("_", last_lastarg);
  FREE (last_lastarg);

  if (token_to_read == '\n')    /* reset_parser was called */
    token_to_read = 0;
}

/* Place to remember the token.  We try to keep the buffer
   at a reasonable size, but it can grow. */
static char *token = (char *)NULL;

/* Current size of the token buffer. */
static int token_buffer_size;

/* Command to read_token () explaining what we want it to do. */
#define READ 0
#define RESET 1
#define prompt_is_ps1 \
      (!prompt_string_pointer || prompt_string_pointer == &ps1_prompt)

/* Function for yyparse to call.  yylex keeps track of
   the last two tokens read, and calls read_token.  */
static int
yylex ()
{
  if (interactive && (current_token == 0 || current_token == '\n'))
    {
      /* Before we print a prompt, we might have to check mailboxes.
     We do this only if it is time to do so. Notice that only here
     is the mail alarm reset; nothing takes place in check_mail ()
     except the checking of mail.  Please don't change this. */
      if (prompt_is_ps1 && time_to_check_mail ())
    {
      check_mail ();
      reset_mail_timer ();
    }

      /* Avoid printing a prompt if we're not going to read anything, e.g.
     after resetting the parser with read_token (RESET). */
      if (token_to_read == 0 && interactive)
    prompt_again ();
    }

  two_tokens_ago = token_before_that;
  token_before_that = last_read_token;
  last_read_token = current_token;
  current_token = read_token (READ);
  return (current_token);
}

/* When non-zero, we have read the required tokens
   which allow ESAC to be the next one read. */
static int esacs_needed_count;

void
gather_here_documents ()
{
  int r = 0;
  while (need_here_doc)
    {
      make_here_document (redir_stack[r++]);
      need_here_doc--;
    }
}

/* When non-zero, an open-brace used to create a group is awaiting a close
   brace partner. */
static int open_brace_count;

#define command_token_position(token) \
  (((token) == ASSIGNMENT_WORD) || \
   ((token) != SEMI_SEMI && reserved_word_acceptable(token)))

#define assignment_acceptable(token) command_token_position(token) && \
                    ((parser_state & PST_CASEPAT) == 0)

/* Check to see if TOKEN is a reserved word and return the token
   value if it is. */
#define CHECK_FOR_RESERVED_WORD(tok) \
  do { \
    if (!dollar_present && !quoted && \
    reserved_word_acceptable (last_read_token)) \
      { \
    int i; \
    for (i = 0; word_token_alist[i].word != (char *)NULL; i++) \
      if (STREQ (tok, word_token_alist[i].word)) \
        { \
          if ((parser_state & PST_CASEPAT) && (word_token_alist[i].token != ESAC)) \
        break; \
          if (word_token_alist[i].token == TIME) \
        break; \
          if (word_token_alist[i].token == ESAC) \
        parser_state &= ~(PST_CASEPAT|PST_CASESTMT); \
          else if (word_token_alist[i].token == CASE) \
        parser_state |= PST_CASESTMT; \
          else if (word_token_alist[i].token == COND_END) \
        parser_state &= ~(PST_CONDCMD|PST_CONDEXPR); \
          else if (word_token_alist[i].token == COND_START) \
        parser_state |= PST_CONDCMD; \
          else if (word_token_alist[i].token == '{') \
        open_brace_count++; \
          else if (word_token_alist[i].token == '}' && open_brace_count) \
        open_brace_count--; \
          return (word_token_alist[i].token); \
        } \
      } \
  } while (0)

#if defined (ALIAS)

    /* OK, we have a token.  Let's try to alias expand it, if (and only if)
       it's eligible.

       It is eligible for expansion if EXPAND_ALIASES is set, and
       the token is unquoted and the last token read was a command
       separator (or expand_next_token is set), and we are currently
       processing an alias (pushed_string_list is non-empty) and this
       token is not the same as the current or any previously
       processed alias.

       Special cases that disqualify:
     In a pattern list in a case statement (parser_state & PST_CASEPAT). */
static int
alias_expand_token (tokstr)
     char *tokstr;
{
  char *expanded;
  alias_t *ap;

  if (((parser_state & PST_ALEXPNEXT) || command_token_position (last_read_token)) &&
    (parser_state & PST_CASEPAT) == 0)
    {
      ap = find_alias (tokstr);

      /* Currently expanding this token. */
      if (ap && (ap->flags & AL_BEINGEXPANDED))
    return (NO_EXPANSION);

      expanded = ap ? savestring (ap->value) : (char *)NULL;
      if (expanded)
    {
      push_string (expanded, ap->flags & AL_EXPANDNEXT, ap);
      return (RE_READ_TOKEN);
    }
      else
    /* This is an eligible token that does not have an expansion. */
    return (NO_EXPANSION);
    }
  return (NO_EXPANSION);
}
#endif /* ALIAS */

static int
time_command_acceptable ()
{
#if defined (COMMAND_TIMING)
  switch (last_read_token)
    {
    case 0:
    case ';':
    case '\n':
    case AND_AND:
    case OR_OR:
    case '&':
    case DO:
    case THEN:
    case ELSE:
    case '{':       /* } */
    case '(':       /* ) */
      return 1;
    default:
      return 0;
    }
#else
  return 0;
#endif /* COMMAND_TIMING */
}

/* Handle special cases of token recognition:
    IN is recognized if the last token was WORD and the token
    before that was FOR or CASE or SELECT.

    DO is recognized if the last token was WORD and the token
    before that was FOR or SELECT.

    ESAC is recognized if the last token caused `esacs_needed_count'
    to be set

    `{' is recognized if the last token as WORD and the token
    before that was FUNCTION, or if we just parsed an arithmetic
    `for' command.

    `}' is recognized if there is an unclosed `{' present.

    `-p' is returned as TIMEOPT if the last read token was TIME.

    ']]' is returned as COND_END if the parser is currently parsing
    a conditional expression ((parser_state & PST_CONDEXPR) != 0)

    `time' is returned as TIME if and only if it is immediately
    preceded by one of `;', `\n', `||', `&&', or `&'.
*/

static int
special_case_tokens (tokstr)
     char *tokstr;
{
  if ((last_read_token == WORD) &&
#if defined (SELECT_COMMAND)
      ((token_before_that == FOR) || (token_before_that == CASE) || (token_before_that == SELECT)) &&
#else
      ((token_before_that == FOR) || (token_before_that == CASE)) &&
#endif
      (tokstr[0] == 'i' && tokstr[1] == 'n' && tokstr[2] == 0))
    {
      if (token_before_that == CASE)
    {
      parser_state |= PST_CASEPAT;
      esacs_needed_count++;
    }
      return (IN);
    }

  if (last_read_token == WORD &&
#if defined (SELECT_COMMAND)
      (token_before_that == FOR || token_before_that == SELECT) &&
#else
      (token_before_that == FOR) &&
#endif
      (tokstr[0] == 'd' && tokstr[1] == 'o' && tokstr[2] == '\0'))
    return (DO);

  /* Ditto for ESAC in the CASE case.
     Specifically, this handles "case word in esac", which is a legal
     construct, certainly because someone will pass an empty arg to the
     case construct, and we don't want it to barf.  Of course, we should
     insist that the case construct has at least one pattern in it, but
     the designers disagree. */
  if (esacs_needed_count)
    {
      esacs_needed_count--;
      if (STREQ (tokstr, "esac"))
    {
      parser_state &= ~PST_CASEPAT;
      return (ESAC);
    }
    }

  /* The start of a shell function definition. */
  if (parser_state & PST_ALLOWOPNBRC)
    {
      parser_state &= ~PST_ALLOWOPNBRC;
      if (tokstr[0] == '{' && tokstr[1] == '\0')        /* } */
    {
      open_brace_count++;
      function_bstart = line_number;
      return ('{');                 /* } */
    }
    }

  /* We allow a `do' after a for ((...)) without an intervening
     list_terminator */
  if (last_read_token == ARITH_FOR_EXPRS && tokstr[0] == 'd' && tokstr[1] == 'o' && !tokstr[2])
    return (DO);
  if (last_read_token == ARITH_FOR_EXPRS && tokstr[0] == '{' && tokstr[1] == '\0')  /* } */
    {
      open_brace_count++;
      return ('{');         /* } */
    }

  if (open_brace_count && reserved_word_acceptable (last_read_token) && tokstr[0] == '}' && !tokstr[1])
    {
      open_brace_count--;       /* { */
      return ('}');
    }

#if defined (COMMAND_TIMING)
  /* Handle -p after `time'. */
  if (last_read_token == TIME && tokstr[0] == '-' && tokstr[1] == 'p' && !tokstr[2])
    return (TIMEOPT);
#endif

#if defined (COMMAND_TIMING)
  if (STREQ (token, "time") && ((parser_state & PST_CASEPAT) == 0) && time_command_acceptable ())
    return (TIME);
#endif /* COMMAND_TIMING */

#if defined (COND_COMMAND) /* [[ */
  if ((parser_state & PST_CONDEXPR) && tokstr[0] == ']' && tokstr[1] == ']' && tokstr[2] == '\0')
    return (COND_END);
#endif

  return (-1);
}

/* Called from shell.c when Control-C is typed at top level.  Or
   by the error rule at top level. */
void
reset_parser ()
{
  dstack.delimiter_depth = 0;   /* No delimiters found so far. */
  open_brace_count = 0;

  parser_state = 0;

#if defined (ALIAS) || defined (DPAREN_ARITHMETIC)
  if (pushed_string_list)
    free_string_list ();
#endif /* ALIAS || DPAREN_ARITHMETIC */

  if (shell_input_line)
    {
      free (shell_input_line);
      shell_input_line = (char *)NULL;
      shell_input_line_size = shell_input_line_index = 0;
    }

  FREE (word_desc_to_read);
  word_desc_to_read = (WORD_DESC *)NULL;

  last_read_token = '\n';
  token_to_read = '\n';
}

/* Read the next token.  Command can be READ (normal operation) or
   RESET (to normalize state). */
static int
read_token (command)
     int command;
{
  int character;        /* Current character. */
  int peek_char;        /* Temporary look-ahead character. */
  int result;           /* The thing to return. */

  if (command == RESET)
    {
      reset_parser ();
      return ('\n');
    }

  if (token_to_read)
    {
      result = token_to_read;
      if (token_to_read == WORD || token_to_read == ASSIGNMENT_WORD)
    {
      yylval.word = word_desc_to_read;
      word_desc_to_read = (WORD_DESC *)NULL;
    }
      token_to_read = 0;
      return (result);
    }

#if defined (COND_COMMAND)
  if ((parser_state & (PST_CONDCMD|PST_CONDEXPR)) == PST_CONDCMD)
    {
      cond_lineno = line_number;
      parser_state |= PST_CONDEXPR;
      yylval.command = parse_cond_command ();
      if (cond_token != COND_END)
    {
      cond_error ();
      return (-1);
    }
      token_to_read = COND_END;
      parser_state &= ~(PST_CONDEXPR|PST_CONDCMD);
      return (COND_CMD);
    }
#endif

#if defined (ALIAS)
  /* This is a place to jump back to once we have successfully expanded a
     token with an alias and pushed the string with push_string () */
 re_read_token:
#endif /* ALIAS */

  /* Read a single word from input.  Start by skipping blanks. */
  while ((character = shell_getc (1)) != EOF && whitespace (character))
    ;

  if (character == EOF)
    {
      EOF_Reached = 1;
      return (yacc_EOF);
    }

  if MBTEST(character == '#' && (!interactive || interactive_comments))
    {
      /* A comment.  Discard until EOL or EOF, and then return a newline. */
      discard_until ('\n');
      shell_getc (0);
      character = '\n'; /* this will take the next if statement and return. */
    }

  if (character == '\n')
    {
      /* If we're about to return an unquoted newline, we can go and collect
     the text of any pending here document. */
      if (need_here_doc)
    gather_here_documents ();

#if defined (ALIAS)
      parser_state &= ~PST_ALEXPNEXT;
#endif /* ALIAS */

      return (character);
    }

  /* Shell meta-characters. */
  if MBTEST(shellmeta (character) && ((parser_state & PST_DBLPAREN) == 0))
    {
#if defined (ALIAS)
      /* Turn off alias tokenization iff this character sequence would
     not leave us ready to read a command. */
      if (character == '<' || character == '>')
    parser_state &= ~PST_ALEXPNEXT;
#endif /* ALIAS */

      peek_char = shell_getc (1);
      if (character == peek_char)
    {
      switch (character)
        {
        case '<':
          /* If '<' then we could be at "<<" or at "<<-".  We have to
         look ahead one more character. */
          peek_char = shell_getc (1);
          if (peek_char == '-')
        return (LESS_LESS_MINUS);
          else if (peek_char == '<')
        return (LESS_LESS_LESS);
          else
        {
          shell_ungetc (peek_char);
          return (LESS_LESS);
        }

        case '>':
          return (GREATER_GREATER);

        case ';':
          parser_state |= PST_CASEPAT;
#if defined (ALIAS)
          parser_state &= ~PST_ALEXPNEXT;
#endif /* ALIAS */
          return (SEMI_SEMI);

        case '&':
          return (AND_AND);

        case '|':
          return (OR_OR);

#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND)
        case '(':       /* ) */
          result = parse_dparen (character);
          if (result == -2)
            break;
          else
            return result;
#endif
        }
    }
      else if MBTEST(character == '<' && peek_char == '&')
    return (LESS_AND);
      else if MBTEST(character == '>' && peek_char == '&')
    return (GREATER_AND);
      else if MBTEST(character == '<' && peek_char == '>')
    return (LESS_GREATER);
      else if MBTEST(character == '>' && peek_char == '|')
    return (GREATER_BAR);
      else if MBTEST(peek_char == '>' && character == '&')
    return (AND_GREATER);

      shell_ungetc (peek_char);

      /* If we look like we are reading the start of a function
     definition, then let the reader know about it so that
     we will do the right thing with `{'. */
      if MBTEST(character == ')' && last_read_token == '(' && token_before_that == WORD)
    {
      parser_state |= PST_ALLOWOPNBRC;
#if defined (ALIAS)
      parser_state &= ~PST_ALEXPNEXT;
#endif /* ALIAS */
      function_dstart = line_number;
    }

      /* case pattern lists may be preceded by an optional left paren.  If
     we're not trying to parse a case pattern list, the left paren
     indicates a subshell. */
      if MBTEST(character == '(' && (parser_state & PST_CASEPAT) == 0) /* ) */
    parser_state |= PST_SUBSHELL;
      /*(*/
      else if MBTEST((parser_state & PST_CASEPAT) && character == ')')
    parser_state &= ~PST_CASEPAT;
      /*(*/
      else if MBTEST((parser_state & PST_SUBSHELL) && character == ')')
    parser_state &= ~PST_SUBSHELL;

#if defined (PROCESS_SUBSTITUTION)
      /* Check for the constructs which introduce process substitution.
     Shells running in `posix mode' don't do process substitution. */
      if MBTEST(posixly_correct || ((character != '>' && character != '<') || peek_char != '(')) /*)*/
#endif /* PROCESS_SUBSTITUTION */
    return (character);
    }

  /* Hack <&- (close stdin) case.  Also <&N- (dup and close). */
  if MBTEST(character == '-' && (last_read_token == LESS_AND || last_read_token == GREATER_AND))
    return (character);

  /* Okay, if we got this far, we have to read a word.  Read one,
     and then check it against the known ones. */
  result = read_token_word (character);
#if defined (ALIAS)
  if (result == RE_READ_TOKEN)
    goto re_read_token;
#endif
  return result;
}

/*
 * Match a $(...) or other grouping construct.  This has to handle embedded
 * quoted strings ('', ``, "") and nested constructs.  It also must handle
 * reprompting the user, if necessary, after reading a newline (unless the
 * P_NONL flag is passed), and returning correct error values if it reads
 * EOF.
 */
#define P_FIRSTCLOSE    0x01
#define P_ALLOWESC  0x02

static char matched_pair_error;
static char *
parse_matched_pair (qc, open, close, lenp, flags)
     int qc;    /* `"' if this construct is within double quotes */
     int open, close;
     int *lenp, flags;
{
  int count, ch, was_dollar;
  int pass_next_character, nestlen, ttranslen, start_lineno;
  char *ret, *nestret, *ttrans;
  int retind, retsize;

  count = 1;
  pass_next_character = was_dollar = 0;

  ret = (char *)xmalloc (retsize = 64);
  retind = 0;

  start_lineno = line_number;
  while (count)
    {
      ch = shell_getc ((qc != '\'' || (flags & P_ALLOWESC)) && pass_next_character == 0);
      if (ch == EOF)
    {
      free (ret);
      parser_error (start_lineno, "unexpected EOF while looking for matching `%c'", close);
      EOF_Reached = 1;  /* XXX */
      return (&matched_pair_error);
    }

      /* Possible reprompting. */
      if (ch == '\n' && interactive &&
        (bash_input.type == st_stdin || bash_input.type == st_stream))
    prompt_again ();

      if (pass_next_character)      /* last char was backslash */
    {
      pass_next_character = 0;
      if (qc != '\'' && ch == '\n') /* double-quoted \<newline> disappears. */
        {
          if (retind > 0) retind--; /* swallow previously-added backslash */
          continue;
        }

      RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64);
      if MBTEST(ch == CTLESC || ch == CTLNUL)
        ret[retind++] = CTLESC;
      ret[retind++] = ch;
      continue;
    }
      else if MBTEST(ch == CTLESC || ch == CTLNUL)  /* special shell escapes */
    {
      RESIZE_MALLOCED_BUFFER (ret, retind, 2, retsize, 64);
      ret[retind++] = CTLESC;
      ret[retind++] = ch;
      continue;
    }
      else if MBTEST(ch == close)       /* ending delimiter */
    count--;
#if 1
      /* handle nested ${...} specially. */
      else if MBTEST(open != close && was_dollar && open == '{' && ch == open) /* } */
    count++;
#endif
      else if MBTEST(((flags & P_FIRSTCLOSE) == 0) && ch == open)   /* nested begin */
    count++;

      /* Add this character. */
      RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64);
      ret[retind++] = ch;

      if (open == '\'')         /* '' inside grouping construct */
    {
      if MBTEST((flags & P_ALLOWESC) && ch == '\\')
        pass_next_character++;
      continue;
    }

      if MBTEST(ch == '\\')         /* backslashes */
    pass_next_character++;

      if (open != close)        /* a grouping construct */
    {
      if MBTEST(shellquote (ch))
        {
          /* '', ``, or "" inside $(...) or other grouping construct. */
          push_delimiter (dstack, ch);
          if MBTEST(was_dollar && ch == '\'')   /* $'...' inside group */
        nestret = parse_matched_pair (ch, ch, ch, &nestlen, P_ALLOWESC);
          else
        nestret = parse_matched_pair (ch, ch, ch, &nestlen, 0);
          pop_delimiter (dstack);
          if (nestret == &matched_pair_error)
        {
          free (ret);
          return &matched_pair_error;
        }
          if MBTEST(was_dollar && ch == '\'')
        {
          /* Translate $'...' here. */
          ttrans = ansiexpand (nestret, 0, nestlen - 1, &ttranslen);
          xfree (nestret);
          nestret = sh_single_quote (ttrans);
          free (ttrans);
          nestlen = strlen (nestret);
          retind -= 2;      /* back up before the $' */
        }
          else if MBTEST(was_dollar && ch == '"')
        {
          /* Locale expand $"..." here. */
          ttrans = localeexpand (nestret, 0, nestlen - 1, start_lineno, &ttranslen);
          xfree (nestret);
          nestret = (char *)xmalloc (ttranslen + 3);
          nestret[0] = '"';
          strcpy (nestret + 1, ttrans);
          nestret[ttranslen + 1] = '"';
          nestret[ttranslen += 2] = '\0';
          free (ttrans);
          nestlen = ttranslen;
          retind -= 2;      /* back up before the $" */
        }

          if (nestlen)
        {
          RESIZE_MALLOCED_BUFFER (ret, retind, nestlen, retsize, 64);
          strcpy (ret + retind, nestret);
          retind += nestlen;
        }
          FREE (nestret);
        }
    }
      /* Parse an old-style command substitution within double quotes as a
     single word. */
      /* XXX - sh and ksh93 don't do this - XXX */
      else if MBTEST(open == '"' && ch == '`')
    {
      nestret = parse_matched_pair (0, '`', '`', &nestlen, 0);
      if (nestret == &matched_pair_error)
        {
          free (ret);
          return &matched_pair_error;
        }
      if (nestlen)
        {
          RESIZE_MALLOCED_BUFFER (ret, retind, nestlen, retsize, 64);
          strcpy (ret + retind, nestret);
          retind += nestlen;
        }
      FREE (nestret);
    }
      else if MBTEST(was_dollar && (ch == '(' || ch == '{' || ch == '['))   /* ) } ] */
    /* check for $(), $[], or ${} inside quoted string. */
    {
      if (open == ch)   /* undo previous increment */
        count--;
      if (ch == '(')        /* ) */
        nestret = parse_matched_pair (0, '(', ')', &nestlen, 0);
      else if (ch == '{')       /* } */
        nestret = parse_matched_pair (0, '{', '}', &nestlen, P_FIRSTCLOSE);
      else if (ch == '[')       /* ] */
        nestret = parse_matched_pair (0, '[', ']', &nestlen, 0);
      if (nestret == &matched_pair_error)
        {
          free (ret);
          return &matched_pair_error;
        }
      if (nestlen)
        {
          RESIZE_MALLOCED_BUFFER (ret, retind, nestlen, retsize, 64);
          strcpy (ret + retind, nestret);
          retind += nestlen;
        }
      FREE (nestret);
    }
      was_dollar = MBTEST(ch == '$');
    }

  ret[retind] = '\0';
  if (lenp)
    *lenp = retind;
  return ret;
}

#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND)
/* Parse a double-paren construct.  It can be either an arithmetic
   command, an arithmetic `for' command, or a nested subshell.  Returns
   the parsed token, -1 on error, or -2 if we didn't do anything and
   should just go on. */
static int
parse_dparen (c)
     int c;
{
  int cmdtyp, len, sline;
  char *wval, *wv2;
  WORD_DESC *wd;

#if defined (ARITH_FOR_COMMAND)
  if (last_read_token == FOR)
    {
      arith_for_lineno = line_number;
      cmdtyp = parse_arith_cmd (&wval);
      if (cmdtyp == 1)
    {
      /* parse_arith_cmd adds quotes at the beginning and end
         of the string it returns; we need to take those out. */
      len = strlen (wval);
      wv2 = (char *)xmalloc (len);
      strncpy (wv2, wval + 1, len - 2);
      wv2[len - 2] = '\0';
      wd = make_word (wv2);
      yylval.word_list = make_word_list (wd, (WORD_LIST *)NULL);
      free (wval);
      free (wv2);
      return (ARITH_FOR_EXPRS);
    }
      else
    return -1;      /* ERROR */
    }
#endif

#if defined (DPAREN_ARITHMETIC)
  if (reserved_word_acceptable (last_read_token))
    {
      sline = line_number;
      cmdtyp = parse_arith_cmd (&wval);
      if (cmdtyp == 1)  /* arithmetic command */
    {
      wd = make_word (wval);
      wd->flags = W_QUOTED;
      yylval.word_list = make_word_list (wd, (WORD_LIST *)NULL);
      free (wval);  /* make_word copies it */
      return (ARITH_CMD);
    }
      else if (cmdtyp == 0) /* nested subshell */
    {
      push_string (wval, 0, (alias_t *)NULL);
      if ((parser_state & PST_CASEPAT) == 0)
        parser_state |= PST_SUBSHELL;
      return (c);
    }
      else          /* ERROR */
    return -1;
    }
#endif

  return -2;            /* XXX */
}

/* We've seen a `(('.  Look for the matching `))'.  If we get it, return 1.
   If not, assume it's a nested subshell for backwards compatibility and
   return 0.  In any case, put the characters we've consumed into a locally-
   allocated buffer and make *ep point to that buffer.  Return -1 on an
   error, for example EOF. */
static int
parse_arith_cmd (ep)
     char **ep;
{
  int exp_lineno, rval, c;
  char *ttok, *tokstr;
  int ttoklen;

  exp_lineno = line_number;
  ttok = parse_matched_pair (0, '(', ')', &ttoklen, 0);
  rval = 1;
  if (ttok == &matched_pair_error)
    return -1;
  /* Check that the next character is the closing right paren.  If
     not, this is a syntax error. ( */
  c = shell_getc (0);
  if MBTEST(c != ')')
    rval = 0;

  tokstr = (char *)xmalloc (ttoklen + 4);

  /* (( ... )) -> "..." */
  tokstr[0] = (rval == 1) ? '"' : '(';
  strncpy (tokstr + 1, ttok, ttoklen - 1);  /* don't copy the final `)' */
  if (rval == 1)
    {
      tokstr[ttoklen] = '"';
      tokstr[ttoklen+1] = '\0';
    }
  else
    {
      tokstr[ttoklen] = ')';
      tokstr[ttoklen+1] = c;
      tokstr[ttoklen+2] = '\0';
    }
  *ep = tokstr;
  FREE (ttok);
  return rval;
}
#endif /* DPAREN_ARITHMETIC || ARITH_FOR_COMMAND */

#if defined (COND_COMMAND)
static void
cond_error ()
{
  char *etext;

  if (EOF_Reached && cond_token != COND_ERROR)      /* [[ */
    parser_error (cond_lineno, "unexpected EOF while looking for `]]'");
  else if (cond_token != COND_ERROR)
    {
      if (etext = error_token_from_token (cond_token))
    {
      parser_error (cond_lineno, "syntax error in conditional expression: unexpected token `%s'", etext);
      free (etext);
    }
      else
    parser_error (cond_lineno, "syntax error in conditional expression");
    }
}

static COND_COM *
cond_expr ()
{
  return (cond_or ());  
}

static COND_COM *
cond_or ()
{
  COND_COM *l, *r;

  l = cond_and ();
  if (cond_token == OR_OR)
    {
      r = cond_or ();
      l = make_cond_node (COND_OR, (WORD_DESC *)NULL, l, r);
    }
  return l;
}

static COND_COM *
cond_and ()
{
  COND_COM *l, *r;

  l = cond_term ();
  if (cond_token == AND_AND)
    {
      r = cond_and ();
      l = make_cond_node (COND_AND, (WORD_DESC *)NULL, l, r);
    }
  return l;
}

static int
cond_skip_newlines ()
{
  while ((cond_token = read_token (READ)) == '\n')
    {
      if (interactive && (bash_input.type == st_stdin || bash_input.type == st_stream))
    prompt_again ();
    }
  return (cond_token);
}

#define COND_RETURN_ERROR() \
  do { cond_token = COND_ERROR; return ((COND_COM *)NULL); } while (0)

static COND_COM *
cond_term ()
{
  WORD_DESC *op;
  COND_COM *term, *tleft, *tright;
  int tok, lineno;
  char *etext;

  /* Read a token.  It can be a left paren, a `!', a unary operator, or a
     word that should be the first argument of a binary operator.  Start by
     skipping newlines, since this is a compound command. */
  tok = cond_skip_newlines ();
  lineno = line_number;
  if (tok == COND_END)
    {
      COND_RETURN_ERROR ();
    }
  else if (tok == '(')
    {
      term = cond_expr ();
      if (cond_token != ')')
    {
      if (term)
        dispose_cond_node (term);       /* ( */
      if (etext = error_token_from_token (cond_token))
        {
          parser_error (lineno, "unexpected token `%s', expected `)'", etext);
          free (etext);
        }
      else
        parser_error (lineno, "expected `)'");
      COND_RETURN_ERROR ();
    }
      term = make_cond_node (COND_EXPR, (WORD_DESC *)NULL, term, (COND_COM *)NULL);
      (void)cond_skip_newlines ();
    }
  else if (tok == BANG || (tok == WORD && (yylval.word->word[0] == '!' && yylval.word->word[1] == '\0')))
    {
      if (tok == WORD)
    dispose_word (yylval.word); /* not needed */
      term = cond_term ();
      if (term)
    term->flags |= CMD_INVERT_RETURN;
    }
  else if (tok == WORD && test_unop (yylval.word->word))
    {
      op = yylval.word;
      tok = read_token (READ);
      if (tok == WORD)
    {
      tleft = make_cond_node (COND_TERM, yylval.word, (COND_COM *)NULL, (COND_COM *)NULL);
      term = make_cond_node (COND_UNARY, op, tleft, (COND_COM *)NULL);
    }
      else
    {
      dispose_word (op);
      if (etext = error_token_from_token (tok))
        {
          parser_error (line_number, "unexpected argument `%s' to conditional unary operator", etext);
          free (etext);
        }
      else
        parser_error (line_number, "unexpected argument to conditional unary operator");
      COND_RETURN_ERROR ();
    }

      (void)cond_skip_newlines ();
    }
  else if (tok == WORD)     /* left argument to binary operator */
    {
      /* lhs */
      tleft = make_cond_node (COND_TERM, yylval.word, (COND_COM *)NULL, (COND_COM *)NULL);

      /* binop */
      tok = read_token (READ);
      if (tok == WORD && test_binop (yylval.word->word))
    op = yylval.word;
      else if (tok == '<' || tok == '>')
    op = make_word_from_token (tok);  /* ( */
      /* There should be a check before blindly accepting the `)' that we have
     seen the opening `('. */
      else if (tok == COND_END || tok == AND_AND || tok == OR_OR || tok == ')')
    {
      /* Special case.  [[ x ]] is equivalent to [[ -n x ]], just like
         the test command.  Similarly for [[ x && expr ]] or
         [[ x || expr ]] or [[ (x) ]]. */
      op = make_word ("-n");
      term = make_cond_node (COND_UNARY, op, tleft, (COND_COM *)NULL);
      cond_token = tok;
      return (term);
    }
      else
    {
      if (etext = error_token_from_token (tok))
        {
          parser_error (line_number, "unexpected token `%s', conditional binary operator expected", etext);
          free (etext);
        }
      else
        parser_error (line_number, "conditional binary operator expected");
      dispose_cond_node (tleft);
      COND_RETURN_ERROR ();
    }

      /* rhs */
      tok = read_token (READ);
      if (tok == WORD)
    {
      tright = make_cond_node (COND_TERM, yylval.word, (COND_COM *)NULL, (COND_COM *)NULL);
      term = make_cond_node (COND_BINARY, op, tleft, tright);
    }
      else
    {
      if (etext = error_token_from_token (tok))
        {
          parser_error (line_number, "unexpected argument `%s' to conditional binary operator", etext);
          free (etext);
        }
      else
        parser_error (line_number, "unexpected argument to conditional binary operator");
      dispose_cond_node (tleft);
      dispose_word (op);
      COND_RETURN_ERROR ();
    }

      (void)cond_skip_newlines ();
    }
  else
    {
      if (tok < 256)
    parser_error (line_number, "unexpected token `%c' in conditional command", tok);
      else if (etext = error_token_from_token (tok))
    {
      parser_error (line_number, "unexpected token `%s' in conditional command", etext);
      free (etext);
    }
      else
    parser_error (line_number, "unexpected token %d in conditional command", tok);
      COND_RETURN_ERROR ();
    }
  return (term);
}      

/* This is kind of bogus -- we slip a mini recursive-descent parser in
   here to handle the conditional statement syntax. */
static COMMAND *
parse_cond_command ()
{
  COND_COM *cexp;

  cexp = cond_expr ();
  return (make_cond_command (cexp));
}
#endif

#if defined (ARRAY_VARS)
/* When this is called, it's guaranteed that we don't care about anything
   in t beyond i.  We do save and restore the chars, though. */
static int
token_is_assignment (t, i)
     char *t;
     int i;
{
  unsigned char c, c1;
  int r;

  c = t[i]; c1 = t[i+1];
  t[i] = '='; t[i+1] = '\0';
  r = assignment (t);
  t[i] = c; t[i+1] = c1;
  return r;
}

static int
token_is_ident (t, i)
     char *t;
     int i;
{
  unsigned char c;
  int r;

  c = t[i];
  t[i] = '\0';
  r = legal_identifier (t);
  t[i] = c;
  return r;
}
#endif

static int
read_token_word (character)
     int character;
{
  /* The value for YYLVAL when a WORD is read. */
  WORD_DESC *the_word;

  /* Index into the token that we are building. */
  int token_index;

  /* ALL_DIGITS becomes zero when we see a non-digit. */
  int all_digit_token;

  /* DOLLAR_PRESENT becomes non-zero if we see a `$'. */
  int dollar_present;

  /* QUOTED becomes non-zero if we see one of ("), ('), (`), or (\). */
  int quoted;

  /* Non-zero means to ignore the value of the next character, and just
     to add it no matter what. */
 int pass_next_character;

  /* The current delimiting character. */
  int cd;
  int result, peek_char;
  char *ttok, *ttrans;
  int ttoklen, ttranslen;
  intmax_t lvalue;

  if (token_buffer_size < TOKEN_DEFAULT_INITIAL_SIZE)
    token = (char *)xrealloc (token, token_buffer_size = TOKEN_DEFAULT_INITIAL_SIZE);

  token_index = 0;
  all_digit_token = DIGIT (character);
  dollar_present = quoted = pass_next_character = 0;

  for (;;)
    {
      if (character == EOF)
    goto got_token;

      if (pass_next_character)
    {
      pass_next_character = 0;
      goto got_character;
    }

      cd = current_delimiter (dstack);

      /* Handle backslashes.  Quote lots of things when not inside of
     double-quotes, quote some things inside of double-quotes. */
      if MBTEST(character == '\\')
    {
      peek_char = shell_getc (0);

      /* Backslash-newline is ignored in all cases except
         when quoted with single quotes. */
      if (peek_char == '\n')
        {
          character = '\n';
          goto next_character;
        }
      else
        {
          shell_ungetc (peek_char);

          /* If the next character is to be quoted, note it now. */
          if (cd == 0 || cd == '`' ||
          (cd == '"' && peek_char >= 0 && (sh_syntaxtab[peek_char] & CBSDQUOTE)))
        pass_next_character++;

          quoted = 1;
          goto got_character;
        }
    }

      /* Parse a matched pair of quote characters. */
      if MBTEST(shellquote (character))
    {
      push_delimiter (dstack, character);
      ttok = parse_matched_pair (character, character, character, &ttoklen, 0);
      pop_delimiter (dstack);
      if (ttok == &matched_pair_error)
        return -1;      /* Bail immediately. */
      RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 2,
                  token_buffer_size, TOKEN_DEFAULT_GROW_SIZE);
      token[token_index++] = character;
      strcpy (token + token_index, ttok);
      token_index += ttoklen;
      all_digit_token = 0;
      quoted = 1;
      dollar_present |= (character == '"' && strchr (ttok, '$') != 0);
      FREE (ttok);
      goto next_character;
    }

#ifdef EXTENDED_GLOB
      /* Parse a ksh-style extended pattern matching specification. */
      if (extended_glob && PATTERN_CHAR (character))
    {
      peek_char = shell_getc (1);
      if MBTEST(peek_char == '(')       /* ) */
        {
          push_delimiter (dstack, peek_char);
          ttok = parse_matched_pair (cd, '(', ')', &ttoklen, 0);
          pop_delimiter (dstack);
          if (ttok == &matched_pair_error)
        return -1;      /* Bail immediately. */
          RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 2,
                      token_buffer_size,
                      TOKEN_DEFAULT_GROW_SIZE);
          token[token_index++] = character;
          token[token_index++] = peek_char;
          strcpy (token + token_index, ttok);
          token_index += ttoklen;
          FREE (ttok);
          dollar_present = all_digit_token = 0;
          goto next_character;
        }
      else
        shell_ungetc (peek_char);
    }
#endif /* EXTENDED_GLOB */

      /* If the delimiter character is not single quote, parse some of
     the shell expansions that must be read as a single word. */
      if (shellexp (character))
    {
      peek_char = shell_getc (1);
      /* $(...), <(...), >(...), $((...)), ${...}, and $[...] constructs */
      if MBTEST(peek_char == '(' || \
        ((peek_char == '{' || peek_char == '[') && character == '$'))   /* ) ] } */
        {
          if (peek_char == '{')     /* } */
        ttok = parse_matched_pair (cd, '{', '}', &ttoklen, P_FIRSTCLOSE);
          else if (peek_char == '(')        /* ) */
        {
          /* XXX - push and pop the `(' as a delimiter for use by
             the command-oriented-history code.  This way newlines
             appearing in the $(...) string get added to the
             history literally rather than causing a possibly-
             incorrect `;' to be added. ) */
          push_delimiter (dstack, peek_char);
          ttok = parse_matched_pair (cd, '(', ')', &ttoklen, 0);
          pop_delimiter (dstack);
        }
          else
        ttok = parse_matched_pair (cd, '[', ']', &ttoklen, 0);
          if (ttok == &matched_pair_error)
        return -1;      /* Bail immediately. */
          RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 2,
                      token_buffer_size,
                      TOKEN_DEFAULT_GROW_SIZE);
          token[token_index++] = character;
          token[token_index++] = peek_char;
          strcpy (token + token_index, ttok);
          token_index += ttoklen;
          FREE (ttok);
          dollar_present = 1;
          all_digit_token = 0;
          goto next_character;
        }
      /* This handles $'...' and $"..." new-style quoted strings. */
      else if MBTEST(character == '$' && (peek_char == '\'' || peek_char == '"'))
        {
          int first_line;

          first_line = line_number;
          push_delimiter (dstack, peek_char);
          ttok = parse_matched_pair (peek_char, peek_char, peek_char,
                     &ttoklen,
                     (peek_char == '\'') ? P_ALLOWESC : 0);
          pop_delimiter (dstack);
          if (ttok == &matched_pair_error)
        return -1;
          if (peek_char == '\'')
        {
          ttrans = ansiexpand (ttok, 0, ttoklen - 1, &ttranslen);
          free (ttok);
          /* Insert the single quotes and correctly quote any
             embedded single quotes (allowed because P_ALLOWESC was
             passed to parse_matched_pair). */
          ttok = sh_single_quote (ttrans);
          free (ttrans);
          ttrans = ttok;
          ttranslen = strlen (ttrans);
        }
          else
        {
          /* Try to locale-expand the converted string. */
          ttrans = localeexpand (ttok, 0, ttoklen - 1, first_line, &ttranslen);
          free (ttok);

          /* Add the double quotes back */
          ttok = (char *)xmalloc (ttranslen + 3);
          ttok[0] = '"';
          strcpy (ttok + 1, ttrans);
          ttok[ttranslen + 1] = '"';
          ttok[ttranslen += 2] = '\0';
          free (ttrans);
          ttrans = ttok;
        }

          RESIZE_MALLOCED_BUFFER (token, token_index, ttranslen + 2,
                      token_buffer_size,
                      TOKEN_DEFAULT_GROW_SIZE);
          strcpy (token + token_index, ttrans);
          token_index += ttranslen;
          FREE (ttrans);
          quoted = 1;
          all_digit_token = 0;
          goto next_character;
        }
      /* This could eventually be extended to recognize all of the
         shell's single-character parameter expansions, and set flags.*/
      else if MBTEST(character == '$' && peek_char == '$')
        {
          ttok = (char *)xmalloc (3);
          ttok[0] = ttok[1] = '$';
          ttok[2] = '\0';
          RESIZE_MALLOCED_BUFFER (token, token_index, 3,
                      token_buffer_size,
                      TOKEN_DEFAULT_GROW_SIZE);
          strcpy (token + token_index, ttok);
          token_index += 2;
          dollar_present = 1;
          all_digit_token = 0;
          FREE (ttok);
          goto next_character;
        }
      else
        shell_ungetc (peek_char);
    }

#if defined (ARRAY_VARS)
      /* Identify possible array subscript assignment; match [...] */
      else if MBTEST(character == '[' && token_index > 0 && assignment_acceptable (last_read_token) && token_is_ident (token, token_index)) /* ] */
        {
      ttok = parse_matched_pair (cd, '[', ']', &ttoklen, 0);
      if (ttok == &matched_pair_error)
        return -1;      /* Bail immediately. */
      RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 2,
                  token_buffer_size,
                  TOKEN_DEFAULT_GROW_SIZE);
      token[token_index++] = character;
      strcpy (token + token_index, ttok);
      token_index += ttoklen;
      FREE (ttok);
      all_digit_token = 0;
      goto next_character;
        }
      /* Identify possible compound array variable assignment. */
      else if MBTEST(character == '=' && token_index > 0 && token_is_assignment (token, token_index))
    {
      peek_char = shell_getc (1);
      if MBTEST(peek_char == '(')       /* ) */
        {
          ttok = parse_compound_assignment (&ttoklen);

          RESIZE_MALLOCED_BUFFER (token, token_index, ttoklen + 4,
                      token_buffer_size,
                      TOKEN_DEFAULT_GROW_SIZE);

          token[token_index++] = '=';
          token[token_index++] = '(';
          if (ttok)
        {
          strcpy (token + token_index, ttok);
          token_index += ttoklen;
        }
          token[token_index++] = ')';
          FREE (ttok);
          all_digit_token = 0;
          goto next_character;
        }
      else
        shell_ungetc (peek_char);
    }
#endif

      /* When not parsing a multi-character word construct, shell meta-
     characters break words. */
      if MBTEST(shellbreak (character))
    {
      shell_ungetc (character);
      goto got_token;
    }

    got_character:

      all_digit_token &= DIGIT (character);
      dollar_present |= character == '$';

      if (character == CTLESC || character == CTLNUL)
    token[token_index++] = CTLESC;

      token[token_index++] = character;

      RESIZE_MALLOCED_BUFFER (token, token_index, 1, token_buffer_size,
                  TOKEN_DEFAULT_GROW_SIZE);

    next_character:
      if (character == '\n' && interactive &&
       (bash_input.type == st_stdin || bash_input.type == st_stream))
    prompt_again ();

      /* We want to remove quoted newlines (that is, a \<newline> pair)
     unless we are within single quotes or pass_next_character is
     set (the shell equivalent of literal-next). */
      cd = current_delimiter (dstack);
      character = shell_getc (cd != '\'' && pass_next_character == 0);
    }   /* end for (;;) */

got_token:

  token[token_index] = '\0';

  /* Check to see what thing we should return.  If the last_read_token
     is a `<', or a `&', or the character which ended this token is
     a '>' or '<', then, and ONLY then, is this input token a NUMBER.
     Otherwise, it is just a word, and should be returned as such. */
  if MBTEST(all_digit_token && (character == '<' || character == '>' || \
            last_read_token == LESS_AND || \
            last_read_token == GREATER_AND))
      {
    if (legal_number (token, &lvalue) && (int)lvalue == lvalue)
      yylval.number = lvalue;
    else
      yylval.number = -1;
    return (NUMBER);
      }

  /* Check for special case tokens. */
  result = (last_shell_getc_is_singlebyte) ? special_case_tokens (token) : -1;
  if (result >= 0)
    return result;

#if defined (ALIAS)
  /* Posix.2 does not allow reserved words to be aliased, so check for all
     of them, including special cases, before expanding the current token
     as an alias. */
  if MBTEST(posixly_correct)
    CHECK_FOR_RESERVED_WORD (token);

  /* Aliases are expanded iff EXPAND_ALIASES is non-zero, and quoting
     inhibits alias expansion. */
  if (expand_aliases && quoted == 0)
    {
      result = alias_expand_token (token);
      if (result == RE_READ_TOKEN)
    return (RE_READ_TOKEN);
      else if (result == NO_EXPANSION)
    parser_state &= ~PST_ALEXPNEXT;
    }

  /* If not in Posix.2 mode, check for reserved words after alias
     expansion. */
  if MBTEST(posixly_correct == 0)
#endif
    CHECK_FOR_RESERVED_WORD (token);

  the_word = (WORD_DESC *)xmalloc (sizeof (WORD_DESC));
  the_word->word = (char *)xmalloc (1 + token_index);
  the_word->flags = 0;
  strcpy (the_word->word, token);
  if (dollar_present)
    the_word->flags |= W_HASDOLLAR;
  if (quoted)
    the_word->flags |= W_QUOTED;
  /* A word is an assignment if it appears at the beginning of a
     simple command, or after another assignment word.  This is
     context-dependent, so it cannot be handled in the grammar. */
  if (assignment (token))
    {
      the_word->flags |= W_ASSIGNMENT;
      /* Don't perform word splitting on assignment statements. */
      if (assignment_acceptable (last_read_token))
    the_word->flags |= W_NOSPLIT;
    }

  yylval.word = the_word;

  result = ((the_word->flags & (W_ASSIGNMENT|W_NOSPLIT)) == (W_ASSIGNMENT|W_NOSPLIT))
        ? ASSIGNMENT_WORD : WORD;

  if (last_read_token == FUNCTION)
    {
      parser_state |= PST_ALLOWOPNBRC;
      function_dstart = line_number;
    }

  return (result);
}

/* Return 1 if TOKSYM is a token that after being read would allow
   a reserved word to be seen, else 0. */
static int
reserved_word_acceptable (toksym)
     int toksym;
{
  switch (toksym)
    {
    case '\n':
    case ';':
    case '(':
    case ')':
    case '|':
    case '&':
    case '{':
    case '}':       /* XXX */
    case AND_AND:
    case BANG:
    case DO:
    case DONE:
    case ELIF:
    case ELSE:
    case ESAC:
    case FI:
    case IF:
    case OR_OR:
    case SEMI_SEMI:
    case THEN:
    case TIME:
    case TIMEOPT:
    case UNTIL:
    case WHILE:
    case 0:
      return 1;
    default:
      return 0;
    }
}
    
/* Return the index of TOKEN in the alist of reserved words, or -1 if
   TOKEN is not a shell reserved word. */
int
find_reserved_word (tokstr)
     char *tokstr;
{
  int i;
  for (i = 0; word_token_alist[i].word; i++)
    if (STREQ (tokstr, word_token_alist[i].word))
      return i;
  return -1;
}

#if 0
#if defined (READLINE)
/* Called after each time readline is called.  This insures that whatever
   the new prompt string is gets propagated to readline's local prompt
   variable. */
static void
reset_readline_prompt ()
{
  char *temp_prompt;

  if (prompt_string_pointer)
    {
      temp_prompt = (*prompt_string_pointer)
            ? decode_prompt_string (*prompt_string_pointer)
            : (char *)NULL;

      if (temp_prompt == 0)
    {
      temp_prompt = (char *)xmalloc (1);
      temp_prompt[0] = '\0';
    }

      FREE (current_readline_prompt);
      current_readline_prompt = temp_prompt;
    }
}
#endif /* READLINE */
#endif /* 0 */

#if defined (HISTORY)
/* A list of tokens which can be followed by newlines, but not by
   semi-colons.  When concatenating multiple lines of history, the
   newline separator for such tokens is replaced with a space. */
static int no_semi_successors[] = {
  '\n', '{', '(', ')', ';', '&', '|',
  CASE, DO, ELSE, IF, SEMI_SEMI, THEN, UNTIL, WHILE, AND_AND, OR_OR, IN,
  0
};

/* If we are not within a delimited expression, try to be smart
   about which separators can be semi-colons and which must be
   newlines.  Returns the string that should be added into the
   history entry. */
char *
history_delimiting_chars ()
{
  register int i;

  if (dstack.delimiter_depth != 0)
    return ("\n");
    
  /* First, handle some special cases. */
  /*(*/
  /* If we just read `()', assume it's a function definition, and don't
     add a semicolon.  If the token before the `)' was not `(', and we're
     not in the midst of parsing a case statement, assume it's a
     parenthesized command and add the semicolon. */
  /*)(*/
  if (token_before_that == ')')
    {
      if (two_tokens_ago == '(')    /*)*/   /* function def */
    return " ";
      /* This does not work for subshells inside case statement
     command lists.  It's a suboptimal solution. */
      else if (parser_state & PST_CASESTMT) /* case statement pattern */
    return " ";
      else  
    return "; ";                /* (...) subshell */
    }
  else if (token_before_that == WORD && two_tokens_ago == FUNCTION)
    return " ";     /* function def using `function name' without `()' */

  else if (token_before_that == WORD && two_tokens_ago == FOR)
    {
      /* Tricky.  `for i\nin ...' should not have a semicolon, but
     `for i\ndo ...' should.  We do what we can. */
      for (i = shell_input_line_index; whitespace(shell_input_line[i]); i++)
    ;
      if (shell_input_line[i] && shell_input_line[i] == 'i' && shell_input_line[i+1] == 'n')
    return " ";
      return ";";
    }

  for (i = 0; no_semi_successors[i]; i++)
    {
      if (token_before_that == no_semi_successors[i])
    return (" ");
    }

  return ("; ");
}
#endif /* HISTORY */

/* Issue a prompt, or prepare to issue a prompt when the next character
   is read. */
static void
prompt_again ()
{
  char *temp_prompt;

  if (!interactive) /* XXX */
    return;

  ps1_prompt = get_string_value ("PS1");
  ps2_prompt = get_string_value ("PS2");

  if (!prompt_string_pointer)
    prompt_string_pointer = &ps1_prompt;

  temp_prompt = *prompt_string_pointer
            ? decode_prompt_string (*prompt_string_pointer)
            : (char *)NULL;

  if (temp_prompt == 0)
    {
      temp_prompt = (char *)xmalloc (1);
      temp_prompt[0] = '\0';
    }

  current_prompt_string = *prompt_string_pointer;
  prompt_string_pointer = &ps2_prompt;

#if defined (READLINE)
  if (!no_line_editing)
    {
      FREE (current_readline_prompt);
      current_readline_prompt = temp_prompt;
    }
  else
#endif  /* READLINE */
    {
      FREE (current_decoded_prompt);
      current_decoded_prompt = temp_prompt;
    }
}

int
get_current_prompt_level ()
{
  return ((current_prompt_string && current_prompt_string == ps2_prompt) ? 2 : 1);
}

void
set_current_prompt_level (x)
     int x;
{
  prompt_string_pointer = (x == 2) ? &ps2_prompt : &ps1_prompt;
  current_prompt_string = *prompt_string_pointer;
}
      
static void
print_prompt ()
{
  fprintf (stderr, "%s", current_decoded_prompt);
  fflush (stderr);
}

/* Return a string which will be printed as a prompt.  The string
   may contain special characters which are decoded as follows:

    \a  bell (ascii 07)
    \d  the date in Day Mon Date format
    \e  escape (ascii 033)
    \h  the hostname up to the first `.'
    \H  the hostname
    \j  the number of active jobs
    \l  the basename of the shell's tty device name
    \n  CRLF
    \r  CR
    \s  the name of the shell
    \t  the time in 24-hour hh:mm:ss format
    \T  the time in 12-hour hh:mm:ss format
    \@  the time in 12-hour hh:mm am/pm format
    \A  the time in 24-hour hh:mm format
    \D{fmt} the result of passing FMT to strftime(3)
    \u  your username
    \v  the version of bash (e.g., 2.00)
    \V  the release of bash, version + patchlevel (e.g., 2.00.0)
    \w  the current working directory
    \W  the last element of $PWD
    \!  the history number of this command
    \#  the command number of this command
    \$  a $ or a # if you are root
    \nnn    character code nnn in octal
    \\  a backslash
    \[  begin a sequence of non-printing chars
    \]  end a sequence of non-printing chars
*/
#define PROMPT_GROWTH 48
char *
decode_prompt_string (string)
     char *string;
{
  WORD_LIST *list;
  char *result, *t;
  struct dstack save_dstack;
  int last_exit_value;
#if defined (PROMPT_STRING_DECODE)
  int result_size, result_index;
  int c, n;
  char *temp, octal_string[4];
  struct tm *tm;  
  time_t the_time;
  char timebuf[128];
  char *timefmt;

  result = (char *)xmalloc (result_size = PROMPT_GROWTH);
  result[result_index = 0] = 0;
  temp = (char *)NULL;

  while (c = *string++)
    {
      if (posixly_correct && c == '!')
    {
      if (*string == '!')
        {
          temp = savestring ("!");
          goto add_string;
        }
      else
        {
#if !defined (HISTORY)
        temp = savestring ("1");
#else /* HISTORY */
        temp = itos (history_number ());
#endif /* HISTORY */
        string--;   /* add_string increments string again. */
        goto add_string;
        }
    }
      if (c == '\\')
    {
      c = *string;

      switch (c)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
          strncpy (octal_string, string, 3);
          octal_string[3] = '\0';

          n = read_octal (octal_string);
          temp = (char *)xmalloc (3);

          if (n == CTLESC || n == CTLNUL)
        {
          temp[0] = CTLESC;
          temp[1] = n;
          temp[2] = '\0';
        }
          else if (n == -1)
        {
          temp[0] = '\\';
          temp[1] = '\0';
        }
          else
        {
          temp[0] = n;
          temp[1] = '\0';
        }

          for (c = 0; n != -1 && c < 3 && ISOCTAL (*string); c++)
        string++;

          c = 0;        /* tested at add_string: */
          goto add_string;

        case 'd':
        case 't':
        case 'T':
        case '@':
        case 'A':
          /* Make the current time/date into a string. */
          (void) time (&the_time);
          tm = localtime (&the_time);

          if (c == 'd')
        n = strftime (timebuf, sizeof (timebuf), "%a %b %d", tm);
          else if (c == 't')
        n = strftime (timebuf, sizeof (timebuf), "%H:%M:%S", tm);
          else if (c == 'T')
        n = strftime (timebuf, sizeof (timebuf), "%I:%M:%S", tm);
          else if (c == '@')
        n = strftime (timebuf, sizeof (timebuf), "%I:%M %p", tm);
          else if (c == 'A')
        n = strftime (timebuf, sizeof (timebuf), "%H:%M", tm);

          timebuf[sizeof(timebuf) - 1] = '\0';
          temp = savestring (timebuf);
          goto add_string;

        case 'D':       /* strftime format */
          if (string[1] != '{')     /* } */
        goto not_escape;

          (void) time (&the_time);
          tm = localtime (&the_time);
          string += 2;          /* skip { */
          timefmt = xmalloc (strlen (string) + 3);
          for (t = timefmt; *string && *string != '}'; )
        *t++ = *string++;
          *t = '\0';
          c = *string;  /* tested at add_string */
          if (timefmt[0] == '\0')
        {
          timefmt[0] = '%';
          timefmt[1] = 'X'; /* locale-specific current time */
          timefmt[2] = '\0';
        }
          n = strftime (timebuf, sizeof (timebuf), timefmt, tm);
          free (timefmt);

          timebuf[sizeof(timebuf) - 1] = '\0';
          if (promptvars || posixly_correct)
        /* Make sure that expand_prompt_string is called with a
           second argument of Q_DOUBLE_QUOTES if we use this
           function here. */
        temp = sh_backslash_quote_for_double_quotes (timebuf);
          else
        temp = savestring (timebuf);
          goto add_string;
          
        case 'n':
          temp = (char *)xmalloc (3);
          temp[0] = no_line_editing ? '\n' : '\r';
          temp[1] = no_line_editing ? '\0' : '\n';
          temp[2] = '\0';
          goto add_string;

        case 's':
          temp = base_pathname (shell_name);
          temp = savestring (temp);
          goto add_string;

        case 'v':
        case 'V':
          temp = (char *)xmalloc (16);
          if (c == 'v')
        strcpy (temp, dist_version);
          else
        sprintf (temp, "%s.%d", dist_version, patch_level);
          goto add_string;

        case 'w':
        case 'W':
          {
        /* Use the value of PWD because it is much more efficient. */
        char t_string[PATH_MAX];
        int tlen;

        temp = get_string_value ("PWD");

        if (temp == 0)
          {
            if (getcwd (t_string, sizeof(t_string)) == 0)
              {
            t_string[0] = '.';
            tlen = 1;
              }
            else
              tlen = strlen (t_string);
          }
        else
          {
            tlen = sizeof (t_string) - 1;
            strncpy (t_string, temp, tlen);
          }
        t_string[tlen] = '\0';

#define ROOT_PATH(x)    ((x)[0] == '/' && (x)[1] == 0)
#define DOUBLE_SLASH_ROOT(x)    ((x)[0] == '/' && (x)[1] == '/' && (x)[2] == 0)
        if (c == 'W')
          {
            if (ROOT_PATH (t_string) == 0 && DOUBLE_SLASH_ROOT (t_string) == 0)
              {
            t = strrchr (t_string, '/');
            if (t)
              strcpy (t_string, t + 1);
              }
          }
#undef ROOT_PATH
#undef DOUBLE_SLASH_ROOT
        else
          /* polite_directory_format is guaranteed to return a string
             no longer than PATH_MAX - 1 characters. */
          strcpy (t_string, polite_directory_format (t_string));

        /* If we're going to be expanding the prompt string later,
           quote the directory name. */
        if (promptvars || posixly_correct)
          /* Make sure that expand_prompt_string is called with a
             second argument of Q_DOUBLE_QUOTES if we use this
             function here. */
          temp = sh_backslash_quote_for_double_quotes (t_string);
        else
          temp = savestring (t_string);

        goto add_string;
          }

        case 'u':
          if (current_user.user_name == 0)
        get_current_user_info ();
          temp = savestring (current_user.user_name);
          goto add_string;

        case 'h':
        case 'H':
          temp = savestring (current_host_name);
          if (c == 'h' && (t = (char *)strchr (temp, '.')))
        *t = '\0';
          goto add_string;

        case '#':
          temp = itos (current_command_number);
          goto add_string;

        case '!':
#if !defined (HISTORY)
          temp = savestring ("1");
#else /* HISTORY */
          temp = itos (history_number ());
#endif /* HISTORY */
          goto add_string;

        case '$':
          t = temp = (char *)xmalloc (3);
          if ((promptvars || posixly_correct) && (current_user.euid != 0))
        *t++ = '\\';
          *t++ = current_user.euid == 0 ? '#' : '$';
          *t = '\0';
          goto add_string;

        case 'j':
          temp = itos (count_all_jobs ());
          goto add_string;

        case 'l':
#if defined (HAVE_TTYNAME)
          temp = (char *)ttyname (fileno (stdin));
          t = temp ? base_pathname (temp) : "tty";
          temp = savestring (t);
#else
          temp = savestring ("tty");
#endif /* !HAVE_TTYNAME */
          goto add_string;

#if defined (READLINE)
        case '[':
        case ']':
          temp = (char *)xmalloc (3);
          temp[0] = '\001';
          temp[1] = (c == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE;
          temp[2] = '\0';
          goto add_string;
#endif /* READLINE */

        case '\\':
        case 'a':
        case 'e':
        case 'r':
          temp = (char *)xmalloc (2);
          if (c == 'a')
        temp[0] = '\07';
          else if (c == 'e')
        temp[0] = '\033';
          else if (c == 'r')
        temp[0] = '\r';
          else          /* (c == '\\') */
            temp[0] = c;
          temp[1] = '\0';
          goto add_string;

        default:
not_escape:
          temp = (char *)xmalloc (3);
          temp[0] = '\\';
          temp[1] = c;
          temp[2] = '\0';

        add_string:
          if (c)
        string++;
          result =
        sub_append_string (temp, result, &result_index, &result_size);
          temp = (char *)NULL; /* Freed in sub_append_string (). */
          result[result_index] = '\0';
          break;
        }
    }
      else
    {
      RESIZE_MALLOCED_BUFFER (result, result_index, 3, result_size, PROMPT_GROWTH);
      result[result_index++] = c;
      result[result_index] = '\0';
    }
    }
#else /* !PROMPT_STRING_DECODE */
  result = savestring (string);
#endif /* !PROMPT_STRING_DECODE */

  /* Save the delimiter stack and point `dstack' to temp space so any
     command substitutions in the prompt string won't result in screwing
     up the parser's quoting state. */
  save_dstack = dstack;
  dstack = temp_dstack;
  dstack.delimiter_depth = 0;

  /* Perform variable and parameter expansion and command substitution on
     the prompt string. */
  if (promptvars || posixly_correct)
    {
      last_exit_value = last_command_exit_value;
      list = expand_prompt_string (result, Q_DOUBLE_QUOTES);
      free (result);
      result = string_list (list);
      dispose_words (list);
      last_command_exit_value = last_exit_value;
    }
  else
    {
      t = dequote_string (result);
      free (result);
      result = t;
    }

  dstack = save_dstack;

  return (result);
}

/************************************************
 *                      *
 *      ERROR HANDLING          *
 *                      *
 ************************************************/

/* Report a syntax error, and restart the parser.  Call here for fatal
   errors. */
int
yyerror (msg)
     const char *msg;
{
  report_syntax_error ((char *)NULL);
  reset_parser ();
  return (0);
}

static char *
error_token_from_token (token)
     int token;
{
  char *t;

  if (t = find_token_in_alist (token, word_token_alist, 0))
    return t;

  if (t = find_token_in_alist (token, other_token_alist, 0))
    return t;

  t = (char *)NULL;
  /* This stuff is dicy and needs closer inspection */
  switch (current_token)
    {
    case WORD:
    case ASSIGNMENT_WORD:
      if (yylval.word)
    t = savestring (yylval.word->word);
      break;
    case NUMBER:
      t = itos (yylval.number);
      break;
    case ARITH_CMD:
      if (yylval.word_list)
        t = string_list (yylval.word_list);
      break;
    case ARITH_FOR_EXPRS:
      if (yylval.word_list)
    t = string_list_internal (yylval.word_list, " ; ");
      break;
    case COND_CMD:
      t = (char *)NULL;     /* punt */
      break;
    }

  return t;
}

static char *
error_token_from_text ()
{
  char *msg, *t;
  int token_end, i;

  t = shell_input_line;
  i = shell_input_line_index;
  token_end = 0;
  msg = (char *)NULL;

  if (i && t[i] == '\0')
    i--;

  while (i && (whitespace (t[i]) || t[i] == '\n'))
    i--;

  if (i)
    token_end = i + 1;

  while (i && (member (t[i], " \n\t;|&") == 0))
    i--;

  while (i != token_end && (whitespace (t[i]) || t[i] == '\n'))
    i++;

  /* Return our idea of the offending token. */
  if (token_end || (i == 0 && token_end == 0))
    {
      if (token_end)
    msg = substring (t, i, token_end);
      else  /* one-character token */
    {
      msg = (char *)xmalloc (2);
      msg[0] = t[i];
      msg[1] = '\0';
    }
    }

  return (msg);
}

static void
print_offending_line ()
{
  char *msg;
  int token_end;

  msg = savestring (shell_input_line);
  token_end = strlen (msg);
  while (token_end && msg[token_end - 1] == '\n')
    msg[--token_end] = '\0';

  parser_error (line_number, "`%s'", msg);
  free (msg);
}

/* Report a syntax error with line numbers, etc.
   Call here for recoverable errors.  If you have a message to print,
   then place it in MESSAGE, otherwise pass NULL and this will figure
   out an appropriate message for you. */
static void
report_syntax_error (message)
     char *message;
{
  char *msg;

  if (message)
    {
      parser_error (line_number, "%s", message);
      if (interactive && EOF_Reached)
    EOF_Reached = 0;
      last_command_exit_value = EX_USAGE;
      return;
    }

  /* If the line of input we're reading is not null, try to find the
     objectionable token.  First, try to figure out what token the
     parser's complaining about by looking at current_token. */
  if (current_token != 0 && EOF_Reached == 0 && (msg = error_token_from_token (current_token)))
    {
      parser_error (line_number, "syntax error near unexpected token `%s'", msg);
      free (msg);

      if (interactive == 0)
    print_offending_line ();

      last_command_exit_value = EX_USAGE;
      return;
    }

  /* If looking at the current token doesn't prove fruitful, try to find the
     offending token by analyzing the text of the input line near the current
     input line index and report what we find. */
  if (shell_input_line && *shell_input_line)
    {
      msg = error_token_from_text ();
      if (msg)
    {
      parser_error (line_number, "syntax error near `%s'", msg);
      free (msg);
    }

      /* If not interactive, print the line containing the error. */
      if (interactive == 0)
        print_offending_line ();
    }
  else
    {
      msg = EOF_Reached ? "syntax error: unexpected end of file" : "syntax error";
      parser_error (line_number, "%s", msg);
      /* When the shell is interactive, this file uses EOF_Reached
     only for error reporting.  Other mechanisms are used to
     decide whether or not to exit. */
      if (interactive && EOF_Reached)
    EOF_Reached = 0;
    }

  last_command_exit_value = EX_USAGE;
}

/* ??? Needed function. ??? We have to be able to discard the constructs
   created during parsing.  In the case of error, we want to return
   allocated objects to the memory pool.  In the case of no error, we want
   to throw away the information about where the allocated objects live.
   (dispose_command () will actually free the command.) */
static void
discard_parser_constructs (error_p)
     int error_p;
{
}

/************************************************
 *                      *
 *      EOF HANDLING            *
 *                      *
 ************************************************/

/* Do that silly `type "bye" to exit' stuff.  You know, "ignoreeof". */

/* A flag denoting whether or not ignoreeof is set. */
int ignoreeof = 0;

/* The number of times that we have encountered an EOF character without
   another character intervening.  When this gets above the limit, the
   shell terminates. */
int eof_encountered = 0;

/* The limit for eof_encountered. */
int eof_encountered_limit = 10;

/* If we have EOF as the only input unit, this user wants to leave
   the shell.  If the shell is not interactive, then just leave.
   Otherwise, if ignoreeof is set, and we haven't done this the
   required number of times in a row, print a message. */
static void
handle_eof_input_unit ()
{
  if (interactive)
    {
      /* shell.c may use this to decide whether or not to write out the
     history, among other things.  We use it only for error reporting
     in this file. */
      if (EOF_Reached)
    EOF_Reached = 0;

      /* If the user wants to "ignore" eof, then let her do so, kind of. */
      if (ignoreeof)
    {
      if (eof_encountered < eof_encountered_limit)
        {
          fprintf (stderr, "Use \"%s\" to leave the shell.\n",
               login_shell ? "logout" : "exit");
          eof_encountered++;
          /* Reset the parsing state. */
          last_read_token = current_token = '\n';
          /* Reset the prompt string to be $PS1. */
          prompt_string_pointer = (char **)NULL;
          prompt_again ();
          return;
        }
    }

      /* In this case EOF should exit the shell.  Do it now. */
      reset_parser ();
      exit_builtin ((WORD_LIST *)NULL);
    }
  else
    {
      /* We don't write history files, etc., for non-interactive shells. */
      EOF_Reached = 1;
    }
}

/************************************************
 *                      *
 *  STRING PARSING FUNCTIONS        *
 *                      *
 ************************************************/

/* It's very important that these two functions treat the characters
   between ( and ) identically. */

static WORD_LIST parse_string_error;

/* Take a string and run it through the shell parser, returning the
   resultant word list.  Used by compound array assignment. */
WORD_LIST *
parse_string_to_word_list (s, whom)
     char *s;
     const char *whom;
{
  WORD_LIST *wl;
  int tok, orig_current_token, orig_line_number, orig_input_terminator;
  int orig_line_count;
  int old_echo_input, old_expand_aliases;
#if defined (HISTORY)
  int old_remember_on_history, old_history_expansion_inhibited;
#endif

#if defined (HISTORY)
  old_remember_on_history = remember_on_history;
#  if defined (BANG_HISTORY)
  old_history_expansion_inhibited = history_expansion_inhibited;
#  endif
  bash_history_disable ();
#endif

  orig_line_number = line_number;
  orig_line_count = current_command_line_count;
  orig_input_terminator = shell_input_line_terminator;
  old_echo_input = echo_input_at_read;
  old_expand_aliases = expand_aliases;

  push_stream (1);
  last_read_token = WORD;       /* WORD to allow reserved words here */
  current_command_line_count = 0;
  echo_input_at_read = expand_aliases = 0;

  with_input_from_string (s, whom);
  wl = (WORD_LIST *)NULL;
  while ((tok = read_token (READ)) != yacc_EOF)
    {
      if (tok == '\n' && *bash_input.location.string == '\0')
    break;
      if (tok == '\n')      /* Allow newlines in compound assignments */
    continue;
      if (tok != WORD && tok != ASSIGNMENT_WORD)
    {
      line_number = orig_line_number + line_number - 1;
      orig_current_token = current_token;
      current_token = tok;
      yyerror ((char *)NULL);   /* does the right thing */
      current_token = orig_current_token;
      if (wl)
        dispose_words (wl);
      wl = &parse_string_error;
      break;
    }
      wl = make_word_list (yylval.word, wl);
    }
  
  last_read_token = '\n';
  pop_stream ();

#if defined (HISTORY)
  remember_on_history = old_remember_on_history;
#  if defined (BANG_HISTORY)
  history_expansion_inhibited = old_history_expansion_inhibited;
#  endif /* BANG_HISTORY */
#endif /* HISTORY */

  echo_input_at_read = old_echo_input;
  expand_aliases = old_expand_aliases;

  current_command_line_count = orig_line_count;
  shell_input_line_terminator = orig_input_terminator;

  if (wl == &parse_string_error)
    {
      last_command_exit_value = EXECUTION_FAILURE;
      if (interactive_shell == 0 && posixly_correct)
    jump_to_top_level (FORCE_EOF);
      else
    jump_to_top_level (DISCARD);
    }

  return (REVERSE_LIST (wl, WORD_LIST *));
}

static char *
parse_compound_assignment (retlenp)
     int *retlenp;
{
  WORD_LIST *wl, *rl;
  int tok, orig_line_number, orig_token_size;
  char *saved_token, *ret;

  saved_token = token;
  orig_token_size = token_buffer_size;
  orig_line_number = line_number;

  last_read_token = WORD;   /* WORD to allow reserved words here */

  token = (char *)NULL;
  token_buffer_size = 0;

  wl = (WORD_LIST *)NULL;   /* ( */
  while ((tok = read_token (READ)) != ')')
    {
      if (tok == '\n')          /* Allow newlines in compound assignments */
    continue;
      if (tok != WORD && tok != ASSIGNMENT_WORD)
    {
      current_token = tok;  /* for error reporting */
      if (tok == yacc_EOF)  /* ( */
        parser_error (orig_line_number, "unexpected EOF while looking for matching `)'");
      else
        yyerror ((char *)NULL); /* does the right thing */
      if (wl)
        dispose_words (wl);
      wl = &parse_string_error;
      break;
    }
      wl = make_word_list (yylval.word, wl);
    }

  FREE (token);
  token = saved_token;
  token_buffer_size = orig_token_size;

  if (wl == &parse_string_error)
    {
      last_command_exit_value = EXECUTION_FAILURE;
      last_read_token = '\n';   /* XXX */
      if (interactive_shell == 0 && posixly_correct)
    jump_to_top_level (FORCE_EOF);
      else
    jump_to_top_level (DISCARD);
    }

  last_read_token = WORD;
  if (wl)
    {
      rl = REVERSE_LIST (wl, WORD_LIST *);
      ret = string_list (rl);
      dispose_words (rl);
    }
  else
    ret = (char *)NULL;

  if (retlenp)
    *retlenp = (ret && *ret) ? strlen (ret) : 0;
  return ret;
}

/************************************************
 *                      *
 *  MULTIBYTE CHARACTER HANDLING        *
 *                      *
 ************************************************/

#if defined (HANDLE_MULTIBYTE)
static void
set_line_mbstate ()
{
  int i, previ, len;
  mbstate_t mbs, prevs;
  size_t mbclen;

  if (shell_input_line == NULL)
    return;
  len = strlen (shell_input_line);  /* XXX - shell_input_line_len ? */
  FREE (shell_input_line_property);
  shell_input_line_property = (char *)xmalloc (len + 1);

  memset (&prevs, '\0', sizeof (mbstate_t));
  for (i = previ = 0; i < len; i++)
    {
      mbs = prevs;

      if (shell_input_line[i] == EOF)
    {
      int j;
      for (j = i; j < len; j++)
        shell_input_line_property[j] = 1;
      break;
    }

      mbclen = mbrlen (shell_input_line + previ, i - previ + 1, &mbs);
      if (mbclen == 1 || mbclen == (size_t)-1)
    {
      mbclen = 1;
      previ = i + 1;
    }
      else if (mbclen == (size_t)-2)
        mbclen = 0;
      else if (mbclen > 1)
    {
      mbclen = 0;
      previ = i + 1;
      prevs = mbs;
    }
      else
    {
      /* mbrlen doesn't return any other values */
    }

      shell_input_line_property[i] = mbclen;
    }
}
#endif /* HANDLE_MULTIBYTE */

https://git.savannah.gnu.org/cgit/bash.git/tree/general.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/general.c used in: Step 1206
/* general.c -- Stuff that is used by all files. */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include "bashtypes.h"
#ifndef _MINIX
#  include <sys/param.h>
#endif
#include "posixstat.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "filecntl.h"
#include "bashansi.h"
#include <stdio.h>
#include "chartypes.h"
#include <errno.h>

#include "shell.h"
#include <tilde/tilde.h>

#if !defined (errno)
extern int errno;
#endif /* !errno */

extern int expand_aliases;
extern int interrupt_immediately;
extern int interactive_comments;
extern int check_hashed_filenames;
extern int source_uses_path;
extern int source_searches_cwd;

static char *bash_special_tilde_expansions __P((char *));
static int unquoted_tilde_word __P((const char *));
static void initialize_group_array __P((void));

/* A standard error message to use when getcwd() returns NULL. */
char *bash_getcwd_errstr = "getcwd: cannot access parent directories";

/* Do whatever is necessary to initialize `Posix mode'. */
void
posix_initialize (on)
     int on;
{
  /* Things that should be turned on when posix mode is enabled. */
  if (on != 0)
    {
      interactive_comments = source_uses_path = expand_aliases = 1;
    }

  /* Things that should be turned on when posix mode is disabled. */
  if (on == 0)
    {
      source_searches_cwd = 1;
      expand_aliases = interactive_shell;
    }
}

/* **************************************************************** */
/*                                  */
/*  Functions to convert to and from and display non-standard types */
/*                                  */
/* **************************************************************** */

#if defined (RLIMTYPE)
RLIMTYPE
string_to_rlimtype (s)
     char *s;
{
  RLIMTYPE ret;
  int neg;

  ret = 0;
  neg = 0;
  while (s && *s && whitespace (*s))
    s++;
  if (*s == '-' || *s == '+')
    {
      neg = *s == '-';
      s++;
    }
  for ( ; s && *s && DIGIT (*s); s++)
    ret = (ret * 10) + TODIGIT (*s);
  return (neg ? -ret : ret);
}

void
print_rlimtype (n, addnl)
     RLIMTYPE n;
     int addnl;
{
  char s[INT_STRLEN_BOUND (RLIMTYPE) + 1], *p;

  p = s + sizeof(s);
  *--p = '\0';

  if (n < 0)
    {
      do
    *--p = '0' - n % 10;
      while ((n /= 10) != 0);

      *--p = '-';
    }
  else
    {
      do
    *--p = '0' + n % 10;
      while ((n /= 10) != 0);
    }

  printf ("%s%s", p, addnl ? "\n" : "");
}
#endif /* RLIMTYPE */

/* **************************************************************** */
/*                                  */
/*             Input Validation Functions           */
/*                                  */
/* **************************************************************** */

/* Return non-zero if all of the characters in STRING are digits. */
int
all_digits (string)
     char *string;
{
  register char *s;

  for (s = string; *s; s++)
    if (DIGIT (*s) == 0)
      return (0);

  return (1);
}

/* Return non-zero if the characters pointed to by STRING constitute a
   valid number.  Stuff the converted number into RESULT if RESULT is
   not null. */
int
legal_number (string, result)
     char *string;
     intmax_t *result;
{
  intmax_t value;
  char *ep;

  if (result)
    *result = 0;

  errno = 0;
  value = strtoimax (string, &ep, 10);
  if (errno)
    return 0;   /* errno is set on overflow or underflow */

  /* Skip any trailing whitespace, since strtoimax does not. */
  while (whitespace (*ep))
    ep++;

  /* If *string is not '\0' but *ep is '\0' on return, the entire string
     is valid. */
  if (string && *string && *ep == '\0')
    {
      if (result)
    *result = value;
      /* The SunOS4 implementation of strtol() will happily ignore
     overflow conditions, so this cannot do overflow correctly
     on those systems. */
      return 1;
    }
    
  return (0);
}

/* Return 1 if this token is a legal shell `identifier'; that is, it consists
   solely of letters, digits, and underscores, and does not begin with a
   digit. */
int
legal_identifier (name)
     char *name;
{
  register char *s;
  unsigned char c;

  if (!name || !(c = *name) || (legal_variable_starter (c) == 0))
    return (0);

  for (s = name + 1; (c = *s) != 0; s++)
    {
      if (legal_variable_char (c) == 0)
    return (0);
    }
  return (1);
}

/* Make sure that WORD is a valid shell identifier, i.e.
   does not contain a dollar sign, nor is quoted in any way.  Nor
   does it consist of all digits.  If CHECK_WORD is non-zero,
   the word is checked to ensure that it consists of only letters,
   digits, and underscores. */
int
check_identifier (word, check_word)
     WORD_DESC *word;
     int check_word;
{
  if ((word->flags & (W_HASDOLLAR|W_QUOTED)) || all_digits (word->word))
    {
      internal_error ("`%s': not a valid identifier", word->word);
      return (0);
    }
  else if (check_word && legal_identifier (word->word) == 0)
    {
      internal_error ("`%s': not a valid identifier", word->word);
      return (0);
    }
  else
    return (1);
}

/* Returns non-zero if STRING is an assignment statement.  The returned value
   is the index of the `=' sign. */
int
assignment (string)
     const char *string;
{
  register unsigned char c;
  register int newi, indx;

  c = string[indx = 0];

  if (legal_variable_starter (c) == 0)
    return (0);

  while (c = string[indx])
    {
      /* The following is safe.  Note that '=' at the start of a word
     is not an assignment statement. */
      if (c == '=')
    return (indx);

#if defined (ARRAY_VARS)
      if (c == '[')
    {
      newi = skipsubscript (string, indx);
      if (string[newi++] != ']')
        return (0);
      return ((string[newi] == '=') ? newi : 0);
    }
#endif /* ARRAY_VARS */

      /* Variable names in assignment statements may contain only letters,
     digits, and `_'. */
      if (legal_variable_char (c) == 0)
    return (0);

      indx++;
    }
  return (0);
}

/* **************************************************************** */
/*                                  */
/*       Functions to manage files and file descriptors     */
/*                                  */
/* **************************************************************** */

/* A function to unset no-delay mode on a file descriptor.  Used in shell.c
   to unset it on the fd passed as stdin.  Should be called on stdin if
   readline gets an EAGAIN or EWOULDBLOCK when trying to read input. */

#if !defined (O_NDELAY)
#  if defined (FNDELAY)
#    define O_NDELAY FNDELAY
#  endif
#endif /* O_NDELAY */

/* Make sure no-delay mode is not set on file descriptor FD. */
int
sh_unset_nodelay_mode (fd)
     int fd;
{
  int flags, bflags;

  if ((flags = fcntl (fd, F_GETFL, 0)) < 0)
    return -1;

  bflags = 0;

  /* This is defined to O_NDELAY in filecntl.h if O_NONBLOCK is not present
     and O_NDELAY is defined. */
#ifdef O_NONBLOCK
  bflags |= O_NONBLOCK;
#endif

#ifdef O_NDELAY
  bflags |= O_NDELAY;
#endif

  if (flags & bflags)
    {
      flags &= ~bflags;
      return (fcntl (fd, F_SETFL, flags));
    }

  return 0;
}

/* Return 1 if file descriptor FD is valid; 0 otherwise. */
int
sh_validfd (fd)
     int fd;
{
  return (fcntl (fd, F_GETFD, 0) >= 0);
}

/* There is a bug in the NeXT 2.1 rlogind that causes opens
   of /dev/tty to fail. */

#if defined (__BEOS__)
/* On BeOS, opening in non-blocking mode exposes a bug in BeOS, so turn it
   into a no-op.  This should probably go away in the future. */
#  undef O_NONBLOCK
#  define O_NONBLOCK 0
#endif /* __BEOS__ */

void
check_dev_tty ()
{
  int tty_fd;
  char *tty;

  tty_fd = open ("/dev/tty", O_RDWR|O_NONBLOCK);

  if (tty_fd < 0)
    {
      tty = (char *)ttyname (fileno (stdin));
      if (tty == 0)
    return;
      tty_fd = open (tty, O_RDWR|O_NONBLOCK);
    }
  close (tty_fd);
}

/* Return 1 if PATH1 and PATH2 are the same file.  This is kind of
   expensive.  If non-NULL STP1 and STP2 point to stat structures
   corresponding to PATH1 and PATH2, respectively. */
int
same_file (path1, path2, stp1, stp2)
     char *path1, *path2;
     struct stat *stp1, *stp2;
{
  struct stat st1, st2;

  if (stp1 == NULL)
    {
      if (stat (path1, &st1) != 0)
    return (0);
      stp1 = &st1;
    }

  if (stp2 == NULL)
    {
      if (stat (path2, &st2) != 0)
    return (0);
      stp2 = &st2;
    }

  return ((stp1->st_dev == stp2->st_dev) && (stp1->st_ino == stp2->st_ino));
}

/* Move FD to a number close to the maximum number of file descriptors
   allowed in the shell process, to avoid the user stepping on it with
   redirection and causing us extra work.  If CHECK_NEW is non-zero,
   we check whether or not the file descriptors are in use before
   duplicating FD onto them.  MAXFD says where to start checking the
   file descriptors.  If it's less than 20, we get the maximum value
   available from getdtablesize(2). */
int
move_to_high_fd (fd, check_new, maxfd)
     int fd, check_new, maxfd;
{
  int script_fd, nfds, ignore;

  if (maxfd < 20)
    {
      nfds = getdtablesize ();
      if (nfds <= 0)
    nfds = 20;
      if (nfds > HIGH_FD_MAX)
    nfds = HIGH_FD_MAX;     /* reasonable maximum */
    }
  else
    nfds = maxfd;

  for (nfds--; check_new && nfds > 3; nfds--)
    if (fcntl (nfds, F_GETFD, &ignore) == -1)
      break;

  if (nfds > 3 && fd != nfds && (script_fd = dup2 (fd, nfds)) != -1)
    {
      if (check_new == 0 || fd != fileno (stderr))  /* don't close stderr */
    close (fd);
      return (script_fd);
    }

  /* OK, we didn't find one less than our artificial maximum; return the
     original file descriptor. */
  return (fd);
}
 
/* Return non-zero if the characters from SAMPLE are not all valid
   characters to be found in the first line of a shell script.  We
   check up to the first newline, or SAMPLE_LEN, whichever comes first.
   All of the characters must be printable or whitespace. */

int
check_binary_file (sample, sample_len)
     char *sample;
     int sample_len;
{
  register int i;
  unsigned char c;

  for (i = 0; i < sample_len; i++)
    {
      c = sample[i];
      if (c == '\n')
    return (0);

      if (ISSPACE (c) == 0 && ISPRINT (c) == 0)
    return (1);
    }

  return (0);
}

/* **************************************************************** */
/*                                  */
/*          Functions to manipulate pathnames           */
/*                                  */
/* **************************************************************** */

/* Turn STRING (a pathname) into an absolute pathname, assuming that
   DOT_PATH contains the symbolic location of `.'.  This always
   returns a new string, even if STRING was an absolute pathname to
   begin with. */
char *
make_absolute (string, dot_path)
     char *string, *dot_path;
{
  char *result;

  if (dot_path == 0 || ABSPATH(string))
    result = savestring (string);
  else
    result = sh_makepath (dot_path, string, 0);

  return (result);
}

/* Return 1 if STRING contains an absolute pathname, else 0.  Used by `cd'
   to decide whether or not to look up a directory name in $CDPATH. */
int
absolute_pathname (string)
     const char *string;
{
  if (string == 0 || *string == '\0')
    return (0);

  if (ABSPATH(string))
    return (1);

  if (string[0] == '.' && PATHSEP(string[1]))   /* . and ./ */
    return (1);

  if (string[0] == '.' && string[1] == '.' && PATHSEP(string[2]))   /* .. and ../ */
    return (1);

  return (0);
}

/* Return 1 if STRING is an absolute program name; it is absolute if it
   contains any slashes.  This is used to decide whether or not to look
   up through $PATH. */
int
absolute_program (string)
     const char *string;
{
  return ((char *)xstrchr (string, '/') != (char *)NULL);
}

/* Return the `basename' of the pathname in STRING (the stuff after the
   last '/').  If STRING is not a full pathname, simply return it. */
char *
base_pathname (string)
     char *string;
{
  char *p;

  if (absolute_pathname (string) == 0)
    return (string);

  p = (char *)strrchr (string, '/');
  return (p ? ++p : string);
}

/* Return the full pathname of FILE.  Easy.  Filenames that begin
   with a '/' are returned as themselves.  Other filenames have
   the current working directory prepended.  A new string is
   returned in either case. */
char *
full_pathname (file)
     char *file;
{
  char *ret;

  file = (*file == '~') ? bash_tilde_expand (file, 0) : savestring (file);

  if (ABSPATH(file))
    return (file);

  ret = sh_makepath ((char *)NULL, file, (MP_DOCWD|MP_RMDOT));
  free (file);

  return (ret);
}

/* A slightly related function.  Get the prettiest name of this
   directory possible. */
static char tdir[PATH_MAX];

/* Return a pretty pathname.  If the first part of the pathname is
   the same as $HOME, then replace that with `~'.  */
char *
polite_directory_format (name)
     char *name;
{
  char *home;
  int l;

  home = get_string_value ("HOME");
  l = home ? strlen (home) : 0;
  if (l > 1 && strncmp (home, name, l) == 0 && (!name[l] || name[l] == '/'))
    {
      strncpy (tdir + 1, name + l, sizeof(tdir) - 2);
      tdir[0] = '~';
      tdir[sizeof(tdir) - 1] = '\0';
      return (tdir);
    }
  else
    return (name);
}

/* Given a string containing units of information separated by colons,
   return the next one pointed to by (P_INDEX), or NULL if there are no more.
   Advance (P_INDEX) to the character after the colon. */
char *
extract_colon_unit (string, p_index)
     char *string;
     int *p_index;
{
  int i, start, len;
  char *value;

  if (string == 0)
    return (string);

  len = strlen (string);
  if (*p_index >= len)
    return ((char *)NULL);

  i = *p_index;

  /* Each call to this routine leaves the index pointing at a colon if
     there is more to the path.  If I is > 0, then increment past the
     `:'.  If I is 0, then the path has a leading colon.  Trailing colons
     are handled OK by the `else' part of the if statement; an empty
     string is returned in that case. */
  if (i && string[i] == ':')
    i++;

  for (start = i; string[i] && string[i] != ':'; i++)
    ;

  *p_index = i;

  if (i == start)
    {
      if (string[i])
    (*p_index)++;
      /* Return "" in the case of a trailing `:'. */
      value = (char *)xmalloc (1);
      value[0] = '\0';
    }
  else
    value = substring (string, start, i);

  return (value);
}

/* **************************************************************** */
/*                                  */
/*          Tilde Initialization and Expansion          */
/*                                  */
/* **************************************************************** */

#if defined (PUSHD_AND_POPD)
extern char *get_dirstack_from_string __P((char *));
#endif

static char **bash_tilde_prefixes;
static char **bash_tilde_suffixes;

/* If tilde_expand hasn't been able to expand the text, perhaps it
   is a special shell expansion.  This function is installed as the
   tilde_expansion_preexpansion_hook.  It knows how to expand ~- and ~+.
   If PUSHD_AND_POPD is defined, ~[+-]N expands to directories from the
   directory stack. */
static char *
bash_special_tilde_expansions (text)
     char *text;
{
  char *result;

  result = (char *)NULL;

  if (text[0] == '+' && text[1] == '\0')
    result = get_string_value ("PWD");
  else if (text[0] == '-' && text[1] == '\0')
    result = get_string_value ("OLDPWD");
#if defined (PUSHD_AND_POPD)
  else if (DIGIT (*text) || ((*text == '+' || *text == '-') && DIGIT (text[1])))
    result = get_dirstack_from_string (text);
#endif

  return (result ? savestring (result) : (char *)NULL);
}

/* Initialize the tilde expander.  In Bash, we handle `~-' and `~+', as
   well as handling special tilde prefixes; `:~" and `=~' are indications
   that we should do tilde expansion. */
void
tilde_initialize ()
{
  static int times_called = 0;

  /* Tell the tilde expander that we want a crack first. */
  tilde_expansion_preexpansion_hook = bash_special_tilde_expansions;

  /* Tell the tilde expander about special strings which start a tilde
     expansion, and the special strings that end one.  Only do this once.
     tilde_initialize () is called from within bashline_reinitialize (). */
  if (times_called++ == 0)
    {
      bash_tilde_prefixes = strvec_create (3);
      bash_tilde_prefixes[0] = "=~";
      bash_tilde_prefixes[1] = ":~";
      bash_tilde_prefixes[2] = (char *)NULL;

      tilde_additional_prefixes = bash_tilde_prefixes;

      bash_tilde_suffixes = strvec_create (3);
      bash_tilde_suffixes[0] = ":";
      bash_tilde_suffixes[1] = "=~";    /* XXX - ?? */
      bash_tilde_suffixes[2] = (char *)NULL;

      tilde_additional_suffixes = bash_tilde_suffixes;
    }
}

/* POSIX.2, 3.6.1:  A tilde-prefix consists of an unquoted tilde character
   at the beginning of the word, followed by all of the characters preceding
   the first unquoted slash in the word, or all the characters in the word
   if there is no slash...If none of the characters in the tilde-prefix are
   quoted, the characters in the tilde-prefix following the tilde shell be
   treated as a possible login name. */

#define TILDE_END(c)    ((c) == '\0' || (c) == '/' || (c) == ':')

static int
unquoted_tilde_word (s)
     const char *s;
{
  const char *r;

  for (r = s; TILDE_END(*r) == 0; r++)
    {
      switch (*r)
    {
    case '\\':
    case '\'':
    case '"':
      return 0;
    }
    }
  return 1;
}

/* Tilde-expand S by running it through the tilde expansion library.
   ASSIGN_P is 1 if this is a variable assignment, so the alternate
   tilde prefixes should be enabled (`=~' and `:~', see above). */
char *
bash_tilde_expand (s, assign_p)
     const char *s;
     int assign_p;
{
  int old_immed, r;
  char *ret;

  old_immed = interrupt_immediately;
  interrupt_immediately = 1;
  tilde_additional_prefixes = assign_p ? bash_tilde_prefixes : (char **)0;
  r = (*s == '~') ? unquoted_tilde_word (s) : 1;
  ret = r ? tilde_expand (s) : savestring (s);
  interrupt_immediately = old_immed;
  return (ret);
}

/* **************************************************************** */
/*                                  */
/*    Functions to manipulate and search the group list     */
/*                                  */
/* **************************************************************** */

static int ngroups, maxgroups;

/* The set of groups that this user is a member of. */
static GETGROUPS_T *group_array = (GETGROUPS_T *)NULL;

#if !defined (NOGROUP)
#  define NOGROUP (gid_t) -1
#endif

static void
initialize_group_array ()
{
  register int i;

  if (maxgroups == 0)
    maxgroups = getmaxgroups ();

  ngroups = 0;
  group_array = (GETGROUPS_T *)xrealloc (group_array, maxgroups * sizeof (GETGROUPS_T));

#if defined (HAVE_GETGROUPS)
  ngroups = getgroups (maxgroups, group_array);
#endif

  /* If getgroups returns nothing, or the OS does not support getgroups(),
     make sure the groups array includes at least the current gid. */
  if (ngroups == 0)
    {
      group_array[0] = current_user.gid;
      ngroups = 1;
    }

  /* If the primary group is not in the groups array, add it as group_array[0]
     and shuffle everything else up 1, if there's room. */
  for (i = 0; i < ngroups; i++)
    if (current_user.gid == (gid_t)group_array[i])
      break;
  if (i == ngroups && ngroups < maxgroups)
    {
      for (i = ngroups; i > 0; i--)
    group_array[i] = group_array[i - 1];
      group_array[0] = current_user.gid;
      ngroups++;
    }

  /* If the primary group is not group_array[0], swap group_array[0] and
     whatever the current group is.  The vast majority of systems should
     not need this; a notable exception is Linux. */
  if (group_array[0] != current_user.gid)
    {
      for (i = 0; i < ngroups; i++)
    if (group_array[i] == current_user.gid)
      break;
      if (i < ngroups)
    {
      group_array[i] = group_array[0];
      group_array[0] = current_user.gid;
    }
    }
}

/* Return non-zero if GID is one that we have in our groups list. */
int
#if defined (__STDC__) || defined ( _MINIX)
group_member (gid_t gid)
#else
group_member (gid)
     gid_t gid;
#endif /* !__STDC__ && !_MINIX */
{
#if defined (HAVE_GETGROUPS)
  register int i;
#endif

  /* Short-circuit if possible, maybe saving a call to getgroups(). */
  if (gid == current_user.gid || gid == current_user.egid)
    return (1);

#if defined (HAVE_GETGROUPS)
  if (ngroups == 0)
    initialize_group_array ();

  /* In case of error, the user loses. */
  if (ngroups <= 0)
    return (0);

  /* Search through the list looking for GID. */
  for (i = 0; i < ngroups; i++)
    if (gid == (gid_t)group_array[i])
      return (1);
#endif

  return (0);
}

char **
get_group_list (ngp)
     int *ngp;
{
  static char **group_vector = (char **)NULL;
  register int i;

  if (group_vector)
    {
      if (ngp)
    *ngp = ngroups;
      return group_vector;
    }

  if (ngroups == 0)
    initialize_group_array ();

  if (ngroups <= 0)
    {
      if (ngp)
    *ngp = 0;
      return (char **)NULL;
    }

  group_vector = strvec_create (ngroups);
  for (i = 0; i < ngroups; i++)
    group_vector[i] = itos (group_array[i]);

  if (ngp)
    *ngp = ngroups;
  return group_vector;
}

int *
get_group_array (ngp)
     int *ngp;
{
  int i;
  static int *group_iarray = (int *)NULL;

  if (group_iarray)
    {
      if (ngp)
    *ngp = ngroups;
      return (group_iarray);
    }

  if (ngroups == 0)
    initialize_group_array ();    

  if (ngroups <= 0)
    {
      if (ngp)
    *ngp = 0;
      return (int *)NULL;
    }

  group_iarray = (int *)xmalloc (ngroups * sizeof (int));
  for (i = 0; i < ngroups; i++)
    group_iarray[i] = (int)group_array[i];

  if (ngp)
    *ngp = ngroups;
  return group_iarray;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/make_cmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/make_cmd.c used in: Step 1207
/* make_cmd.c -- Functions for making instances of the various
   parser constructs. */

/* Copyright (C) 1989-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include <stdio.h>
#include "bashtypes.h"
#ifndef _MINIX
#  include <sys/file.h>
#endif
#include "filecntl.h"
#include "bashansi.h"
#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "syntax.h"
#include "command.h"
#include "general.h"
#include "error.h"
#include "flags.h"
#include "make_cmd.h"
#include "dispose_cmd.h"
#include "variables.h"
#include "subst.h"
#include "input.h"
#include "ocache.h"
#include "externs.h"

#if defined (JOB_CONTROL)
#include "jobs.h"
#endif

#include "shmbutil.h"

extern int line_number, current_command_line_count;
extern int last_command_exit_value;

/* Object caching */
sh_obj_cache_t wdcache = {0, 0, 0};
sh_obj_cache_t wlcache = {0, 0, 0};

#define WDCACHESIZE 60
#define WLCACHESIZE 60

static COMMAND *make_for_or_select __P((enum command_type, WORD_DESC *, WORD_LIST *, COMMAND *));
#if defined (ARITH_FOR_COMMAND)
static WORD_LIST *make_arith_for_expr __P((char *));
#endif
static COMMAND *make_until_or_while __P((enum command_type, COMMAND *, COMMAND *));

void
cmd_init ()
{
  ocache_create (wdcache, WORD_DESC, WDCACHESIZE);
  ocache_create (wlcache, WORD_LIST, WLCACHESIZE);
}

WORD_DESC *
make_bare_word (string)
     const char *string;
{
  WORD_DESC *temp;
#if 0
  temp = (WORD_DESC *)xmalloc (sizeof (WORD_DESC));
#else
  ocache_alloc (wdcache, WORD_DESC, temp);
#endif

  if (*string)
    temp->word = savestring (string);
  else
    {
      temp->word = (char *)xmalloc (1);
      temp->word[0] = '\0';
    }

  temp->flags = 0;
  return (temp);
}

WORD_DESC *
make_word_flags (w, string)
     WORD_DESC *w;
     const char *string;
{
  register int i;
  size_t slen;
  DECLARE_MBSTATE;

  i = 0;
  slen = strlen (string);
  while (i < slen)
    {
      switch (string[i])
    {
    case '$':
      w->flags |= W_HASDOLLAR;
      break;
    case '\\':
      break;    /* continue the loop */
    case '\'':
    case '`':
    case '"':
      w->flags |= W_QUOTED;
      break;
    }

      ADVANCE_CHAR (string, slen, i);
    }

  return (w);
}

WORD_DESC *
make_word (string)
     const char *string;
{
  WORD_DESC *temp;

  temp = make_bare_word (string);
  return (make_word_flags (temp, string));
}

WORD_DESC *
make_word_from_token (token)
     int token;
{
  char tokenizer[2];

  tokenizer[0] = token;
  tokenizer[1] = '\0';

  return (make_word (tokenizer));
}

WORD_LIST *
make_word_list (word, wlink)
     WORD_DESC *word;
     WORD_LIST *wlink;
{
  WORD_LIST *temp;

#if 0
  temp = (WORD_LIST *)xmalloc (sizeof (WORD_LIST));
#else
  ocache_alloc (wlcache, WORD_LIST, temp);
#endif
  temp->word = word;
  temp->next = wlink;
  return (temp);
}

COMMAND *
make_command (type, pointer)
     enum command_type type;
     SIMPLE_COM *pointer;
{
  COMMAND *temp;

  temp = (COMMAND *)xmalloc (sizeof (COMMAND));
  temp->type = type;
  temp->value.Simple = pointer;
  temp->value.Simple->flags = temp->flags = 0;
  temp->redirects = (REDIRECT *)NULL;
  return (temp);
}

COMMAND *
command_connect (com1, com2, connector)
     COMMAND *com1, *com2;
     int connector;
{
  CONNECTION *temp;

  temp = (CONNECTION *)xmalloc (sizeof (CONNECTION));
  temp->connector = connector;
  temp->first = com1;
  temp->second = com2;
  return (make_command (cm_connection, (SIMPLE_COM *)temp));
}

static COMMAND *
make_for_or_select (type, name, map_list, action)
     enum command_type type;
     WORD_DESC *name;
     WORD_LIST *map_list;
     COMMAND *action;
{
  FOR_COM *temp;

  temp = (FOR_COM *)xmalloc (sizeof (FOR_COM));
  temp->flags = 0;
  temp->name = name;
  temp->map_list = map_list;
  temp->action = action;
  return (make_command (type, (SIMPLE_COM *)temp));
}

COMMAND *
make_for_command (name, map_list, action)
     WORD_DESC *name;
     WORD_LIST *map_list;
     COMMAND *action;
{
  return (make_for_or_select (cm_for, name, map_list, action));
}

COMMAND *
make_select_command (name, map_list, action)
     WORD_DESC *name;
     WORD_LIST *map_list;
     COMMAND *action;
{
#if defined (SELECT_COMMAND)
  return (make_for_or_select (cm_select, name, map_list, action));
#else
  last_command_exit_value = 2;
  return ((COMMAND *)NULL);
#endif
}

#if defined (ARITH_FOR_COMMAND)
static WORD_LIST *
make_arith_for_expr (s)
     char *s;
{
  WORD_LIST *result;

  if (s == 0 || *s == '\0')
    return ((WORD_LIST *)NULL);
  result = make_word_list (make_word (s), (WORD_LIST *)NULL);
  return result;
}
#endif

COMMAND *
make_arith_for_command (exprs, action, lineno)
     WORD_LIST *exprs;
     COMMAND *action;
     int lineno;
{
#if defined (ARITH_FOR_COMMAND)
  ARITH_FOR_COM *temp;
  WORD_LIST *init, *test, *step;
  char *s, *t, *start;
  int nsemi;

  init = test = step = (WORD_LIST *)NULL;
  /* Parse the string into the three component sub-expressions. */
  start = t = s = exprs->word->word;
  for (nsemi = 0; ;)
    {
      /* skip whitespace at the start of each sub-expression. */
      while (whitespace (*s))
    s++;
      start = s;
      /* skip to the semicolon or EOS */
      while (*s && *s != ';')
    s++;

      t = (s > start) ? substring (start, 0, s - start) : (char *)NULL;

      nsemi++;
      switch (nsemi)
    {
    case 1:
      init = make_arith_for_expr (t);
      break;
    case 2:
      test = make_arith_for_expr (t);
      break;
    case 3:
      step = make_arith_for_expr (t);
      break;
    }

      FREE (t);
      if (*s == '\0')
    break;
      s++;  /* skip over semicolon */
    }

  if (nsemi != 3)
    {
      if (nsemi < 3)
    parser_error (lineno, "syntax error: arithmetic expression required");
      else
    parser_error (lineno, "syntax error: `;' unexpected");
      parser_error (lineno, "syntax error: `((%s))'", exprs->word->word);
      last_command_exit_value = 2;
      return ((COMMAND *)NULL);
    }

  temp = (ARITH_FOR_COM *)xmalloc (sizeof (ARITH_FOR_COM));
  temp->flags = 0;
  temp->line = lineno;
  temp->init = init ? init : make_arith_for_expr ("1");
  temp->test = test ? test : make_arith_for_expr ("1");
  temp->step = step ? step : make_arith_for_expr ("1");
  temp->action = action;

  return (make_command (cm_arith_for, (SIMPLE_COM *)temp));
#else
  last_command_exit_value = 2;
  return ((COMMAND *)NULL);
#endif /* ARITH_FOR_COMMAND */
}

COMMAND *
make_group_command (command)
     COMMAND *command;
{
  GROUP_COM *temp;

  temp = (GROUP_COM *)xmalloc (sizeof (GROUP_COM));
  temp->command = command;
  return (make_command (cm_group, (SIMPLE_COM *)temp));
}

COMMAND *
make_case_command (word, clauses)
     WORD_DESC *word;
     PATTERN_LIST *clauses;
{
  CASE_COM *temp;

  temp = (CASE_COM *)xmalloc (sizeof (CASE_COM));
  temp->flags = 0;
  temp->word = word;
  temp->clauses = REVERSE_LIST (clauses, PATTERN_LIST *);
  return (make_command (cm_case, (SIMPLE_COM *)temp));
}

PATTERN_LIST *
make_pattern_list (patterns, action)
     WORD_LIST *patterns;
     COMMAND *action;
{
  PATTERN_LIST *temp;

  temp = (PATTERN_LIST *)xmalloc (sizeof (PATTERN_LIST));
  temp->patterns = REVERSE_LIST (patterns, WORD_LIST *);
  temp->action = action;
  temp->next = NULL;
  return (temp);
}

COMMAND *
make_if_command (test, true_case, false_case)
     COMMAND *test, *true_case, *false_case;
{
  IF_COM *temp;

  temp = (IF_COM *)xmalloc (sizeof (IF_COM));
  temp->flags = 0;
  temp->test = test;
  temp->true_case = true_case;
  temp->false_case = false_case;
  return (make_command (cm_if, (SIMPLE_COM *)temp));
}

static COMMAND *
make_until_or_while (which, test, action)
     enum command_type which;
     COMMAND *test, *action;
{
  WHILE_COM *temp;

  temp = (WHILE_COM *)xmalloc (sizeof (WHILE_COM));
  temp->flags = 0;
  temp->test = test;
  temp->action = action;
  return (make_command (which, (SIMPLE_COM *)temp));
}

COMMAND *
make_while_command (test, action)
     COMMAND *test, *action;
{
  return (make_until_or_while (cm_while, test, action));
}

COMMAND *
make_until_command (test, action)
     COMMAND *test, *action;
{
  return (make_until_or_while (cm_until, test, action));
}

COMMAND *
make_arith_command (exp)
     WORD_LIST *exp;
{
#if defined (DPAREN_ARITHMETIC)
  COMMAND *command;
  ARITH_COM *temp;

  command = (COMMAND *)xmalloc (sizeof (COMMAND));
  command->value.Arith = temp = (ARITH_COM *)xmalloc (sizeof (ARITH_COM));

  temp->flags = 0;
  temp->line = line_number;
  temp->exp = exp;

  command->type = cm_arith;
  command->redirects = (REDIRECT *)NULL;
  command->flags = 0;

  return (command);
#else
  last_command_exit_value = 2;
  return ((COMMAND *)NULL);
#endif
}

#if defined (COND_COMMAND)
struct cond_com *
make_cond_node (type, op, left, right)
     int type;
     WORD_DESC *op;
     struct cond_com *left, *right;
{
  COND_COM *temp;

  temp = (COND_COM *)xmalloc (sizeof (COND_COM));
  temp->flags = 0;
  temp->line = line_number;
  temp->type = type;
  temp->op = op;
  temp->left = left;
  temp->right = right;

  return (temp);
}
#endif

COMMAND *
make_cond_command (cond_node)
     COND_COM *cond_node;
{
#if defined (COND_COMMAND)
  COMMAND *command;

  command = (COMMAND *)xmalloc (sizeof (COMMAND));
  command->value.Cond = cond_node;

  command->type = cm_cond;
  command->redirects = (REDIRECT *)NULL;
  command->flags = 0;
  command->line = cond_node ? cond_node->line : 0;

  return (command);
#else
  last_command_exit_value = 2;
  return ((COMMAND *)NULL);
#endif
}

COMMAND *
make_bare_simple_command ()
{
  COMMAND *command;
  SIMPLE_COM *temp;

  command = (COMMAND *)xmalloc (sizeof (COMMAND));
  command->value.Simple = temp = (SIMPLE_COM *)xmalloc (sizeof (SIMPLE_COM));

  temp->flags = 0;
  temp->line = line_number;
  temp->words = (WORD_LIST *)NULL;
  temp->redirects = (REDIRECT *)NULL;

  command->type = cm_simple;
  command->redirects = (REDIRECT *)NULL;
  command->flags = 0;

  return (command);
}

/* Return a command which is the connection of the word or redirection
   in ELEMENT, and the command * or NULL in COMMAND. */
COMMAND *
make_simple_command (element, command)
     ELEMENT element;
     COMMAND *command;
{
  /* If we are starting from scratch, then make the initial command
     structure.  Also note that we have to fill in all the slots, since
     malloc doesn't return zeroed space. */
  if (!command)
    command = make_bare_simple_command ();

  if (element.word)
    command->value.Simple->words = make_word_list (element.word, command->value.Simple->words);
  else
    {
      REDIRECT *r = element.redirect;
      /* Due to the way <> is implemented, there may be more than a single
     redirection in element.redirect.  We just follow the chain as far
     as it goes, and hook onto the end. */
      while (r->next)
    r = r->next;
      r->next = command->value.Simple->redirects;
      command->value.Simple->redirects = element.redirect;
    }
  return (command);
}

/* Because we are Bourne compatible, we read the input for this
   << or <<- redirection now, from wherever input is coming from.
   We store the input read into a WORD_DESC.  Replace the text of
   the redirectee.word with the new input text.  If <<- is on,
   then remove leading TABS from each line. */
void
make_here_document (temp)
     REDIRECT *temp;
{
  int kill_leading, redir_len;
  char *redir_word, *document, *full_line;
  int document_index, document_size, delim_unquoted;

  if (temp->instruction != r_deblank_reading_until &&
      temp->instruction != r_reading_until)
    {
      internal_error ("make_here_document: bad instruction type %d", temp->instruction);
      return;
    }

  kill_leading = temp->instruction == r_deblank_reading_until;

  document = (char *)NULL;
  document_index = document_size = 0;

  /* Quote removal is the only expansion performed on the delimiter
     for here documents, making it an extremely special case. */
  redir_word = string_quote_removal (temp->redirectee.filename->word, 0);

  /* redirection_expand will return NULL if the expansion results in
     multiple words or no words.  Check for that here, and just abort
     this here document if it does. */
  if (redir_word)
    redir_len = strlen (redir_word);
  else
    {
      temp->here_doc_eof = (char *)xmalloc (1);
      temp->here_doc_eof[0] = '\0';
      goto document_done;
    }

  free (temp->redirectee.filename->word);
  temp->here_doc_eof = redir_word;

  /* Read lines from wherever lines are coming from.
     For each line read, if kill_leading, then kill the
     leading tab characters.
     If the line matches redir_word exactly, then we have
     manufactured the document.  Otherwise, add the line to the
     list of lines in the document. */

  /* If the here-document delimiter was quoted, the lines should
     be read verbatim from the input.  If it was not quoted, we
     need to perform backslash-quoted newline removal. */
  delim_unquoted = (temp->redirectee.filename->flags & W_QUOTED) == 0;
  while (full_line = read_secondary_line (delim_unquoted))
    {
      register char *line;
      int len;

      line = full_line;
      line_number++;

      if (kill_leading && *line)
    {
      /* Hack:  To be compatible with some Bourne shells, we
         check the word before stripping the whitespace.  This
         is a hack, though. */
      if (STREQN (line, redir_word, redir_len) && line[redir_len] == '\n')
        goto document_done;

      while (*line == '\t')
        line++;
    }

      if (*line == 0)
    continue;

      if (STREQN (line, redir_word, redir_len) && line[redir_len] == '\n')
    goto document_done;

      len = strlen (line);
      if (len + document_index >= document_size)
    {
      document_size = document_size ? 2 * (document_size + len) : len + 2;
      document = (char *)xrealloc (document, document_size);
    }

      /* len is guaranteed to be > 0 because of the check for line
     being an empty string before the call to strlen. */
      FASTCOPY (line, document + document_index, len);
      document_index += len;
    }

document_done:
  if (document)
    document[document_index] = '\0';
  else
    {
      document = (char *)xmalloc (1);
      document[0] = '\0';
    }
  temp->redirectee.filename->word = document;
}

/* Generate a REDIRECT from SOURCE, DEST, and INSTRUCTION.
   INSTRUCTION is the instruction type, SOURCE is a file descriptor,
   and DEST is a file descriptor or a WORD_DESC *. */
REDIRECT *
make_redirection (source, instruction, dest_and_filename)
     int source;
     enum r_instruction instruction;
     REDIRECTEE dest_and_filename;
{
  REDIRECT *temp;
  WORD_DESC *w;
  int wlen;
  intmax_t lfd;

  temp = (REDIRECT *)xmalloc (sizeof (REDIRECT));

  /* First do the common cases. */
  temp->redirector = source;
  temp->redirectee = dest_and_filename;
  temp->instruction = instruction;
  temp->flags = 0;
  temp->next = (REDIRECT *)NULL;

  switch (instruction)
    {

    case r_output_direction:        /* >foo */
    case r_output_force:        /* >| foo */
    case r_err_and_out:         /* command &>filename */
      temp->flags = O_TRUNC | O_WRONLY | O_CREAT;
      break;

    case r_appending_to:        /* >>foo */
      temp->flags = O_APPEND | O_WRONLY | O_CREAT;
      break;

    case r_input_direction:     /* <foo */
    case r_inputa_direction:        /* foo & makes this. */
      temp->flags = O_RDONLY;
      break;

    case r_input_output:        /* <>foo */
      temp->flags = O_RDWR | O_CREAT;
      break;

    case r_deblank_reading_until:   /* <<-foo */
    case r_reading_until:       /* << foo */
    case r_reading_string:      /* <<< foo */
    case r_close_this:          /* <&- */
    case r_duplicating_input:       /* 1<&2 */
    case r_duplicating_output:      /* 1>&2 */
      break;

    /* the parser doesn't pass these. */
    case r_move_input:          /* 1<&2- */
    case r_move_output:         /* 1>&2- */
    case r_move_input_word:     /* 1<&$foo- */
    case r_move_output_word:        /* 1>&$foo- */
      break;

    /* The way the lexer works we have to do this here. */
    case r_duplicating_input_word:  /* 1<&$foo */
    case r_duplicating_output_word: /* 1>&$foo */
      w = dest_and_filename.filename;
      wlen = strlen (w->word) - 1;
      if (w->word[wlen] == '-')     /* Yuck */
        {
          w->word[wlen] = '\0';
      if (all_digits (w->word) && legal_number (w->word, &lfd) && lfd == (int)lfd)
        {
          dispose_word (w);
          temp->instruction = (instruction == r_duplicating_input_word) ? r_move_input : r_move_output;
          temp->redirectee.dest = lfd;
        }
      else
        temp->instruction = (instruction == r_duplicating_input_word) ? r_move_input_word : r_move_output_word;
        }
          
      break;

    default:
      programming_error ("make_redirection: redirection instruction `%d' out of range", instruction);
      abort ();
      break;
    }
  return (temp);
}

COMMAND *
make_function_def (name, command, lineno, lstart)
     WORD_DESC *name;
     COMMAND *command;
     int lineno, lstart;
{
  FUNCTION_DEF *temp;

  temp = (FUNCTION_DEF *)xmalloc (sizeof (FUNCTION_DEF));
  temp->command = command;
  temp->name = name;
  temp->line = lineno;
  temp->flags = 0;
  command->line = lstart;
  return (make_command (cm_function_def, (SIMPLE_COM *)temp));
}

COMMAND *
make_subshell_command (command)
     COMMAND *command;
{
  SUBSHELL_COM *temp;

  temp = (SUBSHELL_COM *)xmalloc (sizeof (SUBSHELL_COM));
  temp->command = command;
  temp->flags = CMD_WANT_SUBSHELL;
  return (make_command (cm_subshell, (SIMPLE_COM *)temp));
}

/* Reverse the word list and redirection list in the simple command
   has just been parsed.  It seems simpler to do this here the one
   time then by any other method that I can think of. */
COMMAND *
clean_simple_command (command)
     COMMAND *command;
{
  if (command->type != cm_simple)
    command_error ("clean_simple_command", CMDERR_BADTYPE, command->type, 0);
  else
    {
      command->value.Simple->words =
    REVERSE_LIST (command->value.Simple->words, WORD_LIST *);
      command->value.Simple->redirects =
    REVERSE_LIST (command->value.Simple->redirects, REDIRECT *);
    }

  return (command);
}

/* The Yacc grammar productions have a problem, in that they take a
   list followed by an ampersand (`&') and do a simple command connection,
   making the entire list effectively asynchronous, instead of just
   the last command.  This means that when the list is executed, all
   the commands have stdin set to /dev/null when job control is not
   active, instead of just the last.  This is wrong, and needs fixing
   up.  This function takes the `&' and applies it to the last command
   in the list.  This is done only for lists connected by `;'; it makes
   `;' bind `tighter' than `&'. */
COMMAND *
connect_async_list (command, command2, connector)
     COMMAND *command, *command2;
     int connector;
{
  COMMAND *t, *t1, *t2;

  t1 = command;
  t = command->value.Connection->second;

  if (!t || (command->flags & CMD_WANT_SUBSHELL) ||
      command->value.Connection->connector != ';')
    {
      t = command_connect (command, command2, connector);
      return t;
    }

  /* This is just defensive programming.  The Yacc precedence rules
     will generally hand this function a command where t points directly
     to the command we want (e.g. given a ; b ; c ; d &, t1 will point
     to the `a ; b ; c' list and t will be the `d').  We only want to do
     this if the list is not being executed as a unit in the background
     with `( ... )', so we have to check for CMD_WANT_SUBSHELL.  That's
     the only way to tell. */
  while (((t->flags & CMD_WANT_SUBSHELL) == 0) && t->type == cm_connection &&
     t->value.Connection->connector == ';')
    {
      t1 = t;
      t = t->value.Connection->second;
    }
  /* Now we have t pointing to the last command in the list, and
     t1->value.Connection->second == t. */
  t2 = command_connect (t, command2, connector);
  t1->value.Connection->second = t2;
  return command;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/print_cmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/print_cmd.c used in: Step 1208
/* print_command -- A way to make readable commands from a command tree. */
/* Copyright (C) 1989 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include <stdio.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#if defined (PREFER_STDARG)
#  include <stdarg.h>
#else
#  include <varargs.h>
#endif

#include "bashansi.h"

#include "shell.h"
#include "flags.h"
#include <y.tab.h>  /* use <...> so we pick it up from the build directory */
#include "builtins/common.h"

#if !HAVE_DECL_PRINTF
extern int printf __P((const char *, ...)); /* Yuck.  Double yuck. */
#endif

extern int indirection_level;

static int indentation;
static int indentation_amount = 4;

#if defined (PREFER_STDARG)
typedef void PFUNC __P((const char *, ...));

static void cprintf __P((const char *, ...))  __attribute__((__format__ (printf, 1, 2)));
static void xprintf __P((const char *, ...))  __attribute__((__format__ (printf, 1, 2)));
#else
#define PFUNC VFunction
static void cprintf ();
static void xprintf ();
#endif

static void reset_locals __P((void));
static void newline __P((char *));
static void indent __P((int));
static void semicolon __P((void));
static void the_printed_command_resize __P((int));

static void make_command_string_internal __P((COMMAND *));
static void _print_word_list __P((WORD_LIST *, char *, PFUNC *));
static void command_print_word_list __P((WORD_LIST *, char *));
static void print_case_clauses __P((PATTERN_LIST *));
static void print_redirection_list __P((REDIRECT *));
static void print_redirection __P((REDIRECT *));

static void print_for_command __P((FOR_COM *));
#if defined (ARITH_FOR_COMMAND)
static void print_arith_for_command __P((ARITH_FOR_COM *));
#endif
#if defined (SELECT_COMMAND)
static void print_select_command __P((SELECT_COM *));
#endif
static void print_group_command __P((GROUP_COM *));
static void print_case_command __P((CASE_COM *));
static void print_while_command __P((WHILE_COM *));
static void print_until_command __P((WHILE_COM *));
static void print_until_or_while __P((WHILE_COM *, char *));
static void print_if_command __P((IF_COM *));
#if defined (DPAREN_ARITHMETIC)
static void print_arith_command __P((ARITH_COM *));
#endif
#if defined (COND_COMMAND)
static void print_cond_node __P((COND_COM *));
static void print_cond_command __P((COND_COM *));
#endif
static void print_function_def __P((FUNCTION_DEF *));

#define PRINTED_COMMAND_INITIAL_SIZE 64
#define PRINTED_COMMAND_GROW_SIZE 128

char *the_printed_command = (char *)NULL;
int the_printed_command_size = 0;
int command_string_index = 0;

/* Non-zero means the stuff being printed is inside of a function def. */
static int inside_function_def;
static int skip_this_indent;
static int was_heredoc;

/* The depth of the group commands that we are currently printing.  This
   includes the group command that is a function body. */
static int group_command_nesting;

/* A buffer to indicate the indirection level (PS4) when set -x is enabled. */
static char indirection_string[100];

/* Print COMMAND (a command tree) on standard output. */
void
print_command (command)
     COMMAND *command;
{
  command_string_index = 0;
  printf ("%s", make_command_string (command));
}

/* Make a string which is the printed representation of the command
   tree in COMMAND.  We return this string.  However, the string is
   not consed, so you have to do that yourself if you want it to
   remain around. */
char *
make_command_string (command)
     COMMAND *command;
{
  command_string_index = was_heredoc = 0;
  make_command_string_internal (command);
  return (the_printed_command);
}

/* The internal function.  This is the real workhorse. */
static void
make_command_string_internal (command)
     COMMAND *command;
{
  if (command == 0)
    cprintf ("");
  else
    {
      if (skip_this_indent)
    skip_this_indent--;
      else
    indent (indentation);

      if (command->flags & CMD_TIME_PIPELINE)
    {
      cprintf ("time ");
      if (command->flags & CMD_TIME_POSIX)
        cprintf ("-p ");
    }

      if (command->flags & CMD_INVERT_RETURN)
    cprintf ("! ");

      switch (command->type)
    {
    case cm_for:
      print_for_command (command->value.For);
      break;

#if defined (ARITH_FOR_COMMAND)
    case cm_arith_for:
      print_arith_for_command (command->value.ArithFor);
      break;
#endif

#if defined (SELECT_COMMAND)
    case cm_select:
      print_select_command (command->value.Select);
      break;
#endif

    case cm_case:
      print_case_command (command->value.Case);
      break;

    case cm_while:
      print_while_command (command->value.While);
      break;

    case cm_until:
      print_until_command (command->value.While);
      break;

    case cm_if:
      print_if_command (command->value.If);
      break;

#if defined (DPAREN_ARITHMETIC)
    case cm_arith:
      print_arith_command (command->value.Arith);
      break;
#endif

#if defined (COND_COMMAND)
    case cm_cond:
      print_cond_command (command->value.Cond);
      break;
#endif

    case cm_simple:
      print_simple_command (command->value.Simple);
      break;

    case cm_connection:

      skip_this_indent++;
      make_command_string_internal (command->value.Connection->first);

      switch (command->value.Connection->connector)
        {
        case '&':
        case '|':
          {
        char c = command->value.Connection->connector;
        cprintf (" %c", c);
        if (c != '&' || command->value.Connection->second)
          {
            cprintf (" ");
            skip_this_indent++;
          }
          }
          break;

        case AND_AND:
          cprintf (" && ");
          if (command->value.Connection->second)
        skip_this_indent++;
          break;

        case OR_OR:
          cprintf (" || ");
          if (command->value.Connection->second)
        skip_this_indent++;
          break;

        case ';':
          if (was_heredoc == 0)
        cprintf (";");
          else
        was_heredoc = 0;

          if (inside_function_def)
        cprintf ("\n");
          else
        {
          cprintf (" ");
          if (command->value.Connection->second)
            skip_this_indent++;
        }
          break;

        default:
          cprintf ("print_command: bad connector `%d'",
               command->value.Connection->connector);
          break;
        }

      make_command_string_internal (command->value.Connection->second);
      break;

    case cm_function_def:
      print_function_def (command->value.Function_def);
      break;

    case cm_group:
      print_group_command (command->value.Group);
      break;

    case cm_subshell:
      cprintf ("( ");
      skip_this_indent++;
      make_command_string_internal (command->value.Subshell->command);
      cprintf (" )");
      break;

    default:
      command_error ("print_command", CMDERR_BADTYPE, command->type, 0);
      break;
    }


      if (command->redirects)
    {
      cprintf (" ");
      print_redirection_list (command->redirects);
    }
    }
}

static void
_print_word_list (list, separator, pfunc)
     WORD_LIST *list;
     char *separator;
     PFUNC *pfunc;
{
  WORD_LIST *w;

  for (w = list; w; w = w->next)
    (*pfunc) ("%s%s", w->word->word, w->next ? separator : "");
}

void
print_word_list (list, separator)
     WORD_LIST *list;
     char *separator;
{
  _print_word_list (list, separator, xprintf);
}

/* Return a string denoting what our indirection level is. */

char *
indirection_level_string ()
{
  register int i, j;
  char *ps4;

  indirection_string[0] = '\0';
  ps4 = get_string_value ("PS4");

  if (ps4 == 0 || *ps4 == '\0')
    return (indirection_string);

  change_flag ('x', FLAG_OFF);
  ps4 = decode_prompt_string (ps4);
  change_flag ('x', FLAG_ON);

  for (i = 0; *ps4 && i < indirection_level && i < 99; i++)
    indirection_string[i] = *ps4;

  for (j = 1; *ps4 && ps4[j] && i < 99; i++, j++)
    indirection_string[i] = ps4[j];

  indirection_string[i] = '\0';
  free (ps4);
  return (indirection_string);
}

/* A function to print the words of a simple command when set -x is on. */
void
xtrace_print_word_list (list)
     WORD_LIST *list;
{
  WORD_LIST *w;
  char *t, *x;

  fprintf (stderr, "%s", indirection_level_string ());
  for (w = list; w; w = w->next)
    {
      t = w->word->word;
      if (t == 0 || *t == '\0')
    fprintf (stderr, "''%s", w->next ? " " : "");
      else if (sh_contains_shell_metas (t))
    {
      x = sh_single_quote (t);
      fprintf (stderr, "%s%s", x, w->next ? " " : "");
      free (x);
    }
      else if (ansic_shouldquote (t))
    {
      x = ansic_quote (t, 0, (int *)0);
      fprintf (stderr, "%s%s", x, w->next ? " " : "");
      free (x);
    }
      else
    fprintf (stderr, "%s%s", t, w->next ? " " : "");
    }
  fprintf (stderr, "\n");
}

static void
command_print_word_list (list, separator)
     WORD_LIST *list;
     char *separator;
{
  _print_word_list (list, separator, cprintf);
}

static void
print_for_command (for_command)
     FOR_COM *for_command;
{
  cprintf ("for %s in ", for_command->name->word);
  command_print_word_list (for_command->map_list, " ");
  cprintf (";");
  newline ("do\n");
  indentation += indentation_amount;
  make_command_string_internal (for_command->action);
  semicolon ();
  indentation -= indentation_amount;
  newline ("done");
}

#if defined (ARITH_FOR_COMMAND)
static void
print_arith_for_command (arith_for_command)
     ARITH_FOR_COM *arith_for_command;
{
  cprintf ("for (( ");
  command_print_word_list (arith_for_command->init, " ");
  cprintf (" ; ");
  command_print_word_list (arith_for_command->test, " ");
  cprintf (" ; ");
  command_print_word_list (arith_for_command->step, " ");
  cprintf (" ))");
  newline ("do\n");
  indentation += indentation_amount;
  make_command_string_internal (arith_for_command->action);
  semicolon ();
  indentation -= indentation_amount;
  newline ("done");
}
#endif /* ARITH_FOR_COMMAND */

#if defined (SELECT_COMMAND)
static void
print_select_command (select_command)
     SELECT_COM *select_command;
{
  cprintf ("select %s in ", select_command->name->word);
  command_print_word_list (select_command->map_list, " ");
  cprintf (";");
  newline ("do\n");
  indentation += indentation_amount;
  make_command_string_internal (select_command->action);
  semicolon ();
  indentation -= indentation_amount;
  newline ("done");
}
#endif /* SELECT_COMMAND */

static void
print_group_command (group_command)
     GROUP_COM *group_command;
{
  group_command_nesting++;
  cprintf ("{ ");

  if (inside_function_def == 0)
    skip_this_indent++;
  else
    {
      /* This is a group command { ... } inside of a function
     definition, and should be printed as a multiline group
     command, using the current indentation. */
      cprintf ("\n");
      indentation += indentation_amount;
    }

  make_command_string_internal (group_command->command);

  if (inside_function_def)
    {
      cprintf ("\n");
      indentation -= indentation_amount;
      indent (indentation);
    }
  else
    {
      semicolon ();
      cprintf (" ");
    }

  cprintf ("}");

  group_command_nesting--;
}

static void
print_case_command (case_command)
     CASE_COM *case_command;
{
  cprintf ("case %s in ", case_command->word->word);
  if (case_command->clauses)
    print_case_clauses (case_command->clauses);
  newline ("esac");
}

static void
print_case_clauses (clauses)
     PATTERN_LIST *clauses;
{
  indentation += indentation_amount;
  while (clauses)
    {
      newline ("");
      command_print_word_list (clauses->patterns, " | ");
      cprintf (")\n");
      indentation += indentation_amount;
      make_command_string_internal (clauses->action);
      indentation -= indentation_amount;
      newline (";;");
      clauses = clauses->next;
    }
  indentation -= indentation_amount;
}

static void
print_while_command (while_command)
     WHILE_COM *while_command;
{
  print_until_or_while (while_command, "while");
}

static void
print_until_command (while_command)
     WHILE_COM *while_command;
{
  print_until_or_while (while_command, "until");
}

static void
print_until_or_while (while_command, which)
     WHILE_COM *while_command;
     char *which;
{
  cprintf ("%s ", which);
  skip_this_indent++;
  make_command_string_internal (while_command->test);
  semicolon ();
  cprintf (" do\n");    /* was newline ("do\n"); */
  indentation += indentation_amount;
  make_command_string_internal (while_command->action);
  indentation -= indentation_amount;
  semicolon ();
  newline ("done");
}

static void
print_if_command (if_command)
     IF_COM *if_command;
{
  cprintf ("if ");
  skip_this_indent++;
  make_command_string_internal (if_command->test);
  semicolon ();
  cprintf (" then\n");
  indentation += indentation_amount;
  make_command_string_internal (if_command->true_case);
  indentation -= indentation_amount;

  if (if_command->false_case)
    {
      semicolon ();
      newline ("else\n");
      indentation += indentation_amount;
      make_command_string_internal (if_command->false_case);
      indentation -= indentation_amount;
    }
  semicolon ();
  newline ("fi");
}

#if defined (DPAREN_ARITHMETIC)
static void
print_arith_command (arith_command)
     ARITH_COM *arith_command;
{
  cprintf ("(( ");
  command_print_word_list (arith_command->exp, " ");
  cprintf (" ))");
}
#endif

#if defined (COND_COMMAND)
static void
print_cond_node (cond)
     COND_COM *cond;
{
  if (cond->flags & CMD_INVERT_RETURN)
    cprintf ("! ");

  if (cond->type == COND_EXPR)
    {
      cprintf ("( ");
      print_cond_node (cond->left);
      cprintf (" )");
    }
  else if (cond->type == COND_AND)
    {
      print_cond_node (cond->left);
      cprintf (" && ");
      print_cond_node (cond->right);
    }
  else if (cond->type == COND_OR)
    {
      print_cond_node (cond->left);
      cprintf (" || ");
      print_cond_node (cond->right);
    }
  else if (cond->type == COND_UNARY)
    {
      cprintf ("%s", cond->op->word);
      cprintf (" ");
      print_cond_node (cond->left);
    }
  else if (cond->type == COND_BINARY)
    {
      print_cond_node (cond->left);
      cprintf (" ");
      cprintf ("%s", cond->op->word);
      cprintf (" ");
      print_cond_node (cond->right);
    }
  else if (cond->type == COND_TERM)
    {
      cprintf ("%s", cond->op->word);       /* need to add quoting here */
    }
}

static void
print_cond_command (cond)
     COND_COM *cond;
{
  cprintf ("[[ ");
  print_cond_node (cond);
  cprintf (" ]]");
}

#ifdef DEBUG
void
debug_print_cond_command (cond)
     COND_COM *cond;
{
  fprintf (stderr, "DEBUG: ");
  command_string_index = 0;
  print_cond_command (cond);
  fprintf (stderr, "%s\n", the_printed_command);
}
#endif

void
xtrace_print_cond_term (type, invert, op, arg1, arg2)
     int type, invert;
     WORD_DESC *op;
     char *arg1, *arg2;
{
  command_string_index = 0;
  fprintf (stderr, "%s", indirection_level_string ());
  fprintf (stderr, "[[ ");
  if (invert)
    fprintf (stderr, "! ");

  if (type == COND_UNARY)
    {
      fprintf (stderr, "%s ", op->word);
      fprintf (stderr, "%s", (arg1 && *arg1) ? arg1 : "''");
    }
  else if (type == COND_BINARY)
    {
      fprintf (stderr, "%s", (arg1 && *arg1) ? arg1 : "''");
      fprintf (stderr, " %s ", op->word);
      fprintf (stderr, "%s", (arg2 && *arg2) ? arg2 : "''");
    }

  fprintf (stderr, " ]]\n");
}     
#endif /* COND_COMMAND */

#if defined (DPAREN_ARITHMETIC) || defined (ARITH_FOR_COMMAND)
/* A function to print the words of an arithmetic command when set -x is on. */
void
xtrace_print_arith_cmd (list)
     WORD_LIST *list;
{
  WORD_LIST *w;

  fprintf (stderr, "%s", indirection_level_string ());
  fprintf (stderr, "(( ");
  for (w = list; w; w = w->next)
    fprintf (stderr, "%s%s", w->word->word, w->next ? " " : "");
  fprintf (stderr, " ))\n");
}
#endif

void
print_simple_command (simple_command)
     SIMPLE_COM *simple_command;
{
  command_print_word_list (simple_command->words, " ");

  if (simple_command->redirects)
    {
      cprintf (" ");
      print_redirection_list (simple_command->redirects);
    }
}

static void
print_redirection_list (redirects)
     REDIRECT *redirects;
{
  REDIRECT *heredocs, *hdtail, *newredir;

  heredocs = (REDIRECT *)NULL;
  hdtail = heredocs;

  was_heredoc = 0;
  while (redirects)
    {
      /* Defer printing the here documents until we've printed the
     rest of the redirections. */
      if (redirects->instruction == r_reading_until || redirects->instruction == r_deblank_reading_until)
    {
      newredir = copy_redirect (redirects);
      newredir->next = (REDIRECT *)NULL;
      if (heredocs)
        {
          hdtail->next = newredir;
          hdtail = newredir;
        }
      else
        hdtail = heredocs = newredir;
    }
      else
    print_redirection (redirects);

      redirects = redirects->next;
      if (redirects)
    cprintf (" ");
    }

  /* Now that we've printed all the other redirections (on one line),
     print the here documents. */
  if (heredocs)
    {
      cprintf (" "); 
      for (hdtail = heredocs; hdtail; hdtail = hdtail->next)
    {
      print_redirection (hdtail);
      cprintf ("\n");
    }
      dispose_redirects (heredocs);
      was_heredoc = 1;
    }
}

static void
print_redirection (redirect)
     REDIRECT *redirect;
{
  int kill_leading, redirector, redir_fd;
  WORD_DESC *redirectee;

  kill_leading = 0;
  redirectee = redirect->redirectee.filename;
  redirector = redirect->redirector;
  redir_fd = redirect->redirectee.dest;

  switch (redirect->instruction)
    {
    case r_output_direction:
      if (redirector != 1)
    cprintf ("%d", redirector);
      cprintf (">%s", redirectee->word);
      break;

    case r_input_direction:
      if (redirector != 0)
    cprintf ("%d", redirector);
      cprintf ("<%s", redirectee->word);
      break;

    case r_inputa_direction:    /* Redirection created by the shell. */
      cprintf ("&");
      break;

    case r_appending_to:
      if (redirector != 1)
    cprintf ("%d", redirector);
      cprintf (">>%s", redirectee->word);
      break;

    case r_deblank_reading_until:
      kill_leading++;
      /* ... */
    case r_reading_until:
      if (redirector != 0)
    cprintf ("%d", redirector);
      /* If the here document delimiter is quoted, single-quote it. */
      if (redirect->redirectee.filename->flags & W_QUOTED)
    {
      char *x;
      x = sh_single_quote (redirect->here_doc_eof);
      cprintf ("<<%s%s\n", kill_leading? "-" : "", x);
      free (x);
    }
      else
    cprintf ("<<%s%s\n", kill_leading? "-" : "", redirect->here_doc_eof);
      cprintf ("%s%s",
           redirect->redirectee.filename->word, redirect->here_doc_eof);
      break;

    case r_reading_string:
      if (redirector != 0)
    cprintf ("%d", redirector);
      if (ansic_shouldquote (redirect->redirectee.filename->word))
    {
      char *x;
      x = ansic_quote (redirect->redirectee.filename->word, 0, (int *)0);
      cprintf ("<<< %s", x);
      free (x);
    }
      else
    cprintf ("<<< %s", redirect->redirectee.filename->word);
      break;

    case r_duplicating_input:
      cprintf ("%d<&%d", redirector, redir_fd);
      break;

    case r_duplicating_output:
      cprintf ("%d>&%d", redirector, redir_fd);
      break;

    case r_duplicating_input_word:
      cprintf ("%d<&%s", redirector, redirectee->word);
      break;

    case r_duplicating_output_word:
      cprintf ("%d>&%s", redirector, redirectee->word);
      break;

    case r_move_input:
      cprintf ("%d<&%d-", redirector, redir_fd);
      break;

    case r_move_output:
      cprintf ("%d>&%d-", redirector, redir_fd);
      break;

    case r_move_input_word:
      cprintf ("%d<&%s-", redirector, redirectee->word);
      break;

    case r_move_output_word:
      cprintf ("%d>&%s-", redirector, redirectee->word);
      break;

    case r_close_this:
      cprintf ("%d>&-", redirector);
      break;

    case r_err_and_out:
      cprintf (">&%s", redirectee->word);
      break;

    case r_input_output:
      if (redirector != 1)
    cprintf ("%d", redirector);
      cprintf ("<>%s", redirectee->word);
      break;

    case r_output_force:
      if (redirector != 1)
    cprintf ("%d", redirector);
      cprintf (">|%s", redirectee->word);
      break;
    }
}

static void
reset_locals ()
{
  inside_function_def = 0;
  indentation = 0;
}

static void
print_function_def (func)
     FUNCTION_DEF *func;
{
  COMMAND *cmdcopy;
  REDIRECT *func_redirects;

  func_redirects = NULL;
  cprintf ("function %s () \n", func->name->word);
  add_unwind_protect (reset_locals, 0);

  indent (indentation);
  cprintf ("{ \n");

  inside_function_def++;
  indentation += indentation_amount;

  cmdcopy = copy_command (func->command);
  if (cmdcopy->type == cm_group)
    {
      func_redirects = cmdcopy->redirects;
      cmdcopy->redirects = (REDIRECT *)NULL;
    }
  make_command_string_internal (cmdcopy->type == cm_group
                    ? cmdcopy->value.Group->command
                    : cmdcopy);

  remove_unwind_protect ();
  indentation -= indentation_amount;
  inside_function_def--;

  if (func_redirects)
    { /* { */
      newline ("} ");
      print_redirection_list (func_redirects);
      cmdcopy->redirects = func_redirects;
    }
  else
    newline ("}");

  dispose_command (cmdcopy);
}

/* Return the string representation of the named function.
   NAME is the name of the function.
   COMMAND is the function body.  It should be a GROUP_COM.
   MULTI_LINE is non-zero to pretty-print, or zero for all on one line.
  */
char *
named_function_string (name, command, multi_line)
     char *name;
     COMMAND *command;
     int multi_line;
{
  char *result;
  int old_indent, old_amount;
  COMMAND *cmdcopy;
  REDIRECT *func_redirects;

  old_indent = indentation;
  old_amount = indentation_amount;
  command_string_index = was_heredoc = 0;

  if (name && *name)
    cprintf ("%s ", name);

  cprintf ("() ");

  if (multi_line == 0)
    {
      indentation = 1;
      indentation_amount = 0;
    }
  else
    {
      cprintf ("\n");
      indentation += indentation_amount;
    }

  inside_function_def++;

  cprintf (multi_line ? "{ \n" : "{ ");

  cmdcopy = copy_command (command);
  /* Take any redirections specified in the function definition (which should
     apply to the function as a whole) and save them for printing later. */
  func_redirects = (REDIRECT *)NULL;
  if (cmdcopy->type == cm_group)
    {
      func_redirects = cmdcopy->redirects;
      cmdcopy->redirects = (REDIRECT *)NULL;
    }
  make_command_string_internal (cmdcopy->type == cm_group
                    ? cmdcopy->value.Group->command
                    : cmdcopy);

  indentation = old_indent;
  indentation_amount = old_amount;
  inside_function_def--;

  if (func_redirects)
    { /* { */
      newline ("} ");
      print_redirection_list (func_redirects);
      cmdcopy->redirects = func_redirects;
    }
  else
    newline ("}");

  result = the_printed_command;

  if (!multi_line)
    {
#if 0
      register int i;
      for (i = 0; result[i]; i++)
    if (result[i] == '\n')
      {
        strcpy (result + i, result + i + 1);
        --i;
      }
#else
      if (result[2] == '\n')    /* XXX -- experimental */
    strcpy (result + 2, result + 3);
#endif
    }

  dispose_command (cmdcopy);

  return (result);
}

static void
newline (string)
     char *string;
{
  cprintf ("\n");
  indent (indentation);
  if (string && *string)
    cprintf ("%s", string);
}

static char *indentation_string;
static int indentation_size;

static void
indent (amount)
     int amount;
{
  register int i;

  RESIZE_MALLOCED_BUFFER (indentation_string, 0, amount, indentation_size, 16);

  for (i = 0; amount > 0; amount--)
    indentation_string[i++] = ' ';
  indentation_string[i] = '\0';
  cprintf (indentation_string);
}

static void
semicolon ()
{
  if (command_string_index > 0 &&
       (the_printed_command[command_string_index - 1] == '&' ||
        the_printed_command[command_string_index - 1] == '\n'))
    return;
  cprintf (";");
}

/* How to make the string. */
static void
#if defined (PREFER_STDARG)
cprintf (const char *control, ...)
#else
cprintf (control, va_alist)
     const char *control;
     va_dcl
#endif
{
  register const char *s;
  char char_arg[2], *argp, intbuf[INT_STRLEN_BOUND (int) + 1];
  int digit_arg, arg_len, c;
  va_list args;

  SH_VA_START (args, control);

  arg_len = strlen (control);
  the_printed_command_resize (arg_len + 1);

  char_arg[1] = '\0';
  s = control;
  while (s && *s)
    {
      c = *s++;
      argp = (char *)NULL;
      if (c != '%' || !*s)
    {
      char_arg[0] = c;
      argp = char_arg;
      arg_len = 1;
    }
      else
    {
      c = *s++;
      switch (c)
        {
        case '%':
          char_arg[0] = c;
          argp = char_arg;
          arg_len = 1;
          break;

        case 's':
          argp = va_arg (args, char *);
          arg_len = strlen (argp);
          break;

        case 'd':
          /* Represent an out-of-range file descriptor with an out-of-range
         integer value.  We can do this because the only use of `%d' in
         the calls to cprintf is to output a file descriptor number for
         a redirection. */
          digit_arg = va_arg (args, int);
          if (digit_arg < 0)
        {
          sprintf (intbuf, "%u", (unsigned)-1);
          argp = intbuf;
        }
          else
            argp = inttostr (digit_arg, intbuf, sizeof (intbuf));
          arg_len = strlen (argp);
          break;

        case 'c':
          char_arg[0] = va_arg (args, int);
          argp = char_arg;
          arg_len = 1;
          break;

        default:
          programming_error ("cprintf: bad `%%' argument (%c)", c);
          /*NOTREACHED*/
        }
    }

      if (argp && arg_len)
    {
      the_printed_command_resize (arg_len + 1);
      FASTCOPY (argp, the_printed_command + command_string_index, arg_len);
      command_string_index += arg_len;
    }
    }

  the_printed_command[command_string_index] = '\0';
}

/* Ensure that there is enough space to stuff LENGTH characters into
   THE_PRINTED_COMMAND. */
static void
the_printed_command_resize (length)
     int length;
{
  if (the_printed_command == 0)
    {
      the_printed_command_size = (length + PRINTED_COMMAND_INITIAL_SIZE - 1) & ~(PRINTED_COMMAND_INITIAL_SIZE - 1);
      the_printed_command = (char *)xmalloc (the_printed_command_size);
      command_string_index = 0;
    }
  else if ((command_string_index + length) >= the_printed_command_size)
    {
      int new;
      new = command_string_index + length + 1;

      /* Round up to the next multiple of PRINTED_COMMAND_GROW_SIZE. */
      new = (new + PRINTED_COMMAND_GROW_SIZE - 1) & ~(PRINTED_COMMAND_GROW_SIZE - 1);
      the_printed_command_size = new;

      the_printed_command = (char *)xrealloc (the_printed_command, the_printed_command_size);
    }
}

#if defined (HAVE_VPRINTF)
/* ``If vprintf is available, you may assume that vfprintf and vsprintf are
     also available.'' */

static void
#if defined (PREFER_STDARG)
xprintf (const char *format, ...)
#else
xprintf (format, va_alist)
     const char *format;
     va_dcl
#endif
{
  va_list args;

  SH_VA_START (args, format);

  vfprintf (stdout, format, args);
  va_end (args);
}

#else

static void
xprintf (format, arg1, arg2, arg3, arg4, arg5)
     const char *format;
{
  printf (format, arg1, arg2, arg3, arg4, arg5);
}

#endif /* !HAVE_VPRINTF */

https://git.savannah.gnu.org/cgit/bash.git/tree/dispose_cmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/dispose_cmd.c used in: Step 1209
/* dispose_command.c -- dispose of a COMMAND structure. */

/* Copyright (C) 1987,1991 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include "bashtypes.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "bashansi.h"
#include "shell.h"

extern sh_obj_cache_t wdcache, wlcache;

/* Dispose of the command structure passed. */
void
dispose_command (command)
     COMMAND *command;
{
  if (command == 0)
    return;

  if (command->redirects)
    dispose_redirects (command->redirects);

  switch (command->type)
    {
    case cm_for:
#if defined (SELECT_COMMAND)
    case cm_select:
#endif
      {
    register FOR_COM *c;
#if defined (SELECT_COMMAND)
    if (command->type == cm_select)
      c = (FOR_COM *)command->value.Select;
    else
#endif
    c = command->value.For;
    dispose_word (c->name);
    dispose_words (c->map_list);
    dispose_command (c->action);
    free (c);
    break;
      }

#if defined (ARITH_FOR_COMMAND)
    case cm_arith_for:
      {
    register ARITH_FOR_COM *c;

    c = command->value.ArithFor;
    dispose_words (c->init);
    dispose_words (c->test);
    dispose_words (c->step);
    dispose_command (c->action);
    free (c);
    break;
      }
#endif /* ARITH_FOR_COMMAND */

    case cm_group:
      {
    dispose_command (command->value.Group->command);
    free (command->value.Group);
    break;
      }

    case cm_subshell:
      {
    dispose_command (command->value.Subshell->command);
    free (command->value.Subshell);
    break;
      }

    case cm_case:
      {
    register CASE_COM *c;
    PATTERN_LIST *t, *p;

    c = command->value.Case;
    dispose_word (c->word);

    for (p = c->clauses; p; )
      {
        dispose_words (p->patterns);
        dispose_command (p->action);
        t = p;
        p = p->next;
        free (t);
      }
    free (c);
    break;
      }

    case cm_until:
    case cm_while:
      {
    register WHILE_COM *c;

    c = command->value.While;
    dispose_command (c->test);
    dispose_command (c->action);
    free (c);
    break;
      }

    case cm_if:
      {
    register IF_COM *c;

    c = command->value.If;
    dispose_command (c->test);
    dispose_command (c->true_case);
    dispose_command (c->false_case);
    free (c);
    break;
      }

    case cm_simple:
      {
    register SIMPLE_COM *c;

    c = command->value.Simple;
    dispose_words (c->words);
    dispose_redirects (c->redirects);
    free (c);
    break;
      }

    case cm_connection:
      {
    register CONNECTION *c;

    c = command->value.Connection;
    dispose_command (c->first);
    dispose_command (c->second);
    free (c);
    break;
      }

#if defined (DPAREN_ARITHMETIC)
    case cm_arith:
      {
    register ARITH_COM *c;

    c = command->value.Arith;
    dispose_words (c->exp);
    free (c);
    break;
      }
#endif /* DPAREN_ARITHMETIC */

#if defined (COND_COMMAND)
    case cm_cond:
      {
    register COND_COM *c;

    c = command->value.Cond;
    dispose_cond_node (c);
    break;
      }
#endif /* COND_COMMAND */

    case cm_function_def:
      {
    register FUNCTION_DEF *c;

    c = command->value.Function_def;
    dispose_word (c->name);
    dispose_command (c->command);
    free (c);
    break;
      }

    default:
      command_error ("dispose_command", CMDERR_BADTYPE, command->type, 0);
      break;
    }
  free (command);
}

#if defined (COND_COMMAND)
/* How to free a node in a conditional command. */
void
dispose_cond_node (cond)
     COND_COM *cond;
{
  if (cond)
    {
      if (cond->left)
    dispose_cond_node (cond->left);
      if (cond->right)
    dispose_cond_node (cond->right);
      if (cond->op)
    dispose_word (cond->op);
      free (cond);
    }
}
#endif /* COND_COMMAND */

/* How to free a WORD_DESC. */
void
dispose_word (w)
     WORD_DESC *w;
{
  FREE (w->word);
#if 0
  free (w);
#else
  ocache_free (wdcache, WORD_DESC, w);
#endif
}

/* How to get rid of a linked list of words.  A WORD_LIST. */
void
dispose_words (list)
     WORD_LIST *list;
{
  WORD_LIST *t;

  while (list)
    {
      t = list;
      list = list->next;
      dispose_word (t->word);
#if 0
      free (t);
#else
      ocache_free (wlcache, WORD_LIST, t);
#endif
    }
}

#ifdef INCLUDE_UNUSED
/* How to dispose of an array of pointers to char.  This is identical to
   free_array in stringlib.c. */
void
dispose_word_array (array)
     char **array;
{
  register int count;

  if (array == 0)
    return;

  for (count = 0; array[count]; count++)
    free (array[count]);

  free (array);
}
#endif

/* How to dispose of an list of redirections.  A REDIRECT. */
void
dispose_redirects (list)
     REDIRECT *list;
{
  register REDIRECT *t;

  while (list)
    {
      t = list;
      list = list->next;
      switch (t->instruction)
    {
    case r_reading_until:
    case r_deblank_reading_until:
      free (t->here_doc_eof);
    /*FALLTHROUGH*/
    case r_reading_string:
    case r_output_direction:
    case r_input_direction:
    case r_inputa_direction:
    case r_appending_to:
    case r_err_and_out:
    case r_input_output:
    case r_output_force:
    case r_duplicating_input_word:
    case r_duplicating_output_word:
    case r_move_input_word:
    case r_move_output_word:
      dispose_word (t->redirectee.filename);
      /* FALLTHROUGH */
    default:
      break;
    }
      free (t);
    }
}

https://git.savannah.gnu.org/cgit/bash.git/tree/variables.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/variables.c used in: Step 1211
/* variables.c -- Functions for hacking shell variables. */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include "bashtypes.h"
#include "posixstat.h"
#include "posixtime.h"

#if defined (qnx)
#  include <sys/vc.h>
#endif

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <stdio.h>
#include "chartypes.h"
#include <pwd.h>
#include "bashansi.h"

#include "shell.h"
#include "flags.h"
#include "execute_cmd.h"
#include "findcmd.h"
#include "mailcheck.h"
#include "input.h"
#include "hashcmd.h"
#include "pathexp.h"

#include "builtins/getopt.h"
#include "builtins/common.h"

#if defined (READLINE)
#  include "bashline.h"
#  include <readline/readline.h>
#else
#  include <tilde/tilde.h>
#endif

#if defined (HISTORY)
#  include "bashhist.h"
#  include <readline/history.h>
#endif /* HISTORY */

#if defined (PROGRAMMABLE_COMPLETION)
#  include "pcomplete.h"
#endif

#define TEMPENV_HASH_BUCKETS    4   /* must be power of two */

#define ifsname(s)  ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0')

/* Variables used here and defined in other files. */
extern int posixly_correct;
extern int line_number;
extern int subshell_environment, indirection_level;
extern int build_version, patch_level;
extern char *dist_version, *release_status;
extern char *shell_name;
extern char *primary_prompt, *secondary_prompt;
extern char *current_host_name;
extern sh_builtin_func_t *this_shell_builtin;
extern SHELL_VAR *this_shell_function;
extern char *this_command_name;
extern time_t shell_start_time;

/* The list of shell variables that the user has created at the global
   scope, or that came from the environment. */
VAR_CONTEXT *global_variables = (VAR_CONTEXT *)NULL;

/* The current list of shell variables, including function scopes */
VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL;

/* The list of shell functions that the user has created, or that came from
   the environment. */
HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;

/* The current variable context.  This is really a count of how deep into
   executing functions we are. */
int variable_context = 0;

/* The set of shell assignments which are made only in the environment
   for a single command. */
HASH_TABLE *temporary_env = (HASH_TABLE *)NULL;

/* Some funky variables which are known about specially.  Here is where
   "$*", "$1", and all the cruft is kept. */
char *dollar_vars[10];
WORD_LIST *rest_of_args = (WORD_LIST *)NULL;

/* The value of $$. */
pid_t dollar_dollar_pid;

/* An array which is passed to commands as their environment.  It is
   manufactured from the union of the initial environment and the
   shell variables that are marked for export. */
char **export_env = (char **)NULL;
static int export_env_index;
static int export_env_size;

/* Non-zero means that we have to remake EXPORT_ENV. */
int array_needs_making = 1;

/* The number of times BASH has been executed.  This is set
   by initialize_variables (). */
int shell_level = 0;

/* Some forward declarations. */
static void set_machine_vars __P((void));
static void set_home_var __P((void));
static void set_shell_var __P((void));
static char *get_bash_name __P((void));
static void initialize_shell_level __P((void));
static void uidset __P((void));
#if defined (ARRAY_VARS)
static void make_vers_array __P((void));
#endif

static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t));
#if defined (ARRAY_VARS)
static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t));
#endif

static SHELL_VAR *assign_seconds __P((SHELL_VAR *, char *, arrayind_t));
static SHELL_VAR *get_seconds __P((SHELL_VAR *));
static SHELL_VAR *init_seconds_var __P((void));

static int brand __P((void));
static void sbrand __P((unsigned long));        /* set bash random number generator. */
static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t));
static SHELL_VAR *get_random __P((SHELL_VAR *));

static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t));
static SHELL_VAR *get_lineno __P((SHELL_VAR *));

#if defined (HISTORY)
static SHELL_VAR *get_histcmd __P((SHELL_VAR *));
#endif

#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t));
static SHELL_VAR *get_dirstack __P((SHELL_VAR *));
static SHELL_VAR *init_dirstack_var __P((void));
#endif

#if defined (ARRAY_VARS)
static SHELL_VAR *get_groupset __P((SHELL_VAR *));
static SHELL_VAR *init_groups_var __P((void));
#endif

static SHELL_VAR *get_funcname __P((SHELL_VAR *));
static SHELL_VAR *init_funcname_var __P((void));

static void initialize_dynamic_variables __P((void));

static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
static SHELL_VAR *new_shell_variable __P((const char *));
static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int));

static void free_variable_hash_data __P((PTR_T));

static VARLIST *vlist_alloc __P((int));
static VARLIST *vlist_realloc __P((VARLIST *, int));
static void vlist_add __P((VARLIST *, SHELL_VAR *, int));

static void flatten __P((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int));

static int qsort_var_comp __P((SHELL_VAR **, SHELL_VAR **));

static SHELL_VAR **vapply __P((sh_var_map_func_t *));
static SHELL_VAR **fapply __P((sh_var_map_func_t *));

static int visible_var __P((SHELL_VAR *));
static int visible_and_exported __P((SHELL_VAR *));
static int local_and_exported __P((SHELL_VAR *));
static int variable_in_context __P((SHELL_VAR *));
#if defined (ARRAY_VARS)
static int visible_array_vars __P((SHELL_VAR *));
#endif

static SHELL_VAR *bind_tempenv_variable __P((const char *, char *));
static void push_temp_var __P((PTR_T));
static void propagate_temp_var __P((PTR_T));
static void dispose_temporary_env __P((sh_free_func_t *));     

static inline char *mk_env_string __P((const char *, const char *));
static char **make_env_array_from_var_list __P((SHELL_VAR **));
static char **make_var_export_array __P((VAR_CONTEXT *));
static char **make_func_export_array __P((void));
static void add_temp_array_to_env __P((char **, int, int));

static int n_shell_variables __P((void));
static int set_context __P((SHELL_VAR *));

static void push_func_var __P((PTR_T));
static void push_exported_var __P((PTR_T));

static inline int find_special_var __P((const char *));
           
/* Initialize the shell variables from the current environment.
   If PRIVMODE is nonzero, don't import functions from ENV or
   parse $SHELLOPTS. */
void
initialize_shell_variables (env, privmode)
     char **env;
     int privmode;
{
  char *name, *string, *temp_string;
  int c, char_index, string_index, string_length;
  SHELL_VAR *temp_var;

  if (shell_variables == 0)
    {
      shell_variables = global_variables = new_var_context ((char *)NULL, 0);
      shell_variables->scope = 0;
      shell_variables->table = hash_create (0);
    }

  if (shell_functions == 0)
    shell_functions = hash_create (0);

  for (string_index = 0; string = env[string_index++]; )
    {
      char_index = 0;
      name = string;
      while ((c = *string++) && c != '=')
    ;
      if (string[-1] == '=')
    char_index = string - name - 1;

      /* If there are weird things in the environment, like `=xxx' or a
     string without an `=', just skip them. */
      if (char_index == 0)
    continue;

      /* ASSERT(name[char_index] == '=') */
      name[char_index] = '\0';
      /* Now, name = env variable name, string = env variable value, and
     char_index == strlen (name) */

      /* If exported function, define it now.  Don't import functions from
     the environment in privileged mode. */
      if (privmode == 0 && read_but_dont_execute == 0 && STREQN ("() {", string, 4))
    {
      string_length = strlen (string);
      temp_string = (char *)xmalloc (3 + string_length + char_index);

      strcpy (temp_string, name);
      temp_string[char_index] = ' ';
      strcpy (temp_string + char_index + 1, string);

      parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);

      /* Ancient backwards compatibility.  Old versions of bash exported
         functions like name()=() {...} */
      if (name[char_index - 1] == ')' && name[char_index - 2] == '(')
        name[char_index - 2] = '\0';

      if (temp_var = find_function (name))
        {
          VSETATTR (temp_var, (att_exported|att_imported));
          array_needs_making = 1;
        }
      else
        report_error ("error importing function definition for `%s'", name);

      /* ( */
      if (name[char_index - 1] == ')' && name[char_index - 2] == '\0')
        name[char_index - 2] = '(';     /* ) */
    }
#if defined (ARRAY_VARS)
#  if 0
      /* Array variables may not yet be exported. */
      else if (*string == '(' && string[1] == '[' && xstrchr (string, ')'))
    {
      string_length = 1;
      temp_string = extract_array_assignment_list (string, &string_length);
      temp_var = assign_array_from_string (name, temp_string);
      FREE (temp_string);
      VSETATTR (temp_var, (att_exported | att_imported));
      array_needs_making = 1;
    }
#  endif
#endif
      else
    {
      temp_var = bind_variable (name, string);
      VSETATTR (temp_var, (att_exported | att_imported));
      array_needs_making = 1;
    }

      name[char_index] = '=';
      /* temp_var can be NULL if it was an exported function with a syntax
     error (a different bug, but it still shouldn't dump core). */
      if (temp_var && function_p (temp_var) == 0)   /* XXX not yet */
    {
      CACHE_IMPORTSTR (temp_var, name);
    }
    }

  set_pwd ();

  /* Set up initial value of $_ */
  temp_var = bind_variable ("_", dollar_vars[0]);

  /* Remember this pid. */
  dollar_dollar_pid = getpid ();

  /* Now make our own defaults in case the vars that we think are
     important are missing. */
  temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE);
#if 0
  set_auto_export (temp_var);   /* XXX */
#endif

  temp_var = set_if_not ("TERM", "dumb");
#if 0
  set_auto_export (temp_var);   /* XXX */
#endif

#if defined (qnx)
  /* set node id -- don't import it from the environment */
  {
    char node_name[22];
    qnx_nidtostr (getnid (), node_name, sizeof (node_name));
    temp_var = bind_variable ("NODE", node_name);
    set_auto_export (temp_var);
  }
#endif

  /* set up the prompts. */
  if (interactive_shell)
    {
#if defined (PROMPT_STRING_DECODE)
      set_if_not ("PS1", primary_prompt);
#else
      if (current_user.uid == -1)
    get_current_user_info ();
      set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt);
#endif
      set_if_not ("PS2", secondary_prompt);
    }
  set_if_not ("PS4", "+ ");

  /* Don't allow IFS to be imported from the environment. */
  temp_var = bind_variable ("IFS", " \t\n");
  setifs (temp_var);

  /* Magic machine types.  Pretty convenient. */
  set_machine_vars ();

  /* Default MAILCHECK for interactive shells.  Defer the creation of a
     default MAILPATH until the startup files are read, because MAIL
     names a mail file if MAILPATH is not set, and we should provide a
     default only if neither is set. */
  if (interactive_shell)
    set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");

  /* Do some things with shell level. */
  initialize_shell_level ();

  set_ppid ();

  /* Initialize the `getopts' stuff. */
  bind_variable ("OPTIND", "1");
  getopts_reset (0);
  bind_variable ("OPTERR", "1");
  sh_opterr = 1;

  if (login_shell == 1)
    set_home_var ();

  /* Get the full pathname to THIS shell, and set the BASH variable
     to it. */
  name = get_bash_name ();
  temp_var = bind_variable ("BASH", name);
  free (name);

  /* Make the exported environment variable SHELL be the user's login
     shell.  Note that the `tset' command looks at this variable
     to determine what style of commands to output; if it ends in "csh",
     then C-shell commands are output, else Bourne shell commands. */
  set_shell_var ();

  /* Make a variable called BASH_VERSION which contains the version info. */
  bind_variable ("BASH_VERSION", shell_version_string ());
#if defined (ARRAY_VARS)
  make_vers_array ();
#endif

  /* Find out if we're supposed to be in Posix.2 mode via an
     environment variable. */
  temp_var = find_variable ("POSIXLY_CORRECT");
  if (!temp_var)
    temp_var = find_variable ("POSIX_PEDANTIC");
  if (temp_var && imported_p (temp_var))
    sv_strict_posix (temp_var->name);

#if defined (HISTORY)
  /* Set history variables to defaults, and then do whatever we would
     do if the variable had just been set.  Do this only in the case
     that we are remembering commands on the history list. */
  if (remember_on_history)
    {
      name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0);

      set_if_not ("HISTFILE", name);
      free (name);

      set_if_not ("HISTSIZE", "500");
      sv_histsize ("HISTSIZE");
    }
#endif /* HISTORY */

  /* Seed the random number generator. */
  sbrand (dollar_dollar_pid + shell_start_time);

  /* Handle some "special" variables that we may have inherited from a
     parent shell. */
  if (interactive_shell)
    {
      temp_var = find_variable ("IGNOREEOF");
      if (!temp_var)
    temp_var = find_variable ("ignoreeof");
      if (temp_var && imported_p (temp_var))
    sv_ignoreeof (temp_var->name);
    }

#if defined (HISTORY)
  if (interactive_shell && remember_on_history)
    {
      sv_history_control ("HISTCONTROL");
      sv_histignore ("HISTIGNORE");
    }
#endif /* HISTORY */

     /*
      * 24 October 2001
      *
      * I'm tired of the arguing and bug reports.  Bash now leaves SSH_CLIENT
      * and SSH2_CLIENT alone.  I'm going to rely on the shell_level check in
      * isnetconn() to avoid running the startup files more often than wanted.
      * That will, of course, only work if the user's login shell is bash, so
      * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
      * in config-top.h.
      */
#if 0
  temp_var = find_variable ("SSH_CLIENT");
  if (temp_var && imported_p (temp_var))
    {
      VUNSETATTR (temp_var, att_exported);
      array_needs_making = 1;
    }
  temp_var = find_variable ("SSH2_CLIENT");
  if (temp_var && imported_p (temp_var))
    {
      VUNSETATTR (temp_var, att_exported);
      array_needs_making = 1;
    }
#endif

  /* Get the user's real and effective user ids. */
  uidset ();

  /* Initialize the dynamic variables, and seed their values. */
  initialize_dynamic_variables ();
}

/* **************************************************************** */
/*                                  */
/*       Setting values for special shell variables         */
/*                                  */
/* **************************************************************** */

static void
set_machine_vars ()
{
  SHELL_VAR *temp_var;

  temp_var = set_if_not ("HOSTTYPE", HOSTTYPE);
  temp_var = set_if_not ("OSTYPE", OSTYPE);
  temp_var = set_if_not ("MACHTYPE", MACHTYPE);

  temp_var = set_if_not ("HOSTNAME", current_host_name);
}

/* Set $HOME to the information in the password file if we didn't get
   it from the environment. */

/* This function is not static so the tilde and readline libraries can
   use it. */
char *
sh_get_home_dir ()
{
  if (current_user.home_dir == 0)
    get_current_user_info ();
  return current_user.home_dir;
}

static void
set_home_var ()
{
  SHELL_VAR *temp_var;

  temp_var = find_variable ("HOME");
  if (temp_var == 0)
    temp_var = bind_variable ("HOME", sh_get_home_dir ());
#if 0
  VSETATTR (temp_var, att_exported);
#endif
}

/* Set $SHELL to the user's login shell if it is not already set.  Call
   get_current_user_info if we haven't already fetched the shell. */
static void
set_shell_var ()
{
  SHELL_VAR *temp_var;

  temp_var = find_variable ("SHELL");
  if (temp_var == 0)
    {
      if (current_user.shell == 0)
    get_current_user_info ();
      temp_var = bind_variable ("SHELL", current_user.shell);
    }
#if 0
  VSETATTR (temp_var, att_exported);
#endif
}

static char *
get_bash_name ()
{
  char *name;

  if ((login_shell == 1) && RELPATH(shell_name))
    {
      if (current_user.shell == 0)
    get_current_user_info ();
      name = savestring (current_user.shell);
    }
  else if (ABSPATH(shell_name))
    name = savestring (shell_name);
  else if (shell_name[0] == '.' && shell_name[1] == '/')
    {
      /* Fast path for common case. */
      char *cdir;
      int len;

      cdir = get_string_value ("PWD");
      if (cdir)
    {
      len = strlen (cdir);
      name = (char *)xmalloc (len + strlen (shell_name) + 1);
      strcpy (name, cdir);
      strcpy (name + len, shell_name + 1);
    }
      else
    name = savestring (shell_name);
    }
  else
    {
      char *tname;
      int s;

      tname = find_user_command (shell_name);

      if (tname == 0)
    {
      /* Try the current directory.  If there is not an executable
         there, just punt and use the login shell. */
      s = file_status (shell_name);
      if (s & FS_EXECABLE)
        {
          tname = make_absolute (shell_name, get_string_value ("PWD"));
          if (*shell_name == '.')
        {
          name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
          if (name == 0)
            name = tname;
          else
            free (tname);
        }
         else
        name = tname;
        }
      else
        {
          if (current_user.shell == 0)
        get_current_user_info ();
          name = savestring (current_user.shell);
        }
    }
      else
    {
      name = full_pathname (tname);
      free (tname);
    }
    }

  return (name);
}

void
adjust_shell_level (change)
     int change;
{
  char new_level[5], *old_SHLVL;
  intmax_t old_level;
  SHELL_VAR *temp_var;

  old_SHLVL = get_string_value ("SHLVL");
  if (old_SHLVL == 0 || *old_SHLVL == '\0' || legal_number (old_SHLVL, &old_level) == 0)
    old_level = 0;

  shell_level = old_level + change;
  if (shell_level < 0)
    shell_level = 0;
  else if (shell_level > 1000)
    {
      internal_warning ("shell level (%d) too high, resetting to 1", shell_level);
      shell_level = 1;
    }

  /* We don't need the full generality of itos here. */
  if (shell_level < 10)
    {
      new_level[0] = shell_level + '0';
      new_level[1] = '\0';
    }
  else if (shell_level < 100)
    {
      new_level[0] = (shell_level / 10) + '0';
      new_level[1] = (shell_level % 10) + '0';
      new_level[2] = '\0';
    }
  else if (shell_level < 1000)
    {
      new_level[0] = (shell_level / 100) + '0';
      old_level = shell_level % 100;
      new_level[1] = (old_level / 10) + '0';
      new_level[2] = (old_level % 10) + '0';
      new_level[3] = '\0';
    }

  temp_var = bind_variable ("SHLVL", new_level);
  set_auto_export (temp_var);
}

static void
initialize_shell_level ()
{
  adjust_shell_level (1);
}

/* If we got PWD from the environment, update our idea of the current
   working directory.  In any case, make sure that PWD exists before
   checking it.  It is possible for getcwd () to fail on shell startup,
   and in that case, PWD would be undefined.  If this is an interactive
   login shell, see if $HOME is the current working directory, and if
   that's not the same string as $PWD, set PWD=$HOME. */

void
set_pwd ()
{
  SHELL_VAR *temp_var, *home_var;
  char *temp_string, *home_string;

  home_var = find_variable ("HOME");
  home_string = home_var ? value_cell (home_var) : (char *)NULL;

  temp_var = find_variable ("PWD");
  if (temp_var && imported_p (temp_var) &&
      (temp_string = value_cell (temp_var)) &&
      same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
    set_working_directory (temp_string);
  else if (home_string && interactive_shell && login_shell &&
       same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
    {
      set_working_directory (home_string);
      temp_var = bind_variable ("PWD", home_string);
      set_auto_export (temp_var);
    }
  else
    {
      temp_string = get_working_directory ("shell-init");
      if (temp_string)
    {
      temp_var = bind_variable ("PWD", temp_string);
      set_auto_export (temp_var);
      free (temp_string);
    }
    }

  /* According to the Single Unix Specification, v2, $OLDPWD is an
     `environment variable' and therefore should be auto-exported.
     Make a dummy invisible variable for OLDPWD, and mark it as exported. */
  temp_var = bind_variable ("OLDPWD", (char *)NULL);
  VSETATTR (temp_var, (att_exported | att_invisible));
}

/* Make a variable $PPID, which holds the pid of the shell's parent.  */
void
set_ppid ()
{
  char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name;
  SHELL_VAR *temp_var;

  name = inttostr (getppid (), namebuf, sizeof(namebuf));
  temp_var = find_variable ("PPID");
  if (temp_var)
    VUNSETATTR (temp_var, (att_readonly | att_exported));
  temp_var = bind_variable ("PPID", name);
  VSETATTR (temp_var, (att_readonly | att_integer));
}

static void
uidset ()
{
  char buff[INT_STRLEN_BOUND(uid_t) + 1], *b;
  register SHELL_VAR *v;

  b = inttostr (current_user.uid, buff, sizeof (buff));
  v = find_variable ("UID");
  if (v == 0)
    {
      v = bind_variable ("UID", b);
      VSETATTR (v, (att_readonly | att_integer));
    }

  if (current_user.euid != current_user.uid)
    b = inttostr (current_user.euid, buff, sizeof (buff));

  v = find_variable ("EUID");
  if (v == 0)
    {
      v = bind_variable ("EUID", b);
      VSETATTR (v, (att_readonly | att_integer));
    }
}

#if defined (ARRAY_VARS)
static void
make_vers_array ()
{
  SHELL_VAR *vv;
  ARRAY *av;
  char *s, d[32], b[INT_STRLEN_BOUND(int) + 1];

  unbind_variable ("BASH_VERSINFO");

  vv = make_new_array_variable ("BASH_VERSINFO");
  av = array_cell (vv);
  strcpy (d, dist_version);
  s = xstrchr (d, '.');
  if (s)
    *s++ = '\0';
  array_insert (av, 0, d);
  array_insert (av, 1, s);
  s = inttostr (patch_level, b, sizeof (b));
  array_insert (av, 2, s);
  s = inttostr (build_version, b, sizeof (b));
  array_insert (av, 3, s);
  array_insert (av, 4, release_status);
  array_insert (av, 5, MACHTYPE);

  VSETATTR (vv, att_readonly);
}
#endif /* ARRAY_VARS */

/* Set the environment variables $LINES and $COLUMNS in response to
   a window size change. */
void
sh_set_lines_and_columns (lines, cols)
     int lines, cols;
{
  char val[INT_STRLEN_BOUND(int) + 1], *v;

  v = inttostr (lines, val, sizeof (val));
  bind_variable ("LINES", v);

  v = inttostr (cols, val, sizeof (val));
  bind_variable ("COLUMNS", v);
}

/* **************************************************************** */
/*                                  */
/*         Printing variables and values            */
/*                                  */
/* **************************************************************** */

/* Print LIST (a list of shell variables) to stdout in such a way that
   they can be read back in. */
void
print_var_list (list)
     register SHELL_VAR **list;
{
  register int i;
  register SHELL_VAR *var;

  for (i = 0; list && (var = list[i]); i++)
    if (invisible_p (var) == 0)
      print_assignment (var);
}

/* Print LIST (a list of shell functions) to stdout in such a way that
   they can be read back in. */
void
print_func_list (list)
     register SHELL_VAR **list;
{
  register int i;
  register SHELL_VAR *var;

  for (i = 0; list && (var = list[i]); i++)
    {
      printf ("%s ", var->name);
      print_var_function (var);
      printf ("\n");
    }
}
      
/* Print the value of a single SHELL_VAR.  No newline is
   output, but the variable is printed in such a way that
   it can be read back in. */
void
print_assignment (var)
     SHELL_VAR *var;
{
  if (var_isset (var) == 0)
    return;

  if (function_p (var))
    {
      printf ("%s", var->name);
      print_var_function (var);
      printf ("\n");
    }
#if defined (ARRAY_VARS)
  else if (array_p (var))
    print_array_assignment (var, 0);
#endif /* ARRAY_VARS */
  else
    {
      printf ("%s=", var->name);
      print_var_value (var, 1);
      printf ("\n");
    }
}

/* Print the value cell of VAR, a shell variable.  Do not print
   the name, nor leading/trailing newline.  If QUOTE is non-zero,
   and the value contains shell metacharacters, quote the value
   in such a way that it can be read back in. */
void
print_var_value (var, quote)
     SHELL_VAR *var;
     int quote;
{
  char *t;

  if (var_isset (var) == 0)
    return;

  if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var)))
    {
      t = ansic_quote (value_cell (var), 0, (int *)0);
      printf ("%s", t);
      free (t);
    }
  else if (quote && sh_contains_shell_metas (value_cell (var)))
    {
      t = sh_single_quote (value_cell (var));
      printf ("%s", t);
      free (t);
    }
  else
    printf ("%s", value_cell (var));
}

/* Print the function cell of VAR, a shell variable.  Do not
   print the name, nor leading/trailing newline. */
void
print_var_function (var)
     SHELL_VAR *var;
{
  if (function_p (var) && var_isset (var))
    printf ("%s", named_function_string ((char *)NULL, function_cell(var), 1));
}

/* **************************************************************** */
/*                                  */
/*          Dynamic Variables               */
/*                                  */
/* **************************************************************** */

/* DYNAMIC VARIABLES

   These are variables whose values are generated anew each time they are
   referenced.  These are implemented using a pair of function pointers
   in the struct variable: assign_func, which is called from bind_variable
   and, if arrays are compiled into the shell, some of the functions in
   arrayfunc.c, and dynamic_value, which is called from find_variable.

   assign_func is called from bind_variable_internal, if
   bind_variable_internal discovers that the variable being assigned to
   has such a function.  The function is called as
    SHELL_VAR *temp = (*(entry->assign_func)) (entry, value, ind)
   and the (SHELL_VAR *)temp is returned as the value of bind_variable.  It
   is usually ENTRY (self).  IND is an index for an array variable, and
   unused otherwise.

   dynamic_value is called from find_variable_internal to return a `new'
   value for the specified dynamic varible.  If this function is NULL,
   the variable is treated as a `normal' shell variable.  If it is not,
   however, then this function is called like this:
    tempvar = (*(var->dynamic_value)) (var);

   Sometimes `tempvar' will replace the value of `var'.  Other times, the
   shell will simply use the string value.  Pretty object-oriented, huh?

   Be warned, though: if you `unset' a special variable, it loses its
   special meaning, even if you subsequently set it.

   The special assignment code would probably have been better put in
   subst.c: do_assignment_internal, in the same style as
   stupidly_hack_special_variables, but I wanted the changes as
   localized as possible.  */

#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
  do \
    { \
      v = bind_variable (var, (val)); \
      v->dynamic_value = gfunc; \
      v->assign_func = afunc; \
    } \
  while (0)

#define INIT_DYNAMIC_ARRAY_VAR(var, gfunc, afunc) \
  do \
    { \
      v = make_new_array_variable (var); \
      v->dynamic_value = gfunc; \
      v->assign_func = afunc; \
    } \
  while (0)

static SHELL_VAR *
null_assign (self, value, unused)
     SHELL_VAR *self;
     char *value;
     arrayind_t unused;
{
  return (self);
}

#if defined (ARRAY_VARS)
static SHELL_VAR *
null_array_assign (self, value, ind)
     SHELL_VAR *self;
     char *value;
     arrayind_t ind;
{
  return (self);
}
#endif

/* The value of $SECONDS.  This is the number of seconds since shell
   invocation, or, the number of seconds since the last assignment + the
   value of the last assignment. */
static intmax_t seconds_value_assigned;

static SHELL_VAR *
assign_seconds (self, value, unused)
     SHELL_VAR *self;
     char *value;
     arrayind_t unused;
{
  if (legal_number (value, &seconds_value_assigned) == 0)
    seconds_value_assigned = 0;
  shell_start_time = NOW;
  return (self);
}

static SHELL_VAR *
get_seconds (var)
     SHELL_VAR *var;
{
  time_t time_since_start;
  char *p;

  time_since_start = NOW - shell_start_time;
  p = itos(seconds_value_assigned + time_since_start);

  FREE (value_cell (var));

  VSETATTR (var, att_integer);
  var_setvalue (var, p);
  return (var);
}

static SHELL_VAR *
init_seconds_var ()
{
  SHELL_VAR *v;

  v = find_variable ("SECONDS");
  if (v)
    {
      if (legal_number (value_cell(v), &seconds_value_assigned) == 0)
    seconds_value_assigned = 0;
    }
  INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds);
  return v;      
}
     
/* The random number seed.  You can change this by setting RANDOM. */
static unsigned long rseed = 1;
static int last_random_value;

/* A linear congruential random number generator based on the example
   one in the ANSI C standard.  This one isn't very good, but a more
   complicated one is overkill. */

/* Returns a pseudo-random number between 0 and 32767. */
static int
brand ()
{
  rseed = rseed * 1103515245 + 12345;
  return ((unsigned int)((rseed >> 16) & 32767));   /* was % 32768 */
}

/* Set the random number generator seed to SEED. */
static void
sbrand (seed)
     unsigned long seed;
{
  rseed = seed;
  last_random_value = 0;
}

static SHELL_VAR *
assign_random (self, value, unused)
     SHELL_VAR *self;
     char *value;
     arrayind_t unused;
{
  sbrand (strtoul (value, (char **)NULL, 10));
  return (self);
}

int
get_random_number ()
{
  int rv;

  /* Reset for command and process substitution. */
  if (subshell_environment)
    sbrand (rseed + getpid() + NOW);

  do
    rv = brand ();
  while (rv == last_random_value);
  return rv;
}

static SHELL_VAR *
get_random (var)
     SHELL_VAR *var;
{
  int rv;
  char *p;

  rv = get_random_number ();
  last_random_value = rv;
  p = itos (rv);

  FREE (value_cell (var));

  VSETATTR (var, att_integer);
  var_setvalue (var, p);
  return (var);
}

static SHELL_VAR *
assign_lineno (var, value, unused)
     SHELL_VAR *var;
     char *value;
     arrayind_t unused;
{
  intmax_t new_value;

  if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
    new_value = 0;
  line_number = new_value;
  return var;
}

/* Function which returns the current line number. */
static SHELL_VAR *
get_lineno (var)
     SHELL_VAR *var;
{
  char *p;
  int ln;

  ln = executing_line_number ();
  p = itos (ln);
  FREE (value_cell (var));
  var_setvalue (var, p);
  return (var);
}

#if defined (HISTORY)
static SHELL_VAR *
get_histcmd (var)
     SHELL_VAR *var;
{
  char *p;

  p = itos (history_number ());
  FREE (value_cell (var));
  var_setvalue (var, p);
  return (var);
}
#endif

#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
static  SHELL_VAR *
assign_dirstack (self, value, ind)
     SHELL_VAR *self;
     char *value;
     arrayind_t ind;
{
  set_dirstack_element (ind, 1, value);
  return self;
}

static SHELL_VAR *
get_dirstack (self)
     SHELL_VAR *self;
{
  ARRAY *a;
  WORD_LIST *l;

  l = get_directory_stack ();
  a = array_from_word_list (l);
  array_dispose (array_cell (self));
  dispose_words (l);
  var_setarray (self, a);
  return self;
}

static SHELL_VAR *
init_dirstack_var ()
{
  SHELL_VAR *v;

  v = find_variable ("DIRSTACK");
  if (v)
    return v;
  INIT_DYNAMIC_ARRAY_VAR ("DIRSTACK", get_dirstack, assign_dirstack);
  return v;
}
#endif /* PUSHD AND POPD && ARRAY_VARS */

#if defined (ARRAY_VARS)
/* We don't want to initialize the group set with a call to getgroups()
   unless we're asked to, but we only want to do it once. */
static SHELL_VAR *
get_groupset (self)
     SHELL_VAR *self;
{
  register int i;
  int ng;
  ARRAY *a;
  static char **group_set = (char **)NULL;

  if (group_set == 0)
    {
      group_set = get_group_list (&ng);
      a = array_cell (self);
      for (i = 0; i < ng; i++)
    array_insert (a, i, group_set[i]);
    }
  return (self);
}

static SHELL_VAR *
init_groups_var ()
{
  SHELL_VAR *v;

  v = find_variable ("GROUPS");
  if (v)
    return (v);
  INIT_DYNAMIC_ARRAY_VAR ("GROUPS", get_groupset, null_array_assign);
  VSETATTR (v, att_noassign);
  return v;
}
#endif /* ARRAY_VARS */

static SHELL_VAR *
get_funcname (self)
     SHELL_VAR *self;
{
  char *t;
  if (variable_context && this_shell_function)
    {
      FREE (value_cell (self));
      t = savestring (this_shell_function->name);
      var_setvalue (self, t);
    }
  return (self);
}

void
make_funcname_visible (on_or_off)
     int on_or_off;
{
  SHELL_VAR *v;

  v = find_variable ("FUNCNAME");
  if (v == 0 || v->dynamic_value == 0)
    return;

  if (on_or_off)
    VUNSETATTR (v, att_invisible);
  else
    VSETATTR (v, att_invisible);
}

static SHELL_VAR *
init_funcname_var ()
{
  SHELL_VAR *v;

  v = find_variable ("FUNCNAME");
  if (v)
    return v;
  INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign);
  VSETATTR (v, att_invisible|att_noassign);
  return v;
}

static void
initialize_dynamic_variables ()
{
  SHELL_VAR *v;

  v = init_seconds_var ();

  INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
  INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno);

#if defined (HISTORY)
  INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL);
#endif

#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
  v = init_dirstack_var ();
#endif /* PUSHD_AND_POPD && ARRAY_VARS */

#if defined (ARRAY_VARS)
  v = init_groups_var ();
#endif

  v = init_funcname_var ();
}

/* **************************************************************** */
/*                                  */
/*      Retrieving variables and values             */
/*                                  */
/* **************************************************************** */

/* How to get a pointer to the shell variable or function named NAME.
   HASHED_VARS is a pointer to the hash table containing the list
   of interest (either variables or functions). */

static SHELL_VAR *
hash_lookup (name, hashed_vars)
     const char *name;
     HASH_TABLE *hashed_vars;
{
  BUCKET_CONTENTS *bucket;

  bucket = hash_search (name, hashed_vars, 0);
  return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL);
}

SHELL_VAR *
var_lookup (name, vcontext)
     const char *name;
     VAR_CONTEXT *vcontext;
{
  VAR_CONTEXT *vc;
  SHELL_VAR *v;

  v = (SHELL_VAR *)NULL;
  for (vc = vcontext; vc; vc = vc->down)
    if (v = hash_lookup (name, vc->table))
      break;

  return v;
}

/* Look up the variable entry named NAME.  If SEARCH_TEMPENV is non-zero,
   then also search the temporarily built list of exported variables.
   The lookup order is:
    temporary_env
        shell_variables list
*/

SHELL_VAR *
find_variable_internal (name, search_tempenv)
     const char *name;
     int search_tempenv;
{
  SHELL_VAR *var;

  var = (SHELL_VAR *)NULL;

  /* If explicitly requested, first look in the temporary environment for
     the variable.  This allows constructs such as "foo=x eval 'echo $foo'"
     to get the `exported' value of $foo.  This happens if we are executing
     a function or builtin, or if we are looking up a variable in a
     "subshell environment". */
  if ((search_tempenv || subshell_environment) && temporary_env)
    var = hash_lookup (name, temporary_env);

  if (var == 0)
    var = var_lookup (name, shell_variables);

  if (var == 0)
    return ((SHELL_VAR *)NULL);

  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
}

/* Look up the variable entry named NAME.  Returns the entry or NULL. */
SHELL_VAR *
find_variable (name)
     const char *name;
{
  return (find_variable_internal (name, this_shell_builtin != 0));
}

/* Look up the function entry whose name matches STRING.
   Returns the entry or NULL. */
SHELL_VAR *
find_function (name)
     const char *name;
{
  return (hash_lookup (name, shell_functions));
}

/* Return the value of VAR.  VAR is assumed to have been the result of a
   lookup without any subscript, if arrays are compiled into the shell. */
char *
get_variable_value (var)
     SHELL_VAR *var;
{
  if (var == 0)
    return ((char *)NULL);
#if defined (ARRAY_VARS)
  else if (array_p (var))
    return (array_reference (array_cell (var), 0));
#endif
  else
    return (value_cell (var));
}

/* Return the string value of a variable.  Return NULL if the variable
   doesn't exist.  Don't cons a new string.  This is a potential memory
   leak if the variable is found in the temporary environment.  Since
   functions and variables have separate name spaces, returns NULL if
   var_name is a shell function only. */
char *
get_string_value (var_name)
     const char *var_name;
{
  SHELL_VAR *var;

  var = find_variable (var_name);
  return ((var) ? get_variable_value (var) : (char *)NULL);
}

/* This is present for use by the tilde and readline libraries. */
char *
sh_get_env_value (v)
     const char *v;
{
  return get_string_value (v);
}

/* **************************************************************** */
/*                                  */
/*        Creating and setting variables            */
/*                                  */
/* **************************************************************** */

/* Set NAME to VALUE if NAME has no value. */
SHELL_VAR *
set_if_not (name, value)
     char *name, *value;
{
  SHELL_VAR *v;

  v = find_variable (name);
  if (v == 0)
    v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH);
  return (v);
}

/* Create a local variable referenced by NAME. */
SHELL_VAR *
make_local_variable (name)
     const char *name;
{
  SHELL_VAR *new_var, *old_var;
  VAR_CONTEXT *vc;
  int was_tmpvar;
  char *tmp_value;

  /* local foo; local foo;  is a no-op. */
  old_var = find_variable (name);
  if (old_var && local_p (old_var) && old_var->context == variable_context)
    return (old_var);

  was_tmpvar = old_var && tempvar_p (old_var);
  if (was_tmpvar)
    tmp_value = value_cell (old_var);

  for (vc = shell_variables; vc; vc = vc->down)
    if (vc_isfuncenv (vc) && vc->scope == variable_context)
      break;

  if (vc == 0)
    {
      internal_error ("make_local_variable: no function context at current scope found");
      return ((SHELL_VAR *)NULL);
    }
  else if (vc->table == 0)
    vc->table = hash_create (TEMPENV_HASH_BUCKETS);

  /* Since this is called only from the local/declare/typeset code, we can
     call builtin_error here without worry (of course, it will also work
     for anything that sets this_command_name).  Variables with the `noassign'
     attribute may not be made local.  The test against old_var's context
     level is to disallow local copies of readonly global variables (since I
     believe that this could be a security hole).  Readonly copies of calling
     function local variables are OK. */
  if (old_var && (noassign_p (old_var) ||
         (readonly_p (old_var) && old_var->context == 0)))
    {
      if (readonly_p (old_var))
    sh_readonly (name);
      return ((SHELL_VAR *)NULL);
    }

  if (old_var == 0)
    new_var = bind_variable_internal (name, "", vc->table, HASH_NOSRCH);
  else
    {
      new_var = make_new_variable (name, vc->table);

      /* If we found this variable in one of the temporary environments,
     inherit its value.  Watch to see if this causes problems with
     things like `x=4 local x'. */
      if (was_tmpvar)
        var_setvalue (new_var, savestring (tmp_value));

      new_var->attributes = exported_p (old_var) ? att_exported : 0;
    }

  vc->flags |= VC_HASLOCAL;

  new_var->context = variable_context;
  VSETATTR (new_var, att_local);

  if (ifsname (name))
    setifs (new_var);

  return (new_var);
}

#if defined (ARRAY_VARS)
SHELL_VAR *
make_local_array_variable (name)
     char *name;
{
  SHELL_VAR *var;
  ARRAY *array;

  var = make_local_variable (name);
  if (var == 0)
    return var;
  array = array_create ();

  FREE (value_cell(var));
  var_setarray (var, array);
  VSETATTR (var, att_array);
  return var;
}
#endif /* ARRAY_VARS */

/* Create a new shell variable with name NAME. */
static SHELL_VAR *
new_shell_variable (name)
     const char *name;
{
  SHELL_VAR *entry;

  entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));

  entry->name = savestring (name);
  var_setvalue (entry, (char *)NULL);
  CLEAR_EXPORTSTR (entry);

  entry->dynamic_value = (sh_var_value_func_t *)NULL;
  entry->assign_func = (sh_var_assign_func_t *)NULL;

  entry->attributes = 0;

  /* Always assume variables are to be made at toplevel!
     make_local_variable has the responsibilty of changing the
     variable context. */
  entry->context = 0;

  return (entry);
}

/* Create a new shell variable with name NAME and add it to the hash table
   TABLE. */
static SHELL_VAR *
make_new_variable (name, table)
     const char *name;
     HASH_TABLE *table;
{
  SHELL_VAR *entry;
  BUCKET_CONTENTS *elt;

  entry = new_shell_variable (name);

  /* Make sure we have a shell_variables hash table to add to. */
  if (shell_variables == 0)
    {
      shell_variables = global_variables = new_var_context ((char *)NULL, 0);
      shell_variables->scope = 0;
      shell_variables->table = hash_create (0);
    }

  elt = hash_insert (savestring (name), table, HASH_NOSRCH);
  elt->data = (PTR_T)entry;

  return entry;
}

#if defined (ARRAY_VARS)
SHELL_VAR *
make_new_array_variable (name)
     char *name;
{
  SHELL_VAR *entry;
  ARRAY *array;

  entry = make_new_variable (name, global_variables->table);
  array = array_create ();
  var_setarray (entry, array);
  VSETATTR (entry, att_array);
  return entry;
}
#endif

char *
make_variable_value (var, value)
     SHELL_VAR *var;
     char *value;
{
  char *retval;
  intmax_t lval;
  int expok;

  /* If this variable has had its type set to integer (via `declare -i'),
     then do expression evaluation on it and store the result.  The
     functions in expr.c (evalexp()) and bind_int_variable() are responsible
     for turning off the integer flag if they don't want further
     evaluation done. */
  if (integer_p (var))
    {
      lval = evalexp (value, &expok);
      if (expok == 0)
    jump_to_top_level (DISCARD);
      retval = itos (lval);
    }
  else if (value)
    {
      if (*value)
    retval = savestring (value);
      else
    {
      retval = (char *)xmalloc (1);
      retval[0] = '\0';
    }
    }
  else
    retval = (char *)NULL;

  return retval;
}

/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
   temporary environment (but usually is not). */
static SHELL_VAR *
bind_variable_internal (name, value, table, hflags)
     const char *name;
     char *value;
     HASH_TABLE *table;
     int hflags;
{
  char *newval;
  SHELL_VAR *entry;

  entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);

  if (entry == 0)
    {
      entry = make_new_variable (name, table);
      var_setvalue (entry, make_variable_value (entry, value));
    }
  else if (entry->assign_func)  /* array vars have assign functions now */
    {
      INVALIDATE_EXPORTSTR (entry);
      return ((*(entry->assign_func)) (entry, value, -1));
    }
  else
    {
      if (readonly_p (entry) || noassign_p (entry))
    {
      if (readonly_p (entry))
        err_readonly (name);
      return (entry);
    }

      /* Variables which are bound are visible. */
      VUNSETATTR (entry, att_invisible);

      newval = make_variable_value (entry, value);

      /* Invalidate any cached export string */
      INVALIDATE_EXPORTSTR (entry);

#if defined (ARRAY_VARS)
      /* XXX -- this bears looking at again -- XXX */
      /* If an existing array variable x is being assigned to with x=b or
     `read x' or something of that nature, silently convert it to
     x[0]=b or `read x[0]'. */
      if (array_p (entry))
    {
      array_insert (array_cell (entry), 0, newval);
      free (newval);
    }
      else
#endif
    {
      FREE (value_cell (entry));
      var_setvalue (entry, newval);
    }
    }

  if (mark_modified_vars)
    VSETATTR (entry, att_exported);

  if (exported_p (entry))
    array_needs_making = 1;

  return (entry);
}
    
/* Bind a variable NAME to VALUE.  This conses up the name
   and value strings.  If we have a temporary environment, we bind there
   first, then we bind into shell_variables. */

SHELL_VAR *
bind_variable (name, value)
     const char *name;
     char *value;
{
  SHELL_VAR *v;
  VAR_CONTEXT *vc;

  if (shell_variables == 0)
    {
      shell_variables = global_variables = new_var_context ((char *)NULL, 0);
      shell_variables->scope = 0;
      shell_variables->table = hash_create (0);
    }

  /* If we have a temporary environment, look there first for the variable,
     and, if found, modify the value there before modifying it in the
     shell_variables table.  This allows sourced scripts to modify values
     given to them in a temporary environment while modifying the variable
     value that the caller sees. */
  if (temporary_env)
    bind_tempenv_variable (name, value);

  /* XXX -- handle local variables here. */
  for (vc = shell_variables; vc; vc = vc->down)
    {
      if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
        {
          v = hash_lookup (name, vc->table);
          if (v)
        return (bind_variable_internal (name, value, vc->table, 0));
        }
    }
  return (bind_variable_internal (name, value, global_variables->table, 0));
}

/* Make VAR, a simple shell variable, have value VALUE.  Once assigned a
   value, variables are no longer invisible.  This is a duplicate of part
   of the internals of bind_variable.  If the variable is exported, or
   all modified variables should be exported, mark the variable for export
   and note that the export environment needs to be recreated. */
SHELL_VAR *
bind_variable_value (var, value)
     SHELL_VAR *var;
     char *value;
{
  char *t;

  VUNSETATTR (var, att_invisible);

  t = make_variable_value (var, value);
  FREE (value_cell (var));
  var_setvalue (var, t);

  INVALIDATE_EXPORTSTR (var);

  if (mark_modified_vars)
    VSETATTR (var, att_exported);

  if (exported_p (var))
    array_needs_making = 1;

  return (var);
}

/* Bind/create a shell variable with the name LHS to the RHS.
   This creates or modifies a variable such that it is an integer.

   This used to be in expr.c, but it is here so that all of the
   variable binding stuff is localized.  Since we don't want any
   recursive evaluation from bind_variable() (possible without this code,
   since bind_variable() calls the evaluator for variables with the integer
   attribute set), we temporarily turn off the integer attribute for each
   variable we set here, then turn it back on after binding as necessary. */

SHELL_VAR *
bind_int_variable (lhs, rhs)
     char *lhs, *rhs;
{
  register SHELL_VAR *v;
  char *t;
  int isint, isarr;

  isint = isarr = 0;
#if defined (ARRAY_VARS)
  if (t = xstrchr (lhs, '['))   /*]*/
    {
      isarr = 1;
      v = array_variable_part (lhs, (char **)0, (int *)0);
    }
  else
#endif
    v = find_variable (lhs);

  if (v)
    {
      isint = integer_p (v);
      VUNSETATTR (v, att_integer);
    }

#if defined (ARRAY_VARS)
  if (isarr)
    v = assign_array_element (lhs, rhs);
  else
#endif
    v = bind_variable (lhs, rhs);

  if (isint)
    VSETATTR (v, att_integer);

  return (v);
}

SHELL_VAR *
bind_var_to_int (var, val)
     char *var;
     intmax_t val;
{
  char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;

  p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0);
  return (bind_int_variable (var, p));
}

/* Do a function binding to a variable.  You pass the name and
   the command to bind to.  This conses the name and command. */
SHELL_VAR *
bind_function (name, value)
     const char *name;
     COMMAND *value;
{
  SHELL_VAR *entry;

  entry = find_function (name);
  if (entry == 0)
    {
      BUCKET_CONTENTS *elt;

      elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH);
      entry = new_shell_variable (name);
      elt->data = (PTR_T)entry;
    }
  else
    INVALIDATE_EXPORTSTR (entry);

  if (var_isset (entry))
    dispose_command (function_cell (entry));

  if (value)
    var_setfunc (entry, copy_command (value));
  else
    var_setfunc (entry, 0);

  VSETATTR (entry, att_function);

  if (mark_modified_vars)
    VSETATTR (entry, att_exported);

  VUNSETATTR (entry, att_invisible);        /* Just to be sure */

  if (exported_p (entry))
    array_needs_making = 1;

#if defined (PROGRAMMABLE_COMPLETION)
  set_itemlist_dirty (&it_functions);
#endif

  return (entry);
}

/* Add STRING, which is of the form foo=bar, to the temporary environment
   HASH_TABLE (temporary_env).  The functions in execute_cmd.c are
   responsible for moving the main temporary env to one of the other
   temporary environments.  The expansion code in subst.c calls this. */
int
assign_in_env (string)
     const char *string;
{
  int offset;
  char *name, *temp, *value;
  SHELL_VAR *var;

  offset = assignment (string);
  name = savestring (string);
  value = (char *)NULL;

  if (name[offset] == '=')
    {
      name[offset] = 0;

      var = find_variable (name);
      if (var && (readonly_p (var) || noassign_p (var)))
    {
      if (readonly_p (var))
        err_readonly (name);
      free (name);
      return (0);
    }

      temp = name + offset + 1;
      temp = (xstrchr (temp, '~') != 0) ? bash_tilde_expand (temp, 1) : savestring (temp);

      value = expand_string_unsplit_to_string (temp, 0);
      free (temp);
    }

  if (temporary_env == 0)
    temporary_env = hash_create (TEMPENV_HASH_BUCKETS);

  var = hash_lookup (name, temporary_env);
  if (var == 0)
    var = make_new_variable (name, temporary_env);
  else
    FREE (value_cell (var));

  if (value == 0)
    {
      value = (char *)xmalloc (1);  /* like do_assignment_internal */
      value[0] = '\0';
    }

  var_setvalue (var, value);
  var->attributes |= (att_exported|att_tempvar);
  var->context = variable_context;  /* XXX */

  INVALIDATE_EXPORTSTR (var);
  var->exportstr = mk_env_string (name, value);

  array_needs_making = 1;

  if (ifsname (name))
    setifs (var);

  if (echo_command_at_execute)
    {
      /* The Korn shell prints the `+ ' in front of assignment statements,
     so we do too. */
      fprintf (stderr, "%s%s=%s\n", indirection_level_string (), name, value);
      fflush (stderr);
    }

  return 1;
}

/* **************************************************************** */
/*                                  */
/*          Copying variables               */
/*                                  */
/* **************************************************************** */

#ifdef INCLUDE_UNUSED
/* Copy VAR to a new data structure and return that structure. */
SHELL_VAR *
copy_variable (var)
     SHELL_VAR *var;
{
  SHELL_VAR *copy = (SHELL_VAR *)NULL;

  if (var)
    {
      copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));

      copy->attributes = var->attributes;
      copy->name = savestring (var->name);

      if (function_p (var))
    var_setfunc (copy, copy_command (function_cell (var)));
#if defined (ARRAY_VARS)
      else if (array_p (var))
    var_setarray (copy, dup_array (array_cell (var)));
#endif
      else if (value_cell (var))
    var_setvalue (copy, savestring (value_cell (var)));
      else
    var_setvalue (copy, (char *)NULL);

      copy->dynamic_value = var->dynamic_value;
      copy->assign_func = var->assign_func;

      copy->exportstr = COPY_EXPORTSTR (var);

      copy->context = var->context;
    }
  return (copy);
}
#endif

/* **************************************************************** */
/*                                  */
/*        Deleting and unsetting variables          */
/*                                  */
/* **************************************************************** */

/* Dispose of the information attached to VAR. */
void
dispose_variable (var)
     SHELL_VAR *var;
{
  if (var == 0)
    return;

  if (function_p (var))
    dispose_command (function_cell (var));
#if defined (ARRAY_VARS)
  else if (array_p (var))
    array_dispose (array_cell (var));
#endif
  else
    FREE (value_cell (var));

  FREE_EXPORTSTR (var);

  free (var->name);

  if (exported_p (var))
    array_needs_making = 1;

  free (var);
}

/* Unset the shell variable referenced by NAME. */
int
unbind_variable (name)
     const char *name;
{
  return makunbound (name, shell_variables);
}

/* Unset the shell function named NAME. */
int
unbind_func (name)
     const char *name;
{
  BUCKET_CONTENTS *elt;
  SHELL_VAR *func;

  elt = hash_remove (name, shell_functions, 0);

  if (elt == 0)
    return -1;

#if defined (PROGRAMMABLE_COMPLETION)
  set_itemlist_dirty (&it_functions);
#endif

  func = (SHELL_VAR *)elt->data;
  if (func)
    {
      if (exported_p (func))
    array_needs_making++;
      dispose_variable (func);
    }

  free (elt->key);
  free (elt);

  return 0;  
}

/* Make the variable associated with NAME go away.  HASH_LIST is the
   hash table from which this variable should be deleted (either
   shell_variables or shell_functions).
   Returns non-zero if the variable couldn't be found. */
int
makunbound (name, vc)
     const char *name;
     VAR_CONTEXT *vc;
{
  BUCKET_CONTENTS *elt, *new_elt;
  SHELL_VAR *old_var;
  VAR_CONTEXT *v;
  char *t;

  for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
    if (elt = hash_remove (name, v->table, 0))
      break;

  if (elt == 0)
    return (-1);

  old_var = (SHELL_VAR *)elt->data;

  if (old_var && exported_p (old_var))
    array_needs_making++;

  /* If we're unsetting a local variable and we're still executing inside
     the function, just mark the variable as invisible.  The function
     eventually called by pop_var_context() will clean it up later.  This
     must be done so that if the variable is subsequently assigned a new
     value inside the function, the `local' attribute is still present.
     We also need to add it back into the correct hash table. */
  if (old_var && local_p (old_var) && variable_context == old_var->context)
    {
      VSETATTR (old_var, att_invisible);
      FREE (value_cell (old_var));
      var_setvalue (old_var, (char *)NULL);
      INVALIDATE_EXPORTSTR (old_var);

      new_elt = hash_insert (savestring (old_var->name), v->table, 0);
      new_elt->data = (PTR_T)old_var;
      stupidly_hack_special_variables (old_var->name);

      free (elt->key);
      free (elt);
      return (0);
    }

  /* Have to save a copy of name here, because it might refer to
     old_var->name.  If so, stupidly_hack_special_variables will
     reference freed memory. */
  t = savestring (name);

  free (elt->key);
  free (elt);

  dispose_variable (old_var);
  stupidly_hack_special_variables (t);
  free (t);

  return (0);
}

/* Get rid of all of the variables in the current context. */
void
kill_all_local_variables ()
{
  VAR_CONTEXT *vc;

  for (vc = shell_variables; vc; vc = vc->down)
    if (vc_isfuncenv (vc) && vc->scope == variable_context)
      break;
  if (vc == 0)
    return;     /* XXX */

  if (vc->table && vc_haslocals (vc))
    {
      delete_all_variables (vc->table);
      hash_dispose (vc->table);
    }
  vc->table = (HASH_TABLE *)NULL;
}

static void
free_variable_hash_data (data)
     PTR_T data;
{
  SHELL_VAR *var;

  var = (SHELL_VAR *)data;
  dispose_variable (var);
}

/* Delete the entire contents of the hash table. */
void
delete_all_variables (hashed_vars)
     HASH_TABLE *hashed_vars;
{
  hash_flush (hashed_vars, free_variable_hash_data);
}

/* **************************************************************** */
/*                                  */
/*           Setting variable attributes            */
/*                                  */
/* **************************************************************** */

#define FIND_OR_MAKE_VARIABLE(name, entry) \
  do \
    { \
      entry = find_variable (name); \
      if (!entry) \
    { \
      entry = bind_variable (name, ""); \
      if (!no_invisible_vars) entry->attributes |= att_invisible; \
    } \
    } \
  while (0)

/* Make the variable associated with NAME be readonly.
   If NAME does not exist yet, create it. */
void
set_var_read_only (name)
     char *name;
{
  SHELL_VAR *entry;

  FIND_OR_MAKE_VARIABLE (name, entry);
  VSETATTR (entry, att_readonly);
}

#ifdef INCLUDE_UNUSED
/* Make the function associated with NAME be readonly.
   If NAME does not exist, we just punt, like auto_export code below. */
void
set_func_read_only (name)
     const char *name;
{
  SHELL_VAR *entry;

  entry = find_function (name);
  if (entry)
    VSETATTR (entry, att_readonly);
}

/* Make the variable associated with NAME be auto-exported.
   If NAME does not exist yet, create it. */
void
set_var_auto_export (name)
     char *name;
{
  SHELL_VAR *entry;

  FIND_OR_MAKE_VARIABLE (name, entry);
  set_auto_export (entry);
}

/* Make the function associated with NAME be auto-exported. */
void
set_func_auto_export (name)
     const char *name;
{
  SHELL_VAR *entry;

  entry = find_function (name);
  if (entry)
    set_auto_export (entry);
}
#endif

/* **************************************************************** */
/*                                  */
/*           Creating lists of variables            */
/*                                  */
/* **************************************************************** */

static VARLIST *
vlist_alloc (nentries)
     int nentries;
{
  VARLIST  *vlist;

  vlist = (VARLIST *)xmalloc (sizeof (VARLIST));
  vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *));
  vlist->list_size = nentries;
  vlist->list_len = 0;
  vlist->list[0] = (SHELL_VAR *)NULL;

  return vlist;
}

static VARLIST *
vlist_realloc (vlist, n)
     VARLIST *vlist;
     int n;
{
  if (vlist == 0)
    return (vlist = vlist_alloc (n));
  if (n > vlist->list_size)
    {
      vlist->list_size = n;
      vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *));
    }
  return vlist;
}

static void
vlist_add (vlist, var, flags)
     VARLIST *vlist;
     SHELL_VAR *var;
     int flags;
{
  register int i;

  for (i = 0; i < vlist->list_len; i++)
    if (STREQ (var->name, vlist->list[i]->name))
      break;
  if (i < vlist->list_len)
    return;

  if (i >= vlist->list_size)
    vlist = vlist_realloc (vlist, vlist->list_size + 16);

  vlist->list[vlist->list_len++] = var;
  vlist->list[vlist->list_len] = (SHELL_VAR *)NULL;
}

/* Map FUNCTION over the variables in VAR_HASH_TABLE.  Return an array of the
   variables for which FUNCTION returns a non-zero value.  A NULL value
   for FUNCTION means to use all variables. */
SHELL_VAR **
map_over (function, vc)
     sh_var_map_func_t *function;
     VAR_CONTEXT *vc;
{
  VAR_CONTEXT *v;
  VARLIST *vlist;
  SHELL_VAR **ret;
  int nentries;

  for (nentries = 0, v = vc; v; v = v->down)
    nentries += HASH_ENTRIES (v->table);

  if (nentries == 0)
    return (SHELL_VAR **)NULL;

  vlist = vlist_alloc (nentries);

  for (v = vc; v; v = v->down)
    flatten (v->table, function, vlist, 0);

  ret = vlist->list;
  free (vlist);
  return ret;
}

SHELL_VAR **
map_over_funcs (function)
     sh_var_map_func_t *function;
{
  VARLIST *vlist;
  SHELL_VAR **ret;

  if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0)
    return ((SHELL_VAR **)NULL);

  vlist = vlist_alloc (HASH_ENTRIES (shell_functions));

  flatten (shell_functions, function, vlist, 0);

  ret = vlist->list;
  free (vlist);
  return ret;
}

/* Flatten VAR_HASH_TABLE, applying FUNC to each member and adding those
   elements for which FUNC succeeds to VLIST->list.  FLAGS is reserved
   for future use.  Only unique names are added to VLIST.  If FUNC is
   NULL, each variable in VAR_HASH_TABLE is added to VLIST.  If VLIST is
   NULL, FUNC is applied to each SHELL_VAR in VAR_HASH_TABLE.  If VLIST
   and FUNC are both NULL, nothing happens. */
static void
flatten (var_hash_table, func, vlist, flags)
     HASH_TABLE *var_hash_table;
     sh_var_map_func_t *func;
     VARLIST *vlist;
     int flags;
{
  register int i;
  register BUCKET_CONTENTS *tlist;
  int r;
  SHELL_VAR *var;

  if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0))
    return;

  for (i = 0; i < var_hash_table->nbuckets; i++)
    {
      for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next)
    {
      var = (SHELL_VAR *)tlist->data;

      r = func ? (*func) (var) : 1;
      if (r && vlist)
        vlist_add (vlist, var, flags);
    }
    }
}

void
sort_variables (array)
     SHELL_VAR **array;
{
  qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp);
}

static int
qsort_var_comp (var1, var2)
     SHELL_VAR **var1, **var2;
{
  int result;

  if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
    result = strcmp ((*var1)->name, (*var2)->name);

  return (result);
}

/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for
   which FUNC succeeds to an array of SHELL_VAR *s.  Returns the array. */
static SHELL_VAR **
vapply (func)
     sh_var_map_func_t *func;
{
  SHELL_VAR **list;

  list = map_over (func, shell_variables);
  if (list /* && posixly_correct */)
    sort_variables (list);
  return (list);
}

/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for
   which FUNC succeeds to an array of SHELL_VAR *s.  Returns the array. */
static SHELL_VAR **
fapply (func)
     sh_var_map_func_t *func;
{
  SHELL_VAR **list;

  list = map_over_funcs (func);
  if (list /* && posixly_correct */)
    sort_variables (list);
  return (list);
}

/* Create a NULL terminated array of all the shell variables. */
SHELL_VAR **
all_shell_variables ()
{
  return (vapply ((sh_var_map_func_t *)NULL));
}

/* Create a NULL terminated array of all the shell functions. */
SHELL_VAR **
all_shell_functions ()
{
  return (fapply ((sh_var_map_func_t *)NULL));
}

static int
visible_var (var)
     SHELL_VAR *var;
{
  return (invisible_p (var) == 0);
}

SHELL_VAR **
all_visible_functions ()
{
  return (fapply (visible_var));
}

SHELL_VAR **
all_visible_variables ()
{
  return (vapply (visible_var));
}

/* Return non-zero if the variable VAR is visible and exported.  Array
   variables cannot be exported. */
static int
visible_and_exported (var)
     SHELL_VAR *var;
{
  return (invisible_p (var) == 0 && exported_p (var));
}

/* Return non-zero if VAR is a local variable in the current context and
   is exported. */
static int
local_and_exported (var)
     SHELL_VAR *var;
{
  return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context && exported_p (var));
}

SHELL_VAR **
all_exported_variables ()
{
  return (vapply (visible_and_exported));
}

SHELL_VAR **
local_exported_variables ()
{
  return (vapply (local_and_exported));
}

static int
variable_in_context (var)
     SHELL_VAR *var;
{
  return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context);
}

SHELL_VAR **
all_local_variables ()
{
  VARLIST *vlist;
  SHELL_VAR **ret;
  VAR_CONTEXT *vc;

  vc = shell_variables;
  for (vc = shell_variables; vc; vc = vc->down)
    if (vc_isfuncenv (vc) && vc->scope == variable_context)
      break;

  if (vc == 0)
    {
      internal_error ("all_local_variables: no function context at current scope found");
      return (SHELL_VAR **)NULL;
    }
  if (vc->table == 0 || HASH_ENTRIES (vc->table) == 0 || vc_haslocals (vc) == 0)
    return (SHELL_VAR **)NULL;
    
  vlist = vlist_alloc (HASH_ENTRIES (vc->table));

  flatten (vc->table, variable_in_context, vlist, 0);

  ret = vlist->list;
  free (vlist);
  if (ret)
    sort_variables (ret);
  return ret;
}

#if defined (ARRAY_VARS)
/* Return non-zero if the variable VAR is visible and an array. */
static int
visible_array_vars (var)
     SHELL_VAR *var;
{
  return (invisible_p (var) == 0 && array_p (var));
}

SHELL_VAR **
all_array_variables ()
{
  return (vapply (visible_array_vars));
}
#endif /* ARRAY_VARS */

char **
all_variables_matching_prefix (prefix)
     const char *prefix;
{
  SHELL_VAR **varlist;
  char **rlist;
  int vind, rind, plen;

  plen = STRLEN (prefix);
  varlist = all_visible_variables ();
  for (vind = 0; varlist && varlist[vind]; vind++)
    ;
  if (varlist == 0 || vind == 0)
    return ((char **)NULL);
  rlist = strvec_create (vind + 1);
  for (vind = rind = 0; varlist[vind]; vind++)
    {
      if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen))
    rlist[rind++] = savestring (varlist[vind]->name);
    }
  rlist[rind] = (char *)0;
  free (varlist);

  return rlist;
}

/* **************************************************************** */
/*                                  */
/*       Managing temporary variable scopes         */
/*                                  */
/* **************************************************************** */

/* Make variable NAME have VALUE in the temporary environment. */
static SHELL_VAR *
bind_tempenv_variable (name, value)
     const char *name;
     char *value;
{
  SHELL_VAR *var;

  var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL;

  if (var)
    {
      FREE (value_cell (var));
      var_setvalue (var, savestring (value));
      INVALIDATE_EXPORTSTR (var);
    }

  return (var);
}

/* Find a variable in the temporary environment that is named NAME.
   Return the SHELL_VAR *, or NULL if not found. */
SHELL_VAR *
find_tempenv_variable (name)
     const char *name;
{
  return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL);
}

/* Push the variable described by (SHELL_VAR *)DATA down to the next
   variable context from the temporary environment. */
static void
push_temp_var (data)
     PTR_T data;
{
  SHELL_VAR *var, *v;
  HASH_TABLE *binding_table;

  var = (SHELL_VAR *)data;

  binding_table = shell_variables->table;
  if (binding_table == 0)
    {
      if (shell_variables == global_variables)
    /* shouldn't happen */
    binding_table = shell_variables->table = global_variables->table = hash_create (0);
      else
    binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
    }

  v = bind_variable_internal (var->name, value_cell (var), binding_table, 0);

  /* XXX - should we set the context here?  It shouldn't matter because of how
     assign_in_env works, but might want to check. */
  if (binding_table == global_variables->table)     /* XXX */
    var->attributes &= ~(att_tempvar|att_propagate);
  else
    {
      var->attributes |= att_propagate;
      if  (binding_table == shell_variables->table)
    shell_variables->flags |= VC_HASTMPVAR;
    }
  v->attributes |= var->attributes;

  dispose_variable (var);
}

static void
propagate_temp_var (data)
     PTR_T data;
{
  SHELL_VAR *var;

  var = (SHELL_VAR *)data;
  if (tempvar_p (var) && (var->attributes & att_propagate))
    push_temp_var (data);
  else
    dispose_variable (var);
}

/* Free the storage used in the hash table for temporary
   environment variables.  PUSHF is a function to be called
   to free each hash table entry.  It takes care of pushing variables
   to previous scopes if appropriate. */
static void
dispose_temporary_env (pushf)
     sh_free_func_t *pushf;
{
  hash_flush (temporary_env, pushf);
  hash_dispose (temporary_env);
  temporary_env  = (HASH_TABLE *)NULL;

  array_needs_making = 1;

  sv_ifs ("IFS");       /* XXX here for now */
}

void
dispose_used_env_vars ()
{
  if (temporary_env)
    dispose_temporary_env (propagate_temp_var);
}

/* Take all of the shell variables in the temporary environment HASH_TABLE
   and make shell variables from them at the current variable context. */
void
merge_temporary_env ()
{
  if (temporary_env)
    dispose_temporary_env (push_temp_var);
}

/* **************************************************************** */
/*                                  */
/*       Creating and manipulating the environment          */
/*                                  */
/* **************************************************************** */

static inline char *
mk_env_string (name, value)
     const char *name, *value;
{
  int name_len, value_len;
  char  *p;

  name_len = strlen (name);
  value_len = STRLEN (value);
  p = (char *)xmalloc (2 + name_len + value_len);
  strcpy (p, name);
  p[name_len] = '=';
  if (value && *value)
    strcpy (p + name_len + 1, value);
  else
    p[name_len + 1] = '\0';
  return (p);
}

#ifdef DEBUG
/* Debugging */
static int
valid_exportstr (v)
     SHELL_VAR *v;
{
  char *s;

  s = v->exportstr;
  if (legal_variable_starter ((unsigned char)*s) == 0)
    {
      internal_error ("invalid character %d in exportstr for %s", *s, v->name);
      return (0);
    }
  for (s = v->exportstr + 1; s && *s; s++)
    {
      if (*s == '=')
    break;
      if (legal_variable_char ((unsigned char)*s) == 0)
    {
      internal_error ("invalid character %d in exportstr for %s", *s, v->name);
      return (0);
    }
    }
  if (*s != '=')
    {
      internal_error ("no `=' in exportstr for %s", v->name);
      return (0);
    }
  return (1);
}
#endif

static char **
make_env_array_from_var_list (vars)
     SHELL_VAR **vars;
{
  register int i, list_index;
  register SHELL_VAR *var;
  char **list, *value;

  list = strvec_create ((1 + strvec_len ((char **)vars)));

#define USE_EXPORTSTR (value == var->exportstr)

  for (i = 0, list_index = 0; var = vars[i]; i++)
    {
#if defined (__CYGWIN__)
      /* We don't use the exportstr stuff on Cygwin at all. */
      INVALIDATE_EXPORTSTR (var);
#endif
      if (var->exportstr)
    value = var->exportstr;
      else if (function_p (var))
    value = named_function_string ((char *)NULL, function_cell (var), 0);
#if defined (ARRAY_VARS)
      else if (array_p (var))
#  if 0
    value = array_to_assignment_string (array_cell (var));
#  else
    continue;   /* XXX array vars cannot yet be exported */
#  endif
#endif
      else
    value = value_cell (var);

      if (value)
    {
      /* Gee, I'd like to get away with not using savestring() if we're
         using the cached exportstr... */
      list[list_index] = USE_EXPORTSTR ? savestring (value)
                       : mk_env_string (var->name, value);

      if (USE_EXPORTSTR == 0)
        SAVE_EXPORTSTR (var, list[list_index]);

      list_index++;
#undef USE_EXPORTSTR

#if 0   /* not yet */
#if defined (ARRAY_VARS)
      if (array_p (var))
        free (value);
#endif
#endif
    }
    }

  list[list_index] = (char *)NULL;
  return (list);
}

/* Make an array of assignment statements from the hash table
   HASHED_VARS which contains SHELL_VARs.  Only visible, exported
   variables are eligible. */
static char **
make_var_export_array (vcxt)
     VAR_CONTEXT *vcxt;
{
  char **list;
  SHELL_VAR **vars;

  vars = map_over (visible_and_exported, vcxt);

  if (vars == 0)
    return (char **)NULL;

  list = make_env_array_from_var_list (vars);

  free (vars);
  return (list);
}

static char **
make_func_export_array ()
{
  char **list;
  SHELL_VAR **vars;

  vars = map_over_funcs (visible_and_exported);
  if (vars == 0)
    return (char **)NULL;

  list = make_env_array_from_var_list (vars);

  free (vars);
  return (list);
}

/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */
#define add_to_export_env(envstr,do_alloc) \
do \
  { \
    if (export_env_index >= (export_env_size - 1)) \
      { \
    export_env_size += 16; \
    export_env = strvec_resize (export_env, export_env_size); \
      } \
    export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \
    export_env[export_env_index] = (char *)NULL; \
  } while (0)

/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the
   array with the same left-hand side.  Return the new EXPORT_ENV. */
char **
add_or_supercede_exported_var (assign, do_alloc)
     char *assign;
     int do_alloc;
{
  register int i;
  int equal_offset;

  equal_offset = assignment (assign);
  if (equal_offset == 0)
    return (export_env);

  /* If this is a function, then only supersede the function definition.
     We do this by including the `=() {' in the comparison, like
     initialize_shell_variables does. */
  if (assign[equal_offset + 1] == '(' &&
     strncmp (assign + equal_offset + 2, ") {", 3) == 0)        /* } */
    equal_offset += 4;

  for (i = 0; i < export_env_index; i++)
    {
      if (STREQN (assign, export_env[i], equal_offset + 1))
    {
      free (export_env[i]);
      export_env[i] = do_alloc ? savestring (assign) : assign;
      return (export_env);
    }
    }
  add_to_export_env (assign, do_alloc);
  return (export_env);
}

static void
add_temp_array_to_env (temp_array, do_alloc, do_supercede)
     char **temp_array;
     int do_alloc, do_supercede;
{
  register int i;

  if (temp_array == 0)
    return;

  for (i = 0; temp_array[i]; i++)
    {
      if (do_supercede)
    export_env = add_or_supercede_exported_var (temp_array[i], do_alloc);
      else
    add_to_export_env (temp_array[i], do_alloc);
    }

  free (temp_array);
}

/* Make the environment array for the command about to be executed, if the
   array needs making.  Otherwise, do nothing.  If a shell action could
   change the array that commands receive for their environment, then the
   code should `array_needs_making++'.

   The order to add to the array is:
    temporary_env
    list of var contexts whose head is shell_variables
    shell_functions

  This is the shell variable lookup order.  We add only new variable
  names at each step, which allows local variables and variables in
  the temporary environments to shadow variables in the global (or
  any previous) scope.
*/

static int
n_shell_variables ()
{
  VAR_CONTEXT *vc;
  int n;

  for (n = 0, vc = shell_variables; vc; vc = vc->down)
    n += HASH_ENTRIES (vc->table);
  return n;
}

void
maybe_make_export_env ()
{
  register char **temp_array;
  int new_size;
  VAR_CONTEXT *tcxt;

  if (array_needs_making)
    {
      if (export_env)
        strvec_flush (export_env);

      /* Make a guess based on how many shell variables and functions we
     have.  Since there will always be array variables, and array
     variables are not (yet) exported, this will always be big enough
     for the exported variables and functions. */
      new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 +
         HASH_ENTRIES (temporary_env);
      if (new_size > export_env_size)
    {
      export_env_size = new_size;
      export_env = strvec_resize (export_env, export_env_size);
    }
      export_env[export_env_index = 0] = (char *)NULL;

      /* Make a dummy variable context from the  temporary_env, stick it on
         the front of shell_variables, call make_var_export_array on the
         whole thing to flatten it, and convert the list of SHELL_VAR *s
         to the form needed by the environment. */
      if (temporary_env)
        {
          tcxt = new_var_context ((char *)NULL, 0);
          tcxt->table = temporary_env;
          tcxt->down = shell_variables;
        }
      else
        tcxt = shell_variables;
      
      temp_array = make_var_export_array (tcxt);
      if (temp_array)
    add_temp_array_to_env (temp_array, 0, 0);

      if (tcxt != shell_variables)
        free (tcxt);

#if defined (RESTRICTED_SHELL)
      /* Restricted shells may not export shell functions. */
      temp_array = restricted ? (char **)0 : make_func_export_array ();
#else
      temp_array = make_func_export_array ();
#endif
      if (temp_array)
    add_temp_array_to_env (temp_array, 0, 0);

      array_needs_making = 0;
    }
}

/* This is an efficiency hack.  PWD and OLDPWD are auto-exported, so
   we will need to remake the exported environment every time we
   change directories.  `_' is always put into the environment for
   every external command, so without special treatment it will always
   cause the environment to be remade.

   If there is no other reason to make the exported environment, we can
   just update the variables in place and mark the exported environment
   as no longer needing a remake. */
void
update_export_env_inplace (env_prefix, preflen, value)
     char *env_prefix;
     int preflen;
     char *value;
{
  char *evar;

  evar = (char *)xmalloc (STRLEN (value) + preflen + 1);
  strcpy (evar, env_prefix);
  if (value)
    strcpy (evar + preflen, value);
  export_env = add_or_supercede_exported_var (evar, 0);
}

/* We always put _ in the environment as the name of this command. */
void
put_command_name_into_env (command_name)
     char *command_name;
{
  update_export_env_inplace ("_=", 2, command_name);
}

#if 0   /* UNUSED -- it caused too many problems */
void
put_gnu_argv_flags_into_env (pid, flags_string)
     intmax_t pid;
     char *flags_string;
{
  char *dummy, *pbuf;
  int l, fl;

  pbuf = itos (pid);
  l = strlen (pbuf);

  fl = strlen (flags_string);

  dummy = (char *)xmalloc (l + fl + 30);
  dummy[0] = '_';
  strcpy (dummy + 1, pbuf);
  strcpy (dummy + 1 + l, "_GNU_nonoption_argv_flags_");
  dummy[l + 27] = '=';
  strcpy (dummy + l + 28, flags_string);

  free (pbuf);

  export_env = add_or_supercede_exported_var (dummy, 0);
}
#endif

/* **************************************************************** */
/*                                  */
/*            Managing variable contexts            */
/*                                  */
/* **************************************************************** */

/* Allocate and return a new variable context with NAME and FLAGS.
   NAME can be NULL. */

VAR_CONTEXT *
new_var_context (name, flags)
     char *name;
     int flags;
{
  VAR_CONTEXT *vc;

  vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT));
  vc->name = name ? savestring (name) : (char *)NULL;
  vc->scope = variable_context;
  vc->flags = flags;

  vc->up = vc->down = (VAR_CONTEXT *)NULL;
  vc->table = (HASH_TABLE *)NULL;

  return vc;
}

/* Free a variable context and its data, including the hash table.  Dispose
   all of the variables. */
void
dispose_var_context (vc)
     VAR_CONTEXT *vc;
{
  FREE (vc->name);

  if (vc->table)
    {
      delete_all_variables (vc->table);
      hash_dispose (vc->table);
    }

  free (vc);
}

/* Set VAR's scope level to the current variable context. */
static int
set_context (var)
     SHELL_VAR *var;
{
  return (var->context = variable_context);
}

/* Make a new variable context with NAME and FLAGS and a HASH_TABLE of
   temporary variables, and push it onto shell_variables.  This is
   for shell functions. */
VAR_CONTEXT *
push_var_context (name, flags, tempvars)
     char *name;
     int flags;
     HASH_TABLE *tempvars;
{
  VAR_CONTEXT *vc;

  vc = new_var_context (name, flags);
  vc->table = tempvars;
  if (tempvars)
    {
      /* Have to do this because the temp environment was created before
     variable_context was incremented. */
      flatten (tempvars, set_context, (VARLIST *)NULL, 0);
      vc->flags |= VC_HASTMPVAR;
    }
  vc->down = shell_variables;
  shell_variables->up = vc;

  return (shell_variables = vc);
}

static void
push_func_var (data)
     PTR_T data;
{
  SHELL_VAR *var, *v;

  var = (SHELL_VAR *)data;

  if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
    {
      /* XXX - should we set v->context here? */
      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0);
      if (shell_variables == global_variables)
    var->attributes &= ~(att_tempvar|att_propagate);
      else
        shell_variables->flags |= VC_HASTMPVAR;
      v->attributes |= var->attributes;
    }

  dispose_variable (var);
}

/* Pop the top context off of VCXT and dispose of it, returning the rest of
   the stack. */
void
pop_var_context ()
{
  VAR_CONTEXT *ret, *vcxt;

  vcxt = shell_variables;
  if (vc_isfuncenv (vcxt) == 0)
    {
      internal_error ("pop_var_context: head of shell_variables not a function context");
      return;
    }

  if (ret = vcxt->down)
    {
      ret->up = (VAR_CONTEXT *)NULL;
      shell_variables = ret;
      if (vcxt->table)
    hash_flush (vcxt->table, push_func_var);
      dispose_var_context (vcxt);
    }
  else
    internal_error ("pop_var_context: no global_variables context");
}

/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
   all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
void
delete_all_contexts (vcxt)
     VAR_CONTEXT *vcxt;
{
  VAR_CONTEXT *v, *t;

  for (v = vcxt; v != global_variables; v = t)
    {
      t = v->down;
      dispose_var_context (v);
    }            

  delete_all_variables (global_variables->table);
  shell_variables = global_variables;
}

/* **************************************************************** */
/*                                  */
/*     Pushing and Popping temporary variable scopes        */
/*                                  */
/* **************************************************************** */

VAR_CONTEXT *
push_scope (flags, tmpvars)
     int flags;
     HASH_TABLE *tmpvars;
{
  return (push_var_context ((char *)NULL, flags, tmpvars));
}

static void
push_exported_var (data)
     PTR_T data;
{
  SHELL_VAR *var, *v;

  var = (SHELL_VAR *)data;

  /* If a temp var had its export attribute set, or it's marked to be
     propagated, bind it in the previous scope before disposing it. */
  if (exported_p (var) || (var->attributes & att_propagate))
    {
      var->attributes &= ~att_tempvar;      /* XXX */
      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0);
      if (shell_variables == global_variables)
    var->attributes &= ~att_propagate;
      v->attributes |= var->attributes;
    }

  dispose_variable (var);
}

void
pop_scope (is_special)
     int is_special;
{
  VAR_CONTEXT *vcxt, *ret;

  vcxt = shell_variables;
  if (vc_istempscope (vcxt) == 0)
    {
      internal_error ("pop_scope: head of shell_variables not a temporary environment scope");
      return;
    }

  ret = vcxt->down;
  if (ret)
    ret->up = (VAR_CONTEXT *)NULL;

  shell_variables = ret;

  /* Now we can take care of merging variables in VCXT into set of scopes
     whose head is RET (shell_variables). */
  FREE (vcxt->name);
  if (vcxt->table)
    {
      if (is_special)
    hash_flush (vcxt->table, push_func_var);
      else
    hash_flush (vcxt->table, push_exported_var);
      hash_dispose (vcxt->table);
    }
  free (vcxt);

  sv_ifs ("IFS");   /* XXX here for now */
}

/* **************************************************************** */
/*                                  */
/*       Pushing and Popping function contexts          */
/*                                  */
/* **************************************************************** */

static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
static int dollar_arg_stack_slots;
static int dollar_arg_stack_index;

/* XXX - we might want to consider pushing and popping the `getopts' state
   when we modify the positional parameters. */
void
push_context (name, is_subshell, tempvars)
     char *name;    /* function name */
     int is_subshell;
     HASH_TABLE *tempvars;
{
  if (is_subshell == 0)
    push_dollar_vars ();
  variable_context++;
  push_var_context (name, VC_FUNCENV, tempvars);
}

/* Only called when subshell == 0, so we don't need to check, and can
   unconditionally pop the dollar vars off the stack. */
void
pop_context ()
{
  pop_dollar_vars ();
  variable_context--;
  pop_var_context ();

  sv_ifs ("IFS");       /* XXX here for now */
}

/* Save the existing positional parameters on a stack. */
void
push_dollar_vars ()
{
  if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
    {
      dollar_arg_stack = (WORD_LIST **)
    xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
          * sizeof (WORD_LIST **));
    }
  dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
}

/* Restore the positional parameters from our stack. */
void
pop_dollar_vars ()
{
  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
    return;

  remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
  dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
  set_dollar_vars_unchanged ();
}

void
dispose_saved_dollar_vars ()
{
  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
    return;

  dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
}

/*************************************************
 *                       *
 *  Functions to manage special variables    *
 *                       *
 *************************************************/

/* Extern declarations for variables this code has to manage. */
extern int eof_encountered, eof_encountered_limit, ignoreeof;

#if defined (READLINE)
extern int no_line_editing;
extern int hostname_list_initialized;
#endif

/* An alist of name.function for each special variable.  Most of the
   functions don't do much, and in fact, this would be faster with a
   switch statement, but by the end of this file, I am sick of switch
   statements. */

#define SET_INT_VAR(name, intvar)  intvar = find_variable (name) != 0

/* This table will be sorted with qsort() the first time it's accessed. */
struct name_and_function {
  char *name;
  sh_sv_func_t *function;
};

static struct name_and_function special_vars[] = {
  { "GLOBIGNORE", sv_globignore },

#if defined (HISTORY)
  { "HISTCONTROL", sv_history_control },
  { "HISTFILESIZE", sv_histsize },
  { "HISTIGNORE", sv_histignore },
  { "HISTSIZE", sv_histsize },
#endif

#if defined (READLINE)
  { "HOSTFILE", sv_hostfile },
#endif

  { "IFS", sv_ifs },
  { "IGNOREEOF", sv_ignoreeof },

  { "LANG", sv_locale },
  { "LC_ALL", sv_locale },
  { "LC_COLLATE", sv_locale },
  { "LC_CTYPE", sv_locale },
  { "LC_MESSAGES", sv_locale },
  { "LC_NUMERIC", sv_locale },

  { "MAIL", sv_mail },
  { "MAILCHECK", sv_mail },
  { "MAILPATH", sv_mail },

  { "OPTERR", sv_opterr },
  { "OPTIND", sv_optind },

  { "PATH", sv_path },
  { "POSIXLY_CORRECT", sv_strict_posix },

#if defined (READLINE)
  { "TERM", sv_terminal },
  { "TERMCAP", sv_terminal },
  { "TERMINFO", sv_terminal },
#endif /* READLINE */

  { "TEXTDOMAIN", sv_locale },
  { "TEXTDOMAINDIR", sv_locale },

#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
  { "TZ", sv_tz },
#endif

#if defined (HISTORY) && defined (BANG_HISTORY)
  { "histchars", sv_histchars },
#endif /* HISTORY && BANG_HISTORY */

  { "ignoreeof", sv_ignoreeof },

  { (char *)0, (sh_sv_func_t *)0 }
};

#define N_SPECIAL_VARS  (sizeof (special_vars) / sizeof (special_vars[0]) - 1)

static int
sv_compare (sv1, sv2)
     struct name_and_function *sv1, *sv2;
{
  int r;

  if ((r = sv1->name[0] - sv2->name[0]) == 0)
    r = strcmp (sv1->name, sv2->name);
  return r;
}

static inline int
find_special_var (name)
     const char *name;
{
  register int i, r;

  for (i = 0; special_vars[i].name; i++)
    {
      r = special_vars[i].name[0] - name[0];
      if (r == 0)
    r = strcmp (special_vars[i].name, name);
      if (r == 0)
    return i;
      else if (r > 0)
    /* Can't match any of rest of elements in sorted list.  Take this out
       if it causes problems in certain environments. */
        break;
    }
  return -1;
}

/* The variable in NAME has just had its state changed.  Check to see if it
   is one of the special ones where something special happens. */
void
stupidly_hack_special_variables (name)
     char *name;
{
  static int sv_sorted = 0;
  int i;

  if (sv_sorted == 0)   /* shouldn't need, but it's fairly cheap. */
    {
      qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]),
        (QSFUNC *)sv_compare);
      sv_sorted = 1;
    }

  i = find_special_var (name);
  if (i != -1)
    (*(special_vars[i].function)) (name);
}

void
sv_ifs (name)
     char *name;
{
  SHELL_VAR *v;

  v = find_variable ("IFS");
  setifs (v);
}

/* What to do just after the PATH variable has changed. */
void
sv_path (name)
     char *name;
{
  /* hash -r */
  phash_flush ();
}

/* What to do just after one of the MAILxxxx variables has changed.  NAME
   is the name of the variable.  This is called with NAME set to one of
   MAIL, MAILCHECK, or MAILPATH.  */
void
sv_mail (name)
     char *name;
{
  /* If the time interval for checking the files has changed, then
     reset the mail timer.  Otherwise, one of the pathname vars
     to the users mailbox has changed, so rebuild the array of
     filenames. */
  if (name[4] == 'C')  /* if (strcmp (name, "MAILCHECK") == 0) */
    reset_mail_timer ();
  else
    {
      free_mail_files ();
      remember_mail_dates ();
    }
}

/* What to do when GLOBIGNORE changes. */
void
sv_globignore (name)
     char *name;
{
  setup_glob_ignore (name);
}

#if defined (READLINE)
/* What to do just after one of the TERMxxx variables has changed.
   If we are an interactive shell, then try to reset the terminal
   information in readline. */
void
sv_terminal (name)
     char *name;
{
  if (interactive_shell && no_line_editing == 0)
    rl_reset_terminal (get_string_value ("TERM"));
}

void
sv_hostfile (name)
     char *name;
{
  SHELL_VAR *v;

  v = find_variable (name);
  if (v == 0)
    clear_hostname_list ();
  else
    hostname_list_initialized = 0;
}
#endif /* READLINE */

#if defined (HISTORY)
/* What to do after the HISTSIZE or HISTFILESIZE variables change.
   If there is a value for this HISTSIZE (and it is numeric), then stifle
   the history.  Otherwise, if there is NO value for this variable,
   unstifle the history.  If name is HISTFILESIZE, and its value is
   numeric, truncate the history file to hold no more than that many
   lines. */
void
sv_histsize (name)
     char *name;
{
  char *temp;
  intmax_t num;

  temp = get_string_value (name);

  if (temp && *temp)
    {
      if (legal_number (temp, &num))
    {
      if (name[4] == 'S')
        {
          stifle_history (num);
          num = where_history ();
          if (history_lines_this_session > num)
        history_lines_this_session = num;
        }
      else
        {
          history_truncate_file (get_string_value ("HISTFILE"), (int)num);
          if (num <= history_lines_in_file)
        history_lines_in_file = num;
        }
    }
    }
  else if (name[4] == 'S')
    unstifle_history ();
}

/* What to do after the HISTIGNORE variable changes. */
void
sv_histignore (name)
     char *name;
{
  setup_history_ignore (name);
}

/* What to do after the HISTCONTROL variable changes. */
void
sv_history_control (name)
     char *name;
{
  char *temp;

  history_control = 0;
  temp = get_string_value (name);

  if (temp && *temp && STREQN (temp, "ignore", 6))
    {
      if (temp[6] == 's')   /* ignorespace */
    history_control = 1;
      else if (temp[6] == 'd')  /* ignoredups */
    history_control = 2;
      else if (temp[6] == 'b')  /* ignoreboth */
    history_control = 3;
    }
}

#if defined (BANG_HISTORY)
/* Setting/unsetting of the history expansion character. */
void
sv_histchars (name)
     char *name;
{
  char *temp;

  temp = get_string_value (name);
  if (temp)
    {
      history_expansion_char = *temp;
      if (temp[0] && temp[1])
    {
      history_subst_char = temp[1];
      if (temp[2])
          history_comment_char = temp[2];
    }
    }
  else
    {
      history_expansion_char = '!';
      history_subst_char = '^';
      history_comment_char = '#';
    }
}
#endif /* BANG_HISTORY */
#endif /* HISTORY */

#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
void
sv_tz (name)
     char *name;
{
  tzset ();
}
#endif

/* If the variable exists, then the value of it can be the number
   of times we actually ignore the EOF.  The default is small,
   (smaller than csh, anyway). */
void
sv_ignoreeof (name)
     char *name;
{
  SHELL_VAR *tmp_var;
  char *temp;

  eof_encountered = 0;

  tmp_var = find_variable (name);
  ignoreeof = tmp_var != 0;
  temp = tmp_var ? value_cell (tmp_var) : (char *)NULL;
  if (temp)
    eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10;
  set_shellopts (); /* make sure `ignoreeof' is/is not in $SHELLOPTS */
}

void
sv_optind (name)
     char *name;
{
  char *tt;
  int s;

  tt = get_string_value ("OPTIND");
  if (tt && *tt)
    {
      s = atoi (tt);

      /* According to POSIX, setting OPTIND=1 resets the internal state
     of getopt (). */
      if (s < 0 || s == 1)
    s = 0;
    }
  else
    s = 0;
  getopts_reset (s);
}

void
sv_opterr (name)
     char *name;
{
  char *tt;

  tt = get_string_value ("OPTERR");
  sh_opterr = (tt && *tt) ? atoi (tt) : 1;
}

void
sv_strict_posix (name)
     char *name;
{
  SET_INT_VAR (name, posixly_correct);
  posix_initialize (posixly_correct);
#if defined (READLINE)
  if (interactive_shell)
    posix_readline_initialize (posixly_correct);
#endif /* READLINE */
  set_shellopts (); /* make sure `posix' is/is not in $SHELLOPTS */
}

void
sv_locale (name)
     char *name;
{
  char *v;

  v = get_string_value (name);
  if (name[0] == 'L' && name[1] == 'A') /* LANG */
    set_lang (name, v);
  else
    set_locale_var (name, v);       /* LC_*, TEXTDOMAIN* */
}

#if defined (ARRAY_VARS)
void
set_pipestatus_array (ps, nproc)
     int *ps;
     int nproc;
{
  SHELL_VAR *v;
  ARRAY *a;
  ARRAY_ELEMENT *ae;
  register int i;
  char *t, tbuf[INT_STRLEN_BOUND(int) + 1];

  v = find_variable ("PIPESTATUS");
  if (v == 0)
    v = make_new_array_variable ("PIPESTATUS");
  if (array_p (v) == 0)
    return;     /* Do nothing if not an array variable. */
  a = array_cell (v);

  if (a == 0 || array_num_elements (a) == 0)
    {
      for (i = 0; i < nproc; i++)   /* was ps[i] != -1, not i < nproc */
    {
      t = inttostr (ps[i], tbuf, sizeof (tbuf));
      array_insert (a, i, t);
    }
      return;
    }

  /* Fast case */
  if (array_num_elements (a) == nproc && nproc == 1)
    {
      ae = element_forw (a->head);
      free (element_value (ae));
      ae->value = itos (ps[0]);
    }
  else if (array_num_elements (a) <= nproc)
    {
      /* modify in array_num_elements members in place, then add */
      ae = a->head;
      for (i = 0; i < array_num_elements (a); i++)
    {
      ae = element_forw (ae);
      free (element_value (ae));
      ae->value = itos (ps[i]);
    }
      /* add any more */
      for ( ; i < nproc; i++)
    {
      t = inttostr (ps[i], tbuf, sizeof (tbuf));
      array_insert (a, i, t);
    }
    }
  else
    {
      /* deleting elements.  it's faster to rebuild the array. */     
      array_flush (a);
      for (i = 0; ps[i] != -1; i++)
    {
      t = inttostr (ps[i], tbuf, sizeof (tbuf));
      array_insert (a, i, t);
    }
    }
}
#endif

void
set_pipestatus_from_exit (s)
     int s;
{
#if defined (ARRAY_VARS)
  static int v[2] = { 0, -1 };

  v[0] = s;
  set_pipestatus_array (v, 1);
#endif
}

https://git.savannah.gnu.org/cgit/bash.git/tree/copy_cmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/copy_cmd.c used in: Step 1212
/* copy_command.c -- copy a COMMAND structure.  This is needed
   primarily for making function definitions, but I'm not sure
   that anyone else will need it.  */

/* Copyright (C) 1987,1991 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include "bashtypes.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <stdio.h>

#include "shell.h"

static PATTERN_LIST *copy_case_clause __P((PATTERN_LIST *));
static PATTERN_LIST *copy_case_clauses __P((PATTERN_LIST *));
static FOR_COM *copy_for_command __P((FOR_COM *));
#if defined (ARITH_FOR_COMMAND)
static ARITH_FOR_COM *copy_arith_for_command __P((ARITH_FOR_COM *));
#endif
static GROUP_COM *copy_group_command __P((GROUP_COM *));
static SUBSHELL_COM *copy_subshell_command __P((SUBSHELL_COM *));
static CASE_COM *copy_case_command __P((CASE_COM *));
static WHILE_COM *copy_while_command __P((WHILE_COM *));
static IF_COM *copy_if_command __P((IF_COM *));
#if defined (DPAREN_ARITHMETIC)
static ARITH_COM *copy_arith_command __P((ARITH_COM *));
#endif
#if defined (COND_COMMAND)
static COND_COM *copy_cond_command __P((COND_COM *));
#endif
static SIMPLE_COM *copy_simple_command __P((SIMPLE_COM *));
static FUNCTION_DEF *copy_function_def __P((FUNCTION_DEF *));

WORD_DESC *
copy_word (w)
     WORD_DESC *w;
{
  WORD_DESC *new_word;

  new_word = make_bare_word (w->word);
  new_word->flags = w->flags;
  return (new_word);
}

/* Copy the chain of words in LIST.  Return a pointer to
   the new chain. */
WORD_LIST *
copy_word_list (list)
     WORD_LIST *list;
{
  WORD_LIST *new_list;

  for (new_list = (WORD_LIST *)NULL; list; list = list->next)
    new_list = make_word_list (copy_word (list->word), new_list);

  return (REVERSE_LIST (new_list, WORD_LIST *));
}

static PATTERN_LIST *
copy_case_clause (clause)
     PATTERN_LIST *clause;
{
  PATTERN_LIST *new_clause;

  new_clause = (PATTERN_LIST *)xmalloc (sizeof (PATTERN_LIST));
  new_clause->patterns = copy_word_list (clause->patterns);
  new_clause->action = copy_command (clause->action);
  return (new_clause);
}

static PATTERN_LIST *
copy_case_clauses (clauses)
     PATTERN_LIST *clauses;
{
  PATTERN_LIST *new_list, *new_clause;

  for (new_list = (PATTERN_LIST *)NULL; clauses; clauses = clauses->next)
    {
      new_clause = copy_case_clause (clauses);
      new_clause->next = new_list;
      new_list = new_clause;
    }
  return (REVERSE_LIST (new_list, PATTERN_LIST *));
}

/* Copy a single redirect. */
REDIRECT *
copy_redirect (redirect)
     REDIRECT *redirect;
{
  REDIRECT *new_redirect;

  new_redirect = (REDIRECT *)xmalloc (sizeof (REDIRECT));
  FASTCOPY ((char *)redirect, (char *)new_redirect, (sizeof (REDIRECT)));
  switch (redirect->instruction)
    {
    case r_reading_until:
    case r_deblank_reading_until:
      new_redirect->here_doc_eof = savestring (redirect->here_doc_eof);
      /*FALLTHROUGH*/
    case r_reading_string:
    case r_appending_to:
    case r_output_direction:
    case r_input_direction:
    case r_inputa_direction:
    case r_err_and_out:
    case r_input_output:
    case r_output_force:
    case r_duplicating_input_word:
    case r_duplicating_output_word:
    case r_move_input_word:
    case r_move_output_word:
      new_redirect->redirectee.filename = copy_word (redirect->redirectee.filename);
      break;
    case r_duplicating_input:
    case r_duplicating_output:
    case r_move_input:
    case r_move_output:
    case r_close_this:
      break;
    }
  return (new_redirect);
}

REDIRECT *
copy_redirects (list)
     REDIRECT *list;
{
  REDIRECT *new_list, *temp;

  for (new_list = (REDIRECT *)NULL; list; list = list->next)
    {
      temp = copy_redirect (list);
      temp->next = new_list;
      new_list = temp;
    }
  return (REVERSE_LIST (new_list, REDIRECT *));
}

static FOR_COM *
copy_for_command (com)
     FOR_COM *com;
{
  FOR_COM *new_for;

  new_for = (FOR_COM *)xmalloc (sizeof (FOR_COM));
  new_for->flags = com->flags;
  new_for->name = copy_word (com->name);
  new_for->map_list = copy_word_list (com->map_list);
  new_for->action = copy_command (com->action);
  return (new_for);
}

#if defined (ARITH_FOR_COMMAND)
static ARITH_FOR_COM *
copy_arith_for_command (com)
     ARITH_FOR_COM *com;
{
  ARITH_FOR_COM *new_arith_for;

  new_arith_for = (ARITH_FOR_COM *)xmalloc (sizeof (ARITH_FOR_COM));
  new_arith_for->flags = com->flags;
  new_arith_for->line = com->line;
  new_arith_for->init = copy_word_list (com->init);
  new_arith_for->test = copy_word_list (com->test);
  new_arith_for->step = copy_word_list (com->step);
  new_arith_for->action = copy_command (com->action);
  return (new_arith_for);
}
#endif /* ARITH_FOR_COMMAND */

static GROUP_COM *
copy_group_command (com)
     GROUP_COM *com;
{
  GROUP_COM *new_group;

  new_group = (GROUP_COM *)xmalloc (sizeof (GROUP_COM));
  new_group->command = copy_command (com->command);
  return (new_group);
}

static SUBSHELL_COM *
copy_subshell_command (com)
     SUBSHELL_COM *com;
{
  SUBSHELL_COM *new_subshell;

  new_subshell = (SUBSHELL_COM *)xmalloc (sizeof (SUBSHELL_COM));
  new_subshell->command = copy_command (com->command);
  new_subshell->flags = com->flags;
  return (new_subshell);
}

static CASE_COM *
copy_case_command (com)
     CASE_COM *com;
{
  CASE_COM *new_case;

  new_case = (CASE_COM *)xmalloc (sizeof (CASE_COM));
  new_case->flags = com->flags;
  new_case->word = copy_word (com->word);
  new_case->clauses = copy_case_clauses (com->clauses);
  return (new_case);
}

static WHILE_COM *
copy_while_command (com)
     WHILE_COM *com;
{
  WHILE_COM *new_while;

  new_while = (WHILE_COM *)xmalloc (sizeof (WHILE_COM));
  new_while->flags = com->flags;
  new_while->test = copy_command (com->test);
  new_while->action = copy_command (com->action);
  return (new_while);
}

static IF_COM *
copy_if_command (com)
     IF_COM *com;
{
  IF_COM *new_if;

  new_if = (IF_COM *)xmalloc (sizeof (IF_COM));
  new_if->flags = com->flags;
  new_if->test = copy_command (com->test);
  new_if->true_case = copy_command (com->true_case);
  new_if->false_case = com->false_case ? copy_command (com->false_case) : com->false_case;
  return (new_if);
}

#if defined (DPAREN_ARITHMETIC)
static ARITH_COM *
copy_arith_command (com)
     ARITH_COM *com;
{
  ARITH_COM *new_arith;

  new_arith = (ARITH_COM *)xmalloc (sizeof (ARITH_COM));
  new_arith->flags = com->flags;
  new_arith->exp = copy_word_list (com->exp);
  new_arith->line = com->line;

  return (new_arith);
}
#endif

#if defined (COND_COMMAND)
static COND_COM *
copy_cond_command (com)
     COND_COM *com;
{
  COND_COM *new_cond;

  new_cond = (COND_COM *)xmalloc (sizeof (COND_COM));
  new_cond->flags = com->flags;
  new_cond->line = com->line;
  new_cond->type = com->type;
  new_cond->op = com->op ? copy_word (com->op) : com->op;
  new_cond->left = com->left ? copy_cond_command (com->left) : (COND_COM *)NULL;
  new_cond->right = com->right ? copy_cond_command (com->right) : (COND_COM *)NULL;

  return (new_cond);
}
#endif

static SIMPLE_COM *
copy_simple_command (com)
     SIMPLE_COM *com;
{
  SIMPLE_COM *new_simple;

  new_simple = (SIMPLE_COM *)xmalloc (sizeof (SIMPLE_COM));
  new_simple->flags = com->flags;
  new_simple->words = copy_word_list (com->words);
  new_simple->redirects = com->redirects ? copy_redirects (com->redirects) : (REDIRECT *)NULL;
  new_simple->line = com->line;
  return (new_simple);
}

static FUNCTION_DEF *
copy_function_def (com)
     FUNCTION_DEF *com;
{
  FUNCTION_DEF *new_def;

  new_def = (FUNCTION_DEF *)xmalloc (sizeof (FUNCTION_DEF));
  new_def->name = copy_word (com->name);
  new_def->command = copy_command (com->command);
  new_def->flags = com->flags;
  new_def->line = com->line;
  return (new_def);
}

/* Copy the command structure in COMMAND.  Return a pointer to the
   copy.  Don't you forget to dispose_command () on this pointer
   later! */
COMMAND *
copy_command (command)
     COMMAND *command;
{
  COMMAND *new_command;

  if (command == NULL)
    return (command);

  new_command = (COMMAND *)xmalloc (sizeof (COMMAND));
  FASTCOPY ((char *)command, (char *)new_command, sizeof (COMMAND));
  new_command->flags = command->flags;
  new_command->line = command->line;

  if (command->redirects)
    new_command->redirects = copy_redirects (command->redirects);

  switch (command->type)
    {
      case cm_for:
    new_command->value.For = copy_for_command (command->value.For);
    break;

#if defined (ARITH_FOR_COMMAND)
      case cm_arith_for:
    new_command->value.ArithFor = copy_arith_for_command (command->value.ArithFor);
    break;
#endif

#if defined (SELECT_COMMAND)
      case cm_select:
    new_command->value.Select =
      (SELECT_COM *)copy_for_command ((FOR_COM *)command->value.Select);
    break;
#endif

      case cm_group:
    new_command->value.Group = copy_group_command (command->value.Group);
    break;

      case cm_subshell:
    new_command->value.Subshell = copy_subshell_command (command->value.Subshell);
    break;

      case cm_case:
    new_command->value.Case = copy_case_command (command->value.Case);
    break;

      case cm_until:
      case cm_while:
    new_command->value.While = copy_while_command (command->value.While);
    break;

      case cm_if:
    new_command->value.If = copy_if_command (command->value.If);
    break;

#if defined (DPAREN_ARITHMETIC)
      case cm_arith:
    new_command->value.Arith = copy_arith_command (command->value.Arith);
    break;
#endif

#if defined (COND_COMMAND)
      case cm_cond:
    new_command->value.Cond = copy_cond_command (command->value.Cond);
    break;
#endif

      case cm_simple:
    new_command->value.Simple = copy_simple_command (command->value.Simple);
    break;

      case cm_connection:
    {
      CONNECTION *new_connection;

      new_connection = (CONNECTION *)xmalloc (sizeof (CONNECTION));
      new_connection->connector = command->value.Connection->connector;
      new_connection->first = copy_command (command->value.Connection->first);
      new_connection->second = copy_command (command->value.Connection->second);
      new_command->value.Connection = new_connection;
      break;
    }

      case cm_function_def:
    new_command->value.Function_def = copy_function_def (command->value.Function_def);
    break;
    }
  return (new_command);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/error.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/error.c used in: Step 1213
/* error.c -- Functions for handling errors. */
/* Copyright (C) 1993 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include "bashtypes.h"
#include <fcntl.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#if defined (PREFER_STDARG)
#  include <stdarg.h>
#else
#  include <varargs.h>
#endif

#include <stdio.h>

#include <errno.h>
#if !defined (errno)
extern int errno;
#endif /* !errno */

#include "bashansi.h"
#include "flags.h"
#include "error.h"
#include "command.h"
#include "general.h"
#include "externs.h"
#include "input.h"

#if defined (HISTORY)
#  include "bashhist.h"
#endif

extern int executing_line_number __P((void));

extern int interactive_shell, interactive, startup_state;
extern char *dollar_vars[];
extern char *shell_name;
#if defined (JOB_CONTROL)
extern pid_t shell_pgrp;
extern int give_terminal_to __P((pid_t, int));
#endif /* JOB_CONTROL */

static void error_prolog __P((int));

/* The current maintainer of the shell.  You change this in the
   Makefile. */
#if !defined (MAINTAINER)
#define MAINTAINER "bash-maintainers@gnu.org"
#endif

char *the_current_maintainer = MAINTAINER;

static void
error_prolog (print_lineno)
     int print_lineno;
{
  int line;

  fprintf (stderr, "%s: ", get_name_for_error ());

  if (print_lineno && interactive_shell == 0)
    {
      line = executing_line_number ();
      if (line > 0)
    fprintf (stderr, "line %d: ", line);
    }
}

/* Return the name of the shell or the shell script for error reporting. */
char *
get_name_for_error ()
{
  char *name;

  name = (char *)NULL;
  if (interactive_shell == 0)
    name = dollar_vars[0];
  if (name == 0 && shell_name && *shell_name)
    name = base_pathname (shell_name);
  if (name == 0)
#if defined (PROGRAM)
    name = PROGRAM;
#else
    name = "bash";
#endif

  return (name);
}

/* Report an error having to do with FILENAME.  This does not use
   sys_error so the filename is not interpreted as a printf-style
   format string. */
void
file_error (filename)
     const char *filename;
{
  report_error ("%s: %s", filename, strerror (errno));
}

void
#if defined (PREFER_STDARG)
programming_error (const char *format, ...)
#else
programming_error (format, va_alist)
     const char *format;
     va_dcl
#endif
{
  va_list args;
  char *h;

#if defined (JOB_CONTROL)
  give_terminal_to (shell_pgrp, 0);
#endif /* JOB_CONTROL */

  SH_VA_START (args, format);

  vfprintf (stderr, format, args);
  fprintf (stderr, "\n");
  va_end (args);

#if defined (HISTORY)
  if (remember_on_history)
    {
      h = last_history_line ();
      fprintf (stderr, "last command: %s\n", h ? h : "(null)");
    }
#endif

#if 0
  fprintf (stderr, "Report this to %s\n", the_current_maintainer);
#endif

  fprintf (stderr, "Stopping myself...");
  fflush (stderr);

  abort ();
}

/* Print an error message and, if `set -e' has been executed, exit the
   shell.  Used in this file by file_error and programming_error.  Used
   outside this file mostly to report substitution and expansion errors,
   and for bad invocation options. */
void
#if defined (PREFER_STDARG)
report_error (const char *format, ...)
#else
report_error (format, va_alist)
     const char *format;
     va_dcl
#endif
{
  va_list args;

  error_prolog (1);

  SH_VA_START (args, format);

  vfprintf (stderr, format, args);
  fprintf (stderr, "\n");

  va_end (args);
  if (exit_immediately_on_error)
    sh_exit (1);
}

void
#if defined (PREFER_STDARG)
fatal_error (const char *format, ...)
#else
fatal_error (format, va_alist)
     const char *format;
     va_dcl
#endif
{
  va_list args;

  error_prolog (0);

  SH_VA_START (args, format);

  vfprintf (stderr, format, args);
  fprintf (stderr, "\n");

  va_end (args);
  sh_exit (2);
}

void
#if defined (PREFER_STDARG)
internal_error (const char *format, ...)
#else
internal_error (format, va_alist)
     const char *format;
     va_dcl
#endif
{
  va_list args;

  error_prolog (1);

  SH_VA_START (args, format);

  vfprintf (stderr, format, args);
  fprintf (stderr, "\n");

  va_end (args);
}

void
#if defined (PREFER_STDARG)
internal_warning (const char *format, ...)
#else
internal_warning (format, va_alist)
     const char *format;
     va_dcl
#endif
{
  va_list args;

  fprintf (stderr, "%s: warning: ", get_name_for_error ());

  SH_VA_START (args, format);

  vfprintf (stderr, format, args);
  fprintf (stderr, "\n");

  va_end (args);
}

void
#if defined (PREFER_STDARG)
sys_error (const char *format, ...)
#else
sys_error (format, va_alist)
     const char *format;
     va_dcl
#endif
{
  int e;
  va_list args;

  e = errno;
  error_prolog (0);

  SH_VA_START (args, format);

  vfprintf (stderr, format, args);
  fprintf (stderr, ": %s\n", strerror (e));

  va_end (args);
}

/* An error from the parser takes the general form

    shell_name: input file name: line number: message

   The input file name and line number are omitted if the shell is
   currently interactive.  If the shell is not currently interactive,
   the input file name is inserted only if it is different from the
   shell name. */
void
#if defined (PREFER_STDARG)
parser_error (int lineno, const char *format, ...)
#else
parser_error (lineno, format, va_alist)
     int lineno;
     const char *format;
     va_dcl
#endif
{
  va_list args;
  char *ename, *iname;

  ename = get_name_for_error ();
  iname = yy_input_name ();

  if (interactive)
    fprintf (stderr, "%s: ", ename);
  else if (interactive_shell)
    fprintf (stderr, "%s: %s: line %d: ", ename, iname, lineno);
  else if (STREQ (ename, iname))
    fprintf (stderr, "%s: line %d: ", ename, lineno);
  else
    fprintf (stderr, "%s: %s: line %d: ", ename, iname, lineno);

  SH_VA_START (args, format);

  vfprintf (stderr, format, args);
  fprintf (stderr, "\n");

  va_end (args);

  if (exit_immediately_on_error)
    sh_exit (2);
}

#ifdef DEBUG
void
#if defined (PREFER_STDARG)
itrace (const char *format, ...)
#else
itrace (format, va_alist)
     const char *format;
     va_dcl
#endif
{
  va_list args;

  fprintf(stderr, "TRACE: pid %ld: ", (long)getpid());

  SH_VA_START (args, format);

  vfprintf (stderr, format, args);
  fprintf (stderr, "\n");

  va_end (args);

  fflush(stderr);
}

/* A trace function for silent debugging -- doesn't require a control
   terminal. */
void
#if defined (PREFER_STDARG)
trace (const char *format, ...)
#else
trace (format, va_alist)
     const char *format;
     va_dcl
#endif
{
  va_list args;
  static FILE *tracefp = (FILE *)NULL;

  if (tracefp == NULL)
    tracefp = fopen("/tmp/bash-trace.log", "a+");

  if (tracefp == NULL)
    tracefp = stderr;
  else
    fcntl (fileno (tracefp), F_SETFD, 1);     /* close-on-exec */

  fprintf(tracefp, "TRACE: pid %ld: ", (long)getpid());

  SH_VA_START (args, format);

  vfprintf (tracefp, format, args);
  fprintf (tracefp, "\n");

  va_end (args);

  fflush(tracefp);
}

#endif /* DEBUG */

/* **************************************************************** */
/*                                  */
/*              Common error reporting              */
/*                                  */
/* **************************************************************** */


static char *cmd_error_table[] = {
    "unknown command error",    /* CMDERR_DEFAULT */
    "bad command type",     /* CMDERR_BADTYPE */
    "bad connector",        /* CMDERR_BADCONN */
    "bad jump",         /* CMDERR_BADJUMP */
    0
};

void
command_error (func, code, e, flags)
     const char *func;
     int code, e, flags;    /* flags currently unused */
{
  if (code > CMDERR_LAST)
    code = CMDERR_DEFAULT;

  programming_error ("%s: %s: %d", func, cmd_error_table[code], e);
}

char *
command_errstr (code)
     int code;
{
  if (code > CMDERR_LAST)
    code = CMDERR_DEFAULT;

  return (cmd_error_table[code]);
}

#ifdef ARRAY_VARS
void
err_badarraysub (s)
     const char *s;
{
  report_error ("%s: bad array subscript", s);
}
#endif

void
err_unboundvar (s)
     const char *s;
{
  report_error ("%s: unbound variable", s);
}

void
err_readonly (s)
     const char *s;
{
  report_error ("%s: readonly variable", s);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/expr.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/expr.c used in: Step 1214
/* expr.c -- arithmetic expression evaluation. */

/* Copyright (C) 1990-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

/*
 All arithmetic is done as intmax_t integers with no checking for overflow
 (though division by 0 is caught and flagged as an error).

 The following operators are handled, grouped into a set of levels in
 order of decreasing precedence.

    "id++", "id--"      [post-increment and post-decrement]
    "++id", "--id"      [pre-increment and pre-decrement]
    "-", "+"        [(unary operators)]
    "!", "~"
    "**"            [(exponentiation)]
    "*", "/", "%"
    "+", "-"
    "<<", ">>"
    "<=", ">=", "<", ">"
    "==", "!="
    "&"
    "^"
    "|"
    "&&"
    "||"
    "expr ? expr : expr"
    "=", "*=", "/=", "%=", "+=", "-=", "<<=", ">>=", "&=", "^=", "|="

 (Note that most of these operators have special meaning to bash, and an
 entire expression should be quoted, e.g. "a=$a+1" or "a=a+1" to ensure
 that it is passed intact to the evaluator when using `let'.  When using
 the $[] or $(( )) forms, the text between the `[' and `]' or `((' and `))'
 is treated as if in double quotes.)

 Sub-expressions within parentheses have a precedence level greater than
 all of the above levels and are evaluated first.  Within a single prece-
 dence group, evaluation is left-to-right, except for the arithmetic
 assignment operator (`='), which is evaluated right-to-left (as in C).

 The expression evaluator returns the value of the expression (assignment
 statements have as a value what is returned by the RHS).  The `let'
 builtin, on the other hand, returns 0 if the last expression evaluates to
 a non-zero, and 1 otherwise.

 Implementation is a recursive-descent parser.

 Chet Ramey
 chet@ins.CWRU.Edu
*/

#include "config.h"

#include <stdio.h>
#include "bashansi.h"

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "chartypes.h"

#include "shell.h"

/* Because of the $((...)) construct, expressions may include newlines.
   Here is a macro which accepts newlines, tabs and spaces as whitespace. */
#define cr_whitespace(c) (whitespace(c) || ((c) == '\n'))

/* Size be which the expression stack grows when neccessary. */
#define EXPR_STACK_GROW_SIZE 10

/* Maximum amount of recursion allowed.  This prevents a non-integer
   variable such as "num=num+2" from infinitely adding to itself when
   "let num=num+2" is given. */
#define MAX_EXPR_RECURSION_LEVEL 1024

/* The Tokens.  Singing "The Lion Sleeps Tonight". */

#define EQEQ    1   /* "==" */
#define NEQ 2   /* "!=" */
#define LEQ 3   /* "<=" */
#define GEQ 4   /* ">=" */
#define STR 5   /* string */
#define NUM 6   /* number */
#define LAND    7   /* "&&" Logical AND */
#define LOR 8   /* "||" Logical OR */
#define LSH 9   /* "<<" Left SHift */
#define RSH    10   /* ">>" Right SHift */
#define OP_ASSIGN 11    /* op= expassign as in Posix.2 */
#define COND    12  /* exp1 ? exp2 : exp3 */
#define POWER   13  /* exp1**exp2 */
#define PREINC  14  /* ++var */
#define PREDEC  15  /* --var */
#define POSTINC 16  /* var++ */
#define POSTDEC 17  /* var-- */
#define EQ  '='
#define GT  '>'
#define LT  '<'
#define PLUS    '+'
#define MINUS   '-'
#define MUL '*'
#define DIV '/'
#define MOD '%'
#define NOT '!'
#define LPAR    '('
#define RPAR    ')'
#define BAND    '&' /* Bitwise AND */
#define BOR '|' /* Bitwise OR. */
#define BXOR    '^' /* Bitwise eXclusive OR. */
#define BNOT    '~' /* Bitwise NOT; Two's complement. */
#define QUES    '?'
#define COL ':'
#define COMMA   ','

/* This should be the function corresponding to the operator with the
   highest precedence. */
#define EXP_HIGHEST expcomma

static char *expression;    /* The current expression */
static char *tp;        /* token lexical position */
static char *lasttp;    /* pointer to last token position */
static int  curtok;     /* the current token */
static int  lasttok;    /* the previous token */
static int  assigntok;  /* the OP in OP= */
static char *tokstr;    /* current token string */
static intmax_t tokval;     /* current token value */
static int  noeval;     /* set to 1 if no assignment to be done */
static procenv_t evalbuf;

static void readtok __P((void));    /* lexical analyzer */

static intmax_t expr_streval __P((char *, int));
static intmax_t strlong __P((char *));
static void evalerror __P((char *));

static void pushexp __P((void));
static void popexp __P((void));
static void expr_unwind __P((void));

static intmax_t subexpr __P((char *));

static intmax_t expcomma __P((void));
static intmax_t expassign __P((void));
static intmax_t expcond __P((void));
static intmax_t explor __P((void));
static intmax_t expland __P((void));
static intmax_t expbor __P((void));
static intmax_t expbxor __P((void));
static intmax_t expband __P((void));
static intmax_t exp5 __P((void));
static intmax_t exp4 __P((void));
static intmax_t expshift __P((void));
static intmax_t exp3 __P((void));
static intmax_t exp2 __P((void));
static intmax_t exppower __P((void));
static intmax_t exp1 __P((void));
static intmax_t exp0 __P((void));

/* A structure defining a single expression context. */
typedef struct {
  int curtok, lasttok;
  char *expression, *tp, *lasttp;
  intmax_t tokval;
  char *tokstr;
  int noeval;
} EXPR_CONTEXT;

#ifdef INCLUDE_UNUSED
/* Not used yet. */
typedef struct {
  char *tokstr;
  intmax_t tokval;
} LVALUE;
#endif

/* Global var which contains the stack of expression contexts. */
static EXPR_CONTEXT **expr_stack;
static int expr_depth;         /* Location in the stack. */
static int expr_stack_size;    /* Number of slots already allocated. */

extern char *this_command_name;
extern int unbound_vars_is_error;

#define SAVETOK(X) \
  do { \
    (X)->curtok = curtok; \
    (X)->lasttok = lasttok; \
    (X)->tp = tp; \
    (X)->lasttp = lasttp; \
    (X)->tokval = tokval; \
    (X)->tokstr = tokstr; \
    (X)->noeval = noeval; \
  } while (0)

#define RESTORETOK(X) \
  do { \
    curtok = (X)->curtok; \
    lasttok = (X)->lasttok; \
    tp = (X)->tp; \
    lasttp = (X)->lasttp; \
    tokval = (X)->tokval; \
    tokstr = (X)->tokstr; \
    noeval = (X)->noeval; \
  } while (0)

/* Push and save away the contents of the globals describing the
   current expression context. */
static void
pushexp ()
{
  EXPR_CONTEXT *context;

  if (expr_depth >= MAX_EXPR_RECURSION_LEVEL)
    evalerror ("expression recursion level exceeded");

  if (expr_depth >= expr_stack_size)
    {
      expr_stack_size += EXPR_STACK_GROW_SIZE;
      expr_stack = (EXPR_CONTEXT **)xrealloc (expr_stack, expr_stack_size * sizeof (EXPR_CONTEXT *));
    }

  context = (EXPR_CONTEXT *)xmalloc (sizeof (EXPR_CONTEXT));

  context->expression = expression;
  SAVETOK(context);

  expr_stack[expr_depth++] = context;
}

/* Pop the the contents of the expression context stack into the
   globals describing the current expression context. */
static void
popexp ()
{
  EXPR_CONTEXT *context;

  if (expr_depth == 0)
    evalerror ("recursion stack underflow");

  context = expr_stack[--expr_depth];

  expression = context->expression;
  RESTORETOK (context);

  free (context);
}

static void
expr_unwind ()
{
  while (--expr_depth > 0)
    {
      if (expr_stack[expr_depth]->tokstr)
    free (expr_stack[expr_depth]->tokstr);

      if (expr_stack[expr_depth]->expression)
    free (expr_stack[expr_depth]->expression);

      free (expr_stack[expr_depth]);
    }
  free (expr_stack[expr_depth]);    /* free the allocated EXPR_CONTEXT */
}

/* Evaluate EXPR, and return the arithmetic result.  If VALIDP is
   non-null, a zero is stored into the location to which it points
   if the expression is invalid, non-zero otherwise.  If a non-zero
   value is returned in *VALIDP, the return value of evalexp() may
   be used.

   The `while' loop after the longjmp is caught relies on the above
   implementation of pushexp and popexp leaving in expr_stack[0] the
   values that the variables had when the program started.  That is,
   the first things saved are the initial values of the variables that
   were assigned at program startup or by the compiler.  Therefore, it is
   safe to let the loop terminate when expr_depth == 0, without freeing up
   any of the expr_depth[0] stuff. */
intmax_t
evalexp (expr, validp)
     char *expr;
     int *validp;
{
  intmax_t val;

  val = 0;

  if (setjmp (evalbuf))
    {
      FREE (tokstr);
      FREE (expression);
      tokstr = expression = (char *)NULL;

      expr_unwind ();

      if (validp)
    *validp = 0;
      return (0);
    }

  val = subexpr (expr);

  if (validp)
    *validp = 1;

  return (val);
}

static intmax_t
subexpr (expr)
     char *expr;
{
  intmax_t val;
  char *p;

  for (p = expr; p && *p && cr_whitespace (*p); p++)
    ;

  if (p == NULL || *p == '\0')
    return (0);

  pushexp ();
  curtok = lasttok = 0;
  expression = savestring (expr);
  tp = expression;

  tokstr = (char *)NULL;
  tokval = 0;

  readtok ();

  val = EXP_HIGHEST ();

  if (curtok != 0)
    evalerror ("syntax error in expression");

  FREE (tokstr);
  FREE (expression);

  popexp ();

  return val;
}

static intmax_t
expcomma ()
{
  register intmax_t value;

  value = expassign ();
  while (curtok == COMMA)
    {
      readtok ();
      value = expassign ();
    }

  return value;
}
  
static intmax_t
expassign ()
{
  register intmax_t value;
  char *lhs, *rhs;

  value = expcond ();
  if (curtok == EQ || curtok == OP_ASSIGN)
    {
      int special, op;
      intmax_t lvalue;

      special = curtok == OP_ASSIGN;

      if (lasttok != STR)
    evalerror ("attempted assignment to non-variable");

      if (special)
    {
      op = assigntok;       /* a OP= b */
      lvalue = value;
    }

      lhs = savestring (tokstr);
      readtok ();
      value = expassign ();

      if (special)
    {
      switch (op)
        {
        case MUL:
          lvalue *= value;
          break;
        case DIV:
          if (value == 0)
        evalerror ("division by 0");
          lvalue /= value;
          break;
        case MOD:
          if (value == 0)
        evalerror ("division by 0");
          lvalue %= value;
          break;
        case PLUS:
          lvalue += value;
          break;
        case MINUS:
          lvalue -= value;
          break;
        case LSH:
          lvalue <<= value;
          break;
        case RSH:
          lvalue >>= value;
          break;
        case BAND:
          lvalue &= value;
          break;
        case BOR:
          lvalue |= value;
          break;
        case BXOR:
          lvalue ^= value;
          break;
        default:
          free (lhs);
          evalerror ("bug: bad expassign token");
          break;
        }
      value = lvalue;
    }

      rhs = itos (value);
      if (noeval == 0)
    (void)bind_int_variable (lhs, rhs);
      free (rhs);
      free (lhs);
      FREE (tokstr);
      tokstr = (char *)NULL;        /* For freeing on errors. */
    }
  return (value);
}

/* Conditional expression (expr?expr:expr) */
static intmax_t
expcond ()
{
  intmax_t cval, val1, val2, rval;
  int set_noeval;

  set_noeval = 0;
  rval = cval = explor ();
  if (curtok == QUES)       /* found conditional expr */
    {
      readtok ();
      if (curtok == 0 || curtok == COL)
    evalerror ("expression expected");
      if (cval == 0)
    {
      set_noeval = 1;
      noeval++;
    }

      val1 = EXP_HIGHEST ();

      if (set_noeval)
    noeval--;
      if (curtok != COL)
    evalerror ("`:' expected for conditional expression");
      readtok ();
      if (curtok == 0)
    evalerror ("expression expected");
      set_noeval = 0;
      if (cval)
    {
      set_noeval = 1;
      noeval++;
    }
      val2 = explor ();
      if (set_noeval)
    noeval--;
      rval = cval ? val1 : val2;
      lasttok = COND;
    }
  return rval;
}

/* Logical OR. */
static intmax_t
explor ()
{
  register intmax_t val1, val2;
  int set_noeval;

  val1 = expland ();

  while (curtok == LOR)
    {
      set_noeval = 0;
      if (val1 != 0)
    {
      noeval++;
      set_noeval = 1;
    }
      readtok ();
      val2 = expland ();
      if (set_noeval)
    noeval--;
      val1 = val1 || val2;
      lasttok = LOR;
    }

  return (val1);
}

/* Logical AND. */
static intmax_t
expland ()
{
  register intmax_t val1, val2;
  int set_noeval;

  val1 = expbor ();

  while (curtok == LAND)
    {
      set_noeval = 0;
      if (val1 == 0)
    {
      set_noeval = 1;
      noeval++;
    }
      readtok ();
      val2 = expbor ();
      if (set_noeval)
    noeval--;
      val1 = val1 && val2;
      lasttok = LAND;
    }

  return (val1);
}

/* Bitwise OR. */
static intmax_t
expbor ()
{
  register intmax_t val1, val2;

  val1 = expbxor ();

  while (curtok == BOR)
    {
      readtok ();
      val2 = expbxor ();
      val1 = val1 | val2;
    }

  return (val1);
}

/* Bitwise XOR. */
static intmax_t
expbxor ()
{
  register intmax_t val1, val2;

  val1 = expband ();

  while (curtok == BXOR)
    {
      readtok ();
      val2 = expband ();
      val1 = val1 ^ val2;
    }

  return (val1);
}

/* Bitwise AND. */
static intmax_t
expband ()
{
  register intmax_t val1, val2;

  val1 = exp5 ();

  while (curtok == BAND)
    {
      readtok ();
      val2 = exp5 ();
      val1 = val1 & val2;
    }

  return (val1);
}

static intmax_t
exp5 ()
{
  register intmax_t val1, val2;

  val1 = exp4 ();

  while ((curtok == EQEQ) || (curtok == NEQ))
    {
      int op = curtok;

      readtok ();
      val2 = exp4 ();
      if (op == EQEQ)
    val1 = (val1 == val2);
      else if (op == NEQ)
    val1 = (val1 != val2);
    }
  return (val1);
}

static intmax_t
exp4 ()
{
  register intmax_t val1, val2;

  val1 = expshift ();
  while ((curtok == LEQ) ||
     (curtok == GEQ) ||
     (curtok == LT) ||
     (curtok == GT))
    {
      int op = curtok;

      readtok ();
      val2 = expshift ();

      if (op == LEQ)
    val1 = val1 <= val2;
      else if (op == GEQ)
    val1 = val1 >= val2;
      else if (op == LT)
    val1 = val1 < val2;
      else          /* (op == GT) */
    val1 = val1 > val2;
    }
  return (val1);
}

/* Left and right shifts. */
static intmax_t
expshift ()
{
  register intmax_t val1, val2;

  val1 = exp3 ();

  while ((curtok == LSH) || (curtok == RSH))
    {
      int op = curtok;

      readtok ();
      val2 = exp3 ();

      if (op == LSH)
    val1 = val1 << val2;
      else
    val1 = val1 >> val2;
    }

  return (val1);
}

static intmax_t
exp3 ()
{
  register intmax_t val1, val2;

  val1 = exp2 ();

  while ((curtok == PLUS) || (curtok == MINUS))
    {
      int op = curtok;

      readtok ();
      val2 = exp2 ();

      if (op == PLUS)
    val1 += val2;
      else if (op == MINUS)
    val1 -= val2;
    }
  return (val1);
}

static intmax_t
exp2 ()
{
  register intmax_t val1, val2;

  val1 = exppower ();

  while ((curtok == MUL) ||
     (curtok == DIV) ||
     (curtok == MOD))
    {
      int op = curtok;

      readtok ();

      val2 = exppower ();

      if (((op == DIV) || (op == MOD)) && (val2 == 0))
    evalerror ("division by 0");

      if (op == MUL)
    val1 *= val2;
      else if (op == DIV)
    val1 /= val2;
      else if (op == MOD)
    val1 %= val2;
    }
  return (val1);
}

static intmax_t
exppower ()
{
  register intmax_t val1, val2, c;

  val1 = exp1 ();
  if (curtok == POWER)
    {
      readtok ();
      val2 = exp1 ();
      if (val2 == 0)
    return (1);
      if (val2 < 0)
    evalerror ("exponent less than 0");
      for (c = 1; val2--; c *= val1)
    ;
      val1 = c;
    }
  return (val1);
}

static intmax_t
exp1 ()
{
  register intmax_t val;

  if (curtok == NOT)
    {
      readtok ();
      val = !exp1 ();
    }
  else if (curtok == BNOT)
    {
      readtok ();
      val = ~exp1 ();
    }
  else
    val = exp0 ();

  return (val);
}

static intmax_t
exp0 ()
{
  register intmax_t val = 0, v2;
  char *vincdec;
  int stok;

  /* XXX - might need additional logic here to decide whether or not
       pre-increment or pre-decrement is legal at this point. */
  if (curtok == PREINC || curtok == PREDEC)
    {
      stok = lasttok = curtok;
      readtok ();
      if (curtok != STR)
    /* readtok() catches this */
    evalerror ("identifier expected after pre-increment or pre-decrement");

      v2 = tokval + ((stok == PREINC) ? 1 : -1);
      vincdec = itos (v2);
      if (noeval == 0)
    (void)bind_int_variable (tokstr, vincdec);
      free (vincdec);
      val = v2;

      curtok = NUM; /* make sure --x=7 is flagged as an error */
      readtok ();
    }
  else if (curtok == MINUS)
    {
      readtok ();
      val = - exp0 ();
    }
  else if (curtok == PLUS)
    {
      readtok ();
      val = exp0 ();
    }
  else if (curtok == LPAR)
    {
      readtok ();
      val = EXP_HIGHEST ();

      if (curtok != RPAR)
    evalerror ("missing `)'");

      /* Skip over closing paren. */
      readtok ();
    }
  else if ((curtok == NUM) || (curtok == STR))
    {
      val = tokval;
      if (curtok == STR && (*tp == '+' || *tp == '-') && tp[1] == *tp &&
        (tp[2] == '\0' || (ISALNUM ((unsigned char)tp[2]) == 0)))
    {
      /* post-increment or post-decrement */
      v2 = val + ((*tp == '+') ? 1 : -1);
      vincdec = itos (v2);
      if (noeval == 0)
        (void)bind_int_variable (tokstr, vincdec);
      free (vincdec);
      tp += 2;
      curtok = NUM; /* make sure x++=7 is flagged as an error */
    }
      
      readtok ();
    }
  else
    evalerror ("syntax error: operand expected");

  return (val);
}

static intmax_t
expr_streval (tok, e)
     char *tok;
     int e;
{
  SHELL_VAR *v;
  char *value;
  intmax_t tval;

  /* [[[[[ */
#if defined (ARRAY_VARS)
  v = (e == ']') ? array_variable_part (tok, (char **)0, (int *)0) : find_variable (tok);
#else
  v = find_variable (tok);
#endif

  if ((v == 0 || invisible_p (v)) && unbound_vars_is_error)
    {
#if defined (ARRAY_VARS)
      value = (e == ']') ? array_variable_name (tok, (char **)0, (int *)0) : tok;
#else
      value = tok;
#endif

      err_unboundvar (value);

#if defined (ARRAY_VARS)
      if (e == ']')
    FREE (value);   /* array_variable_name returns new memory */
#endif

      if (interactive_shell)
    {
      expr_unwind ();
      jump_to_top_level (DISCARD);
    }
      else
    jump_to_top_level (FORCE_EOF);
    }

#if defined (ARRAY_VARS)
  /* Second argument of 0 to get_array_value means that we don't allow
     references like array[@].  In this case, get_array_value is just
     like get_variable_value in that it does not return newly-allocated
     memory or quote the results. */
  value = (e == ']') ? get_array_value (tok, 0, (int *)NULL) : get_variable_value (v);
#else
  value = get_variable_value (v);
#endif

  tval = (value && *value) ? subexpr (value) : 0;

  return (tval);
}

/* Lexical analyzer/token reader for the expression evaluator.  Reads the
   next token and puts its value into curtok, while advancing past it.
   Updates value of tp.  May also set tokval (for number) or tokstr (for
   string). */
static void
readtok ()
{
  register char *cp;
  register unsigned char c, c1;
  register int e;

  /* Skip leading whitespace. */
  cp = tp;
  c = e = 0;
  while (cp && (c = *cp) && (cr_whitespace (c)))
    cp++;

  if (c)
    cp++;

  lasttp = tp = cp - 1;

  if (c == '\0')
    {
      lasttok = curtok;
      curtok = 0;
      tp = cp;
      return;
    }

  if (legal_variable_starter (c))
    {
      /* variable names not preceded with a dollar sign are shell variables. */
      char *savecp;
      EXPR_CONTEXT ec;
      int peektok;

      while (legal_variable_char (c))
    c = *cp++;

      c = *--cp;

#if defined (ARRAY_VARS)
      if (c == '[')
    {
      e = skipsubscript (cp, 0);
      if (cp[e] == ']')
        {
          cp += e + 1;
          c = *cp;
          e = ']';
        }
      else
        evalerror ("bad array subscript");
    }
#endif /* ARRAY_VARS */

      *cp = '\0';
      FREE (tokstr);
      tokstr = savestring (tp);
      *cp = c;

      SAVETOK (&ec);
      tokstr = (char *)NULL;    /* keep it from being freed */
      tp = savecp = cp;
      noeval = 1;
      readtok ();
      peektok = curtok;
      if (peektok == STR)   /* free new tokstr before old one is restored */
    FREE (tokstr);
      RESTORETOK (&ec);
      cp = savecp;

      /* The tests for PREINC and PREDEC aren't strictly correct, but they
     preserve old behavior if a construct like --x=9 is given. */
      if (lasttok == PREINC || lasttok == PREDEC || peektok != EQ)
    tokval = expr_streval (tokstr, e);
      else
    tokval = 0;

      lasttok = curtok;
      curtok = STR;
    }
  else if (DIGIT(c))
    {
      while (ISALNUM (c) || c == '#' || c == '@' || c == '_')
    c = *cp++;

      c = *--cp;
      *cp = '\0';

      tokval = strlong (tp);
      *cp = c;
      lasttok = curtok;
      curtok = NUM;
    }
  else
    {
      c1 = *cp++;
      if ((c == EQ) && (c1 == EQ))
    c = EQEQ;
      else if ((c == NOT) && (c1 == EQ))
    c = NEQ;
      else if ((c == GT) && (c1 == EQ))
    c = GEQ;
      else if ((c == LT) && (c1 == EQ))
    c = LEQ;
      else if ((c == LT) && (c1 == LT))
    {
      if (*cp == '=')   /* a <<= b */
        {
          assigntok = LSH;
          c = OP_ASSIGN;
          cp++;
        }
      else
        c = LSH;
    }
      else if ((c == GT) && (c1 == GT))
    {
      if (*cp == '=')
        {
          assigntok = RSH;  /* a >>= b */
          c = OP_ASSIGN;
          cp++;
        }
      else
        c = RSH;
    }
      else if ((c == BAND) && (c1 == BAND))
    c = LAND;
      else if ((c == BOR) && (c1 == BOR))
    c = LOR;
      else if ((c == '*') && (c1 == '*'))
    c = POWER;
      else if ((c == '-') && (c1 == '-') && legal_variable_starter ((unsigned char)*cp))
    c = PREDEC;
      else if ((c == '+') && (c1 == '+') && legal_variable_starter ((unsigned char)*cp))
    c = PREINC;
      else if (c1 == EQ && member (c, "*/%+-&^|"))
    {
      assigntok = c;    /* a OP= b */
      c = OP_ASSIGN;
    }
      else
    cp--;           /* `unget' the character */
      lasttok = curtok;
      curtok = c;
    }
  tp = cp;
}

static void
evalerror (msg)
     char *msg;
{
  char *name, *t;

  name = this_command_name;
  for (t = expression; whitespace (*t); t++)
    ;
  internal_error ("%s%s%s: %s (error token is \"%s\")",
           name ? name : "", name ? ": " : "", t,
           msg, (lasttp && *lasttp) ? lasttp : "");
  longjmp (evalbuf, 1);
}

/* Convert a string to an intmax_t integer, with an arbitrary base.
   0nnn -> base 8
   0[Xx]nn -> base 16
   Anything else: [base#]number (this is implemented to match ksh93)

   Base may be >=2 and <=64.  If base is <= 36, the numbers are drawn
   from [0-9][a-zA-Z], and lowercase and uppercase letters may be used
   interchangably.  If base is > 36 and <= 64, the numbers are drawn
   from [0-9][a-z][A-Z]_@ (a = 10, z = 35, A = 36, Z = 61, _ = 62, @ = 63 --
   you get the picture). */

static intmax_t
strlong (num)
     char *num;
{
  register char *s;
  register unsigned char c;
  int base, foundbase;
  intmax_t val;

  s = num;

  base = 10;
  foundbase = 0;
  if (*s == '0')
    {
      s++;

      if (*s == '\0')
    return 0;

       /* Base 16? */
      if (*s == 'x' || *s == 'X')
    {
      base = 16;
      s++;
    }
      else
    base = 8;
      foundbase++;
    }

  val = 0;
  for (c = *s++; c; c = *s++)
    {
      if (c == '#')
    {
      if (foundbase)
        evalerror ("bad number");

      /* Illegal base specifications raise an evaluation error. */
      if (val < 2 || val > 64)
        evalerror ("illegal arithmetic base");

      base = val;
      val = 0;
      foundbase++;
    }
      else if (ISALNUM(c) || (c == '_') || (c == '@'))
    {
      if (DIGIT(c))
        c = TODIGIT(c);
      else if (c >= 'a' && c <= 'z')
        c -= 'a' - 10;
      else if (c >= 'A' && c <= 'Z')
        c -= 'A' - ((base <= 36) ? 10 : 36);
      else if (c == '@')
        c = 62;
      else if (c == '_')
        c = 63;

      if (c >= base)
        evalerror ("value too great for base");

      val = (val * base) + c;
    }
      else
    break;
    }
  return (val);
}

#if defined (EXPR_TEST)
void *
xmalloc (n)
     int n;
{
  return (malloc (n));
}

void *
xrealloc (s, n)
     char *s;
     int n;
{
  return (realloc (s, n));
}

SHELL_VAR *find_variable () { return 0;}
SHELL_VAR *bind_variable () { return 0; }

char *get_string_value () { return 0; }

procenv_t top_level;

main (argc, argv)
     int argc;
     char **argv;
{
  register int i;
  intmax_t v;
  int expok;

  if (setjmp (top_level))
    exit (0);

  for (i = 1; i < argc; i++)
    {
      v = evalexp (argv[i], &expok);
      if (expok == 0)
    fprintf (stderr, "%s: expression error\n", argv[i]);
      else
    printf ("'%s' -> %ld\n", argv[i], v);
    }
  exit (0);
}

int
builtin_error (format, arg1, arg2, arg3, arg4, arg5)
     char *format;
{
  fprintf (stderr, "expr: ");
  fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  fprintf (stderr, "\n");
  return 0;
}

char *
itos (n)
     intmax_t n;
{
  return ("42");
}

#endif /* EXPR_TEST */

https://git.savannah.gnu.org/cgit/bash.git/tree/flags.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/flags.c used in: Step 1215
/* flags.c -- Everything about flags except the `set' command.  That
   is in builtins.c */

/* Copyright (C) 1987,1989 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

/* Flags hacking. */
#include "config.h"
#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "shell.h"
#include "flags.h"

#if defined (BANG_HISTORY)
#  include "bashhist.h"
#endif

#if defined (JOB_CONTROL)
extern int set_job_control __P((int));
#endif

#if defined (RESTRICTED_SHELL)
extern char *shell_name;
#endif

/* -c, -s invocation options -- not really flags, but they show up in $- */
extern int want_pending_command, read_from_stdin;

/* **************************************************************** */
/*                                  */
/*          The Standard Sh Flags.              */
/*                                  */
/* **************************************************************** */

/* Non-zero means automatically mark variables which are modified or created
   as auto export variables. */
int mark_modified_vars = 0;

/* Non-zero causes asynchronous job notification.  Otherwise, job state
   notification only takes place just before a primary prompt is printed. */
int asynchronous_notification = 0;

/* Non-zero means exit immediately if a command exits with a non-zero
   exit status. */
int exit_immediately_on_error = 0;

/* Non-zero means disable filename globbing. */
int disallow_filename_globbing = 0;

/* Non-zero means that all keyword arguments are placed into the environment
   for a command, not just those that appear on the line before the command
   name. */
int place_keywords_in_env = 0;

/* Non-zero means read commands, but don't execute them.  This is useful
   for debugging shell scripts that should do something hairy and possibly
   destructive. */
int read_but_dont_execute = 0;

/* Non-zero means end of file is after one command. */
int just_one_command = 0;

/* Non-zero means don't overwrite existing files while doing redirections. */
int noclobber = 0;

/* Non-zero means trying to get the value of $i where $i is undefined
   causes an error, instead of a null substitution. */
int unbound_vars_is_error = 0;

/* Non-zero means type out input lines after you read them. */
int echo_input_at_read = 0;

/* Non-zero means type out the command definition after reading, but
   before executing. */
int echo_command_at_execute = 0;

/* Non-zero means turn on the job control features. */
int jobs_m_flag = 0;

/* Non-zero means this shell is interactive, even if running under a
   pipe. */
int forced_interactive = 0;

/* By default, follow the symbolic links as if they were real directories
   while hacking the `cd' command.  This means that `cd ..' moves up in
   the string of symbolic links that make up the current directory, instead
   of the absolute directory.  The shell variable `nolinks' also controls
   this flag. */
int no_symbolic_links = 0;

/* **************************************************************** */
/*                                  */
/*           Non-Standard Flags Follow Here.            */
/*                                  */
/* **************************************************************** */

#if 0
/* Non-zero means do lexical scoping in the body of a FOR command. */
int lexical_scoping = 0;
#endif

/* Non-zero means no such thing as invisible variables. */
int no_invisible_vars = 0;

/* Non-zero means look up and remember command names in a hash table, */
int hashing_enabled = 1;

#if defined (BANG_HISTORY)
/* Non-zero means that we are doing history expansion.  The default.
   This means !22 gets the 22nd line of history. */
int history_expansion = 1;
#endif /* BANG_HISTORY */

/* Non-zero means that we allow comments to appear in interactive commands. */
int interactive_comments = 1;

#if defined (RESTRICTED_SHELL)
/* Non-zero means that this shell is `restricted'.  A restricted shell
   disallows: changing directories, command or path names containing `/',
   unsetting or resetting the values of $PATH and $SHELL, and any type of
   output redirection. */
int restricted = 0;     /* currently restricted */
int restricted_shell = 0;   /* shell was started in restricted mode. */
#endif /* RESTRICTED_SHELL */

/* Non-zero means that this shell is running in `privileged' mode.  This
   is required if the shell is to run setuid.  If the `-p' option is
   not supplied at startup, and the real and effective uids or gids
   differ, disable_priv_mode is called to relinquish setuid status. */
int privileged_mode = 0;

#if defined (BRACE_EXPANSION)
/* Zero means to disable brace expansion: foo{a,b} -> fooa foob */
int brace_expansion = 1;
#endif

/* **************************************************************** */
/*                                  */
/*          The Flags ALIST.                */
/*                                  */
/* **************************************************************** */

struct flags_alist shell_flags[] = {
  /* Standard sh flags. */
  { 'a', &mark_modified_vars },
#if defined (JOB_CONTROL)
  { 'b', &asynchronous_notification },
#endif /* JOB_CONTROL */
  { 'e', &exit_immediately_on_error },
  { 'f', &disallow_filename_globbing },
  { 'h', &hashing_enabled },
  { 'i', &forced_interactive },
  { 'k', &place_keywords_in_env },
#if defined (JOB_CONTROL)
  { 'm', &jobs_m_flag },
#endif /* JOB_CONTROL */
  { 'n', &read_but_dont_execute },
  { 'p', &privileged_mode },
#if defined (RESTRICTED_SHELL)
  { 'r', &restricted },
#endif /* RESTRICTED_SHELL */
  { 't', &just_one_command },
  { 'u', &unbound_vars_is_error },
  { 'v', &echo_input_at_read },
  { 'x', &echo_command_at_execute },
  { 'C', &noclobber },

  /* New flags that control non-standard things. */
#if 0
  { 'l', &lexical_scoping },
#endif
  { 'I', &no_invisible_vars },

  { 'P', &no_symbolic_links },

#if defined (BRACE_EXPANSION)
  { 'B', &brace_expansion },
#endif

#if defined (BANG_HISTORY)
  { 'H', &history_expansion },
#endif /* BANG_HISTORY */

  {0, (int *)NULL}
};

#define NUM_SHELL_FLAGS (sizeof (shell_flags) / sizeof (struct flags_alist))

char optflags[NUM_SHELL_FLAGS+4] = { '+' };

int *
find_flag (name)
     int name;
{
  int i;
  for (i = 0; shell_flags[i].name; i++)
    {
      if (shell_flags[i].name == name)
    return (shell_flags[i].value);
    }
  return (FLAG_UNKNOWN);
}

/* Change the state of a flag, and return it's original value, or return
   FLAG_ERROR if there is no flag FLAG.  ON_OR_OFF must be either
   FLAG_ON or FLAG_OFF. */
int
change_flag (flag, on_or_off)
  int flag;
  int on_or_off;
{
  int *value, old_value;

#if defined (RESTRICTED_SHELL)
  /* Don't allow "set +r" in a shell which is `restricted'. */
  if (restricted && flag == 'r' && on_or_off == FLAG_OFF)
    return (FLAG_ERROR);
#endif /* RESTRICTED_SHELL */

  value = find_flag (flag);

  if ((value == (int *)FLAG_UNKNOWN) || (on_or_off != FLAG_ON && on_or_off != FLAG_OFF))
    return (FLAG_ERROR);

  old_value = *value;

  *value = (on_or_off == FLAG_ON) ? 1 : 0;

  /* Special cases for a few flags. */
  switch (flag)
    {
#if defined (BANG_HISTORY)
    case 'H':
      if (on_or_off == FLAG_ON)
    bash_initialize_history ();
      break;
#endif

#if defined (JOB_CONTROL)
    case 'm':
      set_job_control (on_or_off == FLAG_ON);
      break;
#endif /* JOB_CONTROL */

    case 'n':
      if (interactive_shell)
    read_but_dont_execute = 0;
      break;

    case 'p':
      if (on_or_off == FLAG_OFF)
    disable_priv_mode ();
      break;

#if defined (RESTRICTED_SHELL)
    case 'r':
      if (on_or_off == FLAG_ON)
    maybe_make_restricted (shell_name);
      break;
#endif

    }

  return (old_value);
}

/* Return a string which is the names of all the currently
   set shell flags. */
char *
which_set_flags ()
{
  char *temp;
  int i, string_index;

  temp = (char *)xmalloc (1 + NUM_SHELL_FLAGS + read_from_stdin + want_pending_command);
  for (i = string_index = 0; shell_flags[i].name; i++)
    if (*(shell_flags[i].value))
      temp[string_index++] = shell_flags[i].name;

  if (want_pending_command)
    temp[string_index++] = 'c';
  if (read_from_stdin)
    temp[string_index++] = 's';

  temp[string_index] = '\0';
  return (temp);
}

void
reset_shell_flags ()
{
  mark_modified_vars = exit_immediately_on_error = disallow_filename_globbing = 0;
  place_keywords_in_env = read_but_dont_execute = just_one_command = 0;
  noclobber = unbound_vars_is_error = echo_input_at_read = 0;
  echo_command_at_execute = jobs_m_flag = forced_interactive = 0;
  no_symbolic_links = no_invisible_vars = privileged_mode = 0;

  hashing_enabled = interactive_comments = 1;

#if defined (JOB_CONTROL)
  asynchronous_notification = 0;
#endif

#if defined (BANG_HISTORY)
  history_expansion = 1;
#endif

#if defined (BRACE_EXPANSION)
  brace_expansion = 1;
#endif

#if defined (RESTRICTED_SHELL)
  restricted = 0;
#endif
}

void
initialize_flags ()
{
  register int i;

  for (i = 0; shell_flags[i].name; i++)
    optflags[i+1] = shell_flags[i].name;
  optflags[++i] = 'o';
  optflags[++i] = ';';
  optflags[i+1] = '\0';
}

https://git.savannah.gnu.org/cgit/bash.git/tree/nojobs.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/nojobs.c used in: Step 1216
/* The thing that makes children, remembers them, and contains wait loops. */

/* This file works under BSD, System V, minix, and Posix systems.  It does
   not implement job control. */

/* Copyright (C) 1987, 1989, 1992 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include "bashtypes.h"
#include "filecntl.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <stdio.h>
#include <signal.h>
#include <errno.h>

#if defined (BUFFERED_INPUT)
#  include "input.h"
#endif

/* Need to include this up here for *_TTY_DRIVER definitions. */
#include "shtty.h"

#if !defined (STRUCT_WINSIZE_IN_SYS_IOCTL)
/* For struct winsize on SCO */
/*   sys/ptem.h has winsize but needs mblk_t from sys/stream.h */
#  if defined (HAVE_SYS_PTEM_H) && defined (TIOCGWINSZ) && defined (SIGWINCH)
#    if defined (HAVE_SYS_STREAM_H)
#      include <sys/stream.h>
#    endif
#    include <sys/ptem.h>
#  endif /* HAVE_SYS_PTEM_H && TIOCGWINSZ && SIGWINCH */
#endif /* !STRUCT_WINSIZE_IN_SYS_IOCTL */

#include "shell.h"
#include "jobs.h"

#include "builtins/builtext.h"  /* for wait_builtin */

#define DEFAULT_CHILD_MAX 32

#if defined (_POSIX_VERSION) || !defined (HAVE_KILLPG)
#  define killpg(pg, sig)       kill(-(pg),(sig))
#endif /* USG || _POSIX_VERSION */

#if !defined (HAVE_SIGINTERRUPT)
#  define siginterrupt(sig, code)
#endif /* !HAVE_SIGINTERRUPT */

#if defined (HAVE_WAITPID)
#  define WAITPID(pid, statusp, options) waitpid (pid, statusp, options)
#else
#  define WAITPID(pid, statusp, options) wait (statusp)
#endif /* !HAVE_WAITPID */

/* Return the fd from which we are actually getting input. */
#define input_tty() (shell_tty != -1) ? shell_tty : fileno (stderr)

#if !defined (errno)
extern int errno;
#endif /* !errno */

#if defined (READLINE)
extern void rl_set_screen_size __P((int, int));
#endif

extern int interactive, interactive_shell, login_shell;
extern int subshell_environment;
extern int last_command_exit_value;
extern int interrupt_immediately;
extern sh_builtin_func_t *this_shell_builtin;
#if defined (HAVE_POSIX_SIGNALS)
extern sigset_t top_level_mask;
#endif
extern procenv_t wait_intr_buf;
extern int wait_signal_received;

pid_t last_made_pid = NO_PID;
pid_t last_asynchronous_pid = NO_PID;

/* Call this when you start making children. */
int already_making_children = 0;

/* The controlling tty for this shell. */
int shell_tty = -1;

/* If this is non-zero, $LINES and $COLUMNS are reset after every process
   exits from get_tty_state(). */
int check_window_size;

/* STATUS and FLAGS are only valid if pid != NO_PID
   STATUS is only valid if (flags & PROC_RUNNING) == 0 */
struct proc_status {
  pid_t pid;
  int status;   /* Exit status of PID or 128 + fatal signal number */
  int flags;
};

/* Values for proc_status.flags */
#define PROC_RUNNING    0x01
#define PROC_NOTIFIED   0x02
#define PROC_ASYNC  0x04

/* Return values from find_status_by_pid */
#define PROC_BAD     -1
#define PROC_STILL_ALIVE -2

static struct proc_status *pid_list = (struct proc_status *)NULL;
static int pid_list_size;
static int wait_sigint_received;

static long child_max = -1L;

static void alloc_pid_list __P((void));
static int find_proc_slot __P((void));
static int find_index_by_pid __P((pid_t));
static int find_status_by_pid __P((pid_t));
static int process_exit_status __P((WAIT));
static void set_pid_status __P((pid_t, WAIT));
static void set_pid_flags __P((pid_t, int));
static void unset_pid_flags __P((pid_t, int));
static void add_pid __P((pid_t, int));
static void mark_dead_jobs_as_notified __P((int));

static void get_new_window_size __P((int));
static sighandler sigwinch_sighandler __P((int));
static sighandler wait_sigint_handler __P((int));

#if defined (HAVE_WAITPID)
static void reap_zombie_children __P((void));
#endif

static void restore_sigint_handler __P((void));

/* Allocate new, or grow existing PID_LIST. */
static void
alloc_pid_list ()
{
  register int i;
  int old = pid_list_size;

  pid_list_size += 10;
  pid_list = (struct proc_status *)xrealloc (pid_list, pid_list_size * sizeof (struct proc_status));

  /* None of the newly allocated slots have process id's yet. */
  for (i = old; i < pid_list_size; i++)
    pid_list[i].pid = NO_PID;
}

/* Return the offset within the PID_LIST array of an empty slot.  This can
   create new slots if all of the existing slots are taken. */
static int
find_proc_slot ()
{
  register int i;

  for (i = 0; i < pid_list_size; i++)
    if (pid_list[i].pid == NO_PID)
      return (i);

  if (i == pid_list_size)
    alloc_pid_list ();

  return (i);
}

/* Return the offset within the PID_LIST array of a slot containing PID,
   or the value NO_PID if the pid wasn't found. */
static int
find_index_by_pid (pid)
     pid_t pid;
{
  register int i;

  for (i = 0; i < pid_list_size; i++)
    if (pid_list[i].pid == pid)
      return (i);

  return (NO_PID);
}

/* Return the status of PID as looked up in the PID_LIST array.  A
   return value of PROC_BAD indicates that PID wasn't found. */
static int
find_status_by_pid (pid)
     pid_t pid;
{
  int i;

  i = find_index_by_pid (pid);
  if (i == NO_PID)
    return (PROC_BAD);
  if (pid_list[i].flags & PROC_RUNNING)
    return (PROC_STILL_ALIVE);
  return (pid_list[i].status);
}

static int
process_exit_status (status)
     WAIT status;
{
  if (WIFSIGNALED (status))
    return (128 + WTERMSIG (status));
  else
    return (WEXITSTATUS (status));
}

/* Give PID the status value STATUS in the PID_LIST array. */
static void
set_pid_status (pid, status)
     pid_t pid;
     WAIT status;
{
  int slot;

  slot = find_index_by_pid (pid);
  if (slot == NO_PID)
    return;

  pid_list[slot].status = process_exit_status (status);
  pid_list[slot].flags &= ~PROC_RUNNING;
  /* If it's not a background process, mark it as notified so it gets
     cleaned up. */
  if ((pid_list[slot].flags & PROC_ASYNC) == 0)
    pid_list[slot].flags |= PROC_NOTIFIED;
}

/* Give PID the flags FLAGS in the PID_LIST array. */
static void
set_pid_flags (pid, flags)
     pid_t pid;
     int flags;
{
  int slot;

  slot = find_index_by_pid (pid);
  if (slot == NO_PID)
    return;

  pid_list[slot].flags |= flags;
}

/* Unset FLAGS for PID in the pid list */
static void
unset_pid_flags (pid, flags)
     pid_t pid;
     int flags;
{
  int slot;

  slot = find_index_by_pid (pid);
  if (slot == NO_PID)
    return;

  pid_list[slot].flags &= ~flags;
}

static void
add_pid (pid, async)
     pid_t pid;
     int async;
{
  int slot;

  slot = find_proc_slot ();

  pid_list[slot].pid = pid;
  pid_list[slot].status = -1;
  pid_list[slot].flags = PROC_RUNNING;
  if (async)
    pid_list[slot].flags |= PROC_ASYNC;
}

static void
mark_dead_jobs_as_notified (force)
     int force;
{
  register int i, ndead;

  /* first, count the number of non-running async jobs if FORCE == 0 */
  for (i = ndead = 0; force == 0 && i < pid_list_size; i++)
    {
      if (pid_list[i].pid == NO_PID)
    continue;
      if (((pid_list[i].flags & PROC_RUNNING) == 0) &&
       (pid_list[i].flags & PROC_ASYNC))
    ndead++;
    }

  if (child_max < 0)
    child_max = getmaxchild ();
  if (child_max < 0)
    child_max = DEFAULT_CHILD_MAX;

  if (force == 0 && ndead <= child_max)
    return;

  /* If FORCE == 0, we just mark as many non-running async jobs as notified
     to bring us under the CHILD_MAX limit. */
  for (i = 0; i < pid_list_size; i++)
    {
      if (pid_list[i].pid == NO_PID)
    continue;
      if (((pid_list[i].flags & PROC_RUNNING) == 0) &&
       pid_list[i].pid != last_asynchronous_pid)
    {
      pid_list[i].flags |= PROC_NOTIFIED;
      if (force == 0 && (pid_list[i].flags & PROC_ASYNC) && --ndead <= child_max)
        break;
    }
    }
}

/* Remove all dead, notified jobs from the pid_list. */
int
cleanup_dead_jobs ()
{
  register int i;

#if defined (HAVE_WAITPID)
  reap_zombie_children ();
#endif

  for (i = 0; i < pid_list_size; i++)
    {
      if ((pid_list[i].flags & PROC_RUNNING) == 0 &&
      (pid_list[i].flags & PROC_NOTIFIED))
    pid_list[i].pid = NO_PID;
    }

  return 0;
}

void
reap_dead_jobs ()
{
  mark_dead_jobs_as_notified (0);
  cleanup_dead_jobs ();
}

/* Initialize the job control mechanism, and set up the tty stuff. */
initialize_job_control (force)
     int force;
{
  shell_tty = fileno (stderr);

  if (interactive)
    get_tty_state ();
}

#if defined (TIOCGWINSZ) && defined (SIGWINCH)
static SigHandler *old_winch = (SigHandler *)SIG_DFL;

static void
get_new_window_size (from_sig)
     int from_sig;
{
  struct winsize win;
  int tty;

  tty = input_tty ();
  if (tty >= 0 && (ioctl (tty, TIOCGWINSZ, &win) == 0) &&
      win.ws_row > 0 && win.ws_col > 0)
    {
#if defined (aixpc)
      shell_tty_info.c_winsize = win;   /* structure copying */
#endif
      sh_set_lines_and_columns (win.ws_row, win.ws_col);
#if defined (READLINE)
      rl_set_screen_size (win.ws_row, win.ws_col);
#endif
    }
}

static sighandler
sigwinch_sighandler (sig)
     int sig;
{
#if defined (MUST_REINSTALL_SIGHANDLERS)
  set_signal_handler (SIGWINCH, sigwinch_sighandler);
#endif /* MUST_REINSTALL_SIGHANDLERS */
  get_new_window_size (1);
}
#else
static void
get_new_window_size (from_sig)
     int from_sig;
{
}
#endif /* TIOCGWINSZ && SIGWINCH */

void
set_sigwinch_handler ()
{
#if defined (TIOCGWINSZ) && defined (SIGWINCH)
  old_winch = set_signal_handler (SIGWINCH, sigwinch_sighandler);
#endif
}

void
unset_sigwinch_handler ()
{
#if defined (TIOCGWINSZ) && defined (SIGWINCH)
  set_signal_handler (SIGWINCH, old_winch);
#endif
}

/* Setup this shell to handle C-C, etc. */
void
initialize_job_signals ()
{
  set_signal_handler (SIGINT, sigint_sighandler);
  set_sigwinch_handler ();

  /* If this is a login shell we don't wish to be disturbed by
     stop signals. */
  if (login_shell)
    ignore_tty_job_signals ();
}

#if defined (HAVE_WAITPID)
/* Collect the status of all zombie children so that their system
   resources can be deallocated. */
static void
reap_zombie_children ()
{
#if defined (WNOHANG)
  pid_t pid;
  WAIT status;

  while ((pid = waitpid (-1, (int *)&status, WNOHANG)) > 0)
    set_pid_status (pid, status);
#endif
}
#endif /* WAITPID && WNOHANG */

/* Fork, handling errors.  Returns the pid of the newly made child, or 0.
   COMMAND is just for remembering the name of the command; we don't do
   anything else with it.  ASYNC_P says what to do with the tty.  If
   non-zero, then don't give it away. */
pid_t
make_child (command, async_p)
     char *command;
     int async_p;
{
  pid_t pid;
#if defined (HAVE_WAITPID)
  int retry = 1;
#endif /* HAVE_WAITPID */

  /* Discard saved memory. */
  if (command)
    free (command);

  start_pipeline ();

#if defined (BUFFERED_INPUT)
  /* If default_buffered_input is active, we are reading a script.  If
     the command is asynchronous, we have already duplicated /dev/null
     as fd 0, but have not changed the buffered stream corresponding to
     the old fd 0.  We don't want to sync the stream in this case. */
  if (default_buffered_input != -1 && (!async_p || default_buffered_input > 0))
    sync_buffered_stream (default_buffered_input);
#endif /* BUFFERED_INPUT */

  /* Create the child, handle severe errors. */
#if defined (HAVE_WAITPID)
  retry_fork:
#endif /* HAVE_WAITPID */

  if ((pid = fork ()) < 0)
    {
#if defined (HAVE_WAITPID)
      /* Posix systems with a non-blocking waitpid () system call available
     get another chance after zombies are reaped. */
      if (errno == EAGAIN && retry)
    {
      reap_zombie_children ();
      retry = 0;
      goto retry_fork;
    }
#endif /* HAVE_WAITPID */

      sys_error ("fork");

      throw_to_top_level ();
    }

  if (pid == 0)
    {
#if defined (BUFFERED_INPUT)
      unset_bash_input (0);
#endif /* BUFFERED_INPUT */

#if defined (HAVE_POSIX_SIGNALS)
      /* Restore top-level signal mask. */
      sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
#endif

      /* Ignore INT and QUIT in asynchronous children. */
      if (async_p)
    last_asynchronous_pid = getpid ();

      default_tty_job_signals ();
    }
  else
    {
      /* In the parent. */

      last_made_pid = pid;

      if (async_p)
    last_asynchronous_pid = pid;

      add_pid (pid, async_p);
    }
  return (pid);
}

void
ignore_tty_job_signals ()
{
#if defined (SIGTSTP)
  set_signal_handler (SIGTSTP, SIG_IGN);
  set_signal_handler (SIGTTIN, SIG_IGN);
  set_signal_handler (SIGTTOU, SIG_IGN);
#endif
}

void
default_tty_job_signals ()
{
#if defined (SIGTSTP)
  set_signal_handler (SIGTSTP, SIG_DFL);
  set_signal_handler (SIGTTIN, SIG_DFL);
  set_signal_handler (SIGTTOU, SIG_DFL);
#endif
}

/* Wait for a single pid (PID) and return its exit status.  Called by
   the wait builtin. */
int
wait_for_single_pid (pid)
     pid_t pid;
{
  pid_t got_pid;
  WAIT status;
  int pstatus;

  pstatus = find_status_by_pid (pid);

  if (pstatus == PROC_BAD)
    {
      internal_error ("wait: pid %ld is not a child of this shell", (long)pid);
      return (127);
    }

  if (pstatus != PROC_STILL_ALIVE)
    return (pstatus);

  siginterrupt (SIGINT, 1);
  while ((got_pid = WAITPID (pid, &status, 0)) != pid)
    {
      if (got_pid < 0)
    {
      if (errno != EINTR && errno != ECHILD)
        {
          siginterrupt (SIGINT, 0);
          sys_error ("wait");
        }
      break;
    }
      else if (got_pid > 0)
    set_pid_status (got_pid, status);
    }

  if (got_pid > 0)
    {
      set_pid_status (got_pid, status);
      set_pid_flags (got_pid, PROC_NOTIFIED);
    }

  siginterrupt (SIGINT, 0);
  QUIT;

  return (got_pid > 0 ? process_exit_status (status) : -1);
}

/* Wait for all of the shell's children to exit.  Called by the `wait'
   builtin. */
void
wait_for_background_pids ()
{
  pid_t got_pid;
  WAIT status;

  /* If we aren't using job control, we let the kernel take care of the
     bookkeeping for us.  wait () will return -1 and set errno to ECHILD
     when there are no more unwaited-for child processes on both
     4.2 BSD-based and System V-based systems. */

  siginterrupt (SIGINT, 1);

  /* Wait for ECHILD */
  while ((got_pid = WAITPID (-1, &status, 0)) != -1)
    set_pid_status (got_pid, status);

  if (errno != EINTR && errno != ECHILD)
    {
      siginterrupt (SIGINT, 0);
      sys_error("wait");
    }

  siginterrupt (SIGINT, 0);
  QUIT;

  mark_dead_jobs_as_notified (1);
  cleanup_dead_jobs ();
}

/* Make OLD_SIGINT_HANDLER the SIGINT signal handler. */
#define INVALID_SIGNAL_HANDLER (SigHandler *)wait_for_background_pids
static SigHandler *old_sigint_handler = INVALID_SIGNAL_HANDLER;

static void
restore_sigint_handler ()
{
  if (old_sigint_handler != INVALID_SIGNAL_HANDLER)
    {
      set_signal_handler (SIGINT, old_sigint_handler);
      old_sigint_handler = INVALID_SIGNAL_HANDLER;
    }
}

/* Handle SIGINT while we are waiting for children in a script to exit.
   All interrupts are effectively ignored by the shell, but allowed to
   kill a running job. */
static sighandler
wait_sigint_handler (sig)
     int sig;
{
  SigHandler *sigint_handler;

  /* If we got a SIGINT while in `wait', and SIGINT is trapped, do
     what POSIX.2 says (see builtins/wait.def for more info). */
  if (this_shell_builtin && this_shell_builtin == wait_builtin &&
      signal_is_trapped (SIGINT) &&
      ((sigint_handler = trap_to_sighandler (SIGINT)) == trap_handler))
    {
      last_command_exit_value = EXECUTION_FAILURE;
      restore_sigint_handler ();
      interrupt_immediately = 0;
      trap_handler (SIGINT);    /* set pending_traps[SIGINT] */
      wait_signal_received = SIGINT;
      longjmp (wait_intr_buf, 1);
    }

  if (interrupt_immediately)
    {
      last_command_exit_value = EXECUTION_FAILURE;
      restore_sigint_handler ();
      ADDINTERRUPT;
      QUIT;
    }

  wait_sigint_received = 1;

  SIGRETURN (0);
}

/* Wait for pid (one of our children) to terminate.  This is called only
   by the execution code in execute_cmd.c. */
int
wait_for (pid)
     pid_t pid;
{
  int return_val, pstatus;
  pid_t got_pid;
  WAIT status;

  pstatus = find_status_by_pid (pid);

  if (pstatus == PROC_BAD)
    return (0);

  if (pstatus != PROC_STILL_ALIVE)
    return (pstatus);

  /* If we are running a script, ignore SIGINT while we're waiting for
     a child to exit.  The loop below does some of this, but not all. */
  wait_sigint_received = 0;
  if (interactive_shell == 0)
    old_sigint_handler = set_signal_handler (SIGINT, wait_sigint_handler);

  while ((got_pid = WAITPID (-1, &status, 0)) != pid) /* XXX was pid now -1 */
    {
      if (got_pid < 0 && errno == ECHILD)
    {
#if !defined (_POSIX_VERSION)
      status.w_termsig = status.w_retcode = 0;
#else
      status = 0;
#endif /* _POSIX_VERSION */
      break;
    }
      else if (got_pid < 0 && errno != EINTR)
    programming_error ("wait_for(%ld): %s", (long)pid, strerror(errno));
      else if (got_pid > 0)
    set_pid_status (got_pid, status);
    }

  if (got_pid > 0)
    set_pid_status (got_pid, status);

#if defined (HAVE_WAITPID)
  if (got_pid >= 0)
    reap_zombie_children ();
#endif /* HAVE_WAITPID */

  if (interactive_shell == 0)
    {
      SigHandler *temp_handler;

      temp_handler = old_sigint_handler;
      restore_sigint_handler ();

      /* If the job exited because of SIGINT, make sure the shell acts as if
     it had received one also. */
      if (WIFSIGNALED (status) && (WTERMSIG (status) == SIGINT))
    {

      if (maybe_call_trap_handler (SIGINT) == 0)
        {
          if (temp_handler == SIG_DFL)
        termination_unwind_protect (SIGINT);
          else if (temp_handler != INVALID_SIGNAL_HANDLER && temp_handler != SIG_IGN)
        (*temp_handler) (SIGINT);
        }
    }
    }

  /* Default return value. */
  /* ``a full 8 bits of status is returned'' */
  return_val = process_exit_status (status);

#if !defined (DONT_REPORT_SIGPIPE)
  if ((WIFSTOPPED (status) == 0) && WIFSIGNALED (status) &&
    (WTERMSIG (status) != SIGINT))
#else
  if ((WIFSTOPPED (status) == 0) && WIFSIGNALED (status) &&
    (WTERMSIG (status) != SIGINT) && (WTERMSIG (status) != SIGPIPE))
#endif
    {
      fprintf (stderr, "%s", strsignal (WTERMSIG (status)));
      if (WIFCORED (status))
    fprintf (stderr, " (core dumped)");
      fprintf (stderr, "\n");
    }

  if (interactive_shell && subshell_environment == 0)
    {
      if (WIFSIGNALED (status) || WIFSTOPPED (status))
    set_tty_state ();
      else
    get_tty_state ();
    }

  return (return_val);
}

/* Give PID SIGNAL.  This determines what job the pid belongs to (if any).
   If PID does belong to a job, and the job is stopped, then CONTinue the
   job after giving it SIGNAL.  Returns -1 on failure.  If GROUP is non-null,
   then kill the process group associated with PID. */
int
kill_pid (pid, signal, group)
     pid_t pid;
     int signal, group;
{
  int result;

  result = group ? killpg (pid, signal) : kill (pid, signal);
  return (result);
}

static TTYSTRUCT shell_tty_info;
static int got_tty_state;

/* Fill the contents of shell_tty_info with the current tty info. */
get_tty_state ()
{
  int tty;

  tty = input_tty ();
  if (tty != -1)
    {
      ttgetattr (tty, &shell_tty_info);
      got_tty_state = 1;
      if (check_window_size)
    get_new_window_size (0);
    }
}

/* Make the current tty use the state in shell_tty_info. */
int
set_tty_state ()
{
  int tty;

  tty = input_tty ();
  if (tty != -1)
    {
      if (got_tty_state == 0)
    return 0;
      ttsetattr (tty, &shell_tty_info);
    }
  return 0;
}

/* Give the terminal to PGRP.  */
give_terminal_to (pgrp, force)
     pid_t pgrp;
     int force;
{
}

/* Stop a pipeline. */
int
stop_pipeline (async, ignore)
     int async;
     COMMAND *ignore;
{
  already_making_children = 0;
  return 0;
}

void
start_pipeline ()
{
  already_making_children = 1;
}

void
stop_making_children ()
{
  already_making_children = 0;
}

int
get_job_by_pid (pid, block)
     pid_t pid;
     int block;
{
  int i;

  i = find_index_by_pid (pid);
  return ((i == NO_PID) ? PROC_BAD : i);
}

/* Print descriptive information about the job with leader pid PID. */
void
describe_pid (pid)
     pid_t pid;
{
  fprintf (stderr, "%ld\n", (long) pid);
}

void
unfreeze_jobs_list ()
{
}

int
count_all_jobs ()
{
  return 0;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/subst.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/subst.c used in: Step 1217
/* subst.c -- The part of the shell that does parameter, command, and
   globbing substitutions. */

/* ``Have a little faith, there's magic in the night.  You ain't a
     beauty, but, hey, you're alright.'' */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include "bashtypes.h"
#include <stdio.h>
#include "chartypes.h"
#include <pwd.h>
#include <signal.h>
#include <errno.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "bashansi.h"
#include "posixstat.h"

#include "shell.h"
#include "flags.h"
#include "jobs.h"
#include "execute_cmd.h"
#include "filecntl.h"
#include "trap.h"
#include "pathexp.h"
#include "mailcheck.h"

#include "shmbutil.h"

#include "builtins/getopt.h"
#include "builtins/common.h"

#include <tilde/tilde.h>
#include <glob/strmatch.h>

#if !defined (errno)
extern int errno;
#endif /* !errno */

/* The size that strings change by. */
#define DEFAULT_INITIAL_ARRAY_SIZE 112
#define DEFAULT_ARRAY_SIZE 128

/* Variable types. */
#define VT_VARIABLE 0
#define VT_POSPARMS 1
#define VT_ARRAYVAR 2
#define VT_ARRAYMEMBER  3

/* Flags for quoted_strchr */
#define ST_BACKSL   0x01
#define ST_CTLESC   0x02
#define ST_SQUOTE   0x04    /* unused yet */
#define ST_DQUOTE   0x08    /* unused yet */

/* Flags for the string extraction functions. */
#define EX_NOALLOC  0x01    /* just skip; don't return substring */
#define EX_VARNAME  0x02    /* variable name; for string_extract () */

/* These defs make it easier to use the editor. */
#define LBRACE      '{'
#define RBRACE      '}'
#define LPAREN      '('
#define RPAREN      ')'

/* Evaluates to 1 if C is one of the shell's special parameters whose length
   can be taken, but is also one of the special expansion characters. */
#define VALID_SPECIAL_LENGTH_PARAM(c) \
  ((c) == '-' || (c) == '?' || (c) == '#')

/* Evaluates to 1 if C is one of the shell's special parameters for which an
   indirect variable reference may be made. */
#define VALID_INDIR_PARAM(c) \
  ((c) == '#' || (c) == '?' || (c) == '@' || (c) == '*')

/* Evaluates to 1 if C is one of the OP characters that follows the parameter
   in ${parameter[:]OPword}. */
#define VALID_PARAM_EXPAND_CHAR(c) (sh_syntaxtab[(unsigned char)c] & CSUBSTOP)

/* Evaluates to 1 if this is one of the shell's special variables. */
#define SPECIAL_VAR(name, wi) \
 ((DIGIT (*name) && all_digits (name)) || \
      (name[1] == '\0' && (sh_syntaxtab[(unsigned char)*name] & CSPECVAR)) || \
      (wi && name[2] == '\0' && VALID_INDIR_PARAM (name[1])))

/* An expansion function that takes a string and a quoted flag and returns
   a WORD_LIST *.  Used as the type of the third argument to
   expand_string_if_necessary(). */
typedef WORD_LIST *EXPFUNC __P((char *, int));

/* Process ID of the last command executed within command substitution. */
pid_t last_command_subst_pid = NO_PID;
pid_t current_command_subst_pid = NO_PID;

/* Variables used to keep track of the characters in IFS. */
SHELL_VAR *ifs_var;
char *ifs_value;
unsigned char ifs_cmap[UCHAR_MAX + 1];
unsigned char ifs_firstc;

/* Extern functions and variables from different files. */
extern int last_command_exit_value;
extern int subshell_environment;
extern int eof_encountered;
extern int return_catch_flag, return_catch_value;
extern pid_t dollar_dollar_pid;
extern int posixly_correct;
extern char *this_command_name;
extern struct fd_bitmap *current_fds_to_close;
extern int wordexp_only;

/* Non-zero means to allow unmatched globbed filenames to expand to
   a null file. */
int allow_null_glob_expansion;

#if 0
/* Variables to keep track of which words in an expanded word list (the
   output of expand_word_list_internal) are the result of globbing
   expansions.  GLOB_ARGV_FLAGS is used by execute_cmd.c.
   (CURRENTLY UNUSED). */
char *glob_argv_flags;
static int glob_argv_flags_size;
#endif

static WORD_LIST expand_word_error, expand_word_fatal;
static char expand_param_error, expand_param_fatal;

/* Tell the expansion functions to not longjmp back to top_level on fatal
   errors.  Enabled when doing completion and prompt string expansion. */
static int no_longjmp_on_fatal_error = 0;

/* Set by expand_word_unsplit; used to inhibit splitting and re-joining
   $* on $IFS, primarily when doing assignment statements. */
static int expand_no_split_dollar_star = 0;

/* Used to hold a list of variable assignments preceding a command.  Global
   so the SIGCHLD handler in jobs.c can unwind-protect it when it runs a
   SIGCHLD trap. */
WORD_LIST *subst_assign_varlist = (WORD_LIST *)NULL;

/* A WORD_LIST of words to be expanded by expand_word_list_internal,
   without any leading variable assignments. */
static WORD_LIST *garglist = (WORD_LIST *)NULL;

static char *quoted_substring __P((char *, int, int));
static int quoted_strlen __P((char *));
static char *quoted_strchr __P((char *, int, int));

static char *expand_string_if_necessary __P((char *, int, EXPFUNC *));
static inline char *expand_string_to_string_internal __P((char *, int, EXPFUNC *));
static WORD_LIST *call_expand_word_internal __P((WORD_DESC *, int, int, int *, int *));
static WORD_LIST *expand_string_internal __P((char *, int));
static WORD_LIST *expand_string_leave_quoted __P((char *, int));
static WORD_LIST *expand_string_for_rhs __P((char *, int, int *, int *));

static WORD_LIST *list_quote_escapes __P((WORD_LIST *));
static char *dequote_escapes __P((char *));
static char *make_quoted_char __P((int));
static WORD_LIST *quote_list __P((WORD_LIST *));
static WORD_LIST *dequote_list __P((WORD_LIST *));
static char *remove_quoted_escapes __P((char *));
static char *remove_quoted_nulls __P((char *));

static int unquoted_substring __P((char *, char *));
static int unquoted_member __P((int, char *));

static int do_assignment_internal __P((const char *, int));

static char *string_extract_verbatim __P((char *, int *, char *));
static char *string_extract __P((char *, int *, char *, int));
static char *string_extract_double_quoted __P((char *, int *, int));
static inline char *string_extract_single_quoted __P((char *, int *));
static inline int skip_single_quoted __P((char *, size_t, int));
static int skip_double_quoted __P((char *, size_t, int));
static char *extract_delimited_string __P((char *, int *, char *, char *, char *, int));
static char *extract_dollar_brace_string __P((char *, int *, int, int));

static char *pos_params __P((char *, int, int, int));

static char *remove_pattern __P((char *, char *, int));
static int match_pattern_char __P((char *, char *));
static int match_pattern __P((char *, char *, int, char **, char **));
static int getpatspec __P((int, char *));
static char *getpattern __P((char *, int, int));
static char *variable_remove_pattern __P((char *, char *, int, int));
static char *list_remove_pattern __P((WORD_LIST *, char *, int, int, int));
static char *parameter_list_remove_pattern __P((int, char *, int, int));
#ifdef ARRAY_VARS
static char *array_remove_pattern __P((ARRAY *, char *, int, char *, int));
#endif
static char *parameter_brace_remove_pattern __P((char *, char *, char *, int, int));

static char *process_substitute __P((char *, int));

static char *read_comsub __P((int, int));

#ifdef ARRAY_VARS
static arrayind_t array_length_reference __P((char *));
#endif

static int valid_brace_expansion_word __P((char *, int));
static char *parameter_brace_expand_word __P((char *, int, int));
static char *parameter_brace_expand_indir __P((char *, int, int));
static char *parameter_brace_expand_rhs __P((char *, char *, int, int, int *, int *));
static void parameter_brace_expand_error __P((char *, char *));

static int valid_length_expression __P((char *));
static intmax_t parameter_brace_expand_length __P((char *));

static char *skiparith __P((char *, int));
static int verify_substring_values __P((char *, char *, int, intmax_t *, intmax_t *));
static int get_var_and_type __P((char *, char *, SHELL_VAR **, char **));
static char *parameter_brace_substring __P((char *, char *, char *, int));

static char *pos_params_pat_subst __P((char *, char *, char *, int));

static char *parameter_brace_patsub __P((char *, char *, char *, int));

static char *parameter_brace_expand __P((char *, int *, int, int *, int *));
static char *param_expand __P((char *, int *, int, int *, int *, int *, int *, int));

static WORD_LIST *expand_word_internal __P((WORD_DESC *, int, int, int *, int *));

static WORD_LIST *word_list_split __P((WORD_LIST *));

static WORD_LIST *separate_out_assignments __P((WORD_LIST *));
static WORD_LIST *glob_expand_word_list __P((WORD_LIST *, int));
#ifdef BRACE_EXPANSION
static WORD_LIST *brace_expand_word_list __P((WORD_LIST *, int));
#endif
static WORD_LIST *shell_expand_word_list __P((WORD_LIST *, int));
static WORD_LIST *expand_word_list_internal __P((WORD_LIST *, int));

/* **************************************************************** */
/*                                  */
/*          Utility Functions               */
/*                                  */
/* **************************************************************** */

#ifdef INCLUDE_UNUSED
static char *
quoted_substring (string, start, end)
     char *string;
     int start, end;
{
  register int len, l;
  register char *result, *s, *r;

  len = end - start;

  /* Move to string[start], skipping quoted characters. */
  for (s = string, l = 0; *s && l < start; )
    {
      if (*s == CTLESC)
    {
      s++;
      continue;
    }
      l++;
      if (*s == 0)
    break;
    }

  r = result = (char *)xmalloc (2*len + 1);      /* save room for quotes */

  /* Copy LEN characters, including quote characters. */
  s = string + l;
  for (l = 0; l < len; s++)
    {
      if (*s == CTLESC)
    *r++ = *s++;
      *r++ = *s;
      l++;
      if (*s == 0)
    break;
    }
  *r = '\0';
  return result;
}
#endif

#ifdef INCLUDE_UNUSED
/* Return the length of S, skipping over quoted characters */
static int
quoted_strlen (s)
     char *s;
{
  register char *p;
  int i;

  i = 0;
  for (p = s; *p; p++)
    {
      if (*p == CTLESC)
    {
      p++;
      if (*p == 0)
        return (i + 1);
    }
      i++;
    }

  return i;
}
#endif

/* Find the first occurrence of character C in string S, obeying shell
   quoting rules.  If (FLAGS & ST_BACKSL) is non-zero, backslash-escaped
   characters are skipped.  If (FLAGS & ST_CTLESC) is non-zero, characters
   escaped with CTLESC are skipped. */
static char *
quoted_strchr (s, c, flags)
     char *s;
     int c, flags;
{
  register char *p;

  for (p = s; *p; p++)
    {
      if (((flags & ST_BACKSL) && *p == '\\')
        || ((flags & ST_CTLESC) && *p == CTLESC))
    {
      p++;
      if (*p == '\0')
        return ((char *)NULL);
      continue;
    }
      else if (*p == c)
    return p;
    }
  return ((char *)NULL);
}

/* Return 1 if CHARACTER appears in an unquoted portion of
   STRING.  Return 0 otherwise.  CHARACTER must be a single-byte character. */
static int
unquoted_member (character, string)
     int character;
     char *string;
{
  size_t slen;
  int sindex, c;
  DECLARE_MBSTATE;

  slen = strlen (string);
  sindex = 0;
  while (c = string[sindex])
    {
      if (c == character)
    return (1);

      switch (c)
    {
    default:
      ADVANCE_CHAR (string, slen, sindex);
      break;

    case '\\':
      sindex++;
      if (string[sindex])
        ADVANCE_CHAR (string, slen, sindex);
      break;

    case '\'':
      sindex = skip_single_quoted (string, slen, ++sindex);
      break;

    case '"':
      sindex = skip_double_quoted (string, slen, ++sindex);
      break;
    }
    }
  return (0);
}

/* Return 1 if SUBSTR appears in an unquoted portion of STRING. */
static int
unquoted_substring (substr, string)
     char *substr, *string;
{
  size_t slen;
  int sindex, c, sublen;
  DECLARE_MBSTATE;

  if (substr == 0 || *substr == '\0')
    return (0);

  slen = strlen (string);
  sublen = strlen (substr);
  for (sindex = 0; c = string[sindex]; )
    {
      if (STREQN (string + sindex, substr, sublen))
    return (1);

      switch (c)
    {
    case '\\':
      sindex++;

      if (string[sindex])
        ADVANCE_CHAR (string, slen, sindex);
      break;

    case '\'':
      sindex = skip_single_quoted (string, slen, ++sindex);
      break;

    case '"':
      sindex = skip_double_quoted (string, slen, ++sindex);
      break;

    default:
      ADVANCE_CHAR (string, slen, sindex);
      break;
    }
    }
  return (0);
}

/* Most of the substitutions must be done in parallel.  In order
   to avoid using tons of unclear goto's, I have some functions
   for manipulating malloc'ed strings.  They all take INDX, a
   pointer to an integer which is the offset into the string
   where manipulation is taking place.  They also take SIZE, a
   pointer to an integer which is the current length of the
   character array for this string. */

/* Append SOURCE to TARGET at INDEX.  SIZE is the current amount
   of space allocated to TARGET.  SOURCE can be NULL, in which
   case nothing happens.  Gets rid of SOURCE by freeing it.
   Returns TARGET in case the location has changed. */
INLINE char *
sub_append_string (source, target, indx, size)
     char *source, *target;
     int *indx, *size;
{
  if (source)
    {
      int srclen, n;

      srclen = STRLEN (source);
      if (srclen >= (int)(*size - *indx))
    {
      n = srclen + *indx;
      n = (n + DEFAULT_ARRAY_SIZE) - (n % DEFAULT_ARRAY_SIZE);
      target = (char *)xrealloc (target, (*size = n));
    }

      FASTCOPY (source, target + *indx, srclen);
      *indx += srclen;
      target[*indx] = '\0';

      free (source);
    }
  return (target);
}

#if 0
/* UNUSED */
/* Append the textual representation of NUMBER to TARGET.
   INDX and SIZE are as in SUB_APPEND_STRING. */
char *
sub_append_number (number, target, indx, size)
     intmax_t number;
     int *indx, *size;
     char *target;
{
  char *temp;

  temp = itos (number);
  return (sub_append_string (temp, target, indx, size));
}
#endif

/* Extract a substring from STRING, starting at SINDEX and ending with
   one of the characters in CHARLIST.  Don't make the ending character
   part of the string.  Leave SINDEX pointing at the ending character.
   Understand about backslashes in the string.  If (flags & EX_VARNAME)
   is non-zero, and array variables have been compiled into the shell,
   everything between a `[' and a corresponding `]' is skipped over.
   If (flags & EX_NOALLOC) is non-zero, don't return the substring, just
   update SINDEX. */
static char *
string_extract (string, sindex, charlist, flags)
     char *string;
     int *sindex;
     char *charlist;
     int flags;
{
  register int c, i;
  size_t slen;
  char *temp;
  DECLARE_MBSTATE;

  slen = strlen (string + *sindex) + *sindex;
  i = *sindex;
  while (c = string[i])
    {
      if (c == '\\')
    {
      if (string[i + 1])
        i++;
      else
        break;
    }
#if defined (ARRAY_VARS)
      else if ((flags & EX_VARNAME) && c == '[')
    {
      int ni;
      /* If this is an array subscript, skip over it and continue. */
      ni = skipsubscript (string, i);
      if (string[ni] == ']')
        i = ni;
    }
#endif
      else if (MEMBER (c, charlist))
      break;

      ADVANCE_CHAR (string, slen, i);
    }

  temp = (flags & EX_NOALLOC) ? (char *)NULL : substring (string, *sindex, i);
  *sindex = i;
  return (temp);
}

/* Extract the contents of STRING as if it is enclosed in double quotes.
   SINDEX, when passed in, is the offset of the character immediately
   following the opening double quote; on exit, SINDEX is left pointing after
   the closing double quote.  If STRIPDQ is non-zero, unquoted double
   quotes are stripped and the string is terminated by a null byte.
   Backslashes between the embedded double quotes are processed.  If STRIPDQ
   is zero, an unquoted `"' terminates the string. */
static char *
string_extract_double_quoted (string, sindex, stripdq)
     char *string;
     int *sindex, stripdq;
{
  size_t slen;
  char *send;
  int j, i, t;
  unsigned char c;
  char *temp, *ret;     /* The new string we return. */
  int pass_next, backquote, si; /* State variables for the machine. */
  int dquote;
  DECLARE_MBSTATE;

  slen = strlen (string + *sindex) + *sindex;
  send = string + slen;

  pass_next = backquote = dquote = 0;
  temp = (char *)xmalloc (1 + slen - *sindex);

  j = 0;
  i = *sindex;
  while (c = string[i])
    {
      /* Process a character that was quoted by a backslash. */
      if (pass_next)
    {
      /* Posix.2 sez:

         ``The backslash shall retain its special meaning as an escape
         character only when followed by one of the characters:
        $   `   "   \   <newline>''.

         If STRIPDQ is zero, we handle the double quotes here and let
         expand_word_internal handle the rest.  If STRIPDQ is non-zero,
         we have already been through one round of backslash stripping,
         and want to strip these backslashes only if DQUOTE is non-zero,
         indicating that we are inside an embedded double-quoted string. */

         /* If we are in an embedded quoted string, then don't strip
        backslashes before characters for which the backslash
        retains its special meaning, but remove backslashes in
        front of other characters.  If we are not in an
        embedded quoted string, don't strip backslashes at all.
        This mess is necessary because the string was already
        surrounded by double quotes (and sh has some really weird
        quoting rules).
        The returned string will be run through expansion as if
        it were double-quoted. */
      if ((stripdq == 0 && c != '"') ||
          (stripdq && ((dquote && (sh_syntaxtab[c] & CBSDQUOTE)) || dquote == 0)))
        temp[j++] = '\\';
      pass_next = 0;

add_one_character:
      COPY_CHAR_I (temp, j, string, send, i);
      continue;
    }

      /* A backslash protects the next character.  The code just above
     handles preserving the backslash in front of any character but
     a double quote. */
      if (c == '\\')
    {
      pass_next++;
      i++;
      continue;
    }

      /* Inside backquotes, ``the portion of the quoted string from the
     initial backquote and the characters up to the next backquote
     that is not preceded by a backslash, having escape characters
     removed, defines that command''. */
      if (backquote)
    {
      if (c == '`')
        backquote = 0;
      temp[j++] = c;
      i++;
      continue;
    }

      if (c == '`')
    {
      temp[j++] = c;
      backquote++;
      i++;
      continue;
    }

      /* Pass everything between `$(' and the matching `)' or a quoted
     ${ ... } pair through according to the Posix.2 specification. */
      if (c == '$' && ((string[i + 1] == LPAREN) || (string[i + 1] == LBRACE)))
    {
      si = i + 2;
      if (string[i + 1] == LPAREN)
        ret = extract_delimited_string (string, &si, "$(", "(", ")", 0); /*)*/
      else
        ret = extract_dollar_brace_string (string, &si, 1, 0);

      temp[j++] = '$';
      temp[j++] = string[i + 1];

      for (t = 0; ret[t]; t++, j++)
        temp[j] = ret[t];
      temp[j++] = string[si];

      i = si + 1;
      free (ret);
      continue;
    }

      /* Add any character but a double quote to the quoted string we're
     accumulating. */
      if (c != '"')
    goto add_one_character;

      /* c == '"' */
      if (stripdq)
    {
      dquote ^= 1;
      i++;
      continue;
    }

      break;
    }
  temp[j] = '\0';

  /* Point to after the closing quote. */
  if (c)
    i++;
  *sindex = i;

  return (temp);
}

/* This should really be another option to string_extract_double_quoted. */
static int
skip_double_quoted (string, slen, sind)
     char *string;
     size_t slen;
     int sind;
{
  int c, i;
  char *ret;
  int pass_next, backquote, si;
  DECLARE_MBSTATE;

  pass_next = backquote = 0;
  i = sind;
  while (c = string[i])
    {
      if (pass_next)
    {
      pass_next = 0;
      ADVANCE_CHAR (string, slen, i);
      continue;
    }
      else if (c == '\\')
    {
      pass_next++;
      i++;
      continue;
    }
      else if (backquote)
    {
      if (c == '`')
        backquote = 0;
      ADVANCE_CHAR (string, slen, i);
      continue;
    }
      else if (c == '`')
    {
      backquote++;
      i++;
      continue;
    }
      else if (c == '$' && ((string[i + 1] == LPAREN) || (string[i + 1] == LBRACE)))
    {
      si = i + 2;
      if (string[i + 1] == LPAREN)
        ret = extract_delimited_string (string, &si, "$(", "(", ")", EX_NOALLOC);
      else
        ret = extract_dollar_brace_string (string, &si, 0, EX_NOALLOC);

      i = si + 1;
      continue;
    }
      else if (c != '"')
    {
      ADVANCE_CHAR (string, slen, i);
      continue;
    }
      else
    break;
    }

  if (c)
    i++;

  return (i);
}

/* Extract the contents of STRING as if it is enclosed in single quotes.
   SINDEX, when passed in, is the offset of the character immediately
   following the opening single quote; on exit, SINDEX is left pointing after
   the closing single quote. */
static inline char *
string_extract_single_quoted (string, sindex)
     char *string;
     int *sindex;
{
  register int i;
  size_t slen;
  char *t;
  DECLARE_MBSTATE;

  slen = strlen (string + *sindex) + *sindex;
  i = *sindex;
  while (string[i] && string[i] != '\'')
    ADVANCE_CHAR (string, slen, i);

  t = substring (string, *sindex, i);

  if (string[i])
    i++;
  *sindex = i;

  return (t);
}

static inline int
skip_single_quoted (string, slen, sind)
     char *string;
     size_t slen;
     int sind;
{
  register int c;
  DECLARE_MBSTATE;

  c = sind;
  while (string[c] && string[c] != '\'')
    ADVANCE_CHAR (string, slen, c);

  if (string[c])
    c++;
  return c;
}

/* Just like string_extract, but doesn't hack backslashes or any of
   that other stuff.  Obeys CTLESC quoting.  Used to do splitting on $IFS. */
static char *
string_extract_verbatim (string, sindex, charlist)
     char *string;
     int *sindex;
     char *charlist;
{
  register int i = *sindex;
  int c;
  char *temp;

  if (charlist[0] == '\'' && charlist[1] == '\0')
    {
      temp = string_extract_single_quoted (string, sindex);
      --*sindex;    /* leave *sindex at separator character */
      return temp;
    }

  for (i = *sindex; c = string[i]; i++)
    {
      if (c == CTLESC)
    {
      i++;
      continue;
    }

      if (MEMBER (c, charlist))
    break;
    }

  temp = substring (string, *sindex, i);
  *sindex = i;

  return (temp);
}

/* Extract the $( construct in STRING, and return a new string.
   Start extracting at (SINDEX) as if we had just seen "$(".
   Make (SINDEX) get the position of the matching ")". */
char *
extract_command_subst (string, sindex)
     char *string;
     int *sindex;
{
  return (extract_delimited_string (string, sindex, "$(", "(", ")", 0));
}

/* Extract the $[ construct in STRING, and return a new string. (])
   Start extracting at (SINDEX) as if we had just seen "$[".
   Make (SINDEX) get the position of the matching "]". */
char *
extract_arithmetic_subst (string, sindex)
     char *string;
     int *sindex;
{
  return (extract_delimited_string (string, sindex, "$[", "[", "]", 0)); /*]*/
}

#if defined (PROCESS_SUBSTITUTION)
/* Extract the <( or >( construct in STRING, and return a new string.
   Start extracting at (SINDEX) as if we had just seen "<(".
   Make (SINDEX) get the position of the matching ")". */ /*))*/
char *
extract_process_subst (string, starter, sindex)
     char *string;
     char *starter;
     int *sindex;
{
  return (extract_delimited_string (string, sindex, starter, "(", ")", 0));
}
#endif /* PROCESS_SUBSTITUTION */

#if defined (ARRAY_VARS)
char *
extract_array_assignment_list (string, sindex)
     char *string;
     int *sindex;
{
  return (extract_delimited_string (string, sindex, "(", (char *)NULL, ")", 0));
}
#endif

/* Extract and create a new string from the contents of STRING, a
   character string delimited with OPENER and CLOSER.  SINDEX is
   the address of an int describing the current offset in STRING;
   it should point to just after the first OPENER found.  On exit,
   SINDEX gets the position of the last character of the matching CLOSER.
   If OPENER is more than a single character, ALT_OPENER, if non-null,
   contains a character string that can also match CLOSER and thus
   needs to be skipped. */
static char *
extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
     char *string;
     int *sindex;
     char *opener, *alt_opener, *closer;
     int flags;
{
  int i, c, si;
  size_t slen;
  char *t, *result;
  int pass_character, nesting_level;
  int len_closer, len_opener, len_alt_opener;
  DECLARE_MBSTATE;

  slen = strlen (string + *sindex) + *sindex;
  len_opener = STRLEN (opener);
  len_alt_opener = STRLEN (alt_opener);
  len_closer = STRLEN (closer);

  pass_character = 0;

  nesting_level = 1;
  i = *sindex;

  while (nesting_level)
    {
      c = string[i];

      if (c == 0)
    break;

      if (pass_character)   /* previous char was backslash */
    {
      pass_character = 0;
      ADVANCE_CHAR (string, slen, i);
      continue;
    }

      if (c == CTLESC || c == '\\')
    {
      pass_character++;
      i++;
      continue;
    }

      /* Process a nested OPENER. */
      if (STREQN (string + i, opener, len_opener))
    {
      si = i + len_opener;
      t = extract_delimited_string (string, &si, opener, alt_opener, closer, flags|EX_NOALLOC);
      i = si + 1;
      continue;
    }

      /* Process a nested ALT_OPENER */
      if (len_alt_opener && STREQN (string + i, alt_opener, len_alt_opener))
    {
      si = i + len_alt_opener;
      t = extract_delimited_string (string, &si, alt_opener, alt_opener, closer, flags|EX_NOALLOC);
      i = si + 1;
      continue;
    }

      /* If the current substring terminates the delimited string, decrement
     the nesting level. */
      if (STREQN (string + i, closer, len_closer))
    {
      i += len_closer - 1;  /* move to last byte of the closer */
      nesting_level--;
      if (nesting_level == 0)
        break;
    }

      /* Pass old-style command substitution through verbatim. */
      if (c == '`')
    {
      si = i + 1;
      t = string_extract (string, &si, "`", flags|EX_NOALLOC);
      i = si + 1;
      continue;
    }

      /* Pass single-quoted and double-quoted strings through verbatim. */
      if (c == '\'' || c == '"')
    {
      si = i + 1;
      i = (c == '\'') ? skip_single_quoted (string, slen, si)
              : skip_double_quoted (string, slen, si);
      continue;
    }

      /* move past this character, which was not special. */
      ADVANCE_CHAR (string, slen, i);
    }

  if (c == 0 && nesting_level && no_longjmp_on_fatal_error == 0)
    {
      report_error ("bad substitution: no `%s' in %s", closer, string);
      last_command_exit_value = EXECUTION_FAILURE;
      jump_to_top_level (DISCARD);
    }

  si = i - *sindex - len_closer + 1;
  if (flags & EX_NOALLOC)
    result = (char *)NULL;
  else    
    {
      result = (char *)xmalloc (1 + si);
      strncpy (result, string + *sindex, si);
      result[si] = '\0';
    }
  *sindex = i;

  return (result);
}

/* Extract a parameter expansion expression within ${ and } from STRING.
   Obey the Posix.2 rules for finding the ending `}': count braces while
   skipping over enclosed quoted strings and command substitutions.
   SINDEX is the address of an int describing the current offset in STRING;
   it should point to just after the first `{' found.  On exit, SINDEX
   gets the position of the matching `}'.  QUOTED is non-zero if this
   occurs inside double quotes. */
/* XXX -- this is very similar to extract_delimited_string -- XXX */
static char *
extract_dollar_brace_string (string, sindex, quoted, flags)
     char *string;
     int *sindex, quoted, flags;
{
  register int i, c;
  size_t slen;
  int pass_character, nesting_level, si;
  char *result, *t;
  DECLARE_MBSTATE;

  pass_character = 0;
  nesting_level = 1;
  slen = strlen (string + *sindex) + *sindex;

  i = *sindex;
  while (c = string[i])
    {
      if (pass_character)
    {
      pass_character = 0;
      ADVANCE_CHAR (string, slen, i);
      continue;
    }

      /* CTLESCs and backslashes quote the next character. */
      if (c == CTLESC || c == '\\')
    {
      pass_character++;
      i++;
      continue;
    }

      if (string[i] == '$' && string[i+1] == LBRACE)
    {
      nesting_level++;
      i += 2;
      continue;
    }

      if (c == RBRACE)
    {
      nesting_level--;
      if (nesting_level == 0)
        break;
      i++;
      continue;
    }

      /* Pass the contents of old-style command substitutions through
     verbatim. */
      if (c == '`')
    {
      si = i + 1;
      t = string_extract (string, &si, "`", flags|EX_NOALLOC);
      i = si + 1;
      continue;
    }

      /* Pass the contents of new-style command substitutions and
     arithmetic substitutions through verbatim. */
      if (string[i] == '$' && string[i+1] == LPAREN)
    {
      si = i + 2;
      t = extract_delimited_string (string, &si, "$(", "(", ")", flags|EX_NOALLOC); /*)*/
      i = si + 1;
      continue;
    }

      /* Pass the contents of single-quoted and double-quoted strings
     through verbatim. */
      if (c == '\'' || c == '"')
    {
      si = i + 1;
      i = (c == '\'') ? skip_single_quoted (string, slen, si)
              : skip_double_quoted (string, slen, si);
      /* skip_XXX_quoted leaves index one past close quote */
      continue;
    }

      /* move past this character, which was not special. */
      ADVANCE_CHAR (string, slen, i);
    }

  if (c == 0 && nesting_level && no_longjmp_on_fatal_error == 0)
    {
      report_error ("bad substitution: no ending `}' in %s", string);
      last_command_exit_value = EXECUTION_FAILURE;
      jump_to_top_level (DISCARD);
    }

  result = (flags & EX_NOALLOC) ? (char *)NULL : substring (string, *sindex, i);
  *sindex = i;

  return (result);
}

/* Remove backslashes which are quoting backquotes from STRING.  Modifies
   STRING, and returns a pointer to it. */
char *
de_backslash (string)
     char *string;
{
  register size_t slen;
  register int i, j, prev_i;
  DECLARE_MBSTATE;

  slen = strlen (string);
  i = j = 0;

  /* Loop copying string[i] to string[j], i >= j. */
  while (i < slen)
    {
      if (string[i] == '\\' && (string[i + 1] == '`' || string[i + 1] == '\\' ||
                  string[i + 1] == '$'))
    i++;
      prev_i = i;
      ADVANCE_CHAR (string, slen, i);
      if (j < prev_i)
        do string[j++] = string[prev_i++]; while (prev_i < i);
      else
        j = i;
    }
  string[j] = '\0';

  return (string);
}

#if 0
/*UNUSED*/
/* Replace instances of \! in a string with !. */
void
unquote_bang (string)
     char *string;
{
  register int i, j;
  register char *temp;

  temp = (char *)xmalloc (1 + strlen (string));

  for (i = 0, j = 0; (temp[j] = string[i]); i++, j++)
    {
      if (string[i] == '\\' && string[i + 1] == '!')
    {
      temp[j] = '!';
      i++;
    }
    }
  strcpy (string, temp);
  free (temp);
}
#endif

#if defined (READLINE)
/* Return 1 if the portion of STRING ending at EINDEX is quoted (there is
   an unclosed quoted string), or if the character at EINDEX is quoted
   by a backslash. NO_LONGJMP_ON_FATAL_ERROR is used to flag that the various
   single and double-quoted string parsing functions should not return an
   error if there are unclosed quotes or braces.  The characters that this
   recognizes need to be the same as the contents of
   rl_completer_quote_characters. */

#define CQ_RETURN(x) do { no_longjmp_on_fatal_error = 0; return (x); } while (0)

int
char_is_quoted (string, eindex)
     char *string;
     int eindex;
{
  int i, pass_next, c;
  size_t slen;
  DECLARE_MBSTATE;

  slen = strlen (string);
  no_longjmp_on_fatal_error = 1;
  i = pass_next = 0;
  while (i <= eindex)
    {
      c = string[i];

      if (pass_next)
    {
      pass_next = 0;
      if (i >= eindex)  /* XXX was if (i >= eindex - 1) */
        CQ_RETURN(1);
      ADVANCE_CHAR (string, slen, i);
      continue;
    }
      else if (c == '\\')
    {
      pass_next = 1;
      i++;
      continue;
    }
      else if (c == '\'' || c == '"')
    {
      i = (c == '\'') ? skip_single_quoted (string, slen, ++i)
              : skip_double_quoted (string, slen, ++i);
      if (i > eindex)
        CQ_RETURN(1);
      /* no increment, the skip_xxx functions go one past end */
    }
      else
    ADVANCE_CHAR (string, slen, i);
    }

  CQ_RETURN(0);
}

int
unclosed_pair (string, eindex, openstr)
     char *string;
     int eindex;
     char *openstr;
{
  int i, pass_next, openc, olen;
  size_t slen;
  DECLARE_MBSTATE;

  slen = strlen (string);
  olen = strlen (openstr);
  i = pass_next = openc = 0;
  while (i <= eindex)
    {
      if (pass_next)
    {
      pass_next = 0;
      if (i >= eindex)  /* XXX was if (i >= eindex - 1) */
        return 0;
      ADVANCE_CHAR (string, slen, i);
      continue;
    }
      else if (string[i] == '\\')
    {
      pass_next = 1;
      i++;
      continue;
    }
      else if (STREQN (string + i, openstr, olen))
    {
      openc = 1 - openc;
      i += olen;
    }
      else if (string[i] == '\'' || string[i] == '"')
    {
      i = (string[i] == '\'') ? skip_single_quoted (string, slen, i)
                  : skip_double_quoted (string, slen, i);
      if (i > eindex)
        return 0;
    }
      else
    ADVANCE_CHAR (string, slen, i);
    }
  return (openc);
}

/* Skip characters in STRING until we find a character in DELIMS, and return
   the index of that character.  START is the index into string at which we
   begin.  This is similar in spirit to strpbrk, but it returns an index into
   STRING and takes a starting index.  This little piece of code knows quite
   a lot of shell syntax.  It's very similar to skip_double_quoted and other
   functions of that ilk. */
int
skip_to_delim (string, start, delims)
     char *string;
     int start;
     char *delims;
{
  int i, pass_next, backq, si, c;
  size_t slen;
  char *temp;
  DECLARE_MBSTATE;

  slen = strlen (string + start) + start;
  no_longjmp_on_fatal_error = 1;
  i = start;
  pass_next = backq = 0;
  while (c = string[i])
    {
      if (pass_next)
    {
      pass_next = 0;
      if (c == 0)
        CQ_RETURN(i);
      ADVANCE_CHAR (string, slen, i);
      continue;
    }
      else if (c == '\\')
    {
      pass_next = 1;
      i++;
      continue;
    }
      else if (backq)
    {
      if (c == '`')
        backq = 0;
      ADVANCE_CHAR (string, slen, i);
      continue;
    }
      else if (c == '`')
    {
      backq = 1;
      i++;
      continue;
    }
      else if (c == '\'' || c == '"')
    {
      i = (c == '\'') ? skip_single_quoted (string, slen, ++i)
              : skip_double_quoted (string, slen, ++i);
      /* no increment, the skip functions increment past the closing quote. */
    }
      else if (c == '$' && (string[i+1] == LPAREN || string[i+1] == LBRACE))
    {
      si = i + 2;
      if (string[si] == '\0')
        CQ_RETURN(si);

      if (string[i+1] == LPAREN)
        temp = extract_delimited_string (string, &si, "$(", "(", ")", EX_NOALLOC); /* ) */
      else
        temp = extract_dollar_brace_string (string, &si, 0, EX_NOALLOC);
      i = si;
      if (string[i] == '\0')    /* don't increment i past EOS in loop */
        break;
      i++;
      continue;
    }
      else if (member (c, delims))
    break;
      else
    ADVANCE_CHAR (string, slen, i);
    }

  CQ_RETURN(i);
}

/* Split STRING (length SLEN) at DELIMS, and return a WORD_LIST with the
   individual words.  If DELIMS is NULL, the current value of $IFS is used
   to split the string.  SENTINEL is an index to look for.  NWP, if non-NULL
   gets the number of words in the returned list.  CWP, if non-NULL, gets
   the index of the word containing SENTINEL.  Non-whitespace chars in
   DELIMS delimit separate fields. */
WORD_LIST *
split_at_delims (string, slen, delims, sentinel, nwp, cwp)
     char *string;
     int slen;
     char *delims;
     int sentinel;
     int *nwp, *cwp;
{
  int ts, te, i, nw, cw;
  char *token, *d, *d2;
  WORD_LIST *ret, *tl;

  if (string == 0 || *string == '\0')
    {
      if (nwp)
    *nwp = 0;
      if (cwp)
    *cwp = 0;   
      return ((WORD_LIST *)NULL);
    }

  d = (delims == 0) ? ifs_value : delims;

  /* Make d2 the non-whitespace characters in delims */
  d2 = 0;
  if (delims)
    {
      d2 = (char *)xmalloc (strlen (delims) + 1);
      for (i = ts = 0; delims[i]; i++)
    {
      if (whitespace(delims[i]) == 0)
        d2[ts++] = delims[i];
    }
      d2[ts] = '\0';
    }

  ret = (WORD_LIST *)NULL;

  for (i = 0; member (string[i], d) && spctabnl(string[i]); i++)
    ;
  if (string[i] == '\0')
    return (ret);

  ts = i;
  nw = 0;
  cw = -1;
  while (1)
    {
      te = skip_to_delim (string, ts, d);

      /* If we have a non-whitespace delimiter character, use it to make a
     separate field.  This is just about what $IFS splitting does and
     is closer to the behavior of the shell parser. */
      if (ts == te && d2 && member (string[ts], d2))
    {
      te = ts + 1;
      while (member (string[te], d2))
        te++;
    }

      token = substring (string, ts, te);

      ret = add_string_to_list (token, ret);
      free (token);
      nw++;

      if (sentinel >= ts && sentinel <= te)
    cw = nw;

      /* If the cursor is at whitespace just before word start, set the
     sentinel word to the current word. */
      if (cwp && cw == -1 && sentinel == ts-1)
    cw = nw;

      /* If the cursor is at whitespace between two words, make a new, empty
     word, add it before (well, after, since the list is in reverse order)
     the word we just added, and set the current word to that one. */
      if (cwp && cw == -1 && sentinel < ts)
    {
      tl = make_word_list (make_word (""), ret->next);
      ret->next = tl;
      cw = nw;
      nw++;
    }

      if (string[te] == 0)
    break;

      i = te /* + member (string[te], d) */;
      while (member (string[i], d) && spctabnl(string[i]))
    i++;

      if (string[i])
    ts = i;
      else
    break;
    }

  /* Special case for SENTINEL at the end of STRING.  If we haven't found
     the word containing SENTINEL yet, and the index we're looking for is at
     the end of STRING, add an additional null argument and set the current
     word pointer to that. */
  if (cwp && cw == -1 && sentinel >= slen)
    {
      if (whitespace (string[sentinel - 1]))
    {
      token = "";
      ret = add_string_to_list (token, ret);
      nw++;
    }
      cw = nw;
    }

  if (nwp)
    *nwp = nw;
  if (cwp)
    *cwp = cw;

  return (REVERSE_LIST (ret, WORD_LIST *));
}
#endif /* READLINE */

#if 0
/* UNUSED */
/* Extract the name of the variable to bind to from the assignment string. */
char *
assignment_name (string)
     char *string;
{
  int offset;
  char *temp;

  offset = assignment (string);
  if (offset == 0)
    return (char *)NULL;
  temp = substring (string, 0, offset);
  return (temp);
}
#endif

/* **************************************************************** */
/*                                  */
/*     Functions to convert strings to WORD_LISTs and vice versa    */
/*                                  */
/* **************************************************************** */

/* Return a single string of all the words in LIST.  SEP is the separator
   to put between individual elements of LIST in the output string. */
char *
string_list_internal (list, sep)
     WORD_LIST *list;
     char *sep;
{
  register WORD_LIST *t;
  char *result, *r;
  int word_len, sep_len, result_size;

  if (list == 0)
    return ((char *)NULL);

  /* This is nearly always called with either sep[0] == 0 or sep[1] == 0. */
  sep_len = STRLEN (sep);
  result_size = 0;

  for (t = list; t; t = t->next)
    {
      if (t != list)
    result_size += sep_len;
      result_size += strlen (t->word->word);
    }

  r = result = (char *)xmalloc (result_size + 1);

  for (t = list; t; t = t->next)
    {
      if (t != list && sep_len)
    {
      if (sep_len > 1)
        {
          FASTCOPY (sep, r, sep_len);
          r += sep_len;
        }
      else
        *r++ = sep[0];
    }

      word_len = strlen (t->word->word);
      FASTCOPY (t->word->word, r, word_len);
      r += word_len;
    }

  *r = '\0';
  return (result);
}

/* Return a single string of all the words present in LIST, separating
   each word with a space. */
char *
string_list (list)
     WORD_LIST *list;
{
  return (string_list_internal (list, " "));
}

/* Return a single string of all the words present in LIST, obeying the
   quoting rules for "$*", to wit: (P1003.2, draft 11, 3.5.2) "If the
   expansion [of $*] appears within a double quoted string, it expands
   to a single field with the value of each parameter separated by the
   first character of the IFS variable, or by a <space> if IFS is unset." */
char *
string_list_dollar_star (list)
     WORD_LIST *list;
{
  char sep[2];

  sep[0] = ifs_firstc;
  sep[1] = '\0';

  return (string_list_internal (list, sep));
}

/* Turn $@ into a string.  If (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
   is non-zero, the $@ appears within double quotes, and we should quote
   the list before converting it into a string.  If IFS is unset, and the
   word is not quoted, we just need to quote CTLESC and CTLNUL characters
   in the words in the list, because the default value of $IFS is
   <space><tab><newline>, IFS characters in the words in the list should
   also be split.  If IFS is null, and the word is not quoted, we need
   to quote the words in the list to preserve the positional parameters
   exactly. */
char *
string_list_dollar_at (list, quoted)
     WORD_LIST *list;
     int quoted;
{
  char *ifs, sep[2];
  WORD_LIST *tlist;

  /* XXX this could just be ifs = ifs_value; */
  ifs = ifs_var ? value_cell (ifs_var) : (char *)0;

  sep[0] = (ifs == 0 || *ifs == 0) ? ' ' : *ifs;
  sep[1] = '\0';

  tlist = ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (ifs && *ifs == 0))
        ? quote_list (list)
        : list_quote_escapes (list);
  return (string_list_internal (tlist, sep));
}

/* Return the list of words present in STRING.  Separate the string into
   words at any of the characters found in SEPARATORS.  If QUOTED is
   non-zero then word in the list will have its quoted flag set, otherwise
   the quoted flag is left as make_word () deemed fit.

   This obeys the P1003.2 word splitting semantics.  If `separators' is
   exactly <space><tab><newline>, then the splitting algorithm is that of
   the Bourne shell, which treats any sequence of characters from `separators'
   as a delimiter.  If IFS is unset, which results in `separators' being set
   to "", no splitting occurs.  If separators has some other value, the
   following rules are applied (`IFS white space' means zero or more
   occurrences of <space>, <tab>, or <newline>, as long as those characters
   are in `separators'):

    1) IFS white space is ignored at the start and the end of the
       string.
    2) Each occurrence of a character in `separators' that is not
       IFS white space, along with any adjacent occurrences of
       IFS white space delimits a field.
    3) Any nonzero-length sequence of IFS white space delimits a field.
   */

/* BEWARE!  list_string strips null arguments.  Don't call it twice and
   expect to have "" preserved! */

/* This performs word splitting and quoted null character removal on
   STRING. */
#if 0
#define issep(c)    ((separators)[1] ? (member ((c), separators)) : (c) == (separators)[0])
#else
#define issep(c)    ((separators)[1] ? isifs(c) : (c) == (separators)[0])
#endif

WORD_LIST *
list_string (string, separators, quoted)
     register char *string, *separators;
     int quoted;
{
  WORD_LIST *result;
  WORD_DESC *t;
  char *current_word, *s;
  int sindex, sh_style_split, whitesep;

  if (!string || !*string)
    return ((WORD_LIST *)NULL);

  sh_style_split = separators && separators[0] == ' ' &&
                 separators[1] == '\t' &&
                 separators[2] == '\n' &&
                 separators[3] == '\0';

  /* Remove sequences of whitespace at the beginning of STRING, as
     long as those characters appear in IFS.  Do not do this if
     STRING is quoted or if there are no separator characters. */
  if (!quoted || !separators || !*separators)
    {
      for (s = string; *s && spctabnl (*s) && issep (*s); s++);

      if (!*s)
    return ((WORD_LIST *)NULL);

      string = s;
    }

  /* OK, now STRING points to a word that does not begin with white space.
     The splitting algorithm is:
    extract a word, stopping at a separator
    skip sequences of spc, tab, or nl as long as they are separators
     This obeys the field splitting rules in Posix.2. */
  for (result = (WORD_LIST *)NULL, sindex = 0; string[sindex]; )
    {
      current_word = string_extract_verbatim (string, &sindex, separators);
      if (current_word == 0)
    break;

      /* If we have a quoted empty string, add a quoted null argument.  We
     want to preserve the quoted null character iff this is a quoted
     empty string; otherwise the quoted null characters are removed
     below. */
      if (QUOTED_NULL (current_word))
    {
      t = make_bare_word ("");
      t->flags |= W_QUOTED;
      free (t->word);
      t->word = make_quoted_char ('\0');
      result = make_word_list (t, result);
    }
      else if (current_word[0] != '\0')
    {
      /* If we have something, then add it regardless.  However,
         perform quoted null character removal on the current word. */
      remove_quoted_nulls (current_word);
      result = add_string_to_list (current_word, result);
      if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
        result->word->flags |= W_QUOTED;
    }

      /* If we're not doing sequences of separators in the traditional
     Bourne shell style, then add a quoted null argument. */
      else if (!sh_style_split && !spctabnl (string[sindex]))
    {
      t = make_bare_word ("");
      t->flags |= W_QUOTED;
      free (t->word);
      t->word = make_quoted_char ('\0');
      result = make_word_list (t, result);
    }

      free (current_word);

      /* Note whether or not the separator is IFS whitespace, used later. */
      whitesep = string[sindex] && spctabnl (string[sindex]);

      /* Move past the current separator character. */
      if (string[sindex])
    sindex++;

      /* Now skip sequences of space, tab, or newline characters if they are
     in the list of separators. */
      while (string[sindex] && spctabnl (string[sindex]) && issep (string[sindex]))
    sindex++;

      /* If the first separator was IFS whitespace and the current character
     is a non-whitespace IFS character, it should be part of the current
     field delimiter, not a separate delimiter that would result in an
     empty field.  Look at POSIX.2, 3.6.5, (3)(b). */
      if (string[sindex] && whitesep && issep (string[sindex]) && !spctabnl (string[sindex]))
    sindex++;
    }
  return (REVERSE_LIST (result, WORD_LIST *));
}

/* Parse a single word from STRING, using SEPARATORS to separate fields.
   ENDPTR is set to the first character after the word.  This is used by
   the `read' builtin.  This is never called with SEPARATORS != $IFS;
   it should be simplified.

   XXX - this function is very similar to list_string; they should be
     combined - XXX */
char *
get_word_from_string (stringp, separators, endptr)
     char **stringp, *separators, **endptr;
{
  register char *s;
  char *current_word;
  int sindex, sh_style_split, whitesep;

  if (!stringp || !*stringp || !**stringp)
    return ((char *)NULL);

  s = *stringp;

  sh_style_split = separators && separators[0] == ' ' &&
                 separators[1] == '\t' &&
                 separators[2] == '\n' &&
                 separators[3] == '\0';

  /* Remove sequences of whitespace at the beginning of STRING, as
     long as those characters appear in IFS. */
  if (sh_style_split || !separators || !*separators)
    {
      for (; *s && spctabnl (*s) && isifs (*s); s++);

      /* If the string is nothing but whitespace, update it and return. */
      if (!*s)
    {
      *stringp = s;
      if (endptr)
        *endptr = s;
      return ((char *)NULL);
    }
    }

  /* OK, S points to a word that does not begin with white space.
     Now extract a word, stopping at a separator, save a pointer to
     the first character after the word, then skip sequences of spc,
     tab, or nl as long as they are separators.

     This obeys the field splitting rules in Posix.2. */
  sindex = 0;
  current_word = string_extract_verbatim (s, &sindex, separators);

  /* Set ENDPTR to the first character after the end of the word. */
  if (endptr)
    *endptr = s + sindex;

  /* Note whether or not the separator is IFS whitespace, used later. */
  whitesep = s[sindex] && spctabnl (s[sindex]);

  /* Move past the current separator character. */
  if (s[sindex])
    sindex++;

  /* Now skip sequences of space, tab, or newline characters if they are
     in the list of separators. */
  while (s[sindex] && spctabnl (s[sindex]) && isifs (s[sindex]))
    sindex++;

  /* If the first separator was IFS whitespace and the current character is
     a non-whitespace IFS character, it should be part of the current field
     delimiter, not a separate delimiter that would result in an empty field.
     Look at POSIX.2, 3.6.5, (3)(b). */
  if (s[sindex] && whitesep && isifs (s[sindex]) && !spctabnl (s[sindex]))
    sindex++;

  /* Update STRING to point to the next field. */
  *stringp = s + sindex;
  return (current_word);
}

/* Remove IFS white space at the end of STRING.  Start at the end
   of the string and walk backwards until the beginning of the string
   or we find a character that's not IFS white space and not CTLESC.
   Only let CTLESC escape a white space character if SAW_ESCAPE is
   non-zero.  */
char *
strip_trailing_ifs_whitespace (string, separators, saw_escape)
     char *string, *separators;
     int saw_escape;
{
  char *s;

  s = string + STRLEN (string) - 1;
  while (s > string && ((spctabnl (*s) && isifs (*s)) ||
            (saw_escape && *s == CTLESC && spctabnl (s[1]))))
    s--;
  *++s = '\0';
  return string;
}

#if 0
/* UNUSED */
/* Split STRING into words at whitespace.  Obeys shell-style quoting with
   backslashes, single and double quotes. */
WORD_LIST *
list_string_with_quotes (string)
     char *string;
{
  WORD_LIST *list;
  char *token, *s;
  size_t s_len;
  int c, i, tokstart, len;

  for (s = string; s && *s && spctabnl (*s); s++)
    ;
  if (s == 0 || *s == 0)
    return ((WORD_LIST *)NULL);

  s_len = strlen (s);
  tokstart = i = 0;
  list = (WORD_LIST *)NULL;
  while (1)
    {
      c = s[i];
      if (c == '\\')
    {
      i++;
      if (s[i])
        i++;
    }
      else if (c == '\'')
    i = skip_single_quoted (s, s_len, ++i);
      else if (c == '"')
    i = skip_double_quoted (s, s_len, ++i);
      else if (c == 0 || spctabnl (c))
    {
      /* We have found the end of a token.  Make a word out of it and
         add it to the word list. */
      token = substring (s, tokstart, i);
      list = add_string_to_list (token, list);
      free (token);
      while (spctabnl (s[i]))
        i++;
      if (s[i])
        tokstart = i;
      else
        break;
    }
      else
    i++;    /* normal character */
    }
  return (REVERSE_LIST (list, WORD_LIST *));
}
#endif

/********************************************************/
/*                          */
/*  Functions to perform assignment statements  */
/*                          */
/********************************************************/

/* Given STRING, an assignment string, get the value of the right side
   of the `=', and bind it to the left side.  If EXPAND is true, then
   perform parameter expansion, command substitution, and arithmetic
   expansion on the right-hand side.  Perform tilde expansion in any
   case.  Do not perform word splitting on the result of expansion. */
static int
do_assignment_internal (string, expand)
     const char *string;
     int expand;
{
  int offset;
  char *name, *value;
  SHELL_VAR *entry;
#if defined (ARRAY_VARS)
  char *t;
  int ni, assign_list = 0;
#endif

  offset = assignment (string);
  name = savestring (string);
  value = (char *)NULL;

  if (name[offset] == '=')
    {
      char *temp;

      name[offset] = 0;
      temp = name + offset + 1;

#if defined (ARRAY_VARS)
      if (expand && temp[0] == LPAREN && xstrchr (temp, RPAREN))
    {
      assign_list = ni = 1;
      value = extract_delimited_string (temp, &ni, "(", (char *)NULL, ")", 0);
    }
      else
#endif

      /* Perform tilde expansion. */
      if (expand && temp[0])
    {
      temp = (xstrchr (temp, '~') && unquoted_member ('~', temp))
            ? bash_tilde_expand (temp, 1)
            : savestring (temp);

      value = expand_string_if_necessary (temp, 0, expand_string_unsplit);
      free (temp);
    }
      else
    value = savestring (temp);
    }

  if (value == 0)
    {
      value = (char *)xmalloc (1);
      value[0] = '\0';
    }

  if (echo_command_at_execute)
    {
#if defined (ARRAY_VARS)
      if (assign_list)
    fprintf (stderr, "%s%s=(%s)\n", indirection_level_string (), name, value);
      else
#endif
      fprintf (stderr, "%s%s=%s\n", indirection_level_string (), name, value);
    }

#define ASSIGN_RETURN(r)    do { FREE (value); free (name); return (r); } while (0)

#if defined (ARRAY_VARS)
  if (t = xstrchr (name, '['))  /*]*/
    {
      if (assign_list)
    {
      report_error ("%s: cannot assign list to array member", name);
      ASSIGN_RETURN (0);
    }
      entry = assign_array_element (name, value);
      if (entry == 0)
    ASSIGN_RETURN (0);
    }
  else if (assign_list)
    entry = assign_array_from_string (name, value);
  else
#endif /* ARRAY_VARS */
  entry = bind_variable (name, value);

  stupidly_hack_special_variables (name);

  if (entry)
    VUNSETATTR (entry, att_invisible);

  /* Return 1 if the assignment seems to have been performed correctly. */
  ASSIGN_RETURN (entry ? ((readonly_p (entry) == 0) && noassign_p (entry) == 0) : 0);
}

/* Perform the assignment statement in STRING, and expand the
   right side by doing command and parameter expansion. */
int
do_assignment (string)
     const char *string;
{
  return do_assignment_internal (string, 1);
}

/* Given STRING, an assignment string, get the value of the right side
   of the `=', and bind it to the left side.  Do not do command and
   parameter substitution on the right hand side. */
int
do_assignment_no_expand (string)
     const char *string;
{
  return do_assignment_internal (string, 0);
}

/***************************************************
 *                         *
 *  Functions to manage the positional parameters  *
 *                         *
 ***************************************************/

/* Return the word list that corresponds to `$*'. */
WORD_LIST *
list_rest_of_args ()
{
  register WORD_LIST *list, *args;
  int i;

  /* Break out of the loop as soon as one of the dollar variables is null. */
  for (i = 1, list = (WORD_LIST *)NULL; i < 10 && dollar_vars[i]; i++)
    list = make_word_list (make_bare_word (dollar_vars[i]), list);

  for (args = rest_of_args; args; args = args->next)
    list = make_word_list (make_bare_word (args->word->word), list);

  return (REVERSE_LIST (list, WORD_LIST *));
}

int
number_of_args ()
{
  register WORD_LIST *list;
  int n;

  for (n = 0; n < 9 && dollar_vars[n+1]; n++)
    ;
  for (list = rest_of_args; list; list = list->next)
    n++;
  return n;
}

/* Return the value of a positional parameter.  This handles values > 10. */
char *
get_dollar_var_value (ind)
     intmax_t ind;
{
  char *temp;
  WORD_LIST *p;

  if (ind < 10)
    temp = dollar_vars[ind] ? savestring (dollar_vars[ind]) : (char *)NULL;
  else  /* We want something like ${11} */
    {
      ind -= 10;
      for (p = rest_of_args; p && ind--; p = p->next)
    ;
      temp = p ? savestring (p->word->word) : (char *)NULL;
    }
  return (temp);
}

/* Make a single large string out of the dollar digit variables,
   and the rest_of_args.  If DOLLAR_STAR is 1, then obey the special
   case of "$*" with respect to IFS. */
char *
string_rest_of_args (dollar_star)
     int dollar_star;
{
  register WORD_LIST *list;
  char *string;

  list = list_rest_of_args ();
  string = dollar_star ? string_list_dollar_star (list) : string_list (list);
  dispose_words (list);
  return (string);
}

/* Return a string containing the positional parameters from START to
   END, inclusive.  If STRING[0] == '*', we obey the rules for $*,
   which only makes a difference if QUOTED is non-zero.  If QUOTED includes
   Q_HERE_DOCUMENT or Q_DOUBLE_QUOTES, this returns a quoted list, otherwise
   no quoting chars are added. */
static char *
pos_params (string, start, end, quoted)
     char *string;
     int start, end, quoted;
{
  WORD_LIST *save, *params, *h, *t;
  char *ret;
  int i;

  /* see if we can short-circuit.  if start == end, we want 0 parameters. */
  if (start == end)
    return ((char *)NULL);

  save = params = list_rest_of_args ();
  if (save == 0)
    return ((char *)NULL);

  for (i = 1; params && i < start; i++)
    params = params->next;
  if (params == 0)
    return ((char *)NULL);
  for (h = t = params; params && i < end; i++)
    {
      t = params;
      params = params->next;
    }

  t->next = (WORD_LIST *)NULL;
  if (string[0] == '*')
    ret = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? string_list_dollar_star (quote_list (h)) : string_list (h);
  else
    ret = string_list ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? quote_list (h) : h);
  if (t != params)
    t->next = params;

  dispose_words (save);
  return (ret);
}

/******************************************************************/
/*                                */
/*  Functions to expand strings to strings or WORD_LISTs      */
/*                                */
/******************************************************************/

#if defined (PROCESS_SUBSTITUTION)
#define EXP_CHAR(s) (s == '$' || s == '`' || s == '<' || s == '>' || s == CTLESC)
#else
#define EXP_CHAR(s) (s == '$' || s == '`' || s == CTLESC)
#endif

/* If there are any characters in STRING that require full expansion,
   then call FUNC to expand STRING; otherwise just perform quote
   removal if necessary.  This returns a new string. */
static char *
expand_string_if_necessary (string, quoted, func)
     char *string;
     int quoted;
     EXPFUNC *func;
{
  WORD_LIST *list;
  size_t slen;
  int i, saw_quote;
  char *ret;
  DECLARE_MBSTATE;

  slen = strlen (string);
  i = saw_quote = 0;
  while (string[i])
    {
      if (EXP_CHAR (string[i]))
    break;
      else if (string[i] == '\'' || string[i] == '\\' || string[i] == '"')
    saw_quote = 1;
      ADVANCE_CHAR (string, slen, i);
    }

  if (string[i])
    {
      list = (*func) (string, quoted);
      if (list)
    {
      ret = string_list (list);
      dispose_words (list);
    }
      else
    ret = (char *)NULL;
    }
  else if (saw_quote && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0))
    ret = string_quote_removal (string, quoted);
  else
    ret = savestring (string);

  return ret;
}

static inline char *
expand_string_to_string_internal (string, quoted, func)
     char *string;
     int quoted;
     EXPFUNC *func;
{
  WORD_LIST *list;
  char *ret;

  if (string == 0 || *string == '\0')
    return ((char *)NULL);

  list = (*func) (string, quoted);
  if (list)
    {
      ret = string_list (list);
      dispose_words (list);
    }
  else
    ret = (char *)NULL;

  return (ret);
}

char *
expand_string_to_string (string, quoted)
     char *string;
     int quoted;
{
  return (expand_string_to_string_internal (string, quoted, expand_string));
}

char *
expand_string_unsplit_to_string (string, quoted)
     char *string;
     int quoted;
{
  return (expand_string_to_string_internal (string, quoted, expand_string_unsplit));
}

#if defined (COND_COMMAND)
/* Just remove backslashes in STRING.  Returns a new string. */
char *
remove_backslashes (string)
     char *string;
{
  char *r, *ret, *s;

  r = ret = (char *)xmalloc (strlen (string) + 1);
  for (s = string; s && *s; )
    {
      if (*s == '\\')
    s++;
      if (*s == 0)
    break;
      *r++ = *s++;
    }
  *r = '\0';
  return ret;
}

/* This needs better error handling. */
/* Expand W for use as an argument to a unary or binary operator in a
   [[...]] expression.  If SPECIAL is nonzero, this is the rhs argument
   to the != or == operator, and should be treated as a pattern.  In
   this case, we quote the string specially for the globbing code.  The
   caller is responsible for removing the backslashes if the unquoted
   words is needed later. */   
char *
cond_expand_word (w, special)
     WORD_DESC *w;
     int special;
{
  char *r, *p;
  WORD_LIST *l;

  if (w->word == 0 || w->word[0] == '\0')
    return ((char *)NULL);

  if (xstrchr (w->word, '~') && unquoted_member ('~', w->word))
    {
      p = bash_tilde_expand (w->word, 0);
      free (w->word);
      w->word = p;
    }

  l = call_expand_word_internal (w, 0, 0, (int *)0, (int *)0);
  if (l)
    {
      if (special == 0)
    {
      dequote_list (l);
      r = string_list (l);
    }
      else
    {
      p = string_list (l);
      r = quote_string_for_globbing (p, QGLOB_CVTNULL);
      free (p);
    }
      dispose_words (l);
    }
  else
    r = (char *)NULL;

  return r;
}
#endif

/* Call expand_word_internal to expand W and handle error returns.
   A convenience function for functions that don't want to handle
   any errors or free any memory before aborting. */
static WORD_LIST *
call_expand_word_internal (w, q, i, c, e)
     WORD_DESC *w;
     int q, i, *c, *e;
{
  WORD_LIST *result;

  result = expand_word_internal (w, q, i, c, e);
  if (result == &expand_word_error || result == &expand_word_fatal)
    {
      expand_no_split_dollar_star = 0;  /* XXX */
      /* By convention, each time this error is returned, w->word has
     already been freed (it sometimes may not be in the fatal case,
     but that doesn't result in a memory leak because we're going
     to exit in most cases). */
      w->word = (char *)NULL;
      last_command_exit_value = EXECUTION_FAILURE;
      jump_to_top_level ((result == &expand_word_error) ? DISCARD : FORCE_EOF);
      /* NOTREACHED */
    }
  else
    return (result);
}

/* Perform parameter expansion, command substitution, and arithmetic
   expansion on STRING, as if it were a word.  Leave the result quoted. */
static WORD_LIST *
expand_string_internal (string, quoted)
     char *string;
     int quoted;
{
  WORD_DESC td;
  WORD_LIST *tresult;

  if (string == 0 || *string == 0)
    return ((WORD_LIST *)NULL);

  td.flags = 0;
  td.word = savestring (string);

  tresult = call_expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL);

  FREE (td.word);
  return (tresult);
}

/* Expand STRING by performing parameter expansion, command substitution,
   and arithmetic expansion.  Dequote the resulting WORD_LIST before
   returning it, but do not perform word splitting.  The call to
   remove_quoted_nulls () is in here because word splitting normally
   takes care of quote removal. */
WORD_LIST *
expand_string_unsplit (string, quoted)
     char *string;
     int quoted;
{
  WORD_LIST *value;

  if (string == 0 || *string == '\0')
    return ((WORD_LIST *)NULL);

  expand_no_split_dollar_star = 1;
  value = expand_string_internal (string, quoted);
  expand_no_split_dollar_star = 0;

  if (value)
    {
      if (value->word)
    remove_quoted_nulls (value->word->word);
      dequote_list (value);
    }
  return (value);
}


/* Expand one of the PS? prompt strings. This is a sort of combination of
   expand_string_unsplit and expand_string_internal, but returns the
   passed string when an error occurs.  Might want to trap other calls
   to jump_to_top_level here so we don't endlessly loop. */
WORD_LIST *
expand_prompt_string (string, quoted)
     char *string;
     int quoted;
{
  WORD_LIST *value;
  WORD_DESC td;

  if (string == 0 || *string == 0)
    return ((WORD_LIST *)NULL);

  td.flags = 0;
  td.word = savestring (string);

  no_longjmp_on_fatal_error = 1;
  value = expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL);
  no_longjmp_on_fatal_error = 0;

  if (value == &expand_word_error || value == &expand_word_fatal)
    {
      value = make_word_list (make_bare_word (string), (WORD_LIST *)NULL);
      return value;
    }
  FREE (td.word);
  if (value)
    {
      if (value->word)
    remove_quoted_nulls (value->word->word);
      dequote_list (value);
    }
  return (value);
}

/* Expand STRING just as if you were expanding a word, but do not dequote
   the resultant WORD_LIST.  This is called only from within this file,
   and is used to correctly preserve quoted characters when expanding
   things like ${1+"$@"}.  This does parameter expansion, command
   substitution, arithmetic expansion, and word splitting. */
static WORD_LIST *
expand_string_leave_quoted (string, quoted)
     char *string;
     int quoted;
{
  WORD_LIST *tlist;
  WORD_LIST *tresult;

  if (string == 0 || *string == '\0')
    return ((WORD_LIST *)NULL);

  tlist = expand_string_internal (string, quoted);

  if (tlist)
    {
      tresult = word_list_split (tlist);
      dispose_words (tlist);
      return (tresult);
    }
  return ((WORD_LIST *)NULL);
}

/* This does not perform word splitting or dequote the WORD_LIST
   it returns. */
static WORD_LIST *
expand_string_for_rhs (string, quoted, dollar_at_p, has_dollar_at)
     char *string;
     int quoted, *dollar_at_p, *has_dollar_at;
{
  WORD_DESC td;
  WORD_LIST *tresult;

  if (string == 0 || *string == '\0')
    return (WORD_LIST *)NULL;

  td.flags = 0;
  td.word = string;
  tresult = call_expand_word_internal (&td, quoted, 1, dollar_at_p, has_dollar_at);
  return (tresult);
}

/* Expand STRING just as if you were expanding a word.  This also returns
   a list of words.  Note that filename globbing is *NOT* done for word
   or string expansion, just when the shell is expanding a command.  This
   does parameter expansion, command substitution, arithmetic expansion,
   and word splitting.  Dequote the resultant WORD_LIST before returning. */
WORD_LIST *
expand_string (string, quoted)
     char *string;
     int quoted;
{
  WORD_LIST *result;

  if (string == 0 || *string == '\0')
    return ((WORD_LIST *)NULL);

  result = expand_string_leave_quoted (string, quoted);
  return (result ? dequote_list (result) : result);
}

/***************************************************
 *                         *
 *  Functions to handle quoting chars      *
 *                         *
 ***************************************************/

/* Conventions:

     A string with s[0] == CTLNUL && s[1] == 0 is a quoted null string.
     The parser passes CTLNUL as CTLESC CTLNUL. */

/* Quote escape characters in string s, but no other characters.  This is
   used to protect CTLESC and CTLNUL in variable values from the rest of
   the word expansion process after the variable is expanded. */
char *
quote_escapes (string)
     char *string;
{
  register char *s, *t;
  size_t slen;
  char *result, *send;
  DECLARE_MBSTATE; 

  slen = strlen (string);
  send = string + slen;

  t = result = (char *)xmalloc ((slen * 2) + 1);
  s = string;

  while (*s)
    {
      if (*s == CTLESC || *s == CTLNUL)
    *t++ = CTLESC;
      COPY_CHAR_P (t, s, send);
    }
  *t = '\0';
  return (result);
}

static WORD_LIST *
list_quote_escapes (list)
     WORD_LIST *list;
{
  register WORD_LIST *w;
  char *t;

  for (w = list; w; w = w->next)
    {
      t = w->word->word;
      w->word->word = quote_escapes (t);
      free (t);
    }
  return list;
}

/* Inverse of quote_escapes; remove CTLESC protecting CTLESC or CTLNUL.

   The parser passes us CTLESC as CTLESC CTLESC and CTLNUL as CTLESC CTLNUL.
   This is necessary to make unquoted CTLESC and CTLNUL characters in the
   data stream pass through properly.

   We need to remove doubled CTLESC characters inside quoted strings before
   quoting the entire string, so we do not double the number of CTLESC
   characters.

   Also used by parts of the pattern substitution code. */
static char *
dequote_escapes (string)
     char *string;
{
  register char *s, *t;
  size_t slen;
  char *result, *send;
  DECLARE_MBSTATE;

  if (string == 0)
    return string;

  slen = strlen (string);
  send = string + slen;

  t = result = (char *)xmalloc (slen + 1);
  s = string;

  if (strchr (string, CTLESC) == 0)
    return (strcpy (result, s));

  while (*s)
    {
      if (*s == CTLESC && (s[1] == CTLESC || s[1] == CTLNUL))
    {
      s++;
      if (*s == '\0')
        break;
    }
      COPY_CHAR_P (t, s, send);
    }
  *t = '\0';
  return result;
}

/* Return a new string with the quoted representation of character C. */
static char *
make_quoted_char (c)
     int c;
{
  char *temp;

  temp = (char *)xmalloc (3);
  if (c == 0)
    {
      temp[0] = CTLNUL;
      temp[1] = '\0';
    }
  else
    {
      temp[0] = CTLESC;
      temp[1] = c;
      temp[2] = '\0';
    }
  return (temp);
}

/* Quote STRING.  Return a new string. */
char *
quote_string (string)
     char *string;
{
  register char *t;
  size_t slen;
  char *result, *send;

  if (*string == 0)
    {
      result = (char *)xmalloc (2);
      result[0] = CTLNUL;
      result[1] = '\0';
    }
  else
    {
      DECLARE_MBSTATE;

      slen = strlen (string);
      send = string + slen;

      result = (char *)xmalloc ((slen * 2) + 1);

      for (t = result; string < send; )
    {
      *t++ = CTLESC;
      COPY_CHAR_P (t, string, send);
    }
      *t = '\0';
    }
  return (result);
}

/* De-quoted quoted characters in STRING. */
char *
dequote_string (string)
     char *string;
{
  register char *s, *t;
  size_t slen;
  char *result, *send;
  DECLARE_MBSTATE;

  slen = strlen (string);

  t = result = (char *)xmalloc (slen + 1);

  if (QUOTED_NULL (string))
    {
      result[0] = '\0';
      return (result);
    }

  /* If no character in the string can be quoted, don't bother examining
     each character.  Just return a copy of the string passed to us. */
  if (strchr (string, CTLESC) == NULL)
    return (strcpy (result, string));

  send = string + slen;
  s = string;
  while (*s)
    {
      if (*s == CTLESC)
    {
      s++;
      if (*s == '\0')
        break;
    }
      COPY_CHAR_P (t, s, send);
    }

  *t = '\0';
  return (result);
}

/* Quote the entire WORD_LIST list. */
static WORD_LIST *
quote_list (list)
     WORD_LIST *list;
{
  register WORD_LIST *w;
  char *t;

  for (w = list; w; w = w->next)
    {
      t = w->word->word;
      w->word->word = quote_string (t);
      free (t);
      w->word->flags |= W_QUOTED;
    }
  return list;
}

static WORD_LIST *
dequote_list (list)
     WORD_LIST *list;
{
  register char *s;
  register WORD_LIST *tlist;

  for (tlist = list; tlist; tlist = tlist->next)
    {
      s = dequote_string (tlist->word->word);
      free (tlist->word->word);
      tlist->word->word = s;
    }
  return list;
}

/* Remove CTLESC protecting a CTLESC or CTLNUL in place.  Return the passed
   string. */
static char *
remove_quoted_escapes (string)
     char *string;
{
  char *t;

  if (string)
    {
      t = dequote_escapes (string);
      strcpy (string, t);
      free (t);
    }

  return (string);
}

/* Perform quoted null character removal on STRING.  We don't allow any
   quoted null characters in the middle or at the ends of strings because
   of how expand_word_internal works.  remove_quoted_nulls () turns
   STRING into an empty string iff it only consists of a quoted null,
   and removes all unquoted CTLNUL characters. */
static char *
remove_quoted_nulls (string)
     char *string;
{
  register size_t slen;
  register int i, j, prev_i;
  DECLARE_MBSTATE;

  if (strchr (string, CTLNUL) == 0)     /* XXX */
    return string;              /* XXX */

  slen = strlen (string);
  i = j = 0;

  while (i < slen)
    {
      if (string[i] == CTLESC)
        {
          i++; j++;
          if (i == slen)
            break;
        }
      else if (string[i] == CTLNUL)
        i++;

      prev_i = i;
      ADVANCE_CHAR (string, slen, i);
      if (j < prev_i)
    {
      do string[j++] = string[prev_i++]; while (prev_i < i);
    }
      else
    j = i;
    }
  string[j] = '\0';

  return (string);
}

/* Perform quoted null character removal on each element of LIST.
   This modifies LIST. */
void
word_list_remove_quoted_nulls (list)
     WORD_LIST *list;
{
  register WORD_LIST *t;

  for (t = list; t; t = t->next)
    remove_quoted_nulls (t->word->word);
}

/* **************************************************************** */
/*                                  */
/*     Functions for Matching and Removing Patterns         */
/*                                  */
/* **************************************************************** */

/* Remove the portion of PARAM matched by PATTERN according to OP, where OP
   can have one of 4 values:
    RP_LONG_LEFT    remove longest matching portion at start of PARAM
    RP_SHORT_LEFT   remove shortest matching portion at start of PARAM
    RP_LONG_RIGHT   remove longest matching portion at end of PARAM
    RP_SHORT_RIGHT  remove shortest matching portion at end of PARAM
*/

#define RP_LONG_LEFT    1
#define RP_SHORT_LEFT   2
#define RP_LONG_RIGHT   3
#define RP_SHORT_RIGHT  4

static char *
remove_pattern (param, pattern, op)
     char *param, *pattern;
     int op;
{
  register int len;
  register char *end;
  register char *p, *ret, c;

  if (param == NULL || *param == '\0')
    return (param);
  if (pattern == NULL || *pattern == '\0')  /* minor optimization */
    return (savestring (param));

  len = STRLEN (param);
  end = param + len;

  switch (op)
    {
      case RP_LONG_LEFT:    /* remove longest match at start */
    for (p = end; p >= param; p--)
      {
        c = *p; *p = '\0';
        if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
          {
        *p = c;
        return (savestring (p));
          }
        *p = c;
      }
    break;

      case RP_SHORT_LEFT:   /* remove shortest match at start */
    for (p = param; p <= end; p++)
      {
        c = *p; *p = '\0';
        if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
          {
        *p = c;
        return (savestring (p));
          }
        *p = c;
      }
    break;

      case RP_LONG_RIGHT:   /* remove longest match at end */
    for (p = param; p <= end; p++)
      {
        if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
          {
        c = *p; *p = '\0';
        ret = savestring (param);
        *p = c;
        return (ret);
          }
      }
    break;

      case RP_SHORT_RIGHT:  /* remove shortest match at end */
    for (p = end; p >= param; p--)
      {
        if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
          {
        c = *p; *p = '\0';
        ret = savestring (param);
        *p = c;
        return (ret);
          }
      }
    break;
    }
  return (savestring (param));  /* no match, return original string */
}

/* Return 1 of the first character of STRING could match the first
   character of pattern PAT.  Used to avoid n2 calls to strmatch(). */
static int
match_pattern_char (pat, string)
     char *pat, *string;
{
  char c;

  if (*string == 0)
    return (0);

  switch (c = *pat++)
    {
    default:
      return (*string == c);
    case '\\':
      return (*string == *pat);
    case '?':
      return (*pat == LPAREN ? 1 : (*string != '\0'));
    case '*':
      return (1);
    case '+':
    case '!':
    case '@':
      return (*pat == LPAREN ? 1 : (*string == c));
    case '[':
      return (*string != '\0');
    }
}

/* Match PAT anywhere in STRING and return the match boundaries.
   This returns 1 in case of a successful match, 0 otherwise.  SP
   and EP are pointers into the string where the match begins and
   ends, respectively.  MTYPE controls what kind of match is attempted.
   MATCH_BEG and MATCH_END anchor the match at the beginning and end
   of the string, respectively.  The longest match is returned. */
static int
match_pattern (string, pat, mtype, sp, ep)
     char *string, *pat;
     int mtype;
     char **sp, **ep;
{
  int c;
  register char *p, *p1;
  char *end;

  if (string == 0 || *string == 0 || pat == 0 || *pat == 0)
    return (0);

  end = string + STRLEN (string);

  switch (mtype)
    {
    case MATCH_ANY:
      for (p = string; p <= end; p++)
    {
      if (match_pattern_char (pat, p))
        {
          for (p1 = end; p1 >= p; p1--)
        {
          c = *p1; *p1 = '\0';
          if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0)
            {
              *p1 = c;
              *sp = p;
              *ep = p1;
              return 1;
            }
          *p1 = c;
        }
        }
    }
      return (0);

    case MATCH_BEG:
      if (match_pattern_char (pat, string) == 0)
    return (0);
      for (p = end; p >= string; p--)
    {
      c = *p; *p = '\0';
      if (strmatch (pat, string, FNMATCH_EXTFLAG) == 0)
        {
          *p = c;
          *sp = string;
          *ep = p;
          return 1;
        }
      *p = c;
    }
      return (0);

    case MATCH_END:
      for (p = string; p <= end; p++)
    if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0)
      {
        *sp = p;
        *ep = end;
        return 1;
      }
      return (0);
    }

  return (0);
}

static int
getpatspec (c, value)
     int c;
     char *value;
{
  if (c == '#')
    return ((*value == '#') ? RP_LONG_LEFT : RP_SHORT_LEFT);
  else  /* c == '%' */
    return ((*value == '%') ? RP_LONG_RIGHT : RP_SHORT_RIGHT);
}

/* Posix.2 says that the WORD should be run through tilde expansion,
   parameter expansion, command substitution and arithmetic expansion.
   This leaves the result quoted, so quote_string_for_globbing () has
   to be called to fix it up for strmatch ().  If QUOTED is non-zero,
   it means that the entire expression was enclosed in double quotes.
   This means that quoting characters in the pattern do not make any
   special pattern characters quoted.  For example, the `*' in the
   following retains its special meaning: "${foo#'*'}". */
static char *
getpattern (value, quoted, expandpat)
     char *value;
     int quoted, expandpat;
{
  char *pat, *tword;
  WORD_LIST *l;
  int i;

  tword = xstrchr (value, '~') ? bash_tilde_expand (value, 0) : savestring (value);

  /* There is a problem here:  how to handle single or double quotes in the
     pattern string when the whole expression is between double quotes?
     POSIX.2 says that enclosing double quotes do not cause the pattern to
     be quoted, but does that leave us a problem with @ and array[@] and their
     expansions inside a pattern? */
#if 0
  if (expandpat && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && *tword)
    {
      i = 0;
      pat = string_extract_double_quoted (tword, &i, 1);
      free (tword);
      tword = pat;
    }
#endif

  /* expand_string_for_rhs () leaves WORD quoted and does not perform
     word splitting. */
  l = *tword ? expand_string_for_rhs (tword,
                      (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? Q_PATQUOTE : quoted,
                      (int *)NULL, (int *)NULL)
         : (WORD_LIST *)0;
  free (tword);
  pat = string_list (l);
  dispose_words (l);
  if (pat)
    {
      tword = quote_string_for_globbing (pat, QGLOB_CVTNULL);
      free (pat);
      pat = tword;
    }
  return (pat);
}

#if 0
/* Handle removing a pattern from a string as a result of ${name%[%]value}
   or ${name#[#]value}. */
static char *
variable_remove_pattern (value, pattern, patspec, quoted)
     char *value, *pattern;
     int patspec, quoted;
{
  char *tword;

  tword = remove_pattern (value, pattern, patspec);

  return (tword);
}
#endif

static char *
list_remove_pattern (list, pattern, patspec, itype, quoted)
     WORD_LIST *list;
     char *pattern;
     int patspec, itype, quoted;
{
  WORD_LIST *new, *l;
  WORD_DESC *w;
  char *tword;

  for (new = (WORD_LIST *)NULL, l = list; l; l = l->next)
    {
      tword = remove_pattern (l->word->word, pattern, patspec);
      w = make_bare_word (tword);
      free (tword);
      new = make_word_list (w, new);
    }

  l = REVERSE_LIST (new, WORD_LIST *);
  if (itype == '*')
    tword = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? string_list_dollar_star (l) : string_list (l);
  else
    tword = string_list ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? quote_list (l) : l);

  dispose_words (l);
  return (tword);
}

static char *
parameter_list_remove_pattern (itype, pattern, patspec, quoted)
     int itype;
     char *pattern;
     int patspec, quoted;
{
  char *ret;
  WORD_LIST *list;

  list = list_rest_of_args ();
  if (list == 0)
    return ((char *)NULL);
  ret = list_remove_pattern (list, pattern, patspec, itype, quoted);
  dispose_words (list);
  return (ret);
}

#if defined (ARRAY_VARS)
static char *
array_remove_pattern (a, pattern, patspec, varname, quoted)
     ARRAY *a;
     char *pattern;
     int patspec;
     char *varname; /* so we can figure out how it's indexed */
     int quoted;
{
  int itype;
  char *ret;
  WORD_LIST *list;
  SHELL_VAR *v;

  /* compute itype from varname here */
  v = array_variable_part (varname, &ret, 0);
  itype = ret[0];

  list = array_to_word_list (a);
  if (list == 0)
   return ((char *)NULL);
  ret = list_remove_pattern (list, pattern, patspec, itype, quoted);
  dispose_words (list);

  return ret;
}
#endif /* ARRAY_VARS */

static char *
parameter_brace_remove_pattern (varname, value, patstr, rtype, quoted)
     char *varname, *value, *patstr;
     int rtype, quoted;
{
  int vtype, patspec;
  char *temp1, *val, *pattern;
  SHELL_VAR *v;

  if (value == 0)
    return ((char *)NULL);

  this_command_name = varname;

  vtype = get_var_and_type (varname, value, &v, &val);
  if (vtype == -1)
    return ((char *)NULL);

  patspec = getpatspec (rtype, patstr);
  if (patspec == RP_LONG_LEFT || patspec == RP_LONG_RIGHT)
    patstr++;

  pattern = getpattern (patstr, quoted, 1);

  temp1 = (char *)NULL;     /* shut up gcc */
  switch (vtype)
    {
    case VT_VARIABLE:
    case VT_ARRAYMEMBER:
      temp1 = remove_pattern (val, pattern, patspec);
      if (vtype == VT_VARIABLE)
    FREE (val);
      if (temp1)
    {
      val = quote_escapes (temp1);
      free (temp1);
      temp1 = val;
    }
      break;
#if defined (ARRAY_VARS)
    case VT_ARRAYVAR:
      temp1 = array_remove_pattern (array_cell (v), pattern, patspec, varname, quoted);
      if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0))
    {
      val = quote_escapes (temp1);
      free (temp1);
      temp1 = val;
    }
      break;
#endif
    case VT_POSPARMS:
      temp1 = parameter_list_remove_pattern (varname[0], pattern, patspec, quoted);
      if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0))
    {
      val = quote_escapes (temp1);
      free (temp1);
      temp1 = val;
    }
      break;
    }

  FREE (pattern);
  return temp1;
}    

/*******************************************
 *                     *
 *  Functions to expand WORD_DESCs     *
 *                     *
 *******************************************/

/* Expand WORD, performing word splitting on the result.  This does
   parameter expansion, command substitution, arithmetic expansion,
   word splitting, and quote removal. */

WORD_LIST *
expand_word (word, quoted)
     WORD_DESC *word;
     int quoted;
{
  WORD_LIST *result, *tresult;

  tresult = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL);
  result = word_list_split (tresult);
  dispose_words (tresult);
  return (result ? dequote_list (result) : result);
}

/* Expand WORD, but do not perform word splitting on the result.  This
   does parameter expansion, command substitution, arithmetic expansion,
   and quote removal. */
WORD_LIST *
expand_word_unsplit (word, quoted)
     WORD_DESC *word;
     int quoted;
{
  WORD_LIST *result;

  expand_no_split_dollar_star = 1;
  result = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL);
  expand_no_split_dollar_star = 0;

  return (result ? dequote_list (result) : result);
}

/* Perform shell expansions on WORD, but do not perform word splitting or
   quote removal on the result. */
WORD_LIST *
expand_word_leave_quoted (word, quoted)
     WORD_DESC *word;
     int quoted;
{
  return (call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL));
}

#if defined (PROCESS_SUBSTITUTION)

/*****************************************************************/
/*                               */
/*          Hacking Process Substitution         */
/*                               */
/*****************************************************************/

#if !defined (HAVE_DEV_FD)
/* Named pipes must be removed explicitly with `unlink'.  This keeps a list
   of FIFOs the shell has open.  unlink_fifo_list will walk the list and
   unlink all of them. add_fifo_list adds the name of an open FIFO to the
   list.  NFIFO is a count of the number of FIFOs in the list. */
#define FIFO_INCR 20

struct temp_fifo {
  char *file;
  pid_t proc;
};

static struct temp_fifo *fifo_list = (struct temp_fifo *)NULL;
static int nfifo;
static int fifo_list_size;

static void
add_fifo_list (pathname)
     char *pathname;
{
  if (nfifo >= fifo_list_size - 1)
    {
      fifo_list_size += FIFO_INCR;
      fifo_list = (struct temp_fifo *)xrealloc (fifo_list,
                fifo_list_size * sizeof (struct temp_fifo));
    }

  fifo_list[nfifo].file = savestring (pathname);
  nfifo++;
}

void
unlink_fifo_list ()
{
  int saved, i, j;

  if (nfifo == 0)
    return;

  for (i = saved = 0; i < nfifo; i++)
    {
      if ((fifo_list[i].proc == -1) || (kill(fifo_list[i].proc, 0) == -1))
    {
      unlink (fifo_list[i].file);
      free (fifo_list[i].file);
      fifo_list[i].file = (char *)NULL;
      fifo_list[i].proc = -1;
    }
      else
    saved++;
    }

  /* If we didn't remove some of the FIFOs, compact the list. */
  if (saved)
    {
      for (i = j = 0; i < nfifo; i++)
    if (fifo_list[i].file)
      {
        fifo_list[j].file = fifo_list[i].file;
        fifo_list[j].proc = fifo_list[i].proc;
        j++;
      }
      nfifo = j;
    }
  else
    nfifo = 0;
}

static char *
make_named_pipe ()
{
  char *tname;

  tname = sh_mktmpname ("sh-np", MT_USERANDOM);
  if (mkfifo (tname, 0600) < 0)
    {
      free (tname);
      return ((char *)NULL);
    }

  add_fifo_list (tname);
  return (tname);
}

#else /* HAVE_DEV_FD */

/* DEV_FD_LIST is a bitmap of file descriptors attached to pipes the shell
   has open to children.  NFDS is a count of the number of bits currently
   set in DEV_FD_LIST.  TOTFDS is a count of the highest possible number
   of open files. */
static char *dev_fd_list = (char *)NULL;
static int nfds;
static int totfds;  /* The highest possible number of open files. */

static void
add_fifo_list (fd)
     int fd;
{
  if (!dev_fd_list || fd >= totfds)
    {
      int ofds;

      ofds = totfds;
      totfds = getdtablesize ();
      if (totfds < 0 || totfds > 256)
    totfds = 256;
      if (fd > totfds)
    totfds = fd + 2;

      dev_fd_list = (char *)xrealloc (dev_fd_list, totfds);
      memset (dev_fd_list + ofds, '\0', totfds - ofds);
    }

  dev_fd_list[fd] = 1;
  nfds++;
}

void
unlink_fifo_list ()
{
  register int i;

  if (nfds == 0)
    return;

  for (i = 0; nfds && i < totfds; i++)
    if (dev_fd_list[i])
      {
    close (i);
    dev_fd_list[i] = 0;
    nfds--;
      }

  nfds = 0;
}

#if defined (NOTDEF)
print_dev_fd_list ()
{
  register int i;

  fprintf (stderr, "pid %ld: dev_fd_list:", (long)getpid ());
  fflush (stderr);

  for (i = 0; i < totfds; i++)
    {
      if (dev_fd_list[i])
    fprintf (stderr, " %d", i);
    }
  fprintf (stderr, "\n");
}
#endif /* NOTDEF */

static char *
make_dev_fd_filename (fd)
     int fd;
{
  char *ret, intbuf[INT_STRLEN_BOUND (int) + 1], *p;

  ret = (char *)xmalloc (sizeof (DEV_FD_PREFIX) + 4);

  strcpy (ret, DEV_FD_PREFIX);
  p = inttostr (fd, intbuf, sizeof (intbuf));
  strcpy (ret + sizeof (DEV_FD_PREFIX) - 1, p);

  add_fifo_list (fd);
  return (ret);
}

#endif /* HAVE_DEV_FD */

/* Return a filename that will open a connection to the process defined by
   executing STRING.  HAVE_DEV_FD, if defined, means open a pipe and return
   a filename in /dev/fd corresponding to a descriptor that is one of the
   ends of the pipe.  If not defined, we use named pipes on systems that have
   them.  Systems without /dev/fd and named pipes are out of luck.

   OPEN_FOR_READ_IN_CHILD, if 1, means open the named pipe for reading or
   use the read end of the pipe and dup that file descriptor to fd 0 in
   the child.  If OPEN_FOR_READ_IN_CHILD is 0, we open the named pipe for
   writing or use the write end of the pipe in the child, and dup that
   file descriptor to fd 1 in the child.  The parent does the opposite. */

static char *
process_substitute (string, open_for_read_in_child)
     char *string;
     int open_for_read_in_child;
{
  char *pathname;
  int fd, result;
  pid_t old_pid, pid;
#if defined (HAVE_DEV_FD)
  int parent_pipe_fd, child_pipe_fd;
  int fildes[2];
#endif /* HAVE_DEV_FD */
#if defined (JOB_CONTROL)
  pid_t old_pipeline_pgrp;
#endif

  if (!string || !*string || wordexp_only)
    return ((char *)NULL);

#if !defined (HAVE_DEV_FD)
  pathname = make_named_pipe ();
#else /* HAVE_DEV_FD */
  if (pipe (fildes) < 0)
    {
      sys_error ("cannot make pipe for process substitution");
      return ((char *)NULL);
    }
  /* If OPEN_FOR_READ_IN_CHILD == 1, we want to use the write end of
     the pipe in the parent, otherwise the read end. */
  parent_pipe_fd = fildes[open_for_read_in_child];
  child_pipe_fd = fildes[1 - open_for_read_in_child];
  /* Move the parent end of the pipe to some high file descriptor, to
     avoid clashes with FDs used by the script. */
  parent_pipe_fd = move_to_high_fd (parent_pipe_fd, 1, 64);

  pathname = make_dev_fd_filename (parent_pipe_fd);
#endif /* HAVE_DEV_FD */

  if (!pathname)
    {
      sys_error ("cannot make pipe for process substitution");
      return ((char *)NULL);
    }

  old_pid = last_made_pid;

#if defined (JOB_CONTROL)
  old_pipeline_pgrp = pipeline_pgrp;
  pipeline_pgrp = shell_pgrp;
  save_pipeline (1);
#endif /* JOB_CONTROL */

  pid = make_child ((char *)NULL, 1);
  if (pid == 0)
    {
      reset_terminating_signals (); /* XXX */
      /* Cancel traps, in trap.c. */
      restore_original_signals ();
      setup_async_signals ();
      subshell_environment |= SUBSHELL_COMSUB;
    }

#if defined (JOB_CONTROL)
  set_sigchld_handler ();
  stop_making_children ();
  pipeline_pgrp = old_pipeline_pgrp;
#endif /* JOB_CONTROL */

  if (pid < 0)
    {
      sys_error ("cannot make child for process substitution");
      free (pathname);
#if defined (HAVE_DEV_FD)
      close (parent_pipe_fd);
      close (child_pipe_fd);
#endif /* HAVE_DEV_FD */
      return ((char *)NULL);
    }

  if (pid > 0)
    {
#if defined (JOB_CONTROL)
      restore_pipeline (1);
#endif

#if !defined (HAVE_DEV_FD)
      fifo_list[nfifo-1].proc = pid;
#endif

      last_made_pid = old_pid;

#if defined (JOB_CONTROL) && defined (PGRP_PIPE)
      close_pgrp_pipe ();
#endif /* JOB_CONTROL && PGRP_PIPE */

#if defined (HAVE_DEV_FD)
      close (child_pipe_fd);
#endif /* HAVE_DEV_FD */

      return (pathname);
    }

  set_sigint_handler ();

#if defined (JOB_CONTROL)
  set_job_control (0);
#endif /* JOB_CONTROL */

#if !defined (HAVE_DEV_FD)
  /* Open the named pipe in the child. */
  fd = open (pathname, open_for_read_in_child ? O_RDONLY|O_NONBLOCK : O_WRONLY);
  if (fd < 0)
    {
      sys_error ("cannot open named pipe %s for %s", pathname,
    open_for_read_in_child ? "reading" : "writing");
      exit (127);
    }
  if (open_for_read_in_child)
    {
      if (sh_unset_nodelay_mode (fd) < 0)
    {
      sys_error ("cannout reset nodelay mode for fd %d", fd);
      exit (127);
    }
    }
#else /* HAVE_DEV_FD */
  fd = child_pipe_fd;
#endif /* HAVE_DEV_FD */

  if (dup2 (fd, open_for_read_in_child ? 0 : 1) < 0)
    {
      sys_error ("cannot duplicate named pipe %s as fd %d", pathname,
    open_for_read_in_child ? 0 : 1);
      exit (127);
    }

  if (fd != (open_for_read_in_child ? 0 : 1))
    close (fd);

  /* Need to close any files that this process has open to pipes inherited
     from its parent. */
  if (current_fds_to_close)
    {
      close_fd_bitmap (current_fds_to_close);
      current_fds_to_close = (struct fd_bitmap *)NULL;
    }

#if defined (HAVE_DEV_FD)
  /* Make sure we close the parent's end of the pipe and clear the slot
     in the fd list so it is not closed later, if reallocated by, for
     instance, pipe(2). */
  close (parent_pipe_fd);
  dev_fd_list[parent_pipe_fd] = 0;
#endif /* HAVE_DEV_FD */

  result = parse_and_execute (string, "process substitution", (SEVAL_NONINT|SEVAL_NOHIST));

#if !defined (HAVE_DEV_FD)
  /* Make sure we close the named pipe in the child before we exit. */
  close (open_for_read_in_child ? 0 : 1);
#endif /* !HAVE_DEV_FD */

  exit (result);
  /*NOTREACHED*/
}
#endif /* PROCESS_SUBSTITUTION */

/***********************************/
/*                 */
/*  Command Substitution       */
/*                 */
/***********************************/

static char *
read_comsub (fd, quoted)
     int fd, quoted;
{
  char *istring, buf[128], *bufp;
  int istring_index, istring_size, c;
  ssize_t bufn;

  istring = (char *)NULL;
  istring_index = istring_size = bufn = 0;

#ifdef __CYGWIN__
  setmode (fd, O_TEXT);     /* we don't want CR/LF, we want Unix-style */
#endif

  /* Read the output of the command through the pipe. */
  while (1)
    {
      if (fd < 0)
    break;
      if (--bufn <= 0)
    {
      bufn = zread (fd, buf, sizeof (buf));
      if (bufn <= 0) 
        break;
      bufp = buf;
    }
      c = *bufp++;

      if (c == 0)
    {
#if 0
      internal_warning ("read_comsub: ignored null byte in input");
#endif
      continue;
    }

      /* Add the character to ISTRING, possibly after resizing it. */
      RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size, DEFAULT_ARRAY_SIZE);

      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || c == CTLESC || c == CTLNUL)
    istring[istring_index++] = CTLESC;

      istring[istring_index++] = c;

#if 0
#if defined (__CYGWIN__)
      if (c == '\n' && istring_index > 1 && istring[istring_index - 2] == '\r')
    {
      istring_index--;
      istring[istring_index - 1] = '\n';
    }
#endif
#endif
    }

  if (istring)
    istring[istring_index] = '\0';

  /* If we read no output, just return now and save ourselves some
     trouble. */
  if (istring_index == 0)
    {
      FREE (istring);
      return (char *)NULL;
    }

  /* Strip trailing newlines from the output of the command. */
  if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
    {
      while (istring_index > 0)
    {
      if (istring[istring_index - 1] == '\n')
        {
          --istring_index;

          /* If the newline was quoted, remove the quoting char. */
          if (istring[istring_index - 1] == CTLESC)
        --istring_index;
        }
      else
        break;
    }
      istring[istring_index] = '\0';
    }
  else
    strip_trailing (istring, istring_index - 1, 1);

  return istring;
}

/* Perform command substitution on STRING.  This returns a string,
   possibly quoted. */
char *
command_substitute (string, quoted)
     char *string;
     int quoted;
{
  pid_t pid, old_pid, old_pipeline_pgrp;
  char *istring;
  int result, fildes[2], function_value;

  istring = (char *)NULL;

  /* Don't fork () if there is no need to.  In the case of no command to
     run, just return NULL. */
  if (!string || !*string || (string[0] == '\n' && !string[1]))
    return ((char *)NULL);

  if (wordexp_only && read_but_dont_execute)
    {
      last_command_exit_value = 125;
      jump_to_top_level (EXITPROG);
    }

  /* We're making the assumption here that the command substitution will
     eventually run a command from the file system.  Since we'll run
     maybe_make_export_env in this subshell before executing that command,
     the parent shell and any other shells it starts will have to remake
     the environment.  If we make it before we fork, other shells won't
     have to.  Don't bother if we have any temporary variable assignments,
     though, because the export environment will be remade after this
     command completes anyway, but do it if all the words to be expanded
     are variable assignments. */
  if (subst_assign_varlist == 0 || garglist == 0)
    maybe_make_export_env ();   /* XXX */

  /* Pipe the output of executing STRING into the current shell. */
  if (pipe (fildes) < 0)
    {
      sys_error ("cannot make pipe for command substitution");
      goto error_exit;
    }

  old_pid = last_made_pid;
#if defined (JOB_CONTROL)
  old_pipeline_pgrp = pipeline_pgrp;
  /* Don't reset the pipeline pgrp if we're already a subshell in a pipeline. */
  if ((subshell_environment & SUBSHELL_PIPE) == 0)
    pipeline_pgrp = shell_pgrp;
  cleanup_the_pipeline ();
#endif

  pid = make_child ((char *)NULL, 0);
  if (pid == 0)
    /* Reset the signal handlers in the child, but don't free the
       trap strings. */
    reset_signal_handlers ();

#if defined (JOB_CONTROL)
  set_sigchld_handler ();
  stop_making_children ();
  pipeline_pgrp = old_pipeline_pgrp;
#else
  stop_making_children ();
#endif /* JOB_CONTROL */

  if (pid < 0)
    {
      sys_error ("cannot make child for command substitution");
    error_exit:

      FREE (istring);
      close (fildes[0]);
      close (fildes[1]);
      return ((char *)NULL);
    }

  if (pid == 0)
    {
      set_sigint_handler ();    /* XXX */

      if (dup2 (fildes[1], 1) < 0)
    {
      sys_error ("command_substitute: cannot duplicate pipe as fd 1");
      exit (EXECUTION_FAILURE);
    }

      /* If standard output is closed in the parent shell
     (such as after `exec >&-'), file descriptor 1 will be
     the lowest available file descriptor, and end up in
     fildes[0].  This can happen for stdin and stderr as well,
     but stdout is more important -- it will cause no output
     to be generated from this command. */
      if ((fildes[1] != fileno (stdin)) &&
      (fildes[1] != fileno (stdout)) &&
      (fildes[1] != fileno (stderr)))
    close (fildes[1]);

      if ((fildes[0] != fileno (stdin)) &&
      (fildes[0] != fileno (stdout)) &&
      (fildes[0] != fileno (stderr)))
    close (fildes[0]);

      /* The currently executing shell is not interactive. */
      interactive = 0;

      /* This is a subshell environment. */
      subshell_environment |= SUBSHELL_COMSUB;

      /* When not in POSIX mode, command substitution does not inherit
     the -e flag. */
      if (posixly_correct == 0)
    exit_immediately_on_error = 0;

      remove_quoted_escapes (string);

      startup_state = 2;    /* see if we can avoid a fork */
      /* Give command substitution a place to jump back to on failure,
     so we don't go back up to main (). */
      result = setjmp (top_level);

      /* If we're running a command substitution inside a shell function,
     trap `return' so we don't return from the function in the subshell
     and go off to never-never land. */
      if (result == 0 && return_catch_flag)
    function_value = setjmp (return_catch);
      else
    function_value = 0;

      if (result == EXITPROG)
    exit (last_command_exit_value);
      else if (result)
    exit (EXECUTION_FAILURE);
      else if (function_value)
    exit (return_catch_value);
      else
    exit (parse_and_execute (string, "command substitution", SEVAL_NOHIST));
    }
  else
    {
#if defined (JOB_CONTROL) && defined (PGRP_PIPE)
      close_pgrp_pipe ();
#endif /* JOB_CONTROL && PGRP_PIPE */

      close (fildes[1]);

      istring = read_comsub (fildes[0], quoted);

      close (fildes[0]);

      current_command_subst_pid = pid;
      last_command_exit_value = wait_for (pid);
      last_command_subst_pid = pid;
      last_made_pid = old_pid;

#if defined (JOB_CONTROL)
      /* If last_command_exit_value > 128, then the substituted command
     was terminated by a signal.  If that signal was SIGINT, then send
     SIGINT to ourselves.  This will break out of loops, for instance. */
      if (last_command_exit_value == (128 + SIGINT))
    kill (getpid (), SIGINT);

      /* wait_for gives the terminal back to shell_pgrp.  If some other
     process group should have it, give it away to that group here.
     pipeline_pgrp is non-zero only while we are constructing a
     pipline, so what we are concerned about is whether or not that
     pipeline was started in the background.  A pipeline started in
     the background should never get the tty back here. */
#if 0
      if (interactive && pipeline_pgrp != (pid_t)0 && pipeline_pgrp != last_asynchronous_pid)
#else
      if (interactive && pipeline_pgrp != (pid_t)0 && (subshell_environment & SUBSHELL_ASYNC) == 0)
#endif
    give_terminal_to (pipeline_pgrp, 0);
#endif /* JOB_CONTROL */

      return (istring);
    }
}

/********************************************************
 *                          *
 *  Utility functions for parameter expansion   *
 *                          *
 ********************************************************/

#if defined (ARRAY_VARS)

static arrayind_t
array_length_reference (s)
     char *s;
{
  int len;
  arrayind_t ind;
  char *t, c;
  ARRAY *array;
  SHELL_VAR *var;

  var = array_variable_part (s, &t, &len);

  /* If unbound variables should generate an error, report one and return
     failure. */
  if ((var == 0 || array_p (var) == 0) && unbound_vars_is_error)
    {
      c = *--t;
      *t = '\0';
      err_unboundvar (s);
      *t = c;
      return (-1);
    }
  else if (var == 0)
    return 0;

  /* We support a couple of expansions for variables that are not arrays.
     We'll return the length of the value for v[0], and 1 for v[@] or
     v[*].  Return 0 for everything else. */

  array = array_p (var) ? array_cell (var) : (ARRAY *)NULL;

  if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
    return (array_p (var) ? array_num_elements (array) : 1);

  ind = array_expand_index (t, len);
  if (ind < 0)
    {
      err_badarraysub (t);
      return (-1);
    }

  if (array_p (var))
    t = array_reference (array, ind);
  else
    t = (ind == 0) ? value_cell (var) : (char *)NULL;

  len = STRLEN (t);
  return (len);
}
#endif /* ARRAY_VARS */

static int
valid_brace_expansion_word (name, var_is_special)
     char *name;
     int var_is_special;
{
  if (DIGIT (*name) && all_digits (name))
    return 1;
  else if (var_is_special)
    return 1;
#if defined (ARRAY_VARS)
  else if (valid_array_reference (name))
    return 1;
#endif /* ARRAY_VARS */
  else if (legal_identifier (name))
    return 1;
  else
    return 0;
}

/* Parameter expand NAME, and return a new string which is the expansion,
   or NULL if there was no expansion.
   VAR_IS_SPECIAL is non-zero if NAME is one of the special variables in
   the shell, e.g., "@", "$", "*", etc.  QUOTED, if non-zero, means that
   NAME was found inside of a double-quoted expression. */
static char *
parameter_brace_expand_word (name, var_is_special, quoted)
     char *name;
     int var_is_special, quoted;
{
  char *temp, *tt;
  intmax_t arg_index;
  SHELL_VAR *var;
  int atype;

  /* Handle multiple digit arguments, as in ${11}. */
  
  if (legal_number (name, &arg_index))
    {
      tt = get_dollar_var_value (arg_index);
      temp = tt ? quote_escapes (tt) : (char *)NULL;
      FREE (tt);
    }
  else if (var_is_special)      /* ${@} */
    {
      int sindex;
      tt = (char *)xmalloc (2 + strlen (name));
      tt[sindex = 0] = '$';
      strcpy (tt + 1, name);

      temp = param_expand (tt, &sindex, quoted, (int *)NULL, (int *)NULL,
               (int *)NULL, (int *)NULL, 0);
      free (tt);
    }
#if defined (ARRAY_VARS)
  else if (valid_array_reference (name))
    {
      temp = array_value (name, quoted, &atype);
      if (atype == 0 && temp)
    temp = quote_escapes (temp);
    }
#endif
  else if (var = find_variable (name))
    {
      if (var_isset (var) && invisible_p (var) == 0)
    {
#if defined (ARRAY_VARS)
      temp = array_p (var) ? array_reference (array_cell (var), 0) : value_cell (var);
#else
      temp = value_cell (var);
#endif

      if (temp)
        temp = quote_escapes (temp);
    }
      else
    temp = (char *)NULL;
    }
  else
    temp = (char *)NULL;

  return (temp);
}

/* Expand an indirect reference to a variable: ${!NAME} expands to the
   value of the variable whose name is the value of NAME. */
static char *
parameter_brace_expand_indir (name, var_is_special, quoted)
     char *name;
     int var_is_special, quoted;
{
  char *temp, *t;

  t = parameter_brace_expand_word (name, var_is_special, quoted);
  if (t == 0)
    return (t);
  temp = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted);
  free (t);
  return temp;
}

/* Expand the right side of a parameter expansion of the form ${NAMEcVALUE},
   depending on the value of C, the separating character.  C can be one of
   "-", "+", or "=".  QUOTED is true if the entire brace expression occurs
   between double quotes. */
static char *
parameter_brace_expand_rhs (name, value, c, quoted, qdollaratp, hasdollarat)
     char *name, *value;
     int c, quoted, *qdollaratp, *hasdollarat;
{
  WORD_LIST *l;
  char *t, *t1, *temp;
  int hasdol;

  /* XXX - Should we tilde expand in an assignment context if C is `='? */
  if (*value == '~')
    temp = bash_tilde_expand (value, 0);
  else if (xstrchr (value, '~')  && unquoted_substring ("=~", value))
    temp = bash_tilde_expand (value, 1);
  else
    temp = savestring (value);

  /* If the entire expression is between double quotes, we want to treat
     the value as a double-quoted string, with the exception that we strip
     embedded unescaped double quotes. */
  if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && *temp)
    {
      hasdol = 0;
      t = string_extract_double_quoted (temp, &hasdol, 1);
      free (temp);
      temp = t;
    }

  hasdol = 0;
  /* XXX was 0 not quoted */
  l = *temp ? expand_string_for_rhs (temp, quoted, &hasdol, (int *)NULL)
        : (WORD_LIST *)0;
  if (hasdollarat)
    *hasdollarat = hasdol || (l && l->next);
  free (temp);
  if (l)
    {
      /* The expansion of TEMP returned something.  We need to treat things
      slightly differently if HASDOL is non-zero. */
      temp = string_list (l);
      /* If l->next is not null, we know that TEMP contained "$@", since that
     is the only expansion that creates more than one word. */
      if ((hasdol && quoted) || l->next)
    *qdollaratp = 1;
      dispose_words (l);
    }
  else if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && hasdol)
    {
      /* The brace expansion occurred between double quotes and there was
     a $@ in TEMP.  It does not matter if the $@ is quoted, as long as
     it does not expand to anything.  In this case, we want to return
     a quoted empty string. */
      temp = (char *)xmalloc (2);
      temp[0] = CTLNUL;
      temp[1] = '\0';
    }
  else
    temp = (char *)NULL;

  if (c == '-' || c == '+')
    return (temp);

  /* c == '=' */
  t = temp ? savestring (temp) : savestring ("");
  t1 = dequote_string (t);
  free (t);
  bind_variable (name, t1);
  free (t1);
  return (temp);
}

/* Deal with the right hand side of a ${name:?value} expansion in the case
   that NAME is null or not set.  If VALUE is non-null it is expanded and
   used as the error message to print, otherwise a standard message is
   printed. */
static void
parameter_brace_expand_error (name, value)
     char *name, *value;
{
  WORD_LIST *l;
  char *temp;

  if (value && *value)
    {
      if (*value == '~')
    temp = bash_tilde_expand (value, 0);
      else if (xstrchr (value, '~')  && unquoted_substring ("=~", value))
    temp = bash_tilde_expand (value, 1);
      else
    temp = savestring (value);

      l = expand_string (temp, 0);
      FREE (temp);
      temp =  string_list (l);
      report_error ("%s: %s", name, temp ? temp : "");  /* XXX was value not "" */
      FREE (temp);
      dispose_words (l);
    }
  else
    report_error ("%s: parameter null or not set", name);

  /* Free the data we have allocated during this expansion, since we
     are about to longjmp out. */
  free (name);
  FREE (value);
}

/* Return 1 if NAME is something for which parameter_brace_expand_length is
   OK to do. */
static int
valid_length_expression (name)
     char *name;
{
  return (name[1] == '\0' ||                    /* ${#} */
      ((sh_syntaxtab[(unsigned char) name[1]] & CSPECVAR) && name[2] == '\0') ||  /* special param */
      (DIGIT (name[1]) && all_digits (name + 1)) || /* ${#11} */
#if defined (ARRAY_VARS)
      valid_array_reference (name + 1) ||           /* ${#a[7]} */
#endif
      legal_identifier (name + 1));             /* ${#PS1} */
}

/* Handle the parameter brace expansion that requires us to return the
   length of a parameter. */
static intmax_t
parameter_brace_expand_length (name)
     char *name;
{
  char *t, *newname;
  intmax_t number, arg_index;
  WORD_LIST *list;
#if defined (ARRAY_VARS)
  SHELL_VAR *var;
#endif

  if (name[1] == '\0')          /* ${#} */
    number = number_of_args ();
  else if ((name[1] == '@' || name[1] == '*') && name[2] == '\0')   /* ${#@}, ${#*} */
    number = number_of_args ();
  else if ((sh_syntaxtab[(unsigned char) name[1]] & CSPECVAR) && name[2] == '\0')
    {
      /* Take the lengths of some of the shell's special parameters. */
      switch (name[1])
    {
    case '-':
      t = which_set_flags ();
      break;
    case '?':
      t = itos (last_command_exit_value);
      break;
    case '$':
      t = itos (dollar_dollar_pid);
      break;
    case '!':
      if (last_asynchronous_pid == NO_PID)
        t = (char *)NULL;
      else
        t = itos (last_asynchronous_pid);
      break;
    case '#':
      t = itos (number_of_args ());
      break;
    }
      number = STRLEN (t);
      FREE (t);
    }
#if defined (ARRAY_VARS)
  else if (valid_array_reference (name + 1))
    number = array_length_reference (name + 1);
#endif /* ARRAY_VARS */
  else
    {
      number = 0;

      if (legal_number (name + 1, &arg_index))      /* ${#1} */
    {
      t = get_dollar_var_value (arg_index);
      number = STRLEN (t);
      FREE (t);
    }
#if defined (ARRAY_VARS)
      else if ((var = find_variable (name + 1)) && array_p (var))
    {
      t = array_reference (array_cell (var), 0);
      number = STRLEN (t);
    }
#endif
      else              /* ${#PS1} */
    {
      newname = savestring (name);
      newname[0] = '$';
      list = expand_string (newname, Q_DOUBLE_QUOTES);
      t = list ? string_list (list) : (char *)NULL;
      free (newname);
      if (list)
        dispose_words (list);

      number = STRLEN (t);
      FREE (t);
    }
    }

  return (number);
}

/* Skip characters in SUBSTR until DELIM.  SUBSTR is an arithmetic expression,
   so we do some ad-hoc parsing of an arithmetic expression to find
   the first DELIM, instead of using strchr(3).  Two rules:
    1.  If the substring contains a `(', read until closing `)'.
    2.  If the substring contains a `?', read past one `:' for each `?'.
*/

static char *
skiparith (substr, delim)
     char *substr;
     int delim;
{
  size_t sublen;
  int skipcol, pcount, i;
  DECLARE_MBSTATE;

  sublen = strlen (substr);
  i = skipcol = pcount = 0;
  while (substr[i])
    {
      /* Balance parens */
      if (substr[i] == LPAREN)
    {
      pcount++;
      i++;
      continue;
    }
      if (substr[i] == RPAREN && pcount)
    {
      pcount--;
      i++;
      continue;
    }
      if (pcount)
    {
      ADVANCE_CHAR (substr, sublen, i);
      continue;
    }

      /* Skip one `:' for each `?' */
      if (substr[i] == ':' && skipcol)
    {
      skipcol--;
      i++;
      continue;
    }
      if (substr[i] == delim)
    break;
      if (substr[i] == '?')
    {
      skipcol++;
      i++;
      continue;
    }
      ADVANCE_CHAR (substr, sublen, i);
    }

  return (substr + i);
}

/* Verify and limit the start and end of the desired substring.  If
   VTYPE == 0, a regular shell variable is being used; if it is 1,
   then the positional parameters are being used; if it is 2, then
   VALUE is really a pointer to an array variable that should be used.
   Return value is 1 if both values were OK, 0 if there was a problem
   with an invalid expression, or -1 if the values were out of range. */
static int
verify_substring_values (value, substr, vtype, e1p, e2p)
     char *value, *substr;
     int vtype;
     intmax_t *e1p, *e2p;
{
  char *t, *temp1, *temp2;
  arrayind_t len;
  int expok;
#if defined (ARRAY_VARS)
 ARRAY *a;
#endif

  /* duplicate behavior of strchr(3) */
  t = skiparith (substr, ':');
  if (*t && *t == ':')
    *t = '\0';
  else
    t = (char *)0;

  temp1 = expand_string_if_necessary (substr, Q_DOUBLE_QUOTES, expand_string);
  *e1p = evalexp (temp1, &expok);
  free (temp1);
  if (expok == 0)
    return (0);

  len = -1; /* paranoia */
  switch (vtype)
    {
    case VT_VARIABLE:
    case VT_ARRAYMEMBER:
      len = strlen (value);
      break;
    case VT_POSPARMS:
      len = number_of_args () + 1;
      break;
#if defined (ARRAY_VARS)
    case VT_ARRAYVAR:
      a = (ARRAY *)value;
      len = array_num_elements (a) + 1;
      break;
#endif
    }

  if (len == -1)    /* paranoia */
    return -1;

  if (*e1p < 0)     /* negative offsets count from end */
    *e1p += len;

  if (*e1p >= len || *e1p < 0)
    return (-1);

  if (t)
    {
      t++;
      temp2 = savestring (t);
      temp1 = expand_string_if_necessary (temp2, Q_DOUBLE_QUOTES, expand_string);
      free (temp2);
      t[-1] = ':';
      *e2p = evalexp (temp1, &expok);
      free (temp1);
      if (expok == 0)
    return (0);
      if (*e2p < 0)
    {
      internal_error ("%s: substring expression < 0", t);
      return (0);
    }
      *e2p += *e1p;     /* want E2 chars starting at E1 */
      if (*e2p > len)
    *e2p = len;
    }
  else
    *e2p = len;

  return (1);
}

/* Return the type of variable specified by VARNAME (simple variable,
   positional param, or array variable).  Also return the value specified
   by VARNAME (value of a variable or a reference to an array element).
   If this returns VT_VARIABLE, the caller assumes that CTLESC and CTLNUL
   characters in the value are quoted with CTLESC and takes appropriate
   steps.  For convenience, *VALP is set to the dequoted VALUE. */
static int
get_var_and_type (varname, value, varp, valp)
     char *varname, *value;
     SHELL_VAR **varp;
     char **valp;
{
  int vtype;
  char *temp;
#if defined (ARRAY_VARS)
  SHELL_VAR *v;
#endif

  /* This sets vtype to VT_VARIABLE or VT_POSPARMS */
  vtype = (varname[0] == '@' || varname[0] == '*') && varname[1] == '\0';
  *varp = (SHELL_VAR *)NULL;

#if defined (ARRAY_VARS)
  if (valid_array_reference (varname))
    {
      v = array_variable_part (varname, &temp, (int *)0);
      if (v && array_p (v))
    { /* [ */
      if (ALL_ELEMENT_SUB (temp[0]) && temp[1] == ']')
        {
          vtype = VT_ARRAYVAR;
          *valp = (char *)array_cell (v);
        }
      else
        {
          vtype = VT_ARRAYMEMBER;
          *valp = array_value (varname, 1, (int *)NULL);
        }
      *varp = v;
    }
      else
    return -1;
    }
  else if ((v = find_variable (varname)) && array_p (v))
    {
      vtype = VT_ARRAYMEMBER;
      *varp = v;
      *valp = array_reference (array_cell (v), 0);
    }
  else
#endif
  *valp = (value && vtype == VT_VARIABLE) ? dequote_escapes (value) : value;

  return vtype;
}

/******************************************************/
/*                            */
/* Functions to extract substrings of variable values */
/*                            */
/******************************************************/

/* Process a variable substring expansion: ${name:e1[:e2]}.  If VARNAME
   is `@', use the positional parameters; otherwise, use the value of
   VARNAME.  If VARNAME is an array variable, use the array elements. */

static char *
parameter_brace_substring (varname, value, substr, quoted)
     char *varname, *value, *substr;
     int quoted;
{
  intmax_t e1, e2;
  int vtype, r;
  char *temp, *val, *tt;
  SHELL_VAR *v;

  if (value == 0)
    return ((char *)NULL);

  this_command_name = varname;

  vtype = get_var_and_type (varname, value, &v, &val);
  if (vtype == -1)
    return ((char *)NULL);

  r = verify_substring_values (val, substr, vtype, &e1, &e2);
  if (r <= 0)
    return ((r == 0) ? &expand_param_error : (char *)NULL);

  switch (vtype)
    {
    case VT_VARIABLE:
    case VT_ARRAYMEMBER:
      tt = substring (val, e1, e2);
      if (vtype == VT_VARIABLE)
    FREE (val);
      if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
    temp = quote_string (tt);
      else
    temp = tt ? quote_escapes (tt) : (char *)NULL;
      FREE (tt);
      break;
    case VT_POSPARMS:
      tt = pos_params (varname, e1, e2, quoted);
      if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0)
    {
      temp = tt ? quote_escapes (tt) : (char *)NULL;
      FREE (tt);
    }
      else
    temp = tt;
      break;
#if defined (ARRAY_VARS)
    case VT_ARRAYVAR:
      tt = array_subrange (array_cell (v), e1, e2, quoted);
      if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0)
    {
      temp = tt ? quote_escapes (tt) : (char *)NULL;
      FREE (tt);
    }
      else
    temp = tt;
      break;
#endif
    default:
      temp = (char *)NULL;
    }

  return temp;
}

/****************************************************************/
/*                              */
/* Functions to perform pattern substitution on variable values */
/*                              */
/****************************************************************/

char *
pat_subst (string, pat, rep, mflags)
     char *string, *pat, *rep;
     int mflags;
{
  char *ret, *s, *e, *str;
  int rsize, rptr, l, replen, mtype;

  mtype = mflags & MATCH_TYPEMASK;

  /* Special cases:
   *    1.  A null pattern with mtype == MATCH_BEG means to prefix STRING
   *        with REP and return the result.
   *    2.  A null pattern with mtype == MATCH_END means to append REP to
   *        STRING and return the result.
   */
  if ((pat == 0 || *pat == 0) && (mtype == MATCH_BEG || mtype == MATCH_END))
    {
      replen = STRLEN (rep);
      l = strlen (string);
      ret = (char *)xmalloc (replen + l + 2);
      if (replen == 0)
    strcpy (ret, string);
      else if (mtype == MATCH_BEG)
    {
      strcpy (ret, rep);
      strcpy (ret + replen, string);
    }
      else
    {
      strcpy (ret, string);
      strcpy (ret + l, rep);
    }
      return (ret);
    }

  ret = (char *)xmalloc (rsize = 64);
  ret[0] = '\0';

  for (replen = STRLEN (rep), rptr = 0, str = string;;)
    {
      if (match_pattern (str, pat, mtype, &s, &e) == 0)
    break;
      l = s - str;
      RESIZE_MALLOCED_BUFFER (ret, rptr, (l + replen), rsize, 64);

      /* OK, now copy the leading unmatched portion of the string (from
     str to s) to ret starting at rptr (the current offset).  Then copy
     the replacement string at ret + rptr + (s - str).  Increment
     rptr (if necessary) and str and go on. */
      if (l)
    {
      strncpy (ret + rptr, str, l);
      rptr += l;
    }
      if (replen)
    {
      strncpy (ret + rptr, rep, replen);
      rptr += replen;
    }
      if (s == e)
    e++;        /* avoid infinite recursion on zero-length match */
      str = e;      /* e == end of match */
      if (((mflags & MATCH_GLOBREP) == 0) || mtype != MATCH_ANY)
    break;
    }

  /* Now copy the unmatched portion of the input string */
  if (*str)
    {
      RESIZE_MALLOCED_BUFFER (ret, rptr, STRLEN(str) + 1, rsize, 64);
      strcpy (ret + rptr, str);
    }
  else
    ret[rptr] = '\0';

  return ret;
}

/* Do pattern match and replacement on the positional parameters. */
static char *
pos_params_pat_subst (string, pat, rep, mflags)
     char *string, *pat, *rep;
     int mflags;
{
  WORD_LIST *save, *params;
  WORD_DESC *w;
  char *ret, *tt;

  save = params = list_rest_of_args ();
  if (save == 0)
    return ((char *)NULL);

  for ( ; params; params = params->next)
    {
      ret = pat_subst (params->word->word, pat, rep, mflags);
      w = make_bare_word (ret);
      dispose_word (params->word);
      params->word = w;
      FREE (ret);
    }

  ret = string_list ((mflags & MATCH_QUOTED) ? quote_list (save) : save);
  dispose_words (save);

  return (ret);
}

/* Perform pattern substitution on VALUE, which is the expansion of
   VARNAME.  PATSUB is an expression supplying the pattern to match
   and the string to substitute.  QUOTED is a flags word containing
   the type of quoting currently in effect. */
static char *
parameter_brace_patsub (varname, value, patsub, quoted)
     char *varname, *value, *patsub;
     int quoted;
{
  int vtype, mflags;
  char *val, *temp, *pat, *rep, *p, *lpatsub, *tt;
  SHELL_VAR *v;

  if (value == 0)
    return ((char *)NULL);

  this_command_name = varname;

  vtype = get_var_and_type (varname, value, &v, &val);
  if (vtype == -1)
    return ((char *)NULL);

  mflags = 0;
  if (*patsub == '/')
    {
      mflags |= MATCH_GLOBREP;
      patsub++;
    }

  /* Malloc this because expand_string_if_necessary or one of the expansion
     functions in its call chain may free it on a substitution error. */
  lpatsub = savestring (patsub);

  if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
    mflags |= MATCH_QUOTED;

  if (rep = quoted_strchr (lpatsub, '/', ST_BACKSL))
    *rep++ = '\0';
  else
    rep = (char *)NULL;

  if (rep && *rep == '\0')
    rep = (char *)NULL;

  /* Expand PAT and REP for command, variable and parameter, arithmetic,
     and process substitution.  Also perform quote removal.  Do not
     perform word splitting or filename generation. */
  pat = expand_string_if_necessary (lpatsub, (quoted & ~Q_DOUBLE_QUOTES), expand_string_unsplit);

  if (rep)
    {
      if ((mflags & MATCH_QUOTED) == 0)
    rep = expand_string_if_necessary (rep, quoted, expand_string_unsplit);
      else
    rep = expand_string_to_string_internal (rep, quoted, expand_string_unsplit);
    }

  p = pat;
  if (pat && pat[0] == '#')
    {
      mflags |= MATCH_BEG;
      p++;
    }
  else if (pat && pat[0] == '%')
    {
      mflags |= MATCH_END;
      p++;
    }
  else
    mflags |= MATCH_ANY;

  /* OK, we now want to substitute REP for PAT in VAL.  If
     flags & MATCH_GLOBREP is non-zero, the substitution is done
     everywhere, otherwise only the first occurrence of PAT is
     replaced.  The pattern matching code doesn't understand
     CTLESC quoting CTLESC and CTLNUL so we use the dequoted variable
     values passed in (VT_VARIABLE) so the pattern substitution
     code works right.  We need to requote special chars after
     we're done for VT_VARIABLE and VT_ARRAYMEMBER, and for the
     other cases if QUOTED == 0, since the posparams and arrays
     indexed by * or @ do special things when QUOTED != 0. */

  switch (vtype)
    {
    case VT_VARIABLE:
    case VT_ARRAYMEMBER:
      temp = pat_subst (val, p, rep, mflags);
      if (vtype == VT_VARIABLE)
    FREE (val);
      if (temp)
    {
      tt = quote_escapes (temp);
      free (temp);
      temp = tt;
    }
      break;
    case VT_POSPARMS:
      temp = pos_params_pat_subst (val, p, rep, mflags);
      if (temp && (mflags & MATCH_QUOTED) == 0)
    {
      tt = quote_escapes (temp);
      free (temp);
      temp = tt;
    }
      break;
#if defined (ARRAY_VARS)
    case VT_ARRAYVAR:
      temp = array_patsub (array_cell (v), p, rep, mflags);
      if (temp && (mflags & MATCH_QUOTED) == 0)
    {
      tt = quote_escapes (temp);
      free (temp);
      temp = tt;
    }
      break;
#endif
    }

  FREE (pat);
  FREE (rep);
  free (lpatsub);

  return temp;
}

/****************************************************************/
/*                              */
/*  Functions to perform parameter expansion on a string    */
/*                              */
/****************************************************************/

/* ${[#][!]name[[:]#[#]%[%]-=?+[word][:e1[:e2]]]} */
static char *
parameter_brace_expand (string, indexp, quoted, quoted_dollar_atp, contains_dollar_at)
     char *string;
     int *indexp, quoted, *quoted_dollar_atp, *contains_dollar_at;
{
  int check_nullness, var_is_set, var_is_null, var_is_special;
  int want_substring, want_indir, want_patsub;
  char *name, *value, *temp, *temp1;
  int t_index, sindex, c;
  intmax_t number;

  value = (char *)NULL;
  var_is_set = var_is_null = var_is_special = check_nullness = 0;
  want_substring = want_indir = want_patsub = 0;

  sindex = *indexp;
  t_index = ++sindex;
  name = string_extract (string, &t_index, "#%:-=?+/}", EX_VARNAME);

  /* If the name really consists of a special variable, then make sure
     that we have the entire name.  We don't allow indirect references
     to special variables except `#', `?', `@' and `*'. */
  if ((sindex == t_index &&
    (string[t_index] == '-' ||
     string[t_index] == '?' ||
     string[t_index] == '#')) ||
      (sindex == t_index - 1 && string[sindex] == '!' &&
    (string[t_index] == '#' ||
     string[t_index] == '?' ||
     string[t_index] == '@' ||
     string[t_index] == '*')))
    {
      t_index++;
      free (name);
      temp1 = string_extract (string, &t_index, "#%:-=?+/}", 0);
      name = (char *)xmalloc (3 + (strlen (temp1)));
      *name = string[sindex];
      if (string[sindex] == '!')
    {
      /* indirect reference of $#, $?, $@, or $* */
      name[1] = string[sindex + 1];
      strcpy (name + 2, temp1);
    }
      else  
    strcpy (name + 1, temp1);
      free (temp1);
    }
  sindex = t_index;

  /* Find out what character ended the variable name.  Then
     do the appropriate thing. */
  if (c = string[sindex])
    sindex++;

  /* If c is followed by one of the valid parameter expansion
     characters, move past it as normal.  If not, assume that
     a substring specification is being given, and do not move
     past it. */
  if (c == ':' && VALID_PARAM_EXPAND_CHAR (string[sindex]))
    {
      check_nullness++;
      if (c = string[sindex])
    sindex++;
    }
  else if (c == ':' && string[sindex] != RBRACE)
    want_substring = 1;
  else if (c == '/' && string[sindex] != RBRACE)
    want_patsub = 1;

  /* Catch the valid and invalid brace expressions that made it through the
     tests above. */
  /* ${#-} is a valid expansion and means to take the length of $-.
     Similarly for ${#?} and ${##}... */
  if (name[0] == '#' && name[1] == '\0' && check_nullness == 0 &&
    VALID_SPECIAL_LENGTH_PARAM (c) && string[sindex] == RBRACE)
    {
      name = (char *)xrealloc (name, 3);
      name[1] = c;
      name[2] = '\0';
      c = string[sindex++];
    }

  /* ...but ${#%}, ${#:}, ${#=}, ${#+}, and ${#/} are errors. */
  if (name[0] == '#' && name[1] == '\0' && check_nullness == 0 &&
    member (c, "%:=+/") && string[sindex] == RBRACE)
    {
      temp = (char *)NULL;
      goto bad_substitution;
    }

  /* Indirect expansion begins with a `!'.  A valid indirect expansion is
     either a variable name, one of the positional parameters or a special
     variable that expands to one of the positional parameters. */
  want_indir = *name == '!' &&
    (legal_variable_starter ((unsigned char)name[1]) || DIGIT (name[1])
                    || VALID_INDIR_PARAM (name[1]));

  /* Determine the value of this variable. */

  /* Check for special variables, directly referenced. */
  if (SPECIAL_VAR (name, want_indir))
    var_is_special++;

  /* Check for special expansion things, like the length of a parameter */
  if (*name == '#' && name[1])
    {
      /* If we are not pointing at the character just after the
     closing brace, then we haven't gotten all of the name.
     Since it begins with a special character, this is a bad
     substitution.  Also check NAME for validity before trying
     to go on. */
      if (string[sindex - 1] != RBRACE || (valid_length_expression (name) == 0))
    {
      temp = (char *)NULL;
      goto bad_substitution;
    }

      number = parameter_brace_expand_length (name);
      free (name);

      *indexp = sindex;
      return ((number < 0) ? &expand_param_error : itos (number));
    }

  /* ${@} is identical to $@. */
  if (name[0] == '@' && name[1] == '\0')
    {
      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
    *quoted_dollar_atp = 1;

      if (contains_dollar_at)
    *contains_dollar_at = 1;
    }

  /* Process ${PREFIX*} expansion. */
  if (want_indir && string[sindex - 1] == RBRACE &&
      (string[sindex - 2] == '*' || string[sindex - 2] == '@') &&
      legal_variable_starter ((unsigned char) name[1]))
    {
      char **x;
      WORD_LIST *xlist;

      temp1 = savestring (name + 1);
      number = strlen (temp1);
      temp1[number - 1] = '\0';
      x = all_variables_matching_prefix (temp1);
      xlist = strvec_to_word_list (x, 0, 0);
      if (string[sindex - 2] == '*')
    temp = string_list_dollar_star (xlist);
      else
    {
      temp = string_list_dollar_at (xlist, quoted);
      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
        *quoted_dollar_atp = 1;
      if (contains_dollar_at)
        *contains_dollar_at = 1;
    }
      free (x);
      free (xlist);
      free (temp1);
      *indexp = sindex;
      return (temp);
    }
      
  /* Make sure that NAME is valid before trying to go on. */
  if (valid_brace_expansion_word (want_indir ? name + 1 : name,
                    var_is_special) == 0)
    {
      temp = (char *)NULL;
      goto bad_substitution;
    }

  if (want_indir)
    temp = parameter_brace_expand_indir (name + 1, var_is_special, quoted);
  else
    temp = parameter_brace_expand_word (name, var_is_special, quoted);

#if defined (ARRAY_VARS)
  if (valid_array_reference (name))
    {
      temp1 = xstrchr (name, '[');
      if (temp1 && temp1[1] == '@' && temp1[2] == ']')
    {
      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
        *quoted_dollar_atp = 1;
      if (contains_dollar_at)
        *contains_dollar_at = 1;
    }   /* [ */
      /* ${array[*]}, when unquoted, should be treated like ${array[@]},
     which should result in separate words even when IFS is unset. */
      if (temp1 && temp1[1] == '*' && temp1[2] == ']' && quoted == 0)
    {
      if (contains_dollar_at)
        *contains_dollar_at = 1;
    }
    }
#endif

  var_is_set = temp != (char *)0;
  var_is_null = check_nullness && (var_is_set == 0 || *temp == 0);

  /* Get the rest of the stuff inside the braces. */
  if (c && c != RBRACE)
    {
      /* Extract the contents of the ${ ... } expansion
     according to the Posix.2 rules. */
      value = extract_dollar_brace_string (string, &sindex, quoted, 0);
      if (string[sindex] == RBRACE)
    sindex++;
      else
    goto bad_substitution;
    }
  else
    value = (char *)NULL;

  *indexp = sindex;

  /* If this is a substring spec, process it and add the result. */
  if (want_substring)
    {
      temp1 = parameter_brace_substring (name, temp, value, quoted);
      FREE (name);
      FREE (value);
      FREE (temp);
      return (temp1);
    }
  else if (want_patsub)
    {
      temp1 = parameter_brace_patsub (name, temp, value, quoted);
      FREE (name);
      FREE (value);
      FREE (temp);
      return (temp1);
    }

  /* Do the right thing based on which character ended the variable name. */
  switch (c)
    {
    default:
    case '\0':
    bad_substitution:
      report_error ("%s: bad substitution", string ? string : "??");
      FREE (value);
      FREE (temp);
      free (name);
      return &expand_param_error;

    case RBRACE:
      if (var_is_set == 0 && unbound_vars_is_error)
    {
      err_unboundvar (name);
      FREE (value);
      FREE (temp);
      free (name);
      last_command_exit_value = EXECUTION_FAILURE;
      return (interactive_shell ? &expand_param_error : &expand_param_fatal);
    }
      break;

    case '#':   /* ${param#[#]pattern} */
    case '%':   /* ${param%[%]pattern} */
      if (value == 0 || *value == '\0' || temp == 0 || *temp == '\0')
    {
      FREE (value);
      break;
    }
      temp1 = parameter_brace_remove_pattern (name, temp, value, c, quoted);
      free (temp);
      free (value);
      temp = temp1;
      break;

    case '-':
    case '=':
    case '?':
    case '+':
      if (var_is_set && var_is_null == 0)
    {
      /* If the operator is `+', we don't want the value of the named
         variable for anything, just the value of the right hand side. */

      if (c == '+')
        {
          /* XXX -- if we're double-quoted and the named variable is "$@",
            we want to turn off any special handling of "$@" --
            we're not using it, so whatever is on the rhs applies. */
          if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
        *quoted_dollar_atp = 0;
          if (contains_dollar_at)
        *contains_dollar_at = 0;

          FREE (temp);
          if (value)
        {
          temp = parameter_brace_expand_rhs (name, value, c,
                             quoted,
                             quoted_dollar_atp,
                             contains_dollar_at);
          free (value);
        }
          else
        temp = (char *)NULL;
        }
      else
        {
          FREE (value);
        }
      /* Otherwise do nothing; just use the value in TEMP. */
    }
      else  /* VAR not set or VAR is NULL. */
    {
      FREE (temp);
      temp = (char *)NULL;
      if (c == '=' && var_is_special)
        {
          report_error ("$%s: cannot assign in this way", name);
          free (name);
          free (value);
          return &expand_param_error;
        }
      else if (c == '?')
        {
          parameter_brace_expand_error (name, value);
          return (interactive_shell ? &expand_param_error : &expand_param_fatal);
        }
      else if (c != '+')
        {
          /* XXX -- if we're double-quoted and the named variable is "$@",
            we want to turn off any special handling of "$@" --
            we're not using it, so whatever is on the rhs applies. */
          if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp)
        *quoted_dollar_atp = 0;
          if (contains_dollar_at)
        *contains_dollar_at = 0;

          temp = parameter_brace_expand_rhs (name, value, c, quoted,
                         quoted_dollar_atp,
                         contains_dollar_at);
        }
      free (value);
    }

      break;
    }
  free (name);
  return (temp);
}

/* Expand a single ${xxx} expansion.  The braces are optional.  When
   the braces are used, parameter_brace_expand() does the work,
   possibly calling param_expand recursively. */
static char *
param_expand (string, sindex, quoted, expanded_something,
          contains_dollar_at, quoted_dollar_at_p, had_quoted_null_p,
          pflags)
     char *string;
     int *sindex, quoted, *expanded_something, *contains_dollar_at;
     int *quoted_dollar_at_p, *had_quoted_null_p, pflags;
{
  char *temp, *temp1, uerror[3];
  int zindex, t_index, expok;
  unsigned char c;
  intmax_t number;
  SHELL_VAR *var;
  WORD_LIST *list;

  zindex = *sindex;
  c = string[++zindex];

  temp = (char *)NULL;

  /* Do simple cases first. Switch on what follows '$'. */
  switch (c)
    {
    /* $0 .. $9? */
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      temp1 = dollar_vars[TODIGIT (c)];
      if (unbound_vars_is_error && temp1 == (char *)NULL)
    {
      uerror[0] = '$';
      uerror[1] = c;
      uerror[2] = '\0';
      err_unboundvar (uerror);
      last_command_exit_value = EXECUTION_FAILURE;
      return (interactive_shell ? &expand_param_error : &expand_param_fatal);
    }
      temp = temp1 ? quote_escapes (temp1) : (char *)NULL;
      break;

    /* $$ -- pid of the invoking shell. */
    case '$':
      temp = itos (dollar_dollar_pid);
      break;

    /* $# -- number of positional parameters. */
    case '#':
      temp = itos (number_of_args ());
      break;

    /* $? -- return value of the last synchronous command. */
    case '?':
      temp = itos (last_command_exit_value);
      break;

    /* $- -- flags supplied to the shell on invocation or by `set'. */
    case '-':
      temp = which_set_flags ();
      break;

      /* $! -- Pid of the last asynchronous command. */
    case '!':
      /* If no asynchronous pids have been created, expand to nothing.
     If `set -u' has been executed, and no async processes have
     been created, this is an expansion error. */
      if (last_asynchronous_pid == NO_PID)
    {
      if (expanded_something)
        *expanded_something = 0;
      temp = (char *)NULL;
      if (unbound_vars_is_error)
        {
          uerror[0] = '$';
          uerror[1] = c;
          uerror[2] = '\0';
          err_unboundvar (uerror);
          last_command_exit_value = EXECUTION_FAILURE;
          return (interactive_shell ? &expand_param_error : &expand_param_fatal);
        }
    }
      else
    temp = itos (last_asynchronous_pid);
      break;

    /* The only difference between this and $@ is when the arg is quoted. */
    case '*':       /* `$*' */
      list = list_rest_of_args ();

      /* If there are no command-line arguments, this should just
     disappear if there are other characters in the expansion,
     even if it's quoted. */
      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && list == 0)
    temp = (char *)NULL;
      else if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
    {
      /* If we have "$*" we want to make a string of the positional
         parameters, separated by the first character of $IFS, and
         quote the whole string, including the separators.  If IFS
         is unset, the parameters are separated by ' '; if $IFS is
         null, the parameters are concatenated. */
      temp = string_list_dollar_star (list);
      temp1 = quote_string (temp);
      free (temp);
      temp = temp1;
    }
      else
    {
      /* If the $* is not quoted it is identical to $@ */
      temp = string_list_dollar_at (list, quoted);
      if (expand_no_split_dollar_star == 0 && contains_dollar_at)
        *contains_dollar_at = 1;
    }

      dispose_words (list);
      break;

    /* When we have "$@" what we want is "$1" "$2" "$3" ... This
       means that we have to turn quoting off after we split into
       the individually quoted arguments so that the final split
       on the first character of $IFS is still done.  */
    case '@':       /* `$@' */
      list = list_rest_of_args ();

      /* We want to flag the fact that we saw this.  We can't turn
     off quoting entirely, because other characters in the
     string might need it (consider "\"$@\""), but we need some
     way to signal that the final split on the first character
     of $IFS should be done, even though QUOTED is 1. */
      if (quoted_dollar_at_p && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
    *quoted_dollar_at_p = 1;
      if (contains_dollar_at)
    *contains_dollar_at = 1;

      /* We want to separate the positional parameters with the first
     character of $IFS in case $IFS is something other than a space.
     We also want to make sure that splitting is done no matter what --
     according to POSIX.2, this expands to a list of the positional
     parameters no matter what IFS is set to. */
      temp = string_list_dollar_at (list, quoted);

      dispose_words (list);
      break;

    case LBRACE:
      temp = parameter_brace_expand (string, &zindex, quoted,
                     quoted_dollar_at_p,
                     contains_dollar_at);
      if (temp == &expand_param_error || temp == &expand_param_fatal)
    return (temp);

      /* XXX */
      /* Quoted nulls should be removed if there is anything else
     in the string. */
      /* Note that we saw the quoted null so we can add one back at
     the end of this function if there are no other characters
     in the string, discard TEMP, and go on.  The exception to
     this is when we have "${@}" and $1 is '', since $@ needs
     special handling. */
      if (temp && QUOTED_NULL (temp))
    {
      if (had_quoted_null_p)
        *had_quoted_null_p = 1;
      if (*quoted_dollar_at_p == 0)
        {
          free (temp);
          temp = (char *)NULL;
        }
        
    }

      goto return0;

    /* Do command or arithmetic substitution. */
    case LPAREN:
      /* We have to extract the contents of this paren substitution. */
      t_index = zindex + 1;
      temp = extract_command_subst (string, &t_index);
      zindex = t_index;

      /* For Posix.2-style `$(( ))' arithmetic substitution,
     extract the expression and pass it to the evaluator. */
      if (temp && *temp == LPAREN)
    {
      char *temp2;
      temp1 = temp + 1;
      temp2 = savestring (temp1);
      t_index = strlen (temp2) - 1;

      if (temp2[t_index] != RPAREN)
        {
          free (temp2);
          goto comsub;
        }

      /* Cut off ending `)' */
      temp2[t_index] = '\0';

      /* Expand variables found inside the expression. */
      temp1 = expand_string_if_necessary (temp2, Q_DOUBLE_QUOTES, expand_string);
      free (temp2);

arithsub:
      /* No error messages. */
      this_command_name = (char *)NULL;
      number = evalexp (temp1, &expok);
      free (temp);
      free (temp1);
      if (expok == 0)
        {
          if (interactive_shell == 0 && posixly_correct)
        {
          last_command_exit_value = EXECUTION_FAILURE;
          return (&expand_param_fatal);
        }
          else
        return (&expand_param_error);
        }
      temp = itos (number);
      break;
    }

comsub:
      temp1 = command_substitute (temp, quoted);
      FREE (temp);
      temp = temp1;
      break;

    /* Do POSIX.2d9-style arithmetic substitution.  This will probably go
       away in a future bash release. */
    case '[':
      /* Extract the contents of this arithmetic substitution. */
      t_index = zindex + 1;
      temp = extract_arithmetic_subst (string, &t_index);
      zindex = t_index;

       /* Do initial variable expansion. */
      temp1 = expand_string_if_necessary (temp, Q_DOUBLE_QUOTES, expand_string);

      goto arithsub;

    default:
      /* Find the variable in VARIABLE_LIST. */
      temp = (char *)NULL;

      for (t_index = zindex; (c = string[zindex]) && legal_variable_char (c); zindex++)
    ;
      temp1 = (zindex > t_index) ? substring (string, t_index, zindex) : (char *)NULL;

      /* If this isn't a variable name, then just output the `$'. */
      if (temp1 == 0 || *temp1 == '\0')
    {
      FREE (temp1);
      temp = (char *)xmalloc (2);
      temp[0] = '$';
      temp[1] = '\0';
      if (expanded_something)
        *expanded_something = 0;
      goto return0;
    }

      /* If the variable exists, return its value cell. */
      var = find_variable (temp1);

      if (var && invisible_p (var) == 0 && var_isset (var))
    {
#if defined (ARRAY_VARS)
      if (array_p (var))
        {
          temp = array_reference (array_cell (var), 0);
          if (temp)
        temp = quote_escapes (temp);
        }
      else
#endif
      temp = quote_escapes (value_cell (var));
      free (temp1);

      goto return0;
    }

      temp = (char *)NULL;

      if (unbound_vars_is_error)
    err_unboundvar (temp1);
      else
    {
      free (temp1);
      goto return0;
    }

      free (temp1);
      last_command_exit_value = EXECUTION_FAILURE;
      return ((unbound_vars_is_error && interactive_shell == 0)
        ? &expand_param_fatal
        : &expand_param_error);
    }

  if (string[zindex])
    zindex++;

return0:
  *sindex = zindex;
  return (temp);
}

/* Make a word list which is the result of parameter and variable
   expansion, command substitution, arithmetic substitution, and
   quote removal of WORD.  Return a pointer to a WORD_LIST which is
   the result of the expansion.  If WORD contains a null word, the
   word list returned is also null.

   QUOTED contains flag values defined in shell.h.

   ISEXP is used to tell expand_word_internal that the word should be
   treated as the result of an expansion.  This has implications for
   how IFS characters in the word are treated.

   CONTAINS_DOLLAR_AT and EXPANDED_SOMETHING are return values; when non-null
   they point to an integer value which receives information about expansion.
   CONTAINS_DOLLAR_AT gets non-zero if WORD contained "$@", else zero.
   EXPANDED_SOMETHING get non-zero if WORD contained any parameter expansions,
   else zero.

   This only does word splitting in the case of $@ expansion.  In that
   case, we split on ' '. */

/* Values for the local variable quoted_state. */
#define UNQUOTED     0
#define PARTIALLY_QUOTED 1
#define WHOLLY_QUOTED    2

static WORD_LIST *
expand_word_internal (word, quoted, isexp, contains_dollar_at, expanded_something)
     WORD_DESC *word;
     int quoted, isexp;
     int *contains_dollar_at;
     int *expanded_something;
{
  WORD_LIST *list;
  WORD_DESC *tword;

  /* The intermediate string that we build while expanding. */
  char *istring;

  /* The current size of the above object. */
  int istring_size;

  /* Index into ISTRING. */
  int istring_index;

  /* Temporary string storage. */
  char *temp, *temp1;

  /* The text of WORD. */
  register char *string;

  /* The size of STRING. */
  size_t string_size;

  /* The index into STRING. */
  int sindex;

  /* This gets 1 if we see a $@ while quoted. */
  int quoted_dollar_at;

  /* One of UNQUOTED, PARTIALLY_QUOTED, or WHOLLY_QUOTED, depending on
     whether WORD contains no quoting characters, a partially quoted
     string (e.g., "xx"ab), or is fully quoted (e.g., "xxab"). */
  int quoted_state;

  int had_quoted_null;
  int has_dollar_at;
  int tflag;

  register unsigned char c; /* Current character. */
  int t_index;          /* For calls to string_extract_xxx. */

  char twochars[2];

  DECLARE_MBSTATE;

  istring = (char *)xmalloc (istring_size = DEFAULT_INITIAL_ARRAY_SIZE);
  istring[istring_index = 0] = '\0';
  quoted_dollar_at = had_quoted_null = has_dollar_at = 0;
  quoted_state = UNQUOTED;

  string = word->word;
  if (string == 0)
    goto finished_with_string;
  string_size = strlen (string);

  if (contains_dollar_at)
    *contains_dollar_at = 0;

  /* Begin the expansion. */

  for (sindex = 0; ;)
    {
      c = string[sindex];

      /* Case on toplevel character. */
      switch (c)
    {
    case '\0':
      goto finished_with_string;

    case CTLESC:
      sindex++;
#if HANDLE_MULTIBYTE
      if (MB_CUR_MAX > 1 && string[sindex])
        {
          int i;
          mbstate_t state_bak;
          size_t mblength;

          state_bak = state;
          mblength = mbrlen (string + sindex, string_size - sindex, &state);
          if (mblength == (size_t)-1 || mblength == (size_t)-2)
        {
          state = state_bak;
          mblength = 1;
        }
          if (mblength < 1)
        mblength = 1;
          temp = (char *)xmalloc (mblength + 2);
          temp[0] = CTLESC;
          for (i = 0; i < mblength; i++)
        temp[i+1] = string[sindex++];
          temp[mblength + 1] = '\0';

          goto add_string;
        }
      else
#endif
        {
          temp = (char *)xmalloc (3);
          temp[0] = CTLESC;
          temp[1] = c = string[sindex];
          temp[2] = '\0';
        }

dollar_add_string:
      if (string[sindex])
        sindex++;

add_string:
      if (temp)
        {
          istring = sub_append_string (temp, istring, &istring_index, &istring_size);
          temp = (char *)0;
        }

      break;

#if defined (PROCESS_SUBSTITUTION)
      /* Process substitution. */
    case '<':
    case '>':
      {
        if (string[++sindex] != LPAREN || (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || posixly_correct)
          {
        sindex--;   /* add_character: label increments sindex */
        goto add_character;
          }
        else
          t_index = sindex + 1; /* skip past both '<' and LPAREN */

        temp1 = extract_process_subst (string, (c == '<') ? "<(" : ">(", &t_index); /*))*/
        sindex = t_index;

        /* If the process substitution specification is `<()', we want to
           open the pipe for writing in the child and produce output; if
           it is `>()', we want to open the pipe for reading in the child
           and consume input. */
        temp = temp1 ? process_substitute (temp1, (c == '>')) : (char *)0;

        FREE (temp1);

        goto dollar_add_string;
      }
#endif /* PROCESS_SUBSTITUTION */

    case '$':
      if (expanded_something)
        *expanded_something = 1;

      has_dollar_at = 0;
      temp = param_expand (string, &sindex, quoted, expanded_something,
                   &has_dollar_at, &quoted_dollar_at,
                   &had_quoted_null, 0);

      if (temp == &expand_param_error || temp == &expand_param_fatal)
        {
          free (string);
          free (istring);
          return ((temp == &expand_param_error) ? &expand_word_error
                            : &expand_word_fatal);
        }
      if (contains_dollar_at && has_dollar_at)
        *contains_dollar_at = 1;
      goto add_string;
      break;

    case '`':       /* Backquoted command substitution. */
      {
        sindex++;

        if (expanded_something)
          *expanded_something = 1;

        temp = string_extract (string, &sindex, "`", 0);
        de_backslash (temp);
        temp1 = command_substitute (temp, quoted);
        FREE (temp);
        temp = temp1;
        goto dollar_add_string;
      }

    case '\\':
      if (string[sindex + 1] == '\n')
        {
          sindex += 2;
          continue;
        }

      c = string[++sindex];

      if (quoted & Q_HERE_DOCUMENT)
        tflag = CBSHDOC;
      else if (quoted & Q_DOUBLE_QUOTES)
        tflag = CBSDQUOTE;
      else
        tflag = 0;

      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && ((sh_syntaxtab[c] & tflag) == 0))
        {
          SCOPY_CHAR_I (twochars, '\\', c, string, sindex, string_size);
        }
      else if (c == 0)
        {
          c = CTLNUL;
          sindex--;     /* add_character: label increments sindex */
          goto add_character;
        }
      else
        {
          SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size);
        }

      sindex++;
add_twochars:
      /* BEFORE jumping here, we need to increment sindex if appropriate */
      RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size,
                  DEFAULT_ARRAY_SIZE);
      istring[istring_index++] = twochars[0];
      istring[istring_index++] = twochars[1];
      istring[istring_index] = '\0';

      break;

    case '"':
#if 0
      if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT|Q_PATQUOTE))
#else
      if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
#endif
        goto add_character;

      t_index = ++sindex;
      temp = string_extract_double_quoted (string, &sindex, 0);

      /* If the quotes surrounded the entire string, then the
         whole word was quoted. */
      quoted_state = (t_index == 1 && string[sindex] == '\0')
                ? WHOLLY_QUOTED
                : PARTIALLY_QUOTED;

      if (temp && *temp)
        {
          tword = make_word (temp);     /* XXX */
          free (temp);
          temp = (char *)NULL;

          has_dollar_at = 0;
          list = expand_word_internal (tword, Q_DOUBLE_QUOTES, 0, &has_dollar_at, (int *)NULL);

          if (list == &expand_word_error || list == &expand_word_fatal)
        {
          free (istring);
          free (string);
          /* expand_word_internal has already freed temp_word->word
             for us because of the way it prints error messages. */
          tword->word = (char *)NULL;
          dispose_word (tword);
          return list;
        }

          dispose_word (tword);

          /* "$@" (a double-quoted dollar-at) expands into nothing,
         not even a NULL word, when there are no positional
         parameters. */
          if (list == 0 && has_dollar_at)
        {
          quoted_dollar_at++;
          break;
        }

          /* If we get "$@", we know we have expanded something, so we
         need to remember it for the final split on $IFS.  This is
         a special case; it's the only case where a quoted string
         can expand into more than one word.  It's going to come back
         from the above call to expand_word_internal as a list with
         a single word, in which all characters are quoted and
         separated by blanks.  What we want to do is to turn it back
         into a list for the next piece of code. */
          if (list)
        dequote_list (list);

          if (has_dollar_at)
        {
          quoted_dollar_at++;
          if (contains_dollar_at)
            *contains_dollar_at = 1;
          if (expanded_something)
            *expanded_something = 1;
        }
        }
      else
        {
          /* What we have is "".  This is a minor optimization. */
          FREE (temp);
          list = (WORD_LIST *)NULL;
        }

      /* The code above *might* return a list (consider the case of "$@",
         where it returns "$1", "$2", etc.).  We can't throw away the
         rest of the list, and we have to make sure each word gets added
         as quoted.  We test on tresult->next:  if it is non-NULL, we
         quote the whole list, save it to a string with string_list, and
         add that string. We don't need to quote the results of this
         (and it would be wrong, since that would quote the separators
         as well), so we go directly to add_string. */
      if (list)
        {
          if (list->next)
        {
          /* Testing quoted_dollar_at makes sure that "$@" is
             split correctly when $IFS does not contain a space. */
          temp = quoted_dollar_at
                ? string_list_dollar_at (list, Q_DOUBLE_QUOTES)
                : string_list (quote_list (list));
          dispose_words (list);
          goto add_string;
        }
          else
        {
          temp = savestring (list->word->word);
          dispose_words (list);
#if 1
          /* If the string is not a quoted null string, we want
             to remove any embedded unquoted CTLNUL characters.
             We do not want to turn quoted null strings back into
             the empty string, though.  We do this because we
             want to remove any quoted nulls from expansions that
             contain other characters.  For example, if we have
             x"$*"y or "x$*y" and there are no positional parameters,
             the $* should expand into nothing. */
          /* HOWEVER, this fails if the string contains a literal
             CTLNUL or CTLNUL is contained in the (non-null) expansion
             of some variable.  I'm not sure what to do about this
             yet. */
          if (QUOTED_NULL (temp) == 0)
            remove_quoted_nulls (temp); /* XXX */
#endif
        }
        }
      else
        temp = (char *)NULL;

      /* We do not want to add quoted nulls to strings that are only
         partially quoted; we can throw them away. */
      if (temp == 0 && quoted_state == PARTIALLY_QUOTED)
        continue;

    add_quoted_string:

      if (temp)
        {
          temp1 = temp;
          temp = quote_string (temp);
          free (temp1);
          goto add_string;
        }
      else
        {
          /* Add NULL arg. */
          c = CTLNUL;
          sindex--;     /* add_character: label increments sindex */
          goto add_character;
        }

      /* break; */

    case '\'':
#if 0
      if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT|Q_PATQUOTE))
#else
      if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
#endif
        goto add_character;

      t_index = ++sindex;
      temp = string_extract_single_quoted (string, &sindex);

      /* If the entire STRING was surrounded by single quotes,
         then the string is wholly quoted. */
      quoted_state = (t_index == 1 && string[sindex] == '\0')
                ? WHOLLY_QUOTED
                : PARTIALLY_QUOTED;

      /* If all we had was '', it is a null expansion. */
      if (*temp == '\0')
        {
          free (temp);
          temp = (char *)NULL;
        }
      else
        remove_quoted_escapes (temp);   /* ??? */

      /* We do not want to add quoted nulls to strings that are only
         partially quoted; such nulls are discarded. */
      if (temp == 0 && (quoted_state == PARTIALLY_QUOTED))
        continue;

      /* If we have a quoted null expansion, add a quoted NULL to istring. */
      if (temp == 0)
        {
          c = CTLNUL;
          sindex--;     /* add_character: label increments sindex */
          goto add_character;
        }
      else
        goto add_quoted_string;

      /* break; */

    default:
      /* This is the fix for " $@ " */
      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (isexp == 0 && isifs (c)))
        {
          if (string[sindex])   /* from old goto dollar_add_string */
        sindex++;
          if (c == 0)
        {
          c = CTLNUL;
          goto add_character;
        }
          else
        {
#if HANDLE_MULTIBYTE
          /* XXX - I'd like to use SCOPY_CHAR_I here. */
          if (MB_CUR_MAX > 1)
            {
              int i;
              mbstate_t state_bak;
              size_t mblength;

              sindex--;
              state_bak = state;
              mblength = mbrlen (string + sindex, string_size - sindex, &state);
              if (mblength == (size_t)-1 || mblength == (size_t)-2)
            {
              state = state_bak;
              mblength = 1;
            }
              if (mblength < 1)
            mblength = 1;

              temp = (char *)xmalloc (mblength + 2);
              temp[0] = CTLESC;
              for (i = 0; i < mblength; i++)
            temp[i + 1] = string[sindex++];
              temp[mblength + 1] = '\0';

              goto add_string;
            }
          else
#endif
            {
              twochars[0] = CTLESC;
              twochars[1] = c;
              goto add_twochars;
            }
        }
        }

      SADD_MBCHAR (temp, string, sindex, string_size);

    add_character:
      RESIZE_MALLOCED_BUFFER (istring, istring_index, 1, istring_size,
                  DEFAULT_ARRAY_SIZE);
      istring[istring_index++] = c;
      istring[istring_index] = '\0';

      /* Next character. */
      sindex++;
    }
    }

finished_with_string:
  /* OK, we're ready to return.  If we have a quoted string, and
     quoted_dollar_at is not set, we do no splitting at all; otherwise
     we split on ' '.  The routines that call this will handle what to
     do if nothing has been expanded. */

  /* Partially and wholly quoted strings which expand to the empty
     string are retained as an empty arguments.  Unquoted strings
     which expand to the empty string are discarded.  The single
     exception is the case of expanding "$@" when there are no
     positional parameters.  In that case, we discard the expansion. */

  /* Because of how the code that handles "" and '' in partially
     quoted strings works, we need to make ISTRING into a QUOTED_NULL
     if we saw quoting characters, but the expansion was empty.
     "" and '' are tossed away before we get to this point when
     processing partially quoted strings.  This makes "" and $xxx""
     equivalent when xxx is unset.  We also look to see whether we
     saw a quoted null from a ${} expansion and add one back if we
     need to. */

  /* If we expand to nothing and there were no single or double quotes
     in the word, we throw it away.  Otherwise, we return a NULL word.
     The single exception is for $@ surrounded by double quotes when
     there are no positional parameters.  In that case, we also throw
     the word away. */

  if (*istring == '\0')
    {
      if (quoted_dollar_at == 0 && (had_quoted_null || quoted_state == PARTIALLY_QUOTED))
    {
      istring[0] = CTLNUL;
      istring[1] = '\0';
      tword = make_bare_word (istring);
      list = make_word_list (tword, (WORD_LIST *)NULL);
      if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
        tword->flags |= W_QUOTED;
    }
      /* According to sh, ksh, and Posix.2, if a word expands into nothing
     and a double-quoted "$@" appears anywhere in it, then the entire
     word is removed. */
      else  if (quoted_state == UNQUOTED || quoted_dollar_at)
    list = (WORD_LIST *)NULL;
#if 0
      else
    {
      tword = make_bare_word (istring);
      list = make_word_list (tword, (WORD_LIST *)NULL);
      if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
        tword->flags |= W_QUOTED;
    }
#else
      else
    list = (WORD_LIST *)NULL;
#endif
    }
  else if (word->flags & W_NOSPLIT)
    {
      tword = make_bare_word (istring);
      list = make_word_list (tword, (WORD_LIST *)NULL);
      if (word->flags & W_ASSIGNMENT)
    tword->flags |= W_ASSIGNMENT;   /* XXX */
      if (word->flags & W_NOGLOB)
    tword->flags |= W_NOGLOB;   /* XXX */
      if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))
    tword->flags |= W_QUOTED;
    }
  else
    {
      char *ifs_chars;

      ifs_chars = (quoted_dollar_at || has_dollar_at) ? ifs_value : (char *)NULL;

      /* If we have $@, we need to split the results no matter what.  If
     IFS is unset or NULL, string_list_dollar_at has separated the
     positional parameters with a space, so we split on space (we have
     set ifs_chars to " \t\n" above if ifs is unset).  If IFS is set,
     string_list_dollar_at has separated the positional parameters
     with the first character of $IFS, so we split on $IFS. */
      if (has_dollar_at && ifs_chars)
    list = list_string (istring, *ifs_chars ? ifs_chars : " ", 1);
      else
    {
      tword = make_bare_word (istring);
      list = make_word_list (tword, (WORD_LIST *)NULL);
      if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || (quoted_state == WHOLLY_QUOTED))
        tword->flags |= W_QUOTED;
      if (word->flags & W_ASSIGNMENT)
        tword->flags |= W_ASSIGNMENT;
      if (word->flags & W_NOGLOB)
        tword->flags |= W_NOGLOB;
    }
    }

  free (istring);
  return (list);
}

/* **************************************************************** */
/*                                  */
/*         Functions for Quote Removal              */
/*                                  */
/* **************************************************************** */

/* Perform quote removal on STRING.  If QUOTED > 0, assume we are obeying the
   backslash quoting rules for within double quotes or a here document. */
char *
string_quote_removal (string, quoted)
     char *string;
     int quoted;
{
  size_t slen;
  char *r, *result_string, *temp, *send;
  int sindex, tindex, dquote;
  unsigned char c;
  DECLARE_MBSTATE;

  /* The result can be no longer than the original string. */
  slen = strlen (string);
  send = string + slen;

  r = result_string = (char *)xmalloc (slen + 1);

  for (dquote = sindex = 0; c = string[sindex];)
    {
      switch (c)
    {
    case '\\':
      c = string[++sindex];
      if (((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote) && (sh_syntaxtab[c] & CBSDQUOTE) == 0)
        *r++ = '\\';
      /* FALLTHROUGH */

    default:
      SCOPY_CHAR_M (r, string, send, sindex);
      break;

    case '\'':
      if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote)
        {
          *r++ = c;
          sindex++;
          break;
        }
      tindex = sindex + 1;
      temp = string_extract_single_quoted (string, &tindex);
      if (temp)
        {
          strcpy (r, temp);
          r += strlen (r);
          free (temp);
        }
      sindex = tindex;
      break;

    case '"':
      dquote = 1 - dquote;
      sindex++;
      break;
    }
    }
    *r = '\0';
    return (result_string);
}

#if 0
/* UNUSED */
/* Perform quote removal on word WORD.  This allocates and returns a new
   WORD_DESC *. */
WORD_DESC *
word_quote_removal (word, quoted)
     WORD_DESC *word;
     int quoted;
{
  WORD_DESC *w;
  char *t;

  t = string_quote_removal (word->word, quoted);
  w = make_bare_word (t);
  free (t);
  return (w);
}

/* Perform quote removal on all words in LIST.  If QUOTED is non-zero,
   the members of the list are treated as if they are surrounded by
   double quotes.  Return a new list, or NULL if LIST is NULL. */
WORD_LIST *
word_list_quote_removal (list, quoted)
     WORD_LIST *list;
     int quoted;
{
  WORD_LIST *result, *t, *tresult;

  for (t = list, result = (WORD_LIST *)NULL; t; t = t->next)
    {
      tresult = make_word_list (word_quote_removal (t->word, quoted), (WORD_LIST *)NULL);
      result = (WORD_LIST *) list_append (result, tresult);
    }
  return (result);
}
#endif

/*******************************************
 *                     *
 *    Functions to perform word splitting  *
 *                     *
 *******************************************/

void
setifs (v)
     SHELL_VAR *v;
{
  char *t;
  unsigned char uc;

  ifs_var = v;
  ifs_value = v ? value_cell (v) : " \t\n";

  /* Should really merge ifs_cmap with sh_syntaxtab. */
  memset (ifs_cmap, '\0', sizeof (ifs_cmap));
  for (t = ifs_value ; t && *t; t++)
    {
      uc = *t;
      ifs_cmap[uc] = 1;
    }

  ifs_firstc = ifs_value ? *ifs_value : 0;
}

char *
getifs ()
{
  return ifs_value;
}

/* This splits a single word into a WORD LIST on $IFS, but only if the word
   is not quoted.  list_string () performs quote removal for us, even if we
   don't do any splitting. */
WORD_LIST *
word_split (w, ifs_chars)
     WORD_DESC *w;
     char *ifs_chars;
{
  WORD_LIST *result;

  if (w)
    {
      char *xifs;

      xifs = ((w->flags & W_QUOTED) || ifs_chars == 0) ? "" : ifs_chars;
      result = list_string (w->word, xifs, w->flags & W_QUOTED);
    }
  else
    result = (WORD_LIST *)NULL;

  return (result);
}

/* Perform word splitting on LIST and return the RESULT.  It is possible
   to return (WORD_LIST *)NULL. */
static WORD_LIST *
word_list_split (list)
     WORD_LIST *list;
{
  WORD_LIST *result, *t, *tresult;

  for (t = list, result = (WORD_LIST *)NULL; t; t = t->next)
    {
      tresult = word_split (t->word, ifs_value);
      result = (WORD_LIST *) list_append (result, tresult);
    }
  return (result);
}

/**************************************************
 *                        *
 *    Functions to expand an entire WORD_LIST     *
 *                        *
 **************************************************/

/* Put NLIST (which is a WORD_LIST * of only one element) at the front of
   ELIST, and set ELIST to the new list. */
#define PREPEND_LIST(nlist, elist) \
    do { nlist->next = elist; elist = nlist; } while (0)

/* Separate out any initial variable assignments from TLIST.  If set -k has
   been executed, remove all assignment statements from TLIST.  Initial
   variable assignments and other environment assignments are placed
   on SUBST_ASSIGN_VARLIST. */
static WORD_LIST *
separate_out_assignments (tlist)
     WORD_LIST *tlist;
{
  register WORD_LIST *vp, *lp;

  if (!tlist)
    return ((WORD_LIST *)NULL);

  if (subst_assign_varlist)
    dispose_words (subst_assign_varlist);   /* Clean up after previous error */

  subst_assign_varlist = (WORD_LIST *)NULL;
  vp = lp = tlist;

  /* Separate out variable assignments at the start of the command.
     Loop invariant: vp->next == lp
     Loop postcondition:
    lp = list of words left after assignment statements skipped
    tlist = original list of words
  */
  while (lp && (lp->word->flags & W_ASSIGNMENT))
    {
      vp = lp;
      lp = lp->next;
    }

  /* If lp != tlist, we have some initial assignment statements.
     We make SUBST_ASSIGN_VARLIST point to the list of assignment
     words and TLIST point to the remaining words.  */
  if (lp != tlist)
    {
      subst_assign_varlist = tlist;
      /* ASSERT(vp->next == lp); */
      vp->next = (WORD_LIST *)NULL; /* terminate variable list */
      tlist = lp;           /* remainder of word list */
    }

  /* vp == end of variable list */
  /* tlist == remainder of original word list without variable assignments */
  if (!tlist)
    /* All the words in tlist were assignment statements */
    return ((WORD_LIST *)NULL);

  /* ASSERT(tlist != NULL); */
  /* ASSERT((tlist->word->flags & W_ASSIGNMENT) == 0); */

  /* If the -k option is in effect, we need to go through the remaining
     words, separate out the assignment words, and place them on
     SUBST_ASSIGN_VARLIST. */
  if (place_keywords_in_env)
    {
      WORD_LIST *tp;    /* tp == running pointer into tlist */

      tp = tlist;
      lp = tlist->next;

      /* Loop Invariant: tp->next == lp */
      /* Loop postcondition: tlist == word list without assignment statements */
      while (lp)
    {
      if (lp->word->flags & W_ASSIGNMENT)
        {
          /* Found an assignment statement, add this word to end of
         subst_assign_varlist (vp). */
          if (!subst_assign_varlist)
        subst_assign_varlist = vp = lp;
          else
        {
          vp->next = lp;
          vp = lp;
        }

          /* Remove the word pointed to by LP from TLIST. */
          tp->next = lp->next;
          /* ASSERT(vp == lp); */
          lp->next = (WORD_LIST *)NULL;
          lp = tp->next;
        }
      else
        {
          tp = lp;
          lp = lp->next;
        }
    }
    }
  return (tlist);
}

#define WEXP_VARASSIGN  0x001
#define WEXP_BRACEEXP   0x002
#define WEXP_TILDEEXP   0x004
#define WEXP_PARAMEXP   0x008
#define WEXP_PATHEXP    0x010

/* All of the expansions, including variable assignments at the start of
   the list. */
#define WEXP_ALL    (WEXP_VARASSIGN|WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP|WEXP_PATHEXP)

/* All of the expansions except variable assignments at the start of
   the list. */
#define WEXP_NOVARS (WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP|WEXP_PATHEXP)

/* All of the `shell expansions': brace expansion, tilde expansion, parameter
   expansion, command substitution, arithmetic expansion, word splitting, and
   quote removal. */
#define WEXP_SHELLEXP   (WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP)

/* Take the list of words in LIST and do the various substitutions.  Return
   a new list of words which is the expanded list, and without things like
   variable assignments. */

WORD_LIST *
expand_words (list)
     WORD_LIST *list;
{
  return (expand_word_list_internal (list, WEXP_ALL));
}

/* Same as expand_words (), but doesn't hack variable or environment
   variables. */
WORD_LIST *
expand_words_no_vars (list)
     WORD_LIST *list;
{
  return (expand_word_list_internal (list, WEXP_NOVARS));
}

WORD_LIST *
expand_words_shellexp (list)
     WORD_LIST *list;
{
  return (expand_word_list_internal (list, WEXP_SHELLEXP));
}

static WORD_LIST *
glob_expand_word_list (tlist, eflags)
     WORD_LIST *tlist;
     int eflags;
{
  char **glob_array, *temp_string;
  register int glob_index;
  WORD_LIST *glob_list, *output_list, *disposables, *next;
  WORD_DESC *tword;

  output_list = disposables = (WORD_LIST *)NULL;
  glob_array = (char **)NULL;
  while (tlist)
    {
      /* For each word, either globbing is attempted or the word is
     added to orig_list.  If globbing succeeds, the results are
     added to orig_list and the word (tlist) is added to the list
     of disposable words.  If globbing fails and failed glob
     expansions are left unchanged (the shell default), the
     original word is added to orig_list.  If globbing fails and
     failed glob expansions are removed, the original word is
     added to the list of disposable words.  orig_list ends up
     in reverse order and requires a call to REVERSE_LIST to
     be set right.  After all words are examined, the disposable
     words are freed. */
      next = tlist->next;

      /* If the word isn't an assignment and contains an unquoted
     pattern matching character, then glob it. */
      if ((tlist->word->flags & W_NOGLOB) == 0 &&
      unquoted_glob_pattern_p (tlist->word->word))
    {
      glob_array = shell_glob_filename (tlist->word->word);

      /* Handle error cases.
         I don't think we should report errors like "No such file
         or directory".  However, I would like to report errors
         like "Read failed". */

      if (GLOB_FAILED (glob_array))
        {
          glob_array = (char **)xmalloc (sizeof (char *));
          glob_array[0] = (char *)NULL;
        }

      /* Dequote the current word in case we have to use it. */
      if (glob_array[0] == NULL)
        {
          temp_string = dequote_string (tlist->word->word);
          free (tlist->word->word);
          tlist->word->word = temp_string;
        }

      /* Make the array into a word list. */
      glob_list = (WORD_LIST *)NULL;
      for (glob_index = 0; glob_array[glob_index]; glob_index++)
        {
          tword = make_bare_word (glob_array[glob_index]);
          tword->flags |= W_GLOBEXP;    /* XXX */
          glob_list = make_word_list (tword, glob_list);
        }

      if (glob_list)
        {
          output_list = (WORD_LIST *)list_append (glob_list, output_list);
          PREPEND_LIST (tlist, disposables);
        }
      else if (allow_null_glob_expansion == 0)
        {
          /* Failed glob expressions are left unchanged. */
          PREPEND_LIST (tlist, output_list);
        }
      else
        {
          /* Failed glob expressions are removed. */
          PREPEND_LIST (tlist, disposables);
        }
    }
      else
    {
      /* Dequote the string. */
      temp_string = dequote_string (tlist->word->word);
      free (tlist->word->word);
      tlist->word->word = temp_string;
      PREPEND_LIST (tlist, output_list);
    }

      strvec_dispose (glob_array);
      glob_array = (char **)NULL;

      tlist = next;
    }

  if (disposables)
    dispose_words (disposables);

  if (output_list)
    output_list = REVERSE_LIST (output_list, WORD_LIST *);

  return (output_list);
}

#if defined (BRACE_EXPANSION)
static WORD_LIST *
brace_expand_word_list (tlist, eflags)
     WORD_LIST *tlist;
     int eflags;
{
  register char **expansions;
  char *temp_string;
  WORD_LIST *disposables, *output_list, *next;
  WORD_DESC *w;
  int eindex;

  for (disposables = output_list = (WORD_LIST *)NULL; tlist; tlist = next)
    {
      next = tlist->next;

      /* Only do brace expansion if the word has a brace character.  If
     not, just add the word list element to BRACES and continue.  In
     the common case, at least when running shell scripts, this will
     degenerate to a bunch of calls to `xstrchr', and then what is
     basically a reversal of TLIST into BRACES, which is corrected
     by a call to REVERSE_LIST () on BRACES when the end of TLIST
     is reached. */
      if (xstrchr (tlist->word->word, LBRACE))
    {
      expansions = brace_expand (tlist->word->word);

      for (eindex = 0; temp_string = expansions[eindex]; eindex++)
        {
          w = make_word (temp_string);
          /* If brace expansion didn't change the word, preserve
         the flags.  We may want to preserve the flags
         unconditionally someday -- XXX */
          if (STREQ (temp_string, tlist->word->word))
        w->flags = tlist->word->flags;
          output_list = make_word_list (w, output_list);
          free (expansions[eindex]);
        }
      free (expansions);

      /* Add TLIST to the list of words to be freed after brace
         expansion has been performed. */
      PREPEND_LIST (tlist, disposables);
    }
      else
    PREPEND_LIST (tlist, output_list);
    }

  if (disposables)
    dispose_words (disposables);

  if (output_list)
    output_list = REVERSE_LIST (output_list, WORD_LIST *);

  return (output_list);
}
#endif

static WORD_LIST *
shell_expand_word_list (tlist, eflags)
     WORD_LIST *tlist;
     int eflags;
{
  WORD_LIST *expanded, *orig_list, *new_list, *next, *temp_list;
  int expanded_something, has_dollar_at;
  char *temp_string;

  /* We do tilde expansion all the time.  This is what 1003.2 says. */
  new_list = (WORD_LIST *)NULL;
  for (orig_list = tlist; tlist; tlist = next)
    {
      temp_string = tlist->word->word;

      next = tlist->next;

      /* Posix.2 section 3.6.1 says that tildes following `=' in words
     which are not assignment statements are not expanded.  If the
     shell isn't in posix mode, though, we perform tilde expansion
     on `likely candidate' unquoted assignment statements (flags
     include W_ASSIGNMENT but not W_QUOTED).  A likely candidate
     contains an unquoted :~ or =~.  Something to think about: we
     now have a flag that says  to perform tilde expansion on arguments
     to `assignment builtins' like declare and export that look like
     assignment statements.  We now do tilde expansion on such words
     even in POSIX mode. */
      if (((tlist->word->flags & (W_ASSIGNMENT|W_QUOTED)) == W_ASSIGNMENT) &&
         (posixly_correct == 0 || (tlist->word->flags & W_TILDEEXP)) &&
         (unquoted_substring ("=~", temp_string) || unquoted_substring (":~", temp_string)))
    {
      tlist->word->word = bash_tilde_expand (temp_string, 1);
      free (temp_string);
    }
      else if (temp_string[0] == '~')
    {
      tlist->word->word = bash_tilde_expand (temp_string, 0);
      free (temp_string);
    }

      expanded_something = 0;
      expanded = expand_word_internal
    (tlist->word, 0, 0, &has_dollar_at, &expanded_something);

      if (expanded == &expand_word_error || expanded == &expand_word_fatal)
    {
      /* By convention, each time this error is returned,
         tlist->word->word has already been freed. */
      tlist->word->word = (char *)NULL;

      /* Dispose our copy of the original list. */
      dispose_words (orig_list);
      /* Dispose the new list we're building. */
      dispose_words (new_list);

      last_command_exit_value = EXECUTION_FAILURE;
      if (expanded == &expand_word_error)
        jump_to_top_level (DISCARD);
      else
        jump_to_top_level (FORCE_EOF);
    }

      /* Don't split words marked W_NOSPLIT. */
      if (expanded_something && (tlist->word->flags & W_NOSPLIT) == 0)
    {
      temp_list = word_list_split (expanded);
      dispose_words (expanded);
    }
      else
    {
      /* If no parameter expansion, command substitution, process
         substitution, or arithmetic substitution took place, then
         do not do word splitting.  We still have to remove quoted
         null characters from the result. */
      word_list_remove_quoted_nulls (expanded);
      temp_list = expanded;
    }

      expanded = REVERSE_LIST (temp_list, WORD_LIST *);
      new_list = (WORD_LIST *)list_append (expanded, new_list);
    }

  if (orig_list)  
    dispose_words (orig_list);

  if (new_list)
    new_list = REVERSE_LIST (new_list, WORD_LIST *);

  return (new_list);
}

/* The workhorse for expand_words () and expand_words_no_vars ().
   First arg is LIST, a WORD_LIST of words.
   Second arg EFLAGS is a flags word controlling which expansions are
   performed.

   This does all of the substitutions: brace expansion, tilde expansion,
   parameter expansion, command substitution, arithmetic expansion,
   process substitution, word splitting, and pathname expansion, according
   to the bits set in EFLAGS.  Words with the W_QUOTED or W_NOSPLIT bits
   set, or for which no expansion is done, do not undergo word splitting.
   Words with the W_NOGLOB bit set do not undergo pathname expansion. */
static WORD_LIST *
expand_word_list_internal (list, eflags)
     WORD_LIST *list;
     int eflags;
{
  WORD_LIST *new_list, *temp_list;
  int tint;

  if (list == 0)
    return ((WORD_LIST *)NULL);

  garglist = new_list = copy_word_list (list);
  if (eflags & WEXP_VARASSIGN)
    {
      garglist = new_list = separate_out_assignments (new_list);
      if (new_list == 0)
    {
      if (subst_assign_varlist)
        {
          /* All the words were variable assignments, so they are placed
         into the shell's environment. */
          for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next)
        {
          this_command_name = (char *)NULL; /* no arithmetic errors */
          tint = do_assignment (temp_list->word->word);
          /* Variable assignment errors in non-interactive shells
             running in Posix.2 mode cause the shell to exit. */
          if (tint == 0)
            {
              last_command_exit_value = EXECUTION_FAILURE;
              if (interactive_shell == 0 && posixly_correct)
            jump_to_top_level (FORCE_EOF);
              else
            jump_to_top_level (DISCARD);
            }
        }
          dispose_words (subst_assign_varlist);
          subst_assign_varlist = (WORD_LIST *)NULL;
        }
      return ((WORD_LIST *)NULL);
    }
    }

  /* Begin expanding the words that remain.  The expansions take place on
     things that aren't really variable assignments. */

#if defined (BRACE_EXPANSION)
  /* Do brace expansion on this word if there are any brace characters
     in the string. */
  if ((eflags & WEXP_BRACEEXP) && brace_expansion && new_list)
    new_list = brace_expand_word_list (new_list, eflags);
#endif /* BRACE_EXPANSION */

  /* Perform the `normal' shell expansions: tilde expansion, parameter and
     variable substitution, command substitution, arithmetic expansion,
     and word splitting. */
  new_list = shell_expand_word_list (new_list, eflags);

  /* Okay, we're almost done.  Now let's just do some filename
     globbing. */
  if (new_list)
    {
      if ((eflags & WEXP_PATHEXP) && disallow_filename_globbing == 0)
    /* Glob expand the word list unless globbing has been disabled. */
    new_list = glob_expand_word_list (new_list, eflags);
      else
    /* Dequote the words, because we're not performing globbing. */
    new_list = dequote_list (new_list);
    }

  if ((eflags & WEXP_VARASSIGN) && subst_assign_varlist)
    {
      sh_assign_func_t *assign_func;

      /* If the remainder of the words expand to nothing, Posix.2 requires
     that the variable and environment assignments affect the shell's
     environment. */
      assign_func = new_list ? assign_in_env : do_assignment;

      for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next)
    {
      this_command_name = (char *)NULL;
      tint = (*assign_func) (temp_list->word->word);
      /* Variable assignment errors in non-interactive shells running
         in Posix.2 mode cause the shell to exit. */
      if (tint == 0 && assign_func == do_assignment)
        {
          last_command_exit_value = EXECUTION_FAILURE;
          if (interactive_shell == 0 && posixly_correct)
        jump_to_top_level (FORCE_EOF);
          else
        jump_to_top_level (DISCARD);
        }
    }

      dispose_words (subst_assign_varlist);
      subst_assign_varlist = (WORD_LIST *)NULL;
    }

#if 0
  tint = list_length (new_list) + 1;
  RESIZE_MALLOCED_BUFFER (glob_argv_flags, 0, tint, glob_argv_flags_size, 16);
  for (tint = 0, temp_list = new_list; temp_list; temp_list = temp_list->next)
    glob_argv_flags[tint++] = (temp_list->word->flags & W_GLOBEXP) ? '1' : '0';
  glob_argv_flags[tint] = '\0';
#endif

  return (new_list);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/hashcmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/hashcmd.c used in: Step 1218
/* hashcmd.c - functions for managing a hash table mapping command names to
           full pathnames. */

/* Copyright (C) 1997-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

    Bash 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.

    Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
    Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include "bashtypes.h"
#include "posixstat.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "bashansi.h"

#include "shell.h"
#include "findcmd.h"
#include "hashcmd.h"

extern int hashing_enabled;

HASH_TABLE *hashed_filenames = (HASH_TABLE *)NULL;

static void phash_freedata __P((PTR_T));

void
phash_create ()
{
  if (hashed_filenames == 0)
    hashed_filenames = hash_create (FILENAME_HASH_BUCKETS);
}

static void
phash_freedata (data)
     PTR_T data;
{
  free (((PATH_DATA *)data)->path);
  free (data);
}

void
phash_flush ()
{
  if (hashed_filenames)
    hash_flush (hashed_filenames, phash_freedata);
}

/* Remove FILENAME from the table of hashed commands. */
int
phash_remove (filename)
     const char *filename;
{
  register BUCKET_CONTENTS *item;

  if (hashing_enabled == 0 || hashed_filenames == 0)
    return 0;

  item = hash_remove (filename, hashed_filenames, 0);
  if (item)
    {
      if (item->data)
    phash_freedata (item->data);
      free (item->key);
      free (item);
      return 0;
    }
  return 1;
}

/* Place FILENAME (key) and FULL_PATH (data->path) into the
   hash table.  CHECK_DOT if non-null is for future calls to
   phash_search (); it means that this file was found
   in a directory in $PATH that is not an absolute pathname.
   FOUND is the initial value for times_found. */
void
phash_insert (filename, full_path, check_dot, found)
     char *filename, *full_path;
     int check_dot, found;
{
  register BUCKET_CONTENTS *item;

  if (hashing_enabled == 0)
    return;

  if (hashed_filenames == 0)
    phash_create ();

  item = hash_insert (filename, hashed_filenames, 0);
  if (item->data)
    free (pathdata(item)->path);
  else
    {
      item->key = savestring (filename);
      item->data = xmalloc (sizeof (PATH_DATA));
    }
  pathdata(item)->path = savestring (full_path);
  pathdata(item)->flags = 0;
  if (check_dot)
    pathdata(item)->flags |= HASH_CHKDOT;
  if (*full_path != '/')
    pathdata(item)->flags |= HASH_RELPATH;
  item->times_found = found;
}

/* Return the full pathname that FILENAME hashes to.  If FILENAME
   is hashed, but (data->flags & HASH_CHKDOT) is non-zero, check
   ./FILENAME and return that if it is executable.  This always
   returns a newly-allocated string; the caller is responsible
   for freeing it. */
char *
phash_search (filename)
     const char *filename;
{
  register BUCKET_CONTENTS *item;
  char *path, *dotted_filename, *tail;
  int same;

  if (hashing_enabled == 0 || hashed_filenames == 0)
    return ((char *)NULL);

  item = hash_search (filename, hashed_filenames, 0);

  if (item == NULL)
    return ((char *)NULL);

  /* If this filename is hashed, but `.' comes before it in the path,
     see if ./filename is executable.  If the hashed value is not an
     absolute pathname, see if ./`hashed-value' exists. */
  path = pathdata(item)->path;
  if (pathdata(item)->flags & (HASH_CHKDOT|HASH_RELPATH))
    {
      tail = (pathdata(item)->flags & HASH_RELPATH) ? path : (char *)filename;  /* XXX - fix const later */
      /* If the pathname does not start with a `./', add a `./' to it. */
      if (tail[0] != '.' || tail[1] != '/')
    {
      dotted_filename = (char *)xmalloc (3 + strlen (tail));
      dotted_filename[0] = '.'; dotted_filename[1] = '/';
      strcpy (dotted_filename + 2, tail);
    }
      else
    dotted_filename = savestring (tail);

      if (executable_file (dotted_filename))
    return (dotted_filename);

      free (dotted_filename);

#if 0
      if (pathdata(item)->flags & HASH_RELPATH)
    return ((char *)NULL);
#endif

      /* Watch out.  If this file was hashed to "./filename", and
     "./filename" is not executable, then return NULL. */

      /* Since we already know "./filename" is not executable, what
     we're really interested in is whether or not the `path'
     portion of the hashed filename is equivalent to the current
     directory, but only if it starts with a `.'.  (This catches
     ./. and so on.)  same_file () tests general Unix file
     equivalence -- same device and inode. */
      if (*path == '.')
    {
      same = 0;
      tail = (char *)strrchr (path, '/');

      if (tail)
        {
          *tail = '\0';
          same = same_file (".", path, (struct stat *)NULL, (struct stat *)NULL);
          *tail = '/';
        }

      return same ? (char *)NULL : savestring (path);
    }
    }

  return (savestring (path));
}

https://git.savannah.gnu.org/cgit/bash.git/tree/hashlib.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/hashlib.c used in: Step 1219
/* hashlib.c -- functions to manage and access hash tables for bash. */

/* Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include "bashansi.h"

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>

#include "shell.h"
#include "hashlib.h"

/* Rely on properties of unsigned division (unsigned/int -> unsigned) and
   don't discard the upper 32 bits of the value, if present. */
#define HASH_BUCKET(s, t, h) (((h) = hash_string (s)) & ((t)->nbuckets - 1))

static BUCKET_CONTENTS *copy_bucket_array __P((BUCKET_CONTENTS *, sh_string_func_t *));

/* Make a new hash table with BUCKETS number of buckets.  Initialize
   each slot in the table to NULL. */
HASH_TABLE *
hash_create (buckets)
     int buckets;
{
  HASH_TABLE *new_table;
  register int i;

  new_table = (HASH_TABLE *)xmalloc (sizeof (HASH_TABLE));
  if (buckets == 0)
    buckets = DEFAULT_HASH_BUCKETS;

  new_table->bucket_array =
    (BUCKET_CONTENTS **)xmalloc (buckets * sizeof (BUCKET_CONTENTS *));
  new_table->nbuckets = buckets;
  new_table->nentries = 0;

  for (i = 0; i < buckets; i++)
    new_table->bucket_array[i] = (BUCKET_CONTENTS *)NULL;

  return (new_table);
}

int
hash_size (table)
     HASH_TABLE *table;
{
  return (HASH_ENTRIES(table));
}

static BUCKET_CONTENTS *
copy_bucket_array (ba, cpdata)
     BUCKET_CONTENTS *ba;
     sh_string_func_t *cpdata;  /* data copy function */
{
  BUCKET_CONTENTS *new_bucket, *n, *e;

  if (ba == 0)
    return ((BUCKET_CONTENTS *)0);

  for (n = (BUCKET_CONTENTS *)0, e = ba; e; e = e->next)
    {
      if (n == 0)
        {
          new_bucket = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
          n = new_bucket;
        }
      else
        {
          n->next = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
          n = n->next;
        }

      n->key = savestring (e->key);
      n->data = e->data ? (cpdata ? (*cpdata) (e->data) : savestring (e->data))
            : NULL;
      n->khash = e->khash;
      n->times_found = e->times_found;
      n->next = (BUCKET_CONTENTS *)NULL;
    }

  return new_bucket;  
}

HASH_TABLE *
hash_copy (table, cpdata)
     HASH_TABLE *table;
     sh_string_func_t *cpdata;
{
  HASH_TABLE *new_table;
  int i;

  if (table == 0)
    return ((HASH_TABLE *)NULL);

  new_table = hash_create (table->nbuckets);

  for (i = 0; i < table->nbuckets; i++)
    new_table->bucket_array[i] = copy_bucket_array (table->bucket_array[i], cpdata);

  new_table->nentries = table->nentries;
  return new_table;
}

/* The `khash' check below requires that strings that compare equally with
   strcmp hash to the same value. */
unsigned int
hash_string (s)
     const char *s;
{
  register unsigned int i;

  /* This is the best string hash function I found.

     The magic is in the interesting relationship between the special prime
     16777619 (2^24 + 403) and 2^32 and 2^8. */
 
  for (i = 0; *s; s++)
    {
      i *= 16777619;
      i ^= *s;
    }

  return i;
}

/* Return the location of the bucket which should contain the data
   for STRING.  TABLE is a pointer to a HASH_TABLE. */

int
hash_bucket (string, table)
     const char *string;
     HASH_TABLE *table;
{
  unsigned int h;

  return (HASH_BUCKET (string, table, h));
}

/* Return a pointer to the hashed item.  If the HASH_CREATE flag is passed,
   create a new hash table entry for STRING, otherwise return NULL. */
BUCKET_CONTENTS *
hash_search (string, table, flags)
     const char *string;
     HASH_TABLE *table;
     int flags;
{
  BUCKET_CONTENTS *list;
  int bucket;
  unsigned int hv;

  if (table == 0 || ((flags & HASH_CREATE) == 0 && HASH_ENTRIES (table) == 0))
    return (BUCKET_CONTENTS *)NULL;

  bucket = HASH_BUCKET (string, table, hv);

  for (list = table->bucket_array[bucket]; list; list = list->next)
    {
      if (hv == list->khash && STREQ (list->key, string))
    {
      list->times_found++;
      return (list);
    }
    }

  if (flags & HASH_CREATE)
    {
      list = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
      list->next = table->bucket_array[bucket];
      table->bucket_array[bucket] = list;

      list->data = NULL;
      list->key = (char *)string;   /* XXX fix later */
      list->khash = hv;
      list->times_found = 0;

      table->nentries++;
      return (list);
    }
      
  return (BUCKET_CONTENTS *)NULL;
}

/* Remove the item specified by STRING from the hash table TABLE.
   The item removed is returned, so you can free its contents.  If
   the item isn't in this table NULL is returned. */
BUCKET_CONTENTS *
hash_remove (string, table, flags)
     const char *string;
     HASH_TABLE *table;
     int flags;
{
  int bucket;
  BUCKET_CONTENTS *prev, *temp;
  unsigned int hv;

  if (table == 0 || HASH_ENTRIES (table) == 0)
    return (BUCKET_CONTENTS *)NULL;

  bucket = HASH_BUCKET (string, table, hv);
  prev = (BUCKET_CONTENTS *)NULL;
  for (temp = table->bucket_array[bucket]; temp; temp = temp->next)
    {
      if (hv == temp->khash && STREQ (temp->key, string))
    {
      if (prev)
        prev->next = temp->next;
      else
        table->bucket_array[bucket] = temp->next;

      table->nentries--;
      return (temp);
    }
      prev = temp;
    }
  return ((BUCKET_CONTENTS *) NULL);
}

/* Create an entry for STRING, in TABLE.  If the entry already
   exists, then return it (unless the HASH_NOSRCH flag is set). */
BUCKET_CONTENTS *
hash_insert (string, table, flags)
     char *string;
     HASH_TABLE *table;
     int flags;
{
  BUCKET_CONTENTS *item;
  int bucket;
  unsigned int hv;

  if (table == 0)
    table = hash_create (0);

  item = (flags & HASH_NOSRCH) ? (BUCKET_CONTENTS *)NULL
                   : hash_search (string, table, 0);

  if (item == 0)
    {
      bucket = HASH_BUCKET (string, table, hv);

      item = (BUCKET_CONTENTS *)xmalloc (sizeof (BUCKET_CONTENTS));
      item->next = table->bucket_array[bucket];
      table->bucket_array[bucket] = item;

      item->data = NULL;
      item->key = string;
      item->khash = hv;
      item->times_found = 0;

      table->nentries++;
    }

  return (item);
}

/* Remove and discard all entries in TABLE.  If FREE_DATA is non-null, it
   is a function to call to dispose of a hash item's data.  Otherwise,
   free() is called. */
void
hash_flush (table, free_data)
     HASH_TABLE *table;
     sh_free_func_t *free_data;
{
  int i;
  register BUCKET_CONTENTS *bucket, *item;

  if (table == 0 || HASH_ENTRIES (table) == 0)
    return;

  for (i = 0; i < table->nbuckets; i++)
    {
      bucket = table->bucket_array[i];

      while (bucket)
    {
      item = bucket;
      bucket = bucket->next;

      if (free_data)
        (*free_data) (item->data);
      else
        free (item->data);
      free (item->key);
      free (item);
    }
      table->bucket_array[i] = (BUCKET_CONTENTS *)NULL;
    }

  table->nentries = 0;
}

/* Free the hash table pointed to by TABLE. */
void
hash_dispose (table)
     HASH_TABLE *table;
{
  free (table->bucket_array);
  free (table);
}

void
hash_walk (table, func)
     HASH_TABLE *table;
     hash_wfunc *func;
{
  register int i;
  BUCKET_CONTENTS *item;

  if (table == 0 || HASH_ENTRIES (table) == 0)
    return;

  for (i = 0; i < table->nbuckets; i++)
    {
      for (item = hash_items (i, table); item; item = item->next)
    if ((*func) (item) < 0)
      return;
    }
}

#if defined (DEBUG) || defined (TEST_HASHING)
void
hash_pstats (table, name)
     HASH_TABLE *table;
     char *name;
{
  register int slot, bcount;
  register BUCKET_CONTENTS *bc;

  if (name == 0)
    name = "unknown hash table";

  fprintf (stderr, "%s: %d buckets; %d items\n", name, table->nbuckets, table->nentries);

  /* Print out a count of how many strings hashed to each bucket, so we can
     see how even the distribution is. */
  for (slot = 0; slot < table->nbuckets; slot++)
    {
      bc = hash_items (slot, table);

      fprintf (stderr, "\tslot %3d: ", slot);
      for (bcount = 0; bc; bc = bc->next)
    bcount++;

      fprintf (stderr, "%d\n", bcount);
    }
}
#endif

#ifdef TEST_HASHING

/* link with xmalloc.o and lib/malloc/libmalloc.a */
#undef NULL
#include <stdio.h>

#ifndef NULL
#define NULL 0
#endif

HASH_TABLE *table, *ntable;

int interrupt_immediately = 0;

int
signal_is_trapped (s)
     int s;
{
  return (0);
}

void
programming_error (const char *format, ...)
{
  abort();
}

void
fatal_error (const char *format, ...)
{
  abort();
}

main ()
{
  char string[256];
  int count = 0;
  BUCKET_CONTENTS *tt;

  table = hash_create (0);

  for (;;)
    {
      char *temp_string;
      if (fgets (string, sizeof (string), stdin) == 0)
    break;
      if (!*string)
    break;
      temp_string = savestring (string);
      tt = hash_insert (temp_string, table, 0);
      if (tt->times_found)
    {
      fprintf (stderr, "You have already added item `%s'\n", string);
      free (temp_string);
    }
      else
    {
      count++;
    }
    }

  hash_pstats (table, "hash test");

  ntable = hash_copy (table, (sh_string_func_t *)NULL);
  hash_flush (table, (sh_free_func_t *)NULL);
  hash_pstats (ntable, "hash copy test");

  exit (0);
}

#endif /* TEST_HASHING */

https://git.savannah.gnu.org/cgit/bash.git/tree/mailcheck.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/mailcheck.c used in: Step 1220
/* mailcheck.c -- The check is in the mail... */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include <stdio.h>
#include "bashtypes.h"
#include "posixstat.h"
#ifndef _MINIX
#  include <sys/param.h>
#endif
#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif
#include "posixtime.h"
#include "bashansi.h"

#include "shell.h"
#include "execute_cmd.h"
#include "mailcheck.h"
#include <tilde/tilde.h>

extern int mailstat __P((const char *, struct stat *));

typedef struct {
  char *name;
  char *msg;
  time_t access_time;
  time_t mod_time;
  off_t file_size;
} FILEINFO;

/* The list of remembered mail files. */
static FILEINFO **mailfiles = (FILEINFO **)NULL;

/* Number of mail files that we have. */
static int mailfiles_count;

/* The last known time that mail was checked. */
static time_t last_time_mail_checked;

/* Non-zero means warn if a mail file has been read since last checked. */
int mail_warning;

static int find_mail_file __P((char *));
static void update_mail_file __P((int));
static int add_mail_file __P((char *, char *));

static int file_mod_date_changed __P((int));
static int file_access_date_changed __P((int));
static int file_has_grown __P((int));

static char *parse_mailpath_spec __P((char *));

/* Returns non-zero if it is time to check mail. */
int
time_to_check_mail ()
{
  char *temp;
  time_t now;
  intmax_t seconds;

  temp = get_string_value ("MAILCHECK");

  /* Negative number, or non-numbers (such as empty string) cause no
     checking to take place. */
  if (temp == 0 || legal_number (temp, &seconds) == 0 || seconds < 0)
    return (0);

  now = NOW;
  /* Time to check if MAILCHECK is explicitly set to zero, or if enough
     time has passed since the last check. */
  return (seconds == 0 || ((now - last_time_mail_checked) >= seconds));
}

/* Okay, we have checked the mail.  Perhaps I should make this function
   go away. */
void
reset_mail_timer ()
{
  last_time_mail_checked = NOW;
}

/* Locate a file in the list.  Return index of
   entry, or -1 if not found. */
static int
find_mail_file (file)
     char *file;
{
  register int i;

  for (i = 0; i < mailfiles_count; i++)
    if (STREQ (mailfiles[i]->name, file))
      return i;

  return -1;
}

#define RESET_MAIL_FILE(i) \
  do \
    { \
      mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \
      mailfiles[i]->file_size = 0; \
    } \
  while (0)

static void
update_mail_file (i)
     int i;
{
  char *file;
  struct stat finfo;

  file = mailfiles[i]->name;
  if (mailstat (file, &finfo) == 0)
    {
      mailfiles[i]->access_time = finfo.st_atime;
      mailfiles[i]->mod_time = finfo.st_mtime;
      mailfiles[i]->file_size = finfo.st_size;
    }
  else
    RESET_MAIL_FILE (i);
}

/* Add this file to the list of remembered files and return its index
   in the list of mail files. */
static int
add_mail_file (file, msg)
     char *file, *msg;
{
  struct stat finfo;
  char *filename;
  int i;

  filename = full_pathname (file);
  i = find_mail_file (filename);
  if (i >= 0)
    {
      if (mailstat (filename, &finfo) == 0)
    {
      mailfiles[i]->mod_time = finfo.st_mtime;
      mailfiles[i]->access_time = finfo.st_atime;
      mailfiles[i]->file_size = finfo.st_size;
    }
      free (filename);
      return i;
    }

  i = mailfiles_count++;
  mailfiles = (FILEINFO **)xrealloc
        (mailfiles, mailfiles_count * sizeof (FILEINFO *));

  mailfiles[i] = (FILEINFO *)xmalloc (sizeof (FILEINFO));
  mailfiles[i]->name = filename;
  mailfiles[i]->msg = msg ? savestring (msg) : (char *)NULL;
  update_mail_file (i);
  return i;
}

/* Reset the existing mail files access and modification times to zero. */
void
reset_mail_files ()
{
  register int i;

  for (i = 0; i < mailfiles_count; i++)
    {
      RESET_MAIL_FILE (i);
    }
}

/* Free the information that we have about the remembered mail files. */
void
free_mail_files ()
{
  register int i;

  for (i = 0; i < mailfiles_count; i++)
    {
      free (mailfiles[i]->name);
      FREE (mailfiles[i]->msg);
      free (mailfiles[i]);
    }

  if (mailfiles)
    free (mailfiles);

  mailfiles_count = 0;
  mailfiles = (FILEINFO **)NULL;
}

/* Return non-zero if FILE's mod date has changed and it has not been
   accessed since modified. */
static int
file_mod_date_changed (i)
     int i;
{
  time_t mtime;
  struct stat finfo;
  char *file;

  file = mailfiles[i]->name;
  mtime = mailfiles[i]->mod_time;

  if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0))
    return (mtime != finfo.st_mtime);

  return (0);
}

/* Return non-zero if FILE's access date has changed. */
static int
file_access_date_changed (i)
     int i;
{
  time_t atime;
  struct stat finfo;
  char *file;

  file = mailfiles[i]->name;
  atime = mailfiles[i]->access_time;

  if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0))
    return (atime != finfo.st_atime);

  return (0);
}

/* Return non-zero if FILE's size has increased. */
static int
file_has_grown (i)
     int i;
{
  off_t size;
  struct stat finfo;
  char *file;

  file = mailfiles[i]->name;
  size = mailfiles[i]->file_size;

  return ((mailstat (file, &finfo) == 0) && (finfo.st_size > size));
}

/* Take an element from $MAILPATH and return the portion from
   the first unquoted `?' or `%' to the end of the string.  This is the
   message to be printed when the file contents change. */
static char *
parse_mailpath_spec (str)
     char *str;
{
  char *s;
  int pass_next;

  for (s = str, pass_next = 0; s && *s; s++)
    {
      if (pass_next)
    {
      pass_next = 0;
      continue;
    }
      if (*s == '\\')
    {
      pass_next++;
      continue;
    }
      if (*s == '?' || *s == '%')
    return s;
    }
  return ((char *)NULL);
}

char *
make_default_mailpath ()
{
  char *mp;

  get_current_user_info ();
  mp = (char *)xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name));
  strcpy (mp, DEFAULT_MAIL_DIRECTORY);
  mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/';
  strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name);
  return (mp);
}

/* Remember the dates of the files specified by MAILPATH, or if there is
   no MAILPATH, by the file specified in MAIL.  If neither exists, use a
   default value, which we randomly concoct from using Unix. */
void
remember_mail_dates ()
{
  char *mailpaths;
  char *mailfile, *mp;
  int i = 0;

  mailpaths = get_string_value ("MAILPATH");

  /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */
  if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL")))
    {
      add_mail_file (mailpaths, (char *)NULL);
      return;
    }

  if (mailpaths == 0)
    {
      mailpaths = make_default_mailpath ();
      add_mail_file (mailpaths, (char *)NULL);
      free (mailpaths);
      return;
    }

  while (mailfile = extract_colon_unit (mailpaths, &i))
    {
      mp = parse_mailpath_spec (mailfile);
      if (mp && *mp)
    *mp++ = '\0';
      add_mail_file (mailfile, mp);
      free (mailfile);
    }
}

/* check_mail () is useful for more than just checking mail.  Since it has
   the paranoids dream ability of telling you when someone has read your
   mail, it can just as easily be used to tell you when someones .profile
   file has been read, thus letting one know when someone else has logged
   in.  Pretty good, huh? */

/* Check for mail in some files.  If the modification date of any
   of the files in MAILPATH has changed since we last did a
   remember_mail_dates () then mention that the user has mail.
   Special hack:  If the variable MAIL_WARNING is non-zero and the
   mail file has been accessed since the last time we remembered, then
   the message "The mail in <mailfile> has been read" is printed. */
void
check_mail ()
{
  char *current_mail_file, *message;
  int i, use_user_notification;
  char *dollar_underscore, *temp;

  dollar_underscore = get_string_value ("_");
  if (dollar_underscore)
    dollar_underscore = savestring (dollar_underscore);

  for (i = 0; i < mailfiles_count; i++)
    {
      current_mail_file = mailfiles[i]->name;

      if (*current_mail_file == '\0')
    continue;

      if (file_mod_date_changed (i))
    {
      int file_is_bigger;

      use_user_notification = mailfiles[i]->msg != (char *)NULL;
      message = mailfiles[i]->msg ? mailfiles[i]->msg : "You have mail in $_";

      bind_variable ("_", current_mail_file);

#define atime mailfiles[i]->access_time
#define mtime mailfiles[i]->mod_time

      /* Have to compute this before the call to update_mail_file, which
         resets all the information. */
      file_is_bigger = file_has_grown (i);

      update_mail_file (i);

      /* If the user has just run a program which manipulates the
         mail file, then don't bother explaining that the mail
         file has been manipulated.  Since some systems don't change
         the access time to be equal to the modification time when
         the mail in the file is manipulated, check the size also.  If
         the file has not grown, continue. */
      if ((atime >= mtime) || !file_is_bigger)
        continue;

      /* If the mod time is later than the access time and the file
         has grown, note the fact that this is *new* mail. */
      if (use_user_notification == 0 && (atime < mtime) && file_is_bigger)
        message = "You have new mail in $_";
#undef atime
#undef mtime

      if (temp = expand_string_to_string (message, Q_DOUBLE_QUOTES))
        {
          puts (temp);
          free (temp);
        }
      else
        putchar ('\n');
    }

      if (mail_warning && file_access_date_changed (i))
    {
      update_mail_file (i);
      printf ("The mail in %s has been read\n", current_mail_file);
    }
    }

  if (dollar_underscore)
    {
      bind_variable ("_", dollar_underscore);
      free (dollar_underscore);
    }
  else
    unbind_variable ("_");
}

https://git.savannah.gnu.org/cgit/bash.git/tree/support/mksignames.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/support/mksignames.c used in: Step 1221
/* signames.c -- Create and write `signames.h', which contains an array of
   signal names. */

/* Copyright (C) 1992 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#if defined (HAVE_STDLIB_H)
#  include <stdlib.h>
#else
#  include "ansi_stdlib.h"
#endif /* HAVE_STDLIB_H */

#if !defined (NSIG)
#  define NSIG 64
#endif

/*
 * Special traps:
 *  EXIT == 0
 *  DEBUG == NSIG
 *  ERR == NSIG+1
 */
#define LASTSIG NSIG+1

char *signal_names[2 * NSIG + 3];

#define signal_names_size (sizeof(signal_names)/sizeof(signal_names[0]))

char *progname;

/* AIX 4.3 defines SIGRTMIN and SIGRTMAX as 888 and 999 respectively.
   I don't want to allocate so much unused space for the intervening signal
   numbers, so we just punt if SIGRTMAX is past the bounds of the
   signal_names array (handled in configure). */
#if defined (SIGRTMAX) && defined (UNUSABLE_RT_SIGNALS)
#  undef SIGRTMAX
#  undef SIGRTMIN
#endif

#if defined (SIGRTMAX) || defined (SIGRTMIN)
#  define RTLEN 14
#  define RTLIM 256
#endif

void
initialize_signames ()
{
  register int i;
#if defined (SIGRTMAX) || defined (SIGRTMIN)
  int rtmin, rtmax, rtcnt;
#endif

  for (i = 1; i < signal_names_size; i++)
    signal_names[i] = (char *)NULL;

  /* `signal' 0 is what we do on exit. */
  signal_names[0] = "EXIT";

  /* Place signal names which can be aliases for more common signal
     names first.  This allows (for example) SIGABRT to overwrite SIGLOST. */

  /* POSIX 1003.1b-1993 real time signals, but take care of incomplete
     implementations. Acoording to the standard, both, SIGRTMIN and
     SIGRTMAX must be defined, SIGRTMIN must be stricly less than
     SIGRTMAX, and the difference must be at least 7, that is, there
     must be at least eight distinct real time signals. */

  /* The generated signal names are SIGRTMIN, SIGRTMIN+1, ...,
     SIGRTMIN+x, SIGRTMAX-x, ..., SIGRTMAX-1, SIGRTMAX. If the number
     of RT signals is odd, there is an extra SIGRTMIN+(x+1).
     These names are the ones used by ksh and /usr/xpg4/bin/sh on SunOS5. */

#if defined (SIGRTMIN)
  rtmin = SIGRTMIN;
  signal_names[rtmin] = "SIGRTMIN";
#endif

#if defined (SIGRTMAX)
  rtmax = SIGRTMAX;
  signal_names[rtmax] = "SIGRTMAX";
#endif

#if defined (SIGRTMAX) && defined (SIGRTMIN)
  if (rtmax > rtmin)
    {
      rtcnt = (rtmax - rtmin - 1) / 2;
      /* croak if there are too many RT signals */
      if (rtcnt >= RTLIM/2)
    {
      rtcnt = RTLIM/2-1;
      fprintf(stderr, "%s: error: more than %i real time signals, fix `%s'\n",
          progname, RTLIM, progname);
    }

      for (i = 1; i <= rtcnt; i++)
    {
      signal_names[rtmin+i] = (char *)malloc(RTLEN);
      if (signal_names[rtmin+i])
        sprintf (signal_names[rtmin+i], "SIGRTMIN+%d", i);
      signal_names[rtmax-i] = (char *)malloc(RTLEN);
      if (signal_names[rtmax-i])
        sprintf (signal_names[rtmax-i], "SIGRTMAX-%d", i);
    }

      if (rtcnt < RTLIM/2-1 && rtcnt != (rtmax-rtmin)/2)
    {
      /* Need an extra RTMIN signal */
      signal_names[rtmin+rtcnt+1] = (char *)malloc(RTLEN);
      if (signal_names[rtmin+rtcnt+1])
        sprintf (signal_names[rtmin+rtcnt+1], "SIGRTMIN+%d", rtcnt+1);
    }
    }
#endif /* SIGRTMIN && SIGRTMAX */

/* AIX */
#if defined (SIGLOST)   /* resource lost (eg, record-lock lost) */
  signal_names[SIGLOST] = "SIGLOST";
#endif

#if defined (SIGMSG)    /* HFT input data pending */
  signal_names[SIGMSG] = "SIGMSG";
#endif

#if defined (SIGDANGER) /* system crash imminent */
  signal_names[SIGDANGER] = "SIGDANGER";
#endif

#if defined (SIGMIGRATE) /* migrate process to another CPU */
  signal_names[SIGMIGRATE] = "SIGMIGRATE";
#endif

#if defined (SIGPRE)    /* programming error */
  signal_names[SIGPRE] = "SIGPRE";
#endif

#if defined (SIGVIRT)   /* AIX virtual time alarm */
  signal_names[SIGVIRT] = "SIGVIRT";
#endif

#if defined (SIGALRM1)  /* m:n condition variables */
  signal_names[SIGALRM1] = "SIGALRM1";
#endif

#if defined (SIGWAITING)    /* m:n scheduling */
  signal_names[SIGWAITING] = "SIGWAITING";
#endif

#if defined (SIGGRANT)  /* HFT monitor mode granted */
  signal_names[SIGGRANT] = "SIGGRANT";
#endif

#if defined (SIGKAP)    /* keep alive poll from native keyboard */
  signal_names[SIGKAP] = "SIGKAP";
#endif

#if defined (SIGRETRACT) /* HFT monitor mode retracted */
  signal_names[SIGRETRACT] = "SIGRETRACT";
#endif

#if defined (SIGSOUND)  /* HFT sound sequence has completed */
  signal_names[SIGSOUND] = "SIGSOUND";
#endif

#if defined (SIGSAK)    /* Secure Attention Key */
  signal_names[SIGSAK] = "SIGSAK";
#endif

/* SunOS5 */
#if defined (SIGLWP)    /* special signal used by thread library */
  signal_names[SIGLWP] = "SIGLWP";
#endif

#if defined (SIGFREEZE) /* special signal used by CPR */
  signal_names[SIGFREEZE] = "SIGFREEZE";
#endif

#if defined (SIGTHAW)   /* special signal used by CPR */
  signal_names[SIGTHAW] = "SIGTHAW";
#endif

#if defined (SIGCANCEL) /* thread cancellation signal used by libthread */
  signal_names[SIGCANCEL] = "SIGCANCEL";
#endif

/* HP-UX */
#if defined (SIGDIL)    /* DIL signal (?) */
  signal_names[SIGDIL] = "SIGDIL";
#endif

/* System V */
#if defined (SIGCLD)    /* Like SIGCHLD.  */
  signal_names[SIGCLD] = "SIGCLD";
#endif

#if defined (SIGPWR)    /* power state indication */
  signal_names[SIGPWR] = "SIGPWR";
#endif

#if defined (SIGPOLL)   /* Pollable event (for streams)  */
  signal_names[SIGPOLL] = "SIGPOLL";
#endif

/* Unknown */
#if defined (SIGWINDOW)
  signal_names[SIGWINDOW] = "SIGWINDOW";
#endif

/* Common */
#if defined (SIGHUP)    /* hangup */
  signal_names[SIGHUP] = "SIGHUP";
#endif

#if defined (SIGINT)    /* interrupt */
  signal_names[SIGINT] = "SIGINT";
#endif

#if defined (SIGQUIT)   /* quit */
  signal_names[SIGQUIT] = "SIGQUIT";
#endif

#if defined (SIGILL)    /* illegal instruction (not reset when caught) */
  signal_names[SIGILL] = "SIGILL";
#endif

#if defined (SIGTRAP)   /* trace trap (not reset when caught) */
  signal_names[SIGTRAP] = "SIGTRAP";
#endif

#if defined (SIGIOT)    /* IOT instruction */
  signal_names[SIGIOT] = "SIGIOT";
#endif

#if defined (SIGABRT)   /* Cause current process to dump core. */
  signal_names[SIGABRT] = "SIGABRT";
#endif

#if defined (SIGEMT)    /* EMT instruction */
  signal_names[SIGEMT] = "SIGEMT";
#endif

#if defined (SIGFPE)    /* floating point exception */
  signal_names[SIGFPE] = "SIGFPE";
#endif

#if defined (SIGKILL)   /* kill (cannot be caught or ignored) */
  signal_names[SIGKILL] = "SIGKILL";
#endif

#if defined (SIGBUS)    /* bus error */
  signal_names[SIGBUS] = "SIGBUS";
#endif

#if defined (SIGSEGV)   /* segmentation violation */
  signal_names[SIGSEGV] = "SIGSEGV";
#endif

#if defined (SIGSYS)    /* bad argument to system call */
  signal_names[SIGSYS] = "SIGSYS";
#endif

#if defined (SIGPIPE)   /* write on a pipe with no one to read it */
  signal_names[SIGPIPE] = "SIGPIPE";
#endif

#if defined (SIGALRM)   /* alarm clock */
  signal_names[SIGALRM] = "SIGALRM";
#endif

#if defined (SIGTERM)   /* software termination signal from kill */
  signal_names[SIGTERM] = "SIGTERM";
#endif

#if defined (SIGURG)    /* urgent condition on IO channel */
  signal_names[SIGURG] = "SIGURG";
#endif

#if defined (SIGSTOP)   /* sendable stop signal not from tty */
  signal_names[SIGSTOP] = "SIGSTOP";
#endif

#if defined (SIGTSTP)   /* stop signal from tty */
  signal_names[SIGTSTP] = "SIGTSTP";
#endif

#if defined (SIGCONT)   /* continue a stopped process */
  signal_names[SIGCONT] = "SIGCONT";
#endif

#if defined (SIGCHLD)   /* to parent on child stop or exit */
  signal_names[SIGCHLD] = "SIGCHLD";
#endif

#if defined (SIGTTIN)   /* to readers pgrp upon background tty read */
  signal_names[SIGTTIN] = "SIGTTIN";
#endif

#if defined (SIGTTOU)   /* like TTIN for output if (tp->t_local&LTOSTOP) */
  signal_names[SIGTTOU] = "SIGTTOU";
#endif

#if defined (SIGIO) /* input/output possible signal */
  signal_names[SIGIO] = "SIGIO";
#endif

#if defined (SIGXCPU)   /* exceeded CPU time limit */
  signal_names[SIGXCPU] = "SIGXCPU";
#endif

#if defined (SIGXFSZ)   /* exceeded file size limit */
  signal_names[SIGXFSZ] = "SIGXFSZ";
#endif

#if defined (SIGVTALRM) /* virtual time alarm */
  signal_names[SIGVTALRM] = "SIGVTALRM";
#endif

#if defined (SIGPROF)   /* profiling time alarm */
  signal_names[SIGPROF] = "SIGPROF";
#endif

#if defined (SIGWINCH)  /* window changed */
  signal_names[SIGWINCH] = "SIGWINCH";
#endif

/* 4.4 BSD */
#if defined (SIGINFO) && !defined (_SEQUENT_)   /* information request */
  signal_names[SIGINFO] = "SIGINFO";
#endif

#if defined (SIGUSR1)   /* user defined signal 1 */
  signal_names[SIGUSR1] = "SIGUSR1";
#endif

#if defined (SIGUSR2)   /* user defined signal 2 */
  signal_names[SIGUSR2] = "SIGUSR2";
#endif

#if defined (SIGKILLTHR)    /* BeOS: Kill Thread */
  signal_names[SIGKILLTHR] = "SIGKILLTHR";
#endif

  for (i = 0; i < NSIG; i++)
    if (signal_names[i] == (char *)NULL)
      {
    signal_names[i] = (char *)malloc (18);
    if (signal_names[i])
      sprintf (signal_names[i], "SIGJUNK(%d)", i);
      }

  signal_names[NSIG] = "DEBUG";
  signal_names[NSIG+1] = "ERR";
}

void
write_signames (stream)
     FILE *stream;
{
  register int i;

  fprintf (stream, "/* This file was automatically created by %s.\n",
       progname);
  fprintf (stream, "   Do not edit.  Edit support/mksignames.c instead. */\n\n");
  fprintf (stream,
       "/* A translation list so we can be polite to our users. */\n");
  fprintf (stream, "char *signal_names[NSIG + 3] = {\n");

  for (i = 0; i <= LASTSIG; i++)
    fprintf (stream, "    \"%s\",\n", signal_names[i]);

  fprintf (stream, "    (char *)0x0\n");
  fprintf (stream, "};\n");
}

int
main (argc, argv)
     int argc;
     char **argv;
{
  char *stream_name;
  FILE *stream;

  progname = argv[0];

  if (argc == 1)
    {
      stream_name = "stdout";
      stream = stdout;
    }
  else if (argc == 2)
    {
      stream_name = argv[1];
      stream = fopen (stream_name, "w");
    }
  else
    {
      fprintf (stderr, "Usage: %s [output-file]\n", progname);
      exit (1);
    }

  if (!stream)
    {
      fprintf (stderr, "%s: %s: cannot open for writing\n",
           progname, stream_name);
      exit (2);
    }

  initialize_signames ();
  write_signames (stream);
  exit (0);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/trap.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/trap.c used in: Step 1224
/* trap.c -- Not the trap command, but useful functions for manipulating
   those objects.  The trap command is in builtins/trap.def. */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "bashtypes.h"
#include "bashansi.h"

#include <stdio.h>
#include <errno.h>

#include "trap.h"

#include "shell.h"
#include "input.h"  /* for save_token_state, restore_token_state */
#include "signames.h"
#include "builtins.h"
#include "builtins/common.h"
#include "builtins/builtext.h"

#ifndef errno
extern int errno;
#endif

/* Flags which describe the current handling state of a signal. */
#define SIG_INHERITED   0x0 /* Value inherited from parent. */
#define SIG_TRAPPED     0x1 /* Currently trapped. */
#define SIG_HARD_IGNORE 0x2 /* Signal was ignored on shell entry. */
#define SIG_SPECIAL     0x4 /* Treat this signal specially. */
#define SIG_NO_TRAP     0x8 /* Signal cannot be trapped. */
#define SIG_INPROGRESS  0x10    /* Signal handler currently executing. */
#define SIG_CHANGED 0x20    /* Trap value changed in trap handler. */
#define SIG_IGNORED 0x40    /* The signal is currently being ignored. */

#define SPECIAL_TRAP(s) ((s) == EXIT_TRAP || (s) == DEBUG_TRAP || (s) == ERROR_TRAP)

/* An array of such flags, one for each signal, describing what the
   shell will do with a signal.  DEBUG_TRAP == NSIG; some code below
   assumes this. */
static int sigmodes[BASH_NSIG];

static void free_trap_command __P((int));
static void change_signal __P((int, char *));

static void get_original_signal __P((int));

static void _run_trap_internal __P((int, char *));

static void reset_signal __P((int));
static void restore_signal __P((int));
static void reset_or_restore_signal_handlers __P((sh_resetsig_func_t *));

/* Variables used here but defined in other files. */
extern int interrupt_immediately;
extern int last_command_exit_value;
extern int line_number;

extern sh_builtin_func_t *this_shell_builtin;
extern procenv_t wait_intr_buf;

/* The list of things to do originally, before we started trapping. */
SigHandler *original_signals[NSIG];

/* For each signal, a slot for a string, which is a command to be
   executed when that signal is recieved.  The slot can also contain
   DEFAULT_SIG, which means do whatever you were going to do before
   you were so rudely interrupted, or IGNORE_SIG, which says ignore
   this signal. */
char *trap_list[BASH_NSIG];

/* A bitmap of signals received for which we have trap handlers. */
int pending_traps[NSIG];

/* Set to the number of the signal we're running the trap for + 1.
   Used in execute_cmd.c and builtins/common.c to clean up when
   parse_and_execute does not return normally after executing the
   trap command (e.g., when `return' is executed in the trap command). */
int running_trap;

/* The value of line_number when the trap started executing, since
   parse_and_execute resets it to 1 and the trap command might want
   it. */
int trap_line_number;

/* The (trapped) signal received while executing in the `wait' builtin */
int wait_signal_received;

/* A value which can never be the target of a trap handler. */
#define IMPOSSIBLE_TRAP_HANDLER (SigHandler *)initialize_traps

void
initialize_traps ()
{
  register int i;

  trap_list[EXIT_TRAP] = trap_list[DEBUG_TRAP] = trap_list[ERROR_TRAP] = (char *)NULL;
  sigmodes[EXIT_TRAP] = sigmodes[DEBUG_TRAP] = sigmodes[ERROR_TRAP] = SIG_INHERITED;
  original_signals[EXIT_TRAP] = IMPOSSIBLE_TRAP_HANDLER;

  for (i = 1; i < NSIG; i++)
    {
      pending_traps[i] = 0;
      trap_list[i] = (char *)DEFAULT_SIG;
      sigmodes[i] = SIG_INHERITED;
      original_signals[i] = IMPOSSIBLE_TRAP_HANDLER;
    }

  /* Show which signals are treated specially by the shell. */
#if defined (SIGCHLD)
  original_signals[SIGCHLD] =
    (SigHandler *) set_signal_handler (SIGCHLD, SIG_DFL);
  set_signal_handler (SIGCHLD, original_signals[SIGCHLD]);
  sigmodes[SIGCHLD] |= (SIG_SPECIAL | SIG_NO_TRAP);
#endif /* SIGCHLD */

  original_signals[SIGINT] =
    (SigHandler *) set_signal_handler (SIGINT, SIG_DFL);
  set_signal_handler (SIGINT, original_signals[SIGINT]);
  sigmodes[SIGINT] |= SIG_SPECIAL;

#if defined (__BEOS__)
  /* BeOS sets SIGINT to SIG_IGN! */
  original_signals[SIGINT] = SIG_DFL;
#endif

  original_signals[SIGQUIT] =
    (SigHandler *) set_signal_handler (SIGQUIT, SIG_DFL);
  set_signal_handler (SIGQUIT, original_signals[SIGQUIT]);
  sigmodes[SIGQUIT] |= SIG_SPECIAL;

  if (interactive)
    {
      original_signals[SIGTERM] =
    (SigHandler *)set_signal_handler (SIGTERM, SIG_DFL);
      set_signal_handler (SIGTERM, original_signals[SIGTERM]);
      sigmodes[SIGTERM] |= SIG_SPECIAL;
    }
}

#ifdef INCLUDE_UNUSED
/* Return a printable representation of the trap handler for SIG. */
static char *
trap_handler_string (sig)
     int sig;
{
  if (trap_list[sig] == (char *)DEFAULT_SIG)
    return "DEFAULT_SIG";
  else if (trap_list[sig] == (char *)IGNORE_SIG)
    return "IGNORE_SIG";
  else if (trap_list[sig] == (char *)IMPOSSIBLE_TRAP_HANDLER)
    return "IMPOSSIBLE_TRAP_HANDLER";
  else if (trap_list[sig])
    return trap_list[sig];
  else
    return "NULL";
}
#endif

/* Return the print name of this signal. */
char *
signal_name (sig)
     int sig;
{
  char *ret;

  /* on cygwin32, signal_names[sig] could be null */
  ret = (sig >= BASH_NSIG || sig < 0) ? "bad signal number" : signal_names[sig];
  if (ret == NULL)
    ret = "unrecognized signal number";
  return ret;
}

/* Turn a string into a signal number, or a number into
   a signal number.  If STRING is "2", "SIGINT", or "INT",
   then (int)2 is returned.  Return NO_SIG if STRING doesn't
   contain a valid signal descriptor. */
int
decode_signal (string)
     char *string;
{
  intmax_t sig;

  if (legal_number (string, &sig))
    return ((sig >= 0 && sig < NSIG) ? (int)sig : NO_SIG);

  /* A leading `SIG' may be omitted. */
  for (sig = 0; sig < BASH_NSIG; sig++)
    {
      if (signal_names[sig] == 0 || signal_names[sig][0] == '\0')
    continue;
      if (strcasecmp (string, signal_names[sig]) == 0 ||
      (STREQN (signal_names[sig], "SIG", 3) &&
        strcasecmp (string, &(signal_names[sig])[3]) == 0))
    return ((int)sig);
    }

  return (NO_SIG);
}

/* Non-zero when we catch a trapped signal. */
static int catch_flag;

void
run_pending_traps ()
{
  register int sig;
  int old_exit_value, *token_state;

  if (catch_flag == 0)      /* simple optimization */
    return;

  catch_flag = 0;

  /* Preserve $? when running trap. */
  old_exit_value = last_command_exit_value;

  for (sig = 1; sig < NSIG; sig++)
    {
      /* XXX this could be made into a counter by using
     while (pending_traps[sig]--) instead of the if statement. */
      if (pending_traps[sig])
    {
#if defined (HAVE_POSIX_SIGNALS)
      sigset_t set, oset;

      sigemptyset (&set);
      sigemptyset (&oset);

      sigaddset (&set, sig);
      sigprocmask (SIG_BLOCK, &set, &oset);
#else
#  if defined (HAVE_BSD_SIGNALS)
      int oldmask = sigblock (sigmask (sig));
#  endif
#endif /* HAVE_POSIX_SIGNALS */

      if (sig == SIGINT)
        {
          run_interrupt_trap ();
          CLRINTERRUPT;
        }
      else if (trap_list[sig] == (char *)DEFAULT_SIG ||
           trap_list[sig] == (char *)IGNORE_SIG ||
           trap_list[sig] == (char *)IMPOSSIBLE_TRAP_HANDLER)
        {
          /* This is possible due to a race condition.  Say a bash
         process has SIGTERM trapped.  A subshell is spawned
         using { list; } & and the parent does something and kills
         the subshell with SIGTERM.  It's possible for the subshell
         to set pending_traps[SIGTERM] to 1 before the code in
         execute_cmd.c eventually calls restore_original_signals
         to reset the SIGTERM signal handler in the subshell.  The
         next time run_pending_traps is called, pending_traps[SIGTERM]
         will be 1, but the trap handler in trap_list[SIGTERM] will
         be invalid (probably DEFAULT_SIG, but it could be IGNORE_SIG).
         Unless we catch this, the subshell will dump core when
         trap_list[SIGTERM] == DEFAULT_SIG, because DEFAULT_SIG is
         usually 0x0. */
          internal_warning ("run_pending_traps: bad value in trap_list[%d]: %p",
                sig, trap_list[sig]);
          if (trap_list[sig] == (char *)DEFAULT_SIG)
        {
          internal_warning ("run_pending_traps: signal handler is SIG_DFL, resending %d (%s) to myself", sig, signal_name (sig));
          kill (getpid (), sig);
        }
        }
      else
        {
          token_state = save_token_state ();
          parse_and_execute (savestring (trap_list[sig]), "trap", SEVAL_NONINT|SEVAL_NOHIST);
          restore_token_state (token_state);
          free (token_state);
        }

      pending_traps[sig] = 0;

#if defined (HAVE_POSIX_SIGNALS)
      sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL);
#else
#  if defined (HAVE_BSD_SIGNALS)
      sigsetmask (oldmask);
#  endif
#endif /* POSIX_VERSION */
    }
    }

  last_command_exit_value = old_exit_value;
}

sighandler
trap_handler (sig)
     int sig;
{
  int oerrno;

  if ((sig >= NSIG) ||
      (trap_list[sig] == (char *)DEFAULT_SIG) ||
      (trap_list[sig] == (char *)IGNORE_SIG))
    programming_error ("trap_handler: bad signal %d", sig);
  else
    {
      oerrno = errno;
#if defined (MUST_REINSTALL_SIGHANDLERS)
      set_signal_handler (sig, trap_handler);
#endif /* MUST_REINSTALL_SIGHANDLERS */

      catch_flag = 1;
      pending_traps[sig]++;

      if (interrupt_immediately && this_shell_builtin && (this_shell_builtin == wait_builtin))
    {
      wait_signal_received = sig;
      longjmp (wait_intr_buf, 1);
    }

      if (interrupt_immediately)
    run_pending_traps ();

      errno = oerrno;
    }

  SIGRETURN (0);
}

#if defined (JOB_CONTROL) && defined (SIGCHLD)

#ifdef INCLUDE_UNUSED
/* Make COMMAND_STRING be executed when SIGCHLD is caught. */
void
set_sigchld_trap (command_string)
     char *command_string;
{
  set_signal (SIGCHLD, command_string);
}
#endif

/* Make COMMAND_STRING be executed when SIGCHLD is caught iff the current
   SIGCHLD trap handler is DEFAULT_SIG. */
void
maybe_set_sigchld_trap (command_string)
     char *command_string;
{
  if ((sigmodes[SIGCHLD] & SIG_TRAPPED) == 0)
    set_signal (SIGCHLD, command_string);
}
#endif /* JOB_CONTROL && SIGCHLD */

void
set_debug_trap (command)
     char *command;
{
  set_signal (DEBUG_TRAP, command);
}

void
set_error_trap (command)
     char *command;
{
  set_signal (ERROR_TRAP, command);
}

#ifdef INCLUDE_UNUSED
void
set_sigint_trap (command)
     char *command;
{
  set_signal (SIGINT, command);
}
#endif

/* Reset the SIGINT handler so that subshells that are doing `shellsy'
   things, like waiting for command substitution or executing commands
   in explicit subshells ( ( cmd ) ), can catch interrupts properly. */
SigHandler *
set_sigint_handler ()
{
  if (sigmodes[SIGINT] & SIG_HARD_IGNORE)
    return ((SigHandler *)SIG_IGN);

  else if (sigmodes[SIGINT] & SIG_IGNORED)
    return ((SigHandler *)set_signal_handler (SIGINT, SIG_IGN)); /* XXX */

  else if (sigmodes[SIGINT] & SIG_TRAPPED)
    return ((SigHandler *)set_signal_handler (SIGINT, trap_handler));

  /* The signal is not trapped, so set the handler to the shell's special
     interrupt handler. */
  else if (interactive) /* XXX - was interactive_shell */
    return (set_signal_handler (SIGINT, sigint_sighandler));
  else
    return (set_signal_handler (SIGINT, termination_unwind_protect));
}

/* Return the correct handler for signal SIG according to the values in
   sigmodes[SIG]. */
SigHandler *
trap_to_sighandler (sig)
     int sig;
{
  if (sigmodes[sig] & (SIG_IGNORED|SIG_HARD_IGNORE))
    return (SIG_IGN);
  else if (sigmodes[sig] & SIG_TRAPPED)
    return (trap_handler);
  else
    return (SIG_DFL);
}

/* Set SIG to call STRING as a command. */
void
set_signal (sig, string)
     int sig;
     char *string;
{
  if (SPECIAL_TRAP (sig))
    {
      change_signal (sig, savestring (string));
      if (sig == EXIT_TRAP && interactive == 0)
    initialize_terminating_signals ();
      return;
    }

  /* A signal ignored on entry to the shell cannot be trapped or reset, but
     no error is reported when attempting to do so.  -- Posix.2 */
  if (sigmodes[sig] & SIG_HARD_IGNORE)
    return;

  /* Make sure we have original_signals[sig] if the signal has not yet
     been trapped. */
  if ((sigmodes[sig] & SIG_TRAPPED) == 0)
    {
      /* If we aren't sure of the original value, check it. */
      if (original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER)
    {
      original_signals[sig] = (SigHandler *)set_signal_handler (sig, SIG_DFL);
      set_signal_handler (sig, original_signals[sig]);
    }

      /* Signals ignored on entry to the shell cannot be trapped or reset. */
      if (original_signals[sig] == SIG_IGN)
    {
      sigmodes[sig] |= SIG_HARD_IGNORE;
      return;
    }
    }

  /* Only change the system signal handler if SIG_NO_TRAP is not set.
     The trap command string is changed in either case.  The shell signal
     handlers for SIGINT and SIGCHLD run the user specified traps in an
     environment in which it is safe to do so. */
  if ((sigmodes[sig] & SIG_NO_TRAP) == 0)
    {
      set_signal_handler (sig, SIG_IGN);
      change_signal (sig, savestring (string));
      set_signal_handler (sig, trap_handler);
    }
  else
    change_signal (sig, savestring (string));
}

static void
free_trap_command (sig)
     int sig;
{
  if ((sigmodes[sig] & SIG_TRAPPED) && trap_list[sig] &&
      (trap_list[sig] != (char *)IGNORE_SIG) &&
      (trap_list[sig] != (char *)DEFAULT_SIG) &&
      (trap_list[sig] != (char *)IMPOSSIBLE_TRAP_HANDLER))
    free (trap_list[sig]);
}

/* If SIG has a string assigned to it, get rid of it.  Then give it
   VALUE. */
static void
change_signal (sig, value)
     int sig;
     char *value;
{
  if ((sigmodes[sig] & SIG_INPROGRESS) == 0)
    free_trap_command (sig);
  trap_list[sig] = value;

  sigmodes[sig] |= SIG_TRAPPED;
  if (value == (char *)IGNORE_SIG)
    sigmodes[sig] |= SIG_IGNORED;
  else
    sigmodes[sig] &= ~SIG_IGNORED;
  if (sigmodes[sig] & SIG_INPROGRESS)
    sigmodes[sig] |= SIG_CHANGED;
}

#define GET_ORIGINAL_SIGNAL(sig) \
  if (sig && sig < NSIG && original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER) \
    get_original_signal (sig)

static void
get_original_signal (sig)
     int sig;
{
  /* If we aren't sure the of the original value, then get it. */
  if (original_signals[sig] == (SigHandler *)IMPOSSIBLE_TRAP_HANDLER)
    {
      original_signals[sig] =
    (SigHandler *) set_signal_handler (sig, SIG_DFL);
      set_signal_handler (sig, original_signals[sig]);

      /* Signals ignored on entry to the shell cannot be trapped. */
      if (original_signals[sig] == SIG_IGN)
    sigmodes[sig] |= SIG_HARD_IGNORE;
    }
}

/* Restore the default action for SIG; i.e., the action the shell
   would have taken before you used the trap command.  This is called
   from trap_builtin (), which takes care to restore the handlers for
   the signals the shell treats specially. */
void
restore_default_signal (sig)
     int sig;
{
  if (SPECIAL_TRAP (sig))
    {
      if ((sig != DEBUG_TRAP && sig != ERROR_TRAP) || (sigmodes[sig] & SIG_INPROGRESS) == 0)
    free_trap_command (sig);
      trap_list[sig] = (char *)NULL;
      sigmodes[sig] &= ~SIG_TRAPPED;
      if (sigmodes[sig] & SIG_INPROGRESS)
    sigmodes[sig] |= SIG_CHANGED;
      return;
    }

  GET_ORIGINAL_SIGNAL (sig);

  /* A signal ignored on entry to the shell cannot be trapped or reset, but
     no error is reported when attempting to do so.  Thanks Posix.2. */
  if (sigmodes[sig] & SIG_HARD_IGNORE)
    return;

  /* If we aren't trapping this signal, don't bother doing anything else. */
  if ((sigmodes[sig] & SIG_TRAPPED) == 0)
    return;

  /* Only change the signal handler for SIG if it allows it. */
  if ((sigmodes[sig] & SIG_NO_TRAP) == 0)
    set_signal_handler (sig, original_signals[sig]);

  /* Change the trap command in either case. */
  change_signal (sig, (char *)DEFAULT_SIG);

  /* Mark the signal as no longer trapped. */
  sigmodes[sig] &= ~SIG_TRAPPED;
}

/* Make this signal be ignored. */
void
ignore_signal (sig)
     int sig;
{
  if (SPECIAL_TRAP (sig) && ((sigmodes[sig] & SIG_IGNORED) == 0))
    {
      change_signal (sig, (char *)IGNORE_SIG);
      return;
    }

  GET_ORIGINAL_SIGNAL (sig);

  /* A signal ignored on entry to the shell cannot be trapped or reset.
     No error is reported when the user attempts to do so. */
  if (sigmodes[sig] & SIG_HARD_IGNORE)
    return;

  /* If already trapped and ignored, no change necessary. */
  if (sigmodes[sig] & SIG_IGNORED)
    return;

  /* Only change the signal handler for SIG if it allows it. */
  if ((sigmodes[sig] & SIG_NO_TRAP) == 0)
    set_signal_handler (sig, SIG_IGN);

  /* Change the trap command in either case. */
  change_signal (sig, (char *)IGNORE_SIG);
}

/* Handle the calling of "trap 0".  The only sticky situation is when
   the command to be executed includes an "exit".  This is why we have
   to provide our own place for top_level to jump to. */
int
run_exit_trap ()
{
  char *trap_command;
  int code, old_exit_value;

  old_exit_value = last_command_exit_value;

  /* Run the trap only if signal 0 is trapped and not ignored, and we are not
     currently running in the trap handler (call to exit in the list of
     commands given to trap 0). */
  if ((sigmodes[EXIT_TRAP] & SIG_TRAPPED) &&
      (sigmodes[EXIT_TRAP] & (SIG_IGNORED|SIG_INPROGRESS)) == 0)
    {
      trap_command = savestring (trap_list[EXIT_TRAP]);
      sigmodes[EXIT_TRAP] &= ~SIG_TRAPPED;
      sigmodes[EXIT_TRAP] |= SIG_INPROGRESS;

      code = setjmp (top_level);

      if (code == 0)
    {
      reset_parser ();
      parse_and_execute (trap_command, "exit trap", SEVAL_NONINT|SEVAL_NOHIST);
    }
      else if (code == EXITPROG)
    return (last_command_exit_value);
      else
    return (old_exit_value);
    }

  return (old_exit_value);
}

void
run_trap_cleanup (sig)
     int sig;
{
  sigmodes[sig] &= ~(SIG_INPROGRESS|SIG_CHANGED);
}

/* Run a trap command for SIG.  SIG is one of the signals the shell treats
   specially. */
static void
_run_trap_internal (sig, tag)
     int sig;
     char *tag;
{
  char *trap_command, *old_trap;
  int old_exit_value, *token_state;

  /* Run the trap only if SIG is trapped and not ignored, and we are not
     currently executing in the trap handler. */
  if ((sigmodes[sig] & SIG_TRAPPED) && ((sigmodes[sig] & SIG_IGNORED) == 0) &&
      (trap_list[sig] != (char *)IMPOSSIBLE_TRAP_HANDLER) &&
      ((sigmodes[sig] & SIG_INPROGRESS) == 0))
    {
      old_trap = trap_list[sig];
      sigmodes[sig] |= SIG_INPROGRESS;
      sigmodes[sig] &= ~SIG_CHANGED;        /* just to be sure */
      trap_command =  savestring (old_trap);

      running_trap = sig + 1;
      old_exit_value = last_command_exit_value;
      /* Need to copy the value of line_number because parse_and_execute
     resets it to 1, and the trap command might want it. */
      trap_line_number = line_number;

      token_state = save_token_state ();
      parse_and_execute (trap_command, tag, SEVAL_NONINT|SEVAL_NOHIST);
      restore_token_state (token_state);
      free (token_state);

      last_command_exit_value = old_exit_value;
      running_trap = 0;

      sigmodes[sig] &= ~SIG_INPROGRESS;

      if (sigmodes[sig] & SIG_CHANGED)
    {
      free (old_trap);
      sigmodes[sig] &= ~SIG_CHANGED;
    }
    }
}

void
run_debug_trap ()
{
  if ((sigmodes[DEBUG_TRAP] & SIG_TRAPPED) && ((sigmodes[DEBUG_TRAP] & SIG_INPROGRESS) == 0))
    _run_trap_internal (DEBUG_TRAP, "debug trap");
}

void
run_error_trap ()
{
  if ((sigmodes[ERROR_TRAP] & SIG_TRAPPED) && (sigmodes[ERROR_TRAP] & SIG_INPROGRESS) == 0)
    _run_trap_internal (ERROR_TRAP, "error trap");
}

/* Run a trap set on SIGINT.  This is called from throw_to_top_level (), and
   declared here to localize the trap functions. */
void
run_interrupt_trap ()
{
  _run_trap_internal (SIGINT, "interrupt trap");
}

#ifdef INCLUDE_UNUSED
/* Free all the allocated strings in the list of traps and reset the trap
   values to the default. */
void
free_trap_strings ()
{
  register int i;

  for (i = 0; i < BASH_NSIG; i++)
    {
      free_trap_command (i);
      trap_list[i] = (char *)DEFAULT_SIG;
      sigmodes[i] &= ~SIG_TRAPPED;
    }
  trap_list[DEBUG_TRAP] = trap_list[EXIT_TRAP] = trap_list[ERROR_TRAP] = (char *)NULL;
}
#endif

/* Reset the handler for SIG to the original value. */
static void
reset_signal (sig)
     int sig;
{
  set_signal_handler (sig, original_signals[sig]);
  sigmodes[sig] &= ~SIG_TRAPPED;
}

/* Set the handler signal SIG to the original and free any trap
   command associated with it. */
static void
restore_signal (sig)
     int sig;
{
  set_signal_handler (sig, original_signals[sig]);
  change_signal (sig, (char *)DEFAULT_SIG);
  sigmodes[sig] &= ~SIG_TRAPPED;
}

static void
reset_or_restore_signal_handlers (reset)
     sh_resetsig_func_t *reset;
{
  register int i;

  /* Take care of the exit trap first */
  if (sigmodes[EXIT_TRAP] & SIG_TRAPPED)
    {
      free_trap_command (EXIT_TRAP);
      trap_list[EXIT_TRAP] = (char *)NULL;
      sigmodes[EXIT_TRAP] &= ~SIG_TRAPPED;
    }
  for (i = 1; i < NSIG; i++)
    {
      if (sigmodes[i] & SIG_TRAPPED)
    {
      if (trap_list[i] == (char *)IGNORE_SIG)
        set_signal_handler (i, SIG_IGN);
      else
        (*reset) (i);
    }
      else if (sigmodes[i] & SIG_SPECIAL)
    (*reset) (i);
    }

  /* Command substitution and other child processes don't inherit the
     debug or error traps. */
  sigmodes[DEBUG_TRAP] &= ~SIG_TRAPPED;  
  sigmodes[ERROR_TRAP] &= ~SIG_TRAPPED;
}

/* Reset trapped signals to their original values, but don't free the
   trap strings.  Called by the command substitution code. */
void
reset_signal_handlers ()
{
  reset_or_restore_signal_handlers (reset_signal);
}

/* Reset all trapped signals to their original values.  Signals set to be
   ignored with trap '' SIGNAL should be ignored, so we make sure that they
   are.  Called by child processes after they are forked. */
void
restore_original_signals ()
{
  reset_or_restore_signal_handlers (restore_signal);
}

/* If a trap handler exists for signal SIG, then call it; otherwise just
   return failure. */
int
maybe_call_trap_handler (sig)
     int sig;
{
  /* Call the trap handler for SIG if the signal is trapped and not ignored. */
  if ((sigmodes[sig] & SIG_TRAPPED) && ((sigmodes[sig] & SIG_IGNORED) == 0))
    {
      switch (sig)
    {
    case SIGINT:
      run_interrupt_trap ();
      break;
    case EXIT_TRAP:
      run_exit_trap ();
      break;
    case DEBUG_TRAP:
      run_debug_trap ();
      break;
    case ERROR_TRAP:
      run_error_trap ();
      break;
    default:
      trap_handler (sig);
      break;
    }
      return (1);
    }
  else
    return (0);
}

int
signal_is_trapped (sig)
     int sig;
{
  return (sigmodes[sig] & SIG_TRAPPED);
}

int
signal_is_special (sig)
     int sig;
{
  return (sigmodes[sig] & SIG_SPECIAL);
}

int
signal_is_ignored (sig)
     int sig;
{
  return (sigmodes[sig] & SIG_IGNORED);
}

void
set_signal_ignored (sig)
     int sig;
{
  sigmodes[sig] |= SIG_HARD_IGNORE;
  original_signals[sig] = SIG_IGN;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/input.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/input.c used in: Step 1225
/* input.c -- functions to perform buffered input with synchronization. */

/* Copyright (C) 1992 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include "bashtypes.h"
#ifndef _MINIX
#  include <sys/file.h>
#endif
#include "filecntl.h"
#include "posixstat.h"
#include <stdio.h>
#include <errno.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "bashansi.h"
#include "command.h"
#include "general.h"
#include "input.h"
#include "error.h"
#include "externs.h"

#if !defined (errno)
extern int errno;
#endif /* !errno */

/* Functions to handle reading input on systems that don't restart read(2)
   if a signal is received. */

static char localbuf[128];
static int local_index, local_bufused;

/* Posix and USG systems do not guarantee to restart read () if it is
   interrupted by a signal.  We do the read ourselves, and restart it
   if it returns EINTR. */
int
getc_with_restart (stream)
     FILE *stream;
{
  unsigned char uc;

  /* Try local buffering to reduce the number of read(2) calls. */
  if (local_index == local_bufused || local_bufused == 0)
    {
      while (1)
    {
      local_bufused = read (fileno (stream), localbuf, sizeof(localbuf));
      if (local_bufused > 0)
        break;
      else if (local_bufused == 0 || errno != EINTR)
        {
          local_index = 0;
          return EOF;
        }
    }
      local_index = 0;
    }
  uc = localbuf[local_index++];
  return uc;
}

int
ungetc_with_restart (c, stream)
     int c;
     FILE *stream;
{
  if (local_index == 0 || c == EOF)
    return EOF;
  localbuf[--local_index] = c;
  return c;
}

#if defined (BUFFERED_INPUT)

/* A facility similar to stdio, but input-only. */

#if defined (USING_BASH_MALLOC)
#  define MAX_INPUT_BUFFER_SIZE 8176
#else
#  define MAX_INPUT_BUFFER_SIZE 8192
#endif

#if !defined (SEEK_CUR)
#  define SEEK_CUR 1
#endif /* !SEEK_CUR */

#ifdef max
#  undef max
#endif
#define max(a, b)   (((a) > (b)) ? (a) : (b))
#ifdef min
#  undef min
#endif
#define min(a, b)   ((a) > (b) ? (b) : (a))

extern int interactive_shell;

int bash_input_fd_changed;

/* This provides a way to map from a file descriptor to the buffer
   associated with that file descriptor, rather than just the other
   way around.  This is needed so that buffers are managed properly
   in constructs like 3<&4.  buffers[x]->b_fd == x -- that is how the
   correspondence is maintained. */
static BUFFERED_STREAM **buffers = (BUFFERED_STREAM **)NULL;
static int nbuffers;

#define ALLOCATE_BUFFERS(n) \
    do { if ((n) >= nbuffers) allocate_buffers (n); } while (0)

/* Make sure `buffers' has at least N elements. */
static void
allocate_buffers (n)
     int n;
{
  register int i, orig_nbuffers;

  orig_nbuffers = nbuffers;
  nbuffers = n + 20;
  buffers = (BUFFERED_STREAM **)xrealloc
    (buffers, nbuffers * sizeof (BUFFERED_STREAM *));

  /* Zero out the new buffers. */
  for (i = orig_nbuffers; i < nbuffers; i++)
    buffers[i] = (BUFFERED_STREAM *)NULL;
}

/* Construct and return a BUFFERED_STREAM corresponding to file descriptor
   FD, using BUFFER. */
static BUFFERED_STREAM *
make_buffered_stream (fd, buffer, bufsize)
     int fd;
     char *buffer;
     size_t bufsize;
{
  BUFFERED_STREAM *bp;

  bp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM));
  ALLOCATE_BUFFERS (fd);
  buffers[fd] = bp;
  bp->b_fd = fd;
  bp->b_buffer = buffer;
  bp->b_size = bufsize;
  bp->b_used = bp->b_inputp = bp->b_flag = 0;
  if (bufsize == 1)
    bp->b_flag |= B_UNBUFF;
  return (bp);
}

/* Allocate a new BUFFERED_STREAM, copy BP to it, and return the new copy. */
static BUFFERED_STREAM *
copy_buffered_stream (bp)
     BUFFERED_STREAM *bp;
{
  BUFFERED_STREAM *nbp;

  if (!bp)
    return ((BUFFERED_STREAM *)NULL);

  nbp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM));
  xbcopy ((char *)bp, (char *)nbp, sizeof (BUFFERED_STREAM));
  return (nbp);
}

int
set_bash_input_fd (fd)
     int fd;
{
  if (bash_input.type == st_bstream)
    bash_input.location.buffered_fd = fd;
  else if (interactive_shell == 0)
    default_buffered_input = fd;
  return 0;
}

int
fd_is_bash_input (fd)
     int fd;
{
  if (bash_input.type == st_bstream && bash_input.location.buffered_fd == fd)
    return 1;
  else if (interactive_shell == 0 && default_buffered_input == fd)
    return 1;
  return 0;
}

/* Save the buffered stream corresponding to file descriptor FD (which bash
   is using to read input) to a buffered stream associated with NEW_FD.  If
   NEW_FD is -1, a new file descriptor is allocated with fcntl.  The new
   file descriptor is returned on success, -1 on error. */
int
save_bash_input (fd, new_fd)
     int fd, new_fd;
{
  int nfd;

  /* Sync the stream so we can re-read from the new file descriptor.  We
     might be able to avoid this by copying the buffered stream verbatim
     to the new file descriptor. */
  if (buffers[fd])
    sync_buffered_stream (fd);

  /* Now take care of duplicating the file descriptor that bash is
     using for input, so we can reinitialize it later. */
  nfd = (new_fd == -1) ? fcntl (fd, F_DUPFD, 10) : new_fd;
  if (nfd == -1)
    {
      if (fcntl (fd, F_GETFD, 0) == 0)
    sys_error ("cannot allocate new file descriptor for bash input from fd %d", fd);
      return -1;
    }

  if (buffers[nfd])
    {
      /* What's this?  A stray buffer without an associated open file
     descriptor?  Free up the buffer and report the error. */
      internal_error ("check_bash_input: buffer already exists for new fd %d", nfd);
      free_buffered_stream (buffers[nfd]);
    }

  /* Reinitialize bash_input.location. */
  if (bash_input.type == st_bstream)
    {
      bash_input.location.buffered_fd = nfd;
      fd_to_buffered_stream (nfd);
      close_buffered_fd (fd);   /* XXX */
    }
  else
    /* If the current input type is not a buffered stream, but the shell
       is not interactive and therefore using a buffered stream to read
       input (e.g. with an `eval exec 3>output' inside a script), note
       that the input fd has been changed.  pop_stream() looks at this
       value and adjusts the input fd to the new value of
       default_buffered_input accordingly. */
    bash_input_fd_changed++;

  if (default_buffered_input == fd)
    default_buffered_input = nfd;

  SET_CLOSE_ON_EXEC (nfd);
  return nfd;
}

/* Check that file descriptor FD is not the one that bash is currently
   using to read input from a script.  FD is about to be duplicated onto,
   which means that the kernel will close it for us.  If FD is the bash
   input file descriptor, we need to seek backwards in the script (if
   possible and necessary -- scripts read from stdin are still unbuffered),
   allocate a new file descriptor to use for bash input, and re-initialize
   the buffered stream.  Make sure the file descriptor used to save bash
   input is set close-on-exec. Returns 0 on success, -1 on failure.  This
   works only if fd is > 0 -- if fd == 0 and bash is reading input from
   fd 0, save_bash_input is used instead, to cooperate with input
   redirection (look at redir.c:add_undo_redirect()). */
int
check_bash_input (fd)
     int fd;
{
  if (fd > 0 && fd_is_bash_input (fd))
    return ((save_bash_input (fd, -1) == -1) ? -1 : 0);
  return 0;
}
      
/* This is the buffered stream analogue of dup2(fd1, fd2).  The
   BUFFERED_STREAM corresponding to fd2 is deallocated, if one exists.
   BUFFERS[fd1] is copied to BUFFERS[fd2].  This is called by the
   redirect code for constructs like 4<&0 and 3</etc/rc.local. */
int
duplicate_buffered_stream (fd1, fd2)
     int fd1, fd2;
{
  int is_bash_input, m;

  if (fd1 == fd2)
    return 0;

  m = max (fd1, fd2);
  ALLOCATE_BUFFERS (m);

  /* If FD2 is the file descriptor bash is currently using for shell input,
     we need to do some extra work to make sure that the buffered stream
     actually exists (it might not if fd1 was not active, and the copy
     didn't actually do anything). */
  is_bash_input = (bash_input.type == st_bstream) &&
          (bash_input.location.buffered_fd == fd2);

  if (buffers[fd2])
    free_buffered_stream (buffers[fd2]);
  buffers[fd2] = copy_buffered_stream (buffers[fd1]);
  if (buffers[fd2])
    buffers[fd2]->b_fd = fd2;

  if (is_bash_input)
    {
      if (!buffers[fd2])
    fd_to_buffered_stream (fd2);
      buffers[fd2]->b_flag |= B_WASBASHINPUT;
    }

  return (fd2);
}

/* Return 1 if a seek on FD will succeed. */
#ifndef __CYGWIN__
#  define fd_is_seekable(fd) (lseek ((fd), 0L, SEEK_CUR) >= 0)
#else
#  define fd_is_seekable(fd) 0
#endif /* __CYGWIN__ */

/* Take FD, a file descriptor, and create and return a buffered stream
   corresponding to it.  If something is wrong and the file descriptor
   is invalid, return a NULL stream. */
BUFFERED_STREAM *
fd_to_buffered_stream (fd)
     int fd;
{
  char *buffer;
  size_t size;
  struct stat sb;

  if (fstat (fd, &sb) < 0)
    {
      close (fd);
      return ((BUFFERED_STREAM *)NULL);
    }

  size = (fd_is_seekable (fd)) ? min (sb.st_size, MAX_INPUT_BUFFER_SIZE) : 1;
  if (size == 0)
    size = 1;
  buffer = (char *)xmalloc (size);

  return (make_buffered_stream (fd, buffer, size));
}

/* Return a buffered stream corresponding to FILE, a file name. */
BUFFERED_STREAM *
open_buffered_stream (file)
     char *file;
{
  int fd;

  fd = open (file, O_RDONLY);
  return ((fd >= 0) ? fd_to_buffered_stream (fd) : (BUFFERED_STREAM *)NULL);
}

/* Deallocate a buffered stream and free up its resources.  Make sure we
   zero out the slot in BUFFERS that points to BP. */
void
free_buffered_stream (bp)
     BUFFERED_STREAM *bp;
{
  int n;

  if (!bp)
    return;

  n = bp->b_fd;
  if (bp->b_buffer)
    free (bp->b_buffer);
  free (bp);
  buffers[n] = (BUFFERED_STREAM *)NULL;
}

/* Close the file descriptor associated with BP, a buffered stream, and free
   up the stream.  Return the status of closing BP's file descriptor. */
int
close_buffered_stream (bp)
     BUFFERED_STREAM *bp;
{
  int fd;

  if (!bp)
    return (0);
  fd = bp->b_fd;
  free_buffered_stream (bp);
  return (close (fd));
}

/* Deallocate the buffered stream associated with file descriptor FD, and
   close FD.  Return the status of the close on FD. */
int
close_buffered_fd (fd)
     int fd;
{
  if (fd < 0)
    {
      errno = EBADF;
      return -1;
    }
  if (fd >= nbuffers || !buffers || !buffers[fd])
    return (close (fd));
  return (close_buffered_stream (buffers[fd]));
}

/* Make the BUFFERED_STREAM associcated with buffers[FD] be BP, and return
   the old BUFFERED_STREAM. */
BUFFERED_STREAM *
set_buffered_stream (fd, bp)
     int fd;
     BUFFERED_STREAM *bp;
{
  BUFFERED_STREAM *ret;

  ret = buffers[fd];
  buffers[fd] = bp;
  return ret;
}

/* Read a buffer full of characters from BP, a buffered stream. */
static int
b_fill_buffer (bp)
     BUFFERED_STREAM *bp;
{
  ssize_t nr;

  nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
  if (nr <= 0)
    {
      bp->b_used = 0;
      bp->b_buffer[0] = 0;
      if (nr == 0)
    bp->b_flag |= B_EOF;
      else
    bp->b_flag |= B_ERROR;
      return (EOF);
    }

#if defined (__CYGWIN__)
  /* If on cygwin, translate \r\n to \n. */
  if (nr >= 2 && bp->b_buffer[nr - 2] == '\r' && bp->b_buffer[nr - 1] == '\n')
    {
      bp->b_buffer[nr - 2] = '\n';
      nr--;
    }
#endif

  bp->b_used = nr;
  bp->b_inputp = 0;
  return (bp->b_buffer[bp->b_inputp++] & 0xFF);
}

/* Get a character from buffered stream BP. */
#define bufstream_getc(bp) \
  (bp->b_inputp == bp->b_used || !bp->b_used) \
        ? b_fill_buffer (bp) \
        : bp->b_buffer[bp->b_inputp++] & 0xFF

/* Push C back onto buffered stream BP. */
static int
bufstream_ungetc(c, bp)
     int c;
     BUFFERED_STREAM *bp;
{
  if (c == EOF || bp->b_inputp == 0)
    return (EOF);

  bp->b_buffer[--bp->b_inputp] = c;
  return (c);
}

/* Seek backwards on file BFD to synchronize what we've read so far
   with the underlying file pointer. */
int
sync_buffered_stream (bfd)
     int bfd;
{
  BUFFERED_STREAM *bp;
  off_t chars_left;

  if (buffers == 0 || (bp = buffers[bfd]) == 0)
    return (-1);

  chars_left = bp->b_used - bp->b_inputp;
  if (chars_left)
    lseek (bp->b_fd, -chars_left, SEEK_CUR);
  bp->b_used = bp->b_inputp = 0;
  return (0);
}

int
buffered_getchar ()
{
#if !defined (DJGPP)
  return (bufstream_getc (buffers[bash_input.location.buffered_fd]));
#else
  /* On DJGPP, ignore \r. */
  int ch;
  while ((ch = bufstream_getc (buffers[bash_input.location.buffered_fd])) == '\r')
    ;
  return ch;
#endif
}

int
buffered_ungetchar (c)
     int c;
{
  return (bufstream_ungetc (c, buffers[bash_input.location.buffered_fd]));
}

/* Make input come from file descriptor BFD through a buffered stream. */
void
with_input_from_buffered_stream (bfd, name)
     int bfd;
     char *name;
{
  INPUT_STREAM location;
  BUFFERED_STREAM *bp;

  location.buffered_fd = bfd;
  /* Make sure the buffered stream exists. */
  bp = fd_to_buffered_stream (bfd);
  init_yy_io (bp == 0 ? return_EOF : buffered_getchar,
          buffered_ungetchar, st_bstream, name, location);
}

#if defined (TEST)
void *
xmalloc(s)
int s;
{
    return (malloc (s));
}

void *
xrealloc(s, size)
char    *s;
int size;
{
    if (!s)
        return(malloc (size));
    else
        return(realloc (s, size));
}

void
init_yy_io ()
{
}

process(bp)
BUFFERED_STREAM *bp;
{
    int c;

    while ((c = bufstream_getc(bp)) != EOF)
        putchar(c);
}

BASH_INPUT bash_input;

struct stat dsb;        /* can be used from gdb */

/* imitate /bin/cat */
main(argc, argv)
int argc;
char    **argv;
{
    register int i;
    BUFFERED_STREAM *bp;

    if (argc == 1) {
        bp = fd_to_buffered_stream (0);
        process(bp);
        exit(0);
    }
    for (i = 1; i < argc; i++) {
        if (argv[i][0] == '-' && argv[i][1] == '\0') {
            bp = fd_to_buffered_stream (0);
            if (!bp)
                continue;
            process(bp);
            free_buffered_stream (bp);
        } else {
            bp = open_buffered_stream (argv[i]);
            if (!bp)
                continue;
            process(bp);
            close_buffered_stream (bp);
        }
    }
    exit(0);
}
#endif /* TEST */
#endif /* BUFFERED_INPUT */

https://git.savannah.gnu.org/cgit/bash.git/tree/unwind_prot.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/unwind_prot.c used in: Step 1226
/* I can't stand it anymore!  Please can't we just write the
   whole Unix system in lisp or something? */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

/* **************************************************************** */
/*                                  */
/*            Unwind Protection Scheme for Bash         */
/*                                  */
/* **************************************************************** */
#include "config.h"

#include "bashtypes.h"
#include "bashansi.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#if STDC_HEADERS
#  include <stddef.h>
#endif

#ifndef offsetof
#  define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

#include "command.h"
#include "general.h"
#include "unwind_prot.h"
#include "quit.h"
#include "sig.h"

/* Structure describing a saved variable and the value to restore it to.  */
typedef struct {
  char *variable;
  int size;
  char desired_setting[1]; /* actual size is `size' */
} SAVED_VAR;

/* If HEAD.CLEANUP is null, then ARG.V contains a tag to throw back to.
   If HEAD.CLEANUP is restore_variable, then SV.V contains the saved
   variable.  Otherwise, call HEAD.CLEANUP (ARG.V) to clean up.  */
typedef union uwp {
  struct uwp_head {
    union uwp *next;
    Function *cleanup;
  } head;
  struct {
    struct uwp_head uwp_head;
    char *v;
  } arg;
  struct {
    struct uwp_head uwp_head;
    SAVED_VAR v;
  } sv;
} UNWIND_ELT;


extern int interrupt_immediately;

static void without_interrupts __P((VFunction *, char *, char *));
static void unwind_frame_discard_internal __P((char *, char *));
static void unwind_frame_run_internal __P((char *, char *));
static void add_unwind_protect_internal __P((Function *, char *));
static void remove_unwind_protect_internal __P((char *, char *));
static void run_unwind_protects_internal __P((char *, char *));
static void clear_unwind_protects_internal __P((char *, char *));
static inline void restore_variable __P((SAVED_VAR *));
static void unwind_protect_mem_internal __P((char *, char *));

static UNWIND_ELT *unwind_protect_list = (UNWIND_ELT *)NULL;

#define uwpalloc(elt)   (elt) = (UNWIND_ELT *)xmalloc (sizeof (UNWIND_ELT))
#define uwpfree(elt)    free(elt)

/* Run a function without interrupts.  This relies on the fact that the
   FUNCTION cannot change the value of interrupt_immediately.  (I.e., does
   not call QUIT (). */
static void
without_interrupts (function, arg1, arg2)
     VFunction *function;
     char *arg1, *arg2;
{
  int old_interrupt_immediately;

  old_interrupt_immediately = interrupt_immediately;
  interrupt_immediately = 0;

  (*function)(arg1, arg2);

  interrupt_immediately = old_interrupt_immediately;
}

/* Start the beginning of a region. */
void
begin_unwind_frame (tag)
     char *tag;
{
  add_unwind_protect ((Function *)NULL, tag);
}

/* Discard the unwind protects back to TAG. */
void
discard_unwind_frame (tag)
     char *tag;
{
  if (unwind_protect_list)
    without_interrupts (unwind_frame_discard_internal, tag, (char *)NULL);
}

/* Run the unwind protects back to TAG. */
void
run_unwind_frame (tag)
     char *tag;
{
  if (unwind_protect_list)
    without_interrupts (unwind_frame_run_internal, tag, (char *)NULL);
}

/* Add the function CLEANUP with ARG to the list of unwindable things. */
void
add_unwind_protect (cleanup, arg)
     Function *cleanup;
     char *arg;
{
  without_interrupts (add_unwind_protect_internal, (char *)cleanup, arg);
}

/* Remove the top unwind protect from the list. */
void
remove_unwind_protect ()
{
  if (unwind_protect_list)
    without_interrupts
      (remove_unwind_protect_internal, (char *)NULL, (char *)NULL);
}

/* Run the list of cleanup functions in unwind_protect_list. */
void
run_unwind_protects ()
{
  if (unwind_protect_list)
    without_interrupts
      (run_unwind_protects_internal, (char *)NULL, (char *)NULL);
}

/* Erase the unwind-protect list.  If flags is 1, free the elements. */
void
clear_unwind_protect_list (flags)
     int flags;
{
  char *flag;

  if (unwind_protect_list)
    {
      flag = flags ? "" : (char *)NULL;
      without_interrupts
        (clear_unwind_protects_internal, flag, (char *)NULL);
    }
}

/* **************************************************************** */
/*                                  */
/*          The Actual Functions                */
/*                                  */
/* **************************************************************** */

static void
add_unwind_protect_internal (cleanup, arg)
     Function *cleanup;
     char *arg;
{
  UNWIND_ELT *elt;

  uwpalloc (elt);
  elt->head.next = unwind_protect_list;
  elt->head.cleanup = cleanup;
  elt->arg.v = arg;
  unwind_protect_list = elt;
}

static void
remove_unwind_protect_internal (ignore1, ignore2)
     char *ignore1, *ignore2;
{
  UNWIND_ELT *elt;

  elt = unwind_protect_list;
  if (elt)
    {
      unwind_protect_list = unwind_protect_list->head.next;
      uwpfree (elt);
    }
}

static void
run_unwind_protects_internal (ignore1, ignore2)
     char *ignore1, *ignore2;
{
  unwind_frame_run_internal ((char *) NULL, (char *) NULL);
}

static void
clear_unwind_protects_internal (flag, ignore)
     char *flag, *ignore;
{
  if (flag)
    {
      while (unwind_protect_list)
    remove_unwind_protect_internal ((char *)NULL, (char *)NULL);
    }
  unwind_protect_list = (UNWIND_ELT *)NULL;
}

static void
unwind_frame_discard_internal (tag, ignore)
     char *tag, *ignore;
{
  UNWIND_ELT *elt;

  while (elt = unwind_protect_list)
    {
      unwind_protect_list = unwind_protect_list->head.next;
      if (elt->head.cleanup == 0 && (STREQ (elt->arg.v, tag)))
    {
      uwpfree (elt);
      break;
    }
      else
    uwpfree (elt);
    }
}

/* Restore the value of a variable, based on the contents of SV.
   sv->desired_setting is a block of memory SIZE bytes long holding the
   value itself.  This block of memory is copied back into the variable. */
static inline void
restore_variable (sv)
     SAVED_VAR *sv;
{
  FASTCOPY (sv->desired_setting, sv->variable, sv->size);
}

static void
unwind_frame_run_internal (tag, ignore)
     char *tag, *ignore;
{
  UNWIND_ELT *elt;

  while (elt = unwind_protect_list)
    {
      unwind_protect_list = elt->head.next;

      /* If tag, then compare. */
      if (!elt->head.cleanup)
    {
      if (tag && STREQ (elt->arg.v, tag))
        {
          uwpfree (elt);
          break;
        }
    }
      else
    {
      if (elt->head.cleanup == (Function *) restore_variable)
        restore_variable (&elt->sv.v);
      else
        (*(elt->head.cleanup)) (elt->arg.v);
    }

      uwpfree (elt);
    }
}

static void
unwind_protect_mem_internal (var, psize)
     char *var;
     char *psize;
{
  int size, allocated;
  UNWIND_ELT *elt;

  size = *(int *) psize;
  allocated = size + offsetof (UNWIND_ELT, sv.v.desired_setting[0]);
  elt = (UNWIND_ELT *)xmalloc (allocated);
  elt->head.next = unwind_protect_list;
  elt->head.cleanup = (Function *) restore_variable;
  elt->sv.v.variable = var;
  elt->sv.v.size = size;
  FASTCOPY (var, elt->sv.v.desired_setting, size);
  unwind_protect_list = elt;
}

/* Save the value of a variable so it will be restored when unwind-protects
   are run.  VAR is a pointer to the variable.  SIZE is the size in
   bytes of VAR.  */
void
unwind_protect_mem (var, size)
     char *var;
     int size;
{
  without_interrupts (unwind_protect_mem_internal, var, (char *) &size);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/pathexp.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/pathexp.c used in: Step 1227
/* pathexp.c -- The shell interface to the globbing library. */

/* Copyright (C) 1995-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include "bashtypes.h"
#include <stdio.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "bashansi.h"

#include "shell.h"
#include "pathexp.h"
#include "flags.h"

#include "shmbutil.h"

#include <glob/strmatch.h>

static int glob_name_is_acceptable __P((const char *));
static void ignore_globbed_names __P((char **, sh_ignore_func_t *));
               
#if defined (USE_POSIX_GLOB_LIBRARY)
#  include <glob.h>
typedef int posix_glob_errfunc_t __P((const char *, int));
#else
#  include <glob/glob.h>
#endif

/* Control whether * matches .files in globbing. */
int glob_dot_filenames;

/* Control whether the extended globbing features are enabled. */
int extended_glob = 0;

/* Return nonzero if STRING has any unquoted special globbing chars in it.  */
int
unquoted_glob_pattern_p (string)
     register char *string;
{
  register int c;
  char *send;
  int open;

  DECLARE_MBSTATE;

  open = 0;
  send = string + strlen (string);

  while (c = *string++)
    {
      switch (c)
    {
    case '?':
    case '*':
      return (1);

    case '[':
      open++;
      continue;

    case ']':
      if (open)
        return (1);
      continue;

    case '+':
    case '@':
    case '!':
      if (*string == '(')   /*)*/
        return (1);
      continue;

    case CTLESC:
    case '\\':
      if (*string++ == '\0')
        return (0);
    }

      /* Advance one fewer byte than an entire multibyte character to
     account for the auto-increment in the loop above. */
#ifdef HANDLE_MULTIBYTE
      string--;
      ADVANCE_CHAR_P (string, send - string);
      string++;
#else
      ADVANCE_CHAR_P (string, send - string);
#endif
    }
  return (0);
}

/* PATHNAME can contain characters prefixed by CTLESC; this indicates
   that the character is to be quoted.  We quote it here in the style
   that the glob library recognizes.  If flags includes QGLOB_CVTNULL,
   we change quoted null strings (pathname[0] == CTLNUL) into empty
   strings (pathname[0] == 0).  If this is called after quote removal
   is performed, (flags & QGLOB_CVTNULL) should be 0; if called when quote
   removal has not been done (for example, before attempting to match a
   pattern while executing a case statement), flags should include
   QGLOB_CVTNULL.  If flags includes QGLOB_FILENAME, appropriate quoting
   to match a filename should be performed. */
char *
quote_string_for_globbing (pathname, qflags)
     const char *pathname;
     int qflags;
{
  char *temp;
  register int i, j;

  temp = (char *)xmalloc (strlen (pathname) + 1);

  if ((qflags & QGLOB_CVTNULL) && QUOTED_NULL (pathname))
    {
      temp[0] = '\0';
      return temp;
    }

  for (i = j = 0; pathname[i]; i++)
    {
      if (pathname[i] == CTLESC)
    {
      if ((qflags & QGLOB_FILENAME) && pathname[i+1] == '/')
        continue;
      temp[j++] = '\\';
      i++;
      if (pathname[i] == '\0')
        break;
    }
      temp[j++] = pathname[i];
    }
  temp[j] = '\0';

  return (temp);
}

char *
quote_globbing_chars (string)
     char *string;
{
  size_t slen;
  char *temp, *s, *t, *send;
  DECLARE_MBSTATE;

  slen = strlen (string);
  send = string + slen;

  temp = (char *)xmalloc (slen * 2 + 1);
  for (t = temp, s = string; *s; )
    {
      switch (*s)
    {
    case '*':
    case '[':
    case ']':
    case '?':
    case '\\':
      *t++ = '\\';
      break;
    case '+':
    case '@':
    case '!':
      if (s[1] == '(')  /*(*/
        *t++ = '\\';
      break;
    }

      /* Copy a single (possibly multibyte) character from s to t,
         incrementing both. */
      COPY_CHAR_P (t, s, send);
    }
  *t = '\0';
  return temp;
}

/* Call the glob library to do globbing on PATHNAME. */
char **
shell_glob_filename (pathname)
     const char *pathname;
{
#if defined (USE_POSIX_GLOB_LIBRARY)
  register int i;
  char *temp, **results;
  glob_t filenames;
  int glob_flags;

  temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);

  filenames.gl_offs = 0;

#  if defined (GLOB_PERIOD)
  glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0;
#  else
  glob_flags = 0;
#  endif /* !GLOB_PERIOD */

  glob_flags |= (GLOB_ERR | GLOB_DOOFFS);

  i = glob (temp, glob_flags, (posix_glob_errfunc_t *)NULL, &filenames);

  free (temp);

  if (i == GLOB_NOSPACE || i == GLOB_ABORTED)
    return ((char **)NULL);
  else if (i == GLOB_NOMATCH)
    filenames.gl_pathv = (char **)NULL;
  else if (i != 0)      /* other error codes not in POSIX.2 */
    filenames.gl_pathv = (char **)NULL;

  results = filenames.gl_pathv;

  if (results && ((GLOB_FAILED (results)) == 0))
    {
      if (should_ignore_glob_matches ())
    ignore_glob_matches (results);
      if (results && results[0])
    strvec_sort (results);
      else
    {
      FREE (results);
      results = (char **)NULL;
    }
    }

  return (results);

#else /* !USE_POSIX_GLOB_LIBRARY */

  char *temp, **results;

  noglob_dot_filenames = glob_dot_filenames == 0;

  temp = quote_string_for_globbing (pathname, QGLOB_FILENAME);
  results = glob_filename (temp, 0);
  free (temp);

  if (results && ((GLOB_FAILED (results)) == 0))
    {
      if (should_ignore_glob_matches ())
    ignore_glob_matches (results);
      if (results && results[0])
    strvec_sort (results);
      else
    {
      FREE (results);
      results = (char **)&glob_error_return;
    }
    }

  return (results);
#endif /* !USE_POSIX_GLOB_LIBRARY */
}

/* Stuff for GLOBIGNORE. */

static struct ignorevar globignore =
{
  "GLOBIGNORE",
  (struct ign *)0,
  0,
  (char *)0,
  (sh_iv_item_func_t *)0,
};

/* Set up to ignore some glob matches because the value of GLOBIGNORE
   has changed.  If GLOBIGNORE is being unset, we also need to disable
   the globbing of filenames beginning with a `.'. */
void
setup_glob_ignore (name)
     char *name;
{
  char *v;

  v = get_string_value (name);
  setup_ignore_patterns (&globignore);

  if (globignore.num_ignores)
    glob_dot_filenames = 1;
  else if (v == 0)
    glob_dot_filenames = 0;
}

int
should_ignore_glob_matches ()
{
  return globignore.num_ignores;
}

/* Return 0 if NAME matches a pattern in the globignore.ignores list. */
static int
glob_name_is_acceptable (name)
     const char *name;
{
  struct ign *p;
  int flags;

  /* . and .. are never matched */
  if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
    return (0);

  flags = FNM_PATHNAME | FNMATCH_EXTFLAG;
  for (p = globignore.ignores; p->val; p++)
    {
      if (strmatch (p->val, (char *)name, flags) != FNM_NOMATCH)
    return (0);
    }
  return (1);
}

/* Internal function to test whether filenames in NAMES should be
   ignored.  NAME_FUNC is a pointer to a function to call with each
   name.  It returns non-zero if the name is acceptable to the particular
   ignore function which called _ignore_names; zero if the name should
   be removed from NAMES. */

static void
ignore_globbed_names (names, name_func)
     char **names;
     sh_ignore_func_t *name_func;
{
  char **newnames;
  int n, i;

  for (i = 0; names[i]; i++)
    ;
  newnames = strvec_create (i + 1);

  for (n = i = 0; names[i]; i++)
    {
      if ((*name_func) (names[i]))
    newnames[n++] = names[i];
      else
    free (names[i]);
    }

  newnames[n] = (char *)NULL;

  if (n == 0)
    {
      names[0] = (char *)NULL;
      free (newnames);
      return;
    }

  /* Copy the acceptable names from NEWNAMES back to NAMES and set the
     new array end. */
  for (n = 0; newnames[n]; n++)
    names[n] = newnames[n];
  names[n] = (char *)NULL;
  free (newnames);
}

void
ignore_glob_matches (names)
     char **names;
{
  if (globignore.num_ignores == 0)
    return;

  ignore_globbed_names (names, glob_name_is_acceptable);
}

void
setup_ignore_patterns (ivp)
     struct ignorevar *ivp;
{
  int numitems, maxitems, ptr;
  char *colon_bit, *this_ignoreval;
  struct ign *p;

  this_ignoreval = get_string_value (ivp->varname);

  /* If nothing has changed then just exit now. */
  if ((this_ignoreval && ivp->last_ignoreval && STREQ (this_ignoreval, ivp->last_ignoreval)) ||
      (!this_ignoreval && !ivp->last_ignoreval))
    return;

  /* Oops.  The ignore variable has changed.  Re-parse it. */
  ivp->num_ignores = 0;

  if (ivp->ignores)
    {
      for (p = ivp->ignores; p->val; p++)
    free(p->val);
      free (ivp->ignores);
      ivp->ignores = (struct ign *)NULL;
    }

  if (ivp->last_ignoreval)
    {
      free (ivp->last_ignoreval);
      ivp->last_ignoreval = (char *)NULL;
    }

  if (this_ignoreval == 0 || *this_ignoreval == '\0')
    return;

  ivp->last_ignoreval = savestring (this_ignoreval);

  numitems = maxitems = ptr = 0;

  while (colon_bit = extract_colon_unit (this_ignoreval, &ptr))
    {
      if (numitems + 1 >= maxitems)
    {
      maxitems += 10;
      ivp->ignores = (struct ign *)xrealloc (ivp->ignores, maxitems * sizeof (struct ign));
    }
      ivp->ignores[numitems].val = colon_bit;
      ivp->ignores[numitems].len = strlen (colon_bit);
      ivp->ignores[numitems].flags = 0;
      if (ivp->item_func)
    (*ivp->item_func) (&ivp->ignores[numitems]);
      numitems++;
    }
  ivp->ignores[numitems].val = (char *)NULL;
  ivp->num_ignores = numitems;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/sig.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/sig.c used in: Step 1228
/* sig.c - interface for shell signal handlers and signal initialization. */

/* Copyright (C) 1994 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include "bashtypes.h"

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>
#include <signal.h>

#include "shell.h"
#if defined (JOB_CONTROL)
#include "jobs.h"
#endif /* JOB_CONTROL */
#include "siglist.h"
#include "sig.h"
#include "trap.h"

#include "builtins/common.h"

#if defined (READLINE)
#  include "bashline.h"
#endif

#if defined (HISTORY)
#  include "bashhist.h"
#endif

extern int last_command_exit_value;
extern int return_catch_flag;
extern int loop_level, continuing, breaking;
extern int parse_and_execute_level, shell_initialized;

/* Non-zero after SIGINT. */
int interrupt_state;

/* The environment at the top-level R-E loop.  We use this in
   the case of error return. */
procenv_t top_level;

#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
/* The signal masks that this shell runs with. */
sigset_t top_level_mask;
#endif /* JOB_CONTROL */

/* When non-zero, we throw_to_top_level (). */
int interrupt_immediately = 0;

static void initialize_shell_signals __P((void));

void
initialize_signals (reinit)
     int reinit;
{
  initialize_shell_signals ();
  initialize_job_signals ();
#if !defined (HAVE_SYS_SIGLIST) && !defined (HAVE_UNDER_SYS_SIGLIST) && !defined (HAVE_STRSIGNAL)
  if (reinit == 0)
    initialize_siglist ();
#endif /* !HAVE_SYS_SIGLIST && !HAVE_UNDER_SYS_SIGLIST && !HAVE_STRSIGNAL */
}

/* A structure describing a signal that terminates the shell if not
   caught.  The orig_handler member is present so children can reset
   these signals back to their original handlers. */
struct termsig {
     int signum;
     SigHandler *orig_handler;
};

#define NULL_HANDLER (SigHandler *)SIG_DFL

/* The list of signals that would terminate the shell if not caught.
   We catch them, but just so that we can write the history file,
   and so forth. */
static struct termsig terminating_signals[] = {
#ifdef SIGHUP
{  SIGHUP, NULL_HANDLER },
#endif

#ifdef SIGINT
{  SIGINT, NULL_HANDLER },
#endif

#ifdef SIGILL
{  SIGILL, NULL_HANDLER },
#endif

#ifdef SIGTRAP
{  SIGTRAP, NULL_HANDLER },
#endif

#ifdef SIGIOT
{  SIGIOT, NULL_HANDLER },
#endif

#ifdef SIGDANGER
{  SIGDANGER, NULL_HANDLER },
#endif

#ifdef SIGEMT
{  SIGEMT, NULL_HANDLER },
#endif

#ifdef SIGFPE
{  SIGFPE, NULL_HANDLER },
#endif

#ifdef SIGBUS
{  SIGBUS, NULL_HANDLER },
#endif

#ifdef SIGSEGV
{  SIGSEGV, NULL_HANDLER },
#endif

#ifdef SIGSYS
{  SIGSYS, NULL_HANDLER },
#endif

#ifdef SIGPIPE
{  SIGPIPE, NULL_HANDLER },
#endif

#ifdef SIGALRM
{  SIGALRM, NULL_HANDLER },
#endif

#ifdef SIGTERM
{  SIGTERM, NULL_HANDLER },
#endif

#ifdef SIGXCPU
{  SIGXCPU, NULL_HANDLER },
#endif

#ifdef SIGXFSZ
{  SIGXFSZ, NULL_HANDLER },
#endif

#ifdef SIGVTALRM
{  SIGVTALRM, NULL_HANDLER },
#endif

#if 0
#ifdef SIGPROF
{  SIGPROF, NULL_HANDLER },
#endif
#endif

#ifdef SIGLOST
{  SIGLOST, NULL_HANDLER },
#endif

#ifdef SIGUSR1
{  SIGUSR1, NULL_HANDLER },
#endif

#ifdef SIGUSR2
{  SIGUSR2, NULL_HANDLER },
#endif
};

#define TERMSIGS_LENGTH (sizeof (terminating_signals) / sizeof (struct termsig))

#define XSIG(x) (terminating_signals[x].signum)
#define XHANDLER(x) (terminating_signals[x].orig_handler)

static int termsigs_initialized = 0;

/* Initialize signals that will terminate the shell to do some
   unwind protection.  For non-interactive shells, we only call
   this when a trap is defined for EXIT (0). */
void
initialize_terminating_signals ()
{
  register int i;
#if defined (HAVE_POSIX_SIGNALS)
  struct sigaction act, oact;
#endif

  if (termsigs_initialized)
    return;

  /* The following code is to avoid an expensive call to
     set_signal_handler () for each terminating_signals.  Fortunately,
     this is possible in Posix.  Unfortunately, we have to call signal ()
     on non-Posix systems for each signal in terminating_signals. */
#if defined (HAVE_POSIX_SIGNALS)
  act.sa_handler = termination_unwind_protect;
  act.sa_flags = 0;
  sigemptyset (&act.sa_mask);
  sigemptyset (&oact.sa_mask);
  for (i = 0; i < TERMSIGS_LENGTH; i++)
    sigaddset (&act.sa_mask, XSIG (i));
  for (i = 0; i < TERMSIGS_LENGTH; i++)
    {
      /* If we've already trapped it, don't do anything. */
      if (signal_is_trapped (XSIG (i)))
    continue;

      sigaction (XSIG (i), &act, &oact);
      XHANDLER(i) = oact.sa_handler;
      /* Don't do anything with signals that are ignored at shell entry
     if the shell is not interactive. */
      if (!interactive_shell && XHANDLER (i) == SIG_IGN)
    {
      sigaction (XSIG (i), &oact, &act);
      set_signal_ignored (XSIG (i));
    }
#if defined (SIGPROF) && !defined (_MINIX)
      if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
    sigaction (XSIG (i), &oact, (struct sigaction *)NULL);
#endif /* SIGPROF && !_MINIX */
    }

#else /* !HAVE_POSIX_SIGNALS */

  for (i = 0; i < TERMSIGS_LENGTH; i++)
    {
      /* If we've already trapped it, don't do anything. */
      if (signal_is_trapped (XSIG (i)))
    continue;

      XHANDLER(i) = signal (XSIG (i), termination_unwind_protect);
      /* Don't do anything with signals that are ignored at shell entry
     if the shell is not interactive. */
      if (!interactive_shell && XHANDLER (i) == SIG_IGN)
    {
      signal (XSIG (i), SIG_IGN);
      set_signal_ignored (XSIG (i));
    }
#ifdef SIGPROF
      if (XSIG (i) == SIGPROF && XHANDLER (i) != SIG_DFL && XHANDLER (i) != SIG_IGN)
    signal (XSIG (i), XHANDLER (i));
#endif
    }

#endif /* !HAVE_POSIX_SIGNALS */

  termsigs_initialized = 1;
}

static void
initialize_shell_signals ()
{
  if (interactive)
    initialize_terminating_signals ();

#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
  /* All shells use the signal mask they inherit, and pass it along
     to child processes.  Children will never block SIGCHLD, though. */
  sigemptyset (&top_level_mask);
  sigprocmask (SIG_BLOCK, (sigset_t *)NULL, &top_level_mask);
#  if defined (SIGCHLD)
  sigdelset (&top_level_mask, SIGCHLD);
#  endif
#endif /* JOB_CONTROL || HAVE_POSIX_SIGNALS */

  /* And, some signals that are specifically ignored by the shell. */
  set_signal_handler (SIGQUIT, SIG_IGN);

  if (interactive)
    {
      set_signal_handler (SIGINT, sigint_sighandler);
      set_signal_handler (SIGTERM, SIG_IGN);
    }
}

void
reset_terminating_signals ()
{
  register int i;
#if defined (HAVE_POSIX_SIGNALS)
  struct sigaction act;
#endif

  if (termsigs_initialized == 0)
    return;

#if defined (HAVE_POSIX_SIGNALS)
  act.sa_flags = 0;
  sigemptyset (&act.sa_mask);
  for (i = 0; i < TERMSIGS_LENGTH; i++)
    {
      /* Skip a signal if it's trapped or handled specially, because the
     trap code will restore the correct value. */
      if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
    continue;

      act.sa_handler = XHANDLER (i);
      sigaction (XSIG (i), &act, (struct sigaction *) NULL);
    }
#else /* !HAVE_POSIX_SIGNALS */
  for (i = 0; i < TERMSIGS_LENGTH; i++)
    {
      if (signal_is_trapped (XSIG (i)) || signal_is_special (XSIG (i)))
    continue;

      signal (XSIG (i), XHANDLER (i));
    }
#endif /* !HAVE_POSIX_SIGNALS */
}
#undef XSIG
#undef XHANDLER

/* What to do when we've been interrupted, and it is safe to handle it. */
void
throw_to_top_level ()
{
  int print_newline = 0;

  if (interrupt_state)
    {
      print_newline = 1;
      DELINTERRUPT;
    }

  if (interrupt_state)
    return;

  last_command_exit_value |= 128;

  /* Run any traps set on SIGINT. */
  run_interrupt_trap ();

  /* Cleanup string parser environment. */
  while (parse_and_execute_level)
    parse_and_execute_cleanup ();

#if defined (JOB_CONTROL)
  give_terminal_to (shell_pgrp, 0);
#endif /* JOB_CONTROL */

#if defined (JOB_CONTROL) || defined (HAVE_POSIX_SIGNALS)
  /* This should not be necessary on systems using sigsetjmp/siglongjmp. */
  sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL);
#endif

  reset_parser ();

#if defined (READLINE)
  if (interactive)
    bashline_reinitialize ();
#endif /* READLINE */

#if defined (PROCESS_SUBSTITUTION)
  unlink_fifo_list ();
#endif /* PROCESS_SUBSTITUTION */

  run_unwind_protects ();
  loop_level = continuing = breaking = 0;
  return_catch_flag = 0;

  if (interactive && print_newline)
    {
      fflush (stdout);
      fprintf (stderr, "\n");
      fflush (stderr);
    }

  /* An interrupted `wait' command in a script does not exit the script. */
  if (interactive || (interactive_shell && !shell_initialized) ||
      (print_newline && signal_is_trapped (SIGINT)))
    jump_to_top_level (DISCARD);
  else
    jump_to_top_level (EXITPROG);
}

/* This is just here to isolate the longjmp calls. */
void
jump_to_top_level (value)
     int value;
{
  longjmp (top_level, value);
}

sighandler
termination_unwind_protect (sig)
     int sig;
{
  if (sig == SIGINT && signal_is_trapped (SIGINT))
    run_interrupt_trap ();

#if defined (HISTORY)
  if (interactive_shell && sig != SIGABRT)
    maybe_save_shell_history ();
#endif /* HISTORY */

#if defined (JOB_CONTROL)
  if (interactive && sig == SIGHUP)
    hangup_all_jobs ();
  end_job_control ();
#endif /* JOB_CONTROL */

#if defined (PROCESS_SUBSTITUTION)
  unlink_fifo_list ();
#endif /* PROCESS_SUBSTITUTION */

  run_exit_trap ();
  set_signal_handler (sig, SIG_DFL);
  kill (getpid (), sig);

  SIGRETURN (0);
}

/* What we really do when SIGINT occurs. */
sighandler
sigint_sighandler (sig)
     int sig;
{
#if defined (MUST_REINSTALL_SIGHANDLERS)
  signal (sig, sigint_sighandler);
#endif

  /* interrupt_state needs to be set for the stack of interrupts to work
     right.  Should it be set unconditionally? */
  if (interrupt_state == 0)
    ADDINTERRUPT;

  if (interrupt_immediately)
    {
      interrupt_immediately = 0;
      throw_to_top_level ();
    }

  SIGRETURN (0);
}

/* Signal functions used by the rest of the code. */
#if !defined (HAVE_POSIX_SIGNALS)

#if defined (JOB_CONTROL)
/* Perform OPERATION on NEWSET, perhaps leaving information in OLDSET. */
sigprocmask (operation, newset, oldset)
     int operation, *newset, *oldset;
{
  int old, new;

  if (newset)
    new = *newset;
  else
    new = 0;

  switch (operation)
    {
    case SIG_BLOCK:
      old = sigblock (new);
      break;

    case SIG_SETMASK:
      sigsetmask (new);
      break;

    default:
      internal_error ("Bad code in sig.c: sigprocmask");
    }

  if (oldset)
    *oldset = old;
}
#endif /* JOB_CONTROL */

#else

#if !defined (SA_INTERRUPT)
#  define SA_INTERRUPT 0
#endif

#if !defined (SA_RESTART)
#  define SA_RESTART 0
#endif

SigHandler *
set_signal_handler (sig, handler)
     int sig;
     SigHandler *handler;
{
  struct sigaction act, oact;

  act.sa_handler = handler;
  act.sa_flags = 0;
#if 0
  if (sig == SIGALRM)
    act.sa_flags |= SA_INTERRUPT;   /* XXX */
  else
    act.sa_flags |= SA_RESTART;     /* XXX */
#endif
  sigemptyset (&act.sa_mask);
  sigemptyset (&oact.sa_mask);
  sigaction (sig, &act, &oact);
  return (oact.sa_handler);
}
#endif /* HAVE_POSIX_SIGNALS */

https://git.savannah.gnu.org/cgit/bash.git/tree/test.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/test.c used in: Step 1229
/* GNU test program (ksb and mjb) */

/* Modified to run with the GNU shell Apr 25, 1988 by bfox. */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

/* Define PATTERN_MATCHING to get the csh-like =~ and !~ pattern-matching
   binary operators. */
/* #define PATTERN_MATCHING */

#if defined (HAVE_CONFIG_H)
#  include <config.h>
#endif

#include <stdio.h>

#include "bashtypes.h"

#if !defined (HAVE_LIMITS_H)
#  include <sys/param.h>
#endif

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <errno.h>
#if !defined (errno)
extern int errno;
#endif /* !errno */

#if !defined (_POSIX_VERSION)
#  include <sys/file.h>
#endif /* !_POSIX_VERSION */
#include "posixstat.h"
#include "filecntl.h"

#include "shell.h"
#include "pathexp.h"
#include "test.h"
#include "builtins/common.h"

#include <glob/strmatch.h>

#if !defined (STRLEN)
#  define STRLEN(s) ((s)[0] ? ((s)[1] ? ((s)[2] ? strlen(s) : 2) : 1) : 0)
#endif

#if !defined (STREQ)
#  define STREQ(a, b) ((a)[0] == (b)[0] && strcmp (a, b) == 0)
#endif /* !STREQ */

#if !defined (R_OK)
#define R_OK 4
#define W_OK 2
#define X_OK 1
#define F_OK 0
#endif /* R_OK */

#define EQ  0
#define NE  1
#define LT  2
#define GT  3
#define LE  4
#define GE  5

#define NT  0
#define OT  1
#define EF  2

/* The following few defines control the truth and false output of each stage.
   TRUE and FALSE are what we use to compute the final output value.
   SHELL_BOOLEAN is the form which returns truth or falseness in shell terms.
   Default is TRUE = 1, FALSE = 0, SHELL_BOOLEAN = (!value). */
#define TRUE 1
#define FALSE 0
#define SHELL_BOOLEAN(value) (!(value))

#define TEST_ERREXIT_STATUS 2

static procenv_t test_exit_buf;
static int test_error_return;
#define test_exit(val) \
    do { test_error_return = val; longjmp (test_exit_buf, 1); } while (0)

/* We have to use access(2) for machines running AFS, because it's
   not a Unix file system.  This may produce incorrect answers for
   non-AFS files.  I hate AFS. */
#if defined (AFS)
#  define EACCESS(path, mode)   access(path, mode)
#else
#  define EACCESS(path, mode)   test_eaccess(path, mode)
#endif /* AFS */

static int pos;     /* The offset of the current argument in ARGV. */
static int argc;    /* The number of arguments present in ARGV. */
static char **argv; /* The argument list. */
static int noeval;

static void test_syntax_error __P((char *, char *)) __attribute__((__noreturn__));
static void beyond __P((void)) __attribute__((__noreturn__));
static void integer_expected_error __P((char *)) __attribute__((__noreturn__));

static int test_stat __P((char *, struct stat *));

static int unary_operator __P((void));
static int binary_operator __P((void));
static int two_arguments __P((void));
static int three_arguments __P((void));
static int posixtest __P((void));

static int expr __P((void));
static int term __P((void));
static int and __P((void));
static int or __P((void));

static int filecomp __P((char *, char *, int));
static int arithcomp __P((char *, char *, int, int));
static int patcomp __P((char *, char *, int));

static void
test_syntax_error (format, arg)
     char *format, *arg;
{
  builtin_error (format, arg);
  test_exit (TEST_ERREXIT_STATUS);
}

/*
 * beyond - call when we're beyond the end of the argument list (an
 *  error condition)
 */
static void
beyond ()
{
  test_syntax_error ("argument expected", (char *)NULL);
}

/* Syntax error for when an integer argument was expected, but
   something else was found. */
static void
integer_expected_error (pch)
     char *pch;
{
  test_syntax_error ("%s: integer expression expected", pch);
}

/* A wrapper for stat () which disallows pathnames that are empty strings
   and handles /dev/fd emulation on systems that don't have it. */
static int
test_stat (path, finfo)
     char *path;
     struct stat *finfo;
{
  if (*path == '\0')
    {
      errno = ENOENT;
      return (-1);
    }
  if (path[0] == '/' && path[1] == 'd' && strncmp (path, "/dev/fd/", 8) == 0)
    {
#if !defined (HAVE_DEV_FD)
      intmax_t fd;
      int r;

      if (legal_number (path + 8, &fd) && fd == (int)fd)
        {
          r = fstat ((int)fd, finfo);
          if (r == 0 || errno != EBADF)
            return (r);
        }
      errno = ENOENT;
      return (-1);
#else
  /* If HAVE_DEV_FD is defined, DEV_FD_PREFIX is defined also, and has a
     trailing slash.  Make sure /dev/fd/xx really uses DEV_FD_PREFIX/xx.
     On most systems, with the notable exception of linux, this is
     effectively a no-op. */
      char pbuf[32];
      strcpy (pbuf, DEV_FD_PREFIX);
      strcat (pbuf, path + 8);
      return (stat (pbuf, finfo));
#endif /* !HAVE_DEV_FD */
    }
#if !defined (HAVE_DEV_STDIN)
  else if (STREQN (path, "/dev/std", 8))
    {
      if (STREQ (path+8, "in"))
    return (fstat (0, finfo));
      else if (STREQ (path+8, "out"))
    return (fstat (1, finfo));
      else if (STREQ (path+8, "err"))
    return (fstat (2, finfo));
      else
    return (stat (path, finfo));
    }
#endif /* !HAVE_DEV_STDIN */
  return (stat (path, finfo));
}

/* Do the same thing access(2) does, but use the effective uid and gid,
   and don't make the mistake of telling root that any file is
   executable. */
int
test_eaccess (path, mode)
     char *path;
     int mode;
{
  struct stat st;

  if (test_stat (path, &st) < 0)
    return (-1);

  if (current_user.euid == 0)
    {
      /* Root can read or write any file. */
      if (mode != X_OK)
    return (0);

      /* Root can execute any file that has any one of the execute
     bits set. */
      if (st.st_mode & S_IXUGO)
    return (0);
    }

  if (st.st_uid == current_user.euid)   /* owner */
    mode <<= 6;
  else if (group_member (st.st_gid))
    mode <<= 3;

  if (st.st_mode & mode)
    return (0);

  errno = EACCES;
  return (-1);
}

/* Increment our position in the argument list.  Check that we're not
   past the end of the argument list.  This check is supressed if the
   argument is FALSE.  Made a macro for efficiency. */
#define advance(f) do { ++pos; if (f && pos >= argc) beyond (); } while (0)
#define unary_advance() do { advance (1); ++pos; } while (0)

/*
 * expr:
 *  or
 */
static int
expr ()
{
  if (pos >= argc)
    beyond ();

  return (FALSE ^ or ());       /* Same with this. */
}

/*
 * or:
 *  and
 *  and '-o' or
 */
static int
or ()
{
  int value, v2;

  value = and ();
  if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'o' && !argv[pos][2])
    {
      advance (0);
      v2 = or ();
      return (value || v2);
    }

  return (value);
}

/*
 * and:
 *  term
 *  term '-a' and
 */
static int
and ()
{
  int value, v2;

  value = term ();
  if (pos < argc && argv[pos][0] == '-' && argv[pos][1] == 'a' && !argv[pos][2])
    {
      advance (0);
      v2 = and ();
      return (value && v2);
    }
  return (value);
}

/*
 * term - parse a term and return 1 or 0 depending on whether the term
 *  evaluates to true or false, respectively.
 *
 * term ::=
 *  '-'('a'|'b'|'c'|'d'|'e'|'f'|'g'|'h'|'k'|'p'|'r'|'s'|'u'|'w'|'x') filename
 *  '-'('G'|'L'|'O'|'S'|'N') filename
 *  '-t' [int]
 *  '-'('z'|'n') string
 *  '-o' option
 *  string
 *  string ('!='|'='|'==') string
 *  <int> '-'(eq|ne|le|lt|ge|gt) <int>
 *  file '-'(nt|ot|ef) file
 *  '(' <expr> ')'
 * int ::=
 *  positive and negative integers
 */
static int
term ()
{
  int value;

  if (pos >= argc)
    beyond ();

  /* Deal with leading `not's. */
  if (argv[pos][0] == '!' && argv[pos][1] == '\0')
    {
      value = 0;
      while (pos < argc && argv[pos][0] == '!' && argv[pos][1] == '\0')
    {
      advance (1);
      value = 1 - value;
    }

      return (value ? !term() : term());
    }

  /* A paren-bracketed argument. */
  if (argv[pos][0] == '(' && argv[pos][1] == '\0')
    {
      advance (1);
      value = expr ();
      if (argv[pos] == 0)
    test_syntax_error ("`)' expected", (char *)NULL);
      else if (argv[pos][0] != ')' || argv[pos][1])
    test_syntax_error ("`)' expected, found %s", argv[pos]);
      advance (0);
      return (value);
    }

  /* are there enough arguments left that this could be dyadic? */
  if ((pos + 3 <= argc) && test_binop (argv[pos + 1]))
    value = binary_operator ();

  /* Might be a switch type argument */
  else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
    {
      if (test_unop (argv[pos]))
    value = unary_operator ();
      else
    test_syntax_error ("%s: unary operator expected", argv[pos]);
    }
  else
    {
      value = argv[pos][0] != '\0';
      advance (0);
    }

  return (value);
}

static int
filecomp (s, t, op)
     char *s, *t;
     int op;
{
  struct stat st1, st2;
  int r1, r2;

  if ((r1 = test_stat (s, &st1)) < 0)
    {
      if (op == EF)
    return (FALSE);
    }
  if ((r2 = test_stat (t, &st2)) < 0)
    {
      if (op == EF)
    return (FALSE);
    }
  
  switch (op)
    {
    case OT: return (r1 < r2 || (r2 == 0 && st1.st_mtime < st2.st_mtime));
    case NT: return (r1 > r2 || (r1 == 0 && st1.st_mtime > st2.st_mtime));
    case EF: return ((st1.st_dev == st2.st_dev) && (st1.st_ino == st2.st_ino));
    }
  return (FALSE);
}

static int
arithcomp (s, t, op, flags)
     char *s, *t;
     int op, flags;
{
  intmax_t l, r;
  int expok;

  if (flags & TEST_ARITHEXP)
    {
      l = evalexp (s, &expok);
      if (expok == 0)
    return (FALSE);     /* should probably longjmp here */
      r = evalexp (t, &expok);
      if (expok == 0)
    return (FALSE);     /* ditto */
    }
  else
    {
      if (legal_number (s, &l) == 0)
    integer_expected_error (s);
      if (legal_number (t, &r) == 0)
    integer_expected_error (t);
    }

  switch (op)
    {
    case EQ: return (l == r);
    case NE: return (l != r);
    case LT: return (l < r);
    case GT: return (l > r);
    case LE: return (l <= r);
    case GE: return (l >= r);
    }

  return (FALSE);
}

static int
patcomp (string, pat, op)
     char *string, *pat;
     int op;
{
  int m;

  m = strmatch (pat, string, FNMATCH_EXTFLAG);
  return ((op == EQ) ? (m == 0) : (m != 0));
}

int
binary_test (op, arg1, arg2, flags)
     char *op, *arg1, *arg2;
     int flags;
{
  int patmatch;

  patmatch = (flags & TEST_PATMATCH);

  if (op[0] == '=' && (op[1] == '\0' || (op[1] == '=' && op[2] == '\0')))
    return (patmatch ? patcomp (arg1, arg2, EQ) : STREQ (arg1, arg2));

  else if ((op[0] == '>' || op[0] == '<') && op[1] == '\0')
    return ((op[0] == '>') ? (strcmp (arg1, arg2) > 0) : (strcmp (arg1, arg2) < 0));

  else if (op[0] == '!' && op[1] == '=' && op[2] == '\0')
    return (patmatch ? patcomp (arg1, arg2, NE) : (STREQ (arg1, arg2) == 0));

  else if (op[2] == 't')
    {
      switch (op[1])
    {
    case 'n': return (filecomp (arg1, arg2, NT));       /* -nt */
    case 'o': return (filecomp (arg1, arg2, OT));       /* -ot */
    case 'l': return (arithcomp (arg1, arg2, LT, flags));   /* -lt */
    case 'g': return (arithcomp (arg1, arg2, GT, flags));   /* -gt */
    }
    }
  else if (op[1] == 'e')
    {
      switch (op[2])
    {
    case 'f': return (filecomp (arg1, arg2, EF));       /* -ef */
    case 'q': return (arithcomp (arg1, arg2, EQ, flags));   /* -eq */
    }
    }
  else if (op[2] == 'e')
    {
      switch (op[1])
    {
    case 'n': return (arithcomp (arg1, arg2, NE, flags));   /* -ne */
    case 'g': return (arithcomp (arg1, arg2, GE, flags));   /* -ge */
    case 'l': return (arithcomp (arg1, arg2, LE, flags));   /* -le */
    }
    }

  return (FALSE);   /* should never get here */
}


static int
binary_operator ()
{
  int value;
  char *w;

  w = argv[pos + 1];
  if ((w[0] == '=' && (w[1] == '\0' || (w[1] == '=' && w[2] == '\0'))) || /* =, == */
      ((w[0] == '>' || w[0] == '<') && w[1] == '\0') ||     /* <, > */
      (w[0] == '!' && w[1] == '=' && w[2] == '\0'))     /* != */
    {
      value = binary_test (w, argv[pos], argv[pos + 2], 0);
      pos += 3;
      return (value);
    }

#if defined (PATTERN_MATCHING)
  if ((w[0] == '=' || w[0] == '!') && w[1] == '~' && w[2] == '\0')
    {
      value = patcomp (argv[pos], argv[pos + 2], w[0] == '=' ? EQ : NE);
      pos += 3;
      return (value);
    }
#endif

  if ((w[0] != '-' || w[3] != '\0') || test_binop (w) == 0)
    {
      test_syntax_error ("%s: binary operator expected", w);
      /* NOTREACHED */
      return (FALSE);
    }

  value = binary_test (w, argv[pos], argv[pos + 2], 0);
  pos += 3;
  return value;
}

static int
unary_operator ()
{
  char *op;
  intmax_t r;

  op = argv[pos];
  if (test_unop (op) == 0)
    return (FALSE);

  /* the only tricky case is `-t', which may or may not take an argument. */
  if (op[1] == 't')
    {
      advance (0);
      if (pos < argc)
    {
      if (legal_number (argv[pos], &r))
        {
          advance (0);
          return (unary_test (op, argv[pos - 1]));
        }
      else
        return (FALSE);
    }
      else
    return (unary_test (op, "1"));
    }

  /* All of the unary operators take an argument, so we first call
     unary_advance (), which checks to make sure that there is an
     argument, and then advances pos right past it.  This means that
     pos - 1 is the location of the argument. */
  unary_advance ();
  return (unary_test (op, argv[pos - 1]));
}

int
unary_test (op, arg)
     char *op, *arg;
{
  intmax_t r;
  struct stat stat_buf;
     
  switch (op[1])
    {
    case 'a':           /* file exists in the file system? */
    case 'e':
      return (test_stat (arg, &stat_buf) == 0);

    case 'r':           /* file is readable? */
      return (EACCESS (arg, R_OK) == 0);

    case 'w':           /* File is writeable? */
      return (EACCESS (arg, W_OK) == 0);

    case 'x':           /* File is executable? */
      return (EACCESS (arg, X_OK) == 0);

    case 'O':           /* File is owned by you? */
      return (test_stat (arg, &stat_buf) == 0 &&
          (uid_t) current_user.euid == (uid_t) stat_buf.st_uid);

    case 'G':           /* File is owned by your group? */
      return (test_stat (arg, &stat_buf) == 0 &&
          (gid_t) current_user.egid == (gid_t) stat_buf.st_gid);

    case 'N':
      return (test_stat (arg, &stat_buf) == 0 &&
          stat_buf.st_atime <= stat_buf.st_mtime);

    case 'f':           /* File is a file? */
      if (test_stat (arg, &stat_buf) < 0)
    return (FALSE);

      /* -f is true if the given file exists and is a regular file. */
#if defined (S_IFMT)
      return (S_ISREG (stat_buf.st_mode) || (stat_buf.st_mode & S_IFMT) == 0);
#else
      return (S_ISREG (stat_buf.st_mode));
#endif /* !S_IFMT */

    case 'd':           /* File is a directory? */
      return (test_stat (arg, &stat_buf) == 0 && (S_ISDIR (stat_buf.st_mode)));

    case 's':           /* File has something in it? */
      return (test_stat (arg, &stat_buf) == 0 && stat_buf.st_size > (off_t) 0);

    case 'S':           /* File is a socket? */
#if !defined (S_ISSOCK)
      return (FALSE);
#else
      return (test_stat (arg, &stat_buf) == 0 && S_ISSOCK (stat_buf.st_mode));
#endif /* S_ISSOCK */

    case 'c':           /* File is character special? */
      return (test_stat (arg, &stat_buf) == 0 && S_ISCHR (stat_buf.st_mode));

    case 'b':           /* File is block special? */
      return (test_stat (arg, &stat_buf) == 0 && S_ISBLK (stat_buf.st_mode));

    case 'p':           /* File is a named pipe? */
#ifndef S_ISFIFO
      return (FALSE);
#else
      return (test_stat (arg, &stat_buf) == 0 && S_ISFIFO (stat_buf.st_mode));
#endif /* S_ISFIFO */

    case 'L':           /* Same as -h  */
    case 'h':           /* File is a symbolic link? */
#if !defined (S_ISLNK) || !defined (HAVE_LSTAT)
      return (FALSE);
#else
      return ((arg[0] != '\0') &&
          (lstat (arg, &stat_buf) == 0) && S_ISLNK (stat_buf.st_mode));
#endif /* S_IFLNK && HAVE_LSTAT */

    case 'u':           /* File is setuid? */
      return (test_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISUID) != 0);

    case 'g':           /* File is setgid? */
      return (test_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISGID) != 0);

    case 'k':           /* File has sticky bit set? */
#if !defined (S_ISVTX)
      /* This is not Posix, and is not defined on some Posix systems. */
      return (FALSE);
#else
      return (test_stat (arg, &stat_buf) == 0 && (stat_buf.st_mode & S_ISVTX) != 0);
#endif

    case 't':   /* File fd is a terminal? */
      if (legal_number (arg, &r) == 0)
    return (FALSE);
      return ((r == (int)r) && isatty ((int)r));

    case 'n':           /* True if arg has some length. */
      return (arg[0] != '\0');

    case 'z':           /* True if arg has no length. */
      return (arg[0] == '\0');

    case 'o':           /* True if option `arg' is set. */
      return (minus_o_option_value (arg) == 1);
    }

  /* We can't actually get here, but this shuts up gcc. */
  return (FALSE);
}

/* Return TRUE if OP is one of the test command's binary operators. */
int
test_binop (op)
     char *op;
{
  if (op[0] == '=' && op[1] == '\0')
    return (1);     /* '=' */
  else if ((op[0] == '<' || op[0] == '>') && op[1] == '\0')  /* string <, > */
    return (1);
  else if ((op[0] == '=' || op[0] == '!') && op[1] == '=' && op[2] == '\0')
    return (1);     /* `==' and `!=' */
#if defined (PATTERN_MATCHING)
  else if (op[2] == '\0' && op[1] == '~' && (op[0] == '=' || op[0] == '!'))
    return (1);
#endif
  else if (op[0] != '-' || op[2] == '\0' || op[3] != '\0')
    return (0);
  else
    {
      if (op[2] == 't')
    switch (op[1])
      {
      case 'n':     /* -nt */
      case 'o':     /* -ot */
      case 'l':     /* -lt */
      case 'g':     /* -gt */
        return (1);
      default:
        return (0);
      }
      else if (op[1] == 'e')
    switch (op[2])
      {
      case 'q':     /* -eq */
      case 'f':     /* -ef */
        return (1);
      default:
        return (0);
      }
      else if (op[2] == 'e')
    switch (op[1])
      {
      case 'n':     /* -ne */
      case 'g':     /* -ge */
      case 'l':     /* -le */
        return (1);
      default:
        return (0);
      }
      else
    return (0);
    }
}

/* Return non-zero if OP is one of the test command's unary operators. */
int
test_unop (op)
     char *op;
{
  if (op[0] != '-')
    return (0);

  switch (op[1])
    {
    case 'a': case 'b': case 'c': case 'd': case 'e':
    case 'f': case 'g': case 'h': case 'k': case 'n':
    case 'o': case 'p': case 'r': case 's': case 't':
    case 'u': case 'w': case 'x': case 'z':
    case 'G': case 'L': case 'O': case 'S': case 'N':
      return (1);
    }

  return (0);
}

static int
two_arguments ()
{
  if (argv[pos][0] == '!' && argv[pos][1] == '\0')
    return (argv[pos + 1][0] == '\0');
  else if (argv[pos][0] == '-' && argv[pos][2] == '\0')
    {
      if (test_unop (argv[pos]))
    return (unary_operator ());
      else
    test_syntax_error ("%s: unary operator expected", argv[pos]);
    }
  else
    test_syntax_error ("%s: unary operator expected", argv[pos]);

  return (0);
}

#define ANDOR(s)  (s[0] == '-' && !s[2] && (s[1] == 'a' || s[1] == 'o'))

/* This could be augmented to handle `-t' as equivalent to `-t 1', but
   POSIX requires that `-t' be given an argument. */
#define ONE_ARG_TEST(s)     ((s)[0] != '\0')

static int
three_arguments ()
{
  int value;

  if (test_binop (argv[pos+1]))
    {
      value = binary_operator ();
      pos = argc;
    }
  else if (ANDOR (argv[pos+1]))
    {
      if (argv[pos+1][1] == 'a')
    value = ONE_ARG_TEST(argv[pos]) && ONE_ARG_TEST(argv[pos+2]);
      else
    value = ONE_ARG_TEST(argv[pos]) || ONE_ARG_TEST(argv[pos+2]);
      pos = argc;
    }
  else if (argv[pos][0] == '!' && argv[pos][1] == '\0')
    {
      advance (1);
      value = !two_arguments ();
    }
  else if (argv[pos][0] == '(' && argv[pos+2][0] == ')')
    {
      value = ONE_ARG_TEST(argv[pos+1]);
      pos = argc;
    }
  else
    test_syntax_error ("%s: binary operator expected", argv[pos+1]);

  return (value);
}

/* This is an implementation of a Posix.2 proposal by David Korn. */
static int
posixtest ()
{
  int value;

  switch (argc - 1) /* one extra passed in */
    {
      case 0:
    value = FALSE;
    pos = argc;
    break;

      case 1:
    value = ONE_ARG_TEST(argv[1]);
    pos = argc;
    break;

      case 2:
    value = two_arguments ();
    pos = argc;
    break;

      case 3:
    value = three_arguments ();
    break;

      case 4:
    if (argv[pos][0] == '!' && argv[pos][1] == '\0')
      {
        advance (1);
        value = !three_arguments ();
        break;
      }
    /* FALLTHROUGH */
      default:
    value = expr ();
    }

  return (value);
}

/*
 * [:
 *  '[' expr ']'
 * test:
 *  test expr
 */
int
test_command (margc, margv)
     int margc;
     char **margv;
{
  int value;
  int code;

  USE_VAR(margc);

  code = setjmp (test_exit_buf);

  if (code)
    return (test_error_return);

  argv = margv;

  if (margv[0] && margv[0][0] == '[' && margv[0][1] == '\0')
    {
      --margc;

      if (margv[margc] && (margv[margc][0] != ']' || margv[margc][1]))
    test_syntax_error ("missing `]'", (char *)NULL);

      if (margc < 2)
    test_exit (SHELL_BOOLEAN (FALSE));
    }

  argc = margc;
  pos = 1;

  if (pos >= argc)
    test_exit (SHELL_BOOLEAN (FALSE));

  noeval = 0;
  value = posixtest ();

  if (pos != argc)
    test_syntax_error ("too many arguments", (char *)NULL);

  test_exit (SHELL_BOOLEAN (value));
}

https://git.savannah.gnu.org/cgit/bash.git/tree/version.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/version.c used in: Step 1230
/* version.c -- distribution and version numbers. */

/* Copyright (C) 1989 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#include <stdio.h>

#include "stdc.h"

#include "version.h"
#include "patchlevel.h"
#include "conftypes.h"

extern char *shell_name;

/* Defines from version.h */
const char *dist_version = DISTVERSION;
int patch_level = PATCHLEVEL;
int build_version = BUILDVERSION;
#ifdef RELSTATUS
const char *release_status = RELSTATUS;
#else
const char *release_status = (char *)0;
#endif
const char *sccs_version = SCCSVERSION;

/* Functions for getting, setting, and displaying the shell version. */

/* Give version information about this shell. */
char *
shell_version_string ()
{
  static char tt[32] = { '\0' };

  if (tt[0] == '\0')
    {
      if (release_status)
    sprintf (tt, "%s.%d(%d)-%s", dist_version, patch_level, build_version, release_status);
      else
    sprintf (tt, "%s.%d(%d)", dist_version, patch_level, build_version);
    }
  return tt;
}

void
show_shell_version (extended)
     int extended;
{
  printf ("GNU bash, version %s (%s)\n", shell_version_string (), MACHTYPE);
  if (extended)
    printf ("Copyright (C) 2002 Free Software Foundation, Inc.\n");
}

https://git.savannah.gnu.org/cgit/bash.git/tree/patchlevel.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/patchlevel.h used in: Step 1230
/* patchlevel.h -- current bash patch level */

/* Copyright (C) 2001 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#if !defined (_PATCHLEVEL_H_)
#define _PATCHLEVEL_H_

/* It's important that there be no other strings in this file that match the
   regexp `^#define[    ]*PATCHLEVEL', since that's what support/mkversion.sh
   looks for to find the patch level (for the sccs version string). */

#define PATCHLEVEL 0

#endif /* _PATCHLEVEL_H_ */

https://git.savannah.gnu.org/cgit/bash.git/tree/conftypes.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/conftypes.h used in: Step 1230
/* conftypes.h -- defines for build and host system. */

/* Copyright (C) 2001 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#if !defined (_CONFTYPES_H_)
#define _CONFTYPES_H_

/* Placeholder for future modifications if cross-compiling or building a
   `fat' binary, e.g. on Apple Rhapsody.  These values are used in multiple
   files, so they appear here. */
#if !defined (RHAPSODY)
#  define HOSTTYPE  CONF_HOSTTYPE
#  define OSTYPE    CONF_OSTYPE
#  define MACHTYPE  CONF_MACHTYPE
#else /* RHAPSODY */
#  if   defined(__powerpc__) || defined(__ppc__)
#    define HOSTTYPE "powerpc"
#  elif defined(__i386__)
#    define HOSTTYPE "i386"
#  else
#    define HOSTTYPE CONF_HOSTTYPE
#  endif

#  define OSTYPE CONF_OSTYPE
#  define VENDOR CONF_VENDOR

#  define MACHTYPE HOSTTYPE "-" VENDOR "-" OSTYPE
#endif /* RHAPSODY */

#ifndef HOSTTYPE
#  define HOSTTYPE "unknown"
#endif

#ifndef OSTYPE
#  define OSTYPE "unknown"
#endif

#ifndef MACHTYPE
#  define MACHTYPE "unknown"
#endif

#endif /* _CONFTYPES_H_ */

https://git.savannah.gnu.org/cgit/bash.git/tree/alias.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/alias.c used in: Step 1231
/* alias.c -- Not a full alias, but just the kind that we use in the
   shell.  Csh style alias is somewhere else (`over there, in a box'). */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#if defined (ALIAS)

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>
#include "chartypes.h"
#include "bashansi.h"
#include "command.h"
#include "general.h"
#include "externs.h"
#include "alias.h"

#if defined (PROGRAMMABLE_COMPLETION)
#  include "pcomplete.h"
#endif

#define ALIAS_HASH_BUCKETS  16  /* must be power of two */

typedef int sh_alias_map_func_t __P((alias_t *));

static void free_alias_data __P((PTR_T));
static alias_t **map_over_aliases __P((sh_alias_map_func_t *));
static void sort_aliases __P((alias_t **));
static int qsort_alias_compare __P((alias_t **, alias_t **));

#if defined (READLINE)
static int skipquotes __P((char *, int));
static int skipws __P((char *, int));
static int rd_token __P((char *, int));
#endif

/* Non-zero means expand all words on the line.  Otherwise, expand
   after first expansion if the expansion ends in a space. */
int alias_expand_all = 0;

/* The list of aliases that we have. */
HASH_TABLE *aliases = (HASH_TABLE *)NULL;

void
initialize_aliases ()
{
  if (!aliases)
    aliases = hash_create (ALIAS_HASH_BUCKETS);
}

/* Scan the list of aliases looking for one with NAME.  Return NULL
   if the alias doesn't exist, else a pointer to the alias_t. */
alias_t *
find_alias (name)
     char *name;
{
  BUCKET_CONTENTS *al;

  if (aliases == 0)
    return ((alias_t *)NULL);

  al = hash_search (name, aliases, 0);
  return (al ? (alias_t *)al->data : (alias_t *)NULL);
}

/* Return the value of the alias for NAME, or NULL if there is none. */
char *
get_alias_value (name)
     char *name;
{
  alias_t *alias;

  if (aliases == 0)
    return ((char *)NULL);

  alias = find_alias (name);
  return (alias ? alias->value : (char *)NULL);
}

/* Make a new alias from NAME and VALUE.  If NAME can be found,
   then replace its value. */
void
add_alias (name, value)
     char *name, *value;
{
  BUCKET_CONTENTS *elt;
  alias_t *temp;
  int n;

  if (!aliases)
    {
      initialize_aliases ();
      temp = (alias_t *)NULL;
    }
  else
    temp = find_alias (name);

  if (temp)
    {
      free (temp->value);
      temp->value = savestring (value);
      temp->flags &= ~AL_EXPANDNEXT;
      n = value[strlen (value) - 1];
      if (n == ' ' || n == '\t')
    temp->flags |= AL_EXPANDNEXT;
    }
  else
    {
      temp = (alias_t *)xmalloc (sizeof (alias_t));
      temp->name = savestring (name);
      temp->value = savestring (value);
      temp->flags = 0;

      n = value[strlen (value) - 1];
      if (n == ' ' || n == '\t')
    temp->flags |= AL_EXPANDNEXT;

      elt = hash_insert (savestring (name), aliases, HASH_NOSRCH);
      elt->data = temp;
#if defined (PROGRAMMABLE_COMPLETION)
      set_itemlist_dirty (&it_aliases);
#endif
    }
}

/* Delete a single alias structure. */
static void
free_alias_data (data)
     PTR_T data;
{
  register alias_t *a;

  a = (alias_t *)data;
  free (a->value);
  free (a->name);
  free (data);
}

/* Remove the alias with name NAME from the alias table.  Returns
   the number of aliases left in the table, or -1 if the alias didn't
   exist. */
int
remove_alias (name)
     char *name;
{
  BUCKET_CONTENTS *elt;

  if (aliases == 0)
    return (-1);

  elt = hash_remove (name, aliases, 0);
  if (elt)
    {
      free_alias_data (elt->data);
      free (elt->key);      /* alias name */
      free (elt);       /* XXX */
#if defined (PROGRAMMABLE_COMPLETION)
      set_itemlist_dirty (&it_aliases);
#endif
      return (aliases->nentries);
    }
  return (-1);
}

/* Delete all aliases. */
void
delete_all_aliases ()
{
  if (aliases == 0)
    return;

  hash_flush (aliases, free_alias_data);
  hash_dispose (aliases);
  aliases = (HASH_TABLE *)NULL;
#if defined (PROGRAMMABLE_COMPLETION)
  set_itemlist_dirty (&it_aliases);
#endif
}

/* Return an array of aliases that satisfy the conditions tested by FUNCTION.
   If FUNCTION is NULL, return all aliases. */
static alias_t **
map_over_aliases (function)
     sh_alias_map_func_t *function;
{
  register int i;
  register BUCKET_CONTENTS *tlist;
  alias_t *alias, **list;
  int list_index;

  i = HASH_ENTRIES (aliases);
  if (i == 0)
    return ((alias_t **)NULL);

  list = (alias_t **)xmalloc ((i + 1) * sizeof (alias_t *));
  for (i = list_index = 0; i < aliases->nbuckets; i++)
    {
      for (tlist = hash_items (i, aliases); tlist; tlist = tlist->next)
    {
      alias = (alias_t *)tlist->data;

      if (!function || (*function) (alias))
        {
          list[list_index++] = alias;
          list[list_index] = (alias_t *)NULL;
        }
    }
    }
  return (list);
}

static void
sort_aliases (array)
     alias_t **array;
{
  qsort (array, strvec_len ((char **)array), sizeof (alias_t *), (QSFUNC *)qsort_alias_compare);
}

static int
qsort_alias_compare (as1, as2)
     alias_t **as1, **as2;
{
  int result;

  if ((result = (*as1)->name[0] - (*as2)->name[0]) == 0)
    result = strcmp ((*as1)->name, (*as2)->name);

  return (result);
}

/* Return a sorted list of all defined aliases */
alias_t **
all_aliases ()
{
  alias_t **list;

  if (aliases == 0 || HASH_ENTRIES (aliases) == 0)
    return ((alias_t **)NULL);

  list = map_over_aliases ((sh_alias_map_func_t *)NULL);
  if (list)
    sort_aliases (list);
  return (list);
}

char *
alias_expand_word (s)
     char *s;
{
  alias_t *r;

  r = find_alias (s);
  return (r ? savestring (r->value) : (char *)NULL);
}

/* Readline support functions -- expand all aliases in a line. */

#if defined (READLINE)

/* Return non-zero if CHARACTER is a member of the class of characters
   that are self-delimiting in the shell (this really means that these
   characters delimit tokens). */
#define self_delimiting(character) (member ((character), " \t\n\r;|&()"))

/* Return non-zero if CHARACTER is a member of the class of characters
   that delimit commands in the shell. */
#define command_separator(character) (member ((character), "\r\n;|&("))

/* If this is 1, we are checking the next token read for alias expansion
   because it is the first word in a command. */
static int command_word;

/* This is for skipping quoted strings in alias expansions. */
#define quote_char(c)  (((c) == '\'') || ((c) == '"'))

/* Consume a quoted string from STRING, starting at string[START] (so
   string[START] is the opening quote character), and return the index
   of the closing quote character matching the opening quote character.
   This handles single matching pairs of unquoted quotes; it could afford
   to be a little smarter... This skips words between balanced pairs of
   quotes, words where the first character is quoted with a `\', and other
   backslash-escaped characters. */

static int
skipquotes (string, start)
     char *string;
     int start;
{
  register int i;
  int delimiter = string[start];

  /* i starts at START + 1 because string[START] is the opening quote
     character. */
  for (i = start + 1 ; string[i] ; i++)
    {
      if (string[i] == '\\')
    {
      i++;      /* skip backslash-quoted quote characters, too */
      continue;
    }

      if (string[i] == delimiter)
    return i;
    }
  return (i);
}

/* Skip the white space and any quoted characters in STRING, starting at
   START.  Return the new index into STRING, after zero or more characters
   have been skipped. */
static int
skipws (string, start)
     char *string;
     int start;
{
  register int i;
  int pass_next, backslash_quoted_word;
  unsigned char peekc;

  /* skip quoted strings, in ' or ", and words in which a character is quoted
     with a `\'. */
  i = backslash_quoted_word = pass_next = 0;

  /* Skip leading whitespace (or separator characters), and quoted words.
     But save it in the output.  */

  for (i = start; string[i]; i++)
    {
      if (pass_next)
    {
      pass_next = 0;
      continue;
    }

      if (whitespace (string[i]))
    {
      backslash_quoted_word = 0; /* we are no longer in a backslash-quoted word */
      continue;
    }

      if (string[i] == '\\')
    {
      peekc = string[i+1];
      if (ISLETTER (peekc))
        backslash_quoted_word++;    /* this is a backslash-quoted word */
      else
        pass_next++;
      continue;
    }

      /* This only handles single pairs of non-escaped quotes.  This
     overloads backslash_quoted_word to also mean that a word like
     ""f is being scanned, so that the quotes will inhibit any expansion
     of the word. */
      if (quote_char(string[i]))
    {
      i = skipquotes (string, i);
      /* This could be a line that contains a single quote character,
         in which case skipquotes () terminates with string[i] == '\0'
         (the end of the string).  Check for that here. */
      if (string[i] == '\0')
        break;

      peekc = string[i + 1];
      if (ISLETTER (peekc))
        backslash_quoted_word++;
      continue;
    }

      /* If we're in the middle of some kind of quoted word, let it
     pass through. */
      if (backslash_quoted_word)
    continue;

      /* If this character is a shell command separator, then set a hint for
     alias_expand that the next token is the first word in a command. */

      if (command_separator (string[i]))
    {
      command_word++;
      continue;
    }
      break;
    }
  return (i);
}

/* Characters that may appear in a token.  Basically, anything except white
   space and a token separator. */
#define token_char(c)   (!((whitespace (string[i]) || self_delimiting (string[i]))))

/* Read from START in STRING until the next separator character, and return
   the index of that separator.  Skip backslash-quoted characters.  Call
   skipquotes () for quoted strings in the middle or at the end of tokens,
   so all characters show up (e.g. foo'' and foo""bar) */
static int
rd_token (string, start)
     char *string;
     int start;
{
  register int i;

  /* From here to next separator character is a token. */
  for (i = start; string[i] && token_char (string[i]); i++)
    {
      if (string[i] == '\\')
    {
      i++;  /* skip backslash-escaped character */
      continue;
    }

      /* If this character is a quote character, we want to call skipquotes
     to get the whole quoted portion as part of this word.  That word
     will not generally match an alias, even if te unquoted word would
     have.  The presence of the quotes in the token serves then to
     inhibit expansion. */
      if (quote_char (string[i]))
    {
      i = skipquotes (string, i);
      /* This could be a line that contains a single quote character,
         in which case skipquotes () terminates with string[i] == '\0'
         (the end of the string).  Check for that here. */
      if (string[i] == '\0')
        break;

      /* Now string[i] is the matching quote character, and the
         quoted portion of the token has been scanned. */
      continue;
    }
    }
  return (i);
}

/* Return a new line, with any aliases substituted. */
char *
alias_expand (string)
     char *string;
{
  register int i, j, start;
  char *line, *token;
  int line_len, tl, real_start, expand_next, expand_this_token;
  alias_t *alias;

  line_len = strlen (string) + 1;
  line = (char *)xmalloc (line_len);
  token = (char *)xmalloc (line_len);

  line[0] = i = 0;
  expand_next = 0;
  command_word = 1; /* initialized to expand the first word on the line */

  /* Each time through the loop we find the next word in line.  If it
     has an alias, substitute the alias value.  If the value ends in ` ',
     then try again with the next word.  Else, if there is no value, or if
     the value does not end in space, we are done. */

  for (;;)
    {

      token[0] = 0;
      start = i;

      /* Skip white space and quoted characters */
      i = skipws (string, start);

      if (start == i && string[i] == '\0')
    {
      free (token);
      return (line);
    }

      /* copy the just-skipped characters into the output string,
     expanding it if there is not enough room. */
      j = strlen (line);
      tl = i - start;   /* number of characters just skipped */
      RESIZE_MALLOCED_BUFFER (line, j, (tl + 1), line_len, (tl + 50));
      strncpy (line + j, string + start, tl);
      line[j + tl] = '\0';

      real_start = i;

      command_word = command_word || (command_separator (string[i]));
      expand_this_token = (command_word || expand_next);
      expand_next = 0;

      /* Read the next token, and copy it into TOKEN. */
      start = i;
      i = rd_token (string, start);

      tl = i - start;   /* token length */

      /* If tl == 0, but we're not at the end of the string, then we have a
     single-character token, probably a delimiter */
      if (tl == 0 && string[i] != '\0')
    {
      tl = 1;
      i++;      /* move past it */
    }

      strncpy (token, string + start, tl);
      token [tl] = '\0';

      /* If there is a backslash-escaped character quoted in TOKEN,
     then we don't do alias expansion.  This should check for all
     other quoting characters, too. */
      if (xstrchr (token, '\\'))
    expand_this_token = 0;

      /* If we should be expanding here, if we are expanding all words, or if
     we are in a location in the string where an expansion is supposed to
     take place, see if this word has a substitution.  If it does, then do
     the expansion.  Note that we defer the alias value lookup until we
     are sure we are expanding this token. */

      if ((token[0]) &&
      (expand_this_token || alias_expand_all) &&
      (alias = find_alias (token)))
    {
      char *v;
      int vlen, llen;

      v = alias->value;
      vlen = strlen (v);
      llen = strlen (line);

      /* +3 because we possibly add one more character below. */
      RESIZE_MALLOCED_BUFFER (line, llen, (vlen + 3), line_len, (vlen + 50));

      strcpy (line + llen, v);

      if ((expand_this_token && vlen && whitespace (v[vlen - 1])) ||
          alias_expand_all)
        expand_next = 1;
    }
      else
    {
      int llen, tlen;

      llen = strlen (line);
      tlen = i - real_start; /* tlen == strlen(token) */

      RESIZE_MALLOCED_BUFFER (line, llen, (tlen + 1), line_len, (llen + tlen + 50));

      strncpy (line + llen, string + real_start, tlen);
      line[llen + tlen] = '\0';
    }
      command_word = 0;
    }
}
#endif /* READLINE */
#endif /* ALIAS */

https://git.savannah.gnu.org/cgit/bash.git/tree/array.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/array.c used in: Step 1232
/*
 * array.c - functions to create, destroy, access, and manipulate arrays
 *       of strings.
 *
 * Arrays are sparse doubly-linked lists.  An element's index is stored
 * with it.
 *
 * Chet Ramey
 * chet@ins.cwru.edu
 */

/* Copyright (C) 1997-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#if defined (ARRAY_VARS)

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include <stdio.h>
#include "bashansi.h"

#include "shell.h"
#include "array.h"
#include "builtins/common.h"

#define ADD_BEFORE(ae, new) \
    do { \
        ae->prev->next = new; \
        new->prev = ae->prev; \
        ae->prev = new; \
        new->next = ae; \
    } while(0)

static char *array_to_string_internal __P((ARRAY_ELEMENT *, ARRAY_ELEMENT *, char *, int));

ARRAY *
array_create()
{
    ARRAY   *r;
    ARRAY_ELEMENT   *head;

    r =(ARRAY *)xmalloc(sizeof(ARRAY));
    r->type = array_indexed;
    r->max_index = -1;
    r->num_elements = 0;
    head = array_create_element(-1, (char *)NULL);  /* dummy head */
    head->prev = head->next = head;
    r->head = head;
    return(r);
}

void
array_flush (a)
ARRAY   *a;
{
    register ARRAY_ELEMENT *r, *r1;

    if (a == 0)
        return;
    for (r = element_forw(a->head); r != a->head; ) {
        r1 = element_forw(r);
        array_dispose_element(r);
        r = r1;
    }
    a->head->next = a->head->prev = a->head;
    a->max_index = -1;
    a->num_elements = 0;
}

void
array_dispose(a)
ARRAY   *a;
{
    if (a == 0)
        return;
    array_flush (a);
    array_dispose_element(a->head);
    free(a);
}

ARRAY *
array_copy(a)
ARRAY   *a;
{
    ARRAY   *a1;
    ARRAY_ELEMENT   *ae, *new;

    if (!a)
        return((ARRAY *) NULL);
    a1 = array_create();
    a1->type = a->type;
    a1->max_index = a->max_index;
    a1->num_elements = a->num_elements;
    for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
        new = array_create_element(element_index(ae), element_value(ae));
        ADD_BEFORE(a1->head, new);
    }
    return(a1);
}

#ifdef INCLUDE_UNUSED
/*
 * Make and return a new array composed of the elements in array A from
 * S to E, inclusive.
 */
ARRAY *
array_slice(array, s, e)
ARRAY       *array;
ARRAY_ELEMENT   *s, *e;
{
    ARRAY   *a;
    ARRAY_ELEMENT *p, *n;
    int i;
    arrayind_t mi;

    a = array_create ();
    a->type = array->type;

    for (p = s, i = 0; p != e; p = element_forw(p), i++) {
        n = array_create_element (element_index(p), element_value(p));
        ADD_BEFORE(a->head, n);
        mi = element_index(ae);
    }
    a->num_elements = i;
    a->max_index = mi;
    return a;
}
#endif

/*
 * Walk the array, calling FUNC once for each element, with the array
 * element as the argument.
 */
void
array_walk(a, func)
ARRAY   *a;
sh_ae_map_func_t *func;
{
    register ARRAY_ELEMENT *ae;

    if (a == 0 || array_empty(a))
        return;
    for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
        if ((*func)(ae) < 0)
            return;
}

/*
 * Shift the array A N elements to the left.  Delete the first N elements
 * and subtract N from the indices of the remaining elements.  If FLAGS
 * does not include AS_DISPOSE, this returns a singly-linked null-terminated
 * list of elements so the caller can dispose of the chain.  If FLAGS
 * includes AS_DISPOSE, this function disposes of the shifted-out elements
 * and returns NULL.
 */
ARRAY_ELEMENT *
array_shift(a, n, flags)
ARRAY   *a;
int n, flags;
{
    register ARRAY_ELEMENT *ae, *ret;
    register int i;

    if (a == 0 || array_empty(a) || n <= 0)
        return ((ARRAY_ELEMENT *)NULL);

    for (i = 0, ret = ae = element_forw(a->head); ae != a->head && i < n; ae = element_forw(ae), i++)
        ;
    if (ae == a->head) {
        /* Easy case; shifting out all of the elements */
        if (flags & AS_DISPOSE) {
            array_flush (a);
            return ((ARRAY_ELEMENT *)NULL);
        }
        for (ae = ret; element_forw(ae) != a->head; ae = element_forw(ae))
            ;
        element_forw(ae) = (ARRAY_ELEMENT *)NULL;
        a->head->next = a->head->prev = a->head;
        a->max_index = -1;
        a->num_elements = 0;
        return ret;
    }
    /*
     * ae now points to the list of elements we want to retain.
     * ret points to the list we want to either destroy or return.
     */
    ae->prev->next = (ARRAY_ELEMENT *)NULL;     /* null-terminate RET */

    a->head->next = ae;     /* slice RET out of the array */
    ae->prev = a->head;

    for ( ; ae != a->head; ae = element_forw(ae))
        element_index(ae) -= n; /* renumber retained indices */

    a->num_elements -= n;       /* modify bookkeeping information */
    a->max_index -= n;

    if (flags & AS_DISPOSE) {
        for (ae = ret; ae; ) {
            ret = element_forw(ae);
            array_dispose_element(ae);
            ae = ret;
        }
        return ((ARRAY_ELEMENT *)NULL);
    }

    return ret;
}

/*
 * Shift array A right N indices.  If S is non-null, it becomes the value of
 * the new element 0.  Returns the number of elements in the array after the
 * shift.
 */
int
array_rshift (a, n, s)
ARRAY   *a;
int n;
char    *s;
{
    register ARRAY_ELEMENT  *ae, *new;

    if (a == 0)
        return 0;
    if (n <= 0)
        return (a->num_elements);

    ae = element_forw(a->head);
    if (s) {
        new = array_create_element(0, s);
        ADD_BEFORE(ae, new);
        a->num_elements++;
    }

    a->max_index += n;

    /*
     * Renumber all elements in the array except the one we just added.
     */
    for ( ; ae != a->head; ae = element_forw(ae))
        element_index(ae) += n;

    return (a->num_elements);
}

ARRAY   *
array_quote(array)
ARRAY   *array;
{
    ARRAY_ELEMENT   *a;
    char    *t;

    if (array == 0 || array->head == 0 || array_empty (array))
        return (ARRAY *)NULL;
    for (a = element_forw(array->head); a != array->head; a = element_forw(a)) {
        t = quote_string (a->value);
        FREE(a->value);
        a->value = t;
    }
    return array;
}

char *
array_subrange (a, start, end, quoted)
ARRAY   *a;
arrayind_t  start, end;
int quoted;
{
    ARRAY_ELEMENT   *h, *p;
    arrayind_t  i;

    p = array_head (a);
    if (p == 0 || array_empty (a) || start > array_num_elements (a))
        return ((char *)NULL);

    for (i = 0, p = element_forw(p); p != a->head && i < start; i++, p = element_forw(p))
        ;
    if (p == a->head)
        return ((char *)NULL);
    for (h = p; p != a->head && i < end; i++, p = element_forw(p))
        ;

    return (array_to_string_internal (h, p, " ", quoted));
}

char *
array_patsub (a, pat, rep, mflags)
ARRAY   *a;
char    *pat, *rep;
int mflags;
{
    ARRAY       *a2;
    ARRAY_ELEMENT   *e;
    char    *t;

    if (array_head (a) == 0 || array_empty (a))
        return ((char *)NULL);

    a2 = array_copy (a);
    for (e = element_forw(a2->head); e != a2->head; e = element_forw(e)) {
        t = pat_subst(element_value(e), pat, rep, mflags);
        FREE(element_value(e));
        e->value = t;
    }

    if (mflags & MATCH_QUOTED)
        array_quote (a2);
    t = array_to_string (a2, " ", 0);
    array_dispose (a2);

    return t;
}

/*
 * Allocate and return a new array element with index INDEX and value
 * VALUE.
 */
ARRAY_ELEMENT *
array_create_element(indx, value)
arrayind_t  indx;
char    *value;
{
    ARRAY_ELEMENT *r;

    r = (ARRAY_ELEMENT *)xmalloc(sizeof(ARRAY_ELEMENT));
    r->ind = indx;
    r->value = value ? savestring(value) : (char *)NULL;
    r->next = r->prev = (ARRAY_ELEMENT *) NULL;
    return(r);
}

#ifdef INCLUDE_UNUSED
ARRAY_ELEMENT *
array_copy_element(ae)
ARRAY_ELEMENT   *ae;
{
    return(ae ? array_create_element(element_index(ae), element_value(ae))
          : (ARRAY_ELEMENT *) NULL);
}
#endif

void
array_dispose_element(ae)
ARRAY_ELEMENT   *ae;
{
    FREE(ae->value);
    free(ae);
}

/*
 * Add a new element with index I and value V to array A (a[i] = v).
 */
int
array_insert(a, i, v)
ARRAY   *a;
arrayind_t  i;
char    *v;
{
    register ARRAY_ELEMENT *new, *ae;

    if (!a)
        return(-1);
    new = array_create_element(i, v);
    if (i > array_max_index(a)) {
        /*
         * Hook onto the end.  This also works for an empty array.
         * Fast path for the common case of allocating arrays
         * sequentially.
         */
        ADD_BEFORE(a->head, new);
        a->max_index = i;
        a->num_elements++;
        return(0);
    }
    /*
     * Otherwise we search for the spot to insert it.
     */
    for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
        if (element_index(ae) == i) {
            /*
             * Replacing an existing element.
             */
            array_dispose_element(new);
            free(element_value(ae));
            ae->value = savestring(v);
            return(0);
        } else if (element_index(ae) > i) {
            ADD_BEFORE(ae, new);
            a->num_elements++;
            return(0);
        }
    }
    return (-1);        /* problem */
}

/*
 * Delete the element with index I from array A and return it so the
 * caller can dispose of it.
 */
ARRAY_ELEMENT *
array_remove(a, i)
ARRAY   *a;
arrayind_t  i;
{
    register ARRAY_ELEMENT *ae;

    if (!a || array_empty(a))
        return((ARRAY_ELEMENT *) NULL);
    for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
        if (element_index(ae) == i) {
            ae->next->prev = ae->prev;
            ae->prev->next = ae->next;
            a->num_elements--;
            if (i == array_max_index(a))
                a->max_index = element_index(ae->prev);
            return(ae);
        }
    return((ARRAY_ELEMENT *) NULL);
}

/*
 * Return the value of a[i].
 */
char *
array_reference(a, i)
ARRAY   *a;
arrayind_t  i;
{
    register ARRAY_ELEMENT *ae;

    if (a == 0 || array_empty(a))
        return((char *) NULL);
    for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
        if (element_index(ae) == i)
            return(element_value(ae));
    return((char *) NULL);
}

/* Convenience routines for the shell to translate to and from the form used
   by the rest of the code. */
WORD_LIST *
array_to_word_list(a)
ARRAY   *a;
{
    WORD_LIST   *list;
    ARRAY_ELEMENT   *ae;

    if (a == 0 || array_empty(a))
        return((WORD_LIST *)NULL);
    list = (WORD_LIST *)NULL;
    for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
        list = make_word_list (make_bare_word(element_value(ae)), list);
    return (REVERSE_LIST(list, WORD_LIST *));
}

ARRAY *
array_from_word_list (list)
WORD_LIST   *list;
{
    ARRAY   *a;

    if (list == 0)
        return((ARRAY *)NULL);
    a = array_create();
    return (array_assign_list (a, list));
}

ARRAY *
array_assign_list (array, list)
ARRAY   *array;
WORD_LIST   *list;
{
    register WORD_LIST *l;
    register arrayind_t i;

    for (l = list, i = 0; l; l = l->next, i++)
        array_insert(array, i, l->word->word);
    return array;
}

char **
array_to_argv (a)
ARRAY   *a;
{
    char        **ret, *t;
    int     i;
    ARRAY_ELEMENT   *ae;

    if (a == 0 || array_empty(a))
        return ((char **)NULL);
    ret = strvec_create (array_num_elements (a) + 1);
    i = 0;
    for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
        t = element_value (ae);
        ret[i++] = t ? savestring (t) : (char *)NULL;
    }
    ret[i] = (char *)NULL;
    return (ret);
}
    
/*
 * Return a string that is the concatenation of all the elements in A,
 * separated by SEP.
 */
static char *
array_to_string_internal (start, end, sep, quoted)
ARRAY_ELEMENT   *start, *end;
char    *sep;
int quoted;
{
    char    *result, *t;
    ARRAY_ELEMENT *ae;
    int slen, rsize, rlen, reg;

    if (start == end)   /* XXX - should not happen */
        return ((char *)NULL);

    slen = strlen(sep);
    result = NULL;
    for (rsize = rlen = 0, ae = start; ae != end; ae = element_forw(ae)) {
        if (rsize == 0)
            result = (char *)xmalloc (rsize = 64);
        if (element_value(ae)) {
            t = quoted ? quote_string(element_value(ae)) : element_value(ae);
            reg = strlen(t);
            RESIZE_MALLOCED_BUFFER (result, rlen, (reg + slen + 2),
                        rsize, rsize);
            strcpy(result + rlen, t);
            rlen += reg;
            if (quoted && t)
                free(t);
            /*
             * Add a separator only after non-null elements.
             */
            if (element_forw(ae) != end) {
                strcpy(result + rlen, sep);
                rlen += slen;
            }
        }
    }
    if (result)
      result[rlen] = '\0';  /* XXX */
    return(result);
}

char *
array_to_assign (a, quoted)
ARRAY   *a;
int quoted;
{
    char    *result, *valstr, *is;
    char    indstr[INT_STRLEN_BOUND(intmax_t) + 1];
    ARRAY_ELEMENT *ae;
    int rsize, rlen, elen;

    if (a == 0 || array_empty (a))
        return((char *)NULL);

    result = (char *)xmalloc (rsize = 128);
    result[0] = '(';
    rlen = 1;

    for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
        is = inttostr (element_index(ae), indstr, sizeof(indstr));
        valstr = element_value (ae) ? sh_double_quote (element_value(ae))
                        : (char *)NULL;
        elen = STRLEN (indstr) + 8 + STRLEN (valstr);
        RESIZE_MALLOCED_BUFFER (result, rlen, (elen + 1), rsize, rsize);

        result[rlen++] = '[';
        strcpy (result + rlen, is);
        rlen += STRLEN (is);
        result[rlen++] = ']';
        result[rlen++] = '=';
        if (valstr) {
            strcpy (result + rlen, valstr);
            rlen += STRLEN (valstr);
        }

        if (element_forw(ae) != a->head)
          result[rlen++] = ' ';

        FREE (valstr);
    }
    RESIZE_MALLOCED_BUFFER (result, rlen, 1, rsize, 8);
    result[rlen++] = ')';
    result[rlen] = '\0';
    if (quoted) {
        /* This is not as efficient as it could be... */
        valstr = sh_single_quote (result);
        free (result);
        result = valstr;
    }
    return(result);
}

char *
array_to_string (a, sep, quoted)
ARRAY   *a;
char    *sep;
int quoted;
{
    if (a == 0)
        return((char *)NULL);
    if (array_empty(a))
        return(savestring(""));
    return (array_to_string_internal (element_forw(a->head), a->head, sep, quoted));
}

#if defined (INCLUDE_UNUSED) || defined (TEST_ARRAY)
/*
 * Return an array consisting of elements in S, separated by SEP
 */
ARRAY *
array_from_string(s, sep)
char    *s, *sep;
{
    ARRAY   *a;
    WORD_LIST *w;

    if (s == 0)
        return((ARRAY *)NULL);
    w = list_string (s, sep, 0);
    if (w == 0)
        return((ARRAY *)NULL);
    a = array_from_word_list (w);
    return (a);
}
#endif

#if defined (TEST_ARRAY)
/*
 * To make a running version, compile -DTEST_ARRAY and link with:
 *  xmalloc.o syntax.o lib/malloc/libmalloc.a lib/sh/libsh.a
 */
int interrupt_immediately = 0;

int
signal_is_trapped(s)
int s;
{
    return 0;
}

void
fatal_error(const char *s, ...)
{
    fprintf(stderr, "array_test: fatal memory error\n");
    abort();
}

void
programming_error(const char *s, ...)
{
    fprintf(stderr, "array_test: fatal programming error\n");
    abort();
}

WORD_DESC *
make_bare_word (s)
const char  *s;
{
    WORD_DESC *w;

    w = (WORD_DESC *)xmalloc(sizeof(WORD_DESC));
    w->word = s ? savestring(s) : savestring ("");
    w->flags = 0;
    return w;
}

WORD_LIST *
make_word_list(x, l)
WORD_DESC   *x;
WORD_LIST   *l;
{
    WORD_LIST *w;

    w = (WORD_LIST *)xmalloc(sizeof(WORD_LIST));
    w->word = x;
    w->next = l;
    return w;
}

WORD_LIST *
list_string(s, t, i)
char    *s, *t;
int i;
{
    char    *r, *a;
    WORD_LIST   *wl;

    if (s == 0)
        return (WORD_LIST *)NULL;
    r = savestring(s);
    wl = (WORD_LIST *)NULL;
    a = strtok(r, t);
    while (a) {
        wl = make_word_list (make_bare_word(a), wl);
        a = strtok((char *)NULL, t);
    }
    return (REVERSE_LIST (wl, WORD_LIST *));
}

GENERIC_LIST *
list_reverse (list)
GENERIC_LIST    *list;
{
    register GENERIC_LIST *next, *prev;

    for (prev = 0; list; ) {
        next = list->next;
        list->next = prev;
        prev = list;
        list = next;
    }
    return prev;
}

char *
pat_subst(s, t, u, i)
char    *s, *t, *u;
int i;
{
    return ((char *)NULL);
}

char *
quote_string(s)
char    *s;
{
    return savestring(s);
}

print_element(ae)
ARRAY_ELEMENT   *ae;
{
    char    lbuf[INT_STRLEN_BOUND (intmax_t) + 1];

    printf("array[%s] = %s\n",
        inttostr (element_index(ae), lbuf, sizeof (lbuf)),
        element_value(ae));
}

print_array(a)
ARRAY   *a;
{
    printf("\n");
    array_walk(a, print_element);
}

main()
{
    ARRAY   *a, *new_a, *copy_of_a;
    ARRAY_ELEMENT   *ae, *aew;
    char    *s;

    a = array_create();
    array_insert(a, 1, "one");
    array_insert(a, 7, "seven");
    array_insert(a, 4, "four");
    array_insert(a, 1029, "one thousand twenty-nine");
    array_insert(a, 12, "twelve");
    array_insert(a, 42, "forty-two");
    print_array(a);
    s = array_to_string (a, " ", 0);
    printf("s = %s\n", s);
    copy_of_a = array_from_string(s, " ");
    printf("copy_of_a:");
    print_array(copy_of_a);
    array_dispose(copy_of_a);
    printf("\n");
    free(s);
    ae = array_remove(a, 4);
    array_dispose_element(ae);
    ae = array_remove(a, 1029);
    array_dispose_element(ae);
    array_insert(a, 16, "sixteen");
    print_array(a);
    s = array_to_string (a, " ", 0);
    printf("s = %s\n", s);
    copy_of_a = array_from_string(s, " ");
    printf("copy_of_a:");
    print_array(copy_of_a);
    array_dispose(copy_of_a);
    printf("\n");
    free(s);
    array_insert(a, 2, "two");
    array_insert(a, 1029, "new one thousand twenty-nine");
    array_insert(a, 0, "zero");
    array_insert(a, 134, "");
    print_array(a);
    s = array_to_string (a, ":", 0);
    printf("s = %s\n", s);
    copy_of_a = array_from_string(s, ":");
    printf("copy_of_a:");
    print_array(copy_of_a);
    array_dispose(copy_of_a);
    printf("\n");
    free(s);
    new_a = array_copy(a);
    print_array(new_a);
    s = array_to_string (new_a, ":", 0);
    printf("s = %s\n", s);
    copy_of_a = array_from_string(s, ":");
    free(s);
    printf("copy_of_a:");
    print_array(copy_of_a);
    array_shift(copy_of_a, 2, AS_DISPOSE);
    printf("copy_of_a shifted by two:");
    print_array(copy_of_a);
    ae = array_shift(copy_of_a, 2, 0);
    printf("copy_of_a shifted by two:");
    print_array(copy_of_a);
    for ( ; ae; ) {
        aew = element_forw(ae);
        array_dispose_element(ae);
        ae = aew;
    }
    array_rshift(copy_of_a, 1, (char *)0);
    printf("copy_of_a rshift by 1:");
    print_array(copy_of_a);
    array_rshift(copy_of_a, 2, "new element zero");
    printf("copy_of_a rshift again by 2 with new element zero:");
    print_array(copy_of_a);
    s = array_to_assign(copy_of_a, 0);
    printf("copy_of_a=%s\n", s);
    free(s);
    ae = array_shift(copy_of_a, array_num_elements(copy_of_a), 0);
    for ( ; ae; ) {
        aew = element_forw(ae);
        array_dispose_element(ae);
        ae = aew;
    }
    array_dispose(copy_of_a);
    printf("\n");
    array_dispose(a);
    array_dispose(new_a);
}

#endif /* TEST_ARRAY */
#endif /* ARRAY_VARS */

https://git.savannah.gnu.org/cgit/bash.git/tree/arrayfunc.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/arrayfunc.c used in: Step 1233
/* arrayfunc.c -- High-level array functions used by other parts of the shell. */

/* Copyright (C) 2001-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#if defined (ARRAY_VARS)

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif
#include <stdio.h>

#include "shell.h"

#include "shmbutil.h"

#include "builtins/common.h"

extern char *this_command_name;
extern int last_command_exit_value;

static void quote_array_assignment_chars __P((WORD_LIST *));
static char *array_value_internal __P((char *, int, int, int *));

/* **************************************************************** */
/*                                  */
/*  Functions to manipulate array variables and perform assignments */
/*                                  */
/* **************************************************************** */

/* Convert a shell variable to an array variable.  The original value is
   saved as array[0]. */
SHELL_VAR *
convert_var_to_array (var)
     SHELL_VAR *var;
{
  char *oldval;
  ARRAY *array;

  oldval = value_cell (var);
  array = array_create ();
  array_insert (array, 0, oldval);

  FREE (value_cell (var));
  var_setarray (var, array);

  /* these aren't valid anymore */
  var->dynamic_value = (sh_var_value_func_t *)NULL;
  var->assign_func = (sh_var_assign_func_t *)NULL;

  INVALIDATE_EXPORTSTR (var);

  VSETATTR (var, att_array);
  VUNSETATTR (var, att_invisible);

  return var;
}

/* Perform an array assignment name[ind]=value.  If NAME already exists and
   is not an array, and IND is 0, perform name=value instead.  If NAME exists
   and is not an array, and IND is not 0, convert it into an array with the
   existing value as name[0].

   If NAME does not exist, just create an array variable, no matter what
   IND's value may be. */
SHELL_VAR *
bind_array_variable (name, ind, value)
     char *name;
     arrayind_t ind;
     char *value;
{
  SHELL_VAR *entry;
  char *newval;

  entry = var_lookup (name, shell_variables);

  if (entry == (SHELL_VAR *) 0)
    entry = make_new_array_variable (name);
  else if (readonly_p (entry) || noassign_p (entry))
    {
      if (readonly_p (entry))
    err_readonly (name);
      return (entry);
    }
  else if (array_p (entry) == 0)
    entry = convert_var_to_array (entry);

  /* ENTRY is an array variable, and ARRAY points to the value. */
  newval = make_variable_value (entry, value);
  if (entry->assign_func)
    (*entry->assign_func) (entry, newval, ind);
  else
    array_insert (array_cell (entry), ind, newval);
  FREE (newval);

  return (entry);
}

/* Parse NAME, a lhs of an assignment statement of the form v[s], and
   assign VALUE to that array element by calling bind_array_variable(). */
SHELL_VAR *
assign_array_element (name, value)
     char *name, *value;
{
  char *sub, *vname;
  arrayind_t ind;
  int sublen;
  SHELL_VAR *entry;

  vname = array_variable_name (name, &sub, &sublen);

  if (vname == 0)
    return ((SHELL_VAR *)NULL);

  if ((ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']') || (sublen <= 1))
    {
      free (vname);
      err_badarraysub (name);
      return ((SHELL_VAR *)NULL);
    }

  ind = array_expand_index (sub, sublen);
  if (ind < 0)
    {
      free (vname);
      err_badarraysub (name);
      return ((SHELL_VAR *)NULL);
    }

  entry = bind_array_variable (vname, ind, value);

  free (vname);
  return (entry);
}

/* Find the array variable corresponding to NAME.  If there is no variable,
   create a new array variable.  If the variable exists but is not an array,
   convert it to an indexed array.  If CHECK_FLAGS is non-zero, an existing
   variable is checked for the readonly or noassign attribute in preparation
   for assignment (e.g., by the `read' builtin). */
SHELL_VAR *
find_or_make_array_variable (name, check_flags)
     char *name;
     int check_flags;
{
  SHELL_VAR *var;

  var = find_variable (name);

  if (var == 0)
    var = make_new_array_variable (name);
  else if (check_flags && (readonly_p (var) || noassign_p (var)))
    {
      if (readonly_p (var))
    err_readonly (name);
      return ((SHELL_VAR *)NULL);
    }
  else if (array_p (var) == 0)
    var = convert_var_to_array (var);

  return (var);
}
  
/* Perform a compound assignment statement for array NAME, where VALUE is
   the text between the parens:  NAME=( VALUE ) */
SHELL_VAR *
assign_array_from_string (name, value)
     char *name, *value;
{
  SHELL_VAR *var;

  var = find_or_make_array_variable (name, 1);
  if (var == 0)
    return ((SHELL_VAR *)NULL);

  return (assign_array_var_from_string (var, value));
}

/* Sequentially assign the indices of indexed array variable VAR from the
   words in LIST. */
SHELL_VAR *
assign_array_var_from_word_list (var, list)
     SHELL_VAR *var;
     WORD_LIST *list;
{
  register arrayind_t i;
  register WORD_LIST *l;
  ARRAY *a;

  for (a = array_cell (var), l = list, i = 0; l; l = l->next, i++)
    if (var->assign_func)
      (*var->assign_func) (var, l->word->word, i);
    else
      array_insert (a, i, l->word->word);
  return var;
}

/* Perform a compound array assignment:  VAR->name=( VALUE ).  The
   VALUE has already had the parentheses stripped. */
SHELL_VAR *
assign_array_var_from_string (var, value)
     SHELL_VAR *var;
     char *value;
{
  ARRAY *a;
  WORD_LIST *list, *nlist;
  char *w, *val, *nval;
  int ni, len;
  arrayind_t ind, last_ind;

  if (value == 0)
    return var;

  /* If this is called from declare_builtin, value[0] == '(' and
     xstrchr(value, ')') != 0.  In this case, we need to extract
     the value from between the parens before going on. */
  if (*value == '(')    /*)*/
    {
      ni = 1;
      val = extract_array_assignment_list (value, &ni);
      if (val == 0)
    return var;
    }
  else
    val = value;

  /* Expand the value string into a list of words, performing all the
     shell expansions including pathname generation and word splitting. */
  /* First we split the string on whitespace, using the shell parser
     (ksh93 seems to do this). */
  list = parse_string_to_word_list (val, "array assign");

  /* If we're using [subscript]=value, we need to quote each [ and ] to
     prevent unwanted filename expansion. */
  if (list)
    quote_array_assignment_chars (list);

  /* Now that we've split it, perform the shell expansions on each
     word in the list. */
  nlist = list ? expand_words_no_vars (list) : (WORD_LIST *)NULL;

  dispose_words (list);

  if (val != value)
    free (val);

  a = array_cell (var);

  /* Now that we are ready to assign values to the array, kill the existing
     value. */
  if (a)
    array_flush (a);

  for (last_ind = 0, list = nlist; list; list = list->next)
    {
      w = list->word->word;

      /* We have a word of the form [ind]=value */
      if (w[0] == '[')
    {
      len = skipsubscript (w, 0);

      if (w[len] != ']' || w[len+1] != '=')
        {
          nval = make_variable_value (var, w);
          if (var->assign_func)
        (*var->assign_func) (var, nval, last_ind);
          else
        array_insert (a, last_ind, nval);
          FREE (nval);
          last_ind++;
          continue;
        }

      if (len == 1)
        {
          err_badarraysub (w);
          continue;
        }

      if (ALL_ELEMENT_SUB (w[1]) && len == 2)
        {
          report_error ("%s: cannot assign to non-numeric index", w);
          continue;
        }

      ind = array_expand_index (w + 1, len);
      if (ind < 0)
        {
          err_badarraysub (w);
          continue;
        }
      last_ind = ind;
      val = w + len + 2;
    }
      else      /* No [ind]=value, just a stray `=' */
    {
      ind = last_ind;
      val = w;
    }

      if (integer_p (var))
    this_command_name = (char *)NULL;   /* no command name for errors */
      nval = make_variable_value (var, val);
      if (var->assign_func)
    (*var->assign_func) (var, nval, ind);
      else
    array_insert (a, ind, nval);
      FREE (nval);
      last_ind++;
    }

  dispose_words (nlist);
  return (var);
}

/* For each word in a compound array assignment, if the word looks like
   [ind]=value, quote the `[' and `]' before the `=' to protect them from
   unwanted filename expansion. */
static void
quote_array_assignment_chars (list)
     WORD_LIST *list;
{
  char *s, *t, *nword;
  int saw_eq;
  WORD_LIST *l;

  for (l = list; l; l = l->next)
    {
      if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
    continue;   /* should not happen, but just in case... */
      /* Don't bother if it doesn't look like [ind]=value */
      if (l->word->word[0] != '[' || xstrchr (l->word->word, '=') == 0) /* ] */
    continue;
      s = nword = (char *)xmalloc (strlen (l->word->word) * 2 + 1);
      saw_eq = 0;
      for (t = l->word->word; *t; )
    {
      if (*t == '=')
        saw_eq = 1;
      if (saw_eq == 0 && (*t == '[' || *t == ']'))
        *s++ = '\\';
      *s++ = *t++;
    }
      *s = '\0';
      free (l->word->word);
      l->word->word = nword;
    }
}

/* This function assumes s[i] == '['; returns with s[ret] == ']' if
   an array subscript is correctly parsed. */
int
skipsubscript (s, i)
     const char *s;
     int i;
{
  int count, c;
#if defined (HANDLE_MULTIBYTE)
  mbstate_t state, state_bak;
  size_t slength, mblength;
  size_t mb_cur_max;
#endif

#if defined (HANDLE_MULTIBYTE)
  memset (&state, '\0', sizeof (mbstate_t));
  slength = strlen (s + i);
  mb_cur_max = MB_CUR_MAX;
#endif
  
  count = 1;
  while (count)
    {
      /* Advance one (possibly multibyte) character in S starting at I. */
#if defined (HANDLE_MULTIBYTE)
      if (mb_cur_max > 1)
    {
      state_bak = state;
      mblength = mbrlen (s + i, slength, &state);

      if (mblength == (size_t)-2 || mblength == (size_t)-1)
        {
          state = state_bak;
          i++;
          slength--;
        }
      else if (mblength == 0)
        return i;
      else
        {
          i += mblength;
          slength -= mblength;
        }
    }
      else
#endif
      ++i;

      c = s[i];

      if (c == 0)
        break;
      else if (c == '[')
    count++;
      else if (c == ']')
    count--;
    }

  return i;
}

/* This function is called with SUB pointing to just after the beginning
   `[' of an array subscript and removes the array element to which SUB
   expands from array VAR.  A subscript of `*' or `@' unsets the array. */
int
unbind_array_element (var, sub)
     SHELL_VAR *var;
     char *sub;
{
  int len;
  arrayind_t ind;
  ARRAY_ELEMENT *ae;

  len = skipsubscript (sub, 0);
  if (sub[len] != ']' || len == 0)
    {
      builtin_error ("%s[%s: bad array subscript", var->name, sub);
      return -1;
    }
  sub[len] = '\0';

  if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
    {
      unbind_variable (var->name);
      return (0);
    }
  ind = array_expand_index (sub, len+1);
  if (ind < 0)
    {
      builtin_error ("[%s]: bad array subscript", sub);
      return -1;
    }
  ae = array_remove (array_cell (var), ind);
  if (ae)
    array_dispose_element (ae);
  return 0;
}

/* Format and output an array assignment in compound form VAR=(VALUES),
   suitable for re-use as input. */
void
print_array_assignment (var, quoted)
     SHELL_VAR *var;
     int quoted;
{
  char *vstr;

  vstr = array_to_assign (array_cell (var), quoted);

  if (vstr == 0)
    printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
  else
    {
      printf ("%s=%s\n", var->name, vstr);
      free (vstr);
    }
}

/***********************************************************************/
/*                                     */
/* Utility functions to manage arrays and their contents for expansion */
/*                                     */
/***********************************************************************/

/* Return 1 if NAME is a properly-formed array reference v[sub]. */
int
valid_array_reference (name)
     char *name;
{
  char *t;
  int r, len;

  t = xstrchr (name, '[');  /* ] */
  if (t)
    {
      *t = '\0';
      r = legal_identifier (name);
      *t = '[';
      if (r == 0)
    return 0;
      /* Check for a properly-terminated non-blank subscript. */
      len = skipsubscript (t, 0);
      if (t[len] != ']' || len == 1)
    return 0;
      for (r = 1; r < len; r++)
    if (whitespace (t[r]) == 0)
      return 1;
      return 0;
    }
  return 0;
}

/* Expand the array index beginning at S and extending LEN characters. */
arrayind_t
array_expand_index (s, len)
     char *s;
     int len;
{
  char *exp, *t;
  int expok;
  arrayind_t val;

  exp = (char *)xmalloc (len);
  strncpy (exp, s, len - 1);
  exp[len - 1] = '\0';
  t = expand_string_to_string (exp, 0);
  this_command_name = (char *)NULL;
  val = evalexp (t, &expok);
  free (t);
  free (exp);
  if (expok == 0)
    {
      last_command_exit_value = EXECUTION_FAILURE;
      jump_to_top_level (DISCARD);
    }
  return val;
}

/* Return the name of the variable specified by S without any subscript.
   If SUBP is non-null, return a pointer to the start of the subscript
   in *SUBP. If LENP is non-null, the length of the subscript is returned
   in *LENP.  This returns newly-allocated memory. */
char *
array_variable_name (s, subp, lenp)
     char *s, **subp;
     int *lenp;
{
  char *t, *ret;
  int ind, ni;

  t = xstrchr (s, '[');
  if (t == 0)
    return ((char *)NULL);
  ind = t - s;
  ni = skipsubscript (s, ind);
  if (ni <= ind + 1 || s[ni] != ']')
    {
      err_badarraysub (s);
      return ((char *)NULL);
    }

  *t = '\0';
  ret = savestring (s);
  *t++ = '[';       /* ] */

  if (subp)
    *subp = t;
  if (lenp)
    *lenp = ni - ind;

  return ret;
}

/* Return the variable specified by S without any subscript.  If SUBP is
   non-null, return a pointer to the start of the subscript in *SUBP.
   If LENP is non-null, the length of the subscript is returned in *LENP. */
SHELL_VAR *
array_variable_part (s, subp, lenp)
     char *s, **subp;
     int *lenp;
{
  char *t;
  SHELL_VAR *var;

  t = array_variable_name (s, subp, lenp);
  if (t == 0)
    return ((SHELL_VAR *)NULL);
  var = find_variable (t);

  free (t);
  return var;
}

/* Return a string containing the elements in the array and subscript
   described by S.  If the subscript is * or @, obeys quoting rules akin
   to the expansion of $* and $@ including double quoting.  If RTYPE
   is non-null it gets 1 if the array reference is name[@] or name[*]
   and 0 otherwise. */
static char *
array_value_internal (s, quoted, allow_all, rtype)
     char *s;
     int quoted, allow_all, *rtype;
{
  int len;
  arrayind_t ind;
  char *retval, *t, *temp;
  WORD_LIST *l;
  SHELL_VAR *var;

  var = array_variable_part (s, &t, &len);

  /* Expand the index, even if the variable doesn't exist, in case side
     effects are needed, like ${w[i++]} where w is unset. */
#if 0
  if (var == 0)
    return (char *)NULL;
#endif

  /* [ */
  if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
    {
      if (rtype)
    *rtype = 1;
      if (allow_all == 0)
    {
      err_badarraysub (s);
      return ((char *)NULL);
    }
      else if (var == 0)
    return ((char *)NULL);
      else if (array_p (var) == 0)
    l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL);
      else
    {
      l = array_to_word_list (array_cell (var));
      if (l == (WORD_LIST *)NULL)
        return ((char *) NULL);
    }

      if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
    {
      temp = string_list_dollar_star (l);
      retval = quote_string (temp);
      free (temp);
    }
      else  /* ${name[@]} or unquoted ${name[*]} */
    retval = string_list_dollar_at (l, quoted);

      dispose_words (l);
    }
  else
    {
      if (rtype)
    *rtype = 0;
      ind = array_expand_index (t, len);
      if (ind < 0)
    {
      if (var)
        err_badarraysub (var->name);
      else
        {
          t[-1] = '\0';
          err_badarraysub (s);
          t[-1] = '[';  /* ] */
        }
      return ((char *)NULL);
    }
      if (var == 0)
    return ((char *)NULL);
      if (array_p (var) == 0)
    return (ind == 0 ? value_cell (var) : (char *)NULL);
      retval = array_reference (array_cell (var), ind);
    }

  return retval;
}

/* Return a string containing the elements described by the array and
   subscript contained in S, obeying quoting for subscripts * and @. */
char *
array_value (s, quoted, rtype)
     char *s;
     int quoted, *rtype;
{
  return (array_value_internal (s, quoted, 1, rtype));
}

/* Return the value of the array indexing expression S as a single string.
   If ALLOW_ALL is 0, do not allow `@' and `*' subscripts.  This is used
   by other parts of the shell such as the arithmetic expression evaluator
   in expr.c. */
char *
get_array_value (s, allow_all, rtype)
     char *s;
     int allow_all, *rtype;
{
  return (array_value_internal (s, 0, allow_all, rtype));
}

#endif /* ARRAY_VARS */

https://git.savannah.gnu.org/cgit/bash.git/tree/braces.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/braces.c used in: Step 1234
/* braces.c -- code for doing word expansion in curly braces. */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

/* Stuff in curly braces gets expanded before all other shell expansions. */

#include "config.h"

#if defined (BRACE_EXPANSION)

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "bashansi.h"

#if defined (SHELL)
#  include "shell.h"
#endif /* SHELL */

#include "general.h"
#include "shmbutil.h"

#define brace_whitespace(c) (!(c) || (c) == ' ' || (c) == '\t' || (c) == '\n')

/* Basic idea:

   Segregate the text into 3 sections: preamble (stuff before an open brace),
   postamble (stuff after the matching close brace) and amble (stuff after
   preamble, and before postamble).  Expand amble, and then tack on the
   expansions to preamble.  Expand postamble, and tack on the expansions to
   the result so far.
 */

/* The character which is used to separate arguments. */
int brace_arg_separator = ',';

#if defined (__P)
static int brace_gobbler __P((char *, size_t, int *, int));
static char **expand_amble __P((char *, size_t));
static char **array_concat __P((char **, char **));
#else
static int brace_gobbler ();
static char **expand_amble ();
static char **array_concat ();
#endif

/* Return an array of strings; the brace expansion of TEXT. */
char **
brace_expand (text)
     char *text;
{
  register int start;
  size_t tlen;
  char *preamble, *postamble, *amble;
  size_t alen;
  char **tack, **result;
  int i, j, c;

  DECLARE_MBSTATE;

  /* Find the text of the preamble. */
  tlen = strlen (text);
  i = 0;
  c = brace_gobbler (text, tlen, &i, '{');

  preamble = (char *)xmalloc (i + 1);
  strncpy (preamble, text, i);
  preamble[i] = '\0';

  result = (char **)xmalloc (2 * sizeof (char *));
  result[0] = preamble;
  result[1] = (char *)NULL;

  /* Special case.  If we never found an exciting character, then
     the preamble is all of the text, so just return that. */
  if (c != '{')
    return (result);

  /* Find the amble.  This is the stuff inside this set of braces. */
  start = ++i;
  c = brace_gobbler (text, tlen, &i, '}');

  /* What if there isn't a matching close brace? */
  if (c == 0)
    {
#if defined (NOTDEF)
      /* Well, if we found an unquoted BRACE_ARG_SEPARATOR between START
     and I, then this should be an error.  Otherwise, it isn't. */
      j = start;
      while (j < i)
    {
      if (text[j] == '\\')
        {
          j++;
          ADVANCE_CHAR (text, tlen, j);
          continue;
        }

      if (text[j] == brace_arg_separator)
        {
          strvec_dispose (result);
          report_error ("missing `}'");
          throw_to_top_level ();
        }
      ADVANCE_CHAR (text, tlen, j);
    }
#endif
      free (preamble);      /* Same as result[0]; see initialization. */
      result[0] = savestring (text);
      return (result);
    }

#if defined (SHELL)
  amble = substring (text, start, i);
  alen = i - start;
#else
  amble = (char *)xmalloc (1 + (i - start));
  strncpy (amble, &text[start], (i - start));
  alen = i - start;
  amble[alen] = '\0';
#endif

#if defined (SHELL)
  INITIALIZE_MBSTATE;

  /* If the amble does not contain an unquoted BRACE_ARG_SEPARATOR, then
     just return without doing any expansion.  */
  j = 0;
  while (amble[j])
    {
      if (amble[j] == '\\')
    {
      j++;
      ADVANCE_CHAR (amble, alen, j);
      continue;
    }

      if (amble[j] == brace_arg_separator)
    break;

      ADVANCE_CHAR (amble, alen, j);
    }

  if (!amble[j])
    {
      free (amble);
      free (preamble);
      result[0] = savestring (text);
      return (result);
    }
#endif /* SHELL */

  postamble = &text[i + 1];

  tack = expand_amble (amble, alen);
  result = array_concat (result, tack);
  free (amble);
  strvec_dispose (tack);

  tack = brace_expand (postamble);
  result = array_concat (result, tack);
  strvec_dispose (tack);

  return (result);
}

/* Expand the text found inside of braces.  We simply try to split the
   text at BRACE_ARG_SEPARATORs into separate strings.  We then brace
   expand each slot which needs it, until there are no more slots which
   need it. */
static char **
expand_amble (text, tlen)
     char *text;
     size_t tlen;
{
  char **result, **partial;
  char *tem;
  int start, i, c;

  DECLARE_MBSTATE;

  result = (char **)NULL;

  start = i = 0;
  c = 1;
  while (c)
    {
      c = brace_gobbler (text, tlen, &i, brace_arg_separator);
#if defined (SHELL)
      tem = substring (text, start, i);
#else
      tem = (char *)xmalloc (1 + (i - start));
      strncpy (tem, &text[start], (i - start));
      tem[i- start] = '\0';
#endif

      partial = brace_expand (tem);

      if (!result)
    result = partial;
      else
    {
      register int lr = strvec_len (result);
      register int lp = strvec_len (partial);
      register int j;

      result = strvec_resize (result, lp + lr + 1);

      for (j = 0; j < lp; j++)
        result[lr + j] = partial[j];

      result[lr + j] = (char *)NULL;
      free (partial);
    }
      free (tem);
      ADVANCE_CHAR (text, tlen, i);
      start = i;
    }
  return (result);
}

/* Start at INDEX, and skip characters in TEXT. Set INDEX to the
   index of the character matching SATISFY.  This understands about
   quoting.  Return the character that caused us to stop searching;
   this is either the same as SATISFY, or 0. */
static int
brace_gobbler (text, tlen, indx, satisfy)
     char *text;
     size_t tlen;
     int *indx;
     int satisfy;
{
  register int i, c, quoted, level, pass_next;
#if defined (SHELL)
  int si;
  char *t;
#endif
  DECLARE_MBSTATE;

  level = quoted = pass_next = 0;

  i = *indx;
  while (c = text[i])
    {
      if (pass_next)
    {
      pass_next = 0;
      ADVANCE_CHAR (text, tlen, i);
      continue;
    }

      /* A backslash escapes the next character.  This allows backslash to
     escape the quote character in a double-quoted string. */
      if (c == '\\' && (quoted == 0 || quoted == '"' || quoted == '`'))
    {
      pass_next = 1;
      i++;
      continue;
    }

      if (quoted)
    {
      if (c == quoted)
        quoted = 0;
      ADVANCE_CHAR (text, tlen, i);
      continue;
    }

      if (c == '"' || c == '\'' || c == '`')
    {
      quoted = c;
      i++;
      continue;
    }

#if defined (SHELL)
      /* Pass new-style command substitutions through unchanged. */
      if (c == '$' && text[i+1] == '(')         /* ) */
    {
      si = i + 2;
      t = extract_command_subst (text, &si);
      i = si;
      free (t);
      i++;
      continue;
    }
#endif

      if (c == satisfy && level == 0 && quoted == 0)
    {
      /* We ignore an open brace surrounded by whitespace, and also
         an open brace followed immediately by a close brace preceded
         by whitespace.  */
      if (c == '{' &&
          ((!i || brace_whitespace (text[i - 1])) &&
           (brace_whitespace (text[i + 1]) || text[i + 1] == '}')))
        {
          i++;
          continue;
        }
#if defined (SHELL)
      /* If this is being compiled as part of bash, ignore the `{'
         in a `${}' construct */
      if ((c != '{') || i == 0 || (text[i - 1] != '$'))
#endif /* SHELL */
        break;
    }

      if (c == '{')
    level++;
      else if (c == '}' && level)
    level--;

      ADVANCE_CHAR (text, tlen, i);
    }

  *indx = i;
  return (c);
}

/* Return a new array of strings which is the result of appending each
   string in ARR2 to each string in ARR1.  The resultant array is
   len (arr1) * len (arr2) long.  For convenience, ARR1 (and its contents)
   are free ()'ed.  ARR1 can be NULL, in that case, a new version of ARR2
   is returned. */
static char **
array_concat (arr1, arr2)
     char **arr1, **arr2;
{
  register int i, j, len, len1, len2;
  register char **result;

  if (arr1 == 0)
    return (strvec_copy (arr2));

  if (arr2 == 0)
    return (strvec_copy (arr1));

  len1 = strvec_len (arr1);
  len2 = strvec_len (arr2);

  result = (char **)xmalloc ((1 + (len1 * len2)) * sizeof (char *));

  len = 0;
  for (i = 0; i < len1; i++)
    {
      int strlen_1 = strlen (arr1[i]);

      for (j = 0; j < len2; j++)
    {
      result[len] =
        (char *)xmalloc (1 + strlen_1 + strlen (arr2[j]));
      strcpy (result[len], arr1[i]);
      strcpy (result[len] + strlen_1, arr2[j]);
      len++;
    }
      free (arr1[i]);
    }
  free (arr1);

  result[len] = (char *)NULL;
  return (result);
}

#if defined (TEST)
#include <stdio.h>

fatal_error (format, arg1, arg2)
     char *format, *arg1, *arg2;
{
  report_error (format, arg1, arg2);
  exit (1);
}

report_error (format, arg1, arg2)
     char *format, *arg1, *arg2;
{
  fprintf (stderr, format, arg1, arg2);
  fprintf (stderr, "\n");
}

main ()
{
  char example[256];

  for (;;)
    {
      char **result;
      int i;

      fprintf (stderr, "brace_expand> ");

      if ((!fgets (example, 256, stdin)) ||
      (strncmp (example, "quit", 4) == 0))
    break;

      if (strlen (example))
    example[strlen (example) - 1] = '\0';

      result = brace_expand (example);

      for (i = 0; result[i]; i++)
    printf ("%s\n", result[i]);

      free_array (result);
    }
}


/*
 * Local variables:
 * compile-command: "gcc -g -Bstatic -DTEST -o brace_expand braces.c general.o"
 * end:
 */

#endif /* TEST */
#endif /* BRACE_EXPANSION */

https://git.savannah.gnu.org/cgit/bash.git/tree/bracecomp.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/bracecomp.c used in: Step 1235
/* bracecomp.c -- Complete a filename with the possible completions enclosed
   in csh-style braces such that the list of completions is available to the
   shell. */

/* Original version by tromey@cns.caltech.edu,  Fri Feb  7 1992. */

/* Copyright (C) 1993 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"
#if defined (BRACE_EXPANSION) && defined (READLINE)

#include <stdio.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "bashansi.h"

#include "shell.h"
#include <readline/readline.h>

/* Find greatest common prefix of two strings. */
static int
string_gcd (s1, s2)
     char *s1, *s2;
{
  register int i;

  if (s1 == NULL || s2 == NULL)
    return (0);

  for (i = 0; *s1 && *s2; ++s1, ++s2, ++i)
    {
      if (*s1 != *s2)
    break;
    }

  return (i);
}

static char *
really_munge_braces (array, real_start, real_end, gcd_zero)
     char **array;
     int real_start, real_end, gcd_zero;
{
  int start, end, gcd;
  char *result, *subterm, *x;
  int result_size, flag, tlen;

  flag = 0;

  if (real_start == real_end)
    {
      x = array[real_start] ? sh_backslash_quote (array[real_start] + gcd_zero)
                : sh_backslash_quote (array[0]);
      return x;
    }

  result = (char *)xmalloc (result_size = 16);
  *result = '\0';

  for (start = real_start; start < real_end; start = end + 1)
    {
      gcd = strlen (array[start]);
      for (end = start + 1; end < real_end; end++)
    {
      int temp;

      temp = string_gcd (array[start], array[end]);

      if (temp <= gcd_zero)
        break;

      gcd = temp;
    }
      end--;

      if (gcd_zero == 0 && start == real_start && end != (real_end - 1))
    {
      /* In this case, add in a leading '{', because we are at
         top level, and there isn't a consistent prefix. */
      result_size += 1;
      result = (char *)xrealloc (result, result_size);
      result[0] = '{'; result[1] = '\0';
      flag++;
    }

      /* Make sure we backslash quote every substring we insert into the
     resultant brace expression.  This is so the default filename
     quoting function won't inappropriately quote the braces. */
      if (start == end)
    {
      x = savestring (array[start] + gcd_zero);
      subterm = sh_backslash_quote (x);
      free (x);
    }
      else
    {
      /* If there is more than one element in the subarray,
         insert the (quoted) prefix and an opening brace. */
      tlen = gcd - gcd_zero;
      x = (char *)xmalloc (tlen + 1);
      strncpy (x, array[start] + gcd_zero, tlen);
      x[tlen] = '\0';
      subterm = sh_backslash_quote (x);
      free (x);
      result_size += strlen (subterm) + 1;
      result = (char *)xrealloc (result, result_size);
      strcat (result, subterm);
      free (subterm);
      strcat (result, "{");
      subterm = really_munge_braces (array, start, end + 1, gcd);
      subterm[strlen (subterm) - 1] = '}';
    }

      result_size += strlen (subterm) + 1;
      result = (char *)xrealloc (result, result_size);
      strcat (result, subterm);
      strcat (result, ",");
      free (subterm);
    }

  if (gcd_zero == 0)
    result[strlen (result) - 1] = flag ? '}' : '\0';
  return (result);
}

static int
hack_braces_completion (names)
     char **names;
{
  register int i;
  char *temp;

  temp = really_munge_braces (names, 1, strvec_len (names), 0);

  for (i = 0; names[i]; ++i)
    {
      free (names[i]);
      names[i] = NULL;
    }
  names[0] = temp;
  return 0;
}

/* We handle quoting ourselves within hack_braces_completion, so we turn off
   rl_filename_quoting_desired and rl_filename_quoting_function. */
int
bash_brace_completion (count, ignore)
     int count, ignore;
{
  rl_compignore_func_t *orig_ignore_func;
  rl_compentry_func_t *orig_entry_func;
  rl_quote_func_t *orig_quoting_func;
  rl_completion_func_t *orig_attempt_func;
  int orig_quoting_desired, r;

  orig_ignore_func = rl_ignore_some_completions_function;
  orig_attempt_func = rl_attempted_completion_function;
  orig_entry_func = rl_completion_entry_function;
  orig_quoting_func = rl_filename_quoting_function;
  orig_quoting_desired = rl_filename_quoting_desired;

  rl_completion_entry_function = rl_filename_completion_function;
  rl_attempted_completion_function = (rl_completion_func_t *)NULL;
  rl_ignore_some_completions_function = hack_braces_completion;
  rl_filename_quoting_function = (rl_quote_func_t *)NULL;
  rl_filename_quoting_desired = 0;

  r = rl_complete_internal (TAB);

  rl_ignore_some_completions_function = orig_ignore_func;
  rl_attempted_completion_function = orig_attempt_func;
  rl_completion_entry_function = orig_entry_func;
  rl_filename_quoting_function = orig_quoting_func;
  rl_filename_quoting_desired = orig_quoting_desired;

  return r;
}
#endif /* BRACE_EXPANSION && READLINE */

https://git.savannah.gnu.org/cgit/bash.git/tree/bashhist.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/bashhist.c used in: Step 1236
/* bashhist.c -- bash interface to the GNU history library. */

/* Copyright (C) 1993 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#if defined (HISTORY)

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "bashtypes.h"
#include <stdio.h>
#include <errno.h>
#include "bashansi.h"
#include "posixstat.h"
#include "filecntl.h"

#include "shell.h"
#include "flags.h"
#include "input.h"
#include "parser.h" /* for the struct dstack stuff. */
#include "pathexp.h"    /* for the struct ignorevar stuff */
#include "bashhist.h"   /* matching prototypes and declarations */
#include "builtins/common.h"

#include <readline/history.h>
#include <glob/glob.h>
#include <glob/strmatch.h>

#if defined (READLINE)
#  include "bashline.h"
#endif

#if !defined (errno)
extern int errno;
#endif

static int histignore_item_func __P((struct ign *));
static int check_history_control __P((char *));
static void really_add_history __P((char *));

static struct ignorevar histignore =
{
  "HISTIGNORE",
  (struct ign *)0,
  0,
  (char *)0,
  (sh_iv_item_func_t *)histignore_item_func,
};

#define HIGN_EXPAND 0x01

/* Declarations of bash history variables. */
/* Non-zero means to remember lines typed to the shell on the history
   list.  This is different than the user-controlled behaviour; this
   becomes zero when we read lines from a file, for example. */
int remember_on_history = 1;

/* The number of lines that Bash has added to this history session. */
int history_lines_this_session;

/* The number of lines that Bash has read from the history file. */
int history_lines_in_file;

#if defined (BANG_HISTORY)
/* Non-zero means do no history expansion on this line, regardless
   of what history_expansion says. */
int history_expansion_inhibited;
#endif

/* With the old default, every line was saved in the history individually.
   I.e., if the user enters:
    bash$ for i in a b c
    > do
    > echo $i
    > done
   Each line will be individually saved in the history.
    bash$ history
    10  for i in a b c
    11  do
    12  echo $i
    13  done
    14  history
   If the variable command_oriented_history is set, multiple lines
   which form one command will be saved as one history entry.
    bash$ for i in a b c
    > do
    > echo $i
    > done
    bash$ history
    10  for i in a b c
    do
    echo $i
    done
    11  history
   The user can then recall the whole command all at once instead
   of just being able to recall one line at a time.

   This is now enabled by default.
   */
int command_oriented_history = 1;

/* Set to 1 if the first line of a possibly-multi-line command was saved
   in the history list.  Managed by maybe_add_history(), but global so
   the history-manipluating builtins can see it. */
int current_command_first_line_saved = 0;

/* Non-zero means to store newlines in the history list when using
   command_oriented_history rather than trying to use semicolons. */
int literal_history;

/* Non-zero means to append the history to the history file at shell
   exit, even if the history has been stifled. */
int force_append_history;

/* A nit for picking at history saving.
   Value of 0 means save all lines parsed by the shell on the history.
   Value of 1 means save all lines that do not start with a space.
   Value of 2 means save all lines that do not match the last line saved. */
int history_control;

/* Set to 1 if the last command was added to the history list successfully
   as a separate history entry; set to 0 if the line was ignored or added
   to a previous entry as part of command-oriented-history processing. */
int hist_last_line_added;

#if defined (READLINE)
/* If non-zero, and readline is being used, the user is offered the
   chance to re-edit a failed history expansion. */
int history_reediting;

/* If non-zero, and readline is being used, don't directly execute a
   line with history substitution.  Reload it into the editing buffer
   instead and let the user further edit and confirm with a newline. */
int hist_verify;

#endif /* READLINE */

/* Non-zero means to not save function definitions in the history list. */
int dont_save_function_defs;

/* Variables declared in other files used here. */
extern int current_command_line_count;

extern struct dstack dstack;

static int bash_history_inhibit_expansion __P((char *, int));
#if defined (READLINE)
static void re_edit __P((char *));
#endif
static int history_expansion_p __P((char *));
static int shell_comment __P((char *));
static int should_expand __P((char *));
static HIST_ENTRY *last_history_entry __P((void));
static char *expand_histignore_pattern __P((char *));
static int history_should_ignore __P((char *));

/* Is the history expansion starting at string[i] one that should not
   be expanded? */
static int
bash_history_inhibit_expansion (string, i)
     char *string;
     int i;
{
  /* The shell uses ! as a pattern negation character in globbing [...]
     expressions, so let those pass without expansion. */
  if (i > 0 && (string[i - 1] == '[') && member (']', string + i + 1))
    return (1);
  /* The shell uses ! as the indirect expansion character, so let those
     expansions pass as well. */
  else if (i > 1 && string[i - 1] == '{' && string[i - 2] == '$' &&
         member ('}', string + i + 1))
    return (1);
#if defined (EXTENDED_GLOB)
  else if (extended_glob && i > 1 && string[i+1] == '(' && member (')', string + i + 2))
    return (1);
#endif
  else
    return (0);
}

void
bash_initialize_history ()
{
  history_quotes_inhibit_expansion = 1;
  history_search_delimiter_chars = ";&()|<>";
  history_inhibit_expansion_function = bash_history_inhibit_expansion;
}

void
bash_history_reinit (interact)
     int interact;
{
#if defined (BANG_HISTORY)
  history_expansion = interact != 0;
  history_expansion_inhibited = 1;
#endif
  remember_on_history = interact != 0;
  history_inhibit_expansion_function = bash_history_inhibit_expansion;
}

void
bash_history_disable ()
{
  remember_on_history = 0;
#if defined (BANG_HISTORY)
  history_expansion_inhibited = 1;
#endif
}

void
bash_history_enable ()
{
  remember_on_history = 1;
#if defined (BANG_HISTORY)
  history_expansion_inhibited = 0;
#endif
  history_inhibit_expansion_function = bash_history_inhibit_expansion;
  sv_history_control ("HISTCONTROL");
  sv_histignore ("HISTIGNORE");
}

/* Load the history list from the history file. */
void
load_history ()
{
  char *hf;
  struct stat buf;

  /* Truncate history file for interactive shells which desire it.
     Note that the history file is automatically truncated to the
     size of HISTSIZE if the user does not explicitly set the size
     differently. */
  set_if_not ("HISTFILESIZE", get_string_value ("HISTSIZE"));
  sv_histsize ("HISTFILESIZE");

  /* Read the history in HISTFILE into the history list. */
  hf = get_string_value ("HISTFILE");

  if (hf && *hf && stat (hf, &buf) == 0)
    {
      read_history (hf);
      using_history ();
      history_lines_in_file = where_history ();
    }
}

#ifdef INCLUDE_UNUSED
/* Write the existing history out to the history file. */
void
save_history ()
{
  char *hf;
  struct stat buf;

  hf = get_string_value ("HISTFILE");
  if (hf && *hf && stat (hf, &buf) == 0)
    {
      /* Append only the lines that occurred this session to
     the history file. */
      using_history ();

      if (history_lines_this_session < where_history () || force_append_history)
    append_history (history_lines_this_session, hf);
      else
    write_history (hf);

      sv_histsize ("HISTFILESIZE");
    }
}
#endif

int
maybe_append_history (filename)
     char *filename;
{
  int fd, result;
  struct stat buf;

  result = EXECUTION_SUCCESS;
  if (history_lines_this_session && (history_lines_this_session < where_history ()))
    {
      /* If the filename was supplied, then create it if necessary. */
      if (stat (filename, &buf) == -1 && errno == ENOENT)
    {
      fd = open (filename, O_WRONLY|O_CREAT, 0600);
      if (fd < 0)
        {
          builtin_error ("%s: cannot create: %s", filename, strerror (errno));
          return (EXECUTION_FAILURE);
        }
      close (fd);
    }
      result = append_history (history_lines_this_session, filename);
      history_lines_in_file += history_lines_this_session;
      history_lines_this_session = 0;
    }
  return (result);
}

/* If this is an interactive shell, then append the lines executed
   this session to the history file. */
int
maybe_save_shell_history ()
{
  int result;
  char *hf;
  struct stat buf;

  result = 0;
  if (history_lines_this_session)
    {
      hf = get_string_value ("HISTFILE");

      if (hf && *hf)
    {
      /* If the file doesn't exist, then create it. */
      if (stat (hf, &buf) == -1)
        {
          int file;
          file = open (hf, O_CREAT | O_TRUNC | O_WRONLY, 0600);
          if (file != -1)
        close (file);
        }

      /* Now actually append the lines if the history hasn't been
         stifled.  If the history has been stifled, rewrite the
         history file. */
      using_history ();
      if (history_lines_this_session <= where_history () || force_append_history)
        {
          result = append_history (history_lines_this_session, hf);
          history_lines_in_file += history_lines_this_session;
        }
      else
        {
          result = write_history (hf);
          history_lines_in_file = history_lines_this_session;
        }
      history_lines_this_session = 0;

      sv_histsize ("HISTFILESIZE");
    }
    }
  return (result);
}

#if defined (READLINE)
/* Tell readline () that we have some text for it to edit. */
static void
re_edit (text)
     char *text;
{
  if (bash_input.type == st_stdin)
    bash_re_edit (text);
}
#endif /* READLINE */

/* Return 1 if this line needs history expansion. */
static int
history_expansion_p (line)
     char *line;
{
  register char *s;

  for (s = line; *s; s++)
    if (*s == history_expansion_char || *s == history_subst_char)
      return 1;
  return 0;
}

/* Do pre-processing on LINE.  If PRINT_CHANGES is non-zero, then
   print the results of expanding the line if there were any changes.
   If there is an error, return NULL, otherwise the expanded line is
   returned.  If ADDIT is non-zero the line is added to the history
   list after history expansion.  ADDIT is just a suggestion;
   REMEMBER_ON_HISTORY can veto, and does.
   Right now this does history expansion. */
char *
pre_process_line (line, print_changes, addit)
     char *line;
     int print_changes, addit;
{
  char *history_value;
  char *return_value;
  int expanded;

  return_value = line;
  expanded = 0;

#  if defined (BANG_HISTORY)
  /* History expand the line.  If this results in no errors, then
     add that line to the history if ADDIT is non-zero. */
  if (!history_expansion_inhibited && history_expansion && history_expansion_p (line))
    {
      expanded = history_expand (line, &history_value);

      if (expanded)
    {
      if (print_changes)
        {
          if (expanded < 0)
        internal_error ("%s", history_value);
#if defined (READLINE)
          else if (hist_verify == 0 || expanded == 2)
#else
          else
#endif
        fprintf (stderr, "%s\n", history_value);
        }

      /* If there was an error, return NULL. */
      if (expanded < 0 || expanded == 2)    /* 2 == print only */
        {
          free (history_value);

#    if defined (READLINE)
          /* New hack.  We can allow the user to edit the
         failed history expansion. */
          if (history_reediting && expanded < 0)
        re_edit (line);
#    endif /* READLINE */
          return ((char *)NULL);
        }

#    if defined (READLINE)
      if (hist_verify && expanded == 1)
        {
          re_edit (history_value);
          return ((char *)NULL);
        }
#    endif
    }

      /* Let other expansions know that return_value can be free'ed,
     and that a line has been added to the history list.  Note
     that we only add lines that have something in them. */
      expanded = 1;
      return_value = history_value;
    }
#  endif /* BANG_HISTORY */

  if (addit && remember_on_history && *return_value)
    maybe_add_history (return_value);

#if 0
  if (expanded == 0)
    return_value = savestring (line);
#endif

  return (return_value);
}

/* Return 1 if the first non-whitespace character in LINE is a `#', indicating
 * that the line is a shell comment. */
static int
shell_comment (line)
     char *line;
{
  char *p;

  for (p = line; p && *p && whitespace (*p); p++)
    ;
  return (p && *p == '#');
}

#ifdef INCLUDE_UNUSED
/* Remove shell comments from LINE.  A `#' and anything after it is a comment.
   This isn't really useful yet, since it doesn't handle quoting. */
static char *
filter_comments (line)
     char *line;
{
  char *p;

  for (p = line; p && *p && *p != '#'; p++)
    ;
  if (p && *p == '#')
    *p = '\0';
  return (line);
}
#endif

/* Check LINE against what HISTCONTROL says to do.  Returns 1 if the line
   should be saved; 0 if it should be discarded. */
static int
check_history_control (line)
     char *line;
{
  HIST_ENTRY *temp;
  int r;

  switch (history_control)
    {
    case 0:         /* nothing */
      return 1;
    case 1:         /* ignorespace */
      return (*line != ' ');
    case 3:         /* ignoreboth */
      if (*line == ' ')
    return 0;
      /* FALLTHROUGH if case == 3 (`ignoreboth') */
    case 2:         /* ignoredups */
      using_history ();
      temp = previous_history ();

      r = (temp == 0 || STREQ (temp->line, line) == 0);

      using_history ();
      return r;
    }

  return 0;
}

/* Add LINE to the history list, handling possibly multi-line compound
   commands.  We note whether or not we save the first line of each command
   (which is usually the entire command and history entry), and don't add
   the second and subsequent lines of a multi-line compound command if we
   didn't save the first line.  We don't usually save shell comment lines in
   compound commands in the history, because they could have the effect of
   commenting out the rest of the command when the entire command is saved as
   a single history entry (when COMMAND_ORIENTED_HISTORY is enabled).  If
   LITERAL_HISTORY is set, we're saving lines in the history with embedded
   newlines, so it's OK to save comment lines.  We also make sure to save
   multiple-line quoted strings or other constructs. */
void
maybe_add_history (line)
     char *line;
{
  hist_last_line_added = 0;

  /* Don't use the value of history_control to affect the second
     and subsequent lines of a multi-line command (old code did
     this only when command_oriented_history is enabled). */
  if (current_command_line_count > 1)
    {
      if (current_command_first_line_saved &&
      (literal_history || dstack.delimiter_depth != 0 || shell_comment (line) == 0))
    bash_add_history (line);
      return;
    }

  /* This is the first line of a (possible multi-line) command.  Note whether
     or not we should save the first line and remember it. */
  current_command_first_line_saved = check_add_history (line, 0);
}

/* Just check LINE against HISTCONTROL and HISTIGNORE and add it to the
   history if it's OK.  Used by `history -s' as well as maybe_add_history().
   Returns 1 if the line was saved in the history, 0 otherwise. */
int
check_add_history (line, force)
     char *line;
     int force;
{
  if (check_history_control (line) && history_should_ignore (line) == 0)
    {
      if (force)
    {
      really_add_history (line);
      using_history ();
    }
      else
    bash_add_history (line);
      return 1;
    }
  return 0;
}

/* Add a line to the history list.
   The variable COMMAND_ORIENTED_HISTORY controls the style of history
   remembering;  when non-zero, and LINE is not the first line of a
   complete parser construct, append LINE to the last history line instead
   of adding it as a new line. */
void
bash_add_history (line)
     char *line;
{
  int add_it, offset, curlen;
  HIST_ENTRY *current, *old;
  char *chars_to_add, *new_line;

  add_it = 1;
  if (command_oriented_history && current_command_line_count > 1)
    {
      chars_to_add = literal_history ? "\n" : history_delimiting_chars ();

      using_history ();
      current = previous_history ();

      if (current)
    {
      /* If the previous line ended with an escaped newline (escaped
         with backslash, but otherwise unquoted), then remove the quoted
         newline, since that is what happens when the line is parsed. */
      curlen = strlen (current->line);

      if (dstack.delimiter_depth == 0 && current->line[curlen - 1] == '\\' &&
          current->line[curlen - 2] != '\\')
        {
          current->line[curlen - 1] = '\0';
          curlen--;
          chars_to_add = "";
        }

      new_line = (char *)xmalloc (1
                      + curlen
                      + strlen (line)
                      + strlen (chars_to_add));
      sprintf (new_line, "%s%s%s", current->line, chars_to_add, line);
      offset = where_history ();
      old = replace_history_entry (offset, new_line, current->data);
      free (new_line);

      if (old)
        {
          FREE (old->line);
          free (old);
        }
      add_it = 0;
    }
    }

  if (add_it)
    really_add_history (line);

  using_history ();
}

static void
really_add_history (line)
     char *line;
{
  hist_last_line_added = 1;
  add_history (line);
  history_lines_this_session++;
}

int
history_number ()
{
  using_history ();
  return (get_string_value ("HISTSIZE") ? history_base + where_history () : 1);
}

static int
should_expand (s)
     char *s;
{
  char *p;

  for (p = s; p && *p; p++)
    {
      if (*p == '\\')
    p++;
      else if (*p == '&')
    return 1;
    }
  return 0;
}

static int
histignore_item_func (ign)
     struct ign *ign;
{
  if (should_expand (ign->val))
    ign->flags |= HIGN_EXPAND;
  return (0);
}

void
setup_history_ignore (varname)
     char *varname;
{
  setup_ignore_patterns (&histignore);
}

static HIST_ENTRY *
last_history_entry ()
{
  HIST_ENTRY *he;

  using_history ();
  he = previous_history ();
  using_history ();
  return he;
}

char *
last_history_line ()
{
  HIST_ENTRY *he;

  he = last_history_entry ();
  if (he == 0)
    return ((char *)NULL);
  return he->line;
}

static char *
expand_histignore_pattern (pat)
     char *pat;
{
  HIST_ENTRY *phe;
  char *ret;

  phe = last_history_entry ();

  if (phe == (HIST_ENTRY *)0)
    return (savestring (pat));

  ret = strcreplace (pat, '&', phe->line, 1);

  return ret;
}

/* Return 1 if we should not put LINE into the history according to the
   patterns in HISTIGNORE. */
static int
history_should_ignore (line)
     char *line;
{
  register int i, match;
  char *npat;

  if (histignore.num_ignores == 0)
    return 0;

  for (i = match = 0; i < histignore.num_ignores; i++)
    {
      if (histignore.ignores[i].flags & HIGN_EXPAND)
    npat = expand_histignore_pattern (histignore.ignores[i].val);
      else
    npat = histignore.ignores[i].val;

      match = strmatch (npat, line, FNMATCH_EXTFLAG) != FNM_NOMATCH;

      if (histignore.ignores[i].flags & HIGN_EXPAND)
    free (npat);

      if (match)
    break;
    }

  return match;
}
#endif /* HISTORY */

https://git.savannah.gnu.org/cgit/bash.git/tree/bashline.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/bashline.c used in: Step 1237
/* bashline.c -- Bash's interface to the readline library. */

/* Copyright (C) 1987-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#if defined (READLINE)

#include "bashtypes.h"
#include "posixstat.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#if defined (HAVE_GRP_H)
#  include <grp.h>
#endif

#if defined (HAVE_NETDB_H)
#  include <netdb.h>
#endif

#include <stdio.h>
#include "chartypes.h"
#include "bashansi.h"
#include "shell.h"
#include "input.h"
#include "builtins.h"
#include "bashhist.h"
#include "bashline.h"
#include "execute_cmd.h"
#include "findcmd.h"
#include "pathexp.h"
#include "builtins/common.h"
#include <readline/rlconf.h>
#include <readline/readline.h>
#include <readline/history.h>

#include <glob/glob.h>

#if defined (ALIAS)
#  include "alias.h"
#endif

#if defined (PROGRAMMABLE_COMPLETION)
#  include "pcomplete.h"
#endif

/* These should agree with the defines for emacs_mode and vi_mode in
   rldefs.h, even though that's not a public readline header file. */
#ifndef EMACS_EDITING_MODE
#  define NO_EDITING_MODE   -1
#  define EMACS_EDITING_MODE     1
#  define VI_EDITING_MODE    0
#endif

#if defined (BRACE_COMPLETION)
extern int bash_brace_completion __P((int, int));
#endif /* BRACE_COMPLETION */

/* Forward declarations */

/* Functions bound to keys in Readline for Bash users. */
static int shell_expand_line __P((int, int));
static int display_shell_version __P((int, int));
static int operate_and_get_next __P((int, int));

static int bash_ignore_filenames __P((char **));
static int bash_ignore_everything __P((char **));

#if defined (BANG_HISTORY)
static char *history_expand_line_internal __P((char *));
static int history_expand_line __P((int, int));
static int tcsh_magic_space __P((int, int));
#endif /* BANG_HISTORY */
#ifdef ALIAS
static int alias_expand_line __P((int, int));
#endif
#if defined (BANG_HISTORY) && defined (ALIAS)
static int history_and_alias_expand_line __P((int, int));
#endif

/* Helper functions for Readline. */
static int bash_directory_completion_hook __P((char **));
static int filename_completion_ignore __P((char **));
static int bash_push_line __P((void));

static void cleanup_expansion_error __P((void));
static void maybe_make_readline_line __P((char *));
static void set_up_new_line __P((char *));

static int check_redir __P((int));
static char **attempt_shell_completion __P((const char *, int, int));
static char *variable_completion_function __P((const char *, int));
static char *hostname_completion_function __P((const char *, int));
static char *command_subst_completion_function __P((const char *, int));

static void build_history_completion_array __P((void));
static char *history_completion_generator __P((const char *, int));
static int dynamic_complete_history __P((int, int));

static void initialize_hostname_list __P((void));
static void add_host_name __P((char *));
static void snarf_hosts_from_file __P((char *));
static char **hostnames_matching __P((char *));

static void _ignore_completion_names __P((char **, sh_ignore_func_t *));
static int name_is_acceptable __P((const char *));
static int test_for_directory __P((const char *));
static int return_zero __P((const char *));

static char *bash_dequote_filename __P((char *, int));
static char *quote_word_break_chars __P((char *));
static char *bash_quote_filename __P((char *, int, char *));

static int bash_execute_unix_command __P((int, int));
static void init_unix_command_map __P((void));
static int isolate_sequence __P((char *, int, int, int *));

static int set_saved_history __P((void));

#if defined (ALIAS)
static int posix_edit_macros __P((int, int));
#endif

#if defined (PROGRAMMABLE_COMPLETION)
static int find_cmd_start __P((int));
static int find_cmd_end __P((int));
static char *find_cmd_name __P((int));
static char *prog_complete_return __P((const char *, int));

static char **prog_complete_matches;
#endif

/* Variables used here but defined in other files. */
extern int current_command_line_count;
extern int posixly_correct, no_symbolic_links;
extern char *current_prompt_string, *ps1_prompt;
extern STRING_INT_ALIST word_token_alist[];

/* SPECIFIC_COMPLETION_FUNCTIONS specifies that we have individual
   completion functions which indicate what type of completion should be
   done (at or before point) that can be bound to key sequences with
   the readline library. */
#define SPECIFIC_COMPLETION_FUNCTIONS

#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
static int bash_specific_completion __P((int, rl_compentry_func_t *));

static int bash_complete_filename_internal __P((int));
static int bash_complete_username_internal __P((int));
static int bash_complete_hostname_internal __P((int));
static int bash_complete_variable_internal __P((int));
static int bash_complete_command_internal __P((int));

static int bash_complete_filename __P((int, int));
static int bash_possible_filename_completions __P((int, int));
static int bash_complete_username __P((int, int));
static int bash_possible_username_completions __P((int, int));
static int bash_complete_hostname __P((int, int));
static int bash_possible_hostname_completions __P((int, int));
static int bash_complete_variable __P((int, int));
static int bash_possible_variable_completions __P((int, int));
static int bash_complete_command __P((int, int));
static int bash_possible_command_completions __P((int, int));

static char *glob_complete_word __P((const char *, int));
static int bash_glob_completion_internal __P((int));
static int bash_glob_complete_word __P((int, int));
static int bash_glob_expand_word __P((int, int));
static int bash_glob_list_expansions __P((int, int));
#endif /* SPECIFIC_COMPLETION_FUNCTIONS */

static int edit_and_execute_command __P((int, int, int, char *));
#if defined (VI_MODE)
static int vi_edit_and_execute_command __P((int, int));
#endif
static int emacs_edit_and_execute_command __P((int, int));

/* Non-zero once initalize_readline () has been called. */
int bash_readline_initialized = 0;

/* If non-zero, we do hostname completion, breaking words at `@' and
   trying to complete the stuff after the `@' from our own internal
   host list. */
int perform_hostname_completion = 1;

/* If non-zero, we don't do command completion on an empty line. */
int no_empty_command_completion;

static char *bash_completer_word_break_characters = " \t\n\"'@><=;|&(:";
static char *bash_nohostname_word_break_characters = " \t\n\"'><=;|&(:";

static rl_hook_func_t *old_rl_startup_hook = (rl_hook_func_t *)NULL;

/* What kind of quoting is performed by bash_quote_filename:
    COMPLETE_DQUOTE = double-quoting the filename
    COMPLETE_SQUOTE = single_quoting the filename
    COMPLETE_BSQUOTE = backslash-quoting special chars in the filename
*/
#define COMPLETE_DQUOTE  1
#define COMPLETE_SQUOTE  2
#define COMPLETE_BSQUOTE 3
static int completion_quoting_style = COMPLETE_BSQUOTE;

/* Change the readline VI-mode keymaps into or out of Posix.2 compliance.
   Called when the shell is put into or out of `posix' mode. */
void
posix_readline_initialize (on_or_off)
     int on_or_off;
{
  if (on_or_off)
    rl_variable_bind ("comment-begin", "#");
#if defined (VI_MODE)
  rl_bind_key_in_map (CTRL ('I'), on_or_off ? rl_insert : rl_complete, vi_insertion_keymap);
#endif
}

int
enable_hostname_completion (on_or_off)
     int on_or_off;
{
  int old_value;

  old_value = perform_hostname_completion;

  if (on_or_off)
    {
      perform_hostname_completion = 1;
      rl_special_prefixes = "$@";
      rl_completer_word_break_characters = bash_completer_word_break_characters;
    }
  else
    {
      perform_hostname_completion = 0;
      rl_special_prefixes = "$";
      rl_completer_word_break_characters = bash_nohostname_word_break_characters;
    }

  return (old_value);
}

/* Called once from parse.y if we are going to use readline. */
void
initialize_readline ()
{
  if (bash_readline_initialized)
    return;

  rl_terminal_name = get_string_value ("TERM");
  rl_instream = stdin;
  rl_outstream = stderr;

  /* Allow conditional parsing of the ~/.inputrc file. */
  rl_readline_name = "Bash";

  /* Add bindable names before calling rl_initialize so they may be
     referenced in the various inputrc files. */
  rl_add_defun ("shell-expand-line", shell_expand_line, -1);
#ifdef BANG_HISTORY
  rl_add_defun ("history-expand-line", history_expand_line, -1);
  rl_add_defun ("magic-space", tcsh_magic_space, -1);
#endif

#ifdef ALIAS
  rl_add_defun ("alias-expand-line", alias_expand_line, -1);
#  ifdef BANG_HISTORY
  rl_add_defun ("history-and-alias-expand-line", history_and_alias_expand_line, -1);
#  endif
#endif

  /* Backwards compatibility. */
  rl_add_defun ("insert-last-argument", rl_yank_last_arg, -1);

  rl_add_defun ("operate-and-get-next", operate_and_get_next, -1);
  rl_add_defun ("display-shell-version", display_shell_version, -1);
  rl_add_defun ("edit-and-execute-command", emacs_edit_and_execute_command, -1);

#if defined (BRACE_COMPLETION)
  rl_add_defun ("complete-into-braces", bash_brace_completion, -1);
#endif

#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
  rl_add_defun ("complete-filename", bash_complete_filename, -1);
  rl_add_defun ("possible-filename-completions", bash_possible_filename_completions, -1);
  rl_add_defun ("complete-username", bash_complete_username, -1);
  rl_add_defun ("possible-username-completions", bash_possible_username_completions, -1);
  rl_add_defun ("complete-hostname", bash_complete_hostname, -1);
  rl_add_defun ("possible-hostname-completions", bash_possible_hostname_completions, -1);
  rl_add_defun ("complete-variable", bash_complete_variable, -1);
  rl_add_defun ("possible-variable-completions", bash_possible_variable_completions, -1);
  rl_add_defun ("complete-command", bash_complete_command, -1);
  rl_add_defun ("possible-command-completions", bash_possible_command_completions, -1);
  rl_add_defun ("glob-complete-word", bash_glob_complete_word, -1);
  rl_add_defun ("glob-expand-word", bash_glob_expand_word, -1);
  rl_add_defun ("glob-list-expansions", bash_glob_list_expansions, -1);
#endif

  rl_add_defun ("dynamic-complete-history", dynamic_complete_history, -1);

  /* Bind defaults before binding our custom shell keybindings. */
  if (RL_ISSTATE(RL_STATE_INITIALIZED) == 0)
    rl_initialize ();

  /* Bind up our special shell functions. */
  rl_bind_key_in_map (CTRL('E'), shell_expand_line, emacs_meta_keymap);

  /* Bind up our special shell functions. */
#ifdef BANG_HISTORY
  rl_bind_key_in_map ('^', history_expand_line, emacs_meta_keymap);
#endif

  rl_bind_key_in_map (CTRL ('O'), operate_and_get_next, emacs_standard_keymap);
  rl_bind_key_in_map (CTRL ('V'), display_shell_version, emacs_ctlx_keymap);

  /* In Bash, the user can switch editing modes with "set -o [vi emacs]",
     so it is not necessary to allow C-M-j for context switching.  Turn
     off this occasionally confusing behaviour. */
  rl_unbind_key_in_map (CTRL('J'), emacs_meta_keymap);
  rl_unbind_key_in_map (CTRL('M'), emacs_meta_keymap);
#if defined (VI_MODE)
  rl_unbind_key_in_map (CTRL('E'), vi_movement_keymap);
#endif

#if defined (BRACE_COMPLETION)
  rl_bind_key_in_map ('{', bash_brace_completion, emacs_meta_keymap);
#endif /* BRACE_COMPLETION */

#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
  rl_bind_key_in_map ('/', bash_complete_filename, emacs_meta_keymap);
  rl_bind_key_in_map ('/', bash_possible_filename_completions, emacs_ctlx_keymap);

  rl_bind_key_in_map ('~', bash_complete_username, emacs_meta_keymap);
  rl_bind_key_in_map ('~', bash_possible_username_completions, emacs_ctlx_keymap);

  rl_bind_key_in_map ('@', bash_complete_hostname, emacs_meta_keymap);
  rl_bind_key_in_map ('@', bash_possible_hostname_completions, emacs_ctlx_keymap);

  rl_bind_key_in_map ('$', bash_complete_variable, emacs_meta_keymap);
  rl_bind_key_in_map ('$', bash_possible_variable_completions, emacs_ctlx_keymap);

  rl_bind_key_in_map ('!', bash_complete_command, emacs_meta_keymap);
  rl_bind_key_in_map ('!', bash_possible_command_completions, emacs_ctlx_keymap);

  rl_bind_key_in_map ('g', bash_glob_complete_word, emacs_meta_keymap);
  rl_bind_key_in_map ('*', bash_glob_expand_word, emacs_ctlx_keymap);
  rl_bind_key_in_map ('g', bash_glob_list_expansions, emacs_ctlx_keymap);

#endif /* SPECIFIC_COMPLETION_FUNCTIONS */

  rl_bind_key_in_map (TAB, dynamic_complete_history, emacs_meta_keymap);

  /* Tell the completer that we want a crack first. */
  rl_attempted_completion_function = attempt_shell_completion;

  /* Tell the completer that we might want to follow symbolic links or
     do other expansion on directory names. */
  rl_directory_completion_hook = bash_directory_completion_hook;

  /* Tell the filename completer we want a chance to ignore some names. */
  rl_ignore_some_completions_function = filename_completion_ignore;

  /* Bind C-xC-e to invoke emacs and run result as commands. */
  rl_bind_key_in_map (CTRL ('E'), emacs_edit_and_execute_command, emacs_ctlx_keymap);
#if defined (VI_MODE)
  rl_bind_key_in_map ('v', vi_edit_and_execute_command, vi_movement_keymap);
#  if defined (ALIAS)
  rl_bind_key_in_map ('@', posix_edit_macros, vi_movement_keymap);
#  endif
#endif

  rl_completer_quote_characters = "'\"";

  /* This sets rl_completer_word_break_characters and rl_special_prefixes
     to the appropriate values, depending on whether or not hostname
     completion is enabled. */
  enable_hostname_completion (perform_hostname_completion);

  /* characters that need to be quoted when appearing in filenames. */
  rl_filename_quote_characters = " \t\n\\\"'@<>=;|&()#$`?*[!:{";    /*}*/
  rl_filename_quoting_function = bash_quote_filename;
  rl_filename_dequoting_function = bash_dequote_filename;
  rl_char_is_quoted_p = char_is_quoted;

#if 0
  /* This is superfluous and makes it impossible to use tab completion in
     vi mode even when explicitly binding it in ~/.inputrc.  sv_strict_posix()
     should already have called posix_readline_initialize() when
     posixly_correct was set. */
  if (posixly_correct)
    posix_readline_initialize (1);
#endif

  bash_readline_initialized = 1;
}

/* On Sun systems at least, rl_attempted_completion_function can end up
   getting set to NULL, and rl_completion_entry_function set to do command
   word completion if Bash is interrupted while trying to complete a command
   word.  This just resets all the completion functions to the right thing.
   It's called from throw_to_top_level(). */
void
bashline_reinitialize ()
{
  tilde_initialize ();
  rl_attempted_completion_function = attempt_shell_completion;
  rl_completion_entry_function = NULL;
  rl_directory_completion_hook = bash_directory_completion_hook;
  rl_ignore_some_completions_function = filename_completion_ignore;
}

/* Contains the line to push into readline. */
static char *push_to_readline = (char *)NULL;

/* Push the contents of push_to_readline into the
   readline buffer. */
static int
bash_push_line ()
{
  if (push_to_readline)
    {
      rl_insert_text (push_to_readline);
      free (push_to_readline);
      push_to_readline = (char *)NULL;
      rl_startup_hook = old_rl_startup_hook;
    }
  return 0;
}

/* Call this to set the initial text for the next line to read
   from readline. */
int
bash_re_edit (line)
     char *line;
{
  FREE (push_to_readline);

  push_to_readline = savestring (line);
  old_rl_startup_hook = rl_startup_hook;
  rl_startup_hook = bash_push_line;

  return (0);
}

static int
display_shell_version (count, c)
     int count, c;
{
  rl_crlf ();
  show_shell_version (0);
  putc ('\r', rl_outstream);
  fflush (rl_outstream);
  rl_on_new_line ();
  rl_redisplay ();
  return 0;
}

/* **************************************************************** */
/*                                  */
/*               Readline Stuff             */
/*                                  */
/* **************************************************************** */

/* If the user requests hostname completion, then simply build a list
   of hosts, and complete from that forever more, or at least until
   HOSTFILE is unset. */

/* THIS SHOULD BE A STRINGLIST. */
/* The kept list of hostnames. */
static char **hostname_list = (char **)NULL;

/* The physical size of the above list. */
static int hostname_list_size;

/* The number of hostnames in the above list. */
static int hostname_list_length;

/* Whether or not HOSTNAME_LIST has been initialized. */
int hostname_list_initialized = 0;

/* Initialize the hostname completion table. */
static void
initialize_hostname_list ()
{
  char *temp;

  temp = get_string_value ("HOSTFILE");
  if (temp == 0)
    temp = get_string_value ("hostname_completion_file");
  if (temp == 0)
    temp = DEFAULT_HOSTS_FILE;

  snarf_hosts_from_file (temp);

  if (hostname_list)
    hostname_list_initialized++;
}

/* Add NAME to the list of hosts. */
static void
add_host_name (name)
     char *name;
{
  if (hostname_list_length + 2 > hostname_list_size)
    {
      hostname_list_size = (hostname_list_size + 32) - (hostname_list_size % 32);
      hostname_list = strvec_resize (hostname_list, hostname_list_size);
    }

  hostname_list[hostname_list_length++] = savestring (name);
  hostname_list[hostname_list_length] = (char *)NULL;
}

#define cr_whitespace(c) ((c) == '\r' || (c) == '\n' || whitespace(c))

static void
snarf_hosts_from_file (filename)
     char *filename;
{
  FILE *file;
  char *temp, buffer[256], name[256];
  register int i, start;

  file = fopen (filename, "r");
  if (file == 0)
    return;

  while (temp = fgets (buffer, 255, file))
    {
      /* Skip to first character. */
      for (i = 0; buffer[i] && cr_whitespace (buffer[i]); i++)
    ;

      /* If comment or blank line, ignore. */
      if (buffer[i] == '\0' || buffer[i] == '#')
    continue;

      /* If `preprocessor' directive, do the include. */
      if (strncmp (buffer + i, "$include ", 9) == 0)
    {
      char *incfile, *t;

      /* Find start of filename. */
      for (incfile = buffer + i + 9; *incfile && whitespace (*incfile); incfile++)
        ;

      /* Find end of filename. */
      for (t = incfile; *t && cr_whitespace (*t) == 0; t++)
        ;

      *t = '\0';

      snarf_hosts_from_file (incfile);
      continue;
    }

      /* Skip internet address if present. */
      if (DIGIT (buffer[i]))
    for (; buffer[i] && cr_whitespace (buffer[i]) == 0; i++);

      /* Gobble up names.  Each name is separated with whitespace. */
      while (buffer[i])
    {
      for (; cr_whitespace (buffer[i]); i++)
        ;
      if (buffer[i] == '\0' || buffer[i] ==  '#')
        break;

      /* Isolate the current word. */
      for (start = i; buffer[i] && cr_whitespace (buffer[i]) == 0; i++)
        ;
      if (i == start)
        continue;
      strncpy (name, buffer + start, i - start);
      name[i - start] = '\0';
      add_host_name (name);
    }
    }
  fclose (file);
}

/* Return the hostname list. */
char **
get_hostname_list ()
{
  if (hostname_list_initialized == 0)
    initialize_hostname_list ();
  return (hostname_list);
}

void
clear_hostname_list ()
{
  register int i;

  if (hostname_list_initialized == 0)
    return;
  for (i = 0; i < hostname_list_length; i++)
    free (hostname_list[i]);
  hostname_list_length = 0;
}

/* Return a NULL terminated list of hostnames which begin with TEXT.
   Initialize the hostname list the first time if neccessary.
   The array is malloc ()'ed, but not the individual strings. */
static char **
hostnames_matching (text)
     char *text;
{
  register int i, len, nmatch, rsize;
  char **result;

  if (hostname_list_initialized == 0)
    initialize_hostname_list ();

  if (hostname_list_initialized == 0)
    return ((char **)NULL);

  /* Special case.  If TEXT consists of nothing, then the whole list is
     what is desired. */
  if (*text == '\0')
    {
      result = strvec_create (1 + hostname_list_length);
      for (i = 0; i < hostname_list_length; i++)
    result[i] = hostname_list[i];
      result[i] = (char *)NULL;
      return (result);
    }

  /* Scan until found, or failure. */
  len = strlen (text);
  result = (char **)NULL;
  for (i = nmatch = rsize = 0; i < hostname_list_length; i++)
    {
      if (STREQN (text, hostname_list[i], len) == 0)
    continue;

      /* OK, it matches.  Add it to the list. */
      if (nmatch >= (rsize - 1))
    {
      rsize = (rsize + 16) - (rsize % 16);
      result = strvec_resize (result, rsize);
    }

      result[nmatch++] = hostname_list[i];
    }
  if (nmatch)
    result[nmatch] = (char *)NULL;
  return (result);
}

/* The equivalent of the Korn shell C-o operate-and-get-next-history-line
   editing command. */
static int saved_history_line_to_use = -1;

static int
set_saved_history ()
{
  if (saved_history_line_to_use >= 0)
    rl_get_previous_history (history_length - saved_history_line_to_use, 0);
  saved_history_line_to_use = -1;
  rl_startup_hook = old_rl_startup_hook;
  return (0);
}

static int
operate_and_get_next (count, c)
     int count, c;
{
  int where;

  /* Accept the current line. */
  rl_newline (1, c);

  /* Find the current line, and find the next line to use. */
  where = where_history ();

  if ((history_is_stifled () && (history_length >= history_max_entries)) ||
      (where >= history_length - 1))
    saved_history_line_to_use = where;
  else
    saved_history_line_to_use = where + 1;

  old_rl_startup_hook = rl_startup_hook;
  rl_startup_hook = set_saved_history;

  return 0;
}

/* This vi mode command causes VI_EDIT_COMMAND to be run on the current
   command being entered (if no explicit argument is given), otherwise on
   a command from the history file. */

#define VI_EDIT_COMMAND     "fc -e ${VISUAL:-${EDITOR:-vi}}"
#define EMACS_EDIT_COMMAND  "fc -e ${VISUAL:-${EDITOR:-emacs}}"

static int
edit_and_execute_command (count, c, editing_mode, edit_command)
     int count, c, editing_mode;
     char *edit_command;
{
  char *command;
  int r, cclc, rrs;

  rrs = rl_readline_state;
  cclc = current_command_line_count;

  /* Accept the current line. */
  rl_newline (1, c);

  if (rl_explicit_arg)
    {
      command = (char *)xmalloc (strlen (edit_command) + 8);
      sprintf (command, "%s %d", edit_command, count);
    }
  else
    {
      /* Take the command we were just editing, add it to the history file,
     then call fc to operate on it.  We have to add a dummy command to
     the end of the history because fc ignores the last command (assumes
     it's supposed to deal with the command before the `fc'). */
      using_history ();
      bash_add_history (rl_line_buffer);
      bash_add_history ("");
      history_lines_this_session++;
      using_history ();
      command = savestring (edit_command);
    }

  /* Now, POSIX.1-2001 and SUSv3 say that the commands executed from the
     temporary file should be placed into the history.  We don't do that
     yet. */
  r = parse_and_execute (command, (editing_mode == VI_EDITING_MODE) ? "v" : "C-xC-e", SEVAL_NOHIST);

  current_command_line_count = cclc;

  /* Now erase the contents of the current line and undo the effects of the
     rl_accept_line() above.  We don't even want to make the text we just
     executed available for undoing. */
  rl_line_buffer[0] = '\0'; /* XXX */
  rl_point = rl_end = 0;
  rl_done = 0;
  rl_readline_state = rrs;

  rl_forced_update_display ();

  return r;
}

#if defined (VI_MODE)
static int
vi_edit_and_execute_command (count, c)
     int count, c;
{
  return (edit_and_execute_command (count, c, VI_EDITING_MODE, VI_EDIT_COMMAND));
}
#endif /* VI_MODE */

static int
emacs_edit_and_execute_command (count, c)
     int count, c;
{
  return (edit_and_execute_command (count, c, EMACS_EDITING_MODE, EMACS_EDIT_COMMAND));
}

#if defined (ALIAS)
static int
posix_edit_macros (count, key)
     int count, key;
{
  int c;
  char alias_name[3], *alias_value, *macro;

  c = rl_read_key ();
  alias_name[0] = '_';
  alias_name[1] = c;
  alias_name[2] = '\0';

  alias_value = get_alias_value (alias_name);
  if (alias_value && *alias_value)
    {
      macro = savestring (alias_value);
      rl_push_macro_input (macro);
    }
  return 0;
}
#endif

/* **************************************************************** */
/*                                  */
/*          How To Do Shell Completion          */
/*                                  */
/* **************************************************************** */

#define COMMAND_SEPARATORS ";|&{(`"

static int
check_redir (ti)
     int ti;
{
  register int this_char, prev_char;

  /* Handle the two character tokens `>&', `<&', and `>|'.
     We are not in a command position after one of these. */
  this_char = rl_line_buffer[ti];
  prev_char = rl_line_buffer[ti - 1];

  if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) ||
      (this_char == '|' && prev_char == '>'))
    return (1);
  else if ((this_char == '{' && prev_char == '$') || /* } */
       (char_is_quoted (rl_line_buffer, ti)))
    return (1);
  return (0);
}

#if defined (PROGRAMMABLE_COMPLETION)
/*
 * XXX - because of the <= start test, and setting os = s+1, this can
 * potentially return os > start.  This is probably not what we want to
 * happen, but fix later after 2.05a-release.
 */
static int
find_cmd_start (start)
     int start;
{
  register int s, os;

  os = 0;
  while (((s = skip_to_delim (rl_line_buffer, os, COMMAND_SEPARATORS)) <= start) &&
     rl_line_buffer[s])
    os = s+1;
  return os;
}

static int
find_cmd_end (end)
     int end;
{
  register int e;

  e = skip_to_delim (rl_line_buffer, end, COMMAND_SEPARATORS);
  return e;
}

static char *
find_cmd_name (start)
     int start;
{
  char *name;
  register int s, e;

  for (s = start; whitespace (rl_line_buffer[s]); s++)
    ;

  /* skip until a shell break character */
  e = skip_to_delim (rl_line_buffer, s, "()<>;&| \t\n");

  name = substring (rl_line_buffer, s, e);

  return (name);
}

static char *
prog_complete_return (text, matchnum)
     const char *text;
     int matchnum;
{
  static int ind;

  if (matchnum == 0)
    ind = 0;

  if (prog_complete_matches == 0 || prog_complete_matches[ind] == 0)
    return (char *)NULL;
  return (prog_complete_matches[ind++]);
}

#endif /* PROGRAMMABLE_COMPLETION */

/* Do some completion on TEXT.  The indices of TEXT in RL_LINE_BUFFER are
   at START and END.  Return an array of matches, or NULL if none. */
static char **
attempt_shell_completion (text, start, end)
     const char *text;
     int start, end;
{
  int in_command_position, ti, saveti, qc;
  char **matches, *command_separator_chars;

  command_separator_chars = COMMAND_SEPARATORS;
  matches = (char **)NULL;
  rl_ignore_some_completions_function = filename_completion_ignore;

  /* Determine if this could be a command word.  It is if it appears at
     the start of the line (ignoring preceding whitespace), or if it
     appears after a character that separates commands.  It cannot be a
     command word if we aren't at the top-level prompt. */
  ti = start - 1;
  saveti = qc = -1;

  while ((ti > -1) && (whitespace (rl_line_buffer[ti])))
    ti--;

#if 1
  /* If this is an open quote, maybe we're trying to complete a quoted
     command name. */
  if (rl_line_buffer[ti] == '"' || rl_line_buffer[ti] == '\'')
    {
      qc = rl_line_buffer[ti];
      saveti = ti--;
      while (ti > -1 && (whitespace (rl_line_buffer[ti])))
    ti--;
    }
#endif
      
  in_command_position = 0;
  if (ti < 0)
    {
      /* Only do command completion at the start of a line when we
     are prompting at the top level. */
      if (current_prompt_string == ps1_prompt)
    in_command_position++;
    }
  else if (member (rl_line_buffer[ti], command_separator_chars))
    {
      in_command_position++;

      if (check_redir (ti) == 1)
    in_command_position = 0;
    }
  else
    {
      /* This still could be in command position.  It is possible
     that all of the previous words on the line are variable
     assignments. */
    }

  /* Check that we haven't incorrectly flagged a closed command substitution
     as indicating we're in a command position. */
  if (in_command_position && ti >= 0 && rl_line_buffer[ti] == '`' &&
    *text != '`' && unclosed_pair (rl_line_buffer, end, "`") == 0)
    in_command_position = 0;

  /* Special handling for command substitution.  If *TEXT is a backquote,
     it can be the start or end of an old-style command substitution, or
     unmatched.  If it's unmatched, both calls to unclosed_pair will
     succeed.  */
  if (*text == '`' && 
    (in_command_position || (unclosed_pair (rl_line_buffer, start, "`") &&
                 unclosed_pair (rl_line_buffer, end, "`"))))
    matches = rl_completion_matches (text, command_subst_completion_function);

#if defined (PROGRAMMABLE_COMPLETION)
  /* Attempt programmable completion. */
  if (!matches && in_command_position == 0 && prog_completion_enabled &&
      (progcomp_size () > 0) && current_prompt_string == ps1_prompt)
    {
      int s, e, foundcs;
      char *n;

      /* XXX - don't free the members */
      if (prog_complete_matches)
    free (prog_complete_matches);
      prog_complete_matches = (char **)NULL;

      s = find_cmd_start (start);
      e = find_cmd_end (end);
      n = find_cmd_name (s);
      if (e > s)
    prog_complete_matches = programmable_completions (n, text, s, e, &foundcs);
      else
    foundcs = 0;
      FREE (n);
      /* XXX - if we found a COMPSPEC for the command, just return whatever
     the programmable completion code returns, and disable the default
     filename completion that readline will do unless the COPT_DEFAULT
     option has been set with the `-o default' option to complete. */
      if (foundcs)
    {
      /* If the user specified that the compspec returns filenames, make
         sure that readline knows it. */
      if (foundcs & COPT_FILENAMES)
        rl_filename_completion_desired = 1;
      /* If the user doesn't want a space appended, tell readline. */
      if (foundcs & COPT_NOSPACE)
        rl_completion_suppress_append = 1;
      /* Turn what the programmable completion code returns into what
         readline wants.  I should have made compute_lcd_of_matches
         external... */
      matches = rl_completion_matches (text, prog_complete_return);
      if ((foundcs & COPT_DEFAULT) == 0)
        rl_attempted_completion_over = 1;   /* no default */
      return (matches);
    }
    }
#endif

  /* New posix-style command substitution or variable name? */
  if (!matches && *text == '$')
    {
      if (qc != '\'' && text[1] == '(') /* ) */
    matches = rl_completion_matches (text, command_subst_completion_function);
      else
    matches = rl_completion_matches (text, variable_completion_function);
    }

  /* If the word starts in `~', and there is no slash in the word, then
     try completing this word as a username. */
  if (!matches && *text == '~' && !xstrchr (text, '/'))
    matches = rl_completion_matches (text, rl_username_completion_function);

  /* Another one.  Why not?  If the word starts in '@', then look through
     the world of known hostnames for completion first. */
  if (!matches && perform_hostname_completion && *text == '@')
    matches = rl_completion_matches (text, hostname_completion_function);

  /* And last, (but not least) if this word is in a command position, then
     complete over possible command names, including aliases, functions,
     and command names. */
  if (!matches && in_command_position)
    {
      if (start == 0 && end == 0 && text[0] == '\0' && no_empty_command_completion)
    {
      matches = (char **)NULL;
      rl_ignore_some_completions_function = bash_ignore_everything;
    }
      else
    {
      matches = rl_completion_matches (text, command_word_completion_function);
      /* If we are attempting command completion and nothing matches, we
         do not want readline to perform filename completion for us.  We
         still want to be able to complete partial pathnames, so set the
         completion ignore function to something which will remove
         filenames and leave directories in the match list. */
      if (matches == (char **)NULL)
        rl_ignore_some_completions_function = bash_ignore_filenames;
      else if (matches[1] == 0 && *matches[0] != '/')
        /* Turn off rl_filename_completion_desired so readline doesn't
           append a slash if there is a directory with the same name
           in the current directory, or other filename-specific things.
           If the name begins with a slash, we're either completing a
           full pathname or a directory pathname, and readline won't be
           looking in the current directory anyway, so there's no
           conflict. */
        rl_filename_completion_desired = 0;
      else if (matches[0] && matches[1] && STREQ (matches[0], matches[1]) && *matches[0] != '/')
        /* There are multiple instances of the same match (duplicate
           completions haven't yet been removed).  In this case, all of
           the matches will be the same, and the duplicate removal code
           will distill them all down to one.  We turn off
           rl_filename_completion_desired for the same reason as above.
           Remember: we only care if there's eventually a single unique
           completion.  If there are multiple completions this won't
           make a difference and the problem won't occur. */
        rl_filename_completion_desired = 0;
    }
    }

  /* This could be a globbing pattern, so try to expand it using pathname
     expansion. */
  if (!matches && glob_pattern_p (text))
    {
      matches = rl_completion_matches (text, glob_complete_word);
      /* A glob expression that matches more than one filename is problematic.
     If we match more than one filename, punt. */
      if (matches && matches[1] && rl_completion_type == TAB)
    {
      strvec_dispose (matches);
      matches = (char **)0;
    }
    }

  return (matches);
}

/* This is the function to call when the word to complete is in a position
   where a command word can be found.  It grovels $PATH, looking for commands
   that match.  It also scans aliases, function names, and the shell_builtin
   table. */
char *
command_word_completion_function (hint_text, state)
     const char *hint_text;
     int state;
{
  static char *hint = (char *)NULL;
  static char *path = (char *)NULL;
  static char *val = (char *)NULL;
  static char *filename_hint = (char *)NULL;
  static int path_index, hint_len, istate;
  static int mapping_over, local_index;
  static SHELL_VAR **varlist = (SHELL_VAR **)NULL;
#if defined (ALIAS)
  static alias_t **alias_list = (alias_t **)NULL;
#endif /* ALIAS */

  /* We have to map over the possibilities for command words.  If we have
     no state, then make one just for that purpose. */
  if (!state)
    {
      if (hint)
    free (hint);

      mapping_over = 0;
      val = (char *)NULL;

      /* If this is an absolute program name, do not check it against
     aliases, reserved words, functions or builtins.  We must check
     whether or not it is unique, and, if so, whether that filename
     is executable. */
      if (absolute_program (hint_text))
    {
      /* Perform tilde expansion on what's passed, so we don't end up
         passing filenames with tildes directly to stat(). */
      if (*hint_text == '~')
        hint = bash_tilde_expand (hint_text, 0);
      else
        hint = savestring (hint_text);
      hint_len = strlen (hint);

      if (filename_hint)
        free (filename_hint);
      filename_hint = savestring (hint);

      mapping_over = 4;
      istate = 0;
      goto inner;
    }

      hint = savestring (hint_text);
      hint_len = strlen (hint);

      path = get_string_value ("PATH");
      path_index = 0;

      /* Initialize the variables for each type of command word. */
      local_index = 0;

      if (varlist)
    free (varlist);

      varlist = all_visible_functions ();

#if defined (ALIAS)
      if (alias_list)
    free (alias_list);

      alias_list = all_aliases ();
#endif /* ALIAS */
    }

  /* mapping_over says what we are currently hacking.  Note that every case
     in this list must fall through when there are no more possibilities. */

  switch (mapping_over)
    {
    case 0:         /* Aliases come first. */
#if defined (ALIAS)
      while (alias_list && alias_list[local_index])
    {
      register char *alias;

      alias = alias_list[local_index++]->name;

      if (STREQN (alias, hint, hint_len))
        return (savestring (alias));
    }
#endif /* ALIAS */
      local_index = 0;
      mapping_over++;

    case 1:         /* Then shell reserved words. */
      {
    while (word_token_alist[local_index].word)
      {
        register char *reserved_word;

        reserved_word = word_token_alist[local_index++].word;

        if (STREQN (reserved_word, hint, hint_len))
          return (savestring (reserved_word));
      }
    local_index = 0;
    mapping_over++;
      }

    case 2:         /* Then function names. */
      while (varlist && varlist[local_index])
    {
      register char *varname;

      varname = varlist[local_index++]->name;

      if (STREQN (varname, hint, hint_len))
        return (savestring (varname));
    }
      local_index = 0;
      mapping_over++;

    case 3:         /* Then shell builtins. */
      for (; local_index < num_shell_builtins; local_index++)
    {
      /* Ignore it if it doesn't have a function pointer or if it
         is not currently enabled. */
      if (!shell_builtins[local_index].function ||
          (shell_builtins[local_index].flags & BUILTIN_ENABLED) == 0)
        continue;

      if (STREQN (shell_builtins[local_index].name, hint, hint_len))
        {
          int i = local_index++;

          return (savestring (shell_builtins[i].name));
        }
    }
      local_index = 0;
      mapping_over++;
    }

  /* Repeatedly call filename_completion_function while we have
     members of PATH left.  Question:  should we stat each file?
     Answer: we call executable_file () on each file. */
 outer:

  istate = (val != (char *)NULL);

  if (!istate)
    {
      char *current_path;

      /* Get the next directory from the path.  If there is none, then we
     are all done. */
      if (!path || !path[path_index] ||
      (current_path = extract_colon_unit (path, &path_index)) == 0)
    return ((char *)NULL);

      if (*current_path == 0)
    {
      free (current_path);
      current_path = savestring (".");
    }

      if (*current_path == '~')
    {
      char *t;

      t = bash_tilde_expand (current_path, 0);
      free (current_path);
      current_path = t;
    }

      if (filename_hint)
    free (filename_hint);

      filename_hint = sh_makepath (current_path, hint, 0);

      free (current_path);
    }

 inner:
  val = rl_filename_completion_function (filename_hint, istate);
  istate = 1;

  if (val == 0)
    {
      /* If the hint text is an absolute program, then don't bother
     searching through PATH. */
      if (absolute_program (hint))
    return ((char *)NULL);

      goto outer;
    }
  else
    {
      int match, freetemp;
      char *temp;

      if (absolute_program (hint))
    {
      match = strncmp (val, hint, hint_len) == 0;
      /* If we performed tilde expansion, restore the original
         filename. */
      if (*hint_text == '~')
        {
          int l, tl, vl;
          vl = strlen (val);
          tl = strlen (hint_text);
          l = vl - hint_len;    /* # of chars added */
          temp = (char *)xmalloc (l + 2 + tl);
          strcpy (temp, hint_text);
          strcpy (temp + tl, val + vl - l);
        }
      else
        temp = savestring (val);
      freetemp = 1;
    }
      else
    {
      temp = strrchr (val, '/');

      if (temp)
        {
          temp++;
          freetemp = match = strncmp (temp, hint, hint_len) == 0;
          if (match)
        temp = savestring (temp);
        }
      else
        freetemp = match = 0;
    }

      /* If we have found a match, and it is an executable file or a
     directory name, return it. */
      if (match && executable_or_directory (val))
    {
      free (val);
      val = "";     /* So it won't be NULL. */
      return (temp);
    }
      else
    {
      if (freetemp)
        free (temp);
      free (val);
      goto inner;
    }
    }
}

/* Completion inside an unterminated command substitution. */
static char *
command_subst_completion_function (text, state)
     const char *text;
     int state;
{
  static char **matches = (char **)NULL;
  static const char *orig_start;
  static char *filename_text = (char *)NULL;
  static int cmd_index, start_len;
  char *value;

  if (state == 0)
    {
      if (filename_text)
    free (filename_text);
      orig_start = text;
      if (*text == '`')
    text++;
      else if (*text == '$' && text[1] == '(')  /* ) */
    text += 2;
      start_len = text - orig_start;
      filename_text = savestring (text);
      if (matches)
    free (matches);

      /*
       * At this point we can entertain the idea of re-parsing
       * `filename_text' into a (possibly incomplete) command name and
       * arguments, and doing completion based on that.  This is
       * currently very rudimentary, but it is a small improvement.
       */
      for (value = filename_text + strlen (filename_text) - 1; value > filename_text; value--)
        if (whitespace (*value) || member (*value, COMMAND_SEPARATORS))
          break;
      if (value <= filename_text)
    matches = rl_completion_matches (filename_text, command_word_completion_function);
      else
    {
      value++;
      start_len += value - filename_text;
      if (whitespace (value[-1]))
        matches = rl_completion_matches (value, rl_filename_completion_function);
      else
        matches = rl_completion_matches (value, command_word_completion_function);
    }

      /* If there is more than one match, rl_completion_matches has already
     put the lcd in matches[0].  Skip over it. */
      cmd_index = matches && matches[0] && matches[1];
    }

  if (!matches || !matches[cmd_index])
    {
      rl_filename_quoting_desired = 0;  /* disable quoting */
      return ((char *)NULL);
    }
  else
    {
      value = (char *)xmalloc (1 + start_len + strlen (matches[cmd_index]));

      if (start_len == 1)
    value[0] = *orig_start;
      else
    strncpy (value, orig_start, start_len);

      strcpy (value + start_len, matches[cmd_index]);

      cmd_index++;
      return (value);
    }
}

/* Okay, now we write the entry_function for variable completion. */
static char *
variable_completion_function (text, state)
     const char *text;
     int state;
{
  static char **varlist = (char **)NULL;
  static int varlist_index;
  static char *varname = (char *)NULL;
  static int namelen;
  static int first_char, first_char_loc;

  if (!state)
    {
      if (varname)
    free (varname);

      first_char_loc = 0;
      first_char = text[0];

      if (first_char == '$')
    first_char_loc++;

      if (text[first_char_loc] == '{')
    first_char_loc++;

      varname = savestring (text + first_char_loc);

      namelen = strlen (varname);
      if (varlist)
    strvec_dispose (varlist);

      varlist = all_variables_matching_prefix (varname);
      varlist_index = 0;
    }

  if (!varlist || !varlist[varlist_index])
    {
      return ((char *)NULL);
    }
  else
    {
      char *value;

      value = (char *)xmalloc (4 + strlen (varlist[varlist_index]));

      if (first_char_loc)
    {
      value[0] = first_char;
      if (first_char_loc == 2)
        value[1] = '{';
    }

      strcpy (value + first_char_loc, varlist[varlist_index]);
      if (first_char_loc == 2)
    strcat (value, "}");

      varlist_index++;
      return (value);
    }
}

/* How about a completion function for hostnames? */
static char *
hostname_completion_function (text, state)
     const char *text;
     int state;
{
  static char **list = (char **)NULL;
  static int list_index = 0;
  static int first_char, first_char_loc;

  /* If we don't have any state, make some. */
  if (state == 0)
    {
      FREE (list);

      list = (char **)NULL;

      first_char_loc = 0;
      first_char = *text;

      if (first_char == '@')
    first_char_loc++;

      list = hostnames_matching ((char *)text+first_char_loc);
      list_index = 0;
    }

  if (list && list[list_index])
    {
      char *t;

      t = (char *)xmalloc (2 + strlen (list[list_index]));
      *t = first_char;
      strcpy (t + first_char_loc, list[list_index]);
      list_index++;
      return (t);
    }

  return ((char *)NULL);
}

/*
 * A completion function for service names from /etc/services (or wherever).
 */
char *
bash_servicename_completion_function (text, state)
     const char *text;
     int state;
{
#if defined (__WIN32__) || defined (__OPENNT) || !defined (HAVE_GETSERVENT)
  return ((char *)NULL);
#else
  static char *sname = (char *)NULL;
  static struct servent *srvent;
  static int snamelen, firstc;
  char *value;
  char **alist, *aentry;
  int afound;

  if (state == 0)
    {
      FREE (sname);
      firstc = *text;

      sname = savestring (text);
      snamelen = strlen (sname);
      setservent (0);
    }

  while (srvent = getservent ())
    {
      afound = 0;
      if (snamelen == 0 || (STREQN (sname, srvent->s_name, snamelen)))
    break;
      /* Not primary, check aliases */
      for (alist = srvent->s_aliases; aentry = *alist; alist++)
    {
      if (STREQN (sname, aentry, snamelen))
        {
          afound = 1;
          break;
        }
    }

      if (afound)
    break;
    }

  if (srvent == 0)
    {
      endservent ();
      return ((char *)NULL);
    }

  value = afound ? savestring (aentry) : savestring (srvent->s_name);
  return value;
#endif
}

/*
 * A completion function for group names from /etc/group (or wherever).
 */
char *
bash_groupname_completion_function (text, state)
     const char *text;
     int state;
{
#if defined (__WIN32__) || defined (__OPENNT) || !defined (HAVE_GRP_H)
  return ((char *)NULL);
#else
  static char *gname = (char *)NULL;
  static struct group *grent;
  static int gnamelen;
  char *value;

  if (state == 0)
    {
      FREE (gname);
      gname = savestring (text);
      gnamelen = strlen (gname);

      setgrent ();
    }

  while (grent = getgrent ())
    {
      if (gnamelen == 0 || (STREQN (gname, grent->gr_name, gnamelen)))
        break;
    }

  if (grent == 0)
    {
      endgrent ();
      return ((char *)NULL);
    }

  value = savestring (grent->gr_name);
  return (value);
#endif
}

/* Functions to perform history and alias expansions on the current line. */

#if defined (BANG_HISTORY)
/* Perform history expansion on the current line.  If no history expansion
   is done, pre_process_line() returns what it was passed, so we need to
   allocate a new line here. */
static char *
history_expand_line_internal (line)
     char *line;
{
  char *new_line;

  new_line = pre_process_line (line, 0, 0);
  return (new_line == line) ? savestring (line) : new_line;
}
#endif

/* There was an error in expansion.  Let the preprocessor print
   the error here. */
static void
cleanup_expansion_error ()
{
  char *to_free;

  fprintf (rl_outstream, "\r\n");
  to_free = pre_process_line (rl_line_buffer, 1, 0);
  if (to_free != rl_line_buffer)
    free (to_free);
  putc ('\r', rl_outstream);
  rl_forced_update_display ();
}

/* If NEW_LINE differs from what is in the readline line buffer, add an
   undo record to get from the readline line buffer contents to the new
   line and make NEW_LINE the current readline line. */
static void
maybe_make_readline_line (new_line)
     char *new_line;
{
  if (strcmp (new_line, rl_line_buffer) != 0)
    {
      rl_point = rl_end;

      rl_add_undo (UNDO_BEGIN, 0, 0, 0);
      rl_delete_text (0, rl_point);
      rl_point = rl_end = rl_mark = 0;
      rl_insert_text (new_line);
      rl_add_undo (UNDO_END, 0, 0, 0);
    }
}

/* Make NEW_LINE be the current readline line.  This frees NEW_LINE. */
static void
set_up_new_line (new_line)
     char *new_line;
{
  int old_point, at_end;

  old_point = rl_point;
  at_end = rl_point == rl_end;

  /* If the line was history and alias expanded, then make that
     be one thing to undo. */
  maybe_make_readline_line (new_line);
  free (new_line);

  /* Place rl_point where we think it should go. */
  if (at_end)
    rl_point = rl_end;
  else if (old_point < rl_end)
    {
      rl_point = old_point;
      if (!whitespace (rl_line_buffer[rl_point]))
    rl_forward_word (1, 0);
    }
}

#if defined (ALIAS)
/* Expand aliases in the current readline line. */
static int
alias_expand_line (count, ignore)
     int count, ignore;
{
  char *new_line;

  new_line = alias_expand (rl_line_buffer);

  if (new_line)
    {
      set_up_new_line (new_line);
      return (0);
    }
  else
    {
      cleanup_expansion_error ();
      return (1);
    }
}
#endif

#if defined (BANG_HISTORY)
/* History expand the line. */
static int
history_expand_line (count, ignore)
     int count, ignore;
{
  char *new_line;

  new_line = history_expand_line_internal (rl_line_buffer);

  if (new_line)
    {
      set_up_new_line (new_line);
      return (0);
    }
  else
    {
      cleanup_expansion_error ();
      return (1);
    }
}

/* Expand history substitutions in the current line and then insert a
   space (hopefully close to where we were before). */
static int
tcsh_magic_space (count, ignore)
     int count, ignore;
{
  int dist_from_end, old_point;

  old_point = rl_point;
  dist_from_end = rl_end - rl_point;
  if (history_expand_line (count, ignore) == 0)
    {
      /* Try a simple heuristic from Stephen Gildea <gildea@intouchsys.com>.
     This works if all expansions were before rl_point or if no expansions
     were performed. */
      rl_point = (old_point == 0) ? old_point : rl_end - dist_from_end;
      rl_insert (1, ' ');
      return (0);
    }
  else
    return (1);
}
#endif

/* History and alias expand the line. */
static int
history_and_alias_expand_line (count, ignore)
     int count, ignore;
{
  char *new_line;

  new_line = pre_process_line (rl_line_buffer, 0, 0);
  if (new_line == rl_line_buffer)
    new_line = savestring (new_line);

#if defined (ALIAS)
  if (new_line)
    {
      char *alias_line;

      alias_line = alias_expand (new_line);
      free (new_line);
      new_line = alias_line;
    }
#endif /* ALIAS */

  if (new_line)
    {
      set_up_new_line (new_line);
      return (0);
    }
  else
    {
      cleanup_expansion_error ();
      return (1);
    }
}

/* History and alias expand the line, then perform the shell word
   expansions by calling expand_string.  This can't use set_up_new_line()
   because we want the variable expansions as a separate undo'able
   set of operations. */
static int
shell_expand_line (count, ignore)
     int count, ignore;
{
  char *new_line;
  WORD_LIST *expanded_string;

  new_line = pre_process_line (rl_line_buffer, 0, 0);
  if (new_line == rl_line_buffer)
    new_line = savestring (new_line);

#if defined (ALIAS)
  if (new_line)
    {
      char *alias_line;

      alias_line = alias_expand (new_line);
      free (new_line);
      new_line = alias_line;
    }
#endif /* ALIAS */

  if (new_line)
    {
      int old_point = rl_point;
      int at_end = rl_point == rl_end;

      /* If the line was history and alias expanded, then make that
     be one thing to undo. */
      maybe_make_readline_line (new_line);
      free (new_line);

      /* If there is variable expansion to perform, do that as a separate
     operation to be undone. */
      new_line = savestring (rl_line_buffer);
      expanded_string = expand_string (new_line, 0);
      FREE (new_line);
      if (expanded_string == 0)
    {
      new_line = (char *)xmalloc (1);
      new_line[0] = '\0';
    }
      else
    {
      new_line = string_list (expanded_string);
      dispose_words (expanded_string);
    }

      maybe_make_readline_line (new_line);
      free (new_line);

      /* Place rl_point where we think it should go. */
      if (at_end)
    rl_point = rl_end;
      else if (old_point < rl_end)
    {
      rl_point = old_point;
      if (!whitespace (rl_line_buffer[rl_point]))
        rl_forward_word (1, 0);
    }
      return 0;
    }
  else
    {
      cleanup_expansion_error ();
      return 1;
    }
}

/* Define NO_FORCE_FIGNORE if you want to match filenames that would
   otherwise be ignored if they are the only possible matches. */
/* #define NO_FORCE_FIGNORE */

/* If FIGNORE is set, then don't match files with the given suffixes when
   completing filenames.  If only one of the possibilities has an acceptable
   suffix, delete the others, else just return and let the completer
   signal an error.  It is called by the completer when real
   completions are done on filenames by the completer's internal
   function, not for completion lists (M-?) and not on "other"
   completion types, such as hostnames or commands. */

static struct ignorevar fignore =
{
  "FIGNORE",
  (struct ign *)0,
  0,
  (char *)0,
  (sh_iv_item_func_t *) 0,
};

static void
_ignore_completion_names (names, name_func)
     char **names;
     sh_ignore_func_t *name_func;
{
  char **newnames;
  int idx, nidx;
#ifdef NO_FORCE_FIGNORE
  char **oldnames;
  int oidx;
#endif

  /* If there is only one completion, see if it is acceptable.  If it is
     not, free it up.  In any case, short-circuit and return.  This is a
     special case because names[0] is not the prefix of the list of names
     if there is only one completion; it is the completion itself. */
  if (names[1] == (char *)0)
    {
#ifndef NO_FORCE_FIGNORE
      if ((*name_func) (names[0]) == 0)
    {
      free (names[0]);
      names[0] = (char *)NULL;
    }
#endif
      return;
    }

  /* Allocate space for array to hold list of pointers to matching
     filenames.  The pointers are copied back to NAMES when done. */
  for (nidx = 1; names[nidx]; nidx++)
    ;
  newnames = strvec_create (nidx + 1);
#ifdef NO_FORCE_FIGNORE
  oldnames = strvec_create (nidx - 1);
  oidx = 0;
#endif

  newnames[0] = names[0];
  for (idx = nidx = 1; names[idx]; idx++)
    {
      if ((*name_func) (names[idx]))
    newnames[nidx++] = names[idx];
      else
#ifndef NO_FORCE_FIGNORE
    free (names[idx]);
#else
    oldnames[oidx++] = names[idx];
#endif
    }

  newnames[nidx] = (char *)NULL;

  /* If none are acceptable then let the completer handle it. */
  if (nidx == 1)
    {
#ifndef NO_FORCE_FIGNORE
      free (names[0]);
      names[0] = (char *)NULL;
#else
      free (oldnames);
#endif
      free (newnames);
      return;
    }

#ifdef NO_FORCE_FIGNORE
  while (oidx)
    free (oldnames[--oidx]);
  free (oldnames);
#endif

  /* If only one is acceptable, copy it to names[0] and return. */
  if (nidx == 2)
    {
      free (names[0]);
      names[0] = newnames[1];
      names[1] = (char *)NULL;
      free (newnames);
      return;
    }

  /* Copy the acceptable names back to NAMES, set the new array end,
     and return. */
  for (nidx = 1; newnames[nidx]; nidx++)
    names[nidx] = newnames[nidx];
  names[nidx] = (char *)NULL;
  free (newnames);
}

static int
name_is_acceptable (name)
     const char *name;
{
  struct ign *p;
  int nlen;

  for (nlen = strlen (name), p = fignore.ignores; p->val; p++)
    {
      if (nlen > p->len && p->len > 0 && STREQ (p->val, &name[nlen - p->len]))
    return (0);
    }

  return (1);
}

#if 0
static int
ignore_dot_names (name)
     char *name;
{
  return (name[0] != '.');
}
#endif

static int
filename_completion_ignore (names)
     char **names;
{
#if 0
  if (glob_dot_filenames == 0)
    _ignore_completion_names (names, ignore_dot_names);
#endif

  setup_ignore_patterns (&fignore);

  if (fignore.num_ignores == 0)
    return 0;

  _ignore_completion_names (names, name_is_acceptable);

  return 0;
}

/* Return 1 if NAME is a directory. */
static int
test_for_directory (name)
     const char *name;
{
  struct stat finfo;
  char *fn;

  fn = bash_tilde_expand (name, 0);
  if (stat (fn, &finfo) != 0)
    {
      free (fn);
      return 0;
    }
  free (fn);
  return (S_ISDIR (finfo.st_mode));
}

/* Remove files from NAMES, leaving directories. */
static int
bash_ignore_filenames (names)
     char **names;
{
  _ignore_completion_names (names, test_for_directory);
  return 0;
}

static int
return_zero (name)
     const char *name;
{
  return 0;
}

static int
bash_ignore_everything (names)
     char **names;
{
  _ignore_completion_names (names, return_zero);
  return 0;
}

/* Handle symbolic link references and other directory name
   expansions while hacking completion. */
static int
bash_directory_completion_hook (dirname)
     char **dirname;
{
  char *local_dirname, *new_dirname, *t;
  int return_value, should_expand_dirname;
  WORD_LIST *wl;

  return_value = should_expand_dirname = 0;
  local_dirname = *dirname;

#if 0
  should_expand_dirname = xstrchr (local_dirname, '$') || xstrchr (local_dirname, '`');
#else
  if (xstrchr (local_dirname, '$'))
    should_expand_dirname = 1;
  else
    {
      t = xstrchr (local_dirname, '`');
      if (t && unclosed_pair (local_dirname, strlen (local_dirname), "`") == 0)
    should_expand_dirname = 1;
    }
#endif

  if (should_expand_dirname)  
    {
      new_dirname = savestring (local_dirname);
      wl = expand_prompt_string (new_dirname, 0);   /* does the right thing */
      if (wl)
    {
      *dirname = string_list (wl);
      /* Tell the completer to replace the directory name only if we
         actually expanded something. */
      return_value = STREQ (local_dirname, *dirname) == 0;
      free (local_dirname);
      free (new_dirname);
      dispose_words (wl);
      local_dirname = *dirname;
    }
      else
    {
      free (new_dirname);
      free (local_dirname);
      *dirname = (char *)xmalloc (1);
      **dirname = '\0';
      return 1;
    }
    }

  if (!no_symbolic_links && (local_dirname[0] != '.' || local_dirname[1]))
    {
      char *temp1, *temp2;
      int len1, len2;

      t = get_working_directory ("symlink-hook");
      temp1 = make_absolute (local_dirname, t);
      free (t);
      temp2 = sh_canonpath (temp1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
      /* If we can't canonicalize, bail. */
      if (temp2 == 0)
    {
      free (temp1);
      return 1;
    }
      len1 = strlen (temp1);
      if (temp1[len1 - 1] == '/')
    {
      len2 = strlen (temp2);
      temp2 = (char *)xrealloc (temp2, len2 + 2);
      temp2[len2] = '/';
      temp2[len2 + 1] = '\0';
    }
      free (local_dirname);
      *dirname = temp2;
      free (temp1);
    }
  return (return_value);
}

static char **history_completion_array = (char **)NULL;
static int harry_size;
static int harry_len;

static void
build_history_completion_array ()
{
  register int i, j;
  HIST_ENTRY **hlist;
  char **tokens;

  /* First, clear out the current dynamic history completion list. */
  if (harry_size)
    {
      strvec_dispose (history_completion_array);
      history_completion_array = (char **)NULL;
      harry_size = 0;
      harry_len = 0;
    }

  /* Next, grovel each line of history, making each shell-sized token
     a separate entry in the history_completion_array. */
  hlist = history_list ();

  if (hlist)
    {
      for (i = 0; hlist[i]; i++)
    {
      /* Separate each token, and place into an array. */
      tokens = history_tokenize (hlist[i]->line);

      for (j = 0; tokens && tokens[j]; j++)
        {
          if (harry_len + 2 > harry_size)
            history_completion_array = strvec_resize (history_completion_array, harry_size += 10);

          history_completion_array[harry_len++] = tokens[j];
          history_completion_array[harry_len] = (char *)NULL;
        }
      free (tokens);
    }

      /* Sort the complete list of tokens. */
      qsort (history_completion_array, harry_len, sizeof (char *), (QSFUNC *)strvec_strcmp);
    }
}

static char *
history_completion_generator (hint_text, state)
     const char *hint_text;
     int state;
{
  static int local_index, len;
  static const char *text;

  /* If this is the first call to the generator, then initialize the
     list of strings to complete over. */
  if (state == 0)
    {
      local_index = 0;
      build_history_completion_array ();
      text = hint_text;
      len = strlen (text);
    }

  while (history_completion_array && history_completion_array[local_index])
    {
      if (strncmp (text, history_completion_array[local_index++], len) == 0)
    return (savestring (history_completion_array[local_index - 1]));
    }
  return ((char *)NULL);
}

static int
dynamic_complete_history (count, key)
     int count, key;
{
  int r;

  rl_compentry_func_t *orig_func;
  rl_completion_func_t *orig_attempt_func;

  orig_func = rl_completion_entry_function;
  orig_attempt_func = rl_attempted_completion_function;
  rl_completion_entry_function = history_completion_generator;
  rl_attempted_completion_function = (rl_completion_func_t *)NULL;

  /* XXX - use rl_completion_mode here? */
  if (rl_last_func == dynamic_complete_history)
    r = rl_complete_internal ('?');
  else
    r = rl_complete_internal (TAB);

  rl_completion_entry_function = orig_func;
  rl_attempted_completion_function = orig_attempt_func;
  return r;
}

#if defined (SPECIFIC_COMPLETION_FUNCTIONS)
static int
bash_complete_username (ignore, ignore2)
     int ignore, ignore2;
{
  return bash_complete_username_internal (rl_completion_mode (bash_complete_username));
}

static int
bash_possible_username_completions (ignore, ignore2)
     int ignore, ignore2;
{
  return bash_complete_username_internal ('?');
}

static int
bash_complete_username_internal (what_to_do)
     int what_to_do;
{
  return bash_specific_completion (what_to_do, rl_username_completion_function);
}

static int
bash_complete_filename (ignore, ignore2)
     int ignore, ignore2;
{
  return bash_complete_filename_internal (rl_completion_mode (bash_complete_filename));
}

static int
bash_possible_filename_completions (ignore, ignore2)
     int ignore, ignore2;
{
  return bash_complete_filename_internal ('?');
}

static int
bash_complete_filename_internal (what_to_do)
     int what_to_do;
{
  rl_compentry_func_t *orig_func;
  rl_completion_func_t *orig_attempt_func;
  rl_icppfunc_t *orig_dir_func;
  const char *orig_rl_completer_word_break_characters;
  int r;

  orig_func = rl_completion_entry_function;
  orig_attempt_func = rl_attempted_completion_function;
  orig_dir_func = rl_directory_completion_hook;
  orig_rl_completer_word_break_characters = rl_completer_word_break_characters;
  rl_completion_entry_function = rl_filename_completion_function;
  rl_attempted_completion_function = (rl_completion_func_t *)NULL;
  rl_directory_completion_hook = (rl_icppfunc_t *)NULL;
  rl_completer_word_break_characters = " \t\n\"\'";

  r = rl_complete_internal (what_to_do);

  rl_completion_entry_function = orig_func;
  rl_attempted_completion_function = orig_attempt_func;
  rl_directory_completion_hook = orig_dir_func;
  rl_completer_word_break_characters = orig_rl_completer_word_break_characters;

  return r;
}

static int
bash_complete_hostname (ignore, ignore2)
     int ignore, ignore2;
{
  return bash_complete_hostname_internal (rl_completion_mode (bash_complete_hostname));
}

static int
bash_possible_hostname_completions (ignore, ignore2)
     int ignore, ignore2;
{
  return bash_complete_hostname_internal ('?');
}

static int
bash_complete_variable (ignore, ignore2)
     int ignore, ignore2;
{
  return bash_complete_variable_internal (rl_completion_mode (bash_complete_variable));
}

static int
bash_possible_variable_completions (ignore, ignore2)
     int ignore, ignore2;
{
  return bash_complete_variable_internal ('?');
}

static int
bash_complete_command (ignore, ignore2)
     int ignore, ignore2;
{
  return bash_complete_command_internal (rl_completion_mode (bash_complete_command));
}

static int
bash_possible_command_completions (ignore, ignore2)
     int ignore, ignore2;
{
  return bash_complete_command_internal ('?');
}

static int
bash_complete_hostname_internal (what_to_do)
     int what_to_do;
{
  return bash_specific_completion (what_to_do, hostname_completion_function);
}

static int
bash_complete_variable_internal (what_to_do)
     int what_to_do;
{
  return bash_specific_completion (what_to_do, variable_completion_function);
}

static int
bash_complete_command_internal (what_to_do)
     int what_to_do;
{
  return bash_specific_completion (what_to_do, command_word_completion_function);
}

static char *globtext;
static char *globorig;

static char *
glob_complete_word (text, state)
     const char *text;
     int state;
{
  static char **matches = (char **)NULL;
  static int ind;
  int glen;
  char *ret;

  if (state == 0)
    {
      rl_filename_completion_desired = 1;
      FREE (matches);
      if (globorig != globtext)
    FREE (globorig);
      FREE (globtext);

      if (rl_explicit_arg)
    {
      globorig = savestring (text);
      glen = strlen (text);
      globtext = (char *)xmalloc (glen + 2);
      strcpy (globtext, text);
      globtext[glen] = '*';
      globtext[glen+1] = '\0';
    }
      else
        globtext = globorig = savestring (text);

      matches = shell_glob_filename (globtext);
      if (GLOB_FAILED (matches))
    matches = (char **)NULL;
      ind = 0;
    }

  ret = matches ? matches[ind] : (char *)NULL;
  ind++;
  return ret;
}

static int
bash_glob_completion_internal (what_to_do)
     int what_to_do;
{
  return bash_specific_completion (what_to_do, glob_complete_word);
}

/* A special quoting function so we don't end up quoting globbing characters
   in the word if there are no matches or multiple matches. */
static char *
bash_glob_quote_filename (s, rtype, qcp)
     char *s;
     int rtype;
     char *qcp;
{
  if (globorig && qcp && *qcp == '\0' && STREQ (s, globorig))
    return (savestring (s));
  else
    return (bash_quote_filename (s, rtype, qcp));
}

static int
bash_glob_complete_word (count, key)
     int count, key;
{
  int r;
  rl_quote_func_t *orig_quoting_function;

  rl_explicit_arg = 1;  /* force `*' append */
  orig_quoting_function = rl_filename_quoting_function;
  rl_filename_quoting_function = bash_glob_quote_filename;
  
  r = bash_glob_completion_internal (rl_completion_mode (bash_glob_complete_word));

  rl_filename_quoting_function = orig_quoting_function;
  return r;
}

static int
bash_glob_expand_word (count, key)
     int count, key;
{
  return bash_glob_completion_internal ('*');
}

static int
bash_glob_list_expansions (count, key)
     int count, key;
{
  return bash_glob_completion_internal ('?');
}

static int
bash_specific_completion (what_to_do, generator)
     int what_to_do;
     rl_compentry_func_t *generator;
{
  rl_compentry_func_t *orig_func;
  rl_completion_func_t *orig_attempt_func;
  int r;

  orig_func = rl_completion_entry_function;
  orig_attempt_func = rl_attempted_completion_function;
  rl_completion_entry_function = generator;
  rl_attempted_completion_function = NULL;

  r = rl_complete_internal (what_to_do);

  rl_completion_entry_function = orig_func;
  rl_attempted_completion_function = orig_attempt_func;

  return r;
}

#endif  /* SPECIFIC_COMPLETION_FUNCTIONS */

/* Filename quoting for completion. */
/* A function to strip unquoted quote characters (single quotes, double
   quotes, and backslashes).  It allows single quotes to appear
   within double quotes, and vice versa.  It should be smarter. */
static char *
bash_dequote_filename (text, quote_char)
     char *text;
     int quote_char;
{
  char *ret, *p, *r;
  int l, quoted;

  l = strlen (text);
  ret = (char *)xmalloc (l + 1);
  for (quoted = quote_char, p = text, r = ret; p && *p; p++)
    {
      /* Allow backslash-quoted characters to pass through unscathed. */
      if (*p == '\\')
    {
      *r++ = *++p;
      if (*p == '\0')
        break;
      continue;
    }
      /* Close quote. */
      if (quoted && *p == quoted)
    {
      quoted = 0;
      continue;
    }
      /* Open quote. */
      if (quoted == 0 && (*p == '\'' || *p == '"'))
    {
      quoted = *p;
      continue;
    }
      *r++ = *p;
    }
  *r = '\0';
  return ret;
}

/* Quote characters that the readline completion code would treat as
   word break characters with backslashes.  Pass backslash-quoted
   characters through without examination. */
static char *
quote_word_break_chars (text)
     char *text;
{
  char *ret, *r, *s;
  int l;

  l = strlen (text);
  ret = (char *)xmalloc ((2 * l) + 1);
  for (s = text, r = ret; *s; s++)
    {
      /* Pass backslash-quoted characters through, including the backslash. */
      if (*s == '\\')
    {
      *r++ = '\\';
      *r++ = *++s;
      if (*s == '\0')
        break;
      continue;
    }
      /* OK, we have an unquoted character.  Check its presence in
     rl_completer_word_break_characters. */
      if (xstrchr (rl_completer_word_break_characters, *s))
    *r++ = '\\';
      *r++ = *s;
    }
  *r = '\0';
  return ret;
}

/* Quote a filename using double quotes, single quotes, or backslashes
   depending on the value of completion_quoting_style.  If we're
   completing using backslashes, we need to quote some additional
   characters (those that readline treats as word breaks), so we call
   quote_word_break_chars on the result.  This returns newly-allocated
   memory. */
static char *
bash_quote_filename (s, rtype, qcp)
     char *s;
     int rtype;
     char *qcp;
{
  char *rtext, *mtext, *ret;
  int rlen, cs;

  rtext = (char *)NULL;

  /* If RTYPE == MULT_MATCH, it means that there is
     more than one match.  In this case, we do not add
     the closing quote or attempt to perform tilde
     expansion.  If RTYPE == SINGLE_MATCH, we try
     to perform tilde expansion, because single and double
     quotes inhibit tilde expansion by the shell. */

  mtext = s;
  if (mtext[0] == '~' && rtype == SINGLE_MATCH)
    mtext = bash_tilde_expand (s, 0);

  cs = completion_quoting_style;
  /* Might need to modify the default completion style based on *qcp,
     since it's set to any user-provided opening quote.  We also change
     to single-quoting if there is no user-provided opening quote and
     the word being completed contains newlines, since those are not
     quoted correctly using backslashes (a backslash-newline pair is
     special to the shell parser). */
  if (*qcp == '\0' && cs == COMPLETE_BSQUOTE && xstrchr (mtext, '\n'))
    cs = COMPLETE_SQUOTE;
  else if (*qcp == '"')
    cs = COMPLETE_DQUOTE;
  else if (*qcp == '\'')
    cs = COMPLETE_SQUOTE;
#if defined (BANG_HISTORY)
  else if (*qcp == '\0' && history_expansion && cs == COMPLETE_DQUOTE &&
       history_expansion_inhibited == 0 && xstrchr (mtext, '!'))
    cs = COMPLETE_BSQUOTE;

  if (*qcp == '"' && history_expansion && cs == COMPLETE_DQUOTE &&
    history_expansion_inhibited == 0 && xstrchr (mtext, '!'))
    {
      cs = COMPLETE_BSQUOTE;
      *qcp = '\0';
    }
#endif

  switch (cs)
    {
    case COMPLETE_DQUOTE:
      rtext = sh_double_quote (mtext);
      break;
    case COMPLETE_SQUOTE:
      rtext = sh_single_quote (mtext);
      break;
    case COMPLETE_BSQUOTE:
      rtext = sh_backslash_quote (mtext);
      break;
    }

  if (mtext != s)
    free (mtext);

  /* We may need to quote additional characters: those that readline treats
     as word breaks that are not quoted by backslash_quote. */
  if (rtext && cs == COMPLETE_BSQUOTE)
    {
      mtext = quote_word_break_chars (rtext);
      free (rtext);
      rtext = mtext;
    }

  /* Leave the opening quote intact.  The readline completion code takes
     care of avoiding doubled opening quotes. */
  rlen = strlen (rtext);
  ret = (char *)xmalloc (rlen + 1);
  strcpy (ret, rtext);

  /* If there are multiple matches, cut off the closing quote. */
  if (rtype == MULT_MATCH && cs != COMPLETE_BSQUOTE)
    ret[rlen - 1] = '\0';
  free (rtext);
  return ret;
}

/* Support for binding readline key sequences to Unix commands. */
static Keymap cmd_xmap;

static int
bash_execute_unix_command (count, key)
     int count; /* ignored */
     int key;
{
  Keymap ckmap;     /* current keymap */
  Keymap xkmap;     /* unix command executing keymap */
  register int i;
  char *cmd;
  int old_line_count;
  int *ts;

  /* First, we need to find the right command to execute.  This is tricky,
     because we might have already indirected into another keymap. */
  ckmap = rl_get_keymap ();
  if (ckmap != rl_executing_keymap)
    {
      /* bogus.  we have to search.  only handle one level of indirection. */
      for (i = 0; i < KEYMAP_SIZE; i++)
    {
      if (ckmap[i].type == ISKMAP && (Keymap)ckmap[i].function == rl_executing_keymap)
        break;
    }
      if (i < KEYMAP_SIZE)
    xkmap = (Keymap)cmd_xmap[i].function;
      else
    {
      rl_crlf ();
      internal_error ("bash_execute_unix_command: cannot find keymap for command");
      rl_forced_update_display ();
      return 1;
    }
    }
  else
    xkmap = cmd_xmap;

  cmd = (char *)xkmap[key].function;

  if (cmd == 0)
    {
      rl_ding ();
      return 1;
    }

  rl_crlf ();   /* move to a new line */

  old_line_count = current_command_line_count;
  ts = save_token_state ();

  cmd = savestring (cmd);
  parse_and_execute (cmd, "bash_execute_unix_command", SEVAL_NOHIST);

  current_command_line_count = old_line_count;
  restore_token_state (ts);

  /* and restore the readline buffer and display after command execution. */
  rl_forced_update_display ();
  return 0;
}

static void
init_unix_command_map ()
{
  cmd_xmap = rl_make_bare_keymap ();
}

static int
isolate_sequence (string, ind, need_dquote, startp)
     char *string;
     int ind, need_dquote, *startp;
{
  register int i;
  int c, passc, delim;

  for (i = ind; string[i] && whitespace (string[i]); i++)
    ;
  /* NEED_DQUOTE means that the first non-white character *must* be `"'. */
  if (need_dquote && string[i] != '"')
    {
      builtin_error ("%s: first non-whitespace character is not `\"'", string);
      return -1;
    }

  /* We can have delimited strings even if NEED_DQUOTE == 0, like the command
     string to bind the key sequence to. */
  delim = (string[i] == '"' || string[i] == '\'') ? string[i] : 0;
    
  if (startp)
    *startp = delim ? ++i : i;

  for (passc = 0; c = string[i]; i++)
    {
      if (passc)
    {
      passc = 0;
      continue;
    }
      if (c == '\\')
    {
      passc++;
      continue;
    }
      if (c == delim)
    break;
    }

  if (delim && string[i] != delim)
    {
      builtin_error ("%s: no closing `%c'", string, delim);
      return -1;
    }

  return i;
}

int
bind_keyseq_to_unix_command (line)
     char *line;
{
  Keymap kmap;
  char *kseq, *value;
  int i, kstart;

  if (cmd_xmap == 0)
    init_unix_command_map ();

  kmap = rl_get_keymap ();

  /* We duplicate some of the work done by rl_parse_and_bind here, but
     this code only has to handle `"keyseq": ["]command["]' and can
     generate an error for anything else. */
  i = isolate_sequence (line, 0, 1, &kstart);
  if (i < 0)
    return -1;

  /* Create the key sequence string to pass to rl_generic_bind */
  kseq = substring (line, kstart, i);

  for ( ; line[i] && line[i] != ':'; i++)
    ;
  if (line[i] != ':')
    {
      builtin_error ("%s: missing colon separator", line);
      return -1;
    }

  i = isolate_sequence (line, i + 1, 0, &kstart);
  if (i < 0)
    return -1;

  /* Create the value string containing the command to execute. */
  value = substring (line, kstart, i);

  /* Save the command to execute and the key sequence in the CMD_XMAP */
  rl_generic_bind (ISMACR, kseq, value, cmd_xmap);

  /* and bind the key sequence in the current keymap to a function that
     understands how to execute from CMD_XMAP */
  rl_set_key (kseq, bash_execute_unix_command, kmap);
  
  return 0;
}

/* Used by the programmable completion code.  Complete TEXT as a filename,
   but return only directories as matches.  Dequotes the filename before
   attempting to find matches. */
char **
bash_directory_completion_matches (text)
     const char *text;
{
  char **m1;
  char *dfn;
  int qc;

  qc = (text[0] == '"' || text[0] == '\'') ? text[0] : 0;
  dfn = bash_dequote_filename ((char *)text, qc);
  m1 = rl_completion_matches (dfn, rl_filename_completion_function);
  free (dfn);

  if (m1 == 0 || m1[0] == 0)
    return m1;
  /* We don't bother recomputing the lcd of the matches, because it will just
     get thrown away by the programmable completion code and recomputed
     later. */
  (void)bash_ignore_filenames (m1);
  return m1;
}
#endif /* READLINE */

https://git.savannah.gnu.org/cgit/bash.git/tree/list.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/list.c used in: Step 1238
/* list.c - Functions for manipulating linked lists of objects. */

/* Copyright (C) 1996
   Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "shell.h"

/* A global variable which acts as a sentinel for an `error' list return. */
GENERIC_LIST global_error_list;

#ifdef INCLUDE_UNUSED
/* Call FUNCTION on every member of LIST, a generic list. */
void
list_walk (list, function)
     GENERIC_LIST *list;
     sh_glist_func_t *function;
{
  for ( ; list; list = list->next)
    if ((*function) (list) < 0)
      return;
}

/* Call FUNCTION on every string in WORDS. */
void
wlist_walk (words, function)
     WORD_LIST *words;
     sh_icpfunc_t *function;
{
  for ( ; words; words = words->next)
    if ((*function) (words->word->word) < 0)
      return;
}
#endif /* INCLUDE_UNUSED */

/* Reverse the chain of structures in LIST.  Output the new head
   of the chain.  You should always assign the output value of this
   function to something, or you will lose the chain. */
GENERIC_LIST *
list_reverse (list)
     GENERIC_LIST *list;
{
  register GENERIC_LIST *next, *prev;

  for (prev = (GENERIC_LIST *)NULL; list; )
    {
      next = list->next;
      list->next = prev;
      prev = list;
      list = next;
    }
  return (prev);
}

/* Return the number of elements in LIST, a generic list. */
int
list_length (list)
     GENERIC_LIST *list;
{
  register int i;

  for (i = 0; list; list = list->next, i++);
  return (i);
}

/* Append TAIL to HEAD.  Return the header of the list. */
GENERIC_LIST *
list_append (head, tail)
     GENERIC_LIST *head, *tail;
{
  register GENERIC_LIST *t_head;

  if (head == 0)
    return (tail);

  for (t_head = head; t_head->next; t_head = t_head->next)
    ;
  t_head->next = tail;
  return (head);
}

#ifdef INCLUDE_UNUSED
/* Delete the element of LIST which satisfies the predicate function COMPARER.
   Returns the element that was deleted, so you can dispose of it, or -1 if
   the element wasn't found.  COMPARER is called with the list element and
   then ARG.  Note that LIST contains the address of a variable which points
   to the list.  You might call this function like this:

   SHELL_VAR *elt = list_remove (&variable_list, check_var_has_name, "foo");
   dispose_variable (elt);
*/
GENERIC_LIST *
list_remove (list, comparer, arg)
     GENERIC_LIST **list;
     Function *comparer;
     char *arg;
{
  register GENERIC_LIST *prev, *temp;

  for (prev = (GENERIC_LIST *)NULL, temp = *list; temp; prev = temp, temp = temp->next)
    {
      if ((*comparer) (temp, arg))
    {
      if (prev)
        prev->next = temp->next;
      else
        *list = temp->next;
      return (temp);
    }
    }
  return ((GENERIC_LIST *)&global_error_list);
}
#endif

https://git.savannah.gnu.org/cgit/bash.git/tree/stringlib.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/stringlib.c used in: Step 1239
/* stringlib.c - Miscellaneous string functions. */

/* Copyright (C) 1996-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include "bashtypes.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "bashansi.h"
#include <stdio.h>
#include "chartypes.h"

#include "shell.h"
#include "pathexp.h"

#include <glob/glob.h>

#if defined (EXTENDED_GLOB)
#  include <glob/strmatch.h>
#endif

/* **************************************************************** */
/*                                  */
/*      Functions to manage arrays of strings           */
/*                                  */
/* **************************************************************** */

/* Find STRING in ALIST, a list of string key/int value pairs.  If FLAGS
   is 1, STRING is treated as a pattern and matched using strmatch. */
int
find_string_in_alist (string, alist, flags)
     char *string;
     STRING_INT_ALIST *alist;
     int flags;
{
  register int i;
  int r;

  for (i = r = 0; alist[i].word; i++)
    {
#if defined (EXTENDED_GLOB)
      if (flags)
    r = strmatch (alist[i].word, string, FNM_EXTMATCH) != FNM_NOMATCH;
      else
#endif
    r = STREQ (string, alist[i].word);

      if (r)
    return (alist[i].token);
    }
  return -1;
}

/* Find TOKEN in ALIST, a list of string/int value pairs.  Return the
   corresponding string.  Allocates memory for the returned
   string.  FLAGS is currently ignored, but reserved. */
char *
find_token_in_alist (token, alist, flags)
     int token;
     STRING_INT_ALIST *alist;
     int flags;
{
  register int i;

  for (i = 0; alist[i].word; i++)
    {
      if (alist[i].token == token)
        return (savestring (alist[i].word));
    }
  return ((char *)NULL);
}

int
find_index_in_alist (string, alist, flags)
     char *string;
     STRING_INT_ALIST *alist;
     int flags;
{
  register int i;
  int r;

  for (i = r = 0; alist[i].word; i++)
    {
#if defined (EXTENDED_GLOB)
      if (flags)
    r = strmatch (alist[i].word, string, FNM_EXTMATCH) != FNM_NOMATCH;
      else
#endif
    r = STREQ (string, alist[i].word);

      if (r)
    return (i);
    }

  return -1;
}

/* **************************************************************** */
/*                                  */
/*          String Management Functions             */
/*                                  */
/* **************************************************************** */

/* Cons a new string from STRING starting at START and ending at END,
   not including END. */
char *
substring (string, start, end)
     char *string;
     int start, end;
{
  register int len;
  register char *result;

  len = end - start;
  result = (char *)xmalloc (len + 1);
  strncpy (result, string + start, len);
  result[len] = '\0';
  return (result);
}

/* Replace occurrences of PAT with REP in STRING.  If GLOBAL is non-zero,
   replace all occurrences, otherwise replace only the first.
   This returns a new string; the caller should free it. */
char *
strsub (string, pat, rep, global)
     char *string, *pat, *rep;
     int global;
{
  int patlen, replen, templen, tempsize, repl, i;
  char *temp, *r;

  patlen = strlen (pat);
  replen = strlen (rep);
  for (temp = (char *)NULL, i = templen = tempsize = 0, repl = 1; string[i]; )
    {
      if (repl && STREQN (string + i, pat, patlen))
    {
      if (replen)
        RESIZE_MALLOCED_BUFFER (temp, templen, replen, tempsize, (replen * 2));

      for (r = rep; *r; )
        temp[templen++] = *r++;

      i += patlen ? patlen : 1; /* avoid infinite recursion */
      repl = global != 0;
    }
      else
    {
      RESIZE_MALLOCED_BUFFER (temp, templen, 1, tempsize, 16);
      temp[templen++] = string[i++];
    }
    }
  temp[templen] = 0;
  return (temp);
}

/* Replace all instances of C in STRING with TEXT.  TEXT may be empty or
   NULL.  If DO_GLOB is non-zero, we quote the replacement text for
   globbing.  Backslash may be used to quote C. */
char *
strcreplace (string, c, text, do_glob)
     char *string;
     int c;
     char *text;
     int do_glob;
{
  char *ret, *p, *r, *t;
  int len, rlen, ind, tlen;

  len = STRLEN (text);
  rlen = len + strlen (string) + 2;
  ret = (char *)xmalloc (rlen);

  for (p = string, r = ret; p && *p; )
    {
      if (*p == c)
    {
      if (len)
        {
          ind = r - ret;
          if (do_glob && (glob_pattern_p (text) || strchr (text, '\\')))
        {
          t = quote_globbing_chars (text);
          tlen = strlen (t);
          RESIZE_MALLOCED_BUFFER (ret, ind, tlen, rlen, rlen);
          r = ret + ind;    /* in case reallocated */
          strcpy (r, t);
          r += tlen;
          free (t);
        }
          else
        {
          RESIZE_MALLOCED_BUFFER (ret, ind, len, rlen, rlen);
          r = ret + ind;    /* in case reallocated */
          strcpy (r, text);
          r += len;
        }
        }
      p++;
      continue;
    }

      if (*p == '\\' && p[1] == c)
    p++;

      ind = r - ret;
      RESIZE_MALLOCED_BUFFER (ret, ind, 2, rlen, rlen);
      r = ret + ind;            /* in case reallocated */
      *r++ = *p++;
    }
  *r = '\0';

  return ret;
}

#ifdef INCLUDE_UNUSED
/* Remove all leading whitespace from STRING.  This includes
   newlines.  STRING should be terminated with a zero. */
void
strip_leading (string)
     char *string;
{
  char *start = string;

  while (*string && (whitespace (*string) || *string == '\n'))
    string++;

  if (string != start)
    {
      int len = strlen (string);
      FASTCOPY (string, start, len);
      start[len] = '\0';
    }
}
#endif

/* Remove all trailing whitespace from STRING.  This includes
   newlines.  If NEWLINES_ONLY is non-zero, only trailing newlines
   are removed.  STRING should be terminated with a zero. */
void
strip_trailing (string, len, newlines_only)
     char *string;
     int len;
     int newlines_only;
{
  while (len >= 0)
    {
      if ((newlines_only && string[len] == '\n') ||
      (!newlines_only && whitespace (string[len])))
    len--;
      else
    break;
    }
  string[len + 1] = '\0';
}

/* A wrapper for bcopy that can be prototyped in general.h */
void
xbcopy (s, d, n)
     char *s, *d;
     int n;
{
  FASTCOPY (s, d, n);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/findcmd.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/findcmd.c used in: Step 1241
/* findcmd.c -- Functions to search for commands by name. */

/* Copyright (C) 1997 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the
   Free Software Foundation Inc.,
   59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */

#include "config.h"

#include <stdio.h>
#include "chartypes.h"
#include "bashtypes.h"
#ifndef _MINIX
#  include <sys/file.h>
#endif
#include "filecntl.h"
#include "posixstat.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include "bashansi.h"

#include "memalloc.h"
#include "shell.h"
#include "flags.h"
#include "hashlib.h"
#include "pathexp.h"
#include "hashcmd.h"
#include "findcmd.h"    /* matching prototypes and declarations */

extern int posixly_correct;

/* Static functions defined and used in this file. */
static char *_find_user_command_internal __P((const char *, int));
static char *find_user_command_internal __P((const char *, int));
static char *find_user_command_in_path __P((const char *, char *, int));
static char *find_in_path_element __P((const char *, char *, int, int, struct stat *));
static char *find_absolute_program __P((const char *, int));

static char *get_next_path_element __P((char *, int *));

/* The file name which we would try to execute, except that it isn't
   possible to execute it.  This is the first file that matches the
   name that we are looking for while we are searching $PATH for a
   suitable one to execute.  If we cannot find a suitable executable
   file, then we use this one. */
static char *file_to_lose_on;

/* Non-zero if we should stat every command found in the hash table to
   make sure it still exists. */
int check_hashed_filenames;

/* DOT_FOUND_IN_SEARCH becomes non-zero when find_user_command ()
   encounters a `.' as the directory pathname while scanning the
   list of possible pathnames; i.e., if `.' comes before the directory
   containing the file of interest. */
int dot_found_in_search = 0;

#define u_mode_bits(x) (((x) & 0000700) >> 6)
#define g_mode_bits(x) (((x) & 0000070) >> 3)
#define o_mode_bits(x) (((x) & 0000007) >> 0)
#define X_BIT(x) ((x) & 1)

/* Return some flags based on information about this file.
   The EXISTS bit is non-zero if the file is found.
   The EXECABLE bit is non-zero the file is executble.
   Zero is returned if the file is not found. */
int
file_status (name)
     const char *name;
{
  struct stat finfo;

  /* Determine whether this file exists or not. */
  if (stat (name, &finfo) < 0)
    return (0);

  /* If the file is a directory, then it is not "executable" in the
     sense of the shell. */
  if (S_ISDIR (finfo.st_mode))
    return (FS_EXISTS|FS_DIRECTORY);

#if defined (AFS)
  /* We have to use access(2) to determine access because AFS does not
     support Unix file system semantics.  This may produce wrong
     answers for non-AFS files when ruid != euid.  I hate AFS. */
  if (access (name, X_OK) == 0)
    return (FS_EXISTS | FS_EXECABLE);
  else
    return (FS_EXISTS);
#else /* !AFS */

  /* Find out if the file is actually executable.  By definition, the
     only other criteria is that the file has an execute bit set that
     we can use. */

  /* Root only requires execute permission for any of owner, group or
     others to be able to exec a file. */
  if (current_user.euid == (uid_t)0)
    {
      int bits;

      bits = (u_mode_bits (finfo.st_mode) |
          g_mode_bits (finfo.st_mode) |
          o_mode_bits (finfo.st_mode));

      if (X_BIT (bits))
    return (FS_EXISTS | FS_EXECABLE);
    }

  /* If we are the owner of the file, the owner execute bit applies. */
  if (current_user.euid == finfo.st_uid && X_BIT (u_mode_bits (finfo.st_mode)))
    return (FS_EXISTS | FS_EXECABLE);

  /* If we are in the owning group, the group permissions apply. */
  if (group_member (finfo.st_gid) && X_BIT (g_mode_bits (finfo.st_mode)))
    return (FS_EXISTS | FS_EXECABLE);

  /* If `others' have execute permission to the file, then so do we,
     since we are also `others'. */
  if (X_BIT (o_mode_bits (finfo.st_mode)))
    return (FS_EXISTS | FS_EXECABLE);

  return (FS_EXISTS);
#endif /* !AFS */
}

/* Return non-zero if FILE exists and is executable.
   Note that this function is the definition of what an
   executable file is; do not change this unless YOU know
   what an executable file is. */
int
executable_file (file)
     const char *file;
{
  int s;

  s = file_status (file);
  return ((s & FS_EXECABLE) && ((s & FS_DIRECTORY) == 0));
}

int
is_directory (file)
     const char *file;
{
  return (file_status (file) & FS_DIRECTORY);
}

int
executable_or_directory (file)
     const char *file;
{
  int s;

  s = file_status (file);
  return ((s & FS_EXECABLE) || (s & FS_DIRECTORY));
}

/* Locate the executable file referenced by NAME, searching along
   the contents of the shell PATH variable.  Return a new string
   which is the full pathname to the file, or NULL if the file
   couldn't be found.  If a file is found that isn't executable,
   and that is the only match, then return that. */
char *
find_user_command (name)
     const char *name;
{
  return (find_user_command_internal (name, FS_EXEC_PREFERRED|FS_NODIRS));
}

/* Locate the file referenced by NAME, searching along the contents
   of the shell PATH variable.  Return a new string which is the full
   pathname to the file, or NULL if the file couldn't be found.  This
   returns the first file found. */
char *
find_path_file (name)
     const char *name;
{
  return (find_user_command_internal (name, FS_EXISTS));
}

static char *
_find_user_command_internal (name, flags)
     const char *name;
     int flags;
{
  char *path_list, *cmd;
  SHELL_VAR *var;

  /* Search for the value of PATH in both the temporary environments and
     in the regular list of variables. */
  if (var = find_variable_internal ("PATH", 1)) /* XXX could be array? */
    path_list = value_cell (var);
  else
    path_list = (char *)NULL;

  if (path_list == 0 || *path_list == '\0')
    return (savestring (name));

  cmd = find_user_command_in_path (name, path_list, flags);

  return (cmd);
}

static char *
find_user_command_internal (name, flags)
     const char *name;
     int flags;
{
#ifdef __WIN32__
  char *res, *dotexe;

  dotexe = (char *)xmalloc (strlen (name) + 5);
  strcpy (dotexe, name);
  strcat (dotexe, ".exe");
  res = _find_user_command_internal (dotexe, flags);
  free (dotexe);
  if (res == 0)
    res = _find_user_command_internal (name, flags);
  return res;
#else
  return (_find_user_command_internal (name, flags));
#endif
}

/* Return the next element from PATH_LIST, a colon separated list of
   paths.  PATH_INDEX_POINTER is the address of an index into PATH_LIST;
   the index is modified by this function.
   Return the next element of PATH_LIST or NULL if there are no more. */
static char *
get_next_path_element (path_list, path_index_pointer)
     char *path_list;
     int *path_index_pointer;
{
  char *path;

  path = extract_colon_unit (path_list, path_index_pointer);

  if (path == 0)
    return (path);

  if (*path == '\0')
    {
      free (path);
      path = savestring (".");
    }

  return (path);
}

/* Look for PATHNAME in $PATH.  Returns either the hashed command
   corresponding to PATHNAME or the first instance of PATHNAME found
   in $PATH.  Returns a newly-allocated string. */
char *
search_for_command (pathname)
     const char *pathname;
{
  char *hashed_file, *command;
  int temp_path, st;
  SHELL_VAR *path;

  hashed_file = command = (char *)NULL;

  /* If PATH is in the temporary environment for this command, don't use the
     hash table to search for the full pathname. */
  path = find_variable_internal ("PATH", 1);
  temp_path = path && tempvar_p (path);
  if (temp_path == 0 && path)
    path = (SHELL_VAR *)NULL;

  /* Don't waste time trying to find hashed data for a pathname
     that is already completely specified or if we're using a command-
     specific value for PATH. */
  if (path == 0 && absolute_program (pathname) == 0)
    hashed_file = phash_search (pathname);

  /* If a command found in the hash table no longer exists, we need to
     look for it in $PATH.  Thank you Posix.2.  This forces us to stat
     every command found in the hash table. */

  if (hashed_file && (posixly_correct || check_hashed_filenames))
    {
      st = file_status (hashed_file);
      if ((st ^ (FS_EXISTS | FS_EXECABLE)) != 0)
    {
      phash_remove (pathname);
      free (hashed_file);
      hashed_file = (char *)NULL;
    }
    }

  if (hashed_file)
    command = hashed_file;
  else if (absolute_program (pathname))
    /* A command containing a slash is not looked up in PATH or saved in
       the hash table. */
    command = savestring (pathname);
  else
    {
      /* If $PATH is in the temporary environment, we've already retrieved
     it, so don't bother trying again. */
      if (temp_path)
    {
      command = find_user_command_in_path (pathname, value_cell (path),
                           FS_EXEC_PREFERRED|FS_NODIRS);
    }
      else
    command = find_user_command (pathname);
      if (command && hashing_enabled && temp_path == 0)
    phash_insert ((char *)pathname, command, dot_found_in_search, 1);   /* XXX fix const later */
    }
  return (command);
}

char *
user_command_matches (name, flags, state)
     const char *name;
     int flags, state;
{
  register int i;
  int  path_index, name_len;
  char *path_list, *path_element, *match;
  struct stat dotinfo;
  static char **match_list = NULL;
  static int match_list_size = 0;
  static int match_index = 0;

  if (state == 0)
    {
      /* Create the list of matches. */
      if (match_list == 0)
    {
      match_list_size = 5;
      match_list = strvec_create (match_list_size);
    }

      /* Clear out the old match list. */
      for (i = 0; i < match_list_size; i++)
    match_list[i] = 0;

      /* We haven't found any files yet. */
      match_index = 0;

      if (absolute_program (name))
    {
      match_list[0] = find_absolute_program (name, flags);
      match_list[1] = (char *)NULL;
      path_list = (char *)NULL;
    }
      else
    {
      name_len = strlen (name);
      file_to_lose_on = (char *)NULL;
      dot_found_in_search = 0;
          stat (".", &dotinfo);
      path_list = get_string_value ("PATH");
          path_index = 0;
    }

      while (path_list && path_list[path_index])
    {
      path_element = get_next_path_element (path_list, &path_index);

      if (path_element == 0)
        break;

      match = find_in_path_element (name, path_element, flags, name_len, &dotinfo);

      free (path_element);

      if (match == 0)
        continue;

      if (match_index + 1 == match_list_size)
        {
          match_list_size += 10;
          match_list = strvec_resize (match_list, (match_list_size + 1));
        }

      match_list[match_index++] = match;
      match_list[match_index] = (char *)NULL;
      FREE (file_to_lose_on);
      file_to_lose_on = (char *)NULL;
    }

      /* We haven't returned any strings yet. */
      match_index = 0;
    }

  match = match_list[match_index];

  if (match)
    match_index++;

  return (match);
}

static char *
find_absolute_program (name, flags)
     const char *name;
     int flags;
{
  int st;

  st = file_status (name);

  /* If the file doesn't exist, quit now. */
  if ((st & FS_EXISTS) == 0)
    return ((char *)NULL);

  /* If we only care about whether the file exists or not, return
     this filename.  Otherwise, maybe we care about whether this
     file is executable.  If it is, and that is what we want, return it. */
  if ((flags & FS_EXISTS) || ((flags & FS_EXEC_ONLY) && (st & FS_EXECABLE)))
    return (savestring (name));

  return (NULL);
}

static char *
find_in_path_element (name, path, flags, name_len, dotinfop)
     const char *name;
     char *path;
     int flags, name_len;
     struct stat *dotinfop;
{
  int status;
  char *full_path, *xpath;

  xpath = (*path == '~') ? bash_tilde_expand (path, 0) : path;

  /* Remember the location of "." in the path, in all its forms
     (as long as they begin with a `.', e.g. `./.') */
  if (dot_found_in_search == 0 && *xpath == '.')
    dot_found_in_search = same_file (".", xpath, dotinfop, (struct stat *)NULL);

  full_path = sh_makepath (xpath, name, 0);

  status = file_status (full_path);

  if (xpath != path)
    free (xpath);

  if ((status & FS_EXISTS) == 0)
    {
      free (full_path);
      return ((char *)NULL);
    }

  /* The file exists.  If the caller simply wants the first file, here it is. */
  if (flags & FS_EXISTS)
    return (full_path);

  /* If the file is executable, then it satisfies the cases of
      EXEC_ONLY and EXEC_PREFERRED.  Return this file unconditionally. */
  if ((status & FS_EXECABLE) &&
      (((flags & FS_NODIRS) == 0) || ((status & FS_DIRECTORY) == 0)))
    {
      FREE (file_to_lose_on);
      file_to_lose_on = (char *)NULL;
      return (full_path);
    }

  /* The file is not executable, but it does exist.  If we prefer
     an executable, then remember this one if it is the first one
     we have found. */
  if ((flags & FS_EXEC_PREFERRED) && file_to_lose_on == 0)
    file_to_lose_on = savestring (full_path);

  /* If we want only executable files, or we don't want directories and
     this file is a directory, fail. */
  if ((flags & FS_EXEC_ONLY) || (flags & FS_EXEC_PREFERRED) ||
      ((flags & FS_NODIRS) && (status & FS_DIRECTORY)))
    {
      free (full_path);
      return ((char *)NULL);
    }
  else
    return (full_path);
}

/* This does the dirty work for find_user_command_internal () and
   user_command_matches ().
   NAME is the name of the file to search for.
   PATH_LIST is a colon separated list of directories to search.
   FLAGS contains bit fields which control the files which are eligible.
   Some values are:
      FS_EXEC_ONLY:     The file must be an executable to be found.
      FS_EXEC_PREFERRED:    If we can't find an executable, then the
                the first file matching NAME will do.
      FS_EXISTS:        The first file found will do.
      FS_NODIRS:        Don't find any directories.
*/
static char *
find_user_command_in_path (name, path_list, flags)
     const char *name;
     char *path_list;
     int flags;
{
  char *full_path, *path;
  int path_index, name_len;
  struct stat dotinfo;

  /* We haven't started looking, so we certainly haven't seen
     a `.' as the directory path yet. */
  dot_found_in_search = 0;

  if (absolute_program (name))
    {
      full_path = find_absolute_program (name, flags);
      return (full_path);
    }

  if (path_list == 0 || *path_list == '\0')
    return (savestring (name));     /* XXX */

  file_to_lose_on = (char *)NULL;
  name_len = strlen (name);
  stat (".", &dotinfo);
  path_index = 0;

  while (path_list[path_index])
    {
      /* Allow the user to interrupt out of a lengthy path search. */
      QUIT;

      path = get_next_path_element (path_list, &path_index);
      if (path == 0)
    break;

      /* Side effects: sets dot_found_in_search, possibly sets
     file_to_lose_on. */
      full_path = find_in_path_element (name, path, flags, name_len, &dotinfo);
      free (path);

      /* This should really be in find_in_path_element, but there isn't the
     right combination of flags. */
      if (full_path && is_directory (full_path))
    {
      free (full_path);
      continue;
    }

      if (full_path)
    {
      FREE (file_to_lose_on);
      return (full_path);
    }
    }

  /* We didn't find exactly what the user was looking for.  Return
     the contents of FILE_TO_LOSE_ON which is NULL when the search
     required an executable, or non-NULL if a file was found and the
     search would accept a non-executable as a last resort.  If the
     caller specified FS_NODIRS, and file_to_lose_on is a directory,
     return NULL. */
  if (file_to_lose_on && (flags & FS_NODIRS) && is_directory (file_to_lose_on))
    {
      free (file_to_lose_on);
      file_to_lose_on = (char *)NULL;
    }

  return (file_to_lose_on);
}

https://git.savannah.gnu.org/cgit/bash.git/tree/redir.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/redir.c used in: Step 1242
/* redir.c -- Functions to perform input and output redirection. */

/* Copyright (C) 1997-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
#include "config.h"

#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
  #pragma alloca
#endif /* _AIX && RISC6000 && !__GNUC__ */

#include <stdio.h>
#include "bashtypes.h"
#ifndef _MINIX
#  include <sys/file.h>
#endif
#include "filecntl.h"
#include "posixstat.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <errno.h>

#if !defined (errno)
extern int errno;
#endif

#include "bashansi.h"

#include "memalloc.h"
#include "shell.h"
#include "flags.h"
#include "execute_cmd.h"
#include "redir.h"

#if defined (BUFFERED_INPUT)
#  include "input.h"
#endif

extern int posixly_correct;
extern REDIRECT *redirection_undo_list;
extern REDIRECT *exec_redirection_undo_list;

/* Static functions defined and used in this file. */
static void add_undo_close_redirect __P((int));
static void add_exec_redirect __P((REDIRECT *));
static int add_undo_redirect __P((int));
static int expandable_redirection_filename __P((REDIRECT *));
static int stdin_redirection __P((enum r_instruction, int));
static int do_redirection_internal __P((REDIRECT *, int, int, int));

static int write_here_document __P((int, WORD_DESC *));
static int write_here_string __P((int, WORD_DESC *));
static int here_document_to_fd __P((WORD_DESC *, enum r_instruction));

static int redir_special_open __P((int, char *, int, int, enum r_instruction));
static int noclobber_open __P((char *, int, int, enum r_instruction));
static int redir_open __P((char *, int, int, enum r_instruction));

/* Spare redirector used when translating [N]>&WORD[-] or [N]<&WORD[-] to
   a new redirection and when creating the redirection undo list. */
static REDIRECTEE rd;

/* Set to errno when a here document cannot be created for some reason.
   Used to print a reasonable error message. */
static int heredoc_errno;

void
redirection_error (temp, error)
     REDIRECT *temp;
     int error;
{
  char *filename, *allocname;
  int oflags;

  allocname = 0;
  if (temp->redirector < 0)
    /* This can happen when read_token_word encounters overflow, like in
       exec 4294967297>x */
    filename = "file descriptor out of range";
#ifdef EBADF
  else if (temp->redirector >= 0 && errno == EBADF)
    {
      /* If we're dealing with two file descriptors, we have to guess about
         which one is invalid; in the cases of r_{duplicating,move}_input and
         r_{duplicating,move}_output we're here because dup2() failed. */
      switch (temp->instruction)
        {
        case r_duplicating_input:
        case r_duplicating_output:
        case r_move_input:
        case r_move_output:
      filename = allocname = itos (temp->redirectee.dest);
      break;
    default:
      filename = allocname = itos (temp->redirector);
      break;
        }
    }
#endif
  else if (expandable_redirection_filename (temp))
    {
      if (posixly_correct && interactive_shell == 0)
    {
      oflags = temp->redirectee.filename->flags;
      temp->redirectee.filename->flags |= W_NOGLOB;
    }
      filename = allocname = redirection_expand (temp->redirectee.filename);
      if (posixly_correct && interactive_shell == 0)
    temp->redirectee.filename->flags = oflags;
      if (filename == 0)
    filename = temp->redirectee.filename->word;
    }
  else if (temp->redirectee.dest < 0)
    filename = "file descriptor out of range";
  else
    filename = allocname = itos (temp->redirectee.dest);

  switch (error)
    {
    case AMBIGUOUS_REDIRECT:
      internal_error ("%s: ambiguous redirect", filename);
      break;

    case NOCLOBBER_REDIRECT:
      internal_error ("%s: cannot overwrite existing file", filename);
      break;

#if defined (RESTRICTED_SHELL)
    case RESTRICTED_REDIRECT:
      internal_error ("%s: restricted: cannot redirect output", filename);
      break;
#endif /* RESTRICTED_SHELL */

    case HEREDOC_REDIRECT:
      internal_error ("cannot create temp file for here document: %s", strerror (heredoc_errno));
      break;

    default:
      internal_error ("%s: %s", filename, strerror (error));
      break;
    }

  FREE (allocname);
}

/* Perform the redirections on LIST.  If FOR_REAL, then actually make
   input and output file descriptors, otherwise just do whatever is
   neccessary for side effecting.  INTERNAL says to remember how to
   undo the redirections later, if non-zero.  If SET_CLEXEC is non-zero,
   file descriptors opened in do_redirection () have their close-on-exec
   flag set. */
int
do_redirections (list, for_real, internal, set_clexec)
     REDIRECT *list;
     int for_real, internal, set_clexec;
{
  int error;
  REDIRECT *temp;

  if (internal)
    {
      if (redirection_undo_list)
    {
      dispose_redirects (redirection_undo_list);
      redirection_undo_list = (REDIRECT *)NULL;
    }
      if (exec_redirection_undo_list)
    dispose_exec_redirects ();
    }

  for (temp = list; temp; temp = temp->next)
    {
      error = do_redirection_internal (temp, for_real, internal, set_clexec);
      if (error)
    {
      redirection_error (temp, error);
      return (error);
    }
    }
  return (0);
}

/* Return non-zero if the redirection pointed to by REDIRECT has a
   redirectee.filename that can be expanded. */
static int
expandable_redirection_filename (redirect)
     REDIRECT *redirect;
{
  switch (redirect->instruction)
    {
    case r_output_direction:
    case r_appending_to:
    case r_input_direction:
    case r_inputa_direction:
    case r_err_and_out:
    case r_input_output:
    case r_output_force:
    case r_duplicating_input_word:
    case r_duplicating_output_word:
    case r_move_input_word:
    case r_move_output_word:
      return 1;

    default:
      return 0;
    }
}

/* Expand the word in WORD returning a string.  If WORD expands to
   multiple words (or no words), then return NULL. */
char *
redirection_expand (word)
     WORD_DESC *word;
{
  char *result;
  WORD_LIST *tlist1, *tlist2;
  WORD_DESC *w;

  w = copy_word (word);
  if (posixly_correct)
    w->flags |= W_NOSPLIT;

  tlist1 = make_word_list (w, (WORD_LIST *)NULL);
  tlist2 = expand_words_no_vars (tlist1);
  dispose_words (tlist1);

  if (!tlist2 || tlist2->next)
    {
      /* We expanded to no words, or to more than a single word.
     Dispose of the word list and return NULL. */
      if (tlist2)
    dispose_words (tlist2);
      return ((char *)NULL);
    }
  result = string_list (tlist2);  /* XXX savestring (tlist2->word->word)? */
  dispose_words (tlist2);
  return (result);
}

static int
write_here_string (fd, redirectee)
     int fd;
     WORD_DESC *redirectee;
{
  char *herestr;
  int herelen, n, e;

  herestr = expand_string_to_string (redirectee->word, 0);
  herelen = strlen (herestr);

  n = write (fd, herestr, herelen);
  if (n == herelen)
    {
      n = write (fd, "\n", 1);
      herelen = 1;
    }
  e = errno;
  free (herestr);
  if (n != herelen)
    {
      if (e == 0)
    e = ENOSPC;
      return e;
    }
  return 0;
}  

/* Write the text of the here document pointed to by REDIRECTEE to the file
   descriptor FD, which is already open to a temp file.  Return 0 if the
   write is successful, otherwise return errno. */
static int
write_here_document (fd, redirectee)
     int fd;
     WORD_DESC *redirectee;
{
  char *document;
  int document_len, fd2;
  FILE *fp;
  register WORD_LIST *t, *tlist;

  /* Expand the text if the word that was specified had
     no quoting.  The text that we expand is treated
     exactly as if it were surrounded by double quotes. */

  if (redirectee->flags & W_QUOTED)
    {
      document = redirectee->word;
      document_len = strlen (document);
      /* Set errno to something reasonable if the write fails. */
      if (write (fd, document, document_len) < document_len)
    {
      if (errno == 0)
        errno = ENOSPC;
      return (errno);
    }
      else
    return 0;
    }

  tlist = expand_string (redirectee->word, Q_HERE_DOCUMENT);
  if (tlist)
    {
      /* Try using buffered I/O (stdio) and writing a word
     at a time, letting stdio do the work of buffering
     for us rather than managing our own strings.  Most
     stdios are not particularly fast, however -- this
     may need to be reconsidered later. */
      if ((fd2 = dup (fd)) < 0 || (fp = fdopen (fd2, "w")) == NULL)
    {
      if (fd2 >= 0)
        close (fd2);
      return (errno);
    }
      errno = 0;
      for (t = tlist; t; t = t->next)
    {
      /* This is essentially the body of
         string_list_internal expanded inline. */
      document = t->word->word;
      document_len = strlen (document);
      if (t != tlist)
        putc (' ', fp); /* separator */
      fwrite (document, document_len, 1, fp);
      if (ferror (fp))
        {
          if (errno == 0)
        errno = ENOSPC;
          fd2 = errno;
          fclose(fp);
          dispose_words (tlist);
          return (fd2);
        }
    }
      dispose_words (tlist);
      if (fclose (fp) != 0)
    {
      if (errno == 0)
        errno = ENOSPC;
      return (errno);
    }
    }
  return 0;
}

/* Create a temporary file holding the text of the here document pointed to
   by REDIRECTEE, and return a file descriptor open for reading to the temp
   file.  Return -1 on any error, and make sure errno is set appropriately. */
static int
here_document_to_fd (redirectee, ri)
     WORD_DESC *redirectee;
     enum r_instruction ri;
{
  char *filename;
  int r, fd, fd2;

  fd = sh_mktmpfd ("sh-thd", MT_USERANDOM, &filename);

  /* If we failed for some reason other than the file existing, abort */
  if (fd < 0)
    {
      FREE (filename);
      return (fd);
    }

  errno = r = 0;        /* XXX */
  /* write_here_document returns 0 on success, errno on failure. */
  if (redirectee->word)
    r = (ri != r_reading_string) ? write_here_document (fd, redirectee)
                 : write_here_string (fd, redirectee);

  if (r)
    {
      close (fd);
      unlink (filename);
      free (filename);
      errno = r;
      return (-1);
    }

  /* In an attempt to avoid races, we close the first fd only after opening
     the second. */
  /* Make the document really temporary.  Also make it the input. */
  fd2 = open (filename, O_RDONLY, 0600);

  if (fd2 < 0)
    {
      r = errno;
      unlink (filename);
      free (filename);
      close (fd);
      errno = r;
      return -1;
    }

  close (fd);
  if (unlink (filename) < 0)
    {
      r = errno;
#if defined (__CYGWIN__)
      /* Under CygWin 1.1.0, the unlink will fail if the file is
     open. This hack will allow the previous action of silently
     ignoring the error, but will still leave the file there. This
     needs some kind of magic. */
      if (r == EACCES)
    return (fd2);
#endif /* __CYGWIN__ */
      close (fd2);
      free (filename);
      errno = r;
      return (-1);
    }

  free (filename);
  return (fd2);
}

#define RF_DEVFD    1
#define RF_DEVSTDERR    2
#define RF_DEVSTDIN 3
#define RF_DEVSTDOUT    4
#define RF_DEVTCP   5
#define RF_DEVUDP   6

/* A list of pattern/value pairs for filenames that the redirection
   code handles specially. */
static STRING_INT_ALIST _redir_special_filenames[] = {
#if !defined (HAVE_DEV_FD)
  { "/dev/fd/[0-9]*", RF_DEVFD },
#endif
#if !defined (HAVE_DEV_STDIN)
  { "/dev/stderr", RF_DEVSTDERR },
  { "/dev/stdin", RF_DEVSTDIN },
  { "/dev/stdout", RF_DEVSTDOUT },
#endif
#if defined (NETWORK_REDIRECTIONS)
  { "/dev/tcp/*/*", RF_DEVTCP },
  { "/dev/udp/*/*", RF_DEVUDP },
#endif
  { (char *)NULL, -1 }
};

static int
redir_special_open (spec, filename, flags, mode, ri)
     int spec;
     char *filename;
     int flags, mode;
     enum r_instruction ri;
{
  int fd;
#if !defined (HAVE_DEV_FD)
  intmax_t lfd;
#endif

  fd = -1;
  switch (spec)
    {
#if !defined (HAVE_DEV_FD)
    case RF_DEVFD:
      if (all_digits (filename+8) && legal_number (filename+8, &lfd) && lfd == (int)lfd)
    {
      fd = lfd;
      fd = fcntl (fd, F_DUPFD, 10);
    }
      else
    fd = AMBIGUOUS_REDIRECT;
      break;
#endif

#if !defined (HAVE_DEV_STDIN)
    case RF_DEVSTDIN:
      fd = fcntl (0, F_DUPFD, 10);
      break;
    case RF_DEVSTDOUT:
      fd = fcntl (1, F_DUPFD, 10);
      break;
    case RF_DEVSTDERR:
      fd = fcntl (2, F_DUPFD, 10);
      break;
#endif

#if defined (NETWORK_REDIRECTIONS)
    case RF_DEVTCP:
    case RF_DEVUDP:
#if defined (HAVE_NETWORK)
      fd = netopen (filename);
#else
      internal_warning ("/dev/(tcp|udp)/host/port not supported without networking");
      fd = open (filename, flags, mode);
#endif
      break;
#endif /* NETWORK_REDIRECTIONS */
    }

  return fd;
}
      
/* Open FILENAME with FLAGS in noclobber mode, hopefully avoiding most
   race conditions and avoiding the problem where the file is replaced
   between the stat(2) and open(2). */
static int
noclobber_open (filename, flags, mode, ri)
     char *filename;
     int flags, mode;
     enum r_instruction ri;
{
  int r, fd;
  struct stat finfo, finfo2;

  /* If the file exists and is a regular file, return an error
     immediately. */
  r = stat (filename, &finfo);
  if (r == 0 && (S_ISREG (finfo.st_mode)))
    return (NOCLOBBER_REDIRECT);

  /* If the file was not present (r != 0), make sure we open it
     exclusively so that if it is created before we open it, our open
     will fail.  Make sure that we do not truncate an existing file.
     Note that we don't turn on O_EXCL unless the stat failed -- if
     the file was not a regular file, we leave O_EXCL off. */
  flags &= ~O_TRUNC;
  if (r != 0)
    {
      fd = open (filename, flags|O_EXCL, mode);
      return ((fd < 0 && errno == EEXIST) ? NOCLOBBER_REDIRECT : fd);
    }
  fd = open (filename, flags, mode);

  /* If the open failed, return the file descriptor right away. */
  if (fd < 0)
    return (errno == EEXIST ? NOCLOBBER_REDIRECT : fd);

  /* OK, the open succeeded, but the file may have been changed from a
     non-regular file to a regular file between the stat and the open.
     We are assuming that the O_EXCL open handles the case where FILENAME
     did not exist and is symlinked to an existing file between the stat
     and open. */

  /* If we can open it and fstat the file descriptor, and neither check
     revealed that it was a regular file, and the file has not been replaced,
     return the file descriptor. */
  if ((fstat (fd, &finfo2) == 0) && (S_ISREG (finfo2.st_mode) == 0) &&
      r == 0 && (S_ISREG (finfo.st_mode) == 0) &&
      same_file (filename, filename, &finfo, &finfo2))
    return fd;

  /* The file has been replaced.  badness. */
  close (fd);  
  errno = EEXIST;
  return (NOCLOBBER_REDIRECT);
}

static int
redir_open (filename, flags, mode, ri)
     char *filename;
     int flags, mode;
     enum r_instruction ri;
{
  int fd, r;

  r = find_string_in_alist (filename, _redir_special_filenames, 1);
  if (r >= 0)
    return (redir_special_open (r, filename, flags, mode, ri));

  /* If we are in noclobber mode, you are not allowed to overwrite
     existing files.  Check before opening. */
  if (noclobber && CLOBBERING_REDIRECT (ri))
    {
      fd = noclobber_open (filename, flags, mode, ri);
      if (fd == NOCLOBBER_REDIRECT)
    return (NOCLOBBER_REDIRECT);
    }
  else
    {
      fd = open (filename, flags, mode);
#if defined (AFS)
      if ((fd < 0) && (errno == EACCES))
    fd = open (filename, flags & ~O_CREAT, mode);
#endif /* AFS */
    }

  return fd;
}

/* Do the specific redirection requested.  Returns errno or one of the
   special redirection errors (*_REDIRECT) in case of error, 0 on success.
   If FOR_REAL is zero, then just do whatever is neccessary to produce the
   appropriate side effects.   REMEMBERING, if non-zero, says to remember
   how to undo each redirection.  If SET_CLEXEC is non-zero, then
   we set all file descriptors > 2 that we open to be close-on-exec.  */
static int
do_redirection_internal (redirect, for_real, remembering, set_clexec)
     REDIRECT *redirect;
     int for_real, remembering, set_clexec;
{
  WORD_DESC *redirectee;
  int redir_fd, fd, redirector, r, oflags;
  intmax_t lfd;
  char *redirectee_word;
  enum r_instruction ri;
  REDIRECT *new_redirect;

  redirectee = redirect->redirectee.filename;
  redir_fd = redirect->redirectee.dest;
  redirector = redirect->redirector;
  ri = redirect->instruction;

  if (TRANSLATE_REDIRECT (ri))
    {
      /* We have [N]>&WORD[-] or [N]<&WORD[-].  Expand WORD, then translate
     the redirection into a new one and continue. */
      redirectee_word = redirection_expand (redirectee);

      /* XXX - what to do with [N]<&$w- where w is unset or null?  ksh93
           closes N. */
      if (redirectee_word == 0)
    return (AMBIGUOUS_REDIRECT);
      else if (redirectee_word[0] == '-' && redirectee_word[1] == '\0')
    {
      rd.dest = 0;
      new_redirect = make_redirection (redirector, r_close_this, rd);
    }
      else if (all_digits (redirectee_word))
    {
      if (legal_number (redirectee_word, &lfd) && (int)lfd == lfd)
        rd.dest = lfd;
      else
        rd.dest = -1;   /* XXX */
      switch (ri)
        {
        case r_duplicating_input_word:
          new_redirect = make_redirection (redirector, r_duplicating_input, rd);
          break;
        case r_duplicating_output_word:
          new_redirect = make_redirection (redirector, r_duplicating_output, rd);
          break;
        case r_move_input_word:
          new_redirect = make_redirection (redirector, r_move_input, rd);
          break;
        case r_move_output_word:
          new_redirect = make_redirection (redirector, r_move_output, rd);
          break;
        }
    }
      else if (ri == r_duplicating_output_word && redirector == 1)
    {
      rd.filename = make_bare_word (redirectee_word);
      new_redirect = make_redirection (1, r_err_and_out, rd);
    }
      else
    {
      free (redirectee_word);
      return (AMBIGUOUS_REDIRECT);
    }

      free (redirectee_word);

      /* Set up the variables needed by the rest of the function from the
     new redirection. */
      if (new_redirect->instruction == r_err_and_out)
    {
      char *alloca_hack;

      /* Copy the word without allocating any memory that must be
         explicitly freed. */
      redirectee = (WORD_DESC *)alloca (sizeof (WORD_DESC));
      xbcopy ((char *)new_redirect->redirectee.filename,
         (char *)redirectee, sizeof (WORD_DESC));

      alloca_hack = (char *)
        alloca (1 + strlen (new_redirect->redirectee.filename->word));
      redirectee->word = alloca_hack;
      strcpy (redirectee->word, new_redirect->redirectee.filename->word);
    }
      else
    /* It's guaranteed to be an integer, and shouldn't be freed. */
    redirectee = new_redirect->redirectee.filename;

      redir_fd = new_redirect->redirectee.dest;
      redirector = new_redirect->redirector;
      ri = new_redirect->instruction;

      /* Overwrite the flags element of the old redirect with the new value. */
      redirect->flags = new_redirect->flags;
      dispose_redirects (new_redirect);
    }

  switch (ri)
    {
    case r_output_direction:
    case r_appending_to:
    case r_input_direction:
    case r_inputa_direction:
    case r_err_and_out:     /* command &>filename */
    case r_input_output:
    case r_output_force:
      if (posixly_correct && interactive_shell == 0)
    {
      oflags = redirectee->flags;
      redirectee->flags |= W_NOGLOB;
    }
      redirectee_word = redirection_expand (redirectee);
      if (posixly_correct && interactive_shell == 0)
    redirectee->flags = oflags;

      if (redirectee_word == 0)
    return (AMBIGUOUS_REDIRECT);

#if defined (RESTRICTED_SHELL)
      if (restricted && (WRITE_REDIRECT (ri)))
    {
      free (redirectee_word);
      return (RESTRICTED_REDIRECT);
    }
#endif /* RESTRICTED_SHELL */

      fd = redir_open (redirectee_word, redirect->flags, 0666, ri);
      free (redirectee_word);

      if (fd == NOCLOBBER_REDIRECT)
    return (fd);

      if (fd < 0)
    return (errno);

      if (for_real)
    {
      if (remembering)
        {
          /* Only setup to undo it if the thing to undo is active. */
          if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1))
        add_undo_redirect (redirector);
          else
        add_undo_close_redirect (redirector);
        }

#if defined (BUFFERED_INPUT)
      check_bash_input (redirector);
#endif

      if ((fd != redirector) && (dup2 (fd, redirector) < 0))
        return (errno);

#if defined (BUFFERED_INPUT)
      /* Do not change the buffered stream for an implicit redirection
         of /dev/null to fd 0 for asynchronous commands without job
         control (r_inputa_direction). */
      if (ri == r_input_direction || ri == r_input_output)
        duplicate_buffered_stream (fd, redirector);
#endif /* BUFFERED_INPUT */

      /*
       * If we're remembering, then this is the result of a while, for
       * or until loop with a loop redirection, or a function/builtin
       * executing in the parent shell with a redirection.  In the
       * function/builtin case, we want to set all file descriptors > 2
       * to be close-on-exec to duplicate the effect of the old
       * for i = 3 to NOFILE close(i) loop.  In the case of the loops,
       * both sh and ksh leave the file descriptors open across execs.
       * The Posix standard mentions only the exec builtin.
       */
      if (set_clexec && (redirector > 2))
        SET_CLOSE_ON_EXEC (redirector);
    }

      if (fd != redirector)
    {
#if defined (BUFFERED_INPUT)
      if (INPUT_REDIRECT (ri))
        close_buffered_fd (fd);
      else
#endif /* !BUFFERED_INPUT */
        close (fd);     /* Don't close what we just opened! */
    }

      /* If we are hacking both stdout and stderr, do the stderr
     redirection here. */
      if (ri == r_err_and_out)
    {
      if (for_real)
        {
          if (remembering)
        add_undo_redirect (2);
          if (dup2 (1, 2) < 0)
        return (errno);
        }
    }
      break;

    case r_reading_until:
    case r_deblank_reading_until:
    case r_reading_string:
      /* REDIRECTEE is a pointer to a WORD_DESC containing the text of
     the new input.  Place it in a temporary file. */
      if (redirectee)
    {
      fd = here_document_to_fd (redirectee, ri);

      if (fd < 0)
        {
          heredoc_errno = errno;
          return (HEREDOC_REDIRECT);
        }

      if (for_real)
        {
          if (remembering)
            {
          /* Only setup to undo it if the thing to undo is active. */
          if ((fd != redirector) && (fcntl (redirector, F_GETFD, 0) != -1))
            add_undo_redirect (redirector);
          else
            add_undo_close_redirect (redirector);
            }

#if defined (BUFFERED_INPUT)
          check_bash_input (redirector);
#endif
          if (fd != redirector && dup2 (fd, redirector) < 0)
        {
          r = errno;
          close (fd);
          return (r);
        }

#if defined (BUFFERED_INPUT)
          duplicate_buffered_stream (fd, redirector);
#endif

          if (set_clexec && (redirector > 2))
        SET_CLOSE_ON_EXEC (redirector);
        }

      if (fd != redirector)
#if defined (BUFFERED_INPUT)
        close_buffered_fd (fd);
#else
        close (fd);
#endif
    }
      break;

    case r_duplicating_input:
    case r_duplicating_output:
    case r_move_input:
    case r_move_output:
      if (for_real && (redir_fd != redirector))
    {
      if (remembering)
        {
          /* Only setup to undo it if the thing to undo is active. */
          if (fcntl (redirector, F_GETFD, 0) != -1)
        add_undo_redirect (redirector);
          else
        add_undo_close_redirect (redirector);
        }

#if defined (BUFFERED_INPUT)
      check_bash_input (redirector);
#endif
      /* This is correct.  2>&1 means dup2 (1, 2); */
      if (dup2 (redir_fd, redirector) < 0)
        return (errno);

#if defined (BUFFERED_INPUT)
      if (ri == r_duplicating_input || ri == r_move_input)
        duplicate_buffered_stream (redir_fd, redirector);
#endif /* BUFFERED_INPUT */

      /* First duplicate the close-on-exec state of redirectee.  dup2
         leaves the flag unset on the new descriptor, which means it
         stays open.  Only set the close-on-exec bit for file descriptors
         greater than 2 in any case, since 0-2 should always be open
         unless closed by something like `exec 2<&-'. */
      /* if ((already_set || set_unconditionally) && (ok_to_set))
        set_it () */
      if (((fcntl (redir_fd, F_GETFD, 0) == 1) || set_clexec) &&
           (redirector > 2))
        SET_CLOSE_ON_EXEC (redirector);

      /* dup-and-close redirection */
      if (ri == r_move_input || ri == r_move_output)
        close (redir_fd);
    }
      break;

    case r_close_this:
      if (for_real)
    {
      if (remembering && (fcntl (redirector, F_GETFD, 0) != -1))
        add_undo_redirect (redirector);

#if defined (BUFFERED_INPUT)
      check_bash_input (redirector);
      close_buffered_fd (redirector);
#else /* !BUFFERED_INPUT */
      close (redirector);
#endif /* !BUFFERED_INPUT */
    }
      break;

    case r_duplicating_input_word:
    case r_duplicating_output_word:
      break;
    }
  return (0);
}

#define SHELL_FD_BASE   10

/* Remember the file descriptor associated with the slot FD,
   on REDIRECTION_UNDO_LIST.  Note that the list will be reversed
   before it is executed.  Any redirections that need to be undone
   even if REDIRECTION_UNDO_LIST is discarded by the exec builtin
   are also saved on EXEC_REDIRECTION_UNDO_LIST. */
static int
add_undo_redirect (fd)
     int fd;
{
  int new_fd, clexec_flag;
  REDIRECT *new_redirect, *closer, *dummy_redirect;

  new_fd = fcntl (fd, F_DUPFD, SHELL_FD_BASE);

  if (new_fd < 0)
    {
      sys_error ("redirection error: cannot duplicate fd");
      return (-1);
    }

  clexec_flag = fcntl (fd, F_GETFD, 0);

  rd.dest = 0;
  closer = make_redirection (new_fd, r_close_this, rd);
  dummy_redirect = copy_redirects (closer);

  rd.dest = new_fd;
  if (fd == 0)
    new_redirect = make_redirection (fd, r_duplicating_input, rd);
  else
    new_redirect = make_redirection (fd, r_duplicating_output, rd);
  new_redirect->next = closer;

  closer->next = redirection_undo_list;
  redirection_undo_list = new_redirect;

  /* Save redirections that need to be undone even if the undo list
     is thrown away by the `exec' builtin. */
  add_exec_redirect (dummy_redirect);

  /* File descriptors used only for saving others should always be
     marked close-on-exec.  Unfortunately, we have to preserve the
     close-on-exec state of the file descriptor we are saving, since
     fcntl (F_DUPFD) sets the new file descriptor to remain open
     across execs.  If, however, the file descriptor whose state we
     are saving is <= 2, we can just set the close-on-exec flag,
     because file descriptors 0-2 should always be open-on-exec,
     and the restore above in do_redirection() will take care of it. */
  if (clexec_flag || fd < 3)
    SET_CLOSE_ON_EXEC (new_fd);

  return (0);
}

/* Set up to close FD when we are finished with the current command
   and its redirections. */
static void
add_undo_close_redirect (fd)
     int fd;
{
  REDIRECT *closer;

  rd.dest = 0;
  closer = make_redirection (fd, r_close_this, rd);
  closer->next = redirection_undo_list;
  redirection_undo_list = closer;
}

static void
add_exec_redirect (dummy_redirect)
     REDIRECT *dummy_redirect;
{
  dummy_redirect->next = exec_redirection_undo_list;
  exec_redirection_undo_list = dummy_redirect;
}

/* Return 1 if the redirection specified by RI and REDIRECTOR alters the
   standard input. */
static int
stdin_redirection (ri, redirector)
     enum r_instruction ri;
     int redirector;
{
  switch (ri)
    {
    case r_input_direction:
    case r_inputa_direction:
    case r_input_output:
    case r_reading_until:
    case r_deblank_reading_until:
    case r_reading_string:
      return (1);
    case r_duplicating_input:
    case r_duplicating_input_word:
    case r_close_this:
      return (redirector == 0);
    case r_output_direction:
    case r_appending_to:
    case r_duplicating_output:
    case r_err_and_out:
    case r_output_force:
    case r_duplicating_output_word:
      return (0);
    }
  return (0);
}

/* Return non-zero if any of the redirections in REDIRS alter the standard
   input. */
int
stdin_redirects (redirs)
     REDIRECT *redirs;
{
  REDIRECT *rp;
  int n;

  for (n = 0, rp = redirs; rp; rp = rp->next)
    n += stdin_redirection (rp->instruction, rp->redirector);
  return n;
}

https://git.savannah.gnu.org/cgit/bash.git/tree/pcomplete.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/pcomplete.c used in: Step 1243
/* pcomplete.c - functions to generate lists of matches for programmable
         completion. */

/* Copyright (C) 1999-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (PROGRAMMABLE_COMPLETION)

#include "bashtypes.h"
#include "posixstat.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <signal.h>

#if defined (PREFER_STDARG)
#  include <stdarg.h>
#else
#  include <varargs.h>
#endif

#include <stdio.h>
#include "bashansi.h"

#include "shell.h"
#include "pcomplete.h"
#include "alias.h"
#include "bashline.h"
#include "execute_cmd.h"
#include "pathexp.h"

#if defined (JOB_CONTROL)
#  include "jobs.h"
#endif

#if !defined (NSIG)
#  include "trap.h"
#endif

#include "builtins.h"
#include "builtins/common.h"

#include <glob/glob.h>
#include <glob/strmatch.h>

#include <readline/rlconf.h>
#include <readline/readline.h>
#include <readline/history.h>

#ifdef STRDUP
#  undef STRDUP
#endif
#define STRDUP(x)   ((x) ? savestring (x) : (char *)NULL)

typedef SHELL_VAR **SVFUNC ();

#ifndef HAVE_STRPBRK
extern char *strpbrk __P((char *, char *));
#endif

extern int array_needs_making;
extern STRING_INT_ALIST word_token_alist[];
extern char *signal_names[];

#if defined (DEBUG)
#if defined (PREFER_STDARG)
static void debug_printf (const char *, ...)  __attribute__((__format__ (printf, 1, 2)));
#endif
#endif /* DEBUG */

static int it_init_joblist __P((ITEMLIST *, int));

static int it_init_aliases __P((ITEMLIST *));
static int it_init_arrayvars __P((ITEMLIST *));
static int it_init_bindings __P((ITEMLIST *));
static int it_init_builtins __P((ITEMLIST *));
static int it_init_disabled __P((ITEMLIST *));
static int it_init_enabled __P((ITEMLIST *));
static int it_init_exported __P((ITEMLIST *));
static int it_init_functions __P((ITEMLIST *));
static int it_init_hostnames __P((ITEMLIST *));
static int it_init_jobs __P((ITEMLIST *));
static int it_init_running __P((ITEMLIST *));
static int it_init_stopped __P((ITEMLIST *));
static int it_init_keywords __P((ITEMLIST *));
static int it_init_signals __P((ITEMLIST *));
static int it_init_variables __P((ITEMLIST *));
static int it_init_setopts __P((ITEMLIST *));
static int it_init_shopts __P((ITEMLIST *));

static int shouldexp_filterpat __P((char *));
static char *preproc_filterpat __P((char *, char *));

static void init_itemlist_from_varlist __P((ITEMLIST *, SVFUNC *));

static STRINGLIST *gen_matches_from_itemlist __P((ITEMLIST *, const char *));
static STRINGLIST *gen_action_completions __P((COMPSPEC *, const char *));
static STRINGLIST *gen_globpat_matches __P((COMPSPEC *, const char *));
static STRINGLIST *gen_wordlist_matches __P((COMPSPEC *, const char *));
static STRINGLIST *gen_shell_function_matches __P((COMPSPEC *, const char *,
                           char *, int, WORD_LIST *,
                           int, int));
static STRINGLIST *gen_command_matches __P((COMPSPEC *, const char *, char *,
                        int, WORD_LIST *, int, int));

static char *pcomp_filename_completion_function __P((const char *, int));

#if defined (ARRAY_VARS)
static SHELL_VAR *bind_comp_words __P((WORD_LIST *));
#endif
static void bind_compfunc_variables __P((char *, int, WORD_LIST *, int, int));
static void unbind_compfunc_variables __P((int));
static WORD_LIST *build_arg_list __P((char *, const char *, WORD_LIST *, int));
static WORD_LIST *command_line_to_word_list __P((char *, int, int, int *, int *));

#ifdef DEBUG
static int progcomp_debug = 0;
#endif

int prog_completion_enabled = 1;

/* These are used to manage the arrays of strings for possible completions. */
ITEMLIST it_aliases = { 0, it_init_aliases, (STRINGLIST *)0 };
ITEMLIST it_arrayvars  = { LIST_DYNAMIC, it_init_arrayvars, (STRINGLIST *)0 };
ITEMLIST it_bindings  = { 0, it_init_bindings, (STRINGLIST *)0 };
ITEMLIST it_builtins  = { 0, it_init_builtins, (STRINGLIST *)0 };
ITEMLIST it_commands = { LIST_DYNAMIC };    /* unused */
ITEMLIST it_directories = { LIST_DYNAMIC }; /* unused */
ITEMLIST it_disabled = { 0, it_init_disabled, (STRINGLIST *)0 };
ITEMLIST it_enabled = { 0, it_init_enabled, (STRINGLIST *)0 };
ITEMLIST it_exports  = { LIST_DYNAMIC, it_init_exported, (STRINGLIST *)0 };
ITEMLIST it_files = { LIST_DYNAMIC };       /* unused */
ITEMLIST it_functions  = { 0, it_init_functions, (STRINGLIST *)0 };
ITEMLIST it_hostnames  = { LIST_DYNAMIC, it_init_hostnames, (STRINGLIST *)0 };
ITEMLIST it_groups = { LIST_DYNAMIC };      /* unused */
ITEMLIST it_jobs = { LIST_DYNAMIC, it_init_jobs, (STRINGLIST *)0 };
ITEMLIST it_keywords = { 0, it_init_keywords, (STRINGLIST *)0 };
ITEMLIST it_running = { LIST_DYNAMIC, it_init_running, (STRINGLIST *)0 };
ITEMLIST it_services = { LIST_DYNAMIC };    /* unused */
ITEMLIST it_setopts = { 0, it_init_setopts, (STRINGLIST *)0 };
ITEMLIST it_shopts = { 0, it_init_shopts, (STRINGLIST *)0 };
ITEMLIST it_signals = { 0, it_init_signals, (STRINGLIST *)0 };
ITEMLIST it_stopped = { LIST_DYNAMIC, it_init_stopped, (STRINGLIST *)0 };
ITEMLIST it_users = { LIST_DYNAMIC };       /* unused */
ITEMLIST it_variables = { LIST_DYNAMIC, it_init_variables, (STRINGLIST *)0 };

#ifdef DEBUG
/* Debugging code */
static void
#if defined (PREFER_STDARG)
debug_printf (const char *format, ...)
#else
debug_printf (format, va_alist)
     const char *format;
     va_dcl
#endif
{
  va_list args;

  if (progcomp_debug == 0)
    return;

  SH_VA_START (args, format);

  fprintf (stdout, "DEBUG: ");
  vfprintf (stdout, format, args);
  fprintf (stdout, "\n");

  rl_on_new_line ();

  va_end (args);
}
#endif

/* Functions to manage the item lists */

void
set_itemlist_dirty (it)
     ITEMLIST *it;
{
  it->flags |= LIST_DIRTY;
}

void
initialize_itemlist (itp)
     ITEMLIST *itp;
{
  (*itp->list_getter) (itp);
  itp->flags |= LIST_INITIALIZED;
  itp->flags &= ~LIST_DIRTY;
}

void
clean_itemlist (itp)
     ITEMLIST *itp;
{
  STRINGLIST *sl;

  sl = itp->slist;
  if (sl)
    {
      if ((itp->flags & (LIST_DONTFREEMEMBERS|LIST_DONTFREE)) == 0)
    strvec_flush (sl->list);
      if ((itp->flags & LIST_DONTFREE) == 0)
    free (sl->list);
      free (sl);
    }
  itp->slist = (STRINGLIST *)NULL;
  itp->flags &= ~(LIST_DONTFREE|LIST_DONTFREEMEMBERS|LIST_INITIALIZED|LIST_DIRTY);
}


static int
shouldexp_filterpat (s)
     char *s;
{
  register char *p;

  for (p = s; p && *p; p++)
    {
      if (*p == '\\')
    p++;
      else if (*p == '&')
    return 1;
    }
  return 0;
}

/* Replace any instance of `&' in PAT with TEXT.  Backslash may be used to
   quote a `&' and inhibit substitution.  Returns a new string.  This just
   calls stringlib.c:strcreplace(). */
static char *
preproc_filterpat (pat, text)
     char *pat;
     char *text;
{
  char *ret;

  ret = strcreplace (pat, '&', text, 1);
  return ret;
}
    
/* Remove any match of FILTERPAT from SL.  A `&' in FILTERPAT is replaced by
   TEXT.  A leading `!' in FILTERPAT negates the pattern; in this case
   any member of SL->list that does *not* match will be removed.  This returns
   a new STRINGLIST with the matching members of SL *copied*.  Any
   non-matching members of SL->list are *freed*. */   
STRINGLIST *
filter_stringlist (sl, filterpat, text)
     STRINGLIST *sl;
     char *filterpat, *text;
{
  int i, m, not;
  STRINGLIST *ret;
  char *npat, *t;

  if (sl == 0 || sl->list == 0 || sl->list_len == 0)
    return sl;

  npat = shouldexp_filterpat (filterpat) ? preproc_filterpat (filterpat, text) : filterpat;

  not = (npat[0] == '!');
  t = not ? npat + 1 : npat;

  ret = strlist_create (sl->list_size);
  for (i = 0; i < sl->list_len; i++)
    {
      m = strmatch (t, sl->list[i], FNMATCH_EXTFLAG);
      if ((not && m == FNM_NOMATCH) || (not == 0 && m != FNM_NOMATCH))
    free (sl->list[i]);
      else
    ret->list[ret->list_len++] = sl->list[i];
    }

  ret->list[ret->list_len] = (char *)NULL;
  if (npat != filterpat)
    free (npat);

  return ret;
}

/* Turn an array of strings returned by rl_completion_matches into a STRINGLIST.
   This understands how rl_completion_matches sets matches[0] (the lcd of the
   strings in the list, unless it's the only match). */
STRINGLIST *
completions_to_stringlist (matches)
     char **matches;
{
  STRINGLIST *sl;
  int mlen, i, n;

  mlen = (matches == 0) ? 0 : strvec_len (matches);
  sl = strlist_create (mlen + 1);

  if (matches == 0 || matches[0] == 0)
    return sl;

  if (matches[1] == 0)
    {
      sl->list[0] = STRDUP (matches[0]);
      sl->list[sl->list_len = 1] = (char *)NULL;
      return sl;
    }

  for (i = 1, n = 0; i < mlen; i++, n++)
    sl->list[n] = STRDUP (matches[i]);
  sl->list_len = n;
  sl->list[n] = (char *)NULL;

  return sl;
}

/* Functions to manage the various ITEMLISTs that we populate internally.
   The caller is responsible for setting ITP->flags correctly. */

static int
it_init_aliases (itp)
     ITEMLIST *itp;
{
#ifdef ALIAS
  alias_t **alias_list;
  register int i, n;
  STRINGLIST *sl;

  alias_list = all_aliases ();
  if (alias_list == 0)
    {
      itp->slist = (STRINGLIST *)NULL;
      return 0;
    }
  for (n = 0; alias_list[n]; n++)
    ;
  sl = strlist_create (n+1);
  for (i = 0; i < n; i++)
    sl->list[i] = STRDUP (alias_list[i]->name);
  sl->list[n] = (char *)NULL;
  sl->list_size = sl->list_len = n;
  itp->slist = sl;
#else
  itp->slist = (STRINGLIST *)NULL;
#endif
  return 1;
}

static void
init_itemlist_from_varlist (itp, svfunc)
     ITEMLIST *itp;
     SVFUNC *svfunc;
{
  SHELL_VAR **vlist;
  STRINGLIST *sl;
  register int i, n;

  vlist = (*svfunc) ();
  for (n = 0; vlist[n]; n++)
    ;
  sl = strlist_create (n+1);
  for (i = 0; i < n; i++)
    sl->list[i] = savestring (vlist[i]->name);
  sl->list[sl->list_len = n] = (char *)NULL;
  itp->slist = sl;
}

static int
it_init_arrayvars (itp)
     ITEMLIST *itp;
{
#if defined (ARRAY_VARS)
  init_itemlist_from_varlist (itp, all_array_variables);
  return 1;
#else
  return 0;
#endif
}

static int
it_init_bindings (itp)
     ITEMLIST *itp;
{
  char **blist;
  STRINGLIST *sl;

  /* rl_funmap_names allocates blist, but not its members */
  blist = (char **)rl_funmap_names ();  /* XXX fix const later */
  sl = strlist_create (0);
  sl->list = blist;
  sl->list_size = 0;
  sl->list_len = strvec_len (sl->list);
  itp->flags |= LIST_DONTFREEMEMBERS;
  itp->slist = sl;

  return 0;
}

static int
it_init_builtins (itp)
     ITEMLIST *itp;
{
  STRINGLIST *sl;
  register int i, n;

  sl = strlist_create (num_shell_builtins);
  for (i = n = 0; i < num_shell_builtins; i++)
    if (shell_builtins[i].function)
      sl->list[n++] = shell_builtins[i].name;
  sl->list[sl->list_len = n] = (char *)NULL;
  itp->flags |= LIST_DONTFREEMEMBERS;
  itp->slist = sl;
  return 0;
}

static int
it_init_enabled (itp)
     ITEMLIST *itp;
{
  STRINGLIST *sl;
  register int i, n;

  sl = strlist_create (num_shell_builtins);
  for (i = n = 0; i < num_shell_builtins; i++)
    {
      if (shell_builtins[i].function && (shell_builtins[i].flags & BUILTIN_ENABLED))
    sl->list[n++] = shell_builtins[i].name;
    }
  sl->list[sl->list_len = n] = (char *)NULL;
  itp->flags |= LIST_DONTFREEMEMBERS;
  itp->slist = sl;
  return 0;
}

static int
it_init_disabled (itp)
     ITEMLIST *itp;
{
  STRINGLIST *sl;
  register int i, n;

  sl = strlist_create (num_shell_builtins);
  for (i = n = 0; i < num_shell_builtins; i++)
    {
      if (shell_builtins[i].function && ((shell_builtins[i].flags & BUILTIN_ENABLED) == 0))
    sl->list[n++] = shell_builtins[i].name;
    }
  sl->list[sl->list_len = n] = (char *)NULL;
  itp->flags |= LIST_DONTFREEMEMBERS;
  itp->slist = sl;
  return 0;
}

static int
it_init_exported (itp)
     ITEMLIST *itp;
{
  init_itemlist_from_varlist (itp, all_exported_variables);
  return 0;
}

static int
it_init_functions (itp)
     ITEMLIST *itp;
{
  init_itemlist_from_varlist (itp, all_visible_functions);
  return 0;
}

static int
it_init_hostnames (itp)
     ITEMLIST *itp;
{
  STRINGLIST *sl;

  sl = strlist_create (0);
  sl->list = get_hostname_list ();
  sl->list_len = sl->list ? strvec_len (sl->list) : 0;
  sl->list_size = sl->list_len;
  itp->slist = sl;
  itp->flags |= LIST_DONTFREEMEMBERS|LIST_DONTFREE;
  return 0;
}

static int
it_init_joblist (itp, jstate)
     ITEMLIST *itp;
     int jstate;
{
#if defined (JOB_CONTROL)
  STRINGLIST *sl;
  register int i;
  register PROCESS *p;
  char *s, *t;
  JOB_STATE js;

  if (jstate == 0)
    js = JRUNNING;
  else if (jstate == 1)
    js = JSTOPPED;

  sl = strlist_create (job_slots);
  for (i = job_slots - 1; i >= 0; i--)
    {
      if (jobs[i] == 0)
    continue;
      p = jobs[i]->pipe;
      if (jstate == -1 || JOBSTATE(i) == js)
    {
      s = savestring (p->command);
      t = strpbrk (s, " \t\n");
      if (t)
        *t = '\0';
      sl->list[sl->list_len++] = s;
    }
    }
  itp->slist = sl;
#else
  itp->slist = (STRINGLIST *)NULL;
#endif
  return 0;
}

static int
it_init_jobs (itp)
     ITEMLIST *itp;
{
  return (it_init_joblist (itp, -1));
}

static int
it_init_running (itp)
     ITEMLIST *itp;
{
  return (it_init_joblist (itp, 0));
}

static int
it_init_stopped (itp)
     ITEMLIST *itp;
{
  return (it_init_joblist (itp, 1));
}

static int
it_init_keywords (itp)
     ITEMLIST *itp;
{
  STRINGLIST *sl;
  register int i, n;

  for (n = 0; word_token_alist[n].word; n++)
    ;
  sl = strlist_create (n);
  for (i = 0; i < n; i++)
    sl->list[i] = word_token_alist[i].word;
  sl->list[sl->list_len = i] = (char *)NULL;
  itp->flags |= LIST_DONTFREEMEMBERS;
  itp->slist = sl;
  return 0;
}

static int
it_init_signals (itp)
     ITEMLIST *itp;
{
  STRINGLIST *sl;

  sl = strlist_create (0);
  sl->list = signal_names;
  sl->list_len = strvec_len (sl->list);
  itp->flags |= LIST_DONTFREE;
  itp->slist = sl;
  return 0;
}

static int
it_init_variables (itp)
     ITEMLIST *itp;
{
  init_itemlist_from_varlist (itp, all_visible_variables);
  return 0;
}

static int
it_init_setopts (itp)
     ITEMLIST *itp;
{
  STRINGLIST *sl;

  sl = strlist_create (0);
  sl->list = get_minus_o_opts ();
  sl->list_len = strvec_len (sl->list);
  itp->slist = sl;
  itp->flags |= LIST_DONTFREEMEMBERS;
  return 0;
}

static int
it_init_shopts (itp)
     ITEMLIST *itp;
{
  STRINGLIST *sl;

  sl = strlist_create (0);
  sl->list = get_shopt_options ();
  sl->list_len = strvec_len (sl->list);
  itp->slist = sl;
  itp->flags |= LIST_DONTFREEMEMBERS;
  return 0;
}

/* Generate a list of all matches for TEXT using the STRINGLIST in itp->slist
   as the list of possibilities.  If the itemlist has been marked dirty or
   it should be regenerated every time, destroy the old STRINGLIST and make a
   new one before trying the match. */
static STRINGLIST *
gen_matches_from_itemlist (itp, text)
     ITEMLIST *itp;
     const char *text;
{
  STRINGLIST *ret, *sl;
  int tlen, i, n;

  if ((itp->flags & (LIST_DIRTY|LIST_DYNAMIC)) ||
      (itp->flags & LIST_INITIALIZED) == 0)
    {
      if (itp->flags & (LIST_DIRTY | LIST_DYNAMIC))
    clean_itemlist (itp);
      if ((itp->flags & LIST_INITIALIZED) == 0)
    initialize_itemlist (itp);
    }
  if (itp->slist == 0)
    return ((STRINGLIST *)NULL);
  ret = strlist_create (itp->slist->list_len+1);
  sl = itp->slist;
  tlen = STRLEN (text);
  for (i = n = 0; i < sl->list_len; i++)
    {
      if (tlen == 0 || STREQN (sl->list[i], text, tlen))
    ret->list[n++] = STRDUP (sl->list[i]);
    }
  ret->list[ret->list_len = n] = (char *)NULL;
  return ret;
}

/* A wrapper for rl_filename_completion_function that dequotes the filename
   before attempting completions. */
static char *
pcomp_filename_completion_function (text, state)
     const char *text;
     int state;
{
  static char *dfn; /* dequoted filename */
  int qc;

  if (state == 0)
    {
      FREE (dfn);
      /* remove backslashes quoting special characters in filenames. */
      if (rl_filename_dequoting_function)
    {
      qc = (text[0] == '"' || text[0] == '\'') ? text[0] : 0;
      dfn = (*rl_filename_dequoting_function) ((char *)text, qc);
    }
      else
    dfn = savestring (text);
    }

  return (rl_filename_completion_function (dfn, state));
}

#define GEN_COMPS(bmap, flag, it, text, glist, tlist) \
  do { \
    if (bmap & flag) \
      { \
    tlist = gen_matches_from_itemlist (it, text); \
    if (tlist) \
      { \
        glist = strlist_append (glist, tlist); \
        strlist_dispose (tlist); \
      } \
      } \
  } while (0)

#define GEN_XCOMPS(bmap, flag, text, func, cmatches, glist, tlist) \
  do { \
    if (bmap & flag) \
      { \
    cmatches = rl_completion_matches (text, func); \
    tlist = completions_to_stringlist (cmatches); \
    glist = strlist_append (glist, tlist); \
    strvec_dispose (cmatches); \
    strlist_dispose (tlist); \
      } \
  } while (0)

/* Functions to generate lists of matches from the actions member of CS. */

static STRINGLIST *
gen_action_completions (cs, text)
     COMPSPEC *cs;
     const char *text;
{
  STRINGLIST *ret, *tmatches;
  char **cmatches;  /* from rl_completion_matches ... */
  unsigned long flags;

  ret = tmatches = (STRINGLIST *)NULL;
  flags = cs->actions;

  GEN_COMPS (flags, CA_ALIAS, &it_aliases, text, ret, tmatches);
  GEN_COMPS (flags, CA_ARRAYVAR, &it_arrayvars, text, ret, tmatches);
  GEN_COMPS (flags, CA_BINDING, &it_bindings, text, ret, tmatches);
  GEN_COMPS (flags, CA_BUILTIN, &it_builtins, text, ret, tmatches);
  GEN_COMPS (flags, CA_DISABLED, &it_disabled, text, ret, tmatches);
  GEN_COMPS (flags, CA_ENABLED, &it_enabled, text, ret, tmatches);
  GEN_COMPS (flags, CA_EXPORT, &it_exports, text, ret, tmatches);
  GEN_COMPS (flags, CA_FUNCTION, &it_functions, text, ret, tmatches);
  GEN_COMPS (flags, CA_HOSTNAME, &it_hostnames, text, ret, tmatches);
  GEN_COMPS (flags, CA_JOB, &it_jobs, text, ret, tmatches);
  GEN_COMPS (flags, CA_KEYWORD, &it_keywords, text, ret, tmatches);
  GEN_COMPS (flags, CA_RUNNING, &it_running, text, ret, tmatches);
  GEN_COMPS (flags, CA_SETOPT, &it_setopts, text, ret, tmatches);
  GEN_COMPS (flags, CA_SHOPT, &it_shopts, text, ret, tmatches);
  GEN_COMPS (flags, CA_SIGNAL, &it_signals, text, ret, tmatches);
  GEN_COMPS (flags, CA_STOPPED, &it_stopped, text, ret, tmatches);
  GEN_COMPS (flags, CA_VARIABLE, &it_variables, text, ret, tmatches);

  GEN_XCOMPS(flags, CA_COMMAND, text, command_word_completion_function, cmatches, ret, tmatches);
  GEN_XCOMPS(flags, CA_FILE, text, pcomp_filename_completion_function, cmatches, ret, tmatches);
  GEN_XCOMPS(flags, CA_USER, text, rl_username_completion_function, cmatches, ret, tmatches);
  GEN_XCOMPS(flags, CA_GROUP, text, bash_groupname_completion_function, cmatches, ret, tmatches);
  GEN_XCOMPS(flags, CA_SERVICE, text, bash_servicename_completion_function, cmatches, ret, tmatches);

  /* And lastly, the special case for directories */
  if (flags & CA_DIRECTORY)
    {
      rl_completion_mark_symlink_dirs = 1;  /* override user preference */
      cmatches = bash_directory_completion_matches (text);
      tmatches = completions_to_stringlist (cmatches);
      ret = strlist_append (ret, tmatches);
      strvec_dispose (cmatches);
      strlist_dispose (tmatches);
    }

  return ret;
}

/* Generate a list of matches for CS->globpat.  Unresolved: should this use
   TEXT as a match prefix, or just go without?  Currently, the code does not
   use TEXT, just globs CS->globpat and returns the results.  If we do decide
   to use TEXT, we should call quote_string_for_globbing before the call to
   glob_filename. */
static STRINGLIST *
gen_globpat_matches (cs, text)
      COMPSPEC *cs;
      const char *text;
{
  STRINGLIST *sl;

  sl = strlist_create (0);
  sl->list = glob_filename (cs->globpat, 0);
  if (GLOB_FAILED (sl->list))
    sl->list = (char **)NULL;
  if (sl->list)
    sl->list_len = sl->list_size = strvec_len (sl->list);
  return sl;
}

/* Perform the shell word expansions on CS->words and return the results.
   Again, this ignores TEXT. */
static STRINGLIST *
gen_wordlist_matches (cs, text)
     COMPSPEC *cs;
     const char *text;
{
  WORD_LIST *l, *l2;
  STRINGLIST *sl;
  int nw, tlen;

  if (cs->words == 0 || cs->words[0] == '\0')
    return ((STRINGLIST *)NULL);

  /* This used to be a simple expand_string(cs->words, 0), but that won't
     do -- there's no way to split a simple list into individual words
     that way, since the shell semantics say that word splitting is done
     only on the results of expansion. */
  l = split_at_delims (cs->words, strlen (cs->words), (char *)NULL, -1, (int *)NULL, (int *)NULL);
  if (l == 0)
    return ((STRINGLIST *)NULL);
  /* This will jump back to the top level if the expansion fails... */
  l2 = expand_words_shellexp (l);
  dispose_words (l);

  nw = list_length (l2);
  sl = strlist_create (nw + 1);
  tlen = STRLEN (text);

  for (nw = 0, l = l2; l; l = l->next)
    {
      if (tlen == 0 || STREQN (l->word->word, text, tlen))
    sl->list[nw++] = STRDUP (l->word->word);
    }
  sl->list[sl->list_len = nw] = (char *)NULL;

  return sl;
}

#ifdef ARRAY_VARS

static SHELL_VAR *
bind_comp_words (lwords)
     WORD_LIST *lwords;
{
  SHELL_VAR *v;

  v = find_variable ("COMP_WORDS");
  if (v == 0)
    v = make_new_array_variable ("COMP_WORDS");
  if (readonly_p (v))
    VUNSETATTR (v, att_readonly);
  if (array_p (v) == 0)
    v = convert_var_to_array (v);
  v = assign_array_var_from_word_list (v, lwords);
  return v;
}
#endif /* ARRAY_VARS */

static void
bind_compfunc_variables (line, ind, lwords, cw, exported)
     char *line;
     int ind;
     WORD_LIST *lwords;
     int cw, exported;
{
  char ibuf[INT_STRLEN_BOUND(int) + 1];
  char *value;
  SHELL_VAR *v;

  /* Set the variables that the function expects while it executes.  Maybe
     these should be in the function environment (temporary_env). */
  v = bind_variable ("COMP_LINE", line);
  if (v && exported)
    VSETATTR(v, att_exported);

  value = inttostr (ind, ibuf, sizeof(ibuf));
  v = bind_int_variable ("COMP_POINT", value);
  if (v && exported)
    VSETATTR(v, att_exported);

  /* Since array variables can't be exported, we don't bother making the
     array of words. */
  if (exported == 0)
    {
#ifdef ARRAY_VARS
      v = bind_comp_words (lwords);
      value = inttostr (cw, ibuf, sizeof(ibuf));
      bind_int_variable ("COMP_CWORD", value);
#endif
    }
  else
    array_needs_making = 1;
}

static void
unbind_compfunc_variables (exported)
     int exported;
{
  unbind_variable ("COMP_LINE");
  unbind_variable ("COMP_POINT");
#ifdef ARRAY_VARS
  unbind_variable ("COMP_WORDS");
  unbind_variable ("COMP_CWORD");
#endif
  if (exported)
    array_needs_making = 1;
}

/* Build the list of words to pass to a function or external command
   as arguments.  When the function or command is invoked,

    $0 == function or command being invoked
    $1 == command name
    $2 = word to be completed (possibly null)
    $3 = previous word

   Functions can access all of the words in the current command line
   with the COMP_WORDS array.  External commands cannot. */

static WORD_LIST *
build_arg_list (cmd, text, lwords, ind)
     char *cmd;
     const char *text;
     WORD_LIST *lwords;
     int ind;
{
  WORD_LIST *ret, *cl, *l;
  WORD_DESC *w;
  int i;

  ret = (WORD_LIST *)NULL;
  w = make_word (cmd);
  ret = make_word_list (w, (WORD_LIST *)NULL);

  w = (lwords && lwords->word) ? copy_word (lwords->word) : make_word ("");
  cl = ret->next = make_word_list (w, (WORD_LIST *)NULL);

  w = make_word (text);
  cl->next = make_word_list (w, (WORD_LIST *)NULL);
  cl = cl->next;

  /* Search lwords for current word */
  for (l = lwords, i = 1; l && i < ind-1; l = l->next, i++)
    ;
  w = (l && l->word) ? copy_word (l->word) : make_word ("");
  cl->next = make_word_list (w, (WORD_LIST *)NULL);

  return ret;
}

/* Build a command string with
    $0 == cs->funcname  (function to execute for completion list)
    $1 == command name  (command being completed)
    $2 = word to be completed (possibly null)
    $3 = previous word
   and run in the current shell.  The function should put its completion
   list into the array variable COMPREPLY.  We build a STRINGLIST
   from the results and return it.

   Since the shell function should return its list of matches in an array
   variable, this does nothing if arrays are not compiled into the shell. */

static STRINGLIST *
gen_shell_function_matches (cs, text, line, ind, lwords, nw, cw)
     COMPSPEC *cs;
     const char *text;
     char *line;
     int ind;
     WORD_LIST *lwords;
     int nw, cw;
{
  char *funcname;
  STRINGLIST *sl;
  SHELL_VAR *f, *v;
  WORD_LIST *cmdlist;
  int fval;
#if defined (ARRAY_VARS)
  ARRAY *a;
#endif

  funcname = cs->funcname;
  f = find_function (funcname);
  if (f == 0)
    {
      internal_error ("completion: function `%s' not found", funcname);
      rl_ding ();
      rl_on_new_line ();
      return ((STRINGLIST *)NULL);
    }

#if !defined (ARRAY_VARS)
  return ((STRINGLIST *)NULL);
#else

  /* We pass cw - 1 because command_line_to_word_list returns indices that are
     1-based, while bash arrays are 0-based. */
  bind_compfunc_variables (line, ind, lwords, cw - 1, 0);

  cmdlist = build_arg_list (funcname, text, lwords, cw);
  
  fval = execute_shell_function (f, cmdlist);  

  /* Now clean up and destroy everything. */
  dispose_words (cmdlist);
  unbind_compfunc_variables (0);

  /* The list of completions is returned in the array variable COMPREPLY. */
  v = find_variable ("COMPREPLY");
  if (v == 0)
    return ((STRINGLIST *)NULL);
  if (array_p (v) == 0)
    v = convert_var_to_array (v);

  a = array_cell (v);
  if (a == 0 || array_empty (a))
    sl = (STRINGLIST *)NULL;
  else
    {
      /* XXX - should we filter the list of completions so only those matching
     TEXT are returned?  Right now, we do not. */
      sl = strlist_create (0);
      sl->list = array_to_argv (a);
      sl->list_len = sl->list_size = array_num_elements (a);
    }

  /* XXX - should we unbind COMPREPLY here? */
  unbind_variable ("COMPREPLY");

  return (sl);
#endif
}

/* Build a command string with
    $0 == cs->command   (command to execute for completion list)
    $1 == command name  (command being completed)
    $2 = word to be completed (possibly null)
    $3 = previous word
   and run in with command substitution.  Parse the results, one word
   per line, with backslashes allowed to escape newlines.  Build a
   STRINGLIST from the results and return it. */

static STRINGLIST *
gen_command_matches (cs, text, line, ind, lwords, nw, cw)
     COMPSPEC *cs;
     const char *text;
     char *line;
     int ind;
     WORD_LIST *lwords;
     int nw, cw;
{
  char *csbuf, *cscmd, *t;
  int cmdlen, cmdsize, n, ws, we;
  WORD_LIST *cmdlist, *cl;
  STRINGLIST *sl;

  bind_compfunc_variables (line, ind, lwords, cw, 1);
  cmdlist = build_arg_list (cs->command, text, lwords, cw);

  /* Estimate the size needed for the buffer. */
  n = strlen (cs->command);
  cmdsize = n + 1;
  for (cl = cmdlist->next; cl; cl = cl->next)
    cmdsize += STRLEN (cl->word->word) + 3;
  cmdsize += 2;

  /* allocate the string for the command and fill it in. */
  cscmd = (char *)xmalloc (cmdsize + 1);

  strcpy (cscmd, cs->command);          /* $0 */
  cmdlen = n;
  cscmd[cmdlen++] = ' ';
  for (cl = cmdlist->next; cl; cl = cl->next)   /* $1, $2, $3, ... */
    {
      t = sh_single_quote (cl->word->word ? cl->word->word : "");
      n = strlen (t);
      RESIZE_MALLOCED_BUFFER (cscmd, cmdlen, n + 2, cmdsize, 64);
      strcpy (cscmd + cmdlen, t);
      cmdlen += n;
      if (cl->next)
    cscmd[cmdlen++] = ' ';
      free (t);
    }
  cscmd[cmdlen] = '\0';

  csbuf = command_substitute (cscmd, 0);

  /* Now clean up and destroy everything. */
  dispose_words (cmdlist);
  free (cscmd);
  unbind_compfunc_variables (1);

  if (csbuf == 0 || *csbuf == '\0')
    {
      FREE (csbuf);
      return ((STRINGLIST *)NULL);
    }

  /* Now break CSBUF up at newlines, with backslash allowed to escape a
     newline, and put the individual words into a STRINGLIST. */
  sl = strlist_create (16);
  for (ws = 0; csbuf[ws]; )
    {
      we = ws;
      while (csbuf[we] && csbuf[we] != '\n')
    {
      if (csbuf[we] == '\\' && csbuf[we+1] == '\n')
        we++;
      we++;
    }
      t = substring (csbuf, ws, we);
      if (sl->list_len >= sl->list_size - 1)
    strlist_resize (sl, sl->list_size + 16);
      sl->list[sl->list_len++] = t;
      while (csbuf[we] == '\n') we++;
      ws = we;
    }
  sl->list[sl->list_len] = (char *)NULL;

  free (csbuf);
  return (sl);
}

static WORD_LIST *
command_line_to_word_list (line, llen, sentinel, nwp, cwp)
     char *line;
     int llen, sentinel, *nwp, *cwp;
{
  WORD_LIST *ret;
  char *delims;

  delims = "()<>;&| \t\n";  /* shell metacharacters break words */
  ret = split_at_delims (line, llen, delims, sentinel, nwp, cwp);
  return (ret);
}

/* Evaluate COMPSPEC *cs and return all matches for WORD. */

STRINGLIST *
gen_compspec_completions (cs, cmd, word, start, end)
     COMPSPEC *cs;
     const char *cmd;
     const char *word;
     int start, end;
{
  STRINGLIST *ret, *tmatches;
  char *line;
  int llen, nw, cw;
  WORD_LIST *lwords;

#ifdef DEBUG
  debug_printf ("gen_compspec_completions (%s, %s, %d, %d)", cmd, word, start, end);
  debug_printf ("gen_compspec_completions: %s -> %p", cmd, cs);
#endif
  ret = gen_action_completions (cs, word);
#ifdef DEBUG
  if (ret && progcomp_debug)
    {
      debug_printf ("gen_action_completions (%p, %s) -->", cs, word);
      strlist_print (ret, "\t");
      rl_on_new_line ();
    }
#endif

  /* Now we start generating completions based on the other members of CS. */
  if (cs->globpat)
    {
      tmatches = gen_globpat_matches (cs, word);
      if (tmatches)
    {
#ifdef DEBUG
      if (progcomp_debug)
        {
          debug_printf ("gen_globpat_matches (%p, %s) -->", cs, word);
          strlist_print (tmatches, "\t");
          rl_on_new_line ();
        }
#endif
      ret = strlist_append (ret, tmatches);
      strlist_dispose (tmatches);
      rl_filename_completion_desired = 1;
    }
    }

  if (cs->words)
    {
      tmatches = gen_wordlist_matches (cs, word);
      if (tmatches)
    {
#ifdef DEBUG
      if (progcomp_debug)
        {
          debug_printf ("gen_wordlist_matches (%p, %s) -->", cs, word);
          strlist_print (tmatches, "\t");
          rl_on_new_line ();
        }
#endif
      ret = strlist_append (ret, tmatches);
      strlist_dispose (tmatches);
    }
    }

  lwords = (WORD_LIST *)NULL;
  line = (char *)NULL;
  if (cs->command || cs->funcname)
    {
      /* If we have a command or function to execute, we need to first break
     the command line into individual words, find the number of words,
     and find the word in the list containing the word to be completed. */
      line = substring (rl_line_buffer, start, end);
      llen = end - start;

#ifdef DEBUG
      debug_printf ("command_line_to_word_list (%s, %d, %d, %p, %p)",
        line, llen, rl_point - start, &nw, &cw);
#endif
      lwords = command_line_to_word_list (line, llen, rl_point - start, &nw, &cw);
#ifdef DEBUG
      if (lwords == 0 && llen > 0)
    debug_printf ("ERROR: command_line_to_word_list returns NULL");
      else if (progcomp_debug)
    {
      debug_printf ("command_line_to_word_list -->");
      printf ("\t");
      print_word_list (lwords, "!");
      printf ("\n");
      fflush(stdout);
      rl_on_new_line ();
    }
#endif
    }

  if (cs->funcname)
    {
      tmatches = gen_shell_function_matches (cs, word, line, rl_point - start, lwords, nw, cw);
      if (tmatches)
    {
#ifdef DEBUG
      if (progcomp_debug)
        {
          debug_printf ("gen_shell_function_matches (%p, %s, %p, %d, %d) -->", cs, word, lwords, nw, cw);
          strlist_print (tmatches, "\t");
          rl_on_new_line ();
        }
#endif
      ret = strlist_append (ret, tmatches);
      strlist_dispose (tmatches);
    }
    }

  if (cs->command)
    {
      tmatches = gen_command_matches (cs, word, line, rl_point - start, lwords, nw, cw);
      if (tmatches)
    {
#ifdef DEBUG
      if (progcomp_debug)
        {
          debug_printf ("gen_command_matches (%p, %s, %p, %d, %d) -->", cs, word, lwords, nw, cw);
          strlist_print (tmatches, "\t");
          rl_on_new_line ();
        }
#endif
      ret = strlist_append (ret, tmatches);
      strlist_dispose (tmatches);
    }
    }

  if (cs->command || cs->funcname)
    {
      if (lwords)
    dispose_words (lwords);
      FREE (line);
    }

  if (cs->filterpat)
    {
      tmatches = filter_stringlist (ret, cs->filterpat, word);
#ifdef DEBUG
      if (progcomp_debug)
    {
      debug_printf ("filter_stringlist (%p, %s, %s) -->", ret, cs->filterpat, word);
      strlist_print (tmatches, "\t");
      rl_on_new_line ();
    }
#endif
      if (ret && ret != tmatches)
    {
      FREE (ret->list);
      free (ret);
    }
      ret = tmatches;
    }

  if (cs->prefix || cs->suffix)
    ret = strlist_prefix_suffix (ret, cs->prefix, cs->suffix);

  /* If no matches have been generated and the user has specified that
      directory completion should be done as a default, call
      gen_action_completions again to generate a list of matching directory
      names. */
  if ((ret == 0 || ret->list_len == 0) && (cs->options & COPT_DIRNAMES))
    {
      COMPSPEC *dummy;

      dummy = compspec_create ();
      dummy->actions = CA_DIRECTORY;
      ret = gen_action_completions (dummy, word);
      compspec_dispose (dummy);
    }

  return (ret);
}

/* The driver function for the programmable completion code.  Returns a list
   of matches for WORD, which is an argument to command CMD.  START and END
   bound the command currently being completed in rl_line_buffer. */
char **
programmable_completions (cmd, word, start, end, foundp)
     const char *cmd;
     const char *word;
     int start, end, *foundp;
{
  COMPSPEC *cs;
  STRINGLIST *ret;
  char **rmatches, *t;

  /* We look at the basename of CMD if the full command does not have
     an associated COMPSPEC. */
  cs = progcomp_search (cmd);
  if (cs == 0)
    {
      t = strrchr (cmd, '/');
      if (t)
    cs = progcomp_search (++t);
    }
  if (cs == 0)
    {
      if (foundp)
    *foundp = 0;
      return ((char **)NULL);
    }

  /* Signal the caller that we found a COMPSPEC for this command, and pass
     back any meta-options associated with the compspec. */
  if (foundp)
    *foundp = 1|cs->options;

  ret = gen_compspec_completions (cs, cmd, word, start, end);

  if (ret)
    {
      rmatches = ret->list;
      free (ret);
    }
  else
    rmatches = (char **)NULL;

  return (rmatches);
}

#endif /* PROGRAMMABLE_COMPLETION */

https://git.savannah.gnu.org/cgit/bash.git/tree/pcomplib.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/pcomplib.c used in: Step 1244
/* pcomplib.c - library functions for programmable completion. */

/* Copyright (C) 1999-2002 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include <config.h>

#if defined (PROGRAMMABLE_COMPLETION)

#include "bashansi.h"
#include <stdio.h>

#if defined (HAVE_UNISTD_H)
#  ifdef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#include "shell.h"
#include "pcomplete.h"

#define COMPLETE_HASH_BUCKETS   32  /* must be power of two */

#define STRDUP(x)   ((x) ? savestring (x) : (char *)NULL)

HASH_TABLE *prog_completes = (HASH_TABLE *)NULL;

static void free_progcomp __P((PTR_T));

COMPSPEC *
compspec_create ()
{
  COMPSPEC *ret;

  ret = (COMPSPEC *)xmalloc (sizeof (COMPSPEC));
  ret->refcount = 0;

  ret->actions = (unsigned long)0;
  ret->options = (unsigned long)0;

  ret->globpat = (char *)NULL;
  ret->words = (char *)NULL;
  ret->prefix = (char *)NULL;
  ret->suffix = (char *)NULL;
  ret->funcname = (char *)NULL;
  ret->command = (char *)NULL;
  ret->filterpat = (char *)NULL;

  return ret;
}

void
compspec_dispose (cs)
     COMPSPEC *cs;
{
  cs->refcount--;
  if (cs->refcount == 0)
    {
      FREE (cs->globpat);
      FREE (cs->words);
      FREE (cs->prefix);
      FREE (cs->suffix);
      FREE (cs->funcname);
      FREE (cs->command);
      FREE (cs->filterpat);

      free (cs);
    }
}

COMPSPEC *
compspec_copy (cs)
     COMPSPEC *cs;
{
  COMPSPEC *new;

  new = (COMPSPEC *)xmalloc (sizeof (COMPSPEC));

  new->refcount = cs->refcount;
  new->actions = cs->actions;
  new->options = cs->options;

  new->globpat = STRDUP (cs->globpat);
  new->words = STRDUP (cs->words);
  new->prefix = STRDUP (cs->prefix);
  new->suffix = STRDUP (cs->suffix);
  new->funcname = STRDUP (cs->funcname);
  new->command = STRDUP (cs->command);
  new->filterpat = STRDUP (cs->filterpat);

  return new;
}

void
progcomp_create ()
{
  if (prog_completes == 0)
    prog_completes = hash_create (COMPLETE_HASH_BUCKETS);
}

int
progcomp_size ()
{
  return (HASH_ENTRIES (prog_completes));
}

static void
free_progcomp (data)
     PTR_T data;
{
  COMPSPEC *cs;

  cs = (COMPSPEC *)data;
  compspec_dispose (cs);
}
  
void
progcomp_flush ()
{
  if (prog_completes)
    hash_flush (prog_completes, free_progcomp);
}

void
progcomp_dispose ()
{
  if (prog_completes)
    hash_dispose (prog_completes);
  prog_completes = (HASH_TABLE *)NULL;
}

int
progcomp_remove (cmd)
     char *cmd;
{
  register BUCKET_CONTENTS *item;

  if (prog_completes == 0)
    return 1;

  item = hash_remove (cmd, prog_completes, 0);
  if (item)
    {
      if (item->data)
    free_progcomp (item->data);
      free (item->key);
      free (item);
      return (1);
    }
  return (0);
}

int
progcomp_insert (cmd, cs)
      char *cmd;
      COMPSPEC *cs;
{
  register BUCKET_CONTENTS *item;

  if (cs == NULL)
    programming_error ("progcomp_insert: %s: NULL COMPSPEC", cmd);

  if (prog_completes == 0)
    progcomp_create ();

  item = hash_insert (cmd, prog_completes, 0);
  if (item->data)
    free_progcomp (item->data);
  else
    item->key = savestring (cmd);
  item->data = cs;
  cs->refcount++;
  return 1;
}

COMPSPEC *
progcomp_search (cmd)
     const char *cmd;
{
  register BUCKET_CONTENTS *item;
  COMPSPEC *cs;

  if (prog_completes == 0)
    return ((COMPSPEC *)NULL);

  item = hash_search (cmd, prog_completes, 0);

  if (item == NULL)
    return ((COMPSPEC *)NULL);

  cs = (COMPSPEC *)item->data;

  return (cs);
}

void
progcomp_walk (pfunc)
     hash_wfunc *pfunc;
{
  if (prog_completes == 0 || pfunc == 0 || HASH_ENTRIES (prog_completes) == 0)
    return;

  hash_walk (prog_completes, pfunc);
}

#endif /* PROGRAMMABLE_COMPLETION */

https://git.savannah.gnu.org/cgit/bash.git/tree/mksyntax.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/mksyntax.c used in: Step 1245
/*
 * mksyntax.c - construct shell syntax table for fast char attribute lookup.
 */

/* Copyright (C) 2000 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#include <stdio.h>
#include "bashansi.h"
#include "chartypes.h"
#include <errno.h>

#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif

#include "syntax.h"

extern int optind;
extern char *optarg;

#ifndef errno
extern int errno;
#endif

#ifndef HAVE_STRERROR
extern char *strerror();
#endif

struct wordflag {
    int flag;
    char    *fstr;
} wordflags[] = {
    { CWORD,    "CWORD" },
    { CSHMETA,  "CSHMETA" },
    { CSHBRK,   "CSHBRK" },
    { CBACKQ,   "CBACKQ" },
    { CQUOTE,   "CQUOTE" },
    { CSPECL,   "CSPECL" },
    { CEXP,     "CEXP" },
    { CBSDQUOTE,    "CBSDQUOTE" },
    { CBSHDOC,  "CBSHDOC" },
    { CGLOB,    "CGLOB" },
    { CXGLOB,   "CXGLOB" },
    { CXQUOTE,  "CXQUOTE" },
    { CSPECVAR, "CSPECVAR" },
    { CSUBSTOP, "CSUBSTOP" },
};
    
#define N_WFLAGS    (sizeof (wordflags) / sizeof (wordflags[0]))
#define SYNSIZE     256

int lsyntax[SYNSIZE];
int debug;
char    *progname;

char    preamble[] = "\
/*\n\
 * This file was generated by mksyntax.  DO NOT EDIT.\n\
 */\n\
\n";

char    includes[] = "\
#include \"stdc.h\"\n\
#include \"syntax.h\"\n\n";

static void
usage()
{
  fprintf (stderr, "%s: usage: %s [-d] [-o filename]\n", progname, progname);
  exit (2);
}

#ifdef INCLUDE_UNUSED
static int
getcflag (s)
     char *s;
{
  int i;

  for (i = 0; i < N_WFLAGS; i++)
    if (strcmp (s, wordflags[i].fstr) == 0)
      return wordflags[i].flag;
  return -1;
}
#endif

static char *
cdesc (i)
     int i;
{
  static char xbuf[16];

  if (i == ' ')
    return "SPC";
  else if (ISPRINT (i))
    {
      xbuf[0] = i;
      xbuf[1] = '\0';
      return (xbuf);
    }
  else if (i == CTLESC)
    return "CTLESC";
  else if (i == CTLNUL)
    return "CTLNUL";
  else if (i == '\033')     /* ASCII */
    return "ESC";

  xbuf[0] = '\\';
  xbuf[2] = '\0';
    
  switch (i)
    {
    case '\a': xbuf[1] = 'a'; break;
    case '\v': xbuf[1] = 'v'; break;
    case '\b': xbuf[1] = 'b'; break;
    case '\f': xbuf[1] = 'f'; break;
    case '\n': xbuf[1] = 'n'; break;
    case '\r': xbuf[1] = 'r'; break;
    case '\t': xbuf[1] = 't'; break;
    default: sprintf (xbuf, "%d", i); break;
    }

  return xbuf;  
}

static char *
getcstr (f)
     int f;
{
  int i;

  for (i = 0; i < N_WFLAGS; i++)
    if (f == wordflags[i].flag)
      return (wordflags[i].fstr);
  return ((char *)NULL);
}

static void
addcstr (str, flag)
     char *str;
     int flag;
{
  char *s, *fstr;
  unsigned char uc;

  for (s = str; s && *s; s++)
    {
      uc = *s;

      if (debug)
    {
      fstr = getcstr (flag);
      fprintf(stderr, "added %s for character %s\n", fstr, cdesc(uc));
    }
    
      lsyntax[uc] |= flag;
    }
}

static void
addcchar (c, flag)
     unsigned char c;
     int flag;
{
  char *fstr;

  if (debug)
    {
      fstr = getcstr (flag);
      fprintf (stderr, "added %s for character %s\n", fstr, cdesc(c));
    }
  lsyntax[c] |= flag;
}

/* load up the correct flag values in lsyntax */
static void
load_lsyntax ()
{
  /* shell metacharacters */
  addcstr (shell_meta_chars, CSHMETA);

  /* shell word break characters */
  addcstr (shell_break_chars, CSHBRK);

  addcchar ('`', CBACKQ);

  addcstr (shell_quote_chars, CQUOTE);

  addcchar (CTLESC, CSPECL);
  addcchar (CTLNUL, CSPECL);

  addcstr (shell_exp_chars, CEXP);

  addcstr (slashify_in_quotes, CBSDQUOTE);
  addcstr (slashify_in_here_document, CBSHDOC);

  addcstr (shell_glob_chars, CGLOB);

#if defined (EXTENDED_GLOB)
  addcstr (ext_glob_chars, CXGLOB);
#endif

  addcstr (shell_quote_chars, CXQUOTE);
  addcchar ('\\', CXQUOTE);

  addcstr ("@*#?-$!", CSPECVAR);    /* omits $0...$9 and $_ */

  addcstr ("-=?+", CSUBSTOP);       /* OP in ${paramOPword} */
}

static void
dump_lflags (fp, ind)
     FILE *fp;
     int ind;
{
  int xflags, first, i;

  xflags = lsyntax[ind];
  first = 1;

  if (xflags == 0)
    fputs (wordflags[0].fstr, fp);
  else
    {
      for (i = 1; i < N_WFLAGS; i++)
    if (xflags & wordflags[i].flag)
      {
        if (first)
          first = 0;
        else
          putc ('|', fp);
        fputs (wordflags[i].fstr, fp);
      }
    }
}

static void
wcomment (fp, i)
     FILE *fp;
     int i;
{
  fputs ("\t\t/* ", fp);

  fprintf (fp, "%s", cdesc(i));
      
  fputs (" */", fp);
}

static void
dump_lsyntax (fp)
     FILE *fp;
{
  int i;

  fprintf (fp, "const int sh_syntaxtab[%d] = {\n", SYNSIZE);

  for (i = 0; i < SYNSIZE; i++)
    {
      putc ('\t', fp);
      dump_lflags (fp, i);
      putc (',', fp);
      wcomment (fp, i);
      putc ('\n', fp);
    }

  fprintf (fp, "};\n");
}

int
main(argc, argv)
     int argc;
     char **argv;
{
  int opt, i;
  char *filename;
  FILE *fp;

  if ((progname = strrchr (argv[0], '/')) == 0)
    progname = argv[0];
  else
    progname++;

  filename = (char *)NULL;
  debug = 0;

  while ((opt = getopt (argc, argv, "do:")) != EOF)
    {
      switch (opt)
    {
    case 'd':
      debug = 1;
      break;
    case 'o':
      filename = optarg;
      break;
    default:
      usage();
    }
    }

  argc -= optind;
  argv += optind;

  if (filename)
    {
      fp = fopen (filename, "w");
      if (fp == 0)
    {
      fprintf (stderr, "%s: %s: cannot open: %s\n", progname, filename, strerror(errno));
      exit (1);
    }
    }
  else
    {
      filename = "stdout";
      fp = stdout;
    }


  for (i = 0; i < SYNSIZE; i++)
    lsyntax[i] = CWORD;

  load_lsyntax ();

  fprintf (fp, "%s\n", preamble);
  fprintf (fp, "%s\n", includes);

  dump_lsyntax (fp);

  if (fp != stdout)
    fclose (fp);
  exit (0);
}


#if !defined (HAVE_STRERROR)

#include <bashtypes.h>
#ifndef _MINIX
#  include <sys/param.h>
#endif

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

/* Return a string corresponding to the error number E.  From
   the ANSI C spec. */
#if defined (strerror)
#  undef strerror
#endif

char *
strerror (e)
     int e;
{
  static char emsg[40];
#if defined (HAVE_SYS_ERRLIST)
  extern int sys_nerr;
  extern char *sys_errlist[];

  if (e > 0 && e < sys_nerr)
    return (sys_errlist[e]);
  else
#endif /* HAVE_SYS_ERRLIST */
    {
      sprintf (emsg, "Unknown system error %d", e);
      return (&emsg[0]);
    }
}
#endif /* HAVE_STRERROR */

https://git.savannah.gnu.org/cgit/bash.git/tree/syntax.h?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/syntax.h used in: Step 1248
/* syntax.h -- Syntax definitions for the shell */

/* Copyright (C) 2000 Free Software Foundation, Inc.

   This file is part of GNU Bash, the Bourne Again SHell.

   Bash 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.

   Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#ifndef _SYNTAX_H_
#define _SYNTAX_H_

/* Defines for use by mksyntax.c */

#define slashify_in_quotes "\\`$\"\n"
#define slashify_in_here_document "\\`$"

#define shell_meta_chars   "()<>;&|"
#define shell_break_chars  "()<>;&| \t\n"

#define shell_quote_chars   "\"`'"

#if defined (PROCESS_SUBSTITUTION)
#  define shell_exp_chars       "$<>"
#else
#  define shell_exp_chars       "$"
#endif

#if defined (EXTENDED_GLOB)
#  define ext_glob_chars    "@*+?!"
#else
#  define ext_glob_chars    ""
#endif
#define shell_glob_chars    "*?[]^"

/* Defines shared by mksyntax.c and the rest of the shell code. */

/* Values for character flags in syntax tables */

#define CWORD       0x0000  /* nothing special; an ordinary character */
#define CSHMETA     0x0001  /* shell meta character */
#define CSHBRK      0x0002  /* shell break character */
#define CBACKQ      0x0004  /* back quote */
#define CQUOTE      0x0008  /* shell quote character */
#define CSPECL      0x0010  /* special character that needs quoting */
#define CEXP        0x0020  /* shell expansion character */
#define CBSDQUOTE   0x0040  /* characters escaped by backslash in double quotes */
#define CBSHDOC     0x0080  /* characters escaped by backslash in here doc */
#define CGLOB       0x0100  /* globbing characters */
#define CXGLOB      0x0200  /* extended globbing characters */
#define CXQUOTE     0x0400  /* cquote + backslash */
#define CSPECVAR    0x0800  /* single-character shell variable name */
#define CSUBSTOP    0x1000  /* values of OP for ${word[:]OPstuff} */

/* Defines for use by the rest of the shell. */
extern const int sh_syntaxtab[];

#define shellmeta(c)    (sh_syntaxtab[(unsigned char)(c)] & CSHMETA)
#define shellbreak(c)   (sh_syntaxtab[(unsigned char)(c)] & CSHBRK)
#define shellquote(c)   (sh_syntaxtab[(unsigned char)(c)] & CQUOTE)

#define issyntype(c, t) ((sh_syntaxtab[(unsigned char)(c)] & (t)) != 0)
#define notsyntype(c,t) ((sh_syntaxtab[(unsigned char)(c)] & (t)) == 0)

#if defined (PROCESS_SUBSTITUTION)
#  define shellexp(c)   ((c) == '$' || (c) == '<' || (c) == '>')
#else
#  define shellexp(c)   ((c) == '$')
#endif

#if defined (EXTENDED_GLOB)
#  define PATTERN_CHAR(c) \
    ((c) == '@' || (c) == '*' || (c) == '+' || (c) == '?' || (c) == '!')
#else
#  define PATTERN_CHAR(c) 0
#endif

#define GLOB_CHAR(c) \
    ((c) == '*' || (c) == '?' || (c) == '[' || (c) == ']' || (c) == '^')

#define CTLESC '\001'
#define CTLNUL '\177'

#endif /* _SYNTAX_H_ */

https://git.savannah.gnu.org/cgit/bash.git/tree/xmalloc.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/xmalloc.c used in: Step 1249
/* xmalloc.c -- safe versions of malloc and realloc */

/* Copyright (C) 1991 Free Software Foundation, Inc.

   This file is part of GNU Readline, a library for reading lines
   of text with interactive input and history editing.

   Readline 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.

   Readline 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 Readline; see the file COPYING.  If not, write to the Free
   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#if defined (HAVE_CONFIG_H)
#include <config.h>
#endif

#include "bashtypes.h"
#include <stdio.h>

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#if defined (HAVE_STDLIB_H)
#  include <stdlib.h>
#else
#  include "ansi_stdlib.h"
#endif /* HAVE_STDLIB_H */

#include "error.h"

#if !defined (PTR_T)
#  if defined (__STDC__)
#    define PTR_T void *
#  else
#    define PTR_T char *
#  endif /* !__STDC__ */
#endif /* !PTR_T */

#if defined (HAVE_SBRK) && !HAVE_DECL_SBRK
extern char *sbrk();
#endif

static PTR_T lbreak;
static int brkfound;
static size_t allocated;

/* **************************************************************** */
/*                                  */
/*         Memory Allocation and Deallocation.          */
/*                                  */
/* **************************************************************** */

#if defined (HAVE_SBRK)
static size_t
findbrk ()
{
  if (brkfound == 0)
    {
      lbreak = (PTR_T)sbrk (0);
      brkfound++;
    }
  return (char *)sbrk (0) - (char *)lbreak;
}
#endif

/* Return a pointer to free()able block of memory large enough
   to hold BYTES number of bytes.  If the memory cannot be allocated,
   print an error message and abort. */
PTR_T
xmalloc (bytes)
     size_t bytes;
{
  PTR_T temp;

  temp = malloc (bytes);

  if (temp == 0)
    {
#if defined (HAVE_SBRK)
      allocated = findbrk ();
      fatal_error ("xmalloc: cannot allocate %lu bytes (%lu bytes allocated)", (unsigned long)bytes, (unsigned long)allocated);
#else
      fatal_error ("xmalloc: cannot allocate %lu bytes", (unsigned long)bytes);
#endif /* !HAVE_SBRK */
    }

  return (temp);
}

PTR_T
xrealloc (pointer, bytes)
     PTR_T pointer;
     size_t bytes;
{
  PTR_T temp;

  temp = pointer ? realloc (pointer, bytes) : malloc (bytes);

  if (temp == 0)
    {
#if defined (HAVE_SBRK)
      allocated = findbrk ();
      fatal_error ("xrealloc: cannot reallocate %lu bytes (%lu bytes allocated)", (unsigned long)bytes, (unsigned long)allocated);
#else
      fatal_error ("xrealloc: cannot allocate %lu bytes", (unsigned long)bytes);
#endif /* !HAVE_SBRK */
    }

  return (temp);
}

/* Use this as the function to call when adding unwind protects so we
   don't need to know what free() returns. */
void
xfree (string)
     PTR_T string;
{
  if (string)
    free (string);
}

#ifdef USING_BASH_MALLOC
#include <malloc/shmalloc.h>

PTR_T
sh_xmalloc (bytes, file, line)
     size_t bytes;
     char *file;
     int line;
{
  PTR_T temp;

  temp = sh_malloc (bytes, file, line);

  if (temp == 0)
    {
#if defined (HAVE_SBRK)
      allocated = findbrk ();
      fatal_error ("xmalloc: %s:%d: cannot allocate %lu bytes (%lu bytes allocated)", file, line, (unsigned long)bytes, (unsigned long)allocated);
#else
      fatal_error ("xmalloc: %s:%d: cannot allocate %lu bytes", file, line, (unsigned long)bytes);
#endif /* !HAVE_SBRK */
    }

  return (temp);
}

PTR_T
sh_xrealloc (pointer, bytes, file, line)
     PTR_T pointer;
     size_t bytes;
     char *file;
     int line;
{
  PTR_T temp;

  temp = pointer ? sh_realloc (pointer, bytes, file, line) : sh_malloc (bytes, file, line);

  if (temp == 0)
    {
#if defined (HAVE_SBRK)
      allocated = findbrk ();
      fatal_error ("xrealloc: %s:%d: cannot reallocate %lu bytes (%lu bytes allocated)", file, line, (unsigned long)bytes, (unsigned long)allocated);
#else
      fatal_error ("xrealloc: %s:%d: cannot allocate %lu bytes", file, line, (unsigned long)bytes);
#endif /* !HAVE_SBRK */
    }

  return (temp);
}

void
sh_xfree (string, file, line)
     PTR_T string;
     char *file;
     int line;
{
  if (string)
    sh_free (string, file, line);
}
#endif

https://git.savannah.gnu.org/cgit/bash.git/tree/siglist.c?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55

File: ../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/siglist.c used in: Step 1250
/* siglist.c -- signal list for those machines that don't have one. */

/* Copyright (C) 1989 Free Software Foundation, Inc.

This file is part of GNU Bash, the Bourne Again SHell.

Bash 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.

Bash 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 Bash; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#include "config.h"

#if !defined (HAVE_SYS_SIGLIST) && !defined (HAVE_UNDER_SYS_SIGLIST) && !defined (HAVE_STRSIGNAL)

#include <stdio.h>
#include "bashtypes.h"
#include <signal.h>

#include "siglist.h"

#if !defined (NSIG)
#  include "trap.h"
#endif

#include "xmalloc.h"

char *sys_siglist[NSIG];

void
initialize_siglist ()
{
  register int i;

  for (i = 0; i < NSIG; i++)
    sys_siglist[i] = (char *)0x0;

  sys_siglist[0] = "Bogus signal";

#if defined (SIGHUP)
  sys_siglist[SIGHUP] = "Hangup";
#endif

#if defined (SIGINT)
  sys_siglist[SIGINT] = "Interrupt";
#endif

#if defined (SIGQUIT)
  sys_siglist[SIGQUIT] = "Quit";
#endif

#if defined (SIGILL)
  sys_siglist[SIGILL] = "Illegal instruction";
#endif

#if defined (SIGTRAP)
  sys_siglist[SIGTRAP] = "BPT trace/trap";
#endif

#if defined (SIGIOT) && !defined (SIGABRT)
#define SIGABRT SIGIOT
#endif

#if defined (SIGABRT)
  sys_siglist[SIGABRT] = "ABORT instruction";
#endif

#if defined (SIGEMT)
  sys_siglist[SIGEMT] = "EMT instruction";
#endif

#if defined (SIGFPE)
  sys_siglist[SIGFPE] = "Floating point exception";
#endif

#if defined (SIGKILL)
  sys_siglist[SIGKILL] = "Killed";
#endif

#if defined (SIGBUS)
  sys_siglist[SIGBUS] = "Bus error";
#endif

#if defined (SIGSEGV)
  sys_siglist[SIGSEGV] = "Segmentation fault";
#endif

#if defined (SIGSYS)
  sys_siglist[SIGSYS] = "Bad system call";
#endif

#if defined (SIGPIPE)
  sys_siglist[SIGPIPE] = "Broken pipe";
#endif

#if defined (SIGALRM)
  sys_siglist[SIGALRM] = "Alarm clock";
#endif

#if defined (SIGTERM)
  sys_siglist[SIGTERM] = "Terminated";
#endif

#if defined (SIGURG)
  sys_siglist[SIGURG] = "Urgent IO condition";
#endif

#if defined (SIGSTOP)
  sys_siglist[SIGSTOP] = "Stopped (signal)";
#endif

#if defined (SIGTSTP)
  sys_siglist[SIGTSTP] = "Stopped";
#endif

#if defined (SIGCONT)
  sys_siglist[SIGCONT] = "Continue";
#endif

#if !defined (SIGCHLD) && defined (SIGCLD)
#define SIGCHLD SIGCLD
#endif

#if defined (SIGCHLD)
  sys_siglist[SIGCHLD] = "Child death or stop";
#endif

#if defined (SIGTTIN)
  sys_siglist[SIGTTIN] = "Stopped (tty input)";
#endif

#if defined (SIGTTOU)
  sys_siglist[SIGTTOU] = "Stopped (tty output)";
#endif

#if defined (SIGIO)
  sys_siglist[SIGIO] = "I/O ready";
#endif

#if defined (SIGXCPU)
  sys_siglist[SIGXCPU] = "CPU limit";
#endif

#if defined (SIGXFSZ)
  sys_siglist[SIGXFSZ] = "File limit";
#endif

#if defined (SIGVTALRM)
  sys_siglist[SIGVTALRM] = "Alarm (virtual)";
#endif

#if defined (SIGPROF)
  sys_siglist[SIGPROF] = "Alarm (profile)";
#endif

#if defined (SIGWINCH)
  sys_siglist[SIGWINCH] = "Window changed";
#endif

#if defined (SIGLOST)
  sys_siglist[SIGLOST] = "Record lock";
#endif

#if defined (SIGUSR1)
  sys_siglist[SIGUSR1] = "User signal 1";
#endif

#if defined (SIGUSR2)
  sys_siglist[SIGUSR2] = "User signal 2";
#endif

#if defined (SIGMSG)
  sys_siglist[SIGMSG] = "HFT input data pending";
#endif

#if defined (SIGPWR)
  sys_siglist[SIGPWR] = "power failure imminent";
#endif

#if defined (SIGDANGER)
  sys_siglist[SIGDANGER] = "system crash imminent";
#endif

#if defined (SIGMIGRATE)
  sys_siglist[SIGMIGRATE] = "migrate process to another CPU";
#endif

#if defined (SIGPRE)
  sys_siglist[SIGPRE] = "programming error";
#endif

#if defined (SIGGRANT)
  sys_siglist[SIGGRANT] = "HFT monitor mode granted";
#endif

#if defined (SIGRETRACT)
  sys_siglist[SIGRETRACT] = "HFT monitor mode retracted";
#endif

#if defined (SIGSOUND)
  sys_siglist[SIGSOUND] = "HFT sound sequence has completed";
#endif

#if defined (SIGINFO)
  sys_siglist[SIGINFO] = "Information request";
#endif

  for (i = 0; i < NSIG; i++)
    {
      if (!sys_siglist[i])
    {
      sys_siglist[i] =
        (char *)xmalloc (10 + strlen ("Unknown Signal #"));

      sprintf (sys_siglist[i], "Unknown Signal #%d", i);
    }
    }
}
#endif /* !HAVE_SYS_SIGLIST && !HAVE_UNDER_SYS_SIGLIST && !HAVE_STRSIGNAL */

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/bash-2.05b/bash-2.05b.checksums

File: ../git/live-bootstrap/sysa/bash-2.05b/bash-2.05b.checksums used in: Step 1258
a11ab8528cd79c67a0514ce2581ee81f50398cea1b4c616a22c488c0cc2d619e  /usr/bin/bash

https://github.com/fosslinux/live-bootstrap/blob/master/sysa/run.sh

File: ../git/live-bootstrap/sysa/run.sh used in: Step 1259
#!/bin/bash

# SPDX-FileCopyrightText: 2022 Andrius Å tikonas <andrius@stikonas.eu>
# SPDX-FileCopyrightText: 2021-22 fosslinux <fosslinux@aussies.space>
# SPDX-FileCopyrightText: 2021 Paul Dersey <pdersey@gmail.com>
#
# SPDX-License-Identifier: GPL-3.0-or-later

set -e

# shellcheck disable=SC2154
PREFIX="${prefix}"
LIBDIR="${PREFIX}/lib/mes"
# shellcheck disable=SC2154
SOURCES="${sysa}"
DISTFILES="${sysa}/distfiles"
DESTDIR=/tmp/destdir
# shellcheck disable=SC2154
SRCDIR="${srcdir}"

# shellcheck source=sysa/helpers.sh
. helpers.sh

if [ "${KERNEL_BOOTSTRAP}" ]; then
    # Fiwix does not support SMP
    MAKEJOBS="-j1"
else
    MAKEJOBS="-j${JOBS}"
fi

# Ask some questions
echo
echo "Now that bash has been built, there are potentially some questions for you!"
echo "To give your answer, type your answer, press Enter, and then Control-D."
echo

ask_chroot() {
    read -r CHROOT_STRING
    if [ "${CHROOT_STRING}" = "yes" ] || [ "${CHROOT_STRING}" = "y" ]; then
        CHROOT=True
    elif [ "${CHROOT_STRING}" = "no" ] || [ "${CHROOT_STRING}" = "n" ]; then
        CHROOT=False
    else
        echo "Invalid response. Please give a yes/no answer."
        ask_chroot
    fi
}

if [ -z "${CHROOT}" ]; then
    echo "Currently, it is unknown if live-bootstrap is running in a chroot"
    echo "or not. Is it? (yes/no answer)"
    ask_chroot
    echo

    echo "CHROOT=${CHROOT}" >> "${SOURCES}/bootstrap.cfg"
fi

ask_timestamps() {
    read -r TIMESTAMPS_STRING
    if [ "${TIMESTAMPS_STRING}" = "yes" ] || [ "${TIMESTAMPS_STRING}" = "y" ]; then
        FORCE_TIMESTAMPS=True
    elif [ "${TIMESTAMPS_STRING}" = "no" ] || [ "${TIMESTAMPS_STRING}" = "n" ]; then
        FORCE_TIMESTAMPS=False
    else
        echo "Invalid response. Please give a yes/no answer."
        ask_timestamps
    fi
}

if [ -z "${FORCE_TIMESTAMPS}" ]; then
    echo "Would you like all timestamps to be set to unix time 0"
    echo "(Jan 1 1970 00:00) at the end of the bootstrap? This makes a"
    echo "fully reproducible disk image. (yes/no answer)"
    ask_timestamps
    echo

    echo "FORCE_TIMESTAMPS=${FORCE_TIMESTAMPS}" >> "${SOURCES}/bootstrap.cfg"
fi

echo "Thank you! All done."

echo "ARCH=${ARCH}" >> "${SOURCES}/bootstrap.cfg"

mkdir -p "${DESTDIR}" "${SRCDIR}/repo" /dev

build flex-2.5.11

# Rebuild tcc with some patches
build tcc-0.9.27 tcc-mes-pass2.sh

# shellcheck disable=SC2034
LIBDIR="${PREFIX}/lib/i386-unknown-linux-musl"

build musl-1.1.24

# Rebuild tcc using musl
build tcc-0.9.27 tcc-musl-pass1.sh

# Rebuild musl using tcc-musl
build musl-1.1.24

# Rebuild tcc-musl using new musl
build tcc-0.9.27 tcc-musl-pass2.sh

# Rebuild sed using musl
build sed-4.0.9 sed-4.0.9.sh

# Rebuild bzip2 using musl
build bzip2-1.0.8 bzip2-1.0.8.sh

build m4-1.4.7

build flex-2.6.4

build bison-3.4.1 stage1.sh
build bison-3.4.1 stage2.sh
build bison-3.4.1 stage3.sh

build grep-2.4

build diffutils-2.7

# Rebuild coreutils using musl
build coreutils-5.0 coreutils-5.0.sh patches-musl

# Build only date, mktemp and sha256sum
build coreutils-6.10

build gawk-3.0.4

build perl-5.000

build perl-5.003

build perl5.004-05 '' '' perl5.004_05

build perl5.005-03 '' '' perl5.005_03

build perl-5.6.2

populate_device_nodes

build autoconf-2.52

build automake-1.6.3 stage1.sh
build automake-1.6.3 stage2.sh

build autoconf-2.53

build automake-1.7

build autoconf-2.54

build autoconf-2.55

build automake-1.7.8

build autoconf-2.57

build autoconf-2.59

build automake-1.8.5

build help2man-1.36.4

build autoconf-2.61

build automake-1.9.6

build automake-1.10.3

build autoconf-2.64

build automake-1.11.2

build autoconf-2.69

build libtool-2.2.4

cat > .env <<- EOF
export PATH=${PATH}
PREFIX=${PREFIX}
LIBDIR=${LIBDIR}
SOURCES=${SOURCES}
DESTDIR=${DESTDIR}
DISTFILES=${DISTFILES}
SRCDIR=${SRCDIR}
MAKEJOBS=${MAKEJOBS}
EOF

exec env -i bash run2.sh

Parse program

Below the version of the kaem_parser.cpp program is given that is used to produce this page.

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <cerrno>

FILE *debug_out = 0;

// Some generic helper functions:

char *copystr(const char *str)
{
    char *new_str = (char*)malloc(strlen(str) + 1);
    strcpy(new_str, str);
    return new_str;
}

// C Preprocessor iterators

#define CPP_MAX_PATH_SIZE 1000

class AbstractIterator
{
public:
    bool more;
    char ch;
    long line;
    long column;
    char *filename;
    virtual void next() = 0;
};

class StringIterator : public AbstractIterator
{
public:
    StringIterator(const char *s, const char *fn)
    {
        filename = copystr(fn);
        _s = s;
        line = 1;
        column = 1;
        ch = *_s++;
        more = ch != '\0';
    }
    ~StringIterator() { delete filename; }
    void next()
    {
        if (ch == '\0')
            return;
        if (ch == '\n')
        {
            line++;
            column = 1;
        }
        else
            column++;
        ch = *_s++;
        if (ch == '\r')
            ch = *_s++;
        more = ch != '\0';
    }
private:
    const char *_s;
};

/*
class FileIterator : public AbstractIterator
{
public:
    FileIterator(const char *fn)
    {
        filename = copystr(fn);
        _f = fopen(filename, "r");
        more = _f != 0;
        ch = '\n';
        line = 0;
        column = 0;
        next();
    }
    FileIterator(const char *fn, FILE *f)
    {
        filename = copystr(fn);
        _f = f;
        more = _f != 0;
        ch = '\n';
        line = 0;
        column = 0;
        next();
    }
    ~FileIterator() { delete filename; }
    void next()
    {
        if (!more)
            return;
        if (ch == '\n')
        {
            line++;
            column = 0;
        }
        column++;
        ch = fgetc(_f);
        more = !feof(_f);
        if (!more)
        {
            ch = '\0';
            fclose(_f);
            _f = 0;
        }
        else if (ch == '\r')
            next();
    }
private:
    FILE *_f;
};
*/

bool debug_c_parsing = false;

class TrigraphIterator : public AbstractIterator
{
public:
    TrigraphIterator(AbstractIterator *source_it) : _source_it(source_it)
    {
        filename = _source_it->filename;
        _a[0] = _source_it->ch;
        _source_it->next();
        _a[1] = _source_it->ch;
        ch = '\n';
        line = 0;
        more = true;
        _next_column = 1;
        next();
    }
    void next()
    {
        if (_a[0] == '\0')
        {
            more = false;
            ch = '\0';
            return;
        }
        if (ch == '\n')
        {
            line++;
            column = 1;
        }
        else
            column = _next_column;
        ch = _a[0];
        _a[0] = _a[1];
        _source_it->next();
        _a[1] = _source_it->ch;
        if (ch == '?' && _a[0] == '?')
        {
            char rch = '\0';
                 if (_a[1] == '=') rch = '#';
            else if (_a[1] == '/') rch = '\\';
            else if (_a[1] ==  39) rch = '^';
            else if (_a[1] == '(') rch = '[';
            else if (_a[1] == ')') rch = ']';
            else if (_a[1] == '!') rch = '|';
            else if (_a[1] == '<') rch = '{';
            else if (_a[1] == '>') rch = '}';
            else if (_a[1] == '-') rch = '~';
            if (rch != '\0')
            {
                ch = rch;
                _next_column = column + 3;
                
                _source_it->next();
                _a[0] = _source_it->ch;
                _source_it->next();
                _a[1] = _source_it->ch;
                return;
            }
        }
        _next_column = column + 1;
    }
private:
    AbstractIterator *_source_it;
    char _a[2];
    long _next_column;
};

class LineSpliceIterator : public AbstractIterator
{
public:
    LineSpliceIterator(AbstractIterator *source_it) : _source_it(source_it)
    {
        filename = _source_it->filename;
        _a = _source_it->ch;
        next();
    }
    void next()
    {
        if (_a == '\0')
        {
            more = false;
            ch = '\0';
            return;
        }
        if (_a == ' ' || _a == '\t')
        {
            line = _source_it->line;
            column = _source_it->column;
            while (_a == ' ' || _a == '\t')
            {
                _source_it->next();
                _a = _source_it->ch;
            }
            ch = ' ';
            return;
        }
        ch = _a;
        line = _source_it->line;
        column = _source_it->column;
        _source_it->next();
        _a = _source_it->ch;
        while (ch == '\\' && _a == '\n')
        {
            _source_it->next();
            ch = _source_it->ch;
            line = _source_it->line;
            column = _source_it->column;
            _source_it->next();
            _a = _source_it->ch;
        }
        more = ch != '\0';
    }
private:
    AbstractIterator *_source_it;
    char _a;
};

class CommentStripIterator : public AbstractIterator
{
public:
    CommentStripIterator(AbstractIterator *source_it) : _source_it(source_it)
    {
        filename = _source_it->filename;
        _a = _source_it->ch;
        _state = 0;
        more = true;
        next();
    }
    void next()
    {
        switch(_state)
        {
            case 0: goto s0;
            case 1: goto s1;
            case 2: goto s2;
            case 3: goto s3;
            case 4: goto s4;
            case 5: goto s5;
            case 6: goto s6;
            case 7: goto s7;
            case 8: goto s8;
            case 9: goto s9;
        }
        s0:
        if (_a == '\0')
        {
            more = false;
            ch = '\0';
            return;
        }
        ch = _a;
        line = _source_it->line;
        column = _source_it->column;
        _source_it->next();
        _a = _source_it->ch;
        if (ch == '/' && (_a == '/' || _a == '*'))
        {
            if (_a == '/')
            {
                while (_a != '\0' && _a != '\n')
                {
                    _source_it->next();
                    _a = _source_it->ch;
                }
            }
            else
            {
                ch = ' ';
                _state = 1; return; s1:
                _source_it->next();
                ch = _source_it->ch;
                _source_it->next();
                _a = _source_it->ch;
                while (ch != '\0' && (ch != '*' || _a != '/'))
                {
                    ch = _a;
                    _source_it->next();
                    _a = _source_it->ch;
                }
                if (ch != '\0')
                {
                    _source_it->next();
                    _a = _source_it->ch;
                    line = _source_it->line;
                    column = _source_it->column;
                }
            }
            _state = 0;
            goto s0;
        }
        if (ch == '"')
        {
            _state = 2; return; s2:
            ch = _a;
            line = _source_it->line;
            column = _source_it->column;
            
            while (ch != '\0' && ch != '"')
            {
                if (ch == '\\')
                {
                    _state = 3; return; s3:
                    _source_it->next();
                    ch = _source_it->ch;
                    line = _source_it->line;
                    column = _source_it->column;
                }
                
                _state = 4; return; s4:
                _source_it->next();
                ch = _source_it->ch;
                line = _source_it->line;
                column = _source_it->column;
            }
            if (ch == '"')
            {
                _state = 5; return; s5:
                _source_it->next();
                _a = _source_it->ch;
                line = _source_it->line;
                column = _source_it->column;
            }
            _state = 0;
            goto s0;
        }
        if (ch == '\'')
        {
            _state = 6; return; s6:
            ch = _a;
            line = _source_it->line;
            column = _source_it->column;
            if (ch == '\\')
            {
                _state = 7; return; s7:
                _source_it->next();
                ch = _source_it->ch;
                line = _source_it->line;
                column = _source_it->column;
            }
            _state = 8; return; s8:
            _source_it->next();
            ch = _source_it->ch;
            line = _source_it->line;
            column = _source_it->column;
            _state = 0;
            if (ch == '\'')
            {
                _state = 9; return; s9:
                _source_it->next();
                _a = _source_it->ch;
                line = _source_it->line;
                column = _source_it->column;
            }
            _state = 0;
            goto s0;
        }
    }
private:
    AbstractIterator *_source_it;
    char _a;
    int _state;
};

class IncludePathResolver
{
};

class AbstractIncludePathResolver
{
public:
    virtual AbstractIterator *iteratorFor(const char *path, bool include_next, AbstractIterator *context) = 0;
};

class Define
{
public:
    char *name;
    bool defined;
    char *str_value;
    Define *next;
    Define(const char *_name, const char *value, Define *_next) : defined(true), next(_next), str_value(0)
    {
        name = copystr(_name);
        setValue(value);
    }
    ~Define()
    {
        free(name);
        if (str_value != 0) free(str_value);
        delete next;
    }
    int getIntValue()
    {
        if (!defined) return 0;
        if (str_value == 0) return 1;
        int sign = 1;
        char *s = str_value;
        if (*s == '-')
            sign = -1;
        int result = 0;
        while ('0' <= *s && *s <= '9')
            result = 10 * result + *s++ - '0';
        return sign * result;
    }
    void setValue(const char *value)
    {
        defined = true;
        if (str_value != 0) free(str_value);
        if (value == 0)
            str_value = 0;
        else
        {
            str_value = copystr(value);
        }
    }
    static void addDefine(Define *&defines, const char *name, const char *value)
    {
        for (Define *define = defines; define != 0; define = define->next)
            if (strcmp(define->name, name) == 0)
            {
                define->setValue(value);
                return;
            }
        defines = new Define(name, value, defines);
    }
    static void removeDefine(Define *defines, const char *name)
    {
        for (Define *define = defines; define != 0; define = define->next)
            if (strcmp(define->name, name) == 0)
            {
                define->defined = false;
                return;
            }
    }
    static bool isDefined(Define *defines, const char *name)
    {
        for (Define *define = defines; define != 0; define = define->next)
            if (strcmp(define->name, name) == 0)
                return define->defined;
        return false;
    }
    static Define *getDefine(Define *defines, const char *name)
    {
        for (Define *define = defines; define != 0; define = define->next)
            if (strcmp(define->name, name) == 0)
                return define;
        return 0;
    }
    
};


class InlineIncludesIterator : public AbstractIterator
{
public:
    InlineIncludesIterator(AbstractIterator *source_it, AbstractIncludePathResolver *includePathResolver, Define *&defines)
      : _source_it(source_it), _includePathResolver(includePathResolver), _defines(defines)
    {
        filename = _source_it->filename;
        ch = _source_it->ch;
        line = _source_it->line;
        column = _source_it->column;
        more = true;
        _state = 0;
        _if_nesting = -1;
        _skip_level = 0;
        next();
    }
    void next()
    {
        switch(_state)
        {
            case 0: goto s0;
            case 1: goto s1;
            case 2: goto s2;
            case 3: goto s3;
            case 4: goto s4;
            case 5: goto s5;
            case 6: goto s6;
            case 7: goto s7;
            case 8: goto s8;
            case 9: goto s9;
            case 10: goto s10;
            case 11: goto s11;
            case 12: goto s12;
            case 13: goto s13;
            case 14: goto s14;
        }
        s0:
        while (ch != '\0')
        {
            _state = 0; //printf("ch = '%c'", ch);
            while (ch == ' ')
            {
                if (_skip_level == 0)
                {
                    _state = 1; return; s1:;
                }
                _source_it->next(); 
                ch = _source_it->ch;
                line = _source_it->line;
                column = _source_it->column;
            }
            if (ch == '#')
            {
                _i = 0;
                _source_it->next();
                ch = _source_it->ch;
                line = _source_it->line;
                column = _source_it->column;
                _name[0] = '#';
                _i = 1;
                while (ch == ' ' || ch == '\t')
                {
                    _source_it->next(); 
                    ch = _source_it->ch;
                    line = _source_it->line;
                    column = _source_it->column;
                }
                while (('a' <= ch && ch <= 'z') || ch == '_')
                {
                    if (_i < 19)
                        _name[_i++] = ch;
                    _source_it->next(); 
                    ch = _source_it->ch;
                    line = _source_it->line;
                    column = _source_it->column;
                }
                _name[_i] = '\0';
                if (debug_c_parsing && (_skip_level == 0 || (_skip_level == 1 && (strcmp(_name, "#endif") == 0 || strcmp(_name, "#else") == 0 || strcmp(_name, "#elif") == 0))))
                    fprintf(debug_out, "%s.%ld Directive |%s|\n", filename, line, _name);

                if (strcmp(_name, "#if") == 0 || strcmp(_name, "#elif") == 0)
                {
                    bool exec_if = false;
                    if (strcmp(_name, "#if") == 0)
                    {
                        if (_skip_level > 0)
                            _skip_level++;
                        else
                        {
                            _if_nesting++;
                            exec_if = true;
                        }
                    }
                    else // elif
                    {
                        if (_if_state[_if_nesting] == 't')
                        {
                            if (_skip_level == 1)
                            {
                                _skip_level = 0;
                                exec_if = true;
                            }
                        }
                        else if (_if_state[_if_nesting] == 'T')
                        {
                            if (_skip_level == 0)
                            {
                                _if_state[_if_nesting] == 'd';
                            }
                        }
                        else if (_if_state[_if_nesting] == 'd')
                        {
                            // skip
                        }
                    }
                    if (exec_if)
                    {
                        int i = 0;
                        while (ch != '\0' && ch != '\n')
                        {
                            if (_i < 10000)
                                _value[i++] = ch;
                            _source_it->next();
                            ch = _source_it->ch;
                        }
                        _value[i] = '\0';
                        skip_space();
                        //fprintf(stderr, "Parse: |%s|\n", _value);
                        const char *s = _value;
                        if (evaluate_cond(s) != 0)
                            _if_state[_if_nesting] = 'T';
                        else
                        {
                            _if_state[_if_nesting] = 't';
                            _skip_level = 1;
                        }
                        if (*s != '\0')
                            fprintf(stderr, "Error: %s:%ld parsing |%s|%s|\n", filename, line, _value, s);
                        //fprintf(stderr, "Done\n");                        
                    }
                }
                else if (strcmp(_name, "#ifdef") == 0)
                {
                    if (_skip_level > 0)
                        _skip_level++;
                    else
                    {
                        skip_space();
                        parse_ident(_name, 100);
                        _if_nesting++;
                        if (_name[0] != '\0' && Define::isDefined(_defines, _name))
                            _if_state[_if_nesting] = 'T';
                        else
                        {
                            _if_state[_if_nesting] = 't';
                            _skip_level = 1;
                        }
                    }
                }
                else if (strcmp(_name, "#ifndef") == 0)
                {
                    if (_skip_level > 0)
                        _skip_level++;
                    else
                    {
                        skip_space();
                        parse_ident(_name, 100);
                        _if_nesting++;
                        if (_name[0] != '\0' && !Define::isDefined(_defines, _name))
                            _if_state[_if_nesting] = 'T';
                        else
                        {
                            _if_state[_if_nesting] = 't';
                            _skip_level = 1;
                        }
                    }
                }
                else if (strcmp(_name, "#else") == 0)
                {
                    if (_if_state[_if_nesting] == 't')
                    {
                        if (_skip_level == 1)
                        {
                            _skip_level = 0;
                            _if_state[_if_nesting] = 'E';
                        }
                    }
                    else if (_if_state[_if_nesting] == 'T')
                    {
                        if (_skip_level == 0)
                        {
                            _if_state[_if_nesting] = 'e';
                            _skip_level++;
                        }
                    }
                    else if (_if_state[_if_nesting] == 'd')
                    {
                        // skip
                    }
                }
                else if (strcmp(_name, "#endif") == 0)
                {
                    if (_if_state[_if_nesting] == 'e' || _if_state[_if_nesting] == 't' || _if_state[_if_nesting] == 'd')
                    {
                        if (_skip_level == 1)
                        {
                            _skip_level = 0;
                            _if_nesting--;
                        }
                        else
                        {
                             if (_skip_level == 0)
                                fprintf(stderr, "ERROR: Expect _skip_level > 0\n");
                             else
                                _skip_level--;
                        }
                    }
                    else if (_if_state[_if_nesting] == 'E' || _if_state[_if_nesting] == 'T')
                    {
                        if (_skip_level == 0)
                            _if_nesting--;
                        else
                            _skip_level--;
                    }
                }
                else if (_skip_level > 0)
                    ; // skip all other directives
                else if ((strcmp(_name, "#include") == 0/* || strcmp(_name, "#include_next") == 0*/)&& (ch == ' ' || ch == '"' || ch == '<'))
                {
                    //printf("Include ");
                    skip_space();

                    {
                        bool include_next = strcmp(_name, "#include_next") == 0;
                        char include_fn[CPP_MAX_PATH_SIZE];
                        include_fn[0] = ch;
                        int _i = 1;
                        char _end_quote = ch == '<' ? '>' : '"';
                        _source_it->next();
                        ch = _source_it->ch;
                        while (ch != '\0' && ch != _end_quote)
                        {
                            include_fn[_i++] = ch;
                            _source_it->next();
                            ch = _source_it->ch;
                        }
                        if (ch == _end_quote)
                            include_fn[_i++] = _end_quote;
                        include_fn[_i] = '\0';
                        //printf("Filename |%s|", filename);
                        
                        _nested_iterator = _includePathResolver->iteratorFor(include_fn, include_next, this);
                    }
                    
                    //printf("Begin");
                    while (_nested_iterator->ch != '\0')
                    {
                        ch = _nested_iterator->ch;
                        filename = _nested_iterator->filename;
                        line = _nested_iterator->line;
                        column = _nested_iterator->column;
                        _state = 2; return; s2:
                        _nested_iterator->next();
                    }
                    //printf("End");
                    
                    delete _nested_iterator;
                    
                    filename = _source_it->filename;
                    
                    ch = _source_it->ch;
                    while (ch != '\0' && ch != '\n')
                    {
                        //printf("Skip '%c'", ch);
                        _source_it->next();
                        ch = _source_it->ch;
                    }
                    if (ch == '\n')
                    {
                        //printf("Skip newline");
                        _source_it->next();
                        ch = _source_it->ch;
                        line = _source_it->line;
                        column = _source_it->column;
                        continue;
                    }
                }
                else if (strcmp(_name, "#define") == 0)
                {
                    _i = 0;
                    while (_name[_i] != '\0')
                    {
                        ch = _name[_i++];
                        _state = 3; return; s3:;
                    }
                    ch = _source_it->ch;
                    line = _source_it->line;
                    column = _source_it->column;

                    // skip spaces
                    while (ch == ' ')
                    {
                        _state = 4; return; s4:
                        _source_it->next();
                        ch = _source_it->ch;
                        line = _source_it->line;
                        column = _source_it->column;
                    }
                    
                    // parse name of define
                    _i = 0;
                    while (is_ident())
                    {
                        if (_i < 100)
                            _name[_i++] = ch;
                        _state = 5; return; s5:
                        _source_it->next();
                        ch = _source_it->ch;
                        line = _source_it->line;
                        column = _source_it->column;
                    }
                    _name[_i] = '\0';

                    // skip spaces
                    while (ch == ' ')
                    {
                        _state = 6; return; s6:
                        _source_it->next();
                        ch = _source_it->ch;
                        line = _source_it->line;
                        column = _source_it->column;
                    }
                    
                    // parse value of define
                    _i = 0;
                    while (ch != '\0' && ch != '\n')
                    {
                        if (_i < 10000)
                            _value[_i++] = ch;
                        _state = 7; return; s7:
                        _source_it->next();
                        ch = _source_it->ch;
                        line = _source_it->line;
                        column = _source_it->column;
                    }
                    _value[_i] = '\0';
                    if (_i == 0)
                        strcpy(_value, "1");
                    
                    if (_name[0] != '\0')
                    {
                        if (debug_c_parsing) fprintf(debug_out, "#define |%s| |%s|\n", _name, _value);
                        Define::addDefine(_defines, _name, _value);
                    }   
                }
                else if (strcmp(_name, "#undef") == 0)
                {
                    _i = 0;
                    while (_name[_i] != '\0')
                    {
                        ch = _name[_i++];
                        _state = 8; return; s8:;
                    }
                    ch = _source_it->ch;
                    line = _source_it->line;
                    column = _source_it->column;

                    // skip spaces
                    while (ch == ' ')
                    {
                        _state = 9; return; s9:
                        _source_it->next();
                        ch = _source_it->ch;
                        line = _source_it->line;
                        column = _source_it->column;
                    }
                    
                    // parse name of define
                    _i = 0;
                    while (is_ident())
                    {
                        if (_i < 100)
                            _name[_i++] = ch;
                        _state = 10; return; s10:
                        _source_it->next();
                        ch = _source_it->ch;
                        line = _source_it->line;
                        column = _source_it->column;
                    }
                    _name[_i] = '\0';
                    if (_name[0] != '\0')
                        Define::removeDefine(_defines, _name);
                }
                else if (strcmp(_name, "#line") == 0)
                {
                    _i = 0;
                    while (_name[_i] != '\0')
                    {
                        ch = _name[_i++];
                        _state = 11; return; s11:;
                    }
                    ch = _source_it->ch;
                    line = _source_it->line;
                    column = _source_it->column;
                }
                else
                {
                    if (strcmp(_name, "#warning") != 0 && strcmp(_name, "#pragma") != 0 && strcmp(_name, "#error") != 0)
                        fprintf(stderr, "Unknown %s.%ld unknown redirective |%s|\n", filename, line, _name);
                    _i = 0;
                    while (_name[_i] != '\0')
                    {
                        ch = _name[_i++];
                        _state = 12; return; s12:;
                    }
                    ch = _source_it->ch;
                    line = _source_it->line;
                    column = _source_it->column;
                }                   
            }
            while (ch != '\0' && ch != '\n')
            {
                if (_skip_level == 0)
                {   
                    _state = 13; return; s13:;
                }
                _source_it->next();
                ch = _source_it->ch;
                line = _source_it->line;
                column = _source_it->column;
            }
            if (ch == '\n')
            {
                if (_skip_level == 0)
                {   
                    _state = 14; return; s14:;
                }
                _source_it->next();
                ch = _source_it->ch;
                line = _source_it->line;
                column = _source_it->column;
            }
        }
        more = false;   
    }
private:
    void skip_space()
    {
        while (ch == ' ' || ch == '\t')
        {
            //printf("Space ");
            _source_it->next();
            ch = _source_it->ch;
            line = _source_it->line;
            column = _source_it->column;
        }
    }
    void parse_ident(char *name, int len)
    {
        _i = 0;
        while (is_ident())
        {
            if (_i < len)
                name[_i++] = ch;
            _source_it->next();
            ch = _source_it->ch;
            line = _source_it->line;
            column = _source_it->column;
        }
        name[_i] = '\0';
    }
    bool is_ident()
    {
        return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ch == '_' || (_i > 0 && '0' <= ch && ch <= '9');
    }
    bool is_ident_start(char ch) { return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ch == '_'; }
    bool is_ident_next(char ch) { return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ch == '_' || ('0' <= ch && ch <= '9'); }
    
    int evaluate_cond(const char *&s)
    {
        if (debug_c_parsing) fprintf(debug_out, "cond: %s\n", s);
        int v = evaluate_or(s);
        
        if (*s == '?')
        {
            s++;
            int then_v = evaluate_or(s);
            int else_v = 0;
            if (*s == ':')
            {
                s++;
                else_v = evaluate_cond(s);
            }
            return v != 0 ? then_v : else_v;
        }
        if (debug_c_parsing) fprintf(debug_out, "cond: %s = %d\n", s, v);
        return v;
    }
    int evaluate_or(const char *&s)
    {
        //fprintf(stderr, "or: %s\n", s);
        int v = evaluate_and(s);
        while (s[0] == '|' && s[1] == '|')
        {
            s += 2;
            int n_v = evaluate_cmp(s);
            v = v || n_v;
        }
        return v;
    }
    int evaluate_and(const char *&s)
    {
        //fprintf(stderr, "and: %s\n", s);
        int v = evaluate_cmp(s);
        while (s[0] == '&' && s[1] == '&')
        {
            s += 2;
            int n_v = evaluate_cmp(s);
            v = v && n_v;
        }
        return v;
    }
    int evaluate_cmp(const char *&s)
    {
        //fprintf(stderr, "cmp: %s\n", s);
        int v = evaluate_bshift(s);
        if (s[0] == '>' && s[1] == '=') { s += 2; return v >= evaluate_bshift(s); }
        if (s[0] == '>')                { s++;    return v >  evaluate_bshift(s); }
        if (s[0] == '<' && s[1] == '=') { s += 2; return v <= evaluate_bshift(s); }
        if (s[0] == '<')                { s++;    return v <  evaluate_bshift(s); }
        if (s[0] == '=' && s[1] == '=') { s += 2; return v == evaluate_bshift(s); }
        if (s[0] == '!' && s[1] == '=') { s += 2; return v != evaluate_bshift(s); }
        return v;
    }
    int evaluate_bshift(const char *&s)
    {
        int v = evaluate_add(s);
        if (s[0] == '>' && s[1] == '>') { s += 2; return v >= evaluate_add(s); }
        if (s[0] == '<' && s[1] == '<') { s += 2; return v <= evaluate_add(s); }
        return v;
    }   
    int evaluate_add(const char *&s)
    {
        //fprintf(stderr, "add: %s\n", s);
        int v = evaluate_mul(s);
        for (;;)
            if (s[0] == '+')      { s++; v += evaluate_mul(s); }
            else if (s[0] == '-') { s++; v -= evaluate_mul(s); }
            else
                break;
        return v;
    }
    int evaluate_mul(const char *&s)
    {
        //fprintf(stderr, "mul: %s\n", s);
        int v = evaluate_term(s);
        for (;;)
            if (s[0] == '*')      { s++; v *= evaluate_term(s); }
            else if (s[0] == '/') { s++; int divider = evaluate_term(s); if (divider != 0) v /= divider; }
            else if (s[0] == '%') { s++; int divider = evaluate_term(s); if (divider != 0) v %= divider; }
            else
                break;
        return v;
    }
    int evaluate_term(const char *&s)
    {
        int result = 0;
        while (*s == ' ' || *s == '\t') s++;
        //fprintf(stderr, "term: %s\n", s);
        if (*s == '(')
        {
            s++;
            result = evaluate_cond(s);
            if (*s == ')')
                s++;
        }
        else if (is_ident_start(*s))
        {
            char id[101];
            int i = 0;
            for (; is_ident_next(*s); s++)
                if (i < 100)
                    id[i++] = *s;
            id[i] = '\0';
            //fprintf(stderr, "id: |%s|\n", id);
            if (strcmp(id, "defined") == 0)
            {
                while (*s == ' ' || *s == '\t') s++;
                int b = 0;
                while (*s == '(')
                {
                    b++;
                    s++;                    
                    while (*s == ' ' || *s == '\t') s++;
                }
                i = 0;
                for (; is_ident_next(*s); s++)
                    if (i < 100)
                        id[i++] = *s;
                id[i] = '\0';
                //fprintf(stderr, "defined |%s| |%s|\n", id, s);
                while (*s == ' ' || *s == '\t') s++;
                result = Define::isDefined(_defines, id);
                if (debug_c_parsing) fprintf(debug_out, "defined |%s| |%s| %d\n", id, s, result);
                while (*s == ')' && b > 0)
                {
                    b--;
                    s++;                    
                    while (*s == ' ' || *s == '\t') s++;
                }
                //fprintf(stderr, "left: |%s|\n", s);
            }
            else
            {
                if (debug_c_parsing) fprintf(debug_out, "Define |%s|\n", id);
                Define *define = Define::getDefine(_defines, id);
                result = define != 0 ? define->getIntValue() : 0;
                if (debug_c_parsing) fprintf(debug_out, "Value %d\n", result);
            }
        }
        else if ('0' <= *s && *s <= '9')
        {
            while ('0' <= *s && *s <= '9')
                result = 10 * result + *s++ - '0';
            if (*s == 'L') s++;
        }
        else if (*s == '\'')
        {
            s++;
            if (*s == '\\')
                s++;
            result = *s++;
            if (*s == '\'')
                s++;
        }
        else if (*s == '!')
        {
            s++;
            result = !evaluate_term(s);
        }
                
        while (*s == ' ' || *s == '\t') s++;
        return result;
    }
    AbstractIterator *_source_it;
    AbstractIncludePathResolver *_includePathResolver;
    Define *&_defines;
    int _state;
    char _name[101];
    char _value[100000];
    int _i;
    bool _if_state[50];
        // T - then: execute
        // t - then: skip
        // E - else: execute
        // e - else: skip
        // d - done
    int _if_nesting;
    char _end_quote;
    AbstractIterator *_nested_iterator;
    int _skip_level;
    bool _done;
};

#define MAX_LINE_SIZE 10000
#define MAX_PATH_SIZE 1000


class File;

int nr_sources = 0;
class Source
{
public:
    char *url;
    int nr;
    File *files;
    Source *next;
    
    Source(const char *u) : url(copystr(u)), files(0), next(0) { nr = nr_sources++; }
};

Source *sources = 0;

void add_source(const char *url, File *file);

class Usage;
class Step;

void map_filename(const char *fn, char *mapped_fn)
{
    mapped_fn[0] = '\0';
    if (strncmp(fn, "/sysa/", 6) == 0)
        strcpy(mapped_fn, "../git/live-bootstrap");
    strcat(mapped_fn, fn);
}

FILE *fopen_mapped(const char *fn, FILE *fout = 0)
{
    char mapped_fn[MAX_PATH_SIZE];
    mapped_fn[0] = '\0';
    if (strncmp(fn, "/sysa/", 6) == 0)
        strcpy(mapped_fn, "../git/live-bootstrap");
    strcat(mapped_fn, fn);
    if (fout != 0)
        fprintf(fout, "Info '%s' '%s'\n", fn, mapped_fn);
    return fopen(mapped_fn, "r");
}
        

class LineInFile
{
public:
    const char *text;
    File *file;
    long line;
    LineInFile *next;
    LineInFile(const char *t, File *f, long l) : text(copystr(t)), file(f), line(l), next(0) {}
    LineInFile(LineInFile *lif, int offset = 0) : text(lif->text + offset), file(lif->file), line(lif->line), next(0) {}
};

class MergeChild;

int nr_files = 0;
class File
{
public:
    char *name;
    int nr;
    bool exists;
    bool produced;
    bool modified;
    Step *produced_by;
    bool exec;
    bool used_as_input;
    bool removed;
    Source *source;
    File *next_source_file;
    File *alias;
    File *copy_from;
    Usage *usages;
    MergeChild *mergeChildren;
    bool loaded;
    LineInFile *lines;
    File *next;
    
    File(const char *fn) : exists(false), produced(false), produced_by(0), exec(false), next(0), source(0), next_source_file(0), alias(0), copy_from(0), usages(0), mergeChildren(0), loaded(false), lines(0)
    {
        name = copystr(fn);
        nr = nr_files++;
        if (strncmp(name, "/sysa/distfiles/", 16) == 0)
            exists = true; 
        else if (strncmp(name, "/usr/bin", 6) != 0)
        {
            FILE *f = fopen_mapped(name);
            if (f != 0)
            {
                exists = true;
                fclose(f);
            }
            else if (errno != 2)
                fprintf(stderr, "Errno %d for %s\n", errno, name);
        }
    }

    File *existing()
    {
        for (File *f = this; f != 0; f = f->copy_from)
            if (f->exists || f->loaded)
                return f;
        return 0;
    }
    
    bool exists_now() { return !removed && (exists || produced); }
    
    void set_source(const char *url)
    {
        Source **ref_source = &sources;
        for (; *ref_source != 0; ref_source = &(*ref_source)->next)
            if (strcmp((*ref_source)->url, url) == 0)
                break;
        if (*ref_source == 0)
        {
            *ref_source = new Source(url);
        }
        source = *ref_source;
        File **ref_file = &(*ref_source)->files;
        while (*ref_file != 0)
            ref_file = &(*ref_file)->next_source_file;
        *ref_file = this;
    }
    
    void load(FILE *fout)
    {
        if (loaded) return;
        loaded = true;
        FILE *f = fopen_mapped(name, fout);
        if (f == 0)
        {
            fprintf(fout, "Failed to load |%s|\n", name);
            if (copy_from != 0)
            {
                copy_from->load(fout);
                lines = copy_from->lines;
            }
            return;
        }
        LineInFile **ref_line = &lines;
        char buffer[MAX_LINE_SIZE];
        long line = 1;
        while (fgets(buffer, MAX_LINE_SIZE, f))
        {
            int len = strlen(buffer);
            while (len > 0 && (buffer[len - 1] == '\n' || buffer[len - 1] == '\r'))
                buffer[--len] = '\0';
            *ref_line = new LineInFile(buffer, this, line++);
            ref_line = &(*ref_line)->next;
        }
        fclose(f);
    }
    void print_stat(FILE *fout)
    {
        fprintf(fout, "Stat %d: |%s| ", nr, name);
        if (exists) fprintf(fout, " exists");
        if (produced) fprintf(fout, " produced");
        if (exec) fprintf(fout, " exec");
        if (used_as_input) fprintf(fout, " used as input");
        if (removed) fprintf(fout, " removed");
        if (copy_from != 0) fprintf(fout, " copied from |%s|", copy_from->name);
        if (loaded) fprintf(fout, " loaded");
        if (lines != 0) fprintf(fout, " has lines");
        fprintf(fout, "\n");
    }
        
    FILE *open() { return fopen(name, "r"); }
};

File *files = 0;


class MergeChild
{
public:
    File *child;
    MergeChild *next;
    
    MergeChild(File *file) : child(file), next(0) {}
};

void combine_dir_fn(const char *dir, const char *fn, char *filename)
{
    if (fn[0] == '/')
    {
        strcpy(filename, fn);
        return;
    }
    if (fn[0] == '.' && fn[1] == '/')
        fn += 2;
    strcpy(filename, dir);
    char *t = filename + strlen(filename);
    if (fn[0] == '.' && fn[1] == '\0')
        return;
    *t++ = '/';
    for (const char *s = fn; *s != '\0'; s++)
        if (s[0] == '.' && s[1] == '.')
        {
            if (t[-1] == '/')
            {
                --t;
                *t = '\0';
            }
            s++;
            for (char *r = filename; *r != '\0'; r++)
                if (*r == '/')
                    t = r;
        }
        else
            *t++ = *s;
    *t = '\0';
    //fprintf(stdout, "combine dir |%s|%s| into |%s|\n", dir, fn, filename);
}

File *get_file(const char *full_fn, bool use_alias = true)
{
    File **ref = &files;
    for (; *ref != 0; ref = &(*ref)->next)
        if (strcmp((*ref)->name, full_fn) == 0)
            return (*ref)->alias != 0 && use_alias ? (*ref)->alias : (*ref);
    *ref = new File(full_fn);
    return *ref;
}


File *get_file(const char *dir, const char *fn)
{
    if (fn[0] == '/')
        return get_file(fn);
    char full_fn[MAX_PATH_SIZE];
    combine_dir_fn(dir, fn, full_fn);
    return get_file(full_fn);
}

File *find_file(const char *fn)
{
    for (File *file = files; file != 0; file = file->next)
        if (strcmp(file->name, fn) == 0)
            return file->alias != 0 ? file->alias : file;
    return 0;
}

File *find_exec_file(const char *fn)
{
    char full_fn[MAX_PATH_SIZE];
    if (*fn == '/')
        strcpy(full_fn, fn);
    else
        combine_dir_fn("/usr/bin", fn, full_fn);
    return find_file(full_fn);
}

bool file_exists(const char *dir, const char *fn)
{
    char full_fn[MAX_PATH_SIZE];
    combine_dir_fn(dir, fn, full_fn);
    if (find_file(full_fn) != 0)
        return true;
    FILE *f = fopen_mapped(full_fn);
    if (f != 0)
    {
        fclose(f);
        return true;
    }
    return false;
}

int nr_steps = 0;
class Step
{
public:
    int nr;
    Usage *uses;
    Step *next;
    
    Step() : uses(0), next(0)
    {
        nr = ++nr_steps;
    }
    bool hasInputUseOf(File *f);
};

Step *all_steps = 0;
Step **ref_next = &all_steps;
Step *next_step()
{
    Step *step = new Step();
    *ref_next = step;
    ref_next = &step->next;
    return step;
}   

int indent_depth = 0;
void indent(FILE *fout) { fprintf(fout, "%*.*s", indent_depth, indent_depth, ""); }

class Usage
{
public:
    bool as_input;
    bool as_output;
    bool as_modified;
    bool as_exec;
    bool as_removed;
    
    File *file;
    Step *step;
    
    Usage *next_use;
    Usage *next_usage;
    
    Usage (File *_file, Step *_step) : as_input(false), as_output(false), as_exec(false), as_removed(false), file(_file), step(_step), next_use(0), next_usage(0)
    {
        Usage **ref_use = &step->uses;
        while (*ref_use != 0) ref_use = &(*ref_use)->next_use;
        *ref_use = this;
        Usage **ref_usage = &file->usages;
        while (*ref_usage != 0) ref_usage = &(*ref_usage)->next_usage;
        *ref_usage = this;
    }
    
    void is_input(FILE *fout)
    {
        as_input = true;
        file->used_as_input = true;
        indent(fout); fprintf(fout, "%s file: %s\n", file->exists ? "Existing" : file->produced ? "Produced" : "Input", file->name);
        if (!file->exists_now())
            fprintf(fout, "ERROR: Using non existing file %s\n", file->name);
    }
    void is_output(FILE *fout)
    {
        as_output = true;
        indent(fout); fprintf(fout, "Output file: %s\n", file->name);
        file->produced = true;
        file->produced_by = step;
        file->removed = false;
    }
    void is_modified(FILE *fout)
    {
        as_input = true;
        file->modified = true;
        indent(fout); fprintf(fout, "%s file: %s\n", file->exists ? "Existing" : file->produced ? "Produced" : "Input", file->name);
        if (!file->exists_now())
            fprintf(fout, "ERROR: Modifying non existing file %s\n", file->name);
    }
    void is_exec(FILE *fout)
    {
        as_exec = true;
        indent(fout); fprintf(fout, "Exec file: %s", file->name);
        if (!file->exists && !file->produced)
            fprintf(fout, " Not existing nor produced");
        if (file->exists)
            fprintf(fout, " Existing");
        if (file->produced)
            fprintf(fout, " Produced");
        fprintf(fout, "\n");
        if (!file->exists_now())
            fprintf(fout, "ERROR: Executing non existing file %s\n", file->name);
    }
    void is_removed(FILE *fout)
    {
        as_removed = true;
        file->removed = true;
        indent(fout); fprintf(fout, "Deleted file: %s\n", file->name);
    }
};

bool Step::hasInputUseOf(File *f)
{
    for (Usage *use = uses; use != 0; use = use->next_use)
        if (use->file == f && use->as_input)
            return true;
    return false;
}



class Var
{
public:
    char *name;
    char *value;
    bool alias;
    bool override;
    Var *next;
    
    Var(const char *n, const char *v, bool a, Var *nv) : next(nv), alias(a), override(false)
    {
        name = copystr(n);
        value = copystr(v);
    }
};

char *get_var(Var *vars, const char *name)
{
    for (; vars != 0; vars = vars->next)
        if (!vars->alias && strcmp(vars->name, name) == 0)
            return vars->value;
    return 0;
}

Var *get_var_var(Var *vars, const char *name)
{
    for (; vars != 0; vars = vars->next)
        if (!vars->alias && strcmp(vars->name, name) == 0)
            return vars;
    return 0;
}

void copy_file(const char *src, const char *dest)
{
    File *src_file = get_file(src);
    File *dest_file = get_file(dest);
    dest_file->alias = src_file;
    dest_file->exists = src_file->exists;
    dest_file->produced = src_file->produced;
}

void copy_dir(const char *src, const char *dest)
{
    int src_len = strlen(src);
    for (File *file = files; file != 0; file = file->next)
        if (strncmp(file->name, src, src_len) == 0)
        {
            char dest_filename[MAX_PATH_SIZE];
            sprintf(dest_filename, "%s%s", dest, file->name + src_len);
            //fprintf(stderr, "Copy dir |%s| |%s|\n", file->name, dest_filename);
            copy_file(file->name, dest_filename);
        }
}

class SubModule
{
public:
    char *path;
    char *url;
    SubModule *next;
    SubModule(const char *p, const char *u, SubModule *n) : next(n)
    {
        path = copystr(p);
        url = copystr(u);
    }
};

SubModule *read_sub_modules(const char *fn, SubModule *subModules)
{
    char complete_path[MAX_LINE_SIZE];
    strcpy(complete_path, fn);
    char *s_path = complete_path + strlen(complete_path);
    strcpy(s_path, ".gitmodules");
    FILE *f = fopen(complete_path, "r");
    if (f == 0)
        return subModules;
    
    char path[MAX_LINE_SIZE];
    char url[MAX_LINE_SIZE];
    
    while (fgets(path, MAX_LINE_SIZE-1, f) && fgets(path, MAX_LINE_SIZE-1, f) && fgets(url, MAX_LINE_SIZE-1, f))
    {
        if (strncmp(path, "\tpath = ", 8) == 0 && strncmp(url, "\turl = ", 7) == 0)
        {
            while (path[strlen(path)-1] < ' ') path[strlen(path)-1] = '\0';
            while (url[strlen(url)-1] < ' ') url[strlen(url)-1] = '\0';
            char *s = strstr(url, ".git");
            if (s != 0) *s = '\0';
            
            sprintf(s_path, "%s/", path + 8);
            int len = strlen(url);
            char complete_url[MAX_LINE_SIZE];
            if (strncmp(url + 7, "https://github.com/", 19) == 0)
                sprintf(complete_url, "%s/blob/main/%%s", url + 7);
            else if (strncmp(url + 7, "https://git.savannah.nongnu.org/git/", 36) == 0)
                sprintf(complete_url, "https://git.savannah.nongnu.org/gitweb/?p=%s.git;a=blob;f=%%s", url + 43);
            else
                sprintf(complete_url, "%s%%s", url + 7);
            printf("Submodule %s %s|\n", complete_path, complete_url);
            subModules = new SubModule(complete_path, complete_url, subModules);
            subModules = read_sub_modules(complete_path, subModules);
        }
    }
    
    return subModules;
}


class Directory
{
public:
    char *path;
    Directory *next;
    
    Directory(const char *p, Directory *n) : path(copystr(p)), next(n) {} 
};

Directory *directories = 0;

bool is_directory(const char *path)
{
    for (Directory *dir = directories; dir != 0; dir = dir->next)
        if (strcmp(dir->path, path) == 0)
            return true;
    return false;
}

void add_directory(const char *path)
{
    if (!is_directory(path))
        directories = new Directory(path, directories);
}


bool parse_kaem_file(const char *fn, Var *vars, const char *parent_cur_path, FILE *fout);
void dir_of_filename(const char *fn, char *dir);
const char *file_of_filename(const char *fn);
void path_of_filename(const char *fn, char *path);
const char *file_ext(const char *fn);
FILE *open_file(const char *dir, const char *fn);
void write_html(FILE *f, SubModule *submodules);


int main(int argc, char *argv[])
{
    //if (argc != 2)
    //{
    //  fprintf(stdout, "Usage:\n%s <filename>\n", argv[0]);
    //  return -1;
    //}

    char cur_path[MAX_PATH_SIZE];

#define STAGE0POSIX "../git/live-bootstrap/sysa/stage0-posix/src/" /*"../git/stage0-posix/" */

    const char *stage0_path = STAGE0POSIX "kaem.x86";

    SubModule *subModules = new SubModule(STAGE0POSIX, "https://github.com/oriansj/stage0-posix/blob/main/%s", 0);
    SubModule **ref_lastSubmodule = &subModules->next;

    subModules = read_sub_modules(STAGE0POSIX, subModules);

    dir_of_filename(stage0_path, cur_path);
    fprintf(stdout, "Cur path: %s\n", cur_path);
    
    FILE *fout = fopen("out.txt", "w");
    debug_out = fout;
    
    bool result = parse_kaem_file(file_of_filename(stage0_path), new Var("EXE_SUFFIX", "", false, 0), cur_path, fout);
    
    fprintf(fout, "Result: %s\n", result ? "OK" : "FAIL");
    //fclose(fout);
    
    const char *live_bootstrap = "../git/live-bootstrap/sysa/run.kaem";
    
    *ref_lastSubmodule = new SubModule("../git/live-bootstrap/", "https://github.com/fosslinux/live-bootstrap/blob/master/%s", 0); ref_lastSubmodule = &(*ref_lastSubmodule)->next;
    subModules = new SubModule("/sysa/", "https://github.com/fosslinux/live-bootstrap/blob/master/sysa/%s", subModules);
    subModules = new SubModule("../git/live-bootstrap/sysa/bzip2-1.0.8/build/bzip2-1.0.8/", "https://github.com/libarchive/bzip2/blob/6a8690fc8d26c815e798c588f796eabe9d684cf0/%s", subModules);
    subModules = new SubModule("../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/", "https://git.savannah.gnu.org/cgit/mes.git/tree/%s?h=v0.24.2", subModules);
    subModules = new SubModule("../git/live-bootstrap/sysa/tcc-0.9.26/build/tcc-0.9.26-1136-g5bba73cc/", "https://gitlab.com/janneke/tinycc/-/blob/ff2210b308321f27ee07476d07f24b5095602b17/%s", subModules);
    subModules = new SubModule("../git/live-bootstrap/sysa/tcc-0.9.27/build/mes-0.24.2/", "https://git.savannah.gnu.org/cgit/mes.git/tree/%s?h=v0.24.2", subModules);
    subModules = new SubModule("../git/live-bootstrap/sysa/tcc-0.9.27/build/tcc-0.9.27/", "https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/%s", subModules);
    subModules = new SubModule("/sysa/tcc-0.9.27/build/tcc-0.9.27/", "https://gitlab.com/janneke/tinycc/-/tree/release_0_9_27/%s", subModules);
    subModules = new SubModule("../git/live-bootstrap/sysa/make-3.82/build/make-3.82/", "https://git.savannah.gnu.org/cgit/make.git/tree/%s?h=3.82", subModules);
    subModules = new SubModule("../git/live-bootstrap/sysa/coreutils-5.0/build/coreutils-5.0/", "https://git.savannah.gnu.org/cgit/coreutils.git/tree/%s?h=COREUTILS-5_0", subModules);
    subModules = new SubModule("../git/live-bootstrap/sysa/tar-1.12/build/tar-1.12", "https://git.savannah.gnu.org/cgit/tar.git/tree/%s?id=e9da8d1a8020ad4dfe92416f3c77a33e6906b55e", subModules);
    subModules = new SubModule("../git/live-bootstrap/sysa/sed-4.0.9/build/sed-4.0.9/", "https://git.savannah.gnu.org/cgit/sed.git/tree/%s?id=9c9919efe2166efd32409054005619062624226c", subModules);
    subModules = new SubModule("../git/live-bootstrap/sysa/bash-2.05b/build/bash-2.05b/", "https://git.savannah.gnu.org/cgit/bash.git/tree/%s?id=7117c2d221b2aed4ede8600f6a36b7c1454b4f55", subModules);
    //subModules = new SubModule("/sysa/mes-0.24.2", "https://github.com/fosslinux/live-bootstrap/blob/master/sysa/mes-0.24.2/%s", subModules);
    subModules = new SubModule("/sysa/mes-0.24.2/build/mes-0.24.2/", "https://git.savannah.gnu.org/cgit/mes.git/tree/%s?h=v0.24.2", subModules);
    subModules = new SubModule("/M2libc/", "https://github.com/oriansj/M2libc/blob/main/%s", subModules);

    // /sysa/mes-0.24.2/build/mes-0.24.2/
// https://git.savannah.gnu.org/cgit/mes.git/tree/%s?h=v0.24.2

    // subModules = new SubModule("", "", subModules);
    // subModules = new SubModule("", "", subModules);
    // subModules = new SubModule("", "", subModules);
    // subModules = new SubModule("", "", subModules);
// https://download.savannah.gnu.org/releases/nyacc/nyacc-1.00.2.tar.gz
// https://git.savannah.gnu.org/cgit/nyacc.git/tree/?h=V1.00.2


    
    dir_of_filename(live_bootstrap, cur_path);
    fprintf(stdout, "Cur path: %s\n", cur_path);
    
    add_directory("/sysa");
    add_directory("/sysa/distfiles");
    add_directory("/usr/bin");
    add_directory("/usr/lib");
    add_directory("/usr/include");
    add_directory("/usr/src");
    add_directory("/sysa/mes-0.24.2/build/mes-0.24.2/include/mes");
    add_directory("/M2libc");

    Var *live_bootstrap_vars = 0;
    live_bootstrap_vars = new Var("ARCH", "x86", false, live_bootstrap_vars);
    live_bootstrap_vars = new Var("sysa", "/sysa", false, live_bootstrap_vars);
    live_bootstrap_vars = new Var("distfiles", "/sysa/distfiles", false, live_bootstrap_vars);
    live_bootstrap_vars = new Var("bindir", "/usr/bin", false, live_bootstrap_vars);
    live_bootstrap_vars = new Var("libdir", "/usr/lib", false, live_bootstrap_vars);
    live_bootstrap_vars = new Var("incdir", "/usr/include", false, live_bootstrap_vars);
    live_bootstrap_vars = new Var("srcdir", "/usr/src", false, live_bootstrap_vars);
    
    live_bootstrap_vars = new Var("mes_cpu:-x86", "x86", false, live_bootstrap_vars);
    live_bootstrap_vars = new Var("stage0_cpu:-x86", "stage0_cpu", false, live_bootstrap_vars);
    live_bootstrap_vars = new Var("prefix", "/usr", false, live_bootstrap_vars);
    live_bootstrap_vars = new Var("UPDATE_CHECKSUMS", "False", false, live_bootstrap_vars);
    live_bootstrap_vars = new Var("PREFIX", "PREFIX", false, live_bootstrap_vars);
    //live_bootstrap_vars = new Var("MES_ARCH", "x86", false, live_bootstrap_vars);
    //live_bootstrap_vars = new Var("ARCH_BITS", "32", false, live_bootstrap_vars);

    
    live_bootstrap_vars = new Var("KERNEL_BOOTSTRAP", "False", false, live_bootstrap_vars);
    live_bootstrap_vars = new Var("BUILD_KERNELS", "False", false, live_bootstrap_vars);

#define ARCH_PATH "x86"

    copy_file(STAGE0POSIX ARCH_PATH "/bin/blood-elf","/usr/bin/blood-elf");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/catm","/usr/bin/catm");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/chmod","/usr/bin/chmod");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/get_machine","/usr/bin/get_machine");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/hex2","/usr/bin/hex2");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/kaem","/usr/bin/kaem");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/match","/usr/bin/match");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/M1","/usr/bin/M1");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/M2-Mesoplanet","/usr/bin/M2-Mesoplanet");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/M2-Planet","/usr/bin/M2-Planet");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/mkdir","/usr/bin/mkdir");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/sha256sum","/usr/bin/sha256sum");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/unbz2","/usr/bin/unbz2");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/ungz","/usr/bin/ungz");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/untar","/usr/bin/untar");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/cp","/usr/bin/cp");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/replace","/usr/bin/replace");
    copy_file(STAGE0POSIX ARCH_PATH "/bin/rm","/usr/bin/rm");
    copy_dir( STAGE0POSIX "M2libc", "/M2libc");
    copy_file(STAGE0POSIX "x86/x86_defs.M1","/M2libc/x86/x86_defs.M1");
    copy_file(STAGE0POSIX ARCH_PATH "/AMD64_defs.M1","/M2libc/AMD64/AMD64_defs.M1");
    copy_file(STAGE0POSIX "M2libc/bootstrappable.h", "/M2libc/bootstrappable.h");
    
    //fout = fopen("out2.txt", "w");
    
    result = parse_kaem_file(file_of_filename(live_bootstrap), live_bootstrap_vars, cur_path, fout);

    for (File *file = files; file != 0; file = file->next)
        if (file->exec && !file->exists && !file->produced)
            fprintf(fout, "ERROR: File %s is a binary input\n", file->name);
    
    fprintf(fout, "Result: %s\n", result ? "OK" : "FAIL");
    fclose(fout);
            
    FILE *f_html = fopen("livebootstrap.html", "w");
    if (f_html != 0)
    {
        write_html(f_html, subModules);
        fclose(f_html);
    }

    return 0;
}


void parse_c_file(const char *cur_path, File *file, const char **includes, int nr_includes, Define *&defines, Step *step, FILE *fout);
void parse_make_file(const char *cur_path, File *file, char **args, int nr_args, Var *vars, Step *step, FILE *fout);
int parse_command(const char *cur_path, char *command, FILE *fout);
int parse_command(const char *cur_path, char *args[], int nr_args, Var *vars, FILE *fout);
void get_args(char *command, char *args[], int &nr_args);
void process_patch_file(File *patch_file, const char *cur_path, Step *step, FILE *fout);


int nesting = 0;
File *nested_files[20];
bool nested_exec[20];

bool parse_kaem_file(const char *fn, Var *vars, const char *parent_cur_path, FILE *fout)
{
    char cur_path[MAX_PATH_SIZE];
    strcpy(cur_path, parent_cur_path);

    char *bin_path = get_var(vars, "bindir");
    if (bin_path == 0)
        bin_path = cur_path;
    
    FILE *f = open_file(cur_path, fn);
    if (f == 0)
    {
        if (strcmp(cur_path, "/sysa/mes-0.24.2/build/mes-0.24.2") == 0 && strcmp(fn, "kaem.run") == 0)
            f = fopen("../git/mes/kaem.run", "r");
        if (f == 0)
        {
            fprintf(fout, "ERROR: Cannot open file '%s/%s'\n", cur_path, fn);
            return false;
        }
    }

    indent(fout); fprintf(fout, "Parse kaem file: %s/%s\n", cur_path, fn);

    indent_depth += 2;
    
    int skip_level = 0;
    
    char buffer[MAX_LINE_SIZE];
    while (fgets(buffer, MAX_LINE_SIZE-1, f))
    {
        int len = strlen(buffer);
        while (len > 0 && buffer[len-1] < ' ')
            buffer[--len] = '\0';
        
        if (buffer[0] != '\0' && buffer[0] != '#')
        {
            char command_line[MAX_LINE_SIZE];
            strcpy(command_line, buffer);
            
            for (;;)
            {
                int len = strlen(command_line);
                if (command_line[len-1] != '\\')
                    break;
                
                command_line[len-1] = '\0';
                
                if (!fgets(buffer, MAX_LINE_SIZE-1, f))
                    break;
                if (buffer[0] == '\0' && buffer[0] == '#')
                    break;
                
                int len2 = strlen(buffer);
                while (len2 > 0 && buffer[len2-1] < ' ')
                    buffer[--len2] = '\0';
                strcat(command_line, buffer);
            }
            indent(fout); fprintf(fout, "Process %s\n", command_line);
            
            char exp_command_line[MAX_LINE_SIZE];
            int i = 0;
            char *s = command_line;
            while (*s == ' ' || *s == '\t')
                s++;
            if (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z'))
            {
                bool alias_used = false;
                char command[40];
                int j = 0;
                while (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z'))
                    command[j++] = *s++;
                command[j] = '\0';
                if (*s == ' ')
                {
                    for (Var *var = vars; var != 0; var = var->next)
                        if (var->alias && strcmp(var->name, command) == 0)
                        {
                            //fprintf(fout, "Found |%s|\n", var->value); 
                            for (const char *v = var->value; *v != '\0'; v++)
                                exp_command_line[i++] = *v;
                            exp_command_line[i++] = ' ';
                            alias_used = true;
                            break;
                        }
                }
                if (!alias_used)
                    s = command_line;           
            }
            while (*s == ' ' || *s == '\t')
                s++;
            if (strncmp(s, "exec ", 5) == 0)
                s += 5; 
            for (; *s != '\0';)
            {
                if (s[0] == '$' && s[1] == '{')
                {
                    s += 2;
                    char name[50];
                    int j = 0;
                    while (*s != '\0' && *s != '}')
                        name[j++] = *s++;
                    name[j] = '\0';
                    //fprintf(fout, "Find |%s|\n", name);
                    bool var_found = false;
                    for (Var *var = vars; var != 0; var = var->next)
                        if (!var->alias && strcmp(var->name, name) == 0)
                        {
                            //fprintf(fout, "Found |%s|\n", var->value); 
                            for (const char *v = var->value; *v != '\0'; v++)
                                exp_command_line[i++] = *v;
                            var_found = true;
                            break;
                        }
                    if (*s == '}')
                        s++;
                    if (!var_found)
                    {
                        fprintf(fout, "ERROR: '%s' not found\n", name);
                        //return false;
                    }
                }
                else if (*s < ' ')
                {
                    exp_command_line[i++] = ' ';
                    s++;
                }
                else
                    exp_command_line[i++] = *s++;
            }
            exp_command_line[i] = '\0';
            indent(fout); fprintf(fout, "Exp: %s\n", exp_command_line);
            
            char *args[1000];
            int nr_args = 0;
            
            s = exp_command_line;
            get_args(exp_command_line, args, nr_args);

            if (strcmp(exp_command_line, "if") == 0)
            {
                if (skip_level > 0)
                    skip_level++;
                else
                {
                    if (strcmp(args[nr_args - 1], "then") != 0)
                    {
                        fprintf(fout, "ERROR: unsupported if statement");
                        skip_level = 1;
                    }
                    else
                    {
                        // undo part of the get_args
                        int l = strlen(args[nr_args - 2]);
                        if (args[nr_args - 2][l - 1] == ';')
                            args[nr_args - 2][l - 1] = '\0';
                        //fprintf(stderr, "IF: ");
                        //for (int i = 1; i < nr_args - 1; i++)
                        //  fprintf(stderr, " |%s|", args[i]);
                        //fprintf(stderr, "\n");
                        int result = parse_command(cur_path, args + 1, nr_args - 2, vars, fout);
                        fprintf(fout, "MATCH %d\n", result);
                        if (result != 0)
                            skip_level = 1;
                    }
                }
            }
            else if (strcmp(exp_command_line, "else") == 0)
            {
                if (skip_level == 1)
                    skip_level = 0;
                else if (skip_level == 0)
                    skip_level = 1;
            }
            else if (strcmp(exp_command_line, "fi") == 0)
            {
                if (skip_level > 0)
                    skip_level--;
            }
            else if (skip_level > 0)
                ; // skip
            else if (strcmp(exp_command_line, "cd") == 0)
            {
                if (nr_args != 2)
                {
                    fprintf(fout, "ERROR: cd does not have one argument\n");
                }
                else
                {
                    char *s = args[1];
                    if (*s == '/')
                    {
                        strcpy(cur_path, s);
                    }
                    else if (strncmp(s, "../git/", 7) == 0)
                    {
                        strcpy(cur_path, s);
                    }
                    else
                    {
                        while (*s != '\0')
                        {
                            if (s[0] == '.' && s[1] == '.')
                            {
                                s  += 2;
                                char *last = 0;
                                for (char *t = cur_path; *t != '\0'; t++)
                                    if (*t == '/')
                                        last = t;
                                if (last != 0)
                                    *last = '\0';
                                if (*s != '/')
                                    break;
                                s++;
                            }
                            else
                            {
                                char *end = cur_path + strlen(cur_path);
                                *end++ = '/';
                                while (*s != '\0' && *s != ' ')
                                    *end++ = *s++;
                                *end = '\0';
                                break;
                            }
                        }
                    }
                    add_directory(cur_path);
                        
                    indent(fout); fprintf(fout, "cd to %s\n", cur_path);
                }
            }
            else if (strcmp(exp_command_line, "set") == 0)
            {
                if (nr_args == 2 && strcmp(args[1], "-ex") == 0)
                    ;
                else
                    fprintf(fout, "Warning: set\n");
            }
            else if (strcmp(exp_command_line, "echo") == 0)
            {
            }
            else
            {   
                bool is_var_decl = false;
                bool alias = false;
                s = exp_command_line;
                if (strcmp(s, "alias") == 0 && nr_args > 1)
                {
                    s = args[1];
                    alias = true;
                }
                if (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z'))
                {
                    char name[50];
                    int i = 0;
                    while (('a' <= *s && *s <= 'z') || ('A' <= *s && *s <= 'Z') || *s == '_' || ('0' <= *s && *s <= '9'))
                        name[i++] = *s++;
                    name[i] = '\0';
                    if (s[0] == '=')
                    {
                        s++;
                        char close_char = '\0';
                        if (*s == '"')
                        {
                            close_char = '"';
                            s++;
                        }
                        is_var_decl = true;
                        char value[200];
                        i = 0;
                        while (*s != '\0' && *s != close_char)
                            value[i++] = *s++;
                        if (*s != '\0' && *s == close_char)
                            s++;
                        value[i] = '\0';
                        indent(fout); fprintf(fout, "Decl %s = |%s|\n", name, value);
                        vars = new Var(name, value, alias, vars);
                    }
                }
                if (!is_var_decl)
                {                       
                    parse_command(cur_path, args, nr_args, vars, fout);

                }
            }
        }
    }
    
    fclose(f);

    indent_depth -= 2;
    
    return true;
}

void get_args(char *command, char *args[], int &nr_args)
{
    char *s = command;
    nr_args = 0;
    while (*s == ' ' || *s == '\t')
        s++;
    for (;;)
    {
        while (*s != '\0' && *s <= ' ')
            s++;
        if (*s == '\0' || *s == '#')
            break;
        args[nr_args++] = s;
        while (*s > ' ')
            if (*s == '\\' && s[1] != '\0')
                s += 2;
            else if (*s == '"' && *s != '#')
            {
                s++;
                while (*s != '\0' && *s != '"')
                    s++;
                if (*s == '"')
                    s++;
            }
            else
                s++;
        if (*s == '\0' || *s == '#')
            break;
        *s++ = '\0';
    }
}

int parse_command(const char *cur_path, char *command, FILE *fout)
{
    //fprintf(fout, "parse_command( %s |%s|)\n", cur_path, command);
    char *args[100];
    int nr_args;
    get_args(command, args, nr_args);
    //for (int i = 0; i < nr_args; i++)
    //  fprintf(fout, "  |%s|\n", args[i]);
    return parse_command(cur_path, args, nr_args, 0, fout);
}

int parse_command(const char *cur_path, char *args[], int nr_args, Var *vars, FILE *fout)
{
    indent_depth += 2;
    
    int result = 0;
    
    const char *bin_path = get_var(vars, "bindir");
    if (bin_path == 0)
        bin_path = cur_path;
    
    int argnr = 1;
    
    Step *step = next_step();
    indent(fout), fprintf(fout, "Step %d\n", step->nr);
    
    for (int n = 0; n < nesting; n++)
    {
        Usage *usage = new Usage(nested_files[n], step);
        if (nested_exec[n])
            usage->as_exec = true;
        else
            usage->as_input = true;
    }
    
    File *exec_file = find_exec_file(args[0]);
    if (exec_file == 0)
        exec_file = get_file(args[0][0] == '.' ? cur_path : bin_path, args[0]);
    exec_file->exec = true;
    Usage *usage = new Usage(exec_file, step);
    usage->is_exec(fout);
    
    const char *exec_fn = file_of_filename(args[0]);
    if (strcmp(exec_fn, "kaem-optional-seed") == 0 || strcmp(exec_fn, "kaem-0") == 0)
    {
        if (nr_args != 2)
            fprintf(fout, "ERROR: kaem-0 requires one argument\n");
        else
        {
            char *kaem_input_fn = args[1];
            nested_files[nesting] = step->uses->file;
            nested_exec[nesting] = true;
            nesting++;
            parse_kaem_file(kaem_input_fn[0] == '.' && kaem_input_fn[1] == '/' ? kaem_input_fn + 2 : kaem_input_fn, vars, cur_path, fout);
            nesting--;
        }
    }
    else if (strcmp(exec_fn, "kaem") == 0)
    {
        char *kaem_input_fn = 0;
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "--verbose") == 0)
                ;
            else if (strcmp(arg, "--strict") == 0)
                ;
            else if (strcmp(arg, "--file") == 0 && arg_nr + 1 < nr_args)
            {
                kaem_input_fn = args[arg_nr + 1];
                arg_nr++;
            }
            else
                fprintf(fout, "WARNING: Unknown kaem argument '%s'\n", arg);
        }
        if (kaem_input_fn != 0)
        {   
            nested_files[nesting] = step->uses->file;
            nested_exec[nesting] = true;
            nesting++;
            parse_kaem_file(kaem_input_fn[0] == '.' && kaem_input_fn[1] == '/' ? kaem_input_fn + 2 : kaem_input_fn, vars, cur_path, fout);
            nesting--;
        }
    }
    else if (strcmp(exec_fn, "catm") == 0)
    {
        MergeChild **ref_child;
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            File *file = get_file(cur_path, arg);
            Usage *usage = new Usage(file, step);
            if (arg_nr == 1)
            {
                usage->is_output(fout);
                ref_child = &file->mergeChildren;
            }
            else
            {
                usage->is_input(fout);
                *ref_child = new MergeChild(file);
                ref_child = &(*ref_child)->next;
            }
        }                           
    }
    else if (strcmp(exec_fn, "M2") == 0 || strcmp(exec_fn, "M2-Planet") == 0)
    {
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "--architecture") == 0)
                arg_nr++;
            else if (strcmp(arg, "--bootstrap-mode") == 0)
                ;
            else if (strcmp(arg, "--debug") == 0)
                ;
            else if (strcmp(arg, "-f") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_input(fout);
            else if (strcmp(arg, "-o") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_output(fout);
            else
                fprintf(fout, "Warning: Skipped '%s'\n", arg);
        }
    }
    else if (strcmp(exec_fn, "blood-elf-0") == 0 || strcmp(exec_fn, "blood-elf") == 0)
    {
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "--64") == 0)
                ;
            else if (strcmp(arg, "--little-endian") == 0)
                ;
            else if (strcmp(arg, "-f") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_input(fout);
            else if (strcmp(arg, "-o") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_output(fout);
            else
                fprintf(fout, "Warning: Skipped '%s'\n", arg);
        }
    }
    else if (strcmp(exec_fn, "M1-0") == 0 || strcmp(exec_fn, "M1") == 0)
    {
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "--architecture") == 0)
                arg_nr++;
            else if (strcmp(arg, "--little-endian") == 0)
                ;
            else if (strcmp(arg, "-f") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_input(fout);
            else if (strcmp(arg, "-o") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_output(fout);
            else
                fprintf(fout, "Warning: Skipped '%s'\n", arg);
        }
    }
    else if (strcmp(exec_fn, "hex2-1") == 0 || strcmp(exec_fn, "hex2") == 0)
    {
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "--architecture") == 0)
                arg_nr++;
            else if (strcmp(arg, "--little-endian") == 0)
                ;
            else if (strcmp(arg, "--base-address") == 0)
                arg_nr++;
            else if (strcmp(arg, "-f") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_input(fout);
            else if (strcmp(arg, "-o") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_output(fout);
            else
                fprintf(fout, "Warning: Skipped '%s'\n", arg);
        }
    }
    else if (strcmp(exec_fn, "cp") == 0)
    {
        if (nr_args != 3)
            fprintf(fout, "ERROR: cp requires two arguments\n");
        else
        {
            File *source_file = get_file(cur_path, args[1]);
            (new Usage(source_file, step))->is_input(fout);
            char target[MAX_PATH_SIZE];
            combine_dir_fn(cur_path, args[2], target);
            File *target_file = 0;
            if (target[strlen(target) - 1] == '/' || is_directory(target))
            //    || (bin_path != 0 && strcmp(bin_path, target) == 0)
            //  || (*file_ext(args[1]) != '\0' && *file_ext(target) == '\0'))
            {
                char target_fn[MAX_PATH_SIZE];
                strcpy(target_fn, target);
                if (target[strlen(target) - 1] != '/')
                    strcat(target_fn, "/");
                strcat(target_fn, file_of_filename(args[1]));
                target_file = get_file(target_fn);
            }
            else
                target_file = get_file(target);
            target_file->copy_from = source_file;
            //fprintf(fout, "Copy from %s %s\n", target_file->name, source_file->name);
            (new Usage(target_file, step))->is_output(fout);
        }
    }
    else if (strcmp(exec_fn, "mv") == 0)
    {
        File *source_file = 0;
        File *target_file = 0;

        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "-f") == 0 && arg_nr + 1 < nr_args)
            {
                arg = args[++arg_nr];
                source_file = get_file(cur_path, arg);
                (new Usage(source_file, step))->is_input(fout);
                (new Usage(source_file, step))->is_removed(fout);
            }
            else
            {
                target_file = get_file(cur_path, arg);
                (new Usage(target_file, step))->is_output(fout);
            }
        }
        if (source_file != 0 && target_file != 0)
            target_file->copy_from = source_file;
    }
    else if (strcmp(exec_fn, "checksum-transcriber") == 0)
    {
        if (nr_args == 2)
        {
            File *sources_file = get_file(cur_path, args[1]);
            (new Usage(sources_file, step))->is_input(fout);
            char *distfiles = get_var(vars, "distfiles");
            if (distfiles != 0)
            {
                FILE *f_source = fopen(sources_file->name, "r");
                if (f_source != 0)
                {
                    char line[300];
                    while (fgets(line, 299, f_source))
                    {
                        char *s = line;
                        while (*s != '\0' &&  *s != ' ') s++;
                        if (*s == ' ') *s++ = '\0';
                        char *target = 0;
                        if (*s != '\0')
                        {
                            // Skip checksum
                            while (*s == ' ') s++;
                            while (*s != '\0' &&  *s != ' ') s++;
                            // See if target name is specified
                            if (*s == ' ')
                            {
                                while (*s == ' ') s++;
                                target = s;
                                while (*s > ' ') s++;
                                *s = '\0';
                            }
                        }
                            
                        File *source_file = get_file(distfiles, target != 0 ? target : file_of_filename(line));
                        source_file->set_source(line);
                        indent(fout); fprintf(fout, "URL for %s : %s\n", source_file->name, source_file->source->url);
                    }
                    fclose(f_source);
                }
            }
            char outputSHA256SUM[MAX_PATH_SIZE];
            strcpy(outputSHA256SUM, args[1]);
            strcat(outputSHA256SUM, ".SHA256SUM");
            (new Usage(get_file(cur_path, outputSHA256SUM), step))->is_output(fout);
        }
    }
    else if (strcmp(exec_fn, "M2-Mesoplanet") == 0)
    {
        File *input_file = 0;
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "--architecture") == 0)
                arg_nr++;
            else if (strcmp(arg, "--operating-system") == 0)
                arg_nr++;
            else if (strcmp(arg, "-f") == 0 && arg_nr + 1 < nr_args)
            {
                arg = args[++arg_nr];
                input_file = get_file(cur_path, arg); 
                (new Usage(input_file, step))->is_input(fout);
            }
            else if (strcmp(arg, "-o") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_output(fout);
            else
                (new Usage(get_file(cur_path, arg), step))->is_input(fout);
        }
        if (input_file != 0)
        {
            
            const char *includes[2] = { "./M2Libc", "/" }; //{ "../M2-Mesoplanet/M2libc" };
            const char *m2libpath = get_var(vars, "M2LIBC_PATH");
            if (m2libpath != 0)
                includes[0] = m2libpath;
            Define *defines = 0;
            Define::addDefine(defines, "__M2__", 0);
            parse_c_file(cur_path, input_file, includes, 2, defines, step, fout);   
        }
    }
    else if (strcmp(exec_fn, "sha256sum") == 0)
    {
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "-c") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_input(fout);
            else if (strcmp(arg, "-o") == 0)
            {
                for (arg_nr += 2; arg_nr < nr_args; arg_nr++)
                {
                    File *file = get_file(bin_path, args[arg_nr]);
                    //fprintf(fout, "Check: %s %d %d\n", file->name, file->produced, file->exists);
                }
            }
            else
                fprintf(fout, "Warning: Skipped '%s'\n", arg);
        }
    }
    else if (strcmp(exec_fn, "mkdir") == 0)
    {
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char dirname[MAX_PATH_SIZE];
            combine_dir_fn(cur_path, args[arg_nr], dirname);
            add_directory(dirname);
        }
    }
    else if (strcmp(exec_fn, "ungz") == 0 || strcmp(exec_fn, "unbz2") == 0)
    {
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "--file") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_input(fout);
            else if (strcmp(arg, "--output") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_output(fout);
            else
                (new Usage(get_file(cur_path, arg), step))->is_input(fout);
        }
    }
    else if (strcmp(exec_fn, "untar") == 0)
    {
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "--non-strict") == 0)
                ;
            else if (strcmp(arg, "--file") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_input(fout);
            else
                (new Usage(get_file(cur_path, arg), step))->is_input(fout);
        }
    }
    else if (strcmp(exec_fn, "rm") == 0)
    {
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "-r") == 0 && arg_nr + 1 < nr_args)
            {
                arg = args[++arg_nr];
                char full_fn[MAX_PATH_SIZE];
                combine_dir_fn(cur_path, args[arg_nr], full_fn);
                int len = strlen(full_fn);
                bool slash = full_fn[len - 1] == '/';
                for (File *file = files; file != 0; file = file->next)
                    if (strncmp(file->name, full_fn, len) == 0 && (slash ? file->name[len] != '\0' : (file->name[len] == '\0' || file->name[len] == '/')))
                        (new Usage(file, step))->is_removed(fout);
            }
            else
                (new Usage(get_file(cur_path, arg), step))->is_removed(fout);
                
        }
    }
    else if (strcmp(exec_fn, "chmod") == 0)
    {
        // ignore
    }
    else if (strcmp(exec_fn, "replace") == 0)
    {
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "--file") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_input(fout);
            else if (strcmp(arg, "--output") == 0 && arg_nr + 1 < nr_args)
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_output(fout);
            else if (strcmp(arg, "--match-on") == 0 && arg_nr + 1 < nr_args)
                arg_nr++;
            else if (strcmp(arg, "--replace-with") == 0)
            {
                if (arg_nr + 1 < nr_args)
                    arg_nr++;
            }
            else
                (new Usage(get_file(cur_path, arg), step))->is_input(fout);
        }
    }
    else if (strcmp(exec_fn, "mes-m2") == 0 || strcmp(exec_fn, "mes") == 0)
    {
        bool string_input = false;
        char *scm_input = 0;
        char *c_input = 0;
        char *s_input = 0;
        const char *includes[10];
        int nr_includes = 0;
        bool o_flag = false;
        File *c_file = 0;
        Define *defines = 0;
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "--no-auto-compile") == 0 && strcmp(exec_fn, "mes") == 0)
                ;
            else if (strcmp(arg, "-e") == 0 && arg_nr + 1 < nr_args)
                arg_nr++;
            else if (strcmp(arg, "--") == 0 && arg_nr + 1 < nr_args)
                ;
            else if (strcmp(arg, "-S") == 0 && arg_nr + 1 < nr_args)
                ;
            else if (strcmp(arg, "-D") == 0 && arg_nr + 1 < nr_args)
            {
                arg = args[++arg_nr];
                char *s = arg + 1;
                while (*s != '\0' && *s != '=') s++;
                if (*s == '=')
                {
                    *s = '\0';
                    s++;
                }
                Define::addDefine(defines, arg, *s == '\0' ? 0 : s);
            }
            else if (strcmp(arg, "-I") == 0 && arg_nr + 1 < nr_args)
                includes[nr_includes++] = args[++arg_nr];
            else if (strcmp(arg, "-c") == 0 && arg_nr + 1 < nr_args || strncmp(arg, "-c", 2) == 0)
            {
                if (arg[2] == '\0')
                    arg = args[++arg_nr];
                else
                    arg += 2;
                if (arg[0] == '"')
                    string_input = true;
                else
                {
                    if (c_input != 0)
                        fprintf(fout, "ERROR: more than one c file\n");
                    else
                        c_input = arg;
                    c_file = get_file(cur_path, arg);
                    (new Usage(c_file, step))->is_input(fout);
                }
            }
            else if (strcmp(arg, "-L") == 0 && arg_nr + 1 < nr_args)
            {
                arg_nr++;
                char libdir[MAX_PATH_SIZE];
                combine_dir_fn(cur_path, args[arg_nr], libdir);
                strcat(libdir, "/");
                int len = strlen(libdir);
                for (File *file = files; file != 0; file = file->next)
                    if (strncmp(libdir, file->name, len) == 0 && strcmp(file_ext(file->name), "a") == 0)
                        (new Usage(file, step))->is_input(fout);
            }
            else if (strcmp(arg, "--base-address") == 0 && arg_nr + 1 < nr_args)
                arg_nr++;
            else if (strcmp(arg, "-nostdlib") == 0)
                ;
            else if (strcmp(arg, "-o") == 0 && arg_nr + 1 < nr_args)
            {
                (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_output(fout);
                o_flag = true;
            }
            else if (strcmp(arg, "-l") == 0 && arg_nr + 1 < nr_args)
                arg_nr++;
            else if (strncmp(arg, "-l", 2) == 0)
                ;
            else if (arg[0] == '-')
                fprintf(fout, "Warning: Unknown option |%s|\n", arg);
            else
            {
                int len = strlen(arg);
                if (strcmp(file_ext(arg), "scm") == 0)
                {
                    if (scm_input != 0)
                        fprintf(fout, "ERROR: more than one .scm file\n");
                    else
                        scm_input = arg;
                }
                else if (strcmp(file_ext(arg), "c") == 0)
                {
                    if (c_input != 0)
                        fprintf(fout, "ERROR: more than one .c file\n");
                    else
                        c_input = arg;
                    c_file = get_file(cur_path, arg);
                }
                else if (strcmp(file_ext(arg), "s") == 0)
                {
                    if (s_input != 0)
                        fprintf(fout, "ERROR: more than one .s file\n");
                    else
                        s_input = arg;
                }
                (new Usage(get_file(cur_path, arg), step))->is_input(fout);
            }
        }
        if (!string_input)
        {
            if (c_input == 0 && s_input == 0)
                fprintf(fout, "Info: No c and s input\n");
            else if (!o_flag && strcmp(c_input + strlen(c_input) - 2, ".c") == 0)
            {
                c_input[strlen(c_input) - 1] = 'o';
                (new Usage(get_file(cur_path, file_of_filename(c_input)), step))->is_output(fout);
                c_input[strlen(c_input) - 1] = 's';
                (new Usage(get_file(cur_path, file_of_filename(c_input)), step))->is_output(fout);
            }
            
            if (scm_input == 0)
                fprintf(fout, "ERROR: No scm input\n");
                
            if (c_file != 0)
            {
                c_file->print_stat(fout);
                while (!c_file->exists && !c_file->loaded && c_file->copy_from != 0)
                    c_file = c_file->copy_from;
                c_file->print_stat(fout);
                if (!c_file->exists && !c_file->loaded)
                    fprintf(fout, "Warning: Cannot parse C file '%s'\n", c_file->name);
                else
                    parse_c_file(cur_path, c_file, includes, nr_includes, defines, step, fout);
            }
        }
    }
    else if (   strcmp(exec_fn, "mes-tcc") == 0
             || strcmp(exec_fn, "boot0-tcc") == 0
             || strcmp(exec_fn, "boot1-tcc") == 0
             || strcmp(exec_fn, "boot2-tcc") == 0
             || strcmp(exec_fn, "boot3-tcc") == 0
             || strcmp(exec_fn, "boot4-tcc") == 0
             || strcmp(exec_fn, "boot5-tcc") == 0
             || strcmp(exec_fn, "tcc") == 0
             || strcmp(exec_fn, "tcc-0.9.26") == 0)
    {
        if (strcmp(args[1], "-version") == 0)
            ;
        else if (strcmp(args[1], "-ar") == 0)
        {
            int arg_nr = 2;
            //if (arg_nr < nr_args && (strcmp(args[arg_nr], "cr") == 0 || strcmp(args[arg_nr], "-rv") == 0 || strcmp(args[arg_nr],"cq") == 0))
            if (arg_nr < nr_args && strstr(args[arg_nr], ".") == 0)
                arg_nr++;
            if (arg_nr + 2 > nr_args)
                fprintf(fout, "ERROR: -ar requires at least two more arguments\n");
            else
            {
                (new Usage(get_file(cur_path, args[arg_nr]), step))->is_output(fout);
                for (int i = arg_nr + 1; i < nr_args; i++)
                    (new Usage(get_file(cur_path, args[i]), step))->is_input(fout);
            }
        }
        else
        {
            char *c_input = 0;
            char *h_input = 0;
            const char *includes[10];
            int nr_includes = 0;
            bool o_flag = false;
            Define *defines = 0;
            Define::addDefine(defines, "__TINYC__", 0);
            Define::addDefine(defines, "__STDC__", 0);
            
            for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
            {
                char *arg = args[arg_nr];
                //fprintf(fout, "Process |%s|\n", arg);
                if (strcmp(arg, "-c") == 0)
                    ;
                else if (strcmp(arg, "-g") == 0)
                    ;
                else if (strcmp(arg, "-v") == 0)
                    ;
                else if (strcmp(arg, "-static") == 0)
                    ;
                else if ((strcmp(arg, "-D") == 0 && arg_nr + 1 < nr_args) || strncmp(arg, "-D", 2) == 0)
                {
                    if (arg[2] == '\0')
                        arg = args[++arg_nr];
                    else
                        arg += 2;
                    char *s = arg + 1;
                    while (*s != '\0' && *s != '=') s++;
                    if (*s == '=')
                    {
                        *s = '\0';
                        s++;
                    }
                    if (debug_c_parsing) fprintf(fout, "Arg Define %s as |%s|\n", arg, *s == '\0' ? "1" : s);
                    Define::addDefine(defines, arg, *s == '\0' ? "1" : s);
                }
                else if ((strcmp(arg, "-I") == 0 && arg_nr + 1 < nr_args) || strncmp(arg, "-I", 2) == 0)
                {
                    if (arg[2] == '\0')
                        arg = args[++arg_nr];
                    else
                        arg += 2;
                    includes[nr_includes++] = (arg[0] == '.' && arg[1] == '\0') ? "" : arg;
                }
                else if ((strcmp(arg, "-L") == 0 && arg_nr + 1 < nr_args) || strncmp(arg, "-L", 2) == 0)
                {
                    if (arg[2] == '\0')
                        arg = args[++arg_nr];
                    else
                        arg += 2;
                    //if (strcmp(args[arg_nr], ".") != 0)
                    //  (new Usage(get_file(cur_path, args[arg_nr]), step))->is_input(fout);
                }
                else if (strcmp(arg, "-o") == 0 && arg_nr + 1 < nr_args)
                {
                    (new Usage(get_file(cur_path, args[++arg_nr]), step))->is_output(fout);
                    o_flag = true;
                }
                else if (strncmp(arg, "-l", 2) == 0)
                    ;
                else if (strncmp(arg, "-W", 2) == 0)
                    ; // skip warning switches
                else if (strncmp(arg, "-O", 2) == 0)
                    ; // skip optimation switches
                else
                {
                    if (strcmp(file_ext(arg), "c") == 0)
                    {
                        if (c_input != 0)
                            fprintf(fout, "ERROR: more than one c file\n");
                        else
                            c_input = arg;
                    }
                    else if (strcmp(file_ext(arg), "h") == 0 && h_input == 0)
                        h_input = arg;
                    (new Usage(get_file(cur_path, arg), step))->is_input(fout);
                }
            }
            includes[nr_includes++] = "/usr/include";
            if (c_input == 0)
                c_input = h_input;
            if (c_input == 0)
            {
                fprintf(fout, "Info: No c or h input\n");
            }
            else
            {
                File *c_file = get_file(cur_path, c_input);

                if (!o_flag)
                {
                    c_input[strlen(c_input)-1] = 'o';
                    (new Usage(get_file(cur_path, file_of_filename(c_input)), step))->is_output(fout);
                }
                
                if (c_file != 0)
                {
                    fprintf(fout, "PARSE: %s|\n", c_file->name);
                    c_file->print_stat(fout);
                    while (!c_file->exists && !c_file->loaded && c_file->mergeChildren == 0 && c_file->copy_from != 0)
                        c_file = c_file->copy_from;
                    c_file->print_stat(fout);
                    if (c_file->loaded || c_file->existing() != 0)
                        parse_c_file(cur_path, c_file, includes, nr_includes, defines, step, fout);
                    else if (c_file->mergeChildren != 0)
                    {
                        for (MergeChild *child = c_file->mergeChildren; child != 0; child = child->next)
                            if (child->child->existing() != 0)
                            {
                                fprintf(fout, "PARSE %s|\n", child->child->name);
                                parse_c_file(cur_path, child->child, includes, nr_includes, defines, step, fout);
                            }
                            else
                                fprintf(fout, "File %s does not exist\n", child->child->name);
                    }
                    else
                    {
                        fprintf(fout, "Warning: Cannot parse C file '%s'\n", c_file->name);
                    }
                }
                

            }
        }
    }
    else if (strcmp(exec_fn, "make") == 0)
    {
        File *make_file = 0;
        char *make_args[10];
        int nr_make_args = 0;
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "--version") == 0)
                ;
            else if (strcmp(arg, "-f") == 0 && arg_nr + 1 < nr_args)
            {
                make_file = get_file(cur_path, args[++arg_nr]);
            }
            else
            {
                make_args[nr_make_args++] = arg;
                indent(fout); fprintf(fout, "  make switch: %s\n", arg);
            }
        }
        nested_files[nesting] = exec_file;
        nested_exec[nesting] = true;
        nesting++;
        parse_make_file(cur_path, make_file, make_args, nr_make_args, vars, step, fout);
        nesting--;
    }
    else if (strcmp(exec_fn, "gzip") == 0)
    {
        bool deflate = false;
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "-d") == 0)
                deflate = true;
            else if (strcmp(arg, "-f") == 0 && arg_nr + 1 < nr_args)
            {
                arg = args[++arg_nr];
                (new Usage(get_file(cur_path, arg), step))->is_input(fout);
                char *ext = (char *)file_ext(arg);
                if (deflate && strcmp(ext, "gz") == 0)
                {
                    ext[-1] = '\0';
                    (new Usage(get_file(cur_path, arg), step))->is_output(fout);
                }
            }
            else
            {
                fprintf(fout, "ERROR: %s\n", arg);
            }
        }
    }
    else if (strcmp(exec_fn, "simple-patch") == 0)
    {
        if (nr_args != 4)
            fprintf(fout, "ERROR: simple-patch requires 4 arguments, not %d\n", nr_args);
        else
        {
            File *patch_file = get_file(cur_path, args[1]);
            (new Usage(patch_file, step))->is_modified(fout);
            patch_file->load(fout);
            File *from_patch_file = get_file(cur_path, args[2]);
            (new Usage(from_patch_file, step))->is_input(fout);
            from_patch_file->load(fout);
            File *to_patch_file = get_file(cur_path, args[3]);
            (new Usage(to_patch_file, step))->is_input(fout);
            to_patch_file->load(fout);
            bool match = false;
            for (LineInFile **ref_line = &patch_file->lines; *ref_line != 0; ref_line = &(*ref_line)->next)
            {
                match = true;
                LineInFile *l1 = *ref_line;
                LineInFile *l2 = from_patch_file->lines;
                for (; match && l1 != 0 && l2 != 0; l1 = l1->next, l2 = l2->next)
                    match = strcmp(l1->text, l2->text) == 0;
                if (l1 == 0 && l2 != 0)
                    break;
                if (match)
                {
                    fprintf(fout, "Info: simple-patch found match\n");
                    for (LineInFile *to_line = to_patch_file->lines; to_line != 0; to_line = to_line->next)
                    {
                        *ref_line = new LineInFile(to_line);
                        ref_line = &(*ref_line)->next;
                    }
                    *ref_line = l1;
                    break;
                }
            }
            if (!match)
                fprintf(fout, "Info: simple-patch found NO match\n");
        }
    }
    else if (strcmp(exec_fn, "patch") == 0)
    {
        bool deflate = false;
        File *patch_file = 0;
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "-Np0") == 0)
                deflate = true;
            else if (strcmp(arg, "-i") == 0 && arg_nr + 1 < nr_args)
            {
                patch_file = get_file(cur_path, args[++arg_nr]);
                (new Usage(patch_file, step))->is_input(fout);
            }
            else
            {
                fprintf(fout, "ERROR: %s\n", arg);
            }
        }
        if (patch_file != 0)
            process_patch_file(patch_file, cur_path, step, fout);
    }
    else if (strcmp(exec_fn, "bunzip2") == 0)
    {
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "-f") == 0 && arg_nr + 1 < nr_args)
            {
                arg = args[++arg_nr];
                (new Usage(get_file(cur_path, arg), step))->is_input(fout);
                char *ext = (char *)file_ext(arg);
                if (strcmp(ext, "bz2") == 0)
                {
                    ext[-1] = '\0';
                    (new Usage(get_file(cur_path, arg), step))->is_output(fout);
                }
            }
            else
            {
                fprintf(fout, "ERROR: %s\n", arg);
            }
        }
    }
    else if (strcmp(exec_fn, "ln") == 0)
    {
        char *input_fn = 0;
        char *output_fn = 0;
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "-s") == 0 && arg_nr + 1 < nr_args)
                ;
            else if (input_fn == 0)
                input_fn = arg;
            else
                output_fn = arg;
        }
        if (output_fn != 0)
        {
            (new Usage(get_file(cur_path, input_fn), step))->is_input(fout);
            (new Usage(get_file(cur_path, output_fn), step))->is_output(fout);
        }                           
    }
    else if (strcmp(exec_fn, "install") == 0)
    {
        int arg_nr = strcmp(args[1], "-m") == 0 ? 3 : 1;
        if (arg_nr < nr_args - 1)
        {
            char *out_dir = args[nr_args - 1];
            
            for (; arg_nr < nr_args - 1; arg_nr++)
            {
                char *input_fn = args[arg_nr];
                (new Usage(get_file(cur_path, input_fn), step))->is_input(fout);
                char output_fn[MAX_PATH_SIZE];
                sprintf(output_fn, "%s%s%s", out_dir, out_dir[strlen(out_dir)-1] == '/' ? "" : "/", file_of_filename(input_fn));
                (new Usage(get_file(cur_path, output_fn), step))->is_output(fout);
            }
        }
    }
    else if (strcmp(exec_fn, "tar") == 0)
    {
        if (nr_args < 3 || strcmp(args[1], "xf") != 0)
            fprintf(fout, "ERROR: Unknown tar options\n");
        else
        {
            (new Usage(get_file(cur_path, args[2]), step))->is_input(fout);
        }
    }
    else if (strcmp(exec_fn, "mkbuiltins") == 0)
    {
        char *extern_filename = 0;
        bool no_production = false;
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "-externfile") == 0)
            {
                arg = args[++arg_nr];
                extern_filename = arg;
                File *extern_file = get_file(cur_path, arg);
                extern_file->loaded = true;
                (new Usage(extern_file, step))->is_output(fout);
            }
            else if (strcmp(arg, "-structfile") == 0)
            {
                arg = args[++arg_nr];
                File *struct_file = get_file(cur_path, arg);
                struct_file->loaded = true;
                (new Usage(struct_file, step))->is_output(fout);
            }
            else if (strcmp(arg, "-noproduction") == 0)
                no_production = true;
            else
            {
                char filename[MAX_PATH_SIZE];
                char full_fn[MAX_PATH_SIZE];
                combine_dir_fn(cur_path, arg, full_fn);
                FILE *f = fopen_mapped(full_fn);
                if (f != 0)
                {
                    fclose(f);
                                
                    File *file = get_file(cur_path, arg);
                    (new Usage(file, step))->is_input(fout);
                    if (!no_production  && strcmp(file_ext(arg), "def") == 0)
                    {
                        char output_fn[MAX_PATH_SIZE];
                        strcpy(output_fn, arg);
                        strcpy(output_fn + strlen(output_fn) - 3, "c");
                        File *c_file = get_file(cur_path, output_fn);
                        (new Usage(c_file, step))->is_output(fout);
                        
                        file->load(fout);
                        bool skip = true;
                        LineInFile **ref_line = &c_file->lines;
                        for (LineInFile *line = file->lines; line != 0; line = line->next)
                        {
                            if (line->text[0] == '$')
                            {
                                if (strncmp(line->text, "$PRODUCES", 9) == 0 || strncmp(line->text, "$END", 4) == 0)
                                    skip = false;
                                else if (strncmp(line->text, "$SHORT_DOC", 10) == 0)
                                    skip = true;
                            }
                            else if (!skip)
                            {
                                *ref_line = new LineInFile(line);
                                ref_line = &(*ref_line)->next;
                            }
                        }
                        c_file->loaded = true;
                    }
                }
            }
        }
        if (no_production)
        {
            File *builtins_file = get_file(cur_path, "builtins.c");
            (new Usage(builtins_file, step))->is_output(fout);
            // Just create some line for the includes that are included
            builtins_file->loaded = true;
            LineInFile **ref_line = &builtins_file->lines;
            *ref_line = new LineInFile("#include \"../builtins.h\"", builtins_file, 1);
            ref_line = &(*ref_line)->next;
            char include_line[200];
            sprintf(include_line, "#include \"%s\"\n", extern_filename != 0 ? extern_filename : "builtext.h");
            *ref_line = new LineInFile(include_line, builtins_file, 2);
        }
    }
    else if (strcmp(exec_fn, "yacc") == 0)
    {
        File *input_file = 0;
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (arg[0] == '/')
                ; // skip options
            else if (strcmp(arg, "-d") == 0)
                ; // skip option
            else
            {
                input_file = get_file(cur_path, arg);
                (new Usage(input_file, step))->is_input(fout);
            }
        }
        File *output_file = get_file(cur_path, "y.tab.c"); 
        (new Usage(output_file, step))->is_output(fout);
        if (input_file != 0 && input_file->loaded)
        {
            output_file->loaded = true;
            input_file->load(fout);
            LineInFile **ref_line = &output_file->lines;
            bool copy_line = false;
            for (LineInFile *line = input_file->lines; line != 0; line = line->next)
            {
                if (!copy_line && strncmp(line->text, "%{", 2) == 0)
                    copy_line = true;
                else if (copy_line && strncmp(line->text, "%}", 2) == 0)
                    copy_line = false;
                else if (copy_line)
                {
                    fprintf(fout, "YACC: %3ld: %s\n", line->line, line->text);
                    *ref_line = new LineInFile(line);
                    ref_line = &(*ref_line)->next;
                }
            }
        }
    }
    else if (strcmp(exec_fn, "mksyntax") == 0)
    {
        File *input_file = 0;
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "-o") == 0 && arg_nr + 1 < nr_args)
            {
                arg = args[++arg_nr];
                File *output_file = get_file(cur_path, arg);
                (new Usage(output_file, step))->is_output(fout);
                // Add some fake lines for processing include
                output_file->loaded = true;
                LineInFile **ref_line = &output_file->lines;
                *ref_line = new LineInFile("#include \"stdc.h\"", output_file, 1);
                ref_line = &(*ref_line)->next;
                *ref_line = new LineInFile("#include \"syntax.h\"", output_file, 2);
            }
            else
            {
            }
        }
    }
    else if (strcmp(exec_fn, "match") == 0)
    {
        result = nr_args != 3 ? -1 : strcmp(args[1], args[2]) == 0 ? 0 : 1;
    }
    else if (strcmp(exec_fn, "sed") == 0)
    {
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (strcmp(arg, "-i") == 0)
                ;
            else if (strcmp(arg, "-e") == 0 && arg_nr + 1 < nr_args)
                arg_nr++;
            else
            {
                File *output_file = get_file(cur_path, arg);
                (new Usage(output_file, step))->is_input(fout);
                (new Usage(output_file, step))->is_output(fout);
            }
        }
    }
    else
    {
        fprintf(fout, "INFO generic command |%s| %d (Step %d)\n", args[0], nr_args, step->nr);
        for (int arg_nr = 1; arg_nr < nr_args; arg_nr++)
        {
            char *arg = args[arg_nr];
            if (*arg == '-')
            {
                int i = 0;
                indent(fout); fprintf(fout, "Switch %s\n", arg);
                if (strcmp(arg, "--base-address") == 0 || strcmp(arg, "--operating-system") == 0 || strcmp(arg, "--architecture") == 0)
                {
                    arg_nr++;
                }
            }
            else
            {
                File *file = get_file(cur_path, arg);
                
                Usage *usage = new Usage(file, step);
                
                if (file->exists || file->produced)
                    usage->is_input(fout);
                else
                    usage->is_output(fout);
            }
        }
    }
    indent_depth -= 2;
    return result;
}

bool read_line(FILE *f, char *buffer)
{
    bool result = fgets(buffer, MAX_LINE_SIZE, f);
    if (result)
    {
        int len = strlen(buffer);
        while (len > 0 && (buffer[len - 1] == '\n' || buffer[len - 1] == '\r'))
            buffer[--len] = '\0';
    }
    return result;
}   

bool is_pattern_line(const char *line)
{
    return line[0] == ' ' || (line[0] == '-' && line[1] != '-') || line[0] == '+';
} 

void process_patch_file(File *patch_file, const char *cur_path, Step *step, FILE *fout)
{
    patch_file->load(fout);
    if (patch_file->lines == 0)
    {
        fprintf(fout, "ERROR: Cannot open %s\n", patch_file->name);
        return;
    }
    LineInFile *p_line = patch_file->lines;
    while (p_line != 0)
    {
        if (strncmp(p_line->text, "+++ ", 4) != 0)
        {
            //fprintf(fout, "PATCH: Skip %s\n", p_line->text);
            p_line = p_line->next;
            continue;
        }
        
        char filename[MAX_PATH_SIZE];
        int i = 0;
        for (const char *s = p_line->text + 4; *s != '\0' && *s != ' ' && *s != '\t'; s++)
            if (i < MAX_PATH_SIZE - 1)
                filename[i++] = *s;
        filename[i] = '\0';
        fprintf(fout, "PATCH: File %s\n", filename);
        File *file = get_file(cur_path, filename);
        if (file->exists_now())
            (new Usage(file, step))->is_modified(fout);
        else
            (new Usage(file, step))->is_output(fout);
        
        file->load(fout);
        LineInFile **ref_line = &file->lines;
        long nr = 1;
                
        p_line = p_line->next;
        while (p_line != 0 && strncmp(p_line->text, "@@ -", 4) == 0)
        {
            long line_nr = 0;
            for (const char *s = p_line->text + 4; '0' <= *s && *s <= '9'; s++)
                line_nr = 10 * line_nr + *s - '0';
            fprintf(fout, "PATCH: Line %ld\n", line_nr);
            if (line_nr == 0)
                line_nr = 1;
            // Find closest matching
            int max_nr_matching = 0;
            long closest_match = -1;
            long match_line_nr;
            long try_nr = nr;
            for (LineInFile *try_line = *ref_line; try_line != 0 && (closest_match == -1 || try_nr < 10 + line_nr + closest_match); try_nr++, try_line = try_line->next)
            {
                bool matching = true;
                int nr_matching = 0;
                LineInFile *f_line = try_line;
                for (LineInFile *m_line = p_line->next; m_line != 0 && is_pattern_line(m_line->text); m_line = m_line->next) 
                    if (m_line->text[0] == '+')
                        ;
                    else
                    {
                        if (f_line != 0 && strcmp(m_line->text + 1, f_line->text) == 0)
                            nr_matching++;
                        else if (m_line->text[0] == '-')
                        {
                            matching = false;
                            break;
                        }
                        f_line = f_line != 0 ? f_line->next : 0;
                    }
                //fprintf(fout, "Result for %ld %d %ld\n", try_nr, matching, nr_matching);
                if (matching && nr_matching >= max_nr_matching)
                {
                    int diff_nr = try_nr - line_nr;
                    if (diff_nr < 0) diff_nr = -diff_nr;
                    
                    if (closest_match == -1 || nr_matching > max_nr_matching || diff_nr < closest_match)
                    {
                        closest_match = line_nr - try_nr;
                        match_line_nr = try_nr;
                    }
                    max_nr_matching = nr_matching;
                }
            }
            if (closest_match == -1 && *ref_line == 0)
            {
                // Check if it are only additions
                bool matching = true;
                for (LineInFile *m_line = p_line->next; m_line != 0 && is_pattern_line(m_line->text); m_line = m_line->next) 
                    if (m_line->text[0] != '+')
                    {
                        matching = false;
                        break;
                    }
                if (matching)
                    closest_match = nr;
            }
            
            if (closest_match == -1)
            {
                fprintf(fout, "ERROR: No matching for patch fragment in %ld\n", line_nr);
                break;
            }
            if (0 != closest_match)
                fprintf(fout, "INFO: %ld matched for %ld\n", match_line_nr, line_nr);
            while (*ref_line != 0 && nr < match_line_nr)
            {
                //fprintf(fout, "SKIP %ld %s\n", nr, (*ref_line)->text);
                ref_line = &(*ref_line)->next;
                nr++;
            }
            p_line = p_line->next;
            while (p_line != 0 && is_pattern_line(p_line->text))
            {
                fprintf(fout, "PATCH:   patch %c|%s|\n", p_line->text[0], p_line->text + 1);
                if (p_line->text[0] == '+')
                {
                    LineInFile *added_line = new LineInFile(p_line, 1);
                    added_line->next = *ref_line;
                    *ref_line = added_line;
                    ref_line = &added_line->next;
                }
                else
                {
                    fprintf(fout, "PATCH:      in  |%s| %ld\n", *ref_line != 0 ? (*ref_line)->text : "[EOF]", nr);
                    if (*ref_line != 0)
                    {
                        if (p_line->text[0] == '-')
                            *ref_line = (*ref_line)->next;
                        else
                            ref_line = &(*ref_line)->next;
                        nr++;
                    }
                }
                p_line = p_line->next;
            }
        }
    }
}


void dir_of_filename(const char *fn, char *dir)
{
    strcpy(dir, fn);
    char *last = 0;
    for (char *s = dir; *s != '\0'; s++)
        if (*s == '/')
            last = s;
    if (last != 0)
        *last = '\0';
}

const char *file_of_filename(const char *full_fn)
{
    const char *fn = full_fn;
    for (const char *s = full_fn; *s != '\0'; s++)
        if (*s == '/')
            fn = s + 1;
    return fn;
}

void path_of_filename(const char *full_fn, char *path)
{
    strcpy(path, full_fn);
    char *last = path;
    for (char *s = path; *s != '\0'; s++)
        if (*s == '/')
            last = s;
    *last = '\0';
}

FILE *open_file(const char *dir, const char *fn)
{
    char filename[MAX_PATH_SIZE];
    sprintf(filename, "%s/%s", dir, fn);
    //fprintf(stdout, "== '%s'\n", filename);
    return fopen(filename, "r");
}


const char *file_ext(const char *fn)
{
    const char *ext = "";
    for (const char *s = fn; *s != '\0'; s++)
        if (*s == '.')
            ext = s + 1;
        else if (*s == '/')
            ext = "";
    return ext;
}

bool include_source = false;

void output_file(FILE *f, const char *name, bool binary)
{
    char filename[MAX_PATH_SIZE];
    if (strncmp(name, "/sysa/", 6) == 0)
        strcpy(filename, "../git/live-bootstrap");
    else
        filename[0] = '\0';
    strcat(filename, name);
    FILE *fsource = fopen(filename, "r");
    if (fsource != 0)
    {
        fprintf(f, "<PRE>");
        if (binary)
        {
            int i = 0;
            unsigned char ch = fgetc(fsource);
            while (!feof(fsource))
            {
                fprintf(f, " %02X", ch);
                if (++i % 10 == 0)
                    fprintf(f, "\n");
                ch = fgetc(fsource);
            }
        }
        else
        {
            char ch = fgetc(fsource);
            if (ch != -1)
            {
                int col = 0;
                while (!feof(fsource))
                {
                    col++;
                    if (ch == '<')
                        fprintf(f, "&lt;");
                    else if (ch == '>')
                        fprintf(f, "&gt;");
                    else if (ch == '&')
                        fprintf(f, "&amp;");
                    else if ((unsigned char)ch == 160)
                        fprintf(f, "&nbsp;");
                    else if ((unsigned char)ch == 169)
                        fprintf(f, "&copy;");
                    else if ((unsigned char)ch == 194)
                        fprintf(f, "&Acirc;");
                    else if ((unsigned char)ch == 195)
                        fprintf(f, "&Atilde;");
                    else if ((unsigned char)ch == 197)
                        fprintf(f, "&Aring;");
                    else if ((unsigned char)ch == 216)
                        fprintf(f, "&Oslash;");
                    else if ((unsigned char)ch == 231)
                        fprintf(f, "&ccedil;");
                    else if ((unsigned char)ch == 246)
                        fprintf(f, "&ouml;");
                    else if (ch < 0)
                        fprintf(f, "&#%d;", (unsigned char)ch);
                    else if (ch == '\n' || ch == 12)
                    {
                        fprintf(f, "\n");
                        col = 0;
                    }
                    else if (ch == '\t')
                    {
                        fprintf(f, " ");
                        while (col % 4 > 0)
                        {
                            fprintf(f, " ");
                            col++;
                        }
                    }
                    else if (ch < ' ')
                        ; // skip control characters
                    else
                        fprintf(f, "%c", ch);
                    ch = fgetc(fsource);
                }
            }
        }
        
        fprintf(f, "</PRE>");
        fclose(fsource);
    }
}

void write_html_file(FILE *f, File *file, SubModule *submodules, bool binary)
{
    //fprintf(f, "<H3><A NAME=\"F%d\" HREF=\"%s\">%s</A></H3>\n\n", file->nr, file->name, file->name);
    fprintf(f, "<H3><A NAME=\"F%d\">%s</A></H3>\n\n", file->nr, file->name);
    if (file->source != 0)
    {
        fprintf(f, "Source: <A HREF=\"%s\">%s</A>\n<P>\n", file->source->url, file->source->url);
    }
    else
    {
        bool git = false;
        for (SubModule *sm = submodules; sm != 0; sm = sm->next)
        {
            int len = strlen(sm->path);
            if (strncmp(file->name, sm->path, len) == 0)
            {
                char url[MAX_PATH_SIZE];
                sprintf(url, sm->url, file->name + len);
                file->set_source(url);
                fprintf(f, "Source: <A HREF=\"#U%d\">%s</A>\n<P>\n", file->source->nr, url);
                git = true;
                break;
            }
        }
        if (!git)
            fprintf(f, "(No source)\n\n");
    }
    if (file->exec)
        fprintf(f, "Executable%s\n", file->exists ? " seed!" : "");
    fprintf(f, "<UL>\n");
    for (Usage *usage = file->usages; usage != 0; usage = usage->next_usage)
        fprintf(f, "<LI>%s <A HREF=\"#S%d\">step %d</A>\n",
            usage->as_exec ? "Used as executable" : usage->as_input ? "Used as input" : usage->as_output ? "Produced by" : "Used in",
            usage->step->nr, usage->step->nr);
    fprintf(f, "</UL>\n\n");
    
    if (!file->exists)
    {
        int len = strlen(file->name);
        if (len > 7 && strcmp(file->name + len - 7, ".tar.gz") == 0)
            ; // skip
        else if (len > 8 && strcmp(file->name + len - 8, ".tar.bz2") == 0)
            ; // skip
        else
            fprintf(f, "NoInput\n");
    }
    else if (include_source)
    {
        output_file(f, file->name, binary);
    }
}

void write_html(FILE *f, SubModule *submodules)
{
    char githash[41];
    githash[0] = '\0';
    {
        FILE *g = fopen("../git/live-bootstrap/.git/FETCH_HEAD", "r");
        if (g != 0)
        {
            fgets(githash, 40, g);
            githash[40] = '\0';
            fclose(g);
        }
    }
    
    fprintf(f, 
        "<HTML><HEAD>\n<TITLE>live-bootstrap</TITLE>\n"
        "</HEAD><BODY>\n\n<H1>live-bootstrap</H1>"
        "<!--ONEWAY-->\n"
        "This page is produced by the version of the program <TT>keam_parser.cpp</TT>\n"
        "listed at <A HREF=\"#Parser\">the bottom</A> of this page.\n"
        "The program parsers the contents of GitHub repositories\n"
        "<A HREF=\"https://github.com/oriansj/stage0\">oriansj/stage0</A> and\n"
        "<A HREF=\"https://github.com/fosslinux/live-bootstrap\">fosslinux/live-bootstrap</A>\n"
        "(the commit <A HREF=\"https://github.com/fosslinux/live-bootstrap/commit/%s\"><TT>%.7s</TT></A>)\n"
        "in order to find all the dependencies and list relevant input sources.\n"
        "The parsing stops at the execution of <TT>run.sh</TT>.\n"
        "(This is still work in progress.)\n"
        "<P>\n"
        "The code displayed on this page is not copyrighted by me but by the owners of\n"
        "respective repositories as also mentioned in the headers of the various files.\n"
        "<UL>\n"
        "<LI><A HREF=\"#Seeds\">Binary seeds files</A>\n"
        "<LI><A HREF=\"#Input\">Input source files</A>\n"
        "<LI><A HREF=\"#Steps\">Steps</A>\n"
        "<LI><A HREF=\"#Output\">Output files</A>\n"
        "<LI><A HREF=\"#Sources\">Sources</A>\n"
        "<LI><A HREF=\"#Parser\">Parse program</A>\n"
        "</UL>\n", githash, githash);

    fprintf(f, "\n\n<H2><A NAME=\"Seeds\">Binary seeds files</A></H2>\n\n");
    
    for (File *file = files; file != 0; file = file->next)
        if (file->exec && file->exists)
            write_html_file(f, file, submodules, true);
     
    fprintf(f, "<H2><A NAME=\"Input\">Input source files</A></H2>\n\n");
    
    for (File *file = files; file != 0; file = file->next)
        if (!file->exec && file->used_as_input && !file->produced)
            write_html_file(f, file, submodules, false);

     
    fprintf(f, "\n<H2><A NAME=\"Steps\">Steps</A></H2>\n\n");
    for (Step *step = all_steps; step != 0; step = step->next)
    {
        fprintf(f, "<H3><A NAME=\"S%d\">Step %d</A></H3>\n\n<UL>\n", step->nr, step->nr);
        for (Usage *use = step->uses; use != 0; use = use->next_use)
        {
            if (use->as_exec || use->as_input)
            {
                fprintf(f, "<LI>%s <A HREF=\"#%c%d\">%s</A>\n",
                    use->as_exec ? "Executes" : use->as_input ? "Uses as input" : use->as_output ? "Produceses" : "Uses",
                    use->file->produced_by != 0 ? 'S' : 'F',
                    use->file->produced_by != 0 ? use->file->produced_by->nr : use->file->nr,
                    use->file->name);
            }
            if (use->as_output)
            {
                fprintf(f, "<LI>Produces %s\n", use->file->name);
                fprintf(f, "<UL>\n");
                for (Usage *usage = use->file->usages; usage != 0; usage = usage->next_usage)
                    if (usage->as_exec || usage->as_input)
                        fprintf(f, "<LI>%s <A HREF=\"#S%d\">step %d</A>\n",
                            usage->as_exec ? "Used as executable" : usage->as_input ? "Used as input" : usage->as_output ? "Produced by" : 
                            usage->as_removed ? "Removed in" : "Used in",
                            usage->step->nr, usage->step->nr);
                fprintf(f, "</UL>\n\n");
            }
            if (use->as_removed)
            {
                fprintf(f, "<LI>Removed %s\n", use->file->name);
            }
        }
        fprintf(f, "</UL>\n\n");
    }
        
    fprintf(f, "\n<H2><A NAME=\"Output\">Output files</A></H2>\n\n<UL>\n");
    
    for (File *file = files; file != 0; file = file->next)
        if (file->produced && !file->removed)
        {
            bool used_as_input = false;
            int step_nr = -1;
            for (Usage *usage = file->usages; usage != 0 && !used_as_input; usage = usage->next_usage)
            {
                if (usage->as_input || usage->as_exec)
                    used_as_input = true;
                if (usage->as_output)
                    step_nr = usage->step->nr;
            }
            if (!used_as_input || file->exec)
            {
                fprintf(f, "<LI> %s", file->name);
                if (step_nr > 0)
                    fprintf(f, " produced by <A HREF=\"#S%d\">step %d</A>", step_nr, step_nr);
                if (used_as_input)
                    fprintf(f, " (also executed)");
                fprintf(f, "\n");
            }
        }
    fprintf(f, "</UL>\n\n");

    fprintf(f, "\n<H2><A NAME=\"Sources\">Sources</A></H2>\n\n");
    
    for (Source *source = sources; source != 0; source = source->next)
    {
        fprintf(f, "\n<H4><A NAME=\"U%d\" HREF=\"%s\">%s</A></H4>\n\n", source->nr, source->url, source->url);
        int single = source->files->next_source_file == 0; 
        if (single)
            fprintf(f, "File: ");
        else
            fprintf(f, "Files:<BR>\n<UL>\n");
        for (File *file = source->files; file != 0; file = file->next_source_file)
        {
            fprintf(f, "%s<A HREF=\"#F%d\">%s</A> used in:\n", single ? "" : "<LI>", file->nr, file->name);
            const char *comma = "";
            for (Usage *usage = file->usages; usage != 0; usage = usage->next_usage)
                if (usage->as_input)
                {
                    fprintf(f, "%s <A HREF=\"#S%d\">Step %d</A>", comma, usage->step->nr, usage->step->nr);
                    comma = ",";
                }
        }
        if (!single)
            fprintf(f, "</UL>\n");
        
        output_file(f, source->files->name, source->files->exec);
    }
    
    fprintf(f, "\n<H2><A NAME=\"Parser\">Parse program</A></H2>\n\n");
    fprintf(f, "Below the version of the <TT>kaem_parser.cpp</TT> program is given that is used to produce this page.\n<P>\n");

    output_file(f, "kaem_parser.cpp", false);
    
    fprintf(f,
        "\n\n"
        "<P><HR>\n"
        "<ADDRESS>\n"
        "<A HREF=\"index.html\">Home</A>\n"
        "</ADDRESS>\n"
        "</BODY></HTML>\n");
}


class MakeCommand
{
public:
    char *command;
    MakeCommand *next;
    MakeCommand(const char *com) : next(0)
    {
        command = copystr(com);
    }
};

class MakeSource;

class MakeItem
{
public:
    char *name;
    MakeSource *sources;
    bool is_target;
    bool is_phony;
    bool executed;
    MakeCommand *commands;
    MakeItem *next;
    MakeItem(const char *n) : sources(0), is_target(false), is_phony(false), executed(false), commands(0), next(0)
    {
        name = copystr(n);
    }
};

class MakeSource
{
public:
    MakeItem *item;
    MakeSource *next;
    MakeSource(MakeItem *i) : item(i), next(0) {}
};


class MakeCommentStripIterator : public AbstractIterator
{
public:
    MakeCommentStripIterator(AbstractIterator *source_it) : _source_it(source_it)
    {
        filename = _source_it->filename;
        _skip_comment();
    }
    void next()
    {
        _source_it->next();
        _skip_comment();
    }
private:
    AbstractIterator *_source_it;
    void _skip_comment()
    {
        more = _source_it->more;
        ch = _source_it->ch;
        if (!more) return;
        if (ch == '#')
        {
            do
            {
                _source_it->next();
                ch = _source_it->ch;
            }
            while (ch != '\0' && ch != '\n');
        }
        more = ch != '\0';
        filename = _source_it->filename;
        line = _source_it->line;
        column = _source_it->column;
    }
};

#define EXTRACE // fprintf(stderr, "%d %d %c\n", _depth, _state, ch);

class ExpandDefinesIterator : public AbstractIterator
{
public:
    ExpandDefinesIterator(AbstractIterator *source_it, Var *&vars, int depth, FILE *fout, char till = '\0')
      : _source_it(source_it), _vars(vars), _fout(fout), _till(till), _depth(depth)
    {
        filename = _source_it->filename;
        more = _source_it->more;
        ch = _source_it->ch;
        _state = 0;
        next();
    }
    void next()
    {
        if (!more)
        {
            ch = '\0';
            return;
        }

        switch(_state)
        {
            case 0: goto s0;
            case 1: goto s1;
            case 2: goto s2;
            case 3: goto s3;
            case 4: goto s4;
            case 5: goto s5;
            case 6: goto s6;
            case 7: goto s7;
            case 8: goto s8;
            case 9: goto s9;
        }

        s0:
        while (ch != '\0')
        {
            if (ch == '$')
            {
                _source_it_next_char();
                if (ch == '(')
                {
                    _source_it_next_char();
                    _i = 0;
                    while (('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ch == '_') 
                    {
                        if (_i < 100)
                            _buffer[_i++] = ch;
                        _source_it_next_char();
                    }
                    _buffer[_i] = '\0';
                    _is_suffix = strcmp(_buffer, "addsuffix") == 0;
                    if (strcmp(_buffer, "addprefix") == 0 || _is_suffix)
                    {
                        //fprintf(stderr, "Command %s\n", _buffer);
                        _innerIt = new ExpandDefinesIterator(_source_it, _vars, _depth + 1, _fout, ')');
                        while (_innerIt->ch == ' ')
                            _innerIt->next();
                        
                        _i = 0;
                        for (; _innerIt->ch != ','; _innerIt->next())
                            if (_i < 100)
                                _buffer[_i++] = _innerIt->ch;
                        _buffer[_i] = '\0';
                        if (_innerIt->ch == ',')
                        {
                            _innerIt->next();
                        }
                        for (;;)
                        {
                            while (_innerIt->ch == ' ' || _innerIt->ch == '\t')
                            {
                                ch = _innerIt->ch;
                                line = _innerIt->line;
                                column = _innerIt->column;
                                _state = 1; EXTRACE return; s1:
                                _innerIt->next();
                            }
                            
                            if (_innerIt->ch == '\0')
                                break;
                            
                            if (_is_suffix)
                            {
                                ch = _innerIt->ch;
                                while (ch != '\0' && ch != ' ' && ch != '\t')
                                {
                                    line = _innerIt->line;
                                    column = _innerIt->column;
                                    _state = 2; EXTRACE return; s2:
                                    _innerIt->next();
                                    ch = _innerIt->ch;
                                }
                                for (_i = 0; _buffer[_i] != '\0'; _i++)
                                {
                                    ch = _buffer[_i];
                                    _state = 3; EXTRACE return; s3:;
                                }
                            }
                            else
                            {
                                for (_i = 0; _buffer[_i] != '\0'; _i++)
                                {
                                    ch = _buffer[_i];
                                    _state = 4; EXTRACE return; s4:;
                                }
                                ch = _innerIt->ch;
                                while (ch != '\0' && ch != ' ' && ch != '\t')
                                {
                                    line = _innerIt->line;
                                    column = _innerIt->column;
                                    _state = 5; EXTRACE return; s5:
                                    _innerIt->next();
                                    ch = _innerIt->ch;
                                }
                            }
                        }
                        delete _innerIt;
                    }
                    else
                    {
                        _s = (char*)"";
                        {
                            char *value = get_var(_vars, _buffer);
                            if (value == 0)
                                fprintf(_fout, "ERROR: %s.%ld define '%s' not defined\n", filename, line, _buffer);
                            else
                                _s = value;
                        }
                        //fprintf(stderr, "Substitude |%s| by |%s|\n", _buffer, _s);
                        for (; *_s != '\0'; _s++)
                        {
                            ch = *_s;
                            _state = 6; EXTRACE return; s6:;
                        }
                    }
                    while (_source_it->ch != '\0' && _source_it->ch != ')')
                        _source_it->next();
                    ch = _source_it->ch;
                    if (ch == ')')
                        _source_it->next();
                    ch = _source_it->ch;
                    if (ch == _till)
                    {
                        ch = '\0';
                        more = false;
                    }
                }
                else
                {
                    _buffer[0] = ch;
                    ch = '$';
                    _state = 7; EXTRACE return; s7:;
                    if (_buffer[0] != '\0')
                    {
                        ch = _buffer[0];
                        _state = 8; EXTRACE return; s8:;
                        _source_it_next_char();
                    }
                }
                _state = 0;
            }
            else
            {
                _state = 9; EXTRACE return; s9:
                _source_it_next_char();
            }
        }
        ch = '\0';
        more = false;
    }
private:
    void _source_it_next_char()
    {
        _source_it->next();
        ch = _source_it->ch;
        line = _source_it->line;
        column = _source_it->column;
        if (ch == _till)
            ch = '\0';
    }
    
    Var *&_vars;
    AbstractIterator *_source_it;
    int _state;
    ExpandDefinesIterator *_innerIt;
    FILE *_fout;
    char _till;
    int _i;
    char _buffer[101];
    bool _is_suffix;
    char *_s;
    MakeSource *_source_item;
    int _depth;
};

class ToLineIterator
{
public:
    ToLineIterator(AbstractIterator *source_it) : _source_it(source_it)
    {
        more = true;
        next();
    }
    bool more;
    void next()
    {
        if (_source_it->ch == '\0')
        {
            more = false;
            return;
        }
        int i = 0;
        for (;_source_it->ch != '\0' && _source_it->ch != '\n'; _source_it->next())
        {
            if (i < 10000)
                _line[i++] = _source_it->ch;
        }
        _line[i] = '\0';
        //fprintf(stdout, "%s.%ld %s\n", _source_it->filename, _source_it->line, _line);
        if (_source_it->ch == '\n')
            _source_it->next();
    }
    char *line() { return _line; }
private:
    AbstractIterator *_source_it;
    char _line[10001];
};

MakeItem *get_make_item(MakeItem *&items, const char *name)
{
    //fprintf(stderr, "get_make_item '%s'\n", name);
    MakeItem **ref_item = &items;
    for (; *ref_item != 0; ref_item = &(*ref_item)->next)
        if (strcmp((*ref_item)->name, name) == 0)
            return *ref_item;
    *ref_item = new MakeItem(name);
    return *ref_item;
}

void expand_command(MakeItem *item, char *command, Var *vars, char *exp_command, FILE *fout)
{
    if (item->name[0] == '%')
    {
        strcpy(exp_command, command);
        return;
    }
    
    char *t = exp_command;

    char *s_stack[40];
    int depth = 0;

    char *s = command;
    for (;;)
    {
        while (*s == '\0' && depth > 0)
            s = s_stack[--depth];
        if (*s == '\0')
            break;
            
        if (*s == '$')
        {
            s++;
            if (*s == '@')
            {
                s++;
                for (char *n = item->name; *n != '\0'; n++)
                    *t++ = *n;
            }
            else if (*s == '<')
            {
                s++;
                if (item->sources != 0)
                    for (char *n = item->sources->item->name; *n != '\0'; n++)
                        *t++ = *n;
            }
            else if (*s == '^')
            {
                s++;
                for (MakeSource *source = item->sources; source != 0; source = source->next)
                {
                    for (char *n = source->item->name; *n != '\0'; n++)
                        *t++ = *n;
                    if (source->next != 0)
                        *t++ = ' ';
                }
            }
            else if (*s == '*')
            {
                s++;
                if (item->sources != 0)
                {
                    char *name = item->sources->item->name;
                    int end = strlen(name);
                    for (int p = end - 1; p >= 0; p--)
                        if (name[p] == '.')
                        {
                            end = p;
                            break;
                        }
                    for (int i = 0; i < end; i++)
                        *t++ = name[i];
                }
            }
            else if (*s == '(')
            {
                s++;
                char var_name[50];
                int i = 0;
                for (; *s != '\0' && *s != ')'; s++)
                    if (i < 49)
                        var_name[i++] = *s;
                var_name[i] = '\0';
                if (*s == ')') s++;
                char *var_value = get_var(vars, var_name);
                if (var_value == 0)
                    fprintf(fout, "ERROR: Did not find var |%s|\n", var_name);
                else
                {
                    s_stack[depth++] = s;
                    s = var_value;
                }                   
            }
            else if (*s == ' ')
            {
                s++;
                *t++ = '$';
                *t++ = ' ';
            }
            else
            {
                fprintf(stderr, "Unknown $%c\n", *s);
                fprintf(fout, "ERROR: Unknown $%c in |%s|\n", *s, command);
            }
        }
        else
            *t++ = *s++;
    }
    *t = '\0';
}

void expand_commands(MakeItem *item, char *commands[], Var *vars, int nr_commands, FILE *fout)
{
    MakeCommand **ref_command = &item->commands;
    for (int i = 0; i < nr_commands; i++)
    {
        char exp_command[MAX_LINE_SIZE];
        expand_command(item, commands[i], vars, exp_command, fout);
        *ref_command = new MakeCommand(exp_command);
        ref_command = &(*ref_command)->next;
    }
}

void make_item(MakeItem *item, const char *cur_path, bool top, FILE *fout, Step *make_step, Var *vars, MakeItem *&items)
{
    if (item->executed)
        return;
    item->executed = true;
    indent(fout); fprintf(fout, "Make item: %s\n", item->name);
    if (!item->is_phony && top)
    {
        File *file = get_file(cur_path, item->name);
        if (file->produced)
            return;
        //(new Usage(get_file(cur_path, item->name), make_step))->is_output(fout);
        top = false;
    }
    if (item->sources == 0 && item->commands == 0)
    {
        indent(fout); fprintf(fout, "- Input: %s\n", item->name);
    }
    for (MakeSource *source = item->sources; source != 0; source = source->next)
    {
        indent_depth += 4;
        make_item(source->item, cur_path, top, fout, make_step, vars, items);
        indent_depth -= 4;
    }
    for (MakeCommand *command = item->commands; command != 0; command = command->next)
    {
        indent(fout); fprintf(fout, "- Execute: %s\n", command->command);
        parse_command(cur_path, command->command, fout);
    }
}

class TheFileIterator : public AbstractIterator
{
public:
    TheFileIterator(File *f)
    {
        more = false;
        while (f != 0)
        {
            _loaded = f->loaded;
            if (_loaded)
            {
                _line = f->lines;
                _s = _line != 0 ? _line->text : 0;
                more = _s != 0;
                break;
            }
            else
            {
                _f = fopen_mapped(f->name);
                if (_f != 0)
                {
                    more = true;
                    break;
                }
            }
            f = f->copy_from;
        }
        if (!more)
        {
            fprintf(stdout, "File %s is empty %p\n", f->name, _line);
            ch = '\0';
            return;
        }
        filename = f->name;
        ch = '\n';
        line = 0;
        column = 0;
        next();
    }
    void next()
    {
        if (!more)
            return;
        column++;
        if (_loaded)
        {
            if (ch == '\n' && _line != 0)
            {
                filename = _line->file->name;
                line = _line->line;
                column = 1;
            }
            if (_s == 0)
            {
                more = false;
                ch = '\0';
            }
            else if (*_s == '\0')
            {
                ch = '\n';
                _line = _line->next;
                _s = _line != 0 ? _line->text : 0;
            }
            else
                ch = *_s++;
        }
        else
        {
            if (ch == '\n')
            {
                line++;
                column = 1;
            }
            ch = fgetc(_f);
            more = !feof(_f);
            if (!more)
            {
                ch = '\0';
                fclose(_f);
                _f = 0;
            }
            else if (ch == '\r')
                next();
        }
    }
private:
    bool _loaded;
    LineInFile *_line;
    const char *_s;
    FILE *_f;
};

#define ON_LINE // fprintf(stderr, "%d: ", __LINE__);


void remove_escapes(const char *in, char *out)
{
    bool in_string = false;
    for (; *in != '\0'; in++)
    {
        if (*in == '\\')
        {
            in++;
            if (*in == ' ' && !in_string)
                *out++ ='\\';
            if (*in == '"')
                in_string = !in_string;
        }
        *out++ = *in;
    }
    *out = '\0';
}
            

void parse_a_make_file(const char *cur_path, File *file, Var *&vars, MakeItem *&items, Step *step, FILE *fout)
{
    fprintf(fout, "PARSE: %s\n", file->name);
    TheFileIterator *fileIter = new TheFileIterator(file);
    MakeCommentStripIterator *commentStripIterator = new MakeCommentStripIterator(fileIter);
    LineSpliceIterator *lineSpliceIterator = new LineSpliceIterator(commentStripIterator);
    ExpandDefinesIterator *expandDefinesIterator = new ExpandDefinesIterator(lineSpliceIterator, vars, 1, fout);
    //ToLineIterator toLineIterator(commentStripIterator);
    ToLineIterator toLineIterator(expandDefinesIterator);
    
    bool in_if = false;
    bool skip = false;
    
    while (toLineIterator.more)
    {
        char *line = toLineIterator.line();
        if (in_if && *line == ' ')
            line++;
        char *s = line;
        //fprintf(stderr, "Process |%s|\n", s);
        if (*s == '\0')
        {
            toLineIterator.next();
            continue;
        }
        if (*s == ' ')
        {
            toLineIterator.next();
            fprintf(fout, "ERROR: Skipping line with spaces here\n");
            continue;
        }
        
        char target[101];
        if (strncmp(s, "ifeq ", 5) == 0)
        {
            s += 5;
            while (*s == ' ' || *s == '\t') s++;
            if (*s == '(') s++;
            char arg1[50];
            int i = 0;
            for (; *s != '\0' && *s != ','; s++)
                if (i < 49)
                    arg1[i++] = *s;
            arg1[i] = '\0';
            if (*s == ',') s++;
            char arg2[50];
            i = 0;
            for (; *s != '\0' && *s != ','; s++)
                if (i < 49)
                    arg2[i++] = *s;
            arg2[i] = '\0';
            in_if = true;
            skip = strcmp(arg1, arg2) != 0;
            toLineIterator.next();
        }
        else if (strncmp(s, "else", 4) == 0)
        {
            skip = !skip;
            toLineIterator.next();
        }
        else if (strncmp(s, "endif", 5) == 0)
        {
            in_if = false;
            toLineIterator.next();
        }
        else if (skip)
            toLineIterator.next(); // skip
        else if (strncmp(s, "include ", 8) == 0)
        {
            s += 7;
            while (*s == ' ' || *s == '\t' || *s == ':') s++;
            File *include_file = get_file(cur_path, s);
            (new Usage(include_file, step))->is_input(fout);

            fprintf(fout, "PARSE include |%s|\n", s);

            char include_cur_path[MAX_PATH_SIZE];
            path_of_filename(include_file->name, include_cur_path);

            parse_a_make_file(include_cur_path, include_file, vars, items, step, fout);     
            
            toLineIterator.next();
        }
        else if (strncmp(s, ".SUFFIXES", 9) == 0)
        {
            // Skip the line
            toLineIterator.next();
        } 
        else if (strncmp(s, ".PHONY", 6) == 0 && (s[6] == ' ' || s[6] == '\t' || s[6] == ':'))
        {
            s += 7;
            while (*s == ' ' || *s == '\t' || *s == ':') s++;
            
            while (*s != '\0')
            {
                int i = 0;
                for (; *s != '\0' && *s != ' ' && *s != '\t'; s++)
                    if (i < 100)
                        target[i++] = *s;
                target[i] = '\0';
                ON_LINE
                get_make_item(items, target)->is_phony = true;

                while (*s == ' ' || *s == '\t') s++;
            }
            toLineIterator.next();
        }
        else if (strncmp(s, ".c.o", 4) == 0 && (s[4] == ' ' || s[4] == '\t' || s[4] == ':'))
        {
            s += 4;
            while (*s != '\0' && *s != ';') s++;
            if (*s == ';') s++;
            while (*s == ' ' || *s == '\t') s++;
            if (*s != '\0')
                vars = new Var(".c.o", s, false, vars);
            toLineIterator.next();
        }
        else
        {
            bool is_define = false;
            int i = 0;
            while (('A' <= *s && *s <= 'Z') || ('a' <= *s && *s <= 'z') || *s == '_')
                if (i < 100)
                    target[i++] = *s++;
            target[i] = '\0';
            while (*s == ' ' || *s == '\t') s++;
            if (*s == '=')
            {
                s++;
                while (*s == ' ' || *s == '\t') s++;
                Var *prev_dev = get_var_var(vars, target);
                if (prev_dev == 0 || !prev_dev->override)
                {
                    char value[MAX_LINE_SIZE];
                    remove_escapes(s, value);
                    vars = new Var(target, value, false, vars);
                }
                //fprintf(stderr, "INFO '%s' = '%s'\n", target, s);
                toLineIterator.next();
            }
            else if (s[0] == '+' && s[1] == '=')
            {
                s += 2;
                while (*s == ' ' || *s == '\t') s++;
                Var *var = get_var_var(vars, target);
                if (var != 0)
                {
                    char value[MAX_LINE_SIZE];
                    sprintf(value, "%s ", var->value);
                    remove_escapes(s, value + strlen(value));
                    var->value = copystr(value);
                }
                toLineIterator.next();
            }
            else if (strstr(line, ":") != 0)
            {
                MakeSource *targets = 0;
                MakeSource **ref_target = &targets;
                s = line;
                while (*s != ':')
                {
                    i = 0;
                    for (; *s != ':' && *s != ' ' && *s != '\t'; s++)
                        if (i < 100)
                            target[i++] = *s;
                    target[i] = '\0';
                    ON_LINE
                    *ref_target = new MakeSource(get_make_item(items, target));
                    ref_target = &(*ref_target)->next;
                    while (*s == ' ' || *s == '\t') s++;
                }
                s++;
                while (*s == ' ' || *s == '\t') s++;
                
                MakeSource *sources = 0;
                MakeSource **ref_source = &sources;
                char *pattern = 0;
                if (strstr(s, ":") != 0)
                {
                    pattern = copystr(s);
                }
                else
                {
                    while (*s != '\0')
                    {
                        i = 0;
                        for (; *s != '\0' && *s != ' ' && *s != '\t'; s++)
                            if (i < 100)
                                target[i++] = *s;
                        target[i] = '\0';
                        ON_LINE
                        *ref_source = new MakeSource(get_make_item(items, target));
                        ref_source = &(*ref_source)->next;
                        while (*s == ' ' || *s == '\t') s++;
                    }                       
                }
                
                // read the commands
                char *commands[20];
                int nr_commands = 0;

                toLineIterator.next();
                while (toLineIterator.more && (toLineIterator.line()[0] == ' ' || toLineIterator.line()[0] == '\t'))
                {
                    //fprintf(stderr, "Command |%s|\n", toLineIterator.line());
                    char *s = toLineIterator.line();
                    while (*s == ' ' || *s == '\t') s++;
                    if (nr_commands < 10)
                        commands[nr_commands++] = copystr(s);
                    toLineIterator.next();
                }
                
                if (pattern != 0)
                {
                    //fprintf(stderr, "pattern: |%s|\n", pattern);
                    for (MakeSource *make_target = targets; make_target != 0; make_target = make_target->next)
                    {
                        char *s = pattern;
                        i = 0;
                        for (; *s != ':' && *s != ' ' && *s != '\t'; s++)
                            if (*s == '%')
                            {
                                for (char *s2 = make_target->item->name; *s2 != '\0'; s2++)
                                    if (i < 100)
                                        target[i++] = *s2;
                            }
                            else if (i < 100)
                                target[i++] = *s;
                        target[i] = '\0';
                        while (*s == ' ' || *s == '\t') s++;
                        if (*s == ':') s++;
                        ON_LINE
                        MakeItem *target_item = get_make_item(items, target);
                        MakeSource **ref_make_source = &target_item->sources;
                        
                        for (;;)
                        {
                            while (*s == ' ' || *s == '\t') s++;
                            if (*s == '\0') break;
                            i = 0;
                            for (; *s != '\0' && *s != ' ' && *s != '\t'; s++)
                                if (*s == '%')
                                {
                                    for (char *s2 = make_target->item->name; *s2 != '\0'; s2++)
                                        if (i < 100)
                                            target[i++] = *s2;
                                }
                                else if (i < 100)
                                    target[i++] = *s;
                            target[i] = '\0';
                            ON_LINE
                            *ref_make_source = new MakeSource(get_make_item(items, target));
                            ref_make_source = &(*ref_make_source)->next;
                        }
                        expand_commands(target_item, commands, vars, nr_commands, fout);
                    }
                }
                else
                {
                    for (MakeSource *make_target = targets; make_target != 0; make_target = make_target->next)
                    {
                        make_target->item->sources = sources;
                        expand_commands(make_target->item, commands, vars, nr_commands, fout);
                    }
                }
            }
            else
            {
                fprintf(fout, "ERROR: %s unknown: %s\n", expandDefinesIterator->filename, line);
                toLineIterator.next();
            }
        }
    }
}


void parse_make_file(const char *cur_path, File *file, char **args, int nr_args, Var *vars, Step *step, FILE *fout)
{
    //fprintf(stderr, "parse_make_file %s %s\n", cur_path, file->name);
    FILE *make_f = 0;
    if (file == 0)
    {
        char makefile_fn[MAX_PATH_SIZE];
        combine_dir_fn(cur_path, "Makefile", makefile_fn);
        file = find_file(makefile_fn);
        if (file == 0)
        {
            make_f = fopen(makefile_fn, "r");
            if (make_f != 0)
                file = get_file(makefile_fn);
            else
            {
                combine_dir_fn(cur_path, "makefile", makefile_fn);
                file = find_file(makefile_fn);
                if (file == 0)
                {
                    make_f = fopen(makefile_fn, "r");
                    if (make_f != 0)
                        file = get_file(makefile_fn);
                }
            }
        }
    }
    if (file == 0)
        return;
    (new Usage(file, step))->is_input(fout);
    while (file->copy_from != 0)
        file = file->copy_from;
    if (make_f == 0)
        make_f = fopen(file->name, "r");
    if (make_f == 0)
    {
        fprintf(fout, "ERROR: Cannot open file %s\n", file->name);
        return;
    }
    fclose(make_f);
    MakeItem *items = 0;
    
    vars = new Var("CC", "cc", false, vars);
    vars = new Var("YACC", "yacc", false, vars);
    vars = new Var("YFLAGS", "/Define/Verbose", false, vars);
    vars = new Var("LEX", "lex", false, vars);
    vars = new Var("CPPFLAGS", "", false, vars);
    vars = new Var("CFLAGS", "", false, vars);
    vars = new Var("TARGET_ARCH", "", false, vars);
    vars = new Var("DESTDIR", "", false, vars);
    vars = new Var("PREFIX", "/usr", false, vars);
    vars = new Var("CPPFLAGS", "", false, vars);
    vars = new Var("WARN", "", false, vars);
    vars = new Var("ROOT", "", false, vars);
    vars = new Var("INSTALL", "install", false, vars);
    vars = new Var("LIBDIR", "/usr/lib", false, vars);
    vars = new Var("MANDIR", "/usr/man", false, vars);
    vars = new Var("LIBS", "", false, vars);
    vars = new Var("STRIP", "strip", false, vars);
    //vars = new Var("COMPILE.c", "$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c", false, vars);
    //vars = new Var("OUTPUT_OPTION", "-o $@", false, vars);
    vars = new Var(".c.o", "$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<", false, vars);
    vars = new Var(".y.c", "$(YACC) $(YFLAGS) $< \n mv -f y.tab.c $@", false, vars);
    vars = new Var(".l.c", "$(LEX) $(LFLAGS) -t $< \n mv lexyy.c $@", false, vars);
    
    bool is_install = false;
    for (int i = 0; i < nr_args; i++)
    {
        char arg[100];
        int j = 0;
        for (char *s = args[i]; *s != '\0'; s++)
        {
            if (*s == '\\' && s[1] != '\0') s++;
            if (j < 99)
                arg[j++] = *s;
        }
        arg[j] = '\0';
        //fprintf(stdout, "Make arg %s\n", arg);
        char *s = strstr(arg, "=");
        if (s != 0)
        {
            *s++ = '\0';
            char value[201];
            if (*s == '"')
            {
                int i = 0;
                for (s++; *s != '\0' && *s != '"'; s++)
                {
                    if (*s == '\\')
                        s++;
                    if (i < 200)
                        value[i++] = *s;
                }
                value[i] = '\0';
            }
            else
                strcpy(value, s);
            vars = new Var(arg, value, false, vars);
            vars->override = true;
        }
        else
            get_make_item(items, arg)->is_target = true;
    }
    
    if (items == 0)
        get_make_item(items, "all")->is_target = true;

    fprintf(fout, "PARSE %s\n", file->name);
    parse_a_make_file(cur_path, file, vars, items, step, fout);
    
    // Expand possible 
    for (MakeItem *item = items; item != 0; item = item->next)
    {
        if (item->sources == 0 || item->commands == 0)
        {
            // See if there is a matching rule:
            bool wildcard_match = false;
            for (MakeItem *item1 = items; item1 != 0; item1 = item1->next)
                if (item1->name[0] == '%')
                {
                    fprintf(fout, "TRY |%s|\n", item1->name);
                    int len = strlen(item->name);
                    int offset = len - strlen(item1->name) + 1;
                    if (offset > 0 && strcmp(item->name + offset, item1->name + 1) == 0)
                    {
                        MakeSource **ref_source = &item->sources;
                        for (MakeSource *source1 = item1->sources; source1 != 0; source1 = source1->next)
                        {
                            char exp_source[MAX_PATH_SIZE];
                            int i = 0;
                            for (char *s = source1->item->name; *s != '\0'; s++)
                                if (*s == '%')
                                {
                                    for (int j = 0; j < offset; j++)
                                        exp_source[i++] = item->name[j];
                                }
                                else
                                    exp_source[i++] = *s;
                            exp_source[i] = '\0';
                            fprintf(fout, "Added expanded source: |%s|\n", exp_source);
                            *ref_source = new MakeSource(get_make_item(items, exp_source));
                            ref_source = &(*ref_source)->next;
                        }
                        
                        MakeCommand **ref_command = &item->commands;
                        for (MakeCommand *command1 = item1->commands; command1 != 0; command1 = command1->next)
                        {
                            char exp_command[MAX_LINE_SIZE];
                            expand_command(item, command1->command, vars, exp_command, fout);
                            fprintf(fout, "Added expanded command: |%s|%s|\n", command1->command, exp_command);
                            *ref_command = new MakeCommand(exp_command);
                            ref_command = &(*ref_command)->next;
                        }
                        wildcard_match = true;
                        break;
                    }
                }
                
            if (!wildcard_match && item->commands == 0 && !file_exists(cur_path, item->name))
            {
                fprintf(fout, "SEEK sources for %s\n", item->name);
                char *alt_rule = 0;
                if (strcmp(file_ext(item->name), "o") == 0)
                {
                    char c_filename[MAX_LINE_SIZE];
                    strcpy(c_filename, item->name);
                    c_filename[strlen(c_filename) - 1] = 'c';
                    bool included = false;
                    for (MakeSource *source = item->sources; source != 0; source = source->next)
                        if (strcmp(c_filename, source->item->name) == 0)
                        {
                            included = true;
                            break;
                        }
                    if (!included)
                    {
                        MakeSource *old_sources = item->sources;
                        item->sources = new MakeSource(get_make_item(items, c_filename));
                        item->sources->next = old_sources;
                    }
                    alt_rule = get_var(vars, ".c.o");
                    /*
                    if (oc_rule != 0)
                        strcpy(cc_command, oc_rule);
                    else
                    {
                        //(new Usage(get_file(cur_path, c_file), make_step))->is_input(fout);
                        char *cc_def = get_var(vars, "CC");
                        char *cppflags_def = get_var(vars, "CPPFLAGS");
                        char *cflags_def = get_var(vars, "CFLAGS");
                        if (cc_def != 0 && cflags_def != 0)
                            sprintf(cc_command, "%s %s %s $^ -o %s\n", cc_def, cppflags_def, cflags_def, item->name);
                    }
                    */
                }
                else if (strcmp(file_ext(item->name), "c") == 0)
                {
                    char yl_filename[MAX_LINE_SIZE];
                    ///combine_dir_fn(cur_path, item->name, comb_fn);
                    strcpy(yl_filename, item->name);
                    yl_filename[strlen(yl_filename) - 1] = 'l';
                    if (file_exists(cur_path, yl_filename))
                    {
                        if (item->sources == 0)
                        {
                            MakeItem *l_item = get_make_item(item, yl_filename);
                            item->sources = new MakeSource(l_item);
                        }
                        alt_rule = get_var(vars, ".l.c");
                    }
                    else
                    {
                        yl_filename[strlen(yl_filename) - 1] = 'y';
                        if (file_exists(cur_path, yl_filename))
                        {
                            if (item->sources == 0)
                            {
                                MakeItem *y_item = get_make_item(item, yl_filename);
                                item->sources = new MakeSource(y_item);
                            }
                            alt_rule = get_var(vars, ".y.c");
                        }
                    }
                    if (alt_rule != 0)
                        fprintf(fout, "INFO: command for %s is |%s|\n", item->name, alt_rule);
                }
                
                if (alt_rule != 0)
                {
                    MakeCommand **ref_command = &item->commands;
                    for (char *s = alt_rule; s != 0;)
                    {
                        char *ns = strstr(s, "\n");
                        if (ns != 0)
                            *ns++ = '\0';
                        char exp_cc_command[MAX_LINE_SIZE];
                        expand_command(item, s, vars, exp_cc_command, fout);
                        fprintf(fout, "Expand |%s|\n  into |%s|\n", s, exp_cc_command);
                        indent(fout); fprintf(fout, "- Execute: %s\n", exp_cc_command);
                        *ref_command = new MakeCommand(exp_cc_command);
                        ref_command = &(*ref_command)->next;
                        s = 0;
                        if (ns != 0)
                        {
                            while (*ns == ' ' || *ns == '\t')
                                ns++;
                            if (*ns != '\0')
                                s = ns;
                        }
                    }
                }
            }
        }
    }   

    for (MakeItem *item = items; item != 0 && item->is_target; item = item->next)
        make_item(item, cur_path, true, fout, step, vars, items);
}

class LocalIncludeResolver : public AbstractIncludePathResolver
{
public:
    LocalIncludeResolver(const char *cur_path, char *c_file_path, const char **includes, int nr_includes, Define *&defines, Step *step, FILE *fout)
      : _cur_path(cur_path), _c_file_path(c_file_path), _nr_includes(nr_includes), _defines(defines), _step(step), _fout(fout)
    {
        for (int i = 0; i < _nr_includes; i++)
        {
            combine_dir_fn(_cur_path, includes[i], _includes[i]);
            int len = strlen(_includes[i]);
            if (len > 0 && _includes[i][len-1] == '/')
                _includes[i][len-1] = '\0';
            fprintf(_fout, "Info: Include '%s'\n", _includes[i]);
        }
    }
    
    AbstractIterator *iteratorFor(const char *path, bool include_next, AbstractIterator *context)
    {
        if (debug_c_parsing) fprintf(_fout, "INCLUDE %s.%ld |%s|\n", context->filename, context->line, path);
        char path2[MAX_PATH_SIZE];
        strcpy(path2, path + 1);
        char include_fn[MAX_PATH_SIZE];
        FILE *f = 0;
        File *file = 0;
        if (   (path[0] == '<' && path2[strlen(path2) - 1] == '>')
            || (path[0] == '"' && path2[strlen(path2) - 1] == '"'))
        {
            path2[strlen(path2) - 1] = '\0';

            if (include_next)
            {
                int diff_len = strlen(context->filename) - strlen(path2);
                //if (diff_len < 0 || strcmp(context->filename + diff_len, path2) != 0)
                fprintf(stderr, "include_next %s %s\n", context->filename, path2);
                return new StringIterator("","unknown");
            }

            if (path[0] == '"')
            {
                combine_dir_fn(_c_file_path, path2, include_fn);
                file = find_file(include_fn);
                if (file != 0)
                {
                    while (file->copy_from) file = file->copy_from;
                    f = fopen_mapped(file->name, _fout);
                }
                else
                    f = fopen_mapped(include_fn, _fout);
            }
                                                
            for (int i = 0; i < _nr_includes && (file == 0 && f == 0); i++)
            {
                combine_dir_fn(_includes[i], path2, include_fn);
                file = find_file(include_fn);
                if (file != 0)
                {
                    while (file->copy_from) file = file->copy_from;
                    f = fopen_mapped(file->name, _fout);
                }
                else
                    f = fopen_mapped(include_fn, _fout);
            }
        }

        if (file != 0 || f != 0)
        {
            while (f == 0 && file->copy_from != 0)
            {
                file = file->copy_from;
                f = fopen_mapped(file->name, _fout);
            }
            if (f != 0)
            {
                fclose(f);
    
                if (file == 0)
                    file = get_file(include_fn);
                if (_step->hasInputUseOf(file))
                    return new StringIterator("","unknown"); // Lets process include file once
                    
                (new Usage(file, _step))->is_input(_fout);
                
                return 
                    new InlineIncludesIterator(
                        new CommentStripIterator(
                            new LineSpliceIterator(
                                new TrigraphIterator(
                                    new TheFileIterator(file)))),
                            this,
                            _defines);
            }
            else
                fprintf(_fout, "Info: %s.%ld Cannot parse include file %s\n", context->filename, context->line, path);
        }
        else
            fprintf(_fout, "Warning: %s.%ld Cannot parse include file %s\n", context->filename, context->line, path);
            
        return new StringIterator("","unknown");
    }
private:
    const char *_cur_path;
    const char *_c_file_path;
    char _includes[10][MAX_PATH_SIZE];
    int _nr_includes;
    Define *&_defines;
    Step *_step;
    FILE *_fout;
    int _nested_level;
    struct
    {
        const char *file_name;
        int at;
    } _nested[100];
};


void parse_c_file(const char *cur_path, File *file, const char **includes, int nr_includes, Define *&defines, Step *step, FILE *fout)
{
    char c_file_path[MAX_PATH_SIZE];
    path_of_filename(file->name, c_file_path);
    if (!file->exists && !file->loaded)
    {
        file = file->existing();
        if (file == 0)
        {
            fprintf(fout, "ERROR: file does not exist\n");
            return;
        }
        fprintf(fout, "Info: Copied from %s\n", file->name);
    }
    TheFileIterator *fileIterator = new TheFileIterator(file);
    TrigraphIterator *trigraphIterator = new TrigraphIterator(fileIterator);
    LineSpliceIterator *lineSpliceIterator = new LineSpliceIterator(trigraphIterator);
    CommentStripIterator *commentStripIterator = new CommentStripIterator(lineSpliceIterator);

    LocalIncludeResolver includeResolver(cur_path, c_file_path, includes, nr_includes, defines, step, fout);
    InlineIncludesIterator inlineIncludesIterator(commentStripIterator, &includeResolver, defines);
    
    //if (step->nr != 371) 
    //return;
    
    //FILE *pp_out = step->nr == 371 ? fopen("out3.txt", "w") : 0;
    //FILE *pp_out = strcmp(fn, "../git/live-bootstrap/sysa/tcc-0.9.26/build/mes-0.24.2/lib/linux/access.c") == 0 ?  fopen("out3.txt", "w") : 0;
    FILE *pp_out = 0;//strcmp(fn, "../git/live-bootstrap/sysa/make-3.82/build/make-3.82/getopt.c") == 0 ?  fopen("out3.txt", "w") : 0;
    
    const char *prev_file = "";
    while (inlineIncludesIterator.more)
    {
        //fprintf(fout, "%s %ld.%ld\n", inlineIncludesIterator.filename, inlineIncludesIterator.line, inlineIncludesIterator.column);
        inlineIncludesIterator.next();
        if (pp_out != 0)
        {
            if (strcmp(prev_file, inlineIncludesIterator.filename) != 0)
            {
                fprintf(pp_out, "#inline '%s':%ld\n", inlineIncludesIterator.filename, inlineIncludesIterator.line);
                prev_file = inlineIncludesIterator.filename;
            }
            fprintf(pp_out, "%c", inlineIncludesIterator.ch);
        }
    }
    if (pp_out != 0) fclose(pp_out);
}   


Home